Compare commits

..

16 Commits

Author SHA1 Message Date
92787a1036 Remove debug logging and dead code for cleaner output.
This commit removes extensive debug logging, print statements, and unused code scattered across `main.rs` and `lcd.rs`. These changes streamline the codebase, reduce unnecessary noise, and improve maintainability without affecting functionality.
2025-05-19 17:01:55 +01:00
d0f748baa5 Add initial game binary and SDL2 dependencies
Introduce the main game binary `gb240p.gb` along with required SDL2 runtime (`SDL2.dll`) and development (`SDL2.lib`) files. These are essential for compiling and running the game.
2025-05-17 09:04:42 +01:00
b3024e66c8 Refactor and improve instruction execution behavior
Standardize instruction cycle handling and update return types for better clarity and accuracy. Add cycle count assertions in tests to ensure proper execution timings. Minor formatting fixes and typo corrections throughout the codebase.
2025-05-17 09:03:30 +01:00
5848bdcdce Refactor and improve instruction execution behavior
Standardize instruction cycle handling and update return types for better clarity and accuracy. Add cycle count assertions in tests to ensure proper execution timings. Minor formatting fixes and typo corrections throughout the codebase.
2025-05-15 17:22:46 +01:00
cc0278508e Refactor CPU execution flow and improve program counter handling
Introduced `ExecutionReturn` struct to standardize program counter and cycle updates across instructions. Removed redundant handling of program counter increments and refactored control flow for clarity. Also removed unused `Address` target variant from `Target` enum.
2025-05-15 16:43:42 +01:00
03c3c8a4ac Add LCDControlRegister for LCD control bit manipulation
Introduce a new `LCDControlRegister` struct to manage and manipulate individual bits of the LCD control register. Implement `From<u8>` and `Into<u8>` conversions for seamless integration with byte-level operations. This enhances code clarity and simplifies bitwise operations related to LCD control.
2025-05-14 16:33:35 +01:00
b8c49682e2 Refactor instruction parsing logic into separate module
The implementation of instruction parsing has been moved out of `main.rs` into a dedicated `instructions` module. This improves code readability, reduces clutter, and promotes modularity, making it easier to maintain and extend the instruction set functionality.
2025-05-14 16:08:50 +01:00
11f0bb06d6 Refactor CPU instruction set and cleanup unused code
Removed unused instructions (CopyHN16A, CopyHCA, etc.), simplified PC increment logic, and cleaned up test and execution code. Updated dictionaries and Cargo.lock to reflect recent changes in project structure and dependencies.
2025-05-14 08:59:12 +01:00
e9a40bd9f7 Refactor and extend CPU and memory emulation.
Added and refactored several instructions and memory operations, improving accuracy and functionality for emulation. Introduced new dependencies (`serde_json`, `glob`) and submodule for test framework integration. Enhanced debugging, flat RAM handling, and opcode parsing while fixing multiple calculation and flag-setting issues.
2025-05-09 13:47:26 +01:00
9be93f4aa9 Refactor boot process and instruction execution
Introduce clearer boot ROM handling and streamline `execute_next_instruction` for better readability and accuracy. Add program counter (`pc`) updates within each instruction, improving program flow consistency.
2025-05-06 16:20:23 +01:00
74e86f1ab7 Add GameRom struct with MBC1 support and integrate with MemoryBus
Introduced `GameRom` struct and implemented support for MBC1 cartridges, including ROM and RAM banking. Modified `MemoryBus` and `CPU` to use `GameRom` for cartridge interactions, replacing raw game ROM handling. Added logic to load, read, and write cartridge memory based on type and address.
2025-05-06 12:00:52 +01:00
918c9020b5 Add system-specific boot symbols and CPU instruction tests
Introduced symbol files for various Game Boy systems (CGB, AGB, MGB, SGB) to define boot sequences and functionalities. Included CPU instruction behavior tests, with detailed coverage of standard operations and interrupt handling. Added documentation for test execution and internal framework operations.
2025-05-02 17:33:07 +01:00
ae44d43175 implementing op codes 2025-05-02 10:46:49 +01:00
a3e3fbaa6e implementing op codes 2025-04-30 11:15:02 +01:00
611bfe0f74 implementing op codes 2025-04-30 11:14:27 +01:00
f351704b6e implementing op codes 2025-04-28 14:40:54 +01:00
85 changed files with 7629 additions and 294 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "sm83"]
path = sm83
url = https://github.com/SingleStepTests/sm83.git

View File

@@ -4,8 +4,20 @@
<w>adchl</w> <w>adchl</w>
<w>adcn</w> <w>adcn</w>
<w>addhl</w> <w>addhl</w>
<w>addsp</w>
<w>dechl</w>
<w>gameboy</w>
<w>inchl</w>
<w>incu</w>
<w>instrs</w>
<w>jpcc</w>
<w>jphl</w>
<w>jrcc</w>
<w>reti</w>
<w>rrca</w> <w>rrca</w>
<w>rrla</w> <w>rrla</w>
<w>tama</w>
<w>vram</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

1
.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/sm83" vcs="Git" />
</component> </component>
</project> </project>

151
Cargo.lock generated
View File

@@ -1,7 +1,156 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "glob"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "sdl2"
version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a"
dependencies = [
"bitflags",
"lazy_static",
"libc",
"sdl2-sys",
]
[[package]]
name = "sdl2-sys"
version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0"
dependencies = [
"cfg-if",
"libc",
"version-compare",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]] [[package]]
name = "untitled" name = "untitled"
version = "0.1.0" version = "0.1.0"
dependencies = [
"glob",
"sdl2",
"serde_json",
]
[[package]]
name = "version-compare"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"

View File

@@ -6,3 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde_json = "1.0.140"
glob = "0.3.2"
sdl2 = "0.35.2"

BIN
SDL2.dll Normal file

Binary file not shown.

BIN
SDL2.lib Normal file

Binary file not shown.

BIN
boot/agb.bin Normal file

Binary file not shown.

176
boot/agb.sym Normal file
View File

@@ -0,0 +1,176 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0008 ClearLogoGDMA
BOOT:000d ClearLogoGDMA.end
BOOT:000d ClearLogoTilesGDMA
BOOT:0012 OverrideColors
BOOT:0042 LogoTopHalf
BOOT:005a LogoBottomHalf
BOOT:0072 RTile
BOOT:007a RTile.end
BOOT:007a LogoTilemapChecksums
BOOT:007c LogoTilemapChecksums.end
BOOT:007c Setup
BOOT:0093 Setup.clearOAM
BOOT:009e Setup.processLogo
BOOT:00b2 Setup.copyRTile
BOOT:00d8 Setup.checkLogo
BOOT:00db Setup.logoFailure
BOOT:00e7 Setup.computeChecksum
BOOT:00ed Setup.checksumFailure
BOOT:00f5 Setup.done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
BOOT:0200 ClearVRAM
BOOT:0203 ClearUntilMemBoundary
BOOT:0204 ClearUntilMemBoundary.loop
BOOT:020a Memcpy
BOOT:0211 WaitVBlank
BOOT:0217 WaitVBlank.wait
BOOT:021d PollJoypad
BOOT:024a SetOBJAndBGPals
BOOT:0252 SetOBJAndBGPals.writeOBJPalData
BOOT:025c SetOBJAndBGPals.writeBGPalData
BOOT:0262 CommitBGPalettes
BOOT:0275 SetupSound
BOOT:028b SetupSound.initWaveRAM
BOOT:0291 DoLogoAnimation
BOOT:02a5 DoLogoAnimation.writeNintendoLogoMap
BOOT:02b0 DoLogoAnimation.dontWriteNintendoLogo
BOOT:02d3 DoLogoAnimation.changePaletteRow
BOOT:02d5 DoLogoAnimation.changePaletteBlock
BOOT:02d7 DoLogoAnimation.changePaletteLoop
BOOT:02f1 DoLogoAnimation.dontWriteLogoAttrMap
BOOT:02ff DoLogoAnimation.playSFX
BOOT:0306 DoLogoAnimation.dontAnimateLogo
BOOT:0306 DoLogoAnimation.dontPlaySFX
BOOT:0317 DoLogoAnimation.stepAnimation
BOOT:031c PerformFadeout
BOOT:031e PerformFadeout.loop
BOOT:033e PerformFadeout.clearLogoArea
BOOT:0341 PerformFadeout.clearLogoTiles
BOOT:034a PerformFadeout.fadePalettes
BOOT:0352 PerformFadeout.fadeColor
BOOT:035a PerformFadeout.redCap
BOOT:036e PerformFadeout.greenCap
BOOT:0384 PerformFadeout.blueCap
BOOT:038f DecodeLogoHalf
BOOT:0391 DecodeLogoHalf.decodeTileQuarter
BOOT:039a DecodeLogoHalf.decodingLeftHalf
BOOT:03a8 DecodeLogoHalf.decodingRightHalf
BOOT:03bd DecodeLogoHalf.goToRightHalf
BOOT:03bf DecodeLogoHalf.decodingTopHalf
BOOT:03c6 DecompressFirstNibble
BOOT:03c7 DecompressSecondNibble
BOOT:03ca DecompressSecondNibble.decompressBit
BOOT:03da WriteLogoTilemap
BOOT:03e2 WriteLogoTilemap.writeRow
BOOT:03e4 WriteLogoTilemap.writeByte
BOOT:03ef WriteLogoTilemap.done
BOOT:03f0 SetupGameBoyLogo
BOOT:03ff SetupGameBoyLogo.copyLogoRow
BOOT:041e SetupGameBoyLogo.copyRTile
BOOT:042c SetupGameBoyLogo.writeAttrRow
BOOT:042e SetupGameBoyLogo.writeAttrByte
BOOT:0441 SetupGameBoyLogo.writeTilemapByte
BOOT:0449 SetupGameBoyLogo.notFirstRow
BOOT:0450 SetupGameBoyLogo.notSecondRow
BOOT:045c SetupGameBoyLogo.initBGPalsLoop
BOOT:0488 SetupGameBoyLogo.usingOldLicensee
BOOT:048c SetupGameBoyLogo.checkMadeByNintendo
BOOT:0495 SetupGameBoyLogo.checksumTitle
BOOT:04a3 SetupGameBoyLogo.seekTitleChecksum
BOOT:04af SetupGameBoyLogo.foundTitleChecksum
BOOT:04bb SetupGameBoyLogo.seekFourthLetter
BOOT:04ce SetupGameBoyLogo.useDefaultIndex
BOOT:04d0 SetupGameBoyLogo.gotIndex
BOOT:04e9 WriteShuffledPalTriplets
BOOT:04f5 WriteShuffledPalTriplets.get3Indexes
BOOT:04fb WriteShuffledPalTriplets.bit0Set
BOOT:0501 WriteShuffledPalTriplets.cancelBit0Set
BOOT:0507 WriteShuffledPalTriplets.bit1Set
BOOT:050f WriteShuffledPalTriplets.cancelBit1Set
BOOT:0518 WriteShuffledPalTriplets.bit2Reset
BOOT:0528 ApplyPaletteOverride
BOOT:0539 ApplyPaletteOverride.copyPalette
BOOT:0564 GetPalettes
BOOT:0566 GetPalettes.copyPalette
BOOT:057b AddPalTripletOffset
BOOT:0582 AddPalTripletOffset.loop
BOOT:0588 AddPalTripletOffset.done
BOOT:0589 PickDMGPalette
BOOT:0596 PickDMGPalette.seekButtonCombo
BOOT:05a0 PickDMGPalette.jumpToDone
BOOT:05a2 PickDMGPalette.buttonComboFound
BOOT:05cf PickDMGPalette.done
BOOT:05d0 SetupCompatibility
BOOT:05de SetupCompatibility.dmgMode
BOOT:05fe SetupCompatibility.tryWriteLogoTilemap
BOOT:0606 SetupCompatibility.done
BOOT:0607 GameBoyLogoTiles
BOOT:06c7 GameBoyLogoTiles.end
BOOT:06c7 TitleChecksums
BOOT:0708 TitleChecksums.ambiguous
BOOT:0716 TitleChecksums.end
BOOT:0716 TitleFourthLetters
BOOT:0724 TitleFourthLetters.row
BOOT:0733 TitleFourthLetters.end
BOOT:0733 PalTripletIDsAndFlags
BOOT:0791 PaletteOffsets
BOOT:07e8 Palettes
BOOT:08d8 BootAnimationColors
BOOT:08e4 BootAnimationColors.end
BOOT:08e4 JoypadCombos
BOOT:08f0 JoypadCombos.end
BOOT:08f0 JoypadCombosTripletIDsAndFlags
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vTileMap
00:98c2 vGameBoyLogoMap
00:9904 vBigNintendoLogoMap
00:99a7 vNintendoLogoMap
01:8000 vTiles
01:8080 vGameBoyLogoTiles
01:8380 vNintendoLogoTiles
01:83e0 vSecondRTile
01:83f0 vNintendoLogoTilesEnd
01:9800 vAttrMap
01:98c2 vGameBoyLogoAttrs
01:9904 vBigNintendoLogoAttrs
01:99a7 vNintendoLogoAttrs
02:d000 wWorkRAM
02:d000 wTitleChecksum
02:d002 wPreventTerminationCounter
02:d003 wHeldButtons
02:d004 wPressedButtons
02:d005 wPaletteOverrideIndex
02:d006 wWhichPalTripletCopy
02:d007 wOldWhichPalTriplet
02:d008 wWhichPalTriplet
02:d009 wPalShufflingFlagsCopy
02:d00a wOldPalShufflingFlags
02:d00b wPalShufflingFlags
02:d300 wZeroBuffer
02:d800 wOBJPalBuffer
02:d840 wOBJPalBuffer.end
02:d840 wBGPalBuffer
02:d880 wBGPalBuffer.end
02:d900 wPalOfsBuffer
02:d95a wPalOfsBuffer.end
02:d960 wPalOfsBuffer.realEnd
02:da00 wPalBuffer
00:ff80 hLogoBuffer
00:fffe hStackBottom

BIN
boot/agb0.bin Normal file

Binary file not shown.

176
boot/agb0.sym Normal file
View File

@@ -0,0 +1,176 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0008 ClearLogoGDMA
BOOT:000d ClearLogoGDMA.end
BOOT:000d ClearLogoTilesGDMA
BOOT:0012 OverrideColors
BOOT:0042 LogoTopHalf
BOOT:005a LogoBottomHalf
BOOT:0072 RTile
BOOT:007a RTile.end
BOOT:007a LogoTilemapChecksums
BOOT:007c LogoTilemapChecksums.end
BOOT:007c Setup
BOOT:0093 Setup.clearOAM
BOOT:009e Setup.processLogo
BOOT:00b2 Setup.copyRTile
BOOT:00d8 Setup.checkLogo
BOOT:00db Setup.logoFailure
BOOT:00e7 Setup.computeChecksum
BOOT:00ed Setup.checksumFailure
BOOT:00f5 Setup.done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
BOOT:0200 ClearVRAM
BOOT:0203 ClearUntilMemBoundary
BOOT:0204 ClearUntilMemBoundary.loop
BOOT:020a Memcpy
BOOT:0211 WaitVBlank
BOOT:0217 WaitVBlank.wait
BOOT:021d PollJoypad
BOOT:024a SetOBJAndBGPals
BOOT:0252 SetOBJAndBGPals.writeOBJPalData
BOOT:025c SetOBJAndBGPals.writeBGPalData
BOOT:0262 CommitBGPalettes
BOOT:0275 SetupSound
BOOT:028b SetupSound.initWaveRAM
BOOT:0291 DoLogoAnimation
BOOT:02a5 DoLogoAnimation.writeNintendoLogoMap
BOOT:02b0 DoLogoAnimation.dontWriteNintendoLogo
BOOT:02d3 DoLogoAnimation.changePaletteRow
BOOT:02d5 DoLogoAnimation.changePaletteBlock
BOOT:02d7 DoLogoAnimation.changePaletteLoop
BOOT:02f1 DoLogoAnimation.dontWriteLogoAttrMap
BOOT:02ff DoLogoAnimation.playSFX
BOOT:0306 DoLogoAnimation.dontAnimateLogo
BOOT:0306 DoLogoAnimation.dontPlaySFX
BOOT:0317 DoLogoAnimation.stepAnimation
BOOT:031c PerformFadeout
BOOT:031e PerformFadeout.loop
BOOT:033e PerformFadeout.clearLogoArea
BOOT:0341 PerformFadeout.clearLogoTiles
BOOT:034a PerformFadeout.fadePalettes
BOOT:0352 PerformFadeout.fadeColor
BOOT:035a PerformFadeout.redCap
BOOT:036e PerformFadeout.greenCap
BOOT:0384 PerformFadeout.blueCap
BOOT:038f DecodeLogoHalf
BOOT:0391 DecodeLogoHalf.decodeTileQuarter
BOOT:039a DecodeLogoHalf.decodingLeftHalf
BOOT:03a8 DecodeLogoHalf.decodingRightHalf
BOOT:03bd DecodeLogoHalf.goToRightHalf
BOOT:03bf DecodeLogoHalf.decodingTopHalf
BOOT:03c6 DecompressFirstNibble
BOOT:03c7 DecompressSecondNibble
BOOT:03ca DecompressSecondNibble.decompressBit
BOOT:03da WriteLogoTilemap
BOOT:03e2 WriteLogoTilemap.writeRow
BOOT:03e4 WriteLogoTilemap.writeByte
BOOT:03ef WriteLogoTilemap.done
BOOT:03f0 SetupGameBoyLogo
BOOT:03ff SetupGameBoyLogo.copyLogoRow
BOOT:041e SetupGameBoyLogo.copyRTile
BOOT:042c SetupGameBoyLogo.writeAttrRow
BOOT:042e SetupGameBoyLogo.writeAttrByte
BOOT:0441 SetupGameBoyLogo.writeTilemapByte
BOOT:0449 SetupGameBoyLogo.notFirstRow
BOOT:0450 SetupGameBoyLogo.notSecondRow
BOOT:045c SetupGameBoyLogo.initBGPalsLoop
BOOT:0488 SetupGameBoyLogo.usingOldLicensee
BOOT:048c SetupGameBoyLogo.checkMadeByNintendo
BOOT:0495 SetupGameBoyLogo.checksumTitle
BOOT:04a3 SetupGameBoyLogo.seekTitleChecksum
BOOT:04af SetupGameBoyLogo.foundTitleChecksum
BOOT:04bb SetupGameBoyLogo.seekFourthLetter
BOOT:04ce SetupGameBoyLogo.useDefaultIndex
BOOT:04d0 SetupGameBoyLogo.gotIndex
BOOT:04e9 WriteShuffledPalTriplets
BOOT:04f5 WriteShuffledPalTriplets.get3Indexes
BOOT:04fb WriteShuffledPalTriplets.bit0Set
BOOT:0501 WriteShuffledPalTriplets.cancelBit0Set
BOOT:0507 WriteShuffledPalTriplets.bit1Set
BOOT:050f WriteShuffledPalTriplets.cancelBit1Set
BOOT:0518 WriteShuffledPalTriplets.bit2Reset
BOOT:0528 ApplyPaletteOverride
BOOT:0539 ApplyPaletteOverride.copyPalette
BOOT:0564 GetPalettes
BOOT:0566 GetPalettes.copyPalette
BOOT:057b AddPalTripletOffset
BOOT:0582 AddPalTripletOffset.loop
BOOT:0588 AddPalTripletOffset.done
BOOT:0589 PickDMGPalette
BOOT:0596 PickDMGPalette.seekButtonCombo
BOOT:05a0 PickDMGPalette.jumpToDone
BOOT:05a2 PickDMGPalette.buttonComboFound
BOOT:05cf PickDMGPalette.done
BOOT:05d0 SetupCompatibility
BOOT:05de SetupCompatibility.dmgMode
BOOT:05fe SetupCompatibility.tryWriteLogoTilemap
BOOT:0606 SetupCompatibility.done
BOOT:0607 GameBoyLogoTiles
BOOT:06c7 GameBoyLogoTiles.end
BOOT:06c7 TitleChecksums
BOOT:0708 TitleChecksums.ambiguous
BOOT:0716 TitleChecksums.end
BOOT:0716 TitleFourthLetters
BOOT:0724 TitleFourthLetters.row
BOOT:0733 TitleFourthLetters.end
BOOT:0733 PalTripletIDsAndFlags
BOOT:0791 PaletteOffsets
BOOT:07e8 Palettes
BOOT:08d8 BootAnimationColors
BOOT:08e4 BootAnimationColors.end
BOOT:08e4 JoypadCombos
BOOT:08f0 JoypadCombos.end
BOOT:08f0 JoypadCombosTripletIDsAndFlags
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vTileMap
00:98c2 vGameBoyLogoMap
00:9904 vBigNintendoLogoMap
00:99a7 vNintendoLogoMap
01:8000 vTiles
01:8080 vGameBoyLogoTiles
01:8380 vNintendoLogoTiles
01:83e0 vSecondRTile
01:83f0 vNintendoLogoTilesEnd
01:9800 vAttrMap
01:98c2 vGameBoyLogoAttrs
01:9904 vBigNintendoLogoAttrs
01:99a7 vNintendoLogoAttrs
02:d000 wWorkRAM
02:d000 wTitleChecksum
02:d002 wPreventTerminationCounter
02:d003 wHeldButtons
02:d004 wPressedButtons
02:d005 wPaletteOverrideIndex
02:d006 wWhichPalTripletCopy
02:d007 wOldWhichPalTriplet
02:d008 wWhichPalTriplet
02:d009 wPalShufflingFlagsCopy
02:d00a wOldPalShufflingFlags
02:d00b wPalShufflingFlags
02:d300 wZeroBuffer
02:d800 wOBJPalBuffer
02:d840 wOBJPalBuffer.end
02:d840 wBGPalBuffer
02:d880 wBGPalBuffer.end
02:d900 wPalOfsBuffer
02:d95a wPalOfsBuffer.end
02:d960 wPalOfsBuffer.realEnd
02:da00 wPalBuffer
00:ff80 hLogoBuffer
00:fffe hStackBottom

BIN
boot/cgb.bin Normal file

Binary file not shown.

176
boot/cgb.sym Normal file
View File

@@ -0,0 +1,176 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0008 ClearLogoGDMA
BOOT:000d ClearLogoGDMA.end
BOOT:000d ClearLogoTilesGDMA
BOOT:0012 OverrideColors
BOOT:0042 LogoTopHalf
BOOT:005a LogoBottomHalf
BOOT:0072 RTile
BOOT:007a RTile.end
BOOT:007a LogoTilemapChecksums
BOOT:007c LogoTilemapChecksums.end
BOOT:007c Setup
BOOT:0093 Setup.clearOAM
BOOT:009e Setup.processLogo
BOOT:00b2 Setup.copyRTile
BOOT:00d8 Setup.checkLogo
BOOT:00db Setup.logoFailure
BOOT:00e7 Setup.computeChecksum
BOOT:00ed Setup.checksumFailure
BOOT:00f6 Setup.done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
BOOT:0200 ClearVRAM
BOOT:0203 ClearUntilMemBoundary
BOOT:0204 ClearUntilMemBoundary.loop
BOOT:020a Memcpy
BOOT:0211 WaitVBlank
BOOT:0217 WaitVBlank.wait
BOOT:021d PollJoypad
BOOT:024a SetOBJAndBGPals
BOOT:0252 SetOBJAndBGPals.writeOBJPalData
BOOT:025c SetOBJAndBGPals.writeBGPalData
BOOT:0262 CommitBGPalettes
BOOT:0275 SetupSound
BOOT:028b SetupSound.initWaveRAM
BOOT:0291 DoLogoAnimation
BOOT:02a5 DoLogoAnimation.writeNintendoLogoMap
BOOT:02b0 DoLogoAnimation.dontWriteNintendoLogo
BOOT:02d3 DoLogoAnimation.changePaletteRow
BOOT:02d5 DoLogoAnimation.changePaletteBlock
BOOT:02d7 DoLogoAnimation.changePaletteLoop
BOOT:02f1 DoLogoAnimation.dontWriteLogoAttrMap
BOOT:02ff DoLogoAnimation.playSFX
BOOT:0306 DoLogoAnimation.dontAnimateLogo
BOOT:0306 DoLogoAnimation.dontPlaySFX
BOOT:0317 DoLogoAnimation.stepAnimation
BOOT:031c PerformFadeout
BOOT:031e PerformFadeout.loop
BOOT:033e PerformFadeout.clearLogoArea
BOOT:0341 PerformFadeout.clearLogoTiles
BOOT:034a PerformFadeout.fadePalettes
BOOT:0352 PerformFadeout.fadeColor
BOOT:035a PerformFadeout.redCap
BOOT:036e PerformFadeout.greenCap
BOOT:0384 PerformFadeout.blueCap
BOOT:038f DecodeLogoHalf
BOOT:0391 DecodeLogoHalf.decodeTileQuarter
BOOT:039a DecodeLogoHalf.decodingLeftHalf
BOOT:03a8 DecodeLogoHalf.decodingRightHalf
BOOT:03bd DecodeLogoHalf.goToRightHalf
BOOT:03bf DecodeLogoHalf.decodingTopHalf
BOOT:03c6 DecompressFirstNibble
BOOT:03c7 DecompressSecondNibble
BOOT:03ca DecompressSecondNibble.decompressBit
BOOT:03da WriteLogoTilemap
BOOT:03e2 WriteLogoTilemap.writeRow
BOOT:03e4 WriteLogoTilemap.writeByte
BOOT:03ef WriteLogoTilemap.done
BOOT:03f0 SetupGameBoyLogo
BOOT:03ff SetupGameBoyLogo.copyLogoRow
BOOT:041e SetupGameBoyLogo.copyRTile
BOOT:042c SetupGameBoyLogo.writeAttrRow
BOOT:042e SetupGameBoyLogo.writeAttrByte
BOOT:0441 SetupGameBoyLogo.writeTilemapByte
BOOT:0449 SetupGameBoyLogo.notFirstRow
BOOT:0450 SetupGameBoyLogo.notSecondRow
BOOT:045c SetupGameBoyLogo.initBGPalsLoop
BOOT:0488 SetupGameBoyLogo.usingOldLicensee
BOOT:048c SetupGameBoyLogo.checkMadeByNintendo
BOOT:0495 SetupGameBoyLogo.checksumTitle
BOOT:04a3 SetupGameBoyLogo.seekTitleChecksum
BOOT:04af SetupGameBoyLogo.foundTitleChecksum
BOOT:04bb SetupGameBoyLogo.seekFourthLetter
BOOT:04ce SetupGameBoyLogo.useDefaultIndex
BOOT:04d0 SetupGameBoyLogo.gotIndex
BOOT:04e9 WriteShuffledPalTriplets
BOOT:04f5 WriteShuffledPalTriplets.get3Indexes
BOOT:04fb WriteShuffledPalTriplets.bit0Set
BOOT:0501 WriteShuffledPalTriplets.cancelBit0Set
BOOT:0507 WriteShuffledPalTriplets.bit1Set
BOOT:050f WriteShuffledPalTriplets.cancelBit1Set
BOOT:0518 WriteShuffledPalTriplets.bit2Reset
BOOT:0528 ApplyPaletteOverride
BOOT:0539 ApplyPaletteOverride.copyPalette
BOOT:0564 GetPalettes
BOOT:0566 GetPalettes.copyPalette
BOOT:057b AddPalTripletOffset
BOOT:0582 AddPalTripletOffset.loop
BOOT:0588 AddPalTripletOffset.done
BOOT:0589 PickDMGPalette
BOOT:0596 PickDMGPalette.seekButtonCombo
BOOT:05a0 PickDMGPalette.jumpToDone
BOOT:05a2 PickDMGPalette.buttonComboFound
BOOT:05cf PickDMGPalette.done
BOOT:05d0 SetupCompatibility
BOOT:05de SetupCompatibility.dmgMode
BOOT:05fe SetupCompatibility.tryWriteLogoTilemap
BOOT:0606 SetupCompatibility.done
BOOT:0607 GameBoyLogoTiles
BOOT:06c7 GameBoyLogoTiles.end
BOOT:06c7 TitleChecksums
BOOT:0708 TitleChecksums.ambiguous
BOOT:0716 TitleChecksums.end
BOOT:0716 TitleFourthLetters
BOOT:0724 TitleFourthLetters.row
BOOT:0733 TitleFourthLetters.end
BOOT:0733 PalTripletIDsAndFlags
BOOT:0791 PaletteOffsets
BOOT:07e8 Palettes
BOOT:08d8 BootAnimationColors
BOOT:08e4 BootAnimationColors.end
BOOT:08e4 JoypadCombos
BOOT:08f0 JoypadCombos.end
BOOT:08f0 JoypadCombosTripletIDsAndFlags
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vTileMap
00:98c2 vGameBoyLogoMap
00:9904 vBigNintendoLogoMap
00:99a7 vNintendoLogoMap
01:8000 vTiles
01:8080 vGameBoyLogoTiles
01:8380 vNintendoLogoTiles
01:83e0 vSecondRTile
01:83f0 vNintendoLogoTilesEnd
01:9800 vAttrMap
01:98c2 vGameBoyLogoAttrs
01:9904 vBigNintendoLogoAttrs
01:99a7 vNintendoLogoAttrs
02:d000 wWorkRAM
02:d000 wTitleChecksum
02:d002 wPreventTerminationCounter
02:d003 wHeldButtons
02:d004 wPressedButtons
02:d005 wPaletteOverrideIndex
02:d006 wWhichPalTripletCopy
02:d007 wOldWhichPalTriplet
02:d008 wWhichPalTriplet
02:d009 wPalShufflingFlagsCopy
02:d00a wOldPalShufflingFlags
02:d00b wPalShufflingFlags
02:d300 wZeroBuffer
02:d800 wOBJPalBuffer
02:d840 wOBJPalBuffer.end
02:d840 wBGPalBuffer
02:d880 wBGPalBuffer.end
02:d900 wPalOfsBuffer
02:d95a wPalOfsBuffer.end
02:d960 wPalOfsBuffer.realEnd
02:da00 wPalBuffer
00:ff80 hLogoBuffer
00:fffe hStackBottom

