/* * Copyright (C) 2002-2003 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "dosbox.h" #include "callback.h" #include "mem.h" #include "bios.h" #include "keyboard.h" #include "regs.h" #include "inout.h" #include "dos_inc.h" #include "setup.h" #define EMM_USEHANDLER 1 #define EMM_PAGEFRAME 0xE000 #define EMM_MAX_HANDLES 50 /* 255 Max */ #define EMM_PAGE_SIZE (16*1024) #define EMM_MAX_PAGES (32 * 1024 / 16 ) #define EMM_MAX_PHYS 4 /* 4 16kb pages in pageframe */ #define EMM_VERSION 0x40 #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_EMM(){name="EMMXXXX0";} bool Read(Bit8u * data,Bit16u * size) { return false;} bool Write(Bit8u * data,Bit16u * size){ LOG(LOG_IOCTL,"EMS:Write to device"); return false; } bool Seek(Bit32u * pos,Bit32u type){return false;} bool Close(){return false;} Bit16u GetInformation(void){return 0x8093;} private: Bit8u cache; }; struct EMM_Mapping { Bit16u handle; Bit16u page; }; struct EMM_Page { HostPt memory; Bit16u handle; Bit16u next; }; struct EMM_Handle { Bit16u first_page; Bit16u pages; char name[8]; 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]; static HostPt emm_pagebase[EMM_MAX_PHYS]; static Bitu emm_page_count; Bitu call_int67; struct MoveRegion { Bit32u bytes; Bit8u src_type; Bit16u src_handle; Bit16u src_offset; Bit16u src_page_seg; Bit8u dest_type; Bit16u dest_handle; Bit16u dest_offset; Bit16u dest_page_seg; }; #if EMM_USEHANDLER Bit8u EMM_ReadHandler(PhysPt start) { start-=EMM_PAGEFRAME * 16; Bitu page=start>>14; return readb(emm_pagebase[page]+(start&0x3fff)); } void EMM_WriteHandler(PhysPt start,Bit8u val) { start-=EMM_PAGEFRAME * 16; Bitu page=start>>14; writeb(emm_pagebase[page]+(start&0x3fff),val); } #endif static Bit16u EMM_GetFreePages(void) { Bit16u count=0; for (Bitu index=0;index=EMM_MAX_HANDLES) return false; if (emm_handles[handle].pages==NULL_HANDLE) return false; return true; } 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()=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=(HostPt)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_page_count) E_Exit("EMM:Ran out of pages"); } } return EMM_NO_ERROR; } static Bit8u EMM_ReallocatePages(Bit16u handle,Bit16u & pages) { /* Check for valid handle */ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE; /* Check for enough pages */ if ((emm_handles[handle].pages+EMM_GetFreePages())0 && page_count>0) { if (emm_pages[page].handle!=handle) E_Exit("EMM:Error illegal handle reference"); last=page; page=emm_pages[page].next; pages--; page_count--; } /* Free the rest of the handles */ if (page_count && !pages) { emm_handles[handle].pages-=page_count; while (page_count>0) { 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;page_count--; } pages=emm_handles[handle].pages; if (!pages) emm_handles[handle].first_page=NULL_PAGE; return EMM_NO_ERROR; } if (!page_count && pages) { /* Allocate extra pages */ emm_handles[handle].pages+=pages; page=0; while (pages) { if (emm_pages[page].handle==NULL_HANDLE) { emm_pages[page].handle=handle; emm_pages[page].memory=(HostPt)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_page_count) E_Exit("EMM:Ran out of pages"); } } pages=emm_handles[handle].pages; return EMM_NO_ERROR; } /* Size exactly the same as the original size */ pages=emm_handles[handle].pages; return EMM_NO_ERROR; } 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].pages==NULL_HANDLE) return EMM_INVALID_HANDLE; /* Check to do unmapping or mappning */ if (log_page=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) 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=NULL_HANDLE; emm_handles[handle].saved_page_map=false; memset(&emm_handles[handle].name,0,8); return EMM_NO_ERROR; } static Bit8u EMM_SavePageMap(Bit16u handle) { /* Check for valid handle */ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) 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_HANDLES || emm_handles[handle].pages==NULL_HANDLE) 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;i0;count--) { Bit16u page=mem_readw(list);list+=2; if (page>=EMM_MAX_PHYS) return EMM_ILL_PHYS; mem_writew(data,page);data+=2; MEM_BlockWrite(data,&emm_mappings[page],sizeof(EMM_Mapping)); data+=sizeof(EMM_Mapping); } break; case 0x01: /* Restore Partial Page Map */ data = SegPhys(ds)+reg_si; count= mem_readw(data);data+=2; for (;count>0;count--) { Bit16u page=mem_readw(data);data+=2; if (page>=EMM_MAX_PHYS) return EMM_ILL_PHYS; MEM_BlockRead(data,&emm_mappings[page],sizeof(EMM_Mapping)); data+=sizeof(EMM_Mapping); } return EMM_RestoreMappingTable(); break; case 0x02: /* Get Partial Page Map Array Size */ reg_al=2+reg_bx*(2+sizeof(EMM_Mapping)); break; default: LOG(LOG_ERROR|LOG_MISC,"EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al); return EMM_FUNC_NOSUP; } return EMM_NO_ERROR; } static Bit8u HandleNameSearch(void) { char name[9]; Bit16u handle=0;PhysPt data; switch (reg_al) { case 0x00: /* Get all handle names */ reg_al=0;data=SegPhys(es)+reg_di; for (handle=0;handle=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE; MEM_BlockWrite(SegPhys(es)+reg_di,emm_handles[handle].name,8); break; case 0x01: /* Set Handle Name */ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE; MEM_BlockRead(SegPhys(es)+reg_di,emm_handles[handle].name,8); break; default: LOG(LOG_ERROR|LOG_MISC,"EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al); return EMM_FUNC_NOSUP; } return EMM_NO_ERROR; } static void LoadMoveRegion(PhysPt data,MoveRegion & region) { region.bytes=mem_readd(data+0x0); region.src_type=mem_readb(data+0x4); region.src_handle=mem_readw(data+0x5); region.src_offset=mem_readw(data+0x7); region.src_page_seg=mem_readw(data+0x9); region.dest_type=mem_readb(data+0xb); region.dest_handle=mem_readw(data+0xc); region.dest_offset=mem_readw(data+0xe); region.dest_page_seg=mem_readw(data+0x10); } static Bit8u MemoryRegion(void) { MoveRegion region; Bit8u buf_src[EMM_PAGE_SIZE]; Bit8u buf_dest[EMM_PAGE_SIZE]; if (reg_al>1) { LOG(LOG_ERROR|LOG_MISC,"EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al); return EMM_FUNC_NOSUP; } LoadMoveRegion(SegPhys(ds)+reg_si,region); /* Parse the region for information */ PhysPt src_mem,dest_mem; Bit16u src_page,dest_page;Bitu src_off,dest_off;Bitu src_remain,dest_remain; if (!region.src_type) { src_mem=region.src_page_seg*16+region.src_offset; } else { if (!ValidHandle(region.src_handle)) return EMM_INVALID_HANDLE; if (emm_handles[region.src_handle].pages*EMM_PAGE_SIZE < (region.src_page_seg*EMM_PAGE_SIZE)+region.src_offset+region.bytes) return EMM_LOG_OUT_RANGE; src_page=emm_handles[region.src_handle].first_page; while (region.src_page_seg>0) { src_page=emm_pages[src_page].next; region.src_page_seg--; } src_off=region.src_offset; src_remain=EMM_PAGE_SIZE-src_off; } if (!region.dest_type) { dest_mem=region.dest_page_seg*16+region.dest_offset; } else { if (!ValidHandle(region.dest_handle)) return EMM_INVALID_HANDLE; if (emm_handles[region.dest_handle].pages*EMM_PAGE_SIZE < (region.dest_page_seg*EMM_PAGE_SIZE)+region.dest_offset+region.bytes) return EMM_LOG_OUT_RANGE; dest_page=emm_handles[region.dest_handle].first_page; while (region.dest_page_seg>0) { dest_page=emm_pages[dest_page].next; region.dest_page_seg--; } dest_off=region.dest_offset; dest_remain=EMM_PAGE_SIZE-dest_off; } Bitu toread; while (region.bytes>0) { if (region.bytes>EMM_PAGE_SIZE) toread=EMM_PAGE_SIZE; else toread=region.bytes; /* Read from the source */ if (!region.src_type) { MEM_BlockRead(src_mem,buf_src,toread); } else { if (toread(sec); Bitu size=section->Get_int("emssize"); if (!size) return; if ((size*(1024/16))>EMM_MAX_PAGES) { LOG_MSG("EMS Max size is %d",EMM_MAX_PAGES/(1024/16)); emm_page_count=EMM_MAX_PAGES; } else { emm_page_count=size*(1024/16); } call_int67=CALLBACK_Allocate(); CALLBACK_Setup(call_int67,&INT67_Handler,CB_IRET); /* Register the ems device */ DOS_Device * newdev = new device_EMM(); DOS_AddDevice(newdev); /* 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(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(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