Files
TeslaRel410/CODE/RP/MUNGA_L4/PCSPAK.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

2045 lines
62 KiB
NASM

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