From 9186e6c5ed1ab58bf1dc0497bacc352d3d758703 Mon Sep 17 00:00:00 2001 From: Kenneth J Davis Date: Thu, 19 Aug 2021 11:46:10 -0400 Subject: [PATCH] initial windows 3 enhanced support based on unstable kernel branch --- kernel/entry.asm | 49 ++++++++- kernel/int2f.asm | 11 +- kernel/inthndlr.c | 255 +++++++++++++++++++++++++++++++++++++++++++++- kernel/kernel.asm | 18 +++- kernel/main.c | 16 +-- 5 files changed, 332 insertions(+), 17 deletions(-) diff --git a/kernel/entry.asm b/kernel/entry.asm index fa0f5c6..3d4742e 100644 --- a/kernel/entry.asm +++ b/kernel/entry.asm @@ -41,6 +41,7 @@ segment HMA_TEXT extern _user_r extern _ErrorMode extern _InDOS + extern _winInstanced extern _cu_psp extern _MachineId extern critical_sp @@ -276,7 +277,9 @@ int21_reentry: jne int21_1 int21_user: - call dos_crit_sect +%IFNDEF WIN31SUPPORT + call end_dos_crit_sect +%ENDIF ; NOT WIN31SUPPORT push ss push bp @@ -362,15 +365,27 @@ int21_onerrorstack: jmp short int21_exit_nodec -int21_2: inc byte [_InDOS] +int21_2: +%IFDEF WIN31SUPPORT ; begin critical section + ; should be called as needed, but we just + ; mark the whole int21 api as critical + call begin_dos_crit_sect +%ENDIF ; WIN31SUPPORT + inc byte [_InDOS] mov cx,_char_api_tos or ah,ah jz int21_3 +%IFDEF WIN31SUPPORT ; testing, this function call crashes + cmp ah,06h + je int21_3 +%ENDIF ; WIN31SUPPORT cmp ah,0ch jbe int21_normalentry int21_3: - call dos_crit_sect +%IFNDEF WIN31SUPPORT + call end_dos_crit_sect +%ENDIF ; NOT WIN31SUPPORT mov cx,_disk_api_tos int21_normalentry: @@ -390,6 +405,15 @@ int21_normalentry: call _int21_service int21_exit: dec byte [_InDOS] +%IFDEF WIN31SUPPORT + call end_dos_crit_sect ; release all critical sections +%if 0 + push ax + mov ax, 8101h ; Leave Critical Section + int 2ah + pop ax +%endif +%ENDIF ; WIN31SUPPORT ; ; Recover registers from system call. Registers and flags @@ -415,11 +439,28 @@ int21_ret: ; ... and return. ; iret +%IFDEF WIN31SUPPORT +; +; begin DOS Critical Section 1 +; +; +begin_dos_crit_sect: + ; we only enable critical sections if Windows is active + ; we currently use winInstanced, but may need to use separate patchable location + cmp word [_winInstanced], 0 + jz skip_crit_sect + push ax + mov ax, 8001h ; Enter Critical Section + int 2ah + pop ax +skip_crit_sect: + ret +%ENDIF ; WIN31SUPPORT ; ; end Dos Critical Section 0 thur 7 ; ; -dos_crit_sect: +end_dos_crit_sect: mov [_Int21AX],ax ; needed! push ax ; This must be here!!! mov ah,82h ; re-enrty sake before disk stack diff --git a/kernel/int2f.asm b/kernel/int2f.asm index be61ccd..bb2cd91 100644 --- a/kernel/int2f.asm +++ b/kernel/int2f.asm @@ -122,10 +122,14 @@ WinIdle: ; only HLT if at haltlevel 2+ Int2f3: cmp ax,1680h ; Win "release time slice" je WinIdle - cmp ah,16h - je FarTabRetn ; other Win Hook return fast cmp ah,12h je IntDosCal ; Dos Internal calls + cmp ah,13h + je IntDosCal ; Install Int13h Hook + cmp ah,16h + je IntDosCal ; Win (Multitasking) Hook + cmp ah,46h + je IntDosCal ; Win Hook to avoid MCB corruption cmp ax,4a01h je IntDosCal ; Dos Internal calls @@ -135,6 +139,9 @@ Int2f3: cmp ax,1680h ; Win "release time slice" cmp ax,4a33h ; Check DOS version 7 jne Check4Share xor ax,ax ; no undocumented shell strings + xor bx,bx ; RBIL undoc BX = ?? (0h) + ; " DS:DX ASCIIZ shell exe name + ; " DS:SI SHELL= line iret Check4Share: %endif diff --git a/kernel/inthndlr.c b/kernel/inthndlr.c index 22677c8..d766cf7 100644 --- a/kernel/inthndlr.c +++ b/kernel/inthndlr.c @@ -31,6 +31,8 @@ #include "portab.h" #include "globals.h" #include "nls.h" +#include "win.h" +#include "debug.h" #ifdef VERSION_STRINGS BYTE *RcsId = @@ -1901,7 +1903,12 @@ struct int2f12regs { UWORD callerARG1; /* used if called from INT2F/12 */ }; -/* On input pr->AX==0x12xx, 0x4A01 or 0x4A02 +extern intvec BIOSInt13, UserInt13, BIOSInt19; + + +/* WARNING: modifications in `r' are used outside of int2F_12_handler() + * On input r.AX==0x12xx, 0x4A01 or 0x4A02 + * also handle Windows' DOS notification hooks, r.AH==0x16 and r.AH==0x13 */ VOID ASMCFUNC int2F_12_handler(struct int2f12regs FAR *pr) { @@ -1931,6 +1938,252 @@ VOID ASMCFUNC int2F_12_handler(struct int2f12regs FAR *pr) r.BX = size; return; } + else if (r.AH == 0x13) /* set disk interrupt (13h) handler */ + { + /* set new values for int13h calls, and must return old values */ + register intvec tmp = UserInt13; + UserInt13 = MK_FP(r.ds, r.DX); /* int13h handler to use */ + r.ds = FP_SEG(tmp); r.DX = FP_OFF(tmp); + tmp = BIOSInt13; + BIOSInt13 = MK_FP(r.es, r.BX); /* int13h handler to restore on reboot */ + r.es = FP_SEG(tmp); r.BX = FP_OFF(tmp); + return; + } + else if (r.AH == 0x16) /* Window/Multitasking hooks */ + { +#ifdef WIN31SUPPORT /* See "DOS Internals" or RBIL under DOSMGR for details */ + switch (r.AL) + { + /* default: unhandled requests pass through unchanged */ + case 0x0: /* is Windows active */ + case 0x0A: /* identify Windows version */ + { + /* return AX unchanged if Windows not active */ + break; + } /* 0x0, 0x0A */ + case 0x03: /* Windows Get Instance Data */ + { + /* This should only be called if AX=1607h/BX=15h is not supported. */ + /* The data returned here corresponds directly with text entries that + can also be in INSTANCE.386 [which in theory means Windows could + be updated to support FD kernel without responding to these?]. + */ + DebugPrintf(("get instance data\n")); + break; + } /* 0x03 */ + case 0x05: /* Windows Startup Broadcast */ + { + /* After receiving this call we activate compatibility changes + as DOS 5 does, though can wait until 0x07 subfunc 0x01 + */ + /* on entry: + DX flags, bit 0 is set(=1) for standard mode + DI Windows version#, major# in high byte + CX 0, set on exit to nonzero to fail load request + DS:SI is 0000:0000, for enhanced mode, at most 1 program can + set to memory manager calling point to disable V86 + ES:BX is 0000:0000, set to startup structure + */ + r.CX = 0x0; /* it is ok to load Windows, give it a shot anyway :-) */ + r.es = FP_SEG(&winStartupInfo); + r.BX = FP_OFF(&winStartupInfo); + winStartupInfo.winver = r.di; /* match what caller says it is */ + winInstanced = 1; /* internal flag marking Windows is active */ + DebugPrintf(("Win startup\n")); + break; + } /* 0x05 */ + case 0x06: /* Windows Exit Broadcast */ + { + /* can do nothing or can remove any changes made + specifically for Windows, must preserve DS. + Note: If Windows fatally exits then may not be called. + */ + winInstanced = 0; /* internal flag marking Windows is NOT active */ + DebugPrintf(("Win exit\n")); + break; + } /* 0x06 */ + case 0x07: /* DOSMGR Virtual Device API */ + { + DebugPrintf(("Vxd:DOSMGR:%x:%x:%x:%x\n",r.AX,r.BX,r.CX,r.DX)); + if (r.BX == 0x15) /* VxD id of "DOSMGR" */ + { + switch (r.CX) + { + /* default: unhandled requests pass through unchanged */ + case 0x00: /* query if supported */ + { + r.CX = winInstanced; /* should always be nonzero if Win is active */ + r.DX = FP_SEG(&nul_dev); /* data segment / segment of DOS drivers */ + r.es = FP_SEG(&winPatchTable); /* es:bx points to table of offsets */ + r.BX = FP_OFF(&winPatchTable); + break; + } + case 0x01: /* enable Win support, ie patch DOS */ + { + /* DOS 5+ return with bitflags unchanged, Windows critical section + needs are handled without need to patch. If this + function does not return successfully windows will + attempt to do the patching itself (very bad idea). + On entry BX is bitflags describing support requested, + and on return DX is set to which we can support. + Note: any we report as unhandled Windows will attempt + to patch kernel to handle, probably not a good idea. + 0001h: enable critical section signals (int 2Ah functions + 80h/81h) to allow re-entering DOS while InDOS. + 0002h: allow nonzero local machine ID, ie different VMs + report different values. + FIXME: does this mean we need to set this or does Windows? + 0004h: split up binary reads to increase int 2Ah function 84h scheduling + / turn Int 21h function 3Fh on STDIN into polling loop + 0008h: notify Windows of halting due to internal stack errors + 0010h: notify Windows of logical drive map change ("Insert disk X:") + */ + r.BX = r.DX; /* sure we support everything asked for, ;-) */ + r.DX = 0xA2AB; /* on succes DX:AX set to A2AB:B97Ch */ + r.AX = 0xB97C; + /* FIXME: do we need to do anything special for FD kernel? */ + break; + } + case 0x02: /* disable Win support, ie remove patches */ + { + /* Note: if we do anything special in 'patch DOS', undo it here. + This is only called when Windows exits, can be ignored. + */ + r.CX = 0; /* for compatibility with MS-DOS 5/6 */ + break; + } + case 0x03: /* get internal structure sizes */ + { + if (r.CX & 0x01) /* size of Current Directory Structure in bytes */ + { + r.DX = 0xA2AB; /* on succes DS:AX set to A2AB:B97Ch */ + r.AX = 0xB97C; + r.CX = sizeof(struct cds); + } + else + r.CX = 0; /* unknown or unsupported structure requested */ + break; + } + case 0x04: /* Get Instancing Exemptions */ + { + /* On exit BX is bit flags denoting data that is instanced + so Windows need not instance it. DOS 5&6 fail with DX=CX=0. + 0001h: Current Directory Structure + 0002h: System File Table and device status of STDOUT + 0004h: device driver chain + 0008h: Swappable Data Area + */ + r.DX = 0xA2AB; /* on succes DX:AX set to A2AB:B97Ch */ + r.AX = 0xB97C; + r.BX = 0; /* a zero here tells Windows to instance everything */ + break; + } + case 0x05: /* get device driver size */ + { + /* On entry ES:DI points to possible device driver + if is not one return with AX=BX=CX=DX=0 + else return BX:CX size in bytes allocated to driver + and DX:AX set to A2AB:B97Ch */ + mcb FAR *smcb = MK_PTR(mcb, (r.ES-1), 0); /* para before is possibly submcb segment */ + /* drivers always start a seg:0 (DI==0), so if not then either + not device driver or duplicate (ie device driver file loaded + is of multi-driver variety; multiple device drivers in same file, + whose memory was allocated as a single chunk) + Drivers don't really have a MCB, instead the DOS MCB is broken + up into submcbs, which will have a type of 'D' (or 'E') + So we check that this is primary segment, a device driver, and owner. + */ + if (!r.DI && (smcb->m_type == 'D') && (smcb->m_psp == r.ES)) + { + ULONG size = smcb->m_size * 16ul; + r.BX = hiword(size); + r.CX = loword(size); + r.DX = 0xA2AB; /* on succes DX:AX set to A2AB:B97Ch */ + r.AX = 0xB97C; + break; + } + r.DX = 0; /* we aren't one so return unsupported */ + r.AX = 0; + r.BX = 0; + r.CX = 0; + break; + } + } + } + DebugPrintf(("Vxd:DOSMGR:%x:%x:%x:%x\n",r.AX,r.BX,r.CX,r.DX)); + break; + } /* 0x07 */ + case 0x08: /* Windows Init Complete Broadcast */ + { + DebugPrintf(("Init complete\n")); + break; + } /* 0x08 */ + case 0x09: /* Windows Begin Exit Broadcast */ + { + DebugPrintf(("Exit initiated\n")); + break; + } /* 0x09 */ + case 0x0B: /* Win TSR Identify */ + { + DebugPrintf(("TSR identify request.\n")); + break; + } /* 0x0B */ + case 0x80: /* Win Release Time-slice */ + { + /* This function is generally only called in idle loops */ + DosIdle_hlt(); + r.AX = 0; + /* DebugPrintf(("Release Time Slice\n")); */ + break; + } /* 0x80 */ + case 0x81: /* Win3 Begin Critical Section */ + { + DebugPrintf(("Begin CritSect\n")); + break; + } /* 0x81 */ + case 0x82: /* Win3 End Critical Section */ + { + DebugPrintf(("End CritSect\n")); + break; + } /* 0x82 */ + case 0x8F: /* Win4 Close Awareness */ + { + if (r.DH != 0x01) /* query close */ + r.AX = 0x0; + /* else r.AX = 0x168F; don't close -- continue execution */ + break; + } /* 0x8F */ + default: + DebugPrintf(("Win call (int 2Fh/16h): %04x %04x %04x %04x\n", r.AX, r.BX, r.CX, r.DX)); + break; + } +#endif + return; + } + else if (r.AH == 0x46) /* MS Windows WinOLDAP switching */ + { +#ifdef WIN31SUPPORT /* See "DOS Internals" under DOSMGR or RBIL for details */ + if (r.AL == 0x01) /* save MCB */ + { + /* To prevent corruption when dos=umb where Windows 3.0 standard + writes a sentinel at 9FFEh, DOS 5 will save the MCB marking + end of conventional memory (ie MCB following caller's PSP memory + block [which I assume occupies all of conventional memory] into + the DOS data segment. + Note: presumably Win3.1 uses the WinPatchTable.OffLastMCBSeg + when DOS ver > 5 to do this itself. + */ + /* FIXME: Implement this! */ + } + else if (r.AL == 0x02) /* restore MCB */ + { + /* Copy the MCB we previously saved back. */ + /* FIXME: Implement this! */ + } +#endif + return; + } + /* else (r.AH == 0x12) */ switch (r.AL) { diff --git a/kernel/kernel.asm b/kernel/kernel.asm index 80748f6..77c6f8e 100644 --- a/kernel/kernel.asm +++ b/kernel/kernel.asm @@ -320,14 +320,22 @@ segment _LOWTEXT global _intvec_table _intvec_table: db 10h dd 0 + ; used by int13 handler and get/set via int 2f/13h + global _BIOSInt13 ; BIOS provided disk handler + global _UserInt13 ; actual disk handler used by kernel db 13h - dd 0 +_BIOSInt13: dd 0 db 15h dd 0 + ; used for cleanup on reboot + global _BIOSInt19 db 19h - dd 0 +_BIOSInt19: dd 0 db 1Bh dd 0 + ; default to using BIOS provided disk handler + db 13h +_UserInt13: dd 0 ; floppy parameter table global _int1e_table @@ -480,7 +488,7 @@ _winStartupInfo: dw instance_table,seg instance_table ; array of instance data instance_table: ; should include stacks, Win may auto determine SDA region ; we simply include whole DOS data segment - dw 0, seg _DATASTART ; [SEG:OFF] address of region's base + dw seg _DATASTART, 0 ; [SEG:OFF] address of region's base dw markEndInstanceData wrt seg _DATASTART ; size in bytes dd 0 ; 0 marks end of table dw 0 ; and 0 length for end of instance_table entry @@ -508,7 +516,9 @@ _winPatchTable: ; returns offsets to various internal variables _firstsftt: dd -1 ; link to next dw 5 ; count - + times 5*59 db 0 ; reserve space for the 5 sft entries + db 0 ; pad byte so next value on even boundary + ; Some references seem to indicate that this data should start at 01fbh in ; order to maintain 100% MS-DOS compatibility. times (01fbh - ($ - DATASTART)) db 0 diff --git a/kernel/main.c b/kernel/main.c index e4bb6c3..5099298 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -112,6 +112,7 @@ VOID ASMCFUNC FreeDOSmain(void) /* install DOS API and other interrupt service routines, basic kernel functionality works */ setup_int_vectors(); + /* check if booting from floppy/CD */ CheckContinueBootFromHarddisk(); /* display copyright info and kernel emulation status */ @@ -247,24 +248,27 @@ STATIC void setup_int_vectors(void) { 0x1, FP_OFF(empty_handler) }, /* single step */ { 0x3, FP_OFF(empty_handler) }, /* debug breakpoint */ { 0x6, FP_OFF(int6_handler) }, /* invalid opcode */ - { 0x19, FP_OFF(int19_handler) }, + { 0x19, FP_OFF(int19_handler) }, /* BIOS bootstrap loader, vdisk */ { 0x20, FP_OFF(int20_handler) }, - { 0x21, FP_OFF(int21_handler) }, + { 0x21, FP_OFF(int21_handler) }, /* primary DOS API */ { 0x22, FP_OFF(int22_handler) }, { 0x24, FP_OFF(int24_handler) }, - { 0x25, FP_OFF(low_int25_handler) }, + { 0x25, FP_OFF(low_int25_handler) }, /* DOS abs read/write calls */ { 0x26, FP_OFF(low_int26_handler) }, { 0x27, FP_OFF(int27_handler) }, { 0x28, FP_OFF(int28_handler) }, { 0x2a, FP_OFF(int2a_handler) }, - { 0x2f, FP_OFF(int2f_handler) } + { 0x2f, FP_OFF(int2f_handler) } /* multiplex int */ }; struct vec *pvec; struct lowvec FAR *plvec; int i; - for (plvec = intvec_table; plvec < intvec_table + 5; plvec++) + /* save current int vectors so can restore on reboot and call original directly */ + for (plvec = intvec_table; plvec < intvec_table + 6; plvec++) plvec->isv = getvec(plvec->intno); + + /* install default handlers */ for (i = 0x23; i <= 0x3f; i++) setvec(i, empty_handler); HaltCpuWhileIdle = 0; @@ -273,7 +277,7 @@ STATIC void setup_int_vectors(void) pokeb(0, 0x30 * 4, 0xea); pokel(0, 0x30 * 4 + 1, (ULONG)cpm_entry); - /* these two are in the device driver area LOWTEXT (0x70) */ + /* handlers for int 0x1b and 0x29 are in the device driver area LOWTEXT (0x70) */ setvec(0x1b, got_cbreak); setvec(0x29, int29_handler); /* required for printf! */ }