diff --git a/EightBit.sln b/EightBit.sln index 2f20608..46c10bf 100644 --- a/EightBit.sln +++ b/EightBit.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M6502", "M6502\M6502.csproj EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test_M6502", "Test_M6502\Test_M6502.csproj", "{854EDE2F-B54D-425C-8F68-EDA68BDC797E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestEightBit", "UnitTestEightBit\UnitTestEightBit.csproj", "{AA8282A3-212A-42DE-B778-BBAEB7E6D986}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -55,6 +57,18 @@ Global {854EDE2F-B54D-425C-8F68-EDA68BDC797E}.Release|x64.Build.0 = Release|Any CPU {854EDE2F-B54D-425C-8F68-EDA68BDC797E}.Release|x86.ActiveCfg = Release|Any CPU {854EDE2F-B54D-425C-8F68-EDA68BDC797E}.Release|x86.Build.0 = Release|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Debug|x64.ActiveCfg = Debug|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Debug|x64.Build.0 = Debug|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Debug|x86.Build.0 = Debug|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Release|Any CPU.Build.0 = Release|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Release|x64.ActiveCfg = Release|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Release|x64.Build.0 = Release|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Release|x86.ActiveCfg = Release|Any CPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/EightBit/BigEndianProcessor.cs b/EightBit/BigEndianProcessor.cs index bec4eda..0257dd8 100644 --- a/EightBit/BigEndianProcessor.cs +++ b/EightBit/BigEndianProcessor.cs @@ -7,67 +7,65 @@ { } - public override Register16 PeekWord(Register16 address) + public override ushort PeekWord(ushort address) { var high = Bus.Peek(address); var low = Bus.Peek(++address); - return new Register16(low, high); + return MakeWord(low, high); } - public override void PokeWord(Register16 address, Register16 value) + public override void PokeWord(ushort address, ushort value) { - Bus.Poke(address, value.High); - Bus.Poke(++address, value.Low); + Bus.Poke(address, LowByte(value)); + Bus.Poke(++address, HighByte(value)); } - protected override Register16 FetchWord() + protected override ushort FetchWord() { var high = FetchByte(); var low = FetchByte(); - return new Register16(low, high); + return MakeWord(low, high); } - protected override Register16 GetWord() + protected override ushort GetWord() { var high = BusRead(); - ++Bus.Address.Word; + ++Bus.Address; var low = BusRead(); - return new Register16(low, high); + return MakeWord(low, high); } - protected override Register16 GetWordPaged(byte page, byte offset) + protected override ushort GetWordPaged(byte page, byte offset) { var high = GetBytePaged(page, offset); - ++Bus.Address.Low; - var low = BusRead(); - return new Register16(low, high); + var low = GetBytePaged(page, (byte)(offset + 1)); + return MakeWord(low, high); } - protected override Register16 PopWord() + protected override ushort PopWord() { var high = Pop(); var low = Pop(); - return new Register16(low, high); + return MakeWord(low, high); } - protected override void PushWord(Register16 value) + protected override void PushWord(ushort value) { - Push(value.Low); - Push(value.High); + Push(LowByte(value)); + Push(HighByte(value)); } - protected override void SetWord(Register16 value) + protected override void SetWord(ushort value) { - BusWrite(value.High); - ++Bus.Address.Word; - BusWrite(value.Low); + BusWrite(HighByte(value)); + ++Bus.Address; + BusWrite(LowByte(value)); } - protected override void SetWordPaged(byte page, byte offset, Register16 value) + protected override void SetWordPaged(byte page, byte offset, ushort value) { - SetBytePaged(page, offset, value.High); - ++Bus.Address.Low; - BusWrite(value.Low); + SetBytePaged(page, offset, HighByte(value)); + SetBytePaged(page, (byte)(offset + 1), LowByte(value)); } } } diff --git a/EightBit/Bus.cs b/EightBit/Bus.cs index d27c82d..bc5564e 100644 --- a/EightBit/Bus.cs +++ b/EightBit/Bus.cs @@ -5,7 +5,7 @@ abstract public class Bus : IMapper { private byte data; - private Register16 address = new Register16(); + private ushort address; public event EventHandler WritingByte; public event EventHandler WrittenByte; @@ -19,22 +19,21 @@ set { data = value; } } - public Register16 Address + public ushort Address { get { return address; } + set { address = value; } } - public abstract MemoryMapping Mapping(Register16 absolute); + public abstract MemoryMapping Mapping(ushort absolute); public byte Peek() => Reference(); - public byte Peek(Register16 absolute) => Reference(absolute); - public byte Peek(ushort absolute) => Peek(Chip.LowByte(absolute), Chip.HighByte(absolute)); - public byte Peek(byte low, byte high) => Reference(new Register16(low, high)); + public byte Peek(ushort absolute) => Reference(absolute); + public byte Peek(byte low, byte high) => Reference(low, high); public void Poke(byte value) => Reference() = value; - public void Poke(Register16 absolute, byte value) => Reference(absolute) = value; - public byte Poke(ushort absolute, byte value) => Poke(Chip.LowByte(absolute), Chip.HighByte(absolute), value); - public byte Poke(byte low, byte high, byte value) => Reference(new Register16(low, high)) = value; + public byte Poke(ushort absolute, byte value) => Reference(absolute) = value; + public byte Poke(byte low, byte high, byte value) => Reference(low, high) = value; public byte Read() { @@ -44,12 +43,17 @@ return returned; } - public byte Read(Register16 absolute) + public byte Read(ushort absolute) { - Address.Word = absolute.Word; + Address = absolute; return Read(); } + public byte Read(byte low, byte high) + { + return Read(Chip.MakeWord(low, high)); + } + public void Write() { OnWritingByte(); @@ -63,12 +67,17 @@ Write(); } - public void Write(Register16 absolute, byte value) + public void Write(ushort absolute, byte value) { - Address.Word = absolute.Word; + Address = absolute; Write(value); } + public void Write(byte low, byte high, byte value) + { + Write(Chip.MakeWord(low, high), value); + } + public virtual void RaisePOWER() {} public virtual void LowerPOWER() {} @@ -80,7 +89,7 @@ protected virtual void OnReadingByte() => ReadingByte?.Invoke(this, EventArgs.Empty); protected virtual void OnReadByte() => ReadByte?.Invoke(this, EventArgs.Empty); - protected ref byte Reference(Register16 absolute) + protected ref byte Reference(ushort absolute) { var mapped = Mapping(absolute); var offset = (ushort)((absolute - mapped.Begin) & mapped.Mask); @@ -94,13 +103,9 @@ protected ref byte Reference() => ref Reference(Address); - protected ref byte Reference(ushort absolute) => ref Reference(Chip.LowByte(absolute), Chip.HighByte(absolute)); - protected ref byte Reference(byte low, byte high) { - Address.Low = low; - Address.High = high; - return ref Reference(); + return ref Reference(Chip.MakeWord(low, high)); } //[[nodiscard]] static std::map> parseHexFile(std::string path); diff --git a/EightBit/Chip.cs b/EightBit/Chip.cs index e1abe6b..fce5d3e 100644 --- a/EightBit/Chip.cs +++ b/EightBit/Chip.cs @@ -17,18 +17,20 @@ ClearFlag(ref f, flag); } - public static void ClearFlag(ref byte f, byte flag, int condition) => ClearFlag(ref f, flag, condition == 0); + public static void ClearFlag(ref byte f, byte flag, int condition) => ClearFlag(ref f, flag, condition != 0); public static void ClearFlag(ref byte f, byte flag, bool condition) => SetFlag(ref f, flag, !condition); - public static int HighByte(int value) => LowByte(value >> 8); - public static byte HighByte(ushort value) => (byte)LowByte((int)value); - public static int LowByte(int value) => value & (int)Mask.Mask8; - public static byte LowByte(ushort value) => (byte)LowByte((int)value); - public static int PromoteByte(int value) => value << 8; - public static ushort PromoteByte(byte value) => (ushort)PromoteByte((int)value); - public static int DemoteByte(int value) => HighByte(value); - public static byte DemoteByte(ushort value) => (byte)HighByte((int)value); + public static byte HighByte(int value) => (byte)(value >> 8); + public static byte HighByte(ushort value) => HighByte((int)value); + public static byte LowByte(int value) => (byte)(value & (int)Mask.Mask8); + public static byte LowByte(ushort value) => LowByte((int)value); + public static ushort PromoteByte(byte value) => (ushort)(value << 8); + public static byte DemoteByte(ushort value) => HighByte(value); + public static ushort HigherPart(ushort value) => (ushort)(value & 0xff00); + public static byte LowerPart(ushort value) => LowByte(value); + + static public ushort MakeWord(byte low, byte high) => (ushort)(PromoteByte(high) | low); public static int HighNibble(byte value) => value >> 4; public static int LowNibble(byte value) => value & 0xf; @@ -36,7 +38,7 @@ public static int HigherNibble(byte value) => value & 0xf0; public static int LowerNibble(byte value) => LowNibble(value); - public static int PromoteNibble(byte value) => value << 4; + public static int PromoteNibble(byte value) => LowByte(value << 4); public static int DemoteNibble(byte value) => HighNibble(value); } } diff --git a/EightBit/EightBit.csproj b/EightBit/EightBit.csproj index 634b6d7..482cbf4 100644 --- a/EightBit/EightBit.csproj +++ b/EightBit/EightBit.csproj @@ -68,7 +68,6 @@ - diff --git a/EightBit/IntelProcessor.cs b/EightBit/IntelProcessor.cs index d4cc6d2..3f7dc6b 100644 --- a/EightBit/IntelProcessor.cs +++ b/EightBit/IntelProcessor.cs @@ -5,8 +5,8 @@ public abstract class IntelProcessor : LittleEndianProcessor { private readonly IntelOpCodeDecoded[] decodedOpCodes; - private Register16 sp; - private Register16 memptr; + private ushort sp; + private ushort memptr; private PinLevel haltLine; @@ -14,8 +14,8 @@ : base(bus) { decodedOpCodes = new IntelOpCodeDecoded[0x100]; - sp = new Register16((ushort)Mask.Mask16); - memptr = new Register16(); + sp = (ushort)Mask.Mask16; + memptr = (ushort)Mask.Mask16; for (int i = 0; i < 0x100; ++i) decodedOpCodes[i] = new IntelOpCodeDecoded((byte)i); @@ -30,30 +30,30 @@ protected bool Halted => HALT().Lowered(); - public Register16 SP { get => sp; set => sp = value; } - public Register16 MEMPTR { get => memptr; set => memptr = value; } + public ushort SP { get => sp; set => sp = value; } + public ushort MEMPTR { get => memptr; set => memptr = value; } - public abstract Register16 AF { get; set; } - public byte A { get { return AF.High; } set { AF.High = value; } } - public byte F { get { return AF.Low; } set { AF.Low = value; } } + public abstract ushort AF { get; set; } + public byte A { get { return HighByte(AF); } set { AF = (ushort)(LowerPart(AF) | PromoteByte(value)); } } + public byte F { get { return LowByte(AF); } set { AF = (ushort)(HigherPart(AF) | value); } } - public abstract Register16 BC { get; set; } - public byte B { get { return BC.High; } set { BC.High = value; } } - public byte C { get { return BC.Low; } set { BC.Low = value; } } + public abstract ushort BC { get; set; } + public byte B { get { return HighByte(AF); } set { BC = (ushort)(LowerPart(BC) | PromoteByte(value)); } } + public byte C { get { return LowByte(AF); } set { BC = (ushort)(HigherPart(BC) | value); } } - public abstract Register16 DE { get; set; } - public byte D { get { return DE.High; } set { DE.High = value; } } - public byte E { get { return DE.Low; } set { DE.Low = value; } } + public abstract ushort DE { get; set; } + public byte D { get { return HighByte(AF); } set { DE = (ushort)(LowerPart(DE) | PromoteByte(value)); } } + public byte E { get { return LowByte(AF); } set { DE = (ushort)(HigherPart(DE) | value); } } - public abstract Register16 HL { get; set; } - public byte H { get { return HL.High; } set { HL.High = value; } } - public byte L { get { return HL.Low; } set { HL.Low = value; } } + public abstract ushort HL { get; set; } + public byte H { get { return HighByte(AF); } set { HL = (ushort)(LowerPart(AF) | PromoteByte(value)); } } + public byte L { get { return LowByte(AF); } set { HL = (ushort)(HigherPart(AF) | value); } } public override void RaisePOWER() { base.RaisePOWER(); RaiseHALT(); - SP = AF = BC = DE = HL = new Register16((ushort)Mask.Mask16); + SP = AF = BC = DE = HL = (ushort)Mask.Mask16; } public virtual void RaiseHALT() @@ -79,20 +79,20 @@ protected override void HandleRESET() { base.HandleRESET(); - PC.Word = 0; + PC = 0; } protected sealed override void Push(byte value) => Bus.Write(--SP, value); protected sealed override byte Pop() => Bus.Read(SP++); - protected sealed override Register16 GetWord() + protected sealed override ushort GetWord() { var returned = base.GetWord(); MEMPTR = Bus.Address; return returned; } - protected sealed override void SetWord(Register16 value) + protected sealed override void SetWord(ushort value) { base.SetWord(value); MEMPTR = Bus.Address; @@ -102,8 +102,7 @@ protected void Restart(byte address) { - MEMPTR.Low = address; - MEMPTR.High = 0; + MEMPTR = address; Call(MEMPTR); } @@ -132,7 +131,7 @@ protected void JumpRelative(sbyte offset) { - MEMPTR.Word = (ushort)(PC.Word + offset); + MEMPTR = (ushort)(PC + offset); Jump(MEMPTR); } diff --git a/EightBit/LittleEndianProcessor.cs b/EightBit/LittleEndianProcessor.cs index cc6a2dc..d04747a 100644 --- a/EightBit/LittleEndianProcessor.cs +++ b/EightBit/LittleEndianProcessor.cs @@ -1,6 +1,4 @@ -using System; - -namespace EightBit +namespace EightBit { public abstract class LittleEndianProcessor : Processor { @@ -9,75 +7,65 @@ namespace EightBit { } - public override Register16 PeekWord(Register16 address) + public override ushort PeekWord(ushort address) { var low = Bus.Peek(address); var high = Bus.Peek(++address); - return new Register16(low, high); + return MakeWord(low, high); } - public override void PokeWord(Register16 address, Register16 value) + public override void PokeWord(ushort address, ushort value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - Bus.Poke(address, value.Low); - Bus.Poke(++address, value.High); + Bus.Poke(address, LowByte(value)); + Bus.Poke(++address, HighByte(value)); } - protected override Register16 FetchWord() + protected override ushort FetchWord() { var low = FetchByte(); var high = FetchByte(); - return new Register16(low, high); + return MakeWord(low, high); } - protected override Register16 GetWord() + protected override ushort GetWord() { var low = BusRead(); - ++Bus.Address.Word; + ++Bus.Address; var high = BusRead(); - return new Register16(low, high); + return MakeWord(low, high); } - protected override Register16 GetWordPaged(byte page, byte offset) + protected override ushort GetWordPaged(byte page, byte offset) { var low = GetBytePaged(page, offset); - ++Bus.Address.Low; - var high = BusRead(); - return new Register16(low, high); + var high = GetBytePaged(page, (byte)(offset + 1)); + return MakeWord(low, high); } - protected override Register16 PopWord() + protected override ushort PopWord() { var low = Pop(); var high = Pop(); - return new Register16(low, high); + return MakeWord(low, high); } - protected override void PushWord(Register16 value) + protected override void PushWord(ushort value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - Push(value.High); - Push(value.Low); + Push(HighByte(value)); + Push(LowByte(value)); } - protected override void SetWord(Register16 value) + protected override void SetWord(ushort value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - BusWrite(value.Low); - ++Bus.Address.Word; - BusWrite(value.High); + BusWrite(LowByte(value)); + ++Bus.Address; + BusWrite(HighByte(value)); } - protected override void SetWordPaged(byte page, byte offset, Register16 value) + protected override void SetWordPaged(byte page, byte offset, ushort value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - SetBytePaged(page, offset, value.Low); - ++Bus.Address.Low; - BusWrite(value.High); + SetBytePaged(page, offset, LowByte(value)); + SetBytePaged(page, (byte)(offset + 1), HighByte(value)); } } } diff --git a/EightBit/Mapper.cs b/EightBit/Mapper.cs index 2f84318..971594a 100644 --- a/EightBit/Mapper.cs +++ b/EightBit/Mapper.cs @@ -2,6 +2,6 @@ { public interface IMapper { - MemoryMapping Mapping(Register16 address); + MemoryMapping Mapping(ushort address); } } diff --git a/EightBit/Processor.cs b/EightBit/Processor.cs index d488206..ae990e3 100644 --- a/EightBit/Processor.cs +++ b/EightBit/Processor.cs @@ -6,7 +6,7 @@ { private Bus bus; private byte opcode; - private Register16 pc; + private ushort pc; private PinLevel resetLine; private PinLevel intLine; @@ -14,7 +14,7 @@ protected Processor(Bus memory) { bus = memory; - pc = new Register16(); + pc = 0; } public event EventHandler RaisingRESET; @@ -27,7 +27,7 @@ public event EventHandler LoweringINT; public event EventHandler LoweredINT; - public Register16 PC { get => pc; set => pc = value; } + public ushort PC { get => pc; set => pc = value; } protected byte OpCode { get => opcode; set => opcode = value; } public Bus Bus { get => bus; set => bus = value; } @@ -51,11 +51,10 @@ return Execute(); } - public abstract Register16 PeekWord(Register16 address); - public abstract void PokeWord(Register16 address, Register16 value); + public abstract ushort PeekWord(ushort address); + public abstract void PokeWord(ushort address, ushort value); - public Register16 PeekWord(byte low, byte high) => PeekWord(new Register16(low, high)); - public Register16 PeekWord(ushort address) => PeekWord(LowByte(address), HighByte(address)); + public ushort PeekWord(byte low, byte high) => PeekWord((ushort)(PromoteByte(high) | low)); public virtual void RaiseRESET() { @@ -100,14 +99,16 @@ protected virtual void HandleRESET() => RaiseRESET(); protected virtual void HandleINT() => RaiseINT(); - protected void BusWrite(byte low, byte data) => BusWrite(low, 0, data); - - protected void BusWrite(Register16 address, byte data) => BusWrite(address.Low, address.High, data); + #region BusWrite protected void BusWrite(byte low, byte high, byte data) { - Bus.Address.Low = low; - Bus.Address.High = high; + BusWrite(MakeWord(low, high), data); + } + + protected void BusWrite(ushort address, byte data) + { + Bus.Address = address; BusWrite(data); } @@ -117,59 +118,69 @@ BusWrite(); } - protected virtual void BusWrite() => Bus.Write(); + protected virtual void BusWrite() => Bus.Write(); // N.B. Should be the only real call into the "Bus.Write" code. - protected byte BusRead(byte low) => BusRead(low, 0); + #endregion - protected byte BusRead(Register16 address) => BusRead(address.Low, address.High); + #region BusRead protected byte BusRead(byte low, byte high) { - Bus.Address.Low = low; - Bus.Address.High = high; + return BusRead(MakeWord(low, high)); + } + + protected byte BusRead(ushort address) + { + Bus.Address = address; return BusRead(); } - protected virtual byte BusRead() => Bus.Read(); + protected virtual byte BusRead() => Bus.Read(); // N.B. Should be the only real call into the "Bus.Read" code. - protected byte GetBytePaged(byte page, byte offset) => BusRead(new Register16(offset, page)); + #endregion - protected void SetBytePaged(byte page, byte offset, byte value) => BusWrite(new Register16(offset, page), value); + #region Paged reader/writer wrappers + + protected byte GetBytePaged(byte page, byte offset) => BusRead(offset, page); + + protected void SetBytePaged(byte page, byte offset, byte value) => BusWrite(offset, page, value); + + #endregion protected byte FetchByte() => BusRead(PC++); - protected abstract Register16 GetWord(); - protected abstract void SetWord(Register16 value); + protected abstract ushort GetWord(); + protected abstract void SetWord(ushort value); - protected abstract Register16 GetWordPaged(byte page, byte offset); - protected abstract void SetWordPaged(byte page, byte offset, Register16 value); + protected abstract ushort GetWordPaged(byte page, byte offset); + protected abstract void SetWordPaged(byte page, byte offset, ushort value); - protected abstract Register16 FetchWord(); + protected abstract ushort FetchWord(); protected abstract void Push(byte value); protected abstract byte Pop(); - protected abstract void PushWord(Register16 value); - protected abstract Register16 PopWord(); + protected abstract void PushWord(ushort value); + protected abstract ushort PopWord(); - protected Register16 GetWord(Register16 address) + protected ushort GetWord(ushort address) { - Bus.Address.Word = address.Word; + Bus.Address = address; return GetWord(); } - protected void SetWord(Register16 address, Register16 value) + protected void SetWord(ushort address, ushort value) { - Bus.Address.Word = address.Word; + Bus.Address = address; SetWord(value); } - protected void Jump(Register16 destination) + protected void Jump(ushort destination) { PC = destination; } - protected void Call(Register16 destination) + protected void Call(ushort destination) { PushWord(PC); Jump(destination); diff --git a/EightBit/Register16.cs b/EightBit/Register16.cs deleted file mode 100644 index 535dd8b..0000000 --- a/EightBit/Register16.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace EightBit -{ - public sealed class Register16 - { - private byte low; - private byte high; - - public Register16() { } - - public Register16(ushort value) => Word = value; - - public Register16(byte lowValue, byte highValue) - { - Low = lowValue; - High = highValue; - } - - public byte Low { get => low; set => low = value; } - public byte High { get => high; set => high = value; } - - public ushort Word - { - get - { - return (ushort)(Chip.PromoteByte(high) | low); - } - - set - { - high = Chip.DemoteByte(value); - low = Chip.LowByte(value); - } - } - - public static implicit operator ushort(Register16 value) { return ToUInt16(value); } - - public static Register16 operator ++(Register16 left) => Increment(left); - public static Register16 operator --(Register16 left) => Decrement(left); - - public static bool operator ==(Register16 left, Register16 right) => Equals(left, right); - public static bool operator !=(Register16 left, Register16 right) => !(left == right); - - public static Register16 operator +(Register16 left, Register16 right) => Add(left, right); - public static Register16 operator -(Register16 left, Register16 right) => Subtract(left, right); - - public static ushort ToUInt16(Register16 value) { return value.Word; } - public ushort ToUInt16() { return ToUInt16(this); } - - public static Register16 Increment(Register16 left) - { - ++left.Word; - return left; - } - - public static Register16 Decrement(Register16 left) - { - --left.Word; - return left; - } - - public static Register16 Add(Register16 left, Register16 right) - { - left.Word += right.Word; - return left; - } - - public static Register16 Subtract(Register16 left, Register16 right) - { - left.Word -= right.Word; - return left; - } - - public override int GetHashCode() => Word; - - public override bool Equals(object obj) - { - var register = obj as Register16; - return Equals(register); - } - - public bool Equals(Register16 register) - { - return register != null && low == register.low && high == register.high; - } - } -} diff --git a/M6502/Disassembly.cs b/M6502/Disassembly.cs index 9277c61..9f78379 100644 --- a/M6502/Disassembly.cs +++ b/M6502/Disassembly.cs @@ -28,7 +28,7 @@ output.Append(" "); var next = bus.Peek((ushort)(current + 1)); - var relative = (ushort)(processor.PC.Word + 2 + (sbyte)next); + var relative = (ushort)(processor.PC + 2 + (sbyte)next); var aaa = (cell & 0b11100000) >> 5; var bbb = (cell & 0b00011100) >> 2; diff --git a/M6502/M6502.cs b/M6502/M6502.cs index c447c2f..ff9b5ae 100644 --- a/M6502/M6502.cs +++ b/M6502/M6502.cs @@ -14,7 +14,7 @@ private byte s; private byte p; - private Register16 intermediate; + private ushort intermediate; private bool handlingRESET; private bool handlingNMI; @@ -28,7 +28,6 @@ public M6502(Bus bus) : base(bus) { - intermediate = new Register16(); } public event EventHandler ExecutingInstruction; @@ -502,8 +501,8 @@ var software = !hardware; if (reset) { - DummyPush(PC.High); - DummyPush(PC.Low); + DummyPush(HighByte(PC)); + DummyPush(LowByte(PC)); DummyPush(P); } else @@ -521,67 +520,66 @@ { Tick(); Bus.Data = value; - Bus.Address.Low = S--; - Bus.Address.High = 1; + Bus.Address = MakeWord(S--, 1); } // Addressing modes - private Register16 Address_Absolute() => FetchWord(); + private ushort Address_Absolute() => FetchWord(); private byte Address_ZeroPage() => FetchByte(); - private Register16 Address_ZeroPageIndirect() => GetWordPaged(0, Address_ZeroPage()); + private ushort Address_ZeroPageIndirect() => GetWordPaged(0, Address_ZeroPage()); - private Register16 Address_Indirect() + private ushort Address_Indirect() { var address = Address_Absolute(); - return GetWordPaged(address.High, address.Low); + return GetWordPaged(HighByte(address), LowByte(address)); } private byte Address_ZeroPageX() { var address = Address_ZeroPage(); BusRead(address); - return (byte)LowByte(address + X); + return LowByte(address + X); } private byte Address_ZeroPageY() { var address = Address_ZeroPage(); BusRead(address); - return (byte)LowByte(address + Y); + return LowByte(address + Y); } - private Tuple Address_AbsoluteX() + private Tuple Address_AbsoluteX() { var address = Address_Absolute(); - var page = address.High; - address.Word += X; - return new Tuple(address, page); + var page = HighByte(address); + address += X; + return new Tuple(address, page); } - private Tuple Address_AbsoluteY() + private Tuple Address_AbsoluteY() { var address = Address_Absolute(); - var page = address.High; - address.Word += Y; - return new Tuple(address, page); + var page = HighByte(address); + address += Y; + return new Tuple(address, page); } - private Register16 Address_IndexedIndirectX() => GetWordPaged(0, Address_ZeroPageX()); + private ushort Address_IndexedIndirectX() => GetWordPaged(0, Address_ZeroPageX()); - private Tuple Address_IndirectIndexedY() + private Tuple Address_IndirectIndexedY() { var address = Address_ZeroPageIndirect(); - var page = address.High; - address.Word += Y; - return new Tuple(address, page); + var page = HighByte(address); + address += Y; + return new Tuple(address, page); } - private Register16 Address_relative_byte() + private ushort Address_relative_byte() { - intermediate.Word = (ushort)(PC + (byte)FetchByte()); + intermediate = (ushort)(PC + (byte)FetchByte()); return intermediate; } @@ -598,8 +596,8 @@ var crossed = Address_AbsoluteX(); var address = crossed.Item1; var page = crossed.Item2; - var possible = GetBytePaged(page, address.Low); - if ((behaviour == PageCrossingBehavior.AlwaysReadTwice) || (page != address.High)) + var possible = GetBytePaged(page, LowByte(address)); + if ((behaviour == PageCrossingBehavior.AlwaysReadTwice) || (page != HighByte(address))) possible = BusRead(address); return possible; } @@ -609,8 +607,8 @@ var crossed = Address_AbsoluteY(); var address = crossed.Item1; var page = crossed.Item2; - var possible = GetBytePaged(page, address.Low); - if (page != address.High) + var possible = GetBytePaged(page, LowByte(address)); + if (page != HighByte(address)) possible = BusRead(address); return possible; } @@ -626,8 +624,8 @@ var crossed = Address_IndirectIndexedY(); var address = crossed.Item1; var page = crossed.Item2; - var possible = GetBytePaged(page, address.Low); - if (page != address.High) + var possible = GetBytePaged(page, LowByte(address)); + if (page != HighByte(address)) possible = BusRead(address); return possible; } @@ -657,10 +655,10 @@ var destination = Address_relative_byte(); if (condition) { BusRead(); - var page = PC.High; + var page = HighByte(PC); Jump(destination); - if (PC.High != page) - BusRead(PC.Low, page); + if (HighByte(PC) != page) + BusRead(LowByte(PC), page); } } @@ -686,9 +684,9 @@ var returned = SUB(operand, data, ~P & (int)StatusBits.CF); var difference = intermediate; - AdjustNZ(difference.Low); - SetFlag(ref p, StatusBits.VF, (operand ^ data) & (operand ^ difference.Low) & (int)StatusBits.NF); - ClearFlag(ref p, StatusBits.CF, difference.High); + AdjustNZ(LowByte(difference)); + SetFlag(ref p, StatusBits.VF, (operand ^ data) & (operand ^ LowByte(difference)) & (int)StatusBits.NF); + ClearFlag(ref p, StatusBits.CF, HighByte(difference)); return returned; } @@ -700,13 +698,13 @@ private byte SUB_b(byte operand, byte data, int borrow) { - intermediate.Word = (ushort)(operand - data - borrow); - return intermediate.Low; + intermediate = (ushort)(operand - data - borrow); + return LowByte(intermediate); } private byte SUB_d(byte operand, byte data, int borrow) { - intermediate.Word = (ushort)(operand - data - borrow); + intermediate = (ushort)(operand - data - borrow); byte low = (byte)(LowNibble(operand) - LowNibble(data) - borrow); var lowNegative = low & (byte)StatusBits.NF; @@ -724,7 +722,7 @@ private byte ADC(byte operand, byte data) { var returned = ADD(operand, data, Carry); - AdjustNZ(intermediate.Low); + AdjustNZ(LowByte(intermediate)); return returned; } @@ -735,17 +733,17 @@ private byte ADD_b(byte operand, byte data, int carry) { - intermediate.Word = (ushort)(operand + data + carry); + intermediate = (ushort)(operand + data + carry); - SetFlag(ref p, StatusBits.VF, ~(operand ^ data) & (operand ^ intermediate.Low) & (int)StatusBits.NF); - SetFlag(ref p, StatusBits.CF, intermediate.High & (int)StatusBits.CF); + SetFlag(ref p, StatusBits.VF, ~(operand ^ data) & (operand ^ LowByte(intermediate)) & (int)StatusBits.NF); + SetFlag(ref p, StatusBits.CF, HighByte(intermediate) & (int)StatusBits.CF); - return intermediate.Low; + return LowByte(intermediate); } private byte ADD_d(byte operand, byte data, int carry) { - intermediate.Word = (ushort)(operand + data + carry); + intermediate = (ushort)(operand + data + carry); byte low = (byte)(LowNibble(operand) + LowNibble(data) + carry); if (low > 9) @@ -779,9 +777,9 @@ private void CMP(byte first, byte second) { - intermediate.Word = (ushort)(first - second); - AdjustNZ(intermediate.Low); - ClearFlag(ref p, StatusBits.CF, intermediate.High); + intermediate = (ushort)(first - second); + AdjustNZ(LowByte(intermediate)); + ClearFlag(ref p, StatusBits.CF, HighByte(intermediate)); } private byte DEC(byte value) => Through(value - 1); @@ -795,8 +793,7 @@ var low = FetchByte(); GetBytePaged(1, S); // dummy read PushWord(PC); - PC.High = FetchByte(); - PC.Low = low; + PC = MakeWord(low, FetchByte()); } private byte LSR(byte value) @@ -866,7 +863,7 @@ private void AXS(byte value) { X = Through(SUB((byte)(A & X), value)); - ClearFlag(ref p, StatusBits.CF, intermediate.High); + ClearFlag(ref p, StatusBits.CF, HighByte(intermediate)); } private void DCP(byte value) @@ -912,7 +909,7 @@ var crossed = Address_AbsoluteX(); var address = crossed.Item1; var page = crossed.Item2; - GetBytePaged(page, address.Low); + GetBytePaged(page, LowByte(address)); BusWrite(address, A); } @@ -921,7 +918,7 @@ var crossed = Address_AbsoluteY(); var address = crossed.Item1; var page = crossed.Item2; - GetBytePaged(page, address.Low); + GetBytePaged(page, LowByte(address)); BusWrite(address, A); } } diff --git a/Test_M6502/Board.cs b/Test_M6502/Board.cs index 49ebb88..7ef3613 100644 --- a/Test_M6502/Board.cs +++ b/Test_M6502/Board.cs @@ -11,8 +11,7 @@ private readonly Symbols symbols; private readonly Disassembly disassembler; - private Register16 oldPC; - private bool stopped; + private ushort oldPC; public Board(Configuration configuration) { @@ -22,8 +21,7 @@ this.symbols = new Symbols(); this.disassembler = new Disassembly(this, this.cpu, this.symbols); - this.oldPC = new Register16((ushort)Mask.Mask16); - this.stopped = false; + this.oldPC = (ushort)Mask.Mask16; } public M6502 CPU { get { return this.cpu; } } @@ -50,7 +48,7 @@ var programFilename = configuration.Program; var programPath = configuration.RomDirectory + "/" + configuration.Program; var loadAddress = configuration.LoadAddress; - ram.Load(programPath, loadAddress.Word); + ram.Load(programPath, loadAddress); if (configuration.DebugMode) CPU.ExecutingInstruction += CPU_ExecutingInstruction; @@ -58,8 +56,8 @@ CPU.ExecutedInstruction += CPU_ExecutedInstruction; Poke(0x00, 0x4c); - Poke(0x01, configuration.StartAddress.Low); - Poke(0x02, configuration.StartAddress.High); + Poke(0x01, Chip.LowByte(configuration.StartAddress)); + Poke(0x02, Chip.HighByte(configuration.StartAddress)); } private void CPU_ExecutedInstruction(object sender, System.EventArgs e) @@ -110,12 +108,12 @@ output.Append(Disassembly.Dump_ByteValue(CPU.S)); output.Append("\t"); - output.Append(disassembler.Disassemble(address.Word)); + output.Append(disassembler.Disassemble(address)); System.Console.Out.WriteLine(output.ToString()); } - public override MemoryMapping Mapping(Register16 absolute) + public override MemoryMapping Mapping(ushort absolute) { return new MemoryMapping(ram, 0x0000, (ushort)Mask.Mask16, AccessLevel.ReadWrite); } diff --git a/Test_M6502/Configuration.cs b/Test_M6502/Configuration.cs index 92fd107..ddec842 100644 --- a/Test_M6502/Configuration.cs +++ b/Test_M6502/Configuration.cs @@ -5,8 +5,8 @@ internal class Configuration { private bool debugMode = false; - private readonly Register16 loadAddress = new Register16(0x400); - private readonly Register16 startAddress = new Register16(0x400); + private readonly ushort loadAddress = 0x400; + private readonly ushort startAddress = 0x400; private readonly string romDirectory = "roms"; private readonly string program = "6502_functional_test.bin"; @@ -17,8 +17,8 @@ set { debugMode = value; } } - public Register16 LoadAddress { get { return loadAddress; } } - public Register16 StartAddress { get { return startAddress; } } + public ushort LoadAddress { get { return loadAddress; } } + public ushort StartAddress { get { return startAddress; } } public string RomDirectory { get { return romDirectory; } } public string Program { get { return program; } } diff --git a/UnitTestEightBit/ChipUnitTest.cs b/UnitTestEightBit/ChipUnitTest.cs new file mode 100644 index 0000000..909797a --- /dev/null +++ b/UnitTestEightBit/ChipUnitTest.cs @@ -0,0 +1,253 @@ +namespace UnitTestEightBit +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using EightBit; + + [TestClass] + public class ChipUnitTest + { + #region LowByte + + [TestMethod] + public void TestLowByte() + { + ushort input = 0xf00f; + byte low = Chip.LowByte(input); + Assert.AreEqual(0xf, low); + } + + #endregion + + #region HighByte + + [TestMethod] + public void TestHighByte() + { + ushort input = 0xf00f; + byte high = Chip.HighByte(input); + Assert.AreEqual(0xf0, high); + } + + #endregion + + #region ClearFlag + + [TestMethod] + public void TestClearFlag() + { + byte flags = 0xff; + EightBit.Chip.ClearFlag(ref flags, 0x80); + Assert.AreEqual(0x7f, flags); + } + + [TestMethod] + public void TestClearFlagNonZero() + { + byte flags = 0xff; + EightBit.Chip.ClearFlag(ref flags, 0x80, 1); + Assert.AreEqual(0x7f, flags); + } + + [TestMethod] + public void TestClearFlagZero() + { + byte flags = 0xff; + EightBit.Chip.ClearFlag(ref flags, 0x80, 0); + Assert.AreEqual(0xff, flags); + } + + [TestMethod] + public void TestClearFlagFalse() + { + byte flags = 0xff; + EightBit.Chip.ClearFlag(ref flags, 0x80, false); + Assert.AreEqual(0xff, flags); + } + + [TestMethod] + public void TestClearFlagTrue() + { + byte flags = 0xff; + EightBit.Chip.ClearFlag(ref flags, 0x80, true); + Assert.AreEqual(0x7f, flags); + } + + #endregion + + #region SetFlag + + [TestMethod] + public void TestSetFlag() + { + byte flags = 0x7f; + EightBit.Chip.SetFlag(ref flags, 0x80); + Assert.AreEqual(0xff, flags); + } + + [TestMethod] + public void TestSetFlagNonZero() + { + byte flags = 0x7f; + EightBit.Chip.SetFlag(ref flags, 0x80, 1); + Assert.AreEqual(0xff, flags); + } + + [TestMethod] + public void TestSetFlagZero() + { + byte flags = 0x7f; + EightBit.Chip.SetFlag(ref flags, 0x80, 0); + Assert.AreEqual(0x7f, flags); + } + + [TestMethod] + public void TestSetFlagFalse() + { + byte flags = 0x7f; + EightBit.Chip.SetFlag(ref flags, 0x80, false); + Assert.AreEqual(0x7f, flags); + } + + [TestMethod] + public void TestSetFlagTrue() + { + byte flags = 0x7f; + EightBit.Chip.SetFlag(ref flags, 0x80, true); + Assert.AreEqual(0xff, flags); + } + + #endregion + + #region LowerPart + + [TestMethod] + public void TestLowerPart() + { + ushort input = 0xf00f; + ushort lower = Chip.LowerPart(input); + Assert.AreEqual(0xf, lower); + } + + #endregion + + #region HigherPart + + [TestMethod] + public void TestHigherPart() + { + ushort input = 0xf00f; + ushort higher = Chip.HigherPart(input); + Assert.AreEqual(0xf000, higher); + } + + #endregion + + #region DemoteByte + + [TestMethod] + public void TestDemoteByte() + { + ushort input = 0xf00f; + byte demoted = Chip.DemoteByte(input); + Assert.AreEqual(0xf0, demoted); + } + + #endregion + + #region PromoteByte + + [TestMethod] + public void TestPromoteByte() + { + byte input = 0xf0; + ushort promoted = Chip.PromoteByte(input); + Assert.AreEqual(0xf000, promoted); + } + + #endregion + + #region LowNibble + + [TestMethod] + public void TestLowNibble() + { + byte input = 0xab; + int nibble = Chip.LowNibble(input); + Assert.AreEqual(0xb, nibble); + } + + #endregion + + #region HighNibble + + [TestMethod] + public void TestHighNibble() + { + byte input = 0xab; + int nibble = Chip.HighNibble(input); + Assert.AreEqual(0xa, nibble); + } + + #endregion + + #region DemoteNibble + + [TestMethod] + public void TestDemoteNibble() + { + byte input = 0xab; + int nibble = Chip.DemoteNibble(input); + Assert.AreEqual(0xa, nibble); + } + + #endregion + + #region PromoteNibble + + [TestMethod] + public void TestPromoteNibble() + { + byte input = 0xab; + int nibble = Chip.PromoteNibble(input); + Assert.AreEqual(0xb0, nibble); + } + + #endregion + + #region HigherNibble + + [TestMethod] + public void TestHigherNibble() + { + byte input = 0xab; + int nibble = Chip.HigherNibble(input); + Assert.AreEqual(0xa0, nibble); + } + + #endregion + + #region LowerNibble + + [TestMethod] + public void TestLowerNibble() + { + byte input = 0xab; + int nibble = Chip.LowerNibble(input); + Assert.AreEqual(0xb, nibble); + } + + #endregion + + #region MakeWord + + [TestMethod] + public void TestMakeWord() + { + ushort word = Chip.MakeWord(0xcd, 0xab); + Assert.AreEqual(0xabcd, word); + } + + #endregion + + } +} diff --git a/UnitTestEightBit/Properties/AssemblyInfo.cs b/UnitTestEightBit/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b028aa9 --- /dev/null +++ b/UnitTestEightBit/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("UnitTestEightBit")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTestEightBit")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("aa8282a3-212a-42de-b778-bbaeb7e6d986")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/UnitTestEightBit/UnitTestEightBit.csproj b/UnitTestEightBit/UnitTestEightBit.csproj new file mode 100644 index 0000000..ae451be --- /dev/null +++ b/UnitTestEightBit/UnitTestEightBit.csproj @@ -0,0 +1,74 @@ + + + + + + Debug + AnyCPU + {AA8282A3-212A-42DE-B778-BBAEB7E6D986} + Library + Properties + UnitTestEightBit + UnitTestEightBit + v4.7.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + {6ebf8857-62a3-4ef4-af21-c1844031d7e4} + EightBit + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/UnitTestEightBit/packages.config b/UnitTestEightBit/packages.config new file mode 100644 index 0000000..2f7c5a1 --- /dev/null +++ b/UnitTestEightBit/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file