mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2025-01-21 09:29:48 +00:00
Namespace tidying
This commit is contained in:
parent
ed4b4e3736
commit
dc677e5358
1112
M6502/Core.cs
Normal file
1112
M6502/Core.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
public class CycleCountedEventArgs(long cycles, long count) : EventArgs
|
||||
{
|
||||
|
@ -2,16 +2,17 @@
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using EightBit;
|
||||
|
||||
public class Disassembler(Bus bus, M6502Core processor, Files.Symbols.Parser symbols)
|
||||
public class Disassembler(Bus bus, Core processor, Symbols.Parser symbols)
|
||||
{
|
||||
private readonly Bus bus = bus;
|
||||
private readonly M6502Core processor = processor;
|
||||
private readonly Files.Symbols.Parser symbols = symbols;
|
||||
private readonly Core processor = processor;
|
||||
private readonly Symbols.Parser symbols = symbols;
|
||||
private ushort address;
|
||||
|
||||
public static string DumpFlags(byte value)
|
||||
@ -34,16 +35,16 @@ namespace EightBit
|
||||
|
||||
public string Disassemble(ushort current)
|
||||
{
|
||||
this.address = current;
|
||||
address = current;
|
||||
|
||||
var output = new StringBuilder();
|
||||
|
||||
var cell = this.bus.Peek(current);
|
||||
var cell = bus.Peek(current);
|
||||
|
||||
output.Append(DumpByteValue(cell));
|
||||
output.Append(' ');
|
||||
|
||||
var next = this.bus.Peek((ushort)(current + 1));
|
||||
var next = bus.Peek((ushort)(current + 1));
|
||||
var relative = (ushort)(current + 2 + (sbyte)next);
|
||||
|
||||
var aaa = (cell & 0b11100000) >> 5;
|
||||
@ -59,31 +60,31 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b000: // BRK
|
||||
output.Append(this.Disassemble_Implied("BRK"));
|
||||
output.Append(Disassemble_Implied("BRK"));
|
||||
break;
|
||||
case 0b001: // DOP/NOP (0x04)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b010: // PHP
|
||||
output.Append(this.Disassemble_Implied("PHP"));
|
||||
output.Append(Disassemble_Implied("PHP"));
|
||||
break;
|
||||
case 0b011: // TOP/NOP (0b00001100, 0x0c)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b100: // BPL
|
||||
output.Append(this.Disassemble_Relative("BPL", relative));
|
||||
output.Append(Disassemble_Relative("BPL", relative));
|
||||
break;
|
||||
case 0b101: // DOP/NOP (0x14)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b110: // CLC
|
||||
output.Append(this.Disassemble_Implied("CLC"));
|
||||
output.Append(Disassemble_Implied("CLC"));
|
||||
break;
|
||||
case 0b111: // TOP/NOP (0b00011100, 0x1c)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Illegal instruction");
|
||||
throw new InvalidOperationException("Illegal instruction");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -91,25 +92,25 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b000: // JSR
|
||||
output.Append(this.Disassemble_Absolute("JSR"));
|
||||
output.Append(Disassemble_Absolute("JSR"));
|
||||
break;
|
||||
case 0b010: // PLP
|
||||
output.Append(this.Disassemble_Implied("PLP"));
|
||||
output.Append(Disassemble_Implied("PLP"));
|
||||
break;
|
||||
case 0b100: // BMI
|
||||
output.Append(this.Disassemble_Relative("BMI", relative));
|
||||
output.Append(Disassemble_Relative("BMI", relative));
|
||||
break;
|
||||
case 0b101: // DOP/NOP (0x34)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b110: // SEC
|
||||
output.Append(this.Disassemble_Implied("SEC"));
|
||||
output.Append(Disassemble_Implied("SEC"));
|
||||
break;
|
||||
case 0b111: // TOP/NOP (0b00111100, 0x3c)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
default: // BIT
|
||||
output.Append(this.Disassemble_AM_00(bbb, "BIT"));
|
||||
output.Append(Disassemble_AM_00(bbb, "BIT"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -118,31 +119,31 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b000: // RTI
|
||||
output.Append(this.Disassemble_Implied("RTI"));
|
||||
output.Append(Disassemble_Implied("RTI"));
|
||||
break;
|
||||
case 0b001: // DOP/NOP (0x44)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b010: // PHA
|
||||
output.Append(this.Disassemble_Implied("PHA"));
|
||||
output.Append(Disassemble_Implied("PHA"));
|
||||
break;
|
||||
case 0b011: // JMP
|
||||
output.Append(this.Disassemble_Absolute("JMP"));
|
||||
output.Append(Disassemble_Absolute("JMP"));
|
||||
break;
|
||||
case 0b100: // BVC
|
||||
output.Append(this.Disassemble_Relative("BVC", relative));
|
||||
output.Append(Disassemble_Relative("BVC", relative));
|
||||
break;
|
||||
case 0b101: // DOP/NOP (0x54)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b110: // CLI
|
||||
output.Append(this.Disassemble_Implied("CLI"));
|
||||
output.Append(Disassemble_Implied("CLI"));
|
||||
break;
|
||||
case 0b111: // TOP/NOP (0b01011100, 0x5c)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Illegal addressing mode");
|
||||
throw new InvalidOperationException("Illegal addressing mode");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -150,31 +151,31 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b000: // RTS
|
||||
output.Append(this.Disassemble_Implied("RTS"));
|
||||
output.Append(Disassemble_Implied("RTS"));
|
||||
break;
|
||||
case 0b001: // DOP/NOP (0x64)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b010: // PLA
|
||||
output.Append(this.Disassemble_Implied("PLA"));
|
||||
output.Append(Disassemble_Implied("PLA"));
|
||||
break;
|
||||
case 0b011: // JMP (abs)
|
||||
output.Append(this.Disassemble_Indirect("JMP"));
|
||||
output.Append(Disassemble_Indirect("JMP"));
|
||||
break;
|
||||
case 0b100: // BVS
|
||||
output.Append(this.Disassemble_Relative("BVS", relative));
|
||||
output.Append(Disassemble_Relative("BVS", relative));
|
||||
break;
|
||||
case 0b101: // DOP/NOP (0x74)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b110: // SEI
|
||||
output.Append(this.Disassemble_Implied("SEI"));
|
||||
output.Append(Disassemble_Implied("SEI"));
|
||||
break;
|
||||
case 0b111: // TOP/NOP (0b01111100, 0x7c)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Illegal addressing mode");
|
||||
throw new InvalidOperationException("Illegal addressing mode");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -182,19 +183,19 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b000: // DOP/NOP (0x80)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b010: // DEY
|
||||
output.Append(this.Disassemble_Implied("DEY"));
|
||||
output.Append(Disassemble_Implied("DEY"));
|
||||
break;
|
||||
case 0b100: // BCC
|
||||
output.Append(this.Disassemble_Relative("BCC", relative));
|
||||
output.Append(Disassemble_Relative("BCC", relative));
|
||||
break;
|
||||
case 0b110: // TYA
|
||||
output.Append(this.Disassemble_Implied("TYA"));
|
||||
output.Append(Disassemble_Implied("TYA"));
|
||||
break;
|
||||
default: // STY
|
||||
output.Append(this.Disassemble_AM_00(bbb, "STY"));
|
||||
output.Append(Disassemble_AM_00(bbb, "STY"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -203,16 +204,16 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // TAY
|
||||
output.Append(this.Disassemble_Implied("TAY"));
|
||||
output.Append(Disassemble_Implied("TAY"));
|
||||
break;
|
||||
case 0b100: // BCS
|
||||
output.Append(this.Disassemble_Relative("BCS", relative));
|
||||
output.Append(Disassemble_Relative("BCS", relative));
|
||||
break;
|
||||
case 0b110: // CLV
|
||||
output.Append(this.Disassemble_Implied("CLV"));
|
||||
output.Append(Disassemble_Implied("CLV"));
|
||||
break;
|
||||
default: // LDY
|
||||
output.Append(this.Disassemble_AM_00(bbb, "LDY"));
|
||||
output.Append(Disassemble_AM_00(bbb, "LDY"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -221,22 +222,22 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // INY
|
||||
output.Append(this.Disassemble_Implied("INY"));
|
||||
output.Append(Disassemble_Implied("INY"));
|
||||
break;
|
||||
case 0b100: // BNE
|
||||
output.Append(this.Disassemble_Relative("BNE", relative));
|
||||
output.Append(Disassemble_Relative("BNE", relative));
|
||||
break;
|
||||
case 0b101: // DOP/NOP (0xd4)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b110: // CLD
|
||||
output.Append(this.Disassemble_Implied("CLD"));
|
||||
output.Append(Disassemble_Implied("CLD"));
|
||||
break;
|
||||
case 0b111: // TOP/NOP (0b11011100, 0xdc)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
default: // CPY
|
||||
output.Append(this.Disassemble_AM_00(bbb, "CPY"));
|
||||
output.Append(Disassemble_AM_00(bbb, "CPY"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -245,22 +246,22 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // INX
|
||||
output.Append(this.Disassemble_Implied("INX"));
|
||||
output.Append(Disassemble_Implied("INX"));
|
||||
break;
|
||||
case 0b100: // BEQ
|
||||
output.Append(this.Disassemble_Relative("BEQ", relative));
|
||||
output.Append(Disassemble_Relative("BEQ", relative));
|
||||
break;
|
||||
case 0b101: // DOP/NOP (0xf4)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
case 0b110: // SED
|
||||
output.Append(this.Disassemble_Implied("SED"));
|
||||
output.Append(Disassemble_Implied("SED"));
|
||||
break;
|
||||
case 0b111: // TOP/NOP (0b11111100, 0xfc)
|
||||
output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
|
||||
output.Append(Disassemble_AM_00(bbb, "*NOP"));
|
||||
break;
|
||||
default: // CPX
|
||||
output.Append(this.Disassemble_AM_00(bbb, "CPX"));
|
||||
output.Append(Disassemble_AM_00(bbb, "CPX"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -272,31 +273,31 @@ namespace EightBit
|
||||
switch (aaa)
|
||||
{
|
||||
case 0b000: // ORA
|
||||
output.Append(this.Disassemble_AM_01(bbb, "ORA"));
|
||||
output.Append(Disassemble_AM_01(bbb, "ORA"));
|
||||
break;
|
||||
case 0b001: // AND
|
||||
output.Append(this.Disassemble_AM_01(bbb, "AND"));
|
||||
output.Append(Disassemble_AM_01(bbb, "AND"));
|
||||
break;
|
||||
case 0b010: // EOR
|
||||
output.Append(this.Disassemble_AM_01(bbb, "EOR"));
|
||||
output.Append(Disassemble_AM_01(bbb, "EOR"));
|
||||
break;
|
||||
case 0b011: // ADC
|
||||
output.Append(this.Disassemble_AM_01(bbb, "ADC"));
|
||||
output.Append(Disassemble_AM_01(bbb, "ADC"));
|
||||
break;
|
||||
case 0b100: // STA
|
||||
output.Append(this.Disassemble_AM_01(bbb, "STA"));
|
||||
output.Append(Disassemble_AM_01(bbb, "STA"));
|
||||
break;
|
||||
case 0b101: // LDA
|
||||
output.Append(this.Disassemble_AM_01(bbb, "LDA"));
|
||||
output.Append(Disassemble_AM_01(bbb, "LDA"));
|
||||
break;
|
||||
case 0b110: // CMP
|
||||
output.Append(this.Disassemble_AM_01(bbb, "CMP"));
|
||||
output.Append(Disassemble_AM_01(bbb, "CMP"));
|
||||
break;
|
||||
case 0b111: // SBC
|
||||
output.Append(this.Disassemble_AM_01(bbb, "SBC"));
|
||||
output.Append(Disassemble_AM_01(bbb, "SBC"));
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Illegal addressing mode");
|
||||
throw new InvalidOperationException("Illegal addressing mode");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -307,10 +308,10 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b110: // 0x1a
|
||||
output.Append(this.Disassemble_Implied("*NOP"));
|
||||
output.Append(Disassemble_Implied("*NOP"));
|
||||
break;
|
||||
default:
|
||||
output.Append(this.Disassemble_AM_10(bbb, "ASL"));
|
||||
output.Append(Disassemble_AM_10(bbb, "ASL"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -319,10 +320,10 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b110: // 0x3a
|
||||
output.Append(this.Disassemble_Implied("*NOP"));
|
||||
output.Append(Disassemble_Implied("*NOP"));
|
||||
break;
|
||||
default:
|
||||
output.Append(this.Disassemble_AM_10(bbb, "ROL"));
|
||||
output.Append(Disassemble_AM_10(bbb, "ROL"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -331,10 +332,10 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b110: // 0x5a
|
||||
output.Append(this.Disassemble_Implied("*NOP"));
|
||||
output.Append(Disassemble_Implied("*NOP"));
|
||||
break;
|
||||
default:
|
||||
output.Append(this.Disassemble_AM_10(bbb, "LSR"));
|
||||
output.Append(Disassemble_AM_10(bbb, "LSR"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -343,10 +344,10 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b110: // 0x7a
|
||||
output.Append(this.Disassemble_Implied("*NOP"));
|
||||
output.Append(Disassemble_Implied("*NOP"));
|
||||
break;
|
||||
default:
|
||||
output.Append(this.Disassemble_AM_10(bbb, "ROR"));
|
||||
output.Append(Disassemble_AM_10(bbb, "ROR"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -355,13 +356,13 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // TXA
|
||||
output.Append(this.Disassemble_Implied("TXA"));
|
||||
output.Append(Disassemble_Implied("TXA"));
|
||||
break;
|
||||
case 0b110: // TXS
|
||||
output.Append(this.Disassemble_Implied("TXS"));
|
||||
output.Append(Disassemble_Implied("TXS"));
|
||||
break;
|
||||
default: // STX
|
||||
output.Append(this.Disassemble_AM_10_x(bbb, "STX"));
|
||||
output.Append(Disassemble_AM_10_x(bbb, "STX"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -370,13 +371,13 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // TAX
|
||||
output.Append(this.Disassemble_Implied("TAX"));
|
||||
output.Append(Disassemble_Implied("TAX"));
|
||||
break;
|
||||
case 0b110: // TSX
|
||||
output.Append(this.Disassemble_Implied("TSX"));
|
||||
output.Append(Disassemble_Implied("TSX"));
|
||||
break;
|
||||
default: // LDX
|
||||
output.Append(this.Disassemble_AM_10_x(bbb, "LDX"));
|
||||
output.Append(Disassemble_AM_10_x(bbb, "LDX"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -385,13 +386,13 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // DEX
|
||||
output.Append(this.Disassemble_Implied("DEX"));
|
||||
output.Append(Disassemble_Implied("DEX"));
|
||||
break;
|
||||
case 0b110: // 0xda
|
||||
output.Append(this.Disassemble_Implied("*NOP"));
|
||||
output.Append(Disassemble_Implied("*NOP"));
|
||||
break;
|
||||
default: // DEC
|
||||
output.Append(this.Disassemble_AM_10(bbb, "DEC"));
|
||||
output.Append(Disassemble_AM_10(bbb, "DEC"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -400,19 +401,19 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010: // NOP
|
||||
output.Append(this.Disassemble_Implied("NOP"));
|
||||
output.Append(Disassemble_Implied("NOP"));
|
||||
break;
|
||||
case 0b110: // 0xfa
|
||||
output.Append(this.Disassemble_Implied("*NOP"));
|
||||
output.Append(Disassemble_Implied("*NOP"));
|
||||
break;
|
||||
default: // INC
|
||||
output.Append(this.Disassemble_AM_10(bbb, "INC"));
|
||||
output.Append(Disassemble_AM_10(bbb, "INC"));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Illegal instruction");
|
||||
throw new InvalidOperationException("Illegal instruction");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -423,10 +424,10 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010:
|
||||
output.Append(this.Disassemble_Immediate("*AAC"));
|
||||
output.Append(Disassemble_Immediate("*AAC"));
|
||||
break;
|
||||
default:
|
||||
output.Append(this.Disassemble_AM_01(bbb, "*SLO"));
|
||||
output.Append(Disassemble_AM_01(bbb, "*SLO"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -436,28 +437,28 @@ namespace EightBit
|
||||
switch (bbb)
|
||||
{
|
||||
case 0b010:
|
||||
output.Append(this.Disassemble_Immediate("*AAC"));
|
||||
output.Append(Disassemble_Immediate("*AAC"));
|
||||
break;
|
||||
default:
|
||||
output.Append(this.Disassemble_AM_01(bbb, "*RLA"));
|
||||
output.Append(Disassemble_AM_01(bbb, "*RLA"));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 0b010:
|
||||
output.Append(this.Disassemble_AM_01(bbb, "*SRE"));
|
||||
output.Append(Disassemble_AM_01(bbb, "*SRE"));
|
||||
break;
|
||||
case 0b011:
|
||||
output.Append(this.Disassemble_AM_01(bbb, "*RRA"));
|
||||
output.Append(Disassemble_AM_01(bbb, "*RRA"));
|
||||
break;
|
||||
case 0b100:
|
||||
output.Append(this.Disassemble_AM_11(bbb, "*SAX"));
|
||||
output.Append(Disassemble_AM_11(bbb, "*SAX"));
|
||||
break;
|
||||
case 0b101:
|
||||
output.Append(this.Disassemble_AM_11(bbb, "*LAX"));
|
||||
output.Append(Disassemble_AM_11(bbb, "*LAX"));
|
||||
break;
|
||||
case 0b110:
|
||||
output.Append(this.Disassemble_AM_11_x(bbb, "*DCP"));
|
||||
output.Append(Disassemble_AM_11_x(bbb, "*DCP"));
|
||||
break;
|
||||
case 0b111:
|
||||
switch (bbb)
|
||||
@ -469,23 +470,23 @@ namespace EightBit
|
||||
case 0b101:
|
||||
case 0b110:
|
||||
case 0b111:
|
||||
output.Append(this.Disassemble_AM_01(bbb, "*ISB"));
|
||||
output.Append(Disassemble_AM_01(bbb, "*ISB"));
|
||||
break;
|
||||
case 0b010:
|
||||
output.Append(this.Disassemble_AM_11(bbb, "*SBC"));
|
||||
output.Append(Disassemble_AM_11(bbb, "*SBC"));
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Impossible addressing mode");
|
||||
throw new InvalidOperationException("Impossible addressing mode");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Illegal instruction group");
|
||||
throw new InvalidOperationException("Illegal instruction group");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new System.InvalidOperationException("Impossible instruction");
|
||||
throw new InvalidOperationException("Impossible instruction");
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
@ -495,27 +496,27 @@ namespace EightBit
|
||||
|
||||
#region Label conversions
|
||||
|
||||
private string ConvertAddressAt(ushort absolute) => this.ConvertAddress(this.GetWord(absolute));
|
||||
private string ConvertAddressAt(ushort absolute) => ConvertAddress(GetWord(absolute));
|
||||
|
||||
private string ConvertAddress(ushort absolute) => this.TryGetLabel(absolute, out var label) ? label : "$" + DumpWordValue(absolute);
|
||||
private string ConvertAddress(ushort absolute) => TryGetLabel(absolute, out var label) ? label : "$" + DumpWordValue(absolute);
|
||||
|
||||
private string ConvertZPAddressAt(ushort absolute) => this.ConvertZPAddress(this.GetByte(absolute));
|
||||
private string ConvertZPAddressAt(ushort absolute) => ConvertZPAddress(GetByte(absolute));
|
||||
|
||||
private string ConvertZPAddress(byte absolute) => this.TryGetLabel(absolute, out var label) ? label : "$" + DumpByteValue(absolute);
|
||||
private string ConvertZPAddress(byte absolute) => TryGetLabel(absolute, out var label) ? label : "$" + DumpByteValue(absolute);
|
||||
|
||||
private bool TryGetLabel(ushort absolute, out string name)
|
||||
{
|
||||
return this.symbols.TryGetQualifiedLabelByAddress(absolute, out name);
|
||||
return symbols.TryGetQualifiedLabelByAddress(absolute, out name);
|
||||
}
|
||||
|
||||
private string MaybeGetLabel(ushort absolute)
|
||||
{
|
||||
return this.symbols.MaybeGetQualifiedLabelByAddress(absolute);
|
||||
return symbols.MaybeGetQualifiedLabelByAddress(absolute);
|
||||
}
|
||||
|
||||
private string MaybeGetCodeLabel(ushort absolute)
|
||||
{
|
||||
var label = this.MaybeGetLabel(absolute);
|
||||
var label = MaybeGetLabel(absolute);
|
||||
if (string.IsNullOrEmpty(label))
|
||||
{
|
||||
return string.Empty;
|
||||
@ -525,7 +526,7 @@ namespace EightBit
|
||||
|
||||
private string MaybeGetCodeLabel()
|
||||
{
|
||||
return Pad(this.MaybeGetCodeLabel(this.address), 30);
|
||||
return Pad(MaybeGetCodeLabel(address), 30);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -534,46 +535,46 @@ namespace EightBit
|
||||
|
||||
private bool TryGetConstant(ushort value, out string name)
|
||||
{
|
||||
return this.symbols.TryGetQualifiedEquateValue(value, out name);
|
||||
return symbols.TryGetQualifiedEquateValue(value, out name);
|
||||
}
|
||||
|
||||
private string ConvertConstantByte(ushort address) => this.ConvertConstant(this.GetByte(address));
|
||||
private string ConvertConstantByte(ushort address) => ConvertConstant(GetByte(address));
|
||||
|
||||
private string ConvertConstant(byte constant) => this.TryGetConstant(constant, out var label) ? label : "$" + DumpByteValue(constant);
|
||||
private string ConvertConstant(byte constant) => TryGetConstant(constant, out var label) ? label : "$" + DumpByteValue(constant);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private byte GetByte(ushort absolute) => this.bus.Peek(absolute);
|
||||
private byte GetByte(ushort absolute) => bus.Peek(absolute);
|
||||
|
||||
private ushort GetWord(ushort absolute) => this.processor.PeekWord(absolute).Word;
|
||||
private ushort GetWord(ushort absolute) => processor.PeekWord(absolute).Word;
|
||||
|
||||
private string Dump_Byte(ushort absolute) => DumpByteValue(this.GetByte(absolute));
|
||||
private string Dump_Byte(ushort absolute) => DumpByteValue(GetByte(absolute));
|
||||
|
||||
private string Dump_DByte(ushort absolute) => this.Dump_Byte(absolute) + " " + this.Dump_Byte(++absolute);
|
||||
private string Dump_DByte(ushort absolute) => Dump_Byte(absolute) + " " + Dump_Byte(++absolute);
|
||||
|
||||
private string Disassemble_Implied(string instruction) => $"{Pad()}\t{this.MaybeGetCodeLabel()}" + instruction;
|
||||
private string Disassemble_Implied(string instruction) => $"{Pad()}\t{MaybeGetCodeLabel()}" + instruction;
|
||||
|
||||
private string Disassemble_Absolute(string instruction) => this.AM_Absolute_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_Absolute();
|
||||
private string Disassemble_Absolute(string instruction) => AM_Absolute_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_Absolute();
|
||||
|
||||
private string Disassemble_Indirect(string instruction) => this.AM_Absolute_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " (" + this.AM_Absolute() + ")";
|
||||
private string Disassemble_Indirect(string instruction) => AM_Absolute_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " (" + AM_Absolute() + ")";
|
||||
|
||||
private string Disassemble_Relative(string instruction, ushort absolute) => this.AM_Immediate_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.ConvertAddress(absolute);
|
||||
private string Disassemble_Relative(string instruction, ushort absolute) => AM_Immediate_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " " + ConvertAddress(absolute);
|
||||
|
||||
private string Disassemble_Immediate(string instruction) => this.AM_Immediate_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_Immediate();
|
||||
private string Disassemble_Immediate(string instruction) => AM_Immediate_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_Immediate();
|
||||
|
||||
private string Disassemble_AM_00(int bbb, string instruction) => this.AM_00_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_00(bbb);
|
||||
private string Disassemble_AM_00(int bbb, string instruction) => AM_00_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_00(bbb);
|
||||
|
||||
private string Disassemble_AM_01(int bbb, string instruction) => this.AM_01_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_01(bbb);
|
||||
private string Disassemble_AM_01(int bbb, string instruction) => AM_01_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_01(bbb);
|
||||
|
||||
private string Disassemble_AM_10(int bbb, string instruction) => this.AM_10_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_10(bbb);
|
||||
private string Disassemble_AM_10(int bbb, string instruction) => AM_10_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_10(bbb);
|
||||
|
||||
private string Disassemble_AM_10_x(int bbb, string instruction) => this.AM_10_x_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_10_x(bbb);
|
||||
private string Disassemble_AM_10_x(int bbb, string instruction) => AM_10_x_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_10_x(bbb);
|
||||
|
||||
private string Disassemble_AM_11(int bbb, string instruction) => this.AM_11_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_11(bbb);
|
||||
private string Disassemble_AM_11(int bbb, string instruction) => AM_11_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_11(bbb);
|
||||
|
||||
private string Disassemble_AM_11_x(int bbb, string instruction) => this.AM_11_x_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_11_x(bbb);
|
||||
private string Disassemble_AM_11_x(int bbb, string instruction) => AM_11_x_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_11_x(bbb);
|
||||
|
||||
|
||||
private static string Pad(string? value = null, int limit = 10)
|
||||
@ -588,180 +589,180 @@ namespace EightBit
|
||||
return value + padding;
|
||||
}
|
||||
|
||||
private string AM_Immediate_dump() => Pad(this.Dump_Byte((ushort)(this.address + 1)));
|
||||
private string AM_Immediate_dump() => Pad(Dump_Byte((ushort)(address + 1)));
|
||||
|
||||
private string AM_Immediate() => "#" + this.ConvertConstantByte((ushort)(this.address + 1));
|
||||
private string AM_Immediate() => "#" + ConvertConstantByte((ushort)(address + 1));
|
||||
|
||||
private string AM_Absolute_dump() => Pad(this.Dump_DByte((ushort)(this.address + 1)));
|
||||
private string AM_Absolute_dump() => Pad(Dump_DByte((ushort)(address + 1)));
|
||||
|
||||
private string AM_Absolute() => this.ConvertAddressAt((ushort)(this.address + 1));
|
||||
private string AM_Absolute() => ConvertAddressAt((ushort)(address + 1));
|
||||
|
||||
private string AM_ZeroPage_dump() => Pad(this.Dump_Byte((ushort)(this.address + 1)));
|
||||
private string AM_ZeroPage_dump() => Pad(Dump_Byte((ushort)(address + 1)));
|
||||
|
||||
private string AM_ZeroPage() => this.ConvertZPAddressAt((ushort)(this.address + 1));
|
||||
private string AM_ZeroPage() => ConvertZPAddressAt((ushort)(address + 1));
|
||||
|
||||
private string AM_ZeroPageX_dump() => this.AM_ZeroPage_dump();
|
||||
private string AM_ZeroPageX_dump() => AM_ZeroPage_dump();
|
||||
|
||||
private string AM_ZeroPageX() => this.AM_ZeroPage() + ",X";
|
||||
private string AM_ZeroPageX() => AM_ZeroPage() + ",X";
|
||||
|
||||
private string AM_ZeroPageY_dump() => this.AM_ZeroPage_dump();
|
||||
private string AM_ZeroPageY_dump() => AM_ZeroPage_dump();
|
||||
|
||||
private string AM_ZeroPageY() => this.AM_ZeroPage() + ",Y";
|
||||
private string AM_ZeroPageY() => AM_ZeroPage() + ",Y";
|
||||
|
||||
private string AM_AbsoluteX_dump() => this.AM_Absolute_dump();
|
||||
private string AM_AbsoluteX_dump() => AM_Absolute_dump();
|
||||
|
||||
private string AM_AbsoluteX() => this.AM_Absolute() + ",X";
|
||||
private string AM_AbsoluteX() => AM_Absolute() + ",X";
|
||||
|
||||
private string AM_AbsoluteY_dump() => this.AM_Absolute_dump();
|
||||
private string AM_AbsoluteY_dump() => AM_Absolute_dump();
|
||||
|
||||
private string AM_AbsoluteY() => this.AM_Absolute() + ",Y";
|
||||
private string AM_AbsoluteY() => AM_Absolute() + ",Y";
|
||||
|
||||
private string AM_IndexedIndirectX_dump() => this.AM_ZeroPage_dump();
|
||||
private string AM_IndexedIndirectX_dump() => AM_ZeroPage_dump();
|
||||
|
||||
private string AM_IndexedIndirectX() => "(" + this.ConvertZPAddressAt((ushort)(this.address + 1)) + ",X)";
|
||||
private string AM_IndexedIndirectX() => "(" + ConvertZPAddressAt((ushort)(address + 1)) + ",X)";
|
||||
|
||||
private string AM_IndirectIndexedY_dump() => this.AM_ZeroPage_dump();
|
||||
private string AM_IndirectIndexedY_dump() => AM_ZeroPage_dump();
|
||||
|
||||
private string AM_IndirectIndexedY() => "(" + this.ConvertZPAddressAt((ushort)(this.address + 1)) + "),Y";
|
||||
private string AM_IndirectIndexedY() => "(" + ConvertZPAddressAt((ushort)(address + 1)) + "),Y";
|
||||
|
||||
private string AM_00_dump(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_Immediate_dump(),
|
||||
0b001 => this.AM_ZeroPage_dump(),
|
||||
0b011 => this.AM_Absolute_dump(),
|
||||
0b101 => this.AM_ZeroPageX_dump(),
|
||||
0b111 => this.AM_AbsoluteX_dump(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_Immediate_dump(),
|
||||
0b001 => AM_ZeroPage_dump(),
|
||||
0b011 => AM_Absolute_dump(),
|
||||
0b101 => AM_ZeroPageX_dump(),
|
||||
0b111 => AM_AbsoluteX_dump(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_00(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_Immediate(),
|
||||
0b001 => this.AM_ZeroPage(),
|
||||
0b011 => this.AM_Absolute(),
|
||||
0b101 => this.AM_ZeroPageX(),
|
||||
0b111 => this.AM_AbsoluteX(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_Immediate(),
|
||||
0b001 => AM_ZeroPage(),
|
||||
0b011 => AM_Absolute(),
|
||||
0b101 => AM_ZeroPageX(),
|
||||
0b111 => AM_AbsoluteX(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_01_dump(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_IndexedIndirectX_dump(),
|
||||
0b001 => this.AM_ZeroPage_dump(),
|
||||
0b010 => this.AM_Immediate_dump(),
|
||||
0b011 => this.AM_Absolute_dump(),
|
||||
0b100 => this.AM_IndirectIndexedY_dump(),
|
||||
0b101 => this.AM_ZeroPageX_dump(),
|
||||
0b110 => this.AM_AbsoluteY_dump(),
|
||||
0b111 => this.AM_AbsoluteX_dump(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_IndexedIndirectX_dump(),
|
||||
0b001 => AM_ZeroPage_dump(),
|
||||
0b010 => AM_Immediate_dump(),
|
||||
0b011 => AM_Absolute_dump(),
|
||||
0b100 => AM_IndirectIndexedY_dump(),
|
||||
0b101 => AM_ZeroPageX_dump(),
|
||||
0b110 => AM_AbsoluteY_dump(),
|
||||
0b111 => AM_AbsoluteX_dump(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_01(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_IndexedIndirectX(),
|
||||
0b001 => this.AM_ZeroPage(),
|
||||
0b010 => this.AM_Immediate(),
|
||||
0b011 => this.AM_Absolute(),
|
||||
0b100 => this.AM_IndirectIndexedY(),
|
||||
0b101 => this.AM_ZeroPageX(),
|
||||
0b110 => this.AM_AbsoluteY(),
|
||||
0b111 => this.AM_AbsoluteX(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_IndexedIndirectX(),
|
||||
0b001 => AM_ZeroPage(),
|
||||
0b010 => AM_Immediate(),
|
||||
0b011 => AM_Absolute(),
|
||||
0b100 => AM_IndirectIndexedY(),
|
||||
0b101 => AM_ZeroPageX(),
|
||||
0b110 => AM_AbsoluteY(),
|
||||
0b111 => AM_AbsoluteX(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_10_dump(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_Immediate_dump(),
|
||||
0b001 => this.AM_ZeroPage_dump(),
|
||||
0b000 => AM_Immediate_dump(),
|
||||
0b001 => AM_ZeroPage_dump(),
|
||||
0b010 => string.Empty,
|
||||
0b011 => this.AM_Absolute_dump(),
|
||||
0b101 => this.AM_ZeroPageX_dump(),
|
||||
0b111 => this.AM_AbsoluteX_dump(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b011 => AM_Absolute_dump(),
|
||||
0b101 => AM_ZeroPageX_dump(),
|
||||
0b111 => AM_AbsoluteX_dump(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_10(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_Immediate(),
|
||||
0b001 => this.AM_ZeroPage(),
|
||||
0b000 => AM_Immediate(),
|
||||
0b001 => AM_ZeroPage(),
|
||||
0b010 => "A",
|
||||
0b011 => this.AM_Absolute(),
|
||||
0b101 => this.AM_ZeroPageX(),
|
||||
0b111 => this.AM_AbsoluteX(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b011 => AM_Absolute(),
|
||||
0b101 => AM_ZeroPageX(),
|
||||
0b111 => AM_AbsoluteX(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_10_x_dump(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_Immediate_dump(),
|
||||
0b001 => this.AM_ZeroPage_dump(),
|
||||
0b000 => AM_Immediate_dump(),
|
||||
0b001 => AM_ZeroPage_dump(),
|
||||
0b010 => string.Empty,
|
||||
0b011 => this.AM_Absolute_dump(),
|
||||
0b101 => this.AM_ZeroPageY_dump(),
|
||||
0b111 => this.AM_AbsoluteY_dump(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b011 => AM_Absolute_dump(),
|
||||
0b101 => AM_ZeroPageY_dump(),
|
||||
0b111 => AM_AbsoluteY_dump(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_10_x(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_Immediate(),
|
||||
0b001 => this.AM_ZeroPage(),
|
||||
0b000 => AM_Immediate(),
|
||||
0b001 => AM_ZeroPage(),
|
||||
0b010 => "A",
|
||||
0b011 => this.AM_Absolute(),
|
||||
0b101 => this.AM_ZeroPageY(),
|
||||
0b111 => this.AM_AbsoluteY(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b011 => AM_Absolute(),
|
||||
0b101 => AM_ZeroPageY(),
|
||||
0b111 => AM_AbsoluteY(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_11_dump(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_IndexedIndirectX_dump(),
|
||||
0b001 => this.AM_ZeroPage_dump(),
|
||||
0b010 => this.AM_Immediate_dump(),
|
||||
0b011 => this.AM_Absolute_dump(),
|
||||
0b100 => this.AM_IndirectIndexedY_dump(),
|
||||
0b101 => this.AM_ZeroPageY_dump(),
|
||||
0b111 => this.AM_AbsoluteY_dump(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_IndexedIndirectX_dump(),
|
||||
0b001 => AM_ZeroPage_dump(),
|
||||
0b010 => AM_Immediate_dump(),
|
||||
0b011 => AM_Absolute_dump(),
|
||||
0b100 => AM_IndirectIndexedY_dump(),
|
||||
0b101 => AM_ZeroPageY_dump(),
|
||||
0b111 => AM_AbsoluteY_dump(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_11_x_dump(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_IndexedIndirectX_dump(),
|
||||
0b001 => this.AM_ZeroPage_dump(),
|
||||
0b010 => this.AM_Immediate_dump(),
|
||||
0b011 => this.AM_Absolute_dump(),
|
||||
0b100 => this.AM_IndirectIndexedY_dump(),
|
||||
0b101 => this.AM_ZeroPageX_dump(),
|
||||
0b110 => this.AM_AbsoluteY_dump(),
|
||||
0b111 => this.AM_AbsoluteX_dump(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_IndexedIndirectX_dump(),
|
||||
0b001 => AM_ZeroPage_dump(),
|
||||
0b010 => AM_Immediate_dump(),
|
||||
0b011 => AM_Absolute_dump(),
|
||||
0b100 => AM_IndirectIndexedY_dump(),
|
||||
0b101 => AM_ZeroPageX_dump(),
|
||||
0b110 => AM_AbsoluteY_dump(),
|
||||
0b111 => AM_AbsoluteX_dump(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_11(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_IndexedIndirectX(),
|
||||
0b001 => this.AM_ZeroPage(),
|
||||
0b010 => this.AM_Immediate(),
|
||||
0b011 => this.AM_Absolute(),
|
||||
0b100 => this.AM_IndirectIndexedY(),
|
||||
0b101 => this.AM_ZeroPageY(),
|
||||
0b111 => this.AM_AbsoluteY(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_IndexedIndirectX(),
|
||||
0b001 => AM_ZeroPage(),
|
||||
0b010 => AM_Immediate(),
|
||||
0b011 => AM_Absolute(),
|
||||
0b100 => AM_IndirectIndexedY(),
|
||||
0b101 => AM_ZeroPageY(),
|
||||
0b111 => AM_AbsoluteY(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
|
||||
private string AM_11_x(int bbb) => bbb switch
|
||||
{
|
||||
0b000 => this.AM_IndexedIndirectX(),
|
||||
0b001 => this.AM_ZeroPage(),
|
||||
0b010 => this.AM_Immediate(),
|
||||
0b011 => this.AM_Absolute(),
|
||||
0b100 => this.AM_IndirectIndexedY(),
|
||||
0b101 => this.AM_ZeroPageX(),
|
||||
0b110 => this.AM_AbsoluteY(),
|
||||
0b111 => this.AM_AbsoluteX(),
|
||||
_ => throw new System.InvalidOperationException("Illegal addressing mode"),
|
||||
0b000 => AM_IndexedIndirectX(),
|
||||
0b001 => AM_ZeroPage(),
|
||||
0b010 => AM_Immediate(),
|
||||
0b011 => AM_Absolute(),
|
||||
0b100 => AM_IndirectIndexedY(),
|
||||
0b101 => AM_ZeroPageX(),
|
||||
0b110 => AM_AbsoluteY(),
|
||||
0b111 => AM_AbsoluteX(),
|
||||
_ => throw new InvalidOperationException("Illegal addressing mode"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
{
|
||||
private TestRunner Runner { get; }
|
||||
|
||||
private EightBit.Files.Symbols.Parser Symbols { get; } = new();
|
||||
private Symbols.Parser Symbols { get; } = new();
|
||||
|
||||
private EightBit.Disassembler Disassembler { get; }
|
||||
private Disassembler Disassembler { get; }
|
||||
|
||||
private bool CycleCountMismatch { get; set; }
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
public Checker(TestRunner runner)
|
||||
{
|
||||
this.Runner = runner;
|
||||
this.Disassembler = new(this.Runner, (EightBit.M6502Core)this.Runner.CPU, this.Symbols);
|
||||
this.Disassembler = new(this.Runner, (M6502.Core)this.Runner.CPU, this.Symbols);
|
||||
}
|
||||
|
||||
public void Check(Test test)
|
||||
@ -203,8 +203,8 @@
|
||||
|
||||
if (!p_good)
|
||||
{
|
||||
this.Messages.Add($"Expected flags: {EightBit.Disassembler.DumpFlags(final.P)}");
|
||||
this.Messages.Add($"Actual flags : {EightBit.Disassembler.DumpFlags(cpu.P)}");
|
||||
this.Messages.Add($"Expected flags: {Disassembler.DumpFlags(final.P)}");
|
||||
this.Messages.Add($"Actual flags : {Disassembler.DumpFlags(cpu.P)}");
|
||||
}
|
||||
|
||||
if (final.RAM == null)
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
// file id=0,name="sudoku.s",size=9141,mtime=0x6027C7F0,mod=0
|
||||
[Section("file", "Files")]
|
||||
@ -11,6 +11,6 @@
|
||||
public DateTime ModificationTime { get; private set; }
|
||||
|
||||
[SectionReference("mod")]
|
||||
public Symbols.Module? Module { get; private set; }
|
||||
public Module? Module { get; private set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
@ -1,37 +1,37 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
//info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3
|
||||
[Section("info")]
|
||||
[M6502.Symbols.Section("info")]
|
||||
public sealed class Information(Parser container) : Section(container)
|
||||
{
|
||||
[SectionProperty("csym")]
|
||||
[M6502.Symbols.SectionProperty("csym")]
|
||||
public int CSymbol { get; private set; }
|
||||
|
||||
[SectionProperty("file")]
|
||||
[M6502.Symbols.SectionProperty("file")]
|
||||
public int File { get; private set; }
|
||||
|
||||
[SectionProperty("lib")]
|
||||
[M6502.Symbols.SectionProperty("lib")]
|
||||
public int Library { get; private set; }
|
||||
|
||||
[SectionProperty("line")]
|
||||
[M6502.Symbols.SectionProperty("line")]
|
||||
public int Line { get; private set; }
|
||||
|
||||
[SectionProperty("mod")]
|
||||
[M6502.Symbols.SectionProperty("mod")]
|
||||
public int Module { get; private set; }
|
||||
|
||||
[SectionProperty("scope")]
|
||||
[M6502.Symbols.SectionProperty("scope")]
|
||||
public int Scope { get; private set; }
|
||||
|
||||
[SectionProperty("seg")]
|
||||
[M6502.Symbols.SectionProperty("seg")]
|
||||
public int Segment { get; private set; }
|
||||
|
||||
[SectionProperty("span")]
|
||||
[M6502.Symbols.SectionProperty("span")]
|
||||
public int Span { get; private set; }
|
||||
|
||||
[SectionProperty("sym")]
|
||||
[M6502.Symbols.SectionProperty("sym")]
|
||||
public int Symbol { get; private set; }
|
||||
|
||||
[SectionProperty("type")]
|
||||
[M6502.Symbols.SectionProperty("type")]
|
||||
public int Type { get; private set; }
|
||||
|
||||
public int Count(string key) => this.GetValueT<int>(key);
|
||||
|
@ -1,11 +1,13 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
using M6502.Symbols;
|
||||
|
||||
// line id = 268, file = 1, line = 60, type = 2, count = 1, span = 286 + 195
|
||||
[Section("line", "Lines")]
|
||||
public sealed class Line(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
[SectionReference("file")]
|
||||
public Symbols.File? File { get; private set; }
|
||||
public File? File { get; private set; }
|
||||
|
||||
[SectionProperty("line")]
|
||||
public int LineNumber { get; private set; }
|
||||
|
@ -1,10 +1,10 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
// mod id=0,name="sudoku.o",file=0
|
||||
[Section("mod", "Modules")]
|
||||
public sealed class Module(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionReference("file")]
|
||||
public Symbols.File? File { get; private set; }
|
||||
public File? File { get; private set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
public class NamedSection : IdentifiableSection
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
//scope id = 0, name = "", mod = 0, size = 1137, span = 355 + 354
|
||||
//scope id = 1, name = "stack", mod = 0, type = scope, size = 7, parent = 0, span = 15
|
||||
@ -7,7 +7,7 @@
|
||||
public sealed class Scope(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionReference("mod")]
|
||||
public Symbols.Module? Module { get; private set; }
|
||||
public Module? Module { get; private set; }
|
||||
|
||||
[SectionProperty("type")]
|
||||
public string? Type { get; private set; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal class SectionAttribute(string key, string? referencing = null) : Attribute
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionEnumerationAttribute(string key) : SectionPropertyAttribute(key, enumeration: true)
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal class SectionPropertyAttribute(string key, System.Type? type = null, bool enumeration = false, bool hexadecimal = false, bool optional = false, bool many = false) : Attribute
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionReferenceAttribute(string key, bool optional = false) : SectionPropertyAttribute(key, type: typeof(int), optional: optional)
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionReferencesAttribute(string key) : SectionPropertyAttribute(key, many: true)
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
// seg id=1,name="RODATA",start=0x00F471,size=0x0000,addrsize=absolute,type=ro,oname="sudoku.65b",ooffs=1137
|
||||
[Section("seg", "Segments")]
|
||||
|
@ -1,11 +1,11 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
//span id = 351, seg = 7, start = 0, size = 2, type = 2
|
||||
[Section("span", "Spans")]
|
||||
public sealed class Span(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
[SectionReference("seg")]
|
||||
public Symbols.Segment? Segment { get; private set; }
|
||||
public Segment? Segment { get; private set; }
|
||||
|
||||
[SectionProperty("start")]
|
||||
public int Start { get; private set; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
public int? Size { get; private set; }
|
||||
|
||||
[SectionReference("scope")]
|
||||
public Symbols.Scope? Scope { get; private set; }
|
||||
public Scope? Scope { get; private set; }
|
||||
|
||||
[SectionReferences("def")]
|
||||
public List<Line>? Definitions { get; private set; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
// type id = 0, val = "800920"
|
||||
[Section("type", "Types")]
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit.Files.Symbols
|
||||
namespace M6502.Symbols
|
||||
{
|
||||
//version major = 2, minor = 0
|
||||
[Section("version")]
|
||||
|
@ -9,12 +9,13 @@ namespace M6502.Test
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EightBit;
|
||||
using M6502;
|
||||
|
||||
internal class Board : Bus
|
||||
{
|
||||
private readonly Configuration configuration;
|
||||
private readonly Ram ram = new(0x10000);
|
||||
private readonly EightBit.Files.Symbols.Parser symbols = new();
|
||||
private readonly Symbols.Parser symbols = new();
|
||||
private readonly Disassembler disassembler;
|
||||
private readonly Profiler profiler;
|
||||
private readonly MemoryMapping mapping;
|
||||
@ -37,7 +38,7 @@ namespace M6502.Test
|
||||
this.profiler = new(this.CPU, this.disassembler, this.symbols, this.configuration.Profile);
|
||||
}
|
||||
|
||||
public M6502 CPU { get; }
|
||||
public MOS6502 CPU { get; }
|
||||
|
||||
public override void RaisePOWER()
|
||||
{
|
||||
|
345
M6502/M6502.cs
345
M6502/M6502.cs
@ -1,345 +0,0 @@
|
||||
// <copyright file="M6502.cs" company="Adrian Conlon">
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace EightBit
|
||||
{
|
||||
public class M6502(Bus bus) : M6502Core(bus)
|
||||
{
|
||||
#region Core instruction dispatching
|
||||
|
||||
protected override bool MaybeExecute()
|
||||
{
|
||||
if (base.MaybeExecute())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var cycles = this.Cycles;
|
||||
switch (this.OpCode)
|
||||
{
|
||||
case 0x02: this.Jam(); break; // *JAM
|
||||
case 0x03: this.IndexedIndirectXRead(); this.SLO(); break; // *SLO (indexed indirect X)
|
||||
case 0x04: this.ZeroPageRead(); break; // *NOP (zero page)
|
||||
case 0x07: this.ZeroPageRead(); this.SLO(); break; // *SLO (zero page)
|
||||
case 0x0b: this.ImmediateRead(); this.ANC(); break; // *ANC (immediate)
|
||||
case 0x0c: this.AbsoluteRead(); break; // *NOP (absolute)
|
||||
case 0x0f: this.AbsoluteRead(); this.SLO(); break; // *SLO (absolute)
|
||||
|
||||
case 0x12: this.Jam(); break; // *JAM
|
||||
case 0x13: this.IndirectIndexedYAddress(); this.FixupRead(); this.SLO(); break; // *SLO (indirect indexed Y)
|
||||
case 0x14: this.ZeroPageXRead(); break; // *NOP (zero page, X)
|
||||
case 0x17: this.ZeroPageXRead(); this.SLO(); break; // *SLO (zero page, X)
|
||||
case 0x1a: this.SwallowRead(); break; // *NOP (implied)
|
||||
case 0x1b: this.AbsoluteYAddress(); this.FixupRead(); this.SLO(); break; // *SLO (absolute, Y)
|
||||
case 0x1c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x1f: this.AbsoluteXAddress(); this.FixupRead(); this.SLO(); break; // *SLO (absolute, X)
|
||||
|
||||
case 0x22: this.Jam(); break; // *JAM
|
||||
case 0x23: this.IndexedIndirectXRead(); this.RLA(); ; break; // *RLA (indexed indirect X)
|
||||
case 0x27: this.ZeroPageRead(); this.RLA(); ; break; // *RLA (zero page)
|
||||
case 0x2b: this.ImmediateRead(); this.ANC(); break; // *ANC (immediate)
|
||||
case 0x2f: this.AbsoluteRead(); this.RLA(); break; // *RLA (absolute)
|
||||
|
||||
case 0x32: this.Jam(); break; // *JAM
|
||||
case 0x33: this.IndirectIndexedYAddress(); this.FixupRead(); this.RLA(); break; // *RLA (indirect indexed Y)
|
||||
case 0x34: this.ZeroPageXRead(); break; // *NOP (zero page, X)
|
||||
case 0x37: this.ZeroPageXRead(); this.RLA(); ; break; // *RLA (zero page, X)
|
||||
case 0x3a: this.SwallowRead(); break; // *NOP (implied)
|
||||
case 0x3b: this.AbsoluteYAddress(); this.FixupRead(); this.RLA(); break; // *RLA (absolute, Y)
|
||||
case 0x3c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x3f: this.AbsoluteXAddress(); this.FixupRead(); this.RLA(); break; // *RLA (absolute, X)
|
||||
|
||||
case 0x42: this.Jam(); break; // *JAM
|
||||
case 0x43: this.IndexedIndirectXRead(); this.SRE(); break; // *SRE (indexed indirect X)
|
||||
case 0x47: this.ZeroPageRead(); this.SRE(); break; // *SRE (zero page)
|
||||
case 0x4b: this.ImmediateRead(); this.ASR(); break; // *ASR (immediate)
|
||||
case 0x4f: this.AbsoluteRead(); this.SRE(); break; // *SRE (absolute)
|
||||
|
||||
case 0x52: this.Jam(); break; // *JAM
|
||||
case 0x53: this.IndirectIndexedYAddress(); this.FixupRead(); this.SRE(); break; // *SRE (indirect indexed Y)
|
||||
case 0x57: this.ZeroPageXRead(); this.SRE(); break; // *SRE (zero page, X)
|
||||
case 0x5a: this.SwallowRead(); break; // *NOP (implied)
|
||||
case 0x5b: this.AbsoluteYAddress(); this.FixupRead(); this.SRE(); break; // *SRE (absolute, Y)
|
||||
case 0x5c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x5f: this.AbsoluteXAddress(); this.FixupRead(); this.SRE(); break; // *SRE (absolute, X)
|
||||
|
||||
case 0x62: this.Jam(); break; // *JAM
|
||||
case 0x63: this.IndexedIndirectXRead(); this.RRA(); break; // *RRA (indexed indirect X)
|
||||
case 0x64: this.ZeroPageRead(); break; // *NOP (zero page)
|
||||
case 0x67: this.ZeroPageRead(); this.RRA(); break; // *RRA (zero page)
|
||||
case 0x6b: this.ImmediateRead(); this.ARR(); break; // *ARR (immediate)
|
||||
case 0x6f: this.AbsoluteRead(); this.RRA(); break; // *RRA (absolute)
|
||||
|
||||
case 0x72: this.Jam(); break; // *JAM
|
||||
case 0x73: this.IndirectIndexedYAddress(); this.FixupRead(); this.RRA(); break; // *RRA (indirect indexed Y)
|
||||
case 0x74: this.ZeroPageXRead(); break; // *NOP (zero page, X)
|
||||
case 0x77: this.ZeroPageXRead(); this.RRA(); break; // *RRA (zero page, X)
|
||||
case 0x7a: this.SwallowRead(); break; // *NOP (implied)
|
||||
case 0x7b: this.AbsoluteYAddress(); this.FixupRead(); this.RRA(); break; // *RRA (absolute, Y)
|
||||
case 0x7c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x7f: this.AbsoluteXAddress(); this.FixupRead(); this.RRA(); break; // *RRA (absolute, X)
|
||||
|
||||
case 0x80: this.ImmediateRead(); break; // *NOP (immediate)
|
||||
case 0x83: this.IndexedIndirectXAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (indexed indirect X)
|
||||
case 0x87: this.ZeroPageAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (zero page)
|
||||
case 0x89: this.ImmediateRead(); break; // *NOP (immediate)
|
||||
case 0x8b: this.ImmediateRead(); this.ANE(); break; // *ANE (immediate)
|
||||
case 0x8f: this.AbsoluteAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (absolute)
|
||||
|
||||
case 0x92: this.Jam(); break; // *JAM
|
||||
case 0x93: this.IndirectIndexedYAddress(); this.Fixup(); this.SHA(); break; // *SHA (indirect indexed, Y)
|
||||
case 0x97: this.ZeroPageYAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (zero page, Y)
|
||||
case 0x9b: this.AbsoluteYAddress(); this.Fixup(); this.TAS(); break; // *TAS (absolute, Y)
|
||||
case 0x9c: this.AbsoluteXAddress(); this.Fixup(); this.SYA(); break; // *SYA (absolute, X)
|
||||
case 0x9e: this.AbsoluteYAddress(); this.Fixup(); this.SXA(); break; // *SXA (absolute, Y)
|
||||
case 0x9f: this.AbsoluteYAddress(); this.Fixup(); this.SHA(); break; // *SHA (absolute, Y)
|
||||
|
||||
case 0xa3: this.IndexedIndirectXRead(); this.A = this.X = this.Through(); break; // *LAX (indexed indirect X)
|
||||
case 0xa7: this.ZeroPageRead(); this.A = this.X = this.Through(); break; // *LAX (zero page)
|
||||
case 0xab: this.ImmediateRead(); this.ATX(); break; // *ATX (immediate)
|
||||
case 0xaf: this.AbsoluteRead(); this.A = this.X = this.Through(); break; // *LAX (absolute)
|
||||
|
||||
case 0xb2: this.Jam(); break; // *JAM
|
||||
case 0xb3: this.IndirectIndexedYRead(); this.A = this.X = this.Through(); break; // *LAX (indirect indexed Y)
|
||||
case 0xb7: this.ZeroPageYRead(); this.A = this.X = this.Through(); break; // *LAX (zero page, Y)
|
||||
case 0xbb: this.AbsoluteYAddress(); this.MaybeFixup(); this.LAS(); break; // *LAS (absolute, Y)
|
||||
case 0xbf: this.AbsoluteYRead(); this.A = this.X = this.Through(); break; // *LAX (absolute, Y)
|
||||
|
||||
case 0xc3: this.IndexedIndirectXRead(); this.DCP(); break; // *DCP (indexed indirect X)
|
||||
case 0xc7: this.ZeroPageRead(); this.DCP(); break; // *DCP (zero page)
|
||||
case 0xcb: this.ImmediateRead(); this.AXS(); break; // *AXS (immediate)
|
||||
case 0xcf: this.AbsoluteRead(); this.DCP(); break; // *DCP (absolute)
|
||||
|
||||
case 0xd2: this.Jam(); break; // *JAM
|
||||
case 0xd3: this.IndirectIndexedYAddress(); this.FixupRead(); this.DCP(); break; // *DCP (indirect indexed Y)
|
||||
case 0xd7: this.ZeroPageXRead(); this.DCP(); break; // *DCP (zero page, X)
|
||||
case 0xda: this.SwallowRead(); break; // *NOP (implied)
|
||||
case 0xdb: this.AbsoluteYAddress(); this.FixupRead(); this.DCP(); break; // *DCP (absolute, Y)
|
||||
case 0xdc: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0xdf: this.AbsoluteXAddress(); this.FixupRead(); this.DCP(); break; // *DCP (absolute, X)
|
||||
|
||||
case 0xe3: this.IndexedIndirectXRead(); this.ISB(); break; // *ISB (indexed indirect X)
|
||||
case 0xe7: this.ZeroPageRead(); this.ISB(); break; // *ISB (zero page)
|
||||
case 0xeb: this.ImmediateRead(); this.SBC(); break; // *SBC (immediate)
|
||||
case 0xef: this.AbsoluteRead(); this.ISB(); break; // *ISB (absolute)
|
||||
|
||||
case 0xf2: this.Jam(); break; // *JAM
|
||||
case 0xf3: this.IndirectIndexedYAddress(); this.FixupRead(); this.ISB(); break; // *ISB (indirect indexed Y)
|
||||
case 0xf7: this.ZeroPageXRead(); this.ISB(); break; // *ISB (zero page, X)
|
||||
case 0xfa: this.SwallowRead(); break; // *NOP (implied)
|
||||
case 0xfb: this.AbsoluteYAddress(); this.FixupRead(); this.ISB(); break; // *ISB (absolute, Y)
|
||||
case 0xfc: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0xff: this.AbsoluteXAddress(); this.FixupRead(); this.ISB(); break; // *ISB (absolute, X)
|
||||
}
|
||||
|
||||
return cycles != this.Cycles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bus/Memory Access
|
||||
|
||||
protected override void ModifyWrite(byte data)
|
||||
{
|
||||
// The read will have already taken place...
|
||||
this.MemoryWrite(); // Modify cycle
|
||||
this.MemoryWrite(data); // Write cycle
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Addressing modes
|
||||
|
||||
protected override void IndirectAddress()
|
||||
{
|
||||
this.AbsoluteAddress();
|
||||
this.GetAddressPaged();
|
||||
}
|
||||
|
||||
#region Address page fixup
|
||||
|
||||
protected override void Fixup()
|
||||
{
|
||||
this.MemoryRead();
|
||||
this.Bus.Address.High = this.FixedPage;
|
||||
}
|
||||
|
||||
protected override void FixupBranch(sbyte relative)
|
||||
{
|
||||
this.NoteFixedAddress(this.PC.Word + relative);
|
||||
this.MaybeFixup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instruction implementations
|
||||
|
||||
#region Undocumented instructions
|
||||
|
||||
#region Undocumented instructions with BCD effects
|
||||
|
||||
private void ARR()
|
||||
{
|
||||
var value = this.Bus.Data;
|
||||
if (this.DecimalMasked != 0)
|
||||
this.ARR_d(value);
|
||||
else
|
||||
this.ARR_b(value);
|
||||
}
|
||||
|
||||
private void ARR_d(byte value)
|
||||
{
|
||||
// With thanks to https://github.com/TomHarte/CLK
|
||||
// What a very strange instruction ARR is...
|
||||
|
||||
this.A &= value;
|
||||
var unshiftedA = this.A;
|
||||
this.A = this.Through((this.A >> 1) | (this.Carry << 7));
|
||||
this.SetFlag(StatusBits.VF, OverflowTest((byte)(this.A ^ (this.A << 1))));
|
||||
|
||||
if (LowerNibble(unshiftedA) + (unshiftedA & 0x1) > 5)
|
||||
this.A = (byte)(LowerNibble((byte)(this.A + 6)) | HigherNibble(this.A));
|
||||
|
||||
this.SetFlag(StatusBits.CF, HigherNibble(unshiftedA) + (unshiftedA & 0x10) > 0x50);
|
||||
|
||||
if (this.Carry != 0)
|
||||
this.A += 0x60;
|
||||
}
|
||||
|
||||
private void ARR_b(byte value)
|
||||
{
|
||||
this.A &= value;
|
||||
this.A = this.Through((this.A >> 1) | (this.Carry << 7));
|
||||
this.SetFlag(StatusBits.CF, OverflowTest(this.A));
|
||||
this.SetFlag(StatusBits.VF, OverflowTest((byte)(this.A ^ (this.A << 1))));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Undocumented instructions with fixup effects
|
||||
|
||||
private void StoreFixupEffect(byte data)
|
||||
{
|
||||
////var fixedAddress = (byte)(this.Bus.Address.High + 1);
|
||||
//var fixedAddress = this.FixedPage + 1;
|
||||
//var updated = (byte)(data & fixedAddress);
|
||||
//if (this.Fixed)
|
||||
//{
|
||||
// this.Bus.Address.High = updated;
|
||||
//}
|
||||
|
||||
//this.MemoryWrite(updated);
|
||||
|
||||
byte updated;
|
||||
if (this.Fixed)
|
||||
{
|
||||
updated = (byte)(data & this.FixedPage);
|
||||
this.Bus.Address.High = updated;
|
||||
}
|
||||
else
|
||||
{
|
||||
updated = (byte)(data & this.UnfixedPage);
|
||||
this.Bus.Address.High = updated;
|
||||
}
|
||||
this.MemoryWrite(updated);
|
||||
}
|
||||
|
||||
private void SHA() => this.StoreFixupEffect((byte)(this.A & this.X));
|
||||
|
||||
private void SYA() => this.StoreFixupEffect(this.Y);
|
||||
|
||||
private void SXA() => this.StoreFixupEffect(this.X);
|
||||
|
||||
#endregion
|
||||
|
||||
private void ANC()
|
||||
{
|
||||
this.AndR();
|
||||
this.SetFlag(StatusBits.CF, NegativeTest(this.A));
|
||||
}
|
||||
|
||||
private void AXS()
|
||||
{
|
||||
this.X = this.Through(this.BinarySUB((byte)(this.A & this.X)));
|
||||
this.ResetFlag(StatusBits.CF, this.Intermediate.High);
|
||||
}
|
||||
|
||||
private void Jam()
|
||||
{
|
||||
this.Bus.Address.Assign(this.PC);
|
||||
this.MemoryRead();
|
||||
this.MemoryRead(0xff, 0xff);
|
||||
this.Bus.Address.Low = 0xfe;
|
||||
this.MemoryRead();
|
||||
this.MemoryRead();
|
||||
this.Bus.Address.Low = 0xff;
|
||||
this.MemoryRead();
|
||||
this.MemoryRead();
|
||||
this.MemoryRead();
|
||||
this.MemoryRead();
|
||||
this.MemoryRead();
|
||||
this.MemoryRead();
|
||||
}
|
||||
|
||||
private void TAS()
|
||||
{
|
||||
this.S = (byte)(this.A & this.X);
|
||||
this.SHA();
|
||||
}
|
||||
|
||||
private void LAS() => this.A = this.X = this.S = this.Through(this.MemoryRead() & this.S);
|
||||
|
||||
private void ANE() => this.A = this.Through((this.A | 0xee) & this.X & this.Bus.Data);
|
||||
|
||||
private void ATX() => this.A = this.X = this.Through((this.A | 0xee) & this.Bus.Data);
|
||||
|
||||
private void ASR()
|
||||
{
|
||||
this.AndR();
|
||||
this.A = this.LSR(this.A);
|
||||
}
|
||||
|
||||
private void ISB()
|
||||
{
|
||||
this.ModifyWrite(this.INC());
|
||||
this.SBC();
|
||||
}
|
||||
|
||||
private void RLA()
|
||||
{
|
||||
this.ModifyWrite(this.ROL());
|
||||
this.AndR();
|
||||
}
|
||||
|
||||
private void RRA()
|
||||
{
|
||||
this.ModifyWrite(this.ROR());
|
||||
this.ADC();
|
||||
}
|
||||
|
||||
private void SLO()
|
||||
{
|
||||
this.ModifyWrite(this.ASL());
|
||||
this.OrR();
|
||||
}
|
||||
|
||||
private void SRE()
|
||||
{
|
||||
this.ModifyWrite(this.LSR());
|
||||
this.EorR();
|
||||
}
|
||||
|
||||
private void DCP()
|
||||
{
|
||||
this.ModifyWrite(this.DEC());
|
||||
this.CMP(this.A);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
1108
M6502/M6502Core.cs
1108
M6502/M6502Core.cs
File diff suppressed because it is too large
Load Diff
347
M6502/MOS6502.cs
Normal file
347
M6502/MOS6502.cs
Normal file
@ -0,0 +1,347 @@
|
||||
// <copyright file="MOS6502.cs" company="Adrian Conlon">
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace M6502
|
||||
{
|
||||
using EightBit;
|
||||
|
||||
public class MOS6502(Bus bus) : Core(bus)
|
||||
{
|
||||
#region Core instruction dispatching
|
||||
|
||||
protected override bool MaybeExecute()
|
||||
{
|
||||
if (base.MaybeExecute())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var cycles = Cycles;
|
||||
switch (OpCode)
|
||||
{
|
||||
case 0x02: Jam(); break; // *JAM
|
||||
case 0x03: IndexedIndirectXRead(); SLO(); break; // *SLO (indexed indirect X)
|
||||
case 0x04: ZeroPageRead(); break; // *NOP (zero page)
|
||||
case 0x07: ZeroPageRead(); SLO(); break; // *SLO (zero page)
|
||||
case 0x0b: ImmediateRead(); ANC(); break; // *ANC (immediate)
|
||||
case 0x0c: AbsoluteRead(); break; // *NOP (absolute)
|
||||
case 0x0f: AbsoluteRead(); SLO(); break; // *SLO (absolute)
|
||||
|
||||
case 0x12: Jam(); break; // *JAM
|
||||
case 0x13: IndirectIndexedYAddress(); FixupRead(); SLO(); break; // *SLO (indirect indexed Y)
|
||||
case 0x14: ZeroPageXRead(); break; // *NOP (zero page, X)
|
||||
case 0x17: ZeroPageXRead(); SLO(); break; // *SLO (zero page, X)
|
||||
case 0x1a: SwallowRead(); break; // *NOP (implied)
|
||||
case 0x1b: AbsoluteYAddress(); FixupRead(); SLO(); break; // *SLO (absolute, Y)
|
||||
case 0x1c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x1f: AbsoluteXAddress(); FixupRead(); SLO(); break; // *SLO (absolute, X)
|
||||
|
||||
case 0x22: Jam(); break; // *JAM
|
||||
case 0x23: IndexedIndirectXRead(); RLA(); ; break; // *RLA (indexed indirect X)
|
||||
case 0x27: ZeroPageRead(); RLA(); ; break; // *RLA (zero page)
|
||||
case 0x2b: ImmediateRead(); ANC(); break; // *ANC (immediate)
|
||||
case 0x2f: AbsoluteRead(); RLA(); break; // *RLA (absolute)
|
||||
|
||||
case 0x32: Jam(); break; // *JAM
|
||||
case 0x33: IndirectIndexedYAddress(); FixupRead(); RLA(); break; // *RLA (indirect indexed Y)
|
||||
case 0x34: ZeroPageXRead(); break; // *NOP (zero page, X)
|
||||
case 0x37: ZeroPageXRead(); RLA(); ; break; // *RLA (zero page, X)
|
||||
case 0x3a: SwallowRead(); break; // *NOP (implied)
|
||||
case 0x3b: AbsoluteYAddress(); FixupRead(); RLA(); break; // *RLA (absolute, Y)
|
||||
case 0x3c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x3f: AbsoluteXAddress(); FixupRead(); RLA(); break; // *RLA (absolute, X)
|
||||
|
||||
case 0x42: Jam(); break; // *JAM
|
||||
case 0x43: IndexedIndirectXRead(); SRE(); break; // *SRE (indexed indirect X)
|
||||
case 0x47: ZeroPageRead(); SRE(); break; // *SRE (zero page)
|
||||
case 0x4b: ImmediateRead(); ASR(); break; // *ASR (immediate)
|
||||
case 0x4f: AbsoluteRead(); SRE(); break; // *SRE (absolute)
|
||||
|
||||
case 0x52: Jam(); break; // *JAM
|
||||
case 0x53: IndirectIndexedYAddress(); FixupRead(); SRE(); break; // *SRE (indirect indexed Y)
|
||||
case 0x57: ZeroPageXRead(); SRE(); break; // *SRE (zero page, X)
|
||||
case 0x5a: SwallowRead(); break; // *NOP (implied)
|
||||
case 0x5b: AbsoluteYAddress(); FixupRead(); SRE(); break; // *SRE (absolute, Y)
|
||||
case 0x5c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x5f: AbsoluteXAddress(); FixupRead(); SRE(); break; // *SRE (absolute, X)
|
||||
|
||||
case 0x62: Jam(); break; // *JAM
|
||||
case 0x63: IndexedIndirectXRead(); RRA(); break; // *RRA (indexed indirect X)
|
||||
case 0x64: ZeroPageRead(); break; // *NOP (zero page)
|
||||
case 0x67: ZeroPageRead(); RRA(); break; // *RRA (zero page)
|
||||
case 0x6b: ImmediateRead(); ARR(); break; // *ARR (immediate)
|
||||
case 0x6f: AbsoluteRead(); RRA(); break; // *RRA (absolute)
|
||||
|
||||
case 0x72: Jam(); break; // *JAM
|
||||
case 0x73: IndirectIndexedYAddress(); FixupRead(); RRA(); break; // *RRA (indirect indexed Y)
|
||||
case 0x74: ZeroPageXRead(); break; // *NOP (zero page, X)
|
||||
case 0x77: ZeroPageXRead(); RRA(); break; // *RRA (zero page, X)
|
||||
case 0x7a: SwallowRead(); break; // *NOP (implied)
|
||||
case 0x7b: AbsoluteYAddress(); FixupRead(); RRA(); break; // *RRA (absolute, Y)
|
||||
case 0x7c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0x7f: AbsoluteXAddress(); FixupRead(); RRA(); break; // *RRA (absolute, X)
|
||||
|
||||
case 0x80: ImmediateRead(); break; // *NOP (immediate)
|
||||
case 0x83: IndexedIndirectXAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (indexed indirect X)
|
||||
case 0x87: ZeroPageAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (zero page)
|
||||
case 0x89: ImmediateRead(); break; // *NOP (immediate)
|
||||
case 0x8b: ImmediateRead(); ANE(); break; // *ANE (immediate)
|
||||
case 0x8f: AbsoluteAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (absolute)
|
||||
|
||||
case 0x92: Jam(); break; // *JAM
|
||||
case 0x93: IndirectIndexedYAddress(); Fixup(); SHA(); break; // *SHA (indirect indexed, Y)
|
||||
case 0x97: ZeroPageYAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (zero page, Y)
|
||||
case 0x9b: AbsoluteYAddress(); Fixup(); TAS(); break; // *TAS (absolute, Y)
|
||||
case 0x9c: AbsoluteXAddress(); Fixup(); SYA(); break; // *SYA (absolute, X)
|
||||
case 0x9e: AbsoluteYAddress(); Fixup(); SXA(); break; // *SXA (absolute, Y)
|
||||
case 0x9f: AbsoluteYAddress(); Fixup(); SHA(); break; // *SHA (absolute, Y)
|
||||
|
||||
case 0xa3: IndexedIndirectXRead(); A = X = Through(); break; // *LAX (indexed indirect X)
|
||||
case 0xa7: ZeroPageRead(); A = X = Through(); break; // *LAX (zero page)
|
||||
case 0xab: ImmediateRead(); ATX(); break; // *ATX (immediate)
|
||||
case 0xaf: AbsoluteRead(); A = X = Through(); break; // *LAX (absolute)
|
||||
|
||||
case 0xb2: Jam(); break; // *JAM
|
||||
case 0xb3: IndirectIndexedYRead(); A = X = Through(); break; // *LAX (indirect indexed Y)
|
||||
case 0xb7: ZeroPageYRead(); A = X = Through(); break; // *LAX (zero page, Y)
|
||||
case 0xbb: AbsoluteYAddress(); MaybeFixup(); LAS(); break; // *LAS (absolute, Y)
|
||||
case 0xbf: AbsoluteYRead(); A = X = Through(); break; // *LAX (absolute, Y)
|
||||
|
||||
case 0xc3: IndexedIndirectXRead(); DCP(); break; // *DCP (indexed indirect X)
|
||||
case 0xc7: ZeroPageRead(); DCP(); break; // *DCP (zero page)
|
||||
case 0xcb: ImmediateRead(); AXS(); break; // *AXS (immediate)
|
||||
case 0xcf: AbsoluteRead(); DCP(); break; // *DCP (absolute)
|
||||
|
||||
case 0xd2: Jam(); break; // *JAM
|
||||
case 0xd3: IndirectIndexedYAddress(); FixupRead(); DCP(); break; // *DCP (indirect indexed Y)
|
||||
case 0xd7: ZeroPageXRead(); DCP(); break; // *DCP (zero page, X)
|
||||
case 0xda: SwallowRead(); break; // *NOP (implied)
|
||||
case 0xdb: AbsoluteYAddress(); FixupRead(); DCP(); break; // *DCP (absolute, Y)
|
||||
case 0xdc: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0xdf: AbsoluteXAddress(); FixupRead(); DCP(); break; // *DCP (absolute, X)
|
||||
|
||||
case 0xe3: IndexedIndirectXRead(); ISB(); break; // *ISB (indexed indirect X)
|
||||
case 0xe7: ZeroPageRead(); ISB(); break; // *ISB (zero page)
|
||||
case 0xeb: ImmediateRead(); SBC(); break; // *SBC (immediate)
|
||||
case 0xef: AbsoluteRead(); ISB(); break; // *ISB (absolute)
|
||||
|
||||
case 0xf2: Jam(); break; // *JAM
|
||||
case 0xf3: IndirectIndexedYAddress(); FixupRead(); ISB(); break; // *ISB (indirect indexed Y)
|
||||
case 0xf7: ZeroPageXRead(); ISB(); break; // *ISB (zero page, X)
|
||||
case 0xfa: SwallowRead(); break; // *NOP (implied)
|
||||
case 0xfb: AbsoluteYAddress(); FixupRead(); ISB(); break; // *ISB (absolute, Y)
|
||||
case 0xfc: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
|
||||
case 0xff: AbsoluteXAddress(); FixupRead(); ISB(); break; // *ISB (absolute, X)
|
||||
}
|
||||
|
||||
return cycles != Cycles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bus/Memory Access
|
||||
|
||||
protected override void ModifyWrite(byte data)
|
||||
{
|
||||
// The read will have already taken place...
|
||||
MemoryWrite(); // Modify cycle
|
||||
MemoryWrite(data); // Write cycle
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Addressing modes
|
||||
|
||||
protected override void IndirectAddress()
|
||||
{
|
||||
AbsoluteAddress();
|
||||
GetAddressPaged();
|
||||
}
|
||||
|
||||
#region Address page fixup
|
||||
|
||||
protected override void Fixup()
|
||||
{
|
||||
MemoryRead();
|
||||
Bus.Address.High = FixedPage;
|
||||
}
|
||||
|
||||
protected override void FixupBranch(sbyte relative)
|
||||
{
|
||||
NoteFixedAddress(PC.Word + relative);
|
||||
MaybeFixup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instruction implementations
|
||||
|
||||
#region Undocumented instructions
|
||||
|
||||
#region Undocumented instructions with BCD effects
|
||||
|
||||
private void ARR()
|
||||
{
|
||||
var value = Bus.Data;
|
||||
if (DecimalMasked != 0)
|
||||
ARR_d(value);
|
||||
else
|
||||
ARR_b(value);
|
||||
}
|
||||
|
||||
private void ARR_d(byte value)
|
||||
{
|
||||
// With thanks to https://github.com/TomHarte/CLK
|
||||
// What a very strange instruction ARR is...
|
||||
|
||||
A &= value;
|
||||
var unshiftedA = A;
|
||||
A = Through(A >> 1 | Carry << 7);
|
||||
SetFlag(StatusBits.VF, OverflowTest((byte)(A ^ A << 1)));
|
||||
|
||||
if (LowerNibble(unshiftedA) + (unshiftedA & 0x1) > 5)
|
||||
A = (byte)(LowerNibble((byte)(A + 6)) | HigherNibble(A));
|
||||
|
||||
SetFlag(StatusBits.CF, HigherNibble(unshiftedA) + (unshiftedA & 0x10) > 0x50);
|
||||
|
||||
if (Carry != 0)
|
||||
A += 0x60;
|
||||
}
|
||||
|
||||
private void ARR_b(byte value)
|
||||
{
|
||||
A &= value;
|
||||
A = Through(A >> 1 | Carry << 7);
|
||||
SetFlag(StatusBits.CF, OverflowTest(A));
|
||||
SetFlag(StatusBits.VF, OverflowTest((byte)(A ^ A << 1)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Undocumented instructions with fixup effects
|
||||
|
||||
private void StoreFixupEffect(byte data)
|
||||
{
|
||||
////var fixedAddress = (byte)(this.Bus.Address.High + 1);
|
||||
//var fixedAddress = this.FixedPage + 1;
|
||||
//var updated = (byte)(data & fixedAddress);
|
||||
//if (this.Fixed)
|
||||
//{
|
||||
// this.Bus.Address.High = updated;
|
||||
//}
|
||||
|
||||
//this.MemoryWrite(updated);
|
||||
|
||||
byte updated;
|
||||
if (Fixed)
|
||||
{
|
||||
updated = (byte)(data & FixedPage);
|
||||
Bus.Address.High = updated;
|
||||
}
|
||||
else
|
||||
{
|
||||
updated = (byte)(data & UnfixedPage);
|
||||
Bus.Address.High = updated;
|
||||
}
|
||||
MemoryWrite(updated);
|
||||
}
|
||||
|
||||
private void SHA() => StoreFixupEffect((byte)(A & X));
|
||||
|
||||
private void SYA() => StoreFixupEffect(Y);
|
||||
|
||||
private void SXA() => StoreFixupEffect(X);
|
||||
|
||||
#endregion
|
||||
|
||||
private void ANC()
|
||||
{
|
||||
AndR();
|
||||
SetFlag(StatusBits.CF, NegativeTest(A));
|
||||
}
|
||||
|
||||
private void AXS()
|
||||
{
|
||||
X = Through(BinarySUB((byte)(A & X)));
|
||||
ResetFlag(StatusBits.CF, Intermediate.High);
|
||||
}
|
||||
|
||||
private void Jam()
|
||||
{
|
||||
Bus.Address.Assign(PC);
|
||||
MemoryRead();
|
||||
MemoryRead(0xff, 0xff);
|
||||
Bus.Address.Low = 0xfe;
|
||||
MemoryRead();
|
||||
MemoryRead();
|
||||
Bus.Address.Low = 0xff;
|
||||
MemoryRead();
|
||||
MemoryRead();
|
||||
MemoryRead();
|
||||
MemoryRead();
|
||||
MemoryRead();
|
||||
MemoryRead();
|
||||
}
|
||||
|
||||
private void TAS()
|
||||
{
|
||||
S = (byte)(A & X);
|
||||
SHA();
|
||||
}
|
||||
|
||||
private void LAS() => A = X = S = Through(MemoryRead() & S);
|
||||
|
||||
private void ANE() => A = Through((A | 0xee) & X & Bus.Data);
|
||||
|
||||
private void ATX() => A = X = Through((A | 0xee) & Bus.Data);
|
||||
|
||||
private void ASR()
|
||||
{
|
||||
AndR();
|
||||
A = LSR(A);
|
||||
}
|
||||
|
||||
private void ISB()
|
||||
{
|
||||
ModifyWrite(INC());
|
||||
SBC();
|
||||
}
|
||||
|
||||
private void RLA()
|
||||
{
|
||||
ModifyWrite(ROL());
|
||||
AndR();
|
||||
}
|
||||
|
||||
private void RRA()
|
||||
{
|
||||
ModifyWrite(ROR());
|
||||
ADC();
|
||||
}
|
||||
|
||||
private void SLO()
|
||||
{
|
||||
ModifyWrite(ASL());
|
||||
OrR();
|
||||
}
|
||||
|
||||
private void SRE()
|
||||
{
|
||||
ModifyWrite(LSR());
|
||||
EorR();
|
||||
}
|
||||
|
||||
private void DCP()
|
||||
{
|
||||
ModifyWrite(DEC());
|
||||
CMP(A);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
public sealed class ProfileEventArgs(string output) : EventArgs
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
public sealed class ProfileInstructionEventArgs(byte instruction, long cycles, long count) : EventArgs
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
public sealed class ProfileLineEventArgs(ushort address, string source, long cycles, long count, Dictionary<int, long> cycleDistributions) : CycleCountedEventArgs(cycles, count)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
public sealed class ProfileScopeEventArgs(int id, long cycles, long count) : CycleCountedEventArgs(cycles, count)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
using System.Diagnostics;
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
private readonly Dictionary<int, long>[] addressCycleDistributions = new Dictionary<int, long>[0x10000]; // Addresses -> cycles -> counts
|
||||
private readonly Dictionary<int, long> scopeCycles = []; // ID -> Cycles
|
||||
|
||||
private readonly M6502 processor;
|
||||
private readonly MOS6502 processor;
|
||||
private readonly Disassembler disassembler;
|
||||
private readonly Files.Symbols.Parser symbols;
|
||||
private readonly Symbols.Parser symbols;
|
||||
|
||||
private ushort executingAddress;
|
||||
private byte executingInstruction;
|
||||
@ -19,7 +19,7 @@
|
||||
private long totalCycles = -1;
|
||||
private bool totalCyclesValid;
|
||||
|
||||
public Profiler(M6502 processor, Disassembler disassembler, Files.Symbols.Parser symbols, bool activate)
|
||||
public Profiler(MOS6502 processor, Disassembler disassembler, Symbols.Parser symbols, bool activate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(processor);
|
||||
ArgumentNullException.ThrowIfNull(disassembler);
|
||||
@ -31,8 +31,8 @@
|
||||
|
||||
if (activate)
|
||||
{
|
||||
this.processor.RaisingSYNC += this.Processor_RaisingSYNC;
|
||||
this.processor.ExecutedInstruction += this.Processor_ExecutedInstruction;
|
||||
this.processor.RaisingSYNC += Processor_RaisingSYNC;
|
||||
this.processor.ExecutedInstruction += Processor_ExecutedInstruction;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,140 +62,140 @@
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(this.totalCyclesValid);
|
||||
return this.totalCycles;
|
||||
Debug.Assert(totalCyclesValid);
|
||||
return totalCycles;
|
||||
}
|
||||
private set
|
||||
{
|
||||
Debug.Assert(!this.totalCyclesValid);
|
||||
this.totalCycles = value;
|
||||
this.totalCyclesValid = true;
|
||||
Debug.Assert(!totalCyclesValid);
|
||||
totalCycles = value;
|
||||
totalCyclesValid = true;
|
||||
}
|
||||
}
|
||||
public void Generate()
|
||||
{
|
||||
this.OnStartingOutput();
|
||||
OnStartingOutput();
|
||||
try
|
||||
{
|
||||
this.EmitProfileInformation();
|
||||
EmitProfileInformation();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.OnFinishedOutput();
|
||||
OnFinishedOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitProfileInformation()
|
||||
{
|
||||
this.TotalCycles = this.instructionCycles.Sum();
|
||||
TotalCycles = instructionCycles.Sum();
|
||||
|
||||
this.EmitProfileLineInformation();
|
||||
this.EmitProfileScopeInformation();
|
||||
this.EmitProfileInstructionInformation();
|
||||
EmitProfileLineInformation();
|
||||
EmitProfileScopeInformation();
|
||||
EmitProfileInstructionInformation();
|
||||
}
|
||||
|
||||
private void EmitProfileScopeInformation()
|
||||
{
|
||||
this.OnStartingScopeOutput();
|
||||
OnStartingScopeOutput();
|
||||
try
|
||||
{
|
||||
foreach (var (id, cycles) in this.scopeCycles)
|
||||
foreach (var (id, cycles) in scopeCycles)
|
||||
{
|
||||
var symbol = this.symbols.LookupLabelByID(id);
|
||||
var symbol = symbols.LookupLabelByID(id);
|
||||
Debug.Assert(symbol != null);
|
||||
var available = this.ExtractCycleDistribution((ushort)symbol.Value, out var _,out var _, out var count);
|
||||
var available = ExtractCycleDistribution((ushort)symbol.Value, out var _, out var _, out var count);
|
||||
Debug.Assert(available);
|
||||
this.OnEmitScope(id, cycles, count);
|
||||
OnEmitScope(id, cycles, count);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.OnFinishedScopeOutput();
|
||||
OnFinishedScopeOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitProfileLineInformation()
|
||||
{
|
||||
this.OnStartingLineOutput();
|
||||
OnStartingLineOutput();
|
||||
try
|
||||
{
|
||||
// For each memory address
|
||||
for (var i = 0; i < 0x10000; ++i)
|
||||
{
|
||||
var address = (ushort)i;
|
||||
var available = this.ExtractCycleDistribution(address, out var cycleDistributions, out var cycles, out var count);
|
||||
var available = ExtractCycleDistribution(address, out var cycleDistributions, out var cycles, out var count);
|
||||
if (available)
|
||||
{
|
||||
// Dump a profile/disassembly line
|
||||
var source = this.disassembler.Disassemble(address);
|
||||
var source = disassembler.Disassemble(address);
|
||||
Debug.Assert(cycleDistributions != null);
|
||||
this.OnEmitLine(address, source, cycles, count, cycleDistributions);
|
||||
OnEmitLine(address, source, cycles, count, cycleDistributions);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.OnFinishedLineOutput();
|
||||
OnFinishedLineOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitProfileInstructionInformation()
|
||||
{
|
||||
this.OnStartingInstructionOutput();
|
||||
OnStartingInstructionOutput();
|
||||
try
|
||||
{
|
||||
// For each instruction
|
||||
for (var i = 0; i < 0x100; ++i)
|
||||
{
|
||||
// If there are any cycles associated
|
||||
var cycles = this.instructionCycles[i];
|
||||
var cycles = instructionCycles[i];
|
||||
if (cycles > 0)
|
||||
{
|
||||
var count = this.instructionCounts[i];
|
||||
var count = instructionCounts[i];
|
||||
Debug.Assert(count > 0);
|
||||
var instruction = (byte)i;
|
||||
|
||||
// Emit an instruction event
|
||||
this.OnEmitInstruction(instruction, cycles, count);
|
||||
OnEmitInstruction(instruction, cycles, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.OnFinishedInstructionOutput();
|
||||
OnFinishedInstructionOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private void Processor_RaisingSYNC(object? sender, EventArgs e)
|
||||
{
|
||||
this.executingAddress = this.processor.Bus.Address.Word;
|
||||
++this.instructionCounts[this.executingInstruction = this.processor.Bus.Data];
|
||||
executingAddress = processor.Bus.Address.Word;
|
||||
++instructionCounts[executingInstruction = processor.Bus.Data];
|
||||
}
|
||||
|
||||
private void Processor_ExecutedInstruction(object? sender, EventArgs e)
|
||||
{
|
||||
var cycles = this.processor.Cycles;
|
||||
var cycles = processor.Cycles;
|
||||
|
||||
{
|
||||
var addressDistribution = this.addressCycleDistributions[this.executingAddress];
|
||||
var addressDistribution = addressCycleDistributions[executingAddress];
|
||||
if (addressDistribution == null)
|
||||
{
|
||||
this.addressCycleDistributions[this.executingAddress] = addressDistribution = [];
|
||||
addressCycleDistributions[executingAddress] = addressDistribution = [];
|
||||
}
|
||||
_ = addressDistribution.TryGetValue(cycles, out var current);
|
||||
addressDistribution[cycles] = ++current;
|
||||
}
|
||||
|
||||
this.instructionCycles[this.executingInstruction] += cycles;
|
||||
instructionCycles[executingInstruction] += cycles;
|
||||
|
||||
{
|
||||
var scope = this.symbols.LookupScopeByAddress(this.executingAddress);
|
||||
var scope = symbols.LookupScopeByAddress(executingAddress);
|
||||
if (scope != null)
|
||||
{
|
||||
var id = scope.ID;
|
||||
// Current will be initialised to zero, if absent
|
||||
_ = this.scopeCycles.TryGetValue(id, out var current);
|
||||
this.scopeCycles[id] = current + cycles;
|
||||
_ = scopeCycles.TryGetValue(id, out var current);
|
||||
scopeCycles[id] = current + cycles;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,7 +216,7 @@
|
||||
foreach (var (cycle, count) in cycleDistribution)
|
||||
{
|
||||
hitCount += count;
|
||||
cycleCount += (cycle * count);
|
||||
cycleCount += cycle * count;
|
||||
}
|
||||
|
||||
Debug.Assert(hitCount > 0);
|
||||
@ -227,57 +227,57 @@
|
||||
|
||||
private void OnStartingOutput()
|
||||
{
|
||||
this.StartingOutput?.Invoke(this, EventArgs.Empty);
|
||||
StartingOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnFinishedOutput()
|
||||
{
|
||||
this.FinishedOutput?.Invoke(this, EventArgs.Empty);
|
||||
FinishedOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnStartingLineOutput()
|
||||
{
|
||||
this.StartingLineOutput?.Invoke(this, EventArgs.Empty);
|
||||
StartingLineOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnFinishedLineOutput()
|
||||
{
|
||||
this.FinishedLineOutput?.Invoke(this, EventArgs.Empty);
|
||||
FinishedLineOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnStartingInstructionOutput()
|
||||
{
|
||||
this.StartingInstructionOutput?.Invoke(this, EventArgs.Empty);
|
||||
StartingInstructionOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnFinishedInstructionOutput()
|
||||
{
|
||||
this.FinishedInstructionOutput?.Invoke(this, EventArgs.Empty);
|
||||
FinishedInstructionOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnStartingScopeOutput()
|
||||
{
|
||||
this.StartingScopeOutput?.Invoke(this, EventArgs.Empty);
|
||||
StartingScopeOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnFinishedScopeOutput()
|
||||
{
|
||||
this.FinishedScopeOutput?.Invoke(this, EventArgs.Empty);
|
||||
FinishedScopeOutput?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnEmitLine(ushort address, string source, long cycles, long count, Dictionary<int, long> cycleDistributions)
|
||||
{
|
||||
this.EmitLine?.Invoke(this, new ProfileLineEventArgs(address, source, cycles, count, cycleDistributions));
|
||||
EmitLine?.Invoke(this, new ProfileLineEventArgs(address, source, cycles, count, cycleDistributions));
|
||||
}
|
||||
|
||||
private void OnEmitScope(int id, long cycles, long count)
|
||||
{
|
||||
this.EmitScope?.Invoke(this, new ProfileScopeEventArgs(id, cycles, count));
|
||||
EmitScope?.Invoke(this, new ProfileScopeEventArgs(id, cycles, count));
|
||||
}
|
||||
|
||||
private void OnEmitInstruction(byte instruction, long cycles, long count)
|
||||
{
|
||||
this.EmitInstruction?.Invoke(this, new ProfileInstructionEventArgs(instruction, cycles, count));
|
||||
EmitInstruction?.Invoke(this, new ProfileInstructionEventArgs(instruction, cycles, count));
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,10 @@
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace EightBit
|
||||
namespace M6502
|
||||
{
|
||||
using EightBit;
|
||||
|
||||
[Flags]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "Must be castable to byte")]
|
||||
public enum StatusBits : byte
|
||||
|
303
M6502/W65C02.cs
303
M6502/W65C02.cs
@ -1,303 +0,0 @@
|
||||
// <copyright file="W65C02.cs" company="Adrian Conlon">
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace EightBit
|
||||
{
|
||||
public class W65C02(Bus bus) : M6502Core(bus)
|
||||
{
|
||||
private bool _stopped;
|
||||
private bool _waiting;
|
||||
|
||||
private bool Stopped
|
||||
{
|
||||
get => this._stopped; set => this._stopped = value;
|
||||
}
|
||||
|
||||
private bool Waiting
|
||||
{
|
||||
get => this._waiting; set => this._waiting = value;
|
||||
}
|
||||
|
||||
private bool Paused => this.Stopped || this.Waiting;
|
||||
|
||||
#region Interrupts
|
||||
|
||||
protected override void Interrupt(byte vector, InterruptSource source, InterruptType type)
|
||||
{
|
||||
base.Interrupt(vector, source, type);
|
||||
this.ResetFlag(StatusBits.DF); // Disable decimal mode (Change from M6502)
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Core instruction dispatching
|
||||
|
||||
protected override bool MaybeExecute()
|
||||
{
|
||||
if (base.MaybeExecute())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var cycles = this.Cycles;
|
||||
switch (this.OpCode)
|
||||
{
|
||||
case 0x02: this.SwallowFetch(); break; // NOP
|
||||
case 0x03: break; // null
|
||||
case 0x04: this.ZeroPageRead(); this.TSB(); break; // TSB zp
|
||||
case 0x07: this.ZeroPageRead(); this.RMB(Chip.Bit(0)); break; // RMB0 zp
|
||||
case 0x0b: break; // null
|
||||
case 0x0c: this.AbsoluteRead(); this.TSB(); break; // TSB a
|
||||
case 0x0f: this.ZeroPageRead(); this.BBR(Chip.Bit(0)); break; // BBR0 r
|
||||
|
||||
case 0x12: this.ZeroPageIndirectAddress(); this.OrR(); break; // ORA (zp),y
|
||||
case 0x13: break; // null
|
||||
case 0x14: this.ZeroPageRead(); this.TRB(); break; // TRB zp
|
||||
case 0x17: this.ZeroPageRead(); this.RMB(Chip.Bit(1)); break; // RMB1 zp
|
||||
case 0x1a: this.SwallowRead(); this.A = this.INC(this.A); break; // INC A
|
||||
case 0x1b: break; // null
|
||||
case 0x1c: this.AbsoluteRead(); this.TRB(); break; // TRB a
|
||||
case 0x1f: this.ZeroPageRead(); this.BBR(Chip.Bit(1)); break; // BBR1 r
|
||||
|
||||
case 0x22: this.SwallowFetch(); break; // NOP
|
||||
case 0x23: break; // null
|
||||
case 0x27: this.ZeroPageRead(); this.RMB(Chip.Bit(2)); break; // RMB2 zp
|
||||
case 0x2b: break; // null
|
||||
case 0x2f: this.ZeroPageRead(); this.BBR(Chip.Bit(2)); break; // BBR2 r
|
||||
|
||||
case 0x32: this.ZeroPageIndirectRead(); this.AndR(); break; // AND (zp)
|
||||
case 0x33: break; // null
|
||||
case 0x34: break; // BIT zp,x
|
||||
case 0x37: this.ZeroPageRead(); this.RMB(Chip.Bit(3)); break; // RMB3 zp
|
||||
case 0x3a: this.SwallowRead(); this.A = this.DEC(this.A); break; // DEC A
|
||||
case 0x3b: break; // null
|
||||
case 0x3c: break; // BIT a,x
|
||||
case 0x3f: this.ZeroPageRead(); this.BBR(Chip.Bit(3)); break; // BBR3 r
|
||||
|
||||
case 0x42: this.SwallowFetch(); break; // NOP
|
||||
case 0x43: break; // null
|
||||
case 0x47: this.ZeroPageRead(); this.RMB(Chip.Bit(4)); break; // RMB4 zp
|
||||
case 0x4b: break; // null
|
||||
case 0x4f: this.ZeroPageRead(); this.BBR(Chip.Bit(4)); break; // BBR4 r
|
||||
|
||||
case 0x52: this.ZeroPageIndirectRead(); this.EorR(); break; // EOR (zp)
|
||||
case 0x53: break; // null
|
||||
case 0x57: this.ZeroPageRead(); this.RMB(Chip.Bit(5)); break; // RMB5 zp
|
||||
case 0x5a: this.SwallowRead(); this.Push(this.Y); break; // PHY s
|
||||
case 0x5b: break; // null
|
||||
case 0x5c: break; // null
|
||||
case 0x5f: this.ZeroPageRead(); this.BBR(Chip.Bit(5)); break; // BBR5 r
|
||||
|
||||
case 0x62: this.SwallowFetch(); break; // *NOP
|
||||
case 0x63: break; // null
|
||||
case 0x64: this.ZeroPageAddress(); this.MemoryWrite(0); break; // STZ zp
|
||||
case 0x67: this.ZeroPageRead(); this.RMB(Chip.Bit(6)); break; // RMB6 zp
|
||||
case 0x6b: break; // null
|
||||
case 0x6f: this.ZeroPageRead(); this.BBR(Chip.Bit(6)); break; // BBR6 r
|
||||
|
||||
case 0x72: this.ZeroPageIndirectRead(); this.ADC(); break; // ADC (zp)
|
||||
case 0x73: break; // null
|
||||
case 0x74: this.ZeroPageXAddress(); this.MemoryWrite(0); break; // STZ zp,x
|
||||
case 0x77: this.ZeroPageRead(); this.RMB(Chip.Bit(7)); break; // RMB7 zp
|
||||
case 0x7a: this.SwallowRead(); this.SwallowPop(); this.Y = this.Through(this.Pop()); break; // PLY s
|
||||
case 0x7b: break; // null
|
||||
case 0x7c: break; // JMP (a,x)
|
||||
case 0x7f: this.ZeroPageRead(); this.BBR(Chip.Bit(7)); break; // BBR7 r
|
||||
|
||||
case 0x80: Branch(true); break; // BRA r
|
||||
case 0x83: break; // null
|
||||
case 0x87: this.ZeroPageRead(); this.SMB(Chip.Bit(0)); break; // SMB0 zp
|
||||
case 0x89: break; // BIT # (TBC)
|
||||
case 0x8b: break; // null
|
||||
case 0x8f: this.ZeroPageRead(); this.BBS(Chip.Bit(0)); break; // BBS0 r
|
||||
|
||||
case 0x92: ZeroPageIndirectAddress(); this.MemoryWrite(this.A); break; // STA (zp)
|
||||
case 0x93: break; // null
|
||||
case 0x97: this.ZeroPageRead(); this.SMB(Chip.Bit(1)); break; // SMB1 zp
|
||||
case 0x9b: break; // null
|
||||
case 0x9c: this.AbsoluteAddress(); this.MemoryWrite(0); break; // STZ a
|
||||
case 0x9e: this.AbsoluteXAddress(); this.MemoryWrite(0); break; // STZ a,x
|
||||
case 0x9f: this.ZeroPageRead(); this.BBS(Chip.Bit(1)); break; // BBS1 r
|
||||
|
||||
case 0xa3: break; // null
|
||||
case 0xa7: this.ZeroPageRead(); this.SMB(Chip.Bit(2)); break; // SMB2 zp
|
||||
case 0xab: break; // null
|
||||
case 0xaf: this.ZeroPageRead(); this.BBS(Chip.Bit(2)); break; // BBS2 r
|
||||
|
||||
case 0xb2: ZeroPageIndirectRead(); this.A = this.Through(); break; // LDA (zp)
|
||||
case 0xb3: break; // null
|
||||
case 0xb7: this.ZeroPageRead(); this.SMB(Chip.Bit(3)); break; // SMB3 zp
|
||||
case 0xbb: break; // null
|
||||
case 0xbf: this.ZeroPageRead(); this.BBS(Chip.Bit(3)); break; // BBS3 r
|
||||
|
||||
case 0xc3: break; // null
|
||||
case 0xc7: this.ZeroPageRead(); this.SMB(Chip.Bit(4)); break; // SMB4 zp
|
||||
case 0xcb: this.SwallowRead(); this.Waiting = true; break; // WAI i
|
||||
case 0xcf: this.ZeroPageRead(); this.BBS(Chip.Bit(4)); break; // BBS4 r
|
||||
|
||||
case 0xd2: this.ZeroPageIndirectRead(); this.CMP(this.A); break; // CMP (zp)
|
||||
case 0xd3: break; // null
|
||||
case 0xd7: this.ZeroPageRead(); this.SMB(Chip.Bit(5)); break; // SMB5 zp
|
||||
case 0xda: this.SwallowRead(); this.Push(this.X); break; // PHX s
|
||||
case 0xdb: this.SwallowRead(); this.Stopped = true; break; // STP i
|
||||
case 0xdc: this.SwallowRead(); break; // null
|
||||
case 0xdf: this.ZeroPageRead(); this.BBS(Chip.Bit(5)); break; // BBS5 r
|
||||
|
||||
case 0xe3: break; // null
|
||||
case 0xe7: this.ZeroPageRead(); this.SMB(Chip.Bit(6)); break; // SMB6 zp
|
||||
case 0xeb: break; // null
|
||||
case 0xef: this.ZeroPageRead(); this.BBS(Chip.Bit(6)); break; // BBS6 r
|
||||
|
||||
case 0xf2: this.ZeroPageIndirectRead(); this.SBC(); break; // SBC (zp)
|
||||
case 0xf3: break; // null
|
||||
case 0xf7: this.ZeroPageRead(); this.SMB(Chip.Bit(7)); break; // SMB7 zp
|
||||
case 0xfa: this.SwallowRead(); this.SwallowPop(); this.X = this.Through(this.Pop()); break; // PLX s
|
||||
case 0xfb: break; // null
|
||||
case 0xfc: break; // null
|
||||
case 0xff: this.ZeroPageRead(); this.BBS(Chip.Bit(7)); break; // BBS7 r
|
||||
}
|
||||
|
||||
return cycles != this.Cycles;
|
||||
}
|
||||
|
||||
public override void PoweredStep()
|
||||
{
|
||||
if (!this.Paused)
|
||||
{
|
||||
base.PoweredStep();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLoweredRESET()
|
||||
{
|
||||
base.OnLoweredRESET();
|
||||
this.Stopped = this.Waiting = false;
|
||||
}
|
||||
|
||||
protected override void OnLoweredINT()
|
||||
{
|
||||
base.OnLoweredINT();
|
||||
this.Waiting = false;
|
||||
}
|
||||
|
||||
protected override void OnLoweredNMI()
|
||||
{
|
||||
base.OnLoweredNMI();
|
||||
this.Waiting = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bus/Memory Access
|
||||
|
||||
protected override void ModifyWrite(byte data)
|
||||
{
|
||||
// The read will have already taken place...
|
||||
this.MemoryRead(); // Modify cycle (Change from M6502)
|
||||
this.MemoryWrite(data); // Write cycle
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Addressing modes
|
||||
|
||||
#region Address page fixup
|
||||
|
||||
private readonly Register16 lastFetchAddress = new();
|
||||
|
||||
protected override byte FetchByte()
|
||||
{
|
||||
this.lastFetchAddress.Assign(this.PC);
|
||||
return base.FetchByte();
|
||||
}
|
||||
|
||||
protected override void Fixup()
|
||||
{
|
||||
var fixingLow = this.Bus.Address.Low;
|
||||
this.MemoryRead(this.lastFetchAddress);
|
||||
this.Bus.Address.Assign(fixingLow, this.FixedPage);
|
||||
}
|
||||
|
||||
protected override void FixupBranch(sbyte relative)
|
||||
{
|
||||
this.NoteFixedAddress(this.PC.Word + relative);
|
||||
this.lastFetchAddress.Assign(this.Bus.Address); // Effectively negate the use of "lastFetchAddress" for branch fixup usages
|
||||
this.MaybeFixup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Address resolution
|
||||
|
||||
protected void GetAddress()
|
||||
{
|
||||
this.GetWordPaged();
|
||||
|
||||
if (this.Bus.Address.Low == 0)
|
||||
{
|
||||
this.Bus.Address.High++;
|
||||
}
|
||||
|
||||
this.Bus.Address.Assign(this.Intermediate.Low, this.MemoryRead());
|
||||
}
|
||||
|
||||
protected override void IndirectAddress()
|
||||
{
|
||||
this.AbsoluteAddress();
|
||||
this.GetAddress();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Address and read
|
||||
|
||||
private void ZeroPageIndirectRead()
|
||||
{
|
||||
this.ZeroPageIndirectAddress();
|
||||
this.MemoryRead();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private void RMB(byte flag)
|
||||
{
|
||||
this.MemoryRead();
|
||||
this.Bus.Data &= (byte)~flag;
|
||||
this.MemoryWrite();
|
||||
}
|
||||
|
||||
private void SMB(byte flag)
|
||||
{
|
||||
this.MemoryRead();
|
||||
this.Bus.Data |= flag;
|
||||
this.MemoryWrite();
|
||||
}
|
||||
|
||||
private void BBS(byte flag)
|
||||
{
|
||||
this.MemoryRead();
|
||||
this.Branch(this.Bus.Data & flag);
|
||||
}
|
||||
|
||||
private void BBR(byte flag)
|
||||
{
|
||||
this.MemoryRead();
|
||||
this.BranchNot(this.Bus.Data & flag);
|
||||
}
|
||||
|
||||
private void TSB()
|
||||
{
|
||||
this.AdjustZero((byte)(this.A & this.Bus.Data));
|
||||
this.ModifyWrite((byte)(this.A | this.Bus.Data));
|
||||
}
|
||||
|
||||
private void TRB()
|
||||
{
|
||||
this.AdjustZero((byte)(this.A & this.Bus.Data));
|
||||
this.ModifyWrite((byte)(~this.A & this.Bus.Data));
|
||||
}
|
||||
}
|
||||
}
|
305
M6502/WDC65C02.cs
Normal file
305
M6502/WDC65C02.cs
Normal file
@ -0,0 +1,305 @@
|
||||
// <copyright file="WDC65C02.cs" company="Adrian Conlon">
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace M6502
|
||||
{
|
||||
using EightBit;
|
||||
|
||||
public class WDC65C02(Bus bus) : Core(bus)
|
||||
{
|
||||
private bool _stopped;
|
||||
private bool _waiting;
|
||||
|
||||
private bool Stopped
|
||||
{
|
||||
get => _stopped; set => _stopped = value;
|
||||
}
|
||||
|
||||
private bool Waiting
|
||||
{
|
||||
get => _waiting; set => _waiting = value;
|
||||
}
|
||||
|
||||
private bool Paused => Stopped || Waiting;
|
||||
|
||||
#region Interrupts
|
||||
|
||||
protected override void Interrupt(byte vector, InterruptSource source, InterruptType type)
|
||||
{
|
||||
base.Interrupt(vector, source, type);
|
||||
ResetFlag(StatusBits.DF); // Disable decimal mode (Change from MOS6502)
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Core instruction dispatching
|
||||
|
||||
protected override bool MaybeExecute()
|
||||
{
|
||||
if (base.MaybeExecute())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var cycles = Cycles;
|
||||
switch (OpCode)
|
||||
{
|
||||
case 0x02: SwallowFetch(); break; // NOP
|
||||
case 0x03: break; // null
|
||||
case 0x04: ZeroPageRead(); TSB(); break; // TSB zp
|
||||
case 0x07: ZeroPageRead(); RMB(Bit(0)); break; // RMB0 zp
|
||||
case 0x0b: break; // null
|
||||
case 0x0c: AbsoluteRead(); TSB(); break; // TSB a
|
||||
case 0x0f: ZeroPageRead(); BBR(Bit(0)); break; // BBR0 r
|
||||
|
||||
case 0x12: ZeroPageIndirectAddress(); OrR(); break; // ORA (zp),y
|
||||
case 0x13: break; // null
|
||||
case 0x14: ZeroPageRead(); TRB(); break; // TRB zp
|
||||
case 0x17: ZeroPageRead(); RMB(Bit(1)); break; // RMB1 zp
|
||||
case 0x1a: SwallowRead(); A = INC(A); break; // INC A
|
||||
case 0x1b: break; // null
|
||||
case 0x1c: AbsoluteRead(); TRB(); break; // TRB a
|
||||
case 0x1f: ZeroPageRead(); BBR(Bit(1)); break; // BBR1 r
|
||||
|
||||
case 0x22: SwallowFetch(); break; // NOP
|
||||
case 0x23: break; // null
|
||||
case 0x27: ZeroPageRead(); RMB(Bit(2)); break; // RMB2 zp
|
||||
case 0x2b: break; // null
|
||||
case 0x2f: ZeroPageRead(); BBR(Bit(2)); break; // BBR2 r
|
||||
|
||||
case 0x32: ZeroPageIndirectRead(); AndR(); break; // AND (zp)
|
||||
case 0x33: break; // null
|
||||
case 0x34: break; // BIT zp,x
|
||||
case 0x37: ZeroPageRead(); RMB(Bit(3)); break; // RMB3 zp
|
||||
case 0x3a: SwallowRead(); A = DEC(A); break; // DEC A
|
||||
case 0x3b: break; // null
|
||||
case 0x3c: break; // BIT a,x
|
||||
case 0x3f: ZeroPageRead(); BBR(Bit(3)); break; // BBR3 r
|
||||
|
||||
case 0x42: SwallowFetch(); break; // NOP
|
||||
case 0x43: break; // null
|
||||
case 0x47: ZeroPageRead(); RMB(Bit(4)); break; // RMB4 zp
|
||||
case 0x4b: break; // null
|
||||
case 0x4f: ZeroPageRead(); BBR(Bit(4)); break; // BBR4 r
|
||||
|
||||
case 0x52: ZeroPageIndirectRead(); EorR(); break; // EOR (zp)
|
||||
case 0x53: break; // null
|
||||
case 0x57: ZeroPageRead(); RMB(Bit(5)); break; // RMB5 zp
|
||||
case 0x5a: SwallowRead(); Push(Y); break; // PHY s
|
||||
case 0x5b: break; // null
|
||||
case 0x5c: break; // null
|
||||
case 0x5f: ZeroPageRead(); BBR(Bit(5)); break; // BBR5 r
|
||||
|
||||
case 0x62: SwallowFetch(); break; // *NOP
|
||||
case 0x63: break; // null
|
||||
case 0x64: ZeroPageAddress(); MemoryWrite(0); break; // STZ zp
|
||||
case 0x67: ZeroPageRead(); RMB(Bit(6)); break; // RMB6 zp
|
||||
case 0x6b: break; // null
|
||||
case 0x6f: ZeroPageRead(); BBR(Bit(6)); break; // BBR6 r
|
||||
|
||||
case 0x72: ZeroPageIndirectRead(); ADC(); break; // ADC (zp)
|
||||
case 0x73: break; // null
|
||||
case 0x74: ZeroPageXAddress(); MemoryWrite(0); break; // STZ zp,x
|
||||
case 0x77: ZeroPageRead(); RMB(Bit(7)); break; // RMB7 zp
|
||||
case 0x7a: SwallowRead(); SwallowPop(); Y = Through(Pop()); break; // PLY s
|
||||
case 0x7b: break; // null
|
||||
case 0x7c: break; // JMP (a,x)
|
||||
case 0x7f: ZeroPageRead(); BBR(Bit(7)); break; // BBR7 r
|
||||
|
||||
case 0x80: Branch(true); break; // BRA r
|
||||
case 0x83: break; // null
|
||||
case 0x87: ZeroPageRead(); SMB(Bit(0)); break; // SMB0 zp
|
||||
case 0x89: break; // BIT # (TBC)
|
||||
case 0x8b: break; // null
|
||||
case 0x8f: ZeroPageRead(); BBS(Bit(0)); break; // BBS0 r
|
||||
|
||||
case 0x92: ZeroPageIndirectAddress(); MemoryWrite(A); break; // STA (zp)
|
||||
case 0x93: break; // null
|
||||
case 0x97: ZeroPageRead(); SMB(Bit(1)); break; // SMB1 zp
|
||||
case 0x9b: break; // null
|
||||
case 0x9c: AbsoluteAddress(); MemoryWrite(0); break; // STZ a
|
||||
case 0x9e: AbsoluteXAddress(); MemoryWrite(0); break; // STZ a,x
|
||||
case 0x9f: ZeroPageRead(); BBS(Bit(1)); break; // BBS1 r
|
||||
|
||||
case 0xa3: break; // null
|
||||
case 0xa7: ZeroPageRead(); SMB(Bit(2)); break; // SMB2 zp
|
||||
case 0xab: break; // null
|
||||
case 0xaf: ZeroPageRead(); BBS(Bit(2)); break; // BBS2 r
|
||||
|
||||
case 0xb2: ZeroPageIndirectRead(); A = Through(); break; // LDA (zp)
|
||||
case 0xb3: break; // null
|
||||
case 0xb7: ZeroPageRead(); SMB(Bit(3)); break; // SMB3 zp
|
||||
case 0xbb: break; // null
|
||||
case 0xbf: ZeroPageRead(); BBS(Bit(3)); break; // BBS3 r
|
||||
|
||||
case 0xc3: break; // null
|
||||
case 0xc7: ZeroPageRead(); SMB(Bit(4)); break; // SMB4 zp
|
||||
case 0xcb: SwallowRead(); Waiting = true; break; // WAI i
|
||||
case 0xcf: ZeroPageRead(); BBS(Bit(4)); break; // BBS4 r
|
||||
|
||||
case 0xd2: ZeroPageIndirectRead(); CMP(A); break; // CMP (zp)
|
||||
case 0xd3: break; // null
|
||||
case 0xd7: ZeroPageRead(); SMB(Bit(5)); break; // SMB5 zp
|
||||
case 0xda: SwallowRead(); Push(X); break; // PHX s
|
||||
case 0xdb: SwallowRead(); Stopped = true; break; // STP i
|
||||
case 0xdc: SwallowRead(); break; // null
|
||||
case 0xdf: ZeroPageRead(); BBS(Bit(5)); break; // BBS5 r
|
||||
|
||||
case 0xe3: break; // null
|
||||
case 0xe7: ZeroPageRead(); SMB(Bit(6)); break; // SMB6 zp
|
||||
case 0xeb: break; // null
|
||||
case 0xef: ZeroPageRead(); BBS(Bit(6)); break; // BBS6 r
|
||||
|
||||
case 0xf2: ZeroPageIndirectRead(); SBC(); break; // SBC (zp)
|
||||
case 0xf3: break; // null
|
||||
case 0xf7: ZeroPageRead(); SMB(Bit(7)); break; // SMB7 zp
|
||||
case 0xfa: SwallowRead(); SwallowPop(); X = Through(Pop()); break; // PLX s
|
||||
case 0xfb: break; // null
|
||||
case 0xfc: break; // null
|
||||
case 0xff: ZeroPageRead(); BBS(Bit(7)); break; // BBS7 r
|
||||
}
|
||||
|
||||
return cycles != Cycles;
|
||||
}
|
||||
|
||||
public override void PoweredStep()
|
||||
{
|
||||
if (!Paused)
|
||||
{
|
||||
base.PoweredStep();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLoweredRESET()
|
||||
{
|
||||
base.OnLoweredRESET();
|
||||
Stopped = Waiting = false;
|
||||
}
|
||||
|
||||
protected override void OnLoweredINT()
|
||||
{
|
||||
base.OnLoweredINT();
|
||||
Waiting = false;
|
||||
}
|
||||
|
||||
protected override void OnLoweredNMI()
|
||||
{
|
||||
base.OnLoweredNMI();
|
||||
Waiting = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bus/Memory Access
|
||||
|
||||
protected override void ModifyWrite(byte data)
|
||||
{
|
||||
// The read will have already taken place...
|
||||
MemoryRead(); // Modify cycle (Change from MOS6502)
|
||||
MemoryWrite(data); // Write cycle
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Addressing modes
|
||||
|
||||
#region Address page fixup
|
||||
|
||||
private readonly Register16 lastFetchAddress = new();
|
||||
|
||||
protected override byte FetchByte()
|
||||
{
|
||||
lastFetchAddress.Assign(PC);
|
||||
return base.FetchByte();
|
||||
}
|
||||
|
||||
protected override void Fixup()
|
||||
{
|
||||
var fixingLow = Bus.Address.Low;
|
||||
MemoryRead(lastFetchAddress);
|
||||
Bus.Address.Assign(fixingLow, FixedPage);
|
||||
}
|
||||
|
||||
protected override void FixupBranch(sbyte relative)
|
||||
{
|
||||
NoteFixedAddress(PC.Word + relative);
|
||||
lastFetchAddress.Assign(Bus.Address); // Effectively negate the use of "lastFetchAddress" for branch fixup usages
|
||||
MaybeFixup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Address resolution
|
||||
|
||||
protected void GetAddress()
|
||||
{
|
||||
GetWordPaged();
|
||||
|
||||
if (Bus.Address.Low == 0)
|
||||
{
|
||||
Bus.Address.High++;
|
||||
}
|
||||
|
||||
Bus.Address.Assign(Intermediate.Low, MemoryRead());
|
||||
}
|
||||
|
||||
protected override void IndirectAddress()
|
||||
{
|
||||
AbsoluteAddress();
|
||||
GetAddress();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Address and read
|
||||
|
||||
private void ZeroPageIndirectRead()
|
||||
{
|
||||
ZeroPageIndirectAddress();
|
||||
MemoryRead();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private void RMB(byte flag)
|
||||
{
|
||||
MemoryRead();
|
||||
Bus.Data &= (byte)~flag;
|
||||
MemoryWrite();
|
||||
}
|
||||
|
||||
private void SMB(byte flag)
|
||||
{
|
||||
MemoryRead();
|
||||
Bus.Data |= flag;
|
||||
MemoryWrite();
|
||||
}
|
||||
|
||||
private void BBS(byte flag)
|
||||
{
|
||||
MemoryRead();
|
||||
Branch(Bus.Data & flag);
|
||||
}
|
||||
|
||||
private void BBR(byte flag)
|
||||
{
|
||||
MemoryRead();
|
||||
BranchNot(Bus.Data & flag);
|
||||
}
|
||||
|
||||
private void TSB()
|
||||
{
|
||||
AdjustZero((byte)(A & Bus.Data));
|
||||
ModifyWrite((byte)(A | Bus.Data));
|
||||
}
|
||||
|
||||
private void TRB()
|
||||
{
|
||||
AdjustZero((byte)(A & Bus.Data));
|
||||
ModifyWrite((byte)(~A & Bus.Data));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user