summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Q1.core/Arch/Chip.cs75
-rw-r--r--Q1.core/Arch/Constants/ChipLayout.cs (renamed from Q1.core/Arch/ChipLayout.cs)2
-rw-r--r--Q1.core/Arch/Constants/ChipRegisters.cs19
-rw-r--r--Q1.core/Arch/InstructionEncoding.cs26
-rw-r--r--Q1.core/Arch/Lookups/AddressingModes.cs (renamed from Q1.core/Arch/AddressingModes.cs)15
-rw-r--r--Q1.core/Arch/Lookups/InstructionSet.cs (renamed from Q1.core/Arch/InstructionSet.cs)102
-rw-r--r--Q1.core/Exceptions.cs7
-rw-r--r--Q1.tests/Core/AddressingModesTests.cs4
-rw-r--r--Q1.tests/Core/ChipTestBase.cs20
-rw-r--r--Q1.tests/Core/ChipTests.cs87
-rw-r--r--Q1.tests/Core/InstructionEncodingTests.cs28
-rw-r--r--Q1.tests/Core/InstructionShapeTests.cs63
-rw-r--r--Q1.tests/Core/Instructions/ControlInstructionTests.cs451
-rw-r--r--Q1.tests/Core/Instructions/InstructionTestBase.cs6
-rw-r--r--Q1.tests/Core/InstructionsTests.cs20
-rw-r--r--Q1.tests/GlobalUsings.cs12
17 files changed, 844 insertions, 97 deletions
diff --git a/.gitignore b/.gitignore
index f587591..c477ee3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,6 @@ obj/
riderModule.iml
/_ReSharper.Caches/
.idea/
-.config/ \ No newline at end of file
+.config/
+*.sln.DotSettings
+*.sln.DotSettings.user \ 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/Constants/ChipLayout.cs
index 6c7187b..6990789 100644
--- a/Q1.core/Arch/ChipLayout.cs
+++ b/Q1.core/Arch/Constants/ChipLayout.cs
@@ -1,4 +1,4 @@
-namespace Q1.core.Arch;
+namespace Q1.core.Arch.Constants;
public static class ChipLayout
{
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/AddressingModes.cs b/Q1.core/Arch/Lookups/AddressingModes.cs
index 96b4475..95e798d 100644
--- a/Q1.core/Arch/AddressingModes.cs
+++ b/Q1.core/Arch/Lookups/AddressingModes.cs
@@ -1,11 +1,22 @@
-namespace Q1.core.Arch;
+namespace Q1.core.Arch.Lookups;
-using Components;
+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
diff --git a/Q1.core/Arch/InstructionSet.cs b/Q1.core/Arch/Lookups/InstructionSet.cs
index 212e77e..2ea20fc 100644
--- a/Q1.core/Arch/InstructionSet.cs
+++ b/Q1.core/Arch/Lookups/InstructionSet.cs
@@ -1,9 +1,25 @@
-namespace Q1.core.Arch;
+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 };
@@ -173,85 +189,85 @@ public static class InstructionSet
Implicit = nop,
SimpleAddressing = nopSimple,
ExtendedImplicit = nop,
- ExtendedAddressing = new ExtendedInstruction { Name = "DIV", Execute = InstructionSet.Modulo }
+ ExtendedAddressing = new ExtendedInstruction { Name = "MOD", 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 */ }
+ 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
- private static void Halt(Chip chip, bool word)
+ public static void Halt(Chip chip, bool word)
{
- throw new NotImplementedException("HALT instruction is not implemented yet.");
+ throw new HaltException();
}
- private static void Suspend(Chip chip, bool word)
+ public 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)
+ public 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)
+ public 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)
+ public 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)
+ public 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)
+ public static void Return(Chip chip, bool word)
{
u16 returnAddress = chip.Pop();
chip.Pc = returnAddress;
}
- private static void BranchIfZero(Chip chip, u8 m1, bool word)
+ public 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)
+ public static void BranchIfZeroLx(Chip chip, bool word)
{
if (chip.Lx == 0)
chip.SkipInstruction();
}
- private static void Branch(Chip chip, bool word)
+ public static void Branch(Chip chip, bool word)
{
chip.SkipInstruction();
}
- private static void Push(Chip chip, u8 m1, bool word)
+ public 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)
+ public static void Pop(Chip chip, u8 m1, bool word)
{
u16 value = chip.Pop();
chip.Store(m1, value, word);
@@ -261,7 +277,7 @@ public static class InstructionSet
#region Data Instructions
- private static void Inverse(Chip chip, u8 m1, bool word)
+ public static void Inverse(Chip chip, u8 m1, bool word)
{
u16 value = chip.Load(m1, word);
value = (u16)~value;
@@ -269,12 +285,12 @@ public static class InstructionSet
chip.Dx = value;
}
- private static void InverseDx(Chip chip, bool word)
+ public static void InverseDx(Chip chip, bool word)
{
chip.Dx = (u16)~chip.Dx;
}
- private static void And(Chip chip, u8 m1, u8 m2, bool word)
+ public static void And(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -283,7 +299,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void Or(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Or(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -292,7 +308,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void Xor(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Xor(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -301,7 +317,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void ShiftLeft(Chip chip, u8 m1, u8 m2, bool word)
+ 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
@@ -310,7 +326,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void ShiftRight(Chip chip, u8 m1, u8 m2, bool word)
+ 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
@@ -319,7 +335,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void ShiftPlaceLeft(Chip chip, u8 m1, bool word)
+ 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
@@ -327,7 +343,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void ShiftPlaceRight(Chip chip, u8 m1, bool word)
+ 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
@@ -335,7 +351,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void ShiftPlaceLeftDx(Chip chip, bool word)
+ public static void ShiftPlaceLeftDx(Chip chip, bool word)
{
u16 value = chip.Dx;
u16 result = (u16) ((value << 1) | (value >> 15)); // Rotate left
@@ -343,7 +359,7 @@ public static class InstructionSet
chip.Dx = result;
}
- private static void ShiftPlaceRightDx(Chip chip, bool word)
+ public static void ShiftPlaceRightDx(Chip chip, bool word)
{
u16 value = chip.Dx;
u16 result = (u16) ((value >> 1) | (value << 15)); // Rotate right
@@ -355,32 +371,32 @@ public static class InstructionSet
#region Logic Instructions
- private static void Not(Chip chip, u8 m1, bool word)
+ public 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)
+ public 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)
+ 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;
}
- private static void LessThan(Chip chip, u8 m1, u8 m2, bool word)
+ 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;
}
- private static void GreaterThan(Chip chip, u8 m1, u8 m2, bool word)
+ public static void GreaterThan(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -391,31 +407,31 @@ public static class InstructionSet
#region Arithmetic Instructions
- private static void Increment(Chip chip, u8 m1, bool word)
+ public 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)
+ public static void IncrementAx(Chip chip, bool word)
{
chip.Ax++;
}
- private static void Decrement(Chip chip, u8 m1, bool word)
+ public 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)
+ public static void DecrementAx(Chip chip, bool word)
{
chip.Ax--;
}
- private static void Add(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Add(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -424,7 +440,7 @@ public static class InstructionSet
chip.Ax = result;
}
- private static void Subtract(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Subtract(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -433,7 +449,7 @@ public static class InstructionSet
chip.Ax = result;
}
- private static void Multiply(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Multiply(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -442,7 +458,7 @@ public static class InstructionSet
chip.Ax = result;
}
- private static void Divide(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Divide(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
@@ -453,7 +469,7 @@ public static class InstructionSet
chip.Ax = result;
}
- private static void Modulo(Chip chip, u8 m1, u8 m2, bool word)
+ public static void Modulo(Chip chip, u8 m1, u8 m2, bool word)
{
u16 value1 = chip.Load(m1, word);
u16 value2 = chip.Load(m2, word);
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
diff --git a/Q1.tests/Core/AddressingModesTests.cs b/Q1.tests/Core/AddressingModesTests.cs
index 19be64d..058e373 100644
--- a/Q1.tests/Core/AddressingModesTests.cs
+++ b/Q1.tests/Core/AddressingModesTests.cs
@@ -2,6 +2,7 @@ namespace Q1.tests.Core;
using core;
using core.Arch;
+using core.Arch.Lookups;
[TestFixture]
public class AddressingModesTests
@@ -16,5 +17,8 @@ public class AddressingModesTests
Assert.That(populatedModes, Has.Length.EqualTo(8),
"Expected 8 addressing modes to be defined");
+
+ Assert.That(AddressingModes.Lookup.Length, Is.EqualTo(8),
+ "Expected addressing mode lookup to have 8 entries");
}
} \ No newline at end of file
diff --git a/Q1.tests/Core/ChipTestBase.cs b/Q1.tests/Core/ChipTestBase.cs
new file mode 100644
index 0000000..3540954
--- /dev/null
+++ b/Q1.tests/Core/ChipTestBase.cs
@@ -0,0 +1,20 @@
+namespace Q1.tests.Core;
+
+using Q1.core.Arch;
+using Q1.core.Arch.Constants;
+using Q1.core.Components;
+
+public abstract class ChipTestBase
+{
+ protected Chip CreateChip()
+ {
+ var chip = new Chip();
+
+ var memory = new Memory(ChipLayout.MEMORY_START, ChipLayout.MEMORY_SIZE);
+ var bus = new Bus(0x0, u16.MaxValue);
+ bus.MountDevice(memory);
+
+ bus.AttachChip(chip);
+ return chip;
+ }
+} \ No newline at end of file
diff --git a/Q1.tests/Core/ChipTests.cs b/Q1.tests/Core/ChipTests.cs
new file mode 100644
index 0000000..2b0d20f
--- /dev/null
+++ b/Q1.tests/Core/ChipTests.cs
@@ -0,0 +1,87 @@
+namespace Q1.tests.Core;
+
+using core.Arch;
+using core.Arch.Constants;
+
+[TestFixture]
+public class ChipTests : ChipTestBase
+{
+ [TestCase(ChipRegisters.AX)]
+ [TestCase(ChipRegisters.DX)]
+ [TestCase(ChipRegisters.LX)]
+ public void Register_ShouldRoundTrip(u8 register)
+ {
+ var chip = this.CreateChip();
+
+ u16 value = (u16) (register * 0x1111); // unique value for each register
+ chip.SetRegister(register, value);
+ u16 readValue = chip.GetRegister(register);
+
+ Assert.That(readValue, Is.EqualTo(value), $"Register {register:X2} should round trip");
+ }
+
+ [Test]
+ public void PushPop_ShouldRoundTrip()
+ {
+ var chip = this.CreateChip();
+
+ u16 value = 0xABCD;
+ chip.Push(value);
+ u16 poppedValue = chip.Pop();
+
+ Assert.That(poppedValue, Is.EqualTo(value), "Popped value should match pushed value");
+ }
+
+ [Test]
+ public void StackOverflow_ShouldThrow()
+ {
+ var chip = this.CreateChip();
+
+ // Fill the stack to its limit
+ for (int i = 0; i < ChipLayout.STACK_COUNT; i++)
+ chip.Push((u16) i);
+
+ // Next push should cause overflow
+ Assert.Throws<InvalidOperationException>(() => chip.Push(0xFFFF), "Expected stack overflow to throw");
+ }
+
+ [Test]
+ public void StackUnderflow_ShouldThrow()
+ {
+ var chip = this.CreateChip();
+
+ // Attempt to pop from an empty stack
+ Assert.Throws<InvalidOperationException>(() => chip.Pop(), "Expected stack underflow to throw");
+ }
+
+ [Test]
+ public void InvalidRegisterAccess_ShouldThrow()
+ {
+ var chip = this.CreateChip();
+
+ // Attempt to access an invalid register index
+ Assert.Throws<InvalidOperationException>(() => chip.GetRegister(0xFF), "Expected invalid register access to throw");
+ Assert.Throws<InvalidOperationException>(() => chip.SetRegister(0xFF, 0x1234), "Expected invalid register access to throw");
+ }
+
+ [Test]
+ public void InterruptHandling_ShouldJumpToInterruptVector()
+ {
+ var chip = this.CreateChip();
+
+ var originalPc = chip.Pc;
+
+ // Set up an interrupt vector at 0xFFFE
+ u16 interruptHandlerAddress = 0x1234;
+ chip.RegisterInterruptHandler(8, interruptHandlerAddress); // Register handler for interrupt 8
+
+ // Trigger the interrupt
+ chip.Interrupt(8);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(chip.Pc, Is.EqualTo(interruptHandlerAddress), "PC should jump to interrupt handler address");
+ Assert.That(chip.Pop(), Is.EqualTo(originalPc), "PC should be pushed onto the stack during interrupt");
+ });
+ }
+} \ No newline at end of file
diff --git a/Q1.tests/Core/InstructionEncodingTests.cs b/Q1.tests/Core/InstructionEncodingTests.cs
new file mode 100644
index 0000000..7bb653f
--- /dev/null
+++ b/Q1.tests/Core/InstructionEncodingTests.cs
@@ -0,0 +1,28 @@
+namespace Q1.tests.Core;
+
+using core.Arch;
+
+[TestFixture]
+public class InstructionEncodingTests
+{
+ [Test]
+ public void EncodeDecode_ShouldRoundTrip()
+ {
+ u8 opcode = 0x01;
+ u8 m1 = 0x5;
+ u8 m2 = 0xC;
+ bool word = true;
+
+ u16 instruction = InstructionEncoding.Encode(opcode, m1, m2, word);
+
+ InstructionEncoding.Decode(instruction, out var op2, out var m1_2, out var m2_2, out var word2);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(op2, Is.EqualTo(opcode));
+ Assert.That(m1_2, Is.EqualTo(m1));
+ Assert.That(m2_2, Is.EqualTo(m2));
+ Assert.That(word2, Is.EqualTo(word));
+ });
+ }
+} \ No newline at end of file
diff --git a/Q1.tests/Core/InstructionShapeTests.cs b/Q1.tests/Core/InstructionShapeTests.cs
new file mode 100644
index 0000000..969ba7e
--- /dev/null
+++ b/Q1.tests/Core/InstructionShapeTests.cs
@@ -0,0 +1,63 @@
+namespace Q1.tests.Core;
+
+using core;
+using core.Arch;
+using core.Arch.Lookups;
+
+[TestFixture]
+public class InstructionShapeTests
+{
+
+ [Test]
+ public void LookupShouldHaveInstructions()
+ {
+ var populatedSets = InstructionSet.Lookup
+ .Where(x => x != default)
+ .ToArray();
+
+ Assert.That(populatedSets, Has.Length.EqualTo(19),
+ "Expected 19 instruction groups to be defined");
+
+ Assert.That(InstructionSet.Lookup.Length, Is.EqualTo(128),
+ "Expected instruction lookup to have 128 entries");
+ }
+
+ [TestCase(0x00, "NOP", "JMP", "HALT", "MOV")]
+ [TestCase(0x01, "RET", "CALL", "SUS", "NOP")]
+ [TestCase(0x02, "BZ(LX)", "BZ", "BR", "NOP")]
+ [TestCase(0x03, "NOP", "PUSH", "NOP", "NOP")]
+ [TestCase(0x04, "NOP", "POP", "NOP", "NOP")]
+ [TestCase(0x05, "NOP", "INT", "NOP", "NOP")]
+
+ [TestCase(0x10, "INV(DX)", "INV", "NOP", "AND")]
+ [TestCase(0x11, "NOP", "NOP", "NOP", "OR")]
+ [TestCase(0x12, "NOP", "NOP", "NOP", "XOR")]
+ [TestCase(0x13, "SHPL(DX)", "SHPL", "NOP", "SHL")]
+ [TestCase(0x14, "SHPR(DX)", "SHPR", "NOP", "SHR")]
+
+ [TestCase(0x20, "NOT(LX)", "NOT", "NOP", "CMP")]
+ [TestCase(0x21, "NOP", "NOP", "NOP", "LT")]
+ [TestCase(0x22, "NOP", "NOP", "NOP", "GT")]
+
+ [TestCase(0x30, "INC(AX)", "INC", "NOP", "ADD")]
+ [TestCase(0x31, "DEC(AX)", "DEC", "NOP", "SUB")]
+ [TestCase(0x32, "NOP", "NOP", "NOP", "DIV")]
+ [TestCase(0x33, "NOP", "NOP", "NOP", "MUL")]
+ [TestCase(0x34, "NOP", "NOP", "NOP", "MOD")]
+ public void GroupLooksLike(i32 opcode, string impl, string simple, string extendedImpl, string extended)
+ {
+ var group = InstructionSet.Lookup[opcode];
+
+ Assert.That(group.Implicit.Name, Is.EqualTo(impl),
+ $"Expected implicit instruction for opcode {opcode:X2} to be {impl}");
+
+ Assert.That(group.SimpleAddressing.Name, Is.EqualTo(simple),
+ $"Expected simple addressing instruction for opcode {opcode:X2} to be {simple}");
+
+ Assert.That(group.ExtendedImplicit.Name, Is.EqualTo(extendedImpl),
+ $"Expected extended implicit instruction for opcode {opcode:X2} to be {extendedImpl}");
+
+ Assert.That(group.ExtendedAddressing.Name, Is.EqualTo(extended),
+ $"Expected extended addressing instruction for opcode {opcode:X2} to be {extended}");
+ }
+} \ No newline at end of file
diff --git a/Q1.tests/Core/Instructions/ControlInstructionTests.cs b/Q1.tests/Core/Instructions/ControlInstructionTests.cs
new file mode 100644
index 0000000..00c9566
--- /dev/null
+++ b/Q1.tests/Core/Instructions/ControlInstructionTests.cs
@@ -0,0 +1,451 @@
+namespace Q1.tests.Core.Instructions;
+
+using core;
+using core.Arch;
+using core.Arch.Constants;
+using core.Arch.Lookups;
+using core.Components;
+
+public class ControlInstructionTests : InstructionTestBase
+{
+ [Test]
+ public void JumpInstruction_JumpsToAddress()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("JMP"),
+ AddressingModes.GetAddressingMode("Immediate"),
+ 0,
+ true);
+
+ u16 jumpAddress = 0x1234;
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+ chip.Bus.WriteWord((u16) (chip.Pc + 2), jumpAddress);
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(jumpAddress),
+ "PC should jump to the specified address");
+ }
+
+ [Test]
+ public void HaltInstruction_HaltsExecution()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("HALT"),
+ 0,
+ 1,
+ true);
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+
+ // Act
+ var clock = () => chip.Clock();
+
+ // Assert
+ Assert.That(clock, Throws.TypeOf<HaltException>());
+ }
+
+
+ [Test]
+ public void ReturnInstruction_ReturnsFromSubroutine()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ // Simulate a call by pushing a return address onto the stack
+ u16 returnAddress = 0x5678;
+ chip.Push(returnAddress);
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("RET"),
+ 0,
+ 0,
+ true);
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(returnAddress),
+ "PC should return to the address on top of the stack");
+ }
+
+
+ [Test]
+ public void CallInstruction_CallsSubroutine()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16 pc = chip.Pc;
+ u16 subroutineAddress = 0x9ABC;
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("CALL"),
+ AddressingModes.GetAddressingMode("Immediate"),
+ 0,
+ true);
+
+ chip.Bus.WriteWord(pc, instruction);
+ chip.Bus.WriteWord((u16) (pc + 2), subroutineAddress);
+
+ u16 expectedReturnAddress = (u16) (pc + 4);
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(subroutineAddress),
+ "PC should jump to the subroutine address");
+
+ Assert.That(
+ chip.Bus.ReadWord(ChipLayout.STACK_START),
+ Is.EqualTo(expectedReturnAddress),
+ "Return address should be pushed onto the stack");
+ });
+ }
+
+
+ [Test]
+ public void SuspendInstruction_Fails()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("SUS"),
+ 0,
+ 1,
+ true);
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+
+ // Act
+ var clock = () => chip.Clock();
+
+ // Assert
+ Assert.That(clock, Throws.TypeOf<NotImplementedException>());
+ }
+
+
+ [TestCase(0, true)]
+ [TestCase(1, false)]
+ [TestCase(5, false)]
+ public void BranchIfZeroLx_BranchesCorrectly(i32 lxValue, bool shouldBranch)
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16 pc = chip.Pc;
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("BZ(LX)"),
+ 0,
+ 0,
+ true);
+
+ chip.Bus.WriteWord(pc, instruction);
+ chip.Lx = (u16) lxValue;
+
+ u16 nextInstructionAddress = (u16) (pc + 2); // Address of the next instruction
+ u16 branchAddress = (u16) (nextInstructionAddress + 2); // Finish current instruction and skip next one
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ if (shouldBranch)
+ {
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(branchAddress),
+ "PC should skip the next instruction when Lx is zero");
+ }
+ else
+ {
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(nextInstructionAddress),
+ "PC should proceed to the next instruction when Lx is non-zero");
+ }
+ }
+
+ [TestCase(0, true)]
+ [TestCase(1, false)]
+ [TestCase(5, false)]
+ public void BranchIfZero_BranchesCorrectly(int value, bool shouldBranch)
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16 pc = chip.Pc;
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("BZ"),
+ AddressingModes.GetAddressingMode("Immediate"),
+ 0,
+ true);
+
+ chip.Bus.WriteWord(pc, instruction);
+ chip.Bus.WriteWord((u16) (pc + 2), (u16) value);
+
+ u16 nextInstructionAddress = (u16) (pc + 4); // Address of the instruction after the next one
+ u16 branchAddress = (u16) (nextInstructionAddress + 2); // Finish current instruction and skip next one
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ if (shouldBranch)
+ {
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(branchAddress),
+ "PC should skip the next instruction when the value is zero");
+ }
+ else
+ {
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(nextInstructionAddress),
+ "PC should proceed to the next instruction when the value is non-zero");
+ }
+ }
+
+ [Test]
+ public void Branch_SkipsNextInstruction()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16 pc = chip.Pc;
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("BR"),
+ 0,
+ 0,
+ true);
+
+ chip.Bus.WriteWord(pc, instruction);
+
+ u16 nextInstructionAddress = (u16) (pc + 2); // Address of the next instruction
+ u16 branchAddress = (u16) (nextInstructionAddress + 2); // Finish current instruction and skip next one
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(branchAddress),
+ "PC should skip the next instruction unconditionally");
+ }
+
+ [Test]
+ public void PushInstruction_PushesValueOntoStack()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16 pc = chip.Pc;
+ u16 valueToPush = 0xABCD;
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("PUSH"),
+ AddressingModes.GetAddressingMode("Immediate"),
+ 0,
+ true);
+
+ chip.Bus.WriteWord(pc, instruction);
+ chip.Bus.WriteWord((u16) (pc + 2), valueToPush);
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ chip.Bus.ReadWord(ChipLayout.STACK_START),
+ Is.EqualTo(valueToPush),
+ "Value should be pushed onto the stack");
+
+ Assert.That(
+ chip.Sp,
+ Is.EqualTo((u16) (ChipLayout.STACK_START + 2)),
+ "Stack pointer should move up by 2 bytes after push");
+ });
+ }
+
+ [Test]
+ public void PopInstruction_PopsValueFromStack()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16 pc = chip.Pc;
+ u16 valueToPop = 0xABCD;
+
+ // Simulate a push by writing a value to the stack and moving the stack pointer
+ chip.Bus.WriteWord(ChipLayout.STACK_START, valueToPop);
+ chip.Sp = ChipLayout.STACK_START + 2;
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("POP"),
+ AddressingModes.GetAddressingMode("Register"),
+ 0,
+ true);
+
+ chip.Bus.WriteWord(pc, instruction);
+ chip.Bus.Write((u16) (pc + 2), ChipRegisters.DX);
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ chip.Dx,
+ Is.EqualTo(valueToPop),
+ "Value should be popped from the stack into the register");
+
+ Assert.That(
+ chip.Sp,
+ Is.EqualTo(ChipLayout.STACK_START),
+ "Stack pointer should move down by 2 bytes after pop");
+ });
+ }
+
+ [Test]
+ public void PushPopSequence_WorksCorrectly()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ u16[] valuesToPush = [0x1111, 0x2222, 0x3333];
+
+ void PushValue(u16 value)
+ {
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("PUSH"),
+ AddressingModes.GetAddressingMode("Immediate"),
+ 0,
+ true);
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+ chip.Bus.WriteWord((u16) (chip.Pc + 2), value);
+ chip.Clock();
+ }
+
+ u16 PopValue()
+ {
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("POP"),
+ AddressingModes.GetAddressingMode("Register"),
+ 0,
+ true);
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+ chip.Bus.Write((u16) (chip.Pc + 2), ChipRegisters.DX);
+
+ chip.Clock();
+
+ return chip.Dx;
+ }
+
+ // Act & Assert
+ // Push 1 item, then pop it, then push 2 items, then pop them
+ Assert.Multiple(() =>
+ {
+ PushValue(valuesToPush[0]);
+ Assert.That(
+ PopValue(),
+ Is.EqualTo(valuesToPush[0]),
+ "First popped value should match the first pushed value");
+
+ PushValue(valuesToPush[1]);
+ PushValue(valuesToPush[2]);
+ Assert.That(
+ PopValue(),
+ Is.EqualTo(valuesToPush[2]),
+ "Second popped value should match the second pushed value");
+ Assert.That(
+ PopValue(),
+ Is.EqualTo(valuesToPush[1]),
+ "Third popped value should match the first pushed value");
+ });
+ }
+
+ [Test]
+ public void InterruptInstruction_TriggersInterrupt()
+ {
+ // Arrange
+ var chip = CreateChip();
+
+ var instruction = InstructionEncoding
+ .Encode(
+ InstructionSet.GetOpcode("INT"),
+ AddressingModes.GetAddressingMode("Immediate"),
+ 0,
+ true);
+
+ u8 interruptNumber = 8;
+
+ chip.Bus.WriteWord(chip.Pc, instruction);
+ chip.Bus.WriteWord((u16) (chip.Pc + 2), interruptNumber);
+
+ u16 expectedReturnPc = (u16) (chip.Pc + 4); // After the INT instruction and its operand
+ u16 handlerAddress = 0x2345;
+
+ chip.RegisterInterruptHandler(interruptNumber, handlerAddress);
+
+ // Act
+ chip.Clock();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ chip.Pc,
+ Is.EqualTo(handlerAddress),
+ "PC should jump to the interrupt handler address");
+
+ Assert.That(
+ chip.Bus.ReadWord(ChipLayout.STACK_START),
+ Is.EqualTo(expectedReturnPc),
+ "PC should be pushed onto the stack during interrupt");
+ });
+ }
+} \ No newline at end of file
diff --git a/Q1.tests/Core/Instructions/InstructionTestBase.cs b/Q1.tests/Core/Instructions/InstructionTestBase.cs
new file mode 100644
index 0000000..e0cdad4
--- /dev/null
+++ b/Q1.tests/Core/Instructions/InstructionTestBase.cs
@@ -0,0 +1,6 @@
+namespace Q1.tests.Core.Instructions;
+
+public abstract class InstructionTestBase : ChipTestBase
+{
+
+} \ No newline at end of file
diff --git a/Q1.tests/Core/InstructionsTests.cs b/Q1.tests/Core/InstructionsTests.cs
deleted file mode 100644
index 45c85ee..0000000
--- a/Q1.tests/Core/InstructionsTests.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Q1.tests.Core;
-
-using core;
-using core.Arch;
-
-[TestFixture]
-public class InstructionsTests
-{
-
- [Test]
- public void LookupShouldHaveInstructions()
- {
- var populatedSets = InstructionSet.Lookup
- .Where(x => x != default)
- .ToArray();
-
- Assert.That(populatedSets, Has.Length.EqualTo(19),
- "Expected 19 instruction groups to be defined");
- }
-} \ No newline at end of file
diff --git a/Q1.tests/GlobalUsings.cs b/Q1.tests/GlobalUsings.cs
new file mode 100644
index 0000000..aca802e
--- /dev/null
+++ b/Q1.tests/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