├── gb8.sav ├── .gitignore ├── img ├── pong.gif └── breakout.gif ├── inc ├── gfx │ └── border.bin ├── roms │ ├── Blinky.ch8 │ ├── Cave.ch8 │ ├── Pong.ch8 │ ├── Trip8.ch8 │ ├── fall.bin │ ├── Airplane.ch8 │ ├── BC_test.ch8 │ ├── Breakout.ch8 │ ├── Particle.ch8 │ ├── AstroDodge.ch8 │ ├── Sierpinski.ch8 │ ├── test_opcode.ch8 │ └── Kaleidoscope.ch8 ├── font │ ├── letters.bin │ └── numbers.bin ├── gb8.inc └── hardware.inc ├── src ├── data.asm ├── ram.asm ├── init │ ├── gfxinit.asm │ └── emuinit.asm ├── main.asm ├── functions.asm ├── roms.asm ├── selection.asm └── emu.asm ├── Makefile ├── LICENSE └── README.md /gb8.sav: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gb 2 | *.o 3 | *.sym 4 | *.gbr -------------------------------------------------------------------------------- /img/pong.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/img/pong.gif -------------------------------------------------------------------------------- /img/breakout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/img/breakout.gif -------------------------------------------------------------------------------- /inc/gfx/border.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/gfx/border.bin -------------------------------------------------------------------------------- /inc/roms/Blinky.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Blinky.ch8 -------------------------------------------------------------------------------- /inc/roms/Cave.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Cave.ch8 -------------------------------------------------------------------------------- /inc/roms/Pong.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Pong.ch8 -------------------------------------------------------------------------------- /inc/roms/Trip8.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Trip8.ch8 -------------------------------------------------------------------------------- /inc/roms/fall.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/fall.bin -------------------------------------------------------------------------------- /inc/font/letters.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/font/letters.bin -------------------------------------------------------------------------------- /inc/font/numbers.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/font/numbers.bin -------------------------------------------------------------------------------- /inc/roms/Airplane.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Airplane.ch8 -------------------------------------------------------------------------------- /inc/roms/BC_test.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/BC_test.ch8 -------------------------------------------------------------------------------- /inc/roms/Breakout.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Breakout.ch8 -------------------------------------------------------------------------------- /inc/roms/Particle.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Particle.ch8 -------------------------------------------------------------------------------- /inc/roms/AstroDodge.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/AstroDodge.ch8 -------------------------------------------------------------------------------- /inc/roms/Sierpinski.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Sierpinski.ch8 -------------------------------------------------------------------------------- /inc/roms/test_opcode.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/test_opcode.ch8 -------------------------------------------------------------------------------- /inc/roms/Kaleidoscope.ch8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashiepaws/GB-8/HEAD/inc/roms/Kaleidoscope.ch8 -------------------------------------------------------------------------------- /inc/gb8.inc: -------------------------------------------------------------------------------- 1 | EmuScreenPtrVRAM EQU $9842 2 | GameTitlePtrVRAM EQU $99AA 3 | NonCGBPtrVRAM EQU $98A2 4 | 5 | CursorTileNo EQU $AB 6 | 7 | InputCooldownDuration EQU 5 8 | PageSwitchCooldownDuration EQU 15 -------------------------------------------------------------------------------- /src/data.asm: -------------------------------------------------------------------------------- 1 | SECTION "Strings", ROM0 2 | strChip8: db "CHIP 8", 0 3 | 4 | SECTION "Graphics", ROM0 5 | fontNumbers: 6 | INCBIN "inc/font/numbers.bin" 7 | endFontNumbers: 8 | 9 | fontLetters: 10 | INCBIN "inc/font/letters.bin" 11 | endFontLetters: 12 | 13 | borderTiles: 14 | INCBIN "inc/gfx/border.bin" 15 | endBorderTiles: -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME = GB-8 2 | PADVAL = 0 3 | 4 | RGBASM = rgbasm 5 | RGBLINK = rgblink 6 | RGBFIX = rgbfix 7 | 8 | RM_F = rm -f 9 | 10 | ASFLAGS = -h 11 | LDFLAGS = -t -w -n gb8.sym 12 | FIXFLAGS = -v -p $(PADVAL) -t $(NAME) -C 13 | 14 | gb8.gb: gb8.o 15 | $(RGBLINK) $(LDFLAGS) -o $@ $^ 16 | $(RGBFIX) $(FIXFLAGS) $@ 17 | 18 | gb8.o: src/main.asm 19 | $(RGBASM) $(ASFLAGS) -o $@ $< 20 | 21 | .PHONY: clean 22 | clean: 23 | $(RM_F) gb8.o gb8.gb -------------------------------------------------------------------------------- /src/ram.asm: -------------------------------------------------------------------------------- 1 | SECTION "WRAM", WRAM0[$C000] 2 | wBaseMemory:: ds $1000 3 | 4 | _start_sysvars: 5 | 6 | ; Register Pointers 7 | wRegV:: ds 16 8 | wRegI:: dw 9 | wRegDelay:: db 10 | wRegSound:: db 11 | 12 | ; Stack 13 | wSP:: db 14 | wStack:: ds 32 15 | wStackEnd:: 16 | 17 | ; Input 18 | wInputMaskMap:: ds 16 19 | wInputData:: db 20 | 21 | ; Other 22 | wCycleBuf:: db 23 | wCycleLimit:: db 24 | 25 | _end_sysvars: 26 | 27 | SECTION "EmuVRAM", WRAM0, ALIGN[8] 28 | wBaseVRAM:: ds 2048 29 | wEndVRAM:: 30 | wSpriteSize:: db 31 | wSprOverflow:: db 32 | wSprOverflowY:: db 33 | wUpdateDisplay:: db 34 | 35 | SECTION "Selection Menu Variables", WRAM0 36 | wSelectionPage:: db 37 | wSelectionCursorPos:: db 38 | wInputCooldown:: db 39 | wPageTitles:: db 40 | wAllowInput:: db 41 | 42 | SECTION "Other Variables", WRAM0 43 | wInitialRegA:: db -------------------------------------------------------------------------------- /src/init/gfxinit.asm: -------------------------------------------------------------------------------- 1 | SECTION "Graphics Initialization Code", ROM0 2 | 3 | ; ------------------------------------------------------------------------------ 4 | ; Initializes CGB Palette registers 5 | ; ------------------------------------------------------------------------------ 6 | InitPalettes:: 7 | ld a, BCPSF_AUTOINC 8 | ldh [rBCPS], a 9 | xor a 10 | ldh [rBCPD], a 11 | ldh [rBCPD], a 12 | dec a 13 | ldh [rBCPD], a 14 | ldh [rBCPD], a 15 | inc a 16 | ldh [rBCPD], a 17 | ldh [rBCPD], a 18 | ret 19 | 20 | ; ------------------------------------------------------------------------------ 21 | ; Loads font tiles into VRAM 22 | ; ------------------------------------------------------------------------------ 23 | InitFont:: 24 | ld hl, $8800 25 | ld de, fontNumbers 26 | ld bc, endFontNumbers - fontNumbers 27 | call Memcpy 28 | ld hl, $8910 29 | ld de, fontLetters 30 | ld bc, endFontLetters - fontLetters 31 | call Memcpy 32 | ret -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Optix 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 | -------------------------------------------------------------------------------- /src/main.asm: -------------------------------------------------------------------------------- 1 | INCLUDE "inc/hardware.inc" 2 | INCLUDE "inc/gb8.inc" 3 | 4 | INCLUDE "src/data.asm" 5 | INCLUDE "src/roms.asm" 6 | INCLUDE "src/ram.asm" 7 | INCLUDE "src/functions.asm" 8 | INCLUDE "src/emu.asm" 9 | INCLUDE "src/init/gfxinit.asm" 10 | INCLUDE "src/init/emuinit.asm" 11 | INCLUDE "src/selection.asm" 12 | 13 | SECTION "Vectors", ROM0[0] 14 | ds $40 - @ 15 | 16 | VBlankVector:: 17 | reti 18 | 19 | ds $100 - @ 20 | 21 | SECTION "Entry Point", ROM0[$100] 22 | nop 23 | jr Main 24 | 25 | ; Pad header fields 26 | ds $150 - @ 27 | 28 | SECTION "Main", ROM0[$150] 29 | Main:: 30 | ; Store initial value of L in RAM 31 | ld [wInitialRegA], a 32 | 33 | Restart:: 34 | ; Wait for VBlank, disable LCD 35 | ldh a, [rLY] 36 | cp SCRN_Y 37 | jr c, Main 38 | xor a 39 | ld [rLCDC], a 40 | 41 | ; Disable Sound 42 | ld a, AUDENA_OFF 43 | ldh [rAUDENA], a 44 | 45 | ; Zero out VRAM tile data 46 | ld hl, $8000 47 | ld bc, $01A0 48 | call Zerofill 49 | 50 | ; Initialize Graphics 51 | call InitPalettes 52 | call InitFont 53 | 54 | ; Check if running in CGB mode 55 | ld a, [wInitialRegA] 56 | cp BOOTUP_A_CGB 57 | jp nz, ScreenNonCGB 58 | 59 | ; Jump to Selection Menu Initialization 60 | jp StartSelectionMenu 61 | 62 | ScreenNonCGB:: 63 | ; Initialize DMG Palette 64 | ld a, %11010100 65 | ld [rBGP], a 66 | 67 | ; Print CGB-only text to screen 68 | ld hl, (NonCGBPtrVRAM) 69 | ld de, strNonCGB1 70 | call PrintString 71 | ld hl, (NonCGBPtrVRAM + $40) 72 | ld de, strNonCGB2 73 | call PrintString 74 | ld hl, (NonCGBPtrVRAM + $80) 75 | ld de, strNonCGB3 76 | call PrintString 77 | 78 | ; Initialize LCD and clear IF/IE 79 | xor a 80 | ldh [rIF], a 81 | ldh [rIE], a 82 | ld a, LCDCF_ON | LCDCF_BGON | LCDCF_BG8000 83 | ldh [rLCDC], a 84 | ei 85 | nop 86 | 87 | ; HALT infinitely 88 | halt 89 | 90 | strNonCGB1: db "THIS SOFTWARE IS", 0 91 | strNonCGB2: db "INTENDED FOR USE", 0 92 | strNonCGB3: db "ON GAMEBOY COLOR", 0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GB-8 2 | ![GitHub](https://img.shields.io/github/license/Hacktix/GB-8?style=for-the-badge) 3 | ![GitHub Release Date](https://img.shields.io/github/release-date/Hacktix/GB-8?label=Latest%20Release&style=for-the-badge) 4 | ![GitHub last commit](https://img.shields.io/github/last-commit/Hacktix/GB-8?style=for-the-badge) 5 | ![GitHub repo size](https://img.shields.io/github/repo-size/Hacktix/GB-8?style=for-the-badge) 6 | 7 | ## What's this? 8 | 9 | GB-8 is a CHIP-8 interpreter for the Gameboy Color. It comes pre-loaded with a whole bunch of CHIP-8 ROMs and is easily expandable and modular. And thanks to the small size of CHIP-8 ROMs, it even fits onto a 32KB cartridge! 10 | 11 | ## How do I use it? 12 | 13 | Simply download the newest version from the [Releases Section](https://github.com/Hacktix/GB-8/releases) and load it up on hardware or on your favorite Gameboy emulator. You'll be shown a menu which you can navigate using the D-Pad. Once you've selected the game you want to play, press A or Start in order to start the emulator. If you want to quit out of the game, pressing the buttons A, B, Start and Select all at the same time exits the emulator. 14 | 15 | Controls for the games themselves are mapped on a per-game basis, most should be quite intuitive though. 16 | 17 | ## Which ROMs are there? 18 | 19 | The current Release build comes packaged with the following ROMs: 20 | 21 | * Airplane (Game) 22 | * AstroDodge (Game) 23 | * Blinky (Game) 24 | * Breakout (Game) 25 | * Cave (Game) 26 | * Kaleidoscope (Demo) 27 | * Particle (Demo) 28 | * Pong (Game) 29 | * Sierpinski (Demo) 30 | * Trip8 (Demo) 31 | * Fall (Demo) 32 | 33 | If you clone the repository you can add your own CHIP-8 ROMs and assemble a new GB ROM, all you need to do is to change the `src/roms.asm` file to your needs. A description of the file format can be found at the top of the file. 34 | 35 | ## What does it look like? 36 | 37 | Except for the menu parts of the emulator, it's simply just a common CHIP-8 display. For convenience, the emulator scales the screen up by a factor of 2, so that things are visible without having to squint at them. 38 | 39 | But it's probably best to see for yourself: 40 | 41 | ![Pong](./img/pong.gif) 42 | 43 | ![Breakout](./img/breakout.gif) 44 | 45 | -------------------------------------------------------------------------------- /src/functions.asm: -------------------------------------------------------------------------------- 1 | SECTION "Functions", ROM0 2 | ; ------------------------------------------------------------------------------ 3 | ; Copies a memory section of sice BC pointed to by DE to HL. 4 | ; ------------------------------------------------------------------------------ 5 | Memcpy:: 6 | ld a, [de] 7 | inc de 8 | ld [hli], a 9 | dec bc 10 | ld a, b 11 | or c 12 | jr nz, Memcpy 13 | ret 14 | 15 | ; ------------------------------------------------------------------------------ 16 | ; Fills BC bytes with zero, starting at HL. 17 | ; ------------------------------------------------------------------------------ 18 | Zerofill:: 19 | xor a 20 | ld [hli], a 21 | dec bc 22 | ld a, b 23 | or c 24 | jr nz, Zerofill 25 | ret 26 | 27 | ; ------------------------------------------------------------------------------ 28 | ; Fills BC bytes starting from HL with the value D. 29 | ; ------------------------------------------------------------------------------ 30 | Memfill:: 31 | ld a, d 32 | ld [hli], a 33 | dec bc 34 | ld a, b 35 | or c 36 | jr nz, Memfill 37 | ret 38 | 39 | ; ------------------------------------------------------------------------------ 40 | ; Prints the string at DE to VRAM starting at HL. 41 | ; ------------------------------------------------------------------------------ 42 | PrintString:: 43 | ld a, [de] 44 | inc de 45 | and a 46 | ret z 47 | cp $20 48 | jr nz, .noSpace 49 | inc hl 50 | jr PrintString 51 | .noSpace 52 | add $50 53 | ld [hli], a 54 | jr PrintString 55 | 56 | ; ------------------------------------------------------------------------------ 57 | ; Returns the length of the string starting at HL in D. 58 | ; ------------------------------------------------------------------------------ 59 | Strln:: 60 | ld d, $00 61 | .strlenLoop 62 | ld a, [hli] 63 | and a 64 | ret z 65 | inc d 66 | jr .strlenLoop 67 | 68 | ; ------------------------------------------------------------------------------ 69 | ; Starts reading a ROM file entry at HL and initializes the emulator 70 | ; for it. 71 | ; ------------------------------------------------------------------------------ 72 | InitROM:: 73 | ; Loop until string-terminating zero-byte is reached 74 | ld a, [hli] 75 | and a 76 | jr nz, InitROM 77 | 78 | ; Load emulation speed into RAM 79 | ld a, [hli] 80 | ld [wCycleLimit], a 81 | 82 | ; Load Button Mapping into RAM 83 | ld c, 8 84 | ld b, %10000000 85 | 86 | .buttonMappingInitLoop 87 | ; Check if button mapping is valid 88 | ld a, [hli] 89 | cp $10 90 | jr nc, .unmappedButton 91 | 92 | ; Load pointer to proper input mask byte 93 | add LOW(wInputMaskMap) 94 | ld e, a 95 | adc HIGH(wInputMaskMap) 96 | sub e 97 | ld d, a 98 | 99 | ; Load input mask byte into RAM 100 | ld a, b 101 | ld [de], a 102 | 103 | .unmappedButton 104 | ; Update mask byte for next button 105 | srl b 106 | 107 | ; Check if all 8 buttons were mapped, loop if not 108 | dec c 109 | jr nz, .buttonMappingInitLoop 110 | 111 | ; Preserve HL 112 | push hl 113 | 114 | ; Load Fontset into memory 115 | ld hl, wBaseMemory 116 | ld de, Fontset 117 | ld bc, EndFontset - Fontset 118 | call Memcpy 119 | 120 | ; Fill memory with zero up to $200 121 | ld bc, ($200 - (EndFontset - Fontset)) 122 | call Zerofill 123 | 124 | ; Restore HL, load ROM data size and call Memcpy 125 | pop hl 126 | ld a, [hli] 127 | ld c, a 128 | ld a, [hli] 129 | ld b, a 130 | ld d, h 131 | ld e, l 132 | ld hl, wBaseMemory + $200 133 | call Memcpy 134 | 135 | ; Initialization done - return 136 | ret -------------------------------------------------------------------------------- /src/init/emuinit.asm: -------------------------------------------------------------------------------- 1 | SECTION "Emulator Initialization Code", ROM0 2 | 3 | ; ------------------------------------------------------------------------------ 4 | ; Initializes all emu-relevant system variables 5 | ; ------------------------------------------------------------------------------ 6 | InitSysvars:: 7 | ld hl, _start_sysvars 8 | ld bc, _end_sysvars - _start_sysvars 9 | call Zerofill 10 | xor a 11 | ld [wCycleBuf], a 12 | ld [wSprOverflow], a 13 | ld [wSprOverflowY], a 14 | ret 15 | 16 | ; ------------------------------------------------------------------------------ 17 | ; Initializes VRAM as well as emuVRAM 18 | ; ------------------------------------------------------------------------------ 19 | InitEmuVRAM:: 20 | ; Clear EmuVRAM 21 | ld hl, wBaseVRAM 22 | ld bc, wEndVRAM - wBaseVRAM 23 | call Zerofill 24 | 25 | ; Initialize tiles to $FF 26 | ld hl, $9800 27 | .screenClearLoop 28 | ld a, $ff 29 | ld [hli], a 30 | ld a, h 31 | cp $9a 32 | jr nz, .screenClearLoop 33 | ld a, l 34 | cp $34 35 | jr nz, .screenClearLoop 36 | 37 | ; Initialize screen section tiles 38 | ld hl, EmuScreenPtrVRAM 39 | xor a 40 | ld bc, $1008 41 | .screenSectionInitLoop 42 | ld [hli], a 43 | inc a 44 | dec b 45 | jr nz, .screenSectionInitLoop 46 | push af 47 | ld a, $10 48 | add l 49 | ld l, a 50 | adc h 51 | sub l 52 | ld h, a 53 | pop af 54 | ld b, $10 55 | dec c 56 | jr nz, .screenSectionInitLoop 57 | 58 | ; Set emulator to update VRAM on first frame 59 | ld a, 1 60 | ld [wUpdateDisplay], a 61 | 62 | ; Fall-through to Emulator Display Border initialization 63 | 64 | ; ------------------------------------------------------------------------------ 65 | ; Loads emulator screen border tiles into VRAM 66 | ; ------------------------------------------------------------------------------ 67 | InitEmuBorder:: 68 | ; Load Border Tiles 69 | ld hl, $8B00 70 | ld de, borderTiles 71 | ld bc, endBorderTiles - borderTiles 72 | call Memcpy 73 | 74 | ; Load top border into map 75 | ld hl, (EmuScreenPtrVRAM - $21) 76 | ld d, $B0 77 | ld a, d 78 | ld [hli], a 79 | inc d 80 | ld bc, $0010 81 | call Memfill 82 | inc d 83 | ld a, d 84 | ld [hl], a 85 | 86 | ; Load side borders into map 87 | ld hl, (EmuScreenPtrVRAM-1) 88 | ld de, $B708 89 | .sideBordersLoop 90 | ld a, d 91 | ld [hli], a 92 | sub $04 93 | ld d, a 94 | ld a, l 95 | add $10 96 | ld l, a 97 | adc h 98 | sub l 99 | ld h, a 100 | ld a, d 101 | ld [hli], a 102 | add $04 103 | ld d, a 104 | ld a, l 105 | add $0E 106 | ld l, a 107 | adc h 108 | sub l 109 | ld h, a 110 | dec e 111 | jr nz, .sideBordersLoop 112 | 113 | ; Load bottom border into map 114 | ld d, $B6 115 | ld a, d 116 | ld [hli], a 117 | dec d 118 | ld bc, $0010 119 | call Memfill 120 | dec d 121 | ld a, d 122 | ld [hl], a 123 | 124 | ; Load "CHIP 8" text 125 | ld hl, $9967 126 | ld de, strChip8 127 | call PrintString 128 | 129 | ret 130 | 131 | ; ------------------------------------------------------------------------------ 132 | ; Loads the game title from HL and centers it on screen below the emulator 133 | ; display screen. 134 | ; ------------------------------------------------------------------------------ 135 | InitGameTitleDisplay:: 136 | ; Load horizontal offset into B, string pointer into DE 137 | push hl 138 | call Strln 139 | ld a, d 140 | srl a 141 | ld b, a 142 | pop de 143 | 144 | ; Offset HL by B 145 | ld a, LOW(GameTitlePtrVRAM) 146 | sub b 147 | ld l, a 148 | ld h, HIGH(GameTitlePtrVRAM) 149 | jr nc, .skipOffsetCarry 150 | dec h 151 | .skipOffsetCarry 152 | 153 | ; Print String 154 | jp PrintString -------------------------------------------------------------------------------- /src/roms.asm: -------------------------------------------------------------------------------- 1 | SECTION "ROM Files", ROM0 2 | ; ------------------------------------------------------------------------------ 3 | ; # ROM File Repository 4 | ; 5 | ; * Structure: 6 | ; db "", 0 7 | ; db 8 | ; db 9 | ; dw 10 | ; INCBIN "inc/roms/" 11 | ; 12 | ; * Emulation Speed: 13 | ; A single byte determining how many cycles to emulate per frame. The 14 | ; recommended maximum is 40, but may vary between different ROMs. Too high 15 | ; speeds may cause graphical errors. 16 | ; 17 | ; * Button Mapping: 18 | ; Always 8 bytes. Each byte has an assigned button (see below). 19 | ; Pressing the assigned button is interpreted as pressing the button with 20 | ; the value of the byte. Any value greater than $F is interpreted as an 21 | ; unassigned button. Duplicate mappings are not allowed. 22 | ; 23 | ; Button Mapping bytes in order: 24 | ; Start, Select, B, A, DPad Down, DPad Up, DPad Left, DPad Right 25 | ; ------------------------------------------------------------------------------ 26 | 27 | GameDataList:: 28 | dw Airplane, AstroDodge, Blinky, Breakout, Cave, Fall, Kaleidoscope, Particle, Pong, Sierpinski, Trip8 29 | dw $FFFF 30 | EndGameDataList:: 31 | 32 | ; ------------------------------------------------------------------------------ 33 | ; Kaleidoscope 34 | ; ------------------------------------------------------------------------------ 35 | Kaleidoscope:: 36 | ; ROM Title 37 | db "KALEIDOSCOPE", 0 38 | 39 | ; Emulation Speed 40 | db 10 41 | 42 | ; Button Mapping 43 | db $00, $FF, $FF, $FF, $08, $02, $04, $06 44 | 45 | ; ROM Size 46 | dw kaleidoscopeDataEnd - kaleidoscopeData 47 | 48 | ; ROM Data 49 | kaleidoscopeData: 50 | INCBIN "inc/roms/Kaleidoscope.ch8" 51 | kaleidoscopeDataEnd: 52 | 53 | ; ------------------------------------------------------------------------------ 54 | ; Breakout 55 | ; ------------------------------------------------------------------------------ 56 | Breakout:: 57 | ; ROM Title 58 | db "BREAKOUT", 0 59 | 60 | ; Emulation Speed 61 | db 10 62 | 63 | ; Button Mapping 64 | db $FF, $FF, $FF, $FF, $FF, $FF, $04, $06 65 | 66 | ; ROM Size 67 | dw breakoutDataEnd - breakoutData 68 | 69 | ; ROM Data 70 | breakoutData: 71 | INCBIN "inc/roms/Breakout.ch8" 72 | breakoutDataEnd: 73 | 74 | ; ------------------------------------------------------------------------------ 75 | ; Astro Dodge 76 | ; ------------------------------------------------------------------------------ 77 | AstroDodge:: 78 | ; ROM Title 79 | db "ASTRODODGE", 0 80 | 81 | ; Emulation Speed 82 | db 10 83 | 84 | ; Button Mapping 85 | db $05, $FF, $FF, $FF, $08, $02, $04, $06 86 | 87 | ; ROM Size 88 | dw astroDodgeDataEnd - astroDodgeData 89 | 90 | ; ROM Data 91 | astroDodgeData: 92 | INCBIN "inc/roms/AstroDodge.ch8" 93 | astroDodgeDataEnd: 94 | 95 | ; ------------------------------------------------------------------------------ 96 | ; Trip8 Demo 97 | ; ------------------------------------------------------------------------------ 98 | Trip8:: 99 | ; ROM Title 100 | db "TRIP8 DEMO", 0 101 | 102 | ; Emulation Speed 103 | db 25 104 | 105 | ; Button Mapping 106 | db $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF 107 | 108 | ; ROM Size 109 | dw trip8DataEnd - trip8Data 110 | 111 | ; ROM Data 112 | trip8Data: 113 | INCBIN "inc/roms/Trip8.ch8" 114 | trip8DataEnd: 115 | 116 | ; ------------------------------------------------------------------------------ 117 | ; Sierpinski Demo 118 | ; ------------------------------------------------------------------------------ 119 | Sierpinski:: 120 | ; ROM Title 121 | db "SIERPINSKI DEMO", 0 122 | 123 | ; Emulation Speed 124 | db 255 125 | 126 | ; Button Mapping 127 | db $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF 128 | 129 | ; ROM Size 130 | dw sierpinskiDataEnd - sierpinskiData 131 | 132 | ; ROM Data 133 | sierpinskiData: 134 | INCBIN "inc/roms/Sierpinski.ch8" 135 | sierpinskiDataEnd: 136 | 137 | ; ------------------------------------------------------------------------------ 138 | ; Particle Demo 139 | ; ------------------------------------------------------------------------------ 140 | Particle:: 141 | ; ROM Title 142 | db "PARTICLE DEMO", 0 143 | 144 | ; Emulation Speed 145 | db 40 146 | 147 | ; Button Mapping 148 | db $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF 149 | 150 | ; ROM Size 151 | dw particleDataEnd - particleData 152 | 153 | ; ROM Data 154 | particleData: 155 | INCBIN "inc/roms/Particle.ch8" 156 | particleDataEnd: 157 | 158 | ; ------------------------------------------------------------------------------ 159 | ; Pong (Singleplayer) 160 | ; ------------------------------------------------------------------------------ 161 | Pong:: 162 | ; ROM Title 163 | db "PONG 1P", 0 164 | 165 | ; Emulation Speed 166 | db 15 167 | 168 | ; Button Mapping 169 | db $FF, $FF, $FF, $FF, $04, $01, $FF, $FF 170 | 171 | ; ROM Size 172 | dw pongDataEnd - pongData 173 | 174 | ; ROM Data 175 | pongData: 176 | INCBIN "inc/roms/Pong.ch8" 177 | pongDataEnd: 178 | 179 | ; ------------------------------------------------------------------------------ 180 | ; Airplane 181 | ; ------------------------------------------------------------------------------ 182 | Airplane:: 183 | ; ROM Title 184 | db "AIRPLANE", 0 185 | 186 | ; Emulation Speed 187 | db 12 188 | 189 | ; Button Mapping 190 | db $FF, $FF, $08, $08, $FF, $FF, $FF, $FF 191 | 192 | ; ROM Size 193 | dw airplaneDataEnd - airplaneData 194 | 195 | ; ROM Data 196 | airplaneData: 197 | INCBIN "inc/roms/Airplane.ch8" 198 | airplaneDataEnd: 199 | 200 | ; ------------------------------------------------------------------------------ 201 | ; Blinky 202 | ; ------------------------------------------------------------------------------ 203 | Blinky:: 204 | ; ROM Title 205 | db "BLINKY", 0 206 | 207 | ; Emulation Speed 208 | db 20 209 | 210 | ; Button Mapping 211 | db $FF, $FF, $FF, $FF, $06, $03, $07, $08 212 | 213 | ; ROM Size 214 | dw blinkyDataEnd - blinkyData 215 | 216 | ; ROM Data 217 | blinkyData: 218 | INCBIN "inc/roms/Blinky.ch8" 219 | blinkyDataEnd: 220 | 221 | ; ------------------------------------------------------------------------------ 222 | ; Cave 223 | ; ------------------------------------------------------------------------------ 224 | Cave:: 225 | ; ROM Title 226 | db "CAVE", 0 227 | 228 | ; Emulation Speed 229 | db 10 230 | 231 | ; Button Mapping 232 | db $0F, $FF, $FF, $FF, $08, $02, $04, $06 233 | 234 | ; ROM Size 235 | dw caveDataEnd - caveData 236 | 237 | ; ROM Data 238 | caveData: 239 | INCBIN "inc/roms/Cave.ch8" 240 | caveDataEnd: 241 | 242 | ; ------------------------------------------------------------------------------ 243 | ; Fall 244 | ; ------------------------------------------------------------------------------ 245 | Fall:: 246 | ; ROM Title 247 | db "FALL DEMO", 0 248 | 249 | ; Emulation Speed 250 | db 25 251 | 252 | ; Button Mapping 253 | db $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF 254 | 255 | ; ROM Size 256 | dw fallDataEnd - fallData 257 | 258 | ; ROM Data 259 | fallData: 260 | INCBIN "inc/roms/fall.bin" 261 | fallDataEnd: 262 | 263 | 264 | 265 | 266 | 267 | SECTION "Emulator Fontset", ROM0 268 | ; CHIP-8 Fontset (hardcoded) 269 | Fontset: 270 | db $F0, $90, $90, $90, $F0 271 | db $20, $60, $20, $20, $70 272 | db $F0, $10, $F0, $80, $F0 273 | db $F0, $10, $F0, $10, $F0 274 | db $90, $90, $F0, $10, $10 275 | db $F0, $80, $F0, $10, $F0 276 | db $F0, $80, $F0, $90, $F0 277 | db $F0, $10, $20, $40, $40 278 | db $F0, $90, $F0, $90, $F0 279 | db $F0, $90, $F0, $10, $F0 280 | db $F0, $90, $F0, $90, $90 281 | db $E0, $90, $E0, $90, $E0 282 | db $F0, $80, $80, $80, $F0 283 | db $E0, $90, $90, $90, $E0 284 | db $F0, $80, $F0, $80, $F0 285 | db $F0, $80, $F0, $80, $80 286 | EndFontset: -------------------------------------------------------------------------------- /src/selection.asm: -------------------------------------------------------------------------------- 1 | SECTION "Selection Menu", ROM0 2 | StartSelectionMenu:: 3 | ; Initialize page number to zero 4 | xor a 5 | ld [wSelectionPage], a 6 | ld [wInputCooldown], a 7 | ld [wAllowInput], a 8 | 9 | ReloadSelectionMenu:: 10 | ; Clear VRAM 11 | ld hl, $9820 12 | ld bc, $0800 13 | call Zerofill 14 | 15 | ; Reset cursor to zero 16 | xor a 17 | ld [wSelectionCursorPos], a 18 | ld [wPageTitles], a 19 | ld a, CursorTileNo 20 | ld [$9820], a 21 | 22 | ; Validate current page, set to 0 if invalid 23 | ld a, [wSelectionPage] 24 | cp ((EndGameDataList - GameDataList - 2) / 2) / 15 + !!(((EndGameDataList - GameDataList - 2) / 2) % 15) 25 | jr c, .validPage 26 | xor a 27 | ld [wSelectionPage], a 28 | .validPage 29 | 30 | ; Load BC with ROM table pointer + page offset 31 | ld bc, GameDataList 32 | ld a, [wSelectionPage] 33 | and a 34 | jr z, .skipPageOffset 35 | ld d, a 36 | .pageOffsetLoop 37 | ld a, $1E 38 | add c 39 | ld c, a 40 | adc b 41 | sub c 42 | ld b, a 43 | dec d 44 | jr nz, .pageOffsetLoop 45 | .skipPageOffset 46 | push bc ; Preserve pointer for later on 47 | 48 | ; Load HL with VRAM pointer 49 | ld hl, $9821 50 | 51 | .titleLoadLoop 52 | ; Load ROM pointer into DE 53 | ld a, [bc] 54 | inc bc 55 | ld e, a 56 | ld a, [bc] 57 | inc bc 58 | ld d, a 59 | 60 | ; Check if $FFFF pointer was loaded 61 | and e 62 | inc a ; Sets zero flag if DE is $FFFF 63 | jr z, .endTitleLoop 64 | 65 | ; Print string (preserving HL), add $20 to HL 66 | push hl 67 | call PrintString 68 | pop hl 69 | ld a, l 70 | add $20 71 | ld l, a 72 | adc h 73 | sub l 74 | ld h, a 75 | 76 | ; Increment page titles variable 77 | ld a, [wPageTitles] 78 | inc a 79 | ld [wPageTitles], a 80 | 81 | ; Check if end of screen is reached, loop if not 82 | ld a, h 83 | and $0F 84 | cp $0A 85 | jr nz, .titleLoadLoop 86 | .endTitleLoop 87 | 88 | ; Initialize Interrupts 89 | xor a 90 | ldh [rIF], a 91 | ld a, IEF_VBLANK 92 | ldh [rIE], a 93 | ld a, LCDCF_ON | LCDCF_BGON | LCDCF_BG8000 94 | ldh [rLCDC], a 95 | ei 96 | 97 | ; Restore ROM and cursor VRAM pointer 98 | pop bc 99 | ld hl, $9820 100 | 101 | SelectionMenuLoop:: 102 | halt 103 | 104 | ; Check input cooldown 105 | ld a, [wInputCooldown] 106 | and a 107 | jr z, .noInputCooldown 108 | 109 | ; Decrement input cooldown 110 | dec a 111 | ld [wInputCooldown], a 112 | jr SelectionMenuLoop 113 | 114 | .noInputCooldown 115 | ; Fetch Input 116 | ld a, P1F_GET_BTN 117 | ldh [rP1], a 118 | ldh a, [rP1] 119 | cpl 120 | and $0F 121 | swap a 122 | ld d, a 123 | ld a, P1F_GET_DPAD 124 | ldh [rP1], a 125 | ldh a, [rP1] 126 | cpl 127 | and $0F 128 | or d 129 | 130 | ; Check if input allowed, if not wait for input to be all low 131 | ld d, a 132 | ld a, [wAllowInput] 133 | and a 134 | jr nz, .inputAllowed 135 | xor a 136 | or d 137 | jr nz, SelectionMenuLoop 138 | inc a 139 | ld [wAllowInput], a 140 | .inputAllowed 141 | ld a, d 142 | 143 | ; Check for down button press 144 | bit 3, a 145 | jr z, .noDownPress 146 | 147 | ; Update cursor position 148 | ld a, 1 149 | jp UpdateCursorPosition 150 | 151 | .noDownPress 152 | ; Check for up button press 153 | bit 2, a 154 | jr z, .noUpPress 155 | 156 | ; Update cursor position 157 | ld a, $FF 158 | jp UpdateCursorPosition 159 | 160 | .noUpPress 161 | ; Check for left button press 162 | bit 1, a 163 | jr z, .noLeftPress 164 | 165 | ; Disable LCD 166 | xor a 167 | ld [rLCDC], a 168 | 169 | ; Reset input cooldown 170 | ld a, PageSwitchCooldownDuration 171 | ld [wInputCooldown], a 172 | 173 | ; Update page number and reload 174 | ld hl, wSelectionPage 175 | dec [hl] 176 | jp ReloadSelectionMenu 177 | 178 | .noLeftPress 179 | ; Check for right button press 180 | bit 0, a 181 | jr z, .noRightPress 182 | 183 | ; Disable LCD 184 | xor a 185 | ld [rLCDC], a 186 | 187 | ; Reset input cooldown 188 | ld a, PageSwitchCooldownDuration 189 | ld [wInputCooldown], a 190 | 191 | ; Update page number and reload 192 | ld hl, wSelectionPage 193 | inc [hl] 194 | jp ReloadSelectionMenu 195 | 196 | .noRightPress 197 | ; Check for Start/A button press 198 | bit 7, a 199 | jr nz, .startCurROM 200 | bit 4, a 201 | jr nz, .startCurROM 202 | jr .noStartAPress 203 | 204 | .startCurROM 205 | ; Disable LCD 206 | xor a 207 | ld [rLCDC], a 208 | 209 | ; Load HL with ROM pointer and start emulator 210 | ld a, [bc] 211 | inc bc 212 | ld l, a 213 | ld a, [bc] 214 | ld h, a 215 | jp StartROM 216 | 217 | .noStartAPress 218 | jp SelectionMenuLoop 219 | 220 | ; ------------------------------------------------------------------------------ 221 | ; Adds the value in A to the cursor position and updates the position 222 | ; ------------------------------------------------------------------------------ 223 | UpdateCursorPosition:: 224 | ; Preserve A, clear cursor at current position 225 | push af 226 | ld h, $00 227 | ld a, [wSelectionCursorPos] 228 | swap a 229 | sla a 230 | jr nc, .noOldCarry 231 | inc h 232 | .noOldCarry 233 | add $20 234 | ld l, a 235 | adc $98 236 | add h 237 | sub l 238 | ld h, a 239 | xor a 240 | ld [hl], a 241 | 242 | ; Calculate new cursor position 243 | pop af 244 | ld d, a 245 | ld a, [wPageTitles] 246 | ld e, a 247 | ld a, [wSelectionCursorPos] 248 | add d 249 | cp e 250 | jr c, .validRange 251 | xor a 252 | .validRange 253 | ld [wSelectionCursorPos], a 254 | 255 | ; Place cursor tile at new VRAM pointer 256 | ld h, $00 257 | swap a 258 | sla a 259 | jr nc, .noNewCarry 260 | inc h 261 | .noNewCarry 262 | add $20 263 | ld l, a 264 | adc $98 265 | sub l 266 | add h 267 | ld h, a 268 | ld a, CursorTileNo 269 | ld [hl], a 270 | 271 | ; Update input cooldown 272 | ld a, InputCooldownDuration 273 | ld [wInputCooldown], a 274 | 275 | ; Return to main loop 276 | jp SelectionMenuLoop 277 | 278 | ; ------------------------------------------------------------------------------ 279 | ; Starts up the emulator after initializing the ROM 280 | ; ------------------------------------------------------------------------------ 281 | StartROM:: 282 | ; Load HL with ROM table pointer + page offset 283 | ld hl, GameDataList 284 | ld a, [wSelectionPage] 285 | and a 286 | jr z, .skipPageOffset 287 | ld d, a 288 | .pageOffsetLoop 289 | ld a, $1E 290 | add l 291 | ld l, a 292 | adc h 293 | sub l 294 | ld h, a 295 | dec d 296 | jr nz, .pageOffsetLoop 297 | .skipPageOffset 298 | 299 | ; Add cursor offset 300 | ld a, [wSelectionCursorPos] 301 | add a 302 | add a, l 303 | ld l, a 304 | adc h 305 | sub l 306 | ld h, a 307 | 308 | ; Push ROM pointer to stack 309 | ld a, [hli] 310 | ld c, a 311 | ld a, [hl] 312 | ld b, a 313 | push bc 314 | 315 | ; Initialize Emulator Variables 316 | call InitSysvars 317 | call InitEmuVRAM 318 | 319 | ; Turn off Audio initially 320 | ld a, AUDENA_OFF 321 | ldh [rAUDENA], a 322 | 323 | ; Load ROM File 324 | pop hl 325 | push hl 326 | call InitROM 327 | pop hl 328 | call InitGameTitleDisplay 329 | 330 | ; Initialize Interrupts 331 | xor a 332 | ldh [rIF], a 333 | ld a, IEF_VBLANK 334 | ldh [rIE], a 335 | ld a, LCDCF_ON | LCDCF_BGON | LCDCF_BG8000 336 | ldh [rLCDC], a 337 | ei 338 | 339 | ; Initialize system variables and start emulator 340 | ld hl, wBaseMemory + $200 ; Load HL (= PC) with base pointer 341 | jp EmuLoop -------------------------------------------------------------------------------- /inc/hardware.inc: -------------------------------------------------------------------------------- 1 | ;* 2 | ;* Gameboy Hardware definitions 3 | ;* 4 | ;* Based on Jones' hardware.inc 5 | ;* And based on Carsten Sorensen's ideas. 6 | ;* 7 | ;* Rev 1.1 - 15-Jul-97 : Added define check 8 | ;* Rev 1.2 - 18-Jul-97 : Added revision check macro 9 | ;* Rev 1.3 - 19-Jul-97 : Modified for RGBASM V1.05 10 | ;* Rev 1.4 - 27-Jul-97 : Modified for new subroutine prefixes 11 | ;* Rev 1.5 - 15-Aug-97 : Added _HRAM, PAD, CART defines 12 | ;* : and Nintendo Logo 13 | ;* Rev 1.6 - 30-Nov-97 : Added rDIV, rTIMA, rTMA, & rTAC 14 | ;* Rev 1.7 - 31-Jan-98 : Added _SCRN0, _SCRN1 15 | ;* Rev 1.8 - 15-Feb-98 : Added rSB, rSC 16 | ;* Rev 1.9 - 16-Feb-98 : Converted I/O registers to $FFXX format 17 | ;* Rev 2.0 - : Added GBC registers 18 | ;* Rev 2.1 - : Added MBC5 & cart RAM enable/disable defines 19 | ;* Rev 2.2 - : Fixed NR42,NR43, & NR44 equates 20 | ;* Rev 2.3 - : Fixed incorrect _HRAM equate 21 | ;* Rev 2.4 - 27-Apr-13 : Added some cart defines (AntonioND) 22 | ;* Rev 2.5 - 03-May-15 : Fixed format (AntonioND) 23 | ;* Rev 2.6 - 09-Apr-16 : Added GBC OAM and cart defines (AntonioND) 24 | ;* Rev 2.7 - 19-Jan-19 : Added rPCMXX (ISSOtm) 25 | ;* Rev 2.8 - 03-Feb-19 : Added audio registers flags (Álvaro Cuesta) 26 | ;* Rev 2.9 - 28-Feb-20 : Added utility rP1 constants 27 | ;* Rev 3.0 - 27-Aug-20 : Register ordering, byte-based sizes, OAM additions, general cleanup (Blitter Object) 28 | 29 | ; If all of these are already defined, don't do it again. 30 | 31 | IF !DEF(HARDWARE_INC) 32 | HARDWARE_INC SET 1 33 | 34 | rev_Check_hardware_inc : MACRO 35 | ;NOTE: REVISION NUMBER CHANGES MUST BE ADDED 36 | ;TO SECOND PARAMETER IN FOLLOWING LINE. 37 | IF \1 > 3.0 ;PUT REVISION NUMBER HERE 38 | WARN "Version \1 or later of 'hardware.inc' is required." 39 | ENDC 40 | ENDM 41 | 42 | _VRAM EQU $8000 ; $8000->$9FFF 43 | _VRAM8000 EQU _VRAM 44 | _VRAM8800 EQU _VRAM+$800 45 | _VRAM9000 EQU _VRAM+$1000 46 | _SCRN0 EQU $9800 ; $9800->$9BFF 47 | _SCRN1 EQU $9C00 ; $9C00->$9FFF 48 | _SRAM EQU $A000 ; $A000->$BFFF 49 | _RAM EQU $C000 ; $C000->$CFFF / $C000->$DFFF 50 | _RAMBANK EQU $D000 ; $D000->$DFFF 51 | _OAMRAM EQU $FE00 ; $FE00->$FE9F 52 | _IO EQU $FF00 ; $FF00->$FF7F,$FFFF 53 | _AUD3WAVERAM EQU $FF30 ; $FF30->$FF3F 54 | _HRAM EQU $FF80 ; $FF80->$FFFE 55 | 56 | ; *** MBC5 Equates *** 57 | 58 | rRAMG EQU $0000 ; $0000->$1fff 59 | rROMB0 EQU $2000 ; $2000->$2fff 60 | rROMB1 EQU $3000 ; $3000->$3fff - If more than 256 ROM banks are present. 61 | rRAMB EQU $4000 ; $4000->$5fff - Bit 3 enables rumble (if present) 62 | 63 | 64 | ;*************************************************************************** 65 | ;* 66 | ;* Custom registers 67 | ;* 68 | ;*************************************************************************** 69 | 70 | ; -- 71 | ; -- P1 ($FF00) 72 | ; -- Register for reading joy pad info. (R/W) 73 | ; -- 74 | rP1 EQU $FF00 75 | 76 | P1F_5 EQU %00100000 ; P15 out port, set to 0 to get buttons 77 | P1F_4 EQU %00010000 ; P14 out port, set to 0 to get dpad 78 | P1F_3 EQU %00001000 ; P13 in port 79 | P1F_2 EQU %00000100 ; P12 in port 80 | P1F_1 EQU %00000010 ; P11 in port 81 | P1F_0 EQU %00000001 ; P10 in port 82 | 83 | P1F_GET_DPAD EQU P1F_5 84 | P1F_GET_BTN EQU P1F_4 85 | P1F_GET_NONE EQU P1F_4 | P1F_5 86 | 87 | 88 | ; -- 89 | ; -- SB ($FF01) 90 | ; -- Serial Transfer Data (R/W) 91 | ; -- 92 | rSB EQU $FF01 93 | 94 | 95 | ; -- 96 | ; -- SC ($FF02) 97 | ; -- Serial I/O Control (R/W) 98 | ; -- 99 | rSC EQU $FF02 100 | 101 | 102 | ; -- 103 | ; -- DIV ($FF04) 104 | ; -- Divider register (R/W) 105 | ; -- 106 | rDIV EQU $FF04 107 | 108 | 109 | ; -- 110 | ; -- TIMA ($FF05) 111 | ; -- Timer counter (R/W) 112 | ; -- 113 | rTIMA EQU $FF05 114 | 115 | 116 | ; -- 117 | ; -- TMA ($FF06) 118 | ; -- Timer modulo (R/W) 119 | ; -- 120 | rTMA EQU $FF06 121 | 122 | 123 | ; -- 124 | ; -- TAC ($FF07) 125 | ; -- Timer control (R/W) 126 | ; -- 127 | rTAC EQU $FF07 128 | 129 | TACF_START EQU %00000100 130 | TACF_STOP EQU %00000000 131 | TACF_4KHZ EQU %00000000 132 | TACF_16KHZ EQU %00000011 133 | TACF_65KHZ EQU %00000010 134 | TACF_262KHZ EQU %00000001 135 | 136 | 137 | ; -- 138 | ; -- IF ($FF0F) 139 | ; -- Interrupt Flag (R/W) 140 | ; -- 141 | rIF EQU $FF0F 142 | 143 | 144 | ; -- 145 | ; -- AUD1SWEEP/NR10 ($FF10) 146 | ; -- Sweep register (R/W) 147 | ; -- 148 | ; -- Bit 6-4 - Sweep Time 149 | ; -- Bit 3 - Sweep Increase/Decrease 150 | ; -- 0: Addition (frequency increases???) 151 | ; -- 1: Subtraction (frequency increases???) 152 | ; -- Bit 2-0 - Number of sweep shift (# 0-7) 153 | ; -- Sweep Time: (n*7.8ms) 154 | ; -- 155 | rNR10 EQU $FF10 156 | rAUD1SWEEP EQU rNR10 157 | 158 | AUD1SWEEP_UP EQU %00000000 159 | AUD1SWEEP_DOWN EQU %00001000 160 | 161 | 162 | ; -- 163 | ; -- AUD1LEN/NR11 ($FF11) 164 | ; -- Sound length/Wave pattern duty (R/W) 165 | ; -- 166 | ; -- Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) 167 | ; -- Bit 5-0 - Sound length data (# 0-63) 168 | ; -- 169 | rNR11 EQU $FF11 170 | rAUD1LEN EQU rNR11 171 | 172 | 173 | ; -- 174 | ; -- AUD1ENV/NR12 ($FF12) 175 | ; -- Envelope (R/W) 176 | ; -- 177 | ; -- Bit 7-4 - Initial value of envelope 178 | ; -- Bit 3 - Envelope UP/DOWN 179 | ; -- 0: Decrease 180 | ; -- 1: Range of increase 181 | ; -- Bit 2-0 - Number of envelope sweep (# 0-7) 182 | ; -- 183 | rNR12 EQU $FF12 184 | rAUD1ENV EQU rNR12 185 | 186 | 187 | ; -- 188 | ; -- AUD1LOW/NR13 ($FF13) 189 | ; -- Frequency low byte (W) 190 | ; -- 191 | rNR13 EQU $FF13 192 | rAUD1LOW EQU rNR13 193 | 194 | 195 | ; -- 196 | ; -- AUD1HIGH/NR14 ($FF14) 197 | ; -- Frequency high byte (W) 198 | ; -- 199 | ; -- Bit 7 - Initial (when set, sound restarts) 200 | ; -- Bit 6 - Counter/consecutive selection 201 | ; -- Bit 2-0 - Frequency's higher 3 bits 202 | ; -- 203 | rNR14 EQU $FF14 204 | rAUD1HIGH EQU rNR14 205 | 206 | 207 | ; -- 208 | ; -- AUD2LEN/NR21 ($FF16) 209 | ; -- Sound Length; Wave Pattern Duty (R/W) 210 | ; -- 211 | ; -- see AUD1LEN for info 212 | ; -- 213 | rNR21 EQU $FF16 214 | rAUD2LEN EQU rNR21 215 | 216 | 217 | ; -- 218 | ; -- AUD2ENV/NR22 ($FF17) 219 | ; -- Envelope (R/W) 220 | ; -- 221 | ; -- see AUD1ENV for info 222 | ; -- 223 | rNR22 EQU $FF17 224 | rAUD2ENV EQU rNR22 225 | 226 | 227 | ; -- 228 | ; -- AUD2LOW/NR23 ($FF18) 229 | ; -- Frequency low byte (W) 230 | ; -- 231 | rNR23 EQU $FF18 232 | rAUD2LOW EQU rNR23 233 | 234 | 235 | ; -- 236 | ; -- AUD2HIGH/NR24 ($FF19) 237 | ; -- Frequency high byte (W) 238 | ; -- 239 | ; -- see AUD1HIGH for info 240 | ; -- 241 | rNR24 EQU $FF19 242 | rAUD2HIGH EQU rNR24 243 | 244 | 245 | ; -- 246 | ; -- AUD3ENA/NR30 ($FF1A) 247 | ; -- Sound on/off (R/W) 248 | ; -- 249 | ; -- Bit 7 - Sound ON/OFF (1=ON,0=OFF) 250 | ; -- 251 | rNR30 EQU $FF1A 252 | rAUD3ENA EQU rNR30 253 | 254 | 255 | ; -- 256 | ; -- AUD3LEN/NR31 ($FF1B) 257 | ; -- Sound length (R/W) 258 | ; -- 259 | ; -- Bit 7-0 - Sound length 260 | ; -- 261 | rNR31 EQU $FF1B 262 | rAUD3LEN EQU rNR31 263 | 264 | 265 | ; -- 266 | ; -- AUD3LEVEL/NR32 ($FF1C) 267 | ; -- Select output level 268 | ; -- 269 | ; -- Bit 6-5 - Select output level 270 | ; -- 00: 0/1 (mute) 271 | ; -- 01: 1/1 272 | ; -- 10: 1/2 273 | ; -- 11: 1/4 274 | ; -- 275 | rNR32 EQU $FF1C 276 | rAUD3LEVEL EQU rNR32 277 | 278 | 279 | ; -- 280 | ; -- AUD3LOW/NR33 ($FF1D) 281 | ; -- Frequency low byte (W) 282 | ; -- 283 | ; -- see AUD1LOW for info 284 | ; -- 285 | rNR33 EQU $FF1D 286 | rAUD3LOW EQU rNR33 287 | 288 | 289 | ; -- 290 | ; -- AUD3HIGH/NR34 ($FF1E) 291 | ; -- Frequency high byte (W) 292 | ; -- 293 | ; -- see AUD1HIGH for info 294 | ; -- 295 | rNR34 EQU $FF1E 296 | rAUD3HIGH EQU rNR34 297 | 298 | 299 | ; -- 300 | ; -- AUD4LEN/NR41 ($FF20) 301 | ; -- Sound length (R/W) 302 | ; -- 303 | ; -- Bit 5-0 - Sound length data (# 0-63) 304 | ; -- 305 | rNR41 EQU $FF20 306 | rAUD4LEN EQU rNR41 307 | 308 | 309 | ; -- 310 | ; -- AUD4ENV/NR42 ($FF21) 311 | ; -- Envelope (R/W) 312 | ; -- 313 | ; -- see AUD1ENV for info 314 | ; -- 315 | rNR42 EQU $FF21 316 | rAUD4ENV EQU rNR42 317 | 318 | 319 | ; -- 320 | ; -- AUD4POLY/NR43 ($FF22) 321 | ; -- Polynomial counter (R/W) 322 | ; -- 323 | ; -- Bit 7-4 - Selection of the shift clock frequency of the (scf) 324 | ; -- polynomial counter (0000-1101) 325 | ; -- freq=drf*1/2^scf (not sure) 326 | ; -- Bit 3 - Selection of the polynomial counter's step 327 | ; -- 0: 15 steps 328 | ; -- 1: 7 steps 329 | ; -- Bit 2-0 - Selection of the dividing ratio of frequencies (drf) 330 | ; -- 000: f/4 001: f/8 010: f/16 011: f/24 331 | ; -- 100: f/32 101: f/40 110: f/48 111: f/56 (f=4.194304 Mhz) 332 | ; -- 333 | rNR43 EQU $FF22 334 | rAUD4POLY EQU rNR43 335 | 336 | 337 | ; -- 338 | ; -- AUD4GO/NR44 ($FF23) 339 | ; -- 340 | ; -- Bit 7 - Inital 341 | ; -- Bit 6 - Counter/consecutive selection 342 | ; -- 343 | rNR44 EQU $FF23 344 | rAUD4GO EQU rNR44 345 | 346 | 347 | ; -- 348 | ; -- AUDVOL/NR50 ($FF24) 349 | ; -- Channel control / ON-OFF / Volume (R/W) 350 | ; -- 351 | ; -- Bit 7 - Vin->SO2 ON/OFF (Vin??) 352 | ; -- Bit 6-4 - SO2 output level (volume) (# 0-7) 353 | ; -- Bit 3 - Vin->SO1 ON/OFF (Vin??) 354 | ; -- Bit 2-0 - SO1 output level (volume) (# 0-7) 355 | ; -- 356 | rNR50 EQU $FF24 357 | rAUDVOL EQU rNR50 358 | 359 | AUDVOL_VIN_LEFT EQU %10000000 ; SO2 360 | AUDVOL_VIN_RIGHT EQU %00001000 ; SO1 361 | 362 | 363 | ; -- 364 | ; -- AUDTERM/NR51 ($FF25) 365 | ; -- Selection of Sound output terminal (R/W) 366 | ; -- 367 | ; -- Bit 7 - Output sound 4 to SO2 terminal 368 | ; -- Bit 6 - Output sound 3 to SO2 terminal 369 | ; -- Bit 5 - Output sound 2 to SO2 terminal 370 | ; -- Bit 4 - Output sound 1 to SO2 terminal 371 | ; -- Bit 3 - Output sound 4 to SO1 terminal 372 | ; -- Bit 2 - Output sound 3 to SO1 terminal 373 | ; -- Bit 1 - Output sound 2 to SO1 terminal 374 | ; -- Bit 0 - Output sound 0 to SO1 terminal 375 | ; -- 376 | rNR51 EQU $FF25 377 | rAUDTERM EQU rNR51 378 | 379 | ; SO2 380 | AUDTERM_4_LEFT EQU %10000000 381 | AUDTERM_3_LEFT EQU %01000000 382 | AUDTERM_2_LEFT EQU %00100000 383 | AUDTERM_1_LEFT EQU %00010000 384 | ; SO1 385 | AUDTERM_4_RIGHT EQU %00001000 386 | AUDTERM_3_RIGHT EQU %00000100 387 | AUDTERM_2_RIGHT EQU %00000010 388 | AUDTERM_1_RIGHT EQU %00000001 389 | 390 | 391 | ; -- 392 | ; -- AUDENA/NR52 ($FF26) 393 | ; -- Sound on/off (R/W) 394 | ; -- 395 | ; -- Bit 7 - All sound on/off (sets all audio regs to 0!) 396 | ; -- Bit 3 - Sound 4 ON flag (read only) 397 | ; -- Bit 2 - Sound 3 ON flag (read only) 398 | ; -- Bit 1 - Sound 2 ON flag (read only) 399 | ; -- Bit 0 - Sound 1 ON flag (read only) 400 | ; -- 401 | rNR52 EQU $FF26 402 | rAUDENA EQU rNR52 403 | 404 | AUDENA_ON EQU %10000000 405 | AUDENA_OFF EQU %00000000 ; sets all audio regs to 0! 406 | 407 | 408 | ; -- 409 | ; -- LCDC ($FF40) 410 | ; -- LCD Control (R/W) 411 | ; -- 412 | rLCDC EQU $FF40 413 | 414 | LCDCF_OFF EQU %00000000 ; LCD Control Operation 415 | LCDCF_ON EQU %10000000 ; LCD Control Operation 416 | LCDCF_WIN9800 EQU %00000000 ; Window Tile Map Display Select 417 | LCDCF_WIN9C00 EQU %01000000 ; Window Tile Map Display Select 418 | LCDCF_WINOFF EQU %00000000 ; Window Display 419 | LCDCF_WINON EQU %00100000 ; Window Display 420 | LCDCF_BG8800 EQU %00000000 ; BG & Window Tile Data Select 421 | LCDCF_BG8000 EQU %00010000 ; BG & Window Tile Data Select 422 | LCDCF_BG9800 EQU %00000000 ; BG Tile Map Display Select 423 | LCDCF_BG9C00 EQU %00001000 ; BG Tile Map Display Select 424 | LCDCF_OBJ8 EQU %00000000 ; OBJ Construction 425 | LCDCF_OBJ16 EQU %00000100 ; OBJ Construction 426 | LCDCF_OBJOFF EQU %00000000 ; OBJ Display 427 | LCDCF_OBJON EQU %00000010 ; OBJ Display 428 | LCDCF_BGOFF EQU %00000000 ; BG Display 429 | LCDCF_BGON EQU %00000001 ; BG Display 430 | ; "Window Character Data Select" follows BG 431 | 432 | 433 | ; -- 434 | ; -- STAT ($FF41) 435 | ; -- LCDC Status (R/W) 436 | ; -- 437 | rSTAT EQU $FF41 438 | 439 | STATF_LYC EQU %01000000 ; LYC=LY Coincidence (Selectable) 440 | STATF_MODE10 EQU %00100000 ; Mode 10 441 | STATF_MODE01 EQU %00010000 ; Mode 01 (V-Blank) 442 | STATF_MODE00 EQU %00001000 ; Mode 00 (H-Blank) 443 | STATF_LYCF EQU %00000100 ; Coincidence Flag 444 | STATF_HBL EQU %00000000 ; H-Blank 445 | STATF_VBL EQU %00000001 ; V-Blank 446 | STATF_OAM EQU %00000010 ; OAM-RAM is used by system 447 | STATF_LCD EQU %00000011 ; Both OAM and VRAM used by system 448 | STATF_BUSY EQU %00000010 ; When set, VRAM access is unsafe 449 | 450 | 451 | ; -- 452 | ; -- SCY ($FF42) 453 | ; -- Scroll Y (R/W) 454 | ; -- 455 | rSCY EQU $FF42 456 | 457 | 458 | ; -- 459 | ; -- SCY ($FF43) 460 | ; -- Scroll X (R/W) 461 | ; -- 462 | rSCX EQU $FF43 463 | 464 | 465 | ; -- 466 | ; -- LY ($FF44) 467 | ; -- LCDC Y-Coordinate (R) 468 | ; -- 469 | ; -- Values range from 0->153. 144->153 is the VBlank period. 470 | ; -- 471 | rLY EQU $FF44 472 | 473 | 474 | ; -- 475 | ; -- LYC ($FF45) 476 | ; -- LY Compare (R/W) 477 | ; -- 478 | ; -- When LY==LYC, STATF_LYCF will be set in STAT 479 | ; -- 480 | rLYC EQU $FF45 481 | 482 | 483 | ; -- 484 | ; -- DMA ($FF46) 485 | ; -- DMA Transfer and Start Address (W) 486 | ; -- 487 | rDMA EQU $FF46 488 | 489 | 490 | ; -- 491 | ; -- BGP ($FF47) 492 | ; -- BG Palette Data (W) 493 | ; -- 494 | ; -- Bit 7-6 - Intensity for %11 495 | ; -- Bit 5-4 - Intensity for %10 496 | ; -- Bit 3-2 - Intensity for %01 497 | ; -- Bit 1-0 - Intensity for %00 498 | ; -- 499 | rBGP EQU $FF47 500 | 501 | 502 | ; -- 503 | ; -- OBP0 ($FF48) 504 | ; -- Object Palette 0 Data (W) 505 | ; -- 506 | ; -- See BGP for info 507 | ; -- 508 | rOBP0 EQU $FF48 509 | 510 | 511 | ; -- 512 | ; -- OBP1 ($FF49) 513 | ; -- Object Palette 1 Data (W) 514 | ; -- 515 | ; -- See BGP for info 516 | ; -- 517 | rOBP1 EQU $FF49 518 | 519 | 520 | ; -- 521 | ; -- WY ($FF4A) 522 | ; -- Window Y Position (R/W) 523 | ; -- 524 | ; -- 0 <= WY <= 143 525 | ; -- When WY = 0, the window is displayed from the top edge of the LCD screen. 526 | ; -- 527 | rWY EQU $FF4A 528 | 529 | 530 | ; -- 531 | ; -- WX ($FF4B) 532 | ; -- Window X Position (R/W) 533 | ; -- 534 | ; -- 7 <= WX <= 166 535 | ; -- When WX = 7, the window is displayed from the left edge of the LCD screen. 536 | ; -- Values of 0-6 and 166 are unreliable due to hardware bugs. 537 | ; -- 538 | rWX EQU $FF4B 539 | 540 | 541 | ; -- 542 | ; -- SPEED ($FF4D) 543 | ; -- Select CPU Speed (R/W) 544 | ; -- 545 | rKEY1 EQU $FF4D 546 | rSPD EQU rKEY1 547 | 548 | KEY1F_DBLSPEED EQU %10000000 ; 0=Normal Speed, 1=Double Speed (R) 549 | KEY1F_PREPARE EQU %00000001 ; 0=No, 1=Prepare (R/W) 550 | 551 | 552 | ; -- 553 | ; -- VBK ($FF4F) 554 | ; -- Select Video RAM Bank (R/W) 555 | ; -- 556 | ; -- Bit 0 - Bank Specification (0: Specify Bank 0; 1: Specify Bank 1) 557 | ; -- 558 | rVBK EQU $FF4F 559 | 560 | 561 | ; -- 562 | ; -- HDMA1 ($FF51) 563 | ; -- High byte for Horizontal Blanking/General Purpose DMA source address (W) 564 | ; -- CGB Mode Only 565 | ; -- 566 | rHDMA1 EQU $FF51 567 | 568 | 569 | ; -- 570 | ; -- HDMA2 ($FF52) 571 | ; -- Low byte for Horizontal Blanking/General Purpose DMA source address (W) 572 | ; -- CGB Mode Only 573 | ; -- 574 | rHDMA2 EQU $FF52 575 | 576 | 577 | ; -- 578 | ; -- HDMA3 ($FF53) 579 | ; -- High byte for Horizontal Blanking/General Purpose DMA destination address (W) 580 | ; -- CGB Mode Only 581 | ; -- 582 | rHDMA3 EQU $FF53 583 | 584 | 585 | ; -- 586 | ; -- HDMA4 ($FF54) 587 | ; -- Low byte for Horizontal Blanking/General Purpose DMA destination address (W) 588 | ; -- CGB Mode Only 589 | ; -- 590 | rHDMA4 EQU $FF54 591 | 592 | 593 | ; -- 594 | ; -- HDMA5 ($FF55) 595 | ; -- Transfer length (in tiles minus 1)/mode/start for Horizontal Blanking, General Purpose DMA (R/W) 596 | ; -- CGB Mode Only 597 | ; -- 598 | rHDMA5 EQU $FF55 599 | 600 | HDMA5F_MODE_GP EQU %00000000 ; General Purpose DMA (W) 601 | HDMA5F_MODE_HBL EQU %10000000 ; HBlank DMA (W) 602 | 603 | ; -- Once DMA has started, use HDMA5F_BUSY to check when the transfer is complete 604 | HDMA5F_BUSY EQU %10000000 ; 0=Busy (DMA still in progress), 1=Transfer complete (R) 605 | 606 | 607 | ; -- 608 | ; -- RP ($FF56) 609 | ; -- Infrared Communications Port (R/W) 610 | ; -- 611 | rRP EQU $FF56 612 | 613 | 614 | ; -- 615 | ; -- BCPS ($FF68) 616 | ; -- Background Color Palette Specification (R/W) 617 | ; -- 618 | rBCPS EQU $FF68 619 | 620 | BCPSF_AUTOINC EQU %10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) 621 | 622 | 623 | ; -- 624 | ; -- BCPD ($FF69) 625 | ; -- Background Color Palette Data (R/W) 626 | ; -- 627 | rBCPD EQU $FF69 628 | 629 | 630 | ; -- 631 | ; -- OCPS ($FF6A) 632 | ; -- Object Color Palette Specification (R/W) 633 | ; -- 634 | rOCPS EQU $FF6A 635 | 636 | OCPSF_AUTOINC EQU %10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) 637 | 638 | 639 | ; -- 640 | ; -- OCPD ($FF6B) 641 | ; -- Object Color Palette Data (R/W) 642 | ; -- 643 | rOCPD EQU $FF6B 644 | 645 | 646 | ; -- 647 | ; -- SMBK/SVBK ($FF70) 648 | ; -- Select Main RAM Bank (R/W) 649 | ; -- 650 | ; -- Bit 2-0 - Bank Specification (0,1: Specify Bank 1; 2-7: Specify Banks 2-7) 651 | ; -- 652 | rSVBK EQU $FF70 653 | rSMBK EQU rSVBK 654 | 655 | 656 | ; -- 657 | ; -- PCM12 ($FF76) 658 | ; -- Sound channel 1&2 PCM amplitude (R) 659 | ; -- 660 | ; -- Bit 7-4 - Copy of sound channel 2's PCM amplitude 661 | ; -- Bit 3-0 - Copy of sound channel 1's PCM amplitude 662 | ; -- 663 | rPCM12 EQU $FF76 664 | 665 | 666 | ; -- 667 | ; -- PCM34 ($FF77) 668 | ; -- Sound channel 3&4 PCM amplitude (R) 669 | ; -- 670 | ; -- Bit 7-4 - Copy of sound channel 4's PCM amplitude 671 | ; -- Bit 3-0 - Copy of sound channel 3's PCM amplitude 672 | ; -- 673 | rPCM34 EQU $FF77 674 | 675 | 676 | ; -- 677 | ; -- IE ($FFFF) 678 | ; -- Interrupt Enable (R/W) 679 | ; -- 680 | rIE EQU $FFFF 681 | 682 | IEF_HILO EQU %00010000 ; Transition from High to Low of Pin number P10-P13 683 | IEF_SERIAL EQU %00001000 ; Serial I/O transfer end 684 | IEF_TIMER EQU %00000100 ; Timer Overflow 685 | IEF_LCDC EQU %00000010 ; LCDC (see STAT) 686 | IEF_VBLANK EQU %00000001 ; V-Blank 687 | 688 | 689 | ;*************************************************************************** 690 | ;* 691 | ;* Flags common to multiple sound channels 692 | ;* 693 | ;*************************************************************************** 694 | 695 | ; -- 696 | ; -- Square wave duty cycle 697 | ; -- 698 | ; -- Can be used with AUD1LEN and AUD2LEN 699 | ; -- See AUD1LEN for more info 700 | ; -- 701 | AUDLEN_DUTY_12_5 EQU %00000000 ; 12.5% 702 | AUDLEN_DUTY_25 EQU %01000000 ; 25% 703 | AUDLEN_DUTY_50 EQU %10000000 ; 50% 704 | AUDLEN_DUTY_75 EQU %11000000 ; 75% 705 | 706 | 707 | ; -- 708 | ; -- Audio envelope flags 709 | ; -- 710 | ; -- Can be used with AUD1ENV, AUD2ENV, AUD4ENV 711 | ; -- See AUD1ENV for more info 712 | ; -- 713 | AUDENV_UP EQU %00001000 714 | AUDENV_DOWN EQU %00000000 715 | 716 | 717 | ; -- 718 | ; -- Audio trigger flags 719 | ; -- 720 | ; -- Can be used with AUD1HIGH, AUD2HIGH, AUD3HIGH 721 | ; -- See AUD1HIGH for more info 722 | ; -- 723 | 724 | AUDHIGH_RESTART EQU %10000000 725 | AUDHIGH_LENGTH_ON EQU %01000000 726 | AUDHIGH_LENGTH_OFF EQU %00000000 727 | 728 | 729 | ;*************************************************************************** 730 | ;* 731 | ;* CPU values on bootup (a=type, b=qualifier) 732 | ;* 733 | ;*************************************************************************** 734 | 735 | BOOTUP_A_DMG EQU $01 ; Dot Matrix Game 736 | BOOTUP_A_CGB EQU $11 ; Color GameBoy 737 | BOOTUP_A_MGB EQU $FF ; Mini GameBoy (Pocket GameBoy) 738 | 739 | ; if a=BOOTUP_A_CGB, bit 0 in b can be checked to determine if real CGB or 740 | ; other system running in GBC mode 741 | BOOTUP_B_CGB EQU %00000000 742 | BOOTUP_B_AGB EQU %00000001 ; GBA, GBA SP, Game Boy Player, or New GBA SP 743 | 744 | 745 | ;*************************************************************************** 746 | ;* 747 | ;* Cart related 748 | ;* 749 | ;*************************************************************************** 750 | 751 | ; $0143 Color GameBoy compatibility code 752 | CART_COMPATIBLE_DMG EQU $00 753 | CART_COMPATIBLE_DMG_GBC EQU $80 754 | CART_COMPATIBLE_GBC EQU $C0 755 | 756 | ; $0146 GameBoy/Super GameBoy indicator 757 | CART_INDICATOR_GB EQU $00 758 | CART_INDICATOR_SGB EQU $03 759 | 760 | ; $0147 Cartridge type 761 | CART_ROM EQU $00 762 | CART_ROM_MBC1 EQU $01 763 | CART_ROM_MBC1_RAM EQU $02 764 | CART_ROM_MBC1_RAM_BAT EQU $03 765 | CART_ROM_MBC2 EQU $05 766 | CART_ROM_MBC2_BAT EQU $06 767 | CART_ROM_RAM EQU $08 768 | CART_ROM_RAM_BAT EQU $09 769 | CART_ROM_MMM01 EQU $0B 770 | CART_ROM_MMM01_RAM EQU $0C 771 | CART_ROM_MMM01_RAM_BAT EQU $0D 772 | CART_ROM_MBC3_BAT_RTC EQU $0F 773 | CART_ROM_MBC3_RAM_BAT_RTC EQU $10 774 | CART_ROM_MBC3 EQU $11 775 | CART_ROM_MBC3_RAM EQU $12 776 | CART_ROM_MBC3_RAM_BAT EQU $13 777 | CART_ROM_MBC5 EQU $19 778 | CART_ROM_MBC5_BAT EQU $1A 779 | CART_ROM_MBC5_RAM_BAT EQU $1B 780 | CART_ROM_MBC5_RUMBLE EQU $1C 781 | CART_ROM_MBC5_RAM_RUMBLE EQU $1D 782 | CART_ROM_MBC5_RAM_BAT_RUMBLE EQU $1E 783 | CART_ROM_MBC7_RAM_BAT_GYRO EQU $22 784 | CART_ROM_POCKET_CAMERA EQU $FC 785 | CART_ROM_BANDAI_TAMA5 EQU $FD 786 | CART_ROM_HUDSON_HUC3 EQU $FE 787 | CART_ROM_HUDSON_HUC1 EQU $FF 788 | 789 | ; $0148 ROM size 790 | ; these are kilobytes 791 | CART_ROM_32KB EQU $00 ; 2 banks 792 | CART_ROM_64KB EQU $01 ; 4 banks 793 | CART_ROM_128KB EQU $02 ; 8 banks 794 | CART_ROM_256KB EQU $03 ; 16 banks 795 | CART_ROM_512KB EQU $04 ; 32 banks 796 | CART_ROM_1024KB EQU $05 ; 64 banks 797 | CART_ROM_2048KB EQU $06 ; 128 banks 798 | CART_ROM_4096KB EQU $07 ; 256 banks 799 | CART_ROM_8192KB EQU $08 ; 512 banks 800 | CART_ROM_1152KB EQU $52 ; 72 banks 801 | CART_ROM_1280KB EQU $53 ; 80 banks 802 | CART_ROM_1536KB EQU $54 ; 96 banks 803 | 804 | ; $0149 SRAM size 805 | ; these are kilobytes 806 | CART_SRAM_NONE EQU 0 807 | CART_SRAM_2KB EQU 1 ; 1 incomplete bank 808 | CART_SRAM_8KB EQU 2 ; 1 bank 809 | CART_SRAM_32KB EQU 3 ; 4 banks 810 | CART_SRAM_128KB EQU 4 ; 16 banks 811 | 812 | CART_SRAM_ENABLE EQU $0A 813 | CART_SRAM_DISABLE EQU $00 814 | 815 | ; $014A Destination code 816 | CART_DEST_JAPANESE EQU $00 817 | CART_DEST_NON_JAPANESE EQU $01 818 | 819 | 820 | ;*************************************************************************** 821 | ;* 822 | ;* Keypad related 823 | ;* 824 | ;*************************************************************************** 825 | 826 | PADF_DOWN EQU $80 827 | PADF_UP EQU $40 828 | PADF_LEFT EQU $20 829 | PADF_RIGHT EQU $10 830 | PADF_START EQU $08 831 | PADF_SELECT EQU $04 832 | PADF_B EQU $02 833 | PADF_A EQU $01 834 | 835 | PADB_DOWN EQU $7 836 | PADB_UP EQU $6 837 | PADB_LEFT EQU $5 838 | PADB_RIGHT EQU $4 839 | PADB_START EQU $3 840 | PADB_SELECT EQU $2 841 | PADB_B EQU $1 842 | PADB_A EQU $0 843 | 844 | 845 | ;*************************************************************************** 846 | ;* 847 | ;* Screen related 848 | ;* 849 | ;*************************************************************************** 850 | 851 | SCRN_X EQU 160 ; Width of screen in pixels 852 | SCRN_Y EQU 144 ; Height of screen in pixels 853 | SCRN_X_B EQU 20 ; Width of screen in bytes 854 | SCRN_Y_B EQU 18 ; Height of screen in bytes 855 | 856 | SCRN_VX EQU 256 ; Virtual width of screen in pixels 857 | SCRN_VY EQU 256 ; Virtual height of screen in pixels 858 | SCRN_VX_B EQU 32 ; Virtual width of screen in bytes 859 | SCRN_VY_B EQU 32 ; Virtual height of screen in bytes 860 | 861 | 862 | ;*************************************************************************** 863 | ;* 864 | ;* OAM related 865 | ;* 866 | ;*************************************************************************** 867 | 868 | ; OAM attributes 869 | ; each entry in OAM RAM is 4 bytes (sizeof_OAM_ATTRS) 870 | RSRESET 871 | OAMA_Y RB 1 ; y pos 872 | OAMA_X RB 1 ; x pos 873 | OAMA_TILEID RB 1 ; tile id 874 | OAMA_FLAGS RB 1 ; flags (see below) 875 | sizeof_OAM_ATTRS RB 0 876 | 877 | OAM_COUNT EQU 40 ; number of OAM entries in OAM RAM 878 | 879 | ; flags 880 | OAMF_PRI EQU %10000000 ; Priority 881 | OAMF_YFLIP EQU %01000000 ; Y flip 882 | OAMF_XFLIP EQU %00100000 ; X flip 883 | OAMF_PAL0 EQU %00000000 ; Palette number; 0,1 (DMG) 884 | OAMF_PAL1 EQU %00010000 ; Palette number; 0,1 (DMG) 885 | OAMF_BANK0 EQU %00000000 ; Bank number; 0,1 (GBC) 886 | OAMF_BANK1 EQU %00001000 ; Bank number; 0,1 (GBC) 887 | 888 | OAMF_PALMASK EQU %00000111 ; Palette (GBC) 889 | 890 | OAMB_PRI EQU 7 ; Priority 891 | OAMB_YFLIP EQU 6 ; Y flip 892 | OAMB_XFLIP EQU 5 ; X flip 893 | OAMB_PAL1 EQU 4 ; Palette number; 0,1 (DMG) 894 | OAMB_BANK1 EQU 3 ; Bank number; 0,1 (GBC) 895 | 896 | 897 | ;* 898 | ;* Nintendo scrolling logo 899 | ;* (Code won't work on a real GameBoy) 900 | ;* (if next lines are altered.) 901 | NINTENDO_LOGO : MACRO 902 | DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D 903 | DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 904 | DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E 905 | ENDM 906 | 907 | ENDC ;HARDWARE_INC 908 | -------------------------------------------------------------------------------- /src/emu.asm: -------------------------------------------------------------------------------- 1 | SECTION "Emulator Code", ROM0 2 | ; ------------------------------------------------------------------------------ 3 | ; Main loop of the emulator, runs a certain amount of emulated cycles each 4 | ; frame. 5 | ; ------------------------------------------------------------------------------ 6 | EmuLoop:: 7 | ; Check if cycles per frame have been exhausted 8 | ld a, [wCycleBuf] 9 | and a 10 | jr nz, .noScreenUpdate 11 | 12 | ; Wait for VBlank 13 | halt 14 | 15 | ; Update Input Register 16 | ld a, P1F_GET_BTN 17 | ldh [rP1], a 18 | ldh a, [rP1] 19 | cpl 20 | and $0F 21 | swap a 22 | ld d, a 23 | ld a, P1F_GET_DPAD 24 | ldh [rP1], a 25 | ldh a, [rP1] 26 | cpl 27 | and $0F 28 | or d 29 | ld [wInputData], a 30 | 31 | ; Check for reset combo 32 | and $F0 33 | cp $F0 34 | jp z, Restart 35 | 36 | .noReset 37 | 38 | ; Update Delay Timer 39 | ld a, [wRegDelay] 40 | and a 41 | jr z, .zeroDT 42 | dec a 43 | ld [wRegDelay], a 44 | .zeroDT 45 | 46 | ; Update Sound Timer 47 | ld a, [wRegSound] 48 | and a 49 | jr z, .skipUpdateST 50 | dec a 51 | ld [wRegSound], a 52 | jr nz, .skipUpdateST ; Don't disable sound if sound timer not zero 53 | 54 | ; Disable Sound 55 | ld a, AUDENA_OFF 56 | ldh [rAUDENA], a 57 | .skipUpdateST 58 | 59 | ; Reset cycle buffer 60 | ld a, [wCycleLimit] 61 | ld [wCycleBuf], a 62 | 63 | ; Check if display was updated 64 | ld a, [wUpdateDisplay] 65 | and a 66 | jr z, .noScreenUpdate 67 | 68 | ; Initialize HDMA 69 | ld a, HIGH(wBaseVRAM) 70 | ldh [rHDMA1], a 71 | ld a, LOW(wBaseVRAM) 72 | ldh [rHDMA2], a 73 | xor a 74 | ldh [rHDMA3], a 75 | ldh [rHDMA4], a 76 | ld a, (wEndVRAM - wBaseVRAM) / $10 - 1 77 | ldh [rHDMA5], a 78 | 79 | ; Reset display update flag 80 | xor a 81 | ld [wUpdateDisplay], a 82 | .noScreenUpdate 83 | 84 | ; Read instruction into BC 85 | ld a, [hli] 86 | ld b, a 87 | ld a, [hli] 88 | ld c, a 89 | push hl 90 | 91 | ; Load HL with jump table address 92 | ld a, b 93 | and $F0 94 | swap a 95 | add a 96 | add LOW(InstrJumpTable) 97 | ld l, a 98 | adc HIGH(InstrJumpTable) 99 | sub l 100 | ld h, a 101 | 102 | ; Load instruction routine pointer into HL 103 | ld a, [hli] 104 | ld e, a 105 | ld a, [hl] 106 | ld h, a 107 | ld l, e 108 | 109 | ; Decrement cycle buffer 110 | ld a, [wCycleBuf] 111 | dec a 112 | ld [wCycleBuf], a 113 | 114 | ; Jump to instruction routine 115 | jp hl 116 | 117 | ; ------------------------------------------------------------------------------ 118 | ; Placeholder for unimplemented instructions - does absolutely nothing other 119 | ; than triggering an emulator breakpoint using LD b, b. 120 | ; ------------------------------------------------------------------------------ 121 | DummyInstruction:: 122 | pop hl 123 | ld b, b 124 | jp EmuLoop 125 | 126 | ; ------------------------------------------------------------------------------ 127 | ; Checks the low byte of the instruction to check what to actually do. 128 | ; ------------------------------------------------------------------------------ 129 | ZeroByteInstruction:: 130 | ld a, c 131 | cp $E0 132 | jr z, ClearDisplayInstruction 133 | cp $EE 134 | jr z, ReturnInstruction 135 | 136 | pop hl 137 | jp EmuLoop 138 | 139 | ; ------------------------------------------------------------------------------ 140 | ; 00EE - RET 141 | ; Return from a subroutine. 142 | ; 143 | ; The interpreter sets the program counter to the address at the top of the 144 | ; stack, then subtracts 1 from the stack pointer. 145 | ; ------------------------------------------------------------------------------ 146 | ReturnInstruction:: 147 | ; Pop off Emulated stack onto HL 148 | call EmuPop 149 | pop hl 150 | ld h, d 151 | ld l, e 152 | jp EmuLoop 153 | 154 | ; ------------------------------------------------------------------------------ 155 | ; 00E0 - CLS 156 | ; Clear the display. 157 | ; ------------------------------------------------------------------------------ 158 | ClearDisplayInstruction:: 159 | ld hl, wBaseVRAM 160 | ld bc, wEndVRAM - wBaseVRAM 161 | .clsLoop 162 | xor a 163 | ld [hli], a 164 | dec bc 165 | ld a, b 166 | or c 167 | jr nz, .clsLoop 168 | 169 | pop hl 170 | jp EmuLoop 171 | 172 | ; ------------------------------------------------------------------------------ 173 | ; 1nnn - JP addr 174 | ; Jump to location nnn. 175 | ; ------------------------------------------------------------------------------ 176 | JumpInstruction:: 177 | pop hl 178 | ld a, b 179 | and $0F 180 | add HIGH(wBaseMemory) 181 | ld h, a 182 | ld l, c 183 | jp EmuLoop 184 | 185 | ; ------------------------------------------------------------------------------ 186 | ; 2nnn - CALL addr 187 | ; Call subroutine at nnn. 188 | ; ------------------------------------------------------------------------------ 189 | CallInstruction:: 190 | pop hl 191 | ld d, h 192 | ld e, l 193 | call EmuPush 194 | ld a, b 195 | and $0F 196 | add HIGH(wBaseMemory) 197 | ld h, a 198 | ld l, c 199 | jp EmuLoop 200 | 201 | ; ------------------------------------------------------------------------------ 202 | ; 3xkk - SE Vx, byte 203 | ; Skip next instruction if Vx = kk. 204 | ; ------------------------------------------------------------------------------ 205 | SkipEqualInstruction:: 206 | ld a, b 207 | call EmuRegRead 208 | pop hl 209 | cp c 210 | jr nz, .notEqual 211 | inc hl 212 | inc hl 213 | .notEqual 214 | jp EmuLoop 215 | 216 | ; ------------------------------------------------------------------------------ 217 | ; 4xkk - SE Vx, byte 218 | ; Skip next instruction if Vx != kk. 219 | ; ------------------------------------------------------------------------------ 220 | SkipNotEqualInstruction:: 221 | ld a, b 222 | call EmuRegRead 223 | pop hl 224 | cp c 225 | jr z, .isEqual 226 | inc hl 227 | inc hl 228 | .isEqual 229 | jp EmuLoop 230 | 231 | ; ------------------------------------------------------------------------------ 232 | ; 5xy0 - SE Vx, Vy 233 | ; Skip next instruction if Vx = Vy. 234 | ; ------------------------------------------------------------------------------ 235 | SkipEqualRegisterInstruction:: 236 | ld a, b 237 | call EmuRegRead 238 | ld d, a 239 | ld a, c 240 | swap a 241 | call EmuRegRead 242 | pop hl 243 | cp d 244 | jr nz, .notEqual 245 | inc hl 246 | inc hl 247 | .notEqual 248 | jp EmuLoop 249 | 250 | ; ------------------------------------------------------------------------------ 251 | ; 6xkk - LD Vx, byte 252 | ; Set Vx = kk. 253 | ; ------------------------------------------------------------------------------ 254 | LoadInstruction:: 255 | ld a, b 256 | and $0F 257 | ld l, a 258 | ld a, HIGH(wRegV) 259 | ld h, a 260 | ld [hl], c 261 | pop hl 262 | jp EmuLoop 263 | 264 | ; ------------------------------------------------------------------------------ 265 | ; 7xkk - ADD Vx, byte 266 | ; Set Vx = Vx + kk. 267 | ; ------------------------------------------------------------------------------ 268 | AddInstruction:: 269 | ld a, b 270 | call EmuRegRead 271 | add c 272 | ld [hl], a 273 | pop hl 274 | jp EmuLoop 275 | 276 | ; ------------------------------------------------------------------------------ 277 | ; Loads DE with Vx and Vy and calls the proper subroutine for any 8xyz instruction. 278 | ; ------------------------------------------------------------------------------ 279 | ArithmeticInstruction:: 280 | ; Get pointer to Jump Table 281 | ld hl, ArithmeticJumpTable 282 | ld a, c 283 | and $0F 284 | add a 285 | add l 286 | ld l, a 287 | adc h 288 | sub l 289 | ld h, a 290 | ld a, [hli] 291 | ld e, a 292 | ld a, [hl] 293 | ld d, a 294 | ld h, d 295 | ld l, e 296 | 297 | ; Load DE with Vx and Vy 298 | push hl 299 | ld a, c 300 | swap a 301 | call EmuRegRead 302 | ld e, a 303 | ld a, b 304 | call EmuRegRead 305 | ld d, a 306 | pop hl 307 | 308 | ; Jump to jump table vector 309 | jp hl 310 | 311 | ArithmeticJumpTable:: 312 | dw RegisterLoadInstruction 313 | dw ArithmeticOR 314 | dw ArithmeticAND 315 | dw ArithmeticXOR 316 | dw ArithmeticADD 317 | dw ArithmeticSUB 318 | dw ArithmeticSHR 319 | dw ArithmeticSUBN 320 | dw DummyInstruction 321 | dw DummyInstruction 322 | dw DummyInstruction 323 | dw DummyInstruction 324 | dw DummyInstruction 325 | dw DummyInstruction 326 | dw ArithmeticSHL 327 | dw DummyInstruction 328 | 329 | ; ------------------------------------------------------------------------------ 330 | ; 8xy0 - LD Vx, Vy 331 | ; Set Vx = Vy. 332 | ; ------------------------------------------------------------------------------ 333 | RegisterLoadInstruction:: 334 | ld a, b 335 | and $0F 336 | ld l, a 337 | ld a, HIGH(wRegV) 338 | ld h, a 339 | ld [hl], e 340 | pop hl 341 | jp EmuLoop 342 | 343 | ; ------------------------------------------------------------------------------ 344 | ; 8xy1 - OR Vx, Vy 345 | ; Set Vx = Vx OR Vy. 346 | ; ------------------------------------------------------------------------------ 347 | ArithmeticOR:: 348 | or e 349 | ld d, a 350 | ld a, b 351 | and $0F 352 | ld l, a 353 | ld a, HIGH(wRegV) 354 | ld h, a 355 | ld [hl], d 356 | pop hl 357 | jp EmuLoop 358 | 359 | ; ------------------------------------------------------------------------------ 360 | ; 8xy2 - AND Vx, Vy 361 | ; Set Vx = Vx AND Vy. 362 | ; ------------------------------------------------------------------------------ 363 | ArithmeticAND:: 364 | and e 365 | ld d, a 366 | ld a, b 367 | and $0F 368 | ld l, a 369 | ld a, HIGH(wRegV) 370 | ld h, a 371 | ld [hl], d 372 | pop hl 373 | jp EmuLoop 374 | 375 | ; ------------------------------------------------------------------------------ 376 | ; 8xy3 - XOR Vx, Vy 377 | ; Set Vx = Vx XOR Vy. 378 | ; ------------------------------------------------------------------------------ 379 | ArithmeticXOR:: 380 | xor e 381 | ld d, a 382 | ld a, b 383 | and $0F 384 | ld l, a 385 | ld a, HIGH(wRegV) 386 | ld h, a 387 | ld [hl], d 388 | pop hl 389 | jp EmuLoop 390 | 391 | ; ------------------------------------------------------------------------------ 392 | ; 8xy4 - ADD Vx, Vy 393 | ; Set Vx = Vx + Vy, set VF = carry. 394 | ; ------------------------------------------------------------------------------ 395 | ArithmeticADD:: 396 | ld a, b 397 | and $0F 398 | ld l, a 399 | ld a, HIGH(wRegV) 400 | ld h, a 401 | ld a, d 402 | add e 403 | ld [hl], a 404 | ld a, $0F 405 | ld l, a 406 | jr c, .setCarry 407 | ld a, 0 408 | jr .noCarry 409 | .setCarry 410 | ld a, 1 411 | .noCarry 412 | ld [hl], a 413 | pop hl 414 | jp EmuLoop 415 | 416 | ; ------------------------------------------------------------------------------ 417 | ; 8xy5 - SUB Vx, Vy 418 | ; Set Vx = Vx - Vy, set VF = NOT borrow. 419 | ; ------------------------------------------------------------------------------ 420 | ArithmeticSUB:: 421 | ld a, b 422 | and $0F 423 | ld l, a 424 | ld a, HIGH(wRegV) 425 | ld h, a 426 | ld a, d 427 | sub e 428 | ld [hl], a 429 | ld a, $0F 430 | ld l, a 431 | jr nc, .noBorrow 432 | ld a, 0 433 | jr .setBorrow 434 | .noBorrow 435 | ld a, 1 436 | .setBorrow 437 | ld [hl], a 438 | pop hl 439 | jp EmuLoop 440 | 441 | ; ------------------------------------------------------------------------------ 442 | ; 8xy6 - SHR Vx {, Vy} 443 | ; Set Vx = Vx SHR 1. 444 | ; ------------------------------------------------------------------------------ 445 | ArithmeticSHR:: 446 | ld a, b 447 | and $0F 448 | ld l, a 449 | ld a, HIGH(wRegV) 450 | ld h, a 451 | ld a, d 452 | srl a 453 | ld [hl], a 454 | ld a, $0F 455 | ld l, a 456 | jr c, .setLSB 457 | xor a 458 | jr .unsetLSB 459 | .setLSB 460 | ld a, 1 461 | .unsetLSB 462 | ld [hl], a 463 | pop hl 464 | jp EmuLoop 465 | 466 | ; ------------------------------------------------------------------------------ 467 | ; 8xy7 - SUBN Vx, Vy 468 | ; Set Vx = Vy - Vx, set VF = NOT borrow. 469 | ; ------------------------------------------------------------------------------ 470 | ArithmeticSUBN:: 471 | ld a, b 472 | and $0F 473 | ld l, a 474 | ld a, HIGH(wRegV) 475 | ld h, a 476 | ld a, e 477 | sub d 478 | ld [hl], a 479 | ld a, $0F 480 | ld l, a 481 | jr nc, .noBorrow 482 | ld a, 0 483 | jr .setBorrow 484 | .noBorrow 485 | ld a, 1 486 | .setBorrow 487 | ld [hl], a 488 | pop hl 489 | jp EmuLoop 490 | 491 | ; ------------------------------------------------------------------------------ 492 | ; 8xyE - SHL Vx {, Vy} 493 | ; Set Vx = Vx SHL 1. 494 | ; ------------------------------------------------------------------------------ 495 | ArithmeticSHL:: 496 | ld a, b 497 | and $0F 498 | ld l, a 499 | ld a, HIGH(wRegV) 500 | ld h, a 501 | ld a, d 502 | sla a 503 | ld [hl], a 504 | ld a, $0F 505 | ld l, a 506 | jr c, .setMSB 507 | xor a 508 | jr .unsetMSB 509 | .setMSB 510 | ld a, 1 511 | .unsetMSB 512 | ld [hl], a 513 | pop hl 514 | jp EmuLoop 515 | 516 | ; ------------------------------------------------------------------------------ 517 | ; 9xy0 - SNE Vx, Vy 518 | ; Skip next instruction if Vx != Vy. 519 | ; ------------------------------------------------------------------------------ 520 | SkipNotEqualRegisterInstruction:: 521 | ld a, b 522 | call EmuRegRead 523 | ld d, a 524 | ld a, c 525 | swap a 526 | call EmuRegRead 527 | pop hl 528 | cp d 529 | jr z, .isEqual 530 | inc hl 531 | inc hl 532 | .isEqual 533 | jp EmuLoop 534 | 535 | ; ------------------------------------------------------------------------------ 536 | ; Annn - LD I, addr 537 | ; Set I = nnn. 538 | ; ------------------------------------------------------------------------------ 539 | ILoadInstruction:: 540 | ld hl, wRegI 541 | ld a, b 542 | and $0F 543 | ld [hli], a 544 | ld [hl], c 545 | pop hl 546 | jp EmuLoop 547 | 548 | ; ------------------------------------------------------------------------------ 549 | ; Cxkk - RND Vx, byte 550 | ; Set Vx = random byte AND kk. 551 | ; ------------------------------------------------------------------------------ 552 | RandomInstruction:: 553 | ld a, b 554 | and $0F 555 | ld l, a 556 | ld h, HIGH(wRegV) 557 | 558 | ld a, c 559 | and $0F 560 | ld d, a 561 | ldh a, [rDIV] 562 | and d 563 | ld [hl], a 564 | 565 | pop hl 566 | jp EmuLoop 567 | 568 | ; ------------------------------------------------------------------------------ 569 | ; Dxyn - DRW Vx, Vy, nibble 570 | ; Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision. 571 | ; ------------------------------------------------------------------------------ 572 | DrawInstruction:: 573 | ; Load sprite size into RAM 574 | ld a, c 575 | and $0F 576 | inc a 577 | ld [wSpriteSize], a 578 | 579 | ; Load X into B and Y into C 580 | ld a, b 581 | call EmuRegRead 582 | and 63 ; Wrap X-coordinate around 64 583 | ld b, a 584 | ld a, c 585 | swap a 586 | call EmuRegRead 587 | and 31 ; Wrap Y-coordinate around 32 588 | ld c, a 589 | 590 | ; Load HL with VRAM base pointer 591 | ld hl, wBaseVRAM 592 | 593 | ; Add Y-offset (tile-granularity) 594 | ld a, c 595 | and %11111100 ; Divide Y-position by 4 596 | srl a 597 | srl a 598 | add h ; Add to H (one tile down = +256 bytes in VRAM) 599 | ld h, a 600 | 601 | ; Add Y-offset (line-granularity) 602 | ld a, c 603 | and %00000011 ; Modulo Y-position by 4 604 | add a ; Multiply by 4 (one emulated pixel row = 4 bytes) 605 | add a 606 | add l ; Add to HL 607 | ld l, a 608 | adc h 609 | sub l 610 | ld h, a 611 | 612 | ; Add X-offset (tile-granularity) 613 | ld a, b 614 | and %11111100 ; Integer-Divide X-position by 4 and implicitly multiply by 4 615 | sla a ; Multiply by 4 (total factor 16, since 16 bytes = one tile horizontally) 616 | sla a 617 | add l ; Add to HL 618 | ld l, a 619 | adc h 620 | sub l 621 | ld h, a 622 | 623 | ; Initialize bitmask in E and add X-offset (pixel-granularity) 624 | ld e, %11000000 625 | ld a, b 626 | and %00000011 ; Modulo X-position by 4 627 | jr z, .skipBitmaskInit 628 | ld d, a 629 | .bitmaskInitLoop ; Shift right twice for each pixel 630 | srl e 631 | srl e 632 | dec d 633 | jr nz, .bitmaskInitLoop 634 | .skipBitmaskInit 635 | 636 | ; Load sprite pointer into BC 637 | ld a, [wRegI+1] 638 | ld c, a 639 | ld a, [wRegI] 640 | and $0F 641 | or $C0 642 | ld b, a 643 | 644 | ; Initialize VF to zero 645 | xor a 646 | ld [wRegV+$F], a 647 | 648 | .spriteDrawLoop 649 | 650 | ; Decrement sprite size and exit loop if all bytes drawn 651 | ld a, [wSpriteSize] 652 | dec a 653 | ld [wSpriteSize], a 654 | jr z, .endSpriteDraw 655 | 656 | ; Preserve X-offset bitmask and load sprite data byte 657 | push de 658 | ld a, [bc] 659 | 660 | .lineRenderLoop 661 | 662 | ; Preserve rendering bits in D, check if currently rendering bit is set, skip if not 663 | ld d, a 664 | bit 7, a 665 | jr z, .spriteZeroBit 666 | 667 | ; Load VRAM byte, push collision check and XOR 668 | ld a, [hl] 669 | and e 670 | push af ; Zero flag unset if collision detected 671 | ld a, [hl] 672 | xor e 673 | 674 | ; Load XOR result into HL-row and next row (for 2x2 pixels) 675 | ld [hli], a 676 | inc hl 677 | ld [hld], a 678 | dec hl 679 | 680 | ; Check collision and update VF 681 | pop af 682 | jr z, .spriteZeroBit 683 | ld a, 1 684 | ld [wRegV+$F], a 685 | 686 | .spriteZeroBit 687 | 688 | ; Shift bitmask right by 2 and check for X-overflow 689 | srl e 690 | srl e 691 | jr nz, .noRenderOverflowX 692 | 693 | ; Add 16 to X-offset in RAM and HL 694 | ld a, [wSprOverflow] 695 | add 16 696 | ld [wSprOverflow], a 697 | ld a, 16 698 | add l 699 | ld l, a 700 | jr nc, .noHorizontalOverflow 701 | 702 | ; Account for horizontal overflow 703 | ld a, 1 704 | ld [wSprOverflowY], a 705 | 706 | .noHorizontalOverflow 707 | ; Reset bitmask 708 | ld e, %11000000 709 | 710 | .noRenderOverflowX 711 | 712 | ; Restore rendering bits and shift left, render next bit if any left 713 | ld a, d 714 | sla a 715 | jr nz, .lineRenderLoop 716 | 717 | ; If byte is fully drawn, increment pointers accordingly 718 | inc bc ; Next byte of sprite in RAM 719 | inc hl ; Two rows further down, 1 row = 2 bytes => +4 720 | inc hl 721 | inc hl 722 | inc hl 723 | 724 | ; Check if tile boundary was reached, skip adjustments if not 725 | ld a, l 726 | and $0F 727 | jr nz, .noRenderOverflowY 728 | 729 | ; Switch to next tile 730 | ld a, 15*16 731 | add l 732 | ld l, a 733 | adc h 734 | sub l 735 | 736 | ; Check for overflow 737 | cp HIGH(wBaseVRAM + $800) 738 | jr nz, .noFrameOverflow 739 | 740 | ; Subract $800 from HL 741 | sub 8 742 | 743 | .noFrameOverflow 744 | 745 | ; Store A in H register 746 | ld h, a 747 | 748 | .noRenderOverflowY 749 | 750 | ; Reset X-Tile-Overflow 751 | ld a, [wSprOverflow] 752 | ld d, a 753 | ld a, l 754 | sub d 755 | ld l, a 756 | jr nc, .noOverflowResetBorrow 757 | dec h 758 | .noOverflowResetBorrow 759 | xor a 760 | ld [wSprOverflow], a 761 | 762 | ; Account for horizontal frame overflow 763 | ld a, [wSprOverflowY] 764 | and a 765 | jr z, .noResetHorizontalFrameOverflow 766 | inc h 767 | xor a 768 | ld [wSprOverflowY], a 769 | .noResetHorizontalFrameOverflow 770 | 771 | ; Reset bitmask for new row and draw new line 772 | pop de 773 | jr .spriteDrawLoop 774 | 775 | .endSpriteDraw 776 | 777 | ; Set display update flag 778 | ld a, 1 779 | ld [wUpdateDisplay], a 780 | 781 | pop hl 782 | jp EmuLoop 783 | 784 | ; ------------------------------------------------------------------------------ 785 | ; Handles both of the following instructions: 786 | ; 787 | ; Ex9E - SKP Vx 788 | ; Skip next instruction if key with the value of Vx is pressed. 789 | ; 790 | ; ExA1 - SKNP Vx 791 | ; Skip next instruction if key with the value of Vx is not pressed. 792 | ; ------------------------------------------------------------------------------ 793 | InputSkipInstruction:: 794 | ld a, b 795 | call EmuRegRead 796 | 797 | ; Load Input Mask byte pointer 798 | add LOW(wInputMaskMap) 799 | ld l, a 800 | adc HIGH(wInputMaskMap) 801 | sub l 802 | ld h, a 803 | 804 | ; Load input state and check if button is pressed 805 | ; Zero Flag set = no, otherwise yes 806 | ld a, [wInputData] 807 | and [hl] 808 | jr nz, .isPressed 809 | 810 | .notPressed 811 | ld a, c 812 | cp $9E 813 | jr z, .noSkipInstruction 814 | jr .skipInstruction 815 | 816 | .isPressed 817 | ld a, c 818 | cp $9E 819 | jr z, .skipInstruction 820 | jr .noSkipInstruction 821 | 822 | .skipInstruction 823 | pop hl 824 | inc hl 825 | inc hl 826 | jp EmuLoop 827 | 828 | .noSkipInstruction 829 | pop hl 830 | jp EmuLoop 831 | 832 | ; ------------------------------------------------------------------------------ 833 | ; Jumps to the instruction handler for Fxxx instructions. 834 | ; ------------------------------------------------------------------------------ 835 | FInstruction:: 836 | ld a, c 837 | and $0F 838 | add a 839 | add LOW(FJumpTable) 840 | ld l, a 841 | adc HIGH(FJumpTable) 842 | sub l 843 | ld h, a 844 | 845 | ld a, [hli] 846 | ld e, a 847 | ld a, [hl] 848 | ld h, a 849 | ld l, e 850 | jp hl 851 | 852 | FJumpTable:: 853 | dw DummyInstruction 854 | dw DummyInstruction 855 | dw DummyInstruction 856 | dw BCDInstruction 857 | dw DummyInstruction 858 | dw F5Instruction 859 | dw DummyInstruction 860 | dw LoadRegDTInstruction 861 | dw LoadSTInstruction 862 | dw LoadDigitPtrInstruction 863 | dw WaitKeypressInstruction 864 | dw DummyInstruction 865 | dw DummyInstruction 866 | dw DummyInstruction 867 | dw IAddInstruction 868 | 869 | ; ------------------------------------------------------------------------------ 870 | ; Jumps to the instruction handler for Fxx5 instructions. 871 | ; ------------------------------------------------------------------------------ 872 | F5Instruction:: 873 | ld a, c 874 | swap a 875 | and $0F 876 | add a 877 | add LOW(F5JumpTable) 878 | ld l, a 879 | adc HIGH(F5JumpTable) 880 | sub l 881 | ld h, a 882 | 883 | ld a, [hli] 884 | ld e, a 885 | ld a, [hl] 886 | ld h, a 887 | ld l, e 888 | jp hl 889 | 890 | F5JumpTable:: 891 | dw DummyInstruction 892 | dw LoadDTInstruction 893 | dw DummyInstruction 894 | dw DummyInstruction 895 | dw DummyInstruction 896 | dw StoreRegistersInstruction 897 | dw LoadRegistersInstruction 898 | 899 | ; ------------------------------------------------------------------------------ 900 | ; Fx07 - LD Vx, DT 901 | ; Set Vx = delay timer value. 902 | ; ------------------------------------------------------------------------------ 903 | LoadRegDTInstruction:: 904 | ld a, b 905 | and $0F 906 | ld l, a 907 | ld a, HIGH(wRegV) 908 | ld h, a 909 | ld a, [wRegDelay] 910 | ld [hl], a 911 | 912 | pop hl 913 | jp EmuLoop 914 | 915 | ; ------------------------------------------------------------------------------ 916 | ; Fx18 - LD ST, Vx 917 | ; Set sound timer = Vx. 918 | ; ------------------------------------------------------------------------------ 919 | LoadSTInstruction:: 920 | ld a, b 921 | call EmuRegRead 922 | ld [wRegSound], a 923 | and a 924 | jr z, .skipEnableSound 925 | 926 | ; Enable Audio Output 927 | ld a, AUDENA_ON 928 | ldh [rAUDENA], a 929 | ld a, $FF 930 | ldh [rAUDVOL], a 931 | ldh [rAUDTERM], a 932 | ldh [rNR22], a 933 | ld a, %10000000 934 | ldh [rNR21], a 935 | xor a 936 | ldh [rNR23], a 937 | ld a, %10000100 938 | ldh [rNR24], a 939 | 940 | .skipEnableSound 941 | pop hl 942 | jp EmuLoop 943 | 944 | ; ------------------------------------------------------------------------------ 945 | ; Fx15 - LD DT, Vx 946 | ; Set delay timer = Vx. 947 | ; ------------------------------------------------------------------------------ 948 | LoadDTInstruction:: 949 | ld a, b 950 | call EmuRegRead 951 | ld [wRegDelay], a 952 | 953 | pop hl 954 | jp EmuLoop 955 | 956 | ; ------------------------------------------------------------------------------ 957 | ; Fx1E - ADD I, Vx 958 | ; Set I = I + Vx. 959 | ; ------------------------------------------------------------------------------ 960 | IAddInstruction:: 961 | ld a, b 962 | call EmuRegRead 963 | ld d, a 964 | ld a, [wRegI+1] 965 | add d 966 | ld [wRegI+1], a 967 | jr nc, .noCarry 968 | ld a, [wRegI] 969 | inc a 970 | ld [wRegI], a 971 | .noCarry 972 | 973 | pop hl 974 | jp EmuLoop 975 | 976 | ; ------------------------------------------------------------------------------ 977 | ; Fx29 - LD F, Vx 978 | ; Set I = location of sprite for digit Vx. 979 | ; ------------------------------------------------------------------------------ 980 | LoadDigitPtrInstruction:: 981 | ld a, b 982 | call EmuRegRead 983 | 984 | ld de, $0000 985 | and a 986 | jr z, .endDigitSearchLoop 987 | ld b, a 988 | .digitSearchLoop 989 | ld a, 5 990 | add e 991 | ld e, a 992 | adc d 993 | sub e 994 | ld d, a 995 | dec b 996 | jr nz, .digitSearchLoop 997 | .endDigitSearchLoop 998 | 999 | ld a, d 1000 | ld [wRegI], a 1001 | ld a, e 1002 | ld [wRegI+1], a 1003 | 1004 | pop hl 1005 | jp EmuLoop 1006 | 1007 | ; ------------------------------------------------------------------------------ 1008 | ; Fx0A - LD Vx, K 1009 | ; Wait for a key press, store the value of the key in Vx. 1010 | ; ------------------------------------------------------------------------------ 1011 | WaitKeypressInstruction:: 1012 | ; Load Input Data 1013 | ld a, [wInputData] 1014 | ld d, a 1015 | 1016 | ; Check if any buttons are pressed 1017 | ld hl, wInputMaskMap 1018 | ld e, $10 1019 | .inputCheckLoop 1020 | ld a, [hli] 1021 | and d 1022 | jr nz, .inputFound 1023 | dec e 1024 | jr nz, .inputCheckLoop 1025 | 1026 | ; If none pressed, keep repeating instruction 1027 | pop hl 1028 | dec hl 1029 | dec hl 1030 | jp EmuLoop 1031 | 1032 | .inputFound 1033 | ; Get input value 1034 | ld a, $10 1035 | sub e 1036 | ld e, a 1037 | 1038 | ; Store in Vx 1039 | ld a, b 1040 | and $0F 1041 | ld l, a 1042 | ld a, HIGH(wRegV) 1043 | ld h, a 1044 | ld a, e 1045 | ld [hl], a 1046 | 1047 | ; Return to loop 1048 | pop hl 1049 | jp EmuLoop 1050 | 1051 | ; ------------------------------------------------------------------------------ 1052 | ; Fx33 - LD B, Vx 1053 | ; Store BCD representation of Vx in memory locations I, I+1, and I+2. 1054 | ; ------------------------------------------------------------------------------ 1055 | BCDInstruction:: 1056 | ; Load register value 1057 | ld a, b 1058 | call EmuRegRead 1059 | ld d, a 1060 | 1061 | ; Load I base pointer 1062 | ld a, [wRegI+1] 1063 | ld l, a 1064 | ld a, [wRegI] 1065 | and $0F 1066 | or $C0 1067 | ld h, a 1068 | 1069 | ; Initialize BCD region to zero 1070 | xor a 1071 | ld [hli], a 1072 | ld [hli], a 1073 | ld [hld], a 1074 | dec hl 1075 | 1076 | ; Calculate BCD 1077 | ld a, d 1078 | .hundredsLoop 1079 | cp 100 1080 | jr c, .endHundredsLoop 1081 | inc [hl] 1082 | sub 100 1083 | jr .hundredsLoop 1084 | .endHundredsLoop 1085 | inc hl 1086 | .tensLoop 1087 | cp 10 1088 | jr c, .endTensLoop 1089 | inc [hl] 1090 | sub 10 1091 | jr .tensLoop 1092 | .endTensLoop 1093 | inc hl 1094 | .onesLoop 1095 | and a 1096 | jr z, .endOnesLoop 1097 | inc [hl] 1098 | dec a 1099 | jr .onesLoop 1100 | .endOnesLoop 1101 | 1102 | pop hl 1103 | jp EmuLoop 1104 | 1105 | ; ------------------------------------------------------------------------------ 1106 | ; Fx55 - LD [I], Vx 1107 | ; Store registers V0 through Vx in memory starting at location I. 1108 | ; ------------------------------------------------------------------------------ 1109 | StoreRegistersInstruction:: 1110 | ; Load I base pointer 1111 | ld a, [wRegI+1] 1112 | ld l, a 1113 | ld a, [wRegI] 1114 | and $0F 1115 | or $C0 1116 | ld h, a 1117 | 1118 | ; Store register values in EmuRAM 1119 | ld de, wRegV 1120 | ld a, b 1121 | and $0F 1122 | inc a 1123 | ld b, a 1124 | .storeLoop 1125 | ld a, [de] 1126 | inc de 1127 | ld [hli], a 1128 | dec b 1129 | jr nz, .storeLoop 1130 | 1131 | pop hl 1132 | jp EmuLoop 1133 | 1134 | ; ------------------------------------------------------------------------------ 1135 | ; Fx65 - LD Vx, [I] 1136 | ; Read registers V0 through Vx from memory starting at location I. 1137 | ; ------------------------------------------------------------------------------ 1138 | LoadRegistersInstruction:: 1139 | ; Load I base pointer 1140 | ld a, [wRegI+1] 1141 | ld l, a 1142 | ld a, [wRegI] 1143 | and $0F 1144 | or $C0 1145 | ld h, a 1146 | 1147 | ; Store register values in EmuRAM 1148 | ld de, wRegV 1149 | ld a, b 1150 | and $0F 1151 | inc a 1152 | ld b, a 1153 | .storeLoop 1154 | ld a, [hli] 1155 | ld [de], a 1156 | inc de 1157 | dec b 1158 | jr nz, .storeLoop 1159 | 1160 | pop hl 1161 | jp EmuLoop 1162 | 1163 | ; ------------------------------------------------------------------------------ 1164 | ; Table containing jump vectors related to the upper 4 bits of the instruction. 1165 | ; ------------------------------------------------------------------------------ 1166 | InstrJumpTable:: 1167 | dw ZeroByteInstruction 1168 | dw JumpInstruction 1169 | dw CallInstruction 1170 | dw SkipEqualInstruction 1171 | dw SkipNotEqualInstruction 1172 | dw SkipEqualRegisterInstruction 1173 | dw LoadInstruction 1174 | dw AddInstruction 1175 | dw ArithmeticInstruction 1176 | dw SkipNotEqualRegisterInstruction 1177 | dw ILoadInstruction 1178 | dw DummyInstruction 1179 | dw RandomInstruction 1180 | dw DrawInstruction 1181 | dw InputSkipInstruction 1182 | dw FInstruction 1183 | 1184 | ; ------------------------------------------------------------------------------ 1185 | ; Stores the value of emulated register V[A & $0F] in register A. 1186 | ; ------------------------------------------------------------------------------ 1187 | EmuRegRead:: 1188 | and $0F 1189 | ld l, a 1190 | ld a, HIGH(wRegV) 1191 | ld h, a 1192 | ld a, [hl] 1193 | ret 1194 | 1195 | ; ------------------------------------------------------------------------------ 1196 | ; Pops a 16-bit value off the emulated stack onto DE. 1197 | ; ------------------------------------------------------------------------------ 1198 | EmuPop:: 1199 | ; Decrement SP and get pointer to top of stack 1200 | ld a, [wSP] 1201 | dec a 1202 | ld [wSP], a 1203 | add a 1204 | add LOW(wStack) 1205 | ld l, a 1206 | adc HIGH(wStack) 1207 | sub l 1208 | ld h, a 1209 | 1210 | ; Load data from stack into DE and return 1211 | ld a, [hli] 1212 | ld e, a 1213 | ld a, [hl] 1214 | ld d, a 1215 | ret 1216 | 1217 | ; ------------------------------------------------------------------------------ 1218 | ; Pushes a 16-bit value from DE onto the emulated stack. 1219 | ; ------------------------------------------------------------------------------ 1220 | EmuPush:: 1221 | ; Get pointer to top of stack 1222 | ld a, [wSP] 1223 | add a 1224 | add LOW(wStack) 1225 | ld l, a 1226 | adc HIGH(wStack) 1227 | sub l 1228 | ld h, a 1229 | 1230 | ; Load data from DE into stack and return 1231 | ld a, e 1232 | ld [hli], a 1233 | ld a, d 1234 | ld [hli], a 1235 | 1236 | ; Increment SP and return 1237 | ld a, [wSP] 1238 | inc a 1239 | ld [wSP], a 1240 | ret --------------------------------------------------------------------------------