VCPI: Windows INT 2Fh multiplex, provide vm86 control callback function if VCPI emulation active

This commit is contained in:
Jonathan Campbell 2024-12-08 05:57:36 -08:00
parent a57726571d
commit d00f3387f4
4 changed files with 97 additions and 3 deletions

View File

@ -29,6 +29,11 @@ Next
DOS extenders. For the first time, DOS extenders are working with
the DOSBox-X built-in implementation of VCPI and most DOS games
run fine now with it. (joncampbell123).
- VCPI: Add code to INT 2Fh handler regarding Windows startup/shutdown
messages to fill in DS:SI. If EMM386 and VCPI emulation is active
and running the DOS machine in virtual 8086 mode, give Windows the
proper callback procedure to allow Windows to switch off VCPI vm86
mode on startup and turn it back on during shutdown. (joncampbell123).
2024.12.04
- Arrange memory device allocation so that it is possible to allocate

View File

@ -2431,6 +2431,13 @@ Bitu CPU_STR(void) {
return cpu_tss.selector;
}
void CPU_TSS_ForceBusy(bool busy) {
if (cpu_tss.selector != 0) {
cpu_tss.desc.SetBusy(busy);
cpu_tss.SaveSelector();
}
}
bool CPU_LTR(Bitu selector) {
if ((selector & 0xfffc)==0) {
cpu_tss.SetSelector(selector);

View File

@ -110,6 +110,7 @@ extern bool i4dos, shellrun, clipboard_dosapi, swapad;
extern RealPt DOS_DriveDataListHead; // INT 2Fh AX=0803h DRIVER.SYS drive data table list
extern uint16_t seg_win_startup_info;
void PasteClipboard(bool bPressed);
RealPt Get_EMS_vm86control();
// INT 2F
char regpath[CROSS_LEN+1]="C:\\WINDOWS\\SYSTEM.DAT";
@ -356,6 +357,17 @@ static bool DOS_MultiplexFunctions(void) {
reg_bx = 0;
}
/* If EMS emulation is providing VCPI and it is enabled (the system is in vm86 mode),
* provide Windows a callback function to control it */
{
RealPt p = Get_EMS_vm86control();
if (p != 0) {
SegSet16(ds,p >> 16u);
reg_si = p & 0xFFFFu;
LOG_MSG("DEBUG: Providing Windows our VCPI vm86 control entry point 0x%lx",(unsigned long)p);
}
}
return false; /* pass it on to other INT 2F handlers */
case 0x1606: /* Windows exit broadcast */
/* TODO: Maybe future parts of DOSBox-X will do something with this */

View File

@ -869,6 +869,7 @@ static uint8_t MemoryRegion(void) {
return EMM_NO_ERROR;
}
void CPU_TSS_ForceBusy(bool busy);
static Bitu INT67_Handler(void) {
switch (reg_ah) {
@ -1186,6 +1187,7 @@ static Bitu INT67_Handler(void) {
case 0x0c: { /* VCPI Switch from V86 to Protected Mode */
reg_flags&=(~FLAG_IF);
CPU_SetCPL(0);
CPU_TSS_ForceBusy(false);//HACK
/* Read data from ESI (linear address) */
uint32_t new_cr3=mem_readd(reg_esi);
@ -1282,6 +1284,7 @@ static Bitu VCPI_PM_Handler() {
mem_writeb(tbaddr, tb&0xfd);
/* Load descriptor table registers */
CPU_TSS_ForceBusy(false);//HACK
CPU_LGDT(0xff, (unsigned int)vcpi.private_area+0x0000u);
CPU_LIDT(0x7ff, (unsigned int)vcpi.private_area+0x2000u);
if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT");
@ -1327,6 +1330,58 @@ bool VCPI_trapio_w(uint16_t port,uint32_t data,unsigned int sz) {
return false;
}
/* Windows virtual86 mode enable/disable callback.
* If we're emulating VCPI, this is required for Windows to work with it.
* Or else, it refuses to run.
*
* AX = 0 disable virtual 8086 mode, enter real mode
* AX = 1 re-enable virtual 8086 mode, from real mode */
static Bitu WinVM86Ctl() {
switch (reg_ax) {
case 0:
if (cpu.pmode) {
reg_flags &= ~FLAG_VM;
CPU_SetSegGeneral(cs,SegValue(cs));
CPU_SetSegGeneral(ds,SegValue(ds));
CPU_SetSegGeneral(es,SegValue(es));
CPU_SetSegGeneral(ss,SegValue(ss));
CPU_TSS_ForceBusy(false);//HACK
CPU_LTR(0);
CPU_LLDT(0);
CPU_SET_CRX(0, 0);
CPU_LGDT(0xffff, 0);
CPU_LIDT(0xffff, 0);
CPU_SetCPL(0);
assert(!cpu.pmode);
}
LOG_MSG("virtual806 mode disabled");
break;
case 1:
if (!cpu.pmode && ENABLE_V86_STARTUP && ENABLE_VCPI) {
CPU_LGDT(0xff, (unsigned int)vcpi.private_area+0x0000);
CPU_LIDT(0x7ff, (unsigned int)vcpi.private_area+0x2000);
CPU_SET_CRX(0, 1);
CPU_TSS_ForceBusy(false);//HACK
if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT");
if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR");
CPU_SetSegGeneral(cs,SegValue(cs));
CPU_SetSegGeneral(ds,SegValue(ds));
CPU_SetSegGeneral(es,SegValue(es));
CPU_SetSegGeneral(ss,SegValue(ss));
reg_flags |= FLAG_VM;
CPU_SetCPL(3);
assert(cpu.pmode);
}
LOG_MSG("virtual806 mode reenabled");
break;
default:
LOG_MSG("virtual806 mode enable/disable: unknown AX=%u",reg_ax);
break;
};
return CBRET_NONE;
}
static Bitu V86_Monitor() {
/* Calculate which interrupt did occur */
Bitu int_num=((unsigned int)mem_readw(SegPhys(ss)+((unsigned int)reg_esp & (unsigned int)cpu.stack.mask)) - 0x2803u);
@ -1607,9 +1662,16 @@ private:
DOS_Device* emm_device = NULL;
unsigned int oshandle_memsize_16kb = 0;
RealPt /*old4b_pointer,*/old67_pointer = 0/*NULL*/;
CALLBACK_HandlerObject call_vdma,call_vcpi,call_v86mon;
CALLBACK_HandlerObject call_vdma,call_vcpi,call_v86mon,call_win_vm86_ctl;
public:
RealPt Get_EMS_vm86control() {
if (ENABLE_V86_STARTUP && ENABLE_VCPI)
return call_win_vm86_ctl.Get_RealPointer();
else
return 0;
}
EMS(Section* configuration):Module_base(configuration) {
/* Virtual DMA interrupt callback */
@ -1787,6 +1849,9 @@ public:
if (!vcpi.enabled) return;
/* Vm86 mode enable/disable function for Windows */
call_win_vm86_ctl.Install(&WinVM86Ctl,CB_RETF,"VM86 mode Windows control");
/* Install v86-callback that handles interrupts occurring
in v86 mode, including protection fault exceptions */
call_v86mon.Install(&V86_Monitor,CB_IRET,"V86 Monitor");
@ -1826,11 +1891,12 @@ public:
vcpi_virtual_a20 = true;
/* Prepare V86-task */
CPU_SET_CRX(0, 1);
CPU_LGDT(0xff, (unsigned int)vcpi.private_area+0x0000);
CPU_LIDT(0x7ff, (unsigned int)vcpi.private_area+0x2000);
CPU_SET_CRX(0, 1);
if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT");
if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR");
CPU_TSS_ForceBusy(true);//HACK
/* TODO: Page tables are usually involved as well. That is the "magic"
* behind EMM386.EXE page frames. */
@ -1899,11 +1965,15 @@ public:
}
}
};
static EMS* test = NULL;
extern const char* RunningProgram;
void CALLBACK_DeAllocate(Bitu in);
RealPt Get_EMS_vm86control() {
return (test != NULL) ? test->Get_EMS_vm86control() : 0;
}
void EMS_DoShutDown() {
if (!strcmp(RunningProgram, "LOADLIN")) {
test = NULL;