From robf@www.willows.com Sat Jan  6 01:28:37 1996
Return-Path: robf
Received: (from robf@localhost) by www.willows.com (8.6.9/8.6.9) id BAA15332; Sat, 6 Jan 1996 01:28:36 -0800
Date: Sat, 6 Jan 1996 01:28:35 -0800 (PST)
From: Rob Farnum <robf@www.willows.com>
To: tjh@mincom.oz.au
cc: Larry Wagner <lwagner@www.willows.com>, Rob Farnum <robf@www.willows.com>
Subject: Binary DLL Interface
In-Reply-To: <199601052328.AA16902@orb.mincom.oz.au>
Message-ID: <Pine.LNX.3.91.960106000655.15243B-100000@www>
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Status: O
X-Status: 

On Sat, 6 Jan 1996, Tim Hudson wrote:

>     No other responses from support so far.

I and Larry both replied to the original, the day it was posted, I don't
know why it did not get to you.

> >    1) If doing a loadlibrary on a binary dll, then a wrapper dll needs to be
> >       written to interface to it.  
>     I guessed as much ... I've just been asking for the details of how to
> do it as it's going to be the key issue in being able to get things to work.

We use what we call our OEM extensions, you will see in the code below.

     
>     But this is the first email message response ... did I miss something
> or lose some email?

My guess is yes, or our mailer.  I will try to monitor this.

> > 		<ctl3d.h>		We have support for the ctl3d library,

I haven't found this file at our site, but will look for it.  Strictly
speaking, from the library side we don't need it.

> > 	will require a $250 fee per platform per major release of the library.
>     Talk about blowing the industry appart ... this is *incredible*!!!

But will we make money? :-)

>     If you are going to provide sources and want to expand your platform
> options prior to the announcement/release of the source to the net then
> I'm certainly interested. 

This can easily be done, lets talk next week.

>     I can offer my time/skills to work with you using the platforms that
> I have available in-house:
>     AIX 3.2.5, DEC OSF 3.2 (aka DEC Unix), IRIX 4.x, 5.x, SunOS 4.1.3,
> SunOS 5.[34] (aka Solaris 2.[34]), HP-UX 9.04, DG-UX 5.4R3.10, Ultrix 4.3,
> BSD/OS 2.0, (and FreeBSD, Linux, NetBSD naturally).
>     (VMS and MVS if you really want ... actually VMS wouldn't be that
> insane an idea ...)

With the platform model, these should be easy to do, and adding them would
definitely add to its wider use.

>     If you want external comments on the code before it's released or
> someone outside of willows with significant blocks of code that will require
> extensions and the skills to add the extensions then I certainly can help
> in that area.

The area of dynamic binding to dll's as you will see is there, but immature.
I am very interested in cleaning it up, see below.

Rob Farnum
(408) 777-1823				                     robf@willows.com
(408) 777-1827 FAX 				       http://www.willows.com
Willows Software Inc.     12950 Saratoga Ave. Suite A,     Saratoga CA. 95070


Below you will find several files related to our ability to call binary
dll's.  We have a built-in table that defines the interface to a printer
and to both the first and second versions of ctl3d.  You will see the
tables that are used by GetProcAddress() to resolve functions within a
dll.  I'm going to look at something like the meter.dll to provide a sample
for the sdk.  For now, the salient points are:

1) LoadLibrary() needs to be done on the dll,
2) Given the hModule from 1, do GetProcAddress() to get the function pointer.
3) The address will be the binary thunk that you just call using normal
   indirect function calls, passing parameters as defined by the api.
4) The thunk takes parameters, sets up a call to the binary layer through
   the invoke_binary call.  I will check, but at one time I remember having
   a MakeProcInstance to complete the connection, (its been awhile)
5) on return from invoke_binary, pull return values out of the environment
   structure and back to native format, and
6) return from the thunk to your original caller.

Fine Points
a) the binary interface layer is setup when we start, and envp_global is
   a global that points to the active x86 frame, we internally stack them
   when necessary to recurse back into the library.
b) a weakness in the current scheme is that we have a fixed builtin table,
   and a hardcoded pointer to the 'attached' table.  An application can
   provide an alternate builtin table, but unfortunately you need to know
   what binaries you will load, and provide the table for it.  What I want
   to do is create a wrapper dll that will have  the OEM table for its
   associated binary dll.  On LoadLibrary() of the wrapper, in the LibMain
   we LoadLibrary the binary dll, and attach the OEM table.  The attached 
   table would be linked list.  In point of fact, the builtin table should
   be no different.  Building wrapper dll's would be a simple matter of
   providing the connection between the actual physical dll and its OEM
   table.
c) With the current scheme, before updating to the newer interface, all you
   need to do is provide your own 'attached' table, overridding the library
   default version, plus the conversion routines.  For a new dll, in your
   wrapper, all you should have to do is something like the following...

	BOOL
	cnb_function(HINSTANCE hInstance)
	{
		/* allocate pseudo-stack space */
    		envp_global->reg.sp -= HANDLE_86;

		/* put intel formatted data */
		/* note: hInstance is not a handle, but actually a */
		/*       selector in the binary layer */
    		PUTWORD(envp_global->reg.sp,
			GetDataSelectorFromInstance(hInstance));

		/* this will call cs:ip stored in envp_global() */
		/* I need to research how the cs:ip is actually */
		/* put into the envp_global structure, if I can */
		/* trust my memory, GetProcAddress does something */
		/* like  MakeProcAddress, which stores  the function */
		/* and the module/ordinal, and platform dependent code */
		/* in a block of memory.  When you call it, the platform */
		/* independent code takes the module/ordinal within the */
		/* block and stores it in the envp_global, and the jumps */
		/* to the function.  This keeps the stack unchanged, so */
		/* that when we jump to the function, its as though it */
		/* was called directly.  Then the invoke binary can do */
		/* its thing about setting up a call to the interpreter */
		/* or direct execution on intel */
    		invoke_binary();

		/* return  a value */
    		return (BOOL)envp_global->reg.ax;
	}

	static HSMT_OEMENTRYTAB OEM_tab_PRINTER[] =
	{	/* 000 */	{ "", 0 },
		/* 001 */	{ "FUNCTION", cnb_function },
		/* 002 */	{ "ShouldBeCaseSensitive", cnb_simple },
		/* end */	{ 0, 0 }
	};

	HSMT_OEMMODTAB *lpOEM_attached_modtab =
		{
		{ "SOMEDLL",OEM_tab_SOMEDLL },
		{ 0,	0}
		};

	To use the above, you would LoadLibrary() the binary dll, and 
	then call GetProcAddress to get a function within it.

d) To actually link to a binary dll, you would create a dll that has
   functions that correspond to the binary dll.  These functions would
   call an initialization code to load the binary dll, and then call
   GetProcAddress to get the pointer to the underlying thunk, (which 
   will do the actual call to the binary dll).  This would be something
   like...
	
		meter.so/meter.c
		=================
		static HINSTANCE hModule;
		
		UPARAM
		DllFunction(LPSTRUCT param)
		{
			static FARPROC	lpFunc;

			if(!hModule) {
				hModule = LoadLibrary("SomeDLL");
				/* handle failure */
				lpFunc = GetProcAddress(hModule,"DllFunction");	
				/* handle failure */
			}
			return (*lpFunc)(param);
		}
     Now you can link to the binary dll through a wrapper, which would load
     the corresponding binary dll.  You can add  the LoadLibrary to the 
     LibMain to abort early if the binary dll is not present.
	
========================================================================== 

The files below provide the printer and ctl3d interfaces, you may be able
to use them as a model of how to call a binary dll, while I build a more
discrete sample.  Don't be disturbed by the amount of code here, it is the
interface to binary printer drivers and shows how complex it is, but also
that all printers use the same interface.  You will only need a small 
subset in your wrapper thunks, plus a few more header files from us.

Take a look and let me know if this has been helpful.  

===============================================================
OEM.h
===============================================================
#ifndef OEM__h
#define OEM__h
/* "@(#)OEM.h	2.1 :/users/sccs/src/win/s.OEM.h 2/3/95 17:43:22" */

typedef struct tagHSMT_OEMENTRYTAB
{
    LPSTR	name;        /* entry point name */
                        /* will be NULL to indicate end of entry table */
    long int	(*fun)();  /* pointer to C-callable conversion routine */
} HSMT_OEMENTRYTAB;

typedef struct tagHSMT_OEMMODTAB
{
    LPSTR	name;        /* module name */
    HSMT_OEMENTRYTAB	*hsmt_OEMEntry;
} HSMT_OEMMODTAB;
#endif /* OEM__h */
===============================================================
OEMDummy.c
===============================================================

#include "windows.h"
#include "OEM.h"

HSMT_OEMMODTAB *lpOEM_attached_modtab = (HSMT_OEMMODTAB *)0;
===============================================================
OEMRoutines.c
===============================================================
/********************************************************************
*   OEMRoutines.c
*
*   
*
*   Copyright (c) 1995, Willows Software Inc.  All rights reserved.
********************************************************************/

/* "@(#)OEMRoutines.c	2.17 :/users/sccs/src/win/s.OEMRoutines.c 12/12/95 12:09:57" */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "windows.h"
#include "print.h"

#include "kerndef.h"
#include "BinTypes.h"
#include "Endian.h"
#include "Log.h"
#include "DPMI.h"
#include "Resources.h"
#include "Module.h"
#include "OEM.h"
#include "GdiDDK.h"
#include "PrinterBin.h"
#include "PrinterDC.h"

extern ENV *envp_global;
extern DSCR *LDT;
extern HSMT_OEMMODTAB OEM_builtin_modtab[];
extern HSMT_OEMMODTAB *lpOEM_attached_modtab;

extern long int IT_ENUMBRUSHPROC();
extern long int IT_ENUMPENPROC();
extern long int IT_ENUMFONTPROC();

extern LPWORD hsw_ConvertWordArray(LPBYTE,int);
extern LPWORD hsw_ConvertArrayToWord(LPBYTE,int);
extern LPINT hsw_ConvertArrayToInt(LPBYTE,int);

static short int DEVICEsize = 0;

#define STK_DATA_256	256
#define STK_DATA_512	512
#define STK_DATA_576	576
#define STK_EXTDEVMODE	(2 * HANDLE_86  +  5 * LP_86  +  WORD_86)
#define STK_ADVSETUP	(2 * HANDLE_86  +  2 * LP_86)
#define STK_ENABLE	(4 * LP_86 + WORD_86)
#define STK_ENUMDFONTS	(4 * LP_86)
#define STK_ENUMOBJ	(3 * LP_86 + WORD_86)
#define STK_OUTPUT	(6 * LP_86 + 2 * WORD_86)
#define STK_DEVICEMODE	(2 * HANDLE_86  +  2 * LP_86)
#define STK_REALIZEOBJ	(4 * LP_86  +  WORD_86)
#define STK_CONTROL	(3 * LP_86  +  WORD_86)
#define STK_EXTTEXTOUT	(8 * LP_86  +  3 * WORD_86  +  INT_86)
#define STK_GETCHARWID	(5 * LP_86  +  2 * WORD_86)
#define STK_DEVICECAPS	(4 * LP_86  +  WORD_86)
#define STK_STRETCHDIB	(7 * LP_86  +  9 * WORD_86  +  DWORD_86)
#define STK_BITBLT	(4 * LP_86  +  6 * WORD_86  +  DWORD_86)
#define STK_STRETCHBLT	(5 * LP_86  +  8 * WORD_86  +  DWORD_86)
#define STK_COLORINFO	(2 * LP_86  +  DWORD_86)
#define STK_PIXEL	(2 * LP_86  +  2*INT_86 + DWORD_86)

