home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c / tp4ker.pas < prev    next >
Pascal/Delphi Source File  |  2020-01-01  |  140KB  |  5,454 lines

  1. <<< async.pas >>>
  2. {$R-,S-,I-,D+,T+,F-,V+,B-,N-,L+ }
  3. UNIT ASYNC;
  4.  
  5. INTERFACE
  6.  
  7. Uses Delays;
  8.  
  9. (**************************** ASYNC.PAS *********************************)
  10. (*                                                                      *)
  11. (*     Modul for bruk av 1,2,3 el. 4 COM-porter samtidig, med interrupt *)
  12. (*     bde ved sending og mottak og uavhengige ring-buffere opptil     *)
  13. (*     64k for hver retning og port.                                    *)
  14. (*                                                                      *)
  15. (*     Oslo, November 1987 Terje Mathisen, Norsk Hydro                  *)
  16. (*                                                                      *)
  17. (**************************** ASYNC.PRO *********************************)
  18.  
  19. CONST RX_int     = 1;
  20.       TX_int     = 2;
  21.       RLS_int    = 4;
  22.       MODEM_int  = 8;
  23.       SumOf_int  =15;
  24. TYPE
  25.   ComPortType = 1..4;
  26.   ParityType = (No_Parity, Even_Parity, Odd_Parity, Zero_Parity, One_Parity);
  27.  
  28.   RS_IntSet = 0..SumOf_int;
  29.   RS_BufPtrType  = ^RS_BufferType;
  30.   RS_BufferType = RECORD
  31.               ICadr, IntNr     : WORD;
  32.  
  33.               oldModemContrReg : BYTE;
  34.               oldLevel  : BYTE;
  35.               oldVector : Pointer;
  36.  
  37.               xin : Pointer;
  38.               xout, SizeX, LimitX    : WORD;
  39.  
  40.               Tin : WORD;
  41.               Tout : Pointer;
  42.               SizeT, SendFirst : WORD;
  43.  
  44.               ShowXoffPtr : Pointer;
  45.               Toggle_Xoff, RLS_user, MODEM_user : Pointer;
  46.  
  47.               Ctrl_P    : BYTE;  {0 - > default, 1..4 -> NOTIS}
  48.  
  49.               UseTint, HostXoff : BOOLEAN;
  50.               Bufferfilled : BYTE;
  51.  
  52.               AutoXoff, AltXoff : BOOLEAN;
  53.               Xoff1C, Xoff2C, Xon1C, Xon2C     : CHAR;
  54.  
  55.               Line_Status, MODEM_status : BYTE;
  56.               WaitTX : BOOLEAN;
  57.               Int_Mask : BYTE;
  58.               oldIntEnableReg : BYTE;
  59.             END;
  60.  
  61. VAR
  62.   RS_BufPtr : ARRAY [ComPortType] OF RS_BufPtrType;
  63.   RS_TimeOut : WORD;
  64.  
  65.   RS_Buffer : ARRAY [ComPortType] OF RS_BufferType; { Must be in data-seg! }
  66.  
  67.   PROCEDURE RS_MakeBuffer(Rsize,Tsize,IOaddr,SWint:WORD; com : WORD);
  68.  
  69.   PROCEDURE RS_Init (baudRate : LongInt;
  70.                   NbrOfBits,           { 5|6|7|8 }
  71.                   StopBits: WORD;      { 1|2 }
  72.                   Parity: ParityType;
  73.             { (No_Parity, Even_Parity, Odd_Parity, Zero_Parity, One_Parity) }
  74.                   VAR result: BOOLEAN;
  75.                   com: ComPortType); { 1..4 }
  76.  
  77.   PROCEDURE RS_Stop(com: ComPortType);
  78.  
  79.   PROCEDURE RS_Start(rs_int: RS_IntSet; com: ComPortType);
  80.  
  81.   PROCEDURE RS_BusyRead(VAR ch:CHAR;VAR done : BOOLEAN; com : WORD);
  82.  
  83.   PROCEDURE RS_ReadBlock(VAR buf;max:WORD;VAR bytes:WORD;com : WORD);
  84.  
  85.   PROCEDURE RS_Write(ch: CHAR;VAR done : BOOLEAN; com: WORD );
  86.  
  87.   PROCEDURE RS_WriteBlock(VAR buf;len: WORD;VAR bytes:WORD; com: WORD);
  88.  
  89.   FUNCTION RS_GetChar(VAR ch : CHAR; com : WORD): BOOLEAN;
  90.  
  91.   FUNCTION RS_Avail(com : WORD): WORD;
  92.  
  93.   FUNCTION RS_Room(com : WORD): WORD;
  94.  
  95.   PROCEDURE RS_Enable(com : WORD);
  96.  
  97.   PROCEDURE RS_WriteFirst(ch:CHAR;com:WORD);
  98.  
  99.   PROCEDURE RS_ClrBuffer(com: WORD);
  100.  
  101.   PROCEDURE RS_Set_TX_Int(rs_int : RS_IntSet; com : WORD);
  102.  
  103.   FUNCTION RS_Empty(com : WORD) : BOOLEAN;
  104.  
  105.   PROCEDURE RS_Break(ms : WORD;com : WORD);
  106.  
  107.   PROCEDURE RS_StopLink(com : WORD);
  108.  
  109.   PROCEDURE RS_StartLink(com : WORD);
  110.  
  111.   PROCEDURE RS_StopAll;
  112.  
  113. IMPLEMENTATION
  114.  
  115. CONST
  116.   LineContrReg    = 3; { to specify format of transmitted data }
  117.   LowBaudRateDiv  = 0; { lower byte of divisor to select baud rate }
  118.   HighBaudRateDiv = 1; { higher byte of divisor }
  119.   LineStatusReg   = 5; { holds status info on the data transfer }
  120.   ReceiverReg     = 0; { received CHAR is in this register }
  121.   TransmitReg     = 0; { CHAR to send is put in this reg }
  122.   IntEnableReg    = 1; { to enable the selected interrupt }
  123.   IntIdentReg     = 2;
  124.   ModemContrReg   = 4; { controls the interface to a modem }
  125.  
  126.     PROCEDURE GetAlignMem(VAR p : Pointer; size : WORD);
  127.     VAR temp : ^BYTE;
  128.     BEGIN
  129.       REPEAT
  130.         GetMem(p,size);
  131.         IF Ofs(p^) = 0 THEN Exit;
  132.         FreeMem(p,size);
  133.         New(temp);
  134.       UNTIL FALSE;
  135.     END;
  136.  
  137.   PROCEDURE RS_MakeBuffer(Rsize, Tsize, IOaddr, SWint, com: WORD);
  138.   CONST PortTab : ARRAY [ComPortType] OF WORD = ($3F8,$2F8,$3E8,$2E8);
  139.         IntTab  : ARRAY [ComPortType] OF BYTE = (12,11,12,11);
  140.   VAR c, c0, c1 : WORD;
  141.   BEGIN
  142.     IF Rsize + Tsize > MemAvail - $100 THEN BEGIN
  143.       Halt(1);
  144.     END;
  145.     IF com = 0 THEN BEGIN
  146.       c0 := 1; c1 := 4;
  147.     END
  148.     ELSE BEGIN
  149.       IF com > 4 THEN Halt(1);
  150.       c0 := com; c1 := com;
  151.     END;
  152.     FOR c := c0 TO c1 DO WITH RS_Buffer[c] DO BEGIN
  153.       IF (com = 0) AND (c > 1) THEN
  154.         RS_Buffer[c] := RS_Buffer[1]
  155.       ELSE BEGIN
  156.         IF Rsize > 0 THEN BEGIN
  157.           GetAlignMem(xin,Rsize);
  158.           SizeX := Rsize;
  159.           LimitX := Rsize DIV 8;
  160.         END;
  161.  
  162.         IF Tsize > 0 THEN BEGIN
  163.           GetAlignMem(Tout,Tsize);
  164.           SizeT := Tsize;
  165.         END;
  166.       END;
  167.  
  168.       IF IOaddr = 0 THEN
  169.         ICadr := PortTab[c]
  170.       ELSE
  171.         ICadr := IOaddr;
  172.  
  173.       IF SWint = 0 THEN
  174.         IntNr := IntTab[c]
  175.       ELSE
  176.         IntNr := SWint;
  177.  
  178. {                       Disse variablene er nullstilt allerede!
  179.       xin := 0;
  180.       xout := 0;
  181.       SendFirst := 0;
  182.       tin := 0;
  183.       tout := 0;
  184.       Ctrl_P := 0;
  185.       UseTint := FALSE;
  186.       Sending := FALSE;
  187.       Receiving := FALSE;
  188.       HostXoff := FALSE;
  189.       BufferFilled := 0;
  190.       AltXoff  := FALSE;
  191.       ShowXoffPtr := NIL;
  192.       Toggle_Xoff := 0;
  193.       RLS_user := 0;
  194.       MODEM_user := 0;
  195. }
  196.                                        {Default to use XON/XOFF!}
  197.       AutoXoff := TRUE;
  198.       Xoff1C   := ^S;
  199.       Xon1C    := ^Q;
  200.     END;
  201.   END;
  202.  
  203.   PROCEDURE RS_Init (baudRate  : LongInt;
  204.                   NbrOfBits,           { 5|6|7|8 }
  205.                   StopBits: WORD;      { 1|2 }
  206.                   Parity: ParityType;
  207.             { (No_Parity, Even_Parity, Odd_Parity, Zero_Parity, One_Parity) }
  208.                   VAR result: BOOLEAN;
  209.                   com: ComPortType); { 1..4 }
  210.   CONST ParityTab : ARRAY [ParityType] OF BYTE = (0,$18,$08,$38,$28);
  211.     VAR divisor : WORD;
  212.         parameters: BYTE;
  213.  
  214.   BEGIN (* Init *)
  215.     result := FALSE;
  216.  
  217.     WITH RS_Buffer[com] DO BEGIN
  218.       IF Xin = NIL THEN BEGIN          {No buffer allocated!}
  219.         Halt(1);
  220.       END;
  221.  
  222.     (* load the divisor of the baud rate generator: *)
  223.       IF baudrate < 1 THEN Exit;
  224.       divisor := (115200 + (baudrate DIV 2)) DIV baudrate;
  225.       Port[ICadr+LineContrReg] := $80;
  226.       Port[ICadr+HighBaudRateDiv] := Hi(divisor);
  227.       Port[ICadr+LowBaudRateDiv]  := Lo(divisor);
  228.  
  229.         (* prepare the parameters: *)
  230.       parameters := ParityTab[Parity];
  231.  
  232.       IF stopBits = 2 THEN
  233.         parameters := parameters + 4
  234.       ELSE IF stopBits <> 1 THEN Exit;
  235.  
  236.       IF (nbrOfBits < 5) OR (nbrOfBits > 8) THEN Exit;
  237.       Port[ICadr+LineContrReg] := parameters + (nbrOfBits - 5);
  238.  
  239.         (* Disable Interrupts: *)
  240.       Port[ICadr+IntEnableReg] := 0;
  241.       result := TRUE;
  242.     END;
  243.   END                                  { Init };
  244.  
  245.     CONST
  246.       I8259ContrWord1 = $21;  (* Interrupt controller,
  247.                                  Operation Control Word 1 *)
  248.  
  249. (************************* ASSEMBLER ROUTINES FOR MAX SPEED ****************)
  250.  
  251.     PROCEDURE RS_Com1Int;                            EXTERNAL;
  252.  
  253.     PROCEDURE RS_Com2Int;                            EXTERNAL;
  254.  
  255.     PROCEDURE RS_Com3Int;                            EXTERNAL;
  256.  
  257.     PROCEDURE RS_Com4Int;                            EXTERNAL;
  258.  
  259.     PROCEDURE RS_BusyRead(VAR ch:CHAR;VAR done : BOOLEAN;
  260.               com : WORD);                    EXTERNAL;
  261.  
  262.     PROCEDURE RS_ReadBlock(VAR buf;max:WORD;
  263.               VAR bytes : WORD;com : WORD);EXTERNAL;
  264.  
  265.     PROCEDURE RS_Write(ch: CHAR;VAR done : BOOLEAN;
  266.               com: WORD );                    EXTERNAL;
  267.  
  268.     PROCEDURE RS_WriteBlock(VAR buf;len: WORD;
  269.               VAR bytes : WORD; com: WORD);EXTERNAL;
  270.  
  271.     FUNCTION RS_GetChar(VAR ch : CHAR;
  272.              com : WORD): BOOLEAN;            EXTERNAL;
  273.  
  274.     FUNCTION RS_Avail(com : WORD): WORD;   EXTERNAL;
  275.  
  276.     FUNCTION RS_Room(com : WORD): WORD;    EXTERNAL;
  277.  
  278.     PROCEDURE RS_Enable(com : WORD);          EXTERNAL;
  279.  
  280.     PROCEDURE RS_WriteFirst(ch:CHAR;com:WORD);EXTERNAL;
  281.  
  282.   {$L ASYNC.OBJ}
  283.  
  284. (***************************************************************************)
  285.  
  286. VAR vect_tab : ARRAY [0..255] OF Pointer ABSOLUTE 0:0;
  287.  
  288. PROCEDURE Disable; Inline($FA);
  289.  
  290. PROCEDURE Enable; Inline($FB);
  291.  
  292. PROCEDURE GetVector(vnr : WORD; VAR vector : Pointer);
  293. BEGIN
  294.   vector := vect_tab[vnr];
  295. END;                                   {GetVector}
  296.  
  297. PROCEDURE SetVector(vnr : WORD; vector : Pointer);
  298. BEGIN
  299.   Disable;
  300.     vect_tab[vnr] := vector;
  301.   Enable;
  302. END;                                   {PutVector}
  303.  
  304. PROCEDURE RS_Start(rs_int : RS_IntSet; com: ComPortType);
  305. VAR
  306.   adr : Pointer;
  307.   mask, tempSet : BYTE;
  308.   dummy : WORD;
  309.   ch : CHAR;
  310.   ok : BOOLEAN;
  311. BEGIN
  312.   WITH RS_Buffer[com] DO
  313.     IF OldVector = NIL THEN BEGIN
  314.  
  315.       (* enable interrupts in the interrupt controller (8259): *)
  316.       tempSet := Port[I8259ContrWord1];
  317.       (* set the interrupt vector *)
  318.  
  319.       GetVector(IntNr,OldVector);
  320.       CASE com OF
  321.         1 : adr := @RS_Com1int;
  322.         2 : adr := @RS_Com2int;
  323.         3 : adr := @RS_Com3int;
  324.         4 : adr := @RS_Com4int;
  325.       END;
  326.       SetVector(IntNr,adr);
  327.  
  328.       mask := 1 Shl (IntNr - 8);
  329.       oldLevel := tempSet AND mask;
  330.  
  331.       DISABLE;
  332.       Port[I8259ContrWord1] := tempSet AND NOT mask;
  333.  
  334.       dummy  :=  Port[ICadr+IntIdentReg] +
  335.                  Port[ICadr+LineStatusReg] +
  336.                  Port[ICadr+ModemContrReg] +
  337.                  Port[ICadr+ReceiverReg];    (* clear the controller *)
  338.  
  339.       WORD(xin)    := 0;
  340.       xout         := 0;
  341.  
  342.       SendFirst    := 0;
  343.       tin          := 0;
  344.       WORD(tout)   := 0;
  345.  
  346.       HostXoff     := FALSE;
  347.       WaitTX       := FALSE;
  348. {      AutoXoff     := TRUE;      }
  349.       BufferFilled := 0;
  350.       Line_Status  := 0;
  351.       MODEM_Status := 0;
  352.  
  353.       tempSet := Port[ICadr+ModemContrReg];
  354.       oldModemContrReg := tempSet AND 11;    { DTR and RTS }
  355.  
  356.       Port[ICadr+ModemContrReg] := tempSet OR 11;
  357.  
  358.       Int_Mask := rs_int;
  359.       oldIntEnableReg := Port[ICadr+IntEnableReg];
  360.       Port[ICadr+IntEnableReg] := rs_int;
  361.       UseTint := (TX_int AND rs_int) <> 0;
  362.  
  363.       ENABLE;
  364.  
  365.     END;
  366.     dummy := 50;
  367.     REPEAT
  368.       RS_BusyRead(ch,ok,com);               { Remove pending int's }
  369.       Dec(dummy);
  370.     UNTIL NOT ok OR (dummy = 0);
  371. END                                {RS_Start};
  372.  
  373. PROCEDURE RS_Stop(com: ComPortType);
  374. BEGIN
  375.   WITH RS_Buffer[com] DO
  376.     IF OldVector <> NIL THEN BEGIN
  377.       DISABLE;
  378.  
  379.     (* restore old mask in 8259: *)
  380.       Port[I8259ContrWord1] := Port[I8259ContrWord1] OR oldLevel;
  381.  
  382.     (* disable interrupts in 8250: *)
  383.       Port[ICadr+IntEnableReg] := oldIntEnableReg;
  384.     (* restore modem control register in 8250: *)
  385.       Port[ICadr+ModemContrReg] :=
  386.          (Port[ICadr+ModemContrReg] AND 244) OR oldModemContrReg;
  387.       ENABLE;
  388.  
  389.     (* restore the old interrupt vector *)
  390.       SetVector(IntNr,OldVector);
  391.       OldVector := NIL;
  392.     END;
  393. END                                {RS_Stop};
  394. (*
  395. PROCEDURE RS_Read(VAR ch: CHAR;com: WORD );
  396. VAR done : BOOLEAN;
  397. BEGIN
  398.   REPEAT
  399.     RS_BusyRead (ch, done, com);
  400.   UNTIL done;
  401. END                                {RS_Read};
  402. *)
  403. PROCEDURE RS_ClrBuffer(com: WORD);
  404. BEGIN
  405.   WITH RS_Buffer[com] DO BEGIN
  406.     Disable;
  407.     WORD(xin) := 0;
  408.     xout := 0;
  409.     tin := 0;
  410.     WORD(tout) := 0;
  411.     SendFirst := 0;
  412.     Enable;
  413.   END;
  414. END;                               {ClrBuffer}
  415.  
  416. PROCEDURE RS_Set_TX_Int(rs_int : RS_IntSet; com : WORD);
  417. BEGIN
  418.   WITH RS_Buffer[com] DO BEGIN
  419.     Disable;
  420.     tin := 0;
  421.     WORD(tout) := 0;
  422.     SendFirst := 0;
  423.     Int_Mask := rs_int;
  424.     Port[ICadr+IntEnableReg] := rs_int;
  425.     UseTint := (TX_int AND rs_int) <> 0;
  426.     Enable;
  427.   END;
  428. END;                               {RS_Set_TX_Int}
  429.  
  430. FUNCTION RS_Empty(com : WORD) : BOOLEAN;
  431. VAR ch : CHAR;
  432.     ok : BOOLEAN;
  433. BEGIN
  434.   WITH RS_Buffer[com] DO
  435.     RS_Empty := WORD(xin) = xout;
  436. END;                                 {EmptyBuffer}
  437.  
  438. PROCEDURE RS_Break(ms : WORD;com : WORD);
  439. VAR oldreg : BYTE;
  440. BEGIN
  441.   WITH RS_Buffer[com] DO BEGIN
  442.     WaitTX := TRUE;
  443.     WHILE Port[ICadr+LineStatusReg] AND 32 = 0 DO ; { wait for no traffic }
  444.     oldreg := Port[ICadr+LineContrReg];
  445.     Port[ICadr+LineContrReg]:= oldreg OR 64;
  446.     Delay(ms);
  447.     Port[ICadr+LineContrReg] := OldReg;
  448.     Delay(250);
  449.     WaitTX := FALSE;
  450.     IF NOT HostXoff THEN RS_Enable(com);
  451.   END;
  452. END;                                 {RS_Break}
  453.  
  454. PROCEDURE RS_StopLink(com : WORD);
  455. VAR bf : BYTE;
  456. BEGIN
  457.   WITH RS_Buffer[com] DO
  458.     IF AutoXoff THEN BEGIN
  459.       Disable;
  460.       bf := BufferFilled;
  461.       BufferFilled := BufferFilled OR 2;
  462.       Enable;
  463.       IF bf = 0 THEN BEGIN
  464.         RS_WriteFirst(Xoff1C,com);
  465.         Delay(10);
  466.       END;
  467.     END;
  468. END;
  469.  
  470. PROCEDURE RS_StartLink(com : WORD);
  471. VAR bf : BYTE;
  472. BEGIN
  473.   WITH RS_Buffer[com] DO
  474.     IF AutoXoff THEN BEGIN
  475.       Disable;
  476.       BufferFilled := BufferFilled AND 253;
  477.       bf := BufferFilled;
  478.       Enable;
  479.       IF bf = 0 THEN BEGIN
  480.         RS_WriteFirst(Xon1C,com);
  481.       END;
  482.     END;
  483. END;
  484.  
  485. VAR SaveExit : Pointer;
  486.  
  487. PROCEDURE RS_StopAll;
  488. BEGIN
  489.   RS_Stop(1);
  490.   RS_Stop(2);
  491.   RS_Stop(3);
  492.   RS_Stop(4);
  493.   ExitProc := SaveExit;
  494. END;
  495.  
  496. BEGIN
  497.   FillChar(RS_Buffer,SizeOf(RS_Buffer),#0);
  498.   RS_BufPtr[1] := Addr(RS_Buffer[1]);
  499.   RS_BufPtr[2] := Addr(RS_Buffer[2]);
  500.   RS_BufPtr[3] := Addr(RS_Buffer[3]);
  501.   RS_BufPtr[4] := Addr(RS_Buffer[4]);
  502.   RS_TimeOut := 0;
  503.   SaveExit := ExitProc;
  504.   ExitProc := @RS_StopAll;
  505. END.
  506. <<< async.sal >>>
  507. ; ASYNC.SAL Driver for RS232 fra Turbo Pascal V4
  508. ; Version 2.0
  509. ;     Date: 87-11-19, 20:10
  510.  
  511.             saljmp short
  512.             salcmp unsigned
  513.             salmac :=     mov &-,&+
  514.  
  515. include pascal.mac
  516.  
  517. buffers struc
  518.     PortNr  dw ?
  519.     IntNr   dw ?
  520.  
  521.     oldModemCntrReg db ?
  522.     oldLevel        db ?
  523.     oldVector       dd ?
  524.  
  525.     Inx     dw ?
  526.     R_Buf2  dw ?
  527.     OutX    dw ?
  528.     SizeX   dw ?
  529.     LimitX  dw ?
  530.  
  531.     InT     dw ?
  532.     OutT    dw ?
  533.     T_Buf2  dw ?
  534.     SizeT   dw ?
  535.     Send_T  dw ?
  536.  
  537.     Show_X  dw ?
  538.     Show_X2 dw ?
  539.  
  540.     Toggle_Xoff dd ?
  541.     RLS_user    dd ?
  542.     MODEM_user  dd ?
  543.  
  544.     Ctrl_P  db ?
  545.  
  546.     UseTInt db ?
  547.     HostX   db ?
  548.     Bfull   db ?
  549.  
  550.     AutoX   db ?
  551.     AltX    db ?
  552.  
  553.     Xoff1C  db ?
  554.     Xoff2C  db ?
  555.     Xon1C   db ?
  556.     Xon2C   db ?
  557.  
  558.     Line_Status  db ?
  559.     MODEM_Status db ?
  560.  
  561.     WaitTX  db ?
  562.     Int_Mask db ?
  563.  
  564. buffers ends
  565.  
  566. DXofs MACRO ofs
  567.     mif ofs
  568.         ife ofs - 1
  569.             inc dx
  570.         else
  571.             ife ofs + 1
  572.                 dec dx
  573.             else
  574.                 add dx,ofs
  575.             endif
  576.         endif
  577.     endif
  578. ENDM
  579.  
  580. InPort MACRO ofs
  581.     dx := [bx.PortNr]
  582.     DXofs <ofs>
  583.     in al,dx
  584. ENDM
  585.  
  586. OutPort MACRO ofs
  587.     dx := [bx.PortNr]
  588.     DXofs <ofs>
  589.     out dx,al
  590. ENDM
  591.  
  592. InPOfs MACRO ofs
  593.     DXofs <ofs>
  594.     in al,dx
  595. ENDM
  596.  
  597. OutPOfs MACRO ofs
  598.     DXofs <ofs>
  599.     out dx,al
  600. ENDM
  601.  
  602.     LineContrReg    = 3   ; (* to specify format of transmitted data *)
  603.     LowBaudRateDiv  = 0   ; (* lower byte of divisor to select baud rate *)
  604.     HighBaudRateDiv = 1   ; (* higher byte of divisor *)
  605.     LineStatusReg   = 5   ; (* holds status info on the data transfer *)
  606.     ReceiverReg     = 0   ; (* received CHAR is in this register *)
  607.     TransmitReg     = 0   ; (* CHAR to send is to put in this reg *)
  608.     IntEnableReg    = 1   ; (* to enable the selected interrupt *)
  609.     IntIdentReg     = 2   ; (* to identify the interrupt *)
  610.     ModemContrReg   = 4   ; (* controls the interface to a modem *)
  611.     ModemStatusReg  = 6   ; (* holds status of line (BREAK etc.) *)
  612.  
  613.     Icntrlw2 = 20h                     ;Interrupt controller
  614.     SEOI1    = 64h                     ;EOI for COM1
  615.     SEOI2    = 63h                     ;EOI for COM2
  616.  
  617.     FALSE    = 0
  618.     TRUE     = 1
  619.  
  620.     RLSint   = 6
  621.     RDRint   = 4
  622.     THREint  = 2
  623.     MODEMint = 0
  624.  
  625. DATA SEGMENT WORD PUBLIC
  626.     ASSUME DS:DATA
  627.  
  628. EXTRN RS_BufPtr:WORD
  629. EXTRN RS_TimeOut:WORD
  630. DATA ENDS
  631.  
  632. CODE SEGMENT BYTE PUBLIC
  633.     ASSUME CS:CODE
  634.  
  635.     public Rs_Com4int
  636. Rs_Com4int proc far
  637.     push ax
  638.     push bx
  639.     mov bx,offset DATA:rs_bufptr[12]
  640.     jmp short comcont
  641.  
  642.     public rs_com3int
  643. rs_com3int proc far
  644.     push ax
  645.     push bx
  646.     mov bx,offset DATA:rs_bufptr[8]
  647.     jmp short comcont
  648.  
  649.     public rs_com2int
  650. rs_com2int proc far
  651.     push ax
  652.     push bx
  653.     mov bx,offset DATA:rs_bufptr[4]
  654.     jmp short comcont
  655.  
  656.     public rs_com1int
  657. rs_com1int proc far
  658.     push ax
  659.     push bx
  660.     mov bx,offset DATA:rs_bufptr[0]
  661. comcont:
  662.     push ds
  663.     mov ax, DATA
  664.     mov ds,ax
  665. ASSUME DS:DATA
  666.  
  667.     mov bx,[bx]
  668.  
  669. ; Reset Video TimeOut Count
  670.     rs_timeout := 0
  671.  
  672. ;   STI                                ;Enable int's
  673.  
  674.     push cx
  675.     push dx
  676.     push di
  677.     push si
  678.     push es
  679.  
  680. repeat_int:
  681.  
  682.     CLI
  683.     InPort IntIdentReg                 ;Hvorfor er jeg her?
  684.  
  685.     if al = RDRint then
  686.         call ReadInt
  687.         jmp repeat_int
  688.     endif
  689.     if al = THREint then               ;TX int
  690.         call SendNext                  ;Restart
  691.         jmp repeat_int
  692.     endif
  693.     if al = RLSint then
  694.         InPOfs <LineStatusReg - IntIdentReg>
  695.         and al,1Eh                     ;Keep OE(2),PE(4),FE(8) and BI(10)
  696.         or [bx.Line_Status],al
  697.         jmp repeat_int
  698.     endif
  699.     if al = MODEMint then
  700.         InPOfs <ModemStatusReg-IntIdentReg> ;Restart async chip
  701.         or [bx.MODEM_Status],al
  702.         if word ptr [bx].MODEM_user <> 0 then
  703.             push bx
  704.             push ds
  705.             call dword ptr [bx+MODEM_user]
  706.             pop ds
  707.             pop bx
  708.         endif
  709.         jmp repeat_int
  710.     endif
  711.  
  712.     InPOfs <ModemStatusReg-IntIdentReg> ;Restart async chip
  713.     or [bx.MODEM_Status],al
  714.     jmp $+2
  715.     InPOfs <LineStatusReg-ModemStatusReg>
  716.     and al,1Eh                         ;Keep OE(2),PE(4),FE(8) and BI(10)
  717.     or [bx.Line_Status],al
  718.  
  719.     pop  es
  720.     pop  si
  721.     pop  di
  722.     pop  dx
  723.     pop  cx
  724.  
  725.     pop  ds
  726.     pop  bx
  727.  
  728. ; Enable HW int's
  729.  
  730.     CLI
  731.     al := 20h
  732.     out Icntrlw2,al
  733.  
  734.     pop  ax
  735.     iret
  736. rs_com1int endp
  737. rs_com2int endp
  738. rs_com3int endp
  739. rs_com4int endp
  740.  
  741. ReadInt Proc near
  742.  
  743.     InPOfs <ReceiverReg - IntIdentReg> ;Get received char
  744.  
  745. ; Test if room in buffer
  746.  
  747.     les si,dword ptr [bx.InX]          ;Get buffer Address
  748.     lea di,[si+1]
  749.     if di >= [bx.SizeX] then xor di,di
  750.  
  751.     if di <> [bx.OutX] then            ;Buffer not full
  752.         es:[si] := al
  753.         [bx.InX] := di
  754.     else
  755.         or [bx.Line_Status],20h        ;Overrun Error!
  756.     endif
  757.  
  758.     STI
  759.  
  760.     if [bx.AutoX] = FALSE then ret
  761.  
  762. ; Test if XOFF or XON
  763.     ah := al                           ; Test if XOFF or XON
  764.     and ah,7fh                         ; Use 7 low bits!
  765.  
  766.     if [bx.Ctrl_P] < 1 then
  767.             if [bx.HostX] = FALSE then
  768.                 cmp ah,[BX.Xoff1C]
  769.                 je TurnOff
  770.                 if [bx.AltX] = TRUE then
  771.                     cmp ah,[bx.Xoff2C]
  772.                     je TurnOff
  773.                 endif
  774.         endif
  775.         cmp ah,[BX.Xon1C]
  776.         je TurnOn
  777.         cmp [bx.AltX],TRUE
  778.         jne nochange
  779.         cmp ah,[bx.Xon2C]
  780.         je TurnOn
  781.         jmp short nochange
  782.     endif
  783.     if = then                          ; if [bx.Ctrl_P] = 1 then
  784.             if ah = 10h then
  785.                 [bx.Ctrl_P] := 2
  786.                 jmp short nochange
  787.             endif
  788.             cmp [bx.HostX],TRUE
  789.             je TurnOn
  790.             cmp ah,[bx.Xoff1C]
  791.             je TurnOff
  792.             jmp short nochange
  793.     endif
  794.     if [bx.Ctrl_P] = 2 then
  795.             [bx.Ctrl_P] := 3
  796.             jmp short nochange
  797.     endif
  798.     [bx.Ctrl_P] := 1
  799.     jmp short nochange
  800.  
  801. TurnOn:
  802.     [bx.HostX] := FALSE                ; Save new value
  803.     call StartSender
  804.     al := ' '
  805.     jmp short updateX
  806.  
  807. TurnOff:
  808.     [bx.HostX] := TRUE
  809.     al := 'X'
  810. UpdateX:
  811.     if [bx.Show_X2] <> 0 then
  812.         les di,dword ptr [bx.Show_X]
  813.         es:[di] := al
  814.     endif
  815. NoChange:
  816.  
  817. ; Test if buffer almost full
  818.  
  819.     dx := [bx.OutX]
  820.     di := [bx.InX]
  821.     inc di
  822.     sub dx,di                                 ;InX
  823.     if carry then add dx,[bx.SizeX]
  824.  
  825. ; dx = Free space in buffer
  826.  
  827.     cmp dx,[bx.LimitX]
  828.     jbe almost_full
  829.     ret                                ;Buffer not full, early exit
  830. Almost_Full:
  831.     test [bx.Bfull],1                  ;Is our bit set?
  832.     jnz Second_Limit                   ;Yes, check if past second limit
  833.     or [bx.Bfull],1                    ;Set our bit
  834. Stop_Rec:
  835.     if [bx.UseTint] = TRUE then
  836.             al := [bx.Xoff1C]
  837.             ah := TRUE
  838.             [bx.Send_T] := ax          ;Send before all others
  839.             call StartSender
  840.             ret                        ;Exit after XOFF sent
  841.     endif
  842.     call WaitTHRE
  843.     al := [bx.Xoff1C]
  844.     out dx,al
  845.     ret
  846.  
  847. Second_Limit:
  848.     shl dx,1
  849.     cmp dx,[bx.LimitX]
  850.     jbe Stop_Rec
  851.     ret
  852. ReadInt endp
  853.  
  854. WaitTHRE proc near
  855.     mov dx,[bx].PortNr
  856.     DXofs LineStatusReg
  857.     repeat
  858.         in al,dx
  859.         ah := al
  860.         and ah,1Eh
  861.         or [bx.Line_Status],ah
  862.     until al AND 20h true
  863.     DXofs <TransmitReg - LineStatusReg>
  864.     ret
  865. WaitTHRE endp
  866.  
  867. SendByte proc near                     ; Sending WO TX-int
  868. ; INPUT   al : byte to send
  869. ; OUTPUT  ah : status
  870. ; REG'S   dx
  871.  
  872.     push ax
  873.     call WaitTHRE
  874.     pop ax
  875.  
  876.     ah := FALSE;
  877.     if [bx.HostX] = FALSE then
  878.         out dx,al
  879.         ah := TRUE
  880.     endif
  881.  
  882.     ret
  883. SendByte EndP
  884.  
  885. SendInt Proc near
  886. ; Use buffered sending
  887. ; INPUT   al : byte to send
  888. ; OUTPUT  ah : status
  889. ; REG'S   dx,si,di,es,
  890.  
  891.     si := [bx.InT]
  892.     lea di,[si+1]
  893.     if di >= [bx.SizeT] then xor di,di
  894.  
  895.     ah := FALSE
  896.     if di <> [bx.OutT] then
  897.         es := [bx.T_Buf2]
  898.         es:[si] := al
  899.  
  900.         [bx.InT] := di                 ;Update input pointer
  901.         ah := TRUE
  902.     endif
  903.  
  904.     call StartSender                   ;Restart if neccessary
  905.  
  906.     ret
  907. SendInt endp
  908.  
  909. StartSender proc near
  910.     push ax
  911.     call SendNext
  912.                                        ;Turn on TX int's again!
  913.     InPort IntEnableReg
  914.     or al,2
  915.     out dx,al
  916.  
  917.     pop ax
  918.     ret
  919. StartSender endp
  920.  
  921. SendNoMore:
  922. ; Turn off TX int's when no more data
  923.     InPort IntEnableReg
  924.     and al,NOT 2
  925.     out dx,al
  926.     ret
  927.  
  928. SendNext Proc near                     ;SI
  929. ; INPUT
  930. ; OUTPUT
  931. ; REG'S   dx,ax,si,es
  932.  
  933.     if [bx.WaitTX] = FALSE then
  934.         InPort LineStatusReg
  935.         ah := al
  936.         and ah,1Eh
  937.         or [bx.Line_Status],ah
  938.  
  939.         if al AND 20h true then
  940.             DXofs <TransmitReg - LineStatusReg>
  941.  
  942.             xor ax,ax
  943.             xchg ax,[bx.Send_T]
  944.  
  945.             if ah <> FALSE then
  946.                 out dx,al
  947.             elseif [bx.HostX] = FALSE then
  948.                 les si, dword ptr [bx.OutT]
  949.                 if si = [bx.InT] then jmp SendNoMore
  950.                 cld
  951.                 lods byte ptr es:[si]
  952.                 if si >= [bx.SizeT] then xor si,si
  953.                 [bx.OutT] := si
  954.                 out dx,al
  955.             endif
  956.         endif
  957.     endif
  958.     STI
  959.     ret
  960. SendNext endp
  961.  
  962. avail proc near
  963. ; INPUT
  964. ; OUTPUT  cx : bytes in input buffer
  965. ; REG'S   cx
  966.  
  967.     cx := [bx.InX]
  968.     sub cx,[bx.OutX]
  969.     if carry then add cx,[bx.sizeX]
  970.     ret
  971. avail endp
  972.  
  973. checkempty proc near                   ;Local proc for read and readblock
  974. ; INPUT
  975. ; OUTPUT
  976. ; REG'S   cx,ax
  977.     if [bx.Bfull] and 1 true then
  978.         call avail
  979.         if cx <= [bx.LimitX] then
  980.             and [bx.Bfull],254
  981.             if zero then
  982.                 [bx.WaitTX] := TRUE    ;Allocate TX
  983.                 call WaitTHRE
  984.                 al := [bx.Xon1C]
  985.                 out dx,al
  986.                 [bx.WaitTX] := FALSE
  987.             endif
  988.         endif
  989.     endif
  990.     ret
  991. checkempty endp
  992.  
  993. intro MACRO com
  994.     bx := [bp+com]
  995.     shl bx,1
  996.     shl bx,1
  997.     bx := rs_bufptr[bx-4]
  998. ENDM
  999.  
  1000. PasProc rs_readblock <bufs, buf, max, byts, byt, com> FAR
  1001. ; REG'S   dx,cx,si,di,es,bx,ax
  1002.  
  1003.     intro com
  1004.  
  1005.     xor dx,dx                          ;zero bytes read
  1006.     call avail
  1007.     if cx > [bp].max then cx := [bp].max ;max bytes
  1008.     jcxz skipblock
  1009.  
  1010.     mov si,[bx.OutX]                   ;output index
  1011.  
  1012.     les di,[bp].buf                    ;buffer address
  1013.     cld                                ;les forover!
  1014.  
  1015.     dx := [bx.SizeX]                   ;Copy of size
  1016.  
  1017.   push ds
  1018.     ds := [bx.R_buf2]                  ;Segment of buffer
  1019.     push bx
  1020.     xor bx,bx                          ;bytes read
  1021.  
  1022.     repeat
  1023.         lodsb
  1024.         if si >= dx then xor si,si
  1025.  
  1026.         ah := al
  1027.         inc ah
  1028.         and ah,7fh
  1029.         if ah <= ' ' then
  1030.             if bx <> 0 then
  1031.                 if si = 0 then si := dx
  1032.                 dec si
  1033.                 leave
  1034.             endif
  1035.             stosb
  1036.             inc bx
  1037.             leave
  1038.         endif
  1039.  
  1040.         stosb
  1041.         inc bx
  1042.     until loop
  1043.  
  1044.     dx := bx                           ;Save bytes read
  1045.     pop bx
  1046.   pop ds
  1047.  
  1048.     [bx.OutX] := si
  1049.  
  1050. skipblock:
  1051.     les di,[bp].byt
  1052.     es:[di] := dx                      ;bytes read in block
  1053.     call checkempty
  1054.  
  1055. PasRet
  1056.  
  1057. PasProc rs_busyread <chrs, chr, dones, done, com> FAR
  1058.     intro com
  1059.  
  1060.     si := [bx.OutX]
  1061.  
  1062.     ax := FALSE
  1063.     if si <> [bx.InX] then
  1064.         es := [bx.R_Buf2]
  1065.         cld
  1066.         lods byte ptr es:[si]
  1067.         if si >= [bx.SizeX] then xor si,si
  1068.         [bx.OutX] := si
  1069.  
  1070.         les di,[bp+chr]                ;ch
  1071.         stosb
  1072.  
  1073.         call checkempty
  1074.  
  1075.         al := TRUE
  1076.     endif
  1077.  
  1078.     les di,[bp.done]
  1079.     stosb
  1080.  
  1081. PasRet
  1082.  
  1083. PasProc rs_getchar <chrs, chr, com> FAR
  1084.  
  1085.     intro com
  1086.  
  1087.     si := [bx.OutX]
  1088.  
  1089.     xor ax,ax                          ; Return value
  1090.  
  1091.     if si <> [bx.InX] then
  1092.         es := [bx.R_Buf2]
  1093.         cld
  1094.         lods byte ptr es:[si]
  1095.         xor dx,dx
  1096.         ah := al
  1097.         inc ah
  1098.         and ah,7fh
  1099.         if ah > ' ' then
  1100.             if si >= [bx.SizeX] then xor si,si
  1101.             [bx.OutX] := si
  1102.  
  1103.             les di,[bp+chr]            ;ch
  1104.             stosb
  1105.             call checkempty
  1106.             dl := TRUE
  1107.         endif
  1108.         ax := dx
  1109.     endif
  1110. PasRet
  1111.  
  1112. PasProc rs_write <chr, dones, done, com> FAR
  1113.     intro com
  1114.  
  1115.     al := [bp+chr]
  1116.  
  1117.     if [bx.UseTInt] = TRUE then
  1118.         call SendInt
  1119.     else
  1120.         call SendByte
  1121.     endif
  1122.  
  1123.     les di,[bp.done]
  1124.     es:[di] := ah
  1125. PasRet
  1126.  
  1127. PasProc rs_writeblock <bufs, buf, len, byts, byt, com> FAR
  1128.     intro com
  1129.  
  1130.     cld                                ;Forward
  1131.  
  1132.     if [bx.UseTint] = FALSE then
  1133.  
  1134.         les si,[bp+buf]                ;buf
  1135.         cx := [bp+len]                 ;len
  1136.         dx := cx                       ;bytes sent
  1137.         jcxz skipwr
  1138.  
  1139.         push dx
  1140.         repeat
  1141.             lods byte ptr es:[si]
  1142.             call SendByte
  1143.             if ah = FALSE then leave
  1144.         until loop
  1145.         pop dx
  1146.         sub dx,cx
  1147. skipwr:
  1148.         ax := dx                       ;Bytes sent
  1149.  
  1150.     else                               ;Use TX int's
  1151.  
  1152. ; Compute free room in TX buffer
  1153.  
  1154.         cx := [bx.OutT]
  1155.         di := [bx.InT]
  1156.         lea si,[di+1]
  1157.         ax := [bx.SizeT]
  1158.  
  1159.         sub cx,si                      ; OutT - (InT+1)
  1160.         if carry then add cx,ax
  1161.  
  1162.         if cx > [bp+len] then cx := [bp+len] ;Min(room,len)
  1163.  
  1164.         push cx                            ;Bytes sent
  1165.  
  1166.         jcxz skipwblock                    ;Request to send zero bytes!
  1167.  
  1168.         es := [bx.T_Buf2]
  1169. ;       di := [bx.InT]                 ; OK from start
  1170.  
  1171.         push ds
  1172.         mov ds,[bp+bufs]
  1173. ;******************* Her peker DS p bufferet, ikke p RS_Buffer!
  1174.         mov si,[bp+buf]                    ;buf
  1175.  
  1176.         sub ax,di                          ;Size - InT
  1177.         if ax < cx then                    ;Room on top of buffer?
  1178.             sub cx,ax                      ;Overflow part
  1179.             xchg cx,ax                     ;Room on top
  1180.             rep movsb                      ;First block
  1181.             xor di,di                      ;Continue from start of TX buffer
  1182.             cx := ax                       ;last part
  1183.         endif
  1184.         rep movsb                          ;Second block
  1185.  
  1186.         pop ds
  1187. ;******************** N er DS:BX ok igjen!
  1188.  
  1189.         if di >= [bx.SizeT] then xor di,di
  1190.         [bx.InT] := di
  1191.  
  1192. skipwblock:
  1193.         pop ax                         ; # of bytes sent
  1194.  
  1195.     endif
  1196.  
  1197.     les di,[bp+done]
  1198.     stosw
  1199.     call StartSender
  1200.  
  1201. PasRet
  1202.  
  1203. PasProc rs_avail <com> FAR
  1204.     intro com
  1205.     call avail
  1206.     ax := cx
  1207. PasRet
  1208.  
  1209. PasProc rs_room <com> FAR              ;Room in output buffer
  1210.     intro com
  1211.     ax := [bx.OutT]
  1212.     dx := [bx.InT]
  1213.  
  1214.     inc dx
  1215.     sub ax,dx
  1216.     if carry then add ax,[bx].SizeT
  1217.  
  1218. PasRet
  1219.  
  1220. PasProc rs_enable <com> FAR
  1221.     intro com
  1222.     [bx.HostX] := FALSE
  1223.     mov al,0
  1224.     OutPort IntEnableReg
  1225.     al := [bx].Int_Mask
  1226.     out dx,al
  1227.     al := TRUE
  1228.     xchg al,[bx.WaitTX]
  1229.     if al = FALSE then
  1230.         call StartSender
  1231.         [bx.WaitTX] := FALSE
  1232.     endif
  1233. PasRet
  1234.  
  1235. PasProc rs_writefirst <chr, com> FAR
  1236.     intro com
  1237.  
  1238.     [bx.WaitTX] := TRUE                ;Allocate transmitter!
  1239.     call WaitTHRE
  1240.     al := [bp+chr]                     ;ch to send first
  1241.     out dx,al
  1242.     [bx.WaitTX] := FALSE
  1243. PasRet
  1244.  
  1245. CODE ENDS
  1246.  
  1247.     END
  1248. <<< crcs.pas >>>
  1249. {$R-,S-}
  1250.  
  1251. Unit CRCS;
  1252.  
  1253. Interface
  1254.  
  1255. FUNCTION CRC (VAR buf; len : WORD) : WORD;
  1256.  
  1257. FUNCTION ChkSum (VAR buf; len : WORD): WORD;
  1258.  
  1259. Implementation
  1260.  
  1261. TYPE CrcTabType = ARRAY [BYTE] OF WORD;
  1262.  
  1263. VAR CrcTab : CrcTabType;
  1264.  
  1265. FUNCTION CRC (VAR buf; len : WORD) : WORD;
  1266. BEGIN
  1267. Inline(
  1268.   $1E                    {push ds}
  1269.   /$1E                   {push ds}
  1270.   /$07                   {pop es}
  1271.   /$8D/$3E/>CRCTAB       {lea di,[>crctab]}
  1272.   /$C5/$76/<BUF          {lds si,[bp<buf]}
  1273.   /$8B/$4E/<LEN          {mov cx,[bp<len]}
  1274.   /$31/$D2               {xor dx,dx}
  1275.   /$E3/$13               {jcxz done}
  1276.   /$FC                   {cld}
  1277.                          {l1:}
  1278.   /$AC                   {lodsb}
  1279.   /$30/$D0               {xor al,dl}
  1280.   /$88/$C3               {mov bl,al}
  1281.   /$88/$F2               {mov dl,dh}
  1282.   /$30/$FF               {xor bh,bh}
  1283.   /$88/$FE               {mov dh,bh}
  1284.   /$D1/$E3               {shl bx,1}
  1285.   /$26/$33/$11           {es: xor dx,[di+bx]}
  1286.   /$E2/$EE               {loop l1}
  1287.                          {done:}
  1288.   /$89/$56/$FE           {mov [bp-2],dx}
  1289.   /$1F                   {pop ds}
  1290. );
  1291. END;
  1292.  
  1293. FUNCTION ChkSum (VAR buf; len : WORD): WORD;
  1294. BEGIN
  1295. InLine(
  1296.   $1E                    {  push ds}
  1297.   /$C5/$76/<BUF          {  lds si,[bp<buf]}
  1298.   /$8B/$4E/<LEN          {  mov cx,[bp<len]}
  1299.   /$31/$D2               {  xor dx,dx}
  1300.   /$89/$D0               {  mov ax,dx}
  1301.   /$FC                   {  cld}
  1302.   /$88/$CB               {  mov bl,cl}
  1303.   /$D1/$E9               {  shr cx,1}
  1304.   /$D1/$E9               {  shr cx,1}
  1305.   /$41                   {  inc cx}
  1306.   /$80/$E3/$03           {  and bl,3}
  1307.   /$74/$15               {  jz add0}
  1308.   /$80/$FB/$02           {  cmp bl,2}
  1309.   /$77/$07               {  ja add3}
  1310.   /$74/$08               {  je add2}
  1311.   /$EB/$09               {  jmp short add1}
  1312.                          {add4:}
  1313.   /$AC                   {  lodsb}
  1314.   /$01/$C2               {  add dx,ax}
  1315.                          {add3:}
  1316.   /$AC                   {  lodsb}
  1317.   /$01/$C2               {  add dx,ax}
  1318.                          {add2:}
  1319.   /$AC                   {  lodsb}
  1320.   /$01/$C2               {  add dx,ax}
  1321.                          {add1:}
  1322.   /$AC                   {  lodsb}
  1323.   /$01/$C2               {  add dx,ax}
  1324.                          {add0:}
  1325.   /$E2/$F2               {  loop add4}
  1326.                          {done:}
  1327.   /$89/$56/$FE           {  mov [bp-2],dx}
  1328.   /$1F                   {  pop ds}
  1329. );
  1330. END;
  1331.  
  1332. BEGIN
  1333. InLine(
  1334.    $1E                   {push ds}
  1335.   /$07                   {pop es}
  1336.   /$8D/$3E/>CRCTAB       {lea di,[>crctab]}
  1337.   /$BE/$08/$84           {mov si,$8408}
  1338.   /$FC                   {cld}
  1339.   /$31/$DB               {xor bx,bx}
  1340.   /$89/$D9               {mov cx,bx}
  1341.                          {l2:}
  1342.   /$89/$D8               {mov ax,bx}
  1343.   /$B1/$08               {mov cl,8}
  1344.                          {l3:}
  1345.   /$D1/$E8               {shr ax,1}
  1346.   /$73/$02               {jnc l4}
  1347.   /$31/$F0               {xor ax,si}
  1348.                          {l4:}
  1349.   /$E2/$F8               {loop l3}
  1350.   /$AB                   {stosw}
  1351.   /$FE/$C3               {inc bl}
  1352.   /$75/$EF               {jnz l2}
  1353. );
  1354. END.
  1355. <<< feltedit.pas >>>
  1356. {$R-,S-,D+,T+,F-,V+,B-}
  1357.  
  1358. Unit FeltEdit;
  1359.  
  1360. Interface
  1361.  
  1362. Uses Crt;
  1363.  
  1364. CONST
  1365.   ToUpper  = 1;
  1366.   ToLower  = 2;
  1367.   NoInput  = 4;
  1368.  
  1369. TYPE
  1370.   CharSet = SET OF CHAR;
  1371.   CharSetPtr = ^CharSet;
  1372.   JustType = (LeftJ,CenterJ,RightJ);
  1373.   FeltStr = STRING[12];
  1374.   PromptStr = STRING[30];
  1375.   FeltStrArray = ARRAY [0..255] OF FeltStr;
  1376.   FeltType = (CharT, StrT, EnumT, BoolT, ByteT, IntT, WordT, LongT);
  1377.   EditPtr = ^EditRecord;
  1378.   EditRecord = RECORD
  1379.                  x, y, len, xpos : BYTE;
  1380.                  just : JustType;
  1381.                  prompt : PromptStr;
  1382.                  CASE ftype : FeltType OF
  1383.                    CharT : (CharP : ^CHAR;
  1384.                             oksetC : CharSetPtr;
  1385.                             modeC : BYTE);
  1386.                    StrT  : (StrP : ^STRING;
  1387.                             oksetS : CharSetPtr;
  1388.                             modeS : BYTE);
  1389.                    EnumT,
  1390.                    BoolT : (EnumP : ^BYTE;
  1391.                             EnumAntall : BYTE;
  1392.                             EnumStr : ^FeltStrArray);
  1393.                    ByteT : (ByteP : ^BYTE;
  1394.                             ByteMin, ByteMax : LongInt);
  1395.                    IntT  : (IntP  : ^INTEGER;
  1396.                             IntMin, IntMax   : LongInt);
  1397.                    WordT : (WordP : ^WORD;
  1398.                             WordMin, WordMax : LongInt);
  1399.                    LongT : (LongP : ^LongInt;
  1400.                             LongMin, LongMax : LongInt);
  1401.                END;
  1402.  
  1403. CONST
  1404.   Eantall : WORD = 0;
  1405.   BoolStr : ARRAY [0..1] OF FeltStr = ('FALSE','TRUE');
  1406.   NumericSet : CharSet = ['0'..'9','.','+','-'];
  1407.   InsertMode : BOOLEAN = FALSE;
  1408.   LastRecord : WORD = 0;
  1409.   FeltAttr : BYTE =  14;
  1410.   EditAttr : BYTE = 112;
  1411.  
  1412. CONST
  1413.   EditChar : CHAR = #255;
  1414.  
  1415. FUNCTION EditStr(VAR str: String; VAR xpos: BYTE;
  1416.                  len, mode : BYTE; ok : CharSetPtr;just : JustType): BOOLEAN;
  1417.  
  1418. FUNCTION Pad(st:String;len : INTEGER): String;
  1419.  
  1420. FUNCTION Tstr(l : LongInt; len : INTEGER): String;
  1421.  
  1422. PROCEDURE ShowOne(VAR e : EditRecord);
  1423.  
  1424. PROCEDURE ShowAll;
  1425.  
  1426. PROCEDURE EditOne(VAR e : EditRecord);
  1427.  
  1428. PROCEDURE EditARecord(n : WORD);
  1429.  
  1430. FUNCTION UpCase(ch : CHAR): CHAR;
  1431.  
  1432. FUNCTION LoCase(ch : CHAR): CHAR;
  1433.  
  1434. PROCEDURE MakeStr(px, py, plen : BYTE; pjust : JustType;
  1435.                   prstr : PromptStr; VAR v : String; okp : Pointer; mode : BYTE);
  1436.  
  1437. PROCEDURE MakeChar(px, py, plen : BYTE; pjust : JustType;
  1438.                   prstr : PromptStr; VAR v : CHAR; okp : Pointer; mode : BYTE);
  1439.  
  1440. PROCEDURE MakeEnum(px, py, plen : BYTE; pjust : JustType;
  1441.                   prstr : PromptStr; VAR v ; antall : BYTE; VAR enum_ar);
  1442.  
  1443. PROCEDURE MakeBool(px, py, plen : BYTE; pjust : JustType;
  1444.                   prstr : PromptStr; VAR v : BOOLEAN);
  1445.  
  1446. PROCEDURE MakeByte(px, py, plen : BYTE; pjust : JustType;
  1447.                   prstr : PromptStr; VAR v : BYTE; min, max : BYTE);
  1448.  
  1449. PROCEDURE MakeInt(px, py, plen : BYTE; pjust : JustType;
  1450.                   prstr : PromptStr; VAR v : INTEGER; min, max : INTEGER);
  1451.  
  1452. PROCEDURE MakeWord(px, py, plen : BYTE; pjust : JustType;
  1453.                   prstr : PromptStr; VAR v : WORD; min, max : WORD);
  1454.  
  1455. PROCEDURE MakeLong(px, py, plen : BYTE; pjust : JustType;
  1456.                   prstr : PromptStr; VAR v : LongInt; min, max : LongInt);
  1457.  
  1458. PROCEDURE EditAllRecords;
  1459.  
  1460. PROCEDURE EditVar(VAR v);
  1461.  
  1462. (**************************************************************************)
  1463.  
  1464. Implementation
  1465.  
  1466. VAR
  1467.   ERec : ARRAY [0..255] OF EditPtr;
  1468.  
  1469. CONST No_Upper : String[3] = '';
  1470.       No_Lower : String[3] = '';
  1471.  
  1472. FUNCTION UpCase(ch : CHAR): CHAR;
  1473. VAR p : INTEGER;
  1474. BEGIN
  1475.   IF (ch >= 'a') AND (ch <= 'z') THEN ch := CHAR(BYTE(ch)-32)
  1476.   ELSE BEGIN
  1477.     p := Pos(ch,No_Lower);
  1478.     IF p > 0 THEN ch := No_Upper[p];
  1479.   END;
  1480.   UpCase := ch;
  1481. END;
  1482.  
  1483. FUNCTION LoCase(ch : CHAR): CHAR;
  1484. VAR p : INTEGER;
  1485. BEGIN
  1486.   IF (ch >= 'A') AND (ch <= 'Z') THEN ch := CHAR(BYTE(ch)+32)
  1487.   ELSE BEGIN
  1488.     p := Pos(ch,No_Upper);
  1489.     IF p > 0 THEN ch := No_Lower[p];
  1490.   END;
  1491.   LoCase := ch;
  1492. END;
  1493.  
  1494. PROCEDURE MakeStr(px, py, plen : BYTE; pjust : JustType;
  1495.                   prstr : PromptStr; VAR v : String; okp : Pointer; mode : BYTE);
  1496. BEGIN
  1497.   New(ERec[EAntall]);
  1498.  
  1499.   WITH ERec[Eantall]^ DO BEGIN
  1500.     x := px; y := py; len := plen; prompt := prstr;
  1501.     ftype := StrT; xpos := 1; just := pjust;
  1502.     StrP := Addr(v);
  1503.     oksetS := okp;
  1504.     modeS := mode;
  1505.   END;
  1506.   Inc(EAntall);
  1507. END;
  1508.  
  1509. PROCEDURE MakeChar(px, py, plen : BYTE; pjust : JustType;
  1510.                   prstr : PromptStr; VAR v : CHAR; okp : Pointer; mode : BYTE);
  1511. BEGIN
  1512.   New(ERec[EAntall]);
  1513.  
  1514.   WITH ERec[Eantall]^ DO BEGIN
  1515.     x := px; y := py; len := plen; prompt := prstr;
  1516.     ftype := CharT; xpos := 1; just := pjust;
  1517.     CharP := Addr(v);
  1518.     oksetC := okp;
  1519.     modeC := mode;
  1520.   END;
  1521.   Inc(EAntall);
  1522. END;
  1523.  
  1524. PROCEDURE MakeEnum(px, py, plen : BYTE; pjust : JustType;
  1525.                   prstr : PromptStr; VAR v ; antall : BYTE; VAR enum_ar);
  1526. BEGIN
  1527.   New(ERec[EAntall]);
  1528.  
  1529.   WITH ERec[Eantall]^ DO BEGIN
  1530.     x := px; y := py; len := plen; prompt := prstr;
  1531.     ftype := EnumT; xpos := 1; just := pjust;
  1532.     EnumP := Addr(v);
  1533.     EnumAntall := antall;
  1534.     EnumStr := Addr(enum_ar);
  1535.   END;
  1536.   Inc(EAntall);
  1537. END;
  1538.  
  1539. PROCEDURE MakeBool(px, py, plen : BYTE; pjust : JustType;
  1540.                   prstr : PromptStr; VAR v : BOOLEAN);
  1541. BEGIN
  1542.   MakeEnum(px,py,plen,pjust,prstr,v,2,BoolStr);
  1543. END;
  1544.  
  1545. PROCEDURE MakeByte(px, py, plen : BYTE; pjust : JustType;
  1546.                   prstr : PromptStr; VAR v : BYTE; min, max : BYTE);
  1547. BEGIN
  1548.   New(ERec[EAntall]);
  1549.  
  1550.   WITH ERec[Eantall]^ DO BEGIN
  1551.     x := px; y := py; len := plen; prompt := prstr;
  1552.     ftype := ByteT; xpos := 1; just := pjust;
  1553.     ByteP := Addr(v);
  1554.     ByteMin := min;
  1555.     ByteMax := max;
  1556.   END;
  1557.   Inc(EAntall);
  1558. END;
  1559.  
  1560. PROCEDURE MakeInt(px, py, plen : BYTE; pjust : JustType;
  1561.                   prstr : PromptStr; VAR v : INTEGER; min, max : INTEGER);
  1562. BEGIN
  1563.   New(ERec[EAntall]);
  1564.  
  1565.   WITH ERec[Eantall]^ DO BEGIN
  1566.     x := px; y := py; len := plen; prompt := prstr;
  1567.     ftype := IntT; xpos := 1; just := pjust;
  1568.     IntP := Addr(v);
  1569.     IntMin := min;
  1570.     IntMax := max;
  1571.   END;
  1572.   Inc(EAntall);
  1573. END;
  1574.  
  1575. PROCEDURE MakeWord(px, py, plen : BYTE; pjust : JustType;
  1576.                   prstr : PromptStr; VAR v : WORD; min, max : WORD);
  1577. BEGIN
  1578.   New(ERec[EAntall]);
  1579.  
  1580.   WITH ERec[Eantall]^ DO BEGIN
  1581.     x := px; y := py; len := plen; prompt := prstr;
  1582.     ftype := WordT; xpos := 1; just := pjust;
  1583.     WordP := Addr(v);
  1584.     WordMin := min;
  1585.     WordMax := max;
  1586.   END;
  1587.   Inc(EAntall);
  1588. END;
  1589.  
  1590. PROCEDURE MakeLong(px, py, plen : BYTE; pjust : JustType;
  1591.                   prstr : PromptStr; VAR v : LongInt; min, max : LongInt);
  1592. BEGIN
  1593.   New(ERec[EAntall]);
  1594.  
  1595.   WITH ERec[Eantall]^ DO BEGIN
  1596.     x := px; y := py; len := plen; prompt := prstr;
  1597.     ftype := LongT; xpos := 1; just := pjust;
  1598.     LongP := Addr(v);
  1599.     LongMin := min;
  1600.     LongMax := max;
  1601.   END;
  1602.   Inc(EAntall);
  1603. END;
  1604.  
  1605. FUNCTION Pad(st:String;len : INTEGER): String;
  1606. BEGIN
  1607.   IF len < 0 THEN BEGIN
  1608.     len := Lo(-len);
  1609.     WHILE len > Length(st) DO st := ' ' + st;
  1610.   END
  1611.   ELSE IF len > 0 THEN BEGIN
  1612.     len := Lo(len);
  1613.     WHILE len > Length(st) DO st := st + ' ';
  1614.   END;
  1615.   Pad := st;
  1616. END;
  1617.  
  1618. (*
  1619. FUNCTION Justify(st : String; len : BYTE; just : JustType): String;
  1620. VAR front : BOOLEAN;
  1621. BEGIN
  1622.   CASE just OF
  1623.     LeftJ   : Justify := Pad(st,len);
  1624.     CenterJ : BEGIN
  1625.                 front := FALSE;
  1626.                 WHILE Length(st) < len DO BEGIN
  1627.                   IF front THEN st := ' ' + st
  1628.                   ELSE st := st + ' ';
  1629.                   front := NOT front;
  1630.                 END;
  1631.                 Justify := st;
  1632.               END;
  1633.     RightJ  : Justify := Pad(st,-len);
  1634.   END;
  1635. END;
  1636. *)
  1637.  
  1638. FUNCTION Tstr(l : LongInt; len : INTEGER): String;
  1639. VAR st : String;
  1640. BEGIN
  1641.   Str(l:len,st);
  1642.   Tstr := st;
  1643. END;
  1644.  
  1645. FUNCTION Refresh(len: BYTE; just : JustType; st : String): INTEGER;
  1646. VAR front, back, offs : INTEGER;
  1647. BEGIN
  1648.   front := len - Length(st); IF front < 0 THEN front := 0;
  1649.   CASE just OF
  1650.     LeftJ   : BEGIN back := front; front := 0; END;
  1651.     RightJ  : back := 0;
  1652.     CenterJ : BEGIN back := (front+1) DIV 2; Dec(front,back); END;
  1653.   END;
  1654.   IF front > 0 THEN Write('':front);
  1655.   Write(st);
  1656.   IF back > 0 THEN Write('':back);
  1657.   Refresh := front;
  1658. END;
  1659.  
  1660. PROCEDURE ShowOne(VAR e : EditRecord);
  1661. VAR i : WORD;
  1662.     l : LongInt;
  1663.     attr : BYTE;
  1664. BEGIN
  1665.   attr := TextAttr;
  1666.   GotoXY(e.x,e.y);
  1667.   Write(e.prompt);
  1668.   TextAttr := FeltAttr;
  1669.   CASE e.ftype OF
  1670.     CharT : IF Refresh(e.len,e.just,e.CharP^) = 0 THEN ;
  1671.     StrT  : IF Refresh(e.len,e.just,e.StrP^) = 0 THEN ;
  1672.     BoolT,
  1673.     EnumT : IF Refresh(e.len,e.just,e.EnumStr^[e.EnumP^]) = 0 THEN ;
  1674.     ByteT : IF Refresh(e.len,e.just,Tstr(e.ByteP^,1)) = 0 THEN ;
  1675.     IntT  : IF Refresh(e.len,e.just,Tstr(e.IntP^,1)) = 0 THEN ;
  1676.     WordT : IF Refresh(e.len,e.just,Tstr(e.WordP^,1)) = 0 THEN ;
  1677.     LongT : IF Refresh(e.len,e.just,Tstr(e.LongP^,1)) = 0 THEN ;
  1678.   END;
  1679.   TextAttr := attr;
  1680. END;
  1681.  
  1682. PROCEDURE ShowAll;
  1683. VAR i : WORD;
  1684. BEGIN
  1685.   FOR i := 0 TO Eantall-1 DO
  1686.     ShowOne(ERec[i]^);
  1687. END;
  1688.  
  1689. FUNCTION EditStr(VAR str: String; VAR xpos: BYTE;
  1690.                  len, mode : BYTE; ok : CharSetPtr;just : JustType): BOOLEAN;
  1691. VAR sx, sy : BYTE;
  1692.     st : String;
  1693.     cok, ferdig, change, dirty : BOOLEAN;
  1694.  
  1695. PROCEDURE Del1; BEGIN Delete(st,xpos,1); Dirty := TRUE; END;
  1696.  
  1697. PROCEDURE RefreshStr;
  1698. BEGIN
  1699.   GotoXY(sx,sy);
  1700.   GotoXY(sx+xpos+Refresh(len,just,st)-1,sy);
  1701.   Dirty := FALSE;
  1702. END;
  1703.  
  1704. BEGIN
  1705.   EditStr := FALSE;
  1706.   sx := WhereX; sy := WhereY;
  1707.   st := str;
  1708.   dirty := TRUE;
  1709.   ferdig := FALSE;
  1710.   IF xpos > Length(str)+1 THEN xpos := 1;
  1711.   REPEAT
  1712.     IF len <= 1 THEN xpos := 1;
  1713.     {IF Dirty THEN }RefreshStr;
  1714.  
  1715.     EditChar := ReadKey;
  1716.     CASE EditChar OF
  1717.         #0 : BEGIN
  1718.                EditChar := ReadKey;
  1719.                CASE Ord(EditChar) OF
  1720.                  68 : BEGIN
  1721.                         st := str; RefreshStr; Exit;
  1722.                       END;
  1723.                  71 : BEGIN xpos := 1; END;
  1724.                  72,
  1725.                  80 : ferdig := TRUE;
  1726.                  75 : IF xpos > 1 THEN Dec(xpos);
  1727.                  77 : IF xpos <= Length(st) THEN Inc(xpos);
  1728.                  79 : BEGIN xpos := Length(st)+1; END;
  1729.                  82 : InsertMode := NOT InsertMode;
  1730.                  83 : Del1;
  1731.                 $75 : st[0] := Chr(xpos-1);    {Ctrl-End}
  1732.                  ELSE
  1733.                    Exit;
  1734.                END;
  1735.              END;
  1736.         ^H : IF xpos > 1 THEN BEGIN
  1737.                Dec(xpos);
  1738.                Del1;
  1739.              END;
  1740.         ^M : ferdig := TRUE;
  1741.         ^[ : BEGIN
  1742.                change := st <> str;
  1743.                IF change THEN BEGIN st := str; xpos := 1; END;
  1744.                RefreshStr;
  1745.                IF NOT change THEN Exit;
  1746.              END;
  1747.         #0..#255 :
  1748.          BEGIN
  1749.            IF mode AND ToUpper <> 0 THEN EditChar := UpCase(EditChar)
  1750.            ELSE IF mode AND ToLower <> 0 THEN EditChar := LoCase(EditChar);
  1751.  
  1752.            cok := mode AND NoInput = 0;
  1753.            IF (ok <> NIL) AND cok THEN cok := EditChar IN ok^;
  1754.  
  1755.            IF cok THEN BEGIN
  1756.              IF InsertMode THEN BEGIN
  1757.                IF Length(st) < len THEN BEGIN
  1758.                  Insert(EditChar,st,xpos);
  1759.                  Inc(xpos);
  1760.                END;
  1761.              END
  1762.              ELSE BEGIN
  1763.                IF xpos <= len THEN BEGIN
  1764.                  IF xpos > Length(st) THEN
  1765.                    st := st + EditChar
  1766.                  ELSE
  1767.                    st[xpos] := EditChar;
  1768.                  Inc(xpos);
  1769.                END;
  1770.              END;
  1771.              Dirty := TRUE;
  1772.            END;
  1773.          END;
  1774.     END;
  1775.   UNTIL ferdig;
  1776.   str := st;
  1777.   EditStr := TRUE;
  1778. END;
  1779.  
  1780. FUNCTION EditNum(VAR e : EditRecord): BOOLEAN;
  1781. VAR feil, sx, sy : WORD;
  1782.     st : String;
  1783.     num : LongInt;
  1784. BEGIN
  1785.   EditNum:= FALSE;
  1786.   sx := WhereX; sy := WhereY;
  1787.   CASE e.ftype OF
  1788.     ByteT : num := e.ByteP^;
  1789.     IntT  : num := e.IntP^;
  1790.     WordT : num := e.WordP^;
  1791.     LongT : num := e.LongP^;
  1792.   END;
  1793.  
  1794.   REPEAT
  1795.     GotoXY(sx,sy);
  1796.     Str(num:1,st);
  1797.     e.xpos := 1;
  1798.     IF NOT EditStr(st,e.xpos,e.len,0,Addr(NumericSet),e.just) THEN Exit;
  1799.     Val(st,num,feil);
  1800.     IF feil = 0 THEN BEGIN
  1801.       feil := 1;
  1802.       IF num < e.LongMin THEN
  1803.         num := e.LongMin
  1804.       ELSE IF num > e.LongMax THEN
  1805.         num := e.LongMax
  1806.       ELSE
  1807.         feil := 0;
  1808.     END;
  1809.   UNTIL feil = 0;
  1810.   EditNum := TRUE;
  1811.   CASE e.ftype OF
  1812.     ByteT : e.ByteP^ := num;
  1813.     IntT  : e.IntP^  := num;
  1814.     WordT : e.WordP^ := num;
  1815.     LongT : e.LongP^ := num;
  1816.   END;
  1817. END;
  1818.  
  1819. FUNCTION EditEnum(VAR en; max : WORD; len : BYTE; just : JustType;
  1820.          VAR enstr : FeltStrArray): BOOLEAN;
  1821. VAR e : BYTE ABSOLUTE en;
  1822.     b : BYTE;
  1823.     sx, sy : WORD;
  1824. BEGIN
  1825.   b := e;
  1826.   sx := WhereX; sy := WhereY;
  1827.   EditEnum := TRUE;
  1828.  
  1829.   REPEAT
  1830.     GotoXY(sx,sy);
  1831.     IF Refresh(len,just,enstr[b]) = 0 THEN ;
  1832.     GotoXY(sx,sy);
  1833.     EditChar := ReadKey;
  1834.     CASE EditChar OF
  1835.       #0 :
  1836.         BEGIN
  1837.           EditChar := ReadKey;
  1838.           CASE Ord(EditChar) OF
  1839.             68 : BEGIN EditEnum := FALSE; Exit; END;
  1840.             71 : b := 0;
  1841.             72,
  1842.             80 : BEGIN e := b; Exit; END;
  1843.             75 : b := Succ(b) MOD max;
  1844.             77 : b := Pred(b+max) MOD max;
  1845.             79 : b := max-1;
  1846.             ELSE BEGIN
  1847.               e := b;
  1848.               Exit;
  1849.             END;
  1850.           END;
  1851.         END;
  1852.       ^M : BEGIN e := b; Exit; END;
  1853.       ^[ : IF e <> b THEN b := e
  1854.            ELSE BEGIN EditEnum := FALSE; Exit; END;
  1855.       ' ': b := Succ(b) MOD max;
  1856.     END;
  1857.   UNTIL FALSE;
  1858. END;
  1859.  
  1860. PROCEDURE EditOne(VAR e : EditRecord);
  1861. VAR res : BOOLEAN;
  1862.     attr : BYTE;
  1863.     st : String;
  1864. BEGIN
  1865.   attr := TextAttr;
  1866.   WITH e DO BEGIN
  1867.     GotoXY(x,y); Write(prompt);
  1868.     TextAttr := EditAttr;
  1869.     CASE ftype OF
  1870.       CharT : BEGIN
  1871.                 st := CharP^;
  1872.                 res := EditStr(st,xpos,len,modeC,oksetC,just);
  1873.                 IF res AND (Length(st) = 1) THEN CharP^ := st[1];
  1874.               END;
  1875.       StrT  : res := EditStr(StrP^,xpos,len,modeS,oksetS,just);
  1876.       BoolT,
  1877.       EnumT : res := EditEnum(EnumP^,EnumAntall,len,just,EnumStr^);
  1878.       ByteT,
  1879.       IntT,
  1880.       WordT,
  1881.       LongT : res := EditNum(e);
  1882.     END;
  1883.   END;
  1884.   TextAttr := attr;
  1885.   ShowOne(e);
  1886. END;
  1887.  
  1888. PROCEDURE EditVar(VAR v);
  1889. VAR i : INTEGER;
  1890. BEGIN
  1891.   FOR i := 0 TO EAntall-1 DO BEGIN
  1892.     IF Addr(v) = Erec[i]^.StrP THEN EditOne(Erec[i]^);
  1893.     Inc(i);
  1894.   END;
  1895. END;
  1896.  
  1897. PROCEDURE EditARecord(n : WORD);
  1898. BEGIN
  1899.   IF n < Eantall THEN EditOne(Erec[n]^);
  1900. END;
  1901.  
  1902. PROCEDURE EditAllRecords;
  1903. BEGIN
  1904.   REPEAT
  1905.     EditARecord(LastRecord);
  1906.     Case EditChar OF
  1907.       #80 : LastRecord := Succ(LastRecord) MOD Eantall;
  1908.       #72 : LastRecord := Pred(LastRecord + Eantall) MOD Eantall;
  1909.     ELSE
  1910.       Exit;
  1911.     END;
  1912.   UNTIL EditChar = #27;
  1913. END;
  1914.  
  1915. END.
  1916. <<< fixattr.pas >>>
  1917. {$R-,S-}
  1918.  
  1919. Unit FixAttr;
  1920.  
  1921. Interface
  1922.  
  1923. Uses Crt;
  1924.  
  1925. Implementation
  1926.  
  1927. CONST Space : CHAR = ' ';
  1928.  
  1929. BEGIN
  1930. InLine(
  1931.    $B4/$03           {MOV    AH,03           }
  1932.   /$BB/$02/$00       {MOV    BX,0002         }
  1933.   /$CD/$10           {INT    10              }
  1934.   /$52               {PUSH   DX              }  {Save cursor pos}
  1935.   /$B4/$40           {MOV    AH,40           }
  1936.   /$B9/$01/$00       {MOV    CX,1            }
  1937.   /$BA/Space         {MOV    DX,OFFSET Space }
  1938.   /$CD/$21           {INT    21              }  {Write ' ' to stderr}
  1939.   /$B4/$02           {MOV    AH,02           }
  1940.   /$5A               {POP    DX              }
  1941.   /$CD/$10           {INT    10              }  {Restore cursor}
  1942.   /$B4/$08           {MOV    AH,08           }
  1943.   /$CD/$10           {INT    10              }  {Read DOS attr}
  1944.   /$88/$26/TextAttr);{MOV    [TextAttr],AH   }  {Update TextAttr}
  1945. END.
  1946. <<< kermit.inc >>>
  1947. (******************* KERMIT.INC ************************)
  1948.  
  1949. CONST
  1950.   MaxY = 25;
  1951.   LenModulo = 95;
  1952.  
  1953. CONST
  1954.  
  1955.     ErrorLevel : WORD = 0;
  1956.     SendDelay : WORD = 0;
  1957.     FileNameSet : SET OF CHAR =
  1958.       ['!','#'..')','-','.','0'..':','@'..'Z','\','^'..'z','~'];
  1959.  
  1960. VAR
  1961.     InnConvert, UtConvert : ARRAY [CHAR] OF CHAR;
  1962.  
  1963. VAR t2, MaxServer : TimerTableRec;           {Br vre global!}
  1964.     DTA : SearchRec;
  1965.     FTime : DateTime;
  1966.     MaxPrTick : WORD;
  1967.  
  1968. CONST
  1969.     KermitBufSize : WORD = $F000;
  1970.  
  1971. CONST
  1972.   Qrep        : BOOLEAN = TRUE;
  1973.   Q8Bit       : BOOLEAN = TRUE;
  1974.   ServerTimeOut : BOOLEAN = FALSE;
  1975.   RetryLimit  : BYTE =  10;
  1976.   YourTimeOut : BYTE =  15;
  1977.   SendTimeOut : BYTE =   5;
  1978.   MyPad      : BYTE = 0;
  1979.   MyPadChar  : CHAR = ^@;
  1980.   YourPad    : BYTE = 0;
  1981.   YourPadChar: CHAR = ^@;
  1982.  
  1983. TYPE
  1984.   CharArray = ARRAY [1..9040] OF CHAR;
  1985.   CarNum    = 0..222;
  1986.   IBM_Type  = 0..2;
  1987.   UnCarCh   = ' '..#254;
  1988.   PakkeCh   = '@'..'Z';
  1989.   PakkeType = RECORD
  1990.     TotLen: WORD;
  1991.     long  : BOOLEAN;
  1992.     plen  : CHAR;
  1993.     pnr   : UnCarCh;
  1994.     ptype : PakkeCh;
  1995.     CASE BOOLEAN OF
  1996.        TRUE : (plen1,
  1997.               plen2,
  1998.               hchk  : CHAR);
  1999.       FALSE : (pdata : CharArray);
  2000.     END;
  2001.   PakkeTypePtr = ^PakkeType;
  2002.  
  2003. TYPE
  2004.   PacketWindow = RECORD
  2005.     retry : WORD;
  2006.     dptr  : ^PakkeType;
  2007.     CASE BYTE OF
  2008.       0 : (acked, nacked : BOOLEAN);
  2009.       1 : (acknack : WORD);
  2010.     END;
  2011.  
  2012.   FilBuffer = ARRAY [0..$F000] OF CHAR;
  2013.   BufferPtr = ^FilBuffer;
  2014.  
  2015. VAR
  2016.   nr, i, n, ninn, nut : WORD;
  2017.   pw : ARRAY [0..63] OF PacketWindow;
  2018.   LongReply, DiskError : BOOLEAN;
  2019.  
  2020.   StopFile, AttrPakke : BOOLEAN;
  2021.   fil   : FILE;
  2022.   YourMaxLength, RetryNr, LastNr, PakkeNr,
  2023.   CheckType, FeilNr, PacketDelay : WORD;
  2024.   BufSize, BufCount, MaxRep : WORD;
  2025.   Bytes : LongInt;
  2026.   buffer : BufferPtr;
  2027.   BufPtr : ^CHAR;
  2028.   FileMax, TotalNr : LongInt;
  2029.   ShowTimeOut, EndOfFile : BOOLEAN;
  2030.   OriginalName, FileName, ErrorString, DownLoadPath,
  2031.   StatusString : String[80];
  2032.   RX_Pac, TX_Pac, Next_Pac : PakkeTypePtr;
  2033.   Next_Data_OK : BOOLEAN;
  2034.   RepQ, Bit8Q : CHAR;
  2035.   st : String;
  2036.  
  2037. TYPE DupHandleType = (RenameFile, OverWriteFile, SkipFile);
  2038.  
  2039. (**********************************************************************)
  2040. (*         Here are all variables that can be stored on disk:         *)
  2041. (**********************************************************************)
  2042.  
  2043. CONST
  2044.   Versjon : String[4] = 'V0.1';
  2045.  
  2046.   DupHandle : DupHandleType = RenameFile;
  2047.   OldDupHandle : DupHandleType = SkipFile;
  2048.   NewDupHandle : DupHandleType = OverWriteFile;
  2049.  
  2050.   CurBaud      : LongInt       =115200;
  2051.   CurBits      : WORD          =     8;
  2052.   CurStop      : WORD          =     1;
  2053.   CurParity    : ParityType    = No_Parity;
  2054.   CurComPort   : WORD          =     1;
  2055.  
  2056.   LongMaxLength: WORD          =  9020;
  2057.   WinSize      : WORD          =    31;
  2058.   MyTimeOut    : WORD          =    12;
  2059.   ServerTime   : WORD          =     0;
  2060.  
  2061.   LongPakke    : BOOLEAN       =  TRUE;
  2062.   WindowData   : BOOLEAN       = FALSE;
  2063.   TextFile     : BOOLEAN       = FALSE;
  2064.   IBM_Mode     : IBM_Type      =     0;
  2065.   BinaryData   : BOOLEAN       =  TRUE;
  2066.   FileCheck    : BYTE          =     2;
  2067.  
  2068.   MySOH        : CHAR          =    #1;
  2069.   YourSOH      : CHAR          =    #1;
  2070.   MyCR         : CHAR          =   #13;
  2071.   YourCR       : CHAR          =   #13;
  2072.   MyQCtrlChar  : CHAR          =   '#';
  2073.   YourQCtrlChar: CHAR          =   '#';
  2074.   Q8bitChar    : CHAR          =   '&';
  2075.   QrepChar     : CHAR          =   '~';
  2076.  
  2077.   KermitAttr   : BYTE          =     0;
  2078.   MenuAttr     : BYTE          =     0;
  2079.   FieldAttr    : BYTE          =     0;
  2080.   SaveEdit     : BYTE          =     0;
  2081.   DirVideo     : BOOLEAN       =  TRUE;
  2082.  
  2083.   Marker_Byte  : BYTE          =     0;
  2084.  
  2085. (**********************************************************************)
  2086. (*             Slutt p setup-variable!                               *)
  2087. (**********************************************************************)
  2088.  
  2089.   DupString : ARRAY[DupHandleType] OF FeltStr =
  2090.        ('Rename','OverWrite','Skip');
  2091.   BinText : ARRAY [BOOLEAN] OF FeltStr = ('BIN','TEXT');
  2092.   Std_IBM : ARRAY [IBM_Type] OF FeltStr = ('Std','I-E','IBM');
  2093.   ParityStr : ARRAY [ParityType] OF FeltStr =
  2094.       ('NONE','EVEN','ODD','MARK','SPACE');
  2095.  
  2096. PROCEDURE SplitFileName(fn : String; VAR drive,path,name,ext : String);
  2097. VAR e : WORD;
  2098. BEGIN
  2099.   e := Pos(':',fn);
  2100.   drive := '';
  2101.   IF e > 0 THEN BEGIN
  2102.     IF e = 2 THEN drive := Copy(fn,1,2);
  2103.     Delete(fn,1,e);
  2104.   END;
  2105.  
  2106.   e := Length(fn);
  2107.   ext := '';
  2108.   WHILE (e > 0) AND (fn[e] <> '.') AND (fn[e] <> '\') DO Dec(e);
  2109.   IF (e > 0) AND (fn[e] = '.') THEN BEGIN
  2110.     ext := Copy(fn,e,4);
  2111.     fn[0] := Chr(e-1);
  2112.   END;
  2113.  
  2114.   e := Length(fn);
  2115.   path := '';
  2116.   WHILE (e > 0) AND (fn[e] <> '\') DO Dec(e);
  2117.   IF e > 0 THEN path := Copy(fn,1,e);
  2118.   name := Copy(fn,e+1,8);
  2119. END;
  2120.  
  2121. FUNCTION Exist(fn : String): BOOLEAN;
  2122. VAR f : FILE;
  2123.     at : WORD;
  2124. BEGIN
  2125.   Assign(f,fn);
  2126.   GetFAttr(f,at);
  2127.   Exist := DosError = 0;
  2128. END;
  2129.  
  2130. PROCEDURE MoveW(VAR fra, til; len : WORD); BEGIN Move(fra,til,len*2); END;
  2131.  
  2132. PROCEDURE Bell; BEGIN Sound(1000); Delay(150); NoSound; END;
  2133.  
  2134. PROCEDURE ByteToDigits(by : BYTE; VAR buf);
  2135. VAR b : ARRAY [1..2] OF BYTE ABSOLUTE buf;
  2136. BEGIN
  2137.   b[1] := by DIV 10 + 48;
  2138.   b[2] := by MOD 10 + 48;
  2139. END;
  2140.  
  2141. FUNCTION Pad(st : String; len : INTEGER): String;
  2142. BEGIN
  2143.   WHILE len > Length(st) DO st := st + ' ';
  2144.   Pad := st;
  2145. END;
  2146.  
  2147. PROCEDURE SetCursor(mode : WORD);
  2148. BEGIN
  2149. Inline(
  2150.   $B4/$01                {mov ah,1}
  2151.   /$8B/$4E/<MODE         {mov cx,[bp<mode]}
  2152.   /$CD/$10               {int $10}
  2153. );
  2154. END;
  2155.  
  2156. PROCEDURE CursorOn;
  2157. BEGIN
  2158.   IF LastMode = 7 THEN
  2159.     SetCursor($C0D)
  2160.   ELSE
  2161.     SetCursor($607);
  2162. END;
  2163.  
  2164. PROCEDURE CursorOff;
  2165. BEGIN
  2166.   SetCursor($2000);
  2167. END;
  2168.  
  2169. PROCEDURE ClrAll; BEGIN ClrScr; END;
  2170.  
  2171. PROCEDURE ClrLast; BEGIN GotoXY(1,25); ClrEol; END;
  2172.  
  2173. PROCEDURE WriteStr(st : String);
  2174. BEGIN
  2175.   Write(st);
  2176. END;
  2177.  
  2178. PROCEDURE Error(msg : String);
  2179. BEGIN
  2180.   ClrLast;
  2181.   Write(msg,'  Hit Esc!');
  2182.   CursorOn;
  2183.   REPEAT UNTIL ReadKey = #27;
  2184.   CursorOff;
  2185.   ClrLast;
  2186. END;
  2187.  
  2188. (*
  2189. PROCEDURE ReadString(help : INTEGER; prompt : String; len : BYTE;
  2190.                      VAR st : String; VAR ok : BOOLEAN);
  2191. VAR xpos : BYTE;
  2192. BEGIN
  2193.   WriteStr(prompt);
  2194.   st := '';
  2195.   xpos := 1;
  2196.   ok := EditStr(st,xpos,len,0,NIL,LeftJ);
  2197. END;
  2198.  
  2199. PROCEDURE ReadString(help : INTEGER; prompt : String;
  2200.           maxlen : INTEGER;VAR st : String;VAR ok: BOOLEAN);
  2201. VAR key : WORD;
  2202.     ch : CHAR;
  2203. BEGIN
  2204.   ClrLast;
  2205.   CursorOn;
  2206.   Write(prompt);
  2207.   st := '';
  2208.   REPEAT
  2209.     key := ReadKey;
  2210.     IF key = $4400 THEN BEGIN
  2211.       st := '';
  2212.       ok := FALSE;
  2213.       CursorOff;
  2214.       Exit;
  2215.     END
  2216.     ELSE IF Lo(key) <> 0 THEN BEGIN
  2217.       ch := Chr(Lo(key));
  2218.       CASE ch OF
  2219.         ^H : IF Length(st) > 0 THEN BEGIN
  2220.                Dec(st[0]);
  2221.                Write(^H' '^H);
  2222.              END;
  2223.         ^M : BEGIN
  2224.                ok := TRUE;
  2225.                CursorOff;
  2226.                Exit;
  2227.              END;
  2228.         ELSE IF Length(st) < MaxLen THEN BEGIN
  2229.           st := st + ch;
  2230.           Write(ch);
  2231.         END;
  2232.       END;
  2233.     END;
  2234.   UNTIL FALSE;
  2235. END;
  2236.  
  2237. PROCEDURE ReadNum(help : INTEGER;prompt : String;min, max : WORD;
  2238.                   VAR svar : WORD);
  2239. VAR st : String;
  2240.     n, feil : INTEGER;
  2241.     ok : BOOLEAN;
  2242. BEGIN
  2243.   REPEAT
  2244.     ClrLast;
  2245.     ReadString(help,prompt,10,st,ok);
  2246.     IF st = '' THEN Exit;
  2247.     Val(st,n,feil);
  2248.   UNTIL (feil = 0) AND (n >= min) AND (n <= max);
  2249.   svar := n;
  2250. END;
  2251. *)
  2252.  
  2253. PROCEDURE ReadFileName(prompt : String; VAR fil : String);
  2254. VAR e : EditRecord;
  2255.     ok : CharSet;
  2256. BEGIN
  2257.   fil := '';
  2258.   ok := FileNameSet + ['*','?'];
  2259.  
  2260.   e.x := 1; e.y := 25; e.len := 53; e.prompt := prompt;
  2261.   e.ftype := StrT; e.xpos := 1; e.just := LeftJ;
  2262.   e.StrP := Addr(fil);
  2263.   e.okSetS := Addr(ok);
  2264.   e.ModeS := ToUpper;
  2265.   CursorOn;
  2266.   REPEAT
  2267.     EditOne(e);
  2268.   UNTIL EditChar IN [^M,#68,^[];
  2269.   CursorOff;
  2270. END;
  2271.  
  2272. FUNCTION Tstr(n, len : WORD): String;
  2273. VAR st : STRING[20];
  2274. BEGIN
  2275.   Str(n:len,st);
  2276.   Tstr := st;
  2277. END;
  2278.  
  2279. PROCEDURE StartTimerSek(VAR t : TimerTableRec; sek : WORD);
  2280. BEGIN
  2281.   t.count := sek *18;
  2282.   t.UserInt := FALSE;
  2283.   StartTimer(t);
  2284. END;
  2285.  
  2286. PROCEDURE BIOSKbd(help : INTEGER; expand : BOOLEAN; VAR ch : CHAR;
  2287.                   VAR scan : INTEGER);
  2288. BEGIN
  2289.   ch := ReadKey;
  2290.   IF ch = #0 THEN scan := Ord(ReadKey) ELSE scan := 2;
  2291. END;
  2292.  
  2293. FUNCTION KeyPress : BOOLEAN;
  2294. BEGIN
  2295.   KeyPress := KeyPressed;
  2296. END;
  2297.  
  2298. PROCEDURE ScrollWin(x0,y0,x1,y1,lines,attr : INTEGER);
  2299. VAR sx, sy : WORD;
  2300. BEGIN
  2301.   sx := WhereX; sy := WhereY;
  2302.   Window(x0,y0,x1,y1);
  2303.   GotoXY(1,1);
  2304.   IF lines = 0 THEN ClrScr
  2305.   ELSE IF lines > 0 THEN DelLine
  2306.   ELSE InsLine;
  2307.   Window(1,1,80,25);
  2308.   GotoXY(sx,sy);
  2309. END;
  2310.  
  2311. PROCEDURE GetF10;
  2312. BEGIN
  2313.   IF TotalBytes = 0 THEN Exit;
  2314.   ClrLast;
  2315.   WriteStr('File transfer completed!  Hit any key to continue ... ');
  2316.   IF ReadKey = #0 THEN IF ReadKey = #0 THEN;
  2317. END;
  2318.  
  2319. PROCEDURE UpperStr(VAR st : String);
  2320. VAR i : INTEGER;
  2321. BEGIN
  2322.   FOR i := 1 TO Length(st) DO st[i] := UpCase(st[i]);
  2323. END;
  2324.  
  2325. CONST MaxArgC = 2;
  2326.       MaxOptC = 1;
  2327.  
  2328. VAR InitFileName : STRING[80];
  2329.     ArgV : ARRAY [1..2] OF String[64];
  2330.     ArgC, OptC : BYTE;
  2331.     OptV : ARRAY [1..1] OF String[64];
  2332.  
  2333. PROCEDURE ParseCmd;
  2334. VAR i : INTEGER;
  2335.     st : String;
  2336. BEGIN
  2337.   ArgC := 0;
  2338.   OptC := 0;
  2339.   FOR i := 1 TO ParamCount DO BEGIN
  2340.     st := ParamStr(i);
  2341.     UpperStr(st);
  2342.     IF st[1] = '/' THEN BEGIN
  2343.       Inc(OptC);
  2344.       OptV[OptC] := st;
  2345.     END
  2346.     ELSE BEGIN
  2347.       Inc(ArgC);
  2348.       ArgV[ArgC] := st;
  2349.     END;
  2350.   END;
  2351. END;
  2352.  
  2353. PROCEDURE GetInitFileName;
  2354. VAR env_ptr : ^WORD;
  2355.     i : INTEGER;
  2356.     drive, path, name, ext, od, op, on, oe : String[80];
  2357. BEGIN
  2358.   ParseCmd;
  2359.   IF Hi(DosVersion) >= 3 THEN BEGIN
  2360.     env_ptr := Ptr(MemW[PrefixSeg:$2C],0);
  2361.     WHILE env_ptr^ <> 0 DO Inc(Word(env_ptr));
  2362.     Inc(Word(env_ptr),4);
  2363.     InitFileName := '';
  2364.     REPEAT
  2365.       InitFileName := InitFileName + CHAR(env_ptr^);
  2366.       Inc(Word(env_ptr));
  2367.     UNTIL CHAR(env_ptr^) = #0;
  2368.   END
  2369.   ELSE
  2370.     InitFileName := 'KERMIT';
  2371.  
  2372.   SplitFileName(InitFileName,drive,path,name,ext);
  2373.   ext := '.INI';
  2374.  
  2375.   IF (OptC >= 1) AND (Copy(OptV[1],1,3) = '/I=') THEN BEGIN
  2376.     SplitFileName(Copy(OptV[1],4,80),od,op,on,oe);
  2377.     IF (od <> '') OR (op <> '') THEN BEGIN
  2378.       drive := od;
  2379.       path := op;
  2380.     END;
  2381.     IF on <> '' THEN name := on;
  2382.     IF oe <> '' THEN ext := oe;
  2383.   END;
  2384.   InitFileName := drive+path+name+ext;
  2385. END;                                   {GetInitFileName}
  2386.  
  2387. PROCEDURE SaveParam;
  2388. VAR f : FILE;
  2389. BEGIN
  2390.   Assign(f,InitFileName);
  2391.   ReWrite(f,1);
  2392.   BlockWrite(f,Versjon,Ofs(Marker_Byte)-Ofs(Versjon));
  2393.   Close(f);
  2394.   IF IOresult <> 0 THEN Error('Save error!');
  2395. END;
  2396.  
  2397. FUNCTION GetParam : BOOLEAN;
  2398. VAR f : FILE;
  2399.     v : String[4];
  2400.     bytes : WORD;
  2401.     ok : BOOLEAN;
  2402. BEGIN
  2403.   GetParam := FALSE;
  2404.   GetInitFileName;
  2405.  
  2406.   IF Exist(InitFileName) THEN BEGIN
  2407.     Assign(f,InitFileName);
  2408.     Reset(f,1);
  2409.     v := '';
  2410.     BlockRead(f,v,SizeOf(v));
  2411.     bytes := Ofs(Marker_Byte)-Ofs(Versjon);
  2412.     ok := FALSE;
  2413.     IF (v <> Versjon) OR (FileSize(f) <> bytes) THEN Exit;
  2414.     Seek(f,0);
  2415.     BlockRead(f,Versjon,bytes);
  2416.     ok := IOresult = 0;
  2417.     Close(f);
  2418.     IF NOT ok OR (IOresult <> 0) THEN BEGIN
  2419.       Error('Get .INI error!');
  2420.       Exit;
  2421.     END;
  2422.     IF KermitAttr <> 0 THEN TextAttr := KermitAttr;
  2423.     IF SaveEdit <> 0 THEN EditAttr := SaveEdit;
  2424.   END;
  2425.   GetParam := TRUE;
  2426. END;
  2427.  
  2428. PROCEDURE StartLink;
  2429. BEGIN
  2430.   IF NOT DiskStopInt OR BinaryData THEN Exit;
  2431.   RS_Enable(CurComPort);
  2432.   RS_WriteFirst(^Q,CurComPort);
  2433. END;
  2434.  
  2435. PROCEDURE StopLink;
  2436. BEGIN
  2437.   IF DiskStopInt AND NOT BinaryData THEN RS_WriteFirst(^S,CurComPort);
  2438. END;
  2439.  
  2440. (******************** Statistics **********************)
  2441.  
  2442. FUNCTION DOS_Time : LongInt;
  2443. VAR h, m, s, s100 : WORD;
  2444. BEGIN
  2445.   GetTime(h,m,s,s100);
  2446.   DOS_Time := h * 36000 + m * 600 + s * 10 + (s100+5) DIV 10;
  2447. END;
  2448.  
  2449. PROCEDURE InitStat;
  2450. BEGIN
  2451.   TotalTime := DOS_Time; TotalBytes := 0; SendBytes := 0; ReceiveBytes := 0;
  2452.   FileNr := 0;
  2453. END;
  2454.  
  2455. PROCEDURE ShowStat;
  2456. VAR ch : CHAR;
  2457.     t  : REAL;
  2458. BEGIN
  2459.   IF TotalBytes+SendBytes+ReceiveBytes > 0 THEN BEGIN
  2460.     TotalTime := DOS_Time - TotalTime;
  2461.     Window(22,5,80,10);
  2462.     ClrScr;
  2463.     WriteLn('      Total bytes: ',TotalBytes);
  2464.     WriteLn('      Total files: ',FileNr);
  2465.     WriteLn('       Bytes sent: ',SendBytes);
  2466.     WriteLn('   Bytes received: ',ReceiveBytes);
  2467.     WriteLn('       Total time: ',TotalTime DIV 10,'.',TotalTime MOD 10);
  2468.     Write  ('   Effective Baud: ',TotalBytes * 100 DIV TotalTime);
  2469.     Window(1,1,80,25);
  2470.   END;
  2471. END;
  2472.  
  2473. TYPE
  2474.   KeyType = 0..40;
  2475.   KeySet = SET OF KeyType;
  2476.  
  2477. VAR OrigText, OrigMenu, OrigField, OrigEdit : BYTE;
  2478.  
  2479. PROCEDURE Init_Params;
  2480. VAR ok : BOOLEAN;
  2481.     temp : LongInt;
  2482. BEGIN
  2483.   RS_Init(CurBaud,CurBits,CurStop,CurParity,ok,CurComPort);
  2484.  
  2485.   temp := 115200 DIV ((115200 + (CurBaud Shr 1)) DIV CurBaud);
  2486.   IF temp <> CurBaud THEN BEGIN CurBaud := temp; ok := FALSE; END;
  2487.  
  2488.   MaxPrTick := CurBaud DIV 250;
  2489.  
  2490.   IF CurBaud > 30000 THEN BEGIN
  2491.     DiskStopInt := TRUE;
  2492.     WindowData := FALSE;
  2493.     RS_Buffer[CurComPort].AutoXoff := FALSE;
  2494.   END;
  2495.  
  2496.   IF IBM_Mode > 0 THEN BEGIN
  2497.     MySOH := '%';
  2498.     YourSOH := '%';
  2499.     BinaryData := FALSE;
  2500.   END;
  2501.  
  2502.   IF BinaryData THEN BEGIN
  2503.     CurBits := 8;
  2504.     CurParity := No_Parity;
  2505.     RS_Buffer[CurComPort].AutoXoff := FALSE;
  2506.   END;
  2507. {
  2508.     IF (CurBaud <= 2400) AND WindowData THEN
  2509.       RS_Start(RX_Int+TX_Int+RLS_int,CurComPort)
  2510.     ELSE
  2511. }
  2512.     RS_Start(RX_Int+RLS_int,CurComPort);
  2513.  
  2514.   YourQCtrlChar := MyQCtrlChar;
  2515.   YourSOH := MySOH;
  2516.   YourCR := MyCR;
  2517. END;
  2518.  
  2519. PROCEDURE Meny(VAR k : KeyType);
  2520. VAR
  2521.   temp : LongInt;
  2522.   st, keyset : String;
  2523.   ch : CHAR;
  2524.   OldPath : String[64];
  2525.   OldMenu, OldAttr : BYTE;
  2526.   dta : SearchRec;
  2527.  
  2528. PROCEDURE ShowMeny;
  2529. BEGIN
  2530.   IF MenuAttr = 0 THEN MenuAttr := OrigMenu;
  2531.   IF FieldAttr = 0 THEN FieldAttr := OrigField;
  2532.   FeltAttr := FieldAttr;
  2533.   IF KermitAttr = 0 THEN KermitAttr := OrigText;
  2534.   TextAttr := KermitAttr;
  2535.   IF SaveEdit = 0 THEN SaveEdit := OrigEdit;
  2536.   EditAttr := SaveEdit;
  2537.  
  2538.   ClrScr;
  2539.  
  2540.   GotoXY(22,3); Write(CpRt);
  2541.  
  2542.   GotoXY(34,14); WriteStr('Duplicate File Names');
  2543.  
  2544.   OldAttr := TextAttr;
  2545.   TextAttr := MenuAttr;
  2546.   GotoXY(1,25);
  2547.   WriteStr('F1-Send F2-Receive F3-Get F4-Server F5-Save   F7-DOS F8-Term F9-Logout F10-Exit');
  2548.   TextAttr := OldAttr;
  2549.   OldMenu := MenuAttr;
  2550. END;
  2551.  
  2552. BEGIN
  2553.   ShowMeny;
  2554.   CursorOn;
  2555.  
  2556.   REPEAT
  2557.  
  2558.     OldPath := DownLoadPath; OldAttr := KermitAttr;
  2559.  
  2560.     RS_Stop(CurComPort);
  2561.  
  2562.     ShowAll;
  2563.     EditAllRecords;                    {EditChar inneholder siste tast}
  2564.  
  2565.     IF (KermitAttr <> OldAttr) OR (FieldAttr <> FeltAttr) OR
  2566.        (MenuAttr <> OldMenu) THEN BEGIN
  2567.       ShowMeny;
  2568.       ShowAll;
  2569.     END;
  2570.     SaveEdit := EditAttr;
  2571.  
  2572.     Init_Params;
  2573.  
  2574.     IF DownLoadPath <> OldPath THEN BEGIN
  2575.       ChDir(DownLoadPath);
  2576.       IF IOresult = 0 THEN
  2577.         GetDir(0,DownLoadPath)
  2578.       ELSE BEGIN
  2579.         DownLoadPath := OldPath;
  2580.         ShowAll;
  2581.       END;
  2582.     END;
  2583.     DirectVideo := DirVideo;
  2584.   UNTIL EditChar IN [#59..#68];
  2585.   CursorOff;
  2586.  
  2587.   k := Ord(EditChar) - 58;
  2588. END;                                   {Meny}
  2589.  
  2590. <<< kermit.pas >>>
  2591. {$R-,S-,I-,D+,T+,F-,V-,B-,N-}
  2592. { $R+,S+,I-,D+,T+,F-,V-,B-,N-}
  2593. {$M $2000,$9000,$18000}     {8k STACK, 36k-96k HEAP}
  2594.  
  2595. PROGRAM Kermits;
  2596.  
  2597. Uses MyDos, Crt, Timers, {Keyboard, }Async, Crcs, FeltEdit, FixAttr;
  2598.  
  2599. CONST
  2600.   CpRt : String[40] = 'KERMIT file transfer. V1.1a TMa, NH 1988';
  2601.  
  2602.   DiskStopInt : BOOLEAN = FALSE;
  2603.  
  2604. (**********************************************************************)
  2605. (*                                                                    *)
  2606. (*                   Start for Kermits egne procedures                *)
  2607. (*                                                                    *)
  2608. (**********************************************************************)
  2609.  
  2610. VAR TotalTime, TotalBytes, SendBytes, ReceiveBytes : LongInt;
  2611.     FileNr : WORD;
  2612.  
  2613. {$I KERMIT.INC}            {Kermit const, type, var and some proc's.}
  2614.  
  2615. PROCEDURE InitWindow;
  2616. VAR i : WORD;
  2617.     p : Pointer;
  2618. BEGIN
  2619.   FillChar(pw,SizeOf(pw),#0);
  2620.   ninn := PakkeNr; nut := PakkeNr;
  2621.   p := Next_Pac;
  2622.  
  2623.   FOR i := 0 TO 31 DO BEGIN
  2624.     pw[i].dptr    := p;
  2625.     pw[i+32].dptr := p;
  2626.     Inc(Word(p),108);                  {Room for 95 char + fudge factor}
  2627.   END;
  2628.   GotoXY(33,10); WriteStr('Window:');
  2629.   LongPakke := FALSE;
  2630. END;                                   { InitWindow }
  2631.  
  2632. PROCEDURE Warning(msg : String);
  2633. BEGIN
  2634.   ScrollWin(41,14,80,24,-1,KermitAttr);
  2635.   GotoXY(27,14); WriteStr('Last warning: '+msg);
  2636. END;
  2637.  
  2638. TYPE Retry_Code = (r_ok, r_keyboard, r_timeout, r_exit);
  2639. VAR r_code : Retry_Code;
  2640.  
  2641. FUNCTION Retry : Retry_Code;
  2642. VAR ch : CHAR;
  2643.     code : INTEGER;
  2644.     enable : BOOLEAN;
  2645. BEGIN
  2646.   r_code := r_ok;
  2647.   enable := FALSE;
  2648.   IF KeyPress THEN BEGIN
  2649.     BIOSKbd(-1,FALSE,ch,code);
  2650.     IF (ch = #0) THEN
  2651.       CASE code OF
  2652.         45 : enable := TRUE;
  2653.         59 : StopFile := TRUE;
  2654.         67 : BEGIN
  2655.                r_code := r_keyboard;
  2656.                   enable := TRUE;
  2657.              END;
  2658.         68 : r_code := r_exit;
  2659.       END;
  2660.   END
  2661.   ELSE IF NOT RunningTimer(t2) THEN BEGIN
  2662.     r_code := r_timeout;
  2663.     enable := TRUE;
  2664.   END;
  2665.   IF enable THEN BEGIN
  2666.     RS_Enable(CurComPort);
  2667.     StartLink;
  2668.   END;
  2669.   Retry := r_code;
  2670. END;                                   {Retry}
  2671.  
  2672. PROCEDURE SendLink(VAR buf; n : WORD);
  2673. LABEL Ferdig;
  2674. VAR d : CharArray ABSOLUTE buf;
  2675.     i, len : WORD;
  2676.     ok : BOOLEAN;
  2677.     ch : CHAR;
  2678.     dptr : ^CHAR;
  2679. BEGIN
  2680.   Inc(SendBytes,n+2);
  2681.   i := 10;
  2682.   IF SendTimeOut > 0 THEN
  2683.     i := SendTimeOut;
  2684.   StartTimerSek(t2,i);
  2685.   IF NOT WindowData THEN BEGIN
  2686.     WHILE (RS_Buffer[CurComPort].HostXoff OR
  2687.         NOT RS_Empty(CurComPort)) DO BEGIN
  2688.       RS_ClrBuffer(CurComPort);
  2689.       IF Retry <> r_ok THEN GOTO Ferdig;
  2690.     END;
  2691.     Delay(PacketDelay);             { Wait if neccessary! }
  2692.   END;
  2693.   REPEAT
  2694.     IF Retry <> r_ok THEN GOTO Ferdig;
  2695.     RS_Write(YourSOH,ok,CurComPort);
  2696.   UNTIL ok;
  2697.   IF CurBaud > 30000 THEN Delay(1);
  2698.  
  2699.   IF IBM_Mode = 1 THEN BEGIN
  2700.     REPEAT
  2701.       RS_BusyRead(ch,ok,CurComPort);
  2702.       IF NOT ok THEN
  2703.         IF Retry <> r_ok THEN GOTO Ferdig;
  2704.     UNTIL ok AND (ch = YourSOH);
  2705.     len := 1;
  2706.     i := 1;
  2707.     REPEAT
  2708.       IF len <= n THEN BEGIN
  2709.         RS_Write(d[len],ok,CurComPort);
  2710.         IF ok THEN BEGIN
  2711.           Inc(len);
  2712.           Delay(SendDelay);
  2713.         END;
  2714.       END;
  2715.       REPEAT
  2716.         RS_BusyRead(ch,ok,CurComPort);
  2717.         IF ok THEN BEGIN
  2718.           IF (d[i] = ch) OR (d[i] = ' ') THEN
  2719.             Inc(i);
  2720.         END
  2721.         ELSE
  2722.           IF Retry <> r_ok THEN GOTO Ferdig;
  2723.       UNTIL (len - i < 40) AND NOT ok;
  2724.     UNTIL (len > n) AND (i > n);
  2725.   END
  2726.   ELSE BEGIN
  2727.     dptr := Addr(d[1]);
  2728.  
  2729.     IF CurBaud > 30000 THEN BEGIN
  2730.       len := MaxPrTick;
  2731.       REPEAT
  2732.         IF len > n THEN len := n;
  2733.         RS_WriteBlock(dptr^,len,i,CurComPort);
  2734.         Dec(n,len);
  2735.         Inc(Word(dptr),len);
  2736.         Delay(1);
  2737.       UNTIL n = 0;
  2738.     END
  2739.     ELSE BEGIN
  2740.       REPEAT
  2741.         RS_WriteBlock(dptr^,n,i,CurComPort);
  2742.         IF Retry <> r_ok THEN GOTO Ferdig;
  2743.         Dec(n,i);
  2744.         Inc(Word(dptr),len);
  2745.       UNTIL n = 0;
  2746.     END;
  2747.   END;
  2748.  
  2749.   REPEAT
  2750.     RS_Write(YourCR,ok,CurComPort);
  2751.   UNTIL ok OR (Retry <> r_ok);
  2752.  
  2753. Ferdig:
  2754.  
  2755. END;                         { SendLink }
  2756.  
  2757. PROCEDURE GetLink(VAR buf; VAR n : WORD; max : WORD);
  2758. LABEL Ferdig, Restart_Packet;
  2759. VAR d : ARRAY [0..4000] OF CHAR ABSOLUTE buf;
  2760.     bytes, i, x : WORD;
  2761.     ch : CHAR;
  2762.     done : BOOLEAN;
  2763.     escape : STRING[10];
  2764. BEGIN
  2765.   StartTimerSek(t2,YourTimeOut);
  2766.   ch := ' ';
  2767.  
  2768.   REPEAT
  2769.     RS_BusyRead(ch,done,CurComPort);
  2770.     IF NOT done THEN
  2771.       IF Retry <> r_ok THEN GOTO Ferdig;
  2772.     Inc(ReceiveBytes,Ord(done));
  2773.   UNTIL (ch=MySOH);
  2774.  
  2775.   x := 3;
  2776.  
  2777. Restart_Packet:
  2778.   n := 0;
  2779.  
  2780.   d[0] := '~';                   { len   = 94 }
  2781.   d[3] := Chr(LenModulo+31);     { plen1 = 94/63 }
  2782.   d[4] := Chr(LenModulo+31);     { plen2 = 94/63 }
  2783.  
  2784.   REPEAT
  2785.     RS_ReadBlock(d[n],max - n,bytes,CurComPort);
  2786.     Inc(ReceiveBytes,bytes);
  2787.     IF bytes=0 THEN BEGIN
  2788.       IF d[0] > ' ' THEN BEGIN
  2789.         IF n > Ord(d[0]) - 32 THEN GOTO Ferdig;
  2790.       END
  2791.       ELSE
  2792.         IF n > (Ord(d[3]) - 32) * LenModulo + Ord(d[4]) - 32 THEN GOTO Ferdig;
  2793.       IF Retry <> r_ok THEN GOTO Ferdig;
  2794. {      Write_String(d[0],1,1,Byte_Stay,n,KermitAttr);    }
  2795.     END
  2796.     ELSE IF NOT BinaryData AND (d[n] < ' ') THEN BEGIN
  2797.       IF d[n] = MyCR THEN GOTO Ferdig;
  2798.       IF d[n] = MySOH THEN BEGIN
  2799.         GOTO Restart_Packet;
  2800.       END;
  2801.       IF (d[n] = ^[) AND (IBM_Mode > 0) THEN BEGIN
  2802.         escape[0] := #0;
  2803.         REPEAT                         { Read an Escape Seq's }
  2804.           RS_BusyRead(ch,done,CurComPort);
  2805.           IF NOT done THEN BEGIN
  2806.             IF Retry <> r_ok THEN GOTO Ferdig;
  2807.           END
  2808.           ELSE
  2809.             escape := escape + ch;
  2810.         UNTIL done AND (ch IN ['@'..'Z','a'..'z']);
  2811.  
  2812.         Dec(escape[0]);
  2813.         IF ch = 'H' THEN BEGIN
  2814.           WHILE x < 81 DO BEGIN
  2815.             Inc(x);
  2816.             d[n] := ' ';
  2817.             Inc(n);
  2818.           END;
  2819.           x := 1;
  2820.           ch := escape[Length(escape)];
  2821.           WHILE ch > '1' DO BEGIN
  2822.             Inc(x);
  2823.             d[n] := ' ';
  2824.             Inc(n);
  2825.             Dec(ch);
  2826.           END;
  2827.         END;
  2828.       END;
  2829.       { Ignore other control characters ! }
  2830.     END
  2831.     ELSE BEGIN
  2832.       Inc(n,bytes);
  2833.       IF IBM_Mode > 0 THEN BEGIN
  2834.         Inc(x,bytes);
  2835.         IF x > 81 THEN x := 81;
  2836.       END;
  2837.       IF (n >= max) THEN GOTO Ferdig;
  2838.     END;
  2839.   UNTIL FALSE;
  2840.   Ferdig:
  2841. END;                         { GetLink }
  2842.  
  2843. FUNCTION CheckSum(VAR buf; n, CheckType : WORD): WORD;
  2844. BEGIN
  2845.   IF CheckType <= 2 THEN BEGIN
  2846.     n := ChkSum(buf,n);
  2847.     IF CheckType = 1 THEN
  2848.       CheckSum := (n + Lo(n) Shr 6) AND 63
  2849.     ELSE
  2850.       CheckSum := n AND $FFF;
  2851.   END
  2852.   ELSE { CRC }
  2853.     CheckSum := CRC(buf,n);
  2854. END;                         { CheckSum }
  2855.  
  2856. PROCEDURE SendPakkeT(VAR T : PakkeType);
  2857. VAR s : WORD;
  2858. BEGIN
  2859.   IF T.long THEN BEGIN
  2860.     T.plen := ' ';
  2861.     T.plen1 := Chr(32 + (T.TotLen - 1) DIV LenModulo);
  2862.     T.plen2 := Chr(32 + ((T.TotLen - 1) MOD LenModulo));
  2863.     s := CheckSum(T.plen,5,1);
  2864.     T.hchk := Chr(32 + s);
  2865.   END
  2866.   ELSE BEGIN
  2867.     IF (T.TotLen > 95) OR (T.TotLen < 4) THEN BEGIN
  2868.       WriteLn('Gal lengde: ',T.TotLen);
  2869.       Exit;
  2870.     END;
  2871.     T.plen := Chr(31 + T.TotLen);
  2872.   END;
  2873.   s := CheckSum(T.plen,T.TotLen-CheckType,CheckType);
  2874.   IF CheckType >= 2 THEN BEGIN
  2875.     IF CheckType = 3 THEN
  2876.       T.pdata[T.TotLen-5] := Chr(32 + (s Shr 12));
  2877.     T.pdata[T.TotLen-4] := Chr(32 + ((s Shr 6) AND 63));
  2878.   END;
  2879.   T.pdata[T.TotLen-3] := Chr(32 + (s AND 63));
  2880.   SendLink(T.plen,T.TotLen);
  2881. END;                         { SendPakkeT }
  2882.  
  2883. PROCEDURE SendPakke;
  2884. BEGIN
  2885.   SendPakkeT(TX_Pac^);
  2886. END;
  2887.  
  2888. PROCEDURE MakePakke(VAR p : PakkeType; nr : CarNum;
  2889.                     typ : PakkeCh; data : String);
  2890. BEGIN
  2891.   p.pnr := Chr(32 + nr);
  2892.   p.ptype := typ;
  2893.   p.TotLen := Length(data) + 3 + CheckType;
  2894.   p.plen := Chr(31 + p.TotLen);
  2895.   p.long := FALSE;
  2896.   Move(data[1],p.pdata,Length(data));
  2897. END;                         { MakePakke }
  2898.  
  2899. FUNCTION TestPakke(VAR p : PakkeType): BOOLEAN;
  2900. VAR chk, c : WORD;
  2901. BEGIN
  2902.   TestPakke := FALSE;
  2903.   IF p.TotLen <= 2 + CheckType THEN BEGIN
  2904.     IF p.TotLen > 0 THEN
  2905.       Warning('Too short packet!')
  2906.     ELSE IF (p.TotLen = 0) AND ShowTimeOut THEN
  2907.       Warning('TimeOut!');
  2908.     Exit;
  2909.   END;
  2910.   IF (p.ptype < 'A') OR (p.ptype > 'Z') THEN BEGIN
  2911.     Warning('Error in packet type!');
  2912.     Exit;
  2913.   END;
  2914.   IF p.plen > ' ' THEN BEGIN
  2915.     chk := Ord(p.plen) - 32;
  2916.     p.long := FALSE;
  2917.   END
  2918.   ELSE BEGIN
  2919.     chk := CheckSum(p.plen,5,1);
  2920.     IF chk <> Ord(p.hchk)-32 THEN BEGIN
  2921.       Warning('Error in header checksum!');
  2922.       Exit;
  2923.     END;
  2924.     chk := (Ord(p.plen1) - 32) * LenModulo + Ord(p.plen2) - 32;
  2925.     p.long := TRUE;
  2926.   END;
  2927.   IF chk >= p.TotLen THEN BEGIN
  2928.     Warning('Len error: '+Tstr(chk-p.TotLen-1,1));
  2929.     Exit;
  2930.   END;
  2931.   p.TotLen := Succ(chk);
  2932.   IF Ord(p.pnr) - 32 > 63 THEN Exit;
  2933.   chk := CheckSum(p.plen,p.TotLen - CheckType,CheckType);
  2934.   c := Ord(p.pdata[p.TotLen-3]) - 32;
  2935.   IF CheckType >= 2 THEN BEGIN
  2936.     Inc(c,(Ord(p.pdata[p.TotLen-4]) - 32) Shl 6);
  2937.     IF CheckType = 3 THEN
  2938.       Inc(c,(Ord(p.pdata[p.TotLen-5]) - 32) Shl 12);
  2939.   END;
  2940.   IF c = chk THEN
  2941.     TestPakke := TRUE
  2942.   ELSE
  2943.     Warning('CHK err: Calc='+Tstr(chk,1)+', Rec='+Tstr(c,1));
  2944. END;                                   {TestPakke}
  2945.  
  2946. PROCEDURE GetFast(VAR p; VAR len : WORD; max : WORD);
  2947. LABEL Avbryt;
  2948. VAR by : BYTE;
  2949.     ch : CHAR;
  2950.     ok : BOOLEAN;
  2951.     dptr : ^BYTE;
  2952.     md, dend, bytes, receive, status : WORD;
  2953.     count : WORD;
  2954. BEGIN
  2955.   StartTimerSek(t2,YourTimeOut);
  2956.   dptr := Addr(p);
  2957.   dend := Word(dptr) + max;
  2958.  
  2959.   receive := RS_Buffer[CurComPort].ICadr;
  2960.   status := receive + 5;
  2961.  
  2962.   count := MaxPrTick;
  2963.   ch := #255;
  2964.   REPEAT
  2965.     IF (Retry <> r_ok) OR NOT RunningTimer(t2) THEN GOTO Avbryt;
  2966.     RS_BusyRead(ch,ok,CurComPort);
  2967.     Inc(ReceiveBytes,Ord(ok));
  2968.   UNTIL ch = MySOH;
  2969.  
  2970. {  RS_Set_TX_Int(0,CurComPort);}
  2971.   InLine($FA);                         {CLI}
  2972.  
  2973.   Port[receive+1] := 0;                {Turn off all Serial int's}
  2974.  
  2975.   md := 2000;                          {Wait up to 8 ms for first char.}
  2976.   REPEAT
  2977.     repeat
  2978.       Dec(md);
  2979.       if md = 0 then goto avbryt;
  2980.     until Odd(Port[status]);           {Received data available}
  2981.  
  2982.     dptr^ := Port[receive];
  2983.     Inc(Word(dptr));
  2984.     md := 200;                         { >1 ms delay between two chars}
  2985.     Dec(count);
  2986.     IF count = 0 THEN BEGIN
  2987.       InLine($FB);
  2988.         md := 2000;
  2989.         count := MaxPrTick;
  2990.       InLine($FA);
  2991.     END;
  2992.   UNTIL Word(dptr) >= dend;
  2993.  
  2994. Avbryt:
  2995.   InLine($FB);
  2996.   Port[receive+1] := RX_int+RLS_int;   {Turn off all Serial int's}
  2997.  
  2998.   len := Word(dptr) - Ofs(p);
  2999.   Inc(ReceiveBytes,len);
  3000. END;
  3001.  
  3002. PROCEDURE GetPakke;
  3003. VAR max : WORD;
  3004. BEGIN
  3005.   IF LongPakke THEN max := 9030 ELSE max := 95;
  3006.   IF (CurBaud > 30000) THEN
  3007.     GetFast(RX_Pac^.plen,RX_Pac^.TotLen,max)
  3008.   ELSE
  3009.     GetLink(RX_Pac^.plen,RX_Pac^.TotLen,max);
  3010.   IF r_code = r_ok THEN BEGIN
  3011.     IF NOT TestPakke(RX_Pac^) THEN BEGIN
  3012.       MakePakke(RX_Pac^,PakkeNr,'T','P');
  3013.     END;
  3014.   END
  3015.   ELSE IF r_code = r_keyboard THEN
  3016.     MakePakke(RX_Pac^,PakkeNr,'T','K')
  3017.   ELSE IF r_code = r_timeout THEN
  3018.     MakePakke(RX_Pac^,PakkeNr,'T','T')
  3019.   ELSE IF r_code = r_exit THEN
  3020.     MakePakke(RX_Pac^,PakkeNr,'E','F10')
  3021.   ELSE BEGIN
  3022.     Warning('r_code error!');
  3023.     MakePakke(RX_Pac^,PakkeNr,'T','R');
  3024.   END;
  3025. END;                         { GetPakke }
  3026.  
  3027. PROCEDURE Extract(VAR st : String);
  3028. VAR i, l : WORD;
  3029. BEGIN
  3030.   i := 1;
  3031.   IF RX_Pac^.long THEN i := 4;
  3032.  
  3033.   l := RX_Pac^.TotLen - i - 2 - CheckType;
  3034.   IF l >= SizeOf(st) THEN l := SizeOf(st) - 1;
  3035.   st[0] := Chr(l);
  3036.   Move(RX_Pac^.pdata[i],st[1],l);
  3037. END;                         { Extract }
  3038.  
  3039. PROCEDURE DumpPointers;
  3040. CONST NackCh : ARRAY [0..10] OF CHAR = '-123456789A';
  3041. VAR n, i : WORD;
  3042. BEGIN
  3043.   st[0] := #31;
  3044.   FillChar(st[1],31,' ');
  3045.   n := nut;
  3046.   FOR i := 1 TO (ninn-nut) AND 63 DO BEGIN
  3047.     st[i] := NackCh[pw[n].retry];
  3048.     n := Succ(n) AND 63;
  3049.   END;
  3050.   GotoXY(41,10); WriteStr(st);
  3051. END;
  3052.  
  3053. PROCEDURE MakeInfoScreen(s : String);
  3054. BEGIN
  3055.   ClrAll;
  3056.   ClrLast;
  3057.   GotoXY(30,6); WriteStr('File name:');
  3058.   GotoXY(22,7); WriteStr('Bytes transferred:');
  3059.   GotoXY(30,9); WriteStr(s);
  3060.   GotoXY(22,11); WriteStr('Number of packets:');
  3061.   GotoXY(22,12); WriteStr('Number of retries:');
  3062.   GotoXY(29,13); WriteStr('Last error:');
  3063.   GotoXY(1,25); WriteStr('Kermit:  F1=Cancel File');
  3064.  
  3065.   GotoXY(61,MaxY);  WriteStr('F9=Retry  F10=Abort');
  3066. END;  { MakeInfoScreen }
  3067.  
  3068. PROCEDURE WriteFileName;
  3069. BEGIN
  3070.   GotoXY(41,6);
  3071.   IF OriginalName <> FileName THEN
  3072.     WriteStr(Pad(OriginalName + ' as '+FileName,40))
  3073.   ELSE
  3074.     WriteStr(Pad(FileName,40));
  3075. END;
  3076.  
  3077. PROCEDURE WriteBytes;
  3078. BEGIN
  3079.   GotoXY(41,7); Write(Bytes);
  3080. END;
  3081.  
  3082. PROCEDURE WriteFileSize;
  3083. BEGIN
  3084.   GotoXY(30,8); Write('File size: ',FileMax); ClrEol;
  3085. END;  { WriteSize }
  3086.  
  3087. PROCEDURE WriteStatus;
  3088. BEGIN
  3089.   GotoXY(41,9); WriteStr(StatusString); ClrEol;
  3090. END;
  3091.  
  3092. PROCEDURE WriteTotalNr;
  3093. BEGIN
  3094.   Inc(TotalNr);
  3095.   GotoXY(41,11); Write(TotalNr);
  3096. END;  { WriteTotalNr }
  3097.  
  3098. PROCEDURE WriteFeilNr;
  3099. BEGIN
  3100.   Inc(FeilNr);              {Auto-Increment FeilNr}
  3101.   GotoXY(41,12); Write(FeilNr);
  3102. END;
  3103.  
  3104. PROCEDURE WriteError;
  3105. BEGIN
  3106.   GotoXY(41,13); WriteStr(Pad(ErrorString,57));
  3107.   RS_ClrBuffer(CurComPort);
  3108. END;
  3109.  
  3110. PROCEDURE ZeroBytes;
  3111. BEGIN
  3112.   Bytes := 0;
  3113.   GotoXY(41,7); ClrEol;
  3114. END;
  3115.  
  3116. PROCEDURE AddBytes(n : WORD);
  3117. BEGIN
  3118.   Bytes := Bytes + n;
  3119.   WriteBytes;
  3120. END;                                   {AddBytes}
  3121.  
  3122. PROCEDURE SendPacket(PakkeNr : CarNum; typ : PakkeCh; st : String);
  3123. BEGIN
  3124.   MakePakke(TX_Pac^, pakkenr, typ, st);
  3125.   SendPakke;
  3126. END;                         { SendPacket }
  3127.  
  3128. PROCEDURE SendAbort(s : String);
  3129. BEGIN
  3130.   ErrorString := s;
  3131.   WriteError;
  3132.   SendPacket(PakkeNr,'E',s);
  3133. END;                         { SendAbort }
  3134.  
  3135. PROCEDURE MakeNextData; FORWARD;
  3136.  
  3137. TYPE KermitState = (Abort, Complete, SendInit, SendName,
  3138.                     SendAttr, SendData, SendEOF,
  3139.                     SendEnd, WaitInit, WaitName, WaitData, TimeOut);
  3140.  
  3141. PROCEDURE SendAndGet(VAR s : KermitState; OkState : KermitState;
  3142.                          data : BOOLEAN);
  3143. VAR Ferdig : BOOLEAN;
  3144.     nr : WORD;
  3145. BEGIN
  3146.   RetryNr := 0; Ferdig := FALSE;
  3147.   REPEAT
  3148.     SendPakke;
  3149.     IF data THEN
  3150.       MakeNextData;
  3151.     GetPakke;
  3152.     WITH RX_Pac^ DO BEGIN
  3153.       nr := Ord(pnr) - 32;
  3154.       IF ((ptype = 'Y') AND (nr = PakkeNr)) OR
  3155.          ((ptype = 'N')) AND (nr = Succ(PakkeNr) AND 63) THEN BEGIN
  3156.         Ferdig := TRUE;
  3157.         s := OkState;
  3158.         PakkeNr := Succ(PakkeNr) AND 63;
  3159.         WriteTotalNr;
  3160.       END
  3161.       ELSE IF (ptype IN ['N','T']) OR (ptype = TX_Pac^.ptype) THEN BEGIN
  3162.         Inc(RetryNr);
  3163.         WriteFeilNr;
  3164.         Warning(ptype+'-packet received!');
  3165.         IF RetryNr >= RetryLimit THEN BEGIN
  3166.           Ferdig := TRUE;
  3167.           s := Abort;
  3168.           SendAbort('Too many retries!');
  3169.         END;
  3170.       END
  3171.       ELSE IF ptype = 'E' THEN BEGIN
  3172.         Ferdig := TRUE;
  3173.         s := Abort;
  3174.         Extract(ErrorString);
  3175.         WriteError;
  3176.       END
  3177.       ELSE IF (nr = PakkeNr) OR (nr = Succ(PakkeNr) AND 63) THEN BEGIN
  3178.         SendAbort('Wrong packet type: '+ptype);
  3179.         ptype := 'E';
  3180.         Ferdig := TRUE;
  3181.         s := Abort;
  3182.       END;
  3183.     END;
  3184.   UNTIL Ferdig;
  3185.   IF s = Abort THEN ErrorLevel := 2;
  3186. END;                         { SendAndGet }
  3187.  
  3188. CONST
  3189.       Reserved1Bit = 32;
  3190.       Reserved2Bit = 16;
  3191.       A_PacketBit  =  8;
  3192.       WindowBit    =  4;
  3193.       LongPakkeBit =  2;
  3194.  
  3195.       BinaryDataBit= 32;
  3196.  
  3197. PROCEDURE MakeInitPacket(Ptyp : PakkeCh);
  3198. VAR s : String;
  3199.     b : BYTE;
  3200. BEGIN
  3201.   s := Pad('',14);
  3202.   IF LongMaxLength < 95 THEN BEGIN
  3203.     s[1] := Chr(32 + (LongMaxLength));
  3204.     LongPakke := FALSE;
  3205.   END
  3206.   ELSE
  3207.     s[1] := '~';
  3208.   IF Ptyp = 'Y' THEN
  3209.     IF Abs(YourTimeOut-MyTimeOut) < 2 THEN
  3210.       MyTimeOut := YourTimeOut - 2
  3211.   ELSE
  3212.     AttrPakke := TRUE;
  3213.   s[2] := Chr(32 + (MyTimeOut));
  3214.   s[3] := Chr(32 + (MyPad));
  3215.   s[4] := Chr(64 XOR Ord(MyPadChar));
  3216.   s[5] := Chr(32 + (Ord(MyCR)));
  3217.   s[6] := MyQCtrlChar;
  3218.   s[7] := Q8BitChar;
  3219.   IF (Ptyp = 'S') AND (CurBits=8) THEN
  3220.     s[7] := 'Y'
  3221.   ELSE IF (Ptyp = 'Y') AND NOT Q8Bit THEN
  3222.     s[7] := 'N';
  3223.   s[8] := Chr(FileCheck+48);
  3224.   s[9] := QrepChar;
  3225.  
  3226.   b := A_PacketBit + 1;
  3227.   IF LongPakke THEN BEGIN
  3228.     b := b OR LongPakkeBit;
  3229.     s[13] := Chr(32 + (LongMaxLength DIV LenModulo));
  3230.     s[14] := Chr(32 + (LongMaxLength MOD LenModulo));
  3231.   END;
  3232.   IF WindowData THEN BEGIN
  3233.     b := b OR WindowBit;
  3234.     s[12] := Chr(32 + WinSize);
  3235.   END;
  3236.   s[10] := Chr(b+32);
  3237.   b := 0;
  3238.   IF BinaryData THEN b := BinaryDataBit;
  3239.   s[11] := Chr(b+32);
  3240.   MakePakke(TX_Pac^, 0, ptyp, s);
  3241. END;                         { MakeInitPacket }
  3242.  
  3243. PROCEDURE TolkInitPacket;
  3244. VAR c, l, w, a2 : INTEGER;
  3245.     s    : String;
  3246. BEGIN
  3247.   Extract(s);
  3248.   s := Pad(s,30);
  3249.   YourMaxLength := Ord(s[1]) - 32;
  3250.   IF s[2] > ' ' THEN YourTimeOut := -32 + Ord(s[2]);
  3251.   IF RX_Pac^.ptype <> 'Y' THEN
  3252.     IF Abs(YourTimeOut-MyTimeOut) < 2 THEN
  3253.       MyTimeOut := YourTimeOut - 2;
  3254.   YourPad := -32 + Ord(s[3]);
  3255.   YourPadChar := Chr(64 XOR Ord(s[4]));
  3256.   IF s[5] > ' ' THEN YourCR := Chr(Ord(s[5]) - 32);
  3257.   IF s[6] > ' ' THEN YourQCtrlChar := s[6];
  3258.   IF s[7] IN ['!'..'>',#96..'~'] THEN BEGIN
  3259.     Q8bitChar := s[7];
  3260.     Q8bit := TRUE;
  3261.   END
  3262.   ELSE Q8bit := (s[7] = 'Y') AND (CurBits=7);
  3263.   CASE s[8] OF
  3264.     '2' : FileCheck := 2;
  3265.     '3' : FileCheck := 3;
  3266.     ELSE
  3267.       FileCheck := 1;
  3268.   END;
  3269.   Qrep := s[9] = QrepChar;
  3270.   IF Qrep THEN maxrep := 94 ELSE maxrep := 1;
  3271.   c := Ord(s[10]) - 32;
  3272.   a2 := 0;
  3273.   IF Odd(c) THEN a2 := Ord(s[11]) - 32;
  3274.   l := 10;
  3275.   WHILE Odd(Ord(s[l])) DO Inc(l);         {skip all other attribute bits}
  3276.   WindowData := WindowData AND (c AND WindowBit <> 0);
  3277.   IF WindowData THEN BEGIN
  3278.     WinSize := Ord(s[l+1]) - 32;      {We can accept any size up to 31}
  3279.     WindowData := WinSize > 1;
  3280.   END;
  3281.   LongPakke := LongPakke AND (c AND LongPakkeBit <> 0) AND NOT WindowData;
  3282.   AttrPakke := AttrPakke AND (c AND A_PacketBit <> 0);
  3283.   IF LongPakke THEN BEGIN
  3284.     l := (Ord(s[l+2]) - 32) * LenModulo + Ord(s[l+3]) - 32;
  3285.     IF l = 0 THEN
  3286.       LongMaxLength := 500
  3287.     ELSE IF l < LongMaxLength THEN
  3288.       LongMaxLength := l;
  3289.   END;
  3290.   BinaryData := BinaryData AND (a2 AND BinaryDataBit <> 0);
  3291. END;                                   {TolkInitPacket}
  3292.  
  3293. PROCEDURE XmitAttr(VAR state : KermitState);
  3294. VAR siz : String[12];
  3295. BEGIN
  3296.   UnPackTime(DTA.Time,FTime);
  3297.  
  3298.   Str((FileMax + 1023) DIV 1024:1,st);
  3299.   Str(FileMax:1,siz);
  3300.   st := '#/861124 14:56:30!'+Chr(32+Length(st))+
  3301.         st+'1'+Chr(32+Length(siz))+siz;
  3302.  
  3303.   ByteToDigits(FTime.year MOD 100,st[3]);
  3304.   ByteToDigits(FTime.month,st[5]);
  3305.   ByteToDigits(FTime.day,st[7]);
  3306.   ByteToDigits(FTime.hour,st[10]);
  3307.   ByteToDigits(FTime.min,st[13]);
  3308.   ByteToDigits(FTime.sec,st[16]);
  3309.  
  3310.   MakePakke(TX_Pac^, PakkeNr,'A',st);
  3311.   SendAndGet(state,SendData,FALSE);
  3312.   IF (state = SendData) THEN BEGIN
  3313.     Extract(st);
  3314.     IF (Length(st) > 0) AND (st[1] = 'N') THEN BEGIN
  3315.       StopFile := TRUE;
  3316.       state := SendEOF;
  3317.     END;
  3318.   END;
  3319. END;
  3320.  
  3321. PROCEDURE XmitEOF(VAR s : KermitState);
  3322. BEGIN
  3323.   Inc(TotalBytes,FilePos(fil));
  3324.   Close(fil);
  3325. {  Debug('Enter XmitEOF'); }
  3326.   IF StopFile THEN BEGIN
  3327.     MakePakke(TX_Pac^, PakkeNr,'Z','D');
  3328.     Warning(FileName+' discarded!');
  3329.   END
  3330.   ELSE
  3331.     MakePakke(TX_Pac^, PakkeNr,'Z','');
  3332.   SendAndGet(s,SendName,FALSE);
  3333. END;                         { XmitEOF }
  3334.  
  3335. PROCEDURE XmitEnd(VAR s : KermitState);
  3336. BEGIN
  3337.   MakePakke(TX_Pac^, PakkeNr,'B','');
  3338.   SendAndGet(s,Complete,FALSE);
  3339. END;                         { XmitEnd }
  3340.  
  3341. TYPE STRING3 = RECORD
  3342.                  CASE BOOLEAN OF
  3343.                    FALSE: (st : STRING[3]);
  3344.                    TRUE:  (p  : Pointer);
  3345.                END;
  3346.  
  3347. VAR CodeTab : ARRAY [CHAR] OF STRING3;
  3348.  
  3349. PROCEDURE MakeCodeTab;
  3350. TYPE Str3Ptr = ^String3;
  3351. VAR lch, ch : CHAR;
  3352.     b       : WORD;
  3353.     CodePtr : Str3Ptr;
  3354.     st      : ARRAY [0..3] OF CHAR;
  3355.     len     : BYTE ABSOLUTE st;
  3356. BEGIN
  3357.   CodePtr := @CodeTab;
  3358.   FOR b := 0 TO 255 DO BEGIN
  3359.     ch := Chr(b);
  3360.     lch := Chr(b AND 127);
  3361.     len := 0;
  3362.     IF (ch > #127) AND Q8Bit THEN BEGIN
  3363.       len := 1;
  3364.       st[1] := Q8BitChar;
  3365.       ch := lch;
  3366.     END;
  3367.     IF (Succ(b) AND 127) <= 32 THEN BEGIN
  3368.       Inc(len);
  3369.       st[len] := YourQCtrlChar;
  3370.       ch := Chr(64 XOR Ord(ch));
  3371.     END
  3372.     ELSE IF ((lch = Q8BitChar) AND Q8Bit) OR ((lch = QrepChar) AND Qrep) OR
  3373.        (lch = YourQCtrlChar) THEN BEGIN
  3374.       Inc(len);
  3375.       st[len] := YourQCtrlChar;
  3376.     END;
  3377.     Inc(len);
  3378.     st[len] := ch;
  3379.  
  3380.     CodePtr^ := String3(st);
  3381.     Inc(Word(CodePtr),SizeOf(String3));
  3382.   END;
  3383. END;                                   {MakeCodeTab}
  3384.  
  3385. PROCEDURE MakeDataPac(VAR p : PakkeType);
  3386. LABEL Avbryt;
  3387. VAR ch : CHAR;
  3388.     st : STRING[3];
  3389.     pst : Pointer ABSOLUTE st;
  3390.     n, max, databytes : WORD;
  3391.     dptr : ^CHAR;
  3392. BEGIN
  3393.   p.ptype := 'D';
  3394.   p.pnr := Chr(32 + PakkeNr);
  3395.   dptr := @p.pdata[1];
  3396.   IF LongPakke THEN BEGIN
  3397.     Inc(Word(dptr),3);                 {Skip over long header}
  3398.     max := LongMaxLength - 7 - CheckType;
  3399.     p.long := TRUE;
  3400.   END
  3401.   ELSE BEGIN
  3402.     max := YourMaxLength - 7 - CheckType;
  3403.     p.long := FALSE;
  3404.   END;
  3405.  
  3406.   databytes := 0;
  3407.   IF EndOfFile THEN GOTO Avbryt;
  3408.  
  3409.   IF BinaryData THEN BEGIN
  3410.     Inc(max,4);
  3411.     IF BufCount < max THEN BEGIN
  3412.       IF BufCount > 0 THEN BEGIN
  3413.         Move(BufPtr^,dptr^,BufCount);
  3414.         Inc(Word(dptr),BufCount);
  3415.         Inc(databytes,BufCount);
  3416.         Dec(max,BufCount);
  3417.       END;
  3418.  
  3419.       BlockRead(fil,buffer^,BufSize,BufCount);
  3420.  
  3421.       IF (IOresult <> 0) OR (BufCount = 0) THEN BEGIN
  3422.         EndOfFile := TRUE;
  3423.         GOTO Avbryt;
  3424.       END;
  3425.       BufferPtr(BufPtr) := Buffer;
  3426.       IF max > BufCount THEN max := BufCount;
  3427.     END;
  3428.     Move(BufPtr^,dptr^,max);
  3429.     Inc(Word(BufPtr),max);
  3430.     Dec(BufCount,max);
  3431.     Inc(Word(dptr),max);
  3432.     Inc(databytes,max);
  3433.     GOTO Avbryt;
  3434.   END;
  3435.  
  3436.   max := Ofs(p.pdata[max]);
  3437.  
  3438.   REPEAT
  3439.     IF BufCount = 0 THEN BEGIN
  3440.       StopLink;
  3441.       BlockRead(fil,buffer^,BufSize,BufCount);
  3442.       StartLink;
  3443.       IF (IOresult <> 0) OR (BufCount = 0 ) THEN BEGIN
  3444.         EndOfFile := TRUE;
  3445.         GOTO AvBryt;
  3446.       END;
  3447.       BufferPtr(BufPtr) := Buffer;
  3448.       buffer^[BufCount] := Chr(NOT Ord(buffer^[BufCount - 1]));  {guard!}
  3449.     END;
  3450.     ch := BufPtr^;
  3451.     n := 1;
  3452.     Inc(Word(BufPtr));
  3453.     Dec(BufCount);
  3454.     WHILE (ch = BufPtr^) AND (n < MaxRep) DO BEGIN
  3455.       Inc(n);
  3456.       Inc(Word(BufPtr));
  3457.       Dec(BufCount);
  3458.     END;
  3459.     IF TextFile THEN BEGIN
  3460.       ch := UtConvert[ch];
  3461.       IF ch = ^Z THEN BEGIN
  3462.         EndOfFile := TRUE;
  3463.         Goto Avbryt;
  3464.       END;
  3465.     END;
  3466.     Inc(databytes,n);
  3467.     pst := CodeTab[ch].p;   {st := CodeTab[ch].st;}
  3468.     IF (n = 2) AND (st[0] = #1) THEN BEGIN
  3469.       dptr^ := st[1];
  3470.       Inc(Word(dptr));
  3471.       dptr^ := st[1];  {repeat 2 times!}
  3472.       Inc(Word(dptr));
  3473.     END
  3474.     ELSE BEGIN
  3475.       IF n >= 2 THEN BEGIN
  3476.         dptr^ := QrepChar;
  3477.         Inc(Word(dptr));
  3478.         dptr^ := Chr(n+32);
  3479.         Inc(WORD(dptr));
  3480.       END;
  3481.  
  3482.       dptr^ := st[1];
  3483.       Inc(WORD(dptr));
  3484.       IF st[0] > #1 THEN BEGIN
  3485.         dptr^ := st[2];
  3486.         Inc(WORD(dptr));
  3487.         IF st[0] > #2 THEN BEGIN
  3488.           dptr^ := st[3];
  3489.           Inc(WORD(dptr));
  3490.         END;
  3491.       END;
  3492.     END;
  3493.   UNTIL Word(dptr) >= max;
  3494. Avbryt:
  3495.   IF databytes = 0 THEN
  3496.     p.TotLen := 0
  3497.   ELSE BEGIN
  3498.     AddBytes(databytes);
  3499.     p.TotLen := Word(dptr) - Ofs(p.plen) + CheckType;
  3500.   END;
  3501. END;                                   {MakeDataPac}
  3502.  
  3503. PROCEDURE MakeNextData;
  3504. BEGIN
  3505.   IF NOT Next_Data_OK AND (CurBaud < 30000) THEN BEGIN
  3506.     MakeDataPac(Next_Pac^);
  3507.     Next_Data_OK := TRUE;
  3508.   END;
  3509. END;
  3510.  
  3511. PROCEDURE MakeData;
  3512. VAR temp : PakkeTypePtr;
  3513. BEGIN
  3514.   IF Next_Data_OK THEN BEGIN
  3515.     temp := TX_Pac;
  3516.     TX_Pac := Next_Pac;
  3517.     Next_Pac := temp;
  3518.  
  3519.     TX_Pac^.pnr := Chr(32 + PakkeNr);
  3520.     Next_Data_OK := FALSE;
  3521.   END
  3522.   ELSE
  3523.     MakeDataPac(TX_Pac^);
  3524. END;                         { MakeData }
  3525.  
  3526. PROCEDURE Ack(PakkeNr : WORD);
  3527. BEGIN
  3528.   SendPacket(PakkeNr,'Y','');
  3529. END;
  3530.  
  3531. PROCEDURE Nack(PakkeNr : WORD);
  3532. BEGIN
  3533.   SendPacket(PakkeNr,'N','');
  3534. END;
  3535.  
  3536. VAR state : KermitState;
  3537.     NackedNr : WORD;
  3538.     RX_Start : BOOLEAN;
  3539.  
  3540. PROCEDURE InitLesPakke;
  3541. BEGIN
  3542.   StartTimerSek(t2,YourTimeOut);
  3543.   RX_Start := TRUE;
  3544. END;
  3545.  
  3546. PROCEDURE LesPakke(VAR RX: PakkeType; VAR ok : BOOLEAN);
  3547. LABEL Ferdig, Init;
  3548. VAR bytes, n : WORD;
  3549.     buf : ARRAY [-3..100] OF CHAR ABSOLUTE RX;
  3550. BEGIN
  3551.   ok := FALSE;
  3552.   WITH RX DO BEGIN
  3553.     IF Retry <> r_ok THEN BEGIN
  3554.       IF r_code = r_timeout THEN
  3555.         MakePakke(RX,nut,'T','T')
  3556.       ELSE IF r_code = r_keyboard THEN
  3557.         MakePakke(RX,nut,'T','K')
  3558.       ELSE
  3559.         MakePakke(RX,nut,'E','F10');
  3560.       ok := TRUE;
  3561.       GOTO Init;
  3562.     END;
  3563.     IF RX_Start THEN BEGIN
  3564.       n := 100;
  3565.       REPEAT
  3566.         Dec(n);
  3567.         IF n = 0 THEN Exit;
  3568.         RS_ReadBlock(plen,96,bytes,CurComPort);
  3569.         IF bytes = 0 THEN Exit;
  3570.         Inc(ReceiveBytes,bytes);
  3571.       UNTIL plen = MySOH;
  3572.       RX_Start := FALSE;
  3573.       TotLen := 0;
  3574.       plen := '~';
  3575.     END;
  3576.     REPEAT
  3577.       RS_ReadBlock(buf[TotLen],96-TotLen,bytes,CurComPort);
  3578.       IF bytes = 0 THEN BEGIN
  3579.         IF TotLen > Ord(plen) - 32 THEN GOTO Ferdig;
  3580.         Exit;
  3581.       END;
  3582.       Inc(ReceiveBytes,bytes);
  3583.       IF NOT BinaryData AND (buf[TotLen] < ' ') THEN BEGIN
  3584.         IF buf[TotLen] = MyCR THEN GOTO Ferdig;
  3585.         IF buf[TotLen] = MySOH THEN BEGIN
  3586.           TotLen := 0;
  3587.           plen := '~';
  3588.         END;
  3589.         Exit;
  3590.       END;
  3591.       Inc(TotLen,bytes);
  3592.     UNTIL TotLen > 100;
  3593.  
  3594.   Ferdig:
  3595.     ok := TestPakke(RX) AND (TotLen < 96) AND NOT RX.long;
  3596. {$IFDEF DEBUG}
  3597.     IF LogFileMode = LogAll THEN BEGIN
  3598.       LogChar('<');
  3599.       FOR n := 0 TO Pred(TotLen) DO
  3600.         LogChar(buf[n]);
  3601.       LogChar('>');
  3602.     END;
  3603. {$ENDIF}
  3604.   Init:
  3605.     InitLesPakke;
  3606.   END;
  3607. END;                                   {LesPakke}
  3608.  
  3609. PROCEDURE TrySend;
  3610. BEGIN
  3611.   IF RS_Room(CurComPort) < 4000 THEN Exit; { >1 packet already in pipeline}
  3612.   IF NackedNr = 0 THEN BEGIN
  3613.     IF (ninn-nut) AND 63 < WinSize THEN BEGIN
  3614.       IF EndOfFile THEN BEGIN
  3615. {        IF nut = ninn THEN
  3616.         Debug('File completed'); }
  3617.         Exit;            {No more Data packets}
  3618.       END;
  3619.       PakkeNr := ninn;
  3620.       WITH pw[ninn] DO BEGIN
  3621.         MakeDataPac(dptr^);
  3622.         IF dptr^.TotLen > 0 THEN BEGIN
  3623.           SendPakkeT(dptr^);
  3624.           acknack := 0; {acked := FALSE; nacked := FALSE;}
  3625.           retry := 0;
  3626.           ninn := Succ(ninn) AND 63;
  3627.         END;
  3628.       END;
  3629.       Exit;
  3630.     END;
  3631.                                        {Window is full, see if any acked}
  3632.     IF pw[nut].retry > 0 THEN Exit;
  3633.     n := nut;
  3634.     REPEAT
  3635.       n := Succ(n) AND 63;
  3636.       IF n = ninn THEN Exit;
  3637.     UNTIL pw[n].acknack <> 0;
  3638.     SendPakkeT(pw[nut].dptr^);
  3639.     pw[nut].retry := 1;
  3640.     Exit;
  3641.   END
  3642.   ELSE BEGIN  {NackedNr > 0}
  3643.     n := nut;
  3644.     Dec(NackedNr);
  3645.     WHILE NOT pw[n].nacked DO BEGIN
  3646.       n := Succ(n) AND 63;
  3647.       IF n = ninn THEN BEGIN
  3648.         Warning('No NACK');
  3649.         Exit;
  3650.       END;
  3651.     END;
  3652.     SendPakkeT(pw[n].dptr^);
  3653.     pw[n].nacked := FALSE;
  3654.   END;
  3655. END;                                   {TrySend}
  3656.  
  3657. PROCEDURE DoPakke;
  3658. VAR msg : String;
  3659. BEGIN
  3660.   WITH RX_Pac^ DO BEGIN
  3661. {    IF EndOfFile THEN Debug('EOF - '+Tstr((ninn-nut) AND 63,1));  }
  3662.     WriteTotalNr;
  3663.     nr := -32 +Ord(pnr);                  {Position in circular buffer}
  3664.     n := (nr - nut) AND 63;            {Offset from first packet}
  3665.  
  3666.     Extract(msg);
  3667.  
  3668.     IF ptype = 'T' THEN BEGIN
  3669.       RS_Enable(CurComPort);
  3670.       WriteFeilNr;
  3671.       WITH pw[nut] DO BEGIN
  3672.         IF NOT nacked THEN BEGIN
  3673.           Inc(NackedNr);
  3674.           nacked := TRUE;
  3675.         END;
  3676.       END;
  3677.       Inc(RetryNr);
  3678.       IF RetryNr > 10 THEN BEGIN
  3679.         SendAbort('Too many retries!');
  3680.         state := Abort;
  3681.       END;
  3682.       Exit;
  3683.     END;
  3684.  
  3685.     RetryNr := 0;
  3686.  
  3687.     IF ptype = 'Y' THEN BEGIN
  3688.       IF msg = 'X' THEN BEGIN
  3689.         StopFile := TRUE;
  3690.         state := SendEOF;
  3691.       END;
  3692.       IF n >= (ninn-nut) AND 63 THEN BEGIN
  3693. {        Debug('ACK outside');  }
  3694.         Exit;    {ACK outside of window}
  3695.       END;
  3696.       WITH pw[nr] DO BEGIN
  3697.         acked := TRUE;
  3698.         IF nacked THEN BEGIN
  3699.           Dec(NackedNr);
  3700.           nacked := FALSE;
  3701.         END;
  3702.       END;
  3703.       WHILE pw[nut].acked DO BEGIN
  3704.         nut := Succ(nut) AND 63;
  3705.         IF ninn = nut THEN BEGIN
  3706.           IF EndOfFile THEN BEGIN
  3707.             state := SendEOF;
  3708. {            Debug('Exit TrySend'); }
  3709.           END;
  3710.           Exit;
  3711.         END;
  3712.       END;
  3713.       Exit;
  3714.     END;
  3715.     IF ptype = 'N' THEN BEGIN
  3716.       RS_Enable(CurComPort);
  3717.       IF n >= (ninn-nut) AND 63 THEN BEGIN {NACK outside window}
  3718. {        Debug('NACK outside');  }
  3719.         IF nut = ninn THEN BEGIN
  3720. {          Debug('Window empty'); }
  3721.           Exit;
  3722.         END;
  3723.         nr := nut
  3724.       END;
  3725.       WriteFeilNr;
  3726.       WITH pw[nr] DO BEGIN
  3727.         Inc(retry);
  3728.         IF retry > 10 THEN BEGIN
  3729.           SendAbort('Too many retries!');
  3730.           state := Abort;
  3731.           Exit;
  3732.         END;
  3733.         NackedNr := Succ(NackedNr) - Ord(nacked);
  3734.         nacked := TRUE;
  3735.       END;
  3736.       Exit;
  3737.     END;
  3738.     IF ptype = 'E' THEN BEGIN
  3739.       Extract(ErrorString);
  3740.       IF ErrorString <> 'F10' THEN
  3741.         WriteError;
  3742.       state := Abort;
  3743.       Exit;
  3744.     END;
  3745.     SendAbort('Unexpected packet type: '+ptype);
  3746.     state := Abort;
  3747.   END;
  3748. END;
  3749.  
  3750. PROCEDURE SendWindow;
  3751. VAR done : BOOLEAN;
  3752.     i : WORD;
  3753. BEGIN
  3754.   NackedNr := 0;
  3755.   InitLesPakke;
  3756.   InitWindow;
  3757.   REPEAT
  3758.     TrySend;
  3759.     FOR i := 1 TO 4 DO BEGIN
  3760.       LesPakke(RX_Pac^,done);          {Bad packet will be ignored}
  3761.       IF done THEN DoPakke;
  3762.     END;
  3763.     DumpPointers;
  3764.     IF StopFile AND (state<>Abort) THEN state := SendEOF;
  3765.   UNTIL state IN [SendEOF,Abort];
  3766. {
  3767.   IF state = SendEOF THEN
  3768.     Debug('Exit SendEOF')
  3769.   ELSE
  3770.     Debug('Exit Abort');
  3771. }
  3772.   PakkeNr := ninn;
  3773. END;
  3774.  
  3775. PROCEDURE SendManyFiles(FilePattern : String);
  3776. VAR ok, server : BOOLEAN;
  3777.     po : INTEGER;
  3778.     fn : String;
  3779. BEGIN
  3780.   server := FilePattern <> '';
  3781.   IF NOT server THEN BEGIN
  3782.     ReadFileName('File(s) to send: ',FilePattern);
  3783.     IF FilePattern = '' THEN Exit;
  3784.   END;
  3785.  
  3786.   IF Pos('.',FilePattern) = 0 THEN
  3787.     FilePattern := FilePattern + '.';
  3788.   FindFirst(FilePattern,0,DTA);
  3789.   ok := DosError = 0;
  3790.   IF NOT ok THEN BEGIN
  3791.     Error('No files found!');
  3792.     Exit;
  3793.   END;
  3794.  
  3795.   FileName := DTA.Name;
  3796.  
  3797.     po := Ord(FilePattern[0]);
  3798.     WHILE po > 0 DO BEGIN
  3799.       IF FilePattern[po] IN ['\',':'] THEN BEGIN
  3800.         Delete(FilePattern,po+1,30);
  3801.         po := 0;
  3802.       END;
  3803.       Dec(po);
  3804.     END;
  3805.     IF po = 0 THEN FilePattern[0] := #0;
  3806.     state := SendInit;
  3807.     ShowTimeOut := TRUE;
  3808.     PakkeNr := 0;
  3809.     FeilNr := 0;
  3810.     TotalNr := 0;
  3811.     LastNr := 63;
  3812.     MakeInfoScreen('  Sending:');
  3813.     StatusString := 'Init';
  3814.     WriteStatus;
  3815.     InitStat;
  3816.     RS_ClrBuffer(CurComPort);
  3817.     REPEAT
  3818.       CASE state OF
  3819.         SendData : BEGIN
  3820.                      IF WindowData THEN SendWindow
  3821.                      ELSE BEGIN
  3822.                        MakeData;
  3823.                        IF StopFile OR (TX_Pac^.TotLen = 0) THEN
  3824.                          state := SendEOF
  3825.                        ELSE BEGIN
  3826.                          SendAndGet(state,SendData,TRUE);
  3827.                          IF state=Abort THEN BEGIN
  3828.                            Close(fil);
  3829.                          END
  3830.                          ELSE IF (RX_Pac^.TotLen > 4) AND
  3831.                                  (RX_Pac^.pdata[1] = 'X') THEN BEGIN
  3832.                            StopFile := TRUE;
  3833.                            state := SendEOF;
  3834.                          END;
  3835.                        END;
  3836.                      END;
  3837.                    END;
  3838.         SendInit : BEGIN
  3839.                      MakeInitPacket('S');
  3840.                      SendAndGet(state,SendName,FALSE);
  3841.                      IF state=SendName THEN BEGIN
  3842.                        TolkInitPacket;
  3843.                        MakeCodeTab;
  3844.                        CheckType := FileCheck;
  3845.                      END;
  3846.                    END;
  3847.         SendName : BEGIN
  3848.                      fn := FilePattern + FileName + #0;
  3849.                      OriginalName := FileName;
  3850.                      Assign(fil,fn);
  3851.                      Reset(fil,1);
  3852.                      Next_Data_OK := FALSE;
  3853.                      IF IOresult = 0 THEN BEGIN
  3854.                        WriteFileName;
  3855.                        FileMax := FileSize(fil);
  3856.                        WriteFileSize;
  3857.                        Inc(FileNr);
  3858.                        MakePakke(TX_Pac^, PakkeNr,'F',FileName);
  3859.                        SendAndGet(state,SendData,FALSE);
  3860.                        IF state=SendData THEN BEGIN
  3861.                          BufCount := 0;
  3862.                          BufferPtr(BufPtr) := Buffer;
  3863.                          EndOfFile := FALSE;
  3864.                          ZeroBytes;
  3865.                          StatusString := 'In Progress';
  3866.                          WriteStatus;
  3867.                          StopFile := FALSE;
  3868.                          IF AttrPakke THEN state := SendAttr;
  3869.                        END;
  3870.                      END
  3871.                      ELSE BEGIN
  3872.                        Error('File not found: '+fn);
  3873.                        state := Abort;
  3874.                      END;
  3875.                    END;
  3876.         SendAttr : BEGIN
  3877.                      XmitAttr(state);
  3878.                      IF state = Abort THEN
  3879.                        Close(fil)
  3880.                    END;
  3881.         SendEOF  : BEGIN
  3882.                      XmitEOF (state);
  3883.                      IF state <> Abort THEN BEGIN
  3884.                        FindNext(DTA);
  3885.                        ok := DosError = 0;
  3886.                        IF ok THEN BEGIN
  3887.                          state := SendName;
  3888.                          FileName := DTA.Name;
  3889.                        END
  3890.                        ELSE
  3891.                          state := SendEnd;
  3892.                      END;
  3893.                    END;
  3894.         SendEnd  : BEGIN
  3895.                      XmitEnd(state);
  3896.                      StatusString := 'Completed!';
  3897.                      WriteStatus;
  3898.                    END;
  3899.         Abort    : BEGIN
  3900.                      StatusString := 'Aborted';
  3901.                      WriteStatus;
  3902.                      SendAbort('Too many retries!');
  3903.                      Close(fil);
  3904.                      ErrorLevel := 3;
  3905.                    END;
  3906.       END;
  3907.     UNTIL state IN [Complete,Abort];
  3908.   Bell;
  3909.   ShowStat;
  3910. END;  { SendManyFiles }
  3911.  
  3912. TYPE PakkeChar = 'A'..'Z';
  3913.      PakkeSet = SET OF PakkeChar;
  3914.      ReceiveType = (RecF, GetF, ServF, TextF);
  3915.  
  3916. VAR Ferdig, CheckSkip, ValidDate : BOOLEAN;
  3917.     Expect : PakkeSet;
  3918.  
  3919. PROCEDURE TestDate;
  3920. VAR old : FILE;
  3921.     newTime, oldTime : LongInt;
  3922. BEGIN
  3923.   IF OriginalName <> FileName THEN BEGIN
  3924.     Assign(old,OriginalName); Reset(old,1);
  3925.     GetFTime(old,oldTime);
  3926.     Close(old);
  3927.  
  3928.     PackTime(FTime,newTime);
  3929.     IF ((newTime > oldTime) AND (NewDupHandle = SkipFile)) OR
  3930.        ((newTime <= oldTime) AND (OldDupHandle = SkipFile)) THEN
  3931.       StopFile := TRUE;
  3932.   END;
  3933.   CheckSkip := TRUE;
  3934.   IF IOresult <> 0 THEN WriteStr('Test Error'^G);
  3935. END;
  3936.  
  3937. PROCEDURE GetFileAttr;
  3938. VAR l, st : String;
  3939.     p, feil, len : INTEGER;
  3940. BEGIN
  3941.   Extract(st);
  3942.     WHILE st[0] >= #3 DO BEGIN
  3943.       len := Ord(st[2]) - 32;
  3944.       l := Copy(st,3,len);
  3945.       CASE st[1] OF
  3946.         '!' : BEGIN GotoXY(30,8); WriteStr('File size: '+Pad(l+'k',10)); END;
  3947.         '1' : BEGIN GotoXY(30,8); WriteStr('File size: '+Pad(l,10)); END;
  3948.         '#' : BEGIN
  3949.                 p := Pos(' ',l);
  3950.                 Val(Copy(l,p-6,2),FTime.year,feil);
  3951.                 Inc(FTime.year,1900);
  3952.                 IF feil = 0 THEN Val(Copy(l,p-4,2),FTime.month,feil);
  3953.                 IF feil = 0 THEN Val(Copy(l,p-2,2),FTime.day,feil);
  3954.                 IF feil = 0 THEN Val(Copy(l,p+1,2),FTime.hour,feil);
  3955.                 IF feil = 0 THEN Val(Copy(l,p+4,2),FTime.min,feil);
  3956.                 IF (feil = 0) AND (Ord(l[0]) >= p + 8) THEN
  3957.                   Val(Copy(l,p+7,2),FTime.sec,feil);
  3958.                 IF feil = 0 THEN BEGIN
  3959.                   ValidDate := TRUE;
  3960.                   TestDate;
  3961.                 END;
  3962.               END;
  3963.       END;
  3964.       Delete(st,1,len+2);
  3965.     END;
  3966. END;
  3967.  
  3968. PROCEDURE SetFileDate;
  3969. VAR t : LongInt;
  3970. BEGIN
  3971.   IF NOT ValidDate THEN Exit;
  3972.   PackTime(FTime,t);
  3973.   SetFTime(fil,t);
  3974. END;
  3975.  
  3976. VAR CtrlTab : ARRAY [CHAR] OF CHAR;
  3977.  
  3978. PROCEDURE MakeCtrlTab;
  3979. VAR ch : CHAR;
  3980. BEGIN
  3981.   FOR ch := #0 TO #255 DO CtrlTab[ch] := ch;
  3982.   FOR ch := #$3F TO #$5F DO CtrlTab[ch] := Chr(Ord(ch) XOR 64);
  3983.   FOR ch := #$BF TO #$DF DO CtrlTab[ch] := Chr(Ord(ch) XOR 64);
  3984. END;
  3985.  
  3986. PROCEDURE DecodeData(VAR p : PakkeType);
  3987. VAR n, mask : BYTE;
  3988.     ch : CHAR;
  3989.     dptr : ^CHAR;
  3990.     dlen, max, databytes : WORD;
  3991. BEGIN
  3992.   IF DiskError THEN Exit;
  3993.   max := 1;
  3994.   IF p.long THEN max := 4;
  3995.   dptr := Addr(p.pdata[max]);
  3996.   max := Ofs(p.pdata[p.TotLen - 2 - CheckType]);
  3997.   databytes := 0;
  3998.   IF BinaryData THEN BEGIN
  3999.     dlen := max - Word(dptr);
  4000.     IF BufCount < dlen THEN BEGIN
  4001.       Move(dptr^,BufPtr^,BufCount);
  4002.       BlockWrite(fil,buffer^,BufSize);
  4003.       IF IOresult <> 0 THEN BEGIN
  4004.         DiskError := TRUE;
  4005.         Exit;
  4006.       END;
  4007.       Inc(Word(dptr),BufCount);
  4008.       AddBytes(BufCount);
  4009.       Dec(dlen,BufCount);
  4010.       BufferPtr(BufPtr) := Buffer;
  4011.       BufCount := BufSize;
  4012.     END;
  4013.     Move(dptr^,BufPtr^,dlen);
  4014.     Inc(Word(BufPtr),dlen);
  4015.     Dec(BufCount,dlen);
  4016.     AddBytes(dlen);
  4017.  
  4018.     Exit;
  4019.   END;
  4020.  
  4021.   REPEAT
  4022.     ch := dptr^; Inc(WORD(dptr));
  4023.     n := 1;
  4024.     IF ch = RepQ THEN BEGIN
  4025.       n := BYTE(dptr^) - 32; Inc(WORD(dptr));
  4026.       ch := dptr^; Inc(WORD(dptr));
  4027.     END;
  4028.     mask := 0;
  4029.     IF ch = Bit8Q THEN BEGIN
  4030.       mask := $80;
  4031.       ch := dptr^; Inc(WORD(dptr));
  4032.     END;
  4033.     IF ch = YourQCtrlChar THEN BEGIN
  4034.       ch := CtrlTab[dptr^]; Inc(WORD(dptr));
  4035.     END;
  4036.  
  4037.     ch := CHAR(BYTE(ch) OR mask);
  4038.  
  4039.     IF TextFile THEN ch := InnConvert[ch];
  4040.     Inc(databytes,n);
  4041.  
  4042.     REPEAT
  4043.       BufPtr^ := ch;
  4044.       Inc(Word(BufPtr));
  4045.       Dec(BufCount);
  4046.       IF BufCount = 0 THEN BEGIN
  4047.         StopLink;
  4048.         BlockWrite(fil,buffer^,BufSize);
  4049.         StartLink;
  4050.         BufferPtr(BufPtr) := Buffer;
  4051.         BufCount := BufSize;
  4052.         IF IOresult <> 0 THEN BEGIN
  4053.           DiskError := TRUE;
  4054.           Exit;
  4055.         END;
  4056.       END;
  4057.       Dec(n);
  4058.     UNTIL n = 0;
  4059.   UNTIL WORD(dptr) >= max;
  4060.   AddBytes(databytes);
  4061. END;                                   {DecodeData}
  4062.  
  4063. PROCEDURE EOF_Packet;
  4064. VAR EraseFile : BOOLEAN;
  4065.     old, bak : FILE;
  4066.     Bak_file : String[64];
  4067.     punkt : INTEGER;
  4068.     oldTime, newTime : LongInt;
  4069. BEGIN
  4070.   Extract(st);
  4071.   IF BufCount < BufSize THEN BlockWrite(fil,Buffer^,BufSize-BufCount);
  4072.   SetFileDate;
  4073.   Inc(TotalBytes,FilePos(fil));
  4074.   Close(fil);
  4075.   IF (st = 'D') OR StopFile THEN BEGIN
  4076.     Erase(fil);
  4077.     Warning(Filename+' skipped!');
  4078.   END
  4079.   ELSE BEGIN
  4080.     IF OriginalName <> FileName THEN BEGIN
  4081.       Assign(old,OriginalName); Reset(old,1);
  4082.       IF ValidDate THEN BEGIN
  4083.         GetFTime(old,oldTime);
  4084.         PackTime(FTime,newTime);
  4085.         EraseFile := ((newTime>oldTime) AND (NewDupHandle=OverWriteFile)) OR
  4086.                     ((newTime<=oldTime) AND (OldDupHandle=OverWriteFile));
  4087.       END
  4088.       ELSE BEGIN
  4089.         EraseFile := DupHandle = OverWriteFile;
  4090.       END;
  4091.       Close(old);
  4092.       IF EraseFile THEN BEGIN
  4093.         punkt := Pos('.',OriginalName);
  4094.         IF punkt = 0 THEN punkt := Length(OriginalName)+1;
  4095.         BAK_file := Copy(OriginalName,1,punkt-1) + '.BAK';
  4096.         IF (OriginalName <> BAK_File) THEN BEGIN
  4097.           IF Exist(BAK_File) THEN BEGIN
  4098.             Assign(bak,BAK_File);
  4099.             Erase(bak);
  4100.           END;
  4101.           Rename(old,BAK_File);
  4102.           Rename(fil,OriginalName);
  4103.           Warning(FileName+' renamed to '+OriginalName);
  4104.         END;
  4105.       END;
  4106.     END;
  4107.   END;
  4108.   IF IOresult=0 THEN
  4109.     Ack(PakkeNr)
  4110.   ELSE BEGIN
  4111.     SendAbort('File close error!');
  4112.     Ferdig := TRUE;
  4113.   END;
  4114.   Expect := ['B','F'];
  4115.   StatusString := 'File Closed';
  4116.   WriteStatus;
  4117. END;
  4118.  
  4119. PROCEDURE TestPacketNr(VAR ok : BOOLEAN);
  4120. VAR i, j : WORD;
  4121. BEGIN
  4122.   ok := FALSE;
  4123.   n := (nr - nut) AND 63;
  4124.   IF n < (ninn-nut) AND 63 THEN BEGIN
  4125.     ok := n < WinSize;                 {Retransmitted packet}
  4126.     Exit;
  4127.   END;
  4128.   i := (nr - ninn) AND 63;             {Packets past last}
  4129.   IF i >= WinSize THEN Exit;           {Outside of max send window}
  4130.   FOR j := 0 TO i DO BEGIN
  4131.     IF (ninn-nut) AND 63 = WinSize THEN BEGIN
  4132.       IF NOT pw[nut].acked THEN BEGIN
  4133.         SendAbort('Window overflow!');
  4134.         ferdig := TRUE;
  4135.         Exit;
  4136.       END;
  4137.       DecodeData(pw[nut].dptr^);
  4138.       nut := Succ(nut) AND 63;
  4139.     END;
  4140.     WITH pw[ninn] DO BEGIN
  4141.       retry := 0;
  4142.       acked := FALSE;
  4143.       IF j < i THEN BEGIN
  4144.         Nack(ninn);
  4145.         retry := 1;
  4146.       END;
  4147.     END;
  4148.     ninn := Succ(ninn) AND 63;
  4149.   END;
  4150.   ok := TRUE;
  4151. END;                                   { TestPacketNr }
  4152.  
  4153. PROCEDURE WindowReceive;
  4154. VAR ok : BOOLEAN;
  4155. BEGIN                                  { RX_Pac has the first data packet }
  4156.   InitWindow;
  4157.   REPEAT
  4158.    DumpPointers;
  4159.    WITH RX_Pac^ DO BEGIN
  4160.     nr := -32 +Ord(pnr);
  4161.     CASE ptype OF
  4162.       'T' : BEGIN
  4163.         Inc(RetryNr);
  4164.         WriteFeilNr;
  4165.         IF RetryNr > 10 THEN BEGIN
  4166.           SendAbort('Too many timeouts!');
  4167.           Ferdig := TRUE;
  4168.           Exit;
  4169.         END;
  4170.         n := nut;
  4171.         WHILE pw[n].acked AND (n <> ninn) DO n := Succ(n) AND 63;
  4172.         IF (n <> ninn) OR (pdata[1] <> 'P') THEN
  4173.           Nack(n);                         { Most wanted packet nr! }
  4174.         RS_Enable(CurComPort);
  4175.       END;
  4176.       'E' : BEGIN
  4177.         Extract(ErrorString);
  4178.         IF ErrorString <> 'F10' THEN WriteError;
  4179.         IF ErrorLevel < 2 THEN ErrorLevel := 2;
  4180.         Ferdig := TRUE;
  4181.         Exit;
  4182.       END
  4183.       ELSE BEGIN
  4184.         RetryNr := 0;
  4185.         IF ptype = 'Z' THEN BEGIN
  4186.           Extract(st);
  4187.           IF st <> 'D' THEN BEGIN
  4188.             WHILE nut <> ninn DO BEGIN
  4189.               IF NOT pw[nut].acked THEN BEGIN
  4190.                 SendAbort('No ACK at EOF:'+pnr);
  4191.                 Ferdig := TRUE;
  4192.                 Exit;
  4193.               END;
  4194.               DecodeData(pw[nut].dptr^);
  4195.               nut := Succ(nut) AND 63;
  4196.               DumpPointers;
  4197.             END;
  4198.           END;
  4199.           PakkeNr := nr;
  4200.           EOF_Packet;
  4201.           Exit;
  4202.         END;
  4203.         IF StopFile THEN
  4204.           SendPacket(nr,'Y','X')
  4205.         ELSE IF DiskError THEN BEGIN
  4206.           SendAbort('File write error!');
  4207.           ferdig := TRUE;
  4208.           Exit;
  4209.         END
  4210.         ELSE BEGIN
  4211.           TestPacketNr(ok);              {Sjekk om nr i vindu, sett n}
  4212.           IF ferdig THEN Exit;
  4213.           IF ok THEN WITH pw[nr] DO BEGIN
  4214.             IF ptype = 'D' THEN BEGIN
  4215.               IF NOT acked THEN BEGIN
  4216.                 Move(RX_Pac^,dptr^,100);{Room for overhead}
  4217.                 acked := TRUE;
  4218.               END
  4219.               ELSE BEGIN
  4220.                 Inc(retry);
  4221.                 IF retry > 10 THEN BEGIN
  4222.                   SendAbort('Too many retries!');
  4223.                   ferdig := TRUE;
  4224.                   Exit;
  4225.                 END;
  4226.               END;
  4227.               Ack(nr);
  4228.             END
  4229.             ELSE BEGIN
  4230.               SendAbort('Unexpected packet type: '+ptype);
  4231.               Ferdig := TRUE;
  4232.               Exit;
  4233.             END;
  4234.           END
  4235.           ELSE BEGIN
  4236.             WriteFeilNr;
  4237.           END
  4238.         END;
  4239.       END;  {ELSE BEGIN}
  4240.     END;    {CASE ptype OF}
  4241.     GetPakke;
  4242.     WriteTotalNr;
  4243.    END;          {WITH RX_Pac^ DO}
  4244.   UNTIL FALSE;
  4245. END;                                   { WindowReceive }
  4246.  
  4247. PROCEDURE ReceiveFiles(GetFile : ReceiveType; GetName : String);
  4248. VAR LastPk : PakkeCh;
  4249.     state : KermitState;
  4250.     l, n : INTEGER;
  4251.     ch : CHAR;
  4252.     MainName, Ext, Path, st : String;
  4253.     ok, done : BOOLEAN;
  4254. BEGIN
  4255.   IF (GetFile=GetF) AND (GetName = '') THEN BEGIN
  4256.     ReadFileName('File(s) to Get: ',GetName);
  4257.     IF GetName[0]=#0 THEN Exit;
  4258.   END;
  4259.   RS_ClrBuffer(CurComPort);
  4260.   Expect := ['S'];
  4261.   LastPk := '@';
  4262.   PakkeNr := 0;
  4263.   TotalNr := 0;
  4264.   FeilNr  := 0;
  4265.   LastNr := 63;
  4266.   RetryNr := 0;
  4267.   Ferdig := FALSE;
  4268.   ShowTimeOut := TRUE;
  4269.   MakeInfoScreen('Receiving:');
  4270.   FileName[0] := #0;
  4271.   ErrorString[0] := #0;
  4272.   StatusString := 'Init';
  4273.   WriteStatus;
  4274.   RS_ClrBuffer(CurComPort);
  4275.   DiskError := FALSE;
  4276.   IF GetFile=GetF THEN BEGIN
  4277.     MakeInitPacket('I');
  4278.     SendAndGet(state,Complete,FALSE);
  4279.     IF state=Complete THEN
  4280.       TolkInitPacket;
  4281.     SendPacket(0,'R',GetName);
  4282.   END;
  4283.   PakkeNr := 0;
  4284.  
  4285.   IF GetFile<>ServF THEN
  4286.     GetPakke;
  4287.  
  4288.   InitStat;
  4289.   REPEAT
  4290.     WITH RX_Pac^ DO BEGIN
  4291.       IF ptype = 'T' THEN BEGIN
  4292.         Inc(RetryNr);
  4293.         IF RetryNr <= RetryLimit THEN BEGIN
  4294.           WriteFeilNr;
  4295.           Nack(PakkeNr);
  4296.         END
  4297.         ELSE BEGIN
  4298.           SendAbort('Too many retries!');
  4299.           Ferdig := TRUE;
  4300.           ErrorLevel := 1;
  4301.         END;
  4302.       END
  4303.       ELSE BEGIN
  4304.         RetryNr := 0;
  4305.         IF (pnr = Chr(32 + PakkeNr)) AND (ptype IN Expect) THEN BEGIN
  4306.           CASE ptype OF
  4307.             'D' :
  4308.             BEGIN
  4309.               IF NOT CheckSkip THEN BEGIN
  4310.                 IF OriginalName <> FileName THEN
  4311.                   StopFile := DupHandle = SkipFile;
  4312.                 CheckSkip := TRUE;
  4313.               END;
  4314.               IF WindowData THEN
  4315.                 WindowReceive
  4316.               ELSE IF StopFile THEN
  4317.                 SendPacket(PakkeNr,'Y','X')
  4318.               ELSE IF DiskError THEN
  4319.                 SendAbort('File write error!')
  4320.               ELSE BEGIN
  4321.                 IF NOT DiskStopInt THEN Ack(PakkeNr);
  4322.                 Expect := ['D','Z'];
  4323.                 DecodeData(RX_Pac^);
  4324.                 IF DiskStopInt THEN Ack(PakkeNr);
  4325.               END;
  4326.             END;
  4327.             'S' : BEGIN
  4328.                     TolkInitPacket;
  4329.                     RepQ := #0;
  4330.                     IF Qrep THEN RepQ := QrepChar;
  4331.                     Bit8Q := #0;
  4332.                     IF Q8bit THEN Bit8Q := Q8bitChar;
  4333.                     MakeInitPacket('Y');
  4334.                     SendPakke;
  4335.                     CheckType := FileCheck;
  4336.                     IF GetFile = TextF THEN
  4337.                       Expect := ['X']
  4338.                     ELSE
  4339.                       Expect := ['F'];
  4340.                     StatusString := 'GetFileName';
  4341.                     WriteStatus;
  4342.                     MakeCtrlTab;
  4343.                   END;
  4344.             'X' :
  4345.             BEGIN
  4346.               FileName := 'CON'; OriginalName := FileName;
  4347.               Assign(fil,'KERMIT.$$$');
  4348.               ReWrite(fil,1);
  4349.               IF IOresult<>0 THEN BEGIN
  4350.                 SendAbort('Cannot Create File!');
  4351.                 Ferdig := TRUE;
  4352.               END
  4353.               ELSE BEGIN
  4354.                 CheckSkip := FALSE;
  4355.                 ValidDate := FALSE;
  4356.                 BufferPtr(BufPtr) := Buffer;
  4357.                 BufCount := BufSize;
  4358.                 Expect := ['A','D','Z'];
  4359.                 StatusString := 'In progress';
  4360.                 WriteStatus;
  4361.                 WriteFileName;
  4362.                 ZeroBytes;
  4363.                 StopFile := FALSE;
  4364.                 Ack(PakkeNr);
  4365.                 LongReply := TRUE;
  4366.               END;
  4367.             END;
  4368.             'F' :
  4369.             BEGIN
  4370.               Inc(FileNr);
  4371.               Extract(FileName);
  4372.               FOR l := 1 TO Ord(FileName[0]) DO
  4373.                 IF NOT (FileName[l] IN FileNameSet) THEN
  4374.                   FileName[l] := 'X';
  4375.               Ext := '.';
  4376.               MainName[0] := #0;
  4377.               Path[0] := #0;
  4378.               IF Pos(':',FileName) = 2 THEN BEGIN
  4379.                 Path := Copy(FileName,1,2);
  4380.                 IF NOT (Path[1] IN ['A'..'Z']) THEN Path[0] := #0;
  4381.                 Delete(FileName,1,2);
  4382.               END;
  4383.               l := Ord(FileName[0]);
  4384.               WHILE l > 0 DO BEGIN
  4385.                 IF FileName[l] = '.' THEN BEGIN
  4386.                   IF Ext = '.' THEN BEGIN
  4387.                     Ext := Copy(FileName,l,4);
  4388.                     FileName := Copy(FileName,1,Pred(l));
  4389.                   END
  4390.                   ELSE
  4391.                     FileName[l] := 'X';
  4392.                 END
  4393.                 ELSE IF FileName[l] = '\' THEN BEGIN
  4394.                   Path := Path + Copy(FileName,1,l);
  4395.                   Delete(FileName,1,l);
  4396.                   l := 0;
  4397.                 END
  4398.                 ELSE IF FileName[l] = ':' THEN
  4399.                   FileName[l] := 'X';
  4400.                 Dec(l);
  4401.               END;
  4402.               IF FileName[0] > #8 THEN FileName[0] := #8;
  4403. (*
  4404.               IF Path = '' THEN BEGIN
  4405.                 Path := DownLoadPath;
  4406.                 IF Path[Length(Path)] <> '\' THEN
  4407.                   Path := Path + '\';
  4408.               END;
  4409. *)
  4410.               OriginalName := Path+FileName+Ext;
  4411.  
  4412.               MainName := Copy(FileName+'________',1,8);
  4413.               l := 1;
  4414.               FileName := OriginalName;
  4415.  
  4416.               WHILE Exist(FileName) AND (l<100) DO BEGIN
  4417.                 MainName[8] := Chr(l MOD 10 + 48);
  4418.                 IF l>9 THEN MainName[7] := Chr(l DIV 10 + 48);
  4419.                 FileName := MainName+Ext;
  4420.                 Inc(l);
  4421.               END;
  4422.               IF Exist(FileName) THEN BEGIN
  4423.                 SendAbort('Existing File!');
  4424.                 Ferdig := TRUE;
  4425.               END
  4426.               ELSE BEGIN
  4427.                 Assign(fil,FileName);
  4428.                 ReWrite(fil,1);
  4429.                 IF IOresult<>0 THEN BEGIN
  4430.                   SendAbort('Cannot Create File!');
  4431.                   Ferdig := TRUE;
  4432.                 END
  4433.                 ELSE BEGIN
  4434.                   CheckSkip := FALSE;
  4435.                   ValidDate := FALSE;
  4436.                   BufferPtr(BufPtr) := Buffer;
  4437.                   BufCount := BufSize;
  4438.                   Expect := ['A','D','Z'];
  4439.                   StatusString := 'In progress';
  4440.                   WriteStatus;
  4441.                   WriteFileName;
  4442.                   ZeroBytes;
  4443.                   StopFile := FALSE;
  4444.                   Ack(PakkeNr);
  4445.                 END;
  4446.               END;
  4447.               LongReply := FALSE;
  4448.             END;
  4449.             'A' : BEGIN
  4450.                     GetFileAttr;
  4451.                     IF StopFile THEN
  4452.                       SendPacket(PakkeNr,'Y','N')
  4453.                     ELSE
  4454.                       Ack(PakkeNr);
  4455.                   END;
  4456.             'Z' : EOF_Packet;
  4457.             'B' : BEGIN
  4458.                     Ack(PakkeNr);
  4459.                     Ferdig := TRUE;
  4460.                     StatusString := 'Completed';
  4461.                     WriteStatus;
  4462.                   END;
  4463.           END;  { CASE }
  4464.           LastPk := ptype;
  4465.           LastNr := PakkeNr;
  4466.           PakkeNr := Succ(PakkeNr) AND 63;
  4467.           RetryNr := 0;
  4468.           WriteTotalNr;
  4469.         END
  4470.         ELSE IF (pnr = Chr(32 + LastNr)) AND (ptype = LastPk) THEN BEGIN
  4471.           Inc(RetryNr);
  4472.           WriteFeilNr;
  4473.           IF RetryNr > RetryLimit THEN BEGIN
  4474.             SendAbort('Too many retries!');
  4475.             Ferdig := TRUE;
  4476.           END
  4477.           ELSE BEGIN
  4478.             IF ptype = 'S' THEN BEGIN
  4479.               MakeInitPacket('Y');
  4480.               SendPakke;
  4481.             END
  4482.             ELSE
  4483.               Ack(LastNr);
  4484.           END;
  4485.         END
  4486.         ELSE IF ptype = 'E' THEN BEGIN
  4487.           Extract(ErrorString);
  4488.           IF ErrorString <> 'F10' THEN WriteError;
  4489.           IF ErrorLevel < 2 THEN ErrorLevel := 2;
  4490.           Ferdig := TRUE;
  4491.         END
  4492.         ELSE IF (ptype = 'D') AND WindowData THEN
  4493.           WindowReceive
  4494.         ELSE IF (ptype <> 'Y') AND (ptype <> 'N') AND
  4495.                 (pnr <> Chr(32 + LastNr)) THEN BEGIN
  4496.           SendAbort('Wrong packet type: '+ptype);
  4497.           Ferdig := TRUE;
  4498.         END;
  4499.       END;
  4500.     END;
  4501.     IF NOT ferdig THEN
  4502.       GetPakke;
  4503.   UNTIL Ferdig;
  4504.   IF 'D' IN Expect THEN BEGIN
  4505.     Close(fil);
  4506.     IF IOresult = 0 THEN
  4507.       Erase(fil);
  4508.   END;
  4509.   Bell;
  4510.   ShowStat;
  4511.   IF LongReply THEN {ShowReply};
  4512. END;                         { ReceiveFiles }
  4513.  
  4514. PROCEDURE HostCommand;
  4515. BEGIN
  4516.   ClrLast;
  4517.   WriteStr('Remote Directory: ');
  4518.   SendPacket(0,'G','D');
  4519.   GetPakke;
  4520.   IF RX_Pac^.ptype = 'Y' THEN BEGIN
  4521.     Extract(st);
  4522.     IF st = '' THEN BEGIN
  4523.       ReceiveFiles(TextF,'');
  4524.     END
  4525.     ELSE BEGIN
  4526.       GotoXY(1,25);
  4527.       WriteLn(st);
  4528.     END;
  4529.     GetF10;
  4530.   END;
  4531. END;                                   {HostCommand}
  4532.  
  4533. PROCEDURE FinishServer;
  4534. BEGIN
  4535.   ClrLast;
  4536.   WriteStr('Logging out remote server: ');
  4537.   SendPacket(0,'G','F');
  4538.   GetPakke;
  4539.   IF RX_Pac^.ptype = 'Y' THEN BEGIN
  4540.     WriteStr('Done!');
  4541.     Delay(1000);
  4542.   END;
  4543. END;                         { FinishServer }
  4544.  
  4545. VAR
  4546.   StartPath : String[80];
  4547.  
  4548. PROCEDURE Server;
  4549. VAR FilP, FilN, st : String;
  4550.     ok, ResetTimer : BOOLEAN;
  4551. BEGIN
  4552.   ResetTimer := TRUE;
  4553.   ClrScr;
  4554.   REPEAT
  4555.     IF (ServerTime > 0) AND ResetTimer THEN BEGIN
  4556.       MaxServer.count := ServerTime * 1092;
  4557.       MaxServer.UserInt := FALSE;
  4558.       StartTimer(MaxServer);
  4559.     END;
  4560.     CheckType := 1;                          { First packet is always type 1 }
  4561.     ClrLast;
  4562.     WriteStr('Kermit  SERVER');
  4563.     GotoXY(72,MaxY); WriteStr('F10=Exit');
  4564.     PakkeNr := 0;
  4565.     GetPakke;
  4566.     ResetTimer := TRUE;
  4567.     ShowTimeOut := FALSE;
  4568.     IF RX_Pac^.pnr = ' ' THEN BEGIN
  4569.       CASE RX_Pac^.ptype OF
  4570.         'S' : ReceiveFiles(ServF,'');
  4571.         'I' : BEGIN
  4572.                 TolkInitPacket;
  4573.                 MakeInitPacket('Y');
  4574.                 SendPakke;
  4575.               END;
  4576.         'R' : BEGIN
  4577.                 Extract(FilP);
  4578.                 IF FilP[0] = #0 THEN
  4579.                   ok := FALSE
  4580.                 ELSE BEGIN
  4581.                   IF Pos('.',FilP) = 0 THEN FilP := FilP + '.';
  4582.                   FindFirst(FilP,0,DTA);
  4583.                   ok := DosError = 0;
  4584.                 END;
  4585.                 IF ok THEN
  4586.                   SendManyFiles(FilP)
  4587.                 ELSE
  4588.                   SendAbort('No Files Found!');
  4589.               END;
  4590.         'T' : BEGIN
  4591.                 IF ServerTimeOut THEN Nack(PakkeNr);
  4592.                 ResetTimer := FALSE;
  4593.               END;
  4594.         'E' : BEGIN
  4595.                 Extract(ErrorString);
  4596.                 IF ErrorString = 'F10' THEN BEGIN
  4597.                   IF ErrorLevel = 0 THEN ErrorLevel := 1;
  4598.                   Exit;
  4599.                 END;
  4600.                 WriteError;
  4601.               END;
  4602.         'G' : BEGIN
  4603.                 Extract(st);
  4604.                 IF st[1] IN ['F','L'] THEN BEGIN
  4605.                   Ack(0);
  4606.                   Exit;
  4607.                 END
  4608.                 ELSE
  4609.                   SendAbort('Unknown Generic Command!');
  4610.               END;
  4611.         'C' : BEGIN
  4612.                 Extract(st);
  4613.                 IF st = '' THEN st := StartPath;
  4614.                 ChDir(st);
  4615.                 GetDir(0,DownLoadPath);
  4616.                 IF IOresult = 0 THEN ;
  4617.                 SendPacket(PakkeNr,'Y','New dir: '+DownLoadPath);
  4618.               END;
  4619.         ELSE SendAbort('Unknown Server Command!');
  4620.       END;
  4621.     END
  4622.     ELSE
  4623.       Nack(PakkeNr);
  4624.   UNTIL (ServerTime > 0) AND NOT RunningTimer(MaxServer);
  4625. END;                                   {Server}
  4626.  
  4627. {$I Terminal}
  4628.  
  4629. PROCEDURE Kermit;
  4630. VAR
  4631.   key : KeyType;
  4632.   heap : Pointer;
  4633.   st : String;
  4634.   i : INTEGER;
  4635. BEGIN                        { Kermit }
  4636.   Mark(heap);
  4637.  
  4638.   New(RX_Pac); New(TX_Pac); New(Next_Pac);
  4639.  
  4640.   IF MemAvail < KermitBufSize + 2048 THEN
  4641.     KermitBufSize := (MemAvail - 2048) AND $F800;
  4642.  
  4643.   GetMem(buffer,KermitBufSize+1);
  4644.   BufSize := KermitBufSize;
  4645.  
  4646.   AttrPakke := TRUE;
  4647.  
  4648.   YourMaxLength := 80;
  4649.   PakkeNr := 0;
  4650.   ServerTime := 0;
  4651.   PacketDelay := 0;
  4652.   r_code := r_ok;
  4653.  
  4654.   IF ArgC >= 1 THEN BEGIN
  4655.     ShowTimeOut := TRUE;
  4656.     CheckType := 1;
  4657.  
  4658.     Init_Params;
  4659.  
  4660.     st := ArgV[1];
  4661.  
  4662.     IF Pos(st,'SERVER') = 1 THEN Server
  4663.     ELSE IF (Pos(st,'SEND') = 1) AND (ArgC >= 2) THEN SendManyFiles(ArgV[2])
  4664.     ELSE IF Pos(st,'RECEIVE') = 1 THEN ReceiveFiles(RecF,'')
  4665.     ELSE IF (Pos(st,'GET') = 1) AND (ArgC >= 2) THEN ReceiveFiles(GetF,ArgV[2])
  4666.     ELSE BEGIN
  4667.       GotoXY(1,25);
  4668.       WriteLn('Usage: Kermit [SERVER] | [SEND <file>] | [RECEIVE] | [GET <file>');
  4669.       Exit;
  4670.     END;
  4671.   END
  4672.   ELSE BEGIN
  4673.     REPEAT
  4674.       ShowTimeOut := TRUE;
  4675.       CheckType := 1;
  4676.  
  4677.       Meny(key);
  4678.  
  4679.       CASE key OF
  4680.       1 : BEGIN
  4681.             SendManyFiles('');
  4682.             GetF10;
  4683.           END;
  4684.       2 : BEGIN
  4685.             ReceiveFiles(RecF,'');
  4686.             GetF10;
  4687.           END;
  4688.       3 : BEGIN
  4689.             ReceiveFiles(GetF,'');
  4690.             GetF10;
  4691.           END;
  4692.       4 : Server;
  4693.       5 : SaveParam;
  4694.       6 : HostCommand;
  4695.       7 : BEGIN
  4696.             GotoXY(1,25); WriteLn; CursorOn; Exec(FindEnv('COMSPEC='),'');
  4697.             IF DosError <> 0 THEN BEGIN
  4698.               WriteLn('EXEC error # ',DosError);
  4699.               Delay(2000);
  4700.             END;
  4701.           END;
  4702.       8 : BEGIN
  4703.             GotoXY(1,25);
  4704.             ClrEol;
  4705.             GotoXY(72,25); Write('F10-Exit');
  4706.             Window(1,18,80,24);
  4707.             ClrScr;
  4708.             CursorOn;
  4709.             Terminal;
  4710.             Window(1,1,80,25);
  4711.           END;
  4712.       9 : FinishServer;
  4713.       END;
  4714.     UNTIL key = 10;
  4715.   END;
  4716.   Release(heap);
  4717. END;                         { Kermit }
  4718.  
  4719. VAR
  4720.   ok : BOOLEAN;
  4721.   ch : CHAR;
  4722.   key : WORD;
  4723.  
  4724. CONST
  4725.   US_Tab : ARRAY [1..6] OF CHAR = '[\]{|}';
  4726.   NO_Tab : ARRAY [1..6] OF CHAR = '';
  4727.  
  4728. BEGIN                                  {Kermits}
  4729. {  CheckBreak := FALSE; }
  4730.   FileMode := 0;
  4731.  
  4732.   OrigText := TextAttr;
  4733.   OrigMenu := OrigText XOR 8;
  4734.   OrigField := FeltAttr;
  4735.   OrigEdit := EditAttr;
  4736.  
  4737.   GetDir(0,StartPath); DownLoadPath := StartPath;
  4738.  
  4739.   FOR ch := #0 TO #255 DO InnConvert[ch] := ch;
  4740.   UtConvert := InnConvert;
  4741.   FOR key := 1 TO 6 DO BEGIN
  4742.     InnConvert[US_Tab[key]] := NO_Tab[key];
  4743.     UtConvert[NO_Tab[key]] := US_Tab[key];
  4744.   END;
  4745.  
  4746.   RS_MakeBuffer($1000,0,0,0,0);        {Use same buffers for all ports!}
  4747.  
  4748.   MakeStr(4,5,64,LeftJ,'Current Dir: ',DownLoadPath,Addr(FileNameSet),ToUpper);
  4749.  
  4750.   MakeLong(10,7,6,LeftJ,'Baud: ',CurBaud,2,115200);
  4751.   MakeWord(10,8,1,LeftJ,'Bits: ',CurBits,7,8);
  4752.   MakeEnum(8,9,5,CenterJ,'Parity: ',CurParity,5,ParityStr);
  4753.   MakeWord(5,10,1,LeftJ,'Stop Bits: ',CurStop,1,2);
  4754.   MakeWord(6,11,1,LeftJ,'Com Port: ',CurComPort,1,4);
  4755.  
  4756.   MakeWord(32,7,4,LeftJ, 'Max Packet: ',LongMaxLength,20,9020);
  4757.   MakeWord(32,8,2,LeftJ, 'Max Window: ',WinSize,0,31);
  4758.   MakeWord(28,9,3,LeftJ, 'Packet Timeout: ',MyTimeOut,0,120);
  4759.   MakeWord(28,10,3,LeftJ,'Server Timeout: ',ServerTime,0,500);
  4760.   MakeByte(32,11,1,LeftJ,'Check Type: ',FileCheck,1,3);
  4761.  
  4762.   MakeBool(58,7,5,LeftJ, 'Long Packets: ',LongPakke);
  4763.   MakeBool(56,8,5,LeftJ, 'Sliding Window: ',WindowData);
  4764.   MakeEnum(61,9,4,LeftJ,  'File Type: ',TextFile,2,BinText);
  4765.   MakeEnum(62,10,3,LeftJ,  'IBM Mode: ',IBM_Mode,3,Std_IBM);
  4766.   MakeBool(60,11,5,LeftJ,'High Speed: ',BinaryData);
  4767.  
  4768.   MakeByte(2,13,2,LeftJ, 'Packet Start: ',BYTE(MySOH),1,31);
  4769.   MakeByte(4,14,2,LeftJ, 'Packet End: ',BYTE(MyCR),1,31);
  4770.   MakeChar(4,15,1,LeftJ, 'Ctl Prefix: ',MyQCtrlChar,NIL,0);
  4771.   MakeChar(3,16,1,LeftJ, '8bit Prefix: ',Q8bitChar,NIL,0);
  4772.   MakeChar(4,17,1,LeftJ, 'Rep Prefix: ',QrepChar,NIL,0);
  4773.  
  4774.   MakeEnum(34,15,10,CenterJ,' No Date: ',DupHandle,3,DupString);
  4775.   MakeEnum(34,16,10,CenterJ,'Old File: ',OldDupHandle,3,DupString);
  4776.   MakeEnum(34,17,10,CenterJ,'New File: ',NewDupHandle,3,DupString);
  4777.  
  4778.   MakeByte(60,13,3,LeftJ, 'Text Color: ',KermitAttr,0,255);
  4779.   MakeByte(60,14,3,LeftJ, 'Menu Color: ',MenuAttr,0,255);
  4780.   MakeByte(59,15,3,LeftJ,'Field Color: ',FieldAttr,0,255);
  4781.   MakeByte(60,16,3,LeftJ, 'Edit Color: ',EditAttr,0,255);
  4782.  
  4783.   MakeBool(58,17,5,LeftJ,'Direct Video: ',DirVideo);
  4784.  
  4785.   IF NOT GetParam THEN Halt(1);
  4786.  
  4787.   DirectVideo := DirVideo;
  4788.   ClrScr;        {Keep current screen colors!}
  4789.  
  4790.   CursorOff;
  4791.   Kermit;
  4792.   CursorOn;
  4793.  
  4794.   RS_Stop(CurComPort);
  4795.   ChDir(StartPath);
  4796.   GotoXY(1,25);
  4797. END.
  4798. <<< mydos.pas >>>
  4799. {$R-,S-}
  4800.  
  4801. Unit MyDos;
  4802.  
  4803. Interface
  4804.  
  4805. CONST
  4806.   IO_CTRL = $4000;
  4807.   IO_ISDEV  = $80;
  4808.   IO_EOF    = $40;
  4809.   IO_BINARY = $20;
  4810.   IO_ISCLK  =   8;
  4811.   IO_ISNUL  =   4;
  4812.   IO_ISCOT  =   2;
  4813.   IO_ISCIN  =   1;
  4814.  
  4815.   StdIn     =   0;
  4816.   StdOut    =   1;
  4817.   StdErr    =   2;
  4818.   StdLst    =   3;
  4819.   StdAux    =   4;
  4820.  
  4821. TYPE DiskInfo = RECORD
  4822.        Avail_Clu, Total_Clu, BytPrSec, SecPrClu : WORD;
  4823.      END;
  4824. const
  4825.   { Flags bit masks }
  4826.   FCarry     = $0001;
  4827.   FParity    = $0004;
  4828.   FAuxiliary = $0010;
  4829.   FZero      = $0040;
  4830.   FSign      = $0080;
  4831.   FOverflow  = $0800;
  4832.  
  4833.   { File attribute constants }
  4834.   ReadOnly  = $01;
  4835.   Hidden    = $02;
  4836.   SysFile   = $04;
  4837.   VolumeID  = $08;
  4838.   Directory = $10;
  4839.   Archive   = $20;
  4840.   AnyFile   = $3F;
  4841.  
  4842. type
  4843.   { Search record used by FindFirst and FindNext }
  4844.   SearchRec = record
  4845.                 Fill: array[1..21] of Byte;
  4846.                 Attr: Byte;
  4847.                 Time: Longint;
  4848.                 Size: Longint;
  4849.                 Name: string[12];
  4850.               end;
  4851.  
  4852.   { Date and time record used by PackTime and UnpackTime }
  4853.   DateTime = record
  4854.                Year,Month,Day,Hour,Min,Sec: Word;
  4855.              end;
  4856.   String4 = String[4];
  4857.  
  4858. VAR DosError : WORD;
  4859.  
  4860. procedure GetFAttr(var F; var Attr: Word);
  4861. procedure SetFAttr(var F; Attr: Word);
  4862. procedure UnpackTime(P: Longint; var T: DateTime);
  4863. procedure PackTime(var T: DateTime; var P: Longint);
  4864. PROCEDURE ExecEnv(Path,CmdLine: String; environ : Pointer);
  4865.  
  4866. PROCEDURE GetTime(VAR hour, min, sec, s100 : WORD);
  4867. PROCEDURE GetDate(VAR year, month, day, dow : WORD);
  4868. PROCEDURE SetTime(hour, min, sec, s100 : WORD);
  4869. PROCEDURE SetDate(year, month, day : WORD);
  4870. PROCEDURE GetIntVec(nr : BYTE; VAR p : Pointer);
  4871. PROCEDURE SetIntVec(nr : BYTE; p : Pointer);
  4872. PROCEDURE FindFirst(path : String; attr : WORD; VAR dta : SearchRec);
  4873. PROCEDURE FindNext(VAR dta: SearchRec);
  4874. PROCEDURE GetFTime(VAR fil; VAR time : LongInt);
  4875. PROCEDURE SetFTime(VAR fil; time : LongInt);
  4876. FUNCTION  GetDevStat(handle : WORD) : WORD;
  4877. PROCEDURE GetDiskInfo(drive : WORD; VAR dinfo : DiskInfo);
  4878.  
  4879. FUNCTION DosVersion: WORD;
  4880. PROCEDURE Exec(Path,CmdLine: String);
  4881. FUNCTION FindEnv(find : String) : String;
  4882.  
  4883. PROCEDURE PutString(st : String);
  4884. FUNCTION Hex(w : Word): String4;
  4885. PROCEDURE ShrinkHeap;
  4886. PROCEDURE Move(VAR fra, til; bytes : WORD);
  4887.  
  4888. Implementation
  4889.  
  4890. PROCEDURE Move(VAR fra, til; bytes : WORD); {Erstatter SYSTEM:MOVE}
  4891. BEGIN
  4892. Inline(
  4893.   $1E                    {  push ds                ;}
  4894.   /$C5/$76/<FRA          {  lds si,<fra[bp]        ;}
  4895.   /$C4/$7E/<TIL          {  les di,<til[bp]        ;}
  4896.   /$FC                   {  cld                    ;}
  4897.   /$8B/$4E/<BYTES        {  mov cx,<bytes[bp]      ;}
  4898.   /$E3/$38               {  jcxz done              ;}
  4899.   /$39/$FE               {  cmp si,di              ;}
  4900.   /$77/$21               {    ja moveup            ;}
  4901.   /$FD                   {  std                    ;}
  4902.   /$89/$C8               {  mov ax,cx              ;}
  4903.   /$48                   {  dec ax                 ;}
  4904.   /$01/$C6               {  add si,ax              ;}
  4905.   /$01/$C7               {  add di,ax              ;}
  4906.   /$F7/$C6/$01/$00       {  test si,1              ;}
  4907.   /$75/$02               {    jnz dnw              ;}
  4908.   /$A4                   {  movsb                  ;}
  4909.   /$49                   {  dec cx                 ;}
  4910.                          {dnw:                     ;}
  4911.   /$4E                   {  dec si                 ;}
  4912.   /$4F                   {  dec di                 ;}
  4913.   /$D1/$E9               {  shr cx,1               ;}
  4914.   /$9F                   {  lahf                   ;}
  4915.   /$E3/$02               {    jcxz dnwd            ;}
  4916.   /$F2/$A5               {  rep movsw              ;}
  4917.   /$9E                   {dnwd: sahf               ;}
  4918.   /$73/$18               {    jnc done             ;}
  4919.   /$46                   {  inc si                 ;}
  4920.   /$47                   {  inc di                 ;}
  4921.   /$A4                   {  movsb                  ;}
  4922.   /$EB/$13               {    jmp short done       ;}
  4923.   /$F7/$C6/$01/$00       {moveup: test si,1        ;}
  4924.   /$74/$02               {    jz upw               ;}
  4925.   /$A4                   {  movsb                  ;}
  4926.   /$49                   {  dec cx                 ;}
  4927.   /$D1/$E9               {upw: shr cx,1            ;}
  4928.   /$9F                   {  lahf                   ;}
  4929.   /$E3/$02               {    jcxz upwd            ;}
  4930.   /$F2/$A5               {  rep movsw              ;}
  4931.   /$9E                   {upwd: sahf               ;}
  4932.   /$73/$01               {    jnc done             ;}
  4933.   /$A4                   {  movsb                  ;}
  4934.   /$1F                   {done: pop ds             ;}
  4935. );
  4936. END;                                   {Move}
  4937.  
  4938. FUNCTION DosVersion: WORD;
  4939. BEGIN
  4940. Inline(
  4941.   $B4/$30                {mov ah,$30}
  4942.   /$CD/$21               {int $21}
  4943.   /$86/$E0               {xchg al,ah}
  4944.   /$89/$46/$FE           {mov [bp-2],ax}
  4945. );
  4946. END;
  4947.  
  4948. PROCEDURE ShrinkHeap;
  4949. BEGIN
  4950. Inline(
  4951.   $8B/$1E/>HEAPPTR       {mov bx,[>HeapPtr]}
  4952.   /$81/$C3/$0F/$00       {add bx,15}
  4953.   /$B1/$04               {mov cl,4}
  4954.   /$D3/$EB               {shr bx,cl}
  4955.   /$03/$1E/>HEAPPTR+2    {add bx,[>HeapPtr+2]}
  4956.   /$89/$D8               {mov ax,bx}
  4957.   /$2D/$00/$10           {sub ax,$1000}
  4958.   /$A3/>FREEPTR+2        {mov [>FreePtr+2],ax}
  4959.   /$31/$C0               {xor ax,ax}
  4960.   /$A3/>FREEPTR          {mov [>FreePtr],ax}
  4961.   /$B4/$4A               {mov ah,$4A}
  4962.   /$8E/$06/>PREFIXSEG    {mov es,[>PrefixSeg]}
  4963.   /$2B/$1E/>PREFIXSEG    {sub bx,[>PrefixSeg]}
  4964.   /$CD/$21               {int $21}
  4965. );
  4966. END;
  4967.  
  4968. FUNCTION Hex(w : Word): String4;
  4969. CONST HexCh : ARRAY [0..15] OF CHAR = '0123456789ABCDEF';
  4970. VAR   h : String4;
  4971. BEGIN
  4972.   h[0] := #4;
  4973.   h[1] := HexCh[Hi(w) Shr 4];
  4974.   h[2] := HexCh[Hi(w) AND 15];
  4975.   h[3] := HexCh[Lo(w) Shr 4];
  4976.   h[4] := HexCh[Lo(w) AND 15];
  4977.   Hex := h;
  4978. END;
  4979.  
  4980. PROCEDURE SetTime(hour, min, sec, s100 : WORD);
  4981. BEGIN
  4982. Inline(
  4983.   $8A/$56/<S100          {mov dl,[bp+<s100]}
  4984.   /$8A/$76/<SEC          {mov dh,[bp+<sec]}
  4985.   /$8A/$4E/<MIN          {mov cl,[bp+<min]}
  4986.   /$8A/$6E/<HOUR         {mov ch,[bp+<hour]}
  4987.   /$B4/$2D               {mov ah,$2D}
  4988.   /$CD/$21               {int $21}
  4989. );
  4990. END;
  4991.  
  4992. PROCEDURE SetDate(year, month, day : WORD);
  4993. BEGIN
  4994. Inline(
  4995.   $8B/$4E/<YEAR          {mov cx,[bp+<year]}
  4996.   /$8A/$76/<MONTH        {mov dh,[bp+<month]}
  4997.   /$8A/$56/<DAY          {mov dl,[bp+<day]}
  4998.   /$B4/$2B               {mov ah,$2B}
  4999.   /$CD/$21               {int $21}
  5000. );
  5001. END;
  5002.  
  5003. PROCEDURE PutString(st : String);
  5004. BEGIN
  5005. Inline(
  5006.   $B4/$40                {mov ah,$40}
  5007.   /$BB/$01/$00           {mov bx,1}
  5008.   /$8A/$8E/>ST           {mov cl,[bp+>st]}
  5009.   /$30/$ED               {xor ch,ch}
  5010.   /$8D/$96/>ST+1         {lea dx,[bp+>st+1]}
  5011.   /$1E                   {push ds}
  5012.   /$16                   {push ss}
  5013.   /$1F                   {pop ds}
  5014.   /$CD/$21               {int $21}
  5015.   /$1F                   {pop ds}
  5016. );
  5017. END;
  5018.  
  5019. PROCEDURE UnpackTime(P: Longint; var T: DateTime);
  5020. BEGIN
  5021. Inline(
  5022.   $8B/$56/<P+2           {mov dx,[bp+<p+2]}
  5023.   /$C4/$7E/<T            {les di,[bp+<t]}
  5024.   /$FC                   {cld}
  5025.   /$B9/$09/$00           {mov cx,9}
  5026.   /$89/$D0               {mov ax,dx}
  5027.   /$D3/$E8               {shr ax,cl}
  5028.   /$05/$BC/$07           {add ax,1980}
  5029.   /$AB                   {stosw}
  5030.   /$B1/$05               {mov cl,5}
  5031.   /$89/$D0               {mov ax,dx}
  5032.   /$D3/$E8               {shr ax,cl}
  5033.   /$25/$0F/$00           {and ax,15}
  5034.   /$AB                   {stosw}
  5035.   /$89/$D0               {mov ax,dx}
  5036.   /$25/$1F/$00           {and ax,31}
  5037.   /$AB                   {stosw}
  5038.   /$8B/$56/<P            {mov dx,[bp+<p]}
  5039.   /$89/$D0               {mov ax,dx}
  5040.   /$B1/$0B               {mov cl,11}
  5041.   /$D3/$E8               {shr ax,cl}
  5042.   /$AB                   {stosw}
  5043.   /$89/$D0               {mov ax,dx}
  5044.   /$B1/$05               {mov cl,5}
  5045.   /$D3/$E8               {shr ax,cl}
  5046.   /$25/$3F/$00           {and ax,63}
  5047.   /$AB                   {stosw}
  5048.   /$89/$D0               {mov ax,dx}
  5049.   /$D1/$E0               {shl ax,1}
  5050.   /$25/$3F/$00           {and ax,63}
  5051.   /$AB                   {stosw}
  5052. );
  5053. END;
  5054.  
  5055. PROCEDURE PackTime(VAR T : DateTime; VAR P: LongInt);
  5056. BEGIN
  5057. Inline(
  5058.   $1E                    {push ds}
  5059.   /$C5/$76/<T            {lds si,[bp+<T]}
  5060.   /$FC                   {cld}
  5061.   /$C4/$7E/<P            {les di,[bp+<P]}
  5062.   /$AD                   {lodsw                      ; year}
  5063.   /$2D/$BC/$07           {sub ax,1980}
  5064.   /$B9/$09/$00           {mov cx,9}
  5065.   /$D3/$E0               {shl ax,cl}
  5066.   /$89/$C2               {mov dx,ax}
  5067.   /$AD                   {lodsw                      ; month}
  5068.   /$B1/$05               {mov cl,5}
  5069.   /$D3/$E0               {shl ax,cl}
  5070.   /$01/$C2               {add dx,ax}
  5071.   /$AD                   {lodsw                      ; day}
  5072.   /$01/$D0               {add ax,dx}
  5073.   /$26/$89/$45/$02       {es: mov [di+2],ax}
  5074.   /$AD                   {lodsw                      ; hour}
  5075.   /$B1/$0B               {mov cl,11}
  5076.   /$D3/$E0               {shl ax,cl}
  5077.   /$89/$C2               {mov dx,ax}
  5078.   /$AD                   {lodsw                      ; min}
  5079.   /$B1/$05               {mov cl,5}
  5080.   /$D3/$E0               {shl ax,cl}
  5081.   /$01/$C2               {add dx,ax}
  5082.   /$AD                   {lodsw                      ; sec}
  5083.   /$D1/$E8               {shr ax,1}
  5084.   /$01/$D0               {add ax,dx}
  5085.   /$AB                   {stosw}
  5086. );
  5087. END;
  5088.  
  5089. PROCEDURE ExecEnv(Path,CmdLine: String; environ : Pointer); EXTERNAL;
  5090. {$L ExecEnv.obj}
  5091.  
  5092. PROCEDURE Exec(Path,CmdLine: String);
  5093. BEGIN
  5094.   ExecEnv(Path,CmdLine,NIL);
  5095. END;
  5096.  
  5097. PROCEDURE SetFAttr(var F; Attr: Word);
  5098. BEGIN
  5099. Inline(
  5100.   $B8/$01/$43            {mov ax,$4301}
  5101.   /$1E                   {push ds}
  5102.   /$C5/$56/<F            {lds dx,[bp+<f]}
  5103.   /$81/$C2/$30/$00       {add dx,48}
  5104.   /$8B/$4F/<ATTR         {mov cx,[bx+<attr]}
  5105.   /$CD/$21               {int $21}
  5106.   /$1F                   {pop ds}
  5107.   /$72/$02               {jc g1}
  5108.   /$31/$C0               {xor ax,ax}
  5109.                          {g1:}
  5110.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5111. );
  5112. END;                                   {SetFAttr}
  5113.  
  5114. PROCEDURE GetFAttr(var F; var Attr: Word);
  5115. BEGIN
  5116. Inline(
  5117.   $B8/$00/$43            {mov ax,$4300}
  5118.   /$1E                   {push ds}
  5119.   /$C5/$56/<F            {lds dx,[bp+<f]}
  5120.   /$81/$C2/$30/$00       {add dx,48}
  5121.   /$CD/$21               {int $21}
  5122.   /$1F                   {pop ds}
  5123.   /$72/$02               {jc g1}
  5124.   /$31/$C0               {xor ax,ax}
  5125.                          {g1:}
  5126.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5127.   /$C4/$5E/<ATTR         {les bx,[bp+<attr]}
  5128.   /$26/$89/$0F           {es: mov [bx],cx}
  5129. );
  5130. END;                                   {GetFAttr}
  5131.  
  5132. PROCEDURE GetDiskInfo(drive : WORD; VAR dinfo : DiskInfo);
  5133. BEGIN
  5134. Inline(
  5135.   $B4/$36                {mov ah,$36}
  5136.   /$8A/$56/<DRIVE        {mov dl,[bp+<drive]}
  5137.   /$CD/$21               {int $21}
  5138.   /$C4/$7E/<DINFO        {les di,[bp+<dinfo]}
  5139.   /$26/$89/$1D           {es: mov [di],bx}
  5140.   /$26/$89/$55/$02       {es: mov [di+2],dx}
  5141.   /$26/$89/$4D/$04       {es: mov [di+4],cx}
  5142.   /$26/$89/$45/$06       {es: mov [di+6],ax}
  5143. );
  5144. END;                                   {GetDiskInfo}
  5145.  
  5146. FUNCTION  GetDevStat(handle : WORD) : WORD;
  5147. BEGIN
  5148. Inline(
  5149.   $B8/$00/$44            {mov ax,$4400}
  5150.   /$8B/$5E/<HANDLE       {mov bx,[bp+<handle]}
  5151.   /$CD/$21               {int $21}
  5152.   /$72/$02               {jc g1}
  5153.   /$31/$C0               {xor ax,ax}
  5154.                          {g1:}
  5155.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5156.   /$89/$56/$FE           {mov [bp-2],dx}
  5157. );
  5158. END;                                   {GetDevStat}
  5159.  
  5160. PROCEDURE GetTime(VAR hour, min, sec, s100 : WORD);
  5161. BEGIN
  5162. Inline(
  5163.   $B4/$2C                {mov ah,$2C}
  5164.   /$CD/$21               {int $21}
  5165.   /$31/$C0               {xor ax,ax}
  5166.   /$C4/$5E/<HOUR         {les bx,[bp+<hour]}
  5167.   /$88/$E8               {mov al,ch}
  5168.   /$26/$89/$07           {es: mov [bx],ax}
  5169.   /$C4/$5E/<MIN          {les bx,[bp+<min]}
  5170.   /$88/$C8               {mov al,cl}
  5171.   /$26/$89/$07           {es: mov [bx],ax}
  5172.   /$C4/$5E/<SEC          {les bx,[bp+<sec]}
  5173.   /$88/$F0               {mov al,dh}
  5174.   /$26/$89/$07           {es: mov [bx],ax}
  5175.   /$C4/$5E/<S100         {les bx,[bp+<s100]}
  5176.   /$88/$D0               {mov al,dl}
  5177.   /$26/$89/$07           {es: mov [bx],ax}
  5178. );
  5179. END;                                   {GetTime}
  5180.  
  5181. PROCEDURE GetDate(VAR year, month, day, dow : WORD);
  5182. BEGIN
  5183. Inline(
  5184.   $B4/$2A                {mov ah,$2A}
  5185.   /$CD/$21               {int $21}
  5186.   /$30/$E4               {xor ah,ah}
  5187.   /$C4/$5E/<DOW          {les bx,[bp+<dow]}
  5188.   /$26/$89/$07           {es: mov [bx],ax}
  5189.   /$C4/$5E/<YEAR         {les bx,[bp+<year]}
  5190.   /$26/$89/$0F           {es: mov [bx],cx}
  5191.   /$C4/$5E/<MONTH        {les bx,[bp+<month]}
  5192.   /$88/$F0               {mov al,dh}
  5193.   /$26/$89/$07           {es: mov [bx],ax}
  5194.   /$C4/$5E/<DAY          {les bx,[bp+<day]}
  5195.   /$88/$D0               {mov al,dl}
  5196.   /$26/$89/$07           {es: mov [bx],ax}
  5197. );
  5198. END;                                   {GetDate}
  5199.  
  5200. VAR IntVectorTable : ARRAY [BYTE] OF Pointer ABSOLUTE 0:0;
  5201.  
  5202. PROCEDURE GetIntVec(nr : BYTE; VAR p : Pointer);
  5203. BEGIN
  5204.   p := IntVectorTable[nr];
  5205. END;
  5206.  
  5207. PROCEDURE SetIntVec(nr : BYTE; p : Pointer);
  5208. BEGIN
  5209.   InLine($FA);
  5210.   IntVectorTable[nr] := p;
  5211.   InLine($FB);
  5212. END;
  5213.  
  5214. PROCEDURE FindFirst(path : String; attr : WORD; VAR dta : SearchRec);
  5215. BEGIN
  5216. Inline(
  5217.   $1E                    {push ds}
  5218.   /$C5/$56/<DTA          {lds dx,[bp+<dta]}
  5219.   /$B4/$1A               {mov ah,$1A}
  5220.   /$CD/$21               {int $21}
  5221.   /$16                   {push ss}
  5222.   /$1F                   {pop ds}
  5223.   /$8D/$96/>PATH         {lea dx,[bp+>path]}
  5224.   /$89/$D3               {mov bx,dx}
  5225.   /$42                   {inc dx}
  5226.   /$8A/$1F               {mov bl,[bx]}
  5227.   /$30/$FF               {xor bh,bh}
  5228.   /$01/$D3               {add bx,dx}
  5229.   /$C6/$07/$00           {mov byte ptr [bx],0}
  5230.   /$8B/$4E/<ATTR         {mov cx,[bp+<attr]}
  5231.   /$B4/$4E               {mov ah,$4E}
  5232.   /$CD/$21               {int $21}
  5233.   /$72/$22               {jc done}
  5234.   /$C4/$7E/<DTA          {les di,[bp+<dta]}
  5235.   /$8E/$5E/<DTA+2        {mov ds,[bp+<dta+2]}
  5236.   /$81/$C7/$1E/$00       {add di,30}
  5237.   /$30/$C0               {xor al,al}
  5238.   /$FC                   {cld}
  5239.   /$B9/$FF/$FF           {mov cx,-1}
  5240.   /$F2/$AE               {repne scasb}
  5241.   /$F7/$D1               {not cx}
  5242.   /$49                   {dec cx}
  5243.   /$4F                   {dec di}
  5244.   /$8D/$75/$FF           {lea si,[di-1]}
  5245.   /$FD                   {std}
  5246.   /$88/$C8               {mov al,cl}
  5247.   /$F2/$A4               {rep movsb}
  5248.   /$88/$05               {mov [di],al}
  5249.   /$31/$C0               {xor ax,ax}
  5250.                          {done:}
  5251.   /$1F                   {pop ds}
  5252.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5253. );
  5254. END;                                   {FindFirst}
  5255.  
  5256. PROCEDURE FindNext(VAR dta: SearchRec);
  5257. BEGIN
  5258. Inline(
  5259.   $1E                    {push ds}
  5260.   /$C5/$56/<DTA          {lds dx,[bp+<dta]}
  5261.   /$B4/$1A               {mov ah,$1A}
  5262.   /$CD/$21               {int $21}
  5263.   /$B4/$4F               {mov ah,$4F}
  5264.   /$CD/$21               {int $21}
  5265.   /$72/$22               {jc done}
  5266.   /$C4/$7E/<DTA          {les di,[bp+<dta]}
  5267.   /$8E/$5E/<DTA+2        {mov ds,[bp+<dta+2]}
  5268.   /$81/$C7/$1E/$00       {add di,30}
  5269.   /$30/$C0               {xor al,al}
  5270.   /$FC                   {cld}
  5271.   /$B9/$FF/$FF           {mov cx,-1}
  5272.   /$F2/$AE               {repne scasb}
  5273.   /$F7/$D1               {not cx}
  5274.   /$49                   {dec cx}
  5275.   /$4F                   {dec di}
  5276.   /$8D/$75/$FF           {lea si,[di-1]}
  5277.   /$FD                   {std}
  5278.   /$88/$C8               {mov al,cl}
  5279.   /$F2/$A4               {rep movsb}
  5280.   /$88/$05               {mov [di],al}
  5281.   /$31/$C0               {xor ax,ax}
  5282.                          {done:}
  5283.   /$1F                   {pop ds}
  5284.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5285. );
  5286. END;                                   {FindNext}
  5287.  
  5288. PROCEDURE GetFTime(VAR fil; VAR time : LongInt);
  5289. BEGIN
  5290. Inline(
  5291.   $B8/$00/$57            {mov ax,$5700}
  5292.   /$C4/$5E/<FIL          {les bx,[bp+<fil]}
  5293.   /$26/$8B/$1F           {es: mov bx,[bx]}
  5294.   /$CD/$21               {int $21}
  5295.   /$72/$0C               {jc done}
  5296.   /$C4/$5E/<TIME         {les bx,[bp+<time]}
  5297.   /$26/$89/$0F           {es: mov [bx],cx}
  5298.   /$26/$89/$57/$02       {es: mov [bx+2],dx}
  5299.   /$31/$C0               {xor ax,ax}
  5300.                          {done:}
  5301.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5302. );
  5303. END;                                   {GetFTime}
  5304.  
  5305. PROCEDURE SetFTime(VAR fil; time : LongInt);
  5306. BEGIN
  5307. Inline(
  5308.   $B8/$01/$57            {mov ax,$5701}
  5309.   /$C4/$5E/<FIL          {les bx,[bp+<fil]}
  5310.   /$26/$8B/$1F           {es: mov bx,[bx]}
  5311.   /$8B/$4E/<TIME         {mov cx,[bp+<time]}
  5312.   /$8B/$56/<TIME+2       {mov dx,[bp+<time+2]}
  5313.   /$CD/$21               {int $21}
  5314.   /$72/$02               {jc done}
  5315.   /$31/$C0               {xor ax,ax}
  5316.                          {done:}
  5317.   /$A3/>DOSERROR         {mov [>DosError],ax}
  5318. );
  5319. END;                                   {SetFTime}
  5320.  
  5321. FUNCTION FindEnv(find : String) : String;
  5322. VAR st : String;
  5323.     cp : ^CHAR;
  5324. BEGIN
  5325.   cp := Ptr(MemW[PrefixSeg:$2C],0);
  5326.   WHILE cp^ <> #0 DO BEGIN
  5327.     st := '';
  5328.     WHILE cp^ <> #0 DO BEGIN
  5329.       Inc(st[0]);
  5330.       st[Length(st)] := cp^;
  5331.       Inc(WORD(cp));
  5332.     END;
  5333.     IF Copy(st,1,Length(find)) = find THEN BEGIN
  5334.       Delete(st,1,Length(find));
  5335.       FindEnv := st;
  5336.       Exit;
  5337.     END;
  5338.     Inc(WORD(cp));
  5339.   END;
  5340.   FindEnv := '';
  5341. END;
  5342.  
  5343. END.
  5344. <<< timers.pas >>>
  5345. {$R-,S-,F+}                                  {No local proc's!}
  5346. Unit Timers;
  5347.  
  5348. Interface
  5349.  
  5350. TYPE
  5351.   TimerTablePtr = ^TimerTableRec;
  5352.   TimerTableRec = RECORD
  5353.     next : TimerTablePtr;
  5354.     count : LongInt;
  5355.     UserInt, active : BOOLEAN;
  5356.   END;
  5357.  
  5358. CONST
  5359.   TimerPtr : TimerTablePtr = NIL;
  5360.  
  5361. VAR SaveExit, OldTimer : Pointer;
  5362.  
  5363. PROCEDURE StartTimer(VAR t : TimerTableRec);
  5364.  
  5365. PROCEDURE StopTimer(VAR t : TimerTableRec);
  5366.  
  5367. FUNCTION  GetTimer(VAR t : TimerTableRec): LongInt;
  5368.  
  5369. FUNCTION  RunningTimer(VAR t : TimerTableRec): BOOLEAN;
  5370.  
  5371. PROCEDURE GetVector(IntNr : WORD; VAR vector: Pointer);
  5372.  
  5373. PROCEDURE SetVector(IntNr : WORD; vector: Pointer);
  5374.  
  5375. Implementation
  5376.  
  5377. VAR IntVectorTable : ARRAY [0..255] OF Pointer ABSOLUTE 0:0;
  5378.  
  5379. PROCEDURE GetVector(IntNr : WORD; VAR vector: Pointer);
  5380. BEGIN
  5381.   vector := IntVectorTable[IntNr];
  5382. END;
  5383.  
  5384. PROCEDURE SetVector(IntNr : WORD; vector: Pointer);
  5385. BEGIN
  5386.   Inline($FA);
  5387.   IntVectorTable[IntNr] := vector;
  5388.   InLine($FB);
  5389. END;
  5390.  
  5391. PROCEDURE StopTimer(VAR t : TimerTableRec);
  5392. VAR tp, ne : TimerTablePtr;
  5393. BEGIN
  5394.   t.active := FALSE;
  5395. {
  5396.   IF TimerPtr = NIL THEN Exit;
  5397.   IF TimerPtr = @t THEN BEGIN
  5398.     Inline($FA);
  5399.     TimerPtr := t.next;
  5400.     Inline($FB);
  5401.     Exit;
  5402.   END;
  5403. }
  5404.   tp := @TimerPtr;
  5405.   ne := TimerPtr;
  5406.   WHILE ne <> NIL DO BEGIN
  5407.     IF ne = @t THEN BEGIN
  5408.       Inline($FA);
  5409.       tp^.next := t.next;
  5410.       Inline($FB);
  5411.       Exit;
  5412.     END;
  5413.     tp := ne;
  5414.     ne := ne^.next;
  5415.   END;
  5416. END;
  5417.  
  5418. PROCEDURE StartTimer(VAR t : TimerTableRec);
  5419. BEGIN
  5420.   StopTimer(t);
  5421.   t.next := TimerPtr;
  5422.   t.active := TRUE;
  5423.   Inline($FA);
  5424.   TimerPtr := @t;
  5425.   Inline($FB);
  5426. END;
  5427.  
  5428. FUNCTION GetTimer(VAR t : TimerTableRec): LongInt;
  5429. BEGIN
  5430.   Inline($FA);
  5431.   GetTimer := t.count;
  5432.   Inline($FB);
  5433. END;
  5434.  
  5435. FUNCTION RunningTimer(VAR t : TimerTableRec): BOOLEAN;
  5436. BEGIN
  5437.   RunningTimer := t.active;
  5438. END;
  5439.  
  5440. PROCEDURE Timer_Int; EXTERNAL; {$L timers.obj}
  5441.  
  5442. PROCEDURE Exit_Timers;
  5443. BEGIN
  5444.   SetVector(8,OldTimer);
  5445.   ExitProc := SaveExit;
  5446. END;
  5447.  
  5448. BEGIN
  5449.   GetVector(8,OldTimer);
  5450.   SetVector(8,@Timer_Int);
  5451.   SaveExit := ExitProc;
  5452.   ExitProc := @Exit_Timers;
  5453. END.
  5454.