/* This file is DPMIUTIL.C
** Copyright (c) Rainer Schnitker 10/91
*/

/* This file need Turbo C to compile,
** but there are only TurboC specials like  asm { statement }
** Function used : allocmem(),MK_FP(),FP_SEG(),FP_OFF()
** Compile options : -ms (because of other segments in protected mode)
**                   -2  (protected mode)
**                   -B  (Assembler lines)
*/

#include "DPMI.H"
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>

/* CPU switching for DPMI 0.9 */

void real_to_protected()
{
unsigned int speicherseg;
WORD rax,seg,off,hostreq;
DWORD PM_jump ;
int status;
asm {   mov ax,1687h                            /* DMPI supported ? */
        int 2fh
        mov rax,ax
        mov off,di 
        mov seg,es 
        mov hostreq,si }
if (rax){
        printf("no dpmi supported\n");
        exit(1);
        }

PM_jump = (long)seg ;
PM_jump <<= 16 ;
PM_jump += (long)off ; 

status=allocmem(hostreq,&speicherseg);          /* for DPMI-Stack */
if (status!=-1) {                               /* reserve        */
                printf("allocmem() error\n");
                exit(1); 
                }
asm {   mov es,speicherseg ;
        mov ax,0 ;
        call DWORD PTR PM_jump ;    
        jnc PMOK ; }

printf("Can't switch to Protected Mode!");
exit(1);

PMOK:
printf("CPU in Protected Mode\n");
printf("CS=%X DS=%X ES=%X SS=%X\n",_CS,_DS,_ES,_SS);

_BL=0x23 ;          /* Control Break -> protected_to_real() */
_CX=_CS ;
asm mov dx,OFFSET MYINT ;
DPMI(0x0205);  

}

void protected_to_real()
{
printf("CPU in Real Mode\n");
asm MYINT LABEL byte ;
asm {   mov ax,4C00h
    int 21h   }     /* program ends here */
}

/* LDT Descriptor management services  DPMI 0.9 */

WORD AllocLDT(WORD anzahl)
{
_CX=anzahl;
DPMI(0x0000);
asm jnc OKallocldt ;
return 0;
OKallocldt:
return _AX;
}

int FreeLDT(WORD sel)
{
_BX=sel;
DPMI(0x0001);
asm jnc OKfreeldt;
return -1;
OKfreeldt:
return 0;
}

WORD SegtoSel(WORD seg)
{
_BX=seg;
DPMI(0x0002);
asm jnc OKsegsel;
return 0;
OKsegsel:
return _AX;
}

WORD SelInc()
{
DPMI(0x0003);
return _AX ;
}

int LockSel(WORD sel)
{
_BX=sel ;
DPMI(0x0004);
asm jnc OKlocksel;
return -1;
OKlocksel:
return 0;
}

int UnlockSel(WORD sel)
{
_BX=sel ;
DPMI(0x0005);
asm jnc OKunlocksel;
return -1;
OKunlocksel:
return 0;
}

DWORD GetBaseAddress(WORD sel)
{
_BX=sel;
DPMI(0x0006);
asm jnc OKgetbase;
return 0;
OKgetbase:
return ( (DWORD)_DX | ( (DWORD)_CX <<16 ) );
}

int SetBaseAddress(WORD sel,DWORD address)
{
_CX=(WORD) (address>>16);
_DX=(WORD) address ; 
_BX=sel;
DPMI(0x0007);
asm jnc OKsetbase;
return -1;
OKsetbase:
return 0;
}

int SetLimit(WORD sel,DWORD limit)
{
_BX=sel;
_CX=(WORD) (limit>>16) ;
_DX=(WORD) limit ;
DPMI(0x0008);
asm jnc OKsetlimit;
return -1;
OKsetlimit:
return 0;
}

int SetAccess(WORD sel,BYTE access,BYTE extaccess)
{
_BX=sel;
_CL=access;
_CH=extaccess;
DPMI(0x0009);
asm jnc OKsetaccess;
return -1;
OKsetaccess:
return 0;
}

WORD CreatAlias(WORD segsel)
{
_BX=segsel;
DPMI(0x000a);
asm jnc OKcreatalias;
return 0;
OKcreatalias:
return _AX ;
}

int GetDescriptor(WORD sel,DESCRIPTOR *d)
{
asm push es ;
_BX=sel;
_ES=FP_SEG(d);
_DI=FP_OFF(d);
DPMI(0x000b);
asm pop es ;
asm jnc OKgetdisc ;
return -1;
OKgetdisc:
return 0;
}

int SetDescriptor(WORD sel,DESCRIPTOR *d)
{
asm push es ;
_BX=sel;
_ES=FP_SEG(d);
_DI=FP_OFF(d);
DPMI(0x000c);
asm pop es ;
asm jnc OKsetdisc ;
return -1;
OKsetdisc:
return 0;
}

int AllocSpecialLDT(WORD sel)
{
_BX=sel;
DPMI(0x000d);
asm jnc OKspecial;
return -1 ;
OKspecial:
return 0 ;
}

/* Interrupt Services  DPMI 0.9 */

int GetExceptionVector(BYTE b,WORD *sel,WORD *off)
{
_BL=b;
DPMI(0x0202);
asm jnc OKgetexeption;
return -1;
OKgetexeption:
*sel=_CX;
*off=_DX;
return 0;
}

int SetExceptionVector(BYTE b,WORD sel,WORD off)
{
_BL=b;
_CX= sel;
_DX= off;
DPMI(0x0203); 
asm jnc OKsetexeption;
return -1;
OKsetexeption:
return 0;
}

int GetPMinterruptVector(BYTE b,WORD *sel,WORD *off)
{
_BL=b;
DPMI(0x0204); 
asm jnc OKgetpmiv;
return -1;
OKgetpmiv:
*sel=_CX;
*off=_DX;
return 0;
}

int SetPMinterruptVector(BYTE b,WORD sel,WORD off)
{
_BL=b;
_CX= sel;
_DX= off;
DPMI(0x0205); 
asm jnc OKsetpmiv;
return -1;
OKsetpmiv:
return 0;
}

/* Memory managment services DPMI 0.9 */

void getfreeinfo(FREEMEMINFO *fm)
{
asm push es ;
_ES=FP_SEG(fm) ; _DI=FP_OFF(fm) ;
DPMI(0x500);
asm pop es ;
}

void printfreeinfo(FREEMEMINFO *fm)
{
printf("Largest available block           : %lu Byte \n",fm->i1);
printf("Number free pages                 : %3lu = %4lu KB\n",fm->i2,fm->i2*4);
printf("Number free pages to lock         : %3lu = %4lu KB\n",fm->i3,fm->i3*4);
printf("Number pages of linear addr space : %3lu = %4lu KB\n",fm->i4,fm->i4*4);
printf("Number pages not locked           : %3lu = %4lu KB\n",fm->i5,fm->i5*4);
printf("Number pages not used             : %3lu = %4lu KB\n",fm->i6,fm->i6*4);
printf("Number pages managed by the Dpmi  : %3lu = %4lu KB\n",fm->i7,fm->i7*4);
printf("Number pages free addr space      : %3lu = %4lu KB\n",fm->i8,fm->i8*4);
printf("Number pages in swapfile          : %3lu = %4lu KB\n",fm->i9,fm->i9*4);
}

DWORD GlobalAlloc(DWORD bytes,DWORD *memhandle)
{  
_BX=(WORD) (bytes>>16);
_CX=(WORD) bytes ;
DPMI(0x501);
asm jnc OKalloc;
return 0 ;
OKalloc:
*memhandle = (DWORD)_CX | ( (DWORD)_BX <<16 ) ;
return ( (DWORD)_DI | ( (DWORD)_SI <<16 ) );
}

int GlobalFree(DWORD handle)
{
_SI=(WORD) (handle>>16);
_DI=(WORD) handle ;
DPMI(0x0502);
asm jnc OKFREE ;
return -1 ;
OKFREE:
return 0 ;
}

/* Page locking services  DPMI 0.9 */

int LockLinRegion(DWORD size,DWORD address)
{
_BX=(WORD) (address>>16);
_CX=(WORD) address;
_SI=(WORD) (size>>16);
_DI=(WORD) size;
DPMI(0x0600);
asm jnc oklocklin;
return -1;
oklocklin:
return 0;
}

