📄 llcom3.asm
字号:
TITLE LLCOM3 - Communications Interface for DOS 3
PAGE 56,132
;***
; LLCOM3.ASM - GW-BASIC Communications Interface for DOS 3
;
; Copyright <C> 1986, Microsoft Corporation
;
;Purpose:
; This module supports the initialization/open, read, write, line
; status check, termination and interrupt/monitor service for
; communication lines. In DOS3, BASIC handles the actual low level
; functions itself, whereas in DOS5, BASIC takes advantage of the dos
; function calls.
;
;******************************************************************************
INCLUDE switch.inc ;feature switches [new file]
INCLUDE rmacros.inc
; Code Segments
useSeg DV_TEXT
useSeg RT_TEXT
useSeg EV_TEXT
; Data segments
useSeg _DATA
useSeg _BSS
INCLUDE seg.inc
INCLUDE ibmunv.inc
INCLUDE baslibma.inc ; skip macro
INCLUDE comdcb.inc ;comm data control block (DCB) structure
INCLUDE event.inc
INCLUDE idmac.inc
SUBTTL local constant/structure definitions
QUE_CTRL_LEN = QUSIZE ;Length of Queue Control Block
RS232B = 400H ; X'400' RS232 Card(s) I/O addr Save area.
COMDEB STRUC
DEVID DB ? ;device id number
COMBSY DB 0 ; nonzero if Tx Interrupts active
COMRTS DB 2 ; Request to Send Mask bit if RTS wanted
COMCTS DW ? ; CTS timeout count
COMDSR DW ? ; DSR timeout count
COMRLS DW ? ; CD (RLSD) timeout count
COMOVF DB 0 ; Nonzero on overflow
COMERR DB 0 ; Nonzero if I/O error
MSRERR DB 0 ; Nonzero if lost DSR
MSREG DB ? ; Contents of Modem Status Register
PARTYP DB ? ; Parity type (internal form see B$INICOM)
BYTSIZ DB ? ; Size of byte(internal form - see B$INICOM)
CTRLZ DB 0 ; ASCII mode EOF flag
BINMOD DB 0 ; BIN/ASCII (Z/NZ) flag
COMPE DB ? ;nonzero if PE option selected
RCVSEG DW ? ;segment of receive queue buffer
XMTSEG DW ? ;segment of transmit queue buffer
COMDEB ENDS
PAUSE MACRO ;macro to insure that an instruction
JMP $+2 ;fetch occurs between IN and/or OUT
ENDM ;instructions on the Salmon machine
SUBTTL data definitions
page
sBegin _DATA
COMM1 COMDEB <> ;COM1 device equipment block
COMM2 COMDEB <> ;COM2 device equipment block
sEnd _DATA
sBegin _BSS
externB b$COFlag ; non-zero indicates called from COM_OPEN
externB b$EventFlags ; misc. event flags
externW b$IPOLKEY ; vector for B$POLKEY routine
externB b$DOS_INT_MASK ;defined in LLINI.ASM
externW b$ComPort ;I/O port address table
staticB COM1_VECTOR,?,2 ; old COM1 vector is saved here
staticB COM2_VECTOR,?,2 ; old COM2 vector is saved here
externW b$pTrapEvent ; pointer to B$TrapEvent if events linked in
Q_IN QUE_CTRL_BLOCK <> ;Input Que Ctrl Data Blk Ptr
Q_IN2 QUE_CTRL_BLOCK <>
Q_OUT QUE_CTRL_BLOCK <> ;Output Que Ctrl Data Blk Ptr
Q_OUT2 QUE_CTRL_BLOCK <>
sEnd _BSS
SUBTTL code externals
page
sBegin RT_TEXT
externW b$BASDSG ; in code segment to record basic data seg
sEnd Rt_TEXT
sBegin EV_TEXT
externNP B$BREAK_CHK ;check ctrl-break
sEnd EV_TEXT
sBegin DV_TEXT
externNP B$INIQUE
externNP B$QUE
externNP B$DQUE
sEnd DV_TEXT
SUBTTL Comm initialization
page
assumes CS,DV_TEXT
sBegin DV_TEXT
;***
;B$INICOM - initialize the COM device
;[OEM documentation is in file LLCOM5]
;
;Purpose:
; Initialize the requested RS232 port, if present, and set
; up the port with the given attributes, when they are valid.
; The memory for the input and output queues has already been
; allocated, but will need to be initialized. This routine is
; passed the address of a device control block (DCB) as defined
; in the file comdcb.inc.
;
; The signals RLSD, CTS, and DSR should bbe ignored by all
; COM routines if the curresponding Support of Timeout Flags
; by BIOS is optional.
;
;IBM Communications Error Reporting
; The following scheme is used in reporting errors:
;
; 1.) OPEN returns any OPEN specific erros and resets the input buffer.
; 2.) CLOSE, EOF, LOC, LOF, and device polling for trapping returns
; no error.
; 3.) INPUT returns only input errors.
; 4.) WRITE and PRINT return only output errors.
; 5.) Errors are reported at the earliest possible BASIC I/O
; statement (with respect to the rules above).
; 6.) I/O errors are cleared when they are reported to the runtime
; code and the byte being read or written is not removed from
; or added to the buffer.
; 7.) If an error occurs when reading a byte and the port is opened
; with less than eight data bits, then set the high bit of that
; byte.
;
;Entry:
; BX = points to initialized DCB.
;
;Exit:
; AH = 0 if no errors occured
; = 4 if timeout while waiting for DSR signal
; (will cause a DEVICE TIMEOUT error)
; = 5 if timeout while waiting for RLSD signal
; (will cause a DEVICE TIMEOUT error)
; = FE if device unavailable via BIOS data location
; (will cause a DEVICE NOT AVAILABLE error)
; = FF if requested mode is not supported
; (will cause a BAD FILE NAME error)
;
;Uses:
; Per Convention
;
;Exceptions:
; None.
;*************************************************************************
;
;Algorithm:
; If DOS 3,
; check for valid device (0 or 1)
; if card is present enable it else ERROR
; initialize buffer control blocks
; get parity and bytesize
; if parity=NONE and bytesize=4 then ERROR
; if parity<>NONE and bytesize=8 then ERROR
; map parity to easier value
; (0->0, 1->1, 2->3, 3->5, 4->7)
; save new parity value
; get baud rate
; determine and save divisor for setting baud rate
; set up card with appropriate characteristics
cProc B$INICOM,<PUBLIC,NEAR>,<DI,SI> ; save DI, SI
cBegin
MOV AH,0FFH ;assume requested mode unsupported
MOV DL,[BX].CD_DEVID ;get device i.d.
CMP DL,1 ;is i.d. > 1
JBE DeviceOK ;jump if device is 0 or 1
JMP INIRET ;near jump to error
DeviceOK:
MOV SI,OFFSET DGROUP:COMM2 ;get com2 deb (dcb in dos5)
JZ IdOK ; br. if com2
MOV SI,OFFSET DGROUP:COMM1 ;get com1 deb (dcb in dos5)
IdOK:
MOV [SI].DEVID,DL ;save device i.d.
XOR DH,DH
MOV DI,DX ;copy offset
SHL DI,1 ;make word index (COM1=0 / COM2=2)
CALL InitComPort ;initialize the port, int vector, etc.
OR AX,AX ;test if error reported
JZ InitComAvail ;if zero, then initialization successful
JMP INIRET ;else exit with CH set with 0FEH for DNA
InitComAvail:
MOV DX,[BX].CD_RXSIZ ;get size of the receive buffer
MOV CX,[BX].CD_TXSIZ ;get size of the transmit buffer
CALL BUFFINI ;initialize buffer stuff
MOV CL,[BX].CD_CMFLG ;get com flags
MOV [SI].COMBSY,0 ;not initially transmitting
MOV [SI].COMRTS,2 ;set RTS mask
AND [SI].COMRTS,CL ;should RTS be enabled?
MOV [SI].COMPE,4D ;set PE mask
MOV CL,[BX].CD_CMFLG ;get com flags
AND [SI].COMPE,CL ;should PE be enabled?
MOV CX,[BX].CD_RXSEG ;get segment of receive comm buffer
MOV [SI].RCVSEG,CX ;set segment of receive comm buffer
MOV CX,[BX].CD_TXSEG ;get segment of transmit comm buffer
MOV [SI].XMTSEG,CX ;set segment of transmit comm buffer
XOR CX,CX ;assume no timeout value
CMP [BX].CD_RLSTO,CX ;test if RLSD timeout to be tested
JZ InicomNoCD ;if not, then leave timeout as zero
MOV CX,[BX].CD_OPNTO ;get OPEN timeout value
InicomNoCD:
MOV [SI].COMRLS,CX ;set RLSD timeout count
XOR CX,CX ;assume no timeout value
CMP [BX].CD_DSRTO,CX ;test if DSR timeout to be tested
JZ InicomNoDS ;if not, then leave timeout as zero
MOV CX,[BX].CD_OPNTO ;get OPEN timeout value
InicomNoDS:
MOV [SI].COMDSR,CX ;set DSR timeout count
MOV [SI].COMCTS,0 ;don't check CTS during initialization
MOV [SI].COMOVF,0 ;clear overflow flag
MOV [SI].COMERR,0 ;clear I/O error flag
MOV [SI].MSRERR,0 ;clear modem status flag
MOV [SI].MSREG,0 ;clear modem error value
MOV DH,[BX].CD_BYTSZ ;get number of data bits in byte in [DH]
MOV DL,[BX].CD_PARIT ;get requested parity in [DL]
OR DL,DL ;is parity "NONE"?
JZ ChkParity ; br. if so
CMP DH,8 ;is byte size 8?
JNZ ByteSizeOK ; Brif not
ErrorJmp: ; for jump out of range
MOV AH,0FFH ; require mode not support
JMP SHORT TRMRET ; else error (reset interrupt vectors)
ChkParity:
CMP DH,4 ;was it no parity and byte size 4?
JZ ErrorJmp ; Brif yes, error
ByteSizeOK: ; the following is set line status and baud
; rate
SUB DH,5 ;shift byte size to bits 0&1
MOV [SI].BYTSIZ,DH ;save byte size
MOV DH,DL ;copy parity
CMP DL,2 ;does parity need adjustment?
JB ParityOK ; br. if not
MOV DH,1 ;[DH]=new parity value
DEC DL ;[DL] = Loop count
AdjParLoop: ; keep adjust
INC DH ;parity 0 -> 0, parity 1 -> 1
INC DH ;parity 2 -> 3
DEC DL ;parity 3 -> 5
JNZ AdjParLoop ; parity 4 -> 7
ParityOK:
MOV [SI].PARTYP,DH ;store adjusted parity value
MOV AL,[BX].CD_STOPB ;get number of stop bits 0=1,1/2= .GT. 1
DEC AL ;Find out if 0 stop bits?
JS StopBitOK ; Brif sign, must have been zero stop bits
;Request > 1 stop bit if by ORing with BIT 2 on
OR BYTE PTR [SI].BYTSIZ,00000100B
;[BYTSIZ] is number of data bits and stop bits
StopBitOK:
MOV CX,[BX].CD_BAUDR ;get requested baud rate
CALL GETDIV ;get necessary divisor to support baud rate
MOV AH,0FFH ;set error code if baud not supported
JCXZ TRMRET ;reset vector if not and give error
CALL SETEM ;set up RS232 card
OR AH,AH ;test if error occurred
JNZ TRMRET ;if so, then reset vector and jump
MOV CX,[BX].CD_RLSTO ;get RLSD time out count
MOV [SI].COMRLS,CX ;set RLSD timeout count
MOV CX,[BX].CD_DSRTO ;get DSR time out count
MOV [SI].COMDSR,CX ;set DSR timeout count
MOV CX,[BX].CD_CTSTO ;get CTS timeout count
MOV [SI].COMCTS,CX ;store real CTS timeout count
JMP SHORT INIRET
TRMRET:
PUSH AX ;save registers...
PUSH BX
PUSH CX
PUSH DX
XOR CX,CX ; reset DTR
MOV [b$COFlag],CL ; Clear flag for next call to MSRWAT!
CALL CLSMSR ;full termination - bring lines down
POP DX ;restore registers...
POP CX
POP BX
POP AX
CMP AH,0FCh ; was CTRL-BREAK detected in MSRWAT?
JNE INIRET ; brif not
CALL B$BREAK_CHK ; process CTRL-BREAK -- should not return
DbHalt DV_TEXT,<CTRL-BREAK lost during COM OPEN in B$INICOM> ;
INIRET:
cEnd ;B$INICOM ; pop register and exit
page
;***
; InitComPort - Initialize a communications port
;
; Purpose:
; First tests if device is available through the BIOS data location
; (0:400H for COM1 - 0:402H for COM2) which contains the I/O data port.
; If the device is available, then set up the ISR vector and initialize
; the hardware.
; Entry:
; DI = device index (COM1=0 / COM2=2)
; Exit:
; AH = 0FEH if device is not available.
; AX = 0 if device is available.
; Modifies:
; AX, DX.
; Exceptions:
; None.
;*************************************************************************
; First, determine if the device is available by examining the
; BIOS location of the I/O port number. If zero, it is unavailable.
; If nonzero, clear the value for exclusive use and store the port
; address in the DCB.
InitComPort:
PUSH DS ;save DGROUP on stack
XOR DX,DX ;clear DX for use as BIOS segment
MOV DS,DX ;set segment to access BIOS data area
XCHG DX,DS:RS232B[DI] ;get I/O address in DX, clear BIOS data
POP DS ;restore DGROUP from stack
MOV b$ComPort[DI],DX ;store I/O port address in DCB
OR DX,DX ;test if device is available
JNZ ComPortAvail ;if so, then jump
MOV AH,0FEH ;set CH for "device not ready" error
RET ;do a near return to caller with CH=0FEH
; With the device index in DI and the I/O port address in DX,
; initialize the hardware and interrupt vectors.
ComPortAvail:
PUSH BX ;save registers...
PUSH SI
MOV SI,OFFSET CODE:COM_INT1 ;COM1: Service addr
MOV BX,OFFSET DGROUP:COM1_VECTOR ;get offset for saved vector
OR DI,DI ;test if COM1 is being initialized
JZ ComPortStart ;if so, then start the initialization
MOV SI,OFFSET CODE:COM_INT2 ;COM2: Service addr
MOV BX,OFFSET DGROUP:COM2_VECTOR ;get offset for saved vector
ComPortStart:
MOV CX,0EF00H+IRQ4/4 ;8259 enable mask/int number (primary)
CMP b$ComPort[DI],03F8H ;test if primary asynch card
JE ComPortPrimary ;if so, then jump
MOV CX,0F700H+IRQ3/4 ;8259 enable mask/int number (secondary)
ComPortPrimary:
CLI ;disable interrupts during initialization
ADD DX,4 ;Modem Control Register
MOV AL,1 ; AL = 1 = DTR active; RTS off
OUT DX,AL ; Turn of RTS; leave DTR active
PAUSE ;Delay
SUB DX,3 ;Interrupt Enable Register
DEC AX ; AL = 0 = disable interrupts
OUT DX,AL ;Turn off interrupts
MOV DX,SI ;Set IRQ-4 Vector
MOV SI,BX ;get pointer to save vector addr
MOV AL,CL ;get interrupt number
MOV AH,35H ;get vector function call
INT 21H ;get vector in ES:BX
MOV [SI],BX ;store away current vector offset
MOV [SI+2],ES ;and likewise the current segment
PUSH DS ;get BASIC data segment
POP ES ;and restore ES with it
PUSH CS ;DS = CS
POP DS
MOV AH,25H ;DOS Set Vector Function
INT 21H
PUSH ES ;save BASIC data segment
POP DS ;Original DS
MOV AH,CH ;get mask to AND with current one
IN AL,INTA1 ;get interrupt mask for 8259
AND AL,AH ;include IRQ-3 or IRQ-4
PAUSE ;to give settle time for I/O bus signals
OUT INTA1,AL ;output the new interrupt mask
XOR AX,AX ;clear AX for no error
STI ;re-enable interrupts
POP SI ;restore registers...
POP BX
RET ;near return to caller
;***
; BUFFINI - initialize COM buffers
;
; Purpose:
; This routine is responsible for initializing the queue control
; block field elements for the input and output buffers.
; Entry:
; DX = size of receive buffer.
; SI = pointer to deb.
; Exit:
; None.
; Modifies:
; None.
; Exceptions:
; None.
;***************************************************************************
BUFFINI:
PUSH SI
PUSH BX
PUSH AX
MOV BX,OFFSET DGROUP:Q_OUT ;get output QCB for COM1
CMP [SI].DEVID,0 ;is this com1?
JZ BUFF2 ;jump if so
MOV BX,OFFSET DGROUP:Q_OUT2 ;[BX]= Out QCB
BUFF2:
XOR AX,AX ;each buffer starts at zero
MOV [BX].QUEBOT,AX ;Store Out buffer Bottom addr
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -