blob: 42b5cf3692e82dfeb543655de8ece3a0a0228fe3 [file] [log] [blame]
;****************************************************************************
;*
;* SciTech OS Portability Manager Library
;*
;* ========================================================================
;*
;* The contents of this file are subject to the SciTech MGL Public
;* License Version 1.0 (the "License"); you may not use this file
;* except in compliance with the License. You may obtain a copy of
;* the License at http://www.scitechsoft.com/mgl-license.txt
;*
;* Software distributed under the License is distributed on an
;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
;* implied. See the License for the specific language governing
;* rights and limitations under the License.
;*
;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
;*
;* The Initial Developer of the Original Code is SciTech Software, Inc.
;* All Rights Reserved.
;*
;* ========================================================================
;*
;* Language: 80386 Assembler, TASM 4.0 or NASM
;* Environment: IBM PC Real mode and 16/32 bit protected mode
;*
;* Description: Low level assembly support for the PM library specific to
;* MSDOS.
;*
;****************************************************************************
IDEAL
include "scitech.mac" ; Memory model macros
header _pmdos ; Set up memory model
begdataseg _pmdos
ifndef flatmodel
struc rmregs_s
ax dw ?
ax_high dw ?
bx dw ?
bx_high dw ?
cx dw ?
cx_high dw ?
dx dw ?
dx_high dw ?
si dw ?
si_high dw ?
di dw ?
di_high dw ?
cflag dw ?
cflag_high dw ?
ends rmregs_s
RMREGS = (rmregs_s PTR es:bx)
struc rmsregs_s
es dw ?
cs dw ?
ss dw ?
ds dw ?
ends rmsregs_s
RMSREGS = (rmsregs_s PTR es:bx)
endif ; !flatmodel
ifdef flatmodel
cextern _PM_savedDS,USHORT
cextern _PM_VXD_off,UINT
cextern _PM_VXD_sel,UINT
ifdef DOS4GW
cextern _PM_haveCauseWay,UINT
endif
endif
intel_id db "GenuineIntel" ; Intel vendor ID
PMHELP_GETPDB EQU 0026h
PMHELP_FLUSHTLB EQU 0027h
enddataseg _pmdos
P586
begcodeseg _pmdos ; Start of code segment
ifndef flatmodel
;----------------------------------------------------------------------------
; void PM_callRealMode(unsigned s,unsigned o, RMREGS *regs,
; RMSREGS *sregs)
;----------------------------------------------------------------------------
; Calls a real mode procedure, loading the appropriate registers values
; from the passed in structures. Only the DS and ES register are loaded
; from the SREGS structure.
;----------------------------------------------------------------------------
cprocstart PM_callRealMode
ARG s:WORD, o:WORD, regs:DWORD, sregs:DWORD
LOCAL addr:DWORD, bxVal:WORD, esVal:WORD, flags:WORD = LocalSize
enter_c
push ds
push es
mov ax,[o] ; Build the address to call in 'addr'
mov [WORD addr],ax
mov ax,[s]
mov [WORD addr+2],ax
les bx,[sregs]
mov ax,[RMSREGS.ds]
mov ds,ax ; DS := passed in value
mov ax,[RMSREGS.es]
mov [esVal],ax
les bx,[regs]
mov ax,[RMREGS.bx]
mov [bxVal],ax
mov ax,[RMREGS.ax] ; AX := passed in value
mov cx,[RMREGS.cx] ; CX := passed in value
mov dx,[RMREGS.dx] ; DX := passed in value
mov si,[RMREGS.si] ; SI := passed in value
mov di,[RMREGS.di] ; DI := passed in value
push bp
push [esVal]
pop es ; ES := passed in value
mov bx,[bxVal] ; BX := passed in value
call [addr] ; Call the specified routine
pushf ; Save flags for later
pop [flags]
pop bp
push es
pop [esVal]
push bx
pop [bxVal]
les bx,[sregs]
push ds
pop [RMSREGS.ds] ; Save value of DS
push [esVal]
pop [RMSREGS.es] ; Save value of ES
les bx,[regs]
mov [RMREGS.ax],ax ; Save value of AX
mov [RMREGS.cx],cx ; Save value of CX
mov [RMREGS.dx],dx ; Save value of DX
mov [RMREGS.si],si ; Save value of SI
mov [RMREGS.di],di ; Save value of DI
mov ax,[flags] ; Return flags
and ax,1h ; Isolate carry flag
mov [RMREGS.cflag],ax ; Save carry flag status
mov ax,[bxVal]
mov [RMREGS.bx],ax ; Save value of BX
pop es
pop ds
leave_c
ret
cprocend
endif
;----------------------------------------------------------------------------
; void PM_segread(PMSREGS *sregs)
;----------------------------------------------------------------------------
; Read the current value of all segment registers
;----------------------------------------------------------------------------
cprocstartdll16 PM_segread
ARG sregs:DPTR
enter_c
mov ax,es
_les _si,[sregs]
mov [_ES _si],ax
mov [_ES _si+2],cs
mov [_ES _si+4],ss
mov [_ES _si+6],ds
mov [_ES _si+8],fs
mov [_ES _si+10],gs
leave_c
ret
cprocend
; Create a table of the 256 different interrupt calls that we can jump
; into
ifdef USE_NASM
%assign intno 0
intTable:
%rep 256
db 0CDh
db intno
%assign intno intno + 1
ret
nop
%endrep
else
intno = 0
intTable:
REPT 256
db 0CDh
db intno
intno = intno + 1
ret
nop
ENDM
endif
;----------------------------------------------------------------------------
; _PM_genInt - Generate the appropriate interrupt
;----------------------------------------------------------------------------
cprocnear _PM_genInt
push _ax ; Save _ax
push _bx ; Save _bx
ifdef flatmodel
mov ebx,[UINT esp+12] ; EBX := interrupt number
else
mov bx,sp ; Make sure ESP is zeroed
mov bx,[UINT ss:bx+6] ; BX := interrupt number
endif
mov _ax,offset intTable ; Point to interrupt generation table
shl _bx,2 ; _BX := index into table
add _ax,_bx ; _AX := pointer to interrupt code
ifdef flatmodel
xchg eax,[esp+4] ; Restore eax, and set for int
else
mov bx,sp
xchg ax,[ss:bx+2] ; Restore ax, and set for int
endif
pop _bx ; restore _bx
ret
cprocend
;----------------------------------------------------------------------------
; int PM_int386x(int intno, PMREGS *in, PMREGS *out,PMSREGS *sregs)
;----------------------------------------------------------------------------
; Issues a software interrupt in protected mode. This routine has been
; written to allow user programs to load CS and DS with different values
; other than the default.
;----------------------------------------------------------------------------
cprocstartdll16 PM_int386x
ARG intno:UINT, inptr:DPTR, outptr:DPTR, sregs:DPTR
LOCAL flags:UINT, sv_ds:UINT, sv_esi:ULONG = LocalSize
enter_c
push ds
push es ; Save segment registers
push fs
push gs
_lds _si,[sregs] ; DS:_SI -> Load segment registers
mov es,[_si]
mov bx,[_si+6]
mov [sv_ds],_bx ; Save value of user DS on stack
mov fs,[_si+8]
mov gs,[_si+10]
_lds _si,[inptr] ; Load CPU registers
mov eax,[_si]
mov ebx,[_si+4]
mov ecx,[_si+8]
mov edx,[_si+12]
mov edi,[_si+20]
mov esi,[_si+16]
push ds ; Save value of DS
push _bp ; Some interrupts trash this!
clc ; Generate the interrupt
push [UINT intno]
mov ds,[WORD sv_ds] ; Set value of user's DS selector
call _PM_genInt
pop _bp ; Pop intno from stack (flags unchanged)
pop _bp ; Restore value of stack frame pointer
pop ds ; Restore value of DS
pushf ; Save flags for later
pop [UINT flags]
push esi ; Save ESI for later
pop [DWORD sv_esi]
push ds ; Save DS for later
pop [UINT sv_ds]
_lds _si,[outptr] ; Save CPU registers
mov [_si],eax
mov [_si+4],ebx
mov [_si+8],ecx
mov [_si+12],edx
push [DWORD sv_esi]
pop [DWORD _si+16]
mov [_si+20],edi
mov _bx,[flags] ; Return flags
and ebx,1h ; Isolate carry flag
mov [_si+24],ebx ; Save carry flag status
_lds _si,[sregs] ; Save segment registers
mov [_si],es
mov _bx,[sv_ds]
mov [_si+6],bx ; Get returned DS from stack
mov [_si+8],fs
mov [_si+10],gs
pop gs ; Restore segment registers
pop fs
pop es
pop ds
leave_c
ret
cprocend
ifndef flatmodel
_PM_savedDS dw _DATA ; Saved value of DS
endif
;----------------------------------------------------------------------------
; void PM_saveDS(void)
;----------------------------------------------------------------------------
; Save the value of DS into a section of the code segment, so that we can
; quickly load this value at a later date in the PM_loadDS() routine from
; inside interrupt handlers etc. The method to do this is different
; depending on the DOS extender being used.
;----------------------------------------------------------------------------
cprocstartdll16 PM_saveDS
ifdef flatmodel
mov [_PM_savedDS],ds ; Store away in data segment
endif
ret
cprocend
;----------------------------------------------------------------------------
; void PM_loadDS(void)
;----------------------------------------------------------------------------
; Routine to load the DS register with the default value for the current
; DOS extender. Only the DS register is loaded, not the ES register, so
; if you wish to call C code, you will need to also load the ES register
; in 32 bit protected mode.
;----------------------------------------------------------------------------
cprocstartdll16 PM_loadDS
mov ds,[cs:_PM_savedDS] ; We can access the proper DS through CS
ret
cprocend
ifdef flatmodel
;----------------------------------------------------------------------------
; ibool DPMI_allocateCallback(void (*pmcode)(), void *rmregs, long *RMCB)
;----------------------------------------------------------------------------
cprocstart _DPMI_allocateCallback
ARG pmcode:CPTR, rmregs:DPTR, RMCB:DPTR
enter_c
push ds
push es
push cs
pop ds
mov esi,[pmcode] ; DS:ESI -> protected mode code to call
mov edi,[rmregs] ; ES:EDI -> real mode register buffer
mov ax,303h ; AX := allocate realmode callback function
int 31h
mov eax,0 ; Return failure!
jc @@Fail
mov eax,[RMCB]
shl ecx,16
mov cx,dx
mov [es:eax],ecx ; Return real mode address
mov eax,1 ; Return success!
@@Fail: pop es
pop ds
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; void DPMI_freeCallback(long RMCB)
;----------------------------------------------------------------------------
cprocstart _DPMI_freeCallback
ARG RMCB:ULONG
enter_c
mov cx,[WORD RMCB+2]
mov dx,[WORD RMCB] ; CX:DX := real mode callback
mov ax,304h
int 31h
leave_c
ret
cprocend
endif
; Macro to delay briefly to ensure that enough time has elapsed between
; successive I/O accesses so that the device being accessed can respond
; to both accesses even on a very fast PC.
ifdef USE_NASM
%macro DELAY 0
jmp short $+2
jmp short $+2
jmp short $+2
%endmacro
%macro IODELAYN 1
%rep %1
DELAY
%endrep
%endmacro
else
macro DELAY
jmp short $+2
jmp short $+2
jmp short $+2
endm
macro IODELAYN N
rept N
DELAY
endm
endm
endif
;----------------------------------------------------------------------------
; uchar _PM_readCMOS(int index)
;----------------------------------------------------------------------------
; Read the value of a specific CMOS register. We do this with both
; normal interrupts and NMI disabled.
;----------------------------------------------------------------------------
cprocstart _PM_readCMOS
ARG index:UINT
push _bp
mov _bp,_sp
pushfd
mov al,[BYTE index]
or al,80h ; Add disable NMI flag
cli
out 70h,al
IODELAYN 5
in al,71h
mov ah,al
xor al,al
IODELAYN 5
out 70h,al ; Re-enable NMI
sti
mov al,ah ; Return value in AL
popfd
pop _bp
ret
cprocend
;----------------------------------------------------------------------------
; void _PM_writeCMOS(int index,uchar value)
;----------------------------------------------------------------------------
; Read the value of a specific CMOS register. We do this with both
; normal interrupts and NMI disabled.
;----------------------------------------------------------------------------
cprocstart _PM_writeCMOS
ARG index:UINT, value:UCHAR
push _bp
mov _bp,_sp
pushfd
mov al,[BYTE index]
or al,80h ; Add disable NMI flag
cli
out 70h,al
IODELAYN 5
mov al,[value]
out 71h,al
xor al,al
IODELAYN 5
out 70h,al ; Re-enable NMI
sti
popfd
pop _bp
ret
cprocend
ifdef flatmodel
;----------------------------------------------------------------------------
; int _PM_pagingEnabled(void)
;----------------------------------------------------------------------------
; Returns 1 if paging is enabled, 0 if not or -1 if not at ring 0
;----------------------------------------------------------------------------
cprocstart _PM_pagingEnabled
mov eax,-1
ifdef DOS4GW
mov cx,cs
and ecx,3
jz @@Ring0
cmp [UINT _PM_haveCauseWay],0
jnz @@Ring0
jmp @@Exit
@@Ring0:
mov eax,cr0 ; Load CR0
shr eax,31 ; Isolate paging enabled bit
endif
@@Exit: ret
cprocend
;----------------------------------------------------------------------------
; _PM_getPDB - Return the Page Table Directory Base address
;----------------------------------------------------------------------------
cprocstart _PM_getPDB
ifdef DOS4GW
mov ax,cs
and eax,3
jz @@Ring0
cmp [UINT _PM_haveCauseWay],0
jnz @@Ring0
endif
; Call VxD if running at ring 3 in a DOS box
cmp [WORD _PM_VXD_sel],0
jz @@Fail
mov eax,PMHELP_GETPDB
ifdef USE_NASM
call far dword [_PM_VXD_off]
else
call [FCPTR _PM_VXD_off]
endif
ret
@@Ring0:
ifdef DOS4GW
mov eax,cr3
and eax,0FFFFF000h
ret
endif
@@Fail: xor eax,eax
ret
cprocend
;----------------------------------------------------------------------------
; PM_flushTLB - Flush the Translation Lookaside buffer
;----------------------------------------------------------------------------
cprocstart PM_flushTLB
mov ax,cs
and eax,3
jz @@Ring0
ifdef DOS4GW
cmp [UINT _PM_haveCauseWay],0
jnz @@Ring0
endif
; Call VxD if running at ring 3 in a DOS box
cmp [WORD _PM_VXD_sel],0
jz @@Fail
mov eax,PMHELP_FLUSHTLB
ifdef USE_NASM
call far dword [_PM_VXD_off]
else
call [FCPTR _PM_VXD_off]
endif
ret
@@Ring0:
ifdef DOS4GW
wbinvd ; Flush the CPU cache
mov eax,cr3
mov cr3,eax ; Flush the TLB
endif
@@Fail: ret
cprocend
endif
;----------------------------------------------------------------------------
; void _PM_VxDCall(VXD_regs far *r,uint off,uint sel);
;----------------------------------------------------------------------------
cprocstart _PM_VxDCall
ARG r:DPTR, off:UINT, sel:UINT
enter_c
; Load all registers from the registers structure
mov ebx,[r]
mov eax,[ebx+0]
mov ecx,[ebx+8]
mov edx,[ebx+12]
mov esi,[ebx+16]
mov edi,[ebx+20]
mov ebx,[ebx+4] ; Trashes BX structure pointer!
; Call the VxD entry point (on stack)
ifdef USE_NASM
call far dword [off]
else
call [FCPTR off]
endif
; Save all registers back in the structure
push ebx ; Push EBX onto stack for later
mov ebx,[r]
mov [ebx+0],eax
mov [ebx+8],ecx
mov [ebx+12],edx
mov [ebx+16],esi
mov [ebx+20],edi
pop [DWORD ebx+4] ; Save value of EBX from stack
leave_c
ret
cprocend
endcodeseg _pmdos
END ; End of module