/* casm - Simple, portable assembler Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.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 . ------------------------------------------------------------------------- Z80 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 "z80.h" /* ---------------------------------------- TYPES AND GLOBALS */ typedef enum { A8, B8, C8, D8, E8, H8, L8, F8, IXH8, IXL8, IYH8, IYL8, I8, R8, AF16, AF16_ALT, BC16, DE16, HL16, SP16, IX16, IY16, BC_ADDRESS, DE_ADDRESS, HL_ADDRESS, SP_ADDRESS, IX_ADDRESS, IY_ADDRESS, IX_OFFSET, IY_OFFSET, C_PORT, ADDRESS, VALUE, INVALID_REG } RegisterMode; typedef enum { NZ_FLAG, Z_FLAG, NC_FLAG, C_FLAG, PO_FLAG, PE_FLAG, P_FLAG, M_FLAG } ProcessorFlag; typedef enum { IS_NORMAL_8_BIT = 0x001, IS_SPECIAL_8_BIT = 0x002, IS_16_BIT = 0x004, IS_MEMORY = 0x008, IS_INDEX_X = 0x010, IS_INDEX_Y = 0x020, IS_SP = 0x040, IS_VALUE = 0x080, IS_SPECIAL_16_BIT = 0x100, IS_ALTERNATE = 0x200, IS_IO_PORT = 0x400 } 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 IsIndexX(t) ((t) & IS_INDEX_X) #define IsIndexY(t) ((t) & IS_INDEX_Y) #define IsIndex(t) (IsIndexX(t) || IsIndexY(t)) #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 IsAlternate(t) ((t) & IS_ALTERNATE) #define SHIFT_IX 0xdd #define SHIFT_IY 0xfd #define WriteShift(r) \ do \ { \ switch(r) \ { \ case IXH8: \ case IXL8: \ case IX16: \ case IX_OFFSET: \ case IX_ADDRESS: \ PCWrite(SHIFT_IX); \ break; \ case IYH8: \ case IYL8: \ case IY16: \ case IY_OFFSET: \ case IY_ADDRESS: \ PCWrite(SHIFT_IY); \ break; \ default: \ break; \ } \ } while(0) #define WriteEitherShift(r1,r2) \ do \ { \ int h_handled = FALSE; \ switch(r1) \ { \ case IXH8: \ case IXL8: \ case IX16: \ case IX_OFFSET: \ case IX_ADDRESS: \ PCWrite(SHIFT_IX); \ h_handled = TRUE; \ break; \ case IYH8: \ case IYL8: \ case IY16: \ case IY_OFFSET: \ case IY_ADDRESS: \ PCWrite(SHIFT_IY); \ h_handled = TRUE; \ break; \ default: \ break; \ } \ if (!h_handled) \ switch(r2) \ { \ case IXH8: \ case IXL8: \ case IX16: \ case IX_OFFSET: \ case IX_ADDRESS: \ PCWrite(SHIFT_IX); \ h_handled = TRUE; \ break; \ case IYH8: \ case IYL8: \ case IY16: \ case IY_OFFSET: \ case IY_ADDRESS: \ PCWrite(SHIFT_IY); \ h_handled = TRUE; \ break; \ default: \ break; \ } \ } while(0) #define WriteOffset(r,o) \ do \ { \ switch(r) \ { \ case IX_OFFSET: \ case IY_OFFSET: \ PCWrite(o); \ break; \ default: \ break; \ } \ } while(0) #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 */ 0x04, /* PO_FLAG */ 0x05, /* PE_FLAG */ 0x06, /* P_FLAG */ 0x07 /* M_FLAG */ }; static const char *flag_text[] = { "NZ", /* NZ_FLAG */ "Z", /* Z_FLAG */ "NC", /* NC_FLAG */ "C", /* C_FLAG */ "PO", /* PO_FLAG */ "PE", /* PE_FLAG */ "P", /* P_FLAG */ "M", /* M_FLAG */ NULL }; static const char *register_mode_name[] = { "A", "B", "C", "D", "E", "H", "L", "F", "IXH", "IXL", "IYH", "IYL", "I", "R", "AF", "AF'", "BC", "DE", "HL", "SP", "IX", "IY", "(BC)", "(DE)", "(HL)", "(SP)", "(IX)", "(IY)", "(IX+offset)", "(IY+offset)", "(C)", "(address)", "value", 0 }; static const int register_bitmask[] = { 0x7, /* A8 */ 0x0, /* B8 */ 0x1, /* C8 */ 0x2, /* D8 */ 0x3, /* E8 */ 0x4, /* H8 */ 0x5, /* L8 */ 0x6, /* F8 */ 0x4, /* IXH8 - In effect H */ 0x5, /* IXL8 - In effect L */ 0x4, /* IYH8 - In effect H */ 0x5, /* IYL8 - In effect L */ -1, /* I8 */ -1, /* R8 */ 0x3, /* AF16 */ -1, /* AF16_ALT */ 0x0, /* BC16 */ 0x1, /* DE16 */ 0x2, /* HL16 */ 0x3, /* SP16 */ 0x2, /* IX16 - In effect HL */ 0x2, /* IY16 - In effect HL */ 0x0, /* BC_ADDRESS */ 0x0, /* DE_ADDRESS */ 0x0, /* HL_ADDRESS */ 0x0, /* SP_ADDRESS */ 0x0, /* IX_ADDRESS */ 0x0, /* IY_ADDRESS */ 0x0, /* IX_OFFSET */ 0x0, /* IY_OFFSET */ 0x0, /* C_PORT */ 0x0, /* ADDRESS */ 0x0, /* VALUE */ }; typedef struct { RegisterMode mode; int quote; int starts_with; int take_offset; int take_value; const char *ident; RegisterType type; } RegisterModeTable; static RegisterModeTable register_mode_table[] = { { A8, 0, FALSE, FALSE, FALSE, "A", IS_NORMAL_8_BIT }, { B8, 0, FALSE, FALSE, FALSE, "B", IS_NORMAL_8_BIT }, { C8, 0, FALSE, FALSE, FALSE, "C", IS_NORMAL_8_BIT }, { D8, 0, FALSE, FALSE, FALSE, "D", IS_NORMAL_8_BIT }, { E8, 0, FALSE, FALSE, FALSE, "E", IS_NORMAL_8_BIT }, { H8, 0, FALSE, FALSE, FALSE, "H", IS_NORMAL_8_BIT }, { L8, 0, FALSE, FALSE, FALSE, "L", IS_NORMAL_8_BIT }, { F8, 0, FALSE, FALSE, FALSE, "F", IS_SPECIAL_8_BIT }, { IXL8, 0, FALSE, FALSE, FALSE, "IXL", IS_NORMAL_8_BIT|IS_INDEX_X }, { IXH8, 0, FALSE, FALSE, FALSE, "IXH", IS_NORMAL_8_BIT|IS_INDEX_X }, { IYL8, 0, FALSE, FALSE, FALSE, "IYL", IS_NORMAL_8_BIT|IS_INDEX_Y }, { IYH8, 0, FALSE, FALSE, FALSE, "IYH", IS_NORMAL_8_BIT|IS_INDEX_Y }, { I8, 0, FALSE, FALSE, FALSE, "I", IS_SPECIAL_8_BIT }, { R8, 0, FALSE, FALSE, FALSE, "R", IS_SPECIAL_8_BIT }, { AF16, 0, FALSE, FALSE, FALSE, "AF", IS_SPECIAL_16_BIT }, { AF16_ALT, 0, FALSE, FALSE, FALSE, "AF'", IS_SPECIAL_16_BIT|IS_ALTERNATE }, { BC16, 0, FALSE, FALSE, FALSE, "BC", IS_16_BIT }, { DE16, 0, FALSE, FALSE, FALSE, "DE", IS_16_BIT }, { HL16, 0, FALSE, FALSE, FALSE, "HL", IS_16_BIT }, { IX16, 0, FALSE, FALSE, FALSE, "IX", IS_16_BIT|IS_INDEX_X }, { IY16, 0, FALSE, FALSE, FALSE, "IY", IS_16_BIT|IS_INDEX_Y }, { SP16, 0, FALSE, FALSE, FALSE, "SP", IS_16_BIT|IS_SP }, { BC_ADDRESS, '(', FALSE, FALSE, FALSE, "BC", IS_16_BIT|IS_MEMORY }, { DE_ADDRESS, '(', FALSE, FALSE, FALSE, "DE", IS_16_BIT|IS_MEMORY }, { HL_ADDRESS, '(', FALSE, FALSE, FALSE, "HL", IS_16_BIT|IS_MEMORY }, { IX_ADDRESS, '(', FALSE, FALSE, FALSE, "IX", IS_16_BIT|IS_MEMORY|IS_INDEX_X }, { IY_ADDRESS, '(', FALSE, FALSE, FALSE, "IY", IS_16_BIT|IS_MEMORY|IS_INDEX_Y }, { IX_OFFSET, '(', TRUE, TRUE, FALSE, "IX", IS_16_BIT|IS_MEMORY|IS_INDEX_X }, { IY_OFFSET, '(', TRUE, TRUE, FALSE, "IY", IS_16_BIT|IS_MEMORY|IS_INDEX_Y }, { SP_ADDRESS, '(', FALSE, FALSE, FALSE, "SP", IS_SPECIAL_16_BIT|IS_MEMORY|IS_SP }, { C_PORT, '(', FALSE, FALSE, FALSE, "C", IS_IO_PORT }, /* These cheat -- basically anything that doesn't match until here is either an address or a value. No distinction is made between 8/16 bit values. */ { ADDRESS, '(', TRUE, FALSE, TRUE, "", IS_VALUE|IS_MEMORY }, { VALUE, 0, TRUE, FALSE, 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_offset || 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; } } if (t->take_offset) { if (IsFinalPass() && (*offset < -128 || *offset > 127)) { snprintf(err, errsize, "%s: outside valid range " "for offset", arg); 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, {0x3a, WRITE_WORD_RHS}, BC_ADDRESS, A8, {0x02}, DE_ADDRESS, A8, {0x12}, ADDRESS, A8, {0x32, WRITE_WORD_RHS}, A8, I8, {0xed, 0x57}, A8, R8, {0xed, 0x5f}, I8, A8, {0xed, 0x47}, R8, A8, {0xed, 0x4f} }; 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 ((IsIndexX(t1) && IsIndexY(t2)) || (IsIndexY(t1) && IsIndexX(t2))) { snprintf(err, errsize, "%s: can't have mixed IX/IY registers", argv[0]); return CMD_FAILED; } /* LD r,r' */ if (IsNormal8Bit(t1) && IsNormal8Bit(t2)) { CommandStatus s = CMD_OK; if ((IsIndex(t1) || IsIndex(t2)) && (IsAnyOf(r1, H8, L8, INVALID_REG) || IsAnyOf(r2, H8, L8, INVALID_REG))) { snprintf(err, errsize, "%s: H/L will actually be the index " "register low/high register", argv[0]); s = CMD_OK_WARNING; } WriteEitherShift(r1,r2); PCWrite(0x40 | register_bitmask[r1] << 3 | register_bitmask[r2]); return s; } /* LD r,n */ if (IsNormal8Bit(t1) && IsSimpleValue(t2)) { WriteShift(r1); PCWrite(register_bitmask[r1] << 3 | 0x6); PCWrite(off2); return CMD_OK; } /* LD r,(HL)/(IX+d)/(IY+d) */ if ((IsNormal8Bit(t1) && !IsIndex(t1)) && (r2 == HL_ADDRESS || IsIndex(t2))) { WriteShift(r2); PCWrite(0x46 | register_bitmask[r1] << 3); WriteOffset(r2, off2); return CMD_OK; } /* LD (HL)/(IX+d)/(IY+d),r */ if ((IsNormal8Bit(t2) && !IsIndex(t2)) && (r1 == HL_ADDRESS || IsIndex(t1))) { WriteShift(r1); PCWrite(0x70 | register_bitmask[r2]); WriteOffset(r1, off1); return CMD_OK; } /* LD (HL)/(IX+d)/(IY+d),n */ if ((r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET) && r2 == VALUE) { WriteShift(r1); PCWrite(0x36); WriteOffset(r1, off1); PCWrite(off2); return CMD_OK; } /* LD rr,nn */ if (Is16Bit(t1) && !IsMemory(t1) && r2 == VALUE) { WriteShift(r1); PCWrite(register_bitmask[r1] << 4 | 0x01); PCWriteWord(off2); return CMD_OK; } /* LD HL/IX/IY,(nn) */ if ((r1 == HL16 || r1 == IX16 || r1 == IY16) && r2 == ADDRESS) { WriteShift(r1); 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/IX/IY */ if ((r2 == HL16 || r2 == IX16 || r2 == IY16) && r1 == ADDRESS) { WriteShift(r2); 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/IX/IY */ if ((r2 == HL16 || r2 == IX16 || r2 == IY16) && r1 == SP16) { WriteShift(r2); 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 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 HL/IX/IY */ if (r1 == HL16 || r1 == IX16 || r1 == IY16) { WriteShift(r1); PCWrite(0xe5); return CMD_OK; } /* PUSH rr */ if (r1 == AF16 || r1 == BC16 || r1 == DE16) { 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 HL/IX/IY */ if (r1 == HL16 || r1 == IX16 || r1 == IY16) { WriteShift(r1); PCWrite(0xe1); return CMD_OK; } /* POP rr */ if (r1 == AF16 || r1 == BC16 || r1 == DE16) { 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 EX(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { DE16, HL16, {0xeb}, HL16, DE16, {0xeb}, AF16, AF16_ALT, {0x08}, AF16_ALT, AF16, {0x08}, SP_ADDRESS, HL16, {0xe3}, HL16, SP_ADDRESS, {0xe3}, SP_ADDRESS, IX16, {SHIFT_IX, 0xe3}, IX16, SP_ADDRESS, {SHIFT_IX, 0xe3}, SP_ADDRESS, IY16, {SHIFT_IY, 0xe3}, IY16, SP_ADDRESS, {SHIFT_IY, 0xe3} }; 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 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}, A8, IX_OFFSET, {SHIFT_IX, 0x86, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0x86, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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; } /* ADD IX,rr */ if (r1 == IX16 && IsAnyOf(r2, BC16, DE16, IX16, SP16, INVALID_REG)) { PCWrite(SHIFT_IX); PCWrite(0x09 | register_bitmask[r2] << 4); return CMD_OK; } /* ADD IY,rr */ if (r1 == IY16 && IsAnyOf(r2, BC16, DE16, IY16, SP16, INVALID_REG)) { PCWrite(SHIFT_IY); 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}, A8, IX_OFFSET, {SHIFT_IX, 0x8e, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0x8e, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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}, A8, IX_OFFSET, {SHIFT_IX, 0x96, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0x96, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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}, A8, IX_OFFSET, {SHIFT_IX, 0x9e, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0x9e, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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}, A8, IX_OFFSET, {SHIFT_IX, 0xa6, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0xa6, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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}, A8, IX_OFFSET, {SHIFT_IX, 0xb6, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0xb6, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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}, A8, IX_OFFSET, {SHIFT_IX, 0xae, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0xae, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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}, A8, IX_OFFSET, {SHIFT_IX, 0xbe, WRITE_BYTE_RHS}, A8, IY_OFFSET, {SHIFT_IY, 0xbe, WRITE_BYTE_RHS}, }; 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)) { WriteShift(r2); 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 IM(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static int im[3] = {0x46, 0x56, 0x5e}; RegisterMode r1; RegisterType t1; int off1; CMD_ARGC_CHECK(2); if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize)) { return CMD_FAILED; } if (r1 != VALUE || (off1 < 0 || off1 > 2)) { snprintf(err, errsize, "%s: invalid argument %s", argv[0], argv[1]); return CMD_FAILED; } PCWrite(0xed); PCWrite(im[off1]); 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)) { WriteShift(r1); PCWrite(0x04 | register_bitmask[r1] << 3); return CMD_OK; } /* INC (HL)/(IX+d)/(IY+d) */ if (r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET) { WriteShift(r1); PCWrite(0x34); WriteOffset(r1, off1); return CMD_OK; } /* INC rr */ if (Is16Bit(t1) && !IsMemory(t1)) { WriteShift(r1); 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)) { WriteShift(r1); PCWrite(0x05 | register_bitmask[r1] << 3); return CMD_OK; } /* DEC (HL)/(IX+d)/(IY+d) */ if (r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET) { WriteShift(r1); PCWrite(0x35); WriteOffset(r1, off1); return CMD_OK; } /* DEC rr */ if (Is16Bit(t1) && !IsMemory(t1)) { WriteShift(r1); PCWrite(0x0b | register_bitmask[r1] << 4); return CMD_OK; } return IllegalArgs(argc, argv, quoted, err, 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; 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], "RRC")) { opcode_mask = 0x08; } else if (CompareString(argv[0], "RR")) { opcode_mask = 0x18; } else if (CompareString(argv[0], "SLA")) { opcode_mask = 0x20; } else if (CompareString(argv[0], "SRA")) { opcode_mask = 0x28; } else if (CompareString(argv[0], "SRL")) { opcode_mask = 0x38; } else if (CompareString(argv[0], "SLL")) { 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) && !IsIndex(t1)) { PCWrite(0xcb); PCWrite(opcode_mask | register_bitmask[r1]); return CMD_OK; } /* OP (HL)/(IX+d)/(IY+d) */ if (r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET) { WriteShift(r1); PCWrite(0xcb); WriteOffset(r1, off1); PCWrite(opcode_mask | 0x06); return CMD_OK; } } /* Undocumented opcodes */ if (argc == 3) { RegisterMode r2; RegisterType t2; int off2; if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize)) { return CMD_FAILED; } /* OP (IX+d)/(IY+d),r */ if ((r1 == IX_OFFSET || r1 == IY_OFFSET) && (r2 >= A8 && r2 <= L8)) { WriteShift(r1); PCWrite(0xcb); WriteOffset(r1, off1); PCWrite(opcode_mask | register_bitmask[r2]); 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) && !IsIndex(t2)) { PCWrite(0xcb); PCWrite(opcode_mask | off1 << 3 | register_bitmask[r2]); return CMD_OK; } /* OP b,(HL)/(IX+d)/(IY+d) */ if (r2 == HL_ADDRESS || r2 == IX_OFFSET || r2 == IY_OFFSET) { WriteShift(r2); PCWrite(0xcb); WriteOffset(r2, off2); PCWrite(opcode_mask | off1 << 3 | 0x06); return CMD_OK; } } /* Undocumented opcodes */ if (argc > 3 && (CompareString(argv[0], "SET") || CompareString(argv[0], "RES"))) { RegisterMode r3; RegisterType t3; int off3; if (!CalcRegisterMode(argv[3], quoted[3], &r3, &t3, &off3, err, errsize)) { return CMD_FAILED; } /* OP b,(IX+d)/(IY+d),r */ if ((r2 == IX_OFFSET || r2 == IY_OFFSET) && (r3 >= A8 && r3 <= L8)) { WriteShift(r2); PCWrite(0xcb); WriteOffset(r2, off2); PCWrite(opcode_mask | off1 << 3 | register_bitmask[r3]); 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 || mode == IX_ADDRESS || mode == IY_ADDRESS) { WriteShift(mode); 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 DJNZ(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { 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 rel; rel = val - ((PC() + 2) % 0x10000); CheckOffset(argv[1], rel); PCWrite(0x10); 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); } static CommandStatus IN(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(2); if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize)) { return CMD_FAILED; } if (r1 == C_PORT && argc == 2) { PCWrite(0xed); PCWrite(0x70); return CMD_OK; } CMD_ARGC_CHECK(3); if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize)) { return CMD_FAILED; } if (r1 == A8 && r2 == ADDRESS) { CheckRange(argv[2], off2, 0, 255); PCWrite(0xdb); PCWrite(off2); return CMD_OK; } if (!IsIndex(t1) && (IsNormal8Bit(t1) || r1 == F8) && r2 == C_PORT) { PCWrite(0xed); PCWrite(0x40 | register_bitmask[r1] << 3); return CMD_OK; } return IllegalArgs(argc, argv, quoted, err, errsize); } static CommandStatus OUT(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 (!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 && r2 == A8) { CheckRange(argv[1], off1, 0, 255); PCWrite(0xd3); PCWrite(off1); return CMD_OK; } if (r1 == C_PORT && !IsIndex(t2) && (IsNormal8Bit(t2) || r2 == F8)) { PCWrite(0xed); PCWrite(0x41 | register_bitmask[r2] << 3); return CMD_OK; } if (r1 == C_PORT && r2 == VALUE) { PCWrite(0xed); PCWrite(0x71); 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}, {"PUSH", PUSH}, {"POP", POP}, {"EX", EX}, {"ADD", ADD}, {"ADC", ADC}, {"SUB", SUB}, {"SBC", SBC}, {"AND", AND}, {"OR", OR}, {"XOR", XOR}, {"EOR", XOR}, {"CP", CP}, {"INC", INC}, {"DEC", DEC}, {"IM", IM}, {"RLC", RLC_RL_RRC_RR_ETC}, {"RL", RLC_RL_RRC_RR_ETC}, {"RRC", RLC_RL_RRC_RR_ETC}, {"RR", RLC_RL_RRC_RR_ETC}, {"SLA", RLC_RL_RRC_RR_ETC}, {"SRA", RLC_RL_RRC_RR_ETC}, {"SRL", RLC_RL_RRC_RR_ETC}, {"SLL", RLC_RL_RRC_RR_ETC}, {"BIT", BIT_SET_RES}, {"RES", BIT_SET_RES}, {"SET", BIT_SET_RES}, {"JP", JP}, {"JR", JR}, {"DJNZ", DJNZ}, {"CALL", CALL}, {"RET", RET}, {"RST", RST}, {"IN", IN}, {"OUT", OUT}, {NULL} }; static const OpcodeTable implied_opcodes[] = { {"NOP", {0x00}}, {"DI", {0xf3}}, {"EI", {0xfb}}, {"HALT", {0x76}}, {"HLT", {0x76}}, {"EXX", {0xd9}}, {"DAA", {0x27}}, {"CPL", {0x2f}}, {"SCF", {0x37}}, {"CCF", {0x3f}}, {"NEG", {0xed, 0x44}}, {"RLCA", {0x07}}, {"RRCA", {0x0f}}, {"RLA", {0x17}}, {"RRA", {0x1f}}, {"CPI", {0xed, 0xa1}}, {"CPIR", {0xed, 0xb1}}, {"CPD", {0xed, 0xa9}}, {"CPDR", {0xed, 0xb9}}, {"INI", {0xed, 0xa2}}, {"INIR", {0xed, 0xb2}}, {"IND", {0xed, 0xaa}}, {"INDR", {0xed, 0xba}}, {"OUTI", {0xed, 0xa3}}, {"OTIR", {0xed, 0xb3}}, {"OUTD", {0xed, 0xab}}, {"OTDR", {0xed, 0xbb}}, {"LDI", {0xed, 0xa0}}, {"LDIR", {0xed, 0xb0}}, {"LDD", {0xed, 0xa8}}, {"LDDR", {0xed, 0xb8}}, {"RRD", {0xed, 0x67}}, {"RLD", {0xed, 0x6f}}, {"RETI", {0xed, 0x4d}}, {"RETN", {0xed, 0x45}}, {NULL} }; /* ---------------------------------------- PUBLIC INTERFACES */ void Init_Z80(void) { } const ValueTable *Options_Z80(void) { return NULL; } CommandStatus SetOption_Z80(int opt, int argc, char *argv[], int quoted[], char *err, size_t errsize) { return CMD_OK; } CommandStatus Handler_Z80(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 */