diff --git a/AGC/AGC.csproj b/AGC/AGC.csproj index 07743e2..fb5cf8b 100644 --- a/AGC/AGC.csproj +++ b/AGC/AGC.csproj @@ -48,6 +48,7 @@ + diff --git a/AGC/Base/DoublePrecision.cs b/AGC/Base/DoublePrecision.cs index 56fa503..e284504 100644 --- a/AGC/Base/DoublePrecision.cs +++ b/AGC/Base/DoublePrecision.cs @@ -5,7 +5,40 @@ namespace Apollo.Virtual.AGC.Base { + // https://github.com/rburkey2005/virtualagc/blob/master/yaAGC/agc_engine.c public class DoublePrecision { + public SinglePrecision MostSignificantWord { get; protected set; } + public SinglePrecision LeastSignificantWord { get; protected set; } + + public DoublePrecision(ushort most, ushort least) + { + MostSignificantWord = new SinglePrecision(most); + LeastSignificantWord = new SinglePrecision(least); + } + + /// + /// Double Precision Addition + /// + /// left operand + /// right operand + /// + public static DoublePrecision operator +(DoublePrecision left, DoublePrecision right) + { + // single preceision add the least significant word and most significant word + var lsw = left.LeastSignificantWord + right.LeastSignificantWord; + var msw = left.MostSignificantWord + right.MostSignificantWord; + + // check for overflow and adjust + if (lsw.IsPositiveOverflow) + msw += OnesCompliment.PositiveOne; + else if (lsw.IsNegativeOverflow) + msw += OnesCompliment.NegativeOne; + + lsw.OverflowCorrect(); + + + return new DoublePrecision(msw, lsw); + } } } diff --git a/AGC/Base/OnesCompliment.cs b/AGC/Base/OnesCompliment.cs index f472961..fbf1b7a 100644 --- a/AGC/Base/OnesCompliment.cs +++ b/AGC/Base/OnesCompliment.cs @@ -8,6 +8,7 @@ namespace Apollo.Virtual.AGC.Base public class OnesCompliment { public const ushort NegativeZero = 0xFFFF; + public const ushort PositiveZero = 0x0000; public const ushort PositiveOne = 0x0001; public const ushort NegativeOne = 0xFFFE; diff --git a/AGC/Base/SinglePrecision.cs b/AGC/Base/SinglePrecision.cs index ec15d06..91a11fa 100644 --- a/AGC/Base/SinglePrecision.cs +++ b/AGC/Base/SinglePrecision.cs @@ -17,6 +17,22 @@ public SinglePrecision(int v) { } + public bool IsPositiveOverflow + { + get + { + return (Value & 0xC000) == 0x4000; + } + } + + public bool IsNegativeOverflow + { + get + { + return (Value & 0xC000) == 0x8000; + } + } + /// /// Single Precision Addition /// diff --git a/AGC/Instructions/DoubleAddToStorage.cs b/AGC/Instructions/DoubleAddToStorage.cs new file mode 100644 index 0000000..d3dace7 --- /dev/null +++ b/AGC/Instructions/DoubleAddToStorage.cs @@ -0,0 +1,52 @@ +using Apollo.Virtual.AGC.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Apollo.Virtual.AGC.Instructions +{ + /// + /// DAS - 0010 00 + /// QuarterCode Instruction + /// + /// A double-precision (DP) add of the A,L register pair to a pair of variables in erasable memory + /// + class DoubleAddToStorage : IInstruction + { + public ushort Code + { + get { return 0x00; } + } + + public Processor CPU { get; set; } + + public void Execute(ushort K) + { + // find previous address + var K0 = (ushort)(K - 1); + + // read DP values from registers and memroy + var dp1 = new DoublePrecision(CPU.A.Read(), CPU.L.Read()); + var dp2 = new DoublePrecision(CPU.Memory[K0], CPU.Memory[K]); + + // create sum + var sum = dp1 + dp2; + + // store result in memory + CPU.Memory[K0] = sum.MostSignificantWord; + CPU.Memory[K] = sum.LeastSignificantWord; + + // L always cleared to +0 + CPU.L.Write(OnesCompliment.PositiveZero); + + // A set based upon overflow + if(sum.MostSignificantWord.IsPositiveOverflow) + CPU.A.Write(OnesCompliment.PositiveOne); + else if(sum.MostSignificantWord.IsNegativeOverflow) + CPU.A.Write(OnesCompliment.NegativeOne); + else + CPU.A.Write(OnesCompliment.PositiveZero); + } + } +} diff --git a/AGC/Instructions/QuarterCode2.cs b/AGC/Instructions/QuarterCode2.cs index d3a6482..1d57b94 100644 --- a/AGC/Instructions/QuarterCode2.cs +++ b/AGC/Instructions/QuarterCode2.cs @@ -16,6 +16,7 @@ class QuarterCode2 : ExtraInstructionList { public QuarterCode2(Processor cpu) : base(cpu, 3) { + Add(new DoubleAddToStorage()); Add(new AddToStorage()); } diff --git a/Tests/AGC.Tests/AGC.Tests.csproj b/Tests/AGC.Tests/AGC.Tests.csproj index f46dbe7..a3db25e 100644 --- a/Tests/AGC.Tests/AGC.Tests.csproj +++ b/Tests/AGC.Tests/AGC.Tests.csproj @@ -59,6 +59,7 @@ + diff --git a/Tests/AGC.Tests/Instructions/DoubleAddToStorage.cs b/Tests/AGC.Tests/Instructions/DoubleAddToStorage.cs new file mode 100644 index 0000000..5887c7f --- /dev/null +++ b/Tests/AGC.Tests/Instructions/DoubleAddToStorage.cs @@ -0,0 +1,83 @@ +using Apollo.Virtual.AGC.Base; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AGC.Tests.Instructions +{ + [TestClass] + public class DoubleAddToStorage : BaseTest + { + [TestMethod] + public void DoubleAddToStorage_LswValue() + { + // arrange + Memory[0x0] = 0x0; + Memory[0x1] = OnesCompliment.PositiveOne; + + Memory[0x200] = 0x0; + Memory[0x201] = OnesCompliment.PositiveOne; + + Memory.LoadFixedRom(new ushort[] { + Instruction(0x02, 0x201) + }); + + // act + CPU.Execute(); + + // assert + Assert.AreEqual(2, Memory[0x201]); + Assert.AreEqual(0, Memory[0x000]); + } + + [TestMethod] + public void DoubleAddToStorage_LswPositiveOverflow() + { + // arrange + Memory[0x0] = 0x0; + Memory[0x1] = 0x3FFF; + + Memory[0x200] = 0x0; + Memory[0x201] = OnesCompliment.PositiveOne; + + Memory.LoadFixedRom(new ushort[] { + Instruction(0x02, 0x201) + }); + + // act + CPU.Execute(); + + // assert + Assert.AreEqual(1, Memory[0x200]); + Assert.AreEqual(0, Memory[0x201]); + Assert.AreEqual(0, Memory[0x000]); + } + + [TestMethod] + public void DoubleAddToStorage_MswPositiveOverflow() + { + // arrange + Memory[0x0] = 0x3FFF; + Memory[0x1] = 0x3FFF; + + Memory[0x200] = 0x0; + Memory[0x201] = OnesCompliment.PositiveOne; + + Memory.LoadFixedRom(new ushort[] { + Instruction(0x02, 0x201) + }); + + // act + CPU.Execute(); + + // assert + Assert.AreEqual(0x0000, Memory[0x200]); + Assert.AreEqual(0x0000, Memory[0x201]); + Assert.AreEqual(OnesCompliment.PositiveOne, Memory[0x000]); + Assert.AreEqual(OnesCompliment.PositiveZero, Memory[0x001]); + } + } +}