From acdc13da853177ce80158f968a71d2575e127d97 Mon Sep 17 00:00:00 2001 From: Ronny Hansen Date: Mon, 14 Dec 2020 20:35:50 +0100 Subject: [PATCH] First commit --- .gitignore | 269 +++++++++++++++++++++++++++++++++ Program.cs | 315 ++++++++++++++++++++++++++++++++++++++ README.md | 3 + S8/hilsen.s8 | Bin 0 -> 83 bytes S8/hilsen.slede8 | 46 ++++++ S8Assembler.cs | 28 ++++ S8CPU.cs | 375 ++++++++++++++++++++++++++++++++++++++++++++++ S8Debugger.csproj | 10 ++ S8Debugger.sln | 25 ++++ S8Dissasembler.cs | 298 ++++++++++++++++++++++++++++++++++++ S8Instruction.cs | 255 +++++++++++++++++++++++++++++++ 11 files changed, 1624 insertions(+) create mode 100644 .gitignore create mode 100644 Program.cs create mode 100644 S8/hilsen.s8 create mode 100644 S8/hilsen.slede8 create mode 100644 S8Assembler.cs create mode 100644 S8CPU.cs create mode 100644 S8Debugger.csproj create mode 100644 S8Debugger.sln create mode 100644 S8Dissasembler.cs create mode 100644 S8Instruction.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c467940 --- /dev/null +++ b/.gitignore @@ -0,0 +1,269 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/ + diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..32123b4 --- /dev/null +++ b/Program.cs @@ -0,0 +1,315 @@ +using System; +using System.Globalization; +using System.IO; +using System.Runtime.ExceptionServices; +using System.Security.Cryptography; + +namespace S8Debugger +{ + class Program + { + static void Main(string[] args) + { + + string defaultS8File = @"s8.s8"; + + S8Dissasembler s8d = new S8Dissasembler(); + + s8d.Init(defaultS8File); + + + bool showAddress = true; + int currentAddress = 0; + + + Console.WriteLine("Velkommen til Slede8 debugger"); + Console.WriteLine("H => HELP"); + + bool debugging = true; + + while (debugging) + { + int start = 0; + int length = 0; + + if (showAddress) + { + Console.Write("S8#["); + } + else + { + Console.Write("s8 ["); + } + Console.Write(currentAddress.ToString("X4") + "] "); + string input = Console.ReadLine(); + + try + { + var cmd = input.Split(" "); + if (cmd.Length > 1) + { + start = parseValue(cmd[1]); + } + else + { + start = currentAddress; + } + + if (cmd.Length > 2) + { + length = parseValue(cmd[2]); + } + + switch (cmd[0].ToUpper()) + { + + case "ASM!": //Assemble file (for validation only) + if (cmd.Length > 1) + { + var s8prog = Asm(cmd[1]); + if (s8prog is null) + { + Console.WriteLine("Assembly FAILED!!"); + } + else + { + Console.WriteLine("Assembled file OK, size = " + s8prog.Length); + } + ; + } + break; + + case "ASM": //Assemble and LOAD file to memory + if (cmd.Length > 1) + { + var s8prog = Asm(cmd[1]); + if (s8prog is null) + { + Console.WriteLine("Assembly FAILED!!"); + } + else + { + s8d = new S8Dissasembler(); + s8d.InitFromMemory(s8prog); + + + if (cmd.Length > 2) + { + SaveToS8File(s8prog, cmd[2]); + Console.WriteLine("Saved assembled S8 to " + cmd[2]); + } + }; + } + break; + + + case "LOAD": + if (cmd.Length > 1) + { + if (File.Exists(cmd[1])) + { + s8d = new S8Dissasembler(); + if (!s8d.Init(cmd[1])) + { + Console.WriteLine("Failed to load image"); + } + currentAddress = 0; + } + else + { + Console.WriteLine("Unknown S8 file " + cmd[1]); + } + + } + break; + case "FORCELOAD": + if (cmd.Length > 1) + { + if (File.Exists(cmd[1])) + { + s8d = new S8Dissasembler(); + if (!s8d.Init(cmd[1], true)) + { + Console.WriteLine("Failed to load image"); + } + currentAddress = 0; + } + else + { + Console.WriteLine("Unknown file " + cmd[1]); + } + } + break; + + case "INPUT": + case "FØDE": + if (cmd.Length > 1) + { + s8d.SetInput(cmd[1]); + } + else + { + Console.WriteLine("Missing input string"); + } + break; + + case "PC": // set PC + currentAddress = s8d.SetPC(start); + break; + + case "R": + case "RUN": + currentAddress = s8d.Run(); + break; + + case "REGS": + s8d.Regs(); + break; + + case "RESET": + s8d.Reset(); + currentAddress = 0; + break; + + + case "+": + case "S": + case "STEP": + if (start > 0) + { + currentAddress = s8d.Step(start); + } + else + { + currentAddress = s8d.Step(1); + } + + break; + case "SETMAXTICKS": + case "TICKS": + if (start > 0) + { + s8d.SetMaxTicks(start); + } + + Console.WriteLine("MaxTicks is set to " + s8d.GetMaxTicks().ToString()); + break; + case ":": + hard(); + break; + case "": + currentAddress = s8d.Dissasemble(currentAddress, 2, showAddress); + break; + case "D": + currentAddress = s8d.Dissasemble(start, length, showAddress); + break; + case "M": + currentAddress = s8d.MemoryDump(start, length, showAddress); + break; + case "H": + case "HELP": + case "?": + PrintHelp(); + break; + case "Q": + case "QUIT": + case "DIE": + debugging = false; + break; + case "!": + showAddress = !showAddress; + if (showAddress) + { + Console.WriteLine("Show memory address enabled"); + } + else + { + Console.WriteLine("Show memory address disabled"); + } + break; + default: + if (input.Length > 0) + { + Console.WriteLine("Unknown command [" + input + "]. Type H for help"); + } + break; + } + } + catch (Exception) + { + + Console.WriteLine("Den skled unna!"); + } + + } + + + + } + + private static void SaveToS8File(byte[] s8prog, string s8outputfile) + { + var MAGIC = new byte[] { 0x2E, 0x53, 0x4C, 0x45, 0x44, 0x45, 0x38 }; + var stream = File.Open(s8outputfile, FileMode.Create); + + stream.Write(MAGIC, 0, MAGIC.Length); + stream.Write(s8prog, 0, s8prog.Length); + stream.Close(); + + } + + private static byte[] Asm(string sledeFile) + { + + if (!File.Exists(sledeFile)) + { + Console.WriteLine("Can't find SLEDE8 file " + sledeFile); + } + S8Assembler s8 = new S8Assembler(); + + return s8.AssembleFile(sledeFile); + } + + static int parseValue(string v) + { + try + { + return int.Parse(v, System.Globalization.NumberStyles.HexNumber); + } + catch (Exception) + { + return 0; + } + } + + static void hard() + { + // implement hard coded test function + + } + + + + + static void PrintHelp() + { + Console.WriteLine("D - Dissassemble [start] [length]"); + Console.WriteLine("M - Memory Dump [start] [length]"); + Console.WriteLine(); + + Console.WriteLine(""); + + Console.WriteLine("FØDE - SET INPUT hexhexhex"); + Console.WriteLine("PC - SET pc = 0xNNNN"); + 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("! = Change showaddress flag"); + Console.WriteLine(""); + + Console.WriteLine("Q = Quit"); + } + } +} diff --git a/README.md b/README.md index ba45c8d..15c08de 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # S8Debugger Debugger for SLEDE8 + + +Se https://github.com/pstnorge/slede8 for spesifikasjon \ No newline at end of file diff --git a/S8/hilsen.s8 b/S8/hilsen.s8 new file mode 100644 index 0000000000000000000000000000000000000000..3b39dfc543dd30280f51f71724121f51c86970e2 GIT binary patch literal 83 zcmdN3_HlJ_wOGiok&%Z10lApDSh$!O7(6m_ic|9x(uxulLK2HhQqwZ?(o>7L7@8Sb bnAjJ+WENuz-JrqD4dxpvgc^x3aWen_zsC^n literal 0 HcmV?d00001 diff --git a/S8/hilsen.slede8 b/S8/hilsen.slede8 new file mode 100644 index 0000000..c4d289f --- /dev/null +++ b/S8/hilsen.slede8 @@ -0,0 +1,46 @@ +SETT r10, 0 +SETT r11, 1 + +NOPE +NOPE +NOPE ; på neste linje er det et punkt til ettertanke! +NOPE ;! +NOPE +NOPE + +TUR skriv_hilsen ; kaller 'funksjonen' skriv_hilsen +TUR endre_første_bokstav_til_små_versaler +TUR skriv_hilsen +STOPP + +en_liten_hilsen: +.DATA 72,105,108,115,101,110,32,102,114,97,32,84,97,115,116,101,102,105,110,103,101,114,10,0 + + +skriv_hilsen: +FINN en_liten_hilsen ; skriv addressen til labelen 'en_liten_hilsen' til r0 og r1 + +skriv_neste_verdi: +LAST r2 ; last verdien som blir pekt på inn i r2 +LIK r2, r10 ; hvis verdien er lik 0 avslutter vi +BHOPP skriv_hilsen_fullført +SKRIV r2 +PLUSS r0, r11 ; legg 1 til r0, slik at vi nå peker på neste verdi i dataen + ; OBS! hvis vi gjør dette og r0 går fra 0xFF->0x00 må vi plusse på 1 + ; i registeret r1 (0xFF + 0x01 = 0x100). Det håndteres ikke her +HOPP skriv_neste_verdi + +skriv_hilsen_fullført: +RETUR + + + +endre_første_bokstav_til_små_versaler: +FINN en_liten_hilsen +LAST r2 ; r2 = 72 ('H') +SETT r3, 0x20 ; +PLUSS r2, r3 ; r2 = 'h' +LAGR r2 ; skriv verdien i r2 tilbake til adressen som r0 og r1 peker på +RETUR + + diff --git a/S8Assembler.cs b/S8Assembler.cs new file mode 100644 index 0000000..3e43850 --- /dev/null +++ b/S8Assembler.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace S8Debugger +{ + // https://github.com/PSTNorge/slede8/blob/main/src/assembler.ts + public class S8Assembler + { + /// + /// Assemble a .slede file to .s8 file + /// + /// + /// Compiled memory + public byte[] AssembleFile(string sledeFile) + { + + /// TODO implement real assembler :-D + /// + byte[] s8 = new byte[2]; + s8[0] = 0; + s8[1] = 0; + return s8; + } + } +} diff --git a/S8CPU.cs b/S8CPU.cs new file mode 100644 index 0000000..b655504 --- /dev/null +++ b/S8CPU.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.Intrinsics.X86; +using System.Text; +using System.Threading.Tasks; + +namespace S8Debugger +{ + public class CpuStack + { + int RECURSION_LIMIT = 100; + + + Stack stack = new Stack(); + + public bool Push(int pc) + { + if (stack.Count > RECURSION_LIMIT) return false; + stack.Push(pc); + + return true; + } + public int Pop() + { + if (stack.Count == 0) return -1; + return stack.Pop(); + } + + } + + public class CpuState + { + public int inputPtr = 0; + public int tick = 0; + public int pc = 0; + public bool flag = false; + public byte[] regs = new byte[16]; + public byte[] memory = null; + public int memoryUsed = 0; + + + public bool crashed; + + public byte[] stdin = new byte[1]; + public string stdout = ""; + public int maxTicks; + }; + + public class S8CPU + { + int DEFAULT_MAX_STEPS = 50000; //Default allow 50.000 ticks + + public CpuState state; + public CpuStack stack; + + public enum ERROR_MESSAGE_ID { segmentationFault, recursionLimitExceeded, fileSizeTooBig, readAfterEndOfInput, unsupportedExecutable, resourcesExhausted }; + + public static string[] ERROR_MESSAGE = + { + "Segmenteringsfeil", + "Alt for mange funksjonskall inni hverandre", + "Programmet får ikke plass i minnet", + "Programmet gikk tom for føde", + "Dette skjønner jeg ingenting av", + "Programmet ble brutalt drept etter å ha benyttet hele ${maxTicks} sykluser" + }; + + /// + /// Ctor + /// + public S8CPU() + { + state = new CpuState(); + stack = new CpuStack(); + + SetMaxTicks(DEFAULT_MAX_STEPS); + } + + + 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); + //let stdout = new Uint8Array(); + //const backtrace: number[] = []; + + public byte[] Load(byte[] executable, bool skipMagicHeader) + { + state = new CpuState(); + SetMaxTicks(DEFAULT_MAX_STEPS); + + ResetRegs(); + + if (executable.Length > 4096) + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.fileSizeTooBig]); + return null; + } + + if (!skipMagicHeader) + { + if (!validateMagic(executable)) + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.unsupportedExecutable]); + return null; + } + } + + state.memory = new byte[4096]; + int seek = 7; + if (skipMagicHeader) + { + seek = 0; + } + + int i = 0; + while (seek < executable.Length) + { + state.memory[i++] = executable[seek++]; + } + state.memoryUsed = i; + + return state.memory; + } + + private bool validateMagic(byte[] bytes) + { + + var MAGIC = new byte[] { 0x2E, 0x53, 0x4C, 0x45, 0x44, 0x45, 0x38 }; + if (bytes.Length <= MAGIC.Length) + { + return false; + } + + for (int i = 0; i < MAGIC.Length; i++) + { + if (bytes[i] != MAGIC[i]) + { + return false; + } + } + return true; + } + + + /// + /// Reset CPU counter and registers + /// + public void ResetRegs() + { + state.inputPtr = 0; + state.tick = 0; + + state.pc = 0; + state.flag = false; + state.crashed = false; + + for (int i = 0; i < 16; i++) + { + state.regs[i] = 0; + } + + + state.stdout = ""; + } + + public void Run() + { + ResetRegs(); + RunUntil(state.memoryUsed); + } + + internal bool RunUntil(int stop_pc_at) + { + + if (state.memoryUsed == 0) + { + Console.WriteLine("No s8 program loaded"); + return false; + } + + if (state.memoryUsed == 0) return false; + + while ((state.pc < stop_pc_at) && (!state.crashed)) + { + if (++state.tick > state.maxTicks) + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.resourcesExhausted]); + return false; + } + + + byte opcode = state.memory[state.pc]; + byte param = state.memory[state.pc + 1]; + //yield { pc, flag, regs, memory, stdout, inputPtr }; + + S8Instruction s8 = new S8Instruction(opcode, param); + state.pc += 2; + + if (!ExecuteInstruction(s8)) + { + state.crashed = true; + } + } + + if (state.crashed) return false; + return true; + } + + public bool Step(int steps=1) + { + state.crashed = false; + + return RunUntil(state.pc+ (steps*2)); + } + + + public bool ExecuteInstruction(S8Instruction instr) + { + // HALT + if (instr.operationClass == 0x0) return false; + + // SET + + else if (instr.operationClass == 0x1) + { + state.regs[instr.operation] = (byte)instr.value; + } + else if (instr.operationClass == 0x2) + { + state.regs[instr.operation] = state.regs[instr.argument1]; + } + // FINN + else if (instr.operationClass == 0x3) + { + state.regs[1] = (byte)((instr.address & 0x0f00) >> 8); + state.regs[0] = (byte)(instr.address & 0xff); + } + + // LOAD / STORE + else if (instr.operationClass == 0x4) + { + int addr = ((state.regs[1] << 8) | state.regs[0]) & 0xfff; + if (instr.operation == 0) state.regs[instr.argument1] = state.memory[addr]; + else if (instr.operation == 1) state.memory[addr] = (byte)state.regs[instr.argument1]; + else + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); + return false; + } + } + + // ALU + else if (instr.operationClass == 0x5) + { + byte reg1 = state.regs[instr.argument1]; + byte reg2 = state.regs[instr.argument2]; + + if (instr.operation == 0x0) state.regs[instr.argument1] &= reg2; + else if (instr.operation == 0x1) state.regs[instr.argument1] |= reg2; + else if (instr.operation == 0x2) state.regs[instr.argument1] ^= reg2; + else if (instr.operation == 0x3) + state.regs[instr.argument1] = (byte)((reg1 << reg2) & 0xff); + else if (instr.operation == 0x4) state.regs[instr.argument1] >>= reg2; + else if (instr.operation == 0x5) + state.regs[instr.argument1] = (byte)((reg1 + reg2) & 0xff); + else if (instr.operation == 0x6) + state.regs[instr.argument1] = (byte)((reg1 - reg2) & 0xff); + else + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); + return false; + } + } + + // I/O + else if (instr.operationClass == 0x6) + { + // READ + if (instr.operation == 0x0) + { + if (state.stdin.Length > state.inputPtr) + { + state.regs[instr.argument1] = state.stdin[state.inputPtr++]; + } + else + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.readAfterEndOfInput]); + return false; + } + } + + // WRITE + else if (instr.operation == 0x1) + { + byte val = state.regs[instr.argument1]; + state.stdout += val.ToString("X2"); + } + else + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); + return false; + } + } + // CMP + else if (instr.operationClass == 0x7) + { + byte reg1 = state.regs[instr.argument1]; + byte reg2 = state.regs[instr.argument2]; + + if (instr.operation == 0x0) state.flag = reg1 == reg2; + else if (instr.operation == 0x1) state.flag = reg1 != reg2; + else if (instr.operation == 0x2) state.flag = reg1 < reg2; + else if (instr.operation == 0x3) state.flag = reg1 <= reg2; + else if (instr.operation == 0x4) state.flag = reg1 > reg2; + else if (instr.operation == 0x5) state.flag = reg1 >= reg2; + else + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); + return false; + } + } + + // JMP + else if (instr.operationClass == 0x8) state.pc = instr.address; + // COND JMP + else if (instr.operationClass == 0x9) + { + if (state.flag) + { + state.pc = instr.address; + } + } + + // CALL + else if (instr.operationClass == 0xa) + { + + + if (!stack.Push(state.pc)) + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.recursionLimitExceeded]); + return false; + } + state.pc = instr.address; + } + + // RET + else if (instr.operationClass == 0xb) + { + state.pc = stack.Pop(); + if (state.pc < 0) + { + state.pc = 0; + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); + return false; + } + } + else if (instr.operationClass == 0xc) return true; + else + { + Console.WriteLine(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); + return false; + } + + return true; + } + } +} + \ No newline at end of file diff --git a/S8Debugger.csproj b/S8Debugger.csproj new file mode 100644 index 0000000..5d34847 --- /dev/null +++ b/S8Debugger.csproj @@ -0,0 +1,10 @@ + + + + Exe + net5.0 + S8Debugger + S8Debugger + + + diff --git a/S8Debugger.sln b/S8Debugger.sln new file mode 100644 index 0000000..c8aa709 --- /dev/null +++ b/S8Debugger.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30709.64 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S8Debugger", "S8Debugger.csproj", "{98A9D7D6-BFFC-46F2-A1AE-80E4C8778F25}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {98A9D7D6-BFFC-46F2-A1AE-80E4C8778F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98A9D7D6-BFFC-46F2-A1AE-80E4C8778F25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98A9D7D6-BFFC-46F2-A1AE-80E4C8778F25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98A9D7D6-BFFC-46F2-A1AE-80E4C8778F25}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3683CB41-3CBD-409D-8484-2E19E398A1F5} + EndGlobalSection +EndGlobal diff --git a/S8Dissasembler.cs b/S8Dissasembler.cs new file mode 100644 index 0000000..4a98bb1 --- /dev/null +++ b/S8Dissasembler.cs @@ -0,0 +1,298 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace S8Debugger +{ + public class S8Dissasembler + { + byte[] bytes = null; + S8CPU cpu = new S8CPU(); + + public bool Init(string fname, bool force = false) + { + + if (!File.Exists(fname)) return false; + + var executable = File.ReadAllBytes(fname); + bytes = cpu.Load(executable, force); + + if (bytes is null) return false; + + Console.WriteLine("Loaded image " + cpu.state.memoryUsed + " bytes"); + return true; + } + + internal bool InitFromMemory(byte[] s8prog) + { + bytes = cpu.Load(s8prog, true); + + if (bytes is null) return false; + Console.WriteLine("Loaded image " + cpu.state.memoryUsed + " bytes"); + return true; + } + + public int MemoryDump(int start, int length, bool showAddress = false) + { + int currentAddress = start; + int endAddress = currentAddress + length; + + // Special - if no length givien, assume 8 instructions + if (length == 0) + { + endAddress = currentAddress + 8; + //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) + { + line1 = ".DATA "; + line2 = ";" + currentAddress.ToString("X4") + ": "; + } + else + { + line1 += ", "; + line2 += " "; + + } + byte b = bytes[currentAddress]; + //b = (byte)(b ^ (byte)65); + + line1 += "0x" + b.ToString("X2"); + + // Line to ASCII mapping + char c = (char)bytes[currentAddress]; + if (char.IsLetterOrDigit(c)) + { + line2 += c; + } + else + { + line2 += '.'; + } + line2 += " "; + + + currentAddress++; + + if ((lineCounter++ > 8) | (currentAddress >= endAddress)) + { + Console.WriteLine(line1); + Console.WriteLine(line2); + + lineCounter = 0; + Console.WriteLine(); + } + } + + Console.WriteLine(); + + return currentAddress; + } + + + + internal void Reset() + { + cpu.ResetRegs(); + } + + public int Dissasemble(int start, int length, bool showAddress = false) + { + S8Instruction s8i; + + int currentAddress = start; + int endAddress = currentAddress + length; + + // Special - if no length givien, assume 8 instructions + if (length == 0) + { + endAddress = currentAddress + 8; + //endAddress = cpu.state.memoryUsed; + } + + if (endAddress > cpu.state.memoryUsed) + { + endAddress = cpu.state.memoryUsed; + } + + while (currentAddress < endAddress) + { + string sHexAddress = currentAddress.ToString("X4"); + + byte opcode = bytes[currentAddress++]; + byte param = bytes[currentAddress++]; + + s8i = new S8Instruction(opcode, param); + s8i.DecodeInstruction(); + + if (showAddress) + { + string sOpcode = opcode.ToString("X2"); + string sParam = param.ToString("X2"); + Console.Write("A["+sHexAddress + "] | I["+sOpcode + " " + sParam + "] "); + } + + if (s8i.ValidInstruction) + { + Console.WriteLine(s8i.DecodedInstruction); + } + else + { + string data = ".DATA 0x" + opcode.ToString("X2"); + Console.WriteLine(data + " ; " + s8i.ErrorMessage); + } + } + return currentAddress; + + } + + internal int SetPC(int start) + { + if (start > cpu.state.memoryUsed) + { + start = 0; + } + cpu.state.pc = start; + + return cpu.state.pc; + } + + internal void SetInput(string v) + { + string s = ConvertHex2Asii(v); + + cpu.state.stdin = new byte[s.Length]; + for (int i=0;i 0) & (!cpu.state.crashed)); + + Oppgulp(); + Regs(); + return cpu.state.pc; + } + + + private string ConvertHex2Asii(string hex) + { + int i = 0; + int value = 0; + string prefixedHex; + + string returnText = ""; + + try + { + while (i < hex.Length) + { + prefixedHex = "0x" + hex[i] + hex[i + 1]; + value = Convert.ToInt32(prefixedHex, 16); + i = i + 2; + + returnText += (char)value; + } + } + catch (Exception) + { + + // + } + + return returnText; + + } + + + private void Oppgulp() + { + if (cpu.state.stdout.Length > 0) + { + Console.WriteLine(">HEX: " + cpu.state.stdout); + + + Console.WriteLine(">ASCII: " + ConvertHex2Asii(cpu.state.stdout)); + + + cpu.state.stdout = ""; + + } + } + + public void Regs() + { + Console.WriteLine("PC [" + cpu.state.pc.ToString("X4") +"]"); + Console.WriteLine("FLAG [" + cpu.state.flag +"]"); + for (int i=0; i<16;i++) + { + Console.Write("R" + i + "[" + cpu.state.regs[i].ToString("X2") + "] "); + } + Console.WriteLine(); + } + } +} diff --git a/S8Instruction.cs b/S8Instruction.cs new file mode 100644 index 0000000..66c6233 --- /dev/null +++ b/S8Instruction.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace S8Debugger +{ + public class S8Instruction + { + public int operationClass; + public int operation; + public int address; + public int value; + public int argument1; + public int argument2; + + public string DecodedInstruction; + public bool ValidInstruction; + public string ErrorMessage; + + public S8Instruction(byte opcode, byte param) + { + init(opcode, param); + } + public void init(byte opcode, byte param) + { + ValidInstruction = false; + + int instruction = opcode | (param << 8); + + operationClass = instruction & 0xf; + operation = (instruction >> 4) & 0xf; + address = instruction >> 4; + value = instruction >> 8; + argument1 = (instruction >> 8) & 0xf; + argument2 = (instruction >> 12) & 0xf; + } + + public string DefaultDecodeError() + { + return "Unknown operation [" + operation + "] in operationClass 0x" + operationClass; + } + + public void DecodeInstruction() + { + DecodedInstruction = "; NOT DECODED"; + switch (operationClass) + { + + case 0x0: + switch (operation) + { + case 0x0: // STOPP + DecodedInstruction = "STOPP"; + ValidInstruction = true; + break; + default: + ErrorMessage = DefaultDecodeError(); + break; + } + break; + + case 0x1: // 0001 0001 + DecodedInstruction = "SETT r" + operation.ToString() + ", " + value; + ValidInstruction = true; + break; + + case 0x2: // nnnn 0010 + DecodedInstruction = "SETT r" + operation.ToString() + ", r" + argument1; + ValidInstruction = true; + break; + case 0x3: + + int regs1 = (address & 0x0f00) >> 8; + int regs0 = address & 0xff; + DecodedInstruction = "FINN ; r0 = " + regs0.ToString("X2") + " r1 = " + regs1.ToString("X2"); + ValidInstruction = true; + break; + + case 0x04: //Last nn + + switch (operation) + { + case 0x0: + DecodedInstruction = "LAST r" + argument1; + ValidInstruction = true; + break; + case 0x1: + DecodedInstruction = "LAGR r" + argument1; ; + ValidInstruction = true; + break; + + default: + ErrorMessage = DefaultDecodeError(); + break; + } + break; + case 0x05: // Logic + + string logicFunction = ""; + switch (operation) + { + case 0x0: + logicFunction = "OG "; + ValidInstruction = true; + break; + case 0x1: + logicFunction = "ELLER "; + ValidInstruction = true; + break; + case 0x2: + logicFunction = "XELLER "; + ValidInstruction = true; + break; + case 0x3: + logicFunction = "VSKIFT "; + ValidInstruction = true; + break; + case 0x4: + logicFunction = "HSKIFT "; + ValidInstruction = true; + break; + case 0x5: + logicFunction = "PLUSS "; + ValidInstruction = true; + break; + case 0x6: + logicFunction = "MINUS "; + ValidInstruction = true; + break; + + default: + ErrorMessage = DefaultDecodeError(); + break; + } + + DecodedInstruction = logicFunction + "r" + argument1 + ", r" + argument2; + break; + + case 0x6: // LES & SKRIV + switch (operation) + { + case 0x0: //LES + DecodedInstruction = "LES r" + argument1; + ValidInstruction = true; + break; + case 0x1: //LES + DecodedInstruction = "SKRIV r" + argument1; + ValidInstruction = true; + break; + default: + ErrorMessage = DefaultDecodeError(); + break; + + } + break; + case 0x07: //LIK p + string cmpFunction = ""; + switch (operation) + { + case 0x0: + cmpFunction = "LIK "; + ValidInstruction = true; + break; + case 0x1: + cmpFunction = "ULIK "; + ValidInstruction = true; + break; + case 0x2: + cmpFunction = "ME "; + ValidInstruction = true; + break; + case 0x3: + cmpFunction = "MEL "; + ValidInstruction = true; + break; + case 0x4: + cmpFunction = "SE "; + ValidInstruction = true; + break; + case 0x5: + cmpFunction = "SEL "; + ValidInstruction = true; + break; + default: + ErrorMessage = DefaultDecodeError(); + break; + + } + 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 + + ValidInstruction = true; + break; + case 0x9: + //jumpAddress = (param << 4) + operation; + //DecodedInstruction = "BHOPP " + jumpAddress.ToString("X4"); + DecodedInstruction = "BHOPP " + address.ToString("X4"); + + ValidInstruction = true; + break; + + case 0x0A: + //DecodedInstruction = "TUR " + operationClass + " 0x" + sParam; + DecodedInstruction = "TUR " + " 0x" + address.ToString("X4"); + ValidInstruction = true; + break; + case 0x0B: + switch (operation) + { + case 0x0: // RETUR + DecodedInstruction = "RETUR"; + ValidInstruction = true; + break; + default: + ErrorMessage = DefaultDecodeError(); + break; + } + break; + case 0x0C: + switch (operation) + { + case 0x0: // NOPE + DecodedInstruction = "NOPE"; + ValidInstruction = true; + break; + + default: + DecodedInstruction = DefaultDecodeError(); + break; + } + break; + + + default: + DecodedInstruction = DefaultDecodeError(); + break; + + + } + + } + + + + }; +}