BIN
boot/cgb0.bin Normal file

Binary file not shown.

176
boot/cgb0.sym Normal file
View File

@@ -0,0 +1,176 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0008 ClearLogoGDMA
BOOT:000d ClearLogoGDMA.end
BOOT:000d ClearLogoTilesGDMA
BOOT:0012 OverrideColors
BOOT:0042 LogoTopHalf
BOOT:005a LogoBottomHalf
BOOT:0072 RTile
BOOT:007a RTile.end
BOOT:007a LogoTilemapChecksums
BOOT:007c LogoTilemapChecksums.end
BOOT:007c Setup
BOOT:0093 Setup.clearOAM
BOOT:009e Setup.processLogo
BOOT:00b2 Setup.copyRTile
BOOT:00d8 Setup.checkLogo
BOOT:00db Setup.logoFailure
BOOT:00e7 Setup.computeChecksum
BOOT:00ed Setup.checksumFailure
BOOT:00f6 Setup.done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
BOOT:0200 ClearVRAM
BOOT:0203 ClearUntilMemBoundary
BOOT:0204 ClearUntilMemBoundary.loop
BOOT:020a Memcpy
BOOT:0211 WaitVBlank
BOOT:0217 WaitVBlank.wait
BOOT:021d PollJoypad
BOOT:024a SetOBJAndBGPals
BOOT:0252 SetOBJAndBGPals.writeOBJPalData
BOOT:025c SetOBJAndBGPals.writeBGPalData
BOOT:0262 CommitBGPalettes
BOOT:0275 SetupSound
BOOT:0286 DoLogoAnimation
BOOT:029a DoLogoAnimation.writeNintendoLogoMap
BOOT:02a5 DoLogoAnimation.dontWriteNintendoLogo
BOOT:02c8 DoLogoAnimation.changePaletteRow
BOOT:02ca DoLogoAnimation.changePaletteBlock
BOOT:02cc DoLogoAnimation.changePaletteLoop
BOOT:02e6 DoLogoAnimation.dontWriteLogoAttrMap
BOOT:02f4 DoLogoAnimation.playSFX
BOOT:02fb DoLogoAnimation.dontAnimateLogo
BOOT:02fb DoLogoAnimation.dontPlaySFX
BOOT:030c DoLogoAnimation.stepAnimation
BOOT:0311 PerformFadeout
BOOT:0313 PerformFadeout.loop
BOOT:0333 PerformFadeout.clearLogoArea
BOOT:0336 PerformFadeout.clearLogoTiles
BOOT:033f PerformFadeout.fadePalettes
BOOT:0347 PerformFadeout.fadeColor
BOOT:034f PerformFadeout.redCap
BOOT:0363 PerformFadeout.greenCap
BOOT:0379 PerformFadeout.blueCap
BOOT:0384 DecodeLogoHalf
BOOT:0386 DecodeLogoHalf.decodeTileQuarter
BOOT:038f DecodeLogoHalf.decodingLeftHalf
BOOT:039d DecodeLogoHalf.decodingRightHalf
BOOT:03b2 DecodeLogoHalf.goToRightHalf
BOOT:03b4 DecodeLogoHalf.decodingTopHalf
BOOT:03bb DecompressFirstNibble
BOOT:03bc DecompressSecondNibble
BOOT:03bf DecompressSecondNibble.decompressBit
BOOT:03cf WriteLogoTilemap
BOOT:03d7 WriteLogoTilemap.writeRow
BOOT:03d9 WriteLogoTilemap.writeByte
BOOT:03e4 WriteLogoTilemap.done
BOOT:03e5 SetupGameBoyLogo
BOOT:03f4 SetupGameBoyLogo.copyLogoTile
BOOT:03f6 SetupGameBoyLogo.copyLogoRow
BOOT:0418 SetupGameBoyLogo.copyRTile
BOOT:0426 SetupGameBoyLogo.writeAttrRow
BOOT:0428 SetupGameBoyLogo.writeAttrByte
BOOT:043b SetupGameBoyLogo.writeTilemapByte
BOOT:0443 SetupGameBoyLogo.notFirstRow
BOOT:044a SetupGameBoyLogo.notSecondRow
BOOT:0456 SetupGameBoyLogo.initBGPalsLoop
BOOT:0482 SetupGameBoyLogo.usingOldLicensee
BOOT:0486 SetupGameBoyLogo.checkMadeByNintendo
BOOT:048f SetupGameBoyLogo.checksumTitle
BOOT:049d SetupGameBoyLogo.seekTitleChecksum
BOOT:04a9 SetupGameBoyLogo.foundTitleChecksum
BOOT:04b5 SetupGameBoyLogo.seekFourthLetter
BOOT:04c8 SetupGameBoyLogo.useDefaultIndex
BOOT:04ca SetupGameBoyLogo.gotIndex
BOOT:04e9 WriteShuffledPalTriplets
BOOT:04f5 WriteShuffledPalTriplets.get3Indexes
BOOT:04fb WriteShuffledPalTriplets.bit0Set
BOOT:0501 WriteShuffledPalTriplets.cancelBit0Set
BOOT:0507 WriteShuffledPalTriplets.bit1Set
BOOT:050f WriteShuffledPalTriplets.cancelBit1Set
BOOT:0518 WriteShuffledPalTriplets.bit2Reset
BOOT:0528 ApplyPaletteOverride
BOOT:0539 ApplyPaletteOverride.copyPalette
BOOT:0564 GetPalettes
BOOT:0566 GetPalettes.copyPalette
BOOT:057b AddPalTripletOffset
BOOT:0582 AddPalTripletOffset.loop
BOOT:0588 AddPalTripletOffset.done
BOOT:0589 PickDMGPalette
BOOT:0596 PickDMGPalette.seekButtonCombo
BOOT:05a0 PickDMGPalette.jumpToDone
BOOT:05a2 PickDMGPalette.buttonComboFound
BOOT:05cf PickDMGPalette.done
BOOT:05d0 SetupCompatibility
BOOT:05de SetupCompatibility.dmgMode
BOOT:05fe SetupCompatibility.tryWriteLogoTilemap
BOOT:0606 SetupCompatibility.done
BOOT:0607 GameBoyLogoTiles
BOOT:06c7 GameBoyLogoTiles.end
BOOT:06c7 TitleChecksums
BOOT:0708 TitleChecksums.ambiguous
BOOT:0716 TitleChecksums.end
BOOT:0716 TitleFourthLetters
BOOT:0724 TitleFourthLetters.row
BOOT:0733 TitleFourthLetters.end
BOOT:0733 PalTripletIDsAndFlags
BOOT:0791 PaletteOffsets
BOOT:07e8 Palettes
BOOT:08d8 BootAnimationColors
BOOT:08e4 BootAnimationColors.end
BOOT:08e4 JoypadCombos
BOOT:08f0 JoypadCombos.end
BOOT:08f0 JoypadCombosTripletIDsAndFlags
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vTileMap
00:98c2 vGameBoyLogoMap
00:9904 vBigNintendoLogoMap
00:99a7 vNintendoLogoMap
01:8000 vTiles
01:8080 vGameBoyLogoTiles
01:8380 vNintendoLogoTiles
01:83e0 vSecondRTile
01:83f0 vNintendoLogoTilesEnd
01:9800 vAttrMap
01:98c2 vGameBoyLogoAttrs
01:9904 vBigNintendoLogoAttrs
01:99a7 vNintendoLogoAttrs
02:d000 wWorkRAM
02:d000 wTitleChecksum
02:d002 wPreventTerminationCounter
02:d003 wHeldButtons
02:d004 wPressedButtons
02:d005 wPaletteOverrideIndex
02:d006 wWhichPalTripletCopy
02:d007 wOldWhichPalTriplet
02:d008 wWhichPalTriplet
02:d009 wPalShufflingFlagsCopy
02:d00a wOldPalShufflingFlags
02:d00b wPalShufflingFlags
02:d300 wZeroBuffer
02:d800 wOBJPalBuffer
02:d840 wOBJPalBuffer.end
02:d840 wBGPalBuffer
02:d880 wBGPalBuffer.end
02:d900 wPalOfsBuffer
02:d95a wPalOfsBuffer.end
02:d960 wPalOfsBuffer.realEnd
02:da00 wPalBuffer
00:ff80 hLogoBuffer
00:fffe hStackBottom

BIN
boot/cgbE.bin Normal file

Binary file not shown.

176
boot/cgbE.sym Normal file
View File

@@ -0,0 +1,176 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0008 ClearLogoGDMA
BOOT:000d ClearLogoGDMA.end
BOOT:000d ClearLogoTilesGDMA
BOOT:0012 OverrideColors
BOOT:0042 LogoTopHalf
BOOT:005a LogoBottomHalf
BOOT:0072 RTile
BOOT:007a RTile.end
BOOT:007a LogoTilemapChecksums
BOOT:007c LogoTilemapChecksums.end
BOOT:007c Setup
BOOT:0093 Setup.clearOAM
BOOT:009e Setup.processLogo
BOOT:00b2 Setup.copyRTile
BOOT:00d8 Setup.checkLogo
BOOT:00db Setup.logoFailure
BOOT:00e7 Setup.computeChecksum
BOOT:00ed Setup.checksumFailure
BOOT:00f6 Setup.done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
BOOT:0200 ClearVRAM
BOOT:0203 ClearUntilMemBoundary
BOOT:0204 ClearUntilMemBoundary.loop
BOOT:020a Memcpy
BOOT:0211 WaitVBlank
BOOT:0217 WaitVBlank.wait
BOOT:021d PollJoypad
BOOT:024a SetOBJAndBGPals
BOOT:0252 SetOBJAndBGPals.writeOBJPalData
BOOT:025c SetOBJAndBGPals.writeBGPalData
BOOT:0262 CommitBGPalettes
BOOT:0275 SetupSound
BOOT:028b SetupSound.initWaveRAM
BOOT:0291 DoLogoAnimation
BOOT:02a5 DoLogoAnimation.writeNintendoLogoMap
BOOT:02b0 DoLogoAnimation.dontWriteNintendoLogo
BOOT:02d3 DoLogoAnimation.changePaletteRow
BOOT:02d5 DoLogoAnimation.changePaletteBlock
BOOT:02d7 DoLogoAnimation.changePaletteLoop
BOOT:02f1 DoLogoAnimation.dontWriteLogoAttrMap
BOOT:02ff DoLogoAnimation.playSFX
BOOT:0306 DoLogoAnimation.dontAnimateLogo
BOOT:0306 DoLogoAnimation.dontPlaySFX
BOOT:0317 DoLogoAnimation.stepAnimation
BOOT:031c PerformFadeout
BOOT:031e PerformFadeout.loop
BOOT:033e PerformFadeout.clearLogoArea
BOOT:0341 PerformFadeout.clearLogoTiles
BOOT:034a PerformFadeout.fadePalettes
BOOT:0352 PerformFadeout.fadeColor
BOOT:035a PerformFadeout.redCap
BOOT:036e PerformFadeout.greenCap
BOOT:0384 PerformFadeout.blueCap
BOOT:038f DecodeLogoHalf
BOOT:0391 DecodeLogoHalf.decodeTileQuarter
BOOT:039a DecodeLogoHalf.decodingLeftHalf
BOOT:03a8 DecodeLogoHalf.decodingRightHalf
BOOT:03bd DecodeLogoHalf.goToRightHalf
BOOT:03bf DecodeLogoHalf.decodingTopHalf
BOOT:03c6 DecompressFirstNibble
BOOT:03c7 DecompressSecondNibble
BOOT:03ca DecompressSecondNibble.decompressBit
BOOT:03da WriteLogoTilemap
BOOT:03e2 WriteLogoTilemap.writeRow
BOOT:03e4 WriteLogoTilemap.writeByte
BOOT:03ef WriteLogoTilemap.done
BOOT:03f0 SetupGameBoyLogo
BOOT:03ff SetupGameBoyLogo.copyLogoRow
BOOT:041e SetupGameBoyLogo.copyRTile
BOOT:042c SetupGameBoyLogo.writeAttrRow
BOOT:042e SetupGameBoyLogo.writeAttrByte
BOOT:0441 SetupGameBoyLogo.writeTilemapByte
BOOT:0449 SetupGameBoyLogo.notFirstRow
BOOT:0450 SetupGameBoyLogo.notSecondRow
BOOT:045c SetupGameBoyLogo.initBGPalsLoop
BOOT:0488 SetupGameBoyLogo.usingOldLicensee
BOOT:048c SetupGameBoyLogo.checkMadeByNintendo
BOOT:0495 SetupGameBoyLogo.checksumTitle
BOOT:04a3 SetupGameBoyLogo.seekTitleChecksum
BOOT:04af SetupGameBoyLogo.foundTitleChecksum
BOOT:04bb SetupGameBoyLogo.seekFourthLetter
BOOT:04ce SetupGameBoyLogo.useDefaultIndex
BOOT:04d0 SetupGameBoyLogo.gotIndex
BOOT:04e9 WriteShuffledPalTriplets
BOOT:04f5 WriteShuffledPalTriplets.get3Indexes
BOOT:04fb WriteShuffledPalTriplets.bit0Set
BOOT:0501 WriteShuffledPalTriplets.cancelBit0Set
BOOT:0507 WriteShuffledPalTriplets.bit1Set
BOOT:050f WriteShuffledPalTriplets.cancelBit1Set
BOOT:0518 WriteShuffledPalTriplets.bit2Reset
BOOT:0528 ApplyPaletteOverride
BOOT:0539 ApplyPaletteOverride.copyPalette
BOOT:0564 GetPalettes
BOOT:0566 GetPalettes.copyPalette
BOOT:057b AddPalTripletOffset
BOOT:0582 AddPalTripletOffset.loop
BOOT:0588 AddPalTripletOffset.done
BOOT:0589 PickDMGPalette
BOOT:0596 PickDMGPalette.seekButtonCombo
BOOT:05a0 PickDMGPalette.jumpToDone
BOOT:05a2 PickDMGPalette.buttonComboFound
BOOT:05cf PickDMGPalette.done
BOOT:05d0 SetupCompatibility
BOOT:05de SetupCompatibility.dmgMode
BOOT:05fe SetupCompatibility.tryWriteLogoTilemap
BOOT:0606 SetupCompatibility.done
BOOT:0607 GameBoyLogoTiles
BOOT:06c7 GameBoyLogoTiles.end
BOOT:06c7 TitleChecksums
BOOT:0708 TitleChecksums.ambiguous
BOOT:0716 TitleChecksums.end
BOOT:0716 TitleFourthLetters
BOOT:0724 TitleFourthLetters.row
BOOT:0733 TitleFourthLetters.end
BOOT:0733 PalTripletIDsAndFlags
BOOT:0791 PaletteOffsets
BOOT:07e8 Palettes
BOOT:08d8 BootAnimationColors
BOOT:08e4 BootAnimationColors.end
BOOT:08e4 JoypadCombos
BOOT:08f0 JoypadCombos.end
BOOT:08f0 JoypadCombosTripletIDsAndFlags
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vTileMap
00:98c2 vGameBoyLogoMap
00:9904 vBigNintendoLogoMap
00:99a7 vNintendoLogoMap
01:8000 vTiles
01:8080 vGameBoyLogoTiles
01:8380 vNintendoLogoTiles
01:83e0 vSecondRTile
01:83f0 vNintendoLogoTilesEnd
01:9800 vAttrMap
01:98c2 vGameBoyLogoAttrs
01:9904 vBigNintendoLogoAttrs
01:99a7 vNintendoLogoAttrs
02:d000 wWorkRAM
02:d000 wTitleChecksum
02:d002 wPreventTerminationCounter
02:d003 wHeldButtons
02:d004 wPressedButtons
02:d005 wPaletteOverrideIndex
02:d006 wWhichPalTripletCopy
02:d007 wOldWhichPalTriplet
02:d008 wWhichPalTriplet
02:d009 wPalShufflingFlagsCopy
02:d00a wOldPalShufflingFlags
02:d00b wPalShufflingFlags
02:d300 wZeroBuffer
02:d800 wOBJPalBuffer
02:d840 wOBJPalBuffer.end
02:d840 wBGPalBuffer
02:d880 wBGPalBuffer.end
02:d900 wPalOfsBuffer
02:d95a wPalOfsBuffer.end
02:d960 wPalOfsBuffer.realEnd
02:da00 wPalBuffer
00:ff80 hLogoBuffer
00:fffe hStackBottom

BIN
boot/dmg.bin Normal file

Binary file not shown.

42
boot/dmg.sym Normal file
View File

@@ -0,0 +1,42 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0007 EntryPoint.clearVRAM
BOOT:0027 EntryPoint.decompressLogo
BOOT:0039 EntryPoint.copyRTile
BOOT:0048 EntryPoint.writeTilemapRow
BOOT:004a EntryPoint.writeTilemapByte
BOOT:0055 ScrollLogo
BOOT:0060 ScrollLogo.loop
BOOT:0062 ScrollLogo.delayFrames
BOOT:0064 ScrollLogo.waitVBlank
BOOT:0080 ScrollLogo.playSound
BOOT:0086 ScrollLogo.dontPlaySound
BOOT:0095 DecompressFirstNibble
BOOT:0096 DecompressSecondNibble
BOOT:0098 DecompressSecondNibble.loop
BOOT:00a8 Logo
BOOT:00d8 RTile
BOOT:00e0 CheckLogo
BOOT:00e6 CheckLogo.compare
BOOT:00e9 CheckLogo.logoFailure
BOOT:00f4 CheckLogo.computeChecksum
BOOT:00fa CheckLogo.checksumFailure
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vMainTilemap
00:fffe hStackBottom

BIN
boot/dmg0.bin Normal file

Binary file not shown.

40
boot/dmg0.sym Normal file
View File

@@ -0,0 +1,40 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0007 EntryPoint.clearVRAM
BOOT:0028 EntryPoint.checkLogo
BOOT:0036 EntryPoint.computeChecksum
BOOT:0042 EntryPoint.decompressLogo
BOOT:0054 EntryPoint.writeTilemapRow
BOOT:0056 EntryPoint.writeTilemapByte
BOOT:0063 ScrollLogo
BOOT:006e ScrollLogo.loop
BOOT:0083 ScrollLogo.playSound
BOOT:0089 ScrollLogo.dontPlaySound
BOOT:0098 Lockup
BOOT:009c Lockup.loop
BOOT:00a9 DecompressFirstNibble
BOOT:00aa DecompressSecondNibble
BOOT:00ac DecompressSecondNibble.loop
BOOT:00bc DelayFrames
BOOT:00be DelayFrames.loop
BOOT:00cb Logo
BOOT:00fd Done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vMainTilemap
00:fffe hStackBottom

BIN
boot/mgb.bin Normal file

Binary file not shown.

42
boot/mgb.sym Normal file
View File

@@ -0,0 +1,42 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:0007 EntryPoint.clearVRAM
BOOT:0027 EntryPoint.decompressLogo
BOOT:0039 EntryPoint.copyRTile
BOOT:0048 EntryPoint.writeTilemapRow
BOOT:004a EntryPoint.writeTilemapByte
BOOT:0055 ScrollLogo
BOOT:0060 ScrollLogo.loop
BOOT:0062 ScrollLogo.delayFrames
BOOT:0064 ScrollLogo.waitVBlank
BOOT:0080 ScrollLogo.playSound
BOOT:0086 ScrollLogo.dontPlaySound
BOOT:0095 DecompressFirstNibble
BOOT:0096 DecompressSecondNibble
BOOT:0098 DecompressSecondNibble.loop
BOOT:00a8 Logo
BOOT:00d8 RTile
BOOT:00e0 CheckLogo
BOOT:00e6 CheckLogo.compare
BOOT:00e9 CheckLogo.logoFailure
BOOT:00f4 CheckLogo.computeChecksum
BOOT:00fa CheckLogo.checksumFailure
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vMainTilemap
00:fffe hStackBottom

BIN
boot/sgb.bin Normal file

Binary file not shown.

44
boot/sgb.sym Normal file
View File

@@ -0,0 +1,44 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:000b EntryPoint.clearVRAM
BOOT:002b EntryPoint.clearBuffer
BOOT:0036 EntryPoint.copyHeader
BOOT:0039 EntryPoint.computeChecksum
BOOT:0052 EntryPoint.decompressHeader
BOOT:0064 EntryPoint.copyRTile
BOOT:0073 EntryPoint.writeTilemapRow
BOOT:0075 EntryPoint.writeTilemapByte
BOOT:0080 SendData
BOOT:0089 SendData.sendPacket
BOOT:0091 SendData.sendByte
BOOT:0095 SendData.sendBit
BOOT:009d SendData.gotBit
BOOT:00c2 Wait4Frames
BOOT:00c4 Wait4Frames.waitVBlank
BOOT:00cc Wait4Frames.wait
BOOT:00d3 DecompressFirstNibble
BOOT:00d4 DecompressSecondNibble
BOOT:00d6 DecompressSecondNibble.decompressBit
BOOT:00e6 RTile
BOOT:00fc Done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vMainTilemap
00:c000 wBuffer
00:c060 wBufferEnd
00:fffe hStackBottom

BIN
boot/sgb2.bin Normal file

Binary file not shown.

44
boot/sgb2.sym Normal file
View File

@@ -0,0 +1,44 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:000b EntryPoint.clearVRAM
BOOT:002b EntryPoint.clearBuffer
BOOT:0036 EntryPoint.copyHeader
BOOT:0039 EntryPoint.computeChecksum
BOOT:0052 EntryPoint.decompressHeader
BOOT:0064 EntryPoint.copyRTile
BOOT:0073 EntryPoint.writeTilemapRow
BOOT:0075 EntryPoint.writeTilemapByte
BOOT:0080 SendData
BOOT:0089 SendData.sendPacket
BOOT:0091 SendData.sendByte
BOOT:0095 SendData.sendBit
BOOT:009d SendData.gotBit
BOOT:00c2 Wait4Frames
BOOT:00c4 Wait4Frames.waitVBlank
BOOT:00cc Wait4Frames.wait
BOOT:00d3 DecompressFirstNibble
BOOT:00d4 DecompressSecondNibble
BOOT:00d6 DecompressSecondNibble.decompressBit
BOOT:00e6 RTile
BOOT:00fc Done
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:013f HeaderMenufacturer
BOOT:0143 HeaderCGBCompat
BOOT:0144 HeaderNewLicensee
BOOT:0146 HeaderSGBFlag
BOOT:0147 HeaderCartType
BOOT:0148 HeaderROMSize
BOOT:0149 HeaderRAMSize
BOOT:014a HeaderRegionCode
BOOT:014b HeaderOldLicensee
BOOT:014c HeaderROMVersion
BOOT:014d HeaderChecksum
BOOT:014e HeaderGlobalChecksum
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vMainTilemap
00:c000 wBuffer
00:c060 wBufferEnd
00:fffe hStackBottom

BIN
boot/stadium2.bin Normal file

Binary file not shown.

98
boot/stadium2.sym Normal file
View File

@@ -0,0 +1,98 @@
; File generated by rgblink
BOOT:0000 EntryPoint
BOOT:000c EntryPoint.clearBGPalettes
BOOT:0013 ClearLogoGDMA
BOOT:0018 ClearLogoGDMAEnd
BOOT:0018 ClearLogoTilesGDMA
BOOT:001d RTile
BOOT:0025 Main
BOOT:0025 RTileEnd
BOOT:003c Main.clearOAM
BOOT:0047 Main.processLogo
BOOT:005b Main.copyRTile
BOOT:007f Main.nop
BOOT:0084 Main.lockup
BOOT:0086 ClearVRAM
BOOT:0089 ClearUntilMemBoundary
BOOT:008a ClearUntilMemBoundary.loop
BOOT:0090 Memcpy
BOOT:0097 SetupSound
BOOT:00ad SetupSound.initWaveRAM
BOOT:00b3 DecodeLogoHalf
BOOT:00b5 DecodeLogoHalf.decodeTileQuarter
BOOT:00be DecodeLogoHalf.decodingLeftHalf
BOOT:00cc DecodeLogoHalf.decodingRightHalf
BOOT:00e1 DecodeLogoHalf.goToRightHalf
BOOT:00e3 DecodeLogoHalf.decodingTopHalf
BOOT:00ea WaitVBlank
BOOT:00f0 WaitVBlank.wait
BOOT:0104 HeaderLogo
BOOT:0134 HeaderTitle
BOOT:0150 SetOBJAndBGPals
BOOT:0158 SetOBJAndBGPals.writeOBJPalData
BOOT:0162 SetOBJAndBGPals.writeBGPalData
BOOT:0168 CommitBGPalettes
BOOT:017b DoLogoAnimation
BOOT:018f DoLogoAnimation.writeNintendoLogoMap
BOOT:019a DoLogoAnimation.dontWriteNintendoLogo
BOOT:01b1 DoLogoAnimation.changePaletteRow
BOOT:01b3 DoLogoAnimation.changePaletteBlock
BOOT:01b5 DoLogoAnimation.changePaletteLoop
BOOT:01cf DoLogoAnimation.dontWriteLogoAttrMap
BOOT:01dd DoLogoAnimation.playSFX
BOOT:01e4 DoLogoAnimation.dontAnimateLogo
BOOT:01e4 DoLogoAnimation.dontPlaySFX
BOOT:01f5 DoLogoAnimation.stepAnimation
BOOT:01fa PerformFadeout
BOOT:01fc PerformFadeout.loop
BOOT:021f PerformFadeout.clearLogoArea
BOOT:0222 PerformFadeout.clearLogoTiles
BOOT:022b PerformFadeout.fadePalettes
BOOT:0233 PerformFadeout.fadeColor
BOOT:023b PerformFadeout.redCap
BOOT:024f PerformFadeout.greenCap
BOOT:0265 PerformFadeout.blueCap
BOOT:0270 DecompressFirstNibble
BOOT:0271 DecompressSecondNibble
BOOT:0274 DecompressSecondNibble.decompressBit
BOOT:0284 WriteLogoTilemap
BOOT:028c WriteLogoTilemap.writeRow
BOOT:028e WriteLogoTilemap.writeByte
BOOT:0299 WriteLogoTilemap.done
BOOT:029a SetupGameBoyLogo
BOOT:02a9 SetupGameBoyLogo.copyLogoRow
BOOT:02c8 SetupGameBoyLogo.copyRTile
BOOT:02d6 SetupGameBoyLogo.writeAttrRow
BOOT:02d8 SetupGameBoyLogo.writeAttrByte
BOOT:02eb SetupGameBoyLogo.writeTilemapByte
BOOT:02f3 SetupGameBoyLogo.notFirstRow
BOOT:02fa SetupGameBoyLogo.notSecondRow
BOOT:0306 SetupGameBoyLogo.initBGPalsLoop
BOOT:0320 GameBoyLogoTiles
BOOT:03e0 BootAnimationColors
BOOT:03e0 GameBoyLogoTilesEnd
BOOT:03ec BootAnimationColors.end
00:8000 vBlankTile
00:8010 vLogoTiles
00:8190 vRTile
00:9800 vTileMap
00:98c2 vGameBoyLogoMap
00:9904 vBigNintendoLogoMap
00:99a7 vNintendoLogoMap
01:8000 vTiles
01:8080 vGameBoyLogoTiles
01:8380 vNintendoLogoTiles
01:83e0 vSecondRTile
01:83f0 vNintendoLogoTilesEnd
01:9800 vAttrMap
01:98c2 vGameBoyLogoAttrs
01:9904 vBigNintendoLogoAttrs
01:99a7 vNintendoLogoAttrs
02:d000 wWorkRAM
02:d002 wPreventTerminationCounter
02:d300 wZeroBuffer
02:d800 wOBJPalBuffer
02:d840 wBGPalBuffer
02:d840 wOBJPalBufferEnd
02:d880 wBGPalBufferEnd
00:fffe hStackBottom

