/* 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 . ------------------------------------------------------------------------- 65c816 Assembler */ #include #include #include "global.h" #include "expr.h" #include "label.h" #include "parse.h" #include "cmd.h" #include "codepage.h" #include "65c816.h" /* ---------------------------------------- TYPES AND GLOBALS */ /* Note some addressing modes are indistinguable and will never be returned, for example STACK_IMMEDIATE or STACK_PC_LONG. They are kept here as a memory aid. */ typedef enum { ACCUMULATOR, /* A */ IMPLIED, /* none */ IMMEDIATE, /* #$nn */ ABSOLUTE, /* $nnnn */ ABSOLUTE_INDEX_X_INDIRECT, /* ($nnnn,X) */ ABSOLUTE_INDEX_X, /* $nnnn,X */ ABSOLUTE_INDEX_Y, /* $nnnn,Y */ ABSOLUTE_INDIRECT, /* ($nnnn) */ ABSOLUTE_INDIRECT_LONG, /* [$nnnn] */ ABSOLUTE_LONG, /* $nnnnnn */ ABSOLUTE_LONG_INDEX_X, /* $nnnnnn,X */ DIRECT_PAGE, /* $nn */ DIRECT_PAGE_INDEX_X, /* $nn,X */ DIRECT_PAGE_INDEX_Y, /* $nn,Y */ DIRECT_PAGE_INDIRECT, /* ($nn) */ DIRECT_PAGE_INDIRECT_LONG, /* [$nn] */ DIRECT_PAGE_INDEX_X_INDIRECT, /* ($nn,X) */ DIRECT_PAGE_INDEX_Y_INDIRECT, /* ($nn),Y */ DIRECT_PAGE_INDEX_Y_INDIRECT_LONG, /* [$nn],Y */ RELATIVE, /* rr */ RELATIVE_LONG, /* rrrr */ STACK_RELATIVE_INDIRECT_INDEX_Y, /* (sr,S),Y */ STACK_RELATIVE, /* sr,S */ STACK_IMMEDIATE /* #$nnnn */ STACK_DIRECT_PAGE_INDIRECT, /* ($nn) */ STACK_PC_LONG, /* #$nnnn */ ADDR_MODE_ERROR, ADDR_MODE_UNKNOWN } address_mode_t; static const char *address_mode_name[ADDR_MODE_UNKNOWN+1] = { "Accumulator", "Implied", "Immediate", "Absolute", "Absolute Index X, Indirect", "Absolute, Index X", "Absolute, Index Y", "Absolute Indirect", "Absolute Indirect Long", "Absolute Long", "Absolute Long, Index X", "Direct Page", "Direct Page, Index X", "Direct Page, Index Y", "Direct Page Indirect", "Direct Page Indirect Long", "Direct Page Index X, Indirect", "Direct Page, Indirect, Index Y", "Direct Page, Indirect, Index Y Long", "Relative", "Relative Long", "Stack Relative Indirect Index Y", "Stack Relative", "Stack Immediate", "Stack Direct Page Indirect", "Stack Program Counter Relative Long:, "Address Mode Error", "Address Mode Unknown" }; /* ---------------------------------------- MACROS */ #define CMD_ADDRESS_MODE(mode, address) \ do \ { \ CalcAddressMode(argc, argv, quoted, 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_RANGE_ADDR_MODE(mode, dp_mode, norm_mode, long_mode, value) \ do \ { \ if (value >= 0 && value <= 0xff) \ { \ *mode = dp_mode; \ } \ else if (value > 0xffff) \ { \ *mode = long_mode; \ } \ else \ { \ *mode = norm_mode; \ } \ } while (0) /* ---------------------------------------- PRIVATE FUNCTIONS */ static void CalcAddressMode(int argc, char *argv[], int quoted[], char *err, size_t errsize, address_mode_t *mode, int *address) { *mode = ADDR_MODE_UNKNOWN; *address = 0; /* Implied */ if (argc == 1) { *mode = IMPLIED; return; } /* Accumulator */ if (argc == 2 && CompareString(argv[1], "A")) { *mode = ACCUMULATOR; return; } /* Immediate */ if (argc == 2 && argv[1][0] == '#') { *mode = IMMEDIATE; if (!ExprEval(argv[1] + 1, address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; } return; } /* Absolute */ if (argc == 2 && !quoted[1]) { if (!ExprEval(argv[1], address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; return; } CMD_RANGE_ADDR_MODE(mode, DIRECT_PAGE, ABSOLUTE, ABSOLUTE_LONG, address); return; } /* Absolute Indirect */ if (argc == 1 && quoted[1] == '(') { if (!ExprEval(argv[1], address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; return; } CMD_RANGE_ADDR_MODE(mode, DIRECT_PAGE_INDIRECT, ABSOLUTE_INDIRECT, ADDR_MODE_ERROR, address); return; } /* Absolute Indirect Long */ if (argc == 1 && quoted[1] == '[') { if (!ExprEval(argv[1], address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; return; } CMD_RANGE_ADDR_MODE(mode, DIRECT_PAGE_INDIRECT_LONG, ABSOLUTE_INDIRECT_LONG, ADDR_MODE_ERROR, address); return; } /* Absolute,[XY] */ if (argc == 3 && !quoted[1]) { if (!ExprEval(argv[1], address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; return; } if (CompareString(argv[2], "X")) { CMD_RANGE_ADDR_MODE(mode, DIRECT_PAGE_INDEX_X, ABSOLUTE_INDEX_X, ABSOLUTE_LONG_INDEX_X, address); } else if (CompareString(argv[2], "Y")) { CMD_RANGE_ADDR_MODE(mode, DIRECT_PAGE_INDEX_X, ABSOLUTE_INDEX_X, ABSOLUTE_LONG_INDEX_X, address); } else { snprintf(err, errsize, "unknown index register '%s'", argv[2]); *mode = ADDR_MODE_ERROR; return; } return; } /* (zp,x) or (ind) */ if (argc == 2 && quoted[1] == '(') { char *addr; if (!CompareEnd(argv[1], ",x")) { if (!ExprEval(argv[1], address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; return; } *mode = INDIRECT; return; } *mode = ZERO_PAGE_INDIRECT_X; addr = DupStr(argv[1]); *strchr(addr, ',') = 0; if (!ExprEval(addr, address)) { snprintf(err, errsize, "%s: expression error: %s", addr, ExprError()); *mode = ADDR_MODE_ERROR; free(addr); return; } free(addr); if (*address < 0 || *address > 255) { snprintf(err, errsize, "value %d outside of zero page", *address); *mode = ADDR_MODE_ERROR; return; } return; } /* (zp),y */ if (argc == 3 && quoted[1] == '(') { *mode = ZERO_PAGE_INDIRECT_Y; if (!CompareString(argv[2], "y")) { snprintf(err, errsize, "illegal index register '%s' used for " "zero-page indirect", argv[2]); *mode = ADDR_MODE_ERROR; return; } if (!ExprEval(argv[1], address)) { snprintf(err, errsize, "%s: expression error: %s", argv[1], ExprError()); *mode = ADDR_MODE_ERROR; return; } if (*address < 0 || *address > 255) { snprintf(err, errsize, "value %d outside of zero page", *address); *mode = ADDR_MODE_ERROR; return; } return; } } /* ---------------------------------------- COMMAND HANDLERS */ static CommandStatus DUMMY(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ACCUMULATOR: return CMD_OK; case IMPLIED: return CMD_OK; case IMMEDIATE: return CMD_OK; case ABSOLUTE: return CMD_OK; case ZERO_PAGE: return CMD_OK; case ABSOLUTE_INDEX_X: return CMD_OK; case ABSOLUTE_INDEX_Y: return CMD_OK; case ZERO_PAGE_INDEX_X: return CMD_OK; case ZERO_PAGE_INDEX_Y: return CMD_OK; case ZERO_PAGE_INDIRECT_X: return CMD_OK; case ZERO_PAGE_INDIRECT_Y: return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); return CMD_FAILED; } } static CommandStatus ADC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0x69); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0x6d); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x65); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x7d); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0x79); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x75); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0x61); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0x71); 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 AND(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0x29); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0x2d); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x25); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x3d); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0x39); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x35); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0x21); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0x31); 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 ASL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMPLIED: case ACCUMULATOR: PCWrite(0x0a); return CMD_OK; case ABSOLUTE: PCWrite(0x0e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x06); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x1e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x16); 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 BIT(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: PCWrite(0x2c); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x24); 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 CMP(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xc9); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xcd); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xc5); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0xdd); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0xd9); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0xd5); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0xc1); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0xd1); 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 CPX(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xe0); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xec); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xe4); 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 CPY(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xc0); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xcc); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xc4); 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 DEC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: PCWrite(0xce); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xc6); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0xde); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0xd6); 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 EOR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0x49); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0x4d); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x45); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x5d); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0x59); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x55); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0x41); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0x51); 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 INC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: PCWrite(0xee); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xe6); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0xfe); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0xf6); 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 JMP(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: case ZERO_PAGE: PCWrite(0x4c); PCWriteWord(address); return CMD_OK; case INDIRECT: PCWrite(0x6c); 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 JSR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: case ZERO_PAGE: PCWrite(0x20); 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 LDA(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xa9); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xad); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xa5); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0xbd); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0xb9); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0xb5); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0xa1); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0xb1); 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 LDX(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xa2); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xae); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xa6); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_Y: PCWrite(0xbe); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_Y: PCWrite(0xb6); 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 LDY(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xa0); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xac); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xa4); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0xbc); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0xb4); 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 LSR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMPLIED: case ACCUMULATOR: PCWrite(0x4a); return CMD_OK; case ABSOLUTE: PCWrite(0x4e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x46); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x5e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x56); 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 ORA(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0x09); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0x0d); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x05); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x1d); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0x19); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x15); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0x01); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0x11); 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 ROL(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMPLIED: case ACCUMULATOR: PCWrite(0x2a); return CMD_OK; case ABSOLUTE: PCWrite(0x2e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x26); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x3e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x36); 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 ROR(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMPLIED: case ACCUMULATOR: PCWrite(0x6a); return CMD_OK; case ABSOLUTE: PCWrite(0x6e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x66); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x7e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x76); 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 SBC(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case IMMEDIATE: PCWrite(0xe9); PCWrite(address); return CMD_OK; case ABSOLUTE: PCWrite(0xed); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0xe5); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0xfd); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0xf9); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0xf5); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0xe1); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0xf1); 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 STA(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: PCWrite(0x8d); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x85); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: PCWrite(0x9d); PCWriteWord(address); return CMD_OK; case ABSOLUTE_INDEX_Y: case ZERO_PAGE_INDEX_Y: PCWrite(0x99); PCWriteWord(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x95); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_X: PCWrite(0x81); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDIRECT_Y: PCWrite(0x91); 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 STX(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: PCWrite(0x8e); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x86); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_Y: if (address < 0 || address > 255) { snprintf(err, errsize, "%s: value %d outside of zero page", argv[0], address); return CMD_FAILED; } PCWrite(0x96); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDEX_Y: PCWrite(0x96); 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 STY(const char *label, int argc, char *argv[], int quoted[], char *err, size_t errsize) { address_mode_t mode; int address; CMD_ADDRESS_MODE(mode, address); switch(mode) { case ABSOLUTE: PCWrite(0x8c); PCWriteWord(address); return CMD_OK; case ZERO_PAGE: PCWrite(0x84); PCWrite(address); return CMD_OK; case ABSOLUTE_INDEX_X: if (address < 0 || address > 255) { snprintf(err, errsize, "%s: value %d outside of zero page", argv[0], address); return CMD_FAILED; } PCWrite(0x94); PCWrite(address); return CMD_OK; case ZERO_PAGE_INDEX_X: PCWrite(0x94); PCWrite(address); return CMD_OK; default: snprintf(err, errsize, "%s: unsupported addressing mode %s", argv[0], address_mode_name[mode]); 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", 0xea}, {"TXS", 0x9a}, {"TSX", 0xba}, {"PHA", 0x48}, {"PLA", 0x68}, {"PHP", 0x08}, {"PLP", 0x28}, {"CLC", 0x18}, {"SEC", 0x38}, {"CLI", 0x58}, {"SEI", 0x78}, {"CLV", 0xb8}, {"CLD", 0xd8}, {"SED", 0xf8}, {"BRK", 0x00}, {"TAX", 0xaa}, {"TXA", 0x8a}, {"DEX", 0xca}, {"INX", 0xe8}, {"TAY", 0xa8}, {"TYA", 0x98}, {"DEY", 0x88}, {"INY", 0xc8}, {"RTI", 0x40}, {"RTS", 0x60}, {NULL} }; static const OpcodeTable branch_opcodes[] = { {"BPL", 0x10}, {"BMI", 0x30}, {"BVC", 0x50}, {"BVS", 0x70}, {"BCC", 0x90}, {"BCS", 0xB0}, {"BNE", 0xD0}, {"BEQ", 0xF0}, {NULL} }; static const HandlerTable handler_table[] = { {"ADC", ADC}, {"AND", AND}, {"ASL", ASL}, {"BIT", BIT}, {"CMP", CMP}, {"CPX", CPX}, {"CPY", CPY}, {"DEC", DEC}, {"EOR", EOR}, {"INC", INC}, {"JMP", JMP}, {"JSR", JSR}, {"LDA", LDA}, {"LDX", LDX}, {"LDY", LDY}, {"LSR", LSR}, {"ORA", ORA}, {"ROL", ROL}, {"ROR", ROR}, {"SBC", SBC}, {"STA", STA}, {"STX", STX}, {"STY", STY}, {NULL} }; /* ---------------------------------------- PUBLIC FUNCTIONS */ void Init_65c816(void) { SetNeededPasses(3); } const ValueTable *Options_65c816(void) { return NULL; } CommandStatus SetOption_65c816(int opt, int argc, char *argv[], int quoted[], char *err, size_t errsize) { return CMD_OK; } CommandStatus Handler_65c816(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); offset = offset - (PC() + 2); if (IsFinalPass() && (offset < -128 || offset > 127)) { snprintf(err, errsize, "%s: Branch offset (%d) too big", argv[1], offset); 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 */