diff --git a/Program.cs b/Program.cs index 32123b4..8d2e883 100644 --- a/Program.cs +++ b/Program.cs @@ -13,7 +13,7 @@ static void Main(string[] args) string defaultS8File = @"s8.s8"; - S8Dissasembler s8d = new S8Dissasembler(); + S8Dissasembler s8d = new S8Dissasembler(); s8d.Init(defaultS8File); @@ -40,7 +40,7 @@ static void Main(string[] args) { Console.Write("s8 ["); } - Console.Write(currentAddress.ToString("X4") + "] "); + Console.Write(currentAddress.ToString("X3") + "] "); string input = Console.ReadLine(); try @@ -87,7 +87,7 @@ static void Main(string[] args) { Console.WriteLine("Assembly FAILED!!"); } - else + else { s8d = new S8Dissasembler(); s8d.InitFromMemory(s8prog); @@ -140,7 +140,7 @@ static void Main(string[] args) } } break; - + case "INPUT": case "FØDE": if (cmd.Length > 1) @@ -157,6 +157,19 @@ static void Main(string[] args) currentAddress = s8d.SetPC(start); break; + case "PC!": // set PC + currentAddress = s8d.SetPC(start, true); + break; + + + case "UNITTEST": + if (cmd.Length > 1) + { + S8UnitTest s8unit = new S8UnitTest(); + currentAddress = s8unit.RunUnitTest(s8d, cmd[1]); + } + break; + case "R": case "RUN": currentAddress = s8d.Run(); @@ -176,7 +189,7 @@ static void Main(string[] args) case "S": case "STEP": if (start > 0) - { + { currentAddress = s8d.Step(start); } else @@ -191,7 +204,7 @@ static void Main(string[] args) { s8d.SetMaxTicks(start); } - + Console.WriteLine("MaxTicks is set to " + s8d.GetMaxTicks().ToString()); break; case ":": @@ -203,9 +216,16 @@ static void Main(string[] args) case "D": currentAddress = s8d.Dissasemble(start, length, showAddress); break; + case "D!": + currentAddress = s8d.Dissasemble(start, length, showAddress, true); + break; case "M": currentAddress = s8d.MemoryDump(start, length, showAddress); break; + case "M!": + currentAddress = s8d.MemoryDump(start, length, showAddress, true); + break; + case "H": case "HELP": case "?": @@ -266,7 +286,7 @@ private static byte[] Asm(string sledeFile) Console.WriteLine("Can't find SLEDE8 file " + sledeFile); } S8Assembler s8 = new S8Assembler(); - + return s8.AssembleFile(sledeFile); } @@ -294,18 +314,25 @@ static void hard() static void PrintHelp() { Console.WriteLine("D - Dissassemble [start] [length]"); - Console.WriteLine("M - Memory Dump [start] [length]"); + Console.WriteLine("M - Memory Dump [start] [length]"); + Console.WriteLine("Limits itself to inside loaded image"); + Console.WriteLine(); + + Console.WriteLine("D!- Dissassemble [start] [length]"); + Console.WriteLine("M!- Memory Dump [start] [length]"); + Console.WriteLine("Enables access to memory ourside loaded image"); ; Console.WriteLine(); Console.WriteLine(""); - Console.WriteLine("FØDE - SET INPUT hexhexhex"); - Console.WriteLine("PC - SET pc = 0xNNNN"); + Console.WriteLine("INPUT - SET INPUT hexhexhex"); + Console.WriteLine("PC - SET pc = xxx"); Console.WriteLine("RUN - Run program from 0"); Console.WriteLine("REGS - Dump registers"); Console.WriteLine("RESET - Reset registers"); Console.WriteLine("STEP - Step PC [steps]"); Console.WriteLine("TICKS - Set Max Ticks 0xNN"); + Console.WriteLine("UNITTEST [filename] - Run unit tests agains [filename]"); Console.WriteLine("! = Change showaddress flag"); Console.WriteLine(""); diff --git a/ReadMe-release.txt b/ReadMe-release.txt new file mode 100644 index 0000000..78f4016 --- /dev/null +++ b/ReadMe-release.txt @@ -0,0 +1 @@ +dotnet publish -r win-x64 -c release -p:PublishSingleFile=true --self-contained true \ No newline at end of file diff --git a/S8CPU.cs b/S8CPU.cs index b655504..dbb7de4 100644 --- a/S8CPU.cs +++ b/S8CPU.cs @@ -82,7 +82,7 @@ public S8CPU() public void SetMaxTicks(int Ticks) { state.maxTicks = Ticks; - ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.resourcesExhausted] = ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.resourcesExhausted].Replace("${maxTicks}", Ticks.ToString()); + } //const memory = load(executable); @@ -91,8 +91,13 @@ public void SetMaxTicks(int Ticks) public byte[] Load(byte[] executable, bool skipMagicHeader) { + int oldMaxTicks = DEFAULT_MAX_STEPS; + if (state is not null) + { + oldMaxTicks = state.maxTicks; + } state = new CpuState(); - SetMaxTicks(DEFAULT_MAX_STEPS); + SetMaxTicks(oldMaxTicks); ResetRegs(); @@ -190,7 +195,8 @@ internal bool RunUntil(int stop_pc_at) { if (++state.tick > state.maxTicks) { - Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.resourcesExhausted]); + var strErr = ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.resourcesExhausted].Replace("${maxTicks}", state.tick.ToString()); + Console.WriteLine(strErr); return false; } diff --git a/S8Dissasembler.cs b/S8Dissasembler.cs index 4a98bb1..c0bd521 100644 --- a/S8Dissasembler.cs +++ b/S8Dissasembler.cs @@ -12,8 +12,8 @@ namespace S8Debugger { public class S8Dissasembler - { - byte[] bytes = null; + { + byte[] bytes = new byte[4096]; S8CPU cpu = new S8CPU(); public bool Init(string fname, bool force = false) @@ -39,8 +39,8 @@ internal bool InitFromMemory(byte[] s8prog) return true; } - public int MemoryDump(int start, int length, bool showAddress = false) - { + public int MemoryDump(int start, int length, bool showAddress = false, bool allowOutsideLoadedMemory = false) + { int currentAddress = start; int endAddress = currentAddress + length; @@ -51,25 +51,30 @@ public int MemoryDump(int start, int length, bool showAddress = false) //endAddress = cpu.state.memoryUsed; } - if (endAddress > cpu.state.memoryUsed) + if (!allowOutsideLoadedMemory) { - endAddress = cpu.state.memoryUsed; + if (endAddress > cpu.state.memoryUsed) + { + endAddress = cpu.state.memoryUsed; + } } - int lineCounter = 0; string line1 = ""; string line2 = ""; + while (currentAddress < endAddress) { - + if (lineCounter == 0) { + string sHexAddress = currentAddress.ToString("X3"); + Console.WriteLine("m" + sHexAddress + ":"); line1 = ".DATA "; - line2 = ";" + currentAddress.ToString("X4") + ": "; + line2 = ";" + currentAddress.ToString("X3") + ": "; } else { @@ -104,7 +109,7 @@ public int MemoryDump(int start, int length, bool showAddress = false) lineCounter = 0; Console.WriteLine(); - } + } } Console.WriteLine(); @@ -112,14 +117,14 @@ public int MemoryDump(int start, int length, bool showAddress = false) return currentAddress; } - + internal void Reset() { cpu.ResetRegs(); } - public int Dissasemble(int start, int length, bool showAddress = false) + public int Dissasemble(int start, int length, bool showAddress = false, bool allowOutsideLoadedMemory = false) { S8Instruction s8i; @@ -132,29 +137,45 @@ public int Dissasemble(int start, int length, bool showAddress = false) endAddress = currentAddress + 8; //endAddress = cpu.state.memoryUsed; } - - if (endAddress > cpu.state.memoryUsed) + if (!allowOutsideLoadedMemory) { - endAddress = cpu.state.memoryUsed; + if (endAddress > cpu.state.memoryUsed) + { + endAddress = cpu.state.memoryUsed; + } } while (currentAddress < endAddress) { - string sHexAddress = currentAddress.ToString("X4"); + string sHexAddress = currentAddress.ToString("X3"); byte opcode = bytes[currentAddress++]; byte param = bytes[currentAddress++]; - + s8i = new S8Instruction(opcode, param); s8i.DecodeInstruction(); + if (s8i.ValidInstruction) + { + if (!showAddress) + Console.WriteLine("a" + sHexAddress + ":"); + } + else + { + if (!showAddress) + Console.WriteLine("m" + sHexAddress + ":"); + } + if (showAddress) { string sOpcode = opcode.ToString("X2"); string sParam = param.ToString("X2"); - Console.Write("A["+sHexAddress + "] | I["+sOpcode + " " + sParam + "] "); + Console.Write("A[" + sHexAddress + "] | I[" + sOpcode + " " + sParam + "] "); } - + + + + if (s8i.ValidInstruction) { Console.WriteLine(s8i.DecodedInstruction); @@ -169,28 +190,47 @@ public int Dissasemble(int start, int length, bool showAddress = false) } - internal int SetPC(int start) + internal int SetPC(int start, bool allowOutsideLoadedMemory = false) { - if (start > cpu.state.memoryUsed) - { + if (start > 0xFFF) start = 0; + + if (!allowOutsideLoadedMemory) + { + if (start > cpu.state.memoryUsed) + { + start = 0; + } } cpu.state.pc = start; return cpu.state.pc; } + internal void SetInput(byte[] inputBuffer) + { + cpu.state.stdin = inputBuffer; + } + internal void SetInput(string v) { string s = ConvertHex2Asii(v); cpu.state.stdin = new byte[s.Length]; - for (int i=0;i> 8; int regs0 = address & 0xff; - DecodedInstruction = "FINN ; r0 = " + regs0.ToString("X2") + " r1 = " + regs1.ToString("X2"); + DecodedInstruction = "FINN m" + address.ToString("X3"); //+ " ; r0 = " + regs0.ToString("X2") + " r1 = " + regs1.ToString("X2"); ValidInstruction = true; break; @@ -192,25 +192,20 @@ public void DecodeInstruction() } DecodedInstruction = cmpFunction + "r" + argument1 + ", r" + argument2; break; - - case 0x8: - //int address08 = (param << 4) + operation; - //DecodedInstruction = "HOPP " + jumpAddress.ToString("X4"); // address - DecodedInstruction = "HOPP " + address.ToString("X4"); // address + + case 0x8: + DecodedInstruction = "HOPP a" + address.ToString("X3"); // address ValidInstruction = true; break; - case 0x9: - //jumpAddress = (param << 4) + operation; - //DecodedInstruction = "BHOPP " + jumpAddress.ToString("X4"); - DecodedInstruction = "BHOPP " + address.ToString("X4"); + case 0x9: + DecodedInstruction = "BHOPP a" + address.ToString("X3"); ValidInstruction = true; break; - case 0x0A: - //DecodedInstruction = "TUR " + operationClass + " 0x" + sParam; - DecodedInstruction = "TUR " + " 0x" + address.ToString("X4"); + case 0x0A: + DecodedInstruction = "TUR a" + " 0x" + address.ToString("X3"); ValidInstruction = true; break; case 0x0B: diff --git a/S8UnitTest.cs b/S8UnitTest.cs new file mode 100644 index 0000000..7cdae25 --- /dev/null +++ b/S8UnitTest.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace S8Debugger +{ + public class S8UnitTest + { + /// + /// Run one or more unit tests + /// + /// Existing S8Dissasembler object + /// The name of the file containing the unit tests + /// + public int RunUnitTest(S8Dissasembler s8d,string unitTestFile) + { + byte[] bInput; + int lineCounter = 0; + int errCnt = 0; + int currentaddress = 0; + + if (s8d is null) return 0; + + if (!File.Exists(unitTestFile)) + { + Console.WriteLine("Unknown file " + unitTestFile); + return 0; + } + + var allLines = File.ReadAllLines(unitTestFile); + + Console.WriteLine("Read unit test file " + allLines.Length + " lines."); + + foreach (string currentLine in allLines) + { + var actualLine = currentLine.Trim(); + lineCounter++; + + if (actualLine.Length == 0) + continue; + + if (actualLine[0] == '#') + { + Console.WriteLine(actualLine); + continue; + } + + if (actualLine[0] == '!') + { + // COmmands + var cmds = actualLine.Split(" "); + if (cmds.Length < 2) + continue; + + var command = cmds[1].ToUpper().Trim(); + var param = cmds[2].ToUpper().Trim(); + switch (command) + { + case "TICKS": + case "MAXTICKS": + int newTicks = 0; + if (int.TryParse(param, out newTicks)) + { + s8d.SetMaxTicks(newTicks); + Console.WriteLine("[" + lineCounter.ToString() + "] MAXTICKS " + newTicks); + } + + break; + case "LOAD": + Console.WriteLine("[" + lineCounter.ToString() + "] LOAD FILE " + param); + s8d.Init(param); + break; + default: + Console.WriteLine("[" + lineCounter.ToString() + "] Unknown command " + command); + break; + } + + continue; + } + + var input = actualLine.Split(";"); + if (input.Length < 2) + { + Console.WriteLine("[" + lineCounter.ToString() + "] Invalid line format"); + continue; + } + input[0] = input[0].Trim(); + input[1] = input[1].Trim().ToUpper(); + + int inputLen = input[0].Length; + int hexInputLen = inputLen / 2; + + if (hexInputLen > 1000) + { + Console.WriteLine("FAILED: Input length > 1000"); + return 0; + } + Console.WriteLine("[" + lineCounter.ToString() + "] Run started"); + + bInput = new byte[hexInputLen]; + + for (int i = 0; i < hexInputLen; i++) + { + var inStr = input[0].Substring((i * 2), 2); + bInput[i] = byte.Parse(inStr, System.Globalization.NumberStyles.HexNumber); + + } + + s8d.SetInput(bInput); + + currentaddress = s8d.Run(false); + string stdout = s8d.GetOutput(); + + if (input[1].Equals(stdout)) + { + Console.WriteLine("[" + lineCounter.ToString() + "] Run successfull"); + } + else + { + errCnt++; + Console.WriteLine("[" + lineCounter.ToString() + "] FAILED! Output differs"); + Console.WriteLine("[" + lineCounter.ToString() + "] OUTPUT = " + stdout); + Console.WriteLine("[" + lineCounter.ToString() + "] EXPECTED = " + input[1]); + //return 0; + } + } + + if (errCnt > 0) + { + Console.WriteLine("Unit test failed with " + errCnt + " errors!"); + } + return currentaddress; + } + } +} diff --git a/UnitTest/Test1.txt b/UnitTest/Test1.txt new file mode 100644 index 0000000..cd9e39b --- /dev/null +++ b/UnitTest/Test1.txt @@ -0,0 +1,22 @@ +# Script for Unit Testing +# Supports a few commands like "! TICKS" and "!LOAD " +! TICKS 1000000 +! LOAD bubble-sort.s8 + + +# Unit test format is +# INPUT;EXPECTED-OUTPUT +# + +00; +01;AA +020A02;020A +030A0102;01020A +FF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF;000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe + +! LOAD quick-sort.s8 +00; +01;AA +020A02;020A +030A0102;01020A +FF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF;000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe