{
  This stuff should be just a new filetransfer and -linker system,
  written especially for designing programs on 8-bit Commodore
  computers. ...This is the third issue, as I begun it in Sphinx C--,
  then changed to Borland C++ when C-- set me up. And at last, I've
  kicked out the C++ source and this is already the TP version.
  ...Well, I don't know yet if this will not set me up... :-(

  ...Mankind has got 2 stay with traditions... :-)
                                                                      }

{$E-        Don't emulate floating-point part                         }
{$N-        as we don't need it for this program                      }
{$I-        I/O checking off. Runtime error sux...                    }

{
                            ° °
                        I  °°°°°  Pascal!!! :-)
                            °°°
                             °                                        }

Program ComLink;

Uses Dos;

Const
           PULLUP        = $E4;  {11100100, default value}
           STROBE        = $FE;  {11111110}
           AUTOFD        = $FD;  {11111101}
           SEL_IN        = $F7;  {11110111}
           BIN           = 0;
           OBJ           = 1;
           X1541cable    = 0;
           DISK64Ecable  = 1;
           Parallelcable = 2;

           OK            = 0;
           BadLptPort    = 1;
           BadInputType  = 2;
           BadCableType  = 3;
           BadStart      = 4;
           BadRun        = 5;
           NoFileName    = 6;
           UnknownSw     = 7;

           Error         = 1;

           timeout       = 1;
           brk           = 2;

           FirstPass     = True;
           SecondPass    = False;

Var
           filename      :string[80];
           LPT_base      :word;
           in_type       :byte;
           cable         :byte;
           start         :word;
           run           :word;
           init          :boolean;
           ATN0,ATN1     :byte;
           DATA0,DATA1   :byte;
           CLK0,CLK1     :byte;
           prmmatrx      :array[1..6] of string[80];
           makefile      :text;
           dptr          :pointer;
           i,j,k         :word;
   {        cn            :byte;  {***}
           prmrow        :string;
           nextrow       :string;


Function C2hex(n1,n2,n3,n4:char):word;
Begin
  If ((n1 >= 'A') and (n1 <= 'F')) Then n1:=Chr(Ord(n1)-7);
  If ((n2 >= 'A') and (n2 <= 'F')) Then n2:=Chr(Ord(n2)-7);
  If ((n3 >= 'A') and (n3 <= 'F')) Then n3:=Chr(Ord(n3)-7);
  If ((n4 >= 'A') and (n4 <= 'F')) Then n4:=Chr(Ord(n4)-7);

  C2hex := ((Ord(n1) - 48) shl 12) +
           ((Ord(n2) - 48) shl 8)  +
           ((Ord(n3) - 48) shl 4)  +
           (Ord(n4) - 48);
End;

Function Hex2c(num:word):string;
Var  i,tmp   :byte;

Begin
  For i := 1 To 4 Do Begin
                     tmp := (num shr ((i-1)*4)) and 15;
                     If tmp <= 9 Then Hex2c[5-i] := Chr(tmp+48)
                                 Else Hex2c[5-i] := Chr(tmp+55);
                     End;
  Hex2c[0] := Chr(4);
End;

Function Traceparams(count:byte; pass:boolean):byte;
Var i,j :byte;
    prm :string[80];
    stp :byte;

Begin
 stp := OK;
 in_type          := BIN;
 start            := $ffff;
 If pass Then run := $fffe
         Else run := $ffff;
 filename         := '';
 init             := false;

 i:=1;
 While ((i <= count) and (stp = OK)) Do
   Begin
   prm := prmmatrx[i];
   For j:=1 To Ord(prm[0])+1 Do If ((prm[j]>='a') and (prm[j]<='z'))
                                Then prm[j] := Chr(Ord(prm[j])-32);

   If prm[1] = '-' Then
     Begin
       Case prm[2] of

         'P': Begin
                LPT_base := C2hex('0',prm[3],prm[4],prm[5]);
                If ((LPT_base <> $0378) and
                    (LPT_base <> $0278) and
                    (LPT_base <> $03bc) and
                    (Ord(prm[0]) <> 5))  Then stp := BadLptPort;
              End;

         'C': Begin
               Case prm[3] of
                 'X': cable := X1541cable;
                 'D': cable := DISK64Ecable;
                 'P': cable := Parallelcable;
                Else stp := BadCableType;
               End;
               If Ord(prm[0]) <> 3 Then stp := BadCableType;
              End;

         'I': init := True;

         'T': Begin
               Case prm[3] of
                 'B': in_type := BIN;
                 'H': in_type := OBJ;
                 Else stp := BadInputType;
               End;
               If Ord(prm[0]) <> 3 Then stp := BadInputType;
              End;

         'S': If Ord(prm[0]) = 6
                 Then start := C2hex(prm[3],prm[4],prm[5],prm[6])
                 Else stp := BadStart;

         'R': If Ord(prm[0]) = 6
                 Then run := C2hex(prm[3],prm[4],prm[5],prm[6])
                 Else stp := BadRun;

         Else stp:=UnknownSw;

       End;
     End
   Else
       filename := prm;

 Inc(i);
 End;

 If (filename = '') and (not init) Then stp := NoFileName;
 TraceParams := stp;
End;

Procedure SendError(ErrorNum:byte);
Begin

 Case ErrorNum of

   BadLptPort  : Writeln('Invalid LPT-baseaddress!');

   BadCableType: Writeln('[X]1541, [D]isk64e and [P]arallel are valid!');

   BadInputType: Writeln('Only types of [B]inary and [H]ex are valid!');

   BadStart    : Writeln('Bad start-address!');

   NoFileName  : Writeln('...No filename');

   UnknownSw   : Writeln('Unknown switch or parameter!');

 End;
 If ErrorNum <> NoFileName Then Writeln('Cannot transfer ',filename);
End;

Procedure InitCon;
Begin

 Asm
      mov dx,LPT_base
      add dx,2
      mov al,PULLUP
      out dx,al
 End;

 If cable = X1541cable Then Begin
                            ATN0     := STROBE;
                            ATN1     := STROBE xor $FF;
                            DATA0    := SEL_IN;
                            DATA1    := SEL_IN xor $FF;
                            CLK0     := AUTOFD;
                            CLK1     := AUTOFD xor $FF;
                            End

                       Else Begin
                            ATN0     := SEL_IN;
                            ATN1     := SEL_IN xor $FF;
                            DATA0    := AUTOFD;
                            DATA1    := AUTOFD xor $FF;
                            CLK0     := STROBE;
                            CLK1     := STROBE xor $FF;
                            End;

End;

{Function OutBlock(dptr:pointer; LPT_base:word; cable:byte; size:word):byte;

{ This short routine was used to test if the linker's functions work
proprerly. I suppose, it should remain here, as I bet I'll work on the
linker someday.
The 'cn' global variable belongs also to this function.}
{Var f:file;
    st:string;
    st2:string;
Begin
  OutBlock := OK;
  str(start,st);
  str(cn,st2);
  Assign(f,st+st2+'.dat');
  Rewrite(f,1);
  BlockWrite(f,dptr^,size);
  Close(f);
  Inc(cn);
End;}

Function OutBlock(dptr:pointer; LPT_base:word; cable:byte; size:word):byte;
Var res,s               :byte;
    old_s,old_o         :word;

Begin
  Write('Transferring block: ');

  Asm
      cli
      xor  ax,ax
      mov  es,ax
      mov  ax,es:[20h]
      mov  old_o,ax
      mov  ax,es:[22h]
      mov  old_s,ax
      mov  ax,offset @newint
      mov  es:[20h],ax
      mov  ax,cs
      mov  es:[22h],ax
      mov  s,0
      mov  dx,ss:LPT_base
      add  dx,2
      mov  al,PULLUP
      out  dx,al
      sti

      mov  res,OK
      les  si,dptr
      mov  cx,size

@main:
      mov  bh,es:[si]
      push cx
      call @outbyte
      pop  cx
      jc   @timeout

      cmp  cl,0
      jne  @nopress

      mov  dl,'#'
      mov  ah,2
      int  21h
      mov  ah,1
      int  16h
      jz   @nopress

      mov  ah,0
      int  16h
      mov  res,brk
      jmp  @endtrans

@nopress:
      inc  si
      loop @main

      jmp  @endtrans

@timeout:
      mov  res,timeout
      jmp  @endtrans


@outbyte:
      mov  dx,LPT_base
      cmp  cable,Parallelcable
      jne  @sr

      mov  al,bh
      out  dx,al
      mov  bl,DATA1
      add  dx,2
      cmp  s,0
      jne  @p1
      call @sync1
      jc   @out
      mov  s,1
      ret
@p1:  call @sync2
      jc   @out
      mov  s,0
      ret

@sr:  add  dx,2
      mov  bl,ATN1
      mov  cx,4

@s1:
      mov  al,PULLUP
      or   al,CLK1
      shl  bh,1
      jnc  @0b1
      or   al,DATA1
@0b1: out  dx,al

      call @sync11
      jc   @out

      mov  al,PULLUP
      shl  bh,1
      jnc  @0b2
      or   al,DATA1
@0b2: out  dx,al
      call @sync21
      jc   @out
      loop @s1

@out: ret


@sync1:
      in   al,dx
      and  al,PULLUP
      or   al,CLK1
      out  dx,al

@sync11:
      mov  al,00111000b
      out  43h,al
      xor  al,al     {set 1 s delay, using as 'watchdog'}
      out  40h,al
      out  40h,al
      mov  cs:byte ptr @cnt,18

@c1:  in   al,dx
      and  al,bl     {...and wait while the actual synchron-bit goes up}
      jnz  @ok
      cmp  cs:byte ptr @cnt,0
      jne  @c1

      mov  al,PULLUP
      out  dx,al
      stc
      ret

@sync2:
      in   al,dx
      and  al,PULLUP
      out  dx,al

@sync21:
      mov  al,00111000b
      out  43h,al
      xor  al,al
      out  40h,al
      out  40h,al
      mov  cs:byte ptr @cnt,18

@c4:  in   al,dx
      and  al,bl
      jz   @ok
      cmp  cs:byte ptr @cnt,0
      jne  @c4

      mov  al,PULLUP
      out  dx,al
      stc
      ret

@ok:  clc
      ret

@newint:
      push ax
      cmp  cs:byte ptr @cnt,0
      je   @i1
      dec  cs:byte ptr @cnt
      mov  al,00111000b
      out  43h,al
      mov  al,0ffh
      out  40h,al
      out  40h,al
@i1:  mov  al,20h
      out  20h,al
      pop  ax
      iret

@cnt: db  0


@endtrans:
      cli
      xor  ax,ax
      mov  es,ax
      mov  ax,old_o
      mov  es:[20h],ax
      mov  ax,old_s
      mov  es:[22h],ax
      mov  al,00110100b
      out  43h,al
      xor  al,al
      out  40h,al
      out  40h,al
      sti

      mov  dx,LPT_base
      add  dx,2
      mov  al,PULLUP
      out  dx,al

  End;
  OutBlock := res;
  Writeln;
  If res = timeout Then Writeln('No response from other computer.');
  If res = brk Then     Writeln('Transfer cancelled...');

End;

Function LoadBBlock(dptr:pointer; var f:file; start,run:word):word;
Begin
 LoadBBlock := 0;

 If start = $ffff Then Begin
                       If FileSize(f) > 65524 Then Begin
                          Writeln(filename,' is too big to transfer!');
                          Exit;
                          End;

                       memw[Seg(dptr^):Ofs(dptr^)  ] := FileSize(f)-2;
                       memw[Seg(dptr^):Ofs(dptr^)+2] := run;
                       dptr := Ptr(Seg(dptr^),Ofs(dptr^)+4);

                       Writeln('Loading ',filename,'...');
                       BlockRead(f,dptr^,FileSize(f));
                       If IOResult <> 0 Then Begin
                                             Writeln('I/O error...');
                                             Exit;
                                             End;
                       LoadBBlock := FileSize(f)+4;
                       End

                  Else Begin
                       If FileSize(f) > 65522 Then Begin
                          Writeln(filename,' is too big to transfer!');
                          Exit;
                          End;

                       memw[Seg(dptr^):Ofs(dptr^)  ] := FileSize(f);
                       memw[Seg(dptr^):Ofs(dptr^)+2] := run;
                       memw[Seg(dptr^):Ofs(dptr^)+4] := start;
                       dptr := Ptr(Seg(dptr^),Ofs(dptr^)+6);
                       Writeln('Loading ',filename,'...');
                       BlockRead(f,dptr^,FileSize(f));
                       If IOResult <> 0 Then Begin
                                             Writeln('I/O error...');
                                             Exit;
                                             End;
                       LoadBBlock := FileSize(f)+6;
                       End;
End;

Function LoadHBlock(dptr:pointer; var f:text; run:word):word;
Var arow          :string;
    size,start    :word;
    count,i       :byte;
    cp            :pointer;

Begin

  LoadHBlock := 0;
  size       := 0;
  If nextrow = '' Then Readln(f,arow)
                  Else arow := nextrow;

  count := C2hex('0','0',arow[2],arow[3]);
  If count = 0 Then Exit;
  start := C2hex(arow[4],arow[5],arow[6],arow[7]);
  memw[Seg(dptr^):Ofs(dptr^)+4] := start;
  cp := Ptr(Seg(dptr^),Ofs(dptr^)+6);

  Write('Loading block from $',Hex2C(start));

  Repeat
    For i := 0 To count-1 Do
       memw[Seg(cp^):Ofs(cp^)+i] :=
                            C2hex('0','0',arow[i shl 1 +10],arow[i shl 1+11]);

    cp := Ptr(Seg(cp^),Ofs(cp^)+count);
    Inc(size,count);
    Readln(f,arow);
    If IOResult <>0 Then Begin
                         Writeln;
                         Writeln('I/O error...');
                         Exit;
                         End;

    count := C2hex('0','0',arow[2],arow[3]);

  Until ((count=0) or (C2hex(arow[4],arow[5],arow[6],arow[7])<>start+size)
                   or (size > 65400));

  nextrow := arow;
  memw[Seg(dptr^):Ofs(dptr^)] := size;
  Writeln(' to $',Hex2C(start+size));

  If count = 0 Then memw[Seg(dptr^):Ofs(dptr^)+2] := run
               Else memw[Seg(dptr^):Ofs(dptr^)+2] := $ffff;

  LoadHblock := size+6;
End;

Function TransferFile(dptr:pointer; filename:string; LPT_base:word;
                                    cable,in_type:byte; start,run:word):byte;
Var size    :word;
    bfile   :file;
    hfile   :text;
    Outres  :byte;

Begin
  TransferFile := Error;
  If in_type = BIN Then
     Begin
       Assign(bfile,filename);
       Reset(bfile,1);
       If IOResult <> 0 Then Begin
                             Writeln('Cannot open ',filename,'!');
                             Exit;
                             End;

       size := LoadBBlock(dptr,bfile,start,run);
       Close(bfile);
       If size = 0 Then Exit;
       TransferFile := OutBlock(dptr,LPT_base,cable,size);
     End
  Else
     Begin
       nextrow := '';
       Assign(hfile,filename);
       Reset(hfile);
       Writeln('Loading ',filename,'...');
       If IOResult <> 0 Then Begin
                             Writeln('Cannot open ',filename,'!');
                             Exit;
                             End;
       Repeat
         size := LoadHblock(dptr,hfile,run);
         If size <> 0 Then OutRes := OutBlock(dptr,LPT_base,cable,size);
       Until ((size = 0) or (Outres <> OK));
       Close(hfile);
       If (Outres = OK) Then Transferfile := OK;

     End;
End;

Begin
 {cn:=0; {***}
 Asm
   in  al,061h
   and al,0fch
   or  al,01h
   out 061h,al
 End;

 LPT_base := $0378;
 cable    := Parallelcable;

  Writeln('ComLink V0.96á. (C)1996 H rsfalvi Levente');
{ASSUME  Versionnumber = (Year-1900)/100   =) }
  Writeln;
  If ParamCount = 0 Then
      Begin
       Writeln('Usage: COMLINK [filename||makefile.mak] [-pxxx] [-cx] [-tx] [-sxxxx] [-rxxxx]');
       Writeln('Or   : COMLINK -i [-pxxx]');
       Writeln('-p: LPT base-address');
       Writeln('-c: [X]1541, [D]isk64E or [P]arallel cable');
       Writeln('-t: [B]inary or [H]exadecimal (object) input-file');
       Writeln('-s: Start address (hex) in the other computers memory');
       Writeln('-r: Run code from hex xxxx');
       Writeln('-i: Just initialize LPT to leave I/O lines.');
       Writeln('You can use some special values as');
       Writeln('-SFFFF : ComLink will use the first bytes of the file as start-address.');
       Writeln('-RFFFE : Back to Basic after transferring.');
       Writeln('-RFFFF : Skip start-procedure after transferring.');
       Writeln('Defaults : -P378 -CP -TB -SFFFF -RFFFF');
       Halt;
       End;

  If ParamCount > 6 Then
      Begin
       Writeln('Too many parameters!');
       Halt;
      End;

  For i:=1 To ParamCount Do prmmatrx[i]:=ParamStr(i);

  j := TraceParams(ParamCount,FirstPass);

  If j <> OK Then Begin
                  SendError(j);
                  Halt;
                  End;

  If init Then Begin
               Asm
                  mov dx,LPT_base
                  add dx,2
                  mov al,PULLUP
                  out dx,al
               End;
               Writeln('Portbits initialized.');
               Halt;
               End;

  If MaxAvail < 65528 Then Begin
                           Writeln('Not enough memory!');
                           Halt;
                           End;
  GetMem(dptr,65528);
  InitCon;

  i := 1;
  While ((filename[i] <> '.') and ( i<= Ord(filename[0]))) Do Inc(i);

  If Copy(filename,i+1,3) = 'MAK'
   Then
    Begin
    Assign(makefile,filename);
    Reset(makefile);
    Repeat
      Readln(makefile,prmrow);
      i := 1;
      k := 0;
      While(i <= Ord(prmrow[0])) Do
        Begin
          If prmrow[i] <> ' ' Then
             Begin
             Inc(k);
             j := 0;
             While ((prmrow[i+j] <> ' ') and ((i+j) <= Ord(prmrow[0]))) Do
              Begin
                prmmatrx[k,j+1] := prmrow[i+j];
                Inc(j);
              End;
             prmmatrx[k,0] := Chr(j);
             Inc(i,j);
             End;
        Inc(i);
        End;

      j := TraceParams(k,SecondPass);
      If j = OK Then j := TransferFile(dptr,filename,LPT_base,cable,in_type,start,run)
                Else SendError(j);

    Until (Eof(makefile) or (j <> OK));
    End

   Else
     j := TransferFile(dptr,filename,LPT_base,cable,in_type,start,run);

  FreeMem(dptr,65528);

End.
