;**************************************************************************** ;* ;* 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