/* * Copyright (C) 2002-2009 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 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. */ /* $Id: ems.cpp,v 1.65 2009-10-11 17:11:52 c2woody Exp $ */ #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" #include "support.h" #include "cpu.h" #define EMM_PAGEFRAME 0xE000 #define EMM_PAGEFRAME4K ((EMM_PAGEFRAME*16)/4096) #define EMM_MAX_HANDLES 200 /* 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 EMM_MINOR_VERSION 0x00 //#define EMM_MINOR_VERSION 0x30 // emm386 4.48 #define GEMMIS_VERSION 0x0001 // Version 1.0 #define EMM_SYSTEM_HANDLE 0x0000 #define NULL_HANDLE 0xffff #define NULL_PAGE 0xffff #define ENABLE_VCPI 1 #define ENABLE_V86_STARTUP 0 /* 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_SAVEMAP_ERROR 0x86 #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_NO_SAVED_PAGE_MAP 0x8e #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 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 EMM_Mapping emm_segmentmappings[0x40]; static Bit16u GEMMIS_seg; class device_EMM : public DOS_Device { public: device_EMM() { SetName("EMMXXXX0"); GEMMIS_seg=0; } 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 0xc080;} bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode); bool WriteToControlChannel(PhysPt /*bufptr*/,Bit16u /*size*/,Bit16u * /*retcode*/){return true;} private: Bit8u cache; }; bool device_EMM::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { Bitu subfct=mem_readb(bufptr); switch (subfct) { case 0x00: if (size!=6) return false; mem_writew(bufptr+0x00,0x0023); // ID mem_writed(bufptr+0x02,0); // private API entry point *retcode=6; return true; case 0x01: { if (size!=6) return false; if (GEMMIS_seg==0) GEMMIS_seg=DOS_GetMemory(0x20); PhysPt GEMMIS_addr=PhysMake(GEMMIS_seg,0); mem_writew(GEMMIS_addr+0x00,0x0004); // flags mem_writew(GEMMIS_addr+0x02,0x019d); // size of this structure mem_writew(GEMMIS_addr+0x04,GEMMIS_VERSION); // version 1.0 (provide ems information only) mem_writed(GEMMIS_addr+0x06,0); // reserved /* build non-EMS frames (0-0xe000) */ for (Bitu frct=0; frct>4); // version 4 mem_writeb(bufptr+0x01,EMM_MINOR_VERSION); *retcode=2; return true; case 0x03: if (EMM_MINOR_VERSION < 0x2d) return false; if (size!=4) return false; mem_writew(bufptr+0x00,(Bit16u)(MEM_TotalPages()*4)); // max size (kb) mem_writew(bufptr+0x02,0x80); // min size (kb) *retcode=2; return true; } return false; } static struct { bool enabled; Bit16u ems_handle; Bitu pm_interface; MemHandle private_area; Bit8u pic1_remapping,pic2_remapping; } vcpi ; 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 & dhandle,bool can_allocate_zpages) { /* Check for 0 page allocation */ if (!pages) { if (!can_allocate_zpages) return EMM_ZERO_PAGES; } /* Check for enough free pages */ if ((MEM_FreeTotal()/ 4) < pages) { return EMM_OUT_OF_LOG;} Bit16u handle = 1; /* Check for a free handle */ while (emm_handles[handle].pages != NULL_HANDLE) { if (++handle >= EMM_MAX_HANDLES) {return EMM_OUT_OF_HANDLES;} } MemHandle mem = 0; if (pages) { mem = MEM_AllocatePages(pages*4,false); if (!mem) E_Exit("EMS:Memory allocation failure"); } emm_handles[handle].pages = pages; emm_handles[handle].mem = mem; /* Change handle only if there is no error. */ dhandle = handle; return EMM_NO_ERROR; } static Bit8u EMM_AllocateSystemHandle(Bit16u pages) { /* Check for enough free pages */ if ((MEM_FreeTotal()/ 4) < pages) { return EMM_OUT_OF_LOG;} Bit16u handle = EMM_SYSTEM_HANDLE; // emm system handle (reserved for OS usage) /* Release memory if already allocated */ if (emm_handles[handle].pages != NULL_HANDLE) { MEM_ReleasePages(emm_handles[handle].mem); } MemHandle mem = MEM_AllocatePages(pages*4,false); if (!mem) E_Exit("EMS:System handle 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; if (emm_handles[handle].pages != 0) { /* Check for enough pages */ if (!MEM_ReAllocatePages(emm_handles[handle].mem,pages*4,false)) return EMM_OUT_OF_LOG; } else { MemHandle mem = MEM_AllocatePages(pages*4,false); if (!mem) E_Exit("EMS:Memory allocation failure during reallocation"); emm_handles[handle].mem = mem; } /* 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; /* unmapping doesn't need valid handle (as handle isn't used) */ if (log_page==NULL_PAGE) { /* Unmapping */ emm_mappings[phys_page].handle=NULL_HANDLE; emm_mappings[phys_page].page=NULL_PAGE; for (Bitu i=0;i<4;i++) PAGING_MapPage(EMM_PAGEFRAME4K+phys_page*4+i,EMM_PAGEFRAME4K+phys_page*4+i); PAGING_ClearTLB(); return EMM_NO_ERROR; } /* Check for valid handle */ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE; if (log_page=0xa000) && (segment<0xb000)) || ((segment>=EMM_PAGEFRAME-0x1000) && (segment=0) && (tphysPage>10].handle=NULL_HANDLE; emm_segmentmappings[segment>>10].page=NULL_PAGE; } for (Bitu i=0;i<4;i++) PAGING_MapPage(segment*16/4096+i,segment*16/4096+i); PAGING_ClearTLB(); return EMM_NO_ERROR; } /* Check for valid handle */ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE; if (log_page=0) && (tphysPage>10].handle=handle; emm_segmentmappings[segment>>10].page=log_page; } MemHandle memh=MEM_NextHandleAt(emm_handles[handle].mem,log_page*4);; for (Bitu i=0;i<4;i++) { PAGING_MapPage(segment*16/4096+i,memh); memh=MEM_NextHandle(memh); } PAGING_ClearTLB(); return EMM_NO_ERROR; } else { /* Illegal logical page it is */ return EMM_LOG_OUT_RANGE; } } return EMM_ILL_PHYS; } static Bit8u EMM_ReleaseMemory(Bit16u handle) { /* Check for valid handle */ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE; // should check for saved_page_map flag here, returning an error if it's true // as apps are required to restore the pagemap beforehand; to be checked // if (emm_handles[handle].saved_page_map) return EMM_SAVEMAP_ERROR; if (emm_handles[handle].pages != 0) { MEM_ReleasePages(emm_handles[handle].mem); } /* Reset handle */ emm_handles[handle].mem=0; if (handle==0) { emm_handles[handle].pages=0; // OS handle is NEVER deallocated } else { 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) { if (handle!=0) 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_PAGEFRAME/0x400) && (i<(EMM_PAGEFRAME/0x400)+EMM_MAX_PHYS)) continue; result=EMM_MapSegment(i<<10,emm_segmentmappings[i].handle,emm_segmentmappings[i].page); } for (Bitu i=0;i=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) { if (handle!=0) return EMM_INVALID_HANDLE; } /* Check for previous save */ if (!emm_handles[handle].saved_page_map) return EMM_NO_SAVED_PAGE_MAP; /* Restore the mappings */ emm_handles[handle].saved_page_map=false; for (Bitu i=0;i0;count--) { Bit16u segment=mem_readw(list);list+=2; if ((segment>=EMM_PAGEFRAME) && (segment>4); mem_writew(data,segment);data+=2; MEM_BlockWrite(data,&emm_mappings[page],sizeof(EMM_Mapping)); data+=sizeof(EMM_Mapping); } else if (((segment>=EMM_PAGEFRAME-0x1000) && (segment=0xa000) && (segment<0xb000))) { mem_writew(data,segment);data+=2; MEM_BlockWrite(data,&emm_segmentmappings[segment>>10],sizeof(EMM_Mapping)); data+=sizeof(EMM_Mapping); } else { return EMM_ILL_PHYS; } } break; case 0x01: /* Restore Partial Page Map */ data = SegPhys(ds)+reg_si; count= mem_readw(data);data+=2; for (;count>0;count--) { Bit16u segment=mem_readw(data);data+=2; if ((segment>=EMM_PAGEFRAME) && (segment>4); MEM_BlockRead(data,&emm_mappings[page],sizeof(EMM_Mapping)); } else if (((segment>=EMM_PAGEFRAME-0x1000) && (segment=0xa000) && (segment<0xb000))) { MEM_BlockRead(data,&emm_segmentmappings[segment>>10],sizeof(EMM_Mapping)); } else { return EMM_ILL_PHYS; } data+=sizeof(EMM_Mapping); } return EMM_RestoreMappingTable(); break; case 0x02: /* Get Partial Page Map Array Size */ reg_al=(Bit8u)(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_INVALID_SUB; } 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 = 0,dest_mem = 0; MemHandle src_handle = 0,dest_handle = 0; Bitu src_off = 0,dest_off = 0 ;Bitu src_remain = 0,dest_remain = 0; 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>6)+(ct*0x10); real_writew(SegValue(es),entry_addr+0x00+0x01,(memh+0)*0x10); // mapping of 1/4 of page real_writew(SegValue(es),entry_addr+0x04+0x01,(memh+1)*0x10); // mapping of 2/4 of page real_writew(SegValue(es),entry_addr+0x08+0x01,(memh+2)*0x10); // mapping of 3/4 of page real_writew(SegValue(es),entry_addr+0x0c+0x01,(memh+3)*0x10); // mapping of 4/4 of page } } reg_di+=0x400; // advance pointer by 0x100*4 /* Set up three descriptor table entries */ Bit32u cbseg_low=(CALLBACK_GetBase()&0xffff)<<16; Bit32u cbseg_high=(CALLBACK_GetBase()&0x1f0000)>>16; /* Descriptor 1 (code segment, callback segment) */ real_writed(SegValue(ds),reg_si+0x00,0x0000ffff|cbseg_low); real_writed(SegValue(ds),reg_si+0x04,0x00009a00|cbseg_high); /* Descriptor 2 (data segment, full access) */ real_writed(SegValue(ds),reg_si+0x08,0x0000ffff); real_writed(SegValue(ds),reg_si+0x0c,0x00009200); /* Descriptor 3 (full access) */ real_writed(SegValue(ds),reg_si+0x10,0x0000ffff); real_writed(SegValue(ds),reg_si+0x14,0x00009200); reg_ebx=(vcpi.pm_interface&0xffff); reg_ah=EMM_NO_ERROR; break; } case 0x02: /* VCPI Maximum Physical Address */ reg_edx=((MEM_TotalPages()*MEM_PAGESIZE)-1)&0xfffff000; reg_ah=EMM_NO_ERROR; break; case 0x03: /* VCPI Get Number of Free Pages */ reg_edx=MEM_FreeTotal(); reg_ah=EMM_NO_ERROR; break; case 0x04: { /* VCPI Allocate one Page */ MemHandle mem = MEM_AllocatePages(1,false); if (mem) { reg_edx=mem<<12; reg_ah=EMM_NO_ERROR; } else { reg_ah=EMM_OUT_OF_LOG; } break; } case 0x05: /* VCPI Free Page */ MEM_ReleasePages(reg_edx>>12); reg_ah=EMM_NO_ERROR; break; case 0x06: { /* VCPI Get Physical Address of Page in 1st MB */ if (((reg_cx<<8)>=EMM_PAGEFRAME) && ((reg_cx<<8)>12); reg_ah=EMM_NO_ERROR; break; case 0xDE0C: { /* VCPI Switch from Protected Mode to V86 */ reg_flags&=(~FLAG_IF); /* Flags need to be filled in, VM=true, IOPL=3 */ mem_writed(SegPhys(ss) + (reg_esp & cpu.stack.mask)+0x10, 0x23002); /* Disable Paging */ CPU_SET_CRX(0, CPU_GET_CRX(0)&0x7ffffff7); CPU_SET_CRX(3, 0); PhysPt tbaddr=vcpi.private_area+0x0000+(0x10&0xfff8)+5; Bit8u tb=mem_readb(tbaddr); mem_writeb(tbaddr, tb&0xfd); /* Load descriptor table registers */ CPU_LGDT(0xff, vcpi.private_area+0x0000); CPU_LIDT(0x7ff, vcpi.private_area+0x2000); if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT"); if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR"); reg_flags&=(~FLAG_NT); reg_esp+=8; // skip interrupt return information // MEM_A20_Enable(false); /* Switch to v86-task */ CPU_IRET(true,0); } break; default: LOG(LOG_MISC,LOG_WARN)("Unhandled VCPI-function %x in protected mode",reg_al); break; } return CBRET_NONE; } static Bitu V86_Monitor() { /* Calculate which interrupt did occur */ Bitu int_num=(mem_readw(SegPhys(ss)+(reg_esp & cpu.stack.mask))-0x2803); /* See if Exception 0x0d and not Interrupt 0x0d */ if ((int_num==(0x0d*4)) && ((reg_sp&0xffff)!=0x1fda)) { /* Protection violation during V86-execution, needs intervention by monitor (depends on faulting opcode) */ reg_esp+=6; // skip ip of CALL and error code of EXCEPTION 0x0d /* Get adress of faulting instruction */ Bit16u v86_cs=mem_readw(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask)); Bit16u v86_ip=mem_readw(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask)); Bit8u v86_opcode=mem_readb((v86_cs<<4)+v86_ip); // LOG_MSG("v86 monitor caught protection violation at %x:%x, opcode=%x",v86_cs,v86_ip,v86_opcode); switch (v86_opcode) { case 0x0f: // double byte opcode v86_opcode=mem_readb((v86_cs<<4)+v86_ip+1); switch (v86_opcode) { case 0x20: { // mov reg,CRx Bitu rm_val=mem_readb((v86_cs<<4)+v86_ip+2); Bitu which=(rm_val >> 3) & 7; if ((rm_val<0xc0) || (rm_val>=0xe8)) E_Exit("Invalid opcode 0x0f 0x20 %x caused a protection fault!",static_cast(rm_val)); Bit32u crx=CPU_GET_CRX(which); switch (rm_val&7) { case 0: reg_eax=crx; break; case 1: reg_ecx=crx; break; case 2: reg_edx=crx; break; case 3: reg_ebx=crx; break; case 4: reg_esp=crx; break; case 5: reg_ebp=crx; break; case 6: reg_esi=crx; break; case 7: reg_edi=crx; break; } mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+3); } break; case 0x22: { // mov CRx,reg Bitu rm_val=mem_readb((v86_cs<<4)+v86_ip+2); Bitu which=(rm_val >> 3) & 7; if ((rm_val<0xc0) || (rm_val>=0xe8)) E_Exit("Invalid opcode 0x0f 0x22 %x caused a protection fault!",static_cast(rm_val)); Bit32u crx=0; switch (rm_val&7) { case 0: crx=reg_eax; break; case 1: crx=reg_ecx; break; case 2: crx=reg_edx; break; case 3: crx=reg_ebx; break; case 4: crx=reg_esp; break; case 5: crx=reg_ebp; break; case 6: crx=reg_esi; break; case 7: crx=reg_edi; break; } if (which==0) crx|=1; // protection bit always on CPU_SET_CRX(which,crx); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+3); } break; default: E_Exit("Unhandled opcode 0x0f %x caused a protection fault!",v86_opcode); } break; case 0xe4: // IN AL,Ib reg_al=(Bit8u)(IO_ReadB(mem_readb((v86_cs<<4)+v86_ip+1))&0xff); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2); break; case 0xe5: // IN AX,Ib reg_ax=(Bit16u)(IO_ReadW(mem_readb((v86_cs<<4)+v86_ip+1))&0xffff); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2); break; case 0xe6: // OUT Ib,AL IO_WriteB(mem_readb((v86_cs<<4)+v86_ip+1),reg_al); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2); break; case 0xe7: // OUT Ib,AX IO_WriteW(mem_readb((v86_cs<<4)+v86_ip+1),reg_ax); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2); break; case 0xec: // IN AL,DX reg_al=(Bit8u)(IO_ReadB(reg_dx)&0xff); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1); break; case 0xed: // IN AX,DX reg_ax=(Bit16u)(IO_ReadW(reg_dx)&0xffff); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1); break; case 0xee: // OUT DX,AL IO_WriteB(reg_dx,reg_al); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1); break; case 0xef: // OUT DX,AX IO_WriteW(reg_dx,reg_ax); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1); break; case 0xf0: // LOCK prefix mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1); break; case 0xf4: // HLT reg_flags|=FLAG_IF; CPU_HLT(reg_eip); mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1); break; default: E_Exit("Unhandled opcode %x caused a protection fault!",v86_opcode); } return CBRET_NONE; } /* Get address to interrupt handler */ Bit16u vint_vector_seg=mem_readw(SegValue(ds)+int_num+2); Bit16u vint_vector_ofs=mem_readw(int_num); if (reg_sp!=0x1fda) reg_esp+=(2+3*4); // Interrupt from within protected mode else reg_esp+=2; /* Read entries that were pushed onto the stack by the interrupt */ Bit16u return_ip=mem_readw(SegPhys(ss)+(reg_esp & cpu.stack.mask)); Bit16u return_cs=mem_readw(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask)); Bit32u return_eflags=mem_readd(SegPhys(ss)+((reg_esp+8) & cpu.stack.mask)); /* Modify stack to call v86-interrupt handler */ mem_writed(SegPhys(ss)+(reg_esp & cpu.stack.mask),vint_vector_ofs); mem_writed(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask),vint_vector_seg); mem_writed(SegPhys(ss)+((reg_esp+8) & cpu.stack.mask),return_eflags&(~(FLAG_IF|FLAG_TF))); /* Adjust SP of v86-stack */ Bit16u v86_ss=mem_readw(SegPhys(ss)+((reg_esp+0x10) & cpu.stack.mask)); Bit16u v86_sp=mem_readw(SegPhys(ss)+((reg_esp+0x0c) & cpu.stack.mask))-6; mem_writew(SegPhys(ss)+((reg_esp+0x0c) & cpu.stack.mask),v86_sp); /* Return to original code after v86-interrupt handler */ mem_writew((v86_ss<<4)+v86_sp+0,return_ip); mem_writew((v86_ss<<4)+v86_sp+2,return_cs); mem_writew((v86_ss<<4)+v86_sp+4,(Bit16u)(return_eflags&0xffff)); return CBRET_NONE; } static void SetupVCPI() { vcpi.enabled=false; vcpi.ems_handle=0; // use EMM system handle for VCPI data vcpi.enabled=true; vcpi.pic1_remapping=0x08; // master PIC base vcpi.pic2_remapping=0x70; // slave PIC base vcpi.private_area=emm_handles[vcpi.ems_handle].mem<<12; /* GDT */ mem_writed(vcpi.private_area+0x0000,0x00000000); // descriptor 0 mem_writed(vcpi.private_area+0x0004,0x00000000); // descriptor 0 Bit32u ldt_address=(vcpi.private_area+0x1000); Bit16u ldt_limit=0xff; Bit32u ldt_desc_part=((ldt_address&0xffff)<<16)|ldt_limit; mem_writed(vcpi.private_area+0x0008,ldt_desc_part); // descriptor 1 (LDT) ldt_desc_part=((ldt_address&0xff0000)>>16)|(ldt_address&0xff000000)|0x8200; mem_writed(vcpi.private_area+0x000c,ldt_desc_part); // descriptor 1 Bit32u tss_address=(vcpi.private_area+0x3000); Bit32u tss_desc_part=((tss_address&0xffff)<<16)|(0x0068+0x200); mem_writed(vcpi.private_area+0x0010,tss_desc_part); // descriptor 2 (TSS) tss_desc_part=((tss_address&0xff0000)>>16)|(tss_address&0xff000000)|0x8900; mem_writed(vcpi.private_area+0x0014,tss_desc_part); // descriptor 2 /* LDT */ mem_writed(vcpi.private_area+0x1000,0x00000000); // descriptor 0 mem_writed(vcpi.private_area+0x1004,0x00000000); // descriptor 0 Bit32u cs_desc_part=((vcpi.private_area&0xffff)<<16)|0xffff; mem_writed(vcpi.private_area+0x1008,cs_desc_part); // descriptor 1 (code) cs_desc_part=((vcpi.private_area&0xff0000)>>16)|(vcpi.private_area&0xff000000)|0x9a00; mem_writed(vcpi.private_area+0x100c,cs_desc_part); // descriptor 1 Bit32u ds_desc_part=((vcpi.private_area&0xffff)<<16)|0xffff; mem_writed(vcpi.private_area+0x1010,ds_desc_part); // descriptor 2 (data) ds_desc_part=((vcpi.private_area&0xff0000)>>16)|(vcpi.private_area&0xff000000)|0x9200; mem_writed(vcpi.private_area+0x1014,ds_desc_part); // descriptor 2 /* IDT setup */ for (Bit16u int_ct=0; int_ct<0x100; int_ct++) { /* build a CALL NEAR V86MON, the value of IP pushed by the CALL is used to identify the interrupt number */ mem_writeb(vcpi.private_area+0x2800+int_ct*4+0,0xe8); // call mem_writew(vcpi.private_area+0x2800+int_ct*4+1,0x05fd-(int_ct*4)); mem_writeb(vcpi.private_area+0x2800+int_ct*4+3,0xcf); // iret (dummy) /* put a Gate-Descriptor into the IDT */ mem_writed(vcpi.private_area+0x2000+int_ct*8+0,0x000c0000|(0x2800+int_ct*4)); mem_writed(vcpi.private_area+0x2000+int_ct*8+4,0x0000ee00); } /* TSS */ for (Bitu tse_ct=0; tse_ct<0x68+0x200; tse_ct++) { /* clear the TSS as most entries are not used here */ mem_writeb(vcpi.private_area+0x3000,0); } /* Set up the ring0-stack */ mem_writed(vcpi.private_area+0x3004,0x00002000); // esp mem_writed(vcpi.private_area+0x3008,0x00000014); // ss mem_writed(vcpi.private_area+0x3066,0x0068); // io-map base (map follows, all zero) } static Bitu INT4B_Handler() { switch (reg_ah) { case 0x81: CALLBACK_SCF(true); reg_ax=0x1; break; default: LOG(LOG_MISC,LOG_WARN)("Unhandled interrupt 4B function %x",reg_ah); break; } return CBRET_NONE; } class EMS: public Module_base { private: /* location in protected unfreeable memory where the ems name and callback are * stored 32 bytes.*/ static Bit16u ems_baseseg; RealPt old4b_pointer,old67_pointer; CALLBACK_HandlerObject call_vdma,call_vcpi,call_v86mon; Bitu call_int67; public: EMS(Section* configuration):Module_base(configuration){ /* Virtual DMA interrupt callback */ call_vdma.Install(&INT4B_Handler,CB_IRET,"Int 4b vdma"); call_vdma.Set_RealVec(0x4b); vcpi.enabled=false; GEMMIS_seg=0; Section_prop * section=static_cast(configuration); if (!section->Get_bool("ems")) return; if (machine==MCH_PCJR) { LOG_MSG("EMS disabled for PCJr machine"); return; } BIOS_ZeroExtendedSize(true); if (!ems_baseseg) ems_baseseg=DOS_GetMemory(2); //We have 32 bytes /* Add a little hack so it appears that there is an actual ems device installed */ char const* emsname="EMMXXXX0"; MEM_BlockWrite(PhysMake(ems_baseseg,0xa),emsname,(Bitu)(strlen(emsname)+1)); call_int67=CALLBACK_Allocate(); CALLBACK_Setup(call_int67,&INT67_Handler,CB_IRET,PhysMake(ems_baseseg,4),"Int 67 ems"); RealSetVec(0x67,RealMake(ems_baseseg,4),old67_pointer); /* Register the ems device */ //TODO MAYBE put it in the class. DOS_Device * newdev = new device_EMM(); DOS_AddDevice(newdev); /* Clear handle and page tables */ Bitu i; for (i=0;i(m_configuration); if (!section->Get_bool("ems")) return; /* Undo Biosclearing */ BIOS_ZeroExtendedSize(false); /* Remove ems device */ device_EMM newdev; DOS_DelDevice(&newdev); GEMMIS_seg=0; /* Remove the emsname and callback hack */ char buf[32]= { 0 }; MEM_BlockWrite(PhysMake(ems_baseseg,0),buf,32); RealSetVec(0x67,old67_pointer); /* Release memory allocated to system handle */ if (emm_handles[EMM_SYSTEM_HANDLE].pages != NULL_HANDLE) { MEM_ReleasePages(emm_handles[EMM_SYSTEM_HANDLE].mem); } /* Clear handle and page tables */ //TODO if ((!ENABLE_VCPI) || (!vcpi.enabled)) return; if (cpu.pmode && GETFLAG(VM)) { /* Switch back to real mode if in v86-mode */ CPU_SET_CRX(0, 0); CPU_SET_CRX(3, 0); reg_flags&=(~(FLAG_IOPL|FLAG_VM)); CPU_LIDT(0x3ff, 0); cpu.cpl=0; } } }; static EMS* test; void EMS_ShutDown(Section* /*sec*/) { delete test; } void EMS_Init(Section* sec) { test = new EMS(sec); sec->AddDestroyFunction(&EMS_ShutDown,true); } //Initialize static members Bit16u EMS::ems_baseseg = 0;