aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README.md1
-rw-r--r--src/Makefile42
-rw-r--r--src/casm.c13
-rw-r--r--src/example/Makefile5
-rw-r--r--src/example/gb.asm92
-rw-r--r--src/gbcpu.c1928
-rw-r--r--src/gbcpu.h46
-rw-r--r--src/gbout.c432
-rw-r--r--src/gbout.h56
-rw-r--r--src/output.c8
-rw-r--r--src/output.h1
-rw-r--r--src/z80.c111
13 files changed, 2661 insertions, 75 deletions
diff --git a/.gitignore b/.gitignore
index 6d78473..288e30d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@
# Custom
src/casm
+*.rom
*.com
*.tap
*.t64
diff --git a/README.md b/README.md
index 612c88f..254696f 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ Currently **casm** supports:
* Z80 (the default)
* 6502
+* Gameboy CPU
Plans for:
diff --git a/src/Makefile b/src/Makefile
index 29ef34c..6ac1bdf 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -39,10 +39,12 @@ SOURCE = casm.c \
alias.c \
6502.c \
z80.c \
+ gbcpu.c \
rawout.c \
specout.c \
t64out.c \
- zx81out.c
+ zx81out.c \
+ gbout.c
OBJECTS = casm.o \
expr.o \
@@ -59,10 +61,12 @@ OBJECTS = casm.o \
alias.o \
6502.o \
z80.o \
+ gbcpu.o \
rawout.o \
specout.o \
t64out.o \
- zx81out.o
+ zx81out.o \
+ gbout.o
$(TARGET): $(OBJECTS)
$(CC) $(CLAGS) -o $(TARGET) $(OBJECTS)
@@ -71,36 +75,40 @@ clean:
rm -f $(TARGET) $(TARGET).exe $(OBJECTS) core *.core
6502.o: 6502.c global.h basetype.h util.h state.h expr.h label.h parse.h \
- cmd.h codepage.h 6502.h
+ cmd.h codepage.h 6502.h
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
+ 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
codepage.o: codepage.c global.h basetype.h util.h state.h codepage.h \
- parse.h cmd.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
label.o: label.c global.h basetype.h util.h state.h codepage.h parse.h \
- cmd.h stack.h label.h
+ cmd.h stack.h label.h
listing.o: listing.c global.h basetype.h util.h state.h label.h macro.h \
- cmd.h parse.h expr.h varchar.h listing.h
+ cmd.h parse.h expr.h varchar.h listing.h
macro.o: macro.c global.h basetype.h util.h state.h codepage.h parse.h \
- cmd.h varchar.h macro.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
parse.o: parse.c global.h basetype.h util.h state.h codepage.h parse.h \
- cmd.h
+ cmd.h
rawout.o: rawout.c global.h basetype.h util.h state.h rawout.h parse.h \
- cmd.h
+ cmd.h
specout.o: specout.c global.h basetype.h util.h state.h specout.h parse.h \
- cmd.h
+ cmd.h
stack.o: stack.c global.h basetype.h util.h state.h stack.h
state.o: state.c global.h basetype.h util.h state.h expr.h
t64out.o: t64out.c global.h basetype.h util.h state.h codepage.h parse.h \
- cmd.h t64out.h
+ cmd.h t64out.h
util.o: util.c global.h basetype.h util.h state.h
varchar.o: varchar.c global.h basetype.h util.h state.h codepage.h \
- parse.h cmd.h varchar.h
+ parse.h cmd.h varchar.h
z80.o: z80.c global.h basetype.h util.h state.h expr.h label.h parse.h \
- cmd.h codepage.h varchar.h z80.h
+ cmd.h codepage.h varchar.h z80.h
zx81out.o: zx81out.c global.h basetype.h util.h state.h codepage.h \
- parse.h cmd.h zx81out.h
+ parse.h cmd.h zx81out.h
diff --git a/src/casm.c b/src/casm.c
index 0468f06..0553573 100644
--- a/src/casm.c
+++ b/src/casm.c
@@ -46,6 +46,7 @@
*/
#include "z80.h"
#include "6502.h"
+#include "gbcpu.h"
/* ---------------------------------------- MACROS
@@ -56,7 +57,7 @@
*/
static const char *casm_usage =
-"Version 1.0\n"
+"Version 1.1 development\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
@@ -100,6 +101,12 @@ static const CPU cpu_table[]=
LSB_Word,
Init_6502, Options_6502, SetOption_6502, Handler_6502
},
+ {
+ "GAMEBOY",
+ 0x10000,
+ LSB_Word,
+ Init_GBCPU, Options_GBCPU, SetOption_GBCPU, Handler_GBCPU
+ },
{NULL}
};
@@ -423,6 +430,10 @@ static CommandStatus OPTION(const char *label, int argc, char *argv[],
{
return ZX81OutputSetOption(entry->value, ac, args, q, err, errsize);
}
+ else if ((entry = ParseTable(opt, GBOutputOptions())))
+ {
+ return GBOutputSetOption(entry->value, ac, args, q, err, errsize);
+ }
else if ((entry = ParseTable(opt, cpu->options())))
{
return cpu->set_option(entry->value, ac, args, q, err, errsize);
diff --git a/src/example/Makefile b/src/example/Makefile
index 7c53209..d184de0 100644
--- a/src/example/Makefile
+++ b/src/example/Makefile
@@ -20,7 +20,7 @@
# Makefile for examples
#
-ALL = spectrum.tap c64.t64 zx81.p
+ALL = spectrum.tap c64.t64 zx81.p gb.rom
CASM = ../casm
all: $(ALL) $(CASM)
@@ -39,5 +39,8 @@ c64.t64: c64.asm $(CASM)
zx81.p: zx81.asm $(CASM)
$(CASM) zx81.asm
+gb.rom: gb.asm $(CASM)
+ $(CASM) gb.asm
+
clean:
rm -f $(ALL)
diff --git a/src/example/gb.asm b/src/example/gb.asm
new file mode 100644
index 0000000..15cc50b
--- /dev/null
+++ b/src/example/gb.asm
@@ -0,0 +1,92 @@
+
+ cpu gameboy
+ option +list
+ option list-labels,all
+
+ option output-file,gb.rom
+ option output-format,gameboy
+
+ option gameboy-irq,vbl,vbl_code
+
+VRAM equ $8000
+SCRN equ $9800
+OAM equ $fe00
+LCDC equ $ff40
+STAT equ $ff41
+
+READY equ $ff81
+XPOS equ $ff82
+
+VBLANK macro
+ push af
+.wait
+ ldh a,(LCDC)
+ cp $91
+ jr nz,wait
+
+ pop af
+
+ endm
+
+
+ ;
+ ; **********
+ ; CODE START
+ ; **********
+ ;
+ org $150
+
+ di
+ xor a
+ ldh (READY),a
+ ld sp,$fffe
+
+ ; Set LCD so only sprites show
+ ;
+ ld a,$82
+ ldh (LCDC),a
+ ld a,$10
+ ldh (STAT),a
+
+ ; Copy to VRAM
+ ;
+ ld hl,VRAM
+ ld de,sprite
+ ld a,16
+
+ VBLANK
+
+.copy
+ ld a,(hl+)
+ ld (de),a
+ inc de
+ dec a
+ jr nz,copy
+
+ ld a,1
+ ldh (READY),a
+
+.idle
+ halt
+ nop
+ jr idle
+
+vbl_code:
+ ldh a,(READY)
+ jr z,finish
+
+ ldh a,(XPOS)
+ inc a
+ ldh (XPOS),a
+ ld (OAM+1),a
+ xor a
+ ld (OAM),a
+ ld (OAM+2),a
+ ld (OAM+3),a
+
+.finish
+ reti
+
+sprite:
+ defs 16,$ff
+
diff --git a/src/gbcpu.c b/src/gbcpu.c
new file mode 100644
index 0000000..a4fffe5
--- /dev/null
+++ b/src/gbcpu.c
@@ -0,0 +1,1928 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.co.uk)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ GBCPU Assembler
+
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "global.h"
+#include "expr.h"
+#include "label.h"
+#include "parse.h"
+#include "cmd.h"
+#include "codepage.h"
+#include "varchar.h"
+
+#include "gbcpu.h"
+
+
+/* ---------------------------------------- TYPES AND GLOBALS
+*/
+typedef enum
+{
+ A8,
+ B8,
+ C8,
+ D8,
+ E8,
+ H8,
+ L8,
+ F8,
+ AF16,
+ BC16,
+ DE16,
+ HL16,
+ SP16,
+ BC_ADDRESS,
+ DE_ADDRESS,
+ HL_ADDRESS,
+ SP_ADDRESS,
+ HL_INCREMENT,
+ HL_DECREMENT,
+ FF00_C_INDEX,
+ ADDRESS,
+ VALUE,
+ INVALID_REG
+} RegisterMode;
+
+
+typedef enum
+{
+ NZ_FLAG,
+ Z_FLAG,
+ NC_FLAG,
+ C_FLAG,
+} ProcessorFlag;
+
+
+typedef enum
+{
+ IS_NORMAL_8_BIT = 0x01,
+ IS_SPECIAL_8_BIT = 0x02,
+ IS_16_BIT = 0x04,
+ IS_MEMORY = 0x08,
+ IS_FF00_C = 0x10,
+ IS_SP = 0x20,
+ IS_VALUE = 0x40,
+ IS_SPECIAL_16_BIT = 0x80,
+} RegisterType;
+
+#define IsNormal8Bit(t) ((t) & IS_NORMAL_8_BIT)
+#define IsSpecial8Bit(t) ((t) & IS_SPECIAL_8_BIT)
+#define Is16Bit(t) ((t) & IS_16_BIT)
+#define IsMemory(t) ((t) & IS_MEMORY)
+#define IsSP(t) ((t) & IS_SP)
+#define IsAddress(t) (((t) & IS_VALUE) && ((t) & IS_MEMORY))
+#define IsValue(t) ((t) & IS_VALUE)
+#define IsSimpleValue(t) ((t) == IS_VALUE)
+
+#define CheckRange(arg,num,min,max) \
+do \
+{ \
+ if (IsFinalPass() && (num < min || num > max)) \
+ { \
+ snprintf(err, errsize, "%s: outside valid " \
+ "range of %d - %d", arg, min, max); \
+ return CMD_FAILED; \
+ } \
+} while(0)
+
+#define CheckOffset(a,o) CheckRange(a,0,-128,127)
+
+
+static const int flag_bitmask[] =
+{
+ 0x00, /* NZ_FLAG */
+ 0x01, /* Z_FLAG */
+ 0x02, /* NC_FLAG */
+ 0x03 /* C_FLAG */
+};
+
+
+static const char *flag_text[] =
+{
+ "NZ", /* NZ_FLAG */
+ "Z", /* Z_FLAG */
+ "NC", /* NC_FLAG */
+ "C", /* C_FLAG */
+ NULL
+};
+
+
+static const char *register_mode_name[] =
+{
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "H",
+ "L",
+ "F",
+ "AF",
+ "BC",
+ "DE",
+ "HL",
+ "SP",
+ "(BC)",
+ "(DE)",
+ "(HL)",
+ "(SP)",
+ "(HL+)",
+ "(HL-)",
+ "(C)",
+ "(address)",
+ "value",
+ "INVALID"
+};
+
+
+static const int register_bitmask[] =
+{
+ 0x7, /* A8 */
+ 0x0, /* B8 */
+ 0x1, /* C8 */
+ 0x2, /* D8 */
+ 0x3, /* E8 */
+ 0x4, /* H8 */
+ 0x5, /* L8 */
+ 0x6, /* F8 */
+ 0x3, /* AF16 */
+ 0x0, /* BC16 */
+ 0x1, /* DE16 */
+ 0x2, /* HL16 */
+ 0x3, /* SP16 */
+ 0x0, /* BC_ADDRESS */
+ 0x0, /* DE_ADDRESS */
+ 0x0, /* HL_ADDRESS */
+ 0x0, /* SP_ADDRESS */
+ 0x0, /* HL_INCREMENT */
+ 0x0, /* HL_DECREMENT */
+ 0x0, /* FF00_C_INDEX */
+ 0x0, /* ADDRESS */
+ 0x0, /* VALUE */
+};
+
+
+typedef struct
+{
+ RegisterMode mode;
+ int quote;
+ int starts_with;
+ int take_value;
+ const char *ident;
+ RegisterType type;
+} RegisterModeTable;
+
+
+static RegisterModeTable register_mode_table[] =
+{
+ {
+ A8,
+ 0,
+ FALSE,
+ FALSE,
+ "A",
+ IS_NORMAL_8_BIT
+ },
+ {
+ B8,
+ 0,
+ FALSE,
+ FALSE,
+ "B",
+ IS_NORMAL_8_BIT
+ },
+ {
+ C8,
+ 0,
+ FALSE,
+ FALSE,
+ "C",
+ IS_NORMAL_8_BIT
+ },
+ {
+ D8,
+ 0,
+ FALSE,
+ FALSE,
+ "D",
+ IS_NORMAL_8_BIT
+ },
+ {
+ E8,
+ 0,
+ FALSE,
+ FALSE,
+ "E",
+ IS_NORMAL_8_BIT
+ },
+ {
+ H8,
+ 0,
+ FALSE,
+ FALSE,
+ "H",
+ IS_NORMAL_8_BIT
+ },
+ {
+ L8,
+ 0,
+ FALSE,
+ FALSE,
+ "L",
+ IS_NORMAL_8_BIT
+ },
+ {
+ F8,
+ 0,
+ FALSE,
+ FALSE,
+ "F",
+ IS_SPECIAL_8_BIT
+ },
+
+ {
+ AF16,
+ 0,
+ FALSE,
+ FALSE,
+ "AF",
+ IS_SPECIAL_16_BIT
+ },
+ {
+ BC16,
+ 0,
+ FALSE,
+ FALSE,
+ "BC",
+ IS_16_BIT
+ },
+ {
+ DE16,
+ 0,
+ FALSE,
+ FALSE,
+ "DE",
+ IS_16_BIT
+ },
+ {
+ HL16,
+ 0,
+ FALSE,
+ FALSE,
+ "HL",
+ IS_16_BIT
+ },
+ {
+ SP16,
+ 0,
+ FALSE,
+ FALSE,
+ "SP",
+ IS_16_BIT|IS_SP
+ },
+
+ {
+ BC_ADDRESS,
+ '(',
+ FALSE,
+ FALSE,
+ "BC",
+ IS_16_BIT|IS_MEMORY
+ },
+ {
+ DE_ADDRESS,
+ '(',
+ FALSE,
+ FALSE,
+ "DE",
+ IS_16_BIT|IS_MEMORY
+ },
+ {
+ HL_ADDRESS,
+ '(',
+ FALSE,
+ FALSE,
+ "HL",
+ IS_16_BIT|IS_MEMORY
+ },
+ {
+ SP_ADDRESS,
+ '(',
+ FALSE,
+ FALSE,
+ "SP",
+ IS_SPECIAL_16_BIT|IS_MEMORY|IS_SP
+ },
+ {
+ HL_INCREMENT,
+ '(',
+ FALSE,
+ FALSE,
+ "HLI",
+ IS_SPECIAL_16_BIT|IS_MEMORY
+ },
+ {
+ HL_INCREMENT,
+ '(',
+ FALSE,
+ FALSE,
+ "HL+",
+ IS_SPECIAL_16_BIT|IS_MEMORY
+ },
+ {
+ HL_DECREMENT,
+ '(',
+ FALSE,
+ FALSE,
+ "HLD",
+ IS_SPECIAL_16_BIT|IS_MEMORY
+ },
+ {
+ HL_DECREMENT,
+ '(',
+ FALSE,
+ FALSE,
+ "HL-",
+ IS_SPECIAL_16_BIT|IS_MEMORY
+ },
+ {
+ FF00_C_INDEX,
+ '(',
+ FALSE,
+ FALSE,
+ "C",
+ IS_FF00_C
+ },
+
+ /* These cheat -- basically anything that doesn't match until here is either
+ an address or a value.
+ */
+ {
+ ADDRESS,
+ '(',
+ TRUE,
+ TRUE,
+ "",
+ IS_VALUE|IS_MEMORY
+ },
+ {
+ VALUE,
+ 0,
+ TRUE,
+ TRUE,
+ "",
+ IS_VALUE
+ },
+
+ {0}
+};
+
+
+typedef enum
+{
+ WRITE_BYTE_LHS = -1,
+ WRITE_WORD_LHS = -2,
+ WRITE_BYTE_RHS = -3,
+ WRITE_WORD_RHS = -4
+} StreamCodes;
+
+
+typedef struct
+{
+ RegisterMode lhs;
+ RegisterMode rhs;
+ int code[10];
+} RegisterPairCodes;
+
+#define NUM_REGISTER_CODES(a) ((sizeof a)/(sizeof a[1]))
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+static int CalcRegisterMode(const char *arg, int quote,
+ RegisterMode *mode,
+ RegisterType *type,
+ int *offset,
+ char *err, size_t errsize)
+{
+ int f;
+
+ if (IsNullOrEmpty(arg))
+ {
+ snprintf(err, errsize, "empty argument supplied");
+ return FALSE;
+ }
+
+ for(f = 0; register_mode_table[f].ident; f++)
+ {
+ RegisterModeTable *t = register_mode_table + f;
+
+ if (quote == t->quote)
+ {
+ int match;
+
+ if (t->starts_with)
+ {
+ match = CompareStart(arg, t->ident);
+ }
+ else
+ {
+ match = CompareString(arg, t->ident);
+ }
+
+ if (match)
+ {
+ *mode = t->mode;
+ *type = t->type;
+ *offset = 0;
+
+ if (t->take_value)
+ {
+ size_t l = strlen(t->ident);
+
+ if (!ExprEval(arg + l, offset))
+ {
+ snprintf(err, errsize, "%s: expression error: %s",
+ arg, ExprError());
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+ }
+ }
+
+ snprintf(err, errsize, "%s: couldn't calculate register/addressing mode",
+ arg);
+
+ return FALSE;
+}
+
+
+static int CalcFlagMode(const char *arg, ProcessorFlag *flag, int *mask,
+ char *err, size_t errsize)
+{
+ int f;
+
+ for(f = 0; flag_text[f]; f++)
+ {
+ if (CompareString(arg, flag_text[f]))
+ {
+ *flag = f;
+ *mask = flag_bitmask[f];
+ return TRUE;
+ }
+ }
+
+ snprintf(err, errsize, "%s: unknown flag", arg);
+
+ return FALSE;
+}
+
+
+static int WriteRegisterPairModes(const RegisterPairCodes *codes, size_t count,
+ RegisterMode lhs, RegisterMode rhs,
+ int val_lhs, int val_rhs,
+ char *err, size_t errsize)
+{
+ size_t f;
+
+ for(f = 0; f < count; f++)
+ {
+ if (codes[f].lhs == lhs && codes[f].rhs == rhs)
+ {
+ int r;
+
+ for(r = 0; codes[f].code[r]; r++)
+ {
+ switch(codes[f].code[r])
+ {
+ case WRITE_BYTE_LHS:
+ PCWrite(val_lhs);
+ break;
+
+ case WRITE_WORD_LHS:
+ PCWriteWord(val_lhs);
+ break;
+
+ case WRITE_BYTE_RHS:
+ PCWrite(val_rhs);
+ break;
+
+ case WRITE_WORD_RHS:
+ PCWriteWord(val_rhs);
+ break;
+
+ default:
+ PCWrite(codes[f].code[r]);
+ break;
+ }
+ }
+ return TRUE;
+ }
+ }
+
+ snprintf(err, errsize, "LD: no code generation for register pair %s,%s",
+ register_mode_name[lhs], register_mode_name[rhs]);
+
+ return FALSE;
+}
+
+
+/* Assume accumulator if only one argument
+*/
+static CommandStatus AssumeAccumulator(int argc, char *argv[], int quoted[],
+ char *err, size_t errsize,
+ RegisterMode *r1, RegisterMode *r2,
+ RegisterType *t1, RegisterType *t2,
+ int *off1, int *off2)
+{
+ CMD_ARGC_CHECK(2);
+
+ if (argc == 2)
+ {
+ CalcRegisterMode("A", 0, r1, t1, off1, err, errsize);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], r2, t2, off2,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+ }
+ else
+ {
+ if (!CalcRegisterMode(argv[1], quoted[1], r1, t1, off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!CalcRegisterMode(argv[2], quoted[2], r2, t2, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+ }
+
+ return CMD_OK;
+}
+
+
+/* Returns true if the passed register is any of the passed ones. The list of
+ registers is terminated with INVALID_REG
+*/
+static int IsAnyOf(RegisterMode reg, ...)
+{
+ va_list ap;
+ RegisterMode m;
+
+ va_start(ap, reg);
+
+ m = va_arg(ap, RegisterMode);
+
+ while(m != INVALID_REG)
+ {
+ if (reg == m)
+ {
+ return TRUE;
+ }
+
+ m = va_arg(ap, RegisterMode);
+ }
+
+ return FALSE;
+}
+
+
+static CommandStatus IllegalArgs(int argc, char *argv[], int quoted[],
+ char *err, size_t errsize)
+{
+ Varchar *str;
+ int f;
+
+ str = VarcharCreate(NULL);
+
+ switch(argc)
+ {
+ case 0:
+ VarcharPrintf(str, "no command/arguments");
+ break;
+
+ case 1:
+ VarcharPrintf(str, "%s: no arguments", argv[0]);
+ break;
+
+ case 2:
+ VarcharPrintf(str, "%s: illegal argument", argv[0]);
+ break;
+
+ default:
+ VarcharPrintf(str, "%s: illegal arguments", argv[0]);
+ break;
+ }
+
+ for(f = 1; f < argc; f++)
+ {
+ if (f == 1)
+ {
+ VarcharAdd(str, " ");
+ }
+ else
+ {
+ VarcharAdd(str, ", ");
+ }
+
+ if (quoted[f] && quoted[f] == '(')
+ {
+ VarcharPrintf(str, "(%s)", argv[f]);
+ }
+ else if (quoted[f])
+ {
+ VarcharPrintf(str, "%c%s%c", quoted[f], argv[f], quoted[f]);
+ }
+ else
+ {
+ VarcharAdd(str, argv[f]);
+ }
+ }
+
+ snprintf(err, errsize, "%s", VarcharContents(str));
+ VarcharFree(str);
+
+ return CMD_FAILED;
+}
+
+
+/* ---------------------------------------- COMMAND HANDLERS
+*/
+static CommandStatus LD(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, BC_ADDRESS, {0x0a},
+ A8, DE_ADDRESS, {0x1a},
+ A8, ADDRESS, {0xfa, WRITE_WORD_RHS},
+ BC_ADDRESS, A8, {0x02},
+ DE_ADDRESS, A8, {0x12},
+ ADDRESS, A8, {0xea, WRITE_WORD_LHS},
+ HL_DECREMENT, A8, {0x32},
+ A8, HL_DECREMENT, {0x3a},
+ HL_INCREMENT, A8, {0x22},
+ A8, HL_INCREMENT, {0x2a},
+ FF00_C_INDEX, A8, {0xe2}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ CMD_ARGC_CHECK(3);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ /* LD r,r'
+ */
+ if (IsNormal8Bit(t1) && IsNormal8Bit(t2))
+ {
+ CommandStatus s = CMD_OK;
+
+ PCWrite(0x40 | register_bitmask[r1] << 3 | register_bitmask[r2]);
+
+ return s;
+ }
+
+
+ /* LD r,n
+ */
+ if (IsNormal8Bit(t1) && IsSimpleValue(t2))
+ {
+ PCWrite(register_bitmask[r1] << 3 | 0x6);
+ PCWrite(off2);
+ return CMD_OK;
+ }
+
+
+ /* LD r,(HL)
+ */
+ if (IsNormal8Bit(t1) && r2 == HL_ADDRESS)
+ {
+ PCWrite(0x46 | register_bitmask[r1] << 3);
+ return CMD_OK;
+ }
+
+
+ /* LD (HL),r
+ */
+ if (IsNormal8Bit(t2) && r1 == HL_ADDRESS)
+ {
+ PCWrite(0x70 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+
+ /* LD (HL),n
+ */
+ if (r1 == HL_ADDRESS && r2 == VALUE)
+ {
+ PCWrite(0x36);
+ PCWrite(off2);
+ return CMD_OK;
+ }
+
+
+ /* LD rr,nn
+ */
+ if (Is16Bit(t1) && !IsMemory(t1) && r2 == VALUE)
+ {
+ PCWrite(register_bitmask[r1] << 4 | 0x01);
+ return CMD_OK;
+ }
+
+
+ /* LD HL,(nn)
+ */
+ if (r1 == HL16 && r2 == ADDRESS)
+ {
+ PCWrite(0x2a);
+ PCWriteWord(off2);
+ return CMD_OK;
+ }
+
+
+ /* LD rr,(nn)
+ */
+ if (Is16Bit(t1) && !IsMemory(t1) && r2 == ADDRESS)
+ {
+ PCWrite(0xed);
+ PCWrite(0x4b | register_bitmask[r1] << 4);
+ PCWriteWord(off2);
+ return CMD_OK;
+ }
+
+
+ /* LD (nn),HL
+ */
+ if (r2 == HL16 && r1 == ADDRESS)
+ {
+ PCWrite(0x22);
+ PCWriteWord(off1);
+ return CMD_OK;
+ }
+
+
+ /* LD (nn),rr
+ */
+ if (Is16Bit(t2) && !IsMemory(t2) && r1 == ADDRESS)
+ {
+ PCWrite(0xed);
+ PCWrite(0x43 | register_bitmask[r2] << 4);
+ PCWriteWord(off1);
+ return CMD_OK;
+ }
+
+
+ /* LD SP,HL
+ */
+ if (r2 == HL16 && r1 == SP16)
+ {
+ PCWrite(0xf9);
+ return CMD_OK;
+ }
+
+
+ /* Custom opcode generation using the codes table
+ */
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus LDH(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, ADDRESS, {0xf0, WRITE_BYTE_RHS},
+ ADDRESS, A8, {0xe0, WRITE_BYTE_LHS}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ CMD_ARGC_CHECK(3);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (r1 == ADDRESS)
+ {
+ if (off1 >= 0xff00 && off1 <= 0xffff)
+ {
+ off1 -= 0xff00;
+ }
+
+ CheckRange(argv[1], off1, 0, 255);
+ }
+
+ if (r2 == ADDRESS)
+ {
+ if (off2 >= 0xff00 && off2 <= 0xffff)
+ {
+ off2 -= 0xff00;
+ }
+
+ CheckRange(argv[2], off2, 0, 255);
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus LDD(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ HL_ADDRESS, A8, {0x32},
+ A8, HL_ADDRESS, {0x3a}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ CMD_ARGC_CHECK(3);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus LDI(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ HL_ADDRESS, A8, {0x22},
+ A8, HL_ADDRESS, {0x2a}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ CMD_ARGC_CHECK(3);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus PUSH(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ RegisterMode r1;
+ RegisterType t1;
+ int off1;
+
+ CMD_ARGC_CHECK(2);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ /* PUSH rr
+ */
+ if (r1 == AF16 || r1 == BC16 || r1 == DE16 || r1 == HL16)
+ {
+ PCWrite(0xc5 | register_bitmask[r1] << 4);
+ return CMD_OK;
+ }
+
+ snprintf(err, errsize, "%s: invalid argument %s", argv[0], argv[1]);
+
+ return CMD_OK;
+}
+
+
+static CommandStatus POP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ RegisterMode r1;
+ RegisterType t1;
+ int off1;
+
+ CMD_ARGC_CHECK(2);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ /* POP rr
+ */
+ if (r1 == AF16 || r1 == BC16 || r1 == DE16 || r1 == HL16)
+ {
+ PCWrite(0xc1 | register_bitmask[r1] << 4);
+ return CMD_OK;
+ }
+
+ snprintf(err, errsize, "%s: invalid argument %s", argv[0], argv[1]);
+
+ return CMD_OK;
+}
+
+
+static CommandStatus ADD(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xc6, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0x86}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* ADD A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0x80 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ /* ADD HL,rr
+ */
+ if (r1 == HL16 && IsAnyOf(r2, BC16, DE16, HL16, SP16, INVALID_REG))
+ {
+ PCWrite(0x09 | register_bitmask[r2] << 4);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus ADC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xce, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0x8e}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* ADC A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0x88 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ /* ADC HL,rr
+ */
+ if (r1 == HL16 && IsAnyOf(r2, BC16, DE16, HL16, SP16, INVALID_REG))
+ {
+ PCWrite(0xed);
+ PCWrite(0x4a | register_bitmask[r2] << 4);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus SUB(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xd6, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0x96}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* SUB A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0x90 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus SBC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xde, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0x9e}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* SBC A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0x98 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ /* SBC HL,rr
+ */
+ if (r1 == HL16 && IsAnyOf(r2, BC16, DE16, HL16, SP16, INVALID_REG))
+ {
+ PCWrite(0xed);
+ PCWrite(0x42 | register_bitmask[r2] << 4);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus AND(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xe6, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0xa6}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* AND A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0xa0 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus OR(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xf6, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0xb6}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* OR A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0xb0 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus XOR(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xee, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0xae}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* XOR A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0xa8 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus CP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static RegisterPairCodes codes[] =
+ {
+ A8, VALUE, {0xfe, WRITE_BYTE_RHS},
+ A8, HL_ADDRESS, {0xbe}
+ };
+
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+
+ if (AssumeAccumulator(argc, argv, quoted, err, errsize,
+ &r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
+ {
+ return CMD_FAILED;
+ }
+
+ /* CP A,r
+ */
+ if (r1 == A8 && IsNormal8Bit(t2))
+ {
+ PCWrite(0xb8 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
+ r1, r2, off1, off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ return CMD_OK;
+}
+
+
+static CommandStatus INC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ RegisterMode r1;
+ RegisterType t1;
+ int off1;
+
+ CMD_ARGC_CHECK(2);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ /* INC r
+ */
+ if (IsNormal8Bit(t1))
+ {
+ PCWrite(0x04 | register_bitmask[r1] << 3);
+ return CMD_OK;
+ }
+
+ /* INC (HL)
+ */
+ if (r1 == HL_ADDRESS)
+ {
+ PCWrite(0x34);
+ return CMD_OK;
+ }
+
+ /* INC rr
+ */
+ if (Is16Bit(t1) && !IsMemory(t1))
+ {
+ PCWrite(0x03 | register_bitmask[r1] << 4);
+ return CMD_OK;
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus DEC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ RegisterMode r1;
+ RegisterType t1;
+ int off1;
+
+ CMD_ARGC_CHECK(2);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ /* DEC r
+ */
+ if (IsNormal8Bit(t1))
+ {
+ PCWrite(0x05 | register_bitmask[r1] << 3);
+ return CMD_OK;
+ }
+
+ /* DEC (HL)
+ */
+ if (r1 == HL_ADDRESS)
+ {
+ PCWrite(0x35);
+ return CMD_OK;
+ }
+
+ /* DEC rr
+ */
+ if (Is16Bit(t1) && !IsMemory(t1))
+ {
+ PCWrite(0x0b | register_bitmask[r1] << 4);
+ return CMD_OK;
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus RLC_RL_SLA_SWAP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ RegisterMode r1;
+ RegisterType t1;
+ int off1;
+ int opcode_mask;
+
+ CMD_ARGC_CHECK(2);
+
+ if (CompareString(argv[0], "RLC"))
+ {
+ opcode_mask = 0x00;
+ }
+ else if (CompareString(argv[0], "RL"))
+ {
+ opcode_mask = 0x10;
+ }
+ else if (CompareString(argv[0], "SLA"))
+ {
+ opcode_mask = 0x20;
+ }
+ else if (CompareString(argv[0], "SWAP"))
+ {
+ opcode_mask = 0x30;
+ }
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ /* Normal opcodes
+ */
+ if (argc == 2)
+ {
+ /* OP r
+ */
+ if (IsNormal8Bit(t1))
+ {
+ PCWrite(0xcb);
+ PCWrite(opcode_mask | register_bitmask[r1]);
+ return CMD_OK;
+ }
+
+ /* OP (HL)
+ */
+ if (r1 == HL_ADDRESS)
+ {
+ PCWrite(0xcb);
+ PCWrite(opcode_mask | 0x06);
+ return CMD_OK;
+ }
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus BIT_SET_RES(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ RegisterMode r1, r2;
+ RegisterType t1, t2;
+ int off1, off2;
+ int opcode_mask;
+
+ CMD_ARGC_CHECK(3);
+
+ if (CompareString(argv[0], "BIT"))
+ {
+ opcode_mask = 0x40;
+ }
+ else if (CompareString(argv[0], "SET"))
+ {
+ opcode_mask = 0xc0;
+ }
+ else if (CompareString(argv[0], "RES"))
+ {
+ opcode_mask = 0x80;
+ }
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (r1 != VALUE || (off1 < 0 || off1 > 7))
+ {
+ snprintf(err, errsize, "%s: illegal value %s for bit number",
+ argv[0], argv[1]);
+ return CMD_FAILED;
+ }
+
+ /* Normal opcodes
+ */
+ if (argc == 3)
+ {
+ /* OP b,r
+ */
+ if (IsNormal8Bit(t2))
+ {
+ PCWrite(0xcb);
+ PCWrite(opcode_mask | off1 << 3 | register_bitmask[r2]);
+ return CMD_OK;
+ }
+
+ /* OP b,(HL)
+ */
+ if (r2 == HL_ADDRESS)
+ {
+ PCWrite(0xcb);
+ PCWrite(opcode_mask | off1 << 3 | 0x06);
+ return CMD_OK;
+ }
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus JP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ if (argc == 2)
+ {
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE)
+ {
+ PCWrite(0xc3);
+ PCWriteWord(val);
+ return CMD_OK;
+ }
+
+ if (mode == HL_ADDRESS)
+ {
+ PCWrite(0xe9);
+ return CMD_OK;
+ }
+ }
+ else if (argc == 3)
+ {
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+ ProcessorFlag flag;
+ int mask;
+
+ if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize) ||
+ !CalcRegisterMode(argv[2], quoted[2], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE)
+ {
+ PCWrite(0xc2 | mask << 3);
+ PCWriteWord(val);
+ return CMD_OK;
+ }
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus JR(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ if (argc == 2)
+ {
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE)
+ {
+ int rel;
+
+ rel = val - ((PC() + 2) % 0x10000);
+
+ CheckOffset(argv[1], rel);
+
+ PCWrite(0x18);
+ PCWrite(rel);
+ return CMD_OK;
+ }
+ }
+ else if (argc == 3)
+ {
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+ ProcessorFlag flag;
+ int mask;
+
+ if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize) ||
+ !CalcRegisterMode(argv[2], quoted[2], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE && (flag >= NZ_FLAG && flag <= C_FLAG))
+ {
+ int rel;
+
+ rel = val - ((PC() + 2) % 0x10000);
+
+ CheckOffset(argv[2], rel);
+
+ PCWrite(0x20 | mask << 3);
+ PCWrite(rel);
+ return CMD_OK;
+ }
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus CALL(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ if (argc == 2)
+ {
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE)
+ {
+ PCWrite(0xcd);
+ PCWriteWord(val);
+ return CMD_OK;
+ }
+ }
+ else if (argc == 3)
+ {
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+ ProcessorFlag flag;
+ int mask;
+
+ if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize) ||
+ !CalcRegisterMode(argv[2], quoted[2], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE)
+ {
+ PCWrite(0xc4 | mask << 3);
+ PCWriteWord(val);
+ return CMD_OK;
+ }
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus RET(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ if (argc == 1)
+ {
+ PCWrite(0xc9);
+ return CMD_OK;
+ }
+ else if (argc == 2)
+ {
+ ProcessorFlag flag;
+ int mask;
+
+ if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ PCWrite(0xc0 | mask << 3);
+ return CMD_OK;
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+static CommandStatus RST(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ static struct
+ {
+ int dec;
+ int hex;
+ } rst_arg[] =
+ {
+ {0, 0x00},
+ {8, 0x08},
+ {10, 0x10},
+ {18, 0x18},
+ {20, 0x20},
+ {28, 0x28},
+ {30, 0x30},
+ {38, 0x38},
+ {-1}
+ };
+
+ RegisterMode mode;
+ RegisterType type;
+ int val;
+
+ CMD_ARGC_CHECK(2);
+
+ if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
+ err, errsize))
+ {
+ return CMD_FAILED;
+ }
+
+ if (mode == VALUE)
+ {
+ int f;
+ int mask = -1;
+
+ for(f = 0; rst_arg[f].dec != -1; f++)
+ {
+ if (rst_arg[f].dec == val || rst_arg[f].hex == val)
+ {
+ mask = f << 3;
+ }
+ }
+
+ if (mask != -1)
+ {
+ PCWrite(0xc7 | mask);
+ return CMD_OK;
+ }
+ }
+
+ return IllegalArgs(argc, argv, quoted, err, errsize);
+}
+
+
+/* ---------------------------------------- OPCODE TABLES
+*/
+typedef struct
+{
+ const char *op;
+ int code[3]; /* Zero ends, but code[0] always used. */
+} OpcodeTable;
+
+typedef struct
+{
+ const char *op;
+ Command cmd;
+} HandlerTable;
+
+
+static const HandlerTable handler_table[] =
+{
+ {"LD", LD},
+ {"LDH", LDH},
+ {"LDD", LDD},
+ {"LDI", LDI},
+ {"PUSH", PUSH},
+ {"POP", POP},
+ {"ADD", ADD},
+ {"ADC", ADC},
+ {"SUB", SUB},
+ {"SBC", SBC},
+ {"AND", AND},
+ {"OR", OR},
+ {"XOR", XOR},
+ {"EOR", XOR},
+ {"CP", CP},
+ {"INC", INC},
+ {"DEC", DEC},
+ {"RLC", RLC_RL_SLA_SWAP},
+ {"RL", RLC_RL_SLA_SWAP},
+ {"SLA", RLC_RL_SLA_SWAP},
+ {"SWAP", RLC_RL_SLA_SWAP},
+ {"BIT", BIT_SET_RES},
+ {"RES", BIT_SET_RES},
+ {"SET", BIT_SET_RES},
+ {"JP", JP},
+ {"JR", JR},
+ {"CALL", CALL},
+ {"RET", RET},
+ {"RST", RST},
+ {NULL}
+};
+
+
+static const OpcodeTable implied_opcodes[] =
+{
+ {"NOP", {0x00}},
+ {"DI", {0xf3}},
+ {"EI", {0xfb}},
+ {"HALT", {0x76}},
+ {"HLT", {0x76}},
+ {"DAA", {0x27}},
+ {"CPL", {0x2f}},
+ {"SCF", {0x37}},
+ {"CCF", {0x3f}},
+ {"RLCA", {0x07}},
+ {"RRCA", {0x0f}},
+ {"RLA", {0x17}},
+ {"RRA", {0x1f}},
+ {"RETI", {0xd9}},
+ {"STOP", {0x10}},
+ {NULL}
+};
+
+
+/* ---------------------------------------- PUBLIC INTERFACES
+*/
+
+void Init_GBCPU(void)
+{
+}
+
+
+const ValueTable *Options_GBCPU(void)
+{
+ return NULL;
+}
+
+
+CommandStatus SetOption_GBCPU(int opt, int argc, char *argv[], int quoted[],
+ char *err, size_t errsize)
+{
+ return CMD_OK;
+}
+
+
+CommandStatus Handler_GBCPU(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ int f;
+
+ /* Check for simple (implied addressing) opcodes
+ */
+ for(f = 0; implied_opcodes[f].op; f++)
+ {
+ if (CompareString(argv[0], implied_opcodes[f].op))
+ {
+ int r;
+
+ PCWrite(implied_opcodes[f].code[0]);
+
+ for(r = 1; implied_opcodes[f].code[r]; r++)
+ {
+ PCWrite(implied_opcodes[f].code[r]);
+ }
+
+ return CMD_OK;
+ }
+ }
+
+ /* Check for other opcodes
+ */
+ for(f = 0; handler_table[f].op; f++)
+ {
+ if (CompareString(argv[0], handler_table[f].op))
+ {
+ return handler_table[f].cmd(label, argc, argv,
+ quoted, err, errsize);;
+ }
+ }
+
+ return CMD_NOT_KNOWN;
+}
+
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/
diff --git a/src/gbcpu.h b/src/gbcpu.h
new file mode 100644
index 0000000..68b0d7c
--- /dev/null
+++ b/src/gbcpu.h
@@ -0,0 +1,46 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.co.uk)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ Gameboy Z80-alike Assembler
+
+*/
+
+#ifndef CASM_GBCPU_H
+#define CASM_GBCPU_H
+
+#include "parse.h"
+
+void Init_GBCPU(void);
+
+const ValueTable *Options_GBCPU(void);
+
+CommandStatus SetOption_GBCPU(int opt, int argc, char *argv[], int quoted[],
+ char *err, size_t errsize);
+
+CommandStatus Handler_GBCPU(const char *label, int argc, char *argv[],
+ int quoted[], char *error, size_t error_size);
+
+
+#endif
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/
diff --git a/src/gbout.c b/src/gbout.c
new file mode 100644
index 0000000..8fa3e7a
--- /dev/null
+++ b/src/gbout.c
@@ -0,0 +1,432 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.co.uk)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ Gameboy ROM output handler.
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "global.h"
+#include "expr.h"
+#include "codepage.h"
+#include "gbout.h"
+
+
+/* ---------------------------------------- MACROS & TYPES
+*/
+
+enum option_t
+{
+ OPT_COLOUR,
+ OPT_SUPER,
+ OPT_CART_RAM,
+ OPT_CART_TYPE,
+ OPT_RST,
+ OPT_IRQ
+};
+
+static const ValueTable option_set[] =
+{
+ {"gameboy-colour", OPT_COLOUR},
+ {"gameboy-color", OPT_COLOUR},
+ {"gameboy-super", OPT_SUPER},
+ {"gameboy-cart-ram", OPT_CART_RAM},
+ {"gameboy-cart-type", OPT_CART_RAM},
+ {"gameboy-irq", OPT_IRQ},
+ {NULL}
+};
+
+typedef enum
+{
+ IRQ_VBLANK,
+ IRQ_LCD,
+ IRQ_TIMER,
+ IRQ_SERIAL,
+ IRQ_JOYPAD
+} IRQ_Type;
+
+static ValueTable irq_table[] =
+{
+ {"vbl", IRQ_VBLANK},
+ {"lcd", IRQ_LCD},
+ {"timer", IRQ_TIMER},
+ {"serial", IRQ_SERIAL},
+ {"joypad", IRQ_JOYPAD},
+ {NULL}
+};
+
+static struct
+{
+ int is_colour;
+ int is_super;
+ int cart_ram;
+ int cart_type;
+ int irq_vector[5];
+} option =
+{
+ FALSE, FALSE, -1, -1,
+ {-1, -1, -1, -1, -1}
+};
+
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+
+static int PokeB(Byte *mem, int addr, Byte b)
+{
+ mem[addr++] = b;
+ return (addr % 0x10000);
+}
+
+
+static int PokeW(Byte *mem, int addr, int w)
+{
+ addr = PokeB(mem, addr, w & 0xff);
+ return PokeB(mem, addr, (w & 0xff00) >> 8);
+}
+
+
+static int PokeS(Byte *mem, int addr, const char *str, int maxlen)
+{
+ while(*str && maxlen--)
+ {
+ addr = PokeB(mem, addr, CodeFromNative(CP_ASCII, *str++));
+ }
+
+ return addr;
+}
+
+
+static int PokeCode(Byte *mem, int addr, const int *code)
+{
+ while(*code != -1)
+ {
+ addr = PokeB(mem, addr, *code++);
+ }
+
+ return addr;
+}
+
+
+
+/* ---------------------------------------- INTERFACES
+*/
+const ValueTable *GBOutputOptions(void)
+{
+ return option_set;
+}
+
+CommandStatus GBOutputSetOption(int opt, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
+{
+ const ValueTable *val;
+ int f;
+
+ CMD_ARGC_CHECK(1);
+
+ switch(opt)
+ {
+ case OPT_COLOUR:
+ option.is_colour = ParseTrueFalse(argv[0], TRUE);
+ break;
+
+ case OPT_SUPER:
+ option.is_super = ParseTrueFalse(argv[0], TRUE);
+ break;
+
+ case OPT_CART_RAM:
+ CMD_EXPR(argv[0], option.cart_ram);
+ break;
+
+ case OPT_CART_TYPE:
+ CMD_EXPR(argv[0], option.cart_type);
+ break;
+
+ case OPT_IRQ:
+ CMD_ARGC_CHECK(2);
+ CMD_TABLE(argv[0], irq_table, val);
+ CMD_EXPR(argv[1], f);
+ option.irq_vector[val->value] = f;
+ break;
+
+ default:
+ break;
+ }
+
+ return CMD_OK;
+
+}
+
+int GBOutput(const char *filename, const char *filename_bank,
+ MemoryBank **bank, int count, char *error, size_t error_size)
+{
+ static const int logo[] =
+ {
+ 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00,
+ 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89,
+ 0x00, 0x0E, 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB,
+ 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F,
+ 0xBB, 0xB9, 0x33, 0x3E, -1
+ };
+
+ FILE *fp = fopen(filename, "wb");
+ int f;
+ int offset;
+ int rom_size;
+ unsigned global_csum = 0;
+ Byte hdr_csum = 0;
+ Byte *mem;
+
+ if (!fp)
+ {
+ snprintf(error, error_size, "Failed to create %s", filename);
+ return FALSE;
+ }
+
+ /* Check bank sizes and layouts
+ */
+ if (count == 1 && (bank[0]->min_address_used < 0x150 ||
+ bank[0]->max_address_used > 0x7fff))
+ {
+ snprintf(error, error_size, "A simple ROM must be in the address "
+ "space 0x150 to 0x7fff");
+ return FALSE;
+ }
+
+ if (count > 1 && (bank[0]->min_address_used < 0x150 ||
+ bank[0]->max_address_used > 0x3fff))
+ {
+ snprintf(error, error_size, "Bank zero of a banked ROM must be in the "
+ "address space 0x150 to 0x3fff");
+ return FALSE;
+ }
+
+ for(f = 1 ; f < count; f++)
+ {
+ if (bank[f]->min_address_used < 0x4000 ||
+ bank[f]->max_address_used > 0x7fff)
+ {
+ snprintf(error, error_size,
+ "Bank %u must be in the address space "
+ "0x4000 to 0x7fff", bank[f]->number);
+ return FALSE;
+ }
+ }
+
+ if (option.cart_type == -1)
+ {
+ if (count == 1)
+ {
+ option.cart_type = 0;
+ }
+ else
+ {
+ option.cart_type = 1;
+ }
+
+ if (option.cart_ram != -1)
+ {
+ option.cart_type = 3;
+ }
+ }
+
+ if (count == 1)
+ {
+ rom_size = 0;
+ }
+ else
+ {
+ rom_size = (count / 4) + 1;
+ }
+
+ mem = bank[0]->memory;
+
+ /* Create the RST handlers
+ */
+ for(f = 0; f < 8; f++)
+ {
+ int addr = f * 8;
+
+ addr = PokeB(mem, addr, 0xc3);
+ addr = PokeW(mem, addr, 0x100);
+ }
+
+ /* Create the IRQ handlers
+ */
+ for(f = 0; f < 5; f++)
+ {
+ int addr = (f * 8) + 0x40;
+
+ if (option.irq_vector[f] == -1)
+ {
+ addr = PokeB(mem, addr, 0xd9);
+ }
+ else
+ {
+ addr = PokeB(mem, addr, 0xc3);
+ addr = PokeW(mem, addr, option.irq_vector[f]);
+ }
+ }
+
+ /* Create the entry point
+ */
+ PokeB(mem, 0x100, 0);
+ PokeB(mem, 0x101, 0xc3);
+ PokeW(mem, 0x102, bank[0]->min_address_used);
+
+ /* Title (new smaller size)
+ */
+ PokeS(mem, 0x134, filename, 11);
+
+ /* GBC flag
+ */
+ if (option.is_colour)
+ {
+ PokeB(mem, 0x143, 0xc0);
+ }
+
+ /* Super Gameboy flag
+ */
+ if (option.is_super)
+ {
+ PokeB(mem, 0x146, 3);
+ }
+
+ /* Type/ROM size
+ */
+ PokeB(mem, 0x147, option.cart_type);
+ PokeB(mem, 0x148, rom_size);
+
+ /* RAM size
+ */
+ switch(option.cart_ram)
+ {
+ case 2:
+ PokeB(mem, 0x149, 1);
+ break;
+
+ case 8:
+ PokeB(mem, 0x149, 2);
+ break;
+
+ case 32:
+ PokeB(mem, 0x149, 3);
+ break;
+
+ case 128:
+ PokeB(mem, 0x149, 4);
+ break;
+
+ case 64:
+ PokeB(mem, 0x149, 5);
+ break;
+
+ default:
+ PokeB(mem, 0x149, 0);
+ break;
+ }
+
+ /* To be sold everywhere
+ */
+ PokeB(mem, 0x14a, 1);
+
+ /* Old licensee
+ */
+ if (option.is_super)
+ {
+ PokeB(mem, 0x14a, 0x33);
+ }
+ else
+ {
+ PokeB(mem, 0x14a, 0);
+ }
+
+ /* Header checksum
+ */
+ for(f = 0x134 ; f < 0x14d; f++)
+ {
+ hdr_csum -= mem[f];
+ }
+
+ PokeB(mem, 0x14d, hdr_csum);
+
+ /* Global checksum
+ */
+ if (count == 1)
+ {
+ for(f = 0x150; f < 0x8000; f++)
+ {
+ global_csum += mem[f];
+ }
+ }
+ else
+ {
+ int r;
+
+ for(f = 0x150; f < 0x4000; f++)
+ {
+ global_csum += mem[f];
+ }
+
+ for(r = 1; r < count; r++)
+ {
+ for(f = 0x4000; f < 0x8000; f++)
+ {
+ global_csum += bank[r]->memory[f];
+ }
+ }
+ }
+
+ PokeW(mem, 0x14e, global_csum);
+
+ /* Output the ROM contents
+ */
+ if (count == 1)
+ {
+ fwrite(mem, 0x8000, 1, fp);
+ }
+ else
+ {
+ int r;
+
+ fwrite(mem, 0x4000, 1, fp);
+
+ for(r = 1; r < count; r++)
+ {
+ for(f = 0x4000; f < 0x8000; f++)
+ {
+ fwrite(bank[r]->memory + 0x4000, 0x4000, 1, fp);
+ }
+ }
+ }
+
+
+ fclose(fp);
+
+ return TRUE;
+}
+
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/
diff --git a/src/gbout.h b/src/gbout.h
new file mode 100644
index 0000000..9140844
--- /dev/null
+++ b/src/gbout.h
@@ -0,0 +1,56 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.co.uk)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ Gameboy ROM output
+
+*/
+
+#ifndef CASM_GBOUT_H
+#define CASM_GBOUT_H
+
+#include "parse.h"
+#include "state.h"
+#include "cmd.h"
+
+/* ---------------------------------------- INTERFACES
+*/
+
+
+/* RAW Output options
+*/
+const ValueTable *GBOutputOptions(void);
+
+CommandStatus GBOutputSetOption(int opt, int argc, char *argv[],
+ int quoted[], char *error,
+ size_t error_size);
+
+
+/* Gameboy ROM output of assembly. Returns TRUE if OK, FALSE for failure.
+*/
+int GBOutput(const char *filename, const char *filename_bank,
+ MemoryBank **bank, int count,
+ char *error, size_t error_size);
+
+#endif
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/
diff --git a/src/output.c b/src/output.c
index 8476f9d..7d42565 100644
--- a/src/output.c
+++ b/src/output.c
@@ -51,7 +51,8 @@ typedef enum
RAW,
TAP,
T64,
- ZX81
+ ZX81,
+ GAMEBOY
} Format;
static char output[4096] = "output";
@@ -65,6 +66,7 @@ static ValueTable format_table[] =
{"spectrum", TAP},
{"t64", T64},
{"zx81", ZX81},
+ {"gameboy", GAMEBOY},
{NULL}
};
@@ -142,6 +144,10 @@ int OutputCode(void)
return ZX81Output(output, output_bank, bank, count,
error, sizeof error);
+ case GAMEBOY:
+ return GBOutput(output, output_bank, bank, count,
+ error, sizeof error);
+
default:
break;
}
diff --git a/src/output.h b/src/output.h
index 8ea1c1d..1069fed 100644
--- a/src/output.h
+++ b/src/output.h
@@ -35,6 +35,7 @@
#include "specout.h"
#include "t64out.h"
#include "zx81out.h"
+#include "gbout.h"
/* ---------------------------------------- INTERFACES
*/
diff --git a/src/z80.c b/src/z80.c
index 71bda75..5cb003c 100644
--- a/src/z80.c
+++ b/src/z80.c
@@ -811,11 +811,11 @@ static int WriteRegisterPairModes(const RegisterPairCodes *codes, size_t count,
/* Assume accumulator if only one argument
*/
-CommandStatus AssumeAccumulator(int argc, char *argv[], int quoted[],
- char *err, size_t errsize,
- RegisterMode *r1, RegisterMode *r2,
- RegisterType *t1, RegisterType *t2,
- int *off1, int *off2)
+static CommandStatus AssumeAccumulator(int argc, char *argv[], int quoted[],
+ char *err, size_t errsize,
+ RegisterMode *r1, RegisterMode *r2,
+ RegisterType *t1, RegisterType *t2,
+ int *off1, int *off2)
{
CMD_ARGC_CHECK(2);
@@ -933,8 +933,8 @@ static CommandStatus IllegalArgs(int argc, char *argv[], int quoted[],
/* ---------------------------------------- COMMAND HANDLERS
*/
-CommandStatus LD(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus LD(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1124,8 +1124,8 @@ CommandStatus LD(const char *label, int argc, char *argv[],
}
-CommandStatus PUSH(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus PUSH(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
@@ -1162,8 +1162,8 @@ CommandStatus PUSH(const char *label, int argc, char *argv[],
}
-CommandStatus POP(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus POP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
@@ -1200,8 +1200,8 @@ CommandStatus POP(const char *label, int argc, char *argv[],
}
-CommandStatus EX(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus EX(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1243,8 +1243,8 @@ CommandStatus EX(const char *label, int argc, char *argv[],
}
-CommandStatus ADD(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus ADD(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1309,8 +1309,8 @@ CommandStatus ADD(const char *label, int argc, char *argv[],
}
-CommandStatus ADC(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus ADC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1358,8 +1358,8 @@ CommandStatus ADC(const char *label, int argc, char *argv[],
}
-CommandStatus SUB(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus SUB(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1398,8 +1398,8 @@ CommandStatus SUB(const char *label, int argc, char *argv[],
}
-CommandStatus SBC(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus SBC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1447,8 +1447,8 @@ CommandStatus SBC(const char *label, int argc, char *argv[],
}
-CommandStatus AND(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus AND(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1487,8 +1487,8 @@ CommandStatus AND(const char *label, int argc, char *argv[],
}
-CommandStatus OR(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus OR(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1527,8 +1527,8 @@ CommandStatus OR(const char *label, int argc, char *argv[],
}
-CommandStatus XOR(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus XOR(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1567,8 +1567,8 @@ CommandStatus XOR(const char *label, int argc, char *argv[],
}
-CommandStatus CP(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus CP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
@@ -1607,8 +1607,8 @@ CommandStatus CP(const char *label, int argc, char *argv[],
}
-CommandStatus IM(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus IM(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static int im[3] = {0x46, 0x56, 0x5e};
RegisterMode r1;
@@ -1635,8 +1635,8 @@ CommandStatus IM(const char *label, int argc, char *argv[],
}
-CommandStatus INC(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus INC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
@@ -1681,8 +1681,8 @@ CommandStatus INC(const char *label, int argc, char *argv[],
}
-CommandStatus DEC(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus DEC(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
@@ -1727,8 +1727,9 @@ CommandStatus DEC(const char *label, int argc, char *argv[],
}
-CommandStatus RLC_RL_RRC_RR_ETC(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus RLC_RL_RRC_RR_ETC(const char *label,
+ int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
@@ -1830,8 +1831,8 @@ CommandStatus RLC_RL_RRC_RR_ETC(const char *label, int argc, char *argv[],
}
-CommandStatus BIT_SET_RES(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus BIT_SET_RES(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1, r2;
RegisterType t1, t2;
@@ -1926,8 +1927,8 @@ CommandStatus BIT_SET_RES(const char *label, int argc, char *argv[],
}
-CommandStatus JP(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus JP(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
if (argc == 2)
{
@@ -1982,8 +1983,8 @@ CommandStatus JP(const char *label, int argc, char *argv[],
}
-CommandStatus JR(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus JR(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
if (argc == 2)
{
@@ -2043,8 +2044,8 @@ CommandStatus JR(const char *label, int argc, char *argv[],
}
-CommandStatus DJNZ(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus DJNZ(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode mode;
RegisterType type;
@@ -2076,8 +2077,8 @@ CommandStatus DJNZ(const char *label, int argc, char *argv[],
}
-CommandStatus CALL(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus CALL(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
if (argc == 2)
{
@@ -2125,8 +2126,8 @@ CommandStatus CALL(const char *label, int argc, char *argv[],
}
-CommandStatus RET(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus RET(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
if (argc == 1)
{
@@ -2151,8 +2152,8 @@ CommandStatus RET(const char *label, int argc, char *argv[],
}
-CommandStatus RST(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus RST(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
static struct
{
@@ -2207,8 +2208,8 @@ CommandStatus RST(const char *label, int argc, char *argv[],
}
-CommandStatus IN(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus IN(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1, r2;
RegisterType t1, t2;
@@ -2255,8 +2256,8 @@ CommandStatus IN(const char *label, int argc, char *argv[],
}
-CommandStatus OUT(const char *label, int argc, char *argv[],
- int quoted[], char *err, size_t errsize)
+static CommandStatus OUT(const char *label, int argc, char *argv[],
+ int quoted[], char *err, size_t errsize)
{
RegisterMode r1, r2;
RegisterType t1, t2;