diff --git a/.gitignore b/.gitignore index 5c8a8e9..42ad913 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ riderModule.iml YODA_Python/venv/ YODA_TypeScript/node_modules/ YODA_Python/__pycache__/ +.idea/ diff --git a/YODA/OpCodes.cs b/YODA/OpCodes.cs index dd6201d..7852fcc 100644 --- a/YODA/OpCodes.cs +++ b/YODA/OpCodes.cs @@ -10,11 +10,11 @@ public static class OpCode { // Misc (opcodes without operands) public const byte Halt = 0b0000_0000; // 00 - public const byte Wait = 0b0000_0001; // 00 - public const byte Ret = 0b0000_0011; // 02 - public const byte Nop = 0b0000_0100; // 03 - public const byte Sif = 0b0000_0101; // 04 - public const byte Cif = 0b0000_0110; // 05 + public const byte Wait = 0b0000_0001; // 01 + public const byte Ret = 0b0000_0010; // 02 + public const byte Nop = 0b0000_0011; // 03 + public const byte Sif = 0b0000_0100; // 04 + public const byte Cif = 0b0000_0101; // 05 public const byte SaveToFileMMM = 0b0001_0000; //16 public const byte SaveToFileMMI = 0b0001_0001; //17 diff --git a/YODA/VirtualMachine.cs b/YODA/VirtualMachine.cs index da11ce8..859cdbc 100644 --- a/YODA/VirtualMachine.cs +++ b/YODA/VirtualMachine.cs @@ -84,7 +84,9 @@ public async Task Run(string folderPath) Add(opCode); continue; case Mask.Sub: - throw new Exception("Due to lack of time this method has not been implemented"); + // throw new Exception("Due to lack of time this method has not been implemented"); + Sub(opCode); + continue; case Mask.Inc: Inc(opCode); continue; @@ -405,4 +407,18 @@ private byte Read(int location,int opCode, int mask) return _memory[reference]; } + + /// + /// Sub LHS RHS Total + /// + private void Sub(int opCode) + { + var lhs = Read(_instructionPointer + 1, opCode, 2); + var rhs = Read(_instructionPointer + 2, opCode, 1); + var location = Read(_instructionPointer + 3, opCode, 0); + + if (Debug) Console.WriteLine($"{_instructionPointer:x4} Sub:: {lhs} - {rhs} = {lhs - rhs} ==> {location:x2}"); + _memory[location] = (byte)(lhs - rhs); // Can overflow + _instructionPointer += 4; + } } \ No newline at end of file diff --git a/mark-csharp.sln b/mark-csharp.sln new file mode 100644 index 0000000..895695d --- /dev/null +++ b/mark-csharp.sln @@ -0,0 +1,56 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mark-csharp", "mark-csharp", "{5C3CA1F0-BEFB-913A-467C-9BC8E8763114}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YodaExercises", "mark-csharp\YodaExercises\YodaExercises.csproj", "{8D8C9463-C990-4962-996E-1EEE84C8441E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "YODA", "YODA", "{53427892-8E56-C6A7-C67E-0E35C96E3AED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleInstructionMachine", "YODA\SimpleInstructionMachine.csproj", "{7B6C7992-E188-49CD-AC66-6B959606752F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Debug|x64.Build.0 = Debug|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Debug|x86.Build.0 = Debug|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Release|Any CPU.Build.0 = Release|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Release|x64.ActiveCfg = Release|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Release|x64.Build.0 = Release|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Release|x86.ActiveCfg = Release|Any CPU + {8D8C9463-C990-4962-996E-1EEE84C8441E}.Release|x86.Build.0 = Release|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Debug|x64.ActiveCfg = Debug|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Debug|x64.Build.0 = Debug|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Debug|x86.ActiveCfg = Debug|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Debug|x86.Build.0 = Debug|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Release|Any CPU.Build.0 = Release|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Release|x64.ActiveCfg = Release|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Release|x64.Build.0 = Release|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Release|x86.ActiveCfg = Release|Any CPU + {7B6C7992-E188-49CD-AC66-6B959606752F}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8D8C9463-C990-4962-996E-1EEE84C8441E} = {5C3CA1F0-BEFB-913A-467C-9BC8E8763114} + {7B6C7992-E188-49CD-AC66-6B959606752F} = {53427892-8E56-C6A7-C67E-0E35C96E3AED} + EndGlobalSection +EndGlobal diff --git a/mark-csharp/Files/0 b/mark-csharp/Files/0 new file mode 100644 index 0000000..9acfbbf --- /dev/null +++ b/mark-csharp/Files/0 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mark-csharp/Files/1 b/mark-csharp/Files/1 new file mode 100644 index 0000000..fa7af8b --- /dev/null +++ b/mark-csharp/Files/1 @@ -0,0 +1 @@ +z \ No newline at end of file diff --git a/mark-csharp/Files/2 b/mark-csharp/Files/2 new file mode 100644 index 0000000..d8188da --- /dev/null +++ b/mark-csharp/Files/2 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mark-csharp/Files/9.txt b/mark-csharp/Files/9.txt new file mode 100644 index 0000000..4dd7de8 --- /dev/null +++ b/mark-csharp/Files/9.txt @@ -0,0 +1 @@ +5 4 3 2 1 LIFTOFF \ No newline at end of file diff --git a/mark-csharp/Files/ex1 b/mark-csharp/Files/ex1 new file mode 100644 index 0000000..65f57c2 Binary files /dev/null and b/mark-csharp/Files/ex1 differ diff --git a/mark-csharp/Files/ex2 b/mark-csharp/Files/ex2 new file mode 100644 index 0000000..551efc7 Binary files /dev/null and b/mark-csharp/Files/ex2 differ diff --git a/mark-csharp/Files/ex3 b/mark-csharp/Files/ex3 new file mode 100644 index 0000000..94cd65e Binary files /dev/null and b/mark-csharp/Files/ex3 differ diff --git a/mark-csharp/Files/ex4 b/mark-csharp/Files/ex4 new file mode 100644 index 0000000..084dfe4 Binary files /dev/null and b/mark-csharp/Files/ex4 differ diff --git a/mark-csharp/Files/ex5 b/mark-csharp/Files/ex5 new file mode 100644 index 0000000..0e6f02c Binary files /dev/null and b/mark-csharp/Files/ex5 differ diff --git a/mark-csharp/Files/ex6 b/mark-csharp/Files/ex6 new file mode 100644 index 0000000..255a05c Binary files /dev/null and b/mark-csharp/Files/ex6 differ diff --git a/mark-csharp/Files/ex8 b/mark-csharp/Files/ex8 new file mode 100644 index 0000000..5f534af Binary files /dev/null and b/mark-csharp/Files/ex8 differ diff --git a/mark-csharp/Files/refuelFile1 b/mark-csharp/Files/refuelFile1 new file mode 100644 index 0000000..f070424 Binary files /dev/null and b/mark-csharp/Files/refuelFile1 differ diff --git a/mark-csharp/YodaExercises/Program.cs b/mark-csharp/YodaExercises/Program.cs new file mode 100644 index 0000000..6522b47 --- /dev/null +++ b/mark-csharp/YodaExercises/Program.cs @@ -0,0 +1,290 @@ +using System.Reflection; +using System.Xml.Serialization; +using SimpleInstructionMachine; + +namespace YodaExercises; + +internal static class Program +{ + private static string _filePath = null!; + + internal static void Main(string[] args) + { + // Get individual path parts to the assembly and where the assembly is in the list + var pathParts = (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "") + .Split(Path.DirectorySeparatorChar) + .ToList(); + var assemblyIndex = pathParts.IndexOf(nameof(YodaExercises)); + // Reassemble the paths up to the point of the assembly name + var basePath = string.Join(Path.DirectorySeparatorChar, pathParts[..assemblyIndex]); + // Append Files to the path - this is the location of the boot file area + _filePath = Path.Combine(basePath, "Files"); + + // Make sure the Files folder exists + if (!Directory.Exists(_filePath)) + Directory.CreateDirectory(_filePath); + + // Exercise1(); + // Exercise2(); + // Exercise3(); + // Exercise4(); + // Exercise5(); + // Exercise6(); + // Exercise7(); + Exercise8(); + } + + private static void WriteFile(string name, byte[]? contents) + { + // programs can only occupy 256 bytes - any more will break the sim! + var fileLength = 1 + byte.MaxValue; + ArgumentOutOfRangeException.ThrowIfGreaterThan(contents?.Length ?? 0, fileLength, nameof(contents)); + + // Create the 256 byte buffer for the file + var buffer = new byte[fileLength]; + // Copy any contents we have been sent to the buffer + if (contents?.Length > 0) + contents.CopyTo(buffer, 0); + // Dump the buffer to the specified file, ensuring we only retain the name of the file and place it correctly + File.WriteAllBytes(Path.Combine(_filePath, Path.GetFileName(name)), buffer); + } + + private static void MakeFileBoot(string name) + { + var fileName = Path.Combine(_filePath, Path.GetFileName(name)); + if (!File.Exists(fileName)) + throw new FileNotFoundException($"File {fileName} not found."); + File.Copy(fileName, Path.Combine(_filePath, "boot"), true); + } + + private static void RunVm(string program) + { + MakeFileBoot(program); + + var vm = new VirtualMachine(true); + vm.Run(_filePath).Wait(); + } + + private static void Exercise1() + { + WriteFile("ex1", null); + RunVm("ex1"); + } + + private static void Exercise2() + { + WriteFile("ex2", [0xFF]); + RunVm("ex2"); + } + + private static void Exercise3() + { + // Two parts to this... the program itself and its data + // Program writes to file 0, from address 0x20 for 5 bytes + byte[] program = [OpCode.SaveToFileIII, 0, 0x20, 5]; + // Data to be stored at byte 0x20 + byte[] data = [5, 4, 3, 2, 1]; + + // Copy program and data to a temp buffer to pass to the VM Runner + var buffer = new byte[64]; + program.CopyTo(buffer, 0); + data.CopyTo(buffer, 0x20); + WriteFile("ex3", buffer); + RunVm("ex3"); + } + + private static void Exercise4() + { + byte[] message = "5 4 3 2 1 LIFTOFF".ToCharArray().Select(x => (byte)x).ToArray(); + byte[] program = [OpCode.SaveToFileIII, 9, 0x20, (byte)message.Length]; + + // Copy program and message to a temp buffer to pass to the VM Runner + var buffer = new byte[64]; + program.CopyTo(buffer, 0); + message.CopyTo(buffer, 0x20); + WriteFile("ex4", buffer); + RunVm("ex4"); + } + + private static void RefuelFile1(byte refuelAmount) + { + byte[] program = [OpCode.SaveToFileIII, 1, 0x20, 1]; + var buffer = new byte[64]; + program.CopyTo(buffer, 0); + buffer[0x20] = refuelAmount; + WriteFile("refuelFile1", buffer); + RunVm("refuelFile1"); + } + + private static void Exercise5() + { + RefuelFile1(123); + byte[] program = + [ + OpCode.LoadFromFileII, 1, 0x80, + OpCode.DecI, 0x80, + OpCode.SaveToFileIII, 1, 0x80, 1 + ]; + var buffer = new byte[64]; + program.CopyTo(buffer, 0); + WriteFile("ex5", buffer); + RunVm("ex5"); + } + + private static void Exercise6() + { + byte[] message = "54321".ToCharArray().Select(x => (byte)x).ToArray(); + byte[] program = + [ + OpCode.WriteIM, KnownMemory.LCD_0, 0x80, + OpCode.WriteII, KnownMemory.ControlFlags, 0x01, + OpCode.WriteII, KnownMemory.ControlFlags, 0x00, + OpCode.Wait, + OpCode.WriteIM, KnownMemory.LCD_0, 0x81, + OpCode.WriteII, KnownMemory.ControlFlags, 0x01, + OpCode.WriteII, KnownMemory.ControlFlags, 0x00, + OpCode.Wait, + OpCode.WriteIM, KnownMemory.LCD_0, 0x82, + OpCode.WriteII, KnownMemory.ControlFlags, 0x01, + OpCode.WriteII, KnownMemory.ControlFlags, 0x00, + OpCode.Wait, + OpCode.WriteIM, KnownMemory.LCD_0, 0x83, + OpCode.WriteII, KnownMemory.ControlFlags, 0x01, + OpCode.WriteII, KnownMemory.ControlFlags, 0x00, + OpCode.Wait, + OpCode.WriteIM, KnownMemory.LCD_0, 0x84, + OpCode.WriteII, KnownMemory.ControlFlags, 0x01, + OpCode.WriteII, KnownMemory.ControlFlags, 0x00, + OpCode.Wait, + ]; + + // Copy program and message to a temp buffer to pass to the VM Runner + var buffer = new byte[1 + byte.MaxValue]; + program.CopyTo(buffer, 0); + message.CopyTo(buffer, 0x80); + + WriteFile("ex6", buffer); + RunVm("ex6"); + } + + private static void Exercise7() + { + // Spaces that can be used to blank the screen area + byte[] spaces = " "u8.ToArray(); + byte leftVector = 0x40; + byte rightVector = 0x60; + // If the position has moved, then a value here will indicate the screen needs a refresh + byte movementCheck = 0x90; + byte currentPosition = 0x91; + byte blankScreen = 0x80; + + byte[] program = + [ + // 0x00 + // Write the spaces to file 8 so LCD segments can be overwritten simultaneously. + OpCode.SaveToFileIII, 8, blankScreen, (byte)spaces.Length, + // 0x04 + OpCode.Sif, + // 0x05 + OpCode.Wait, + // 0x06 + OpCode.JumpIfZeroII, movementCheck, 0x04, + // 0x09 - reset movement check + OpCode.WriteII, movementCheck, 0x00, + // 0x0C - write spaces into all LCD segments + OpCode.LoadFromFileII, 8, KnownMemory.LCD_0, + // 0x0F - write the dash to LCD at the correct position + OpCode.WriteMI, currentPosition, (byte)'-', + // 0x12 - refresh LCD + OpCode.WriteII, KnownMemory.ControlFlags, 0x01, + // 0x15 - reset LCD refresh + OpCode.WriteII, KnownMemory.ControlFlags, 0x00, + // 0x18 - loop from start of main loop + OpCode.JumpIfZeroII, movementCheck, 0x04, + // 0x1B + ]; + + // Interrupt handler for the left arrow keypress + byte[] leftInterrupt = + [ + // +0x00 + // Do not interrupt the interrupt + OpCode.Cif, + // +0x01 + // Check if current position is far-left, store result in movementCheck location + OpCode.SubMII, currentPosition, KnownMemory.LCD_0, movementCheck, + // +0x05 + // If movementCheck location is zero, do not perform any movement + OpCode.JumpIfZeroII, movementCheck, (byte)(leftVector + 0x0A), + // +0x08 + // Move one position left + OpCode.DecI, currentPosition, + // +0x0A + OpCode.Ret, + ]; + + // Interrupt handler for the right arrow keypress + byte[] rightInterrupt = + [ + // +0x00 + // Do not interrupt the interrupt + OpCode.Cif, + // +0x01 + OpCode.SubMII, currentPosition, KnownMemory.LCD_4, movementCheck, + // +0x05 + OpCode.JumpIfZeroII, movementCheck, (byte)(rightVector + 0x0A), + // +0x08 + // Move one position right + OpCode.IncI, currentPosition, + // +0x0A + OpCode.Ret, + ]; + + // Copy program and data to a temp buffer to pass to the VM Runner + var buffer = new byte[1 + byte.MaxValue]; + // Copy main program loop + program.CopyTo(buffer, 0); + // Copy left arrow interrupt routine + leftInterrupt.CopyTo(buffer, leftVector); + // Copy right arrow interrupt routine + rightInterrupt.CopyTo(buffer, rightVector); + + // Initial signal is we have "moved" into the central location + buffer[movementCheck] = 0x01; + // Set current position as central LCD segment + buffer[currentPosition] = KnownMemory.LCD_2; + // Write the spaces into a "high" memory area + spaces.CopyTo(buffer, blankScreen); + buffer[KnownMemory.IVT_LEFT_ARROW] = leftVector; + buffer[KnownMemory.IVT_RIGHT_ARROW] = rightVector; + + WriteFile("ex7", buffer); + RunVm("ex7"); + } + + private static void Exercise8() + { + // Load values to this location + byte loadArea = 0x80; + // Write result to this location + byte saveArea = 0x90; + // Define the file number to read values from + byte valuesFile = 2; + // Define the file number to write the result to + byte resultFile = 3; + byte[] program = + [ + // Load from file + OpCode.LoadFromFileII, valuesFile, loadArea, + // Subtract, store result in area to be saved + OpCode.SubMMI, loadArea, (byte)(loadArea + 1), saveArea, + // Save the result to the file + OpCode.SaveToFileIII, resultFile, saveArea, 1 + ]; + + byte[] buffer = new byte[1 + program.Length]; + program.CopyTo(buffer, 0); + WriteFile("ex8", buffer); + RunVm("ex8"); + } +} \ No newline at end of file diff --git a/mark-csharp/YodaExercises/YodaExercises.csproj b/mark-csharp/YodaExercises/YodaExercises.csproj new file mode 100644 index 0000000..0b036e3 --- /dev/null +++ b/mark-csharp/YodaExercises/YodaExercises.csproj @@ -0,0 +1,15 @@ + + + + + + + + Exe + net9.0 + YodaExercises + enable + enable + + +