diff --git a/SPEC.txt b/SPEC.txt index 4a77857..e36d87e 100644 --- a/SPEC.txt +++ b/SPEC.txt @@ -16,11 +16,11 @@ Marker (:) :XYZ Locations (@) Code ASM Type Values -0x0 P Port 0-255 +0x0 P Port 0-255 | 0xFF 0x1 R Register A-D -0x2 M Memory 0-65535 -0x3 I Immediate 0-65535 | 'a' | "a" | 0xFFFF -0x4 F Flag 0-255 +0x2 M Memory 0-65535 | 0xFFFF +0x3 # Immediate 0-65535 | 0xFFFF | 'a' | "a" +0x4 F Flag 0-255 | 0xFF 0x5 A Address In Register A-D Instructions @@ -48,10 +48,10 @@ Code ASM 0x31 AND R @ 0x32 OR R @ 0x33 XOR R @ -0x33 SHL R 0-8 Shift Left -0x34 SHR R 0-8 Shift Right -0x35 BTS R 0-8 Bit Set -0x36 BTC R 0-8 Bit Clear +0x34 SHL R 0-7 Shift Left +0x35 SHR R 0-7 Shift Right +0x36 BTS R 0-7 Bit Set +0x37 BTC R 0-7 Bit Clear Stack 0x40 CALL #|: 0x41 RET diff --git a/SVM/Assembler.cs b/SVM/Assembler.cs index 0124696..923bba3 100644 --- a/SVM/Assembler.cs +++ b/SVM/Assembler.cs @@ -49,8 +49,16 @@ namespace SVM //Records marker locations if (line.StartsWith(':')) { - markers.Add(line.Substring(1), mempos); - continue; + var markerParts = line.Split(" ", 2); + markers.Add(markerParts[0].Substring(1), mempos); + if (markerParts.Length > 1 && !string.IsNullOrWhiteSpace(markerParts[1])) + { + line = markerParts[1].Trim(); + } + else + { + continue; + } } parts = line.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries); @@ -122,6 +130,7 @@ namespace SVM } byte[] lineData = new byte[0]; + parts[1] = parts[1].Trim(); if (parts[1].StartsWith('\'') || parts[1].StartsWith('"')) { string asciiContent = string.Empty; diff --git a/SVM/Instructions/AND.cs b/SVM/Instructions/AND.cs new file mode 100644 index 0000000..8a000db --- /dev/null +++ b/SVM/Instructions/AND.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class AND : ADD + { + public override string ASM => "AND"; + public override byte OP => 0x31; + + protected override void Run(VM vm, byte reg, Location loc) + { + vm.R[reg] &= loc.Read(vm); + } + } +} diff --git a/SVM/Instructions/BTC.cs b/SVM/Instructions/BTC.cs new file mode 100644 index 0000000..5c5f62f --- /dev/null +++ b/SVM/Instructions/BTC.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class BTC : SHL + { + public override string ASM => "BTC"; + + public override byte OP => 0x37; + + protected override void Run(VM vm, byte reg, byte bit) + { + vm.R[reg] = (ushort)(vm.R[reg] & ~(1 << bit)); + } + } +} diff --git a/SVM/Instructions/BTS.cs b/SVM/Instructions/BTS.cs new file mode 100644 index 0000000..3c9d4b7 --- /dev/null +++ b/SVM/Instructions/BTS.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class BTS : SHL + { + public override string ASM => "BTS"; + + public override byte OP => 0x36; + + protected override void Run(VM vm, byte reg, byte bit) + { + vm.R[reg] = (ushort)(vm.R[reg] | 1 << bit); + } + } +} diff --git a/SVM/Instructions/CLR.cs b/SVM/Instructions/CLR.cs index 8830ae5..693da66 100644 --- a/SVM/Instructions/CLR.cs +++ b/SVM/Instructions/CLR.cs @@ -13,6 +13,7 @@ namespace SVM.Instructions public override byte[] Encode(string asm, Dictionary markerRefs) { + Debug.Assert(asm.Length == 1); byte reg = Register.FromASM(asm); return new byte[] { OP, reg }; } diff --git a/SVM/Instructions/CLRI.cs b/SVM/Instructions/CLRI.cs deleted file mode 100644 index e46519d..0000000 --- a/SVM/Instructions/CLRI.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Diagnostics; - -namespace SVM.Instructions -{ - class CLRI : Instruction - { - public override string ASM => "CLRI"; - - public override byte OP => 0x62; - - public override byte[] Encode(string asm, Dictionary markerRefs) - { - return new byte[] { OP }; - } - - public override void Exec(VM vm, byte[] vars) - { - Debug.Assert(vars.Length == 0); - vm.RI = 0; - } - - public override string ToASM(byte[] vars) - { - Debug.Assert(vars.Length == 0); - return ASM; - } - } -} diff --git a/SVM/Instructions/DECI.cs b/SVM/Instructions/DECI.cs deleted file mode 100644 index b67c3c7..0000000 --- a/SVM/Instructions/DECI.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Diagnostics; - -namespace SVM.Instructions -{ - class DECI : Instruction - { - public override string ASM => "DECI"; - - public override byte OP => 0x64; - - public override byte[] Encode(string asm, Dictionary markerRefs) - { - return new byte[] { OP }; - } - - public override void Exec(VM vm, byte[] vars) - { - Debug.Assert(vars.Length == 0); - vm.RI--; - } - - public override string ToASM(byte[] vars) - { - Debug.Assert(vars.Length == 0); - return ASM; - } - } -} diff --git a/SVM/Instructions/INCI.cs b/SVM/Instructions/INCI.cs deleted file mode 100644 index a5cd6ba..0000000 --- a/SVM/Instructions/INCI.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Diagnostics; - -namespace SVM.Instructions -{ - class INCI : Instruction - { - public override string ASM => "INCI"; - - public override byte OP => 0x63; - - public override byte[] Encode(string asm, Dictionary markerRefs) - { - return new byte[] { OP }; - } - - public override void Exec(VM vm, byte[] vars) - { - Debug.Assert(vars.Length == 0); - vm.RI++; - } - - public override string ToASM(byte[] vars) - { - Debug.Assert(vars.Length == 0); - return ASM; - } - } -} diff --git a/SVM/Instructions/LOADI.cs b/SVM/Instructions/LOADI.cs deleted file mode 100644 index 90ad27a..0000000 --- a/SVM/Instructions/LOADI.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace SVM.Instructions -{ - class LOADI : Instruction - { - public override string ASM => "LOADI"; - - public override byte OP => 0x60; - - public override byte[] Encode(string asm, Dictionary markerRefs) - { - var reg = Register.FromASM(asm); - return new byte[] { OP, reg }; - } - - public override byte[] Decode(VM vm) - { - var reg = vm.MEM[vm.PC]; - vm.PC++; - return new byte[] { reg }; - } - - public override void Exec(VM vm, byte[] vars) - { - Debug.Assert(vars.Length == 1); - var reg = vars[0]; - Debug.Assert(reg <= VM.REGISTERS); - Run(vm, reg); - } - - protected virtual void Run(VM vm, byte reg) - { - vm.R[reg] = vm.MEM[vm.RI]; - } - - public override string ToASM(byte[] vars) - { - return string.Format("{0} {1}", ASM, Register.ToASM(vars[0])); - } - } -} diff --git a/SVM/Instructions/SETI.cs b/SVM/Instructions/NOT.cs similarity index 58% rename from SVM/Instructions/SETI.cs rename to SVM/Instructions/NOT.cs index 24835f5..253100c 100644 --- a/SVM/Instructions/SETI.cs +++ b/SVM/Instructions/NOT.cs @@ -1,19 +1,19 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Text; +using System.Diagnostics; namespace SVM.Instructions { - class SETI : LOADI + class NOT : CLR { - public override string ASM => "SETI"; + public override string ASM => "NOT"; - public override byte OP => 0x65; + public override byte OP => 0x30; protected override void Run(VM vm, byte reg) { - vm.RI = vm.R[reg]; + vm.R[reg] = (ushort)~vm.R[reg]; } } } diff --git a/SVM/Instructions/OR.cs b/SVM/Instructions/OR.cs new file mode 100644 index 0000000..ae526f9 --- /dev/null +++ b/SVM/Instructions/OR.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class OR : ADD + { + public override string ASM => "OR"; + public override byte OP => 0x32; + + protected override void Run(VM vm, byte reg, Location loc) + { + vm.R[reg] |= loc.Read(vm); + } + } +} diff --git a/SVM/Instructions/RET.cs b/SVM/Instructions/RET.cs index f87116d..8ea8d91 100644 --- a/SVM/Instructions/RET.cs +++ b/SVM/Instructions/RET.cs @@ -28,7 +28,7 @@ namespace SVM.Instructions public override string ToASM(byte[] vars) { - throw new NotImplementedException(); + return ASM; } } } diff --git a/SVM/Instructions/SAVEI.cs b/SVM/Instructions/SAVEI.cs deleted file mode 100644 index 08a4f51..0000000 --- a/SVM/Instructions/SAVEI.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace SVM.Instructions -{ - class SAVEI : LOADI - { - public override string ASM => "SAVEI"; - - public override byte OP => 0x61; - - protected override void Run(VM vm, byte reg) - { - vm.MEM[vm.RI] = (byte)vm.R[reg]; - } - } -} diff --git a/SVM/Instructions/SHL.cs b/SVM/Instructions/SHL.cs new file mode 100644 index 0000000..8498904 --- /dev/null +++ b/SVM/Instructions/SHL.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace SVM.Instructions +{ + class SHL : Instruction + { + public override string ASM => "SHL"; + + public override byte OP => 0x34; + + public override byte[] Encode(string asm, Dictionary markerRefs) + { + var parts = asm.Split(" "); + Debug.Assert(parts.Length == 2); + byte reg = Register.FromASM(parts[0]); + byte bit = byte.Parse(parts[1]); + Debug.Assert(bit < 8); + + return new byte[] { OP, reg, bit }; + } + + public override byte[] Decode(VM vm) + { + var bc = vm.MEM.Subset(vm.PC, 2); + vm.PC += 2; + return bc; + } + + public override void Exec(VM vm, byte[] vars) + { + Debug.Assert(vars.Length == 2); + + byte reg = vars[0]; + byte bit = vars[1]; + + Debug.Assert(reg <= VM.REGISTERS); + Debug.Assert(bit < 8); + + Run(vm, reg, bit); + } + + protected virtual void Run(VM vm, byte reg, byte bit) + { + vm.R[reg] = (ushort)(vm.R[reg] << bit); + } + + public override string ToASM(byte[] vars) + { + Debug.Assert(vars.Length == 2); + + byte reg = vars[0]; + byte bit = vars[1]; + + return string.Format("{0} {1} {2}", ASM, Register.ToASM(reg), bit); + } + } +} diff --git a/SVM/Instructions/SHR.cs b/SVM/Instructions/SHR.cs new file mode 100644 index 0000000..11e54b2 --- /dev/null +++ b/SVM/Instructions/SHR.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class SHR : SHL + { + public override string ASM => "SHR"; + + public override byte OP => 0x35; + + protected override void Run(VM vm, byte reg, byte bit) + { + vm.R[reg] = (ushort)(vm.R[reg] >> bit); + } + } +} diff --git a/SVM/Instructions/XOR.cs b/SVM/Instructions/XOR.cs new file mode 100644 index 0000000..6edc6ae --- /dev/null +++ b/SVM/Instructions/XOR.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SVM.Instructions +{ + class XOR : ADD + { + public override string ASM => "XOR"; + + public override byte OP => 0x33; + + protected override void Run(VM vm, byte reg, Location loc) + { + vm.R[reg] ^= loc.Read(vm); + } + } +} diff --git a/SVM/Location.cs b/SVM/Location.cs index c1327d0..f5beca8 100644 --- a/SVM/Location.cs +++ b/SVM/Location.cs @@ -9,12 +9,16 @@ namespace SVM { public const byte PORT_CODE = 0x0; public const string PORT_ASM = "P"; - public const byte REG_CODE = 0x1; - public const string REG_ASM = "R"; - public const byte MEM_CODE = 0x2; - public const string MEM_ASM = "M"; - public const byte LITERAL_CODE = 0x3; - public const string LITERAL_ASM = "L"; + public const byte REGISTER_CODE = 0x1; + public const string REGISTER_ASM = "R"; + public const byte MEMORY_CODE = 0x2; + 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 string ADDRESS_ASM = "A"; public const int SIZE = 3; @@ -23,9 +27,11 @@ namespace SVM switch (type) { case PORT_ASM: return PORT_CODE; - case REG_ASM: return REG_CODE; - case MEM_ASM: return MEM_CODE; - case LITERAL_ASM: return LITERAL_CODE; + 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"); } } @@ -34,38 +40,63 @@ namespace SVM switch (type) { case PORT_CODE: return PORT_ASM; - case REG_CODE: return REG_ASM; - case MEM_CODE: return MEM_ASM; - case LITERAL_CODE: return LITERAL_ASM; + 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"); } } public static Location FromASM(string asm) { - var parts = asm.Split(" ", 2); - Debug.Assert(parts.Length > 0); - byte type = EncodeType(parts[0]); + Debug.Assert(asm.Length > 1); + asm = asm.Replace(" ", ""); + byte type = EncodeType(asm[0].ToString()); ushort loc = 0; - Debug.Assert(parts.Length == 2); - string locVal = parts[1]; - if (locVal.StartsWith('\'') || locVal.StartsWith('"')) + string locVal = asm.Substring(1); + switch(type) { - Debug.Assert(locVal.Length > 1); - loc = (byte)locVal[1]; - } - else if (locVal.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - { - Debug.Assert(locVal.Length > 2); - loc = ushort.Parse(locVal.Substring(2), System.Globalization.NumberStyles.HexNumber); - } - else - { - loc = ushort.Parse(locVal); + case PORT_CODE: + case FLAG_CODE: + loc = byte.Parse(locVal); + break; + case ADDRESS_CODE: + case REGISTER_CODE: + loc = Register.FromASM(locVal); + break; + case MEMORY_CODE: + loc = ReadNumber(locVal, true); + break; + case IMMEDIATE_CODE: + loc = ReadNumber(locVal, false); + break; } return new Location(type, loc); } + + private static ushort ReadNumber(string val, bool allowChar) + { + ushort converted; + if (allowChar && (val.StartsWith('\'') || val.StartsWith('"'))) + { + Debug.Assert(val.Length > 1); + converted = (byte)val[1]; + } + else if (val.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + { + Debug.Assert(val.Length > 2); + converted = ushort.Parse(val.Substring(2), System.Globalization.NumberStyles.HexNumber); + } + else + { + converted = ushort.Parse(val); + } + return converted; + } + public static Location FromByteCode(byte[] data, int start) { Debug.Assert(data.Length > start); @@ -90,13 +121,20 @@ namespace SVM switch (type) { case PORT_CODE: + Debug.Assert(loc <= VM.PORTS); return vm.Ports[loc].Read(); - case REG_CODE: + case REGISTER_CODE: + Debug.Assert(loc <= VM.REGISTERS); return vm.R[loc]; - case MEM_CODE: + case MEMORY_CODE: return vm.MEM[loc]; - case LITERAL_CODE: + 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]]; default: throw new Exception("Invalid location type"); } } @@ -105,16 +143,24 @@ namespace SVM switch (type) { case PORT_CODE: - vm.Ports[loc].Write((byte)val); + Debug.Assert(loc <= VM.PORTS); + vm.Ports[loc].Write((byte)(val & 0xFF)); break; - case REG_CODE: + case REGISTER_CODE: + Debug.Assert(loc <= VM.REGISTERS); vm.R[loc] = val; break; - case MEM_CODE: - vm.MEM[loc] = (byte)val; + case MEMORY_CODE: + vm.MEM[loc] = (byte)(val & 0xFF); break; - case LITERAL_CODE: + 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); + break; default: throw new Exception("Invalid location type"); } @@ -122,14 +168,25 @@ namespace SVM public byte[] Encode() { - byte ub = (byte)((loc & 0xFF00) >> 8); - byte lb = (byte)(loc & 0xFF); - return new byte[] { type, ub, lb }; + return new byte[] { type, loc.HiByte(), loc.LoByte() }; } public string ToASM() { - return string.Format("{0} 0x{1:X}", DecodeType(type), loc); + 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)); + case ADDRESS_CODE: + return string.Format("A{0}", Register.ToASM((byte)loc)); + case IMMEDIATE_CODE: + case MEMORY_CODE: + default: + return string.Format("{0}0x{1:X}", DecodeType(type), loc); + } } } } diff --git a/SVM/PGM/CALL.txt b/SVM/PGM/CALL.txt index 89d79c2..e6e2fbe 100644 --- a/SVM/PGM/CALL.txt +++ b/SVM/PGM/CALL.txt @@ -1,29 +1,25 @@ ORIGIN 0 -JMP :PGM + JMP :PGM -:WRITESTR -SETI A -:WRITESTR_LOOP -LOADI A -JZ A :WRITESTR_RET -SAVE A P 0 -INCI -JMP :WRITESTR_LOOP -:WRITESTR_RET -RET +:WRITESTR LOAD C RA +:WRITESTR_L LOAD A AC + JZ A :WRITESTR_R + SAVE A P0 + INC C + JMP :WRITESTR_L +:WRITESTR_R CLR C + RET -:WRITENL -LOAD A L 13 -SAVE A P 0 -LOAD A L 10 -SAVE A P 0 -RET +:WRITENL LOAD A I13 + SAVE A P0 + LOAD A I10 + SAVE A P0 + RET -:PGM -LOAD A L 0x100 -CALL :WRITESTR -CALL :WRITENL -HALT +:PGM LOAD A I0x100 + CALL :WRITESTR + CALL :WRITENL + HALT MEMORY -0x100 "CALL/RET Test" \ No newline at end of file +0x100 "CALL/RET Test" \ No newline at end of file diff --git a/SVM/PGM/HELLO.txt b/SVM/PGM/HELLO.txt index 010b992..88a8f26 100644 --- a/SVM/PGM/HELLO.txt +++ b/SVM/PGM/HELLO.txt @@ -2,36 +2,33 @@ #Print string 5 times CLR B -ADD B L 5 -ADD B L 48 -SAVE B P 0 -SUB B L 48 +ADD B I5 +ADD B I48 +SAVE B P0 +SUB B I48 -LOAD A L 0x300 #Set string start -SETI A -JMP :PRINTA +LOAD C I0x300 #Set string start :PRINTA #Print until zero -LOADI A +LOAD A AC JZ A :NEWLINE -SAVE A P 0 -INCI +SAVE A P0 +INC C JMP :PRINTA :NEWLINE #New Line -LOAD A L 13 -SAVE A P 0 -LOAD A L 10 -SAVE A P 0 +LOAD A I13 +SAVE A P0 +LOAD A I10 +SAVE A P0 -LOAD A L 0x300 #Set string start -SETI A +LOAD C I0x300 #Set string start DEC B #Check Counter and stop if 5 printed JZ B :STOP -ADD B L 48 -SAVE B P 0 -SUB B L 48 +ADD B I48 +SAVE B P0 +SUB B I48 JMP :PRINTA :STOP diff --git a/SVM/PGM/LOGIC.txt b/SVM/PGM/LOGIC.txt new file mode 100644 index 0000000..59c9c8d --- /dev/null +++ b/SVM/PGM/LOGIC.txt @@ -0,0 +1,11 @@ +ORIGIN 0 + LOAD A I1 + JMP :PGM + +:PGM SHL A 1 + NOT A + NOT A + JZ A :EXIT + JMP :PGM + +:EXIT HALT \ No newline at end of file diff --git a/SVM/SVM.csproj b/SVM/SVM.csproj index 998612e..ef052d8 100644 --- a/SVM/SVM.csproj +++ b/SVM/SVM.csproj @@ -10,6 +10,7 @@ + @@ -19,6 +20,9 @@ PreserveNewest + + PreserveNewest + diff --git a/SVM/VM.cs b/SVM/VM.cs index 5e82eee..5d68793 100644 --- a/SVM/VM.cs +++ b/SVM/VM.cs @@ -72,8 +72,8 @@ namespace SVM var pc = PC; var instr = instructions[MEM[PC++]]; byte[] decoded = instr.Decode(this); - //Console.WriteLine("A{0} B{1} C{2} D{3}", R[0], R[1], R[2], R[3]); - //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("0x{0:X4} {1}", pc, instr.ToASM(decoded)); instr.Exec(this, decoded); //Console.ReadKey(true); }