Files
TeslaRel410/CODE/RP/MUNGA_L4/L4SERIAL.ASM
T
CydandClaude Fable 5 fdd9ac9d97 Initial import: Tesla Release 4.10 (Tesla:BattleTech & Tesla:Red Planet)
Archival snapshot of the Virtual World Entertainment Tesla cockpit
software, 1994-1996: MUNGA engine and L4 pod layer source (Borland
C++ 5.0), BT/RP game code, and game content (models, audio, maps,
gauges, Division renderer data). Includes third-party libraries:
Division dVS/DPL graphics, HMI SOS audio, WATTCP networking.

Files are preserved byte-for-byte (.gitattributes disables all
line-ending conversion). README.md documents the layout, target
hardware, and toolchain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:21:58 -05:00

1249 lines
41 KiB
NASM

;===========================================================================
; 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 <IntCom1, 0,0,0,0,0,0,0,0>
irq_com2 IRQDATA <IntCom2, 0,0,0,0,0,0,0,0>
irq_com3 IRQDATA <IntCom3, 0,0,0,0,0,0,0,0>
irq_com4 IRQDATA <IntCom4, 0,0,0,0,0,0,0,0>
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 <eax,ebx,ecx,edx,esi,edi,es>
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 <ebx,ecx,edx,esi,edi>
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 <ebx,ecx,edx,esi,edi>
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 <eax,ebx,ecx,edx,esi,edi>
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 <eax,ebx,ecx,edx,esi,edi>
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 <ebx,ecx,edx,esi>
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 <ebx,ecx,edx,esi>
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 <esi>
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 <esi>
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 <esi,eax,ebx,ecx,edx>
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 <esi,eax,ebx,ecx,edx>
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 <esi,ebx,ecx,edx>
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