mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2878 lines
72 KiB
2878 lines
72 KiB
page,132
|
|
;---------------------------Module-Header-------------------------------;
|
|
; Module Name: IBMSETUP.ASM
|
|
;
|
|
; Copyright (c) Microsoft Corporation 1985-1990. All Rights Reserved.
|
|
;
|
|
; General Description:
|
|
;
|
|
; History:
|
|
; sudeepb 10-Jan-1993 changed the costly cli/sti with non-trapping
|
|
; FCLI/FSTI macros
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
title IBMSetup - IBM PC, PC-XT, PC-AT, PS/2 Communications Interface
|
|
|
|
.xlist
|
|
include cmacros.inc
|
|
include comdev.inc
|
|
include ins8250.inc
|
|
include ibmcom.inc
|
|
include BIMODINT.INC
|
|
include vint.inc
|
|
.list
|
|
|
|
|
|
EBIS_Sel1 equ SIZE Bimodal_Int_Struc
|
|
EBIS_Sel2 equ EBIS_Sel1 + (SIZE EBIS_Sel_Struc)
|
|
|
|
externA __WinFlags
|
|
|
|
externFP GetSystemMsecCount
|
|
externFP CreateSystemTimer
|
|
externFP AllocCStoDSAlias
|
|
externFP LockSegment
|
|
externFP UnlockSegment
|
|
externFP FreeSelector
|
|
externFP GetSelectorBase
|
|
externFP GetModuleHandle
|
|
externFP GetProcAddress
|
|
externFP GetPrivateProfileInt
|
|
externFP GetPrivateProfileString
|
|
externFP GetAppCompatFlags
|
|
externFP WowCloseComPort
|
|
|
|
externNP $RECCOM
|
|
|
|
externA __0040H
|
|
externA __F000h
|
|
|
|
externB IRQhooks
|
|
|
|
IF 0
|
|
externD OldIntVecIntB
|
|
externD OldIntVecIntC
|
|
externD OurIntVecIntB
|
|
externD OurIntVecIntC
|
|
ENDIF
|
|
|
|
externB szMessage
|
|
externB pLPTByte
|
|
externB szCOMMessage
|
|
externB pCOMByte
|
|
externB _szTitle
|
|
|
|
|
|
MULTIPLEX equ 2Fh ; multiplex interrupt number
|
|
GET386API equ 1684h ; Get API entry point from VxD
|
|
VPD equ 000Fh ; device ID of VPD device
|
|
VPD_GETPORT equ 0004h ; function: assign port to current VM
|
|
VPD_RELPORT equ 0005h ; function: release port
|
|
VCD equ 000Eh ; device ID of VCD device
|
|
VCD_GETVER equ 0000h ; get version API
|
|
VCD_GETPORT equ 0004h ; function: assign port to current VM
|
|
VCD_RELPORT equ 0005h ; function: release port
|
|
VCD_STEALPORT equ 0006h
|
|
VPICD equ 0003h ; device ID of VPICD device
|
|
|
|
POSTMESSAGE equ 110 ; export ordinal of PostMessage()
|
|
MESSAGEBOX equ 1 ; export ordinal of MessageBox()
|
|
MB_TASKMODAL equ 2000h
|
|
MB_YESNO equ 0004h ; messagebox flags
|
|
MB_ICONEXCLAMATION equ 0030h
|
|
IDYES equ 6
|
|
|
|
|
|
createSeg _INTERRUPT,IntCode,word,public,CODE
|
|
sBegin IntCode
|
|
assumes cs,IntCode
|
|
|
|
externFP FakeCOMIntFar
|
|
externFP TimerProc
|
|
externFP Control
|
|
externFP COMHandler
|
|
externFP APIHandler
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
externW RM_IntDataSeg
|
|
externFP RM_APIHandler
|
|
externFP Entry_From_RM
|
|
externD RM_CallBack
|
|
ENDIF
|
|
|
|
sEnd IntCode
|
|
|
|
page
|
|
sBegin Data
|
|
|
|
externB lpCommBase
|
|
externB CommBaseX
|
|
externB lpCommIrq
|
|
externB CommIrqX
|
|
externB lpCommFifo
|
|
externB CommFifoX
|
|
externB lpCommDSR
|
|
externB CommDSRx
|
|
|
|
externB lpCommSection
|
|
externB lpSYSTEMINI
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
; Reserve data space for COM ports
|
|
;
|
|
DefineCommX MACRO num
|
|
public Comm&num
|
|
Comm&num label byte
|
|
db num-1
|
|
.errnz DCB_Id
|
|
db ((DCBSize+1) AND 0FFFEh)-1 DUP (0) ; ComDCB
|
|
dw 0 ; ComErr
|
|
dw 0 ; Port
|
|
dw 0 ; NotifyHandle
|
|
dw 0 ; NotifyFlags
|
|
dw -1 ; RecvTrigger
|
|
dw 0 ; SendTrigger
|
|
.errnz IRQhook - SendTrigger - 2
|
|
db (SIZE ComDEB) - IRQhook DUP(0)
|
|
.errnz $ - Comm&num - (SIZE ComDEB)
|
|
Declare_PM_BIS 0,Control,COMHandler,APIHandler,_INTERRUPT,_DATA
|
|
db (SIZE EBIS_Sel_Struc) * 2 DUP(0) ; res space for 2 selectors
|
|
ENDM
|
|
DW_OFFSET_CommX MACRO num
|
|
dw DataOFFSET Comm&num
|
|
ENDM
|
|
|
|
|
|
??portnum = 1
|
|
REPT MAXCOM+1
|
|
DefineCommX %??portnum
|
|
??portnum = ??portnum+1
|
|
ENDM
|
|
|
|
PUBLIC COMptrs ; table of offsets to CommX's declared above
|
|
COMptrs label word
|
|
??portnum = 1
|
|
REPT MAXCOM+1
|
|
DW_OFFSET_CommX %??portnum
|
|
??portnum = ??portnum+1
|
|
ENDM
|
|
|
|
PURGE DefineCommX
|
|
PURGE DW_OFFSET_CommX
|
|
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
; Reserve data space for LPT ports
|
|
;
|
|
DefineLPTx MACRO num
|
|
public LPT&num
|
|
LPT&num label byte
|
|
db num-1+LPTx
|
|
.errnz DCB_Id
|
|
db ((DCBSize+1) AND 0FFFEh)-1 DUP (0) ; xComDCB
|
|
dw 0 ; xComErr
|
|
dw 0 ; xPort
|
|
dw 0 ; xNotifyHandle
|
|
dw 0 ; xNotifyFlags
|
|
dw -1 ; xRecvTrigger
|
|
dw 0 ; xSendTrigger
|
|
IF num LE 3
|
|
dw LPTB + (num-1)*2
|
|
ELSE
|
|
dw 0 ; BIOSPortLoc
|
|
ENDIF
|
|
.errnz $-LPT&num - SIZE LptDEB
|
|
ENDM
|
|
|
|
??portnum = 1
|
|
REPT MAXLPT+1
|
|
DefineLPTx %??portnum
|
|
??portnum = ??portnum+1
|
|
ENDM
|
|
|
|
PURGE DefineLPTx
|
|
|
|
page
|
|
|
|
PUBLIC $MachineID, Using_DPMI
|
|
$MachineID db 0 ;IBM Machine ID
|
|
Using_DPMI db 0 ; 0FFh, if TRUE
|
|
|
|
ALIGN 2
|
|
|
|
PUBLIC activeCOMs
|
|
activeCOMs dw 0
|
|
|
|
PUBLIC lpPostMessage, lpfnMessageBox, lpfnVPD, fVPD
|
|
|
|
lpPostMessage dd 0
|
|
lpfnMessageBox dd 0
|
|
|
|
lpfnVPD dd 0 ; far pointer to win 386 VPD entry point
|
|
lpfnVCD dd 0 ; far pointer to win 386 VCD entry point
|
|
lpfnVPICD dd 0 ; far pointer to win 386 VPICD entry point
|
|
PUBLIC VCD_int_callback
|
|
VCD_int_callback df 0 ; VCD returns the address for this callback
|
|
; on every call to acquire a COM port, but
|
|
; it is always the same address, so we will
|
|
; just maintain it globally.
|
|
fVPD db 0 ; 0-not checked, 1 vpd present, -1 no vpd
|
|
fVCD db 0 ; 0-not checked, 1 vcd present, -1 no vcd
|
|
fVPICD db 0 ; 0-not checked, 1 vpicd present, -1 no vpicd
|
|
|
|
szUser db 'USER',0
|
|
|
|
|
|
default_table db 4, 3, 4, 3, 0 ; Default IRQ's (COM3 default is changed to
|
|
; 3 for PS/2's during LoadLib)
|
|
|
|
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
RM_Call_Struc Real_Mode_Call_Struc <>
|
|
ENDIF
|
|
|
|
IFDEF DEBUG_TimeOut
|
|
%OUT including code to display MsgBox, if closing comm with data in buffer
|
|
szSendTO db 'TimedOut CloseComm with data in buffer. Retry?', 0
|
|
ENDIF
|
|
|
|
|
|
sEnd Data
|
|
|
|
ROMBios segment at 0F000h
|
|
org 0FFFEh
|
|
|
|
MachineID label byte
|
|
RomBios Ends
|
|
|
|
|
|
sBegin Code
|
|
assumes cs,Code
|
|
assumes ds,Data
|
|
|
|
page
|
|
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
;----------------------------Private-Routine----------------------------;
|
|
; SegmentFromSelector
|
|
;
|
|
; Converts a selector to a segment...note that this routine assumes
|
|
; the memory pointed to by the selector is below the 1Meg line!
|
|
;
|
|
; Params:
|
|
; AX = selector to convert to segment
|
|
;
|
|
; Returns:
|
|
; AX = segment of selector given
|
|
;
|
|
; Error Returns:
|
|
; None
|
|
;
|
|
; Registers Destroyed:
|
|
; CX
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public SegmentFromSelector
|
|
SegmentFromSelector proc far
|
|
|
|
.286
|
|
push dx
|
|
cCall GetSelectorBase,<ax> ;DX:AX = segment of selector
|
|
shr ax, 4
|
|
shl dl, 4
|
|
or ah, dl ;AX now points to interrupt *segment*
|
|
pop dx
|
|
ret
|
|
.8086
|
|
|
|
SegmentFromSelector endp
|
|
ENDIF
|
|
page
|
|
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
; Get_API_Entry
|
|
;
|
|
; entry - BX = device id
|
|
; DS:DI -> DWORD for proc address
|
|
; exit - Z flag set, if failed
|
|
;
|
|
Get_API_Entry proc near
|
|
|
|
push di
|
|
xor di, di
|
|
mov es, di
|
|
mov ax, GET386API
|
|
int MULTIPLEX
|
|
mov ax, di
|
|
pop di
|
|
mov [di], ax
|
|
mov [di+2], es
|
|
or ax, [di+2]
|
|
ret
|
|
|
|
Get_API_Entry endp
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; Contention_Dlg
|
|
;
|
|
; If running under Win386, this routine can be called to ask the user to
|
|
; resolve contention for a COM or LPT port.
|
|
;
|
|
; entry - CX is offset of message string for dialog box
|
|
;
|
|
; exit - Z flag set, if user specified that Windows should steal the port
|
|
|
|
Contention_Dlg proc near
|
|
PUBLIC Contention_Dlg
|
|
|
|
xor ax,ax
|
|
push ax ; hwndOwner
|
|
push ds
|
|
push cx ; message ptr
|
|
|
|
cmp wo lpfnMessageBox[2], 0 ;Q: ptr to MessageBox proc valid?
|
|
jne short gmbp_done ; Y: we can call it
|
|
push ds ; N: get module handle of USER
|
|
lea ax, szUser
|
|
push ax
|
|
cCall GetModuleHandle
|
|
|
|
push ax ; module handle
|
|
mov ax, MESSAGEBOX
|
|
cwd
|
|
push dx
|
|
push ax
|
|
cCall GetProcAddress
|
|
mov wo lpfnMessageBox[0], ax ; save received proc address
|
|
mov wo lpfnMessageBox[2], dx
|
|
gmbp_done:
|
|
|
|
push ds
|
|
lea ax, _szTitle
|
|
push ax
|
|
mov ax, MB_ICONEXCLAMATION or MB_YESNO or MB_TASKMODAL
|
|
push ax
|
|
cCall lpfnMessageBox
|
|
cmp ax, IDYES ; user allows us to take the port?
|
|
ret
|
|
Contention_Dlg endp
|
|
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; GetPort386
|
|
;
|
|
; If running under Win386, tell the VPD to assign an LPT port to us.
|
|
; The comm driver will handle contention.
|
|
;
|
|
; entry - DI contains offset in ROM area of port...
|
|
; 8 - LPT1, A - LPT2, etc
|
|
;
|
|
; exit - registers saved, carry = clear if OK to proceed, set if
|
|
; user won't allow assignment of port or Win386 error
|
|
;
|
|
|
|
GetPort386 proc near
|
|
public GetPort386
|
|
|
|
cmp fVPD, 0
|
|
jl getport_VPDNotInstalled
|
|
jnz short getport_CallVPD
|
|
|
|
push di
|
|
mov bx, VPD
|
|
mov di, DataOFFSET lpfnVPD
|
|
call Get_API_Entry
|
|
pop di
|
|
jnz short getport_CallVPD
|
|
mov fVPD, -1
|
|
|
|
getport_VPDNotInstalled:
|
|
clc
|
|
jmp short getport_exit
|
|
|
|
getport_CallVPD:
|
|
mov fVPD, 1
|
|
push di
|
|
sub di, LPTB
|
|
shr di, 1 ; turn DI into port number
|
|
|
|
xor ax, ax
|
|
mov dx, VPD_GETPORT
|
|
mov cx, di
|
|
call [lpfnVPD]
|
|
jnc getport_gotit
|
|
|
|
; port owned by another VM... ask the user for it
|
|
|
|
add cl, '1' ; fix up the port name...
|
|
mov pLPTByte, cl ; HACK HACK HACK
|
|
lea cx, szMessage
|
|
call Contention_Dlg
|
|
jnz getport_userwontallow
|
|
|
|
mov ax, 1 ; tell win386 we really do want it
|
|
mov cx, di ;
|
|
mov dx, VPD_GETPORT ;
|
|
call [lpfnVPD] ; return with C set or clear...
|
|
jmp short getport_gotit
|
|
|
|
getport_userwontallow:
|
|
stc
|
|
|
|
getport_gotit:
|
|
pop di
|
|
|
|
getport_exit:
|
|
ret
|
|
|
|
GetPort386 endp
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; ReleasePort386
|
|
;
|
|
; If running under Win386, tell the VPD to deassign an LPT port.
|
|
;
|
|
; entry - DS:SI -> COMDEB
|
|
;
|
|
|
|
ReleasePort386 proc near
|
|
|
|
cmp fVPD, 1
|
|
jne release_noVPD
|
|
|
|
xor cx, cx
|
|
mov cl, [si.DCB_id]
|
|
and cl, NOT LPTx ; clear high bit
|
|
mov dx, VPD_RELPORT
|
|
call [lpfnVPD]
|
|
|
|
release_noVPD:
|
|
ret
|
|
|
|
ReleasePort386 endp
|
|
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; GetCOMport386
|
|
;
|
|
; If running under Win386, tell the VCD to assign a COM port to us.
|
|
; The comm driver will handle contention.
|
|
;
|
|
; entry - DS:SI -> COMDEB
|
|
;
|
|
; exit - registers saved, carry = clear if OK to proceed, set if
|
|
; user won't allow assignment of port or Win386 error
|
|
;
|
|
.386
|
|
GetCOMport386 proc near
|
|
public GetCOMport386
|
|
|
|
push es
|
|
pushad
|
|
cmp fVCD, 0
|
|
jl short getcomport_VCDNotInstalled
|
|
jnz short getcomport_CallVCD
|
|
|
|
mov bx, VCD
|
|
mov di, DataOFFSET lpfnVCD
|
|
call Get_API_Entry
|
|
jz short getcomport_checknoVCD
|
|
|
|
mov dx, VCD_GETVER
|
|
call [lpfnVCD]
|
|
cmp ax, 30Ah ;Q: 3.1 or greater?
|
|
jae short getcomport_CallVCD ; Y:
|
|
|
|
getcomport_checknoVCD:
|
|
mov fVCD, -1
|
|
|
|
getcomport_VCDNotInstalled:
|
|
clc
|
|
jmp short getcomport_exit
|
|
|
|
getcomport_CallVCD:
|
|
mov fVCD, 1
|
|
|
|
mov ax, 10b ; flag ring0 int handler
|
|
call VCD_GetPort_API
|
|
jnc short getcomport_success ; jump if acquire worked
|
|
jnz short getcomport_noport ; jump if port doesn't exist
|
|
|
|
; port owned by another VM... ask the user for it
|
|
|
|
mov cl, [si.DCB_id]
|
|
add cl, '1' ; fix up the port name...
|
|
mov pCOMByte, cl
|
|
lea cx, szCOMMessage
|
|
call Contention_Dlg
|
|
stc
|
|
jnz short getcomport_exit
|
|
|
|
mov ax, 11b ; tell win386 we really do want it
|
|
call VCD_GetPort_API
|
|
jc short getcomport_exit
|
|
|
|
getcomport_success:
|
|
mov dword ptr [VCD_int_callback], edi
|
|
mov word ptr [VCD_int_callback+4], cx
|
|
mov [si.VCD_data], ebx
|
|
xchg ax, [si.Port]
|
|
or ax, ax ;Q: already had port base?
|
|
jnz short getcomport_exit ; Y: don't update vector #, or FIFO
|
|
mov [si.IntVecNum], dl
|
|
call GetPortFlags
|
|
clc
|
|
|
|
getcomport_exit:
|
|
popad
|
|
pop es
|
|
ret
|
|
|
|
getcomport_noport:
|
|
mov [si.Port], -1
|
|
jmp getcomport_exit
|
|
|
|
GetCOMport386 endp
|
|
|
|
VCD_GetPort_API proc near
|
|
mov dx, VCD_GETPORT
|
|
xor cx, cx
|
|
mov cl, [si.DCB_Id] ; cx = port #
|
|
mov di, VCDflags ; offset from start of DEB
|
|
call [lpfnVCD]
|
|
ret
|
|
VCD_GetPort_API endp
|
|
.8086
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; ReleaseCOMport386
|
|
;
|
|
; If running under Win386, tell the VCD to deassign a COM port.
|
|
;
|
|
; entry - DS:SI -> COMDEB
|
|
;
|
|
|
|
ReleaseCOMport386 proc near
|
|
|
|
ifndef WOW
|
|
cmp fVCD, 1
|
|
jne release_noVCD
|
|
|
|
xor cx, cx
|
|
mov cl, [si.DCB_id]
|
|
mov dx, VCD_RELPORT
|
|
call [lpfnVCD]
|
|
else
|
|
xor cx, cx
|
|
mov cl, [si.DCB_id]
|
|
push cx
|
|
call WowCloseComPort
|
|
endif
|
|
|
|
release_noVCD:
|
|
ret
|
|
|
|
ReleaseCOMport386 endp
|
|
|
|
PUBLIC StealPort
|
|
StealPort proc near
|
|
|
|
cmp fVCD, 1
|
|
jne sp_yes
|
|
mov dx, VCD_STEALPORT
|
|
xor cx, cx
|
|
mov cl, [si.DCB_id]
|
|
call [lpfnVCD]
|
|
or al, al
|
|
jnz sp_yes
|
|
|
|
sp_no:
|
|
stc
|
|
ret
|
|
|
|
sp_yes:
|
|
clc
|
|
mov [si.VCDflags], 0
|
|
ret
|
|
|
|
StealPort endp
|
|
|
|
page
|
|
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
; ENTER: DS:SI -> ComDEB
|
|
; EXIT: AL = 0, if IRQ was unmasked, else -1, if IRQ was already masked
|
|
;
|
|
MaskIRQ proc near
|
|
push es
|
|
push di
|
|
mov di, ds
|
|
mov es, di
|
|
lea di, [si+SIZE ComDEB]
|
|
mov ax, BIH_API_Get_Mask
|
|
call APIHandler ; returns Carry Set, if masked
|
|
jc short already_masked
|
|
pushf
|
|
mov ax, BIH_API_Mask
|
|
call APIHandler ; mask IRQ
|
|
xor ax, ax
|
|
popf
|
|
jnc short mask_exit
|
|
already_masked:
|
|
or al, -1
|
|
mask_exit:
|
|
pop di
|
|
pop es
|
|
ret
|
|
MaskIRQ endp
|
|
|
|
;------------------------------------------------------------------------------
|
|
;
|
|
; ENTER: DS:SI -> ComDEB
|
|
;
|
|
UnmaskIRQ proc near
|
|
push es
|
|
push di
|
|
mov di, ds
|
|
mov es, di
|
|
lea di, [si+SIZE ComDEB]
|
|
mov ax, BIH_API_Unmask
|
|
call APIHandler
|
|
pop di
|
|
pop es
|
|
ret
|
|
UnmaskIRQ endp
|
|
page
|
|
|
|
;----------------------------Public Routine-----------------------------;
|
|
;
|
|
; $INICOM - Initialize A Port
|
|
;
|
|
; Initalizes the requested port if present, and sets
|
|
; up the port with the given attributes when they are valid.
|
|
; This routine also initializes communications buffer control
|
|
; variables. This routine is passed the address of a device
|
|
; control block.
|
|
;
|
|
; The RLSD, CTS, and DSR signals should be ignored by all COM
|
|
; routines if the corresponding timeout values are 0.
|
|
;
|
|
; For the LPT ports, a check is performed to see if the hardware
|
|
; is present (via the LPT port addresses based at 40:8h. If the
|
|
; port is unavailable, an error is returned. If the port is
|
|
; available, then the DEB is set up for the port. $SETCOM will
|
|
; be called to set up the DEB so that there will be something
|
|
; valid to pass back to the caller when he inquires the DEB.
|
|
;
|
|
; No hardware initialization will be performed to prevent the
|
|
; RESET line from being asserted and resetting the printer every
|
|
; time this routine is called.
|
|
;
|
|
; Entry:
|
|
; EX:BX --> Device Control Block with all fields set.
|
|
; Returns:
|
|
; AX = 0 if no errors occured
|
|
; Error Returns:
|
|
; AX = initialization error code otherwise
|
|
; Registers Preserved:
|
|
; None
|
|
; Registers Destroyed:
|
|
; AX,BX,CX,DX,ES,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public $INICOM
|
|
$INICOM proc near
|
|
push si ;As usual, save register variables
|
|
push di
|
|
mov ah,es:[bx.DCB_Id] ;Get device i.d.
|
|
call GetDEB ;--> DEB for this device
|
|
mov ax, IE_BADID ; call it a bad id (spooler uses DOS)
|
|
jc InitCom15 ;Invalid device
|
|
jns InitCom20 ; jmp if COM port
|
|
|
|
push ds
|
|
|
|
mov di, [si.BIOSPortLoc]
|
|
cmp di, LPTB
|
|
jb short InitLPT_Installed
|
|
|
|
mov cx,__0040H ;[rkh] ...
|
|
mov ds,cx ;Point DS: at ROM Save Area.
|
|
assumes ds,nothing
|
|
|
|
mov ax, IE_HARDWARE
|
|
mov cx, wo [di]
|
|
jcxz InitCom10 ; if zero, no hardware
|
|
|
|
mov ax,IE_BadID ;Show bad device
|
|
cmp ch, 0 ; zero hibyte -> not valid (redir)
|
|
jz InitCom10 ; call it a bad id (spooler uses DOS)
|
|
|
|
cmp di, LPTB ; LPT1?
|
|
jz InitLPT_Installed ; yes, must be installed
|
|
|
|
cmp cx, wo [di-2] ;Q: duplicate of previous port
|
|
je InitCom10 ; Y: (redirected port)
|
|
|
|
InitLPT_Installed:
|
|
pop ds
|
|
mov [si.Port], cx
|
|
call $SETCOM
|
|
call GetPort386 ; tell win386 we're using the port
|
|
mov ax, IE_OPEN ; port already open (by another VM)
|
|
jc InitCom15 ; error
|
|
jmp InitCom90 ;That's all
|
|
|
|
InitCom10:
|
|
pop ds ; get DS back
|
|
InitCom15:
|
|
jmp InitCom100
|
|
|
|
|
|
assumes ds,Data
|
|
InitCom17:
|
|
mov ax, IE_OPEN
|
|
cmp [si.Port], -1 ;Q: determined that port didn't exist?
|
|
jne InitCom15 ; N: return IE_OPEN
|
|
jmp short InitCom27 ; Y: return IE_HARDWARE
|
|
|
|
; *** Set up serial port ***
|
|
;
|
|
InitCom20:
|
|
cmp [si.Port], -1 ;Q: port exists?
|
|
je InitCom27 ; N: report not found
|
|
|
|
mov ax, __WinFlags
|
|
test ax, WF_ENHANCED
|
|
jz short @F
|
|
call GetCOMport386
|
|
jc InitCom17
|
|
|
|
@@:
|
|
cmp [si.Port], 0 ;Q: already got info?
|
|
jnz @F
|
|
call FindCOMPort
|
|
jc InitCom27 ; report not found, if error
|
|
mov [si.Port], ax
|
|
mov [si.IntVecNum], dl
|
|
@@:
|
|
|
|
push es ;Save these registers
|
|
push di
|
|
push cx ;needed later for $SETCOM etc
|
|
push bx
|
|
|
|
mov al, [si.IntVecNum]
|
|
xor ah, ah
|
|
lea di, [si+SIZE ComDEB]
|
|
mov [di.BIS_IRQ_Number], ax
|
|
|
|
mov di, DataOFFSET IRQhooks
|
|
mov cx, MAXCOM+1
|
|
InitCom25:
|
|
cmp al, [di.IRQn] ;Q: hooked IRQ matches ours?
|
|
je short InitCom30 ; Y:
|
|
cmp [di.IRQn], 0 ;Q: end of hooked IRQ list?
|
|
je short InitCom35 ; Y:
|
|
add di, SIZE IRQ_Hook_Struc ; N: check next hook
|
|
loop InitCom25
|
|
int 3 ; data structures corrupt if we
|
|
; get here, because no hook table
|
|
; entries exist and there is suppose
|
|
; to be at least 1 for each DEB
|
|
InitCom26:
|
|
call ReleaseCOMport386 ; give port back to 386...
|
|
pop bx
|
|
pop cx
|
|
pop di
|
|
pop es
|
|
|
|
InitCom27:
|
|
mov ax, IE_HARDWARE ; jump if port not available
|
|
jmp InitCom100
|
|
|
|
InitCom30:
|
|
cmp [di.HookCnt], 0 ;Q: IRQ still hooked?
|
|
je short InitCom35 ; N: rehook
|
|
inc [di.HookCnt] ; Y: inc hook count
|
|
mov [si.IRQhook], di ; & link DEB into list
|
|
mov ax, [di.First_DEB]
|
|
mov [si.NextDEB], ax
|
|
mov [di.First_DEB], si
|
|
jmp short InitCom40
|
|
|
|
InitCom35:
|
|
mov [di.IRQn], al ; hook IRQ for first time, or rehook
|
|
mov [si.IRQhook], di
|
|
mov [di.First_DEB], si
|
|
mov [di.HookCnt], 1
|
|
call MaskIRQ
|
|
mov [di.OldMask], al
|
|
|
|
InitCom40: ; di -> IRQ_Hook_Struc
|
|
|
|
cmp [fVPICD], 0 ;Q: VPICD bimodel services available?
|
|
jl short InitCom415 ; N:
|
|
mov ax, ds ; Y: use them
|
|
mov es, ax
|
|
lea di, [si+SIZE ComDEB]
|
|
|
|
mov [di.BIS_Descriptor_Count], 2
|
|
mov ax, word ptr [si.QInAddr+2] ; get selector of in queue
|
|
mov [di.EBIS_Sel1.EBIS_User_Mode_Sel], ax
|
|
mov ax, word ptr [si.QOutAddr+2] ; get selector of out queue
|
|
mov [di.EBIS_Sel2.EBIS_User_Mode_Sel], ax
|
|
|
|
mov ax, VPICD_Install_Handler
|
|
call [lpfnVPICD]
|
|
jnc InitCom42
|
|
cmp [di.OldMask], 0
|
|
jne InitCom26
|
|
call UnmaskIRQ
|
|
jmp InitCom26
|
|
|
|
InitCom42:
|
|
;
|
|
; save newly allocated selectors/segments into "Alt" queue pointers
|
|
;
|
|
mov ax, [di.EBIS_Sel1.EBIS_Super_Mode_Sel]
|
|
mov word ptr [si.AltQInAddr+2], ax
|
|
mov ax, [di.EBIS_Sel2.EBIS_Super_Mode_Sel]
|
|
mov word ptr [si.AltQOutAddr+2], ax
|
|
|
|
InitCom414:
|
|
jmp InitCom59
|
|
|
|
InitCom415:
|
|
cmp [di.VecN], 0FFh ;Q: int already hooked?
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
jnz short InitCom52 ; Y: init RMode ptrs in BIS
|
|
ELSE
|
|
jnz InitCom414 ; Y:
|
|
ENDIF
|
|
mov al, [si.IntVecNum]
|
|
add al, 8 ; 1st PIC starts at vector 8h
|
|
cmp al, 16 ;Q: 2nd PIC?
|
|
jb short InitCom418 ; N:
|
|
add al, 70h-16 ; Y: 2nd PIC starts at vector 70h
|
|
InitCom418:
|
|
mov [di.VecN], al
|
|
|
|
; *** Set interrupt vectors ***
|
|
;
|
|
mov ah,35h ;Get the DOS vector
|
|
int 21h ;DOS Get Vector Function
|
|
mov wo [di.OldIntVec][0], bx
|
|
mov wo [di.OldIntVec][2], es
|
|
|
|
InitCom50:
|
|
push ds ;Save original DS
|
|
mov dx, [di.HandlerOff]
|
|
mov bx, _INTERRUPT
|
|
mov ds, bx ;Interrupt handler address in ds:dx
|
|
assumes ds,nothing
|
|
mov ah, 25h ;DOS Set Vector Function
|
|
int 21h ;Set the DOS vector
|
|
pop ds ;Original DS
|
|
assumes ds,Data
|
|
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
InitCom52:
|
|
cmp [Using_DPMI], 0
|
|
jz short InitCom57
|
|
|
|
mov ax, Int31_Get_Version SHL 8
|
|
int 31h
|
|
mov bl, [si.IntVecNum]
|
|
mov bh, bl
|
|
add bl, dh ; assume master PIC
|
|
sub bh, 8 ;Q: IRQ in master?
|
|
jb @f ; Y: add master's base vec
|
|
add bh, dl ; N: add slave's base vec
|
|
mov bl, bh
|
|
@@:
|
|
mov ax, Get_RM_IntVector
|
|
int 31h
|
|
mov wo [di.RM_OldIntVec][0], dx
|
|
mov wo [di.RM_OldIntVec][2], cx
|
|
|
|
mov dx, [di.RM_HandlerOff]
|
|
mov ax, _INTERRUPT
|
|
call SegmentFromSelector
|
|
mov cx, ax
|
|
push cx
|
|
mov ax, Set_RM_IntVector
|
|
int 31h
|
|
|
|
lea di, [si+SIZE ComDEB]
|
|
mov wo [di.BIS_Super_Mode_API], IntCodeOFFSET RM_APIHandler
|
|
pop cx
|
|
mov wo [di.BIS_Super_Mode_API+2], cx
|
|
|
|
;
|
|
; Get segment addresses for the Q's and set AltQInAddr and AltQOutAddr
|
|
;
|
|
mov ax, wo [si.AltQInAddr+2]
|
|
call SegmentFromSelector
|
|
mov wo [si.AltQInAddr+2], ax
|
|
mov ax, wo [si.AltQOutAddr+2]
|
|
call SegmentFromSelector
|
|
mov wo [si.AltQOutAddr+2], ax
|
|
InitCom57:
|
|
ENDIF
|
|
mov ax, __WinFlags ;In Standard mode, the PIC IRQ
|
|
test al, WF_STANDARD ; priorities get rotated to favor
|
|
jz InitCom59 ; the comm ports.
|
|
|
|
call Rotate_PIC
|
|
|
|
; *** Interrupt handler set : jump here if handler is already installed ***
|
|
;
|
|
InitCom59:
|
|
pop bx
|
|
pop cx
|
|
pop di
|
|
pop es
|
|
|
|
InitCom60:
|
|
mov dx,[si.Port] ;Set comm card address
|
|
xor ax,ax ;Need a zero
|
|
inc dx ;--> Interrupt Enable Register
|
|
.errnz ACE_IER-ACE_RBR-1
|
|
out dx,al ;Turn off interrupts
|
|
call FlagNotActive
|
|
add dl,ACE_MCR-ACE_IER ;--> Modem Control Register
|
|
in al,dx
|
|
and al,ACE_DTR+ACE_RTS ;Leave DTR, RTS high if already so
|
|
iodelay ; but tri-state IRQ line
|
|
out dx,al
|
|
|
|
InitCom70:
|
|
push es ;Zero queue counts and indexes
|
|
|
|
push ds
|
|
pop es
|
|
assumes es,Data
|
|
|
|
lea di,QInCount[si]
|
|
mov cx,(EFlags-QInCount)/2
|
|
.errnz (EFlags-QInCount) AND 1
|
|
xor ax,ax
|
|
cld
|
|
rep stosw
|
|
|
|
.errnz QInGet-QInCount-2
|
|
.errnz QInPut-QInGet-2
|
|
.errnz QOutCount-QInPut-2
|
|
.errnz QOutGet-QOutCount-2
|
|
.errnz QOutPut-QOutGet-2
|
|
.errnz EFlags-QOutPut-2 ;First non-queue item
|
|
|
|
pop es
|
|
assumes es,nothing
|
|
|
|
mov HSFlag[si],al ;Show no handshakes yet
|
|
mov MiscFlags[si],al ;Show not discarding
|
|
mov EvtWord[si],ax ;Show no events
|
|
mov [si.VCDflags], al
|
|
|
|
mov [si.SendTrigger], ax
|
|
dec ax
|
|
mov [si.RecvTrigger], ax
|
|
|
|
;Call $SETCOM to perform further hardware initialization.
|
|
|
|
InitCom80:
|
|
sub dl,ACE_MCR-ACE_FCR ; dx -> FCR
|
|
in al, dx
|
|
iodelay
|
|
test al, ACE_FIFO_E2 ;Q: FIFO already on?
|
|
jz short @F ; N:
|
|
or EFlags[si], fFIFOpre ; Y: flag it
|
|
@@:
|
|
|
|
; needs si, di, and es to be saved from the beginning of inicom
|
|
call $SETCOM ;Set up Comm Device
|
|
jnz short InitCom110 ;jump if failed
|
|
|
|
call UnmaskIRQ
|
|
and EFlags[si], fEFlagsMask ;Clear internal state
|
|
|
|
InitCom90:
|
|
xor ax,ax ;Return AX = 0 to show success
|
|
mov ComErr[si],ax ;Get rid of any bogus init error
|
|
|
|
InitCom100:
|
|
pop di
|
|
pop si
|
|
ret
|
|
|
|
;
|
|
; jump to here, if call to $SETCOM failed
|
|
;
|
|
; DANGER! *** Call into middle of Terminate to clean things up *** DANGER!
|
|
;
|
|
InitCom110:
|
|
push ax ;Failure, save error code
|
|
call Terminate45 ;Restore port address, int vec
|
|
pop ax ;Restore error code and exit
|
|
jmp InitCom100
|
|
|
|
$INICOM endp
|
|
page
|
|
|
|
;----------------------------Public Routine-----------------------------;
|
|
;
|
|
; $TRMCOM - Terminate Communications Channel
|
|
;
|
|
; Wait for any outbound data to be transmitted, drop the hardware
|
|
; handshaking lines, and disable interrupts. If the output queue
|
|
; contained data when it was closed, an error will be returned
|
|
;
|
|
; LPT devices have it easy. They just need to restore the I/O port
|
|
; address.
|
|
;
|
|
; Entry:
|
|
; AH = Device ID
|
|
; Returns:
|
|
; AX = 0
|
|
; Error Returns:
|
|
; AX = 8000h if invalid device ID
|
|
; AX = -2 if output queue timeout occured
|
|
; Registers Destroyed:
|
|
; AX,BX,CX,DX,ES,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public $TRMCOM
|
|
$TRMCOM proc near
|
|
|
|
push si
|
|
push di
|
|
xor cx,cx ;Show no error if LPT port
|
|
call GetDEB
|
|
jc TermCom60 ;ID is invalid, return error
|
|
js TermCom30 ;Port is a LPT port
|
|
|
|
push ax ;Save port id
|
|
or MiscFlags[si],Discard ;Show discarding serial data
|
|
mov ComErr[si],cx ;Clear error flags
|
|
mov QInCount[si],cx ;Show no chars in input queue
|
|
call $RECCOM ;Send XON if needed
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; We have to wait for the output queue to empty. To do this,
|
|
; a timer will be created. If no character has been transmitted
|
|
; when the timeout occurs, then an error will be indicated and
|
|
; the port closed anyway. If the timer cannot be created, then
|
|
; just loop until the queue empties, which will be better than
|
|
; discarding charatcers if there are any
|
|
;-----------------------------------------------------------------------;
|
|
|
|
test [si.HSFlag], HHSAlwaysDown ; Q: handshaking ever up?
|
|
jnz TermCom17 ; N: skip wait loop
|
|
|
|
TermCom10:
|
|
mov cx,QOutCount[si] ;Get current queue count
|
|
jcxz TermCom20 ;No characters in queue
|
|
|
|
cCall GetSystemMsecCount
|
|
mov di, ax
|
|
|
|
TermCom15:
|
|
cmp QOutCount[si],cx ;Queue count change?
|
|
jne TermCom10 ; Yes, restart timeout
|
|
|
|
cCall GetSystemMsecCount
|
|
sub ax, di
|
|
cmp ax, Timeout * 1000 ;Q: Timeout reached?
|
|
jb TermCom15 ; No, keep waiting
|
|
|
|
IFDEF DEBUG_TimeOut
|
|
.286
|
|
pusha
|
|
lea cx, szSendTO
|
|
call Contention_Dlg
|
|
popa
|
|
jz TermCom10
|
|
.8086
|
|
ENDIF
|
|
|
|
TermCom17:
|
|
mov cx, TimeoutError ; Yes, show timeout error
|
|
|
|
TermCom20:
|
|
pop ax ;Restore cid
|
|
|
|
TermCom30:
|
|
mov dx,Port[si] ;Get port base address
|
|
call Terminate ;The real work is done here
|
|
mov ax,cx ;Set return code
|
|
|
|
TermCom60:
|
|
pop di
|
|
pop si
|
|
ret
|
|
|
|
$TRMCOM endp
|
|
page
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; Terminate - Terminate Device
|
|
;
|
|
; Restore the port I/O address and make sure that interrupts are off
|
|
;
|
|
; Entry:
|
|
; AH = Device Id.
|
|
; DX = Device I/O port address.
|
|
; SI --> DEB
|
|
; Returns:
|
|
; AX = 0
|
|
; Error Returns:
|
|
; AX = -1
|
|
; Registers Destroyed:
|
|
; AX,BX,DX,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public Terminate ;Public for debugging
|
|
Terminate proc near
|
|
|
|
or ah,ah ;LPT port?
|
|
jns Terminate10 ; No, process COM port
|
|
.errnz LPTx-10000000b
|
|
|
|
Terminate5:
|
|
call ReleasePort386 ; give port back to 386...
|
|
jmp Terminate50 ;That's all
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; It is a com port!
|
|
;
|
|
; We delay for a bit while the last character finishes transmitting
|
|
; Then we drop DTR and RTS, and disable the interrupt generation at
|
|
; the 8250. Even if fRTSDisable or fDTRDisable is set, those lines
|
|
; will be dropped when the port is closed.
|
|
;-----------------------------------------------------------------------;
|
|
;
|
|
; When the OUT2 bit is reset to 0 to disable interrupts, many ports
|
|
; generate an interrupt which can not be identified, because the the
|
|
; interrupt ID register will not be set. To work around this hardware
|
|
; problem we first mask the IRQ, then set the port into loopback mode
|
|
; and output a NULL to generate a receive interrupt request. Then we
|
|
; reset OUT2 and unmask the IRQ. This will cause the interrupt to occur
|
|
; and the interrupt handler will be able to correctly identify the
|
|
; interrupt as coming from the com port.
|
|
|
|
Terminate10:
|
|
inc dx ;Disable chip interrupts
|
|
.errnz ACE_IER-ACE_RBR-1
|
|
mov al, ACE_ERBFI ; except receive
|
|
out dx,al
|
|
call FlagNotActive ; don't need to check for postmessage
|
|
; on timer ticks
|
|
add dl,ACE_LSR-ACE_IER ;--> line status register
|
|
iodelay
|
|
|
|
Terminate20:
|
|
in al,dx ;Wait until xmit is empty
|
|
and al,ACE_THRE+ACE_TSRE
|
|
cmp al,ACE_THRE+ACE_TSRE
|
|
jne Terminate20 ;Not empty yet
|
|
|
|
Terminate30:
|
|
xor al, al
|
|
test EFlags[si], fFIFOpre ;Q: leave FIFO enabled?
|
|
jz short @F ; N:
|
|
mov al, ACE_TRIG14 OR ACE_EFIFO OR ACE_CRFIFO OR ACE_CTFIFO
|
|
@@:
|
|
sub dl, ACE_LSR-ACE_FCR
|
|
out dx, al
|
|
iodelay
|
|
call MaskIRQ
|
|
add dl, ACE_MCR-ACE_FCR ;--> Modem Control Register
|
|
in al,dx
|
|
iodelay
|
|
mov ah, al
|
|
or al,ACE_LOOP ; turn on loopback
|
|
out dx, al
|
|
iodelay
|
|
sub dl, ACE_MCR-ACE_THR
|
|
xor al, al
|
|
out dx, al ; output a NULL to generate an int
|
|
iodelay
|
|
add dl, ACE_LSR-ACE_THR
|
|
Terminate35:
|
|
in al,dx ;Wait until xmit is empty
|
|
and al,ACE_THRE+ACE_TSRE
|
|
cmp al,ACE_THRE+ACE_TSRE
|
|
jne Terminate35 ;Not empty yet
|
|
mov al, ah
|
|
dec dl ; now clear OUT2 and loopback
|
|
.errnz ACE_LSR-ACE_MCR-1
|
|
and al,ACE_DTR+ACE_RTS ;Leave DTR, RTS high if already so
|
|
out dx,al ; but tri-state IRQ line
|
|
|
|
call UnmaskIRQ ; this will cause the receive int
|
|
; to occur and be processed
|
|
sub dl, ACE_MCR-ACE_IER ; clear the receive int enable
|
|
xor al, al
|
|
out dx, al
|
|
dec dx
|
|
.errnz ACE_IER-ACE_RBR-1
|
|
call MaskIRQ
|
|
|
|
;******* DANGER! ***** NOTICE! ***** DANGER! ***** WARNING! ***** NOTICE!
|
|
;
|
|
; Terminate45 is a secondary entrypoint into this routine--it's called
|
|
; by the initialization code when that code is unable to properly init
|
|
; a com port and needs to clean-up the mess it's made.
|
|
;
|
|
;******* DANGER! ***** NOTICE! ***** DANGER! ***** WARNING! ***** NOTICE!
|
|
|
|
Terminate45:
|
|
push cx ;Save original cx
|
|
push bx ;Save original bx
|
|
|
|
cmp [fVPICD], 0 ;Q: VPICD bimodel services available?
|
|
jl short @F ; N:
|
|
mov ax, ds ; Y: use them
|
|
mov es, ax
|
|
lea di, [si+SIZE ComDEB]
|
|
mov ax, VPICD_Remove_Handler
|
|
call [lpfnVPICD]
|
|
@@:
|
|
|
|
mov di, [si.IRQhook]
|
|
dec [di.HookCnt] ;Q: last port using IRQ?
|
|
jne short Terminate495 ; N: unmask IRQ again
|
|
mov al, 0FFh
|
|
xchg al, [di.VecN] ;Interrupt vector number
|
|
cmp al, 0FFh ;Q: IRQ vector hooked?
|
|
je short Terminate49 ; no...
|
|
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
cmp [Using_DPMI], 0
|
|
jz short term_no_dpmi
|
|
|
|
;
|
|
; unhook RM vector thru DPMI for standard mode
|
|
;
|
|
push ax
|
|
mov ax, Int31_Get_Version SHL 8
|
|
int 31h
|
|
mov bl, [si.IntVecNum]
|
|
mov bh, bl
|
|
add bl, dh ; assume master PIC
|
|
sub bh, 8 ;Q: IRQ in master?
|
|
jb @f ; Y: add master's base vec
|
|
add bh, dl ; N: add slave's base vec
|
|
mov bl, bh
|
|
@@:
|
|
mov dx, wo [di.RM_OldIntVec][0]
|
|
mov cx, wo [di.RM_OldIntVec][2]
|
|
mov ax, Set_RM_IntVector
|
|
int 31h
|
|
pop ax
|
|
term_no_dpmi:
|
|
ENDIF
|
|
mov dx, __WinFlags ;In Standard mode the PIC interrupt
|
|
test dl, WF_STANDARD ; priorities are changed to favor
|
|
jz Terminate48 ; the comm ports.
|
|
|
|
call Rotate_PIC ;This port no longer needs priority
|
|
|
|
Terminate48:
|
|
; *** reset int vector to it's previous state
|
|
assumes ds,nothing
|
|
push ds ;Save original DS [rkh] ...
|
|
lds dx, [di.OldIntVec]
|
|
mov ah, 25h ;DOS Set Vector Function
|
|
int 21h ;Set the DOS vector
|
|
pop ds ;Original DS
|
|
assumes ds,data
|
|
|
|
; *** interrupt vectors have been reset if needed at this point ***
|
|
;
|
|
Terminate49:
|
|
mov cl, [di.OldMask]
|
|
|
|
; Set the 8259 interrupt mask bit for this IRQ. Leave interrupts enabled
|
|
; if they were already enabled when the comm port was initialized by us.
|
|
|
|
or cl, cl
|
|
jnz @f
|
|
Terminate495:
|
|
call UnmaskIRQ
|
|
@@:
|
|
|
|
xor ax, ax
|
|
xchg ax, [si.NextDEB]
|
|
cmp [di.First_DEB], si ;Q: DEB first for IRQ hook?
|
|
je short Terminate46 ; Y:
|
|
mov bx, [di.First_DEB] ; N: get first
|
|
Terminate453:
|
|
cmp [bx.NextDEB], si ;Q: does this DEB point to one terminating?
|
|
je Terminate455 ; Y:
|
|
mov bx, [bx.NextDEB] ; N: get next DEB
|
|
jmp Terminate453
|
|
Terminate455:
|
|
mov [bx.NextDEB], ax ; link previous DEB to NextDEB
|
|
jmp short Terminate47
|
|
Terminate46:
|
|
mov [di.First_DEB], ax ; point IRQ hook at NextDEB
|
|
Terminate47:
|
|
pop bx ;Original BX
|
|
|
|
call ReleaseCOMport386 ; give port back to 386...
|
|
|
|
pop cx ;Original CX
|
|
|
|
Terminate50: ;Also called from $INICOM !
|
|
xor ax,ax ;Indicate no error
|
|
ret ;Port is closed and deallocated
|
|
|
|
Terminate endp
|
|
page
|
|
|
|
;----------------------------Public Routine-----------------------------;
|
|
;
|
|
; $ENANOTIFY - Enable Event Notification
|
|
;
|
|
; Entry:
|
|
; AH = Device ID
|
|
; BX = Window handle for PostMessage
|
|
; CX = Receive threshold
|
|
; DX = Transmit threshold
|
|
; Returns:
|
|
; AX = 1, if no errors occured
|
|
; Error Returns:
|
|
; AX = 0
|
|
; Registers Preserved:
|
|
; BX,SI,DI,DS
|
|
; Registers Destroyed:
|
|
; AX,CX,DX,ES,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public $ENANOTIFY
|
|
$ENANOTIFY proc near
|
|
push si
|
|
call GetDEB
|
|
mov ax, 0
|
|
jc scb_exit
|
|
|
|
mov ax, cx
|
|
inc ax
|
|
jz short scb_recv_ok
|
|
cmp cx, [si.QInSize] ;Q: receive threshold reasonable?
|
|
jb short scb_recv_ok ; Y:
|
|
%OUT should we return an error, if thresholds invalid?
|
|
mov cx, [si.QInSize] ; N:
|
|
sub cx, 10
|
|
scb_recv_ok:
|
|
inc dx
|
|
jz short scb_send_ok
|
|
dec dx
|
|
cmp dx, [si.QOutSize] ;Q: receive threshold reasonable?
|
|
jb short scb_send_ok ; Y:
|
|
mov dx, [si.QOutSize] ; N:
|
|
sub dx, 10
|
|
scb_send_ok:
|
|
mov [si.NotifyHandle], bx
|
|
mov [si.NotifyFlagsHI], CN_Notify
|
|
or bx, bx ;Q: null callback?
|
|
jnz scb_save_thresholds ; N: save thresholds
|
|
or cx, -1 ; Y: zero thresholds
|
|
xor dx, dx
|
|
mov [si.NotifyFlagsHI], 0
|
|
scb_save_thresholds:
|
|
mov [si.RecvTrigger], cx
|
|
mov [si.SendTrigger], dx
|
|
or [si.NotifyFlagsHI], CN_TRANSMIT ; we don't want to send
|
|
; a transmit trigger notification until
|
|
; the transmit buffer has been filled
|
|
; above the trigger level and then
|
|
; emptied below it again!
|
|
|
|
cmp wo lpPostMessage[2], 0 ;Q: gotten addr of PostMessage yet?
|
|
jne short scb_good ; Y:
|
|
push ds ; N: get module handle of USER
|
|
lea ax, szUser
|
|
push ax
|
|
cCall GetModuleHandle
|
|
|
|
push ax ; module handle
|
|
mov ax, POSTMESSAGE
|
|
cwd
|
|
push dx
|
|
push ax
|
|
cCall GetProcAddress
|
|
mov wo lpPostMessage[0], ax ; save received proc address
|
|
mov wo lpPostMessage[2], dx
|
|
|
|
scb_good:
|
|
mov ax, 1
|
|
|
|
scb_exit:
|
|
pop si
|
|
ret
|
|
$ENANOTIFY endp
|
|
page
|
|
|
|
;----------------------------Public Routine-----------------------------;
|
|
;
|
|
; $SETQUE - Set up Queue Pointers
|
|
;
|
|
; Sets pointers to Receive and Transmit Queues, as provided by the
|
|
; caller, and initializes those queues to be empty.
|
|
;
|
|
; Queues must be set before $INICOM is called!
|
|
;
|
|
; Entry:
|
|
; AH = Device ID
|
|
; ES:BX --> Queue Definition Block
|
|
; Returns:
|
|
; AX = 0 if no errors occured
|
|
; Error Returns:
|
|
; AX = error code
|
|
; Registers Preserved:
|
|
; BX,DX,SI,DI,DS
|
|
; Registers Destroyed:
|
|
; AX,CX,ES,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public $SETQUE
|
|
$SETQUE proc near
|
|
|
|
push si ;These will be used
|
|
push di
|
|
call GetDEB ;Get DEB
|
|
jc SetQue10 ;Invalid, ignore the call
|
|
js SetQue10 ;Ignore call for LPT ports
|
|
push ds ;Set ds:si --> QDB
|
|
push es ;Set es:di --> to ComDCB.QInAddr
|
|
pop ds
|
|
assumes ds,nothing
|
|
pop es
|
|
assumes es,Data
|
|
lea di,QInAddr[si]
|
|
mov si,bx
|
|
cld
|
|
FCLI ;No one else can play with queues
|
|
movsw ; QInAddr = QueueRxAddr
|
|
movsw
|
|
.errnz QueueRxAddr
|
|
sub si, 4 ; AltQInAddr = QueueRxAddr
|
|
mov cx, 5 ; QInSize = QueueRxSize
|
|
rep movsw ; QOutAddr = QueueTxAddr
|
|
sub si, 4
|
|
mov cx, 3 ; AltQOutAddr = QueueTxAddr
|
|
rep movsw ; QOutSize = QueueTxSize
|
|
|
|
xor ax,ax ;Will do some zero filling
|
|
mov cl,(EFlags-QInCount)/2
|
|
.errnz (EFlags-QInCount) AND 0FE01h
|
|
rep stosw
|
|
FSTI
|
|
push es ;Restore the data segment
|
|
pop ds
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
SetQue10:
|
|
pop di ;Restore saved registers
|
|
pop si
|
|
ret
|
|
|
|
; The above code made a few assumptions about how memory
|
|
; was allocated within the structures:
|
|
|
|
.errnz AltQInAddr-QInAddr-4
|
|
.errnz (QueueRxSize-QueueRxAddr)-(QInSize-AltQInAddr)
|
|
.errnz (QueueTxAddr-QueueRxSize)-(QOutAddr-QInSize)
|
|
.errnz AltQOutAddr-QOutAddr-4
|
|
.errnz (QueueTxSize-QueueTxAddr)-(QOutSize-AltQOutAddr)
|
|
|
|
.errnz QueueRxSize-QueueRxAddr-4
|
|
.errnz QueueTxAddr-QueueRxSize-2
|
|
.errnz QueueTxSize-QueueTxAddr-4
|
|
|
|
.errnz QInSize-AltQInAddr-4
|
|
.errnz QOutAddr-QInSize-2
|
|
.errnz QOutSize-AltQOutAddr-4
|
|
|
|
.errnz QInCount-QOutSize-2
|
|
.errnz QInGet-QInCount-2
|
|
.errnz QInPut-QInGet-2
|
|
.errnz QOutCount-QInPut-2
|
|
.errnz QOutGet-QOutCount-2
|
|
.errnz QOutPut-QOutGet-2
|
|
.errnz EFlags-QOutPut-2 ;First non-queue item
|
|
|
|
$SETQUE endp
|
|
page
|
|
|
|
;----------------------------Public Routine-----------------------------;
|
|
;
|
|
; $SETCOM - Set Communications parameters
|
|
;
|
|
; Re-initalizes the requested port if present, and sets up the
|
|
; port with the given attributes when they are valid.
|
|
;
|
|
; For LPT ports, just copies whatever is given since it's ignored
|
|
; anyway.
|
|
;
|
|
; Entry:
|
|
; ES:BX --> DCB with all fields set.
|
|
; Returns:
|
|
; 'Z' Set if no errors occured
|
|
; AX = 0
|
|
; Error Returns:
|
|
; 'Z' clear if errors occured
|
|
; AX = initialization error code.
|
|
; Registers Destroyed:
|
|
; AX,BX,CX,DX,ES,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public $SETCOM
|
|
$SETCOM proc near
|
|
|
|
cld
|
|
push si
|
|
push di
|
|
mov ah,es:[bx.DCB_Id] ;Get device i.d.
|
|
call GetDEB ;Get DEB pointer in SI
|
|
mov ax,IE_BadID ;Assume unknown device
|
|
jc SetCom10 ;Invalid device, return error
|
|
jns SetCom20 ;COM port
|
|
call SetCom100 ;Copy the DCB
|
|
|
|
SetCom5:
|
|
xor ax,ax ;Show no error
|
|
|
|
SetCom10:
|
|
or ax,ax ;Set/clear 'Z'
|
|
pop di ; and exit
|
|
pop si
|
|
ret
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; Have a comm device, check all the serial parameters to make
|
|
; sure they are correct before moving the new DCB into our space
|
|
; and changing the ACE parameters.
|
|
;-----------------------------------------------------------------------;
|
|
|
|
SetCom20:
|
|
call SetCom300 ;Baud rate valid?
|
|
jcxz SetCom10 ; No, return error
|
|
call SetCom400 ;Byte size/parity/stop bits correct?
|
|
jc SetCom10 ; No, return error
|
|
|
|
; The parameters seem correct. Copy the DCB into our space and
|
|
; initialize the ACE with the new parameters
|
|
|
|
mov dx,Port[si] ;Disable interrupts from the 8250
|
|
inc dx
|
|
.errnz ACE_IER-1
|
|
xor ax,ax
|
|
out dx,al
|
|
call FlagNotActive
|
|
|
|
call SetCom100 ;Copy the DCB
|
|
mov bx,si ;Set ES:BX --> DCB
|
|
call SetCom200 ;Get timeout masks
|
|
xchg al,ah ;Want them in the correct registers
|
|
mov wo MSRMask[si],ax
|
|
.errnz MSRInfinite-MSRMask-1
|
|
|
|
call SetCom400 ;Get line control byte
|
|
push ax ; and save LCR value
|
|
inc dx ;--> LCR
|
|
inc dx
|
|
.errnz ACE_LCR-ACE_IER-2
|
|
or al,ACE_DLAB ;Want access to divisor latch
|
|
out dx,al
|
|
mov RxMask[si],ah ;Save Receive character mask
|
|
mov ax,di ;Get flags mask, error mask
|
|
and [si.DCB_Flags],ah ;Disable parity checking if no parity
|
|
mov ErrorMask[si],al ;Save line status error mask
|
|
|
|
call SetCom300 ;Get baud rate
|
|
sub dl,ACE_LCR-ACE_DLL ;--> LSB of divisor latch
|
|
mov al,cl
|
|
out dx,al
|
|
mov al,ch
|
|
inc dx ;--> MSB of divisor latch
|
|
.errnz ACE_DLM-ACE_DLL-1
|
|
iodelay
|
|
out dx,al
|
|
inc dx ;--> LCR and clear divisor access bit
|
|
inc dx
|
|
.errnz ACE_LCR-ACE_DLM-2
|
|
pop ax
|
|
out dx,al
|
|
|
|
inc dx ;--> Modem Control Register
|
|
.errnz ACE_MCR-ACE_LCR-1
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; Compute initial state of DTR and RTS. If they have been disabled,
|
|
; then do not raise them, and disallow being used as a handshaking
|
|
; line. Also compute the bits to use as hardware handshake bits
|
|
; (DTR and/or RTS as indicated, qualified with the disabled flags).
|
|
;-----------------------------------------------------------------------;
|
|
|
|
mov al,[si.DCB_Flags] ;Align DTR/RTS disable flags for 8250
|
|
and al,fRTSDisable+fDTRDisable
|
|
rol al,1 ;d0 = DTR, d2 = RTS (1 = disabled)
|
|
shr al,1 ;'C'= DTR, d1 = RTS
|
|
adc al,0 ;d0 = DTR, d1 = RTS
|
|
.errnz fRTSDisable-00000010b
|
|
.errnz fDTRDisable-10000000b
|
|
.errnz ACE_DTR-00000001b
|
|
.errnz ACE_RTS-00000010b
|
|
|
|
mov ah,al ;Save disable mask
|
|
xor al,ACE_DTR+ACE_RTS+ACE_OUT2
|
|
out dx,al ;Set Modem Control Register
|
|
|
|
mov al,[si.DCB_Flags2] ;Get hardware handshake flags
|
|
rol al,1 ;Align flags as needed
|
|
rol al,1
|
|
rol al,1
|
|
and al,ACE_DTR+ACE_RTS ;Mask bits of interest
|
|
not ah ;Want inverse of disable mask
|
|
and al,ah ;al = bits to handshake with
|
|
mov HHSLines[si],al ;Save for interrupt code
|
|
|
|
.errnz fDTRFlow-00100000b
|
|
.errnz fRTSFlow-01000000b
|
|
.errnz ACE_DTR-00000001b
|
|
.errnz ACE_RTS-00000010b
|
|
|
|
mov al,[si.DCB_Flags] ;Compute the mask for the output
|
|
shl al,1 ; hardware handshake lines
|
|
and al,ACE_DSR+ACE_CTS
|
|
mov OutHHSLines[si],al
|
|
|
|
.errnz fOutXCTSFlow-00001000b
|
|
.errnz fOutXDSRFlow-00010000b
|
|
.errnz ACE_CTS-00010000b
|
|
.errnz ACE_DSR-00100000b
|
|
|
|
; Compute the queue count where XOff should be issued (or hardware
|
|
; lines dropped). This will prevent having to do it at interrupt
|
|
; time.
|
|
|
|
mov ax,QInSize[si] ;Get where they want it
|
|
sub ax,[si.DCB_XoffLim] ; and compute queue count
|
|
mov XOffPoint[si],ax
|
|
|
|
; Enable FIFO if possible when baudrate >= 4800
|
|
;
|
|
sub dl,ACE_MCR - ACE_FCR ; dx = FCR
|
|
test EFlags[si], fNoFIFO ;Q: FIFO can be enabled?
|
|
jnz sc_nofifo ; N:
|
|
mov ax, [si.DCB_BaudRate]
|
|
cmp ax, 4800
|
|
jb sc_nofifo
|
|
cmp ah, -1 ;Q: baudrate index?
|
|
jne sc_fifo ; N: baudrate >= 4800, enable FIFO
|
|
cmp ax, CBR_4800
|
|
jb sc_nofifo
|
|
%OUT this isn't correct, if lower baudrates are assigned indices above CBR_4800
|
|
|
|
sc_fifo:
|
|
mov al, ACE_TRIG14 OR ACE_EFIFO OR ACE_CRFIFO OR ACE_CTFIFO
|
|
out dx, al ; attempt to enable FIFO
|
|
test EFlags[si], fFIFOchkd ;Q: FIFO detect been done?
|
|
jnz sc_fifodone ; Y: enabled FIFO
|
|
iodelay
|
|
.errnz ACE_IIDR-ACE_FCR
|
|
in al, dx
|
|
or EFlags[si], fFIFOchkd
|
|
test al, ACE_FIFO_E2 ;Q: FIFO enabled?
|
|
jz short @F
|
|
test al, ACE_FIFO_E1 ;Q: 16550A detected?
|
|
jnz sc_fifodone ; Y: enabled FIFO
|
|
@@:
|
|
iodelay
|
|
or EFlags[si], fNoFIFO
|
|
|
|
sc_nofifo:
|
|
xor al, al
|
|
out dx, al
|
|
sc_fifodone:
|
|
|
|
sub dl,ACE_FCR-ACE_RBR ; dx -> RBR
|
|
;
|
|
; Delay for things to settle
|
|
;
|
|
push dx
|
|
cCall GetSystemMsecCount
|
|
pop dx
|
|
mov cx, ax
|
|
delay_loop:
|
|
in al, dx ;Read it once
|
|
push dx
|
|
cCall GetSystemMsecCount
|
|
pop dx
|
|
sub ax, cx
|
|
cmp ax, DELAY_TIME ;Q: Timeout reached?
|
|
ifndef WOW
|
|
jb delay_loop ; N:
|
|
endif
|
|
|
|
add dl,ACE_MSR ;--> Modem Status reg
|
|
in al,dx ;Throw away 1st status read
|
|
iodelay
|
|
in al,dx ;Save 2nd for MSRWait (Clear MSR int)
|
|
mov MSRShadow[si],al
|
|
|
|
; Win 3.0 didn't check hardware handshaking until the line status changed.
|
|
; Allow some apps to keep that behavior.
|
|
|
|
push dx
|
|
xor ax, ax
|
|
cCall GetAppCompatFlags,<ax>
|
|
pop dx
|
|
test ax, GACF_DELAYHWHNDSHAKECHK
|
|
jnz short sc_HHSup
|
|
|
|
;
|
|
; HACK FOR SOME MODEMS: apparently some modems set CTS, but don't set DSR
|
|
; which means that COMM.DRV won't send if the app specifies that hardware
|
|
; handshaking is based on CTS & DSR being set.
|
|
;
|
|
mov ah,OutHHSLines[si]
|
|
mov al, MSRShadow[si]
|
|
and al,ah ;Only leave bits of interest
|
|
cmp al, ah ;Q: handshaking lines ok?
|
|
je short sc_HHSup ; Y:
|
|
cmp ah, ACE_CTS OR ACE_DSR ;Q: app looking for both high?
|
|
jne short sc_HHSdown ; N: skip hack
|
|
test [si.EFlags], fUseDSR ;Q: DSR is always significant?
|
|
jnz short sc_HHSdown ; Y: skip hack
|
|
cmp al, ACE_CTS ;Q: DSR low & CTS high
|
|
jne short sc_HHSdown ; N: skip hack
|
|
and ah, NOT ACE_DSR ; Y: ignore DSR line
|
|
mov OutHHSLines[si], ah
|
|
jmp short sc_HHSup
|
|
|
|
sc_HHSdown:
|
|
or [si.HSFlag], HHSDown OR HHSAlwaysDown ; flag handshaking down
|
|
sc_HHSup:
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; Now, at last, interrupts can be enabled. Don't enable the
|
|
; transmitter empty interrupt. It will be enabled by the first
|
|
; call to KickTx.
|
|
;-----------------------------------------------------------------------;
|
|
|
|
sub dl,ACE_MSR-ACE_IER ;--> Interrupt Enable Register
|
|
|
|
; flag port as being active
|
|
push cx
|
|
mov cl, [si.DCB_Id]
|
|
mov ax, 1
|
|
shl ax, cl
|
|
or [activeCOMs], ax
|
|
pop cx
|
|
|
|
mov al,ACE_ERBFI+ACE_ELSI+ACE_EDSSI
|
|
FCLI
|
|
out dx,al ;Enable interrupts.
|
|
add dl,ACE_LSR-ACE_IER ;--> Line Status Register
|
|
iodelay
|
|
in al,dx ;Clear any Line Status interrupt
|
|
sub dl,ACE_LSR ;--> Receiver Buffer Register
|
|
iodelay
|
|
in al,dx ;Clear any Received Data interrupt
|
|
FSTI
|
|
jmp SetCom5 ;All done
|
|
|
|
$SETCOM endp
|
|
page
|
|
|
|
FlagNotActive proc near
|
|
push cx
|
|
mov cl, [si.DCB_Id]
|
|
mov ax, NOT 1
|
|
rol ax, cl
|
|
and [activeCOMs], ax
|
|
pop cx
|
|
ret
|
|
FlagNotActive endp
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; SetCom100
|
|
;
|
|
; Copy the given DCB into the appropriate DEB. The check has
|
|
; already been made to determine that the ID was valid, so
|
|
; that check can be skipped.
|
|
;
|
|
; Entry:
|
|
; ES:BX --> DCB
|
|
; DS:SI --> DEB
|
|
; Returns:
|
|
; DS:SI --> DEB
|
|
; ES = Data
|
|
; Error Returns:
|
|
; None
|
|
; Registers Destroyed:
|
|
; AX,CX,ES,DI,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
SetCom100 proc near
|
|
push si ;Save DEB pointer
|
|
mov di,si
|
|
mov si,bx
|
|
push es
|
|
mov ax,ds
|
|
pop ds
|
|
assumes ds,nothing
|
|
|
|
mov es,ax
|
|
assumes es,Data
|
|
|
|
mov cx,DCBSize
|
|
cld
|
|
rep movsb
|
|
mov ds,ax
|
|
assumes ds,Data
|
|
|
|
pop si ;Restore DEB pointer
|
|
ret
|
|
|
|
SetCom100 endp
|
|
page
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; SetCom200
|
|
;
|
|
; Based on whether or not a timeout has been specified for each
|
|
; signal, set up a mask byte which is used to mask off lines for
|
|
; which we wish to detect timeouts. 0 indicates that the line is
|
|
; to be ignored.
|
|
;
|
|
; Also set up a mask to indicate those lines which are set for
|
|
; infinite timeout. 1 indicates that the line has infinite
|
|
; timeout.
|
|
;
|
|
; Entry:
|
|
; ES:BX --> DCB
|
|
; Returns:
|
|
; ES:BX --> DCB
|
|
; AH = lines to check
|
|
; AL = lines with infinite timeout
|
|
; Error Returns:
|
|
; None
|
|
; Registers Destroyed:
|
|
; AX,CX,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
SetCom200 proc near
|
|
|
|
xor ax,ax
|
|
xor cx,cx ;Get mask of lines with timeout = 0
|
|
call SetCom210
|
|
not al ;Invert result to get lines to check
|
|
and al,ACE_CTS+ACE_DSR+ACE_RLSD
|
|
xchg ah,al
|
|
dec cx ;Get mask of infinite timeouts
|
|
|
|
SetCom210:
|
|
cmp es:[bx.DCB_RlsTimeout],cx ;Timeout set to passed value?
|
|
jne SetCom220 ; No
|
|
or al,ACE_RLSD ; Yes, show checking line
|
|
|
|
SetCom220:
|
|
cmp es:[bx.DCB_CtsTimeout],cx ;Timeout set to passed value?
|
|
jne SetCom230 ; No
|
|
or al,ACE_CTS ; Yes, show checking line
|
|
|
|
SetCom230:
|
|
cmp es:[bx.DCB_DsrTimeout],cx ;Timeout set to passed value?
|
|
jne SetCom240 ; No
|
|
or al,ACE_DSR ; Yes, show checking line
|
|
|
|
SetCom240:
|
|
ret
|
|
|
|
SetCom200 endp
|
|
page
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; SetCom300
|
|
;
|
|
; Calculate the correct baudrate divisor for the comm chip.
|
|
;
|
|
; Note that the baudrate is allowed to be any integer in the
|
|
; range 2-19200. The divisor is computed as 115,200/baudrate.
|
|
;
|
|
; Entry:
|
|
; ES:BX --> DCB
|
|
; Returns:
|
|
; ES:BX --> DCB
|
|
; CX = baudrate
|
|
; Error Returns:
|
|
; CX = 0 if error
|
|
; AX = error code if invalid baud rate
|
|
; Registers Destroyed:
|
|
; AX,CX,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
BaudRateByIndexTable label word
|
|
dw 1047 ; CBR_110
|
|
dw 384 ; CBR_300
|
|
dw 192 ; CBR_600
|
|
dw 96 ; CBR_1200
|
|
dw 48 ; CBR_2400
|
|
dw 24 ; CBR_4800
|
|
dw 12 ; CBR_9600
|
|
dw 9 ; CBR_14400
|
|
dw 6 ; CBR_19200
|
|
dw 0 ; 0FF19h (reserved)
|
|
dw 0 ; 0FF1Ah (reserved)
|
|
dw 3 ; CBR_38400
|
|
dw 0 ; 0FF1Ch (reserved)
|
|
dw 0 ; 0FF1Dh (reserved)
|
|
dw 0 ; 0FF1Eh (reserved)
|
|
dw 2 ; CBR_56000
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
SetCom300 proc near
|
|
|
|
push dx
|
|
mov cx,es:[bx.DCB_BaudRate] ;Get requested baud rate
|
|
xor ax,ax ;Assume error
|
|
cmp cx, CBR_110 ;Q: baudrate specified as an index?
|
|
jae by_index
|
|
cmp cx,2 ;Within valid range?
|
|
jnae SetCom310 ; No, return error
|
|
|
|
mov dx,1 ;(dx:ax) = 115,200
|
|
mov ax,0C200h
|
|
div cx ;(ax) = 115,200/baud
|
|
|
|
SetCom310:
|
|
mov cx,ax ;(cx) = baud rate, or error code (0)
|
|
mov ax,IE_Baudrate ;Set error code incase bad baud
|
|
pop dx
|
|
ret
|
|
|
|
by_index:
|
|
cmp cx, CBR_56000 ;Q: above supported?
|
|
ja SetCom310 ; Y: return error
|
|
push bx
|
|
mov bx, cx
|
|
sub bx, CBR_110
|
|
shl bx, 1
|
|
mov ax, cs:[bx+BaudRateByIndexTable] ; get divisor
|
|
pop bx
|
|
jmp SetCom310 ; Y: return error
|
|
|
|
|
|
SetCom300 endp
|
|
page
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; SetCom400
|
|
;
|
|
; Check the line configuration (Parity, Stop bits, Byte size)
|
|
;
|
|
; Entry:
|
|
; ES:BX --> DCB
|
|
; Returns:
|
|
; ES:BX --> DCB
|
|
; 'C' clear if OK
|
|
; AL = Line Control Register
|
|
; AH = RxMask
|
|
; DI[15:8] = Flags mask (to remove parity checking)
|
|
; DI[7:0] = Error mask (to remove parity error)
|
|
; Error Returns:
|
|
; 'C' set if error
|
|
; AX = error code
|
|
; Registers Destroyed:
|
|
; AX,CX,DI,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
SetCom400 proc near
|
|
|
|
mov ax,wo es:[bx.DCB_ByteSize] ;al = byte size, ah = parity
|
|
cmp ah,SpaceParity ;Parity out of range?
|
|
ja SetCom470 ; Yes, return error
|
|
mov di,0FF00h+ACE_OR+ACE_PE+ACE_FE+ACE_BI
|
|
or ah,ah ;Is parity "NONE"?
|
|
jnz SetCom410 ; No, something is there for parity
|
|
xor di,(fParity*256)+ACE_PE ;Disable parity checking
|
|
|
|
SetCom410:
|
|
cmp al,8 ;Byte size out of range?
|
|
ja SetCom460 ; Yes, error
|
|
|
|
SetCom420:
|
|
sub al,5 ;Shift byte size to bits 0&1
|
|
.errnz ACE_WLS-00000011b ;Word length must be these bits
|
|
jc SetCom460 ;Byte size is illegal, return error
|
|
add ah,ah ;Map parity to ACE bits
|
|
jz SetCom430 ;0=>0, 1=>1, 2=>3, 3=>5, 4=>7
|
|
dec ah
|
|
|
|
SetCom430:
|
|
shl ah,1 ;Align with 8250 parity bits
|
|
shl ah,1
|
|
shl ah,1
|
|
or al,ah ;Add to byte size
|
|
|
|
.errnz NoParity-0
|
|
.errnz OddParity-1
|
|
.errnz EvenParity-2
|
|
.errnz MarkParity-3
|
|
.errnz SpaceParity-4
|
|
.errnz ACE_PEN-00001000b
|
|
.errnz ACE_PSB-00110000b
|
|
.errnz ACE_EPS-00010000b
|
|
.errnz ACE_SP-00100000b
|
|
|
|
or al,ACE_2SB ;Assume 2 stop bits
|
|
mov ah,es:[bx.DCB_StopBits] ;Get # of stop bits 0=1,1/2= .GT. 1
|
|
or ah,ah ;Out of range?
|
|
js SetCom470 ; Yes, return error
|
|
jz SetCom440 ;One stop bit
|
|
sub ah,2
|
|
jz SetCom450 ;Two stop bits
|
|
jns SetCom470 ;Not 1.5, return error
|
|
test al,ACE_WLS ;1.5 stop bits, 5 bit words?
|
|
jnz SetCom470 ; No, illegal
|
|
.errnz OneStopBit-0
|
|
.errnz One5StopBits-1
|
|
.errnz TwoStopBits-2
|
|
.errnz ACE_5BW
|
|
|
|
SetCom440:
|
|
and al,NOT ACE_2SB ;Show 1 (or 1.5) stop bit(s)
|
|
|
|
|
|
; From the byte size, get a mask to be used for stripping
|
|
; off unused bits as the characters are received.
|
|
|
|
SetCom450:
|
|
push dx
|
|
mov cl,es:[bx.DCB_ByteSize] ;Get data byte size
|
|
mov dx,00FFh ;Turn into mask by shifting bits
|
|
shl dx,cl
|
|
mov ah,dh ;Return mask in ah
|
|
pop dx
|
|
clc ;Show all is fine
|
|
ret
|
|
|
|
SetCom460:
|
|
mov ax,IE_ByteSize ;Show byte size is wrong
|
|
stc ;Show error
|
|
ret
|
|
|
|
SetCom470:
|
|
mov ax,IE_Default ;Show something is wrong
|
|
stc ;Show error
|
|
ret
|
|
|
|
SetCom400 endp
|
|
page
|
|
|
|
;----------------------------------------------------------------------------
|
|
; SuspendOpenCommPorts:
|
|
;
|
|
; This routine is called from 286 Winoldaps to simply deinstall the comm port
|
|
; hooks.
|
|
;----------------------------------------------------------------------------
|
|
|
|
cProc SuspendOpenCommPorts,<FAR,PUBLIC,PASCAL>
|
|
|
|
cBegin nogen
|
|
|
|
assumes cs,Code
|
|
assumes ds,Data
|
|
|
|
%OUT not masking IRQ's
|
|
|
|
; Nothing to do under 3.1!
|
|
|
|
ret
|
|
|
|
cEnd nogen
|
|
|
|
;----------------------------------------------------------------------------;
|
|
; ReactivateOpenCommPorts: ;
|
|
; ;
|
|
; This routine reinstalls the comm hooks in real mode and reads the 8250 ;
|
|
; data and status registers to clear pending interrupts. ;
|
|
;----------------------------------------------------------------------------;
|
|
|
|
cProc ReactivateOpenCommPorts,<FAR,PASCAL,PUBLIC>,<si,di>
|
|
|
|
cBegin
|
|
call Rotate_PIC ;make comm ports highest priority
|
|
|
|
mov cx, MAXCOM+1
|
|
mov di,dataOffset COMptrs
|
|
rcp_loop:
|
|
mov si, [di]
|
|
mov dx, Port[si]
|
|
or dx, dx
|
|
jz @f
|
|
call InitAPort ;read comm port regs to clr pending ints
|
|
@@:
|
|
add di, 2
|
|
loop rcp_loop
|
|
|
|
cEnd
|
|
|
|
;----------------------------------------------------------------------------;
|
|
; InitAPort: ;
|
|
; ;
|
|
; reads the data,status & IIR registers of a port (has to be 8250!) ;
|
|
; ;
|
|
; If the port has an out queue pending, then this woutine will also start ;
|
|
; the transmit process by faking a comm interrupt. ;
|
|
;----------------------------------------------------------------------------;
|
|
|
|
public InitAPort
|
|
InitAPort proc near
|
|
|
|
add dl,ACE_RBR ;dx=receive buffer register
|
|
in al,dx ;read the data port
|
|
jmp short $+2 ;i/o delay
|
|
add dl,ACE_LSR - ACE_RBR ;get to the status port
|
|
in al,dx ;read it too.
|
|
jmp short $+2 ;i/o delay
|
|
add dl,ACE_IIDR - ACE_LSR ;get to the line status register
|
|
in al,dx ;read it once more
|
|
jmp short $+2 ;i/o delay
|
|
add dl,ACE_MSR - ACE_IIDR ;get to the modem status register
|
|
in al,dx ;read it once more
|
|
jmp short $+2 ;i/o delay
|
|
add dl,ACE_RBR - ACE_MSR ;get to the receive buffer register
|
|
in al,dx ;read it once more
|
|
jmp short $+2 ;i/o delay
|
|
call UnmaskIRQ
|
|
|
|
; now if the port has characters pending to be sent out then we must fake a
|
|
; comm interrupt.
|
|
|
|
cmp [si].QOutCount,0 ;characters pending to be sent ?
|
|
jz @f ;no.
|
|
FCLI ;disable interrupts
|
|
call FakeCOMIntFar ;fake an interrupt
|
|
FSTI ;renable interrupts
|
|
@@:
|
|
ret
|
|
|
|
InitAPort endp
|
|
|
|
page
|
|
|
|
;----------------------------Public Routine-----------------------------;
|
|
;
|
|
; $DCBPtr - Return Pointer To DCB
|
|
;
|
|
; Returns a long pointer to the DCB for the requested device.
|
|
;
|
|
; Entry:
|
|
; AH = Device ID
|
|
; Returns:
|
|
; DX:AX = pointer to DCB.
|
|
; Error Returns:
|
|
; DX:AX = 0
|
|
; Registers Preserved:
|
|
; SI,DI,DS
|
|
; Registers Destroyed:
|
|
; BX,CX,ES,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public $DCBPTR
|
|
$DCBPTR proc near
|
|
|
|
push si
|
|
xor dx,dx
|
|
call GetDEB ;Get pointer to DEB
|
|
mov ax,dx
|
|
jc DCBPtr10 ;Jump if invalid device
|
|
mov ax,si ;else return value here
|
|
mov dx,ds
|
|
|
|
DCBPtr10:
|
|
pop si
|
|
ret
|
|
|
|
$DCBPTR endp
|
|
page
|
|
|
|
;----------------------------Private-Routine----------------------------;
|
|
;
|
|
; GetDEB - Get Pointer To Device's DEB
|
|
;
|
|
; Returns a pointer to appropriate DEB, based on device number.
|
|
;
|
|
; Entry:
|
|
; AH = cid
|
|
; Returns:
|
|
; 'C' clear
|
|
; 'S' set if LPT device
|
|
; DS:SI --> DEB is valid cid
|
|
; AH = cid
|
|
; Error Returns:
|
|
; 'C' set if error (cid is invalid)
|
|
; AX = 8000h
|
|
; Registers Preserved:
|
|
; BX,CX,DX,DI,DS,ES
|
|
; Registers Destroyed:
|
|
; AX,SI,FLAGS
|
|
; History:
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;------------------------------Pseudo-Code------------------------------;
|
|
; {
|
|
; }
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public GetDEB ;Public for debugging
|
|
GetDEB proc near
|
|
|
|
push cx
|
|
mov cl, ah
|
|
and cx, (NOT LPTx AND 0FFh)
|
|
test ah, ah ;Q: LPT id?
|
|
js short GetDEB10 ; Y:
|
|
.errnz LPTx - 80h
|
|
cmp ah, MAXCOM ;Q: Within range?
|
|
ja GetDEB30 ; N: return invalid ID
|
|
shl cx, 1
|
|
mov si, cx
|
|
mov si, [si+COMptrs]
|
|
jmp short GetDEB20
|
|
|
|
GetDEB10:
|
|
cmp ah, LPTx+MAXLPT ;Q: Within range?
|
|
ja GetDEB30 ; N: return invalid ID
|
|
mov si, DataOFFSET LPT1
|
|
jcxz GetDEB20
|
|
GetDEB15:
|
|
add si, SIZE LptDEB
|
|
loop GetDEB15
|
|
GetDEB20:
|
|
pop cx
|
|
or ah, ah ; clear Carry & set S, if LPT port
|
|
ret
|
|
|
|
GetDEB30:
|
|
pop cx
|
|
mov ax,8000h ;Set error code
|
|
stc ;Set 'C' to show error
|
|
ret
|
|
|
|
GetDEB endp
|
|
page
|
|
|
|
|
|
CvtHex proc near
|
|
; assume DS=SS
|
|
push si
|
|
mov cl, 4
|
|
mov si, di
|
|
xor dx, dx
|
|
cld
|
|
ch_lp:
|
|
lodsb
|
|
sub al, '0' ;Q: char < '0'
|
|
jb ch_exit ; Y: return
|
|
cmp al, 9 ;Q: char <= '9'
|
|
jbe ch_got_digit ; Y: move digit into result
|
|
sub al, 'A' - '0' ;Q: char < 'A'
|
|
jb ch_exit ; Y: return
|
|
add al, 10
|
|
cmp al, 15 ;Q: char <= 'F'
|
|
jbe ch_got_digit ; Y: move hex char into result
|
|
sub al, 10 + 'a' - 'A' ;Q: char < 'a'
|
|
jb ch_exit ; Y: return
|
|
add al, 10
|
|
cmp al, 15 ;Q: char > 'f'
|
|
ja ch_exit ; Y: return
|
|
ch_got_digit:
|
|
shl dx, cl
|
|
or dl, al
|
|
jmp ch_lp
|
|
ch_exit:
|
|
mov ax, dx
|
|
pop si
|
|
ret
|
|
CvtHex endp
|
|
|
|
.286
|
|
; attempt to read base from SYSTEM.INI
|
|
GetComBase proc near
|
|
push ds ; save our DS
|
|
sub sp, 6
|
|
mov di, sp
|
|
mov byte ptr ss:[di], 0
|
|
push ds
|
|
push DataOFFSET lpCommSection
|
|
push ds
|
|
push DataOFFSET lpCommBase
|
|
push ss ; temp buffer
|
|
push di
|
|
push ss ; default = temp buffer
|
|
push di
|
|
push 5
|
|
push ds
|
|
push DataOFFSET lpSYSTEMINI
|
|
mov cx, ss ; temporarily assign DS=SS
|
|
mov ds, cx ; to allow KERNEL to thunk
|
|
assumes ds,nothing
|
|
call GetPrivateProfileString ; our segment in real mode
|
|
or ax, ax
|
|
jz short gcb_exit
|
|
call CvtHex ; DS still equal to SS
|
|
gcb_exit:
|
|
add sp, 6
|
|
pop ds ; restore our DS
|
|
assumes ds,Data
|
|
ret
|
|
GetComBase endp
|
|
|
|
GetPortIRQ proc near
|
|
push ds ; save our DS
|
|
push ds
|
|
push DataOFFSET lpCommSection
|
|
push ds
|
|
push DataOFFSET lpCommIrq
|
|
push bx
|
|
mov bl, [si.DCB_Id]
|
|
cmp bl, 4
|
|
jb @f
|
|
mov bl, 4
|
|
@@:
|
|
xor bh, bh
|
|
mov bl, [bx+default_table]
|
|
mov cx, bx
|
|
pop bx
|
|
push cx ; default
|
|
push ds
|
|
push DataOFFSET lpSYSTEMINI
|
|
mov cx, ss ; temporarily assign DS=SS
|
|
mov ds, cx ; to allow KERNEL to thunk
|
|
assumes ds,nothing
|
|
call GetPrivateProfileInt ; our segment in real mode
|
|
pop ds ; restore our DS
|
|
assumes ds,Data
|
|
ret
|
|
GetPortIRQ endp
|
|
|
|
|
|
GetPortFlags proc near
|
|
mov al, [si.DCB_Id]
|
|
.erre MAXCOM LT 9 ;only single digit port numbers supported
|
|
add al, '1'
|
|
mov [CommFIFOX], al
|
|
mov [CommDSRx], al
|
|
call GetPortFIFO
|
|
call GetPortDSR
|
|
ret
|
|
GetPortFlags endp
|
|
|
|
GetPortFIFO proc near
|
|
push ds ; save our DS
|
|
push ds
|
|
push DataOFFSET lpCommSection
|
|
push ds
|
|
push DataOFFSET lpCommFifo
|
|
push 2
|
|
push ds
|
|
push DataOFFSET lpSYSTEMINI
|
|
mov cx, ss ; temporarily assign DS=SS
|
|
mov ds, cx ; to allow KERNEL to thunk
|
|
assumes ds,nothing
|
|
call GetPrivateProfileInt ; our segment in real mode
|
|
pop ds ; restore our DS
|
|
assumes ds,Data
|
|
cmp ax, 1
|
|
ja short gpf_exit ; just check at open
|
|
jb short gpf_no_fifo ; force OFF, if = 0
|
|
or EFlags[si], fFIFOchkd ; flag as checked, to force ON
|
|
jmp short gpf_exit
|
|
|
|
gpf_no_fifo:
|
|
or EFlags[si], fNoFIFO OR fFIFOchkd ; force OFF
|
|
|
|
gpf_exit:
|
|
ret
|
|
GetPortFIFO endp
|
|
|
|
GetPortDSR proc near
|
|
push ds ; save our DS
|
|
push ds
|
|
push DataOFFSET lpCommSection
|
|
push ds
|
|
push DataOFFSET lpCommDSR
|
|
push 0
|
|
push ds
|
|
push DataOFFSET lpSYSTEMINI
|
|
mov cx, ss ; temporarily assign DS=SS
|
|
mov ds, cx ; to allow KERNEL to thunk
|
|
assumes ds,nothing
|
|
call GetPrivateProfileInt ; our segment in real mode
|
|
pop ds ; restore our DS
|
|
assumes ds,Data
|
|
or ax, ax
|
|
jz short gpd_exit
|
|
or EFlags[si], fUseDSR
|
|
|
|
gpd_exit:
|
|
ret
|
|
GetPortDSR endp
|
|
|
|
|
|
; FindCOMPort
|
|
;
|
|
; DS:SI -> DEB
|
|
;
|
|
PUBLIC FindCOMPort
|
|
FindCOMPort proc near
|
|
;
|
|
; Examine BIOS data area to get base I/O addresses for COM and LPT ports
|
|
;
|
|
push bx
|
|
push cx
|
|
push es
|
|
mov ax, __0040H
|
|
mov es, ax
|
|
assumes es,nothing
|
|
|
|
mov al, [si.DCB_Id]
|
|
mov ah, al
|
|
.erre MAXCOM LT 9 ;only single digit port numbers supported
|
|
add ah, '1'
|
|
mov [CommBaseX], ah
|
|
mov [CommIRQX], ah
|
|
mov [CommFIFOX], ah
|
|
mov [CommDSRx], ah
|
|
|
|
cmp al, 4
|
|
jae fcp_not_phys_com
|
|
xor ah, ah
|
|
shl ax, 1
|
|
mov bx, ax
|
|
mov ax, es:[bx+RS232B]
|
|
or ax, ax
|
|
jnz fcp_got_com_base
|
|
fcp_not_phys_com:
|
|
call GetComBase
|
|
or ax, ax
|
|
jnz fcp_got_com_base
|
|
mov bl, [si.DCB_Id]
|
|
cmp bl, 2
|
|
jne fcp_invalid ; jump, if base = 0 & com port <> com3
|
|
mov ax, 3E8h ; default COM3 to 3E8h
|
|
fcp_got_com_base:
|
|
push ax
|
|
call GetPortIRQ
|
|
mov dx, ax
|
|
pop ax
|
|
or dl, dl ;Q: non-zero IRQ?
|
|
jz fcp_invalid ; N:
|
|
cmp dl, 15 ;Q: IRQ in range?
|
|
ja fcp_invalid ; N:
|
|
xor dh, dh
|
|
push ax
|
|
push dx
|
|
call GetPortFIFO
|
|
call GetPortDSR
|
|
pop dx
|
|
pop ax
|
|
clc
|
|
fcp_exit:
|
|
pop es
|
|
pop cx
|
|
pop bx
|
|
ret
|
|
|
|
fcp_invalid:
|
|
or ax, -1
|
|
mov dx, ax
|
|
stc
|
|
jmp fcp_exit
|
|
|
|
FindCOMPort endp
|
|
.8086
|
|
|
|
page
|
|
;--------------------------Private Routine-----------------------------;
|
|
;
|
|
; Rotate the PIC interrupt priorities so the communication ports are
|
|
; highest priority.
|
|
;
|
|
; NOTE: Only rotates priorities on master PIC.
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
assumes ds,Data
|
|
assumes es,nothing
|
|
|
|
public Rotate_PIC
|
|
|
|
Rotate_PIC proc near
|
|
|
|
push ax
|
|
push cx
|
|
push di
|
|
|
|
mov al, 8 ; 0 - 7 rotated
|
|
mov cx, MAXCOM+1
|
|
mov di, DataOFFSET IRQhooks
|
|
rp_loop:
|
|
mov ah, [di.IRQn]
|
|
cmp ah, 0 ;End of hooked IRQ list?
|
|
je rp_set
|
|
cmp [di.VecN], 0FFh ;Hooked?
|
|
je rp_next
|
|
cmp ah, 8 ;If on slave PIC, treat as IRQ2
|
|
jb @f
|
|
mov ah, 2
|
|
@@:
|
|
cmp ah, al
|
|
jae rp_next
|
|
mov al, ah ;AL = lowest hooked comm IRQ
|
|
rp_next:
|
|
add di, SIZE IRQ_Hook_Struc
|
|
loop rp_loop
|
|
|
|
rp_set:
|
|
dec al ;Setting IRQ(n-1) as the lowest
|
|
and al, 07h ; priority makes IRQn the highest
|
|
or al, 0C0h
|
|
out INTA0, al
|
|
|
|
pop di
|
|
pop cx
|
|
pop ax
|
|
ret
|
|
|
|
Rotate_PIC endp
|
|
|
|
|
|
ifdef DEBUG
|
|
public InitCom10, InitCom20, InitCom40, InitCom50, InitCom59
|
|
public InitCom60, InitCom70, InitCom80, InitCom90, InitCom100
|
|
public TermCom10, TermCom15, TermCom20, TermCom30
|
|
public TermCom60, Terminate5, Terminate10, Terminate20, Terminate30
|
|
public Terminate45, Terminate49, Terminate50
|
|
public SetQue10
|
|
public SetCom5, SetCom10, SetCom20, SetCom210, SetCom220, SetCom230
|
|
public SetCom240, SetCom310, SetCom410, SetCom420, SetCom430
|
|
public SetCom440, SetCom450, SetCom460, SetCom470
|
|
public GetDEB10, GetDEB20, GetDEB30
|
|
public DCBPtr10
|
|
endif
|
|
|
|
sEnd code
|
|
|
|
page
|
|
|
|
createSeg _INIT,init,word,public,CODE
|
|
sBegin init
|
|
assumes cs,init
|
|
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
;------------------------------------------------------------------------------
|
|
IBMmodel proc near
|
|
push ax
|
|
push bx
|
|
push es
|
|
|
|
mov ah, 0c0h
|
|
int 15h
|
|
jc IBMmodel_exit
|
|
|
|
assumes es,nothing
|
|
|
|
cmp by es:[bx+2], 0f8h ; PS/2 80
|
|
je IBMmodel_exit ; return carry clear
|
|
|
|
cmp by es:[bx+2], 0fch ; AT or PS/2 50 or 60
|
|
jne OldBios ; assume OldBios
|
|
|
|
cmp by es:[bx+3], 04h ; PS/2 50
|
|
je IBMmodel_exit ; return carry clear
|
|
cmp by es:[bx+3], 05h ; PS/2 60
|
|
je IBMmodel_exit ; return carry clear
|
|
|
|
OldBios:
|
|
stc
|
|
|
|
IBMmodel_exit:
|
|
pop es
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
|
|
IBMmodel endp
|
|
|
|
cProc LoadLib, <FAR,PUBLIC,NODATA>,<si,di>
|
|
cBegin
|
|
|
|
push ds
|
|
mov ax, __F000H
|
|
mov ds, ax
|
|
assumes ds, ROMBios
|
|
mov al, [MachineID]
|
|
pop ds
|
|
assumes ds,Data
|
|
mov [$MachineID], al
|
|
|
|
call IBMmodel ;Q: PS/2?
|
|
jc @F ; N:
|
|
mov [default_table+2], 3 ; Y: change COM3 default IRQ to 3
|
|
@@:
|
|
|
|
push ds
|
|
mov ax, __0040H
|
|
mov ds, ax
|
|
assumes ds,nothing
|
|
cmp word ptr ds:[RS232B], 2F8h ;Q: COM2 base in COM1 slot?
|
|
pop ds
|
|
assumes ds,Data
|
|
jne @F ; N: leave IRQ default as 4
|
|
mov [default_table], 3 ; Y: change IRQ default to 3
|
|
@@:
|
|
|
|
mov [fVPICD], -1 ; assume no
|
|
|
|
xor di, di
|
|
mov es, di
|
|
mov ax, GET386API
|
|
mov bx, VPICD
|
|
int MULTIPLEX
|
|
mov wo [lpfnVPICD], di
|
|
mov wo [lpfnVPICD+2], es
|
|
mov ax, es
|
|
or ax, di
|
|
jz short no_VPICD ; jump if no bimodel services available
|
|
;
|
|
; version check VPICD
|
|
;
|
|
mov ax, VPICD_API_Get_Ver
|
|
call [lpfnVPICD]
|
|
%OUT version check VPICD
|
|
|
|
mov [fVPICD], 1 ; flag services are available
|
|
|
|
IFDEF No_DOSX_Bimodal_Services
|
|
jmp short skip_dosx_stuff
|
|
|
|
no_VPICD:
|
|
mov ax, __WinFlags
|
|
and al, WF_PMODE or WF_WIN286
|
|
cmp al, WF_PMODE or WF_WIN286
|
|
jne skip_dosx_stuff
|
|
|
|
.286
|
|
mov ax, Int31_Get_Version SHL 8
|
|
int 31h
|
|
test bl, 10b ;Q: processor goes to real mode
|
|
; for int reflection?
|
|
jz skip_dosx_stuff ; N:
|
|
mov [Using_DPMI], 0FFh ; Y: flag use of DPMI
|
|
|
|
mov ax, ds
|
|
cCall GetSelectorBase,<ax> ;DX:AX = segment of selector
|
|
shr ax, 4
|
|
shl dl, 4
|
|
or ah, dl ;AX now points to interrupt *segment*
|
|
push ax ;save on stack
|
|
mov ax, _INTERRUPT ;write data SEGMENT into _INTERRUPT
|
|
cCall AllocCStoDSAlias,<ax> ; code segment -- requires a data alias
|
|
mov es, ax
|
|
pop ax
|
|
mov es:[RM_IntDataSeg],ax
|
|
push ds
|
|
push es
|
|
mov ax, ds
|
|
mov es, ax
|
|
mov ax, _INTERRUPT
|
|
mov ds, ax
|
|
mov ax, (Int31_Trans_Serv SHL 8) + Trans_Call_Back
|
|
mov si, IntCodeOFFSET Entry_From_RM
|
|
mov di, DataOFFSET RM_Call_Struc
|
|
int 31h
|
|
pop es
|
|
pop ds
|
|
mov ax, 0
|
|
jnc @f
|
|
jmp short LoadExit
|
|
@@:
|
|
mov wo es:[RM_CallBack], dx
|
|
mov wo es:[RM_CallBack+2], cx
|
|
cCall FreeSelector,<es> ;don't need CS alias any longer
|
|
.8086
|
|
skip_dosx_stuff:
|
|
ELSE
|
|
no_VPICD:
|
|
ENDIF
|
|
|
|
;
|
|
; find base values for LPT ports
|
|
;
|
|
mov cx, __0040h
|
|
mov es, cx
|
|
mov cx, MAXLPT+1
|
|
mov si, DataOFFSET LPT1
|
|
ll_initl_lp:
|
|
mov bx, [si.BIOSPortLoc]
|
|
or bx, bx
|
|
jz ll_not_phys_lpt
|
|
mov ax, es:[bx]
|
|
or ah, ah ;Q: lpt redirected, or 0?
|
|
jz ll_not_phys_lpt ; Y:
|
|
cmp bx, LPTB ;Q: first LPT?
|
|
je ll_got_lpt_base ; Y:
|
|
cmp ax, es:[bx-2] ;Q: base same as previous (redirected)?
|
|
jne ll_got_lpt_base ; N: must be real
|
|
ll_not_phys_lpt:
|
|
%OUT attempt to read base from SYSTEM.INI
|
|
|
|
ll_got_lpt_base:
|
|
mov [si.Port], ax
|
|
loop ll_initl_lp
|
|
|
|
;
|
|
; create system timer for signalling chars in receive buffer
|
|
;
|
|
|
|
ifndef WOW
|
|
mov ax, 100 ; create 100msec timer
|
|
push ax
|
|
mov ax, _INTERRUPT
|
|
push ax
|
|
mov ax, IntCodeOFFSET TimerProc
|
|
push ax
|
|
call CreateSystemTimer ; ax = 0, if failed
|
|
%OUT should I display an error message here?
|
|
|
|
endif
|
|
assumes es,nothing
|
|
LoadExit:
|
|
cEnd
|
|
|
|
sEnd init
|
|
|
|
End LoadLib
|