program Emulator; {$MODE OBJFPC} {$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} //Tape file path and reset state type Tape = record Path: shortstring; Reset: boolean; Pos: integer; end; {$endif} {$ifdef modem} //Modem connection state type Connection = record Originate: boolean; Answer: boolean; Dial: boolean; Addr: longword; Port: word; Hang: boolean; end; {$endif} const //The last address of RAM {$if defined(RAM4)} LastRAM = $fff; //4 KiB {$elseif defined(RAM8)} LastRAM = $1fff; //8 KiB {$elseif defined(RAM16)} LastRAM = $3fff; //16 KiB {$elseif defined(RAM32)} LastRAM = $7fff; //32 KiB {$elseif defined(RAM64)} LastRAM = $ffef; //64 KiB {$else} LastRAM = $7ff; //2 KiB (default) {$endif} var 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 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 {$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{$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 procedure Output; begin //Do not output most of the control codes if Ch <= ansichar ($1f) then begin if Ch = ansichar (7) then write (Ch) //Bell else if Ch = ansichar (8) then write (Ch) //Backspace else if Ch = ansichar ($a) then begin //Bodge for line feed LFX := WhereX; write (Ch); GotoXY (LFX, WhereY); end else if Ch = ansichar ($d) then write (Ch) //Carriage return else write (''); //Others end else if Ch = ansichar ($7f) then write ('') //Delete //Output all regular characters else write (Ch); end; {$ifndef fast} //Wait to emulate CPU speed of roughly 500 KIPS procedure wait (I: integer); begin if IC div 500 < I then sleep (I) else begin sleep (IC div 500); if IC mod 500 >= 250 then sleep (1); end; IC := 0; end; {$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 function LoadByte (W: word): byte; var B: byte; begin //Terminal input if W = $ffff then begin {$ifndef fast} wait (1); {$endif} //Read a keypress Ch := ReadKey; //Handle non-character keys if Ch = ansichar (0) then begin if keypressed then begin Scan := ReadKey; //The delete key inserts the delete character if Scan = ansichar ($53) then Ch := ansichar ($7f); end; end //Bodge for the CRT unit not working perfectly in Linux else if Ch <= ansichar ($7f) then begin if keypressed then begin Ch := ansichar (0); repeat scan := ReadKey; until keypressed = false; end; end; //Process the keypress if Echo then Output; //Local echo B := byte (Ch); end {$ifdef tape} //Tape reader else if W = $fffd then begin {$ifndef fast} wait (2); {$endif} assign (Tapes, ExpandFileName ('~/.thingamajig/tapes')); //Check the reader state if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin try reset (Tapes); read (Tapes, Reader); read (Tapes, Punch); close (Tapes); except end; end; //Read assign (TapeIn, Reader.Path); try reset (TapeIn); seek (TapeIn, Reader.Pos); read (TapeIn, B); close (TapeIn); Reader.Pos := Reader.Pos + 1; except B := $ff; end; //Save the reader state if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin try rewrite (Tapes); write (Tapes, Reader); write (Tapes, Punch); close (Tapes); except end; 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} {$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 else if W > LastRAM then B := 0 //Regular load else B := Mem [W]; //Result LoadByte := B; end; procedure StoreByte (W: word; B: byte); begin //Terminal output if W = $ffff then begin {$ifndef fast} wait (1); {$endif} Ch := ansichar (B); Output; if Ch = ansichar ($12) then Echo := true; if Ch = ansichar ($14) then Echo := false; end {$ifdef printer} //Printer else if W = $fffe then begin {$ifndef fast} wait (1); {$endif} assign (Prn, '/dev/usb/lp0'); try rewrite (Prn); write (Prn, B); close (Prn); except end; end {$endif} {$ifdef tape} //Tape punch else if W = $fffd then begin {$ifndef fast} wait (20); {$endif} assign (Tapes, ExpandFileName ('~/.thingamajig/tapes')); //Check the punch state if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin try reset (Tapes); read (Tapes, Reader); read (Tapes, Punch); close (Tapes); except end; end; //Punch if Punch.Path <> '' then begin assign (TapeOut, Punch.Path); if FileExists (Punch.Path) = false then begin try rewrite (TapeOut); write (TapeOut, B); close (TapeOut); Punch.Reset := false; except end; end else if Punch.Reset then begin try rewrite (TapeOut); write (TapeOut, B); close (TapeOut); Punch.Reset := false; except end; end else begin try reset (TapeOut); seek (TapeOut, FileSize (TapeOut)); write (TapeOut, B); close (TapeOut); except end; end; end; //Save the punch state if FileExists (ExpandFileName ('~/.thingamajig/tapes')) then begin try rewrite (Tapes); write (Tapes, Reader); write (Tapes, Punch); close (Tapes); except end; 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 $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 else if W <= LastRAM then Mem [W] := B; end; procedure Call; begin //Low byte of the return address RP := RP - 1; StoreByte (RP, IP and $ff); //High byte of the return address RP := RP - 1; StoreByte (RP, IP shr 8); //Call IP := Addr; end; begin //Initialise the halt and echo flags, the pointers, and the instruction counter Hlt := false; Echo := true; IP := 0; RP := LastRAM + 1; IC := 0; {$ifdef tape} //Initialise the tape reader and punch Reader.Path := ''; Reader.Reset := true; Reader.Pos := 0; Punch.Path := ''; Punch.Reset := true; 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} {$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 if ParamCount = 0 then begin writeln ('Usage: emulator (-v) program (2> verbose_output)'); halt (1); end; if ParamStr (1) = '-v' then begin 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 writeln ('Usage: emulator (-v) program (2> verbose_output)'); halt (1); end; end else begin Verbose := 0; if ParamCount <> 1 then begin writeln ('Usage: emulator (-v) program (2> verbose_output)'); halt (1); end; end; //Read a program file and check for errors {$i-} if Verbose = 1 then assign (Prog, ParamStr (2)) else assign (Prog, ParamStr (1)); reset (Prog); if FileSize (Prog) > LastRAM + 1 then begin writeln ('Error: program size cannot exceed ', LastRam + 1, ' bytes'); halt (1); end; {$i+} if IOResult <> 0 then begin writeln ('Error: program file cannot be read from'); halt (1); end; repeat read (Prog, Mem [IP]); IP := IP + 1; IC := IC + 1; until (eof (Prog)); //Reinitialise the instruction pointer IP := 0; //Begin the main loop while Hlt = false do begin //Print the CPU state to StdErr 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 opcode and register arguments Fetched := LoadByte (IP); //Decode the opcode Op := Fetched and $f0 shr 4; //Decode the register arguments Regs := Fetched and $f; X := Fetched and $c shr 2; Y := Fetched and 3; IP := IP + 1; //Immediate or address argument if Op >= $a then begin //Immediate or high byte of address Fetched := LoadByte (IP); Addr := Fetched; Addr := Addr shl 8; IP := IP + 1; //Low byte of address if Op = $a then begin if Y = 0 then begin Fetched := LoadByte (IP); Addr := Addr + Fetched; IP := IP + 1; end; end else begin Fetched := LoadByte (IP); Addr := Addr + Fetched; IP := IP + 1; end; end else Addr := 0; //Decode and execute the instruction //Halt if Op = 0 then Hlt := true //Ret else if Op = 1 then begin //High byte of the return address IP := LoadByte (RP); IP := IP shl 8; RP := RP + 1; //Low byte of the return address IP := IP + LoadByte (RP); RP := RP + 1; end //Shl 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 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 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 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 else if Op = 6 then R [X] := not (R [X] and R [Y]) //And else if Op = 7 then R [X] := R [X] and R [Y] //Or else if Op = 8 then R [X] := R [X] or R [Y] //Xor else if Op = 9 then R [X] := R [X] xor R [Y] //Load else if Op = $a then begin //Immediate if Y <> 0 then R [X] := Addr shr 8 //Address else R [X] := LoadByte (Addr); end //Store else if Op = $b then StoreByte (Addr, R [Y]) //Breq else if Op = $c then begin if R [X] = R [Y] then IP := Addr; end //Brneq else if Op = $d then begin if R [X] <> R [Y] then IP := Addr; end //Cleq else if Op = $e then begin if R [X] = R [Y] then Call; end //Clneq else if Op = $f then begin if R [X] <> R [Y] then Call; end; //Increment the instruction counter IC := IC + 1; 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} wait (1); {$endif} end.