int UnlockLinRegion(DWORD size,DWORD address)
{
_BX=(WORD) (address>>16);
_CX=(WORD) address;
_SI=(WORD) (size>>16);
_DI=(WORD) size;
DPMI(0x0600);
asm jnc okunlocklin;
return -1;
okunlocklin:
return 0;
}

/* Other multiplex int 2F */

void Yield()    /* give control to operating system (like GetMassage) */
{
    _AX=0x1680 ;
    asm int 2Fh ;
}

/* Protected mode API */

DWORD lsl(WORD sel)  /* load selector limit */
{
asm {   db 66h;
        sub ax,ax;      /* sub eax,eax */
        db 66h;
        mov bx,ax;      /* mov ebx,eax */
        mov bx,sel
        db 66h;
        lsl ax,bx;      /* lsl eax,ebx */
        db 66h;
        mov dx,ax ;     /* mov eax,eax  ax:low word */
        db 66h ;
        shr dx,16       /* shr edx,16   dx:high word */
    }
}

WORD lsl16(WORD sel)  /* 16-bit version of lsl for 80286 */
{
    asm sub ax,ax
    asm lsl ax,sel
    return _AX ;
}

WORD lar(WORD sel)  /* load access rights */
{
    asm sub ax,ax ;
    asm lar ax,sel;
    asm shr ax,8 ;
    return _AX ;
}

WORD verr(WORD sel)  /* verify read */
{
asm mov ax,1 ;
asm verr sel ;
asm je okr ;
asm dec ax ;
okr:
return _AX ;
}

WORD verw(WORD sel)  /* verify write */
{
asm mov ax,1 ;
asm verw sel ;
asm je okw ;
asm dec ax ;
okw:
return _AX ;
}

void sgdt(GDTR *g)  /* save gdt table */
{
asm mov si,g ;
asm sgdt [si] ;
}

void sidt(GDTR *g)   /* save idt table */
{
asm mov si,g ;
asm sidt [si] ;
}

WORD sldt(void)          /* save ldt-selector */
{
asm sldt ax ;
return _AX ;
}

WORD str(void)           /* save task-register */
{
asm str ax ;
return _AX ;
}

/* Protected mode utilities */

void far * incfp(void far *a)  /* get next selector */
{
    FP_SEG(a)+=SelInc();
    return(a);
}

void far * decfp(void far *a)  /* get previous selector */
{
FP_SEG(a)-=SelInc();
return(a);
}

void printdescriptor(DESCRIPTOR d)
{
printf("b=%02X%02X%04X lim=%01X%04X p=%u dpl=%u %s t4=%X t=%u %s %s\n",
        d.base_hi,d.base_mi,d.base_lo,
        d.lim_hi&15,d.lim_lo,
        (d.access&128)>>7,
        (d.access&96)>>5,
        (d.access&16) ? "MEMseg" : "SYSseg",
        (d.access&15),
        (d.access&7),
        (d.lim_hi &  64) ? "32bit" : "16bit" ,
        (d.lim_hi & 128) ? "paging": " " );
}

void farcopy(void far *dest,void far *source,DWORD bytes)
{
    unsigned long i ;

    bytes++;                    /* if bytes %2 = 1 */
    bytes>>=1;
    poke( FP_SEG(dest),FP_OFF(dest),peek(FP_SEG(source),FP_OFF(source)) );
    for (i=1; i< bytes ; i++) {
        FP_OFF(dest)+=2 ;
        if (FP_OFF(dest)==0) dest=incfp(dest);
        FP_OFF(source)+=2 ;
        if (FP_OFF(source)==0) source=incfp(source);
        poke( FP_SEG(dest),FP_OFF(dest),peek(FP_SEG(source),FP_OFF(source)) );
        }
}

/* high level C-function using DPMI-API */
/* malloc,free */

#define PAGESIZE 4096
#define PAGEBUCKET 11
#define NBUCKETS 30
#define MAGIC 0xFFF0

union overhead {
                struct {
                        WORD page;
                        WORD offset;
                } free ;
                struct {
                        WORD magic ;
                        WORD index ;
                } use ;
        } ;

static union overhead nextf[NBUCKETS];

