/****************************************************************************
*
* VESA BIOS Extensions/Accelerator Functions
* Version 1.0
*
* Copyright (C) 1996 SciTech Software.
* All rights reserved.
*
* Filename: $Workfile: vbeaf.c $
* Developed by: SciTech Software
*
* Language: ANSI C
* Environment: IBM PC 32 bit Protected Mode.
*
* Description: C module for the Graphics Acclerator Driver API. Uses
* the SciTech PM/Pro library for interfacing with DOS
* extender specific functions.
*
* $Date: 11 Jul 1996 20:11:42 $ $Author: KendallB $
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vbeaf.h"
#include "pmode.h"
#if !defined(__16BIT__) || defined(TESTING)
#ifndef _MAX_PATH
#define _MAX_PATH 255
#endif
/*------------------------- Global Variables ------------------------------*/
#define AF_DRIVERDIR AF_DRIVERDIR_DOS
static int status = afOK;
static void *IOMemMaps[4] = {NULL,NULL,NULL,NULL};
static void *BankedMem = NULL;
static void *LinearMem = NULL;
static AF_int32 Sel0000h = 0;
static AF_int32 Sel0040h = 0;
static AF_int32 SelA000h = 0;
static AF_int32 SelB000h = 0;
static AF_int32 SelC000h = 0;
/*-------------------------- Implementation -------------------------------*/
/* Internal assembler functions */
AF_int32 _ASMAPI _AF_initDriver(AF_devCtx *dc);
void _ASMAPI _AF_int86(void);
void _ASMAPI _AF_callRealMode(void);
static void backslash(char *s)
/****************************************************************************
*
* Function: backslash
* Parameters: s - String to add backslash to
*
* Description: Appends a trailing '\' pathname separator if the string
* currently does not have one appended.
*
****************************************************************************/
{
uint pos = strlen(s);
if (s[pos-1] != '\\') {
s[pos] = '\\';
s[pos+1] = '\0';
}
}
static long fileSize(FILE *f)
/****************************************************************************
*
* Function: fileSize
* Parameters: f - Open file to determine the size of
* Returns: Length of the file in bytes.
*
* Description: Determines the length of the file, without altering the
* current position in the file.
*
****************************************************************************/
{
long size,oldpos = ftell(f);
fseek(f,0,SEEK_END); /* Seek to end of file */
size = ftell(f); /* Determine the size of the file */
fseek(f,oldpos,SEEK_SET); /* Seek to old position in file */
return size; /* Return the size of the file */
}
void _ASMAPI _AF_callRealMode_C(AF_DPMI_regs *dregs)
/****************************************************************************
*
* Function: _AF_callRealMode_C
* Parameters: dregs - Pointer to DPMI register structure
*
* Description: Calls a real mode procedure. This does not need to be
* speedy, so we simply convert the registers to the format
* expected by the PM/Pro library and let it handle it.
*
****************************************************************************/
{
RMREGS regs;
RMSREGS sregs;
regs.x.ax = (short)dregs->eax;
regs.x.bx = (short)dregs->ebx;
regs.x.cx = (short)dregs->ecx;
regs.x.dx = (short)dregs->edx;
regs.x.si = (short)dregs->esi;
regs.x.di = (short)dregs->edi;
sregs.es = dregs->es;
sregs.ds = dregs->ds;
PM_callRealMode(dregs->cs,dregs->ip,®s,&sregs);
dregs->eax = regs.x.ax;
dregs->ebx = regs.x.bx;
dregs->ecx = regs.x.cx;
dregs->edx = regs.x.dx;
dregs->esi = regs.x.si;
dregs->edi = regs.x.di;
dregs->es = sregs.es;
dregs->ds = sregs.ds;
}
void _ASMAPI _AF_int86_C(AF_int32 intno,AF_DPMI_regs *dregs)
/****************************************************************************
*
* Function: _AF_int86_C
* Parameters: intno - Interrupt number to issue
* dregs - Pointer to DPMI register structure
*
* Description: Issues a real mode interrupt. This does not need to be
* speedy, so we simply convert the registers to the format
* expected by the PM/Pro library and let it handle it.
*
****************************************************************************/
{
RMREGS regs;
RMSREGS sregs;
regs.x.ax = (short)dregs->eax;
regs.x.bx = (short)dregs->ebx;
regs.x.cx = (short)dregs->ecx;
regs.x.dx = (short)dregs->edx;
regs.x.si = (short)dregs->esi;
regs.x.di = (short)dregs->edi;
sregs.es = dregs->es;
sregs.ds = dregs->ds;
PM_int86x(intno,®s,®s,&sregs);
dregs->eax = regs.x.ax;
dregs->ebx = regs.x.bx;
dregs->ecx = regs.x.cx;
dregs->edx = regs.x.dx;
dregs->esi = regs.x.si;
dregs->edi = regs.x.di;
dregs->es = sregs.es;
dregs->ds = sregs.ds;
}
AF_int32 _AF_initInternal(AF_devCtx *dc)
/****************************************************************************
*
* Function: _AF_initInternal
* Parameters: dc - Pointer to device context
* Returns: Error code.
*
* Description: Performs internal initialisation on the AF_devCtx driver
* block, assuming that it has been loaded correctly.
*
****************************************************************************/
{
/* Verify that the file is the driver file we are expecting */
if (strcmp(dc->Signature,VBEAF_DRV) != 0)
return status = afCorruptDriver;
if (dc->Version < VBEAF_VERSION)
return status = afOldVersion;
/* Map the memory mapped register locations for the driver. We need to
* map up to four different locations that may possibly be needed. If
* the base address is zero then the memory does not need to be mapped.
*/
if (IOMemMaps[0] == NULL) {
if (dc->IOMemoryBase[0]) {
IOMemMaps[0] = PM_mapPhysicalAddr(dc->IOMemoryBase[0],dc->IOMemoryLen[0]-1);
if (IOMemMaps[0] == NULL)
return status = afMemMapError;
}
if (dc->IOMemoryBase[1]) {
IOMemMaps[1] = PM_mapPhysicalAddr(dc->IOMemoryBase[1],dc->IOMemoryLen[1]-1);
if (IOMemMaps[1] == NULL)
return status = afMemMapError;
}
if (dc->IOMemoryBase[2]) {
IOMemMaps[2] = PM_mapPhysicalAddr(dc->IOMemoryBase[2],dc->IOMemoryLen[2]-1);
if (IOMemMaps[2] == NULL)
return status = afMemMapError;
}
if (dc->IOMemoryBase[3]) {
IOMemMaps[3] = PM_mapPhysicalAddr(dc->IOMemoryBase[3],dc->IOMemoryLen[3]-1);
if (IOMemMaps[3] == NULL)
return status = afMemMapError;
}
}
dc->IOMemMaps[0] = IOMemMaps[0];
dc->IOMemMaps[1] = IOMemMaps[1];
dc->IOMemMaps[2] = IOMemMaps[2];
dc->IOMemMaps[3] = IOMemMaps[3];
/* Map the banked video memory area for the driver */
#ifndef __WIN386__
if (BankedMem == NULL && dc->BankedBasePtr) {
BankedMem = PM_mapPhysicalAddr(dc->BankedBasePtr,0xFFFF);
if (BankedMem == NULL)
return status = afMemMapError;
}
#endif
dc->BankedMem = BankedMem;
/* Map the linear video memory area for the driver */
if (LinearMem == NULL && dc->LinearBasePtr) {
LinearMem = PM_mapPhysicalAddr(dc->LinearBasePtr,dc->LinearSize*1024L - 1);
if (LinearMem == NULL)
return status = afMemMapError;
}
dc->LinearMem = LinearMem;
/* Provide selectors to important real mode segment areas */
if (Sel0000h == 0) {
Sel0000h = PM_createSelector(0x00000L,0xFFFFFL);
Sel0040h = PM_createSelector(0x00400L,0xFFFF);
SelA000h = PM_createSelector(0xA0000L,0xFFFF);
SelB000h = PM_createSelector(0xB0000L,0xFFFF);
SelC000h = PM_createSelector(0xC0000L,0xFFFF);
}
dc->Sel0000h = Sel0000h;
dc->Sel0040h = Sel0040h;
dc->SelA000h = SelA000h;
dc->SelB000h = SelB000h;
dc->SelC000h = SelC000h;
/* Install the device callback functions */
dc->Int86 = _AF_int86;
dc->CallRealMode = _AF_callRealMode;
return afOK;
}
AF_devCtx * AFAPI AF_loadDriver(const char *driverDir)
/****************************************************************************
*
* Function: AF_loadDriver
* Parameters: driverDir - Directory to load the driver file from
* Returns: Pointer to the loaded driver file.
*
* Description: Loads the driver file and intialises the device context
* ready for use. If the driver file cannot be found, or the
* driver does not detect the installed hardware, we return
* NULL and the application can get the status code with
* AF_status().
*
****************************************************************************/
{
char filename[_MAX_PATH];
FILE *f;
int size;
AF_devCtx *dc;
/* Reset status flag */
status = afOK;
/* Try if the default operating system location first */
strcpy(filename,AF_DRIVERDIR);
strcat(filename,VBEAF_DRV);
if ((f = fopen(filename,"rb")) == NULL) {
/* Now try to find the driver in the VBEAF_PATH directory */
if (getenv(VBEAF_PATH)) {
strcpy(filename, getenv(VBEAF_PATH));
backslash(filename);
strcat(filename,VBEAF_DRV);
if ((f = fopen(filename,"rb")) != NULL)
goto FoundDriver;
}
/* Else try in the specified path */
if (driverDir) {
strcpy(filename, driverDir);
backslash(filename);
strcat(filename,VBEAF_DRV);
if ((f = fopen(filename,"rb")) != NULL)
goto FoundDriver;
}
/* Driver file was not found */
status = afDriverNotFound;
return NULL;
}
/* Allocate memory for driver file and load it */
FoundDriver:
size = fileSize(f);
if ((dc = malloc(size+16)) == NULL) {
status = afLoadMem;
fclose(f);
return NULL;
}
fread(dc,1,size,f);
fclose(f);
/* Perform internal initialisation */
if (_AF_initInternal(dc) != afOK)
goto Error;
/* Now call the driver to detect the installed hardware and initialise
* the driver.
*/
if (_AF_initDriver(dc) != 0) {
status = afNotDetected;
goto Error;
}
return dc;
Error:
free(dc);
return NULL;
}
void AFAPI AF_unloadDriver(AF_devCtx *dc)
/****************************************************************************
*
* Function: AF_unloadDriver
* Parameters: dc - Pointer to device context
*
* Description: Unloads the loaded device driver.
*
****************************************************************************/
{
free(dc);
}
/* Set of code stubs used to build the final bank switch code */
PRIVATE uchar AF_bankFunc32_Start[] = {
0x53,0x51, /* push ebx,ecx */
0x8B,0xD0, /* mov edx,eax */
};
PRIVATE uchar AF_bankFunc32_End[] = {
0x59,0x5B, /* pop ecx,ebx */
};
PRIVATE uchar bankFunc32[100];
#define copy(p,b,a) memcpy(b,a,sizeof(a)); (p) = (b) + sizeof(a)
bool AFAPI AF_getBankFunc32(AF_devCtx *dc,int *codeLen,void **bankFunc)
/****************************************************************************
*
* Function: VBE_getBankFunc32
* Parameters: dc - VBE/AF device context block
* codeLen - Place to store length of code
* bankFunc - Place to store pointer to bank switch code
* Returns: True on success, false if not compatible.
*
* Description: Creates a local 32 bit bank switch function from the
* VBE/AF bank switch code that is compatible with the
* virtual flat framebuffer devices (does not have a return
* instruction at the end and takes the bank number in EAX
* not EDX).
*
****************************************************************************/
{
int len;
uchar *code = 0;
uchar *p;
if (dc->SetBank32) {
code = (uchar*)dc->SetBank32;
len = dc->SetBank32Len-1;
copy(p,bankFunc32,AF_bankFunc32_Start);
memcpy(p,code,len);
p += len;
copy(p,p,AF_bankFunc32_End);
*codeLen = p - bankFunc32;
*bankFunc = bankFunc32;
return true;
}
return false;
}
AF_int32 AFAPI AF_status(void)
{ return status; }
const char * AFAPI AF_errorMsg(int status)
/****************************************************************************
*
* Function: AF_errorMsg
* Returns: String describing error condition.
*
****************************************************************************/
{
const char *msg[] = {
"No error",
"Graphics hardware not detected",
"Driver file not found",
"File loaded was not a driver file",
"Not enough memory to load driver",
"Driver file is an older version",
"Could not map physical memory areas",
};
if (status >= afOK && status < afMaxError)
return msg[status];
return "Unknown error!";
}
#endif /* !defined(__16BIT__) */