unit kermutil;

{ Change log:

        13 May 84: Use KERNEL's syscom record for screen control -sp-
}

interface

   uses {$U kermglob.code} kermglob;


   function read_ch(unitno: integer; var ch: char): boolean;

   procedure read_str(unitno:integer; var s: string255);

   procedure echo(ch: char);

   procedure clear_buf(unitno:integer);

   function aand(x,y: integer): integer;

   function aor(x,y: integer): integer;

   function xor(x,y: integer): integer;

   procedure uppercase(var s: string255);

   procedure error(p: packettype; len: integer);

   procedure io_error(i: integer);

   procedure debugwrite(s: string255);

   procedure debugint(s: string255; i: integer);

   function min(x,y: integer): integer;

   function tochar(ch: char): char;

   function unchar(ch: char): char;

   function ctl(ch: char): char;

   function getch(var r: char_int_rec): boolean;

   function getsoh: boolean;

   function getfil(filename: string255): boolean;

   procedure send_brk;

   procedure setup_comm;

   procedure write_ch(unitno: integer; ch: char );

   procedure screen( scrcmd: scrcommands );

   procedure writescreen(s: string255);

   procedure refresh_screen(numtry, num: integer);


implementation

uses {$U remunit.code} remunit,  {SP, 1/14/84}
     {$U kernel.code} kernel;


procedure uppercase(*var s: string255*);

var i: integer;

  begin
    for i := 1 to length(s) do
        if s[i] in ['a'..'z'] then
            s[i] := chr(ord(s[i]) - ord('a') + ord('A'))
  end; (* uppercase *)


 screen -- perform screen operations 
procedure screen{( scrcmd: scrcommands )};
begin
   { for portability, peek in at syscom vector to get control chars }
   with syscom^ do begin
      if crtctrl.prefixed[ord(scrcmd)] then
         write( crtinfo.prefix );

      with crtctrl do
         case scrcmd of
            sc_up:       write( rlf );
            sc_right:    write( ndfs );
            sc_clreol:   write( eraseeol );
            sc_clreos:   write( eraseeos );
            sc_home:     write( home );
            sc_escape:   write( escape );
            sc_left:     write( backspace );
            sc_clrall:   write( clearscreen );
            scr_clrline: write( clearline )
         end
   end
end; { screen }


function read_ch(*unitno:integer; var ch: char): boolean*);

(* read a character from an input queue *)
var
   ready: boolean;

  begin
    if unitno=keyport then
       ready := cr_kbstat
    else if unitno=inport then
       ready := cr_remstat
    else
       ready := false;
    if ready then            (* if a char there *)
       if unitno=keyport then begin
          ch := ' ';
          unitread( keyport, ch, 1,, 12 )
       end
       else
          ch := cr_getrem;
    read_ch := ready
  end; (* read_ch *)

procedure write_ch(*unitno: integer; ch: char*);
begin
   if unitno=oport then
      cr_putrem( ch )
end;


procedure read_str(*unitno:integer; var s: string255*);

(* acts like readln(s) but takes input from input queue *)

var i: integer;

  begin
    i := 0;
    s := copy('',0,0);
    repeat
      repeat                              (* get a character *)
      until read_ch(unitno,ch);
      if (ord(ch) = backspace) then       (* if it's a backspace then *)
        begin
          if (i > 0) then                   (* if not at beginning of line *)
            begin
              write(ch);                      (* go back a space on screen *)
              write(' ');                     (* erase char on screen *)
              write(ch);                      (* go back a space again *)
              i := i - 1;                     (* adjust string counter *)
              s := copy(s,1,i)                (* adjust string *)
            end (* if *)
        end (* if *)
      else if (ord(ch) <> eoln_sym) then  (* otherwise if not at eoln  then *)
        begin
          write(ch);                        (* echo char on screen *)
          i := i + 1;                       (* inc string counter *)
          s := concat(s,' ');
          s[i] := ch;                       (* put char in string *)
        end; (* if *)
    until (ord(ch) = eoln_sym);           (* if not eoln, get another char *)
    s := copy(s,1,i);                     (* correct string length *)
    writeln                               (* write a line on the screen *)
  end; (* read_str *)


procedure clear_buf(*unitno:integer*);
 modified by SP 
begin
   if unitno=keyport then
      unitclear( unitno )
end;


procedure send_brk;
begin
   cr_break
end;


procedure setup_comm;
 SP, 14 Jan 84 
var
   result: cr_baud_result;
begin
   cr_setcommunications(false,
                        false,
                        baud,
                        8,
                        1,
                        cr_orig,
                        'IBM PC',
                        result );
end;


function aand(*x,y: integer): integer*);

(* arithmetic and--takes 2 integers and ands them, yeilding an integer *)

var xrec, yrec, temp: int_bool_rec;

  begin
    xrec.i := x;                  (* put the two numbers in variant record *)
    yrec.i := y;
    temp.b := xrec.b and yrec.b;  (* use as booleans to 'and' them *)
    aand := temp.i                (* return integer result *)
  end; (* aand *)


function aor(*x,y: integer): integer*);

(* arithmetic or *)

var xrec, yrec, temp: int_bool_rec;

  begin
    xrec.i := x;                  (* put two numbers in variant record *)
    yrec.i := y;
    temp.b := xrec.b or yrec.b;   (* use as booleans to 'or' them *)
    aor := temp.i                 (* return integer result *)
  end; (* aor *)

function xor(*x,y: integer): integer*);

(* exclusive or *)

var xrec, yrec, temp: int_bool_rec;

  begin
    xrec.i := x;                  (* put two numbers in variant record *)
    yrec.i := y;
                                  (* use as booleans to 'xor' them *)
    temp.b := (xrec.b or yrec.b) and (not(xrec.b and yrec.b));
    xor := temp.i                 (* return integer result *)
  end; (* xor *)

procedure error(*p: packettype; len: integer*);

(* writes error message sent by remote host *)

var i: integer;

  begin
    gotoxy(0,errorline);
    for i := 0 to len-1 do
        write(p[i]);
    gotoxy(0,promptline);
  end; (* error *)

procedure io_error(*i: integer*);

  begin
    gotoxy( 0, errorline );
    screen( sc_clreol );
    case i of
        0: writeln('No error');
        1: writeln('Bad Block, Parity error (CRC)');
        2: writeln('Bad Unit Number');
        3: writeln('Bad Mode, Illegal operation');
        4: writeln('Undefined hardware error');
        5: writeln('Lost unit, Unit is no longer on-line');
        6: writeln('Lost file, File is no longer in directory');
        7: writeln('Bad Title, Illegal file name');
        8: writeln('No room, insufficient space');
        9: writeln('No unit, No such volume on line');
        10: writeln('No file, No such file on volume');
        11: writeln('Duplicate file');
        12: writeln('Not closed, attempt to open an open file');
        13: writeln('Not open, attempt to close a closed file');
        14: writeln('Bad format, error in reading real or integer');
        15: writeln('Ring buffer overflow')
      end; (* case *)
    gotoxy(0,promptline)
  end; (* io_error *)

procedure debugwrite(*s: string255*);

(* writes a debugging message *)
var i: integer;

  begin
    if debug then
      begin
        gotoxy(0,debugline);
        screen( sc_clreol );
        write(s);
        for i := 1 to 2000 do ;                (* write debugging message *)
      end (* if debug *)
  end; (* debugwrite *)

procedure debugint(*s: string255; i: integer*);

(* write a debugging message and an integer *)

  begin
    if debug then
      begin
        debugwrite(s);
        write(i)
      end (* if debug *)
  end; (* debugint *)

function min(*x,y: integer): integer*);

(* returns smaller of two integers *)

  begin
    if x < y then
        min := x
    else
        min := y
  end; (* min *)

function tochar(*ch: char): char*);

(* tochar converts a control character to a printable one by adding space *)

  begin
    tochar := chr(ord(ch) + ord(' '))
  end; (* tochar *)

function unchar(*ch: char): char*);

(* unchar undoes tochar *)

  begin
    unchar := chr(ord(ch) - ord(' '))
  end; (* unchar *)

function ctl(*ch: char): char*);

(* ctl toggles control bit: ^A becomes A, A becomes ^A *)

  begin
    ctl := chr(xor(ord(ch),64))
  end; (* ctl *)

procedure echo(*ch: char*);