void far * morepages(WORD bucket)
{
WORD nblks,i,page,nsel,nextpage;
DWORD sz,handle,memaddress;

sz = ((DWORD)1) << (bucket + 2) ;

if (sz<PAGESIZE)    /* 1st case : divide 4K-Page in sz-big blocks */
        {
        if ((handle=GlobalAlloc(PAGESIZE,&memaddress))==0) 
                return NULL ;
        if ( (page=AllocLDT(1)) ==0) {    /* no more selectors ? */
            GlobalFree(handle);           /* free memory */
            return NULL;
            }
        if (SetBaseAddress(page,memaddress)==-1) return NULL;
        if (SetLimit(page,PAGESIZE-1)==-1) return NULL;

        nblks = PAGESIZE / (WORD)sz ;     /* number of pieces */
        nblks--;
        nextf[bucket].free.page=page;     /* freelist -> second block */
        nextf[bucket].free.offset=sz;
        for (i=1;i< (nblks);i++)          /* fill header of blocks */
                {
                poke(page,i*sz,page);
                poke(page,i*sz +2,(i+1)*sz);    
                }
        poke(page,nblks*sz,0);            /* last block -> NULL */
        poke(page,nblks*sz+2,0);
        }

else    {           /* 2.nd case : one selectors per 64 KB */
        if ((handle=GlobalAlloc(sz,&memaddress))==0) return NULL ;
        nsel=(WORD) ((sz-1)>>16);         /* nsel = number of selectors */
        nsel++;
        if ((page=AllocLDT(nsel))==0) {   /* no more selectors? */
                GlobalFree(handle) ;      /* free memory */
                return NULL;
                }
        if (sz>0x10000L) sz=0x10000L ;    /* divide in 64KB */
        nextpage=page;
        for (i=1;i<=nsel;i++) {
                if (SetBaseAddress(nextpage,memaddress)==-1) 
                        printf("error set Base\n");
                if (SetLimit(nextpage,sz-1)==-1) 
                        printf("error set Limit\n");
                memaddress+=0x10000L ;
                nextpage+=SelInc();       /* get next selector number */
                }
        }
return (MK_FP(page,0)) ;
}

void far *extmalloc(DWORD nbytes)
{
        WORD bucket, n=0 ;
        DWORD amt ;
        union overhead far *fp;

        if (nbytes == 0 ) return NULL;
        nbytes+= sizeof(union overhead) ;

        if (nbytes <= PAGESIZE ) {              /* nbytes <= PAGE */
                amt = 4;                        /* bucket = 1..12 */
                bucket = 0;
                n = 0 ; 
                } 
        else    {                               /* nbytes > PAGE   */
                amt = PAGESIZE;                 /* bucket = 13..30 */
                bucket = PAGEBUCKET;
                n=PAGESIZE;
                }
        while (nbytes > amt + n) {
                amt <<= 1;
                if (amt == 0)
                        return NULL;
                bucket++;
                }

        if (nextf[bucket].free.page == 0) {          /* no mem in list */
                fp=(union overhead far *)morepages(bucket);
                if ((fp) == NULL)                    /* no mem from dpmi */
                        return NULL;
                }
        else    {                               /* mem available in list */
                /* now : nextf[] -> myblock -> nextp */

                /* fp -> myblock */
                fp=(union overhead far *)MK_FP(nextf[bucket].free.page,nextf[bucket].free.offset);
                /* nextf[] -> nextp */
                nextf[bucket].free.page  =fp->free.page;
                nextf[bucket].free.offset=fp->free.offset;
                }
        fp->use.magic= MAGIC ;
        fp->use.index= bucket;
        return ((void far *)(fp+1));
}

void extfree(void far *p)
{
    union overhead far *fp ;
    WORD bucket ;

        fp=(union overhead far *)p - 1;            /* get header */
        if (fp->use.magic!= MAGIC) return ;        /* bad pointer */
        bucket = fp->use.index ;                   /* get size of pointer */
                                 /* nextf[] -> nextp */
                                 /* wanted : nextf[] -> fp -> nextf */
        fp->free.page=nextf[bucket].free.page;
        fp->free.offset=nextf[bucket].free.offset;
        nextf[bucket].free.page = FP_SEG(fp);
        nextf[bucket].free.offset=FP_OFF(fp);
}
