Push more core processor handling into base classes.

This commit is contained in:
Adrian Conlon
2025-05-04 10:53:23 +01:00
parent 47374e591d
commit 2336222c97
8 changed files with 63 additions and 73 deletions

View File

@@ -84,6 +84,10 @@ namespace EightBit
}
}
protected abstract void DisableInterrupts();
protected abstract void EnableInterrupts();
protected override void IncrementPC()
{
if (this.HALT.Raised())
@@ -120,6 +124,7 @@ namespace EightBit
protected override void HandleRESET()
{
base.HandleRESET();
this.DisableInterrupts();
this.Jump(0);
}

View File

@@ -224,10 +224,15 @@ namespace EightBit
protected virtual void DecrementPC() => --this.PC.Word;
protected virtual byte FetchByte()
protected virtual void ImmediateAddress()
{
this.Bus.Address.Assign(this.PC);
IncrementPC();
this.IncrementPC();
}
protected virtual byte FetchByte()
{
this.ImmediateAddress();
return this.MemoryRead();
}

View File

@@ -7,17 +7,11 @@ namespace Intel8080
{
using EightBit;
public class Intel8080 : IntelProcessor
public class Intel8080(Bus bus, InputOutput ports) : IntelProcessor(bus)
{
public Intel8080(Bus bus, InputOutput ports)
: base(bus)
{
this.ports = ports;
}
private readonly Register16 af = new();
private readonly InputOutput ports;
private readonly InputOutput ports = ports;
private bool interruptEnable;
@@ -62,6 +56,7 @@ namespace Intel8080
}
else if (this.HALT.Lowered())
{
_ = this.FetchByte();
this.Execute(0); // NOP
}
else
@@ -73,7 +68,6 @@ namespace Intel8080
protected override void HandleRESET()
{
base.HandleRESET();
this.DisableInterrupts();
this.Tick(3);
}
@@ -172,9 +166,9 @@ namespace Intel8080
private static byte AdjustAuxiliaryCarrySub(byte input, byte before, byte value, int calculation) => ClearBit(input, StatusBits.AC, CalculateHalfCarrySub(before, value, calculation));
private void DisableInterrupts() => this.interruptEnable = false;
protected override void DisableInterrupts() => this.interruptEnable = false;
private void EnableInterrupts() => this.interruptEnable = true;
protected override void EnableInterrupts() => this.interruptEnable = true;
private byte R(int r) => r switch
{
@@ -489,7 +483,7 @@ namespace Intel8080
this.Tick(10);
break;
case 2: // JP HL
this.Jump(this.HL.Word);
this.Jump(this.HL);
this.Tick(4);
break;
case 3: // LD SP,HL

View File

@@ -238,7 +238,6 @@ namespace LR35902
protected override void HandleRESET()
{
base.HandleRESET();
this.DI();
this.SP.Word = (ushort)(Mask.Sixteen - 1);
this.TickMachine(4);
}
@@ -247,7 +246,7 @@ namespace LR35902
{
base.HandleINT();
this.RaiseHALT();
this.DI();
this.DisableInterrupts();
this.Restart(this.Bus.Data);
}
@@ -387,9 +386,9 @@ namespace LR35902
private static byte Set(int n, byte operand) => SetBit(operand, Bit(n));
private void DI() => this.IME = false;
protected override void DisableInterrupts() => this.IME = false;
private void EI() => this.IME = true;
protected override void EnableInterrupts() => this.IME = true;
private void Stop() => this.Stopped = true;
@@ -834,10 +833,10 @@ namespace LR35902
this.Execute(this.FetchByte());
break;
case 6: // DI
this.DI();
this.DisableInterrupts();
break;
case 7: // EI
this.EI();
this.EnableInterrupts();
break;
default:
break;
@@ -1147,7 +1146,7 @@ namespace LR35902
private void RetI()
{
this.Return();
this.EI();
this.EnableInterrupts();
}
}
}

View File

@@ -330,7 +330,7 @@ namespace M6502
case 0x05: this.ZeroPageRead(); this.OrR(); break; // ORA (zero page)
case 0x06: this.ZeroPageRead(); this.ModifyWrite(this.ASL()); break; // ASL (zero page)
case 0x08: this.SwallowRead(); this.PHP(); break; // PHP (implied)
case 0x09: this.ImmediateRead(); this.OrR(); break; // ORA (immediate)
case 0x09: this.FetchByte(); this.OrR(); break; // ORA (immediate)
case 0x0a: this.SwallowRead(); this.A = this.ASL(this.A); break; // ASL A (implied)
case 0x0d: this.AbsoluteRead(); this.OrR(); break; // ORA (absolute)
case 0x0e: this.AbsoluteRead(); this.ModifyWrite(this.ASL()); break; // ASL (absolute)
@@ -350,7 +350,7 @@ namespace M6502
case 0x25: this.ZeroPageRead(); this.AndR(); break; // AND (zero page)
case 0x26: this.ZeroPageRead(); this.ModifyWrite(this.ROL()); break; // ROL (zero page)
case 0x28: this.SwallowRead(); this.PLP(); break; // PLP (implied)
case 0x29: this.ImmediateRead(); this.AndR(); break; // AND (immediate)
case 0x29: this.FetchByte(); this.AndR(); break; // AND (immediate)
case 0x2a: this.SwallowRead(); this.A = this.ROL(this.A); break; // ROL A (implied)
case 0x2c: this.AbsoluteRead(); this.BIT(); break; // BIT (absolute)
case 0x2d: this.AbsoluteRead(); this.AndR(); break; // AND (absolute)
@@ -371,7 +371,7 @@ namespace M6502
case 0x45: this.ZeroPageRead(); this.EorR(); break; // EOR (zero page)
case 0x46: this.ZeroPageRead(); this.ModifyWrite(this.LSR()); break; // LSR (zero page)
case 0x48: this.SwallowRead(); this.Push(this.A); break; // PHA (implied)
case 0x49: this.ImmediateRead(); this.EorR(); break; // EOR (immediate)
case 0x49: this.FetchByte(); this.EorR(); break; // EOR (immediate)
case 0x4a: this.SwallowRead(); this.A = this.LSR(this.A); break; // LSR A (implied)
case 0x4c: this.AbsoluteAddress(); this.Jump(this.Bus.Address); break; // JMP (absolute)
case 0x4d: this.AbsoluteRead(); this.EorR(); break; // EOR (absolute)
@@ -392,7 +392,7 @@ namespace M6502
case 0x65: this.ZeroPageRead(); this.ADC(); break; // ADC (zero page)
case 0x66: this.ZeroPageRead(); this.ModifyWrite(this.ROR()); break; // ROR (zero page)
case 0x68: this.SwallowRead(); this.SwallowPop(); this.A = this.Through(this.Pop()); break; // PLA (implied)
case 0x69: this.ImmediateRead(); this.ADC(); break; // ADC (immediate)
case 0x69: this.FetchByte(); this.ADC(); break; // ADC (immediate)
case 0x6a: this.SwallowRead(); this.A = this.ROR(this.A); break; // ROR A (implied)
case 0x6c: this.IndirectAddress(); this.Jump(this.Bus.Address); break; // JMP (indirect)
case 0x6d: this.AbsoluteRead(); this.ADC(); break; // ADC (absolute)
@@ -408,7 +408,7 @@ namespace M6502
case 0x7e: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.ROR()); break; // ROR (absolute, X)
case 0x81: this.IndexedIndirectXAddress(); this.MemoryWrite(this.A); break; // STA (indexed indirect X)
case 0x82: this.ImmediateRead(); break; // *NOP (immediate)
case 0x82: this.FetchByte(); break; // *NOP (immediate)
case 0x84: this.ZeroPageAddress(); this.MemoryWrite(this.Y); break; // STY (zero page)
case 0x85: this.ZeroPageAddress(); this.MemoryWrite(this.A); break; // STA (zero page)
case 0x86: this.ZeroPageAddress(); this.MemoryWrite(this.X); break; // STX (zero page)
@@ -428,14 +428,14 @@ namespace M6502
case 0x9a: this.SwallowRead(); this.S = this.X; break; // TXS (implied)
case 0x9d: this.AbsoluteXAddress(); this.Fixup(); this.MemoryWrite(this.A); break; // STA (absolute, X)
case 0xa0: this.ImmediateRead(); this.Y = this.Through(); break; // LDY (immediate)
case 0xa0: this.FetchByte(); this.Y = this.Through(); break; // LDY (immediate)
case 0xa1: this.IndexedIndirectXRead(); this.A = this.Through(); break; // LDA (indexed indirect X)
case 0xa2: this.ImmediateRead(); this.X = this.Through(); break; // LDX (immediate)
case 0xa2: this.FetchByte(); this.X = this.Through(); break; // LDX (immediate)
case 0xa4: this.ZeroPageRead(); this.Y = this.Through(); break; // LDY (zero page)
case 0xa5: this.ZeroPageRead(); this.A = this.Through(); break; // LDA (zero page)
case 0xa6: this.ZeroPageRead(); this.X = this.Through(); break; // LDX (zero page)
case 0xa8: this.SwallowRead(); this.Y = this.Through(this.A); break; // TAY (implied)
case 0xa9: this.ImmediateRead(); this.A = this.Through(); break; // LDA (immediate)
case 0xa9: this.FetchByte(); this.A = this.Through(); break; // LDA (immediate)
case 0xaa: this.SwallowRead(); this.X = this.Through(this.A); break; // TAX (implied)
case 0xac: this.AbsoluteRead(); this.Y = this.Through(); break; // LDY (absolute)
case 0xad: this.AbsoluteRead(); this.A = this.Through(); break; // LDA (absolute)
@@ -453,14 +453,14 @@ namespace M6502
case 0xbd: this.AbsoluteXRead(); this.A = this.Through(); break; // LDA (absolute, X)
case 0xbe: this.AbsoluteYRead(); this.X = this.Through(); break; // LDX (absolute, Y)
case 0xc0: this.ImmediateRead(); this.CMP(this.Y); break; // CPY (immediate)
case 0xc0: this.FetchByte(); this.CMP(this.Y); break; // CPY (immediate)
case 0xc1: this.IndexedIndirectXRead(); this.CMP(this.A); break; // CMP (indexed indirect X)
case 0xc2: this.ImmediateRead(); break; // *NOP (immediate)
case 0xc2: this.FetchByte(); break; // *NOP (immediate)
case 0xc4: this.ZeroPageRead(); this.CMP(this.Y); break; // CPY (zero page)
case 0xc5: this.ZeroPageRead(); this.CMP(this.A); break; // CMP (zero page)
case 0xc6: this.ZeroPageRead(); this.ModifyWrite(this.DEC()); break; // DEC (zero page)
case 0xc8: this.SwallowRead(); this.Y = this.INC(this.Y); break; // INY (implied)
case 0xc9: this.ImmediateRead(); this.CMP(this.A); break; // CMP (immediate)
case 0xc9: this.FetchByte(); this.CMP(this.A); break; // CMP (immediate)
case 0xca: this.SwallowRead(); this.X = this.DEC(this.X); break; // DEX (implied)
case 0xcc: this.AbsoluteRead(); this.CMP(this.Y); break; // CPY (absolute)
case 0xcd: this.AbsoluteRead(); this.CMP(this.A); break; // CMP (absolute)
@@ -476,14 +476,14 @@ namespace M6502
case 0xdd: this.AbsoluteXRead(); this.CMP(this.A); break; // CMP (absolute, X)
case 0xde: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.DEC()); break; // DEC (absolute, X)
case 0xe0: this.ImmediateRead(); this.CMP(this.X); break; // CPX (immediate)
case 0xe0: this.FetchByte(); this.CMP(this.X); break; // CPX (immediate)
case 0xe1: this.IndexedIndirectXRead(); this.SBC(); break; // SBC (indexed indirect X)
case 0xe2: this.ImmediateRead(); break; // *NOP (immediate)
case 0xe2: this.FetchByte(); break; // *NOP (immediate)
case 0xe4: this.ZeroPageRead(); this.CMP(this.X); break; // CPX (zero page)
case 0xe5: this.ZeroPageRead(); this.SBC(); break; // SBC (zero page)
case 0xe6: this.ZeroPageRead(); this.ModifyWrite(this.INC()); break; // INC (zero page)
case 0xe8: this.SwallowRead(); this.X = this.INC(this.X); break; // INX (implied)
case 0xe9: this.ImmediateRead(); this.SBC(); break; // SBC (immediate)
case 0xe9: this.FetchByte(); this.SBC(); break; // SBC (immediate)
case 0xea: this.SwallowRead(); break; // NOP (implied)
case 0xec: this.AbsoluteRead(); this.CMP(this.X); break; // CPX (absolute)
case 0xed: this.AbsoluteRead(); this.SBC(); break; // SBC (absolute)
@@ -541,7 +541,7 @@ namespace M6502
this.LowerSYNC();
System.Diagnostics.Debug.Assert(this.Cycles == 1, "An extra cycle has occurred");
// Can't use fetchByte, since that would add an extra tick.
// Can't use "FetchByte", since that would add an extra tick.
this.ImmediateAddress();
this.OpCode = this.ReadFromBus();
@@ -661,12 +661,6 @@ namespace M6502
this.Bus.Address.Assign(this.Intermediate);
}
protected void ImmediateAddress()
{
this.Bus.Address.Assign(this.PC);
this.IncrementPC();
}
protected void AbsoluteAddress() => this.FetchWordAddress();
protected void ZeroPageAddress() => this.Bus.Address.Assign(this.FetchByte(), 0);
@@ -715,12 +709,6 @@ namespace M6502
#region Address and read
protected void ImmediateRead()
{
this.ImmediateAddress();
_ = this.MemoryRead();
}
protected void AbsoluteRead()
{
this.AbsoluteAddress();
@@ -781,7 +769,7 @@ namespace M6502
protected void Branch(bool condition)
{
this.ImmediateRead();
this.FetchByte();
if (condition)
{
var relative = (sbyte)this.Bus.Data;

View File

@@ -24,7 +24,7 @@ namespace M6502
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 0x0b: this.FetchByte(); this.ANC(); break; // *ANC (immediate)
case 0x0c: this.AbsoluteRead(); break; // *NOP (absolute)
case 0x0f: this.AbsoluteRead(); this.SLO(); break; // *SLO (absolute)
@@ -40,7 +40,7 @@ namespace M6502
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 0x2b: this.FetchByte(); this.ANC(); break; // *ANC (immediate)
case 0x2f: this.AbsoluteRead(); this.RLA(); break; // *RLA (absolute)
case 0x32: this.Jam(); break; // *JAM
@@ -55,7 +55,7 @@ namespace M6502
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 0x4b: this.FetchByte(); this.ASR(); break; // *ASR (immediate)
case 0x4f: this.AbsoluteRead(); this.SRE(); break; // *SRE (absolute)
case 0x52: this.Jam(); break; // *JAM
@@ -70,7 +70,7 @@ namespace M6502
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 0x6b: this.FetchByte(); this.ARR(); break; // *ARR (immediate)
case 0x6f: this.AbsoluteRead(); this.RRA(); break; // *RRA (absolute)
case 0x72: this.Jam(); break; // *JAM
@@ -82,11 +82,11 @@ namespace M6502
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 0x80: this.FetchByte(); break; // *NOP (immediate)
case 0x83: this.IndexedIndirectXAddress(); this.SAX(); break; // *SAX (indexed indirect X)
case 0x87: this.ZeroPageAddress(); this.SAX(); break; // *SAX (zero page)
case 0x89: this.ImmediateRead(); break; // *NOP (immediate)
case 0x8b: this.ImmediateRead(); this.ANE(); break; // *ANE (immediate)
case 0x89: this.FetchByte(); break; // *NOP (immediate)
case 0x8b: this.FetchByte(); this.ANE(); break; // *ANE (immediate)
case 0x8f: this.AbsoluteAddress(); this.SAX(); break; // *SAX (absolute)
case 0x92: this.Jam(); break; // *JAM
@@ -99,7 +99,7 @@ namespace M6502
case 0xa3: this.IndexedIndirectXRead(); this.LAX(); break; // *LAX (indexed indirect X)
case 0xa7: this.ZeroPageRead(); this.LAX(); break; // *LAX (zero page)
case 0xab: this.ImmediateRead(); this.ATX(); break; // *ATX (immediate)
case 0xab: this.FetchByte(); this.ATX(); break; // *ATX (immediate)
case 0xaf: this.AbsoluteRead(); this.LAX(); break; // *LAX (absolute)
case 0xb2: this.Jam(); break; // *JAM
@@ -110,7 +110,7 @@ namespace M6502
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 0xcb: this.FetchByte(); this.AXS(); break; // *AXS (immediate)
case 0xcf: this.AbsoluteRead(); this.DCP(); break; // *DCP (absolute)
case 0xd2: this.Jam(); break; // *JAM
@@ -123,7 +123,7 @@ namespace M6502
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 0xeb: this.FetchByte(); this.SBC(); break; // *SBC (immediate)
case 0xef: this.AbsoluteRead(); this.ISB(); break; // *ISB (absolute)
case 0xf2: this.Jam(); break; // *JAM

View File

@@ -228,8 +228,15 @@
var pc_good = this.Check("PC", final.PC, cpu.PC);
var sp_good = this.Check("SP", final.SP, cpu.SP);
var a_good = this.Check("A", final.A, cpu.A);
//byte xyMask = 0;
//unchecked
//{
// xyMask = (byte)~(StatusBits.XF | StatusBits.YF);
//}
//var f_good = this.Check("F", (byte)(final.F & xyMask), (byte)(cpu.F & xyMask));
var f_good = this.Check("F", final.F, cpu.F);
var a_good = this.Check("A", final.A, cpu.A);
var b_good = this.Check("B", final.B, cpu.B);
var c_good = this.Check("C", final.C, cpu.C);
var d_good = this.Check("D", final.D, cpu.D);

View File

@@ -158,7 +158,7 @@ namespace Z80
// received from the memory is ignored and an NOP instruction is forced internally to the
// CPU.The HALT acknowledge signal is active during this time indicating that the processor
// is in the HALT state.
_ = this.ReadInitialOpCode();
_ = this.FetchInitialOpCode();
this.Execute(0); // NOP
handled = true;
}
@@ -519,7 +519,6 @@ namespace Z80
protected override void HandleRESET()
{
base.HandleRESET();
this.DisableInterrupts();
this.IV = this.REFRESH = 0;
this.SP.Word = this.AF.Word = (ushort)Mask.Sixteen;
this.Tick(3);
@@ -708,9 +707,9 @@ namespace Z80
private static byte SET(int n, byte operand) => SetBit(operand, Bit(n));
private void DisableInterrupts() => this.IFF1 = this.IFF2 = false;
protected override void DisableInterrupts() => this.IFF1 = this.IFF2 = false;
private void EnableInterrupts() => this.IFF1 = this.IFF2 = true;
protected override void EnableInterrupts() => this.IFF1 = this.IFF2 = true;
private Register16 HL2() => this._prefixDD ? this.IX : this._prefixFD ? this.IY : this.HL;
@@ -1669,13 +1668,6 @@ namespace Z80
private void FetchDisplacement() => this._displacement = (sbyte)this.FetchByte();
private byte FetchInitialOpCode()
{
var returned = this.ReadInitialOpCode();
this.IncrementPC();
return returned;
}
// ** From the Z80 CPU User Manual
// Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is
// placed on the address bus at the beginning of the M1 cycle. One half clock cycle later, the
@@ -1688,10 +1680,10 @@ namespace Z80
// before the RD signal becomes inactive. Clock states T3 and T4 of a fetch cycle are used to
// _refresh dynamic memories. The CPU uses this time to decode and execute the fetched
// instruction so that no other concurrent operation can be performed.
private byte ReadInitialOpCode()
private byte FetchInitialOpCode()
{
this.LowerM1();
var returned = this.MemoryRead(this.PC);
var returned = this.FetchByte();
this.RaiseM1();
return returned;
}