;=========================================================================== ; File: PCSPAK.asm ; Project: MUNGA ; Contents: PC serial packet driver ;--------------------------------------------------------------------------- ; Date Who Modification ; -------- --- ----------------------------------------------------------- ; 01/16/95 CPB Initial coding ;--------------------------------------------------------------------------- ; Copyright (C) 1994, Virtual World Entertainment, Inc. ; All Rights reserved worldwide ; This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL ;=========================================================================== include l4asm.inc include pcserial.inc include pcpic.inc include pcdpmi.inc ;=========================================================================== ;; Diagnostics flags ;=========================================================================== KEEP_HISTORY equ 0 DIE_ON_ERROR equ 1 DIE_ON_OVERFLOW equ 1 WATCH_IRQ equ 1 THE_WAY_IT_WAS equ 0 ;=========================================================================== ; Equates ;=========================================================================== if DIE_ON_ERROR DISABLE_AND_DIE MACRO val ;debugging macro: crash to debugger ;-------------------------------------------------------------- ; Disable UART interrupt ;-------------------------------------------------------------- mov dx,global_serial_port RETRACT_MCR UART_MCR_IRQ+UART_MCR_RTS ; NOTE: from this point on, interrupts CANNOT occur from the UART. ;-------------------------------------------------------------- ; Send non-specific EOI just in case we're in IRQ routine ;-------------------------------------------------------------- PIC_RESET_MASTER ;-------------------------------------------------------------- ; Okay, NOW die. ;-------------------------------------------------------------- push eax ;save ax push edx ;save dx mov edx,0FFFFFFFFh ;crash loudly mov eax,val mov [edx],eax ENDM else DISABLE_AND_DIE MACRO val ENDM endif if WATCH_IRQ IRQ_WATCH MACRO val mov iThinkIRQIsOn,val ENDM IRQ_WATCH_ENTRY MACRO push iThinkIRQIsOn mov iThinkIRQIsOn,0 ENDM IRQ_WATCH_EXIT MACRO pop iThinkIRQIsOn ENDM else IRQ_WATCH MACRO val ENDM IRQ_WATCH_ENTRY MACRO ENDM IRQ_WATCH_EXIT MACRO ENDM endif TICK MACRO ;debugging macro: ticks the speaker push dx push ax mov dx,061h in al,dx xor al,002h out dx,al nop nop nop xor al,002h out dx,al pop ax pop dx ENDM SPKR_SET MACRO ;debugging macro push dx push ax mov dx,061h in al,dx or al,002h out dx,al pop ax pop dx ENDM SPKR_RESET MACRO ;debugging macro push dx push ax mov dx,061h in al,dx and al,(NOT 002h) out dx,al pop ax pop dx ENDM SPKR_TOGGLE MACRO ;debugging macro push dx push ax mov dx,061h in al,dx xor al,002h out dx,al pop ax pop dx ENDM ;------------------------------------------------------------------------- ; Command buffer equates ; ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ; Any modifications to this structure MUST be ; reflected in L4PCSPAK.hpp!! ; ; BE ESPECIALLY CAREFUL IF 'BUFSIZE' IS CHANGED! ; ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ;------------------------------------------------------------------------- COMBUFSIZE EQU 512 ;;;1024 ;buffer size (must be power of 2) ;; was 256 before 7-13-95 cpb COMBUF STRUC head dw 0 tail dw 0 tempHead dw 0 tempTail dw 0 count dw 0 tempIn dw 0 tempOut dw 0 data db COMBUFSIZE dup (?) COMBUF ENDS ;------------------------------------------------ ; COM_INIT macro ; Initializes COMBUF structure ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; (none) ;------------------------------------------------ COM_INIT MACRO name ;enter with object pointer in esi add esi,name ;;build COMBUF pointer in esi mov head[esi],0 mov tempHead[esi],0 mov tempTail[esi],0 mov tail[esi],0 mov count[esi],0 mov tempIn[esi],0 mov tempOut[esi],0 sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_PUT_AL macro ; 'Temporarily' puts character in AL into COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; ebx modified ;------------------------------------------------ COM_PUT_AL MACRO name LOCAL label1 LOCAL label2 add esi,name ;;build COMBUF pointer in esi cmp tempIn[esi],COMBUFSIZE if DIE_ON_OVERFLOW jb label1 DISABLE_AND_DIE 8 endif jge label2 label1: inc tempIn[esi] ;;increment 'tempIn' count xor ebx,ebx ;;get 'tempHead' offset in ebx mov bx,tempHead[esi] add esi,ebx ;;add to base mov data[esi],al ;;write the data sub esi,ebx ;;restore COMBUF pointer inc bx ;;bump 'tempHead' and bx,COMBUFSIZE-1 ;;limit it mov tempHead[esi],bx ;;save the new 'tempHead' offset label2: sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_RESTORE_IN macro ; Discards characters 'temporarily' placed in COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; bx modified ;------------------------------------------------ COM_RESTORE_IN MACRO name add esi,name ;;build COMBUF pointer in ecx mov tempIn[esi],0 ;;clear the 'tempIn' count mov bx,head[esi] ;;restore 'tempHead' index mov tempHead[esi],bx sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_ACCEPT_IN macro ; Accepts characters 'temporarily' placed in COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; bx modified ;------------------------------------------------ COM_ACCEPT_IN MACRO name add esi,name ;;build COMBUF pointer in ecx xor bx,bx ;;prepare for exchange xchg bx,tempIn[esi] ;;get, clear 'tempIn' count add count[esi],bx ;;add to current count mov bx,tempHead[esi] ;;set the new head offset mov head[esi],bx sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_GET_AL macro ; 'Temporarily' gets character into AL from COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; ebx modified ;------------------------------------------------ COM_GET_AL MACRO name add esi,name ;;build COMBUF pointer in ecx inc tempOut[esi] ;;inc 'tempOut' count xor ebx,ebx ;;get 'tempTail' offset in ebx mov bx,tempTail[esi] add esi,ebx ;;add to base mov al,data[esi] ;;read the data sub esi,ebx ;;restore COMBUF pointer inc bx ;;bump 'tempTail' and bx,COMBUFSIZE-1 ;;limit it mov tempTail[esi],bx ;;save the new 'tempTail' offset sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_RESTORE_OUT macro ; Restores characters 'temporarily' removed from COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; bx modified ;------------------------------------------------ COM_RESTORE_OUT MACRO name add esi,name ;;build COMBUF pointer in ecx mov tempOut[esi],0 ;;clear the 'tempOut' count mov bx,tail[esi] ;;restore 'tempTail' index mov tempTail[esi],bx sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_ACCEPT_OUT macro ; Discards characters 'temporarily' removed from COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; bx modified ;------------------------------------------------ COM_ACCEPT_OUT MACRO name add esi,name ;;build COMBUF pointer in ecx xor ax,ax ;;prepare for exchange xchg ax,tempOut[esi] ;;get, clear 'tempOut' count sub count[esi],ax ;;subtract from current count mov ax,tempTail[esi] ;;set the new tail offset mov tail[esi],ax sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_EAT_PACKET macro ; Discards entire buffer item from COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; ebx = pointer to command length table ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; ax modified ;------------------------------------------------ COM_EAT_PACKET MACRO name add esi,name ;;build COMBUF pointer in esi push ebx xor ebx,ebx ;;get 'tail' offset in ebx mov bx,tail[esi] add esi,ebx ;;add to base mov al,data[esi] ;;read the packet command byte sub esi,ebx ;;restore COMBUF pointer pop ebx and al,07fh ;;remove the MSB xlatb ;;convert packet command to length xor ah,ah ;;convert packet length to unsigned word inc ax ;;include the command byte sub count[esi],ax ;;subtract from count add ax,tail[esi] ;;move the tail and ax,COMBUFSIZE-1 ;;limit it mov tail[esi],ax ;;save it mov tempTail[esi],ax ;;set the new tempTail index mov tempOut[esi],0 ;;clear the 'tempOut' count also sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_SPACE_IN macro ; Returns input bytes available in COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ; reg = register to use ;------------------------------------------------ ; Side effects: ; ax contains number of bytes available ;------------------------------------------------ COM_SPACE_IN MACRO name,reg add esi,name ;;build COMBUF pointer in ecx mov reg,COMBUFSIZE sub reg,count[esi] sub reg,tempIn[esi] sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; COM_SPACE_OUT macro ; Returns output bytes available in COMBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing COMBUF ; name = offset to COMBUF within object ;------------------------------------------------ ; Side effects: ; ax contains number of bytes available ;------------------------------------------------ COM_SPACE_OUT MACRO name add esi,name ;;build COMBUF pointer in ecx mov ax,count[esi] sub ax,tempOut[esi] sub esi,name ;;restore esi ENDM ;------------------------------------------------------------------------- ; IRQ data equates ;------------------------------------------------------------------------- IRQDATA STRUC irqHandler dd ? irqThis dd ? irqOldOffset dd ? ;THE ORDER OF THESE TWO IS CRITICAL. irqOldSelector dw ? ;See the interrupt handlers near the end. irqPort dw ? irqNum db ? irqPICbit db ? irqOldPICbits db ? irqPadByte db ? IRQDATA ENDS ;------------------------------------------------------------------------- ; PCSPAK object equates ; ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ; Any modifications to this structure MUST be ; reflected in PCSPAK.hh!! ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ;------------------------------------------------------------------------- TX_IDLE_STATE EQU 0 TX_RESTART_STATE EQU 1 TX_BODY_STATE EQU 2 TX_WAIT_STATE EQU 3 RX_IDLE_STATE EQU 0 RX_ACTIVE_STATE EQU 1 ; ; Lower eight bits reserved for UART status ; ERR_INIT EQU 0000000100000000b TX_EARLY_ERR EQU 0000001000000000b TX_ABANDON EQU 0000010000000000b TX_RESTART EQU 0000100000000000b TX_NAK EQU 0001000000000000b RX_SEQ_ERR EQU 0010000000000000b RX_BAD_CHECKSUM EQU 0100000000000000b RX_COM_ERR EQU 1000000000000000b TXMAXRESET EQU 3 TXMAXERROR EQU 3 TXMAXIDLE EQU 4 ; 080h...0FBh ;command codes MAX_COMMAND_CHAR EQU 0FBh ;largest command code allowed ACK_CHAR EQU 0FCh NAK_CHAR EQU 0FDh RESTART_CHAR EQU 0FEh IDLE_CHAR EQU 0FFh RX_ACK_BIT EQU 00000001b RX_NAK_BIT EQU 00000010b PCSPAK STRUC portBase dw ? ;base port address ErrorValue dw ? enabled db ? ;set if rx/tx enabled txHoldOff db ? txReply db ? txState db ? txCount db ? txResetCount db ? txErrorCount db ? txIdleCount db ? txChecksum db ? txMaximum db ? rxFlags db ? rxState db ? rxCount db ? rxChecksum db ? rxMaximum db ? txTablePtr dd ? rxTablePtr dd ? intDataPtr dd ? ;pointer to IRQDATA structure txBuf COMBUF <> rxBuf COMBUF <> PCSPAK ENDS HISTORYSIZE equ 256 IRQ_EVENT equ 0 IRQ_EVENT_RX equ 1 IRQ_EVENT_TX equ 2 IRQ_EVENT_MSR equ 3 IRQ_EVENT_LSR equ 4 IRQ_EVENT_BOGUS equ 5 TX_EVENT_RESTART equ 10 TX_EVENT_ABANDON equ 11 TX_EVENT_DONE equ 12 TX_EVENT_ACK equ 13 TX_EVENT_NAK equ 14 TX_EVENT_EARLY equ 15 TX_EVENT_EMPTY equ 16 TX_EVENT_KICK equ 17 RX_EVENT_FULL equ 20 RX_EVENT_NOT_CMD equ 21 RX_EVENT_FULLBODY equ 22 RX_EVENT_NAK equ 23 RX_EVENT_CKSMERR equ 24 RX_EVENT_OK equ 25 if KEEP_HISTORY SAVE_TX MACRO LOCAL skip push ax push ebx mov ebx,historyIndex cmp ebx,HISTORYSIZE jge skip mov ah,01h mov history[ebx],ax add ebx,2 mov historyIndex,ebx inc pcspakCharactersSent ;;keep a running count skip: pop ebx pop ax ENDM SAVE_RX MACRO LOCAL skip push ax push ebx mov ebx,historyIndex cmp ebx,HISTORYSIZE jge skip mov ah,02h mov history[ebx],ax add ebx,2 mov historyIndex,ebx skip: pop ebx pop ax ENDM SAVE_EVENT MACRO marker LOCAL skip push ax push ebx mov ebx,historyIndex cmp ebx,HISTORYSIZE jge skip mov ah,03h mov al,marker mov history[ebx],ax add ebx,2 mov historyIndex,ebx skip: pop ebx pop ax ENDM else SAVE_TX MACRO inc pcspakCharactersSent ;;keep a running count ENDM SAVE_RX MACRO ENDM SAVE_EVENT MACRO marker ENDM endif ;=========================================================================== ; Data segment ;=========================================================================== BEGIN_DATA data_selector dw ? irq_com1 IRQDATA irq_com2 IRQDATA irq_com3 IRQDATA irq_com4 IRQDATA public historyIndex historyIndex dd 0 public history if KEEP_HISTORY history dw HISTORYSIZE dup(0) else history dw 0 endif public iThinkIRQIsOn iThinkIRQIsOn dd 0 public pcspakCharactersSent pcspakCharactersSent dd 0 public previousIMR,previousISR,previousIRR previousIMR db 0 previousISR db 0 previousIRR db 0 public middleIMR,middleISR,middleIRR middleIMR db 0 middleISR db 0 middleIRR db 0 public postIMR,postISR,postIRR postIMR db 0 postISR db 0 postIRR db 0 public pcspakLSR,pcspakIER,pcspakIIR pcspakLSR db 0 pcspakIER db 0 pcspakIIR db 0 global_serial_port dw 0 END_DATA ;=========================================================================== ; Code segment ;=========================================================================== BEGIN_CODE ;------------------------------------------------------------------------- ; PCSPAKInit() ; Constructor for serial packet driver interface ;------------------------------------------------------------------------- ; Enter: ; init_this = pointer to object ; init_port = address of serial port ; init_irq_num = interrupt number ; init_rx_len = pointer to (byte) receive length table ; init_tx_len = pointer to (byte) transmit length table ;------------------------------------------------------------------------- ; Returns: ; 1 if ok, ; -1 if IRQ's all used, ; -2 if DPMI error, ; -3 if really weird bogus error, ; else IIR value ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKInit ARG_DPTR init_this ARG_WORD init_rate ARG_WORD init_port ARG_WORD init_irq_num ARG_DPTR init_rx_len ARG_WORD init_rx_count ARG_DPTR init_tx_len ARG_WORD init_tx_count LOCAL_DWORD return_status BUILD_STACK_FRAME mov return_status,0 ;everything's OK ;-------------------------------------------------------------- ; Save off the data selector so we can get it back later. ;-------------------------------------------------------------- ; ...In flat mode, CS and DS overlap, but CS is write-protected ; and DS isn't. Therefore we can write DS to [data_selector] ; and read it back from CS later. ;-------------------------------------------------------------- mov [data_selector],ds ;-------------------------------------------------------------- ; Disable interrupts ;-------------------------------------------------------------- DPMI_DISABLE_SAVE_INT ;pushes ax! IRQ_WATCH 0 ;-------------------------------------------------------------- ; Point ESI at object data ;-------------------------------------------------------------- mov esi,init_this ;-------------------------------------------------------------- ; Clear the data buffers ;-------------------------------------------------------------- COM_INIT rxBuf ;assumes esi points to object COM_INIT txBuf ;assumes esi points to object ;-------------------------------------------------------------- ; Initialize values ;-------------------------------------------------------------- xor eax,eax mov enabled[esi],al ;clear 'enabled' flag mov txHoldOff[esi],al ;flag as not held off mov txReply[esi],al ;clear 'reply' char mov rxFlags[esi],al ;clear receive flags (ack/nak) mov ErrorValue[esi],ax ;clear errors mov intDataPtr[esi],eax ;(flag as not allocated) mov historyIndex,eax mov al,TX_IDLE_STATE ;initialize to idle state mov txState[esi],al mov al,RX_IDLE_STATE ;initialize to idle state mov rxState[esi],al mov ax,init_port ;get the port address mov portBase[esi],ax ;save it mov eax,init_tx_len ;set dialect transmit table pointer mov txTablePtr[esi],eax mov ax,init_tx_count ;set maximum dialect transmit index mov txMaximum[esi],al mov eax,init_rx_len ;set dialect receive table pointer mov rxTablePtr[esi],eax mov ax,init_rx_count ;set maximum dialect receive index mov rxMaximum[esi],al ;-------------------------------------------------------------- ; Find unused IRQDATA structure ;-------------------------------------------------------------- mov ecx,4 mov edi,OFFSET irq_com1 searchLoop: mov ax,irqPort[edi] ;is irqPort equal to zero? je short searchDone ;yes, use this structure add edi,SIZE IRQDATA ;no, move to next one loop searchLoop mov ErrorValue[esi],ERR_INIT ;allocation failed, set errors! mov return_status,-1 ;return an error code jmp allocFailed searchDone: mov ax,init_port ;save port address mov irqPort[edi],ax mov global_serial_port,ax ;save in case of crash mov ax,init_irq_num ;save interrupt number mov irqNum[edi],al ;-------------------------------------------------------------- ; Save init_this in irq structure, irqPtr in "this" structure ;-------------------------------------------------------------- mov irqThis[edi],esi mov intDataPtr[esi],edi ;-------------------------------------------------------------- ; Save the old PROTECTED MODE interrupt vector ;-------------------------------------------------------------- mov bl,irqNum[edi] ;bl has interrupt number DPMI_GET_PROT_VECTOR ;returns cx:edx mov irqOldSelector[edi],cx mov irqOldOffset[edi],edx ;-------------------------------------------------------------- ; Set the new PROTECTED MODE interrupt vector ;-------------------------------------------------------------- mov bx,init_irq_num ;bl has interrupt number mov cx,cs ;cx:edx has selector:offset mov edx,irqHandler[edi] DPMI_SET_PROT_VECTOR jnc short allocOk ;carry clear, allocation succeeded mov ErrorValue[esi],ERR_INIT ;allocation failed, set errors! mov return_status,-2 ;return an error code jmp allocFailed allocOk: ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Diagnostics values ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PIC_IMR_TO_AL mov previousIMR,al PIC_ISR_TO_AL mov previousISR,al PIC_IRR_TO_AL mov previousIRR,al ;-------------------------------------------------------------- ; Initialize PIC ;-------------------------------------------------------------- ;----------------------------- ; Get master PIC data: ; enable bit in bl, ; interrupt number in bh ;----------------------------- mov ax,init_irq_num ;get interrupt number cmp ax,0000Ch ;COM1? jnz short mustBeCom2 ;no, must be COM2 mov bl,PIC_COM1COM3 ;yes, get COM1, COM3 enable... mov bh,OCW2_SPEC_EOI+4 ;...and PIC specific EOI jmp short hasPicBits mustBeCom2: mov bl,PIC_COM2COM4 ;get COM2, COM4 enable... mov bh,OCW2_SPEC_EOI+3 ;...and PIC specific EOI hasPicBits: mov irqPICbit[edi],bl ;save for termination ;----------------------------- ; Enable interrupts in PIC ;----------------------------- ASSERT_PIC_REG bl ;----------------------------- ; Make sure PIC interrupt ; request is cleared ;----------------------------- mov al,bh ;output the specific EOI out PIC_MASTER_0,al ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Diagnostics values ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PIC_IMR_TO_AL mov middleIMR,al PIC_ISR_TO_AL mov middleISR,al PIC_IRR_TO_AL mov middleIRR,al ;-------------------------------------------------------------- ; Initialize UART ;-------------------------------------------------------------- mov dx,init_port ;get port address from stack ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Diagnostics values ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ add dx,UART_IIR in al,dx mov pcspakIIR,al sub dx,UART_IIR ;----------------------------- ; Disable UART interrupts ;----------------------------- RETRACT_MCR UART_MCR_IRQ ;This clears irq into PIC ;----------------------------- ; Disable all UART interrupt ; sources. Not mandatory, ; but a safe practice. ;----------------------------- RETRACT_IER UART_IER_ALL ;----------------------------- ; Set baud rate ; Side effect: the divisor ; latch access bit is cleared ;----------------------------- mov cx,init_rate ;set data rate SETBAUD_CX ;macro from PCSERIAL.INC ;----------------------------- ; Set data format: ; 8 data bits, ; 1 stop bit, ; no parity ; Side effect: the divisor ; latch access bit is cleared ;----------------------------- mov al,UART_LCR_8+UART_LCR_S1+UART_LCR_NP SETFORMAT_AL ;----------------------------- ; Enable UART interrupt sources ;----------------------------- ASSERT_IER UART_IER_ALL ;----------------------------- ; Enable UART interrupts, RTS ;----------------------------- ASSERT_MCR UART_MCR_IRQ+UART_MCR_RTS ;----------------------------- ; Clear UART interrupt sources ;----------------------------- in al,dx ;clear receiver interrupt (read inputs) in al,dx ;; mov al,IDLE_CHAR ; clear transmit interrupt ;; out dx,al ;; out dx,al add dx,UART_MSR ;read modem status reg to clear IRQ in al,dx ;discard the result sub dx,UART_MSR add dx,UART_LSR ;read line status reg to clear IRQ in al,dx ;read value mov pcspakLSR,al ;(save for diagnostics) sub dx,UART_LSR ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Diagnostics values ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ add dx,UART_IER in al,dx mov pcspakIER,al sub dx,UART_IER PIC_IMR_TO_AL mov postIMR,al PIC_ISR_TO_AL mov postISR,al PIC_IRR_TO_AL mov postIRR,al ;-------------------------------------------------------------- ; Mark as 'enabled' ;-------------------------------------------------------------- mov enabled[esi],1 allocFailed: ;-------------------------------------------------------------- ; Reenable the old interrupt state and return ;-------------------------------------------------------------- DPMI_RESTORE_PREV_INT ;pops ax! IRQ_WATCH 1 mov eax,return_status END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKTerm() ; Destructor for serial packet interface ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; (nothing) ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKTerm ARG_DPTR term_this BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Disable interrupts ;-------------------------------------------------------------- DPMI_DISABLE_SAVE_INT ;pushes ax! IRQ_WATCH 0 ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,term_this mov eax,esi ;is it NULL? or eax,eax jz short skipRestore ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Turn off RTS and interrupts ;-------------------------------------------------------------- mov dx,portBase[esi] RETRACT_MCR UART_MCR_IRQ+UART_MCR_RTS ; NOTE: from this point on, interrupts CANNOT occur from the UART. ;-------------------------------------------------------------- ; If improperly allocated, skip 'release' ;-------------------------------------------------------------- mov eax,intDataPtr[esi] ;get pointer to IRQ data or eax,eax jz short skipRestore ;null pointer, skip release mov edi,eax ;place pointer in edi ;-------------------------------------------------------------- ; Mark IRQDATA record as unused ;-------------------------------------------------------------- mov irqPort[edi],0 ;-------------------------------------------------------------- ; Restore the old PROTECTED MODE interrupt vector ;-------------------------------------------------------------- mov bl,irqNum[edi] ;bl has interrupt number mov cx,irqOldSelector[edi] ;cx:edx has selector:offset mov edx,irqOldOffset[edi] DPMI_SET_PROT_VECTOR ;-------------------------------------------------------------- ; Disable PIC channel ;-------------------------------------------------------------- mov bl,irqPICbit[edi] ;get our PIC bit RETRACT_PIC bl ;no, turn it off skipRestore: ;-------------------------------------------------------------- ; Reenable the old interrupt state and return ;-------------------------------------------------------------- DPMI_RESTORE_PREV_INT ;pops ax! IRQ_WATCH 1 END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKReceive() ; Returns packet if available ;------------------------------------------------------------------------- ; Enter: ; rp_this = pointer to object ; Byte *destPtr = pointer to 128-byte destination buffer ;------------------------------------------------------------------------- ; Returns: ; int packetSize (or zero if no packet available) ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKReceive ARG_DPTR rp_this ARG_DPTR rp_dest BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,rp_this mov eax,esi ;is it NULL? or eax,eax jz spr_done ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Skip if disabled ;-------------------------------------------------------------- mov al,enabled[esi] ;if disabled, don't even try or al,al jnz short recvOK xor eax,eax jmp spr_done recvOK: ;-------------------------------------------------------------- ; Check character count: non-zero indicates entire packet ;-------------------------------------------------------------- xor eax,eax ;clear top of eax mov ax,rxBuf.count[esi] ;get # of characters in ax or ax,ax ;empty? jz spr_done ;yes, return zero ;;; ;-------------------------------------------------------------- ;;; ; Disable interrupts ;;; ;-------------------------------------------------------------- ;;; DPMI_DISABLE_INT ;;; IRQ_WATCH 0 ;-------------------------------------------------------------- ; Get buffer length ;-------------------------------------------------------------- mov edi,rp_dest ;get pointer to destination cld ;make sure direction is ok xor eax,eax ;clear top of ax COM_GET_AL rxBuf ;get character into al ;-------------------------------------------------------------- ; Verify first char is a valid command character ;-------------------------------------------------------------- test al,080h ;1st character = command (high bit set)? jnz short sprOk DISABLE_AND_DIE 9 xor eax,eax jmp short spr_done sprOk: stosb ;save it and al,07Fh ;remove MSB mov ebx,rxTablePtr[esi] ;point bx at length table xlatb ;translate into length xor ecx,ecx ;set ecx from al mov cl,al mov edx,ecx ;save in edx inc edx ;return at least one character ;-------------------------------------------------------------- ; Transfer data from buffer ;-------------------------------------------------------------- or cx,cx ;if no additional characters, skip jz short spr_skip spr_loop: COM_GET_AL rxBuf ;get character into al stosb ;save in buffer loop spr_loop spr_skip: COM_ACCEPT_OUT rxBuf ;accept all 'taken' characters ;;; ;-------------------------------------------------------------- ;;; ; Reenable the old interrupt state ;;; ;-------------------------------------------------------------- ;;; DPMI_RESTORE_PREV_INT ;pops ax! ;;; IRQ_WATCH 1 mov eax,edx ;place count in eax ;-------------------------------------------------------------- ; Return count in eax ;-------------------------------------------------------------- spr_done: END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKSend() ; Transmits packet ;------------------------------------------------------------------------- ; Enter: ; sp_this = pointer to object ; Byte *sp_src = pointer to (up to) 128-byte source buffer ;------------------------------------------------------------------------- ; Returns: ; void ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKSend ARG_DPTR sp_this ARG_DPTR sp_src BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,sp_this mov eax,esi ;is it NULL? or eax,eax jz sendSkip ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Wait for sufficient room ;-------------------------------------------------------------- mov al,enabled[esi] ;if disabled, don't even try or al,al jz sendSkip ;;; SET_LPT 5 mov edi,sp_src ;get pointer to source mov al,[edi] ;get the command byte and al,07Fh ;remove MSB mov ebx,txTablePtr[esi] ;set ebx to point to length table xlatb ;get the length in al xor ecx,ecx ;move al to ecx mov cl,al inc cx ;add one for command byte spt_wait: COM_SPACE_IN txBuf,ax ;get buffer 'in' space cmp ax,cx ;enough room? jae spt_space ;yes, continue DISABLE_AND_DIE 0 jmp short spt_wait spt_space: ;-------------------------------------------------------------- ; Disable interrupts ;-------------------------------------------------------------- DPMI_DISABLE_SAVE_INT ;pushes ax! IRQ_WATCH 0 ;-------------------------------------------------------------- ; Copy data into tx buffer ;-------------------------------------------------------------- spt_loop: mov al,[edi] ;get the byte inc edi ;increment the source pointer COM_PUT_AL txBuf ;place it in the buffer loop spt_loop COM_ACCEPT_IN txBuf ;accept all 'placed' characters mov dx,portBase[esi] ;set dx to port base address call StartSending ;attempt to start tx interrupt ;-------------------------------------------------------------- ; Reenable the old interrupt state ;-------------------------------------------------------------- DPMI_RESTORE_PREV_INT ;pops ax! IRQ_WATCH 1 ;;; CLEAR_LPT 5 sendSkip: END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKActive() ; Returns transmitter 'active' state (=0 when all transmission is done). ;------------------------------------------------------------------------- ; Enter: ; pa_this = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int errors ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKActive ARG_DPTR pa_this BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,pa_this mov eax,esi ;is it NULL? or eax,eax jz short actNULL ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Get 'active' status ; ; The LSR register has two bits reflecting the ; transmitter state: ; THR = transmit holding register (1=empty) ; TSR = transmit serial register (1=empty) ; ; The transmission is completely finished when both of these ; bits are high. ;-------------------------------------------------------------- xor eax,eax ;clear top of eax mov dx,portBase[esi] ;get the port base add dx,UART_LSR ;get LSR in al,dx ;;sub dx,UART_LSR ;we usually subtract, no need here and al,UART_LSR_THR+UART_LSR_TSR ; remove all but THR,TSR xor al,UART_LSR_THR+UART_LSR_TSR ; invert the bits actNULL: END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKErrors() ; Returns error bits: zero indicates no errors. ; The error code is cleared by this call as well. ;------------------------------------------------------------------------- ; Enter: ; pe_this = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int errors ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKErrors ARG_DPTR pe_this BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,pe_this mov eax,esi ;is it NULL? or eax,eax jz short errNULL ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Get, clear error word ;-------------------------------------------------------------- mov ax,ErrorValue[esi] ;init error? and ax,ERR_INIT jnz short haveInitError ;yes, don't clear it xor ax,ax ;no, go ahead and clear it xchg ax,ErrorValue[esi] haveInitError: and eax,0FFFFh ;make sure top of eax is clear errNULL: END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKIRQState() ; Returns IRQ state ;------------------------------------------------------------------------- ; Enter: ; (void) ;------------------------------------------------------------------------- ; Returns: ; int state ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKIRQState BUILD_STACK_FRAME DPMI_INT_STATE and eax,0FFh ;make sure top of eax is clear END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKTestInstance() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int True ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKTestInstance mov eax,1 ret END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKIsSignatureBad() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int False ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKIsSignatureBad mov eax,0 ret END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKReceiveCount() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int number of items in receive queue ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKReceiveCount ARG_DPTR prc_this BUILD_STACK_FRAME mov esi,prc_this xor eax,eax mov ax,rxBuf.count[esi] END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKTransmitCount() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int number of items in transmit queue ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKTransmitCount ARG_DPTR ptc_this BUILD_STACK_FRAME mov esi,ptc_this xor eax,eax mov ax,txBuf.count[esi] END_C_PROC ;------------------------------------------------------------------------- ; PCSPAKSetDTR() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ; on = zero to retract DTR, non-zero to assert DTR ;------------------------------------------------------------------------- ; Returns: ; int number of items in transmit queue ;------------------------------------------------------------------------- BEGIN_C_PROC PCSPAKSetDTR ARG_DPTR sdtr_this ARG_DWORD sdtr_on BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,sdtr_this mov eax,esi ;is it NULL? or eax,eax jz short sdtrExit ;OHMIGOSH!! EXIT!!! mov dx,portBase[esi] ;get the port base ;-------------------------------------------------------------- ; Get "on" ;-------------------------------------------------------------- cmp sdtr_on,0 je sdtrOff ;-------------------------------------------------------------- ; Assert DTR ;-------------------------------------------------------------- ASSERT_MCR UART_MCR_DTR jmp short sdtrExit ;-------------------------------------------------------------- ; Retract DTR ;-------------------------------------------------------------- sdtrOff: RETRACT_MCR UART_MCR_DTR sdtrExit: END_C_PROC ;------------------------------------------------------------------------- ; StartSending - attempt to start transmitter (if allowed) ; ...called from the receive interrupt routine and PCSPAK::sendPacket. ;------------------------------------------------------------------------- ; Enter: ; ESI = pointer to PCSPAK structure ; DX = UART base address ;------------------------------------------------------------------------- ; Returns: ; (void) ;------------------------------------------------------------------------- ; Side effects: ; Modifies register AL ;------------------------------------------------------------------------- StartSending PROC NOLANGUAGE NEAR add dx,UART_LSR ;get THR bit from LSR in al,dx sub dx,UART_LSR test al,UART_LSR_THR ;is there anything in the holding reg? jz sptNoKick ;yes, interrupt will occur later SAVE_EVENT TX_EVENT_KICK call SendChar ;restart transmitter sptNoKick: ret StartSending ENDP ;------------------------------------------------------------------------- ; SendChar - send character through UART ; ...called from the interrupt routine and PCSPAK::sendCommand. ;------------------------------------------------------------------------- ; Enter: ; ESI = pointer to PCSPAK structure ; DX = UART base address ;------------------------------------------------------------------------- ; Returns: ; (void) ;------------------------------------------------------------------------- ; Side effects: ; registers EAX, EBX, ECX modified ;------------------------------------------------------------------------- SendChar PROC NOLANGUAGE NEAR ;;; SET_LPT 6 ;-------------------------------------------------------------- ; Send reply character if one is waiting ;-------------------------------------------------------------- cmp txReply[esi],0 ;"reply" character waiting? je noReply ;no, continue xor al,al ;prepare for exchange xchg al,txReply[esi] ;get and clear the "reply" character out dx,al ;yes, send the reply character SAVE_TX jmp sendDone ;done for now. noReply: ;-------------------------------------------------------------- ; Enter the appropriate state handler ;-------------------------------------------------------------- mov al,txState[esi] ;get the transmit state cmp al,TX_IDLE_STATE ;idle state? je short txIdleState ;yes, go to it cmp al,TX_BODY_STATE ;"body" state? je txBodyState ;yes, go to it cmp al,TX_WAIT_STATE ;"wait" state? je txWaitState ;yes, go to it cmp al,TX_RESTART_STATE ;restart state? je short txRestartState ;yes, go to it DISABLE_AND_DIE 1 jmp txWaitState ;must be "wait" state ;-------------------------------------------------------------- ; Transmitter idle state ;-------------------------------------------------------------- txIdleState: cmp txHoldOff[esi],0 ;If transmitter 'held off', do nothing jne short txIsIdle cmp txBuf.count[esi],0 ;input buffer available? jne short txIdleAvail ;yes, send something txIsIdle: SAVE_EVENT TX_EVENT_EMPTY ;nothing to send, just return jmp sendDone txIdleAvail: ;--------------------------- ; Buffer ready, start ;--------------------------- mov txResetCount[esi],TXMAXRESET ;set the 'reset' counter mov txErrorCount[esi],TXMAXERROR ;set the 'error' counter ;...deliberately fall through into 'txRestartState' ;-------------------------------------------------------------- ; Transmitter restart state ;-------------------------------------------------------------- txRestartState: COM_GET_AL txBuf ;assumes esi points to object cmp al,07Fh ;is it a command? jbe short txRestartBad ;no, discard it mov ah,al ;transfer to ah and ah,07Fh ;remove MSB cmp ah,txMaximum[esi] ;legitimate command? jb short txRestartOk ;yes txRestartBad: DISABLE_AND_DIE 2 COM_ACCEPT_OUT txBuf ;eat the character mov txState[esi],TX_IDLE_STATE ;move to the "idle" state jmp sendDone txRestartOk: out dx,al ;transmit the command character mov txChecksum[esi],al ;begin a new checksum count SAVE_TX mov al,ah ;move AND'ed value back to al mov ebx,txTablePtr[esi] ;get length table pointer into ebx xlatb ;look up length inc al ;include the command byte mov txCount[esi],al ;save the length mov txState[esi],TX_BODY_STATE ;move to the "body" state jmp sendDone ;-------------------------------------------------------------- ; Transmitter "body" state ; Send message body, then checksum (unless interrupted by ACK/NAK) ;-------------------------------------------------------------- txBodyState: ;--------------------------- ; Interrupted by ACK/NAK? ;--------------------------- cmp rxFlags[esi],0 ;flag set? je txBodySend ;no, send normal body COM_RESTORE_OUT txBuf ;interrupted, reset 'out' pointer mov al,rxFlags[esi] ;get flags mov rxFlags[esi],0 ;clear them test al,RX_ACK_BIT ;was it an ACK? jne short txBodyNak ;no or ErrorValue[esi],TX_EARLY_ERR ;yes, set error flag txBodyNak: SAVE_EVENT TX_EVENT_EARLY ;--------------------------- ; Restart or die ;--------------------------- dec txResetCount[esi] ;check the restart count jl short txBodyGiveUp ;if too many errors, give up ;--------------------------- ; Attempt restart ;--------------------------- mov al,RESTART_CHAR ;no, send restart character out dx,al SAVE_TX mov txState[esi],TX_RESTART_STATE ;move to the "restart" state jmp sendDone ;--------------------------- ; Too many errors, die! ;--------------------------- txBodyGiveUp: SAVE_EVENT TX_EVENT_ABANDON or ErrorValue[esi],TX_ABANDON ;set error flag mov ebx,txTablePtr[esi] ;get pointer to length table COM_EAT_PACKET txBuf ;discard current packet jmp txIdleState ;start another packet ;--------------------------- ; Send body or checksum ;--------------------------- txBodySend: dec txCount[esi] ;is buffer done? jle txBodyCheck ;yes, send checksum ;--------------------------- ; Send a body character ;--------------------------- COM_GET_AL txBuf ;get a character from the buffer cmp al,07Fh ;legitimate body char? jbe txBodyOk ;yes, continue DISABLE_AND_DIE 3 and al,07Fh ;prevent bad data from becoming command txBodyOk: out dx,al ;send it add txChecksum[esi],al ;add to the checksum SAVE_TX jmp sendDone ;--------------------------- ; End of body, send checksum ;--------------------------- txBodyCheck: mov al,txChecksum[esi] ;get the checksum and al,07Fh ;limit it out dx,al ;send it SAVE_TX SAVE_EVENT TX_EVENT_DONE ;--------------------------- ; prepare for "wait" state ;--------------------------- mov txIdleCount[esi],TXMAXIDLE ;set the idle counter mov txState[esi],TX_WAIT_STATE ;move to the "wait" state jmp sendDone ;-------------------------------------------------------------- ; Transmitter "wait" state ; Waits for reply from other end ;-------------------------------------------------------------- txWaitState: xor al,al ;prepare to exchange flags xchg al,rxFlags[esi] ;get flags, also clear them or al,al ;flags clear? jz txWaitMore ;yes, keep waiting test al,RX_ACK_BIT ;ACK? jz short txWaitNak ;no, process NAK ;--------------------------- ; ACK, buffer received OK ;--------------------------- SAVE_EVENT TX_EVENT_ACK COM_ACCEPT_OUT txBuf ;yes, good transmission. Discard packet. mov txState[esi],TX_IDLE_STATE ;move to the "idle" state jmp txIdleState ;start another buffer ;--------------------------- ; NAK! ;--------------------------- txWaitNak: SAVE_EVENT TX_EVENT_ACK or ErrorValue[esi],TX_NAK ;set error flag COM_RESTORE_OUT txBuf ;restore output pointers dec txErrorCount[esi] ;check the error count jl txWaitGiveUp ;if too many errors, give up jmp txRestartState ;no, resend it ;--------------------------- ; Keep waiting ;--------------------------- txWaitMore: dec txIdleCount[esi] ;have we waited too long? jl short txWaitDone ;yes, attempt to restart mov al,IDLE_CHAR ;no, send idle character out dx,al SAVE_TX jmp sendDone ;--------------------------- ; Attempt restart ;--------------------------- txWaitDone: SAVE_EVENT TX_EVENT_RESTART or ErrorValue[esi],TX_RESTART ;set error flag COM_RESTORE_OUT txBuf ;restore output pointers dec txResetCount[esi] ;too many restarts? jl short txWaitGiveUp ;yes, abandon buffer mov al,RESTART_CHAR ;no, send restart character out dx,al SAVE_TX mov txIdleCount[esi],TXMAXIDLE ;reset the idle counter mov txState[esi],TX_RESTART_STATE ;move to the "idle" state jmp short sendDone ;--------------------------- ; Give up ;--------------------------- txWaitGiveUp: SAVE_EVENT TX_EVENT_ABANDON or ErrorValue[esi],TX_ABANDON ;set error flag mov ebx,txTablePtr[esi] ;get pointer to length table COM_EAT_PACKET txBuf ;discard the packet mov txState[esi],TX_IDLE_STATE ;move to the "idle" state jmp txIdleState ;start another packet sendDone: ;;; CLEAR_LPT 6 ret SendChar ENDP ;------------------------------------------------------------------------- ; RecvChar - receive character from UART ;------------------------------------------------------------------------- ; Enter: ; ESI = pointer to PCSPAK structure ; DX = UART base address ; AL = character ;------------------------------------------------------------------------- ; Returns: ; (void) ;------------------------------------------------------------------------- ; Side effects: ; registers EAX, EBX, ECX modified ;------------------------------------------------------------------------- RecvChar PROC NOLANGUAGE NEAR SAVE_RX ;-------------------------------------------------------------- ; Process universal special characters: ACK, NAK, IDLE ;-------------------------------------------------------------- test al,080h ;MSB set? jz short rcNotSpecial ;no, can't be special character ;------------------------------ ; IDLE ;------------------------------ cmp al,IDLE_CHAR ;IDLE character? jne short rcNotIdle ;no jmp recvDone ;yes, ignore it rcNotIdle: ;------------------------------ ; ACK ;------------------------------ cmp al,ACK_CHAR ;ACK character? jne short rcNotAck ;no or rxFlags[esi],RX_ACK_BIT ;yes, flag for transmitter jmp recvDone rcNotAck: ;------------------------------ ; NAK ;------------------------------ cmp al,NAK_CHAR ;NAK character? jne short rcNotNak ;no or rxFlags[esi],RX_NAK_BIT ;yes, flag for transmitter jmp recvDone rcNotNak: rcNotSpecial: ;-------------------------------------------------------------- ; Process state-dependent characters ;-------------------------------------------------------------- cmp rxState[esi],0 ;test the state jne rxActive ;non-zero, so active ;-------------------------------------------------------------- ; Receiver "idle" state ; Wait for command character ;-------------------------------------------------------------- test al,080h ;is it a control character? jnz short rxIdleSpecial ;yes, process it or ErrorValue[esi],RX_SEQ_ERR ;Not expecting data, so error. jmp recvDone rxIdleSpecial: cmp al,RESTART_CHAR ;restart? je recvDone ;yes, discard it COM_SPACE_IN rxBuf,bx ;get remaining space or bx,bx ;zero? jnz short rxIdleSpace ;no, continue SAVE_EVENT RX_EVENT_FULL DISABLE_AND_DIE 4 or ErrorValue[esi],RX_COM_ERR ;error. Set flag. jmp recvDone rxIdleSpace: mov ah,al ;move to ah for testing and ah,07Fh ;remove MSB cmp ah,rxMaximum[esi] ;legitimate command? jb short rxIdleOk ;yes, continue SAVE_EVENT RX_EVENT_NOT_CMD DISABLE_AND_DIE 5 or ErrorValue[esi],RX_COM_ERR ;no. jmp recvDone rxIdleOk: COM_PUT_AL rxBuf ;save command character in buffer mov rxChecksum[esi],al ;start a new receive checksum mov al,ah ;put ah back into al mov ebx,rxTablePtr[esi] ;get length table pointer into ebx xlatb ;look up length inc al ;include the command byte mov rxCount[esi],al ;save the length mov rxState[esi],RX_ACTIVE_STATE ;change to "active" state jmp recvDone ;-------------------------------------------------------------- ; Receiver "active" state ; Place N characters into buffer, test checksum, and reply ;-------------------------------------------------------------- rxActive: test al,080h ;control character? jz short rxActiveData ;no, therefore data or ErrorValue[esi],RX_SEQ_ERR ;yes, error. Set error flag COM_RESTORE_IN rxBuf ;Discard received data ;------------------------------ ; Process control char ;------------------------------ cmp al,RESTART_CHAR ;restart character? je rxActiveAbandon ;yes, abandon received data SAVE_EVENT RX_EVENT_NAK mov al,NAK_CHAR ;error, tell transmitter to send NAK jmp rxActiveReply ;------------------------------ ; Process data ;------------------------------ rxActiveData: dec rxCount[esi] ;check the receive count jle rxActiveCheck ;if end of message, process checksum ;------------------------------ ; Enough space in buffer? ;------------------------------ COM_SPACE_IN rxBuf,bx ;get remaining space or bx,bx ;zero? jnz short rxActiveSpace ;no, continue SAVE_EVENT RX_EVENT_FULLBODY DISABLE_AND_DIE 6 ;crash hard (if enabled) or ErrorValue[esi],RX_COM_ERR ;error. Set flag. COM_RESTORE_IN rxBuf ;Discard received data jmp rxActiveAbandon ;give up on packet rxActiveSpace: ;------------------------------ ; Save data in buffer ;------------------------------ add rxChecksum[esi],al ;update the checksum COM_PUT_AL rxBuf ;save the character jmp recvDone ;------------------------------ ; Test checksum ;------------------------------ rxActiveCheck: mov bl,rxChecksum[esi] ;get the checksum and bl,07Fh ;remove MSB cmp al,bl ;checksum OK? je short rxActiveOk ;yes! ;------------------------------ ; Buffer error ;------------------------------ SAVE_EVENT RX_EVENT_CKSMERR or ErrorValue[esi],RX_BAD_CHECKSUM ;error. Set flag. COM_RESTORE_IN rxBuf ;Discard received data mov al,NAK_CHAR ;tell transmitter to send NAK jmp short rxActiveReply ;------------------------------ ; Buffer OK ;------------------------------ rxActiveOk: SAVE_EVENT RX_EVENT_OK COM_ACCEPT_IN rxBuf ;accept the received data mov al,ACK_CHAR ;tell transmitter to send ACK rxActiveReply: mov txReply[esi],al call StartSending ;restart transmitter if needed ;------------------------------ ; Return to inactive state ;------------------------------ rxActiveAbandon: mov rxState[esi],RX_IDLE_STATE recvDone: ret RecvChar ENDP ;------------------------------------------------------------------------- ; Interrupt handlers ;------------------------------------------------------------------------- IntHandler PROC NOLANGUAGE NEAR ASSUME cs:_TEXT IntCom1: pushad mov edi,OFFSET irq_com1 ;point EDI at IRQDATA structure call IntMain popad iret ;jmp cs:fword ptr [irq_com1.irqOldOffset] IntCom2: pushad mov edi,OFFSET irq_com2 ;point EDI at IRQDATA structure call IntMain popad iret ;jmp cs:fword ptr [irq_com2.irqOldOffset] IntCom3: pushad mov edi,OFFSET irq_com3 ;point EDI at IRQDATA structure call IntMain popad iret ;jmp cs:fword ptr [irq_com3.irqOldOffset] IntCom4: pushad mov edi,OFFSET irq_com4 ;point EDI at IRQDATA structure call IntMain popad iret ;jmp cs:fword ptr [irq_com4.irqOldOffset] ;-------------------------------------------------------------- ; Serial interrupt handler main body ;-------------------------------------------------------------- IntMain: push ds ;save registers push es mov ds,cs:[data_selector] ;get our DS ASSUME ds:_DATA IRQ_WATCH_ENTRY ;;; SPKR_SET ;********************************************* SAVE_EVENT IRQ_EVENT ;-------------------------------------------------------------- ; Get pointer to PCSPAK structure, UART base address ;-------------------------------------------------------------- mov esi,irqThis[edi] mov dx,irqPort[edi] ;-------------------------------------------------------------- ; Get status in al ;-------------------------------------------------------------- procLoop: add dx,UART_IIR ;point to interrupt ident reg in al,dx ;get interrupt source ID sub dx,UART_IIR ;restore dx ;-------------------------------------------------------------- ; Receive character ;-------------------------------------------------------------- cmp al,UART_IIR_RDR ;receiver ready? jne short procNoRx ;no, skip it in al,dx ;get character call RecvChar ;process it jmp short procLoop procNoRx: ;-------------------------------------------------------------- ; Send character ;-------------------------------------------------------------- cmp al,UART_IIR_THR ;transmitter ready? jne short procNoTx ;no, skip it call SendChar ;send a character if available jmp short procLoop procNoTx: ;-------------------------------------------------------------- ; Process modem status register change ; ; We currently ignore it. We could someday implement ; DTR/CTS/... flow control here if we desire. ;-------------------------------------------------------------- cmp al,UART_IIR_MSR ;MSR change? jne short procNoMSR ;no, skip it SAVE_EVENT IRQ_EVENT_MSR add dx,UART_MSR ;read modem status reg to clear IRQ in al,dx ;discard the result sub dx,UART_MSR jmp procLoop procNoMSR: ;-------------------------------------------------------------- ; Process line status register change (error or break) ;-------------------------------------------------------------- cmp al,UART_IIR_LSR ;LSR change? jne short procNoLSR ;no, skip it SAVE_EVENT IRQ_EVENT_LSR add dx,UART_LSR ;read line status reg to clear IRQ in al,dx ;read value sub dx,UART_LSR and ax,UART_LSR_ERRS ;remove non-error bits or ErrorValue[esi],ax ;OR with current error state jmp procLoop procNoLSR: ;-------------------------------------------------------------- ; Check for UART done ;-------------------------------------------------------------- cmp al,001h ;if LSB is set, no interrupt je short procDone SAVE_EVENT IRQ_EVENT_BOGUS DISABLE_AND_DIE 7 ;weird state, flag error procDone: ;-------------------------------------------------------------- ; PIC port write ; (allows future interrupts to occur) ;-------------------------------------------------------------- PIC_RESET_MASTER ;-------------------------------------------------------------- ; Restore registers, return from SUBROUTINE (not interrupt!) ;-------------------------------------------------------------- IRQ_WATCH_EXIT ;;; SPKR_RESET ;********************************************* pop es pop ds ret IntHandler ENDP END_CODE END