📄 com_pkg.asm
字号:
; char *tbuf; /* Transmit buffer address. */
; int tbuflen; /* Transmit buffer length. */
; char *rbuf; /* Receive buffer address. */
; int rbuflen; /* Receive buffer length. */
; Initialize the Intel 8250 and set up interrupt vector to int_hndlr.
IF LDATA
BENTRY COM_INI <UNIT,DIVISOR,TBOFF,TBSEG,TBLEN,RBOFF,RBSEG,RBLEN>
ELSE
BENTRY COM_INI <UNIT,DIVISOR,TBOFF,TBLEN,RBOFF,RBLEN>
ENDIF
AUTO <HANDLR>
MOV AX,UNIT ; Get the unit number.
CALL SET_SI ; Point to proper control area.
; (Leaves AX unchanged)
CMP AX,2 ; Initializing unit #2?
JE INIT2 ; (yes)
; Select constants for unit #1:
MOV AX,COM1_INT ; Interrupt number.
MOV BX,COM1_BASE ; I/O base address.
MOV CX,OFFSET INT_HNDLR1 ; Start of interrupt handler.
JMP SHORT INITCOM ; Go join common code.
INIT2: ; Select constants for unit #2:
MOV AX,COM2_INT ; Interrupt number.
MOV BX,COM2_BASE ; I/O base address.
MOV CX,OFFSET INT_HNDLR2 ; Start of interrupt handler.
; Fall into INITCOM:
INITCOM:
MOV [SI].COMX_INT,AX ; Save the interrupt number.
MOV [SI].COMX_BASE,BX ; Save the I/O base address.
MOV HANDLR,CX ; Save the interrupt handler starting address.
MOV AX,DS ; Copy our data segment number.
IFE LDATA
MOV ES,AX ; Save for buffer addresses.
ENDIF
MOV CS:DATASEG,AX ; Store segment # in code space (gulp!).
IF LDATA
MOV AX,TBSEG ; Get the transmit buffer segment number.
ENDIF
MOV [SI].TBUF_SEG,AX ; Save it.
MOV BX,TBOFF ; Copy the transmit buffer offset.
MOV [SI].TBUF_OFF,BX ;
MOV BX,TBLEN ; Copy the transmit buffer length.
MOV [SI].TBUF_SIZE,BX ;
IF LDATA
MOV AX,RBSEG ; Get the receive buffer segment number.
ENDIF
MOV [SI].RBUF_SEG,AX ; Save it.
MOV AX,RBOFF ; Copy the receive buffer offset.
MOV [SI].RBUF_OFF,AX ;
MOV AX,RBLEN ; Copy the receive buffer length.
MOV [SI].RBUF_SIZE,AX ;
XOR AX,AX ; Clear the accumulator.
MOV [SI].START_TDATA,AX ; Reset start of transmitted data.
MOV [SI].END_TDATA,AX ; Reset end of transmitted data.
MOV [SI].SIZE_TDATA,AX ; Reset number of transmitted chars.
MOV [SI].START_RDATA,AX ; Reset start of received data.
MOV [SI].END_RDATA,AX ; Reset end of received data.
MOV [SI].SIZE_RDATA,AX ; Reset number of received chars.
CLI ; ******* Disable Interrupts *******
MOV DX,[SI].COMX_BASE ;;;
ADD DX,MCR ;;; Reset the UART (AX is still zero).
OUT DX,AL ;;;
ADD DX,(LSR-MCR) ;;; Reset line status condition.
IN AL,DX ;;;
ADD DX,(DATREG-LSR) ;;; Reset receive data condition.
IN AL,DX ;;;
ADD DX,(MSR-DATREG) ;;; Reset modem deltas and conditions.
IN AL,DX ;;;
ADD DX,(LCR-MSR) ;;; Set baud rate with the passed argument.
MOV AL,DLA+MODE ;;;
OUT DX,AL ;;;
ADD DX,(DLL-LCR) ;;;
MOV AX,DIVISOR ;;;
OUT DX,AL ;;; Low byte of passed argument.
ADD DX,(DLH-DLL) ;;;
MOV AL,AH ;;;
OUT DX,AL ;;; High byte of passed argument.
ADD DX,(LCR-DLH) ;;; Set 8250 to 8 bits, no parity.
MOV AL,MODE ;;;
OUT DX,AL ;;;
PUSH SI ;;; Save pointer to COM block.
MOV AX,[SI].COMX_INT ;;; Get the 8259 interrupt number.
ADD AX,INT_OFF ;;; Convert to 8086 interrupt number.
BCALL INT_SETU <AX HANDLR CS> ;;; Call int_setup(vec, newip, newcs).
POP SI ;;; Restore data block pointer.
;;; Enable interrupts on 8259 and 8250:
IN AL,IMR ;;; Get current enable bits on 8259.
MOV CL,BYTE PTR [SI].COMX_INT ;;; Get interrupt number.
MOV BL,1 ;;; Convert to
SHL BL,CL ;;; bit position.
NOT BL ;;; Clear current
AND AL,BL ;;; interrupt bit.
OUT IMR,AL ;;; Set enable on 8259.
MOV DX,[SI].COMX_BASE ;;;
ADD DX,IER ;;; Enable interrupts on 8250.
MOV AL,RXINT ;;;
OUT DX,AL ;;;
ADD DX,(MCR-IER) ;;; Set dtr and enable int driver.
MOV AL,DTR ;;;
OUT DX,AL ;;;
STI ;;; ******* Enable Interrupts *******
;;; (Next instruction still disabled)
BEND COM_INI
.SBHED <COM_TRM -- Turn Off Interrupts and Shutdown>
; void
; com_trm(unit) /* Turns off interrupts from the COM1: port. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
BENTRY COM_TRM <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV DX,[SI].COMX_BASE
ADD DX,IER ; Turn off 8250.
MOV AL,0
OUT DX,AL
IN AL,IMR ; Turn off 8259.
MOV CL,BYTE PTR [SI].COMX_INT ; Get interrupt number.
MOV BL,1 ; Convert to
SHL BL,CL ; bit position.
OR AL,BL ; Disable this interrupt.
OUT IMR,AL
; Reset interrupt vector:
MOV AX,[SI].COMX_INT ; Get the 8259 interrupt number.
ADD AX,INT_OFF ; Convert to 8086 interrupt number.
BCALL INT_REST <AX> ; Call int_restore(vec).
BEND COM_TRM
.SBHED <COM_DOFF -- Turn off DTR>
; void
; com_doff(unit) /* Turns off DTR. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
; Turns off DTR to tell modems that the terminal has gone away
; and to hang up the phone.
BENTRY COM_DOFF <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV DX,[SI].COMX_BASE
ADD DX,MCR
MOV AL,DTR_OF
OUT DX,AL
BEND COM_DOFF
.SBHED <COM_DON -- Turn On DTR>
; void
; com_don(unit) /* Turns on DTR. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
BENTRY COM_DON <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV DX,[SI].COMX_BASE
ADD DX,MCR
MOV AL,DTR
OUT DX,AL
BEND COM_DON
.SBHED <COM_ICNT -- Return Number of Input Bytes>
; int /* Number of characters in input buffer. */
; com_icnt(unit) /* Returns number of characters in input buffer. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
BENTRY COM_ICNT <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV AX,[SI].SIZE_RDATA ; Get number of bytes used.
BEND COM_ICNT
.SBHED <COM_GETC -- Get the Next Received Character>
; int /* Next character in input buffer or EOF. */
; com_getc(unit) /* Reads next character in input buffer. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
;
; Returns the next character from the receive buffer and
; removes it from the buffer.
BENTRY COM_GETC <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
CMP [SI].SIZE_RDATA,0 ; Is there anything in the buffer?
JE L12 ; (nothing)
MOV ES,[SI].RBUF_SEG ; Get receive buffer segment number.
MOV DI,[SI].RBUF_OFF ; Get receive buffer offset.
MOV BX,[SI].START_RDATA ; Fetch next data byte.
MOV AL,ES:[BX+DI] ; Get data from buffer.
XOR AH,AH
INC BX ; Bump START_RDATA so it points at next char.
CMP BX,[SI].RBUF_SIZE ; See if past end.
JB L10 ; If not then skip.
XOR BX,BX ; Adjust to beginning.
L10: MOV [SI].START_RDATA,BX ; Save the new START_RDATA value.
DEC [SI].SIZE_RDATA ; One less character.
JMP SHORT L14 ; Skip to common return code.
L12: MOV AX,-1 ; Indicate no characters available.
L14: BEND COM_GETC
.SBHED <COM_OCNT -- Returns Number of Free Bytes>
; int /* Number of free bytes in output buffer. */
; com_ocnt(unit) /* Returns number of free bytes in output buffer. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
BENTRY COM_OCNT <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV AX,[SI].TBUF_SIZE ; Get the size of the x-mit buffer.
SUB AX,[SI].SIZE_TDATA ; Subtract the number of bytes used.
BEND COM_OCNT
.SBHED <COM_PUTC -- Queue a Character for Output>
; bool /* Returns FALSE if no more room. */
; com_putc(unit, ch) /* Writes a character to the output buffer. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
; char ch; /* The character to write. */
; Note that there is an implicit interlock with the interrupt
; level. It is OK for an interrupt to occur between incrementing
; SIZE_TDATA and the end of the code that monkeys with the interrupt
; enable bits. The worst that can happen is that there will be an
; extra interrupt, which will be ignored (because SIZE_TDATA will be
; zero again by then).
BENTRY COM_PUTC <UNIT,OCHAR>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV AX,[SI].TBUF_SIZE ; Get the size of the x-mit buffer.
SUB AX,[SI].SIZE_TDATA ; Subtract the number of bytes used.
JE L24 ; No more free space.
MOV ES,[SI].TBUF_SEG ; Get transmit buffer segment number.
MOV DI,[SI].TBUF_OFF ; Get transmit buffer offset.
MOV BX,[SI].END_TDATA ; BX points to free space.
MOV AL,OCHAR ; Move data from stack to x-mit buffer.
MOV ES:[BX+DI],AL
INC BX ; Increment END_TDATA to point to free space.
CMP BX,[SI].TBUF_SIZE ; See if past end.
JB L20 ; If not then skip.
XOR BX,BX ; Adjust to beginning.
L20: MOV [SI].END_TDATA,BX ; Save new END_TDATA.
; (Implicit interlock with interrupt level):
INC [SI].SIZE_TDATA ; One more character in x-mit buffer.
MOV DX,[SI].COMX_BASE
ADD DX,IER ; See if tx interrupts are enabled.
IN AL,DX
AND AL,TXINT
OR AL,AL
JNZ L22
MOV AL,RXINT+TXINT ; If not then set them.
OUT DX,AL
L22: ; (End of implicit interlock)
MOV AX,TRUE ; Indicate all's OK.
JMP SHORT L26 ; Go join common return code.
L24: MOV AX,FALSE ; No more space in buffer.
L26: BEND COM_PUTC
.SBHED <COM_LOOPC -- Write to the Input Buffer>
; bool /* Returns FALSE if no more room. */
; com_loopc(unit, ch) /* Writes a character to the input buffer. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
; char ch; /* The character to write. */
BENTRY COM_LOOPC <UNIT,OCHAR>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
CLI ; ******* Disable Interrupts *******
MOV DX,[SI].RBUF_SIZE ;;; Get the size of the receive buffer.
CMP [SI].SIZE_RDATA,DX ;;; See if any room for more data.
JAE L32 ;;; If no room then quit.
MOV ES,[SI].RBUF_SEG ;;; Get receive buffer segment number.
MOV DI,[SI].RBUF_OFF ;;; Get receive buffer offset.
MOV BX,[SI].END_RDATA ;;; BX points to free space.
MOV AL,OCHAR ;;; Get data.
MOV ES:[BX+DI],AL ;;; Send data to buffer.
INC [SI].SIZE_RDATA ;;; Got one more character.
INC BX ;;; Increment END_RDATA pointer.
CMP BX,DX ;;; See if gone past end.
JL L30 ;;; If not then skip,
XOR BX,BX ;;; else adjust to beginning.
L30: MOV [SI].END_RDATA,BX ;;; Save value.
STI ;;; ******* Enable Interrupts *******
MOV AX,TRUE ;;; Indicate success.
JMP SHORT L34 ; Go join common return code.
L32: STI ;;; ******* Enable Interrupts *******
MOV AX,FALSE ;;; Indicate no more room.
L34: BEND COM_LOOPC
.SBHED <COM_BON -- Turn On BREAK Condition>
; void
; com_bon(unit) /* Turns on BREAK. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
;
; Causes the UART to send the BREAK condition.
BENTRY COM_BON <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV DX,[SI].COMX_BASE
ADD DX,LCR
MOV AL,BREAK ; Set break condition.
OUT DX,AL
BEND COM_BON
.SBHED <COM_BOFF -- Turn Off BREAK Condition>
; void
; com_boff(unit) /* Turns off BREAK. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
;
; Returns the transmit line to the normal state.
BENTRY COM_BOFF <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV DX,[SI].COMX_BASE
ADD DX,LCR
MOV AL,MODE ; Restore the line control register.
OUT DX,AL
BEND COM_BOFF
.SBHED <COM_BREAK -- Complete BREAK Sequence>
; void
; com_break(unit) /* Sends complete BREAK sequence. */
; int unit; /* 1 ==> COM1:, 2 ==> COM2:. */
BENTRY COM_BREA <UNIT>
MOV AX,UNIT ; Point to COM1: or COM2: save area.
CALL SET_SI ;
MOV DX,[SI].COMX_BASE
ADD DX,LCR
MOV AL,BREAK ; Set break condition.
OUT DX,AL
MOV CX,0 ; Wait a while.
WAIT: LOOP WAIT
MOV AL,MODE ; Restore the line control register.
OUT DX,AL
BEND COM_BREA
ENDPS
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -