├── .gitignore ├── asm ├── enias.cfg ├── enias.inc ├── gamepad.inc └── gfx.inc ├── bin ├── compile.sh └── run_shmup.sh ├── deps.toml ├── enias-project.yaml ├── license.txt ├── readme.md ├── src ├── .clang-format ├── compile.sh ├── examples │ └── shmup │ │ └── enias-shmup.prg ├── include │ └── enias │ │ ├── graphics_chip.h │ │ ├── input_chip.h │ │ ├── ipu.h │ │ ├── machine.h │ │ ├── ppu.h │ │ ├── render_sdl2.h │ │ ├── sound_chip.h │ │ └── sound_chip_sdl2.h └── lib │ ├── graphics_chip.c │ ├── input_chip.c │ ├── ipu.c │ ├── machine.c │ ├── main.c │ ├── ppu.c │ ├── render_sdl2.c │ ├── sound_chip.c │ └── sound_chip_sdl2.c └── tutorial ├── assets ├── palette.bin ├── tiles.bin └── tiles.png ├── lesson1 ├── compile.sh └── lesson1.s ├── lesson2 ├── compile.sh ├── lesson2.s └── window.png ├── lesson3 ├── compile.sh └── lesson3.s ├── lesson4 ├── compile.sh └── lesson4.s └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | deps*/ 2 | *.o 3 | a.out 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /asm/enias.cfg: -------------------------------------------------------------------------------- 1 | SYMBOLS { 2 | __RESERVED_SIZE__: type = weak, value = $0200; 3 | } 4 | MEMORY { 5 | ZP: file = "", define = yes, start = $0000, size = $01FF; 6 | MAIN: file = %O, start = $0200, size = $10000 - __RESERVED_SIZE__, fill=yes; 7 | } 8 | 9 | SEGMENTS { 10 | ZEROPAGE: load = ZP, type = zp; 11 | CODE: load = MAIN, type = rw; 12 | GFX_TILES: load = MAIN, start=$B000, optional=yes; 13 | GFX_EXT_TILES: load = MAIN, start=$D000, optional=yes; 14 | GFX_NAMETABLE: load = MAIN, start=$F000, optional=yes; 15 | GFX_SPRITES: load = MAIN, start=$F400, optional=yes; 16 | GFX_PALETTE: load = MAIN, start=$F500, optional=yes; 17 | SND_WAVES: load = MAIN, start=$F600, optional=yes; 18 | SND_INSTRUMENTS: load = MAIN, start=$F900, optional=yes; 19 | SND_CHANNELS: load = MAIN, start=$FA00, optional=yes; 20 | SND_EFFECTS: load = MAIN, start=$FAF0, optional=yes; 21 | } 22 | 23 | FEATURES { 24 | } 25 | -------------------------------------------------------------------------------- /asm/enias.inc: -------------------------------------------------------------------------------- 1 | ENIAS_ENTRY_POINT=$FFFE 2 | ENIAS_ENTRY_POINT_LO=$FFFE 3 | ENIAS_ENTRY_POINT_HI=$FFFF 4 | 5 | ENIAS_KEYBOARD=$FB08 6 | ENIAS_GAMEPAD=$FB00 7 | 8 | ENIAS_GAMEPAD_A = $80 9 | ENIAS_GAMEPAD_B = $40 10 | ENIAS_GAMEPAD_SELECT = $20 11 | ENIAS_GAMEPAD_START = $10 12 | ENIAS_GAMEPAD_UP = $08 13 | ENIAS_GAMEPAD_DOWN = $04 14 | ENIAS_GAMEPAD_LEFT = $02 15 | ENIAS_GAMEPAD_RIGHT = $01 16 | 17 | ENIAS_TILES=$B000 18 | ENIAS_SPRITES=$F400 19 | ENIAS_PALETTE=$F500 20 | ENIAS_NAMETABLE=$F000 21 | 22 | 23 | ENIAS_WAVES=$F600 24 | ENIAS_INSTRUMENTS=$F900 25 | ENIAS_CHANNELS=$FA00 26 | ENIAS_EFFECTS=$FAF0 27 | -------------------------------------------------------------------------------- /asm/gamepad.inc: -------------------------------------------------------------------------------- 1 | 2 | .proc gamepad_delta_and_buttons 3 | ; result: 4 | ; x: delta 5 | ; y: delta 6 | ; a: buttons 7 | lda ENIAS_GAMEPAD 8 | 9 | ldx #0 10 | ldy #0 11 | 12 | check_right: 13 | lsr ; RIGHT in carry 14 | bcc check_left 15 | inx 16 | 17 | check_left: 18 | lsr ; LEFT in carry 19 | bcc check_down 20 | dex 21 | 22 | check_down: 23 | lsr ; DOWN in carry 24 | bcc check_up 25 | iny 26 | 27 | check_up: 28 | lsr ; UP in carry 29 | bcc check_buttons 30 | dey 31 | 32 | check_buttons: 33 | 34 | rts 35 | .endproc 36 | -------------------------------------------------------------------------------- /asm/gfx.inc: -------------------------------------------------------------------------------- 1 | .proc sprite_move 2 | ; Move sprite to position 3 | ; a: sprite index 4 | ; x: x-position 5 | ; y: y-position 6 | clc 7 | asl ; sprite_index * 4 8 | asl 9 | sta $fd 10 | stx $fe 11 | 12 | ldx $fd ; sprite-offset 13 | lda $fe ; x-position 14 | sta ENIAS_SPRITES,x ; sprite x-position 15 | inx 16 | 17 | tya 18 | sta ENIAS_SPRITES,x ; sprite y-position 19 | 20 | rts 21 | .endproc 22 | 23 | .proc sprite_set_tile 24 | ; a: sprite index 25 | ; y: tile-index 26 | clc 27 | asl 28 | asl 29 | clc 30 | adc #$02 ; skip x and y position 31 | tax 32 | 33 | tya 34 | sta ENIAS_SPRITES,x ; tile-index 35 | inx 36 | 37 | lda #$ff 38 | sta ENIAS_SPRITES,x ; status 39 | 40 | rts 41 | .endproc 42 | 43 | .proc sprite_set_status 44 | ; a: sprite index 45 | ; y: status 46 | asl 47 | asl 48 | clc 49 | adc #$03 ; skip x, y position and tile index 50 | tax 51 | 52 | tya 53 | sta ENIAS_SPRITES,x ; status 54 | 55 | rts 56 | .endproc 57 | 58 | .proc sprite_set_visible 59 | cpy #$00 60 | bne done 61 | 62 | ldx #$00 63 | ldy #$FF 64 | jsr sprite_move 65 | 66 | done: 67 | rts 68 | .endproc 69 | 70 | 71 | .proc sprite_move_2x2 72 | ; a start-sprite 73 | ; x: x-coord 74 | ; y: y-coord 75 | SPRITE_INDEX_REG=$f0 76 | X_REG=$f1 77 | Y_REG=$f2 78 | sta SPRITE_INDEX_REG 79 | stx X_REG 80 | sty Y_REG 81 | 82 | jsr sprite_move ; upper-left 83 | 84 | lda X_REG 85 | adc #$08 ; x += 8 86 | tax 87 | ldy Y_REG 88 | inc SPRITE_INDEX_REG 89 | lda SPRITE_INDEX_REG 90 | jsr sprite_move ; upper-right 91 | 92 | ldx X_REG 93 | lda Y_REG 94 | adc #$08 ; y += 8 95 | tay 96 | sty Y_REG 97 | 98 | inc SPRITE_INDEX_REG 99 | lda SPRITE_INDEX_REG 100 | jsr sprite_move ; lower-left 101 | 102 | 103 | lda X_REG 104 | adc #$08 ; x += 8 105 | tax 106 | ldy Y_REG 107 | inc SPRITE_INDEX_REG 108 | lda SPRITE_INDEX_REG 109 | jsr sprite_move ; lower-right 110 | 111 | .endproc 112 | -------------------------------------------------------------------------------- /bin/compile.sh: -------------------------------------------------------------------------------- 1 | cd ../src/lib 2 | 3 | deps build --log-level debug 4 | -------------------------------------------------------------------------------- /bin/run_shmup.sh: -------------------------------------------------------------------------------- 1 | cd ../src/lib 2 | 3 | deps run --log-level debug ../examples/shmup/enias-shmup.prg 4 | -------------------------------------------------------------------------------- /deps.toml: -------------------------------------------------------------------------------- 1 | depsversion = "0.0.0" 2 | 3 | name = "piot/enias" 4 | version = "0.0.0" 5 | 6 | 7 | [[dependencies]] 8 | name = 'piot/tyran' 9 | version = "*" 10 | 11 | [[dependencies]] 12 | name = 'piot/shout' 13 | version = "*" 14 | 15 | 16 | [[dependencies]] 17 | name = 'piot/zany' 18 | version = "*" -------------------------------------------------------------------------------- /enias-project.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | project: 3 | name: Enias 4 | platforms: 5 | general: 6 | configuration: 7 | debug: 8 | defines: 9 | TYRAN_CONFIGURATION_DEBUG: 1 10 | include: 11 | - src/external/include 12 | - src/include 13 | src: 14 | - src/lib/ 15 | - src/external/lib/ 16 | ios: 17 | defines: 18 | TORNADO_OS_IOS: 1 19 | configuration: 20 | debug: 21 | frameworks: 22 | - StoreKit 23 | resource-dirs: 24 | - ios/images.xcassets/ 25 | src: 26 | - src/external/platform/ios/ 27 | # - src/external/resources/ios 28 | # - src/settings/ios/ 29 | tvos: 30 | defines: 31 | TORNADO_OS_IOS: 1 32 | TORNADO_OS_TVOS: 1 33 | frameworks: 34 | - StoreKit 35 | - GameController 36 | resource-dirs: 37 | - tvos/images.xcassets/ 38 | src: 39 | - src/external/platform/ios 40 | #- src/external/resources/tvos 41 | #- src/settings/tvos/ 42 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Peter Bjorklund 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Enias 2 | 3 | 8-bit Fantasy Console! 4 | 5 | [![Gitter](https://badges.gitter.im/Piot/enias.svg)](https://gitter.im/Piot/enias?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 6 | 7 | ## Compile 8 | 9 | Make sure that you have the sdl2 libraries installed. 10 | 11 | ``` 12 | brew install sdl2 13 | ``` 14 | 15 | 16 | ``` 17 | cd src 18 | ./compile.sh 19 | 20 | ./enias examples/hackman/hackman.prg 21 | ``` 22 | 23 | 24 | ## Memory Layout 25 | All addresses are "indirect". The 16-bit value stored is used as the actual address. 26 | 27 | | Memory Address | Size | Name | Description | 28 | | --------------- |-------|-----------------------|-------------------------------------------------------------------------| 29 | | Cpu | | | | 30 | | $0000-$00FF | $0100 | ZP | Zero page (registers / scratch area) | 31 | | $0100-$01FF | $0100 | Stack | | 32 | | Program | | | | 33 | | $0200-$AFFF | $AE00 | Program area | | 34 | | Graphics | | | | 35 | | $B000-$CFFF | $2000 | tiles | 256 tiles (8x8) each pixel is 4-bit. (8 rows with 32 tiles) sprites and nametable. | 36 | | $D000-$EFFF | $2000 | extended tiles | 256 tiles (8x8) each pixel is 4-bit. Can only be used for sprites. | 37 | | $F000-$F3FF | $0400 | nametable (default) | $F000 = upper-left, $F100 = upper-right, $F200 = bottom-left, $F300 = bottom right. | 38 | | $F400-$F4FF | $0100 | sprites (default) | | 39 | | $F500-$F52F | $0030 | palette | 16 entries with R,G,B (three octets) | 40 | | $F530-$F533 | $0004 | nametable scrolling | $F530 x-offset (0-255), $F531 (0/1), $F532 y-offset (0-255), $F533 (0/1)| 41 | | Audio | | | | 42 | | $F600-$F8BF | $02C0 | 64 waves (x 11 octets)| | 43 | | $F900-$F977 | $0078 | 24 instruments (x 5 octets)| | 44 | | $FA00-$FAAF | $00B0 | 16 channels | | 45 | | $FAF0-$FAF7 | $0008 | Effects | | 46 | | Input | | | | 47 | | $FB00 | $0008 | Gamepad 4 x 2 octets | | 48 | | $FB08 | $0001 | Keyboard input | | 49 | | Reserved | | | | 50 | | $FB09-$FEFF | $0500 | Reserved | | 51 | | Vectors | | | | 52 | | $FFFC-$FFFD | $0002 | Reset call address. | Defaults to $0200 | 53 | | $FFFE-$FFFF | $0002 | Frame call address. | Defaults to $0200 | 54 | 55 | ## Tiles 56 | Each tile is 8x8 pixels, with four bits for each pixel. The pixel value is an actual index lookup into the palette. 0 (zero) is always considered to be transparent. 57 | 58 | ## Sprites 59 | 60 | Usually starts at $FD00 61 | 62 | | Octet Offset | Name | bits | Description | 63 | | -------------| ------- |----------|-------------------| 64 | | 0 | x | xxxxxxxx | from left to right| 65 | | 1 | y | yyyyyyyy | top to bottom | 66 | | 2 | tile | tttttttt | tile index (each row is 32 tiles)| 67 | | 3 | status | vhp0000t | v: vertical flip, h: horizontal flip, p: priority (0 = behind background tiles) t: add 256 to tile index| 68 | 69 | ## Palette 70 | 16 palette entries. Each entry is: 71 | 72 | | Octet Offset | Name | 73 | |---------------|-------| 74 | | 0 | red | 75 | | 1 | green | 76 | | 2 | blue | 77 | 78 | ## Name-table 79 | The index for each tile to be displayed from top-left corner (32x28), 896 octets. 80 | 81 | | Octet Offset | Size | Name | Description | 82 | |--------------|-------|------|------------------------------| 83 | | $0000 | $0100 | | "top-left". 32x28 index to tiles. (last $80 tiles not used). | 84 | | $0100 | $0100 | | "top-right". 32x28 index to tiles. (last $80 tiles not used). | 85 | | $0200 | $0100 | | "bottom-left". 32x28 index to tiles. (last $80 tiles not used). | 86 | | $0300 | $0100 | | "bottom-right". 32x28 index to tiles. (last $80 tiles not used). | 87 | 88 | ## Input 89 | 90 | Four gamepads, each has the following 91 | 92 | | Octet Offset | Name | bits | Description | 93 | |---------------|----------|----------|--------------| 94 | | 0 | normal | abstudlr | $80 A, $40 B, $20 Select, $10 Start, $08 Up, $04 Down, $02 Left, $01 Right 95 | | 1 | extended | xylr0000 | $80 X, $40 Y, $20 Left trigger, $10 Right trigger 96 | 97 | ### Keyboard input 98 | 99 | | Address | Name | Description | 100 | |---------------|----------|---------------| 101 | | $FB08 | key | Latest pressed key (ASCII) or 0 (zero) if no key pressed | 102 | 103 | ## Sound Chip 104 | 105 | ### Voice / Channel 106 | 16 voices. Each voice has the following info: 107 | 108 | | Octet Offset | Name | bits | Description | 109 | | -------------| -----------|----------|-------------------| 110 | | 0 | instrument | 000iiiii | index into instruments. (0-23)| 111 | | 1 | note | kkkkkkkk | 0: key off | 112 | | 2 | velocity | vvvvvvvv | | 113 | | 3 | pan | vvvvvvvv | 0: all left, 255: all right| 114 | | 4 | pitch | ffffffff | signed diff for key | 115 | | 5 | volume | vvvvvvvv | | 116 | | 6 | fx1-gain | ffffffff | fx1 = feed to reverb | 117 | | 7 | fx2-gain | ffffffff | fx2 = feed to delay | 118 | | 8 | low-pass | ffffffff | | 119 | | 9 | high-pass | ffffffff | | 120 | | A | q-filter | ffffffff | | 121 | 122 | 123 | ### Instrument (Sound definition) 124 | 125 | 24 Instruments. 126 | 127 | | Octet Offset | Name | bits | Description | 128 | | -------------| ------------------------|----------|-------------------| 129 | | 0 | wave-index | wwwwwwww | 0-64. index into waves | 130 | | 1 | attack | aaaaaaaa | time before maximum volume | 131 | | 2 | decay | dddddddd | time before it reaches sustain volume | 132 | | 3 | sustain | ssssssss | (optional?) volume level for the sustain part. | 133 | | 4 | release | rrrrrrrr | (optional?) time before sound reaches zero volume. | 134 | 135 | 136 | ### Wave 137 | 138 | 64 Waves. 139 | 140 | | Octet Offset | Name | bits | Description | 141 | | -------------| ------------------------|----------|-------------------| 142 | | 0 | sample-pointer (low) | wwwwwwww | | 143 | | 1 | sample-pointer (high) | wwwwwwww | | 144 | | 2 | sample-length (low) | wwwwwwww | | 145 | | 3 | sample-length (high) | wwwwwwww | | 146 | | 4 | loop | wll00000 | w: (0=8-bit samples, 1:16-bit samples). loop-mode (0: no loop, 1: forward, 2: ping-pong) | 147 | | 5 | loop start (low) | rrrrrrrr | loop start point | 148 | | 6 | loop start (high) | rrrrrrrr | | 149 | | 7 | loop length (low) | rrrrrrrr | loop length | 150 | | 8 | loop length (high) | rrrrrrrr | | 151 | | 9 | relative_note_number | rrrrrrrr | | 152 | | A | volume | rrrrrrrr | | 153 | 154 | 155 | 156 | ### Effect settings 157 | One global effect system 158 | 159 | | Octet Offset | Name | bits | Description | 160 | | -------------| ------------------------|----------|-------------------| 161 | | 0 | reverb-mix | wwwwwwww | | 162 | | 1 | delay-time | wwwwwwww | | 163 | | 2 | delay-reflect | wwwwwwww | | 164 | | 3 | delay-mix | wwwwwwww | | 165 | 166 | 167 | ### Samples 168 | 169 | | Octet Offset | Name | 170 | | -------------| ------------------------| 171 | | 0 - x | 8/16-bit samples | 172 | -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp # Includes C 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 # The extra indent or outdent of access modifiers, e.g. public:. 5 | AlignAfterOpenBracket: true # If true, horizontally aligns arguments after an open bracket. 6 | AlignEscapedNewlinesLeft: false 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AlignConsecutiveAssignments: false 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: Never 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortIfStatementsOnASingleLine: Never 14 | AllowShortLoopsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: None 16 | AlwaysBreakAfterDefinitionReturnType: false 17 | AlwaysBreakTemplateDeclarations: false 18 | AlwaysBreakBeforeMultilineStrings: false 19 | BreakBeforeBinaryOperators: None 20 | BreakBeforeTernaryOperators: true 21 | BreakConstructorInitializersBeforeComma: true 22 | BinPackParameters: true 23 | BinPackArguments: true 24 | ColumnLimit: 120 25 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 26 | ConstructorInitializerIndentWidth: 4 27 | DerivePointerAlignment: false 28 | ExperimentalAutoDetectBinPacking: false 29 | IndentCaseLabels: true 30 | IndentWrappedFunctionNames: false 31 | IndentFunctionDeclarationAfterType: false 32 | MaxEmptyLinesToKeep: 1 33 | KeepEmptyLinesAtTheStartOfBlocks: true 34 | NamespaceIndentation: None 35 | ObjCBlockIndentWidth: 4 36 | ObjCSpaceAfterProperty: false 37 | ObjCSpaceBeforeProtocolList: true 38 | PenaltyBreakBeforeFirstCallParameter: 19 39 | PenaltyBreakComment: 300 40 | PenaltyBreakString: 1000 41 | PenaltyBreakFirstLessLess: 120 42 | PenaltyBreakAssignment: 1000 43 | PenaltyExcessCharacter: 1000000 44 | PenaltyReturnTypeOnItsOwnLine: 60 45 | PointerAlignment: Left 46 | SpacesBeforeTrailingComments: 1 47 | Cpp11BracedListStyle: true 48 | Standard: Auto 49 | IndentWidth: 4 50 | TabWidth: 4 51 | UseTab: Never 52 | BreakBeforeBraces: Linux 53 | SpacesInParentheses: false 54 | SpacesInSquareBrackets: false 55 | SpacesInAngles: false 56 | SpaceInEmptyParentheses: false 57 | SpacesInCStyleCastParentheses: false 58 | SpaceAfterCStyleCast: true 59 | SpacesInContainerLiterals: true 60 | SpaceBeforeAssignmentOperators: true 61 | ContinuationIndentWidth: 4 62 | SpaceBeforeParens: ControlStatements 63 | DisableFormat: false 64 | -------------------------------------------------------------------------------- /src/compile.sh: -------------------------------------------------------------------------------- 1 | clang -O3 lib/*.c external/lib/zany/*.c external/lib/shout/*.c -I /usr/local/include/SDL2/ -I external/include/ -I include/ -lSDL2 -o enias 2 | -------------------------------------------------------------------------------- /src/examples/shmup/enias-shmup.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piot/enias/ef818c838b4471566f4874a9da06368fb0fed937/src/examples/shmup/enias-shmup.prg -------------------------------------------------------------------------------- /src/include/enias/graphics_chip.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_graphics_chip_h 27 | #define enias_graphics_chip_h 28 | 29 | #include 30 | #include 31 | 32 | typedef struct enias_graphics_chip { 33 | enias_render_sdl2 render_sdl2; 34 | enias_ppu ppu; 35 | } enias_graphics_chip; 36 | 37 | void enias_graphics_chip_init(enias_graphics_chip* self); 38 | void enias_graphics_chip_render(enias_graphics_chip* self, const uint8_t* memory); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/include/enias/input_chip.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_input_chip_h 27 | #define enias_input_chip_h 28 | 29 | #include 30 | 31 | #include 32 | 33 | typedef struct enias_input_chip { 34 | enias_ipu ipu; 35 | } enias_input_chip; 36 | 37 | void enias_input_chip_init(enias_input_chip* self); 38 | int enias_input_chip_update(enias_input_chip* self, uint8_t* target_memory); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/include/enias/ipu.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_ipu_h 27 | #define enias_ipu_h 28 | 29 | #include 30 | 31 | #define ENIAS_IPU_GAMEPAD_A (0x80) 32 | #define ENIAS_IPU_GAMEPAD_B (0x40) 33 | #define ENIAS_IPU_GAMEPAD_SELECT (0x20) 34 | #define ENIAS_IPU_GAMEPAD_START (0x10) 35 | #define ENIAS_IPU_GAMEPAD_UP (0x08) 36 | #define ENIAS_IPU_GAMEPAD_DOWN (0x04) 37 | #define ENIAS_IPU_GAMEPAD_LEFT (0x02) 38 | #define ENIAS_IPU_GAMEPAD_RIGHT (0x01) 39 | 40 | typedef struct enias_ipu_gamepad { 41 | uint8_t normal; 42 | uint8_t extended; 43 | } enias_ipu_gamepad; 44 | 45 | typedef struct enias_ipu { 46 | uint8_t keyboard_char; 47 | enias_ipu_gamepad gamepads[2]; 48 | } enias_ipu; 49 | 50 | void enias_ipu_update(enias_ipu* self, uint8_t* memory); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/include/enias/machine.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_machine_h 27 | #define enias_machine_h 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | typedef struct enias_machine { 35 | zany_cpu cpu; 36 | enias_graphics_chip graphics; 37 | enias_input_chip input; 38 | enias_sound_chip sound; 39 | } enias_machine; 40 | 41 | void enias_machine_init(enias_machine* self); 42 | void enias_machine_load_memory(enias_machine* self, const char* filename); 43 | int enias_machine_go(enias_machine* self); 44 | void enias_machine_close(enias_machine* self); 45 | #endif 46 | -------------------------------------------------------------------------------- /src/include/enias/ppu.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_ppu_h 27 | #define enias_ppu_h 28 | 29 | #include 30 | 31 | #define ENIAS_PPU_SCREEN_WIDTH (256) 32 | #define ENIAS_PPU_SCREEN_HEIGHT (224) 33 | 34 | #pragma pack(1) 35 | typedef struct enias_sprite_info { 36 | uint8_t x; 37 | uint8_t y; 38 | uint8_t tile; 39 | uint8_t flags; 40 | } enias_sprite_info; 41 | typedef struct enias_palette_info { 42 | uint8_t r; 43 | uint8_t g; 44 | uint8_t b; 45 | } enias_palette_info; 46 | typedef struct enias_name_table_info { 47 | uint8_t tile_index; 48 | } enias_name_table_info; 49 | #pragma pack() 50 | 51 | typedef struct enias_ppu { 52 | const enias_name_table_info* name_table; 53 | const enias_palette_info* palette; 54 | const enias_sprite_info* sprites; 55 | const uint8_t* chars; 56 | uint8_t scroll_x; 57 | uint8_t scroll_y; 58 | } enias_ppu; 59 | 60 | void enias_ppu_setup(enias_ppu* ppu, const uint8_t* memory); 61 | void enias_ppu_render(enias_ppu* ppu, uint32_t* pixels); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/include/enias/render_sdl2.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_render_sdl2_h 27 | #define enias_render_sdl2_h 28 | 29 | #include 30 | 31 | typedef struct enias_render_sdl2 { 32 | SDL_Window* window; 33 | SDL_Surface* screen_surface; 34 | SDL_Surface* virtual_screen_surface; 35 | uint32_t next_frame_tick; 36 | } enias_render_sdl2; 37 | 38 | typedef void (*enias_render_sdl2_callback)(void* self, uint32_t* pixels); 39 | 40 | void enias_render_sdl2_init(enias_render_sdl2* self, int width, int height); 41 | void enias_render_sdl2_render(enias_render_sdl2* self, uint32_t color, void* userdata, enias_render_sdl2_callback callback); 42 | void enias_render_sdl2_close(enias_render_sdl2* self); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/include/enias/sound_chip.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_sound_chip_h 27 | #define enias_sound_chip_h 28 | 29 | #include 30 | 31 | typedef struct enias_sound_chip { 32 | shout_chip chip; 33 | uint32_t debug_frame_count; 34 | } enias_sound_chip; 35 | 36 | int enias_sound_chip_init(enias_sound_chip* self); 37 | void enias_sound_chip_update(enias_sound_chip* self, const uint8_t* memory); 38 | void enias_sound_chip_close(enias_sound_chip* self); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/include/enias/sound_chip_sdl2.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #ifndef enias_chip_sdl2_h 27 | #define enias_chip_sdl2_h 28 | 29 | #include 30 | 31 | typedef void (*enias_sound_chip_callback)(void* self, int16_t* sample, int sample_count); 32 | 33 | int enias_sound_chip_sdl2_init(void* userdata, enias_sound_chip_callback callback); 34 | void enias_sound_chip_sdl2_close(void* userdata); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/lib/graphics_chip.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | 28 | typedef struct render_info { 29 | enias_ppu* ppu; 30 | const uint8_t* memory; 31 | } render_info; 32 | 33 | static void render_callback(void* _self, uint32_t* pixels) 34 | { 35 | render_info* self = (render_info*) _self; 36 | enias_ppu_render(self->ppu, pixels); 37 | } 38 | 39 | void enias_graphics_chip_render(enias_graphics_chip* self, const uint8_t* memory) 40 | { 41 | enias_ppu_setup(&self->ppu, memory); 42 | 43 | render_info info; 44 | info.ppu = &self->ppu; 45 | info.memory = memory; 46 | const enias_palette_info* background_color = &self->ppu.palette[0]; 47 | uint32_t background_value = background_color->r << 16 | background_color->g << 8 | background_color->b; 48 | enias_render_sdl2_render(&self->render_sdl2, background_value, &info, render_callback); 49 | } 50 | 51 | void enias_graphics_chip_init(enias_graphics_chip* self) 52 | { 53 | enias_render_sdl2_init(&self->render_sdl2, ENIAS_PPU_SCREEN_WIDTH, ENIAS_PPU_SCREEN_HEIGHT); 54 | } 55 | -------------------------------------------------------------------------------- /src/lib/input_chip.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | #include 28 | 29 | static uint8_t get_gamepad_mask(SDL_Keycode code) 30 | { 31 | switch (code) { 32 | case SDLK_DOWN: 33 | return ENIAS_IPU_GAMEPAD_DOWN; 34 | case SDLK_UP: 35 | return ENIAS_IPU_GAMEPAD_UP; 36 | case SDLK_LEFT: 37 | return ENIAS_IPU_GAMEPAD_LEFT; 38 | case SDLK_RIGHT: 39 | return ENIAS_IPU_GAMEPAD_RIGHT; 40 | case SDLK_z: 41 | return ENIAS_IPU_GAMEPAD_A; 42 | case SDLK_x: 43 | return ENIAS_IPU_GAMEPAD_B; 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | static void handle_key(enias_ipu* ipu, const SDL_KeyboardEvent* event, int on) 50 | { 51 | uint8_t mask = get_gamepad_mask(event->keysym.sym); 52 | enias_ipu_gamepad* gamepad = &ipu->gamepads[0]; 53 | if (on) { 54 | gamepad->normal |= mask; 55 | } else { 56 | gamepad->normal &= ~mask; 57 | } 58 | 59 | if (mask != 0) { 60 | // printf("Gamepad %02X\n", gamepad->normal); 61 | } 62 | } 63 | 64 | static void handle_text_input(enias_ipu* ipu, const SDL_TextInputEvent* event) 65 | { 66 | ipu->keyboard_char = event->text[0]; 67 | // printf("Text Input %02X\n", ipu->keyboard_char); 68 | } 69 | 70 | static void handle_key_up(enias_ipu* ipu, const SDL_KeyboardEvent* event) 71 | { 72 | handle_key(ipu, event, 0); 73 | } 74 | 75 | static void handle_key_down(enias_ipu* ipu, const SDL_KeyboardEvent* event) 76 | { 77 | handle_key(ipu, event, 1); 78 | } 79 | 80 | static int check_sdl_events(enias_ipu* ipu) 81 | { 82 | SDL_Event event; 83 | int quit = 0; 84 | 85 | if (SDL_PollEvent(&event)) { 86 | 87 | switch (event.type) { 88 | case SDL_QUIT: 89 | quit = 1; 90 | break; 91 | case SDL_KEYDOWN: 92 | if (event.key.keysym.sym == SDLK_ESCAPE) { 93 | quit = 1; 94 | } else { 95 | handle_key_down(ipu, &event.key); 96 | } 97 | break; 98 | case SDL_KEYUP: 99 | handle_key_up(ipu, &event.key); 100 | break; 101 | case SDL_TEXTINPUT: 102 | handle_text_input(ipu, &event.text); 103 | break; 104 | } 105 | } 106 | 107 | return quit; 108 | } 109 | 110 | void enias_input_chip_init(enias_input_chip* self) 111 | { 112 | } 113 | 114 | int enias_input_chip_update(enias_input_chip* self, uint8_t* target_memory) 115 | { 116 | int quit = check_sdl_events(&self->ipu); 117 | enias_ipu_update(&self->ipu, target_memory); 118 | return quit; 119 | } 120 | -------------------------------------------------------------------------------- /src/lib/ipu.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | 28 | #include 29 | 30 | #define IPU_START_ADDRESS (0xFB00) 31 | 32 | static void set_input_to_memory(const enias_ipu* input, uint8_t* memory) 33 | { 34 | for (size_t i = 0; i < 2; ++i) { 35 | memory[IPU_START_ADDRESS + i * 2] = input->gamepads[i].normal; 36 | memory[IPU_START_ADDRESS + i * 2 + 1] = input->gamepads[i].extended; 37 | } 38 | 39 | memory[IPU_START_ADDRESS + 0x08] = input->keyboard_char; 40 | } 41 | 42 | void enias_ipu_update(enias_ipu* self, uint8_t* memory) 43 | { 44 | set_input_to_memory(self, memory); 45 | self->keyboard_char = 0; 46 | } 47 | -------------------------------------------------------------------------------- /src/lib/machine.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | #include 28 | #include 29 | 30 | #if defined ENIAS_PLATFORM_WEBASSEMBLY 31 | #include 32 | #endif 33 | 34 | void enias_machine_init(enias_machine* self) 35 | { 36 | zany_cpu_init(&self->cpu); 37 | enias_graphics_chip_init(&self->graphics); 38 | enias_input_chip_init(&self->input); 39 | enias_sound_chip_init(&self->sound); 40 | } 41 | 42 | void enias_machine_close(enias_machine* self) 43 | { 44 | //zany_cpu_close(&self->cpu); 45 | //enias_graphics_chip_close(&self->graphics); 46 | //enias_input_chip_close(&self->input); 47 | enias_sound_chip_close(&self->sound); 48 | } 49 | 50 | void enias_machine_load_memory(enias_machine* self, const char* filename) 51 | { 52 | zany_load(&self->cpu, filename); 53 | self->cpu.memory[ZANY_CONTINUE_VECTOR] = 0x00; 54 | self->cpu.memory[ZANY_CONTINUE_VECTOR + 1] = 0x02; 55 | } 56 | 57 | static int update_frame(enias_machine* self) 58 | { 59 | zany_cpu_set_continue_vector(&self->cpu); 60 | int error_code = zany_run(&self->cpu); 61 | if (error_code) { 62 | printf("ERR: cpu error code:%d\n", error_code); 63 | return error_code; 64 | } 65 | enias_sound_chip_update(&self->sound, self->cpu.memory); 66 | int quit = enias_input_chip_update(&self->input, self->cpu.memory); 67 | enias_graphics_chip_render(&self->graphics, self->cpu.memory); 68 | return quit; 69 | } 70 | 71 | #if defined ENIAS_PLATFORM_WEBASSEMBLY 72 | static void on_frame(void* _self) 73 | { 74 | enias_machine* self = (enias_machine*) _self; 75 | update_frame(self); 76 | } 77 | #endif 78 | 79 | static int loop(enias_machine* self) 80 | { 81 | int quit = 0; 82 | 83 | while (!quit) { 84 | quit = update_frame(self); 85 | } 86 | 87 | enias_machine_close(self); 88 | return quit; 89 | } 90 | 91 | int enias_machine_go(enias_machine* self) 92 | { 93 | #if defined ENIAS_PLATFORM_WEBASSEMBLY 94 | emscripten_set_main_loop_arg(on_frame, self, 0, 1); 95 | return 0; 96 | #else 97 | return loop(self); 98 | #endif 99 | } 100 | -------------------------------------------------------------------------------- /src/lib/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | 28 | int main(int argc, char* argv[]) 29 | { 30 | #if defined ENIAS_PLATFORM_WEBASSEMBLY 31 | const char* program_file = "shmup.prg"; 32 | #else 33 | if (argc < 2) { 34 | printf("\nUsage: enias prg-file\n"); 35 | return 0; 36 | } 37 | const char* program_file = argv[1]; 38 | #endif 39 | enias_machine enias; 40 | enias_machine_init(&enias); 41 | enias_machine_load_memory(&enias, program_file); 42 | enias_machine_go(&enias); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/lib/ppu.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | #include 28 | 29 | #define ENIAS_PPU_TILE_ADDRESS (0xB000) 30 | #define ENIAS_PPU_SPRITE_ADDRESS (0xF400) 31 | #define ENIAS_PPU_PALETTE_ADDRESS (0xF500) 32 | #define ENIAS_PPU_NAMETABLE_ADDRESS (0xF000) 33 | 34 | #define ENIAS_PPU_SPRITE_FLAG_PRIORITY (0x20) 35 | #define ENIAS_PPU_SPRITE_FLAG_HFLIP (0x40) 36 | #define ENIAS_PPU_SPRITE_FLAG_VFLIP (0x80) 37 | 38 | static void tile_to_screen(uint32_t* surface_pixels, const enias_ppu* ppu, size_t tile_index, uint8_t screen_x, uint8_t screen_y) 39 | { 40 | if (screen_y >= ENIAS_PPU_SCREEN_HEIGHT) { 41 | return; 42 | } 43 | uint8_t rows_to_draw = 8; 44 | uint8_t cols_to_draw = 8; 45 | if (screen_y + 8 >= ENIAS_PPU_SCREEN_HEIGHT) { 46 | rows_to_draw = ENIAS_PPU_SCREEN_HEIGHT - screen_y; 47 | } 48 | if (screen_x + cols_to_draw > ENIAS_PPU_SCREEN_WIDTH) { 49 | cols_to_draw = ENIAS_PPU_SCREEN_WIDTH - (uint16_t) screen_x; 50 | } 51 | uint8_t tix = tile_index % 32; 52 | uint8_t tiy = tile_index / 32; 53 | uint16_t tmx = tix * 8; 54 | uint16_t tmy = tiy * 8; 55 | for (uint8_t ty = 0; ty < rows_to_draw; ++ty) { 56 | for (uint8_t tx = 0; tx < cols_to_draw; ++tx) { 57 | uint16_t vx = tmx + tx; 58 | uint16_t vy = tmy + ty; 59 | const uint16_t chars_offset = (vy * ENIAS_PPU_SCREEN_WIDTH / 2) + (vx / 2); 60 | uint8_t chars_pixel = *(ppu->chars + chars_offset); 61 | if (vx & 1) { 62 | chars_pixel &= 0xf; 63 | } else { 64 | chars_pixel >>= 4; 65 | } 66 | if (chars_pixel) { 67 | const enias_palette_info* palette_entry = &ppu->palette[chars_pixel]; 68 | uint32_t color = palette_entry->r << 16 | palette_entry->g << 8 | palette_entry->b; 69 | 70 | uint8_t tpx = screen_x + tx; 71 | uint8_t tpy = screen_y + ty; 72 | uint32_t target_surface_offset = (tpy * ENIAS_PPU_SCREEN_WIDTH + tpx); 73 | surface_pixels[target_surface_offset] = color; 74 | } 75 | } 76 | } 77 | } 78 | 79 | static void render_background_chars(uint32_t* surface_pixels, enias_ppu* ppu) 80 | { 81 | for (uint8_t y = 0; y < ENIAS_PPU_SCREEN_HEIGHT / 8; ++y) { 82 | for (uint8_t x = 0; x < ENIAS_PPU_SCREEN_WIDTH / 8; ++x) { 83 | uint32_t name_table_offset = y * 32 + x; 84 | const enias_name_table_info* name_entry = ppu->name_table + name_table_offset; 85 | uint8_t tile_index = name_entry->tile_index; 86 | uint8_t screen_x = x * 8 - ppu->scroll_x; 87 | uint8_t screen_y = y * 8 - ppu->scroll_y; 88 | tile_to_screen(surface_pixels, ppu, tile_index, screen_x, screen_y); 89 | } 90 | } 91 | } 92 | 93 | static void render_sprites(uint32_t* surface_pixels, enias_ppu* ppu, uint8_t priority) 94 | { 95 | for (uint8_t i = 0; i < 64; ++i) { 96 | const enias_sprite_info* sprite = ppu->sprites + i; 97 | uint8_t tile_index = sprite->tile; 98 | uint8_t screen_x = sprite->x; 99 | uint8_t screen_y = sprite->y; 100 | uint8_t flags = sprite->flags; 101 | 102 | int should_be_shown = (flags & ENIAS_PPU_SPRITE_FLAG_PRIORITY) == priority; 103 | 104 | if (should_be_shown && (screen_y < ENIAS_PPU_SCREEN_HEIGHT)) { 105 | tile_to_screen(surface_pixels, ppu, tile_index, screen_x, screen_y); 106 | } 107 | } 108 | } 109 | 110 | void enias_ppu_setup(enias_ppu* ppu, const uint8_t* memory) 111 | { 112 | const enias_sprite_info* sprite_info = (const enias_sprite_info*) (memory + ENIAS_PPU_SPRITE_ADDRESS); 113 | const enias_palette_info* palette = (const enias_palette_info*) (memory + ENIAS_PPU_PALETTE_ADDRESS); 114 | const enias_name_table_info* name_table = (const enias_name_table_info*) (memory + ENIAS_PPU_NAMETABLE_ADDRESS); 115 | const uint8_t* tile_start = (memory + ENIAS_PPU_TILE_ADDRESS); 116 | 117 | ppu->sprites = sprite_info; 118 | ppu->palette = palette; 119 | ppu->name_table = name_table; 120 | ppu->chars = tile_start; 121 | ppu->scroll_x = memory[0xfe08]; 122 | ppu->scroll_y = memory[0xfe09]; 123 | } 124 | 125 | void enias_ppu_render(enias_ppu* self, uint32_t* pixels) 126 | { 127 | render_sprites(pixels, self, 0); 128 | render_background_chars(pixels, self); 129 | render_sprites(pixels, self, ENIAS_PPU_SPRITE_FLAG_PRIORITY); 130 | } 131 | -------------------------------------------------------------------------------- /src/lib/render_sdl2.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | 28 | static SDL_Surface* create_surface(int width, int height) 29 | { 30 | SDL_Surface* surface; 31 | Uint32 rmask, gmask, bmask, amask; 32 | 33 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN 34 | rmask = 0xff000000; 35 | gmask = 0x00ff0000; 36 | bmask = 0x0000ff00; 37 | amask = 0x000000ff; 38 | #else 39 | rmask = 0x000000ff; 40 | gmask = 0x0000ff00; 41 | bmask = 0x00ff0000; 42 | amask = 0xff000000; 43 | #endif 44 | 45 | surface = SDL_CreateRGBSurface(0, width, height, 32, rmask, gmask, bmask, amask); 46 | if (surface == NULL) { 47 | SDL_Log("SDL_CreateRGBSurface() failed: %s", SDL_GetError()); 48 | exit(1); 49 | } 50 | 51 | surface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0); 52 | 53 | return surface; 54 | } 55 | 56 | #define ENIAS_FRAME_TIME (16) 57 | 58 | void enias_render_sdl2_init(enias_render_sdl2* self, int VIRTUAL_SCREEN_WIDTH, int VIRTUAL_SCREEN_HEIGHT) 59 | { 60 | SDL_Window* window = 0; 61 | SDL_Surface* screen_surface = 0; 62 | 63 | int SCREEN_WIDTH = VIRTUAL_SCREEN_WIDTH * 3; 64 | int SCREEN_HEIGHT = VIRTUAL_SCREEN_HEIGHT * 3; 65 | 66 | SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); 67 | SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); 68 | 69 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE | SDL_INIT_AUDIO) < 0) { 70 | printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); 71 | } 72 | 73 | window = SDL_CreateWindow("enias", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS); 74 | if (window == 0) { 75 | printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); 76 | } 77 | SDL_RaiseWindow(window); 78 | screen_surface = SDL_GetWindowSurface(window); 79 | SDL_FillRect(screen_surface, NULL, SDL_MapRGB(screen_surface->format, 0xFF, 0xFF, 0xFF)); 80 | SDL_Surface* virtual_screen_surface = create_surface(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT); 81 | 82 | self->window = window; 83 | self->screen_surface = screen_surface; 84 | self->virtual_screen_surface = virtual_screen_surface; 85 | 86 | self->next_frame_tick = 0; 87 | } 88 | 89 | static void fill_background_color(SDL_Surface* surface, uint32_t color) 90 | { 91 | SDL_FillRect(surface, 0, color); 92 | } 93 | 94 | static uint32_t ticks_to_sleep(enias_render_sdl2* self) 95 | { 96 | uint32_t now = SDL_GetTicks(); 97 | if (self->next_frame_tick <= now) 98 | return 0; 99 | else 100 | return self->next_frame_tick - now; 101 | } 102 | 103 | static void update_screen(SDL_Window* window, SDL_Surface* screen_surface, SDL_Surface* virtual_screen_surface) 104 | { 105 | SDL_BlitScaled(virtual_screen_surface, 0, screen_surface, 0); 106 | SDL_UpdateWindowSurface(window); 107 | } 108 | 109 | void enias_render_sdl2_render(enias_render_sdl2* self, uint32_t background_color, void* userdata, enias_render_sdl2_callback callback) 110 | { 111 | fill_background_color(self->virtual_screen_surface, background_color); 112 | SDL_LockSurface(self->virtual_screen_surface); 113 | 114 | uint32_t* pixels = self->virtual_screen_surface->pixels; 115 | 116 | callback(userdata, pixels); 117 | 118 | SDL_UnlockSurface(self->virtual_screen_surface); 119 | update_screen(self->window, self->screen_surface, self->virtual_screen_surface); 120 | 121 | SDL_Delay(ticks_to_sleep(self)); 122 | self->next_frame_tick = SDL_GetTicks() + ENIAS_FRAME_TIME; 123 | } 124 | 125 | void enias_render_sdl2_close(enias_render_sdl2* self) 126 | { 127 | SDL_DestroyWindow(self->window); 128 | SDL_Quit(); 129 | } 130 | -------------------------------------------------------------------------------- /src/lib/sound_chip.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | static void audio_callback(void* _self, int16_t* sample, int sample_count) 33 | { 34 | enias_sound_chip* self = (enias_sound_chip*) _self; 35 | shout_update(&self->chip, sample, sample_count); 36 | } 37 | 38 | int enias_sound_chip_init(enias_sound_chip* self) 39 | { 40 | shout_init(&self->chip); 41 | enias_sound_chip_sdl2_init(self, audio_callback); 42 | 43 | return 0; 44 | } 45 | 46 | void enias_sound_chip_close(enias_sound_chip* self) 47 | { 48 | enias_sound_chip_sdl2_close(self); 49 | } 50 | 51 | #pragma pack(1) 52 | typedef struct enias_sound_wave { 53 | uint8_t sample_lo; 54 | uint8_t sample_hi; 55 | uint8_t sample_length_lo; 56 | uint8_t sample_length_hi; 57 | uint8_t loop_type; 58 | uint8_t sample_loop_start_lo; 59 | uint8_t sample_loop_start_hi; 60 | uint8_t sample_loop_length_lo; 61 | uint8_t sample_loop_length_hi; 62 | uint8_t relative_note_number; 63 | } enias_sound_wave; 64 | #pragma pack() 65 | 66 | #define ENIAS_SOUND_WAVE_ADDRESS (0xF600) 67 | #define ENIAS_SOUND_CHANNEL_ADDRESS (0xFA00) 68 | #define ENIAS_SOUND_INSTRUMENTS_ADDRESS (0xF900) 69 | 70 | void enias_sound_chip_update(enias_sound_chip* self, const uint8_t* memory) 71 | { 72 | const shout_instrument* instruments = (const shout_instrument*) (memory + ENIAS_SOUND_INSTRUMENTS_ADDRESS); 73 | const enias_sound_wave* enias_waves = (const enias_sound_wave*) (memory + ENIAS_SOUND_WAVE_ADDRESS); 74 | const shout_channel* channels = (const shout_channel*) (memory + ENIAS_SOUND_CHANNEL_ADDRESS); 75 | 76 | shout_chip* chip = &self->chip; 77 | memcpy(chip->instruments, instruments, sizeof(shout_instrument) * MAX_INSTRUMENTS); 78 | memcpy(chip->channels, channels, sizeof(shout_channel) * MAX_CHANNELS); 79 | 80 | for (size_t wave_index = 0; wave_index < MAX_WAVES; ++wave_index) { 81 | shout_wave* wave = &chip->waves[wave_index]; 82 | const enias_sound_wave* enias_wave = &enias_waves[wave_index]; 83 | uint16_t sample_start_address = (enias_wave->sample_hi << 8) + enias_wave->sample_lo; 84 | wave->samples = (const int16_t*) (memory + sample_start_address); 85 | wave->sample_length = (enias_wave->sample_length_hi << 8) + enias_wave->sample_length_lo; 86 | wave->sample_loop_start = (enias_wave->sample_loop_start_hi << 8) + enias_wave->sample_loop_start_lo; 87 | wave->sample_loop_length = (enias_wave->sample_loop_length_hi << 8) + enias_wave->sample_loop_length_lo; 88 | wave->fine_tune = 0; 89 | wave->volume = 32; 90 | wave->panning = 127; 91 | wave->loop_type = enias_wave->loop_type; 92 | wave->relative_note_number = enias_wave->relative_note_number; 93 | } 94 | 95 | shout_update_params(&self->chip); 96 | } 97 | -------------------------------------------------------------------------------- /src/lib/sound_chip_sdl2.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Peter Bjorklund 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | 28 | #include 29 | 30 | #define TYRAN_LOG_ERROR(x) 31 | 32 | static enias_sound_chip_callback g_enias_sound_chip_callback; 33 | 34 | static void audio_callback(void* _self, Uint8* target, int octet_length) 35 | { 36 | if ((octet_length % 4) != 0) { 37 | TYRAN_LOG_ERROR("ERROR!!!!!"); 38 | } 39 | size_t sample_count_to_fill = octet_length / (sizeof(int16_t) * 2); 40 | if (sample_count_to_fill > 16 * 1024) { 41 | TYRAN_LOG_ERROR("ERROR!!!!!"); 42 | return; 43 | } 44 | if ((sample_count_to_fill % 8) != 0) { 45 | TYRAN_LOG_ERROR("DIVISON ERR"); 46 | return; 47 | } 48 | g_enias_sound_chip_callback(_self, (int16_t*) target, sample_count_to_fill); 49 | } 50 | 51 | int enias_sound_chip_sdl2_init(void* userdata, enias_sound_chip_callback callback) 52 | { 53 | g_enias_sound_chip_callback = callback; 54 | SDL_AudioSpec wav_spec; 55 | 56 | wav_spec.callback = audio_callback; 57 | wav_spec.userdata = userdata; 58 | wav_spec.format = AUDIO_S16; 59 | wav_spec.samples = 2048; 60 | wav_spec.channels = 2; 61 | wav_spec.freq = 44100; 62 | 63 | if (SDL_OpenAudio(&wav_spec, 0) < 0) { 64 | fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); 65 | return 1; 66 | } 67 | 68 | SDL_PauseAudio(0); 69 | return 0; 70 | } 71 | 72 | void enias_sound_chip_sdl2_close(void* userdata) 73 | { 74 | SDL_CloseAudio(); 75 | } 76 | -------------------------------------------------------------------------------- /tutorial/assets/palette.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piot/enias/ef818c838b4471566f4874a9da06368fb0fed937/tutorial/assets/palette.bin -------------------------------------------------------------------------------- /tutorial/assets/tiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piot/enias/ef818c838b4471566f4874a9da06368fb0fed937/tutorial/assets/tiles.bin -------------------------------------------------------------------------------- /tutorial/assets/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piot/enias/ef818c838b4471566f4874a9da06368fb0fed937/tutorial/assets/tiles.png -------------------------------------------------------------------------------- /tutorial/lesson1/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ca65 lesson1.s 4 | ld65 -C ../../asm/enias.cfg lesson1.o 5 | ../../src/enias a.out 6 | -------------------------------------------------------------------------------- /tutorial/lesson1/lesson1.s: -------------------------------------------------------------------------------- 1 | .org $0200 2 | 3 | rts 4 | -------------------------------------------------------------------------------- /tutorial/lesson2/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ca65 lesson2.s 4 | ld65 -C ../../asm/enias.cfg lesson2.o 5 | ../../src/enias a.out 6 | -------------------------------------------------------------------------------- /tutorial/lesson2/lesson2.s: -------------------------------------------------------------------------------- 1 | .org $0200 2 | 3 | ENIAS_SPRITES = $F400 ; Memory address of the sprites 4 | 5 | ldx #00 ; No offset 6 | lda #124 ; X position of the sprite 7 | sta ENIAS_SPRITES,x 8 | 9 | inx ; Increase the offset to 1 10 | lda #108 ; Y position of the sprite 11 | sta ENIAS_SPRITES,x 12 | 13 | inx ; Increase the offset to 2 14 | lda #101 ; Tile index 15 | sta ENIAS_SPRITES,x 16 | 17 | rts 18 | 19 | .segment "GFX_TILES" 20 | .incbin "../assets/tiles.bin" 21 | 22 | .segment "GFX_PALETTE" 23 | .incbin "../assets/palette.bin" 24 | -------------------------------------------------------------------------------- /tutorial/lesson2/window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piot/enias/ef818c838b4471566f4874a9da06368fb0fed937/tutorial/lesson2/window.png -------------------------------------------------------------------------------- /tutorial/lesson3/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ca65 lesson3.s 4 | ld65 -C ../../asm/enias.cfg lesson3.o 5 | ../../src/enias a.out 6 | -------------------------------------------------------------------------------- /tutorial/lesson3/lesson3.s: -------------------------------------------------------------------------------- 1 | .org $0200 2 | 3 | ENIAS_SPRITES = $F400 4 | 5 | ;; Set X 6 | ldx #00 7 | lda sprite_x ; a = sprite_x 8 | sta ENIAS_SPRITES,x 9 | 10 | ;; Set Y 11 | inx 12 | lda #108 13 | sta ENIAS_SPRITES,x 14 | 15 | ;; Set tile index 16 | inx 17 | lda #101 18 | sta ENIAS_SPRITES,x 19 | 20 | ;; Update sprite_x 21 | ldx sprite_x 22 | inx 23 | stx sprite_x 24 | 25 | rts 26 | 27 | sprite_x: 28 | .byte $00 29 | 30 | .segment "GFX_TILES" 31 | .incbin "../assets/tiles.bin" 32 | 33 | .segment "GFX_PALETTE" 34 | .incbin "../assets/palette.bin" 35 | -------------------------------------------------------------------------------- /tutorial/lesson4/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ca65 lesson4.s 4 | ld65 -C ../../asm/enias.cfg lesson4.o 5 | ../../src/enias a.out 6 | -------------------------------------------------------------------------------- /tutorial/lesson4/lesson4.s: -------------------------------------------------------------------------------- 1 | .org $0200 2 | 3 | ENIAS_SPRITES = $F400 4 | ENIAS_GAMEPAD = $FB00 5 | 6 | ;; Set X 7 | ldx #00 8 | lda sprite_x 9 | sta ENIAS_SPRITES,x 10 | 11 | ;; Set Y 12 | inx 13 | lda sprite_y 14 | sta ENIAS_SPRITES,x 15 | 16 | ;; Set tile index 17 | inx 18 | lda #101 19 | sta ENIAS_SPRITES,x 20 | 21 | ;; Update sprite_x 22 | ldx sprite_x 23 | inx 24 | stx sprite_x 25 | 26 | ;; Input logic 27 | lda ENIAS_GAMEPAD ; a = ENIAS_GAMEPAD 28 | and #$04 ; result = a & 0x04 29 | beq dont_move_down ; if(result == 0) { goto dont_move } 30 | inc sprite_y ; sprite_y++ 31 | dont_move_down: 32 | lda ENIAS_GAMEPAD 33 | and #$08 34 | beq dont_move_up 35 | dec sprite_y 36 | dont_move_up: 37 | 38 | rts 39 | 40 | sprite_x: 41 | .byte $00 42 | 43 | sprite_y: 44 | .byte 108 45 | 46 | .segment "GFX_TILES" 47 | .incbin "../assets/tiles.bin" 48 | 49 | .segment "GFX_PALETTE" 50 | .incbin "../assets/palette.bin" 51 | -------------------------------------------------------------------------------- /tutorial/readme.md: -------------------------------------------------------------------------------- 1 | # The Enias Tutorial 2 | 3 | ## Lesson 0 - installing Enias 4 | 1. Install the `ca65` compiler, either through `brew install cc65` or manually: 5 | 1. Clone [https://github.com/cc65/cc65](https://github.com/cc65/cc65) 6 | 2. `cd` into the directory and run `make` 7 | 3. Finally, to make the binaries available on your system, run `sudo make avail` 8 | 2. Install SDL2 via brew or from source (https://www.libsdl.org/download-2.0.php). 9 | 3. Clone this repo. 10 | 4. Go to `/src/` and run `$ ./compile` 11 | 12 | ## Lesson 1 - a minimal project 13 | 1. Create an empty project directory 14 | 2. Add a file called `lesson1.s` and enter the following 6502 assembler: 15 | 16 | ```6502 17 | .org $0200 18 | 19 | rts 20 | ``` 21 | 22 | 3. Compile to an object file with 23 | 24 | ```bash 25 | $ ca65 lesson1.s 26 | ``` 27 | 28 | 4. Then link it (using the Enias memory [layout config file](../asm/enias.cfg)) 29 | 30 | ```bash 31 | $ ld65 -C /asm/enias.cfg lesson1.o 32 | ``` 33 | 34 | 5. This will produce a `a.out` executable: 35 | 36 | ```bash 37 | $ ls 38 | a.out 39 | lesson1.s 40 | lesson1.o 41 | ``` 42 | 43 | 6. Finally - run it inside enias 44 | 45 | ```bash 46 | $ /src/enias a.out 47 | ``` 48 | 49 | 7. For your convenience we've added a `compile.sh` script that does all of the above steps in one go, so you can just do 50 | 51 | ```bash 52 | $ ./compile.sh 53 | ``` 54 | 55 | ## Lesson 2 - rendering a sprite 56 | 57 | 1. Enias only support 16 colors at the same time. To define those colors, a palette file is used. An example palette can be found in [/assets/palette.bin](assets/palette.bin). It contains 16 RGB colors of 24-bits each. 58 | 59 | 2. Sprites and symbols (font characters, map tiles, etc.) all share the same pixel definition. It can be found in the file [/assets/tiles.bin](assets/tiles.bin) 60 | 61 | 3. The `.segment` tells the `ld65` linker where a certain resource is stored in memory. Adding the following statements will let Enias find the pixel data and the palette. 62 | 63 | ```6502 64 | .org $0200 65 | 66 | rts 67 | 68 | .segment "GFX_TILES" 69 | .incbin "../assets/tiles.bin" 70 | 71 | .segment "GFX_PALETTE" 72 | .incbin "../assets/palette.bin" 73 | ``` 74 | 75 | 4. Enias supports 64 sprites of 8x8 pixels each. To actually render a sprite we have to define its position (X and Y) plus its tile index (its position in GFX_TILES). 76 | 77 | 78 | 79 | For example, to render the letter 'e' in the middle of the screen we have to set one of the sprites' X to 124, Y to 108, and tile to 101. It doesn't really matter which one of the sprites we use (0 - 63) unless we care about the order of rendering (higher sprite indexes are rendered on top). 80 | 81 | 5. To make it easier to refer to the "array" of sprites we define a constant: 82 | 83 | ```6502 84 | ENIAS_SPRITES = $F400 85 | ``` 86 | 87 | This magical value is the memory location (in hex) where Enias knows to look for the sprites. 88 | 89 | 6. With the help of this constant and the registers `x` (usually used for offsets) and `a` (usually used for doing math) we can set the X position of sprite nr 0 (C-style code on the right): 90 | 91 | ```6502 92 | ldx #00 ; x = 0 93 | lda #124 ; a = 124 94 | sta ENIAS_SPRITES,x ; ENIAS_SPRITES[x] = a 95 | ``` 96 | 97 | 7. By changing the offset we can set the Y and the tile index too: 98 | 99 | ```6502 100 | inx ; x++ 101 | lda #108 ; a = 108 102 | sta ENIAS_SPRITES,x ; ENIAS_SPRITES[x] = a 103 | 104 | inx ; x++ 105 | lda #101 ; a = 101 106 | sta ENIAS_SPRITES,x ; ENIAS_SPRITES[x] = a 107 | ``` 108 | 109 | 8. Remember to not remove the call to `rts` at the end, it passes execution back to Enias. Without it your program will crash! 110 | 111 | 9. Try building and running the code with the same commands as in lesson 1. If it fails, check out the [example source code](lesson2/lesson2.s). If all goes well you should see the following window: 112 | 113 | 114 | 115 | ## Lesson 3 - moving a sprite 116 | 117 | 1. A static sprite is no fun - lets make it move! To do this we need a variable. Copy the code from lesson 2 and then add this before the other segment definitions: 118 | 119 | ```6502 120 | sprite_x: 121 | .byte $00 122 | ``` 123 | 124 | The linker (ld65) will find a good memory location for this variable that now has the name "sprite_x". The `.byte` reserves 1 byte in memory. 125 | 126 | 2. Instead of setting the sprite X position to a compile time constant we can now use this fancy (global) variable instead: 127 | 128 | ```6502 129 | ldx #00 130 | lda sprite_x ; a = sprite_x 131 | sta ENIAS_SPRITES,x 132 | ``` 133 | 134 | 3. But hold your horses, this will not be a very fun demo. We need to change `sprite_x` for the sprite to start moving. Let's increase it by one each frame: 135 | 136 | ```6502 137 | ldx sprite_x ; x = sprite_x 138 | inx ; x++ 139 | stx sprite_x ; sprite_x = x 140 | ``` 141 | 142 | This pattern is so common that there is a shortcut though: 143 | 144 | ```6502 145 | inc sprite_x 146 | ``` 147 | 148 | ## Lesson 4 - interactivity 149 | 150 | 1. Enias supports both computer keyboards and USB gamepads. To get input we can read the memory from `$FB00` and use a bitmask to know if a certain key/button is pressed. In this example we will expand the lesson 3 demo with the ability to move up or down ($04 and $08 respectively). First, define a constant for the input memory location: 151 | 152 | ```6502 153 | ENIAS_GAMEPAD = $FB00 154 | ``` 155 | 156 | 2. Below the code for moving the sprite (but before `rts`!) we can read the value of the gamepad into register `a`: 157 | 158 | ```6502 159 | lda ENIAS_GAMEPAD 160 | ``` 161 | 162 | 3. To enable vertical movement another global variable must be added: 163 | 164 | ```6502 165 | sprite_y: 166 | .byte 108 167 | ``` 168 | 169 | Make sure it is used in the code above too: 170 | 171 | ```6502 172 | ;; Set Y 173 | inx 174 | lda sprite_y 175 | sta ENIAS_SPRITES,x 176 | ``` 177 | 178 | 4. To handle the input, compare the bits in register `a` with the bits in the constant for the up button ($04, or `00000100` in binary) using `and`. If the result contains was only zeroes (checked with the `beq` command) we jump over the code that modifies `sprite_y`. 179 | 180 | ```6502 181 | lda ENIAS_GAMEPAD ; a = ENIAS_GAMEPAD 182 | and #$04 ; result = a & 0x04 183 | beq dont_move ; if(result == 0) { goto dont_move } 184 | inc sprite_y ; sprite_y++ 185 | dont_move: 186 | ``` 187 | 188 | Now the 'e' can be moved with the `down` key. 189 | 190 | 5. To handle going up also, the code needs a little extension. We also rename "dont_move" to "dont_move_down" to make it a bit more clear. 191 | 192 | ```6502 193 | lda ENIAS_GAMEPAD 194 | and #$04 195 | beq dont_move_down 196 | inc sprite_y 197 | dont_move_down: 198 | lda ENIAS_GAMEPAD 199 | and #$08 200 | beq dont_move_up 201 | dec sprite_y 202 | dont_move_up: 203 | ``` 204 | 205 | # Lesson 5 - Printing a string 206 | Todo. 207 | 208 | # Lesson 6 - Reading and storing strings 209 | Todo. 210 | 211 | # Lesson 7 - Building a level using tiles 212 | Todo. 213 | 214 | # Lesson 8 - Controlling a platforming character 215 | Todo. 216 | 217 | # Lesson 9 - Pickups 218 | Todo. 219 | 220 | # Lesson 10 - Sound effects 221 | Todo. 222 | 223 | # Lesson 11 - Enemies 224 | Todo. 225 | --------------------------------------------------------------------------------