Game Boy Assembler & Emulator/Debugger
I’ve been having some fun writing a Game Boy game with a co-worker, nothing fancy, just for fun to learn more about the Game Boy and teaching my co-worker how to write games for it in assembly. One of the problems we have is that the tools we use to debug the code are a little difficult to use. Basically you have to run it in an emulator/debugger like No$GMB and then hit break points and review the state of the device. I thought it would be really cool to just write assertions directly into the assembly code and have it tell me when things are wrong. Basically like unit testing the Game Boy code.
So I wrote both a non-graphical emulator and a custom assembler for the Game Boy.
JMP
- WebASM Demo
- The Emulator
- The Assembler
- Printing
- Writing Assertions
- Available Assertions
- Game Boy OpCodes
WebASM Demo
Below is a demo of the C code compiled for WebASM. You can test out it’s functionality. To know how to use it, check out the descriptions below the demo.
The Emulator
The emulator is being developed in C for maximum portability. I mainly wrote the emulator for debugging purposes. I wanted an easy way to test subroutines for the Game Boy without having to load up a big graphical debugger in order to do so. Also those debuggers often lack the things I would like where are mainly assertions to prove that values are what they should be at a given point in the code/memory. So this emulator is not a graphical emulator and I still have yet to do any of the timing based parts of the Game Boy to call it a complete emulator. What it is good at right now is being able to run the opcode instructions given to it and manipulate registers and memory.
The Assembler
The assembler is being developed in C for maximum portability as well. In order to be able to make assertions directly in the code, it was important for me to write my own Assembler. It currently does not have any support for macros, IF
statements or any fancy math, but it does have the ability to assemble the z80 code and labels into code that the emulator can run and test against. All of the Game Boy instruction set is supported in the current build of the Assembler and subroutines (with dot labels) are supported as well in order to be able to test loops, jumps and all that sort of stuff easily. It also strips comments from the code as well.
Printing
Though you can get a print out of the value of registers or number of clock cycles that have passed when doing assertions, you may not want to kill the program with the wrong assert value. For this reason I’ve added the ability to print out any of the registers in 8-bit mode or 16-bit pair mode (including the stack pointer and program counter). You can also print out the number of clock cycles that have passed (great for performance testing). Below is an example of how to print out things.
ld hl, $2021 ; Load a value int HL
ld [hl], $09 ; Save the value 9 to memory at HL
print hl ; Print what the current value of HL is
print h ; Print the high byte of HL
print l ; Print the low byte of HL
print [hl] ; Print the value in memory pointed to by HL
Available Prints
Below is a list of all prints that you can do currently.
Keywords | Description |
---|---|
print a | Prints the value in the A register |
print f | Prints the value in the F register |
print b | Prints the value in the B register |
print c | Prints the value in the C register |
print d | Prints the value in the D register |
print e | Prints the value in the E register |
print h | Prints the value in the H register |
print l | Prints the value in the L register |
print af | Prints the 16-bit value in the AF register pair |
print bc | Prints the 16-bit value in the BC register pair |
print de | Prints the 16-bit value in the DE register pair |
print hl | Prints the 16-bit value in the HL register pair |
print [bc] | Prints the value in memory pointed to by [BC] |
print [de] | Prints the value in memory pointed to by [DE] |
print [hl] | Prints the value in memory pointed to by [HL] |
print sp | Prints the value of the stack pointer |
print pc | Prints the value of the program counter |
print clocks | Prints the number of clock cycles that have passed |
Writing Assertions
Here is an example of what the Assembler can do with assertions.
; Subtract BC from HL and store the result in HL
HL_minus_BC::
ld hl, $1104 ; Our test LHS
ld bc, $1005 ; Our test RHS
push af
ld a, l ; Get low byte
sub c ; Subtract rhs low byte
jr nc, .skip ; If we didn't go negative, jump to skip
dec h ; Otherwise we decrement high byte
.skip
ld l, a ; Set low byte to new value
ld a, h ; Get high byte
sub b ; Subtract rhs high byte
ld h, a ; Set high byte to new value
pop af
assert eq hl, $00FF
What you will see in the code above we have a line assert eq hl, $00FF
. This will test the code immediately after pop af
has ran to determine if the value in HL
is euqal to the value $00FF
. This will then print out to the console if the assertian has passed or failed. This allows for quickly testing out subroutines to make sure they work as expected.
Since the assertions are available on the state of the machine, you can use assertions to dynamically check code while it is running instead of doing static checking on that specific line. For example, here is some code that uses the e
register as a temporary value to use in the assert in each iteration through the loop.
check_my_sanity::
ld a, $09 ; Increment value in memory at address $FF00 9x
ld hl, $FF00 ; Address to increment
ld e, $00 ; Our assertion checking device
ld [hl], e ; Start our value off as 0
.loop
inc [hl] ; Increment the value at $FF00
inc e ; Increment our sanity checker
dec a ; Decrement our loop counter
assert eq [hl], e ; Assert on our dynamic value
jr nz, .loop
The above code uses the e
register as a temp value to use in the assertions. Of course this follows the same rules for non-asserted code, so you’d probably want to push whatever is inside of e
to save and restore it if you are going to do something like this.
Also, for those of you who enjoy counting clock cycles to see how fast you can make a piece of code, you can assert on cycles as well. You can check ==
, !=
, <=
, >=
, <
, >
in your clocks
assert. Below is an example of checking clock cycles at a given line. You can imagine checking eq
clocks might not be as useful as using lt
though!
; Just loading up some stuff, both take 3 clock cycles
ld hl, $1104 ; Our test LHS
ld bc, $1005 ; Our test RHS
assert eq clocks, $06
Available Assertions
Below are 3 tables, the first table is explaining the syntax used, the second is the comparison options, and the third are the actual assertions (reference the 2 tables above it).
Keywords
Keyword | Description |
---|---|
R | Any 8-bit register (a, f, b, c, d, e, h, l) |
RR | Any 16-bit register pair (af, bc, de, hl) |
%x | Any 8-bit number (5, $3A) |
%xx | Any 16-bit number (536, $3A9E) |
%xxxx | Any 32-bit number (12345678, $F36B3A9E) |
<=> | Comparison operator (eq, neq, leq, geq, lt, gt) |
clocks | The number of clock cycles that have passed since start |
Comparison operators
Keyword | Description |
---|---|
eq | Are equal |
neq | Are not equal |
leq | Left is less than or equal to right |
geq | Left is greater than or equal to right |
lt | Left is less than right |
gt | Left is greater than right |
Assertion instructions
Format | Example | Description |
---|---|---|
assert <=> R, %x | assert eq a, $3F |
Compares a register to an 8-bit value |
assert <=> R, R | assert neq b, e |
Compares the value of 2 registers |
assert <=> RR, RR | assert leq bc, de |
Compares the values of 2 16-bit register pairs |
assert <=> RR, %xx | assert geq de, $020F |
Compares the values of a 16-bit register to a 16-bit value |
assert <=> [RR], %x | assert lt [hl], $03 |
Compares the value in memory at address held in 16-bit register pair to an 8-bit value |
assert <=> [RR], R | assert gt [hl], e |
Compares the value in memory at address held in 16-bit register pair to a register value |
assert <=> [%xx], %x | assert eq [$3F9A], $09 |
Compares the value in memory at address to an 8-bit value |
assert <=> [%xx], R | assert eq [$2000], a |
Compares the value in memory at address to a register value |
assert <=> clocks, %xxxx | assert lt clocks, 9 |
Compares the number of clock cycles that have passed to the given value |
Game Boy OpCodes
Instruction | OpCode | Clocks |
---|---|---|
nop | 00 | 1 |
ld bc, %xx | 01 | 3 |
ld [bc], a | 02 | 2 |
inc bc | 03 | 2 |
inc b | 04 | 1 |
dec b | 05 | 1 |
ld b, %x | 06 | 2 |
rlca | 07 | 1 |
ld [%xx], sp | 08 | 5 |
add hl, bc | 09 | 2 |
ld a, [bc] | 0A | 2 |
inc bc | 0B | 2 |
inc c | 0C | 1 |
dec c | 0D | 1 |
ld c, %x | 0E | 2 |
rrca | 0F | 1 |
ld de, %xx | 11 | 3 |
ld [de], a | 12 | 2 |
inc de | 13 | 2 |
inc d | 14 | 1 |
dec d | 15 | 1 |
ld d, %x | 16 | 2 |
rla | 17 | 1 |
jr %x | 18 | 2 |
add hl, de | 19 | 2 |
ld a, [de] | 1A | 2 |
inc de | 1B | 2 |
inc e | 1C | 1 |
dec e | 1D | 1 |
ld e, %x | 1E | 2 |
rra | 1F | 1 |
jr nz, %x | 20 | 2 |
ld hl, %xx | 21 | 3 |
ld [hli], a | 22 | 2 |
inc hl | 23 | 2 |
inc h | 24 | 1 |
dec h | 25 | 1 |
ld h, %x | 26 | 2 |
daa | 27 | 1 |
jr z, %xx | 28 | 2 |
add hl, hl | 29 | 2 |
ld a, [hli] | 2A | 2 |
inc hl | 2B | 2 |
inc l | 2C | 1 |
dec l | 2D | 1 |
ld l, %x | 2E | 2 |
cpl | 2F | 1 |
jr nc, %x | 30 | 2 |
ld sp, %xx | 31 | 3 |
ld [hld], a | 32 | 2 |
inc sp | 33 | 2 |
inc [hl] | 34 | 3 |
dec [hl] | 35 | 3 |
ld [hl], %x | 36 | 3 |
scf | 37 | 1 |
jr c, %x | 38 | 2 |
add hl, sp | 39 | 2 |
ld a, [hld] | 3A | 2 |
inc sp | 3B | 2 |
inc a | 3C | 1 |
dec a | 3D | 1 |
ld a, %x | 3E | 2 |
ccf | 3F | 1 |
ld b, b | 40 | 1 |
ld b, c | 41 | 1 |
ld b, d | 42 | 1 |
ld b, e | 43 | 1 |
ld b, h | 44 | 1 |
ld b, l | 45 | 1 |
ld b, [hl] | 46 | 2 |
ld b, a | 47 | 1 |
ld c, b | 48 | 1 |
ld c, c | 49 | 1 |
ld c, d | 4A | 1 |
ld c, e | 4B | 1 |
ld c, h | 4C | 1 |
ld c, l | 4D | 1 |
ld c, [hl] | 4E | 2 |
ld c, a | 4F | 1 |
ld d, b | 50 | 1 |
ld d, c | 51 | 1 |
ld d, d | 52 | 1 |
ld d, e | 53 | 1 |
ld d, h | 54 | 1 |
ld d, l | 55 | 1 |
ld d, [hl] | 56 | 2 |
ld d, a | 57 | 1 |
ld e, b | 58 | 1 |
ld e, c | 59 | 1 |
ld e, d | 5A | 1 |
ld e, e | 5B | 1 |
ld e, h | 5C | 1 |
ld e, l | 5D | 1 |
ld e, [hl] | 5E | 2 |
ld e, a | 5F | 1 |
ld h, b | 60 | 1 |
ld h, c | 61 | 1 |
ld h, d | 62 | 1 |
ld h, e | 63 | 1 |
ld h, h | 64 | 1 |
ld h, l | 65 | 1 |
ld h, [hl] | 66 | 2 |
ld h, a | 67 | 1 |
ld l, b | 68 | 1 |
ld l, c | 69 | 1 |
ld l, d | 6A | 1 |
ld l, e | 6B | 1 |
ld l, h | 6C | 1 |
ld l, l | 6D | 1 |
ld l, [hl] | 6E | 2 |
ld l, a | 6F | 1 |
ld [hl], b | 70 | 2 |
ld [hl], c | 71 | 2 |
ld [hl], d | 72 | 2 |
ld [hl], e | 73 | 2 |
ld [hl], h | 74 | 2 |
ld [hl], l | 75 | 2 |
halt | 76 | 1 |
ld [hl], a | 77 | 2 |
ld a, b | 78 | 1 |
ld a, c | 79 | 1 |
ld a, d | 7A | 1 |
ld a, e | 7B | 1 |
ld a, h | 7C | 1 |
ld a, l | 7D | 1 |
ld a, [hl] | 7E | 2 |
ld a, a | 7F | 1 |
add b | 80 | 1 |
add c | 81 | 1 |
add d | 82 | 1 |
add e | 83 | 1 |
add h | 84 | 1 |
add l | 85 | 1 |
add [hl] | 86 | 2 |
add a | 87 | 1 |
adc b | 88 | 1 |
adc c | 89 | 1 |
adc d | 8A | 1 |
adc e | 8B | 1 |
adc h | 8C | 1 |
adc l | 8D | 1 |
adc [hl] | 8E | 2 |
adc a | 8F | 1 |
sub b | 90 | 1 |
sub c | 91 | 1 |
sub d | 92 | 1 |
sub e | 93 | 1 |
sub h | 94 | 1 |
sub l | 95 | 1 |
sub [hl] | 96 | 2 |
sub a | 97 | 1 |
sbc b | 98 | 1 |
sbc c | 99 | 1 |
sbc d | 9A | 1 |
sbc e | 9B | 1 |
sbc h | 9C | 1 |
sbc l | 9D | 1 |
sbc [hl] | 9E | 2 |
sbc a | 9F | 1 |
and b | A0 | 1 |
and c | A1 | 1 |
and d | A2 | 1 |
and e | A3 | 1 |
and h | A4 | 1 |
and l | A5 | 1 |
and [hl] | A6 | 2 |
and a | A7 | 1 |
xor b | A8 | 1 |
xor c | A9 | 1 |
xor d | AA | 1 |
xor e | AB | 1 |
xor h | AC | 1 |
xor l | AD | 1 |
xor [hl] | AE | 2 |
xor a | AF | 1 |
or b | B0 | 1 |
or c | B1 | 1 |
or d | B2 | 1 |
or e | B3 | 1 |
or h | B4 | 1 |
or l | B5 | 1 |
or [hl] | B6 | 2 |
or a | B7 | 1 |
cp b | B8 | 1 |
cp c | B9 | 1 |
cp d | BA | 1 |
cp e | BB | 1 |
cp h | BC | 1 |
cp l | BD | 1 |
cp [hl] | BE | 2 |
cp a | BF | 1 |
ret nz | C0 | 2 |
pop bc | C1 | 3 |
jp nz, %xx | C2 | 3 |
jp %xx | C3 | 3 |
call nz, %xx | C4 | 3 |
push bc | C5 | 4 |
add a, %x | C6 | 2 |
rst 00H | C7 | 8 |
ret z | C8 | 2 |
ret | C9 | 2 |
jp z, %xx | CA | 3 |
call z, %xx | CC | 3 |
call %xx | CD | 3 |
adc a, %x | CE | 2 |
rst 08H | CF | 8 |
ret nc | D0 | 2 |
pop de | D1 | 3 |
jp nc, %xx | D2 | 3 |
call nc, %xx | D4 | 0 |
push de | D5 | 0 |
sub a, %x | D6 | 2 |
rst 10H | D7 | 8 |
ret c | D8 | 2 |
reti | D9 | 2 |
jp c, %xx | DA | 3 |
call c, %xx | DC | 3 |
sbc a, %x | DE | 0 |
rst 18H | DF | 8 |
ld [$ff00+c], a | E0 | 3 |
pop hl | E1 | 3 |
ld [c], a | E2 | 2 |
push hl | E5 | 4 |
and %x | E6 | 2 |
rst 20H | E7 | 8 |
add sp, %x | E8 | 4 |
jp [hl] | E9 | 1 |
ld [%xx], a | EA | 4 |
xor %x | EE | 2 |
rst 28H | EF | 8 |
ld a, [$ff00+c] | F0 | 3 |
pop af | F1 | 3 |
ld a, [c] | F2 | 2 |
di | F3 | 1 |
push af | F5 | 4 |
or %x | F6 | 2 |
rst 30H | F7 | 8 |
ldhl sp, %x | F8 | 3 |
ld sp, hl | F9 | 2 |
ld a, [%xx] | FA | 4 |
ei | FB | 1 |
cp %x | FE | 2 |
rst 38H | FF | 8 |
swap a | CB37 | 2 |
swap b | CB30 | 2 |
swap c | CB31 | 2 |
swap d | CB32 | 2 |
swap e | CB33 | 2 |
swap h | CB34 | 2 |
swap l | CB35 | 2 |
swap [hl] | CB36 | 4 |
rlc a | CB07 | 2 |
rlc b | CB00 | 2 |
rlc c | CB01 | 2 |
rlc d | CB02 | 2 |
rlc e | CB03 | 2 |
rlc h | CB04 | 2 |
rlc l | CB05 | 2 |
rlc [hl] | CB06 | 4 |
rl a | CB17 | 2 |
rl b | CB10 | 2 |
rl c | CB11 | 2 |
rl d | CB12 | 2 |
rl e | CB13 | 2 |
rl h | CB14 | 2 |
rl l | CB15 | 2 |
rl [hl] | CB16 | 4 |
rrc a | CB0F | 2 |
rrc b | CB08 | 2 |
rrc c | CB09 | 2 |
rrc d | CB0A | 2 |
rrc e | CB0B | 2 |
rrc h | CB0C | 2 |
rrc l | CB0D | 2 |
rrc [hl] | CB0E | 4 |
rr a | CB1F | 2 |
rr b | CB18 | 2 |
rr c | CB19 | 2 |
rr d | CB1A | 2 |
rr e | CB1B | 2 |
rr h | CB1C | 2 |
rr l | CB1D | 2 |
rr [hl] | CB1E | 4 |
sla a | CB27 | 2 |
sla b | CB20 | 2 |
sla c | CB21 | 2 |
sla d | CB22 | 2 |
sla e | CB23 | 2 |
sla h | CB24 | 2 |
sla l | CB25 | 2 |
sla [hl] | CB26 | 4 |
sra a | CB2F | 2 |
sra b | CB28 | 2 |
sra c | CB29 | 2 |
sra d | CB2A | 2 |
sra e | CB2B | 2 |
sra h | CB2C | 2 |
sra l | CB2D | 2 |
sra [hl] | CB2E | 4 |
srl a | CB3F | 2 |
srl b | CB38 | 2 |
srl c | CB39 | 2 |
srl d | CB3A | 2 |
srl e | CB3B | 2 |
srl h | CB3C | 2 |
srl l | CB3D | 2 |
srl [hl] | CB3E | 4 |
bit a | CB47 | 2 |
bit b | CB40 | 2 |
bit c | CB41 | 2 |
bit d | CB42 | 2 |
bit e | CB43 | 2 |
bit h | CB44 | 2 |
bit l | CB45 | 2 |
bit [hl] | CB46 | 4 |
set a | CBC7 | 2 |
set b | CBC0 | 2 |
set c | CBC1 | 2 |
set d | CBC2 | 2 |
set e | CBC3 | 2 |
set h | CBC4 | 2 |
set l | CBC5 | 2 |
set [hl] | CBC6 | 4 |
res a | CB87 | 2 |
res b | CB80 | 2 |
res c | CB81 | 2 |
res d | CB82 | 2 |
res e | CB83 | 2 |
res h | CB84 | 2 |
res l | CB85 | 2 |
res [hl] | CB86 | 4 |
stop | 1000 | 1 |