Compare commits

..

11 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
13 changed files with 2170 additions and 833 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

@@ -6,7 +6,9 @@
<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>
@@ -14,6 +16,7 @@
<w>reti</w>
<w>rrca</w>
<w>rrla</w>
<w>tama</w>
<w>vram</w>
</words>
</dictionary>

1
.idea/vcs.xml generated
View File

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

151
Cargo.lock generated
View File

@@ -1,7 +1,156 @@
# This file is automatically @generated by Cargo.
# 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]]
name = "untitled"
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
[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
gb240p.gb Normal file

Binary file not shown.

1
sm83 Submodule

Submodule sm83 added at f9c3021024

609
src/instructions.rs Normal file
View File

@@ -0,0 +1,609 @@
#[derive(Clone, Copy, Debug)]
pub enum TargetRegister { A, B, C, D, E, H, L, }
#[derive(Clone, Copy, Debug)]
pub enum TargetU16Register {AF, BC, DE, HL, SP, PC}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Condition {
NZ, // Not Zero
Z, // Zero
NC, // Not Carry
C, // Carry
None,
}
#[derive(Clone, Copy, Debug)]
pub enum Target {
U8Register(TargetRegister),
U16Register(TargetU16Register),
Immediate(u8)
}
#[derive(Clone, Copy, Debug)]
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),
ADD(Target),
ADDHL(TargetU16Register),
ADDSP(i8),
AND(Target),
BIT(u8, Target),
CALL(Condition, u16),
CCF,
CP(Target),
CPL,
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),
}
fn get_u16(low: u8, high: u8) -> u16 {
((high as u16) << 8) | low as u16
}
pub fn parse_instruction(opcode: u8, arg1: u8, arg2: u8) -> Instruction {
match opcode {
0x00 => { Instruction::NOP }
0x01 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::BC, get_u16(arg1, arg2))) }
0x02 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::BC)) }
0x03 => { Instruction::INCU16(TargetU16Register::BC) }
0x04 => { Instruction::INC(Target::U8Register(TargetRegister::B)) }
0x05 => { Instruction::DEC(Target::U8Register(TargetRegister::B)) }
0x06 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::B, arg1)) }
0x07 => { Instruction::RLC(Target::U8Register(TargetRegister::A)) }
0x08 => { Instruction::LD(LoadTarget::CopyN16SP(get_u16(arg1, arg2))) }
0x09 => { Instruction::ADDHL(TargetU16Register::BC) }
0x0A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::BC)) }
0x0B => { Instruction::DECU16(TargetU16Register::BC) }
0x0C => { Instruction::INC(Target::U8Register(TargetRegister::C)) }
0x0D => { Instruction::DEC(Target::U8Register(TargetRegister::C)) }
0x0E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::C, arg1)) }
0x0F => { Instruction::RRC(Target::U8Register(TargetRegister::A)) }
0x10 => { Instruction::STOP(arg1) }
0x11 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::DE, get_u16(arg1, arg2))) }
0x12 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::DE)) }
0x13 => { Instruction::INCU16(TargetU16Register::DE) }
0x14 => { Instruction::INC(Target::U8Register(TargetRegister::D)) }
0x15 => { Instruction::DEC(Target::U8Register(TargetRegister::D)) }
0x16 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::D, arg1)) }
0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) }
0x18 => { Instruction::JR(Condition::None, arg1 as i8) }
0x19 => { Instruction::ADDHL(TargetU16Register::DE) }
0x1A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::DE)) }
0x1B => { Instruction::DECU16(TargetU16Register::DE) }
0x1C => { Instruction::INC(Target::U8Register(TargetRegister::E)) }
0x1D => { Instruction::DEC(Target::U8Register(TargetRegister::E)) }
0x1E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::E, arg1)) }
0x1F => { Instruction::RR(Target::U8Register(TargetRegister::A)) }
0x20 => { Instruction::JR(Condition::NZ, arg1 as i8) }
0x21 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::HL, get_u16(arg1, arg2))) }
0x22 => { Instruction::LD(LoadTarget::CopyHLIA) }
0x23 => { Instruction::INCU16(TargetU16Register::HL) }
0x24 => { Instruction::INC(Target::U8Register(TargetRegister::H)) }
0x25 => { Instruction::DEC(Target::U8Register(TargetRegister::H)) }
0x26 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::H, arg1)) }
0x27 => { Instruction::DAA }
0x28 => { Instruction::JR(Condition::Z, arg1 as i8) }
0x29 => { Instruction::ADDHL(TargetU16Register::HL) }
0x2A => { Instruction::LD(LoadTarget::CopyAHLI) }
0x2B => { Instruction::DECU16(TargetU16Register::HL) }
0x2C => { Instruction::INC(Target::U8Register(TargetRegister::L)) }
0x2D => { Instruction::DEC(Target::U8Register(TargetRegister::L)) }
0x2E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::L, arg1)) }
0x2F => { Instruction::CPL }
0x30 => { Instruction::JR(Condition::NC, arg1 as i8) }
0x31 => { Instruction::LD(LoadTarget::CopySPN16(get_u16(arg1, arg2))) }
0x32 => { Instruction::LD(LoadTarget::CopyHLDA) }
0x33 => { Instruction::INCU16(TargetU16Register::SP) }
0x34 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) }
0x35 => { Instruction::DEC(Target::U16Register(TargetU16Register::HL)) }
0x36 => { Instruction::LD(LoadTarget::CopyHLN8(arg1)) }
0x37 => { Instruction::SCF }
0x38 => { Instruction::JR(Condition::C, arg1 as i8) }
0x39 => { Instruction::ADDHL(TargetU16Register::SP) }
0x3A => { Instruction::LD(LoadTarget::CopyAHLD)}
0x3B => { Instruction::DECU16(TargetU16Register::SP) }
0x3C => { Instruction::INC(Target::U8Register(TargetRegister::A)) }
0x3D => { Instruction::DEC(Target::U8Register(TargetRegister::A)) }
0x3E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::A, arg1)) }
0x3F => { Instruction::CCF }
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)) }
0x43 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::E)) }
0x44 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::H)) }
0x45 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::L)) }
0x46 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::B)) }
0x47 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::A)) }
0x48 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::B)) }
0x49 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::C)) }
0x4A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::D)) }
0x4B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::E)) }
0x4C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::H)) }
0x4D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::L)) }
0x4E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::C)) }
0x4F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::A)) }
0x50 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::B)) }
0x51 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::C)) }
0x52 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::D)) }
0x53 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::E)) }
0x54 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::H)) }
0x55 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::L)) }
0x56 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::D)) }
0x57 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::A)) }
0x58 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::B)) }
0x59 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::C)) }
0x5A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::D)) }
0x5B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::E)) }
0x5C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::H)) }
0x5D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::L)) }
0x5E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::E)) }
0x5F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::A)) }
0x60 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::B)) }
0x61 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::C)) }
0x62 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::D)) }
0x63 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::E)) }
0x64 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::H)) }
0x65 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::L)) }
0x66 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::H)) }
0x67 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::A)) }
0x68 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::B)) }
0x69 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::C)) }
0x6A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::D)) }
0x6B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::E)) }
0x6C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::H)) }
0x6D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::L)) }
0x6E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::L)) }
0x6F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::A)) }
0x70 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::B)) }
0x71 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::C)) }
0x72 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::D)) }
0x73 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::E)) }
0x74 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::H)) }
0x75 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::L)) }
0x76 => { Instruction::HALT }
0x77 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::A)) }
0x78 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::B)) }
0x79 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::C)) }
0x7A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::D)) }
0x7B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::E)) }
0x7C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::H)) }
0x7D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::L)) }
0x7E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::A)) }
0x7F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::A)) }
0x80 => { Instruction::ADD(Target::U8Register(TargetRegister::B)) }
0x81 => { Instruction::ADD(Target::U8Register(TargetRegister::C)) }
0x82 => { Instruction::ADD(Target::U8Register(TargetRegister::D)) }
0x83 => { Instruction::ADD(Target::U8Register(TargetRegister::E)) }
0x84 => { Instruction::ADD(Target::U8Register(TargetRegister::H)) }
0x85 => { Instruction::ADD(Target::U8Register(TargetRegister::L)) }
0x86 => { Instruction::ADD(Target::U16Register(TargetU16Register::HL)) }
0x87 => { Instruction::ADD(Target::U8Register(TargetRegister::A)) }
0x88 => { Instruction::ADC(Target::U8Register(TargetRegister::B)) }
0x89 => { Instruction::ADC(Target::U8Register(TargetRegister::C)) }
0x8A => { Instruction::ADC(Target::U8Register(TargetRegister::D)) }
0x8B => { Instruction::ADC(Target::U8Register(TargetRegister::E)) }
0x8C => { Instruction::ADC(Target::U8Register(TargetRegister::H)) }
0x8D => { Instruction::ADC(Target::U8Register(TargetRegister::L)) }
0x8E => { Instruction::ADC(Target::U16Register(TargetU16Register::HL)) }
0x8F => { Instruction::ADC(Target::U8Register(TargetRegister::A)) }
0x90 => { Instruction::SUB(Target::U8Register(TargetRegister::B)) }
0x91 => { Instruction::SUB(Target::U8Register(TargetRegister::C)) }
0x92 => { Instruction::SUB(Target::U8Register(TargetRegister::D)) }
0x93 => { Instruction::SUB(Target::U8Register(TargetRegister::E)) }
0x94 => { Instruction::SUB(Target::U8Register(TargetRegister::H)) }
0x95 => { Instruction::SUB(Target::U8Register(TargetRegister::L)) }
0x96 => { Instruction::SUB(Target::U16Register(TargetU16Register::HL)) }
0x97 => { Instruction::SUB(Target::U8Register(TargetRegister::A)) }
0x98 => { Instruction::SBC(Target::U8Register(TargetRegister::B)) }
0x99 => { Instruction::SBC(Target::U8Register(TargetRegister::C)) }
0x9A => { Instruction::SBC(Target::U8Register(TargetRegister::D)) }
0x9B => { Instruction::SBC(Target::U8Register(TargetRegister::E)) }
0x9C => { Instruction::SBC(Target::U8Register(TargetRegister::H)) }
0x9D => { Instruction::SBC(Target::U8Register(TargetRegister::L)) }
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) zero: bool,
pub(crate) subtract: bool,
@@ -7,6 +63,7 @@ pub(crate) struct FlagsRegister {
pub(crate) carry: bool
}
const ZERO_FLAG_BYTE_POSITION: u8 = 7;
const SUBTRACT_FLAG_BYTE_POSITION: u8 = 6;
const HALF_CARRY_FLAG_BYTE_POSITION: u8 = 5;
@@ -36,6 +93,7 @@ impl From<u8> for FlagsRegister {
}
}
}
#[derive(Debug)]
pub(crate) struct Registers {
pub(crate) a: u8,
pub(crate) b: u8,