diff options
-rw-r--r-- | doc/casm.html | 206 | ||||
-rw-r--r-- | src/Makefile | 8 | ||||
-rw-r--r-- | src/example/gb.asm | 86 | ||||
-rw-r--r-- | src/gbout.c | 40 |
4 files changed, 280 insertions, 60 deletions
diff --git a/doc/casm.html b/doc/casm.html index 245a1ee..dcd21bd 100644 --- a/doc/casm.html +++ b/doc/casm.html @@ -140,8 +140,23 @@ along with this program. If not, see http://www.gnu.org/licenses/gpl-3.0.html)</a></p> </div> +<h1>Index</h1> + +<p> +<a href="#casm">CASM</a> - General usage +</p> +<p> +<a href="#z80">Z80</a> - Z80 processor support. +</p> +<p> +<a href="#6502">6502</a> - 6502 processor support. +</p> +<p> +<a href="#gbcpu">Gameboy Z80</a> - The Gameboy Z80 derivative processor. +</p> -<h1>CASM</h1> + +<h1 id="casm">CASM</h1> <h2>Usage</h2> @@ -885,6 +900,13 @@ A ZX81 emulator P file. A Commodore 64 T64 tape file. </td></tr> +<tr><td class="cmd"> +<a href="#gameboyout">gameboy</a> +</td> +<td class="def"> +A Nintendo Gameboy ROM file. +</td></tr> + </table> </td></tr> @@ -1012,6 +1034,75 @@ to start the machine code, e.g. <p>Any remaining blocks will be stored as-is without any basic loader.</p> +<h3 id="gameboyout">Nintendo Gameboy ROM File Output Format</h3> + +<p> +Generates a ROM file for a Gameboy emulator or hardware. Note that large ROM +sizes have not been extensively checked and verified. +</p> + +<p>If a single bank was used during the assembly then a simple 32K ROM is +assumed, and an error will be shown if the addresses used fall outside the range +0x150 to 0x7fff. </p> + +<p>Similarly if multiple banks are used then it is assumed that the first bank +is only used in the range 0x150 to 0x3fff, and subsequent banks in the range +0x4000 to 0x7fff. This is to fit in with the method the Gameboy uses to page +memory banks into the upper 16K portion of the normal 32K ROM address space. +</p> + +<p>By default the output driver will try and fill in the ROM size and type in +the header properly, but these can be overridden using settings.</p> + +<h4>Gameboy ROM Output Format options</h4> + +<p>The Gameboy output driver supports the following settings that can be set via +an <b>option</b> command. +</p> + +<table> + +<thead><tr><td class="head">Option</td> +<td class="head">Description</td></tr></thead> + +<tr><td class="cmd"> +option gameboy-colour, <on|off> +</td> +<td class="def"> +Whether this is a Gameboy Colour cartridge. Defaults to <i>off</i>. +Note that <b>gameboy-color</b> can be used as a different spelling for this +setting. +</td></tr> + +<tr><td class="cmd"> +option gameboy-super, <on|off> +</td> +<td class="def"> +Whether this is a Gameboy Super extended cartridge. Defaults to <i>off</i>. +</td></tr> + +<tr><td class="cmd"> +option gameboy-cart-type, <on|off> +</td> +<td class="def"> +Specifies the cartridge type. Defaults to -1, which means the output driver +will pick the appropriate type. +</td></tr> + +<tr><td class="cmd"> +option gameboy-irq, <i>irq</i>, <i>address</i>; +</td> +<td class="def"> +Specifies an address where an IRQ routine is stored, and requests that the +IRQ be transferred to that address when it happens. The IRQ routine must end +with a <b>reti</b> opcode.<br><br> +<i>irq</i> can be either <b>vbl</b>, <b>lcd</b>, <b>timer</b>, <b>serial</b> +or <b>joypad</b>. If left at the default value of -1 then an IRQ handler is +installed with just a <b>reti</b> instruction. +</td></tr> + +</table> + <h2>Listing</h2> <p> @@ -1138,7 +1229,7 @@ down to a single blank line in the listing. </table> -<h1>Z80 CPU</h1> +<h1 id="z80">Z80 CPU</h1> <h2>Opcodes</h2> @@ -1239,7 +1330,7 @@ can be any value at all: The Z80 assembler has no options. -<h1>6502 CPU</h1> +<h1 id="6502">6502 CPU</h1> <h2>Opcodes</h2> @@ -1308,6 +1399,115 @@ e.g. </td></tr> </table> +<h1 id="gbcpu">Gameboy Z80 derivative CPU</h1> + +<h2>Opcodes</h2> + +<p> +The Gameboy assembler uses the standard Z80 opcodes where applicable. +Note that the Gameboy processor has a reduced number of opcodes, flags +and no index registers, though it has some additional instructions and +addressing modes. +</p> + +<p> +For instructions were the Accumulator can be assumed it can be omitted, and +EOR can be used the same as XOR: +</p> + +<pre class="codeblock"> + xor a,a ; These are equivalent + xor a + eor a,a + + and a,b ; These are equivalent + and b +</pre> + +<p> +The Gameboy CPU has a special addressing mode used for one opcode, where the +referenced address is stored as a single byte, and used as an offset into the +top page (0xff00). This can be either triggered by using the special opcode, or +will automatically used whenever an address is accessed in the range 0xff00 to +0xffff: +</p> + +<pre class="codeblock"> + ; These all will use the special addressing mode opcode, accessing + ; memory location $ff34 + ; +label equ $ff34 + + ldh a,($34) + ldh a,($ff34) + ld a,($ff34) + ld a,(label) + + ld (label),a + ld ($ff34),a + ldh ($34),a + ldh ($ff34),a +</pre> + +<p> +The Gameboy CPU also supports incrementing or decrementing the HL register when +it is used as an address: +</p> + +<pre class="codeblock"> + ; All these decrement HL after the value has been used. + ; + ld a,(hl-) + ld a,(hld) + ldd a,(hl) + ld (hl-),a + ld (hld),a + ldd (hl),a + + ; All these increment HL after the value has been used. + ; + ld a,(hl+) + ld a,(hli) + ldi a,(hl) + ld (hl+),a + ld (hli),a + ldi (hl),a +</pre> + +<p>In addition the Gameboy CPU supports these extra instructions over the Z80: +</p> + +<pre class="codeblock"> + ; Actually loads using the address $ff00 + C + ; + ld a,(c) + ld (c),a + + ; Put the Gameboy into a low-power mode till a control is pressed. + ; Note it is accepted practice to put a NOP afterwards. This may be + ; due to the stop replacing DJNZ, which may still be wired to expect + ; an argument. That is just a wild guess though. + ; + stop + nop + + ; Swaps the low/high nibbles of the register + ; + swap a + swap b + swap c + swap d + swap e + swap h + swap l + swap (hl) +</pre> + + +<h2>Options</h2> + +The Gameboy CPU assembler has no options. + <!-- vim: ai sw=4 ts=8 expandtab spell --> </body> diff --git a/src/Makefile b/src/Makefile index 6ac1bdf..baadcb4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -79,14 +79,14 @@ clean: alias.o: alias.c global.h basetype.h util.h state.h alias.h casm.o: casm.c global.h basetype.h util.h state.h expr.h label.h macro.h \ cmd.h parse.h codepage.h stack.h listing.h alias.h output.h rawout.h \ - specout.h t64out.h zx81out.h z80.h 6502.h gbcpu.h + specout.h t64out.h zx81out.h gbout.h z80.h 6502.h gbcpu.h codepage.o: codepage.c global.h basetype.h util.h state.h codepage.h \ parse.h cmd.h expr.o: expr.c global.h basetype.h util.h state.h expr.h label.h gbcpu.o: gbcpu.c global.h basetype.h util.h state.h expr.h label.h \ parse.h cmd.h codepage.h varchar.h gbcpu.h -gbout.o: gbout.c global.h basetype.h util.h state.h codepage.h parse.h \ - cmd.h gbout.h +gbout.o: gbout.c global.h basetype.h util.h state.h expr.h codepage.h \ + parse.h cmd.h gbout.h label.o: label.c global.h basetype.h util.h state.h codepage.h parse.h \ cmd.h stack.h label.h listing.o: listing.c global.h basetype.h util.h state.h label.h macro.h \ @@ -94,7 +94,7 @@ listing.o: listing.c global.h basetype.h util.h state.h label.h macro.h \ macro.o: macro.c global.h basetype.h util.h state.h codepage.h parse.h \ cmd.h varchar.h macro.h output.o: output.c global.h basetype.h util.h state.h output.h parse.h \ - cmd.h rawout.h specout.h t64out.h zx81out.h + cmd.h rawout.h specout.h t64out.h zx81out.h gbout.h parse.o: parse.c global.h basetype.h util.h state.h codepage.h parse.h \ cmd.h rawout.o: rawout.c global.h basetype.h util.h state.h rawout.h parse.h \ diff --git a/src/example/gb.asm b/src/example/gb.asm index 10381e9..2f52e0e 100644 --- a/src/example/gb.asm +++ b/src/example/gb.asm @@ -1,22 +1,26 @@ + ; + ; This example is poor, and leaves lots of junk lymg in memory. + ; Still, it works enough to test + ; cpu gameboy option output-file,gb.gb option output-format,gameboy - ;option gameboy-irq,vbl,vbl_code + option gameboy-irq,vbl,vbl_code VRAM equ $8000 SCRN equ $9800 OAM equ $fe00 LCDC equ $ff40 STAT equ $ff41 +ISWITCH equ $ffff BGRDPAL equ $ff47 OBJ0PAL equ $ff48 OBJ1PAL equ $ff49 CURLINE equ $ff44 -READY equ $ff81 XPOS equ $ff82 VBLANK macro @@ -39,31 +43,29 @@ VBLANK macro org $150 di - xor a - ldh (READY),a ld sp,$fffe - ; Set LCD so only sprites show + ; Switch of display during setup ; VBLANK + xor a + ld (LCDC),a + + ld (XPOS),a - ld a,$81 - ldh (LCDC),a - ld a,$10 - ldh (STAT),a ld a,$e4 - ldh (BGRDPAL),a ldh (OBJ0PAL),a ldh (OBJ1PAL),a - ; Copy to VRAM + swap a + ldh (BGRDPAL),a + + ; Copy to VRAM and set up ; ld hl,VRAM ld de,sprite ld c,16 - VBLANK - .copy ld a,(de) ld (hl+),a @@ -71,12 +73,6 @@ VBLANK macro dec c jr nz,copy - ld a,1 - ldh (READY),a - - ei - - VBLANK ; Set sprite numbers ; @@ -92,9 +88,24 @@ VBLANK macro ld (OAM+7),a ld (OAM+11),a + ; Set LCD back on + ; + ld a,$83 + ldh (LCDC),a + + ; Activate VBL + ; + ld a,1 + ldh (ISWITCH),a + + ei + .idle - VBLANK + halt + nop + jr idle +vbl_code: ldh a,(XPOS) inc a ldh (XPOS),a @@ -110,23 +121,6 @@ VBLANK macro ld (OAM+8),a ld (OAM+9),a - jr idle - -vbl_code: - ldh a,(READY) - or a - jr z,finish - - ldh a,(XPOS) - inc a - ldh (XPOS),a - ld (OAM),a - ld (OAM+1),a - xor a - ld (OAM+2),a - ld (OAM+3),a - -.finish reti sprite: @@ -141,3 +135,21 @@ sprite: defb $ff,$ff defb $00,00 + ; + ; Assembly checks + ; + stop + swap a + swap b + swap c + swap d + swap e + swap h + swap l + swap (hl) + ld a,(hl+) + ld a,(hli) + ldi a,(hl) + ld a,(hl-) + ld a,(hld) + ldd a,(hl) diff --git a/src/gbout.c b/src/gbout.c index 24e365a..71547f9 100644 --- a/src/gbout.c +++ b/src/gbout.c @@ -41,7 +41,6 @@ enum option_t OPT_SUPER, OPT_CART_RAM, OPT_CART_TYPE, - OPT_RST, OPT_IRQ }; @@ -51,7 +50,7 @@ static const ValueTable option_set[] = {"gameboy-color", OPT_COLOUR}, {"gameboy-super", OPT_SUPER}, {"gameboy-cart-ram", OPT_CART_RAM}, - {"gameboy-cart-type", OPT_CART_RAM}, + {"gameboy-cart-type", OPT_CART_TYPE}, {"gameboy-irq", OPT_IRQ}, {NULL} }; @@ -193,8 +192,8 @@ int GBOutput(const char *filename, const char *filename_bank, int f; int offset; int rom_size; - unsigned global_csum = 0; - unsigned hdr_csum = 0; + Word global_csum = 0; + Byte hdr_csum = 0; Byte *mem; if (!fp) @@ -374,35 +373,43 @@ int GBOutput(const char *filename, const char *filename_bank, /* Header checksum */ + hdr_csum = 0; + for(f = 0x134 ; f < 0x14d; f++) { - hdr_csum -= mem[f] - 1; + hdr_csum = hdr_csum - mem[f] - 1u; } PokeB(mem, 0x14d, hdr_csum); /* Global checksum */ + global_csum = 0; + if (count == 1) { - for(f = 0; f < 0x8000; f++) + for(f = 0; f < 0x14e; f++) { - if (f < 0x14e || f > 0x14f) - { - global_csum += mem[f]; - } + global_csum += mem[f]; + } + + for(f = 0x150; f < 0x8000; f++) + { + global_csum += mem[f]; } } else { int r; - for(f = 0; f < 0x4000; f++) + for(f = 0; f < 0x14e; f++) { - if (f < 0x14e || f > 0x14f) - { - global_csum += mem[f]; - } + global_csum += mem[f]; + } + + for(f = 0x150; f < 0x4000; f++) + { + global_csum += mem[f]; } for(r = 1; r < count; r++) @@ -414,7 +421,8 @@ int GBOutput(const char *filename, const char *filename_bank, } } - PokeW(mem, 0x14e, global_csum); + PokeB(mem, 0x14e, global_csum >> 8); + PokeB(mem, 0x14f, global_csum); /* Output the ROM contents */ |