📄 llcom3.asm
字号:
MOV AH,DH ;Copy error code
CMP AH,0 ;time out?
JNZ PUTRET ;br. if so
MOV CX,[BX].QUELEN ;Length of queue
;PUTCLP:
CALL B$BREAK_CHK
CMP CX,[BX].QUENUM ;Queue full?
; JZ PUTCLP ;Wait for space in queue
JZ SND2 ; always check modem lines
; PUSH AX ;save char
; PUSH BX ;Save queout
; CALL MSRWAT ;Check MSR, (on ret, [BX] = Q_DEB).
; POP BX ;[SI] = queout
; POP AX ;Restore char to output
; MOV AH,DH ;copy error code
; CMP AH,0 ;time out?
; JNZ PUTRET ;br. if so
CLI ;Critical section
CMP [SI].COMBSY,0 ;Tx already running?
JNZ PUTINQ ;If so, just queue and exit.
MOV [SI].COMBSY,255D ;Pre-set Tx busy flag.
XOR DX,DX
MOV DL,[SI].DEVID ;get device offset
MOV DI,DX
SHL DI,1 ;make word index
MOV DX,b$ComPort[DI] ;get the I/O address port
DbAssertRel DX,NE,0,DV_TEXT,<LLCOM3.ASM: Com I/O address = 0 in B$SNDCOM>
OUT DX,AL ; restarting Tx interrupts.
JMP SHORT POPBAR
PUTINQ:
PUSH ES ; make [ES] equal to [DS]
MOV ES,[SI].XMTSEG ;get buffer segment from DEB
cCALL B$QUE ; Put char [AL] in output queue
POP ES
POPBAR:
STI ;End Critical
PUTRET:
POP BX
POP CX
POP DX
POP SI
POP DI
cEnd ; return to caller
;***
; B$STACOM
;[OEM documentation in file LLCOM5]
;
; Purpose:
; Returns the number of bytes of free space in the input queue
; and the number of bytes of information queued for input.
; In order to support the IBM Communications Error Reporting
; philosophy, this routine will not report communications errors.
;
; Entry:
; AH = RS232 device ID (0..255)
;
; Exit:
; AH = 0 for no I/O error
; CX = number of bytes of free space in the output queue
; DX = number of bytes queued for input
;
; Modified:
; None.
;****
cProc B$STACOM,<PUBLIC,NEAR>,<BX,SI> ;[1]preserve registers
cBegin
MOV BX,OFFSET DGROUP:Q_IN ;Addr of Input Queue
MOV SI,OFFSET DGROUP:Q_OUT ;address of COM1 output queue
OR AH,AH ;is this COM1?
JZ STA2 ;br. if so
MOV BX,OFFSET DGROUP:Q_IN2 ;else get com2 input queue
MOV SI,OFFSET DGROUP:Q_OUT2 ;address of COM2 output queue
STA2:
MOV DX,[BX].QUENUM ;get number of bytes in queue
MOV CX,[SI].QUELEN ;get length of queue
SUB CX,[SI].QUENUM ;determine amount of free space in queue
XOR AH,AH ;indicate no errors
cEnd ; pop BX and exit
;***
;B$TRMCOM - Terminate the communications device
;[OEM documentation given in LLCOM5]
;
;Purpose:
; Terminate an RS232 channel. This routine is called when a file
; associated with a COM channel is closed, or when a SYSTEM statement
; is executed. It waits for any outbound data to be transmitted, drops
; the DTR line, disables interrupts from this device, and disables the
; queues if needed. Any interupt handlers or threads should be removed.
;
; While waiting for any data to be written, make sure to check the
; keyboard for a ^Break and to check the Modem Status Registers for
; any timeout conditions so as not to hang. This routine will not
; report communications errors unless operating under OS/2.
;
;Entry:
; [AH] = RS232 device ID (0 <= AH < NUM_RS232 <= 2)
; [CX] = FLAG: 0 = reset DTR
;
;Exit:
; [AH] = 0 to indicate that there are no errors.
;
;Uses:
; Per Convention
;
;Preserves:
; BX, CX, DX
;
;Exceptions:
; None.
;****
cProc B$TRMCOM,<PUBLIC,NEAR>
cBegin
PUSH SI
PUSH BX
PUSH CX
PUSH DX
MOV SI,OFFSET DGROUP:COMM1 ;Com1 deb
MOV BX,OFFSET DGROUP:Q_OUT ; Get Output Queue base
OR AH,AH ;is this com1?
JZ TRM2 ;br.if so
MOV SI,OFFSET DGROUP:COMM2 ;get com2 deb
MOV BX,OFFSET DGROUP:Q_OUT2 ; Get Output Queue base
TRM2:
CMP [BX].QUENUM,0 ;Output busy?
JZ CLSWAX ;Brif done
CLSWAT:
CALL MSRWAT ;Allow "Time out" during CLOSE
OR DH,DH ;did a timeout occur?
JNE CLSWAX ;if so, just call quits and close it
CMP [BX].QUENUM,0 ;Output busy?
JNZ CLSWAT ;Yes, wait until done
CLSWAX:
PUSH CX ; save flag
XOR CX,CX ;Lots of time
CLSDLY:
LOOP CLSDLY ;for Shift Reg to empty...
POP CX ; CX = flag; 0 = reset DTR
CALL CLSMSR ;Force the file closed
POP DX
POP CX
POP BX
POP SI
RET ;COM file is closed and deallocated
CLSMSR:
PUSH DI
XOR DX,DX
MOV DL,[SI].DEVID
MOV DI,DX ;get device offset
SHL DI,1 ;make a word index
MOV DX,b$ComPort[DI] ;get the device I/O port address
DbAssertRel DX,NE,0,DV_TEXT,<LLCOM3.ASM: Com I/O address = 0 at start of B$TRMCOM>
MOV AL,0 ; 0 to reset lines
JCXZ Reset_RTS ; if CX = 0, reset DTR
INC AX ; else, leave DTR active
Reset_RTS: ; always reset RTS
ADD DX,4 ;Modem Ctrl reg.
OUT DX,AL ;Clear MSR, disable card.
MOV [BX].QUENUM,0 ;Clear # to output
SUB DX,3 ;Interrupt Enable Reg.
PAUSE ;make sure instruction fetch has occurred
MOV AX,1000h + 0 ; 0 to disable interrupts; AH=10H for below
OUT DX,AL ; Disable COMM Interrupts
DEC DX ;get back base I/O address
; MOV AH,10H ;IRQ-4 Disable Mask
CMP DH,3 ; RS232 Port addr 3xx?
JZ $C_COM_3xx ;Yes, Disable IRQ-4
MOV AH,8 ;IRQ-3 Disable Mask, assume 2xx.
$C_COM_3xx:
CLI
TEST b$DOS_INT_MASK,AH ;test if bit was initially enabled
JZ NO_INT_DISABLE ;if so (bit was 0), then no disable
IN AL,INTA1 ;Get INT masks from 8259 IMR Reg.
OR AL,AH ;Disable IRQ-3 or IRQ-4
PAUSE ;make sure instruction fetch has occurred
OUT INTA1,AL ;For RS232 Cards.
NO_INT_DISABLE:
MOV BX,OFFSET DGROUP:COM1_VECTOR ;get start of old COM1 vector
OR DI,DI ;test if COM1
JZ REST_VECTOR ;if COM1, then jump
MOV BX,OFFSET DGROUP:COM2_VECTOR ;get start of old COM2 vector
REST_VECTOR:
MOV AL,IRQ4/4 ;vector number for primary card
CMP DH,3 ;test if primary asynch card
JE REST_MASK ;if so, then jump
MOV AL,IRQ3/4 ;vector number for secondary card
REST_MASK:
PUSH DS ; Save BASIC data segment
LDS DX,DWORD PTR [BX] ;load vector into DS:DX
MOV AH,25H ;get DOS function code
INT 21H ;and restore it
STI
POP DS ;and restore DS to it
XOR AX,AX ;clear AX
MOV [BX],AX ;clear offset of saved vector
MOV [BX+2],AX ;and same for the segment...
; Set the BIOS data location for the COM I/O port address.
XOR BX,BX ; get a zero
XCHG BX,b$ComPort[DI] ;get the COM I/O port address [25]& reset
DbAssertRel BX,NE,0,DV_TEXT,<LLCOM3.ASM: Com I/O address = 0 at end of B$TRMCOM>
PUSH DS ;save DGROUP on the stack
XOR DX,DX ;set to zero to access...
MOV DS,DX ;...the BIOS data segment
MOV DS:RS232B[DI],BX ;restore the BIOS data location
POP DS ;restore DGROUP from the stack
POP DI
cEnd ; return to caller
SUBTTL Comm interrupt service routine
page
;***
;COM_INT1 / COM_INT2
;
;PURPOSE:
; Interrupt handlers for COM1: and COM2:. This is the communications
; interrupt service routine for RS232 communications. When an RS232
; event occurs the interrupt vectors here. This routine determines
; who the caller was and services the appropriate interrupt. The
; interrupts are prioritized in the following order:
; 1. line status interrupt
; 2. read data available interrupt
; 3. transmit buffer empty interrupt
; 4. modem service interrupt
; This routine continues to service until all interrupts have been
; satisfied.
;
;ENTRY:
; none
;EXIT:
; none
;MODIFIED:
; none
;******************************************************************************
COM_INT2:
PUSH DS
PUSH DI
PUSH DX
MOV DS,CS:b$BASDSG ;get BASIC's data seg
MOV DX,DS:b$ComPort+2 ;DX = COM2 I/O addr.
DbAssertRel DX,NE,0,DV_TEXT,<LLCOM3.ASM: Com I/O address = 0 in COM_INT2>
MOV DI,OFFSET DGROUP:COMM2 ;get com2 deb
JMP SHORT COMM_INT
COM_INT1:
PUSH DS
PUSH DI
PUSH DX
MOV DS,CS:b$BASDSG ;get BASIC's data seg
MOV DX,DS:b$ComPort ;DX = COM1 I/O addr.
DbAssertRel DX,NE,0,DV_TEXT,<LLCOM3.ASM: Com I/O address = 0 in COM_INT1>
MOV DI,OFFSET DGROUP:COMM1 ;get com1 deb
COMM_INT:
PUSH AX
PUSH BX
PUSH SI
INC DX
INC DX ;Interrupt Id Reg.
COMM_ILP:
PAUSE ;make sure instruction fetch has occurred
IN AL,DX ;Get Interrupt Id.
TEST AL,1 ;Interrupt need servicing?
JNZ COMSRX ;Brif not, all done..
XOR AH,AH ;Using [AX] for Interrupt dispatch.
MOV SI,AX
PUSH DX ;Save Id reg.
CALL CS:SRVTAB[SI] ;Service the Interrupt..
CLI ; disable interrupts for next iter. of loop
POP DX ;Interrupt Id reg.
JMP COMM_ILP ; Until all Interrupts Serviced
COMSRX:
POP SI
POP BX
MOV DX,INTA0
MOV AL,EOI ;Send End-of-Interrupt
PAUSE ;make sure instruction fetch has occurred
OUT DX,AL ; to 8259
POP AX
POP DX
POP DI
POP DS
IRET
SRVTAB LABEL WORD
DW MSISRV ;Service Modem Status Interrupt
DW THRSRV ;Service Tx Holding Reg. Interrupt
DW RDASRV ;Service Rx Data Available Interrupt
DW RLSSRV ;Service Line Status Interrupt
; Rx Data Available Interrupt Service (Second)
; Line Status Interrupt Service (Highest)
; Not currently enabled. Line status is read
; on RDA Interrupt and sets COMERR error flag
; if any errors exist.
RLSSRV:
STI ; we're not reading, reenable interrupts
RET
RDASRV:
XOR AH,AH ;Don't set hi bit if no errors
ADD DX,3 ;Line Status Reg.
PAUSE ;make sure instruction fetch has occurred
IN AL,DX
AND AL,1EH ;Mask for errors
JZ RDASR2 ;Brif no errors
CMP [DI].COMPE,0 ;is PE option selected?
JE RDASR4 ;if not, do not parity error
CMP [DI].PARTYP,0 ;Checking Parity errors
JNZ RDASR1 ;Brif so, report all errors
RDASR4:
CMP AL,4 ;Parity error?
JZ RDASR2 ;Ignore if so, else report others
RDASR1:
MOV [DI].COMERR,AL ;Nonzero for "Device I/O Error"
MOV AH,80H ;Will set hi bit of byte in error
RDASR2:
SUB DX,5 ;Data I/O Reg.
MOV BX,OFFSET DGROUP:Q_IN ;Addr of Input Queue
CMP [DI].DEVID,0 ;is this COM1?
JZ RDASR3 ;br. if so
MOV BX,OFFSET DGROUP:Q_IN2 ;else get com2 input queue
RDASR3:
MOV SI,[BX].QUENUM
CMP SI,[BX].QUELEN ;Is queue full?
JZ COMOVR ;Yes, comm overrun
CMP [DI].CTRLZ,0 ;has control z been struck?
JNZ COMOVR ;br. if so - treat as overflow
PAUSE ;make sure instruction fetch has occurred
IN AL,DX ;Read Char from 8250
STI ; reenable interrupts now that char is read
CMP [DI].BINMOD,0 ;is this binary stuff?
JZ QUEIT ;br. if so
CMP AL,26D ;is this a control z?
JNZ QUEIT ;br. if not - que data
MOV [DI].CTRLZ,255D ;set control z flag
JMP SHORT NOQUE ;skip the queing process
QUEIT:
OR AL,AH ;Sets hi bit if error
;#***
PUSH ES
MOV ES,[DI].RCVSEG ;get buffer segment from DCB
cCALL B$QUE ; Put char [AL] in output queue
POP ES
JMP SHORT NOQUE
;#***
COMOVR:
PAUSE ;make sure instruction fetch has occurred
IN AL,DX ;Dismiss and lose char
STI ; reenable interrupts now that char is read
MOV [DI].COMOVF,1 ;Set we have overflowed
NOQUE:
MOV AX,DI ; AL = 0 for COM 1, 2 for COM2
SHR AL,1 ; AL = trap number for B$TrapEvent
; ADD AL,COMOFF ; COMOFF = 0
JMP [b$pTrapEvent] ; and indicate an event if events linked
; in, and return
; Tx Holding Reg Empty Interrupt Service (Third)
THRSRV:
STI ; we're not reading, reenable interrupts
CMP [DI].MSRERR,0 ;If Modem status fault then turn
JNZ TXSTOP ; off Tx interrupts until cleared
THRSR2:
MOV SI,BX ;[SI] = Queue base for speed
MOV BX,OFFSET DGROUP:Q_OUT ;Get Output Queue
CMP [DI].DEVID,0 ;is this com1?
JZ THRSR3 ;br. if so
MOV BX,OFFSET DGROUP:Q_OUT2 ;else get com2 output queue
THRSR3:
CMP [BX].QUENUM,0 ;Zero if queue empty
JZ XMITOF ;Brif No chars, Set COMBSY = 0.
DEC DX
DEC DX ;Data I/O Reg.
PUSH ES ;save register
MOV ES,[DI].XMTSEG ;get buffer segment from DEB
cCALL B$DQUE ; get char from queue in [AL]
POP ES ;restore register
OUT DX,AL ;Send char
TXSTOP:
RET
XMITOF:
MOV [DI].COMBSY,0 ;Set Tx Interrupt status.
RET
;Modem Status Interrupt Service (Lowest)
MSISRV:
STI ; we're not reading, reenable interrupts
ADD DX,4 ;Modem Status Reg.
PAUSE ;make sure instruction fetch has occurred
IN AL,DX
MOV [DI].MSREG,AL ;Save MSR data for others
CMP [DI].COMRLS,0 ;Did user want to check?
JZ MSISRD ;Brif not, Try DSR.
TEST AL,RLSD ;Did we lose RLSD?
JZ MSISRE ;"Device Timeout" if so.
MSISRD:
CMP [DI].COMDSR,0 ;Did user want to check?
JZ MSISRC ;Brif not, Try CTS.
TEST AL,DSR ;Did we lose DSR?
JZ MSISRE ;"Device Timeout" if so.
MSISRC:
CMP [DI].COMCTS,0 ;Did user want to check?
JZ MSISRX ;Brif not.
TEST AL,CTS ;id we lose CTS?
JNZ MSISRX ;Ignore if not.
MSISRE:
MOV [DI].MSRERR,DL ;will give "Device Timeout".
MSIRET:
RET
MSISRX:
CMP [DI].MSRERR,0 ;If was running
JZ MSIRET ; leave alone
MOV [DI].MSRERR,0 ;Clear the Modem status fault
DEC DX ;Line Status reg.
MSISRH:
PAUSE ;make sure instruction fetch has occurred
IN AL,DX
AND AL,20H ;Tx Holding reg empty?
JZ MSISRH ;Brif not, wait for it
SUB DX,3 ;Restore to Int Id reg.
JMP THRSRV ; and turn Tx Interrupts on if chars
sEnd DV_TEXT
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -