diff options
| author | Melodi <62381002+Melodi17@users.noreply.github.com> | 2025-08-05 21:24:16 +1000 |
|---|---|---|
| committer | Melodi <62381002+Melodi17@users.noreply.github.com> | 2025-08-05 21:24:16 +1000 |
| commit | 16f3cb74564cf7c19da6e13666c98aca36094716 (patch) | |
| tree | 7346a7c12a82bc372ed82dad8e9a5d9ad6b098b7 | |
| parent | 72f297a479b6749674ec80e91be53b8c482123d2 (diff) | |
updates
| -rw-r--r-- | .DS_Store | bin | 8196 -> 8196 bytes | |||
| -rw-r--r-- | Assembler/Assembler.cs | 297 | ||||
| -rw-r--r-- | Assembler/Disassembler.cs | 67 | ||||
| -rw-r--r-- | Chip/Bus.cs | 18 | ||||
| -rw-r--r-- | Chip/BusDevice.cs | 18 | ||||
| -rw-r--r-- | Chip/DisplayDevice.cs | 21 | ||||
| -rw-r--r-- | Chip/Q1Cpu.cs | 9 | ||||
| -rw-r--r-- | Chip/Q1CpuAddressingModes.cs | 238 | ||||
| -rw-r--r-- | Chip/Q1CpuInstructions.cs | 133 | ||||
| -rw-r--r-- | Chip/RamDevice.cs | 13 | ||||
| -rw-r--r-- | DisplayEngine.cs | 2 | ||||
| -rw-r--r-- | Extensions.cs | 23 | ||||
| -rw-r--r-- | Make.cs | 5 | ||||
| -rw-r--r-- | Program.cs | 44 | ||||
| -rw-r--r-- | ROMs/.DS_Store | bin | 6148 -> 6148 bytes | |||
| -rw-r--r-- | ROMs/font copy.bin | bin | 0 -> 32768 bytes | |||
| -rw-r--r-- | ROMs/font.bin | bin | 0 -> 32768 bytes | |||
| -rw-r--r-- | ROMs/hello_world.dat | 33 | ||||
| -rw-r--r-- | ROMs/hello_world.q1 | bin | 0 -> 32842 bytes | |||
| -rw-r--r-- | ROMs/manual.q1 (renamed from ROMs/test.q1) | bin | 10 -> 10 bytes | |||
| -rw-r--r-- | ROMs/script.dat | 24 | ||||
| -rw-r--r-- | ROMs/script.q1 | bin | 67 -> 0 bytes | |||
| -rw-r--r-- | ROMs/write_display_direct.dat | 21 | ||||
| -rw-r--r-- | ROMs/write_display_direct.q1 | bin | 0 -> 51 bytes |
24 files changed, 567 insertions, 399 deletions
| 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 @@ -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 @@ -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 Binary files differindex 28ca9c8..523af33 100644 --- a/ROMs/.DS_Store +++ b/ROMs/.DS_Store diff --git a/ROMs/font copy.bin b/ROMs/font copy.bin Binary files differnew file mode 100644 index 0000000..88e4a93 --- /dev/null +++ b/ROMs/font copy.bin diff --git a/ROMs/font.bin b/ROMs/font.bin Binary files differnew file mode 100644 index 0000000..88e4a93 --- /dev/null +++ b/ROMs/font.bin 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 Binary files differnew file mode 100644 index 0000000..4c1b1cd --- /dev/null +++ b/ROMs/hello_world.q1 diff --git a/ROMs/test.q1 b/ROMs/manual.q1 Binary files differindex b166c7a..b166c7a 100644 --- a/ROMs/test.q1 +++ b/ROMs/manual.q1 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 Binary files differdeleted file mode 100644 index 3de4840..0000000 --- a/ROMs/script.q1 +++ /dev/null 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 Binary files differnew file mode 100644 index 0000000..4041649 --- /dev/null +++ b/ROMs/write_display_direct.q1 |
