home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
c
/
ucmicro.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2020-01-01
|
88KB
|
2,734 lines
{>>>> KERMIT.TEXT}
program kermit;
{UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
{Adapted to Pascal Microengine by Tim Shimeall, UCI}
{Changes:
- Added device declarations copied from Microengine hardware documentation
- Replaced external assembly language routines with Pascal versions
- Modified debug messages to be label values printed
- Changed format of packetwrite display to show header fields
- Implemented machine-dependent packet timeout
- Added debug packetwrites in recsw
- Added wrap-around debug info region
- Added legality check in showparms
- Removed lf elimination check in echo procedure
- Unitwrite calls replaced by calls to device driving routines
- Most uses of char_int_rec replaced by ord and chr
- Removed queue (no interrupts)
- Used sets for integer ops to getaround Microengine bug
- Changed parser from a unit to a segment procedure to allow swapping
- Split utility procs into separate files for editing and transfer convinience
}
(*$R-*) (* turn range checking off *)
(*$S+*) (* turn swapping on *)
(* $L+*) (* no listing *)
const blksize = 512;
oport = 8; (* output port # *)
(* clearscreen = 12; charcter which erases screen *)
bell = 7; (* ASCII bell *)
esc = 27; (* ASCII escape *)
maxpack = 93; (* maximum packet size minus 1 *)
soh = 1; (* start of header *)
sp = 32; (* ASCII space *)
cr = 13; (* ASCII CR *)
lf = 10; (* ASCII line feed *)
dle = 16; (* ASCII DLE (space compression prefix for psystem) *)
del = 127; (* delete *)
my_esc = 29; (* default esc char for connect (^]) *)
maxtry = 5; (* number of times to retry sending packet *)
my_quote = '#'; (* quote character I'll use *)
my_pad = 0; (* number of padding chars I need *)
my_pchar = 0; (* padding character I need *)
my_eol = 13; (* end of line character i need *)
my_time = 5; (* seconds after which I should be timed out *)
maxtim = 20; (* maximum timeout interval *)
mintim = 2; (* minimum time out interval *)
at_eof = -1; (* value to return if at eof *)
eoln_sym = 13; (* pascal eoln sym *)
back_space = 8; (* pascal backspace sym *)
(* MICROENGINE dependent constants *)
intsize = 15; (* number of bits in an integer minus 1 *)
Channel0=-992; {FC20 = serial Port B register}
Channel1=-1008; {FC10 = serial Port A register}
(* Elements of the status vector in the "StatCmdRec" declared below*)
RegEmpty=0;
DataReceived=1;
OverError=2;
FrameError=4;
(* bits 3,5,6,and 7 are not used, since they rely on specific wiring,
and seem to be unreliable *)
(* screen control information *)
(* console line on which to put specified info *)
title_line = 1;
statusline = 2;
packet_line = 3;
retry_line = 4;
file_line = 5;
error_line = 6;
prompt_line = 7;
debug_line = 9;
debug_max = 12; (* Max lines of debug to show at once *)
(* position on line to put info *)
statuspos = 70;
packet_pos = 19;
retry_pos = 17;
file_pos = 11;
type packettype = packed array[0..maxpack] of char;
parity_type = (evenpar, oddpar, markpar, spacepar, nopar);
char_int_rec = record (* allows character to be treated as integer... *)
(* is system dependent *)
case boolean of
true: (i: integer);
false: (ch: char)
end; (* record *)
int_bool_rec = record (* allows integer to be treated as boolean... *)
(* used for numeric AND,OR,XOR...system dependent *)
(* replaced by set version to escape microengine
bug *)
case boolean of
true: (i: integer);
false: (b: set of 0..intsize);
end; (* record *)
(* MICROENGINE Dependent Types *)
Port = (Terminal,Modem);
Statcmdrec = RECORD CASE BOOLEAN OF (* Only the Status field is used
in this code, but the declaration
is from Western Digital doc. *)
TRUE:(Command:INTEGER);
FALSE:(Status:PACKED ARRAY [0:7] OF BOOLEAN);
END;
SerialRec = RECORD
SerData:INTEGER;
StatSynDle:StatCmdRec;
Control2:INTEGER;
Control1:INTEGER;
filler:ARRAY [0..3] OF INTEGER;
Switch:StatCmdRec;
END;
(* Parser Types *)
statustype = (null, at_eol, unconfirmed, parm_expected, ambiguous,
unrec, fn_expected, ch_expected);
vocab = (nullsym, allsym, consym, debugsym, escsym, evensym, exitsym,
filewarnsym,helpsym, ibmsym, localsym, marksym, nonesym,
oddsym, offsym, onsym, paritysym, quitsym, recsym, sendsym,
setsym, showsym, spacesym);
var state: char; (* current state *)
f: file of char; (* file to be received *)
oldf: file; (* file to be sent *)
s: string;
eol, quote, esc_char: char;
fwarn, ibm, half_duplex, debug: boolean;
i, size, rpsiz, spsiz, pad, n, num_try, oldtry, timint: integer;
recpkt, packet: packettype;
padchar, ch: char;
debf: text; (* file for debug output *)
debnext:0..7; (* offset for next debug message *)
parity: parity_type;
xon: char;
filebuf: packed array[1..1024] of char;
bufpos, bufend: integer;
parity_array: packed array[char] of char;
ctlset: set of char;
rec_ok, send_ok: boolean;
(* MICROENGINE Dependent Variable declarations *)
PortA,PortB:RECORD CASE BOOLEAN OF
TRUE:(DevAdd:INTEGER);
FALSE:(Serial:^SerialRec);
END;
(* Parser vars *)
noun, verb, adj: vocab;
status: statustype;
vocablist: array[vocab] of string[13];
filename, line: string;
newescchar: char;
expected: set of vocab;
function read_ch(p: port; var ch: char): boolean;
forward;
function aand(x,y: integer): integer;
forward;
function aor(x,y: integer): integer;
forward;
function xor(x,y: integer): integer;
forward;
procedure error(p: packettype; len: integer);
forward;
procedure io_error(i: integer);
forward;
procedure debugwrite(s: string);
forward;
procedure debugint(s: string; i: integer);
forward;
procedure writescreen(s: string);
forward;
procedure refresh_screen(numtry, num: integer);
forward;
function min(x,y: integer): integer;
forward;
function tochar(ch: char): char;
forward;
function unchar(ch: char): char;
forward;
function ctl(ch: char): char;
forward;
function getfil(filename: string): boolean;
forward;
procedure bufemp(buffer: packettype; var f: text; len: integer);
forward;
function bufill(var buffer: packettype): integer;
forward;
procedure spar(var packet: packettype);
forward;
procedure rpar(var packet: packettype);
forward;
procedure spack(ptype: char; num:integer; len: integer; data: packettype);
forward;
function getch(var r: char; p: port): boolean;
forward;
function getsoh(p: port): boolean;
forward;
function rpack(var len, num: integer; var data: packettype): char;
forward;
procedure read_str(p: port; var s: string);
forward;
procedure packetwrite(p: packettype; len: integer);
forward;
procedure show_parms;
forward;
procedure uppercase(var s: string); forward;
(*$I WDFORW.TEXT *) (* Forward Declarations for WDPROCS.TEXT *)
(*$I HELP.TEXT*) (* Segment Procedure Help *)
(*$I SENDSW.TEXT*) (* Segment Procedure Sendsw *)
(*$I RECSW.TEXT*) (* Segment Procedure Recsw *)
(*$I PARSE.TEXT*) (* Segment Function Parse *)
(*$I WDPROCS.TEXT*) (* MICROENGINE dependent routines*)
(*$I UTILS.TEXT *) (* General Utility procedures *)
(*$I RSUTILS.TEXT *) (* Utility procedures for send and receive *)
procedure connect;
(* connect to remote host (terminal emulation *)
var ch: char;
close: boolean;
procedure read_esc;
(* read charcter after esc char and interpret it *)
begin
repeat
until read_ch(terminal,ch); (* wait until they've typed something in *)
if (ch in ['a'..'z']) then (* uppercase it *)
ch := chr(ord(ch) - ord('a') + ord('A'));
if ch in [{'B',}'C','S','?'] then
case ch of
(*'B': sendbrk; B: send a break to the IBM *)
'C': close := true; (* C: end connection *)
'S': begin (* S: show status *)
noun := allsym;
showparms
end; (* S *)
'?': begin (* ?: show options *)
(* writeln('B Send a BREAK signal.'); *)
write('C Close Connection, return to ');
writeln('KERMIT-UCSD command level.');
writeln('S Show Status of connection');
writeln('? Print this list');
write('^',esc_char,' send the escape ');
writeln('character itself to the');
writeln(' remote host.')
end; (* ? *)
end (* case *)
else if ch = esc_char then (* ESC-char: send it out *)
begin
if half_duplex then
begin
echo(ch);
while not istbtr do;
sndbbt(ch);
end (* if *)
end (* else if *)
else (* anything else: ignore *)
write(chr(bell))
end; (* read_esc *)
begin (* connect *)
writeln('Connecting to host...type CTRL-',ctl(esc_char),' C to exit');
close := false;
repeat
if read_ch(modem,ch) then (* if char from host then *)
echo(ch); (* echo it *)
if read_ch(terminal,ch) then (* if char from keyboard then *)
if ch <> esc_char then (* if not ESC-char then *)
begin
if half_duplex then (* echo it if half-duplex *)
echo(ch);
while not istbtr do;
sndbbt(ch) (* send it out the port *)
end (* if *)
else (* ch = esc_char *) (* else is ESC-char so *)
read_esc; (* interpret next char *)
until close; (* if still connected, get more *)
writeln('Disconnected')
end; (* connect *)
procedure fill_parity_array;
(* parity value table for even parity...not(entry) = odd parity *)
const min = 0;
max = 126;
var i, shifter, counter: integer;
minch, maxch, ch: char;
r: char_int_rec;
begin
minch := chr(min);
maxch := chr(max);
case parity of
evenpar:
begin
for ch := minch to maxch do
begin
r.ch := ch; (* put char into variant record *)
shifter := aand(r.i,255); (* mask off parity bit *)
counter := 0;
for i := 1 to 7 do (* count the 1's *)
begin
if odd(shifter) then
counter := counter + 1;
shifter := shifter div 2
end; (* for i *)
if odd(counter) then (* stick a 1 on if necessary *)
parity_array[ch] := chr(aor(ord(ch),128))
else
parity_array[ch] := chr(aand(ord(ch),127))
end; (* for ch *)
end; (* case even *)
oddpar:
begin
for ch := minch to maxch do
begin
r.ch := ch; (* put char into variant record *)
shifter := aand(r.i,255); (* mask off parity bit *)
counter := 0;
for i := 1 to 7 do (* count the 1's *)
begin
if odd(shifter) then
counter := counter + 1;
shifter := shifter div 2
end; (* for i *)
if odd(counter) then (* stick a 1 on if necessary *)
parity_array[ch] := chr(aand(ord(ch),127))
else
parity_array[ch] := chr(aor(ord(ch),128))
end; (* for ch *)
end; (* case odd *)
markpar:
for ch := minch to maxch do (* stick a 1 on all chars *)
parity_array[ch] := chr(aor(ord(ch),128));
spacepar:
for ch := minch to maxch do (* mask off parity on all chars *)
parity_array[ch] := chr(aand(ord(ch),127));
nopar:
for ch := minch to maxch do (* don't mess w/parity bit at all *)
parity_array[ch] := ch;
end; (* case *)
end; (* fill_parity_array *)
procedure write_bool(s: string; b: boolean);
(* writes message & 'on' if b, 'off' if not b *)
begin
write(s);
case b of
true: writeln('on');
false: writeln('off');
end; (* case *)
end; (* write_bool *)
procedure show_parms;
(* shows the various settable parameters *)
begin
if noun in [allsym, debugsym, ibmsym, escsym, filewarnsym, localsym,
paritysym] then
case noun of
allsym:
begin
write_bool('Debugging is ',debug);
writeln('Escape character is ^',ctl(esc_char));
write_bool('File warning is ',fwarn);
write_bool('IBM is ',ibm);
write_bool('Local echo is ',halfduplex);
case parity of
evenpar: write('Even');
markpar: write('Mark');
nopar: write('No');
oddpar: write('Odd');
spacepar: write('Space');
end; (* case *)
writeln(' parity');
end; (* allsym *)
debugsym: write_bool('Debugging is ',debug);
escsym: writeln('Escape character is ^',ctl(esc_char));
filewarnsym: write_bool('File warning is ',fwarn);
ibmsym: write_bool('IBM is ',ibm);
localsym: write_bool('Local echo is ',halfduplex);
paritysym: begin
case parity of
evenpar: write('Even');
markpar: write('Mark');
nopar: write('No');
oddpar: write('Odd');
(' parity');
end; (* paritysym *)
end (* case *)
else write(chr(bell));
end; (* show_sym *)
procedure set_parms;
(* sets the parameters *)
begin
case noun of
debugsym: case adj of
onsym: begin
debug := true;
(*$I-*)
rewrite(debf,'CONSOLE:')
(*I+*)
end; (* onsym *)
offsym: debug := false
end; (* case adj *)
escsym: escchar := newescchar;
filewarnsym: fwarn := (adj = onsym);
ibmsym: case adj of
onsym: begin
ibm := true;
parity := markpar;
half_duplex := true;
fillparityarray
end; (* onsym *)
offsym: begin
ibm := false;
parity := nopar;
half_duplex := false;
fillparityarray
end; (* onsym *)
end; (* case adj *)
localsym: halfduplex := (adj = onsym);
paritysym: begin
case adj of
evensym: parity := evenpar;
marksym: parity := markpar;
nonesym: parity := nopar;
oddsym: parity := oddpar;
spacesym: parity := spacepar;
end; (* case *)
fill_parity_array;
end; (* paritysym *)
end; (* case *)
end; (* set_parms *)
procedure initialize;
var ch: char;
begin
pad := mypad;
padchar := chr(mypchar);
eol := chr(my_eol);
esc_char := chr(my_esc);
quote := my_quote;
ctlset := [chr(1)..chr(31),chr(del),quote];
half_duplex := false;
debug := false;
debnext:=0;
fwarn := false;
spsiz := max_pack;
rpsiz := max_pack;
n := 0;
parity := nopar;
initvocab;
fill_parity_array;
ibm := false;
xon := chr(17);
bufpos := 1;
bufend := 0;
init;
end; (* initialize *)
procedure closeup;
begin
finit;
writeln(chr(esc),'E'{clearscreen});
end; (* closeup *)
begin (* kermit *)
initialize;
repeat
write('Kermit-UCSD> ');
readstr(terminal,line);
case parse of
unconfirmed: writeln('Unconfirmed');
parm_expected: writeln('Parameter expected');
ambiguous: writeln('Ambiguous');
unrec: writeln('Unrecognized command');
fn_expected: writeln('File name expected');
ch_expected: writeln('Single character expected');
null: case verb of
consym: connect;
helpsym: help;
recsym: begin
recsw(rec_ok);
gotoxy(0,debugline);
write(chr(bell));
if rec_ok then
writeln('successful receive')
else
writeln('unsuccessful receive');
(*$I-*) (* set i/o checking off *)
close(oldf);
(*$I+*) (* set i/o checking back on *)
gotoxy(0,promptline);
end; (* recsym *)
sendsym: begin
uppercase(filename);
sendsw(send_ok);
gotoxy(0,debugline);
write(chr(bell));
if send_ok then
writeln('successful send')
else
writeln('unsuccessful send');
(*$I-*) (* set i/o checking off *)
close(oldf);
(*$I+*) (* set i/o checking back on *)
gotoxy(0,promptline);
end; (* sendsym *)
setsym: set_parms;
show_sym: show_parms;
end; (* case verb *)
end; (* case parse *)
unitclear(1); (* clear any trash in input *)
unitclear(2);
until (verb = exitsym) or (verb = quitsym);
closeup
end. (* kermit *)
{>>>>WDFORW.TEXT}
procedure INIT; forward;
function ISTARR:boolean ; forward;
function ISTBRR:boolean; forward;
function ISTAOR:boolean ; forward;
function ISTBOR:boolean ; forward;
function ISTAFE:boolean ; forward;
function ISTBFE:boolean; forward;
function ISTATR:boolean ; forward;
function ISTBTR :boolean; forward;
function RCVABT:CHAR ; forward;
function RCVBBT:CHAR ; forward;
procedure SNDABT (BT:CHAR); forward;
procedure SNDBBT (BT:CHAR); forward;
procedure FINIT; forward;
{>>>> HELP.TEXT}
segment procedure help;
{UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
procedure keypress;
var ch: char;
begin
writeln('---------------Press any key to continue---------------');
repeat
until readch(terminal,ch);
writeln(chr(esc),'E'{clearscreen})
end; (* keypress *)
procedure help1;
var ch: char;
begin
if (noun = nullsym) then
begin
writeln('KERMIT is a family of programs that do reliable file transfer');
write('between computers over TTY lines. KERMIT can also be ');
writeln('used to make the ');
writeln('microcomputer behave as a terminal for a mainframe. These are the ');
writeln('commands for theUCSD p-system version, KERMIT-UCSD:');
writeln
end; (* if *)
if (noun = nullsym) or (noun = consym) then
begin
writeln(' CONNECT To make a "virutual terminal" connection to a remote');
writeln(' system.');
writeln;
write(' To break the connection and "escape" back to the micro,');
writeln;
writeln(' type the escape sequence (CTRL-] C, that is Control ');
writeln(' rightbracket followed immediately by the letter C.)');
writeln;
end; (* if *)
if (noun = nullsym) or (noun = exitsym) then
begin
writeln(' EXIT To return back to main command level of the p-system.');
writeln;
end; (* if *)
if (noun = nullsym) or (noun = helpsym) then
begin
writeln(' HELP To get a list of KERMIT commands.');
writeln;
end; (* if *)
if (noun = nullsym) or (noun = quitsym) then
begin
writeln(' QUIT Same as EXIT.');
writeln;
end; (* if *)
if (noun = nullsym) or (noun = recsym) then
begin
writeln(' RECEIVE To accept a file from the remote system.');
writeln;
end; (* if *)
end; (* help1 *)
procedure help2;
var ch: char;
begin
if (noun = nullsym) or (noun = sendsym) then
begin
writeln(' SEND To send a file or group of files to the remote system.');
writeln;
end; (* if *)
if (noun = nullsym) then
keypress;
if (noun = nullsym) or (noun = setsym) then
begin
writeln(' SET To establish system-dependent parameters. The ');
writeln(' SET options are as follows: ');
writeln;
if (adj = nullsym) or (adj = debugsym) then
begin
writeln(' DEBUG To set debug mode ON or OFF ');
writeln(' (default is OFF).');
writeln;
end; (* if *)
if (adj = nullsym) or (adj = escsym) then
begin
writeln(' ESCAPE To change the escape sequence that ');
writeln(' lets you return to the PC Kermit from');
write(' the remote host.');
writeln(' The default is CTRL-] c.');
writeln;
end; (* if *)
if (adj = nullsym) or (adj = filewarnsym) then
begin
writeln(' FILE-WARNING ON/OFF, default is OFF. If ON, ');
writeln(' Kermit will warn you and rename an ');
writeln(' incoming file so as not to write over');
writeln(' a file that currently exists with the');
writeln(' same name');
writeln;
end; (* if *)
if (adj = nullsym) then
keypress;
end; (* if *)
end; (* help2 *)
procedure help3;
begin
if (noun = nullsym) or (noun = setsym) then
begin
if (adj = nullsym) or (adj = ibmsym) then
begin
writeln(' IBM ON/OFF, default is OFF. This flag ');
write(' should be ON only when ');
writeln('transfering files');
writeln(' between the micro and an IBM VM/CMS');
writeln(' system. It also causes the parity to');
write(' be set appropriately ');
writeln('(mark) and activates');
writeln(' local echoing');
writeln;
end; (* if *)
if (adj = nullsym) or (adj = localsym) then
begin
write(' LOCAL-ECHO ON/OFF, default is OFF. This sets the');
writeln;
writeln(' duplex. It should be ON when using ');
writeln(' the IBM and OFF for the DEC-20.');
writeln;
end; (* if *)
end; (* if *)
end; (* help3 *)
procedure help4;
begin
if (noun = setsym) or (noun = nullsym) then
begin
if (adj = nullsym) or (adj = paritysym) then
begin
writeln(' PARITY EVEN, ODD, MARK, SPACE, or NONE.');
writeln(' NONE is the default but if the IBM ');
writeln(' flag is set, parity is set to MARK. ');
writeln(' This flag selects the parity for ');
write(' outgoing and incoming characters during');
writeln;
write(' CONNECT and file transfer to match the');
writeln;
writeln(' requirements of the host.');
writeln;
end; (* if *)
end; (* if *)
if (noun = nullsym) or (noun = showsym) then
begin
writeln(' SHOW To see the values of parameters that can be modified');
writeln(' via the SET command. Options are the same as for SET,');
writeln(' except that a SHOW ALL command has been added.');
end; (* if *)
end; (* help4 *)
begin
help1;
help2;
help3;
help4
end; (* help *)
{>>>> SENDSW.TEXT}
(* Send Section *)
{UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
segment procedure sendsw(var send_ok: boolean);
var io_status: integer;
procedure openfile;
(* resets file & gets past first 2 blocks *)
begin
(*$I-*) (* turn off compiler i/o checking temporarily *)
reset(oldf,filename);
(*$I+*) (* turn compiler i/o checking back on *)
io_status := io_result;
if (iostatus = 0) then
if (pos('.TEXT',filename) = length(filename) - 4) then
(* is a text file, so *)
i := blockread(oldf,filebuf,2); (* skip past 2 block header *)
end; (* openfile *)
function sinit: char;
(* send init packet & receive other side's *)
var num, len, i: integer; (* packet number and length *)
ch: char;
begin
if debug then
debugwrite('sinit');
if numtry > maxtry then
begin
sinit := 'a';
exit(sinit)
end;
num_try := num_try + 1;
spar(packet);
if istbrr then ch:=rcvbbt; (* clear modem buffer *)
refresh_screen(numtry,n);
spack('S',n mod 64,6,packet);
ch := rpack(len,num,recpkt);
if (ch = 'N') then
begin
sinit := 's';
exit(sinit)
end (* if 'N' *)
else if (ch = 'Y') then
begin
if ((n mod 64) <> num) then (* not the right ack *)
begin
sinit := state;
exit(sinit)
end;
rpar(recpkt);
if (eol = chr(0)) then (* if they didn't spec eol *)
eol := chr(my_eol); (* use mine *)
if (quote = chr(0)) then (* if they didn't spec quote *)
quote := my_quote; (* use mine *)
ctl_set := [chr(1)..chr(31),chr(del),quote];
numtry := 0;
n := n + 1; (* increase packet number *)
sinit := 'f';
exit(sinit)
end (* else if 'Y' *)
else if (ch = 'E') then
begin
error(recpkt,len);
sinit := 'a'
end (* if 'E' *)
else if (ch = chr(0)) then
sinit := state
else if (ch <> 'N') then
sinit := 'a'
end; (* sinit *)
function sdata: char;
(* send file data *)
var num, len: integer;
ch: char;
packarray: array[false..true] of packettype;
sizearray: array[false..true] of integer;
current: boolean;
b: boolean;
function other(b: boolean): boolean;
(* complements a boolean which is used as array index *)
begin
if b then
other := false
else
other := true
end; (* other *)
begin
current := true;
packarray[current] := packet;
sizearray[current] := size;
while (state = 'd') do
begin
if (numtry > maxtry) then (* if too many tries, give up *)
state := 'a';
b := other(current);
numtry := numtry + 1;
(* send a data packet *)
spack('D',n mod 64,sizearray[current],packarray[current]);
refresh_screen(numtry,n);
(* set up next packet *)
sizearray[b] := bufill(packarray[b]);
ch := rpack(len,num,recpkt); (* receive a packet *)
if ch = 'N' then (* NAK, so just stay in this state *)
if ((n+1) mod 64 <> num) then (* unless NAK for next, which *)
sdata := state
else (* is just like ACK for this packet *)
begin
if num > 0 then
num := (num - 1) (* in which case, decrement num *)
else
num := 63;
ch := 'Y'; (* and indicate an ACK *)
end; (* else *)
if (ch = 'Y') then
begin
if ((n mod 64) <> num) then (* if wrong ACK *)
begin
sdata := state; (* stay in same state *)
exit(sdata); (* get out of here *)
end; (* if *)
if numtry > 1 then (* if anything in buffer, flush it *)
if istbrr then begin
ch:=rcvbbt;
ch:='Y';
end;
numtry := 0;
n := n + 1;
current := b;
if sizearray[current] = ateof then
state := 'z' (* set state to eof *)
else
state := 'd' (* else stay in data state *)
end (* if *)
else if (ch = 'E') then
begin
error(recpkt,len);
state := 'a'
end (* if 'E' *)
else if (ch = chr(0)) then (* receive failure, so stay in d *)
begin
end
else if (ch <> 'N') then
state := 'a' (* on any other goto abort state *)
end; (* while *)
size := sizearray[current];
packet := packarray[current];
sdata := state
end; (* sdata *)
function sfile: char;
(* send file header *)
var num, len, i: integer;
ch: char;
fn: packettype;
oldfn: string;
procedure legalize(var fn: string);
(* make sure file name will be legal to other computer *)
var count, i, j, l: integer;
procedure uppercase(var s: string);
var i: integer;
begin
for i := 1 to length(s) do
if s[i] in ['a'..'z'] then
s[i] := chr(ord('A') + ord(s[i]) - ord('a'))
end; (* uppercase *)
begin
count := 0;
l := length(fn);
for i := 1 to l do (* count '.'s in fn *)
if fn[i] = '.' then
count := count + 1;
for i := 1 to count-1 do (* remove all but 1 *)
begin
j := 1;
while (j < l) and (fn[j] <> '.') do
j := j + 1;
delete(fn,j,1);l := l - 1
end; (* for i *)
l := length(fn);
i := pos(':',fn);
if (i <> 0) then
begin
fn := copy(fn,i,l-i);
l := length(fn)
end;
i := 1;
while (i <= length(fn)) do
if not(fn[i] in ['a'..'z','A'..'Z','.','0'..'9']) then
delete(fn,i,1)
else
i := i + 1;
uppercase(fn)
end; (* legalize *)
begin
if debug then
debugwrite('sfile');
if (numtry > maxtry) then (* if too many tries, give up *)
begin
sfile := 'a';
exit(sfile)
end;
numtry := numtry + 1;
oldfn := filename;
legalize(filename); (* make filename acceptable to remote *)
len := length(filename);
moveleft(filename[1],fn[0],len); (* move filename into a packettype *)
gotoxy(filepos,fileline);
write(oldfn,' ==> ',filename);
refresh_screen(numtry,n);
spack('F',n mod 64,len,fn); (* send file header packet *)
size := bufill(packet); (* get first data from file *)
(* while waiting for response *)
ch := rpack(len,num,recpkt);
if ch = 'N' then (* NAK, so just stay in this state *)
if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
exit(sfile) (* is just like ACK for this packet *)
else
begin
if (num > 0) then
num := (num - 1) (* in which case, decrement num *)
else
num := 63;
ch := 'Y'; (* and indicate an ACK *)
end; (* else *)
if (ch = 'Y') then
begin
if ((n mod 64) <> num) then (* if wrong ACK, stay in F state *)
exit(sfile);
numtry := 0;
n := n + 1;
sfile := 'd';
end (* if *)
else if (ch = 'E') then
begin
error(recpkt,len);
sfile := 'a'
end (* if 'E' *)
else if (ch <> chr(0)) and (ch <> 'N') then (* don't recognize it *)
sfile := 'a'
end; (* sfile *)
function seof: char;
(* send end of file *)
var num, len: integer;
ch: char;
begin
if debug then
debugwrite('seof');
if (numtry > maxtry) then (* if too many tries, give up *)
begin
seof := 'a';
exit(seof)
end;
numtry := numtry + 1;
refresh_screen(numtry,n);
spack('Z',(n mod 64),0,packet); (* send end of file packet *)
if debug then
debugwrite('seof1');
ch := rpack(len,num,recpkt);
if ch = 'N' then (* NAK, so just stay in this state *)
if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
exit(seof) (* is just like ACK for this packet *)
else
begin
if num > 0 then
num := (num - 1) (* in which case, decrement num *)
else
num := 63;
ch := 'Y'; (* and indicate an ACK *)
end; (* else *)
if (ch = 'Y') then
begin
if debug then
debugwrite('seof2');
if ((n mod 64) <> num) then (* if wrong ACK, stay in F state *)
exit(seof);
numtry := 0;
n := n + 1;
if debug then
debugwrite(concat('closing ',s));
close(oldf);
seof := 'b'
end (* if *)
else if (ch = 'E') then
begin
error(recpkt,len);
seof := 'a'
end (* if 'E' *)
else if (ch = chr(0)) then (* receive failed, so stay in z state *)
begin
end
else if (ch <> 'N') then (* other error, just abort *)
seof := 'a'
end; (* seof *)
function sbreak: char;
var num, len: integer;
ch: char;
(* send break (end of transmission) *)
begin
if debug then
debugwrite('sbreak');
if (numtry > maxtry) then (* if too many tries, give up *)
begin
sbreak := 'a';
exit(sbreak)
end;
numtry := numtry + 1;
refresh_screen(numtry,n);
spack('B',(n mod 64),0,packet); (* send end of file packet *)
ch := rpack(len,num,recpkt);
if ch = 'N' then (* NAK, so just stay in this state *)
if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which *)
exit(sbreak) (* is just like ACK for this packet *)
else
begin
if num > 0 then
num := (num - 1) (* in which case, decrement num *)
else
num := 63;
ch := 'Y'; (* and indicate an ACK *)
end; (* else *)
if (ch = 'Y') then
begin
if ((n mod 64) <> num) then (* if wrong ACK, stay in B state *)
exit(sbreak);
numtry := 0;
n := n + 1;
sbreak := 'c' (* else, switch state to complete *)
end (* if *)
else if (ch = 'E') then
begin
error(recpkt,len);
sbreak := 'a'
end (* if 'E' *)
else if (ch = chr(0)) then (* receive failed, so stay in z state *)
begin
end
else if (ch <> 'N') then (* other error, just abort *)
sbreak := 'a'
end; (* sbreak *)
(* state table switcher for sending *)
begin (* sendsw *)
if debug then
debugwrite(concat('Opening ',filename));
openfile;
if io_status <> 0 then
begin
writeln(chr(esc),'E'{clear_screen});
io_error(io_status);
send_ok := false;
exit(sendsw)
end;
write_screen('Sending');
state := 's';
n := 0; (* set packet # *)
numtry := 0;
while true do
if state in ['d', 'f', 'z', 's', 'b', 'c', 'a'] then
case state of
'd': state := sdata;
'f': state := sfile;
'z': state := seof;
's': state := sinit;
'b': state := sbreak;
'c': begin
send_ok := true;
exit(sendsw)
end; (* case c *)
'a': begin
send_ok := false;
exit(sendsw)
end (* case a *)
end (* case *)
else (* state not in legal states *)
begin
send_ok := false;
exit(sendsw)
end (* else *)
end; (* sendsw *)
{>>>> RECSW.TEXT}
(* RECEIVE SECTION *)
{UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U}
segment procedure recsw(var rec_ok: boolean);
function rdata: char;
(* send file data *)
var num, len: integer;
ch: char;
begin
repeat
if numtry > maxtry then
begin
debugwrite('too many intial retries in rdata');
state := 'a';
exit(rdata)
end;
num_try := num_try + 1;
ch := rpack(len,num,recpkt); (* receive a packet *)
if debug and (ch<>chr(0)) then packetwrite(recpkt,len);
refresh_screen(numtry,n);
if (ch = 'D') then (* got data packet *)
begin
if (num <> (n mod 64)) then (* wrong packet *)
begin
if (oldtry > maxtry) then
begin
debugwrite('too many data retries in rdata');
rdata := 'a'; (* too many tries, abort *)
exit(rdata)
end; (* if *)
n := n - 1;
if (num = (n mod 64)) then (* previous packet again *)
begin (* so re-ACK it *)
debugint('re-acking ',num);
spack('Y',num,6,packet);
numtry := 0; (* reset try counter *)
(* stay in same state *)
end (* if *)
else begin (* wrong number *)
debugwrite('wrong data sequence no. in rdata');
state := 'a' (* so abort *)
end
end (* if *)
else (* right packet *)
begin
bufemp(recpkt,f,len); (* write data to file *)
spack('Y',(n mod 64),0,packet); (* ACK packet *)
oldtry := numtry; (* reset try counters *)
if numtry > 1 then
if istbrr then (* clear buffer *)
begin
ch:=rcvbbt;
ch:='D';
end;
numtry := 0;
n := n + 1 (* bump packet number *)
(* stay in data send state *)
end (* else *)
end (* if 'D' *)
else if (ch = 'F') then (* file header *)
begin
if (oldtry > maxtry) then
begin
debugwrite('too many file head tries in rdata');
rdata := 'a'; (* too many tries, abort *)
exit(rdata)
end; (* if *)
n := n - 1;
if (num = (n mod 64)) then (* previous packet again *)
begin (* so re-ACK it *)
debugint('re-acking file header ',num);
spack('Y',num,0,packet);
if istbrr then begin
ch:=rcvbbt; (* and empty out buffer *)
ch:='F';
end;
numtry := 0; (* reset try counter *)
state := state; (* stay in same state *)
end (* if *)
else begin
debugwrite('file info not previous packet in rdata');
state := 'a' (* not previous packet, abort *)
end
end (* if 'F' *)
else if (ch = 'Z') then (* end of file *)
begin
if (num <> (n mod 64)) then(* wrong packet, abort *)
begin
debugwrite('wrong eof packet in rdata');
rdata := 'a';
exit(rdata)
end; (* if *)
spack('Y',n mod 64,0,packet); (* ok, ACK it *)
close(f,lock); (* close up the file *)
n := n + 1; (* bump packet counter *)
state := 'f'; (* go to complete state *)
end (* else if 'Z' *)
else if (ch = 'E') then (* error packet *)
begin
error(recpkt,len); (* display error *)
state := 'a' (* and abort *)
end (* if 'E' *)
else if (ch <> chr(0)) then begin (* some other packet type, *)
state := 'a'; (* abort *)
debugwrite('wierd rdata packet');
end
until (state <> 'd');
rdata := state
end; (* rdata *)
function rfile: char;
(* receive file header *)
var num, len: integer;
ch: char;
oldfn: string;
i: integer;
procedure makename(recpkt: packettype; var fn: string; l: integer);
function exist(fn: string): boolean;
(* returns true if file named fn exists *)
var f: file;
begin
(*$I-*) (* turn off i/o checking *)
reset(f,fn);
exist := (ioresult = 0)
(*$I+*)
end; (* exist *)
procedure checkname(var fn: string);
(* if file fn exists, makes a new name which doesn't *)
(* does this by changing letters in file name until it *)
(* finds some combination which doesn't exitst *)
var ch: char;
i: integer;
begin
i := 1;
while (i <= length(fn)) and exist(fn) do
begin
ch := 'A';
while (ch in ['A'..'Z']) and exist(fn) do
begin
fn[i] := ch;
ch := succ(ch);
end; (* while *)
i := i + 1
end; (* while *)
end; (* checkname *)
begin (* makename *)
fn := copy(' ',1,15); (* stretch length *)
moveleft(recpkt[0],fn[1],l); (* get filename from packet *)
oldfn := copy(fn, 1,l); (* save fn sent to show user *)
fn := copy(fn,1,min(15,l)); (* set length of filename *)
(* and make sure <= 15 *)
uppercase(fn);
if pos('.TEXT',fn) <> length(fn)-4 then
begin
if length(fn) > 10 then
fn := copy(fn,1,10); (* can only be 15 long in all *)
fn := concat(fn,'.TEXT'); (* and we'll add .TEXT *)
end; (* if *)
if fwarn then (* if file warning is on *)
checkname(fn); (* must check that name unique *)
end; (* makename *)
begin (* rfile *)
if debug then
debugwrite('rfile');
if (numtry > maxtry) then (* if too many tries, give up *)
begin
rfile := 'a';
exit(rfile)
end;
numtry := numtry + 1;
ch := rpack(len,num,recpkt); (* receive a packet *)
if debug and (ch<>chr(0)) then packetwrite(recpkt,len);
refresh_screen(numtry,n);
if ch = 'S' then (* send init, maybe our ACK lost *)
begin
if (oldtry > maxtry) then (* too many tries, abort *)
begin
debugwrite('too many tries in rfile init');
rfile := 'a';
exit(rfile)
end; (* if *)
n := n - 1;
if num = (n mod 64) then (* previous packet mod 64? *)
begin (* yes, ACK it again *)
debugint('re-acking init ',num);
spar(packet); (* with our send init params *)
spack('Y',num,7,packet);
numtry := 0; (* reset try counter *)
rfile := state; (* stay in same state *)
end (* if *)
else (* not previous packet, abort *)
state := 'a'
end (* if 'S' *)
else if (ch = 'Z') then (* end of file *)
begin
if (oldtry > maxtry) then (* too many tries, abort *)
begin
debugwrite('too many tries in filehead eof');
rfile := 'a';
exit(rfile)
end; (* if *)
n := n - 1;
if num = (n mod 64) then (* previous packet mod 64? *)
begin (* yes, ACK it again *)
debugint('re-acking eof ',num);
spack('Y',num,0,packet);
numtry := 0;
rfile := state (* stay in same state *)
end (* if *)
else
rfile := 'a' (* no, abort *)
end (* else if *)
else if (ch = 'F') then (* file header *)
begin (* which is what we really want *)
if (num <> (n mod 64)) then (* if wrong packet, abort *)
begin
debugwrite('wrong seq. of file header');
rfile := 'a';
exit(rfile)
end;
makename(recpkt,filename,len); (* get filename, make unique if filew *)
gotoxy(filepos,fileline);
write(oldfn,' ==> ',filename);
if not getfil(filename) then (* try to open new file *)
begin
ioerror(ioresult); (* if unsuccessful, tell them *)
rfile := 'a'; (* and abort *)
exit(rfile)
end; (* if *)
spack('Y',n mod 64,0,packet); (* ACK file header *)
oldtry := numtry; (* reset try counters *)
numtry := 0;
n := n + 1; (* bump packet number *)
rfile := 'd'; (* switch to data state *)
end (* else if *)
else if ch = 'B' then (* break transmission *)
begin
if (num <> (n mod 64)) then (* wrong packet, abort *)
begin
debugwrite('wrong sequence in break packet');
rfile := 'a';
exit(rfile)
end;
spack('Y',n mod 64,0,packet); (* say ok *)
rfile := 'c' (* go to complete state *)
end (* else if *)
else if (ch = 'E') then
begin
error(recpkt,len);
rfile := 'a'
end
else if (ch = chr(0)) then (* returned false *)
rfile := state (* so stay in same state *)
else begin (* some weird state, so abort *)
rfile := 'a';
debugwrite('wierd rfile packet');
end
end; (* rfile *)
function rinit: char;
(* receive initialization *)
var num, len: integer; (* packet number and length *)
ch: char;
begin
if debug then
debugwrite('rinit');
numtry := numtry + 1;
ch := rpack(len,num,recpkt); (* receive a packet *)
if debug and (ch<>chr(0)) then packetwrite(recpkt,len);
refresh_screen(num_try,n);
if (ch = 'S') then (* send init packet *)
begin
rpar(recpkt); (* get other side's init data *)
spar(packet); (* fill packet with my init data *)
ctl_set := [chr(1)..chr(31),chr(del),quote];
spack('Y',n mod 64,7,packet); (* ACK with my params *)
oldtry := numtry; (* save old try count *)
numtry := 0; (* start a new counter *)
n := n + 1; (* bump packet number *)
rinit := 'f'; (* enter file send state *)
end (* if 'S' *)
else if (ch = 'E') then
begin
rinit := 'a';
error(recpkt,len)
end (* if 'E' *)
else if (ch = chr(0)) then
rinit := 'r' (* stay in same state *)
else begin
rinit := 'a'; (* abort *)
debugwrite('wierd rinit packet');
end
end; (* rinit *)
(* state table switcher for receiving packets *)
begin (* recswok *)
writescreen('Receiving');
state := 'r'; (* initial state is send *)
n := 0; (* set packet # *)
numtry := 0; (* no tries yet *)
while true do
if state in ['d', 'f', 'r', 'c', 'a'] then
case state of
'd': state := rdata;
'f': state := rfile;
'r': state := rinit;
'c': begin
rec_ok := true;
exit(recsw)
end; (* case c *)
'a': begin
rec_ok := false;
exit(recsw)
end (* case a *)
end (* case *)
else (* state not in legal states *)
begin
rec_ok := false;
exit(recsw)
end (* else *)
end; (* recsw *)
{>>>> PARSE.TEXT}
segment function parse: statustype;
(* NOTE: due to procedures at the end of this file, this must be the
LAST segment declared *)
type states = (start, fin, get_filename, get_set_parm, get_parity, get_on_off,
get_char, get_show_parm, get_help_show, get_help_parm,
exitstate);
var status: statustype;
word: vocab;
state: states;
procedure eatspaces(var s: string);
var done: boolean;
i: integer;
begin
done := (length(s) = 0);
while not done do
begin
if s[1] = ' ' then
begin
i := length(s) - 1;
s := copy(s,2,i);
done := length(s) = 0
end (* if *)
else
done := true
end (* while *)
end; (* eatspaces *)
procedure isolate_word(var line, s: string);
var i: integer;
done: boolean;
begin
done := false;
i := 1;
s := copy(' ',0,0);
while (i <= length(line)) and not done do
begin
if line[i] = ' ' then
done := true
else
s := concat(s,copy(line,i,1));
i := i + 1;
end; (* while *)
line := copy(line,i,length(line)-i+1);
end; (* isolate_word *)
function get_fn(var line, fn: string): boolean;
var i, l: integer;
begin
get_fn := true;
isolate_word(line, fn);
l := length(fn);
if (l < 1) then
get_fn := false
end; (* get_fn *)
function getch(var ch: char): boolean;
var s: string;
begin
isolate_word(line,s);
if length(s) <> 1 then
getch := false
else
begin
ch := s[1];
get_ch := true
end (* else *)
end; (* getch *)
function get_sym(var word: vocab): statustype;
var i: vocab;
s: string;
stat: statustype;
done: boolean;
matches: integer;
begin
eat_spaces(line);
if length(line) = 0 then
getsym := ateol
else
begin
stat := null;
done := false;
isolate_word(line,s);
i := allsym;
matches := 0;
repeat
if (pos(s,vocablist[i]) = 1) and (i in expected) then
begin
matches := matches + 1;
word := i
end
else if (s[1] < vocablist[i,1]) then
done := true;
if (i = spacesym) then
done := true
else
i := succ(i)
until (matches > 1) or done;
if matches > 1 then
stat := ambiguous
else if (matches = 0) then
stat := unrec;
getsym := stat
end (* else *)
end; (* getsym *)
begin
state := start;
parse := null;
noun := nullsym;
verb := nullsym;
adj := nullsym;
uppercase(line);
repeat
case state of
start:
begin
expected := [consym, exitsym, helpsym, quitsym, recsym, sendsym,
setsym, showsym];
status := getsym(verb);
if status = ateol then
begin
parse := null;
exit(parse)
end (* if *)
else if (status <> unrec) and (status <> ambiguous) then
case verb of
consym: state := fin;
exitsym, quitsym: state := fin;
helpsym: state := get_help_parm;
recsym: state := fin;
sendsym: state := getfilename;
setsym: state := get_set_parm;
showsym: state := get_show_parm;
end (* case *)
end; (* case start *)
fin:
begin
expected := [];
status := getsym(verb);
if status = ateol then
begin
parse := null;
exit(parse)
end (* if status *)
else
status := unconfirmed
end; (* case fin *)
getfilename:
begin
expected := [];
if getfn(line,filename) then
begin
status := null;
state := fin
end (* if *)
else
status := fnexpected
end; (* case get file name *)
get_set_parm:
begin
expected := [paritysym, localsym, ibmsym, escsym,
debugsym, filewarnsym];
status := getsym(noun);
if status = ateol then
status := parm_expected
else if (status <> unrec) and (status <> ambiguous) then
case noun of
paritysym: state := get_parity;
localsym: state := get_on_off;
ibmsym: state := get_on_off;
escsym: state := getchar;
debugsym: state := getonoff;
filewarnsym: state := getonoff;
end (* case *)
end; (* case get_set_parm *)
get_parity:
begin
expected := [marksym, spacesym, nonesym, evensym, oddsym];
status := getsym(adj);
if status = ateol then
status := parm_expected
else if (status <> unrec) and (status <> ambiguous) then
state := fin
end; (* case get_parity *)
get_on_off:
begin
expected := [onsym, offsym];
status := getsym(adj);
if status = ateol then
status := parm_expected
else if (status <> unrec) and (status <> ambiguous) then
state := fin
end; (* get_on_off *)
get_char:
if getch(newescchar) then
state := fin
else
status := ch_expected;
get_show_parm:
begin
expected := [allsym, paritysym, localsym, ibmsym, escsym,
debugsym, filewarnsym];
status := getsym(noun);
if status = ateol then
status := parm_expected
else if (status <> unrec) and (status <> ambiguous) then
state := fin
end; (* case get_show_parm *)
get_help_show:
begin
expected := [paritysym, localsym, ibmsym, escsym,
debugsym, filewarnsym];
status := getsym(adj);
if (status = at_eol) then
begin
status := null;
state := fin
end
else if (status <> unrec) and (status <> ambiguous) then
state := fin
end; (* case get_help_show *)
get_help_parm:
begin
expected := [consym, exitsym, helpsym, quitsym, recsym,
sendsym, setsym, showsym];
status := getsym(noun);
if status = ateol then
begin
parse := null;
exit(parse)
end;
if (status <> unrec) and (status <> ambiguous) then
case noun of
consym: state := fin;
sendsym: state := fin;
recsym: state := fin;
setsym: state := get_help_show;
showsym: state := fin;
helpsym: state := fin;
exitsym, quitsym: state := fin;
end (* case *)
end; (* case get_help_show *)
end (* case *)
until (status <> null);
parse := status
end; (* parse *)
procedure initvocab;
var i: integer;
begin
vocablist[allsym] := 'ALL';
vocablist[consym] := 'CONNECT';
vocablist[debugsym] := 'DEBUG';
vocablist[escsym] := 'ESCAPE';
vocablist[evensym] := 'EVEN';
vocablist[exitsym] := 'EXIT';
vocablist[filewarnsym] := 'FILE-WARNING';
vocablist[helpsym] := 'HELP';
vocablist[ibmsym] := 'IBM';
vocablist[localsym] := 'LOCAL-ECHO';
vocablist[marksym] := 'MARK';
vocablist[nonesym] := 'NONE';
vocablist[oddsym] := 'ODD';
vocablist[offsym] := 'OFF';
vocablist[onsym] := 'ON';
vocablist[paritysym] := 'PARITY';
vocablist[quitsym] := 'QUIT';
vocablist[recsym] := 'RECEIVE';
vocablist[sendsym] := 'SEND';
vocablist[setsym] := 'SET';
vocablist[showsym] := 'SHOW';
vocablist[spacesym] := 'SPACE';
end; (* initvocab *)
procedure uppercase(*var s: string*);
var i: integer;
begin
for i := 1 to length(s) do
if s[i] in ['a'..'z'] then
s[i] := chr(ord(s[i]) - ord('a') + ord('A'))
end; (* uppercase *)
{>>>>WDPROCS.TEXT}
(* These drivers were adapted from routines written by Tim Shimeall
for a PCNET implementation, based on information from Western
Digital.
On the Microengine, there are two RS232C Serial Ports. Port A is
reserved for the system terminal. Port B is available for all other
devices which may be desired to hang off a Microengine. In this code,
it is assumed that Port B holds the modem.
*)
(* All functions are duplicated on ports A and B for simplicity *)
PROCEDURE Init;
BEGIN (* InitM *)
PortB.DevAdd:= Channel0;
PortA.DevAdd:= Channel1;
WITH PortB.Serial^ DO BEGIN
{The following two lines set the serial port to the following
commands:
Control1:
1 - Full Duplex Operation
0 - Break or Transmit NOT transparent
0 - Send 2 stop bits on Transmitted 8-bit data
0 - No echo of Recieved data
0 - Parity checking/generation OFF
1 - Reciever is enabled (chars in Rec. holding reg.)
1 - REQUEST TO SEND is enabled if CTS is low
1 - DTR is ON
Control2:
0 - 8 bits
0 - 8 bits
0 - Asynchronous character mode
0 - even parity
0 - select reciever rate 1
0 - +
0 | - Clock select to rate 1 (32X)
1 - +
}
Control1:=135; {87 hex}
Control2:=1;
END;
WITH PortA.Serial^ DO BEGIN
Control1:=135;
Control2:=1;
END;
END; (*InitM*)
(*---------------------UART FLAG CHECKING-------------------------------*)
function ISTARR(*:boolean *);
(* ARR -- IS True Port A Receive Ready?
This checks the UART status bit corresponding to Receive Data
Available. If data is available a true result is returned.*)
BEGIN
ISTARR:=PortA.Serial^.StatSynDle.status[DataReceived];
END;
function ISTBRR(*:boolean*);
(* BRR -- IS True Port B Receive Ready?*)
BEGIN
ISTBRR:=PortB.Serial^.StatSynDle.status[DataReceived];
END;
function ISTAOR(*:boolean*);
(* AOR -- IS it True that data OverRun occurred?:0 istor<ditto>
Immediately after RCVBT is called, ISTOR may be called to check for
data overrun. This function isn't necessary, but it helps diagnose
software that is losing data because it is too slow to receive data
before that data starts getting shifted out of the way to make way for
later data that has already started to arrive.
*)
BEGIN
ISTAOR:=PortA.Serial^.StatSynDle.Status[OverError];
END;
function ISTBOR(*:boolean*);
BEGIN
ISTBOR:=PortB.Serial^.StatSynDle.Status[OverError];
END;
function ISTAFE(*:boolean *);
(* FE -- IS it True that Framing-Error occurred?:0 istfe<ditto>
Immediately after RCVBT is called, ISTFE may be called to check for
framing error. This function isn't necessary, but it helps diagnose
various errors such as phone-line-noise and wrong-speed-UART. Normally
ISTOR will be called before ISTFE since data overrun is a more serious
error than framing-error and thus pre-empts framing-error. The entire
sequence is thus: ISTRR, RCVBT, ISTOR, ISTFE.
*)
BEGIN
ISTAFE:=PortA.Serial^.StatSynDle.Status[FrameError];
END;
function ISTBFE(*:boolean*);
BEGIN
ISTBFE:=PortB.Serial^.StatSynDle.Status[FrameError];
END;
function ISTATR(*:boolean *);
(* TR -- IS it True that Transmit is Ready?:0 isttr<used in FDX&SDWBT>
ISTTR is analagous to ISTRR, it tells whether it's safe to transmit
(rather than to receive) a byte of data. Internally it tells whether
the previous byte has cleared the device so that the buffer is empty
to accept another byte. In the device descripion it's usually called
Transmit Buffer Empty. For instantaneous devices such as memory-mapped
CRTs, this function will always return TRUE. For most other devices
such as UARTs and ACIAs (connected directly to terminals, or to
modems), ISTTR will return TRUE initially, then return FALSE as soon
as a byte is sent to the device, and then return TRUE when actual
transmission is done. For double-buffered devices it may only go FALSE
only after two characters are sent to it, one of which is actually en
route and the other of which is merely occupying the extra buffer.
*)
BEGIN
ISTATR:=PortA.Serial^.StatSynDle.Status[RegEmpty];
END;
function ISTBTR(*:boolean*);
BEGIN
ISTBTR:=PortB.Serial^.StatSynDle.Status[RegEmpty];
END;
(*------------------Primitive character sending and receiving---------------*)
function RCVABT(*:CHAR*) ;
(* ReCeiVe ByTe of data from device:0 rcvbt<used in FDX and RCWBT>
This is the function that is called after ISTRR returns true, to
actually fetch the waiting data from the UART or ACIA into the
computer, freeing the device to accept the next byte of data. These
two functions, testing for data ready and actually fetching the data,
are kept separate for two reasons: (1) they are separate hardware
functions in most existing devices, ISTRR being a read of the status
port with testing for a bit and RCVBT being a read of the data
port, and (2) often they must be separate in the software, such as
when it's necessary to verify both that data is available and there's
a place to put it before fetching the data, such as in a terminal emulator.
Note that calling RCVBT any time other than after getting a true
result from ISTRR is invalid, yielding random garbage such as part of
an incoming byte shifted. Note also that RCVBT fetches all 8 bits of
the incoming byte of data, returning an 8-bit number with each bit in
its normal position, for example the first-arrived bit is the 1 bit,
then the 2 bit, etc., with the "parity" bit which is the last-arrived
appearing simply as an 8th bit (hexadecimal value 80). No checking of
parity is allowed, nor is stripping off of the parity bit. When only 7
bits are desired, a higher-level function will strip off the parity bit.*)
BEGIN
RCVABT:=CHR(PortA.Serial^.SerData);
END;
function RCVBBT(*:CHAR*);
BEGIN
RCVBBT:=CHR(PortB.Serial^.SerData);
END;
procedure SNDABT(* (BT:CHAR)*);
(* SeND ByTe of data:0 sndbt<used in FDX&SDWBT>
After getting back a TRUE result from isttr, this function SNDBT is
used to actually send the byte of data from the CPU to the device, so
as to effect sending it out the I/O port (modem or local CRT). Note
that any attempt to call SNDBT without first getting TRUE from isttr
can result in clobbering previous data that is still in transit from
the UART or ACIA bit by bit, causing both that previous byte and this
new byte to be lost/garbaged. *)
BEGIN (* SNDABT*)
PortA.Serial^.SerData:=ORD(BT);
END(*SNDABT*);
procedure SNDBBT(* (BT:CHAR)*);
(* SeND ByTe of data:0 sndbt<used in FDX&SDWBT>
After getting back a TRUE result from isttr, this function SNDBT is
used to actually send the byte of data from the CPU to the device, so
as to effect sending it out the I/O port (modem or local CRT). Note
that any attempt to call SNDBT without first getting TRUE from isttr
can result in clobbering previous data that is still in transit from
the UART or ACIA bit by bit, causing both that previous byte and this
new byte to be lost/garbaged. *)
BEGIN (* SNDBBT*)
PortB.Serial^.SerData:=ORD(BT);
END(*SNDBBT*);
procedure finit;
BEGIN
PortB.Serial^.Control1:=0; {Turn off DTR, which causes modem to hang up}
END;
{>>>>UTILS.TEXT}
function ready(p:port):boolean;
begin
ready:= ((p=terminal) and istarr) or ((p=modem) and istbrr);
end;
function pget(p:port):char;
begin
if p=terminal then pget:=rcvabt
else pget:=rcvbbt;
end;
procedure read_str(*var p: port; var s: string*);
(* acts like readln(s) but takes input from specified port *)
var i: integer;
begin
i := 0;
s := copy('',0,0);
repeat
repeat (* get a character *)
until ready(p);
ch:=pget(p);
if (ord(ch) = backspace) then (* if it's a backspace then *)
begin
if (i > 0) then (* if not at beginning of line *)
begin
write(ch); (* go back a space on screen *)
write(' '); (* erase char on screen *)
write(ch); (* go back a space again *)
i := i - 1; (* adjust string counter *)
s := copy(s,1,i) (* adjust string *)
end (* if *)
end (* if *)
else if (ord(ch) <> eoln_sym) then (* otherwise if not at eoln then *)
begin
write(ch); (* echo char on screen *)
i := i + 1; (* inc string counter *)
s := concat(s,' ');
s[i] := ch; (* put char in string *)
end; (* if *)
until (ord(ch) = eoln_sym); (* if not eoln, get another char *)
s := copy(s,1,i); (* correct string length *)
writeln (* write a line on the screen *)
end; (* read_str *)
function read_ch(*p: port; var ch: char): boolean*);
(* read a character from an input port *)
begin
if ready(p) then (* if a char there *)
begin
ch := pget(p); (* get the char *)
read_ch := true; (* and return true *)
end (* if *)
else (* otherwise *)
read_ch := false; (* return false *)
end; (* read_ch *)
function getch(*var r: char; p: port): boolean*);
(* gets a character, strips parity, returns true if it got a char which *)
(* isn't Kermit SOH, false if it gets SOH or nothing after maxtry *)
const maxtry = 10000;
var count: integer;
begin
count := 0;
getch := false;
repeat
count := count + 1;
until ready(p) or (count > maxtry); (* wait for a character *)
if (count > maxtry) then (* if wait too long then *)
exit(getch); (* get out of here *)
r:=pget(p); (* get the character *)
r := chr(aand(ord(r),127)); (* strip parity from char *)
getch := (r <> chr(soh)); (* return true if not SOH *)
end; (* getch *)
function aand(*x,y: integer): integer*);
(* arithmetic and--takes 2 integers and ands them, yeilding an integer *)
var xrec, yrec, temp: int_bool_rec;
begin
xrec.i := x; (* put the two numbers in variant record *)
yrec.i := y;
temp.b := xrec.b * yrec.b; (* use as sets to 'and' them *)
aand := temp.i (* return integer result *)
end; (* aand *)
function aor(*x,y: integer): integer*);
(* arithmetic or *)
var xrec, yrec, temp: int_bool_rec;
begin
xrec.i := x; (* put two numbers in variant record *)
yrec.i := y;
temp.b := xrec.b + yrec.b; (* use as sets to 'or' them *)
aor := temp.i (* return integer result *)
end; (* aor *)
function xor(*x,y: integer): integer*);
(* exclisive or *)
var xrec, yrec, temp: int_bool_rec;
begin
xrec.i := x; (* put two numbers in variant record *)
yrec.i := y;
(* use as sets to 'xor' them *)
temp.b := (xrec.b - yrec.b) + (yrec.b - xrec.b);
xor := temp.i (* return integer result *)
end; (* xor *)
procedure error(*p: packettype; len: integer*);
(* writes error message sent by remote host *)
var i: integer;
begin
gotoxy(0,errorline);
for i := 0 to len-1 do
write(p[i]);
gotoxy(0,promptline);
end; (* error *)
procedure io_error(*i: integer*);
begin
gotoxy(0,errorline);
write(chr(27),'K'); (* erase to end of line *)
case i of
0: writeln('No error');
1: writeln('Bad Block, Parity error (CRC)');
2: writeln('Bad Unit Number');
3: writeln('Bad Mode, Illegal operation');
4: writeln('Undefined hardware error');
5: writeln('Lost unit, Unit is no longer on-line');
6: writeln('Lost file, File is no longer in directory');
7: writeln('Bad Title, Illegal file name');
8: writeln('No room, insufficient space');
9: writeln('No unit, No such volume on line');
10: writeln('No file, No such file on volume');
11: writeln('Duplicate file');
12: writeln('Not closed, attempt to open an open file');
13: writeln('Not open, attempt to close a closed file');
14: writeln('Bad format, error in reading real or integer');
15: writeln('Ring buffer overflow')
end; (* case *)
gotoxy(0,promptline)
end; (* io_error *)
procedure debugwrite(*s: string*);
(* writes a debugging message *)
var i: integer;
begin
if debug then
begin
gotoxy(0,debugline+debnext);
debnext:=(debnext+1) mod debug_max;
write(chr(27),'K'); (* erase to end of line *)
write(s); (* write debugging message *)
end (* if debug *)
end; (* debugwrite *)
procedure debugint(*s: string; i: integer*);
(* write a debugging message and an integer *)
begin
if debug then
begin
debugwrite(s);
write(i)
end (* if debug *)
end; (* debugint *)
procedure writescreen(*s: string*);
(* sets up the screen for receiving or sending files *)
begin
write(chr(esc),'E'{clearscreen});
gotoxy(0,titleline);
write(' Kermit UCSD p-system');
gotoxy(statuspos,statusline);
write(s);
gotoxy(0,packetline);
write('Number of Packets: ');
gotoxy(0,retryline);
write('Number of Tries: ');
gotoxy(0,fileline);
write('File Name: ');
end; (* writescreen *)
procedure refresh_screen(*numtry, num: integer*);
(* keeps track of packet count on screen *)
begin
gotoxy(retrypos,retryline);
write(numtry: 5);
gotoxy(packetpos,packetline);
write(num: 5)
end; (* refresh_screen *)
function min(*x,y: integer): integer*);
(* returns smaller of two integers *)
begin
if x < y then
min := x
else
min := y
end; (* min *)
function tochar(*ch: char): char*);
(* tochar converts a control character to a printable one by adding space *)
begin
tochar := chr(ord(ch) + ord(' '))
end; (* tochar *)
function unchar(*ch: char): char*);
(* unchar undoes tochar *)
begin
unchar := chr(ord(ch) - ord(' '))
end; (* unchar *)
function ctl(*ch: char): char*);
(* ctl toggles control bit: ^A becomes A, A becomes ^A *)
begin
ctl := chr(xor(ord(ch),64))
end; (* ctl *)
procedure echo(ch: char);
(* echos a character on the screen *)
begin
ch := chr(aand(ord(ch),127)); (* mask off parity bit *)
repeat until istatr;
sndabt(ch)
end; (* echo *)
{>>>>RSUTILS.TEXT}
function getfil(*filename: string): boolean*);
(* opens a file for writing *)
begin
(*$I-*) (* turn i/o checking off *)
rewrite(f,filename);
(*$I-*) (* turn i/o checking on *)
getfil := (ioresult = 0)
end; (* getfil *)
procedure bufemp(*buffer: packettype; var f: text; len: integer*);
(* empties a packet into a file *)
var i,ls: integer;
r: char;
s: string;
begin
s := copy('',0,0);
ls := 0;
i := 0;
while i < len do
begin
r := buffer[i]; (* get a character *)
if (r = myquote) then (* if character is control quote *)
begin
i := i + 1; (* skip over quote and *)
r := buffer[i]; (* get quoted character *)
if (aand(ord(r),127) <> ord(myquote)) then
r := ctl(r); (* controllify it *)
end; (* if *)
if (ord(r) = cr) then (* else if a carriage return then *)
begin
i := i + 3; (* skip over that and line feed *)
(*$I-*) (* turn i/o checking off *)
writeln(f,s); (* and write out line to file *)
s := copy('',0,0); (* empty the string var *)
ls := 0;
if (io_result <> 0) then (* if io_error *)
begin
io_error(ioresult); (* tell them and *)
state := 'a'; (* abort *)
end (* if *)
end
(*$I+*) (* turn i/o checking back on *)
else (* else, is a regular char, so *)
begin
r:= chr(aand(ord(r),127)); (* mask off parity bit *)
s := concat(s,' '); (* and add character to out string *)
ls := ls + 1;
s[ls] := r;
i := i + 1 (* increase buffer pointer *)
end; (* else *)
end; (* while *) (* and get another char *)
(*$I-*) (* turn i/o checking off *)
write(f,s); (* and write out line to file *)
if (io_result <> 0) then (* if io_error *)
begin
io_error(ioresult); (* tell them and *)
state := 'a'; (* abort *)
end (* if *)
(*$I+*) (* turn i/o checking back on *)
end; (* bufemp *)
function bufill(*var buffer: packettype): integer*);
(* fill a packet with data from a file...manages a 2 block buffer *)
var i, j, k, t7, count: integer;
r: char;
begin
i := 0;
(* while file has some data & packet has some room we'll keep going *)
while ((bufpos <= bufend) or (not eof(oldf))) and (i < spsiz-9) do
begin
(* if we need more data from disk then *)
if (bufpos > bufend) and (not eof(oldf)) then
begin
(* read a couple of blocks *)
bufend := blockread(oldf,filebuf[1],2) * blksize;
(* and adjust buffer pointer *)
bufpos := 1
end; (* if *)
if (bufpos <= bufend) then (* if we're within buffer bounds *)
begin
r := filebuf[bufpos]; (* get a character *)
bufpos := bufpos + 1; (* increase buffer pointer *)
if (ord(r) = dle) then (* if it's space compression char, *)
begin
count := ord(unchar(filebuf[bufpos])); (* get # of spaces *)
bufpos := bufpos + 1; (* read past # *)
r := ' '; (* and make current char a space *)
end (* else if *)
else (* otherwise, it's just a char *)
count := 1; (* so only 1 copy of it *)
if (r in ctlset) then (* if a control char *)
begin
if (ord(r) = cr) then (* if a carriage return *)
begin
buffer[i] := quote; (* put (quoted) CR in buffer *)
i := i + 1;
buffer[i] := ctl(chr(cr));
i := i + 1;
r := chr(lf); (* and we'll stick a LF after *)
end; (* if *)
if r <> chr(0) then (* if not a NUL then *)
begin
buffer[i] := quote; (* put the quote in buffer *)
i := i + 1;
if r <> quote then
r := ctl(r); (* and un-controllify char *)
end (* if *)
end; (* if *)
end; (* if *)
j := 1;
while (j <= count) and (i <= spsiz - 5) do
begin (* put all the chars in buffer *)
if (ord(r) <> 0) then (* so long as not a NUL *)
begin
buffer[i] := r;
i := i + 1;
end (* if *)
else (* is a NUL so *)
if (bufpos > blksize) then (* skip to end of block *)
bufpos := bufend + 1 (* since rest will be NULs *)
else
bufpos := blksize + 1;
j := j + 1
end; (* while *)
end; (* while *)
if (i = 0) then (* if we're at end of file, *)
bufill := (at_eof) (* indicate it *)
else (* else *)
begin
if (j <= count) then (* if didn't all fit in packet *)
begin
bufpos := bufpos - 2; (* put buf pointer at DLE *)
(* and update compress count *)
filebuf[bufpos + 1] := tochar(chr(count-j+1));
end; (* if *)
bufill := i (* return # of chars in packet *)
end; (* else *)
end; (* bufill *)
procedure spar(*var packet: packettype*);
(* fills data array with my send-init parameters *)
begin
packet[0] := tochar(chr(maxpack)); (* biggest packet i can receive *)
packet[1] := tochar(chr(mytime)); (* when i want to be timed out *)
packet[2] := tochar(chr(mypad)); (* how much padding i need *)
packet[3] := ctl(chr(mypchar)); (* padding char i want *)
packet[4] := tochar(chr(myeol)); (* end of line character i want *)
packet[5] := myquote; (* control-quote char i want *)
packet[6] := 'N'; (* I won't do 8-bit quoting *)
end; (* spar *)
procedure rpar(*var packet: packettype*);
(* gets their init params *)
var s:string;
begin
s:='rpar:spsize:## timint:## pad:## padchar:### eol:### quote:###';
spsiz := ord(unchar(packet[0])); (* max send packet size *)
s[13]:=chr(ord('0')+(spsiz div 10));
s[14]:=chr(ord('0')+(spsiz mod 10));
timint := ord(unchar(packet[1])); (* when i should time out *)
s[23]:=chr(ord('0')+(timint div 10));
s[24]:=chr(ord('0')+(timint mod 10));
pad := ord(unchar(packet[2])); (* number of pads to send *)
s[30]:=chr(ord('0')+(pad div 10));
s[31]:=chr(ord('0')+(pad mod 10));
padchar := ctl(packet[3]); (* padding char to send *)
s[41]:=chr(ord('0')+(ord(padchar) div 100));
s[42]:=chr(ord('0')+((ord(padchar) mod 100) div 10));
s[43]:=chr(ord('0')+(ord(padchar) mod 10));
eol := unchar(packet[4]); (* eol char i must send *)
s[49]:=chr(ord('0')+(ord(eol) div 100));
s[50]:=chr(ord('0')+((ord(eol) mod 100) div 10));
s[51]:=chr(ord('0')+(ord(eol) mod 10));
quote := packet[5]; (* incoming data quote char *)
s[59]:=chr(ord('0')+(ord(quote) div 100));
s[60]:=chr(ord('0')+((ord(quote) mod 100) div 10));
s[61]:=chr(ord('0')+(ord(quote) mod 10));
debugwrite(s);
end; (* rpar *)
procedure packetwrite(*p: packettype; len: integer*);
(* writes out all of a packet for debugging purposes *)
var i: integer;
s: string;
begin
s:='length:## Sequence:## Type: #';
if p[0]=chr(soh) then s:=concat('SOH ',s);
s[8]:=chr(ord('0')+(ord(p[1]) div 10));
s[9]:=chr(ord('0')+(ord(p[1]) mod 10));
s[20]:=chr(ord('0')+(ord(p[2]) div 10));
s[21]:=chr(ord('0')+(ord(p[2]) mod 10));
s[length(s)]:=p[3];
debugwrite(s);
gotoxy(0,debugline+debnext);
debnext:=(debnext+1) mod debug_max;
for i := 4 to len+3 do
begin
if i = 84 then
begin
gotoxy(0,debugline+debnext);
debnext:=(debnext+1) mod debug_max;
write(chr(27),'K');
end; (* if *)
write(p[i])
end; (* for *)
end; (* packetwrite *)
procedure spack(*ptype: char; num: integer; len: integer; data: packettype*);
(* send a packet *)
const maxtry = 10000;
var bufp, i, count: integer;
chksum: char;
buffer: packettype;
ch: char;
begin
if ibm and (state <> 's') then (* if ibm and not SINIT then *)
begin
count := 0;
repeat (* wait for an xon *)
repeat
count := count + 1
until (readch(modem,ch)) or (count > maxtry );
until (ch = xon) or (count > maxtry);
if count > maxtry then (* if wait too long then *)
begin
exit(spack) (* get out *)
end; (* if *)
end; (* if *)
bufp := 0;
for i := 1 to pad do begin
while not istbtr do ;
sndbbt(padchar); (* write out any padding chars *)
end;
buffer[bufp] := chr(soh); (* packet sync character *)
bufp := bufp + 1;
chksum := tochar(chr(len + 3)); (* init chksum *)
buffer[bufp] := tochar(chr(len + 3)); (* character count *)
bufp := bufp + 1;
chksum := chr(ord(chksum) + ord(tochar(chr(num))));
buffer[bufp] := tochar(chr(num));
bufp := bufp + 1;
chksum := chr(ord(chksum) + ord(ptype));
buffer[bufp] := ptype; (* packet type *)
bufp := bufp + 1;
for i := 0 to len - 1 do (* loop through data chars *)
begin
buffer[bufp] := data[i]; (* store char *)
bufp := bufp + 1;
chksum := chr(ord(chksum) + ord(data[i]))
end; (* for i *)
(* compute final chksum *)
chksum := chr(aand(ord(chksum) + (aand(ord(chksum),192) div 64), 63));
buffer[bufp] := tochar(chksum);
bufp := bufp + 1;
buffer[bufp] := eol;
if (parity <> nopar) then
for i := 0 to bufp do (* set correct parity on buffer *)
buffer[i] := parity_array[buffer[i]];
for i:=0 to bufp do begin
while not istbtr do;
sndbbt(buffer[i]); (* send the packet out *)
end;
debugwrite('sending');
if debug then
packetwrite(buffer,len);
end; (* spack *)
function getsoh(*p: port): boolean*);
(* reads characters until it finds an SOH; returns false if has to read more *)
(* than maxtry chars *)
const maxtry = 10000; (* allows about 1 second of trying *)
var ch: char;
seconds,count: integer;
begin
count := 0;
seconds:=0;
get_soh := true;
repeat
repeat
count := count + 1;
if count>maxtry then begin
seconds:=seconds+1;
count:=0;
end;
until ready(p) or (seconds > timint); (* wait for a character *)
if (seconds > timint) then
begin
get_soh := false;
exit(get_soh);
end;
ch := pget(p); (* get the character *)
ch := chr(aand(ord(ch),127)); (* strip parity of char *)
until (ch = chr(SOH)) (* if not SOH, get more *)
end; (* getsoh *)
(*$G+*) (* turn on goto option...need it for next routine *)
function rpack(*var len, num: integer; var data: packettype): char*);
(* read a packet *)
label 1; (* used to emulate C's CONTINUE statement *)
const maxtry = 10000; (* allows for about 1 second of checking *)
var seconds, count, i, ichksum: integer;
chksum, ptype: char;
r: char;
begin
count := 0;
seconds := 0;
if not getsoh(modem) and (state<>'r') then (*if don't get synch char then *)
begin
rpack := 'N'; (* treat as a NAK *)
num := n mod 64;
exit(rpack) (* and get out of here *)
end;
1: count := count + 1;
if (count>maxtry)and(state<>'r') then (* end of one second *)
if seconds<timint then begin (* and aren't waiting for init *)
count:=0;
seconds:=seconds+1;
end
else begin (* if we've tried too many times *)
rpack := 'N'; (* treat as NAK *)
exit(rpack) (* and get out of here *)
end; (* if *)
if not getch(r,modem) then (* get a char and *)
goto 1; (* resynch if soh *)
ichksum := ord(r); (* start checksum *)
len := ord(unchar(r)) - 3; (* character count *)
if not getch(r,modem) then (* get a char and *)
goto 1; (* resynch if soh *)
ichksum := ichksum + ord(r);
num := ord(unchar(r)); (* packet number *)
if not getch(r,modem) then (* get a char and *)
goto 1; (* resynch if soh *)
ichksum := ichksum + ord(r);
ptype := r; (* packet type *)
for i := 0 to len-1 do (* get any data *)
begin
if not getch(r,modem) then (* get a char and *)
goto 1; (* resynch if soh *)
ichksum := ichksum + ord(r);
data[i] := r;
end; (* for i *)
data[len] := chr(0); (* mark end of data *)
if not getch(r,modem) then (* get a char and *)
goto 1; (* resynch if soh *)
(* compute final checksum *)
chksum := chr(aand(ichksum + (aand(ichksum,192) div 64), 63));
if (chksum <> unchar(r)) then (* if checksum bad *)
rpack := chr(0) (* return 'false' indicator *)
else (* else *)
rpack := ptype; (* return packet type *)
if debug then
begin
gotoxy(0,debugline+debnext);
debnext:= (debnext+1) mod debug_max;
write('rpack: len:',len,' num:',num,' ptype:',ptype);
end; (* if *)
end; (* rpack *)
(*$G-*) (* turn off goto option...don't need it anymore *)