. | . | . | . | David McCracken |
Assembly Language Examplesupdated:2016.07.13 |
See other examples (M6801, Z8, etc.) in Firmware Examples
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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
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
/* 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;
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
;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
;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
; 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