(* echos a character on the screen *)
const
   maxtry = 30000;

var count, cursorx, cursory:integer;
 The DataMedia emulation is by John Socha. 
begin
   ch := chr(aand(ord(ch),127)); (* mask off parity bit *)

   if emulating and (ord(ch) in [30,25,28,31,29,11]) then
      case ord(ch) of
         { Datamedia 1520 emulation }
         { rs }30: begin
                      { allow timeout while waiting for coordinates
                        so computer doesn't freeze }
                      count := 0;
                      repeat
                         count := count + 1
                      until read_ch( inport, ch ) or (count>maxtry);
                      if count<=maxtry then begin
                         cursorx:=ord(ch)-32;
                         count := 0;
                         repeat
                            count := count + 1
                         until read_ch( inport, ch ) or (count>maxtry);
                         if count<=maxtry then begin
                            cursory:=ord(ch)-32;
                            gotoxy(cursorx,cursory)
                         end
                      end
                   end;
         { em }25: screen( sc_home );
         { fs }28: screen( sc_right );
         { us }31: screen( sc_up );
         { gs }29: screen( sc_clreol );
         { vt }11: screen( sc_clreos )
      end
    else
       unitwrite(1,ch,1,,12)  { the 12 eliminates DLE & CR expansion }
  end; (* echo *)


function getch(*var r: char_int_rec): boolean*);

(* gets a character, strips parity, returns true if it got a char which *)
(* isn't Kermit SOH, false if it gets SOH or nothing after maxtry *)

const maxtry = 10000;

var count: integer;

  begin
    count := 0;
    getch := false;
    repeat
        count := count + 1;
    until (read_ch(inport,r.ch)) or (count>maxtry);  (* wait for a character *)
    if (count > maxtry) then                    (* if wait too long then *)
        exit(getch);                              (* get out of here *)
    r.i := aand(r.i,127);                       (* strip parity from char *)
    getch := (r.ch <> chr(soh));                (* return true if not SOH *)
  end; (* getch *)


function getsoh(*: boolean*);

(* reads characters until it finds an SOH; returns false if has to read more *)
(* than maxtry chars *)
 modified by SP 

const maxtry = 10000;

var ch: char;
    count: integer;

  begin
    count := 0;
    getsoh := true;
    repeat
      repeat
        count := count + 1
      until (read_ch(inport,ch)) or (count > maxtry); (* wait for a character *)
      if (count > maxtry) then
        begin
            getsoh := false;
            exit(getsoh)
          end; (* if *)
        ch := chr(aand(ord(ch),127));            (* strip parity of char *)
    until (ch = chr(SOH))                        (* if not SOH, get more *)
  end; (* getsoh *)


function getfil(*filename: string255): boolean*);

(* opens a file for writing *)

  begin
    (*$I-*) (* turn i/o checking off *)
    rewrite(f,filename);
    (*$I-*) (* turn i/o checking on *)
    getfil := (ioresult = 0)
  end; (* getfil *)


procedure writescreen(*s: string255*);

(* sets up the screen for receiving or sending files *)

begin
   page(output);
   gotoxy(0,titleline);
   write('            Kermit UCSD p-system, Version ', version );
   gotoxy(statuspos,statusline);
   write(s);
   gotoxy(0,packetline);
   write('Number of Packets: ');
   gotoxy(0,retryline);
   write('Number of Tries: ');
   gotoxy(0,fileline);
   write('File Name: ');
end; (* writescreen *)


procedure refresh_screen(*numtry, num: integer*);

(* keeps track of packet count on screen *)

begin
   gotoxy(retrypos,retryline);
   write(numtry: 5);
   gotoxy(packetpos,packetline);
   write(num: 5)
end; (* refresh_screen *)


begin { body of unit kermutil }
   { initialization code }
   syscom^.crtinfo.flush := chr(255);  { effectively turning flush off }
   syscom^.crtinfo.stop := chr(254);   { effectively turning stop off }

   ***;  { <-- would you believe that this is Pascal? }

   { termination code }
   syscom^.crtinfo.flush := chr(6);  { turn flush back on }
   syscom^.crtinfo.stop := chr(19)   { effectively turning stop off }

end. { kermutil }

