EMM completely redone for new memory system, 3.2 EMM Manager support
Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@197
This commit is contained in:
parent
067304c30a
commit
57c02611d7
1 changed files with 283 additions and 85 deletions
368
src/ints/ems.cpp
368
src/ints/ems.cpp
|
@ -18,6 +18,7 @@
|
|||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "dosbox.h"
|
||||
#include "callback.h"
|
||||
#include "mem.h"
|
||||
|
@ -28,105 +29,296 @@
|
|||
#include "dos_inc.h"
|
||||
|
||||
|
||||
#define PAGEFRAME_SEG 0xe000
|
||||
#define EMM_PAGEFRAME 0xE000
|
||||
#define EMM_MAX_HANDLES 50 /* 255 Max */
|
||||
#define EMM_PAGE_SIZE (16*1024)
|
||||
#define EMM_MAX_PAGES (C_MEM_EMS_SIZE * 1024 / 16 )
|
||||
#define EMM_MAX_PHYS 4 /* 4 16kb pages in pageframe */
|
||||
|
||||
class device_EMS : public DOS_Device {
|
||||
#define EMM_VERSION 0x32
|
||||
|
||||
#define NULL_HANDLE 0xffff
|
||||
#define NULL_PAGE 0xffff
|
||||
|
||||
/* EMM errors */
|
||||
#define EMM_NO_ERROR 0x00
|
||||
#define EMM_SOFT_MAL 0x80
|
||||
#define EMM_HARD_MAL 0x81
|
||||
#define EMM_INVALID_HANDLE 0x83
|
||||
#define EMM_FUNC_NOSUP 0x84
|
||||
#define EMM_OUT_OF_HANDLES 0x85
|
||||
#define EMM_OUT_OF_PHYS 0x87
|
||||
#define EMM_OUT_OF_LOG 0x88
|
||||
#define EMM_ZERO_PAGES 0x89
|
||||
#define EMM_LOG_OUT_RANGE 0x8a
|
||||
#define EMM_ILL_PHYS 0x8b
|
||||
#define EMM_PAGE_MAP_SAVED 0x8d
|
||||
#define EMM_INVALID_SUB 0x8f
|
||||
#define EMM_FEAT_NOSUP 0x91
|
||||
#define EMM_MOVE_OVLAP 0x92
|
||||
#define EMM_MOVE_OVLAPI 0x97
|
||||
#define EMM_NOT_FOUND 0xa0
|
||||
|
||||
|
||||
class device_EMM : public DOS_Device {
|
||||
public:
|
||||
device_EMS();
|
||||
bool Read(Bit8u * data,Bit16u * size);
|
||||
bool Write(Bit8u * data,Bit16u * size);
|
||||
bool Seek(Bit32u * pos,Bit32u type);
|
||||
bool Close();
|
||||
Bit16u GetInformation(void);
|
||||
device_EMM(){name="EMMXXXX0";}
|
||||
bool Read(Bit8u * data,Bit16u * size) { return false;}
|
||||
bool Write(Bit8u * data,Bit16u * size){
|
||||
LOG_DEBUG("Write to ems device");
|
||||
return false;
|
||||
}
|
||||
bool Seek(Bit32u * pos,Bit32u type){return false;}
|
||||
bool Close(){return false;}
|
||||
Bit16u GetInformation(void){return 0x8093;}
|
||||
private:
|
||||
Bit8u cache;
|
||||
};
|
||||
device_EMM::device_EMM();
|
||||
|
||||
bool device_EMS::Read(Bit8u * data,Bit16u * size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_EMS::Write(Bit8u * data,Bit16u * size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_EMS::Seek(Bit32u * pos,Bit32u type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_EMS::Close() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bit16u device_EMS::GetInformation(void) {
|
||||
return 0x8093;
|
||||
struct EMM_Mapping {
|
||||
Bit16u handle;
|
||||
Bit16u page;
|
||||
};
|
||||
|
||||
device_EMS::device_EMS() {
|
||||
name="EMMXXXX0";
|
||||
struct EMM_Page {
|
||||
void * memory;
|
||||
Bit16u handle;
|
||||
Bit16u next;
|
||||
};
|
||||
|
||||
struct EMM_Handle {
|
||||
Bit16u first_page;
|
||||
Bit16u pages;
|
||||
char name[9];
|
||||
bool saved_page_map;
|
||||
EMM_Mapping page_map[EMM_MAX_PHYS];
|
||||
};
|
||||
|
||||
static EMM_Handle emm_handles[EMM_MAX_HANDLES];
|
||||
static EMM_Page emm_pages[EMM_MAX_PAGES];
|
||||
static EMM_Mapping emm_mappings[EMM_MAX_PHYS];
|
||||
Bitu call_int67;
|
||||
|
||||
static Bit16u EMM_GetFreePages(void) {
|
||||
Bit16u count=0;
|
||||
for (Bitu index=0;index<EMM_MAX_PAGES;index++) {
|
||||
if (emm_pages[index].handle==NULL_HANDLE) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static Bit8u EMM_AllocateMemory(Bit16u pages,Bit16u & handle) {
|
||||
/* Check for 0 page allocation */
|
||||
if (!pages) return EMM_ZERO_PAGES;
|
||||
/* Check for enough free pages */
|
||||
if (EMM_GetFreePages()<pages){ handle=NULL_HANDLE; return EMM_OUT_OF_LOG;}
|
||||
handle=1;
|
||||
/* Check for a free handle */
|
||||
while (emm_handles[handle].first_page!=NULL_PAGE) {
|
||||
if (++handle>=EMM_MAX_HANDLES) {handle=NULL_HANDLE;return EMM_OUT_OF_HANDLES;}
|
||||
}
|
||||
/* Allocate the pages */
|
||||
Bit16u page=0;Bit16u last=NULL_PAGE;
|
||||
emm_handles[handle].pages=pages;
|
||||
while (pages) {
|
||||
if (emm_pages[page].handle==NULL_HANDLE) {
|
||||
emm_pages[page].handle=handle;
|
||||
emm_pages[page].memory=malloc(EMM_PAGE_SIZE);
|
||||
if (!emm_pages[page].memory) E_Exit("EMM:Cannont allocate memory");
|
||||
if (last!=NULL_PAGE) emm_pages[last].next=page;
|
||||
else emm_handles[handle].first_page=page;
|
||||
last=page;
|
||||
pages--;
|
||||
} else {
|
||||
if (++page>=EMM_MAX_PAGES) E_Exit("EMM:Ran out of pages");
|
||||
}
|
||||
}
|
||||
return EMM_NO_ERROR;
|
||||
}
|
||||
|
||||
PageEntry ems_entries[4];
|
||||
static Bit8u EMM_MapPage(Bitu phys_page,Bit16u handle,Bit16u log_page) {
|
||||
/* Check for too high physical page */
|
||||
if (phys_page>=EMM_MAX_PHYS) return EMM_ILL_PHYS;
|
||||
/* Check for valid handle */
|
||||
if (handle>=EMM_MAX_HANDLES || emm_handles[handle].first_page==NULL_PAGE) return EMM_INVALID_HANDLE;
|
||||
/* Check to do unmapping or mappning */
|
||||
if (log_page<emm_handles[handle].pages) {
|
||||
/* Mapping it is */
|
||||
emm_mappings[phys_page].handle=handle;
|
||||
emm_mappings[phys_page].page=log_page;
|
||||
Bit16u index=emm_handles[handle].first_page;
|
||||
while (log_page) {
|
||||
index=emm_pages[index].next;
|
||||
if (index==NULL_PAGE) E_Exit("EMM:Detected NULL Page in chain");
|
||||
log_page--;
|
||||
}
|
||||
/* Do the actual mapping */
|
||||
MEM_SetupMapping(PAGE_COUNT(PhysMake(EMM_PAGEFRAME,phys_page*EMM_PAGE_SIZE)),PAGE_COUNT(PAGE_SIZE),emm_pages[index].memory);
|
||||
return EMM_NO_ERROR;
|
||||
} else if (log_page==NULL_PAGE) {
|
||||
/* Unmapping it is */
|
||||
emm_mappings[phys_page].handle=NULL_HANDLE;
|
||||
emm_mappings[phys_page].page=NULL_PAGE;
|
||||
MEM_ClearMapping(PAGE_COUNT(PhysMake(EMM_PAGEFRAME,phys_page*EMM_PAGE_SIZE)),PAGE_COUNT(PAGE_SIZE));
|
||||
return EMM_NO_ERROR;
|
||||
} else {
|
||||
/* Illegal logical page it is */
|
||||
return EMM_LOG_OUT_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
static Bit8u EMM_ReleaseMemory(Bit16u handle) {
|
||||
/* Check for valid handle */
|
||||
if (handle>=EMM_MAX_HANDLES || emm_handles[handle].first_page==NULL_PAGE) return EMM_INVALID_HANDLE;
|
||||
Bit16u page=emm_handles[handle].first_page;
|
||||
Bit16u pages=emm_handles[handle].pages;
|
||||
while (pages) {
|
||||
free(emm_pages[page].memory);
|
||||
emm_pages[page].memory=0;
|
||||
emm_pages[page].handle=NULL_HANDLE;
|
||||
Bit16u next_page=emm_pages[page].next;
|
||||
emm_pages[page].next=NULL_PAGE;
|
||||
page=next_page;pages--;
|
||||
}
|
||||
/* Reset handle */
|
||||
emm_handles[handle].first_page=NULL_PAGE;
|
||||
emm_handles[handle].pages=0;
|
||||
emm_handles[handle].saved_page_map=false;
|
||||
memset(&emm_handles[handle].name,0,9);
|
||||
return EMM_NO_ERROR;
|
||||
}
|
||||
|
||||
static Bit8u EMM_SavePageMap(Bit16u handle) {
|
||||
/* Check for valid handle */
|
||||
if (handle>=EMM_MAX_HANDLES || emm_handles[handle].first_page==NULL_PAGE) return EMM_INVALID_HANDLE;
|
||||
/* Check for previous save */
|
||||
if (emm_handles[handle].saved_page_map) return EMM_PAGE_MAP_SAVED;
|
||||
/* Copy the mappings over */
|
||||
for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
|
||||
emm_handles[handle].page_map[i].page=emm_mappings[i].page;
|
||||
emm_handles[handle].page_map[i].handle=emm_mappings[i].handle;
|
||||
}
|
||||
emm_handles[handle].saved_page_map=true;
|
||||
return EMM_NO_ERROR;
|
||||
}
|
||||
|
||||
static Bitu EMM_RestoreMappingTable(void) {
|
||||
Bit8u result;
|
||||
/* Move through the mappings table and setup mapping accordingly */
|
||||
for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
|
||||
result=EMM_MapPage(i,emm_mappings[i].handle,emm_mappings[i].page);
|
||||
}
|
||||
return EMM_NO_ERROR;
|
||||
}
|
||||
static Bit8u EMM_RestorePageMap(Bit16u handle) {
|
||||
/* Check for valid handle */
|
||||
if (handle>=EMM_MAX_HANDLES || emm_handles[handle].first_page==NULL_PAGE) return EMM_INVALID_HANDLE;
|
||||
/* Check for previous save */
|
||||
if (!emm_handles[handle].saved_page_map) return EMM_INVALID_HANDLE;
|
||||
/* Restore the mappings */
|
||||
emm_handles[handle].saved_page_map=false;
|
||||
for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
|
||||
emm_mappings[i].page=emm_handles[handle].page_map[i].page;
|
||||
emm_mappings[i].handle=emm_handles[handle].page_map[i].handle;
|
||||
}
|
||||
return EMM_RestoreMappingTable();
|
||||
}
|
||||
|
||||
static Bit8u EMM_GetPagesForAllHandles(PhysPt table,Bit16u & handles) {
|
||||
handles=0;
|
||||
for (Bit16u i=0;i<EMM_MAX_HANDLES;i++) {
|
||||
if (emm_handles[i].first_page!=NULL_PAGE) {
|
||||
handles++;
|
||||
mem_writew(table,i);
|
||||
mem_writew(table+2,emm_handles[i].pages);
|
||||
table+=4;
|
||||
}
|
||||
}
|
||||
return EMM_NO_ERROR;
|
||||
}
|
||||
|
||||
Bitu call_int67;
|
||||
static Bitu INT67_Handler(void) {
|
||||
Bitu i;
|
||||
switch (reg_ah) {
|
||||
case 0x40: /* Get Status */
|
||||
reg_ah=0; //Status ok :)
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
case 0x41: /* Get PageFrame Segment */
|
||||
reg_bx=PAGEFRAME_SEG;
|
||||
reg_ah=0;
|
||||
reg_bx=EMM_PAGEFRAME;
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
case 0x42: /* Get number of pages */
|
||||
{
|
||||
//HEHE Hope this works not exactly like the specs but who cares
|
||||
Bit16u maxfree,total;
|
||||
EMM_GetFree(&maxfree,&total);
|
||||
reg_dx=maxfree>>2;
|
||||
reg_bx=total>>2;
|
||||
reg_ah=0;
|
||||
};
|
||||
reg_dx=EMM_MAX_PAGES;
|
||||
reg_bx=EMM_GetFreePages();
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
case 0x43: /* Get Handle and Allocate Pages */
|
||||
{
|
||||
if (!reg_bx) { reg_ah=0x89;break; }
|
||||
Bit16u handle;
|
||||
EMM_Allocate(reg_bx*4,&handle);
|
||||
if (handle) {
|
||||
reg_ah=0;
|
||||
reg_dx=handle;
|
||||
} else { reg_ah=0x88; }
|
||||
break;
|
||||
}
|
||||
case 0x44: /* Map Memory */
|
||||
{
|
||||
if (reg_al>3) { reg_ah=0x8b;break; }
|
||||
HostPt pagestart=memory+EMM_Handles[reg_dx].phys_base+reg_bx*16*1024;
|
||||
ems_entries[reg_al].relocate=pagestart;
|
||||
reg_ah=0;
|
||||
break;
|
||||
}
|
||||
case 0x45: /* Release handle and free pages */
|
||||
EMM_Free(reg_dx);
|
||||
reg_ah=0;
|
||||
break;
|
||||
case 0x46: /* Get EMM Version */
|
||||
reg_ah=0;
|
||||
reg_al=0x32; //Only 3.2 support for now
|
||||
reg_ah=EMM_AllocateMemory(reg_bx,reg_dx);
|
||||
break;
|
||||
case 0x47: /* Save Mapping Context */
|
||||
LOG_ERROR("EMS:47:Save Mapping Context not supported");
|
||||
reg_ah=0x8c;
|
||||
case 0x44: /* Map Expanded Memory Page */
|
||||
reg_ah=EMM_MapPage(reg_al,reg_dx,reg_bx);
|
||||
break;
|
||||
case 0x45: /* Release handle and free pages */
|
||||
reg_ah=EMM_ReleaseMemory(reg_dx);
|
||||
break;
|
||||
case 0x46: /* Get EMM Version */
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
reg_al=EMM_VERSION;
|
||||
break;
|
||||
case 0x47: /* Save Page Map */
|
||||
reg_ah=EMM_SavePageMap(reg_dx);
|
||||
break;
|
||||
case 0x48: /* Restore Page Map */
|
||||
reg_ah=EMM_RestorePageMap(reg_dx);
|
||||
break;
|
||||
case 0x4b: /* Get Handle Count */
|
||||
reg_bx=0;
|
||||
for (i=0;i<EMM_MAX_HANDLES;i++) if (emm_handles[i].first_page!=NULL_PAGE) reg_bx++;
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
case 0x4c: /* Get Pages for one Handle */
|
||||
/* Check for valid handle */
|
||||
if (reg_bx>=EMM_MAX_HANDLES || emm_handles[reg_bx].first_page==NULL_PAGE) {reg_ah=EMM_INVALID_HANDLE;break;}
|
||||
reg_bx=emm_handles[reg_dx].pages;
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
case 0x4d: /* Get Pages for all Handles */
|
||||
reg_ah=EMM_GetPagesForAllHandles(SegPhys(es)+reg_di,reg_bx);
|
||||
break;
|
||||
case 0x4e: /*Save/Restore Page Map */
|
||||
LOG_WARN("Save/resetore %d",reg_al);
|
||||
switch (reg_al) {
|
||||
case 0x00: /* Save Page Map */
|
||||
MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings));
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
case 0x01: /* Restore Page Map */
|
||||
MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings));
|
||||
reg_ah=EMM_RestoreMappingTable();
|
||||
break;
|
||||
case 0x02: /* Save and Restore Page Map */
|
||||
MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings));
|
||||
MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings));
|
||||
reg_ah=EMM_RestoreMappingTable();
|
||||
break;
|
||||
case 0x03: /* Get Page Map Array Size */
|
||||
reg_al=sizeof(emm_mappings);
|
||||
reg_ah=EMM_NO_ERROR;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
|
||||
reg_ah=EMM_FUNC_NOSUP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xDE: /* VCPI Functions */
|
||||
LOG_ERROR("VCPI Functions not supported");
|
||||
reg_ah=0x8c;
|
||||
reg_ah=EMM_FUNC_NOSUP;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("EMS:Call %2X not supported",reg_ah);
|
||||
reg_ah=0x8c;
|
||||
reg_ah=EMM_FUNC_NOSUP;
|
||||
break;
|
||||
}
|
||||
return CBRET_NONE;
|
||||
|
@ -138,28 +330,34 @@ void EMS_Init(void) {
|
|||
call_int67=CALLBACK_Allocate();
|
||||
CALLBACK_Setup(call_int67,&INT67_Handler,CB_IRET);
|
||||
/* Register the ems device */
|
||||
DOS_Device * newdev = new device_EMS();
|
||||
DOS_Device * newdev = new device_EMM();
|
||||
DOS_AddDevice(newdev);
|
||||
/* Setup the page handlers for the page frame */
|
||||
for (Bitu i=0;i<4;i++) {
|
||||
ems_entries[i].base=(PAGEFRAME_SEG<<4)+i*16*1024;
|
||||
ems_entries[i].type=MEMORY_RELOCATE;
|
||||
ems_entries[i].relocate=memory+(PAGEFRAME_SEG<<4)+i*16*1024;
|
||||
/* Place the page handlers in the ems page fram piece of the memory handler*/
|
||||
MEMORY_SetupHandler(((PAGEFRAME_SEG<<4)+i*16*1024)>>12,4,&ems_entries[i]);
|
||||
}
|
||||
|
||||
|
||||
/* Add a little hack so it appears that there is an actual ems device installed */
|
||||
char * emsname="EMMXXXX0";
|
||||
Bit16u seg=DOS_GetMemory(2); //We have 32 bytes
|
||||
MEM_BlockWrite(real_phys(seg,0xa),emsname,strlen(emsname)+1);
|
||||
/* Copy the callback piece into the beginning */
|
||||
MEM_BlockWrite(PhysMake(seg,0xa),emsname,strlen(emsname)+1);
|
||||
/* Copy the callback piece into the beginning, and set the interrupt vector to it*/
|
||||
char buf[16];
|
||||
MEM_BlockRead(real_phys(CB_SEG,call_int67<<4),buf,0xa);
|
||||
MEM_BlockWrite(real_phys(seg,0),buf,0xa);
|
||||
|
||||
MEM_BlockRead(PhysMake(CB_SEG,call_int67<<4),buf,0xa);
|
||||
MEM_BlockWrite(PhysMake(seg,0),buf,0xa);
|
||||
RealSetVec(0x67,RealMake(seg,0));
|
||||
/* Clear handle and page tables */
|
||||
Bitu i;
|
||||
for (i=0;i<EMM_MAX_PAGES;i++) {
|
||||
emm_pages[i].memory=0;
|
||||
emm_pages[i].handle=NULL_HANDLE;
|
||||
emm_pages[i].next=NULL_PAGE;
|
||||
}
|
||||
for (i=0;i<EMM_MAX_HANDLES;i++) {
|
||||
emm_handles[i].first_page=NULL_PAGE;
|
||||
memset(&emm_handles[i].name,0,9);
|
||||
}
|
||||
for (i=0;i<EMM_MAX_PHYS;i++) {
|
||||
emm_mappings[i].page=NULL_PAGE;
|
||||
emm_mappings[i].handle=NULL_HANDLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue