From 7d20ff2a8e89e646fa79be69c8d07a512081daba Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:08:58 +1000 Subject: test: Add tests for all control instructions and base CPU implementation --- Q1.core/Arch/AddressingModes.cs | 333 ---------------------- Q1.core/Arch/Chip.cs | 75 +++-- Q1.core/Arch/ChipLayout.cs | 17 -- Q1.core/Arch/Constants/ChipLayout.cs | 17 ++ Q1.core/Arch/Constants/ChipRegisters.cs | 19 ++ Q1.core/Arch/InstructionEncoding.cs | 26 ++ Q1.core/Arch/InstructionSet.cs | 468 ------------------------------ Q1.core/Arch/Lookups/AddressingModes.cs | 344 +++++++++++++++++++++++ Q1.core/Arch/Lookups/InstructionSet.cs | 484 ++++++++++++++++++++++++++++++++ Q1.core/Exceptions.cs | 7 + 10 files changed, 942 insertions(+), 848 deletions(-) delete mode 100644 Q1.core/Arch/AddressingModes.cs delete mode 100644 Q1.core/Arch/ChipLayout.cs create mode 100644 Q1.core/Arch/Constants/ChipLayout.cs create mode 100644 Q1.core/Arch/Constants/ChipRegisters.cs create mode 100644 Q1.core/Arch/InstructionEncoding.cs delete mode 100644 Q1.core/Arch/InstructionSet.cs create mode 100644 Q1.core/Arch/Lookups/AddressingModes.cs create mode 100644 Q1.core/Arch/Lookups/InstructionSet.cs create mode 100644 Q1.core/Exceptions.cs (limited to 'Q1.core') diff --git a/Q1.core/Arch/AddressingModes.cs b/Q1.core/Arch/AddressingModes.cs deleted file mode 100644 index 96b4475..0000000 --- a/Q1.core/Arch/AddressingModes.cs +++ /dev/null @@ -1,333 +0,0 @@ -namespace Q1.core.Arch; - -using Components; - -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 index 9f5f109..fef9cea 100644 --- a/Q1.core/Arch/Chip.cs +++ b/Q1.core/Arch/Chip.cs @@ -1,6 +1,8 @@ namespace Q1.core.Arch; using Components; +using Constants; +using Lookups; // Big endian public class Chip @@ -10,10 +12,15 @@ public class Chip public u16 Ax, Dx, Lx; private u16[] _registers; + + public Chip() + { + this.Reset(); + } public void Reset() { - this._registers = new u16[64]; + this._registers = new u16[ChipRegisters.GENERIC_REGISTER_COUNT]; this.Pc = ChipLayout.PROGRAM_START; this.Sp = ChipLayout.STACK_START; } @@ -23,23 +30,25 @@ public class Chip this.Bus.Clock(); this.Fetch(out u8 opcode, out u8 m1, out u8 m2, out bool word); + + var instruction = InstructionSet.Lookup[opcode]; switch (m1, m2) { case (0x00, 0x00): - InstructionSet.Lookup[opcode].Implicit.Execute(this, word); + instruction.Implicit.Execute(this, word); break; case (_, 0x00): - InstructionSet.Lookup[opcode].SimpleAddressing.Execute(this, m1, word); + instruction.SimpleAddressing.Execute(this, m1, word); break; case (0x00, 0x01): - InstructionSet.Lookup[opcode].ExtendedImplicit.Execute(this, word); + instruction.ExtendedImplicit.Execute(this, word); break; case (_, _): - InstructionSet.Lookup[opcode].ExtendedAddressing.Execute(this, m1, m2, word); + instruction.ExtendedAddressing.Execute(this, m1, m2, word); break; } } @@ -49,12 +58,8 @@ public class Chip { 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 + InstructionEncoding.Decode(instruction, out opcode, out m1, out m2, out word); } public void SkipInstruction() @@ -105,43 +110,44 @@ public class Chip public u16 GetRegister(u8 index) { - if (index < 64) return this._registers[index]; + if (index < ChipRegisters.GENERIC_REGISTER_COUNT) 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 == ChipRegisters.AX) return this.Ax; + if (index == ChipRegisters.AH) return (u16) (this.Ax >> 8); // High byte of Ax + if (index == ChipRegisters.AL) 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 == ChipRegisters.DX) return this.Dx; + if (index == ChipRegisters.DH) return (u16) (this.Dx >> 8); // High byte of Dx + if (index == ChipRegisters.DL) return (u16) (this.Dx & 0xFF); // Low byte of Dx - if (index == 0x48) return this.Lx; + if (index == ChipRegisters.LX) return this.Lx; - if (index == 0x4C) return this.Pc; - if (index == 0x4D) return this.Sp; + if (index == ChipRegisters.PC) return this.Pc; + if (index == ChipRegisters.SP) return this.Sp; throw new InvalidOperationException($"Invalid register index: {index}"); } public void SetRegister(u8 index, u16 value) { - if (index < 64) this._registers[index] = value; + if (index < ChipRegisters.GENERIC_REGISTER_COUNT) 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 == ChipRegisters.AX) this.Ax = value; + else if (index == ChipRegisters.AH) this.Ax = (u16)((this.Ax & 0x00FF) | ((value & 0xFF) << 8)); // Set high byte of Ax + else if (index == ChipRegisters.AL) 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 == ChipRegisters.DX) this.Dx = value; + else if (index == ChipRegisters.DH) this.Dx = (u16)((this.Dx & 0x00FF) | ((value & 0xFF) << 8)); // Set high byte of Dx + else if (index == ChipRegisters.DL) this.Dx = (u16)((this.Dx & 0xFF00) | (value & 0xFF)); // Set low byte of Dx - else if (index == 0x48) this.Lx = value; + else if (index == ChipRegisters.LX) this.Lx = value; - else if (index == 0x4C) this.Pc = value; - else if (index == 0x4D) this.Sp = value; + else if (index == ChipRegisters.PC) this.Pc = value; + else if (index == ChipRegisters.SP) this.Sp = value; else throw new InvalidOperationException($"Invalid register index: {index}"); } + public void Interrupt(u16 code) { if (code >= ChipLayout.IVT_COUNT) @@ -151,4 +157,13 @@ public class Chip u16 interruptVectorAddress = (u16) (ChipLayout.IVT_START + code * 2); this.Pc = this.Bus.ReadWord(interruptVectorAddress); } + + public void RegisterInterruptHandler(u16 code, u16 handlerAddress) + { + if (code >= ChipLayout.IVT_COUNT) + throw new InvalidOperationException($"Invalid interrupt code: {code}"); + + u16 interruptVectorAddress = (u16) (ChipLayout.IVT_START + code * 2); + this.Bus.WriteWord(interruptVectorAddress, handlerAddress); + } } \ No newline at end of file diff --git a/Q1.core/Arch/ChipLayout.cs b/Q1.core/Arch/ChipLayout.cs deleted file mode 100644 index 6c7187b..0000000 --- a/Q1.core/Arch/ChipLayout.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Q1.core.Arch; - -public static class ChipLayout -{ - public const u16 MEMORY_START = 0x0000; - public const u16 MEMORY_SIZE = 0x4000; // 16KB of memory (0x0000 to 0x3FFF) - - public const u16 IVT_START = ChipLayout.MEMORY_START; - public const u16 IVT_COUNT = 128; // 128 interrupt vectors - public const u16 IVT_SIZE = ChipLayout.IVT_COUNT * 2; // Each vector is 2 bytes (u16) - - public const u16 STACK_START = ChipLayout.IVT_START + ChipLayout.IVT_SIZE; - public const u16 STACK_COUNT = 256; - public const u16 STACK_SIZE = ChipLayout.STACK_COUNT * 2; // Each stack entry is 2 bytes (u16) - - public const u16 PROGRAM_START = ChipLayout.STACK_START + ChipLayout.STACK_SIZE; -} \ No newline at end of file diff --git a/Q1.core/Arch/Constants/ChipLayout.cs b/Q1.core/Arch/Constants/ChipLayout.cs new file mode 100644 index 0000000..6990789 --- /dev/null +++ b/Q1.core/Arch/Constants/ChipLayout.cs @@ -0,0 +1,17 @@ +namespace Q1.core.Arch.Constants; + +public static class ChipLayout +{ + public const u16 MEMORY_START = 0x0000; + public const u16 MEMORY_SIZE = 0x4000; // 16KB of memory (0x0000 to 0x3FFF) + + public const u16 IVT_START = ChipLayout.MEMORY_START; + public const u16 IVT_COUNT = 128; // 128 interrupt vectors + public const u16 IVT_SIZE = ChipLayout.IVT_COUNT * 2; // Each vector is 2 bytes (u16) + + public const u16 STACK_START = ChipLayout.IVT_START + ChipLayout.IVT_SIZE; + public const u16 STACK_COUNT = 256; + public const u16 STACK_SIZE = ChipLayout.STACK_COUNT * 2; // Each stack entry is 2 bytes (u16) + + public const u16 PROGRAM_START = ChipLayout.STACK_START + ChipLayout.STACK_SIZE; +} \ No newline at end of file diff --git a/Q1.core/Arch/Constants/ChipRegisters.cs b/Q1.core/Arch/Constants/ChipRegisters.cs new file mode 100644 index 0000000..042ce32 --- /dev/null +++ b/Q1.core/Arch/Constants/ChipRegisters.cs @@ -0,0 +1,19 @@ +namespace Q1.core.Arch.Constants; + +public static class ChipRegisters +{ + public const u8 GENERIC_REGISTER_COUNT = 64; + + public const u8 AX = 0x40; + public const u8 AH = 0x41; + public const u8 AL = 0x42; + + public const u8 DX = 0x44; + public const u8 DH = 0x45; + public const u8 DL = 0x46; + + public const u8 LX = 0x48; + + public const u8 PC = 0x4C; + public const u8 SP = 0x4D; +} \ No newline at end of file diff --git a/Q1.core/Arch/InstructionEncoding.cs b/Q1.core/Arch/InstructionEncoding.cs new file mode 100644 index 0000000..479d61f --- /dev/null +++ b/Q1.core/Arch/InstructionEncoding.cs @@ -0,0 +1,26 @@ +namespace Q1.core.Arch; + +public static class InstructionEncoding +{ + public static void Decode(u16 instruction, out u8 opcode, out u8 m1, out u8 m2, out bool word) + { + word = ((instruction >> 15) & 0x1) != 0; // top bit + opcode = (u8)((instruction >> 8) & 0x7F); // 7 clean bits + + m1 = (u8)((instruction >> 4) & 0x0F); + m2 = (u8)(instruction & 0x0F); + } + + public static u16 Encode(u8 opcode, u8 m1, u8 m2, bool word) + { + u16 instruction = 0; + + instruction |= (u16)((opcode & 0x7F) << 8); // 7-bit opcode + instruction |= (u16)((word ? 1 : 0) << 15); // top bit flag + + instruction |= (u16)((m1 & 0x0F) << 4); + instruction |= (u16)(m2 & 0x0F); + + return instruction; + } +} \ No newline at end of file diff --git a/Q1.core/Arch/InstructionSet.cs b/Q1.core/Arch/InstructionSet.cs deleted file mode 100644 index 212e77e..0000000 --- a/Q1.core/Arch/InstructionSet.cs +++ /dev/null @@ -1,468 +0,0 @@ -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 - }; - - InstructionSet.Lookup[0x05] = new InstructionGroup - { - Implicit = nop, - SimpleAddressing = new SimpleInstruction { Name = "INT", Execute = InstructionSet.Interrupt }, - 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 = "SHR", Execute = InstructionSet.ShiftRight } - }; - - #endregion - - #region Logic Group - - InstructionSet.Lookup[0x20] = new InstructionGroup - { - Implicit = new ImplicitInstruction { Name = "NOT(LX)", Execute = InstructionSet.NotLx }, - SimpleAddressing = new SimpleInstruction { Name = "NOT", Execute = InstructionSet.Not }, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "CMP", Execute = InstructionSet.Compare } - }; - - InstructionSet.Lookup[0x21] = new InstructionGroup - { - Implicit = nop, - SimpleAddressing = nopSimple, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "LT", Execute = InstructionSet.LessThan } - }; - - InstructionSet.Lookup[0x22] = new InstructionGroup - { - Implicit = nop, - SimpleAddressing = nopSimple, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "GT", Execute = InstructionSet.GreaterThan } - }; - - #endregion - - #region Arithmatic Group - - InstructionSet.Lookup[0x30] = new InstructionGroup - { - Implicit = new ImplicitInstruction { Name = "INC(AX)", Execute = InstructionSet.IncrementAx }, - SimpleAddressing = new SimpleInstruction { Name = "INC", Execute = InstructionSet.Increment }, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "ADD", Execute = InstructionSet.Add } - }; - - InstructionSet.Lookup[0x31] = new InstructionGroup - { - Implicit = new ImplicitInstruction { Name = "DEC(AX)", Execute = InstructionSet.DecrementAx }, - SimpleAddressing = new SimpleInstruction { Name = "DEC", Execute = InstructionSet.Decrement }, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "SUB", Execute = InstructionSet.Subtract } - }; - - InstructionSet.Lookup[0x32] = new InstructionGroup - { - Implicit = nop, - SimpleAddressing = nopSimple, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "DIV", Execute = InstructionSet.Divide } - }; - - InstructionSet.Lookup[0x33] = new InstructionGroup - { - Implicit = nop, - SimpleAddressing = nopSimple, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "MUL", Execute = InstructionSet.Multiply } - }; - - InstructionSet.Lookup[0x34] = new InstructionGroup - { - Implicit = nop, - SimpleAddressing = nopSimple, - ExtendedImplicit = nop, - ExtendedAddressing = new ExtendedInstruction { Name = "DIV", Execute = InstructionSet.Modulo } - }; - - #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 Interrupt(Chip chip, u8 m1, bool word) - { - u16 interruptNumber = chip.Load(m1, word); - - chip.Interrupt(interruptNumber); - } - - 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; - } - - private static void And(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - u16 result = (u16)(value1 & value2); - - chip.Dx = result; - } - - private static void Or(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - u16 result = (u16)(value1 | value2); - - chip.Dx = result; - } - - private static void Xor(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - u16 result = (u16)(value1 ^ value2); - - chip.Dx = result; - } - - private static void ShiftLeft(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value = chip.Load(m1, word); - u16 shiftAmount = (u16) (chip.Load(m2, word) & 0xF); // Limit shift to 0-15 - u16 result = (u16) (value << shiftAmount); - - chip.Dx = result; - } - - private static void ShiftRight(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value = chip.Load(m1, word); - u16 shiftAmount = (u16) (chip.Load(m2, word) & 0xF); // Limit shift to 0-15 - u16 result = (u16) (value >> shiftAmount); - - chip.Dx = result; - } - - private static void ShiftPlaceLeft(Chip chip, u8 m1, bool word) - { - u16 value = chip.Load(m1, word); - u16 result = (u16) ((value << 1) | (value >> 15)); // Rotate left - - chip.Dx = result; - } - - private static void ShiftPlaceRight(Chip chip, u8 m1, bool word) - { - u16 value = chip.Load(m1, word); - u16 result = (u16) ((value >> 1) | (value << 15)); // Rotate right - - chip.Dx = result; - } - - private static void ShiftPlaceLeftDx(Chip chip, bool word) - { - u16 value = chip.Dx; - u16 result = (u16) ((value << 1) | (value >> 15)); // Rotate left - - chip.Dx = result; - } - - private static void ShiftPlaceRightDx(Chip chip, bool word) - { - u16 value = chip.Dx; - u16 result = (u16) ((value >> 1) | (value << 15)); // Rotate right - - chip.Dx = result; - } - - #endregion - - #region Logic Instructions - - private static void Not(Chip chip, u8 m1, bool word) - { - u16 value = chip.Load(m1, word); - chip.Lx = value == 0 ? (u16)1 : (u16)0; - } - - private static void NotLx(Chip chip, bool word) - { - chip.Lx = chip.Lx == 0 ? (u16)1 : (u16)0; - } - - private static void Compare(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - chip.Lx = value1 == value2 ? (u16)1 : (u16)0; - } - - private static void LessThan(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - chip.Lx = value1 < value2 ? (u16)1 : (u16)0; - } - - private static void GreaterThan(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - chip.Lx = value1 > value2 ? (u16)1 : (u16)0; - } - - #endregion - - #region Arithmetic Instructions - - private static void Increment(Chip chip, u8 m1, bool word) - { - u16 value = chip.Load(m1, word); - value++; - chip.Store(m1, value, word); - } - - private static void IncrementAx(Chip chip, bool word) - { - chip.Ax++; - } - - private static void Decrement(Chip chip, u8 m1, bool word) - { - u16 value = chip.Load(m1, word); - value--; - chip.Store(m1, value, word); - } - - private static void DecrementAx(Chip chip, bool word) - { - chip.Ax--; - } - - private static void Add(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - u16 result = (u16)(value1 + value2); - - chip.Ax = result; - } - - private static void Subtract(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - u16 result = (u16)(value1 - value2); - - chip.Ax = result; - } - - private static void Multiply(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - u16 result = (u16)(value1 * value2); - - chip.Ax = result; - } - - private static void Divide(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - if (value2 == 0) - throw new DivideByZeroException("Attempted to divide by zero."); - - u16 result = (u16)(value1 / value2); - chip.Ax = result; - } - - private static void Modulo(Chip chip, u8 m1, u8 m2, bool word) - { - u16 value1 = chip.Load(m1, word); - u16 value2 = chip.Load(m2, word); - if (value2 == 0) - throw new DivideByZeroException("Attempted to modulo by zero."); - - u16 result = (u16)(value1 % value2); - chip.Ax = result; - } - - #endregion -} \ No newline at end of file diff --git a/Q1.core/Arch/Lookups/AddressingModes.cs b/Q1.core/Arch/Lookups/AddressingModes.cs new file mode 100644 index 0000000..95e798d --- /dev/null +++ b/Q1.core/Arch/Lookups/AddressingModes.cs @@ -0,0 +1,344 @@ +namespace Q1.core.Arch.Lookups; + +using Q1.core.Components; + +public static class AddressingModes +{ + public static AddressingMode[] Lookup = new AddressingMode[8]; + + public static u8 GetAddressingMode(string name) + { + for (u8 i = 0; i < AddressingModes.Lookup.Length; i++) + { + if (AddressingModes.Lookup[i].Name == name) + return i; + } + + throw new ArgumentException($"Invalid addressing mode: {name}"); + } + + 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/Lookups/InstructionSet.cs b/Q1.core/Arch/Lookups/InstructionSet.cs new file mode 100644 index 0000000..2ea20fc --- /dev/null +++ b/Q1.core/Arch/Lookups/InstructionSet.cs @@ -0,0 +1,484 @@ +namespace Q1.core.Arch.Lookups; + +public static class InstructionSet +{ + public static readonly InstructionGroup[] Lookup = new InstructionGroup[128]; + + public static u8 GetOpcode(string instructionName) + { + for (u8 opcode = 0; opcode < InstructionSet.Lookup.Length; opcode++) + { + var group = InstructionSet.Lookup[opcode]; + if (group.Implicit.Name == instructionName || + group.SimpleAddressing.Name == instructionName || + group.ExtendedImplicit.Name == instructionName || + group.ExtendedAddressing.Name == instructionName) + { + return opcode; + } + } + throw new ArgumentException($"Instruction '{instructionName}' not found in the instruction set."); + } + + 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 + }; + + InstructionSet.Lookup[0x05] = new InstructionGroup + { + Implicit = nop, + SimpleAddressing = new SimpleInstruction { Name = "INT", Execute = InstructionSet.Interrupt }, + 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 = "SHR", Execute = InstructionSet.ShiftRight } + }; + + #endregion + + #region Logic Group + + InstructionSet.Lookup[0x20] = new InstructionGroup + { + Implicit = new ImplicitInstruction { Name = "NOT(LX)", Execute = InstructionSet.NotLx }, + SimpleAddressing = new SimpleInstruction { Name = "NOT", Execute = InstructionSet.Not }, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "CMP", Execute = InstructionSet.Compare } + }; + + InstructionSet.Lookup[0x21] = new InstructionGroup + { + Implicit = nop, + SimpleAddressing = nopSimple, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "LT", Execute = InstructionSet.LessThan } + }; + + InstructionSet.Lookup[0x22] = new InstructionGroup + { + Implicit = nop, + SimpleAddressing = nopSimple, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "GT", Execute = InstructionSet.GreaterThan } + }; + + #endregion + + #region Arithmatic Group + + InstructionSet.Lookup[0x30] = new InstructionGroup + { + Implicit = new ImplicitInstruction { Name = "INC(AX)", Execute = InstructionSet.IncrementAx }, + SimpleAddressing = new SimpleInstruction { Name = "INC", Execute = InstructionSet.Increment }, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "ADD", Execute = InstructionSet.Add } + }; + + InstructionSet.Lookup[0x31] = new InstructionGroup + { + Implicit = new ImplicitInstruction { Name = "DEC(AX)", Execute = InstructionSet.DecrementAx }, + SimpleAddressing = new SimpleInstruction { Name = "DEC", Execute = InstructionSet.Decrement }, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "SUB", Execute = InstructionSet.Subtract } + }; + + InstructionSet.Lookup[0x32] = new InstructionGroup + { + Implicit = nop, + SimpleAddressing = nopSimple, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "DIV", Execute = InstructionSet.Divide } + }; + + InstructionSet.Lookup[0x33] = new InstructionGroup + { + Implicit = nop, + SimpleAddressing = nopSimple, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "MUL", Execute = InstructionSet.Multiply } + }; + + InstructionSet.Lookup[0x34] = new InstructionGroup + { + Implicit = nop, + SimpleAddressing = nopSimple, + ExtendedImplicit = nop, + ExtendedAddressing = new ExtendedInstruction { Name = "MOD", Execute = InstructionSet.Modulo } + }; + + #endregion + } + + public static void Nop(Chip chip, bool word) { /* No operation */ } + public static void Nop(Chip chip, u8 m1, bool word) { /* No operation */ } + public static void Nop(Chip chip, u8 m1, u8 m2, bool word) { /* No operation */ } + + #region Control Instructions + + public static void Halt(Chip chip, bool word) + { + throw new HaltException(); + } + + public static void Suspend(Chip chip, bool word) + { + throw new NotImplementedException("SUS instruction is not implemented yet."); + } + + public static void Interrupt(Chip chip, u8 m1, bool word) + { + u16 interruptNumber = chip.Load(m1, word); + + chip.Interrupt(interruptNumber); + } + + public static void Move(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value = chip.Load(m1, word); + chip.Store(m2, value, word); + } + + public static void Jump(Chip chip, u8 m1, bool word) + { + u16 address = chip.Load(m1, word); + chip.Pc = address; + } + + public static void Call(Chip chip, u8 m1, bool word) + { + u16 targetAddress = chip.Load(m1, word); + chip.Push(chip.Pc); + chip.Pc = targetAddress; + } + + public static void Return(Chip chip, bool word) + { + u16 returnAddress = chip.Pop(); + chip.Pc = returnAddress; + } + + public static void BranchIfZero(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + if (value == 0) + chip.SkipInstruction(); + } + + public static void BranchIfZeroLx(Chip chip, bool word) + { + if (chip.Lx == 0) + chip.SkipInstruction(); + } + + public static void Branch(Chip chip, bool word) + { + chip.SkipInstruction(); + } + + public static void Push(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + chip.Push(value); + } + + public static void Pop(Chip chip, u8 m1, bool word) + { + u16 value = chip.Pop(); + chip.Store(m1, value, word); + } + + #endregion + + #region Data Instructions + + public static void Inverse(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + value = (u16)~value; + + chip.Dx = value; + } + + public static void InverseDx(Chip chip, bool word) + { + chip.Dx = (u16)~chip.Dx; + } + + public static void And(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + u16 result = (u16)(value1 & value2); + + chip.Dx = result; + } + + public static void Or(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + u16 result = (u16)(value1 | value2); + + chip.Dx = result; + } + + public static void Xor(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + u16 result = (u16)(value1 ^ value2); + + chip.Dx = result; + } + + public static void ShiftLeft(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value = chip.Load(m1, word); + u16 shiftAmount = (u16) (chip.Load(m2, word) & 0xF); // Limit shift to 0-15 + u16 result = (u16) (value << shiftAmount); + + chip.Dx = result; + } + + public static void ShiftRight(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value = chip.Load(m1, word); + u16 shiftAmount = (u16) (chip.Load(m2, word) & 0xF); // Limit shift to 0-15 + u16 result = (u16) (value >> shiftAmount); + + chip.Dx = result; + } + + public static void ShiftPlaceLeft(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + u16 result = (u16) ((value << 1) | (value >> 15)); // Rotate left + + chip.Dx = result; + } + + public static void ShiftPlaceRight(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + u16 result = (u16) ((value >> 1) | (value << 15)); // Rotate right + + chip.Dx = result; + } + + public static void ShiftPlaceLeftDx(Chip chip, bool word) + { + u16 value = chip.Dx; + u16 result = (u16) ((value << 1) | (value >> 15)); // Rotate left + + chip.Dx = result; + } + + public static void ShiftPlaceRightDx(Chip chip, bool word) + { + u16 value = chip.Dx; + u16 result = (u16) ((value >> 1) | (value << 15)); // Rotate right + + chip.Dx = result; + } + + #endregion + + #region Logic Instructions + + public static void Not(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + chip.Lx = value == 0 ? (u16)1 : (u16)0; + } + + public static void NotLx(Chip chip, bool word) + { + chip.Lx = chip.Lx == 0 ? (u16)1 : (u16)0; + } + + public static void Compare(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + chip.Lx = value1 == value2 ? (u16)1 : (u16)0; + } + + public static void LessThan(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + chip.Lx = value1 < value2 ? (u16)1 : (u16)0; + } + + public static void GreaterThan(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + chip.Lx = value1 > value2 ? (u16)1 : (u16)0; + } + + #endregion + + #region Arithmetic Instructions + + public static void Increment(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + value++; + chip.Store(m1, value, word); + } + + public static void IncrementAx(Chip chip, bool word) + { + chip.Ax++; + } + + public static void Decrement(Chip chip, u8 m1, bool word) + { + u16 value = chip.Load(m1, word); + value--; + chip.Store(m1, value, word); + } + + public static void DecrementAx(Chip chip, bool word) + { + chip.Ax--; + } + + public static void Add(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + u16 result = (u16)(value1 + value2); + + chip.Ax = result; + } + + public static void Subtract(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + u16 result = (u16)(value1 - value2); + + chip.Ax = result; + } + + public static void Multiply(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + u16 result = (u16)(value1 * value2); + + chip.Ax = result; + } + + public static void Divide(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + if (value2 == 0) + throw new DivideByZeroException("Attempted to divide by zero."); + + u16 result = (u16)(value1 / value2); + chip.Ax = result; + } + + public static void Modulo(Chip chip, u8 m1, u8 m2, bool word) + { + u16 value1 = chip.Load(m1, word); + u16 value2 = chip.Load(m2, word); + if (value2 == 0) + throw new DivideByZeroException("Attempted to modulo by zero."); + + u16 result = (u16)(value1 % value2); + chip.Ax = result; + } + + #endregion +} \ No newline at end of file diff --git a/Q1.core/Exceptions.cs b/Q1.core/Exceptions.cs new file mode 100644 index 0000000..fa5156f --- /dev/null +++ b/Q1.core/Exceptions.cs @@ -0,0 +1,7 @@ +namespace Q1.core; + +public class ChipException(string message) : Exception(message); + +public class InvalidInstructionException(u8 opcode) : ChipException($"Invalid instruction: 0x{opcode:X2}"); +public class InvalidAddressingModeException(u8 mode) : ChipException($"Invalid addressing mode: 0x{mode:X2}"); +public class HaltException() : ChipException("Execution halted"); \ No newline at end of file -- cgit v1.3.1