Add an assembler, a section on the assembly language on the readme, example assembly files, and proper error handling to the emulator and disassembler
This commit is contained in:
parent
bbb2a1a695
commit
c4c41274f9
|
@ -1,6 +1,8 @@
|
||||||
/*
|
/*
|
||||||
!/disassembler.pas
|
!/*.pas
|
||||||
!/emulator.pas
|
!/examples/
|
||||||
|
/examples/*
|
||||||
|
!/examples/*.asm
|
||||||
!/.gitignore
|
!/.gitignore
|
||||||
!/license.md
|
!/license.md
|
||||||
!/readme.md
|
!/readme.md
|
||||||
|
|
|
@ -0,0 +1,355 @@
|
||||||
|
program Assembler;
|
||||||
|
|
||||||
|
{$MODE OBJFPC}
|
||||||
|
|
||||||
|
uses Sysutils, Strutils;
|
||||||
|
|
||||||
|
type
|
||||||
|
//Label or reference table entry
|
||||||
|
LblRec = record
|
||||||
|
Addr: word;
|
||||||
|
Lbl: string;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
LP, Count: integer; //Line pointer and generic counter
|
||||||
|
Line, DatOrg: string; //Line of assembly and data or org element
|
||||||
|
Elem: array [0 .. 4] of string; //Parsed elements
|
||||||
|
Lbl, Ref: array [0 .. $ffff] of LblRec; //Label and reference tables
|
||||||
|
Addr, BP, SP, EP: word; //Address argument and byte, start, and end pointers
|
||||||
|
Bin: array [0 .. $ffef] of byte; //Assembled binary
|
||||||
|
Prog: file of byte; //Program file
|
||||||
|
|
||||||
|
//Print an argument error and abort
|
||||||
|
procedure ArgError;
|
||||||
|
begin
|
||||||
|
writeln ('Error (line ', LP, '): incorrect argument(s)');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Print a memory error and abort if the assembler is about to write beyond available memory
|
||||||
|
procedure MemError;
|
||||||
|
begin
|
||||||
|
if (BP + 2) >= $fff0 then begin
|
||||||
|
writeln ('Error (line ', LP, '): out of memory');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Assemble a first or only argument that is a register
|
||||||
|
procedure FirstArgReg;
|
||||||
|
begin
|
||||||
|
if CompareText (Elem [2], 'R0') = 0 then Bin [BP] := Bin [BP] + 0
|
||||||
|
else if CompareText (Elem [2], 'R1') = 0 then Bin [BP] := Bin [BP] + 4
|
||||||
|
else if CompareText (Elem [2], 'R2') = 0 then Bin [BP] := Bin [BP] + 8
|
||||||
|
else if CompareText (Elem [2], 'R3') = 0 then Bin [BP] := Bin [BP] + $c
|
||||||
|
else ArgError;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Assemble two register arguments
|
||||||
|
procedure TwoArgRegs;
|
||||||
|
begin
|
||||||
|
//First argument
|
||||||
|
FirstArgReg;
|
||||||
|
//Second argument
|
||||||
|
if CompareText (Elem [3], 'R0') = 0 then Bin [BP] := Bin [BP] + 0
|
||||||
|
else if CompareText (Elem [3], 'R1') = 0 then Bin [BP] := Bin [BP] + 1
|
||||||
|
else if CompareText (Elem [3], 'R2') = 0 then Bin [BP] := Bin [BP] + 2
|
||||||
|
else if CompareText (Elem [3], 'R3') = 0 then Bin [BP] := Bin [BP] + 3
|
||||||
|
else ArgError;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
//Check for an set up a program file
|
||||||
|
if ParamCount <> 1 then begin
|
||||||
|
writeln ('Usage: assembler program (< input)');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Initialise the byte and start pointers
|
||||||
|
LP := 1;
|
||||||
|
BP := 0;
|
||||||
|
SP := 0;
|
||||||
|
|
||||||
|
//Begin the main loop
|
||||||
|
repeat
|
||||||
|
|
||||||
|
//Read a line
|
||||||
|
readln (Line);
|
||||||
|
|
||||||
|
//Remove the comment if any
|
||||||
|
Line := Trim (ExtractDelimited (1, Line, [';']));
|
||||||
|
|
||||||
|
//Check for an empty line
|
||||||
|
if Line <> '' then begin
|
||||||
|
|
||||||
|
//(Re-)initialise the elements
|
||||||
|
Elem [0] := '';
|
||||||
|
Elem [1] := '';
|
||||||
|
Elem [2] := '';
|
||||||
|
Elem [3] := '';
|
||||||
|
Elem [4] := '';
|
||||||
|
|
||||||
|
//Check if the first element is a label
|
||||||
|
if RightStr (Trim (ExtractWord (1, Line, [' ', ' '])), 1) = ':' then begin
|
||||||
|
//Find the first empty slot in the label table
|
||||||
|
Count := 0;
|
||||||
|
while Lbl [Count] .Lbl <> '' do Count := Count + 1;
|
||||||
|
//Extract the label
|
||||||
|
Lbl [Count] .Addr := BP;
|
||||||
|
Elem [0] := Trim (ExtractDelimited (1, Line, [':']));
|
||||||
|
Lbl [Count] .Lbl := Elem [0];
|
||||||
|
Line := Trim (ExtractDelimited (2, Line, [':']));
|
||||||
|
//Check for forward references
|
||||||
|
Count := 0;
|
||||||
|
repeat
|
||||||
|
while CompareText (Elem [0], Ref [Count] .Lbl) <> 0 do begin
|
||||||
|
if Ref [Count] .Lbl = '' then break;
|
||||||
|
Count := Count + 1;
|
||||||
|
end;
|
||||||
|
if Ref [Count] .Lbl <> '' then begin
|
||||||
|
Bin [Ref [Count] .Addr] := BP shr 8;
|
||||||
|
Bin [Ref [Count] .Addr + 1] := BP and $00ff;
|
||||||
|
Count := Count + 1;
|
||||||
|
end;
|
||||||
|
until Ref [Count] .Lbl = '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Check for the org pseudo-instruction
|
||||||
|
if CompareText (Trim (ExtractWord (1, Line, [' ', ' '])), 'ORG') = 0 then begin
|
||||||
|
if BP = 0 then begin
|
||||||
|
if Elem [0] = '' then begin
|
||||||
|
//Set the starting point
|
||||||
|
if Trim (ExtractWord (3, Line, [' ', ' '])) <> '' then ArgError;
|
||||||
|
DatOrg := Trim (ExtractWord (2, Line, [' ', ' ']));
|
||||||
|
try
|
||||||
|
if Hex2Dec (DatOrg) <=$ffff then begin
|
||||||
|
BP := Hex2Dec (DatOrg);
|
||||||
|
SP := BP;
|
||||||
|
end
|
||||||
|
else ArgError;
|
||||||
|
except
|
||||||
|
ArgError;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
writeln ('Error (line ', LP, '): ORG cannot have a label');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
writeln ('Error (line ', LP, '): ORG must be the first instruction');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
|
||||||
|
//Check for the data pseudo-instruction
|
||||||
|
else if CompareText (Trim (ExtractWord (1, Line, [' ', ' '])), 'DATA') = 0 then begin
|
||||||
|
//Extract and store the data
|
||||||
|
if Trim (ExtractWord (3, Line, [' ', ' '])) <> '' then ArgError;
|
||||||
|
DatOrg := Trim (ExtractWord (2, Line, [' ', ' ']));
|
||||||
|
try
|
||||||
|
if Hex2Dec (DatOrg) <=$ff then Bin [BP] := Hex2Dec (DatOrg)
|
||||||
|
else ArgError;
|
||||||
|
except
|
||||||
|
ArgError;
|
||||||
|
end;
|
||||||
|
//Increment the byte pointer
|
||||||
|
BP := BP + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
//Check for an instruction
|
||||||
|
else if Line <> '' then begin
|
||||||
|
|
||||||
|
//Parse the instruction
|
||||||
|
//Extract the opcode
|
||||||
|
Elem [1] := Trim (ExtractWord (1, Line, [' ', ' ']));
|
||||||
|
//Extract the arguments
|
||||||
|
Elem [2] := Trim (ExtractWord (2, Trim (ExtractDelimited (1, Line, [','])), [' ', ' ']));
|
||||||
|
Elem [3] := Trim (ExtractDelimited (2, Line, [',']));
|
||||||
|
Elem [4] := Trim (ExtractDelimited (3, Line, [',']));
|
||||||
|
|
||||||
|
//Assemble the opcode
|
||||||
|
if CompareText (Elem [1], 'HALT') = 0 then Bin [BP] := 0
|
||||||
|
else if CompareText (Elem [1], 'RET') = 0 then Bin [BP] := $10
|
||||||
|
else if CompareText (Elem [1], 'SHL') = 0 then Bin [BP] := $20
|
||||||
|
else if CompareText (Elem [1], 'SHR') = 0 then Bin [BP] := $30
|
||||||
|
else if CompareText (Elem [1], 'ROL') = 0 then Bin [BP] := $40
|
||||||
|
else if CompareText (Elem [1], 'ROR') = 0 then Bin [BP] := $50
|
||||||
|
else if CompareText (Elem [1], 'NAND') = 0 then Bin [BP] := $60
|
||||||
|
else if CompareText (Elem [1], 'AND') = 0 then Bin [BP] := $70
|
||||||
|
else if CompareText (Elem [1], 'OR') = 0 then Bin [BP] := $80
|
||||||
|
else if CompareText (Elem [1], 'XOR') = 0 then Bin [BP] := $90
|
||||||
|
else if CompareText (Elem [1], 'LOAD') = 0 then Bin [BP] := $a0
|
||||||
|
else if CompareText (Elem [1], 'STORE') = 0 then Bin [BP] := $b0
|
||||||
|
else if CompareText (Elem [1], 'BREQ') = 0 then Bin [BP] := $c0
|
||||||
|
else if CompareText (Elem [1], 'BRNEQ') = 0 then Bin [BP] := $d0
|
||||||
|
else if CompareText (Elem [1], 'CLEQ') = 0 then Bin [BP] := $e0
|
||||||
|
else if CompareText (Elem [1], 'CLNEQ') = 0 then Bin [BP] := $f0
|
||||||
|
else begin
|
||||||
|
writeln ('Error (line ', LP, '): no such instruction');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Check for incorrect number of arguments
|
||||||
|
if Bin [BP] <= $10 then begin
|
||||||
|
if Elem [2] <> '' then ArgError
|
||||||
|
else if Elem [3] <> '' then ArgError
|
||||||
|
else if Elem [4] <> '' then ArgError;
|
||||||
|
end
|
||||||
|
else if Bin [BP] <= $50 then begin
|
||||||
|
if Elem [3] <> '' then ArgError
|
||||||
|
else if Elem [4] <> '' then ArgError;
|
||||||
|
end
|
||||||
|
else if Bin [BP] <= $b0 then begin
|
||||||
|
if Elem [4] <> '' then ArgError;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Assemble the arguments
|
||||||
|
//Shifts
|
||||||
|
if Bin [BP] >= $20 then if Bin [BP] <= $50 then FirstArgReg;
|
||||||
|
//Logical operations
|
||||||
|
if Bin [BP] >= $60 then if Bin [BP] <= $90 then TwoArgRegs;
|
||||||
|
//Load
|
||||||
|
if Bin [BP] = $a0 then begin
|
||||||
|
//First argument
|
||||||
|
FirstArgReg;
|
||||||
|
//Second argument
|
||||||
|
try
|
||||||
|
//Address
|
||||||
|
if Hex2Dec (Elem [3]) <= $ffff then Addr := Hex2Dec (Elem [3])
|
||||||
|
else ArgError;
|
||||||
|
except
|
||||||
|
//Label reference
|
||||||
|
//Backwards
|
||||||
|
Count := 0;
|
||||||
|
while CompareText (Elem [3], Lbl [Count] .Lbl) <> 0 do begin
|
||||||
|
if Lbl [Count] .Lbl = '' then break;
|
||||||
|
Count := Count + 1;
|
||||||
|
end;
|
||||||
|
if Lbl [Count] .Lbl <> '' then Addr := Lbl [Count] .Addr
|
||||||
|
//Forwards
|
||||||
|
else begin
|
||||||
|
//Find the first empty slot in the reference table
|
||||||
|
Count := 0;
|
||||||
|
while Ref [Count] .Lbl <> '' do Count := Count + 1;
|
||||||
|
//Extract the reference
|
||||||
|
Ref [Count] .Addr := BP + 1;
|
||||||
|
Ref [Count] .Lbl := Elem [3];
|
||||||
|
//Placeholder
|
||||||
|
Addr := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
MemError;
|
||||||
|
Bin [BP + 1] := Addr shr 8;
|
||||||
|
Bin [BP + 2] := Addr and $00ff;
|
||||||
|
end;
|
||||||
|
//Store
|
||||||
|
if Bin [BP] = $b0 then begin
|
||||||
|
//First argument
|
||||||
|
try
|
||||||
|
//Address
|
||||||
|
if Hex2Dec (Elem [2]) <= $ffff then Addr := Hex2Dec (Elem [2])
|
||||||
|
else ArgError;
|
||||||
|
except
|
||||||
|
//Label reference
|
||||||
|
//Backwards
|
||||||
|
Count := 0;
|
||||||
|
while CompareText (Elem [2], Lbl [Count] .Lbl) <> 0 do begin
|
||||||
|
if Lbl [Count] .Lbl = '' then break;
|
||||||
|
Count := Count + 1;
|
||||||
|
end;
|
||||||
|
if Lbl [Count] .Lbl <> '' then Addr := Lbl [Count] .Addr
|
||||||
|
//Forwards
|
||||||
|
else begin
|
||||||
|
//Find the first empty slot in the reference table
|
||||||
|
Count := 0;
|
||||||
|
while Ref [Count] .Lbl <> '' do Count := Count + 1;
|
||||||
|
//Extract the reference
|
||||||
|
Ref [Count] .Addr := BP + 1;
|
||||||
|
Ref [Count] .Lbl := Elem [2];
|
||||||
|
//Placeholder
|
||||||
|
Addr := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
MemError;
|
||||||
|
Bin [BP + 1] := Addr shr 8;
|
||||||
|
Bin [BP + 2] := Addr and $00ff;
|
||||||
|
//Second argument
|
||||||
|
if CompareText (Elem [3], 'R0') = 0 then Bin [BP] := Bin [BP] + 0
|
||||||
|
else if CompareText (Elem [3], 'R1') = 0 then Bin [BP] := Bin [BP] + 4
|
||||||
|
else if CompareText (Elem [3], 'R2') = 0 then Bin [BP] := Bin [BP] + 8
|
||||||
|
else if CompareText (Elem [3], 'R3') = 0 then Bin [BP] := Bin [BP] + $c
|
||||||
|
else ArgError;
|
||||||
|
end;
|
||||||
|
//Branches and calls
|
||||||
|
if Bin [BP] >= $c0 then begin
|
||||||
|
//First and second arguments
|
||||||
|
TwoArgRegs;
|
||||||
|
//Third argument
|
||||||
|
try
|
||||||
|
//Address
|
||||||
|
if Hex2Dec (Elem [4]) <= $ffff then Addr := Hex2Dec (Elem [4])
|
||||||
|
else ArgError;
|
||||||
|
except
|
||||||
|
//Label reference
|
||||||
|
//Backwards
|
||||||
|
Count := 0;
|
||||||
|
while CompareText (Elem [4], Lbl [Count] .Lbl) <> 0 do begin
|
||||||
|
if Lbl [Count] .Lbl = '' then break;
|
||||||
|
Count := Count + 1;
|
||||||
|
end;
|
||||||
|
if Lbl [Count] .Lbl <> '' then Addr := Lbl [Count] .Addr
|
||||||
|
//Forwards
|
||||||
|
else begin
|
||||||
|
//Find the first empty slot in the reference table
|
||||||
|
Count := 0;
|
||||||
|
while Ref [Count] .Lbl <> '' do Count := Count + 1;
|
||||||
|
//Extract the reference
|
||||||
|
Ref [Count] .Addr := BP + 1;
|
||||||
|
Ref [Count] .Lbl := Elem [4];
|
||||||
|
//Placeholder
|
||||||
|
Addr := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
MemError;
|
||||||
|
Bin [BP + 1] := Addr shr 8;
|
||||||
|
Bin [BP + 2] := Addr and $00ff;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Increment the byte pointer
|
||||||
|
if Bin [BP] >= $a0 then BP := BP + 3
|
||||||
|
else BP := BP + 1;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Increment the line pointer
|
||||||
|
LP := LP + 1;
|
||||||
|
|
||||||
|
//A hack for fixing an unexplained extra readln after the loop that does nothing
|
||||||
|
if BP = $fff0 then break;
|
||||||
|
|
||||||
|
until eof ();
|
||||||
|
|
||||||
|
//Set the end pointer and reset the byte pointer to the start of the program
|
||||||
|
EP := BP;
|
||||||
|
BP := SP;
|
||||||
|
|
||||||
|
//Write the program file
|
||||||
|
{$i-}
|
||||||
|
assign (Prog, ParamStr (1));
|
||||||
|
rewrite (Prog);
|
||||||
|
{$i+}
|
||||||
|
if IOResult <> 0 then begin
|
||||||
|
writeln ('Error: program file cannot be written to');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
|
repeat
|
||||||
|
write (Prog, Bin [BP]);
|
||||||
|
BP := BP + 1;
|
||||||
|
until BP = EP;
|
||||||
|
|
||||||
|
end.
|
|
@ -35,23 +35,23 @@ begin
|
||||||
|
|
||||||
//Read a program file and check for errors
|
//Read a program file and check for errors
|
||||||
if ParamCount <> 1 then begin
|
if ParamCount <> 1 then begin
|
||||||
writeln ('Usage: disassembler program');
|
writeln ('Usage: disassembler program (> output)');
|
||||||
exit;
|
halt;
|
||||||
end;
|
end;
|
||||||
{$i-}
|
{$i-}
|
||||||
assign (Prog, ParamStr (1));
|
assign (Prog, ParamStr (1));
|
||||||
reset (Prog);
|
reset (Prog);
|
||||||
{$i+}
|
{$i+}
|
||||||
if IOResult <> 0 then begin
|
if IOResult <> 0 then begin
|
||||||
writeln ('Usage: disassembler program');
|
writeln ('Error: program file cannot be read from');
|
||||||
exit;
|
halt;
|
||||||
end;
|
end;
|
||||||
repeat
|
repeat
|
||||||
read (Prog, Bin [BP]);
|
read (Prog, Bin [BP]);
|
||||||
BP := BP + 1;
|
BP := BP + 1;
|
||||||
until (eof (Prog)) or (BP = $fff0);
|
until (eof (Prog)) or (BP = $fff0);
|
||||||
|
|
||||||
//Save the end point and reinitialise the instruction pointer
|
//Save the end point and reinitialise the byte pointer
|
||||||
EP := BP;
|
EP := BP;
|
||||||
BP := 0;
|
BP := 0;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ begin
|
||||||
if BP < $10 then write (' ');
|
if BP < $10 then write (' ');
|
||||||
write (IntToHex (BP, 1), ' ');
|
write (IntToHex (BP, 1), ' ');
|
||||||
|
|
||||||
//Fetch the instruction and increment the instruction pointer
|
//Fetch the instruction and increment the byte pointer
|
||||||
//Opcode
|
//Opcode
|
||||||
Op := Bin [BP] and $f0 shr 4;
|
Op := Bin [BP] and $f0 shr 4;
|
||||||
//Register arguments
|
//Register arguments
|
||||||
|
@ -97,6 +97,6 @@ begin
|
||||||
writeln ();
|
writeln ();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
until (BP >= EP);
|
until BP >= EP;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
70
emulator.pas
70
emulator.pas
|
@ -6,7 +6,7 @@ const
|
||||||
IO = $ffff;
|
IO = $ffff;
|
||||||
|
|
||||||
var
|
var
|
||||||
Halt: boolean; //Halt flag
|
Hlt: boolean; //Halt flag
|
||||||
Op: 0 .. $f; //Opcode
|
Op: 0 .. $f; //Opcode
|
||||||
X, Y: 0 .. 3; //Register arguments
|
X, Y: 0 .. 3; //Register arguments
|
||||||
Addr, IP, RP: word; //Address argument and instruction and return pointers
|
Addr, IP, RP: word; //Address argument and instruction and return pointers
|
||||||
|
@ -18,22 +18,22 @@ var
|
||||||
begin
|
begin
|
||||||
|
|
||||||
//Initialise the halt flag and the pointers
|
//Initialise the halt flag and the pointers
|
||||||
Halt := false;
|
Hlt := false;
|
||||||
IP := 0;
|
IP := 0;
|
||||||
RP := $fff0;
|
RP := $fff0;
|
||||||
|
|
||||||
//Read a program file and check for errors
|
//Read a program file and check for errors
|
||||||
if ParamCount <> 1 then begin
|
if ParamCount <> 1 then begin
|
||||||
writeln ('Usage: emulator program');
|
writeln ('Usage: emulator program');
|
||||||
exit;
|
halt;
|
||||||
end;
|
end;
|
||||||
{$i-}
|
{$i-}
|
||||||
assign (Prog, ParamStr (1));
|
assign (Prog, ParamStr (1));
|
||||||
reset (Prog);
|
reset (Prog);
|
||||||
{$i+}
|
{$i+}
|
||||||
if IOResult <> 0 then begin
|
if IOResult <> 0 then begin
|
||||||
writeln ('Usage: emulator program');
|
writeln ('Error: program file cannot be read from');
|
||||||
exit;
|
halt;
|
||||||
end;
|
end;
|
||||||
repeat
|
repeat
|
||||||
read (Prog, Mem [IP]);
|
read (Prog, Mem [IP]);
|
||||||
|
@ -44,7 +44,7 @@ begin
|
||||||
IP := 0;
|
IP := 0;
|
||||||
|
|
||||||
//Begin the main loop
|
//Begin the main loop
|
||||||
while (Halt = false) do begin
|
while Hlt = false do begin
|
||||||
|
|
||||||
//Fetch the instruction and increment the instruction pointer
|
//Fetch the instruction and increment the instruction pointer
|
||||||
//Opcode
|
//Opcode
|
||||||
|
@ -53,34 +53,49 @@ begin
|
||||||
X := Mem [IP] and $c shr 2;
|
X := Mem [IP] and $c shr 2;
|
||||||
Y := Mem [IP] and 3;
|
Y := Mem [IP] and 3;
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then break;
|
if IP > $ffef then begin
|
||||||
|
writeln ('Error: illegal instruction pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
//Address argument
|
//Address argument
|
||||||
if Op >= $a then begin
|
if Op >= $a then begin
|
||||||
//High byte
|
//High byte
|
||||||
Addr := Mem [IP];
|
Addr := Mem [IP];
|
||||||
Addr := Addr shl 8;
|
Addr := Addr shl 8;
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then break;
|
if IP > $ffef then begin
|
||||||
|
writeln ('Error: illegal instruction pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
//Low byte
|
//Low byte
|
||||||
Addr := Addr + Mem [IP];
|
Addr := Addr + Mem [IP];
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then break;
|
if IP > $ffef then begin
|
||||||
|
writeln ('Error: illegal instruction pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//Decode and execute the instruction
|
//Decode and execute the instruction
|
||||||
//Halt
|
//Halt
|
||||||
if Op = 0 then Halt := true
|
if Op = 0 then Hlt := true
|
||||||
//Ret
|
//Ret
|
||||||
else if Op = 1 then begin
|
else if Op = 1 then begin
|
||||||
//High byte of the return address
|
//High byte of the return address
|
||||||
IP := Mem [RP];
|
IP := Mem [RP];
|
||||||
IP := IP shl 8;
|
IP := IP shl 8;
|
||||||
RP := RP + 1;
|
RP := RP + 1;
|
||||||
if RP > $fff0 then break;
|
if RP > $fff0 then begin
|
||||||
|
writeln ('Error: illegal return pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
//Low byte of the return address
|
//Low byte of the return address
|
||||||
IP := IP + Mem [RP];
|
IP := IP + Mem [RP];
|
||||||
RP := RP + 1;
|
RP := RP + 1;
|
||||||
if RP > $fff0 then break;
|
if RP > $fff0 then begin
|
||||||
|
writeln ('Error: illegal return pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
//Shl
|
//Shl
|
||||||
else if Op = 2 then R [X] := R [X] shl 1
|
else if Op = 2 then R [X] := R [X] shl 1
|
||||||
|
@ -132,15 +147,24 @@ begin
|
||||||
if R [X] = R [Y] then begin
|
if R [X] = R [Y] then begin
|
||||||
//Low byte of the return address
|
//Low byte of the return address
|
||||||
RP := RP - 1;
|
RP := RP - 1;
|
||||||
if RP > $fff0 then break;
|
if RP > $fff0 then begin
|
||||||
|
writeln ('Error: illegal return pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
Mem [RP] := IP and $ff;
|
Mem [RP] := IP and $ff;
|
||||||
//High byte of the return address
|
//High byte of the return address
|
||||||
RP := RP - 1;
|
RP := RP - 1;
|
||||||
if RP > $fff0 then break;
|
if RP > $fff0 then begin
|
||||||
|
writeln ('Error: illegal return pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
Mem [RP] := IP shr 8;
|
Mem [RP] := IP shr 8;
|
||||||
//Call
|
//Call
|
||||||
IP := Addr;
|
IP := Addr;
|
||||||
if IP > $ffef then break;
|
if IP > $ffef then begin
|
||||||
|
writeln ('Error: illegal instruction pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
//Clneq
|
//Clneq
|
||||||
|
@ -148,19 +172,27 @@ begin
|
||||||
if R [X] <> R [Y] then begin
|
if R [X] <> R [Y] then begin
|
||||||
//Low byte of the return address
|
//Low byte of the return address
|
||||||
RP := RP - 1;
|
RP := RP - 1;
|
||||||
if RP > $fff0 then break;
|
if RP > $fff0 then begin
|
||||||
|
writeln ('Error: illegal return pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
Mem [RP] := IP and $ff;
|
Mem [RP] := IP and $ff;
|
||||||
//High byte of the return address
|
//High byte of the return address
|
||||||
RP := RP - 1;
|
RP := RP - 1;
|
||||||
if RP > $fff0 then break;
|
if RP > $fff0 then begin
|
||||||
|
writeln ('Error: illegal return pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
Mem [RP] := IP shr 8;
|
Mem [RP] := IP shr 8;
|
||||||
//Call
|
//Call
|
||||||
IP := Addr;
|
IP := Addr;
|
||||||
if IP > $ffef then break;
|
if IP > $ffef then begin
|
||||||
|
writeln ('Error: illegal instruction pointer value');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//End the main loop
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
;ASCII code printer
|
||||||
|
|
||||||
|
;Print a prompt
|
||||||
|
load r0, prompt
|
||||||
|
store ffff, r0
|
||||||
|
|
||||||
|
;Read a character to r0 and load it to r2
|
||||||
|
load r0, ffff
|
||||||
|
cleq r0, r0, newln
|
||||||
|
xor r2, r2
|
||||||
|
xor r2, r0
|
||||||
|
|
||||||
|
;Get the hexadecimal digit of the high nibble
|
||||||
|
ror r0
|
||||||
|
ror r0
|
||||||
|
ror r0
|
||||||
|
ror r0
|
||||||
|
cleq r0, r0, n2hex
|
||||||
|
|
||||||
|
;Print the digit
|
||||||
|
store ffff, r0
|
||||||
|
|
||||||
|
;Re-load the character to r0
|
||||||
|
xor r0, r0
|
||||||
|
xor r0, r2
|
||||||
|
|
||||||
|
;Get the hexadecimal digit of the low nibble
|
||||||
|
cleq r0, r0, n2hex
|
||||||
|
|
||||||
|
;Print the digit
|
||||||
|
store ffff, r0
|
||||||
|
cleq r0, r0, newln
|
||||||
|
|
||||||
|
;Halt
|
||||||
|
halt
|
||||||
|
|
||||||
|
;Print a newline
|
||||||
|
newln: load r1, cr
|
||||||
|
store ffff, r1
|
||||||
|
load r1, lf
|
||||||
|
store ffff, r1
|
||||||
|
ret
|
||||||
|
|
||||||
|
;Get the hexadecimal digit of a nibble
|
||||||
|
;Extract the low nibble
|
||||||
|
n2hex: load r1, mask
|
||||||
|
and r0, r1
|
||||||
|
;Locate the nibble in the table
|
||||||
|
load r1, tbl00
|
||||||
|
breq r0, r1, dgt0
|
||||||
|
load r1, tbl02
|
||||||
|
breq r0, r1, dgt1
|
||||||
|
load r1, tbl04
|
||||||
|
breq r0, r1, dgt2
|
||||||
|
load r1, tbl06
|
||||||
|
breq r0, r1, dgt3
|
||||||
|
load r1, tbl08
|
||||||
|
breq r0, r1, dgt4
|
||||||
|
load r1, tbl0a
|
||||||
|
breq r0, r1, dgt5
|
||||||
|
load r1, tbl0c
|
||||||
|
breq r0, r1, dgt6
|
||||||
|
load r1, tbl0e
|
||||||
|
breq r0, r1, dgt7
|
||||||
|
load r1, tbl10
|
||||||
|
breq r0, r1, dgt8
|
||||||
|
load r1, tbl12
|
||||||
|
breq r0, r1, dgt9
|
||||||
|
load r1, tbl14
|
||||||
|
breq r0, r1, dgta
|
||||||
|
load r1, tbl16
|
||||||
|
breq r0, r1, dgtb
|
||||||
|
load r1, tbl18
|
||||||
|
breq r0, r1, dgtc
|
||||||
|
load r1, tbl1a
|
||||||
|
breq r0, r1, dgtd
|
||||||
|
load r1, tbl1c
|
||||||
|
breq r0, r1, dgte
|
||||||
|
load r1, tbl1e
|
||||||
|
breq r0, r1, dgtf
|
||||||
|
;Load the hexadecimal digit of the nibble
|
||||||
|
dgt0: load r0, tbl01
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt1: load r0, tbl03
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt2: load r0, tbl05
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt3: load r0, tbl07
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt4: load r0, tbl09
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt5: load r0, tbl0b
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt6: load r0, tbl0d
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt7: load r0, tbl0f
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt8: load r0, tbl11
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgt9: load r0, tbl13
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgta: load r0, tbl15
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgtb: load r0, tbl17
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgtc: load r0, tbl19
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgtd: load r0, tbl1b
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgte: load r0, tbl1d
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
dgtf: load r0, tbl1f
|
||||||
|
breq r0, r0, n2hend
|
||||||
|
;Return
|
||||||
|
n2hend: ret
|
||||||
|
|
||||||
|
;Characters
|
||||||
|
prompt: data 3e
|
||||||
|
cr: data d
|
||||||
|
lf: data a
|
||||||
|
|
||||||
|
;Mask
|
||||||
|
mask: data f
|
||||||
|
|
||||||
|
;Hexadecimal table
|
||||||
|
tbl00: data 0
|
||||||
|
tbl01: data 30
|
||||||
|
tbl02: data 1
|
||||||
|
tbl03: data 31
|
||||||
|
tbl04: data 2
|
||||||
|
tbl05: data 32
|
||||||
|
tbl06: data 3
|
||||||
|
tbl07: data 33
|
||||||
|
tbl08: data 4
|
||||||
|
tbl09: data 34
|
||||||
|
tbl0a: data 5
|
||||||
|
tbl0b: data 35
|
||||||
|
tbl0c: data 6
|
||||||
|
tbl0d: data 36
|
||||||
|
tbl0e: data 7
|
||||||
|
tbl0f: data 37
|
||||||
|
tbl10: data 8
|
||||||
|
tbl11: data 38
|
||||||
|
tbl12: data 9
|
||||||
|
tbl13: data 39
|
||||||
|
tbl14: data a
|
||||||
|
tbl15: data 41
|
||||||
|
tbl16: data b
|
||||||
|
tbl17: data 42
|
||||||
|
tbl18: data c
|
||||||
|
tbl19: data 43
|
||||||
|
tbl1a: data d
|
||||||
|
tbl1b: data 44
|
||||||
|
tbl1c: data e
|
||||||
|
tbl1d: data 45
|
||||||
|
tbl1e: data f
|
||||||
|
tbl1f: data 46
|
18
readme.md
18
readme.md
|
@ -49,6 +49,24 @@ D BRNEQ RX, RY, ADDR if (RX != RY) IP = ADDR
|
||||||
E CLEQ RX, RY, ADDR if (RX == RY) {RP -= 2; *RP = IP; IP = ADDR}
|
E CLEQ RX, RY, ADDR if (RX == RY) {RP -= 2; *RP = IP; IP = ADDR}
|
||||||
F CLNEQ RX, RY, ADDR if (RX != RY) {RP -= 2; *RP = IP; IP = ADDR}
|
F CLNEQ RX, RY, ADDR if (RX != RY) {RP -= 2; *RP = IP; IP = ADDR}
|
||||||
|
|
||||||
|
Assembly Language
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Lines of assembly are of the following form:
|
||||||
|
|
||||||
|
LABEL: OPER ARG1, ARG2, ARG3 ;Comment
|
||||||
|
|
||||||
|
The language is case-insensitive and uses hexadecimal numbers. A label
|
||||||
|
can consist of any alphanumeric characters as long as it is not
|
||||||
|
interpretable as a hexadecimal number. The label, instruction, and
|
||||||
|
comment elements are all optional, as is spacing between the arguments.
|
||||||
|
For the arguments of each instruction see the previous section.
|
||||||
|
|
||||||
|
In addition to the true instructions there are two pseudo-instructions.
|
||||||
|
ORG defines the starting address of the program: it can only occur as
|
||||||
|
the first instruction and cannot have a label, and is not required if
|
||||||
|
the starting address is 0. DATA introduces a byte of data.
|
||||||
|
|
||||||
Memory-Mapped Devices
|
Memory-Mapped Devices
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue