From 7a8d2bcd75d88a5429937bf551878c55e1dcf0fd Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:36:34 +1000 Subject: moved everything to seperate projects, and started compiler work + push and pop instructions added --- Emulator/Chip/Bus.cs | 37 +++++ Emulator/Chip/BusDevice.cs | 49 ++++++ Emulator/Chip/DebugDevice.cs | 13 ++ Emulator/Chip/DisplayDevice.cs | 76 +++++++++ Emulator/Chip/Q1Cpu.cs | 171 +++++++++++++++++++++ Emulator/Chip/Q1CpuAddressingModes.cs | 187 ++++++++++++++++++++++ Emulator/Chip/Q1CpuInstructions.cs | 282 ++++++++++++++++++++++++++++++++++ Emulator/Chip/RamDevice.cs | 33 ++++ 8 files changed, 848 insertions(+) create mode 100644 Emulator/Chip/Bus.cs create mode 100644 Emulator/Chip/BusDevice.cs create mode 100644 Emulator/Chip/DebugDevice.cs create mode 100644 Emulator/Chip/DisplayDevice.cs create mode 100644 Emulator/Chip/Q1Cpu.cs create mode 100644 Emulator/Chip/Q1CpuAddressingModes.cs create mode 100644 Emulator/Chip/Q1CpuInstructions.cs create mode 100644 Emulator/Chip/RamDevice.cs (limited to 'Emulator/Chip') diff --git a/Emulator/Chip/Bus.cs b/Emulator/Chip/Bus.cs new file mode 100644 index 0000000..0907f61 --- /dev/null +++ b/Emulator/Chip/Bus.cs @@ -0,0 +1,37 @@ +namespace Q1.Emulator.Chip; + +using System.Collections.Generic; +using System.Linq; + +public class Bus : BusDevice +{ + public List Devices; + + public Bus(u16 addressableStart, u16 addressableEnd) : base(addressableStart, addressableEnd) + { + this.Devices = []; + } + private BusDevice? GetDevice(u16 address) + { + BusDevice? device = this.Devices.FirstOrDefault(x => address >= x.AddressableStart && address < x.AddressableEnd); + return device; + } + + public override u8 Read(u16 address) + { + BusDevice? device = this.GetDevice(address); + if (device == null) + return 0; + + return device.Read(address); + } + + public override void Write(u16 address, u8 value) + { + BusDevice? device = this.GetDevice(address); + if (device == null) + return; + + device.Write(address, value); + } +} \ No newline at end of file diff --git a/Emulator/Chip/BusDevice.cs b/Emulator/Chip/BusDevice.cs new file mode 100644 index 0000000..1e1e972 --- /dev/null +++ b/Emulator/Chip/BusDevice.cs @@ -0,0 +1,49 @@ +namespace Q1.Emulator.Chip; + +using System.Runtime.CompilerServices; + +public abstract class BusDevice +{ + public BusDevice(u16 addressableStart, u16 addressableEnd) + { + this.AddressableStart = addressableStart; + this.AddressableEnd = addressableEnd; + } + + public Bus Bus { get; set; } + public abstract u8 Read(u16 address); + public abstract void Write(u16 address, u8 value); + + public u16 ReadWord(u16 address) + { + u8 high = this.Read(address); + u8 low = this.Read((u16) (address + 1)); + u16 value = Make.u16(high, low); + return value; + } + + public void WriteWord(u16 address, u16 value) + { + ref8 data = Make.ref8(value); + this.Write(address, data[0]); + this.Write((u16) (address + 1), data[1]); + } + + public virtual void Clock() { } + public virtual void Reset() { } + + public void AttachToBus(Bus bus) + { + this.Bus = bus; + bus.Devices.Add(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected u16 GetRelativeAddress(u16 address) + { + return (u16) (address - this.AddressableStart); + } + + public u16 AddressableStart { get; } + public u16 AddressableEnd { get; } +} \ No newline at end of file diff --git a/Emulator/Chip/DebugDevice.cs b/Emulator/Chip/DebugDevice.cs new file mode 100644 index 0000000..4e869bb --- /dev/null +++ b/Emulator/Chip/DebugDevice.cs @@ -0,0 +1,13 @@ +namespace Q1.Emulator.Chip; + +using System; + +public class DebugDevice(u16 addressPoint) + : BusDevice(addressPoint, (u16) (addressPoint + 2)) +{ + public override u8 Read(u16 address) => throw new Exception("DebugDevice.Read not permitted"); + public override void Write(u16 address, u8 value) + { + Console.WriteLine($"DebugDevice.Write: x{value:X4} b{value:b8}"); + } +} \ No newline at end of file diff --git a/Emulator/Chip/DisplayDevice.cs b/Emulator/Chip/DisplayDevice.cs new file mode 100644 index 0000000..a18dbd4 --- /dev/null +++ b/Emulator/Chip/DisplayDevice.cs @@ -0,0 +1,76 @@ +namespace Q1.Emulator.Chip; + +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +public class DisplayDevice : RamDevice +{ + public Texture2D? OutputTexture; + private readonly u16 _paletteSize; + private readonly u16 _displayWidth; + private readonly u16 _displayHeight; + private Color[] textureData; + + public DisplayDevice(u16 paletteSize, u16 displayWidth, u16 displayHeight, u16 addressableStart) + : base(addressableStart, DisplayDevice.DetermineSize(paletteSize, displayWidth, displayHeight)) + { + this._paletteSize = paletteSize; + this._displayWidth = displayWidth; + this._displayHeight = displayHeight; + this.textureData = new Color[this._displayWidth * this._displayHeight]; + } + + private static u16 DetermineSize(u16 paletteSize, u16 displayWidth, u16 displayHeight) + { + return (u16) ((paletteSize * 3) + (displayWidth * displayHeight)); + } + + public override void Clock() + { + Color[] palette = this.ReadPalette(); + ref8 pixelData = this.Memory.AsSpan((u16) (this._paletteSize * 3), (u16) (this._displayWidth * this._displayHeight)); + // Console.WriteLine($"DisplayDevice.Clock: @{(this.AddressableStart + this._paletteSize * 3):X4} palette size={this._paletteSize} display size={this._displayWidth}x{this._displayHeight}"); + for (u16 i = 0; i < this.textureData.Length; i++) + { + u8 pixelValue = pixelData[i]; + this.textureData[i] = palette[pixelValue % this._paletteSize]; + } + + if (this.OutputTexture != null) + this.OutputTexture.SetData(this.textureData); + } + + private Color[] ReadPalette() + { + ref8 paletteData = this.Memory.AsSpan(0, (u16) (this._paletteSize * 3)); + Color[] colors = new Color[this._paletteSize + 1]; + colors[0] = Color.Transparent; + + for (u16 i = 0; i < this._paletteSize; i++) + { + u16 offset = (u16) (i * 3); + byte r = paletteData[offset]; + byte g = paletteData[offset + 1]; + byte b = paletteData[offset + 2]; + colors[(u16) (i + 1)] = new Color(r, g, b); + } + + return colors; + } + + public void SetPalette(Color[] palette) + { + if (palette.Length != this._paletteSize) + throw new ArgumentException($"Palette must have {this._paletteSize} colors."); + + ref8 paletteData = this.Memory.AsSpan(0, (u16) (this._paletteSize * 3)); + for (u16 i = 0; i < this._paletteSize; i++) + { + u16 offset = (u16) (i * 3); + paletteData[offset] = palette[i].R; + paletteData[offset + 1] = palette[i].G; + paletteData[offset + 2] = palette[i].B; + } + } +} \ No newline at end of file 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 diff --git a/Emulator/Chip/Q1CpuAddressingModes.cs b/Emulator/Chip/Q1CpuAddressingModes.cs new file mode 100644 index 0000000..40020f1 --- /dev/null +++ b/Emulator/Chip/Q1CpuAddressingModes.cs @@ -0,0 +1,187 @@ +namespace Q1.Emulator.Chip; + +using System; + +public partial class Q1Cpu +{ + public (Func read, Action write, Action writeByte) Address(u8 mode) + { + return mode switch + { + 0x0 => this.AddressingMode_Implicit(), + 0x1 => this.AddressingMode_Immediate(), + 0x2 => this.AddressingMode_Direct(), + 0x3 => this.AddressingMode_Indirect(), + 0x4 => this.AddressingMode_Relative(), + 0x5 => this.AddressingMode_Register(), + 0x6 => this.AddressingMode_RegisterIndirect(), + 0x7 => this.AddressingMode_RegisterIndirectOffset(), + _ => throw new InvalidOperationException($"Unknown addressing mode: {mode:X2}"), + }; + } + + private u8 AddressingModeLength(u8 mode) + { + return mode switch + { + 0x0 => 0, // Implicit + 0x1 => 2, // Immediate + 0x2 => 2, // Direct + 0x3 => 2, // Indirect + 0x4 => 2, // Relative + 0x5 => 1, // Register + 0x6 => 1, // Register indirect + 0x7 => 3, // Register indirect with offset + _ => throw new InvalidOperationException($"Unknown addressing mode: {mode:X2}"), + }; + } + + public (Func read, Action write, Action writeByte) AddressingMode_Implicit() + { + u16 Read() => throw new InvalidOperationException("Cannot read from implicit mode."); + void Write(u16 value) => throw new InvalidOperationException("Cannot write to implicit mode."); + void WriteByte(u8 value) => throw new InvalidOperationException("Cannot write byte to implicit mode."); + + return (Read, Write, WriteByte); + } + + + public (Func read, Action write, Action writeByte) AddressingMode_Immediate() + { + u16 value = this.Bus.ReadWord(this.PC); + this.PC += 2; + + u16 Read() => value; + void Write(u16 v) => throw new InvalidOperationException("Cannot write to immediate mode."); + void WriteByte(u8 v) => throw new InvalidOperationException("Cannot write byte to immediate mode."); + + return (Read, Write, WriteByte); + } + + public (Func read, Action write, Action writeByte) AddressingMode_Direct() + { + u16 address = this.Bus.ReadWord(this.PC); + this.PC += 2; + + u16 Read() => this.Bus.ReadWord(address); + void Write(u16 value) => this.Bus.WriteWord(address, value); + void WriteByte(u8 value) => this.Bus.Write(address, value); + + return (Read, Write, WriteByte); + } + + public (Func read, Action write, Action writeByte) AddressingMode_Indirect() + { + u16 address = this.Bus.ReadWord(this.PC); + this.PC += 2; + + u16 Read() + { + u16 indirectAddress = this.Bus.ReadWord(address); + u16 value = this.Bus.ReadWord(indirectAddress); + return value; + } + + void Write(u16 value) + { + u16 indirectAddress = this.Bus.ReadWord(address); + this.Bus.WriteWord(indirectAddress, value); + } + + void WriteByte(u8 value) + { + u16 indirectAddress = this.Bus.ReadWord(address); + this.Bus.Write(indirectAddress, value); + } + + return (Read, Write, WriteByte); + } + + public (Func read, Action write, Action writeByte) AddressingMode_Relative() + { + u16 offset = this.Bus.ReadWord(this.PC); + this.PC += 2; + + u16 address = (u16) (this.PC + offset); + + u16 Read() => address; + void Write(u16 value) => throw new InvalidOperationException("Cannot write to relative mode."); + void WriteByte(u8 value) => throw new InvalidOperationException("Cannot write byte to relative mode."); + + return (Read, Write, WriteByte); + } + + public (Func read, Action write, Action writeByte) AddressingMode_Register() + { + u8 regIndex = this.Bus.Read(this.PC); + this.PC += 1; + + u16 Read() => this.FetchRegister(regIndex); + void Write(u16 value) => this.StoreRegister(regIndex, value); + void WriteByte(u8 value) + { + u16 currentValue = this.FetchRegister(regIndex); + u16 newValue = (u16) ((currentValue & 0xFF00) | (value & 0x00FF)); // Update low byte + this.StoreRegister(regIndex, newValue); + } + + return (Read, Write, WriteByte); + } + + public (Func read, Action write, Action writeByte) AddressingMode_RegisterIndirect() + { + u8 regIndex = this.Bus.Read(this.PC); + this.PC += 1; + + u16 Read() + { + u16 address = this.FetchRegister(regIndex); + u16 value = this.Bus.ReadWord(address); + return value; + } + + void Write(u16 value) + { + u16 address = this.FetchRegister(regIndex); + this.Bus.WriteWord(address, value); + } + + void WriteByte(u8 value) + { + u16 address = this.FetchRegister(regIndex); + this.Bus.Write(address, value); + } + + return (Read, Write, WriteByte); + } + + public (Func read, Action write, Action writeByte) AddressingMode_RegisterIndirectOffset() + { + u8 regIndex = this.Bus.Read(this.PC); + this.PC += 1; + + u16 offset = this.Bus.ReadWord(this.PC); + this.PC += 2; + + u16 Read() + { + u16 address = (u16) (this.FetchRegister(regIndex) + offset); + u16 value = this.Bus.ReadWord(address); + return value; + } + + void Write(u16 value) + { + u16 address = (u16) (this.FetchRegister(regIndex) + offset); + this.Bus.WriteWord(address, value); + } + + void WriteByte(u8 value) + { + u16 address = (u16) (this.FetchRegister(regIndex) + offset); + this.Bus.Write(address, value); + } + + return (Read, Write, WriteByte); + } +} \ No newline at end of file diff --git a/Emulator/Chip/Q1CpuInstructions.cs b/Emulator/Chip/Q1CpuInstructions.cs new file mode 100644 index 0000000..2da8969 --- /dev/null +++ b/Emulator/Chip/Q1CpuInstructions.cs @@ -0,0 +1,282 @@ +namespace Q1.Emulator.Chip; + +using System; +using System.Collections.Generic; + +public partial class Q1Cpu +{ + public static Dictionary InstructionLookupStrings = new() + { + { 0x00, ["NOP", "JMP", "HALT", "MOV"] }, + { 0x01, ["RET", "CALL", "SUS", "MOVB"] }, + { 0x02, ["BZ", "BZ", "BR", "NOP"] }, + { 0x03, ["NOP", "PUSH", "NOP", "NOP"] }, + { 0x04, ["NOP", "POP", "NOP", "NOP"] }, + + { 0x10, ["INV", "INV", "NOP", "AND"] }, + { 0x11, ["SHL", "SHL", "NOP", "OR"] }, + { 0x12, ["SHR", "SHR", "NOP", "XOR"] }, + + { 0x20, ["NOT", "NOT", "NOP", "CMP"] }, + { 0x21, ["NOP", "NOP", "NOP", "LT"] }, + { 0x22, ["NOP", "NOP", "NOP", "GT"] }, + + { 0x30, ["INC_AX", "INC", "NOP", "ADD"] }, + { 0x31, ["DEC_AX", "DEC", "NOP", "SUB"] }, + { 0x32, ["NOP", "NOP", "NOP", "DIV"] }, + { 0x33, ["NOP", "NOP", "NOP", "MUL"] }, + { 0x34, ["NOP", "NOP", "NOP", "MOD"] }, + }; + + public Dictionary, Action, Action)> InstructionLookup; + private void LoadInstructionLookup() + { + this.InstructionLookup = new Dictionary, Action, Action)> + { + { 0x00, (this.Instruction_NOP, this.Instruction_JMP, this.Instruction_HALT, this.Instruction_MOV) }, + { 0x01, (this.Instruction_RET, this.Instruction_CALL, this.Instruction_SUS, this.Instruction_MOVB) }, + { 0x02, (this.Instruction_BZ_LX, this.Instruction_BZ, this.Instruction_BR, this.Instruction_NOP) }, + { 0x03, (this.Instruction_NOP, this.Instruction_PUSH, this.Instruction_NOP, this.Instruction_NOP) }, + { 0x04, (this.Instruction_NOP, this.Instruction_POP, this.Instruction_NOP, this.Instruction_NOP) }, + + { 0x10, (this.Instruction_INV_DX, this.Instruction_INV, this.Instruction_NOP, this.Instruction_AND) }, + { 0x11, (this.Instruction_SHL_DX, this.Instruction_SHL, this.Instruction_NOP, this.Instruction_OR) }, + { 0x12, (this.Instruction_SHR_DX, this.Instruction_SHR, this.Instruction_NOP, this.Instruction_XOR) }, + + { 0x20, (this.Instruction_NOT_LX, this.Instruction_NOT, this.Instruction_NOP, this.Instruction_CMP) }, + { 0x21, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_LT) }, + { 0x22, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_GT) }, + + { 0x30, (this.Instruction_INC_AX, this.Instruction_INC, this.Instruction_NOP, this.Instruction_ADD) }, + { 0x31, (this.Instruction_DEC_AX, this.Instruction_DEC, this.Instruction_NOP, this.Instruction_SUB) }, + { 0x32, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_DIV) }, + { 0x33, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MUL) }, + { 0x34, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MOD) }, + }; + } + private void Instruction_ADD(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + u16 result = (u16) (v1 + v2); + this.AX = result; + } + private void Instruction_SUB(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + u16 result = (u16) (v1 - v2); + this.AX = result; + } + private void Instruction_MUL(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + u16 result = (u16) (v1 * v2); + this.AX = result; + } + private void Instruction_DIV(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + if (v2 == 0) + throw new DivideByZeroException("Cannot perform division by zero."); + u16 result = (u16) (v1 / v2); + this.AX = result; + } + private void Instruction_MOD(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + if (v2 == 0) + throw new DivideByZeroException("Cannot perform modulo operation with divisor zero."); + u16 result = (u16) (v1 % v2); + this.AX = result; + } + private void Instruction_INC(u8 obj) + { + var address = this.Address(obj); + u16 value = address.read(); + address.write((u16) (value + 1)); + } + private void Instruction_DEC(u8 obj) + { + var address = this.Address(obj); + u16 value = address.read(); + address.write((u16) (value - 1)); + } + private void Instruction_INC_AX() + { + this.AX = (u16) (this.AX + 1); + } + private void Instruction_DEC_AX() + { + this.AX = (u16) (this.AX - 1); + } + private void Instruction_SHL(u8 obj) + { + var address = this.Address(obj); + u16 value = address.read(); + u16 result = (u16) (value << 1); + address.write(result); + } + private void Instruction_SHR(u8 obj) + { + var address = this.Address(obj); + u16 value = address.read(); + u16 result = (u16) (value >> 1); + address.write(result); + } + private void Instruction_SHL_DX() + { + this.DX = (u16) (this.DX << 1); + } + private void Instruction_SHR_DX() + { + this.DX = (u16) (this.DX >> 1); + } + private void Instruction_XOR(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + u16 result = (u16) (v1 ^ v2); + this.DX = result; + } + private void Instruction_OR(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + u16 result = (u16) (v1 | v2); + this.DX = result; + } + private void Instruction_INV(u8 obj) + { + var address = this.Address(obj); + u16 value = address.read(); + u16 result = (u16) ~value; + this.DX = result; + } + private void Instruction_INV_DX() + { + this.DX = (u16) ~this.DX; + } + private void Instruction_AND(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + u16 result = (u16) (v1 & v2); + this.DX = result; + } + private void Instruction_NOT(u8 obj) + { + u16 value = this.Address(obj).read(); + u16 result = value == 0 ? (u16) 1 : (u16) 0; + this.LX = result; + } + private void Instruction_NOT_LX() + { + this.LX = this.LX == 0 ? (u16) 1 : (u16) 0; + } + private void Instruction_GT(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + this.LX = (u16) (v1 > v2 ? 1 : 0); + } + private void Instruction_LT(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + this.LX = (u16) (v1 < v2 ? 1 : 0); + } + + private void Instruction_BR() + { + this.FetchInstruction(out _, out _, out _, out u8 length); + this.PC += length; + } + private void Instruction_BZ_LX() + { + if (this.LX == 0) + this.Instruction_BR(); + } + private void Instruction_BZ(u8 obj) + { + u16 value = this.Address(obj).read(); + if (value == 0) + this.Instruction_BR(); + } + private void Instruction_CMP(u8 m1, u8 m2) + { + u16 v1 = this.Address(m1).read(); + u16 v2 = this.Address(m2).read(); + + // Set LX based on comparison + this.LX = (u16) (v1 == v2 ? 1 : 0); + } + private void Instruction_MOV(u8 m1, u8 m2) + { + u16 value = this.Address(m1).read(); + this.Address(m2).write(value); + } + + private void Instruction_MOVB(u8 m1, u8 m2) + { + u16 value = this.Address(m1).read(); + u8 low = Make.ref8(value)[1]; + this.Address(m2).writeByte(low); + } + + public void Instruction_NOP() { } + public void Instruction_NOP(u8 m1) { } + public void Instruction_NOP(u8 m1, u8 m2) { } + + public void Instruction_JMP(u8 m1) + { + this.PC = this.Address(m1).read(); + } + + public void Instruction_RET() + { + this.PC = this.PopStack(); + } + + public void Instruction_CALL(u8 m1) + { + u16 address = this.Address(m1).read(); + this.PushStack(this.PC); + this.PC = address; + } + + public void Instruction_HALT() + { + this.AbortRequested = true; + } + + public void Instruction_SUS() + { + this.PC -= 2; + } + + public void Instruction_PUSH(u8 m1) + { + u16 value = this.Address(m1).read(); + this.PushStack(value); + } + + public void Instruction_POP(u8 m1) + { + u16 value = this.PopStack(); + this.Address(m1).write(value); + } +} \ No newline at end of file diff --git a/Emulator/Chip/RamDevice.cs b/Emulator/Chip/RamDevice.cs new file mode 100644 index 0000000..8d5b051 --- /dev/null +++ b/Emulator/Chip/RamDevice.cs @@ -0,0 +1,33 @@ +namespace Q1.Emulator.Chip; + +public class RamDevice : BusDevice +{ + public u8[] Memory; + + public RamDevice(u16 start, u16 size) : base(start, (u16) (start + size)) + { + this.Memory = new u8[size]; + } + + public override void Reset() + { + this.Memory = new u8[this.AddressableEnd - this.AddressableStart]; + } + + public override void Clock() + { + // No clocking needed for RAM + } + + public override u8 Read(u16 address) + { + u16 relativeAddress = this.GetRelativeAddress(address); + return this.Memory[relativeAddress]; + } + + public override void Write(u16 address, u8 value) + { + u16 relativeAddress = this.GetRelativeAddress(address); + this.Memory[relativeAddress] = value; + } +} \ No newline at end of file -- cgit v1.3.1