;=========================================================================== ; File: L$SERIAL.asm ; Project: MUNGA ; Contents: PC serial 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 ;=========================================================================== THE_WAY_IT_WAS equ 0 include l4asm.inc include pcserial.inc include pcpic.inc include pcdpmi.inc ;=========================================================================== ; Equates ;=========================================================================== 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 L4SERIAL.hpp!! ; ; BE ESPECIALLY CAREFUL IF 'BUFSIZE' IS CHANGED! ; ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ;------------------------------------------------------------------------- SERIALBUFSIZE EQU 1024 ;buffer size (must be power of 2) SERIALBUF STRUC head dw 0 tail dw 0 count dw 0 data db SERIALBUFSIZE dup (?) SERIALBUF ENDS ;------------------------------------------------ ; SERBUFINIT macro ; Initializes SERIALBUF structure ;------------------------------------------------ ; Enter: ; esi = pointer to object containing SERIALBUF ; name = offset to SERIALBUF within object ;------------------------------------------------ ; Side effects: ; (none) ;------------------------------------------------ SERBUFINIT MACRO name ;enter with object pointer in esi add esi,name ;;build SERIALBUF pointer in esi mov head[esi],0 mov tail[esi],0 mov count[esi],0 sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; SERBUFPUT_AL macro ; Puts character in AL into SERIALBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing SERIALBUF ; name = offset to SERIALBUF within object ;------------------------------------------------ ; Side effects: ; ebx modified ;------------------------------------------------ SERBUFPUT_AL MACRO name add esi,name ;;build SERIALBUF pointer in esi xor ebx,ebx ;;get 'head' offset in ebx mov bx,head[esi] add esi,ebx ;;add to base mov data[esi],al ;;write the data sub esi,ebx ;;restore SERIALBUF pointer inc bx ;;bump 'head' and bx,SERIALBUFSIZE-1 ;;limit it mov head[esi],bx ;;save the new 'head' offset inc count[esi] ;;update the character count sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; SERBUFGET_AL macro ; Gets character into AL from SERIALBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing SERIALBUF ; name = offset to SERIALBUF within object ;------------------------------------------------ ; Side effects: ; ebx modified ;------------------------------------------------ SERBUFGET_AL MACRO name add esi,name ;;build SERIALBUF pointer in ecx xor ebx,ebx ;;get 'tail' offset in ebx mov bx,tail[esi] add esi,ebx ;;add to base mov al,data[esi] ;;read the data sub esi,ebx ;;restore SERIALBUF pointer inc bx ;;bump 'tail' and bx,SERIALBUFSIZE-1 ;;limit it mov tail[esi],bx ;;save the new 'tail' offset dec count[esi] ;;update the character count sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; SERBUFSPACE_IN macro ; Returns input bytes available in SERIALBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing SERIALBUF ; name = offset to SERIALBUF within object ; reg = register to use ;------------------------------------------------ ; Side effects: ; ax contains number of bytes available ;------------------------------------------------ SERBUFSPACE_IN MACRO name,reg add esi,name ;;build SERIALBUF pointer in ecx mov reg,SERIALBUFSIZE sub reg,count[esi] sub esi,name ;;restore esi ENDM ;------------------------------------------------ ; SERBUFSPACE_OUT macro ; Returns output bytes available in SERIALBUF ;------------------------------------------------ ; Enter: ; esi = pointer to object containing SERIALBUF ; name = offset to SERIALBUF within object ;------------------------------------------------ ; Side effects: ; ax contains number of bytes available ;------------------------------------------------ SERBUFSPACE_OUT MACRO name add esi,name ;;build SERIALBUF pointer in ecx mov ax,count[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 ;------------------------------------------------------------------------- ; PCSERIAL object equates ; ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ; Any modifications to this structure MUST be ; reflected in PCSERIAL.hpp!! ; WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING**WARNING ;------------------------------------------------------------------------- ; ; Lower eight bits reserved for UART status ; ERR_INIT EQU 0000000100000000b PCSERIAL STRUC portBase dw ? ;base port address ErrorValue dw ? enabled db ? ;set if rx/tx enabled txFlow db ? flowOverride db ? intDataPtr dd ? ;pointer to IRQDATA structure txBuf SERIALBUF <> rxBuf SERIALBUF <> PCSERIAL ENDS ;=========================================================================== ; Data segment ;=========================================================================== BEGIN_DATA data_selector dw ? irq_com1 IRQDATA irq_com2 IRQDATA irq_com3 IRQDATA irq_com4 IRQDATA public uartLSR,uartIER,uartIIR uartLSR db 0 uartIER db 0 uartIIR db 0 END_DATA ;=========================================================================== ; Code segment ;=========================================================================== BEGIN_CODE ;------------------------------------------------------------------------- ; PCSerialInit() ; Constructor for serial driver interface ;------------------------------------------------------------------------- ; Enter: ; init_this = pointer to object ; init_rate = data rate ; init_format = data format ; init_port = address of serial port ; init_irq_num = interrupt number ;------------------------------------------------------------------------- ; Returns: ; (nothing) ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialInit ARG_DPTR init_this ARG_WORD init_rate ARG_WORD init_format ARG_WORD init_port ARG_WORD init_irq_num BUILD_STACK_FRAME ;-------------------------------------------------------------- ; 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! ;-------------------------------------------------------------- ; Point ESI at object data ;-------------------------------------------------------------- mov esi,init_this ;-------------------------------------------------------------- ; Clear the data buffers ;-------------------------------------------------------------- SERBUFINIT rxBuf ;assumes esi points to object SERBUFINIT txBuf ;assumes esi points to object ;-------------------------------------------------------------- ; Initialize values ;-------------------------------------------------------------- xor eax,eax mov enabled[esi],al ;clear 'enabled' flag mov ErrorValue[esi],ax ;clear errors mov txFlow[esi],al ;flow control off mov flowOverride[esi],al ;...use flow control mov intDataPtr[esi],eax ;(flag as not allocated) mov ax,init_port ;get the port address mov portBase[esi],ax ;save it ;-------------------------------------------------------------- ; 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! jmp allocFailed searchDone: mov ax,init_port ;save port address mov irqPort[edi],ax 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! jmp allocFailed allocOk: ;-------------------------------------------------------------- ; Set baud rate ;-------------------------------------------------------------- mov dx,init_port ;get port address from stack mov cx,init_rate ;set data rate SETBAUD_CX ;macro from PCSERIAL.INC ;-------------------------------------------------------------- ; Set data format ;-------------------------------------------------------------- mov ax,init_format SETFORMAT_AL ;##################################################################################### IF THE_WAY_IT_WAS ;-------------------------------------------------------------- ; Clear interrupt sources, initialize flow control ;-------------------------------------------------------------- in al,dx ;read input, clear input irq add dx,UART_IIR ;point to interrupt ident reg in al,dx ;read IIR to clear tx interrupt in al,dx in al,dx sub dx,UART_IIR ;restore dx add dx,UART_MSR ;point to MSR in al,dx ;read modem status reg to clear IRQ sub dx,UART_MSR ;restore dx and al,UART_MSR_CTS ;save flow control bit (cts) mov txFlow[esi],al ;-------------------------------------------------------------- ; Enable interrupts in PIC ;-------------------------------------------------------------- PIC_IMR_TO_AL ;save old PIC bits for termination test mov irqOldPICbits[edi],al 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 jmp short hasPicBits mustBeCom2: mov bl,PIC_COM2COM4 ;get COM2, COM4 enable hasPicBits: mov irqPICbit[edi],bl ;save for termination ASSERT_PIC_REG bl ;-------------------------------------------------------------- ; Enable UART interrupts, RTS, and DTR ;-------------------------------------------------------------- ASSERT_IER UART_IER_RDR+UART_IER_THR+UART_IER_LSR+UART_IER_MSR ASSERT_MCR UART_MCR_IRQ+UART_MCR_RTS+UART_MCR_DTR ;##################################################################################### else ;-------------------------------------------------------------- ; 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 ;-------------------------------------------------------------- ; Initialize UART ;-------------------------------------------------------------- mov dx,init_port ;get port address from stack ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Diagnostics values ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ add dx,UART_IIR in al,dx mov uartIIR,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 ;----------------------------- ; Enable UART interrupt sources ;----------------------------- ASSERT_IER UART_IER_ALL ;----------------------------- ; Enable UART interrupts, RTS ;----------------------------- ASSERT_MCR UART_MCR_IRQ+UART_MCR_RTS+UART_MCR_DTR ;----------------------------- ; Clear UART interrupt sources ;----------------------------- in al,dx ;clear receiver interrupt (read inputs) in al,dx 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 uartLSR,al ;(save for diagnostics) sub dx,UART_LSR ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Diagnostics values ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ add dx,UART_IER in al,dx mov uartIER,al sub dx,UART_IER ;##################################################################################### endif ;-------------------------------------------------------------- ; Mark as 'enabled' ;-------------------------------------------------------------- mov enabled[esi],1 allocFailed: ;-------------------------------------------------------------- ; Reenable the old interrupt state and return ;-------------------------------------------------------------- DPMI_RESTORE_PREV_INT ;pops ax! END_C_PROC ;------------------------------------------------------------------------- ; PCSerialTerm() ; Destructor for serial packet interface ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int zero if ok ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialTerm ARG_DPTR term_this BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Disable interrupts ;-------------------------------------------------------------- DPMI_DISABLE_SAVE_INT ;pushes ax! ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,term_this mov eax,esi ;is it NULL? or eax,eax jz short skipError1 ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Turn off RTS, DTR, and interrupts ;-------------------------------------------------------------- mov dx,portBase[esi] RETRACT_MCR UART_MCR_IRQ+UART_MCR_RTS+UART_MCR_DTR ; 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 skipError2 ;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 ;; mov al,irqOldPICbits[edi] ;get old PIC bits ;; not al ;invert (zero=on) ;; and al,bl ;was it enabled before we got to it? ;; jnz skipRetract ;yes, leave it enabled RETRACT_PIC bl ;no, turn it off skipRetract: xor eax,eax jmp short skipRestore skipError1: mov eax,1 jmp short skipRestore skipError2: mov eax,2 skipRestore: ;-------------------------------------------------------------- ; Reenable the old interrupt state and return ;-------------------------------------------------------------- DPMI_RESTORE_PREV_INT ;pops ax! ;;this routine does not properly return status yet END_C_PROC ;------------------------------------------------------------------------- ; PCSerialReceive() ; Returns characters if available ;------------------------------------------------------------------------- ; Enter: ; rp_this = pointer to object ; Byte *destPtr = pointer to 128-byte destination buffer ; max_characters = size of destination buffer ;------------------------------------------------------------------------- ; Returns: ; int number of characters ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialReceive ARG_DPTR rp_this ARG_DPTR rp_dest ARG_DWORD rp_maximum BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,rp_this mov eax,esi ;is it NULL? or eax,eax jz short spr_done ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Skip if disabled ;-------------------------------------------------------------- cmp enabled[esi],0 ;if disabled, don't even try jne short recvOK xor eax,eax jmp short spr_done recvOK: ;-------------------------------------------------------------- ; Check character count ;-------------------------------------------------------------- xor edx,edx ;clear top of edx mov dx,rxBuf.count[esi] ;get # of characters in dx or dx,dx ;empty? jz short spr_done ;yes, return zero ;-------------------------------------------------------------- ; Limit to maximum ;-------------------------------------------------------------- mov ecx,rp_maximum ;get the maximum cmp edx,ecx ;total > max? jle short recvNoLimit ;no, use total mov edx,ecx ;yes, limit to maximum recvNoLimit: ;;; ;-------------------------------------------------------------- ;;; ; Disable interrupts ;;; ;-------------------------------------------------------------- ;;; DPMI_DISABLE_INT ;-------------------------------------------------------------- ; Transfer data from buffer ;-------------------------------------------------------------- mov ecx,edx ;place count in ecx mov edi,rp_dest ;get pointer to destination cld ;make sure direction is ok spr_loop: SERBUFGET_AL rxBuf ;get character into al stosb ;save it loop spr_loop ;;; ;-------------------------------------------------------------- ;;; ; Reenable the old interrupt state ;;; ;-------------------------------------------------------------- ;;; DPMI_RESTORE_PREV_INT ;pops ax! spr_done: mov eax,edx ;place count in eax ;-------------------------------------------------------------- ; Return count in eax ;-------------------------------------------------------------- END_C_PROC ;------------------------------------------------------------------------- ; PCSerialFlowControl() ; Turns transmit fow control on or off ;------------------------------------------------------------------------- ; Enter: ; sp_this = pointer to object ; int count = non-zero = on ;------------------------------------------------------------------------- ; Returns: ; void ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialFlowControl ARG_DPTR sf_this ARG_DWORD sf_enable BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,sf_this mov eax,esi ;is it NULL? or eax,eax jz short sfDone ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; If enabled, turn it on: else, turn it off ;-------------------------------------------------------------- cmp sf_enable,0 je short sfOff mov flowOverride[esi],0 ;turn it on (disable override) jmp short sfDone sfOff: mov flowOverride[esi],1 ;turn it off (enable override) sfDone: END_C_PROC ;------------------------------------------------------------------------- ; PCSerialSend() ; Transmits serial buffer ;------------------------------------------------------------------------- ; Enter: ; sp_this = pointer to object ; Byte *sp_src = pointer to source buffer ; int count = number of bytes to send ;------------------------------------------------------------------------- ; Returns: ; void ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialSend ARG_DPTR sp_this ARG_DPTR sp_src ARG_DWORD sp_count BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,sp_this mov eax,esi ;is it NULL? or eax,eax jz short sendSkip ;OHMIGOSH!! EXIT!!! ;-------------------------------------------------------------- ; Wait for sufficient room ;-------------------------------------------------------------- cmp enabled[esi],0 ;if disabled, don't even try je short sendSkip mov ecx,sp_count ;get request count cmp ecx,SERIALBUFSIZE ;too big? jge short sendSkip ;yes, don't even try! spt_wait: SERBUFSPACE_IN txBuf,ax ;get buffer 'in' space cmp ax,cx ;enough room? jb short spt_wait ;no, hang out ;-------------------------------------------------------------- ; Disable interrupts ;-------------------------------------------------------------- DPMI_DISABLE_SAVE_INT ;pushes ax! ;-------------------------------------------------------------- ; Copy data into tx buffer ;-------------------------------------------------------------- mov edi,sp_src ;get pointer to source spt_loop: mov al,[edi] ;get the byte inc edi ;increment the source pointer SERBUFPUT_AL txBuf ;place it in the buffer loop spt_loop 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! sendSkip: END_C_PROC ;------------------------------------------------------------------------- ; PCSerialActive() ; Returns transmitter 'active' state (=0 when all transmission is done). ;------------------------------------------------------------------------- ; Enter: ; pa_this = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int errors ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialActive 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 ;------------------------------------------------------------------------- ; PCSerialErrors() ; 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 PCSerialErrors 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 ;------------------------------------------------------------------------- ; PCSerialTestInstance() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int True ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialTestInstance mov eax,1 ret END_C_PROC ;------------------------------------------------------------------------- ; PCSerialIsSignatureBad() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int False ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialIsSignatureBad mov eax,0 ret END_C_PROC ;------------------------------------------------------------------------- ; PCSerialReceiveCount() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int number of items in receive queue ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialReceiveCount ARG_DPTR prc_this BUILD_STACK_FRAME mov esi,prc_this xor eax,eax mov ax,rxBuf.count[esi] END_C_PROC ;------------------------------------------------------------------------- ; PCSerialTransmitCount() ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int number of items in transmit queue ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialTransmitCount ARG_DPTR ptc_this BUILD_STACK_FRAME mov esi,ptc_this xor eax,eax mov ax,txBuf.count[esi] END_C_PROC ;------------------------------------------------------------------------- ; PCSerialSetRate(Word baud) ; Set new data rate for serial object ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ; baud = new data rate ;------------------------------------------------------------------------- ; Returns: ; (void) ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialSetRate ARG_DPTR ssr_this ARG_WORD ssr_rate BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,ssr_this ;-------------------------------------------------------------- ; Set baud rate ;-------------------------------------------------------------- mov dx,portBase[esi] ;get port base address mov cx,ssr_rate ;get data rate from stack SETBAUD_CX ;macro from PCSERIAL.INC END_C_PROC ;------------------------------------------------------------------------- ; PCSerialSetDataFormat(Word dataFormat) ; Set new data format for serial object ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ; baud = new data format ;------------------------------------------------------------------------- ; Returns: ; (void) ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialSetDataFormat ARG_DPTR sdf_this ARG_WORD sdf_format BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,sdf_this ;-------------------------------------------------------------- ; Set data format ;-------------------------------------------------------------- mov dx,portBase[esi] ;get port base address mov ax,sdf_format SETFORMAT_AL END_C_PROC ;------------------------------------------------------------------------- ; PCSerialLineStatus() ; Return state of line status register ;------------------------------------------------------------------------- ; Enter: ; (this) = pointer to object ;------------------------------------------------------------------------- ; Returns: ; int line status ;------------------------------------------------------------------------- BEGIN_C_PROC PCSerialModemStatus ARG_DPTR sls_this BUILD_STACK_FRAME ;-------------------------------------------------------------- ; Get "this" pointer ;-------------------------------------------------------------- mov esi,sls_this ;-------------------------------------------------------------- ; Get LSR data ;-------------------------------------------------------------- xor eax,eax ;clear returned value mov dx,portBase[esi] ;get port base address add dx,UART_MSR in al,dx END_C_PROC ;------------------------------------------------------------------------- ; StartSending - attempt to start transmitter (if allowed) ; ...called from the receive interrupt routine and PCSerialSend. ;------------------------------------------------------------------------- ; Enter: ; ESI = pointer to PCSERIAL structure ; DX = UART base address ;------------------------------------------------------------------------- ; Returns: ; (void) ;------------------------------------------------------------------------- ; Side effects: ; Modifies al, bx ;------------------------------------------------------------------------- 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 cmp flowOverride[esi],0 ;use flow control? jne sptNow ;no, just do it cmp txFlow[esi],0 ;if flow control off, don't start! je short sptNoKick sptNow: SERBUFGET_AL txBuf ;get a character from the buffer out dx,al ;send it sptNoKick: ret StartSending 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 ;;; SPKR_SET ;********************************************* ;-------------------------------------------------------------- ; Get pointer to PCSERIAL structure, UART base address ;-------------------------------------------------------------- mov esi,irqThis[edi] mov dx,irqPort[edi] ;-------------------------------------------------------------- ; Process all pending operations ;-------------------------------------------------------------- procLoop: add dx,UART_IIR ;point to interrupt ident reg in al,dx ;get interrupt source ID sub dx,UART_IIR ;restore dx cmp al,001h ;if LSB is set, no interrupt je procDone ;-------------------------------------------------------------- ; Receive character ;-------------------------------------------------------------- cmp al,UART_IIR_RDR ;receiver ready? jne short procNoRx ;no, skip it in al,dx ;get character SERBUFPUT_AL rxBuf ;save command character in buffer jmp short procLoop procNoRx: ;-------------------------------------------------------------- ; Send character ;-------------------------------------------------------------- cmp al,UART_IIR_THR ;transmitter ready? jne short procNoTx ;no, skip it cmp txBuf.count[esi],0 ;any data to send? je short procTxOff ;no, skip it cmp flowOverride[esi],0 ;use flow control? jne procNow ;no, just do it cmp txFlow[esi],0 ;flow enabled? je short procTxOff ;no, skip it procNow: SERBUFGET_AL txBuf ;get a character from the buffer out dx,al ;send it jmp short procLoop ;------------------------------ ; Couldn't send, just exit ;------------------------------ procTxOff: jmp procLoop procNoTx: ;-------------------------------------------------------------- ; Process modem status register change ;-------------------------------------------------------------- cmp al,UART_IIR_MSR ;MSR change? jne short procNoMSR ;no, skip it add dx,UART_MSR ;read modem status reg to clear IRQ in al,dx sub dx,UART_MSR test al,UART_MSR_DCTS ;CTS changed? jz short procNoDCTS test al,UART_MSR_CTS ;CTS active? jz short procCTSoff ;------------------------------ ; Turn flow control on ;------------------------------ mov txFlow[esi],1 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 procNoDCTS ;yes, must be active cmp txBuf.count[esi],0 ;any data to send? je short procNoDCTS ;no, skip it call StartSending ;yes, restart sending jmp short procNoDCTS procCTSoff: ;------------------------------ ; Turn flow control off ;------------------------------ mov txFlow[esi],0 procNoDCTS: jmp procLoop procNoMSR: ;-------------------------------------------------------------- ; Process line status register change (error or break) ;-------------------------------------------------------------- ;MUST BE LSR CHANGE. No need to check IIR. ;; cmp al,UART_IIR_LSR ;LSR change? ;; jne short procNoLSR ;no, skip it 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 procDone: ;-------------------------------------------------------------- ; PIC port write ; (allows future interrupts to occur) ;-------------------------------------------------------------- PIC_RESET_MASTER ;-------------------------------------------------------------- ; Restore registers, return from SUBROUTINE (not interrupt!) ;-------------------------------------------------------------- ;;; SPKR_RESET ;********************************************* pop es pop ds ret IntHandler ENDP END_CODE END