;**************************************************************************** ;* ;* PM/Lite Library ;* ;* Copyright (C) 1996 SciTech Software ;* All rights reserved. ;* ;* Filename: $Workfile: _pmlite.asm $ ;* Version: $Revision: 1.0 $ ;* ;* Language: 80386 Assembler, TASM 4.0 or later ;* Environment: IBM PC Real mode and 16/32 bit protected mode ;* ;* Description: Low level assembly support. ;* ;* $Date: 05 Feb 1996 21:42:18 $ $Author: KendallB $ ;* ;**************************************************************************** IDEAL INCLUDE "model.mac" ; Memory model macros header _pmlite ; Set up memory model begdataseg _pmlite if flatmodel _PM_savedDS dw 0 ; Saved value of DS PUBLIC _PM_savedDS endif ifdef X32VM DOSX EQU 1 endif ifdef DOSX ; Special external declarations for the DOSX extender that we may need $EXTRN __x386_data_16_alias,WORD $EXTRN __x386_zero_base_selector,WORD $EXTRN __x386_zero_base_ptr,DWORD endif enddataseg _pmlite ifdef DOSX SEGMENT __X386_DATASEG_16 PARA PUBLIC USE16 'DATA16' $EXTRN __x386_fm,DWORD ENDS __X386_DATASEG_16 endif begcodeseg _pmlite ; Start of code segment ifndef DPMI16 ifndef __WINDOWS16__ ife flatmodel struc rmregs_s ax dw ? bx dw ? cx dw ? dx dw ? si dw ? di dw ? cflag 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) ;---------------------------------------------------------------------------- ; 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. ;---------------------------------------------------------------------------- procstart _PM_callRealMode ARG s:WORD, o:WORD, regs:DWORD, sregs:DWORD LOCAL addr:DWORD, bxVal:WORD, esVal:WORD, flags:WORD = LocalSize enter_c LocalSize 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 procend _PM_callRealMode endif endif endif ;---------------------------------------------------------------------------- ; void PM_segread(PMSREGS *sregs) ;---------------------------------------------------------------------------- ; Read the current value of all segment registers ;---------------------------------------------------------------------------- procstartdll16 _PM_segread ARG sregs:DPTR enter_c 0 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_nolocal ret procenddll16 _PM_segread ; Create a table of the 256 different interrupt calls that we can jump ; into intno = 0 intTable: REPT 256 db 0CDh db intno intno = intno + 1 ret nop ENDM PROC genInt near push _ax ; Save _ax push _bx ; Save _bx if 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 if 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 ENDP genInt ;---------------------------------------------------------------------------- ; 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. ;---------------------------------------------------------------------------- procstartdll16 _PM_int386x ARG intno:UINT, inptr:DPTR, outptr:DPTR, sregs:DPTR LOCAL flags:UINT, sv_ds:UINT, sv_esi:ULONG = LocalSize enter_c LocalSize 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 [intno] mov ds,[WORD sv_ds] ; Set value of user's DS selector call 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 [flags] push esi ; Save ESI for later pop [sv_esi] push ds ; Save DS for later pop [sv_ds] _lds _si,[outptr] ; Save CPU registers mov [_si],eax mov [_si+4],ebx mov [_si+8],ecx mov [_si+12],edx push [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 procenddll16 _PM_int386x if flatmodel ;---------------------------------------------------------------------------- ; unsigned char PM_getByte(unsigned s, unsigned o) ;---------------------------------------------------------------------------- procstartdll16 _PM_getByte ARG s:UINT, o:UINT push ebp mov ebp,esp push ds mov ax,[WORD s] mov ds,ax mov eax,[o] mov al,[eax] pop ds pop ebp ret procenddll16 _PM_getByte ;---------------------------------------------------------------------------- ; unsigned short PM_getWord(unsigned s, unsigned o) ;---------------------------------------------------------------------------- procstartdll16 _PM_getWord ARG s:UINT, o:UINT push ebp mov ebp,esp push ds mov ax,[WORD s] mov ds,ax mov eax,[o] mov ax,[eax] pop ds pop ebp ret procenddll16 _PM_getWord ;---------------------------------------------------------------------------- ; unsigned long PM_getLong(unsigned s, unsigned o) ;---------------------------------------------------------------------------- procstartdll16 _PM_getLong ARG s:UINT, o:UINT push ebp mov ebp,esp push ds mov ax,[WORD s] mov ds,ax mov eax,[o] mov eax,[eax] pop ds pop ebp ret procenddll16 _PM_getLong ;---------------------------------------------------------------------------- ; void PM_setByte(unsigned s, unsigned o,unsigned char v) ;---------------------------------------------------------------------------- procstartdll16 _PM_setByte ARG s:UINT, o:UINT, v:UCHAR push ebp mov ebp,esp push ds mov ax,[WORD s] mov ds,ax mov edx,[o] mov al,[v] mov [edx],al pop ds pop ebp ret procenddll16 _PM_setByte ;---------------------------------------------------------------------------- ; void PM_setWord(unsigned s, unsigned o,unsigned short v) ;---------------------------------------------------------------------------- procstartdll16 _PM_setWord ARG s:UINT, o:UINT, v:USHORT push ebp mov ebp,esp push ds mov ax,[WORD s] mov ds,ax mov edx,[o] mov ax,[v] mov [edx],ax pop ds pop ebp ret procenddll16 _PM_setWord ;---------------------------------------------------------------------------- ; void PM_setLong(unsigned s, unsigned o,unsigned long v) ;---------------------------------------------------------------------------- procstartdll16 _PM_setLong ARG s:UINT, o:UINT, v:ULONG push ebp mov ebp,esp push ds mov ax,[WORD s] mov ds,ax mov edx,[o] mov eax,[v] mov [edx],eax pop ds pop ebp ret procenddll16 _PM_setLong ;---------------------------------------------------------------------------- ; void PM_memcpynf(void *dst,unsigned src_s,unsigned src_o,unsigned n) ;---------------------------------------------------------------------------- ; Copies a block of memory from a far memory block to a near memory block. ;---------------------------------------------------------------------------- procstartdll16 _PM_memcpynf ARG dst:DPTR, src_s:UINT, src_o:UINT, n:UINT enter_c 0 push ds force_es_eq_ds ; Force ES == DS mov edi,[dst] ; ES:EDI -> destination memory block mov ax,[WORD src_s] mov ds,ax mov esi,[src_o] ; DS:ESI -> source memory block mov ecx,[n] mov eax,ecx shr ecx,2 rep movsd mov ecx,eax and ecx,3 rep movsb pop ds leave_c_nolocal ret procenddll16 _PM_memcpynf ;---------------------------------------------------------------------------- ; void PM_memcpyfn(unsigned dst_s,unsigned dst_o,void *src,unsigned n) ;---------------------------------------------------------------------------- ; Copies a block of memory from a near memory block to a far memory block. ;---------------------------------------------------------------------------- procstartdll16 _PM_memcpyfn ARG dst_s:UINT, dst_o:UINT, src:DPTR, n:UINT enter_c 0 push es mov esi,[src] ; DS:ESI -> source memory block mov ax,[WORD dst_s] mov es,ax mov edi,[dst_o] ; ES:EDI -> source memory block mov ecx,[n] mov eax,ecx shr ecx,2 rep movsd mov ecx,eax and ecx,3 rep movsb pop es leave_c_nolocal ret procenddll16 _PM_memcpyfn ifdef TNT ;---------------------------------------------------------------------------- ; unsigned _PL_allocsel(void) ;---------------------------------------------------------------------------- ; Allocate am empty segment selector with Phar Lap (there is no C based ; API for doing this in a compiler portable manner). ;---------------------------------------------------------------------------- procstart __PL_allocsel mov ah,48h ; Allocate memory service xor ebx,ebx ; EBX := 0 pages to allocate int 21h jc @@Fail and eax,0FFFFh ; EAX := segment selector ret @@Fail: xor eax,eax ret procend __PL_allocsel endif ifdef DOSX ;---------------------------------------------------------------------------- ; ulong _X32_getPhysMem(void) ;---------------------------------------------------------------------------- ; Determines the amount of physical memory available under the X32 DOS ; extender. There is no proper way to do this, so what we have to do is ; look in the X32 extender stub and sniff around for the info we need. This ; is what was suggested by Joe Huffman and provided to us by Kevin Aguilar. ;---------------------------------------------------------------------------- procstart __X32_getPhysMem push es mov es,[__x386_data_16_alias] mov eax,[es:__x386_fm+12] sub eax,[es:__x386_fm+4] sub eax,100 * 1024 pop es ret procend __X32_getPhysMem endif endif ife flatmodel _PM_savedDS dw DGROUP ; 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. ;---------------------------------------------------------------------------- procstartdll16 _PM_saveDS if flatmodel mov [_PM_savedDS],ds ; Store away in data segment endif ret procenddll16 _PM_saveDS ;---------------------------------------------------------------------------- ; 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. ;---------------------------------------------------------------------------- procstartdll16 _PM_loadDS mov ds,[cs:_PM_savedDS] ; We can access the proper DS through CS ret procenddll16 _PM_loadDS endcodeseg _pmlite END ; End of module