Jump to content

GB Programming: Difference between revisions

>ISSOtm
m (→‎Chaining conditionals: Fixed a mistake.)
 
(9 intermediate revisions by 6 users not shown)
Line 48:
* A group of 32 bits is called a '''double word''' or '''dword'''.
* A group of 64 bits is called a '''quadruple word''' or '''qword'''.
We will mostly be working with bytes, sometimes with words and rarely with nibbles. It is weryvery rare to work with other structures, so you may forget them if you will.
 
Now, let's talk about '''hexadecimal'''. It is '''base 16''', so we will be working with 16 symbols : 0 1 2 3 4 5 6 7 8 9 A B C D E F. Again, we will prepend hex numbers with a $ to differentiate them.
Line 123:
|DE
|HL
|([BC)]
|([DE)]
|([HL)]
|([imm16)]
|-
|A
Line 288:
|Yes
|-
|([BC)]
|Yes
|No
Line 304:
|No
|-
|([DE)]
|Yes
|No
Line 320:
|No
|-
|([HL)]
|Yes
|Yes
Line 336:
|No
|-
|([imm16)]
|Yes
|No
Line 396:
|Store value of register B into register D.
|-
|''ld ([$8325)], a''
|Store the value of register A into memory address $8325.
|}
Line 444:
So, how does running a program works? What happens is that a special register is incremented (its value is raised by one), then the processor fetches the byte located at the address held by that register, and processes it as an opcode ; when done, everything is repeated. Instructions can be one to three opcodes (bytes) large, so this cycle may repeat for a single instruction.
 
So now, how to access memory? With parenthesesbrackets! To access memory address $CD38, you just have to use ([$CD38)]. Yay!
 
To access the memory location pointed to by HL, just do... ([hl)]! It's the same with BC and DE.
 
So, to retrieve the value at memory address $6511 into register A : ''ld a, ([$6511)]''
 
And to store the value of register C into the memory pointed to by HL : ''ld ([hl)], c''
 
Remember to refer to the chart above for the legal LD combinations.
 
Obviously, ''ld ([$6511)], a'' will overwrite the previous value stored here. But ''ld ([$6511)], hl'' will store a 16-bit value, which is a word long, that is two bytes long! So, not only will ([$6511)] be overwritten, but ([$6512)] too! Always be very careful about the memory you're touching. Otherwise, stuff like the [[ZZAZZ glitch]] happen.
 
For those wondering, ''ld a, ([$6511)]'' leaves ([$6511)] untouched.
 
==Flags==
Line 465:
|7||6||5||4||3||2||1||0
|-
|S||Z||-N||H||C||-||P/V-||N-||C-
|}
BothAll "-" are unused flags. TheirThey behaviorare isset very complicated, and they aren't "official".to Treat0 themat asall randomtimes.
 
===S : Sign===
If the accumulator is negative (from a signed perspective), the flag is set. You can also view it as a copy of the accumulator's 7th bit.
 
===Z : Zero===
Line 477 ⟶ 474:
===H : Half-Carry===
Works like the Carry flag, but referring to the least-significant ''nibble''. It is only used with the DAA instruction, so... forget it until then.
 
===P/V : Parity/Overflow===
This flag's meaning depends on the last operation.
 
If it means parity, it is set if the number of 1 in the accumulator is even. If it is odd, then it is reset.
 
If it means overflow, it is set if the last operation caused the accumulator's sign to change.
 
===N : Add/Subtract===
Line 501 ⟶ 491:
!Syntax
!Effect
!S
!Z
!P/V
!C
|-
|INC <nowiki>{reg8 | reg16 | ([hl)]}</nowiki>
|Adds one to the operand ("increments" it)
|Affected, except for reg16
|Affected, except for reg16
|Detects overflow, except for reg16
|Not affected
|-
|DEC <nowiki>{reg8 | reg16 | ([hl)]}</nowiki>
|Subtracts one to the operand ("decrements" it)
|Affected, except for reg16
|Affected, except for reg16
|Detects overflow, except for reg16
|Not affected
|-
|ADD A, <nowiki>{reg8 | imm8 | reg16 | ([hl)]}</nowiki>
|Adds the operand to the accumulator
|Affected
|Affected
|Detects overflow
|Not affected
|-
Line 530 ⟶ 512:
|Adds the operand to HL
|Affected
|Affected
|Detects overflow
|Not affected
|-
|SUB <nowiki>{reg8 | imm8 | reg16 | ([hl)]}</nowiki>
|Subtracts the operand from the accumulator. The syntax SUB A, <nowiki>{...}</nowiki> is also valid but less common.
|Affected
|Affected
|Detects overflow
|Not affected
|-
|SBC HL, reg16
|Subtracts the operand plus the carry flag from HL
|Affected
|Affected
|Detects overflow
|Not affected
|}
Line 554 ⟶ 525:
A : Nowhere :D To multiply, you must write your own routines! However, a nice lil' trick : to do A <- A*2, simply ''add a, a''! To do A <- A*3, do ''ld b, a'', ''add a, a'', ''add a, b'' (you can swap B with any other register, of course). I'll leave you A <- A*4, A*5, A*6 and A*7 as an exercise.
 
For the rest of the tutorial, you'll see some text prefixed by a ";". These are comments, and are NOT part of the code. This line : "ld ([hl)], a ; Store the mon's ID" will be interpreted as "ld ([hl)], a". Everything following a ";" is ignored.
 
Also, the Game Boy's CPU as four very specific instructions :
{| class="wikitable"
|ld ([hli)], a
|Equivalent to ''ld ([hl)], a'' then ''inc hl''.
|-
|ld ([hld)], a
|Equivalent to ''ld ([hl)], a'' then ''dec hl''.
|-
|ld a, ([hli)]
|Equivalent to ''ld a, ([hl)]'' then ''inc hl''.
|-
|ld a, ([hld)]
|Equivalent to ''ld a, ([hl)]'' then ''dec hl''.
|}
These are often used to operate on cHunkschunks of memory.
 
===Overflow===
Line 587 ⟶ 558:
Because two hex digits mean one byte, $D3, as well as $61, is a byte. Since $D3 and H are leftmost in both cases, ld hl, $D361 is actually a shorter form of ld h, $D3 then ld l, $61.
 
Let's say the following instruction is ld ([$2315)], hl. Applying the same logic would mean H's value would be stored at ([$2315)], and L's would be at ([$2316)]. However, you just lost THE GAME; because the z80 is a "little-endian" processor, L's value (the "little-end") is stored first, at ([$2315)]. So ([$2315)] is $61, and ([$2316)] is $D3.
 
Stop here, and remember this until it becomes natural to you. Because this "little-endian"ness is very tricky for beginners. It is ''very'' important when working with memory.
 
Here is an exercise : what values will ([$C000)] to ([$C00F)] contain after this code is ran?
 
Initial values :
Line 602 ⟶ 573:
<pre>
ld hl, $C303
ld a, ([$C001)]
ld b, 3
add a, b
ld c, 0
sbc hl, bc
ld ([hl)], a
inc hl
ld b, ([hl)]
sub a, b
inc ([hl)]
inc hl
ld ([hl)], b
ld bc, 9
add hl, bc
ld ([hl)], a
ld ([$C00B)], hl
</pre>
 
Line 662 ⟶ 633:
To push register DE :
<pre>
ld hl, ([$C000)] ; Retrieve stack pointer
ld ([hl)], e ; Push the low-order byte
inc hl ; Move stack pointer
ld ([hl)], d ; Repeat
inc hl
ld ([$C000)], hl ; Save stack pointer
</pre>
To pop into register DE :
<pre>
ld hl, ([$C000)] ; Retrieve stack pointer
dec hl ; Move stack pointer
ld d, ([hl)] ; Pop the high-order byte
dec hl ; Repeat
ld e, ([hl)]
ld ([$C000)], hl ; Save stack pointer
</pre>
 
Line 702 ⟶ 673:
where reg16 is any 16-bit register pair. AF can be used here.
 
Also meet SP, which makes all of this possible. SP is the '''hardware Stack Pointer'''. You can INC and DEC it, and you can't use it as a source in LD. Here are equivalents of ''push hl'' and ''pop hl'' (assuming we could use ([sp)], 'cause we can't :3)
{| class="wikitable"
|PUSH HL
|<pre>
dec sp
ld ([sp)], h
dec sp
ld ([sp)], l
</pre>
|-
|POP HL
|<pre>
ld l, ([sp)]
inc sp
ld h, ([sp)]
inc sp
</pre>
Line 725 ⟶ 696:
<pre>
push af
ld a, ([$C000)]
pop de
</pre>
Line 799 ⟶ 770:
There is one special case of jp, though !
{| class="wikitable"
|JP (HL)
|Has execution jumping to the address pointed to by hl.<br/>Does not accept any conditionals.
|}
Line 805 ⟶ 776:
<pre>
ld hl, $2457
jp (hl)
</pre>
will jump to $2457. Some might argue that "jp $2457" is better, as it'd save 1 byte and preserve the hl register.
 
However, "jp (hl)" is used to do dynamic jumps : "jp (hl)" may jump to a different location every time it is ran. When doing "static" (ie. always the same) jumps, it IS'''is''' better to use jp $xxyy. "jp (hl)" mostly used with function pointer tables - we'll see that later.
 
===Comparing stuff===
Line 839 ⟶ 810:
Example :
<pre>
ld a, ([hl)]
cp $63
jr z, placeItems
Line 846 ⟶ 817:
jr someplace
placeItems:
ld b, ([hl)]
</pre>
If ([hl)] equals $63, execution jumps to placeItems.
 
Otherwise, executions continues through, increments hl twice, then jumps to "someplace"
Line 1,083 ⟶ 1,054:
ld hl, $C303 ; Now H = $C3 and L = $03
 
ld a, ([$C001)] ; A = $03
 
ld b, 3 ; B = $03
Line 1,093 ⟶ 1,064:
sbc hl, bc ; HL = HL - (BC + C flag) = $C303 - ($0300 + $00) = $C003
 
ld ([hl)], a ; ([HL)] = ([$C003)] <- A = $06
 
inc hl ; HL = $C004
 
ld b, ([hl)] ; B = ([HL)] = ([$C004)] = $DE
 
sub a, b ; A = A - B = $06 - $DE = $06 + (-$DE) = $06 + ($21 + $01) = $28, C flag = 0
Line 1,103 ⟶ 1,074:
Notice here that doing ''sub a, b'' actually increased A's value!
 
inc ([hl)] ; ([HL)] = ([$C004)] = $DF
 
inc hl ; HL = $C005
 
ld ([hl)], b ; ([HL)] = B = $DE
 
ld bc, 9 ; B = $00, C = $09
Line 1,113 ⟶ 1,084:
add hl, bc ; HL = HL + BC = $C005 + $0009 = $C00E
 
ld ([hl)], a ; ([HL)] = ([$C00E)] = A = $28
 
ld ([$C00B)], hl ; ([$C00B)] = L = $0E, and ([$C00C)] = H = $C0
 
Initial values :
Line 1,179 ⟶ 1,150:
 
Heavily using [http://gbdev.gg8.se/wiki/articles/Pan_Docs the Pan Docs] for GameBoy-specific stuff.
 
The [http://marc.rawer.de/Gameboy/Docs/GBCPU_Instr.html GCISheet] is useful for understanding CPU instructions and can be combined with [https://iimarckus.org/etc/asmopcodes.txt IIMarckus's opcode to instruction page] or the copy on [[The Big HEX List]].
 
[https://tcrf.net/Help:Contents/Finding_Content/Debugger_guide/BGB Torchickens has started a tutorial for BGB emulator's debugger at The Cutting Room Floor]
 
[[Category:Arbitrary code execution]]
Cookies help us deliver our services. By using our services, you agree to our use of cookies.