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>
1249 lines
41 KiB
NASM
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
|
|
|
|
|