/*
	CLDEMO.C
   
   Demonstration of the communications routines for the
   Protocol Switch, Little Giant and Tiny Giant. These
   routines retrieve the time of day from the target system.

   Data buffers are laid out as follows:

   buffer #2 - a flag that is set to request the time of
               day and cleared when the time of day is placed
               in buffer #3
               
   buffer #3 - holds a 7-byte structure holding the time and date

	Compile in small memory model for DOS in Borland C/C++.
*/

void clink_init(void);
void clink_initswt(void);

int clink_setflag(char buf_num);
int clink_clearflag(char buf_num);
int clink_testflag(char buf_num, char *flagptr);
int clink_to_buf(char buf_num, int count, char *buf);
int clink_from_buf(char buf_num, int count, char *buf);

int main(void)
{
char flgptr;
char timer[10];

   flgptr=0;
   clink_init();           // initialize the parallel port 
// clink_initswt();      // hardware initialize of protocol switch 

   delay(1000); // wait 1 second 
   
   // set the flag to signal remote to get time 

   if( clink_setflag(2) ) {

      printf("\nerror...\n");
      return(-1); // clink functions return 1 if error
   }

   printf("\nPress Control+C to exit\n\n");

   while(1)
   {
      delay(200);
   
      // wait for the flag to clear 

      while( flgptr) 
      {
         if( clink_testflag (2, &flgptr)) 
            break;
      }
      
      if( clink_from_buf (3, 7, timer))      // read the time 
         break;

      printf("Hour:Minute:Second %2d:%2d:%2d\r",
	      timer[2], timer[1], timer[0]);
               
      if( clink_setflag(2))       // ask for the time again 
         break;
   }
   
   printf("\ndone\n");
   return(0);
}

/*

The following routines are used to communicate with a slave
processor connected to the printer port of a PC. The slave processor
will generally be one of the Z-World miniature controllers, such
as the Little Giant, Tiny Giant or Protocol Switch.

These routines are designed to be assembled under Borland-C/C++
development tools for the IBM-PC.

The following calls are available:

void clink_init() - Initialize the printer parallel port.
void clink_initswt() - Reset the protocol switch (lasts 50 milliseconds).

Each of the following routines returns 0 for success, 1 for failure.

clink_setflag( char buf_num) 
            - Set a flag specified by the buffer number to true (1).

clink_clearflag(  char buf_num)
            - Set a flag specified by the buffer number to false (0)

clink_testflag(  char buf_num,char *flgptr);
            - Return 1 or 0 in flagptr depending on state of flag
              specified by the buffer number.

int clink_to_buf(  char buf_num, int count, char *buf);
            - Send count characters from buf to the buffer specified by
              buf_num. IMPORTANT: the buffer in the PC must have 2 extra bytes
              (i.e. be 2 bytes longer than count. Data is stored in these
              bytes by clink_to_buf.)
              
int clink_from_buf(  char buf_num, int count, char *buf);
            - Read count characters to buf from the buffer specified by
              buf_num. Important- the buffer in the PC must be at least 1 byte
              longer than count and a terminal null will be placed after
              the last byte of the message received.
*/

//#define NO_TIMEOUT 1 // uncomment to inhibit timeout for debugging

int pport=0x378;  // define address of port
//int pport=0x3be // alternate printer port

unsigned int timeout_count= 8000; // timeout counter, microseconds to wait

char strobelow=5;   // to store at 37ah in printer adapter
char strobehigh=4;
char initlow=0;     // initialize the remote unit
long error_count;

/*
	Initialize the Protocol Switch
*/

void clink_initswt(void) {

   asm   mov   dx,pport
   asm   add   dx,2
   asm   mov   al,initlow
   asm   out   dx,al

   delay(1);   // delay for 1 millisecond

   asm   mov   dx,pport
   asm   add   dx,2
   asm   mov   al,strobehigh
   asm   out   dx,al ; // turn the init off
}

/*
	Initialize the parallel port.
*/

void clink_init(void) {

unsigned long time,count;

   asm   mov   dx,pport
   asm   mov   al,88h      ; // abort
   asm   out   dx,al       ; // set abort signal on output lines
   asm   mov   al,strobehigh
   asm   inc   dx
   asm   inc   dx
   asm   out   dx,al       ; // set strobe high

   time=biostime(0,0L);
   
   while(time==biostime(0,0L)); // wait for time to sync

   time=biostime(0,0L);

   count=0;

   while(time==biostime(0,0L)) count++; // count reaches 650 on a 25mhz 386

   // now adjust timeout count for systems slower or faster

   timeout_count= (timeout_count * count) / 650L;

   error_count=0L;
}


char setfbuf[6], repfbuf;
int test1,test2;

int clink_setflag(char buf_num) {

int ecount,k;

   setfbuf[0]=0x33;
   setfbuf[1]=0;     // future device address
   setfbuf[2]=buf_num;
   setfbuf[3]=setfbuf[0]+setfbuf[1]+setfbuf[2]; // checksum

   for(ecount=0; ecount<3; ecount++)
   {
   	test1 = psendfast( (char far *) setfbuf, 2);
      if(test1) goto next;  // send 4 bytes

      test2 = precfast((char far *) &repfbuf, 1);
      if(test2) goto next;

      if(repfbuf==((char)0xaa)) goto okmsg; // good transmission

next:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al

      error_count++;
      delay(1);   // slight delay
   }

   return(1); // error return

okmsg:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al

   return(0);   
}


int clink_clearflag(char buf_num) {

int ecount,k;

   setfbuf[0]=0x44;
   setfbuf[1]=0;     // future device address
   setfbuf[2]=buf_num;
   setfbuf[3]=setfbuf[0]+setfbuf[1]+setfbuf[2]; // checksum

   for(ecount=0; ecount<3; ecount++)
   {
      if(psendfast( (char far *) setfbuf, 2))
         goto next; // send 4 bytes
   
      if(precfast((char far *) &repfbuf,1)) goto next;
      else if(repfbuf==((char)0xaa)) goto okmsg; // good transmission

next:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al

      error_count++;

      delay(1);   // slight delay
   }

   return(1); // error return
   
okmsg:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al

   return(0);
}

int clink_testflag(char buf_num,char *flgptr) {

int ecount,k;

   setfbuf[0]=0x55;
   setfbuf[1]=0;
   setfbuf[2]=buf_num;
   setfbuf[3]=setfbuf[0]+setfbuf[1]+setfbuf[2]; // checksum

   for(ecount=0; ecount<3; ecount++)
   {
      if(psendfast( (char far *) setfbuf, 2))
      	goto next; // send 4 bytes
   
      if(precfast((char far *) &repfbuf,1)) goto next;
      else if(repfbuf==((char)0xbb) || repfbuf==((char)0xaa))
      	goto okmsg; // good transmission

next:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al
      
      error_count++;

      delay(1);   // slight delay
   }
   
   return(1); // error return

okmsg:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al
      
   *flgptr=repfbuf & 1;
   
   return(0);
}


int clink_to_buf(char buf_num, int count, char *buf) {

int k,ecount;
char jj;

   setfbuf[0]=0x11;        // operation code send to
   setfbuf[1]=0;           // future device address
   setfbuf[2]=buf_num;
   
   *( (int*) &setfbuf[3])=count; // set the count
   jj=0;
   
   for(k=0; k<5; k++) jj=jj+setfbuf[k];      // compute checksum
   setfbuf[5]=jj;
   
   jj=0;
   for(k=0; k<count; k++) jj=jj+buf[k];
   buf[count]=jj; // insert checksum  // DEBUG***** bad checksum

   for(ecount=0; ecount<3; ecount++)
   {
      if(psendfast( (char far *) setfbuf, 3)) goto next; // send 6 bytes
      
      if(psendfast( (char far *) buf, (count+2)/2)) goto next; // count+
   
      if(precfast((char far *) &repfbuf,1)) goto next;
      else if(repfbuf==((char)0xaa)) goto okmsg; // good transmission

next:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al
      
      error_count++;
      
      delay(1);   // slight delay
   }
   
   return(1); // error return
   
okmsg:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al
      
   return(0);
}

int clink_from_buf(  char buf_num, int count, char *buf){

int k,ecount;
char jj;

   setfbuf[0]=0x22;        // operation code send to
   setfbuf[1]=0;           // future device address
   setfbuf[2]=buf_num;
   
   *( (int*) &setfbuf[3])=count; // set the count
   
   jj=0;
   for(k=0; k<5; k++) jj=jj+setfbuf[k];      // compute checksum
   setfbuf[5]=jj;
   
/* jj=0;
   for(k=0; k<count; k++) jj=jj+buf[k];
   buf[count]=jj; */ // insert checksum

   for(ecount=0; ecount<3; ecount++)
   {
      if(psendfast( (char far *) setfbuf, 3)) goto next; // send 6 bytes
      if(precfast( (char far *) &repfbuf, 1)) goto next; // get reply code
      if(repfbuf!=((char)0xaa)) goto next; // bad checksum on header

      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al
      
      if(precfast( (char far *) buf, count) ) goto next; // count+
      
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al

      if(precfast( (char far*) &repfbuf,1) ) goto next; // checksum

      jj=0;
      for(k=0; k<count; k++) jj=jj+buf[k]; // compute checksum
      buf[count]=0;                // place terminal null
      if(repfbuf==jj) goto okmsg;  // check checksum

next:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al
      
      error_count++;

      delay(1);   // slight delay
   }

   return(1); // error return
   
okmsg:
      asm   mov   dx,pport
      asm   mov   al,88h   ; // ready_first
      asm   out   dx,al

   return(0);
}
   
/*
  Borland C/C++ program to send a block of data at high speed to
  Little Giant or Tiny giant using the parallel printer port 

dx    points to parallel port 0378h etc
ds:bx points to data array
cx    is a 1/2 count of bytes to send 

returns 0 for good transfer, 1 for a timeout or other failure

communications failures:

   1) other side is not sending ready_first code (98h on this side)
      when routine is entered.

   2) After first character is output and strobe is lowered other
      side does not respond with ready_next (A8h on this side) within
      the timeout period.

   3) If the other side fails to respond with appropriate change in ack
      signal to a change in strobe signal within the timeout period
      a timeout occurrs. If the other side fails to continue sending
      ready_next and also fails to respond to a strobe, then an immediate
      abort takes place. 
*/

int psendfast(char far *buffer, int count) {

int retval;

asm      push  si
asm      push  di
asm      les   bx,buffer      // get address of buffer in es:bx 
asm      mov   dx,pport    // the port address of the printer port
asm      mov   cx,count    ;// count of bytes to send 

// wait for ready first (x&b8)==98h 
asm      mov   si,timeout_count  
asm      inc   dx          ;// point to data in port 

fff0a:   
asm      in    al,dx
asm      and   al,0b8h
asm      cmp   al,098h
asm      je    fff0b
asm      dec   si
asm      jnz   fff0a

#ifdef NO_TIMEOUT
asm      jmp   fff0a
#endif

asm      jmp   timeouta    ;// timed out 

fff0b:
asm      dec   dx          ; // restore 
// end of timeout test 

asm      mov   al,es:(bx)  ;// first byte 
asm      out   dx,al    //; output data lines
asm      add   dx,2       //  ;point to strobe register
asm      mov   al,strobelow
asm      out   dx,al    //;set strobe low 

// wait for ready next (x&b8)==a8  
asm      mov   si,timeout_count  
asm      dec   dx          ;// point to data in port 

fff0c:   
asm      in    al,dx
asm      and   al,0b8h
asm      cmp   al,0a8h
asm      je    fff0d
asm      dec   si
asm      jnz   fff0c

#ifdef NO_TIMEOUT
asm      jmp   fff0c
#endif

asm      jmp   timeoutb    ;// timed out 

fff0d:
asm      inc   dx          ; // restore 
asm      jmp   fff1a       ;// jump to middle of loop 

fff1:
asm      mov   al,es:(bx)  ;// first byte 
asm      out   dx,al    //; output data lines
asm      add   dx,2       //  ;point to strobe register
asm      mov   al,strobelow
asm      out   dx,al    //;set strobe low 

fff1a:
asm      dec   dx
asm      inc   bx
asm      mov   si,timeout_count     ;// set timeout count 

asm      in    al,dx    ;//get ack
asm      test  al,40h   ;//test for ack low
asm      jz fff2a

asm      in    al,dx    ;//get ack
asm      test  al,40h   ;//test for ack low
asm      jz fff2a

asm      in    al,dx    ;//get ack
asm      test  al,40h   ;//test for ack low
asm      jz fff2a

fff2:
asm      in    al,dx    ;//get ack
asm      test  al,40h   ;//test for ack low
asm      jz fff2a

// confirm ready next (x&b8)==a8  
asm      and   al,0b8h
asm      cmp   al,0a8h
asm      jne   timeoutc    ;// aborted by sender 
asm      dec   si
asm      jnz   fff2     ;// timeout counter

#ifdef NO_TIMEOUT
asm      jmp   fff2
#endif

asm      jmp   timeoutd

fff2a:
asm      dec   dx
asm      mov   al,es:(bx)  // second byte 
asm      out   dx,al    ; // out data 
asm      add   dx,2
asm      mov   al,strobehigh
asm      out   dx,al    ;//set strobe high
asm      dec   dx
asm      inc   bx
asm      mov   si,timeout_count     ; //timeout count    

asm      in al,dx    ;//get ack
asm      test  al,40h
asm      jnz   fff3a    ; //test for ack high

asm      in al,dx    ;//get ack
asm      test  al,40h
asm      jnz   fff3a    ; //test for ack high

asm      in al,dx    ;//get ack
asm      test  al,40h
asm      jnz   fff3a    ; //test for ack high

fff3:
asm      in al,dx    ;//get ack
asm      test  al,40h
asm      jnz   fff3a    ; //test for ack high

// confirm ready next (x&b8)==a8  
asm      and   al,0b8h
asm      cmp   al,0a8h
asm      jne   timeoute    ;// aborted by sender 
asm      dec   si
asm      jnz   fff3     ; //timeout counter

#ifdef NO_TIMEOUT
asm      jmp   fff3
#endif

asm      jmp   timeoutf

fff3a:
asm      dec   dx    ;// to 378 
asm      dec   cx    
asm      jnz   fff1  // loop counter 

asm      mov   al,88h   ;// set up ready first code 
asm      out   dx,al    ;// restore ready first state 
asm      pop   di
asm      pop   si

   return(0);
timeouta:
   asm   mov   bx,1
   asm   jmp   timeout

timeoutb:
   asm   mov   bx,2
   asm   jmp   timeout

timeoutc:
   asm   mov   bx,3
   asm   jmp   timeout

timeoutd:
   asm   mov   bx,4
   asm   jmp   timeout

timeoute:
   asm   mov   bx,5
   asm   jmp   timeout

timeoutf:
   asm   mov   bx,6
   asm   jmp   timeout

timeout:
asm      mov   retval,bx
asm      mov   dx,pport
asm      inc   dx
asm      inc   dx
asm      mov   al,strobehigh
asm      out   dx,al
asm      mov   dx,pport
asm      mov   al,88h   ;// set up ready first code */
asm      out   dx,al
asm      pop   di
asm      pop   si

return(retval);

}

/*
   Receive a block of data at high speed from remote unit.
   Return zero for sucess, else 1.
*/

unsigned int prec(char far *buf, unsigned int max) {

unsigned int kk;

   if(precfast((char far *) &kk,2)) return 0xffff;
   if(kk>max) return 0xffff;
   if(precfast(buf,kk)) return 0xffff;

   return(kk);
}

/*
  Borland C/C++ program to receive a block of data at high speed from
  a Little Giant or Tiny giant using the parallel printer port 

dx    points to parallel port 0378h etc
ds:bx points to data array
si is count of bytes to send 

  returns 0 for good transfer, 1 for a timeout 
*/

int precfast(char far *buffer, int count) {

int retval,kk;

asm      push  si
asm      push  di
asm      les   di,buffer      // get address of buffer in es:di 
asm      mov   dx,pport    // the port address of the printer port
asm      inc   dx          // plus 1 is 379h 
asm      mov   si,count    ;// count of bytes to send 
asm      push  si          ;// save count on stack 
//asm    mov   bx,offset tab      ;// set up for translate instruction 

fff0:                ;// wait for ack to go low 
asm   in    al,dx    ; // get bits and ack at 379h 
asm   test  al,40h
asm   jz    fff1     ; // if ack low 
asm   in    al,dx    ; // get bits and ack at 379h 
asm   test  al,40h
asm   jz    fff1     ; // if ack low 
asm   in    al,dx    ; // get bits and ack at 379h 
asm   test  al,40h
asm   jz    fff1     ; // if ack low 
asm   in    al,dx    ; // get bits and ack at 379h 
asm   test  al,40h
asm   jz    fff1     ; // if ack low 

// now timeout loop         
asm      mov   si,timeout_count

fff1a:
asm      in al,dx
asm      test  al,040h  // test ack 
asm      jz fff1        // ack low on 1st 4-bit nibble 
asm      dec   si
asm      jnz   fff1a

#ifdef NO_TIMEOUT
asm      jmp   fff1a
#endif

asm      jmp   ftimeouta

fff1:
// al contains 379h register 
asm      xor   al,080h  ;// invert inverted bit 
asm      mov   ah,al
asm      mov   al,85h   ; // ready_next code 
asm      dec   dx       ; // 378 
asm      out   dx,al    ;// place ready next code on bus 
asm      inc   dx       ; // 379 
asm      mov   al,strobelow   // handshake 1st nibble 
asm      inc   dx       ; // 37a 
asm      out   dx,al    ; // respond by lowering strobe 
asm      dec   dx       ; // 379 
asm      mov   al,ah    ; // restore data 

// packing of data 
asm      mov   cl,4
asm      rcr   al,cl
asm      rcl   ah,1
asm      rcr   al,1
asm      rcl   ah,1
asm      rcr   al,1
asm      rcl   ah,1     ; // 0 0 0 0 0 0 b7 b6 b5 
asm      sar   al,1
asm      rcr   al,1
asm      rcl   ah,1     ; //  0 0 0 0 b7 b6 b5 b4  -- in ah 

// now next data nibble 
asm      in    al,dx    ; 
asm      test  al,040h  ;// test ack 
asm      jnz   fff2     ; // if ack high continue 2nd 4-bit nibble 
asm      in    al,dx    ; 
asm      test  al,040h  ;// test ack 
asm      jnz   fff2     ; // if ack high continue 2nd 6 bit nibble 
asm      in    al,dx    ; 
asm      test  al,040h  ;// test ack 
asm      jnz   fff2     ; // if ack high continue 2nd 6 bit nibble 
asm      in    al,dx    ; 
asm      test  al,040h  ;// test ack 
asm      jnz   fff2     ; // if ack high continue 2nd 6 bit nibble 

// here to test timeout 
asm      mov   si,timeout_count

fff2a:
asm      in    al,dx
asm      test  al,040h  // test ack 
asm      jnz   fff2
asm      dec   si
asm      jnz   fff2a

#ifdef NO_TIMEOUT
asm      jmp   fff2a
#endif

asm      jmp   ftimeoutb

fff2:
asm      xor   al,080h  ;// invert inverted bit 
asm      mov   bl,al    ;// save 
asm      mov   al,strobehigh
asm      inc   dx       ;// point to strobe 
asm      out   dx,al    ;// set strobe high 
asm      dec   dx
asm      mov   cl,4
asm      rcr   bl,cl
asm      rcl   ah,1
asm      rcr   bl,1
asm      rcl   ah,1
asm      rcr   bl,1
asm      rcl   ah,1
asm      sar   bl,1
asm      rcr   bl,1
asm      rcl   ah,1     ;// now have 8 bits in ah 

asm         mov   es:[di],ah  ;// store it, 1st data byte 
asm         inc   di

fff5:
asm      pop   si    ;// count 
asm      dec   si
asm      jz doret
asm      push  si
asm      jmp   fff0  // loop counter 
doret:
asm      mov   dx,pport
asm      mov   al,88h      ;//ready first
asm      out   dx,al
asm      pop   di
asm      pop   si

   return(0);
   
ftimeouta:
   asm   mov   bx,1
   asm   jmp   ftimeout

ftimeoutb:
   asm   mov   bx,2
   asm   jmp   ftimeout

ftimeout:
asm      mov   retval,bx
asm      mov   dx,pport
asm      inc   dx
asm      inc   dx
asm      mov   al,strobehigh
asm      out   dx,al
asm      mov   dx,pport 
asm      mov   al,88h
asm      out   dx,al    ; //ready first code
asm      pop   si

asm      pop   di ;// restore saved registers 
asm      pop   si

   return(retval);
}


