242 lines
11 KiB
C#
242 lines
11 KiB
C#
/*
|
|
* M6502DASM.cs
|
|
*
|
|
* Provides disassembly services.
|
|
*
|
|
* Copyright © 2003, 2004 Mike Murphy
|
|
*
|
|
*/
|
|
using System;
|
|
using System.Text;
|
|
|
|
namespace EMU7800.Core
|
|
{
|
|
public static class M6502DASM
|
|
{
|
|
// Instruction Mnemonics
|
|
enum m : uint
|
|
{
|
|
ADC = 1, AND, ASL,
|
|
BIT, BCC, BCS, BEQ, BMI, BNE, BPL, BRK, BVC, BVS,
|
|
CLC, CLD, CLI, CLV, CMP, CPX, CPY,
|
|
DEC, DEX, DEY,
|
|
EOR,
|
|
INC, INX, INY,
|
|
JMP, JSR,
|
|
LDA, LDX, LDY, LSR,
|
|
NOP,
|
|
ORA,
|
|
PLA, PLP, PHA, PHP,
|
|
ROL, ROR, RTI, RTS,
|
|
SEC, SEI, STA, SBC, SED, STX, STY,
|
|
TAX, TAY, TSX, TXA, TXS, TYA,
|
|
|
|
// Illegal/undefined opcodes
|
|
isb,
|
|
kil,
|
|
lax,
|
|
rla,
|
|
sax,
|
|
top
|
|
}
|
|
|
|
// Addressing Modes
|
|
enum a : uint
|
|
{
|
|
REL, // Relative: $aa (branch instructions only)
|
|
ZPG, // Zero Page: $aa
|
|
ZPX, // Zero Page Indexed X: $aa,X
|
|
ZPY, // Zero Page Indexed Y: $aa,Y
|
|
ABS, // Absolute: $aaaa
|
|
ABX, // Absolute Indexed X: $aaaa,X
|
|
ABY, // Absolute Indexed Y: $aaaa,Y
|
|
IDX, // Indexed Indirect: ($aa,X)
|
|
IDY, // Indirect Indexed: ($aa),Y
|
|
IND, // Indirect Absolute: ($aaaa) (JMP only)
|
|
IMM, // Immediate: #aa
|
|
IMP, // Implied
|
|
ACC // Accumulator
|
|
}
|
|
|
|
static readonly m[] MnemonicMatrix = {
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
/*0*/ m.BRK, m.ORA, m.kil, 0, 0, m.ORA, m.ASL, 0, m.PHP, m.ORA, m.ASL, 0, m.top, m.ORA, m.ASL, 0,/*0*/
|
|
/*1*/ m.BPL, m.ORA, m.kil, 0, 0, m.ORA, m.ASL, 0, m.CLC, m.ORA, 0, 0, m.top, m.ORA, m.ASL, 0,/*1*/
|
|
/*2*/ m.JSR, m.AND, m.kil, 0, m.BIT, m.AND, m.ROL, 0, m.PLP, m.AND, m.ROL, 0, m.BIT, m.AND, m.ROL, 0,/*2*/
|
|
/*3*/ m.BMI, m.AND, m.kil, 0, 0, m.AND, m.ROL, 0, m.SEC, m.AND, 0, 0, m.top, m.AND, m.ROL, m.rla,/*3*/
|
|
/*4*/ m.RTI, m.EOR, m.kil, 0, 0, m.EOR, m.LSR, 0, m.PHA, m.EOR, m.LSR, 0, m.JMP, m.EOR, m.LSR, 0,/*4*/
|
|
/*5*/ m.BVC, m.EOR, m.kil, 0, 0, m.EOR, m.LSR, 0, m.CLI, m.EOR, 0, 0, m.top, m.EOR, m.LSR, 0,/*5*/
|
|
/*6*/ m.RTS, m.ADC, m.kil, 0, 0, m.ADC, m.ROR, 0, m.PLA, m.ADC, m.ROR, 0, m.JMP, m.ADC, m.ROR, 0,/*6*/
|
|
/*7*/ m.BVS, m.ADC, m.kil, 0, 0, m.ADC, m.ROR, 0, m.SEI, m.ADC, 0, 0, m.top, m.ADC, m.ROR, 0,/*7*/
|
|
/*8*/ 0, m.STA, 0, m.sax, m.STY, m.STA, m.STX, m.sax, m.DEY, 0, m.TXA, 0, m.STY, m.STA, m.STX, m.sax,/*8*/
|
|
/*9*/ m.BCC, m.STA, m.kil, 0, m.STY, m.STA, m.STX, m.sax, m.TYA, m.STA, m.TXS, 0, m.top, m.STA, 0, 0,/*9*/
|
|
/*A*/ m.LDY, m.LDA, m.LDX, m.lax, m.LDY, m.LDA, m.LDX, m.lax, m.TAY, m.LDA, m.TAX, 0, m.LDY, m.LDA, m.LDX, m.lax,/*A*/
|
|
/*B*/ m.BCS, m.LDA, m.kil, m.lax, m.LDY, m.LDA, m.LDX, m.lax, m.CLV, m.LDA, m.TSX, 0, m.LDY, m.LDA, m.LDX, m.lax,/*B*/
|
|
/*C*/ m.CPY, m.CMP, 0, 0, m.CPY, m.CMP, m.DEC, 0, m.INY, m.CMP, m.DEX, 0, m.CPY, m.CMP, m.DEC, 0,/*C*/
|
|
/*D*/ m.BNE, m.CMP, m.kil, 0, 0, m.CMP, m.DEC, 0, m.CLD, m.CMP, 0, 0, m.top, m.CMP, m.DEC, 0,/*D*/
|
|
/*E*/ m.CPX, m.SBC, 0, 0, m.CPX, m.SBC, m.INC, 0, m.INX, m.SBC, m.NOP, 0, m.CPX, m.SBC, m.INC, m.isb,/*E*/
|
|
/*F*/ m.BEQ, m.SBC, m.kil, 0, 0, m.SBC, m.INC, 0, m.SED, m.SBC, 0, 0, m.top, m.SBC, m.INC, m.isb /*F*/
|
|
};
|
|
|
|
static readonly a[] AddressingModeMatrix = {
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
/*0*/ a.IMP, a.IDX, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.ABS, a.ABS, a.ABS, 0,/*0*/
|
|
/*1*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*1*/
|
|
/*2*/ a.ABS, a.IDX, a.IMP, 0, a.ZPG, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.ABS, a.ABS, a.ABS, 0,/*2*/
|
|
/*3*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, a.ABX,/*3*/
|
|
/*4*/ a.IMP, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.ABS, a.ABS, a.ABS, 0,/*4*/
|
|
/*5*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*5*/
|
|
/*6*/ a.IMP, a.IDX, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.IND, a.ABS, a.ABS, 0,/*6*/
|
|
/*7*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPX, a.ZPX, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*7*/
|
|
/*8*/ 0, a.IDY, 0, a.IDX, a.ZPG, a.ZPG, a.ZPG, a.ZPG, a.IMP, 0, a.IMP, 0, a.ABS, a.ABS, a.ABS, a.ABS,/*8*/
|
|
/*9*/ a.REL, a.IDY, a.IMP, 0, a.ZPX, a.ZPX, a.ZPY, a.ZPY, a.IMP, a.ABY, a.IMP, 0, a.ABS, a.ABX, 0, 0,/*9*/
|
|
/*A*/ a.IMM, a.IND, a.IMM, a.IDX, a.ZPG, a.ZPG, a.ZPG, a.ZPX, a.IMP, a.IMM, a.IMP, 0, a.ABS, a.ABS, a.ABS, a.ABS,/*A*/
|
|
/*B*/ a.REL, a.IDY, a.IMP, a.IDY, a.ZPX, a.ZPX, a.ZPY, a.ZPY, a.IMP, a.ABY, a.IMP, 0, a.ABX, a.ABX, a.ABY, a.ABY,/*B*/
|
|
/*C*/ a.IMM, a.IDX, 0, 0, a.ZPG, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.IMP, 0, a.ABS, a.ABS, a.ABS, 0,/*C*/
|
|
/*D*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPX, a.ZPX, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*D*/
|
|
/*E*/ a.IMM, a.IDX, 0, 0, a.ZPG, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.IMP, 0, a.ABS, a.ABS, a.ABS, a.ABS,/*E*/
|
|
/*F*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPX, a.ZPX, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, a.ABX /*F*/
|
|
};
|
|
|
|
public static string GetRegisters(M6502 cpu)
|
|
{
|
|
var dSB = new StringBuilder();
|
|
dSB.Append(String.Format(
|
|
"PC:{0:x4} A:{1:x2} X:{2:x2} Y:{3:x2} S:{4:x2} P:",
|
|
cpu.PC, cpu.A, cpu.X, cpu.Y, cpu.S));
|
|
|
|
const string flags = "nv0bdizcNV1BDIZC";
|
|
|
|
for (var i = 0; i < 8; i++)
|
|
{
|
|
dSB.Append(((cpu.P & (1 << (7 - i))) == 0) ? flags[i] : flags[i + 8]);
|
|
}
|
|
return dSB.ToString();
|
|
}
|
|
|
|
public static string Disassemble(AddressSpace addrSpace, ushort atAddr, ushort untilAddr)
|
|
{
|
|
var dSB = new StringBuilder();
|
|
var dPC = atAddr;
|
|
while (atAddr < untilAddr)
|
|
{
|
|
dSB.AppendFormat("{0:x4}: ", dPC);
|
|
var len = GetInstructionLength(addrSpace, dPC);
|
|
for (var i = 0; i < 3; i++)
|
|
{
|
|
if (i < len)
|
|
{
|
|
dSB.AppendFormat("{0:x2} ", addrSpace[atAddr++]);
|
|
}
|
|
else
|
|
{
|
|
dSB.Append(" ");
|
|
}
|
|
}
|
|
dSB.AppendFormat("{0,-15}{1}", RenderOpCode(addrSpace, dPC), Environment.NewLine);
|
|
dPC += (ushort)len;
|
|
}
|
|
if (dSB.Length > 0)
|
|
{
|
|
dSB.Length--; // Trim trailing newline
|
|
}
|
|
return dSB.ToString();
|
|
}
|
|
|
|
public static string MemDump(AddressSpace addrSpace, ushort atAddr, ushort untilAddr)
|
|
{
|
|
var dSB = new StringBuilder();
|
|
var len = untilAddr - atAddr;
|
|
while (len-- >= 0)
|
|
{
|
|
dSB.AppendFormat("{0:x4}: ", atAddr);
|
|
for (var i = 0; i < 8; i++)
|
|
{
|
|
dSB.AppendFormat("{0:x2} ", addrSpace[atAddr++]);
|
|
if (i == 3)
|
|
{
|
|
dSB.Append(" ");
|
|
}
|
|
}
|
|
dSB.Append("\n");
|
|
}
|
|
if (dSB.Length > 0)
|
|
{
|
|
dSB.Length--; // Trim trailing newline
|
|
}
|
|
return dSB.ToString();
|
|
}
|
|
|
|
public static string RenderOpCode(AddressSpace addrSpace, ushort PC)
|
|
{
|
|
var num_operands = GetInstructionLength(addrSpace, PC) - 1;
|
|
var PC1 = (ushort)(PC + 1);
|
|
string addrmodeStr;
|
|
|
|
switch (AddressingModeMatrix[addrSpace[PC]])
|
|
{
|
|
case a.REL:
|
|
addrmodeStr = String.Format("${0:x4}", (ushort)(PC + (sbyte)(addrSpace[PC1]) + 2));
|
|
break;
|
|
case a.ZPG:
|
|
case a.ABS:
|
|
addrmodeStr = RenderEA(addrSpace, PC1, num_operands);
|
|
break;
|
|
case a.ZPX:
|
|
case a.ABX:
|
|
addrmodeStr = RenderEA(addrSpace, PC1, num_operands) + ",X";
|
|
break;
|
|
case a.ZPY:
|
|
case a.ABY:
|
|
addrmodeStr = RenderEA(addrSpace, PC1, num_operands) + ",Y";
|
|
break;
|
|
case a.IDX:
|
|
addrmodeStr = "(" + RenderEA(addrSpace, PC1, num_operands) + ",X)";
|
|
break;
|
|
case a.IDY:
|
|
addrmodeStr = "(" + RenderEA(addrSpace, PC1, num_operands) + "),Y";
|
|
break;
|
|
case a.IND:
|
|
addrmodeStr = "(" + RenderEA(addrSpace, PC1, num_operands) + ")";
|
|
break;
|
|
case a.IMM:
|
|
addrmodeStr = "#" + RenderEA(addrSpace, PC1, num_operands);
|
|
break;
|
|
default:
|
|
// a.IMP, a.ACC
|
|
addrmodeStr = string.Empty;
|
|
break;
|
|
}
|
|
|
|
return string.Format("{0} {1}", MnemonicMatrix[addrSpace[PC]], addrmodeStr);
|
|
}
|
|
|
|
static int GetInstructionLength(AddressSpace addrSpace, ushort PC)
|
|
{
|
|
switch (AddressingModeMatrix[addrSpace[PC]])
|
|
{
|
|
case a.ACC:
|
|
case a.IMP:
|
|
return 1;
|
|
case a.REL:
|
|
case a.ZPG:
|
|
case a.ZPX:
|
|
case a.ZPY:
|
|
case a.IDX:
|
|
case a.IDY:
|
|
case a.IMM:
|
|
return 2;
|
|
default:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
static string RenderEA(AddressSpace addrSpace, ushort PC, int bytes)
|
|
{
|
|
var lsb = addrSpace[PC];
|
|
var msb = (bytes == 2) ? addrSpace[(ushort)(PC + 1)] : (byte)0;
|
|
var ea = (ushort)(lsb | (msb << 8));
|
|
return string.Format((bytes == 1) ? "${0:x2}" : "${0:x4}", ea);
|
|
}
|
|
}
|
|
} |