mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2024-06-02 16:41:33 +00:00
Resurrect the Register16 class. This (or something *very* much like it) is going to be necessary to add a Z80 emulator (reference access to the high/low parts of 16-bit registers).
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
b61d884679
commit
63db46a7bc
|
@ -10,65 +10,65 @@ namespace EightBit
|
|||
{
|
||||
}
|
||||
|
||||
public override ushort PeekWord(ushort address)
|
||||
public override Register16 PeekWord(ushort address)
|
||||
{
|
||||
var high = this.Bus.Peek(address);
|
||||
var low = this.Bus.Peek(++address);
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
public override void PokeWord(ushort address, ushort value)
|
||||
public override void PokeWord(ushort address, Register16 value)
|
||||
{
|
||||
this.Bus.Poke(address, Chip.LowByte(value));
|
||||
this.Bus.Poke(++address, Chip.HighByte(value));
|
||||
this.Bus.Poke(address, value.Low);
|
||||
this.Bus.Poke(++address, value.High);
|
||||
}
|
||||
|
||||
protected override ushort FetchWord()
|
||||
protected override Register16 FetchWord()
|
||||
{
|
||||
var high = this.FetchByte();
|
||||
var low = this.FetchByte();
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override ushort GetWord()
|
||||
protected override Register16 GetWord()
|
||||
{
|
||||
var high = this.BusRead();
|
||||
++this.Bus.Address;
|
||||
++this.Bus.Address();
|
||||
var low = this.BusRead();
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override ushort GetWordPaged(byte page, byte offset)
|
||||
protected override Register16 GetWordPaged(byte page, byte offset)
|
||||
{
|
||||
var high = this.BusRead(offset, page);
|
||||
var low = this.BusRead(++offset, page);
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override ushort PopWord()
|
||||
protected override Register16 PopWord()
|
||||
{
|
||||
var high = this.Pop();
|
||||
var low = this.Pop();
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override void PushWord(ushort value)
|
||||
protected override void PushWord(Register16 value)
|
||||
{
|
||||
this.Push(Chip.LowByte(value));
|
||||
this.Push(Chip.HighByte(value));
|
||||
this.Push(value.Low);
|
||||
this.Push(value.High);
|
||||
}
|
||||
|
||||
protected override void SetWord(ushort value)
|
||||
protected override void SetWord(Register16 value)
|
||||
{
|
||||
this.BusWrite(Chip.HighByte(value));
|
||||
++this.Bus.Address;
|
||||
this.BusWrite(Chip.LowByte(value));
|
||||
this.BusWrite(value.High);
|
||||
++this.Bus.Address();
|
||||
this.BusWrite(value.Low);
|
||||
}
|
||||
|
||||
protected override void SetWordPaged(byte page, byte offset, ushort value)
|
||||
protected override void SetWordPaged(byte page, byte offset, Register16 value)
|
||||
{
|
||||
this.BusWrite(offset, page, Chip.HighByte(value));
|
||||
this.BusWrite(++offset, page, Chip.LowByte(value));
|
||||
this.BusWrite(offset, page, value.High);
|
||||
this.BusWrite(++offset, page, value.Low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace EightBit
|
|||
|
||||
public abstract class Bus : IMapper
|
||||
{
|
||||
private Register16 address;
|
||||
private byte data;
|
||||
|
||||
public event EventHandler<EventArgs> WritingByte;
|
||||
|
@ -20,7 +21,7 @@ namespace EightBit
|
|||
|
||||
public byte Data { get => this.data; set => this.data = value; }
|
||||
|
||||
public ushort Address { get; set; }
|
||||
public ref Register16 Address() => ref this.address;
|
||||
|
||||
public abstract MemoryMapping Mapping(ushort absolute);
|
||||
|
||||
|
@ -28,12 +29,16 @@ namespace EightBit
|
|||
|
||||
public byte Peek(ushort absolute) => this.Reference(absolute);
|
||||
|
||||
public byte Peek(Register16 absolute) => this.Peek(absolute.Word);
|
||||
|
||||
public byte Peek(byte low, byte high) => this.Reference(low, high);
|
||||
|
||||
public void Poke(byte value) => this.Reference() = value;
|
||||
|
||||
public byte Poke(ushort absolute, byte value) => this.Reference(absolute) = value;
|
||||
|
||||
public byte Poke(Register16 absolute, byte value) => this.Poke(absolute.Word, value);
|
||||
|
||||
public byte Poke(byte low, byte high, byte value) => this.Reference(low, high) = value;
|
||||
|
||||
public byte Read()
|
||||
|
@ -46,11 +51,21 @@ namespace EightBit
|
|||
|
||||
public byte Read(ushort absolute)
|
||||
{
|
||||
this.Address = absolute;
|
||||
this.Address().Word = absolute;
|
||||
return this.Read();
|
||||
}
|
||||
|
||||
public byte Read(byte low, byte high) => this.Read(Chip.MakeWord(low, high));
|
||||
public byte Read(Register16 absolute)
|
||||
{
|
||||
return this.Read(absolute.Word);
|
||||
}
|
||||
|
||||
public byte Read(byte low, byte high)
|
||||
{
|
||||
this.Address().Low = low;
|
||||
this.Address().High = high;
|
||||
return this.Read();
|
||||
}
|
||||
|
||||
public void Write()
|
||||
{
|
||||
|
@ -67,11 +82,21 @@ namespace EightBit
|
|||
|
||||
public void Write(ushort absolute, byte value)
|
||||
{
|
||||
this.Address = absolute;
|
||||
this.Address().Word = absolute;
|
||||
this.Write(value);
|
||||
}
|
||||
|
||||
public void Write(byte low, byte high, byte value) => this.Write(Chip.MakeWord(low, high), value);
|
||||
public void Write(Register16 absolute, byte value)
|
||||
{
|
||||
this.Write(absolute.Word, value);
|
||||
}
|
||||
|
||||
public void Write(byte low, byte high, byte value)
|
||||
{
|
||||
this.Address().Low = low;
|
||||
this.Address().High = high;
|
||||
this.Write(value);
|
||||
}
|
||||
|
||||
public virtual void RaisePOWER()
|
||||
{
|
||||
|
@ -104,9 +129,11 @@ namespace EightBit
|
|||
return ref mapped.Memory.Reference(offset);
|
||||
}
|
||||
|
||||
protected ref byte Reference() => ref this.Reference(this.Address);
|
||||
protected ref byte Reference(Register16 absolute) => ref this.Reference(absolute.Word);
|
||||
|
||||
protected ref byte Reference(byte low, byte high) => ref this.Reference(Chip.MakeWord(low, high));
|
||||
protected ref byte Reference() => ref this.Reference(this.Address());
|
||||
|
||||
protected ref byte Reference(byte low, byte high) => ref this.Reference(new Register16(low, high).Word);
|
||||
|
||||
////[[nodiscard]] static std::map<uint16_t, std::vector<uint8_t>> parseHexFile(std::string path);
|
||||
////void loadHexFile(std::string path);
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
<Compile Include="Processor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Ram.cs" />
|
||||
<Compile Include="Register16.cs" />
|
||||
<Compile Include="Rom.cs" />
|
||||
<Compile Include="UnusedMemory.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -16,11 +16,11 @@ namespace EightBit
|
|||
{
|
||||
}
|
||||
|
||||
public IntelOpCodeDecoded(byte opcode)
|
||||
public IntelOpCodeDecoded(byte opCode)
|
||||
{
|
||||
this.x = (opcode & 0b11000000) >> 6; // 0 - 3
|
||||
this.y = (opcode & 0b00111000) >> 3; // 0 - 7
|
||||
this.z = opcode & 0b00000111; // 0 - 7
|
||||
this.x = (opCode & 0b11000000) >> 6; // 0 - 3
|
||||
this.y = (opCode & 0b00111000) >> 3; // 0 - 7
|
||||
this.z = opCode & 0b00000111; // 0 - 7
|
||||
this.p = (this.y & 0b110) >> 1; // 0 - 3
|
||||
this.q = this.y & 1; // 0 - 1
|
||||
}
|
||||
|
|
|
@ -10,14 +10,15 @@ namespace EightBit
|
|||
{
|
||||
private readonly IntelOpCodeDecoded[] decodedOpCodes = new IntelOpCodeDecoded[0x100];
|
||||
|
||||
private Register16 sp = new Register16((ushort)Mask.Mask16);
|
||||
|
||||
private Register16 memptr = new Register16((ushort)Mask.Mask16);
|
||||
|
||||
private PinLevel haltLine;
|
||||
|
||||
protected IntelProcessor(Bus bus)
|
||||
: base(bus)
|
||||
{
|
||||
this.SP = (ushort)Mask.Mask16;
|
||||
this.MEMPTR = (ushort)Mask.Mask16;
|
||||
|
||||
for (int i = 0; i < 0x100; ++i)
|
||||
{
|
||||
this.decodedOpCodes[i] = new IntelOpCodeDecoded((byte)i);
|
||||
|
@ -32,43 +33,43 @@ namespace EightBit
|
|||
|
||||
public event EventHandler<EventArgs> LoweredHALT;
|
||||
|
||||
public ushort SP { get; set; }
|
||||
|
||||
public ushort MEMPTR { get; set; }
|
||||
|
||||
public abstract ushort AF { get; set; }
|
||||
|
||||
public byte A { get => Chip.HighByte(this.AF); set => this.AF = (ushort)(Chip.LowerPart(this.AF) | Chip.PromoteByte(value)); }
|
||||
|
||||
public byte F { get => Chip.LowByte(this.AF); set => this.AF = (ushort)(Chip.HigherPart(this.AF) | value); }
|
||||
|
||||
public abstract ushort BC { get; set; }
|
||||
|
||||
public byte B { get => Chip.HighByte(this.BC); set => this.BC = (ushort)(Chip.LowerPart(this.BC) | Chip.PromoteByte(value)); }
|
||||
|
||||
public byte C { get => Chip.LowByte(this.BC); set => this.BC = (ushort)(Chip.HigherPart(this.BC) | value); }
|
||||
|
||||
public abstract ushort DE { get; set; }
|
||||
|
||||
public byte D { get => Chip.HighByte(this.DE); set => this.DE = (ushort)(Chip.LowerPart(this.DE) | Chip.PromoteByte(value)); }
|
||||
|
||||
public byte E { get => Chip.LowByte(this.DE); set => this.DE = (ushort)(Chip.HigherPart(this.DE) | value); }
|
||||
|
||||
public abstract ushort HL { get; set; }
|
||||
|
||||
public byte H { get => Chip.HighByte(this.HL); set => this.HL = (ushort)(Chip.LowerPart(this.HL) | Chip.PromoteByte(value)); }
|
||||
|
||||
public byte L { get => Chip.LowByte(this.HL); set => this.HL = (ushort)(Chip.HigherPart(this.HL) | value); }
|
||||
|
||||
protected bool Halted => this.HALT().Lowered();
|
||||
|
||||
public ref Register16 SP() => ref this.sp;
|
||||
|
||||
public ref Register16 MEMPTR() => ref this.memptr;
|
||||
|
||||
public abstract ref Register16 AF();
|
||||
|
||||
public ref byte A() => ref this.AF().High;
|
||||
|
||||
public ref byte F() => ref this.AF().Low;
|
||||
|
||||
public abstract ref Register16 BC();
|
||||
|
||||
public ref byte B() => ref this.BC().High;
|
||||
|
||||
public ref byte C() => ref this.BC().Low;
|
||||
|
||||
public abstract ref Register16 DE();
|
||||
|
||||
public ref byte D() => ref this.DE().High;
|
||||
|
||||
public ref byte E() => ref this.DE().Low;
|
||||
|
||||
public abstract ref Register16 HL();
|
||||
|
||||
public ref byte H() => ref this.HL().High;
|
||||
|
||||
public ref byte L() => ref this.HL().Low;
|
||||
|
||||
public ref PinLevel HALT() => ref this.haltLine;
|
||||
|
||||
public override void RaisePOWER()
|
||||
{
|
||||
base.RaisePOWER();
|
||||
this.RaiseHALT();
|
||||
this.SP = this.AF = this.BC = this.DE = this.HL = (ushort)Mask.Mask16;
|
||||
this.SP().Word = this.AF().Word = this.BC().Word = this.DE().Word = this.HL().Word = (ushort)Mask.Mask16;
|
||||
}
|
||||
|
||||
public virtual void RaiseHALT()
|
||||
|
@ -96,40 +97,41 @@ namespace EightBit
|
|||
protected override void HandleRESET()
|
||||
{
|
||||
base.HandleRESET();
|
||||
this.PC = 0;
|
||||
this.PC().Word = 0;
|
||||
}
|
||||
|
||||
protected sealed override void Push(byte value) => this.Bus.Write(--this.SP, value);
|
||||
protected sealed override void Push(byte value) => this.Bus.Write(--this.SP(), value);
|
||||
|
||||
protected sealed override byte Pop() => this.Bus.Read(this.SP++);
|
||||
protected sealed override byte Pop() => this.Bus.Read(this.SP()++);
|
||||
|
||||
protected sealed override ushort GetWord()
|
||||
protected sealed override Register16 GetWord()
|
||||
{
|
||||
var returned = base.GetWord();
|
||||
this.MEMPTR = this.Bus.Address;
|
||||
this.MEMPTR().Word = this.Bus.Address().Word;
|
||||
return returned;
|
||||
}
|
||||
|
||||
protected sealed override void SetWord(ushort value)
|
||||
protected sealed override void SetWord(Register16 value)
|
||||
{
|
||||
base.SetWord(value);
|
||||
this.MEMPTR = this.Bus.Address;
|
||||
this.MEMPTR().Word = this.Bus.Address().Word;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
protected void Restart(byte address)
|
||||
{
|
||||
this.MEMPTR = address;
|
||||
this.Call(this.MEMPTR);
|
||||
this.MEMPTR().Low = address;
|
||||
this.MEMPTR().High = 0;
|
||||
this.Call(this.MEMPTR().Word);
|
||||
}
|
||||
|
||||
protected bool CallConditional(bool condition)
|
||||
{
|
||||
this.MEMPTR = this.FetchWord();
|
||||
this.MEMPTR().Word = this.FetchWord().Word;
|
||||
if (condition)
|
||||
{
|
||||
this.Call(this.MEMPTR);
|
||||
this.Call(this.MEMPTR().Word);
|
||||
}
|
||||
|
||||
return condition;
|
||||
|
@ -137,10 +139,10 @@ namespace EightBit
|
|||
|
||||
protected bool JumpConditional(bool condition)
|
||||
{
|
||||
this.MEMPTR = this.FetchWord();
|
||||
this.MEMPTR().Word = this.FetchWord().Word;
|
||||
if (condition)
|
||||
{
|
||||
this.Jump(this.MEMPTR);
|
||||
this.Jump(this.MEMPTR().Word);
|
||||
}
|
||||
|
||||
return condition;
|
||||
|
@ -158,8 +160,8 @@ namespace EightBit
|
|||
|
||||
protected void JumpRelative(sbyte offset)
|
||||
{
|
||||
this.MEMPTR = (ushort)(this.PC + offset);
|
||||
this.Jump(this.MEMPTR);
|
||||
this.MEMPTR().Word = (ushort)(this.PC().Word + offset);
|
||||
this.Jump(this.MEMPTR().Word);
|
||||
}
|
||||
|
||||
protected bool JumpRelativeConditional(bool condition)
|
||||
|
@ -176,18 +178,18 @@ namespace EightBit
|
|||
protected override sealed void Return()
|
||||
{
|
||||
base.Return();
|
||||
this.MEMPTR = this.PC;
|
||||
this.MEMPTR().Word = this.PC().Word;
|
||||
}
|
||||
|
||||
protected void Halt()
|
||||
{
|
||||
--this.PC;
|
||||
--this.PC();
|
||||
this.LowerHALT();
|
||||
}
|
||||
|
||||
protected void Proceed()
|
||||
{
|
||||
++this.PC;
|
||||
++this.PC();
|
||||
this.RaiseHALT();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,65 +11,65 @@ namespace EightBit
|
|||
{
|
||||
}
|
||||
|
||||
public override ushort PeekWord(ushort address)
|
||||
public override Register16 PeekWord(ushort address)
|
||||
{
|
||||
var low = this.Bus.Peek(address);
|
||||
var high = this.Bus.Peek(++address);
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
public override void PokeWord(ushort address, ushort value)
|
||||
public override void PokeWord(ushort address, Register16 value)
|
||||
{
|
||||
this.Bus.Poke(address, Chip.LowByte(value));
|
||||
this.Bus.Poke(++address, Chip.HighByte(value));
|
||||
this.Bus.Poke(address, value.Low);
|
||||
this.Bus.Poke(++address, value.High);
|
||||
}
|
||||
|
||||
protected override ushort FetchWord()
|
||||
protected override Register16 FetchWord()
|
||||
{
|
||||
var low = this.FetchByte();
|
||||
var high = this.FetchByte();
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override ushort GetWord()
|
||||
protected override Register16 GetWord()
|
||||
{
|
||||
var low = this.BusRead();
|
||||
++this.Bus.Address;
|
||||
++this.Bus.Address();
|
||||
var high = this.BusRead();
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override ushort GetWordPaged(byte page, byte offset)
|
||||
protected override Register16 GetWordPaged(byte page, byte offset)
|
||||
{
|
||||
var low = this.BusRead(offset, page);
|
||||
var high = this.BusRead(++offset, page);
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override ushort PopWord()
|
||||
protected override Register16 PopWord()
|
||||
{
|
||||
var low = this.Pop();
|
||||
var high = this.Pop();
|
||||
return Chip.MakeWord(low, high);
|
||||
return new Register16(low, high);
|
||||
}
|
||||
|
||||
protected override void PushWord(ushort value)
|
||||
protected override void PushWord(Register16 value)
|
||||
{
|
||||
this.Push(Chip.HighByte(value));
|
||||
this.Push(Chip.LowByte(value));
|
||||
this.Push(value.High);
|
||||
this.Push(value.Low);
|
||||
}
|
||||
|
||||
protected override void SetWord(ushort value)
|
||||
protected override void SetWord(Register16 value)
|
||||
{
|
||||
this.BusWrite(Chip.LowByte(value));
|
||||
++this.Bus.Address;
|
||||
this.BusWrite(Chip.HighByte(value));
|
||||
this.BusWrite(value.Low);
|
||||
++this.Bus.Address();
|
||||
this.BusWrite(value.High);
|
||||
}
|
||||
|
||||
protected override void SetWordPaged(byte page, byte offset, ushort value)
|
||||
protected override void SetWordPaged(byte page, byte offset, Register16 value)
|
||||
{
|
||||
this.BusWrite(offset, page, Chip.LowByte(value));
|
||||
this.BusWrite(++offset, page, Chip.HighByte(value));
|
||||
this.BusWrite(offset, page, value.Low);
|
||||
this.BusWrite(++offset, page, value.High);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace EightBit
|
|||
{
|
||||
private PinLevel resetLine;
|
||||
private PinLevel intLine;
|
||||
private Register16 pc;
|
||||
|
||||
protected Processor(Bus memory)
|
||||
{
|
||||
|
@ -32,12 +33,12 @@ namespace EightBit
|
|||
|
||||
public event EventHandler<EventArgs> LoweredINT;
|
||||
|
||||
public ushort PC { get; set; }
|
||||
|
||||
public Bus Bus { get; }
|
||||
|
||||
protected byte OpCode { get; set; }
|
||||
|
||||
public ref Register16 PC() => ref this.pc;
|
||||
|
||||
public ref PinLevel RESET() => ref this.resetLine;
|
||||
|
||||
public ref PinLevel INT() => ref this.intLine;
|
||||
|
@ -63,11 +64,9 @@ namespace EightBit
|
|||
return this.Execute();
|
||||
}
|
||||
|
||||
public abstract ushort PeekWord(ushort address);
|
||||
public abstract Register16 PeekWord(ushort address);
|
||||
|
||||
public abstract void PokeWord(ushort address, ushort value);
|
||||
|
||||
public ushort PeekWord(byte low, byte high) => this.PeekWord((ushort)(Chip.PromoteByte(high) | low));
|
||||
public abstract void PokeWord(ushort address, Register16 value);
|
||||
|
||||
public virtual void RaiseRESET()
|
||||
{
|
||||
|
@ -117,11 +116,16 @@ namespace EightBit
|
|||
|
||||
protected virtual void HandleINT() => this.RaiseINT();
|
||||
|
||||
protected void BusWrite(byte low, byte high, byte data) => this.BusWrite(Chip.MakeWord(low, high), data);
|
||||
protected void BusWrite(byte low, byte high, byte data)
|
||||
{
|
||||
this.Bus.Address().Low = low;
|
||||
this.Bus.Address().High = high;
|
||||
this.BusWrite(data);
|
||||
}
|
||||
|
||||
protected void BusWrite(ushort address, byte data)
|
||||
{
|
||||
this.Bus.Address = address;
|
||||
this.Bus.Address().Word = address;
|
||||
this.BusWrite(data);
|
||||
}
|
||||
|
||||
|
@ -133,56 +137,61 @@ namespace EightBit
|
|||
|
||||
protected virtual void BusWrite() => this.Bus.Write(); // N.B. Should be the only real call into the "Bus.Write" code.
|
||||
|
||||
protected byte BusRead(byte low, byte high) => this.BusRead(Chip.MakeWord(low, high));
|
||||
protected byte BusRead(byte low, byte high)
|
||||
{
|
||||
this.Bus.Address().Low = low;
|
||||
this.Bus.Address().High = high;
|
||||
return this.BusRead();
|
||||
}
|
||||
|
||||
protected byte BusRead(ushort address)
|
||||
{
|
||||
this.Bus.Address = address;
|
||||
this.Bus.Address().Word = address;
|
||||
return this.BusRead();
|
||||
}
|
||||
|
||||
protected virtual byte BusRead() => this.Bus.Read(); // N.B. Should be the only real call into the "Bus.Read" code.
|
||||
|
||||
protected byte FetchByte() => this.BusRead(this.PC++);
|
||||
protected byte FetchByte() => this.BusRead(this.PC().Word++);
|
||||
|
||||
protected abstract ushort GetWord();
|
||||
protected abstract Register16 GetWord();
|
||||
|
||||
protected abstract void SetWord(ushort value);
|
||||
protected abstract void SetWord(Register16 value);
|
||||
|
||||
protected abstract ushort GetWordPaged(byte page, byte offset);
|
||||
protected abstract Register16 GetWordPaged(byte page, byte offset);
|
||||
|
||||
protected abstract void SetWordPaged(byte page, byte offset, ushort value);
|
||||
protected abstract void SetWordPaged(byte page, byte offset, Register16 value);
|
||||
|
||||
protected abstract ushort FetchWord();
|
||||
protected abstract Register16 FetchWord();
|
||||
|
||||
protected abstract void Push(byte value);
|
||||
|
||||
protected abstract byte Pop();
|
||||
|
||||
protected abstract void PushWord(ushort value);
|
||||
protected abstract void PushWord(Register16 value);
|
||||
|
||||
protected abstract ushort PopWord();
|
||||
protected abstract Register16 PopWord();
|
||||
|
||||
protected ushort GetWord(ushort address)
|
||||
protected Register16 GetWord(ushort address)
|
||||
{
|
||||
this.Bus.Address = address;
|
||||
this.Bus.Address().Word = address;
|
||||
return this.GetWord();
|
||||
}
|
||||
|
||||
protected void SetWord(ushort address, ushort value)
|
||||
protected void SetWord(ushort address, Register16 value)
|
||||
{
|
||||
this.Bus.Address = address;
|
||||
this.Bus.Address().Word = address;
|
||||
this.SetWord(value);
|
||||
}
|
||||
|
||||
protected void Jump(ushort destination) => this.PC = destination;
|
||||
protected void Jump(ushort destination) => this.PC().Word = destination;
|
||||
|
||||
protected void Call(ushort destination)
|
||||
{
|
||||
this.PushWord(this.PC);
|
||||
this.PushWord(this.PC());
|
||||
this.Jump(destination);
|
||||
}
|
||||
|
||||
protected virtual void Return() => this.Jump(this.PopWord());
|
||||
protected virtual void Return() => this.Jump(this.PopWord().Word);
|
||||
}
|
||||
}
|
||||
|
|
99
EightBit/Register16.cs
Normal file
99
EightBit/Register16.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
// <copyright file="Register16.cs" company="Adrian Conlon">
|
||||
// Copyright (c) Adrian Conlon. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace EightBit
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// This'll only work for little endian host processors...
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Register16
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public byte Low;
|
||||
|
||||
[FieldOffset(1)]
|
||||
public byte High;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public ushort Word;
|
||||
|
||||
public Register16(byte low, byte high)
|
||||
{
|
||||
this.Word = 0;
|
||||
this.Low = low;
|
||||
this.High = high;
|
||||
}
|
||||
|
||||
public Register16(ushort value)
|
||||
: this(Chip.LowByte(value), Chip.HighByte(value))
|
||||
{
|
||||
}
|
||||
|
||||
public Register16(int value)
|
||||
: this((ushort)value)
|
||||
{
|
||||
}
|
||||
|
||||
public Register16(byte low)
|
||||
: this(low, 0)
|
||||
{
|
||||
}
|
||||
|
||||
public Register16(Register16 rhs)
|
||||
{
|
||||
this.Low = 0;
|
||||
this.High = 0;
|
||||
this.Word = rhs.Word;
|
||||
}
|
||||
|
||||
public static Register16 operator ++(Register16 value)
|
||||
{
|
||||
return Increment(value);
|
||||
}
|
||||
|
||||
public static Register16 operator --(Register16 value)
|
||||
{
|
||||
return Decrement(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(Register16 left, Register16 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Register16 left, Register16 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static Register16 Increment(Register16 value)
|
||||
{
|
||||
++value.Word;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Register16 Decrement(Register16 value)
|
||||
{
|
||||
--value.Word;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is Register16))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Register16 rhs = (Register16)obj;
|
||||
return rhs.Word == this.Word;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Word;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ namespace EightBit
|
|||
output.Append(" ");
|
||||
|
||||
var next = this.bus.Peek((ushort)(current + 1));
|
||||
var relative = (ushort)(this.processor.PC + 2 + (sbyte)next);
|
||||
var relative = (ushort)(this.processor.PC().Word + 2 + (sbyte)next);
|
||||
|
||||
var aaa = (cell & 0b11100000) >> 5;
|
||||
var bbb = (cell & 0b00011100) >> 2;
|
||||
|
@ -550,7 +550,7 @@ namespace EightBit
|
|||
|
||||
private ushort GetWord(ushort absolute)
|
||||
{
|
||||
return this.processor.PeekWord(absolute);
|
||||
return this.processor.PeekWord(absolute).Word;
|
||||
}
|
||||
|
||||
private string Dump_Byte(ushort absolute)
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace M6502.Test
|
|||
var programFilename = this.configuration.Program;
|
||||
var programPath = this.configuration.RomDirectory + "/" + this.configuration.Program;
|
||||
var loadAddress = this.configuration.LoadAddress;
|
||||
this.ram.Load(programPath, loadAddress);
|
||||
this.ram.Load(programPath, loadAddress.Word);
|
||||
|
||||
if (this.configuration.DebugMode)
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ namespace M6502.Test
|
|||
|
||||
private void CPU_ExecutedInstruction(object sender, System.EventArgs e)
|
||||
{
|
||||
var pc = this.CPU.PC;
|
||||
var pc = this.CPU.PC().Word;
|
||||
if (this.oldPC != pc)
|
||||
{
|
||||
this.oldPC = pc;
|
||||
|
@ -89,7 +89,7 @@ namespace M6502.Test
|
|||
|
||||
private void CPU_ExecutingInstruction(object sender, System.EventArgs e)
|
||||
{
|
||||
var address = this.CPU.PC;
|
||||
var address = this.CPU.PC().Word;
|
||||
var cell = this.Peek(address);
|
||||
|
||||
var output = new StringBuilder();
|
||||
|
|
|
@ -8,8 +8,8 @@ namespace M6502.Test
|
|||
|
||||
internal class Configuration
|
||||
{
|
||||
private readonly ushort loadAddress = 0x400;
|
||||
private readonly ushort startAddress = 0x400;
|
||||
private readonly Register16 loadAddress = new Register16(0x400);
|
||||
private readonly Register16 startAddress = new Register16(0x400);
|
||||
private readonly string romDirectory = "roms";
|
||||
private readonly string program = "6502_functional_test.bin";
|
||||
private bool debugMode = false;
|
||||
|
@ -24,9 +24,9 @@ namespace M6502.Test
|
|||
set => this.debugMode = value;
|
||||
}
|
||||
|
||||
public ushort LoadAddress { get => this.loadAddress; }
|
||||
public Register16 LoadAddress { get => this.loadAddress; }
|
||||
|
||||
public ushort StartAddress { get => this.startAddress; }
|
||||
public Register16 StartAddress { get => this.startAddress; }
|
||||
|
||||
public string RomDirectory { get => this.romDirectory; }
|
||||
|
||||
|
|
107
M6502/M6502.cs
107
M6502/M6502.cs
|
@ -233,7 +233,7 @@ namespace EightBit
|
|||
case 0x49: this.A = this.EorR(this.A, this.AM_Immediate()); break; // EOR (immediate)
|
||||
case 0x4a: this.BusRead(); this.A = this.LSR(this.A); break; // LSR A (implied)
|
||||
case 0x4b: this.ASR(this.AM_Immediate()); break; // *ASR (immediate)
|
||||
case 0x4c: this.Jump(this.Address_Absolute()); break; // JMP (absolute)
|
||||
case 0x4c: this.Jump(this.Address_Absolute().Word); break; // JMP (absolute)
|
||||
case 0x4d: this.A = this.EorR(this.A, this.AM_Absolute()); break; // EOR (absolute)
|
||||
case 0x4e: this.BusReadModifyWrite(this.LSR(this.AM_Absolute())); break; // LSR (absolute)
|
||||
case 0x4f: this.SRE(this.AM_Absolute()); break; // *SRE (absolute)
|
||||
|
@ -267,7 +267,7 @@ namespace EightBit
|
|||
case 0x69: this.A = this.ADC(this.A, this.AM_Immediate()); break; // ADC (immediate)
|
||||
case 0x6a: this.BusRead(); this.A = this.ROR(this.A); break; // ROR A (implied)
|
||||
case 0x6b: this.ARR(this.AM_Immediate()); break; // *ARR (immediate)
|
||||
case 0x6c: this.Jump(this.Address_Indirect()); break; // JMP (indirect)
|
||||
case 0x6c: this.Jump(this.Address_Indirect().Word); break; // JMP (indirect)
|
||||
case 0x6d: this.A = this.ADC(this.A, this.AM_Absolute()); break; // ADC (absolute)
|
||||
case 0x6e: this.BusReadModifyWrite(this.ROR(this.AM_Absolute())); break; // ROR (absolute)
|
||||
case 0x6f: this.RRA(this.AM_Absolute()); break; // *RRA (absolute)
|
||||
|
@ -290,9 +290,9 @@ namespace EightBit
|
|||
case 0x7f: this.RRA(this.AM_AbsoluteX()); break; // *RRA (absolute, X)
|
||||
|
||||
case 0x80: this.AM_Immediate(); break; // *NOP (immediate)
|
||||
case 0x81: this.BusWrite(this.Address_IndexedIndirectX(), this.A); break; // STA (indexed indirect X)
|
||||
case 0x81: this.BusWrite(this.Address_IndexedIndirectX().Word, this.A); break; // STA (indexed indirect X)
|
||||
case 0x82: this.AM_Immediate(); break; // *NOP (immediate)
|
||||
case 0x83: this.BusWrite(this.Address_IndexedIndirectX(), (byte)(this.A & this.X)); break; // *SAX (indexed indirect X)
|
||||
case 0x83: this.BusWrite(this.Address_IndexedIndirectX().Word, (byte)(this.A & this.X)); break; // *SAX (indexed indirect X)
|
||||
case 0x84: this.BusWrite(this.Address_ZeroPage(), this.Y); break; // STY (zero page)
|
||||
case 0x85: this.BusWrite(this.Address_ZeroPage(), this.A); break; // STA (zero page)
|
||||
case 0x86: this.BusWrite(this.Address_ZeroPage(), this.X); break; // STX (zero page)
|
||||
|
@ -301,10 +301,10 @@ namespace EightBit
|
|||
case 0x89: this.AM_Immediate(); break; // *NOP (immediate)
|
||||
case 0x8a: this.BusRead(); this.A = this.Through(this.X); break; // TXA (implied)
|
||||
case 0x8b: break;
|
||||
case 0x8c: this.BusWrite(this.Address_Absolute(), this.Y); break; // STY (absolute)
|
||||
case 0x8d: this.BusWrite(this.Address_Absolute(), this.A); break; // STA (absolute)
|
||||
case 0x8e: this.BusWrite(this.Address_Absolute(), this.X); break; // STX (absolute)
|
||||
case 0x8f: this.BusWrite(this.Address_Absolute(), (byte)(this.A & this.X)); break; // *SAX (absolute)
|
||||
case 0x8c: this.BusWrite(this.Address_Absolute().Word, this.Y); break; // STY (absolute)
|
||||
case 0x8d: this.BusWrite(this.Address_Absolute().Word, this.A); break; // STA (absolute)
|
||||
case 0x8e: this.BusWrite(this.Address_Absolute().Word, this.X); break; // STX (absolute)
|
||||
case 0x8f: this.BusWrite(this.Address_Absolute().Word, (byte)(this.A & this.X)); break; // *SAX (absolute)
|
||||
|
||||
case 0x90: this.Branch(this.Carry == 0); break; // BCC (relative)
|
||||
case 0x91: this.AM_IndirectIndexedY(); this.BusWrite(this.A); break; // STA (indirect indexed Y)
|
||||
|
@ -443,7 +443,7 @@ namespace EightBit
|
|||
if (this.RDY().Raised())
|
||||
{
|
||||
this.LowerSYNC(); // Instruction fetch beginning
|
||||
this.OpCode = this.Bus.Read(this.PC++); // can't use fetchByte
|
||||
this.OpCode = this.Bus.Read(this.PC()++); // can't use fetchByte
|
||||
if (this.RESET().Lowered())
|
||||
{
|
||||
this.HandleRESET();
|
||||
|
@ -567,19 +567,19 @@ namespace EightBit
|
|||
var software = !hardware;
|
||||
if (reset)
|
||||
{
|
||||
this.DummyPush(Chip.HighByte(this.PC));
|
||||
this.DummyPush(Chip.LowByte(this.PC));
|
||||
this.DummyPush(this.PC().High);
|
||||
this.DummyPush(this.PC().Low);
|
||||
this.DummyPush(this.P);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.PushWord(this.PC);
|
||||
this.PushWord(this.PC());
|
||||
this.Push((byte)(this.P | (int)(software ? StatusBits.BF : 0)));
|
||||
}
|
||||
|
||||
this.P = SetFlag(this.P, StatusBits.IF); // Disable IRQ
|
||||
var vector = reset ? RSTvector : (nmi ? NMIvector : IRQvector);
|
||||
this.Jump(this.GetWordPaged(0xff, vector));
|
||||
this.Jump(this.GetWordPaged(0xff, vector).Word);
|
||||
this.handlingRESET = this.handlingNMI = this.handlingINT = false;
|
||||
}
|
||||
|
||||
|
@ -587,19 +587,20 @@ namespace EightBit
|
|||
{
|
||||
this.Tick();
|
||||
this.Bus.Data = value;
|
||||
this.Bus.Address = Chip.MakeWord(this.S--, 1);
|
||||
this.Bus.Address().Low = this.S--;
|
||||
this.Bus.Address().High = 1;
|
||||
}
|
||||
|
||||
private ushort Address_Absolute() => this.FetchWord();
|
||||
private Register16 Address_Absolute() => this.FetchWord();
|
||||
|
||||
private byte Address_ZeroPage() => this.FetchByte();
|
||||
|
||||
private ushort Address_ZeroPageIndirect() => this.GetWordPaged(0, this.Address_ZeroPage());
|
||||
private Register16 Address_ZeroPageIndirect() => this.GetWordPaged(0, this.Address_ZeroPage());
|
||||
|
||||
private ushort Address_Indirect()
|
||||
private Register16 Address_Indirect()
|
||||
{
|
||||
var address = this.Address_Absolute();
|
||||
return this.GetWordPaged(Chip.HighByte(address), Chip.LowByte(address));
|
||||
return this.GetWordPaged(address.High, address.Low);
|
||||
}
|
||||
|
||||
private byte Address_ZeroPageX()
|
||||
|
@ -616,42 +617,42 @@ namespace EightBit
|
|||
return Chip.LowByte(address + this.Y);
|
||||
}
|
||||
|
||||
private Tuple<ushort, byte> Address_AbsoluteX()
|
||||
private Tuple<Register16, byte> Address_AbsoluteX()
|
||||
{
|
||||
var address = this.Address_Absolute();
|
||||
var page = HighByte(address);
|
||||
address += this.X;
|
||||
return new Tuple<ushort, byte>(address, page);
|
||||
var page = address.High;
|
||||
address.Word += this.X;
|
||||
return new Tuple<Register16, byte>(address, page);
|
||||
}
|
||||
|
||||
private Tuple<ushort, byte> Address_AbsoluteY()
|
||||
private Tuple<Register16, byte> Address_AbsoluteY()
|
||||
{
|
||||
var address = this.Address_Absolute();
|
||||
var page = HighByte(address);
|
||||
address += this.Y;
|
||||
return new Tuple<ushort, byte>(address, page);
|
||||
var page = address.High;
|
||||
address.Word += this.Y;
|
||||
return new Tuple<Register16, byte>(address, page);
|
||||
}
|
||||
|
||||
private ushort Address_IndexedIndirectX() => this.GetWordPaged(0, this.Address_ZeroPageX());
|
||||
private Register16 Address_IndexedIndirectX() => this.GetWordPaged(0, this.Address_ZeroPageX());
|
||||
|
||||
private Tuple<ushort, byte> Address_IndirectIndexedY()
|
||||
private Tuple<Register16, byte> Address_IndirectIndexedY()
|
||||
{
|
||||
var address = this.Address_ZeroPageIndirect();
|
||||
var page = Chip.HighByte(address);
|
||||
address += this.Y;
|
||||
return new Tuple<ushort, byte>(address, page);
|
||||
var page = address.High;
|
||||
address.Word += this.Y;
|
||||
return new Tuple<Register16, byte>(address, page);
|
||||
}
|
||||
|
||||
private ushort Address_relative_byte()
|
||||
{
|
||||
var offset = (sbyte)this.FetchByte();
|
||||
this.intermediate = (ushort)(this.PC + offset);
|
||||
this.intermediate = (ushort)(this.PC().Word + offset);
|
||||
return this.intermediate;
|
||||
}
|
||||
|
||||
private byte AM_Immediate() => this.FetchByte();
|
||||
|
||||
private byte AM_Absolute() => this.BusRead(this.Address_Absolute());
|
||||
private byte AM_Absolute() => this.BusRead(this.Address_Absolute().Word);
|
||||
|
||||
private byte AM_ZeroPage() => this.BusRead(this.Address_ZeroPage());
|
||||
|
||||
|
@ -660,10 +661,10 @@ namespace EightBit
|
|||
var crossed = this.Address_AbsoluteX();
|
||||
var address = crossed.Item1;
|
||||
var page = crossed.Item2;
|
||||
var possible = this.BusRead(Chip.LowByte(address), page);
|
||||
if ((behaviour == PageCrossingBehavior.AlwaysReadTwice) || (page != Chip.HighByte(address)))
|
||||
var possible = this.BusRead(address.Low, page);
|
||||
if ((behaviour == PageCrossingBehavior.AlwaysReadTwice) || (page != address.High))
|
||||
{
|
||||
possible = this.BusRead(address);
|
||||
possible = this.BusRead(address.Word);
|
||||
}
|
||||
|
||||
return possible;
|
||||
|
@ -674,10 +675,10 @@ namespace EightBit
|
|||
var crossed = this.Address_AbsoluteY();
|
||||
var address = crossed.Item1;
|
||||
var page = crossed.Item2;
|
||||
var possible = this.BusRead(Chip.LowByte(address), page);
|
||||
if (page != Chip.HighByte(address))
|
||||
var possible = this.BusRead(address.Low, page);
|
||||
if (page != address.High)
|
||||
{
|
||||
possible = this.BusRead(address);
|
||||
possible = this.BusRead(address.Word);
|
||||
}
|
||||
|
||||
return possible;
|
||||
|
@ -687,17 +688,17 @@ namespace EightBit
|
|||
|
||||
private byte AM_ZeroPageY() => this.BusRead(this.Address_ZeroPageY());
|
||||
|
||||
private byte AM_IndexedIndirectX() => this.BusRead(this.Address_IndexedIndirectX());
|
||||
private byte AM_IndexedIndirectX() => this.BusRead(this.Address_IndexedIndirectX().Word);
|
||||
|
||||
private byte AM_IndirectIndexedY()
|
||||
{
|
||||
var crossed = this.Address_IndirectIndexedY();
|
||||
var address = crossed.Item1;
|
||||
var page = crossed.Item2;
|
||||
var possible = this.BusRead(LowByte(address), page);
|
||||
if (page != Chip.HighByte(address))
|
||||
var possible = this.BusRead(address.Low, page);
|
||||
if (page != address.High)
|
||||
{
|
||||
possible = this.BusRead(address);
|
||||
possible = this.BusRead(address.Word);
|
||||
}
|
||||
|
||||
return possible;
|
||||
|
@ -719,11 +720,11 @@ namespace EightBit
|
|||
if (condition)
|
||||
{
|
||||
this.BusRead();
|
||||
var page = HighByte(this.PC);
|
||||
var page = this.PC().High;
|
||||
this.Jump(destination);
|
||||
if (Chip.HighByte(this.PC) != page)
|
||||
if (this.PC().High != page)
|
||||
{
|
||||
this.BusRead(Chip.LowByte(this.PC), page);
|
||||
this.BusRead(this.PC().Low, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -864,8 +865,10 @@ namespace EightBit
|
|||
{
|
||||
var low = this.FetchByte();
|
||||
this.BusRead(this.S, 1); // dummy read
|
||||
this.PushWord(this.PC);
|
||||
this.PC = Chip.MakeWord(low, this.FetchByte());
|
||||
this.PushWord(this.PC());
|
||||
var high = this.FetchByte();
|
||||
this.PC().Low = low;
|
||||
this.PC().High = high;
|
||||
}
|
||||
|
||||
private byte LSR(byte value)
|
||||
|
@ -977,8 +980,8 @@ namespace EightBit
|
|||
var crossed = this.Address_AbsoluteX();
|
||||
var address = crossed.Item1;
|
||||
var page = crossed.Item2;
|
||||
this.BusRead(Chip.LowByte(address), page);
|
||||
this.BusWrite(address, this.A);
|
||||
this.BusRead(address.Low, page);
|
||||
this.BusWrite(address.Word, this.A);
|
||||
}
|
||||
|
||||
private void STA_AbsoluteY()
|
||||
|
@ -986,8 +989,8 @@ namespace EightBit
|
|||
var crossed = this.Address_AbsoluteY();
|
||||
var address = crossed.Item1;
|
||||
var page = crossed.Item2;
|
||||
this.BusRead(Chip.LowByte(address), page);
|
||||
this.BusWrite(address, this.A);
|
||||
this.BusRead(address.Low, page);
|
||||
this.BusWrite(address.Word, this.A);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user