// ACME - a crossassembler for producing 6502/65c02/65816 code. // Copyright (C) 1998-2009 Marco Baye // Have a look at "acme.c" for further info // // Mnemonics stuff #include "config.h" #include "alu.h" #include "cpu.h" #include "dynabuf.h" #include "global.h" #include "input.h" #include "output.h" #include "tree.h" // Constants #define s_ror (s_error + 2) // Yes, I know I'm sick #define MNEMO_DYNABUF_INITIALSIZE 8 // 4 + terminator should suffice // These values are needed to recognize addressing modes. // Bits: // 7....... "Implied" no value given // .6...... "Immediate" "#" at start // ..5..... "IndirectLong" "[" at start and "]" after value // ...4.... "Indirect" Value has at least one unnecessary pair of "()" // ....32.. "Indexed-Int" Index given inside of "()" // ......10 "Indexed-Ext" Index given outside of (or without any) "()" // // Index bits: // 00 = no index // 01 = ",s" (Stack-indexed) // 10 = ",x" (X-indexed) // 11 = ",y" (Y-indexed) // Components (Values for indices) #define HAM__ (0u << 0) // No index #define HAM_S (1u << 0) // Stack-indexed #define HAM_X (2u << 0) // X-indexed #define HAM_Y (3u << 0) // Y-indexed // End values base value internal index external index #define HAM_IMP (1u << 7) #define HAM_IMM (1u << 6) #define HAM_ABS 0 #define HAM_ABSS (1u << 0) #define HAM_ABSX (2u << 0) #define HAM_ABSY (3u << 0) #define HAM_IND (1u << 4) #define HAM_XIND ((1u << 4) | (2u << 2)) #define HAM_INDY ((1u << 4) | (3u << 0)) #define HAM_SINDY ((1u << 4) | (1u << 2) | (3u << 0)) #define HAM_LIND (1u << 5) #define HAM_LINDY ((1u << 5) | (3u << 0)) // Values of internal indices equal values of external indices, shifted left // by two bits. The program relies on this ! // Constant values, used to mark the possible parameter lengths of commands. // Not all of the eight values are actually used, however (because of the // supported CPUs). #define MAYBE______ (0) #define MAYBE_1____ (MVALUE_FORCE08) #define MAYBE___2__ (MVALUE_FORCE16) #define MAYBE_1_2__ (MVALUE_FORCE08 | MVALUE_FORCE16) #define MAYBE_____3 (MVALUE_FORCE24) #define MAYBE_1___3 (MVALUE_FORCE08 | MVALUE_FORCE24) #define MAYBE___2_3 (MVALUE_FORCE16 | MVALUE_FORCE24) #define MAYBE_1_2_3 (MVALUE_FORCE08 | MVALUE_FORCE16 | MVALUE_FORCE24) // The mnemonics are split up into groups, each group has its own function to be dealt with: enum mnemogroup_t { 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_RELATIVE16, // mnemonics with 16bit relative addressing Byte value = opcode GROUP_BOTHMOVES // the "move" commands MVP and MVN Byte value = opcode }; // 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 | 6510 illegals | enum { IDX_ORA,IDX_AND,IDX_EOR,IDX_ADC,IDX_STA,IDX_LDA,IDX_CMP,IDX_SBC,IDXcORA,IDXcAND,IDXcEOR,IDXcADC,IDXcSTA,IDXcLDA,IDXcCMP,IDXcSBC,IDX816ORA,IDX816AND,IDX816EOR,IDX816ADC,IDX816STA,IDX816LDA,IDX816CMP,IDX816SBC,IDX816PEI,IDX_SLO,IDX_RLA,IDX_SRE,IDX_RRA,IDX_SAX,IDX_LAX,IDX_DCP,IDX_ISC}; SCL accu_abs[] = { 0x0d05, 0x2d25, 0x4d45, 0x6d65, 0x8d85, 0xada5, 0xcdc5, 0xede5, 0x0d05, 0x2d25, 0x4d45, 0x6d65, 0x8d85, 0xada5, 0xcdc5, 0xede5, 0x0f0d05, 0x2f2d25, 0x4f4d45, 0x6f6d65, 0x8f8d85, 0xafada5, 0xcfcdc5, 0xefede5, 0, 0x0f07, 0x2f27, 0x4f47, 0x6f67, 0x8f87, 0xafa7, 0xcfc7, 0xefe7}; // $ff $ffff $ffffff SCL accu_xabs[] = { 0x1d15, 0x3d35, 0x5d55, 0x7d75, 0x9d95, 0xbdb5, 0xddd5, 0xfdf5, 0x1d15, 0x3d35, 0x5d55, 0x7d75, 0x9d95, 0xbdb5, 0xddd5, 0xfdf5, 0x1f1d15, 0x3f3d35, 0x5f5d55, 0x7f7d75, 0x9f9d95, 0xbfbdb5, 0xdfddd5, 0xfffdf5, 0, 0x1f17, 0x3f37, 0x5f57, 0x7f77, 0, 0, 0xdfd7, 0xfff7}; // $ff,x $ffff,x $ffffff,x SCS accu_yabs[] = { 0x1900, 0x3900, 0x5900, 0x7900, 0x9900, 0xb900, 0xd900, 0xf900, 0x1900, 0x3900, 0x5900, 0x7900, 0x9900, 0xb900, 0xd900, 0xf900, 0x1900, 0x3900, 0x5900, 0x7900, 0x9900, 0xb900, 0xd900, 0xf900, 0, 0x1b00, 0x3b00, 0x5b00, 0x7b00, 0x97, 0xbfb7, 0xdb00, 0xfb00}; // $ff,y $ffff,y SCB accu_xind8[] = { 0x01, 0x21, 0x41, 0x61, 0x81, 0xa1, 0xc1, 0xe1, 0x01, 0x21, 0x41, 0x61, 0x81, 0xa1, 0xc1, 0xe1, 0x01, 0x21, 0x41, 0x61, 0x81, 0xa1, 0xc1, 0xe1, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3}; // ($ff,x) SCB accu_indy8[] = { 0x11, 0x31, 0x51, 0x71, 0x91, 0xb1, 0xd1, 0xf1, 0x11, 0x31, 0x51, 0x71, 0x91, 0xb1, 0xd1, 0xf1, 0x11, 0x31, 0x51, 0x71, 0x91, 0xb1, 0xd1, 0xf1, 0, 0x13, 0x33, 0x53, 0x73, 0, 0xb3, 0xd3, 0xf3}; // ($ff),y SCB accu_imm[] = { 0x09, 0x29, 0x49, 0x69, 0, 0xa9, 0xc9, 0xe9, 0x09, 0x29, 0x49, 0x69, 0, 0xa9, 0xc9, 0xe9, 0x09, 0x29, 0x49, 0x69, 0, 0xa9, 0xc9, 0xe9, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // #$ff #$ffff SCB accu_ind8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x32, 0x52, 0x72, 0x92, 0xb2, 0xd2, 0xf2, 0x12, 0x32, 0x52, 0x72, 0x92, 0xb2, 0xd2, 0xf2, 0xd4, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff) SCB accu_sabs8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,s SCB accu_sindy8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x13, 0x33, 0x53, 0x73, 0x93, 0xb3, 0xd3, 0xf3, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff,s),y SCB accu_lind8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, 0x27, 0x47, 0x67, 0x87, 0xa7, 0xc7, 0xe7, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff] SCB accu_lindy8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x17, 0x37, 0x57, 0x77, 0x97, 0xb7, 0xd7, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff],y // 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 | 65c02 | 65816 | 6510 illegals | enum { IDX_BIT,IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_STY,IDX_STX,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_DEC,IDX_INC,IDXcTSB,IDXcTRB,IDXcBIT,IDXcDEC,IDXcINC,IDXcSTZ,IDX816COP,IDX816REP,IDX816SEP,IDX816PEA,IDX_ANC,IDX_ASR,IDX_ARR,IDX_SBX,IDX_DOP,IDX_TOP,IDX_JAM}; SCS misc_abs[] = { 0x2c24, 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0x8c84, 0x8e86, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0xcec6, 0xeee6, 0x0c04, 0x1c14, 0x2c24, 0xcec6, 0xeee6, 0x9c64, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x04, 0x0c00, 0}; // $ff $ffff SCS misc_xabs[] = { 0, 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0x94, 0, 0xbcb4, 0, 0, 0, 0xded6, 0xfef6, 0, 0, 0x3c34, 0xded6, 0xfef6, 0x9e74, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x1c00, 0}; // $ff,x $ffff,x SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0, 0x96, 0, 0xbeb6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,y $ffff,y SCB misc_imm[] = { 0, 0, 0, 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0, 0, 0, 0x89, 0, 0, 0, 0, 0xc2, 0xe2, 0, 0x2b, 0x4b, 0x6b, 0xcb, 0x80, 0, 0}; // #$ff SCB misc_impl[] = { 0, 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0x1a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x0c, 0x02}; // implied/accu // 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 | enum { IDX_JMP,IDX_JSR,IDXcJMP,IDX816JMP,IDX816JML,IDX816JSR,IDX816JSL}; SCL jump_abs[] = { 0x4c00, 0x2000, 0x4c00, 0x5c4c00, 0x5c0000, 0x222000, 0x220000}; // $ffff $ffffff SCS jump_ind[] = { 0x6c00, 0, 0x6c00, 0x6c00, 0, 0, 0}; // ($ffff) SCS jump_xind[] = { 0, 0, 0x7c00, 0x7c00, 0, 0xfc00, 0}; // ($ffff,x) SCS jump_lind[] = { 0, 0, 0, 0xdc00, 0xdc00, 0, 0}; // [$ffff] #undef SCB #undef SCS #undef SCL // error message strings static const char exception_illegal_combination[] = "Illegal combination of command and addressing mode."; static const char exception_highbyte_zero[]= "Using oversized addressing mode."; // Variables static struct dynabuf_t *mnemo_dyna_buf; // dynamic buffer for mnemonics // predefined stuff static struct node_t *mnemo_6502_tree = NULL; // holds 6502 mnemonics static struct node_t *mnemo_6510_tree = NULL; // holds 6510 extensions static struct node_t *mnemo_65c02_tree = NULL; // holds 65c02 extensions //static node_t *mnemo_Rockwell65c02_tree = NULL; // Rockwell static struct node_t *mnemo_WDC65c02_tree = NULL; // WDC's "stp"/"wai" static struct node_t *mnemo_65816_tree = NULL; // holds 65816 extensions // Command's code and group values are stored together in a single integer. // To extract the code, use "& CODEMASK". // To extract the group, use ">> GROUPSHIFT" #define CODEMASK 0xff // opcode or table index #define FLAGSMASK 0x300 // flags concerning immediate addressing: #define IMM_ACCU 0x100 // ...depends on accumulator length #define IMM_IDX 0x200 // ...depends on index register length #define GROUPSHIFT 10 // shift right by this to extract group #define MERGE(g, v) ((g << GROUPSHIFT) | v) static struct node_t mnemos_6502[] = { PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX_ORA | IMM_ACCU)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX_AND | IMM_ACCU)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX_EOR | IMM_ACCU)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX_ADC | IMM_ACCU)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX_STA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX_LDA | IMM_ACCU)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX_CMP | IMM_ACCU)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX_SBC | IMM_ACCU)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDX_BIT | IMM_ACCU)), PREDEFNODE(s_asl, MERGE(GROUP_MISC, IDX_ASL)), PREDEFNODE("rol", MERGE(GROUP_MISC, IDX_ROL)), PREDEFNODE(s_lsr, MERGE(GROUP_MISC, IDX_LSR)), PREDEFNODE(s_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 | IMM_IDX)), PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX | IMM_IDX)), PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY | IMM_IDX)), PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX | IMM_IDX)), PREDEFNODE("dec", MERGE(GROUP_MISC, IDX_DEC)), PREDEFNODE("inc", MERGE(GROUP_MISC, IDX_INC)), PREDEFNODE("bpl", MERGE(GROUP_RELATIVE8, 16)), PREDEFNODE("bmi", MERGE(GROUP_RELATIVE8, 48)), PREDEFNODE("bvc", MERGE(GROUP_RELATIVE8, 80)), PREDEFNODE("bvs", MERGE(GROUP_RELATIVE8, 112)), PREDEFNODE("bcc", MERGE(GROUP_RELATIVE8, 144)), PREDEFNODE("bcs", MERGE(GROUP_RELATIVE8, 176)), PREDEFNODE("bne", MERGE(GROUP_RELATIVE8, 208)), PREDEFNODE("beq", MERGE(GROUP_RELATIVE8, 240)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX_JMP)), PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX_JSR)), PREDEFNODE("brk", MERGE(GROUP_IMPLIEDONLY, 0)), PREDEFNODE("php", MERGE(GROUP_IMPLIEDONLY, 8)), PREDEFNODE("clc", MERGE(GROUP_IMPLIEDONLY, 24)), PREDEFNODE("plp", MERGE(GROUP_IMPLIEDONLY, 40)), PREDEFNODE("sec", MERGE(GROUP_IMPLIEDONLY, 56)), PREDEFNODE("rti", MERGE(GROUP_IMPLIEDONLY, 64)), PREDEFNODE("pha", MERGE(GROUP_IMPLIEDONLY, 72)), PREDEFNODE("cli", MERGE(GROUP_IMPLIEDONLY, 88)), PREDEFNODE("rts", MERGE(GROUP_IMPLIEDONLY, 96)), PREDEFNODE("pla", MERGE(GROUP_IMPLIEDONLY, 104)), PREDEFNODE("sei", MERGE(GROUP_IMPLIEDONLY, 120)), PREDEFNODE("dey", MERGE(GROUP_IMPLIEDONLY, 136)), PREDEFNODE("txa", MERGE(GROUP_IMPLIEDONLY, 138)), PREDEFNODE("tya", MERGE(GROUP_IMPLIEDONLY, 152)), PREDEFNODE("txs", MERGE(GROUP_IMPLIEDONLY, 154)), PREDEFNODE("tay", MERGE(GROUP_IMPLIEDONLY, 168)), PREDEFNODE("tax", MERGE(GROUP_IMPLIEDONLY, 170)), PREDEFNODE("clv", MERGE(GROUP_IMPLIEDONLY, 184)), PREDEFNODE("tsx", MERGE(GROUP_IMPLIEDONLY, 186)), PREDEFNODE("iny", MERGE(GROUP_IMPLIEDONLY, 200)), PREDEFNODE("dex", MERGE(GROUP_IMPLIEDONLY, 202)), PREDEFNODE("cld", MERGE(GROUP_IMPLIEDONLY, 216)), PREDEFNODE("inx", MERGE(GROUP_IMPLIEDONLY, 232)), PREDEFNODE("nop", MERGE(GROUP_IMPLIEDONLY, 234)), PREDEFLAST("sed", MERGE(GROUP_IMPLIEDONLY, 248)), // ^^^^ this marks the last element }; static struct node_t mnemos_6510[] = { PREDEFNODE("slo", MERGE(GROUP_ACCU, IDX_SLO)), // ASL + ORA (aka ASO) PREDEFNODE("rla", MERGE(GROUP_ACCU, IDX_RLA)), // ROL + AND PREDEFNODE("sre", MERGE(GROUP_ACCU, IDX_SRE)), // LSR + EOR (aka LSE) PREDEFNODE("rra", MERGE(GROUP_ACCU, IDX_RRA)), // ROR + ADC PREDEFNODE("sax", MERGE(GROUP_ACCU, IDX_SAX)), // STX + STA (aka AAX aka AXS) PREDEFNODE("lax", MERGE(GROUP_ACCU, IDX_LAX)), // LDX + LDA PREDEFNODE("dcp", MERGE(GROUP_ACCU, IDX_DCP)), // DEC + CMP (aka DCM) PREDEFNODE("isc", MERGE(GROUP_ACCU, IDX_ISC)), // INC + SBC (aka ISB aka INS) PREDEFNODE("anc", MERGE(GROUP_MISC, IDX_ANC)), // ROL + AND, ASL + ORA (aka AAC) PREDEFNODE(s_asr, MERGE(GROUP_MISC, IDX_ASR)), // LSR + EOR (aka ALR) PREDEFNODE("arr", MERGE(GROUP_MISC, IDX_ARR)), // ROR + ADC PREDEFNODE("sbx", MERGE(GROUP_MISC, IDX_SBX)), // DEX + CMP (aka AXS aka SAX) PREDEFNODE("dop", MERGE(GROUP_MISC, IDX_DOP)), // skip next byte PREDEFNODE("top", MERGE(GROUP_MISC, IDX_TOP)), // skip next two bytes PREDEFLAST("jam", MERGE(GROUP_MISC, IDX_JAM)), // jam/crash/kill/halt-and-catch-fire // ^^^^ this marks the last element }; static struct node_t mnemos_65c02[] = { PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXcORA | IMM_ACCU)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXcAND | IMM_ACCU)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXcEOR | IMM_ACCU)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXcADC | IMM_ACCU)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXcSTA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXcLDA | IMM_ACCU)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXcCMP | IMM_ACCU)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXcSBC | IMM_ACCU)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDXcJMP)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT | IMM_ACCU)), PREDEFNODE("dec", MERGE(GROUP_MISC, IDXcDEC)), PREDEFNODE("inc", MERGE(GROUP_MISC, IDXcINC)), PREDEFNODE("bra", MERGE(GROUP_RELATIVE8, 128)), PREDEFNODE("phy", MERGE(GROUP_IMPLIEDONLY, 90)), PREDEFNODE("ply", MERGE(GROUP_IMPLIEDONLY, 122)), PREDEFNODE("phx", MERGE(GROUP_IMPLIEDONLY, 218)), PREDEFNODE("plx", MERGE(GROUP_IMPLIEDONLY, 250)), PREDEFNODE("tsb", MERGE(GROUP_MISC, IDXcTSB)), PREDEFNODE("trb", MERGE(GROUP_MISC, IDXcTRB)), PREDEFLAST("stz", MERGE(GROUP_MISC, IDXcSTZ)), // ^^^^ this marks the last element }; //static struct node_t mnemos_Rockwell65c02[] = { // PREDEFNODE("rmb0", MERGE(G_ , 0x07)), // PREDEFNODE("rmb1", MERGE(G_ , 0x17)), // PREDEFNODE("rmb2", MERGE(G_ , 0x27)), // PREDEFNODE("rmb3", MERGE(G_ , 0x37)), // PREDEFNODE("rmb4", MERGE(G_ , 0x47)), // PREDEFNODE("rmb5", MERGE(G_ , 0x57)), // PREDEFNODE("rmb6", MERGE(G_ , 0x67)), // PREDEFNODE("rmb7", MERGE(G_ , 0x77)), // PREDEFNODE("smb0", MERGE(G_ , 0x87)), // PREDEFNODE("smb1", MERGE(G_ , 0x97)), // PREDEFNODE("smb2", MERGE(G_ , 0xa7)), // PREDEFNODE("smb3", MERGE(G_ , 0xb7)), // PREDEFNODE("smb4", MERGE(G_ , 0xc7)), // PREDEFNODE("smb5", MERGE(G_ , 0xd7)), // PREDEFNODE("smb6", MERGE(G_ , 0xe7)), // PREDEFNODE("smb7", MERGE(G_ , 0xf7)), // PREDEFNODE("bbr0", MERGE(G_ , 0x0f)), // PREDEFNODE("bbr1", MERGE(G_ , 0x1f)), // PREDEFNODE("bbr2", MERGE(G_ , 0x2f)), // PREDEFNODE("bbr3", MERGE(G_ , 0x3f)), // PREDEFNODE("bbr4", MERGE(G_ , 0x4f)), // PREDEFNODE("bbr5", MERGE(G_ , 0x5f)), // PREDEFNODE("bbr6", MERGE(G_ , 0x6f)), // PREDEFNODE("bbr7", MERGE(G_ , 0x7f)), // PREDEFNODE("bbs0", MERGE(G_ , 0x8f)), // PREDEFNODE("bbs1", MERGE(G_ , 0x9f)), // PREDEFNODE("bbs2", MERGE(G_ , 0xaf)), // PREDEFNODE("bbs3", MERGE(G_ , 0xbf)), // PREDEFNODE("bbs4", MERGE(G_ , 0xcf)), // PREDEFNODE("bbs5", MERGE(G_ , 0xdf)), // PREDEFNODE("bbs6", MERGE(G_ , 0xef)), // PREDEFLAST("bbs7", MERGE(G_ , 0xff)), // // ^^^^ this marks the last element //}; static struct node_t mnemos_WDC65c02[] = { PREDEFNODE("stp", MERGE(GROUP_IMPLIEDONLY, 219)), PREDEFLAST("wai", MERGE(GROUP_IMPLIEDONLY, 203)), // ^^^^ this marks the last element }; static struct node_t mnemos_65816[] = { PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX816ORA | IMM_ACCU)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX816AND | IMM_ACCU)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX816EOR | IMM_ACCU)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX816ADC | IMM_ACCU)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX816STA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX816LDA | IMM_ACCU)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX816CMP | IMM_ACCU)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX816SBC | IMM_ACCU)), PREDEFNODE("pei", MERGE(GROUP_ACCU, IDX816PEI)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX816JMP)), PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX816JSR)), PREDEFNODE("jml", MERGE(GROUP_ALLJUMPS, IDX816JML)), PREDEFNODE("jsl", MERGE(GROUP_ALLJUMPS, IDX816JSL)), PREDEFNODE("mvp", MERGE(GROUP_BOTHMOVES, 0x44)), PREDEFNODE("mvn", MERGE(GROUP_BOTHMOVES, 0x54)), PREDEFNODE("per", MERGE(GROUP_RELATIVE16, 98)), PREDEFNODE(s_brl, MERGE(GROUP_RELATIVE16, 130)), PREDEFNODE("cop", MERGE(GROUP_MISC, IDX816COP)), PREDEFNODE("rep", MERGE(GROUP_MISC, IDX816REP)), PREDEFNODE("sep", MERGE(GROUP_MISC, IDX816SEP)), PREDEFNODE("pea", MERGE(GROUP_MISC, IDX816PEA)), PREDEFNODE("phd", MERGE(GROUP_IMPLIEDONLY, 11)), PREDEFNODE("tcs", MERGE(GROUP_IMPLIEDONLY, 27)), PREDEFNODE("pld", MERGE(GROUP_IMPLIEDONLY, 43)), PREDEFNODE("tsc", MERGE(GROUP_IMPLIEDONLY, 59)), PREDEFNODE("wdm", MERGE(GROUP_IMPLIEDONLY, 66)), PREDEFNODE("phk", MERGE(GROUP_IMPLIEDONLY, 75)), PREDEFNODE("tcd", MERGE(GROUP_IMPLIEDONLY, 91)), PREDEFNODE("rtl", MERGE(GROUP_IMPLIEDONLY, 107)), PREDEFNODE("tdc", MERGE(GROUP_IMPLIEDONLY, 123)), PREDEFNODE("phb", MERGE(GROUP_IMPLIEDONLY, 139)), PREDEFNODE("txy", MERGE(GROUP_IMPLIEDONLY, 155)), PREDEFNODE("plb", MERGE(GROUP_IMPLIEDONLY, 171)), PREDEFNODE("tyx", MERGE(GROUP_IMPLIEDONLY, 187)), PREDEFNODE("xba", MERGE(GROUP_IMPLIEDONLY, 235)), PREDEFLAST("xce", MERGE(GROUP_IMPLIEDONLY, 251)), // ^^^^ this marks the last element }; // Functions // create dynamic buffer, build keyword trees void Mnemo_init(void) { mnemo_dyna_buf = DynaBuf_create(MNEMO_DYNABUF_INITIALSIZE); Tree_add_table(&mnemo_6502_tree, mnemos_6502); Tree_add_table(&mnemo_6510_tree, mnemos_6510); Tree_add_table(&mnemo_65c02_tree, mnemos_65c02); // Tree_add_table(&mnemo_Rockwell65c02_tree, mnemos_Rockwell65c02); Tree_add_table(&mnemo_WDC65c02_tree, mnemos_WDC65c02); Tree_add_table(&mnemo_65816_tree, mnemos_65816); } // Address mode parsing // Utility function for parsing indices. static int get_index(int next) { int addressing_mode = HAM__; if (next) GetByte(); if (!Input_accept_comma()) return addressing_mode; switch (GotByte) { case 's': case 'S': addressing_mode = HAM_S; break; case 'x': case 'X': addressing_mode = HAM_X; break; case 'y': case 'Y': addressing_mode = HAM_Y; break; default: Throw_error(exception_syntax); } if (addressing_mode != HAM__) NEXTANDSKIPSPACE(); return addressing_mode; } // This function stores the command's argument in the given result_int_t // structure (using the valueparser). The addressing mode is returned. static int get_argument(struct result_int_t *result) { int open_paren, addressing_mode = HAM_ABS; SKIPSPACE(); switch (GotByte) { case '[': GetByte(); // proceed with next char ALU_int_result(result); if (GotByte == ']') addressing_mode |= HAM_LIND | get_index(TRUE); else Throw_error(exception_syntax); break; case '#': GetByte(); // proceed with next char addressing_mode |= HAM_IMM; ALU_int_result(result); break; default: // liberal, to allow for "(...," open_paren = ALU_liberal_int(result); // check for implied addressing if ((result->flags & MVALUE_EXISTS) == 0) addressing_mode |= HAM_IMP; // check for indirect addressing if (result->flags & MVALUE_INDIRECT) addressing_mode |= HAM_IND; // check for internal index (before closing parenthesis) if (open_paren) { // in case there are still open parentheses, // read internal index addressing_mode |= (get_index(FALSE) << 2); if (GotByte == ')') GetByte(); // go on with next char else Throw_error(exception_syntax); } // check for external index (after closing parenthesis) addressing_mode |= get_index(FALSE); } // ensure end of line Input_ensure_EOS(); //printf("AM: %x\n", addressing_mode); return addressing_mode; } // Helper function for calc_arg_size() // Only call with "size_bit = MVALUE_FORCE16" or "size_bit = MVALUE_FORCE24" static int check_oversize(int size_bit, struct result_int_t *argument) { // only check if value is *defined* if ((argument->flags & MVALUE_DEFINED) == 0) return size_bit; // pass on result // value is defined, so check if (size_bit == MVALUE_FORCE16) { // check 16-bit argument for high byte zero if ((argument->intval <= 255) && (argument->intval >= -128)) Throw_warning(exception_highbyte_zero); } else { // check 24-bit argument for bank byte zero if ((argument->intval <= 65535) && (argument->intval >= -32768)) Throw_warning(exception_highbyte_zero); } 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) static int calc_arg_size(int force_bit, struct result_int_t *argument, int addressing_modes) { // if there are no possible addressing modes, complain if (addressing_modes == MAYBE______) { Throw_error(exception_illegal_combination); return 0; } // if command has force bit, act upon it if (force_bit) { // if command allows this force bit, return it if (addressing_modes & force_bit) return force_bit; // if not, complain Throw_error("Illegal combination of command and postfix."); return 0; } // Command has no force bit. Check whether value has one // if value has force bit, act upon it if (argument->flags & MVALUE_FORCEBITS) { // Value has force bit set, so return this or bigger size if (MVALUE_FORCE08 & addressing_modes & argument->flags) return MVALUE_FORCE08; if (MVALUE_FORCE16 & addressing_modes & argument->flags) return MVALUE_FORCE16; if (MVALUE_FORCE24 & addressing_modes) return MVALUE_FORCE24; 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 == MVALUE_FORCE08) || (addressing_modes == MVALUE_FORCE16) || (addressing_modes == MVALUE_FORCE24)) 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 & MVALUE_UNSURE) { // 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 & MVALUE_FORCE08) && (argument->flags & MVALUE_ISBYTE)) return MVALUE_FORCE08; // if there is a 16-bit addressing, use that // call helper function for "oversized addr mode" warning if (MVALUE_FORCE16 & addressing_modes) return check_oversize(MVALUE_FORCE16, argument); // if there is a 24-bit addressing, use that // call helper function for "oversized addr mode" warning if (MVALUE_FORCE24 & addressing_modes) return check_oversize(MVALUE_FORCE24, argument); // otherwise, use 8-bit-addressing, which will raise an // error later on if the value won't fit return MVALUE_FORCE08; } // Value is sure, so use its own size // if value is negative, size cannot be chosen. Complain! if (argument->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 & MVALUE_FORCE08) && (argument->intval < 256)) return MVALUE_FORCE08; // if there is a 16-bit addressing mode and value fits, use 16 bits if ((addressing_modes & MVALUE_FORCE16) && (argument->intval < 65536)) return MVALUE_FORCE16; // 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 & MVALUE_FORCE24) return MVALUE_FORCE24; // 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) { 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).", target); Throw_error(buffer); } // Mnemonics using only 8bit relative addressing (short branch instructions). static void group_only_relative8_addressing(int opcode) { struct result_int_t target; intval_t offset = 0; // dummy value, to not throw more errors than necessary ALU_int_result(&target); if (CPU_pc.flags & target.flags & MVALUE_DEFINED) { if ((target.intval | 0xffff) != 0xffff) { not_in_bank(target.intval); } else { offset = (target.intval - (CPU_pc.intval + 2)) & 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).", offset, offset < -128 ? -128 - offset : offset - 127); Throw_error(buffer); } } } Output_byte(opcode); // 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_8b() //Output_8b(offset); Output_byte(offset); Input_ensure_EOS(); } // Mnemonics using only 16bit relative addressing (BRL and PER). static void group_only_relative16_addressing(int opcode) { struct result_int_t target; intval_t offset = 0; // dummy value, to not throw more errors than necessary ALU_int_result(&target); if (CPU_pc.flags & target.flags & MVALUE_DEFINED) { if ((target.intval | 0xffff) != 0xffff) { not_in_bank(target.intval); } else { offset = (target.intval - (CPU_pc.intval + 3)) & 0xffff; // no further checks necessary, 16-bit branches can access whole bank } } Output_byte(opcode); Output_16b(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_command(int force_bit, struct result_int_t *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 MVALUE_FORCE08: Output_byte(opcodes & 255); Output_8b(result->intval); break; case MVALUE_FORCE16: Output_byte((opcodes >> 8) & 255); Output_16b(result->intval); break; case MVALUE_FORCE24: Output_byte((opcodes >> 16) & 255); Output_24b(result->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(int *force_bit, unsigned char opcode, int imm_flag) { // if the CPU does not allow 16bit immediate addressing (or if the // opcode does not allow it), return immediately. if (((CPU_now->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) || (imm_flag == 0)) return opcode; // check force bits (if no force bits given, use relevant flag) if (*force_bit == 0) *force_bit = ((imm_flag & IMM_ACCU) ? CPU_now->a_is_long : CPU_now->xy_are_long) ? MVALUE_FORCE16 : MVALUE_FORCE08; // return identical opcodes for 8bit and 16bit args! return (((unsigned int) opcode) << 8) | opcode; } // The main accumulator stuff (ADC, AND, CMP, EOR, LDA, ORA, SBC, STA) // plus PEI. static void group_main(int index, int imm_flag) { unsigned long imm_opcodes; struct result_int_t result; int force_bit = Input_get_force_bit(); // skips spaces after switch (get_argument(&result)) { case HAM_IMM: // #$ff or #$ffff (depending on accu length) imm_opcodes = imm_ops(&force_bit, accu_imm[index], imm_flag); // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_command(force_bit, &result, imm_opcodes); break; case HAM_ABS: // $ff, $ffff, $ffffff make_command(force_bit, &result, accu_abs[index]); break; case HAM_ABSX: // $ff,x, $ffff,x, $ffffff,x make_command(force_bit, &result, accu_xabs[index]); break; case HAM_ABSY: // $ffff,y (in theory, "$ff,y" as well) make_command(force_bit, &result, accu_yabs[index]); break; case HAM_ABSS: // $ff,s make_command(force_bit, &result, accu_sabs8[index]); break; case HAM_XIND: // ($ff,x) make_command(force_bit, &result, accu_xind8[index]); break; case HAM_IND: // ($ff) make_command(force_bit, &result, accu_ind8[index]); break; case HAM_INDY: // ($ff),y make_command(force_bit, &result, accu_indy8[index]); break; case HAM_LIND: // [$ff] make_command(force_bit, &result, accu_lind8[index]); break; case HAM_LINDY: // [$ff],y make_command(force_bit, &result, accu_lindy8[index]); break; case HAM_SINDY: // ($ff,s),y make_command(force_bit, &result, accu_sindy8[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Various mnemonics with different addressing modes. static void group_misc(int index, int imm_flag) { unsigned long imm_opcodes; struct result_int_t result; int force_bit = Input_get_force_bit(); // skips spaces after switch (get_argument(&result)) { case HAM_IMP: // implied addressing if (misc_impl[index]) Output_byte(misc_impl[index]); else Throw_error(exception_illegal_combination); break; case HAM_IMM: // #$ff or #$ffff (depending on index register length) imm_opcodes = imm_ops(&force_bit, misc_imm[index], imm_flag); // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_command(force_bit, &result, imm_opcodes); break; case HAM_ABS: // $ff or $ffff make_command(force_bit, &result, misc_abs[index]); break; case HAM_ABSX: // $ff,x or $ffff,x make_command(force_bit, &result, misc_xabs[index]); break; case HAM_ABSY: // $ff,y or $ffff,y make_command(force_bit, &result, misc_yabs[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Mnemonics using "8bit, 8bit" addressing. Only applies to "MVN" and "MVP". static void group_move(int opcode) { intval_t source, target; // assembler syntax: "opcode source, target" source = ALU_any_int(); if (Input_accept_comma()) { target = ALU_any_int(); // machine language: Output_byte(opcode); // opcode Output_8b(target); // target Output_8b(source); // source Input_ensure_EOS(); } else { Throw_error(exception_syntax); } } // The jump instructions. static void group_jump(int index) { struct result_int_t result; int force_bit = Input_get_force_bit(); // skips spaces after switch (get_argument(&result)) { case HAM_ABS: // absolute16 or absolute24 make_command(force_bit, &result, jump_abs[index]); break; case HAM_IND: // ($ffff) make_command(force_bit, &result, jump_ind[index]); // check whether to warn about 6502's JMP() bug if (((result.intval & 0xff) == 0xff) && (result.flags & MVALUE_DEFINED) && (CPU_now->flags & CPUFLAG_INDIRECTJMPBUGGY)) Throw_warning("Assembling buggy JMP($xxff) instruction"); break; case HAM_XIND: // ($ffff,x) make_command(force_bit, &result, jump_xind[index]); break; case HAM_LIND: // [$ffff] make_command(force_bit, &result, jump_lind[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Work function static int check_mnemo_tree(struct node_t *tree, struct dynabuf_t *dyna_buf) { void *node_body; int code, imm_flag; // flag for immediate addressing // 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 imm_flag = ((int) node_body) & FLAGSMASK; // get flag switch (((long) node_body) >> GROUPSHIFT) { case GROUP_ACCU: // main accumulator stuff group_main(code, imm_flag); break; case GROUP_MISC: // misc group_misc(code, imm_flag); 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_only_relative8_addressing(code); break; case GROUP_RELATIVE16: // long relative group_only_relative16_addressing(code); break; case GROUP_BOTHMOVES: // "mvp" and "mvn" group_move(code); break; default: // others indicate bugs Bug_found("IllegalGroupIndex", code); } return TRUE; } // Check whether mnemonic in GlobalDynaBuf is supported by 6502 cpu. int keyword_is_6502mnemo(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) ? TRUE : FALSE; } // Check whether mnemonic in GlobalDynaBuf is supported by 6510 cpu. int keyword_is_6510mnemo(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... if (check_mnemo_tree(mnemo_6510_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // Check whether mnemonic in GlobalDynaBuf is supported by 65c02 cpu. int keyword_is_65c02mnemo(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... 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) ? TRUE : FALSE; } //// Check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65c02 cpu. //int keyword_is_Rockwell65c02mnemo(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... // 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/WDC extensions (rmb, smb, bbr, bbs) // return check_mnemo_tree(mnemo_Rockwell65c02_tree, mnemo_dyna_buf) ? TRUE : FALSE; //} //// Check whether mnemonic in GlobalDynaBuf is supported by WDC 65c02 cpu. //int keyword_is_WDC65c02mnemo(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... // 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/WDC extensions (rmb, smb, bbr, bbs)... // if (check_mnemo_tree(mnemo_Rockwell65c02_tree, mnemo_dyna_buf)) // return TRUE; // // // ...then check WDC extensions (only two mnemonics, so do last) // return check_mnemo_tree(mnemo_WDC65c02_tree, mnemo_dyna_buf) ? TRUE : FALSE; //} // Check whether mnemonic in GlobalDynaBuf is supported by 65816 cpu. int keyword_is_65816mnemo(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 "new" extensions... if (check_mnemo_tree(mnemo_65816_tree, mnemo_dyna_buf)) return TRUE; // ...then "old" extensions... 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_WDC65c02_tree, mnemo_dyna_buf) ? TRUE : FALSE; }