summaryrefslogtreecommitdiff
path: root/Q1.core
diff options
context:
space:
mode:
Diffstat (limited to 'Q1.core')
-rw-r--r--Q1.core/Arch/AddressingMode.cs13
-rw-r--r--Q1.core/Arch/AddressingModes.cs331
-rw-r--r--Q1.core/Arch/Chip.cs143
-rw-r--r--Q1.core/Arch/ChipLayout.cs12
-rw-r--r--Q1.core/Arch/IInstruction.cs35
-rw-r--r--Q1.core/Arch/InstructionSet.cs203
-rw-r--r--Q1.core/GlobalUsings.cs12
-rw-r--r--Q1.core/IAddressable.cs26
-rw-r--r--Q1.core/Memory.cs15
-rw-r--r--Q1.core/Q1.core.csproj9
10 files changed, 799 insertions, 0 deletions
diff --git a/Q1.core/Arch/AddressingMode.cs b/Q1.core/Arch/AddressingMode.cs
new file mode 100644
index 0000000..190439b
--- /dev/null
+++ b/Q1.core/Arch/AddressingMode.cs
@@ -0,0 +1,13 @@
+namespace Q1.core.Arch;
+
+public record struct AddressingMode
+{
+ public string Name;
+ public u8 SizeB;
+ public u8 SizeW;
+
+ public Func<Chip, u8> LoadByte;
+ public Func<Chip, u16> LoadWord;
+ public Action<Chip, u8> StoreByte;
+ public Action<Chip, u16> StoreWord;
+} \ No newline at end of file
diff --git a/Q1.core/Arch/AddressingModes.cs b/Q1.core/Arch/AddressingModes.cs
new file mode 100644
index 0000000..206ee4f
--- /dev/null
+++ b/Q1.core/Arch/AddressingModes.cs
@@ -0,0 +1,331 @@
+namespace Q1.core.Arch;
+
+public static class AddressingModes
+{
+ public static AddressingMode[] Lookup = new AddressingMode[8];
+
+ static AddressingModes()
+ {
+ AddressingModes.Lookup[0x00] = new AddressingMode
+ {
+ Name = "Implicit",
+ SizeB = 0,
+ SizeW = 0,
+ LoadByte = AddressingModes.ImplicitLoadByte,
+ LoadWord = AddressingModes.ImplicitLoadWord,
+ StoreByte = AddressingModes.ImplicitStoreByte,
+ StoreWord = AddressingModes.ImplicitStoreWord
+ };
+
+ AddressingModes.Lookup[0x01] = new AddressingMode
+ {
+ Name = "Immediate",
+ SizeB = 1,
+ SizeW = 2,
+ LoadByte = AddressingModes.ImmediateLoadByte,
+ LoadWord = AddressingModes.ImmediateLoadWord,
+ StoreByte = AddressingModes.ImmediateStoreByte,
+ StoreWord = AddressingModes.ImmediateStoreWord
+ };
+
+ AddressingModes.Lookup[0x02] = new AddressingMode
+ {
+ Name = "Direct",
+ SizeB = 2,
+ SizeW = 2,
+ LoadByte = AddressingModes.DirectLoadByte,
+ LoadWord = AddressingModes.DirectLoadWord,
+ StoreByte = AddressingModes.DirectStoreByte,
+ StoreWord = AddressingModes.DirectStoreWord
+ };
+
+ AddressingModes.Lookup[0x03] = new AddressingMode
+ {
+ Name = "Indirect",
+ SizeB = 2,
+ SizeW = 2,
+ LoadByte = AddressingModes.IndirectLoadByte,
+ LoadWord = AddressingModes.IndirectLoadWord,
+ StoreByte = AddressingModes.IndirectStoreByte,
+ StoreWord = AddressingModes.IndirectStoreWord
+ };
+
+ AddressingModes.Lookup[0x04] = new AddressingMode
+ {
+ Name = "Relative",
+ SizeB = 2,
+ SizeW = 2,
+ LoadByte = AddressingModes.RelativeLoadByte,
+ LoadWord = AddressingModes.RelativeLoadWord,
+ StoreByte = AddressingModes.RelativeStoreByte,
+ StoreWord = AddressingModes.RelativeStoreWord
+ };
+
+ AddressingModes.Lookup[0x05] = new AddressingMode
+ {
+ Name = "Register",
+ SizeB = 1,
+ SizeW = 1,
+ LoadByte = AddressingModes.RegisterLoadByte,
+ LoadWord = AddressingModes.RegisterLoadWord,
+ StoreByte = AddressingModes.RegisterStoreByte,
+ StoreWord = AddressingModes.RegisterStoreWord
+ };
+
+ AddressingModes.Lookup[0x06] = new AddressingMode
+ {
+ Name = "Register Indirect",
+ SizeB = 1,
+ SizeW = 1,
+ LoadByte = AddressingModes.RegisterIndirectLoadByte,
+ LoadWord = AddressingModes.RegisterIndirectLoadWord,
+ StoreByte = AddressingModes.RegisterIndirectStoreByte,
+ StoreWord = AddressingModes.RegisterIndirectStoreWord
+ };
+
+ AddressingModes.Lookup[0x07] = new AddressingMode
+ {
+ Name = "Register Indirect with Offset",
+ SizeB = 3,
+ SizeW = 3,
+ LoadByte = AddressingModes.RegisterIndirectOffsetLoadByte,
+ LoadWord = AddressingModes.RegisterIndirectOffsetLoadWord,
+ StoreByte = AddressingModes.RegisterIndirectOffsetStoreByte,
+ StoreWord = AddressingModes.RegisterIndirectOffsetStoreWord
+ };
+ }
+
+ private static u8 ImplicitLoadByte(Chip chip)
+ => throw new InvalidOperationException("Cannot load from an implicit value");
+ private static u16 ImplicitLoadWord(Chip chip)
+ => throw new InvalidOperationException("Cannot load from an implicit value");
+ private static void ImplicitStoreByte(Chip chip, u8 value)
+ => throw new InvalidOperationException("Cannot store to an implicit value");
+ private static void ImplicitStoreWord(Chip chip, u16 value)
+ => throw new InvalidOperationException("Cannot store to an implicit value");
+
+
+ private static u8 ImmediateLoadByte(Chip chip)
+ {
+ u8 value = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+ return value;
+ }
+ private static u16 ImmediateLoadWord(Chip chip)
+ {
+ u16 value = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+ return value;
+ }
+ private static void ImmediateStoreByte(Chip chip, u8 value)
+ => throw new InvalidOperationException("Cannot store to an immediate value");
+ private static void ImmediateStoreWord(Chip chip, u16 value)
+ => throw new InvalidOperationException("Cannot store to an immediate value");
+
+
+ private static u8 DirectLoadByte(Chip chip)
+ {
+ u16 address = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ return chip.Bus.Read(address);
+ }
+ private static u16 DirectLoadWord(Chip chip)
+ {
+ u16 address = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ return chip.Bus.ReadWord(address);
+ }
+ private static void DirectStoreByte(Chip chip, u8 value)
+ {
+ u16 address = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ chip.Bus.Write(address, value);
+ }
+ private static void DirectStoreWord(Chip chip, u16 value)
+ {
+ u16 address = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ chip.Bus.WriteWord(address, value);
+ }
+
+
+ private static u8 IndirectLoadByte(Chip chip)
+ {
+ u16 pointer = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = chip.Bus.ReadWord(pointer);
+ return chip.Bus.Read(address);
+ }
+ private static u16 IndirectLoadWord(Chip chip)
+ {
+ u16 pointer = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = chip.Bus.ReadWord(pointer);
+ return chip.Bus.ReadWord(address);
+ }
+ private static void IndirectStoreByte(Chip chip, u8 value)
+ {
+ u16 pointer = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = chip.Bus.ReadWord(pointer);
+ chip.Bus.Write(address, value);
+ }
+ private static void IndirectStoreWord(Chip chip, u16 value)
+ {
+ u16 pointer = chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = chip.Bus.ReadWord(pointer);
+ chip.Bus.WriteWord(address, value);
+ }
+
+
+ private static u8 RelativeLoadByte(Chip chip)
+ {
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16) (chip.Pc + offset);
+ return chip.Bus.Read(address);
+ }
+ private static u16 RelativeLoadWord(Chip chip)
+ {
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16) (chip.Pc + offset);
+ return chip.Bus.ReadWord(address);
+ }
+ private static void RelativeStoreByte(Chip chip, u8 value)
+ {
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16) (chip.Pc + offset);
+ chip.Bus.Write(address, value);
+ }
+ private static void RelativeStoreWord(Chip chip, u16 value)
+ {
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16) (chip.Pc + offset);
+ chip.Bus.WriteWord(address, value);
+ }
+
+ private static u8 RegisterLoadByte(Chip chip)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ return (u8) (chip.GetRegister(regIndex) & 0xFF);
+ }
+ private static u16 RegisterLoadWord(Chip chip)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ return chip.GetRegister(regIndex);
+ }
+ private static void RegisterStoreByte(Chip chip, u8 value)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ u16 current = chip.GetRegister(regIndex);
+ u16 newValue = (u16)((current & 0xFF00) | value);
+ chip.SetRegister(regIndex, newValue);
+ }
+ private static void RegisterStoreWord(Chip chip, u16 value)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ chip.SetRegister(regIndex, value);
+ }
+
+ private static u8 RegisterIndirectLoadByte(Chip chip)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ u16 address = chip.GetRegister(regIndex);
+ return chip.Bus.Read(address);
+ }
+ private static u16 RegisterIndirectLoadWord(Chip chip)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ u16 address = chip.GetRegister(regIndex);
+ return chip.Bus.ReadWord(address);
+ }
+ private static void RegisterIndirectStoreByte(Chip chip, u8 value)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ u16 address = chip.GetRegister(regIndex);
+ chip.Bus.Write(address, value);
+ }
+ private static void RegisterIndirectStoreWord(Chip chip, u16 value)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ u16 address = chip.GetRegister(regIndex);
+ chip.Bus.WriteWord(address, value);
+ }
+
+ private static u8 RegisterIndirectOffsetLoadByte(Chip chip)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16)(chip.GetRegister(regIndex) + offset);
+ return chip.Bus.Read(address);
+ }
+ private static u16 RegisterIndirectOffsetLoadWord(Chip chip)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16)(chip.GetRegister(regIndex) + offset);
+ return chip.Bus.ReadWord(address);
+ }
+ private static void RegisterIndirectOffsetStoreByte(Chip chip, u8 value)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16)(chip.GetRegister(regIndex) + offset);
+ chip.Bus.Write(address, value);
+ }
+ private static void RegisterIndirectOffsetStoreWord(Chip chip, u16 value)
+ {
+ u8 regIndex = chip.Bus.Read(chip.Pc);
+ chip.Pc += 1;
+
+ i16 offset = (i16) chip.Bus.ReadWord(chip.Pc);
+ chip.Pc += 2;
+
+ u16 address = (u16)(chip.GetRegister(regIndex) + offset);
+ chip.Bus.WriteWord(address, value);
+ }
+} \ No newline at end of file
diff --git a/Q1.core/Arch/Chip.cs b/Q1.core/Arch/Chip.cs
new file mode 100644
index 0000000..af131dd
--- /dev/null
+++ b/Q1.core/Arch/Chip.cs
@@ -0,0 +1,143 @@
+namespace Q1.core.Arch;
+
+// Big endian
+public class Chip
+{
+ public IAddressable Bus;
+ public u16 Pc, Sp;
+ public u16 Ax, Dx, Lx;
+
+ private u16[] _registers;
+
+ public void Reset()
+ {
+ this._registers = new u16[64];
+ this.Pc = ChipLayout.PROGRAM_START;
+ this.Sp = ChipLayout.STACK_START;
+ }
+
+ public void Clock()
+ {
+ this.Bus.Clock();
+
+ this.Fetch(out u8 opcode, out u8 m1, out u8 m2, out bool word);
+
+ switch (m1, m2)
+ {
+ case (0x00, 0x00):
+ InstructionSet.Lookup[opcode].Implicit.Execute(this, word);
+ break;
+
+ case (_, 0x00):
+ InstructionSet.Lookup[opcode].SimpleAddressing.Execute(this, m1, word);
+ break;
+
+ case (0x00, 0x01):
+ InstructionSet.Lookup[opcode].ExtendedImplicit.Execute(this, word);
+ break;
+
+ case (_, _):
+ InstructionSet.Lookup[opcode].ExtendedAddressing.Execute(this, m1, m2, word);
+ break;
+ }
+ }
+
+ /// [opcode: 7 bits] [word: 1 bits] [m1: 4 bits] [m2: 4 bits]
+ public void Fetch(out u8 opcode, out u8 m1, out u8 m2, out bool word)
+ {
+ u16 instruction = this.Bus.ReadWord(this.Pc);
+ this.Pc += 2;
+
+ opcode = (u8) ((instruction >> 8) & 0xFE); // 128 opcodes
+ word = ((instruction >> 8) & 0x1) != 0; // flag for word mode
+
+ m1 = (u8) ((instruction >> 4) & 0x0F); // 16 modes
+ m2 = (u8) (instruction & 0x0F); // 16 modes
+ }
+
+ public void SkipInstruction()
+ {
+ this.Fetch(out u8 _, out u8 m1, out u8 m2, out bool word);
+ this.Pc += (u8)(Size(m1, word) + Size(m2, word));
+ }
+
+ public u16 Load(u8 mode, bool word)
+ {
+ AddressingMode am = AddressingModes.Lookup[mode];
+ return word ? am.LoadWord(this) : am.LoadByte(this);
+ }
+
+ public void Store(u8 mode, u16 value, bool word)
+ {
+ AddressingMode am = AddressingModes.Lookup[mode];
+ if (word) am.StoreWord(this, value);
+ else am.StoreByte(this, (u8)(value & 0xFF));
+ }
+
+ public u8 Size(u8 mode, bool word)
+ {
+ AddressingMode am = AddressingModes.Lookup[mode];
+ return word ? am.SizeW : am.SizeB;
+ }
+
+ public void Push(u16 value)
+ {
+ if (this.Sp + 2 > ChipLayout.STACK_START + ChipLayout.STACK_SIZE)
+ throw new InvalidOperationException("Stack overflow");
+
+ this.Bus.WriteWord(this.Sp, value);
+
+ this.Sp += 2;
+ }
+
+ public u16 Pop()
+ {
+ if (this.Sp - 2 < ChipLayout.STACK_START)
+ throw new InvalidOperationException("Stack underflow");
+
+ this.Sp -= 2;
+ u16 value = this.Bus.ReadWord(this.Sp);
+
+ return value;
+ }
+
+ public u16 GetRegister(u8 index)
+ {
+ if (index < 64) return this._registers[index];
+
+ if (index == 0x40) return this.Ax;
+ if (index == 0x41) return (u16) (this.Ax >> 8); // High byte of Ax
+ if (index == 0x42) return (u16) (this.Ax & 0xFF); // Low byte of Ax
+
+ if (index == 0x44) return this.Dx;
+ if (index == 0x45) return (u16) (this.Dx >> 8); // High byte of Dx
+ if (index == 0x46) return (u16) (this.Dx & 0xFF); // Low byte of Dx
+
+ if (index == 0x48) return this.Lx;
+
+ if (index == 0x4C) return this.Pc;
+ if (index == 0x4D) return this.Sp;
+
+ throw new InvalidOperationException($"Invalid register index: {index}");
+ }
+
+ public void SetRegister(u8 index, u16 value)
+ {
+ if (index < 64) this._registers[index] = value;
+
+ else if (index == 0x40) this.Ax = value;
+ else if (index == 0x41) this.Ax = (u16)((this.Ax & 0x00FF) | ((value & 0xFF) << 8)); // Set high byte of Ax
+ else if (index == 0x42) this.Ax = (u16)((this.Ax & 0xFF00) | (value & 0xFF)); // Set low byte of Ax
+
+ else if (index == 0x44) this.Dx = value;
+ else if (index == 0x45) this.Dx = (u16)((this.Dx & 0x00FF) | ((value & 0xFF) << 8)); // Set high byte of Dx
+ else if (index == 0x46) this.Dx = (u16)((this.Dx & 0xFF00) | (value & 0xFF)); // Set low byte of Dx
+
+ else if (index == 0x48) this.Lx = value;
+
+ else if (index == 0x4C) this.Pc = value;
+ else if (index == 0x4D) this.Sp = value;
+
+ else throw new InvalidOperationException($"Invalid register index: {index}");
+ }
+} \ No newline at end of file
diff --git a/Q1.core/Arch/ChipLayout.cs b/Q1.core/Arch/ChipLayout.cs
new file mode 100644
index 0000000..8c916f6
--- /dev/null
+++ b/Q1.core/Arch/ChipLayout.cs
@@ -0,0 +1,12 @@
+namespace Q1.core.Arch;
+
+public static class ChipLayout
+{
+ public const u16 MEMORY_START = 0x0000;
+ public const u16 MEMORY_SIZE = 0xFF00; // 64KB - 256 bytes reserved for stack
+
+ public const u16 STACK_START = ChipLayout.MEMORY_START;
+ public const u16 STACK_SIZE = 256 * 2; // 256 words (512 bytes)
+
+ public const u16 PROGRAM_START = ChipLayout.MEMORY_START + ChipLayout.STACK_SIZE;
+} \ No newline at end of file
diff --git a/Q1.core/Arch/IInstruction.cs b/Q1.core/Arch/IInstruction.cs
new file mode 100644
index 0000000..2889937
--- /dev/null
+++ b/Q1.core/Arch/IInstruction.cs
@@ -0,0 +1,35 @@
+namespace Q1.core.Arch;
+
+public interface IInstruction
+{
+ public string Name { get; }
+}
+
+public record struct ImplicitInstruction : IInstruction
+{
+ public string Name { get; set; }
+
+ public Action<Chip, bool> Execute;
+}
+
+public record struct SimpleInstruction : IInstruction
+{
+ public string Name { get; set; }
+
+ public Action<Chip, u8, bool> Execute;
+}
+
+public record struct ExtendedInstruction : IInstruction
+{
+ public string Name { get; set; }
+
+ public Action<Chip, u8, u8, bool> Execute;
+}
+
+public record struct InstructionGroup
+{
+ public ImplicitInstruction Implicit;
+ public SimpleInstruction SimpleAddressing;
+ public ImplicitInstruction ExtendedImplicit;
+ public ExtendedInstruction ExtendedAddressing;
+} \ No newline at end of file
diff --git a/Q1.core/Arch/InstructionSet.cs b/Q1.core/Arch/InstructionSet.cs
new file mode 100644
index 0000000..68a9b55
--- /dev/null
+++ b/Q1.core/Arch/InstructionSet.cs
@@ -0,0 +1,203 @@
+namespace Q1.core.Arch;
+
+public static class InstructionSet
+{
+ public static readonly InstructionGroup[] Lookup = new InstructionGroup[128];
+
+ static InstructionSet()
+ {
+ var nop = new ImplicitInstruction { Name = "NOP", Execute = InstructionSet.Nop };
+ var nopSimple = new SimpleInstruction { Name = "NOP", Execute = InstructionSet.Nop };
+ var nopExtended = new ExtendedInstruction { Name = "NOP", Execute = InstructionSet.Nop };
+
+ #region Control Group
+
+ InstructionSet.Lookup[0x00] = new InstructionGroup
+ {
+ Implicit = nop,
+ SimpleAddressing = new SimpleInstruction { Name = "JMP", Execute = InstructionSet.Jump },
+ ExtendedImplicit = new ImplicitInstruction { Name = "HALT", Execute = InstructionSet.Halt },
+ ExtendedAddressing = new ExtendedInstruction { Name = "MOV", Execute = InstructionSet.Move }
+ };
+
+ InstructionSet.Lookup[0x01] = new InstructionGroup
+ {
+ Implicit = new ImplicitInstruction { Name = "RET", Execute = InstructionSet.Return },
+ SimpleAddressing = new SimpleInstruction { Name = "CALL", Execute = InstructionSet.Call },
+ ExtendedImplicit = new ImplicitInstruction { Name = "SUS", Execute = InstructionSet.Suspend },
+ ExtendedAddressing = nopExtended
+ };
+
+ InstructionSet.Lookup[0x02] = new InstructionGroup
+ {
+ Implicit = new ImplicitInstruction { Name = "BZ(LX)", Execute = InstructionSet.BranchIfZeroLx },
+ SimpleAddressing = new SimpleInstruction { Name = "BZ", Execute = InstructionSet.BranchIfZero },
+ ExtendedImplicit = new ImplicitInstruction { Name = "BR", Execute = InstructionSet.Branch },
+ ExtendedAddressing = nopExtended
+ };
+
+ InstructionSet.Lookup[0x03] = new InstructionGroup
+ {
+ Implicit = nop,
+ SimpleAddressing = new SimpleInstruction { Name = "PUSH", Execute = InstructionSet.Push },
+ ExtendedImplicit = nop,
+ ExtendedAddressing = nopExtended
+ };
+
+ InstructionSet.Lookup[0x04] = new InstructionGroup
+ {
+ Implicit = nop,
+ SimpleAddressing = new SimpleInstruction { Name = "POP", Execute = InstructionSet.Pop },
+ ExtendedImplicit = nop,
+ ExtendedAddressing = nopExtended
+ };
+
+ #endregion
+
+ #region Data Group
+
+ InstructionSet.Lookup[0x10] = new InstructionGroup
+ {
+ Implicit = new ImplicitInstruction { Name = "INV(DX)", Execute = InstructionSet.InverseDx },
+ SimpleAddressing = new SimpleInstruction { Name = "INV", Execute = InstructionSet.Inverse },
+ ExtendedImplicit = nop,
+ ExtendedAddressing = new ExtendedInstruction { Name = "AND", Execute = InstructionSet.And }
+ };
+
+ InstructionSet.Lookup[0x11] = new InstructionGroup
+ {
+ Implicit = nop,
+ SimpleAddressing = nopSimple,
+ ExtendedImplicit = nop,
+ ExtendedAddressing = new ExtendedInstruction { Name = "OR", Execute = InstructionSet.Or }
+ };
+
+ InstructionSet.Lookup[0x12] = new InstructionGroup
+ {
+ Implicit = nop,
+ SimpleAddressing = nopSimple,
+ ExtendedImplicit = nop,
+ ExtendedAddressing = new ExtendedInstruction { Name = "XOR", Execute = InstructionSet.Xor }
+ };
+
+ InstructionSet.Lookup[0x13] = new InstructionGroup
+ {
+ Implicit = new ImplicitInstruction { Name = "SHPL(DX)", Execute = InstructionSet.ShiftPlaceLeftDx },
+ SimpleAddressing = new SimpleInstruction { Name = "SHPL", Execute = InstructionSet.ShiftPlaceLeft },
+ ExtendedImplicit = nop,
+ ExtendedAddressing = new ExtendedInstruction { Name = "SHL", Execute = InstructionSet.ShiftLeft }
+ };
+
+ InstructionSet.Lookup[0x14] = new InstructionGroup
+ {
+ Implicit = new ImplicitInstruction { Name = "SHPR(DX)", Execute = InstructionSet.ShiftPlaceRightDx },
+ SimpleAddressing = new SimpleInstruction { Name = "SHPR", Execute = InstructionSet.ShiftPlaceRight },
+ ExtendedImplicit = nop,
+ ExtendedAddressing = new ExtendedInstruction { Name = "SHL", Execute = InstructionSet.ShiftRight }
+ };
+
+ #endregion
+ }
+
+ private static void Nop(Chip chip, bool word) { /* No operation */ }
+ private static void Nop(Chip chip, u8 m1, bool word) { /* No operation */ }
+ private static void Nop(Chip chip, u8 m1, u8 m2, bool word) { /* No operation */ }
+
+ #region Control Instructions
+
+ private static void Halt(Chip chip, bool word)
+ {
+ throw new NotImplementedException("HALT instruction is not implemented yet.");
+ }
+
+ private static void Suspend(Chip chip, bool word)
+ {
+ throw new NotImplementedException("SUS instruction is not implemented yet.");
+ }
+
+ private static void Move(Chip chip, u8 m1, u8 m2, bool word)
+ {
+ u16 value = chip.Load(m1, word);
+ chip.Store(m2, value, word);
+ }
+
+ private static void Jump(Chip chip, u8 m1, bool word)
+ {
+ u16 address = chip.Load(m1, word);
+ chip.Pc = address;
+ }
+
+ private static void Call(Chip chip, u8 m1, bool word)
+ {
+ u16 targetAddress = chip.Load(m1, word);
+ chip.Push(chip.Pc);
+ chip.Pc = targetAddress;
+ }
+
+ private static void Return(Chip chip, bool word)
+ {
+ u16 returnAddress = chip.Pop();
+ chip.Pc = returnAddress;
+ }
+
+ private static void BranchIfZero(Chip chip, u8 m1, bool word)
+ {
+ u16 value = chip.Load(m1, word);
+ if (value == 0)
+ chip.SkipInstruction();
+ }
+
+ private static void BranchIfZeroLx(Chip chip, bool word)
+ {
+ if (chip.Lx == 0)
+ chip.SkipInstruction();
+ }
+
+ private static void Branch(Chip chip, bool word)
+ {
+ chip.SkipInstruction();
+ }
+
+ private static void Push(Chip chip, u8 m1, bool word)
+ {
+ u16 value = chip.Load(m1, word);
+ chip.Push(value);
+ }
+
+ private static void Pop(Chip chip, u8 m1, bool word)
+ {
+ u16 value = chip.Pop();
+ chip.Store(m1, value, word);
+ }
+
+ #endregion
+
+ #region Data Instructions
+
+ private static void Inverse(Chip chip, u8 m1, bool word)
+ {
+ u16 value = chip.Load(m1, word);
+ value = (u16)~value;
+
+ chip.Dx = value;
+ }
+
+ private static void InverseDx(Chip chip, bool word)
+ {
+ chip.Dx = (u16)~chip.Dx;
+ }
+
+ #endregion
+
+ #region Logic Instructions
+
+
+
+ #endregion
+
+ #region Arithmetic Instructions
+
+
+
+ #endregion
+} \ No newline at end of file
diff --git a/Q1.core/GlobalUsings.cs b/Q1.core/GlobalUsings.cs
new file mode 100644
index 0000000..aca802e
--- /dev/null
+++ b/Q1.core/GlobalUsings.cs
@@ -0,0 +1,12 @@
+// Integer type aliases
+global using i8 = sbyte; // -128 to 127
+global using u8 = byte; // 0 to 255
+global using i16 = short; // -32,768 to 32,767
+global using u16 = ushort; // 0 to 65,535
+global using i32 = int; // -2,147,483,648 to 2,147,483,647
+global using u32 = uint; // 0 to 4,294,967,295
+global using i64 = long; // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+global using u64 = ulong; // 0 to 18,446,744,073,709,551,615
+
+global using ref8 = System.Span<byte>;
+global using data8 = byte[]; \ No newline at end of file
diff --git a/Q1.core/IAddressable.cs b/Q1.core/IAddressable.cs
new file mode 100644
index 0000000..49a697e
--- /dev/null
+++ b/Q1.core/IAddressable.cs
@@ -0,0 +1,26 @@
+namespace Q1.core;
+
+public interface IAddressable
+{
+ public u8 Read(u16 address);
+ public void Write(u16 address, u8 value);
+ public void Clock();
+}
+
+public static class AddressableExtensions
+{
+ public static u16 ReadWord(this IAddressable bus, u16 address)
+ {
+ u8 high = bus.Read(address);
+ u8 low = bus.Read((u16)(address + 1));
+ return (u16)((high << 8) | low);
+ }
+
+ public static void WriteWord(this IAddressable bus, u16 address, u16 value)
+ {
+ u8 high = (u8) (value & 0xFF);
+ u8 low = (u8)((value >> 8) & 0xFF);
+ bus.Write(address, low);
+ bus.Write((u16)(address + 1), high);
+ }
+} \ No newline at end of file
diff --git a/Q1.core/Memory.cs b/Q1.core/Memory.cs
new file mode 100644
index 0000000..d140809
--- /dev/null
+++ b/Q1.core/Memory.cs
@@ -0,0 +1,15 @@
+namespace Q1.core;
+
+public class Memory : IAddressable
+{
+ private readonly u8[] _memory;
+
+ public Memory(int size)
+ {
+ _memory = new u8[size];
+ }
+
+ public u8 Read(u16 address) => _memory[address];
+ public void Write(u16 address, u8 value) => _memory[address] = value;
+ public void Clock() { /* No timing behavior for simple memory */ }
+} \ No newline at end of file
diff --git a/Q1.core/Q1.core.csproj b/Q1.core/Q1.core.csproj
new file mode 100644
index 0000000..17b910f
--- /dev/null
+++ b/Q1.core/Q1.core.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net9.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+</Project>