/* * 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 "paging.h" #include "bios.h" #include "keyboard.h" #include "regs.h" #include "inout.h" #include "dos_inc.h" #include "setup.h" #define EMM_PAGEFRAME 0xE000 #define EMM_PAGEFRAME4K ((EMM_PAGEFRAME*16)/4096) #define EMM_MAX_HANDLES 100 /* 255 Max */ #define EMM_PAGE_SIZE (16*1024U) #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,LOG_NORMAL)("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_Handle { Bit16u pages; MemHandle mem; char name[8]; bool saved_page_map; EMM_Mapping page_map[EMM_MAX_PHYS]; }; static EMM_Handle emm_handles[EMM_MAX_HANDLES]; static EMM_Mapping emm_mappings[EMM_MAX_PHYS]; static 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; }; static Bit16u EMM_GetFreePages(void) { Bitu count=MEM_FreeTotal()/4; if (count>0x7fff) count=0x7fff; return (Bit16u)count; } static bool INLINE ValidHandle(Bit16u handle) { if (handle>=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 ((MEM_FreeTotal()/4)=EMM_MAX_HANDLES) {handle=NULL_HANDLE;return EMM_OUT_OF_HANDLES;} } MemHandle mem=MEM_AllocatePages(pages*4,false); if (!mem) E_Exit("EMS:Memory allocation failure"); emm_handles[handle].pages=pages; emm_handles[handle].mem=mem; return EMM_NO_ERROR; } static Bit8u EMM_ReallocatePages(Bit16u handle,Bit16u & pages) { /* Check for valid handle */ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE; /* Check for enough pages */ if (!MEM_ReAllocatePages(emm_handles[handle].mem,pages*4,false)) return EMM_OUT_OF_LOG; /* Update size */ emm_handles[handle].pages=pages; return EMM_NO_ERROR; } static Bit8u EMM_MapPage(Bitu phys_page,Bit16u handle,Bit16u log_page) { // LOG_MSG("EMS MapPage handle %d phys %d log %d",handle,phys_page,log_page); /* Check for too high physical page */ if (phys_page>=EMM_MAX_PHYS) return EMM_ILL_PHYS; /* Check for valid handle */ if (!ValidHandle(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; /* 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_PAGEFRAME+0x1000)) return EMM_ILL_PHYS; page = (page-EMM_PAGEFRAME) / (EMM_PAGE_SIZE>>4); 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_MISC,LOG_ERROR)("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_MISC,LOG_ERROR)("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[MEM_PAGE_SIZE]; Bit8u buf_dest[MEM_PAGE_SIZE]; if (reg_al>1) { LOG(LOG_MISC,LOG_ERROR)("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; MemHandle src_handle,dest_handle; 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_handle=emm_handles[region.src_handle].mem; Bitu pages=region.src_page_seg*4+(region.src_offset/MEM_PAGE_SIZE); for (;pages>0;pages--) src_handle=MEM_NextHandle(src_handle); src_off=region.src_offset&(MEM_PAGE_SIZE-1); src_remain=MEM_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_handle=emm_handles[region.dest_handle].mem; Bitu pages=region.dest_page_seg*4+(region.dest_offset/MEM_PAGE_SIZE); for (;pages>0;pages--) dest_handle=MEM_NextHandle(dest_handle); dest_off=region.dest_offset&(MEM_PAGE_SIZE-1); dest_remain=MEM_PAGE_SIZE-dest_off; } Bitu toread; while (region.bytes>0) { if (region.bytes>MEM_PAGE_SIZE) toread=MEM_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); if (!section->Get_bool("ems")) return; 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