From 24ebb224a11d31bfe039352fd29d2b89368583ac Mon Sep 17 00:00:00 2001 From: Ian C Date: Fri, 15 Apr 2016 15:58:09 +0100 Subject: Started on Gameboy support. --- src/Makefile | 42 +- src/casm.c | 13 +- src/example/Makefile | 5 +- src/example/gb.asm | 92 +++ src/gbcpu.c | 1928 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/gbcpu.h | 46 ++ src/gbout.c | 432 +++++++++++ src/gbout.h | 56 ++ src/output.c | 8 +- src/output.h | 1 + src/z80.c | 111 +-- 11 files changed, 2659 insertions(+), 75 deletions(-) create mode 100644 src/example/gb.asm create mode 100644 src/gbcpu.c create mode 100644 src/gbcpu.h create mode 100644 src/gbout.c create mode 100644 src/gbout.h (limited to 'src') 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 . + + ------------------------------------------------------------------------- + + GBCPU Assembler + +*/ +#include +#include +#include + +#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 . + + ------------------------------------------------------------------------- + + 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 . + + ------------------------------------------------------------------------- + + Gameboy ROM output handler. + +*/ +#include +#include +#include + +#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 . + + ------------------------------------------------------------------------- + + 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; -- cgit v1.2.3