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 ifdef NEC_98 externFP KillSystemTimer ;Ins 940923 KBNES externFP TickEntry4 ;Ins 940923 KBNES else ; NEC_98 externFP WowCloseComPort endif ; NEC_98 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 ifdef NEC_98 CM_VxD_Device_ID EQU 034h CM_GetVersion EQU 0 CM_GetConfig EQU 1 CM_LockConfig EQU 2 CM_UnlockConfig EQU 3 CM_ConfigBuffSize EQU SIZE Config_Info_Struc Max_Mem_Registers EQU 9 Max_IO_Ports EQU 20 Max_IRQs EQU 7 Max_DMA_Channels EQU 7 Config_info_Struc STRUC CIS_Bus_id dd ? CIS_Device_id dd ? CIS_Serial_Number dd ? CIS_Log_ID dd ? CIS_Flags dd ? ; ; NOTE:For this example,we are using the ISA Plug&Play card access data only ; since the sample driver is for a theoretical new ISA card. ; CIS_CSN db ? CIS_LogicalDevNumber db ? CIS_Read_data_port dw ? ; ; Below this point is data common to all device on all busses ; CIS_NumMemWindows dw ? CIS_MemBase dd Max_Mem_Registers dup(?) CIS_MemLength dd Max_Mem_Registers dup(?) CIS_MemAttrib dw Max_Mem_Registers dup(?) CIS_NumIOPorts dw ? CIS_IOPort_Base dw Max_IO_Ports dup(?) CIS_IOPort_Length dw Max_IO_Ports dup(?) CIS_NumIRQs dw ? CIS_IRQRegisters db Max_IRQs dup(?) CIS_IRQAttrib db Max_IRQs dup(?) CIS_NumDMAs dw ? CIS_DMAList db Max_DMA_Channels dup(?) CIS_DMAAttrib dw Max_DMA_Channels dup(?) CIS_Reserved db 3 dup(?) Config_Info_Struc ENDS CM_Entry_Point LABEL DWORD CM_Entry_Point_Off dw ? CM_Entry_Point_Seg dw ? My_Config_Buffer Config_Info_Struc<> My_Search_Buffer LABEL DWORD dd ? ;My_Bus_ID dd ? ;My_Dev_ID dd ? ;My_Serial_Num My_Log_ID dd 04180a3b8h ;My_Log_ID Fax ID 94.08.02 endif ; NEC_98 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, ;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 ifndef NEC_98 ;----------------------------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 endif ; NEC_98 ;----------------------------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 ifndef NEC_98 ;----------------------------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 endif ; NEC_98 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 ifndef NEC_98 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 endif ; NEC_98 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 ifndef NEC_98 call GetCOMport386 jc InitCom17 endif ; NEC_98 @@: ifdef NEC_98 push ax ;Ins 940923 KBNES lea ax,TickEntry4 ;Ins 940923 KBNES mov TimerProcAdr[si],ax ;Ins 940923 KBNES pop ax ;Ins 940923 KBNES endif ; NEC_98 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: ifdef NEC_98 jmp InitCom35 ;Add 1994.08.02 KBNES endif ; NEC_98 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: ifndef NEC_98 call ReleaseCOMport386 ; give port back to 386... endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 ;-------------------------------------------------------------- Ins 940923 KBNES ; Close Process of System Timer Ins 940923 KBNES ;-------------------------------------------------------------- Ins 940923 KBNES push ax ; Ins 940923 KBNES mov ax,hTimer[si] ; Check hTimer Ins 940923 KBNES or ax,ax ; Is there TimerProcess?Ins 940923 KBNES jz NoKillTimer ; Y:(Nothing) Ins 940923 KBNES cCall KillSystemTimer, ; Kill System Timer Ins 940923 KBNES mov hTimer[si] ,NULL ; Clear hTimer Ins 940923 KBNES NoKillTimer: ; Ins 940923 KBNES pop ax ; Ins 940923 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifndef NEC_98 call ReleaseCOMport386 ; give port back to 386... endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 inc dx ;--> LCR and clear divisor access bit inc dx .errnz ACE_LCR-ACE_DLM-2 pop ax out dx,al ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 test EFlags[si], fFIFOchkd ;Q: FIFO detect been done? jnz sc_fifodone ; Y: enabled FIFO iodelay .errnz ACE_IIDR-ACE_FCR in al, dx ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 push dx cCall GetSystemMsecCount pop dx sub ax, cx cmp ax, DELAY_TIME ;Q: Timeout reached? ifdef NEC_98 jb delay_loop ; N: else ; NEC_98 ifndef WOW jb delay_loop ; N: endif endif ; NEC_98 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) ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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, 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 ifdef NEC_98 ;---------------------------------------------------------------Ins 940923 KBNES ; Close process of system timer Ins 940923 KBNES ;---------------------------------------------------------------Ins 940923 KBNES push dx ; Ins 940924 KBNES push ax ; Ins 940923 KBNES mov ax,hTimer[si] ; Check hTimer Ins 940923 KBNES or ax,ax ; TimerProcess Nothing? Ins 940923 KBNES jz SetNoKillTimer ; Y:(Nothing) Ins 940923 KBNES cCall KillSystemTimer, ; Kill Syatem Timer Ins 940923 KBNES mov hTimer[si] ,00h ; Clear hTimer Ins 940923 KBNES SetNoKillTimer: ; Ins 940923 KBNES ;---------------------------------------------------------------Ins 940923 KBNES ; make process of system timer Ins 940923 KBNES ;---------------------------------------------------------------Ins 940923 KBNES mov ax, 200 ; create 200msec timer Ins 940923 KBNES push ax ; Ins 940923 KBNES mov ax, _INTERRUPT ; Ins 940923 KBNES push ax ; Ins 940923 KBNES mov ax,TimerProcAdr[si] ;--> timer processor Ins 940923 KBNES push ax ; Ins 940923 KBNES call CreateSystemTimer ; ax = 0, if failed Ins 940923 KBNES mov hTimer[si] ,ax ; Get hTimer Ins 940923 KBNES SetCom3003: ; Ins 940923 KBNES pop ax ; Ins 940923 KBNES pop dx ; Ins 940924 KBNES endif ; NEC_98 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES endif ; NEC_98 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 ifdef NEC_98 dw 0 ; 0FF20h (reserved) dw 0 ; 0FF21h (reserved) dw 0 ; 0FF22h (reserved) dw 1 ; CBR_128000 endif ; NEC_98 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: ifdef NEC_98 cmp cx, CBR_128000 ;ins 95.03.25 KBNES else ; NEC_98 cmp cx, CBR_56000 ;Q: above supported? endif ; NEC_98 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, 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,, 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 ifdef NEC_98 iodelay ;1994.08.01 KBNES else ; NEC_98 jmp short $+2 ;i/o delay endif ; NEC_98 add dl,ACE_LSR - ACE_RBR ;get to the status port in al,dx ;read it too. ifdef NEC_98 iodelay ;1994.08.01 KBNES else ; NEC_98 jmp short $+2 ;i/o delay endif ; NEC_98 add dl,ACE_IIDR - ACE_LSR ;get to the line status register in al,dx ;read it once more ifdef NEC_98 iodelay ;1994.08.01 KBNES else ; NEC_98 jmp short $+2 ;i/o delay endif ; NEC_98 add dl,ACE_MSR - ACE_IIDR ;get to the modem status register in al,dx ;read it once more ifdef NEC_98 iodelay ;1994.08.01 KBNES else ; NEC_98 jmp short $+2 ;i/o delay endif ; NEC_98 add dl,ACE_RBR - ACE_MSR ;get to the receive buffer register in al,dx ;read it once more ifdef NEC_98 iodelay ;1994.08.01 KBNES else ; NEC_98 jmp short $+2 ;i/o delay endif ; NEC_98 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? ifdef NEC_98 js short GetDEB30 ; Y: error if LPT else ; NEC_98 js short GetDEB10 ; Y: endif ; NEC_98 .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] ifndef NEC_98 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: endif ; NEC_98 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 ifndef NEC_98 mov ax, __0040H mov es, ax assumes es,nothing endif ; NEC_98 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 ifdef NEC_98 mov ax,238h ;I/O Base Addr Set mov dx,5 ;IRQ Set else ; NEC_98 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 endif ; NEC_98 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 ifndef NEC_98 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 endif ; NEC_98 ret Rotate_PIC endp ifdef DEBUG ifdef NEC_98 public InitCom20, InitCom40, InitCom50, InitCom59 else ; NEC_98 public InitCom10, InitCom20, InitCom40, InitCom50, InitCom59 endif ; NEC_98 public InitCom60, InitCom70, InitCom80, InitCom90, InitCom100 public TermCom10, TermCom15, TermCom20, TermCom30 public TermCom60, Terminate5, Terminate10, Terminate20, Terminate30 public Terminate45, Terminate49, Terminate50 public SetQue10 ifdef NEC_98 public SetCom5, SetCom20, SetCom210, SetCom220, SetCom230 else ; NEC_98 public SetCom5, SetCom10, SetCom20, SetCom210, SetCom220, SetCom230 endif ; NEC_98 public SetCom240, SetCom310, SetCom410, SetCom420, SetCom430 public SetCom440, SetCom450, SetCom460, SetCom470 ifdef NEC_98 public GetDEB30 else ; NEC_98 public GetDEB10, GetDEB20, GetDEB30 endif ; NEC_98 public DCBPtr10 endif sEnd code page createSeg _INIT,init,word,public,CODE sBegin init assumes cs,init ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ifndef NEC_98 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 endif ; NEC_98 cProc LoadLib, , cBegin ifndef NEC_98 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 @@: endif ; NEC_98 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, ;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, ; 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, ;don't need CS alias any longer .8086 skip_dosx_stuff: ELSE no_VPICD: ENDIF ifndef NEC_98 ; ; 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 endif ; NEC_98 ; ; 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