BIN
cpu_instrs/cpu_instrs.gb Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

119
cpu_instrs/readme.txt Normal file
View File

@@ -0,0 +1,119 @@
Game Boy CPU Instruction Behavior Test
--------------------------------------
This ROM tests the behavior of all CPU instructions except STOP and the
11 illegal opcodes. The tests are fairly thorough, running instructions
with boundary data and verifying both the result and that other
registers are not modified. Instructions which perform the same
operation on different registers are each tested just as thoroughly, in
case an emulator implements each independently. Some sub-tests take half
minute to complete.
Failed instructions are listed as
[CB] opcode
Some errors cannot of course be diagnosed properly, since the test
framework itself relies on basic instruction behavior being correct.
Internal operation
------------------
The main tests use a framework that runs each instruction in a loop,
varying the register values on input and examining them on output.
Rather than keep a table of correct values, it simply calculates a
CRC-32 checksum of all the output, then compares this with the correct
value. Instructions are divided into several groups, each with a
different set of input values suited for their behavior; for example,
the bit test instructions are fed $01, $02, $04 ... $40, $80, to ensure
each bit is handled properly, while the arithmetic instructions are fed
$01, $0F, $10, $7F, $FF, to exercise carry and half-carry. A few
instructions require a custom test due to their uniqueness.
Multi-ROM
---------
In the main directory is a single ROM which runs all the tests. It
prints a test's number, runs the test, then "ok" if it passes, otherwise
a failure code. Once all tests have completed it either reports that all
tests passed, or prints the number of failed tests. Finally, it makes
several beeps. If a test fails, it can be run on its own by finding the
corresponding ROM in individual/.
Ths compact format on screen is to avoid having the results scroll off
the top, so the test can be started and allowed to run without having to
constantly monitor the display.
Currently there is no well-defined way for an emulator test rig to
programatically find the result of the test; contact me if you're trying
to do completely automated testing of your emulator. One simple approach
is to take a screenshot after all tests have run, or even just a
checksum of one, and compare this with a previous run.
Failure codes
-------------
Failed tests may print a failure code, and also short description of the
problem. For more information about a failure code, look in the
corresponding source file in source/; the point in the code where
"set_test n" occurs is where that failure code will be generated.
Failure code 1 is a general failure of the test; any further information
will be printed.
Note that once a sub-test fails, no further tests for that file are run.
Console output
--------------
Information is printed on screen in a way that needs only minimum LCD
support, and won't hang if LCD output isn't supported at all.
Specifically, while polling LY to wait for vblank, it will time out if
it takes too long, so LY always reading back as the same value won't
hang the test. It's also OK if scrolling isn't supported; in this case,
text will appear starting at the top of the screen.
Everything printed on screen is also sent to the game link port by
writing the character to SB, then writing $81 to SC. This is useful for
tests which print lots of information that scrolls off screen.
Source code
-----------
Source code is included for all tests, in source/. It can be used to
build the individual test ROMs. Code for the multi test isn't included
due to the complexity of putting everything together.
Code is written for the wla-dx assembler. To assemble a particular test,
execute
wla -o "source_filename.s" test.o
wlalink linkfile test.gb
Test code uses a common shell framework contained in common/.
Internal framework operation
----------------------------
Tests use a common framework for setting things up, reporting results,
and ending. All files first include "shell.inc", which sets up the ROM
header and shell code, and includes other commonly-used modules.
One oddity is that test code is first copied to internal RAM at $D000,
then executed there. This allows self-modification, and ensures the code
is executed the same way it is on my devcart, which doesn't have a
rewritable ROM as most do.
Some macros are used to simplify common tasks:
Macro Behavior
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
wreg addr,data Writes data to addr using LDH
lda addr Loads byte from addr into A using LDH
sta addr Stores A at addr using LDH
delay n Delays n cycles, where NOP = 1 cycle
delay_msec n Delays n milliseconds
set_test n,"Cause" Sets failure code and optional string
Routines and macros are documented where they are defined.
--
Shay Green <gblargg@gmail.com>

View File

@@ -0,0 +1,78 @@
; Tests instructions that don't fit template
.include "shell.inc"
main:
set_test 2,"JR negative"
ld a,0
jp jr_neg
inc a
- inc a
inc a
cp 2
jp nz,test_failed
jp +
jr_neg:
jr -
+
set_test 3,"JR positive"
ld a,0
jr +
inc a
+ inc a
inc a
cp 2
jp nz,test_failed
set_test 4,"LD PC,HL"
ld hl,+
ld a,0
ld pc,hl
inc a
+ inc a
inc a
cp 2
jp nz,test_failed
set_test 5,"POP AF"
ld bc,$1200
- push bc
pop af
push af
pop de
ld a,c
and $F0
cp e
jp nz,test_failed
inc b
inc c
jr nz,-
set_test 6,"DAA"
; Test all combinations of A and flags (256*16 total)
ld de,0
- push de
pop af
daa
push af
call update_crc
pop hl
ld a,l
call update_crc
inc d
jr nz,-
ld a,e
add $10
ld e,a
jr nz,-
check_crc $6A9F8D8A
jp tests_passed

View File

@@ -0,0 +1,73 @@
; Tests DI, EI, and HALT (STOP proved untestable)
.include "shell.inc"
main:
wreg IE,$04
set_test 2,"EI"
ei
ld bc,0
push bc
pop bc
inc b
wreg IF,$04
interrupt_addr:
dec b
jp nz,test_failed
ld hl,sp-2
ldi a,(hl)
cp <interrupt_addr
jp nz,test_failed
ld a,(hl)
cp >interrupt_addr
jp nz,test_failed
lda IF
and $04
jp nz,test_failed
set_test 3,"DI"
di
ld bc,0
push bc
pop bc
wreg IF,$04
ld hl,sp-2
ldi a,(hl)
or (hl)
jp nz,test_failed
lda IF
and $04
jp z,test_failed
set_test 4,"Timer doesn't work"
wreg TAC,$05
wreg TIMA,0
wreg IF,0
delay 500
lda IF
delay 500
and $04
jp nz,test_failed
delay 500
lda IF
and $04
jp z,test_failed
pop af
set_test 5,"HALT"
wreg TAC,$05
wreg TIMA,0
wreg IF,0
halt ; timer interrupt will exit halt
nop ; avoids DMG bug
lda IF
and $04
jp z,test_failed
jp tests_passed
.bank 0 slot 0
.org $50
inc a
ret

View File

@@ -0,0 +1,102 @@
; Tests SP/HL instructions
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $33,0,0 ; INC SP
.byte $3B,0,0 ; DEC SP
.byte $39,0,0 ; ADD HL,SP
.byte $F9,0,0 ; LD SP,HL
.byte $E8,$01,0 ; ADD SP,1
.byte $E8,$FF,0 ; ADD SP,-1
.byte $F8,$01,0 ; LD HL,SP+1
.byte $F8,$FF,0 ; LD HL,SP-1
instrs_end:
test_instr:
; C = flags register
ld c,$00
call test
ld c,$F0
call test
ret
test:
; Go through each value for HL
ld hl,values
hl_loop:
ld e,(hl)
inc hl
ld d,(hl)
inc hl
push hl
; Go through each value for SP
ld hl,values
values_loop:
push bc
push de
push hl
push bc
pop af
; Switch stack
ld (temp),sp
ld a,(hl+)
ld h,(hl)
ld l,a
; call print_regs
ld sp,hl
; Set registers
ld h,d
ld l,e
ld a,$12
ld bc,$5691
ld de,$9ABC
jp instr
instr_done:
; Save new SP and switch to yet another stack
ld (temp+2),sp
ld sp,$DF70
call checksum_af_bc_de_hl
; Checksum SP
ld a,(temp+2)
call update_crc_fast
ld a,(temp+3)
call update_crc_fast
ldsp temp
pop hl
pop de
pop bc
inc hl
inc hl
ld a,l
cp <values_end
jr nz,values_loop
pop hl
ld a,l
cp <values_end
jr nz,hl_loop
ret
values:
.word $0000,$0001,$000F,$0010,$001F,$007F,$0080,$00FF
.word $0100,$0F00,$1F00,$1000,$7FFF,$8000,$FFFF
values_end:
.word $0000,$0001,$000F,$0010,$001F,$007F,$0080,$00FF
.word $0100,$0F00,$1F00,$1000,$7FFF,$8000,$FFFF
checksums:
.byte $BC,$F4,$CD,$8C,$C7,$5E,$89,$E5,$36,$65,$21,$55,$D6,$6A,$2A,$FF
.byte $EB,$34,$37,$B9,$08,$5F,$22,$13,$B6,$2A,$37,$C3,$72,$43,$5C,$4D

View File

@@ -0,0 +1,88 @@
; Tests immediate instructions
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $36,0,0 ; LD (HL),$00
.byte $06,0,0 ; LD B,$00
.byte $0E,0,0 ; LD C,$00
.byte $16,0,0 ; LD D,$00
.byte $1E,0,0 ; LD E,$00
.byte $26,0,0 ; LD H,$00
.byte $2E,0,0 ; LD L,$00
.byte $3E,0,0 ; LD A,$00
.byte $F6,0,0 ; OR $00
.byte $FE,0,0 ; CP $00
.byte $C6,0,0 ; ADD $00
.byte $CE,0,0 ; ADC $00
.byte $D6,0,0 ; SUB $00
.byte $DE,0,0 ; SBC $00
.byte $E6,0,0 ; AND $00
.byte $EE,0,0 ; XOR $00
instrs_end:
test_instr:
ld c,$00
call test
ld c,$10
call test
ld c,$E0
call test
ld c,$F0
call test
ret
test:
; Go through each value for A
ld hl,values
a_loop:
ld b,(hl)
push hl
; Go through each value for immediate data
ld hl,values
values_loop:
push bc
push hl
; Set registers
push bc
ld a,(hl)
ld (instr+1),a
ld bc,$1234
ld de,$5678
ld hl,rp_temp
pop af
; call print_regs
jp instr
instr_done:
; Checksum registers and (hl)
call checksum_af_bc_de_hl
ld a,(rp_temp)
call update_crc_fast
pop hl
pop bc
inc hl
ld a,l
cp <values_end
jr nz,values_loop
pop hl
inc hl
ld a,l
cp <values_end
jr nz,a_loop
ret
values:
.byte $00,$01,$0F,$10,$1F,$7F,$80,$F0,$FF
values_end:
checksums:
.byte $7F,$7F,$05,$B7,$85,$82,$94,$B6,$D8,$0A,$D6,$F5,$44,$8C,$37,$2A,$FB,$46,$05,$FA,$BD,$2F,$9E,$C1,$5A,$56,$2A,$DA,$D0,$EE,$14,$BA,$EA,$42,$36,$D2,$87,$28,$AB,$30,$4D,$A2,$63,$C6,$34,$4E,$55,$08,$9B,$1C,$97,$0E,$49,$F8,$73,$D4,$86,$C7,$DC,$C6,$03,$BF,$43,$21,

View File

@@ -0,0 +1,98 @@
; Tests BC/DE/HL arithmetic
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $0B,0,0 ; DEC BC
.byte $1B,0,0 ; DEC DE
.byte $2B,0,0 ; DEC HL
.byte $03,0,0 ; INC BC
.byte $13,0,0 ; INC DE
.byte $23,0,0 ; INC HL
.byte $09,0,0 ; ADD HL,BC
.byte $19,0,0 ; ADD HL,DE
.byte $29,0,0 ; ADD HL,HL
instrs_end:
test_instr:
ld c,$00
call test
ld c,$10
call test
ld c,$E0
call test
ld c,$F0
call test
ret
test:
; Go through each value for HL
ld hl,values
hl_loop:
ld e,(hl)
inc hl
ld d,(hl)
inc hl
push hl
; Go through each value for BC, DE, A
ld hl,values
values_loop:
push bc
push de
push hl
push de
push bc
; BC
ld c,(hl)
inc hl
ld b,(hl)
inc hl
; DE
ld e,(hl)
inc hl
ld d,(hl)
inc hl
; HL, AF
pop af
ld a,(hl)
pop hl
; call print_regs
jp instr
instr_done:
; Checksum registers
call checksum_af_bc_de_hl
pop hl
pop de
pop bc
inc hl
inc hl
ld a,l
cp <values_end
jr nz,values_loop
pop hl
ld a,l
cp <values_end
jr nz,hl_loop
ret
values:
.word $0000,$0001,$000F,$0010,$001F,$007F,$0080,$00FF
.word $0100,$0F00,$1F00,$1000,$7FFF,$8000,$FFFF
values_end:
.word $0000,$0001,$000F,$0010,$001F,$007F,$0080,$00FF
.word $0100,$0F00,$1F00,$1000,$7FFF,$8000,$FFFF
checksums:
.byte $C0,$A1,$36,$A3,$BE,$15,$B8,$2B,$9F,$93,$C6,$C2,$86,$C0,$07,$81,$0F,$75,$35,$38,$6B,$C7,$0A,$1B,$06,$68,$4B,$42,$64,$B4,$8C,$18,$FB,$6C,$31,$94,

View File

@@ -0,0 +1,115 @@
; Tests LD r,r ($40-$7F)
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $40,0,0 ; LD B,B
.byte $41,0,0 ; LD B,C
.byte $42,0,0 ; LD B,D
.byte $43,0,0 ; LD B,E
.byte $44,0,0 ; LD B,H
.byte $45,0,0 ; LD B,L
.byte $46,0,0 ; LD B,(HL)
.byte $47,0,0 ; LD B,A
.byte $48,0,0 ; LD C,B
.byte $49,0,0 ; LD C,C
.byte $4A,0,0 ; LD C,D
.byte $4B,0,0 ; LD C,E
.byte $4C,0,0 ; LD C,H
.byte $4D,0,0 ; LD C,L
.byte $4E,0,0 ; LD C,(HL)
.byte $4F,0,0 ; LD C,A
.byte $50,0,0 ; LD D,B
.byte $51,0,0 ; LD D,C
.byte $52,0,0 ; LD D,D
.byte $53,0,0 ; LD D,E
.byte $54,0,0 ; LD D,H
.byte $55,0,0 ; LD D,L
.byte $56,0,0 ; LD D,(HL)
.byte $57,0,0 ; LD D,A
.byte $58,0,0 ; LD E,B
.byte $59,0,0 ; LD E,C
.byte $5A,0,0 ; LD E,D
.byte $5B,0,0 ; LD E,E
.byte $5C,0,0 ; LD E,H
.byte $5D,0,0 ; LD E,L
.byte $5E,0,0 ; LD E,(HL)
.byte $5F,0,0 ; LD E,A
.byte $60,0,0 ; LD H,B
.byte $61,0,0 ; LD H,C
.byte $62,0,0 ; LD H,D
.byte $63,0,0 ; LD H,E
.byte $64,0,0 ; LD H,H
.byte $65,0,0 ; LD H,L
.byte $66,0,0 ; LD H,(HL)
.byte $67,0,0 ; LD H,A
.byte $68,0,0 ; LD L,B
.byte $69,0,0 ; LD L,C
.byte $6A,0,0 ; LD L,D
.byte $6B,0,0 ; LD L,E
.byte $6C,0,0 ; LD L,H
.byte $6D,0,0 ; LD L,L
.byte $6E,0,0 ; LD L,(HL)
.byte $6F,0,0 ; LD L,A
.byte $70,0,0 ; LD (HL),B
.byte $71,0,0 ; LD (HL),C
.byte $72,0,0 ; LD (HL),D
.byte $73,0,0 ; LD (HL),E
.byte $74,0,0 ; LD (HL),H
.byte $75,0,0 ; LD (HL),L
.byte $77,0,0 ; LD (HL),A
.byte $78,0,0 ; LD A,B
.byte $79,0,0 ; LD A,C
.byte $7A,0,0 ; LD A,D
.byte $7B,0,0 ; LD A,E
.byte $7C,0,0 ; LD A,H
.byte $7D,0,0 ; LD A,L
.byte $7E,0,0 ; LD A,(HL)
.byte $7F,0,0 ; LD A,A
instrs_end:
test_instr:
ld c,$00
call test
ld c,$10
call test
ld c,$E0
call test
ld c,$F0
call test
ret
test:
; Put different value in each register and (hl_temp)
ld b,$BC
push bc
ld a,$DE
ld (rp_temp),a
ld a,$12
ld bc,$3456
ld de,$789A
ld hl,rp_temp ; (HL) points to RAM
pop af
; call print_regs
jp instr
instr_done:
; Checksum registers and (HL)
call checksum_af_bc_de_hl
ld a,(rp_temp)
call update_crc_fast
ret
checksums:
.byte $40,$3A,$AF,$06,$B6,$CB,$B2,$AB,$6F,$EF,$71,$9B,$75,$E3,$6C,$B9,$34,$FB,$26,$B7,$5A,$B9,$2F,$CE,$34,$FB,$26,$B7,$C2,$0A,$3B,$1A,$2A,$8A,$D6,$7C,$40,$3A,$AF,$06,$AF,$0A,$74,$70,$19,$A9,$6E,$6F,$11,$DA,$FE,$FE,$18,$10,$04,$2B,$11,$DA,$FE,$FE,$7B,$6A,$87,$84,$8B,$87,$34,$12,$00,$45,$DE,$01,$40,$3A,$AF,$06,$93,$E2,$8F,$C6,$DD,$7D,$90,$32,$FF,$90,$1B,$A8,$DD,$7D,$90,$32,$56,$BF,$7A,$21,$23,$C0,$FA,$06,$3B,$1D,$A0,$80,$3F,$44,$1B,$9C,$40,$3A,$AF,$06,$56,$25,$85,$CD,$D7,$B1,$DB,$F9,$56,$25,$85,$CD,$4E,$F8,$DF,$4B,$F0,$C3,$F9,$18,$20,$0F,$F6,$91,$71,$69,$CE,$46,$F0,$A0,$03,$4D,$40,$3A,$AF,$06,$29,$47,$E2,$36,$40,$3A,$AF,$06,$90,$F6,$A0,$8F,$3D,$62,$26,$A9,$A4,$52,$C1,$75,$45,$ED,$75,$40,$8A,$4D,$63,$56,$AF,$BA,$2D,$FE,$40,$3A,$AF,$06,$AF,$BA,$2D,$FE,$36,$8A,$CA,$22,$34,$8D,$C2,$65,$1A,$DB,$FF,$54,$32,$C0,$E8,$55,$ED,$4A,$87,$2F,$40,$3A,$AF,$06,$9D,$BC,$81,$E6,$6E,$6C,$92,$37,$B1,$EC,$C3,$29,$1D,$C5,$9F,$A1,$59,$6F,$66,$CD,$B4,$FB,$FD,$74,$EC,$13,$F3,$8E,$70,$0C,$5F,$ED,$EC,$13,$F3,$8E,$40,$3A,$AF,$06,

View File

@@ -0,0 +1,127 @@
; Tests branch instructions
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
; JR cond,skip
; INC A
; skip:
.byte $18,$01,$3C ; JR *+3
.byte $20,$01,$3C ; JR NZ,*+3
.byte $28,$01,$3C ; JR Z,*+3
.byte $30,$01,$3C ; JR NC,*+3
.byte $38,$01,$3C ; JR C,*+3
.byte $C2,<taken,>taken ; JP NZ,taken
.byte $C3,<taken,>taken ; JP taken
.byte $CA,<taken,>taken ; JP Z,taken
.byte $D2,<taken,>taken ; JP NC,taken
.byte $DA,<taken,>taken ; JP C,taken
.byte $C4,<taken,>taken ; CALL NZ,taken
.byte $CC,<taken,>taken ; CALL Z,taken
.byte $CD,<taken,>taken ; CALL taken
.byte $D4,<taken,>taken ; CALL NC,taken
.byte $DC,<taken,>taken ; CALL C,taken
; RET cond
; INC A
.byte $C0,$3C,0 ; RET NZ
.byte $C8,$3C,0 ; RET Z
.byte $C9,$3C,0 ; RET
.byte $D0,$3C,0 ; RET NC
.byte $D8,$3C,0 ; RET C
.byte $D9,$3C,0 ; RETI
; RST
; can only easily test this one on devcart
.byte $C7,0,0 ; RST $00
.ifndef BUILD_DEVCART
.byte $CF,0,0 ; RST $08
.byte $D7,0,0 ; RST $10
.byte $DF,0,0 ; RST $18
.byte $E7,0,0 ; RST $20
.byte $EF,0,0 ; RST $28
.byte $F7,0,0 ; RST $30
.byte $FF,0,0 ; RST $38
.endif
instrs_end:
test_instr:
wreg IE,0 ; disable interrupts, since RETI does EI
; Go through all 16 combinations of flags
ld bc,$1200
-
; Fill 4 bytes of new stack
ld a,$12
ld ($DF80-2),a
ld a,$34
ld ($DF80-3),a
ld a,$56
ld ($DF80-4),a
ld a,$78
ld ($DF80-5),a
; Set AF
push bc
pop af
; Switch to new stack
ld (temp),sp
ld sp,$DF80
; Set return address
ld de,instr+3
push de
jp instr
instr_done:
inc a
taken:
di ; RETI enables interrupts
; Save new SP and switch to yet another stack
ld (temp+2),sp
ld sp,$DF70
; Checksum A and SP
call update_crc_fast
ld a,(temp+2)
call update_crc_fast
ld a,(temp+3)
call update_crc_fast
; Checksum 4 bytes of stack
ld a,($DF80-2)
call update_crc_fast
ld a,($DF80-3)
call update_crc_fast
ld a,($DF80-4)
call update_crc_fast
ld a,($DF80-5)
call update_crc_fast
ldsp temp
ld a,c
add $10
ld c,a
jr nz,-
ret
checksums:
.byte $EC,$A4,$94,$79,$C4,$00,$96,$2C,$C4,$64,$90,$33,$77,$C7,$0A,$D4
.byte $77,$A3,$0C,$CB,$79,$E7,$7E,$AE,$DA,$DC,$03,$F7,$4F,$9F,$E9,$20
.byte $72,$12,$DA,$01,$44,$6A,$4D,$8F,$D1,$79,$30,$4C,$AA,$37,$F2,$6A
.byte $97,$EA,$56,$5F,$32,$28,$C7,$D1,$49,$66,$05,$F7,$80,$0F,$BA,$8E
.byte $41,$E2,$A4,$9A,$2D,$2D,$8C,$72,$A5,$13,$76,$A8,$64,$FE,$68,$BC
.byte $2D,$2D,$8C,$72,$50,$96,$24,$27,$50,$96,$24,$27,$50,$96,$24,$27
.byte $50,$96,$24,$27,$50,$96,$24,$27,$50,$96,$24,$27,$50,$96,$24,$27
.byte $50,$96,$24,$27
.include "multi_custom.s"

View File

@@ -0,0 +1,110 @@
; Tests miscellaneous instructions
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $F0,$91,0 ; LDH A,($91)
.byte $E0,$91,0 ; LDH ($91),A
.byte $F2,$00,0 ; LDH A,(C)
.byte $E2,$00,0 ; LDH (C),A
.byte $FA,$91,$FF ; LD A,($FF91)
.byte $EA,$91,$FF ; LD ($FF91),A
.byte $08,$91,$FF ; LD ($FF91),SP
.byte $01,$23,$01 ; LD BC,$0123
.byte $11,$23,$01 ; LD DE,$0123
.byte $21,$23,$01 ; LD HL,$0123
.byte $31,$23,$01 ; LD SP,$0123
.byte $F5,0,0 ; PUSH AF
.byte $C5,0,0 ; PUSH BC
.byte $D5,0,0 ; PUSH DE
.byte $E5,0,0 ; PUSH HL
.byte $F1,0,0 ; POP AF
.byte $C1,0,0 ; POP BC
.byte $D1,0,0 ; POP DE
.byte $E1,0,0 ; POP HL
instrs_end:
test_instr:
; C = flags register
ld c,$00
call test
ld c,$10
call test
ld c,$E0
call test
ld c,$F0
call test
ret
test:
; Fill RAM
ld a,$FE
ld ($FF90),a
ld a,$DC
ld ($FF91),a
ld a,$BA
ld ($FF92),a
; Fill stack
ld a,$13
ld ($DF80),a
ld a,$57
ld ($DF80-1),a
ld a,$9B
ld ($DF80-2),a
ld a,$DF
ld ($DF80-3),a
; Set registers
ld b,$12
push bc
ld bc,$5691
ld de,$9ABC
ld hl,$DEF0
pop af
; Switch stack
ld (temp),sp
ld sp,$DF80-2
jp instr
instr_done:
; Save new SP and switch to another stack
ld (temp+2),sp
ld sp,$DF70
call checksum_af_bc_de_hl
; Checksum SP
ld a,(temp+2)
call update_crc_fast
ld a,(temp+3)
call update_crc_fast
; Checksum RAM
ld a,($FF90)
call update_crc_fast
ld a,($FF91)
call update_crc_fast
ld a,($FF92)
call update_crc_fast
; Checksum stack
ld a,($DF80)
call update_crc_fast
ld a,($DF80-1)
call update_crc_fast
ld a,($DF80-2)
call update_crc_fast
ld a,($DF80-3)
call update_crc_fast
; Restore SP
ldsp temp
ret
checksums:
.byte $4D,$FF,$15,$97,$6D,$A7,$35,$65,$4D,$FF,$15,$97,$6D,$A7,$35,$65,$4D,$FF,$15,$97,$6D,$A7,$35,$65,$AD,$FA,$5E,$41,$D0,$78,$79,$C1,$AF,$66,$99,$34,$0D,$E1,$97,$99,$6F,$D0,$6F,$5D,$C3,$1F,$A3,$8A,$C2,$F1,$9C,$F3,$C1,$C3,$DC,$78,$C0,$2D,$E3,$01,$8F,$C4,$0F,$44,$95,$22,$6A,$39,$61,$C5,$AB,$55,$FB,$DF,$2C,$52,

View File

@@ -0,0 +1,269 @@
; Tests most register instructions.
; Takes 10 seconds.
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $00,0,0 ; NOP
.byte $2F,0,0 ; CPL
.byte $37,0,0 ; SCF
.byte $3F,0,0 ; CCF
.byte $B0,0,0 ; OR B
.byte $B1,0,0 ; OR C
.byte $B2,0,0 ; OR D
.byte $B3,0,0 ; OR E
.byte $B4,0,0 ; OR H
.byte $B5,0,0 ; OR L
.byte $B7,0,0 ; OR A
.byte $B8,0,0 ; CP B
.byte $B9,0,0 ; CP C
.byte $BA,0,0 ; CP D
.byte $BB,0,0 ; CP E
.byte $BC,0,0 ; CP H
.byte $BD,0,0 ; CP L
.byte $BF,0,0 ; CP A
.byte $80,0,0 ; ADD B
.byte $81,0,0 ; ADD C
.byte $82,0,0 ; ADD D
.byte $83,0,0 ; ADD E
.byte $84,0,0 ; ADD H
.byte $85,0,0 ; ADD L
.byte $87,0,0 ; ADD A
.byte $88,0,0 ; ADC B
.byte $89,0,0 ; ADC C
.byte $8A,0,0 ; ADC D
.byte $8B,0,0 ; ADC E
.byte $8C,0,0 ; ADC H
.byte $8D,0,0 ; ADC L
.byte $8F,0,0 ; ADC A
.byte $90,0,0 ; SUB B
.byte $91,0,0 ; SUB C
.byte $92,0,0 ; SUB D
.byte $93,0,0 ; SUB E
.byte $94,0,0 ; SUB H
.byte $95,0,0 ; SUB L
.byte $97,0,0 ; SUB A
.byte $98,0,0 ; SBC B
.byte $99,0,0 ; SBC C
.byte $9A,0,0 ; SBC D
.byte $9B,0,0 ; SBC E
.byte $9C,0,0 ; SBC H
.byte $9D,0,0 ; SBC L
.byte $9F,0,0 ; SBC A
.byte $A0,0,0 ; AND B
.byte $A1,0,0 ; AND C
.byte $A2,0,0 ; AND D
.byte $A3,0,0 ; AND E
.byte $A4,0,0 ; AND H
.byte $A5,0,0 ; AND L
.byte $A7,0,0 ; AND A
.byte $A8,0,0 ; XOR B
.byte $A9,0,0 ; XOR C
.byte $AA,0,0 ; XOR D
.byte $AB,0,0 ; XOR E
.byte $AC,0,0 ; XOR H
.byte $AD,0,0 ; XOR L
.byte $AF,0,0 ; XOR A
.byte $05,0,0 ; DEC B
.byte $0D,0,0 ; DEC C
.byte $15,0,0 ; DEC D
.byte $1D,0,0 ; DEC E
.byte $25,0,0 ; DEC H
.byte $2D,0,0 ; DEC L
.byte $3D,0,0 ; DEC A
.byte $04,0,0 ; INC B
.byte $0C,0,0 ; INC C
.byte $14,0,0 ; INC D
.byte $1C,0,0 ; INC E
.byte $24,0,0 ; INC H
.byte $2C,0,0 ; INC L
.byte $3C,0,0 ; INC A
.byte $07,0,0 ; RLCA
.byte $17,0,0 ; RLA
.byte $0F,0,0 ; RRCA
.byte $1F,0,0 ; RRA
.byte $CB,$00,0 ; RLC B
.byte $CB,$01,0 ; RLC C
.byte $CB,$02,0 ; RLC D
.byte $CB,$03,0 ; RLC E
.byte $CB,$04,0 ; RLC H
.byte $CB,$05,0 ; RLC L
.byte $CB,$07,0 ; RLC A
.byte $CB,$08,0 ; RRC B
.byte $CB,$09,0 ; RRC C
.byte $CB,$0A,0 ; RRC D
.byte $CB,$0B,0 ; RRC E
.byte $CB,$0C,0 ; RRC H
.byte $CB,$0D,0 ; RRC L
.byte $CB,$0F,0 ; RRC A
.byte $CB,$10,0 ; RL B
.byte $CB,$11,0 ; RL C
.byte $CB,$12,0 ; RL D
.byte $CB,$13,0 ; RL E
.byte $CB,$14,0 ; RL H
.byte $CB,$15,0 ; RL L
.byte $CB,$17,0 ; RL A
.byte $CB,$18,0 ; RR B
.byte $CB,$19,0 ; RR C
.byte $CB,$1A,0 ; RR D
.byte $CB,$1B,0 ; RR E
.byte $CB,$1C,0 ; RR H
.byte $CB,$1D,0 ; RR L
.byte $CB,$1F,0 ; RR A
.byte $CB,$20,0 ; SLA B
.byte $CB,$21,0 ; SLA C
.byte $CB,$22,0 ; SLA D
.byte $CB,$23,0 ; SLA E
.byte $CB,$24,0 ; SLA H
.byte $CB,$25,0 ; SLA L
.byte $CB,$27,0 ; SLA A
.byte $CB,$28,0 ; SRA B
.byte $CB,$29,0 ; SRA C
.byte $CB,$2A,0 ; SRA D
.byte $CB,$2B,0 ; SRA E
.byte $CB,$2C,0 ; SRA H
.byte $CB,$2D,0 ; SRA L
.byte $CB,$2F,0 ; SRA A
.byte $CB,$30,0 ; SWAP B
.byte $CB,$31,0 ; SWAP C
.byte $CB,$32,0 ; SWAP D
.byte $CB,$33,0 ; SWAP E
.byte $CB,$34,0 ; SWAP H
.byte $CB,$35,0 ; SWAP L
.byte $CB,$37,0 ; SWAP A
.byte $CB,$38,0 ; SRL B
.byte $CB,$39,0 ; SRL C
.byte $CB,$3A,0 ; SRL D
.byte $CB,$3B,0 ; SRL E
.byte $CB,$3C,0 ; SRL H
.byte $CB,$3D,0 ; SRL L
.byte $CB,$3F,0 ; SRL A
instrs_end:
test_instr:
ld c,$00
call test
ld c,$F0
call test
ret
test:
; Go through each value for A
ld hl,values
a_loop:
ld b,(hl)
push hl
; Go through each value for other registers
ld hl,values
values_loop:
push bc
push hl
push bc
; BC
ld a,(hl+)
ld b,a
ld a,(hl+)
ld c,a
; HL
ld a,(hl+)
ld d,a
ld a,(hl+)
ld e,a
push de
; DE
ld a,(hl+)
ld d,a
ld a,(hl+)
ld e,a
pop hl
pop af
; call print_regs
jp instr
instr_done:
; Checksum registers
call checksum_af_bc_de_hl
pop hl
pop bc
inc hl
ld a,l
cp <values_end
jr nz,values_loop
pop hl
inc hl
ld a,l
cp <values_end
jr nz,a_loop
ret
values:
.byte $00,$01,$0F,$10,$1F,$7F,$80,$F0,$FF
values_end:
.byte $00,$01,$0F,$10,$1F,$7F,$80,$F0,$FF
checksums:
.byte $7C,$55,$BD,$05,$BA,$C7,$AC,$D1,$74,$6D,$82,$4A,$0F,$06,$2A,$C5
.byte $FA,$97,$9B,$9D,$C3,$32,$A0,$78,$00,$C1,$9F,$69,$C0,$D1,$C2,$A1
.byte $55,$0D,$3F,$C8,$09,$7D,$97,$92,$CE,$66,$30,$56,$95,$F3,$01,$A1
.byte $5B,$97,$54,$4C,$56,$FC,$A0,$89,$42,$F8,$7B,$2A,$E6,$7C,$03,$40
.byte $45,$60,$C5,$A8,$B7,$BF,$B0,$EF,$A0,$7A,$1B,$4F,$FB,$22,$B4,$33
.byte $06,$3D,$B5,$C7,$3C,$A4,$D5,$23,$C1,$BE,$75,$8B,$E0,$9B,$98,$BB
.byte $0E,$75,$D9,$E6,$82,$A7,$E2,$66,$CD,$78,$4F,$E8,$8E,$D4,$2D,$3E
.byte $88,$5C,$58,$C7,$F9,$20,$5F,$B9,$A8,$E4,$CA,$5E,$C8,$DB,$88,$94
.byte $A3,$0D,$87,$60,$8B,$BA,$2B,$27,$41,$88,$83,$B1,$0A,$41,$9E,$D6
.byte $98,$8D,$19,$B7,$13,$C6,$D5,$BF,$83,$CE,$74,$9F,$00,$34,$07,$5E
.byte $F0,$E1,$1A,$68,$8F,$BA,$85,$A7,$A0,$46,$06,$A5,$75,$F9,$83,$48
.byte $12,$EF,$1B,$03,$C8,$FB,$79,$EA,$9B,$00,$6C,$A9,$0D,$5E,$CB,$57
.byte $41,$1B,$4B,$0C,$B2,$08,$D8,$E3,$43,$07,$E1,$93,$34,$73,$23,$C9
.byte $18,$2F,$38,$F9,$D1,$3B,$AB,$5A,$BF,$C6,$F8,$03,$50,$0C,$A4,$32
.byte $6B,$06,$7E,$FE,$ED,$8B,$D4,$15,$29,$46,$6D,$24,$6E,$5B,$15,$1A
.byte $32,$AE,$87,$B0,$DC,$20,$AC,$4B,$2B,$63,$60,$C7,$C1,$92,$75,$AA
.byte $6F,$CA,$17,$53,$5A,$C5,$78,$EA,$61,$01,$10,$83,$DD,$08,$D8,$78
.byte $CA,$0B,$F5,$1F,$92,$55,$08,$01,$7F,$EA,$CD,$9B,$2A,$AA,$73,$17
.byte $E0,$9F,$D0,$BA,$E7,$73,$72,$3D,$B7,$95,$2F,$3B,$A7,$78,$50,$36
.byte $81,$04,$5B,$9E,$9A,$DE,$A4,$DD,$21,$B2,$9B,$36,$9F,$D7,$C8,$32
.byte $48,$0E,$FC,$E5,$55,$C3,$53,$75,$A4,$ED,$A9,$E0,$9E,$78,$A7,$1D
.byte $B8,$F4,$7C,$D6,$90,$2A,$03,$87,$81,$D8,$D5,$90,$63,$02,$C4,$52
.byte $C2,$BE,$85,$B3,$32,$9A,$9E,$2D,$E3,$FB,$22,$47,$8E,$65,$08,$73
.byte $72,$5A,$73,$95,$ED,$EC,$59,$9D,$C8,$67,$68,$F1,$4B,$ED,$41,$D5
.byte $68,$39,$75,$F3,$FC,$09,$EF,$0D,$20,$2B,$43,$A3,$69,$AA,$89,$4F
.byte $84,$87,$7B,$58,$42,$0A,$56,$EF,$1B,$0E,$19,$CA,$6F,$1B,$F9,$17
.byte $EA,$B6,$4C,$B2,$1A,$C4,$C0,$B1,$E2,$B2,$45,$4E,$91,$0A,$8D,$AE
.byte $17,$31,$55,$A3,$1B,$69,$72,$D8,$03,$E9,$55,$8D,$87,$27,$36,$63
.byte $E6,$85,$12,$D1,$F2,$32,$97,$4D,$B5,$FA,$08,$A9,$97,$2A,$5A,$C2
.byte $FD,$2D,$A4,$27,$57,$7C,$EC,$BD,$CC,$67,$19,$21,$46,$D4,$CD,$D6
.byte $CB,$55,$D4,$E2,$9E,$F3,$32,$2E,$AA,$F8,$BB,$B3,$F6,$3A,$CC,$08
.byte $64,$8B,$C2,$5F,$58,$66,$AF,$67,$B3,$44,$2C,$66,$72,$E7,$3B,$3F
.byte $5B,$87,$0C,$17,$58,$E2,$B4,$A0,$70,$18,$81,$E6,$42,$56,$12,$CE
.byte $BB,$13,$46,$3C,$BE,$5A,$FB,$53

View File

@@ -0,0 +1,315 @@
; Tests most register instructions.
; Takes 15 seconds.
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $CB,$40,0 ; BIT 0,B
.byte $CB,$41,0 ; BIT 0,C
.byte $CB,$42,0 ; BIT 0,D
.byte $CB,$43,0 ; BIT 0,E
.byte $CB,$44,0 ; BIT 0,H
.byte $CB,$45,0 ; BIT 0,L
.byte $CB,$47,0 ; BIT 0,A
.byte $CB,$48,0 ; BIT 1,B
.byte $CB,$49,0 ; BIT 1,C
.byte $CB,$4A,0 ; BIT 1,D
.byte $CB,$4B,0 ; BIT 1,E
.byte $CB,$4C,0 ; BIT 1,H
.byte $CB,$4D,0 ; BIT 1,L
.byte $CB,$4F,0 ; BIT 1,A
.byte $CB,$50,0 ; BIT 2,B
.byte $CB,$51,0 ; BIT 2,C
.byte $CB,$52,0 ; BIT 2,D
.byte $CB,$53,0 ; BIT 2,E
.byte $CB,$54,0 ; BIT 2,H
.byte $CB,$55,0 ; BIT 2,L
.byte $CB,$57,0 ; BIT 2,A
.byte $CB,$58,0 ; BIT 3,B
.byte $CB,$59,0 ; BIT 3,C
.byte $CB,$5A,0 ; BIT 3,D
.byte $CB,$5B,0 ; BIT 3,E
.byte $CB,$5C,0 ; BIT 3,H
.byte $CB,$5D,0 ; BIT 3,L
.byte $CB,$5F,0 ; BIT 3,A
.byte $CB,$60,0 ; BIT 4,B
.byte $CB,$61,0 ; BIT 4,C
.byte $CB,$62,0 ; BIT 4,D
.byte $CB,$63,0 ; BIT 4,E
.byte $CB,$64,0 ; BIT 4,H
.byte $CB,$65,0 ; BIT 4,L
.byte $CB,$67,0 ; BIT 4,A
.byte $CB,$68,0 ; BIT 5,B
.byte $CB,$69,0 ; BIT 5,C
.byte $CB,$6A,0 ; BIT 5,D
.byte $CB,$6B,0 ; BIT 5,E
.byte $CB,$6C,0 ; BIT 5,H
.byte $CB,$6D,0 ; BIT 5,L
.byte $CB,$6F,0 ; BIT 5,A
.byte $CB,$70,0 ; BIT 6,B
.byte $CB,$71,0 ; BIT 6,C
.byte $CB,$72,0 ; BIT 6,D
.byte $CB,$73,0 ; BIT 6,E
.byte $CB,$74,0 ; BIT 6,H
.byte $CB,$75,0 ; BIT 6,L
.byte $CB,$77,0 ; BIT 6,A
.byte $CB,$78,0 ; BIT 7,B
.byte $CB,$79,0 ; BIT 7,C
.byte $CB,$7A,0 ; BIT 7,D
.byte $CB,$7B,0 ; BIT 7,E
.byte $CB,$7C,0 ; BIT 7,H
.byte $CB,$7D,0 ; BIT 7,L
.byte $CB,$7F,0 ; BIT 7,A
.byte $CB,$80,0 ; RES 0,B
.byte $CB,$81,0 ; RES 0,C
.byte $CB,$82,0 ; RES 0,D
.byte $CB,$83,0 ; RES 0,E
.byte $CB,$84,0 ; RES 0,H
.byte $CB,$85,0 ; RES 0,L
.byte $CB,$87,0 ; RES 0,A
.byte $CB,$88,0 ; RES 1,B
.byte $CB,$89,0 ; RES 1,C
.byte $CB,$8A,0 ; RES 1,D
.byte $CB,$8B,0 ; RES 1,E
.byte $CB,$8C,0 ; RES 1,H
.byte $CB,$8D,0 ; RES 1,L
.byte $CB,$8F,0 ; RES 1,A
.byte $CB,$90,0 ; RES 2,B
.byte $CB,$91,0 ; RES 2,C
.byte $CB,$92,0 ; RES 2,D
.byte $CB,$93,0 ; RES 2,E
.byte $CB,$94,0 ; RES 2,H
.byte $CB,$95,0 ; RES 2,L
.byte $CB,$97,0 ; RES 2,A
.byte $CB,$98,0 ; RES 3,B
.byte $CB,$99,0 ; RES 3,C
.byte $CB,$9A,0 ; RES 3,D
.byte $CB,$9B,0 ; RES 3,E
.byte $CB,$9C,0 ; RES 3,H
.byte $CB,$9D,0 ; RES 3,L
.byte $CB,$9F,0 ; RES 3,A
.byte $CB,$A0,0 ; RES 4,B
.byte $CB,$A1,0 ; RES 4,C
.byte $CB,$A2,0 ; RES 4,D
.byte $CB,$A3,0 ; RES 4,E
.byte $CB,$A4,0 ; RES 4,H
.byte $CB,$A5,0 ; RES 4,L
.byte $CB,$A7,0 ; RES 4,A
.byte $CB,$A8,0 ; RES 5,B
.byte $CB,$A9,0 ; RES 5,C
.byte $CB,$AA,0 ; RES 5,D
.byte $CB,$AB,0 ; RES 5,E
.byte $CB,$AC,0 ; RES 5,H
.byte $CB,$AD,0 ; RES 5,L
.byte $CB,$AF,0 ; RES 5,A
.byte $CB,$B0,0 ; RES 6,B
.byte $CB,$B1,0 ; RES 6,C
.byte $CB,$B2,0 ; RES 6,D
.byte $CB,$B3,0 ; RES 6,E
.byte $CB,$B4,0 ; RES 6,H
.byte $CB,$B5,0 ; RES 6,L
.byte $CB,$B7,0 ; RES 6,A
.byte $CB,$B8,0 ; RES 7,B
.byte $CB,$B9,0 ; RES 7,C
.byte $CB,$BA,0 ; RES 7,D
.byte $CB,$BB,0 ; RES 7,E
.byte $CB,$BC,0 ; RES 7,H
.byte $CB,$BD,0 ; RES 7,L
.byte $CB,$BF,0 ; RES 7,A
.byte $CB,$C0,0 ; SET 0,B
.byte $CB,$C1,0 ; SET 0,C
.byte $CB,$C2,0 ; SET 0,D
.byte $CB,$C3,0 ; SET 0,E
.byte $CB,$C4,0 ; SET 0,H
.byte $CB,$C5,0 ; SET 0,L
.byte $CB,$C7,0 ; SET 0,A
.byte $CB,$C8,0 ; SET 1,B
.byte $CB,$C9,0 ; SET 1,C
.byte $CB,$CA,0 ; SET 1,D
.byte $CB,$CB,0 ; SET 1,E
.byte $CB,$CC,0 ; SET 1,H
.byte $CB,$CD,0 ; SET 1,L
.byte $CB,$CF,0 ; SET 1,A
.byte $CB,$D0,0 ; SET 2,B
.byte $CB,$D1,0 ; SET 2,C
.byte $CB,$D2,0 ; SET 2,D
.byte $CB,$D3,0 ; SET 2,E
.byte $CB,$D4,0 ; SET 2,H
.byte $CB,$D5,0 ; SET 2,L
.byte $CB,$D7,0 ; SET 2,A
.byte $CB,$D8,0 ; SET 3,B
.byte $CB,$D9,0 ; SET 3,C
.byte $CB,$DA,0 ; SET 3,D
.byte $CB,$DB,0 ; SET 3,E
.byte $CB,$DC,0 ; SET 3,H
.byte $CB,$DD,0 ; SET 3,L
.byte $CB,$DF,0 ; SET 3,A
.byte $CB,$E0,0 ; SET 4,B
.byte $CB,$E1,0 ; SET 4,C
.byte $CB,$E2,0 ; SET 4,D
.byte $CB,$E3,0 ; SET 4,E
.byte $CB,$E4,0 ; SET 4,H
.byte $CB,$E5,0 ; SET 4,L
.byte $CB,$E7,0 ; SET 4,A
.byte $CB,$E8,0 ; SET 5,B
.byte $CB,$E9,0 ; SET 5,C
.byte $CB,$EA,0 ; SET 5,D
.byte $CB,$EB,0 ; SET 5,E
.byte $CB,$EC,0 ; SET 5,H
.byte $CB,$ED,0 ; SET 5,L
.byte $CB,$EF,0 ; SET 5,A
.byte $CB,$F0,0 ; SET 6,B
.byte $CB,$F1,0 ; SET 6,C
.byte $CB,$F2,0 ; SET 6,D
.byte $CB,$F3,0 ; SET 6,E
.byte $CB,$F4,0 ; SET 6,H
.byte $CB,$F5,0 ; SET 6,L
.byte $CB,$F7,0 ; SET 6,A
.byte $CB,$F8,0 ; SET 7,B
.byte $CB,$F9,0 ; SET 7,C
.byte $CB,$FA,0 ; SET 7,D
.byte $CB,$FB,0 ; SET 7,E
.byte $CB,$FC,0 ; SET 7,H
.byte $CB,$FD,0 ; SET 7,L
.byte $CB,$FF,0 ; SET 7,A
instrs_end:
test_instr:
ld c,$00
call test
ld c,$F0
call test
ret
test:
; Go through each value for A
ld hl,values
a_loop:
ld b,(hl)
push hl
; Go through each value for other registers
ld hl,values
values_loop:
push bc
push hl
push bc
; BC
ld a,(hl+)
ld b,a
ld a,(hl+)
ld c,a
; HL
ld a,(hl+)
ld d,a
ld a,(hl+)
ld e,a
push de
; DE
ld a,(hl+)
ld d,a
ld a,(hl+)
ld e,a
pop hl
pop af
; call print_regs
jp instr
instr_done:
; Checksum registers
call checksum_af_bc_de_hl
pop hl
pop bc
inc hl
ld a,l
cp <values_end
jr nz,values_loop
pop hl
inc hl
ld a,l
cp <values_end
jr nz,a_loop
ret
values:
.byte $00,$01,$02,$04,$08,$10,$20,$40,$80,$FF
values_end:
.byte $00,$01,$02,$04,$08,$10,$20,$40,$80,$FF
checksums:
.byte $46,$51,$4A,$16,$D4,$18,$B2,$4E,$ED,$B5,$15,$EA,$74,$66,$66,$3E
.byte $C2,$F3,$7F,$6A,$63,$CA,$62,$21,$72,$1E,$E4,$83,$6A,$56,$41,$1D
.byte $91,$90,$DB,$38,$54,$0A,$6C,$24,$02,$9E,$EA,$5B,$6D,$A7,$CB,$80
.byte $B4,$0B,$F3,$0F,$40,$38,$75,$BB,$AF,$30,$2B,$E5,$BD,$97,$D0,$33
.byte $83,$CB,$FD,$0A,$BB,$21,$93,$95,$28,$2F,$A2,$F6,$1B,$5F,$47,$E5
.byte $A3,$2E,$39,$63,$6C,$E0,$02,$BB,$78,$F1,$BA,$CB,$2C,$9F,$49,$E0
.byte $6C,$E0,$02,$BB,$04,$28,$A9,$FD,$5E,$D7,$2E,$93,$1B,$78,$08,$00
.byte $83,$CB,$FD,$0A,$BB,$21,$93,$95,$69,$17,$20,$96,$C3,$B4,$B6,$51
.byte $C1,$4E,$C3,$05,$72,$D0,$25,$98,$44,$F0,$99,$B7,$B4,$0B,$F3,$0F
.byte $54,$0A,$6C,$24,$45,$10,$2B,$9D,$86,$3C,$DF,$27,$02,$9E,$EA,$5B
.byte $B7,$B6,$4F,$60,$70,$E0,$E1,$AA,$C2,$F3,$7F,$6A,$63,$CA,$62,$21
.byte $80,$76,$41,$65,$AA,$3B,$D4,$2C,$ED,$B5,$15,$EA,$74,$66,$66,$3E
.byte $AD,$FF,$A0,$43,$7B,$4C,$06,$A4,$15,$32,$EE,$44,$43,$A6,$68,$3B
.byte $6F,$5D,$BE,$D4,$DA,$75,$1B,$EF,$9B,$4D,$99,$8F,$49,$E8,$A9,$1D
.byte $F5,$1B,$58,$3A,$92,$25,$2D,$51,$38,$5C,$62,$05,$DD,$A9,$63,$AD
.byte $E3,$78,$2F,$37,$90,$15,$DB,$62,$58,$E2,$E8,$35,$BB,$C1,$5A,$EA
.byte $06,$FE,$28,$AA,$4F,$5D,$64,$BF,$83,$CF,$7F,$B2,$F9,$A9,$90,$BF
.byte $DD,$06,$B6,$64,$25,$8A,$E0,$24,$FA,$40,$95,$13,$91,$61,$93,$0D
.byte $69,$A8,$0E,$0B,$AE,$FD,$DF,$1A,$D4,$98,$D8,$11,$61,$E9,$16,$66
.byte $BD,$82,$1F,$2C,$E2,$74,$26,$77,$13,$E4,$6A,$25,$D7,$DE,$8A,$4F
.byte $1F,$7B,$47,$BC,$DA,$DB,$31,$E7,$2B,$06,$2C,$39,$15,$FC,$1C,$0B
.byte $1A,$3B,$A0,$0F,$55,$E5,$D8,$1C,$6D,$6C,$7F,$B8,$14,$AD,$9C,$AF
.byte $92,$B6,$60,$40,$76,$E6,$6D,$2F,$9E,$CA,$45,$6D,$54,$97,$47,$35
.byte $EE,$39,$50,$63,$47,$8C,$8A,$AB,$18,$F7,$6D,$10,$B7,$A6,$74,$0C
.byte $11,$24,$9C,$F5,$64,$5D,$FB,$16,$65,$1C,$59,$C6,$B9,$E3,$30,$52
.byte $1D,$E4,$B8,$9E,$A3,$2F,$7B,$6F,$03,$20,$24,$41,$4C,$F7,$22,$B8
.byte $92,$A7,$75,$E3,$1D,$F2,$5E,$FD,$B7,$A4,$F3,$34,$BF,$F7,$37,$CA
.byte $67,$22,$D4,$4D,$DE,$1A,$99,$58,$B2,$65,$91,$12,$F2,$8C,$65,$08
.byte $69,$E2,$9B,$D3,$94,$8C,$71,$F1,$D8,$22,$29,$53,$E8,$6A,$D9,$55
.byte $3E,$24,$42,$EF,$38,$12,$AC,$02,$35,$84,$7D,$2C,$C2,$34,$AC,$E2
.byte $4B,$AA,$E0,$31,$8F,$A0,$F2,$13,$A8,$4F,$7B,$98,$02,$16,$3B,$D4
.byte $8D,$09,$58,$A4,$FF,$46,$CA,$17,$08,$AA,$78,$02,$4A,$CF,$72,$E1
.byte $A8,$55,$52,$89,$F8,$FD,$D6,$4E,$22,$E7,$8F,$C6,$80,$F1,$BB,$3C
.byte $09,$1B,$4A,$4A,$06,$A1,$FD,$54,$E4,$BF,$D8,$27,$14,$23,$42,$90
.byte $B3,$7B,$55,$14,$77,$22,$EE,$92,$E9,$37,$76,$8C,$7D,$CF,$B7,$C7
.byte $D2,$90,$17,$48,$BB,$52,$BC,$19,$AA,$91,$9F,$DC,$0D,$AA,$C9,$24
.byte $C8,$45,$DF,$AB,$B3,$83,$A8,$9E,$0F,$AA,$62,$2F,$C4,$C0,$28,$BA
.byte $32,$56,$99,$69,$C9,$77,$4B,$62,$6B,$FF,$B6,$DD,$42,$46,$7A,$00
.byte $DA,$E9,$67,$4D,$46,$9C,$B5,$92,$04,$B5,$F6,$03,$01,$3C,$A2,$47
.byte $40,$15,$4A,$D6,$04,$39,$BC,$2F,$E9,$E1,$39,$59,$9B,$6A,$A4,$12
.byte $97,$23,$99,$30,$9E,$A6,$70,$AD,$C7,$1B,$D6,$1F,$05,$15,$D2,$5B
.byte $29,$0F,$5A,$CC,$0A,$99,$A2,$68,$5D,$58,$ED,$9C,$B9,$82,$CD,$74

View File

