/* 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 . ------------------------------------------------------------------------- SPC700 Assembler */ #include #include #include "global.h" #include "expr.h" #include "label.h" #include "parse.h" #include "cmd.h" #include "codepage.h" #include "spc700.h" /* ---------------------------------------- TYPES AND GLOBALS */ enum option_t { OPT_DP }; enum dp_mode_t { DP_OFF, DP_ON, DP_AUTO }; static const ValueTable options[] = { {"direct-page", OPT_DP}, {NULL} }; static const ValueTable dp_table[] = { YES_NO_ENTRIES(DP_ON, DP_OFF), {"auto", DP_AUTO}, {NULL} }; static struct { enum dp_mode_t dp_mode; } option; typedef enum { ACCUMULATOR, X_REGISTER, Y_REGISTER, YA_REGISTER, SP_REGISTER, C_FLAG, PSW_REGISTER, BIT_ADDRESS, NOTTED_BIT_ADDRESS, INDIRECT_X, INDIRECT_Y, INDIRECT_X_INC, IMPLIED, IMMEDIATE, ABSOLUTE, DIRECT_PAGE, ABSOLUTE_INDEX_X, ABSOLUTE_INDEX_Y, DIRECT_PAGE_INDEX_X, DIRECT_PAGE_INDEX_Y, DIRECT_PAGE_INDIRECT_X, DIRECT_PAGE_INDIRECT_Y, ADDR_MODE_ERROR, ADDR_MODE_UNKNOWN } address_mode_t; static const char *address_mode_name[] = { "Accumulator", "X register", "Y register", "YA register", "Stack Pointer", "Carry flag", "PSW register", "Bit address", "Notted (/) bit address", "Indirect X", "Indirect Y", "Indirect X increment", "Implied", "Immediate", "Absolute", "Direct Page", "Absolute, index X", "Absolute, index Y", "Direct Page, index X", "Direct Page, index Y", "Direct Page, indirect X", "Direct Page, indirect Y", "Address Mode Error", "Address Mode Unknown" }; typedef enum { WB_LHS = -1, WW_LHS = -2, WB_RHS = -3, WW_RHS = -4 } StreamCodes; typedef struct { address_mode_t lhs; address_mode_t rhs; int code[10]; } RegisterPairCodes; #define NUM_REGISTER_CODES(a) ((sizeof a)/(sizeof a[1])) /* ---------------------------------------- MACROS */ #define ADDRESS_MODE(mode, address, idx) \ do \ { \ CalcAddressMode(argc, argv, quoted, idx, err, errsize, \ &mode, &address); \ \ if (mode == ADDR_MODE_UNKNOWN) \ { \ snprintf(err, errsize, "%s: couldn't work out " \ "addressing mode", argv[0]); \ return CMD_FAILED; \ } \ \ if (mode == ADDR_MODE_ERROR) return CMD_FAILED; \ } while(0) #define CMD_DP_MODE(DP_mode, non_DP_mode) \ do \ { \ switch(option.dp_mode) \ { \ case DP_ON: \ if (*address < 0 || *address > 255) \ { \ snprintf(err, errsize, "value %d outside of " \ "zero page", *address); \ *mode = ADDR_MODE_ERROR; \ return; \ } \ \ *mode = DP_mode; \ break; \ \ case DP_OFF: \ *mode = non_DP_mode; \ break; \ \ case DP_AUTO: \ if (*address >= 0 && *address <= 255) \ { \ *mode = DP_mode; \ } \ else \ { \ *mode = non_DP_mode; \ } \ break; \ } \ } while (0) #define CHECK_RANGE(val, min, max) \ do { \ if (val < min || val > max) \ { \ snprintf(err, errsize, "%s: value %d outside " \ "of valid range %d - %d", \ argv[0], val, min, max); \ return CMD_FAILED; \ } \ } while(0) /* ---------------------------------------- PRIVATE FUNCTIONS */ static void CalcAddressMode(int argc, char *argv[], int quoted[], int index, char *err, size_t errsize, address_mode_t *mode, int *address) { char *arg; int quote; *mode = ADDR_MODE_UNKNOWN; *address = 0; /* Implied */ if (argc == 1) { *mode = IMPLIED; return; } arg = argv[index]; quote = quoted[index]; /* Accumulator and other simple fixed string register modes */ if (CompareString(arg, "A")) { *mode = ACCUMULATOR; return; } if (CompareString(arg, "C")) { *mode = C_FLAG; return; } if (CompareString(arg, "PSW")) { *mode = PSW_REGISTER; return; } if (CompareString(arg, "X")) { if (quote == '(') { *mode = INDIRECT_X; } else { *mode = X_REGISTER; } return; } if (CompareString(arg, "Y")) { if (quote == '(') { *mode = INDIRECT_Y; } else { *mode = Y_REGISTER; } return; } if (CompareString(arg, "YA")) { *mode = YA_REGISTER; return; } if (CompareString(arg, "SP")) { *mode = SP_REGISTER; return; } if (CompareString(arg, "(X)+")) { *mode = INDIRECT_X_INC; return; } /* Bit addresses */ if (strchr(arg, '.')) { char *copy; char *end; int a,b; copy = DupStr(arg); end = strrchr(copy, '.'); *end++ = 0; if (arg[0] == '/') { *mode = NOTTED_BIT_ADDRESS; if (IsFinalPass() && !ExprEval(copy + 1, &a)) { snprintf(err, errsize, "%s: expression error: %s", copy + 1, ExprError()); *mode = ADDR_MODE_ERROR; free(copy); return; } } else { *mode = BIT_ADDRESS; if (IsFinalPass() && !ExprEval(copy, &a)) { snprintf(err, errsize, "%s: expression error: %s", copy, ExprError()); *mode = ADDR_MODE_ERROR; free(copy); return; } } if (IsFinalPass() && !ExprEval(end, &b)) { snprintf(err, errsize, "%s: expression error: %s", end, ExprError()); *mode = ADDR_MODE_ERROR; free(copy); return; } if (IsFinalPass() && (a < 0 || a > 0x1fff)) { snprintf(err, errsize, "%s: address component of bit address " "out of valid range 0x0 to 0x1fff", copy); *mode = ADDR_MODE_ERROR; free(copy); return; } if (IsFinalPass() && (b < 0 || b > 7)) { snprintf(err, errsize, "%s: address component of bit address " "out of valid range 0 to 7", end); *mode = ADDR_MODE_ERROR; free(copy); return; } *address = a | b << 13; free(copy); return; } /* Immediate */ if (arg[0] == '#') { *mode = IMMEDIATE; if (IsFinalPass() && !ExprEval(arg + 1, address)) { snprintf(err, errsize, "%s: expression error: %s", arg + 1, ExprError()); *mode = ADDR_MODE_ERROR; } return; } /* Address modes involving '+' character */ if (strchr(arg, '+')) { char *copy; char *end; copy = DupStr(arg); end = strrchr(copy, '+'); *end++ = 0; /* Direct page indirect X */ if (quote == '(' && CompareString(end, "X")) { if (IsFinalPass() && !ExprEval(copy, address)) { snprintf(err, errsize, "%s: expression error: %s", copy, ExprError()); *mode = ADDR_MODE_ERROR; } else { *mode = DIRECT_PAGE_INDIRECT_X; } free(copy); return; } /* Direct page indirect Y */ if (!quote && *copy == '(' && CompareString(end, "Y")) { /* Remember that the expression parser doesn't allow round braclets. */ TrimChars(copy, "()"); if (IsFinalPass() && !ExprEval(copy, address)) { snprintf(err, errsize, "%s: expression error: %s", copy, ExprError()); *mode = ADDR_MODE_ERROR; } else { *mode = DIRECT_PAGE_INDIRECT_Y; } free(copy); return; } /* Direct page or absolute index X */ if (!quote && CompareString(end, "X")) { if (IsFinalPass() && !ExprEval(copy, address)) { snprintf(err, errsize, "%s: expression error: %s", copy, ExprError()); *mode = ADDR_MODE_ERROR; } else { CMD_DP_MODE(DIRECT_PAGE_INDEX_X, ABSOLUTE_INDEX_X); } free(copy); return; } /* Direct page or absolute index Y */ if (!quote && CompareString(end, "Y")) { if (IsFinalPass() && !ExprEval(copy, address)) { snprintf(err, errsize, "%s: expression error: %s", copy, ExprError()); *mode = ADDR_MODE_ERROR; } else { CMD_DP_MODE(DIRECT_PAGE_INDEX_Y, ABSOLUTE_INDEX_Y); } free(copy); return; } free(copy); } /* If all else fails, Absolute */ if (IsFinalPass() && !ExprEval(arg, address)) { snprintf(err, errsize, "%s: expression error: %s", arg, ExprError()); *mode = ADDR_MODE_ERROR; return; } CMD_DP_MODE(DIRECT_PAGE, ABSOLUTE); } static int WriteRegisterPairModes(const char *caller, const RegisterPairCodes *codes, size_t count, address_mode_t lhs, address_mode_t 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 WB_LHS: PCWrite(val_lhs); break; case WW_LHS: PCWriteWord(val_lhs); break; case WB_RHS: PCWrite(val_rhs); break; case WW_RHS: PCWriteWord(val_rhs); break; default: PCWrite(codes[f].code[r]); break; } } return TRUE; } } snprintf(err, errsize, "%s: no code generation for register pair %s/%s", caller, address_mode_name[lhs], address_mode_name[rhs]); return FALSE; } static int MakeRelative(int *addr, char *cmd, char *err, size_t errsize) { *addr = *addr - (PC() + 2); if (IsFinalPass() && (*addr < -128 || *addr > 127)) { snprintf(err, errsize, "%s: Branch offset (%d) too big", cmd, *addr); return FALSE; } return TRUE; } /* ---------------------------------------- COMMAND HANDLERS */ #define COMMON_ALU(base) \ do { \ static RegisterPairCodes codes[] = \ { \ {ACCUMULATOR, IMMEDIATE, {base + 0x08, WB_RHS}}, \ {ACCUMULATOR, INDIRECT_X, {base + 0x06}}, \ {ACCUMULATOR, DIRECT_PAGE, {base + 0x04, WB_RHS}}, \ {ACCUMULATOR, DIRECT_PAGE_INDEX_X, {base + 0x14, WB_RHS}}, \ {ACCUMULATOR, ABSOLUTE, {base + 0x05, WW_RHS}}, \ {ACCUMULATOR, ABSOLUTE_INDEX_X, {base + 0x15, WW_RHS}}, \ {ACCUMULATOR, ABSOLUTE_INDEX_Y, {base + 0x16, WW_RHS}}, \ {ACCUMULATOR, DIRECT_PAGE_INDEX_Y, {base + 0x16, WW_RHS}}, \ {ACCUMULATOR, DIRECT_PAGE_INDIRECT_X, {base + 0x07, WB_RHS}}, \ {ACCUMULATOR, DIRECT_PAGE_INDIRECT_Y, {base + 0x17, WB_RHS}}, \ {INDIRECT_X, INDIRECT_Y, {base + 0x19}}, \ {DIRECT_PAGE, DIRECT_PAGE, {base + 0x09, \ WB_LHS, WB_RHS}}, \ {DIRECT_PAGE, IMMEDIATE, {base + 0x18, \ WB_LHS, WB_RHS}} \ }; \ \ address_mode_t mode1, mode2; \ int addr1, addr2; \ \ CMD_ARGC_CHECK(3); \ \ ADDRESS_MODE(mode1, addr1, 1); \ ADDRESS_MODE(mode2, addr2, 2); \ \ if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), \ mode1, mode2, addr1, addr2, err, errsize)) \ { \ return CMD_FAILED; \ } \ \ return CMD_OK; \ } while(0) static CommandStatus MOV(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {ACCUMULATOR, IMMEDIATE, {0xe8, WB_RHS}}, {ACCUMULATOR, INDIRECT_X, {0xe6}}, {ACCUMULATOR, INDIRECT_X_INC, {0xbf}}, {ACCUMULATOR, DIRECT_PAGE, {0xe4, WB_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDEX_X, {0xf4, WB_RHS}}, {ACCUMULATOR, ABSOLUTE, {0xe5, WW_RHS}}, {ACCUMULATOR, ABSOLUTE_INDEX_X, {0xf5, WW_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDEX_Y, {0xf6, WW_RHS}}, {ACCUMULATOR, ABSOLUTE_INDEX_Y, {0xf6, WW_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDIRECT_X, {0xe7, WB_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDIRECT_Y, {0xf7, WB_RHS}}, {X_REGISTER, IMMEDIATE, {0xcd, WB_RHS}}, {X_REGISTER, DIRECT_PAGE, {0xf8, WB_RHS}}, {X_REGISTER, DIRECT_PAGE_INDEX_Y, {0xf9, WB_RHS}}, {X_REGISTER, ABSOLUTE, {0xe9, WW_RHS}}, {Y_REGISTER, IMMEDIATE, {0x8d, WB_RHS}}, {Y_REGISTER, DIRECT_PAGE, {0xeb, WB_RHS}}, {Y_REGISTER, DIRECT_PAGE_INDEX_X, {0xfb, WB_RHS}}, {Y_REGISTER, ABSOLUTE, {0xec, WW_RHS}}, {INDIRECT_X, ACCUMULATOR, {0xc6}}, {INDIRECT_X_INC, ACCUMULATOR, {0xaf}}, {DIRECT_PAGE, ACCUMULATOR, {0xc4, WB_LHS}}, {DIRECT_PAGE_INDEX_X, ACCUMULATOR, {0xd4, WB_LHS}}, {ABSOLUTE, ACCUMULATOR, {0xc5, WW_LHS}}, {ABSOLUTE_INDEX_X, ACCUMULATOR, {0xd5, WW_LHS}}, {ABSOLUTE_INDEX_Y, ACCUMULATOR, {0xd6, WW_LHS}}, {DIRECT_PAGE_INDEX_Y, ACCUMULATOR, {0xd6, WW_LHS}}, {DIRECT_PAGE_INDIRECT_X,ACCUMULATOR, {0xc7, WB_LHS}}, {DIRECT_PAGE_INDIRECT_Y,ACCUMULATOR, {0xd7, WB_LHS}}, {DIRECT_PAGE, X_REGISTER, {0xd8, WB_LHS}}, {DIRECT_PAGE_INDEX_Y, X_REGISTER, {0xd9, WB_LHS}}, {ABSOLUTE, X_REGISTER, {0xc9, WW_LHS}}, {DIRECT_PAGE, Y_REGISTER, {0xcb, WB_LHS}}, {DIRECT_PAGE_INDEX_X, Y_REGISTER, {0xdb, WB_LHS}}, {ABSOLUTE, Y_REGISTER, {0x0c, WW_LHS}}, {ACCUMULATOR, X_REGISTER, {0x7d}}, {ACCUMULATOR, Y_REGISTER, {0xdd}}, {X_REGISTER, ACCUMULATOR, {0x5d}}, {Y_REGISTER, ACCUMULATOR, {0xfd}}, {X_REGISTER, SP_REGISTER, {0x9d}}, {SP_REGISTER, X_REGISTER, {0xbd}}, {DIRECT_PAGE, DIRECT_PAGE, {0xfa, WB_LHS, WB_RHS}}, {DIRECT_PAGE, IMMEDIATE, {0x8f, WB_LHS, WB_RHS}} }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, 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) { COMMON_ALU(0x80); } static CommandStatus ADDW(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {YA_REGISTER, DIRECT_PAGE, {0x7a, WB_RHS}}, }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, 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) { COMMON_ALU(0x20); } static CommandStatus ASL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x1c); return CMD_OK; case DIRECT_PAGE: PCWrite(0x0b); PCWrite(addr); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0x1b); PCWrite(addr); return CMD_OK; case ABSOLUTE: PCWrite(0xcc); PCWriteWord(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus CMP(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {ACCUMULATOR, IMMEDIATE, {0x68, WB_RHS}}, {ACCUMULATOR, INDIRECT_X, {0x66}}, {ACCUMULATOR, DIRECT_PAGE, {0x64, WB_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDEX_X, {0x74, WB_RHS}}, {ACCUMULATOR, ABSOLUTE, {0x65, WW_RHS}}, {ACCUMULATOR, ABSOLUTE_INDEX_X, {0x75, WW_RHS}}, {ACCUMULATOR, ABSOLUTE_INDEX_Y, {0x76, WW_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDEX_Y, {0x76, WW_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDIRECT_X, {0x67, WB_RHS}}, {ACCUMULATOR, DIRECT_PAGE_INDIRECT_Y, {0x77, WB_RHS}}, {INDIRECT_X, INDIRECT_Y, {0x79}}, {DIRECT_PAGE, DIRECT_PAGE, {0x69, WB_LHS, WB_RHS}}, {DIRECT_PAGE, IMMEDIATE, {0x78, WB_LHS, WB_RHS}}, {X_REGISTER, IMMEDIATE, {0xc8, WB_RHS}}, {X_REGISTER, DIRECT_PAGE, {0x3e, WB_RHS}}, {X_REGISTER, ABSOLUTE, {0x1e, WW_RHS}}, {Y_REGISTER, IMMEDIATE, {0xad, WB_RHS}}, {Y_REGISTER, DIRECT_PAGE, {0x7e, WB_RHS}}, {Y_REGISTER, ABSOLUTE, {0x5e, WW_RHS}}, }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, err, errsize)) { return CMD_FAILED; } return CMD_OK; } static CommandStatus CMPW(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {YA_REGISTER, DIRECT_PAGE, {0x5a, WB_RHS}}, }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, err, errsize)) { return CMD_FAILED; } return CMD_OK; } static CommandStatus DEC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x9c); return CMD_OK; case DIRECT_PAGE: PCWrite(0x8b); PCWrite(addr); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0x9b); PCWrite(addr); return CMD_OK; case ABSOLUTE: PCWrite(0x8c); PCWriteWord(addr); return CMD_OK; case X_REGISTER: PCWrite(0x1d); return CMD_OK; case Y_REGISTER: PCWrite(0xdc); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus DECW(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case DIRECT_PAGE: PCWrite(0x1a); PCWrite(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus EOR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { COMMON_ALU(0x40); } static CommandStatus INC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0xbc); return CMD_OK; case DIRECT_PAGE: PCWrite(0xab); PCWrite(addr); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0xbb); PCWrite(addr); return CMD_OK; case ABSOLUTE: PCWrite(0xac); PCWriteWord(addr); return CMD_OK; case X_REGISTER: PCWrite(0x3d); return CMD_OK; case Y_REGISTER: PCWrite(0xfc); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus INCW(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case DIRECT_PAGE: PCWrite(0x3a); PCWrite(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus JMP(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; ADDRESS_MODE(mode, address, 1); switch(mode) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x5f); PCWriteWord(address); return CMD_OK; case DIRECT_PAGE_INDEX_X: case ABSOLUTE_INDEX_X: case DIRECT_PAGE_INDIRECT_X: PCWrite(0x1f); PCWriteWord(address); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus CALL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; ADDRESS_MODE(mode, address, 1); switch(mode) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x3f); PCWriteWord(address); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus PCALL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; ADDRESS_MODE(mode, address, 1); switch(mode) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x4f); PCWrite(address); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus TCALL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; ADDRESS_MODE(mode, address, 1); switch(mode) { case ABSOLUTE: case DIRECT_PAGE: CHECK_RANGE(address, 0, 15); PCWrite(0x01 + 0x10 * address); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus LSR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x5c); return CMD_OK; case DIRECT_PAGE: PCWrite(0x4b); PCWrite(addr); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0x5b); PCWrite(addr); return CMD_OK; case ABSOLUTE: PCWrite(0x4c); PCWriteWord(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus OR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { COMMON_ALU(0x00); } static CommandStatus ROL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x3c); return CMD_OK; case DIRECT_PAGE: PCWrite(0x2b); PCWrite(addr); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0x3b); PCWrite(addr); return CMD_OK; case ABSOLUTE: PCWrite(0x2c); PCWriteWord(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus ROR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x7c); return CMD_OK; case DIRECT_PAGE: PCWrite(0x6b); PCWrite(addr); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0x7b); PCWrite(addr); return CMD_OK; case ABSOLUTE: PCWrite(0x6c); PCWriteWord(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus SBC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { COMMON_ALU(0xa0); } static CommandStatus SUBW(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {YA_REGISTER, DIRECT_PAGE, {0x9a, WB_RHS}}, }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, err, errsize)) { return CMD_FAILED; } return CMD_OK; } static CommandStatus XCN(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x9f); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus MOVW(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {YA_REGISTER, DIRECT_PAGE, {0xba}}, {DIRECT_PAGE, YA_REGISTER, {0xda}} }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, err, errsize)) { return CMD_FAILED; } return CMD_OK; } static CommandStatus MUL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case YA_REGISTER: PCWrite(0xcf); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus DIV(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { static RegisterPairCodes codes[] = { {YA_REGISTER, X_REGISTER, {0x9e}} }; address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!WriteRegisterPairModes(argv[0], codes, NUM_REGISTER_CODES(codes), mode1, mode2, addr1, addr2, err, errsize)) { return CMD_FAILED; } return CMD_OK; } static CommandStatus DAA(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0xdf); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus DAS(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0xbe); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus PUSH(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0x2d); return CMD_OK; case X_REGISTER: PCWrite(0x4d); return CMD_OK; case Y_REGISTER: PCWrite(0x6d); return CMD_OK; case PSW_REGISTER: PCWrite(0x0d); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus POP(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ACCUMULATOR: PCWrite(0xae); return CMD_OK; case X_REGISTER: PCWrite(0xce); return CMD_OK; case Y_REGISTER: PCWrite(0xee); return CMD_OK; case PSW_REGISTER: PCWrite(0x8e); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus BBCx(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; int bit; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); bit = argv[0][strlen(argv[0]) - 1] - '0'; if (!MakeRelative(&addr2, argv[0], err, errsize)) { return CMD_FAILED; } switch(mode1) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x13 + bit * 0x20); PCWrite(addr1); PCWrite(addr2); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode1]); return CMD_FAILED; } } static CommandStatus BBSx(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; int bit; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); bit = argv[0][strlen(argv[0]) - 1] - '0'; if (!MakeRelative(&addr2, argv[0], err, errsize)) { return CMD_FAILED; } switch(mode1) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x03 + bit * 0x20); PCWrite(addr1); PCWrite(addr2); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode1]); return CMD_FAILED; } } static CommandStatus CBNE(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!MakeRelative(&addr2, argv[0], err, errsize)) { return CMD_FAILED; } switch(mode1) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x2e); PCWrite(addr1); PCWrite(addr2); return CMD_OK; case DIRECT_PAGE_INDEX_X: PCWrite(0xde); PCWrite(addr1); PCWrite(addr2); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode1]); return CMD_FAILED; } } static CommandStatus DBNZ(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (!MakeRelative(&addr2, argv[0], err, errsize)) { return CMD_FAILED; } switch(mode1) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x6e); PCWrite(addr1); PCWrite(addr2); return CMD_OK; case Y_REGISTER: PCWrite(0xfe); PCWrite(addr2); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode1]); return CMD_FAILED; } } static CommandStatus SETx(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; int bit; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); bit = argv[0][strlen(argv[0]) - 1] - '0'; switch(mode) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x12 + bit * 0x20); PCWrite(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus CLRx(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; int bit; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); bit = argv[0][strlen(argv[0]) - 1] - '0'; switch(mode) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0x02 + bit * 0x20); PCWrite(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus TSET1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case DIRECT_PAGE: case ABSOLUTE: PCWrite(0x0e); PCWriteWord(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus TCLR1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); switch(mode) { case ABSOLUTE: case DIRECT_PAGE: PCWrite(0xe4); PCWriteWord(addr); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus AND1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (mode1 == C_FLAG) { switch(mode2) { case BIT_ADDRESS: PCWrite(0x4a); PCWriteWord(addr2); return CMD_OK; case NOTTED_BIT_ADDRESS: PCWrite(0x6a); PCWriteWord(addr2); return CMD_OK; default: break; } } snprintf(err, errsize, "%s: unsupported addressing mode %s, %s", argv[0], address_mode_name[mode1], address_mode_name[mode2]); return CMD_FAILED; } static CommandStatus OR1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (mode1 == C_FLAG) { switch(mode2) { case BIT_ADDRESS: PCWrite(0x0a); PCWriteWord(addr2); return CMD_OK; case NOTTED_BIT_ADDRESS: PCWrite(0x2a); PCWriteWord(addr2); return CMD_OK; default: break; } } snprintf(err, errsize, "%s: unsupported addressing mode %s, %s", argv[0], address_mode_name[mode1], address_mode_name[mode2]); return CMD_FAILED; } static CommandStatus EOR1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (mode1 == C_FLAG) { switch(mode2) { case BIT_ADDRESS: PCWrite(0x8a); PCWriteWord(addr2); return CMD_OK; default: break; } } snprintf(err, errsize, "%s: unsupported addressing mode %s, %s", argv[0], address_mode_name[mode1], address_mode_name[mode2]); return CMD_FAILED; } static CommandStatus NOT1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int addr; CMD_ARGC_CHECK(2); ADDRESS_MODE(mode, addr, 1); if (mode == BIT_ADDRESS) { PCWrite(0xea); PCWriteWord(addr); return CMD_OK; } snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } static CommandStatus MOV1(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode1, mode2; int addr1, addr2; CMD_ARGC_CHECK(3); ADDRESS_MODE(mode1, addr1, 1); ADDRESS_MODE(mode2, addr2, 2); if (mode1 == C_FLAG && mode2 == BIT_ADDRESS) { PCWrite(0xaa); PCWriteWord(addr2); return CMD_OK; } if (mode2 == C_FLAG && mode1 == BIT_ADDRESS) { PCWrite(0xca); PCWriteWord(addr2); return CMD_OK; } snprintf(err, errsize, "%s: unsupported addressing mode %s, %s", argv[0], address_mode_name[mode1], address_mode_name[mode2]); return CMD_FAILED; } /* ---------------------------------------- OPCODE TABLES */ typedef struct { const char *op; int code; } OpcodeTable; typedef struct { const char *op; Command cmd; } HandlerTable; static const OpcodeTable implied_opcodes[] = { {"NOP", 0x00}, {"SLEEP", 0xef}, {"STOP", 0xff}, {"CLRC", 0x60}, {"SETC", 0x80}, {"NOTC", 0xed}, {"CLRV", 0xe0}, {"CLRP", 0x20}, {"SETP", 0x40}, {"EI", 0xa0}, {"DI", 0xc0}, {"BRK", 0x0f}, {"RET", 0x6f}, {"RETI", 0x7f}, {NULL} }; static const OpcodeTable branch_opcodes[] = { {"BRA", 0x2f}, {"BEQ", 0xF0}, {"BNE", 0xD0}, {"BCS", 0xB0}, {"BVC", 0x50}, {"BCC", 0x90}, {"BVS", 0x70}, {"BVC", 0x50}, {"BMI", 0x30}, {"BPL", 0x10}, {NULL} }; static const HandlerTable handler_table[] = { {"ADC", ADC}, {"ADDW", ADDW}, {"AND", AND}, {"AND1", AND1}, {"ASL", ASL}, {"BBC0", BBCx}, {"BBC1", BBCx}, {"BBC2", BBCx}, {"BBC3", BBCx}, {"BBC4", BBCx}, {"BBC5", BBCx}, {"BBC6", BBCx}, {"BBC7", BBCx}, {"BBS0", BBSx}, {"BBS1", BBSx}, {"BBS2", BBSx}, {"BBS3", BBSx}, {"BBS4", BBSx}, {"BBS5", BBSx}, {"BBS6", BBSx}, {"BBS7", BBSx}, {"CALL", CALL}, {"CBNE", CBNE}, {"CLR0", CLRx}, {"CLR1", CLRx}, {"CLR2", CLRx}, {"CLR3", CLRx}, {"CLR4", CLRx}, {"CLR5", CLRx}, {"CLR6", CLRx}, {"CLR7", CLRx}, {"CMP", CMP}, {"CMPW", CMPW}, {"DAA", DAA}, {"DAS", DAS}, {"DBNZ", DBNZ}, {"DEC", DEC}, {"DECW", DECW}, {"DIV", DIV}, {"EOR", EOR}, {"EOR1", EOR1}, {"INC", INC}, {"INCW", INCW}, {"JMP", JMP}, {"LSR", LSR}, {"MOV", MOV}, {"MOV1", MOV1}, {"MOVW", MOVW}, {"MUL", MUL}, {"NOT1", NOT1}, {"OR", OR}, {"OR1", OR1}, {"PCALL", PCALL}, {"POP", POP}, {"PUSH", PUSH}, {"ROL", ROL}, {"ROR", ROR}, {"SBC", SBC}, {"SET0", SETx}, {"SET1", SETx}, {"SET2", SETx}, {"SET3", SETx}, {"SET4", SETx}, {"SET5", SETx}, {"SET6", SETx}, {"SET7", SETx}, {"SUBW", SUBW}, {"TCALL", TCALL}, {"TCLR1", TCLR1}, {"TSET1", TSET1}, {"XCN", XCN}, {NULL} }; /* ---------------------------------------- PUBLIC FUNCTIONS */ void Init_SPC700(void) { option.dp_mode = DP_AUTO; SetNeededPasses(3); } const ValueTable *Options_SPC700(void) { return options; } CommandStatus SetOption_SPC700(int opt, int argc, char *argv[], int quoted[], char *err, size_t errsize) { const ValueTable *val; switch(opt) { case OPT_DP: CMD_ARGC_CHECK(1); CMD_TABLE(argv[0], dp_table, val); option.dp_mode = val->value; break; default: break; } return CMD_OK; } CommandStatus Handler_SPC700(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)) { PCWrite(implied_opcodes[f].code); return CMD_OK; } } /* Check for branch opcodes */ for(f = 0; branch_opcodes[f].op; f++) { if (CompareString(argv[0], branch_opcodes[f].op)) { int offset; CMD_ARGC_CHECK(2); CMD_EXPR(argv[1], offset); if (!MakeRelative(&offset, argv[0], err, errsize)) { return CMD_FAILED; } PCWrite(branch_opcodes[f].code); PCWrite(offset); 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 */