Subject: EMS Routines for TurboC
From: [email-masked] (Roy Frederick)
Date: 23 Apr 89 17:56:03 GMT
Newsgroups: comp.sys.ibm.pc

These EMS access routines are in the public domain - no secrets,
copyrights, or restrictions on use.  There really isn't much to them -
but I use them in production data collection programs that run every
day (at least I think these are the versions I am currently using!).

Three routines are included: Emstest.c, emstbl.c, emssubr.asm.  Also a
header file etbl.h.  Emssubr.asm provides the lowest level access to
EMS.  It is called by emstbl.c to provide access to tables of fixed
length items with a total length limited by the amount of EMS present.
The length of a single table item is limited to <= 16384 (EMS page
length).  Space beyond the last item in an EMS page is wasted.
Emstest.c demonstrates the use of emstbl.c.  The routines are set up
for large model.

C routines have 4 col tabs; ASM , 8 

Etbl.h -------------------------------------------------------------
/*
 * Etbl.h -- Structure describing table in EMS
 */

struct etbl {
	int e_elen;							/* Table entry length */
	int e_ecnt;							/* Table entry count */
	int e_efrm;							/* Frame to be used */
	int e_ecur;							/* Last assigned entry number */
	int e_ecpf;							/* Entry count per page */
	int e_pgs;							/* Pages allocated */
	int e_hand;							/* EMS Handle */
} ;

void far * far etmap(struct etbl far *, int);
int far etinit(struct etbl far *);
void far etclose(struct etbl far *);

Emstest.c --------------------------------------------------------------------
/*
 * Emstest.c -- EMS Table Routines Test Pgm
 */

#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#include "etbl.h"

struct etbl et1 = { 100, 1000, 1, 0, 0, 0 } ;

main()
{
	int i;
	int far *q;

	etinit(&et1);

	for (i = 1; i <= 1000; i++) {
		q = etmap(&et1, i);
		if (q == 0L)
			err("Etmap failed for %d", i);
		*q = i;
	}
	for (i = 1; i <= 1000; i++) {
		q = etmap(&et1, i);
		if (q == 0L)
			err("Etmap failed for %d", i);
		if (*q != i)
			err("Data error - exp %d got %d", i, *q);
	}
	etclose(&et1);
	err("Test successful");
}

err(s)
const char *s;
{
	va_list argptr;
	va_start(argptr, s);
	fprintf(stderr, "Terminated: ");
	vfprintf(stderr, s, argptr);
	fprintf(stderr, "\n");
	exit(2);
}

Emstbl.c ----------------------------------------------------------------
/*
 * Emstbl -- EMS Table Routines
 */

#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#include "etbl.h"

static int eseg;						/* EMS Segment Address */

extern int ems_err;

int far etinit(e)
struct etbl far *e;
{
	int i;

	if (ems_verify() == 0)
		err("No EMS driver found");

	eseg = ems_getframe();
	e->e_ecpf = 16384 / e->e_elen;
	e->e_pgs = (e->e_ecnt + e->e_ecpf - 1) / e->e_ecpf;
	e->e_ecnt = e->e_pgs * e->e_ecpf;

	if ((e->e_hand = ems_alloc(e->e_pgs)) == 0)
		err("Not enough EMS available");
	for (i = 0; i < e->e_pgs; i++) {
		if (ems_map(e->e_efrm, e->e_hand, i) == 0)
			err("EMS_MAP failed err %02x  handle %04X  page %02X",
				ems_err, e->e_hand, i);
		memset(MK_FP(eseg, (e->e_efrm << 14)), 0, 16384);
	}

	e->e_ecur = 0;
	return(0);
}

void far * far etmap(e, ndx)
struct etbl far *e;
int ndx;
{
	int i, j, pg;
	j = ndx - 1;
	i = j / e->e_ecpf;
	j = j % e->e_ecpf;
	pg = e->e_efrm;
	if (ems_map(pg, e->e_hand, i) == 0)
		err("EMS_MAP failed err %02x  handle %04X  page %02X",
			ems_err, e->e_hand, i);
	return(MK_FP((eseg + (pg << 10)), (j * e->e_elen)));
}