FARPROC
OEM_GetProcAddress(LPMODULEINFO lpModuleInfo, UINT uOrdinal)
{
    HSMT_OEMMODTAB *lpModTab;
    DWORD dwBinAddr = 0;
    FARPROC lpFunc;
    WORD wSel = 0;
    char szBuf[128];
    BOOL bPrintDrv = FALSE;
    LPSTR lpszDriver;

    if ( !lpModuleInfo->lpDLL )
	return (FARPROC)0;

    if (GetProfileString("windows", "device", "", szBuf, 128)) {
	if ((lpszDriver = strtok(szBuf, ",")) &&
	    (lpszDriver = strtok(NULL, ",")))
	    bPrintDrv = TRUE;
    }

    /* first search builtin table, then attached table */
    lpModTab = &OEM_builtin_modtab[0];
    while (lpModTab->name &&
	   strcasecmp(lpModTab->name,lpModuleInfo->lpModuleName) &&
	   (!bPrintDrv || strcasecmp(lpModuleInfo->lpModuleName,lpszDriver) ||
	    strcasecmp(lpModTab->name,"PRINTER")))
	lpModTab++;
    if (!lpModTab->name) {
	if (!(lpModTab = lpOEM_attached_modtab))
	    return (FARPROC)0;
	while (lpModTab->name &&
		strcasecmp(lpModTab->name,lpModuleInfo->lpModuleName))
	    lpModTab++;
	if (!lpModTab->name)
	    return (FARPROC)0;
    }

    if (lpModuleInfo->lpEntryTable[uOrdinal].sel) {
	wSel = ((lpModuleInfo->wSelBase<<3) + 
		lpModuleInfo->lpEntryTable[uOrdinal].sel - 0x8) | 0x7;
	dwBinAddr = (DWORD)MAKELP(wSel,
		lpModuleInfo->lpEntryTable[uOrdinal].off);
    }
    if (dwBinAddr) {
	if (lpFunc = (FARPROC)lpModTab->hsmt_OEMEntry[uOrdinal].fun)
	    return (FARPROC)make_native_thunk(dwBinAddr,lpFunc);
	else {
	    LOGSTR((LF_PRINTER,
		"OEM_GetProcAddress: UNIMP conversion mod %s func %s\n",
		lpModuleInfo->lpModuleName,
		lpModTab->hsmt_OEMEntry[uOrdinal].name));
	    return (FARPROC)0;
	}
    }
    else
	return (FARPROC)0;
}

