summaryrefslogtreecommitdiff
path: root/Emulator/Chip/Q1Cpu.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emulator/Chip/Q1Cpu.cs')
-rw-r--r--Emulator/Chip/Q1Cpu.cs171
1 files changed, 171 insertions, 0 deletions
diff --git a/Emulator/Chip/Q1Cpu.cs b/Emulator/Chip/Q1Cpu.cs
new file mode 100644
index 0000000..16e1e14
--- /dev/null
+++ b/Emulator/Chip/Q1Cpu.cs
@@ -0,0 +1,171 @@
+namespace Q1.Emulator.Chip;
+
+using System;
+using Assembler;
+
+public partial class Q1Cpu
+{
+ public const u16 StackStart = 0x2FFF;
+ public const u16 StackLength = 16 * 2; // 16 16-bit values
+
+ public Random Random;
+
+ public u16 AX = 0; // Accumulator register
+ public u16 DX = 0; // Data register
+ public u16 LX = 0; // Logical register
+
+ public u16 SP { get; set; } // Stack pointer
+ public u16 PC { get; set; } // Program counter
+
+ public u16[] V { get; set; } // Variable registers (64 total)
+ public u16 I { get; set; } // Index register
+ public u16 Cycles { get; set; } // Number of cycles
+
+ public Bus Bus { get; set; }
+ public bool AbortRequested = false;
+
+ public Q1Cpu()
+ {
+ this.LoadInstructionLookup();
+ }
+
+ public void Reset()
+ {
+ this.AbortRequested = false;
+
+ this.V = new u16[16];
+
+ this.SP = Q1Cpu.StackStart;
+ this.PushStack(0xFFFF);
+
+ this.I = 0;
+ }
+
+ public void Clock()
+ {
+ var pc = this.PC;
+ if (pc == 0xFFFF)
+ {
+ this.AbortRequested = true;
+ return;
+ }
+
+ u16 instruction = this.FetchInstruction(out u8 op, out u8 m1, out u8 m2, out _);
+ if (instruction != 0x0101)
+ Console.WriteLine($"CPU.Clock: {pc:X4} | {Disassembler.ParseInstruction(instruction)}");
+
+ bool ex = m2 > 0;
+ switch (m1, ex)
+ {
+ case (0x00, false):
+ this.InstructionLookup[op].Item1(); // Implicit
+ break;
+ case (> 0x00, false):
+ this.InstructionLookup[op].Item2(m1); // Simple addressing mode
+ break;
+ case (0x00, true):
+ this.InstructionLookup[op].Item3(); // Extended implicit
+ break;
+ case (> 0x00, true):
+ this.InstructionLookup[op].Item4(m1, m2); // Extended addressing mode
+ break;
+ }
+
+ this.Cycles++;
+ }
+ public u16 FetchInstruction(out u8 op, out u8 m1, out u8 m2, out u8 length)
+ {
+ u16 instruction = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ op = (u8) ((instruction >> 8) & 0xFF); // 256 opcodes
+ m1 = (u8) ((instruction >> 4) & 0x0F); // 16 modes
+ m2 = (u8) (instruction & 0x0F); // 16 modes
+
+ length = (u8) (this.AddressingModeLength(m1) + this.AddressingModeLength(m2));
+ return instruction;
+ }
+
+ private void PushStack(u16 value)
+ {
+ if (this.SP + 2 >= Q1Cpu.StackStart + Q1Cpu.StackLength)
+ throw new InvalidOperationException("Stack overflow");
+
+ this.Bus.WriteWord(this.SP, value);
+ this.SP += 2;
+ }
+
+ private u16 PopStack()
+ {
+ if (this.SP - 2 < Q1Cpu.StackStart)
+ throw new InvalidOperationException("Stack underflow");
+
+ this.SP -= 2;
+ return this.Bus.ReadWord(this.SP);
+ }
+
+ private u16 FetchRegister(u8 regIndex)
+ {
+ return regIndex switch
+ {
+ <= 0x3F => this.V[regIndex],
+
+ 0x40 => this.AX,
+ 0x41 => (u16) (this.AX >> 8), // High byte of AX
+ 0x42 => (u16) (this.AX & 0xFF), // Low byte of AX
+
+ 0x43 => this.DX,
+ 0x44 => (u16) (this.DX >> 8), // High byte of DX
+ 0x45 => (u16) (this.DX & 0xFF), // Low byte of DX
+
+ 0x46 => this.LX,
+ 0x50 => this.PC,
+ 0x51 => this.SP,
+
+ _ => throw new InvalidOperationException($"Unknown register index: {regIndex:X2}"),
+ };
+ }
+
+ private void StoreRegister(u8 regIndex, u16 value)
+ {
+ switch (regIndex)
+ {
+ case <= 0x3F:
+ this.V[regIndex] = value;
+ break;
+
+ case 0x40:
+ this.AX = value;
+ break;
+ case 0x41:
+ this.AX = (u16) ((this.AX & 0x00FF) | (value << 8));
+ break; // Set high byte
+ case 0x42:
+ this.AX = (u16) ((this.AX & 0xFF00) | (value & 0x00FF));
+ break; // Set low byte
+
+ case 0x43:
+ this.DX = value;
+ break;
+ case 0x44:
+ this.DX = (u16) ((this.DX & 0x00FF) | (value << 8));
+ break; // Set high byte
+ case 0x45:
+ this.DX = (u16) ((this.DX & 0xFF00) | (value & 0x00FF));
+ break; // Set low byte
+
+ case 0x46:
+ this.LX = value;
+ break;
+ case 0x50:
+ this.PC = value;
+ break;
+ case 0x51:
+ this.SP = value;
+ break;
+
+ default:
+ throw new InvalidOperationException($"Unknown register index: {regIndex:X2}");
+ }
+ }
+} \ No newline at end of file