void far etclose(e)
struct etbl far *e;
{
	ems_free(e->e_hand);
	e->e_elen = e->e_ecur = e->e_ecnt = e->e_hand = 0;
}

Emssubr.asm -----------------------------------------------------------
----assemble with TASM /W2 /MX emssubr -------------------------------
                PAGE ,132
                TITLE Expanded Memory Access Routines

		.MODEL	LARGE,C

                PUBLIC  ems_err
                PUBLIC  ems_verify
                PUBLIC  ems_alloc
                PUBLIC  ems_map
                PUBLIC  ems_free
                PUBLIC  ems_getframe
                PUBLIC  ems_pagecnt

		LOCALS	LL

                .DATA

emname		db	'EMMXXXX0'	; name of ems driver

ems_err		dw	0

                .CODE

                SUBTTL ems_verify
                PAGE +

ems_verify	PROC	uses DS SI DI
		mov	ax, DGROUP
		mov	ds, ax

		mov	ax, 0  		; check if int vect 67 set up
		mov	es, ax
		mov	ax, es:[4*67h+2]
		or	ax, ax		; segment non zero ?
		jz	LL01		; if not
		mov	es, ax

		mov	di, 10		; offset to driver name
		mov	si, offset emname
		mov	cx, 8
		cld
		rep	cmpsb		; check driver name
		jne	LL01

		mov	ax, 1
		jmp	short LL02

LL01:		mov	ax, 0ffh	; no driver code
		mov	[ems_err], ax
		xor	ax, ax		; zero ret code = bad
		
LL02:		ret

ems_verify	ENDP

                SUBTTL ems_alloc
                PAGE +

ems_alloc	PROC	uses DS
		arg	pgcnt:word
		mov	ax, DGROUP
		mov	ds, ax

                mov	bx, [pgcnt]	; get number of pages to allocate
                mov	ah, 43H		; allocate space
                int	67H
                mov	byte ptr [ems_err], ah
                or	ah, ah		; ah is zero if call succeeded
                jnz	LL01
		mov	ax, dx		; return handle
                jmp	short LL02
LL01:
                xor	ax, ax
LL02:
                ret
ems_alloc	ENDP

                SUBTTL ems_map
                PAGE +

ems_map		PROC	uses DS
		arg	phys:word,handle:word,lpage:word
		mov	ax, DGROUP
		mov	ds, ax

		mov	ax, [phys]	; get phys page slot
		mov	dx, [handle]	; get handle number
		mov	bx, [lpage]	; get logical page number
                mov	ah, 44H		; map page
                int	67H
                mov	byte ptr [ems_err], ah
                or	ah, ah		; see if ok
                jnz	LL01

                mov	ax, 1
                jmp	short LL02
LL01:
                xor	ax, ax
LL02:
                ret
ems_map		ENDP
                
                SUBTTL ems_free
                PAGE +

ems_free	PROC
		arg	handle:word

                mov	dx, [handle]
                mov	ah, 45H
                int	67H
		xor	ax, ax

                ret
ems_free	ENDP

                SUBTTL ems_getframe
                PAGE +

ems_getframe	PROC	uses DS		; sets AX to EMS page frame
		mov	ax, DGROUP
		mov	ds, ax

                mov	ah, 41H		; get frame seq
                int	67H
                mov	byte ptr [ems_err], ah
                or	ah, ah		; ah is zero if call succeeded
                jnz	LL01
                mov	ax, bx		; return page frame in ax
                jmp	short LL02
LL01:
                xor	ax, ax
LL02:
                ret
ems_getframe	ENDP

                SUBTTL ems_pagecnt
                PAGE +

ems_pagecnt	PROC	uses DS		; sets AX to EMS page frame
		mov	ax, DGROUP
		mov	ds, ax

                mov	ah, 42H		; get ems page count
                int	67H
                mov	byte ptr [ems_err], ah
                or	ah, ah		; see if ok
                jnz	LL01

                mov	ax, bx		; return page count
                jmp	short LL02
LL01:
                xor	ax, ax
LL02:
                ret
ems_pagecnt	ENDP

                END
-----------------------------------------------------------------------------

Roy Frederick ([email-masked])
Dallas County Data Services  (214) 749-6340
504 Records Bldg.
Dallas, TX 75202