Implemeted Flag support and flag CONSTS.
Implmeneted ALIAS assembler instruction, JBC & JBS instructions.
This commit is contained in:
12
FLAGS.md
12
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
|
||||
<!--
|
||||
### Name | ALIAS | 0x00
|
||||
|
||||
| | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
| | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||
| --------------- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| **Read/Write** | | | | | | | | |
|
||||
| **Defaults** | | | | | | | | |
|
||||
| **Default ** | | | | | | | | |
|
||||
| **Name** | | | | | | | | |
|
||||
-->
|
||||
7
SPEC.md
7
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
|
||||
|
||||
|
||||
@@ -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<string, ushort> markers = new Dictionary<string, ushort>();
|
||||
Dictionary<string, List<ushort>> markerUses = new Dictionary<string, List<ushort>>();
|
||||
Dictionary<string, string> aliases = new Dictionary<string, string>();
|
||||
|
||||
//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);
|
||||
|
||||
|
||||
33
SVM/Flag.cs
Normal file
33
SVM/Flag.cs
Normal file
@@ -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<Flag>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
51
SVM/Flags/CONSTS.cs
Normal file
51
SVM/Flags/CONSTS.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
83
SVM/Instructions/JBC.cs
Normal file
83
SVM/Instructions/JBC.cs
Normal file
@@ -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<string, ushort> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
SVM/Instructions/JBS.cs
Normal file
19
SVM/Instructions/JBS.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
52
SVM/PGM/FlagTest.txt
Normal file
52
SVM/PGM/FlagTest.txt
Normal file
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="PGM\CALL.txt" />
|
||||
<None Remove="PGM\FlagTest.txt" />
|
||||
<None Remove="PGM\HELLO.txt" />
|
||||
<None Remove="PGM\LOGIC.txt" />
|
||||
</ItemGroup>
|
||||
@@ -17,6 +18,9 @@
|
||||
<Content Include="PGM\CALL.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="PGM\FlagTest.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="PGM\HELLO.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
43
SVM/VM.cs
43
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<byte, Instruction> instructions = new Dictionary<byte, Instruction>();
|
||||
private Dictionary<byte, Flag> flags = new Dictionary<byte, Flag>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user