diff --git a/emulator.pas b/emulator.pas index 376a88d..702cbaa 100644 --- a/emulator.pas +++ b/emulator.pas @@ -4,6 +4,16 @@ program Emulator; uses SysUtils, Crt; +{$ifdef tape} +type + //Tape file path and reset state + Tape = record + Path: shortstring; + Reset: boolean; + Pos: integer; + end; +{$endif} + const IO = $ffff; @@ -14,10 +24,10 @@ var Addr, IP, RP: word; //Address argument and instruction and return pointers R: array [0 .. 3] of byte; //General-purpose registers Mem: array [0 .. $ffef] of byte; //Memory - Prog{$ifdef printer}, Prn{$endif}{$ifdef tape}, TapeIn, TapeOut{$endif}: file of byte; //Program file, line printer, and punched tape reader and punch + 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} - TapeInFile: string; //Punched tape reader source file - TapeInPos: integer; //Punched tape reader position pointer + Reader, Punch: Tape; //States of the tape reader and punch + State: file of Tape; //File storing the states of the tape reader and punch {$endif} Ch, Scan: ansichar; //Character for input and output and scancode for non-ASCII keys @@ -27,9 +37,15 @@ begin Hlt := false; IP := 0; RP := $fff0; + + //Initialise the tape reader and punch {$ifdef tape} - TapeInFile := ' '; - TapeInPos := 0; + Reader.Path := ''; + Reader.Reset := true; + Reader.Pos := 0; + Punch.Path := ''; + Punch.Reset := true; + Punch.Pos := 0; {$endif} //Read a program file and check for errors @@ -144,17 +160,6 @@ begin Ch := ansichar ($7f); ASCII := true; end - {$ifdef tape} - //The insert key sets a tape to be read - else if Scan = ansichar ($52) then begin - writeln (); - write ('Tape to be read: '); - readln (TapeInFile); - if TapeInFile = '' then TapeInFile := ' '; - TapeInPos := 0; - ASCII := false; - end - {$endif} //Unused function keys insert a null else ASCII := true; end @@ -188,16 +193,39 @@ begin //Tape reader {$ifdef tape} else if Addr = $fffd then begin - assign (TapeIn, TapeInFile); + 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, TapeInPos); + seek (TapeIn, Reader.Pos); read (TapeIn, R [X]); - TapeInPos := TapeInPos + 1; close (TapeIn); + Reader.Pos := Reader.Pos + 1; except R [X] := 0; 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 @@ -225,26 +253,52 @@ begin //Tape punch {$ifdef tape} else if Addr = $fffd then begin - assign (TapeOut, 'tapeout'); - if FileExists ('tapeout') = false then begin + assign (State, ExpandFileName ('~/.tapes.thingamajig')); + //Check the punch state + if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin try - rewrite (TapeOut); - write (TapeOut, R [X]); - close (TapeOut); + reset (State); + read (State, Reader); + read (State, Punch); + close (State); except end; - end - else begin + end; + //Punch + if Punch.Path <> '' then begin + assign (TapeOut, Punch.Path); + if Punch.Reset then begin + try + rewrite (TapeOut); + write (TapeOut, R [X]); + close (TapeOut); + Punch.Reset := false; + except + end; + end + else begin + try + reset (TapeOut); + seek (TapeOut, FileSize (TapeOut)); + write (TapeOut, R [X]); + close (TapeOut); + except + end; + end; + end; + //Save the punch state + if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin try - reset (TapeOut); - seek (TapeOut, FileSize (TapeOut)); - write (TapeOut, R [X]); - close (TapeOut); + rewrite (State); + write (State, Reader); + write (State, Punch); + close (State); except end; end; end {$endif} + //Regular store else Mem [Addr] := R [X]; end diff --git a/readme.md b/readme.md index 78d9f05..8d5457f 100644 --- a/readme.md +++ b/readme.md @@ -8,10 +8,11 @@ https://ahti.space/git/crazyettin/Thingamajig. Included Software ----------------- -The repository includes an emulator implementation of Thingamajig as -well as 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, a +control program for the emulated punched 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. Registers and Memory -------------------- @@ -91,14 +92,13 @@ this. Arbitrary devices can be mapped to the other reserved addresses. -The emulator can be compiled with support for a line printer and an -emulated punched tape reader and punch with the arguments -dprinter and --dtape respectively. The printer outputs into /dev/usb/lp0 and the tape -punch to tapeout. To (re)set the tape to be read press the insert key at -any input and enter a file name; the input will resume after this. The -printer is mapped to address FFFE in the emulator and the tape reader -and punch to FFFD. If you wish to use a different setup you have to -modify the code yourself. +In Linux the emulator can be compiled with support for a line printer +and an emulated punched tape reader and punch with the arguments +-dprinter and -dtape respectively. The printer prints into /dev/usb/lp0 +and the tape files read from and punched to are (re)set using the +settape program. The printer is mapped to address FFFE in the emulator +and the tape reader and punch to FFFD. If you wish to use a different +setup you have to modify the code yourself. Initial Program Loader ---------------------- diff --git a/settape.pas b/settape.pas new file mode 100644 index 0000000..9e06228 --- /dev/null +++ b/settape.pas @@ -0,0 +1,93 @@ +program Settape; + +{$MODE OBJFPC} + +uses SysUtils; + +type + //Tape file path and reset state + Tape = record + Path: shortstring; + Reset: boolean; + Pos: integer; + end; + +var + Reader, Punch: Tape; //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 + +begin + + //Check whether to set the reader, the punch, or both + if ParamCount <> 1 then begin + writeln ('Usage: settape reader/punch/both'); + halt; + end; + if ParamStr (1) = 'reader' then begin + DoRead := true; + DoPunch := false; + end + else if ParamStr (1) = 'punch' then begin + DoRead := false; + DoPunch := true; + end + else if ParamStr (1) = 'both' then begin + DoRead := true; + DoPunch := true; + end + else begin + writeln ('Usage: settape reader/punch/both'); + halt; + end; + + //Assign the state file + assign (State, ExpandFileName ('~/.tapes.thingamajig')); + + //Read existing state if any + if FileExists (ExpandFileName ('~/.tapes.thingamajig')) then begin + try + reset (State); + read (State, Reader); + read (State, Punch); + close (State); + except + end; + end + //Or else assign a default state + else begin + Reader.Path := ''; + Reader.Reset := true; + Reader.Pos := 0; + Punch.Path := ''; + Punch.Reset := true; + Punch.Pos := 0; + end; + + //Input the files to be read from or punched to + if DoRead then begin + write ('Reader: '); + readln (Reader.Path); + Reader.Path := ExpandFileName (Reader.Path); + Reader.Reset := true; + Reader.Pos := 0; + end; + if DoPunch then begin + write ('Punch: '); + readln (Punch.Path); + Punch.Path := ExpandFileName (Punch.Path); + Punch.Reset := true; + Punch.Pos := 0; + end; + + //Write the state + try + rewrite (State); + write (State, Reader); + write (State, Punch); + close (State); + except + writeln ('Error: could not set the tape(s)'); + end; + +end.