@@ -0,0 +1,162 @@
; Tests (HL/BC/DE) instructions.
; Takes 20 seconds.
;.define PRINT_CHECKSUMS 1
.include "shell.inc"
.include "instr_test.s"
instrs:
.byte $0A,0,0 ; LD A,(BC)
.byte $1A,0,0 ; LD A,(DE)
.byte $02,0,0 ; LD (BC),A
.byte $12,0,0 ; LD (DE),A
.byte $2A,0,0 ; LD A,(HL+)
.byte $3A,0,0 ; LD A,(HL-)
.byte $22,0,0 ; LD (HL+),A
.byte $32,0,0 ; LD (HL-),A
.byte $B6,0,0 ; OR (HL)
.byte $BE,0,0 ; CP (HL)
.byte $86,0,0 ; ADD (HL)
.byte $8E,0,0 ; ADC (HL)
.byte $96,0,0 ; SUB (HL)
.byte $9E,0,0 ; SBC (HL)
.byte $A6,0,0 ; AND (HL)
.byte $AE,0,0 ; XOR (HL)
.byte $35,0,0 ; DEC (HL)
.byte $34,0,0 ; INC (HL)
.byte $CB,$06,0 ; RLC (HL)
.byte $CB,$0E,0 ; RRC (HL)
.byte $CB,$16,0 ; RL (HL)
.byte $CB,$1E,0 ; RR (HL)
.byte $CB,$26,0 ; SLA (HL)
.byte $CB,$2E,0 ; SRA (HL)
.byte $CB,$36,0 ; SWAP (HL)
.byte $CB,$3E,0 ; SRL (HL)
.byte $CB,$46,0 ; BIT 0,(HL)
.byte $CB,$4E,0 ; BIT 1,(HL)
.byte $CB,$56,0 ; BIT 2,(HL)
.byte $CB,$5E,0 ; BIT 3,(HL)
.byte $CB,$66,0 ; BIT 4,(HL)
.byte $CB,$6E,0 ; BIT 5,(HL)
.byte $CB,$76,0 ; BIT 6,(HL)
.byte $CB,$7E,0 ; BIT 7,(HL)
.byte $CB,$86,0 ; RES 0,(HL)
.byte $CB,$8E,0 ; RES 1,(HL)
.byte $CB,$96,0 ; RES 2,(HL)
.byte $CB,$9E,0 ; RES 3,(HL)
.byte $CB,$A6,0 ; RES 4,(HL)
.byte $CB,$AE,0 ; RES 5,(HL)
.byte $CB,$B6,0 ; RES 6,(HL)
.byte $CB,$BE,0 ; RES 7,(HL)
.byte $CB,$C6,0 ; SET 0,(HL)
.byte $CB,$CE,0 ; SET 1,(HL)
.byte $CB,$D6,0 ; SET 2,(HL)
.byte $CB,$DE,0 ; SET 3,(HL)
.byte $CB,$E6,0 ; SET 4,(HL)
.byte $CB,$EE,0 ; SET 5,(HL)
.byte $CB,$F6,0 ; SET 6,(HL)
.byte $CB,$FE,0 ; SET 7,(HL)
.byte $27,0,0 ; DAA
instrs_end:
test_instr:
ld c,$00
call test
ld c,$10
call test
ld c,$E0
call test
ld c,$F0
call test
ret
test:
; Go through each value for A
ld hl,values
a_loop:
ld b,(hl)
push hl
; Go through each value for (HL)
ld hl,values
values_loop:
push bc
push hl
push bc
; BC
ld a,(hl+)
ld bc,rp_temp
ld (rp_temp),a
; DE
ld a,(hl+)
ld de,rp_temp+1
ld (rp_temp+1),a
; HL
ld a,(hl)
ld hl,rp_temp+2
ld (rp_temp+2),a
; AF
pop af
; call print_regs
jp instr
instr_done:
; Checksum AF, HL, and (HL)
push hl
push af
call update_crc_fast
pop hl
ld a,l
call update_crc_fast
pop bc
ld a,b
call update_crc_fast
ld a,c
call update_crc_fast
ld a,(rp_temp)
call update_crc_fast
ld a,(rp_temp+1)
call update_crc_fast
ld a,(rp_temp+2)
call update_crc_fast
pop hl
pop bc
inc hl
ld a,l
cp <values_end
jr nz,values_loop
pop hl
inc hl
ld a,l
cp <values_end
jr nz,a_loop
ret
values:
.byte $00,$01,$0F,$10,$1F,$7F,$80,$F0,$FF,$02,$04,$08,$20,$40
values_end:
.byte $00,$01,$0F,$10,$1F,$7F,$80,$F0,$FF,$02,$04,$08,$20,$40
checksums:
.byte $E0,$E5,$09,$A7,$FB,$28,$0D,$AE,$AC,$BB,$91,$D8,$B3,$E2,$AF,$C4
.byte $3D,$B5,$02,$07,$4F,$6E,$5B,$7E,$AE,$02,$E7,$14,$DC,$D9,$BE,$6D
.byte $F1,$48,$A9,$42,$67,$08,$FE,$57,$06,$6A,$A9,$B1,$FD,$A5,$84,$F0
.byte $82,$FC,$24,$A9,$A8,$1D,$BB,$E2,$F8,$23,$8C,$DE,$0E,$1D,$64,$D1
.byte $05,$E0,$24,$41,$53,$75,$47,$55,$F4,$D9,$10,$6A,$38,$16,$28,$D8
.byte $D1,$28,$A3,$E0,$A2,$05,$B8,$FE,$B0,$F4,$F5,$8F,$4B,$39,$03,$B0
.byte $8A,$07,$BA,$90,$25,$99,$A7,$78,$E6,$9A,$D1,$49,$C9,$B2,$A3,$E5
.byte $36,$34,$CB,$5A,$97,$42,$71,$09,$39,$87,$25,$EC,$54,$EE,$C5,$B3
.byte $FC,$B5,$6F,$BD,$0B,$D8,$46,$6F,$6A,$27,$81,$9F,$F8,$38,$E2,$71
.byte $55,$19,$21,$83,$4B,$85,$9F,$4B,$A1,$78,$14,$60,$58,$08,$D9,$57
.byte $11,$8C,$83,$9A,$9F,$01,$D1,$90,$E8,$82,$0B,$5A,$BD,$75,$86,$21
.byte $DF,$83,$E9,$23,$1E,$B6,$7F,$D1,$4A,$18,$A5,$8E,$CF,$CF,$CA,$51
.byte $3F,$03,$A4,$96,$C3,$1F,$9E,$88,$0C,$DF,$1F,$B1

View File

@@ -0,0 +1,215 @@
; Sound chip utilities
; Turns APU off
; Preserved: BC, DE, HL
sound_off:
wreg NR52,0
ret
; Turns APU on
; Preserved: BC, DE, HL
sound_on:
wreg NR52,$80 ; power
wreg NR51,$FF ; mono
wreg NR50,$77 ; volume
ret
; Synchronizes to APU length counter within
; tens of clocks. Uses square 2 channel.
; Preserved: BC, DE, HL
sync_apu:
wreg NR24,$00 ; disable length
wreg NR21,$3E ; length = 2 (in case of extra len clk)
wreg NR22,$08 ; silent without disabling channel
wreg NR24,$C0 ; start length
- lda NR52 ; wait for length to reach zero
and $02
jr nz,-
ret
; Synchronizes to first square sweep within
; tens of clocks. Uses square 1 channel.
; Preserved: BC, DE, HL
sync_sweep:
wreg NR10,$11 ; sweep period = 1, shift = 1
wreg NR12,$08 ; silent without disabling channel
wreg NR13,$FF ; freq = $3FF
wreg NR14,$83 ; start
- lda NR52
and $01
jr nz,-
ret
; Copies 16-byte wave from (HL) to wave RAM
; Preserved: BC, DE
load_wave:
push bc
wreg NR30,$00 ; disable while writing
ld c,$30
- ld a,(hl+)
ld ($FF00+c),a
inc c
bit 6,c
jr z,-
pop bc
ret
; Makes short beep
; Preserved: BC, DE, HL
beep:
xor a ; sound off
sta NR52
dec a
sta NR52 ; sound on
sta NR51 ; mono
sta NR50 ; volume
wreg NR12,$F1 ; volume, envelope rate
wreg NR14,$86 ; note on, pitch
delay_msec 250
ret
; Marks sound with bits of A encoded into volume
; Preserved: BC, DE, HL
mark_sound:
push bc
ld c,a
ld b,8
wreg NR10,0
wreg NR11,$80
wreg NR13,$F8
- ld a,$60
rl c
jr nc,+
ld a,$A0
+ sta NR12
wreg NR14,$87
delay_usec 300
wreg NR12,0
delay_usec 100
dec b
jr nz,-
pop bc
ret
; Fills wave RAM with A
; Preserved: BC, DE, HL
fill_wave:
push bc
ld c,$30
- ld ($FF00+c),a
inc c
bit 6,c
jr z,-
pop bc
ret
; Gets current length counter value for
; channel with mask A into A. Length counter
; must be enabled for that channel.
; Preserved: BC, DE, HL
get_len_a:
push bc
ld c,a
ld b,0
- lda NR52 ; 3
and c ; 1
jr z,+ ; 2
delay 4096-10
inc b ; 1
jr nz,- ; 3
+ ld a,b
pop bc
ret
; Synchronizes exactly to length clock. Next length clock
; occurs by 4079 clocks after this returns. Uses NR2x.
; Preserved: AF, BC, DE, HL
sync_length:
push af
push hl
ld hl,NR52
wreg NR22,$08 ; silent without disabling channel
wreg NR24,$40 ; avoids extra length clock on trigger
wreg NR21,-2 ; length = 2, in case clock occurs immediately
wreg NR24,$C0 ; start length
; Coarse sync
ld a,$02
- and (hl)
jr nz,-
; Fine sync. Slowly moves "forward" until
; length clock occurs just before reading NR52.
- delay 4097-20
wreg NR21,-1 ; 5
wreg NR24,$C0 ; 5
lda NR52 ; 3
delay 2 ; 2
and $02 ; 2
jr nz,- ; 3
pop hl
pop af
ret
; Delays n*4096 cycles
; Preserved: BC, DE, HL
.macro delay_frames ; n
ld a,\1
call delay_frames_
.endm
; Delays A*4096+13 cycles (including CALL)
; Preserved: BC, DE, HL
delay_a_frames:
or a ; 1
jr nz,+ ; 3
; -1
ret
delay_frames_: ; delays 4096*A-2 cycles (including CALL)
push af ; 4
ld a,256-13-20-12 ; 2
jr ++ ; 3
+
- push af ; 4
ld a,256-13-20 ; 2
++ call delay_a_20_cycles
delay 4096-256
pop af ; 3
dec a ; 1
jr nz,- ; 3
; -1
ret
.macro test_chan_timing ; chan, iter
ld a,\1
call print_dec
call print_space
ld a,\2
- push af
test_chan 1<<\1, \1*5+NR10
pop af
dec a
jr nz,-
call print_newline
.endm
.macro test_chans ARGS iter
test_chan_timing 0,iter
test_chan_timing 1,iter
test_chan_timing 2,iter
test_chan_timing 3,iter
.endm

View File

@@ -0,0 +1,121 @@
; Build as GBS music file
.memoryMap
defaultSlot 0
slot 0 $3000 size $1000
slot 1 $C000 size $1000
.endMe
.romBankSize $1000
.romBanks 2
;;;; GBS music file header
.byte "GBS"
.byte 1 ; vers
.byte 1 ; songs
.byte 1 ; first song
.word load_addr
.word reset
.word gbs_play
.word std_stack
.byte 0,0 ; timer
.ds $60,0
load_addr:
; WLA assumes we're building ROM and messes
; with bytes at the beginning, so skip them.
.ds $100,0
;;;; Shell
.include "runtime.s"
init_runtime:
ld a,$01 ; Identify as DMG hardware
ld (gb_id),a
.ifdef TEST_NAME
print_str TEST_NAME,newline,newline
.endif
ret
std_print:
sta SB
wreg SC,$81
delay 2304
ret
post_exit:
call play_byte
forever:
wreg NR52,0 ; sound off
- jp -
.ifndef CUSTOM_RESET
gbs_play:
.endif
console_flush:
console_normal:
console_inverse:
console_set_mode:
ret
; Reports A in binary as high and low tones, with
; leading low tone for reference. Omits leading
; zeroes.
; Preserved: AF, BC, DE, HL
play_byte:
push af
push hl
; HL = (A << 1) | 1
scf
rla
ld l,a
ld h,0
rl h
; Shift left until next-to-top bit is 1
- add hl,hl
bit 6,h
jr z,-
; Reset sound
delay_msec 400
wreg NR52,0 ; sound off
wreg NR52,$80 ; sound on
wreg NR51,$FF ; mono
wreg NR50,$77 ; volume
- add hl,hl
; Low or high pitch based on bit shifted out
; of HL
ld a,0
jr nc,+
ld a,$FF
+ sta NR23
; Play short tone
wreg NR21,$A0
wreg NR22,$F0
wreg NR24,$86
delay_msec 75
wreg NR22,0
wreg NR23,$F8
wreg NR24,$87
delay_msec 200
; Loop until HL = $8000
ld a,h
xor $80
or l
jr nz,-
pop hl
pop af
ret
.ends

View File

@@ -0,0 +1,80 @@
; Build as GB ROM
.memoryMap
defaultSlot 0
slot 0 $0000 size $4000
slot 1 $C000 size $4000
.endMe
.romBankSize $4000 ; generates $8000 byte ROM
.romBanks 2
.cartridgeType 1 ; MBC1
.computeChecksum
.computeComplementCheck
;;;; GB ROM header
; GB header read by bootrom
.org $100
nop
jp reset
; Nintendo logo required for proper boot
.byte $CE,$ED,$66,$66,$CC,$0D,$00,$0B
.byte $03,$73,$00,$83,$00,$0C,$00,$0D
.byte $00,$08,$11,$1F,$88,$89,$00,$0E
.byte $DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
.byte $BB,$BB,$67,$63,$6E,$0E,$EC,$CC
.byte $DD,$DC,$99,$9F,$BB,$B9,$33,$3E
; Internal name
.ifdef ROM_NAME
.byte ROM_NAME
.endif
; CGB/DMG requirements
.org $143
.ifdef REQUIRE_CGB
.byte $C0
.else
.ifndef REQUIRE_DMG
.byte $80
.endif
.endif
.org $200
;;;; Shell
.include "runtime.s"
.include "console.s"
init_runtime:
call console_init
.ifdef TEST_NAME
print_str TEST_NAME,newline,newline
.endif
ret
std_print:
push af
sta SB
wreg SC,$81
delay 2304
pop af
jp console_print
post_exit:
call console_show
call play_byte
forever:
wreg NR52,0 ; sound off
- jr -
play_byte:
ret
.ends

View File

@@ -0,0 +1,98 @@
; Multiple checksum table handling
.define next_checksum bss+0
.redefine bss bss+2
; If PRINT_CHECKSUMS is defined, checksums are printed
; rather than compared.
; Initializes multiple checksum handler to use checksums
; table (defined by user).
; Preserved: BC, DE, HL
checksums_init:
ld a,<checksums
ld (next_checksum),a
ld a,>checksums
ld (next_checksum+1),a
ret
; Compares current checksum with next checksum in
; list. Z if they match, NZ if not.
; Preserved: BC, DE, HL
checksums_compare:
.ifdef PRINT_CHECKSUMS
lda checksum+3
push af
lda checksum+2
push af
lda checksum+1
push af
lda checksum+0
push af
ld a,(next_checksum)
inc a
ld (next_checksum),a
sub <checksums+1
and $03
ld a,','
jr nz,+
print_str newline,'.',"byte"
ld a,' '
+ call print_char
pop af
call @print_byte
pop af
call @print_byte
pop af
call @print_byte
ld a,'$'
call print_char
pop af
call print_hex
xor a
ret
@print_byte:
push af
ld a,'$'
call print_char
pop af
call print_hex
ld a,','
call print_char
ret
.else
push bc
push de
push hl
ld a,(next_checksum)
ld l,a
ld a,(next_checksum+1)
ld h,a
ld de,checksum
ld b,0
- ld a,(de)
xor (hl)
or b
ld b,a
inc hl
inc e
ld a,e
cp <(checksum+4)
jr nz,-
ld a,l
ld (next_checksum),a
ld a,h
ld (next_checksum+1),a
ld a,b
cp 0
pop hl
pop de
pop bc
ret
.endif

Binary file not shown.

View File

@@ -0,0 +1,291 @@
; Scrolling text console
; Console is 20x18 characters. Buffers lines, so
; output doesn't appear until a newline or flush.
; If scrolling isn't supported (i.e. SCY is treated
; as if always zero), the first 18 lines will
; still print properly). Also works properly if
; LY isn't supported (always reads back as the same
; value).
.define console_width 20
.define console_buf bss+0
.define console_pos bss+console_width
.define console_mode bss+console_width+1
.define console_scroll bss+console_width+2
.redefine bss bss+console_width+3
; Waits for start of LCD blanking period
; Preserved: BC, DE, HL
console_wait_vbl:
push bc
; Wait for start of vblank, with
; timeout in case LY doesn't work
; or LCD is disabled.
ld bc,-1250
- inc bc
ld a,b
or c
jr z,@timeout
lda LY
cp 144
jr nz,-
@timeout:
pop bc
ret
; Initializes text console
console_init:
call console_hide
; CGB-specific inits
ld a,(gb_id)
and gb_id_cgb
call nz,@init_cgb
; Clear nametable
ld a,' '
call @fill_nametable
; Load tiles
ld hl,TILES+$200
ld c,0
call @load_tiles
ld hl,TILES+$A00
ld c,$FF
call @load_tiles
; Init state
ld a,console_width
ld (console_pos),a
ld a,0
ld (console_mode),a
ld a,-8
ld (console_scroll),a
call console_scroll_up_
jr console_show
@fill_nametable:
ld hl,BGMAP0
ld b,4
- ld (hl),a
inc l
jr nz,-
inc h
dec b
jr nz,-
ret
@init_cgb:
; Clear palette
wreg $FF68,$80
ld b,16
- wreg $FF69,$FF
wreg $FF69,$7F
wreg $FF69,$00
wreg $FF69,$00
wreg $FF69,$00
wreg $FF69,$00
wreg $FF69,$00
wreg $FF69,$00
dec b
jr nz,-
; Clear attributes
ld a,1
ld (VBK),a
ld a,0
call @fill_nametable
ld a,0
ld (VBK),a
ret
@load_tiles:
ld de,ASCII
ld b,96
-- push bc
ld b,8
- ld a,(de)
inc de
xor c
ldi (hl),a
ldi (hl),a
dec b
jr nz,-
pop bc
dec b
jr nz,--
ret
; Shows console display
; Preserved: AF, BC, DE, HL
console_show:
push af
; Enable LCD
call console_wait_vbl
wreg LCDC,$91
wreg SCX,0
wreg BGP,$E4
jp console_apply_scroll_
; Hides console display by turning LCD off
; Preserved: AF, BC, DE, HL
console_hide:
push af
; LCD off
call console_wait_vbl
wreg LCDC,$11
pop af
ret
; Changes to normal text mode
; Preserved: BC, DE, HL
console_normal:
xor a
jr console_set_mode
; Changes to inverse text mode
; Preserved: BC, DE, HL
console_inverse:
ld a,$80
; Changes console mode to A.
; 0: Normal, $80: Inverse
; Preserved: BC, DE, HL
console_set_mode:
and $80
ld (console_mode),a
ret
; Prints char A to console. Will not appear until
; a newline or flush occurs.
; Preserved: AF, BC, DE, HL
console_print:
push af
cp 10
jr z,console_newline_
push hl
push af
ld hl,console_pos
ldi a,(hl)
cp <console_buf
jr nz,@not_at_end
; Newline if at end of current line. If this
; were done after writing to buffer, calling
; console_newline would print extra newline.
; Doing it before eliminates this.
; Ignore any spaces at end of line
pop af
cp ' '
jr z,@ignore_space
call console_newline
push af
@not_at_end:
pop af
or (hl) ; apply current attributes
dec l ; hl = console_pos
dec (hl) ; console_pos = console_pos - 1
ld l,(hl) ; hl = position in buffer
ld (hl),a
@ignore_space
pop hl
pop af
ret
; Displays current line and starts new one
; Preserved: AF, BC, DE, HL
console_newline:
push af
console_newline_:
call console_wait_vbl
call console_flush_
call console_scroll_up_
call console_flush_
jp console_apply_scroll_
console_scroll_up_:
push bc
push hl
; Scroll up 8 pixels
ld a,(console_scroll)
add 8
ld (console_scroll),a
; Start new clear line
ld a,' '
ld hl,console_buf + console_width - 1
ld b,console_width
- ldd (hl),a
dec b
jr nz,-
ld a,<(console_buf + console_width)
ld (console_pos),a
pop hl
pop bc
ret
; Displays current line's contents without scrolling.
; Preserved: A, BC, DE, HL
console_flush:
push af
call console_wait_vbl
call console_flush_
console_apply_scroll_:
ld a,(console_scroll)
sub 136
sta SCY
pop af
ret
console_flush_:
push de
push hl
; Address of row in nametable
ld a,(console_scroll)
ld l,a
ld h,(>BGMAP0) >> 2
add hl,hl
add hl,hl
; Copy line
ld de,console_buf + console_width
- dec e
ld a,(de)
ldi (hl),a
ld a,e
cp <console_buf
jr nz,-
pop hl
pop de
ret
ASCII:
.incbin "console.bin"

View File

@@ -0,0 +1,64 @@
; CPU speed manipulation.
; Switches to normal speed. No effect on DMG.
; Preserved: BC, DE, HL
cpu_norm:
; Do nothing if not CGB
ld a,(gb_id)
and gb_id_cgb
ret z
lda KEY1
rlca
ret nc
jr cpu_speed_toggle
; Switches to double speed. No effect on DMG.
; Preserved: BC, DE, HL
cpu_fast:
; Do nothing if not CGB
ld a,(gb_id)
and gb_id_cgb
ret z
lda KEY1
rlca
ret c
cpu_speed_toggle:
di
lda IE
push af
xor a
sta IE
sta IF
wreg P1,$30
wreg KEY1,1
stop
nop
pop af
sta IE
ret
; Determines current CPU speed without using KEY1.
; A=1 if fast, 0 if normal. Always 0 on DMG.
; Preserved: BC, DE,HL
get_cpu_speed:
push bc
call sync_apu
wreg NR14,$C0
wreg NR11,-1
wreg NR12,8
wreg NR14,$C0
ld bc,-$262
- inc bc
lda NR52
and 1
jr nz,-
ld a,0
bit 7,b
jr nz,+
inc a
+ pop bc
ret

View File

@@ -0,0 +1,78 @@
; CRC-32 checksum calculation
.define checksum dp+0 ; little-endian, complemented
.redefine dp dp+4
; Initializes checksum module. Might initialize tables
; in the future.
init_crc:
jr reset_crc
; Clears CRC
; Preserved: BC, DE, HL
reset_crc:
ld a,$FF
sta checksum+0
sta checksum+1
sta checksum+2
sta checksum+3
ret
; Updates current checksum with byte A
; Preserved: AF, BC, DE, HL
; Time: 237 cycles average
update_crc:
; 65 cycles + 8*cycles per bit
; min cycles per bit: 14
; max cycles per bit: 29
push af
push bc
push de
push hl
ld hl,checksum+3
ld b,(hl)
dec l
ld c,(hl)
dec l
ld d,(hl)
dec l
xor (hl)
ld h,8
- srl b
rr c
rr d
rra
jr nc,+
ld e,a
ld a,b
xor $ED
ld b,a
ld a,c
xor $B8
ld c,a
ld a,d
xor $83
ld d,a
ld a,e
xor $20
+ dec h
jr nz,-
ld h,>checksum
ldi (hl),a
ld (hl),d
inc l
ld (hl),c
inc l
ld (hl),b
pop hl
pop de
pop bc
pop af
ret

View File

@@ -0,0 +1,88 @@
; Fast table-based CRC-32
.define crc_tables (bss+$FF)&$FF00 ; 256-byte aligned
.redefine bss crc_tables+$400
; Initializes fast CRC tables and resets checksum.
; Time: 47 msec
init_crc_fast:
ld l,0
@next:
xor a
ld c,a
ld d,a
ld e,l
ld h,8
- rra
rr c
rr d
rr e
jr nc,+
xor $ED
ld b,a
ld a,c
xor $B8
ld c,a
ld a,d
xor $83
ld d,a
ld a,e
xor $20
ld e,a
ld a,b
+ dec h
jr nz,-
ld h,>crc_tables
ld (hl),e
inc h
ld (hl),d
inc h
ld (hl),c
inc h
ld (hl),a
inc l
jr nz,@next
jp init_crc
; Faster version of update_crc
; Preserved: BC, DE
; Time: 50 cycles (including CALL)
update_crc_fast:
; Fastest inline macro version of update_crc_fast
; Time: 40 cycles
; Size: 28 bytes
.macro update_crc_fast
ld l,a ; 1
lda checksum ; 3
xor l ; 1
ld l,a ; 1
ld h,>crc_tables ; 2
lda checksum+1 ; 3
xor (hl) ; 2
inc h ; 1
sta checksum ; 3
lda checksum+2 ; 3
xor (hl) ; 2
inc h ; 1
sta checksum+1 ; 3
lda checksum+3 ; 3
xor (hl) ; 2
inc h ; 1
sta checksum+2 ; 3
ld a,(hl) ; 2
sta checksum+3 ; 3
.endm
update_crc_fast
ret

View File

@@ -0,0 +1,220 @@
; Delays in cycles, milliseconds, etc.
; All routines are re-entrant (no global data). Routines never
; touch BC, DE, or HL registers. These ASSUME CPU is at normal
; speed. If running at double speed, msec/usec delays are half advertised.
; Delays n cycles, from 0 to 16777215
; Preserved: AF, BC, DE, HL
.macro delay ARGS n
.if n < 0
.printt "Delay must be >= 0"
.fail
.endif
.if n > 16777215
.printt "Delay must be < 16777216"
.fail
.endif
delay_ n&$FFFF, n>>16
.endm
; Delays n clocks, from 0 to 16777216*4. Must be multiple of 4.
; Preserved: AF, BC, DE, HL
.macro delay_clocks ARGS n
.if n # 4 != 0
.printt "Delay must be a multiple of 4"
.fail
.endif
delay_ (n/4)&$FFFF,(n/4)>>16
.endm
; Delays n microseconds (1/1000000 second)
; n can range from 0 to 4000 usec.
; Preserved: AF, BC, DE, HL
.macro delay_usec ARGS n
.if n < 0
.printt "Delay must be >= 0"
.fail
.endif
.if n > 4000
.printt "Delay must be <= 4000 usec"
.fail
.endif
delay_ ((n * 1048576 + 500000) / 1000000)&$FFFF,((n * 1048576 + 500000) / 1000000)>>16
.endm
; Delays n milliseconds (1/1000 second)
; n can range from 0 to 10000 msec.
; Preserved: AF, BC, DE, HL
.macro delay_msec ARGS n
.if n < 0
.printt "Delay must be >= 0"
.fail
.endif
.if n > 10000
.printt "Delay must be <= 10000 msec"
.fail
.endif
delay_ ((n * 1048576 + 500) / 1000)&$FFFF, ((n * 1048576 + 500) / 1000)>>16
.endm
; All the low/high quantities are to deal wla-dx's asinine
; restriction full expressions must evaluate to a 16-bit
; value. If the author ever rectifies this, all "high"
; arguments can be treated as zero and removed. Better yet,
; I'll just find an assembler that didn't crawl out of
; the sewer (this is one of too many bugs I've wasted
; hours working around).
.define max_short_delay 28
.macro delay_long_ ARGS n, high
; 0+ to avoid assembler treating as memory read
ld a,0+(((high<<16)+n) - 11) >> 16
call delay_65536a_9_cycles_
delay_nosave_ (((high<<16)+n) - 11)&$FFFF, 0
.endm
; Doesn't save AF, allowing minimization of AF save/restore
.macro delay_nosave_ ARGS n, high
; 65536+11 = maximum delay using delay_256a_9_cycles_
; 255+22 = maximum delay using delay_a_20_cycles
; 22 = minimum delay using delay_a_20_cycles
.if high > 1
delay_long_ n, high
.else
.if high*n > 11
delay_long_ n, high
.else
.if (high*(255+22+1))|n > 255+22
ld a,>(((high<<16)+n) - 11)
call delay_256a_9_cycles_
delay_nosave_ <(((high<<16)+n) - 11), 0
.else
.if n >= 22
ld a,n - 22
call delay_a_20_cycles
.else
delay_short_ n
.endif
.endif
.endif
.endif
.endm
.macro delay_ ARGS low, high
.if (high*(max_short_delay+1))|low > max_short_delay
push af
delay_nosave_ ((high<<16)+low - 7)&$FFFF, ((high<<16)+low - 7)>>16
pop af
.else
delay_short_ low
.endif
.endm
; Delays A cycles + overhead
; Preserved: BC, DE, HL
; Time: A+20 cycles (including CALL)
delay_a_20_cycles:
- sub 5 ; 2
jr nc,- ;3/2 do multiples of 5
rra ; 1
jr nc,+ ;3/2 bit 0
+ adc 1 ; 2
ret nc ;5/2 -1: 0 cycles
ret z ;5/2 0: 2 cycles
nop ; 1 1: 4 cycles
ret ; 4 (thanks to dclxvi for original algorithm)
; Delays A*256 cycles + overhead
; Preserved: BC, DE, HL
; Time: A*256+12 cycles (including CALL)
delay_256a_12_cycles:
or a ; 1
ret z ; 5/2
delay_256a_9_cycles_:
- delay 256-4
dec a ; 1
jr nz,- ;3/2
ret ; 4
; Delays A*65536 cycles + overhead
; Preserved: BC, DE, HL
; Time: A*65536+12 cycles (including CALL)
delay_65536a_12_cycles:
or a ; 1
ret z ;5/2
delay_65536a_9_cycles_:
- delay 65536-4
dec a ; 1
jr nz,- ;3/2
ret ; 4
; Delays H*256+L cycles + overhead
; Preserved: AF, BC, DE, HL
; Time: H*256+L+51 cycles
delay_hl_51_cycles:
push af
ld a,h
call delay_256a_12_cycles
ld a,l
call delay_a_20_cycles
pop af
ret
; delay_short_ macro calls into these
.ds max_short_delay-10,$00 ; NOP repeated several times
delay_unrolled_:
ret
.macro delay_short_ ARGS n
.if n < 0
.fail
.endif
.if n > max_short_delay
.fail
.endif
.if n == 1
nop
.endif
.if n == 2
nop
nop
.endif
.if n == 3
.byte $18,$00 ; JR +0
.endif
.if n == 4
.byte $18,$00 ; JR +0
nop
.endif
.if n == 5
.byte $18,$00 ; JR +0
nop
nop
.endif
.if n == 6
.byte $18,$00 ; JR +0
.byte $18,$00 ; JR +0
.endif
.if n == 7
push af
pop af
.endif
.if n == 8
push af
pop af
nop
.endif
.if n == 9
push af
pop af
nop
nop
.endif
.if n >= 10
call delay_unrolled_ + 10 - n
.endif
.endm

View File

@@ -0,0 +1,64 @@
; Game Boy hardware addresses
; Memory
.define VRAM $8000 ; video memory
.define TILES $8000 ; tile images
.define BGMAP0 $9800 ; first 32x32 tilemap
.define BGMAP1 $9C00 ; second 32x32 tilemap
.define WRAM $C000 ; internal memory
.define OAM $FE00 ; sprite memory
.define HRAM $FF80 ; fast memory for LDH
.define P1 $FF00
; Game link I/O
.define SB $FF01
.define SC $FF02
; Interrupts
.define DIV $FF04
.define TIMA $FF05
.define TMA $FF06
.define TAC $FF07
.define IF $FF0F
.define IE $FFFF
; LCD registers
.define LCDC $FF40 ; control
.define STAT $FF41 ; status
.define SCY $FF42 ; scroll Y
.define SCX $FF43 ; scroll X
.define LY $FF44 ; current Y being rendered
.define BGP $FF47
.define KEY1 $FF4D ; for changing CPU speed
.define VBK $FF4F
; Sound registers
.define NR10 $FF10
.define NR11 $FF11
.define NR12 $FF12
.define NR13 $FF13
.define NR14 $FF14
.define NR21 $FF16
.define NR22 $FF17
.define NR23 $FF18
.define NR24 $FF19
.define NR30 $FF1A
.define NR31 $FF1B
.define NR32 $FF1C
.define NR33 $FF1D
.define NR34 $FF1E
.define NR41 $FF20
.define NR42 $FF21
.define NR43 $FF22
.define NR44 $FF23
.define NR50 $FF24
.define NR51 $FF25
.define NR52 $FF26
.define WAVE $FF30

View File

@@ -0,0 +1,105 @@
; Framework for CPU instruction tests
; Calls test_instr with each instruction copied
; to instr, with a JP instr_done after it.
; Verifies checksum after testing instruction and
; prints opcode if it's wrong.
.include "checksums.s"
.include "cpu_speed.s"
.include "apu.s"
.include "crc_fast.s"
.define instr $DEF8
.define rp_temp (instr-4)
.define temp bss
; Sets SP to word at addr
; Preserved: BC, DE
.macro ldsp ; addr
ld a,(\1)
ld l,a
ld a,((\1)+1)
ld h,a
ld sp,hl
.endm
main:
call cpu_fast
call init_crc_fast
call checksums_init
set_test 0
ld hl,instrs
- ; Copy instruction
ld a,(hl+)
ld (instr),a
ld a,(hl+)
ld (instr+1),a
ld a,(hl+)
ld (instr+2),a
push hl
; Put JP instr_done after it
ld a,$C3
ld (instr+3),a
ld a,<instr_done
ld (instr+4),a
ld a,>instr_done
ld (instr+5),a
call reset_crc
call test_instr
call checksums_compare
jr z,passed
set_test 1
ld a,(instr)
call print_a
cp $CB
jr nz,+
ld a,(instr+1)
call print_a
+
passed:
; Next instruction
pop hl
ld a,l
cp <instrs_end
jr nz,-
ld a,h
cp >instrs_end
jr nz,-
jp tests_done
; Updates checksum with AF, BC, DE, and HL
checksum_af_bc_de_hl:
push hl
push af
update_crc_fast
pop hl
ld a,l
update_crc_fast
ld a,b
update_crc_fast
ld a,c
update_crc_fast
ld a,d
update_crc_fast
ld a,e
update_crc_fast
pop de
ld a,d
update_crc_fast
ld a,e
update_crc_fast
ret

View File

@@ -0,0 +1,73 @@
; General macros
; Reads A from addr, from $FF00 to $FFFF
; Preserved: F, BC, DE, HL
; Time: 3 cycles
.macro lda ARGS addr
ldh a,(addr - $FF00)
.endm
; Writes A to addr, from $FF00 to $FFFF
; Preserved: AF, BC, DE, HL
; Time: 3 cycles
.macro sta ARGS addr
ldh (addr - $FF00),a
.endm
; Writes immediate data to addr, from $FF00 to $FFFF
; Preserved: F, BC, DE, HL
; Time: 5 cycles
.macro wreg ARGS addr, data
ld a,data
sta addr
.endm
; Calls routine multiple times, with A having the
; value 'start' the first time, 'start+step' the
; second time, up to 'end' for the last time.
; Preserved: BC, DE, HL
.macro for_loop ; routine,start,end,step
ld a,\2
for_loop\@:
push af
call \1
pop af
add \4
cp <(\3 + \4)
jr nz,for_loop\@
.endm
; Calls routine n times. The value of A in the routine
; counts from 0 to n-1.
; Preserved: BC, DE, HL
.macro loop_n_times ; routine,n
for_loop \1,0,\2 - 1,+1
.endm
; Same as for_loop, but counts with 16-bit value in BC.
; Preserved: DE, HL
.macro for_loop16 ; routine,start,end,step
ld bc,\2
for_loop16\@:
push bc
call \1
pop bc
ld a,c
add <\4
ld c,a
ld a,b
adc >\4
ld b,a
cp >(\3+\4)
jr nz,for_loop16\@
ld a,c
cp <(\3+\4)
jr nz,for_loop16\@
.endm

View File

@@ -0,0 +1,38 @@
; RST handlers
.bank 0 slot 0
.org 0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret
.ds 6,0
inc a
ret

View File

@@ -0,0 +1,177 @@
; Printing of numeric values
; Prints value of indicated register/pair
; as 2/4 hex digits, followed by a space.
; Updates checksum with printed values.
; Preserved: AF, BC, DE, HL
print_regs:
call print_af
call print_bc
call print_de
call print_hl
call print_newline
ret
print_a:
push af
print_a_:
call print_hex
ld a,' '
call print_char_nocrc
pop af
ret
print_af:
push af
call print_hex
pop af
print_f:
push bc
push af
pop bc
call print_c
pop bc
ret
print_b:
push af
ld a,b
jr print_a_
print_c:
push af
ld a,c
jr print_a_
print_d:
push af
ld a,d
jr print_a_
print_e:
push af
ld a,e
jr print_a_
print_h:
push af
ld a,h
jr print_a_
print_l:
push af
ld a,l
jr print_a_
print_bc:
push af
push bc
print_bc_:
ld a,b
call print_hex
ld a,c
pop bc
jr print_a_
print_de:
push af
push bc
ld b,d
ld c,e
jr print_bc_
print_hl:
push af
push bc
ld b,h
ld c,l
jr print_bc_
; Prints A as two hex chars and updates checksum
; Preserved: BC, DE, HL
print_hex:
call update_crc
print_hex_nocrc:
push af
swap a
call +
pop af
+ and $0F
cp 10
jr c,+
add 7
+ add '0'
jp print_char_nocrc
; Prints char_nz if Z flag is clear,
; char_z if Z flag is set.
; Preserved: AF, BC, DE, HL
.macro print_nz ARGS char_nz, char_z
push af
ld a,char_nz
jr nz,print_nz\@
ld a,char_z
print_nz\@:
call print_char
pop af
.endm
; Prints char_nc if C flag is clear,
; char_c if C flag is set.
; Preserved: AF, BC, DE, HL
.macro print_nc ARGS char_nc, char_c
push af
ld a,char_nc
jr nz,print_nc\@
ld a,char_c
print_nc\@:
call print_char
pop af
.endm
; Prints A as 2 decimal digits
; Preserved: AF, BC, DE, HL
print_dec2:
push af
push bc
jr +
; Prints A as 1-3 digit decimal value
; Preserved: AF, BC, DE, HL
print_dec:
push af
push bc
cp 10
jr c,++
ld c,100
cp c
call nc,@digit
+ ld c,10
call @digit
++ add '0'
call print_char
pop bc
pop af
ret
@digit:
ld b,'0'-1
- inc b
sub c
jr nc,-
add c
ld c,a
ld a,b
call print_char
ld a,c
ret

View File

@@ -0,0 +1,98 @@
; Main printing routine that checksums and
; prints to output device
; Character that does equivalent of print_newline
.define newline 10
; Prints char without updating checksum
; Preserved: BC, DE, HL
.define print_char_nocrc bss
.redefine bss bss+3
; Initializes printing. HL = print routine
init_printing:
ld a,l
ld (print_char_nocrc+1),a
ld a,h
ld (print_char_nocrc+2),a
jr show_printing
; Hides/shows further printing
; Preserved: BC, DE, HL
hide_printing:
ld a,$C9 ; RET
jr +
show_printing:
ld a,$C3 ; JP (nn)
+ ld (print_char_nocrc),a
ret
; Prints character and updates checksum UNLESS
; it's a newline.
; Preserved: AF, BC, DE, HL
print_char:
push af
cp newline
call nz,update_crc
call print_char_nocrc
pop af
ret
; Prints space. Does NOT update checksum.
; Preserved: AF, BC, DE, HL
print_space:
push af
ld a,' '
call print_char_nocrc
pop af
ret
; Advances to next line. Does NOT update checksum.
; Preserved: AF, BC, DE, HL
print_newline:
push af
ld a,newline
call print_char_nocrc
pop af
ret
; Prints immediate string
; Preserved: AF, BC, DE, HL
.macro print_str ; string,string2
push hl
call print_str_
.byte \1
.if NARGS > 1
.byte \2
.endif
.if NARGS > 2
.byte \3
.endif
.byte 0
pop hl
.endm
print_str_:
pop hl
call print_str_hl
jp hl
; Prints zero-terminated string pointed to by HL.
; On return, HL points to byte AFTER zero terminator.
; Preserved: AF, BC, DE
print_str_hl:
push af
jr +
- call print_char
+ ldi a,(hl)
or a
jr nz,-
pop af
ret

View File

@@ -0,0 +1,142 @@
; Common routines and runtime
; Must be defined by target-specific runtime:
;
; init_runtime: ; target-specific inits
; std_print: ; default routine to print char A
; post_exit: ; called at end of std_exit
; report_byte: ; report A to user
.define RUNTIME_INCLUDED 1
.ifndef bss
; address of next normal variable
.define bss $D800
.endif
.ifndef dp
; address of next direct-page ($FFxx) variable
.define dp $FF80
.endif
; DMG/CGB hardware identifier
.define gb_id_cgb $10 ; mask for testing CGB bit
.define gb_id_devcart $04 ; mask for testing "on devcart" bit
.define gb_id bss
.redefine bss bss+1
; Stack is normally here
.define std_stack $DFFF
; Copies $1000 bytes from HL to $C000, then jumps to it.
; A is preserved for jumped-to code.
copy_to_wram_then_run:
ld b,a
ld de,$C000
ld c,$10
- ldi a,(hl)
ld (de),a
inc e
jr nz,-
inc d
dec c
jr nz,-
ld a,b
jp $C000
.ifndef CUSTOM_RESET
reset:
; Run code from $C000, as is done on devcart. This
; ensures minimal difference in how it behaves.
ld hl,$4000
jp copy_to_wram_then_run
.bank 1 slot 1
.org $0 ; otherwise wla pads with lots of zeroes
jp std_reset
.endif
; Common routines
.include "gb.inc"
.include "macros.inc"
.include "delay.s"
.include "crc.s"
.include "printing.s"
.include "numbers.s"
.include "testing.s"
; Sets up hardware and runs main
std_reset:
; Init hardware
di
ld sp,std_stack
; Save DMG/CGB id
ld (gb_id),a
; Init hardware
.ifndef BUILD_GBS
wreg TAC,$00
wreg IF,$00
wreg IE,$00
.endif
wreg NR52,0 ; sound off
wreg NR52,$80 ; sound on
wreg NR51,$FF ; mono
wreg NR50,$77 ; volume
; TODO: clear all memory?
ld hl,std_print
call init_printing
call init_testing
call init_runtime
call reset_crc ; in case init_runtime prints anything
delay_msec 250
; Run user code
call main
; Default is to successful exit
ld a,0
jp exit
; Exits code and reports value of A
exit:
ld sp,std_stack
push af
call +
pop af
jp post_exit
+ push af
call print_newline
call show_printing
pop af
; Report exit status
cp 1
; 0: ""
ret c
; 1: "Failed"
jr nz,+
print_str "Failed",newline
ret
; n: "Failed #n"
+ print_str "Failed #"
call print_dec
call print_newline
ret
; returnOrg puts this code AFTER user code.
.section "runtime" returnOrg

View File

@@ -0,0 +1,176 @@
; Diagnostic and testing utilities
.define result bss+0
.define test_name bss+1
.redefine bss bss+3
; Sets test code and optional error text
; Preserved: AF, BC, DE, HL
.macro set_test ; code[,text[,text2]]
push hl
call set_test_
jr @set_test\@
.byte \1
.if NARGS > 1
.byte \2
.endif
.if NARGS > 2
.byte \3
.endif
.byte 0
@set_test\@:
pop hl
.endm
set_test_:
pop hl
push hl
push af
inc hl
inc hl
ldi a,(hl)
ld (result),a
ld a,l
ld (test_name),a
ld a,h
ld (test_name+1),a
pop af
ret
; Initializes testing module
init_testing:
set_test $FF
call init_crc
ret
; Reports "Passed", then exits with code 0
tests_passed:
call print_newline
print_str "Passed"
ld a,0
jp exit
; Reports "Done" if set_test has never been used,
; "Passed" if set_test 0 was last used, or
; failure if set_test n was last used.
tests_done:
ld a,(result)
inc a
jr z,+
dec a
jr z,tests_passed
jr test_failed
+ print_str "Done"
ld a,0
jp exit
; Reports current error text and exits with result code
test_failed:
ld a,(test_name)
ld l,a
ld a,(test_name+1)
ld h,a
ld a,(hl)
or a
jr z,+
call print_newline
call print_str_hl
call print_newline
+
ld a,(result)
cp 1 ; if a = 0 then a = 1
adc 0
jp exit
; Prints checksum as 8-character hex value
; Preserved: AF, BC, DE, HL
print_crc:
push af
; Must read checksum entirely before printing,
; since printing updates it.
lda checksum
cpl
push af
lda checksum+1
cpl
push af
lda checksum+2
cpl
push af
lda checksum+3
cpl
call print_hex
pop af
call print_hex
pop af
call print_hex
pop af
call print_a
pop af
ret
; If checksum doesn't match expected, reports failed test.
; Passing 0 just prints checksum. Clears checksum afterwards.
.macro check_crc ARGS crc
.if crc == 0
call show_printing
call print_newline
call print_crc
.else
ld bc,(crc >> 16) ~ $FFFF
ld de,(crc & $FFFF) ~ $FFFF
call check_crc_
.endif
.endm
check_crc_:
lda checksum+0
cp e
jr nz,+
lda checksum+1
cp d
jr nz,+
lda checksum+2
cp c
jr nz,+
lda checksum+3
cp b
jr nz,+
jp reset_crc
+ call print_crc
jp test_failed
; Updates checksum with bytes from addr to addr+size-1
.macro checksum_mem ARGS addr,size
ld hl,addr
ld bc,size
call checksum_mem_
.endm
checksum_mem_:
- ldi a,(hl)
call update_crc
dec bc
ld a,b
or c
jr nz,-
ret

View File

@@ -0,0 +1,2 @@
[objects]
test.o

View File

@@ -0,0 +1,21 @@
.incdir "common"
; GBS music file
.ifdef BUILD_GBS
.include "build_gbs.s"
.endif
; Devcart
.ifdef BUILD_DEVCART
.include "build_devcart.s"
.endif
; Sub-test in a multi-test ROM
.ifdef BUILD_MULTI
.include "build_multi.s"
.endif
; GB ROM (default)
.ifndef RUNTIME_INCLUDED
.include "build_rom.s"
.endif

BIN
gb240p.gb Normal file

Binary file not shown.

BIN
libpng16.dll Normal file

Binary file not shown.

BIN
rgbasm.exe Normal file

Binary file not shown.

BIN
rgbfix.exe Normal file

Binary file not shown.

BIN
rgbgfx.exe Normal file

Binary file not shown.

BIN
rgblink.exe Normal file

Binary file not shown.

1
sm83 Submodule

Submodule sm83 added at f9c3021024

View File

@@ -1,308 +1,609 @@
use crate::registers::Registers;
struct CPU { #[derive(Clone, Copy, Debug)]
registers: Registers, pub enum TargetRegister { A, B, C, D, E, H, L, }
pc: u16, #[derive(Clone, Copy, Debug)]
bus: MemoryBus, pub enum TargetU16Register {AF, BC, DE, HL, SP, PC}
sp: u16,
}
struct MemoryBus { #[derive(Clone, Copy, Debug, PartialEq)]
memory: [u8; 0xFFFF] pub enum Condition {
NZ, // Not Zero
Z, // Zero
NC, // Not Carry
C, // Carry
None,
} }
impl MemoryBus { #[derive(Clone, Copy, Debug)]
fn read_byte(&self, address: u16) -> u8 { pub enum Target {
self.memory[address as usize]
}
}
enum Target {
U8Register(TargetRegister), U8Register(TargetRegister),
U16Register(TargetU16Register), U16Register(TargetU16Register),
Address(u16), Immediate(u8)
} }
#[derive(Clone, Copy, Debug)]
enum Instruction { pub enum LoadTarget{
CopyR8R8(TargetRegister, TargetRegister),
CopyR8N8(TargetRegister, u8),
CopyR16N16(TargetU16Register, u16),
CopyHLR8(TargetRegister),
CopyHLN8(u8),
CopyR8HL(TargetRegister),
CopyR16A(TargetU16Register),
CopyN16A(u16),
CopyAR16(TargetU16Register),
CopyAN16(u16),
CopyHLIA,
CopyHLDA,
CopyAHLD,
CopyAHLI,
CopySPN16(u16),
CopyN16SP(u16),
CopyHLSPE8(i8),
CopySPHL,
CopyPortA(Target),
CopyAPort(Target)
}
#[derive(Debug)]
pub enum Instruction {
ADC(Target), ADC(Target),
ADD(Target), ADD(Target),
ADDHL(TargetU16Register), ADDHL(TargetU16Register),
ADDSP(i8),
AND(Target), AND(Target),
BIT(u8, Target),
SUB(TargetRegister), CALL(Condition, u16),
SBC(TargetRegister),
OR(TargetRegister),
XOR(TargetRegister),
CP(TargetRegister),
INC(TargetRegister),
DEC(TargetRegister),
CCF, CCF,
SCF, CP(Target),
RRA,
RLA,
RRCA,
CPL, CPL,
BIT(u8, TargetRegister), DAA,
DEC(Target),
DECU16(TargetU16Register),
DI,
EI,
HALT,
INC(Target),
INCU16(TargetU16Register),
JP(Condition, u16),
JPHL,
JR(Condition, i8),
LD(LoadTarget),
NOP,
OR(Target),
POP(TargetU16Register),
PUSH(TargetU16Register),
RES(u8, Target),
RET(Condition),
RETI,
RL(Target),
RLC(Target),
RR(Target),
RRC(Target),
RST(u8),
SBC(Target),
SCF,
SET(u8, Target),
SLA(Target),
SRA(Target),
SRL(Target),
STOP(u8),
SUB(Target),
SWAP(Target),
XOR(Target),
} }
enum TargetRegister { A, B, C, D, E, H, L, }
enum TargetU16Register {AF, BC, DE, HL, SP, PC}
impl CPU {
fn get_u8_reg_value(&mut self, target: TargetRegister) -> u8 {
match target {
TargetRegister::A => { self.registers.a }
TargetRegister::B => { self.registers.b }
TargetRegister::C => { self.registers.c }
TargetRegister::D => { self.registers.d }
TargetRegister::E => { self.registers.e }
TargetRegister::H => { self.registers.h }
TargetRegister::L => { self.registers.l }
}
}
fn get_u16_reg_value(&mut self, target: TargetU16Register) -> u16 {
match target {
TargetU16Register::AF => {self.registers.get_af()}
TargetU16Register::BC => {self.registers.get_bc()}
TargetU16Register::DE => {self.registers.get_de()}
TargetU16Register::HL => {self.registers.get_hl()}
TargetU16Register::SP => {self.sp}
TargetU16Register::PC => {self.pc}
}
}
fn get_target_value(&mut self, target: Target) -> u8 {
match target {
Target::U8Register(target_register) => { self.get_u8_reg_value(target_register) },
Target::U16Register(target_register) => { self.bus.read_byte(self.get_u16_reg_value(target_register)) },
Target::Address(address) => { self.bus.read_byte(address) },
}
}
///
fn execute(&mut self, instruction: Instruction) {
match instruction {
Instruction::ADC(target) => {
let value = self.get_target_value(target);
let (mut new_value, did_overflow) = self.registers.a.overflowing_add(value);
new_value += did_overflow as u8;
self.registers.a = new_value;
self.registers.f.zero = new_value == 0;
self.registers.f.subtract = false;
self.registers.f.carry = did_overflow;
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
}
Instruction::ADD(target) => {
let value = self.get_target_value(target);
let (new_value, did_overflow) = self.registers.a.overflowing_add(value);
self.registers.a = new_value;
self.registers.f.zero = new_value == 0;
self.registers.f.subtract = false;
self.registers.f.carry = did_overflow;
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
}
Instruction::ADDHL(target) => {
let value = self.get_u16_reg_value(target);
let (new_value, did_overflow) = self.registers.get_hl().overflowing_add(value);
self.registers.set_hl(new_value);
self.registers.f.zero = new_value == 0;
self.registers.f.subtract = false;
self.registers.f.carry = did_overflow;
self.registers.f.half_carry = (new_value & 0xFF) + (value & 0xFF) > 0xFF;
}
Instruction::AND(target) => {
let value = self.get_target_value(target);
self.registers.a = value & self.registers.a;
self.registers.f.zero = self.registers.a == 0;
self.registers.f.subtract = false;
self.registers.f.carry = false;
self.registers.f.half_carry = true;
}
fn get_u16(low: u8, high: u8) -> u16 {
Instruction::SUB(target) => { ((high as u16) << 8) | low as u16
let value = self.get_u8_reg_value(target); }
let (new_value, did_overflow) = self.registers.a.overflowing_sub(value);
self.registers.a = new_value;
self.registers.f.zero = new_value == 0;
self.registers.f.subtract = true;
self.registers.f.carry = did_overflow;
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
}
Instruction::SBC(target) => {
let value = self.get_u8_reg_value(target);
let (mut new_value, did_overflow) = self.registers.a.overflowing_sub(value);
new_value -= did_overflow as u8;
self.registers.a = new_value;
self.registers.f.zero = new_value == 0;
self.registers.f.subtract = true;
self.registers.f.carry = did_overflow;
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
}
Instruction::OR(target) => { pub fn parse_instruction(opcode: u8, arg1: u8, arg2: u8) -> Instruction {
let value = self.get_u8_reg_value(target); match opcode {
self.registers.a = value | self.registers.a; 0x00 => { Instruction::NOP }
self.registers.f.zero = self.registers.a == 0; 0x01 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::BC, get_u16(arg1, arg2))) }
self.registers.f.subtract = false; 0x02 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::BC)) }
self.registers.f.carry = false; 0x03 => { Instruction::INCU16(TargetU16Register::BC) }
self.registers.f.half_carry = false; 0x04 => { Instruction::INC(Target::U8Register(TargetRegister::B)) }
} 0x05 => { Instruction::DEC(Target::U8Register(TargetRegister::B)) }
Instruction::XOR(target) => { 0x06 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::B, arg1)) }
let value = self.get_u8_reg_value(target); 0x07 => { Instruction::RLC(Target::U8Register(TargetRegister::A)) }
self.registers.a = value ^ self.registers.a; 0x08 => { Instruction::LD(LoadTarget::CopyN16SP(get_u16(arg1, arg2))) }
self.registers.f.zero = self.registers.a == 0; 0x09 => { Instruction::ADDHL(TargetU16Register::BC) }
self.registers.f.subtract = false; 0x0A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::BC)) }
self.registers.f.carry = false; 0x0B => { Instruction::DECU16(TargetU16Register::BC) }
self.registers.f.half_carry = false; 0x0C => { Instruction::INC(Target::U8Register(TargetRegister::C)) }
} 0x0D => { Instruction::DEC(Target::U8Register(TargetRegister::C)) }
Instruction::CP(target) => { 0x0E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::C, arg1)) }
let value = self.get_u8_reg_value(target); 0x0F => { Instruction::RRC(Target::U8Register(TargetRegister::A)) }
let (new_value, did_overflow) = self.registers.a.overflowing_sub(value); 0x10 => { Instruction::STOP(arg1) }
self.registers.f.zero = new_value == 0; 0x11 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::DE, get_u16(arg1, arg2))) }
self.registers.f.subtract = true; 0x12 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::DE)) }
self.registers.f.carry = did_overflow; 0x13 => { Instruction::INCU16(TargetU16Register::DE) }
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; 0x14 => { Instruction::INC(Target::U8Register(TargetRegister::D)) }
} 0x15 => { Instruction::DEC(Target::U8Register(TargetRegister::D)) }
Instruction::INC(target) => { 0x16 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::D, arg1)) }
let (value, new_value) = match target { 0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) }
TargetRegister::A => { 0x18 => { Instruction::JR(Condition::None, arg1 as i8) }
let value = self.registers.a; 0x19 => { Instruction::ADDHL(TargetU16Register::DE) }
let (new_value, _) = value.overflowing_add(1); 0x1A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::DE)) }
self.registers.a = new_value; 0x1B => { Instruction::DECU16(TargetU16Register::DE) }
(value, new_value) 0x1C => { Instruction::INC(Target::U8Register(TargetRegister::E)) }
}, 0x1D => { Instruction::DEC(Target::U8Register(TargetRegister::E)) }
TargetRegister::B => { 0x1E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::E, arg1)) }
let value = self.registers.b; 0x1F => { Instruction::RR(Target::U8Register(TargetRegister::A)) }
let (new_value, _) = value.overflowing_add(1); 0x20 => { Instruction::JR(Condition::NZ, arg1 as i8) }
self.registers.b = new_value; 0x21 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::HL, get_u16(arg1, arg2))) }
(value, new_value) 0x22 => { Instruction::LD(LoadTarget::CopyHLIA) }
}, 0x23 => { Instruction::INCU16(TargetU16Register::HL) }
TargetRegister::C => { 0x24 => { Instruction::INC(Target::U8Register(TargetRegister::H)) }
let value = self.registers.c; 0x25 => { Instruction::DEC(Target::U8Register(TargetRegister::H)) }
let (new_value, _) = value.overflowing_add(1); 0x26 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::H, arg1)) }
self.registers.c = new_value; 0x27 => { Instruction::DAA }
(value, new_value) 0x28 => { Instruction::JR(Condition::Z, arg1 as i8) }
}, 0x29 => { Instruction::ADDHL(TargetU16Register::HL) }
TargetRegister::D => { 0x2A => { Instruction::LD(LoadTarget::CopyAHLI) }
let value = self.registers.d; 0x2B => { Instruction::DECU16(TargetU16Register::HL) }
let (new_value, _) = value.overflowing_add(1); 0x2C => { Instruction::INC(Target::U8Register(TargetRegister::L)) }
self.registers.d = new_value; 0x2D => { Instruction::DEC(Target::U8Register(TargetRegister::L)) }
(value, new_value) 0x2E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::L, arg1)) }
}, 0x2F => { Instruction::CPL }
TargetRegister::E => { 0x30 => { Instruction::JR(Condition::NC, arg1 as i8) }
let value = self.registers.e; 0x31 => { Instruction::LD(LoadTarget::CopySPN16(get_u16(arg1, arg2))) }
let (new_value, _) = value.overflowing_add(1); 0x32 => { Instruction::LD(LoadTarget::CopyHLDA) }
self.registers.e = new_value; 0x33 => { Instruction::INCU16(TargetU16Register::SP) }
(value, new_value) 0x34 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) }
}, 0x35 => { Instruction::DEC(Target::U16Register(TargetU16Register::HL)) }
TargetRegister::H => { 0x36 => { Instruction::LD(LoadTarget::CopyHLN8(arg1)) }
let value = self.registers.h; 0x37 => { Instruction::SCF }
let (new_value, _) = value.overflowing_add(1); 0x38 => { Instruction::JR(Condition::C, arg1 as i8) }
self.registers.h = new_value; 0x39 => { Instruction::ADDHL(TargetU16Register::SP) }
(value, new_value) 0x3A => { Instruction::LD(LoadTarget::CopyAHLD)}
}, 0x3B => { Instruction::DECU16(TargetU16Register::SP) }
TargetRegister::L => { 0x3C => { Instruction::INC(Target::U8Register(TargetRegister::A)) }
let value = self.registers.l; 0x3D => { Instruction::DEC(Target::U8Register(TargetRegister::A)) }
let (new_value, _) = value.overflowing_add(1); 0x3E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::A, arg1)) }
self.registers.l = new_value; 0x3F => { Instruction::CCF }
(value, new_value) 0x40 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::B)) }
} 0x41 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::C)) }
}; 0x42 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::D)) }
self.registers.f.zero = new_value == 0; 0x43 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::E)) }
self.registers.f.subtract = false; 0x44 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::H)) }
self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; 0x45 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::L)) }
} 0x46 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::B)) }
Instruction::DEC(target) => { 0x47 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::A)) }
let (value, new_value) = match target { 0x48 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::B)) }
TargetRegister::A => { 0x49 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::C)) }
let value = self.registers.a; 0x4A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::D)) }
let (new_value, _) = value.overflowing_sub(1); 0x4B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::E)) }
self.registers.a = new_value; 0x4C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::H)) }
(value, new_value) 0x4D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::L)) }
}, 0x4E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::C)) }
TargetRegister::B => { 0x4F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::A)) }
let value = self.registers.b; 0x50 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::B)) }
let (new_value, _) = value.overflowing_sub(1); 0x51 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::C)) }
self.registers.b = new_value; 0x52 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::D)) }
(value, new_value) 0x53 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::E)) }
}, 0x54 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::H)) }
TargetRegister::C => { 0x55 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::L)) }
let value = self.registers.c; 0x56 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::D)) }
let (new_value, _) = value.overflowing_sub(1); 0x57 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::A)) }
self.registers.c = new_value; 0x58 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::B)) }
(value, new_value) 0x59 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::C)) }
}, 0x5A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::D)) }
TargetRegister::D => { 0x5B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::E)) }
let value = self.registers.d; 0x5C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::H)) }
let (new_value, _) = value.overflowing_sub(1); 0x5D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::L)) }
self.registers.d = new_value; 0x5E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::E)) }
(value, new_value) 0x5F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::A)) }
}, 0x60 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::B)) }
TargetRegister::E => { 0x61 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::C)) }
let value = self.registers.e; 0x62 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::D)) }
let (new_value, _) = value.overflowing_sub(1); 0x63 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::E)) }
self.registers.e = new_value; 0x64 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::H)) }
(value, new_value) 0x65 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::L)) }
}, 0x66 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::H)) }
TargetRegister::H => { 0x67 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::A)) }
let value = self.registers.h; 0x68 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::B)) }
let (new_value, _) = value.overflowing_sub(1); 0x69 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::C)) }
self.registers.h = new_value; 0x6A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::D)) }
(value, new_value) 0x6B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::E)) }
}, 0x6C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::H)) }
TargetRegister::L => { 0x6D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::L)) }
let value = self.registers.l; 0x6E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::L)) }
let (new_value, _) = value.overflowing_sub(1); 0x6F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::A)) }
self.registers.l = new_value; 0x70 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::B)) }
(value, new_value) 0x71 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::C)) }
} 0x72 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::D)) }
}; 0x73 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::E)) }
self.registers.f.zero = new_value == 0; 0x74 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::H)) }
self.registers.f.subtract = true; 0x75 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::L)) }
self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; 0x76 => { Instruction::HALT }
} 0x77 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::A)) }
Instruction::CCF => { 0x78 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::B)) }
self.registers.f.carry = !self.registers.f.carry; 0x79 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::C)) }
} 0x7A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::D)) }
Instruction::SCF => { 0x7B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::E)) }
self.registers.f.carry = true; 0x7C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::H)) }
} 0x7D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::L)) }
Instruction::RRA => { 0x7E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::A)) }
let old_carry = self.registers.f.carry; 0x7F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::A)) }
self.registers.f.carry = self.registers.a & 0x1 == 0x1; 0x80 => { Instruction::ADD(Target::U8Register(TargetRegister::B)) }
self.registers.a = (self.registers.a >> 1) | (if old_carry { 0x80 } else { 0 }); 0x81 => { Instruction::ADD(Target::U8Register(TargetRegister::C)) }
self.registers.f.zero = false; 0x82 => { Instruction::ADD(Target::U8Register(TargetRegister::D)) }
self.registers.f.subtract = false; 0x83 => { Instruction::ADD(Target::U8Register(TargetRegister::E)) }
self.registers.f.half_carry = false; 0x84 => { Instruction::ADD(Target::U8Register(TargetRegister::H)) }
} 0x85 => { Instruction::ADD(Target::U8Register(TargetRegister::L)) }
Instruction::RLA => { 0x86 => { Instruction::ADD(Target::U16Register(TargetU16Register::HL)) }
let old_carry = self.registers.f.carry; 0x87 => { Instruction::ADD(Target::U8Register(TargetRegister::A)) }
self.registers.f.carry = self.registers.a & 0x80 == 0x80; 0x88 => { Instruction::ADC(Target::U8Register(TargetRegister::B)) }
self.registers.a = (self.registers.a << 1) | (if old_carry { 0x01 } else { 0 }); 0x89 => { Instruction::ADC(Target::U8Register(TargetRegister::C)) }
self.registers.f.zero = false; 0x8A => { Instruction::ADC(Target::U8Register(TargetRegister::D)) }
self.registers.f.subtract = false; 0x8B => { Instruction::ADC(Target::U8Register(TargetRegister::E)) }
self.registers.f.half_carry = false; 0x8C => { Instruction::ADC(Target::U8Register(TargetRegister::H)) }
} 0x8D => { Instruction::ADC(Target::U8Register(TargetRegister::L)) }
Instruction::RRCA => { 0x8E => { Instruction::ADC(Target::U16Register(TargetU16Register::HL)) }
self.registers.f.carry = self.registers.a & 0x1 == 0x1; 0x8F => { Instruction::ADC(Target::U8Register(TargetRegister::A)) }
self.registers.a = self.registers.a >> 1; 0x90 => { Instruction::SUB(Target::U8Register(TargetRegister::B)) }
self.registers.f.zero = false; 0x91 => { Instruction::SUB(Target::U8Register(TargetRegister::C)) }
self.registers.f.subtract = false; 0x92 => { Instruction::SUB(Target::U8Register(TargetRegister::D)) }
self.registers.f.half_carry = false; 0x93 => { Instruction::SUB(Target::U8Register(TargetRegister::E)) }
} 0x94 => { Instruction::SUB(Target::U8Register(TargetRegister::H)) }
Instruction::CPL => { 0x95 => { Instruction::SUB(Target::U8Register(TargetRegister::L)) }
self.registers.a = !self.registers.a; 0x96 => { Instruction::SUB(Target::U16Register(TargetU16Register::HL)) }
self.registers.f.subtract = true; 0x97 => { Instruction::SUB(Target::U8Register(TargetRegister::A)) }
self.registers.f.half_carry = true; 0x98 => { Instruction::SBC(Target::U8Register(TargetRegister::B)) }
} 0x99 => { Instruction::SBC(Target::U8Register(TargetRegister::C)) }
Instruction::BIT(bit, target) => { 0x9A => { Instruction::SBC(Target::U8Register(TargetRegister::D)) }
let value = self.get_u8_reg_value(target); 0x9B => { Instruction::SBC(Target::U8Register(TargetRegister::E)) }
self.registers.f.zero = value >> bit & 0x1 == 0; 0x9C => { Instruction::SBC(Target::U8Register(TargetRegister::H)) }
self.registers.f.subtract = false; 0x9D => { Instruction::SBC(Target::U8Register(TargetRegister::L)) }
self.registers.f.half_carry = true; 0x9E => { Instruction::SBC(Target::U16Register(TargetU16Register::HL)) }
} 0x9F => { Instruction::SBC(Target::U8Register(TargetRegister::A)) }
0xA0 => { Instruction::AND(Target::U8Register(TargetRegister::B)) }
} 0xA1 => { Instruction::AND(Target::U8Register(TargetRegister::C)) }
0xA2 => { Instruction::AND(Target::U8Register(TargetRegister::D)) }
0xA3 => { Instruction::AND(Target::U8Register(TargetRegister::E)) }
0xA4 => { Instruction::AND(Target::U8Register(TargetRegister::H)) }
0xA5 => { Instruction::AND(Target::U8Register(TargetRegister::L)) }
0xA6 => { Instruction::AND(Target::U16Register(TargetU16Register::HL)) }
0xA7 => { Instruction::AND(Target::U8Register(TargetRegister::A)) }
0xA8 => { Instruction::XOR(Target::U8Register(TargetRegister::B)) }
0xA9 => { Instruction::XOR(Target::U8Register(TargetRegister::C)) }
0xAA => { Instruction::XOR(Target::U8Register(TargetRegister::D)) }
0xAB => { Instruction::XOR(Target::U8Register(TargetRegister::E)) }
0xAC => { Instruction::XOR(Target::U8Register(TargetRegister::H)) }
0xAD => { Instruction::XOR(Target::U8Register(TargetRegister::L)) }
0xAE => { Instruction::XOR(Target::U16Register(TargetU16Register::HL)) }
0xAF => { Instruction::XOR(Target::U8Register(TargetRegister::A)) }
0xB0 => { Instruction::OR(Target::U8Register(TargetRegister::B)) }
0xB1 => { Instruction::OR(Target::U8Register(TargetRegister::C)) }
0xB2 => { Instruction::OR(Target::U8Register(TargetRegister::D)) }
0xB3 => { Instruction::OR(Target::U8Register(TargetRegister::E)) }
0xB4 => { Instruction::OR(Target::U8Register(TargetRegister::H)) }
0xB5 => { Instruction::OR(Target::U8Register(TargetRegister::L)) }
0xB6 => { Instruction::OR(Target::U16Register(TargetU16Register::HL)) }
0xB7 => { Instruction::OR(Target::U8Register(TargetRegister::A)) }
0xB8 => { Instruction::CP(Target::U8Register(TargetRegister::B)) }
0xB9 => { Instruction::CP(Target::U8Register(TargetRegister::C)) }
0xBA => { Instruction::CP(Target::U8Register(TargetRegister::D)) }
0xBB => { Instruction::CP(Target::U8Register(TargetRegister::E)) }
0xBC => { Instruction::CP(Target::U8Register(TargetRegister::H)) }
0xBD => { Instruction::CP(Target::U8Register(TargetRegister::L)) }
0xBE => { Instruction::CP(Target::U16Register(TargetU16Register::HL)) }
0xBF => { Instruction::CP(Target::U8Register(TargetRegister::A)) }
0xC0 => { Instruction::RET(Condition::NZ) }
0xC1 => { Instruction::POP(TargetU16Register::BC) }
0xC2 => { Instruction::JP(Condition::NZ, get_u16(arg1, arg2)) }
0xC3 => { Instruction::JP(Condition::None, get_u16(arg1, arg2)) }
0xC4 => { Instruction::CALL(Condition::NZ, get_u16(arg1, arg2)) }
0xC5 => { Instruction::PUSH(TargetU16Register::BC) }
0xC6 => { Instruction::ADD(Target::Immediate(arg1)) }
0xC7 => { Instruction::RST(0x00) }
0xC8 => { Instruction::RET(Condition::Z) }
0xC9 => { Instruction::RET(Condition::None) }
0xCA => { Instruction::JP(Condition::Z, get_u16(arg1, arg2)) }
0xCB => {match_cb_instruction(arg1)},
0xCC => { Instruction::CALL(Condition::Z, get_u16(arg1, arg2)) }
0xCD => { Instruction::CALL(Condition::None, get_u16(arg1, arg2)) }
0xCE => { Instruction::ADC(Target::Immediate(arg1)) }
0xCF => { Instruction::RST(0x01) }
0xD0 => { Instruction::RET(Condition::NC) }
0xD1 => { Instruction::POP(TargetU16Register::DE) }
0xD2 => { Instruction::JP(Condition::NC, get_u16(arg1, arg2)) }
0xD4 => { Instruction::CALL(Condition::NC, get_u16(arg1, arg2)) }
0xD5 => { Instruction::PUSH(TargetU16Register::DE) }
0xD6 => { Instruction::SUB(Target::Immediate(arg1)) }
0xD7 => { Instruction::RST(0x02) }
0xD8 => { Instruction::RET(Condition::C) }
0xD9 => { Instruction::RETI }
0xDA => { Instruction::JP(Condition::C, get_u16(arg1, arg2)) }
0xDC => { Instruction::CALL(Condition::C, get_u16(arg1, arg2)) }
0xDE => { Instruction::SBC(Target::Immediate(arg1)) }
0xDF => { Instruction::RST(0x03) }
0xE0 => { Instruction::LD(LoadTarget::CopyPortA(Target::Immediate(arg1))) }
0xE1 => { Instruction::POP(TargetU16Register::HL) }
0xE2 => { Instruction::LD(LoadTarget::CopyPortA(Target::U8Register(TargetRegister::C))) }
0xE5 => { Instruction::PUSH(TargetU16Register::HL) }
0xE6 => { Instruction::AND(Target::Immediate(arg1)) }
0xE7 => { Instruction::RST(0x04) }
0xE8 => { Instruction::ADDSP(arg1 as i8) }
0xE9 => { Instruction::JPHL }
0xEA => { Instruction::LD(LoadTarget::CopyN16A(get_u16(arg1, arg2))) }
0xEE => { Instruction::XOR(Target::Immediate(arg1)) }
0xEF => { Instruction::RST(0x05) }
0xF0 => { Instruction::LD(LoadTarget::CopyAPort(Target::Immediate(arg1))) }
0xF1 => { Instruction::POP(TargetU16Register::AF) }
0xF2 => { Instruction::LD(LoadTarget::CopyAPort(Target::U8Register(TargetRegister::C))) }
0xF3 => { Instruction::DI }
0xF5 => { Instruction::PUSH(TargetU16Register::AF) }
0xF6 => { Instruction::OR(Target::Immediate(arg1)) }
0xF7 => { Instruction::RST(0x06) }
0xF8 => { Instruction::LD(LoadTarget::CopyHLSPE8(arg1 as i8)) }
0xF9 => { Instruction::LD(LoadTarget::CopySPHL) }
0xFA => { Instruction::LD(LoadTarget::CopyAN16(get_u16(arg1, arg2))) }
0xFB => { Instruction::EI }
0xFE => { Instruction::CP(Target::Immediate(arg1)) }
0xFF => { Instruction::RST(0x07) }
_ => { panic!("Invalid u8 opcode: {:02X}", opcode); }
}
}
fn match_cb_instruction(opcode: u8) -> Instruction {
match opcode {
0x00 => { Instruction::RLC(Target::U8Register(TargetRegister::B)) }
0x01 => { Instruction::RLC(Target::U8Register(TargetRegister::C)) }
0x02 => { Instruction::RLC(Target::U8Register(TargetRegister::D)) }
0x03 => { Instruction::RLC(Target::U8Register(TargetRegister::E)) }
0x04 => { Instruction::RLC(Target::U8Register(TargetRegister::H)) }
0x05 => { Instruction::RLC(Target::U8Register(TargetRegister::L)) }
0x06 => { Instruction::RLC(Target::U16Register(TargetU16Register::HL)) }
0x07 => { Instruction::RLC(Target::U8Register(TargetRegister::A)) }
0x08 => { Instruction::RRC(Target::U8Register(TargetRegister::B)) }
0x09 => { Instruction::RRC(Target::U8Register(TargetRegister::C)) }
0x0A => { Instruction::RRC(Target::U8Register(TargetRegister::D)) }
0x0B => { Instruction::RRC(Target::U8Register(TargetRegister::E)) }
0x0C => { Instruction::RRC(Target::U8Register(TargetRegister::H)) }
0x0D => { Instruction::RRC(Target::U8Register(TargetRegister::L)) }
0x0E => { Instruction::RRC(Target::U16Register(TargetU16Register::HL)) }
0x0F => { Instruction::RRC(Target::U8Register(TargetRegister::A)) }
0x10 => { Instruction::RL(Target::U8Register(TargetRegister::B)) }
0x11 => { Instruction::RL(Target::U8Register(TargetRegister::C)) }
0x12 => { Instruction::RL(Target::U8Register(TargetRegister::D)) }
0x13 => { Instruction::RL(Target::U8Register(TargetRegister::E)) }
0x14 => { Instruction::RL(Target::U8Register(TargetRegister::H)) }
0x15 => { Instruction::RL(Target::U8Register(TargetRegister::L)) }
0x16 => { Instruction::RL(Target::U16Register(TargetU16Register::HL)) }
0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) }
0x18 => { Instruction::RR(Target::U8Register(TargetRegister::B)) }
0x19 => { Instruction::RR(Target::U8Register(TargetRegister::C)) }
0x1A => { Instruction::RR(Target::U8Register(TargetRegister::D)) }
0x1B => { Instruction::RR(Target::U8Register(TargetRegister::E)) }
0x1C => { Instruction::RR(Target::U8Register(TargetRegister::H)) }
0x1D => { Instruction::RR(Target::U8Register(TargetRegister::L)) }
0x1E => { Instruction::RR(Target::U16Register(TargetU16Register::HL)) }
0x1F => { Instruction::RR(Target::U8Register(TargetRegister::A)) }
0x20 => { Instruction::SLA(Target::U8Register(TargetRegister::B)) }
0x21 => { Instruction::SLA(Target::U8Register(TargetRegister::C)) }
0x22 => { Instruction::SLA(Target::U8Register(TargetRegister::D)) }
0x23 => { Instruction::SLA(Target::U8Register(TargetRegister::E)) }
0x24 => { Instruction::SLA(Target::U8Register(TargetRegister::H)) }
0x25 => { Instruction::SLA(Target::U8Register(TargetRegister::L)) }
0x26 => { Instruction::SLA(Target::U16Register(TargetU16Register::HL)) }
0x27 => { Instruction::SLA(Target::U8Register(TargetRegister::A)) }
0x28 => { Instruction::SRA(Target::U8Register(TargetRegister::B)) }
0x29 => { Instruction::SRA(Target::U8Register(TargetRegister::C)) }
0x2A => { Instruction::SRA(Target::U8Register(TargetRegister::D)) }
0x2B => { Instruction::SRA(Target::U8Register(TargetRegister::E)) }
0x2C => { Instruction::SRA(Target::U8Register(TargetRegister::H)) }
0x2D => { Instruction::SRA(Target::U8Register(TargetRegister::L)) }
0x2E => { Instruction::SRA(Target::U16Register(TargetU16Register::HL)) }
0x2F => { Instruction::SRA(Target::U8Register(TargetRegister::A)) }
0x30 => { Instruction::SWAP(Target::U8Register(TargetRegister::B)) }
0x31 => { Instruction::SWAP(Target::U8Register(TargetRegister::C)) }
0x32 => { Instruction::SWAP(Target::U8Register(TargetRegister::D)) }
0x33 => { Instruction::SWAP(Target::U8Register(TargetRegister::E)) }
0x34 => { Instruction::SWAP(Target::U8Register(TargetRegister::H)) }
0x35 => { Instruction::SWAP(Target::U8Register(TargetRegister::L)) }
0x36 => { Instruction::SWAP(Target::U16Register(TargetU16Register::HL)) }
0x37 => { Instruction::SWAP(Target::U8Register(TargetRegister::A)) }
0x38 => { Instruction::SRL(Target::U8Register(TargetRegister::B)) }
0x39 => { Instruction::SRL(Target::U8Register(TargetRegister::C)) }
0x3A => { Instruction::SRL(Target::U8Register(TargetRegister::D)) }
0x3B => { Instruction::SRL(Target::U8Register(TargetRegister::E)) }
0x3C => { Instruction::SRL(Target::U8Register(TargetRegister::H)) }
0x3D => { Instruction::SRL(Target::U8Register(TargetRegister::L)) }
0x3E => { Instruction::SRL(Target::U16Register(TargetU16Register::HL)) }
0x3F => { Instruction::SRL(Target::U8Register(TargetRegister::A)) }
0x40 => { Instruction::BIT(0, Target::U8Register(TargetRegister::B)) }
0x41 => { Instruction::BIT(0, Target::U8Register(TargetRegister::C)) }
0x42 => { Instruction::BIT(0, Target::U8Register(TargetRegister::D)) }
0x43 => { Instruction::BIT(0, Target::U8Register(TargetRegister::E)) }
0x44 => { Instruction::BIT(0, Target::U8Register(TargetRegister::H)) }
0x45 => { Instruction::BIT(0, Target::U8Register(TargetRegister::L)) }
0x46 => { Instruction::BIT(0, Target::U16Register(TargetU16Register::HL)) }
0x47 => { Instruction::BIT(0, Target::U8Register(TargetRegister::A)) }
0x48 => { Instruction::BIT(1, Target::U8Register(TargetRegister::B)) }
0x49 => { Instruction::BIT(1, Target::U8Register(TargetRegister::C)) }
0x4A => { Instruction::BIT(1, Target::U8Register(TargetRegister::D)) }
0x4B => { Instruction::BIT(1, Target::U8Register(TargetRegister::E)) }
0x4C => { Instruction::BIT(1, Target::U8Register(TargetRegister::H)) }
0x4D => { Instruction::BIT(1, Target::U8Register(TargetRegister::L)) }
0x4E => { Instruction::BIT(1, Target::U16Register(TargetU16Register::HL)) }
0x4F => { Instruction::BIT(1, Target::U8Register(TargetRegister::A)) }
0x50 => { Instruction::BIT(2, Target::U8Register(TargetRegister::B)) }
0x51 => { Instruction::BIT(2, Target::U8Register(TargetRegister::C)) }
0x52 => { Instruction::BIT(2, Target::U8Register(TargetRegister::D)) }
0x53 => { Instruction::BIT(2, Target::U8Register(TargetRegister::E)) }
0x54 => { Instruction::BIT(2, Target::U8Register(TargetRegister::H)) }
0x55 => { Instruction::BIT(2, Target::U8Register(TargetRegister::L)) }
0x56 => { Instruction::BIT(2, Target::U16Register(TargetU16Register::HL)) }
0x57 => { Instruction::BIT(2, Target::U8Register(TargetRegister::A)) }
0x58 => { Instruction::BIT(3, Target::U8Register(TargetRegister::B)) }
0x59 => { Instruction::BIT(3, Target::U8Register(TargetRegister::C)) }
0x5A => { Instruction::BIT(3, Target::U8Register(TargetRegister::D)) }
0x5B => { Instruction::BIT(3, Target::U8Register(TargetRegister::E)) }
0x5C => { Instruction::BIT(3, Target::U8Register(TargetRegister::H)) }
0x5D => { Instruction::BIT(3, Target::U8Register(TargetRegister::L)) }
0x5E => { Instruction::BIT(3, Target::U16Register(TargetU16Register::HL)) }
0x5F => { Instruction::BIT(3, Target::U8Register(TargetRegister::A)) }
0x60 => { Instruction::BIT(4, Target::U8Register(TargetRegister::B)) }
0x61 => { Instruction::BIT(4, Target::U8Register(TargetRegister::C)) }
0x62 => { Instruction::BIT(4, Target::U8Register(TargetRegister::D)) }
0x63 => { Instruction::BIT(4, Target::U8Register(TargetRegister::E)) }
0x64 => { Instruction::BIT(4, Target::U8Register(TargetRegister::H)) }
0x65 => { Instruction::BIT(4, Target::U8Register(TargetRegister::L)) }
0x66 => { Instruction::BIT(4, Target::U16Register(TargetU16Register::HL)) }
0x67 => { Instruction::BIT(4, Target::U8Register(TargetRegister::A)) }
0x68 => { Instruction::BIT(5, Target::U8Register(TargetRegister::B)) }
0x69 => { Instruction::BIT(5, Target::U8Register(TargetRegister::C)) }
0x6A => { Instruction::BIT(5, Target::U8Register(TargetRegister::D)) }
0x6B => { Instruction::BIT(5, Target::U8Register(TargetRegister::E)) }
0x6C => { Instruction::BIT(5, Target::U8Register(TargetRegister::H)) }
0x6D => { Instruction::BIT(5, Target::U8Register(TargetRegister::L)) }
0x6E => { Instruction::BIT(5, Target::U16Register(TargetU16Register::HL)) }
0x6F => { Instruction::BIT(5, Target::U8Register(TargetRegister::A)) }
0x70 => { Instruction::BIT(6, Target::U8Register(TargetRegister::B)) }
0x71 => { Instruction::BIT(6, Target::U8Register(TargetRegister::C)) }
0x72 => { Instruction::BIT(6, Target::U8Register(TargetRegister::D)) }
0x73 => { Instruction::BIT(6, Target::U8Register(TargetRegister::E)) }
0x74 => { Instruction::BIT(6, Target::U8Register(TargetRegister::H)) }
0x75 => { Instruction::BIT(6, Target::U8Register(TargetRegister::L)) }
0x76 => { Instruction::BIT(6, Target::U16Register(TargetU16Register::HL)) }
0x77 => { Instruction::BIT(6, Target::U8Register(TargetRegister::A)) }
0x78 => { Instruction::BIT(7, Target::U8Register(TargetRegister::B)) }
0x79 => { Instruction::BIT(7, Target::U8Register(TargetRegister::C)) }
0x7A => { Instruction::BIT(7, Target::U8Register(TargetRegister::D)) }
0x7B => { Instruction::BIT(7, Target::U8Register(TargetRegister::E)) }
0x7C => { Instruction::BIT(7, Target::U8Register(TargetRegister::H)) }
0x7D => { Instruction::BIT(7, Target::U8Register(TargetRegister::L)) }
0x7E => { Instruction::BIT(7, Target::U16Register(TargetU16Register::HL)) }
0x7F => { Instruction::BIT(7, Target::U8Register(TargetRegister::A)) }
0x80 => { Instruction::RES(0, Target::U8Register(TargetRegister::B)) }
0x81 => { Instruction::RES(0, Target::U8Register(TargetRegister::C)) }
0x82 => { Instruction::RES(0, Target::U8Register(TargetRegister::D)) }
0x83 => { Instruction::RES(0, Target::U8Register(TargetRegister::E)) }
0x84 => { Instruction::RES(0, Target::U8Register(TargetRegister::H)) }
0x85 => { Instruction::RES(0, Target::U8Register(TargetRegister::L)) }
0x86 => { Instruction::RES(0, Target::U16Register(TargetU16Register::HL)) }
0x87 => { Instruction::RES(0, Target::U8Register(TargetRegister::A)) }
0x88 => { Instruction::RES(1, Target::U8Register(TargetRegister::B)) }
0x89 => { Instruction::RES(1, Target::U8Register(TargetRegister::C)) }
0x8A => { Instruction::RES(1, Target::U8Register(TargetRegister::D)) }
0x8B => { Instruction::RES(1, Target::U8Register(TargetRegister::E)) }
0x8C => { Instruction::RES(1, Target::U8Register(TargetRegister::H)) }
0x8D => { Instruction::RES(1, Target::U8Register(TargetRegister::L)) }
0x8E => { Instruction::RES(1, Target::U16Register(TargetU16Register::HL)) }
0x8F => { Instruction::RES(1, Target::U8Register(TargetRegister::A)) }
0x90 => { Instruction::RES(2, Target::U8Register(TargetRegister::B)) }
0x91 => { Instruction::RES(2, Target::U8Register(TargetRegister::C)) }
0x92 => { Instruction::RES(2, Target::U8Register(TargetRegister::D)) }
0x93 => { Instruction::RES(2, Target::U8Register(TargetRegister::E)) }
0x94 => { Instruction::RES(2, Target::U8Register(TargetRegister::H)) }
0x95 => { Instruction::RES(2, Target::U8Register(TargetRegister::L)) }
0x96 => { Instruction::RES(2, Target::U16Register(TargetU16Register::HL)) }
0x97 => { Instruction::RES(2, Target::U8Register(TargetRegister::A)) }
0x98 => { Instruction::RES(3, Target::U8Register(TargetRegister::B)) }
0x99 => { Instruction::RES(3, Target::U8Register(TargetRegister::C)) }
0x9A => { Instruction::RES(3, Target::U8Register(TargetRegister::D)) }
0x9B => { Instruction::RES(3, Target::U8Register(TargetRegister::E)) }
0x9C => { Instruction::RES(3, Target::U8Register(TargetRegister::H)) }
0x9D => { Instruction::RES(3, Target::U8Register(TargetRegister::L)) }
0x9E => { Instruction::RES(3, Target::U16Register(TargetU16Register::HL)) }
0x9F => { Instruction::RES(3, Target::U8Register(TargetRegister::A)) }
0xA0 => { Instruction::RES(4, Target::U8Register(TargetRegister::B)) }
0xA1 => { Instruction::RES(4, Target::U8Register(TargetRegister::C)) }
0xA2 => { Instruction::RES(4, Target::U8Register(TargetRegister::D)) }
0xA3 => { Instruction::RES(4, Target::U8Register(TargetRegister::E)) }
0xA4 => { Instruction::RES(4, Target::U8Register(TargetRegister::H)) }
0xA5 => { Instruction::RES(4, Target::U8Register(TargetRegister::L)) }
0xA6 => { Instruction::RES(4, Target::U16Register(TargetU16Register::HL)) }
0xA7 => { Instruction::RES(4, Target::U8Register(TargetRegister::A)) }
0xA8 => { Instruction::RES(5, Target::U8Register(TargetRegister::B)) }
0xA9 => { Instruction::RES(5, Target::U8Register(TargetRegister::C)) }
0xAA => { Instruction::RES(5, Target::U8Register(TargetRegister::D)) }
0xAB => { Instruction::RES(5, Target::U8Register(TargetRegister::E)) }
0xAC => { Instruction::RES(5, Target::U8Register(TargetRegister::H)) }
0xAD => { Instruction::RES(5, Target::U8Register(TargetRegister::L)) }
0xAE => { Instruction::RES(5, Target::U16Register(TargetU16Register::HL)) }
0xAF => { Instruction::RES(5, Target::U8Register(TargetRegister::A)) }
0xB0 => { Instruction::RES(6, Target::U8Register(TargetRegister::B)) }
0xB1 => { Instruction::RES(6, Target::U8Register(TargetRegister::C)) }
0xB2 => { Instruction::RES(6, Target::U8Register(TargetRegister::D)) }
0xB3 => { Instruction::RES(6, Target::U8Register(TargetRegister::E)) }
0xB4 => { Instruction::RES(6, Target::U8Register(TargetRegister::H)) }
0xB5 => { Instruction::RES(6, Target::U8Register(TargetRegister::L)) }
0xB6 => { Instruction::RES(6, Target::U16Register(TargetU16Register::HL)) }
0xB7 => { Instruction::RES(6, Target::U8Register(TargetRegister::A)) }
0xB8 => { Instruction::RES(7, Target::U8Register(TargetRegister::B)) }
0xB9 => { Instruction::RES(7, Target::U8Register(TargetRegister::C)) }
0xBA => { Instruction::RES(7, Target::U8Register(TargetRegister::D)) }
0xBB => { Instruction::RES(7, Target::U8Register(TargetRegister::E)) }
0xBC => { Instruction::RES(7, Target::U8Register(TargetRegister::H)) }
0xBD => { Instruction::RES(7, Target::U8Register(TargetRegister::L)) }
0xBE => { Instruction::RES(7, Target::U16Register(TargetU16Register::HL)) }
0xBF => { Instruction::RES(7, Target::U8Register(TargetRegister::A)) }
0xC0 => { Instruction::SET(0, Target::U8Register(TargetRegister::B)) }
0xC1 => { Instruction::SET(0, Target::U8Register(TargetRegister::C)) }
0xC2 => { Instruction::SET(0, Target::U8Register(TargetRegister::D)) }
0xC3 => { Instruction::SET(0, Target::U8Register(TargetRegister::E)) }
0xC4 => { Instruction::SET(0, Target::U8Register(TargetRegister::H)) }
0xC5 => { Instruction::SET(0, Target::U8Register(TargetRegister::L)) }
0xC6 => { Instruction::SET(0, Target::U16Register(TargetU16Register::HL)) }
0xC7 => { Instruction::SET(0, Target::U8Register(TargetRegister::A)) }
0xC8 => { Instruction::SET(1, Target::U8Register(TargetRegister::B)) }
0xC9 => { Instruction::SET(1, Target::U8Register(TargetRegister::C)) }
0xCA => { Instruction::SET(1, Target::U8Register(TargetRegister::D)) }
0xCB => { Instruction::SET(1, Target::U8Register(TargetRegister::E)) }
0xCC => { Instruction::SET(1, Target::U8Register(TargetRegister::H)) }
0xCD => { Instruction::SET(1, Target::U8Register(TargetRegister::L)) }
0xCE => { Instruction::SET(1, Target::U16Register(TargetU16Register::HL)) }
0xCF => { Instruction::SET(1, Target::U8Register(TargetRegister::A)) }
0xD0 => { Instruction::SET(2, Target::U8Register(TargetRegister::B)) }
0xD1 => { Instruction::SET(2, Target::U8Register(TargetRegister::C)) }
0xD2 => { Instruction::SET(2, Target::U8Register(TargetRegister::D)) }
0xD3 => { Instruction::SET(2, Target::U8Register(TargetRegister::E)) }
0xD4 => { Instruction::SET(2, Target::U8Register(TargetRegister::H)) }
0xD5 => { Instruction::SET(2, Target::U8Register(TargetRegister::L)) }
0xD6 => { Instruction::SET(2, Target::U16Register(TargetU16Register::HL)) }
0xD7 => { Instruction::SET(2, Target::U8Register(TargetRegister::A)) }
0xD8 => { Instruction::SET(3, Target::U8Register(TargetRegister::B)) }
0xD9 => { Instruction::SET(3, Target::U8Register(TargetRegister::C)) }
0xDA => { Instruction::SET(3, Target::U8Register(TargetRegister::D)) }
0xDB => { Instruction::SET(3, Target::U8Register(TargetRegister::E)) }
0xDC => { Instruction::SET(3, Target::U8Register(TargetRegister::H)) }
0xDD => { Instruction::SET(3, Target::U8Register(TargetRegister::L)) }
0xDE => { Instruction::SET(3, Target::U16Register(TargetU16Register::HL)) }
0xDF => { Instruction::SET(3, Target::U8Register(TargetRegister::A)) }
0xE0 => { Instruction::SET(4, Target::U8Register(TargetRegister::B)) }
0xE1 => { Instruction::SET(4, Target::U8Register(TargetRegister::C)) }
0xE2 => { Instruction::SET(4, Target::U8Register(TargetRegister::D)) }
0xE3 => { Instruction::SET(4, Target::U8Register(TargetRegister::E)) }
0xE4 => { Instruction::SET(4, Target::U8Register(TargetRegister::H)) }
0xE5 => { Instruction::SET(4, Target::U8Register(TargetRegister::L)) }
0xE6 => { Instruction::SET(4, Target::U16Register(TargetU16Register::HL)) }
0xE7 => { Instruction::SET(4, Target::U8Register(TargetRegister::A)) }
0xE8 => { Instruction::SET(5, Target::U8Register(TargetRegister::B)) }
0xE9 => { Instruction::SET(5, Target::U8Register(TargetRegister::C)) }
0xEA => { Instruction::SET(5, Target::U8Register(TargetRegister::D)) }
0xEB => { Instruction::SET(5, Target::U8Register(TargetRegister::E)) }
0xEC => { Instruction::SET(5, Target::U8Register(TargetRegister::H)) }
0xED => { Instruction::SET(5, Target::U8Register(TargetRegister::L)) }
0xEE => { Instruction::SET(5, Target::U16Register(TargetU16Register::HL)) }
0xEF => { Instruction::SET(5, Target::U8Register(TargetRegister::A)) }
0xF0 => { Instruction::SET(6, Target::U8Register(TargetRegister::B)) }
0xF1 => { Instruction::SET(6, Target::U8Register(TargetRegister::C)) }
0xF2 => { Instruction::SET(6, Target::U8Register(TargetRegister::D)) }
0xF3 => { Instruction::SET(6, Target::U8Register(TargetRegister::E)) }
0xF4 => { Instruction::SET(6, Target::U8Register(TargetRegister::H)) }
0xF5 => { Instruction::SET(6, Target::U8Register(TargetRegister::L)) }
0xF6 => { Instruction::SET(6, Target::U16Register(TargetU16Register::HL)) }
0xF7 => { Instruction::SET(6, Target::U8Register(TargetRegister::A)) }
0xF8 => { Instruction::SET(7, Target::U8Register(TargetRegister::B)) }
0xF9 => { Instruction::SET(7, Target::U8Register(TargetRegister::C)) }
0xFA => { Instruction::SET(7, Target::U8Register(TargetRegister::D)) }
0xFB => { Instruction::SET(7, Target::U8Register(TargetRegister::E)) }
0xFC => { Instruction::SET(7, Target::U8Register(TargetRegister::H)) }
0xFD => { Instruction::SET(7, Target::U8Register(TargetRegister::L)) }
0xFE => { Instruction::SET(7, Target::U16Register(TargetU16Register::HL)) }
0xFF => { Instruction::SET(7, Target::U8Register(TargetRegister::A)) }
_ => { panic!("Invalid u16 opcode: {:02X}", opcode); }
} }
} }