int
cnb_ExtDeviceMode(HWND hwnd, HINSTANCE hDriver, LPDEVMODE lpdmOutput,
		LPSTR lpszDevice, LPSTR lpszPort, LPDEVMODE lpdmInput,
		LPSTR lpszProfile, WORD fwMode)
{
    LPBYTE lpSP,ss,lpStruct;
    int retcode;
    WORD wSelProfile=0,wSelPort=0,wSelDevice=0;

    LOGSTR((LF_INTERFACE,"cnb_ExtDeviceMode: calling ExtDeviceMode\n"));
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_EXTDEVMODE;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = GetPhysicalAddress((WORD)envp_global->reg.ss);
    PUTWORD(lpSP,fwMode);
    if (lpszProfile) {
	wSelProfile = ASSIGNSEL(lpszProfile, strlen(lpszProfile)+1);
	PUTDWORD(lpSP+2,MAKELP(wSelProfile,0));
    }
    else
	PUTDWORD(lpSP+2,0L);
    if (fwMode) {
	if (lpdmInput) {
	    PutDEVMODE(lpStruct,lpdmInput);
	    PUTWORD(lpSP+8,(WORD)envp_global->reg.ss);
	    PUTWORD(lpSP+6,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    lpStruct += lpdmInput->dmSize + lpdmInput->dmDriverExtra;
	}
	else
	    PUTDWORD(lpSP+6,0L);
	if (lpdmOutput) {
	    PutDEVMODE(lpStruct,lpdmOutput);
	    PUTWORD(lpSP+20,(WORD)envp_global->reg.ss);
	    PUTWORD(lpSP+18,(WORD)((DWORD)lpStruct - (DWORD)ss));
	}
	else
	    PUTDWORD(lpSP+18,0L);
    }
    if (lpszPort) {
	wSelPort = ASSIGNSEL(lpszPort, strlen(lpszPort)+1);
	PUTDWORD(lpSP+10,MAKELP(wSelPort,0));
    }
    else
	PUTDWORD(lpSP+10,0L);
    if (lpszDevice) {
	wSelDevice = ASSIGNSEL(lpszDevice, strlen(lpszDevice)+1);
	PUTDWORD(lpSP+14,MAKELP(wSelDevice,0));
    }
    else
	PUTDWORD(lpSP+14,0L);
    PUTWORD(lpSP+22,(WORD)GetDataSelectorFromInstance(hDriver));
    PUTWORD(lpSP+24,(WORD)hwnd);
    invoke_binary();
    if (wSelProfile)
	FreeSelector(wSelProfile);
    if (wSelPort)
	FreeSelector(wSelPort);
    if (wSelDevice)
	FreeSelector(wSelDevice);
    if (fwMode && lpdmOutput)
	GetDEVMODE(lpdmOutput,lpStruct);
    retcode = (int)(short)LOWORD(envp_global->reg.ax);
    if (fwMode == 0 && retcode > 0)
		/* return the size of buffer for DEVMODE struct */
	retcode = retcode - 12*INT_86 - 4*UINT_86 - DWORD_86 - CCHDEVICENAME +
			sizeof(DEVMODE);
    envp_global->reg.sp += STK_DATA_512;
    return retcode;
}

WORD
cnb_AdvancedSetupDialog(HWND hwnd, HINSTANCE hDriver, LPDEVMODE lpDMin,
		LPDEVMODE lpDMout)
{
	LPBYTE	lpSP,ss,lpStruct, lpStructOut=NULL;
	int	retcode;

	LOGSTR((LF_INTERFACE,"cnb_AdvancedSetupDialog: %s\n",
			"calling AdvancedSetupDialog"));
	envp_global->reg.sp -= STK_DATA_512;
	lpStruct = (LPBYTE)envp_global->reg.sp;
	envp_global->reg.sp -= STK_ADVSETUP;
	lpSP = (LPBYTE)envp_global->reg.sp;
	ss = GetPhysicalAddress((WORD)envp_global->reg.ss);

	if (lpDMout) {
		PutDEVMODE(lpStruct, lpDMout);
		PUTWORD(lpSP+2, (WORD)envp_global->reg.ss);
		PUTWORD(lpSP  , (WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStructOut = lpStruct;
		lpStruct += lpDMin->dmSize + lpDMin->dmDriverExtra;
	}
	else
		PUTDWORD(lpSP, 0L);

	if (lpDMin) {
		PutDEVMODE(lpStruct, lpDMin);
		PUTWORD(lpSP+6, (WORD)envp_global->reg.ss);
		PUTWORD(lpSP+4, (WORD)((DWORD)lpStruct - (DWORD)ss));
	}
	else
		PUTDWORD(lpSP+4, 0L);

	PUTWORD(lpSP+8,  (WORD)GetDataSelectorFromInstance(hDriver));
	PUTWORD(lpSP+10, (WORD)hwnd);

	invoke_binary();

	if (lpDMout)
		GetDEVMODE(lpDMout,lpStructOut);

	retcode = (WORD)(short)LOWORD(envp_global->reg.ax);
	envp_global->reg.sp += STK_DATA_512;
	return retcode;
}

WORD
cnb_Enable(LPVOID lpDevInfo, WORD wStyle, LPSTR lpDestDevType,
		LPSTR lpOutputFile, LPVOID lpData)
{
    LPBYTE lpSP,ss,lpStruct;
    WORD wSel1=0,wSel2=0,wSel3=0;

    LOGSTR((LF_INTERFACE,"cnb_Enable: calling Enable\n"));
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_ENABLE;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = GetPhysicalAddress(envp_global->reg.ss);
    if (lpData) {
	PutDEVMODE(lpStruct,(LPDEVMODE)lpData);
	PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
	PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
	lpStruct += 12*INT_86 + 4*UINT_86 + DWORD_86 + CCHDEVICENAME;
    }
    else
	PUTDWORD(lpSP,0L);
    if (lpOutputFile) {
	wSel1 = ASSIGNSEL(lpOutputFile, strlen(lpOutputFile)+1);
	PUTDWORD(lpSP+4,MAKELP(wSel1,0));
    }
    else 
	PUTDWORD(lpSP+4,0L);
    if (lpDestDevType) {
	wSel2 = ASSIGNSEL(lpDestDevType, strlen(lpDestDevType)+1);
	PUTDWORD(lpSP+8,MAKELP(wSel2,0));
    }
    else 
	PUTDWORD(lpSP+8,0L);
    PUTWORD(lpSP+12,wStyle);
    if (lpDevInfo) {
	if (wStyle & 1) {
	   PUTWORD(lpSP+16,(WORD)envp_global->reg.ss);
	   PUTWORD(lpSP+14,(WORD)((DWORD)lpStruct - (DWORD)ss));
	}
	else {
	    wSel3 = ASSIGNSEL(lpDevInfo, DEVICEsize);
	    PUTDWORD(lpSP+14,MAKELP(wSel3,0));
	}
    }
    else
	PUTDWORD(lpSP+14,0L);
    invoke_binary();
    if (wSel1)
	FreeSelector(wSel1);
    if (wSel2)
	FreeSelector(wSel2);
    if (wSel3)
	FreeSelector(wSel3);
    if (lpDevInfo && (wStyle & 1)) {
	GetGDIINFO((GDIINFO *)lpDevInfo,lpStruct);
	DEVICEsize = ((GDIINFO *)lpDevInfo)->dpDEVICEsize;
    }

    envp_global->reg.sp += STK_DATA_512;
    return (LOWORD(envp_global->reg.ax));
}

WORD
cnb_EnumDFonts(LPPDEVICE lpDestDev, LPSTR lpFaceName,
		FARPROC lpCallbackFunc, LPVOID lpClientData)
{
    LPBYTE lpSP;
    DWORD dwProc;
    WORD wSel=0,wSel1=0,wSel2=0;

    LOGSTR((LF_INTERFACE,"cnb_EnumDFonts: calling EnumDFonts\n"));
    envp_global->reg.sp -= STK_ENUMDFONTS;
    lpSP = (LPBYTE)envp_global->reg.sp;

    if (lpDestDev) {
	wSel = ASSIGNSEL(lpDestDev, DEVICEsize);
	PUTDWORD(lpSP+12,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+12,0L);

    if (lpFaceName) {
	wSel1 = ASSIGNSEL(lpFaceName, strlen(lpFaceName)+1);
	PUTDWORD(lpSP+8,MAKELP(wSel1,0));
    }
    else
	PUTDWORD(lpSP+8,0L);

    dwProc = (lpCallbackFunc)?
		make_binary_thunk(lpCallbackFunc,IT_ENUMFONTPROC):0;
    PUTDWORD(lpSP+4,dwProc);

    PUTDWORD(lpSP,(DWORD)lpClientData);

    invoke_binary();
    if (wSel)
	FreeSelector(wSel);
    if (wSel1)
	FreeSelector(wSel1);
    if (wSel2)
	FreeSelector(wSel2);
    return (LOWORD(envp_global->reg.ax));
}

WORD
cnb_EnumObj(LPPDEVICE lpDestDev, WORD wStyle,
		FARPROC lpCallbackFunc, LPVOID lpClientData)
{
    LPBYTE lpSP;
    DWORD dwProc;
    WORD wSel=0,wSel1=0;

    LOGSTR((LF_INTERFACE,"cnb_EnumObj: calling EnumObjects\n"));
    envp_global->reg.sp -= STK_ENUMOBJ;
    lpSP = (LPBYTE)envp_global->reg.sp;

    if (lpDestDev) {
	wSel = ASSIGNSEL(lpDestDev, DEVICEsize);
	PUTDWORD(lpSP+10,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+10,0L);
    PUTWORD(lpSP+8,wStyle);

    switch (wStyle) {
	case OBJ_PEN:
	    dwProc = (lpCallbackFunc)?
		make_binary_thunk(lpCallbackFunc,IT_ENUMPENPROC):0;
	    break;
	case OBJ_BRUSH:
	    dwProc = (lpCallbackFunc)?
		make_binary_thunk(lpCallbackFunc,IT_ENUMBRUSHPROC):0;
	    break;
	default:
	    dwProc = 0;
	    printf("EnumObj: unknown object type!!!\n");
	    break;
    }
    PUTDWORD(lpSP+4,dwProc);
    PUTDWORD(lpSP,(DWORD)lpClientData);

    invoke_binary();
    if (wSel1)
	FreeSelector(wSel1);
    if (wSel)
	FreeSelector(wSel);
    return (LOWORD(envp_global->reg.ax));
}

int
cnb_Output(LPPDEVICE lpDestDev, WORD wStyle, WORD wCount, 
		LPPOINT lpPoints, LPVOID lpPhPen, LPVOID lpPhBrush,
		LPDRAWMODE lpDrawMode,
		LPRECT lpClipRect)
{
    LPBYTE lpSP;
    LPBYTE lpStruct,ss,lpData;
    WORD wSelDest=0,wSel1=0, wSel2=0, wSel3=0, wSelDestBmp = 0;
    WORD wSelDestBits = 0;
    LPPSBITMAP lpPSBitmap;
    int nByteCount;
    BYTE bufDest[40];

    LOGSTR((LF_INTERFACE,"cnb_Output: calling Output\n"));
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_OUTPUT;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = (LPBYTE)GetPhysicalAddress(envp_global->reg.ss);

    if (lpDestDev) {
	if (lpDestDev->pdType) {		/* device DC */
	    wSelDest = ASSIGNSEL(lpDestDev, DEVICEsize);
	    PUTDWORD(lpSP+24, MAKELP(wSelDest,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpDestDev;
	    lpData = bufDest;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelDestBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelDestBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_Output: huge dest bitmap");
	    wSelDestBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+24, MAKELP(wSelDestBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+24, 0L);

    PUTWORD(lpSP+22,wStyle);
    PUTWORD(lpSP+20,wCount);

    /* now convert the array of points */
    if (lpPoints) {
	wSel1 = ASSIGNSEL(hsw_ConvertArrayToWord((LPBYTE)lpPoints,2*wCount),
					2 * wCount * WORD_86);
	PUTDWORD(lpSP+16,MAKELP(wSel1,0));
    }
    else
	PUTDWORD(lpSP+16,0L);

    /* convert the physical pen */
    if (lpPhPen) {
	wSel2 = ASSIGNSEL(lpPhPen, STK_DATA_256);
	PUTDWORD(lpSP+12,MAKELP(wSel2,0));
    }
    else
	PUTDWORD(lpSP+12,0L);

    /* convert the physical brush */
    if (lpPhBrush) {
	wSel3 = ASSIGNSEL(lpPhBrush, STK_DATA_256);
	PUTDWORD(lpSP+8,MAKELP(wSel3,0));
    }
    else
	PUTDWORD(lpSP+8,0L);

    /* convert the DRAWMODE struct */
    if (lpDrawMode) {
	PutDRAWMODE(lpStruct,lpDrawMode);
	PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
	PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
	lpStruct += 32;
    }
    else
	PUTDWORD(lpSP+4,0L);

    /* convert the clipping rect */
    if (lpClipRect) {
	PUTWORD(lpStruct,(WORD)lpClipRect->left);
	PUTWORD(lpStruct+2,(WORD)lpClipRect->top);
	PUTWORD(lpStruct+4,(WORD)lpClipRect->right);
	PUTWORD(lpStruct+6,(WORD)lpClipRect->bottom);
	PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
	PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
    }
    else
	PUTDWORD(lpSP,0L);

    invoke_binary();
    if (wSel3)
	FreeSelector(wSel3);
    if (wSel2)
	FreeSelector(wSel2);
    if (wSel1)
	FreeSelector(wSel1);
    if (wSelDest)
	FreeSelector(wSelDest);
    if (wSelDestBmp)
	FreeSelector(wSelDestBmp);
    if (wSelDestBits)
	FreeSelector(wSelDestBits);

    envp_global->reg.sp += STK_DATA_512;
    return (int)(short)(LOWORD(envp_global->reg.ax));
}

void
cnb_DeviceMode(HWND hWnd, HINSTANCE hInstance,
		LPSTR lpDestDevType, LPSTR lpOutputFile)
{
    LPBYTE lpSP;
    WORD wSel1=0,wSel2=0;

    LOGSTR((LF_INTERFACE,"cnb_DeviceMode: calling DeviceMode\n"));
    envp_global->reg.sp -= STK_DEVICEMODE;
    lpSP = (LPBYTE)envp_global->reg.sp;
    PUTWORD(lpSP+10,(WORD)hWnd);
    PUTWORD(lpSP+8,(WORD)GetDataSelectorFromInstance(hInstance));
    if (lpDestDevType) {
	wSel1 = ASSIGNSEL(lpDestDevType, strlen(lpDestDevType)+1);
	PUTDWORD(lpSP+4,MAKELP(wSel1,0));
    }
    else 
	PUTDWORD(lpSP+4,0L);
    if (lpOutputFile) {
	wSel2 = ASSIGNSEL(lpOutputFile, strlen(lpOutputFile)+1);
	PUTDWORD(lpSP,MAKELP(wSel2,0));
    }
    else 
	PUTDWORD(lpSP,0L);
    invoke_binary();
    if (wSel1)
	FreeSelector(wSel1);
    if (wSel2)
	FreeSelector(wSel2);
}


DWORD
cnb_RealizeObject(LPPDEVICE lpDestDev, int iStyle, LPVOID lpInObj,
		LPVOID lpOutObj, TEXTXFORM *lpTextXForm)   
{
    static int	iPhFontSize;
    LPBYTE	lpOutBin = NULL;
    LPBYTE	lpSP, lpStruct,ss;
    LPBYTE	lptxfBin=NULL;
    DWORD	retcode;
    LPLOGPEN	lplp;
    LPLOGBRUSH	lplb;
    WORD	wSel=0, wSel1 = 0, wSel2 = 0, wSelBmp = 0;
    BOOL	b2ndCall = FALSE;
    int		size;
	

    LOGSTR((LF_INTERFACE,"cnb_RealizeObject: %s Object Type %d:",
		(lpOutObj) ? "(2nd pass)" : "(1st pass-GetSize)", iStyle));
    envp_global->reg.ax = envp_global->reg.dx = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_REALIZEOBJ;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = GetPhysicalAddress(envp_global->reg.ss);

    PUTWORD(lpSP+12, iStyle);

    if (lpDestDev) {
	wSel = ASSIGNSEL(lpDestDev, DEVICEsize);
	PUTDWORD(lpSP+14,MAKELP(wSel,0));
    }
    else 
	PUTDWORD(lpSP+14,0L);

    switch ( iStyle ) {

	case OBJ_PEN:
	    LOGSTR((LF_INTERFACE,"PEN\n"));
	    PUTDWORD(lpSP+8,0L);

	    if ( lpInObj ) {
		/* Convert the logical pen */
		lplp = (LPLOGPEN)lpInObj;
		PUTWORD(lpStruct,(WORD)lplp->lopnStyle);
		PUTWORD(lpStruct+2,(WORD)lplp->lopnWidth.x);
		PUTWORD(lpStruct+4,(WORD)lplp->lopnWidth.y);
		PUTDWORD(lpStruct+6,lplp->lopnColor);
		PUTWORD(lpSP+10,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+8,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStruct += 12;
	    }
	    else
		PUTDWORD(lpSP+8,0L);

	    if (lpOutObj) {
		wSel2 = ASSIGNSEL(lpOutObj, STK_DATA_256);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    else
		PUTDWORD(lpSP+4,0L);

	    break;

	case OBJ_FONT:
	    LOGSTR((LF_INTERFACE,"FONT\n"));
	    if (lpTextXForm) {
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lptxfBin = lpStruct;
		lpStruct += 20;		/* 19 */
	    }
	    else 
		PUTDWORD(lpSP,0L);

	    if ( lpInObj ) {
		PutLOGFONT(lpStruct,(LPLOGFONT)lpInObj,80);
		PUTWORD(lpSP+10,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+8,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStruct += 52;	/* 50 */
	    }
	    else 
		PUTDWORD(lpSP+8,0L);

	    if (lpOutObj) {
		/* 2nd call */
		/* Should be in separate segment */
		if ( !(lpOutBin = (LPBYTE)WinMalloc(iPhFontSize)) ) {
		    envp_global->reg.sp += STK_DATA_512 + STK_REALIZEOBJ;
		    return 0;
		}
		b2ndCall = TRUE;
		wSel2 = ASSIGNSEL(lpOutBin, iPhFontSize);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    else
		/* 1st call */
		PUTDWORD(lpSP+4,0L);

	    break;

	case OBJ_BRUSH:
	    LOGSTR((LF_INTERFACE,"BRUSH\n"));
	    if (lpTextXForm) {
		PUTWORD(lpStruct,((LPPOINT)lpTextXForm)->x);
		PUTWORD(lpStruct+2,((LPPOINT)lpTextXForm)->y);
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStruct += 4;
	    }
	    else
		PUTDWORD(lpSP,0L);

	    if ( lpInObj ) {
		LPBYTE lpTmp = lpStruct+20;

		lplb = (LOGBRUSH *)lpInObj;
		PUTWORD(lpStruct,lplb->lbStyle);

		if ( lplb->lbStyle == BS_PATTERN ) {
			wSelBmp = PutBITMAP(lpTmp, (LPBITMAP)lplb->lbColor);
			PUTWORD(lpStruct+4,(WORD)envp_global->reg.ss);
			PUTWORD(lpStruct+2,(WORD)((DWORD)lpTmp - (DWORD)ss));
			lpTmp += 16;
		}
		else
		    	PUTDWORD(lpStruct+2,lplb->lbColor);

		PUTWORD(lpStruct+6,lplb->lbHatch);
		PUTWORD(lpSP+10,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+8,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    }
	    else
		PUTDWORD(lpSP+8,0L);

	    if (lpOutObj) {
		wSel2 = ASSIGNSEL(lpOutObj, STK_DATA_256);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    else
		PUTDWORD(lpSP+4,0L);

	    break;

	case OBJ_PBITMAP:
	    LOGSTR((LF_INTERFACE,"BITMAP\n"));
#ifdef LATER
	    PUTDWORD(lpSP+8,0L);
	    if (lpInObj) {
		wSel1 = ASSIGNSEL(lpInObj, xxx);    /* Figure it out LATER */
		PUTDWORD(lpSP+8,MAKELP(wSel1,0));
	    }
	    else {
		PUTDWORD(lpSP+8,0L);
	    }
	    if (lpOutObj) {
		wSel2 = ASSIGNSEL(lpOutObj, xxx);   /* Figure it out LATER */
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    else
		PUTDWORD(lpSP+4,0L);

	    break;
#endif

	case -OBJ_PEN:
	case -OBJ_FONT:
	case -OBJ_BRUSH:
	    LOGSTR((LF_INTERFACE,"Delete PEN\\FONT\\BRUSH\n"));
	    PUTDWORD(lpSP,0L);		/* TEXTXFORM */
	    size = 0;

	    if ( iStyle == -OBJ_FONT ) {
		size = SIZEOF_DFONTINFO_BIN + ((RFONT*)lpOutObj)->iBinSize;

		if ( !(lpOutBin = (LPBYTE)WinMalloc(size)) ) {
		     envp_global->reg.sp += STK_DATA_512 + STK_REALIZEOBJ;
		     return 0;
		}
		PutRFONT(lpOutBin, (RFONT*)lpOutObj);
	    }
	    else if ( lpOutObj ) {
		lpOutBin = (LPBYTE)lpOutObj;
		size = STK_DATA_256;
	    }

	    if ( lpOutBin ) {
		wSel2 = ASSIGNSEL(lpOutBin, size);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));	/* OutObj */
		PUTDWORD(lpSP+8,MAKELP(wSel2,0));	/* InObj == OutObj */
	    }
	    else {
		PUTDWORD(lpSP+4,0L);
		PUTDWORD(lpSP+8,0L);
	    }

	    break;

	default:
	    LOGSTR((LF_INTERFACE,"UNKNOWN OBJECT (RealizObject failed)!!!\n"));
	    retcode = 0x80000000L;
	    envp_global->reg.sp += STK_DATA_512 + STK_REALIZEOBJ;
	    return retcode;
    }

    invoke_binary();

    if (wSel)
	FreeSelector(wSel);
    if (wSel1)
	FreeSelector(wSel1);
    if (wSel2)
	FreeSelector(wSel2);
    if (wSelBmp)
	FreeSelector(wSelBmp);

    retcode = MAKELONG(LOWORD(envp_global->reg.ax),LOWORD(envp_global->reg.dx));

    if ( retcode ) {
	if ( iStyle == OBJ_FONT )
	    if ( b2ndCall && lpOutBin) {
		GetTEXTXFORM(lpTextXForm, lptxfBin);
		GetRFONT((RFONT*)lpOutObj, lpOutBin, retcode);
		WinFree((LPSTR)lpOutBin);
	    }
	    else {
		iPhFontSize = retcode;
		retcode += sizeof(RFONT) + SIZEOF_DFONTINFO_BIN;
	    }
    }
    else
	LOGSTR((LF_INTERFACE,"failed to RealizeObject of type %d\n", iStyle));

    envp_global->reg.sp += STK_DATA_512;
    return retcode;	/* Size of the output structure */
}


WORD
cnb_Control(LPPDEVICE lpDestDev, WORD wFunction,
		LPSTR lpInData, LPSTR lpOutData)
{
    WORD	wSel = 0, wSel1 = 0, wSel2 = 0, wSel3 = 0;
    LPBYTE	lpSP,lpStruct,ss;
    HDC		hDC;
    LPRECT	lpRect;
    LPDOCINFO	lpdi;

    LOGSTR((LF_INTERFACE,"cnb_Control: calling Control\n"));
    envp_global->reg.ax = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_CONTROL;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = GetPhysicalAddress(envp_global->reg.ss);

    PUTWORD(lpSP+8,wFunction);

    if (lpDestDev) {
	wSel = ASSIGNSEL(lpDestDev, DEVICEsize);
	PUTDWORD(lpSP+10,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+10,0L);

    switch(wFunction) {
	case NEWFRAME:
	case ENDDOC:
	case ABORTDOC:
	case BEGIN_PATH:
	case FLUSHOUTPUT:
	    PUTDWORD(lpSP,0L);
	    PUTDWORD(lpSP+4,0L);
	    invoke_binary();
	    break;
	case SETABORTPROC:
	    PUTDWORD(lpSP,0L);
	    hDC = GETWORD(lpInData);
	    wSel1 = ASSIGNSEL(&hDC, sizeof(HDC));
	    PUTDWORD(lpSP+4,MAKELP(wSel1,0));
	    invoke_binary();
	    break;
	case RESETDC:
	    PUTDWORD(lpSP,0L);
	    wSel1 = ASSIGNSEL(lpInData, strlen(lpInData)+1);
	    PUTDWORD(lpSP+4,MAKELP(wSel1,0));
	    invoke_binary();
	    break;

	case STARTDOC:
	    wSel1 = ASSIGNSEL(lpInData, strlen(lpInData)+1);
	    PUTDWORD(lpSP+4,MAKELP(wSel1,0));
	    if (lpdi = (LPDOCINFO)lpOutData) {
		PUTWORD(lpStruct, lpdi->cbSize);
		if (lpdi->lpszDocName) {
		    wSel2 = ASSIGNSEL(lpdi->lpszDocName,
				strlen(lpdi->lpszDocName)+1);
		    PUTDWORD(lpStruct+2,MAKELP(wSel2,0));
		}
		else
		    PUTDWORD(lpStruct+2,0);
		if (lpdi->lpszOutput) {
		    wSel3 = ASSIGNSEL(lpdi->lpszOutput,
				strlen(lpdi->lpszOutput)+1);
		    PUTDWORD(lpStruct+6,MAKELP(wSel3,0));
		}
		else
		    PUTDWORD(lpStruct+6,0);
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    }
	    else
		PUTDWORD(lpSP,0L);
	    invoke_binary();
	    break;

	case GETFACENAME:
	case GETTECHNOLOGY:
	    PUTDWORD(lpSP+4,0L);
	    if (lpOutData) {
		wSel1 = ASSIGNSEL(lpOutData, STK_DATA_256);
		PUTDWORD(lpSP,MAKELP(wSel1,0));
	    }
	    else
		PUTDWORD(lpSP,0L);
	    invoke_binary();
	    break;

	case GETPHYSPAGESIZE:
	case GETPRINTINGOFFSET:
	case GETSCALINGFACTOR:
	    PUTDWORD(lpSP+4,0L);
	    if (lpOutData) {
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    }
	    else
		PUTDWORD(lpSP,0L);
	    invoke_binary();
	    if (lpOutData) {
		((LPPOINT)lpOutData)->x = (int)(short)GETWORD(lpStruct);
		((LPPOINT)lpOutData)->y = (int)(short)GETWORD(lpStruct+2);
	    }
	    break;

	case ENABLEPAIRKERNING:
	case SETKERNTRACK:
	    if ( lpInData ) {
		/* cbRFsize = 0 : Don't care about font */
		PutEXTTEXTDATA(lpStruct,(WORD)envp_global->reg.ss,(DWORD)ss,0);
		PUTWORD(lpSP+6, (WORD)envp_global->reg.ss);
		PUTWORD(lpSP+4, (WORD)((DWORD)lpStruct - (DWORD)ss));
		/* Prepare APPEXTTEXTDATA (PutAPPEXTTEXTDATA() ?) */
		PUTWORD(lpStruct + SIZEOF_APPEXTTEXTDATA_BIN,
				(WORD)(((LPEXTTEXTDATA)lpInData)->lpInData->x));
	    }
	    else
		PUTDWORD(lpSP+4,0L);
	    if ( lpOutData ) {
		wSel2 = ASSIGNSEL(lpOutData, STK_DATA_256);
            	PUTDWORD(lpSP,MAKELP(wSel2,0));
	    }
	    else
		PUTDWORD(lpSP,0L);
	    invoke_binary();
	    if ( lpOutData )
		*((LPINT)lpOutData) = (int)GETWORD(lpStruct);
	    break;

	case SETLINECAP:
	case SETCOPYCOUNT:
	case ENABLERELATIVEWIDTHS:
	case QUERYESCSUPPORT:
	    if (lpInData) {
		PUTWORD(lpStruct,(WORD)(*((int *)lpInData)));
		PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStruct += 4;
	    }
	    else
		PUTDWORD(lpSP+4,0L);
	    if (lpOutData) {
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    }
	    else
		PUTDWORD(lpSP,0L);
	    invoke_binary();
	    if (lpOutData) 
		*((int *)lpOutData) = (int)(short)GETWORD(lpStruct);
	    break;
	case EXT_DEVICE_CAPS:
	    if (lpInData) {
		PUTWORD(lpStruct,(WORD)(*((int *)lpInData)));
		PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStruct += 4;
	    }
	    else
		PUTDWORD(lpSP+4,0L);
	    if (lpOutData) {
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    }
	    else
		PUTDWORD(lpSP,0L);
	    invoke_binary();
	    if (lpOutData) 
		*((long *)lpOutData) = GETDWORD(lpStruct);
	    break;
	case SET_BOUNDS:
	case SET_CLIP_BOX:
	    if (lpInData) {
		lpRect = (LPRECT)lpInData;
		PUTWORD(lpStruct,(WORD)lpRect->left);
		PUTWORD(lpStruct+2,(WORD)lpRect->top);
		PUTWORD(lpStruct+4,(WORD)lpRect->right);
		PUTWORD(lpStruct+6,(WORD)lpRect->bottom);
		PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
	    }
	    else
		PUTDWORD(lpSP+4,0L);
	    PUTDWORD(lpSP,0L);
	    invoke_binary();
	    break;
	case NEXTBAND:
		PUTWORD(lpStruct,  (WORD)((LPRECT)lpOutData)->left);
		PUTWORD(lpStruct+2,(WORD)((LPRECT)lpOutData)->top);
		PUTWORD(lpStruct+4,(WORD)((LPRECT)lpOutData)->right);
		PUTWORD(lpStruct+6,(WORD)((LPRECT)lpOutData)->bottom);
		PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP  ,(WORD)((DWORD)lpStruct - (DWORD)ss));
		invoke_binary();
		((LPRECT)lpOutData)->left   = (int)GETWORD(lpStruct);
		((LPRECT)lpOutData)->top    = (int)GETWORD(lpStruct+2);
		((LPRECT)lpOutData)->right  = (int)GETWORD(lpStruct+4);
		((LPRECT)lpOutData)->bottom = (int)GETWORD(lpStruct+6);
		break;
	case SETCHARSET:
	    if (lpInData) {
		PUTWORD(lpStruct,(WORD)(*((int *)lpInData)));
		PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
		PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
		lpStruct += 4;
	    }
	    else
		PUTDWORD(lpSP+4,0L);

	    PUTDWORD(lpSP,0L);
	    invoke_binary();
	    break;

	case GETEXTENDEDTEXTMETRICS: {	/* MS Word 6.0 */
		LPEXTTEXTDATA	lpetd = (LPEXTTEXTDATA)lpInData;
		LPBYTE		lpExtTextData;
		LPBYTE		lpAppExtTextData;
		LPBYTE		lpDFontInfo;
		LPBYTE		lpXForm;
		LPBYTE		lpDrawMode;
		int		cbRFsize;
		WORD		wStackSeg;

		if ( !lpInData  ||  !lpOutData ) {
			envp_global->reg.ax = 0;
			break;
		}
		cbRFsize = SIZEOF_DFONTINFO_BIN +
                                        ((RFONT*)lpetd->lpFont)->iBinSize;

		/* Create separate frame for every structure */
		lpExtTextData     = lpStruct;
		lpStruct         += SIZEOF_EXTTEXTDATA_BIN;
		lpAppExtTextData  = lpStruct;
		lpStruct         += SIZEOF_APPEXTTEXTDATA_BIN;
		lpDFontInfo       = lpStruct;
		lpStruct         += cbRFsize;
		lpXForm           = lpStruct;
		lpStruct         += SIZEOF_TEXTXFORM_BIN;
		lpDrawMode        = lpStruct;
		lpStruct         += SIZEOF_DRAWMODE_BIN;
		/* lpStruct points to output binary ETM structure */

		wStackSeg = (WORD)envp_global->reg.ss;

		/* Prepare EXTTEXTDATA */
		PutEXTTEXTDATA(lpExtTextData, wStackSeg, (DWORD)ss, cbRFsize);

		/* Prepare APPEXTTEXTDATA (PutAPPEXTTEXTDATA() ?) */
		PUTWORD(lpAppExtTextData, (WORD)lpetd->lpInData->x);

		/* Prepare realized font */
		PutRFONT(lpDFontInfo, (RFONT*)lpetd->lpFont);

		/* Prepare TEXTXFORM */
		PutTEXTXFORM(lpXForm, lpetd->lpXForm);

		/* Prepare DRAWMODE */
		PutDRAWMODE(lpDrawMode, lpetd->lpDrawMode);

		/* Put address of input structure onto stack */
		PUTWORD(lpSP+6, wStackSeg);
		PUTWORD(lpSP+4, (WORD)((DWORD)lpExtTextData - (DWORD)ss));

		/* Put address of output structure onto stack */
		PUTWORD(lpSP+2, wStackSeg);
		PUTWORD(lpSP,   (WORD)((DWORD)lpStruct - (DWORD)ss));
		PUTWORD(lpStruct, SIZEOF_ETM_BIN);

		invoke_binary();

		GetETM((EXTTEXTMETRIC *)lpOutData, lpStruct);
		break;
	}

	default:
	    LOGSTR((LF_PRINTER,"CONTROL: UNIMP escape %d (%#x)\n",wFunction));
	    LOGSTR((LF_STDERR,"CONTROL: UNIMP escape %d (%#x)\n",wFunction));
	    if (wSel)
		FreeSelector(wSel);
	    return 0;
    }
    if (wSel)
	FreeSelector(wSel);
    if (wSel1)
	FreeSelector(wSel1);
    if (wSel2)
	FreeSelector(wSel2);
    if (wSel3)
	FreeSelector(wSel3);
    envp_global->reg.sp += STK_DATA_512;
    return LOWORD(envp_global->reg.ax);
}


DWORD
cnb_ExtTextOut(LPPDEVICE lpDestDev, WORD wDestXOrg, WORD wDestYOrg,
		LPRECT lpClipRect, LPSTR lpString, int wCount,
		LPDFONTINFO lpFontInfo, LPDRAWMODE lpDrawMode,
		TEXTXFORM *lpTextXForm, short int *lpCharWidths,
		LPRECT lpOpaqueRect, WORD wOptions)
{
    LPBYTE	lpSP;
    LPBYTE	lpStruct,ss;
    LPBYTE	lpOutBin=NULL;
    WORD	wSel=0,wSel1=0,wSel2=0,wSel3=0;

    LOGSTR((LF_INTERFACE,"cnb_ExtTextOut: calling ExtTextOut\n"));

    envp_global->reg.ax = envp_global->reg.dx = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_EXTTEXTOUT;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = (LPBYTE)GetPhysicalAddress(envp_global->reg.ss);

    if (lpDestDev) {
	wSel = ASSIGNSEL(lpDestDev, DEVICEsize);
	PUTDWORD(lpSP+36,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+36,0L);

    PUTWORD(lpSP+34,wDestXOrg);
    PUTWORD(lpSP+32,wDestYOrg);

    /* convert the clipping rect */
    if (lpClipRect) {
        PUTWORD(lpStruct,(WORD)lpClipRect->left);
        PUTWORD(lpStruct+2,(WORD)lpClipRect->top);
        PUTWORD(lpStruct+4,(WORD)lpClipRect->right);
        PUTWORD(lpStruct+6,(WORD)lpClipRect->bottom);
        PUTWORD(lpSP+30,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+28,(WORD)((DWORD)lpStruct - (DWORD)ss));
	lpStruct += 8;
    }
    else
        PUTDWORD(lpSP+28,0L);

    /* convert text string */
    if (lpString) {
	wSel1 = ASSIGNSEL(lpString, strlen(lpString)+1);
	PUTDWORD(lpSP+24,MAKELP(wSel1,0));
    }
    else
	PUTDWORD(lpSP+24,0L);

    PUTWORD(lpSP+22,(WORD)wCount);

    /* pass a pointer to DFONTINFO struct */
    if (lpFontInfo) {
	/* Should be in separate segment */
	int size = SIZEOF_DFONTINFO_BIN + ((RFONT*)lpFontInfo)->iBinSize;

	if ( !(lpOutBin = (LPBYTE)WinMalloc(size)) ) {
		envp_global->reg.sp += STK_DATA_512 + STK_EXTTEXTOUT;
		return 0;
	}
	PutRFONT(lpOutBin, (RFONT*)lpFontInfo);
	wSel2 = ASSIGNSEL(lpOutBin, size);
	PUTDWORD(lpSP+18,MAKELP(wSel2,0));
    }
    else
	PUTDWORD(lpSP+18,0L);

    /* convert the DRAWMODE struct */
    if (lpDrawMode) {
	PutDRAWMODE(lpStruct,lpDrawMode);
        PUTWORD(lpSP+16,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+14,(WORD)((DWORD)lpStruct - (DWORD)ss));
        lpStruct += 32;
    }
    else
        PUTDWORD(lpSP+14,0L);

    /* convert the TEXTXFORM struct */
    if (lpTextXForm) {
	PutTEXTXFORM(lpStruct,lpTextXForm);
        PUTWORD(lpSP+12,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+10,(WORD)((DWORD)lpStruct - (DWORD)ss));
        lpStruct += 24;
    }
    else
        PUTDWORD(lpSP+10,0L);
 
    /* convert array of char widths  */
    if (lpCharWidths && wCount) {
	wSel3 = ASSIGNSEL(hsw_ConvertArrayToWord((LPBYTE)lpCharWidths,
					abs(wCount)), abs(wCount) * WORD_86);
	PUTDWORD(lpSP+6,MAKELP(wSel3,0));
    }
    else
        PUTDWORD(lpSP+6,0L);

    /* convert the opaquing rectangle */
    if (lpOpaqueRect) {
        PUTWORD(lpStruct,(WORD)lpOpaqueRect->left);
        PUTWORD(lpStruct+2,(WORD)lpOpaqueRect->top);
        PUTWORD(lpStruct+4,(WORD)lpOpaqueRect->right);
        PUTWORD(lpStruct+6,(WORD)lpOpaqueRect->bottom);
        PUTWORD(lpSP+4,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+2,(WORD)((DWORD)lpStruct - (DWORD)ss));
	lpStruct += 8;
    }
    else
        PUTDWORD(lpSP+2,0L);

    PUTWORD(lpSP,wOptions);

    invoke_binary();

    if (wSel)
        FreeSelector(wSel);
    if (wSel1)
        FreeSelector(wSel1);
    if (wSel2)
        FreeSelector(wSel2);
    if (wSel3)
        FreeSelector(wSel3);
    if ( lpOutBin )
	WinFree((LPSTR)lpOutBin);

    envp_global->reg.sp += STK_DATA_512;
    return MAKELONG(LOWORD(envp_global->reg.ax),
		    LOWORD(envp_global->reg.dx));
}


WORD
cnb_GetCharWidth(LPPDEVICE lpDestDev, LPWORD lpBuffer,
		WORD wFirstChar, WORD wLastChar, 
		LPDFONTINFO lpFontInfo, LPDRAWMODE lpDrawMode,
		TEXTXFORM *lpTextXForm)
{
    LPBYTE	lpSP;
    LPBYTE	lpStruct,ss;
    LPBYTE	lpOutBin = (LPBYTE)0;
    int		nCount;
    WORD	wSel=0,wSel1=0,wSel2=0;

    LOGSTR((LF_INTERFACE,"cnb_GetCharWidth: calling GetCharWidth\n"));
    envp_global->reg.ax = 0;
    envp_global->reg.sp -= STK_DATA_576;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_GETCHARWID;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = (LPBYTE)GetPhysicalAddress(envp_global->reg.ss);

    if (lpDestDev) {
	wSel = ASSIGNSEL(lpDestDev, DEVICEsize);
	PUTDWORD(lpSP+20,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+20,0L);

    PUTWORD(lpSP+14,wFirstChar);
    PUTWORD(lpSP+12,wLastChar);

    /* convert the DFONTINFO struct */
    if (lpFontInfo) {
	/* Should be in separate segment */
	int size = SIZEOF_DFONTINFO_BIN + ((RFONT*)lpFontInfo)->iBinSize;

	if ( !(lpOutBin = (LPBYTE)WinMalloc(size)) ) {
		envp_global->reg.sp += STK_DATA_576 + STK_GETCHARWID;
		return 0;
	}
	PutRFONT(lpOutBin, (RFONT*)lpFontInfo);
	wSel1 = ASSIGNSEL(lpOutBin, size);
	PUTDWORD(lpSP+8,MAKELP(wSel1,0));
    }
    else
	PUTDWORD(lpSP+8,0L);

    /* convert the DRAWMODE struct */
    if (lpDrawMode) {
	PutDRAWMODE(lpStruct,lpDrawMode);
        PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
        lpStruct += 32;
    }
    else
        PUTDWORD(lpSP+4,0L);

    /* convert the TEXTXFORM struct */
    if (lpTextXForm) {
	PutTEXTXFORM(lpStruct,lpTextXForm);
        PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
        lpStruct += 24;
    }
    else
        PUTDWORD(lpSP,0L);

    /* determine the number of chars in given range */
    if ( (nCount = wLastChar - wFirstChar + 1) < 0 )
	nCount = 0;

    if (lpBuffer && nCount) {
        PUTWORD(lpSP+18,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+16,(WORD)((DWORD)lpStruct - (DWORD)ss));
    }
    else
	PUTDWORD(lpSP+16,0L);

    if (nCount) {				/* sanity check */
	invoke_binary();
	if (LOWORD(envp_global->reg.ax)) 	/* function was successful */
	    memcpy(lpBuffer,(LPBYTE)hsw_ConvertArrayToInt(lpStruct,nCount),
			sizeof(int)*nCount);
    }

    if (wSel)
        FreeSelector(wSel);
    if (wSel1)
        FreeSelector(wSel1);
    if (wSel2)
        FreeSelector(wSel2);
    if ( lpOutBin )
	WinFree((LPSTR)lpOutBin);

    envp_global->reg.sp += STK_DATA_576;
    return LOWORD(envp_global->reg.ax);
}

DWORD
cnb_DeviceCapabilities(LPSTR lpDeviceName, LPSTR lpPort, WORD wIndex,
		LPSTR lpOutput, LPDEVMODE lpDevMode)
{
    LPBYTE lpSP;
    LPBYTE lpStruct,ss;
    DWORD retcode;
    int i;
    WORD wSel=0,wSel1=0,wSel2=0;
    LPBYTE lpCaps=NULL;
    static int nNumPapers = 0;
    static int nNumBins = 0;
    static int nNumRes = 0;

    LOGSTR((LF_INTERFACE,
	"cnb_DeviceCapabilities: calling DeviceCapabilities\n"));
    envp_global->reg.ax = envp_global->reg.dx = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_DEVICECAPS;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = (LPBYTE)GetPhysicalAddress(envp_global->reg.ss);

    if (lpDeviceName) {
	wSel = ASSIGNSEL(lpDeviceName, strlen(lpDeviceName)+1);
	PUTDWORD(lpSP+14,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+14,0L);

    if (lpPort) {
	wSel1 = ASSIGNSEL(lpPort, strlen(lpPort)+1);
	PUTDWORD(lpSP+10,MAKELP(wSel1,0));
    }
    else
	PUTDWORD(lpSP+10,0L);

    PUTWORD(lpSP+8,wIndex);

    if (lpDevMode) {
	PutDEVMODE(lpStruct,lpDevMode);
	PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
	PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
    }
    else
	PUTDWORD(lpSP,0L);

    PUTDWORD(lpSP+4,0L);	/* default; overwritten when needed */

    switch (wIndex) {
	case DC_MAXEXTENT:
	case DC_MINEXTENT:	/* ??? -- not referred to in DDK source */

	case DC_PAPERSIZE:
	    printf("ALARM: unimp interface to func %x in DeviceCapabilities!\n",
				wIndex);
	    retcode = (DWORD)-1;
	    break;

	case DC_ENUMRESOLUTIONS:
	    if (nNumRes && lpOutput) {
		lpCaps = (LPBYTE)WinMalloc(4*nNumRes);
		wSel2 = ASSIGNSEL(lpCaps, 4 * nNumRes);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    invoke_binary();
	    retcode = MAKELONG(LOWORD(envp_global->reg.ax),
		    LOWORD(envp_global->reg.dx));
	    if (retcode != (DWORD)-1) {
		if (!lpOutput)
		    nNumRes = (int)retcode;
		else if (nNumRes) {
		    for (i=0; i<nNumRes; i++)
			*(LPDWORD)(lpOutput+4*i) = GETDWORD(lpCaps+4*i);
		    WinFree((LPSTR)lpCaps);
		}
	    }
	    break;

	case DC_PAPERS:
	    if (nNumPapers && lpOutput) {
		lpCaps = (LPBYTE)WinMalloc(2*nNumPapers);
		wSel2 = ASSIGNSEL(lpCaps, 2 * nNumPapers);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    invoke_binary();
	    retcode = MAKELONG(LOWORD(envp_global->reg.ax),
		    LOWORD(envp_global->reg.dx));
	    if (retcode != (DWORD)-1) {
		if (!lpOutput)
		    nNumPapers = (int)retcode;
		else if (nNumPapers) {
		    memcpy(lpOutput,(LPSTR)hsw_ConvertWordArray
				(lpCaps,nNumPapers),sizeof(WORD)*nNumPapers);
		    WinFree((LPSTR)lpCaps);
		}
	    }
	    break;

	case DC_BINS:
	    if (nNumBins && lpOutput) {
		lpCaps = (LPBYTE)WinMalloc(2*nNumBins);
		wSel2 = ASSIGNSEL(lpCaps, 2*nNumBins);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
	    invoke_binary();
	    retcode = MAKELONG(LOWORD(envp_global->reg.ax),
		    LOWORD(envp_global->reg.dx));
	    if (retcode != (DWORD)-1) {
		if (!lpOutput)
		    nNumBins = (int)retcode;
		else if (nNumBins) {
		    memcpy(lpOutput,(LPSTR)hsw_ConvertWordArray
				(lpCaps,nNumBins),sizeof(WORD)*nNumBins);
		    WinFree((LPSTR)lpCaps);
		}
	    }
	    break;

	case DC_BINNAMES:
	case DC_FILEDEPENDENCIES:
	case DC_PAPERNAMES:
	    if (lpOutput) {
		wSel2 = ASSIGNSEL(lpOutput, STK_DATA_256);
		PUTDWORD(lpSP+4,MAKELP(wSel2,0));
	    }
		/* fall through */
	default:
	    invoke_binary();
	    retcode = MAKELONG(LOWORD(envp_global->reg.ax),
		    LOWORD(envp_global->reg.dx));
	    break;
    }

    if (wSel)
        FreeSelector(wSel);
    if (wSel1)
        FreeSelector(wSel1);
    if (wSel2)
        FreeSelector(wSel2);
    envp_global->reg.sp += STK_DATA_512;
    return retcode;
}

int
cnb_StretchDIBits(LPPDEVICE lpPDevice, WORD fGet, 
			WORD DestX, WORD DestY, WORD DestXE, WORD DestYE,
			WORD SrcX,  WORD SrcY,  WORD SrcXE,  WORD SrcYE,
			LPSTR lpBits, LPBITMAPINFO lpBitmapInfo,
			LPINT lpTranslate, DWORD dwROP, LPBYTE lpPBrush, 
			LPDRAWMODE lpDrawMode, LPRECT lpClipRect)
{
    LPBYTE lpSP;
    LPBYTE lpStruct,ss;
    LPBYTE lpBinBMInfo = 0;
    WORD wSel=0,wSel1=0,wSel2=0,wSel3=0;
    int nNumSel = 0;
    WORD wIndex;
    DWORD dwSelSize;
    int i;

    LOGSTR((LF_INTERFACE,"cnb_StretchDIBits\n"));
    envp_global->reg.ax = envp_global->reg.dx = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_STRETCHDIB;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = (LPBYTE)GetPhysicalAddress(envp_global->reg.ss);

    if (lpPDevice) {
	wSel = ASSIGNSEL(lpPDevice, DEVICEsize);
	PUTDWORD(lpSP+46,MAKELP(wSel,0));
    }
    else
	PUTDWORD(lpSP+46,0L);

    PUTWORD(lpSP+44,fGet);
    PUTWORD(lpSP+42,DestX);
    PUTWORD(lpSP+40,DestY);
    PUTWORD(lpSP+38,DestXE);
    PUTWORD(lpSP+36,DestYE);
    PUTWORD(lpSP+34,SrcX);
    PUTWORD(lpSP+32,SrcY);
    PUTWORD(lpSP+30,SrcXE);
    PUTWORD(lpSP+28,SrcYE);

    if (lpBits) {
	if (lpBitmapInfo->bmiHeader.biSizeImage <= 0xffff) {
	    wSel1 = ASSIGNSEL(lpBits, lpBitmapInfo->bmiHeader.biSizeImage);
	}
	else { /* huge bits */
	    nNumSel = (lpBitmapInfo->bmiHeader.biSizeImage + 0xffff) / 0x10000;
	    wIndex = AssignSelRange(nNumSel);
	    for (i = 0, dwSelSize = lpBitmapInfo->bmiHeader.biSizeImage;
		 i < nNumSel; dwSelSize -= 0x10000, i++) {
		wSel1 = (wIndex + i) << 3;
		AssignSelector((LPBYTE)(lpBits + (i * 0x10000)), wSel1,
				TRANSFER_DATA,dwSelSize);
	    }
	    wSel1 = wIndex << 3 | 7;
	}
	PUTDWORD(lpSP+24,MAKELP(wSel1,0));
    }
    else
	PUTDWORD(lpSP+24,0L);

    if (lpBitmapInfo) {
	int size = sizeof(BITMAPINFOHEADER);

	if ( lpBitmapInfo->bmiHeader.biBitCount == 1 )
	    size += 2 * sizeof(RGBQUAD);
	else if ( lpBitmapInfo->bmiHeader.biBitCount == 4 )
	    size += 16 * sizeof(RGBQUAD);
	else if ( lpBitmapInfo->bmiHeader.biBitCount == 8 )
	    size += 256 * sizeof(RGBQUAD);

	lpBinBMInfo = PutBITMAPINFO(lpBitmapInfo);
	wSel2 = ASSIGNSEL(lpBinBMInfo, size);
	PUTDWORD(lpSP+20,MAKELP(wSel2,0));
    }
    else
	PUTDWORD(lpSP+20,0L);

#ifdef LATER
	convert INT array of palette indices
#else
    PUTDWORD(lpSP+16,0L);
#endif

    PUTDWORD(lpSP+12,dwROP);

    if (lpPBrush) {
	wSel3 = ASSIGNSEL(lpPBrush, STK_DATA_256);
	PUTDWORD(lpSP+8,MAKELP(wSel3,0));
    }
    else
	PUTDWORD(lpSP+8,0L);

    /* convert the DRAWMODE struct */
    if (lpDrawMode) {
	PutDRAWMODE(lpStruct,lpDrawMode);
        PUTWORD(lpSP+6,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP+4,(WORD)((DWORD)lpStruct - (DWORD)ss));
        lpStruct += 32;
    }
    else
        PUTDWORD(lpSP+4,0L);

    if (lpClipRect) {
        PUTWORD(lpStruct,(WORD)lpClipRect->left);
        PUTWORD(lpStruct+2,(WORD)lpClipRect->top);
        PUTWORD(lpStruct+4,(WORD)lpClipRect->right);
        PUTWORD(lpStruct+6,(WORD)lpClipRect->bottom);
        PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
        PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
	lpStruct += 8;
    }
    else
        PUTDWORD(lpSP,0L);

    invoke_binary();

    if (wSel)
        FreeSelector(wSel);
    if (wSel1)
        FreeSelector(wSel1);
    for (i = 1; i < nNumSel; i++)
	FreeSelector(wSel1+8*i);
    if (wSel2)
        FreeSelector(wSel2);
    if (wSel3)
        FreeSelector(wSel3);
    if (lpBinBMInfo)
	WinFree((LPSTR)lpBinBMInfo);
    envp_global->reg.sp += STK_DATA_512;
    return (int)(short)(LOWORD(envp_global->reg.ax));
}

int
cnb_BitBlt(LPPDEVICE lpDestDev, WORD wDestX, WORD wDestY,
		LPPDEVICE lpSrcDev, WORD wSrcX, WORD wSrcY,
		WORD wXext, WORD wYext, DWORD dwRop3, 
		LPVOID lpPhBrush, LPDRAWMODE lpDrawMode)
{
    LPBYTE	lpSP, lpStruct, ss, lpData;
    WORD	wSelDest = 0, wSelSrc = 0, wSelBr = 0;
    BYTE	bufSrc[64], bufDest[64];
    LPPSBITMAP lpPSBitmap;
    int nByteCount;
    WORD wSelSrcBits = 0, wSelSrcBmp = 0, wSelDestBits = 0, wSelDestBmp = 0;

    LOGSTR((LF_INTERFACE,"cnb_BitBlt: calling BitBlt\n"));
    envp_global->reg.ax = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_BITBLT;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = GetPhysicalAddress(envp_global->reg.ss);

    if (lpDestDev) {
	if (lpDestDev->pdType) {		/* device DC */
	    wSelDest = ASSIGNSEL(lpDestDev, DEVICEsize);
	    PUTDWORD(lpSP+28, MAKELP(wSelDest,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpDestDev;
	    lpData = bufDest;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelDestBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelDestBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_BitBlt: huge dest bitmap");
	    wSelDestBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+28, MAKELP(wSelDestBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+28, 0L);

    PUTWORD(lpSP+26, wDestX);
    PUTWORD(lpSP+24, wDestY);

    if (lpSrcDev) {
	if (lpSrcDev->pdType) {		/* device DC */
	    wSelSrc = ASSIGNSEL(lpSrcDev, DEVICEsize);
	    PUTDWORD(lpSP+20, MAKELP(wSelSrc,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpSrcDev;
	    lpData = bufSrc;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelSrcBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelSrcBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_BitBlt: huge source bitmap");
	    wSelSrcBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+20, MAKELP(wSelSrcBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+20, 0L);

    PUTWORD(lpSP+18, wSrcX);
    PUTWORD(lpSP+16, wSrcY);
    PUTWORD(lpSP+14, wXext);
    PUTWORD(lpSP+12, wYext);
    PUTDWORD(lpSP+8, dwRop3);

    if (lpPhBrush) {
        wSelBr = ASSIGNSEL(lpPhBrush, STK_DATA_256);
        PUTDWORD(lpSP+4, MAKELP(wSelBr,0));
    }
    else
        PUTDWORD(lpSP+4, 0L);

    if (lpDrawMode) {
	PutDRAWMODE(lpStruct, lpDrawMode);
        PUTWORD(lpSP+2, (WORD)envp_global->reg.ss);
        PUTWORD(lpSP  , (WORD)((DWORD)lpStruct - (DWORD)ss));
    }
    else
        PUTDWORD(lpSP, 0L);

    invoke_binary();

    if (wSelDest)
        FreeSelector(wSelDest);
    if (wSelSrc)
        FreeSelector(wSelSrc);
    if (wSelBr)
        FreeSelector(wSelBr);
    if (wSelSrcBits)
        FreeSelector(wSelSrcBits);
    if (wSelSrcBmp)
        FreeSelector(wSelSrcBmp);
    if (wSelDestBits)
        FreeSelector(wSelDestBits);
    if (wSelDestBmp)
        FreeSelector(wSelDestBmp);

    envp_global->reg.sp += STK_DATA_512;
    return (int)(short)(LOWORD(envp_global->reg.ax));
}

int
cnb_StretchBlt(LPPDEVICE lpDestDev, WORD wDestX, WORD wDestY,
		WORD wDestXext, WORD wDestYext,
		LPPDEVICE lpSrcDev, WORD wSrcX, WORD wSrcY,
		WORD wSrcXext, WORD wSrcYext, DWORD dwRop3, 
		LPVOID lpPhBrush, LPDRAWMODE lpDrawMode, LPRECT lpClipRect)
{
    LPBYTE	lpSP, lpStruct, ss, lpData;
    WORD	wSelDest = 0, wSelSrc = 0, wSelBr = 0;
    WORD	wSelSrcBits = 0, wSelSrcBmp = 0;
    WORD	wSelDestBits = 0, wSelDestBmp = 0;
    LPPSBITMAP  lpPSBitmap;
    BYTE	bufSrc[64];
    BYTE	bufDest[64];
    int		nByteCount;

    LOGSTR((LF_INTERFACE,"cnb_StretchBlt: calling StretchBlt\n"));
    envp_global->reg.ax = 0;
    envp_global->reg.sp -= STK_DATA_512;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.sp -= STK_STRETCHBLT;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = GetPhysicalAddress(envp_global->reg.ss);

    if (lpDestDev) {
	if (lpDestDev->pdType) {		/* device DC */
	    wSelDest = ASSIGNSEL(lpDestDev, DEVICEsize);
	    PUTDWORD(lpSP+36, MAKELP(wSelDest,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpDestDev;
	    lpData = bufDest;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelDestBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelDestBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_StretchBlt: huge dest bitmap");
	    wSelDestBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+36, MAKELP(wSelDestBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+36, 0L);

    PUTWORD(lpSP+34, wDestX);
    PUTWORD(lpSP+32, wDestY);
    PUTWORD(lpSP+30, wDestXext);
    PUTWORD(lpSP+28, wDestYext);

    if (lpSrcDev) {
	if (lpSrcDev->pdType) {		/* device DC */
	    wSelSrc = ASSIGNSEL(lpSrcDev, DEVICEsize);
	    PUTDWORD(lpSP+24, MAKELP(wSelSrc,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpSrcDev;
	    lpData = bufSrc;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelSrcBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelSrcBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_StretchBlt: huge source bitmap");
	    wSelSrcBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+24, MAKELP(wSelSrcBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+24, 0L);

    PUTWORD(lpSP+22, wSrcX);
    PUTWORD(lpSP+20, wSrcY);
    PUTWORD(lpSP+18, wSrcXext);
    PUTWORD(lpSP+16, wSrcYext);
    PUTDWORD(lpSP+12, dwRop3);

    if (lpPhBrush) {
        wSelBr = ASSIGNSEL(lpPhBrush, STK_DATA_256);
        PUTDWORD(lpSP+8, MAKELP(wSelBr,0));
    }
    else
        PUTDWORD(lpSP+8, 0L);

    if (lpDrawMode) {
	PutDRAWMODE(lpStruct, lpDrawMode);
        PUTWORD(lpSP+6, (WORD)envp_global->reg.ss);
        PUTWORD(lpSP+4, (WORD)((DWORD)lpStruct - (DWORD)ss));
	lpStruct += 60;   /* ??? */
    }
    else
        PUTDWORD(lpSP+4, 0L);

    if ( lpClipRect ) {
        PUTWORD(lpStruct,   (WORD)lpClipRect->left);
        PUTWORD(lpStruct+2, (WORD)lpClipRect->top);
        PUTWORD(lpStruct+4, (WORD)lpClipRect->right);
        PUTWORD(lpStruct+6, (WORD)lpClipRect->bottom);
        PUTWORD(lpSP+2, (WORD)envp_global->reg.ss);
        PUTWORD(lpSP  , (WORD)((DWORD)lpStruct - (DWORD)ss));
    }
    else
	PUTDWORD(lpSP, 0L);

    invoke_binary();
    if (wSelDest)
        FreeSelector(wSelDest);
    if (wSelSrc)
        FreeSelector(wSelSrc);
    if (wSelBr)
        FreeSelector(wSelBr);
    if (wSelSrcBits)
        FreeSelector(wSelSrcBits);
    if (wSelSrcBmp)
        FreeSelector(wSelSrcBmp);
    if (wSelDestBits)
        FreeSelector(wSelDestBits);
    if (wSelDestBmp)
        FreeSelector(wSelDestBmp);

    envp_global->reg.sp += STK_DATA_512;
    return (int)(short)(LOWORD(envp_global->reg.ax));
}

COLORREF
cnb_ColorInfo(LPPDEVICE lpDestDev, DWORD dwRGB, LPVOID lpPhColor)
{
    LPBYTE	lpSP,lpData;
    WORD	wSelDest = 0, wSelCol = 0, wSelDestBits = 0, wSelDestBmp = 0;
    BOOL	retcode;
    int		nByteCount;
    BYTE	bufDest[64];
    LPPSBITMAP lpPSBitmap;

    LOGSTR((LF_INTERFACE,"cnb_ColorInfo: calling ColorInfo\n"));
    envp_global->reg.ax = 0;
    envp_global->reg.sp -= STK_COLORINFO;
    lpSP = (LPBYTE)envp_global->reg.sp;

    if (lpDestDev) {
	if (lpDestDev->pdType) {		/* device DC */
	    wSelDest = ASSIGNSEL(lpDestDev, DEVICEsize);
	    PUTDWORD(lpSP+8, MAKELP(wSelDest,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpDestDev;
	    lpData = bufDest;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelDestBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelDestBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_ColorInfo: huge dest bitmap");
	    wSelDestBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+8, MAKELP(wSelDestBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+8, 0L);

    PUTDWORD(lpSP+4, dwRGB);

    if (lpPhColor) {
        wSelCol = ASSIGNSEL(lpPhColor, STK_DATA_256);
        PUTDWORD(lpSP, MAKELP(wSelCol,0));
    }
    else
        PUTDWORD(lpSP, 0L);

    invoke_binary();

    if (wSelDest)
        FreeSelector(wSelDest);
    if (wSelCol)
        FreeSelector(wSelCol);
    if (wSelDestBits)
        FreeSelector(wSelDestBits);
    if (wSelDestBmp)
        FreeSelector(wSelDestBmp);

    retcode = (COLORREF)MAKELONG(envp_global->reg.ax,envp_global->reg.dx);

    return retcode;
}

COLORREF
cnb_Pixel(LPPDEVICE lpDestDev, int X, int Y, COLORREF dwPhysColor,
	LPDRAWMODE lpDrawMode)
{
    LPBYTE	lpSP,lpData,lpStruct,ss;
    WORD	wSelDest = 0, wSelDestBits = 0, wSelDestBmp = 0;
    BOOL	retcode;
    int		nByteCount;
    BYTE	bufDest[64];
    LPPSBITMAP lpPSBitmap;

    LOGSTR((LF_INTERFACE,"cnb_Pixel: calling Pixel\n"));

    envp_global->reg.sp -= STK_DATA_256;
    lpStruct = (LPBYTE)envp_global->reg.sp;
    envp_global->reg.ax = 0;
    envp_global->reg.sp -= STK_PIXEL;
    lpSP = (LPBYTE)envp_global->reg.sp;
    ss = (LPBYTE)GetPhysicalAddress(envp_global->reg.ss);

    if (lpDestDev) {
	if (lpDestDev->pdType) {		/* device DC */
	    wSelDest = ASSIGNSEL(lpDestDev, DEVICEsize);
	    PUTDWORD(lpSP+12, MAKELP(wSelDest,0));
	}
	else {				/* memory DC */
	    lpPSBitmap = (LPPSBITMAP)lpDestDev;
	    lpData = bufDest;
	    PutPSBITMAP(lpData,lpPSBitmap);
	    if (lpPSBitmap->bm.bmBits) {
		nByteCount = 
			lpPSBitmap->bm.bmWidthBytes*lpPSBitmap->bm.bmHeight;
		wSelDestBits = ASSIGNSEL(lpPSBitmap->bm.bmBits,nByteCount);
		PUTDWORD(lpData+10, MAKELP(wSelDestBits,0));
	    }
	    else
		PUTDWORD(lpData+10,0L);
	    if (nByteCount > 65535)
		FatalAppExit(0,"cnb_ColorInfo: huge dest bitmap");
	    wSelDestBmp = ASSIGNSEL(lpData,32);
	    PUTDWORD(lpSP+12, MAKELP(wSelDestBmp,0));
	}
    }
    else
        PUTDWORD(lpSP+12, 0L);

    PUTWORD(lpSP+10, (WORD)X);
    PUTWORD(lpSP+8, (WORD)Y);
    PUTDWORD(lpSP+4, dwPhysColor);

    invoke_binary();

    /* convert the DRAWMODE struct */
    if (lpDrawMode) {
	PutDRAWMODE(lpStruct,lpDrawMode);
	PUTWORD(lpSP+2,(WORD)envp_global->reg.ss);
	PUTWORD(lpSP,(WORD)((DWORD)lpStruct - (DWORD)ss));
    }
    else
	PUTDWORD(lpSP,0L);

    if (wSelDest)
        FreeSelector(wSelDest);
    if (wSelDestBits)
        FreeSelector(wSelDestBits);
    if (wSelDestBmp)
        FreeSelector(wSelDestBmp);

    retcode = (COLORREF)MAKELONG(envp_global->reg.ax,envp_global->reg.dx);

    envp_global->reg.sp += STK_DATA_256;

    return retcode;
}

/*********************************************************************/

BOOL
cnb_CTL3DV2_Ctl3dRegister(HINSTANCE hInstance)
{
    envp_global->reg.sp -= HANDLE_86;
    PUTWORD(envp_global->reg.sp,GetDataSelectorFromInstance(hInstance));
    invoke_binary();
    return (BOOL)envp_global->reg.ax;
}

BOOL
cnb_CTL3DV2_Ctl3dSubclassCtl(HWND hWnd)
{
    envp_global->reg.sp -= HANDLE_86;
    PUTWORD(envp_global->reg.sp,hWnd);
    invoke_binary();
    return (BOOL)envp_global->reg.ax;
}

BOOL
cnb_CTL3DV2_Ctl3dSubclassDlg(HWND hWnd, WORD wFlags)
{
    envp_global->reg.sp -= HANDLE_86 + WORD_86;
    PUTWORD(envp_global->reg.sp,wFlags);
    PUTWORD(envp_global->reg.sp+2,hWnd);
    invoke_binary();
    return (BOOL)envp_global->reg.ax;
}

BOOL
cnb_CTL3DV2_Ctl3dSubclassDlgEx(HWND hWnd, DWORD dwFlags)
{
    envp_global->reg.sp -= HANDLE_86 + DWORD_86;
    PUTDWORD(envp_global->reg.sp,dwFlags);
    PUTWORD(envp_global->reg.sp+4,hWnd);
    invoke_binary();
    return (BOOL)envp_global->reg.ax;
}

HBRUSH
cnb_CTL3DV2_Ctl3dCtlColor(HDC hDC, LONG l)
{
    envp_global->reg.sp -= HANDLE_86 + DWORD_86;
    PUTDWORD(envp_global->reg.sp,l);
    PUTWORD(envp_global->reg.sp+4,hDC);
    invoke_binary();
    return (HBRUSH)envp_global->reg.ax;
}

HBRUSH
cnb_CTL3DV2_Ctl3dCtlColorEx(UINT wm, WPARAM wParam, LPARAM lParam)
{
    envp_global->reg.sp -= UINT_86 + WORD_86 + DWORD_86;
    PUTDWORD(envp_global->reg.sp,lParam);
    PUTWORD(envp_global->reg.sp+4,wParam);
    PUTWORD(envp_global->reg.sp+6,wm);
    invoke_binary();
    return (HBRUSH)envp_global->reg.ax;
}

LONG
cnb_CTL3DV2_Ctl3dDlgFramePaint(HWND hWnd, UINT wm, WPARAM wParam, LPARAM lParam)
{
    return hsw_common_nat_to_bin(hWnd, wm, wParam, lParam);
}

BOOL
cnb_CTL3DV2_Ctl3dColorChange(void)
{
    invoke_binary();
    return (BOOL)envp_global->reg.ax;
}

void
cnb_CTL3DV2_Stub(void)
{
    MessageBox((HWND)NULL,"cnb_CTL3DV2_Stub called...",
		"Unimplemented interface",MB_OK|MB_ICONEXCLAMATION);
    FatalExit(0);
}
===============================================================
OEMTab.c
===============================================================

/* "@(#)OEMTab.c	2.4 :/users/sccs/src/win/s.OEMTab.c 10/12/95 18:02:11"  */

#include "windows.h"
#include "print.h"

#include "Endian.h"
#include "Log.h"
#include "OEM.h"

extern long int cnb_AdvancedSetupDialog();
extern long int cnb_Control();
extern long int cnb_ColorInfo();
extern long int cnb_Enable();
extern long int cnb_EnumDFonts();
extern long int cnb_EnumObj();
extern long int cnb_Output();
extern long int cnb_RealizeObject();
extern long int cnb_DeviceMode();
extern long int cnb_ExtTextOut();
extern long int cnb_GetCharWidth();
extern long int cnb_ExtDeviceMode();
extern long int cnb_DeviceCapabilities();
extern long int cnb_StretchDIBits();
extern long int cnb_BitBlt();
extern long int cnb_StretchBlt();
extern long int cnb_Pixel();

static HSMT_OEMENTRYTAB OEM_tab_PRINTER[] =
{	/* 000 */	{ "", 0 },
	/* 001 */	{ "BITBLT", cnb_BitBlt },
	/* 002 */	{ "COLORINFO", cnb_ColorInfo },
	/* 003 */	{ "CONTROL", cnb_Control },
	/* 004 */	{ "DISABLE", 0 },
	/* 005 */	{ "ENABLE", cnb_Enable },
	/* 006 */	{ "ENUMDFONTS", cnb_EnumDFonts },
	/* 007 */	{ "ENUMOBJ", cnb_EnumObj },
	/* 008 */	{ "OUTPUT", cnb_Output },
	/* 009 */	{ "PIXEL", cnb_Pixel },
	/* 00a */	{ "REALIZEOBJECT", cnb_RealizeObject },
	/* 00b */	{ "STRBLT", 0 },
	/* 00c */	{ "SCANLR", 0 },
	/* 00d */	{ "DEVICEMODE", cnb_DeviceMode },
	/* 00e */	{ "EXTTEXTOUT", cnb_ExtTextOut },
	/* 00f */	{ "GETCHARWIDTH", cnb_GetCharWidth },
	/* 010 */	{ "DEVICEBITMAP", 0 },
	/* 011 */	{ "FASTBORDER", 0 },
	/* 012 */	{ "SETATTRIBUTE", 0 },
	/* 013 */	{ "", 0 },
	/* 014 */	{ "", 0 },
	/* 015 */	{ "DIBTODEVICE", 0 },
	/* 016 */	{ "", 0 },
	/* 017 */	{ "", 0 },
	/* 018 */	{ "", 0 },
	/* 019 */	{ "", 0 },
	/* 01a */	{ "", 0 },
	/* 01b */	{ "STRETCHBLT", cnb_StretchBlt },
	/* 01c */	{ "STRETCHDIB", cnb_StretchDIBits },
	/* 01d */	{ "", 0 },
	/* 01e */	{ "", 0 },
	/* 01f */	{ "", 0 },
	/* 020 */	{ "", 0 },
	/* 021 */	{ "", 0 },
	/* 022 */	{ "", 0 },
	/* 023 */	{ "", 0 },
	/* 024 */	{ "", 0 },
	/* 025 */	{ "", 0 },
	/* 026 */	{ "", 0 },
	/* 027 */	{ "", 0 },
	/* 028 */	{ "", 0 },
	/* 029 */	{ "", 0 },
	/* 02a */	{ "", 0 },
	/* 02b */	{ "", 0 },
	/* 02c */	{ "", 0 },
	/* 02d */	{ "", 0 },
	/* 02e */	{ "", 0 },
	/* 02f */	{ "", 0 },
	/* 030 */	{ "", 0 },
	/* 031 */	{ "", 0 },
	/* 032 */	{ "", 0 },
	/* 033 */	{ "", 0 },
	/* 034 */	{ "", 0 },
	/* 035 */	{ "", 0 },
	/* 036 */	{ "", 0 },
	/* 037 */	{ "", 0 },
	/* 038 */	{ "", 0 },
	/* 039 */	{ "", 0 },
	/* 03a */	{ "", 0 },
	/* 03b */	{ "", 0 },
	/* 03c */	{ "", 0 },
	/* 03d */	{ "", 0 },
	/* 03e */	{ "", 0 },
	/* 03f */	{ "", 0 },
	/* 040 */	{ "", 0 },
	/* 041 */	{ "", 0 },
	/* 042 */	{ "", 0 },
	/* 043 */	{ "", 0 },
	/* 044 */	{ "", 0 },
	/* 045 */	{ "", 0 },
	/* 046 */	{ "", 0 },
	/* 047 */	{ "", 0 },
	/* 048 */	{ "", 0 },
	/* 049 */	{ "", 0 },
	/* 04a */	{ "", 0 },
	/* 04b */	{ "", 0 },
	/* 04c */	{ "", 0 },
	/* 04d */	{ "", 0 },
	/* 04e */	{ "", 0 },
	/* 04f */	{ "", 0 },
	/* 050 */	{ "", 0 },
	/* 051 */	{ "", 0 },
	/* 052 */	{ "", 0 },
	/* 053 */	{ "", 0 },
	/* 054 */	{ "", 0 },
	/* 055 */	{ "", 0 },
	/* 056 */	{ "", 0 },
	/* 057 */	{ "", 0 },
	/* 058 */	{ "", 0 },
	/* 059 */	{ "", 0 },
	/* 05a */	{ "EXTDEVICEMODE", cnb_ExtDeviceMode },
	/* 05b */	{ "DEVICECAPABILITIES", cnb_DeviceCapabilities},
	/* 05c */	{ "", 0 },
	/* 05d */	{ "ADVANCEDSETUPDIALOG", cnb_AdvancedSetupDialog },
	/* 05e */	{ "", 0 },
	/* 05f */	{ "", 0 },
	/* end */	{ 0, 0 }
};

extern long int cnb_CTL3DV2_Stub();
extern long int cnb_CTL3DV2_Ctl3dRegister();
extern long int cnb_CTL3DV2_Ctl3dColorChange();
extern long int cnb_CTL3DV2_Ctl3dSubclassCtl();
extern long int cnb_CTL3DV2_Ctl3dSubclassDlg();
extern long int cnb_CTL3DV2_Ctl3dSubclassDlgEx();
extern long int cnb_CTL3DV2_Ctl3dCtlColor();
extern long int cnb_CTL3DV2_Ctl3dCtlColorEx();
extern long int cnb_CTL3DV2_Ctl3dDlgFramePaint();
#define	cnb_CTL3DV2_Ctl3dUnregister cnb_CTL3DV2_Ctl3dRegister
#define	cnb_CTL3DV2_Ctl3dAutoSubclass cnb_CTL3DV2_Ctl3dRegister
#define	cnb_CTL3DV2_Ctl3dWinIniChange cnb_CTL3DV2_Ctl3dColorChange
#define	cnb_CTL3DV2_Ctl3dEnabled cnb_CTL3DV2_Ctl3dColorChange
#define	cnb_CTL3DV2_Ctl3dGetVer cnb_CTL3DV2_Ctl3dColorChange

static HSMT_OEMENTRYTAB OEM_tab_CTL3D[] =
{	/* 000 */	{ "", 0 },
	/* 001 */	{ "CTL3DGETVER", cnb_CTL3DV2_Ctl3dGetVer },
	/* 002 */	{ "CTL3DSUBCLASSDLG", cnb_CTL3DV2_Ctl3dSubclassDlg },
	/* 003 */	{ "CTL3DSUBCLASSCTL", cnb_CTL3DV2_Ctl3dSubclassCtl },
	/* 004 */	{ "CTL3DCTLCOLOR", cnb_CTL3DV2_Ctl3dCtlColor },
	/* 005 */	{ "CTL3DENABLED", cnb_CTL3DV2_Ctl3dEnabled },
	/* 006 */	{ "CTL3DCOLORCHANGE", cnb_CTL3DV2_Ctl3dColorChange },
	/* 007 */	{ "BTNWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 008 */	{ "EDITWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 009 */	{ "LISTWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 00a */	{ "COMBOWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 00b */	{ "STATICWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 00c */	{ "CTL3DREGISTER", cnb_CTL3DV2_Ctl3dRegister },
	/* 00d */	{ "CTL3DUNREGISTER", cnb_CTL3DV2_Ctl3dUnregister },
	/* 00e */	{ "", cnb_CTL3DV2_Stub },
	/* 00f */	{ "", cnb_CTL3DV2_Stub },
	/* 010 */	{ "CTL3DAUTOSUBCLASS", cnb_CTL3DV2_Ctl3dAutoSubclass },
	/* 011 */	{ "CTL3DDLGPROC", cnb_CTL3DV2_Stub },
	/* 012 */	{ "CTL3DCTLCOLOREX", cnb_CTL3DV2_Ctl3dCtlColorEx },
	/* 013 */	{ "CTL3DSETSTYLE", cnb_CTL3DV2_Stub },
	/* 014 */	{ "CTL3DDLGFRAMEPAINT", cnb_CTL3DV2_Ctl3dDlgFramePaint },
	/* 015 */	{ "CTL3DSUBCLASSDLGEX", cnb_CTL3DV2_Ctl3dSubclassDlgEx },
	/* end */	{ 0, 0 }
};

static HSMT_OEMENTRYTAB OEM_tab_CTL3DV2[] =
{	/* 000 */	{ "", 0 },
	/* 001 */	{ "CTL3DGETVER", cnb_CTL3DV2_Ctl3dGetVer },
	/* 002 */	{ "CTL3DSUBCLASSDLG", cnb_CTL3DV2_Ctl3dSubclassDlg },
	/* 003 */	{ "CTL3DSUBCLASSCTL", cnb_CTL3DV2_Ctl3dSubclassCtl },
	/* 004 */	{ "CTL3DCTLCOLOR", cnb_CTL3DV2_Ctl3dCtlColor },
	/* 005 */	{ "CTL3DENABLED", cnb_CTL3DV2_Ctl3dEnabled },
	/* 006 */	{ "CTL3DCOLORCHANGE", cnb_CTL3DV2_Ctl3dColorChange },
	/* 007 */	{ "BTNWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 008 */	{ "EDITWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 009 */	{ "LISTWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 00a */	{ "COMBOWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 00b */	{ "STATICWNDPROC3D", cnb_CTL3DV2_Stub },
	/* 00c */	{ "CTL3DREGISTER", cnb_CTL3DV2_Ctl3dRegister },
	/* 00d */	{ "CTL3DUNREGISTER", cnb_CTL3DV2_Ctl3dUnregister },
	/* 00e */	{ "", cnb_CTL3DV2_Stub },
	/* 00f */	{ "", cnb_CTL3DV2_Stub },
	/* 010 */	{ "CTL3DAUTOSUBCLASS", cnb_CTL3DV2_Ctl3dAutoSubclass },
	/* 011 */	{ "CTL3DDLGPROC", cnb_CTL3DV2_Stub },
	/* 012 */	{ "CTL3DCTLCOLOREX", cnb_CTL3DV2_Ctl3dCtlColorEx },
	/* 013 */	{ "CTL3DSETSTYLE", cnb_CTL3DV2_Stub },
	/* 014 */	{ "CTL3DDLGFRAMEPAINT", cnb_CTL3DV2_Ctl3dDlgFramePaint },
	/* 015 */	{ "CTL3DSUBCLASSDLGEX", cnb_CTL3DV2_Ctl3dSubclassDlgEx },
	/* 016 */	{ "CTL3DWININICHANGE", cnb_CTL3DV2_Ctl3dWinIniChange },
	/* end */	{ 0, 0 }
};

HSMT_OEMMODTAB OEM_builtin_modtab[] =
{
	{ "PRINTER\0\0\0",OEM_tab_PRINTER },
	{ "CTL3D", OEM_tab_CTL3D },
	{ "CTL3DV2", OEM_tab_CTL3DV2 },
	{ 0,	0}
};

=============================================================================

