// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2020 Marco Baye // Have a look at "acme.c" for further info // // Mnemonics stuff #include "mnemo.h" #include "config.h" #include "alu.h" #include "cpu.h" #include "dynabuf.h" #include "global.h" #include "input.h" #include "output.h" #include "tree.h" #include "typesystem.h" // constants #define MNEMO_INITIALSIZE 8 // 4 + terminator should suffice // These values are needed to recognize addressing modes: // indexing: #define INDEX_NONE 0 // no index #define INDEX_S 1 // stack-indexed (",s" or ",sp"), for 65816 and 65ce02 #define INDEX_X 2 // x-indexed (",x") #define INDEX_Y 3 // y-indexed (",y") #define INDEX_Z 4 // z-indexed (",z"), only for 65ce02/4502/m65 // 5..7 are left for future expansion, 8 would need the AMB_INDEX macro below to be adjusted! // adress mode bits: #define AMB_IMPLIED (1u << 0) // no value given #define AMB_IMMEDIATE (1u << 1) // '#' at start #define AMB_INDIRECT (1u << 2) // value has at least one unnecessary pair of "()" #define AMB_LONGINDIRECT (1u << 3) // value is given in [] #define AMB_PREINDEX(idx) ((idx) << 4) // three bits for indexing inside () #define AMB_INDEX(idx) ((idx) << 7) // three bits for external indexing // end values (here, "absolute addressing" always includes zeropage addressing, because they look the same) #define IMPLIED_ADDRESSING AMB_IMPLIED #define IMMEDIATE_ADDRESSING AMB_IMMEDIATE #define ABSOLUTE_ADDRESSING 0 #define X_INDEXED_ADDRESSING AMB_INDEX(INDEX_X) #define Y_INDEXED_ADDRESSING AMB_INDEX(INDEX_Y) #define INDIRECT_ADDRESSING AMB_INDIRECT #define X_INDEXED_INDIRECT_ADDRESSING (AMB_PREINDEX(INDEX_X) | AMB_INDIRECT) #define INDIRECT_Y_INDEXED_ADDRESSING (AMB_INDIRECT | AMB_INDEX(INDEX_Y)) // only for 65ce02/4502/m65: #define INDIRECT_Z_INDEXED_ADDRESSING (AMB_INDIRECT | AMB_INDEX(INDEX_Z)) // for 65816 and 65ce02/4502: #define STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING (AMB_PREINDEX(INDEX_S) | AMB_INDIRECT | AMB_INDEX(INDEX_Y)) // only for 65816: #define STACK_INDEXED_ADDRESSING AMB_INDEX(INDEX_S) #define LONG_INDIRECT_ADDRESSING AMB_LONGINDIRECT #define LONG_INDIRECT_Y_INDEXED_ADDRESSING (AMB_LONGINDIRECT | AMB_INDEX(INDEX_Y)) // only for m65: #define LONG_INDIRECT_Z_INDEXED_ADDRESSING (AMB_LONGINDIRECT | AMB_INDEX(INDEX_Z)) // constant values, used to mark the possible parameter lengths of instructions. // Not all of the eight possible combinations are actually used, however (because of the supported CPUs). #define MAYBE______ 0 #define MAYBE_1____ NUMBER_FORCES_8 #define MAYBE___2__ NUMBER_FORCES_16 #define MAYBE_____3 NUMBER_FORCES_24 // The mnemonics are split up into groups, each group has its own function to be dealt with: enum mnemogroup { GROUP_ACCU, // main accumulator stuff, plus PEI Byte value = table index GROUP_MISC, // read-modify-write and others Byte value = table index GROUP_ALLJUMPS, // the jump instructions Byte value = table index GROUP_IMPLIEDONLY, // mnemonics using only implied addressing Byte value = opcode GROUP_RELATIVE8, // short branch instructions Byte value = opcode GROUP_BITBRANCH, // bbr0..7 and bbs0..7 Byte value = opcode GROUP_REL16_2, // 16bit relative to pc+2 Byte value = opcode GROUP_REL16_3, // 16bit relative to pc+3 Byte value = opcode GROUP_BOTHMOVES, // the "move" instructions MVP and MVN Byte value = opcode GROUP_ZPONLY, // rmb0..7, smb0..7, inw, dew Byte value = opcode GROUP_PREFIX, // NOP on m65 (throws error) Byte value = opcode }; // TODO: make sure groups like IMPLIEDONLY and ZPONLY output // "Mnemonic does not support this addressing mode" instead of // "Garbage data at end of statement". // TODO: maybe add GROUP_IMMEDIATEONLY? // (for RTN, REP, SEP, ANC, ALR, ARR, SBX, LXA, ANE, SAC, SIR) // save some space #define SCB static const unsigned char #define SCS static const unsigned short #define SCL static const unsigned long // Code tables for group GROUP_ACCU: // These tables are used for the main accumulator-related mnemonics. By reading // the mnemonic's byte value (from the mnemotable), the assembler finds out the // column to use here. The row depends on the used addressing mode. A zero // entry in these tables means that the combination of mnemonic and addressing // mode is illegal. // | 6502/65c02/65816/65ce02/4502/m65 | 65816 | NMOS 6502 undocumented opcodes | enum { IDX_ORA,IDXcORA,IDX16ORA,IDXeORA,IDXmORA,IDXmORQ,IDX_AND,IDXcAND,IDX16AND,IDXeAND,IDXmAND,IDXmANDQ,IDX_EOR,IDXcEOR,IDX16EOR,IDXeEOR,IDXmEOR,IDXmEORQ,IDX_ADC,IDXcADC,IDX16ADC,IDXeADC,IDXmADC,IDXmADCQ,IDX_STA,IDXcSTA,IDX16STA,IDXeSTA,IDXmSTA,IDXmSTQ,IDX_LDA,IDXcLDA,IDX16LDA,IDXeLDA,IDXmLDA,IDXmLDQ,IDX_CMP,IDXcCMP,IDX16CMP,IDXeCMP,IDXmCMP,IDXmCPQ,IDX_SBC,IDXcSBC,IDX16SBC,IDXeSBC,IDXmSBC,IDXmSBCQ,IDX16PEI,IDXuSLO,IDXuRLA,IDXuSRE,IDXuRRA,IDXuSAX,IDXuLAX,IDXuDCP,IDXuISC,IDXuSHA}; SCB accu_imm[] = { 0x09, 0x09, 0x09, 0x09, 0x09, 0, 0x29, 0x29, 0x29, 0x29, 0x29, 0, 0x49, 0x49, 0x49, 0x49, 0x49, 0, 0x69, 0x69, 0x69, 0x69, 0x69, 0, 0, 0, 0, 0, 0, 0, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // #$ff #$ffff SCL accu_abs[] = { 0x0d05, 0x0d05,0x0f0d05, 0x0d05, 0x0d05, 0x0d05, 0x2d25, 0x2d25,0x2f2d25, 0x2d25, 0x2d25, 0x2d25, 0x4d45, 0x4d45,0x4f4d45, 0x4d45, 0x4d45, 0x4d45, 0x6d65, 0x6d65,0x6f6d65, 0x6d65, 0x6d65, 0x6d65, 0x8d85, 0x8d85,0x8f8d85, 0x8d85, 0x8d85, 0x8d85, 0xada5, 0xada5,0xafada5, 0xada5, 0xada5, 0xada5, 0xcdc5, 0xcdc5,0xcfcdc5, 0xcdc5, 0xcdc5, 0xcdc5, 0xede5, 0xede5,0xefede5, 0xede5, 0xede5, 0xede5, 0, 0x0f07, 0x2f27, 0x4f47, 0x6f67, 0x8f87, 0xafa7, 0xcfc7, 0xefe7, 0}; // $ff $ffff $ffffff SCL accu_xabs[] = { 0x1d15, 0x1d15,0x1f1d15, 0x1d15, 0x1d15, 0, 0x3d35, 0x3d35,0x3f3d35, 0x3d35, 0x3d35, 0, 0x5d55, 0x5d55,0x5f5d55, 0x5d55, 0x5d55, 0, 0x7d75, 0x7d75,0x7f7d75, 0x7d75, 0x7d75, 0, 0x9d95, 0x9d95,0x9f9d95, 0x9d95, 0x9d95, 0, 0xbdb5, 0xbdb5,0xbfbdb5, 0xbdb5, 0xbdb5, 0xbdb5, 0xddd5, 0xddd5,0xdfddd5, 0xddd5, 0xddd5, 0, 0xfdf5, 0xfdf5,0xfffdf5, 0xfdf5, 0xfdf5, 0, 0, 0x1f17, 0x3f37, 0x5f57, 0x7f77, 0, 0, 0xdfd7, 0xfff7, 0}; // $ff,x $ffff,x $ffffff,x SCS accu_yabs[] = { 0x1900, 0x1900, 0x1900, 0x1900, 0x1900, 0, 0x3900, 0x3900, 0x3900, 0x3900, 0x3900, 0, 0x5900, 0x5900, 0x5900, 0x5900, 0x5900, 0, 0x7900, 0x7900, 0x7900, 0x7900, 0x7900, 0, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0, 0xb900, 0xb900, 0xb900, 0xb900, 0xb900, 0xb900, 0xd900, 0xd900, 0xd900, 0xd900, 0xd900, 0, 0xf900, 0xf900, 0xf900, 0xf900, 0xf900, 0, 0, 0x1b00, 0x3b00, 0x5b00, 0x7b00, 0x97, 0xbfb7, 0xdb00, 0xfb00, 0x9f00}; // $ff,y $ffff,y SCB accu_xind8[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0, 0x21, 0x21, 0x21, 0x21, 0x21, 0, 0x41, 0x41, 0x41, 0x41, 0x41, 0, 0x61, 0x61, 0x61, 0x61, 0x61, 0, 0x81, 0x81, 0x81, 0x81, 0x81, 0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3, 0}; // ($ff,x) SCB accu_indy8[] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0, 0x31, 0x31, 0x31, 0x31, 0x31, 0, 0x51, 0x51, 0x51, 0x51, 0x51, 0, 0x71, 0x71, 0x71, 0x71, 0x71, 0, 0x91, 0x91, 0x91, 0x91, 0x91, 0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0, 0, 0x13, 0x33, 0x53, 0x73, 0, 0xb3, 0xd3, 0xf3, 0x93}; // ($ff),y SCB accu_ind8[] = { 0, 0x12, 0x12, 0, 0, 0x12, 0, 0x32, 0x32, 0, 0, 0x32, 0, 0x52, 0x52, 0, 0, 0x52, 0, 0x72, 0x72, 0, 0, 0x72, 0, 0x92, 0x92, 0, 0, 0x92, 0, 0xb2, 0xb2, 0, 0, 0xb2, 0, 0xd2, 0xd2, 0, 0, 0xd2, 0, 0xf2, 0xf2, 0, 0, 0xf2, 0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff) SCB accu_sabs8[] = { 0, 0, 0x03, 0, 0, 0, 0, 0, 0x23, 0, 0, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0x63, 0, 0, 0, 0, 0, 0x83, 0, 0, 0, 0, 0, 0xa3, 0, 0, 0, 0, 0, 0xc3, 0, 0, 0, 0, 0, 0xe3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,s SCB accu_sindy8[] = { 0, 0, 0x13, 0, 0, 0, 0, 0, 0x33, 0, 0, 0, 0, 0, 0x53, 0, 0, 0, 0, 0, 0x73, 0, 0, 0, 0, 0, 0x93, 0x82, 0x82, 0, 0, 0, 0xb3, 0xe2, 0xe2, 0xe2, 0, 0, 0xd3, 0, 0, 0, 0, 0, 0xf3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff,s),y SCB accu_lind8[] = { 0, 0, 0x07, 0, 0, 0x12, 0, 0, 0x27, 0, 0, 0x32, 0, 0, 0x47, 0, 0, 0x52, 0, 0, 0x67, 0, 0, 0x72, 0, 0, 0x87, 0, 0, 0x92, 0, 0, 0xa7, 0, 0, 0xb2, 0, 0, 0xc7, 0, 0, 0xd2, 0, 0, 0xe7, 0, 0, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff] SCB accu_lindy8[] = { 0, 0, 0x17, 0, 0, 0, 0, 0, 0x37, 0, 0, 0, 0, 0, 0x57, 0, 0, 0, 0, 0, 0x77, 0, 0, 0, 0, 0, 0x97, 0, 0, 0, 0, 0, 0xb7, 0, 0, 0, 0, 0, 0xd7, 0, 0, 0, 0, 0, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff],y SCB accu_indz8[] = { 0, 0, 0, 0x12, 0x12, 0, 0, 0, 0, 0x32, 0x32, 0, 0, 0, 0, 0x52, 0x52, 0, 0, 0, 0, 0x72, 0x72, 0, 0, 0, 0, 0x92, 0x92, 0, 0, 0, 0, 0xb2, 0xb2, 0, 0, 0, 0, 0xd2, 0xd2, 0, 0, 0, 0, 0xf2, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff),z SCB accu_lindz8[] = { 0, 0, 0, 0, 0x12, 0, 0, 0, 0, 0, 0x32, 0, 0, 0, 0, 0, 0x52, 0, 0, 0, 0, 0, 0x72, 0, 0, 0, 0, 0, 0x92, 0, 0, 0, 0, 0, 0xb2, 0, 0, 0, 0, 0, 0xd2, 0, 0, 0, 0, 0, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff],z (encoded as a NOP plus ($ff),z) // Code tables for group GROUP_MISC: // These tables are needed for finding out the correct code in cases when // there are no general rules. By reading the mnemonic's byte value (from the // mnemotable), the assembler finds out the column to use here. The row // depends on the used addressing mode. A zero entry in these tables means // that the combination of mnemonic and addressing mode is illegal. // | 6502 | 6502/65c02/65ce02/m65 | 65c02 | 65ce02 | 65816 | NMOS 6502 undocumented opcodes | C64DTV2 | enum { IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_BIT,IDXcBIT,IDXmBITQ,IDX_STX,IDXeSTX,IDX_STY,IDXeSTY,IDX_DEC,IDXcDEC,IDX_INC,IDXcINC,IDXcTSB,IDXcTRB,IDXcSTZ,IDXeASR,IDXeASW,IDXeCPZ,IDXeLDZ,IDXePHW,IDXeROW,IDXeRTN,IDX16COP,IDX16REP,IDX16SEP,IDX16PEA,IDXuANC,IDXuALR,IDXuARR,IDXuSBX,IDXuNOP,IDXuDOP,IDXuTOP,IDXuLXA,IDXuANE,IDXuLAS,IDXuTAS,IDXuSHX,IDXuSHY,IDX_SAC,IDX_SIR}; SCB misc_impl[] = { 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0, 0x1a, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xea, 0x80, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0}; // implied/accu SCB misc_imm[] = { 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc2, 0xa3, 0xf4, 0, 0x62, /*2?*/0, 0xc2, 0xe2, 0, 0x0b, 0x4b, 0x6b, 0xcb, 0x80, 0x80, 0, 0xab, 0x8b, 0, 0, 0, 0, 0x32, 0x42}; // #$ff #$ffff SCS misc_abs[] = { 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0x2c24, 0x2c24, 0x2c24, 0x8e86, 0x8e86, 0x8c84, 0x8c84, 0xcec6, 0xcec6, 0xeee6, 0xeee6, 0x0c04, 0x1c14, 0x9c64, 0x44, 0xcb00, 0xdcd4, 0xab00, 0xfc00, 0xeb00, 0, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x0c04, 0x04, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff $ffff SCS misc_xabs[] = { 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0xbcb4, 0, 0, 0, 0, 0x3c34, 0, 0, 0, 0x94, 0x8b94, 0xded6, 0xded6, 0xfef6, 0xfef6, 0, 0, 0x9e74, 0x54, 0, 0, 0xbb00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1c14, 0x14, 0x1c00, 0, 0, 0, 0, 0, 0x9c00, 0, 0}; // $ff,x $ffff,x SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0xbeb6, 0, 0, 0, 0, 0, 0x96, 0x9b96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbb00, 0x9b00, 0x9e00, 0, 0, 0}; // $ff,y $ffff,y // Code tables for group GROUP_ALLJUMPS: // These tables are needed for finding out the correct code when the mnemonic // is "jmp" or "jsr" (or the long versions "jml" and "jsl"). // By reading the mnemonic's byte value (from the mnemotable), the assembler // finds out the column to use here. The row depends on the used addressing // mode. A zero entry in these tables means that the combination of mnemonic // and addressing mode is illegal. // | 6502/65c02/65816/65ce02 | 65816 | enum { IDX_JMP,IDXcJMP,IDX16JMP,IDX_JSR,IDXeJSR,IDX16JSR,IDX16JML,IDX16JSL}; SCL jump_abs[] = { 0x4c00, 0x4c00,0x5c4c00, 0x2000, 0x2000,0x222000,0x5c0000,0x220000}; // $ffff $ffffff SCS jump_ind[] = { 0x6c00, 0x6c00, 0x6c00, 0, 0x2200, 0, 0, 0}; // ($ffff) SCS jump_xind[] = { 0, 0x7c00, 0x7c00, 0, 0x2300, 0xfc00, 0, 0}; // ($ffff,x) SCS jump_lind[] = { 0, 0, 0xdc00, 0, 0, 0, 0xdc00, 0}; // [$ffff] #undef SCB #undef SCS #undef SCL // error message strings static const char exception_illegal_combination[] = "CPU does not support this addressing mode for this mnemonic."; static const char exception_oversized_addrmode[] = "Using oversized addressing mode."; // Variables static STRUCT_DYNABUF_REF(mnemo_dyna_buf, MNEMO_INITIALSIZE); // for mnemonics // mnemonic's code, flags and group values are stored together in a single integer. // ("code" is either a table index or the opcode itself, depending on group value) // To extract the code, use "& CODEMASK". // To extract the flags, use "& FLAGSMASK". // To extract only the immediate mode flags, use "& IMMASK". // To extract the group, use GROUP() #define CODEMASK 0x0ff // opcode or table index // immediate mode: #define IM_FORCE8 0x000 // immediate values are 8 bits (CAUTION - program relies on "no bits set" being the default!) #define IM_FORCE16 0x100 // immediate value is 16 bits (for 65ce02's PHW#) #define IM_ACCUMULATOR 0x200 // immediate value depends on accumulator length #define IM_INDEXREGS 0x300 // immediate value depends on index register length #define IMMASK 0x300 // mask for immediate mode flags #define PREFIX_NEGNEG 0x400 // output NEG:NEG before actual opcode #define LI_PREFIX_NOP 0x800 // when using long indirect addressing, output NOP before actual opcode #define FLAGSMASK 0xf00 // mask for all four flags #define MERGE(g, v) (((g) << 12) | (v)) #define GROUP(v) ((v) >> 12) // 6502 mnemonics static struct ronode mnemo_6502_tree[] = { PREDEF_START, PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX_ORA)), PREDEFNODE("and", MERGE(GROUP_ACCU, IDX_AND)), PREDEFNODE("eor", MERGE(GROUP_ACCU, IDX_EOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX_ADC)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX_STA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX_LDA)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX_CMP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX_SBC)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDX_BIT)), PREDEFNODE("asl", MERGE(GROUP_MISC, IDX_ASL)), PREDEFNODE("rol", MERGE(GROUP_MISC, IDX_ROL)), PREDEFNODE("lsr", MERGE(GROUP_MISC, IDX_LSR)), PREDEFNODE("ror", MERGE(GROUP_MISC, IDX_ROR)), PREDEFNODE("sty", MERGE(GROUP_MISC, IDX_STY)), PREDEFNODE("stx", MERGE(GROUP_MISC, IDX_STX)), PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY)), PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX)), PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY)), PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX)), PREDEFNODE("dec", MERGE(GROUP_MISC, IDX_DEC)), PREDEFNODE("inc", MERGE(GROUP_MISC, IDX_INC)), PREDEFNODE("bpl", MERGE(GROUP_RELATIVE8, 0x10)), PREDEFNODE("bmi", MERGE(GROUP_RELATIVE8, 0x30)), PREDEFNODE("bvc", MERGE(GROUP_RELATIVE8, 0x50)), PREDEFNODE("bvs", MERGE(GROUP_RELATIVE8, 0x70)), PREDEFNODE("bcc", MERGE(GROUP_RELATIVE8, 0x90)), PREDEFNODE("bcs", MERGE(GROUP_RELATIVE8, 0xb0)), PREDEFNODE("bne", MERGE(GROUP_RELATIVE8, 0xd0)), PREDEFNODE("beq", MERGE(GROUP_RELATIVE8, 0xf0)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX_JMP)), PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX_JSR)), PREDEFNODE("brk", MERGE(GROUP_IMPLIEDONLY, 0x00)), PREDEFNODE("php", MERGE(GROUP_IMPLIEDONLY, 0x08)), PREDEFNODE("clc", MERGE(GROUP_IMPLIEDONLY, 0x18)), PREDEFNODE("plp", MERGE(GROUP_IMPLIEDONLY, 0x28)), PREDEFNODE("sec", MERGE(GROUP_IMPLIEDONLY, 0x38)), PREDEFNODE("rti", MERGE(GROUP_IMPLIEDONLY, 0x40)), PREDEFNODE("pha", MERGE(GROUP_IMPLIEDONLY, 0x48)), PREDEFNODE("cli", MERGE(GROUP_IMPLIEDONLY, 0x58)), PREDEFNODE("rts", MERGE(GROUP_IMPLIEDONLY, 0x60)), PREDEFNODE("pla", MERGE(GROUP_IMPLIEDONLY, 0x68)), PREDEFNODE("sei", MERGE(GROUP_IMPLIEDONLY, 0x78)), PREDEFNODE("dey", MERGE(GROUP_IMPLIEDONLY, 0x88)), PREDEFNODE("txa", MERGE(GROUP_IMPLIEDONLY, 0x8a)), PREDEFNODE("tya", MERGE(GROUP_IMPLIEDONLY, 0x98)), PREDEFNODE("txs", MERGE(GROUP_IMPLIEDONLY, 0x9a)), PREDEFNODE("tay", MERGE(GROUP_IMPLIEDONLY, 0xa8)), PREDEFNODE("tax", MERGE(GROUP_IMPLIEDONLY, 0xaa)), PREDEFNODE("clv", MERGE(GROUP_IMPLIEDONLY, 0xb8)), PREDEFNODE("tsx", MERGE(GROUP_IMPLIEDONLY, 0xba)), PREDEFNODE("iny", MERGE(GROUP_IMPLIEDONLY, 0xc8)), PREDEFNODE("dex", MERGE(GROUP_IMPLIEDONLY, 0xca)), PREDEFNODE("cld", MERGE(GROUP_IMPLIEDONLY, 0xd8)), PREDEFNODE("inx", MERGE(GROUP_IMPLIEDONLY, 0xe8)), PREDEFNODE("nop", MERGE(GROUP_IMPLIEDONLY, 0xea)), PREDEF_END("sed", MERGE(GROUP_IMPLIEDONLY, 0xf8)), // ^^^^ this marks the last element }; // undocumented opcodes of the NMOS 6502 that are also supported by c64dtv2: static struct ronode mnemo_6502undoc1_tree[] = { PREDEF_START, PREDEFNODE("slo", MERGE(GROUP_ACCU, IDXuSLO)), // ASL + ORA (aka ASO) PREDEFNODE("rla", MERGE(GROUP_ACCU, IDXuRLA)), // ROL + AND (aka RLN) PREDEFNODE("sre", MERGE(GROUP_ACCU, IDXuSRE)), // LSR + EOR (aka LSE) PREDEFNODE("rra", MERGE(GROUP_ACCU, IDXuRRA)), // ROR + ADC (aka RRD) PREDEFNODE("sax", MERGE(GROUP_ACCU, IDXuSAX)), // store A & X (aka AXS/AAX) PREDEFNODE("lax", MERGE(GROUP_ACCU, IDXuLAX)), // LDX + LDA PREDEFNODE("dcp", MERGE(GROUP_ACCU, IDXuDCP)), // DEC + CMP (aka DCM) PREDEFNODE("isc", MERGE(GROUP_ACCU, IDXuISC)), // INC + SBC (aka ISB/INS) PREDEFNODE("las", MERGE(GROUP_MISC, IDXuLAS)), // A,X,S = {addr} & S (aka LAR/LAE) PREDEFNODE("tas", MERGE(GROUP_MISC, IDXuTAS)), // S = A & X {addr} = A&X& {H+1} (aka SHS/XAS) PREDEFNODE("sha", MERGE(GROUP_ACCU, IDXuSHA)), // {addr} = A & X & {H+1} (aka AXA/AHX) PREDEFNODE("shx", MERGE(GROUP_MISC, IDXuSHX)), // {addr} = X & {H+1} (aka XAS/SXA) PREDEFNODE("shy", MERGE(GROUP_MISC, IDXuSHY)), // {addr} = Y & {H+1} (aka SAY/SYA) PREDEFNODE("alr", MERGE(GROUP_MISC, IDXuALR)), // A = A & arg, then LSR (aka ASR) PREDEFNODE("asr", MERGE(GROUP_MISC, IDXuALR)), // A = A & arg, then LSR (aka ALR) PREDEFNODE("arr", MERGE(GROUP_MISC, IDXuARR)), // A = A & arg, then ROR PREDEFNODE("sbx", MERGE(GROUP_MISC, IDXuSBX)), // X = (A & X) - arg (aka AXS/SAX) PREDEFNODE("nop", MERGE(GROUP_MISC, IDXuNOP)), // combines documented $ea and the undocumented dop/top below PREDEFNODE("dop", MERGE(GROUP_MISC, IDXuDOP)), // "double nop" (skip next byte) PREDEFNODE("top", MERGE(GROUP_MISC, IDXuTOP)), // "triple nop" (skip next word) PREDEFNODE("ane", MERGE(GROUP_MISC, IDXuANE)), // A = (A | ??) & X & arg (aka XAA/AXM) PREDEFNODE("lxa", MERGE(GROUP_MISC, IDXuLXA)), // A,X = (A | ??) & arg (aka LAX/ATX/OAL) PREDEF_END("jam", MERGE(GROUP_IMPLIEDONLY, 0x02)), // jam/crash/kill/halt-and-catch-fire // ^^^^ this marks the last element }; // undocumented opcodes of the NMOS 6502 that are _not_ supported by c64dtv2: // (currently ANC only, maybe more will get moved) static struct ronode mnemo_6502undoc2_tree[] = { PREDEF_START, PREDEF_END("anc", MERGE(GROUP_MISC, IDXuANC)), // A = A & arg, then C=N (aka ANA, ANB) // ^^^^ this marks the last element }; // additional opcodes of c64dtv2: static struct ronode mnemo_c64dtv2_tree[] = { PREDEF_START, PREDEFNODE("bra", MERGE(GROUP_RELATIVE8, 0x12)), // branch always PREDEFNODE("sac", MERGE(GROUP_MISC, IDX_SAC)), // set accumulator mapping PREDEF_END("sir", MERGE(GROUP_MISC, IDX_SIR)), // set index register mapping // ^^^^ this marks the last element }; // new stuff in CMOS re-design: static struct ronode mnemo_65c02_tree[] = { PREDEF_START, // more addressing modes for some mnemonics: PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXcORA)), PREDEFNODE("and", MERGE(GROUP_ACCU, IDXcAND)), PREDEFNODE("eor", MERGE(GROUP_ACCU, IDXcEOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXcADC)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXcSTA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXcLDA)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXcCMP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXcSBC)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDXcJMP)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT)), PREDEFNODE("dec", MERGE(GROUP_MISC, IDXcDEC)), PREDEFNODE("inc", MERGE(GROUP_MISC, IDXcINC)), // and eight new mnemonics: PREDEFNODE("bra", MERGE(GROUP_RELATIVE8, 0x80)), PREDEFNODE("phy", MERGE(GROUP_IMPLIEDONLY, 0x5a)), PREDEFNODE("ply", MERGE(GROUP_IMPLIEDONLY, 0x7a)), PREDEFNODE("phx", MERGE(GROUP_IMPLIEDONLY, 0xda)), PREDEFNODE("plx", MERGE(GROUP_IMPLIEDONLY, 0xfa)), PREDEFNODE("tsb", MERGE(GROUP_MISC, IDXcTSB)), PREDEFNODE("trb", MERGE(GROUP_MISC, IDXcTRB)), PREDEF_END("stz", MERGE(GROUP_MISC, IDXcSTZ)), // ^^^^ this marks the last element }; // bit-manipulation extensions (by Rockwell?) static struct ronode mnemo_bitmanips_tree[] = { PREDEF_START, PREDEFNODE("rmb0", MERGE(GROUP_ZPONLY, 0x07)), PREDEFNODE("rmb1", MERGE(GROUP_ZPONLY, 0x17)), PREDEFNODE("rmb2", MERGE(GROUP_ZPONLY, 0x27)), PREDEFNODE("rmb3", MERGE(GROUP_ZPONLY, 0x37)), PREDEFNODE("rmb4", MERGE(GROUP_ZPONLY, 0x47)), PREDEFNODE("rmb5", MERGE(GROUP_ZPONLY, 0x57)), PREDEFNODE("rmb6", MERGE(GROUP_ZPONLY, 0x67)), PREDEFNODE("rmb7", MERGE(GROUP_ZPONLY, 0x77)), PREDEFNODE("smb0", MERGE(GROUP_ZPONLY, 0x87)), PREDEFNODE("smb1", MERGE(GROUP_ZPONLY, 0x97)), PREDEFNODE("smb2", MERGE(GROUP_ZPONLY, 0xa7)), PREDEFNODE("smb3", MERGE(GROUP_ZPONLY, 0xb7)), PREDEFNODE("smb4", MERGE(GROUP_ZPONLY, 0xc7)), PREDEFNODE("smb5", MERGE(GROUP_ZPONLY, 0xd7)), PREDEFNODE("smb6", MERGE(GROUP_ZPONLY, 0xe7)), PREDEFNODE("smb7", MERGE(GROUP_ZPONLY, 0xf7)), PREDEFNODE("bbr0", MERGE(GROUP_BITBRANCH, 0x0f)), PREDEFNODE("bbr1", MERGE(GROUP_BITBRANCH, 0x1f)), PREDEFNODE("bbr2", MERGE(GROUP_BITBRANCH, 0x2f)), PREDEFNODE("bbr3", MERGE(GROUP_BITBRANCH, 0x3f)), PREDEFNODE("bbr4", MERGE(GROUP_BITBRANCH, 0x4f)), PREDEFNODE("bbr5", MERGE(GROUP_BITBRANCH, 0x5f)), PREDEFNODE("bbr6", MERGE(GROUP_BITBRANCH, 0x6f)), PREDEFNODE("bbr7", MERGE(GROUP_BITBRANCH, 0x7f)), PREDEFNODE("bbs0", MERGE(GROUP_BITBRANCH, 0x8f)), PREDEFNODE("bbs1", MERGE(GROUP_BITBRANCH, 0x9f)), PREDEFNODE("bbs2", MERGE(GROUP_BITBRANCH, 0xaf)), PREDEFNODE("bbs3", MERGE(GROUP_BITBRANCH, 0xbf)), PREDEFNODE("bbs4", MERGE(GROUP_BITBRANCH, 0xcf)), PREDEFNODE("bbs5", MERGE(GROUP_BITBRANCH, 0xdf)), PREDEFNODE("bbs6", MERGE(GROUP_BITBRANCH, 0xef)), PREDEF_END("bbs7", MERGE(GROUP_BITBRANCH, 0xff)), // ^^^^ this marks the last element }; // "stp" and "wai" extensions by WDC: static struct ronode mnemo_stp_wai_tree[] = { PREDEF_START, PREDEFNODE("wai", MERGE(GROUP_IMPLIEDONLY, 0xcb)), PREDEF_END("stp", MERGE(GROUP_IMPLIEDONLY, 0xdb)), // ^^^^ this marks the last element }; // the 65816 stuff static struct ronode mnemo_65816_tree[] = { PREDEF_START, // CAUTION - these use 6502/65c02 indices, because the opcodes are the same - but I need flags for immediate mode! PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY | IM_INDEXREGS)), PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX | IM_INDEXREGS)), PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY | IM_INDEXREGS)), PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX | IM_INDEXREGS)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT | IM_ACCUMULATOR)), // more addressing modes for some mnemonics: PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX16ORA | IM_ACCUMULATOR)), PREDEFNODE("and", MERGE(GROUP_ACCU, IDX16AND | IM_ACCUMULATOR)), PREDEFNODE("eor", MERGE(GROUP_ACCU, IDX16EOR | IM_ACCUMULATOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX16ADC | IM_ACCUMULATOR)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX16STA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX16LDA | IM_ACCUMULATOR)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX16CMP | IM_ACCUMULATOR)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX16SBC | IM_ACCUMULATOR)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX16JMP)), PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX16JSR)), // PREDEFNODE("pei", MERGE(GROUP_ACCU, IDX16PEI)), PREDEFNODE("jml", MERGE(GROUP_ALLJUMPS, IDX16JML)), PREDEFNODE("jsl", MERGE(GROUP_ALLJUMPS, IDX16JSL)), PREDEFNODE("mvp", MERGE(GROUP_BOTHMOVES, 0x44)), PREDEFNODE("mvn", MERGE(GROUP_BOTHMOVES, 0x54)), PREDEFNODE("per", MERGE(GROUP_REL16_3, 0x62)), PREDEFNODE("brl", MERGE(GROUP_REL16_3, 0x82)), PREDEFNODE("cop", MERGE(GROUP_MISC, IDX16COP)), PREDEFNODE("rep", MERGE(GROUP_MISC, IDX16REP)), PREDEFNODE("sep", MERGE(GROUP_MISC, IDX16SEP)), PREDEFNODE("pea", MERGE(GROUP_MISC, IDX16PEA)), PREDEFNODE("phd", MERGE(GROUP_IMPLIEDONLY, 0x0b)), PREDEFNODE("tcs", MERGE(GROUP_IMPLIEDONLY, 0x1b)), PREDEFNODE("pld", MERGE(GROUP_IMPLIEDONLY, 0x2b)), PREDEFNODE("tsc", MERGE(GROUP_IMPLIEDONLY, 0x3b)), PREDEFNODE("phk", MERGE(GROUP_IMPLIEDONLY, 0x4b)), PREDEFNODE("tcd", MERGE(GROUP_IMPLIEDONLY, 0x5b)), PREDEFNODE("rtl", MERGE(GROUP_IMPLIEDONLY, 0x6b)), PREDEFNODE("tdc", MERGE(GROUP_IMPLIEDONLY, 0x7b)), PREDEFNODE("phb", MERGE(GROUP_IMPLIEDONLY, 0x8b)), PREDEFNODE("txy", MERGE(GROUP_IMPLIEDONLY, 0x9b)), PREDEFNODE("plb", MERGE(GROUP_IMPLIEDONLY, 0xab)), PREDEFNODE("tyx", MERGE(GROUP_IMPLIEDONLY, 0xbb)), // 0xcb is WAI // 0xdb is STP PREDEFNODE("xba", MERGE(GROUP_IMPLIEDONLY, 0xeb)), PREDEFNODE("xce", MERGE(GROUP_IMPLIEDONLY, 0xfb)), PREDEF_END("wdm", MERGE(GROUP_IMPLIEDONLY, 0x42)), // ^^^^ this marks the last element }; // 65ce02 has 46 new opcodes and a few changes: static struct ronode mnemo_65ce02_tree[] = { PREDEF_START, // 65ce02 changes (zp) addressing of 65c02 to (zp),z addressing: PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXeORA)), PREDEFNODE("and", MERGE(GROUP_ACCU, IDXeAND)), PREDEFNODE("eor", MERGE(GROUP_ACCU, IDXeEOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXeADC)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXeSTA)), // +1 for (8,s),y PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXeLDA)), // +1 for (8,s),y PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXeCMP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXeSBC)), // more addressing modes: PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDXeJSR)), // +2 PREDEFNODE("stx", MERGE(GROUP_MISC, IDXeSTX)), // +1 PREDEFNODE("sty", MERGE(GROUP_MISC, IDXeSTY)), // +1 // +10 long branches (8 normal, 1 unconditional, BSR uncond to subroutine)) PREDEFNODE("lbpl", MERGE(GROUP_REL16_2, 0x13)), PREDEFNODE("lbmi", MERGE(GROUP_REL16_2, 0x33)), PREDEFNODE("lbvc", MERGE(GROUP_REL16_2, 0x53)), PREDEFNODE("lbvs", MERGE(GROUP_REL16_2, 0x73)), PREDEFNODE("lbcc", MERGE(GROUP_REL16_2, 0x93)), PREDEFNODE("lbcs", MERGE(GROUP_REL16_2, 0xb3)), PREDEFNODE("lbne", MERGE(GROUP_REL16_2, 0xd3)), PREDEFNODE("lbeq", MERGE(GROUP_REL16_2, 0xf3)), PREDEFNODE("bru", MERGE(GROUP_RELATIVE8, 0x80)), // alias for 65c02's "bra" PREDEFNODE("bsr", MERGE(GROUP_REL16_2, 0x63)), PREDEFNODE("lbru", MERGE(GROUP_REL16_2, 0x83)), PREDEFNODE("lbra", MERGE(GROUP_REL16_2, 0x83)), // alias // new mnemonics: PREDEFNODE("asr", MERGE(GROUP_MISC, IDXeASR)), PREDEFNODE("asw", MERGE(GROUP_MISC, IDXeASW)), PREDEFNODE("cpz", MERGE(GROUP_MISC, IDXeCPZ)), PREDEFNODE("dew", MERGE(GROUP_ZPONLY, 0xc3)), PREDEFNODE("inw", MERGE(GROUP_ZPONLY, 0xe3)), PREDEFNODE("ldz", MERGE(GROUP_MISC, IDXeLDZ)), PREDEFNODE("phw", MERGE(GROUP_MISC, IDXePHW | IM_FORCE16)), // when using immediate addressing, arg is 16 bit PREDEFNODE("row", MERGE(GROUP_MISC, IDXeROW)), PREDEFNODE("rtn", MERGE(GROUP_MISC, IDXeRTN)), PREDEFNODE("cle", MERGE(GROUP_IMPLIEDONLY, 0x02)), PREDEFNODE("see", MERGE(GROUP_IMPLIEDONLY, 0x03)), PREDEFNODE("inz", MERGE(GROUP_IMPLIEDONLY, 0x1b)), PREDEFNODE("dez", MERGE(GROUP_IMPLIEDONLY, 0x3b)), PREDEFNODE("neg", MERGE(GROUP_IMPLIEDONLY, 0x42)), PREDEFNODE("tsy", MERGE(GROUP_IMPLIEDONLY, 0x0b)), PREDEFNODE("tys", MERGE(GROUP_IMPLIEDONLY, 0x2b)), PREDEFNODE("taz", MERGE(GROUP_IMPLIEDONLY, 0x4b)), PREDEFNODE("tab", MERGE(GROUP_IMPLIEDONLY, 0x5b)), PREDEFNODE("tza", MERGE(GROUP_IMPLIEDONLY, 0x6b)), PREDEFNODE("tba", MERGE(GROUP_IMPLIEDONLY, 0x7b)), PREDEFNODE("phz", MERGE(GROUP_IMPLIEDONLY, 0xdb)), PREDEF_END("plz", MERGE(GROUP_IMPLIEDONLY, 0xfb)), // ^^^^ this marks the last element }; // 65ce02's "aug" opcode: static struct ronode mnemo_aug_tree[] = { PREDEF_START, PREDEF_END("aug", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // actually a "4-byte NOP reserved for future expansion" // ^^^^ this marks the last element }; // 4502's "map" and "eom" opcodes: static struct ronode mnemo_map_eom_tree[] = { PREDEF_START, PREDEFNODE("map", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // change memory mapping PREDEF_END("eom", MERGE(GROUP_IMPLIEDONLY, 0xea)), // actually the NOP opcode // ^^^^ this marks the last element }; // m65 has a few extensions using prefix codes: static struct ronode mnemo_m65_tree[] = { PREDEF_START, // extension 1: // a NOP prefix changes ($ff),z addressing from using 16-bit pointers to // using 32-bit pointers. I chose "[$ff],z" to indicate this. // this applies to LDA/STA/CMP/ADC/SBC/AND/EOR/ORA // extension 2: // a NEG:NEG prefix causes the next instruction to use the four A/X/Y/Z // registers as a single 32-bit register (A is the lsb, Z is the msb). // it works with these mnemonics: // LDA/STA/CMP/ADC/SBC/AND/EOR/ORA // ...now LDQ/STQ/CPQ/ADCQ/SBCQ/ANDQ/EORQ/ORQ // ASL/LSR/ROL/ROR // ...now ASLQ/LSRQ/ROLQ/RORQ // INC/DEC // ...now INQ/DEQ // BIT // ...now BITQ // ASR // ...now ASRQ // it works with most addressing modes (beware of index register usage!) // except for immediate addressing and "($ff),z", which becomes "($ff)" // extension 3: // extensions 1 and 2 can be combined (NEG:NEG:NOP prefix), then // "[$ff],z" becomes "[$ff]" PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXmLDA | LI_PREFIX_NOP)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXmSTA | LI_PREFIX_NOP)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXmCMP | LI_PREFIX_NOP)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXmADC | LI_PREFIX_NOP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXmSBC | LI_PREFIX_NOP)), PREDEFNODE("and", MERGE(GROUP_ACCU, IDXmAND | LI_PREFIX_NOP)), PREDEFNODE("eor", MERGE(GROUP_ACCU, IDXmEOR | LI_PREFIX_NOP)), PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXmORA | LI_PREFIX_NOP)), PREDEFNODE("ldq", MERGE(GROUP_ACCU, IDXmLDQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("stq", MERGE(GROUP_ACCU, IDXmSTQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("cpq", MERGE(GROUP_ACCU, IDXmCPQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("adcq", MERGE(GROUP_ACCU, IDXmADCQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("sbcq", MERGE(GROUP_ACCU, IDXmSBCQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("andq", MERGE(GROUP_ACCU, IDXmANDQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("eorq", MERGE(GROUP_ACCU, IDXmEORQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), PREDEFNODE("orq", MERGE(GROUP_ACCU, IDXmORQ | PREFIX_NEGNEG | LI_PREFIX_NOP)), // CAUTION - these use 6502/65c02 indices, because the available addressing modes and opcodes are identical: PREDEFNODE("aslq", MERGE(GROUP_MISC, IDX_ASL | PREFIX_NEGNEG)), PREDEFNODE("lsrq", MERGE(GROUP_MISC, IDX_LSR | PREFIX_NEGNEG)), PREDEFNODE("rolq", MERGE(GROUP_MISC, IDX_ROL | PREFIX_NEGNEG)), PREDEFNODE("rorq", MERGE(GROUP_MISC, IDX_ROR | PREFIX_NEGNEG)), PREDEFNODE("inq", MERGE(GROUP_MISC, IDXcINC | PREFIX_NEGNEG)), PREDEFNODE("deq", MERGE(GROUP_MISC, IDXcDEC | PREFIX_NEGNEG)), PREDEFNODE("bitq", MERGE(GROUP_MISC, IDXmBITQ | PREFIX_NEGNEG)), PREDEFNODE("asrq", MERGE(GROUP_MISC, IDXeASR | PREFIX_NEGNEG)), // because the NOP opcode is used as a prefix code, the mnemonic was disabled: PREDEF_END("nop", MERGE(GROUP_PREFIX, 0xea)), // ^^^^ this marks the last element }; // Functions // Address mode parsing // utility function for parsing indices. result must be processed via AMB_PREINDEX() or AMB_INDEX() macro! // TODO: add pointer arg for result, use return value to indicate parse error! static int get_index(void) { if (!Input_accept_comma()) return INDEX_NONE; // there was a comma, so check next character (spaces will have been skipped): switch (GotByte) { case 's': case 'S': // if next character is 'p' or 'P', eat that as well, so ",sp" works just like ",s" GetByte(); if ((GotByte == 'p') || (GotByte == 'P')) GetByte(); SKIPSPACE(); return INDEX_S; case 'x': case 'X': NEXTANDSKIPSPACE(); return INDEX_X; case 'y': case 'Y': NEXTANDSKIPSPACE(); return INDEX_Y; case 'z': case 'Z': NEXTANDSKIPSPACE(); return INDEX_Z; } Throw_error(exception_syntax); return INDEX_NONE; } // wrapper function to read integer argument static void get_int_arg(struct number *result, boolean complain_about_indirect) { struct expression expression; ALU_addrmode_int(&expression, 0); // accept 0 parentheses still open (-> complain!) if (expression.is_parenthesized && complain_about_indirect) { // TODO - raise error and be done with it? Throw_first_pass_warning("There are unneeded parentheses, you know indirect addressing is impossible here, right?"); // FIXME - rephrase? add to docs! } *result = expression.result.u.number; } // wrapper function to detect addressing mode, and, if not IMPLIED, read arg. // argument is stored in given result structure, addressing mode is returned. // TODO: add pointer arg for result, use return value to indicate parse error! static bits get_addr_mode(struct number *result) { struct expression expression; bits address_mode_bits = 0; SKIPSPACE(); switch (GotByte) { case CHAR_EOS: address_mode_bits = AMB_IMPLIED; break; case '#': GetByte(); // proceed with next char address_mode_bits = AMB_IMMEDIATE; get_int_arg(result, FALSE); typesystem_want_nonaddr(result); // FIXME - this is wrong for 65ce02's PHW# break; case '[': GetByte(); // proceed with next char get_int_arg(result, FALSE); typesystem_want_addr(result); if (GotByte == ']') { GetByte(); address_mode_bits = AMB_LONGINDIRECT | AMB_INDEX(get_index()); } else { Throw_error(exception_syntax); } break; default: ALU_addrmode_int(&expression, 1); // direct call instead of wrapper, to allow for "(EXPR,x)" where parsing stops at comma *result = expression.result.u.number; typesystem_want_addr(result); // check for indirect addressing if (expression.is_parenthesized) address_mode_bits |= AMB_INDIRECT; // check for internal index (before closing parenthesis) if (expression.open_parentheses) { // in case there are still open parentheses, // read internal index address_mode_bits |= AMB_PREINDEX(get_index()); if (GotByte == ')') { GetByte(); // go on with next char } else { Throw_error(exception_syntax); } } // check for external index (after closing parenthesis) address_mode_bits |= AMB_INDEX(get_index()); } // ensure end of line Input_ensure_EOS(); //printf("AM: %x\n", address_mode_bits); return address_mode_bits; } // Helper function for calc_arg_size() // Only call with "size_bit = NUMBER_FORCES_16" or "size_bit = NUMBER_FORCES_24" static bits check_oversize(bits size_bit, struct number *argument) { // only check if value is *defined* if (argument->ntype == NUMTYPE_UNDEFINED) return size_bit; // pass on result // value is defined, so check if (size_bit == NUMBER_FORCES_16) { // check 16-bit argument for high byte zero if ((argument->val.intval <= 255) && (argument->val.intval >= -128)) Throw_warning(exception_oversized_addrmode); } else { // check 24-bit argument for bank byte zero if ((argument->val.intval <= 65535) && (argument->val.intval >= -32768)) Throw_warning(exception_oversized_addrmode); } return size_bit; // pass on result } // Utility function for comparing force bits, argument value, argument size, // "unsure"-flag and possible addressing modes. Returns force bit matching // number of parameter bytes to send. If it returns zero, an error occurred // (and has already been delivered). // force_bit none, 8b, 16b, 24b // argument value and flags of parameter // addressing_modes adressing modes (8b, 16b, 24b or any combination) // Return value = force bit for number of parameter bytes to send (0 = error) // TODO: add pointer arg for result, use return value to indicate error ONLY! static bits calc_arg_size(bits force_bit, struct number *argument, bits addressing_modes) { // if there are no possible addressing modes, complain if (addressing_modes == MAYBE______) { Throw_error(exception_illegal_combination); return 0; } // if a force bit postfix was given, act upon it if (force_bit) { // if mnemonic supports this force bit, return it if (addressing_modes & force_bit) return force_bit; // if not, complain Throw_error("CPU does not support this postfix for this mnemonic."); return 0; } // mnemonic did not have a force bit postfix. // if value has force bit, act upon it if (argument->flags & NUMBER_FORCEBITS) { // Value has force bit set, so return this or bigger size if (NUMBER_FORCES_8 & addressing_modes & argument->flags) return NUMBER_FORCES_8; if (NUMBER_FORCES_16 & addressing_modes & argument->flags) return NUMBER_FORCES_16; if (NUMBER_FORCES_24 & addressing_modes) return NUMBER_FORCES_24; Throw_error(exception_number_out_of_range); // else error return 0; } // Value has no force bit. Check whether there's only one addr mode // if only one addressing mode, use that if ((addressing_modes == NUMBER_FORCES_8) || (addressing_modes == NUMBER_FORCES_16) || (addressing_modes == NUMBER_FORCES_24)) { return addressing_modes; // There's only one, so use it } // There's more than one addressing mode. Check whether value is sure // if value is unsure, use default size if (argument->flags & NUMBER_EVER_UNDEFINED) { // if there is an 8-bit addressing mode *and* the value // is sure to fit into 8 bits, use the 8-bit mode if ((addressing_modes & NUMBER_FORCES_8) && (argument->flags & NUMBER_FITS_BYTE)) { return NUMBER_FORCES_8; } // if there is a 16-bit addressing, use that // call helper function for "oversized addr mode" warning if (NUMBER_FORCES_16 & addressing_modes) { return check_oversize(NUMBER_FORCES_16, argument); } // if there is a 24-bit addressing, use that // call helper function for "oversized addr mode" warning if (NUMBER_FORCES_24 & addressing_modes) { return check_oversize(NUMBER_FORCES_24, argument); } // otherwise, use 8-bit-addressing, which will raise an // error later on if the value won't fit return NUMBER_FORCES_8; } // Value is sure, so use its own size // if value is negative, size cannot be chosen. Complain! if (argument->val.intval < 0) { Throw_error("Negative value - cannot choose addressing mode."); return 0; } // Value is positive or zero. Check size ranges // if there is an 8-bit addressing mode and value fits, use 8 bits if ((addressing_modes & NUMBER_FORCES_8) && (argument->val.intval < 256)) { return NUMBER_FORCES_8; } // if there is a 16-bit addressing mode and value fits, use 16 bits if ((addressing_modes & NUMBER_FORCES_16) && (argument->val.intval < 65536)) { return NUMBER_FORCES_16; } // if there is a 24-bit addressing mode, use that. In case the // value doesn't fit, the output function will do the complaining. if (addressing_modes & NUMBER_FORCES_24) { return NUMBER_FORCES_24; } // Value is too big for all possible addressing modes Throw_error(exception_number_out_of_range); return 0; } // Mnemonics using only implied addressing. static void group_only_implied_addressing(int opcode) { //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? // TODO - accept argument and complain about it? error message should tell more than "garbage data at end of line"! // for 65ce02 and 4502, warn about buggy decimal mode if ((opcode == 0xf8) && (CPU_state.type->flags & CPUFLAG_DECIMALSUBTRACTBUGGY)) Throw_first_pass_warning("Found SED instruction for CPU with known decimal SBC bug."); Output_byte(opcode); Input_ensure_EOS(); } // helper function to output "Target not in bank" message static void not_in_bank(intval_t target) { char buffer[60]; // 640K should be enough for anybody sprintf(buffer, "Target not in bank (0x%lx).", (long) target); Throw_error(buffer); } // helper function for branches with 8-bit offset (including bbr0..7/bbs0..7) static void near_branch(int preoffset) { struct number pc; struct number target; intval_t offset = 0; // dummy value, to not throw more errors than necessary vcpu_read_pc(&pc); get_int_arg(&target, TRUE); typesystem_want_addr(&target); if ((pc.ntype == NUMTYPE_INT) && (target.ntype == NUMTYPE_INT)) { if ((target.val.intval | 0xffff) != 0xffff) { not_in_bank(target.val.intval); } else { offset = (target.val.intval - (pc.val.intval + preoffset)) & 0xffff; // clip to 16 bit offset // fix sign if (offset & 0x8000) offset -= 0x10000; // range check if ((offset < -128) || (offset > 127)) { char buffer[60]; // 640K should be enough for anybody sprintf(buffer, "Target out of range (%ld; %ld too far).", (long) offset, (long) (offset < -128 ? -128 - offset : offset - 127)); Throw_error(buffer); } } } // this fn has its own range check (see above). // No reason to irritate the user with another error message, // so use Output_byte() instead of output_8() //output_8(offset); Output_byte(offset); Input_ensure_EOS(); } // helper function for relative addressing with 16-bit offset static void far_branch(int preoffset) { struct number pc; struct number target; intval_t offset = 0; // dummy value, to not throw more errors than necessary vcpu_read_pc(&pc); get_int_arg(&target, TRUE); typesystem_want_addr(&target); if ((pc.ntype == NUMTYPE_INT) && (target.ntype == NUMTYPE_INT)) { if ((target.val.intval | 0xffff) != 0xffff) { not_in_bank(target.val.intval); } else { offset = (target.val.intval - (pc.val.intval + preoffset)) & 0xffff; // no further checks necessary, 16-bit branches can access whole bank } } output_le16(offset); Input_ensure_EOS(); } // set addressing mode bits depending on which opcodes exist, then calculate // argument size and output both opcode and argument static void make_instruction(bits force_bit, struct number *result, unsigned long opcodes) { int addressing_modes = MAYBE______; if (opcodes & 0x0000ff) addressing_modes |= MAYBE_1____; if (opcodes & 0x00ff00) addressing_modes |= MAYBE___2__; if (opcodes & 0xff0000) addressing_modes |= MAYBE_____3; switch (calc_arg_size(force_bit, result, addressing_modes)) { case NUMBER_FORCES_8: Output_byte(opcodes & 255); output_8(result->val.intval); break; case NUMBER_FORCES_16: Output_byte((opcodes >> 8) & 255); output_le16(result->val.intval); break; case NUMBER_FORCES_24: Output_byte((opcodes >> 16) & 255); output_le24(result->val.intval); } } // check whether 16bit immediate addressing is allowed. If not, return given // opcode. If it is allowed, set force bits according to CPU register length // and return given opcode for both 8- and 16-bit mode. static unsigned int imm_ops(bits *force_bit, unsigned char opcode, bits immediate_mode) { boolean long_register = FALSE; switch (immediate_mode) { case IM_FORCE8: return opcode; // result in bits 0..7 forces single-byte argument case IM_FORCE16: // currently only for 65ce02's PHW# return ((unsigned int) opcode) << 8; // opcode in bits8.15 forces two-byte argument case IM_ACCUMULATOR: // for 65816 long_register = CPU_state.a_is_long; break; case IM_INDEXREGS: // for 65816 long_register = CPU_state.xy_are_long; break; default: Bug_found("IllegalImmediateMode", immediate_mode); } // if the CPU does not support long registers... if ((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) return opcode; // result in bits 0..7 forces single-byte argument // check force bits - if no force bits given, use cpu state and convert to force bit if (*force_bit == 0) *force_bit = long_register ? NUMBER_FORCES_16 : NUMBER_FORCES_8; // return identical opcodes for single-byte and two-byte arguments: return (((unsigned int) opcode) << 8) | opcode; } // helper function to warn if zp pointer wraps around static void check_zp_wraparound(struct number *result) { if ((result->ntype == NUMTYPE_INT) && (result->val.intval == 0xff) && (CPU_state.type->flags & CPUFLAG_WARN_ABOUT_FF_PTR)) Throw_warning("Zeropage pointer wraps around from $ff to $00"); } // The main accumulator stuff (ADC, AND, CMP, EOR, LDA, ORA, SBC, STA) // plus PEI. static void group_main(int index, bits flags) { unsigned long immediate_opcodes; struct number result; bits force_bit = Input_get_force_bit(); // skips spaces after switch (get_addr_mode(&result)) { case IMMEDIATE_ADDRESSING: // #$ff or #$ffff (depending on accu length) immediate_opcodes = imm_ops(&force_bit, accu_imm[index], flags & IMMASK); // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_instruction(force_bit, &result, immediate_opcodes); break; case ABSOLUTE_ADDRESSING: // $ff, $ffff, $ffffff make_instruction(force_bit, &result, accu_abs[index]); break; case X_INDEXED_ADDRESSING: // $ff,x, $ffff,x, $ffffff,x make_instruction(force_bit, &result, accu_xabs[index]); break; case Y_INDEXED_ADDRESSING: // $ffff,y (in theory, "$ff,y" as well) make_instruction(force_bit, &result, accu_yabs[index]); break; case STACK_INDEXED_ADDRESSING: // $ff,s make_instruction(force_bit, &result, accu_sabs8[index]); break; case X_INDEXED_INDIRECT_ADDRESSING: // ($ff,x) make_instruction(force_bit, &result, accu_xind8[index]); break; case INDIRECT_ADDRESSING: // ($ff) make_instruction(force_bit, &result, accu_ind8[index]); check_zp_wraparound(&result); break; case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y make_instruction(force_bit, &result, accu_indy8[index]); check_zp_wraparound(&result); break; case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z only for 65ce02/4502/m65 make_instruction(force_bit, &result, accu_indz8[index]); check_zp_wraparound(&result); break; case LONG_INDIRECT_ADDRESSING: // [$ff] for 65816 and m65 // if in quad mode, m65 encodes this as NOP + ($ff),z if (flags & LI_PREFIX_NOP) Output_byte(0xea); make_instruction(force_bit, &result, accu_lind8[index]); break; case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y only for 65816 make_instruction(force_bit, &result, accu_lindy8[index]); break; case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y only for 65816 and 65ce02/4502/m65 make_instruction(force_bit, &result, accu_sindy8[index]); break; case LONG_INDIRECT_Z_INDEXED_ADDRESSING: // [$ff],z only for m65 // if not in quad mode, m65 encodes this as NOP + ($ff),z if (flags & LI_PREFIX_NOP) Output_byte(0xea); make_instruction(force_bit, &result, accu_lindz8[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Various mnemonics with different addressing modes. static void group_misc(int index, bits immediate_mode) { unsigned long immediate_opcodes; struct number result; bits force_bit = Input_get_force_bit(); // skips spaces after switch (get_addr_mode(&result)) { case IMPLIED_ADDRESSING: // implied addressing if (misc_impl[index]) Output_byte(misc_impl[index]); else Throw_error(exception_illegal_combination); break; case IMMEDIATE_ADDRESSING: // #$ff or #$ffff (depending on index register length) immediate_opcodes = imm_ops(&force_bit, misc_imm[index], immediate_mode); // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_instruction(force_bit, &result, immediate_opcodes); // warn about unstable ANE/LXA (undocumented opcode of NMOS 6502)? if ((CPU_state.type->flags & CPUFLAG_8B_AND_AB_NEED_0_ARG) && (result.ntype == NUMTYPE_INT) && (result.val.intval != 0x00)) { if (immediate_opcodes == 0x8b) Throw_warning("Assembling unstable ANE #NONZERO instruction"); else if (immediate_opcodes == 0xab) Throw_warning("Assembling unstable LXA #NONZERO instruction"); } break; case ABSOLUTE_ADDRESSING: // $ff or $ffff make_instruction(force_bit, &result, misc_abs[index]); break; case X_INDEXED_ADDRESSING: // $ff,x or $ffff,x make_instruction(force_bit, &result, misc_xabs[index]); break; case Y_INDEXED_ADDRESSING: // $ff,y or $ffff,y make_instruction(force_bit, &result, misc_yabs[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // mnemonics using only 8bit relative addressing (short branch instructions). static void group_std_branches(int opcode) { //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? Output_byte(opcode); near_branch(2); } // "bbr0..7" and "bbs0..7" static void group_bbr_bbs(int opcode) { struct number zpmem; //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? get_int_arg(&zpmem, TRUE); typesystem_want_addr(&zpmem); if (Input_accept_comma()) { Output_byte(opcode); Output_byte(zpmem.val.intval); near_branch(3); } else { Throw_error(exception_syntax); } } // mnemonics using only 16bit relative addressing (BRL and PER of 65816, and the long branches of 65ce02) static void group_relative16(int opcode, int preoffset) { //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? Output_byte(opcode); far_branch(preoffset); } // "mvn" and "mvp" // TODO - allow alternative syntax with 24-bit addresses (select via CLI switch)? static void group_mvn_mvp(int opcode) { boolean unmatched_hash = FALSE; struct number source, target; //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? // assembler syntax: "mnemonic source, target" or "mnemonic #source, #target" // machine language order: "opcode target source" SKIPSPACE(); // get first arg if (GotByte == '#') { GetByte(); // eat char unmatched_hash = !unmatched_hash; } get_int_arg(&source, TRUE); typesystem_want_nonaddr(&source); // get comma if (!Input_accept_comma()) { Throw_error(exception_syntax); return; } // get second arg if (GotByte == '#') { GetByte(); // eat char unmatched_hash = !unmatched_hash; } get_int_arg(&target, TRUE); typesystem_want_nonaddr(&target); // output Output_byte(opcode); output_8(target.val.intval); output_8(source.val.intval); // sanity check if (unmatched_hash) Throw_error(exception_syntax); Input_ensure_EOS(); } // "rmb0..7" and "smb0..7" static void group_only_zp(int opcode) { //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? struct number target; get_int_arg(&target, TRUE); typesystem_want_addr(&target); Output_byte(opcode); output_8(target.val.intval); Input_ensure_EOS(); } // NOP on m65 cpu (FIXME - "!align" outputs NOPs, what about that? what if user writes NEG:NEG?) static void group_prefix(int opcode) { //bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it? char buffer[100]; // 640K should be enough for anybody sprintf(buffer, "The chosen CPU uses opcode 0x%02x as a prefix code, do not use this mnemonic!", opcode); Throw_error(buffer); } // The jump instructions. static void group_jump(int index) { struct number result; bits force_bit = Input_get_force_bit(); // skips spaces after switch (get_addr_mode(&result)) { case ABSOLUTE_ADDRESSING: // absolute16 or absolute24 make_instruction(force_bit, &result, jump_abs[index]); break; case INDIRECT_ADDRESSING: // ($ffff) make_instruction(force_bit, &result, jump_ind[index]); // check whether to warn about 6502's JMP() bug if ((result.ntype == NUMTYPE_INT) && ((result.val.intval & 0xff) == 0xff) && (CPU_state.type->flags & CPUFLAG_INDIRECTJMPBUGGY)) Throw_warning("Assembling buggy JMP($xxff) instruction"); break; case X_INDEXED_INDIRECT_ADDRESSING: // ($ffff,x) make_instruction(force_bit, &result, jump_xind[index]); break; case LONG_INDIRECT_ADDRESSING: // [$ffff] make_instruction(force_bit, &result, jump_lind[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Work function static boolean check_mnemo_tree(struct ronode *tree, struct dynabuf *dyna_buf) { void *node_body; int code; bits flags; // search for tree item if (!Tree_easy_scan(tree, &node_body, dyna_buf)) return FALSE; code = ((int) node_body) & CODEMASK; // get opcode or table index flags = ((int) node_body) & FLAGSMASK; // get immediate mode flags and prefix flags if (flags & PREFIX_NEGNEG) { Output_byte(0x42); Output_byte(0x42); } switch (GROUP((long) node_body)) { case GROUP_ACCU: // main accumulator stuff group_main(code, flags); break; case GROUP_MISC: // misc group_misc(code, flags & IMMASK); break; case GROUP_ALLJUMPS: // the jump instructions group_jump(code); break; case GROUP_IMPLIEDONLY: // mnemonics with only implied addressing group_only_implied_addressing(code); break; case GROUP_RELATIVE8: // short relative group_std_branches(code); break; case GROUP_BITBRANCH: // "bbr0..7" and "bbs0..7" group_bbr_bbs(code); break; case GROUP_REL16_2: // long relative to pc+2 group_relative16(code, 2); break; case GROUP_REL16_3: // long relative to pc+3 group_relative16(code, 3); break; case GROUP_BOTHMOVES: // "mvp" and "mvn" group_mvn_mvp(code); break; case GROUP_ZPONLY: // "rmb0..7", "smb0..7", "inw", "dew" group_only_zp(code); break; case GROUP_PREFIX: // NOP for m65 cpu group_prefix(code); break; default: // others indicate bugs Bug_found("IllegalGroupIndex", code); } return TRUE; } // check whether mnemonic in GlobalDynaBuf is supported by 6502 cpu. boolean keyword_is_6502_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by NMOS 6502 cpu. boolean keyword_is_nmos6502_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check undocumented ("illegal") opcodes... if (check_mnemo_tree(mnemo_6502undoc1_tree, mnemo_dyna_buf)) return TRUE; // then check some more undocumented ("illegal") opcodes... if (check_mnemo_tree(mnemo_6502undoc2_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by C64DTV2 cpu. boolean keyword_is_c64dtv2_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check C64DTV2 extensions... if (check_mnemo_tree(mnemo_c64dtv2_tree, mnemo_dyna_buf)) return TRUE; // ...then check a few undocumented ("illegal") opcodes... if (check_mnemo_tree(mnemo_6502undoc1_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by 65c02 cpu. boolean keyword_is_65c02_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65c02 cpu. boolean keyword_is_r65c02_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65c02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs) return check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by WDC w65c02 cpu. boolean keyword_is_w65c02_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65c02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check WDC extensions "stp" and "wai" return check_mnemo_tree(mnemo_stp_wai_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by CSG 65CE02 cpu. boolean keyword_is_65ce02_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65ce02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check "aug" return check_mnemo_tree(mnemo_aug_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by CSG 4502 cpu. boolean keyword_is_4502_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65ce02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check "map" and "eom" return check_mnemo_tree(mnemo_map_eom_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by MEGA65 cpu. boolean keyword_is_m65_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check m65 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_m65_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65ce02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check "map" and "eom" return check_mnemo_tree(mnemo_map_eom_tree, mnemo_dyna_buf); } // check whether mnemonic in GlobalDynaBuf is supported by 65816 cpu. boolean keyword_is_65816_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65816 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65816_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check WDC extensions "stp" and "wai" return check_mnemo_tree(mnemo_stp_wai_tree, mnemo_dyna_buf); }