diff --git a/emulator.pas b/emulator.pas index cb07615..77e697d 100644 --- a/emulator.pas +++ b/emulator.pas @@ -2,17 +2,35 @@ program Emulator; {$MODE OBJFPC} -uses SysUtils, Crt; +{$ifdef full} +{$define RAM64} +{$define printer} +{$define tape} +{$define floppy} +{$define modem} +{$endif} + +uses SysUtils, Crt{$ifdef modem}, Sockets{$endif}; -{$ifdef tape} type + {$ifdef tape} //Tape file path and reset state Tape = record Path: shortstring; Reset: boolean; Pos: integer; end; -{$endif} + {$endif} + {$ifdef modem} + //Modem connection state + Connection = record + Call: boolean; + Addr: longword; + Port: word; + Answer: boolean; + Hang: boolean; + end; + {$endif} const //The last address of RAM @@ -31,7 +49,7 @@ const {$endif} var - Hlt, Echo{$ifdef floppy}, DiscRead, DiscWrite, DiscTrackSet, DiscSectSet{$endif}: boolean; //Halt and echo flags and disc system flags + Hlt, Echo{$ifdef floppy}, DiscRead, DiscWrite, DiscTrackSet, DiscSectSet{$endif}{$ifdef modem}, Calling{$endif}: boolean; //Halt and echo flags, disc system flags, and modem connection flag Op, Regs: 0 .. $f; //Opcode X, Y: 0 .. 3; //Register arguments Addr, IP, RP: word; //Immediate or address argument and instruction and return pointers @@ -56,6 +74,12 @@ var {$ifdef floppy} DiscDrive: 0 .. 1; //Current disc drive number {$endif} + {$ifdef modem} + ModemConn: Connection; //State of the modem + ModemState: file of Connection; //File storing the state of the modem + ServerSocket: longint; //Server socket + ServerAddr: TInetSockAddr; //Server address + {$endif} //Terminal output procedure Output; @@ -90,6 +114,57 @@ begin end; {$endif} +{$ifdef modem} +//Connect +procedure CallServer; +begin + assign (ModemState, ExpandFileName ('~/.thingamajig/connection')); + //Check the modem state + if FileExists (ExpandFileName ('~/.thingamajig/connection')) then begin + try + reset (ModemState); + read (ModemState, ModemConn); + close (ModemState); + except + end; + end; + //Hang + if ModemConn.Hang then if Calling then begin + CloseSocket (ServerSocket); + ModemConn.Hang := false; + Calling := false; + end + else if ModemConn.Call then if Calling then begin + CloseSocket (ServerSocket); + Calling := false; + end; + //Call + if ModemConn.Call then if Calling = false then begin + //Create a socket + ServerSocket := fpSocket (AF_INET, SOCK_STREAM, 0); + if ServerSocket <> -1 then begin + //Connect + ServerAddr.sin_family := AF_INET; + ServerAddr.sin_port := htons (ModemConn.Port); + ServerAddr.sin_addr.s_addr := htonl (ModemConn.Addr); + if fpconnect (ServerSocket, @ServerAddr, Sizeof (ServerAddr)) <> -1 then begin + ModemConn.Call := false; + Calling := true; + end; + end; + end; + //Save the modem state + if FileExists (ExpandFileName ('~/.thingamajig/connection')) then begin + try + rewrite (ModemState); + write (ModemState, ModemConn); + close (ModemState); + except + end; + end; +end; +{$endif} + //Load a byte from memory function LoadByte (W: word): byte; var @@ -178,6 +253,19 @@ begin else B := 0; end {$endif} + {$ifdef modem} + //Modem + else if W = $fffa then begin + {$ifndef fast} + wait (26); + {$endif} + //Connect + CallServer; + //Recieve + if Calling then fpRecv (ServerSocket, @B, 1, 0) + else B := 0; + end + {$endif} //Unused addresses else if W > LastRAM then B := 0 //Regular load @@ -515,6 +603,18 @@ begin end; end {$endif} + {$ifdef modem} + //Modem + else if W = $fffa then begin + {$ifndef fast} + wait (26); + {$endif} + //Connect + CallServer; + //Send + if Calling then fpSend (ServerSocket, @B, 1, 0); + end + {$endif} //Regular store else if W <= LastRAM then Mem [W] := B; end; @@ -565,6 +665,11 @@ begin Disc1Sect := 0; {$endif} + {$ifdef modem} + //Initialise the modem + Calling := false; + {$endif} + //Check the arguments if ParamCount = 0 then begin writeln ('Usage: emulator (-v) program (2> verbose_output)'); @@ -726,6 +831,11 @@ begin end; + {$ifdef modem} + //Disconnect the modem + if Calling then CloseSocket (ServerSocket); + {$endif} + {$ifndef fast} wait (1); {$endif} diff --git a/modemctl.pas b/modemctl.pas new file mode 100644 index 0000000..dd27a2d --- /dev/null +++ b/modemctl.pas @@ -0,0 +1,106 @@ +program ModemCtl; + +{$MODE OBJFPC} + +uses SysUtils, StrUtils, Sockets; + +type + //Connection state + Connection = record + Call: boolean; + Addr: longword; + Port: word; + Answer: boolean; + Hang: boolean; + end; + +var + ModemConn: Connection; //State of the modem + State: file of Connection; //File storing the state of the modem + Addr: in_addr; //Temporary variable + +begin + + //Check the arguments + if ParamStr (1) = '-c' then begin + if ParamCount > 2 then begin + writeln ('Usage: modemctl [-c address:port]/-a/-h'); + halt (1); + end; + end + else begin + if ParamCount > 1 then begin + writeln ('Usage: modemctl [-c address:port]/-a/-h'); + halt (1); + end; + end; + + //Assign the state file + assign (State, ExpandFileName ('~/.thingamajig/connection')); + + //Read existing state if any + if FileExists (ExpandFileName ('~/.thingamajig/connection')) then begin + try + reset (State); + read (State, ModemConn); + close (State); + except + end; + end + //Or else assign a default state + else begin + ModemConn.Call := false; + ModemConn.Addr := 0; + ModemConn.Port := 0; + ModemConn.Answer := false; + ModemConn.Hang := false; + end; + + //Set the connection + if ParamStr (1) = '-c' then begin + ModemConn.Call := true; + try + Addr := StrToHostAddr (ExtractDelimited (1, ParamStr (2), [':'])); + ModemConn.Addr := Addr.s_addr; + except + writeln ('Usage: modemctl [-c address:port]/-a/-h'); + halt (1); + end; + try + ModemConn.Port := StrToInt (ExtractDelimited (2, ParamStr (2), [':'])); + except + writeln ('Usage: modemctl [-c address:port]/-a/-h'); + halt (1); + end; + ModemConn.Answer := false; + ModemConn.Hang := false; + end + else if ParamStr (1) = '-a' then begin + ModemConn.Call := false; + ModemConn.Addr := 0; + ModemConn.Port := 0; + ModemConn.Answer := true; + ModemConn.Hang := false; + end + else if ParamStr (1) = '-h' then begin + ModemConn.Call := false; + ModemConn.Addr := 0; + ModemConn.Port := 0; + ModemConn.Answer := false; + ModemConn.Hang := true; + end + else begin + writeln ('Usage: modemctl [-c address:port]/-a/-h'); + halt (1); + end; + + //Write the state + try + rewrite (State); + write (State, ModemConn); + close (State); + except + writeln ('Error: could not set the connection'); + end; + +end. diff --git a/readme.md b/readme.md index 949b72b..2a9bb35 100644 --- a/readme.md +++ b/readme.md @@ -9,8 +9,8 @@ Included Software ----------------- 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 +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 @@ -91,13 +91,14 @@ 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 Storage Device Control Programs --------------------------------------------- +Emulator and Device Control Programs +------------------------------------ Usage: * emulator (-v) program (2> verbose_output) * tapectl (-r tape) (-p tape) * floppyctl (-0 disc) (-1 disc) + * modemctl [-c address:port]/-a/-h 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 @@ -118,16 +119,23 @@ characters and non-character keys null. In Linux the emulator can be compiled with support for a character 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 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. +8-bit paper tape reader and punch, an emulated two-drive 8" floppy disc +system, and a roughly 300 b/s modem with the arguments -dprinter, +-dtape, -dfloppy, and -dmodem respectively. Full 64 KiB of RAM and all +of these options can 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, and the modem to FFFA. 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 option -c is +used to "call" an IP address and port, -a to set the modem ready for +"answering" "calls" to port 1337, and -h to "hang". "Hanging" manually +is not necessary when "calling" a different address or switching between +"calling" and "answering". Note: "Answering" is not functional yet. 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