/* * 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); } } }