. . . .

Assembly Language Examples

 

updated:2016.07.13

Prev   Next   Site Map   Home  
Text Size
Please reload page for style change

See other examples (M6801, Z8, etc.) in Firmware Examples



68K (CPU32) ASM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 68K ASM Source File:  APPUART.ASM
;
; Description: CDX Instrument APP
; UART Functions. Two channels. A is always master, which may be to another
; IML unit or data station. B may be to IML slave or other device such as bar
; code reader. 
; This version is for IML slave interface in channel B.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
APPUART_ASM EQU 1

;------------------ IMPORTS -----------------------------------------------
    INCLUDE sys.inc
    INCLUDE com.inc

    XREF    _tick5ms

;------------------ EXPORTS -----------------------------------------------
    XDEF    _rxTimeout
    XDEF    _uartIsr
    XDEF    _ubTxPtr
    XDEF    _ubTxCnt

;*************************************************************************
;                       DATA 
;*************************************************************************
    
    SECT ASMD,2,D 

FIFOLEN     EQU 3
SINGLEMR1   EQU UARTMR1_8BIT+UARTMR1_NOPARITY+UARTMR1_RXRTS
FIFOMR1     EQU SINGLEMR1+UARTMR1_FIFOINT

; TXB control elements not part of Com struct because they are not used by
; DMA transmit (TXB is by interrupt because there aren't enough DMA channels.
    DS 0                ; Return to even boundary

_rxTimeout  DC.L 200    ; Timeout (number of 5msec. ticks) allowed for RX 
; message to complete starting from the first byte. Default is 1 second. 

_ubTxPtr    DS.L 1      ; Pointer to next interrupt-driven transmit source.
_ubTxCnt    DS.B 1      ; Interrupt-driver transmit byte remaining count.
    
;*****************************************************************************
;                           CODE
;*****************************************************************************
    
    SECT ASMC,4,C    

;/****************************************************************************
; Function:     _uartIsr
; Authors:      David McCracken
; Description:  Dual UART driver. There is only one vector for the entire
; DUART so this ISR has to service multiple events. Regardless of the trigger
; event, this function processes all UART events, looping until none remain,
; before returning. Channel A is used for communication with the master and has
; only receive events, as transmit is by DMA. Channel B is used for
; communication with the slave and has receive, and transmit events. The ISR
; does not process error events to avoid wasting interrupt time. Even a single
; error taints the entire message, so higher level functions can clear the
; errors before the message begins and check them afterward.
;
; Globals:      
; uaRxnn and ubRxnn variables are used identically. They are collectively 
; referred in this description as rxnn.
; - rxPtr is input destination pointer, which higher level functions
; initialize to point to the beginning of a buffer before each message. The
; ISR advances it as the message is received. Higher level functions must
; reload it. The ISR doesn't do this itself because buffers may be ping-ponged
; for a 0-copy system.
;
; - rxMax is the size of the input buffer. It must be destination buffer size
; -1 even though the attempted overrun detector tests for length < rxMax,
; because the destination pointer is incremented on the last byte before
; dumping of overrun begins. The fact that the length byte itself is not
; included in the length is accomodated by testing for < rxMax instead of <=.
;
; - rxCnt serves as a received bytes counter and as a status flag. Before each
; message, higher level functions initialize this to FF, which tells the ISR
; that the next input is the first byte of a message, i.e.  the length byte,
; ACK (FD), ACKPAUSE (FE), or NAK (FF). If the byte is < FD then it is the
; length, which is written to uaRxCnt (as well as to the destination). The ISR
; subsequently counts rxCnt down. When it reaches 0, this tells both the ISR
; and higher level functions that the message is done. rxCnt is also used to
; determine whether to interrupt on FIFO full or single byte.
;
; - rxBadLen functions as a flag for this function to tell higher levels that
; the input length byte indicates a message longer than the buffer or < 3.
; This may be caused by a transmission error in that byte or misunderstanding
; between this program and the sender. When the length >= rxMax, the value is
; stored in rxBadLen. Higher levels initialize rxBadLen to 0, so the non-0
; value flags that an attempted overrun is being dumped. The length value may
; be reported to help diagnose the cause.
;
; - rxDump is an indicator of on-going dumping. Higher functions need to wait
; for input to stop before recovering from an attempted overrun. They do this
; by watching for no change in rxDump for a specified period. The ISR
; increments rxDump at each dumped byte. The actual value doesn't matter and
; rxDump doesn't need to be initialized.
;
; .......................... notes ..........................................
;- The receive interrupts are sometimes based on FIFO full and sometimes on
; single byte. The first byte interrupt has to be on single byte. After that,
; whether to interrupt on FIFO full or single byte is determined by comparing
; the remaining count (from first message byte) to the FIFO size. 
;- For interrupt-driven TXB, we can't simply check TxEmpty, because it is true
; even when we're not transmitting. Instead, check the remaining transmit
; count. Higher levels enable TxB interrupt and it is disabled here at the end
; but interrupts still occur for RXA and RXB. 
;
;...........................................................................*/
_uartIsr 
    MOVEM.L D0/A0-A1,-(SP)   
    LEA     ASIM+UART,A0
uartLoop:
    MOVE.B  UARTISR(A0),D0
    BTST.B  #UARTISR_RXA,D0
    BNE     rxA
    BTST.B  #UARTISR_RXB,D0
    BNE     rxB
    TST.B   _ubTxCnt
    BNE     txB
uartIsrDone:
    MOVEM.L (SP)+,D0/A0-A1      
    RTE

;.........................................................................
rxA: ;.... Channel A Rx interrupt = input from master .......
    MOVE.L  uaRxPtr,A1          ; Get destination pointer.
    CMP.B   #$FF,uaRxCnt        ; Byte count. FF means starting new message.
    BEQ.S   newmsgA
    CMP.B   #0,uaRxCnt
    BEQ.S   dumpA               ; Guard against overrun (could be ACK/NAK though).

inmsgA:    ; Continuing message.
    MOVE.B  UARTADAT(A0),D0     ; Get input byte.
storeA:
    MOVE.B  D0,(A1)+            ; Store it at next destination address.
    SUBQ.B  #1,uaRxCnt
    BEQ.S   nearEndA            ; End of message. Go set interrupt per byte.
    BTST.B  #UARTISR_RXA,UARTISR(A0)
    BNE.S   inmsgA              ; If more input then loop to get it.
    ; No more input. 
    MOVE.L  A1,uaRxPtr          ; Save destination pointer.
    ; Set FIFO full interrupt and leave it if correct else change to per byte.
    MOVE.B  #FIFOMR1,UARTMR1A(A0)   ; Interrupt on FIFO Full.
    CMP.B   #FIFOLEN,uaRxCnt 
    BCC     uartLoop            ; Leave it if remainder > FIFO count.
nearEndA:   ; uaRxCnt < FIFO. Including uaRxCnt = 0, which is end of message.
    MOVE.B  #SINGLEMR1,UARTMR1A(A0) ; Else change to interrupt per byte.
    BRA     uartLoop            ; Go see if there is more work.

newmsgA:    ; This is the beginning of a message.
    MOVE.B  UARTADAT(A0),D0     ; Get input byte.
    CMP.B   #ACKNAK,D0             
    BCC.S   acknakA             ; If input >= ACKNAK then it is a control byte.

    ; This is the length byte for the message.
    CMP.B   uaRxMax,D0
    BCC.S   badLenA             ; Too long for destination (len >= uaRxMax).
    CMP.B   #3,D0
    BCC.S   lenokA              ; If D0 >= 3 then length is legal.
badLenA:    ; Engage dumping mechanism if the len is too long or too short. 
; Both indicate that the length byte is erroneous, so we have no idea how 
; many bytes will be sent.
    MOVE.B  D0,uaRxBadLen       ; Flag attempted overrun.
    MOVE.B  uaRxMax,D0 ; Don't let input overrun. Dump excess after count reaches 0.
lenokA:
    MOVE.B  D0,uaRxCnt
    ADDQ.B  #1,uaRxCnt ; Precompensate for count, because length byte is not included in count.
    MOVE.W  D0,-(A7)
    MOVE.L  _tick5ms,D0
    ADD.L   _rxTimeout,D0       ; e.g. 1 second timeout for input to complete.
    MOVE.L  D0,uaRxTimer
    MOVE.W  (A7)+,D0
    BRA     storeA              ; Go store length byte at destination.

dumpA:  ; uaRxCnt is already 0. Read and discard the input to clear the UART,
; unless 1) not currently dumping, 2) txState is WANTS_ACK, and 3) the input 
; is ACK/NAK, which can be a fast response due to full duplex.
    MOVE.B  UARTADAT(A0),D0     ; Get input byte to clear it.
    TST.B   uaRxBadLen
    BNE.S   realDumpA           
    CMP.B   #TX_WANTS_ACK,uaTxState
    BNE.S   realDumpA           ; If not expecting ACK/NAK then overrun.
    ; Not currently dumping and we are expecting a response.
    CMP.B   #ACKNAK,D0             
    BCC.S   acknakA             ; If input >= ACKNAK then it is a control byte.
    MOVE.B  #1,uaRxBadLen       ; Start dumping now if the input isn't ACK/NAK.
realDumpA:
    ADDQ.B  #1,uaRxDump         ; Count to show that overrun is continuing.
    BRA     uartLoop            ; Go see if there is more work.

acknakA:    ; Input is a control byte, ACK (FD), ACKPAUSE (FE), or NAK (FF)
    MOVE.B  D0,uaRxAck          ; Signal ACK or NAK.
    BRA     uartLoop            ; Go see if there is more work.

;.........................................................................
rxB: ;.... Channel B Rx interrupt = input from slave ...............
    MOVE.L  ubRxPtr,A1          ; Get destination pointer.
    CMP.B   #$FF,ubRxCnt        ; Byte count. FF means starting new message.
    BEQ.S   newmsgB
    CMP.B   #0,ubRxCnt
    BEQ     dumpB               ; Guard against overrun (could be ACK/NAK though).

inmsgB:    ; Continuing message.
    MOVE.B  UARTBDAT(A0),D0     ; Get input byte.
storeB:
    MOVE.B  D0,(A1)+            ; Store it at next destination address.
    SUBQ.B  #1,ubRxCnt
    BEQ.S   nearEndB            ; End of message. Go set interrupt per byte.
    BTST.B  #UARTISR_RXB,UARTISR(A0)
    BNE.S   inmsgB              ; If more input then loop to get it.
    ; No more input. 
    MOVE.L  A1,ubRxPtr          ; Save destination pointer.
    ; Set FIFO full interrupt and leave it if correct else change to per byte.
    MOVE.B  #FIFOMR1,UARTMR1B(A0)   ; Interrupt on FIFO Full.
    CMP.B   #FIFOLEN,ubRxCnt 
    BCC     uartLoop            ; Leave it if remainder > FIFO count.
nearEndB:   ; ubRxCnt < FIFO. Including ubRxCnt = 0, which is end of message.
    MOVE.B  #SINGLEMR1,UARTMR1B(A0) ; Else change to interrupt per byte.
    BRA     uartLoop            ; Go see if there is more work.

newmsgB:    ; This is the beginning of a message.
    MOVE.B  UARTBDAT(A0),D0     ; Get input byte.
    CMP.B   #ACKNAK,D0             
    BCC.S   acknakB             ; If input >= ACKNAK then it is a control byte.
    
    ; This is the length byte for the message.
    CMP.B   ubRxMax,D0
    BCC.S   badLenB             ; Too long for destination (len >= uaRxMax).
    CMP.B   #3,D0
    BCC.S   lenokB              ; If D0 >= 3 then length is legal.
badLenB:    ; Engage dumping mechanism if the len is too long or too short. 
; Both indicate that the length byte is erroneous, so we have no idea how 
; many bytes will be sent.
    MOVE.B  D0,ubRxBadLen       ; Flag attempted overrun.
    MOVE.B  ubRxMax,D0 ; Don't let input overrun. Dump excess after count reaches 0.
lenokB:
    MOVE.B  D0,ubRxCnt
    ADDQ.B  #1,ubRxCnt ; Precompensate for count, because length byte is not included in count.
    MOVE.W  D0,-(A7)
    MOVE.L  _tick5ms,D0
    ADD.L   _rxTimeout,D0       ; e.g. 1 second timeout for input to complete.
    MOVE.L  D0,ubRxTimer
    MOVE.W  (A7)+,D0
    BRA     storeB              ; Go store length byte at destination.

dumpB:  ; ubRxCnt is already 0. Read and discard the input to clear the UART,
; unless 1) not currently dumping, 2) txState is WANTS_ACK, and 3) the input 
; is ACK/NAK, which can be a fast response due to full duplex.
    MOVE.B  UARTBDAT(A0),D0     ; Get input byte to clear it.
    TST.B   ubRxBadLen
    BNE.S   realDumpB           
    CMP.B   #TX_WANTS_ACK,ubTxState
    BNE.S   realDumpB           ; If not expecting ACK/NAK then overrun.
    ; Not currently dumping and we are expecting a response.
    CMP.B   #ACKNAK,D0             
    BCC.S   acknakB             ; If input >= ACKNAK then it is a control byte.
    MOVE.B  #1,ubRxBadLen      ; Start dumping now if the input isn't ACK/NAK.
realDumpB:
    ADDQ.B  #1,ubRxDump         ; Count to show that overrun is continuing.
    BRA     uartLoop            ; Go see if there is more work.

acknakB:    ; Input is a control byte, ACK (FD), ACKPAUSE (FE), or NAK (FF)
    MOVE.B  D0,ubRxAck          ; Signal ACK or NAK.
    BRA     uartLoop            ; Go see if there is more work.

txB: ;.... Channel B Tx interrupt ......................................
; Unlike the RXs, we have branched to here in case of something to send, not
; because of UART status. So we have to check TxEmpty before doing anything.
; Never loop back from this point because there is no more RX in either 
; channel and if we didn't tx anything then we're done, and if we did then
; we have to wait for it.
    BTST.B  #UARTISR_TXB,D0     
    BEQ     uartIsrDone         ; Not TxEmpty.
    SUBQ.B  #1,_ubTxCnt    
    BNE.S   txbbyte
    ; Disable TXB interrupt on last byte.
    MOVE.B  #UARTISR_RXAEN+UARTISR_RXBEN,UARTISR(A0)
txbbyte:
    MOVE.L  _ubTxPtr,A1         ; Get source pointer.
    MOVE.B  (A1)+,UARTBDAT(A0)  ; Read/write data and advance source pointer.
    MOVE.L  A1,_ubTxPtr         ; Save updated pointer.
    BRA     uartIsrDone


I80386 WIN9X VXD

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ASM Source File:  ADVRHSL.ASM
;
; Description:  CDX Master HSSL analyzer communication driver. This is a
; module of DVRHSL.VXD. It provides a shell to redirect asm driver interface
; to C functions.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ADVRHSL_ASM EQU 1

    .386p
    include vmm.inc
    include vdmad.inc
    include vpicd.inc
    include vtd.inc
    ;include debug.inc
    ;include configmg.inc
    
Declare_Virtual_Device CDXHSL, 1, 0, CDXHSL_control,\
    Undefined_Device_ID, Undefined_Init_Order

Begin_Control_Dispatch CDXHSL
    Control_Dispatch Sys_Dynamic_Device_Init, _DriverEntry, cCall
    Control_Dispatch Sys_Dynamic_Device_Exit, _unload, cCall
    Control_Dispatch W32_DeviceIOControl, _dispatch, cCall, <esi>
End_Control_Dispatch   CDXHSL
              
HSSL            equ 150h          ; 2681 duart address - channel A
HSSL_MRA        equ HSSL          ; mode registers 1-2            
HSSL_CSRA       equ HSSL + 1      ; clock select                   
HSSL_SRA        equ HSSL + 1      ; status register                
HSSL_CRA        equ HSSL + 2      ; command register               
HSSL_DAT        equ HSSL + 3      ; RX/TX data register          
HSSL_ACR        equ HSSL + 4      ; aux control                    
HSSL_IMR        equ HSSL + 5      ; interrupt mask                 
HSSL_ISR        equ HSSL + 5      ; interrupt status               
HSSL_OPCR       equ HSSL + 13     ; output port config             
HSSL_OPRSET     equ HSSL + 14     ; set output port bits           
HSSL_OPRCLR     equ HSSL + 15     ; clear output port bits         

NAK             equ 0FFh
ACK             equ 0FEh   
ACKPAUSE        equ 0FDh
IfCtrlJump      MACRO B, J
    cmp     B,ACKPAUSE
    jnc     J   ; If input >= LowestControl then it is ACKPAUSE, ACK, or NAK
ENDM

MAX_RX_PACKET_SIZE equ 250  ; Match value in onRxMsg.

; The following values must be synchronized to enums in cdvrhsl.c
TX_PASSIVE      EQU 0       ; txState value.
TX_WANTS_ACK    EQU 1       ; txState value.
WAIT_RXDONE     EQU 1       ; statusMap bit mask. WAIT_TXDONE = 2, WAIT_RXACK = 4

VXD_LOCKED_DATA_SEG

    EXTERN  _rxIp:DWORD     ; UCHAR * = Pointer to next raw input destination.
    EXTERN  _rxCnt:BYTE     ; Rx byte down counter. Initialize to 0xFF.
    EXTERN  _rxBadLen:BYTE  ; Flag bad length byte (msg.len >= imax or < 3 ).
    EXTERN  _rxDump:BYTE    ; Bad len dump counter. Value isn't important,
    ; only change. When stops changing for rxDumpTimeout, the input is assumed to 
    ; have stopped and recovery can begin.
    EXTERN  _receivedAckNak:BYTE ; Response to current Tx message. 0, ACK, or NAK.
    EXTERN  _txState:BYTE   ; TX_PASSIVE, TS_WANTS_ACK, TX_SUSPEND.
    EXTERN  _rxState:BYTE   ; Receiving state: RX_NORMAL or RX_DUMP ??? RX_PAUSE.
    EXTERN  _statusMap:WORD ; statusMap WORD
    EXTERN  _hOnRxMsg:DWORD ; dvros.h. In DDK95\INC32\VMM.H HEVENT = DWORD.
    EXTERN  _hOnRxAckNak:DWORD ; HEVENT in cdvrhsl.c.
    EXTERN  _linkStopped:WORD

QuadWord    STRUC
L           DWORD ?
H           DWORD ?
QuadWord    ENDS
    EXTERN  _startTime:QuadWord ; Array of waitingFor start times.

IDX_WAITRX  equ 1 * SIZEOF(QWORD)
    EXTERN  _waitingFor:WORD
MASK_WAITRX equ 2           ; Bit mask for waitingFor.
 
saveByte    DB 1
     
VXD_LOCKED_DATA_ENDS

VXD_LOCKED_CODE_SEG
    EXTERN  _onRxMsg:PROTO
    EXTERN  _onRxAckNak:PROTO

;****************************************************************************
; Function:     _hslIsr
; Description:  HSSL (High Speed Link) serial ISR for analyzer communication.
; Globals:      
;..................... notes ...............................................
;- Callbacks:
; onRxMsg is scheduled when a full message has been input, onRxAckNak
; when a control byte (ACK or NAK) is received. The onRxMsg function may
; modify virtual memory by automatically copying the message to an app's
; private memory. If a page fault occurs at this time and one is already being
; handled in the interrupted thread, the system will crash. To avoid this,
; Oney suggests restricting the callback to occur only when no VM is holding
; the critical section. This can be done by scheduling the callback using
; Call_Restricted_Event with PEF_Wait_Not_Crit instead of 
; Schedule_Global_Event. 
;   Also, either of these callback functions may set an app event (through its
; ring 0 shadow). The DDK warns that this can only be done when the
; interrupted VM is the system VM. To further restrict the callback to only
; interrupt the system VM, we can pass the system VM handle in ebx and add
; PEF_Thread_Event to the filter list. The problem is that the callback never
; occurs with this restriction. Typical DDK misinformation.  Since I don't
; know which lies to believe, I just used Schedule_Global_Event for the
; onRxAckNak. For onRxMsg, I provided this as an option, because it may
; afford faster response than the restricted call, for which I included only
; the critical section filter. If no app wants automatic copy msg copy to app
; memory then Schedule_Global_Event is preferred.
;...........................................................................
_hslIsr PROC PUBLIC
; eax = IRQ handle, ebx = VM handle, edx = DWORD refdata
; VM uses ecx, edx, and esi, so these can be used here too I think.
; return with C set to indicate interrupt handled.
    mov     ebx,eax             ; Save IRQ handle.
    mov     dx,HSSL_DAT
    in      al,dx               ; Read HSSL input data.
    cmp     _linkStopped,0
    jne     irqRet
    cmp     _rxCnt,0FFh         ; FF means starting new message.
    je      newMsg
    cmp     _rxCnt,0
    je      maybeOverrun        ; Guard against overrun (could be ACK/NAK though).
    
storeMsgByte:    
    mov     edx,_rxIp           ; Get destination pointer. 
    mov     [edx],al            ; Save data to pointer.
    inc     edx                 ; Advance destination pointer.
    mov     _rxIp,edx
    dec    _rxCnt
    jz     done                 
    
irqRet:
    mov     eax,ebx             ; Pass IRQ handle to VPICD.
    VxDcall VPICD_Phys_EOI      ; Pseudo EOI (VPICD did the real one before dispatching us).
    cmp     al,al               ; Clear carry to tell VPICD that we handled the interrupt.
    ret
    
done:
    mov     eax,ebx             ; Pass IRQ handle to VPICD.
    VxDcall VPICD_Phys_EOI      ; Pseudo EOI (VPICD did the real one before dispatching us).
    ; The message finished so cancel waitingFor rx to complete.
    and     [_waitingFor], NOT 2
    and     [_statusMap], not WAIT_RXDONE
    
    mov     esi, OFFSET32 _onRxMsg
    VMMcall Schedule_Global_Event
    mov     _hOnRxMsg,esi       ; Record for stopLink.
    cmp     al,al               ; Clear carry
    ret
    
newMsg:
    IfCtrlJump al,ackOrNak
; Not Control so this must be the length byte for the message.
    cmp     al,MAX_RX_PACKET_SIZE
    jnc     lenBad              ; Too long for destination.
    cmp     al,3               
    jnc     lenOk               ; Length >= 3 is legal.
    
lenBad:    ; Engage dumping mechanism if the len is too long or too short. 
; Both indicate that the length byte is erroneous, so we have no idea how 
; many bytes will be sent.
    mov     _rxBadLen,al ; Flag bad length and tell what it is.
    mov     al,MAX_RX_PACKET_SIZE ; Don't let input overrun. Dump excess after count reaches 0.
lenOk:
    mov     saveByte,al         ; Save input data.
; Set timeout in case this message doesn't finish. This can be tested by
; starting the analyzer before the master, which usually leaves garbage or the
; middle of the log on message in the input. The timeout function will be
; called and it will reset the input mechanism. Before setting the timeout,
; check for unfinished timeout. This should only be possible on the subsequent
; timeout after an unfinished rx, i.e. after rxTimeout. The dangling timeout
; must be cancelled before starting a new one.
    or      [_statusMap], WAIT_RXDONE
    VxDcall VTD_Get_Real_Time
    mov     [_startTime[IDX_WAITRX]].L,eax
    mov     [_startTime[IDX_WAITRX]].H,edx
    or      [_waitingFor],MASK_WAITRX
    mov     al,saveByte         ; Retrieve input data.
    inc     al                  ; Add length byte to byte counter to be stored.
    mov     _rxCnt,al           ; Save length for counting bytes.
    dec     al                  ; Restore length value to be stored as beginning of msg.
    jmp     storeMsgByte    

maybeOverrun:  ; rxCnt is already 0. Discard the input unless is can be a fast
; full duplex ACK/NAK as indicated by all of the following conditions being true:
; 1) not currently dumping, 
; 2) txState is WANTS_ACK, and 
; 3) the input is ACK/NAK.
    cmp     _rxBadLen,0
    jne     irqRet              ; Already dumping.
    cmp     _txState,TX_WANTS_ACK
    jne     startDump
    IfCtrlJump al,ackOrNak
; Not Control so start dumping.
    
startDump:
    mov     _rxBadLen,1         ; Begin dumping now. Could use input for flag but only if not 0.
; Set Waiting for Rx so that the periodic healthCheck will know when to resume normal.
    VxDcall VTD_Get_Real_Time
    mov     [_startTime[IDX_WAITRX]].L,eax
    mov     [_startTime[IDX_WAITRX]].H,edx
    or      [_waitingFor],MASK_WAITRX
    jmp     irqRet

ackOrNak:
;    mov     _txState,TX_PASSIVE let callback decide when to do this.
    mov     _receivedAckNak,al  ; Post what was received for mid-level.
    mov     eax,ebx             ; Pass IRQ handle to VPICD.
    VxDcall VPICD_Phys_EOI      ; Pseudo EOI (VPICD did the real one before dispatching us).
    mov     esi, OFFSET32 _onRxAckNak
    VMMcall Schedule_Global_Event
    mov     _hOnRxAckNak,esi    ; Record for stopLink
    cmp     al,al               ; Clear carry
    ret
_hslIsr ENDP

VXD_LOCKED_CODE_ENDS   
 
;VXD_PAGEABLE_DATA_SEG
;   PUBLIC _devNode
;_devNode       DD ?
;
;VXD_PAGEABLE_DATA_ENDS
;
;VXD_PAGEABLE_CODE_SEG
;_getRes PROC PUBLIC
;    ; VxDcall _CONFIGMG_Read_Registry_Value, 0, 0, 0, 0 \ ;<[esi.AH_Devnode], 0, 0, <OFFSET32 szOptions>,
;    ; REG_BINARY, ebp, edx, CM_REGISTRY_SOFTWARE>
;    VxDcall _CONFIGMG_Locate_DevNode, <<OFFSET32 _devNode>, 0, 0>
;     
;    ret
;_getRes ENDP
;    
;VXD_PAGEABLE_CODE_ENDS
    
    end


I80386 ASM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ASM Source File:  HSSL.ASM
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.386p
.model large

public    _hssl_getpkt;
public    _hssl_crc;

HsslBase        equ       0150h                 ;Base addr of serial link
HsslRxReg       equ       0153h                 ;Receiver register
HsslIntReg      equ       0155h                 ;Interrupt status register
AckNakMask      equ       10000000b             ;80h Mask for acknowledge bit
BufferAddress   equ       <[ebp+8]>             ;Buffer address
RxReady         equ       02h                   ;Recv rdy bit in isr
TimeOutCount    equ       1000d                 ;Trys before TimeOutCount

.const
CrcTable  db        000h,  000h,  0c0h,  0c1h,  0c1h,  081h,  001h,  040h
          db        0c3h,  001h,  003h,  0c0h,  002h,  080h,  0c2h,  041h
          db        0c6h,  001h,  006h,  0c0h,  007h,  080h,  0c7h,  041h
          db        005h,  000h,  0c5h,  0c1h,  0c4h,  081h,  004h,  040h
          db        0cch,  001h,  00ch,  0c0h,  00dh,  080h,  0cdh,  041h
          db        00fh,  000h,  0cfh,  0c1h,  0ceh,  081h,  00eh,  040h
          db        00ah,  000h,  0cah,  0c1h,  0cbh,  081h,  00bh,  040h
          db        0c9h,  001h,  009h,  0c0h,  008h,  080h,  0c8h,  041h
          db        0d8h,  001h,  018h,  0c0h,  019h,  080h,  0d9h,  041h
          db        01bh,  000h,  0dbh,  0c1h,  0dah,  081h,  01ah,  040h
          db        01eh,  000h,  0deh,  0c1h,  0dfh,  081h,  01fh,  040h
          db        0ddh,  001h,  01dh,  0c0h,  01ch,  080h,  0dch,  041h
          db        014h,  000h,  0d4h,  0c1h,  0d5h,  081h,  015h,  040h
          db        0d7h,  001h,  017h,  0c0h,  016h,  080h,  0d6h,  041h
          db        0d2h,  001h,  012h,  0c0h,  013h,  080h,  0d3h,  041h
          db        011h,  000h,  0d1h,  0c1h,  0d0h,  081h,  010h,  040h
          db        0f0h,  001h,  030h,  0c0h,  031h,  080h,  0f1h,  041h
          db        033h,  000h,  0f3h,  0c1h,  0f2h,  081h,  032h,  040h
          db        036h,  000h,  0f6h,  0c1h,  0f7h,  081h,  037h,  040h
          db        0f5h,  001h,  035h,  0c0h,  034h,  080h,  0f4h,  041h
          db        03ch,  000h,  0fch,  0c1h,  0fdh,  081h,  03dh,  040h
          db        0ffh,  001h,  03fh,  0c0h,  03eh,  080h,  0feh,  041h
          db        0fah,  001h,  03ah,  0c0h,  03bh,  080h,  0fbh,  041h
          db        039h,  000h,  0f9h,  0c1h,  0f8h,  081h,  038h,  040h
          db        028h,  000h,  0e8h,  0c1h,  0e9h,  081h,  029h,  040h
          db        0ebh,  001h,  02bh,  0c0h,  02ah,  080h,  0eah,  041h
          db        0eeh,  001h,  02eh,  0c0h,  02fh,  080h,  0efh,  041h
          db        02dh,  000h,  0edh,  0c1h,  0ech,  081h,  02ch,  040h
          db        0e4h,  001h,  024h,  0c0h,  025h,  080h,  0e5h,  041h
          db        027h,  000h,  0e7h,  0c1h,  0e6h,  081h,  026h,  040h
          db        022h,  000h,  0e2h,  0c1h,  0e3h,  081h,  023h,  040h
          db        0e1h,  001h,  021h,  0c0h,  020h,  080h,  0e0h,  041h
          db        0a0h,  001h,  060h,  0c0h,  061h,  080h,  0a1h,  041h
          db        063h,  000h,  0a3h,  0c1h,  0a2h,  081h,  062h,  040h
          db        066h,  000h,  0a6h,  0c1h,  0a7h,  081h,  067h,  040h
          db        0a5h,  001h,  065h,  0c0h,  064h,  080h,  0a4h,  041h
          db        06ch,  000h,  0ach,  0c1h,  0adh,  081h,  06dh,  040h
          db        0afh,  001h,  06fh,  0c0h,  06eh,  080h,  0aeh,  041h
          db        0aah,  001h,  06ah,  0c0h,  06bh,  080h,  0abh,  041h
          db        069h,  000h,  0a9h,  0c1h,  0a8h,  081h,  068h,  040h
          db        078h,  000h,  0b8h,  0c1h,  0b9h,  081h,  079h,  040h
          db        0bbh,  001h,  07bh,  0c0h,  07ah,  080h,  0bah,  041h
          db        0beh,  001h,  07eh,  0c0h,  07fh,  080h,  0bfh,  041h
          db        07dh,  000h,  0bdh,  0c1h,  0bch,  081h,  07ch,  040h
          db        0b4h,  001h,  074h,  0c0h,  075h,  080h,  0b5h,  041h
          db        077h,  000h,  0b7h,  0c1h,  0b6h,  081h,  076h,  040h
          db        072h,  000h,  0b2h,  0c1h,  0b3h,  081h,  073h,  040h
          db        0b1h,  001h,  071h,  0c0h,  070h,  080h,  0b0h,  041h
          db        050h,  000h,  090h,  0c1h,  091h,  081h,  051h,  040h
          db        093h,  001h,  053h,  0c0h,  052h,  080h,  092h,  041h
          db        096h,  001h,  056h,  0c0h,  057h,  080h,  097h,  041h
          db        055h,  000h,  095h,  0c1h,  094h,  081h,  054h,  040h
          db        09ch,  001h,  05ch,  0c0h,  05dh,  080h,  09dh,  041h
          db        05fh,  000h,  09fh,  0c1h,  09eh,  081h,  05eh,  040h
          db        05ah,  000h,  09ah,  0c1h,  09bh,  081h,  05bh,  040h
          db        099h,  001h,  059h,  0c0h,  058h,  080h,  098h,  041h
          db        088h,  001h,  048h,  0c0h,  049h,  080h,  089h,  041h
          db        04bh,  000h,  08bh,  0c1h,  08ah,  081h,  04ah,  040h
          db        04eh,  000h,  08eh,  0c1h,  08fh,  081h,  04fh,  040h
          db        08dh,  001h,  04dh,  0c0h,  04ch,  080h,  08ch,  041h
          db        044h,  000h,  084h,  0c1h,  085h,  081h,  045h,  040h
          db        087h,  001h,  047h,  0c0h,  046h,  080h,  086h,  041h
          db        082h,  001h,  042h,  0c0h,  043h,  080h,  083h,  041h
          db        041h,  000h,  081h,  0c1h,  080h,  081h,  040h,  040h

.code
;-----------------------------------------------------------------------
;                       HSSL_GETPKT PROCEDURE
;Gets one packet from the HSSL receiver.
;short _hssl_getpkt (unsigned char pkt[]);
;-----------------------------------------------------------------------
_hssl_getpkt    PROC      NEAR
                push      ebp                   
                mov       ebp, esp              
                push      ebx                   
                push      edx                   
                push      edi                   
                push      es                    
                push      ds                    
                pop       es
                xor       ah, ah                ;No charactor recieved yet
                xor       bh, bh                ;Start packet length at 0
                mov       edi, BufferAddress    ;Set DI to start of buffer
                CLD
;In this loop:  AH = Expected length of packet from the first byte
;               AL = the byte from the uart RX register
;               ECX = # of times to check for a byte waiting
;               EDX = address of HSSL uart register
NextChar:       mov       ecx, TimeOutCount     ;CX=how many times to check
                mov       edx, HsslIntReg       ;Get address of interrupt reg.
RecieveWait:    in        al, dx                ;Read the interrupt status
                test      al, RxReady           ;Ignore all but RX full bit
                JNE       ByteWaiting           ;If set then go get the char.
                loop      RecieveWait           ;Wait for the charactor
RxTimeOut:      JMP       GetPacketDone         ;Exit if timed out

ByteWaiting:    mov       edx, HsslRxReg        ;Get address of RX register
                in        al, dx                ;Get the charactor
                cmp       ah, 0                 ;Is this the first charactor?
                je        FirstChar             ;The handle as a first charactor
NotFirstChar:   stosb                           ;Store char to memory
                inc       bh                    ;Bump the charactor count
                cmp       bh, ah                ;End of packet?
                jne       NextChar              ;branch if not
                jmp       GetPacketDone         ;and exit

FirstChar:      cmp       al, 00h               ;Check if first char = NULL
                je        GetPacketDone         ;exit if so,  no write to memory
                stosb                           ;Store length byte
                mov       ah, al                ;Save the length byte in AH
                test      ah, AckNakMask        ;Is this an ack or nak? ACK=0xFF, NAK=0x80
                jz        NextChar              ;Continue recieving the packet
                mov       ah, 1                 ;Set length to 1 and exit
GetPacketDone:  xor       edx, edx
                mov       dl, ah                ;Set count of charactors in AL
                mov       eax, edx              ;Return count in EAX
                pop       es   
                pop       edi                   
                pop       edx
                pop       ebx
                pop       ebp
                ret                             
_hssl_getpkt    ENDP

;-----------------------------------------------------------------------
;                       HSSL_CRC PROCEDURE
;void _hssl_crc (unsigned char pkt[],            ;Packet address
;               unsigned short len,             ;Length of packet
;               unsigned char *crc0,            ;MSB of CRC
;               unsigned char *crc1);           ;LSB of CRC
;
;              Calculates the CRC for a given packet.
;REGISTERS: ON ENTRY: NOTHING              ON EXIT:  NOTHING
;-----------------------------------------------------------------------
PacketAddress   equ       <[ebp+ 8]>            ;Packet address
PacketLength    equ       <[ebp+12]>            ;Packet length
CrcMsbAddress   equ       <[ebp+16]>            ;Address of CRC msbyte
CrcLsbAddress   equ       <[ebp+20]>            ;Address of CRC lsbyte

_hssl_crc       PROC      NEAR
                push      ebp                   
                mov       ebp, esp              
                push      ebx                   ;32 bit C assumes these won't
                push      edi                   ;Change.
                mov       ecx, PacketLength     ;String length
                jcxz      CalcCRCRet            ;Exit if zero
                mov       edx, 0FFFFh           ;Initial CRC value
                mov       edi, PacketAddress    ;DI points to start of string
                CLD
;In this loop: EBX points to the start of the CRC table.
;              EDI points to the packet string
;              EDX contains the current CRC value starting at 0xFFFF
CalcCrc:        xor       ebx, ebx              ;Clear table offset
                mov       bl, [edi]             ;Get next byte of string
                inc       edi                   ;Point to next byte in string
                xor       bl, dl                ;Form table offset
                shl       ebx, 1                ;Multiply by 2 for word access
                mov       dl, CrcTable+1[ebx]   ;Get LSB from table
                xor       dl, dh                ;Combine with CRC
                mov       dh, CrcTable[ebx]     ;Get MSB from table
                loop      CalcCrc               ;Continue until end of string

CalcCrc2one:    mov       edi, CrcMsbAddress    ;DI = addr of CRC msbyte
                mov       [edi], dh             ;Store result MSB
                mov       edi, CrcLsbAddress    ;DI = addr of CRC lsbyte
                mov       [edi], dl             ;Store to result LSB
CalcCrcRet:     pop       edi                   ;Restore saved register values
                pop       ebx                   
                pop       ebp                   
                ret                             
_hssl_crc       ENDP
                END


I80286 DOSX DEVICE DRIVER

        NAME    cugps
        PAGE , 132

COMMENT $ For linking to C
Purpose: Control Unit GPIB low-level communication. Designed for Ziatech's 1444 GPIB board.
Configuration notes for Z1444:
        1. IRQ 5 : connect W4.
        2. DRQ1/GRNT1 : connect W7 and W8.
        3. DIP switch 1. I/O base address 210h: 2 and 7 off.
        4. DIP switch 2. IEEE488 3-state: 7 off.
                         GPIB controller: 8 off for CU, on for AU.
----------------------------------------------------------------------------$
_CSNAME EQU <cugps_TEXT>
;******************************* OPTIONS ************************************
REAL     = 1 ;For a non-extended application, which is the only one to support source code debugging of this module.
XREAL    = 2 ;Extended dual or with interrupts passed to real, real unit.
XPASSPP  = 3 ;Extended with interrupts passed to protected.
XDUALP   = 4 ;Extended dual mode, protected unit.
XPASSRP  = 5 ;Extended with interrupts passed to real, protected unit. 
;************************** DECLARATIONS **********************************
ifndef MODESELECT
    MODESELECT = XPASSRP
endif                       ;ifndef MODESELECT
if (MODESELECT ne REAL) and (MODESELECT ne XREAL) and (MODESELECT ne XPASSRP)
    .ERR
if1
    %out MODESELECT incorrectly defined. Only the following are legal:
    %out 1(REAL), 2(XREAL), 5(XPASSRP)
endif                       ;if1
endif                       ;If not REAL, XREAL, or XPASSRP.
;*************************** DEFINITIONS ***********************************
;+++++++++++++++++++++++++++ PRAGMAS +++++++++++++++++++++++++++++++++++++
            DOSSEG
            .286
            .SALL           ;Don't expand macro sources at instantiations
ARG1        equ 6
;+++++++++++++++++++++++++++ PUBLIC DECLARATIONS +++++++++++++++++++++++++
if          MODESELECT ne REAL
INCLUDE     phapi.inc
endif
INCLUDE     cmnasm.inc
;-------------------------- PUBLIC AND EXTRN -------------------------------
;.................. Imports from cugpbuf.c, static link ....................
EXTRN       _smpt_x:byte, _cmd_x:byte ;Normal data because of static link to cugpbuf.
EXTRN       _spld_x:byte, _ispd_x:byte, _stat_x:byte, _sbyprm:byte

;......... Imports from buz.asm and hcsys.asm .......
if      MODESELECT le XREAL         ;Only real and xreal refer to gptime(f) and inqstat.
EXTRN   _gptime:WORD, _gptimef:BYTE ;From buz.asm
EXTRN   _inqstat:byte               ;5 byte array from hcsysr.dll:hcsys.asm (or cugpm in REAL).
endif                               ;REAL and XREAL
if      MODESELECT ne XREAL
EXTRN   _gpsti:word, _gpstif:byte, _gpjumpvec:dword ;From buzr.dll:buz.asm.
EXTRN   _afteritime:FAR             ;From buzp.dll:buz.asm
endif                               ;REAL and any protected instance.

;.......................... Exported Functions ..........................
if          MODESELECT ne XREAL 
PUBLIC      _configgp, _passctla, _passctlb, _gptx, _gprx, _gpdclr
PUBLIC      _gpcycle, _initgp, _gpaccess, _au_status, _farchk
else        ;XREAL
PUBLIC      _gategp, gpisr, dmaset, gps0
endif       ;XREAL  
;.......................... Modal Aliases ...............................
if          MODESELECT eq REAL 
PROTDATA    equ <REALDATA>
VIDEOADDRESS equ 0B800H
else        ;Not REAL
VIDEOADDRESS equ <[vidselect]>
endif       ;Not REAL
    
if          (MODESELECT eq REAL) or (MODESELECT eq XREAL)
shared      MACRO var, siz
            PUBLIC &var
            ENDM
DGROUP      GROUP REALDATA
DSEG        EQU <DGROUP>
REALDSEG    EQU <DGROUP>
REALDATATYPE equ <DATA>
PUBLIC      _gpibindump, _gpibinhead ;Exported only to high level.
PUBLIC      _keyinput, _status_blink ; "
endif       ;REAL or XREAL

if          MODESELECT gt XREAL
shared      MACRO var, siz
            EXTRN &var:&siz
            ENDM
DSEG        EQU <SEG _gpinlen> ;Pick any variable defined in the real mode unit for seg reference.
REALDSEG    EQU <REALDATA>
REALDATATYPE equ <FAR_DATA>
FARCODE     SEGMENT WORD PUBLIC  'CODE'
EXTRN       gpisr:FAR, dmaset:FAR, gps0:FAR  ;From real to protected instance of cugps.asm.
FARCODE     ENDS
endif       ;Protected instance.    

REALDATA    SEGMENT WORD PUBLIC 'REALDATATYPE'
;All shared variables must also be listed in the EXPORTS section of gpsysr.def
;...Exported by XREAL to High Level Program and to XPROT .................
    shared _gpinlen     WORD
    shared _rerack      WORD
    shared _donegp      BYTE
    shared _gpactiv     BYTE
    shared _instat      DWORD
    shared _gptrans     WORD
    shared _undisgpvec  DWORD
if      (MODESELECT ne REAL)    
;.. Exported only from XREAL to XPROT ......................................
    shared raustab      DWORD
    shared ausm1        BYTE    ;First of 14 15-byte au status strings.
    shared tciip        WORD
    shared tcoip        WORD
    shared gpivec       WORD
    shared dprem        BYTE
    shared dstruct      WORD    ;3 WORD array.
    shared dmaload      WORD    ;3 WORD array alternate to dstruct for prot to call real dmaset.
;.. Used only by protected module and could be defined in PROTDATA........
    shared gprealvec    DWORD
    shared gpprotvec    DWORD
    shared iftype       BYTE
    shared errdi        WORD
    shared errsi        WORD
    shared errbp        WORD
    shared vidselect    WORD
endif   ;Not REAL
REALDATA    ENDS
;+++++++++++++++++++++++++++ LOCAL DECLARATIONS ++++++++++++++++++++++++++
;----------------------------- MACROS -----------------------------------------
;********************** NON-STORAGE DATA DEFINITIONS **********************
;--------- TI9914 GPIB registers and values from Ziatech's 1444 board -------------
;.................... io addresses .....................................
gpbase  equ 210h                ;For PC/AT
int0    equ gpbase              ;Read status, write mask
int1    equ gpbase + 1          ;Read status, write mask
astat   equ gpbase + 2          ;Read address status
ziacr   equ gpbase + 2          ;Write Ziatech control register
bstat   equ gpbase + 3          ;Read GPIB (raw) bus status
auxcmd  equ gpbase + 3          ;Write auxiliary command 
gpaddr  equ gpbase + 4          ;Write my GPIB address
gpcpt   equ gpbase + 6          ;Read Command Pass Through
gpdat   equ gpbase + 7          ;Read/write GPIB data

;.................... control values .......................................
;                      for ziacr
GCRESET equ 00110001b           ;GPIB board all functions disabled.
GCRI    equ 00010001b           ;Enable interrupt, presumeably for GPIB only.
DMARES  equ 00010001b           ;Enable int, disable DMA req and TC int, reset DMA TC ff.
DMAARM  equ 00000000b           ;Enable int, enable DMA req and TC int, unreset DMA TC ff.
;Write DMARES followed by DMAARM to reset and then arm DMA.
DMANOTC equ 1                   ;Enable GPIB interrupt, DMA operation but not interrupt on TC.

;.................... for int0 ......................
NORMI0  equ 0           
BIMASK  equ 20h
BOMASK  equ 10h
ENDMASK equ 8
BIINT   equ 0a0h
;................... for int1 .............................................
ERRMASK equ 40h
;................... for auxcmd ...............................
;................ SET/RESET COMMANDS ...........................
RESET   equ 80h                 ;Reset TMS9914
RSTCLR  equ 0                   ;Ureset
DACR    equ 1                   ;Release holdoff
NBAF    equ 5                   ;Set "new byte available false".
SRE     equ 90h                 ;Send remote enable
SERECLR  equ 10h                 ;Clear REN
LON     equ 89h                 ;Make ourself Listen Only
LONCLR  equ 9                   ;Reset LON
TON     equ 8ah                 ;Make ourself Talk Only
TONCLR  equ 0Ah                 ; RESET TALK ONLY
SIC     equ 8Fh                 ; SEND INTERFACE CLEAR
SICLR   equ 0Fh                 ; RESET INTERFACE CLEAR
STDL    equ 95h                 ; SET T1 DELAY
STDCLR  equ 15h                 ; RESET T1 TIMER
VSTDL   equ 97h                 ; SET FAST T1 DELAY
VSTCLR  equ 17h                 ; RESET FAST T1 DELAY
;............... PULSE TYPE COMMANDS .............................
GTS     equ 0Bh                 ; GO TO STANDBY
TCA     equ 0Ch                 ; TAKE CONTROL ASYNCh.
TCS     equ 0Dh                 ; TAKE CONTROL SYNCH.
RQC     equ 11h                 ; REQUEST CONTROL
RLC     equ 12h                 ; RELEASE CONTROL
FEOI    equ 8                   ;Send EOI with next byte.
;
;      GPIB COMMANDS (written to gpdat with ATN asserted)
;
MLA     equ 20h                 ; MY LISTEN ADDRESS
MTA     equ 40h                 ; MY TALK ADDRESS
UNL     equ 3Fh                 ; UNIVERSAL UNLISTEN
UNT     equ 5Fh                 ; UNIVERSAL UNTALK
TCT     equ 09h                 ; TAKE CONTROL
DCL     equ 14h                 ;Device Clear

;++++++++++++++++ DMA ADDRESSES AND CONTROL VALUES +++++++++++++++++++++++
;................ io addresses ............................................
dmapr   equ 083h                ;Channel 1 dma page register
dmamar  equ 2                   ;Channel 1 memory address register
dmacr   equ 3                   ;Channel 1 transfer count register
dmastat equ 8                   ;Channels 0-3 status
dmamsk  equ 0ah                 ;Channels 0-3 enable mask (set/reset)
dmamod  equ 0bh                 ;Channels 0-3 mode
clrbp   equ 0ch                 ;Clear byte pointer (by writing anything)
clrall  equ 0dh                 ;Master clear
clrmsk  equ 0eh                 ;Channel 0-3 clear mask register
wrmsk   equ 0fh                 ;Channel 0-3 write mask register
xfunc   equ 18h                 ;Extended function register (selector)
xfuncx  equ 1ah                 ;Extended function execute
SNGLM   equ 40h                 ;To select single byte transfer 
DMAR    equ 8                   ;Select DMA read (memory->gpib)
DMAW    equ 4                   ;DMA write (gpib->memory)
DMACH   equ 1                   ;Use hard-coded channel
GPLTDLY equ 200                 ;GPIB listener to talker turn-around delay to give the AU a chance to become listener.
SFRSIZE equ 21                  ;Startup frame size is 21 for input to CU, 25 for input to AU.

;++++++++++++++++++++++ Interrupt Controls ++++++++++++++++++++++++++++++++
picimr  equ 21h                 ;PIC interrupt mask register address.
picctl  equ 20h                 ;PIC operation control word (2 & 3) address.
piceoi  equ 20h                 ;EOI command byte.
gpint   equ 5                   ;Use interrupt 5 (PP2) or 3 (SP2)
gpiv    equ 8 + gpint           ;Vector 0bh if int 3, or 0dh if int 5.
engpint equ ( 1 SHL gpint )     ;Interrupt disable mask. AND current picimr with NOT engpint to enable gpib interrupt.
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DISCONT     equ 15 * 18     ;Disconnect timeout is 15 seconds (@18 Hz)
NOLSTNR     equ 0f000h      ;Return value when ERR bit is set, indicating no listener.
NOTC        equ 0ffffh      ;Constant used to indicate that TX or RX is not in progress. Otherwise use the actual TC.

;********************** DATA STORAGE DEFINITIONS *************************
if (MODESELECT eq XREAL) or (MODESELECT eq REAL)
REALDATA    SEGMENT WORD PUBLIC  'DATA'
ALIGN 2
_gpinlen    dw 0            ;Actual length of input.
_rerack     dw 0ffffh       ;Number (MOTO form) of rack to be rerun. Since there is only one, this could probably be Boolean Y/N.
_donegp     db 0            ;Flag for gpib isr to indicate DMA TC. Not done = 0.
_gpactiv    db 0            ;Report GPIB subclass and toggle its display attr at every conversation.
;------------------------ Additions during DOSX analysis ------------------
ALIGN 2
_keyinput       dw 0        ;Export to ldb\auman.c.
_status_blink   db 0        ;Export to auman. No blink = 0, blink = 80.
ALIGN 2
;............ dma and isr state machine variables ........................................
respptr     dw ?            ;Use to pass response pointer from state 2 to 2a.
dmarem      dw 0            ;DMA remainder in case physical address covers two segments.
gpivec      dw OFFSET isrx  ;Start in last state to get recycling machine going.  
;6/7/94 DM changed to empty ISR initially because of a bad chipset that refused to disable the interrupt in configgp.
;Although this occurred with a Symphony chip set (HP uses C&T) it doesn't hurt to guard in any case.
dma2vec     dw 0            ;Vector for splitting dma across physical boundary
dstruct     dw 0,0,0        ;Transitory DMA descriptor. This is needed because we may change the value of length given in 
dmaload     dw 0,0,0        ;Alternate to dstruct for prot to call real dmaset.
dprem       db 20h          ;Dma page register remainder, i.e. next page for split transfer.
dma2typ     db 0            ;Read or write to share ISR split dma state.
                            ;descriptor table. Also, needed for start up functions.
;............. communication variables .....................................
iftype      db ?            ;Input frame type 00 normal or 0fh startup.
lasttx      db ?            ;Last byte to transmit (with EOI).
gpdispl     db 13h          ;Color of AU minor status toggles to indicate end of gpcycle.

ALIGN 2
rrif    db 0,1,0,6,0,0,0,0,0,28 ;rrif header is static.
        db 18 DUP (?)       ;rrif remainder including 2 input checksum bytes (output needs only one).
_gpibinhead equ this byte   ;For debugging/testing.
inhead  db 12 DUP (0aah)    ;Input header destination. This must not cross a physical address boundary. 
ihdesc      dd inhead       ;Input header dma descriptor address part.
            dw 12           ;length part.
tciip       dw NOTC         ;Current TC input in progress.
tcoip       dw NOTC         ;Current TC output in progress.
inpind      dw ?            ;Input matching TC index (0-based and * 2).
transact    dw ?            ;Address of transaction descriptor in response to input TC.
_gpibindump equ this byte   ;Added for initializing transaction table in test program.
dump        db 300 DUP (?)  ;Dump for tsak and remainder after bad header.

;+++++++++++++++++++ Response descriptors ++++++++++++++++++++++++++++++++++
        ALIGN 2
rxtc    LABEL WORD  ;Array of all legitimate AU input compressed Type+Class ID's.
;...... stat, cdak, cda2, tsak, tsrq, ispd, rrak............
        dw 0b00h                ;stat_x: T=0, C=0, len=2252
        dw 0101h                ;cdak_x: T=1, C=1, len=12
        dw 0102h                ;cda2_x: T=1, C=2, len=12
        dw 0104h                ;tsak_x: T=1, C=4, len=268
        dw 0c00h                ;tsrq_x: T=0c, C=0, len=268
        dw 1000h                ;ispd_x, T=10h, C=0, len=56
        dw 0105h                ;rrak_x, T=1, C=5, len=28
;...... Normal test results: spld, clbd, iscb................
        dw 0e00h                ;spld_x, T=0e, C=0, len=10,776
        dw 0f00h                ;clbd_x, T=0f, C=0, len=14,444
        dw 1100h                ;iscb_x, T=11h, C=0, len=268
;...... T/M results: ftck, clbk, bcdt, ogpt, smpc, memc......
        dw 1800h                ;ftck_x, T=18h, C=0, len=1036
        dw 1900h                ;clbk_x, T=19h, C=0, len=1152
        dw 1b00h                ;bcdt_x, T=1bh, C=0, len=168
        dw 1c00h                ;ogpt_x, T=1ch, C=0, len=17,016
        dw 1d00h                ;smpc_x, T=1dh, C=0, len=20
        dw 1e00h                ;memc_x: T=1eh, C=0, len=216
AUTCC   equ 16                  ;AU TC count, i.e. sizeof this array.
;-------------------- Input Available Status ----------------------
_instat dd 0 ;I have defined as 4 byte object so that a C program can access as UC/byte, US/word, or UL/dd. This affords quick
    ;and/or selective bit testing. e.g. if( UL ) then there is some input: if( *(UC*)(&UL+2) ) then there is a T/M input.
    ;Lowest byte = Status and inquiries: b0=stat, b1=cdak, b2=cda2, b3=tsak, b4=tsrq.
    ;Next byte = Results: b0/8=spld, b1/9=clbd, b2/10=iscb, b3/11=ispd, b4/12=rrak. rrak is a dummy, never being set.
    ;Next byte = T/M results: b0/16=ftck, b1/17=clbk, b2/18=bcdt, b3/19=ogpt, b4/20=smpc, b5/21=memc.
    ;MSByte= spare.

avaif   LABEL WORD              ;Flag selectors: byte selector and mask.
;...... stat, cdak, cda2, tsak, tsrq, ispd, rrak.............
        dw 0001h                ;stat_x: flag byte 0, bit 0.
        dw 0002h                ;cdak_x: flag byte 0, bit 1.
        dw 0004h                ;cda2_x: flag byte 0, bit 2.
        dw 0008h                ;tsak_x: flag byte 0, bit 3. Dummy is never set.
        dw 0010h                ;tsrq_x: flag byte 0, bit 4.
        dw 0108h                ;ispd_x, flag byte 1, bit 3.
        dw 0110h                ;rrak_x, flag byte 1, bit 4. 
;.......Normal test results: spld, clbd, iscb................
        dw 0101h                ;spld_x, flag byte 1, bit 0.
        dw 0102h                ;clbd_x, flag byte 1, bit 1.
        dw 0104h                ;iscb_x, flag byte 1, bit 2.
;...... T/M results: ftck, clbk, bcdt, ogpt, smpc, memc......
        dw 0201h                ;ftck_x, flag byte 2, bit 0.
        dw 0202h                ;clbk_x, flag byte 2, bit 1.
        dw 0204h                ;bcdt_x, flag byte 2, bit 2.
        dw 0208h                ;ogpt_x, flag byte 2, bit 3.
        dw 0210h                ;smpc_x, flag byte 2, bit 4.
        dw 0220h                ;memc_x: flag byte 2, bit 5.
;------------------- Static response frames -------------------------------
;          cu_address  type     class   text_length   checksum   name
ack     db   00,01,   00,0f0h,  00,00,  00,00,00,12,  00,0fdh    ;ACK
nak     db   00,01,   00,0f1h,  00,00,  00,00,00,12,  00,0feh    ;NAK

;---------------- transaction descriptors---------------------------------
COMMENT $ The following transaction descriptors have input destination and length organized identically to dma descriptors. 
However, they won't be passed to dmaset, because the address will be changed by the first 2 bytes from "header" and length 
may be reduced to match the length extracted from input header. The response descriptors are passed directly to dmaset. In
order to send EOI with the last byte, the length listed here is the complete length, including header and checksum, minus one.
Thus, all execept the final byte will be sent by dma. $
_gptrans LABEL WORD                            
;   |-----INPUT DESTINATION-------------| |---------------- RESPONSE ---------------------|  
;         address                 length   TC  CHKSTAT/chkLB      address           length  
;T0:............stat_x..................| |...............ack.............................|
 dw OFFSET _stat_x, SEG _stat_x,    2392, 0f000h,  00fdh, OFFSET ack   , SEG ack   ,    11     
;T1:............cdak_x..................| |.............. cmd_x ..........................|
 dw        0      ,     0      ,      12, 0200h,   0100h, OFFSET _cmd_x, SEG _cmd_x,   179
;T2:............cda2_x..................| |.............. cmd_x ..........................|
 dw        0      ,     0      ,      12, 0200h,   0100h, OFFSET _cmd_x, SEG _cmd_x,   179
;T3:............tsak_x..................| |.............. smpt_x/clbt_x ..................|
 dw OFFSET dump   , SEG dump   ,     300, 0500h,   0200h, OFFSET _smpt_x, SEG _smpt_x, 267    
;T4:............tsrq_x..................| |.............. ack ............................|
 dw OFFSET _smpt_x+10, SEG _smpt_x, 2060, 0f000h,  00fdh, OFFSET ack    , SEG ack    ,  11
;T5:............ispd_x..................| |...............ack.............................|
 dw OFFSET _ispd_x   , SEG _ispd_x,   56, 0f000h,  00fdh, OFFSET ack    , SEG ack    ,  11     
;T6:............rrak_x..................| |...............rrif_x..........................|
 dw OFFSET rrif+10   , SEG rrif   ,   28, 0600h,   0200h, OFFSET rrif   , SEG rrif   ,  27   
;T7-15:spld,clbd,iscb,ftck,clbk,bcdt,ogpt,smpc,memc..><...ack.............................|
dw OFFSET _spld_x  , SEG _spld_x, 17016, 0f000h,  00fdh, OFFSET ack    , SEG ack    ,  11     
;.........................naktext "response" descriptor...................................|
nakd    dw  0       ,    0        ,    0, 0f100h,  00feh, OFFSET nak    , SEG nak    ,  11 

;----------------------------- AU status messages -----------------------------
; Each message is a string of exactly 15 characters.
; These messages are now loaded into here from MSGRUN by "msgload()"
;  X's are just place holders for debug aid
;
ausm1   db "XXXX-AUSM1-XXXX"
ausm2   db "XXXX-AUSM2-XXXX"
ausm3   db "XXXX-AUSM3-XXXX"
ausm4   db "XXXX-AUSM4-XXXX"
ausm5   db "XXXX-AUSM5-XXXX"
ausm6   db "XXXX-AUSM6-XXXX"
ausm7   db "XXXX-AUSM7-XXXX"
ausm8   db "XXXX-AUSM8-XXXX"
ausm9   db "XXXX-AUSM9-XXXX"
ausma   db "XXXX-AUSMA-XXXX"
ausmb   db "XXXX-AUSMB-XXXX"
ausmc   db "XXXX-AUSMC-XXXX"
ausmd   db "XXXX-AUSMD-XXXX"
ausme   db "XXXX-AUSME-XXXX"

; Array of real mode pointers to the 15 strings above. This was _austab in the real mode program, 
; initialized by msghand:msgload(). Now, the protected instance, gpsysp.dll defines _austab as an equivalent
; array of protected addresses.
raustab dd REALDSEG:ausm1, REALDSEG:ausm2, REALDSEG:ausm3
    dd REALDSEG:ausm4, REALDSEG:ausm5, REALDSEG:ausm6, REALDSEG:ausm7
    dd REALDSEG:ausm8, REALDSEG:ausm9, REALDSEG:ausma, REALDSEG:ausmb
    dd REALDSEG:ausmc, REALDSEG:ausmd, REALDSEG:ausme

ALIGN 2
_undisgpvec dd 0
gprealvec   dd 0
gpprotvec   dd 0
errbp       dw 0                    
errsi       dw 0
errdi       dw 0
vidselect   dw 0            ;Video address selector for protected module.
REALDATA    ENDS
endif                       ;if (MODESELECT eq XREAL) or (MODESELECT eq REAL)

if MODESELECT ne XREAL
PROTDATA    SEGMENT WORD PUBLIC 'DATA'
PUBLIC      _austab         ;Protected mode table for msghand:msgload.
_austab dd ausm1, ausm1+15, ausm1+30, ausm1+45, ausm1+60, ausm1+75, ausm1+90
    dd ausm1+105, ausm1+120, ausm1+135, ausm1+150, ausm1+165, ausm1+180
    dd ausm1+195
PROTDATA    ENDS
endif

;***************************************************************************
;                         CODE
;***************************************************************************
ASSUME cs:_CSNAME, ds:REALDSEG
_CSNAME SEGMENT DWORD PUBLIC  'CODE'

;******************** GENERAL-PURPOSE FUNCTIONS *************************
if (MODESELECT eq XREAL) or (MODESELECT eq REAL)
COMMENT $--------------------- DMASET ------------------------------------
        Set up and enable DMA to/from GPIB. Caller passes pointer to string src/dest descriptor in di and mode selector in cl.
Descriptor is a 6 byte array of address (2byte offset, 2byte segment), length. Mode selection is SNGLM OR DMAR/DMAW OR DMACH.
$
dmaset  PROC FAR
        out clrbp,al            ;Write anything to reset byte selector.
;...Convert SEG+OFF address into 20-bit real and write to page register and DMAC...
        cmp [dprem], 0fh
        ja noresid              ;Use residual page > 0f to flag no residual due to previous split physical segment.
;....... this call to dmaset is to continue a transfer accross physical boundary..........
        xor al,al
        out dmamar,al           ;Next address A0-7
        jmp $+2
        out dmamar,al           ;A8-15
        mov al,[dprem]          ;Get the page remainder (previous + 1)
        out dmapr,al
        mov [dprem],20h         ;Flag no residual.
        mov ax,[dmarem]
        jmp short setc          ;Go set up count.
noresid: mov ax,[di+2]          ;Get addr bits 4-20.
        rol ax,4                
        mov ch,al               ;Save page nibble
        and al,0f0h             ;Remove shifted A16-19
        add ax, [di]            ;Add address offset to shifted segment
        jnc noovr
        inc ch
noovr:  out dmamar,al           ;Write a0-7
        jmp $+2
        xchg al,ah
        out dmamar,al           ;Write a8-15
        xchg al,ah              ;Convert back to address.
;....... see if requested length splits a physical address segment boundary....
        neg ax                  ;Available length til overflow
        mov dx,ax               ;Head room result may be needed for dma length.
        sub dx, [di + 4]        ;If headroom - requested length >= 0 then no physical segment crossing.
        jnc nocross
;........ process first half of a split transfer .......................
        push ax                 ;Use headroom for length of first transfer.
        neg dx                  ;Convert borrow to positive remainder.
        mov [dmarem],dx         ;Save for transfer completion.
        mov al,ch               ;Retrieve page nibble
        and al,0fh              ;Force into lower 1M address
        out dmapr,al            ;Write to page register
        inc al                  ;Prepare next page in case there is residual.
        jmp short setpc         ;Go set page and count.
nocross: push [ di + 4 ]        ;Requested length.
        mov al,ch               ;Retrieve page nibble
        and al,0fh              ;Force into lower 1M address
        out dmapr,al            ;Write to page register
        mov al,20h              ;Flag no residual by page > 0fh
setpc:  mov [ dprem ], al       ;Store flag = "no residual" or next page if split transfer.
        pop ax                  ;Get byte count
setc:   dec ax                  ;Adjust for dma count -1.
        out dmacr,al            ;Write count bits 0-7
        mov al,ah
        jmp $+2
        out dmacr,al            ;Write count bits 8-15
;.......Now set up controls...................................
        mov al,cl               ;Retrieve mode control
        jmp $+2
        out dmamod,al           ;Set mode for GPIB's DMA channel.
        outfd ziacr, DMAARM     ;Enable DMA REQ and TC interrupt ff.
        mov al,DMACH
        out dmamsk,al           ;Enable DMA to service GPIB channel.
        jmp $+2                 
        ret
dmaset  ENDP

;******************** NORMAL PROTOCOL **********************************
COMMENT $ ------------------- SETDUMP --------------------------------
$
setdump PROC NEAR
        out clrbp, al           ;Reset even/odd byte ff.
        mov ax, SEG dump        ;Get addr bits 4-20.
        rol ax,4                
        mov ch,al               ;Save page nibble
        and al,0f0h             ;Remove shifted A16-19
        add ax, OFFSET REALDSEG:dump ;Add address offset to shifted segment
        jnc nvr
        inc ch
nvr:    out dmamar,al           ;Write a0-7
        jmp $+2
        xchg al,ah
        out dmamar,al           ;Write a8-15
        mov al,ch               ;Retrieve page nibble
        and al,0fh              ;Force into lower 1M address
        out dmapr,al            ;Write to page register
        jmp $+2
        mov al, 15
        out dmacr, al           ;Count low = 15 to transfer 16
        jmp $+2
        xor al,al
        out dmacr, al           ;Count high = 0.
        mov al, SNGLM OR DMAW OR DMACH
        jmp $+2
        out dmamod, al 
        jmp $+2 
        outfd ziacr, DMAARM    ;Enable interrupt on GPIB and TC 
        jmp $+2
        mov al, DMACH
        out dmamsk, al          ;Enable DMA channel.
        jmp $+2
        ret
setdump ENDP

COMMENT $----------------- GPISR --------------------------------------
        GPIB interrupt service routine-- including DMA TC.
$
gpisr   PROC FAR                
        sti
        push ax
        push cx
        push dx
        push di
        push ds
        cld                     ; Make sure direction right in interrupt processing
        ds_parm
        jmp [ gpivec ]
;.................... GPS0: BO after last byte tx .......................
COMMENT $ Interrupt on BI but don't read gpib data. Instead, use it as a trigger to launch the dma. Otherwise the previous 
TX TC req/grnt will sometimes remain (asserting NBAF doesn't always fix this problem). This state is our initial launch 
point. For general error recovery, we could establish this point as a long jump target of watch dog time out. At each pass
through gps0, set timer to end shortly before we would expect the next input. If the tick times out then simulate the gpib
interrupt from tick isr. This would effectively relaunch the cycle.
$
gps0:   outfd int0, 0           ;Disable BI and BO interrupts.
;...... Set up dma to receive the first 12 bytes input.................
;For efficient dma, I assume that the input header doesn't cross a physical address boundary.
nextcyc: mov [ gpivec ], OFFSET gps1    ;Skip dma2-- the boundary cross checker.
        mov cl, SNGLM OR DMAW OR DMACH
        mov di, OFFSET REALDSEG:ihdesc ;Pass input header descriptor to dmaset. 
        call dmaset
        jmp isrx                
;.................... GPS1: DMA RX TC at end of header....................
gps1:   outfd ziacr, DMARES     ;Reset the DMA TC interrupt ff.
;...... Extract input communication type and class, converting to a single word "TC"........
        push es                 ;Used throughout this state.
        push bx
        mov ax, ds
        mov es, ax              ;Set up es for quick linear search.
        mov ah, BYTE PTR [inhead + 3] ;Get communication type low byte.
        mov al, BYTE PTR [inhead + 5] ;Major classification low byte.
        mov cx, AUTCC           ;Count to limit scanning of input TC table.
;.....Search input response list for match to TC ......................
        mov di, OFFSET REALDSEG:rxtc  ;Set table begin address for search.
        repne scasw
        jne badhead
        sub di, OFFSET REALDSEG:rxtc+2 ;Go back to address of match and convert to index (0-based and * 2 ). 
        mov [ inpind ], di      ;Save for selecting the "input-available" mask after the input is complete.
        cmp di, 16              ;Every item after index 14/15 has identical response to 14/15.
        jb seltran
;.....iscb should use ispd buffer ............. 747D ....................
        cmp di, 18              ;iscb is the exception.
        jne seltr14
        mov di, 10              ;Let iscb share the descriptor for ispd.
        jmp seltran
seltr14:mov di, 14              ;Let all test results share the descriptor for spld.

;Test results share only the response descriptor, not the input available flag selector, accessed by inpind.
seltran: shl di, 3              ;Convert *2 index to *16 for accessing the response descriptors.
        add di, OFFSET REALDSEG:_gptrans ;Compute actual address of response descriptor.
        mov [ transact ], di
        mov cx, WORD PTR [ inhead + 8 ] ;Get length listed in header. MOTO order long with upper 2 bytes assumed 0.
        xchg ch, cl             ;Convert to internal byte order.
        cmp cx, [ di + 4 ]      ;Compare to length listed in the descriptor.
        ja badhead              ;If length from header > listed in table then header is wrong.
        sub cx,12               ;Compensate for bytes already received.
        mov [ dstruct + 4 ], cx ;Even if input is done, we need this for next state to determine checksum range.
        jz nodat                ;If done then skip input remainder.
;Store tciip only after verifying that further input is required. Otherwise, it retains NOTC.
        mov [ tciip ], ax       ;Save TC input-in-progress.
        les bx, [ di ]          ;Get destination address from table.
        mov ax, WORD PTR [ inhead + 10 ] ;Retrieve the first two data bytes.
        mov es:[ bx ], ax       ;Transfer to destination.
        add bx, 2
        mov [ dstruct ], bx     ;Point dma to fill starting at third byte.
        mov [ dstruct + 2 ], es
        mov [ dma2vec ], OFFSET gps2    ;Next communication state is waiting for end of input tail.
        mov [ gpivec ], OFFSET dma2     ;But first we may have to help the brain-damaged DMAC.
        mov cl, SNGLM OR DMAW OR DMACH
        mov [ dma2typ ], cl
        mov di, OFFSET REALDSEG:dstruct
        call dmaset
        pop bx
        pop es
        jmp isrx
nodat:  pop bx                  ;Clean up and fall through if header is complete input.
        pop es
        jmp gps2
;Badhead branch is taken if input TC matches nothing in table or if heads lists a length > listed in table (< is OK). 

badhead:
;Be sure that dumper pops bx then es before leaving.
dumper: farin int0              
        test al, ENDMASK
        jz noteoi
        pop bx
        pop es
;        jmp short nakbh
        mov [ respptr ], OFFSET REALDSEG:nakd
        push si
        jmp chkmata
noteoi: outfd int0, 10001000b   ;Interrupt on last byte (received with EOI).
        mov [ gpivec ], OFFSET dumpeoi
        call setdump
        pop bx
        pop es
        jmp isrx
dumpeoi:
        outfd ziacr, DMARES  
        jmp $+2 
        farin int0
        test al, ENDMASK
        jnz endump
        call setdump
        jmp isrx
endump:
        out clrbp, al
        jmp $+2 
        mov al,0ffh
        out dmacr, al
        jmp $+2
        out dmacr, al
        jmp $+2
        outfd int0, 0
nakbh:  mov [ respptr ], OFFSET REALDSEG:nakd
        push si
        jmp chkmata

;:::::::::::::::::::::: Input to Output Transition :::::::::::::::::::::::::::
;..................... GPS2: DMA RX TC at end of input ....................
gps2:   push si
;.......Compute checksum of first 10 bytes of header..................
        mov cx, 5
        mov si, OFFSET REALDSEG:inhead
        xor dx, dx
headck: lodsw                   ;Next ds:si->ax
        xchg ah, al             ;Convert MOTO to INTEL
        add dx, ax
        loop headck
        mov cx, [ dstruct + 4 ] ;Get the remaining input length.
        or cx, cx               ;Was there any data?
        jz cmpchk
;.......Add the data words to checksum.......................
        shr cx, 1               ;Byte length / 2 for word checksumming. Note that data count -2 compensates for chksm not counted.
        lds si, DWORD PTR [ dstruct ]   ;Get data destination<--- watch out for ds change.
        sub si, 2               ;We left destination + 2 for dma in previous state. Now we need the beginning again.
tailck: lodsw
        xchg ah, al
        add dx, ax
        loop tailck
cmpchk: mov ax, [ si ]          ;Get input checksum from header if no data else from end of data.
        xchg ah, al             ;Convert MOTO to INTEL.
        mov cx, DSEG
        mov ds, cx              ;Restore ds in case we did have data. Equivalent to ds_parm.
        mov di, OFFSET REALDSEG:nakd  ;Assume checksum mismatch response. Set up pointer to nak "response" descriptor.
        cmp ax, dx              ;Does computed chk match input chk?
        jne chkmat              ;We can leave tcoip = NOTC because no one writes into nak source.
;.......Process input................................................
        cmp [ tciip ], 0104h    ;Test for special case input tsak. AU manager ignores tsak. 
        jne iaflg
;XDOS change: set up far data segment to access host comm inqstat.
        push ds
        mov ax, SEG _inqstat    
        mov ds, ax
        xor ax,ax
inqstatref:
        mov WORD PTR [ _inqstat ], ax  ;Clear request pending flags in dbm/hcman since it is too late now.
        mov WORD PTR [ _inqstat + 2 ], ax
        mov BYTE PTR [ _inqstat + 4 ], al
        pop ds
        jmp short prpr          ;Go directly to prepare response, skipping instat flagging.
;...... Use TC match index to get mask and set input available flag. 
iaflg:  mov di, [ inpind ] 
        mov ax, [ avaif + di ]  ;Get input available flag selector.
        mov cl, ah              ;Copy byte selector.
        xor ch, ch              ;And convert to 16 bit offset.
        mov di, cx
        or BYTE PTR [ _instat + di ], al ;Set selected bit.
;....... Check for remaining special case inputs.............................
        cmp [ tciip ], 0105h    ;Is input rrak?
        je rreq                 ;Go set up rerun response into the buffer shared by rrak_x and rrif_x.
        cmp [ tciip ], 0b00h    ;Is input stat_x?
        je disaus               ;Go display au status. Then "return" to prpr.
;...... Prepare response transmission................................
prpr:   mov di, [ transact ]    ;Retrieve transaction descriptor address.
        mov ax, [ di + 6 ]      ;Get tx TC
        mov [ tcoip ], ax       ;Save to protect output from further changes during transmission.
        cmp BYTE PTR [ di + 9 ], 0 ;MSByte (Intel form) of CHKST/CksLSB is the CHKST. 0 = don't recompute chksum.
        je chkmat               ;If CHKST is 0 then we don't have to recompute checksum.
;.......Since this transaction's CHKST is not 0, we have to compute the tx response checksum.   
        dec BYTE PTR [ di + 9 ] ;Assume CHKST was 1 and convert it to 0.
        jz chkstok
        mov BYTE PTR [ di + 9 ], 2 ;If it wasn't 1 then it must have been 2, which we aren't allowed to change.
chkstok: mov cx, [ di + 14 ]    ;Get source total length - 1 (allows us to pass the descriptor to dmaset).
        dec cx                  ;Total length - 1 - 1 = length w/o checksum word.
        shr cx, 1               ;Word check is byte count / 2.
        lds si, [ di + 10 ]     ;Get source address. ******DS change*****
        xor dx, dx              ;0 the chksm accumulator.
txchk:  lodsw                   ;Next ds:si->ax
        xchg ah, al             ;Output string is MOTO and must be converted to INTEL to compute checksum.
        add dx, ax
        loop txchk
        mov [ si ], dh          ;Store checksum MSByte as last dma tx'd byte.
        ds_parm
        mov BYTE PTR [ di + 8 ], dl     ;Save checksum LSByte in CHKST/CksLSB low byte (Intel form).
chkmat: mov [ respptr ], di     ;Save pointer for next isr state to use.
chkmata: mov [ gpivec ], OFFSET gps2a
        mov [ tciip ], NOTC     ;No input is in progress now.
        outfd int0, 90h         ;Enable BO interrupt.
        pop si
        jmp isrx
;...... Prepare real-time response to rerun inquiry. In rrak_x, out rrif_x.....
rreq:   mov di, [ transact ]    ;Point to response descriptor regardless of outcome of rerun test.
        mov WORD PTR [ rrif + 14 ], 0   ;Assume response is no rerun.
        mov ax, [ _rerack ]
        cmp ax, WORD PTR [ rrif + 12 ]  ;Compare rack inq. to req. (MOTO forms).
        jne chkstok             ;Share the normal checksum calculator.
        mov WORD PTR [ rrif + 14 ], 0100h ;Store "0001" to request rerun.
        mov WORD PTR [ _rerack ], 0     ;Takehashi says this is needed in some bizarre user operating sequences.
        jmp chkstok             
;.......Display AU status...................................................
;     If  STAT_X keycode == received keycode 
;     then clear status_blink flag 
disaus: push es
        mov di, [_gptrans]       ;Get offset of STAT_X
        mov es, [_gptrans+2]     ;Get segment of STAT_X
        mov al, es:[di+201]      ;Get input key code from STAT_X
        xor ah, ah
        cmp ax, [ _keyinput ]   ;Compare input key and key acknowledgement
        jne daus                ;Go to display AU part if not equal
        mov [_status_blink], 0  ;else clear status_blink flag 
;
daus:   mov ax, 0b800h
        mov es, ax              ;Segment address for direct video write.
        mov di, 58              ;Destination is row 0, col. 29.
        mov al, [ inhead + 11 ] ;Second data byte of stat_x
        xor ah, ah              ;Convert to word.
        mov si, ax
        cmp si, 13
        jbe ausok
        mov si, 14              ;If unrecognized status then select error string.
ausok:  shl si, 2               ;Scale * 4 for FAR pointers to source strings.
        mov si, WORD PTR [ raustab - 4 + si ] ;Convert to 0-based index and use to extract string pointer.
        mov cx, 15              ;Character count.
        mov al, [_status_blink]
        or  al, 07h              ;Attribute byte 
audl:   movsb                    ;mov message byte to video memory
        mov es:[di], al          ;Set attribute bytes,
        inc di                   
        loop audl
        cmp [ _gpactiv ], 0
        je gisrx                ;Display subclass only if requested at cu command line. 
        lds si, DWORD PTR [ dstruct ] ;Get destination addressed we passed to dma set.
        mov al, [ si + 1 ]      ;Get second byte of second word.
        add al, 40h             ;Convert 1-35 to 'A' - 'q'
        mov es:[ di + 2 ], al
        ds_parm                 ;Restore ds to local
gisrx:  pop es
        jmp prpr
;.................. GPS2A: Waiting for AU to make us talker................
;I have broken this part out of gps2 to help AU in case it has trouble at TX/RX change and to reduce the execution time of gps2.
gps2a:  outfd int0, 0
        mov di, [ respptr ]
        mov al, [ di + 8 ]              ;Retrieve checksum low byte from most recent calculation.
        mov [ lasttx ], al
        add di, 10                      ;Point to output dma descriptor.
        mov [ dma2vec ], OFFSET gps3    ;Next communication state is waiting for TX DMA TC.
        mov [ gpivec ], OFFSET dma2     ;But first we may have to help the brain-damaged DMAC.
        mov cl, SNGLM OR DMAR OR DMACH
        mov [ dma2typ ], cl
        call dmaset
        jmp short isrx
;......................GPS3: DMA TX TC at penultimate byte......................
gps3:   outfd int0, 10010000b   ;Advance to next state on BO instead of DMA TC, which is too soon for reliable tx.
        mov [ gpivec ], OFFSET gps3a
        jmp short isrx
;..................... GPS3A: BO at penultimate byte.............................
gps3a:  outfd auxcmd, FEOI
        mov al, [ lasttx ]
        farout gpdat
        mov [ tcoip ], NOTC     ;Flag OK to write into any output source at this time.
        farin int0
        test al, BOMASK
        jnz gps4                ;If strobes already settled then we are done with this RX/TX cycle.
        mov [ gpivec ], OFFSET gps4
isrx:   pop ds                  
        pop di
        pop dx
        pop cx
        cli
        mov al,piceoi
        out picctl,al           ;Issue EOI to PIC.
        pop ax
        iret
;..................... GPS4: BO at end of last byte..........................
;Use this time to force EOI off to compensate for apparent TI9914 error.
gps4:   
    outfd auxcmd, NBAF      ;Set "new byte available false" to force EOI off. This also eliminates a carry over dreq/grnt.
    mov [ gpivec ], OFFSET gps0
    cmp [ _gpactiv ], 0
    je gps4a                ;Display activity only if requested at cu command line. 
    xor [ gpdispl ], 22h    ;Toggle color to indicate change.
    mov al, [ gpdispl ]
    mov cx, 0b800h
    mov ds, cx
    mov ds:[ 91 ], al       ;Apply to the au minor status display.
gps4a:  
    outfd int0, BIINT       ;Interrupt on BI to launch RX
    mov ax, SEG _gptime     ;Note move from gps4 to here so that we don't have to save and restore ds (DM 6/12/94).
    mov ds, ax
gptimeref: ;For verifying real mode address.
    mov [_gptime], DISCONT
    mov [ _gptimef ], 0     ;Reset gpib timeout. The system manager will dectect disconnect by polling gptimef.
    jmp isrx

;......... DMA2: fixes dma in case of physical address boundary split ......
dma2:
    outfd ziacr, DMARES     ;Reset the DMA TC interrupt ff.
    cmp [dprem], 0fh        ;Check page register/split xfer flag.
    ja dmaenda
    mov cl, [ dma2typ ]
    call dmaset
    mov [ gpivec ], OFFSET dmaend
    jmp isrx
dmaend: 
    outfd ziacr, DMARES     ;Reset the DMA TC interrupt ff.
dmaenda: 
    jmp [ dma2vec ]
gpisr       ENDP
endif                       ;(MODESELECT eq XREAL) or (MODESELECT eq REAL)

;********************** NORMAL INTERFACE FUNCTIONS ************************
if MODESELECT ne XREAL      

COMMENT $------------------ _GPACCESS ------------------------------------
        Read or write to input/output buffers with concurrency protection.
int gpaccess( US ctype, US mode, US len, UC far *dest, UC far *src,... ) ;
ctype: compressed communication type in INTEL form.
mode: 0=undispatch if blocked. 1=return error if blocked. 
load structures: arbitrary count terminated by US 0 (in "len" position).
$
_gpaccess   PROC FAR
        push bp
        mov bp,sp
        push ds
        push si
        push di
        ds_parm
tstct:  cli                     ;Establish concurrency in test vs. execution.
        mov ax, [ bp + ARG1 ]   ;Get communication type to be protected from.
        cmp ax, [ tciip ]       ;Does it match input in progress?
        je accblk
        cmp ax, [ tcoip ]       ;Match output in progress?
        je accblk
        add bp, ARG1 + 4        ;Point to first descriptor in load list.
        mov cx, [ bp ]          ;Get length of first data block.
setacc: les di, DWORD PTR [ bp + 2 ]    ;Destination address.
        lds si, DWORD PTR [ bp + 6 ]    ;Source address.
        cmp cx, 5               ;I have not thoroughly analyzed for best size break, as this is statistical.
        ja stropt               ;Go optimize string move if size warrants.
        rep movsb               ;Else use simple byte move.
accyc:  add bp, 10              ;Point to the next structure in load list.
        mov cx, [ bp ]
        or cx, cx               ;0 length marks end of load list.
        jnz setacc
        sti
        xor ax, ax              ;Return 0 to indicate success.
        jmp short gpaex
stropt: test di, 1              ;Test destination for odd. Hopefully, we won't have odd/even src/dest or vice-versa.
        jz deven
        movsb                   ;Adjust to even address.
        dec cx
deven:  xor ah, ah              ;Default flag for case of even remaining count.
        shr cx, 1               ;Change remaining byte count to word count.
        jnc ceven               ;If count was even then we don't need a final residual byte write.
        inc ah                  ;Flag that a final byte move is needed.
ceven:  rep movsw
        dec ah
        jnz accyc
        movsb                   ;Move the final odd byte.
        jmp accyc
;.......Access is blocked. Mode 0=undispatch, 1=return...................
accblk: sti
        cmp WORD PTR [ bp + ARG1 + 2 ], 0
        je undis                ;Undispatch or return with non-0 ctype in ax to indicate error.
gpaex:  pop di
        pop si
        pop ds
        pop bp
        ret                     
undis:  call ds:[_undisgpvec]
        jmp tstct               ;Try it again at next dispatch.
_gpaccess ENDP

COMMENT $ ---------------------- AU_STATUS ---------------------------
void au_status()
Display blinking AU status, this function is for key_input to
call whenever a START, STOP, S.STOP was accepted
$
_au_status  PROC FAR
    push bp
    mov bp,sp
    push di
    push si
    push ds
    ds_parm
    push es
if  MODESELECT eq REAL
    mov di, [_gptrans]     ;Get offset of STAT_X
    mov es, [_gptrans+2]   ;Get segment of STAT_X
else
    mov di, SEG REALDSEG:_stat_x
    mov es, di
    mov di, OFFSET REALDSEG:_stat_x
endif                       ;Not MODESELECT eq REAL
    mov al, es:[di+1]       ;Get AU status from STAT_X
    xor ah, ah              ;Convert to word.
    mov si, ax              ; NOTE: ax has value of 1 - 13.
    mov di, 58              ;Destination is row 0, col. 29.
;    mov ax, 0b800h
    mov ax, VIDEOADDRESS
    mov es, ax              ;Segment address for direct video write.
    cmp si, 13
    jbe ausok1
    mov si, 14              ;If unrecognized status then select error string.
ausok1: 
    shl si, 2               ;Scale * 4 for pointers to source strings.
    mov si, WORD PTR [ raustab - 4 + si ] ;Convert to 0-based index and use to extract string pointer. It is OK to
; Use the real mode table because we only need the offset, which is the same in either mode.
    mov cx, 15              ;Character count.
    mov al, 87h             ;Attribute - set blinking
audl1:  
    movsb
    mov es:[di], al         ;Set attribute bytes,
    inc di                  
    loop audl1
audx:   
    pop es
    pop ds                  
    pop si
    pop di
    pop bp
    ret
_au_status  ENDP

;********************** STARTUP FUNCTIONS *****************************
COMMENT $-------------------- _GPCYCLE --------------------------------
        Launch continuous gpib isr cycle.
$
_gpcycle    PROC FAR
    push ds
    ds_parm
    outfd ziacr, DMARES     ;Reset the DMA TC interrupt ff.
    mov [ gpivec ], OFFSET gps0     ;Set up initialization point for isr.
    cli
    in al,picimr
    and al,NOT engpint      ;Unmask our physical IRQ
    out picimr, al          
    sti
    int gpiv                ;This launches a continuously self-recycling background process based on isr/dma.
    pop ds
    ret
_gpcycle    ENDP

COMMENT $---------------------- gpjump ----------------------------------
$
gpjump  PROC FAR
    call _afteritime        ;Restore interrupted code.
    ds_parm
    mov di, [ errdi ]
    mov si, [ errsi ]
    mov bp, [ errbp ]
    mov ds, [ bp - 2 ]
    mov sp, bp
    pop bp                  
    sti
    ret
gpjump  ENDP

COMMENT $---------------------- GPSETJ ------------------------------------
        Assume that all callers have done push bp: mov bp,sp: push ds: call us.
Also, assume that high level caller has passed time delay as the first parameter. Assume that the asm module that includes
timer isr uses the same DS as we do here. Otherwise our attempt to access gpsti and gpstif will fail.
$
gpsetj      PROC NEAR
    ds_parm 
    mov ax, SEG _gpsti
    mov es, ax
    mov ax, [ bp + ARG1 ]   ;Get time delay
    mov es:[_gpsti], ax
    mov es:[_gpstif], 0
    mov [ errbp ], bp       ;Save sp(-2) for long jump on error.
    mov [ errsi ], si
    mov [ errdi ], di
    ret
gpsetj      ENDP

COMMENT $-------------------- GPOUT -----------------------------------------
        Output one byte to GPIB. This could be device or bus data, depending on present condition of ATN.
Caller passes byte in ah. Return NZ if error.
$
gpout   PROC NEAR
        farin int1
        test al,ERRMASK         ;If ERR bit then the intended receiver is asleep.
        jnz poutx
        mov dx,int0
isordy: in al,dx
        test al,BOMASK
        jz isordy
        mov al,ah
        farout gpdat
        xor al,al
poutx:  ret                     ;Returns NZ if error.
gpout   ENDP

COMMENT $-------------------- WAITGPS --------------------------------------
        Wait for GPIB strobes to complete. Called before taking or leaving control (TCA/S).
$
waitgps PROC NEAR
wgpsl:  farin bstat             ;Read raw GPIB bus status
        and al,01110000b        ;Read only DAV, NDAC, and NRFD
        cmp al,00100000b
        jne wgpsl
wgpex:  ret                     
waitgps ENDP

COMMENT $-------------------- GPCOM ----------------------------------------
        Send 1 to 2 GPIB commands when we are the controller. Caller passes the first command byte in cl, next in ch. To send 
only one, put ffh in ch. Return NE/NZ if GPIB error. Assert ATN synchronously (9914's TCS doesn't work) at the beginning. ATN
is left asserted in case the caller is making AU talker and needs to make itself listener before disasserting ATN. Note that
TON is asserted and then deasserted here. Caller should reassert TON to talk.
Input: cx
Uses: dx, ax
$
gpcom   PROC NEAR
        farin astat
        push ax                 ;Save our GPIB address status to restore after temporarily going to TON.
        call waitgps
        outfd auxcmd, TCA
        jmp $+2
        jmp $+2
        outfd auxcmd, TON
        jmp $+2
        mov ah,cl               ;Send the first byte w/o question.
        call gpout
        jnz gpcex
        cmp ch,0ffh
        je gpcex
        mov ah,ch
        call gpout
        jnz gpcex
        call waitgps            ;Now caller can assume it is safe to do GTS.
        xor ax,ax               ;Set up return Z flag.
gpcex:  pop ax                  ;Recover address status.
        pushf                   ;Save ZF return value.
        test al,2               ;Were we TADS?
        jnz gpcexx              ;Remain TON if so.
        outfd auxcmd, TONCLR
        jmp $+2
        outfd auxcmd, LON       ;Else assume listener and restore it. We must be one or the other.
        jmp $+2
gpcexx: popf                    ;Recover return flag Z.
        jmp $+2
        ret                     ;Return Z if all OK else NZ.
gpcom   ENDP

COMMENT $--------------------- DMAWAIT -------------------------------------
    Caller passes ds:di as pointer to UC far * src/dest, US length, US eoiflag. Most of the callers use dstruct for
the transfer structure. We don't want to reuse this to pass arguments to dmaset, because we will be changing the 
src/dest selector to segment. It would be possible to simply restore the old one but the safest policy is to use a
dedicated location and restore the incoming ds and di. Then any caller is safe.
    Caller also pass dma control byte in cl. This is passed through to dmaset.
$
dmawait PROC NEAR
if  MODESELECT eq REAL
    call dmaset       
else
    push ds                 ;Save these for restoration. This isn't really needed with the existing programs but
    push di                 ;provides greater generality.
    push ds:[di+4]          ;Save the length argument.
    push cx                 ;Protect the dma control argument.
;Convert the select:offset pointer to segment:offset to pass to real mode dmaset.
    push [di+2]             ;Selector of src/destination.
    push [di]               ;Offset of src/destination.
    call DOSPROTTOREAL      ;Pascal function clears stack. Returns real address in dx:ax.
    mov bx, DSEG
    mov ds, bx              ;Access real data.
    mov [dmaload], ax       ;Pass pointer to dmaset in real data.
    mov [dmaload+2], dx
    pop cx                  ;Recover the dma control argument.
    pop [dmaload+4]         ;Recover the length argument and pass it to dmaset.
    mov di, OFFSET REALDSEG:dmaload
    call dmaset
    pop di
    pop ds
endif
tstdma: 
    farin dmacr             ;Read remaining count low.
    mov ah,al
    jmp $+2
    in al, dx               ;Remaining count high.
    inc ax                  ;Count = ffff increments to 0.
    jnz tstdma              ;If count is ffff then dma is done.
    ret                     ;Return ax = 0 and Z.
dmawait ENDP

COMMENT $-------------------- GPTX ------------------------------------
        Transmit the string passed by reference. 
int gptx( US timeout, char far *src, US length, US frame ) ; /* If frame = 0, 
this is the last (or only) frame and we will send EOI with the last byte. */
$
gptxexv: jmp gptxex
_gptx   PROC FAR
        push bp
        mov bp,sp
        push ds
        call gpsetj             ;Set long jump leaves ds_parm'd
        push si
        push di
        farin int1      
        mov cx, 4100h + UNL     ;UNL followed by MTA(CU:41h).
        call gpcom
        jnz gptxex
        mov cx, 0ff00h + 22h    ;MLA(AU:22h)
        call gpcom              ;Untalk and unlisten everyone.
        jnz gptxex
        outfd auxcmd, TON       ;Gpcom retained previous status which was probably LADS.
        outfd auxcmd, GTS
;....... Now set up the transfer..................................
        mov di,ss
        mov ds,di
        mov di, bp
        add di, ARG1 + 2        ;Pass transfer descriptor by reference.
        dec WORD PTR [ di + 4 ] ;Length -1 to save last transfer to do with EOI.
        mov cl, SNGLM OR DMAR OR DMACH  ;Read from memory to GPIB command.
        call dmawait
        mov ax, SEG dprem
        mov es, ax
        cmp es:[dprem], 0fh        ;Check page register/split xfer flag.
        ja gptbo
        mov cl, SNGLM OR DMAR OR DMACH  
        call dmawait
gptbo:  farin int0
        test al, BOMASK
        jz gptbo
        cmp WORD PTR [ bp + ARG1 + 8 ], 0
        jne txlb
        outfd auxcmd, FEOI      ;Assert EOI on last byte of final frame.
txlb:   lds di, [ bp + ARG1 + 2 ]       ;Get source begin address.
        add di, [ bp + ARG1 + 6 ] ;Calculate address of last byte. Length is requested value - 1.
        mov al, [ di ]
        farout gpdat
;Added the following 5 line to wait 20 micro sec before calling waitgps
;by Mashiko.
;        jmp $+2
        push cx               ;2 clock
        mov cx, 021h          ;2 clock
waiteoi: jmp $+2              ;3 clock  -+-- * 33 
        loop waiteoi          ;12 clock _|
        pop cx                ;4 clock
;
        call waitgps
        outfd auxcmd, NBAF      ;Set "new byte available false" to force EOI off. This also eliminates a carry over dreq/grnt.*/
        jmp $+2
        xor ax,ax               ;Return no error.
gptxex: pop di
        pop si
        pop ds
        pop bp
        ret
_gptx   ENDP

COMMENT $-------------------- BLOCKIN -----------------------------------
    This calls dmawait, which expects ds:si pointing to a transfer structure as follows:
UC far * dest/src, US length, US eioflag. 
$
blockin PROC NEAR
        mov cl, SNGLM OR DMAW OR DMACH  ;Write GPIB to memory command.
        call dmawait
        cmp [dprem], 0fh        ;Check page register/split xfer flag.
        ja blinx
        mov cl, SNGLM OR DMAW OR DMACH  ;Write GPIB to memory command.
        call dmawait
blinx:  ret                     
blockin ENDP

COMMENT $-------------------- GPRX --------------------------------------
US gprx( US timeout, UC far *des, US length ) ; /* Length is dummy for CU but is used by AU. Return 0 if normal frame, 1 if error, 0fh if
startup. Caller can read input length from global gpinlen.
$
_gprx   PROC FAR
        push bp
        mov bp,sp
        push ds
        call gpsetj             ;Leaves ds_parm'd
        push si
        push di
;.......Make AU talker and ourself listener...........................
        mov cx, 4200h + UNL
        call gpcom              ;Untalk and unlisten everyone.
        jnz gpiderr
        outfd auxcmd, TONCLR    ;gpcom retained previous state which was probably talker.
        outfd auxcmd, LON       ;Make ourself listener.
        outfd auxcmd, GTS
;Transfer the destination address to data segment to help brain damaged 80286.
        les ax,[ bp + ARG1 + 2 ]
        mov [dstruct],ax
        mov [dstruct + 2],es
        mov di, OFFSET REALDSEG:dstruct  ;Pass structure address to dmaset.
;Input the first 12 bytes to determine the total length of input.
        mov [dstruct + 4], 12
        call blockin            ;Input the header via dma.
        add [dstruct], 12       ;Next address: 12 already received.
        les bx, [ bp + ARG1 + 2 ] ;Get destination address again.
        mov al, es:[bx]         ;Read the first input byte.
        mov [iftype],al         ;Save frame type: 0, 0fh, or error.
        or al,al                ;Is the first input byte 0, i.e. normal frame?
        jnz notnorm
        mov ax, es:[ bx + 8 ]   ;Read Moto-ordered length lowest 2 bytes. The upper two are 0.
        xchg al,ah
        mov [_gpinlen], ax      ;Save for caller.
        sub ax, 12              ;Subtract the 12 bytes already received.
        jz gpiex
        mov [dstruct + 4],ax    ;Pass remaining length indirectly to dmaset.
        jmp short rxrmdr
notnorm: cmp al,0fh
        jne gpiex               ;If first byte isn't 0 or 0fh then it is incorrect.
        mov [dstruct + 4], SFRSIZE - 12 ;Assume startup frame size - 12 already received.
rxrmdr: call blockin
gpiex:  mov al,[iftype]
        xor ah,ah               ;Return US value of frame: 0, 0fh, error.
gpiex1: pop di
        pop si
        pop ds
        pop bp
        ret
gpiderr: mov ax, NOLSTNR        ;AU didn't respond to interface commands, i.e. ERR flag.
        jmp short gpiex1
_gprx   ENDP

COMMENT $ -------------------- _CONFIGGP -------------------------------------
        Configure GPIB.
void configgp( int new_old ) ; /* 1 = configure new. 0=restore old config. */
Add to this a test for inhead address not splitting dma physical address boundary.
$
_configgp   PROC FAR
    startproc
;Disable interrupt and DMA at any configuration change, whether to new or old.
;This doesn't work with all chip sets, so gpivec is initially set for empty ISR.
    cli
    in al,picimr
    or al, engpint          ;Disable our IRQ
    out picimr, al          
    jmp $+2
    sti
    mov al,4 + DMACH
    out dmamsk,al           ;Mask out GPIB DMA channel
    ds_parm
    cmp WORD PTR [bp + ARG1 ], 0
    jne newsys
;...... Restore system ..............................................
if  MODESELECT eq REAL
    lds dx, [gprealvec]
    i21h 25h, gpiv
else                        ;Not MODESELECT eq REAL
    resetRealProt gpiv, gpprotvec, gprealvec
endif                       ;Not MODESELECT eq REAL
    jmp confx
newsys: ;............. Set up new ...................................
    mov ax, SEG _gpjumpvec
    mov es, ax
    mov WPTR es:[_gpjumpvec], OFFSET gpjump
    mov WPTR es:[_gpjumpvec+2], SEG gpjump
if  MODESELECT eq REAL
    setRealInterrupt gpiv, gpisr, gprealvec
;Note- setRealInterrupt requires valid ds at entry but uses es, ds, and bx.
else                        ;Not MODESELECT eq REAL
;Set up video address selector. The selector returned by DPMI is permanent and doesn't need to be freed. Duplicates are not made.
    mov bx, 0b800h
    mov ax, 2
    int 31h                 ;Returns permanent selector in ax. C is set if error.
    mov bx, DSEG
    mov ds, bx
    mov [vidselect], ax
;Install gpisr as reflected to real interrupt.
    push WPTR gpiv
;Convert the real isr's selector to segment address.
    push SEG gpisr          ;Selector. 
    push OFFSET gpisr
    call DOSPROTTOREAL      ;Pascal function clears stack.
;Now we can pass the real mode address of the real isr to the vector installer.
    push dx                 ;Segment address of realser
    push ax                 ;Its offset address.
    push DSEG   
    push OFFSET REALDSEG:gpprotvec  ;Point to storage for old protected mode vector.
    push DSEG   
    push OFFSET REALDSEG:gprealvec  ;Point to storage for old real mode vector.
    call DOSSETPASSTOREALVEC
endif                       ;Not MODESELECT eq REAL 
confx:  
    endproc
_configgp   ENDP

COMMENT $--------------- INITGP ------------------------------------
        Initialize GPIB board (ZT1444) and try to talk to the AU's GPIB controller. Success here, only indicates that the AU
is powered up, because the AU's CPU doesn't need to be involved in this process.
int initgp( US timeout ) ;
$
_initgp     PROC FAR
        push bp
        mov bp,sp
        push ds
        call gpsetj             ;Set long jump leaves ds_parm'd
        push si
        push di
        mov al,4 + DMACH
        out dmamsk,al           ;Mask out GPIB DMA channel
gpbeg:  outfd ziacr, 1          ;Simulate hardware reset.
        ds_parm
        outfd auxcmd, RESET
        mov cx,100
        loop $
        inc dx                  ;Point to gpaddr
        mov al,1                ;CU GPIB address 1 
        out dx,al               ;My address and disable dual addressing and don't disable talker or listener
        outfd int1, 60h         ;Enable ERR and UNC functions.
        outfd int0, NORMI0
        outfd auxcmd,RSTCLR     ;Unreset 9914
        outfd ziacr, DMARES     ;Enable GPIB interrupts but not DMA request.
        outfd auxcmd, SIC       ;Send IFC
        mov cx, 10000
        loop $
        mov al,SICLR
        out dx,al               ;Disassert IFC
        mov cx,10000
        loop $
        outfd auxcmd, LON       ;Make sure that someone is listener for handshakes.
        mov cx,0ff22h           ;Command to make device 2 (AU) a listener
        call gpcom              ;Note that we are neither listener nor talker upon return.
        jnz gpbeg               ;Keep trying til time out.
        outfd auxcmd, GTS
        xor ax, ax              ;Return 0 = no error.
        pop di
        pop si
        pop ds
        pop bp
        ret
_initgp     ENDP

COMMENT $-------------------- _GPDCLR -----------------------------------
        Transmit Device Clear to AU.
gpdclr( US timeout ) ;
$
_gpdclr     PROC FAR
        push bp                 ;Protect bp from DOS time function.
        mov bp,sp
        push ds
        call gpsetj             ;Leaves ds_parm'd
        push si
        push di
        mov cx,0ffh * 256 + DCL
        call gpcom
        outfd auxcmd, GTS
        jmp $+2
        pop di
        pop si
        pop ds
        pop bp
        xor ax,ax               ;Return no error
        ret
_gpdclr     ENDP

;Pass CTL is divided into 2 parts to allow the startup program to be split off from normal operation. Startup would
;end after passctla and normal would begin with passctlb.

COMMENT $---------------------- PASSCTLA ------------------------------------
        Pass GPIB control to the AU: part A. Make AU talker.
int passctl( US timeout ) ; 
$
_passctla   PROC FAR
        push bp
        mov bp,sp
        push ds
        call gpsetj             ;Leaves ds_parm'd
        push si
        push di
        mov cx,TCT * 256 + 42h  ;First make the AU a talker and then send TCT to it.
        call gpcom
        jnz pctlerr
        xor ax,ax               ;Return no error.
psctx:  pop di
        pop si
        pop ds
        pop bp
        ret
pctlerr: mov ax, NOLSTNR
        jmp psctx
_passctla   ENDP

COMMENT $------------------- PASSCTLB ---------------------------------------
        Pass GPIB control part B. Release ATN and pass control.
void passctlb() ;
$
_passctlb   PROC FAR
        outfd auxcmd, GTS
        outfd auxcmd, TONCLR
        outfd auxcmd, RLC
        ret
_passctlb   ENDP

COMMENT $------------------ _FARCHK -----------------------------------------
        This calculates the US checksum of a block of UC data in a far buffer. I have done this in asm because the stupid
compiler generates flakey code. Moved from gputil.asm as part of DOSX modifications.
US farchk( UC far *buf, US len, int mode ) ; /* Assume that this doesn't cross segment boundary. */
mode 0 = byte, 1 = word. Len is byte count in either case.
$
_farchk PROC FAR
        push bp
        mov bp,sp
        push si
        xor bx, bx              ;Initialize chksm accumulator.
        les si, [bp + ARG1]     ;Get pointer to source.
        mov cx, [bp + ARG1 + 4] ;Get length.
        cmp WORD PTR [ bp + ARG1 + 6 ], 0 ;Mode: 0 = byte, 1 = word.
        jnz wchk
        xor ah,ah               ;Permanent upper byte of input bytes.
chkl:   lods BYTE PTR es:0
        add bx,ax
        loop chkl
fchkx:  mov ax,bx
        pop si
        pop bp
        ret
wchk:   shr cx,1                ;Divide count by 2 for words.
wchkl:  lods WORD PTR es:0
        xchg ah,al
        add bx, ax
        loop wchkl
        jmp fchkx
_farchk ENDP

else                        ;not MODESELECT ne XREAL.
COMMENT $ ----------------- GATEGP -------------------------------------
    This provides an entry point for getting to CVr from CVP. At the CVP prompt, enter "?cvreal(gategp)". The 
cvreal function replaces the retf instruction at gategp with int3, the debug break, and then calls gategp 
through the DOS extender prot-real translator. CVr pops up when int3 is executed, because the CPU is in real mode at
this time.
    Restore the retf by either "a ENTER retf ENTER " or "eb . cb ENTER ". This should be habitually done immediately at the break.
Without this, we can't return to CVP. 
    The gategp function is followed by a table of local addresses chosen by the programmer to assist real mode
debugging without source. CVr can't do source level debugging of the application. At the break, the easiest means to
show the table is by entering "dw.". Ignore the first word, which is the retf and nop filler. To recap:
1. Restore retf by "a ENTER retf ENTER " or "eb . cb ENTER "
2. Display address table by "dw."
$
_gategp PROC FAR        ;To get from CVP to CVR enter "?cvreal(gategp)"
    ret                     ;which returns CBh = far ret. Restore this op code at break in CVr.
    nop
_gategp ENDP
;.....................Debug address table..................................
    dw OFFSET inqstatref
    dw OFFSET gptimeref
    dw offset gpisr
    dw offset gps0          ;BO after last byte tx. Use to verify that gpcycle (prot) loads same gps0 offset as real isr.
    dw offset gps1          ;DMA RX TC at end of header.
    dw offset gps2          ;DMA RX TC at end of input.     
    dw offset gps2a         ;Waiting for AU to make us talker.
    dw offset gps3          ;DMA TX TC at penultimate byte. 
    dw offset gps3a         ;BO at penultimate byte.
    dw offset gps4          ;BO at end of last byte. Second instruction at this label also loads gpivec with real mode gps0.
    dw offset dma2          ;Fixes dma in case of physical address boundary split.
    dd _cmd_x               ;For testing ldb:auman.c:auman().
    dd _sbyprm              ;For testing ldb:auman.c:key_start().
endif                       ;not MODESELECT ne XREAL.
_CSNAME     ENDS
                            END 


ADSP2105 DSP MOTOR CONTROLLER

/* servo.dsp
ADSP2105 Assembler. Use asm21 to assemble, because it has C preprocessor for comments, etc.
    This program depends on unused port bits all reading as 0. This can be done by pulldown resistors or an active
bit driver. I have added a (un)defineable value DATA_FLOATS to add masking if unconnected data isn't pulled low, but 
I can't be sure that I can easily do this for all cases.
******************* DEFINITIONS ********************************************/
.module/boot=0/abs=0          servo_main;
/*----------------- MACROS ------------------------------------------------*/
#define TRUE 1
#define FALSE 0
/*................. Three-part DAG setup macro ............................*/
#define DAG(D,IV,MV,LV) i##D = IV; m##D = MV; l##D = LV;
/*----------------- CIRCUIT CONDITIONALS ----------------------------------*/
#define DATA_FLOATS 0 /* True if the circuit doesn't have passive pulldowns on data lines. */
/*----------------- MEMORY MAP --------------------------------------------*/
/*................. Ports .................................................*/
.port                   master;   /* Parallel master CPU-- probably Loader/Slave */
.var/pm/circ/abs=0x800/seg=xpm   encods[4]; /* Motor position encoder signals converted from gray to binary on D0, D1. */
.var/dm/circ/abs=0x10/seg=xdm0   pwms[4];   /* Motor drive PWM devices (I8254) */
.var/dm/circ/abs=0x20/seg=xdm0   currents[4]; /* Over-current detectors. */
/*................. Internal Memory .......................................*/
.var/dm/circ/seg=indm   motpos[8]; /* m0, m1, m2, m3 of { previous encoder value; previous motor position; } */
/*................. Port simulation source arrays .........................*/
/* We have to use programmed simulation in cases where the ports have been mapped into arrays, because the language doesn't
support arrayed ports. Physically, there is no difference between contiguous ports and data array. 
    All ports, whether mapped to pm or dm, are physically attached to external data lines D8-D23. Thus, they all read
similarly into registers. To simulate this, we need to shift simulation values by one byte. */
#define TESTPOSITION TRUE
#if TESTPOSITION
.var/pm/circ/seg=inpm   enctest[16]; /* Motor encoder simulation source. */
.init enctest:  0x100, 0x300, 0x100, 0x100, 
                0x200, 0x200, 0x000, 0x100, 
                0x300, 0x100, 0x300, 0x200, 
                0x000, 0x000, 0x000, 0x300;
/* Motor 0 counts monotonically up. Motor 1 counts monotonically down. 
Motor 2 counts from 0, 1,0,-1,0. Motor 3 counts from 0, 1,1,2,3, error (3 to 1 = 2 counts). */
#endif
/****************** FUNCTIONS **********************************************/
    jump start; rti; rti; rti;      {Reset Vector}    
    rti; rti; rti; rti;             {irq2}
    rti; rti; rti; rti;             {sport0 TX}
    jump start; rti; rti; rti;      {sport0 RX} 
    rti; rti; rti; rti;             {irq0}
    rti; rti; rti; rti;             {irq1}
    call start; rti; rti; rti;      {timer}

start:  
/*................. Set up control registers...............................*/
/*................. Initialize buffers ....................................*/
/*  0 all 4 motor position structs { encoder value; previous position; } */
    DAG(0, ^motpos, 1, %motpos)
    cntr = %motpos;
    do ib1 until ce;
ib1: dm(i0, m0) = 0;
/*................. test programs .........................................*/
#if TESTPOSITION 
/* Transfer one block of 4 values from test source to encoder ports and call position computer. Repeat with the 
next block of source and continue until end of source. Then repeat. Inner loop of 4 word transfers is automatically
constrained and controlled by DAG6's initial values. Similarly, the outer loop which repeats the test pattern is 
controlled by DAG7. Neither DAG needs adjusting after initialization. */
    DAG(7, ^enctest, 1, %enctest)
    DAG(6, ^encods, 1, %encods)
nextpos:
    cntr = 4;
    do encblock until ce;
    ax0 = pm(i7, m7);
encblock: pm(i6, m6) = ax0;
    call calcpos;
    jump nextpos;
#endif    
/*----------------- Update motor position counters ------------------------*/
/* Local Register definitions */
#define C4  mr0         /* Constant 4 */
#define CM2 mr1         /* Constant -2 */
#define C3  mr2         /* Constant 3 for use as floating data mask if needed. */
/* DAG4 -> motor encoder pointer.
   DAG0 -> previous encoder value and motor position.
    Only the two LSBits of encoder actually change. The rest are hard-wired 0 unless inactive data lines float. 
*/
calcpos:
    DAG(4, ^encods, 1, %encods) /* Point to encoders. */
    DAG(0, ^motpos, 0, %motpos) /* Point to previous encoder readings and motor positions. Use m0 for non-advancing access. */
    m1 = 1;                     /* Use m1 with i0 for advancing access via DAG0. */
    C4 = 4; CM2 = -2;            /* Set up constants. */
#if DATA_FLOATS
    C3 = 3;                     /* Mask to convert unknown data bits to 0. */
#endif
    cntr = 4;                   /* We will check all 4 motors. */
/* Loop: compute motor position. */            
    do encend until ce;
    ax0 = dm(i0, m0), ay0 = pm(i4, m4); /* Previous encoder -> ax0, current (from port) -> ay0. Advance only port pointer. */
#if DATA_FLOATS
    ar = C3 and ay0, ay0 = ax0; /* Convert unknown port bits to 0. Copy previous encoder value to ay0 for next instruction. */
    af = ar-ay0, dm(i0, m1) = ar;   /* Current encoder - previous encoder. Overwrite stored encoder value with current and advance pointer. */
#else
    af = ay0-ax0, dm(i0, m1) = ay0; /* Current encoder - previous encoder. Overwrite stored encoder value with current and advance pointer. */
#endif
    if eq jump samenc;          /* If current encoder value = previous then no position change. */    
    if lt af = C4 + af;         /* Convert result range of -3 to -1 to 1 to 3 by adding constant 4. */
    af = CM2 + af, ax0 = dm(i0, m0); /* Subtract 2 for transform to -1, 0, +1. Read previous position-- retain pointer.*/
    if eq jump encerr;          /* Encoder over-run if old and new values are separated by 2, i.e. odd count error detected. */
    ar = ax0 - af;              /* New position is old -(-1) or -(+1). */
encend: dm(i0, m1) = ar;        /* Store new position and advance pointer to read next motor data. */    
    rts;
encerr: ar = 32767;             /* Flag position error by writing maximum positive value. */
    jump encend;
samenc: ar = dm(i0, m0);        /* Read previous position to have valid value to store. Don't advance pointer. */
    jump encend;
here:   
    jump here;                  
.endmod;


I80186

    NAME NIWBOOT
    PAGE , 132
COMMENT | 
Purpose: Demo board master V40 EPROM program. The EPROM is only 32K*8 and is mapped by hardware into the V40's 
address range F8000-FFFFF. e.g. EPROM 0 = system F8000, EPROM 7FF0 = system FFFF0 (program address after reset).
This program assumes that RAM is located 00000-07fff (32K*8) or 00000-1ffff (128K*8).
Build with m.bat = 
    tasm niwboot,,niwboot.lst
    tlink niwboot
    exe2bin niwboot
    debug niwboot.bin (not needed for actual build)
|
;****************** DECLARATIONS ********************************************
;++++++++++++++++++ Pragmas +++++++++++++++++++++++++++++++++++++++++++++++++
    .186
;   .SALL           ;Don't expand macro source at instantiation.
    .MODEL TINY
;------------------ macros --------------------------------------------------
iout    MACRO addr, val
    mov dx, addr
    mov al, val
    out dx, al
;   jmp $+2
    ENDM

iin MACRO addr
    mov dx, addr
    in al, dx
;   jmp $+2
    ENDM

setivec MACRO inum, proc
    xor ax, ax
    mov es, ax
    mov WORD PTR es:[(inum+VECBASE)*4], OFFSET proc
    mov WORD PTR es:[(inum+VECBASE)*4+2], PROGSEG
    ENDM

;****************** DEFINITIONS *********************************************
;------ Internal (V40) I/O addresses ---------------------------------------
OPCN    equ 0FFFEh  ;On-chip Peripheral Connection Register.
OPSEL   equ 0FFFDh  ;On-chip Peripheral Selection Register.
;...... On-chip Peripheral Relocation Registers................
OPHA    equ 0FFFCh  ;High order address (for all of the other registers). All bits significant.
DULA    equ 0FFFBh  ;DMAU (DMA Control Unit) base address selector. Bits 7-4 only, 3-0 = don't care.
IULA    equ 0FFFAh  ;ICU (Interrupt Control Unit) base address selector. Bits 7-2 only.
TULA    equ 0FFF9h  ;TCU (Timer Control Unit) base address selector. Bits 7-2 only. 
SULA    equ 0FFF8h  ;SCU (Serial Control Unit)base address selector. Bits 7-2 only.
;...... Wait Control Registers ..............................................
WCY2    equ 0FFF6h  ;Wait Cycle 1 Control Register.
WCY1    equ 0FFF5h  ;Wait Cycle 2 Control Register.
WMB equ 0FFF4h  ;Wait Memory Boundary Control Register.
RFC     equ 0FFF2h  ;Refresh Control Register.
TCKS    equ 0FFF0h  ;Timer clock selection control register.
;------ Addressing Instances ----------------------------------------
; Application-defined base addresses.
OPBASE  equ 0   
DUBASE  equ 0       
IUBASE  equ 10h
TUBASE  equ 20h
SUBASE  equ 30h
;Each address = OPBASE*256+base, e.g. DMAU @100-10F, ICU @110-113, TCU @120-123, SCU @130-133.
;...... ICU ....................................................
; Addresses
IMDW    equ IUBASE  ;Interrupt Mode Word.
IMKW    equ IUBASE+1    ;Interrupt Mask Word.
; Control Words
IIW1    equ 10h     ; IIW1 selector 
IIW4NR  equ 0       ; IIW4 NOT required 
IIW4R   equ 1       ; IIW4 required 
IEM equ 0       ; Extended mode - slave controllers 
ISM equ 2       ; Single mode - no slave controllers. 
IET equ 0       ; Edge triggered. 
ILT equ 8       ;Level triggered.
IIW4    equ 0       ;IIW4 select.
IFI equ 0       ;FI Command mode.
ISFI    equ 2       ;Self finish mode.
INN equ 0       ;Normal nesting.
IEN equ 10h     ;Extended nesting.
SI1 equ 2       ;IRQ1 is slave.
SI2 equ 4       ;IRQ2 is slave.
SI3 equ 8       ;IRQ3 is slave.
SI4 equ 10h     ;IRQ4 is slave.
SI5 equ 20h     ;IRQ5 is slave.
SI6 equ 40h     ;IRQ6 is slave.
SI7 equ 80h     ;IRQ7 is slave.
ISEOI   equ 20h     ;End of Interrupt.
; Application-defined values
VECBASE equ 8       ;Base of interrupt vectors, i.e. int0-7 use vectors 8-f.
;...... TCU ....................................................
; Addresses
TCT0    equ OPBASE*256+TUBASE   ;Multiple use dictated by selection commands to TMD. see NEC p. 41.
TCT1    equ OPBASE*256+TUBASE+1
TCT2    equ OPBASE*256+TUBASE+2
TMD equ OPBASE*256+TUBASE+3 ;Timer Mode Register controls the others.
;...... SCU ..............................................................
; Addresses
SRB equ OPBASE*256+SUBASE   ;Serial Read Buffer used for both read RX and write TX.
SST equ OPBASE*256+SUBASE+1 ;Serial Status register.
SCM equ SST         ;Serial Command register.
SMD equ OPBASE*256+SUBASE+2 ;Serial Mode register.
SIMK    equ OPBASE*256+SUBASE+3 ;Serial Interrupt register.
; Control Words
;At reset, SMD =   01001011
;At reset, SCM =   --0000-0
;At reset, SIMK =  ------11
;At reset, SST =   10000100
;At reset, DCH =   ---00001 
;At reset, DMD =   000000-0
SCMODE  equ 00110101B   ;XX+1(MRDY=SRDY=1mask)+1(clear err flag)+0(send break normal)+1(enable RX)+X+1(enable TX).
MSKSCRX equ 1       ;Set bit to mask (disable) receive interrupt.
MSKSCTX equ 2       ;Set bit to mask transmit interrupt.
BAUD115 equ 4       ;7372800/(16*115200)    t1out = 1.84MHz.
BAUD38  equ BAUD115*3   ;38.4KBAUD      t1out = 614Khz.
BAUD19  equ BAUD115*6   ;19.2KBAUD      t1out = 307Khz.
BAUD9   equ BAUD115*12  ;9.6KBAUD       t1out = 154Khz. Minimum rate supported by Turbo Debugger.
BAUD4   equ BAUD115*24  ;4.8KBAUD       t1out = 76.8Khz. Maximum rate supported by Windows w/o UART with FIFO.
BAUDIV  = BAUD4     ;Initial rate.

;------ Internal I/O Control Words ---------------------------------------
;...... System I/O Area .........................................
;At reset, OPCN =  ----0000
;At reset, OPSEL = ----0000
;At reset, WCY1 =  11111111
;At reset, WCY2 =  ----1111
;At reset, WMB =   -111-111
;At reset, TCKS =  ---00000
;At reset, RFC =   X--01000
;...... SCU (Serial Control Unit)...................................
;....... DMAU (DMA Control Unit).................................
;At reset, DDCl =  --00-0-- 
;At reset, DDCh =  ------00
;At reset, DST =   XXXX0000
;At reset, DMK =   ----1111
;------------------ MEMORY ADDRESSING ------------------------------------
PROGSEG equ 0f800h
STACKSEG32 equ 0        ;Last RAM address if 32K*8.
STACKOFF32 equ 7fffh
STACKSEG128 equ 1000h   ;Last RAM address if 128K*8.
STACKOFF128 equ 0ffffh
STACKSIZE equ 1024
HEAPSIZE equ 1024-1 ;Use 4n-1 to produce quad word alignment.
IF 1
STACKSEG equ STACKSEG32
STACKOFF equ STACKOFF32
ELSE
STACKSEG equ STACKSEG128
STACKOFF equ STACKOFF128
ENDIF
;++++++++++++++++++ DATA ++++++++++++++++++++++++++++++++++++++++++++++++++++
MYID = 1
HEAPOFF equ STACKOFF-STACKSIZE-HEAPSIZE
IDATLEN = 17            ;Byte count of initialized data.
heapdat STRUC
;...... Initialized data (be sure IDATLEN and idatsrc track changes)...
    h_ack   db 6 DUP(?)
    h_nak   db 6 DUP(?)
    h_dp    dw ?        ;Pointer to packet destination/source.
    h_cnt   db ?        ;Packet byte count.
    h_chk   db ?        ;Checksum.
    h_dly   db ?        ;Delayed process flag. see writeregs.
;.........................................................
    h_buf   db 256 DUP(?)
    ENDS
hpdp    EQU ds:[HEAPOFF+h_dp]
hpdpseg EQU ds:[HEAPOFF+h_dp+2]
hpcnt   EQU ds:[HEAPOFF+h_cnt]
hpchk   EQU ds:[HEAPOFF+h_chk]
hpdly   EQU ds:[HEAPOFF+h_dly]
hpbuf   EQU ds:[HEAPOFF+h_buf]
;...... positions within the packet in buffer ..........
func    EQU ds:[HEAPOFF+h_buf+4]
firstdat EQU HEAPOFF+h_buf+5
addrl   EQU ds:[HEAPOFF+h_buf+5]
addrh   EQU ds:[HEAPOFF+h_buf+7]
readmcnt EQU ds:[HEAPOFF+h_buf+9]
;....................... output packet locations..................
SEQOFF = 1
SIDOFF = 2
RIDOFF = 3
FUNCOFF = 4
MEMDATOFF = HEAPOFF+h_buf+5 ;Output memory response first data position.
MWROFF = HEAPOFF+h_buf+9 ;Input data (wrmem) first data position.
REGSFUNCOFF = 30
COMMENT |---------------------packet types---------------------------
Basic packet is len, seq, sid, rid, func, chksm = min 6 byte. Packets which ask to read or write data require 2 byte address.
So the minimum for read request would be len, seq, sid, rid, func, ah, al, cnt, chksm = 9 bytes. Minimum size packet to write
would be len, seq, sid, rid, func, ah, al, 1 byte data, chksm = 9 bytes. Thus, we should read at least 6 bytes as header and
up to 9 if the packet size <= 9. Then analyze to determine whether more should be read into remote locations. |
;.....................................................................
;++++++++++++++++++ FUNCTIONS +++++++++++++++++++++++++++++++++++++++++++++++
.CODE
    ORG 0
init    PROC
;...... Initialize segments and memory....................................
    mov ax, STACKOFF
    mov sp, ax
    mov ax, PROGSEG     
    mov ds, ax      ;Use data seg in EPROM only for initializing RAM data.
    mov di, HEAPOFF
    mov si, OFFSET idatsrc
    mov ax, STACKSEG
    mov ss, ax
    mov es, ax      ;Destination for initializing communication sources.
    cld
    mov cx, IDATLEN
    rep movsb
    mov ds, ax      ;Heap and stack share one segment.
; Set up internal I/O addressing.
    iout OPHA OPBASE
    iout DULA DUBASE
    iout IULA IUBASE
    iout TULA TUBASE
    iout SULA SUBASE
    iout OPSEL 0fh      ;Enable all on-chip peripherals.
;...... Initialize Interrupt Control Unit .............................
    iout IMDW (IIW1+IIW4NR+ISM+IET) ;Initialization Word 1 + no IIW4 + no slaves + edge trigger.
    iout IMKW VECBASE   ;Set base of vector table, i.e. ints 0-7 use vectors VECBASE to VECBASE+7. Note IIW2 address.
    iout IMKW 11111101B ;Mask (disable) all but int1 (serial).
    setivec 1 serialisr
; Initialize Timer Control Unit ........................................
;   iout OPCN 00001101B ;Use xxxx1101 to output Tout1 on pin 36 for testing baud clock or..
    iout OPCN 00001111B ;xxxx1111 for serial comm with SRDY output on pin 36.
    iout TCKS 00001000B ;TCT2 use internal, TCT1 use TCLK pin 44, TCT0 use internal. Internal clock prescale / 2.   
; Set up TCT1 to be used for BAUD rate generator. This process is duplicated at baud change. Be sure to duplicate any changes.
    iout TMD 01110110B  ;Select TCT1, lower byte followed by upper, mode 3, binary.
    iout TCT1 BAUDIV    
    iout TCT1 0     ;High byte of divisor.
;...... Initialize Serial Control Unit ................................
    iout SMD 01001110B  ;1 stop, no parity, 8 bit, clock/16.
    iout SCM SCMODE     
    iout SIMK MSKSCTX   ;Disable TX but enable RX interrupt.
    STI         ;Enable interrupts.
    mov dx, SRB
    mov ah, 'M'
    call txbyte
    mov ax, 1
endless1: 
    cmp ax, 1
    je endless1
endless2:
    jmp $
endless3:
    jmp $
init    ENDP
;############################################################################
;.......................... control words in packets ..............
REGSACKNOW = 1  ;Tells register change to occur immediately and to send ACK even if this means operating isr in a new thread.
REGSNOACK = 2   ;Make register changes immediately and don't send ACK.
;Any other value in last byte of register change packet is interpreted as send ACK and change registers at end of transmission.
REGSAFTERACK = 1 ;For delayed register change, this value is put into hpdly. At end of tx check this to see if any job to do.
DOINGPARK = 2   ;Put this into hpdly if using readregs as part of park. Really could use any non-0.
CHANGEBAUD = 3  ;Put this into hpdly to trigger baud change only after sending ACK to the packet which contained baud change req.
;.......................... function values.......................
F_ACK =     0
F_NAK =     1
F_SYNC =    2
F_READMEM =     3
F_WRITEMEM =    4
F_READIO =  5
F_WRITEIO =     6
F_READREGS =    7
F_WRITEREGS =   8   ;This includes execute by changing cs and/or ip.
F_PARK =    9   ;Park the CPU in a loop in ROM, e.g. while downloading files.
F_NEWBAUD =     10
F_TDREMOTE =    11
;----------------------- Function Vector Table --------------------------
funcvec dw OFFSET ackfunc, OFFSET nakfunc, OFFSET syncfunc, OFFSET readmem
    dw OFFSET writemem, OFFSET readio, OFFSET writeio, OFFSET readregs
    dw OFFSET writeregs, OFFSET parkcpu, OFFSET newbaud, OFFSET tdremote
MAXFUNC = 11
;----------------------- Initialized RAM data source ---------------
idatsrc db 6, 0, MYID, 0, F_ACK, 0
    db 6, 0, MYID, 0, F_NAK, 0
    dw HEAPOFF+h_buf        ;Initial rx destination pointer.
    db 0ffh             ;Initial data count to indicate first byte.
    db 0                ;Initial checksum.
    db 0                ;Delayed process flag.

;+++++++++++++++++++++++++++ ISR FUNCTIONS +++++++++++++++++++++++++++++++
COMMENT | ---------------------- CALCCHK ---------------------------------
    Assume ds = STACKSEG, si points to output source.
|
calcchk PROC
    mov dx, si
    xor al, al
    mov ah, ds:[si]     ;Get packet length.
    dec ah          ;We don't want to count the checksum position.
chkloop:
    add al, ds:[si]
    inc si
    dec ah
    jnz chkloop
    neg al
    mov ds:[si], al
    mov si, dx      ;Restore pointer.
    ret
calcchk ENDP
;............................ function processors ..........................
ackfunc PROC
    jmp sendack
ackfunc ENDP
nakfunc PROC
    jmp sendack
nakfunc ENDP
syncfunc PROC
    jmp sendack
syncfunc ENDP
readmem PROC
    push cx
    push di
    mov cl, readmcnt    ;Get count of data to read.
    xor ch, ch
    mov al, cl
    add al, 6       
    mov hpbuf, al       ;Response will have count bytes + 6 bytes overhead.
    inc al          ;Need one more for tx function to discard final interrupt.
    mov hpcnt, al       
    mov si, WORD PTR addrl
    mov di, WORD PTR addrh
    mov ds, di      ;Set up source pointer for block move to buffer.
    mov di, STACKSEG
    mov es, di
    mov di, MEMDATOFF   ;Destination pointer.
    rep movsb
    pop di
    pop cx
preprep:            ;Prepare reply by pointing to buffer, adding our own and destination IDs.
    mov si, HEAPOFF+h_buf
    mov ax, STACKSEG
    mov ds, ax
    mov ds:[si+SIDOFF], BYTE PTR MYID
    mov ds:[si+RIDOFF], BYTE PTR 0
    jmp totxisr
readmem ENDP
writemem PROC
    push cx
    push di
    mov cl, hpbuf       ;Read total packet length.
    sub cl, 10      ;Don't count overhead = len+seq+sid+rid+f+4byte_addr+chk
    xor ch, ch
    mov di, WORD PTR addrh
    mov es, di
    mov di, WORD PTR addrl
    mov si, MWROFF
    rep movsb
    pop di
    pop cx
    jmp sendack
writemem ENDP
readio  PROC
    jmp sendack
readio  ENDP
writeio PROC
    jmp sendack
writeio ENDP
tdremote PROC
    jmp sendack
tdremote ENDP
newbaud PROC
    mov [hpdly], CHANGEBAUD
    jmp sendack
newbaud ENDP
COMMENT | Read and write registers functions assume that the stack has been set up a certain way to due automatic
interrupt operation and the pushes that the isr does. The stack, beginning at SP and incrementing by words is assumed
SP-> ax, dx, si, ds, es, ip, cs, flags.
Register values are sent to and received from host in the order expected by Borland's Turbo Debugger:
ax, bx, cx, dx, sp, bp, si, di, flags, ip, cs, ds, ss, es.
On F_WRITEREGS there is one final word which is a bit flag mask, where each 1 indicates that the corresponding 
register should be written. e.g. if LSBit = 1 then write es. The two MSBits are not used (b13 = ax flag).
|
parkcpu PROC        ;Jump CPU to a safe loop in ROM and send current registers.
    mov hpdly, DOINGPARK
    ; Fall through to readregs
parkcpu ENDP
readregs PROC
    mov hpbuf, 34       ;Packet length is 14 words (28 bytes) plus 6 bytes overhead.
    mov hpcnt, 35       ;One extra for the last tx interrupt.       
    mov si, sp
    mov ax, ss:[si]     ;Read saved ax          
    mov WORD PTR ds:[firstdat], ax          ;ax
    mov WORD PTR ds:[firstdat+2], bx            ;bx
    mov WORD PTR ds:[firstdat+4], cx        ;cx
    mov ax, ss:[si+2]   ;Read saved dx
    mov WORD PTR ds:[firstdat+6], ax        ;dx
    mov ax, 16      ;Difference between current SP and its value before interrupt.
    add ax, si      ;Plus current sp copied to si.
    mov WORD PTR ds:[firstdat+8], ax        ;sp
    mov WORD PTR ds:[firstdat+10], bp       ;bp
    mov ax, ss:[si+4]   ;Read saved si
    mov WORD PTR ds:[firstdat+12], ax       ;si
    mov WORD PTR ds:[firstdat+14], di       ;di
    mov ax, ss:[si+14]  ;Read flags saved by interrupt.
    mov WORD PTR ds:[firstdat+16], ax       ;flags
    mov ax, ss:[si+10]  ;Read ip saved by interrupt.
    mov WORD PTR ds:[firstdat+18], ax       ;ip
    mov ax, ss:[si+12]  ;Read cs saved by interrupt.
    mov WORD PTR ds:[firstdat+20], ax       ;cs
    mov ax, ss:[si+6]   ;Read saved ds
    mov WORD PTR ds:[firstdat+22], ax       ;ds
    mov ax, ss      ;We are working off of the application ss currently.
    mov WORD PTR ds:[firstdat+24], ax       ;ss
    mov ax, ss:[si+8]   ;Read saved es
    mov WORD PTR ds:[firstdat+26], ax       ;es
    cmp hpdly, DOINGPARK
    jne jprep
; After setting up registers to send, move the CPU to safe loop if F_PARK command.
; This is not entire satisfactory, because we would probably want to change the stack as well.
    mov hpdly, 0
    mov ss:[si+10], 0fffah  ;Offset of safe park loop.
    mov ss:[si+12], 0f000h  ;Segment of safe park loop.
jprep:  jmp preprep
readregs ENDP
;
writeregs PROC
    mov al, ds:[firstdat+REGSFUNCOFF]
    cmp al, REGSACKNOW
    je regsnow          ;Change regs immediately and send ACK.
    cmp al, REGSNOACK
    je regsnow          ;Change regs immediately but send no ACK.
; Else delay changing the registers until end of ACK transmission.
    mov hpdly, REGSAFTERACK
    jmp sendack 
regsnow:
    mov si, sp
    mov ax, WORD PTR ds:[firstdat+28]   ;Read write bit flags.
    test al, 1
    jz wrss             ;Skip over es if not requested.
    mov dx, WORD PTR ds:[firstdat+26]   ;Get new es.
    mov ss:[si+8], dx                   ;es
wrss:   ;Skip new stack segment for now-- this would create big problems for my heap.
;   test al, 2
;   jz wrds             ;Skip write ss.
;   mov dx, WORD PTR ds:[firstdat+24]   ;Get new ss.
;   mov ss, dx                      ;ss
wrds:   test al, 4
    jz wrcs             ;Skip write ds.
    mov dx, WORD PTR ds:[firstdat+22]   ;Get new ds.
    mov ss:[si+6], dx                   ;ds
wrcs:   test al, 8
    jz wrip                 ;Skip write cs.
    mov dx, WORD PTR ds:[firstdat+20]   ;Get new cs.
    mov ss:[si+12], dx                  ;cs
wrip:   test al, 16
    jz wrfl             ;Skip write ip.
    mov dx, WORD PTR ds:[firstdat+18]   ;Get new ip.
    mov ss:[si+10], dx                  ;ip
wrfl:   test al, 32
    jz wrdi             ;Skip write flags.
    mov dx, WORD PTR ds:[firstdat+16]   ;Get new flags.
    mov ss:[si+14], dx                  ;flags
wrdi:   test al, 64
    jz wrsi             ;Skip write di.
    mov di, WORD PTR ds:[firstdat+14]           ;di
wrsi:   test al, 128
    jz wrbp                 ;Skip write si.
    mov dx, WORD PTR ds:[firstdat+12]   ;Get new si.
    mov ss:[si+4], dx                   ;si
wrbp:   test ah, 1
    jz wrsp             ;Skip write bp.
    mov bp, WORD PTR ds:[firstdat+10]           ;bp
wrsp:   test ah, 2
    jz wrdx             ;Skip write sp.
; To write a new sp, copy the old activation frame to a scratch area in heap and from there to the new location.
; This allows us to overlap new and old stack without having to either copy upwards or downwards.
    pop WORD PTR ds:[firstdat+10]   ;Read saved ax.
    pop WORD PTR ds:[firstdat+12]   ;Read saved dx
    pop WORD PTR ds:[firstdat+14]   ;Read saved si.
    pop WORD PTR ds:[firstdat+16]   ;Read saved ds.
    pop WORD PTR ds:[firstdat+18]   ;Read saved es.
    pop WORD PTR ds:[firstdat+20]   ;Read ip saved by interrupt.
    pop dx              ;Read cs saved by interrupt.
    pop ax              ;Read flags saved by interrupt.
    mov si, WORD PTR ds:[firstdat+8] ;Get new sp
    mov sp, si
    push ax             ;Push flags onto new stack.
    push dx             ;Push return cs.
    push WORD PTR ds:[firstdat+20]
    push WORD PTR ds:[firstdat+18]
    push WORD PTR ds:[firstdat+16]
    push WORD PTR ds:[firstdat+14]
    push WORD PTR ds:[firstdat+12]
    push WORD PTR ds:[firstdat+10]
    mov si, sp
wrdx:   test ah, 4
    jz wrcx             ;Skip write dx.
    mov dx, WORD PTR ds:[firstdat+6]    ;Get new dx.
    mov ss:[si+2], dx                   ;dx
wrcx:   test ah, 8
    jz wrbx             ;Skip write cx.
    mov cx, WORD PTR ds:[firstdat+4]            ;cx
wrbx:   test ah, 16
    jz wrax                 ;Skip write bx.
    mov bx, WORD PTR ds:[firstdat+2]            ;bx
wrax:   test ah, 32
    jz wregx                ;Skip write ax.
    mov ax, WORD PTR ds:[firstdat]      ;Get new ax.
    mov ss:[si], ax                     ;ax
wregx:
    cmp ds:[firstdat+REGSFUNCOFF], REGSNOACK
    je endisr
    jmp sendack
writeregs ENDP
;.............................. isr shell and function selector.............
serialisr PROC FAR
    push es
    push ds
    push si
    push dx
    push ax
    mov ax, STACKSEG
    mov ds, ax      ;DS points to our data segment (the heap).
    mov si, hpdp        ;Next destination for input or source for output.
; First read the serial status to determine possible cause(s) of interrupt.
    iin SST
    test al, 2      ;If input, process it first but don't forget to check others.
    jnz rxisr;
    test al, 1
    jnz txisr
testerr:
    jmp endisr
;................... Process input.............................................
rxisr:
    mov dx, SRB
    in al, dx       ;Read input byte.
    cmp hpcnt, 0ffh
    jne inpack
;This is the first byte of packet. Calculate the amount of data that we will put into the header.
    mov hpcnt, al   
inpack:
    mov ds:[si], al     ;Save this byte at the next packet byte location (first byte -> head).
    add hpchk, al       ;Add it to checksum.
    inc si
    mov hpdp, si
    dec hpcnt
    jnz endisr
; All bytes of the input packet have been received. Now process check for checksum error and process the function.
    cmp hpchk, 0
    jne sendnak
    mov al, func        ;Read function from packet.
    cmp al, MAXFUNC
    ja sendack 
    xor ah, ah
    shl ax, 1
    mov si, OFFSET cs:funcvec
    add si, ax
    jmp cs:[si]     ;Jump to input function process through vector.
endisr:
    jmp $+2
    iout IMDW (ISEOI+1) ;EOI int1
    pop ax
    pop dx
    pop si
    pop ds
    pop es
    jmp $+2
    iret
;...................................................................
sendnak:
    mov hpcnt, 7
    mov si, HEAPOFF+h_nak
    jmp short totxisr
sendack:
    mov hpcnt, 7
    mov si, HEAPOFF+h_ack       ;Point to beginning of header buffer.
totxisr:
    iout SIMK MSKSCRX       ;Enable TX but disable RX interrupt.
    call calcchk    
    ;Fall through to txisr.
;.........................................................................
txisr:  ; Transmit next byte until last interrupt, which we respond to in order to clear it.
; This is why we need to initialize count to packet size + 1.
    dec hpcnt
    jz endtx
    mov al, ds:[si]
    mov dx, SRB
    out dx, al
    inc si
    mov hpdp, si
    jmp endisr
endtx:
    iout SIMK MSKSCTX       
    mov hpdp, HEAPOFF+h_buf     ;Point to beginning of header buffer for first byte of next packet.
    mov hpcnt, 0ffh
    mov hpchk, 0
;....... Check for delayed functions before exiting .................
    cmp hpdly, 0
    je endisr           ;No delayed function.
    xor ax, ax
dfuncl:                 ;Extra delay before doing the function.
    dec ax
    jnz dfuncl
    cmp hpdly, REGSAFTERACK
    je regsafter
    cmp hpdly, CHANGEBAUD
    je baudafter
    jmp endisr
;The following programs are functions that are executed after sending a reply to the request that invokes them.
baudafter:
    mov hpdly, 0
    iout TMD 01110110B  ;Select TCT1, lower byte followed by upper, mode 3, binary.
    mov al, ds:[firstdat]   ;Get low byte of new divisor.
    mov dx, TCT1
    out dx, al
    jmp $+2
    mov al, ds:[firstdat+1] ;Get high byte of divisor.
    out dx, al
    jmp endisr
regsafter:
    mov hpdly, 0
    mov ds:[firstdat+REGSFUNCOFF], REGSNOACK
    jmp writeregs
serialisr ENDP
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

txbyte  PROC
    mov dx, SST     ;Point to serial status register.
waittx:
    in al, dx
    and al, 1
    jz waittx       ;Wait for TX data register empty.
    mov al, ah      ;Get byte to transmit.
    mov dx, SRB     ;Serial TX register AKA RX register.
    out dx, al
    jmp $+2
    ret
txbyte  ENDP
;------------------- BEGIN AT RESET -----------------------------------
    ORG 7ff0h       ;Real address is FFFF0h but EPROM is only 32K*8 so ignore A15-19.
atreset PROC
    db 0eah, 0, 0, 0, 0f8h  ;Far jump to init (f800:0).
atreset ENDP
    ORG 7ffah       ;Any program can jump to FFFFA to park the CPU where it won't act crazy.
safepark PROC
    jmp $
safepark ENDP
    END init


Z80

;servo.a: control routines for servo with LM629. Lattice asmz80.
;********************** DATA ********************************************
;---------------------- DECLARATIONS --------------------------------------
;...................... system addresses and commands...................
DCNTL    equ 32h        ;DMA/WAIT control register
;...................... servo commands ..................................
;Initialization types
RSTI     equ 1dh        ;Reset interrupts.
;Trajectory types
LFIL    equ 1eh
LTRJ    equ 1fh         ;Write desired acceleration, velocity, position. 2-14 data bytes + 1 command byte.
STT     equ 1           ;Start motion control (needed to load parameters).
LPES    equ 1ah         ;Load position error for stopping (safety feature).
;....................... Single byte commands......................
RESET   equ 0
DEFHOME equ 2
UDF     equ 4           ;Update filter (after giving the controller new values).
;..................................................................
;Report types
RDDV    equ 7           ;Read desired velocity- 4 data bytes.
RDSIGS  equ 0ch         ;Read "signals" register-- 2 data bytes.
RDRP    equ 0ah         ;Read real position.
;...................... status bit masks ................................
BUSY     equ 1
COMMERR  equ 2          ;Command Error.
COMPLETE equ 4          ;Trajectory complete.
NOTCOMPLETE equ 11111011b
WRAP     equ 10h        ;Wraparound Occurred.
POSERR   equ 20h        ;Excessive Position Error.
BREAKPT  equ 40h        ;Breakpoint Reached.
MTROFF   equ 80h        ;Motor off.
;...................... servo addresses .................................
S2BSTAT equ 6ah         ;Servo B on board 2 status/control register.
S1ASTAT equ 60h         ;Servo A on board 1. Accessible only by Analyzer.
;.....................................................................
MAXFUNC equ 4
;********************** BEGIN CODE **************************************
        csect text
;---------------------- DECLARATIONS -------------------------------------
b       equ 0
c       equ 1
d       equ 2
e       equ 3
h       equ 4
l       equ 5
a       equ 7

        macro
        out0 port, register
        db 0edh, 1+(register << 3), port
        endm

        macro
        in0 register, port
        db 0edh, register << 3, port
        endm

        macro
        waitrdy statreg
        ld c, statreg
        call svrrdy        
        endm

        macro
        motgoto ph, pl
        ld hl, ((ph << 8) & 0ff00h) ! ((ph >> 8) & 0ffh)
        ld de, ((pl << 8) & 0ff00h) ! ((pl >> 8) & 0ffh)
        call gotopos
        endm

        macro
        startmot
        ld d, STT
        call wrcommand
        endm

        macro
        retack
        ld bc, 1
        ret
        endm

        macro 
        retnak
        ld bc, 2
        ret
        endm
;*************************** CODE **************************************
        jp startup      ;Jump over message buffer and function selector.
message: 
        ds 29
;------- begin function 7e (or other number) @ base address + 32d
        ld hl, 2
        add hl, sp      ;Point to passed parameter = address of message begin.
        ld e, (hl)      
        inc hl
        ld d, (hl)      ;DE now points to beginning of input message (i.e. to length byte).
        ld hl, 4        ;Offset from first byte of message to first data byte.
        add hl, de      ;Point to function byte.
        ld a, (de)      ;Read length byte.
        cp 5
        jr c, nofunc    ;At least one data byte is needed to select function (MSG is the same for all functions).
        jr z, msgrdy    ;If only 5 bytes
;Function selector is 5th. byte (msg[4]). If more than 5 bytes then 7 or more and last byte is checksum.
        sub 6           ;Remaining count, e.g. total 7 has 1 more data byte.
        ld c, a
        ld b, 0         ;BC = remaining byte count.
        add hl, bc      ;Point to last byte of input data (after function byte).
        ex de, hl       ;Save pointer to input data.
        ld hl, message-1  ;Destination with adjustment for our not needing to store function number.
        add hl, bc      ;Add data byte count. Now HL points to last data byte destination.
        ex de, hl       ;Now, source = hl, dest = de.
        lddr
msgrdy:
        ld a, (hl)      ;First data byte is function selector.
        cp MAXFUNC+1
        jr nc, nofunc
        ld b, 0
        sla a           ;Function * 2 for 2-byte vectors
        ld c, a
        ld ix, vectab   ;Point to base of servo functions vector table.
        add ix, bc      ;Point to selected function vector.
        ld l, (ix+0)    ;Get low byte of address.
        ld h, (ix+1)    ;Get high byte.
        jp (hl)
nofunc:
        retnak     
startup:
        retack
;-------------- FUNCTION TEST INTERFACE ----------------------------
;Write pattern to LED's to test and demonstrate.
func0:
        ld a, (message)
        out0 0e0h, a
        retack
;------------- FUNCTION INITIALIZE SERVO -----------------------
;Set up DMA/CONTROL register to give 2 wait states on I/O.
func1:
        in0 a, DCNTL    ;Read current to retain lower nibble.           
        and 0fh
        or 10h          ;Set 0 wait states on memory and 2 on I/O
        out0 DCNTL, a
resrvo:
        ld bc, S1ASTAT  ;Point to servo board 1, unit A.
        call resetservo
        ld hl, s_lfil
        ld d, 11
        call wrservo
        ld d, UDF       ;Update filter, i.e. start using the parameters.
        call wrcommand
        retack
;------------------- FUNCTION : MAP LIMITS -----------------------
func2:
        ld bc, S1ASTAT  ;Point to servo board 1, unit A.
;...... Set up parameters for finding limits ................
        ld hl, s_lpes
        ld d, 3         ;3 bytes total for LPES command.
        call wrservo    ;Set up position error that will be apparent error.
        ld hl, slowvert
        ld d, 11
        call wrservo
;...... Find positive limit ............................
        motgoto 3fffh, 0ffffh ;Maximum positive.
waitmax:
        in a, (c)
        and POSERR
        jr z, waitmax  ;Wait for jammed at lower end of frame.
        ld d, DEFHOME
        call wrcommand        
        call resetall        ;Reset all interrupt flags.        
;...... Find negative limit (Home).........................                
        motgoto 0c000h, 0ffffh ;Maximum negative.
waithome:
        call getmpos    ;Constantly read position because we will lose it at mechanical crash.
        in a, (c)
        and POSERR
        jr z, waithome  ;Wait for jammed at top end of frame.
        ld d, DEFHOME
        call wrcommand        
        call resetall   ;Reset all interrupt flags.        
;While going home, we counted negatively. Now we want to change to positive offset from home - margin to get max safe limit.
;Basically, subtract negative count from negative (much less though) offset to produce positive = maximum count - offset.
;Use in-line offset e.g. -256 = ffffff00, -400 = fffffe70, -512 = fffffe00.
        ld hl, d_rdrp+4 ;Point to LSB of last recorded position.
        ld a, 70h       ;Negative offset LSB
        sub (hl)
        ld (limitpos+3), a ;Store at limit LSB.
        dec hl          ;Point to next more significant byte of position.
        ld a, 0feh      ;Negative offset next MSB.
        sbc a, (hl)
        ld (limitpos+2), a ;Store at limit next MSB.
        dec hl
        ld a, 0ffh
        sbc a, (hl)
        ld (limitpos+1), a
        dec hl
        ld a, 0ffh
        sbc a, (hl)
        ld (limitpos), a ;MSB
;...... Set up normal trajectory parameters .........................        
        ld hl, s_accvel  
        ld d, 11
        call wrservo    ;Set up acceleration and velocity trajectory parameters.
        retack
;---------------------------- FUNCTION : GO TO HOME ----------------------
func3:
        ld bc, S1ASTAT  ;Point to servo board 1, unit A.
        motgoto 0, 400
        call waitmotor
        retack
;---------------------------- FUNCTION : GO TO MAXIMUM ----------------------
func4:
        ld bc, S1ASTAT  ;Point to servo board 1, unit A.
        ld hl, (limitpos)
        ld de, (limitpos+2)
        call gotopos
        call waitmotor
        retack
        nop
;************************************************************************
;-------------------------- GOTOPOS ---------------------------------------
;Send motor to new position.
;Input HL = high word, DE = low word of new position.
gotopos:       
        ld (s_goto+3), hl
        ld (s_goto+5), de
        ld hl, s_goto
        ld d, 7         ;7 bytes comprised of command byte, command word, 4-byte position.
        call wrservo
        startmot
        ret
;-------------------------- DLYSEC ----------------------------------------
;Programmed delay for approx 1/2 second.
dlysec:
        ld hl, 0
        ld d, 4
dlyseca:
        dec hl
        ld a, h
        or l
        jr nz, dlyseca
        dec d
        jr nz, dlyseca
        ret
;-------------------------- WAITMOTOR -------------------------------------
;Wait for motor to reach final point after move command.
;Input: BC = address of command/status register (b = 0)
;Uses A
waitmotor:
        call dlysec
wmb:
;First wait until controller says that it has reached the end of the trajectory.
        in a, (c)
        and COMPLETE    ;Test Trajectory complete flag.
        jr z, wmb       ;waitmotor
        in a, (c)
        and NOTCOMPLETE
        out (c), a      ;Clear the flag.
;Add loop for settling by reading current position and comparing to previous til below threshold.
        ret
;-------------------------- GETMPOS ---------------------------------------
;Get current motor position in terms of absolute encoder count (from last home)
;Input: BC = address of command/status register (b = 0)
;Place current position at d_rdrp in Motorola-type byte order.
getmpos:
        ld hl, d_rdrp   ;Command to read real position.
        ld d, 4         ;4 data bytes.
        call rdservo
        ret
;-------------------------- SVRRDY ----------------------------------------
;Wait for the servo to become ready to accept another command.
;Input: BC = address of command/status register (b = 0)
;Uses A
svrrdy:
        in a, (c)
        and BUSY
        jr nz, svrrdy
        ret
;-------------------------- WRCOMMAND -------------------------------------
;Write the command byte in D to the servo addressed by BC
wrcommand:
        call svrrdy
        ld a, d
        out (c), a
        ret
;-------------------------- RDSERVO ---------------------------------------
;Read bytes from servo to destination. The caller provides the appropriate read command in the first byte of the destination.
;The first byte read from the servo will be placed at the next location of the destination and the others will follow this.
;The command byte is written to the servo to initiate the read process. The count argument specifies the number of data bytes
;to read and the command byte is not counted. The count must be even.
;Input  BC = command/status address.
;       HL = destination address (read command byte at first address)
;       D = data byte count (command byte not counted).
;Uses A. Advances HL to point to last byte of data. Changes D to 0. 
;Calls svrrdy.
rdservo:
        call svrrdy     ;Wait for not BUSY.
        ld a, (hl)      ;Get the command byte.
        out (c), a
;Now read the data. Assume that at least 2 bytes are requested.
rddata:
        call svrrdy     ;First byte (MSB) of every word must be coordinated with BUSY.
        inc c           ;Point to data port.        
        in a, (c)       
        inc hl          ;Point to next byte destination.
        ld (hl), a      ;Transfer MSB from servo to destination.
        dec d           ;Count it.
        in a, (c)
        inc hl          
        ld (hl), a      ;Transfer LSB.
        dec c           ;Point to command/status again for reading BUSY for next word.
        dec d
        jr nz, rddata
        ret                
;-------------------------- WRSERVO ---------------------------------------
;Write bytes from source to servo. The first byte is always command. If there are any data bytes, they must be even in 
;number. Count is 1 (command w/o data), 3, 5, etc.
;Input  BC = command/status address.
;       HL = source address.
;       D = command+data byte count-- must be odd since 1 command byte + even data byte count.
;Uses A. Advances HL to last byte of source. Changes D to 0.
;Calls svrrdy.
wrservo:
        call svrrdy     ;Wait for not BUSY.
        ld a, (hl)      ;Get the command byte.
        out (c), a
;Now send any additional data bytes to the data port address (command/status + 1).
wrdata:
        dec d           
        ret z
        call svrrdy     ;First byte (MSB) of every word must be coordinated with BUSY.
        inc c           ;Point to data port.        
        inc hl          ;Point to next data byte.        
        ld a, (hl)      ;Get MSB of data word.
        out (c), a
        dec d           ;Count this byte.
        inc hl          
        ld a, (hl)
        out (c), a      ;Write data LSB.
        dec c           ;Point to command/status again for reading BUSY for next word.
        jr wrdata
;-------------------------- RESETALL -----------------------------------
resetall:
        ld hl, s_rstiall
        ld d, 3         ;Count = 1 + 2.
        call wrservo
        ret
;-------------------------- RESETSERVO ------------------------------------
;Reset servo. Input BC = servo command/status register address.
;Calls wrservo.
resetservo:       
;Repeat this until the LM629 indicates that it has reset.
        ld a, RESET
        out (c), a
        in a, (c)
        and 10111110b   ;Ignore Breakpoint and BUSY, i.e. accept 84, 85, c4, c5.
        cp 84h
        jr nz, resetservo
;To be sure of reset, test effect of rsti command.
clrints:
        call resetall
        in a, (c)       ;Read the status for effect of rsti.
        ld d, a         ;Copy for two part testing.
        and 00001110b   ;Test only bits 1,2,3. LM629 has an error in rsti immediately after reset but these are ok.
        jr nz, resetservo ;If it didn't work then try full reset again.
;Reset is OK but we still need to be sure that all flags are clear, which may require that rsti be repeated.
        ld a, d         ;Recover flag values.
        and 01111110b   ;Test all flags except BUSY and motor off.
        jr nz, clrints
        ret
;********************** DATA STORAGE **************************************
s_lfil      db LFIL,03,0fh,  1,0,  0,80h,  1,0,  10h,0 ;For old servo horizontal.
;s_lfil      db LFIL,03,0fh,  1,0,  1,0  0,80h,  10h,0 ;For new servo horizontal.
;s_lfil      db LFIL,03,0fh,  4,0,  0,80h,  3,0,  10h,0 ;OK for belted horizontal
;s_lfil      db LFIL,03,0fh,  2,0,  0,80h,  4,0,  10h,0 ;OK for no load horizontal.
;................. values for finding home stop ...........................
s_lpes      db LPES, 0, 200 ;Excessive load but less than completely stalled.
slowvert    db LTRJ, 0,28h,  0,0,128,0,  0,2,0,0 ;Very slow
;slowvert    db LTRJ, 0,28h,  0,0,1,0,  0,0,80h,0 ;Very slow
;..........................................................................
s_accvel    db LTRJ, 0,28h,  0,0,32,0,  0,128,0,0 ; Very fast.
;s_accvel    db LTRJ, 0,28h,  0,0,1,0,  0,0,80h,0 ;Very slow

s_govel     db LTRJ, 18h, 0
s_goto      db LTRJ, 0,2, 0,0,0,0
s_rstiall   db RSTI, 0, 0
s_motoff    db LTRJ, 1, 0
s_rdsigs    db RDSIGS, 0, 0
d_rdrp      db RDRP, 0,0,0,0
d_rddv      db RDDV, 0, 0, 0, 0
s_hold      db LTRJ, 0, 0
;s_ltrj      db LTRJ, 0,2ah,  0,0,10h,0,  0,10h,0,0,  0,0,40h,0
limitpos    ds 4
;locstack    ds 202
vectab dw   func0, func1, func2, func3, func4
        end


I8080

;MZ8.ASM 8080 ASM for host computer to control target Z8
BNKFLG    EQU 0EF08H     ;SET=0 IF IN B2,01 IF IN B1 
FCB1      EQU 5CH        ;FCB MUST=XX5CH
BEGDMA    EQU 80H        ;BEGINNING OF DMA BUFFER FOR FILES
BDOS      EQU 5
LOCSTK    EQU 0C730H     ;LOCAL STACK USED BY FILE ROUTINES          
      ORG 0C700H
MTBA      DS 150     ;DESTINATION FOR BREAK DATA UPLOAD

      ORG 0C0A0H     ;BEGIN MACHINE FOR OSBORNE
                 ;BASIC-MACHINE DATA BUFFER
MCHN      DS 1           ;TARGET ID:1=2K-Z8: 2=4K-Z8
MCHKL     DS 1           ;CHECKSUM LOW BYTE
MCHKH     DS 1           ;CHECKSUM HIGH       
          DS 1           ;(MCHN+3)=WORKING STORAGE 
          
MFTYP    DS 1           ;1,2="U",3="B",4=IHEX
MXDAT    DS 1           ;#00=NOT EXTERNAL DATA
MFBEG    DS 2           ;FILE BEGIN ADDR
MFLEN    DS 2           ;FILE LENGTH
MHBEG    DS 2           ;HOST MEM FILE ADDR BEG
BUF       DS 2           ;CURRENT BUFFER POSITION
LEN       DS 2           ;FILE ENTRY LENGTH

MCHKY      DS 1           ;FF=KEY IS PRESSED,0=NO KEY PRESSED
MKYVAL    DS 1           ;FAILSAFE REQUEST KEY IN ASCII
MCOMM      DS 1           ;COMMUNICATION (TO uCISE) PROTOCOL.
     ;B7:0=GPIB, 1=RS232; B6:1=UCISE IS TALKER,IE HOST IS L
     ;B5:1=UCISE IS L,IE HOST IS TALKER.B0-4=ADDRESS OF UCISE
MDOUT      DS 1           ;DATA OUTPUT BUFFER
MDIN       DS 1           ;DATA INPUT BUFFER
DOLD      DS 1           ;OLD OUTPUT DATA--FOR ECHO CHECK
MPRFLG     DS 1           ;PRINT MODE FLAG.0=CON ONLY,>80H=LST
     ;ONLY, ELSE BOTH
COMLN     DS 4           ;COMMAND LINE FOR IHEX FILE
          DS 3           ;UNUSED
;
;HIGH LEVEL CALLS MINIT, MRECTAR, MRRT, MXWR, MXRD.REGCHNG
 
          JMP MCSTAT
          JMP MOUT
          JMP MIN
          JMP MINITJMP
          JMP MRSINIT
          JMP MOUTB           ;OUTPUT ONE BYTE FROM @M
          JMP MTXC            ;SEND @M TO UCISE,ADD TO CHKSM
          JMP MRXC            ;INPUT UCISE,ADD TO CHKSM
          JMP MTXCSM          ;SEND CHKSM
          JMP MRXCSM          ;INPUT UCISE CHKSM,COMPARE
          JMP MBITDIS         ;DISPLAY 5*4 TRACE BITS
          JMP MBUPLD          ;UPLOAD BRK DATA
          JMP MRDDSK
          JMP MWRDSK
          JMP MRDHM
          JMP MWRHM
;BIOS JMP TABLE IS JUMPPED TO INDIRECTLY THRU THIS TABLE
;DUMMY ADDRESSES WILL BE REPLACED WITH JMP TABLE LOCATIONS @RUN
CONSTAT   JMP 0
CONIN     JMP 0
LSTOUT    JMP 0
LSTSTAT   JMP 0
;::::::::::::::::::::HOST-SPECIFIC::::::::::::::::::::::::::::
;THE NEXT 4 ARE OSBORNE-SPECIFIC GPIB JMPS
GPSTAT    JMP 0
GPIO      JMP 0
GPDO      JMP 0
GPDI      JMP 0
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;THE NEXT JUMP IS NOT TO BIOS TABLE BUT TO THE ACTUAL BEGIN
;ADDR OF THE CON WRITE ROUTINE. THE BIOS TABLE CON WR JUMP 
;WILL BE REDIRECTED TO A TRANSIENT PROGRAM CALLED PRCON.
CONOUT    JMP 0
;
;INITIALIZE BIOS JMP INDIRECT (DOUBLE) TABLE BY INSERTING ADH

MINITJMP  LHLD 1         ;GET ADDRESS OF WARM START BIOS JMP
          LXI B,3        ;SET UP FOR 16-BIT ADD
          DAD B          ;ADDRESS IN HX=BIOS CONSTAT JMP
          SHLD CONSTAT+1
          DAD B
          SHLD CONIN+1
          MVI C,4        ;OFFSET TO MAKE HX POINT TO CONOUTADL
          DAD B          ;HX POINTS TO ADL
          MOV A,M        ;GET CONOUT JMP ADL
          STA CONOUT+1
          LXI D,PRCON    ;GET ADDR OF NEW PRINT CONTROL
          MOV M,E        ;SET UP NEW ADL IN BIOS TABLE
          INX H
          MOV A,M
          STA CONOUT+2   ;SETUP CONOUT JMP ADH 
          MOV M,D        ;NEW ADH INTO BIOS TABLE
          MVI C,1
          DAD B          ;HX NOW POINTS TO LSTOUT IN BIOS
          SHLD LSTOUT+1
          MVI C,1EH      ;OFFSET TO MAKE HX POINT TO LSTSTATJ
          DAD B
          SHLD LSTSTAT+1
;::::::::::::::::::::::::HOST-SPECIFIC::::::::::::::::::::::::
          MVI C,15H      ;OFFSET FOR GPIB STATUS IN IN BIOS
          DAD B
          SHLD GPSTAT+1
          MVI C,9        ;OFFSET FOR GPIO JMP IN BIOS
          DAD B
          SHLD GPIO+1
          MVI C,3
          DAD B
          SHLD GPDO+1
          DAD B
          SHLD GPDI+1
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
          RET
;
;COMMUNICATION RECOVERY ROUTINE ALLOWS USER TO FORCE EXIT FROM
;TARGET EXECUTION OR HOST-UCISE COMMUNICATION."C"=TERMINATE
;COMM.:"E"=JAM TARGET PLUS RESET:ALL OTHERS=JAM TARGET.
COMMREC   CALL CONSTAT   ;CHECK FOR KEY PRESSED
          STA MCHKY       ;0=NO KEY, ELSE USER INPUT
          ORA A          ;SET Z IF NO KEY PRESSED
          JZ CREND       ;DONE IF NO KEY ELSE GET KEY
          CALL CONIN
          STA MKYVAL
;NOW CHECK FOR NATURE OF USER REQUEST, "C","E",OTHER
          CPI 'C'
          JNZ CREND      ;SKIP MSG IF NOT "C".NOTE THAT Z=0
          CALL COMMERR
          ORI 2          ;ANY VALUE TO CLEAR Z TO ALERT CALLER
CREND     RET            ;Z=1 ONLY IF NO USER INPUT
;
;::::::::::::::::::::::HOST-SPECIFIC::::::::::::::::::::::::::
;RS232 INITIALIZE ROUTINE CALLED BY BASIC
MRSINIT   DI
          OUT 0          ;SWITCH TO BANK 2
          XRA A          ;CLEAR A
          STA BNKFLG
          LXI H,2A00H    ;POINT TO ACIA CONTROL
          MVI M,57H      ;RESET ACIA
          MVI M,55H      ;ESTABLISH 1200BAUD,8BIT,NO PARITY
          INR L          ;POINT TO DATA BUFFER
          MOV A,M        ;FIRST READ TO CLEAR CHANNEL
          MVI M,0DH      ;SEND "CR" TO UCISE TO START LINK
          DCR L          ;POINT TO STATUS REG
CHKTX     MOV A,M
          ANI 2          ;MASK OFF TX EMPTY BIT
          JZ CHKTX       ;WAIT UNTIL TX IS DONE 
          OUT 1          ;SWITCH BACK TO B1
          MVI A,1
          STA BNKFLG
          EI
          RET
;END OF MRSINIT
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;CHECK STATUS OF uCISE COMMUNICATION CHANNEL
;THIS IS CALLED ONLY DURING "G",IE TARGET EXECUTE
MCSTAT    LDA MCOMM       ;GET PROTOCOL
          ORA A          ;S=0 IF GPIB
          JP DONEGP      ;GO WAIT FOR GPIB DONE
          CALL MIN       ;WAIT FOR DONE IF RS232
CHKDONE   LDA MCHKY       ;GET USER INPUT STATUS
          ORA A
          JZ CMSTND      ;JUMP TO EXIT IF NORMAL DONE
          LDA MKYVAL     ;EXIT DUE TO USER REQUEST SO..
          STA MDOUT
          CALL MOUT      ;SEND REQUEST KEY TO UCISE
          CALL MIN       ;GET UCISE RESPONSE--NOT RS232 ECHO
;NOTE THAT UCISE RESPONDS ONLY AFTER REGAINING CONTROL OF TAR
          LXI H,MDOUT
          MOV A,M        ;GET USER KEY
          INX H          ;POINT TO UCISE RESPONSE,MDIN
          CMP M
          JZ FSEXIT      ;SKIP MSG IF RESPONSE MATCHES
          CALL COMMERR   ;ELSE PRINT COMMERR MSG
FSEXIT    MVI A,0EEH     ;ANY NON-ZERO
          STA MCHKY       ;INDICATE THAT EXIT AT USER REQ
CMSTND    RET
;END OF MCSTAT
;
;::::::::::::::::::::::::::HOST-SPECIFIC::::::::::::::::::::::
;WAIT FOR TARGET DONE/USER REQ WITH GPIB 
;
DONEGP    CALL COMMREC   ;CHECK USER REQ
          JNZ GPDNEX     ;USER INPUT IF Z=0
          CALL GPSTAT    ;CHECK SRQ
          RAR            ;Ab0>C, SET=SRQ
          JNC DONEGP     ;REPEAT IF NEITHER EXIT
POLLEN    MVI C,18H
          CALL GPIO      ;OUTPUT ENABLE SERIAL POLL
          ORA A
          JNZ POLLEN     ;REPEAT TILL MESSAGE ACCEPTED
          CALL MIN       ;GET SERIAL ONLY TO CLEAR
POLLDIS   MVI C,19H
          CALL GPIO      ;DISABLE SERIAL POLL
          ORA A
          JNZ POLLDIS    ;REPEAT TIL ACCEPTED
GPDNEX    JMP CHKDONE
;END OF DONEGP--THIS IS NOT A SUBROUTINE      
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;OUTPUT TO uCISE CHECKS CHANNEL/ADDRESS,OUTPUTS FROM BUFFER--
;ALLOWS DIRECT CALL FROM BASIC
MOUT      LDA MCOMM       ;GET COMMUNICATION PROTOCOL
          ORA A          ;GPIB/RS232->SBIT
          JM RS2OUT
          MOV B,A        ;SAVE CURRENT COMM BYTE
          ANI 20H        ;MASK OUT ALL BUT B5
          JNZ GPOUT      ;LEAVE T/L FLAGS IF UCISE=L
          MOV A,B        ;GET COMM BYTE
          ORI 20H        ;SET L FLAG
          ANI 0BFH       ;CLEAR T FLAG
          STA MCOMM       ;STORE UPDATED COMM BYTE
;::::::::::::::::::HOST-SPECIFIC::::::::::::::::::::::::::::::
          ANI 0FH
          ADI 20H
          MOV C,A
          CALL GPIO      ;MAKE UCISE LISTENER
          MVI C,5FH
          CALL GPIO      ;UNTALK UCISE+ANY OTHERS
GPOUT     LDA MDOUT       ;GET DATA FROM OUT BUFFER
          MOV C,A
          CALL GPDO      ;OUTPUT DATA
          ORA A          ;CHECK FOR COMPLETION
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
          JZ CMMOTEX     ;JUMP TO EXIT IF SUCCESSFUL ELSE
          CALL COMMREC   ;CHECK FOR FAILSAFE REC OF COMM
          JZ GPOUT       ;REPEAT TX IF NEITHER EXIT
CMMOTEX   RET
;
RS2OUT    LDA MDOUT
          MOV E,A
          MVI C,5
          CALL BDOS         ;LIST OUTPUT THROUGH BDOS
          CALL RSRDR     ;CALL RS232 RDR IN
          LXI H,MDOUT     ;POINT TO OUTPUT DATA BUFFER
          CMP M
          JZ CMMOTEX     ;GOTO RET IF NO ERROR
          CALL COMMERR   ;PRINT MSG IF ERROR
          JMP CMMOTEX         
;END OF uCISE COMMUNICATION OUTPUT
;
;uCISE COMMUNICATION INPUT
MIN       LDA MCOMM       ;GET COMMUNICATION PROTOCOL
          ORA A          ;GPIB VS RS232->SBIT
          JM RS2IN
          MOV B,A        ;SAVE COMM BYTE
          ANI 40H        ;MASK OUT ALL BUT B6
          JNZ GPIN       ;INTERFACE ALREADY OK FOR HOST IN
          MOV A,B        ;GET COMM BYTE
          ANI 0DFH       ;CLEAR L FLAG (UCISE STATUS)
          ORI 40H        ;SET T FLAG
          STA MCOMM       ;STORE UPDATED COMM BYTE
;::::::::::::::::HOST-SPECIFIC::::::::::::::::::::::::::::::::
          ANI 0FH
          ADI 40H
          MOV C,A
          CALL GPIO      ;MAKE UCISE TALKER
          MVI C,3FH
          CALL GPIO      ;UNLISTEN ALL UNITS ON BUS
GPIN      CALL GPDI      ;INPUT DEVICE MESSAGE
          STA MDIN        ;ASSUME THAT INPUT IS OK
          MOV A,L        ;GET ERR STATUS
          ORA A
          JZ CMMINEX     ;JMP TO EXIT IF SUCCESSFUL ELSE
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
          CALL COMMREC   ;CHECK FOR USER-REQ FS RECOVERY
          JZ GPIN        ;REPEAT IF NEITHER EXIT
CMMINEX   RET
;
RS2IN     CALL RSRDR     ;READ RS232 RDR
          STA MDIN        ;THIS MAY BE A DUMMY BYTE
          MOV E,A        ;DITTO, BUT THIS SAVES GOOD READ
          LDA MCHKY       ;..FROM THIS FETCH
          ORA A
          JNZ CMMINEX    ;SKIP ECHO IF EXIT DUE TO USER REQ
          MVI C,5
          CALL BDOS         ;OUTPUT ECHO TO LST
          JMP CMMINEX    ;GO TO EXIT
;END OF MIN
;
COMMERR   LXI D,COMMSG
          MVI C,9
          CALL BDOS
          RET
COMMSG    DB 'COMMUNICATION FAILURE$'
;
;SPECIAL PRINT CONTROL ALLOWS CON ONLY,PRNTR ONLY, OR BOTH
PRCON     LDA MPRFLG      ;GET MODE FLAG
          MOV B,A
          ORA A          ;SET Z IF CON ONLY,S IF PNTR ONLY
          JM LSTONLY
          PUSH B         ;SAVE MODE FLAG AND DATA IN
          CALL CONOUT
          POP B
          MOV A,B        ;GET SAVED MODE FLAG
          ORA A
          JZ PRRET       ;SKIP LST IF CON ONLY
LSTONLY   PUSH B
GETPRST   CALL LSTSTAT
          ORA A
          JZ GETPRST     ;REPEAT TIL PRINTER RDY
          POP B
          CALL LSTOUT
PRRET     RET  
;
;:::::::::::::::::::HOST-SPECIFIC:::::::::::::::::::::::::::::
;RS232 RDR INPUT DIRECTLY MANIPULATES SERIAL INTERFACE
RSRDR     DI
          OUT 0
          XRA A
          STA BNKFLG
          LDA 2A00H      ;GET ACIA STATUS
          ANI 5          ;MASK FOR DCD AND RXIN
          CPI 1
          JZ GETRDR      ;JMP TO GET RDR INPUT IF OK ELSE
          OUT 1
          LXI H,BNKFLG
          MVI M,1
          EI
          CALL COMMREC   ;CHECK FOR USER REQUEST TO EXIT
          JZ RSRDR       ;CONTINUE IF NO EXIT
          DI
          OUT 0
          XRA A
          STA BNKFLG     ;ALLOW BOTH FS. AND NORMAL EXIT TO
               ;READ RX IN ORDER TO CLEAR IT
GETRDR    LDA 2A01H
          OUT 1
          LXI H,BNKFLG
          MVI M,1
          EI
          RET
;END OF RSRDR
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;OUTPUT BYTE.BASIC POINTS TO SOURCE WITH HX
MOUTB     MOV A,M
          STA MDOUT
          CALL MOUT
          RET
;OUTPUT BYTE @M AND ADD TO CHECKSUM
MTXC      MOV A,M        ;GET OUTPUT BYTE
          STA MDOUT
          MOV C,A
          MVI B,0
          LHLD MCHN+1    ;GET CHECKSUM
          DAD B
          SHLD MCHN+1    ;STORE UPDATED CHKSM
          CALL MOUT
          RET
;END OF MTXC
;
;INPUT BYTE FROM UCISE AND ADD TO CHKSM
MRXC      CALL MIN 
          LDA MDIN
          MOV C,A
          MVI B,0
          LHLD MCHN+1
          DAD B
          SHLD MCHN+1
          RET
;END OF MRXC
;
;SEND CHKSM TO UCISE
MTXCSM    LDA MCHN+2     ;GET CHKSM HIGH
          STA MDOUT
          CALL MOUT
          LDA MCHN+1     ;GET CHKSM LOW
          STA MDOUT
          CALL MOUT
          RET
;END OF MTXCSM
;
;INPUT CHKSM FROM UCISE,COMPARE TO CALCULATED CHKSM
MRXCSM    XRA A
          STA MCHN+3     ;DEFAULT CLEAR ERR FLAG
          CALL MIN       ;GET CHKSM HIGH INPUT
          LHLD MDIN       ;CHKSM HIGH>>L
          LDA MCHN+2     ;GET CALCULATED CHKSMHIGH
          CMP L
          JZ CHKSML      ;SKIP ERR MOD IF MATCH
          MVI A,255
          STA MCHN+3     ;SET ERR FLAG
CHKSML    CALL MIN       ;GET CHKSM LOW INPUT EVEN IF HIGH ERR
          LHLD MDIN       ;RETRIEVE CHKSM>>L
          LDA MCHN+1     ;GET CAL CHKSM LOW
          CMP L
          JZ CHKEX       ;EXIT,LEAVING CURRENT FLAG IF MATCH
          MVI A,255      
          STA MCHN+3     ;SET ERR FLAG DUE TO LOW MISMATCH
CHKEX     LDA MCHN+3
          STA MDIN        ;MOVE ERR FLAG TO MDIN FOR BASIC
          RET
;END OF MRXCSM
;DISPLAY 5*4 BITS OF TRACE BY CALLING FOR UCISE INPUT AND TO
;BDOS TO DISPLAY 1'S & 0'S
MBITDIS   CALL MIN       ;GET X12-19
          LDA MDIN
          MVI H,9        ;INIT SHIFT COUNT.ACTUAL SHIFT IS 8
          CALL BITSUB    ;BUT EXTRA 1 COMPENSATES FOR SPACE
          CALL MIN
          LDA MDIN
          MVI H,9
          CALL BITSUB
          CALL MIN
          LDA MDIN
          MVI H,4        ;SHIFT COUNT FOR NIBBLE
          CALL BITSUB
          RET
;
BITSUB    MVI E,'0'      ;DEFAULT DISPLAY
          RLC            ;TEST B7
          MOV L,A
          JNC DEF0OK
          INR E          ;ADJUST TO "1"
DEF0OK    PUSH H         ;SAVE CURRENT SHIFTED BYTE & COUNT
          MVI C,2
          CALL BDOS         ;BDOS F#2=DISPLAY
          POP H
          MVI E,20H      ;DEFAULT " "
          DCR H
          JZ BSUBEX      ;EXIT LOOP IF DONE
          MVI A,5        ;@ COUNT 5 IS SPACE PRINT
          CMP H
          JZ DEF0OK      ;GO BACK TO PRINT SPACE
          MOV A,L        ;RETRIEVE CURRENT BYTE
          JMP BITSUB
BSUBEX    MVI C,2
          CALL BDOS         ;PRINT FINAL " "
          RET
;END OF MBITDIS
;
;::::::::::::::::::::::TARGET-SPECIFIC::::::::::::::::::::::::
;UPLOAD BREAK DATA
MBUPLD    LXI H,MTBA
          LXI B,92H      ;LENGTH=146D
BUPLDLP   SHLD MCHN+4    ;USE INSTEAD OF PUSH
          PUSH B
          CALL MIN
          POP B
          LHLD MCHN+4
          LDA MDIN
          MOV M,A
          INX H          ;NOTE FORWARD STORAGE:8048 IS BACKWARD
          DCX B
          XRA A          ;CLEAR A TO COMPARE AGAINST B,C
          CMP B          ;CHECK HIGH COUNT
          JNZ BUPLDLP
          CMP C          ;CHECK LOW COUNT
          JNZ BUPLDLP
          RET
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
;FILE TRANSFER ROUTINES
;CONVERT ASCII IN HL TO BINARY IN A
ASCON     MOV A,L        ;GET LOW NIBBLE
          CALL NIBCON    ;CONVERT TO BINARY
          MOV L,A        ;SAVE
          MOV A,H        ;GET HIGH NIBBLE
          CALL NIBCON
          RLC
          RLC
          RLC
          RLC            ;ROTATE HIGH NIBBLE
          ORA L          ;ADD LOW NIBBLE
          RET
NIBCON    CPI 41H
          JC ASCNUM      ;JUMP IF ASCII<41H,IE NUMERIC
          SUI 7          ;ALIGN ALPHA HEX
ASCNUM    SUI 30H        ;ADJUST TO REAL VALUE
          RET
;END OF ASCII CONVERSION
;
;READ FILE BUFFER ENTRY,LOADING NEW RECORD IF NEEDED
RDBUF     LHLD BUF       ;GET CURRENT BUFFER ADDRESS
          INR L          ;BUF+1.NOTE THAT DMA=XX80-FF
          CZ RDSEQ       ;GET NEXT REC IF BUF OVERFLOWS
          MOV A,M        ;@BUF->A
          SHLD BUF       ;SAVE UPDATED BUF
          RET
;          
RDSEQ     MVI C,20       ;F#READ SEQUENTIAL
          LXI D,FCB1     
          CALL BDOS
          LXI H,BEGDMA   ;REINITIALIZE BUF
;CHECKING FOR VALID NEXT REC MAY BE USEFUL HERE
SEQRDX    RET
;
;READ NEXT 2 ASCII FILE ENTRIES,CONVERT TO BINARY IN A
RDCON     CALL RDBUF
          PUSH PSW       ;SAVE A=HIGH NIBBLE IN ASCII
          CALL RDBUF
          POP H          ;RETRIEVE HIGH NIB
          MOV L,A        ;GET LOW NIB
          CALL ASCON     ;CONVERT TO BIN-->A
          RET
;
;READ INTEL HEX FILE
RDIHEX    LXI H,BEGDMA+7FH
          SHLD BUF       ;INITIALIZE BUF TO FORCE READ REC
PREPNXT   CALL RDBUF
          CPI ':'
          JNZ PREPNXT    ;READ @BUF+1 UNTIL ENTRY=":"        
          LXI H,COMLN    ;INIT COMMAND POINTER 
          MVI B,4        ;INIT COMMAND LENGTH 
CMMNDLP   PUSH H
          PUSH B
          CALL RDCON     ;GET NEXT ENTR AS BINARY
          POP B          ;RETRIEVE COMMAND LENGTH
          POP H          ;COMLINE POINTER
          MOV M,A        ;STORE COMMAND BYTE
          INX H          ;POINT TO NEXT DEST
          DCR B
          JNZ CMMNDLP    ;CONTINE UNTIL COMM LINE IS FULL
          LXI H,COMLN    ;POINT TO DATA FIELD LENGTH
          MOV A,M
          ORA A
          JZ RWFILEX     ;EXIT IF LENGTH=0
          MVI A,1BH      ;uCISE F#=DOWNLOAD
          STA MDOUT
          CALL MOUT
          LXI H,COMLN+1  ;POINT TO ADH
          CALL MOUTB
          LXI H,COMLN+2  ;POINT TO ADL
          CALL MOUTB
          MVI A,0        ;DATA LENGTH HIGH=00
          STA MDOUT       ;STORE @MAILBOX
          CALL MOUT      ;SEND LENGTH HIGH
          LXI H,COMLN    ;POINT TO LENGTH (LOW)
          CALL MOUTB
          LXI H,MXDAT   ;POINT TO FLAG
          CALL MOUTB
;COMMAND LINE HAS BEEN SENT.NOW SEND DATA
          XRA A          ;A<-#00
          STA MCHN+1
          STA MCHN+2     ;ZERO CHKSM
IHXDAT    CALL RDCON
          LXI H,COMLN+3  ;USE TYP SLOT FOR MAILBOX
          MOV M,A        ;PUT DATA IN MAILBOX
          CALL MTXC      ;SEND DATA,ADD TO CHKSM
          LXI H,COMLN    ;POINT TO LEN
          DCR M
          JNZ IHXDAT     ;CONTINUE SENDING DATA
          CALL MTXCSM    ;SEND CHKSM FOR EACH DATA BLOCK
          JMP PREPNXT    ;GO PROCESS NEXT COMM LINE
;END OF IHEX FILE READ
;READ BINARY FILES,U1,U2,B
RDU1      CALL RDSEQ     ;GET FIRST REC
                         ;HL=BEGDMA FROM RDSEQ
          MOV E,M        ;GET BEG ADL FROM FIRST REC
          INX H
          MOV D,M        ;GET BEG ADH
          XCHG
          SHLD MFBEG    ;MOVE ADDR TO COMMAND BUFFER
RDUTYP    LXI H,BEGDMA+2 ;POINT TO LENGTH LOW
          MOV E,M        ;GET LENGTH LOW
          INX H
          MOV D,M        ;GET LENGTH HIGH
          SHLD BUF       ;INIT BUF=XX83H
          XCHG
          SHLD MFLEN    ;MOVE LENGTH TO COMMAND BUFFER
RDUB      MVI A,1BH      ;uCISE F#=DOWNLOAD
          CALL SNDARG    ;SEND ARGUMENTS TO uCISE
UBDATLP   CALL RDBUF     ;READ REC ENTR AT BUF+1
          CALL MTXC      ;SEND @M TO uCISE, ADD TO CHECKSUM
          CALL ADFLEN    ;ADVANCE LENGTH
          JNZ UBDATLP    ;CONTINUE SENDING DATA
          CALL MTXCSM    ;SEND CHKSM TO uCISE
RWFILEX   MVI C,16       ;BDOS F#.BOTH R/W EXIT HERE
          LXI D,FCB1
          CALL BDOS         ;CLOSE FILE
          POP H          ;RETRIEVE OLD SP
          SPHL           ;RESTORE SP
          RET            ;RET TO ADDR ON OLD STACK
;
ADFLEN    LHLD LEN
          DCX H
          SHLD LEN       ;ADVANCE FILE LEN COUNT
          MOV A,L
          ORA H          ;SET Z IF END OF FILE DATA
          RET
;
;SEND FILE ARGUMENTS TO uCISE USED BY R AND W
SNDARG    STA MDOUT       ;STORE uCISE F#
          CALL MOUT      ;SEND F#
          LDA MFBEG+1   ;GET ADH
          STA MDOUT
          CALL MOUT
          LDA MFBEG     ;GET ADL
          STA MDOUT
          CALL MOUT
          LHLD MFLEN    ;GET LENGTH
          SHLD LEN       ;INIT CNTR
          MOV A,H
          STA MDOUT
          CALL MOUT      ;SEND LEN HIGH
          LDA MFLEN     ;GET LEN LOW
          STA MDOUT
          CALL MOUT 
          LDA MXDAT     ;GET FLAG
          STA MDOUT
          CALL MOUT
          RET
;END OF SNDARG
RDU2      CALL RDSEQ     ;READ FIRST REC
          JMP RDUTYP     ;SAME AS RDU1 BUT ADDR=USER ENTRY
RDBDT     LXI H,BEGDMA+7FH
          SHLD BUF       ;FORCE INITIAL REC READ
          JMP RDUB       ;SAME AS U TYPES BUT ALL USER ARGS
;END OF FILE READ SUBS
FSETUP    XCHG       ;SAVE PNTR TO STRING DESC IN DE
      POP B          ;GET CALLER'S RET ADDR
          LXI H,0
          DAD SP         ;SP-->HL
          LXI SP,LOCSTK  ;MAKE LOCAL STACK 
          PUSH H         ;SAVE OLD SP AT TOP OF NEW
          PUSH B         ;SAVE CALLER'S RET ADDR
;USE FILE NAME STRING DESCRIPTOR TO LOCATE--MOVE TO FCB1
      LDAX D     ;GET LENGTH
      MOV C,A    ;SAVE IN COUNTER
      INX D
      LDAX D     ;GET ADL OF STRING
      MOV L,A
      INX D
      LDAX D     ;GET ADH
      MOV H,A
;NOW PUT DEFAULT ' 'INTO FCB NAME
      LXI D,FCB1+11
      MVI B,11
      MVI A,' '
CLRFCB    STAX D
      DCX D      ;DE WILL BE LEFT-->FCB[0]
      DCR B
      JNZ CLRFCB
      XRA A
      STA MCHKY  ;ZERO ABN FLAG FOR BASIC=NO FILE ERR
      STAX D     ;DEFAULT USE CURRENT DRIVE
      INX H      ;POINT TO CHAR[1],IE SECOND
      MVI A,':'
      CMP M          ;DOES NAME INCLUDE DRIVE?
      JNZ MOVFNAM    ;LEAVE CURRENT IF NOT
;LOAD EXPLICIT DR CODE
      DCX H
      MOV A,M    ;GET DR NAME
      SUI '@'    ;CONVERT TO CODE
      STAX D     ;DR CD-->FCB[0]
      INX H
      INX H
      INX H      ;POINT TO SECOND FILENAME CHAR
      DCR C
      DCR C      ;REMOVE ":X" FROM CHAR COUNT
MOVFNAM   DCX H          ;POINT TO F NAME[0]
      INX D          ;FCB+1
FNAMLP    MOV A,M    ;GET CHAR
      CPI '.'    
      JZ FTYPADJ     ;ADJUST FOR TYPE ALIGNMENT IN FCB
      STAX D     ;STORE CHAR IN FCB
      INX D
FNMOVC    INX H          ;POINT TO NEXT CHAR IN NAME OR TYP
      DCR C
      JNZ FNAMLP
      LXI H,FCB1+12
      MVI B,24
ZERFCB    MOV M,C    ;C=0 FROM LOOP
      INX H
      DCR B
      JNZ ZERFCB
      LXI H,MCHN+1
      MOV M,C    ;ZERO CHKSM
      INX H
      MOV M,C       
          MVI C,26       ;BDOS F#MAKE DMA
          LXI D,BEGDMA
          CALL BDOS
      MVI C,15
      LXI D,FCB1
      CALL BDOS  ;OPEN FILE
      INR A      ;SET Z IF OPEN ERR
          RET
FTYPADJ   LXI D,FCB1+9   ;ADJ DESTINATION TO TYP
      JMP FNMOVC     ;CONT MOVING NAME.C-,BUT "." NOT STOR
;
MRDDSK    CALL FSETUP
          JNZ RDFILOP    ;Z=0 IF FILE OPENED OK
      DCR A
      STA MCHKY  ;FLAG ERR FOR BASIC
      LXI D,FRDOPER  ;POINT TO FILE RD OPEN ERR 
      MVI C,9
      CALL BDOS  ;PRINT ERROR
      JP RWFILEX        
RDFILOP   LDA MFTYP
          DCR A
          JZ RDU1
          DCR A
          JZ RDU2
          DCR A
          JZ RDBDT
          JMP RDIHEX
;
FRDOPER   DB 'FILE NOT FOUND$'
;
;WRITE @BUF=1,WRITING OLD REC TO FILE ON OVERFLOW
WRBUF     LHLD BUF
          INR L          ;ONLY L BECAUSE BUF=XX80-FFH
          JNZ WRFB
          CALL WRSEQ
WRFB      MOV M,A        ;PUT INPUT INTO BUF
          SHLD BUF       ;STORE UPDATED BUF
          RET
;
WRSEQ     PUSH PSW       ;SAVE INPUT A
          MVI C,21       ;BDOS F#WR SEQ
          LXI D,FCB1
          CALL BDOS         ;WRITE PREVIOUS REC
          ORA A
          JZ WRFOK
          MVI C,9        ;BDOS F#=WRITE MESS
          LXI D,OVRMSS
          CALL BDOS
WRFOK     LXI H,BEGDMA
          POP PSW        ;RETRIEVE INPUT          
          RET
OVRMSS    DB 'FILE OVERRUN$'
;
MWRDSK    CALL FSETUP
      JZ FWOPOK      ;Z=1 IF OPEN ERR,IE DOESN'T EXIST
      LXI D,FEXIST   ;POINT TO FILE EXISTS MESS
      MVI C,9
      CALL BDOS
      MVI C,1
      CALL BDOS      ;GET USER REQUEST
      CPI 'Y'        ;OVERWRITE?
      JNZ WERREX
          LXI D,FCB1
      MVI C,19       
      CALL BDOS      ;ERASE FILE BEFORE WRITE 
FWOPOK    LXI D,FCB1
          MVI C,22       ;BDOS MAKE FILE
          CALL BDOS
      INR A
      JNZ WRPRO  ;CONTINUE IF MAKE OK
      LXI D,DSKF     ;DISK FULL MESS
      MVI C,9
      CALL BDOS
WERREX    MVI A,255
      STA MCHKY  ;FLAG ABN FOR BASIC
      JMP RWFILEX
WRPRO     MVI A,1AH      ;uCISE F# UPLOAD
          CALL SNDARG
          LXI H,BEGDMA   ;DESTINATION
          LXI D,MFBEG   ;SOURCE
          MVI B,4        ;COUNT FOR ARG MOVE
MOVARGS   LDAX D
          MOV M,A
          INX H
          INX D
          DCR B
          JNZ MOVARGS
          LXI H,BEGDMA+3                    
          SHLD BUF       ;INIT BUF=XX83H
MVUCF     CALL MRXC      ;GET uCISE IN,ADD TO CHKSM
          CALL WRBUF     ;WR A TO @BUF+1
          CALL ADFLEN    ;ADVANCE LEN COUNT
          JNZ MVUCF
          LHLD BUF       ;GET LAST DISK BUFFER ADDR
          DCR L
          JP WFDON       ;NO NEED FOR FINAL WR IF FINAL BUF
                         ;...ADL=80H
          CALL WRSEQ     ;ELSE WRITE LAST REC
WFDON     CALL MRXCSM
          ORA A          ;A=0 IF CHKSM'S MATCH
          CNZ COMMERR    ;PRINT "COMM FAILURE"
          JMP RWFILEX
FEXIST    DB 'FILE ALREADY EXISTS: DO YOU WANT TO OVERWRITE?$'
DSKF      DB 'DISK OR DIRECTORY FULL OR PROTECTED$'
;
;HOST MEMORY FILE R/W ROUTINES
HMINIT    CALL SNDARG    ;SEND ARGS TO uCISE,F# IS IN A
          LHLD MFLEN
          XCHG           ;GET FILE LENGTH
          LHLD MHBEG    ;GET HOST MEM BEG
          RET
;
MRDHM     CALL HMSTUP    ;SETUP LOCAL STACK
      MVI A,1BH      ;uCISE F#=DOWNLOAD
          CALL HMINIT    ;SEND ARGS TO uCISE AND INTO REGS
HMRDLP    PUSH D         ;SAVE LEN
          PUSH H         ;SAVE HM ADDR
          CALL MTXC      ;@M-->uCISE+CHKSM
          POP H
          POP D
          INX H
          DCX D
          MOV A,E
          ORA D
          JNZ HMRDLP
          CALL MTXCSM    ;SEND CHKSM TO uCISE
      POP H     ;RECOVER OLD SP
      SPHL      ;RESTORE SP
          RET
;
MWRHM     CALL HMSTUP
      MVI A,1AH      ;uCISE F#UPLOAD
          CALL HMINIT
HMWRLP    PUSH D         ;LENGTH
          PUSH H         ;HM ADDR
          CALL MRXC      ;GET uCISE INPUT
          POP H
          POP D
          MOV M,A
          INX H
          DCX D
          MOV A,E
          ORA D
          JNZ HMWRLP
          CALL MRXCSM    ;GET CHKSM FROM uCISE
          ORA A
          CNZ COMMERR
          POP H
      SPHL
      RET
HMSTUP    POP B     ;GET CALLER RET ADDR
      LXI H,0
      DAD SP    ;GET OLD SP
      LXI SP,LOCSTK 
      PUSH H    ;SAVE OLD SP
      PUSH B    ;SETUP RET ADDR
      XRA A
      STA MCHKL
      STA MCHKH ;ZERO CHKSUM
      RET
      END


M6809

; N6809.ASM
; NIW Demo co-processor program for assembly by Cross32.
;-------------------------- PRAGMAS -------------------------------
    HOF "INT8"          ;Output Intel hex.
    CPU "C:\CROSS32\6809.TBL"   ;Location of CPU table.
;-------------------------- DEFINITIONS ---------------------------
LEDBAR: EQU 1000H
;-------------------------- LOW END STORAGE -----------------------
;....................... ALTERNATE BLINKER ......................................
    ORG 0E000H
DELAY68: DWM 0
ONOFF68: DFB 1  
ALTBLINK:
    LDA #55H
    STA LEDBAR
    JSR DLY
    LDA #0AAH
    STA LEDBAR
    JSR DLY
    BRA ALTBLINK
    ORG 0EF00H     
STKBEG: DFS 100
STKEND:
    ORG 0F000H
;-------------------------- PROGRAMS ------------------------------
DLY:    LDD DELAY68
DLOOP:  SUBD #1
    BNE DLOOP
    RTS
; ................. LED Blinker .....................................
INIT:
    LDS #STKEND
    LDB #0ABH
    LDY #1234H
    LDX #5678H
    LDU #9ABCH
LOOP:
    LDA #0
    STA LEDBAR
    JSR DLY
    LDA #0FFH
    STA LEDBAR
    JSR DLY
    BRA LOOP
;....................... NMIFUNCTIONS ....................................
    ORG 0FE00H
NMIWRITEREGS:
;Register change flags: @FFE0h, b0->S; @FFE1h, 
;b0->CCR, b1->ACCA, b2->ACCB, b3->DP, b4->X, b5->Y, b6->U, b7->PC.
;New values are stored from FFE2h-FFEFh in the same order as when reading registers.
;To accomodate S change request, we first copy all unchanging registers to the buffer, which then contains a mix of 
;new values of changing regs and old values of unchanging ones. Then we change the S if requested, and then copy the 
;entire buffer into the stack frame. This is doing a lot of unnecessary work in case of no S change, which could be
;handled by simply copying new values of changing registers from buffer into the stack frame.
;First copy unchanging byte registers from stack frame to buffer.
    LDX #0FFE2H ;Beginning of buffer.
    LDB #4      ;4 byte registers.
BYTEREGS:
    PULS A      
    ROR 0FFE1H
    BCS BYTERG  ;If this register is changing then don't copy its value from stack frame to buffer.
    STA ,X+     ;Copy CCR, ACCA, ACCB, DP if corresponding flag is clear, i.e. retain current application value.
    DECB
    BNE BYTEREGS
    BRA INIWORD
BYTERG: 
    LDA ,X+     ;Advance destination pointer without changing the buffer contents.
    DECB
    BNE BYTEREGS
;Now copy the word registers that aren't changing from stack frame to buffer.
INIWORD:
    LDB #4      ;4 word registers.
WORDREGS:
    PULS Y
    ROR 0FFE1H
    BCS WORDRG
    STY ,X++    ;Copy X, Y, U, PC if corresponding flag is clear.
    DECB
    BNE WORDREGS
    BRA CHKSTK
WORDRG: LDY ,X++    ;Advance pointer.
    DECB
    BNE WORDREGS
CHKSTK: 
    ROR 0FFE0H
    BCC REPSTK
    LDS 0FFEEH  ;Get new S value if S change requested.
;Now work back down the stack frame, transferring the buffer into the frame.
REPSTK: LDB #6      ;4 word regs. plus 2 byte reg pairs = 6 words.
RPSLP:  LDY ,--X
    PSHS Y
    DECB
    BNE RPSLP
    RTI
;....................................................................
    ORG 0FF00H
NMIREADREGS:    
;Copy registers from NMI stack frame to buffer at FFE2-FFEF in the same order as the stack frame:
;CCR@E2, ACCA@E3, ACCB@E4, DP@E5, Xh@E6, Xl@E7, Yh@E8, Yl@E9, Uh@EA, Ul@EB, PCh@EC, PCl@ED, Sh@EE, Sl@EF.
    TFR S, Y    ;Copy S to Y for use in transfer.
    LDX #0FFE2H
    LDA #6
NMIRRL: LDU ,Y++
    STU ,X++
    DECA
    BNE NMIRRL
    STY ,X      ;By advancing up the stack, Y now has the value of S before NMI. 
    RTI
;------------------- PARKING LOOP -------------------------------
    ORG 0FFD0H
PARKSPACE:
    BRA $
;------------------- HIGH END STORAGE ---------------------------
;................... Vectors ....................................
    ORG 0FFF2H
    DWM INIT    ;SWI3   @FFF2
    DWM INIT    ;SWI2   @FFF4
    DWM INIT    ;FIRQ   @FFF6
    DWM INIT    ;IRQ    @FFF8
    DWM INIT    ;SWI    @FFFA
    DWM NMIREADREGS ;NMI    @FFFC
    DWM INIT    ;RESET  @FFFE
    END




Prev   Next   Site Map   Home   Top   Valid HTML   Valid CSS