commo.asm




;
;            FILE NAME  COMMO.ASM
;
;------- HPC COMMUNICATION ROUTINES TO & FROM MASTER COMPUTER ---------------
;        
;             SynPet Personal Electronic Technologies
;             7225 Franklin Road, Boise, Idaho 83709
;
;    Created:  8/9/89   LRE
;
;
;
;
;
;----------------------------------------------------------------------------


 .chip      16003
 .incld     GLOBDEFS

;---------------------- MODULE PRIVATE DEFINES ----------------------------

COMMO_STATUS_PORT    =  01400H  ; Address of commo id & busy bit port.
COMMO_DATA_IN_PORT   =  01000H  ; Data from PC.
INT_ID_BIT           =  01 ; Bit position of id bit.
STX                  =  02 ; ASCII start of text.  Flags msg begin.
COMMO_DATA_OUT_PORT  =  01000H ; Adress to send data.
PC_BUSY_BIT          =  0   ; PC busy bit.
IN_BUFFER_ROLLOVER   =  07FH ; 128 byte buffer.
CGIE_BIT             =  06H ; Bit position of global int en copy.
OUT_INT_ID_BIT       =  00  ; ID bit position sent to PC.
CLR_BUSY_ADD         =  0A000H ; Read or Write this port to clr busy.
LENGTH_BYTE_OFFSET   =  01 ; Position of length byte in Q.
RET_STATUS_OFFSET    =  03 ; Position of status byte in Q.
FAIL_MSG_LENGTH      =  03 ; Length of last mst failed msg.
FAIL_MSG_STATUS      =  0FEH ; Status to show last wasn't echoed right.
CMD_OFFSET           =  01   ; Position of job code & option in process
                             ; buffer.


;---------------- END OF MODULE PRIVATE DEFINES --------------------------

 .sect     DATA,ram8

;--------------------- MODULE PRIVATE VARIABLES -----------------------------
 .public commo_out_buffer
 .public commo_out_ptr
 .public bytes_to_send
 .public bytes_sent
 .public send_next_tx_flag
 .public tx_busy
 .public two_in_progress
 .public commo_in_buffer
 .public commo_in_process_ptr
 .public commo_in_ptr
 .public rx_buff_full
 .public strt_new_msg
 .public msg_process_buff_ptr
 .public msg_process_buffer
 .public process_byte_cnt
 .public first_process_two_flag
 .public job_transfer_cnt
 .public cur_transfer_top_ptr
 .public cur_transfer_btm_ptr
 .public next_q_to_send
 .public tx_transfer_ptr
 .public commo_out_trnsfr_ptr
 .public commo_out_transfer_bytes


commo_out_buffer:    .DSB   32  ; Buffer for current msg being sent.
commo_out_ptr:       .DSB   1   ; Offset into buffer of next byte to send.
bytes_to_send:       .DSB   1   ; Number of bytes in msg.
bytes_sent:          .DSB   1   ; Number of bytes of current msg sent already.
last_msg_nok:        .DSB   1   ; Flag to indicate last msg had error.
send_next_tx_flag:   .DSB   1   ; Tell higher to send next char.
tx_busy:             .DSB   1   ; Flag to keep others from messing up current. 
two_in_progress:     .DSB   1   ; Flag for handling STX vs. data 02. 
commo_in_buffer:     .DSB   128 ; Receive buffer of 128 bytes.
commo_in_ptr:        .DSB   1   ; Pointer to next mt slot in buffer. 
commo_in_process_ptr: .DSB   1   ; Pointer to next slot to be processed.
rx_buff_full:        .DSB   1   ; Flag set if buffer is full.
ack_byte:            .DSB   1   ; Byte to be echoed by higher level if PC busy.
send_ack_flag:       .DSB   1   ; Flag to tell higher level to send ack if
                                ; PC busy
old_msg_length:      .DSB   1   ; For last byte sent error recovery.
old_msg_status:      .DSB   1   ; For last byte sent error recovery.
strt_new_msg:        .DSB   1   ; Flag to say send first msg byte.
last_resend:         .DSB   1   ; Flag to say resend last after error msg
                                ; is finished.
waiting_for_stx:     .DSB   1   ; Flag to tell if need stx to start rx msg  
                                ; assembly.  INITIALIZE TO 0FFH
msg_process_buff_ptr: .DSB   1   ; Ptr to next slot in rx msg assembly buffer.
msg_process_buffer:  .DSB   32  ; Temporary holder for assembly rx msg.
process_byte_cnt:    .DSB   1   ; rx bytes still expected for assembly.
first_process_two_flag: .DSB 1   ; rx STX vs data byte flag.
job_transfer_cnt:    .DSB   1   ; Number of bytes tranfered to job q.
cur_transfer_top_ptr: .DSB 1 ; Local copy of job q pointer.
cur_transfer_btm_ptr: .DSB 1 ; Likewise.
next_q_to_send:       .DSB 1 ; Number of next job q to chk for tx msg.
tx_transfer_ptr:      .DSB 1 ; Next byte in to trnsfr to in commo out buffer.
commo_out_trnsfr_ptr: .DSB 1 ; Next byte in q to go to commo out buffer.
commo_out_transfer_bytes: .DSB 1 ; Number of bytes to transfer to tx.



;------------------ END OF MODULE PRIVATE VARIABLES ------------------------

;--------------------- EXTERNAL GLOBAL VARIABLES ---------------------------

 .extrn    start_of_job_msg_in_queues:BYTE
 .extrn    job_in_q_top_ptrs:BYTE
 .extrn    job_in_q_btm_ptrs:BYTE
 .extrn    buffer_full_errors:BYTE
 .extrn    jobs_in_q:BYTE
 .extrn    cancel_in_q:BYTE
 .extrn    continue_in_q:BYTE
 .extrn    total_jobs_in_qs:BYTE
 .extrn    start_of_job_msg_out_queues:BYTE
 .extrn    msgs_to_send:BYTE
 .extrn    total_msgs_to_send:BYTE
 .extrn    job_out_q_btm_ptrs:BYTE

;----------------- END OF EXTERNAL GLOBAL VARIABLES -----------------------

 .endsect

;------------------------- START OF MODULE CODE ----------------------------

 .sect      CODE,rom8
 .ipt       1,COMMO_NMI
 .public    PROCESS_COMMO
 .public    INIT_COMMO
 .public    NEW_MESSAGE
 .public    PROCESS_MSG

;------------------  NON MASKABLE INTERRUPT -------------------------------
;             RECEIVES & TRANSMITS TO/FROM XT BUS 

COMMO_NMI:    ;  TX & RX to & from XT bus.

    PUSH   A ; Going to use A.
    PUSH   B ; & B.


    IFBIT  INT_ID_BIT,COMMO_STATUS_PORT.B ;  Chk for rx or tx.
    JMP    RX_HNDLR ;  Go receive if bit set.

;--------------  START OF TRANSMIT PART OF NMI ----------------------------


TX_HNDLR:  ; Bit cleared means tx ack interrrupt.
    
    IFEQ   two_in_progress,#0 ; 
    JMP    CHK_REGULAR ;
    IFEQ   COMMO_DATA_IN_PORT.B,#2 ; Stx back?
    JMP    ACK_OK ; Yes - all sympatico.
    JMP    TX_ERROR ; No - start again.
 CHK_REGULAR:
    LD     A,commo_out_ptr ; Get pointer to next byte.
    DEC    A               ; Point to last byte sent.
    LD     B,A             ; Offset in B.
    LD     A,COMMO_DATA_IN_PORT.B ; Get the ack byte.
    IFEQ   A,commo_out_buffer[B].B ; Compare with last sent.
    JMP    ACK_OK  ;  Sent back correct byte.
    
TX_ERROR:  ; Fall thru means didn't get back the last byte sent.

    IFEQ   bytes_sent,bytes_to_send ; See if was last byte.
    JMP    END_OF_MSG_ERROR ;  We'll have to let higher level cancel msg.

         ; Otherwise we'll restart msg.
    LD     commo_out_ptr,#0 ; Reset to beginning of buffer.
    LD     bytes_sent,#0 ; Starting all over.
    LD     two_in_progress,#0 ; In case it was set.
          ; Fall thru to send byte.

ACK_OK:   ; Check for byte to send & send if is one to send.
          ; Else tell higher level done with tx.
    IFEQ   bytes_to_send,bytes_sent ; See if last was sent.
    JMP    DONE_WITH_TX ; If so, we're done.
 CHK_TX_BUSY:
    IFBIT  PC_BUSY_BIT,COMMO_STATUS_PORT.B ; See if PC ready for int.
    JMP    DO_TX_BUSY ; Let higher level take care of next send if busy.
             ; Else send next byte.
    SBIT   OUT_INT_ID_BIT,PORT_B_LO.B ; ID as tx int.
    LD     B,commo_out_ptr ; Pointer to next byte to send.
    LD     A,commo_out_buffer[B].B ; Get the byte.
    IFEQ   A,#STX ; See if is stx.
    JMP    TWO_HNDLR ; Takes special handling if it is stx.
           ; Otherwise go ahead and send it.
SND_BYTE:  ; Comes here for send with ptr and bytes sent increment.
    LD     COMMO_DATA_OUT_PORT.B,A ; Send the byte.
    INC    commo_out_ptr ; Increment ptr for next.
    INC    bytes_sent ; Just sent another byte.
    JMP    RETURN_FROM_NMI ; Done for this time.

TWO_HNDLR:
    IFEQ   bytes_sent,#0 ; STX will be sent normally.
    JMP    SND_BYTE ;
    IFEQ   two_in_progress,#0 ; First of double two.
    JMP    NEW_TWO ; Yes - go set flag & send.
    LD     two_in_progress,#0 ; No this is second of pair.
    JMP    SND_BYTE ; So well send with advanced count & pointer.
NEW_TWO: 
    LD     two_in_progress,#0FFH ; 
    LD     COMMO_DATA_OUT_PORT.B,A ; We send this one --
    JMP    RETURN_FROM_NMI ;  Without advancing count or pointer.

END_OF_MSG_ERROR:
    LD     last_msg_nok,#0FFH ; Tell higher level to cancel last msg & resend.
    LD     tx_busy,#0 ; All done with tx.
    JMP    RETURN_FROM_NMI ; All done with interrupt.

DO_TX_BUSY:
    LD     send_next_tx_flag,#0FFH ; Let higher level crank up commo when ready.
    JMP    RETURN_FROM_NMI ; In the meanwhile we'll get out of here.

DONE_WITH_TX:
    LD     tx_busy,#0 ; Higher level can startup new msg.
    JMP    RETURN_FROM_NMI ;

;------------------  END OF TRANSMIT PART OF NMI ---------------------------



;------------------  START OF RECEIVE PART OF NMI ---------------------------


RX_HNDLR:

    LD     A,COMMO_DATA_IN_PORT.B ; Get the byte.
    IFEQ   rx_buff_full,#0FFH ; Chk for buffer overflow.
    JMP    RX_BUFF_FULL_ERROR ; Go handle if buffer full.
           ; Else put in buffer.
BUFFER_NOT_FULL:
    LD     B,commo_in_ptr ; Get pointer to next in slot.
    LD     commo_in_buffer[B].B,A ; & store it.
    INC    commo_in_ptr ; For next.
    AND    commo_in_ptr,#IN_BUFFER_ROLLOVER ; Circular Q.
    INC    B ; Going to chk for full buffer.
    INC    B ;
    AND    B,#IN_BUFFER_ROLLOVER ;
    IFEQ   B,commo_in_process_ptr ; Chk for buffer full - 1.
    LD     rx_buff_full,#0FF ; Can't have equal or we'll mess up ptrs.
 
    
SND_ACK:  ; Send back char for ack, echo chk, & ready for more.
    IFBIT  PC_BUSY_BIT,COMMO_STATUS_PORT.B ; See if PC ready for int.
    JMP    DO_RX_BUSY ; Let higher level take care of next ack if busy.
             ; Else send next ack. 
    RBIT   OUT_INT_ID_BIT,PORT_B_LO.B ; ID as ack int.
    IFEQ   rx_buff_full,#0FFH ; See if buffer full.
    COMP   A ; Send ones complement back if buffer full.
    LD     COMMO_DATA_OUT_PORT.B,A ; Echo byte.
    JMP    RETURN_FROM_NMI ; All done.

DO_RX_BUSY: ; Set up for higher level to send ack.
    LD     send_ack_flag,#0FFH ;  Flag for higher.
    LD     ack_byte,A ; Char for higher to ack with.
    JMP    RETURN_FROM_NMI ; Let main scan do when PC not busy.

RX_BUFF_FULL_ERROR:
    LD     B,commo_in_ptr ; Get pointer to next in slot.
    INC    B ; Going to chk for full buffer.
    INC    B ;
    AND    B,#IN_BUFFER_ROLLOVER ;
    IFEQ   B,commo_in_process_ptr ; Chk for buffer full - 1.
    JMP    SND_ACK ; Still full - don't clear full.
    LD     rx_buff_full,#0 ; Clear full flag.
    JMP    BUFFER_NOT_FULL ; Go ahead & keep char.

;--------------- END OF NMI RECEIVE ROUTINE ----------------------------



RETURN_FROM_NMI:

    POP    B ; Restore registers.
    POP    A ;

    IFBIT  CGIE_BIT,PSW.B ; See if ints enabled when entered.
    JMP    INT_EN_NMI_RET ; Enable ints on return if were to start.
    
    LD     CLR_BUSY_ADD.B,A ; Let PC interrupt again.
    RET      ; & Return with ints disabled.
   
INT_EN_NMI_RET:
    LD     CLR_BUSY_ADD.B,A ; Strobe latch.
    RETI     ; & Return with ints enabled.


;------------------  END OF NMI  ROUTINE ---------------------------------

;-------------------  MAIN COMMO SCAN ROUTINE -----------------------------
 
 ;COMMENTED FOR TESTING - REMOVE COMMENTS FOR REAL!!!!!!!!!!!!!!!!


PROCESS_COMMO:  ; See if communications need any attention.

SND_ACK_CHK:   ; See if rx is waiting for scan to send ack.
    IFEQ   send_ack_flag,#0FFH ;
    JMP    HI_LVL_SEND_ACK ; Go try to do it if flag set by NMI.
        ; NOTE! HI_LVL_SEND_ACK JUMPS BACK TO END OF CHECKS TO
        ;  SKIP REST OF CHECKS IF THIS ONE TRUE.
SND_NXT_CHK:  ; See if tx is waiting for scan to send next char.
    IFEQ   send_next_tx_flag,#0FFH ;
    JMP    HI_LVL_SND_NXT ;  Go do it if NMI wants scan to.
        ; JUMPS TO RETURN AS ABOVE.
SND_LAST_CHK:  ; See if nok error sent & ready to send old msg again.
 ;  IFEQ   tx_busy,#0FFH ; Don't chk if tx busy.
 ;  JMP    LAST_NOK_CHK ;
 ;  IFEQ   strt_new_msg,#0FFH ; Don't chk if waiting to send msg.
 ;  JMP    LAST_NOK_CHK ;
 ;  IFEQ   last_resend,#0FFH ; See if resend just occured.
 ;  JMP    RESEND_OLD  ;
LAST_NOK_CHK:
 ;  IFEQ   last_msg_nok,#0FFH ; See if last char of last msg garbled.
                    ; Can't have strt new & have last nok.
 ;  JSR    LAST_NOK ; Will return to same place as others.

CHK_COMMO_IN:
    IFEQ   commo_in_process_ptr,commo_in_ptr ; See if anything to be 
               ; processed.
    JMP    CHK_COMMO_OUT ; No - Go check for new msg to send.
    JSR    PROCESS_RECEIVE ; Yes - go work on.
    JMP    CHK_COMMO_IN ; Will stay here as long as chars are available.
                        ; Except that once a message is complete the return
                        ; from process receive will be a return & skip.
                        ; Only one message max will be completely processed
                        ; per scan.
CHK_COMMO_OUT:
    IFEQ   tx_busy, #0FFH ; Check for last msg complete.
    RET    ; All done if msg in progress.
    IFEQ   strt_new_msg,#0FFH ; No new msg if waiting for one to get sent.
    JMP    SEND_NEW ; Try to send if one waiting.
;   IFEQ   last_msg_nok,#0FFH ; May have happened between chk & now.
;   RET       ; If so catch next time.
    IFEQ   total_msgs_to_send.B,#0 ; See if anything in out Q's.
    RET      ; No - all done.
    IFEQ   tx_busy,#0FFH ;
    RET      ;
    JSR    NEW_MESSAGE ; ; Yes - assemble & start send.

SEND_NEW:
    IFEQ   tx_busy,#0FFH ;
    RET      ;
    JSR    TRY_NEW ; See if can send. Do if PC not busy.

    RET      ; All done checking commo.

;----------------- END OF MAIN COMMO SCAN ROUTINE -----------------------


;---------- HI LEVEL ROUTINES TO RECOVER FROM NMI PC BUSY -----------------

HI_LVL_SEND_ACK:  ; NMI ran into a PC busy so we'll do it.
    _DIS_NMI_    ; Can't let NMI in while doing activity based on PC
                 ;  busy bit.
    IFBIT  PC_BUSY_BIT,COMMO_STATUS_PORT.B ; See if PC ready.
    JMP    HI_LVL_SEND_ACK_RET ; Is busy - we'll try again next time.
          ; Fall thru means we'll send ack.
    RBIT   OUT_INT_ID_BIT,PORT_B_LO.B ; ID as ack int.
    LD     A,ack_byte ; Get the ack byte.
    IFEQ   rx_buff_full,#0FFH ; See if buffer full.
    COMP   A ; Send ones complement back if buffer full.
    LD     COMMO_DATA_OUT_PORT.B,A ; Echo byte.
 HI_LVL_SEND_ACK_RET:
    _EN_NMI_   ; Turn NMI back on.
    JMP    CHK_COMMO_IN ; All done - Go check for received chars.

HI_LVL_SND_NXT:  ; Send next char if NMI ran into PC busy.
    _DIS_NMI_   ; Can't let NMI confuse us.
    IFBIT  PC_BUSY_BIT,COMMO_STATUS_PORT.B ; See if PC busy.
    JMP    HI_LVL_SND_NXT_RET ; Try next time if it is.   
    SBIT   OUT_INT_ID_BIT,PORT_B_LO.B ; ID as tx int.
    IFEQ   two_in_progress,#0FFH ; See if second two is to be sent.
    JMP    HI_LVL_SEND_TWO ;
    LD     B,commo_out_ptr ; Get pointer to next to tx.
    LD     A,commo_out_buffer[B].B ; Get the char.
    IFEQ   A,#STX ;  Chk for a two.
    JMP    HI_LVL_TWO_HNDLR ; Two's take special handling.
 HI_LVL_SND_BYTE:
    LD     COMMO_DATA_OUT_PORT.B,A ; Send the byte.
    LD     send_next_tx_flag,#0 ; Skip next.
    INC    commo_out_ptr ; Set up for next.
    INC    bytes_sent ; One more gone.
    IFEQ   bytes_sent,bytes_to_send ; See if we're done.
    JMP    DONE_WITH_HI_LVL_SND ;
 HI_LVL_SND_NXT_RET:
    _EN_NMI_      ;  Let NMI back in.
    JMP    CHK_COMMO_IN ; Go back to main scan.

 HI_LVL_TWO_HNDLR:
    IFEQ   bytes_sent,#0  ; First byte is Single STX.
    JMP    HI_LVL_SND_BYTE ; Go send like regular char.
    LD     two_in_progress,#0FFH ; Next ack needs advanced ptrs.
    LD     COMMO_DATA_OUT_PORT.B,A ; Send the byte.
    LD     send_next_tx_flag,#0 ; Skip next.
    INC    commo_out_ptr ; Set up for next.
    INC    bytes_sent ; One more gone.
    JMP    HI_LVL_SND_NXT_RET ; 

 HI_LVL_SEND_TWO:

    LD     two_in_progress,#0 ; Second two is on its way.
    LD     A,#2 ; Going to send two.
    LD     COMMO_DATA_OUT_PORT.B,A ; Send byte without advancing pointers.
    LD     send_next_tx_flag,#0 ; Skip next.
    IFEQ   bytes_sent,bytes_to_send ; See if we're done.
    JMP    DONE_WITH_HI_LVL_SND ;
    JMP    HI_LVL_SND_NXT_RET ; 

 DONE_WITH_HI_LVL_SND:
    LD     tx_busy,#0 ; Ready for next msg.
    LD     send_next_tx_flag,#0 ; Skip next.
    JMP    HI_LVL_SND_NXT_RET ;
 
;-------------------  END OF PC BUSY RECOVERY ROUTINES ---------------------



;------------ RECOVERY FROM LAST BYTE OF MSG NOT ECHOED RIGHT --------------

LAST_NOK: ; Last msg byte not echoed right - tell PC.
             ; Save old msg length.
    LD     old_msg_length,commo_out_buffer+LENGTH_BYTE_OFFSET ;
            ; Save old msg byte where we're going to put failed status.
    LD     old_msg_status,commo_out_buffer+RET_STATUS_OFFSET ;
            ; Set fail msg length.
    LD     commo_out_buffer+LENGTH_BYTE_OFFSET.B,#FAIL_MSG_LENGTH ;
            ; Set fail msg status.
    LD     commo_out_buffer+RET_STATUS_OFFSET.B,#FAIL_MSG_STATUS ;
            ; Will go back with same job code, but fail status.
            ; Set up to send.
    LD     bytes_sent,#0 ;
    LD     bytes_to_send,#FAIL_MSG_LENGTH+1 ;
            ; Tell startup procedure to go when PC ready.
    LD     strt_new_msg,#0FFH ;
            ; Tell scan to send last when this one is finished.
    LD     last_resend,#0FFH ;
            ; Done with nok flag.
    LD     last_msg_nok,#0 ;
    RET   ;


RESEND_OLD:  ; Last nok error msg has been sent - now resend 
                 ;  the nok msg.
             ; Restore old msg length.
    LD     commo_out_buffer+LENGTH_BYTE_OFFSET.B,old_msg_length ;
            ; Restore old msg byte to where we had put failed status.
    LD     commo_out_buffer+RET_STATUS_OFFSET.B,old_msg_status ;
            ; Set up to send.
    LD     bytes_sent,#0 ;
    LD     bytes_to_send,old_msg_length ;
    INC    bytes_to_send  ;
            ; Tell startup procedure to go when PC ready.
    LD     strt_new_msg,#0FFH ;
            ; Tell scan to we're done. 
    LD     last_resend,#0 ;
    JMP    CHK_COMMO_IN ;

;-------- END OF RECOVERY FROM LAST BYTE NOT ECHOED RIGHT -------------------



;---- START OF TRY NEW - START NEW MSG TRANSMISSION IF PC NOT BUSY ----------

TRY_NEW:  ; Start mesg send if PC not busy.

    _DIS_NMI_    ; Can't let NMI in while doing activity based on PC
                 ;  busy bit.
    IFBIT  PC_BUSY_BIT,COMMO_STATUS_PORT.B ; See if PC ready.
    JMP    TRY_NEW_RET ; Is busy - we'll try again next time.
          ; Fall thru means we'll send char.
    SBIT   OUT_INT_ID_BIT,PORT_B_LO.B ; ID as tx int.
    LD     A,#STX ; Start of msg.
    LD     COMMO_DATA_OUT_PORT.B,A ; Send the byte.
    INC    bytes_sent ;
    INC    commo_out_ptr ;
    LD     tx_busy,#0FFH ; Show we're cranked up.
    LD     strt_new_msg,#0 ; Have it started now.
 TRY_NEW_RET:
    _EN_NMI_   ; Turn NMI back on.
    RET  ; All done - Go back to main scan. 

;-------- END OF TRY NEW - START NEW MSG XMT IF PC NOT BUSY ---------------



;----- START OF PROCESS RECEIVE - GET INPUT CHARS & ASSEMBLE INTO MSG ------

PROCESS_RECEIVE: ; Get characters out of rx buffer & assemble into msg.

    LD     B,commo_in_process_ptr ; Get buffer ptr.
    LD     A,commo_in_buffer[B].B ; Get char
    INC    commo_in_process_ptr ;  Set for next. 
    AND    commo_in_process_ptr,#IN_BUFFER_ROLLOVER ; Maybe need to dis nmi?
    IFEQ   waiting_for_stx,#0FFH ; Need first?
    JMP    PROCESS_STX ; Yes - go do stx stuff.
 NOT_WAITING:
    IFEQ   A,#STX ; See if char is to be checked for data two.
    JMP    PROCESS_TWO ; Is a two - go chk.
    IFEQ   first_process_two_flag,#0FFH ; Fall thru means restart msg.
                                        ; Cause single STX has been received.
    JMP    CHK_FOR_NOT_2ND_STX ;
 PUT_IN_PROCESS_BUFFER: 
    LD     B,msg_process_buff_ptr ;
    LD     msg_process_buffer[B].B,A ; Put char in buffer.
    IFEQ   B,#0 ; See if cnt byte.
    LD     process_byte_cnt,A ; Set byte count if is.
    INC    msg_process_buff_ptr ; Set up for next.
    SC         ; We'll set up for next & see if done.
    SUBC   process_byte_cnt,#1 ;
    IFEQ   process_byte_cnt,#0 ; Will be zero if done.
    JMP    PROCESS_MSG ; Got a whole message - go take care of it.
    RET      ; Otherwise we're done with this byte.

PROCESS_STX:  ; Looking for stx. 
    IFEQ   first_process_two_flag,#0FFH ;
    JMP    CHK_FOR_NOT_2ND_STX ;
    IFEQ   A,#STX ;  See if char is STX.
    LD     first_process_two_flag,#0FF ; Set flag if is.
    RET       ; Return with flag set or not set according to char.
  CHK_FOR_NOT_2ND_STX: ; Have received one stx - make sure this is not another.
    LD     first_process_two_flag,#0 ; Whether STX or not going to clr.
    IFEQ   A,#STX ; See if char is STX
    RET      ; Is a second STX - dump in bit bucket & return.
       ; Fall thru means not stx - must be count byte.
    LD     waiting_for_stx,#0 ; We have our stx.
    LD     msg_process_buff_ptr,#0 ; Set ptr to beginning of buffer.
    JMP    PUT_IN_PROCESS_BUFFER ; Put count byte into buffer. 

PROCESS_TWO: ; Handle data two's.
    IFEQ   first_process_two_flag,#0FFH ; See if second two.
    JMP    SECOND_TWO ; Yes go do flags & put in buffer.
    LD     first_process_two_flag,#0FFH ; Is 1st - set flag & return.
    RET    ; Don't do anything but set flag on first 2;
 SECOND_TWO: ; Clear flag & put data 2 in buffer.
    LD    first_process_two_flag,#0 ;
    JMP   PUT_IN_PROCESS_BUFFER ;

;------- END OF PROCESS RECEIVE - TAKE INPUT CHARS & MAKE A MSG -----------


;--------- START OF PROCESS MESSAGE - PUT IN JOB QUEUE -------------------

PROCESS_MSG:  ; Have a complete message - put it in job q.

    LD     A,msg_process_buffer+CMD_OFFSET.B ; Get job & option.
    AND    A,#0F0H ; Mask off option bits. CAREFUL! HARD NUMBER HERE!
    PUSH   A   ; Save a copy.
    SHL    A   ; WATCH OUT ! DEPENDENT ON BUFFER SIZE!!
               ; Job number * 64 in A.
    SHL    A   ; Have offset into job buffers in A.
    LD     BP_temp_reg.W,A ; Offset into X.
    POP    A   ; Get job number back.
    SWAP   A   ; Get job number into lower byte.
    AND    A,#0FH ; Mask off swap garbage.
    LD     B,A ; Offset into B.
    LD     A,job_in_q_btm_ptrs[B].B ; Pointer to next byte to be processed
                                    ; by job.
    DEC    A    ; Only A can be decremted.
    AND    A,#JOB_IN_Q_ROLLOVER ; Mod q size.
    LD     cur_transfer_btm_ptr,A ;  For buffer full error chk.
           ; Get pointer to byte in.
    LD     A,job_in_q_top_ptrs[B].B ;
    LD     cur_transfer_top_ptr.B,A ;
    PUSH   B ; Save index to set new pointer.
    LD     job_transfer_cnt,#0 ;  For loop. 
 TRANSFER_LOOP:
    IFEQ   cur_transfer_top_ptr,cur_transfer_btm_ptr ; Chk buffer full.
    JMP    TRANSFER_BUFFER_FULL_ERROR ; Go error if equal.
           ; Otherwise transfer byte.
    LD     B,job_transfer_cnt ; Get byte to transfer.
    LD     A,msg_process_buffer[B].B ;
    LD     B,BP_temp_reg.W ; Get base offset.
    ADD    B,cur_transfer_top_ptr ; Full address of slot.
    LD     start_of_job_msg_in_queues[B].B,A ;  Byte into q.
    INC    cur_transfer_top_ptr ; Set up for next.
    AND    cur_transfer_top_ptr,#JOB_IN_Q_ROLLOVER ;
    INC    job_transfer_cnt ;
    IFEQ   job_transfer_cnt,msg_process_buffer ; First byte is count.
    JMP    DONE_WITH_TRANSFER ;
    JMP    TRANSFER_LOOP ;
 DONE_WITH_TRANSFER:  ; All of message transfered - clean up for next.
    POP    B ; Index into pointers back.
    IFBIT  CANCEL_BIT,A ; A still has last byte & last byte has cancel.
    INC    cancel_in_q[B].B ; Tell job it has a cancel in the queue.
    IFBIT  CONTINUE_BIT,A ;
    INC    continue_in_q[B].B ; Tell job it has a continue.
    LD     A,cur_transfer_top_ptr.B ; set new q ptr.
    LD     job_in_q_top_ptrs[B].B,A ;
    INC    jobs_in_q[B].B ; Show another job to be done.
    INC    total_jobs_in_qs ; & more total to be done.
    RETSK  ;  All done.  Won't process more rx until next time thru 
           ;   commo scan.

 TRANSFER_BUFFER_FULL_ERROR:  ; Tell job it missed a msg.
    POP    B ; Index into pointers back.
    LD     A,msg_process_buffer+CMD_OFFSET.B ; Get job code.
    LD     buffer_full_errors[B].B,A ; & put in errors for error msg.
    INC    jobs_in_q[B].B ; So will get processed.
    INC    total_jobs_in_qs ; Likewise.
    RETSK  ;

;---- END OF PROCESSING INCOMING MSG - HAS BEEN PUT IN JOB QUEUE -----------



;------  NEW MESSAGE - TRANSFER FROM JOB MSG OUT Q TO TX Q -----------------

NEW_MESSAGE:

      ; First find the first job with a message waiting for tx.
   LD      B,next_q_to_send ; Next q to chk.  Round robin equal priority.
 NEXT_TX_TRNSFR_LOOP:
   LD      A,msgs_to_send[B].B ; Get msgs to send in this q.
   IFEQ    A,#0 ; See if any msgs this q.
   JMP     CHK_FOR_NEXT ; No go set up to chk next q.
   JMP     TRANSFER ; Yes - go get it.
 CHK_FOR_NEXT:       ; Set up to chk next q.
   INC     B ; For next q.
   IFGT    B,#NUMBER_OF_JOBS_ROLLOVER ; Mod rollover.
   LD      B,#0 ;
   JMP     NEXT_TX_TRNSFR_LOOP ; Go try next q.

 TRANSFER:  ; Found the q with a msg. Now put it into the tx buffer.
            ; Still have job number in B.
   LD      A,msgs_to_send[B].B ; Get number of msgs in q.
   DEC     A  ; Will be one less in q.
   LD      msgs_to_send[B].B,A ; For next.
   LD      A,total_msgs_to_send ; Likewise for total.
   DEC     A  ;
   LD      total_msgs_to_send.B,A ;
   PUSH    B ; Save for q base address calc & ptr fetch.
   PUSH    B ; Save another copy of job number for end.
   INC     B ; For next time thru chk.
   IFGT    B,#NUMBER_OF_JOBS_ROLLOVER ; Mod rollover.
   LD      B,#0 ;
   LD      next_q_to_send.B,B ;
   POP     A ; Get job number into A.
   LD      B,A ; Save another copy in B.
   SWAP    A ; Job number * 16 in A.
   SHL     A ; Job number * 32 = base q address in A.
   LD      BP_temp_reg.W,A ; Save q base address.
            ; Get pointer to bytes to be transfered.
   LD      A,job_out_q_btm_ptrs[B].B ;
   LD      tx_transfer_ptr.B,A  ;
   LD      commo_out_buffer.B,#STX ; Msg starts with STX.
   LD      commo_out_trnsfr_ptr,#1 ; First byte transferred will go
                                   ; after STX.
   LD      B,BP_temp_reg.W ; Q base address into B.
   ADD     B,tx_transfer_ptr ; Offset address of count byte in q.
   LD      A,start_of_job_msg_out_queues[B].B ;
   LD      commo_out_transfer_bytes.B,A ;
                       ; Number of bytes in msg into transfer bytes.
   INC     A ; Send will also have STX.
   LD      bytes_to_send.B,A ; For TX.
   
 TRANSFER_OUT_LOOP:
   LD      A,start_of_job_msg_out_queues[B].B ; Get byte to transfer.
   LD      B,commo_out_trnsfr_ptr.B ; Slot in output buffer.
   LD      commo_out_buffer[B].B,A ; Transfer byte.
   INC     tx_transfer_ptr ;
   AND     tx_transfer_ptr,#JOB_OUT_Q_ROLLOVER ; Do rollover.
   IFEQ    commo_out_trnsfr_ptr.B,commo_out_transfer_bytes ; Done with msg? 
   JMP     DONE_WITH_TX_TRANSFER ; Yes - cleanup & leave.
   INC     commo_out_trnsfr_ptr ; For next & done check.
   LD      B,BP_temp_reg.W ;     No - Set up for next.
   ADD     B,tx_transfer_ptr ; New offset address.
   JMP     TRANSFER_OUT_LOOP ; Do next byte.

 DONE_WITH_TX_TRANSFER:  ; Set up for next & let main loop start tx.
   POP     B ; Get job number back into B.
   LD      A.B,tx_transfer_ptr ; Ptr for next msg this job.
   LD      job_out_q_btm_ptrs[B].B,A ; Advanced ptr.
   LD      bytes_sent.B,#0 ; Haven't sent any yet.
   LD      commo_out_ptr.B,#0 ;
   LD      strt_new_msg,#0FF ; Tell main loop to send the msg.
   
   RET     ; All done.
  
;------ END OF TRANSFERRING MSG FROM JOB OUT Q TO TX BUFFER --------------

;----------------- INITIALIZE COMMUNICATIONS -----------------------------

INIT_COMMO:

    LD     waiting_for_stx,#0FF ; Haven't got a msg yet.
    SBIT  1,DIRB_LO.B ; B1 pin is output that controls NMI disable.
    SBIT  OUT_INT_ID_BIT,DIRB_LO.B ; B2 pin is output for int id.
    LD    CLR_BUSY_ADD.B,A ; Not busy yet.
    _DIS_NMI_
    RET     ;

;-----------------  DONE WITH INITIALIZATION -----------------------------



;----------------  END OF COMMUNICATIONS MODULE --------------------------


 .endsect
 .end