Remove limits on the pointer values, specify access to unused addresses and modify instruction fetching and calling to take into account unused addresses and memory mapped devices in the emulator, and write a separate section for the emulator in the readme
This commit is contained in:
parent
3eba71ab0e
commit
6d14b9a5bf
476
emulator.pas
476
emulator.pas
|
@ -16,6 +16,7 @@ type
|
||||||
|
|
||||||
const
|
const
|
||||||
IO = $ffff;
|
IO = $ffff;
|
||||||
|
LastRAM = $ffef;
|
||||||
|
|
||||||
var
|
var
|
||||||
Hlt, ASCII, Verbose: boolean; //Halt, ASCII, and verbose flags
|
Hlt, ASCII, Verbose: boolean; //Halt, ASCII, and verbose flags
|
||||||
|
@ -23,7 +24,7 @@ var
|
||||||
X, Y: 0 .. 3; //Register arguments
|
X, Y: 0 .. 3; //Register arguments
|
||||||
Addr, IP, RP: word; //Immediate or address argument and instruction and return pointers
|
Addr, IP, RP: word; //Immediate or address argument and instruction and return pointers
|
||||||
R: array [0 .. 3] of byte; //General-purpose registers
|
R: array [0 .. 3] of byte; //General-purpose registers
|
||||||
Mem: array [0 .. $ffef] of byte; //Memory
|
Mem: array [0 .. LastRAM] of byte; //Random access memory
|
||||||
Prog{$ifdef printer}, Prn{$endif}{$ifdef tape}, TapeIn, TapeOut{$endif}: file of byte; //Program file, line printer, and tape reader and punch tapes
|
Prog{$ifdef printer}, Prn{$endif}{$ifdef tape}, TapeIn, TapeOut{$endif}: file of byte; //Program file, line printer, and tape reader and punch tapes
|
||||||
{$ifdef tape}
|
{$ifdef tape}
|
||||||
Reader, Punch: Tape; //States of the tape reader and punch
|
Reader, Punch: Tape; //States of the tape reader and punch
|
||||||
|
@ -31,6 +32,7 @@ var
|
||||||
{$endif}
|
{$endif}
|
||||||
Ch, Scan: ansichar; //Character for input and output and scancode for non-ASCII keys
|
Ch, Scan: ansichar; //Character for input and output and scancode for non-ASCII keys
|
||||||
IC, LFX: integer; //Instruction counter for CPU speed
|
IC, LFX: integer; //Instruction counter for CPU speed
|
||||||
|
Fetched: byte; //Fetched byte
|
||||||
|
|
||||||
//Terminal output
|
//Terminal output
|
||||||
procedure Output;
|
procedure Output;
|
||||||
|
@ -60,12 +62,204 @@ begin
|
||||||
IC := 0;
|
IC := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
//Load a byte from memory
|
||||||
|
function LoadByte (W: word): byte;
|
||||||
|
var
|
||||||
|
B: byte;
|
||||||
|
begin
|
||||||
|
//Terminal input
|
||||||
|
if W = IO then begin
|
||||||
|
wait;
|
||||||
|
//Read a keypress
|
||||||
|
repeat
|
||||||
|
Ch := ReadKey;
|
||||||
|
//Check for non-ASCII keys
|
||||||
|
if Ch = ansichar (0) then begin
|
||||||
|
//Non-ASCII
|
||||||
|
if keypressed then begin
|
||||||
|
Scan := ReadKey;
|
||||||
|
//The delete key inserts the delete character
|
||||||
|
if Scan = ansichar ($53) then begin
|
||||||
|
Ch := ansichar ($7f);
|
||||||
|
ASCII := true;
|
||||||
|
end
|
||||||
|
//Unused function keys insert a null
|
||||||
|
else ASCII := true;
|
||||||
|
end
|
||||||
|
//Null
|
||||||
|
else ASCII := true;
|
||||||
|
end
|
||||||
|
//Other ASCII
|
||||||
|
else ASCII := true;
|
||||||
|
until ASCII = true;
|
||||||
|
//Bodge for the home and end keys
|
||||||
|
if Ch = ansichar ($37) then begin
|
||||||
|
if keypressed then begin
|
||||||
|
Scan := ReadKey;
|
||||||
|
Scan := ReadKey;
|
||||||
|
Scan := ReadKey;
|
||||||
|
Ch := ansichar (0);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Ch = ansichar ($38) then begin
|
||||||
|
if keypressed then begin
|
||||||
|
Scan := ReadKey;
|
||||||
|
Scan := ReadKey;
|
||||||
|
Scan := ReadKey;
|
||||||
|
Ch := ansichar (0);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Process the keypress
|
||||||
|
Output; //Local echo
|
||||||
|
B := byte (Ch);
|
||||||
|
end
|
||||||
|
//Tape reader
|
||||||
|
{$ifdef tape}
|
||||||
|
else if W = $fffd then begin
|
||||||
|
wait;
|
||||||
|
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
||||||
|
//Check the reader state
|
||||||
|
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
||||||
|
try
|
||||||
|
reset (State);
|
||||||
|
read (State, Reader);
|
||||||
|
read (State, Punch);
|
||||||
|
close (State);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Read
|
||||||
|
assign (TapeIn, Reader.Path);
|
||||||
|
try
|
||||||
|
reset (TapeIn);
|
||||||
|
seek (TapeIn, Reader.Pos);
|
||||||
|
read (TapeIn, B);
|
||||||
|
close (TapeIn);
|
||||||
|
Reader.Pos := Reader.Pos + 1;
|
||||||
|
except
|
||||||
|
B := $ff;
|
||||||
|
end;
|
||||||
|
//Save the reader state
|
||||||
|
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
||||||
|
try
|
||||||
|
rewrite (State);
|
||||||
|
write (State, Reader);
|
||||||
|
write (State, Punch);
|
||||||
|
close (State);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
|
//Unused addresses
|
||||||
|
else if W > LastRAM then B := 0
|
||||||
|
//Regular load
|
||||||
|
else B := Mem [W];
|
||||||
|
//Result
|
||||||
|
LoadByte := B;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure StoreByte (W: word; B: byte);
|
||||||
|
begin
|
||||||
|
//Terminal output
|
||||||
|
if W = IO then begin
|
||||||
|
wait;
|
||||||
|
Ch := ansichar (B);
|
||||||
|
Output;
|
||||||
|
end
|
||||||
|
//Printer
|
||||||
|
{$ifdef printer}
|
||||||
|
else if W = $fffe then begin
|
||||||
|
wait;
|
||||||
|
assign (Prn, '/dev/usb/lp0');
|
||||||
|
try
|
||||||
|
rewrite (Prn);
|
||||||
|
write (Prn, B);
|
||||||
|
close (Prn);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
|
//Tape punch
|
||||||
|
{$ifdef tape}
|
||||||
|
else if W = $fffd then begin
|
||||||
|
wait;
|
||||||
|
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
||||||
|
//Check the punch state
|
||||||
|
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
||||||
|
try
|
||||||
|
reset (State);
|
||||||
|
read (State, Reader);
|
||||||
|
read (State, Punch);
|
||||||
|
close (State);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Punch
|
||||||
|
if Punch.Path <> '' then begin
|
||||||
|
assign (TapeOut, Punch.Path);
|
||||||
|
if FileExists (Punch.Path) = false then begin
|
||||||
|
try
|
||||||
|
rewrite (TapeOut);
|
||||||
|
write (TapeOut, B);
|
||||||
|
close (TapeOut);
|
||||||
|
Punch.Reset := false;
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Punch.Reset then begin
|
||||||
|
try
|
||||||
|
rewrite (TapeOut);
|
||||||
|
write (TapeOut, B);
|
||||||
|
close (TapeOut);
|
||||||
|
Punch.Reset := false;
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
try
|
||||||
|
reset (TapeOut);
|
||||||
|
seek (TapeOut, FileSize (TapeOut));
|
||||||
|
write (TapeOut, B);
|
||||||
|
close (TapeOut);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Save the punch state
|
||||||
|
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
||||||
|
try
|
||||||
|
rewrite (State);
|
||||||
|
write (State, Reader);
|
||||||
|
write (State, Punch);
|
||||||
|
close (State);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
|
//Regular store
|
||||||
|
else if W <= LastRAM then Mem [W] := B;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Call;
|
||||||
|
begin
|
||||||
|
//Low byte of the return address
|
||||||
|
RP := RP - 1;
|
||||||
|
StoreByte (RP, IP and $ff);
|
||||||
|
//High byte of the return address
|
||||||
|
RP := RP - 1;
|
||||||
|
StoreByte (RP, IP shr 8);
|
||||||
|
//Call
|
||||||
|
IP := Addr;
|
||||||
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
||||||
//Initialise the halt flag, the pointers, and the instruction counter
|
//Initialise the halt flag, the pointers, and the instruction counter
|
||||||
Hlt := false;
|
Hlt := false;
|
||||||
IP := 0;
|
IP := 0;
|
||||||
RP := $fff0;
|
RP := LastRAM + 1;
|
||||||
IC := 0;
|
IC := 0;
|
||||||
|
|
||||||
//Initialise the tape reader and punch
|
//Initialise the tape reader and punch
|
||||||
|
@ -103,6 +297,10 @@ begin
|
||||||
if Verbose = true then assign (Prog, ParamStr (2))
|
if Verbose = true then assign (Prog, ParamStr (2))
|
||||||
else assign (Prog, ParamStr (1));
|
else assign (Prog, ParamStr (1));
|
||||||
reset (Prog);
|
reset (Prog);
|
||||||
|
if FileSize (Prog) > LastRAM + 1 then begin
|
||||||
|
writeln ('Error: program size cannot exceed ', LastRam + 1, ' bytes');
|
||||||
|
halt;
|
||||||
|
end;
|
||||||
{$i+}
|
{$i+}
|
||||||
if IOResult <> 0 then begin
|
if IOResult <> 0 then begin
|
||||||
writeln ('Error: program file cannot be read from');
|
writeln ('Error: program file cannot be read from');
|
||||||
|
@ -111,11 +309,7 @@ begin
|
||||||
repeat
|
repeat
|
||||||
read (Prog, Mem [IP]);
|
read (Prog, Mem [IP]);
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
until (eof (Prog)) or (IP = $fff0);
|
until (eof (Prog));
|
||||||
if IP = $fff0 then begin
|
|
||||||
writeln ('Error: memory overflow');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
|
|
||||||
//Reinitialise the instruction pointer
|
//Reinitialise the instruction pointer
|
||||||
IP := 0;
|
IP := 0;
|
||||||
|
@ -127,45 +321,34 @@ begin
|
||||||
if Verbose = true then writeln (StdErr, 'IR: ', IntToHex (Op, 1), IntToHex (Regs, 1), IntToHex (Addr, 4), '; IP: ', IntToHex (IP, 4), ', RP: ', IntToHex (RP, 4), '; R0: ', IntToHex (R[0], 2), ', R1: ', IntToHex (R[1], 2), ', R2: ', IntToHex (R[2], 2), ', R3: ', IntToHex (R[3], 2), ansichar ($d));
|
if Verbose = true then writeln (StdErr, 'IR: ', IntToHex (Op, 1), IntToHex (Regs, 1), IntToHex (Addr, 4), '; IP: ', IntToHex (IP, 4), ', RP: ', IntToHex (RP, 4), '; R0: ', IntToHex (R[0], 2), ', R1: ', IntToHex (R[1], 2), ', R2: ', IntToHex (R[2], 2), ', R3: ', IntToHex (R[3], 2), ansichar ($d));
|
||||||
|
|
||||||
//Fetch the instruction and increment the instruction pointer
|
//Fetch the instruction and increment the instruction pointer
|
||||||
//Opcode
|
//Fetch the opcode and register arguments
|
||||||
Op := Mem [IP] and $f0 shr 4;
|
Fetched := LoadByte (IP);
|
||||||
//Register arguments
|
//Decode the opcode
|
||||||
Regs := Mem [IP] and $f;
|
Op := Fetched and $f0 shr 4;
|
||||||
X := Mem [IP] and $c shr 2;
|
//Decode the register arguments
|
||||||
Y := Mem [IP] and 3;
|
Regs := Fetched and $f;
|
||||||
|
X := Fetched and $c shr 2;
|
||||||
|
Y := Fetched and 3;
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then begin
|
|
||||||
writeln ('Error: illegal instruction pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
//Immediate or address argument
|
//Immediate or address argument
|
||||||
if Op >= $a then begin
|
if Op >= $a then begin
|
||||||
//Immediate or high byte of address
|
//Immediate or high byte of address
|
||||||
Addr := Mem [IP];
|
Fetched := LoadByte (IP);
|
||||||
|
Addr := Fetched;
|
||||||
Addr := Addr shl 8;
|
Addr := Addr shl 8;
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then begin
|
|
||||||
writeln ('Error: illegal instruction pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
//Low byte of address
|
//Low byte of address
|
||||||
if Op = $a then begin
|
if Op = $a then begin
|
||||||
if Y = 0 then begin
|
if Y = 0 then begin
|
||||||
Addr := Addr + Mem [IP];
|
Fetched := LoadByte (IP);
|
||||||
|
Addr := Addr + Fetched;
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then begin
|
|
||||||
writeln ('Error: illegal instruction pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
Addr := Addr + Mem [IP];
|
Fetched := LoadByte (IP);
|
||||||
|
Addr := Addr + Fetched;
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
if IP > $ffef then begin
|
|
||||||
writeln ('Error: illegal instruction pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else Addr := 0;
|
else Addr := 0;
|
||||||
|
@ -176,20 +359,12 @@ begin
|
||||||
//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 := LoadByte (RP);
|
||||||
IP := IP shl 8;
|
IP := IP shl 8;
|
||||||
RP := RP + 1;
|
RP := RP + 1;
|
||||||
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 + LoadByte (RP);
|
||||||
RP := RP + 1;
|
RP := RP + 1;
|
||||||
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
|
||||||
|
@ -212,177 +387,10 @@ begin
|
||||||
//Immediate
|
//Immediate
|
||||||
if Y <> 0 then R [X] := Addr shr 8
|
if Y <> 0 then R [X] := Addr shr 8
|
||||||
//Address
|
//Address
|
||||||
else begin
|
else R [X] := LoadByte (Addr);
|
||||||
//Terminal input
|
|
||||||
if Addr = IO then begin
|
|
||||||
wait;
|
|
||||||
//Read a keypress
|
|
||||||
repeat
|
|
||||||
Ch := ReadKey;
|
|
||||||
//Check for non-ASCII keys
|
|
||||||
if Ch = ansichar (0) then begin
|
|
||||||
//Non-ASCII
|
|
||||||
if keypressed then begin
|
|
||||||
Scan := ReadKey;
|
|
||||||
//The delete key inserts the delete character
|
|
||||||
if Scan = ansichar ($53) then begin
|
|
||||||
Ch := ansichar ($7f);
|
|
||||||
ASCII := true;
|
|
||||||
end
|
|
||||||
//Unused function keys insert a null
|
|
||||||
else ASCII := true;
|
|
||||||
end
|
|
||||||
//Null
|
|
||||||
else ASCII := true;
|
|
||||||
end
|
|
||||||
//Other ASCII
|
|
||||||
else ASCII := true;
|
|
||||||
until ASCII = true;
|
|
||||||
//Bodge for the home and end keys
|
|
||||||
if Ch = ansichar ($37) then begin
|
|
||||||
if keypressed then begin
|
|
||||||
Scan := ReadKey;
|
|
||||||
Scan := ReadKey;
|
|
||||||
Scan := ReadKey;
|
|
||||||
Ch := ansichar (0);
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if Ch = ansichar ($38) then begin
|
|
||||||
if keypressed then begin
|
|
||||||
Scan := ReadKey;
|
|
||||||
Scan := ReadKey;
|
|
||||||
Scan := ReadKey;
|
|
||||||
Ch := ansichar (0);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
//Process the keypress
|
|
||||||
Output; //Local echo
|
|
||||||
R [X] := byte (Ch);
|
|
||||||
end
|
|
||||||
//Tape reader
|
|
||||||
{$ifdef tape}
|
|
||||||
else if Addr = $fffd then begin
|
|
||||||
wait;
|
|
||||||
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
|
||||||
//Check the reader state
|
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
|
||||||
try
|
|
||||||
reset (State);
|
|
||||||
read (State, Reader);
|
|
||||||
read (State, Punch);
|
|
||||||
close (State);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
//Read
|
|
||||||
assign (TapeIn, Reader.Path);
|
|
||||||
try
|
|
||||||
reset (TapeIn);
|
|
||||||
seek (TapeIn, Reader.Pos);
|
|
||||||
read (TapeIn, R [X]);
|
|
||||||
close (TapeIn);
|
|
||||||
Reader.Pos := Reader.Pos + 1;
|
|
||||||
except
|
|
||||||
R [X] := $ff;
|
|
||||||
end;
|
|
||||||
//Save the reader state
|
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
|
||||||
try
|
|
||||||
rewrite (State);
|
|
||||||
write (State, Reader);
|
|
||||||
write (State, Punch);
|
|
||||||
close (State);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
{$endif}
|
|
||||||
//Regular load
|
|
||||||
else R [X] := Mem [Addr];
|
|
||||||
end;
|
|
||||||
end
|
end
|
||||||
//Store
|
//Store
|
||||||
else if Op = $b then begin
|
else if Op = $b then StoreByte (Addr, R [Y])
|
||||||
//Terminal output
|
|
||||||
if Addr = IO then begin
|
|
||||||
wait;
|
|
||||||
Ch := ansichar (R [Y]);
|
|
||||||
Output;
|
|
||||||
end
|
|
||||||
//Printer
|
|
||||||
{$ifdef printer}
|
|
||||||
else if Addr = $fffe then begin
|
|
||||||
wait;
|
|
||||||
assign (Prn, '/dev/usb/lp0');
|
|
||||||
try
|
|
||||||
rewrite (Prn);
|
|
||||||
write (Prn, R [Y]);
|
|
||||||
close (Prn);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
{$endif}
|
|
||||||
//Tape punch
|
|
||||||
{$ifdef tape}
|
|
||||||
else if Addr = $fffd then begin
|
|
||||||
wait;
|
|
||||||
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
|
||||||
//Check the punch state
|
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
|
||||||
try
|
|
||||||
reset (State);
|
|
||||||
read (State, Reader);
|
|
||||||
read (State, Punch);
|
|
||||||
close (State);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
//Punch
|
|
||||||
if Punch.Path <> '' then begin
|
|
||||||
assign (TapeOut, Punch.Path);
|
|
||||||
if FileExists (Punch.Path) = false then begin
|
|
||||||
try
|
|
||||||
rewrite (TapeOut);
|
|
||||||
write (TapeOut, R [Y]);
|
|
||||||
close (TapeOut);
|
|
||||||
Punch.Reset := false;
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if Punch.Reset then begin
|
|
||||||
try
|
|
||||||
rewrite (TapeOut);
|
|
||||||
write (TapeOut, R [Y]);
|
|
||||||
close (TapeOut);
|
|
||||||
Punch.Reset := false;
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
try
|
|
||||||
reset (TapeOut);
|
|
||||||
seek (TapeOut, FileSize (TapeOut));
|
|
||||||
write (TapeOut, R [Y]);
|
|
||||||
close (TapeOut);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
//Save the punch state
|
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
|
||||||
try
|
|
||||||
rewrite (State);
|
|
||||||
write (State, Reader);
|
|
||||||
write (State, Punch);
|
|
||||||
close (State);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
{$endif}
|
|
||||||
//Regular store
|
|
||||||
else Mem [Addr] := R [Y];
|
|
||||||
end
|
|
||||||
//Breq
|
//Breq
|
||||||
else if Op = $c then begin
|
else if Op = $c then begin
|
||||||
if R [X] = R [Y] then IP := Addr;
|
if R [X] = R [Y] then IP := Addr;
|
||||||
|
@ -393,53 +401,11 @@ begin
|
||||||
end
|
end
|
||||||
//Cleq
|
//Cleq
|
||||||
else if Op = $e then begin
|
else if Op = $e then begin
|
||||||
if R [X] = R [Y] then begin
|
if R [X] = R [Y] then Call;
|
||||||
//Low byte of the return address
|
|
||||||
RP := RP - 1;
|
|
||||||
if RP > $fff0 then begin
|
|
||||||
writeln ('Error: illegal return pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
Mem [RP] := IP and $ff;
|
|
||||||
//High byte of the return address
|
|
||||||
RP := RP - 1;
|
|
||||||
if RP > $fff0 then begin
|
|
||||||
writeln ('Error: illegal return pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
Mem [RP] := IP shr 8;
|
|
||||||
//Call
|
|
||||||
IP := Addr;
|
|
||||||
if IP > $ffef then begin
|
|
||||||
writeln ('Error: illegal instruction pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
end
|
||||||
//Clneq
|
//Clneq
|
||||||
else if Op = $f then begin
|
else if Op = $f then begin
|
||||||
if R [X] <> R [Y] then begin
|
if R [X] <> R [Y] then Call;
|
||||||
//Low byte of the return address
|
|
||||||
RP := RP - 1;
|
|
||||||
if RP > $fff0 then begin
|
|
||||||
writeln ('Error: illegal return pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
Mem [RP] := IP and $ff;
|
|
||||||
//High byte of the return address
|
|
||||||
RP := RP - 1;
|
|
||||||
if RP > $fff0 then begin
|
|
||||||
writeln ('Error: illegal return pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
Mem [RP] := IP shr 8;
|
|
||||||
//Call
|
|
||||||
IP := Addr;
|
|
||||||
if IP > $ffef then begin
|
|
||||||
writeln ('Error: illegal instruction pointer value');
|
|
||||||
halt;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//Increment the instruction counter
|
//Increment the instruction counter
|
||||||
|
|
52
readme.md
52
readme.md
|
@ -14,12 +14,6 @@ assembler and a disassembler, all written in FreePascal. It also
|
||||||
includes couple of simple example programs for Thingamajig written in
|
includes couple of simple example programs for Thingamajig written in
|
||||||
Assembly.
|
Assembly.
|
||||||
|
|
||||||
Speed
|
|
||||||
-----
|
|
||||||
|
|
||||||
Thingamajig does not have a prescribed speed. The emulator runs at
|
|
||||||
roughly 500 KIPS.
|
|
||||||
|
|
||||||
Registers and Memory
|
Registers and Memory
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -28,11 +22,10 @@ Registers and Memory
|
||||||
* 8-bit general-purpose registers R0-R3
|
* 8-bit general-purpose registers R0-R3
|
||||||
* 8-bit memory locations 0-FFFF
|
* 8-bit memory locations 0-FFFF
|
||||||
|
|
||||||
Multi-byte values are big-endian. Memory addresses FFF0-FFFF are
|
Multi-byte values are big-endian. Memory locations 0-FFEF are used for
|
||||||
reserved for memory mapped devices; the instruction and return pointers
|
RAM while FFF0-FFFF are reserved for memory mapped devices. Input and
|
||||||
cannot have values higher than FFEF and FFF0 respectively to avoid them.
|
output are mapped to address FFFF, while arbitrary devices can be mapped
|
||||||
The instruction and return pointers are initialised as 0 and FFF0
|
to the other reserved addresses.
|
||||||
respectively, while other registers and memory are unitialised.
|
|
||||||
|
|
||||||
Instructions
|
Instructions
|
||||||
------------
|
------------
|
||||||
|
@ -78,8 +71,7 @@ For the arguments of each instruction see the previous section.
|
||||||
|
|
||||||
Address arguments can be either absolute addresses or references to or
|
Address arguments can be either absolute addresses or references to or
|
||||||
relative to a label. Relative references are of the form LABEL +/- N,
|
relative to a label. Relative references are of the form LABEL +/- N,
|
||||||
the spacing being optional. Note that the assembler does not check for
|
the spacing being optional.
|
||||||
addresses or references to reserved addresses.
|
|
||||||
|
|
||||||
In addition to the true instructions there are three
|
In addition to the true instructions there are three
|
||||||
pseudo-instructions. ORG defines the starting address of the program: it
|
pseudo-instructions. ORG defines the starting address of the program: it
|
||||||
|
@ -88,26 +80,34 @@ not required if the starting address is 0. DATA introduces a byte of
|
||||||
data. ADDR introduces two bytes of data containing the address of a
|
data. ADDR introduces two bytes of data containing the address of a
|
||||||
reference to or relative to a label.
|
reference to or relative to a label.
|
||||||
|
|
||||||
Memory-Mapped Devices
|
Boot
|
||||||
---------------------
|
----
|
||||||
|
|
||||||
Input and output are mapped to address FFFF; the emulator emulates a
|
At boot the initial program loader (IPL) loads a program to the RAM
|
||||||
roughly 1000 CPS glass teletype with local echo.
|
starting at address 0 after which is cedes control to the CPU. The
|
||||||
|
instruction and return pointers are initialised as 0 and the first
|
||||||
|
address after RAM respectively, while other registers and RAM are
|
||||||
|
uninitialised.
|
||||||
|
|
||||||
Arbitrary devices can be mapped to the other reserved addresses.
|
Emulator
|
||||||
|
--------
|
||||||
|
|
||||||
|
The emulator runs at roughly 500 KIPS and has the full 65520 bytes of
|
||||||
|
RAM.
|
||||||
|
|
||||||
|
Input and output are handled by an emulated roughly 1000 CPS glass
|
||||||
|
teletype terminal with local echo. Note that of the control characters
|
||||||
|
only bell, backspace, line feed, and carriage return are used by the
|
||||||
|
terminal, and the backspace and delete keys are tied to their respective
|
||||||
|
characters and non-character keys to null.
|
||||||
|
|
||||||
In Linux the emulator can be compiled with support for a character
|
In Linux the emulator can be compiled with support for a character
|
||||||
printer and an emulated punched tape reader and punch with the arguments
|
printer and an emulated punched tape reader and punch with the arguments
|
||||||
-dprinter and -dtape respectively. The printer is mapped to address FFFE
|
-dprinter and -dtape respectively. The printer is mapped to address FFFE
|
||||||
and the tape reader and punch to FFFD. The printer prints into
|
and the tape reader and punch to FFFD. The printer prints into
|
||||||
/dev/usb/lp0 and the tape files read from and punched to are (re)set
|
/dev/usb/lp0 and the tape files read from and punched to are (re)set
|
||||||
using the program tapectl. If you wish to use a different setup you have
|
using the program tapectl.
|
||||||
to modify the code yourself.
|
|
||||||
|
|
||||||
Initial Program Loader
|
The IPL loads a program from a file specified when launching the
|
||||||
----------------------
|
emulator.
|
||||||
|
|
||||||
At boot the initial program loader (IPL) loads a program to the memory
|
|
||||||
starting from address 0 after which is cedes control to the CPU. The
|
|
||||||
emulator loads the program from a ROM file.
|
|
||||||
|
|
Loading…
Reference in New Issue