Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
CrazyEttin | 1f7bc8b5b5 | |
CrazyEttin | 64ddf705ea | |
CrazyEttin | e626369a16 | |
CrazyEttin | 85495426b2 | |
CrazyEttin | d324e795ed | |
CrazyEttin | f8b27b8465 | |
CrazyEttin | 7c2905b558 | |
CrazyEttin | da67a19ac6 | |
CrazyEttin | 23b32d7835 | |
CrazyEttin | bf36aab7a4 | |
CrazyEttin | 0b07a2b214 | |
CrazyEttin | 2feaba4ee9 | |
CrazyEttin | 3f7cf0d8ea | |
CrazyEttin | 5fd7fa707f | |
CrazyEttin | f0e676facb | |
CrazyEttin | 9d318c4c75 | |
CrazyEttin | c78d7c69ef | |
CrazyEttin | 13e56f57a7 | |
CrazyEttin | d4720f48c2 |
|
@ -170,6 +170,7 @@ begin
|
||||||
LP := 1;
|
LP := 1;
|
||||||
BP := 0;
|
BP := 0;
|
||||||
SP := 0;
|
SP := 0;
|
||||||
|
EP := 0;
|
||||||
|
|
||||||
//Begin the main loop
|
//Begin the main loop
|
||||||
repeat
|
repeat
|
||||||
|
@ -258,8 +259,22 @@ begin
|
||||||
else LblError;
|
else LblError;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
writeln ('Error (line ', LP, '): ORG must be the first instruction');
|
if Elem [0] = '' then begin
|
||||||
halt (1);
|
//Set the starting point
|
||||||
|
if ExtractWord (3, Line, [' ']) <> '' then ArgError;
|
||||||
|
DatOrg := ExtractWord (2, Line, [' ']);
|
||||||
|
try
|
||||||
|
if Hex2Dec (DatOrg) <=$ffff then begin
|
||||||
|
if BP > EP then EP := BP;
|
||||||
|
BP := Hex2Dec (DatOrg);
|
||||||
|
if BP < SP then SP := BP;
|
||||||
|
end
|
||||||
|
else ArgError;
|
||||||
|
except
|
||||||
|
ArgError;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else LblError;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -331,17 +346,22 @@ begin
|
||||||
else if Elem [3] <> '' then ArgError
|
else if Elem [3] <> '' then ArgError
|
||||||
else if Elem [4] <> '' then ArgError;
|
else if Elem [4] <> '' then ArgError;
|
||||||
end
|
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
|
else if Bin [BP] <= $b0 then begin
|
||||||
if Elem [4] <> '' then ArgError;
|
if Elem [4] <> '' then ArgError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//Assemble the arguments
|
//Assemble the arguments
|
||||||
//Shifts
|
//Shifts
|
||||||
if Bin [BP] >= $20 then if Bin [BP] <= $50 then OneArgReg (2);
|
if Bin [BP] >= $20 then if Bin [BP] <= $50 then begin
|
||||||
|
//First argument
|
||||||
|
OneArgReg (2);
|
||||||
|
//Second argument
|
||||||
|
if CompareText (Elem [3], '1') = 0 then Bin [BP] := Bin [BP] + 1
|
||||||
|
else if CompareText (Elem [3], '2') = 0 then Bin [BP] := Bin [BP] + 2
|
||||||
|
else if CompareText (Elem [3], '3') = 0 then Bin [BP] := Bin [BP] + 3
|
||||||
|
else if CompareText (Elem [3], '4') = 0 then Bin [BP] := Bin [BP] + 0
|
||||||
|
else ArgError;
|
||||||
|
end;
|
||||||
//Logical operations
|
//Logical operations
|
||||||
if Bin [BP] >= $60 then if Bin [BP] <= $90 then TwoArgRegs;
|
if Bin [BP] >= $60 then if Bin [BP] <= $90 then TwoArgRegs;
|
||||||
//Load
|
//Load
|
||||||
|
@ -417,7 +437,7 @@ begin
|
||||||
if AllRefsResolved = false then halt (1);
|
if AllRefsResolved = false then halt (1);
|
||||||
|
|
||||||
//Set the end pointer and reset the byte pointer to the start of the program
|
//Set the end pointer and reset the byte pointer to the start of the program
|
||||||
EP := BP;
|
if BP > EP then EP := BP;
|
||||||
BP := SP;
|
BP := SP;
|
||||||
|
|
||||||
//Write the program file
|
//Write the program file
|
||||||
|
|
|
@ -111,7 +111,13 @@ begin
|
||||||
write (Opcodes [Op]);
|
write (Opcodes [Op]);
|
||||||
if Op = $b then writeln (IntToHex (Addr, 1), ', R', X)
|
if Op = $b then writeln (IntToHex (Addr, 1), ', R', X)
|
||||||
else begin
|
else begin
|
||||||
if Op >= 2 then write ('R', X);
|
if Op >= 2 then begin
|
||||||
|
write ('R', X);
|
||||||
|
if Op <= 5 then begin
|
||||||
|
if Y = 0 then write (', 4')
|
||||||
|
else write (', ', Y);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
if Op >= 6 then if Op <= 9 then write (', R', Y);
|
if Op >= 6 then if Op <= 9 then write (', R', Y);
|
||||||
if OP >= $c then write (', R', Y);
|
if OP >= $c then write (', R', Y);
|
||||||
if Op >= $a then begin
|
if Op >= $a then begin
|
||||||
|
|
736
emulator.pas
736
emulator.pas
|
@ -2,17 +2,38 @@ program Emulator;
|
||||||
|
|
||||||
{$MODE OBJFPC}
|
{$MODE OBJFPC}
|
||||||
|
|
||||||
uses SysUtils, Crt;
|
{$ifdef full}
|
||||||
|
{$define RAM64}
|
||||||
|
{$define printer}
|
||||||
|
{$define tape}
|
||||||
|
{$define floppy}
|
||||||
|
{$define modem}
|
||||||
|
{$define status}
|
||||||
|
{$endif}
|
||||||
|
|
||||||
|
uses SysUtils, Crt{$ifdef modem}, BaseUnix, Sockets{$endif}{$ifdef status}{$ifndef modem}, BaseUnix{$endif}{$endif};
|
||||||
|
|
||||||
{$ifdef tape}
|
{$ifdef tape}
|
||||||
|
//Tape file path and reset state
|
||||||
type
|
type
|
||||||
//Tape file path and reset state
|
|
||||||
Tape = record
|
Tape = record
|
||||||
Path: shortstring;
|
Path: shortstring;
|
||||||
Reset: boolean;
|
Reset: boolean;
|
||||||
Pos: integer;
|
Pos: integer;
|
||||||
end;
|
end;
|
||||||
{$endif}
|
{$endif}
|
||||||
|
{$ifdef modem}
|
||||||
|
//Modem connection state
|
||||||
|
type
|
||||||
|
Connection = record
|
||||||
|
Originate: boolean;
|
||||||
|
Answer: boolean;
|
||||||
|
Dial: boolean;
|
||||||
|
Addr: longword;
|
||||||
|
Port: word;
|
||||||
|
Hang: boolean;
|
||||||
|
end;
|
||||||
|
{$endif}
|
||||||
|
|
||||||
const
|
const
|
||||||
//The last address of RAM
|
//The last address of RAM
|
||||||
|
@ -31,20 +52,49 @@ const
|
||||||
{$endif}
|
{$endif}
|
||||||
|
|
||||||
var
|
var
|
||||||
Hlt, Verbose, Echo: boolean; //Halt, verbose, and echo flags
|
Hlt, Echo{$ifdef floppy}, DiscRead, DiscWrite, DiscTrackSet, DiscSectSet{$endif}{$ifdef modem}, Listening, Connected{$endif}: boolean; //Halt and echo flags, disc system flags, and modem connection flags
|
||||||
Op, Regs: 0 .. $f; //Opcode
|
Op, Regs: 0 .. $f; //Opcode
|
||||||
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 .. LastRAM] of byte; //Random access 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
|
{$ifdef floppy}
|
||||||
|
DiscSB: array [0 .. $88] of byte; //Sector buffer for the disc system
|
||||||
|
DiscSBP: 0 .. $88; //Sector buffer pointer
|
||||||
|
{$endif}
|
||||||
|
Prog{$ifdef printer}, Prn{$endif}{$ifdef tape}, TapeIn, TapeOut{$endif}{$ifdef floppy}, Disc{$endif}: file of byte; //Program file, line printer, tape reader and punch tapes, and current disc
|
||||||
{$ifdef tape}
|
{$ifdef tape}
|
||||||
Reader, Punch: Tape; //States of the tape reader and punch
|
Reader, Punch: Tape; //States of the tape reader and punch
|
||||||
State: file of Tape; //File storing the states of the tape reader and punch
|
Tapes: file of Tape; //File storing the states of the tape reader and punch
|
||||||
|
{$endif}
|
||||||
|
{$ifdef floppy}
|
||||||
|
Disc0Path, Disc1Path, DiscPath: shortstring; //Paths of the discs in the drives and the current disc
|
||||||
|
Discs: file of shortstring; //File storing the state of the disc system
|
||||||
{$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
|
Verbose, IC, LFX: integer; //Verbose flag, instruction counter for CPU speed, and line feed position marker
|
||||||
Fetched: byte; //Fetched byte
|
Fetched{$ifdef floppy}, Disc0Track, Disc1Track, Disc0Sect, Disc1Sect{$endif}: byte; //Fetched byte and disc drive locations
|
||||||
|
{$ifdef floppy}
|
||||||
|
DiscDrive: 0 .. 1; //Current disc drive number
|
||||||
|
{$endif}
|
||||||
|
{$ifdef modem}
|
||||||
|
ConnVar: Connection; //State of the modem
|
||||||
|
ConnFile: file of Connection; //File storing the state of the modem
|
||||||
|
Mode: (Originate, Answer); //Modem mode
|
||||||
|
ServerSocket, ListenSocket, ClientSocket, ClientAddrSize: longint; //Server socket
|
||||||
|
ServerAddr, ClientAddr: TInetSockAddr; //Server address
|
||||||
|
SigPipeHandler: pSigActionRec; //SIGPIPE handler
|
||||||
|
{$endif}
|
||||||
|
{$ifdef status}
|
||||||
|
FileDescs: TFDset; //File descriptor set
|
||||||
|
{$endif}
|
||||||
|
|
||||||
|
//Ignore signal
|
||||||
|
{$ifdef modem}
|
||||||
|
procedure DoSig (Sig: cint); cdecl;
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
{$endif}
|
||||||
|
|
||||||
//Terminal output
|
//Terminal output
|
||||||
procedure Output;
|
procedure Output;
|
||||||
|
@ -66,16 +116,136 @@ begin
|
||||||
else write (Ch);
|
else write (Ch);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//Wait to emulate CPU speed of ~500 KIPS
|
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
procedure wait;
|
//Wait to emulate CPU speed of roughly 500 KIPS
|
||||||
|
procedure wait (I: integer);
|
||||||
begin
|
begin
|
||||||
if IC div 500 = 0 then sleep (1)
|
if IC div 500 < I then sleep (I)
|
||||||
else sleep (IC div 500);
|
else begin
|
||||||
|
sleep (IC div 500);
|
||||||
|
if IC mod 500 >= 250 then sleep (1);
|
||||||
|
end;
|
||||||
IC := 0;
|
IC := 0;
|
||||||
end;
|
end;
|
||||||
{$endif}
|
{$endif}
|
||||||
|
|
||||||
|
{$ifdef modem}
|
||||||
|
//Check the modem state
|
||||||
|
procedure CheckModem;
|
||||||
|
begin
|
||||||
|
assign (ConnFile, ExpandFileName ('~/.thingamajig/connection'));
|
||||||
|
//Check the modem state
|
||||||
|
if FileExists (ExpandFileName ('~/.thingamajig/connection')) then begin
|
||||||
|
try
|
||||||
|
reset (ConnFile);
|
||||||
|
read (ConnFile, ConnVar);
|
||||||
|
close (ConnFile);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Auto-set things when dialing
|
||||||
|
if ConnVar.Dial then begin
|
||||||
|
//Auto-change to originate mode if dialing in answer mode
|
||||||
|
if Mode = Answer then ConnVar.Originate := true;
|
||||||
|
//Auto-hang if dialing
|
||||||
|
if Mode = Originate then if Connected then ConnVar.Hang := true;
|
||||||
|
end;
|
||||||
|
//Mode change
|
||||||
|
//Originate
|
||||||
|
if ConnVar.Originate then begin
|
||||||
|
//Change the mode
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ServerSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
CloseSocket (ListenSocket);
|
||||||
|
Listening := false;
|
||||||
|
Mode := Originate;
|
||||||
|
end;
|
||||||
|
ConnVar.Originate := false;
|
||||||
|
end
|
||||||
|
//Answer
|
||||||
|
else if ConnVar.Answer then begin
|
||||||
|
//Change the mode
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ServerSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
Mode := Answer;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
CloseSocket (ListenSocket);
|
||||||
|
Listening := false;
|
||||||
|
end;
|
||||||
|
//Create a listening socket
|
||||||
|
ListenSocket := fpSocket (AF_INET, SOCK_STREAM, 0);
|
||||||
|
if ListenSocket <> -1 then begin
|
||||||
|
ServerAddr.sin_family := AF_INET;
|
||||||
|
ServerAddr.sin_addr.s_addr := htonl (ConnVar.Addr);
|
||||||
|
ServerAddr.sin_port := htons (ConnVar.Port);
|
||||||
|
if fpBind (ListenSocket, @ServerAddr, Sizeof (ServerAddr)) <> -1 then begin
|
||||||
|
if fpListen (ListenSocket, 1) <> -1 then begin
|
||||||
|
ConnVar.Answer := false;
|
||||||
|
Listening := true;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Hang
|
||||||
|
if ConnVar.Hang then begin
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ServerSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
ConnVar.Hang := false;
|
||||||
|
end;
|
||||||
|
//Dial
|
||||||
|
if ConnVar.Dial then if Connected = false then begin
|
||||||
|
//Create a server socket
|
||||||
|
ServerSocket := fpSocket (AF_INET, SOCK_STREAM, 0);
|
||||||
|
if ServerSocket <> -1 then begin
|
||||||
|
//Connect
|
||||||
|
ServerAddr.sin_family := AF_INET;
|
||||||
|
ServerAddr.sin_addr.s_addr := htonl (ConnVar.Addr);
|
||||||
|
ServerAddr.sin_port := htons (ConnVar.Port);
|
||||||
|
if fpConnect (ServerSocket, @ServerAddr, Sizeof (ServerAddr)) <> -1 then begin
|
||||||
|
ConnVar.Dial := false;
|
||||||
|
Connected := true;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Save the modem state
|
||||||
|
if FileExists (ExpandFileName ('~/.thingamajig/connection')) then begin
|
||||||
|
try
|
||||||
|
rewrite (ConnFile);
|
||||||
|
write (ConnFile, ConnVar);
|
||||||
|
close (ConnFile);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{$endif}
|
||||||
|
|
||||||
//Load a byte from memory
|
//Load a byte from memory
|
||||||
function LoadByte (W: word): byte;
|
function LoadByte (W: word): byte;
|
||||||
var
|
var
|
||||||
|
@ -84,7 +254,7 @@ begin
|
||||||
//Terminal input
|
//Terminal input
|
||||||
if W = $ffff then begin
|
if W = $ffff then begin
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
wait;
|
wait (1);
|
||||||
{$endif}
|
{$endif}
|
||||||
//Read a keypress
|
//Read a keypress
|
||||||
Ch := ReadKey;
|
Ch := ReadKey;
|
||||||
|
@ -109,20 +279,20 @@ begin
|
||||||
if Echo then Output; //Local echo
|
if Echo then Output; //Local echo
|
||||||
B := byte (Ch);
|
B := byte (Ch);
|
||||||
end
|
end
|
||||||
//Tape reader
|
|
||||||
{$ifdef tape}
|
{$ifdef tape}
|
||||||
|
//Tape reader
|
||||||
else if W = $fffd then begin
|
else if W = $fffd then begin
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
wait;
|
wait (2);
|
||||||
{$endif}
|
{$endif}
|
||||||
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
assign (Tapes, ExpandFileName ('~/.thingamajig/tapes'));
|
||||||
//Check the reader state
|
//Check the reader state
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin
|
||||||
try
|
try
|
||||||
reset (State);
|
reset (Tapes);
|
||||||
read (State, Reader);
|
read (Tapes, Reader);
|
||||||
read (State, Punch);
|
read (Tapes, Punch);
|
||||||
close (State);
|
close (Tapes);
|
||||||
except
|
except
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
@ -138,17 +308,124 @@ begin
|
||||||
B := $ff;
|
B := $ff;
|
||||||
end;
|
end;
|
||||||
//Save the reader state
|
//Save the reader state
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin
|
||||||
try
|
try
|
||||||
rewrite (State);
|
rewrite (Tapes);
|
||||||
write (State, Reader);
|
write (Tapes, Reader);
|
||||||
write (State, Punch);
|
write (Tapes, Punch);
|
||||||
close (State);
|
close (Tapes);
|
||||||
except
|
except
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
{$endif}
|
{$endif}
|
||||||
|
{$ifdef floppy}
|
||||||
|
//Floppy disc drive system
|
||||||
|
//Data
|
||||||
|
else if W = $fffc then begin
|
||||||
|
if DiscRead then begin
|
||||||
|
B := DiscSB [DiscSBP];
|
||||||
|
if DiscSBP < $88 then DiscSBP := DiscSBP + 1
|
||||||
|
else begin
|
||||||
|
DiscSBP := 0;
|
||||||
|
DiscRead := false;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else B := 0;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
|
{$ifdef modem}
|
||||||
|
//Modem
|
||||||
|
//Data
|
||||||
|
else if W = $fffa then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
wait (33);
|
||||||
|
{$endif}
|
||||||
|
//Check the modem state
|
||||||
|
CheckModem;
|
||||||
|
//Recieve
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then begin
|
||||||
|
if fpRecv (ServerSocket, @B, 1, 0) <> 1 then begin
|
||||||
|
CloseSocket (ServerSocket);
|
||||||
|
Connected := false;
|
||||||
|
B := 0;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else B := 0;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
if fpRecv (ClientSocket, @B, 1, 0) <> 1 then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
Connected := false;
|
||||||
|
B := 0;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else B := 0;
|
||||||
|
end
|
||||||
|
else B := 0;
|
||||||
|
end
|
||||||
|
//Status
|
||||||
|
else if W = $fff9 then begin
|
||||||
|
//Check the modem state
|
||||||
|
CheckModem;
|
||||||
|
//Load the status
|
||||||
|
if Connected then B := 1
|
||||||
|
else B := 0;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
|
{$ifdef status}
|
||||||
|
//Input status register
|
||||||
|
else if W = $fff8 then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
wait (1);
|
||||||
|
{$endif}
|
||||||
|
//Initialise the register
|
||||||
|
B := 0;
|
||||||
|
//FFFF: Terminal
|
||||||
|
fpfd_zero (FileDescs);
|
||||||
|
fpfd_set (0, FileDescs);
|
||||||
|
if fpSelect (1, @FileDescs, nil, nil, 1) > 0 then B := B or 1;
|
||||||
|
//FFFE: No input
|
||||||
|
B := B or 2;
|
||||||
|
//FFFD: Tape reader or no input
|
||||||
|
B := B or 4;
|
||||||
|
//FFFC: Disc system data or no input
|
||||||
|
B := B or 8;
|
||||||
|
//FFFB: No input
|
||||||
|
B := B or $10;
|
||||||
|
//FFFA: Modem or no input
|
||||||
|
{$ifdef modem}
|
||||||
|
//Check the modem state
|
||||||
|
CheckModem;
|
||||||
|
//Check connection status
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then begin
|
||||||
|
fpfd_zero (FileDescs);
|
||||||
|
fpfd_set (ServerSocket, FileDescs);
|
||||||
|
if fpSelect (ServerSocket + 1, @FileDescs, nil, nil, 0) > 0 then B := B or $20;
|
||||||
|
end
|
||||||
|
else B := B or $20;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
fpfd_zero (FileDescs);
|
||||||
|
fpfd_set (ClientSocket, FileDescs);
|
||||||
|
if fpSelect (ClientSocket + 1, @FileDescs, nil, nil, 0) > 0 then B := B or $20;
|
||||||
|
end
|
||||||
|
else B := B or $20;
|
||||||
|
end;
|
||||||
|
{$endif}
|
||||||
|
{$ifndef modem}
|
||||||
|
B := B or $20;
|
||||||
|
{$endif}
|
||||||
|
//FFF9: No input
|
||||||
|
B := B or $40;
|
||||||
|
//FFF8: Input status register
|
||||||
|
B := B or $80;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
//Unused addresses
|
//Unused addresses
|
||||||
else if W > LastRAM then B := 0
|
else if W > LastRAM then B := 0
|
||||||
//Regular load
|
//Regular load
|
||||||
|
@ -162,18 +439,18 @@ begin
|
||||||
//Terminal output
|
//Terminal output
|
||||||
if W = $ffff then begin
|
if W = $ffff then begin
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
wait;
|
wait (1);
|
||||||
{$endif}
|
{$endif}
|
||||||
Ch := ansichar (B);
|
Ch := ansichar (B);
|
||||||
Output;
|
Output;
|
||||||
if Ch = ansichar ($12) then Echo := true;
|
if Ch = ansichar ($12) then Echo := true;
|
||||||
if Ch = ansichar ($14) then Echo := false;
|
if Ch = ansichar ($14) then Echo := false;
|
||||||
end
|
end
|
||||||
//Printer
|
|
||||||
{$ifdef printer}
|
{$ifdef printer}
|
||||||
|
//Printer
|
||||||
else if W = $fffe then begin
|
else if W = $fffe then begin
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
wait;
|
wait (1);
|
||||||
{$endif}
|
{$endif}
|
||||||
assign (Prn, '/dev/usb/lp0');
|
assign (Prn, '/dev/usb/lp0');
|
||||||
try
|
try
|
||||||
|
@ -184,20 +461,20 @@ begin
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
{$endif}
|
{$endif}
|
||||||
//Tape punch
|
|
||||||
{$ifdef tape}
|
{$ifdef tape}
|
||||||
|
//Tape punch
|
||||||
else if W = $fffd then begin
|
else if W = $fffd then begin
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
wait;
|
wait (20);
|
||||||
{$endif}
|
{$endif}
|
||||||
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
assign (Tapes, ExpandFileName ('~/.thingamajig/tapes'));
|
||||||
//Check the punch state
|
//Check the punch state
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin
|
||||||
try
|
try
|
||||||
reset (State);
|
reset (Tapes);
|
||||||
read (State, Reader);
|
read (Tapes, Reader);
|
||||||
read (State, Punch);
|
read (Tapes, Punch);
|
||||||
close (State);
|
close (Tapes);
|
||||||
except
|
except
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
@ -233,17 +510,313 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
//Save the punch state
|
//Save the punch state
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin
|
||||||
try
|
try
|
||||||
rewrite (State);
|
rewrite (Tapes);
|
||||||
write (State, Reader);
|
write (Tapes, Reader);
|
||||||
write (State, Punch);
|
write (Tapes, Punch);
|
||||||
close (State);
|
close (Tapes);
|
||||||
except
|
except
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
{$endif}
|
{$endif}
|
||||||
|
{$ifdef floppy}
|
||||||
|
//Floppy disc drive system
|
||||||
|
//Data
|
||||||
|
else if W = $fffc then begin
|
||||||
|
if DiscWrite then begin
|
||||||
|
DiscSB [DiscSBP] := B;
|
||||||
|
if DiscSBP < $88 then DiscSBP := DiscSBP + 1
|
||||||
|
else begin
|
||||||
|
DiscSBP := 0;
|
||||||
|
DiscWrite := false;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if DiscTrackSet then begin
|
||||||
|
if B <= $4c then begin
|
||||||
|
if DiscDrive = 0 then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
if Disc0Track > B then wait (((Disc0Track - B) * 10) + 45)
|
||||||
|
else if B > Disc0Track then wait (((B - Disc0Track) * 10) + 45);
|
||||||
|
{$endif}
|
||||||
|
Disc0Track := B;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
{$ifndef fast}
|
||||||
|
if Disc1Track > B then wait (((Disc1Track - B) * 10) + 45)
|
||||||
|
else if B > Disc1Track then wait (((B - Disc1Track) * 10) + 45);
|
||||||
|
{$endif}
|
||||||
|
Disc1Track := B;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
DiscTrackSet := false;
|
||||||
|
end
|
||||||
|
else if DiscSectSet then begin
|
||||||
|
if B <= $1f then begin
|
||||||
|
if DiscDrive = 0 then Disc0Sect := B
|
||||||
|
else Disc1Sect := B;
|
||||||
|
end;
|
||||||
|
DiscSectSet := false;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Command
|
||||||
|
else if W = $fffb then begin
|
||||||
|
B := B and $f;
|
||||||
|
//Reset the system
|
||||||
|
if B and $e = 0 then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
if ((Disc0Track * 10) + 45 ) > ((Disc1Track * 10) + 45 ) then wait ((Disc0Track * 10) + 45)
|
||||||
|
else wait ((Disc1Track * 10) + 45);
|
||||||
|
{$endif}
|
||||||
|
Disc0Track := 0;
|
||||||
|
Disc1Track := 0;
|
||||||
|
Disc0Sect := 0;
|
||||||
|
Disc1Sect := 0;
|
||||||
|
DiscRead := false;
|
||||||
|
DiscWrite := false;
|
||||||
|
DiscTrackSet := false;
|
||||||
|
DiscSectSet := false;
|
||||||
|
for DiscSBP := 0 to $88 do DiscSB [DiscSBP] := 0;
|
||||||
|
DiscSBP := 0;
|
||||||
|
end
|
||||||
|
//Format the disc
|
||||||
|
else if B and $e = 2 then begin
|
||||||
|
if DiscRead = false then if DiscWrite = false then if DiscTrackSet = false then if DiscSectSet = false then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
wait (30000);
|
||||||
|
{$endif}
|
||||||
|
assign (Discs, ExpandFileName ('~/.thingamajig/discs'));
|
||||||
|
//Check the system state
|
||||||
|
if FileExists (ExpandFileName ('~/.thingamajig/discs')) then begin
|
||||||
|
try
|
||||||
|
reset (Discs);
|
||||||
|
read (Discs, Disc0Path);
|
||||||
|
read (Discs, Disc1Path);
|
||||||
|
close (Discs);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Set the drive
|
||||||
|
if B and 1 = 0 then DiscPath := Disc0Path
|
||||||
|
else DiscPath := Disc1Path;
|
||||||
|
if DiscPath <> '' then begin
|
||||||
|
assign (Disc, DiscPath);
|
||||||
|
//Write
|
||||||
|
try
|
||||||
|
reset (Disc);
|
||||||
|
if FileSize (Disc) = $526a0 then begin
|
||||||
|
if B and 1 = 0 then begin
|
||||||
|
for Disc0Track := 0 to $4c do begin
|
||||||
|
for Disc0Sect := 0 to $1f do begin
|
||||||
|
for DiscSBP := 0 to $88 do begin
|
||||||
|
seek (Disc, (Disc0Track * $1120) + (Disc0Sect * $89) + DiscSBP);
|
||||||
|
if DiscSBP = 0 then write (Disc, $80)
|
||||||
|
else write (Disc, 0);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
for Disc1Track := 0 to $4c do begin
|
||||||
|
for Disc1Sect := 0 to $1f do begin
|
||||||
|
for DiscSBP := 0 to $88 do begin
|
||||||
|
seek (Disc, (Disc1Track * $1120) + (Disc1Sect * $89) + DiscSBP);
|
||||||
|
if DiscSBP = 0 then write (Disc, $80)
|
||||||
|
else write (Disc, 0);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
close (Disc);
|
||||||
|
Disc0Track := 0;
|
||||||
|
Disc1Track := 0;
|
||||||
|
Disc0Sect := 0;
|
||||||
|
Disc1Sect := 0;
|
||||||
|
DiscRead := false;
|
||||||
|
DiscWrite := false;
|
||||||
|
DiscTrackSet := false;
|
||||||
|
DiscSectSet := false;
|
||||||
|
for DiscSBP := 0 to $88 do DiscSB [DiscSBP] := 0;
|
||||||
|
DiscSBP := 0;
|
||||||
|
end
|
||||||
|
else close (Disc);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Read a sector from the buffer to the computer
|
||||||
|
else if B and $e = 4 then begin
|
||||||
|
if DiscWrite = false then if DiscTrackSet = false then if DiscSectSet = false then begin
|
||||||
|
DiscRead := true;
|
||||||
|
DiscSBP := 0;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Write a sector from the computer to the buffer
|
||||||
|
else if B and $e = 6 then begin
|
||||||
|
if DiscRead = false then if DiscTrackSet = false then if DiscSectSet = false then begin
|
||||||
|
DiscWrite := true;
|
||||||
|
for DiscSBP := 0 to $88 do DiscSB [DiscSBP] := 0;
|
||||||
|
DiscSBP := 0;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Set the track to be accessed
|
||||||
|
else if B and $e = 8 then begin
|
||||||
|
if DiscRead = false then if DiscWrite = false then if DiscSectSet = false then begin
|
||||||
|
DiscDrive := B and 1;
|
||||||
|
DiscTrackSet := true;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Set the sector to be accessed
|
||||||
|
else if B and $e = $a then begin
|
||||||
|
if DiscRead = false then if DiscWrite = false then if DiscTrackSet = false then begin
|
||||||
|
DiscDrive := B and 1;
|
||||||
|
DiscSectSet := true;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Read a sector from the disc to the buffer
|
||||||
|
else if B and $e = $c then begin
|
||||||
|
if DiscRead = false then if DiscWrite = false then if DiscTrackSet = false then if DiscSectSet = false then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
wait (5);
|
||||||
|
{$endif}
|
||||||
|
assign (Discs, ExpandFileName ('~/.thingamajig/discs'));
|
||||||
|
//Check the system state
|
||||||
|
if FileExists (ExpandFileName ('~/.thingamajig/discs')) then begin
|
||||||
|
try
|
||||||
|
reset (Discs);
|
||||||
|
read (Discs, Disc0Path);
|
||||||
|
read (Discs, Disc1Path);
|
||||||
|
close (Discs);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Set the drive
|
||||||
|
if B and 1 = 0 then DiscPath := Disc0Path
|
||||||
|
else DiscPath := Disc1Path;
|
||||||
|
assign (Disc, DiscPath);
|
||||||
|
//Read
|
||||||
|
try
|
||||||
|
reset (Disc);
|
||||||
|
if FileSize (Disc) = $526a0 then begin
|
||||||
|
if B and 1 = 0 then seek (Disc, (Disc0Track * $1120) + (Disc0Sect * $89))
|
||||||
|
else seek (Disc, (Disc1Track * $1120) + (Disc1Sect * $89));
|
||||||
|
read (Disc, DiscSB [0]);
|
||||||
|
if DiscSB [0] and $80 = $80 then begin
|
||||||
|
for DiscSBP := 1 to $88 do begin
|
||||||
|
if B and 1 = 0 then seek (Disc, (Disc0Track * $1120) + (Disc0Sect * $89) + DiscSBP)
|
||||||
|
else seek (Disc, (Disc1Track * $1120) + (Disc1Sect * $89) + DiscSBP);
|
||||||
|
read (Disc, DiscSB [DiscSBP]);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else for DiscSBP := 0 to $88 do DiscSB [DiscSBP] := 0;
|
||||||
|
close (Disc)
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
close (Disc);
|
||||||
|
for DiscSBP := 0 to $88 do DiscSB [DiscSBP] := 0;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
for DiscSBP := 0 to $88 do DiscSB [DiscSBP] := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Write a sector from the buffer to the disc
|
||||||
|
else if B and $e = $e then begin
|
||||||
|
if DiscRead = false then if DiscWrite = false then if DiscTrackSet = false then if DiscSectSet = false then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
wait (5);
|
||||||
|
{$endif}
|
||||||
|
assign (Discs, ExpandFileName ('~/.thingamajig/discs'));
|
||||||
|
//Check the system state
|
||||||
|
if FileExists (ExpandFileName ('~/.thingamajig/discs')) then begin
|
||||||
|
try
|
||||||
|
reset (Discs);
|
||||||
|
read (Discs, Disc0Path);
|
||||||
|
read (Discs, Disc1Path);
|
||||||
|
close (Discs);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//Set the drive
|
||||||
|
if B and 1 = 0 then DiscPath := Disc0Path
|
||||||
|
else DiscPath := Disc1Path;
|
||||||
|
if DiscPath <> '' then begin
|
||||||
|
assign (Disc, DiscPath);
|
||||||
|
//Write
|
||||||
|
try
|
||||||
|
reset (Disc);
|
||||||
|
if FileSize (Disc) = $526a0 then begin
|
||||||
|
for DiscSBP := 0 to $88 do begin
|
||||||
|
if B and 1 = 0 then seek (Disc, (Disc0Track * $1120) + (Disc0Sect * $89) + DiscSBP)
|
||||||
|
else seek (Disc, (Disc1Track * $1120) + (Disc1Sect * $89) + DiscSBP);
|
||||||
|
write (Disc, DiscSB [DiscSBP]);
|
||||||
|
end;
|
||||||
|
close (Disc);
|
||||||
|
end
|
||||||
|
else close (Disc);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
|
{$ifdef modem}
|
||||||
|
//Modem
|
||||||
|
//Data
|
||||||
|
else if W = $fffa then begin
|
||||||
|
{$ifndef fast}
|
||||||
|
wait (33);
|
||||||
|
{$endif}
|
||||||
|
//Check the modem state
|
||||||
|
CheckModem;
|
||||||
|
//Send
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then begin
|
||||||
|
if fpSend (ServerSocket, @B, 1, 0) <> 1 then begin
|
||||||
|
CloseSocket (ServerSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
if fpSend (ClientSocket, @B, 1, 0) <> 1 then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Status
|
||||||
|
else if W = $fff9 then begin
|
||||||
|
//Check the modem state
|
||||||
|
CheckModem;
|
||||||
|
//Change the status
|
||||||
|
if Mode = Originate then begin
|
||||||
|
if Connected then if (B and 1) = 0 then begin
|
||||||
|
CloseSocket (ServerSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Mode = Answer then begin
|
||||||
|
if Connected then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
Connected := false;
|
||||||
|
end;
|
||||||
|
if Listening then if (B and 1) = 1 then begin
|
||||||
|
//Connect
|
||||||
|
ClientAddrSize := sizeof (ClientAddr);
|
||||||
|
ClientSocket := fpAccept (ListenSocket, @ClientAddr, @ClientAddrSize) ;
|
||||||
|
if ClientSocket <> -1 then begin
|
||||||
|
Connected := true;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
{$endif}
|
||||||
//Regular store
|
//Regular store
|
||||||
else if W <= LastRAM then Mem [W] := B;
|
else if W <= LastRAM then Mem [W] := B;
|
||||||
end;
|
end;
|
||||||
|
@ -269,8 +842,8 @@ begin
|
||||||
RP := LastRAM + 1;
|
RP := LastRAM + 1;
|
||||||
IC := 0;
|
IC := 0;
|
||||||
|
|
||||||
//Initialise the tape reader and punch
|
|
||||||
{$ifdef tape}
|
{$ifdef tape}
|
||||||
|
//Initialise the tape reader and punch
|
||||||
Reader.Path := '';
|
Reader.Path := '';
|
||||||
Reader.Reset := true;
|
Reader.Reset := true;
|
||||||
Reader.Pos := 0;
|
Reader.Pos := 0;
|
||||||
|
@ -279,20 +852,59 @@ begin
|
||||||
Punch.Pos := 0;
|
Punch.Pos := 0;
|
||||||
{$endif}
|
{$endif}
|
||||||
|
|
||||||
|
{$ifdef floppy}
|
||||||
|
//Initialise the disc system
|
||||||
|
Disc0Path := '';
|
||||||
|
Disc1Path := '';
|
||||||
|
DiscRead := false;
|
||||||
|
DiscWrite := false;
|
||||||
|
DiscTrackSet := false;
|
||||||
|
DiscSectSet := false;
|
||||||
|
DiscSBP := 0;
|
||||||
|
Disc0Track := 0;
|
||||||
|
Disc1Track := 0;
|
||||||
|
Disc0Sect := 0;
|
||||||
|
Disc1Sect := 0;
|
||||||
|
{$endif}
|
||||||
|
|
||||||
|
{$ifdef modem}
|
||||||
|
//Initialise the modem
|
||||||
|
Mode := Originate;
|
||||||
|
Listening := false;
|
||||||
|
Connected := false;
|
||||||
|
//Initialise the SIGPIPE handler
|
||||||
|
new (SigPipeHandler);
|
||||||
|
SigPipeHandler^.sa_Handler := SigActionHandler (@DoSig);
|
||||||
|
fillchar (SigPipeHandler^.Sa_Mask, sizeof (SigPipeHandler^.sa_mask), #0);
|
||||||
|
SigPipeHandler^.Sa_Flags := 0;
|
||||||
|
SigPipeHandler^.Sa_Restorer := nil;
|
||||||
|
try
|
||||||
|
fpSigAction (SigPipe, SigPipeHandler, nil);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
{$endif}
|
||||||
|
|
||||||
//Check the arguments
|
//Check the arguments
|
||||||
if ParamCount = 0 then begin
|
if ParamCount = 0 then begin
|
||||||
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
||||||
halt (1);
|
halt (1);
|
||||||
end;
|
end;
|
||||||
if ParamStr (1) = '-v' then begin
|
if ParamStr (1) = '-v' then begin
|
||||||
Verbose := true;
|
Verbose := 1;
|
||||||
|
if ParamCount <> 2 then begin
|
||||||
|
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if ParamStr (2) = '-v' then begin
|
||||||
|
Verbose := 2;
|
||||||
if ParamCount <> 2 then begin
|
if ParamCount <> 2 then begin
|
||||||
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
||||||
halt (1);
|
halt (1);
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
Verbose := false;
|
Verbose := 0;
|
||||||
if ParamCount <> 1 then begin
|
if ParamCount <> 1 then begin
|
||||||
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
||||||
halt (1);
|
halt (1);
|
||||||
|
@ -301,7 +913,7 @@ begin
|
||||||
|
|
||||||
//Read a program file and check for errors
|
//Read a program file and check for errors
|
||||||
{$i-}
|
{$i-}
|
||||||
if Verbose = true then assign (Prog, ParamStr (2))
|
if Verbose = 1 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
|
if FileSize (Prog) > LastRAM + 1 then begin
|
||||||
|
@ -316,6 +928,7 @@ begin
|
||||||
repeat
|
repeat
|
||||||
read (Prog, Mem [IP]);
|
read (Prog, Mem [IP]);
|
||||||
IP := IP + 1;
|
IP := IP + 1;
|
||||||
|
IC := IC + 1;
|
||||||
until (eof (Prog));
|
until (eof (Prog));
|
||||||
|
|
||||||
//Reinitialise the instruction pointer
|
//Reinitialise the instruction pointer
|
||||||
|
@ -325,7 +938,7 @@ begin
|
||||||
while Hlt = false do begin
|
while Hlt = false do begin
|
||||||
|
|
||||||
//Print the CPU state to StdErr
|
//Print the CPU state to StdErr
|
||||||
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 <> 0 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
|
||||||
//Fetch the opcode and register arguments
|
//Fetch the opcode and register arguments
|
||||||
|
@ -374,13 +987,25 @@ begin
|
||||||
RP := RP + 1;
|
RP := RP + 1;
|
||||||
end
|
end
|
||||||
//Shl
|
//Shl
|
||||||
else if Op = 2 then R [X] := R [X] shl 1
|
else if Op = 2 then begin
|
||||||
|
if Y = 0 then R [X] := R [X] shl 4
|
||||||
|
else R [X] := R [X] shl Y;
|
||||||
|
end
|
||||||
//Shr
|
//Shr
|
||||||
else if Op = 3 then R [X] := R [X] shr 1
|
else if Op = 3 then begin
|
||||||
|
if Y = 0 then R [X] := R [X] shr 4
|
||||||
|
else R [X] := R [X] shr Y;
|
||||||
|
end
|
||||||
//Rol
|
//Rol
|
||||||
else if Op = 4 then R [X] := RolByte (R [X])
|
else if Op = 4 then begin
|
||||||
|
if Y = 0 then R [X] := RolByte (R [X], 4)
|
||||||
|
else R [X] := RolByte (R [X], Y);
|
||||||
|
end
|
||||||
//Ror
|
//Ror
|
||||||
else if Op = 5 then R [X] := RorByte (R [X])
|
else if Op = 5 then begin
|
||||||
|
if Y = 0 then R [X] := RorByte (R [X], 4)
|
||||||
|
else R [X] := RorByte (R [X], Y);
|
||||||
|
end
|
||||||
//Nand
|
//Nand
|
||||||
else if Op = 6 then R [X] := not (R [X] and R [Y])
|
else if Op = 6 then R [X] := not (R [X] and R [Y])
|
||||||
//And
|
//And
|
||||||
|
@ -420,8 +1045,17 @@ begin
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{$ifdef modem}
|
||||||
|
//Disconnect the modem
|
||||||
|
if Mode = Originate then if Connected then CloseSocket (ServerSocket);
|
||||||
|
if Mode = Answer then if Connected then begin
|
||||||
|
CloseSocket (ClientSocket);
|
||||||
|
CloseSocket (ListenSocket);
|
||||||
|
end;
|
||||||
|
{$endif}
|
||||||
|
|
||||||
{$ifndef fast}
|
{$ifndef fast}
|
||||||
wait;
|
wait (1);
|
||||||
{$endif}
|
{$endif}
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
|
@ -16,10 +16,7 @@
|
||||||
|
|
||||||
;Convert and print the high nibble
|
;Convert and print the high nibble
|
||||||
;Convert
|
;Convert
|
||||||
ror r0
|
ror r0, 4
|
||||||
ror r0
|
|
||||||
ror r0
|
|
||||||
ror r0
|
|
||||||
cleq r0, r0, n2hex
|
cleq r0, r0, n2hex
|
||||||
;Print
|
;Print
|
||||||
store ffff, r0
|
store ffff, r0
|
||||||
|
|
|
@ -41,8 +41,8 @@ inloop: load r1, ffff
|
||||||
load r2, bfsize
|
load r2, bfsize
|
||||||
brneq r0, r2, chstor
|
brneq r0, r2, chstor
|
||||||
|
|
||||||
;Backtrack if at the buffer end
|
;Ignore the input and print an underscore if at the buffer end
|
||||||
load r2, #8
|
load r2, #5f
|
||||||
store ffff, r2
|
store ffff, r2
|
||||||
breq r0, r0, inloop
|
breq r0, r0, inloop
|
||||||
|
|
||||||
|
@ -228,11 +228,11 @@ sumlop: xor r1, r1
|
||||||
xor r1, r1
|
xor r1, r1
|
||||||
xor r1, r2
|
xor r1, r2
|
||||||
;Shift the carry
|
;Shift the carry
|
||||||
shl r2
|
shl r2, 1
|
||||||
|
|
||||||
;Check for and store overflow if any
|
;Check for and store overflow if any
|
||||||
;Check
|
;Check
|
||||||
rol r1
|
rol r1, 1
|
||||||
breq r1, r2, nvrflw
|
breq r1, r2, nvrflw
|
||||||
;Store
|
;Store
|
||||||
load r1, #1
|
load r1, #1
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
program FloppyCtl;
|
||||||
|
|
||||||
|
{$MODE OBJFPC}
|
||||||
|
|
||||||
|
uses SysUtils;
|
||||||
|
|
||||||
|
var
|
||||||
|
Disc0, Disc1: shortstring; //States of the floppy discs
|
||||||
|
State: file of shortstring; //File storing the states of the floppy discs
|
||||||
|
Set0, Set1: integer; //Whether to (re)set the discs
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
//Check the arguments
|
||||||
|
if ParamCount > 4 then begin
|
||||||
|
writeln ('Usage: floppyctl (-0 disc) (-1 disc)');
|
||||||
|
halt (1);
|
||||||
|
end
|
||||||
|
else if ParamCount mod 2 <> 0 then begin
|
||||||
|
writeln ('Usage: floppyctl (-0 disc) (-1 disc)');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
if ParamStr (1) = '-0' then begin
|
||||||
|
Set0 := 2;
|
||||||
|
Set1 := 0;
|
||||||
|
if ParamStr (3) = '-1' then begin
|
||||||
|
Set1 := 4;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if ParamStr (1) = '-1' then begin
|
||||||
|
Set0 := 0;
|
||||||
|
Set1 := 2;
|
||||||
|
if ParamStr (3) = '-0' then Set0 := 4;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
writeln ('Usage: floppyctl (-0 disc) (-1 disc)');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Assign the state file
|
||||||
|
assign (State, ExpandFileName ('~/.thingamajig/discs'));
|
||||||
|
|
||||||
|
//Read existing state if any
|
||||||
|
if FileExists (ExpandFileName ('~/.thingamajig/discs')) then begin
|
||||||
|
try
|
||||||
|
reset (State);
|
||||||
|
read (State, Disc0);
|
||||||
|
read (State, Disc1);
|
||||||
|
close (State);
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
//Or else assign a default state
|
||||||
|
else begin
|
||||||
|
Disc0 := '';
|
||||||
|
Disc1 := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Get the files
|
||||||
|
if Set0 <> 0 then Disc0 := ExpandFileName (ParamStr (Set0));
|
||||||
|
if Set1 <> 0 then Disc1 := ExpandFileName (ParamStr (Set1));
|
||||||
|
|
||||||
|
//Write the state
|
||||||
|
try
|
||||||
|
rewrite (State);
|
||||||
|
write (State, Disc0);
|
||||||
|
write (State, Disc1);
|
||||||
|
close (State);
|
||||||
|
except
|
||||||
|
writeln ('Error: could not set the disc(s)');
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
|
@ -0,0 +1,118 @@
|
||||||
|
program ModemCtl;
|
||||||
|
|
||||||
|
{$MODE OBJFPC}
|
||||||
|
|
||||||
|
uses SysUtils, StrUtils, Sockets;
|
||||||
|
|
||||||
|
type
|
||||||
|
//Connection state
|
||||||
|
Connection = record
|
||||||
|
Originate: boolean;
|
||||||
|
Answer: boolean;
|
||||||
|
Dial: boolean;
|
||||||
|
Addr: longword;
|
||||||
|
Port: word;
|
||||||
|
Hang: boolean;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
ConnVar: Connection; //State of the modem
|
||||||
|
ConnFile: file of Connection; //File storing the state of the modem
|
||||||
|
Addr: in_addr; //Temporary variable
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
//Check the arguments
|
||||||
|
if ParamStr (1) = '-a' then begin
|
||||||
|
if ParamCount <> 2 then begin
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if ParamStr (1) = '-d' then begin
|
||||||
|
if ParamCount <> 2 then begin
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
if ParamCount > 1 then begin
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Assign the state file
|
||||||
|
assign (ConnFile, ExpandFileName ('~/.thingamajig/connection'));
|
||||||
|
|
||||||
|
//Set the connection
|
||||||
|
if ParamStr (1) = '-o' then begin
|
||||||
|
ConnVar.Originate := true;
|
||||||
|
ConnVar.Answer := false;
|
||||||
|
ConnVar.Dial := false;
|
||||||
|
ConnVar.Addr := 0;
|
||||||
|
ConnVar.Port := 0;
|
||||||
|
ConnVar.Hang := false;
|
||||||
|
end
|
||||||
|
else if ParamStr (1) = '-a' then begin
|
||||||
|
ConnVar.Originate := false;
|
||||||
|
ConnVar.Answer := true;
|
||||||
|
ConnVar.Dial := false;
|
||||||
|
try
|
||||||
|
Addr := StrToHostAddr (ExtractDelimited (1, ParamStr (2), [':']));
|
||||||
|
ConnVar.Addr := Addr.s_addr;
|
||||||
|
except
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
try
|
||||||
|
ConnVar.Port := StrToInt (ExtractDelimited (2, ParamStr (2), [':']));
|
||||||
|
except
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
ConnVar.Hang := false;
|
||||||
|
ConnVar.Hang := false;
|
||||||
|
end
|
||||||
|
else if ParamStr (1) = '-d' then begin
|
||||||
|
ConnVar.Originate := false;
|
||||||
|
ConnVar.Answer := false;
|
||||||
|
ConnVar.Dial := true;
|
||||||
|
try
|
||||||
|
Addr := StrToHostAddr (ExtractDelimited (1, ParamStr (2), [':']));
|
||||||
|
ConnVar.Addr := Addr.s_addr;
|
||||||
|
except
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
try
|
||||||
|
ConnVar.Port := StrToInt (ExtractDelimited (2, ParamStr (2), [':']));
|
||||||
|
except
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
ConnVar.Hang := false;
|
||||||
|
end
|
||||||
|
else if ParamStr (1) = '-h' then begin
|
||||||
|
ConnVar.Originate := false;
|
||||||
|
ConnVar.Answer := false;
|
||||||
|
ConnVar.Dial := false;
|
||||||
|
ConnVar.Addr := 0;
|
||||||
|
ConnVar.Port := 0;
|
||||||
|
ConnVar.Hang := true;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
writeln ('Usage: modemctl -o/[-a address:port]/[-d address:port]/-h');
|
||||||
|
halt (1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
//Write the state
|
||||||
|
try
|
||||||
|
rewrite (ConnFile);
|
||||||
|
write (ConnFile, ConnVar);
|
||||||
|
close (ConnFile);
|
||||||
|
except
|
||||||
|
writeln ('Error: could not set the connection');
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
170
readme.md
170
readme.md
|
@ -1,18 +1,17 @@
|
||||||
Thingamajig v1.0
|
Thingamajig v2.0
|
||||||
================
|
================
|
||||||
|
|
||||||
Thingamajig is a RISC/MISC homebrew computer architecture. Its git
|
Thingamajig v2.0 is a RISC/MISC homebrew computer architecture. Its git
|
||||||
repository can be found at
|
repository can be found at
|
||||||
https://ahti.space/git/crazyettin/Thingamajig.
|
https://ahti.space/git/crazyettin/Thingamajig.
|
||||||
|
|
||||||
Included Software
|
Included Software
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The repository includes an emulator implementation of Thingamajig with a
|
The repository includes an emulator implementation of Thingamajig with
|
||||||
control program for the emulated punched tape reader and punch, and an
|
device control programs, and an assembler and a disassembler, all
|
||||||
assembler and a disassembler, all written in FreePascal. It also
|
written in FreePascal. It also includes couple of simple example
|
||||||
includes couple of simple example programs for Thingamajig written in
|
programs for Thingamajig written in assembly.
|
||||||
assembly.
|
|
||||||
|
|
||||||
Registers and Memory
|
Registers and Memory
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -23,9 +22,12 @@ Registers and Memory
|
||||||
* 8-bit memory locations 0-FFFF
|
* 8-bit memory locations 0-FFFF
|
||||||
|
|
||||||
Multi-byte values are big-endian. Memory locations 0-FFEF are used for
|
Multi-byte values are big-endian. Memory locations 0-FFEF are used for
|
||||||
RAM while FFF0-FFFF are reserved for memory mapped devices. Input and
|
RAM while FFF0-FFFF are reserved for memory mapped devices.
|
||||||
output are mapped to address FFFF, while arbitrary devices can be mapped
|
|
||||||
to the other reserved addresses.
|
Input and output are mapped to address FFFF, while arbitrary devices can
|
||||||
|
be mapped to the other reserved addresses. When interacting with memory
|
||||||
|
mapped devices Thingamajig will stop processing to wait for the device
|
||||||
|
to be ready if needed.
|
||||||
|
|
||||||
Instructions
|
Instructions
|
||||||
------------
|
------------
|
||||||
|
@ -37,10 +39,10 @@ instruction pointer is incremented before being accessed or modified.
|
||||||
0 HALT
|
0 HALT
|
||||||
1 RET IP = *RP; RP += 2
|
1 RET IP = *RP; RP += 2
|
||||||
|
|
||||||
2 SHL RX RX <<= 1 Logical shifts
|
2 SHL RX, N RX <<= N (logical) Shifts of 1-4 steps,
|
||||||
3 SHR RX RX >>= 1
|
3 SHR RX, N RX >>= N (logical) with 4 encoded as 0
|
||||||
4 ROL RX RX <<= 1 Rotating shifts
|
4 ROL RX, N RX <<= N (rotating) in machine code.
|
||||||
5 ROR RX RX >>= 1
|
5 ROR RX, N RX >>= N (rotating)
|
||||||
|
|
||||||
6 NAND RX, RY RX = ~(RX & RY)
|
6 NAND RX, RY RX = ~(RX & RY)
|
||||||
7 AND RX, RY RX &= RY
|
7 AND RX, RY RX &= RY
|
||||||
|
@ -74,11 +76,11 @@ relative to a label. Relative references are of the form LABEL +/- N;
|
||||||
the spacing is optional.
|
the spacing is optional.
|
||||||
|
|
||||||
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 sets the location of the following code and
|
||||||
can only occur as the first instruction and cannot have a label, and is
|
data; as it has no direct equivalent in machine code it cannot have a
|
||||||
not required if the starting address is 0. DATA introduces a byte of
|
label. The default starting address of 0 does not need to be indicated
|
||||||
data. ADDR introduces two bytes of data containing the address of a
|
with ORG. DATA introduces a byte of data. ADDR introduces two bytes of
|
||||||
reference to or relative to a label.
|
data containing the address of a reference to or relative to a label.
|
||||||
|
|
||||||
Boot
|
Boot
|
||||||
----
|
----
|
||||||
|
@ -89,32 +91,118 @@ implementation has a front panel the IPL is optional. The instruction
|
||||||
and return pointers are initialised as 0 and the first address after RAM
|
and return pointers are initialised as 0 and the first address after RAM
|
||||||
respectively, while other registers and RAM are uninitialised.
|
respectively, while other registers and RAM are uninitialised.
|
||||||
|
|
||||||
Emulator
|
Emulator and Device Control Programs
|
||||||
--------
|
------------------------------------
|
||||||
|
|
||||||
By default the emulator runs at roughly 500 KIPS, has 2 KiB of RAM, and
|
Usage:
|
||||||
interacts with memory mapped devices at roughly 1000 B/s. The arguments
|
* emulator (-v) program (2> verbose_output)
|
||||||
-dRAM4, -dRAM8, -dRAM16, -dRAM32, and -dRAM64 can be used to compile
|
* tapectl (-r tape) (-p tape)
|
||||||
the emulator with 4, 8, 16, 32, or 64 KiB (minus the reserved addresses)
|
* floppyctl (-0 disc) (-1 disc)
|
||||||
of RAM respectively instead and the speed limitations can be removed
|
* modemctl -o/[-a address:port]/[-d address:port]/-h
|
||||||
with the argument -dfast.
|
|
||||||
|
|
||||||
Input and output are handled by an emulated glass teletype terminal with
|
By default the emulator runs at roughly 500 KIPS and has 2 KiB of RAM.
|
||||||
local echo on by default. Of the control characters bell (^G),
|
The arguments -dRAM4, -dRAM8, -dRAM16, -dRAM32, and -dRAM64 can be used
|
||||||
backspace (^H), line feed (^J), carriage return (^M), and device control
|
to compile the emulator with 4, 8, 16, 32, or 64 KiB (minus the reserved
|
||||||
characters two (^R) and four (^T) are used by the terminal: the device
|
addresses) of RAM respectively instead and the speed limitations can be
|
||||||
control characters are used to turn the local echo on and off
|
removed with the argument -dfast. When run with the argument -v the
|
||||||
respectively while the rest have their standard uses. The backspace and
|
current state of the registers is output to stderr before each
|
||||||
delete keys input their respective characters and non-character keys
|
instruction.
|
||||||
null.
|
|
||||||
|
Input and output are handled by an emulated roughly 1000 CPS
|
||||||
|
ASCII-compatible glass teletype terminal with local echo on by default.
|
||||||
|
Of the control characters bell (^G), backspace (^H), line feed (^J),
|
||||||
|
carriage return (^M), and device control characters two (^R) and four
|
||||||
|
(^T) are used by the terminal: the device control characters are used to
|
||||||
|
turn the local echo on and off respectively while the rest have their
|
||||||
|
standard uses. The backspace and delete keys input their respective
|
||||||
|
characters and non-character keys 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, an emulated high speed 8-bit paper tape reader and punch, an
|
||||||
-dprinter and -dtape respectively. The printer is mapped to address FFFE
|
emulated two-drive 8" floppy disc system, a modem, and an input status
|
||||||
and the tape reader and punch to FFFD. The printer prints into
|
register with the arguments -dprinter, -dtape, -dfloppy, -dmodem, and
|
||||||
/dev/usb/lp0 and the tape files read from and punched to are (re)set
|
-dstatus respectively. Full 64 KiB of RAM and all of these options can
|
||||||
using the program tapectl.
|
also be enabled with the argument -dfull. The printer is mapped to
|
||||||
|
address FFFE, the tape reader and punch to FFFD, the disc system to FFFB
|
||||||
|
and FFFC, the modem to FFF9 and FFFA, and the input status register to
|
||||||
|
FFF8.
|
||||||
|
|
||||||
The IPL loads a program from a file specified when launching the
|
The printer prints into /dev/usb/lp0. The tape files read from and
|
||||||
|
punched to are (re)set using the program tapectl with the arguments -r
|
||||||
|
and -p respectively and the disc files in drives 0 and 1 using the
|
||||||
|
program floppyctl with the arguments -0 and -1 respectively. The disc
|
||||||
|
system uses hard sectored single-sided discs with 77 tracks of 32
|
||||||
|
sectors of 137 bytes, or 337568 bytes in total: the disc files must be
|
||||||
|
of this size. The modem is controlled by the program modemctl: the
|
||||||
|
options -o and -a are used to set the originate and answer modes, the
|
||||||
|
latter with the address and port to be listened; -d to dial an address
|
||||||
|
and port to be called; and -h to hang. Dialing when in answer mode
|
||||||
|
automatically switches the modem to originate mode. The modem hangs
|
||||||
|
automatically if the connection is lost or hanged from the other side,
|
||||||
|
if another address and port are dialed, if a mode change command is
|
||||||
|
given, or if the emulator is halted.
|
||||||
|
|
||||||
|
The floppy disc system uses two ports: command at FFFB and data at FFFC.
|
||||||
|
Only the low nibble of the command port is used, consisting of a
|
||||||
|
three-bit command followed by a one-bit drive number used as an argument
|
||||||
|
by instructions 1 and 4-7. The track or sector to be accessed is input
|
||||||
|
to and the buffer accessed sequentially through the data port after the
|
||||||
|
relevant command is input to the command port. New commands other than
|
||||||
|
resetting the system are ignored until the current command is fully
|
||||||
|
executed. Note that each sector begins with a synchronisation bit that
|
||||||
|
must always be set.
|
||||||
|
|
||||||
|
The commands for the disc system are:
|
||||||
|
0: Reset the system.
|
||||||
|
1: Format a disc.
|
||||||
|
2: Read a sector from the buffer to the computer.
|
||||||
|
3: Write a sector from the computer to the buffer.
|
||||||
|
4: Set the track to be accessed.
|
||||||
|
5: Set the sector to be accessed.
|
||||||
|
6: Read a sector from a disc to the buffer.
|
||||||
|
7: Write a sector from the buffer to a disc.
|
||||||
|
|
||||||
|
The modem also uses two ports: status at FFF9 and data at FFFA. Only the
|
||||||
|
least significant bit of the status port is used, indicating whether
|
||||||
|
the modem is connected or not. Unsetting the bit hangs the modem while
|
||||||
|
setting it in answer mode hangs any current call and listens for an
|
||||||
|
incoming call to answer. Setting the bit in originate mode is ignored.
|
||||||
|
|
||||||
|
The input status register can be used to check if a device is ready to
|
||||||
|
be read from. The reserved addresses FFF8-FFFF are mapped to the bits
|
||||||
|
7-0: a set bit means either that the device is ready or that no input
|
||||||
|
device is mapped to the address in question.
|
||||||
|
|
||||||
|
The IPL loads the program specified as an argument when running the
|
||||||
emulator.
|
emulator.
|
||||||
|
|
||||||
|
Assembler and Disassembler
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
* assembler program (< input)
|
||||||
|
* disassembler program (> output)
|
||||||
|
|
||||||
|
Both the assembler and the disassembler are run with a program as their
|
||||||
|
sole argument: they take their input from and print their output to
|
||||||
|
stdin and stdout respectively.
|
||||||
|
|
||||||
|
An initial gap created with ORG is not included in an assembled program.
|
||||||
|
All possible interpretations of a disassembled program are included in
|
||||||
|
its listing, resulting in overlapping information.
|
||||||
|
|
||||||
|
Changelog
|
||||||
|
---------
|
||||||
|
|
||||||
|
v2.0
|
||||||
|
* Added the ability to shift a register multiple steps in one
|
||||||
|
instruction
|
||||||
|
* Changed the register argument of STORE from RX to RY
|
||||||
|
* Added support for an emulated floppy disc system and modem and an
|
||||||
|
input status register to the emulator
|
||||||
|
* Added the ability to use ORG non-initially to the assembler
|
||||||
|
* Changed tapectl so that it uses arguments for input
|
||||||
|
|
||||||
|
v1.0
|
||||||
|
* Initial release
|
||||||
|
|
52
tapectl.pas
52
tapectl.pas
|
@ -15,37 +15,41 @@ type
|
||||||
var
|
var
|
||||||
Reader, Punch: Tape; //States of the reader and punch
|
Reader, Punch: Tape; //States of the reader and punch
|
||||||
State: file of Tape; //File storing the states of the reader and punch
|
State: file of Tape; //File storing the states of the reader and punch
|
||||||
DoRead, DoPunch: boolean; //Whether to reset the reader or the punch
|
DoRead, DoPunch: integer; //Whether to (re)set the reader or the punch
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
||||||
//Check whether to set the reader, the punch, or both
|
//Check the arguments
|
||||||
if ParamCount <> 1 then begin
|
if ParamCount > 4 then begin
|
||||||
writeln ('Usage: tapectl reader/punch/both');
|
writeln ('Usage: tapectl (-r tape) (-p tape)');
|
||||||
|
halt (1);
|
||||||
|
end
|
||||||
|
else if ParamCount mod 2 <> 0 then begin
|
||||||
|
writeln ('Usage: tapectl (-r tape) (-p tape)');
|
||||||
halt (1);
|
halt (1);
|
||||||
end;
|
end;
|
||||||
if ParamStr (1) = 'reader' then begin
|
if ParamStr (1) = '-r' then begin
|
||||||
DoRead := true;
|
DoRead := 2;
|
||||||
DoPunch := false;
|
DoPunch := 0;
|
||||||
|
if ParamStr (3) = '-p' then begin
|
||||||
|
DoPunch := 4;
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else if ParamStr (1) = 'punch' then begin
|
else if ParamStr (1) = '-p' then begin
|
||||||
DoRead := false;
|
DoRead := 0;
|
||||||
DoPunch := true;
|
DoPunch := 2;
|
||||||
end
|
if ParamStr (3) = '-r' then DoRead := 4;
|
||||||
else if ParamStr (1) = 'both' then begin
|
|
||||||
DoRead := true;
|
|
||||||
DoPunch := true;
|
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
writeln ('Usage: tapectl reader/punch/both');
|
writeln ('Usage: tapectl (-r tape) (-p tape)');
|
||||||
halt (1);
|
halt (1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//Assign the state file
|
//Assign the state file
|
||||||
assign (State, ExpandFileName ('~/.tapes.thingamajig'));
|
assign (State, ExpandFileName ('~/.thingamajig/tapes'));
|
||||||
|
|
||||||
//Read existing state if any
|
//Read existing state if any
|
||||||
if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin
|
if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin
|
||||||
try
|
try
|
||||||
reset (State);
|
reset (State);
|
||||||
read (State, Reader);
|
read (State, Reader);
|
||||||
|
@ -64,18 +68,14 @@ begin
|
||||||
Punch.Pos := 0;
|
Punch.Pos := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//Input the files to be read from or punched to
|
//Get the files to be read from or punched to
|
||||||
if DoRead then begin
|
if DoRead <> 0 then begin
|
||||||
write ('Reader: ');
|
Reader.Path := ExpandFileName (ParamStr (DoRead));
|
||||||
readln (Reader.Path);
|
|
||||||
Reader.Path := ExpandFileName (Reader.Path);
|
|
||||||
Reader.Reset := true;
|
Reader.Reset := true;
|
||||||
Reader.Pos := 0;
|
Reader.Pos := 0;
|
||||||
end;
|
end;
|
||||||
if DoPunch then begin
|
if DoPunch <> 0 then begin
|
||||||
write ('Punch: ');
|
Punch.Path := ExpandFileName (ParamStr (DoPunch));
|
||||||
readln (Punch.Path);
|
|
||||||
Punch.Path := ExpandFileName (Punch.Path);
|
|
||||||
Punch.Reset := true;
|
Punch.Reset := true;
|
||||||
Punch.Pos := 0;
|
Punch.Pos := 0;
|
||||||
end;
|
end;
|
||||||
|
|
Loading…
Reference in New Issue