/*
** DPMI services for GNU_C
**
** (c) Rainer Schnitker 1994
*/

#include <stdio.h>

#define INLINE static inline

typedef unsigned char   BYTE;
typedef unsigned short	WORD;
typedef unsigned long   DWORD;

typedef struct {
    WORD lim_lo;
    WORD base_lo;
    BYTE base_mi,access,lim_hi,base_hi;
} DESCRIPTOR ;
typedef DESCRIPTOR *PDESCRIPTOR;

typedef struct { DWORD LargestFree;
                 DWORD MaxUnlockedPages;
                 DWORD MaxLockedPages;
                 DWORD AddressSpacePages;
                 DWORD UnlockedPages;
                 DWORD UnusedPages;
                 DWORD PhysicalPages;
                 DWORD FreeAddressSpacePages;
                 DWORD PagingFilePages;  /* or partition size */
                 DWORD reserved[3];
               } FREEMEMINFO ;
typedef FREEMEMINFO *PFREEMEMINFO;

/* descriptor makros */
#define DESC_BASE(d)  (((((DWORD)d.base_hi<<8)|(DWORD)d.base_mi)<<16)|(DWORD)d.base_lo)
#define DESC_LIMIT(d) ((((DWORD)(d.lim_hi&15))<<16)|(DWORD)d.lim_lo)
#define GATE_OFFSET(d) (((((DWORD)d.base_hi<<8)|(DWORD)d.lim_hi)<<16)|(DWORD)d.lim_lo)

/* descriptor fields */
#define ACCESS_BIT              1
#define READ_BIT                2
#define WRITE_BIT               2
#define CONFIRMING_BIT          4
#define EXPAND_BIT              4
#define CODE_BIT                8
#define SEGMENT_BIT             16
#define PRESENT_BIT             128
#define AVL_BIT                 16
#define DEFAULT_BIT             64
#define BIG_BIT                 64
#define GRANULAR_BIT            128
#define DPL_MASK                96
#define TYPE_MASK               15
#define LIMIT_HI_MASK           15

#define APP_CODE_SEL            250   /* dpl=3 , read */
#define APP_DATA_SEL            242   /* dpl=3 ,write */

#define TYPE_CODE               10
#define TYPE_CONFIRM_CODE       14
#define TYPE_DATA               2
#define TYPE_EXPAND_DATA        6
#define TYPE_286TSS             1
#define TYPE_LDT                2
#define TYPE_BUSY286TSS         3
#define TYPE_286CALLGATE        4
#define TYPE_TASKGATE           5
#define TYPE_286INTGATE         6
#define TYPE_286TRAPGATE        7
#define TYPE_386TSS             9
#define TYPE_BUSY386TSS         11
#define TYPE_386CALLGATE        12
#define TYPE_386INTGATE         14
#define TYPE_386TRAPGATE        15

/* breakpoints-types */
#define BREAK_CODE              0x0001
#define BREAK_DATA_W1           0x0101
#define BREAK_DATA_RW1          0x0201
#define BREAK_DATA_W2           0x0102
#define BREAK_DATA_RW2          0x0202
#define BREAK_DATA_W4           0x0104
#define BREAK_DATA_RW4          0x0204


#define CHECKERR "jnc    1f\n\tmovl   $-1, %0 \n\tjmp    2f \n\t1: \n\t"
#define CHECK_ERR_DPMI10 "jc    2f\n\t"
#define OKEAX0 "xorl   %0, %0 \n\t2: \n\t"

INLINE int GetDescriptor(int sel, DESCRIPTOR *desc)
{
    register int _v;

    __asm__ __volatile__(
	"int    $0x31 \n\t"
        CHECKERR
	OKEAX0
	: "=r" (_v)
	: "b" (sel) , "D" ((long)desc) , "a" (0x0B)
	);

    return _v ;
}

INLINE int GetFreeMemInfo(FREEMEMINFO *info)
{
    register int _v;

    __asm__ __volatile__(
	"int    $0x31 \n\t"
	CHECKERR
	OKEAX0
	: "=r" (_v)
	: "D" ((long)info), "a" (0x500)
	);

    return _v ;
}

int is_emx_exe(void)
{
    register int ver;

#ifdef __EMX__
    /* os_version() uses ebx */
    __asm__ ("pushl  %ebx \n\t");
    ver = __os_version();
    __asm__ ("popl   %ebx \n\t");
#else
    __asm__ __volatile__(
	"movb   $0x30, %%ah \n\t"
	"int    $0x21 \n\t"
        : "=a" (ver)
        :
        : "ax","bx");
#endif

    /* EMX set high eax to "em" */
    if (ver >> 16 == 0x6d65)
	return 1;
    else
	return 0;
}

void PrintFreeMemInfo()
{
    FREEMEMINFO fmi;
    FREEMEMINFO *fm = &fmi;

    if (is_emx_exe()) {
	puts("emx is running; no DPMI services");
	return;
    }

    if (GetFreeMemInfo(fm))
	return;

    printf("Largest available block           : %lu Bytes = %lu KB\n",
	   fm->LargestFree, fm->LargestFree >> 10);
    printf("Number free pages                 : %4lu = %4lu KB\n"
	   ,fm->MaxUnlockedPages, fm->MaxUnlockedPages * 4);
    printf("Number free pages to lock         : %4lu = %4lu KB\n"
	   ,fm->MaxLockedPages, fm->MaxLockedPages * 4);
    printf("Number pages of linear addr space : %4lu = %4lu KB\n"
	   ,fm->AddressSpacePages, fm->AddressSpacePages * 4);
    printf("Number pages not locked           : %4lu = %4lu KB\n"
	   ,fm->UnlockedPages, fm->UnlockedPages * 4);
    printf("Number pages not used             : %4lu = %4lu KB\n"
	   ,fm->UnusedPages, fm->UnusedPages * 4);
    printf("Number pages managed by the Dpmi  : %4lu = %4lu KB\n"
	   ,fm->PhysicalPages, fm->PhysicalPages * 4);
    printf("Number pages free addr space      : %4lu = %4lu KB\n"
	   ,fm->FreeAddressSpacePages, fm->FreeAddressSpacePages * 4);

    if (fm->PagingFilePages <= 0xFFFFFL)
	printf("Number pages in swapfile          : %4lu = %4lu KB\n"
	       ,fm->PagingFilePages, fm->PagingFilePages * 4);
    else
	printf("DPMI host paging partition        : %lu Bytes = %lu MB\n"
	       ,fm->PagingFilePages, fm->PagingFilePages >> 20);
}

static void PrintDescriptor(DESCRIPTOR *d)
{
    BYTE flag;

    if (!(d->access & PRESENT_BIT)) {
	printf("Not Present\n");
	return;
    }
    if (d->access & SEGMENT_BIT)/* code or data selector */
	printf("Base=%02X%02X%04X Limit=%01X%04X %s DPL=%u %s %s %s %s\n",
	       d->base_hi, d->base_mi, d->base_lo,
	       d->lim_hi & LIMIT_HI_MASK, d->lim_lo,
	       (d->lim_hi & GRANULAR_BIT) ? "Pages" : "Bytes",
	       (d->access & DPL_MASK) >> 5,
	       (d->lim_hi & DEFAULT_BIT) ? "32bit" : "16bit",
	       (d->access & CODE_BIT) ?
	       (d->access & CONFIRMING_BIT) ? "CONF" : "CODE" :
	       (d->access & EXPAND_BIT) ? "EXPD" : "DATA",
	       (d->access & WRITE_BIT) ?
	       (d->access & CODE_BIT) ? "R" : "W" : "-",
	       (d->access & ACCESS_BIT) ? "ACC" : " ");

    else {			/* system selector */
	flag = d->access & (BYTE) 7;
	switch (flag) {
	case 0:
	    break;
	case 1:
	    printf("TSS%s ", (d->access & 8) ? "386" : "286");
	    break;
	case 2:
	    printf("LDT ");
	    break;
	case 3:
	    printf("BUSYTSS%s ", (d->access & 8) ? "386" : "286");
	    break;
	case 4:
	    printf("CALLGATE%s ", (d->access & 8) ? "386" : "286");
	    break;
	case 5:
	    printf("TASKGATE ");
	    break;
	case 6:
	    printf("INT_GATE%s ", (d->access & 8) ? "386" : "286");
	    break;
	case 7:
	    printf("TRAPGATE%s ", (d->access & 8) ? "386" : "286");
	    break;
	}


	if (flag <= 3) {
	    printf("Base=%02X%02X%04X Limit=%01X%04X DPL=%u\n",
		   d->base_hi, d->base_mi, d->base_lo,
		   d->lim_hi & LIMIT_HI_MASK, d->lim_lo,
		   (d->access & DPL_MASK) >> 5);
	} else {
	    printf("Sel=%04X Offset=%02X%02X%04X DPL=%u\n",
		   d->base_lo,
		   d->base_hi, d->lim_hi, d->lim_lo,
		   (d->access & DPL_MASK) >> 5);
	}
    }				/* else system descriptor */
}

void show_descriptor(int sel, int number)
{
    DESCRIPTOR desc;
    int i;

    if (is_emx_exe()) {
	puts("emx is running; no DPMI services");
	return;
    }

    for (i = 1; i <= number; i++) {
	printf("sel %X: ", sel);
	if (GetDescriptor(sel, &desc)) {
	    printf("- invalid -\n");
	    break;
	}
	PrintDescriptor(&desc);
	sel += 8;
    }
}