493
src/lcd.rs Normal file
View File

@@ -0,0 +1,493 @@
use crate::registers::LCDControlRegister;
use std::cmp::min;
// LCD Mode represents the current mode of the LCD controller
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LCDMode {
HBlank = 0, // Horizontal Blank
VBlank = 1, // Vertical Blank
OAMSearch = 2, // Searching OAM (Object Attribute Memory)
PixelTransfer = 3, // Transferring data to LCD driver
}
// LCD-related constants
pub const SCREEN_WIDTH: usize = 160;
pub const SCREEN_HEIGHT: usize = 144;
// LCD memory addresses
pub const LCDC_ADDR: usize = 0xFF40;
pub const STAT_ADDR: usize = 0xFF41;
pub const SCY_ADDR: usize = 0xFF42;
pub const SCX_ADDR: usize = 0xFF43;
pub const LY_ADDR: usize = 0xFF44;
pub const LYC_ADDR: usize = 0xFF45;
pub const DMA_ADDR: usize = 0xFF46;
pub const BGP_ADDR: usize = 0xFF47;
pub const OBP0_ADDR: usize = 0xFF48;
pub const OBP1_ADDR: usize = 0xFF49;
pub const WY_ADDR: usize = 0xFF4A;
pub const WX_ADDR: usize = 0xFF4B;
// LCD timing constants
pub const MODE_0_CYCLES: u32 = 204; // H-Blank
pub const MODE_1_CYCLES: u32 = 456 * 10; // V-Blank (10 scanlines)
pub const MODE_2_CYCLES: u32 = 80; // OAM Search
pub const MODE_3_CYCLES: u32 = 172; // Pixel Transfer
// OAM (Object Attribute Memory) constants
pub const OAM_SIZE: usize = 160; // 40 sprites * 4 bytes each
// GPU with LCD functionality
#[derive(Debug)]
pub struct GPU {
pub vram: Vec<u8>,
pub tile_set: [[[TilePixelValue; 8]; 8]; 384],
// LCD registers
pub lcd_control: LCDControlRegister,
pub lcd_status: u8,
pub scroll_y: u8,
pub scroll_x: u8,
pub ly: u8, // Current scanline
pub lyc: u8, // Scanline compare
pub window_y: u8,
pub window_x: u8,
pub bg_palette: u8,
pub obj_palette0: u8,
pub obj_palette1: u8,
// OAM (Object Attribute Memory) for sprites
pub oam: [u8; OAM_SIZE],
// LCD screen buffer (160x144 pixels)
pub screen_buffer: [[u8; SCREEN_WIDTH]; SCREEN_HEIGHT],
// LCD timing
pub mode_clock: u32,
pub mode: LCDMode,
}
#[derive(Copy, Clone, Debug)]
pub enum TilePixelValue { Zero, One, Two, Three }
impl GPU {
// Create a new GPU with default values
pub fn new(vram: Vec<u8>) -> Self {
GPU {
vram,
tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384],
lcd_control: LCDControlRegister::from(0),
lcd_status: 0,
scroll_y: 0,
scroll_x: 0,
ly: 0,
lyc: 0,
window_y: 0,
window_x: 0,
bg_palette: 0,
obj_palette0: 0,
obj_palette1: 0,
oam: [0; OAM_SIZE],
screen_buffer: [[0; SCREEN_WIDTH]; SCREEN_HEIGHT],
mode_clock: 0,
mode: LCDMode::HBlank,
}
}
// Read from VRAM
pub fn read_vram(&self, address: usize) -> u8 {
self.vram[address]
}
// Write to VRAM and update tile data if necessary
pub fn write_vram(&mut self, index: usize, value: u8) {
self.vram[index] = value;
// If our index is greater than 0x1800, we're not writing to the tile set storage
// so we can just return.
if index >= 0x1800 { return }
// Tiles rows are encoded in two bytes with the first byte always
// on an even address. Bitwise ANDing the address with 0xffe
// gives us the address of the first byte.
// For example, `12 & 0xFFFE == 12` and `13 & 0xFFFE == 12`
let normalized_index = index & 0xFFFE;
// First, we need to get the two bytes that encode the tile row.
let byte1 = self.vram[normalized_index];
let byte2 = self.vram[normalized_index + 1];
// Tiles are 8 rows tall. Since each row is encoded with two bytes, a tile
// is therefore 16 bytes in total.
let tile_index = index / 16;
// Every two bytes is a new row
let row_index = (index % 16) / 2;
// Now we're going to loop 8 times to get the 8 pixels that make up a given row.
for pixel_index in 0..8 {
// To determine a pixel's value, we must first find the corresponding bit that encodes
// that pixel value.
let mask = 1 << (7 - pixel_index);
let lsb = byte1 & mask;
let msb = byte2 & mask;
// If the masked values are not 0, the masked bit must be 1. If they are 0, the masked
// bit must be 0.
let value = match (lsb != 0, msb != 0) {
(true, true) => TilePixelValue::Three,
(false, true) => TilePixelValue::Two,
(true, false) => TilePixelValue::One,
(false, false) => TilePixelValue::Zero,
};
self.tile_set[tile_index][row_index][pixel_index] = value;
}
}
// Read from OAM
pub fn read_oam(&self, address: usize) -> u8 {
self.oam[address]
}
// Write to OAM
pub fn write_oam(&mut self, address: usize, value: u8) {
self.oam[address] = value;
}
// Update the LCD state
pub fn update(&mut self, cycles: u32) {
// If LCD is disabled, reset and return
if !self.lcd_control.lcd_enabled {
self.mode_clock = 0;
self.ly = 0;
self.mode = LCDMode::HBlank;
return;
}
self.mode_clock += cycles;
// Update LCD based on current mode
match self.mode {
LCDMode::HBlank => {
if self.mode_clock >= MODE_0_CYCLES {
self.mode_clock = 0;
self.ly += 1;
if self.ly == SCREEN_HEIGHT as u8 {
// Enter V-Blank
self.mode = LCDMode::VBlank;
// TODO: Request V-Blank interrupt
} else {
// Start next scanline
self.mode = LCDMode::OAMSearch;
}
}
},
LCDMode::VBlank => {
if self.mode_clock >= MODE_1_CYCLES / 10 {
self.mode_clock = 0;
self.ly += 1;
// Debug print for VBlank mode
println!("VBlank: LY = {}", self.ly);
if self.ly > 153 {
// End of V-Blank, start new frame
self.ly = 0;
self.mode = LCDMode::OAMSearch;
println!("End of VBlank, starting new frame");
}
}
},
LCDMode::OAMSearch => {
if self.mode_clock >= MODE_2_CYCLES {
self.mode_clock = 0;
self.mode = LCDMode::PixelTransfer;
}
},
LCDMode::PixelTransfer => {
if self.mode_clock >= MODE_3_CYCLES {
self.mode_clock = 0;
self.mode = LCDMode::HBlank;
// Render scanline
self.render_scanline();
}
}
}
// Update LCD status register
self.update_status();
// Print debug info when LY changes
// if self.ly != old_ly {
// println!("LY changed: {} -> {} (Mode: {:?})", old_ly, self.ly, self.mode);
// }
}
// Update the LCD status register
fn update_status(&mut self) {
// Update mode bits
self.lcd_status = (self.lcd_status & 0xFC) | (self.mode as u8);
// Check LYC=LY coincidence flag
if self.ly == self.lyc {
self.lcd_status |= 0x04;
// TODO: Request LYC=LY interrupt if enabled
} else {
self.lcd_status &= !0x04;
}
// TODO: Check for mode interrupts
}
// Render a single scanline
fn render_scanline(&mut self) {
// Debug print for rendering
// if self.ly % 20 == 0 { // Print every 20th scanline to avoid flooding the console
// println!("Rendering scanline {} (Mode: {:?})", self.ly, self.mode);
// println!(" LCD Control: {:?}", self.lcd_control);
// println!(" Background Palette: 0x{:02X}", self.bg_palette);
// }
if self.lcd_control.bg_and_window_enable {
self.render_background();
if self.lcd_control.window_enable {
self.render_window();
}
} else {
}
if self.lcd_control.object_enable {
self.render_sprites();
}
}
// Render the background for the current scanline
fn render_background(&mut self) {
let tile_map_area = if self.lcd_control.bg_tile_map_area { 0x9C00 } else { 0x9800 };
let tile_data_area = if self.lcd_control.bg_and_window_tile_area { 0x8000 } else { 0x8800 };
let signed_addressing = tile_data_area == 0x8800;
// // Debug print for rendering
// if self.ly == 80 { // Only print for a specific scanline to avoid flooding the console
// println!("Rendering background for scanline {}", self.ly);
// println!(" Tile map area: 0x{:04X}", tile_map_area);
// println!(" Tile data area: 0x{:04X}", tile_data_area);
// println!(" Signed addressing: {}", signed_addressing);
// println!(" Background palette: 0x{:02X}", self.bg_palette);
// println!(" LCD Control: {:?}", self.lcd_control);
// }
let y_pos = self.ly.wrapping_add(self.scroll_y);
let tile_row = (y_pos / 8) as usize;
// Track if any non-zero pixels are set
let mut non_zero_pixels = 0;
for x in 0..SCREEN_WIDTH {
let x_pos = (x as u8).wrapping_add(self.scroll_x);
let tile_col = (x_pos / 8) as usize;
// Get the tile index from the tile map
let tile_map_addr = tile_map_area - 0x8000 + tile_row * 32 + tile_col;
let tile_index = self.vram[tile_map_addr];
// Get the tile data
let tile_data_addr = if signed_addressing {
// 8800 method uses signed addressing
let signed_index = tile_index as i8;
// Calculate the offset in i16 to handle negative indices correctly
let offset = 0x1000i16 + (signed_index as i16 * 16);
// Ensure the result is non-negative before converting to usize
if offset < 0 {
// Handle the error case - use a default address or log an error
println!("Warning: Negative tile data address calculated in render_background: {}", offset);
0 // Use tile 0 as a fallback
} else {
offset as usize
}
} else {
// 8000 method uses unsigned addressing
(tile_data_area - 0x8000) + (tile_index as usize * 16)
};
// Get the specific row of the tile
let row = (y_pos % 8) as usize;
let row_addr = tile_data_addr + row * 2;
// Get the pixel data for the row
let byte1 = self.vram[row_addr];
let byte2 = self.vram[row_addr + 1];
// Get the specific pixel in the row
let bit = 7 - (x_pos % 8);
let pixel = ((byte1 >> bit) & 1) | (((byte2 >> bit) & 1) << 1);
// Map the pixel value through the palette
let color = (self.bg_palette >> (pixel * 2)) & 0x03;
// Set the pixel in the screen buffer
self.screen_buffer[self.ly as usize][x] = color;
// Count non-zero pixels
if color > 0 {
non_zero_pixels += 1;
}
}
}
// Render the window for the current scanline
fn render_window(&mut self) {
// Only render if the window is visible on this scanline
if self.window_y > self.ly {
return;
}
let tile_map_area = if self.lcd_control.window_tile_map_area { 0x9C00 } else { 0x9800 };
let tile_data_area = if self.lcd_control.bg_and_window_tile_area { 0x8000 } else { 0x8800 };
let signed_addressing = tile_data_area == 0x8800;
let y_pos = self.ly - self.window_y;
let tile_row = (y_pos / 8) as usize;
for x in 0..SCREEN_WIDTH {
// Window X position is offset by 7
let window_x = self.window_x.wrapping_sub(7);
// Only render if this pixel is within the window
if (x as u8) < window_x {
continue;
}
let x_pos = (x as u8) - window_x;
let tile_col = (x_pos / 8) as usize;
// Get the tile index from the tile map
let tile_map_addr = tile_map_area - 0x8000 + tile_row * 32 + tile_col;
let tile_index = self.vram[tile_map_addr];
// Get the tile data
let tile_data_addr = if signed_addressing {
// 8800 method uses signed addressing
let signed_index = tile_index as i8;
// Calculate the offset in i16 to handle negative indices correctly
let offset = 0x1000i16 + (signed_index as i16 * 16);
// Ensure the result is non-negative before converting to usize
if offset < 0 {
// Handle the error case - use a default address or log an error
println!("Warning: Negative tile data address calculated in render_window: {}", offset);
0 // Use tile 0 as a fallback
} else {
offset as usize
}
} else {
// 8000 method uses unsigned addressing
(tile_data_area - 0x8000) + (tile_index as usize * 16)
};
// Get the specific row of the tile
let row = (y_pos % 8) as usize;
let row_addr = tile_data_addr + row * 2;
// Get the pixel data for the row
let byte1 = self.vram[row_addr];
let byte2 = self.vram[row_addr + 1];
// Get the specific pixel in the row
let bit = 7 - (x_pos % 8);
let pixel = ((byte1 >> bit) & 1) | (((byte2 >> bit) & 1) << 1);
// Map the pixel value through the palette
let color = (self.bg_palette >> (pixel * 2)) & 0x03;
// Set the pixel in the screen buffer
self.screen_buffer[self.ly as usize][x] = color;
}
}
// Render sprites for the current scanline
fn render_sprites(&mut self) {
// Sprite height depends on the object size flag
let sprite_height = if self.lcd_control.object_size { 16 } else { 8 };
// We can have up to 10 sprites per scanline
let mut sprites_on_line = 0;
// Check all 40 sprites
for sprite_idx in 0..40 {
if sprites_on_line >= 10 {
break;
}
let sprite_addr = sprite_idx * 4;
let sprite_y = self.oam[sprite_addr].wrapping_sub(16);
let sprite_x = self.oam[sprite_addr + 1].wrapping_sub(8);
let tile_idx = self.oam[sprite_addr + 2];
let attributes = self.oam[sprite_addr + 3];
// Check if sprite is on this scanline
if self.ly < sprite_y || self.ly >= sprite_y.wrapping_add(sprite_height) {
continue;
}
sprites_on_line += 1;
// Get sprite flags
let palette = if attributes & 0x10 != 0 { self.obj_palette1 } else { self.obj_palette0 };
let x_flip = attributes & 0x20 != 0;
let y_flip = attributes & 0x40 != 0;
let priority = attributes & 0x80 != 0;
// Calculate the row of the sprite to use
let mut row = (self.ly - sprite_y) as usize;
if y_flip {
row = sprite_height as usize - 1 - row;
}
// For 8x16 sprites, the bottom half uses the next tile
let tile = if sprite_height == 16 && row >= 8 {
(tile_idx & 0xFE) + 1
} else {
tile_idx
};
// Get the tile data address
let tile_addr = (0x8000 - 0x8000) + (tile as usize * 16) + (row % 8) * 2;
// Get the pixel data for the row
let byte1 = self.vram[tile_addr];
let byte2 = self.vram[tile_addr + 1];
// Draw the sprite pixels
for x in 0..8 {
// Skip if sprite pixel is off-screen
if sprite_x.wrapping_add(x) >= SCREEN_WIDTH as u8 {
continue;
}
// Get the pixel bit (flipped if needed)
let bit = if x_flip { x } else { 7 - x };
let pixel = ((byte1 >> bit) & 1) | (((byte2 >> bit) & 1) << 1);
// Skip transparent pixels
if pixel == 0 {
continue;
}
// Check background priority
let screen_x = sprite_x.wrapping_add(x) as usize;
if priority && self.screen_buffer[self.ly as usize][screen_x] != 0 {
continue;
}
// Map the pixel value through the palette
let color = (palette >> (pixel * 2)) & 0x03;
// Set the pixel in the screen buffer
self.screen_buffer[self.ly as usize][screen_x] = color;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,61 @@
#[derive(Clone, Copy)] const LCD_ENABLED_BIT: u8 = 7;
const WINDOW_TILE_MAP_AREA_BIT: u8 = 6;
const WINDOW_ENABLE_BIT: u8 = 5;
const BG_AND_WINDOW_TILE_DATA_AREA_BIT: u8 = 4;
const BG_TILE_MAP_AREA_BIT: u8 = 3;
const OBJECT_SIZE_BIT: u8 = 2;
const OBJECT_ENABLE_BIT: u8 = 1;
const BG_AND_WINDOW_ENABLE_BIT: u8 = 0;
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct LCDControlRegister {
pub(crate) lcd_enabled: bool,
pub(crate) window_tile_map_area: bool,
pub(crate) window_enable: bool,
pub(crate) bg_and_window_tile_area: bool,
pub(crate) bg_tile_map_area: bool,
pub(crate) object_size: bool,
pub(crate) object_enable: bool,
pub(crate) bg_and_window_enable: bool,
}
impl From<LCDControlRegister> for u8 {
fn from(flag: LCDControlRegister) -> u8 {
(if flag.lcd_enabled { 1 } else { 0 }) << LCD_ENABLED_BIT |
(if flag.window_tile_map_area { 1 } else { 0 }) << WINDOW_TILE_MAP_AREA_BIT |
(if flag.window_enable { 1 } else { 0 }) << WINDOW_ENABLE_BIT |
(if flag.bg_and_window_tile_area{ 1 } else { 0 }) << BG_AND_WINDOW_TILE_DATA_AREA_BIT |
(if flag.bg_tile_map_area { 1 } else { 0 }) << BG_TILE_MAP_AREA_BIT |
(if flag.object_size { 1 } else { 0 }) << OBJECT_SIZE_BIT |
(if flag.object_enable { 1 } else { 0 }) << OBJECT_ENABLE_BIT |
(if flag.bg_and_window_enable { 1 } else { 0 }) << BG_AND_WINDOW_ENABLE_BIT
}
}
impl From<u8> for LCDControlRegister {
fn from(byte: u8) -> Self {
let lcd_enabled = ((byte >> LCD_ENABLED_BIT) & 0b1) != 0;
let window_tile_map_area = ((byte >> WINDOW_TILE_MAP_AREA_BIT) & 0b1) != 0;
let window_enable = ((byte >> WINDOW_ENABLE_BIT) & 0b1) != 0;
let bg_and_window_tile_area = ((byte >> BG_AND_WINDOW_TILE_DATA_AREA_BIT) & 0b1) != 0;
let bg_tile_map_area = ((byte >> BG_TILE_MAP_AREA_BIT) & 0b1) != 0;
let object_size = ((byte >> OBJECT_SIZE_BIT) & 0b1) != 0;
let object_enable = ((byte >> OBJECT_ENABLE_BIT) & 0b1) != 0;
let bg_and_window_enable = ((byte >> BG_AND_WINDOW_ENABLE_BIT) & 0b1) != 0;
LCDControlRegister {
lcd_enabled,
window_tile_map_area,
window_enable,
bg_and_window_tile_area,
bg_tile_map_area,
object_size,
object_enable,
bg_and_window_enable,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct FlagsRegister { pub(crate) struct FlagsRegister {
pub(crate) zero: bool, pub(crate) zero: bool,
pub(crate) subtract: bool, pub(crate) subtract: bool,
@@ -7,12 +63,13 @@ pub(crate) struct FlagsRegister {
pub(crate) carry: bool pub(crate) carry: bool
} }
const ZERO_FLAG_BYTE_POSITION: u8 = 7; const ZERO_FLAG_BYTE_POSITION: u8 = 7;
const SUBTRACT_FLAG_BYTE_POSITION: u8 = 6; const SUBTRACT_FLAG_BYTE_POSITION: u8 = 6;
const HALF_CARRY_FLAG_BYTE_POSITION: u8 = 5; const HALF_CARRY_FLAG_BYTE_POSITION: u8 = 5;
const CARRY_FLAG_BYTE_POSITION: u8 = 4; const CARRY_FLAG_BYTE_POSITION: u8 = 4;
impl std::convert::From<FlagsRegister> for u8 { impl From<FlagsRegister> for u8 {
fn from(flag: FlagsRegister) -> u8 { fn from(flag: FlagsRegister) -> u8 {
(if flag.zero { 1 } else { 0 }) << ZERO_FLAG_BYTE_POSITION | (if flag.zero { 1 } else { 0 }) << ZERO_FLAG_BYTE_POSITION |
(if flag.subtract { 1 } else { 0 }) << SUBTRACT_FLAG_BYTE_POSITION | (if flag.subtract { 1 } else { 0 }) << SUBTRACT_FLAG_BYTE_POSITION |
@@ -21,7 +78,7 @@ impl std::convert::From<FlagsRegister> for u8 {
} }
} }
impl std::convert::From<u8> for FlagsRegister { impl From<u8> for FlagsRegister {
fn from(byte: u8) -> Self { fn from(byte: u8) -> Self {
let zero = ((byte >> ZERO_FLAG_BYTE_POSITION) & 0b1) != 0; let zero = ((byte >> ZERO_FLAG_BYTE_POSITION) & 0b1) != 0;
let subtract = ((byte >> SUBTRACT_FLAG_BYTE_POSITION) & 0b1) != 0; let subtract = ((byte >> SUBTRACT_FLAG_BYTE_POSITION) & 0b1) != 0;
@@ -36,6 +93,7 @@ impl std::convert::From<u8> for FlagsRegister {
} }
} }
} }
#[derive(Debug)]
pub(crate) struct Registers { pub(crate) struct Registers {
pub(crate) a: u8, pub(crate) a: u8,
pub(crate) b: u8, pub(crate) b: u8,

BIN
zlib1.dll Normal file

Binary file not shown.