summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.DS_Storebin8196 -> 8196 bytes
-rw-r--r--Assembler/Assembler.cs297
-rw-r--r--Assembler/Disassembler.cs67
-rw-r--r--Chip/Bus.cs18
-rw-r--r--Chip/BusDevice.cs18
-rw-r--r--Chip/DisplayDevice.cs21
-rw-r--r--Chip/Q1Cpu.cs9
-rw-r--r--Chip/Q1CpuAddressingModes.cs238
-rw-r--r--Chip/Q1CpuInstructions.cs133
-rw-r--r--Chip/RamDevice.cs13
-rw-r--r--DisplayEngine.cs2
-rw-r--r--Extensions.cs23
-rw-r--r--Make.cs5
-rw-r--r--Program.cs44
-rw-r--r--ROMs/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--ROMs/font copy.binbin0 -> 32768 bytes
-rw-r--r--ROMs/font.binbin0 -> 32768 bytes
-rw-r--r--ROMs/hello_world.dat33
-rw-r--r--ROMs/hello_world.q1bin0 -> 32842 bytes
-rw-r--r--ROMs/manual.q1 (renamed from ROMs/test.q1)bin10 -> 10 bytes
-rw-r--r--ROMs/script.dat24
-rw-r--r--ROMs/script.q1bin67 -> 0 bytes
-rw-r--r--ROMs/write_display_direct.dat21
-rw-r--r--ROMs/write_display_direct.q1bin0 -> 51 bytes
24 files changed, 567 insertions, 399 deletions
diff --git a/.DS_Store b/.DS_Store
index 7ae4f61..b56fb2b 100644
--- a/.DS_Store
+++ b/.DS_Store
Binary files differ
diff --git a/Assembler/Assembler.cs b/Assembler/Assembler.cs
index 5df068d..09ac30e 100644
--- a/Assembler/Assembler.cs
+++ b/Assembler/Assembler.cs
@@ -2,16 +2,32 @@ namespace Q1Emu.Assembler;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
+using Chip;
public class Assembler
{
public readonly BinaryWriter Writer;
public readonly StringReader Reader;
- private Dictionary<string, u16> _labels = new();
- private u16 _addressOffset = 0;
+ private Dictionary<string, u16> _labels = new();
+ private Dictionary<u16, string> _unloadedLabels = new();
+ private u16 _addressOffset = 0;
+
+ public static Dictionary<u16, string> RegisterLookup = new()
+ {
+ { 0x40, "AX" },
+ { 0x41, "AH" },
+ { 0x42, "AL" },
+ { 0x43, "DX" },
+ { 0x44, "DH" },
+ { 0x45, "DL" },
+ { 0x46, "LX" },
+ { 0x50, "PC" },
+ { 0x51, "SP" },
+ };
public Assembler(StringReader reader, BinaryWriter writer)
{
@@ -21,8 +37,10 @@ public class Assembler
public void Assemble()
{
+ int lineNumber = 0;
while (this.Reader.ReadLine() is { } line)
{
+ lineNumber++;
string trimmedLine = line.Split(";")[0].Trim(); // Remove comments
if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith(';'))
continue;
@@ -32,17 +50,91 @@ public class Assembler
// Handle .org directive
string orgValue = trimmedLine[5..].Trim();
this._addressOffset = this.GetConstant(orgValue);
- Console.WriteLine($"Setting address offset to {this._addressOffset:X4}");
+ }
+
+ else if (trimmedLine.StartsWith(".ascii"))
+ {
+ string asciiValue = trimmedLine[7..].Trim()
+ [1..^1]; // Remove quotes
+
+ for (int i = 0; i < asciiValue.Length; i++)
+ {
+ char c = asciiValue[i];
+ if (c == '\\')
+ c = asciiValue[++i] switch
+ {
+ 'n' => '\n',
+ 't' => '\t',
+ '\\' => '\\',
+ '\'' => '\'',
+ '"' => '"',
+ _ => throw new AssemblerException($"Unknown escape sequence: \\{asciiValue[i]}"),
+ };
+
+ this.Write((u8) c);
+ }
+ }
+
+ else if (trimmedLine.StartsWith(".raw"))
+ {
+ string[] values = trimmedLine[5..].Split(',');
+ foreach (string value in values)
+ this.Write(GetConstant(value.Trim()));
+ }
+
+ else if (trimmedLine.StartsWith(".include"))
+ {
+ string includeFile = trimmedLine[9..].Trim()[1..^1];
+ if (!File.Exists(includeFile))
+ {
+ WriteError(line, lineNumber, $"Include file '{includeFile}' not found");
+ return;
+ }
+
+ foreach (byte b in File.ReadAllBytes(includeFile))
+ this.Writer.Write(b);
}
else if (trimmedLine.EndsWith(":"))
this._labels[trimmedLine[..^1]] = (u16) (this.Writer.BaseStream.Position + this._addressOffset);
-
+
+ else
+ {
+ try
+ {
+ this.ParseInstruction(trimmedLine);
+ }
+ catch (AssemblerException ex)
+ {
+ WriteError(line, lineNumber, ex.Message);
+ return;
+ }
+ }
+ }
+
+ foreach ((u16 key, string value) in this._unloadedLabels)
+ {
+ if (this._labels.TryGetValue(value, out u16 address))
+ {
+ Console.WriteLine($"Resolving label '{value}' at address {address:X4} for key {key:X4}");
+ this.Writer.BaseStream.Seek(key, SeekOrigin.Begin);
+ this.Write(address);
+ }
else
- this.ParseInstruction(trimmedLine);
+ {
+ WriteError($"", 0, "Label not defined");
+ }
}
}
+ private void WriteError(string line, int lineNumber, string message)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"Error on line {lineNumber}: {message}");
+ Console.WriteLine($" --> {line}");
+ Console.ResetColor();
+ }
+
private void ParseInstruction(string instruction)
{
string[] parts = instruction.Split(' ', 2);
@@ -50,37 +142,93 @@ public class Assembler
parts = parts.Length > 1 ? parts[1].Split(',') : [];
if (parts.Length > 2)
- throw new ArgumentException($"Invalid instruction format: {instruction}");
+ throw new AssemblerException($"Invalid instruction format");
- u8 opcodeValue = this.ParseOpcode(opcode);
+ u8 opcodeValue = this.ParseOpcode(opcode, parts.Length, out u8 addressingIndex);
- u8 m1 = 0, m2 = 0;
- if (parts.Length > 0)
- m1 = this.ParseMode(parts[0]);
- if (parts.Length > 1)
- m2 = this.ParseMode(parts[1]);
+ u8 m1, m2;
+ switch (addressingIndex)
+ {
+ case 0: // Implicit
+ if (parts.Length > 0)
+ throw new AssemblerException($"Implicit instruction should not have operands");
+
+ m1 = 0x00;
+ m2 = 0x00;
+ break;
+
+ case 1: // Simple addressing mode
+ if (parts.Length > 2)
+ throw new AssemblerException($"Simple addressing mode should have at most one operand");
+ if (parts.Length == 0)
+ throw new AssemblerException($"Simple addressing mode requires an operand");
+
+ m1 = this.ParseMode(parts[0]);
+ m2 = 0x00;
+ break;
+
+ case 2: // Extended implicit
+ if (parts.Length > 0)
+ throw new AssemblerException($"Extended implicit instruction should not have operands");
+
+ m1 = 0x00;
+ m2 = 0x01;
+ break;
+
+ case 3: // Dual addressing mode
+ if (parts.Length != 2)
+ throw new AssemblerException($"Dual addressing mode requires exactly two operands");
+
+ m1 = this.ParseMode(parts[0]);
+ m2 = this.ParseMode(parts[1]);
+ break;
+
+ default:
+ throw new UnreachableException("Invalid addressing index: " + addressingIndex);
+ }
- Console.WriteLine($"Parsed instruction: {opcode} with modes {m1:X2}, {m2:X2} (at address {this.Writer.BaseStream.Position + this._addressOffset:X4})");
this.WriteInstruction(opcodeValue, m1, m2);
+
if (parts.Length > 0)
this.WriteMode(m1, parts[0]);
if (parts.Length > 1)
this.WriteMode(m2, parts[1]);
}
- private u8 ParseOpcode(string opcode)
+ private u8 ParseOpcode(string opcode, int parts, out u8 addressingIndex)
{
- return opcode switch
+ List<int> addressingIndices = new();
+ u8? opCode = null;
+ foreach ((u8 key, string[] value) in Q1Cpu.InstructionLookupStrings)
{
- "NOP" => 0x00, "JMP" => 0x00, "MOV" => 0x00,
- "RET" => 0x01, "CALL" => 0x01, "CMP" => 0x02,
- "BR" => 0x02, "BZ" => 0x02, "LT" => 0x02,
- "GT" => 0x03,
- "INC" => 0x08, "ADD" => 0x08,
- "DEC" => 0x09,
- "MUL" => 0x0B,
- _ => throw new ArgumentException($"Unknown opcode: {opcode}")
- };
+ if (value.Contains(opcode))
+ {
+ addressingIndices.AddRange(value.AllIndexesOf(opcode));
+ if (opCode is not null && opCode != key)
+ throw new AssemblerException($"Ambiguous opcode '{opcode}' matches multiple instructions: {opCode:X4} and {key:X4}");
+
+ opCode = key;
+ }
+ }
+
+ if (opCode is null)
+ throw new AssemblerException($"Unknown opcode '{opcode}'");
+
+ int Parts(int index)
+ => index switch
+ {
+ 0 => 0, // Implicit
+ 1 => 1, // Simple addressing mode
+ 2 => 0, // Extended implicit
+ 3 => 2, // Dual addressing mode
+ _ => throw new UnreachableException("Invalid addressing index: " + index)
+ };
+
+ if (addressingIndices.All(x => Parts(x) != parts))
+ throw new AssemblerException($"Opcode '{opcode}' does not match the expected number of operands: {parts}");
+
+ addressingIndex = (u8) addressingIndices.First(x => Parts(x) == parts);
+ return opCode.Value;
}
private u8 ParseMode(string mode)
@@ -92,20 +240,7 @@ public class Assembler
if (text.Length > 1 && text[0] == 'V')
return true;
- string[] hardcodedRegisters =
- [
- "AX",
- "AH",
- "AL",
- "DX",
- "DH",
- "DL",
- "LX",
- "PC",
- "SP",
- ];
-
- return hardcodedRegisters.Contains(text);
+ return Assembler.RegisterLookup.ContainsValue(text);
}
if (this._labels.ContainsKey(mode))
@@ -157,12 +292,12 @@ public class Assembler
this.Write(immediateValue);
break;
- case 0x02: // Direct
+ case 0x02: // Direct
u16 directAddress = this.GetConstant(operand); // Remove brackets
this.Write(directAddress);
break;
- case 0x03: // Indirect
+ case 0x03: // Indirect
u16 indirectAddress = this.GetConstant(operand); // Remove brackets and @
this.Write(indirectAddress);
break;
@@ -177,7 +312,7 @@ public class Assembler
Write(regIndex);
break;
- case 0x06: // Register Indirect
+ case 0x06: // Register Indirect
u8 indirectRegIndex = this.GetRegisterIndex(operand); // Remove brackets
Write(indirectRegIndex);
break;
@@ -190,54 +325,26 @@ public class Assembler
break;
}
}
- private i16 GetSignedConstant(string value)
- {
- value = this.Clean(value);
-
- if (value.StartsWith("$"))
- {
- // Hexadecimal offset
- return Convert.ToInt16(value[1..], 16);
- }
- else if (i16.TryParse(value, out i16 result))
- // Decimal offset
- return result;
- else
- {
- throw new ArgumentException($"Invalid offset value: {value}");
- }
- }
private u8 GetRegisterIndex(string reg)
{
reg = this.Clean(reg);
-
+
if (reg.StartsWith("V"))
if (u8.TryParse(reg[1..], out u8 index) && index < 0x3F)
return index;
- return reg switch
+ if (Assembler.RegisterLookup.ContainsValue(reg))
{
- "AX" => 0x40,
- "AH" => 0x41,
- "AL" => 0x42,
- "DX" => 0x43,
- "DH" => 0x44,
- "DL" => 0x45,
- "LX" => 0x46,
- "PC" => 0x50,
- "SP" => 0x51,
- _ => throw new ArgumentException($"Invalid register index: {reg}")
- };
+ // Find the register index by value
+ return (u8) Assembler.RegisterLookup.FirstOrDefault(x => x.Value == reg).Key;
+ }
+
+ throw new AssemblerException($"Invalid register: {reg}");
}
private u16 GetConstant(string value)
{
value = this.Clean(value);
-
- if (this._labels.ContainsKey(value))
- {
- // Label reference
- return this._labels[value];
- }
+
if (value.StartsWith("$"))
{
// Hexadecimal constant (backwards) $1FFF
@@ -249,7 +356,26 @@ public class Assembler
return result;
else
{
- throw new ArgumentException($"Invalid constant value: {value}");
+ this._unloadedLabels[(u16) this.Writer.BaseStream.Position] = value;
+ return 0;
+ }
+ }
+ private i16 GetSignedConstant(string value)
+ {
+ value = this.Clean(value);
+
+ if (value.StartsWith("$"))
+ {
+ // Hexadecimal offset
+ return Convert.ToInt16(value[1..], 16);
+ }
+ else if (i16.TryParse(value, out i16 result))
+ // Decimal offset
+ return result;
+ else
+ {
+ this._unloadedLabels[(u16) this.Writer.BaseStream.Position] = value;
+ return 0;
}
}
@@ -257,27 +383,30 @@ public class Assembler
{
if (value.StartsWith("@"))
value = value[1..]; // Remove @ for indirect addressing
-
+
if (value.StartsWith("[") && value.EndsWith("]"))
value = value[1..^1].Trim(); // Remove brackets for direct addressing
-
+
return value.Trim();
}
-
+
private void Write(u16 value)
{
u16 swappedValue = (u16) ((value >> 8) | (value << 8));
this.Writer.Write(swappedValue);
}
-
+
private void Write(u8 value)
{
this.Writer.Write(value);
}
-
+
private void Write(i16 value)
{
u16 swappedValue = (u16) ((value >> 8) | (value << 8));
this.Writer.Write(swappedValue);
}
-} \ No newline at end of file
+}
+
+public class AssemblerException(string message)
+ : Exception(message); \ No newline at end of file
diff --git a/Assembler/Disassembler.cs b/Assembler/Disassembler.cs
index 9b1c44d..816ddbc 100644
--- a/Assembler/Disassembler.cs
+++ b/Assembler/Disassembler.cs
@@ -2,29 +2,21 @@ namespace Q1Emu.Assembler;
using System;
using System.Collections.Generic;
+using System.IO;
+using Chip;
public class Disassembler
{
- public static Dictionary<u8, string[]> InstructionLookup = new()
+ public readonly StringWriter Writer;
+ public readonly BinaryReader Reader;
+
+ public Disassembler(StringWriter writer, BinaryReader reader)
{
- { 0x00, ["NOP", "JMP", "HALT", "MOV"] },
- { 0x01, ["RET", "CALL", "SUS", "CMP"] },
- { 0x02, ["BR", "BZ", "BZ_LX", "LT"] },
- { 0x03, ["NOP", "NOP", "NOP", "GT"] },
-
- { 0x04, ["NOT_LX", "NOT", "NOP", "AND"] },
- { 0x05, ["INV_DX", "INV", "NOP", "OR"] },
- { 0x06, ["SHL_DX", "SHL", "NOP", "XOR"] },
- { 0x07, ["SHR_DX", "SHR", "NOP", "NOP"] },
-
- { 0x08, ["INC_AX", "INC", "NOP", "ADD"] },
- { 0x09, ["DEC_AX", "DEC", "NOP", "SUB"] },
- { 0x0A, ["NOP", "NOP", "NOP", "DIV"] },
- { 0x0B, ["NOP", "NOP", "NOP", "MUL"] },
- { 0x0C, ["NOP", "NOP", "NOP", "MOD"] },
- };
+ this.Writer = writer;
+ this.Reader = reader;
+ }
- public static Dictionary<u8, string> ModeLookup = new()
+ private static Dictionary<u8, string> ModeLookup = new()
{
{ 0x00, "" },
{ 0x01, "c" },
@@ -35,7 +27,7 @@ public class Disassembler
{ 0x06, "[r]" },
{ 0x07, "[r + c]" },
};
-
+
public static string ParseInstruction(u16 instruction)
{
u8 op = (u8) ((instruction >> 8) & 0xFF); // 256 opcodes
@@ -45,43 +37,38 @@ public class Disassembler
u8 i = (m1, m2) switch
{
(0x00, 0x00) => 0, // Implicit
- (0x00, _) => 2, // Extended implicit
+ (0x00, 0x01) => 2, // Extended implicit
(_, 0x00) => 1, // Simple addressing mode
_ => 3 // Extended addressing mode
};
-
+
string opcodeName = GetOpcodeName(op, i);
- string mode1 = GetModeName(m1);
- string mode2 = GetModeName(m2);
- if (i == 0)
+ if (i == 0 || i == 2)
return opcodeName;
-
- if (i == 2)
- return $"{opcodeName}";
-
+
if (i == 1)
- return $"{opcodeName} {mode1}";
-
+ return $"{opcodeName} {GetModeName(m1)}";
+
if (i == 3)
- return $"{opcodeName} {mode1}, {mode2}";
-
+ return $"{opcodeName} {GetModeName(m1)}, {GetModeName(m2)}";
+
throw new InvalidOperationException($"Unknown instruction format for opcode {op:X2} with modes {m1:X2}, {m2:X2}");
}
-
+
private static string GetOpcodeName(u8 opcode, u8 i)
{
- if (!InstructionLookup.ContainsKey(opcode) || i >= InstructionLookup[opcode].Length)
- return $"{opcode:X2}";
-
- return InstructionLookup[opcode][i];
+ if (!Q1Cpu.InstructionLookupStrings.TryGetValue(opcode, out string[]? opNames))
+ return $"[{opcode:X2}]"; // Fallback for unknown opcodes
+
+ return opNames[i];
}
-
+
private static string GetModeName(u8 mode)
{
- if (ModeLookup.TryGetValue(mode, out string modeName))
+ if (ModeLookup.TryGetValue(mode, out string? modeName))
return modeName;
-
+
return $"[{mode:X2}]"; // Fallback for unknown modes
}
} \ No newline at end of file
diff --git a/Chip/Bus.cs b/Chip/Bus.cs
index 951d23f..3578ad6 100644
--- a/Chip/Bus.cs
+++ b/Chip/Bus.cs
@@ -34,22 +34,4 @@ public class Bus : BusDevice
device.Write(address, value);
}
-
- public override ref8 ReadSeq(u16 address, u16 length)
- {
- BusDevice? device = this.GetDevice(address);
- if (device == null)
- return new u8[length];
-
- return device.ReadSeq(address, length);
- }
-
- public override void WriteSeq(u16 address, u16 length, ref8 value)
- {
- BusDevice? device = this.GetDevice(address);
- if (device == null)
- return;
-
- device.WriteSeq(address, length, value);
- }
} \ No newline at end of file
diff --git a/Chip/BusDevice.cs b/Chip/BusDevice.cs
index e1f559c..8b677c5 100644
--- a/Chip/BusDevice.cs
+++ b/Chip/BusDevice.cs
@@ -14,20 +14,26 @@ public abstract class BusDevice
public abstract u8 Read(u16 address);
public abstract void Write(u16 address, u8 value);
- public abstract ref8 ReadSeq(u16 address, u16 length);
- public abstract void WriteSeq(u16 address, u16 length, ref8 value);
-
public u16 ReadWord(u16 address)
{
- ref8 data = this.ReadSeq(address, 2);
- u16 value = Make.u16(data);
+ 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.WriteSeq(address, 2, data);
+ this.Write(address, data[0]);
+ this.Write((u16) (address + 1), data[1]);
+ }
+
+ public i16 ReadSignedWord(u16 address)
+ {
+ u8 high = this.Read(address);
+ u8 low = this.Read((u16) (address + 1));
+ return Make.i16(high, low);
}
public virtual void Clock() { }
diff --git a/Chip/DisplayDevice.cs b/Chip/DisplayDevice.cs
index 28bf4e0..1bf4f14 100644
--- a/Chip/DisplayDevice.cs
+++ b/Chip/DisplayDevice.cs
@@ -31,12 +31,12 @@ public class DisplayDevice : RamDevice
public override void Clock()
{
Color[] palette = this.ReadPalette();
- ref8 pixelData = this.ReadSeq((u16) (this.AddressableStart + this._paletteSize * 3), (u16) (this._displayWidth * this._displayHeight));
-
+ 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 < textureData.Length; i++)
{
u8 pixelValue = pixelData[i];
- textureData[i] = palette[pixelValue];
+ textureData[i] = palette[pixelValue % this._paletteSize];
}
if (this.OutputTexture != null)
@@ -45,7 +45,7 @@ public class DisplayDevice : RamDevice
private Color[] ReadPalette()
{
- ref8 paletteData = this.ReadSeq(this.AddressableStart, (u16) (this._paletteSize * 3));
+ ref8 paletteData = this.Memory.AsSpan(0, (u16) (this._paletteSize * 3));
Color[] colors = new Color[this._paletteSize + 1];
colors[0] = Color.Transparent;
@@ -66,7 +66,7 @@ public class DisplayDevice : RamDevice
if (palette.Length != this._paletteSize)
throw new ArgumentException($"Palette must have {this._paletteSize} colors.");
- ref8 paletteData = this.ReadSeq(this.AddressableStart, (u16) (this._paletteSize * 3));
+ ref8 paletteData = this.Memory.AsSpan(0, (u16) (this._paletteSize * 3));
for (u16 i = 0; i < this._paletteSize; i++)
{
u16 offset = (u16) (i * 3);
@@ -75,15 +75,4 @@ public class DisplayDevice : RamDevice
paletteData[offset + 2] = palette[i].B;
}
}
-
- public override void Write(u16 address, u8 value)
- {
- Console.WriteLine($"DisplayDevice.Write: {address:X4} = {value:X2}");
- base.Write(address, value);
- }
- public override void WriteSeq(u16 address, u16 length, ref8 value)
- {
- Console.WriteLine($"DisplayDevice.WriteSeq: {address:X4} length={length} value={Make.u16(value)}");
- base.WriteSeq(address, length, value);
- }
} \ No newline at end of file
diff --git a/Chip/Q1Cpu.cs b/Chip/Q1Cpu.cs
index 910ad64..eefc61c 100644
--- a/Chip/Q1Cpu.cs
+++ b/Chip/Q1Cpu.cs
@@ -7,7 +7,7 @@ using Assembler;
public partial class Q1Cpu
{
- public const u16 StackStart = 0x30;
+ public const u16 StackStart = 0x2FFF;
public const u16 StackLength = 16 * 2; // 16 16-bit values
public Random Random;
@@ -45,7 +45,8 @@ public partial class Q1Cpu
{
var pc = this.PC;
u16 instruction = this.FetchInstruction(out u8 op, out u8 m1, out u8 m2, out _);
- Console.WriteLine($"{pc:X4}: {Disassembler.ParseInstruction(instruction)}");
+ Console.WriteLine($"CPU.Clock: {pc:X4} | {Disassembler.ParseInstruction(instruction)}");
+ // Console.WriteLine($"{V[0]:X4} {V[1]:X4} {V[2]:X4} {V[3]:X4} {V[4]:X4} {V[5]:X4} {V[6]:X4} {V[7]:X4}");
bool ex = m2 > 0;
switch (m1, ex)
@@ -84,7 +85,7 @@ public partial class Q1Cpu
if (this.SP + 2 >= StackStart + StackLength)
throw new InvalidOperationException("Stack overflow");
- this.Bus.WriteSeq(this.SP, 2, Make.ref8(value));
+ this.Bus.WriteWord(this.SP, value);
this.SP += 2;
}
@@ -94,7 +95,7 @@ public partial class Q1Cpu
throw new InvalidOperationException("Stack underflow");
this.SP -= 2;
- return Make.u16(this.Bus.ReadSeq(this.SP, 2));
+ return this.Bus.ReadWord(this.SP);
}
private u16 FetchRegister(u8 regIndex)
diff --git a/Chip/Q1CpuAddressingModes.cs b/Chip/Q1CpuAddressingModes.cs
index f809ff3..2b79db7 100644
--- a/Chip/Q1CpuAddressingModes.cs
+++ b/Chip/Q1CpuAddressingModes.cs
@@ -5,7 +5,7 @@ using System.Collections.Generic;
public partial class Q1Cpu
{
- public u16 FetchData(u8 mode)
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) Address(u8 mode)
{
return mode switch
{
@@ -20,61 +20,8 @@ public partial class Q1Cpu
_ => throw new InvalidOperationException($"Unknown addressing mode: {mode:X2}"),
};
}
-
- public void StoreData(u8 mode, u16 value)
- {
- switch (mode)
- {
- case 0x0:
- throw new InvalidOperationException("Cannot store data in implicit mode.");
- case 0x1:
- throw new InvalidOperationException("Cannot store data in immediate mode.");
- case 0x2:
- this.AddressingMode_Direct_Store(value);
- break;
- case 0x3:
- this.AddressingMode_Indirect_Store(value);
- break;
- case 0x4:
- this.AddressingMode_Relative_Store(value);
- break;
- case 0x5:
- this.AddressingMode_Register_Store(value);
- break;
- case 0x6:
- this.AddressingMode_RegisterIndirect_Store(value);
- break;
- case 0x7:
- this.AddressingMode_RegisterIndirectOffset_Store(value);
- break;
- default:
- throw new InvalidOperationException($"Unknown addressing mode: {mode:X2}");
- }
- }
-
- public void TransformData(u8 mode, Func<u16, u16> transform)
- {
- // if (mode != 0x05)
- // {
- // u16 address = this.FetchData(mode);
- // u16 value = this.Bus.ReadWord(address);
- // u16 transformedValue = transform(value);
- // this.Bus.WriteWord(address, transformedValue);
- // }
- // else
- // {
- // u8 regIndex = this.Bus.Read(this.PC);
- // this.PC += 1;
- //
- // u16 value = this.FetchRegister(regIndex);
- // u16 transformedValue = transform(value);
- // this.StoreRegister(regIndex, transformedValue);
- // }
-
- throw new NotImplementedException("TransformData is not implemented yet.");
- }
- public u8 AddressingModeLength(u8 mode)
+ private u8 AddressingModeLength(u8 mode)
{
return mode switch
{
@@ -90,133 +37,152 @@ public partial class Q1Cpu
};
}
- public u16 AddressingMode_Implicit()
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_Implicit()
{
- return 0;
+ 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 u16 AddressingMode_Immediate()
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_Immediate()
{
u16 value = this.Bus.ReadWord(this.PC);
this.PC += 2;
- return value;
+ 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 u16 AddressingMode_Direct()
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_Direct()
{
u16 address = this.Bus.ReadWord(this.PC);
this.PC += 2;
-
- u16 value = this.Bus.ReadWord(address);
- return value;
+
+ 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 u16 AddressingMode_Indirect()
+ public (Func<u16> read, Action<u16> write, Action<u8> 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;
+ }
- 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 u16 AddressingMode_Relative()
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_Relative()
{
u16 offset = this.Bus.ReadWord(this.PC);
this.PC += 2;
u16 address = (u16) (this.PC + offset);
- return address;
- }
-
- public u16 AddressingMode_Register()
- {
- u8 regIndex = this.Bus.Read(this.PC);
- this.PC += 1;
-
- return this.FetchRegister(regIndex);
- }
-
- public u16 AddressingMode_RegisterIndirect()
- {
- u8 regIndex = this.Bus.Read(this.PC);
- this.PC += 1;
-
- u16 address = this.FetchRegister(regIndex);
- u16 value = this.Bus.ReadWord(address);
- Console.WriteLine($"Value at {address:X4}: {value:X4}");
- return value;
- }
-
- public u16 AddressingMode_RegisterIndirectOffset()
- {
- u8 regIndex = this.Bus.Read(this.PC);
- this.PC += 1;
-
- i16 offset = Make.i16(this.Bus.ReadSeq(this.PC, 2));
- Console.WriteLine("Offset: " + offset);
- this.PC += 2;
- u16 address = (u16) (this.FetchRegister(regIndex) + offset);
- u16 value = this.Bus.ReadWord(address);
- return value;
- }
-
- public void AddressingMode_Direct_Store(u16 data)
- {
- u16 address = this.Bus.ReadWord(this.PC);
- this.PC += 2;
-
- this.Bus.WriteWord(address, data);
- }
-
- public void AddressingMode_Indirect_Store(u16 data)
- {
- u16 address = this.Bus.ReadWord(this.PC);
- this.PC += 2;
-
- u16 indirectAddress = this.Bus.ReadWord(address);
- this.Bus.WriteWord(indirectAddress, data);
- }
-
- public void AddressingMode_Relative_Store(u16 data)
- {
- u16 offset = this.Bus.ReadWord(this.PC);
- this.PC += 2;
-
- u16 address = (u16) (this.PC + offset);
- this.Bus.WriteWord(address, data);
+ 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 void AddressingMode_Register_Store(u16 data)
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_Register()
{
u8 regIndex = this.Bus.Read(this.PC);
this.PC += 1;
- this.StoreRegister(regIndex, data);
+ 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 void AddressingMode_RegisterIndirect_Store(u16 data)
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_RegisterIndirect()
{
u8 regIndex = this.Bus.Read(this.PC);
this.PC += 1;
- u16 address = this.FetchRegister(regIndex);
- this.Bus.WriteWord(address, data);
+ 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 void AddressingMode_RegisterIndirectOffset_Store(u16 data)
+ public (Func<u16> read, Action<u16> write, Action<u8> writeByte) AddressingMode_RegisterIndirectOffset()
{
u8 regIndex = this.Bus.Read(this.PC);
this.PC += 1;
- i16 offset = Make.i16(this.Bus.ReadSeq(this.PC, 2));
+ i16 offset = this.Bus.ReadSignedWord(this.PC);
this.PC += 2;
- u16 address = (u16) (this.FetchRegister(regIndex) + offset);
- this.Bus.WriteWord(address, data);
+ 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/Chip/Q1CpuInstructions.cs b/Chip/Q1CpuInstructions.cs
index 8b79a1d..b31a102 100644
--- a/Chip/Q1CpuInstructions.cs
+++ b/Chip/Q1CpuInstructions.cs
@@ -5,56 +5,79 @@ using System.Collections.Generic;
public partial class Q1Cpu
{
+ public static Dictionary<u8, string[]> InstructionLookupStrings = new()
+ {
+ { 0x00, ["NOP", "JMP", "HALT", "MOV"] },
+ { 0x01, ["RET", "CALL", "SUS", "MOVB"] },
+ { 0x02, ["BZ", "BZ", "BR", "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<u8, (Action, Action<u8>, Action, Action<u8, u8>)> InstructionLookup;
private void LoadInstructionLookup()
{
this.InstructionLookup = new Dictionary<u8, (Action, Action<u8>, Action, Action<u8, u8>)>
{
{ 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_CMP) },
- { 0x02, (this.Instruction_BR, this.Instruction_BZ, this.Instruction_BZ_LX, this.Instruction_LT) },
- { 0x03, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_GT) },
+ { 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) },
- { 0x04, (this.Instruction_NOT_LX, this.Instruction_NOT, this.Instruction_NOP, this.Instruction_AND) },
- { 0x05, (this.Instruction_INV_DX, this.Instruction_INV, this.Instruction_NOP, this.Instruction_OR) },
- { 0x06, (this.Instruction_SHL_DX, this.Instruction_SHL, this.Instruction_NOP, this.Instruction_XOR) },
- { 0x07, (this.Instruction_SHR_DX, this.Instruction_SHR, 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) },
- { 0x08, (this.Instruction_INC_AX, this.Instruction_INC, this.Instruction_NOP, this.Instruction_ADD) },
- { 0x09, (this.Instruction_DEC_AX, this.Instruction_DEC, this.Instruction_NOP, this.Instruction_SUB) },
- { 0x0A, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_DIV) },
- { 0x0B, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MUL) },
- { 0x0C, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MOD) },
+ { 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.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(m1);
- u16 v2 = this.FetchData(m2);
+ u16 v1 = this.Address(m1).read();
+ u16 v2 = this.Address(m2).read();
if (v2 == 0)
throw new DivideByZeroException("Cannot perform division by zero.");
@@ -63,8 +86,8 @@ public partial class Q1Cpu
}
private void Instruction_MOD(u8 m1, u8 m2)
{
- u16 v1 = this.FetchData(m1);
- u16 v2 = this.FetchData(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.");
@@ -73,11 +96,15 @@ public partial class Q1Cpu
}
private void Instruction_INC(u8 obj)
{
- this.TransformData(obj, value => (u16)(value + 1));
+ var address = this.Address(obj);
+ u16 value = address.read();
+ address.write((u16)(value + 1));
}
private void Instruction_DEC(u8 obj)
{
- this.TransformData(obj, value => (u16)(value - 1));
+ var address = this.Address(obj);
+ u16 value = address.read();
+ address.write((u16)(value - 1));
}
private void Instruction_INC_AX()
{
@@ -89,11 +116,17 @@ public partial class Q1Cpu
}
private void Instruction_SHL(u8 obj)
{
- this.TransformData(obj, value => (u16)(value << 1));
+ var address = this.Address(obj);
+ u16 value = address.read();
+ u16 result = (u16)(value << 1);
+ address.write(result);
}
private void Instruction_SHR(u8 obj)
{
- this.TransformData(obj, value => (u16)(value >> 1));
+ var address = this.Address(obj);
+ u16 value = address.read();
+ u16 result = (u16)(value >> 1);
+ address.write(result);
}
private void Instruction_SHL_DX()
{
@@ -105,25 +138,26 @@ public partial class Q1Cpu
}
private void Instruction_XOR(u8 m1, u8 m2)
{
- u16 v1 = this.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(m1);
- u16 v2 = this.FetchData(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)
{
- u16 value = this.FetchData(obj);
+ var address = this.Address(obj);
+ u16 value = address.read();
u16 result = (u16)~value;
- this.DX = result;
+ address.write(result);
}
private void Instruction_INV_DX()
{
@@ -131,15 +165,15 @@ public partial class Q1Cpu
}
private void Instruction_AND(u8 m1, u8 m2)
{
- u16 v1 = this.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(obj);
+ u16 value = this.Address(obj).read();
u16 result = value == 0 ? (u16)1 : (u16)0;
this.LX = result;
}
@@ -149,15 +183,15 @@ public partial class Q1Cpu
}
private void Instruction_GT(u8 m1, u8 m2)
{
- u16 v1 = this.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(m1);
- u16 v2 = this.FetchData(m2);
+ u16 v1 = this.Address(m1).read();
+ u16 v2 = this.Address(m2).read();
this.LX = (u16)(v1 < v2 ? 1 : 0);
}
@@ -174,22 +208,29 @@ public partial class Q1Cpu
}
private void Instruction_BZ(u8 obj)
{
- u16 value = this.FetchData(obj);
+ u16 value = this.Address(obj).read();
if (value == 0)
this.Instruction_BR();
}
private void Instruction_CMP(u8 m1, u8 m2)
{
- u16 v1 = this.FetchData(m1);
- u16 v2 = this.FetchData(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.FetchData(m1);
- this.StoreData(m2, value);
+ 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() { }
@@ -198,7 +239,7 @@ public partial class Q1Cpu
public void Instruction_JMP(u8 m1)
{
- this.PC = this.FetchData(m1);
+ this.PC = this.Address(m1).read();
}
public void Instruction_RET()
@@ -208,7 +249,7 @@ public partial class Q1Cpu
public void Instruction_CALL(u8 m1)
{
- u16 address = this.FetchData(m1);
+ u16 address = this.Address(m1).read();
this.PushStack(this.PC);
this.PC = address;
}
diff --git a/Chip/RamDevice.cs b/Chip/RamDevice.cs
index 6811d22..509a2df 100644
--- a/Chip/RamDevice.cs
+++ b/Chip/RamDevice.cs
@@ -33,17 +33,4 @@ public class RamDevice : BusDevice
u16 relativeAddress = this.GetRelativeAddress(address);
this.Memory[relativeAddress] = value;
}
-
- public override ref8 ReadSeq(u16 address, u16 length)
- {
- u16 relativeAddress = this.GetRelativeAddress(address);
- // Console.WriteLine($"{address:X4} {relativeAddress:X4} {length:X4}");
- return new ref8(this.Memory, relativeAddress, length);
- }
-
- public override void WriteSeq(u16 address, u16 length, ref8 value)
- {
- ref8 destination = this.ReadSeq(address, length);
- value.CopyTo(destination);
- }
} \ No newline at end of file
diff --git a/DisplayEngine.cs b/DisplayEngine.cs
index f983226..3f67bed 100644
--- a/DisplayEngine.cs
+++ b/DisplayEngine.cs
@@ -61,7 +61,7 @@ public class DisplayEngine : Game
protected override void Draw(GameTime gameTime)
{
- GraphicsDevice.Clear(Color.CornflowerBlue);
+ // GraphicsDevice.Clear(Color.CornflowerBlue);
this._requestUpdate();
diff --git a/Extensions.cs b/Extensions.cs
new file mode 100644
index 0000000..6303c8c
--- /dev/null
+++ b/Extensions.cs
@@ -0,0 +1,23 @@
+namespace Q1Emu;
+
+using System.Collections.Generic;
+
+public static class Extensions
+{
+ public static IEnumerable<int> AllIndexesOf<T>(this IEnumerable<T> arr, T q)
+ {
+ var indexes = new List<int>();
+ int index = 0;
+
+ foreach (var item in arr)
+ {
+ if (EqualityComparer<T>.Default.Equals(item, q))
+ {
+ indexes.Add(index);
+ }
+ index++;
+ }
+
+ return indexes;
+ }
+} \ No newline at end of file
diff --git a/Make.cs b/Make.cs
index 293312e..24f3ac2 100644
--- a/Make.cs
+++ b/Make.cs
@@ -50,4 +50,9 @@ public static class Make
{
return BinaryPrimitives.ReadInt16BigEndian(value);
}
+
+ public static i16 i16(u8 high, u8 low)
+ {
+ return BinaryPrimitives.ReadInt16BigEndian(new ReadOnlySpan<u8>([high, low]));
+ }
} \ No newline at end of file
diff --git a/Program.cs b/Program.cs
index 63906a0..016e557 100644
--- a/Program.cs
+++ b/Program.cs
@@ -14,6 +14,10 @@ internal class Program
if (args.Length > 0)
{
string filePath = args[0];
+
+ Environment.CurrentDirectory = Path.GetDirectoryName(filePath) ?? Environment.CurrentDirectory;
+ filePath = Path.GetFileName(filePath);
+
if (filePath.EndsWith(".q1"))
{
Run(filePath);
@@ -34,7 +38,7 @@ internal class Program
{
StringReader reader = new(File.ReadAllText(filePath));
BinaryWriter writer = new(File.Create(Path.ChangeExtension(filePath, ".q1")));
-
+
Assembler assembler = new(reader, writer);
assembler.Assemble();
writer.Close();
@@ -49,31 +53,49 @@ internal class Program
File.ReadAllBytes(filePath).CopyTo(ram.Memory, 0);
cpu.PC = ram.AddressableStart;
-
+
DisplayDevice display = new(16, 64, 32, 0x3FFF);
display.SetPalette([
- Color.Red, Color.Green, Color.Blue, Color.Yellow,
- Color.Cyan, Color.Magenta, Color.White, Color.Black,
- Color.Gray, Color.Orange, Color.Purple, Color.Brown,
- Color.LightGray, Color.DarkGray, Color.LightBlue, Color.LightGreen]);
-
+ Color.Red,
+ Color.Green,
+ Color.Blue,
+ Color.Yellow,
+ Color.Cyan,
+ Color.Magenta,
+ Color.White,
+ Color.Black,
+ Color.Gray,
+ Color.Orange,
+ Color.Purple,
+ Color.Brown,
+ Color.LightGray,
+ Color.DarkGray,
+ Color.LightBlue,
+ Color.LightGreen
+ ]);
+
cpu.Bus = bus;
ram.AttachToBus(bus);
display.AttachToBus(bus);
- int frameRate = 600;
Thread t = new(() =>
{
while (true)
{
- cpu.Clock();
- Thread.Sleep(1000 / frameRate);
+ for (int i = 0; i < 64; i++)
+ {
+ cpu.Clock();
+ }
+ Thread.Sleep(1);
+ //
+ // cpu.Clock();
+ // Thread.Sleep(1000);
}
});
t.IsBackground = true;
t.Start();
- using DisplayEngine game = new(64, 32, 10,
+ using DisplayEngine game = new(64, 32, 10,
tex =>
{
display.OutputTexture = tex;
diff --git a/ROMs/.DS_Store b/ROMs/.DS_Store
index 28ca9c8..523af33 100644
--- a/ROMs/.DS_Store
+++ b/ROMs/.DS_Store
Binary files differ
diff --git a/ROMs/font copy.bin b/ROMs/font copy.bin
new file mode 100644
index 0000000..88e4a93
--- /dev/null
+++ b/ROMs/font copy.bin
Binary files differ
diff --git a/ROMs/font.bin b/ROMs/font.bin
new file mode 100644
index 0000000..88e4a93
--- /dev/null
+++ b/ROMs/font.bin
Binary files differ
diff --git a/ROMs/hello_world.dat b/ROMs/hello_world.dat
new file mode 100644
index 0000000..6cdacff
--- /dev/null
+++ b/ROMs/hello_world.dat
@@ -0,0 +1,33 @@
+.org $1FFF
+JMP program
+
+font:
+ .include "font.bin"
+
+program:
+ MOV 13, V0 ; Load character #12
+ MOV 0, V1 ; X position
+ MOV 0, V2 ; Y position
+ MOV 1, V3 ; Color (1 = white)
+ CALL write_char ; Call function to write character
+ SUS
+
+write_char:
+ ; V0: Character to write
+ ; V1: X position
+ ; V2: Y position
+ ; V3: Color
+ ; Write a character to the screen at (V1, V2) with color V3
+
+ MUL V0, 64 ; V0 = Character * 64 (character size)
+ MOV AX, V0 ; Store result back into character
+
+ MUL V2, 64
+ ADD AX, V1
+ MOV AX, V4 ; V4 = X position + Y position * 64 (screen index)
+
+ MOV [V0 + $1FFF], V5 ; Read character
+ MUL V5, V3 ; V5 = Character * Color (color index)
+ MOV AX, V5 ; Store result into V5
+ MOV V5, [V4 + $3FFF] ; Write character to screen memory
+ RET \ No newline at end of file
diff --git a/ROMs/hello_world.q1 b/ROMs/hello_world.q1
new file mode 100644
index 0000000..4c1b1cd
--- /dev/null
+++ b/ROMs/hello_world.q1
Binary files differ
diff --git a/ROMs/test.q1 b/ROMs/manual.q1
index b166c7a..b166c7a 100644
--- a/ROMs/test.q1
+++ b/ROMs/manual.q1
Binary files differ
diff --git a/ROMs/script.dat b/ROMs/script.dat
deleted file mode 100644
index c2c5dc2..0000000
--- a/ROMs/script.dat
+++ /dev/null
@@ -1,24 +0,0 @@
-.org $1FFF
-
-program:
-MOV $0000, V0
-MUL 32, 64 ; Set V1 to 32 * 64
-MOV AX, V1 ; Copy V1 to AX
-MOV 1, V2 ; Color value 1
-
-reset:
-INC
-MOV $0000, V0 ;
-ADD V2, 1 ; Increment color value V2
-MOV AX, V2 ; Copy V2 to AX
-
-draw:
-ADD V0, 2 ; Increment V0 by 1
-MOV AX, V0 ; Copy AX to V0
-
-GT V0, V1 ; V0 > V1?
-BZ
-JMP reset
-
-MOV $06, [V0+$3FFF] ; Set pixel at V0 to color 6
-JMP draw ; Repeat drawing \ No newline at end of file
diff --git a/ROMs/script.q1 b/ROMs/script.q1
deleted file mode 100644
index 3de4840..0000000
--- a/ROMs/script.q1
+++ /dev/null
Binary files differ
diff --git a/ROMs/write_display_direct.dat b/ROMs/write_display_direct.dat
new file mode 100644
index 0000000..9fde417
--- /dev/null
+++ b/ROMs/write_display_direct.dat
@@ -0,0 +1,21 @@
+.org $1FFF
+
+program:
+MOV $0000, V0
+MUL 32, 64 ; Set AX to 32 * 64
+MOV AX, V1 ; Copy AX to V1
+MOV 1, V2 ; Color value 1
+
+reset:
+MOV 0, V0
+INC V2 ; Increment color value
+
+draw:
+GT V0, V1 ; V0 > V1?
+BZ ; If number exceeds, branch to reset
+JMP reset
+
+MOVB V2, [V0+$402F] ; Set pixel at V0 to color 6
+INC V0 ; Increment V0
+
+JMP draw ; Repeat drawing \ No newline at end of file
diff --git a/ROMs/write_display_direct.q1 b/ROMs/write_display_direct.q1
new file mode 100644
index 0000000..4041649
--- /dev/null
+++ b/ROMs/write_display_direct.q1
Binary files differ