/* AppleWin : An Apple //e emulator for Windows Copyright (C) 1994-1996, Michael O'Brien Copyright (C) 1999-2001, Oliver Schmidt Copyright (C) 2002-2005, Tom Charlesworth Copyright (C) 2006-2014, Tom Charlesworth, Michael Pohoreski AppleWin is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. AppleWin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AppleWin; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Description: Debugger Assembler * * Author: Copyright (C) 2006-2010 Michael Pohoreski */ #include "StdAfx.h" #include "Debug.h" #include "..\CPU.h" #include "..\Frame.h" #include "..\Memory.h" #define DEBUG_ASSEMBLER 0 // Globals __________________________________________________________________ // Addressing _____________________________________________________________________________________ AddressingMode_t g_aOpmodes[ NUM_ADDRESSING_MODES ] = { // Outut, but eventually used for Input when Assembler is working. {TEXT("") , 1 , "(implied)" }, // (implied) {TEXT("") , 1 , "n/a 1" }, // INVALID1 {TEXT("") , 2 , "n/a 2" }, // INVALID2 {TEXT("") , 3 , "n/a 3" }, // INVALID3 {TEXT("%02X") , 2 , "Immediate" }, // AM_M // #$%02X -> %02X {TEXT("%04X") , 3 , "Absolute" }, // AM_A {TEXT("%02X") , 2 , "Zero Page" }, // AM_Z {TEXT("%04X,X") , 3 , "Absolute,X" }, // AM_AX // %s,X {TEXT("%04X,Y") , 3 , "Absolute,Y" }, // AM_AY // %s,Y {TEXT("%02X,X") , 2 , "Zero Page,X" }, // AM_ZX // %s,X {TEXT("%02X,Y") , 2 , "Zero Page,Y" }, // AM_ZY // %s,Y {TEXT("%s") , 2 , "Relative" }, // AM_R {TEXT("(%02X,X)"), 2 , "(Zero Page),X" }, // AM_IZX // ($%02X,X) -> %s,X {TEXT("(%04X,X)"), 3 , "(Absolute),X" }, // AM_IAX // ($%04X,X) -> %s,X {TEXT("(%02X),Y"), 2 , "(Zero Page),Y" }, // AM_NZY // ($%02X),Y {TEXT("(%02X)") , 2 , "(Zero Page)" }, // AM_NZ // ($%02X) -> $%02X {TEXT("(%04X)") , 3 , "(Absolute)" } // AM_NA // (%04X) -> %s }; // Assembler ______________________________________________________________________________________ int g_bAssemblerOpcodesHashed = false; Hash_t g_aOpcodesHash[ NUM_OPCODES ]; // for faster mnemonic lookup, for the assembler bool g_bAssemblerInput = false; int g_nAssemblerAddress = 0; const Opcodes_t *g_aOpcodes = NULL; // & g_aOpcodes65C02[ 0 ]; // Disassembler Data _____________________________________________________________________________ std::vector g_aDisassemblerData; // Instructions / Opcodes _________________________________________________________________________ // @reference: http://www.6502.org/tutorials/compare_instructions.html // 10 signed: BPL BGE // B0 unsigned: BCS BGE #define R_ MEM_R #define _W MEM_W #define RW MEM_R | MEM_W #define _S MEM_S #define im MEM_IM #define SW MEM_S | MEM_WI #define SR MEM_S | MEM_RI const Opcodes_t g_aOpcodes65C02[ NUM_OPCODES ] = { {"BRK", 0 , 0}, {"ORA", AM_IZX, R_}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // 00 .. 03 {"TSB", AM_Z , _W}, {"ORA", AM_Z , R_}, {"ASL", AM_Z , RW}, {"nop", 0 , 0 }, // 04 .. 07 {"PHP", 0 , SW}, {"ORA", AM_M , im}, {"ASL", 0 , 0}, {"nop", 0 , 0 }, // 08 .. 0B {"TSB", AM_A , _W}, {"ORA", AM_A , R_}, {"ASL", AM_A , RW}, {"nop", 0 , 0 }, // 0C .. 0F {"BPL", AM_R , 0}, {"ORA", AM_NZY, R_}, {"ORA", AM_NZ , R_}, {"nop", 0 , 0 }, // 10 .. 13 {"TRB", AM_Z , _W}, {"ORA", AM_ZX , R_}, {"ASL", AM_ZX , RW}, {"nop", 0 , 0 }, // 14 .. 17 {"CLC", 0 , 0}, {"ORA", AM_AY , R_}, {"INC", 0 , 0}, {"nop", 0 , 0 }, // 18 .. 1B {"TRB", AM_A , _W}, {"ORA", AM_AX , R_}, {"ASL", AM_AX , RW}, {"nop", 0 , 0 }, // 1C .. 1F {"JSR", AM_A , SW}, {"AND", AM_IZX, R_}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // 20 .. 23 {"BIT", AM_Z , R_}, {"AND", AM_Z , R_}, {"ROL", AM_Z , RW}, {"nop", 0 , 0 }, // 24 .. 27 {"PLP", 0 , SR}, {"AND", AM_M , im}, {"ROL", 0 , 0}, {"nop", 0 , 0 }, // 28 .. 2B {"BIT", AM_A , R_}, {"AND", AM_A , R_}, {"ROL", AM_A , RW}, {"nop", 0 , 0 }, // 2C .. 2F {"BMI", AM_R , 0}, {"AND", AM_NZY, R_}, {"AND", AM_NZ , R_}, {"nop", 0 , 0 }, // 30 .. 33 {"BIT", AM_ZX , R_}, {"AND", AM_ZX , R_}, {"ROL", AM_ZX , RW}, {"nop", 0 , 0 }, // 34 .. 37 {"SEC", 0 , 0}, {"AND", AM_AY , R_}, {"DEC", 0 , 0}, {"nop", 0 , 0 }, // 38 .. 3B {"BIT", AM_AX , R_}, {"AND", AM_AX , R_}, {"ROL", AM_AX , RW}, {"nop", 0 , 0 }, // 3C .. 3F {"RTI", 0 , 0}, {"EOR", AM_IZX, R_}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // 40 .. 43 {"nop", AM_Z , 0}, {"EOR", AM_Z , R_}, {"LSR", AM_Z , _W}, {"nop", 0 , 0 }, // 44 .. 47 {"PHA", 0 , SW}, {"EOR", AM_M , im}, {"LSR", 0 , 0}, {"nop", 0 , 0 }, // 48 .. 4B {"JMP", AM_A , 0}, {"EOR", AM_A , R_}, {"LSR", AM_A , _W}, {"nop", 0 , 0 }, // 4C .. 4F {"BVC", AM_R , 0}, {"EOR", AM_NZY, R_}, {"EOR", AM_NZ , R_}, {"nop", 0 , 0 }, // 50 .. 53 {"nop", AM_ZX , 0}, {"EOR", AM_ZX , R_}, {"LSR", AM_ZX , _W}, {"nop", 0 , 0 }, // 54 .. 57 {"CLI", 0 , 0}, {"EOR", AM_AY , R_}, {"PHY", 0 , SW}, {"nop", 0 , 0 }, // 58 .. 5B {"nop", AM_AX , 0}, {"EOR", AM_AX , R_}, {"LSR", AM_AX , RW}, {"nop", 0 , 0 }, // 5C .. 5F {"RTS", 0 , SR}, {"ADC", AM_IZX, R_}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // 60 .. 63 {"STZ", AM_Z , _W}, {"ADC", AM_Z , R_}, {"ROR", AM_Z , RW}, {"nop", 0 , 0 }, // 64 .. 67 {"PLA", 0 , SR}, {"ADC", AM_M , im}, {"ROR", 0 , 0}, {"nop", 0 , 0 }, // 68 .. 6B {"JMP", AM_NA , 0}, {"ADC", AM_A , R_}, {"ROR", AM_A , RW}, {"nop", 0 , 0 }, // 6C .. 6F {"BVS", AM_R , 0}, {"ADC", AM_NZY, R_}, {"ADC", AM_NZ , R_}, {"nop", 0 , 0 }, // 70 .. 73 {"STZ", AM_ZX , _W}, {"ADC", AM_ZX , R_}, {"ROR", AM_ZX , RW}, {"nop", 0 , 0 }, // 74 .. 77 {"SEI", 0 , 0}, {"ADC", AM_AY , R_}, {"PLY", 0 , SR}, {"nop", 0 , 0 }, // 78 .. 7B {"JMP", AM_IAX, 0}, {"ADC", AM_AX , R_}, {"ROR", AM_AX , RW}, {"nop", 0 , 0 }, // 7C .. 7F {"BRA", AM_R , 0}, {"STA", AM_IZX, _W}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // 80 .. 83 {"STY", AM_Z , _W}, {"STA", AM_Z , _W}, {"STX", AM_Z , _W}, {"nop", 0 , 0 }, // 84 .. 87 {"DEY", 0 , 0}, {"BIT", AM_M , im}, {"TXA", 0 , 0}, {"nop", 0 , 0 }, // 88 .. 8B {"STY", AM_A , _W}, {"STA", AM_A , _W}, {"STX", AM_A , _W}, {"nop", 0 , 0 }, // 8C .. 8F {"BCC", AM_R , 0}, {"STA", AM_NZY, _W}, {"STA", AM_NZ , _W}, {"nop", 0 , 0 }, // 90 .. 93 {"STY", AM_ZX , _W}, {"STA", AM_ZX , _W}, {"STX", AM_ZY , _W}, {"nop", 0 , 0 }, // 94 .. 97 {"TYA", 0 , 0}, {"STA", AM_AY , _W}, {"TXS", 0 , 0}, {"nop", 0 , 0 }, // 98 .. 9B {"STZ", AM_A , _W}, {"STA", AM_AX , _W}, {"STZ", AM_AX , _W}, {"nop", 0 , 0 }, // 9C .. 9F {"LDY", AM_M , im}, {"LDA", AM_IZX, R_}, {"LDX", AM_M , im}, {"nop", 0 , 0 }, // A0 .. A3 {"LDY", AM_Z , R_}, {"LDA", AM_Z , R_}, {"LDX", AM_Z , R_}, {"nop", 0 , 0 }, // A4 .. A7 {"TAY", 0 , 0}, {"LDA", AM_M , im}, {"TAX", 0 , 0 }, {"nop", 0 , 0 }, // A8 .. AB {"LDY", AM_A , R_}, {"LDA", AM_A , R_}, {"LDX", AM_A , R_}, {"nop", 0 , 0 }, // AC .. AF {"BCS", AM_R , 0}, {"LDA", AM_NZY, R_}, {"LDA", AM_NZ , R_}, {"nop", 0 , 0 }, // B0 .. B3 {"LDY", AM_ZX , R_}, {"LDA", AM_ZX , R_}, {"LDX", AM_ZY , R_}, {"nop", 0 , 0 }, // B4 .. B7 {"CLV", 0 , 0}, {"LDA", AM_AY , R_}, {"TSX", 0 , 0 }, {"nop", 0 , 0 }, // B8 .. BB {"LDY", AM_AX , R_}, {"LDA", AM_AX , R_}, {"LDX", AM_AY , R_}, {"nop", 0 , 0 }, // BC .. BF {"CPY", AM_M , im}, {"CMP", AM_IZX, R_}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // C0 .. C3 {"CPY", AM_Z , R_}, {"CMP", AM_Z , R_}, {"DEC", AM_Z , RW}, {"nop", 0 , 0 }, // C4 .. C7 {"INY", 0 , 0}, {"CMP", AM_M , im}, {"DEX", 0 , 0}, {"nop", 0 , 0 }, // C8 .. CB {"CPY", AM_A , R_}, {"CMP", AM_A , R_}, {"DEC", AM_A , RW}, {"nop", 0 , 0 }, // CC .. CF {"BNE", AM_R , 0}, {"CMP", AM_NZY, R_}, {"CMP", AM_NZ , 0}, {"nop", 0 , 0 }, // D0 .. D3 {"nop", AM_ZX , 0}, {"CMP", AM_ZX , R_}, {"DEC", AM_ZX , RW}, {"nop", 0 , 0 }, // D4 .. D7 {"CLD", 0 , 0}, {"CMP", AM_AY , R_}, {"PHX", 0 , 0}, {"nop", 0 , 0 }, // D8 .. DB {"nop", AM_AX , 0}, {"CMP", AM_AX , R_}, {"DEC", AM_AX , RW}, {"nop", 0 , 0 }, // DC .. DF {"CPX", AM_M , im}, {"SBC", AM_IZX, R_}, {"nop", AM_M , im}, {"nop", 0 , 0 }, // E0 .. E3 {"CPX", AM_Z , R_}, {"SBC", AM_Z , R_}, {"INC", AM_Z , RW}, {"nop", 0 , 0 }, // E4 .. E7 {"INX", 0 , 0}, {"SBC", AM_M , R_}, {"NOP", 0 , 0}, {"nop", 0 , 0 }, // E8 .. EB {"CPX", AM_A , R_}, {"SBC", AM_A , R_}, {"INC", AM_A , RW}, {"nop", 0 , 0 }, // EC .. EF {"BEQ", AM_R , 0}, {"SBC", AM_NZY, R_}, {"SBC", AM_NZ , 0}, {"nop", 0 , 0 }, // F0 .. F3 {"nop", AM_ZX , 0}, {"SBC", AM_ZX , R_}, {"INC", AM_ZX , RW}, {"nop", 0 , 0 }, // F4 .. F7 {"SED", 0 , 0}, {"SBC", AM_AY , R_}, {"PLX", 0 , 0}, {"nop", 0 , 0 }, // F8 .. FB {"nop", AM_AX , 0}, {"SBC", AM_AX , R_}, {"INC", AM_AX , RW}, {"nop", 0 , 0 } // FF .. FF }; const Opcodes_t g_aOpcodes6502[ NUM_OPCODES ] = { // Should match Cpu.cpp InternalCpuExecute() switch (*(mem+regs.pc++)) !! /* Based on: http://axis.llx.com/~nparker/a2/opcodes.html If you really want to know what the undocumented --- (n/a) opcodes do, see CPU.cpp x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF 0x BRK ORA (d,X) --- --- tsb d ORA d ASL d --- PHP ORA # ASL A --- tsb a ORA a ASL a --- 1x BPL r ORA (d),Y ora (d) --- trb d ORA d,X ASL d,X --- CLC ORA a,Y ina A --- trb a ORA a,X ASL a,X --- 2x JSR a AND (d,X) --- --- BIT d AND d ROL d --- PLP AND # ROL A --- BIT a AND a ROL a --- 3x BMI r AND (d),Y and (d) --- bit d,X AND d,X ROL d,X --- SEC AND a,Y dea A --- bit a,X AND a,X ROL a,X --- 4x RTI EOR (d,X) --- --- --- EOR d LSR d --- PHA EOR # LSR A --- JMP a EOR a LSR a --- 5x BVC r EOR (d),Y eor (d) --- --- EOR d,X LSR d,X --- CLI EOR a,Y phy --- --- EOR a,X LSR a,X --- 6x RTS ADC (d,X) --- --- stz d ADC d ROR d --- PLA ADC # ROR A --- JMP (a) ADC a ROR a --- 7x BVS r ADC (d),Y adc (d) --- stz d,X ADC d,X ROR d,X --- SEI ADC a,Y ply --- jmp (a,X) ADC a,X ROR a,X --- 8x bra r STA (d,X) --- --- STY d STA d STX d --- DEY bit # TXA --- STY a STA a STX a --- 9x BCC r STA (d),Y sta (d) --- STY d,X STA d,X STX d,Y --- TYA STA a,Y TXS --- Stz a STA a,X stz a,X --- Ax LDY # LDA (d,X) LDX # --- LDY d LDA d LDX d --- TAY LDA # TAX --- LDY a LDA a LDX a --- Bx BCS r LDA (d),Y lda (d) --- LDY d,X LDA d,X LDX d,Y --- CLV LDA a,Y TSX --- LDY a,X LDA a,X LDX a,Y --- Cx CPY # CMP (d,X) --- --- CPY d CMP d DEC d --- INY CMP # DEX --- CPY a CMP a DEC a --- Dx BNE r CMP (d),Y cmp (d) --- --- CMP d,X DEC d,X --- CLD CMP a,Y phx --- --- CMP a,X DEC a,X --- Ex CPX # SBC (d,X) --- --- CPX d SBC d INC d --- INX SBC # NOP --- CPX a SBC a INC a --- Fx BEQ r SBC (d),Y sbc (d) --- --- SBC d,X INC d,X --- SED SBC a,Y plx --- --- SBC a,X INC a,X --- Legend: UPPERCASE 6502 lowercase 65C02 80 12, 32, 52, 72, 92, B2, D2, F2 04, 14, 34, 64, 74 89 1A, 3A, 5A, 7A, DA, FA 0C, 1C, 3C, 7C, 9C; # Immediate A Accumulator (implicit for mnemonic) a absolute r Relative d Destination z Zero Page d,x (d,X) (d),Y */ {"BRK", 0 , 0}, {"ORA", AM_IZX, R_}, {"hlt", 0 , 0 }, {"aso", AM_IZX, RW}, // 00 .. 03 {"nop", AM_Z , R_}, {"ORA", AM_Z , R_}, {"ASL", AM_Z , RW}, {"aso", AM_Z , RW}, // 04 .. 07 {"PHP", 0 , SW}, {"ORA", AM_M , im}, {"ASL", 0 , 0}, {"anc", AM_M , im}, // 08 .. 0B {"nop", AM_AX , 0}, {"ORA", AM_A , R_}, {"ASL", AM_A , RW}, {"aso", AM_A , RW}, // 0C .. 0F {"BPL", AM_R , 0}, {"ORA", AM_NZY, R_}, {"hlt", 0 , 0}, {"aso", AM_NZY, RW}, // 10 .. 13 {"nop", AM_ZX , 0}, {"ORA", AM_ZX , R_}, {"ASL", AM_ZX , RW}, {"aso", AM_ZX , RW}, // 14 .. 17 {"CLC", 0 , 0}, {"ORA", AM_AY , R_}, {"nop", 0 , 0}, {"aso", AM_AY , RW}, // 18 .. 1B {"nop", AM_AX , 0}, {"ORA", AM_AX , R_}, {"ASL", AM_AX , RW}, {"aso", AM_AX , RW}, // 1C .. 1F {"JSR", AM_A , SW}, {"AND", AM_IZX, R_}, {"hlt", 0 , 0}, {"rla", AM_IZX, RW}, // 20 .. 23 {"BIT", AM_Z , R_}, {"AND", AM_Z , R_}, {"ROL", AM_Z , RW}, {"rla", AM_Z , RW}, // 24 .. 27 {"PLP", 0 , SR}, {"AND", AM_M , im}, {"ROL", 0 , 0}, {"anc", AM_M , im}, // 28 .. 2B {"BIT", AM_A , R_}, {"AND", AM_A , R_}, {"ROL", AM_A , RW}, {"rla", AM_A , RW}, // 2C .. 2F {"BMI", AM_R , 0}, {"AND", AM_NZY, R_}, {"hlt", 0 , 0}, {"rla", AM_NZY, RW}, // 30 .. 33 {"nop", AM_ZX , 0}, {"AND", AM_ZX , R_}, {"ROL", AM_ZX , RW}, {"rla", AM_ZX , RW}, // 34 .. 37 {"SEC", 0 , 0}, {"AND", AM_AY , R_}, {"nop", 0 , 0}, {"rla", AM_AY , RW}, // 38 .. 3B {"nop", AM_AX , 0}, {"AND", AM_AX , R_}, {"ROL", AM_AX , RW}, {"rla", AM_AX , RW}, // 3C .. 3F {"RTI", 0 , 0}, {"EOR", AM_IZX, R_}, {"hlt", 0 , 0}, {"lse", AM_IZX, RW}, // 40 .. 43 {"nop", AM_Z , 0}, {"EOR", AM_Z , R_}, {"LSR", AM_Z , RW}, {"lse", AM_Z , RW}, // 44 .. 47 {"PHA", 0 , SW}, {"EOR", AM_M , im}, {"LSR", 0 , 0}, {"alr", AM_M , im}, // 48 .. 4B {"JMP", AM_A , 0}, {"EOR", AM_A , R_}, {"LSR", AM_A , RW}, {"lse", AM_A , RW}, // 4C .. 4F {"BVC", AM_R , 0}, {"EOR", AM_NZY, R_}, {"hlt", 0 , 0}, {"lse", AM_NZY, RW}, // 50 .. 53 {"nop", AM_ZX , 0}, {"EOR", AM_ZX , R_}, {"LSR", AM_ZX , RW}, {"lse", AM_ZX , RW}, // 54 .. 57 {"CLI", 0 , 0}, {"EOR", AM_AY , R_}, {"nop", 0 , 0}, {"lse", AM_AY , RW}, // 58 .. 5B {"nop", AM_AX , 0}, {"EOR", AM_AX , R_}, {"LSR", AM_AX , RW}, {"lse", AM_AX , RW}, // 5C .. 5F {"RTS", 0 , SR}, {"ADC", AM_IZX, R_}, {"hlt", 0 , 0}, {"rra", AM_IZX, RW}, // 60 .. 63 {"nop", AM_Z , 0}, {"ADC", AM_Z , R_}, {"ROR", AM_Z , RW}, {"rra", AM_Z , RW}, // 64 .. 67 {"PLA", 0 , SR}, {"ADC", AM_M , im}, {"ROR", 0 , 0}, {"arr", AM_M , im}, // 68 .. 6B {"JMP", AM_NA , 0}, {"ADC", AM_A , R_}, {"ROR", AM_A , RW}, {"rra", AM_A , RW}, // 6C .. 6F {"BVS", AM_R , 0}, {"ADC", AM_NZY, R_}, {"hlt", 0 , 0}, {"rra", AM_NZY, RW}, // 70 .. 73 {"nop", AM_ZX , 0}, {"ADC", AM_ZX , R_}, {"ROR", AM_ZX , RW}, {"rra", AM_ZX , RW}, // 74 .. 77 {"SEI", 0 , 0}, {"ADC", AM_AY , R_}, {"nop", 0 , 0}, {"rra", AM_AY , RW}, // 78 .. 7B {"nop", AM_AX , 0}, {"ADC", AM_AX , R_}, {"ROR", AM_AX , RW}, {"rra", AM_AX , RW}, // 7C .. 7F {"nop", AM_M , im}, {"STA", AM_IZX, _W}, {"nop", AM_M , im}, {"axs", AM_IZX, _W}, // 80 .. 83 {"STY", AM_Z , _W}, {"STA", AM_Z , _W}, {"STX", AM_Z , _W}, {"axs", AM_Z , _W}, // 84 .. 87 {"DEY", 0 , 0}, {"nop", AM_M , im}, {"TXA", 0 , 0}, {"xaa", AM_M , im}, // 88 .. 8B {"STY", AM_A , _W}, {"STA", AM_A , _W}, {"STX", AM_A , _W}, {"axs", AM_A , _W}, // 8C .. 8F {"BCC", AM_R , 0}, {"STA", AM_NZY, _W}, {"hlt", 0 , 0}, {"axa", AM_NZY, _W}, // 90 .. 93 {"STY", AM_ZX , _W}, {"STA", AM_ZX , _W}, {"STX", AM_ZY , _W}, {"axs", AM_ZY , _W}, // 94 .. 97 {"TYA", 0 , 0}, {"STA", AM_AY , _W}, {"TXS", 0 , 0}, {"tas", AM_AY , _W}, // 98 .. 9B {"say", AM_AX , _W}, {"STA", AM_AX , _W}, {"xas", AM_AX , _W}, {"axa", AM_AY , _W}, // 9C .. 9F {"LDY", AM_M , im}, {"LDA", AM_IZX, R_}, {"LDX", AM_M , im}, {"lax", AM_IZX, R_}, // A0 .. A3 {"LDY", AM_Z , R_}, {"LDA", AM_Z , R_}, {"LDX", AM_Z , R_}, {"lax", AM_Z , R_}, // A4 .. A7 {"TAY", 0 , 0}, {"LDA", AM_M , im}, {"TAX", 0 , 0 }, {"oal", AM_M , im}, // A8 .. AB {"LDY", AM_A , R_}, {"LDA", AM_A , R_}, {"LDX", AM_A , R_}, {"lax", AM_A , R_}, // AC .. AF {"BCS", AM_R , 0}, {"LDA", AM_NZY, R_}, {"hlt", 0 , 0 }, {"lax", AM_NZY, R_}, // B0 .. B3 {"LDY", AM_ZX , R_}, {"LDA", AM_ZX , R_}, {"LDX", AM_ZY , R_}, {"lax", AM_ZY , 0 }, // B4 .. B7 {"CLV", 0 , 0}, {"LDA", AM_AY , R_}, {"TSX", 0 , 0 }, {"las", AM_AY , R_}, // B8 .. BB {"LDY", AM_AX , R_}, {"LDA", AM_AX , R_}, {"LDX", AM_AY , R_}, {"lax", AM_AY , R_}, // BC .. BF {"CPY", AM_M , im}, {"CMP", AM_IZX, R_}, {"nop", AM_M , im}, {"dcm", AM_IZX, RW}, // C0 .. C3 {"CPY", AM_Z , R_}, {"CMP", AM_Z , R_}, {"DEC", AM_Z , RW}, {"dcm", AM_Z , RW}, // C4 .. C7 {"INY", 0 , 0}, {"CMP", AM_M , im}, {"DEX", 0 , 0}, {"sax", AM_M , im}, // C8 .. CB {"CPY", AM_A , R_}, {"CMP", AM_A , R_}, {"DEC", AM_A , RW}, {"dcm", AM_A , RW}, // CC .. CF {"BNE", AM_R , 0}, {"CMP", AM_NZY, R_}, {"hlt", 0 , 0}, {"dcm", AM_NZY, RW}, // D0 .. D3 {"nop", AM_ZX , 0}, {"CMP", AM_ZX , R_}, {"DEC", AM_ZX , RW}, {"dcm", AM_ZX , RW}, // D4 .. D7 {"CLD", 0 , 0}, {"CMP", AM_AY , R_}, {"nop", 0 , 0}, {"dcm", AM_AY , RW}, // D8 .. DB {"nop", AM_AX , 0}, {"CMP", AM_AX , R_}, {"DEC", AM_AX , RW}, {"dcm", AM_AX , RW}, // DC .. DF {"CPX", AM_M , im}, {"SBC", AM_IZX, R_}, {"nop", AM_M , im}, {"ins", AM_IZX, RW}, // E0 .. E3 {"CPX", AM_Z , R_}, {"SBC", AM_Z , R_}, {"INC", AM_Z , RW}, {"ins", AM_Z , RW}, // E4 .. E7 {"INX", 0 , 0}, {"SBC", AM_M , im}, {"NOP", 0 , 0}, {"sbc", AM_M , im}, // E8 .. EB {"CPX", AM_A , R_}, {"SBC", AM_A , R_}, {"INC", AM_A , RW}, {"ins", AM_A , RW}, // EC .. EF {"BEQ", AM_R , 0}, {"SBC", AM_NZY, R_}, {"hlt", 0 , 0}, {"ins", AM_NZY, RW}, // F0 .. F3 {"nop", AM_ZX , 0}, {"SBC", AM_ZX , R_}, {"INC", AM_ZX , RW}, {"ins", AM_ZX , RW}, // F4 .. F7 {"SED", 0 , 0}, {"SBC", AM_AY , R_}, {"nop", 0 , 0}, {"ins", AM_AY , RW}, // F8 .. FB {"nop", AM_AX , 0}, {"SBC", AM_AX , R_}, {"INC", AM_AX , RW}, {"ins", AM_AX , RW} // FF .. FF }; #undef R_ #undef _W #undef RW #undef _S #undef im #undef SW #undef SR // @reference: http://www.textfiles.com/apple/DOCUMENTATION/merlin.docs1 // Private __________________________________________________________________ // NOTE: Keep in sync AsmDirectives_e g_aAssemblerDirectives ! AssemblerDirective_t g_aAssemblerDirectives[ NUM_ASM_DIRECTIVES ] = { // NULL n/a {""}, // Origin, Target Address, EndProg, Equate, Data, AsciiString,HexString // Acme {"???"}, // Big Mac {"???"}, // DOS Tool Kit {"???"}, // Lisa {"???"}, // Merlin {"ASC"}, // ASC "postive" 'negative' {"DDB"}, // Define Double Byte (Define WORD) {"DFB"}, // DeFine Byte {"DS" }, // Define Storage {"HEX"}, // HEX ###### or HEX ##,##,... {"ORG"}, // Origin // MicroSparc {"???"}, // ORCA/M {"???"}, // SC ... {".OR"}, // ORigin {".TA"}, // Target Address {".EN"}, // ENd of program {".EQ"}, // EQuate {".DA"}, // DAta {".AS"}, // Ascii String {".HS"}, // Hex String // Ted II {"???"}, // Weller {"???"}, // User-Defined // NOTE: Keep in sync AsmCustomDirective_e g_aAssemblerDirectives ! {"db" }, // ASM_DEFINE_BYTE {"dw" }, // ASM_DEFINE_WORD {"da" }, // ASM_DEFINE_ADDRESS_16 // d memory Dump // da Memory Ascii, Define Address // ds S = Ascii (Low), // dt T = Apple (High) // dm M = Mixed (Low,High=EndofString) {"ds" }, // ASM_DEFINE_ASCII_TEXT {"dt" }, // ASM_DEFINE_APPLE_TEXT {"dm" }, // ASM_DEFINE_TEXT_HI_LO {"df" }, // ASM_DEFINE_FLOAT {"dfx"}, // ASM_DEFINE_FLOAT_X }; int g_iAssemblerSyntax = ASM_CUSTOM; // Which assembler syntax to use int g_aAssemblerFirstDirective[ NUM_ASSEMBLERS ] = { FIRST_A_DIRECTIVE, FIRST_B_DIRECTIVE, FIRST_D_DIRECTIVE, FIRST_L_DIRECTIVE, FIRST_M_DIRECTIVE, FIRST_u_DIRECTIVE, FIRST_O_DIRECTIVE, FIRST_S_DIRECTIVE, FIRST_T_DIRECTIVE, FIRST_W_DIRECTIVE, FIRST_Z_DIRECTIVE }; // Assemblers enum AssemblerFlags_e { AF_HaveLabel = (1 << 0), AF_HaveComma = (1 << 1), AF_HaveHash = (1 << 2), AF_HaveImmediate = (1 << 3), AF_HaveDollar = (1 << 4), AF_HaveLeftParen = (1 << 5), AF_HaveRightParen = (1 << 6), AF_HaveEitherParen= (1 << 7), AF_HaveBothParen = (1 << 8), AF_HaveRegisterX = (1 << 9), AF_HaveRegisterY = (1 <<10), AF_HaveZeroPage = (1 <<11), AF_HaveTarget = (1 <<12), }; enum AssemblerState_e { AS_GET_MNEMONIC , AS_GET_MNEMONIC_PARM , AS_GET_HASH , AS_GET_TARGET , AS_GET_PAREN , AS_GET_INDEX , AS_DONE }; int m_bAsmFlags; std::vector m_vAsmOpcodes; int m_iAsmAddressMode = AM_IMPLIED; struct DelayedTarget_t { char m_sAddress[ MAX_SYMBOLS_LEN + 1 ]; WORD m_nBaseAddress; // mem address to store symbol at int m_nOpcode ; int m_iOpmode ; // AddressingMode_e }; std::vector m_vDelayedTargets; bool m_bDelayedTargetsDirty = false; int m_nAsmBytes = 0; WORD m_nAsmBaseAddress = 0; WORD m_nAsmTargetAddress = 0; WORD m_nAsmTargetValue = 0; // Private void AssemblerHashOpcodes (); void AssemblerHashDirectives (); // Implementation ___________________________________________________________ //=========================================================================== bool _6502_CalcRelativeOffset( int nOpcode, int nBaseAddress, int nTargetAddress, WORD * pTargetOffset_ ) { if (_6502_IsOpcodeBranch( nOpcode)) { // Branch is // a) relative to address+2 // b) in 2's compliment // // i.e. // 300: D0 7F -> BNE $381 0x381 - 0x300 = 0x81 +129 // 300: D0 80 -> BNE $282 0x282 - 0x300 = -126 // // 300: D0 7E BNE $380 // ^ ^ ^ ^ // | | | TargetAddress // | | TargetOffset // | Opcode // BaseAddress int nDistance = nTargetAddress - nBaseAddress; if (pTargetOffset_) *pTargetOffset_ = (BYTE)(nDistance - 2); if ((nDistance - 2) > _6502_BRANCH_POS) m_iAsmAddressMode = NUM_OPMODES; // signal bad if ((nDistance - 2) < _6502_BRANCH_NEG) m_iAsmAddressMode = NUM_OPMODES; // signal bad return true; } return false; } //=========================================================================== int _6502_GetOpmodeOpbyte ( const int nBaseAddress, int & iOpmode_, int & nOpbyte_, const DisasmData_t** pData_ ) { #if _DEBUG if (! g_aOpcodes) { MessageBox( g_hFrameWindow, "Debugger not properly initialized", "ERROR", MB_OK ); } #endif int iOpcode_ = *(mem + nBaseAddress); iOpmode_ = g_aOpcodes[ iOpcode_ ].nAddressMode; nOpbyte_ = g_aOpmodes[ iOpmode_ ].m_nBytes; // 2.6.2.25 Fixed: DB DW custom data byte sizes weren't scrolling properly in the disasm view. // Changed _6502_GetOpmodeOpbyte() to be aware of data bytes. // // NOTE: _6502_GetOpmodeOpbyte() needs to (effectively) call Disassembly_GetData() // a) the CmdCursorLineUp() calls us to calc for -X bytes back up how to reach the cursor (address) line below // b) The disassembler view needs to know how many bytes each line is. int nSlack; // 2.7.0.0 TODO: FIXME: Opcode length that over-lap data, should be shortened ... if (nOpbyte_ > 1) if Disassembly_IsDataAddress( nBaseAddress + 1 ) nOpbyte_ = 1; DisasmData_t* pData = Disassembly_IsDataAddress( nBaseAddress ); if( pData ) { if( pData_ ) *pData_ = pData; nSlack = pData->nEndAddress - pData->nStartAddress + 1; // *inclusive* KEEP IN SYNC: _CmdDefineByteRange() CmdDisasmDataList() _6502_GetOpmodeOpbyte() FormatNopcodeBytes() // Data Disassembler // Smart Disassembly - Data Section // Assemblyer Directives - Psuedo Mnemonics switch( pData->eElementType ) { case NOP_BYTE_1: nOpbyte_ = 1; iOpmode_ = AM_M; break; case NOP_BYTE_2: nOpbyte_ = 2; iOpmode_ = AM_M; break; case NOP_BYTE_4: nOpbyte_ = 4; iOpmode_ = AM_M; break; case NOP_BYTE_8: nOpbyte_ = 8; iOpmode_ = AM_M; break; case NOP_WORD_1: nOpbyte_ = 2; iOpmode_ = AM_M; break; case NOP_WORD_2: nOpbyte_ = 4; iOpmode_ = AM_M; break; case NOP_WORD_4: nOpbyte_ = 8; iOpmode_ = AM_M; break; case NOP_ADDRESS:nOpbyte_ = 2; iOpmode_ = AM_A; // BUGFIX: 2.6.2.33 Define Address should be shown as Absolute mode, not Indirect Absolute mode. DA BASIC.FPTR D000:D080 // was showing as "da (END-1)" now shows as "da END-1" pData->nTargetAddress = *(LPWORD)(mem+nBaseAddress); break; case NOP_STRING_APPLE: iOpmode_ = AM_DATA; nOpbyte_ = nSlack; break; case NOP_STRING_APPLESOFT: // TODO: FIXME: scan memory for high byte nOpbyte_ = 8; iOpmode_ = AM_DATA; break; default: #if _DEBUG // not implemented! int *fatal = 0; *fatal = 0xDEADC0DE; #endif break; } /* // REMOVED in v1.25 ... because of AppleSoft Basic: DW NEXT1 801 DW LINE1 803 // Check if we are not element aligned ... nSlack = (nOpbyte_ > 1) ? (nBaseAddress & nOpbyte_-1 ) : 0; if (nSlack) { nOpbyte_ = nSlack; iOpmode_ = AM_M; } */ //iOpcode_ = NUM_OPCODES; // Don't have valid opcodes ... we have data ! // iOpcode_ = (int)( pData ); // HACK: pass pData back to caller ... iOpcode_ = 0xEA; // OP_NOP } #if _DEBUG if (iOpcode_ >= NUM_OPCODES) { bool bStop = true; } #endif return iOpcode_; } //=========================================================================== void _6502_GetOpcodeOpmodeOpbyte ( int & iOpcode_, int & iOpmode_, int & nOpbyte_ ) { iOpcode_ = _6502_GetOpmodeOpbyte( regs.pc, iOpmode_, nOpbyte_ ); } //=========================================================================== bool _6502_GetStackReturnAddress ( WORD & nAddress_ ) { unsigned nStack = regs.sp; nStack++; if (nStack <= (_6502_STACK_END - 1)) { nAddress_ = 0; nAddress_ = (unsigned)*(LPBYTE)(mem + nStack); nStack++; nAddress_ += ((unsigned)*(LPBYTE)(mem + nStack)) << 8; nAddress_++; return true; } return false; } //=========================================================================== bool _6502_GetTargets ( WORD nAddress, int *pTargetPartial_, int *pTargetPointer_, int * pTargetBytes_, bool bIgnoreJSRJMP, bool bIgnoreBranch ) { bool bStatus = false; if (! pTargetPartial_) return bStatus; if (! pTargetPointer_) return bStatus; // if (! pTargetBytes_) // return bStatus; *pTargetPartial_ = NO_6502_TARGET; *pTargetPointer_ = NO_6502_TARGET; if (pTargetBytes_) *pTargetBytes_ = 0; bStatus = true; BYTE nOpcode = *(LPBYTE)(mem + nAddress ); BYTE nTarget8 = *(LPBYTE)(mem + nAddress + 1); WORD nTarget16 = *(LPWORD)(mem + nAddress + 1); int eMode = g_aOpcodes[ nOpcode ].nAddressMode; // We really need to use the values that are code and data assembler // TODO: FIXME: _6502_GetOpmodeOpbyte( iAddress, iOpmode, nOpbytes ); switch (eMode) { case AM_A: // $Absolute *pTargetPointer_ = nTarget16; if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_IAX: // Indexed (Absolute) Indirect nTarget16 += regs.x; *pTargetPartial_ = nTarget16; *pTargetPointer_ = *(LPWORD)(mem + nTarget16); if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_AX: // Absolute, X nTarget16 += regs.x; *pTargetPointer_ = nTarget16; if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_AY: // Absolute, Y nTarget16 += regs.y; *pTargetPointer_ = nTarget16; if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_NA: // Indirect (Absolute) i.e. JMP *pTargetPartial_ = nTarget16; *pTargetPointer_ = *(LPWORD)(mem + nTarget16); if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_IZX: // Indexed (Zeropage Indirect, X) nTarget8 += regs.x; *pTargetPartial_ = nTarget8; *pTargetPointer_ = *(LPWORD)(mem + nTarget8); if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_NZY: // Indirect (Zeropage) Indexed, Y *pTargetPartial_ = nTarget8; *pTargetPointer_ = ((*(LPWORD)(mem + nTarget8)) + regs.y) & _6502_MEM_END; // Bugfix: if (pTargetBytes_) *pTargetBytes_ = 1; break; case AM_NZ: // Indirect (Zeropage) *pTargetPartial_ = nTarget8; *pTargetPointer_ = *(LPWORD)(mem + nTarget8); if (pTargetBytes_) *pTargetBytes_ = 2; break; case AM_R: if (! bIgnoreBranch) { *pTargetPartial_ = nTarget8; *pTargetPointer_ = nAddress + 2; if (nTarget8 <= _6502_BRANCH_POS) *pTargetPointer_ += nTarget8; // + else *pTargetPointer_ -= nTarget8; // - *pTargetPointer_ &= _6502_MEM_END; if (pTargetBytes_) *pTargetBytes_ = 1; } break; case AM_Z: // Zeropage *pTargetPointer_ = nTarget8; if (pTargetBytes_) *pTargetBytes_ = 1; break; case AM_ZX: // Zeropage, X *pTargetPointer_ = (nTarget8 + regs.x) & 0xFF; // .21 Bugfix: shouldn't this wrap around? Yes. if (pTargetBytes_) *pTargetBytes_ = 1; break; case AM_ZY: // Zeropage, Y *pTargetPointer_ = (nTarget8 + regs.y) & 0xFF; // .21 Bugfix: shouldn't this wrap around? Yes. if (pTargetBytes_) *pTargetBytes_ = 1; break; default: if (pTargetBytes_) *pTargetBytes_ = 0; break; } if (bIgnoreJSRJMP) { // If 6502 is jumping, don't show byte [nAddressTarget] if ((*pTargetPointer_ >= 0) && ( (nOpcode == OPCODE_JSR ) || // 0x20 (nOpcode == OPCODE_JMP_A ))) // 0x4C // (nOpcode == OPCODE_JMP_NA ) || // 0x6C // (nOpcode == OPCODE_JMP_IAX))) // 0x7C { *pTargetPointer_ = NO_6502_TARGET; if (pTargetBytes_) *pTargetBytes_ = 0; } } return bStatus; } //=========================================================================== bool _6502_GetTargetAddress ( const WORD & nAddress, WORD & nTarget_ ) { int iOpcode; int iOpmode; int nOpbytes; iOpcode = _6502_GetOpmodeOpbyte( nAddress, iOpmode, nOpbytes ); // Composite string that has the target nAddress // WORD nTarget = 0; int nTargetOffset_ = 0; if ((iOpmode != AM_IMPLIED) && (iOpmode != AM_1) && (iOpmode != AM_2) && (iOpmode != AM_3)) { int nTargetPartial; int nTargetPointer; WORD nTargetValue = 0; // de-ref int nTargetBytes; _6502_GetTargets( nAddress, &nTargetPartial, &nTargetPointer, &nTargetBytes, false, false ); // if (nTargetPointer == NO_6502_TARGET) // { // if (_6502_IsOpcodeBranch( nOpcode ) // { // return true; // } // } if (nTargetPointer != NO_6502_TARGET) // else { nTarget_ = nTargetPointer & _6502_MEM_END; return true; } } return false; } //=========================================================================== bool _6502_IsOpcodeBranch ( int iOpcode ) { // 76543210 Bit // xxx10000 Branch if (iOpcode == OPCODE_BRA) return true; if ((iOpcode & 0x1F) != 0x10) // low nibble not zero? return false; if ((iOpcode >> 4) & 1) return true; // (nOpcode == 0x10) || // BPL // (nOpcode == 0x30) || // BMI // (nOpcode == 0x50) || // BVC // (nOpcode == 0x70) || // BVS // (nOpcode == 0x90) || // BCC // (nOpcode == 0xB0) || // BCS // (nOpcode == 0xD0) || // BNE // (nOpcode == 0xF0) || // BEQ return false; } //=========================================================================== bool _6502_IsOpcodeValid ( int iOpcode ) { if ((iOpcode & 0x3) == 0x3) return false; if (islower( g_aOpcodes6502[ iOpcode ].sMnemonic[ 0 ] )) return false; return true; } // Assembler ________________________________________________________________ //=========================================================================== int AssemblerHashMnemonic ( const TCHAR * pMnemonic ) { const TCHAR *pText = pMnemonic; int nMnemonicHash = 0; int iHighBits; const int NUM_LOW_BITS = 19; // 24 -> 19 prime const int NUM_MSK_BITS = 5; // 4 -> 5 prime const Hash_t BIT_MSK_HIGH = ((1 << NUM_MSK_BITS) - 1) << NUM_LOW_BITS; int nLen = strlen( pMnemonic ); #if DEBUG_ASSEMBLER static char sText[ CONSOLE_WIDTH * 3 ]; static int nMaxLen = 0; if (nMaxLen < nLen) { nMaxLen = nLen; sprintf( sText, "New Max Len: %d %s", nMaxLen, pMnemonic ); ConsolePrint( sText ); } #endif while( *pText ) // for( int iChar = 0; iChar < 4; iChar++ ) { char c = tolower( *pText ); // TODO: based on ALLOW_INPUT_LOWERCASE ?? nMnemonicHash = (nMnemonicHash << NUM_MSK_BITS) + c; iHighBits = (nMnemonicHash & BIT_MSK_HIGH); if (iHighBits) { nMnemonicHash = (nMnemonicHash ^ (iHighBits >> NUM_LOW_BITS)) & ~ BIT_MSK_HIGH; } pText++; } return nMnemonicHash; } //=========================================================================== void AssemblerHashOpcodes () { static char sText[ 128 ]; Hash_t nMnemonicHash; int iOpcode; for( iOpcode = 0; iOpcode < NUM_OPCODES; iOpcode++ ) { const TCHAR *pMnemonic = g_aOpcodes65C02[ iOpcode ].sMnemonic; nMnemonicHash = AssemblerHashMnemonic( pMnemonic ); g_aOpcodesHash[ iOpcode ] = nMnemonicHash; #if DEBUG_ASSEMBLER //OutputDebugString( "" ); sprintf( sText, "%s : %08X ", pMnemonic, nMnemonicHash ); ConsolePrint( sText ); // CLC: 002B864 #endif } ConsoleUpdate(); } //=========================================================================== void AssemblerHashDirectives () { Hash_t nMnemonicHash; int iOpcode; for( iOpcode = 0; iOpcode < NUM_ASM_M_DIRECTIVES; iOpcode++ ) { int iNopcode = FIRST_M_DIRECTIVE + iOpcode; //. const TCHAR *pMnemonic = g_aAssemblerDirectivesMerlin[ iOpcode ].m_pMnemonic; const TCHAR *pMnemonic = g_aAssemblerDirectives[ iNopcode ].m_pMnemonic; nMnemonicHash = AssemblerHashMnemonic( pMnemonic ); g_aAssemblerDirectives[ iNopcode ].m_nHash = nMnemonicHash; } } //=========================================================================== void AssemblerStartup() { AssemblerHashOpcodes(); AssemblerHashDirectives(); } //=========================================================================== void _CmdAssembleHashDump () { // #if DEBUG_ASM_HASH std::vector vHashes; HashOpcode_t tHash; TCHAR sText[ CONSOLE_WIDTH ]; int iOpcode; for( iOpcode = 0; iOpcode < NUM_OPCODES; iOpcode++ ) { tHash.m_iOpcode = iOpcode; tHash.m_nValue = g_aOpcodesHash[ iOpcode ]; vHashes.push_back( tHash ); } std::sort( vHashes.begin(), vHashes.end(), HashOpcode_t() ); Hash_t nPrevHash = vHashes.at( 0 ).m_nValue; Hash_t nThisHash = 0; for( iOpcode = 0; iOpcode < NUM_OPCODES; iOpcode++ ) { tHash = vHashes.at( iOpcode ); Hash_t iThisHash = tHash.m_nValue; int nOpcode = tHash.m_iOpcode; int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; wsprintf( sText, "%08X %02X %s %s" , iThisHash , nOpcode , g_aOpcodes65C02[ nOpcode ].sMnemonic , g_aOpmodes[ nOpmode ].m_sName ); ConsoleBufferPush( sText ); nThisHash++; // if (nPrevHash != iThisHash) // { // wsprintf( sText, "Total: %d", nThisHash ); // ConsoleBufferPush( sText ); // nThisHash = 0; // } } ConsoleUpdate(); //#endif } //=========================================================================== int AssemblerPokeAddress( const int Opcode, const int nOpmode, const WORD nBaseAddress, const WORD nTargetOffset ) { // int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; int nOpbytes = g_aOpmodes[ nOpmode ].m_nBytes; // if (nOpbytes != nBytes) // ConsoleDisplayError( TEXT(" ERROR: Input Opcode bytes differs from actual!" ) ); *(memdirty + (nBaseAddress >> 8)) |= 1; // *(mem + nBaseAddress) = (BYTE) nOpcode; if (nOpbytes > 1) *(mem + nBaseAddress + 1) = (BYTE)(nTargetOffset >> 0); if (nOpbytes > 2) *(mem + nBaseAddress + 2) = (BYTE)(nTargetOffset >> 8); return nOpbytes; } //=========================================================================== bool AssemblerPokeOpcodeAddress( const WORD nBaseAddress ) { int iAddressMode = m_iAsmAddressMode; // opmode detected from input int nTargetValue = m_nAsmTargetValue; int iOpcode; int nOpcodes = m_vAsmOpcodes.size(); for( iOpcode = 0; iOpcode < nOpcodes; iOpcode++ ) { int nOpcode = m_vAsmOpcodes.at( iOpcode ); // m_iOpcode; int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; if (nOpmode == iAddressMode) { *(mem + nBaseAddress) = (BYTE) nOpcode; int nOpbytes = AssemblerPokeAddress( nOpcode, nOpmode, nBaseAddress, nTargetValue ); if (m_bDelayedTargetsDirty) { int nDelayedTargets = m_vDelayedTargets.size(); DelayedTarget_t *pTarget = & m_vDelayedTargets.at( nDelayedTargets - 1 ); pTarget->m_nOpcode = nOpcode; pTarget->m_iOpmode = nOpmode; } g_nAssemblerAddress += nOpbytes; return true; } } return false; } //=========================================================================== bool TestFlag( AssemblerFlags_e eFlag ) { if (m_bAsmFlags & eFlag) return true; return false; } //=========================================================================== void SetFlag( AssemblerFlags_e eFlag, bool bValue = true ) { if (bValue) m_bAsmFlags |= eFlag; else m_bAsmFlags &= ~ eFlag; } /* Output AM_IMPLIED AM_M AM_A AM_Z AM_I // indexed or indirect */ //=========================================================================== bool AssemblerGetArgs( int iArg, int nArgs, WORD nBaseAddress ) { m_iAsmAddressMode = AM_IMPLIED; AssemblerState_e eNextState = AS_GET_MNEMONIC; m_bAsmFlags = 0; m_nAsmTargetAddress = 0; int nBase = 10; // Sync up to Raw Args for matching mnemonic // Process them instead of the cooked args, since we need the orginal tokens Arg_t *pArg = &g_aArgRaw[ iArg ]; while (iArg < g_nArgRaw) { int iToken = pArg->eToken; int iType = pArg->bType; if (iToken == TOKEN_HASH) { if (eNextState != AS_GET_MNEMONIC_PARM) { ConsoleBufferPush( TEXT( " Syntax Error: '#'" ) ); return false; } if (TestFlag( AF_HaveHash )) { ConsoleBufferPush( TEXT( " Syntax Error: Extra '#'" ) ); // No thanks, we already have one return false; } SetFlag( AF_HaveHash ); m_iAsmAddressMode = AM_M; // Immediate eNextState = AS_GET_TARGET; m_nAsmBytes = 1; } else if (iToken == TOKEN_DOLLAR) { if (TestFlag( AF_HaveDollar )) { ConsoleBufferPush( TEXT( " Syntax Error: Extra '$'" ) ); // No thanks, we already have one return false; } nBase = 16; // switch to hex if (! TestFlag( AF_HaveHash)) { SetFlag( AF_HaveDollar ); m_iAsmAddressMode = AM_A; // Absolute } eNextState = AS_GET_TARGET; m_nAsmBytes = 2; } else if (iToken == TOKEN_PAREN_L) { if (TestFlag( AF_HaveLeftParen )) { ConsoleBufferPush( TEXT( " Syntax Error: Extra '('" ) ); // No thanks, we already have one return false; } SetFlag( AF_HaveLeftParen ); // Indexed or Indirect m_iAsmAddressMode = AM_I; } else if (iToken == TOKEN_PAREN_R) { if (TestFlag( AF_HaveRightParen )) { ConsoleBufferPush( TEXT( " Syntax Error: Extra ')'" ) ); // No thanks, we already have one return false; } SetFlag( AF_HaveRightParen ); // Indexed or Indirect m_iAsmAddressMode = AM_I; } else if (iToken == TOKEN_COMMA) { if (TestFlag( AF_HaveComma )) { ConsoleBufferPush( TEXT( " Syntax Error: Extra ','" ) ); // No thanks, we already have one return false; } SetFlag( AF_HaveComma ); eNextState = AS_GET_INDEX; // We should have address by now } else if (iToken == TOKEN_LESS_THAN) { } else if (iToken == TOKEN_GREATER_THAN) { } else if (iToken == TOKEN_SEMI) // comment { break; } else if (iToken == TOKEN_ALPHANUMERIC) { if (eNextState == AS_GET_MNEMONIC) { eNextState = AS_GET_MNEMONIC_PARM; } else if (eNextState == AS_GET_MNEMONIC_PARM) { eNextState = AS_GET_TARGET; } if (eNextState == AS_GET_TARGET) { SetFlag( AF_HaveTarget ); ArgsGetValue( pArg, & m_nAsmTargetAddress, nBase ); // Do Symbol Lookup WORD nSymbolAddress; bool bExists = FindAddressFromSymbol( pArg->sArg, &nSymbolAddress ); if (bExists) { m_nAsmTargetAddress = nSymbolAddress; if (m_iAsmAddressMode == AM_IMPLIED) m_iAsmAddressMode = AM_A; } else { // if valid hex address, don't have delayed target TCHAR sAddress[ 32 ]; wsprintf( sAddress, "%X", m_nAsmTargetAddress); if (_tcscmp( sAddress, pArg->sArg)) { DelayedTarget_t tDelayedTarget; tDelayedTarget.m_nBaseAddress = nBaseAddress; strncpy( tDelayedTarget.m_sAddress, pArg->sArg, MAX_SYMBOLS_LEN ); tDelayedTarget.m_sAddress[ MAX_SYMBOLS_LEN ] = 0; // Flag this target that we need to update it when we have the relevent info m_bDelayedTargetsDirty = true; tDelayedTarget.m_nOpcode = 0; tDelayedTarget.m_iOpmode = m_iAsmAddressMode; m_vDelayedTargets.push_back( tDelayedTarget ); m_nAsmTargetAddress = 0; } } if ((m_iAsmAddressMode != AM_M) && (m_iAsmAddressMode != AM_IMPLIED) && (! m_bDelayedTargetsDirty)) { if (m_nAsmTargetAddress <= _6502_ZEROPAGE_END) { m_iAsmAddressMode = AM_Z; m_nAsmBytes = 1; } } } if (eNextState == AS_GET_INDEX) { if (pArg->nArgLen == 1) { if (pArg->sArg[0] == 'X') { if (! TestFlag( AF_HaveComma )) { ConsoleBufferPush( TEXT( " Syntax Error: Missing ','" ) ); return false; } SetFlag( AF_HaveRegisterX ); } if (pArg->sArg[0] == 'Y') { if (! (TestFlag( AF_HaveComma ))) { ConsoleBufferPush( TEXT( " Syntax Error: Missing ','" ) ); return false; } SetFlag( AF_HaveRegisterY ); } } } } iArg++; pArg++; } return true; } //=========================================================================== bool AssemblerUpdateAddressingMode() { SetFlag( AF_HaveEitherParen, TestFlag(AF_HaveLeftParen) || TestFlag(AF_HaveRightParen) ); SetFlag( AF_HaveBothParen, TestFlag(AF_HaveLeftParen) && TestFlag(AF_HaveRightParen) ); if ((TestFlag( AF_HaveLeftParen )) && (! TestFlag( AF_HaveRightParen ))) { ConsoleBufferPush( TEXT( " Syntax Error: Missing ')'" ) ); return false; } if ((! TestFlag( AF_HaveLeftParen )) && ( TestFlag( AF_HaveRightParen ))) { ConsoleBufferPush( TEXT( " Syntax Error: Missing '('" ) ); return false; } if (TestFlag( AF_HaveComma )) { if ((! TestFlag( AF_HaveRegisterX )) && (! TestFlag( AF_HaveRegisterY ))) { ConsoleBufferPush( TEXT( " Syntax Error: Index 'X' or 'Y'" ) ); return false; } } if (TestFlag( AF_HaveBothParen )) { if (TestFlag( AF_HaveComma )) { if (TestFlag( AF_HaveRegisterX )) { m_iAsmAddressMode = AM_AX; m_nAsmBytes = 2; if (m_nAsmTargetAddress <= _6502_ZEROPAGE_END) { m_iAsmAddressMode = AM_ZX; m_nAsmBytes = 1; } } if (TestFlag( AF_HaveRegisterY )) { m_iAsmAddressMode = AM_AY; m_nAsmBytes = 2; if (m_nAsmTargetAddress <= _6502_ZEROPAGE_END) { m_iAsmAddressMode = AM_ZY; m_nAsmBytes = 1; } } } } if ((m_iAsmAddressMode == AM_A) || (m_iAsmAddressMode == AM_Z)) { if (! TestFlag( AF_HaveEitherParen)) // if no paren { if (TestFlag( AF_HaveComma ) && TestFlag( AF_HaveRegisterX )) { if (m_iAsmAddressMode == AM_Z) m_iAsmAddressMode = AM_ZX; else m_iAsmAddressMode = AM_AX; } if (TestFlag( AF_HaveComma ) && TestFlag( AF_HaveRegisterY )) { if (m_iAsmAddressMode == AM_Z) m_iAsmAddressMode = AM_ZY; else m_iAsmAddressMode = AM_AY; } } } if (m_iAsmAddressMode == AM_I) { if (! TestFlag( AF_HaveEitherParen)) // if no paren { // Indirect Zero Page // Indirect Absolute } } m_nAsmTargetValue = m_nAsmTargetAddress; int nOpcode = m_vAsmOpcodes.at( 0 ); // branch opcodes don't vary (only 1 Addressing Mode) if (_6502_CalcRelativeOffset( nOpcode, m_nAsmBaseAddress, m_nAsmTargetAddress, & m_nAsmTargetValue )) { if (m_iAsmAddressMode == NUM_OPMODES) return false; m_iAsmAddressMode = AM_R; } return true; } //=========================================================================== int AssemblerDelayedTargetsSize() { int nSize = m_vDelayedTargets.size(); return nSize; } // The Assembler was terminated, with Symbol(s) declared, but not (yet) defined. // i.e. // A 300 // BNE $DONE // //=========================================================================== void AssemblerProcessDelayedSymols() { m_bDelayedTargetsDirty = false; // assembler set signal if new symbol was added bool bModified = false; while (! bModified) { bModified = false; std::vector::iterator iSymbol; for( iSymbol = m_vDelayedTargets.begin(); iSymbol != m_vDelayedTargets.end(); ++iSymbol ) { DelayedTarget_t *pTarget = & (*iSymbol); // m_vDelayedTargets.at( iSymbol ); WORD nTargetAddress; bool bExists = FindAddressFromSymbol( pTarget->m_sAddress, & nTargetAddress ); if (bExists) { // TODO: need to handle #symbol, symbol+n, symbol-n bModified = true; int nOpcode = pTarget->m_nOpcode; int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; // int nOpbytes = g_aOpmodes[ nOpmode ].m_nBytes; // 300: D0 7E BNE $380 // ^ ^ ^ // | | TargetAddress // | TargetValue // BaseAddress WORD nTargetValue = nTargetAddress; if (_6502_CalcRelativeOffset( nOpcode, pTarget->m_nBaseAddress, nTargetAddress, & nTargetValue )) { if (m_iAsmAddressMode == NUM_OPMODES) { nTargetValue = 0; bModified = false; } } if (bModified) { AssemblerPokeAddress( nOpcode, nOpmode, pTarget->m_nBaseAddress, nTargetValue ); *(memdirty + (pTarget->m_nBaseAddress >> 8)) |= 1; m_vDelayedTargets.erase( iSymbol ); // iterators are invalid after the point of deletion // need to restart enumeration break; } } } if (! bModified) break; } } bool Assemble( int iArg, int nArgs, WORD nAddress ) { bool bGotArgs; bool bGotMode; bool bGotByte; // Since, making 2-passes is not an option, // we need to buffer the target address fix-ups. AssemblerProcessDelayedSymols(); m_nAsmBaseAddress = nAddress; TCHAR *pMnemonic = g_aArgs[ iArg ].sArg; int nMnemonicHash = AssemblerHashMnemonic( pMnemonic ); #if DEBUG_ASSEMBLER static char sText[ CONSOLE_WIDTH * 2 ]; sprintf( sText, "%s%04X%s: %s%s%s -> %s%08X", CHC_ADDRESS, nAddress, CHC_DEFAULT, CHC_STRING, pMnemonic, CHC_DEFAULT, CHC_NUM_HEX, nMnemonicHash ); ConsolePrint( sText ); #endif m_vAsmOpcodes.clear(); // Candiate opcodes int iOpcode; // Ugh! Linear search. for( iOpcode = 0; iOpcode < NUM_OPCODES; iOpcode++ ) { if (nMnemonicHash == g_aOpcodesHash[ iOpcode ]) { m_vAsmOpcodes.push_back( iOpcode ); } } int nOpcodes = m_vAsmOpcodes.size(); if (! nOpcodes) { // Check for assembler directive ConsoleBufferPush( TEXT(" Syntax Error: Invalid mnemonic") ); return false; } else { bGotArgs = AssemblerGetArgs( iArg, nArgs, nAddress ); if (bGotArgs) { bGotMode = AssemblerUpdateAddressingMode(); if (bGotMode) { bGotByte = AssemblerPokeOpcodeAddress( nAddress ); } } } return true; } //=========================================================================== void AssemblerOn () { g_bAssemblerInput = true; g_sConsolePrompt[0] = g_aConsolePrompt[ PROMPT_ASSEMBLER ]; } //=========================================================================== void AssemblerOff () { g_bAssemblerInput = false; g_sConsolePrompt[0] = g_aConsolePrompt[ PROMPT_COMMAND ]; }