jeremybanks / 0dmg Goto Github PK
View Code? Open in Web Editor NEWLearning Rust by building a partial Game Boy emulator.
License: MIT License
Learning Rust by building a partial Game Boy emulator.
License: MIT License
as defined by https://pastraiser.com/cpu/gameboy/gameboy_opcodes.html
00
NOP
01
LD BC, d16
02
LD (BC), A
03
INC BC
04
INC B
05
DEC B
06
LD B, d8
07
RLCA
08
LD (a16), SP
09
ADD HL, BC
0A
LD A, (BC)
0B
DEC BC
0C
INC C
0D
DEC C
0E
LD C, d8
0F
RRCA
10
STOP 0
11
LD DE, d16
12
LD (DE), A
13
INC DE
14
INC D
15
DEC D
16
LD D, d8
17
RLA
18
JR r8
19
ADD HL, DE
1A
LD A, (DE)
1B
DEC DE
1C
INC E
1D
DEC E
1E
LD E, d8
1F
RRA
20
JR NZ, r8
21
LD HL, d16
22
LD (HL+), A
23
INC HL
24
INC H
25
DEC H
26
LD H, d8
27
DAA
28
JR Z, r8
29
ADD HL, HL
2A
LD A, (HL+)
2B
DEC HL
2C
INC L
2D
DEC L
2E
LD L, d8
2F
CPL
30
JR NC, r8
31
LD SP, d16
32
LD (HL-), A
33
INC SP
34
INC (HL)
35
DEC (HL)
36
LD (HL), d8
37
SCF
38
JR C, r8
39
ADD HL, SP
3A
LD A, (HL-)
3B
DEC SP
3C
INC A
3D
DEC A
3E
LD A, d8
3F
CCF
40
LD B, B
41
LD B, C
42
LD B, D
43
LD B, E
44
LD B, H
45
LD B, L
46
LD B, (HL)
47
LD B, A
48
LD C, B
49
LD C, C
4A
LD C, D
4B
LD C, E
4C
LD C, H
4D
LD C, L
4E
LD C, (HL)
4F
LD C, A
50
LD D, B
51
LD D, C
52
LD D, D
53
LD D, E
54
LD D, H
55
LD D, L
56
LD D, (HL)
57
LD D, A
58
LD E, B
59
LD E, C
5A
LD E, D
5B
LD E, E
5C
LD E, H
5D
LD E, L
5E
LD E, (HL)
5F
LD E, A
60
LD H, B
61
LD H, C
62
LD H, D
63
LD H, E
64
LD H, H
65
LD H, L
66
LD H, (HL)
67
LD H, A
68
LD L, B
69
LD L, C
6A
LD L, D
6B
LD L, E
6C
LD L, H
6D
LD L, L
6E
LD L, (HL)
6F
LD L, A
70
LD (HL), B
71
LD (HL), C
72
LD (HL), D
73
LD (HL), E
74
LD (HL), H
75
LD (HL), L
76
HALT
77
LD (HL), A
78
LD A, B
79
LD A, C
7A
LD A, D
7B
LD A, E
7C
LD A, H
7D
LD A, L
7E
LD A, (HL)
7F
LD A, A
80
ADD A, B
81
ADD A, C
82
ADD A, D
83
ADD A, E
84
ADD A, H
85
ADD A, L
86
ADD A, (HL)
87
ADD A, A
88
ADC A, B
89
ADC A, C
8A
ADC A, D
8B
ADC A, E
8C
ADC A, H
8D
ADC A, L
8E
ADC A, (HL)
8F
ADC A, A
90
SUB B
91
SUB C
92
SUB D
93
SUB E
94
SUB H
95
SUB L
96
SUB (HL)
97
SUB A
98
SBC A, B
99
SBC A, C
9A
SBC A, D
9B
SBC A, E
9C
SBC A, H
9D
SBC A, L
9E
SBC A, (HL)
9F
SBC A, A
A0
AND B
A1
AND C
A2
AND D
A3
AND E
A4
AND H
A5
AND L
A6
AND (HL)
A7
AND A
A8
XOR B
A9
XOR C
AA
XOR D
AB
XOR E
AC
XOR H
AD
XOR L
AE
XOR (HL)
AF
XOR A
B0
OR B
B1
OR C
B2
OR D
B3
OR E
B4
OR H
B5
OR L
B6
OR (HL)
B7
OR A
B8
CP B
B9
CP C
BA
CP D
BB
CP E
BC
CP H
BD
CP L
BE
CP (HL)
BF
CP A
C0
RET NZ
C1
POP BC
C2
JP NZ, a16
C3
JP a16
C4
CALL NZ, a16
C5
PUSH BC
C6
ADD A, d8
C7
RST 00H
C8
RET Z
C9
RET
CA
JP Z, a16
CB
prefixCC
CALL Z, a16
CD
CALL a16
CE
ADC A, d8
CF
RST 08H
D0
RET NC
D1
POP DE
D2
JP NC, a16
D3
* invalidD4
CALL NC, a16
D5
PUSH DE
D6
SUB d8
D7
RST 10H
D8
RET C
D9
RETI
DA
JP C, a16
DB
invalidDC
CALL C, a16
DD
invalidDE
SBC A, d8
DF
RST 18H
E0
LDH (a8), A
E1
POP HL
E2
LD (C), A
E3
invalidE4
invalidE5
PUSH HL
E6
AND d8
E7
RST 20H
E8
ADD SP, r8
E9
JP (HL)
EA
LD (a16), A
EB
invalidEC
invalidED
invalidEE
XOR d8
EF
RST 28H
F0
LDH A, (a8)
F1
POP AF
F2
LD A, (C)
F3
DI
F4
invalidF5
PUSH AF
F6
OR d8
F7
RST 30H
F8
LD HL, SP+r8
F9
LD SP, HL
FA
LD A, (a16)
FB
EI
FC
invalidFD
invalidFE
CP d8
FF
RST 38H
CB 00
RLC B
CB 01
RLC C
CB 02
RLC D
CB 03
RLC E
CB 04
RLC H
CB 05
RLC L
CB 06
RLC (HL)
CB 07
RLC A
CB 08
RRC B
CB 09
RRC C
CB 0A
RRC D
CB 0B
RRC E
CB 0C
RRC H
CB 0D
RRC L
CB 0E
RRC (HL)
CB 0F
RRC A
CB 10
RL B
CB 11
RL C
CB 12
RL D
CB 13
RL E
CB 14
RL H
CB 15
RL L
CB 16
RL (HL)
CB 17
RL A
CB 18
RR B
CB 19
RR C
CB 1A
RR D
CB 1B
RR E
CB 1C
RR H
CB 1D
RR L
CB 1E
RR (HL)
CB 1F
RR A
CB 20
SLA B
CB 21
SLA C
CB 22
SLA D
CB 23
SLA E
CB 24
SLA H
CB 25
SLA L
CB 26
SLA (HL)
CB 27
SLA A
CB 28
SRA B
CB 29
SRA C
CB 2A
SRA D
CB 2B
SRA E
CB 2C
SRA H
CB 2D
SRA L
CB 2E
SRA (HL)
CB 2F
SRA A
CB 30
SWAP B
CB 31
SWAP C
CB 32
SWAP D
CB 33
SWAP E
CB 34
SWAP H
CB 35
SWAP L
CB 36
SWAP (HL)
CB 37
SWAP A
CB 38
SRL B
CB 39
SRL C
CB 3A
SRL D
CB 3B
SRL E
CB 3C
SRL H
CB 3D
SRL L
CB 3E
SRL (HL)
CB 3F
SRL A
CB 40
BIT 0, B
CB 41
BIT 0, C
CB 42
BIT 0, D
CB 43
BIT 0, E
CB 44
BIT 0, H
CB 45
BIT 0, L
CB 46
BIT 0, (HL)
CB 47
BIT 0, A
CB 48
BIT 1, B
CB 49
BIT 1, C
CB 4A
BIT 1, D
CB 4B
BIT 1, E
CB 4C
BIT 1, H
CB 4D
BIT 1, L
CB 4E
BIT 1, (HL)
CB 4F
BIT 1, A
CB 50
BIT 2, B
CB 51
BIT 2, C
CB 52
BIT 2, D
CB 53
BIT 2, E
CB 54
BIT 2, H
CB 55
BIT 2, L
CB 56
BIT 2, (HL)
CB 57
BIT 2, A
CB 58
BIT 3, B
CB 59
BIT 3, C
CB 5A
BIT 3, D
CB 5B
BIT 3, E
CB 5C
BIT 3, H
CB 5D
BIT 3, L
CB 5E
BIT 3, (HL)
CB 5F
BIT 3, A
CB 60
BIT 4, B
CB 61
BIT 4, C
CB 62
BIT 4, D
CB 63
BIT 4, E
CB 64
BIT 4, H
CB 65
BIT 4, L
CB 66
BIT 4, (HL)
CB 67
BIT 4, A
CB 68
BIT 5, B
CB 69
BIT 5, C
CB 6A
BIT 5, D
CB 6B
BIT 5, E
CB 6C
BIT 5, H
CB 6D
BIT 5, L
CB 6E
BIT 5, (HL)
CB 6F
BIT 5, A
CB 70
BIT 6, B
CB 71
BIT 6, C
CB 72
BIT 6, D
CB 73
BIT 6, E
CB 74
BIT 6, H
CB 75
BIT 6, L
CB 76
BIT 6, (HL)
CB 77
BIT 6, A
CB 78
BIT 7, B
CB 79
BIT 7, C
CB 7A
BIT 7, D
CB 7B
BIT 7, E
CB 7C
BIT 7, H
CB 7D
BIT 7, L
CB 7E
BIT 7, (HL)
CB 7F
BIT 7, A
CB 80
RES 0, B
CB 81
RES 0, C
CB 82
RES 0, D
CB 83
RES 0, E
CB 84
RES 0, H
CB 85
RES 0, L
CB 86
RES 0, (HL)
CB 87
RES 0, A
CB 88
RES 1, B
CB 89
RES 1, C
CB 8A
RES 1, D
CB 8B
RES 1, E
CB 8C
RES 1, H
CB 8D
RES 1, L
CB 8E
RES 1, (HL)
CB 8F
RES 1, A
CB 90
RES 2, B
CB 91
RES 2, C
CB 92
RES 2, D
CB 93
RES 2, E
CB 94
RES 2, H
CB 95
RES 2, L
CB 96
RES 2, (HL)
CB 97
RES 2, A
CB 98
RES 3, B
CB 99
RES 3, C
CB 9A
RES 3, D
CB 9B
RES 3, E
CB 9C
RES 3, H
CB 9D
RES 3, L
CB 9E
RES 3, (HL)
CB 9F
RES 3, A
CB A0
RES 4, B
CB A1
RES 4, C
CB A2
RES 4, D
CB A3
RES 4, E
CB A4
RES 4, H
CB A5
RES 4, L
CB A6
RES 4, (HL)
CB A7
RES 4, A
CB A8
RES 5, B
CB A9
RES 5, C
CB AA
RES 5, D
CB AB
RES 5, E
CB AC
RES 5, H
CB AD
RES 5, L
CB AE
RES 5, (HL)
CB AF
RES 5, A
CB B0
RES 6, B
CB B1
RES 6, C
CB B2
RES 6, D
CB B3
RES 6, E
CB B4
RES 6, H
CB B5
RES 6, L
CB B6
RES 6, (HL)
CB B7
RES 6, A
CB B8
RES 7, B
CB B9
RES 7, C
CB BA
RES 7, D
CB BB
RES 7, E
CB BC
RES 7, H
CB BD
RES 7, L
CB BE
RES 7, (HL)
CB BF
RES 7, A
CB C0
SET 0, B
CB C1
SET 0, C
CB C2
SET 0, D
CB C3
SET 0, E
CB C4
SET 0, H
CB C5
SET 0, L
CB C6
SET 0, (HL)
CB C7
SET 0, A
CB C8
SET 1, B
CB C9
SET 1, C
CB CA
SET 1, D
CB CB
SET 1, E
CB CC
SET 1, H
CB CD
SET 1, L
CB CE
SET 1, (HL)
CB CF
SET 1, A
CB D0
SET 2, B
CB D1
SET 2, C
CB D2
SET 2, D
CB D3
SET 2, E
CB D4
SET 2, H
CB D5
SET 2, L
CB D6
SET 2, (HL)
CB D7
SET 2, A
CB D8
SET 3, B
CB D9
SET 3, C
CB DA
SET 3, D
CB DB
SET 3, E
CB DC
SET 3, H
CB DD
SET 3, L
CB DE
SET 3, (HL)
CB DF
SET 3, A
CB E0
SET 4, B
CB E1
SET 4, C
CB E2
SET 4, D
CB E3
SET 4, E
CB E4
SET 4, H
CB E5
SET 4, L
CB E6
SET 4, (HL)
CB E7
SET 4, A
CB E8
SET 5, B
CB E9
SET 5, C
CB EA
SET 5, D
CB EB
SET 5, E
CB EC
SET 5, H
CB ED
SET 5, L
CB EE
SET 5, (HL)
CB EF
SET 5, A
CB F0
SET 6, B
CB F1
SET 6, C
CB F2
SET 6, D
CB F3
SET 6, E
CB F4
SET 6, H
CB F5
SET 6, L
CB F6
SET 6, (HL)
CB F7
SET 6, A
CB F8
SET 7, B
CB F9
SET 7, C
CB FA
SET 7, D
CB FB
SET 7, E
CB FC
SET 7, H
CB FD
SET 7, L
CB FE
SET 7, (HL)
CB FF
SET 7, A
This is a nice idea but right now we'd have no way to represent this information in the disassembled form we go through to get human-readable representation.
If we even added labels to our disassembled code blocks, we could add this information in there.
If a block is only ever jumped to from itself, maybe we call it a "loop"
?
We'll keep using the hard-coded bytes, but also use those to test an assembled version, once we've added all of the necessary instructions.
Our root crate (zerodmg
) only provides a binary target, no libraries, and so we should include the lockfile for more reproducible/reliable builds. This requires the unstable publish-lockfile
option:
https://doc.rust-lang.org/cargo/reference/unstable.html#publish-lockfile
cargo-features = ["publish-lockfile"]
[project]
...
publish-lockfile = true
Our other crates, which provide library targets, won't do this.
Let's break the emulator logic itself, separate from its interface or any binary targets, into a separate crate. We'll keep this in the same repository, and won't publish anything yet, but we'll use a path dependency and call it zerodmg-emulator
.
Any generic logic we need to share between both can be grouped into a zerodmg-utils
crate, though the ideal would be to move anything substantial into an appropriate standalone package.
For now, the root zerodmg
crate will remain the only one with a binary target. Even if we end up having multiple distinct interfaces (perhaps web vs headless CLI), we may still want to keep the single binary target and entry point, with flags to show what we want to invoke.
Instruction
NOP
INC(U8Register)
DEC(U8Register)
JP(u16)
JP_NZ(u16)
.to_bytes()
.from_byte_iter()
.byte_length()
.byte_length()
.to_string()
(pseudo-assembly) rom::FlowsTo for instruction::Instruction
with next/absolute/relative instruction flows.
DisassembledRom
.assemble()
AssembledRom
::new(u8)
.to_bytes()
instead of .into::<Vec<u8>>()
.from_bytes()
instead of .from::<Vec<u8>>()
.to_string()
(pseudo-assembly).read_known_instruction()
panic!
if read is mis-aligned.disassemble()
The Game Boy defines five types of "interrupts". Each is a type of event that can occur during execution. Each interrupt type has a constant handler address. When an interrupt is triggered, the CPU jumps to the handler instead of executing the next instruction. It will also temporarily disable interrupts, so that your interrupt doesn't itself get interrupted, although this can be toggled back if desired. Each interrupt handler routine typically ends with a RETI
instruction to jump back to the original instruction address and re-enable interrupts.
An interrupt is "triggered", either manually or automatically, by setting the appropriate bit of the interrupt trigger flag register at 0xFF0F
. The corresponding bit in the interrupt enable flag register at 0xFFFF
must be set, or else the request is ignored. If the interrupt is triggered, the corresponding request flag bit is cleared, but if it's ignored it remains set, and may be triggered if the corresponding enable bit becomes set later.
0b00000001
0x0040
V-Blank0b00000010
0x0048
LCD Status0xFF41
.0b00000100
0x0050
Timer Overflow0xFF04
, 0xFF05
, and 0xFF06
.0b00001000
0x0058
Serial Transfer0xFF01
.0b00100000
0x0060
Button Released Jump to interrupts when they're triggered.
This should be a pretty simple new branch at the beginning of each CPU tick. If interrupt trigger register bitwise-and interrupt enable register is nonzero, then clear the first set bit and jump to the corresponding handler instead of running the next instruction.
Trigger interrupts
Blargg wrote a series of test suites for the Game Boy. We have the corresponding ROMs included in this repository, and will use them to measure and confirm progress.
cpu_instrs
suite.instr_timing
suite.interrupt_time
suite.mem_timing
suite.mem_timing-2
suite.dmg_sound
suite.oam_bug
suite.halt_bug
suite.The cgb_sound
suite is omitted because we're not CGB-compatible.
zerodmg_codes
AssembledRom
AssembledRom
Gotta look into how I might run or use these:
The hardware wasn't always properly zeroed; we don't want to start making ROMs that rely on that property.
Initially, the only logic we'll be using is decoding individual instructions -- not touching the ROM types.
This will still require a lot of change, and possibly some horrible giant match blocks until/unless Rust eventually adds impl for enum variants: rust-lang/rfcs#1450.
We aren't have a fully-featured debugger, but we should add a few initial debugging features to our interpreter.
Currently, once per second we print the latest N (100?) instructions that we've executed, including their opcodes, addresses, clock time, and some debug info about what they modified. We throttle our execution so it doesn't exceed real-time, but aside from that we just run undisturbed until we hit a panic.
We should allow you to run in --debugger
mode where you have commands like:
next $N=1
: Run next $N instructions.until $T
: Run until clock time >= T.to $A
: Run until instruction pointer equals $A.resume
: Run forever.log
: Displays the latest 20(?) instructions, and the next 8 bytes from the instruction pointer.registers
: Show all registers.memory $N
: Show value in memory at address $N.read $A
: Displays the value in memory at address $A.To make this most useful, we also need to:
Result
s. If they return an Err
, they must make sure to have had no side effects, so we can (if appropriate) bring up the debugger instead of aborting the program.Whether or not we decide to codes::Instruction in emulator::CPU #11, we can still use it to assemble a demo ROM, like the one removed at 0502b32/src/emulator/memory.rs
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.