home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c / ucterak.pas < prev    next >
Pascal/Delphi Source File  |  2020-01-01  |  90KB  |  2,767 lines

  1. >>>> UCSD Pascal KERMIT for the Terak p-System, from Kate MacGregor, Cornell U
  2. >>>>
  3. >>>> All files are concatenated together into this single file, separated by
  4. >>>> lines beginning like this one does, followed by the name of the file.
  5. >>>>
  6. >>>> HELP.TEXT
  7. segment procedure help;
  8.  
  9. procedure keypress;
  10.  
  11. const clearscreen = 12;
  12.  
  13. var ch: char;
  14.  
  15.   begin
  16.     writeln('---------------Press any key to continue---------------');
  17.     repeat
  18.     until readch(kq,ch);
  19.     writeln(chr(clearscreen))
  20.   end; (* keypress *)
  21.  
  22. procedure help1;
  23.  
  24. var ch: char;
  25.  
  26. begin
  27. if (noun = nullsym) then
  28. begin
  29. writeln('KERMIT is a family of  programs that do reliable file transfer');
  30. write('between computers over TTY lines.  KERMIT can also be ');
  31. writeln('used to make the ');
  32. writeln('microcomputer behave as a terminal for a mainframe.  These are the ');
  33. writeln('commands for theUCSD p-system version, KERMIT-UCSD:');
  34. writeln
  35. end; (* if *)
  36. if (noun = nullsym) or (noun = consym) then
  37. begin
  38. writeln('  CONNECT     To make a "virutual terminal" connection to a remote');
  39. writeln('              system.');
  40. writeln;
  41. write('              To break the connection and "escape" back to the micro,');
  42. writeln;
  43. writeln('              type the escape sequence (CTRL-] C, that is Control ');
  44. writeln('              rightbracket followed immediately by the letter C.)');
  45. writeln;
  46. end; (* if *)
  47. if (noun = nullsym) or (noun = exitsym) then
  48. begin
  49. writeln('  EXIT        To return back to main command level of the p-system.');
  50. writeln;
  51. end; (* if *)
  52. if (noun = nullsym) or (noun = helpsym) then
  53. begin
  54. writeln('  HELP        To get a list of KERMIT commands.');
  55. writeln;
  56. end; (* if *)
  57. if (noun = nullsym) or (noun = quitsym) then
  58. begin
  59. writeln('  QUIT        Same as EXIT.');
  60. writeln;
  61. end; (* if *)
  62. if (noun = nullsym) or (noun = recsym) then
  63. begin
  64. writeln('  RECEIVE     To accept a file from the remote system.');
  65. writeln;
  66. end; (* if *)
  67. end; (* help1 *)
  68.  
  69. procedure help2;
  70.  
  71. var ch: char;
  72.  
  73. begin
  74. if (noun = nullsym) or (noun = sendsym) then
  75. begin
  76. writeln('  SEND        To send a file or group of files to the remote system.');
  77. writeln;
  78. end; (* if *)
  79. if (noun = nullsym) then
  80.     keypress;
  81. if (noun = nullsym) or (noun = setsym) then
  82. begin
  83. writeln('  SET         To establish system-dependent parameters.  The ');
  84. writeln('              SET options are as follows: ');
  85. writeln;
  86. if (adj = nullsym) or (adj = debugsym) then
  87. begin
  88. writeln('              DEBUG            To set debug mode ON or OFF ');
  89. writeln('                               (default is OFF).');
  90. writeln;
  91. end; (* if *)
  92. if (adj = nullsym) or (adj = escsym) then
  93. begin
  94. writeln('              ESCAPE           To change the escape sequence that ');
  95. writeln('                               lets you return to the PC Kermit from');
  96. write('                               the remote host.');
  97. writeln('  The default is CTRL-] c.');
  98. writeln;
  99. end; (* if *)
  100. if (adj = nullsym) or (adj = filewarnsym) then
  101. begin
  102. writeln('              FILE-WARNING     ON/OFF, default is OFF.  If ON, ');
  103. writeln('                               Kermit will warn you and rename an ');
  104. writeln('                               incoming file so as not to write over');
  105. writeln('                               a file that currently exists with the');
  106. writeln('                               same name');
  107. writeln;
  108. end; (* if *)
  109. if (adj = nullsym) then
  110.     keypress;
  111. end; (* if *)
  112. end; (* help2 *)
  113.  
  114. procedure help3;
  115.  
  116. begin
  117. if (noun = nullsym) or (noun = setsym) then
  118. begin
  119. if (adj = nullsym) or (adj = ibmsym) then
  120. begin
  121. writeln('              IBM              ON/OFF, default is OFF.  This flag ');
  122. write('                               should be ON only when ');
  123. writeln('transfering files');
  124. writeln('                               between the micro and an IBM VM/CMS');
  125. writeln('                               system.  It also causes the parity to');
  126. write('                               be set appropriately ');
  127. writeln('(mark) and activates');
  128. writeln('                               local echoing');
  129. writeln;
  130. end; (* if *)
  131. if (adj = nullsym) or (adj = localsym) then
  132. begin
  133. write('              LOCAL-ECHO       ON/OFF, default is OFF.  This sets the');
  134. writeln;
  135. writeln('                               duplex.  It should be ON when using ');
  136. writeln('                               the IBM and OFF for the DEC-20.');
  137. writeln;
  138. end; (* if *)
  139. end; (* if *)
  140. end; (* help3 *)
  141.  
  142. procedure help4;
  143.  
  144. begin
  145. if (noun = setsym) or (noun = nullsym) then
  146. begin
  147. if (adj = nullsym) or (adj = paritysym) then
  148. begin
  149. writeln('              PARITY           EVEN, ODD, MARK, SPACE, or NONE.');
  150. writeln('                               NONE is the default but if the IBM ');
  151. writeln('                               flag is set, parity is set to MARK.  ');
  152. writeln('                               This flag selects the parity for ');
  153. write('                               outgoing and incoming characters during');
  154. writeln;
  155. write('                               CONNECT and file transfer to match the');
  156. writeln;
  157. writeln('                               requirements of the host.');
  158. writeln;
  159. end; (* if *)
  160. end; (* if *)
  161. if (noun = nullsym) or (noun = showsym) then
  162. begin
  163. writeln('  SHOW        To see the values of parameters that can be modified');
  164. writeln('              via the SET command.  Options are the same as for SET,');
  165. writeln('              except that a SHOW ALL command has been added.');
  166. end; (* if *)
  167. end; (* help4 *)
  168.  
  169. begin
  170. help1;
  171. help2;
  172. help3;
  173. help4
  174. end; (* help *)
  175.  
  176.  
  177. >>>> KBDHANDLR.TEXT
  178. ;                        ----------------------------
  179. ;                        KBDHNDLR TTY Receive Handler
  180. ;                        ----------------------------
  181. ;
  182. ;    Two routines are provided that maintain an interrupt-driven
  183. ;    TTY-receive queue.  Appropriate PASCAL declarations are:
  184. ;
  185. ;        CONST KQSIZE = maximum number of queued characters
  186. ;
  187. ;        TYPE QUEUE = RECORD    (* These are order-dependent !!! *)
  188. ;                       QSIZE: INTEGER;
  189. ;                       INP: INTEGER;
  190. ;                       OUTP: INTEGER;
  191. ;                       MAXCHAR: INTEGER;
  192. ;                       DATA: PACKED ARRAY [0..RCVQSIZE] OF CHAR;
  193. ;                     END;
  194. ;        VAR KQ: QUEUE;     (* must be declared in outermost block *)
  195. ;
  196. ;        PROCEDURE KBDINIT (VAR Q: QUEUE; SIZE:INTEGER); EXTERNAL;
  197. ;        PROCEDURE KBDFINIT; EXTERNAL;
  198. ;
  199. ;        KBDINIT (KQ,KQSIZE);    (* initialize the queue handler *)
  200. ;
  201. ;        WHILE TRUE DO
  202. ;          WITH KQ DO
  203. ;           IF INP <> OUTP THEN    (* characters available *)
  204. ;             BEGIN
  205. ;               CH := DATA[OUTP];
  206. ;               OUTP := OUTP+1; IF OUTP > QSIZE THEN OUTP := 0;
  207. ;                ...
  208. ;             END;
  209. ;
  210. ;        KBDFINIT;       (* terminate the queue handler *)
  211. ;
  212. ;    The RECORD declaration for the queue must appear exactly as it
  213. ;    does above except that you can of course use any names you like.
  214. ;    Do NOT attempt to lump the first four integer variables together
  215. ;    into a single group of the form list:INTEGER.  In that case,
  216. ;    the compiler allocates them in reverse order, so that your code
  217. ;    and the interrupt handler will not agree about which words have
  218. ;    what meaning.
  219. ;
  220. ;    The queue handler runs continuously as an interrupt-driven task
  221. ;    at high priority.  As characters come in, it advances the queue
  222. ;    INP pointer and keeps track of the maximum number of characters in
  223. ;    the queue in the MAXCHAR variable.  Queue overflow is indicated
  224. ;    by MAXCHAR > QSIZE.  You must terminate by calling KBDFINIT, or
  225. ;    the TTY receive interrupts will be left enabled and you will end
  226. ;    up crashing the system by executing garbage code when the next
  227. ;    character is received.  (KBDFINIT also repairs the interrupt
  228. ;    vectors for breakpoints and the clock, so failing to call it will
  229. ;    quite likely crash the system even in the absence of incoming
  230. ;    TTY characters.)
  231. ;
  232. ;    The manipulation of the clock and BPT interrupt vectors is borrowed
  233. ;    from UCSD's old communications program.  The purpose is to allow
  234. ;    the clock handler to be interrupted by incoming TTY characters.
  235. ;
  236. KDB       .EQU   177562          ; Receive Data Buffer absolute address
  237. KSR       .EQU   177560          ; Receive Status Register absolute address
  238. KINTV     .EQU       60          ; Receiver Interrupt Vector address
  239. CLKINTV   .EQU      100          ; Clock interrupt vector address
  240. BPTINTV   .EQU       14          ; BPT interrupt vector address
  241. QXCINTV   .EQU      250          ; QX controller interrupt vector
  242. ;
  243.           .PROC  KBDINIT,2         ; (VAR Q:QUEUE, SIZE:INTEGER)
  244. ;
  245.           .DEF   KBDLOC          ; holds vector address
  246.           .DEF   KBDPR           ; holds old priority
  247. Q         .EQU   4               ; stack offset for Q address
  248. SIZE      .EQU   2               ; stack offset for QSIZE value
  249. ;
  250.           MOV    Q(SP),R0        ; obtain the Q record address
  251.           MOV    R0,KQADRS       ; remember Q address
  252.           MOV    SIZE(SP),(R0)+  ; store size in QSIZE word
  253.           MOV    #0,(R0)+        ; clear INP, OUTP, and MAXCHAR
  254.           MOV    #0,(R0)+
  255.           MOV    #0,(R0)
  256. ;
  257. ;
  258.           MOV    @#KINTV,KBDLOC      ; save old interrupt vector
  259.           MOV    @#KINTV+2,KBDPR     ; and old priority
  260.  
  261.           MOV    #KHNDLR,@#KINTV     ; store interrupt handler address
  262.           MOV    #200,@#KINTV+2      ; set interrupt priority 4 for TTY input
  263.           ;MOV    #100,@#KSR          ; enable interrupts for TTY input
  264. ;
  265.           MOV    (SP)+,R0        ; pop return address from stack
  266.           ADD    #4,SP           ; discard 2 parameters (4 bytes)
  267.           JMP    @R0             ; and return to PASCAL interpreter
  268. ;
  269. KQADRS    .WORD   0              ; holds Q address for handler
  270. KBDLOC    .WORD   0              ; holds old interrupt vector
  271. KBDPR     .WORD   0              ; holds old interrupt priority
  272. ;
  273. QSIZE     .EQU    0              ; offset from Q
  274. INP       .EQU    2              ; likewise
  275. OUTP      .EQU    4
  276. MAXCHAR   .EQU    6
  277. DATA      .EQU   10
  278. ;
  279. KHNDLR:   MOV    R0,-(SP)        ; free registers R0, R1, R2 for use
  280.           MOV    R1,-(SP)
  281.           MOV    R2,-(SP)
  282.           MOV    KQADRS,R2       ; fetch Q address saved by KBDINIT
  283.           MOV    INP(R2),R0      ; fetch INP value
  284.           MOV    R0,R1           ; make a working copy
  285.           ADD    R2,R1           ; R1 = address (Q) + value (INP)
  286.           MOVB   @#KDB,DATA(R1)  ; DATA[INP] := input character
  287.           BICB   #200,DATA(R1)   ; clear bit 8 (parity)
  288.           BEQ    EXIT            ; ignore nulls (do not bump INP)
  289.           INC    R0              ; INP := INP+1
  290.           CMP    QSIZE(R2),R0
  291.           BPL    NOWRAP          ; if QSIZE >= INP then no wraparound
  292.           CLR    R0              ; else INP := 0
  293. NOWRAP    MOV    R0,INP(R2)      ; restore INP
  294. ;
  295.           SUB    OUTP(R2),R0
  296.           BMI    INOUT
  297.           BEQ    INOUT
  298.           BR     OUTIN           ; if INP > OUTP, # char = INP - OUTP
  299. INOUT     ADD    QSIZE(R2),R0    ; otherwise, # char = QSIZE+1 + INP - OUTP
  300.           ADD    #1,R0
  301. OUTIN     CMP    MAXCHAR(R2),R0
  302.           BPL    EXIT            ; if MAXCHAR >= # char, exit
  303.           MOV    R0,MAXCHAR(R2)  ; otherwise, store new MAXCHAR
  304. ;
  305. EXIT      MOV    (SP)+,R2        ; restore registers for caller
  306.           MOV    (SP)+,R1
  307.           MOV    (SP)+,R0
  308.           RTT                    ; return from interrupt
  309. ;
  310. CLKHNDLR: COM    CLKFLG          ; do not reexecute BPT if BPT handler
  311.           BEQ    CLKEXIT         ;   takes so long that clock ticks again
  312.           BPT                    ; let breakpoint transfer to old clock
  313. CLKEXIT   COM    CLKFLG          ; reset flag
  314.           RTI                    ; and exit
  315. ;
  316. CLKFLG    .WORD  0               ; flags reentry before BPT exit
  317. ;
  318.           .PROC  KBDFINIT
  319.           .REF   KBDLOC          ; old interrupt vector saved by KBDINIT
  320.           .REF   KBDPR           ; old kbd priority saved by KBDINIT
  321. ;
  322.            MOV    @#KBDPR,@#KINTV+2    ; restore interrupt priority
  323.            MOV    @#KBDLOC,@#KINTV     ; and interrupt vector
  324.            RTS    PC                   ; and return
  325. ;
  326.            .END
  327.  
  328.  
  329. >>>> KERMIT.TEXT
  330. program kermit;
  331.  
  332. (* $R-*) (* turn range checking off *)
  333. (*$S+*) (* turn swapping on *)
  334. (* $L+*)
  335. (*$U PARSELIB.CODE*)
  336. USES PARSER;
  337.  
  338. const blksize = 512;
  339.       oport = 8;          (* output port # *)
  340.       clearscreen = 12;   (* charcter which erases screen *)
  341.       bell = 7;           (* ASCII bell *)
  342.       maxpack = 93;       (* maximum packet size minus 1 *)
  343.       soh = 1;            (* start of header *)
  344.       sp = 32;            (* ASCII space *)
  345.       cr = 13;            (* ASCII CR *)
  346.       lf = 10;            (* ASCII line feed *)
  347.       dle = 16;           (* ASCII DLE (space compression prefix for psystem) *)
  348.       del = 127;          (* delete *)
  349.       my_esc = 29;        (* default esc char for connect (^]) *)
  350.       maxtry = 5;         (* number of times to retry sending packet *)
  351.       my_quote = '#';     (* quote character I'll use *)
  352.       my_pad = 0;         (* number of padding chars I need *)
  353.       my_pchar = 0;       (* padding character I need *)
  354.       my_eol = 13;        (* end of line character i need *)
  355.       my_time = 5;        (* seconds after which I should be timed out *)
  356.       maxtim = 20;        (* maximum timeout interval *)
  357.       mintim = 2;         (* minimum time out interval *)
  358.       at_eof = -1;        (* value to return if at eof *)
  359.       rqsize = 5000;      (* input queue size *)
  360.       qsize1 = 5001;      (* qsize + 1 *)
  361.       eoln_sym = 13;      (* pascal eoln sym *)
  362.       back_space = 8;     (* pascal backspace sym *)
  363.  
  364. (* screen control information *)
  365.   (* console line on which to put specified info *)
  366.       title_line = 1;
  367.       statusline = 2;
  368.       packet_line = 3;
  369.       retry_line = 4;
  370.       file_line = 5;
  371.       error_line = 6;
  372.       debug_line = 7;
  373.       prompt_line = 8;
  374.   (* position on line to put info *)
  375.       statuspos = 70;
  376.       packet_pos = 19;
  377.       retry_pos = 17;
  378.       file_pos = 11;
  379.  
  380. type queue = record (* input queue *)
  381.                  qsize: integer;
  382.                  inp: integer;
  383.                  outp: integer;
  384.                  maxchar: integer;
  385.                  data: packed array[0..rqsize] of char;
  386.                end; (* queue *)
  387.      packettype = packed array[0..maxpack] of char;
  388.      parity_type = (evenpar, oddpar, markpar, spacepar, nopar);
  389.  
  390.      char_int_rec = record (* allows character to be treated as integer... *)
  391.                            (* is system dependent *)
  392.                       case boolean of
  393.                           true: (i: integer);
  394.                           false: (ch: char)
  395.                     end; (* record *)
  396.  
  397.      int_bool_rec = record (* allows integer to be treated as boolean... *)
  398.                            (* used for numeric AND,OR,XOR...system dependent *)
  399.                       case boolean of
  400.                           true: (i: integer);
  401.                           false: (b: boolean)
  402.                     end; (* record *)
  403.  
  404. var kq, rq: queue;
  405.     state: char; (* current state *)
  406.     f: file of char; (* file to be received *)
  407.     oldf: file; (* file to be sent *)
  408.     s: string;
  409.     eol, quote, esc_char: char;
  410.     fwarn, ibm, half_duplex, debug: boolean;
  411.     i, size, rpsiz, spsiz, pad, n, num_try, oldtry, timint: integer;
  412.     recpkt, packet: packettype;
  413.     padchar, ch: char;
  414.     debf: text; (* file for debug output *)
  415.     parity: parity_type;
  416.     xon: char;
  417.     filebuf: packed array[1..1024] of char;
  418.     bufpos, bufend: integer;
  419.     parity_array: packed array[char] of char;
  420.     ctlset: set of char;
  421.     rec_ok, send_ok: boolean;
  422.  
  423. function read_ch(var q: queue; var ch: char): boolean;
  424. forward;
  425.  
  426. procedure clear_buf(var q: queue);
  427. forward;
  428.  
  429. function aand(x,y: integer): integer;
  430. forward;
  431.  
  432. function aor(x,y: integer): integer;
  433. forward;
  434.  
  435. function xor(x,y: integer): integer;
  436. forward;
  437.  
  438. procedure error(p: packettype; len: integer);
  439. forward;
  440.  
  441. procedure io_error(i: integer);
  442. forward;
  443.  
  444. procedure debugwrite(s: string);
  445. forward;
  446.  
  447. procedure debugint(s: string; i: integer);
  448. forward;
  449.  
  450. procedure writescreen(s: string);
  451. forward;
  452.  
  453. procedure refresh_screen(numtry, num: integer);
  454. forward;
  455.  
  456. function min(x,y: integer): integer;
  457. forward;
  458.  
  459. function tochar(ch: char): char;
  460. forward;
  461.  
  462. function unchar(ch: char): char;
  463. forward;
  464.  
  465. function ctl(ch: char): char;
  466. forward;
  467.  
  468. function getfil(filename: string): boolean;
  469. forward;
  470.  
  471. procedure bufemp(buffer: packettype; var f: text; len: integer);
  472. forward;
  473.  
  474. function bufill(var buffer: packettype): integer;
  475. forward;
  476.  
  477. procedure spar(var packet: packettype);
  478. forward;
  479.  
  480. procedure rpar(var packet: packettype);
  481. forward;
  482.  
  483. procedure spack(ptype: char; num:integer; len: integer; data: packettype);
  484. forward;
  485.  
  486. function getch(var r: char_int_rec; var q: queue): boolean;
  487. forward;
  488.  
  489. function getsoh(var q: queue): boolean;
  490. forward;
  491.  
  492. function rpack(var len, num: integer; var data: packettype): char;
  493. forward;
  494.  
  495. procedure read_str(var q: queue; var s: string);
  496. forward;
  497.  
  498. procedure show_parms;
  499. forward;
  500.  
  501. (*$I HELP.TEXT*)
  502. (*$I SENDSW.TEXT*)
  503. (*$I RECSW.TEXT*)
  504.  
  505. procedure rcvinit(var q: queue; size: integer);
  506.   external;
  507.  
  508. procedure rcvfinit;
  509.   external;
  510.  
  511. procedure kbdinit(var q: queue; size: integer);
  512.   external;
  513.  
  514. procedure kbdfinit;
  515.   external;
  516.  
  517. procedure sendbrk;
  518.   external;
  519.  
  520. procedure read_str(*var q: queue; var s: string*);
  521.  
  522. (* acts like readln(s) but takes input from input queue *)
  523.  
  524. var i: integer;
  525.  
  526.   begin
  527.     i := 0;
  528.     s := copy('',0,0);
  529.     repeat
  530.       repeat                              (* get a character *)
  531.       until read_ch(kq,ch);
  532.       if (ord(ch) = backspace) then       (* if it's a backspace then *)
  533.         begin
  534.           if (i > 0) then                   (* if not at beginning of line *)
  535.             begin
  536.               write(ch);                      (* go back a space on screen *)
  537.               write(' ');                     (* erase char on screen *)
  538.               write(ch);                      (* go back a space again *)
  539.               i := i - 1;                     (* adjust string counter *)
  540.               s := copy(s,1,i)                (* adjust string *)
  541.             end (* if *)
  542.         end (* if *)
  543.       else if (ord(ch) <> eoln_sym) then  (* otherwise if not at eoln  then *)
  544.         begin
  545.           write(ch);                        (* echo char on screen *)
  546.           i := i + 1;                       (* inc string counter *)
  547.           s := concat(s,' ');
  548.           s[i] := ch;                       (* put char in string *)
  549.         end; (* if *)
  550.     until (ord(ch) = eoln_sym);           (* if not eoln, get another char *)
  551.     s := copy(s,1,i);                     (* correct string length *)
  552.     writeln                               (* write a line on the screen *)
  553.   end; (* read_str *)
  554.  
  555. function aand(*x,y: integer): integer*);
  556.  
  557. (* arithmetic and--takes 2 integers and ands them, yeilding an integer *)
  558.  
  559. var xrec, yrec, temp: int_bool_rec;
  560.  
  561.   begin
  562.     xrec.i := x;                  (* put the two numbers in variant record *)
  563.     yrec.i := y;
  564.     temp.b := xrec.b and yrec.b;  (* use as booleans to 'and' them *)
  565.     aand := temp.i                (* return integer result *)
  566.   end; (* aand *)
  567.  
  568. function aor(*x,y: integer): integer*);
  569.  
  570. (* arithmetic or *)
  571.  
  572. var xrec, yrec, temp: int_bool_rec;
  573.  
  574.   begin
  575.     xrec.i := x;                  (* put two numbers in variant record *)
  576.     yrec.i := y;
  577.     temp.b := xrec.b or yrec.b;   (* use as booleans to 'or' them *)
  578.     aor := temp.i                 (* return integer result *)
  579.   end; (* aor *)
  580.  
  581. function xor(*x,y: integer): integer*);
  582.  
  583. (* exclisive or *)
  584.  
  585. var xrec, yrec, temp: int_bool_rec;
  586.  
  587.   begin
  588.     xrec.i := x;                  (* put two numbers in variant record *)
  589.     yrec.i := y;
  590.                                   (* use as booleans to 'xor' them *)
  591.     temp.b := (xrec.b or yrec.b) and (not(xrec.b and yrec.b));
  592.     xor := temp.i                 (* return integer result *)
  593.   end; (* xor *)
  594.  
  595. procedure error(*p: packettype; len: integer*);
  596.  
  597. (* writes error message sent by remote host *)
  598.  
  599. var i: integer;
  600.  
  601.   begin
  602.     gotoxy(0,errorline);
  603.     for i := 0 to len-1 do
  604.         write(p[i]);
  605.     gotoxy(0,promptline);
  606.   end; (* error *)
  607.  
  608. procedure io_error(*i: integer*);
  609.  
  610.   begin
  611.     gotoxy(0,errorline);
  612.     write(chr(27),'K');         (* erase to end of line *)
  613.     case i of
  614.         0: writeln('No error');
  615.         1: writeln('Bad Block, Parity error (CRC)');
  616.         2: writeln('Bad Unit Number');
  617.         3: writeln('Bad Mode, Illegal operation');
  618.         4: writeln('Undefined hardware error');
  619.         5: writeln('Lost unit, Unit is no longer on-line');
  620.         6: writeln('Lost file, File is no longer in directory');
  621.         7: writeln('Bad Title, Illegal file name');
  622.         8: writeln('No room, insufficient space');
  623.         9: writeln('No unit, No such volume on line');
  624.         10: writeln('No file, No such file on volume');
  625.         11: writeln('Duplicate file');
  626.         12: writeln('Not closed, attempt to open an open file');
  627.         13: writeln('Not open, attempt to close a closed file');
  628.         14: writeln('Bad format, error in reading real or integer');
  629.         15: writeln('Ring buffer overflow')
  630.       end; (* case *)
  631.     gotoxy(0,promptline)
  632.   end; (* io_error *)
  633.  
  634. procedure debugwrite(*s: string*);
  635.  
  636. (* writes a debugging message *)
  637. var i: integer;
  638.  
  639.   begin
  640.     if debug then
  641.       begin
  642.         gotoxy(0,debugline);
  643.         write(chr(27),'K');         (* erase to end of line *)
  644.         write(s);
  645.         for i := 1 to 2000 do ;                (* write debugging message *)
  646.       end (* if debug *)
  647.   end; (* debugwrite *)
  648.  
  649. procedure debugint(*s: string; i: integer*);
  650.  
  651. (* write a debugging message and an integer *)
  652.  
  653.   begin
  654.     if debug then
  655.       begin
  656.         debugwrite(s);
  657.         write(i)
  658.       end (* if debug *)
  659.   end; (* debugint *)
  660.  
  661. procedure writescreen(*s: string*);
  662.  
  663. (* sets up the screen for receiving or sending files *)
  664.  
  665.   begin
  666.     write(chr(clearscreen));
  667.     gotoxy(0,titleline);
  668.     write('                        Kermit UCSD p-system');
  669.     gotoxy(statuspos,statusline);
  670.     write(s);
  671.     gotoxy(0,packetline);
  672.     write('Number of Packets: ');
  673.     gotoxy(0,retryline);
  674.     write('Number of Tries: ');
  675.     gotoxy(0,fileline);
  676.     write('File Name: ');
  677.   end; (* writescreen *)
  678.  
  679. procedure refresh_screen(*numtry, num: integer*);
  680.  
  681. (* keeps track of packet count on screen *)
  682.  
  683.   begin
  684.     gotoxy(retrypos,retryline);
  685.     write(numtry: 5);
  686.     gotoxy(packetpos,packetline);
  687.     write(num: 5)
  688.   end; (* refresh_screen *)
  689.  
  690. function min(*x,y: integer): integer*);
  691.  
  692. (* returns smaller of two integers *)
  693.  
  694.   begin
  695.     if x < y then
  696.         min := x
  697.     else
  698.         min := y
  699.   end; (* min *)
  700.  
  701. function tochar(*ch: char): char*);
  702.  
  703. (* tochar converts a control character to a printable one by adding space *)
  704.  
  705.   begin
  706.     tochar := chr(ord(ch) + ord(' '))
  707.   end; (* tochar *)
  708.  
  709. function unchar(*ch: char): char*);
  710.  
  711. (* unchar undoes tochar *)
  712.  
  713.   begin
  714.     unchar := chr(ord(ch) - ord(' '))
  715.   end; (* unchar *)
  716.  
  717. function ctl(*ch: char): char*);
  718.  
  719. (* ctl toggles control bit: ^A becomes A, A becomes ^A *)
  720.  
  721.   begin
  722.     ctl := chr(xor(ord(ch),64))
  723.   end; (* ctl *)
  724.  
  725. procedure echo(ch: char);
  726.  
  727. (* echos a character on the screen *)
  728.  
  729.   begin
  730.     ch := chr(aand(ord(ch),127)); (* mask off parity bit *)
  731.     if ch <> chr(lf) then
  732.       begin
  733.         unitwrite(1,ch,1)
  734.       end (* if *)
  735.   end; (* echo *)
  736.  
  737. procedure clear_buf(*var q: queue*);
  738.  
  739. (* empties the buffer input buffer *)
  740.  
  741.   begin
  742.     q.outp := q.inp
  743.   end; (* clear_buf *)
  744.  
  745. function getfil(*filename: string): boolean*);
  746.  
  747. (* opens a file for writing *)
  748.  
  749.   begin
  750.     (*$I-*) (* turn i/o checking off *)
  751.     rewrite(f,filename);
  752.     (*$I-*) (* turn i/o checking on *)
  753.     getfil := (ioresult = 0)
  754.   end; (* getfil *)
  755.  
  756. procedure bufemp(*buffer: packettype; var f: text; len: integer*);
  757.  
  758. (* empties a packet into a file *)
  759.  
  760. var i,ls: integer;
  761.     r: char_int_rec;
  762.     s: string;
  763.  
  764.   begin
  765.     s := copy('',0,0);
  766.     ls := 0;
  767.     i := 0;
  768.     while i < len do
  769.       begin
  770.         r.ch := buffer[i];          (* get a character *)
  771.         if (r.ch = myquote) then    (* if character is control quote *)
  772.           begin
  773.             i := i + 1;               (* skip over quote and *)
  774.             r.ch := buffer[i];        (* get quoted character *)
  775.             if (aand(r.i,127) <> ord(myquote)) then
  776.                 r.ch := ctl(r.ch);    (* controllify it *)
  777.           end; (* if *)
  778.         if (r.i = cr) then          (* else if a carriage return then *)
  779.           begin
  780.             i := i + 3;               (* skip over that and line feed *)
  781.             (*$I-*)                   (* turn i/o checking off *)
  782.             writeln(f,s);             (* and write out line to file *)
  783.             s := copy('',0,0);        (* empty the string var *)
  784.             ls := 0;
  785.             if (io_result <> 0) then  (* if io_error *)
  786.               begin
  787.                 io_error(ioresult);     (* tell them and *)
  788.                 state := 'a';           (* abort *)
  789.               end (* if *)
  790.           end
  791.       (*$I+*)                      (* turn i/o checking back on *)
  792.       else                        (* else, is a regular char, so *)
  793.           begin
  794.             r.i := aand(r.i,127);     (* mask off parity bit *)
  795.             s := concat(s,' ');       (* and add character to out string *)
  796.             ls := ls + 1;
  797.             s[ls] := r.ch;
  798.             i := i + 1                (* increase buffer pointer *)
  799.           end; (* else *)
  800.       end; (* while *)              (* and get another char *)
  801.       (*$I-*)                     (* turn i/o checking off *)
  802.       write(f,s);                 (* and write out line to file *)
  803.       if (io_result <> 0) then    (* if io_error *)
  804.         begin
  805.           io_error(ioresult);       (* tell them and *)
  806.           state := 'a';             (* abort *)
  807.         end (* if *)
  808.       (*$I+*)                      (* turn i/o checking back on *)
  809.   end; (* bufemp *)
  810.  
  811. function bufill(*var buffer: packettype): integer*);
  812.  
  813. (* fill a packet with data from a file...manages a 2 block buffer *)
  814.  
  815. var i, j, k, t7, count: integer;
  816.     r: char_int_rec;
  817.  
  818.   begin
  819.     i := 0;
  820.     (* while file has some data & packet has some room we'll keep going *)
  821.     while ((bufpos <= bufend) or (not eof(oldf))) and (i < spsiz-9) do
  822.       begin
  823.         (* if we need more data from disk then *)
  824.         if (bufpos > bufend) and (not eof(oldf)) then
  825.           begin
  826.             (* read a couple of blocks *)
  827.             bufend := blockread(oldf,filebuf[1],2) * blksize;
  828.             (* and adjust buffer pointer *)
  829.             bufpos := 1
  830.           end; (* if *)
  831.         if (bufpos <= bufend) then     (* if we're within buffer bounds *)
  832.           begin
  833.             r.ch := filebuf[bufpos];      (* get a character *)
  834.             bufpos := bufpos + 1;         (* increase buffer pointer *)
  835.             if (r.i = dle) then           (* if it's space compression char, *)
  836.               begin
  837.                 count := ord(unchar(filebuf[bufpos])); (* get # of spaces *)
  838.                 bufpos := bufpos + 1;       (* read past # *)
  839.                 r.ch := ' ';                (* and make current char a space *)
  840.               end (* else if *)
  841.             else                           (* otherwise, it's just a char *)
  842.                 count := 1;                (* so only 1 copy of it *)
  843.             if (r.ch in ctlset) then     (* if a control char *)
  844.               begin
  845.                 if (r.i = cr) then         (* if a carriage return *)
  846.                   begin
  847.                     buffer[i] := quote;      (* put (quoted) CR in buffer *)
  848.                     i := i + 1;
  849.                     buffer[i] := ctl(chr(cr));
  850.                     i := i + 1;
  851.                     r.i := lf;                (* and we'll stick a LF after *)
  852.                   end; (* if *)
  853.                 if r.i <> 0 then           (* if not a NUL then *)
  854.                   begin
  855.                     buffer[i] := quote;      (* put the quote in buffer *)
  856.                     i := i + 1;
  857.                     if r.ch <> quote then
  858.                         r.ch := ctl(r.ch);   (* and un-controllify char *)
  859.                   end (* if *)
  860.               end; (* if *)
  861.           end; (* if *)
  862.         j := 1;
  863.         while (j <= count) and (i <= spsiz - 5) do
  864.           begin                           (* put all the chars in buffer *)
  865.             if (r.i <> 0) then            (* so long as not a NUL *)
  866.               begin
  867.                 buffer[i] := r.ch;
  868.                 i := i + 1;
  869.               end (* if *)
  870.             else                          (* is a NUL so *)
  871.                 if (bufpos > blksize) then  (* skip to end of block *)
  872.                     bufpos := bufend + 1    (* since rest will be NULs *)
  873.                 else
  874.                     bufpos := blksize + 1;
  875.             j := j + 1
  876.           end; (* while *)
  877.       end; (* while *)
  878.     if (i = 0) then                         (* if we're at end of file, *)
  879.         bufill := (at_eof)                    (* indicate it *)
  880.     else                                    (* else *)
  881.       begin
  882.         if (j <= count) then                  (* if didn't all fit in packet *)
  883.           begin
  884.             bufpos := bufpos - 2;               (* put buf pointer at DLE *)
  885.                                                 (* and update compress count *)
  886.             filebuf[bufpos + 1] := tochar(chr(count-j+1));
  887.           end; (* if *)
  888.         bufill := i                           (* return # of chars in packet *)
  889.       end; (* else *)
  890.   end; (* bufill *)
  891.  
  892. procedure spar(*var packet: packettype*);
  893.  
  894. (* fills data array with my send-init parameters *)
  895.  
  896.   begin
  897.     packet[0] := tochar(chr(maxpack));   (* biggest packet i can receive *)
  898.     packet[1] := tochar(chr(mytime));    (* when i want to be timed out *)
  899.     packet[2] := tochar(chr(mypad));     (* how much padding i need *)
  900.     packet[3] := ctl(chr(mypchar));      (* padding char i want *)
  901.     packet[4] := tochar(chr(myeol));     (* end of line character i want *)
  902.     packet[5] := myquote;                (* control-quote char i want *)
  903.     packet[6] := 'N';                    (* I won't do 8-bit quoting *)
  904.   end; (* spar *)
  905.  
  906. procedure rpar(*var packet: packettype*);
  907.  
  908. (* gets their init params *)
  909.  
  910.   begin
  911.     spsiz := ord(unchar(packet[0]));     (* max send packet size *)
  912.     timint := ord(unchar(packet[1]));    (* when i should time out *)
  913.     pad := ord(unchar(packet[2]));       (* number of pads to send *)
  914.     padchar := ctl(packet[3]);           (* padding char to send *)
  915.     eol := unchar(packet[4]);            (* eol char i must send *)
  916.     quote := packet[5];                  (* incoming data quote char *)
  917.   end; (* rpar *)
  918.  
  919. procedure packetwrite(p: packettype; len: integer);
  920.  
  921. (* writes out all of a packet for debugging purposes *)
  922.  
  923. var i: integer;
  924.  
  925.   begin
  926.     gotoxy(0,debugline);
  927.     for i := 0 to len+3 do
  928.       begin
  929.         if i = 80 then
  930.           begin
  931.             gotoxy(0,debugline+1);
  932.             write(chr(27),'K');
  933.           end; (* if *)
  934.         write(p[i])
  935.       end; (* for *)
  936.     for i := 1 to 2000 do ;
  937.   end; (* packetwrite *)
  938.  
  939. procedure spack(*ptype: char; num: integer; len: integer; data: packettype*);
  940.  
  941. (* send a packet *)
  942.  
  943. const maxtry = 10000;
  944.  
  945. var bufp, i, count: integer;
  946.     chksum: char;
  947.     buffer: packettype;
  948.     ch: char;
  949.  
  950.   begin
  951.     if ibm and (state <> 's') then           (* if ibm and not SINIT then *)
  952.       begin
  953.         count := 0;
  954.         repeat                                 (* wait for an xon *)
  955.             repeat
  956.                 count := count + 1
  957.             until (readch(rq,ch)) or (count > maxtry );
  958.         until (ch = xon) or (count > maxtry);
  959.         if count > maxtry then                 (* if wait too long then *)
  960.           begin
  961.             exit(spack)                          (* get out *)
  962.           end; (* if *)
  963.       end; (* if *)
  964.  
  965.     bufp := 0;
  966.     for i := 1 to pad do
  967.         unitwrite(oport,padchar,1);          (* write out any padding chars *)
  968.     buffer[bufp] := chr(soh);                (* packet sync character *)
  969.     bufp := bufp + 1;
  970.     chksum := tochar(chr(len + 3));          (* init chksum *)
  971.     buffer[bufp] := tochar(chr(len + 3));    (* character count *)
  972.     bufp := bufp + 1;
  973.     chksum := chr(ord(chksum) + ord(tochar(chr(num))));
  974.     buffer[bufp] := tochar(chr(num));
  975.     bufp := bufp + 1;
  976.     chksum := chr(ord(chksum) + ord(ptype));
  977.     buffer[bufp] := ptype;                   (* packet type *)
  978.     bufp := bufp + 1;
  979.  
  980.     for i := 0 to len - 1 do                 (* loop through data chars *)
  981.       begin
  982.         buffer[bufp] := data[i];             (* store char *)
  983.         bufp := bufp + 1;
  984.         chksum := chr(ord(chksum) + ord(data[i]))
  985.       end; (* for i *)
  986.                                              (* compute final chksum *)
  987.     chksum := chr(aand(ord(chksum) + (aand(ord(chksum),192) div 64), 63));
  988.     buffer[bufp] := tochar(chksum);
  989.     bufp := bufp + 1;
  990.     buffer[bufp] := eol;
  991.  
  992.     if (parity <> nopar) then
  993.         for i := 0 to bufp do                 (* set correct parity on buffer *)
  994.             buffer[i] := parity_array[buffer[i]];
  995.  
  996.     unitwrite(oport,buffer[0],bufp+1);        (* send the packet out *)
  997.  
  998.     if debug then
  999.         packetwrite(buffer,len);
  1000.   end; (* spack *)
  1001.  
  1002. function read_ch(*var q: queue; var ch: char): boolean*);
  1003.  
  1004. (* read a character from an input queue *)
  1005.  
  1006.   begin
  1007.     with q do
  1008.       begin
  1009.         if (inp <> outp) then            (* if a char there *)
  1010.           begin
  1011.             ch := data[outp];              (* get the char *)
  1012.             outp := (outp + 1) mod qsize1; (* increment buffer pointer *)
  1013.             read_ch := true;               (* and return true *)
  1014.           end (* if *)
  1015.         else                             (* otherwise *)
  1016.             read_ch := false;              (* return false *)
  1017.       end (* with *)
  1018.   end; (* read_ch *)
  1019.  
  1020. function getch(*var r: char_int_rec; var q: queue): boolean*);
  1021.  
  1022. (* gets a character, strips parity, returns true if it got a char which *)
  1023. (* isn't Kermit SOH, false if it gets SOH or nothing after maxtry *)
  1024.  
  1025. const maxtry = 10000;
  1026.  
  1027. var count: integer;
  1028.  
  1029.   begin
  1030.     count := 0;
  1031.     getch := false;
  1032.     with q do
  1033.       begin
  1034.         repeat
  1035.             count := count + 1;
  1036.         until (inp <> outp) or (count > maxtry);    (* wait for a character *)
  1037.         if (count > maxtry) then                    (* if wait too long then *)
  1038.             exit(getch);                              (* get out of here *)
  1039.         r.ch := data[outp];                         (* get the character *)
  1040.         outp := (outp + 1) mod qsize1;              (* increment pointer *)
  1041.         r.i := aand(r.i,127);                       (* strip parity from char *)
  1042.         getch := (r.ch <> chr(soh));                (* return true if not SOH *)
  1043.       end (* with *)
  1044.   end; (* getch *)
  1045.  
  1046. function getsoh(*var q: queue): boolean*);
  1047.  
  1048. (* reads characters until it finds an SOH; returns false if has to read more *)
  1049. (* than maxtry chars *)
  1050.  
  1051. const maxtry = 10000;
  1052.  
  1053. var ch: char;
  1054.     count: integer;
  1055.  
  1056.   begin
  1057.     count := 0;
  1058.     get_soh := true;
  1059.     with q do
  1060.       begin
  1061.         repeat
  1062.             repeat
  1063.                 count := count + 1
  1064.             until (inp <> outp) or (count > maxtry); (* wait for a character *)
  1065.             if (count > maxtry) then
  1066.               begin
  1067.                 get_soh := false;
  1068.                 exit(get_soh)
  1069.               end; (* if *)
  1070.             ch := data[outp];                        (* get the character *)
  1071.             outp := (outp + 1) mod qsize1;           (* increment pointer *)
  1072.             ch := chr(aand(ord(ch),127));            (* strip parity of char *)
  1073.         until (ch = chr(SOH))                        (* if not SOH, get more *)
  1074.     end (* with q *)
  1075.   end; (* getsoh *)
  1076.  
  1077. (*$G+*) (* turn on goto option...need it for next routine *)
  1078.  
  1079. function rpack(*var len, num: integer; var data: packettype): char*);
  1080.  
  1081. (* read a packet *)
  1082.  
  1083. label 1; (* used to emulate C's CONTINUE statement *)
  1084.  
  1085. const maxtry = 10000;
  1086.  
  1087. var count, i, ichksum: integer;
  1088.     chksum, ptype: char;
  1089.     r: char_int_rec;
  1090.  
  1091.   begin
  1092.     count := 0;
  1093.  
  1094.     if not getsoh(rq) and (state<>'r') then (*if don't get synch char then *)
  1095.       begin
  1096.         rpack := 'N';                        (* treat as a NAK *)
  1097.         num := n mod 64;
  1098.         exit(rpack)                          (* and get out of here *)
  1099.       end;
  1100.  
  1101.   1: count := count + 1;
  1102.      if (count>maxtry)and(state<>'r') then (* if we've tried too many times *)
  1103.         begin                               (* and aren't waiting for init *)
  1104.           rpack := 'N';                      (* treat as NAK *)
  1105.           exit(rpack)                        (* and get out of here *)
  1106.         end; (* if *)
  1107.  
  1108.     if not getch(r,rq) then                (* get a char and *)
  1109.             goto 1;                        (* resynch if soh *)
  1110.  
  1111.     ichksum := r.i;                        (* start checksum *)
  1112.     len := ord(unchar(r.ch)) - 3;          (* character count *)
  1113.  
  1114.     if not getch(r,rq) then                (* get a char and *)
  1115.         goto 1;                            (* resynch if soh *)
  1116.     ichksum := ichksum + r.i;
  1117.     num := ord(unchar(r.ch));              (* packet number *)
  1118.  
  1119.     if not getch(r,rq) then                (* get a char and *)
  1120.         goto 1;                            (* resynch if soh *)
  1121.     ichksum := ichksum + r.i;
  1122.     ptype := r.ch;                         (* packet type *)
  1123.  
  1124.     for i := 0 to len-1 do                 (* get any data *)
  1125.       begin
  1126.         if not getch(r,rq) then            (* get a char and *)
  1127.             goto 1;                        (* resynch if soh *)
  1128.         ichksum := ichksum + r.i;
  1129.         data[i] := r.ch;
  1130.       end; (* for i *)
  1131.     data[len] := chr(0);                   (* mark end of data *)
  1132.  
  1133.     if not getch(r,rq) then                (* get a char and *)
  1134.         goto 1;                            (* resynch if soh *)
  1135.  
  1136.                                            (* compute final checksum *)
  1137.     chksum := chr(aand(ichksum + (aand(ichksum,192) div 64), 63));
  1138.  
  1139.     if (chksum <> unchar(r.ch)) then       (* if checksum bad *)
  1140.         rpack := chr(0)                      (* return 'false' indicator *)
  1141.     else                                   (* else *)
  1142.         rpack := ptype;                      (* return packet type *)
  1143.  
  1144.     if debug then
  1145.       begin
  1146.         gotoxy(0,debugline);
  1147.         write(len,num,ptype);
  1148.         for i := 1 to 1000 do
  1149.             ;
  1150.       end; (* if *)
  1151.   end; (* rpack *)
  1152.  
  1153. (*$G-*) (* turn off goto option...don't need it anymore *)
  1154.  
  1155. procedure connect;
  1156.  
  1157. (* connect to remote host (terminal emulation *)
  1158.  
  1159. var ch: char;
  1160.     close: boolean;
  1161.  
  1162.   procedure read_esc;
  1163.  
  1164.   (* read charcter after esc char and interpret it *)
  1165.  
  1166.     begin
  1167.       repeat
  1168.       until read_ch(kq,ch);       (* wait until they've typed something in *)
  1169.       if (ch in ['a'..'z']) then  (* uppercase it *)
  1170.           ch := chr(ord(ch) - ord('a') + ord('A'));
  1171.       if ch in ['B','C','S','?'] then
  1172.           case ch of
  1173.               'B': sendbrk;       (* B: send a break to the IBM *)
  1174.               'C': close := true; (* C: end connection *)
  1175.               'S': begin          (* S: show status *)
  1176.                   noun := allsym;
  1177.                   showparms
  1178.                 end; (* S *)
  1179.               '?': begin          (* ?: show options *)
  1180.                   writeln('B    Send a BREAK signal.');
  1181.                   write('C    Close Connection, return to ');
  1182.                   writeln('KERMIT-UCSD command level.');
  1183.                   writeln('S    Show Status of connection');
  1184.                   writeln('?    Print this list');
  1185.                   write('^',esc_char,'   send the escape ');
  1186.                   writeln('character itself to the');
  1187.                   writeln('     remote host.')
  1188.                 end; (* ? *)
  1189.             end (* case *)
  1190.       else if ch = esc_char then  (* ESC-char: send it out *)
  1191.         begin
  1192.           if half_duplex then
  1193.             begin
  1194.               echo(ch);
  1195.               unitwrite(oport,ch,1)
  1196.             end (* if *)
  1197.         end (* else if *)
  1198.       else                        (* anything else: ignore *)
  1199.           write(chr(bell))
  1200.     end; (* read_esc *)
  1201.  
  1202.   begin (* connect *)
  1203.     clear_buf(kq);                    (* empty keyboard buffer *)
  1204.     clear_buf(rq);                    (* empty remote input buffer *)
  1205.     writeln('Connecting to host...type CTRL-',ctl(esc_char),' C to exit');
  1206.     close := false;
  1207.     repeat
  1208.         if read_ch(rq,ch) then        (* if char from host then *)
  1209.             echo(ch);                   (* echo it *)
  1210.  
  1211.         if read_ch(kq,ch) then        (* if char from keyboard then *)
  1212.             if ch <> esc_char then      (* if not ESC-char then *)
  1213.               begin
  1214.                 if half_duplex then       (* echo it if half-duplex *)
  1215.                     echo(ch);
  1216.                 unitwrite(oport,ch,1)     (* send it out the port *)
  1217.               end (* if *)
  1218.             else (* ch = esc_char *)    (* else is ESC-char so *)
  1219.               read_esc;                   (* interpret next char *)
  1220.     until close;                      (* if still connected, get more *)
  1221.     writeln('Disconnected')
  1222.   end; (* connect *)
  1223.  
  1224. procedure fill_parity_array;
  1225.  
  1226. (* parity value table for even parity...not(entry) = odd parity *)
  1227.  
  1228. const min = 0;
  1229.       max = 126;
  1230.  
  1231. var i, shifter, counter: integer;
  1232.     minch, maxch, ch: char;
  1233.     r: char_int_rec;
  1234.  
  1235.   begin
  1236.     minch := chr(min);
  1237.     maxch := chr(max);
  1238.     case parity of
  1239.       evenpar:
  1240.         begin
  1241.           for ch := minch to maxch do
  1242.             begin
  1243.               r.ch := ch;               (* put char into variant record *)
  1244.               shifter := aand(r.i,255); (* mask off parity bit *)
  1245.               counter := 0;
  1246.               for i := 1 to 7 do        (* count the 1's *)
  1247.                 begin
  1248.                   if odd(shifter) then
  1249.                       counter := counter + 1;
  1250.                   shifter := shifter div 2
  1251.                 end; (* for i *)
  1252.               if odd(counter) then       (* stick a 1 on if necessary *)
  1253.                   parity_array[ch] := chr(aor(ord(ch),128))
  1254.               else
  1255.                   parity_array[ch] := chr(aand(ord(ch),127))
  1256.             end; (* for ch *)
  1257.         end; (* case even *)
  1258.       oddpar:
  1259.         begin
  1260.           for ch := minch to maxch do
  1261.             begin
  1262.               r.ch := ch;                (* put char into variant record *)
  1263.               shifter := aand(r.i,255);  (* mask off parity bit *)
  1264.               counter := 0;
  1265.               for i := 1 to 7 do         (* count the 1's *)
  1266.                 begin
  1267.                   if odd(shifter) then
  1268.                       counter := counter + 1;
  1269.                   shifter := shifter div 2
  1270.                 end; (* for i *)
  1271.               if odd(counter) then        (* stick a 1 on if necessary *)
  1272.                   parity_array[ch] := chr(aand(ord(ch),127))
  1273.               else
  1274.                   parity_array[ch] := chr(aor(ord(ch),128))
  1275.             end; (* for ch *)
  1276.         end; (* case odd *)
  1277.       markpar:
  1278.           for ch := minch to maxch do     (* stick a 1 on all chars *)
  1279.               parity_array[ch] := chr(aor(ord(ch),128));
  1280.       spacepar:
  1281.           for ch := minch to maxch do     (* mask off parity on all chars *)
  1282.               parity_array[ch] := chr(aand(ord(ch),127));
  1283.       nopar:
  1284.           for ch := minch to maxch do     (* don't mess w/parity bit at all *)
  1285.               parity_array[ch] := ch;
  1286.     end; (* case *)
  1287.   end; (* fill_parity_array *)
  1288.  
  1289. procedure write_bool(s: string; b: boolean);
  1290.  
  1291. (* writes message & 'on' if b, 'off' if not b *)
  1292.   begin
  1293.     write(s);
  1294.     case b of
  1295.         true: writeln('on');
  1296.         false: writeln('off');
  1297.       end; (* case *)
  1298.   end; (* write_bool *)
  1299.  
  1300. procedure show_parms;
  1301.  
  1302. (* shows the various settable parameters *)
  1303.  
  1304.   begin
  1305.     case noun of
  1306.         allsym:
  1307.           begin
  1308.             write_bool('Debugging is ',debug);
  1309.             writeln('Escape character is ^',ctl(esc_char));
  1310.             write_bool('File warning is ',fwarn);
  1311.             write_bool('IBM is ',ibm);
  1312.             write_bool('Local echo is ',halfduplex);
  1313.             case parity of
  1314.                 evenpar: write('Even');
  1315.                 markpar: write('Mark');
  1316.                 nopar: write('No');
  1317.                 oddpar: write('Odd');
  1318.                 spacepar: write('Space');
  1319.               end; (* case *)
  1320.             writeln(' parity');
  1321.           end; (* allsym *)
  1322.         debugsym: write_bool('Debugging is ',debug);
  1323.         escsym: writeln('Escape character is ^',ctl(esc_char));
  1324.         filewarnsym: write_bool('File warning is ',fwarn);
  1325.         ibmsym: write_bool('IBM is ',ibm);
  1326.         localsym: write_bool('Local echo is ',halfduplex);
  1327.         paritysym: begin
  1328.             case parity of
  1329.                 evenpar: write('Even');
  1330.                 markpar: write('Mark');
  1331.                 nopar: write('No');
  1332.                 oddpar: write('Odd');
  1333.                 spacepar: write('Space');
  1334.               end; (* case *)
  1335.             writeln(' parity');
  1336.            end; (* paritysym *)
  1337.       end; (* case *)
  1338.   end; (* show_sym *)
  1339.  
  1340. procedure set_parms;
  1341.  
  1342. (* sets the parameters *)
  1343.  
  1344.   begin
  1345.     case noun of
  1346.         debugsym: case adj of
  1347.                       onsym: begin
  1348.                           debug := true;
  1349.                           (*$I-*)
  1350.                           rewrite(debf,'CONSOLE:')
  1351.                           (*I+*)
  1352.                         end; (* onsym *)
  1353.                       offsym: debug := false
  1354.                     end; (* case adj *)
  1355.         escsym: escchar := newescchar;
  1356.         filewarnsym: fwarn := (adj = onsym);
  1357.         ibmsym: case adj of
  1358.                     onsym: begin
  1359.                         ibm := true;
  1360.                         parity := markpar;
  1361.                         half_duplex := true;
  1362.                         fillparityarray
  1363.                       end; (* onsym *)
  1364.                     offsym: begin
  1365.                         ibm := false;
  1366.                         parity := nopar;
  1367.                         half_duplex := false;
  1368.                         fillparityarray
  1369.                       end; (* onsym *)
  1370.                   end; (* case adj *)
  1371.         localsym: halfduplex := (adj = onsym);
  1372.         paritysym: begin
  1373.               case adj of
  1374.                   evensym: parity := evenpar;
  1375.                   marksym: parity := markpar;
  1376.                   nonesym: parity := nopar;
  1377.                   oddsym: parity := oddpar;
  1378.                   spacesym: parity := spacepar;
  1379.                 end; (* case *)
  1380.               fill_parity_array;
  1381.              end; (* paritysym *)
  1382.       end; (* case *)
  1383.   end; (* set_parms *)
  1384.  
  1385. procedure initialize;
  1386.  
  1387. var ch: char;
  1388.  
  1389.   begin
  1390.     pad := mypad;
  1391.     padchar := chr(mypchar);
  1392.     eol := chr(my_eol);
  1393.     esc_char := chr(my_esc);
  1394.     quote := my_quote;
  1395.     ctlset := [chr(1)..chr(31),chr(del),quote];
  1396.     half_duplex := false;
  1397.     debug := false;
  1398.     fwarn := false;
  1399.     spsiz := max_pack;
  1400.     rpsiz := max_pack;
  1401.     n := 0;
  1402.     parity := nopar;
  1403.     initvocab;
  1404.     fill_parity_array;
  1405.     ibm := false;
  1406.     xon := chr(17);
  1407.     bufpos := 1;
  1408.     bufend := 0;
  1409.     rcvinit(rq,rqsize);
  1410.     kbdinit(kq,rqsize);
  1411.   end; (* initialize *)
  1412.  
  1413. procedure closeup;
  1414.  
  1415.   begin
  1416.     kbdfinit;
  1417.     rcvfinit;
  1418.     writeln(chr(clear_screen))
  1419.   end; (* closeup *)
  1420.  
  1421.   begin (* kermit *)
  1422.     initialize;
  1423.     repeat
  1424.         write('Kermit-UCSD> ');
  1425.         readstr(kq,line);
  1426.         case parse of
  1427.             unconfirmed: writeln('Unconfirmed');
  1428.             parm_expected: writeln('Parameter expected');
  1429.             ambiguous: writeln('Ambiguous');
  1430.             unrec: writeln('Unrecognized command');
  1431.             fn_expected: writeln('File name expected');
  1432.             ch_expected: writeln('Single character expected');
  1433.             null: case verb of
  1434.                       consym: connect;
  1435.                       helpsym: help;
  1436.                       recsym: begin
  1437.                           recsw(rec_ok);
  1438.                           gotoxy(0,debugline);
  1439.                           write(chr(bell));
  1440.                           if rec_ok then
  1441.                               writeln('successful receive')
  1442.                           else
  1443.                               writeln('unsuccessful receive');
  1444.                           (*$I-*) (* set i/o checking off *)
  1445.                           close(oldf);
  1446.                           (*$I+*) (* set i/o checking back on *)
  1447.                           gotoxy(0,promptline);
  1448.                         end; (* recsym *)
  1449.                       sendsym: begin
  1450.                           uppercase(filename);
  1451.                           sendsw(send_ok);
  1452.                           gotoxy(0,debugline);
  1453.                           write(chr(bell));
  1454.                           if send_ok then
  1455.                               writeln('successful send')
  1456.                           else
  1457.                               writeln('unsuccessful send');
  1458.                           (*$I-*) (* set i/o checking off *)
  1459.                           close(oldf);
  1460.                           (*$I+*) (* set i/o checking back on *)
  1461.                           gotoxy(0,promptline);
  1462.                         end; (* sendsym *)
  1463.                       setsym: set_parms;
  1464.                       show_sym: show_parms;
  1465.                   end; (* case verb *)
  1466.         end; (* case parse *)
  1467.      until (verb = exitsym) or (verb = quitsym);
  1468.      closeup
  1469.    end. (* kermit *)
  1470. >>>> PARSER.TEXT
  1471. (*$S+*)
  1472. unit parser;
  1473.  
  1474. INTERFACE
  1475.  
  1476. type statustype = (null, at_eol, unconfirmed, parm_expected, ambiguous,
  1477.                    unrec, fn_expected, ch_expected);
  1478.      vocab = (nullsym, allsym, consym, debugsym, escsym, evensym, exitsym,
  1479.               filewarnsym,helpsym, ibmsym, localsym, marksym, nonesym,
  1480.               oddsym, offsym, onsym, paritysym, quitsym, recsym, sendsym,
  1481.               setsym, showsym, spacesym);
  1482.  
  1483. var noun, verb, adj: vocab;
  1484.     status: statustype;
  1485.     vocablist: array[vocab] of string;
  1486.     filename, line: string;
  1487.     newescchar: char;
  1488.     expected: set of vocab;
  1489.  
  1490. procedure uppercase(var s: string);
  1491.  
  1492. function parse: statustype;
  1493.  
  1494. procedure initvocab;
  1495.  
  1496. IMPLEMENTATION
  1497.  
  1498. procedure uppercase(*var s: string*);
  1499.  
  1500. var i: integer;
  1501.  
  1502.   begin
  1503.     for i := 1 to length(s) do
  1504.         if s[i] in ['a'..'z'] then
  1505.             s[i] := chr(ord(s[i]) - ord('a') + ord('A'))
  1506.   end; (* uppercase *)
  1507.  
  1508. procedure eatspaces(var s: string);
  1509.  
  1510. var done: boolean;
  1511.     i: integer;
  1512.  
  1513.   begin
  1514.     done := (length(s) = 0);
  1515.     while not done do
  1516.       begin
  1517.         if s[1] = ' ' then
  1518.           begin
  1519.             i := length(s) - 1;
  1520.             s := copy(s,2,i);
  1521.             done := length(s) = 0
  1522.           end (* if *)
  1523.         else
  1524.             done := true
  1525.       end (* while *)
  1526.   end; (* eatspaces *)
  1527.  
  1528. procedure isolate_word(var line, s: string);
  1529.  
  1530. var i: integer;
  1531.     done: boolean;
  1532.  
  1533.   begin
  1534.     done := false;
  1535.     i := 1;
  1536.     s := copy(' ',0,0);
  1537.     while (i <= length(line)) and not done do
  1538.       begin
  1539.         if line[i] = ' ' then
  1540.             done := true
  1541.         else
  1542.             s := concat(s,copy(line,i,1));
  1543.         i := i + 1;
  1544.       end; (* while *)
  1545.     line := copy(line,i,length(line)-i+1);
  1546.   end; (* isolate_word *)
  1547.  
  1548. function get_fn(var line, fn: string): boolean;
  1549.  
  1550. var i, l: integer;
  1551.  
  1552.   begin
  1553.     get_fn := true;
  1554.     isolate_word(line, fn);
  1555.     l := length(fn);
  1556.     if (l < 1) then
  1557.         get_fn := false
  1558.   end; (* get_fn *)
  1559.  
  1560. function getch(var ch: char): boolean;
  1561.  
  1562. var s: string;
  1563.  
  1564.   begin
  1565.     isolate_word(line,s);
  1566.     if length(s) <> 1 then
  1567.         getch := false
  1568.     else
  1569.       begin
  1570.         ch := s[1];
  1571.         get_ch := true
  1572.       end (* else *)
  1573.   end; (* getch *)
  1574.  
  1575. function parse(*: statustype*);
  1576.  
  1577. type states = (start, fin, get_filename, get_set_parm, get_parity, get_on_off,
  1578.                get_char, get_show_parm, get_help_show, get_help_parm,
  1579.                exitstate);
  1580.  
  1581. var status: statustype;
  1582.     word: vocab;
  1583.     state: states;
  1584.  
  1585. function get_sym(var word: vocab): statustype;
  1586.  
  1587. var i: vocab;
  1588.     s: string;
  1589.     stat: statustype;
  1590.     done: boolean;
  1591.     matches: integer;
  1592.  
  1593.   begin
  1594.     eat_spaces(line);
  1595.     if length(line) = 0 then
  1596.         getsym := ateol
  1597.     else
  1598.       begin
  1599.         stat := null;
  1600.         done := false;
  1601.         isolate_word(line,s);
  1602.         i := allsym;
  1603.         matches := 0;
  1604.         repeat
  1605.             if (pos(s,vocablist[i]) = 1) and (i in expected) then
  1606.               begin
  1607.                 matches := matches + 1;
  1608.                 word := i
  1609.               end
  1610.             else if (s[1] < vocablist[i,1]) then
  1611.                 done := true;
  1612.             if (i = spacesym) then
  1613.                 done := true
  1614.             else
  1615.                 i := succ(i)
  1616.         until (matches > 1) or done;
  1617.         if matches > 1 then
  1618.             stat := ambiguous
  1619.         else if (matches = 0) then
  1620.             stat := unrec;
  1621.         getsym := stat
  1622.       end (* else *)
  1623.   end; (* getsym *)
  1624.  
  1625.   begin
  1626.     state := start;
  1627.     parse := null;
  1628.     noun := nullsym;
  1629.     verb := nullsym;
  1630.     adj := nullsym;
  1631.     uppercase(line);
  1632.     repeat
  1633.         case state of
  1634.           start:
  1635.               begin
  1636.                 expected := [consym, exitsym, helpsym, quitsym, recsym, sendsym,
  1637.                              setsym, showsym];
  1638.                 status := getsym(verb);
  1639.                 if status = ateol then
  1640.                   begin
  1641.                     parse := null;
  1642.                     exit(parse)
  1643.                   end (* if *)
  1644.                 else if (status <> unrec) and (status <>  ambiguous) then
  1645.                     case verb of
  1646.                       consym: state := fin;
  1647.                       exitsym, quitsym: state := fin;
  1648.                       helpsym: state := get_help_parm;
  1649.                       recsym: state := fin;
  1650.                       sendsym: state := getfilename;
  1651.                       setsym: state := get_set_parm;
  1652.                       showsym: state := get_show_parm;
  1653.                     end (* case *)
  1654.               end; (* case start *)
  1655.           fin:
  1656.               begin
  1657.                 expected := [];
  1658.                 status := getsym(verb);
  1659.                 if status = ateol then
  1660.                   begin
  1661.                     parse := null;
  1662.                     exit(parse)
  1663.                   end (* if status *)
  1664.                 else
  1665.                     status := unconfirmed
  1666.               end; (* case fin *)
  1667.           getfilename:
  1668.             begin
  1669.               expected := [];
  1670.               if getfn(line,filename) then
  1671.                 begin
  1672.                   status := null;
  1673.                   state := fin
  1674.                 end (* if *)
  1675.               else
  1676.                   status := fnexpected
  1677.             end; (* case get file name *)
  1678.           get_set_parm:
  1679.               begin
  1680.                 expected := [paritysym, localsym, ibmsym, escsym,
  1681.                              debugsym, filewarnsym];
  1682.                 status := getsym(noun);
  1683.                 if status = ateol then
  1684.                     status := parm_expected
  1685.                 else if (status <> unrec) and (status <>  ambiguous) then
  1686.                     case noun of
  1687.                       paritysym: state := get_parity;
  1688.                       localsym: state := get_on_off;
  1689.                       ibmsym: state := get_on_off;
  1690.                       escsym: state := getchar;
  1691.                       debugsym: state := getonoff;
  1692.                       filewarnsym: state := getonoff;
  1693.                     end (* case *)
  1694.             end; (* case get_set_parm *)
  1695.           get_parity:
  1696.               begin
  1697.                 expected := [marksym, spacesym, nonesym, evensym, oddsym];
  1698.                 status := getsym(adj);
  1699.                 if status = ateol then
  1700.                     status := parm_expected
  1701.                 else if (status <> unrec) and (status <> ambiguous) then
  1702.                     state := fin
  1703.               end; (* case get_parity  *)
  1704.           get_on_off:
  1705.               begin
  1706.                 expected := [onsym, offsym];
  1707.                 status := getsym(adj);
  1708.                 if status = ateol then
  1709.                     status := parm_expected
  1710.                 else if (status <> unrec) and (status <> ambiguous) then
  1711.                     state := fin
  1712.               end; (* get_on_off *)
  1713.           get_char:
  1714.               if getch(newescchar) then
  1715.                  state := fin
  1716.               else
  1717.                  status := ch_expected;
  1718.           get_show_parm:
  1719.               begin
  1720.                 expected := [allsym, paritysym, localsym, ibmsym, escsym,
  1721.                              debugsym, filewarnsym];
  1722.                 status := getsym(noun);
  1723.                 if status = ateol then
  1724.                     status := parm_expected
  1725.                 else if (status <> unrec) and (status <>  ambiguous) then
  1726.                     state := fin
  1727.               end; (* case get_show_parm *)
  1728.           get_help_show:
  1729.               begin
  1730.                 expected := [paritysym, localsym, ibmsym, escsym,
  1731.                            debugsym, filewarnsym];
  1732.                 status := getsym(adj);
  1733.                 if (status = at_eol) then
  1734.                   begin
  1735.                     status := null;
  1736.                     state := fin
  1737.                   end
  1738.                 else if (status <> unrec) and (status <>  ambiguous) then
  1739.                     state := fin
  1740.               end; (* case get_help_show *)
  1741.           get_help_parm:
  1742.               begin
  1743.                 expected := [consym, exitsym, helpsym, quitsym, recsym,
  1744.                              sendsym, setsym, showsym];
  1745.                 status := getsym(noun);
  1746.                 if status = ateol then
  1747.                   begin
  1748.                     parse := null;
  1749.                     exit(parse)
  1750.                   end;
  1751.                 if (status <> unrec) and (status <>  ambiguous) then
  1752.                     case noun of
  1753.                       consym: state := fin;
  1754.                       sendsym: state := fin;
  1755.                       recsym: state := fin;
  1756.                       setsym: state := get_help_show;
  1757.                       showsym: state := fin;
  1758.                       helpsym: state := fin;
  1759.                       exitsym, quitsym: state := fin;
  1760.                     end (* case *)
  1761.               end; (* case get_help_show *)
  1762.         end (* case *)
  1763.     until (status <> null);
  1764.     parse := status
  1765.   end; (* parse *)
  1766.  
  1767. procedure initvocab;
  1768.  
  1769. var i: integer;
  1770.  
  1771.   begin
  1772.     vocablist[allsym] := 'ALL';
  1773.     vocablist[consym] := 'CONNECT';
  1774.     vocablist[debugsym] := 'DEBUG';
  1775.     vocablist[escsym] := 'ESCAPE';
  1776.     vocablist[evensym] := 'EVEN';
  1777.     vocablist[exitsym] := 'EXIT';
  1778.     vocablist[filewarnsym] := 'FILE-WARNING';
  1779.     vocablist[helpsym] := 'HELP';
  1780.     vocablist[ibmsym] := 'IBM';
  1781.     vocablist[localsym] := 'LOCAL-ECHO';
  1782.     vocablist[marksym] := 'MARK';
  1783.     vocablist[nonesym] := 'NONE';
  1784.     vocablist[oddsym] := 'ODD';
  1785.     vocablist[offsym] := 'OFF';
  1786.     vocablist[onsym] := 'ON';
  1787.     vocablist[paritysym] := 'PARITY';
  1788.     vocablist[quitsym] := 'QUIT';
  1789.     vocablist[recsym] := 'RECEIVE';
  1790.     vocablist[sendsym] := 'SEND';
  1791.     vocablist[setsym] := 'SET';
  1792.     vocablist[showsym] := 'SHOW';
  1793.     vocablist[spacesym] := 'SPACE';
  1794.   end; (* initvocab *)
  1795.  
  1796.   end. (* end of unit *)
  1797. >>>> RCVHANDLR.TEXT
  1798. ;                        ----------------------------
  1799. ;                        RCVHNDLR TTY Receive Handler
  1800. ;                        ----------------------------
  1801. ;
  1802. ;    Two routines are provided that maintain an interrupt-driven
  1803. ;    TTY-receive queue.  Appropriate PASCAL declarations are:
  1804. ;
  1805. ;        CONST RCVQSIZE = maximum number of queued characters
  1806. ;
  1807. ;        TYPE QUEUE = RECORD    (* These are order-dependent !!! *)
  1808. ;                       QSIZE: INTEGER;
  1809. ;                       INP: INTEGER;
  1810. ;                       OUTP: INTEGER;
  1811. ;                       MAXCHAR: INTEGER;
  1812. ;                       DATA: PACKED ARRAY [0..RCVQSIZE] OF CHAR;
  1813. ;                     END;
  1814. ;        VAR RCVQ: QUEUE;     (* must be declared in outermost block *)
  1815. ;
  1816. ;        PROCEDURE RCVINIT (VAR Q: QUEUE; SIZE:INTEGER); EXTERNAL;
  1817. ;        PROCEDURE RCVFINIT; EXTERNAL;
  1818. ;
  1819. ;        RCVINIT (RCVQ,RCVQSIZE);    (* initialize the queue handler *)
  1820. ;
  1821. ;        WHILE TRUE DO
  1822. ;          WITH RCVQ DO
  1823. ;           IF INP <> OUTP THEN    (* characters available *)
  1824. ;             BEGIN
  1825. ;               CH := DATA[OUTP];
  1826. ;               OUTP := OUTP+1; IF OUTP > QSIZE THEN OUTP := 0;
  1827. ;                ...
  1828. ;             END;
  1829. ;
  1830. ;        RCVFINIT;       (* terminate the queue handler *)
  1831. ;
  1832. ;    The RECORD declaration for the queue must appear exactly as it
  1833. ;    does above except that you can of course use any names you like.
  1834. ;    Do NOT attempt to lump the first four integer variables together
  1835. ;    into a single group of the form list:INTEGER.  In that case,
  1836. ;    the compiler allocates them in reverse order, so that your code
  1837. ;    and the interrupt handler will not agree about which words have
  1838. ;    what meaning.
  1839. ;
  1840. ;    The queue handler runs continuously as an interrupt-driven task
  1841. ;    at high priority.  As characters come in, it advances the queue
  1842. ;    INP pointer and keeps track of the maximum number of characters in
  1843. ;    the queue in the MAXCHAR variable.  Queue overflow is indicated
  1844. ;    by MAXCHAR > QSIZE.  You must terminate by calling RCVFINIT, or
  1845. ;    the TTY receive interrupts will be left enabled and you will end
  1846. ;    up crashing the system by executing garbage code when the next
  1847. ;    character is received.  (RCVFINIT also repairs the interrupt
  1848. ;    vectors for breakpoints and the clock, so failing to call it will
  1849. ;    quite likely crash the system even in the absence of incoming
  1850. ;    TTY characters.)
  1851. ;
  1852. ;    The manipulation of the clock and BPT interrupt vectors is borrowed
  1853. ;    from UCSD's old communications program.  The purpose is to allow
  1854. ;    the clock handler to be interrupted by incoming TTY characters.
  1855. ;
  1856. RDB       .EQU   177522          ; Receive Data Buffer absolute address
  1857. RSR       .EQU   177520          ; Receive Status Register absolute address
  1858. RCVINTV   .EQU      120          ; Receiver Interrupt Vector address
  1859. CLKINTV   .EQU      100          ; Clock interrupt vector address
  1860. BPTINTV   .EQU       14          ; BPT interrupt vector address
  1861. QXCINTV   .EQU      250          ; QX controller interrupt vector
  1862. ;
  1863.           .PROC  RCVINIT,2       ; (VAR Q:QUEUE, SIZE:INTEGER)
  1864. ;
  1865.           .DEF   BPTLOC          ; used to save BPT interrupt handler adrs
  1866.           .DEF   BPTPR           ; used to save BPT handler priority
  1867. Q         .EQU   4               ; stack offset for Q address
  1868. SIZE      .EQU   2               ; stack offset for QSIZE value
  1869. ;
  1870.           MOV    Q(SP),R0        ; obtain the Q record address
  1871.           MOV    R0,RCVQADRS     ; remember Q address
  1872.           MOV    SIZE(SP),(R0)+  ; store size in QSIZE word
  1873.           MOV    #0,(R0)+        ; clear INP, OUTP, and MAXCHAR
  1874.           MOV    #0,(R0)+
  1875.           MOV    #0,(R0)
  1876. ;
  1877.           MOV    @#BPTINTV,BPTLOC    ; save old BPT handler address
  1878.           MOV    @#BPTINTV+2,BPTPR   ; and old BPT handler priority
  1879.           MOV    @#CLKINTV,@#BPTINTV ; make BPT vector point to old clock
  1880.           MOV    #0,@#BPTINTV+2      ; and let it run at low priority
  1881.           MOV    #CLKHNDLR,@#CLKINTV ; and replace clock handler with ours
  1882.           MOV    #0,@#QXCINTV+2      ; make floppy interruptable
  1883. ;
  1884.           MOV    #RCVHNDLR,@#RCVINTV ; store interrupt handler address
  1885.           MOV    #200,@#RCVINTV+2    ; set interrupt priority 4 for TTY input
  1886.           MOV    #100,@#RSR          ; enable interrupts for TTY input
  1887. ;
  1888.           MOV    (SP)+,R0        ; pop return address from stack
  1889.           ADD    #4,SP           ; discard 2 parameters (4 bytes)
  1890.           JMP    @R0             ; and return to PASCAL interpreter
  1891. ;
  1892. RCVQADRS  .WORD   0              ; holds Q address for handler
  1893. BPTLOC    .WORD   0              ; saves old BPT handler location
  1894. BPTPR     .WORD   0              ; saves old BPT handler priority
  1895. ;
  1896. QSIZE     .EQU    0              ; offset from Q
  1897. INP       .EQU    2              ; likewise
  1898. OUTP      .EQU    4
  1899. MAXCHAR   .EQU    6
  1900. DATA      .EQU   10
  1901. ;
  1902. RCVHNDLR: MOV    R0,-(SP)        ; free registers R0, R1, R2 for use
  1903.           MOV    R1,-(SP)
  1904.           MOV    R2,-(SP)
  1905.           MOV    RCVQADRS,R2     ; fetch Q address saved by RCVINIT
  1906.           MOV    INP(R2),R0      ; fetch INP value
  1907.           MOV    R0,R1           ; make a working copy
  1908.           ADD    R2,R1           ; R1 = address (Q) + value (INP)
  1909.           MOVB   @#RDB,DATA(R1)  ; DATA[INP] := input character
  1910.           BICB   #200,DATA(R1)   ; clear bit 8 (parity)
  1911.           BEQ    EXIT            ; ignore nulls (do not bump INP)
  1912.           INC    R0              ; INP := INP+1
  1913.           CMP    QSIZE(R2),R0
  1914.           BPL    NOWRAP          ; if QSIZE >= INP then no wraparound
  1915.           CLR    R0              ; else INP := 0
  1916. NOWRAP    MOV    R0,INP(R2)      ; restore INP
  1917. ;
  1918.           SUB    OUTP(R2),R0
  1919.           BMI    INOUT
  1920.           BEQ    INOUT
  1921.           BR     OUTIN           ; if INP > OUTP, # char = INP - OUTP
  1922. INOUT     ADD    QSIZE(R2),R0    ; otherwise, # char = QSIZE+1 + INP - OUTP
  1923.           ADD    #1,R0
  1924. OUTIN     CMP    MAXCHAR(R2),R0
  1925.           BPL    EXIT            ; if MAXCHAR >= # char, exit
  1926.           MOV    R0,MAXCHAR(R2)  ; otherwise, store new MAXCHAR
  1927. ;
  1928. EXIT      MOV    (SP)+,R2        ; restore registers for caller
  1929.           MOV    (SP)+,R1
  1930.           MOV    (SP)+,R0
  1931.           RTT                    ; return from interrupt
  1932. ;
  1933. CLKHNDLR: COM    CLKFLG          ; do not reexecute BPT if BPT handler
  1934.           BEQ    CLKEXIT         ;   takes so long that clock ticks again
  1935.           BPT                    ; let breakpoint transfer to old clock
  1936. CLKEXIT   COM    CLKFLG          ; reset flag
  1937.           RTI                    ; and exit
  1938. ;
  1939. CLKFLG    .WORD  0               ; flags reentry before BPT exit
  1940. ;
  1941.           .PROC  RCVFINIT
  1942.           .REF   BPTLOC          ; old BPT handler loc, saved by RCVINIT
  1943.           .REF   BPTPR           ; old BPT handler priority, likewise
  1944. ;
  1945.           MOV    #0,@#RSR             ; disable receive interrupt
  1946.           MOV    @#BPTINTV,@#CLKINTV  ; repair clock interrupt vector
  1947.           MOV    @#BPTPR,@#BPTINTV+2  ; reestablish BPT handler priority
  1948.           MOV    @#BPTLOC,@#BPTINTV   ; repair BPT handler address
  1949.           MOV    #340,@#QXCINTV+2     ; repair QX controller vector
  1950.           RTS    PC                   ; and return
  1951. ;
  1952.           .END
  1953.  
  1954. >>>> RECSW.TEXT
  1955.  
  1956. (* RECEIVE SECTION *)
  1957.  
  1958. segment procedure recsw(var rec_ok: boolean);
  1959.  
  1960. function rdata: char;
  1961.  
  1962. (* send file data *)
  1963.  
  1964. var num, len: integer;
  1965.     ch: char;
  1966.  
  1967.   begin
  1968.  
  1969.     repeat
  1970.         if numtry > maxtry then
  1971.           begin
  1972.             state := 'a';
  1973.             exit(rdata)
  1974.           end;
  1975.  
  1976.         num_try := num_try + 1;
  1977.  
  1978.         ch := rpack(len,num,recpkt);   (* receive a packet *)
  1979.  
  1980.         refresh_screen(numtry,n);
  1981.  
  1982.         if (ch = 'D') then             (* got data packet *)
  1983.           begin
  1984.             if (num <> (n mod 64)) then (* wrong packet *)
  1985.               begin
  1986.                 if (oldtry > maxtry) then
  1987.                   begin
  1988.                     rdata := 'a';      (* too many tries, abort *)
  1989.                     exit(rdata)
  1990.                   end; (* if *)
  1991.  
  1992.                 n := n - 1;
  1993.  
  1994.                 if (num = (n mod 64)) then (* previous packet again *)
  1995.                   begin                (* so re-ACK it *)
  1996.                     spack('Y',num,6,packet);
  1997.                     numtry := 0;       (* reset try counter *)
  1998.                                        (* stay in same state *)
  1999.                   end (* if *)
  2000.                 else                   (* wrong number *)
  2001.                     state := 'a'       (* so abort *)
  2002.               end (* if *)
  2003.             else                       (* right packet *)
  2004.               begin
  2005.                 bufemp(recpkt,f,len);  (* write data to file *)
  2006.                 spack('Y',(n mod 64),0,packet); (* ACK packet *)
  2007.                 oldtry := numtry;      (* reset try counters *)
  2008.                 if numtry > 1 then
  2009.                     clearbuf(rq);      (* clear buffer *)
  2010.                 numtry := 0;
  2011.                 n := n + 1             (* bump packet number *)
  2012.                                        (* stay in data send state *)
  2013.               end (* else *)
  2014.           end (* if 'D' *)
  2015.         else if (ch = 'F') then        (* file header *)
  2016.           begin
  2017.             if (oldtry > maxtry) then
  2018.               begin
  2019.                 rdata := 'a';          (* too many tries, abort *)
  2020.                 exit(rdata)
  2021.               end; (* if *)
  2022.  
  2023.             n := n - 1;
  2024.  
  2025.             if (num = (n mod 64)) then (* previous packet again *)
  2026.               begin                    (* so re-ACK it *)
  2027.                 spack('Y',num,0,packet);
  2028.                 clear_buf(rq);         (* and empty out buffer *)
  2029.                 numtry := 0;           (* reset try counter *)
  2030.                 state := state;        (* stay in same state *)
  2031.               end (* if *)
  2032.             else
  2033.                 state := 'a'           (* not previous packet, abort *)
  2034.           end (* if 'F' *)
  2035.         else if (ch = 'Z') then        (* end of file *)
  2036.           begin
  2037.             if (num <> (n mod 64)) then(* wrong packet, abort *)
  2038.               begin
  2039.                 rdata := 'a';
  2040.                 exit(rdata)
  2041.               end; (* if *)
  2042.             spack('Y',n mod 64,0,packet); (* ok, ACK it *)
  2043.             close(f,lock);             (* close up the file *)
  2044.             n :=  n + 1;               (* bump packet counter *)
  2045.             state := 'f';              (* go to complete state *)
  2046.           end (* else if 'Z' *)
  2047.         else if (ch = 'E') then        (* error packet *)
  2048.           begin
  2049.             error(recpkt,len);         (* display error *)
  2050.             state := 'a'               (* and abort *)
  2051.           end (* if 'E' *)
  2052.         else if (ch <> chr(0)) then    (* some other packet type, *)
  2053.             state := 'a'               (* abort *)
  2054.     until (state <> 'd');
  2055.     rdata := state
  2056.   end; (* rdata *)
  2057.  
  2058. function rfile: char;
  2059.  
  2060. (* receive file header *)
  2061.  
  2062. var num, len: integer;
  2063.     ch: char;
  2064.     oldfn: string;
  2065.     i: integer;
  2066.  
  2067. procedure makename(recpkt: packettype; var fn: string; l: integer);
  2068.  
  2069. function exist(fn: string): boolean;
  2070.  
  2071. (* returns true if file named fn exists *)
  2072.  
  2073. var f: file;
  2074.  
  2075.   begin
  2076.     (*$I-*) (* turn off i/o checking *)
  2077.     reset(f,fn);
  2078.     exist := (ioresult = 0)
  2079.     (*$I+*)
  2080.   end; (* exist *)
  2081.  
  2082. procedure checkname(var fn: string);
  2083.  
  2084. (* if file fn exists, makes a new name which doesn't *)
  2085. (* does this by changing letters in file name until it *)
  2086. (* finds some combination which doesn't exitst *)
  2087.  
  2088. var ch: char;
  2089.     i: integer;
  2090.  
  2091.   begin
  2092.     i := 1;
  2093.     while (i <= length(fn)) and exist(fn) do
  2094.       begin
  2095.         ch := 'A';
  2096.         while (ch in ['A'..'Z']) and exist(fn) do
  2097.           begin
  2098.             fn[i] := ch;
  2099.             ch := succ(ch);
  2100.           end; (* while *)
  2101.         i := i + 1
  2102.       end; (* while *)
  2103.     end; (* checkname *)
  2104.  
  2105.   begin (* makename *)
  2106.     fn := copy('               ',1,15);    (* stretch length *)
  2107.     moveleft(recpkt[0],fn[1],l);           (* get filename from packet *)
  2108.     oldfn := copy(fn, 1,l);                (* save fn sent to show user *)
  2109.     fn := copy(fn,1,min(15,l));            (* set length of filename *)
  2110.                                            (* and make sure <= 15 *)
  2111.     uppercase(fn);
  2112.     if pos('.TEXT',fn) <> length(fn)-4 then
  2113.       begin
  2114.         if length(fn) > 10 then
  2115.             fn := copy(fn,1,10);           (* can only be 15 long in all *)
  2116.         fn := concat(fn,'.TEXT');          (* and we'll add .TEXT *)
  2117.       end; (* if *)
  2118.     if fwarn then                          (* if file warning is on *)
  2119.         checkname(fn);                       (* must check that name unique *)
  2120.   end; (* makename *)
  2121.  
  2122.   begin (* rfile *)
  2123.     if debug then
  2124.         debugwrite('rfile');
  2125.  
  2126.     if (numtry > maxtry) then         (* if too many tries, give up *)
  2127.       begin
  2128.         rfile := 'a';
  2129.         exit(rfile)
  2130.       end;
  2131.     numtry := numtry + 1;
  2132.  
  2133.     ch := rpack(len,num,recpkt);      (* receive a packet *)
  2134.  
  2135.     refresh_screen(numtry,n);
  2136.  
  2137.     if ch = 'S' then                  (* send init, maybe our ACK lost *)
  2138.       begin
  2139.         if (oldtry > maxtry) then     (* too many tries, abort *)
  2140.           begin
  2141.             rfile := 'a';
  2142.             exit(rfile)
  2143.           end; (* if *)
  2144.  
  2145.         n := n - 1;
  2146.  
  2147.         if num = (n mod 64) then      (* previous packet mod 64? *)
  2148.           begin                       (* yes, ACK it again *)
  2149.             spar(packet);             (* with our send init params *)
  2150.             spack('Y',num,6,packet);
  2151.             numtry := 0;              (* reset try counter *)
  2152.             rfile := state;           (* stay in same state *)
  2153.           end (* if *)
  2154.         else                          (* not previous packet, abort *)
  2155.           state := 'a'
  2156.       end (* if 'S' *)
  2157.     else if (ch = 'Z') then           (* end of file *)
  2158.       begin
  2159.         if (oldtry > maxtry) then     (* too many tries, abort *)
  2160.           begin
  2161.             rfile := 'a';
  2162.             exit(rfile)
  2163.           end; (* if *)
  2164.  
  2165.         n := n - 1;
  2166.  
  2167.         if num = (n mod 64) then       (* previous packet mod 64? *)
  2168.           begin                       (* yes, ACK it again *)
  2169.             spack('Y',num,0,packet);
  2170.             numtry := 0;
  2171.             rfile := state            (* stay in same state *)
  2172.           end (* if *)
  2173.         else
  2174.             rfile := 'a'              (* no, abort *)
  2175.       end (* else if *)
  2176.     else if (ch = 'F') then           (* file header *)
  2177.       begin                           (* which is what we really want *)
  2178.         if (num <> (n mod 64)) then   (* if wrong packet, abort *)
  2179.           begin
  2180.             rfile := 'a';
  2181.             exit(rfile)
  2182.           end;
  2183.  
  2184.         makename(recpkt,filename,len); (* get filename, make unique if filew *)
  2185.         gotoxy(filepos,fileline);
  2186.         write(oldfn,' ==> ',filename);
  2187.  
  2188.         if not getfil(filename) then  (* try to open new file *)
  2189.           begin
  2190.             ioerror(ioresult);        (* if unsuccessful, tell them *)
  2191.             rfile := 'a';             (* and abort *)
  2192.             exit(rfile)
  2193.           end; (* if *)
  2194.  
  2195.         spack('Y',n mod 64,0,packet); (* ACK file header *)
  2196.         oldtry := numtry;             (* reset try counters *)
  2197.         numtry := 0;
  2198.         n := n + 1;                   (* bump packet number *)
  2199.         rfile := 'd';                 (* switch to data state *)
  2200.       end (* else if *)
  2201.     else if ch = 'B' then             (* break transmission *)
  2202.       begin
  2203.         if (num <> (n mod 64)) then            (* wrong packet, abort *)
  2204.           begin
  2205.             rfile := 'a';
  2206.             exit(rfile)
  2207.           end;
  2208.         spack('Y',n mod 64,0,packet); (* say ok *)
  2209.         rfile := 'c'                  (* go to complete state *)
  2210.       end (* else if *)
  2211.     else if (ch = 'E') then
  2212.       begin
  2213.         error(recpkt,len);
  2214.         rfile := 'a'
  2215.       end
  2216.     else if (ch = chr(0)) then        (* returned false *)
  2217.         rfile := state                (* so stay in same state *)
  2218.     else                              (* some weird state, so abort *)
  2219.         rfile := 'a'
  2220.   end; (* rfile *)
  2221.  
  2222. function rinit: char;
  2223.  
  2224. (* receive initialization *)
  2225.  
  2226. var num, len: integer;  (* packet number and length *)
  2227.     ch: char;
  2228.  
  2229.   begin
  2230.     if debug then
  2231.         debugwrite('rinit');
  2232.  
  2233.     numtry := numtry + 1;
  2234.  
  2235.     ch := rpack(len,num,recpkt); (* receive a packet *)
  2236.     refresh_screen(num_try,n);
  2237.  
  2238.     if (ch = 'S') then           (* send init packet *)
  2239.       begin
  2240.         rpar(recpkt);            (* get other side's init data *)
  2241.         spar(packet);            (* fill packet with my init data *)
  2242.         ctl_set := [chr(1)..chr(31),chr(del),quote];
  2243.         spack('Y',n mod 64,6,packet); (* ACK with my params *)
  2244.         oldtry := numtry;        (* save old try count *)
  2245.         numtry := 0;             (* start a new counter *)
  2246.         n := n + 1;              (* bump packet number *)
  2247.         rinit := 'f';            (* enter file send state *)
  2248.       end (* if 'S' *)
  2249.     else if (ch = 'E') then
  2250.       begin
  2251.         rinit := 'a';
  2252.         error(recpkt,len)
  2253.       end (* if 'E' *)
  2254.     else if (ch = chr(0)) then
  2255.         rinit := 'r'             (* stay in same state *)
  2256.     else
  2257.         rinit := 'a'             (* abort *)
  2258.   end; (* rinit *)
  2259.  
  2260. (* state table switcher for receiving packets *)
  2261.  
  2262.   begin (* recswok *)
  2263.     writescreen('Receiving');
  2264.     state := 'r';            (* initial state is send *)
  2265.     n := 0;                  (* set packet # *)
  2266.     numtry := 0;             (* no tries yet *)
  2267.  
  2268.     while true do
  2269.         if state in ['d', 'f', 'r', 'c', 'a'] then
  2270.           case state of
  2271.               'd': state := rdata;
  2272.               'f': state := rfile;
  2273.               'r': state := rinit;
  2274.               'c': begin
  2275.                      rec_ok := true;
  2276.                      exit(recsw)
  2277.                    end; (* case c *)
  2278.               'a': begin
  2279.                      rec_ok := false;
  2280.                      exit(recsw)
  2281.                    end (* case a *)
  2282.             end (* case *)
  2283.         else (* state not in legal states *)
  2284.           begin
  2285.             rec_ok := false;
  2286.             exit(recsw)
  2287.           end (* else *)
  2288.   end; (* recsw *)
  2289.  
  2290. >>>> SENDB.TEXT
  2291. ;                    ------------------------------
  2292. ;                    .  SENDS TTY Output Routine  .
  2293. ;                    ------------------------------
  2294. ;
  2295. ;     SENDBRK is a routine to send a continuous break to an IBM mainframe.
  2296. ;     The appropriate PASCAL declaration is:
  2297. ;
  2298. ;         PROCEDURE SENDBRK; EXTERNAL;  (*to send a break*)
  2299. ;
  2300. ;
  2301. XDB       .EQU   177526        ; absolute address, transmit data buffer
  2302. XSR       .EQU   177524        ; absolute address, transmit status register
  2303. ;
  2304. ;
  2305.           .PROC  SENDBRK
  2306. ;
  2307. SNDB1:    BIT    #200,@#XSR    ; wait for previous char to complete
  2308.           BEQ    SNDB1
  2309. ;
  2310.           MOV    #1,@#XSR      ; transmit continuous break
  2311.           MOV    #310,R1       ; wait 200 (=310 octal) milliseconds
  2312. SNDB2:    MOV    #124,R0
  2313. SNDB3:    SUB    #1,R0
  2314.           BNE    SNDB3
  2315.           SUB    #1,R1
  2316.           BNE    SNDB2
  2317.           MOV    #0,@#XSR      ; clear continuous break
  2318. ;
  2319.           RTS    PC            ; and return
  2320. ;
  2321.           .END
  2322.  
  2323. >>>> SENDSW.TEXT
  2324.  
  2325. (* Send Section *)
  2326.  
  2327. segment procedure sendsw(var send_ok: boolean);
  2328.  
  2329. var io_status: integer;
  2330.  
  2331. procedure openfile;
  2332.  
  2333. (* resets file & gets past first 2 blocks *)
  2334.  
  2335.   begin
  2336.     (*$I-*) (* turn off compiler i/o checking temporarily *)
  2337.     reset(oldf,filename);
  2338.     (*$I+*) (* turn compiler i/o checking back on *)
  2339.     io_status := io_result;
  2340.     if (iostatus = 0) then
  2341.       if (pos('.TEXT',filename) = length(filename) - 4) then
  2342.                                           (* is a text file, so *)
  2343.           i := blockread(oldf,filebuf,2); (* skip past 2 block header *)
  2344.   end; (* openfile *)
  2345.  
  2346. function sinit: char;
  2347.  
  2348. (* send init packet & receive other side's *)
  2349.  
  2350. var num, len, i: integer;  (* packet number and length *)
  2351.     ch: char;
  2352.  
  2353.   begin
  2354.     if debug then
  2355.         debugwrite('sinit');
  2356.  
  2357.     if numtry > maxtry then
  2358.       begin
  2359.         sinit := 'a';
  2360.         exit(sinit)
  2361.       end;
  2362.  
  2363.     num_try := num_try + 1;
  2364.     spar(packet);
  2365.  
  2366.     clear_buf(rq);
  2367.  
  2368.     refresh_screen(numtry,n);
  2369.  
  2370.     spack('S',n mod 64,6,packet);
  2371.  
  2372.     ch := rpack(len,num,recpkt);
  2373.  
  2374.     if (ch = 'N') then
  2375.       begin
  2376.         sinit := 's';
  2377.         exit(sinit)
  2378.       end (* if 'N' *)
  2379.     else if (ch = 'Y') then
  2380.       begin
  2381.         if ((n mod 64) <> num) then       (* not the right ack *)
  2382.           begin
  2383.             sinit := state;
  2384.             exit(sinit)
  2385.           end;
  2386.         rpar(recpkt);
  2387.         if (eol = chr(0)) then   (* if they didn't spec eol *)
  2388.             eol := chr(my_eol);    (* use mine *)
  2389.         if (quote = chr(0)) then (* if they didn't spec quote *)
  2390.             quote := my_quote;     (* use mine *)
  2391.         ctl_set := [chr(1)..chr(31),chr(del),quote];
  2392.         numtry := 0;
  2393.         n := n + 1;              (* increase packet number *)
  2394.         sinit := 'f';
  2395.         exit(sinit)
  2396.       end (* else if 'Y' *)
  2397.     else if (ch = 'E') then
  2398.       begin
  2399.         error(recpkt,len);
  2400.         sinit := 'a'
  2401.       end (* if 'E' *)
  2402.     else if (ch = chr(0)) then
  2403.         sinit := state
  2404.     else if (ch <> 'N') then
  2405.         sinit := 'a'
  2406.   end; (* sinit *)
  2407.  
  2408. function sdata: char;
  2409.  
  2410. (* send file data *)
  2411.  
  2412. var num, len: integer;
  2413.     ch: char;
  2414.     packarray: array[false..true] of packettype;
  2415.     sizearray: array[false..true] of integer;
  2416.     current: boolean;
  2417.     b: boolean;
  2418.  
  2419. function other(b: boolean): boolean;
  2420.  
  2421. (* complements a boolean which is used as array index *)
  2422.  
  2423.   begin
  2424.     if b then
  2425.         other := false
  2426.     else
  2427.         other := true
  2428.   end; (* other *)
  2429.  
  2430.   begin
  2431.     current := true;
  2432.     packarray[current] := packet;
  2433.     sizearray[current] := size;
  2434.     while (state = 'd') do
  2435.       begin
  2436.         if (numtry > maxtry) then             (* if too many tries, give up *)
  2437.             state := 'a';
  2438.  
  2439.         b := other(current);
  2440.         numtry := numtry + 1;
  2441.  
  2442.                                           (* send a data packet *)
  2443.         spack('D',n mod 64,sizearray[current],packarray[current]);
  2444.  
  2445.         refresh_screen(numtry,n);
  2446.                                           (* set up next packet *)
  2447.         sizearray[b] := bufill(packarray[b]);
  2448.  
  2449.         ch := rpack(len,num,recpkt);      (* receive a packet *)
  2450.         if ch = 'N' then                  (* NAK, so just stay in this state *)
  2451.             if ((n+1) mod 64 <> num) then (* unless NAK for next, which *)
  2452.                 sdata := state
  2453.             else                          (* is just like ACK for this packet *)
  2454.               begin
  2455.                 if num > 0 then
  2456.                     num := (num - 1)      (* in which case, decrement num *)
  2457.                 else
  2458.                     num := 63;
  2459.                 ch := 'Y';                (* and indicate an ACK *)
  2460.               end; (* else *)
  2461.  
  2462.         if (ch = 'Y') then
  2463.            begin
  2464.              if ((n mod 64) <> num) then (* if wrong ACK *)
  2465.                begin
  2466.                  sdata := state;         (* stay in same state *)
  2467.                  exit(sdata);            (* get out of here *)
  2468.                end; (* if *)
  2469.              if numtry > 1 then
  2470.                  clear_buf(rq);          (* if anything in buffer, flush it *)
  2471.              numtry := 0;
  2472.              n := n + 1;
  2473.              current := b;
  2474.              if sizearray[current] = ateof then
  2475.                  state := 'z'            (* set state to eof *)
  2476.              else
  2477.                  state := 'd'            (* else stay in data state *)
  2478.            end (* if *)
  2479.           else if (ch = 'E') then
  2480.             begin
  2481.               error(recpkt,len);
  2482.               state := 'a'
  2483.             end (* if 'E' *)
  2484.           else if (ch = chr(0)) then      (* receive failure, so stay in d *)
  2485.             begin
  2486.             end
  2487.           else if (ch <> 'N') then
  2488.             state := 'a'                  (* on any other goto abort state *)
  2489.       end; (* while *)
  2490.     size := sizearray[current];
  2491.     packet := packarray[current];
  2492.     sdata := state
  2493.   end; (* sdata *)
  2494.  
  2495. function sfile: char;
  2496.  
  2497. (* send file header *)
  2498.  
  2499. var num, len, i: integer;
  2500.     ch: char;
  2501.     fn: packettype;
  2502.     oldfn: string;
  2503.  
  2504. procedure legalize(var fn: string);
  2505.  
  2506. (* make sure file name will be legal to other computer *)
  2507.  
  2508. var count, i, j, l: integer;
  2509.  
  2510. procedure uppercase(var s: string);
  2511.  
  2512. var i: integer;
  2513.  
  2514.   begin
  2515.     for i := 1 to length(s) do
  2516.         if s[i] in ['a'..'z'] then
  2517.             s[i] := chr(ord('A') + ord(s[i]) - ord('a'))
  2518.   end; (* uppercase *)
  2519.  
  2520.   begin
  2521.     count := 0;
  2522.     l := length(fn);
  2523.     for i := 1 to l do                                  (* count '.'s in fn *)
  2524.         if fn[i] = '.' then
  2525.             count := count + 1;
  2526.     for i := 1 to count-1 do                            (* remove all but 1 *)
  2527.       begin
  2528.         j := 1;
  2529.         while (j < l) and (fn[j] <> '.') do
  2530.             j := j + 1;
  2531.             delete(fn,j,1);l := l - 1
  2532.       end; (* for i *)
  2533.     l := length(fn);
  2534.     i := pos(':',fn);
  2535.     if (i <> 0) then
  2536.       begin
  2537.         fn := copy(fn,i,l-i);
  2538.         l := length(fn)
  2539.       end;
  2540.     i := 1;
  2541.     while (i <= length(fn)) do
  2542.         if not(fn[i] in ['a'..'z','A'..'Z','.','0'..'9']) then
  2543.             delete(fn,i,1)
  2544.         else
  2545.             i := i + 1;
  2546.     uppercase(fn)
  2547.   end; (* legalize *)
  2548.  
  2549.   begin
  2550.     if debug then
  2551.         debugwrite('sfile');
  2552.  
  2553.     if (numtry > maxtry) then          (* if too many tries, give up *)
  2554.       begin
  2555.         sfile := 'a';
  2556.         exit(sfile)
  2557.       end;
  2558.     numtry := numtry + 1;
  2559.  
  2560.     oldfn := filename;
  2561.     legalize(filename);                (* make filename acceptable to remote *)
  2562.     len := length(filename);
  2563.  
  2564.     moveleft(filename[1],fn[0],len);   (* move filename into a packettype *)
  2565.  
  2566.     gotoxy(filepos,fileline);
  2567.     write(oldfn,' ==> ',filename);
  2568.  
  2569.     refresh_screen(numtry,n);
  2570.  
  2571.     spack('F',n mod 64,len,fn);               (* send file header packet *)
  2572.  
  2573.     size := bufill(packet);            (* get first data from file *)
  2574.                                        (* while waiting for response *)
  2575.  
  2576.     ch := rpack(len,num,recpkt);
  2577.     if ch = 'N' then                   (* NAK, so just stay in this state *)
  2578.         if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
  2579.             exit(sfile)                (* is just like ACK for this packet *)
  2580.         else
  2581.           begin
  2582.             if (num > 0) then
  2583.                 num := (num - 1)       (* in which case, decrement num *)
  2584.             else
  2585.                 num := 63;
  2586.             ch := 'Y';                 (* and indicate an ACK *)
  2587.           end; (* else *)
  2588.  
  2589.     if (ch = 'Y') then
  2590.       begin
  2591.         if ((n mod 64) <> num) then  (* if wrong ACK, stay in F state *)
  2592.             exit(sfile);
  2593.         numtry := 0;
  2594.         n := n + 1;
  2595.         sfile := 'd';
  2596.       end (* if *)
  2597.     else if (ch = 'E') then
  2598.       begin
  2599.         error(recpkt,len);
  2600.         sfile := 'a'
  2601.       end (* if 'E' *)
  2602.     else if (ch <> chr(0)) and (ch <> 'N') then (* don't recognize it *)
  2603.         sfile := 'a'
  2604.   end; (* sfile *)
  2605.  
  2606. function seof: char;
  2607.  
  2608. (* send end of file *)
  2609.  
  2610. var num, len: integer;
  2611.     ch: char;
  2612.  
  2613.   begin
  2614.     if debug then
  2615.         debugwrite('seof');
  2616.  
  2617.     if (numtry > maxtry) then          (* if too many tries, give up *)
  2618.       begin
  2619.         seof := 'a';
  2620.         exit(seof)
  2621.       end;
  2622.     numtry := numtry + 1;
  2623.  
  2624.     refresh_screen(numtry,n);
  2625.  
  2626.     spack('Z',(n mod 64),0,packet);    (* send end of file packet *)
  2627.  
  2628.     if debug then
  2629.         debugwrite('seof1');
  2630.  
  2631.     ch := rpack(len,num,recpkt);
  2632.     if ch = 'N' then                   (* NAK, so just stay in this state *)
  2633.         if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
  2634.             exit(seof)                 (* is just like ACK for this packet *)
  2635.         else
  2636.           begin
  2637.             if num > 0 then
  2638.                 num := (num - 1)       (* in which case, decrement num *)
  2639.             else
  2640.                 num := 63;
  2641.             ch := 'Y';                 (* and indicate an ACK *)
  2642.           end; (* else *)
  2643.  
  2644.     if (ch = 'Y') then
  2645.       begin
  2646.         if debug then
  2647.             debugwrite('seof2');
  2648.         if ((n mod 64) <> num) then     (* if wrong ACK, stay in F state *)
  2649.             exit(seof);
  2650.         numtry := 0;
  2651.         n := n + 1;
  2652.         if debug then
  2653.             debugwrite(concat('closing ',s));
  2654.         close(oldf);
  2655.         seof := 'b'
  2656.       end (* if *)
  2657.     else if (ch = 'E') then
  2658.       begin
  2659.         error(recpkt,len);
  2660.         seof := 'a'
  2661.       end (* if 'E' *)
  2662.     else if (ch = chr(0)) then         (* receive failed, so stay in z state *)
  2663.       begin
  2664.       end
  2665.     else if (ch <> 'N') then           (* other error, just abort *)
  2666.         seof := 'a'
  2667.   end; (* seof *)
  2668.  
  2669. function sbreak: char;
  2670.  
  2671. var num, len: integer;
  2672.     ch: char;
  2673.  
  2674. (* send break (end of transmission) *)
  2675.  
  2676.   begin
  2677.     if debug then
  2678.         debugwrite('sbreak');
  2679.  
  2680.     if (numtry > maxtry) then          (* if too many tries, give up *)
  2681.       begin
  2682.         sbreak := 'a';
  2683.         exit(sbreak)
  2684.       end;
  2685.     numtry := numtry + 1;
  2686.  
  2687.     refresh_screen(numtry,n);
  2688.  
  2689.     spack('B',(n mod 64),0,packet);    (* send end of file packet *)
  2690.  
  2691.     ch := rpack(len,num,recpkt);
  2692.     if ch = 'N' then                   (* NAK, so just stay in this state *)
  2693.         if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
  2694.             exit(sbreak)               (* is just like ACK for this packet *)
  2695.         else
  2696.           begin
  2697.             if num > 0 then
  2698.                 num := (num - 1)       (* in which case, decrement num *)
  2699.             else
  2700.                 num := 63;
  2701.             ch := 'Y';                 (* and indicate an ACK *)
  2702.           end; (* else *)
  2703.  
  2704.     if (ch = 'Y') then
  2705.       begin
  2706.         if ((n mod 64) <> num) then    (* if wrong ACK, stay in B state *)
  2707.             exit(sbreak);
  2708.         numtry := 0;
  2709.         n := n + 1;
  2710.         sbreak := 'c'                  (* else, switch state to complete *)
  2711.       end (* if *)
  2712.     else if (ch = 'E') then
  2713.       begin
  2714.         error(recpkt,len);
  2715.         sbreak := 'a'
  2716.       end (* if 'E' *)
  2717.     else if (ch = chr(0)) then         (* receive failed, so stay in z state *)
  2718.       begin
  2719.       end
  2720.     else if (ch <> 'N') then           (* other error, just abort *)
  2721.         sbreak := 'a'
  2722.   end; (* sbreak *)
  2723.  
  2724. (* state table switcher for sending *)
  2725.  
  2726.   begin (* sendsw *)
  2727.  
  2728.     if debug then
  2729.         debugwrite(concat('Opening ',filename));
  2730.  
  2731.     openfile;
  2732.     if io_status <> 0 then
  2733.       begin
  2734.         writeln(chr(clear_screen));
  2735.         io_error(io_status);
  2736.         send_ok := false;
  2737.         exit(sendsw)
  2738.       end;
  2739.  
  2740.     write_screen('Sending');
  2741.     state := 's';
  2742.     n := 0;       (* set packet # *)
  2743.     numtry := 0;
  2744.     while true do
  2745.         if state in ['d', 'f', 'z', 's', 'b', 'c', 'a'] then
  2746.           case state of
  2747.               'd': state := sdata;
  2748.               'f': state := sfile;
  2749.               'z': state := seof;
  2750.               's': state := sinit;
  2751.               'b': state := sbreak;
  2752.               'c': begin
  2753.                      send_ok := true;
  2754.                      exit(sendsw)
  2755.                    end; (* case c *)
  2756.               'a': begin
  2757.                      send_ok := false;
  2758.                      exit(sendsw)
  2759.                    end (* case a *)
  2760.             end (* case *)
  2761.         else (* state not in legal states *)
  2762.           begin
  2763.             send_ok := false;
  2764.             exit(sendsw)
  2765.           end (* else *)
  2766.   end; (* sendsw *)
  2767.