From 1efb9d69b5dd5ca3fcfbe2617917842359108ad7 Mon Sep 17 00:00:00 2001 From: Sam Stevens Date: Sat, 23 Dec 2017 17:26:35 +0000 Subject: [PATCH] Implemeted Flag support and flag CONSTS. Implmeneted ALIAS assembler instruction, JBC & JBS instructions. --- FLAGS.md | 12 +++--- SPEC.md | 7 ++-- SVM/Assembler.cs | 22 +++++++++++ SVM/Flag.cs | 33 ++++++++++++++++ SVM/Flags/CONSTS.cs | 51 ++++++++++++++++++++++++ SVM/Instructions/JBC.cs | 83 ++++++++++++++++++++++++++++++++++++++++ SVM/Instructions/JBS.cs | 19 +++++++++ SVM/Instructions/SHL.cs | 2 +- SVM/Location.cs | 20 +++------- SVM/PGM/FlagTest.txt | 52 +++++++++++++++++++++++++ SVM/Ports/ConsolePort.cs | 21 ++++++++-- SVM/SVM.csproj | 4 ++ SVM/VM.cs | 43 ++++++++++++++++++--- 13 files changed, 335 insertions(+), 34 deletions(-) create mode 100644 SVM/Flag.cs create mode 100644 SVM/Flags/CONSTS.cs create mode 100644 SVM/Instructions/JBC.cs create mode 100644 SVM/Instructions/JBS.cs create mode 100644 SVM/PGM/FlagTest.txt diff --git a/FLAGS.md b/FLAGS.md index 70f13b8..7e3e5eb 100644 --- a/FLAGS.md +++ b/FLAGS.md @@ -8,11 +8,11 @@ Bits marked N/A read as zero and writing values will not have an effect. ### Status | CONSTS | 0x00 -| | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +| | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | | --------------- | --- | --- | --- | --- | --- | --- | --- | --- | -| **Read/Write** | | | | | | | R/W | R | -| **Defaults** | | | | | | | 1 | X | -| **Name** | N/A | N/A | N/A | N/A | N/A | N/A | Enabled | Available | +| **Read/Write** | | | | | R | R/W | R/W | R | +| **Default ** | | | | | X | 1 | 1 | X | +| **Name** | N/A | N/A | N/A | N/A | ReadAvailable | ReadBlock | Enabled | Available | ### Cursor X | CONPOSX | 0x01 @@ -50,9 +50,9 @@ Trap status and settings \ No newline at end of file diff --git a/SPEC.md b/SPEC.md index 7610e98..f467eef 100644 --- a/SPEC.md +++ b/SPEC.md @@ -71,8 +71,8 @@ | 0x51 | JMP | [#\|:] | | 0x52 | JZ | [R] [#\|:] | | 0x53 | JNZ | [R] [#\|:] | -| 0x54 | JBS | [R] [0-7] [#\|:] | -| 0x55 | JBC | [R] [0-7] [#\|:] | +| 0x54 | JBS | [R] [1-8] [#\|:] | +| 0x55 | JBC | [R] [1-8] [#\|:] | | **System** | 0x60 | SYS | [0-255] | Syscall, parameters depend on call @@ -80,7 +80,8 @@ | ASM | Parameters | Notes | | ------ | ---------- | ------------ | -| ORIGIN | [#] | Sets the Memory Address for the next instruction | +| ORIGIN | [#] | Sets the Memory Address for the next instruction +| ALIAS | X Y | Replaces word X with word Y where X and Y are alpha numberic, only applies after the instruction ## Program Format diff --git a/SVM/Assembler.cs b/SVM/Assembler.cs index 923bba3..9a5c9be 100644 --- a/SVM/Assembler.cs +++ b/SVM/Assembler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text; using System.Linq; using System.Diagnostics; +using System.Text.RegularExpressions; namespace SVM { @@ -28,8 +29,11 @@ namespace SVM ushort lastpos = 0; string line; string[] parts; + Dictionary markers = new Dictionary(); Dictionary> markerUses = new Dictionary>(); + Dictionary aliases = new Dictionary(); + //Encode ops until memory section for (; i < lines.Length; i++) { @@ -46,6 +50,12 @@ namespace SVM line = line.Replace(" ", " "); } + //Replace aliases + foreach(var alias in aliases) + { + line = Regex.Replace(line, string.Format(@"(^|\b){0}($|\b)", alias.Key), alias.Value); + } + //Records marker locations if (line.StartsWith(':')) { @@ -77,6 +87,18 @@ namespace SVM if (mempos > lastpos) lastpos = mempos; continue; } + if (op == "ALIAS") + { + Debug.Assert(parts.Length == 2); + var aliasParts = parts[1].Split(' ', 2); + Debug.Assert(aliasParts.Length == 2); + if (aliases.ContainsKey(aliasParts[0])) + { + aliases.Remove(aliasParts[0]); + } + aliases.Add(aliasParts[0], aliasParts[1]); + continue; + } var instr = instructions.First(x => x.ASM == op); diff --git a/SVM/Flag.cs b/SVM/Flag.cs new file mode 100644 index 0000000..5cbae4b --- /dev/null +++ b/SVM/Flag.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace SVM +{ + abstract class Flag + { + public static Flag[] GetAllFlags() + { + var types = from t in Assembly.GetAssembly(typeof(Flag)).GetTypes() + where t.IsSubclassOf(typeof(Flag)) + select t; + + var flags = new List(); + + foreach (var t in types) + { + flags.Add((Flag)Activator.CreateInstance(t)); + } + + return flags.ToArray(); + } + + public abstract string ASM { get; } + public abstract byte Address { get; } + + public abstract byte Read(VM vm); + public abstract void Write(VM vm, byte val); + } +} diff --git a/SVM/Flags/CONSTS.cs b/SVM/Flags/CONSTS.cs new file mode 100644 index 0000000..5d9f1e9 --- /dev/null +++ b/SVM/Flags/CONSTS.cs @@ -0,0 +1,51 @@ +using SVM.Ports; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace SVM.Flags +{ + class CONSTS : Flag + { + public const byte AVAILABLE = 1; + public const byte ENABLED = 2; + public const byte READBLOCK = 4; + public const byte READAVAILABLE = 8; + + public override string ASM => "CONSTS"; + + public override byte Address => 0x00; + + public override byte Read(VM vm) + { + var port = vm.Ports[0] as ConsolePort; + Debug.Assert(port != null); + + byte result = AVAILABLE; + if (port.Enabled) + { + result |= ENABLED; + } + if (port.ReadBlock) + { + result |= READBLOCK; + } + if (Console.KeyAvailable) + { + result |= READAVAILABLE; + } + + return result; + } + + public override void Write(VM vm, byte val) + { + var port = vm.Ports[0] as ConsolePort; + Debug.Assert(port != null); + + port.Enabled = (val & ENABLED) == ENABLED; + port.ReadBlock = (val & READBLOCK) == READBLOCK; + } + } +} diff --git a/SVM/Instructions/JBC.cs b/SVM/Instructions/JBC.cs new file mode 100644 index 0000000..457a2e9 --- /dev/null +++ b/SVM/Instructions/JBC.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace SVM.Instructions +{ + class JBC : Instruction + { + public override string ASM => "JBC"; + + public override byte OP => 0x55; + + public override byte[] Encode(string asm, Dictionary markerRefs) + { + var parts = asm.Split(' '); + Debug.Assert(parts.Length == 3); + + byte reg = Register.FromASM(parts[0]); + + byte bit = byte.Parse(parts[1]); + Debug.Assert(bit >= 1 && bit <= 8); + + ushort loc = 0; + if (parts[2].StartsWith(':') && markerRefs != null) + { + markerRefs.Add(parts[2].Substring(1), 3); + } + else if (parts[2].StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + { + loc = ushort.Parse(parts[2].Substring(2), System.Globalization.NumberStyles.HexNumber); + } + else + { + loc = ushort.Parse(parts[2]); + } + + return new byte[] { OP, reg, bit, loc.HiByte(), loc.LoByte() }; + } + + public override byte[] Decode(VM vm) + { + var bc = vm.MEM.Subset(vm.PC, 4); + vm.PC += 4; + return bc; + } + + public override void Exec(VM vm, byte[] vars) + { + Debug.Assert(vars.Length == 4); + + byte reg = vars[0]; + Debug.Assert(reg <= VM.REGISTERS); + + byte bit = vars[1]; + Debug.Assert(bit >= 1 && bit <= 8); + + ushort loc = (ushort)((vars[2] << 8) + vars[3]); + + if (CheckJump(vm, reg, bit)) + { + vm.PC = loc; + } + } + + protected virtual bool CheckJump(VM vm, byte reg, byte bit) + { + byte compare = (byte)(1 << bit - 1); + return (vm.R[reg] & compare) == 0; + } + + public override string ToASM(byte[] vars) + { + Debug.Assert(vars.Length == 4); + byte reg = vars[0]; + Debug.Assert(reg <= VM.REGISTERS); + byte bit = vars[1]; + ushort loc = (ushort)((vars[2] << 8) + vars[3]); + + return string.Format("{0} {1} {2} {3}", ASM, Register.ToASM(reg), bit, string.Format("0x{0:X}", loc)); + } + } +} diff --git a/SVM/Instructions/JBS.cs b/SVM/Instructions/JBS.cs new file mode 100644 index 0000000..82781f1 --- /dev/null +++ b/SVM/Instructions/JBS.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class JBS : JBC + { + public override string ASM => "JBS"; + + public override byte OP => 0x54; + + protected override bool CheckJump(VM vm, byte reg, byte bit) + { + byte compare = (byte)(1 << bit - 1); + return (vm.R[reg] & compare) == compare; + } + } +} diff --git a/SVM/Instructions/SHL.cs b/SVM/Instructions/SHL.cs index 8498904..f02a4bf 100644 --- a/SVM/Instructions/SHL.cs +++ b/SVM/Instructions/SHL.cs @@ -44,7 +44,7 @@ namespace SVM.Instructions protected virtual void Run(VM vm, byte reg, byte bit) { - vm.R[reg] = (ushort)(vm.R[reg] << bit); + vm.R[reg] = (ushort)(vm.R[reg] << bit-1); } public override string ToASM(byte[] vars) diff --git a/SVM/Location.cs b/SVM/Location.cs index f5beca8..0dd807c 100644 --- a/SVM/Location.cs +++ b/SVM/Location.cs @@ -15,9 +15,7 @@ namespace SVM public const string MEMORY_ASM = "M"; public const byte IMMEDIATE_CODE = 0x3; public const string IMMEDIATE_ASM = "I"; - public const byte FLAG_CODE = 0x4; - public const string FLAG_ASM = "F"; - public const byte ADDRESS_CODE = 0x5; + public const byte ADDRESS_CODE = 0x4; public const string ADDRESS_ASM = "A"; public const int SIZE = 3; @@ -30,7 +28,6 @@ namespace SVM case REGISTER_ASM: return REGISTER_CODE; case MEMORY_ASM: return MEMORY_CODE; case IMMEDIATE_ASM: return IMMEDIATE_CODE; - case FLAG_ASM: return FLAG_CODE; case ADDRESS_ASM:return ADDRESS_CODE; default: throw new Exception("Invalid location type"); } @@ -43,7 +40,6 @@ namespace SVM case REGISTER_CODE: return REGISTER_ASM; case MEMORY_CODE: return MEMORY_ASM; case IMMEDIATE_CODE: return IMMEDIATE_ASM; - case FLAG_CODE: return FLAG_ASM; case ADDRESS_CODE: return ADDRESS_ASM; default: throw new Exception("Invalid location type"); } @@ -59,7 +55,6 @@ namespace SVM switch(type) { case PORT_CODE: - case FLAG_CODE: loc = byte.Parse(locVal); break; case ADDRESS_CODE: @@ -127,14 +122,12 @@ namespace SVM Debug.Assert(loc <= VM.REGISTERS); return vm.R[loc]; case MEMORY_CODE: - return vm.MEM[loc]; + return vm.Read(loc); case IMMEDIATE_CODE: return loc; - case FLAG_CODE: - throw new NotImplementedException(); case ADDRESS_CODE: Debug.Assert(loc <= VM.REGISTERS); - return vm.MEM[vm.R[loc]]; + return vm.Read(vm.R[loc]); default: throw new Exception("Invalid location type"); } } @@ -151,15 +144,13 @@ namespace SVM vm.R[loc] = val; break; case MEMORY_CODE: - vm.MEM[loc] = (byte)(val & 0xFF); + vm.Write(loc, (byte)(val & 0xFF)); break; case IMMEDIATE_CODE: throw new Exception("Invalid operation"); - case FLAG_CODE: - throw new NotImplementedException(); case ADDRESS_CODE: Debug.Assert(loc <= VM.REGISTERS); - vm.MEM[vm.R[loc]] = (byte)(val & 0xFF); + vm.Write(vm.R[loc], (byte)(val & 0xFF)); break; default: throw new Exception("Invalid location type"); @@ -176,7 +167,6 @@ namespace SVM switch(type) { case PORT_CODE: - case FLAG_CODE: return string.Format("{0}{1}", DecodeType(type), loc); case REGISTER_CODE: return string.Format("R{0}", Register.ToASM((byte)loc)); diff --git a/SVM/PGM/FlagTest.txt b/SVM/PGM/FlagTest.txt new file mode 100644 index 0000000..5710c71 --- /dev/null +++ b/SVM/PGM/FlagTest.txt @@ -0,0 +1,52 @@ +ALIAS F_CONSTS 0xFF00 +ALIAS STR_ENABLED 0x100 +ALIAS STR_AVAILABLE 0x110 +ALIAS STR_READBLOCK 0x120 +ALIAS STR_READAVAILABLE 0x130 +ALIAS STR_YES 0x140 +ALIAS STR_NO 0x150 + +ORIGIN 0 + LOAD D M F_CONSTS # Read the flag into reg D + + LOAD A I STR_ENABLED + CALL :WSTR + JBS D 2 :ENYES # Check for enabled + LOAD A I STR_NO # Set str to NO if enabled bit not set + JMP :ENWR +:ENYES LOAD A I STR_YES +:ENWR CALL :WSTR + CALL :WNL + + LOAD A I STR_READAVAILABLE + CALL :WSTR + JBS D 4 :RAYES + LOAD A I STR_NO + JMP :RAWR +:RAYES LOAD A I STR_YES +:RAWR CALL :WSTR + CALL :WNL + + HALT + +:WSTR LOAD C RA +:WSTR_L LOAD A AC + JZ A :WSTR_R + SAVE A P0 + INC C + JMP :WSTR_L +:WSTR_R RET + +:WNL LOAD A I13 + SAVE A P0 + LOAD A I10 + SAVE A P0 + RET + +MEMORY +0x100 "Enabled " +0x110 "Available " +0x120 "ReadBlock " +0x130 "ReadAvailable " +0x140 "Yes" +0x150 "No" \ No newline at end of file diff --git a/SVM/Ports/ConsolePort.cs b/SVM/Ports/ConsolePort.cs index 884eca0..ce9fa9b 100644 --- a/SVM/Ports/ConsolePort.cs +++ b/SVM/Ports/ConsolePort.cs @@ -6,19 +6,32 @@ namespace SVM.Ports { class ConsolePort : Port { + public bool Enabled { get; set; } + public bool ReadBlock { get; set; } + public ConsolePort(VM vm) : base(vm) - { } + { + Enabled = true; + ReadBlock = true; + } public override ushort Read() { - var result = Console.ReadKey(true); - return (byte)result.KeyChar; + if (ReadBlock || Console.KeyAvailable) + { + var result = Console.ReadKey(true); + return (byte)result.KeyChar; + } + return 0; } public override void Write(byte val) { - Console.Write((char)val); + if (Enabled) + { + Console.Write((char)val); + } } } } diff --git a/SVM/SVM.csproj b/SVM/SVM.csproj index ef052d8..81fa700 100644 --- a/SVM/SVM.csproj +++ b/SVM/SVM.csproj @@ -9,6 +9,7 @@ + @@ -17,6 +18,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/SVM/VM.cs b/SVM/VM.cs index 5d68793..66756d5 100644 --- a/SVM/VM.cs +++ b/SVM/VM.cs @@ -8,7 +8,7 @@ namespace SVM class VM { public const int REGISTERS = 4; - public const int PORTS = 8; + public const int PORTS = 255; public const int STACKDEPTH = 16; public const int MEMSIZE = 0xFFFF; @@ -20,18 +20,23 @@ namespace SVM public ushort[] STACK = new ushort[STACKDEPTH]; public byte[] MEM = new byte[MEMSIZE]; public Port[] Ports = new Port[PORTS]; + public ushort FlagStart = 0xFF00; public int CycleDelay = 0; private Dictionary instructions = new Dictionary(); + private Dictionary flags = new Dictionary(); public VM() { - var instrs = Instruction.GetAllInstructions(); - foreach(var instr in instrs) + foreach(var instr in Instruction.GetAllInstructions()) { instructions.Add(instr.OP, instr); } + foreach(var flag in Flag.GetAllFlags()) + { + flags.Add(flag.Address, flag); + } Ports[0] = new Ports.ConsolePort(this); Reset(); @@ -72,11 +77,39 @@ namespace SVM var pc = PC; var instr = instructions[MEM[PC++]]; byte[] decoded = instr.Decode(this); - Console.Write("{4:X4} | A{0:X3} B{1:X3} C{2:X3} D{3:X3} | ", R[0], R[1], R[2], R[3], pc); - Console.WriteLine("0x{0:X4} {1}", pc, instr.ToASM(decoded)); + //Console.Write("{4:X4} | A{0:X3} B{1:X3} C{2:X3} D{3:X3} | ", R[0], R[1], R[2], R[3], pc); + //Console.WriteLine("{0}", instr.ToASM(decoded)); instr.Exec(this, decoded); //Console.ReadKey(true); } } + + public byte Read(ushort location) + { + if (location >= FlagStart && location < FlagStart + 0xFF) + { + byte flagAddress = (byte)(location - FlagStart); + if (flags.ContainsKey(flagAddress)) + { + return flags[flagAddress].Read(this); + } + return 0; + } + return MEM[location]; + } + public void Write(ushort location, byte val) + { + if (location >= FlagStart && location < FlagStart + 0xFF) + { + byte flagAddress = (byte)(location - FlagStart); + if (flags.ContainsKey(flagAddress)) + { + flags[flagAddress].Write(this, val); + } + } else + { + MEM[location] = val; + } + } } }