Implemented updated spec.

Added new logic instructions and test program.
ASM markers may now start a line with an instruction.
Improved logic in location class.
This commit is contained in:
2017-12-18 22:37:19 +00:00
parent 1c79cff33f
commit ad50509f26
23 changed files with 342 additions and 258 deletions

View File

@@ -16,11 +16,11 @@ Marker (:) :XYZ
Locations (@) Locations (@)
Code ASM Type Values Code ASM Type Values
0x0 P Port 0-255 0x0 P Port 0-255 | 0xFF
0x1 R Register A-D 0x1 R Register A-D
0x2 M Memory 0-65535 0x2 M Memory 0-65535 | 0xFFFF
0x3 I Immediate 0-65535 | 'a' | "a" | 0xFFFF 0x3 # Immediate 0-65535 | 0xFFFF | 'a' | "a"
0x4 F Flag 0-255 0x4 F Flag 0-255 | 0xFF
0x5 A Address In Register A-D 0x5 A Address In Register A-D
Instructions Instructions
@@ -48,10 +48,10 @@ Code ASM
0x31 AND R @ 0x31 AND R @
0x32 OR R @ 0x32 OR R @
0x33 XOR R @ 0x33 XOR R @
0x33 SHL R 0-8 Shift Left 0x34 SHL R 0-7 Shift Left
0x34 SHR R 0-8 Shift Right 0x35 SHR R 0-7 Shift Right
0x35 BTS R 0-8 Bit Set 0x36 BTS R 0-7 Bit Set
0x36 BTC R 0-8 Bit Clear 0x37 BTC R 0-7 Bit Clear
Stack Stack
0x40 CALL #|: 0x40 CALL #|:
0x41 RET 0x41 RET

View File

@@ -49,8 +49,16 @@ namespace SVM
//Records marker locations //Records marker locations
if (line.StartsWith(':')) if (line.StartsWith(':'))
{ {
markers.Add(line.Substring(1), mempos); var markerParts = line.Split(" ", 2);
continue; 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); parts = line.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries);
@@ -122,6 +130,7 @@ namespace SVM
} }
byte[] lineData = new byte[0]; byte[] lineData = new byte[0];
parts[1] = parts[1].Trim();
if (parts[1].StartsWith('\'') || parts[1].StartsWith('"')) if (parts[1].StartsWith('\'') || parts[1].StartsWith('"'))
{ {
string asciiContent = string.Empty; string asciiContent = string.Empty;

17
SVM/Instructions/AND.cs Normal file
View File

@@ -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);
}
}
}

18
SVM/Instructions/BTC.cs Normal file
View File

@@ -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));
}
}
}

18
SVM/Instructions/BTS.cs Normal file
View File

@@ -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);
}
}
}

View File

@@ -13,6 +13,7 @@ namespace SVM.Instructions
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs) public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
{ {
Debug.Assert(asm.Length == 1);
byte reg = Register.FromASM(asm); byte reg = Register.FromASM(asm);
return new byte[] { OP, reg }; return new byte[] { OP, reg };
} }

View File

@@ -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<string, ushort> 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;
}
}
}

View File

@@ -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<string, ushort> 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;
}
}
}

View File

@@ -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<string, ushort> 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;
}
}
}

View File

@@ -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<string, ushort> 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]));
}
}
}

View File

@@ -1,19 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Text; using System.Text;
using System.Diagnostics;
namespace SVM.Instructions 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) protected override void Run(VM vm, byte reg)
{ {
vm.RI = vm.R[reg]; vm.R[reg] = (ushort)~vm.R[reg];
} }
} }
} }

17
SVM/Instructions/OR.cs Normal file
View File

@@ -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);
}
}
}

View File

@@ -28,7 +28,7 @@ namespace SVM.Instructions
public override string ToASM(byte[] vars) public override string ToASM(byte[] vars)
{ {
throw new NotImplementedException(); return ASM;
} }
} }
} }

View File

@@ -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];
}
}
}

60
SVM/Instructions/SHL.cs Normal file
View File

@@ -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<string, ushort> 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);
}
}
}

18
SVM/Instructions/SHR.cs Normal file
View File

@@ -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);
}
}
}

18
SVM/Instructions/XOR.cs Normal file
View File

@@ -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);
}
}
}

View File

@@ -9,12 +9,16 @@ namespace SVM
{ {
public const byte PORT_CODE = 0x0; public const byte PORT_CODE = 0x0;
public const string PORT_ASM = "P"; public const string PORT_ASM = "P";
public const byte REG_CODE = 0x1; public const byte REGISTER_CODE = 0x1;
public const string REG_ASM = "R"; public const string REGISTER_ASM = "R";
public const byte MEM_CODE = 0x2; public const byte MEMORY_CODE = 0x2;
public const string MEM_ASM = "M"; public const string MEMORY_ASM = "M";
public const byte LITERAL_CODE = 0x3; public const byte IMMEDIATE_CODE = 0x3;
public const string LITERAL_ASM = "L"; 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; public const int SIZE = 3;
@@ -23,9 +27,11 @@ namespace SVM
switch (type) switch (type)
{ {
case PORT_ASM: return PORT_CODE; case PORT_ASM: return PORT_CODE;
case REG_ASM: return REG_CODE; case REGISTER_ASM: return REGISTER_CODE;
case MEM_ASM: return MEM_CODE; case MEMORY_ASM: return MEMORY_CODE;
case LITERAL_ASM: return LITERAL_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"); default: throw new Exception("Invalid location type");
} }
} }
@@ -34,38 +40,63 @@ namespace SVM
switch (type) switch (type)
{ {
case PORT_CODE: return PORT_ASM; case PORT_CODE: return PORT_ASM;
case REG_CODE: return REG_ASM; case REGISTER_CODE: return REGISTER_ASM;
case MEM_CODE: return MEM_ASM; case MEMORY_CODE: return MEMORY_ASM;
case LITERAL_CODE: return LITERAL_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"); default: throw new Exception("Invalid location type");
} }
} }
public static Location FromASM(string asm) public static Location FromASM(string asm)
{ {
var parts = asm.Split(" ", 2); Debug.Assert(asm.Length > 1);
Debug.Assert(parts.Length > 0); asm = asm.Replace(" ", "");
byte type = EncodeType(parts[0]); byte type = EncodeType(asm[0].ToString());
ushort loc = 0; ushort loc = 0;
Debug.Assert(parts.Length == 2); string locVal = asm.Substring(1);
string locVal = parts[1]; switch(type)
if (locVal.StartsWith('\'') || locVal.StartsWith('"'))
{ {
Debug.Assert(locVal.Length > 1); case PORT_CODE:
loc = (byte)locVal[1]; case FLAG_CODE:
} loc = byte.Parse(locVal);
else if (locVal.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) break;
{ case ADDRESS_CODE:
Debug.Assert(locVal.Length > 2); case REGISTER_CODE:
loc = ushort.Parse(locVal.Substring(2), System.Globalization.NumberStyles.HexNumber); loc = Register.FromASM(locVal);
} break;
else case MEMORY_CODE:
{ loc = ReadNumber(locVal, true);
loc = ushort.Parse(locVal); break;
case IMMEDIATE_CODE:
loc = ReadNumber(locVal, false);
break;
} }
return new Location(type, loc); 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) public static Location FromByteCode(byte[] data, int start)
{ {
Debug.Assert(data.Length > start); Debug.Assert(data.Length > start);
@@ -90,13 +121,20 @@ namespace SVM
switch (type) switch (type)
{ {
case PORT_CODE: case PORT_CODE:
Debug.Assert(loc <= VM.PORTS);
return vm.Ports[loc].Read(); return vm.Ports[loc].Read();
case REG_CODE: case REGISTER_CODE:
Debug.Assert(loc <= VM.REGISTERS);
return vm.R[loc]; return vm.R[loc];
case MEM_CODE: case MEMORY_CODE:
return vm.MEM[loc]; return vm.MEM[loc];
case LITERAL_CODE: case IMMEDIATE_CODE:
return loc; 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"); default: throw new Exception("Invalid location type");
} }
} }
@@ -105,16 +143,24 @@ namespace SVM
switch (type) switch (type)
{ {
case PORT_CODE: case PORT_CODE:
vm.Ports[loc].Write((byte)val); Debug.Assert(loc <= VM.PORTS);
vm.Ports[loc].Write((byte)(val & 0xFF));
break; break;
case REG_CODE: case REGISTER_CODE:
Debug.Assert(loc <= VM.REGISTERS);
vm.R[loc] = val; vm.R[loc] = val;
break; break;
case MEM_CODE: case MEMORY_CODE:
vm.MEM[loc] = (byte)val; vm.MEM[loc] = (byte)(val & 0xFF);
break; break;
case LITERAL_CODE: case IMMEDIATE_CODE:
throw new Exception("Invalid operation"); 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: default:
throw new Exception("Invalid location type"); throw new Exception("Invalid location type");
} }
@@ -122,14 +168,25 @@ namespace SVM
public byte[] Encode() public byte[] Encode()
{ {
byte ub = (byte)((loc & 0xFF00) >> 8); return new byte[] { type, loc.HiByte(), loc.LoByte() };
byte lb = (byte)(loc & 0xFF);
return new byte[] { type, ub, lb };
} }
public string ToASM() 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);
}
} }
} }
} }

View File

@@ -1,29 +1,25 @@
ORIGIN 0 ORIGIN 0
JMP :PGM JMP :PGM
:WRITESTR :WRITESTR LOAD C RA
SETI A :WRITESTR_L LOAD A AC
:WRITESTR_LOOP JZ A :WRITESTR_R
LOADI A SAVE A P0
JZ A :WRITESTR_RET INC C
SAVE A P 0 JMP :WRITESTR_L
INCI :WRITESTR_R CLR C
JMP :WRITESTR_LOOP RET
:WRITESTR_RET
RET
:WRITENL :WRITENL LOAD A I13
LOAD A L 13 SAVE A P0
SAVE A P 0 LOAD A I10
LOAD A L 10 SAVE A P0
SAVE A P 0 RET
RET
:PGM :PGM LOAD A I0x100
LOAD A L 0x100 CALL :WRITESTR
CALL :WRITESTR CALL :WRITENL
CALL :WRITENL HALT
HALT
MEMORY MEMORY
0x100 "CALL/RET Test" 0x100 "CALL/RET Test"

View File

@@ -2,36 +2,33 @@
#Print string 5 times #Print string 5 times
CLR B CLR B
ADD B L 5 ADD B I5
ADD B L 48 ADD B I48
SAVE B P 0 SAVE B P0
SUB B L 48 SUB B I48
LOAD A L 0x300 #Set string start LOAD C I0x300 #Set string start
SETI A
JMP :PRINTA
:PRINTA #Print until zero :PRINTA #Print until zero
LOADI A LOAD A AC
JZ A :NEWLINE JZ A :NEWLINE
SAVE A P 0 SAVE A P0
INCI INC C
JMP :PRINTA JMP :PRINTA
:NEWLINE #New Line :NEWLINE #New Line
LOAD A L 13 LOAD A I13
SAVE A P 0 SAVE A P0
LOAD A L 10 LOAD A I10
SAVE A P 0 SAVE A P0
LOAD A L 0x300 #Set string start LOAD C I0x300 #Set string start
SETI A
DEC B #Check Counter and stop if 5 printed DEC B #Check Counter and stop if 5 printed
JZ B :STOP JZ B :STOP
ADD B L 48 ADD B I48
SAVE B P 0 SAVE B P0
SUB B L 48 SUB B I48
JMP :PRINTA JMP :PRINTA
:STOP :STOP

11
SVM/PGM/LOGIC.txt Normal file
View File

@@ -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

View File

@@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<None Remove="PGM\CALL.txt" /> <None Remove="PGM\CALL.txt" />
<None Remove="PGM\HELLO.txt" /> <None Remove="PGM\HELLO.txt" />
<None Remove="PGM\LOGIC.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -19,6 +20,9 @@
<Content Include="PGM\HELLO.txt"> <Content Include="PGM\HELLO.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="PGM\LOGIC.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -72,8 +72,8 @@ namespace SVM
var pc = PC; var pc = PC;
var instr = instructions[MEM[PC++]]; var instr = instructions[MEM[PC++]];
byte[] decoded = instr.Decode(this); byte[] decoded = instr.Decode(this);
//Console.WriteLine("A{0} B{1} C{2} D{3}", R[0], R[1], R[2], R[3]); 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.WriteLine("0x{0:X4} {1}", pc, instr.ToASM(decoded));
instr.Exec(this, decoded); instr.Exec(this, decoded);
//Console.ReadKey(true); //Console.ReadKey(true);
} }