AppleWin/source/Debugger/Debugger_Assembler.cpp

1496 lines
47 KiB
C++

/*
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<DisasmData_t> 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<int> 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 <DelayedTarget_t> 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<HashOpcode_t> 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
// <enter>
//===========================================================================
void AssemblerProcessDelayedSymols()
{
m_bDelayedTargetsDirty = false; // assembler set signal if new symbol was added
bool bModified = false;
while (! bModified)
{
bModified = false;
std::vector<DelayedTarget_t>::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, 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 ];
}