Add a floppy disc system option and tweak the timings to make them more accurate
This commit is contained in:
parent
0b07a2b214
commit
bf36aab7a4
3 changed files with 330 additions and 25 deletions
232
emulator.pas
232
emulator.pas
|
@ -31,20 +31,31 @@ const
|
|||
{$endif}
|
||||
|
||||
var
|
||||
Hlt, Echo: boolean; //Halt and echo flags
|
||||
Hlt, Echo{$ifdef floppy}, DiscRead, DiscWrite, DiscTrackSet, DiscSectSet{$endif}: boolean; //Halt and echo flags and disc system flags
|
||||
Op, Regs: 0 .. $f; //Opcode
|
||||
X, Y: 0 .. 3; //Register arguments
|
||||
Addr, IP, RP: word; //Immediate or address argument and instruction and return pointers
|
||||
R: array [0 .. 3] of byte; //General-purpose registers
|
||||
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}
|
||||
Reader, Punch: Tape; //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}
|
||||
Ch, Scan: ansichar; //Character for input and output and scancode for non-ASCII keys
|
||||
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}
|
||||
|
||||
//Terminal output
|
||||
procedure Output;
|
||||
|
@ -68,9 +79,9 @@ end;
|
|||
|
||||
{$ifndef fast}
|
||||
//Wait to emulate CPU speed of roughly 500 KIPS
|
||||
procedure wait;
|
||||
procedure wait (I: integer);
|
||||
begin
|
||||
if IC div 500 = 0 then sleep (1)
|
||||
if IC div 500 < I then sleep (I)
|
||||
else begin
|
||||
sleep (IC div 500);
|
||||
if IC mod 500 >= 250 then sleep (1);
|
||||
|
@ -87,7 +98,7 @@ begin
|
|||
//Terminal input
|
||||
if W = $ffff then begin
|
||||
{$ifndef fast}
|
||||
wait;
|
||||
wait (1);
|
||||
{$endif}
|
||||
//Read a keypress
|
||||
Ch := ReadKey;
|
||||
|
@ -116,8 +127,7 @@ begin
|
|||
//Tape reader
|
||||
else if W = $fffd then begin
|
||||
{$ifndef fast}
|
||||
wait;
|
||||
sleep (1);
|
||||
wait (2);
|
||||
{$endif}
|
||||
assign (Tapes, ExpandFileName ('~/.thingamajig/tapes'));
|
||||
//Check the reader state
|
||||
|
@ -153,6 +163,21 @@ begin
|
|||
end;
|
||||
end
|
||||
{$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}
|
||||
//Unused addresses
|
||||
else if W > LastRAM then B := 0
|
||||
//Regular load
|
||||
|
@ -166,7 +191,7 @@ begin
|
|||
//Terminal output
|
||||
if W = $ffff then begin
|
||||
{$ifndef fast}
|
||||
wait;
|
||||
wait (1);
|
||||
{$endif}
|
||||
Ch := ansichar (B);
|
||||
Output;
|
||||
|
@ -176,6 +201,9 @@ begin
|
|||
{$ifdef printer}
|
||||
//Printer
|
||||
else if W = $fffe then begin
|
||||
{$ifndef fast}
|
||||
wait (1);
|
||||
{$endif}
|
||||
assign (Prn, '/dev/usb/lp0');
|
||||
try
|
||||
rewrite (Prn);
|
||||
|
@ -189,8 +217,7 @@ begin
|
|||
//Tape punch
|
||||
else if W = $fffd then begin
|
||||
{$ifndef fast}
|
||||
wait;
|
||||
sleep (19);
|
||||
wait (20);
|
||||
{$endif}
|
||||
assign (Tapes, ExpandFileName ('~/.thingamajig/tapes'));
|
||||
//Check the punch state
|
||||
|
@ -246,6 +273,172 @@ begin
|
|||
end;
|
||||
end
|
||||
{$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 $c = 0 then begin
|
||||
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
|
||||
//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
|
||||
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);
|
||||
read (Disc, DiscSB [DiscSBP]);
|
||||
end;
|
||||
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}
|
||||
//Regular store
|
||||
else if W <= LastRAM then Mem [W] := B;
|
||||
end;
|
||||
|
@ -281,6 +474,21 @@ begin
|
|||
Punch.Pos := 0;
|
||||
{$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}
|
||||
|
||||
//Check the arguments
|
||||
if ParamCount = 0 then begin
|
||||
writeln ('Usage: emulator (-v) program (2> verbose_output)');
|
||||
|
@ -443,7 +651,7 @@ begin
|
|||
end;
|
||||
|
||||
{$ifndef fast}
|
||||
wait;
|
||||
wait (1);
|
||||
{$endif}
|
||||
|
||||
end.
|
||||
|
|
73
floppyctl.pas
Normal file
73
floppyctl.pas
Normal file
|
@ -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.
|
50
readme.md
50
readme.md
|
@ -8,11 +8,10 @@ https://ahti.space/git/crazyettin/Thingamajig.
|
|||
Included Software
|
||||
-----------------
|
||||
|
||||
The repository includes an emulator implementation of Thingamajig with a
|
||||
control program for the emulated paper tape reader and punch, and an
|
||||
assembler and a disassembler, all written in FreePascal. It also
|
||||
includes couple of simple example programs for Thingamajig written in
|
||||
assembly.
|
||||
The repository includes an emulator implementation of Thingamajig with
|
||||
storage device control programs, and an assembler and a disassembler,
|
||||
all written in FreePascal. It also includes couple of simple example
|
||||
programs for Thingamajig written in assembly.
|
||||
|
||||
Registers and Memory
|
||||
--------------------
|
||||
|
@ -92,12 +91,13 @@ implementation has a front panel the IPL is optional. The instruction
|
|||
and return pointers are initialised as 0 and the first address after RAM
|
||||
respectively, while other registers and RAM are uninitialised.
|
||||
|
||||
Emulator and Tapectl
|
||||
--------------------
|
||||
Emulator and Storage Device Control Programs
|
||||
--------------------------------------------
|
||||
|
||||
Usage:
|
||||
* emulator (-v) program (2> verbose_output)
|
||||
* tapectl (-r tape) (-p tape)
|
||||
* floppyctl (-0 disc) (-1 disc)
|
||||
|
||||
By default the emulator runs at roughly 500 KIPS and has 2 KiB of RAM.
|
||||
The arguments -dRAM4, -dRAM8, -dRAM16, -dRAM32, and -dRAM64 can be used
|
||||
|
@ -117,12 +117,36 @@ 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
|
||||
printer and an emulated high speed (roughly 500 CPS in and 50 CPS out)
|
||||
8-bit paper tape reader and punch with the arguments -dprinter and
|
||||
-dtape respectively. The printer is mapped to address FFFE 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 using the program
|
||||
tapectl with the arguments -r and -p respectively.
|
||||
printer, an emulated high speed (roughly 500 CPS in and 50 CPS out)
|
||||
8-bit paper tape reader and punch, and an emulated two-drive 8" floppy
|
||||
disc system with the arguments -dprinter, -dtape, and -dfloppy
|
||||
respectively. The printer is mapped to address FFFE, the tape reader and
|
||||
punch to FFFD, and the disc system to FFFB and FFFC. 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 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 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 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.
|
||||
|
||||
The commands for the disc system are:
|
||||
0: Reset the system.
|
||||
1: "
|
||||
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 the disc to the buffer.
|
||||
7: Write a sector from the buffer to the disc.
|
||||
|
||||
The IPL loads the program specified as an argument when running the
|
||||
emulator.
|
||||
|
|
Loading…
Add table
Reference in a new issue