aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/casm.html206
-rw-r--r--src/Makefile8
-rw-r--r--src/example/gb.asm86
-rw-r--r--src/gbout.c40
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, &lt;on|off&gt;
+</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, &lt;on|off&gt;
+</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, &lt;on|off&gt;
+</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
*/