Add a floppy disc system option and tweak the timings to make them more accurate

This commit is contained in:
CrazyEttin 2022-09-23 11:56:31 +03:00
parent 0b07a2b214
commit bf36aab7a4
3 changed files with 330 additions and 25 deletions

View File

@ -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
View 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.

View File

@ -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.