📄 comm.asm
字号:
CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL?
JB SENDII2 ; JUMP IF NOT
INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT (Can this happen?)
ADD BX,START_TDATA[SI] ; ES:BX point to 1st chr in buffer
MOV ES:[BX],AL ; Overwrite 1st character
JMP SHORT SENDII4
SENDII2:MOV DX,START_TDATA[SI] ; DX is index of 1st char
DEC DX ; Back it up
AND DX,S_SIZE-1 ; Ring it
MOV START_TDATA[SI],DX ; Save new value
ADD BX,DX ; Address within buffer
MOV ES:[BX],AL ; Move character to buffer
INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER
MOV URGENT_SEND[SI],1 ; Flag high priority message
; No check for PC_OFF here. Flow control ALWAYS gets sent!
SENDII4:CALL CHROUT ; Output a chr if possible
SENDIIX:POP ES
POP DX
POP BX
RET
SENDII ENDP
;*---------------------------------------------------------------------*
;* s p i n l o o p *
;* *
;* Waste time to allow slow UART chips to catch up *
;*---------------------------------------------------------------------*
SPINLOOP PROC NEAR
PUSH CX
MOV CX,100
WASTERS:
CALL CRET ; Better time waster than a 1-byte NOP
LOOP WASTERS
POP CX
CRET: RET
SPINLOOP ENDP
; CHROUT() Process level routine to remove a chr from the buffer,
; give it to the UART and adjust the pointer and count.
; If interrupts are disabled at entry, nothing is done.
; If a character is successfully output, Tx ints are enabled.
; Requires: SI pointing at the appropriate data area
; Clobbers: AX, BX, DX, ES
; Must preserve: CX in case there is a count there
CHROUT PROC NEAR
MOV DX,IER[SI] ; Interrupt Enable Register
IN AL,DX
TEST AL,2 ; Tx interrupts enabled?
JNZ CHROUX ; Jump if not
CMP SEND_OK[SI],1 ; See if Data Set Ready & CTS are on
JNE CHROUX ; No. Still can't enable TX ints
CALL TX_CHR ; Actually transmit the chr
MOV DX,IER[SI] ; Interrupt Enable Register
MOV AL,0FH ; Rx, Tx, Line & Modem enable bits
OUT DX,AL ; Enable those interrupts
CHROUX: RET
CHROUT ENDP
PAGE;
IFNDEF UUPC
;
; void far send_local(char);
; Simulate a loopback by placing characters sent in recv buffer
;
_send_local PROC FAR
push bp
mov bp,sp
PUSHF
PUSH SI
PUSH ES
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ SLX ; ABORT IF NOT
CLI ; INTERRUPTS OFF
CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM
JB SL3 ; SKIP IF ROOM
INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW COUNT
JMP SHORT SLX ; PUNT
SL3: LES BX,RBuff[SI] ; Receive buffer location
ADD BX,END_RDATA[SI] ; ES:BX POINTS TO FREE SPACE
MOV AL,[BP+6] ; Get the byte to send
MOV ES:[BX],AL ; Put into buffer
MOV BX,END_RDATA[SI] ; Get the end pointer
INC BX ; INCREMENT END_RDATA POINTER
AND BX,R_SIZE-1 ; Ring the pointer
MOV END_RDATA[SI],BX ; SAVE VALUE
INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER
SLX: POP ES
POP SI
POPF ; Restore interrupt state
mov sp,bp
pop bp
RET ; DONE
_send_local ENDP
ENDIF
PAGE;
;
; void far break_com(void) Send a BREAK out to alert the remote end
;
_break_com PROC FAR
push bp
mov bp,sp
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ BREAKX ; ABORT IF NOT
MOV DX,LCR[SI] ; LINE CONTROL REGISTER
IN AL,DX ; GET CURRENT SETTING
OR AL,40H ; TURN ON BREAK BIT
OUT DX,AL ; SET IT ON THE UART
MOV AX,25 ; 25/100 of a second
CALL WaitN
MOV DX,LCR[SI] ; LINE CONTROL REGISTER
IN AL,DX ; GET CURRENT SETTING
AND AL,0BFH ; TURN OFF BREAK BIT
OUT DX,AL ; RESTORE LINE CONTROL REGISTER
BREAKX: POP SI
mov sp,bp
pop bp
RET
_break_com ENDP
PAGE;
;
; ERROR_STRUCT far *com_errors(void)
; Returns a pointer to the table of error counters
;
_com_errors PROC FAR
push bp
mov bp,sp
PUSH SI
MOV SI,CURRENT_AREA ; Point to block for selected port
LEA AX,ERROR_BLOCK[SI] ; Offset to error counters
MOV DX,DS ; Value is in DX:AX
POP SI
mov sp,bp
pop bp
RET
_com_errors ENDP
;
; char far modem_status(void)
; Returns the modem status register in AL
;
; Bits are: 0x80: Carrier Detect
; 0x40: Ring Indicator
; 0x20: Data Set Ready
; 0x10: Clear To Send
; 0x08: Delta Carrier Detect (CD changed)
; 0x04: Trailing edge of RI (RI went OFF)
; 0x02: Delta DSR (DSR changed)
; 0x01: Delta CTS (CTS changed)
_modem_status PROC FAR
push bp
mov bp,sp
PUSH SI
MOV SI,CURRENT_AREA ; Point to block for selected port
MOV DX,MSR[SI] ; IO Addr of Modem Status Register
IN AL,DX ; Get the live value
XOR AH,AH ; Flush unwanted bits
POP SI
mov sp,bp
pop bp
RET
_modem_status ENDP
PAGE;
;
; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
;
INT_HNDLR1 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA1 ; DATA AREA FOR COM1:
JMP SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2:
;
INT_HNDLR2 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA2 ; DATA AREA FOR COM2:
JMP SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR3 - HANDLES INTERRUPTS GENERATED BY COM3:
;
INT_HNDLR3 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA3 ; DATA AREA FOR COM3:
JMP SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR4 - HANDLES INTERRUPTS GENERATED BY COM4:
;
INT_HNDLR4 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA4 ; DATA AREA FOR COM4:
; Fall into INT_COMMON
PAGE;
;
; BODY OF INTERRUPT HANDLER
;
INT_COMMON: ; SI has been pushed and loaded
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH ES
MOV AX,DGROUP ; Offsets are relative to DGROUP [WWP]
MOV DS,AX
; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT
REPOLL: MOV DX,IIR[SI] ; Interrupt Identification Register
IN AL,DX
TEST AL,1 ; Check the "no interrupt present" bit
JNZ INT_END ; ON means we are done
MOV BL,AL ; Put where we can index by it
AND BX,000EH ; Ignore FIFO_ENABLED bits, etc.
JMP WORD PTR CS:INT_DISPATCH[BX]; Go to appropriate routine
INT_DISPATCH:
DW MSI ; 0: Modem status interrupt
DW TXI ; 2: Transmitter interrupt
DW RXI ; 4: Receiver interrupt
DW LSI ; 6: Line status interrupt
DW REPOLL ; 8: (Future use by UART makers)
DW REPOLL ; A: (Future use by UART makers)
DW RXI ; C: FIFO Timeout
DW REPOLL ; E: (Future use by UART makers)
INT_END: ; Now tell 8259 we handled that IRQ
MOV DX,IER[SI] ; Gordon Lee's cure for dropped ints
IN AL,DX ; Get enabled interrupts
MOV AH,AL
XOR AL,AL
OUT DX,AL ; Disable UART interrupts
MOV AL,EOI[SI] ; End of Interrupt. With input gone,
OUT INTA00,AL ; Give EOI to 8259 Interrupt ctlr
MOV AL,AH ; Get save interrupt enable bits
OUT DX,AL ; Restore them, maybe causing a
; transition into the 8259!!!
POP ES
POP DS
POP DX
POP CX
POP BX
POP AX
POP SI
IRET
PAGE;
;
; Line status interrupt
;
LSI: MOV DX,LSR[SI] ; Line status register
IN AL,DX ; Read line status & bump error counts
TEST AL,2 ; OVERRUN ERROR?
JZ LSI1 ; JUMP IF NOT
INC WORD PTR EOVRUN[SI] ; ELSE BUMP ERROR COUNT
LSI1: TEST AL,4 ; PARITY ERROR?
JZ LSI2 ; JUMP IF NOT
INC WORD PTR EPARITY[SI] ; ELSE BUMP ERROR COUNT
LSI2: TEST AL,8 ; FRAMING ERROR?
JZ LSI3 ; JUMP IF NOT
INC WORD PTR EFRAME[SI] ; ELSE BUMP ERROR COUNT
LSI3: TEST AL,16 ; BREAK RECEIVED?
JZ LSI4 ; JUMP IF NOT
INC WORD PTR EBREAK[SI] ; ELSE BUMP ERROR COUNT
LSI4: JMP REPOLL ; SEE IF ANY MORE INTERRUPTS
;
; Modem status interrupt
;
MSI: MOV DX,MSR[SI] ; Modem Status Register
IN AL,DX ; Read status & clear interrupt
CMP CONNECTION[SI],'D' ; Direct connection - ignore int
JE MSI0 ; Just noise on DSR,CTS pins
AND AL,30H ; Expose CTS and Data Set Ready
CMP AL,30H ; Both on?
JE MSI0 ; Yes. Enable output at TXI
XOR AL,AL
JMP SHORT MSI1
MSI0: MOV AL,1
MSI1: MOV SEND_OK[SI],AL ; Put where TXI and send_com can see
MOV DX,IER[SI] ; Let a TX int happen for thoro chks
MOV AL,0FH ; Line & modem sts, recv, send.
OUT DX,AL
JMP REPOLL ; Check for other interrupts
PAGE;
;
; Transmit interrupt
;
TXI: CMP SEND_OK[SI],1 ; Harware (CTS & DSR on) OK?
JNE TXI9 ; No. Must wait 'til cable right!
MOV CX,1 ; Transfer count for flow ctl
CMP URGENT_SEND[SI],1 ; Flow control character to send?
JE TXI3 ; Yes. Always send flow control.
CMP PC_OFF[SI],1 ; Flow control (XON/XOFF) OK?
JE TXI9 ; Stifled & not urgent. Forget it.
TXI1: MOV CL,UART_SILO_LEN[SI] ; MAX size chunk (1 for simple 8250)
; Too bad there is no "Tranmitter FIFO Full" indication!
CMP SIZE_TDATA[SI],CX ; SEE IF ANY MORE DATA TO SEND
JG TXI2 ; UART is the limit
MOV CX,SIZE_TDATA[SI] ; Buffer space limited. Use that.
TXI2: JCXZ TXI9 ; No data, disable TX ints
TXI3: CALL TX_CHR ; Transmit a character
LOOP TXI3 ; Keep going 'til silo is full
MOV URGENT_SEND[SI],0 ; Tell process level we sent flow ctl
JMP REPOLL
; IF NO DATA TO SEND, or can't send, RESET TX INTERRUPT AND RETURN
TXI9: MOV DX,IER[SI]
MOV AL,0DH ; Line & modem sts, recv, no send.
OUT DX,AL
JMP REPOLL
; TX_CHR Internal routine used to actually move a character from
; the transmit buffer to the UART and adjust pointers.
; Called from process and interrupt levels with interrutps
; enabled or disabled.
; Requires: SI pointing at data area for this UART
; Clobbers: AX, BX, DX, ES
; Must preserve: CX (Caller has count here).
TX_CHR PROC NEAR
LES BX,TBuff[SI] ; Pointer to buffer
ADD BX,START_TDATA[SI] ; ES:BX points to next char to send
MOV AL,ES:[BX] ; Get character from buffer
MOV DX,DATREG[SI] ; I/O address of data register
OUT DX,AL ; Output the character
INC BX ; Bump the head pointer
AND BX,S_SIZE-1 ; Ring it
MOV START_TDATA[SI],BX ; Store back in private block
DEC SIZE_TDATA[SI] ; One fewer in buffer now
RET
TX_CHR ENDP
PAGE;
;
; Receive interrupt
;
RXI:
RXI0: MOV DX,LSR[SI] ; Line Status Register
IN AL,DX ; Read it
TEST AL,1 ; Check the RECV DATA READY bit
JE RXIX ; No more data available
MOV DX,DATREG[SI] ; UART DATA REGISTER
IN AL,DX ; Get data, clear status
CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
JNE RXI2 ; No. Don't check for XON/XOFF
; Check each character for possible flow control (XON/XOFF)
AND AL,7FH ; STRIP PARITY
CMP AL,CONTROL_S ; STOP COMMAND RECEIVED?
JNE RXI1 ; Jump if not. Might be ^Q though.
MOV PC_OFF[SI],1 ; Stop output
JMP SHORT RXI0 ; Don't store character
RXI1: CMP AL,CONTROL_Q ; GO COMMAND RECEIVED?
JNE RXI2 ; No. Not a flow control character
MOV PC_OFF[SI],0 ; Enable output
JMP SHORT RXI0 ; Don't store character
; Have a real data byte. Store if possible.
RXI2: CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM
JL RXI6 ; CONTINUE IF SO
INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW ERROR COUNT
JMP SHORT RXIX
RXI6: LES BX,RBuff[SI] ; Receive buffer location
ADD BX,END_RDATA[SI] ; ES:BX points to free space
MOV ES:[BX],AL ; Put character in buffer
INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER
MOV BX,END_RDATA[SI] ; Get the end index
INC BX ; Bump it passed location just used
AND BX,R_SIZE-1 ; Ring the pointer
MOV END_RDATA[SI],BX ; SAVE VALUE
; See if we must tell remote host to stop outputting.
CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
JNE RXI0 ; No
CMP HOST_OFF[SI],1 ; Already told remote to shut up?
JE RXI0 ; Yes. Don't flood him with ^Ss
CMP SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL?
JLE RXIX ; No. No need to stifle remote end
; Would like to wait here for URGENT_SEND to go off if it is on.
; But we need to take a TX interrupt for that to happen.
MOV AL,CONTROL_S ; TURN OFF HOST IF SO
CALL SENDII ; SEND IMMEDIATELY INTERNAL
MOV HOST_OFF[SI],1 ; HOST IS NOW OFF
RXIX: JMP REPOLL
INT_HNDLR4 ENDP
INT_HNDLR3 ENDP
INT_HNDLR2 ENDP
INT_HNDLR1 ENDP
COM_TEXT ENDS
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -