Repository: Baron-von-Riedesel/Jemm Branch: master Commit: 87055a625835 Files: 197 Total size: 1.2 MB Directory structure: gitextract_t40dgl9z/ ├── Artistic.txt ├── History.txt ├── Html/ │ └── Readme.html ├── Include/ │ ├── FILEACC.INC │ ├── FINDRES.INC │ ├── JLM.H │ ├── JLM.INC │ ├── JLMFASM.INC │ ├── JSYSTEM.INC │ ├── PRINTF.INC │ ├── VDS.INC │ └── X86.INC ├── JLM/ │ ├── AHCICD/ │ │ ├── AHCICD.ASM │ │ ├── AHCICD.txt │ │ ├── MAKE.BAT │ │ └── MAKEM.BAT │ ├── GENERIC/ │ │ ├── GENERIC.ASM │ │ ├── MAKEFILE │ │ ├── README.TXT │ │ └── TESTGEN.ASM │ ├── HELLO/ │ │ ├── HELLO.ASM │ │ ├── MAKE.BAT │ │ └── README.TXT │ ├── HELLO2/ │ │ ├── HELLO2.C │ │ ├── HELLO2W.C │ │ ├── JLMW.ASM │ │ ├── JLMW.h │ │ ├── MAKEJLMW.BAT │ │ ├── MAKEOW.BAT │ │ ├── MAKEVC.BAT │ │ ├── README.TXT │ │ └── makeow.sh │ ├── IOTRAP/ │ │ ├── IOTRAP.ASM │ │ ├── MAKEFILE │ │ ├── README.TXT │ │ └── TESTIOT.ASM │ ├── JCLOCK/ │ │ ├── JCLOCK.ASM │ │ ├── JCLOCK2.ASM │ │ ├── JCLOCK3.ASM │ │ ├── MAKE.BAT │ │ ├── MAKE3.BAT │ │ └── README.TXT │ ├── JLSTUB/ │ │ ├── Build/ │ │ │ └── JLSTUB.lst │ │ ├── JLSTUB.ASM │ │ ├── JLStub.txt │ │ └── Make.BAT │ ├── KEYBGR/ │ │ ├── KEYBGR.ASM │ │ ├── MAKE.BAT │ │ ├── MAKEFILE │ │ └── README.TXT │ ├── QPIEMU/ │ │ ├── MAKE.BAT │ │ ├── MAKEFILE │ │ ├── QPIEMU.ASM │ │ ├── QPIEMU.txt │ │ └── Test/ │ │ ├── MAKEFILE │ │ ├── PRINTF16.INC │ │ ├── SETARGV.INC │ │ ├── TESTDMA.ASM │ │ └── TESTQPI.ASM │ ├── README.TXT │ ├── REBOOT/ │ │ ├── FASTBOOT.ASM │ │ ├── FASTBOOT.txt │ │ ├── FASTBRM.ASM │ │ ├── FBOOT.ASM │ │ ├── LDI13EXT.ASM │ │ ├── MAKE.BAT │ │ ├── REBOOT.ASM │ │ └── REBOOT.txt │ ├── XCDROM32/ │ │ ├── History.txt │ │ ├── MAKE.BAT │ │ ├── MAKEM.BAT │ │ ├── XCDROM32.ASM │ │ └── XCDROM32.txt │ └── XDMA32/ │ ├── History.txt │ ├── IDENTIFY.INC │ ├── MAKE.BAT │ ├── MAKEM.BAT │ ├── XDMA32.ASM │ └── XDMA32.txt ├── JemmExL.mak ├── Linux.mak ├── Makefile ├── Readme.txt ├── Test/ │ ├── BMINTRM.ASM │ ├── EMS4E.ASM │ ├── EMS4F.ASM │ ├── EMS56.ASM │ ├── EMS57.ASM │ ├── EMS57A.ASM │ ├── EMS5B.ASM │ ├── EXC00.ASM │ ├── EXC06.ASM │ ├── FRAMERES.ASM │ ├── HLTTEST.ASM │ ├── HLTTEST2.ASM │ ├── I15MOVE.ASM │ ├── INT67.ASM │ ├── INT88.ASM │ ├── MACROS.INC │ ├── MAKE.BAT │ ├── PRINTF.INC │ ├── README.TXT │ ├── TEST0C.DEB │ ├── TEST0C1.DEB │ ├── TEST0D.DEB │ ├── TEST10.DEB │ ├── TEST11.DEB │ ├── TESTDMA.ASM │ ├── TESTDMA2.ASM │ ├── TESTDMA3.ASM │ ├── TESTVCPI.ASM │ ├── TESTVDS.ASM │ ├── TESTVDS2.ASM │ ├── TESTVDS3.ASM │ ├── TIMERMS.INC │ ├── VCPI.INC │ ├── XMSTEST.ASM │ ├── XMSTEST2.ASM │ ├── XMSTEST3.ASM │ ├── XMSTEST4.ASM │ └── XMSTEST5.ASM ├── Tools/ │ ├── CLEAR.BAT │ ├── CPUID/ │ │ ├── CPUID.ASM │ │ ├── MAKE.BAT │ │ └── PRINTF.INC │ ├── Cpustat/ │ │ ├── CPUSTAT.ASM │ │ ├── MAKE.BAT │ │ └── PRINTF.INC │ ├── EMSSTAT/ │ │ ├── EMSSTAT.ASM │ │ ├── MAKE.BAT │ │ └── PRINTF.INC │ ├── JEMFBHLP/ │ │ ├── JEMFBHLP.ASM │ │ └── MAKE.BAT │ ├── JLOAD/ │ │ ├── DEBUG.INC │ │ ├── DPRINTF.INC │ │ ├── DPRNTF16.INC │ │ ├── History.txt │ │ ├── JLOAD.ASM │ │ ├── JLOAD.INC │ │ ├── JLOAD.TXT │ │ ├── JLOAD.txt │ │ ├── JLOAD32.ASM │ │ ├── JLOAD32.INC │ │ ├── Linux.mak │ │ ├── MAKEFILE │ │ ├── VDMAD.ASM │ │ ├── VIOOUT.INC │ │ ├── VMM.ASM │ │ ├── WINNT.INC │ │ └── license.txt │ ├── MAKE.BAT │ ├── MEMSTAT/ │ │ ├── MAKE.BAT │ │ ├── MEMSTAT.ASM │ │ └── PRINTF.INC │ ├── MoveXBDA/ │ │ ├── MAKE.BAT │ │ └── MOVEXBDA.ASM │ ├── UMBM/ │ │ ├── MAKE.BAT │ │ └── UMBM.ASM │ ├── VCPI/ │ │ ├── MAKE.BAT │ │ ├── PRINTF.INC │ │ ├── VCPI.ASM │ │ ├── VCPI.INC │ │ └── VIOOUT.INC │ └── XMSSTAT/ │ ├── MAKE.BAT │ ├── PRINTF.INC │ └── XMSSTAT.ASM ├── XMS35.txt └── src/ ├── AUXIO.INC ├── DEBUG.ASM ├── DEBUG16.INC ├── DEBUG32.INC ├── DEBUGSYS.INC ├── DEV.ASM ├── DMA.INC ├── DPRINTF.INC ├── DPRNTF16.INC ├── EMS.ASM ├── EMS.INC ├── EMS32.INC ├── EMU.ASM ├── EXTERN32.INC ├── I15.ASM ├── INIT.ASM ├── INIT16.ASM ├── JEMM.INC ├── JEMM16.ASM ├── JEMM16.INC ├── JEMM32.ASM ├── JEMM32.INC ├── POOL.ASM ├── UMB.ASM ├── VCPI.ASM ├── VCPI.INC ├── VDMA.ASM ├── VDS.ASM ├── XMS.ASM └── XMS.INC ================================================ FILE CONTENTS ================================================ ================================================ FILE: Artistic.txt ================================================ The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End ================================================ FILE: History.txt ================================================ History __.__.____: v5.87 - fixed regression in v5.86: exceptions in PL0 were displayed incorrectly. - fixed: NMIs that occured while no full stack frame was setup caused a crash. - segment registers DS/ES/FS/GS and [CS:IP] now also displayed for PL0 exceptions. - NMIs are masked while NMI exceptions are handled. - fixed: Simulate_Int() cleared bits 16-31 of register ESP. - JemmEx fix: if option XMSHANDLES was > 59 and Jemm wasn't allowed to move its resident part into the upper memory region, it crashed. - Linux.mak: GNU makefile added to create Jemm binaries on Linux. - fixed JemmEx: unknown A20METHOD value caused a crash. - AHCICD: driver now supports long reads in "raw mode". 25.01.2026: v5.86 - service table: added control proc member to allow Jemm to call out on certain events; currently "device_reboot_notify" is the only call-out. - service table: added v86faults member - allows programs to hook into v86 faults; used by JLOAD. - Simulate_Int: call an installed v86 hook proc before running the INT. - JemmEx, XMS AH=0Eh: invalid handle error returned if size > 65535 kB; this is MS Himem compatible. - JLMs Reboot & FastBoot added. - fixed debug displays in 16-bit part. - fixed debug displays in Jemm16.asm, UnloadJemm. - JemmEx: option NOE820 reintroduced; it's a noop unless MAXSEXT is 0. - fixed option UNLOAD: on 80386/80486 cpus, it wasn't safe. - JLM QPIEmu: added function ax=5000h. - reintroduced compatibility with SB emulation drivers SB(E)INIT for SB PCI cards by extending functionality of cmdline option SB. - NMI stability increased. 10.01.2025: v5.85 - fixed: VDS func ax=810Ch: set ZF if disable cnt reaches zero. - fixed: VDS: added several argument checks ( mostly DX ). - fixed: ISA DMA ports 000A-000F must be handled by Jemm to detect FD accesss even if external handler has trapped these ports. - fixed: it was assumed that value for TR to be loaded in VCPI function ax=DE0Ch ( switch from v86- to protected-mode) had bits 0-2 cleared. - linear address region 110000-3FFFFF is now only mapped as far as needed by the monitor program (looses SB(E)INIT compatibility). - optional kernel-debugger-aware variant calls int 68h for deinitialization. - cmdline switch NOVMW added. - easier log displays via macro @dprintf. - log displays redirected to kernel debugger if one has been detected. - buffered output of log displays removed. - switch ?SAFEKBD=1 is now default for JemmEx (restores IVT vector 15h before reading a key press via int 16h during system errors). - JemmDbg renamed to JDeb386 and removed from Jemm source package. - building JemmExL.exe removed from Makefile; to create it, use JemmExL.mak. - CPUSTAT: optionally displays interrupt redirection bitmap. 12.02.2024: v5.84 - fixed: Simulate_IO() no longer calls trap handler. - QPIEMU: new JLM that partly implements Qemm's API (QPI). - fixed: int 67h, ax=5B01h will return error code A3h if checksum invalid. - JemmDbg: removed from binary package. 06.12.2022: v5.83 - AHCICD: new JLM for CD/DVD devices attached to a SATA controller running in AHCI mode (cooked mode only). - XDMA32, XCDROM32: now accepting "AHCI-disabled" SATA controllers. - JemmEx: warning displayed if I15 memory blocks had to be ignored due to insufficient XMS "static" handles (8); static handles increased to 10. - JemmDbg: a PL0 debugger, based on Debug/X; may be used to debug JLMs. - Makefile supports creating variants of Jemm386/JemmEx that are kernel debugger aware. - CPUSTAT: optionally displays IOPB (trapped ports). 09.08.2022: v5.82 - JemmEx: minor adjustment to always keep monitor stack dword-aligned. - fixed: MOVEXBDA option didn't check that XBDA was adjacent to conv. mem. - JLoad: fixed: hook int 2Fh (used to get JLM API entry) was incorrect. - CPUID: fixed: feature information incorrect. - CPUSTAT: fixed: options -g and -i if physical != linear address. - XDMA32: fixed: did reject many disks if /M option wasn't set. 12.04.2022: v5.81 - JemmEx: XMS extension v3.51 (super-extended block moves) implemented. 18.06.2021: v5.80 - for exceptions other than page faults, the monitor will display register CR4 instead of CR2, if it exists. - Jemm might have reduced last MCB by 16 byte when loaded while UMBs where already available and used by DOS. - Jemm does again scan the region C000-EFFF for RAM. This avoids a system crash if upper memory has been supplied by UMBPCI and is used by DOS. - new option MOVEXBDA for Jemm implemented. - JemmEx will recognize memory blocks activated by UMBPCI and include them into its UMB pool. Makes option S=start-end virtually obsolete. - a warning is displayed if the extended memory block where Jemm386 will reside is beyond the 16MB limit. - strategy to realloc an EMB to increase its size has been changed and is now smart enough to check if the block can be increased without moving it. - UMBM no longer requires the /I parameter; it will scan for blocks with the UMBPCI signature. - UMBM has new option /XBDA. - UMBM now acts gracefully if line DOS=UMB is missing in CONFIG.SYS. - MOVEXBDA has got option /A to make it align the new XBDA to a kB boundary. - tool CPUSTAT split into CPUSTAT and CPUID. - emulation of Int 15h, ah=87 (move extended memory) has been enhanced to allow to access physical memory beyond the 4 GB border via PSE-36. - JemmEx XMM now implements XMS v3.5 (extended memory > 4 GB). Default XMS handles changed to 48 (previously 32). Added MAXSEXT option for JemmEx, and also added JemmExL.exe, that still supports XMS v3.0 only. - JemmEx: return DX=0 if XMS alloc functions fail (XMS spec). - option I=TEST does now indeed what's documented. Previously it only scanned regions of "external" ROMs ( i.e., the ROM of the VGA card ). This means it's more "aggressive" now. - ALTBOOT option sends "system reset" command to keyboard controller. 02/02/2020: v5.79 - if physical address of DMA buffer was beyond 16M, its size was reset to zero. This is no longer done. - default linker is now JWlink. - debug displays added for mode switch calls. - Jemm will now use info returned by Int 15h, ax=E820h to mark regions in the upper memory area as reserved. If the call was successful, Jemm won't scan the memory region C000-EFFF for RAM. - VME (virtual mode extensions) is now off by default; there exist CPUs that claim to support VME but actually don't. - JemmEx's built-in XMS host will now use up to 8 memory blocks listed by ACPI (int 0x15, ax=0xe820). Previously it could just use the one with base address 0x100000. - fixed: shrinking an EMB didn't work correctly. 07/15/2012: v5.78 - bugfix: if SPLIT option was set and there were 2 ROMs to be split on adjacent 4K-pages, the latter one may have been overwritten with zeros (problem found in Qemu v0.15.1). - bugfix: MOVEXBDA didn't restore the XBDA at reboot. This may have been a problem if the FASTBOOT option was active. - a warning is displayed if the page frame isn't set to a segment address divisible by 0x400 without remainder ( i.e, FRAME=DF00 will trigger the warning ). - MEMSTAT additionally displays location and size of XBDA. - source package only: extrsect.exe replaced by bin2inc.exe; bin2inc.exe is needed only if Masm is used to assemble the source. 06/20/2012: v5.77 - commandline option I=A000-B7FF didn't increase DOS low memory, the region became just the first UMB. - tool MOVEXBDA added. 05/25/2012: v5.76 - an 64kB EMS page frame may now be located inside address space included with 'I=XXXX'. 05/21/2012: v5.75a - no changes in JemmEx/Jemm386, just bugfixes in XDMA32.DLL and XCDROM32.DLL. 05/31/2011: v5.75 - bugfix: when Jemm detected Ctrl-Alt-Del, it didn't reset the keyboard status. This may have caused a locked keyboard if FASTBOOT was set. 04/14/2011: v5.74 - tool MEMSTAT added, which will display the size of the reserved BIOS-ROM region. - Make process simplified, all sources are assembly now. 12/02/2010: v5.73 - bugfix: int 67h, ah=51h didn't complain if handle was invalid in v5.61-5.72. - tool CPUSTAT added. - for emulation of Int 15h, AH=87h (extended memory block move), the region 100000h-10FFFFh is now handled similar to the rest of extended memory. Previously it was handled as "real-mode" memory, which means that access did depend on the A20 state. This new behavior matches more closely the BIOS implementation of this interrupt. - option V86EXC0D added. 03/13/2008: v5.72 - source has been made compatible with JWasm; OTOH, TASM compatibility has been abandoned. 02/25/2008: v5.71 - bugfix: DMA buffer size of 0 (D=0) didn't work in v5.70. - bugfix: JLOAD v5.70 made wrong assumptions about Jemm's TSS location, which caused JLM sample JCLOCK2 to fail. - some minor changes to make it compatible with JWasm. 01/30/2008: v5.70 - bugfix: the "copy memory" routine wasn't reentrant, making an XMS "block move" or a call of Int 15h, ah=87h from within an ISR slightly unsafe. - bugfix: int 67h, ah=57h returned error 8Ah instead of 93h when a region exceeded the size of the EMS handle. - bugfix: int 67h, ah=57h accepted any region type (just 00 and 01 are valid ones). - bugfix: int 67h, ax=5701h accepted overlapping EMS regions. Now status 97h is returned. - bugfix: since v5.61 (support for mappable pages in conventional memory) option NODYN often failed to make all memory available which was requested with the MIN=x option. - bugfix: exception 11h in v86-mode caused a loop in Jemm's exception handler. - bugfix: due to an alignment problem the low byte of max. free memory block for XMS v2 (X2MIN) was 0, as a result the default block size was 65280 (0xFF00) instead of 65535 kB (JemmEx only, bug since v5.64). - int 67h,ax=5701h, "memory exchange" is now also done with interrupts enabled if client's interrupt flag is set. - port 60h access is now trapped only if the keyboard controller's output port is accessed. - tool XMSSTAT and source of EMSSTAT added. 01/07/2008: v5.69 - descriptions/changelogs for XDMA32 and XCDROM32 extracted from this readme into separate files XDMA32.TXT and XCDROM32.TXT. - memory block copies are now done with interrupts enabled - if client's interrupt flag is set. This avoids interrupt latency and makes the previously implemented "interrupt windows" obsolete. It also avoids problems with Jemm's HLT emulation causing IRQs to be misinterpreted as exceptions (08, 09, 74, ...) on some cpus. - bugfix: VDS scatter/gather lock with region size 0 returned 0 entries, but 1 entry must be returned in any case (MS Emm386 does as well). - bugfix: there was a risk that a ROM located contiguously behind another ROM in address space wasn't detected. - bugfix: if SPLIT option was set and a split ROM page followed an UMB page (which is possible if ROM size is < 4 kB), the ROM part of this page was added to the UMB. - bugfix: synchronization of VCPI and EMS free memory count did work for VCPI only, that is, allocating all VCPI memory didn't reduce free EMS memory to zero. - bugfix: the last XMS memory block allocated for EMS/VCPI memory was likely to be too large, thus wasting XMS memory. - VCPI/EMS memory alloc/release speed-up. - EMSSTAT -v displays more VCPI infos. - v86 exceptions >= 8 are routed to v86 interrupt 06h only if this vector has been changed by another program. - exception 10h (floating point) is now detected and displayed, thus avoiding the hazardous call of video interrupt 10h (MS Emm386 also detects exception 10h, but just allows to reboot then). 12/03/2007: v5.68 - bugfix: JLOAD's "Lock DMA" function ignored flag to check for 64 kB border crossing. - NMIs occuring inside the monitor are now silently routed to v86-mode. Previous Jemm versions displayed an exception dump. - XDMA32 v1.0 added. - XCDROM32 changed to v1.1. 10/13/2007: v5.67 - bugfix: I=TEST always scanned 4 kB chunks, although the last page of a ROM might be smaller (512, 1024, ... bytes). - bugfix: if I=TEST found an "unused" page in a ROM, v5.64-5.66 messed up an internal counter. As a result, there was a small risk that the begin of another ROM following this one in the address space wasn't detected. - error code displayed in exception dump. - selected A20 method now displayed in "verbose" mode only. - tool EMSSTAT added. 08/24/2007: v5.66 - bugfix: GDT, which has been moved to nonshared space in v5.6, moved to shared space again. Required by applications written for the Phar Lab TNT DOS extender. 08/19/2007: v5.65 - Standard reboot handler made fully MS Emm386 compatible. The ALTBOOT option is now a dummy. - auto-scanning for a page frame will now ensure that the frame begins on a 16 kB boundary. This is MS Emm386 compatible. 07/12/2007: v5.64 - bugfix: for FASTBOOT, IVT vectors 78h-7Fh should be reset to 0000:0000. This wasn't done. - SPLIT option added. 06/22/2007: v5.63 - bugfix: NOEMS didn't disable EMS in v5.62 - bugfix [JemmEx only]: MAXEXT option wasn't displayed in help screen. 06/19/2007: v5.62 - bugfix: int 67h, ah=5301h (set handle name) didn't check if this name was already assigned (and return status A1h if yes). - bugfix: using I=TEST might have reduced amount of UMBs by 4 kB. - bugfix: in v5.61, Int 67h, ax=5900h still returned 8 as mapping context size, which usually is too small now (# of physical pages are > 4). - bugfix: in v5.61, if Jemm was loaded from the command line and remained in low memory, it didn't hook into the driver chain. - bugfix: in v5.61, there was a risk that Jemm386 didn't install an UMB handler (and then the UMBs were "lost"). - int 67h, ah=5Bh implemented (alternate map register sets). - the EMS system handle is now populated with the mappable pages below A000h, as described in EMS 4.0 docs (and also done by MS Emm386). 06/04/2007: v5.61 - bugfix: Int 67h, ah=51h (EMS reallocate) might have failed to increase the handle's pages although there were enough free pages available. This was a design flaw. Now for each EMS handle a linked lists of allocated pages is maintained. - bugfix: trying to release the NULL handle will now reallocate it with size 0. This is the behavior specified in the EMS docs. - bugfix: there was a chance that 4 VCPI pages got lost. They could be allocated, but weren't found anymore when trying to release them. - bugfix: memory for UMBs is no longer contained in EMS system handle 0. This handle should only contain pages which "backfill" conventional memory (segment addresses below A000h). - bugfix: int 67h, ah=56h (alter page map and call) ignored the "old" member in the "map_and_call" parameter. - value of GDTR, IDTR and TR exposed by Jemm. This allows JLoad to avoid usage of opcodes SGDT/SIDT, which caused problems with most virtualizers. - allocating a 4kB page with VCPI is now significantly faster. - B=xxxx parameter supported. Pages 4000-9FFF are mappable by default. 05/24/2007: v5.6 - bugfix: if Jemm had to emulate an IRET in v86-mode and the copy of the flag register onto the stack was "invalid" (IOPL != 3), a GPF occured at the next IO-sensitive instruction in v86-mode. - bugfix [JemmEx only]: NODYN option caused a GPF on initialization. - dword and string I/O now simulated correctly for trapped port access. - GDT and IDT moved to nonshared memory. - the ring 0 stack has been increased to 4 kB and moved to nonshared space. This allows "nested execution" without using the client's v86-stack. - JLOAD.EXE added. Jemm splitted in 2 packages: binaries and source. Some sample JLMs added (XCDROM32, KEYBGR, ...). - new EMMXXXX0 function exposes some of Jemm's internal call tables, thus allowing external modules (JLOAD) to implement a 32bit ring 0 API. - switched to a table-oriented IO-port trapping implementation, which allows external modules (JLOAD) to hook/take over port trapping. 05/04/2007: v5.52 - bugfix: a bug introduced in v5.5 caused garbage to be displayed if option I=B000-B7FF was used. 05/03/2007: v5.51 - bugfix [JemmEx only]: XMS memory realloc function (ah=0Fh) caused the stack not to be dword-aligned, which slows things down and might cause troubles if an interrupt occurs during a memory block move. - JemmEx: EMS block moves (int 67h, ah=57h) now also done with interrupt window if client's IF is set. 04/29/2007: v5.5 - bugfix: if Ctrl-Alt-Del was pressed with FASTBOOT active and an event proc for the PS/2 mouse was set, a crash might have occured. Now the PS/2 mouse is reset by Jemm before rebooting. - bugfix: S=XXXX was ignored if it was the only region to be added as UMBs. - in v5.46, FASTBOOT only worked by pressing Ctrl-Alt-Del. Now it should work as well if int 19h is called. - in v5.46, FASTBOOT always needed ALTBOOT to be set and therefore has set this option internally. This is no longer done. - JEMMEX.EXE added. - JemmEx opens interrupt window in block moves (int 15h, ah=87h and XMS function ah=0Bh) each 8 kB if client's IF is set. 03/27/2007: v5.46 - bugfix: free EMS pages not always reported correctly. - bugfix: translation DMA for 16-bit controller (channels 4-7) didn't work. - bugfix: Jemm always displayed "can't continue, please reboot" on invalid opcode exceptions in v5.45. - MIN= no longer restricted to a maximum of 512 MB (EMS pages still are). - FASTBOOT option implemented, JEMFBHLP.EXE added. 03/02/2007: v5.45 - bugfix: in v5.40, if no option was entered when installing Jemm, it might have displayed some garbage. - bugfix: invalid opcode generated by Jemm if running on a cpu < 80386. - bugfix: XMS function 11h (free UMB) always returned an error. - option UNLOAD added. - option RAM/NORAM implemented. - valid range for page frame set with FRAME=xxxx now 8000-E000. - the MAX= prefix can be omitted now. Makes the cmdline more MS Emm386 compatible. - memory required for UMBs will no longer reduce the memory available for EMS. This is similiar to what MS Emm386 does. - installing Jemm from the command line now prohibited if a DPMI host is detected. 02/20/2007: v5.4 - some minor bugfixes. - Jemm now moves its resident part to the first UMB which it supplies. - NOHI option implemented. - Jemm now uses the true (=zero-based) flat memory model. 01/11/2007: v5.34 - bugfix: Jemm always hooked int 15h, ah=4Fh, thus always acting as if option ALTBOOT was set (bug introduced in v5.25). 01/06/2007: v5.33 - bugfix: Jemm erroneously might have disabled EMS - reboot code slightly changed, there were still some machines (old 80486?) were Ctrl-Alt-Del didn't work. 11/13/2006: v5.32 - removed an optimisation which PKZIP didn't like. 11/09/2006: v5.31 - some minor bugfixes in function int 67h, ah=57h. - bugfix: wrong register contents were displayed in v5.30 for exceptions occuring in protected-mode. - v86-mode int 06 handler moved to protected-mode, thus further reducing Jemm's conventional memory usage. 10/27/2006: v5.30 - bugfix: int 67h, ah=57h (move/exchange memory region) didn't always invalidate the TLB cache for the EMS memory to copy/exchange. - bugfix: int 67h, ax=4F01h didn't work. - int 67h, ah=55h/56h implemented. - int 67h, ah=57h now works with overlapping memory regions. Furthermore it is now ensured that all EMS pages involved in the copy operation are valid before copy actually begins. 10/24/2006: v5.29 - bugfix: the error msg when the EMS call to map UMBs failed contained garbage as "function". - int 67h, AH=58h will now return success even with NOEMS. - now error code 80h, not 91h is returned if functions int 67h, ah=41h or 47h or 48h are called and no page frame is defined. - NOINVLPG option added. - I=TEST is now a bit less restrictive and will increase compatibility with MS-Emm386. - help text displayed by Jemm adjusted where it was wrong or unclear. 10/13/2006: v5.28 - bugfix, VDS function scatter/gather lock (int 4Bh, ax=8105h): if lower 12 bits of offset in EDDS were <> 000h, this function always returned the first page as a separate region. - bugfix, VDS function scatter/gather lock: if this function failed with AL=9, it did not always set the correct value for items required to describe the full region. - bugfix: int 67h, ah=53h (get/set handle name) and ah=54h (get handle directory) wrongly assumed that they won't be called with handles other than 0000 if NOEMS is set. - bugfix: int 67h, ah=57h (move/exchange memory region) didn't work without a page frame (NOEMS or FRAME=NONE). 10/07/2006: v5.27 - bugfix: allocating a EMS block with zero pages (int 67h, ah=5Ah, bx=0) returned with AH=0, but did not return a valid handle in DX. - bugfix: calling int 67h, AH=51h modified AL register. 09/30/2006: v5.26 - bugfix: VDS functions 03, 04, 07 and 08 may have failed if bit 1 of DX was set (request to copy in/out of DMA buffer) and registers BX,CX were <> 0. - bugfix: 1 kB of DMA buffer may have been inaccessible. - bugfix: releasing a DMA buffer with size > 0 and < 1 kB always failed. 09/22/2006: v5.25 - bugfix: if option ALTBOOT is set and Ctrl-Alt-Del being pressed has been detected, then the real-mode int 15h chain is called first before rebooting. This gives other hookers (SmartDrv) a chance to cleanup. 09/19/2006: v5.24 - bugfix: int 67h, ax=5001h (implemented in v5.2) didn't work. - bugfix: setting Jemm options from the command line did reset the options which were not explicitely entered. - PGE/NOPGE options added. 09/14/2006: v5.23 - bugfix: if the amount of preallocated EMS memory (MIN=xxxx) was not below largest XMS memory block - 384 kB, the amount of free/ available EMS pages reported by Jemm was up to 384 kB too large. - implemented VDS function 05 (scatter lock) returning PTEs instead of "regions". This makes Jemm fully support the VDS API. - some message texts adjusted to make it clear what is an error, a warning or just informational. - warning about detected system memory region now displays all such regions, start and size. - if wanted EMS memory has to be reduced, a warning is displayed. - UMBM.EXE added. - EMX option added. 09/09/2006: v5.22 - bugfix: the DMA buffer 64kb alignment may cause up to 32 kB to be lost for EMS/VCPI memory. This wasn't taken into account, and may have resulted in Jemm unable to alloc UMBs if options NODYN and MIN= were set. - bugfix: in displayed text "wanted preallocated EMS memory kB" was always blank. - space for UMBs now rounded up to next 16 kB boundary (previously it was rounded to 32 kB). 09/07/2006: v5.21 - bugfix: in v5.2 if an invalid command line parameter was entered, the program displayed garbage. - bugfix: in v5.2 options setting both options NOEMS and NOVCPI might have caused Jemm to run out of memory for UMBs. - XMS dynamic memory allocation was automatically disabled if NOEMS and NOVCPI options were set, but this is no longer useful since option NODYN exists and VCPI can be dynamically set/reset from the command line. - DMA buffer size now always rounded up to a full 4 kB boundary. - LOAD option added which allows to install Jemm from the command line. 09/06/2006: v5.2 - bugfix: writing to "ROM" page FF000 if ALTBOOT wasn't set caused a crash. - bugfix: if XMS handle array was in HMA and A20 got disabled results may have been "unexpected". - bugfix: "disable A20 local" emulation did not return an error code if count was already 0. - there was slightly more XMS memory allocated than required. - support for Int 67h, ax=5001h implemented. - option MIN= no longer deactivates XMS dynamic memory allocation. option NODYN added, option EMM= discarded. - If XMS memory must be preallocated (because XMS host doesn't export its handle table) and MIN= option is not set, Jemm allocates half of extended memory (but not more than 32 MB) for EMS/VCPI memory. - Assembler: MASM may be used instead of TASM. - Compiler: Open Watcom WCC or MS VC 1.5 may be used instead of TC. - Linker: Digital Mars OPTLINK may be used instead of TLINK. - protected-mode code switched to 32-bit. - port trapping added to the A20 enable/disable emulation. XMS hook is still required due to the "A20 always on" case, which causes most XMS hosts not to touch the ports at all. 08/30/2006: v5.1 - bugfix: the DMA master mask register was read, which doesn't work on all machines and may have caused an "illegal instruction exception" if the floppy disk was accessed. This bug was introduced in v5.00. - bugfix in EMS function 4Eh. - bugfix: A20 emulation works now if no UMB handler has been installed. - DMA buffer size may be set by commandline option. - VDS functions 07/08 (request/release DMA buffer) and 09/0A (copy into/out of DMA buffer) implemented. VDS functions 03/04 (lock/unlock region) extended so that they call functions 07/08 if needed. - NOVCPI does no longer require NOEMS. - ALTBOOT (re)implemented, it hooks int 15h, ah=4Fh now. - VME mode may now be set/reset with VME/NOVME options. - LIBM.LIB no longer needed to create the JEMM386.EXE binary. 08/25/2006: v5.01 - bugfix: "A20 enabled"-count was 0 on startup, should have been 1. - bugfix: unsupported VDS functions caused a debug display, which didn't work and may have caused corruption of monitor data. Debug displays removed. - bugfix: original int 4Bh vector may have been 0000:0000 - if this is true, it must never be chained to of course. Instead carry flag is set. - VDS functions 0Bh and 0Ch (disable/enable translation) implemented. 08/23/2006: v5.00 - EMMXXXX0 device communication implemented - bugfix: DMA buffer is ensured to begin on a 64 kB physical address boundary. - bugfix: XMS handles which are no longer used now get the "in pool" flag set, not the "free" flag. - Pentium+ VME extensions supported. - A20 disable emulation activated. 08/17/2006: v5.00pre - bugfix: in VCPI protected mode entry switch to host stack before context is switched. - command line option S=xxxx added. - option NOALTBOOT deleted. - the full physical memory is no longer mapped in the address space. This reduces extended memory usage a lot on machines with large amounts or RAM, since no page tables are needed. - VDS code moved to extended memory, reducing conventional memory usage by about 1 kB. --------------------------------------------------------------------- c't/Harald Albrecht: created the v86 monitor part and EMS functions 1990 tom ehlert: implemented UMB support, 2001-2004 reboot hooks (keyboard ctrl-alt-del, BIOS FFFF:0000) int 15h ext memory copy, VDS lock function, introduced english comments. michael devore: implemented support for VCPI, VDS (partially), dynamic 2004-2006 XMS memory allocation, EMS v4.0 (partially) - Modified for >64M and VCPI support - Updated for EMS 4.0, some extended API returns unsupported, this will change as requested or needed by other software requirements - Documented EMS 4.0 only supports up to 32M (0x800 pages), but some EMS drivers allow more than 32M. The EMS 4.0 spec could be extended to 1G-32K (map value of 0xFFFF means unmap) without breaking the spec API, but unfortunately breaking unsophisticated programs which get confused. I have arbitrarily decided to allow up to 32M of EMS allocations, leaving the high bit of page allocation alone since specific drivers may make use of high bit values other than 0xFFFF for their own purposes, or may only check high bit for unmap -- as FreeDOS EMM386 does. - Minor English corrections where useful for comprehension Michael Devore's changes are not copyrighted and are released to the public domain. This does not affect copyright on the rest of the code. ================================================ FILE: Html/Readme.html ================================================

 Contents

  1.   About Jemm
  2.   Features
  3.   Commandline Options
  4.   Technical Details
  4.1  EMS Implementation Notes
  4.2  Emulation of privileged Opcodes
  4.3  IOPL Sensitive Instructions
  4.4  VMWare Detection
  4.5  Option FASTBOOT
  4.6  Option MAXSEXT
  4.7  Option NOEMS
  4.8  Option SB
  5.   Compatibility
  6.   Errors and Warnings
  7.   Troubleshooting, Hints
  8.   Additional Tools
  8.1  UMBM
  8.2  JEMFBHLP
  8.3  CPUSTAT
  8.4  MEMSTAT
  8.5  XMSSTAT
  8.6  EMSSTAT
  8.7  VCPI
  8.8  MOVEXBDA
  8.9  CPUID
  9.   License


 1. About Jemm

 Jemm is an "Expanded Memory Manager" (EMM), based on the source of FreeDOS
 Emm386. It should work with MS-DOS and compatible DOSes, including FreeDOS.
 Like other EMMs it installs the following services:

 - uses extended memory to simulate expanded memory (EMS) according to 
   EMS v3.2 and EMS v4.0.
 - upper memory blocks (UMB) where drivers and resident programs may
   be loaded, thus increasing available free DOS memory.
 - mapping RAM to the video address segments A000-AFFF and B000-B7FF.
 - VCPI services to allow DOS applications running in V86-mode to
   switch to protected mode. VCPI also implements a simple memory management.
 - VDS API to give drivers/applications some control over DMA and physical
   addresses in V86-mode.

 There exist 2 versions of Jemm:

 - Jemm386: standard version which needs an external eXtended Memory Manager
            (XMM; examples: Himem[S]X, MS Himem, XMGR ) to be loaded.
 - JemmEx:  extended version which has an XMM already included.

 JemmEx most likely is the better choice because it will need less DOS
 memory than an external XMM + Jemm386.


 2. Features

 The main purpose of making Jemm was to make it use less resources than
 other EMMs, without making compromises regarding speed or compatibility.
 The results currently are:

 - Jemm386 needs just 128 bytes DOS upper memory. JemmEx uses more, mostly
   because it includes the XMS handle array which is located in DOS memory.
   With 32 XMS handles JemmEx needs 496 bytes DOS memory.
 - Jemm's extended memory usage is:
   + 44 kB for Jemm itself
   + 64 kB for the DMA buffer (default size)
   + xx kB for UMBs mapped in the first MB
   +  4 kB fixed amount for EMS handle management.
   + xx kB variable amount for EMS/VCPI memory management. For each 1.5 MB
        VCPI memory 64 bytes are needed, for each EMS page 5 bytes are needed.
        For the default values (120 MB VCPI, 2048 EMS pages) this is 16 kB.
 - VCPI shared address space in extended memory is just 4 kB.

 Other features are:

 - Jemm can be loaded and unloaded from the command line. Disadvantage:
   DOS won't care about UMBs supplied this way.
 - CPUs which provide the Virtual-8086 Mode Extensions (Pentium+) are
   actively supported, which increases the emulation speed.
 - the FASTBOOT option shortens reboot time.
 - the SPLIT option can gain additional DOS high memory.
 - all "lengthy" memory copy operations ( EMS, VDS, Int 15h, JemmEx: XMS )
   are done with interrupts enabled.
 - Exceptions in protected-mode are detected and displayed.
 - a (rudimentary) API is supplied which allows to extend Jemm. [ JLoad
   uses this API to add support for 32bit protected-mode extensions (JLMs). ]
 - JemmEx supports XMS v3.5, allowing access to extended memory beyond the
   4 GB barrier. See XMS35.txt for technical details.


 3. Commandline Options

 For a list of available options enter:

 JEMM386/JEMMEX -?

 Option     Comment
 ----------------------------------------------------------
 A20/NOA20  will set the A20 enable/disable emulation accordingly.
 ALTBOOT    this option is meant to select an alternate reboot handler,
            if the standard handler doesn't work. The current implementation
            sends a "system reset" command to the keyboard controller.
 B=xxxx     specify lowest segment address for EMS banking (default=4000,
            min=1000).
 D=nnn      this option will set the size of the DMA buffer to <nnn> kB.
            The default size is 64 kB, max size is 128 kB. Will always be
            rounded up to next 4 kB.
 EMX        option to prevent EMX DOS extender from quickly terminating with
            message "out of memory (RM)" on machines with large amounts
            of RAM (> 256 MB). This is optionally because it makes Jemm
            behave not fully VCPI compliant, but shouldn't hurt usually.
 FASTBOOT   option might allow a fast reboot. There is no guarantee that it
            will work, though, see the "Option FASTBOOT" chapter below.
 FRAME=nnn  instructs Jemm to use a certain page frame. Accepted are frame
            values from 8000 to E000 or NONE. The page frame should start
            at the beginning of a physical EMS page, that is, the frame 
            address should be divisible by 0x400 without remainder. FRAME=NONE
            disables the page frame, but there are quite some programs which
            won't run with this setting. A page frame below A000 should 
            be set only on computers with 512 kB of conventional memory.
            Usually it's better to let Jemm find a page frame on its own, 
            because choosing an address which is not free might cause troubles.
 I=mmmm-nnnn force Jemm to use a memory range for UMBs (or page frame).
            <mmmm> must be >= A000. Specifying a range which is not
            really free may give "unexpected" results.
 I=TEST     scan ROMs for unused address space. This option will regard any
            4 kB page containing identical byte values in read-only memory as
            "unused".
 LOAD       installs Jemm from the command line. Be aware that UMBs
            cannot be provided this way, since DOS will ignore them.
 [MAX=]nnn  limit the maximum amount of memory to be allocated for VCPI (and
            EMS, if <nnn> is below 32 MB). The MAX= prefix is optional (a
            number found as option will be handled as if it has a MAX=
            prefix). Default value is 120 MB.
 MIN=nnn    preallocates <nnn> kB of XMS memory thus making sure this
            memory is truly available for EMS/VCPI. If MIN is higher than
            MAX, MAX will be adjusted accordingly. Default for <nnn> is 0.
 MOVEXBDA   move XBDA into UMB, thus increasing low DOS memory. This option
            may cause a system "lock" because the XBDA might contain buffers
            used for DMA operations. In such cases either use MOVEXBDA.EXE or
            UMBPCI+UMBM instead.
 NOCHECK    disallows access via Int 15h, AH=87h to address regions which
            aren't backuped by RAM. As for Jemm386: be aware that some old 
            XMMs return wrong values for highest physical RAM - then this 
            option may cause strange results. Also, this option may prevent
            real-mode programs from using the VESA LFB.
 NODYN      disables XMS dynamic memory allocation. Jemm will allocate
            XMS memory for EMS/VCPI on initialization. Default is size
            of largest XMS block/2, but max. 32MB. With option MIN=xxx one
            may override this.
 NOEMS      disables EMS support.
 NOHI       this option will prevent Jemm from moving its resident part
            into upper memory. If no UMBs are installed by Jemm, NOHI
            has no effect.
 NOINVLPG   disables usage of INVLPG opcode on 80486+ cpus. Might be useful
            if Jemm runs in a virtual environment (see "Troubleshooting").
 NOVCPI     disables VCPI. Option can be set from the command line.
 NOVMW      disables VMWare detection.
 PGE/NOPGE  options will enable/disable the Page Global Enable feature
            on Pentium Pro+ cpus. This allows to mark all PTEs for the
            real-mode address space 0-110000h as "global", which gives
            a slight speed benefit for VCPI applications. Option is off by
            default because some DOS extenders will not work with PGE
            enabled. Also setting both PGE + NOINVLPG will not work.
 RAM/NORAM  will instruct Jemm to supply UMBs or not. RAM is the default.
            NORAM is intended to be used when loading Jemm from the
            cmdline, in which case adding UMBs might be less useful.
 S=mmmm-nnnn add a memory region (which must be in range C800-EFFF) as UMB.
            The memory region has to be filled with Shadow-RAM activated by
            UMBPCI, which must have been loaded *before* Himem/JemmEx.
            NOTE: since v5.80, this option is virtually obsolete, because
            either Jemm should find RAM activated by UMBPCI automatically, or,
            if UMBM.EXE has been loaded in CONFIG.SYS, the RAM is already used
            by DOS.
 SB         Soundblaster driver compatibility mode on.
 SPLIT      if ROMs are found which don't end exactly at a 4 kB boundary
            then setting this option will increase available UMB space. ROM
            sizes are defined in 0.5 kB units, so there might be up to 3.5 kB
            wasted in the ROM's last 4 kB page. There is a small catch: the
            full 4k page will be made writeable, including the ROM part.
 UNLOAD     uninstalls Jemm from the command line. Uninstalling the EMM
            might confuse resident programs which rely on EMS or VCPI but
            didn't ensure this configuration to keep unchanged (by allocating
            an EMS page) while they are running.
 V86EXC0D   makes Jemm route General Protection Faults (GPF) that occur in
            V86-mode to Int 0Dh. Without this option they are routed to
            Int 06h. This option should only be set if a resident program
            is to be installed that can handle GPFs in V86-mode.
 VCPI       (re)enables VCPI. Option can be set from the command line.
 VERBOSE    talk a bit more during the load process (abbreviation: /V).
 VME/NOVME  options will enable/disable using the V86 Mode Extensions
            on Pentium+ CPUs. These options can be set from the command line.
            NOVME is the default.
 X=mmmm-nnnn exclude a memory range to be used as UMBs or page frame.
            <mmmm> must be >= A000.
 X=TEST     will exclude all upper memory regions which contain byte values
            other than 00 or FF.

 JemmEx additionally understands:

 A20METHOD:x   select A20 switch method.
               Possible values for <x>:
               ALWAYSON    Assume that A20 line is permanently ON
               BIOS        Use BIOS to toggle the A20 line
               FAST        Use port 92h, bypass INT 15h test
               PS2         Use port 92h, bypass PS/2 test
               KBC         Use the keyboard controller
               PORT92      Use port 92h always
 HMAMIN=k      set minimum amount in kB to get the HMA (default=0, max=63).
 MAXEXT=l      limit extended memory controlled by XMM to <l> kB.
 MAXSEXT=l     limit extended memory beyond 4GB barrier ("super-extended") to
               <l> kB; setting MAXSEXT=0 will make JemmEx behave like a
               v3.0 XMM.
 NOE801        don't use int 15h, ax=E801h to get amount of extended memory.
 NOE820        don't use int 15h, ax=E820h to get amount of extended memory;
               option is ignored unless MAXSEXT=0 is set.
 X2MAX=m       limit for free extended memory in kB reported by XMS V2 
               (default 65535). It is reported that some old applications
               need a value of <m>=32767.
 XMSHANDLES=n  set number of XMS handles (default=48, min=10, max=128).


 4. Technical Details

 4.1 EMS Implementation Notes

 - The number of EMS pages is limited to 2048 (= 32 MB). It can be increased
   up to 32768 pages (= 512 MB) by setting MIN=<nnn> to a value higher than
   32 MB. However, this memory will then be preallocated, and not be available
   as XMS memory. Some applications will not work if EMS > 32 MB.

 - There is one memory pool, which is shared by EMS and VCPI.

 - The following EMS 4.0 functions aren't implemented. Calling these
   functions will return error code 84h in register AH:

   + Int 67h, AH=5Ch, prepare expanded memory manager hardware for warm boot
   + Int 67h, AH=5Dh, enable/disable OS/E


 4.2 Emulation of privileged Opcodes

 To provide Expanded Memory an EMM Emulator like Jemm runs the cpu in
 so-called V86-mode. This mode does not allow to run privileged opcodes.
 Some of these opcodes which might be useful for application programs
 are emulated by Jemm. These are:

 - mov <special_reg>, <reg>   ;special_reg = CRn, DRn, TRn
 - mov <reg>, <special_reg>   ;reg = eax, ebx, ecx, edx, esi, edi, ebp
 - WBINVD
 - INVD
 - WRMSR
 - RDMSR
 - RDTSC
 - HLT

 Those instructions may generate an exception 0x0D if they occur in V86-mode
 ( for RDTSC, this depends on a bit in register CR4 ). Jemm's exception handler
 examines the opcode causing the exception and - if the instruction is to be
 "emulated" - runs it in ring 0.


 4.3 IOPL Sensitive Instructions

 Jemm runs V86-mode code with IOPL 3. That means, the instructions that are
 sensitive to IOPL in V86-mode - CLI, STI, PUSHF, POPF, INT xx, IRET - will
 run at full speed, without causing a GPF.


 4.4 VMWare Detection

 As default, Jemm tries to detect if it's running under VMWare. This is done
 by reading port 0x5658 with value "VMXh" in register EAX. If the detection
 is successful, Jemm assumes that address range E8000-EFFFF is not to be used.
 However, range E8000-EBFFF may still be included with the "I=" option.

 The VMWare detection can be disabled with option NOVMW.


 4.5 Option FASTBOOT

 The FASTBOOT option may work with many versions of DOS. However, there are
 quite a few restrictions. To speed up the boot process, FASTBOOT tries to
 avoid resetting the system. This implicates that the interrupt vector table
 and at least the disk devices can be reset to the state before DOS has been
 loaded. Resetting the interrupt vector table requires some cooperation from
 DOS. Other things that may get in the way are:

 - Moving the XBDA into an UMB with option MOVEXBDA is critical, because to
   move it back into conventional memory might not work for various reasons.

 - older FreeDOS versions may need JEMFBHLP.EXE to be installed prior to the
   XMM (Himem). Possibly this might also be needed for MS-DOS versions < 5.

 - UMBPCI allows to activate "Shadow RAM", but there's no option to deactivate
   it. This may confuse Jemm, since it can only detect RAM activated by UMBPCI
   if UMBPCI's signature is found.

 - A boot loader (Grub) might have inserted code in conventional memory to
   modify hard disks numbers. There's no guarantee that this code survives the 
   fastboot process.


 4.6 Option MAXSEXT

 JemmEx only: this option limits amount of "super-extended" memory. Setting
 MAXSEXT=0 disables this feature. If enabled, JemmEx must exclusively manage
 this type of memory; to achieve this, interrupt 15h, AX=0xE820 is hooked and
 any memory region beyond 4 GB that's marked as available is changed to
 "reserved" ( tool MEMSTAT may be used to verify this behavior ).


 4.7 Option NOEMS

 Despite its name, this option does not fully disable EMS. What's done is:

 - device name changed from EMMXXXX0 to EMMQXXX0
 - no page frame installed
 - EMS memory pool is limited to 8 MB

 The change of the device name makes the usual EMM detection routines fail.
 OTOH, the interrupt 67h API is fully functional. Most of this is pretty
 similar to what "recent" MS Emm386 versions do - the only difference is
 that they won't limit the memory pool to 8 MB, but install it in its full
 size (32 MB).


 4.8 Option SB

 Option SB is supposed to help Creative's SoundBlaster emulation drivers for
 SB PCI devices. It does 2 things:

 a) It translates an exception 0Dh with error code 0x1A that may have occured
    in V86 mode to an INT 3. It's unclear what this is supposed to fix.
 b) Since v5.86, it "identity maps" region 0-0x3fffff in the first page table.
    This mapping was done generally until v5.85.

 With b), Creative's SBINIT/SBEINIT tools should again be compatible with Jemm.
 However, due to the rather hackish nature of those drivers ( they modify
 Jemm's IDT/GDT and page tables ), one may experience negative effects on
 stability.


 5. Compatibility

 Jemm is NOT an MS Emm386 clone. Differences are:
  • - obviously commandline options differ.
  • - the device driver API, accessed by opening file "EMMXXXX0" and then issueing IOCTL cmds, differs.
  • - Jemm doesn't support the so-called GEMMIS API.
  • - Jemm doesn't support the IO port trapping API of MS Emm386.
  • - Jemm supports UMBs (Upper Memory Blocks) accessed via the XMS API, but the management is different: unlike MS Emm386, there's no chain of UMBs, linked with a 16-byte header ( somewhat similar to DOS MCBs ). Instead, the UMB addresses and sizes are stored in a table in extended memory, not accessible by external programs. The table can hold up to 8 blocks.
  • - MS Emm386 tries to map all of physical memory in its linear address space, in such a way that linear and physical addresses are identical. This isn't done by Jemm, because it may consume quite a lot of physical memory for paging tables - at least if 4KB pages are used, as it is the case with MS Emm386. See option SB, though, which triggers this kind of mapping for the first 4 MB.
  • - Both Jemm and MS Emm386 supply the NOVCPI commandline option. However, the effects differ: with Jemm, VCPI function DE01h will be deactivated by this option, thus refusing to start any VCPI client. In contrast, MS Emm386's NOVCPI makes the monitor still offer the full API, but no VCPI memory can be allocated.
6. Errors and Warnings Errors will make Jemm386/JemmEx abort the load process, while warnings will be displayed and then the load process continues. - "Error: no XMM found, required" Jemm386 only. Jemm386 needs an XMM - try Himem(S)X. - "Error: XMM already installed" JemmEx only. Since JemmEx has its very own XMM, it cannot tolerate another one in the system. - "Error: DPMI host detected" Both Jemm386 and JemmEx refuse to load if a DPMI host has been detected. - "Error: No supported A20 method detected" JemmEx only. JemmEx is NOT compatible with this system. - "Error: enable A20 failed" JemmEx only. JemmEx is NOT compatible with this system. - "Error: can't get I15 memory status" JemmEx only. Int 15h, AX=E820h ( and the fallbacks, Int 15h, ax=E801h/ah=88h ) failed or returned an amount of 0 kB extended memory. Tool memstat may help to find the problem. - "Error: can't get XMS memory status" Jemm386 only. The XMS host reports no extended memory. Tool xmsstat may give a hint what's wrong. - "Error: can't allocate enough I15 memory" JemmEx only. Run tool memstat to see how much extended memory the system is reporting. - "Error: can't allocate enough XMS memory" Jemm386 only. Run tool xmsstat to see how much extended memory the XMM is reporting. - "Error: can't lock XMS memory" Jemm386 only. Well, this shouldn't happen. Jemm386 needs the physical address of the memory block where it is to reside. The XMM most likely has to be replaced. - "Warning: unknown A20 method" JemmEx only. Option A20METHOD was given with an invalid method. - "Warning: option 'xxx=' rejected, invalid syntax" The option is ignored. - "Warning: E820 - too many ext memory blocks, block xxxxxxxx ignored!" JemmEx only. Occurs if int 15h, ax=e820h returns more than 10 available memory blocks. It's very well possible that super-extended memory isn't available if this warning has occured. The only cure is to adjust constant ?XMS_STATICHDLS in file jemm16.inc and recompile JemmEx. - "Warning: address of allocated EMB (=XXXXXX) is beyond 16MB" Jemm386 only. Means that Jemm386 is forced to reside beyond the physical 16MB barrier. No problem for Jemm386 itself, but the VDS API requires a DMA buffer that is located below that 16MB barrier. So one is better off not to ignore this warning. To fix it: a) ensure that Jemm386 is loaded just after the XMM; b) replace the XMM, use HimemX2.exe instead of HimemX.exe (if an extended memory block is allocated, HimemX2 tries to return the block with the lowest address that satisfies the request). - "Warning: no suitable page frame found, EMS functions limited." Most programs using EMS won't work without a page frame, so this warning should not be ignored. To analyze the problem, a first step may be to load Jemm from the command line, with the /V option to see why it cannot find a 64 kB region to be used as page frame. Possible reasons are: - UMBPCI is used. Memory regions activated by UMBPCI are detected by Jemm and hence won't be used for mapping the Page Frame. - BIOS marks 128 kB ( E0000-FFFFF ) as reserved in Int 15h, ax=E820h. Using the video segment A000 as page frame is useful only for very specific applications and cannot be recommended. Using regions below A000 are even more problematic on machines with 640 kB conventional memory, since the address range is already "owned" by DOS. If it's not possible to make Jemm use a valid page frame, it should be setup with option NOEMS. - "Warning: EMS banking start too low, set to 0x1000." This warning only occurs if option B=xxxx has been used inappropriately. - "Warning: MIN has been reduced to n kB" Warning occurs if the amount given for MIN exceeds available free memory. - "Warning: wanted DMA buffer size too large, set to 128 kB" Warning occurs if option D=nnn has been used with a value larger than the maximum of 128 (kb). - "Warning: XMS host doesn't provide handle array, dynamic memory allocation off!" Jemm386 only. Jemm wants the XMM to reveal its handle table. If this feature isn't available, Jemm has to allocate its memory for EMS and VCPI statically on startup. 7. Troubleshooting, Hints - If Jemm halts or reboots the machine, the following combinations of parameters may help to find the reason. Generally, Jemm386 should be loaded immediately after the XMM (HIMEM[S]X.EXE, HIMEM.SYS), and the XMM itself should be the first device driver to be loaded. For testing, it might also help to prevent DOS from loading in the HMA and/or not to use UMBs at all. - X=A000-FFFF NOHI NOINVLPG This is the safest combination. If this doesn't work, Jemm most likely isn't compatible with the current DOS/BIOS; for example, the BIOS might try to unconditionally use "unreal" mode to access USB mass storage devices - with Jemm, this will cause an exception. - X=TEST NOHI NOINVLPG This is slightly less safe, since Jemm will scan the upper memory region to find "unused" address ranges usable for UMBs. If this doesn't work, one has to manually set X=xxxx-yyyy to finally find the region which causes the troubles. Tool MEMSTAT may be used to find the address region which is reserved for the ROM-BIOS. - Jemm can be loaded from the command line with option LOAD. This may be helpful if there are so many displays during the boot process that one cannot read them carefully. Also, option /V should be added to make Jemm talkative. To load Jemm386 from the command line, ensure that a XMM has been loaded previously. For JemmEx, no XMM must be loaded, since it is included. - Jemm has been verified to run on the following virtual environments: Qemu, VMware, VirtualPC, Bochs, VirtualBox However, it might be necessary to set option NOINVLPG. - Some DOS programs will not work if EMS is enabled without a page frame. MS CodeView v4.1, for example, may refuse to start then. - Some DOS programs will crash if too much VCPI memory is offered. Jemm's default is 120 MB, it can be changed with option MAX=xxx. Popular programs that may cause troubles are programs that use Borland's DPMI hosts DPMI16BI.OVL/DPMI32VM.OVL (coupled with RTM.EXE/32RTM.EXE). Setting Jemm option MAX=32752K ( that's 32MB minus 16kB) should help; alternately, try setting environment variable "DPMIMEM=MAXMEM 16383". - The JEMM ;-) DOS extender (used for "Strike Commander" and "Privateer") isn't compatible with the VME option. This requirement is a strong sign that this extender switches to V86 mode on its own, which is a bad idea for a VCPI client. - Unlike MS Emm386, Jemm does not try to identity-map extended memory in its address space. This prevents the PL0 debugger 386SWAT from "intruding". - If Jemm is installed from the commandline, loading the CTMOUSE driver v1.9x and v2.0x might cause an exception. Adding option NOHI or NORAM when installing Jemm should avoid that. In CTMOUSE v2.1 the bug has been fixed. - With some BIOSes disk access speed slows down significantly in V86-mode. Most likely this is because the BIOS routines want to avoid using DMA in this mode. Loading XDMA32 ( and XCDROM32 ) might fix that. - FreeDOS regrettably accepts just one UMB provider. This makes it impossible for example to use UMBM.EXE to supply UMB D000-DFFF and then tell Jemm to additionally supply B000-B7FF as UMB. MS-DOS has no such problems. - Although including the region B000-B7FF might work in most cases, one should be aware that this region is not really free, it's used by the VGA "monochrome" video modes. - To make Jemm behave (almost) like MS Emm386 regarding UMBs one should set both options X=TEST and I=TEST. For better MS Emm386 compatibility one might also consider to restrict VCPI memory to 32 MB by adding option MAX=32M. If NODYN is used, one should also set at least MIN=256K. - The NOVCPI option may be used to setup an environment similiar to Windows DOS boxes: - install Jemm with VCPI enabled - install a DPMI host residently (HDPMI, DPMIONE, ...) - disable VCPI with the NOVCPI option This forces any DOS extended application to either use DPMI or abort. - If Jemm displays warning System memory found at XXXX-XXXX, region might be in use then that region is not used by Jemm. If you are sure that the region is ok to be used, include it with 'I=XXXX-XXXX'. - The I=XXXX commandline option may be used to include the VGA "graphics" segment A000h. It might be possible to increase DOS conventional memory up to 736 kB by option I=A000-B7FF. However, there are quite a few hurdles that may cause unexpected results: - conventional memory is increased only if the region to include is adjacent to current memory. On newer machines, there's very often a "hole", caused by the Extended BIOS Data Area (XBDA or EBDA); thus the included region just becomes an UMB and won't increase lower memory. Option MOVEXBDA or tool MOVEXBDA.EXE may fix this issue. - Once address space A000h is remapped, any attempts to run programs that use VGA graphics most likely will cause a crash. - Depending on the VGA-BIOS it may happen that some non-graphics functions won't work anymore and may also cause a crash. Not unusual is that the current text font becomes corrupted ( text font bitmaps must be copied from ROM to VGA memory when a video text mode is set ). - DOS conventional memory cannot be increased anymore once UMB have been added to the DOS memory pool. So you cannot use tool UMBM here! - if MOVEXBDA.EXE causes a system lock during boot, add the /A option to the line in CONFIG.SYS that loads the driver. This aligns the XBDA to a kB boundary, which may cure the lock. - the GRUB boot loader may, under certain conditions, allocate a chunk of conventional memory BELOW the XBDA. In this case conventional memory cannot be increased anymore, even if the XBDA is moved. 8. Additional Tools 8.1 UMBM UMBM is a small tool only useful in conjunction with Uwe Sieber's UMBPCI. The main purpose of UMBM is to allow DOS to load the XMM into upper memory. This driver must be loaded before Jemm386 and therefore DOS can't use UMBs provided by Jemm386. For JemmEx, UMBM is usually not needed, since the XMM is included and needs no low memory. How does UMBM work? It expects to find a "shadow" RAM region activated by UMBPCI. Then it installs inself as a temporary XMS host which just provides support for allocating UMBs. This is enough for most DOSes to grab the memory (note: for this to work line DOS=UMB is required in CONFIG.SYS). After the UMBs have been allocated, UMBM will be removed from memory automatically. Additionally, UMBM understands the /XBDA option. This will cause UMBM to move the XBDA into its first UMB, thus freeing even more low DOS memory. UMBs based on "shadow" RAM, as it is supplied by UMBPCI+UMBM, may have limitations depending on the motherboard's chipset. Sometimes the memory is inaccessible for DMA (read the documentation coming with UMBPCI for more details). OTOH, logical and physical addresses for these UMBs are identical, which may be an advantage, especially for the XBDA. Enter "UMBM" on the command line and read the example how to add UMBM to CONFIG.SYS. UMBM has been tested to run with MS-DOS 6/7 and FreeDOS. 8.2 JEMFBHLP JEMFBHLP is a tiny device driver only needed if both FreeDOS and Jemm's FASTBOOT option are used. FreeDOS v1.0 does not provide the information that Jemm needs for FASTBOOT to work, so this driver tries to cure FreeDOS' incapability. It saves the values for interrupt vectors 15h and 19h at 0070h:0100h, which is the MS-DOS compatible way to do it. In more recent FreeDOS versions this problem has been fixed. 8.3 CPUSTAT CPUSTAT displays some system registers and tables. Most of the registers aren't accessible in v86-mode, so - for Jemm - this program may be seen as a test if the emulation of privileged opcodes works as expected. Optionally, if cpu runs in V86-mode, CPUSTAT may display GDT, IDT, paging tables and informations stored in the task state segment of the v86-monitor. Run "CPUSTAT -?" for more details. 8.4 MEMSTAT MEMSTAT may be used to display the machine's memory layout, as it is returned by the BIOS. The most interesting infos are: - address region reserved for the ROM-BIOS - total amount of free memory 8.5 XMSSTAT XMSSTAT can be used to display the current status of the installed XMM. It allows to check current values of JemmEx options X2MAX, MAXEXT and XMSHANDLES. 8.6 EMSSTAT EMSSTAT can be used to display the current status of the installed EMM. It works with any EMM, not just Jemm. 8.7 VCPI VCPI may be used to display the VCPI status of the installed EMM. With option -p it will display the page table entries for the conventional memory. 8.8 MOVEXBDA MOVEXBDA is a device driver supposed to move the Extended BIOS Data Area ( XBDA or EBDA ) to low DOS memory. If an XBDA exists, it is usually located just below the A000 video segment, with a size of 1-12 kB. Some DOS versions will move the XBDA on their own - at least if its size doesn't exceed 1 kB; then MOVEXBDA is not needed and will do nothing if launched. Also, both JemmEx and UMBM have the ability to move the XBDA as well; those tools move the XBDA to upper memory, thus increasing low DOS memory. However, sometimes MOVEXBDA may be the best option available - because moving the XBDA into an UMB supplied by Jemm (or UMBPCI) may cause the system to "lock". MOVEXBDA should work with any EMM. 8.9 CPUID CPUID displays cpu features returned by the CPUID instruction. 9. License - JEMM386/JEMMEX: partly Artistic License (see ARTISTIC.TXT for details) - UMBM: Public Domain - JEMFBHLP: Public Domain - CPUSTAT: Public Domain - MEMSTAT: Public Domain - XMSSTAT: Public Domain - EMSSTAT: Public Domain - VCPI: Public Domain - MOVEXBDA: Public Domain - CPUID: Public Domain Binaries and source are distributed in separate packages. The binaries' package has a 'B' suffix in its name, the package containing the source has a 'S'. Japheth
================================================ FILE: Include/FILEACC.INC ================================================ ;--- dos file access for JLMs ;--- although addresses are linear, they must ;--- be within 64 kB distance of v86 DS register. w textequ ;--- open file (for reading) ;--- edx = file name ;--- out: ax=handle OpenFile proc movzx eax,word ptr [ebp].Client_Reg_Struc.Client_DS shl eax, 4 sub edx, eax mov w [ebp].Client_Reg_Struc.Client_EDX, dx mov w [ebp].Client_Reg_Struc.Client_EAX, 3D00h mov eax,21h @VMMCall Exec_Int mov ax,w [ebp].Client_Reg_Struc.Client_EAX bt [ebp].Client_Reg_Struc.Client_EFlags,0 ret OpenFile endp ;--- close file ;--- ebx = file handle CloseFile proc mov w [ebp].Client_Reg_Struc.Client_EBX, bx mov w [ebp].Client_Reg_Struc.Client_EAX, 3E00h mov eax,21h @VMMCall Exec_Int bt [ebp].Client_Reg_Struc.Client_EFlags,0 ret CloseFile endp ;--- get file size ;--- ebx = file handle ;--- out: eax=file size GetFileSize proc mov w [ebp].Client_Reg_Struc.Client_EBX, bx mov w [ebp].Client_Reg_Struc.Client_EAX, 4202h ;goto EOF mov w [ebp].Client_Reg_Struc.Client_ECX, 0 mov w [ebp].Client_Reg_Struc.Client_EDX, 0 mov eax,21h @VMMCall Exec_Int bt [ebp].Client_Reg_Struc.Client_EFlags,0 jc exit mov ax, w [ebp].Client_Reg_Struc.Client_EDX shl eax, 16 mov ax, w [ebp].Client_Reg_Struc.Client_EAX push eax mov w [ebp].Client_Reg_Struc.Client_EAX, 4200h ;goto start mov w [ebp].Client_Reg_Struc.Client_ECX, 0 mov w [ebp].Client_Reg_Struc.Client_EDX, 0 mov eax,21h @VMMCall Exec_Int pop eax bt [ebp].Client_Reg_Struc.Client_EFlags,0 exit: ret GetFileSize endp ;--- Read file into buffer ;--- bx = file handle ;--- cx = bytes to read ;--- edx = buffer (in conv. memory) ;--- out: eax=bytes read ReadFile proc movzx eax,w [ebp].Client_Reg_Struc.Client_DS shl eax, 4 sub edx, eax mov w [ebp].Client_Reg_Struc.Client_EBX, bx mov w [ebp].Client_Reg_Struc.Client_ECX, cx mov w [ebp].Client_Reg_Struc.Client_EDX, dx mov w [ebp].Client_Reg_Struc.Client_EAX, 3F00h mov eax,21h @VMMCall Exec_Int movzx eax, w [ebp].Client_Reg_Struc.Client_EAX bt [ebp].Client_Reg_Struc.Client_EFlags,0 ret ReadFile endp ================================================ FILE: Include/FINDRES.INC ================================================ ?NOCASEMAP equ 0 ;win32 std is 0 ;--- eax = resource directory ;--- id may be an id (HIWORD=0) or a string pointer ;--- used by EnumResourceXXX as well !! ScanResDir proc public uses esi pRes:DWORD, dwDir:DWORD, id:DWORD mov esi, pRes add esi, dwDir movzx ecx, [esi].IMAGE_RESOURCE_DIRECTORY.NumberOfNamedEntries mov eax, id .if (eax & 0FFFF0000h) .if (byte ptr [eax] == '#') lea edx,[eax+1] call GetNumber jmp ok .endif lea esi, [esi + sizeof IMAGE_RESOURCE_DIRECTORY] .while (ecx) mov edx, [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.Name_ and edx, edx .if (SIGN?) and edx, 7FFFFFFFh add edx, pRes pushad mov esi, edx mov edi, eax lodsw movzx ecx, ax @@: if ?NOCASEMAP lodsw scasb else lodsw call ToLower mov ah,al mov al,[edi] inc edi call ToLower cmp al,ah endif loopz @B popad .if (ZERO? && (byte ptr [edi] == 0)) mov eax, [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData jmp done .endif .endif add esi, sizeof IMAGE_RESOURCE_DIRECTORY_ENTRY dec ecx .endw jmp error .endif ok: push eax mov eax, sizeof IMAGE_RESOURCE_DIRECTORY_ENTRY mul ecx ;-------------------- let esi point to resources with id entries movzx ecx, [esi].IMAGE_RESOURCE_DIRECTORY.NumberOfIdEntries lea esi, [esi + eax + sizeof IMAGE_RESOURCE_DIRECTORY] pop eax .while (ecx) .if ((ax == [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.Id) || (ax == 0)) mov eax, [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData jmp done .endif add esi, sizeof IMAGE_RESOURCE_DIRECTORY_ENTRY dec ecx .endw error: xor eax, eax done: ret ife ?NOCASEMAP ToLower: cmp al,'A' jb @F cmp al,'Z' jae @F or al,20h @@: retn endif GetNumber: push ebx xor eax, eax .while (byte ptr [edx]) movzx ebx,byte ptr [edx] inc edx sub bl,'0' add eax, eax push eax shl eax, 2 add eax, [esp] add eax, ebx pop ebx .endw pop ebx retn align 4 ScanResDir endp ;--- hiword of lpName and lpType may be NULL ;--- in this case it is an integer identifier FindResourceA proc public uses esi edi hModule:DWORD, lpName:DWORD, lpType:DWORD mov esi, hModule .if (!esi) invoke GetModuleHandle, esi mov esi, eax mov hModule, eax .endif add esi, [esi].IMAGE_DOS_HEADER.e_lfanew mov edi, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE * sizeof IMAGE_DATA_DIRECTORY].VirtualAddress mov edx, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE * sizeof IMAGE_DATA_DIRECTORY].Size_ .if (edi) add edi, hModule invoke ScanResDir, edi, 0, lpType .if (eax) and eax, eax .if (SIGN?) and eax, 7FFFFFFFh invoke ScanResDir, edi, eax, lpName .if (eax) and eax, eax .if (SIGN?) and eax, 7FFFFFFFh invoke ScanResDir, edi, eax, 0 .if (eax) add eax, edi jmp done .endif .else add eax, edi jmp done .endif .endif .endif .endif .endif error: xor eax, eax done: if 0;def _DEBUG @trace <"FindResourceA("> @tracedw hModule @trace <", "> .if (word ptr lpName+2) @trace lpName .else @tracedw lpName .endif @trace <", "> .if (word ptr lpType+2) @trace lpType .else @tracedw lpType .endif @trace <")="> @tracedw eax @trace <13,10> endif ret align 4 FindResourceA endp ;--- attention: name and type are exchanged! FindResourceExA proc public hModule:DWORD, lpType:DWORD, lpName:DWORD, wLanguage:DWORD invoke FindResourceA, hModule, lpName, lpType ; @strace <"FindResourceExA(", hModule, ", ", lpType, ", ", lpName, ", ", wLanguage, ")=", eax> ret align 4 FindResourceExA endp LoadResource proc public hModule:DWORD, hres:DWORD .if (!hModule) invoke GetModuleHandle, hModule mov hModule, eax .endif mov eax, hres .if (eax) mov eax, [eax].IMAGE_RESOURCE_DATA_ENTRY.OffsetToData add eax, hModule .endif ; @strace <"LoadResource(", hModule, ", ", hres, ")=", eax> ret align 4 LoadResource endp LockResource proc public hres:DWORD mov eax,hres ; @strace <"LockResource(", hres, ")=", eax> ret align 4 LockResource endp FreeResource proc public hres:DWORD mov eax,1 ; @strace <"FreeResource(", hres, ")=", eax> ret align 4 FreeResource endp SizeofResource proc public hModule:DWORD, hres:DWORD mov eax, hres .if (eax) mov eax, [eax].IMAGE_RESOURCE_DATA_ENTRY.Size_ .endif ; @strace <"SizeofResource(", hModule, ", ", hres, ")=", eax> ret align 4 SizeofResource endp ================================================ FILE: Include/JLM.H ================================================ /* Header file for Jemm JLMs which defines the subset of the Win3x/Win9x */ /* ring 0 API (VMM) that Jemm/JLoad exposes. */ /* The parameters and return values of the functions are the same as in */ /* the Windows implementation, register usage is as in Win32: EAX, ECX */ /* and EDX may be modified inside functions, the other registers are */ /* preserved (exception: Get_Cur_VM_Handle). */ typedef unsigned char UCHAR; typedef unsigned short USHORT; typedef unsigned long ULONG; struct Pushad_Struc { ULONG Pushad_EDI; /* Client's EDI */ ULONG Pushad_ESI; /* Client's ESI */ ULONG Pushad_EBP; /* Client's EBP */ ULONG Pushad_ESP; /* ESP before pushad */ ULONG Pushad_EBX; /* Client's EBX */ ULONG Pushad_EDX; /* Client's EDX */ ULONG Pushad_ECX; /* Client's ECX */ ULONG Pushad_EAX; /* Client's EAX */ }; #define VMM_DEVICE_ID 0x00001 #define VDMAD_DEVICE_ID 0x00004 #define GetVxDServiceOrdinal(service) __ ## service #define Begin_Service_Table(device, seg) \ enum device##_SERVICES { \ device##_dummy = (device##_DEVICE_ID << 16) - 1, #define Declare_Service(service) \ GetVxDServiceOrdinal(service), #define Declare_SCService(service, args, local) \ GetVxDServiceOrdinal(service), #define End_Service_Table(device, seg) \ Num_##device##_Services}; #define VXDINLINE static __inline /* */ #define GetVxDServiceAddress(service) service #ifdef __WATCOMC__ // Open Watcom C currently can't call the VMM services directly // because ENUM values aren't accessible by the inline assembler. #define VxDCall(service) \ _asm int 20h \ _asm dd (GetVxDServiceOrdinal(service) ) \ #define VxDJmp(service) \ _asm int 20h \ _asm dd (GetVxDServiceOrdinal(service) | 0x8000 ) \ #else #define VxDCall(service) \ _asm _emit 0xcd \ _asm _emit 0x20 \ _asm _emit (GetVxDServiceOrdinal(service) & 0xff) \ _asm _emit (GetVxDServiceOrdinal(service) >> 8) & 0xff \ _asm _emit (GetVxDServiceOrdinal(service) >> 16) & 0xff \ _asm _emit (GetVxDServiceOrdinal(service) >> 24) & 0xff \ #define VxDJmp(service) \ _asm _emit 0xcd \ _asm _emit 0x20 \ _asm _emit (GetVxDServiceOrdinal(service) & 0xff) \ _asm _emit ((GetVxDServiceOrdinal(service) >> 8) & 0xff) | 0x80 \ _asm _emit (GetVxDServiceOrdinal(service) >> 16) & 0xff \ _asm _emit (GetVxDServiceOrdinal(service) >> 24) & 0xff \ #endif #define VMMCall VxDCall #define VMMJmp VxDJmp #define SERVICE __cdecl #define VMM_Service Declare_Service Begin_Service_Table(VMM, VMM) VMM_Service (Get_VMM_Version) VMM_Service (Get_Cur_VM_Handle) VMM_Service (Allocate_V86_Call_Back) VMM_Service (Crash_Cur_VM) VMM_Service (Hook_V86_Int_Chain) VMM_Service (Get_V86_Int_Vector) VMM_Service (Set_V86_Int_Vector) VMM_Service (Get_PM_Int_Vector) VMM_Service (Set_PM_Int_Vector) VMM_Service (Simulate_Int) VMM_Service (Simulate_Iret) VMM_Service (Simulate_Far_Call) VMM_Service (Simulate_Far_Jmp) VMM_Service (Simulate_Far_Ret) VMM_Service (Simulate_Far_Ret_N) VMM_Service (Build_Int_Stack_Frame) VMM_Service (Simulate_Push) VMM_Service (Simulate_Pop) VMM_Service (_PageFree) VMM_Service (_PhysIntoV86) VMM_Service (_LinMapIntoV86) VMM_Service (Hook_V86_Fault) VMM_Service (Hook_PM_Fault) VMM_Service (Begin_Nest_Exec) VMM_Service (Exec_Int) VMM_Service (Resume_Exec) VMM_Service (End_Nest_Exec) VMM_Service (Save_Client_State) VMM_Service (Restore_Client_State) VMM_Service (Simulate_IO) VMM_Service (Install_Mult_IO_Handlers) VMM_Service (Install_IO_Handler) VMM_Service (VMM_Add_DDB) VMM_Service (VMM_Remove_DDB) VMM_Service (Remove_IO_Handler) VMM_Service (Remove_Mult_IO_Handlers) VMM_Service (Unhook_V86_Int_Chain) VMM_Service (Unhook_V86_Fault) VMM_Service (Unhook_PM_Fault) VMM_Service (_PageReserve) VMM_Service (_PageCommit) VMM_Service (_PageDecommit) VMM_Service (_PageCommitPhys) VMM_Service (Free_V86_Call_Back) VMM_Service (Yield) VMM_Service (MoveMemory) VMM_Service (_Allocate_GDT_Selector) VMM_Service (_Free_GDT_Selector) VMM_Service (Get_DDB) End_Service_Table(VMM, VMM) struct cb_s { ULONG CB_VM_Status; /* VM status flags */ ULONG CB_High_Linear; /* Address of VM mapped high */ ULONG CB_Client_Pointer; ULONG CB_VMID; ULONG CB_Signature; }; struct Client_Reg_Struc { ULONG Client_EDI; /* Client's EDI */ ULONG Client_ESI; /* Client's ESI */ ULONG Client_EBP; /* Client's EBP */ ULONG Client_res0; /* ESP at pushall */ ULONG Client_EBX; /* Client's EBX */ ULONG Client_EDX; /* Client's EDX */ ULONG Client_ECX; /* Client's ECX */ ULONG Client_EAX; /* Client's EAX */ ULONG Client_IntNo; ULONG Client_Error; /* Dword error code */ ULONG Client_EIP; /* EIP */ USHORT Client_CS; /* CS */ USHORT Client_res1; /* (padding) */ ULONG Client_EFlags; /* EFLAGS */ ULONG Client_ESP; /* ESP */ USHORT Client_SS; /* SS */ USHORT Client_res2; /* (padding) */ USHORT Client_ES; /* ES */ USHORT Client_res3; /* (padding) */ USHORT Client_DS; /* DS */ USHORT Client_res4; /* (padding) */ USHORT Client_FS; /* FS */ USHORT Client_res5; /* (padding) */ USHORT Client_GS; /* GS */ USHORT Client_res6; /* (padding) */ }; ================================================ FILE: Include/JLM.INC ================================================ ;--- this is an assembler include file in MASM format, ;--- to be used for JLMs (Jemm Loadable Modules). ;--- it defines the API exposed by Jemm/JLoad. ;--- parameters, calling convention and return values are the same as ;--- for Win9x. ;--- the register usage usually is the same as for the Win32 stdcall ;--- convention: registers EAX, ECX and EDX may be changed inside a function, ;--- registers EBX, ESI, EDI, EBP are preserved. ;--- the exception of this rule is Get_Cur_VM_Handle, which returns the ;--- "handle" in EBX. ;-------------------------------------------------------------------------- ;--- v5.83: VMMCall renamed to @VMMCall ( VMMCALL is a x86 instruction now ) ifndef __POASM__ @VMMCall macro name_ int 20h dw VMM_&name_ dw 1 endm else @VMMCall macro name_ int 20h dw VMM_ # name_ dw 1 endm endif ifndef __POASM__ VMMJmp macro name_ int 20h dw VMM_&name_ + 8000h dw 1 endm else VMMJmp macro name_ int 20h dw VMM_ # name_ + 8000h dw 1 endm endif VxDCall macro name_ int 20h dd name_ endm ;--- macro to define a hook proc (Hook_V86_Int_Chain) HookProc macro name_, oldvect ; jmp $+8 ; v5.85: removed jmp dword ptr [oldvect] name_ proc endm ;-------------------------------------------------------------------------- ;--- VMM API. This device is installed by JLoad. The name is misleading, ;--- though, VMs aren't supported. ;--- the following functions aren't implemented yet and will always return ;--- an error: ;--- Install_Mult_IO_Handlers, Remove_Mult_IO_Handlers ;--- Hook_V86_Fault, Unhook_V86_Fault ;--- Hook_PM_Fault, Unhook_PM_Fault ;--- Get_PM_Int_Vector, Set_PM_Int_Vector @@VMM_Service_no = 0 ifndef __POASM__ VMM_Service macro name_ VMM_&name_ equ @@VMM_Service_no @@VMM_Service_no = @@VMM_Service_no +1 endm else VMM_Service macro name_ VMM_#name_ equ @@VMM_Service_no @@VMM_Service_no = @@VMM_Service_no +1 endm endif VMM_Service Get_VMM_Version VMM_Service Get_Cur_VM_Handle VMM_Service Allocate_V86_Call_Back VMM_Service Crash_Cur_VM VMM_Service Hook_V86_Int_Chain VMM_Service Get_V86_Int_Vector VMM_Service Set_V86_Int_Vector VMM_Service Get_PM_Int_Vector VMM_Service Set_PM_Int_Vector VMM_Service Simulate_Int VMM_Service Simulate_Iret VMM_Service Simulate_Far_Call VMM_Service Simulate_Far_Jmp VMM_Service Simulate_Far_Ret VMM_Service Simulate_Far_Ret_N VMM_Service Build_Int_Stack_Frame VMM_Service Simulate_Push VMM_Service Simulate_Pop VMM_Service _PageFree VMM_Service _PhysIntoV86 VMM_Service _LinMapIntoV86 VMM_Service Hook_V86_Fault ;implemented in v5.86 - previously a stub only VMM_Service Hook_PM_Fault VMM_Service Begin_Nest_Exec VMM_Service Exec_Int VMM_Service Resume_Exec VMM_Service End_Nest_Exec VMM_Service Save_Client_State VMM_Service Restore_Client_State VMM_Service Simulate_IO VMM_Service Install_Mult_IO_Handlers VMM_Service Install_IO_Handler VMM_Service VMM_Add_DDB VMM_Service VMM_Remove_DDB VMM_Service Remove_IO_Handler VMM_Service Remove_Mult_IO_Handlers VMM_Service Unhook_V86_Int_Chain VMM_Service Unhook_V86_Fault ;implemented in v5.86 - previously a stub only VMM_Service Unhook_PM_Fault VMM_Service _PageReserve VMM_Service _PageCommit VMM_Service _PageDecommit VMM_Service _PageCommitPhys ;--- the following functions are Jemm specific VMM_Service Free_V86_Call_Back ;release v86 callback in EAX VMM_Service Yield ;release cpu for IRQs VMM_Service MoveMemory ;copy memory with ints enabled ;--- added in v5.83 VMM_Service _Allocate_GDT_Selector VMM_Service _Free_GDT_Selector VMM_Service Get_DDB ;--- added in v5.85 VMM_Service V86ToPM ; jump to VCPI client, esi->V86toPM struct, ebp->client regs ;--- added in v5.86 VMM_Service GetDOSBuffer; get linear address of 4kB DOS buffer in JLOAD (during init phase only) ;--- equates for the page memory functions ;_PageReserve() page parameter: PR_PRIVATE EQU 80000400H PR_SHARED EQU 80060000H PR_SYSTEM EQU 80080000H ;_PageCommit() hpd parameter: PD_ZEROINIT EQU 00000001H ;not supported PD_NOINIT EQU 00000002H ;not supported PD_FIXEDZERO EQU 00000003H PD_FIXED EQU 00000004H ;_PageCommit() flags parameter: PC_FIXED EQU 00000008H ;PC_FIXED and/or PC_LOCKED must be set PC_LOCKED EQU 00000080H PC_WRITEABLE EQU 00020000H ;also valid for _PageCommitPhys() PC_USER EQU 00040000H ;also valid for _PageCommitPhys() PC_CACHEWT EQU 00080000H ;write thru PC_CACHEDIS EQU 00100000H ;cache disable PC_INCR EQU 40000000H ;also valid for _PageCommitPhys() ; PTE bits P_PRESBIT EQU 0 P_PRES EQU (1 SHL P_PRESBIT) P_WRITEBIT EQU 1 P_WRITE EQU (1 SHL P_WRITEBIT) P_USERBIT EQU 2 P_USER EQU (1 SHL P_USERBIT) P_ACCBIT EQU 5 P_ACC EQU (1 SHL P_ACCBIT) P_DIRTYBIT EQU 6 P_DIRTY EQU (1 SHL P_DIRTYBIT) ;--- equates for IO trapping functions BYTE_INPUT EQU 000H BYTE_OUTPUT EQU 004H WORD_INPUT EQU 008H WORD_OUTPUT EQU 00CH DWORD_INPUT EQU 010H DWORD_OUTPUT EQU 014H OUTPUT_BIT EQU 2 WORD_IO_BIT EQU 3 DWORD_IO_BIT EQU 4 STRING_IO_BIT EQU 5 REP_IO_BIT EQU 6 ;rep prefix with "string io" ADDR_32_IO_BIT EQU 7 ;ECX used instead of CX for "rep string io" REVERSE_IO_BIT EQU 8 OUTPUT EQU (1 SHL OUTPUT_BIT) WORD_IO EQU (1 SHL WORD_IO_BIT) DWORD_IO EQU (1 SHL DWORD_IO_BIT) STRING_IO EQU (1 SHL STRING_IO_BIT) REP_IO EQU (1 SHL REP_IO_BIT) ;--- messages SYSTEM_EXIT EQU 5 DEVICE_REBOOT_NOTIFY EQU 17h ;-------------------------------------------------------------------------- ;--- VDMA device. This device is also installed by JLoad @@VDMAD_Service_no = 0 VDMAD_Device equ 4 VDMAD_Service macro name_ name_ equ @@VDMAD_Service_no + VDMAD_Device shl 16 @@VDMAD_Service_no = @@VDMAD_Service_no +1 endm VDMAD_Service VDMAD_Get_Version VDMAD_Service VDMAD_Lock_DMA_Region VDMAD_Service VDMAD_Unlock_DMA_Region VDMAD_Service VDMAD_Scatter_Lock VDMAD_Service VDMAD_Scatter_Unlock VDMAD_Service VDMAD_Request_Buffer VDMAD_Service VDMAD_Release_Buffer VDMAD_Service VDMAD_Copy_To_Buffer ;new v5.69 VDMAD_Service VDMAD_Copy_From_Buffer ;new v5.69 ;-------------------------------------------------------------------------- ;--- VM control block structure ;--- since VMs aren't supported yet, the fields aren't that useful. ;--- CB_Client_Pointer will always contain a valid pointer. cb_s STRUC CB_VM_Status DD ? CB_High_Linear DD ? CB_Client_Pointer DD ? CB_VMID DD ? cb_s ENDS ;--- DDB (device description block) ;--- to be used for VMM_Add_DDB / VMM_Remove_DDB UNDEFINED_INIT_ORDER EQU 080000000H VxD_Desc_Block STRUC DDB_Next DD ? DDB_Version DW ? DDB_Req_Device_Number DW 0 DDB_Dev_Major_Version DB 0 DDB_Dev_Minor_Version DB 0 DDB_Flags DW 0 ifndef __POASM__ DDB_Name DB 8 dup (20h) else DDB_Name DB 8 dup (?) endif DDB_Init_Order DD UNDEFINED_INIT_ORDER DDB_Control_Proc DD ? DDB_V86_API_Proc DD 0 DDB_PM_API_Proc DD 0 DDB_V86_API_CSIP DD 0 DDB_PM_API_CSIP DD 0 DDB_Reference_Data DD ? DDB_Service_Table_Ptr DD 0 DDB_Service_Table_Size DD 0 DDB_Win32_Service_Table DD 0 DDB_Prev DD 0 DDB_Size DD SIZE(VxD_Desc_Block) DDB_Reserved1 DD 0 ;holds PE module handle DDB_Reserved2 DD 0 DDB_Reserved3 DD 0 VxD_Desc_Block ENDS ;--- client register structure ;--- note the "Client_Int" member, which is Jemm specific. Client_Reg_Struc struc Client_EDI dd ? ;+0 Client_ESI dd ? ;+4 Client_EBP dd ? ;+8 Client_res0 dd ? ;+12 Client_EBX dd ? ;+16 Client_EDX dd ? ;+20 Client_ECX dd ? ;+24 Client_EAX dd ? ;+28 Client_Int dd ? ;+32 (pushed as sign-extended byte - so check lowest byte only!) Client_Error dd ? ;+36 Client_EIP dd ? ;+40 Client_CS dd ? ;+44 Client_EFlags dd ? ;+48 Client_ESP dd ? ;+52 Client_SS dd ? ;+56 Client_ES dd ? ;+60 Client_DS dd ? ;+64 Client_FS dd ? ;+68 Client_GS dd ? ;+72 Client_Reg_Struc ends ;-------------------------------------------------------------------------- ;--- JLoad init/term structure ;--- this is 3. parameter for a JLM's DllMain entry ;--- lpRequest is set for LOAD ;--- lpDrivers is set for UNLOAD JLCOMM struc wLdrCS dw ? ;CS of JLOAD.EXE wFlags dw ? ;flags (see below) lpCmdLine dd ? ;linear address cmdline union lpRequest dd ? ;LOAD: linear address DOS request hdr lpDrivers dd ? ;UNLOAD: linear address DOS device driver list start ends JLCOMM ends JLF_UNLOAD equ 1 ;set if JLM is to be unloaded JLF_DRIVER equ 2 ;set if JLoad is loaded as driver in config.sys JLF_UNLOAD_BIT equ 0 JLF_DRIVER_BIT equ 1 ================================================ FILE: Include/JLMFASM.INC ================================================ ;--- this is an assembler include file in FASM format ;--- to be used for JLMs (Jemm Loadable Modules). ;--- it defines the API exposed by Jemm/JLoad. ;--- parameters, calling convention and return values are the same as ;--- for Win9x. ;--- the register usage usually is the same as for the Win32 stdcall ;--- convention: registers EAX, ECX and EDX may be changed inside a function, ;--- registers EBX, ESI, EDI, EBP are preserved. ;--- the exception of this rule is Get_Cur_VM_Handle, which returns the ;--- "handle" in EBX. ;-------------------------------------------------------------------------- macro @VMMCall name_ { int 20h dw VMM_#name_ dw 1 } macro VMMJmp name_ { int 20h dw VMM_#name_ + 8000h dw 1 } macro VxDCall name_ { int 20h dd name_ } ;--- macro to define a hook proc (Hook_V86_Int_Chain) macro HookProc name_, oldvect { jmp $+8 jmp dword [oldvect] name_ proc } ;-------------------------------------------------------------------------- ;--- VMM API. This device is installed by JLoad. The name is misleading, ;--- though, VMs aren't supported. ;--- not implemented yet: ;--- Install_Mult_IO_Handlers, Remove_Mult_IO_Handlers ;--- Hook_V86_Fault, Unhook_V86_Fault ;--- Hook_PM_Fault, Unhook_PM_Fault ;--- Get_PM_Int_Vector, Set_PM_Int_Vector @VMM_Service_no = 0 macro VMM_Service name_ { VMM_#name_ = @VMM_Service_no @VMM_Service_no = @VMM_Service_no +1 } VMM_Service Get_VMM_Version VMM_Service Get_Cur_VM_Handle VMM_Service Allocate_V86_Call_Back VMM_Service Crash_Cur_VM VMM_Service Hook_V86_Int_Chain VMM_Service Get_V86_Int_Vector VMM_Service Set_V86_Int_Vector VMM_Service Get_PM_Int_Vector VMM_Service Set_PM_Int_Vector VMM_Service Simulate_Int VMM_Service Simulate_Iret VMM_Service Simulate_Far_Call VMM_Service Simulate_Far_Jmp VMM_Service Simulate_Far_Ret VMM_Service Simulate_Far_Ret_N VMM_Service Build_Int_Stack_Frame VMM_Service Simulate_Push VMM_Service Simulate_Pop VMM_Service _PageFree VMM_Service _PhysIntoV86 VMM_Service _LinMapIntoV86 VMM_Service Hook_V86_Fault VMM_Service Hook_PM_Fault VMM_Service Begin_Nest_Exec VMM_Service Exec_Int VMM_Service Resume_Exec VMM_Service End_Nest_Exec VMM_Service Save_Client_State VMM_Service Restore_Client_State VMM_Service Simulate_IO VMM_Service Install_Mult_IO_Handlers VMM_Service Install_IO_Handler VMM_Service VMM_Add_DDB VMM_Service VMM_Remove_DDB VMM_Service Remove_IO_Handler VMM_Service Remove_Mult_IO_Handlers VMM_Service Unhook_V86_Int_Chain VMM_Service Unhook_V86_Fault VMM_Service Unhook_PM_Fault VMM_Service _PageReserve VMM_Service _PageCommit VMM_Service _PageDecommit VMM_Service _PageCommitPhys ;--- the following functions are Jemm specific VMM_Service Free_V86_Call_Back ;release v86 callback in EAX VMM_Service Yield ;release cpu for IRQs VMM_Service MoveMemory ;copy memory with ints enabled ;--- added in v5.83 VMM_Service _Allocate_GDT_Selector VMM_Service _Free_GDT_Selector VMM_Service Get_DDB ;--- equates for the page memory functions ;_PageReserve() page parameter: PR_PRIVATE EQU 80000400H PR_SHARED EQU 80060000H PR_SYSTEM EQU 80080000H ;_PageCommit() hpd parameter: PD_ZEROINIT EQU 00000001H ;not supported PD_NOINIT EQU 00000002H ;not supported PD_FIXEDZERO EQU 00000003H PD_FIXED EQU 00000004H ;_PageCommit() flags parameter: PC_FIXED EQU 00000008H ;PC_FIXED and/or PC_LOCKED must be set PC_LOCKED EQU 00000080H PC_WRITEABLE EQU 00020000H ;also valid for _PageCommitPhys() PC_USER EQU 00040000H ;also valid for _PageCommitPhys() PC_INCR EQU 40000000H ;also valid for _PageCommitPhys() ; PTE bits P_PRESBIT EQU 0 P_PRES EQU (1 SHL P_PRESBIT) P_WRITEBIT EQU 1 P_WRITE EQU (1 SHL P_WRITEBIT) P_USERBIT EQU 2 P_USER EQU (1 SHL P_USERBIT) P_ACCBIT EQU 5 P_ACC EQU (1 SHL P_ACCBIT) P_DIRTYBIT EQU 6 P_DIRTY EQU (1 SHL P_DIRTYBIT) ;--- equates for IO trapping functions BYTE_INPUT EQU 000H BYTE_OUTPUT EQU 004H WORD_INPUT EQU 008H WORD_OUTPUT EQU 00CH DWORD_INPUT EQU 010H DWORD_OUTPUT EQU 014H OUTPUT_BIT EQU 2 WORD_IO_BIT EQU 3 DWORD_IO_BIT EQU 4 STRING_IO_BIT EQU 5 REP_IO_BIT EQU 6 ;rep prefix with "string io" ADDR_32_IO_BIT EQU 7 ;ECX used instead of CX for "rep string io" REVERSE_IO_BIT EQU 8 OUTPUT EQU (1 SHL OUTPUT_BIT) WORD_IO EQU (1 SHL WORD_IO_BIT) DWORD_IO EQU (1 SHL DWORD_IO_BIT) STRING_IO EQU (1 SHL STRING_IO_BIT) REP_IO EQU (1 SHL REP_IO_BIT) ;-------------------------------------------------------------------------- ;--- VDMA device. This device is also installed by JLoad @VDMAD_Service_no = 0 VDMAD_Device equ 4 macro VDMAD_Service name_ { name_ equ @VDMAD_Service_no + VDMAD_Device shl 16 @VDMAD_Service_no = @VDMAD_Service_no +1 } VDMAD_Service VDMAD_Get_Version VDMAD_Service VDMAD_Lock_DMA_Region VDMAD_Service VDMAD_Unlock_DMA_Region VDMAD_Service VDMAD_Scatter_Lock VDMAD_Service VDMAD_Scatter_Unlock VDMAD_Service VDMAD_Request_Buffer VDMAD_Service VDMAD_Release_Buffer VDMAD_Service VDMAD_Copy_To_Buffer ;new v5.69 VDMAD_Service VDMAD_Copy_From_Buffer ;new v5.69 ;-------------------------------------------------------------------------- ;--- VM control block structure ;--- since VMs aren't supported yet, the fields aren't that useful. ;--- CB_Client_Pointer will always contain a valid pointer. struct cb_s CB_VM_Status DD ? CB_High_Linear DD ? CB_Client_Pointer DD ? CB_VMID DD ? ends if 0 ;--- DDB (device description block) ;--- to be used for VMM_Add_DDB / VMM_Remove_DDB struct VxD_Desc_Block DDB_Next dd ? DDB_Version DW ? DDB_Req_Device_Number DW ? DDB_Dev_Major_Version DB ? DDB_Dev_Minor_Version DB ? DDB_Flags DW ? DDB_Name rb 8 DDB_Init_Order DD ? DDB_Control_Proc DD ? DDB_V86_API_Proc DD ? DDB_PM_API_Proc DD ? DDB_V86_API_CSIP DD ? DDB_PM_API_CSIP DD ? DDB_Reference_Data DD ? DDB_Service_Table_Ptr DD ? DDB_Service_Table_Size DD ? DDB_Win32_Service_Table DD ? DDB_Prev DD ? DDB_Size DD ? DDB_Reserved1 DD ? DDB_Reserved2 DD ? DDB_Reserved3 DD ? ends end if ;--- client register structure ;--- note the "Client_Int" member, which is Jemm specific. struct Client_Reg_Struc Client_EDI dd ? ;+0 Client_ESI dd ? ;+4 Client_EBP dd ? ;+8 Client_res0 dd ? ;+12 Client_EBX dd ? ;+16 Client_EDX dd ? ;+20 Client_ECX dd ? ;+24 Client_EAX dd ? ;+28 Client_Int dd ? ;+32 Client_Error dd ? ;+36 Client_EIP dd ? ;+40 Client_CS dd ? ;+44 Client_EFlags dd ? ;+48 Client_ESP dd ? ;+52 Client_SS dd ? ;+56 Client_ES dd ? ;+60 Client_DS dd ? ;+64 Client_FS dd ? ;+68 Client_GS dd ? ;+72 ends ;-------------------------------------------------------------------------- ;--- JLoad init/term structure ;--- this is 3. parameter for a JLM's DllMain entry ;--- lpRequest is set for LOAD ;--- lpDrivers is set for UNLOAD struct JLCOMM wLdrCS dw ? ;CS of JLOAD.EXE wFlags dw ? ;flags (see below) lpCmdLine dd ? ;linear address cmdline lpRequest dd ? ;LOAD: linear address DOS request hdr virtual at JLCOMM.lpRequest lpDrivers dd ? ;UNLOAD: linear address DOS device driver list start end virtual ends JLF_UNLOAD equ 1 ;set if JLM is to be unloaded JLF_DRIVER equ 2 ;set if JLoad is loaded as driver in config.sys JLF_UNLOAD_BIT equ 0 JLF_DRIVER_BIT equ 1 ================================================ FILE: Include/JSYSTEM.INC ================================================ ;--- constants used by Jemm & JLoad ?VERSIONHIGH equ 5 ?VERSIONLOW equ 87 ?BASE EQU 110000h ; base of Jemm 32bit code ?SYSBASE EQU 0F8000000h ; linear address system space ?PAGEDIR EQU ?SYSBASE+1000h ; linear address page dir ?PAGETABSYS EQU 3000h ; rel. offset (?SYSBASE) for system page table ;--- 3000 = 3 page tables ( pagedir, page tab0, system page tab ) ?TOS EQU ?SYSBASE+1000h+3000h+1000h ;top of ring 0 stack ;--- this is the structure for a "Get VMM info" request ;--- v5.87: fields e08_pCallBack and e08_CallBackRM do now ;--- point to the *second* static breakpoint in Jemm32/Jemm16. ;--- Thus JLoad doesn't need to know anything about the breakpoint array. EMX08 struct e08_ServiceTable dd ? ;linear address service table e08_pCallBack dd ? ;linear address callback BP in Jemm32 e08_CallBackRM dd ? ;segment:offset of callback BP in Jemm16 e08_GDTR df ? e08_IDTR df ? e08_TR dw ? e08_FlatCS dw ? EMX08 ends ================================================ FILE: Include/PRINTF.INC ================================================ ;--- simple printf ;--- i64toa(long long n, char * s, int base); ;--- convert 64-bit long long to string i64toa proc stdcall uses edi number:qword, outb:ptr, base:dword mov ch,0 mov edi, base mov eax, dword ptr number+0 mov esi, dword ptr number+4 cmp edi,-10 jne @F neg edi and esi,esi jns @F neg esi neg eax sbb esi,0 mov ch,'-' @@: mov ebx,outb add ebx,22 mov byte ptr [ebx],0 @@nextdigit: dec ebx xor edx,edx xchg eax,esi div edi xchg eax,esi div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov [ebx],dl mov edx, eax or edx, esi jne @@nextdigit cmp ch,0 je @F dec ebx mov [ebx],ch @@: mov eax,ebx ret i64toa endp printf proc c fmt:ptr, args:vararg local flag:byte local longarg:byte local size_:dword local fillchr:dword local szTmp[24]:byte local crs:Client_Reg_Struc pushad mov ebp, [ebp] lea edi, crs @VMMCall Save_Client_State @VMMCall Begin_Nest_Exec mov ebp, [esp+2*4] ;restore EBP cld lea edi,args @@L335: mov esi,fmt nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: mov ebp, [ebp] @VMMCall End_Nest_Exec lea esi, crs @VMMCall Restore_Client_State popad ret formatitem: push offset @@L335 xor edx,edx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [esi],'-' jne @F dec bl inc esi @@: mov [flag],bl cmp BYTE PTR [esi],'0' jne @F mov cl,'0' inc esi @@: mov [fillchr],ecx mov ebx,edx .while ( byte ptr [esi] >= '0' && byte ptr [esi] <= '9' ) lodsb sub al,'0' movzx eax,al imul ecx,ebx,10 ;ecx = ebx * 10 add eax,ecx mov ebx,eax .endw mov [size_],ebx cmp BYTE PTR [esi],'l' jne @F mov [longarg],1 inc esi @@: lodsb mov [fmt],esi cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'d' je handle_d cmp al,'u' je handle_u cmp al,'s' je handle_s cmp al,'c' je handle_c and al,al jnz @F pop eax jmp done handle_c: mov eax,[edi] add edi, 4 @@: call handle_char retn handle_s: mov esi,[edi] add edi,4 jmp print_string handle_d: handle_i: mov ebx,-10 jmp @F handle_u: mov ebx, 10 jmp @F handle_x: mov ebx, 16 @@: xor edx,edx mov eax,[edi] add edi,4 cmp longarg,1 jnz @F mov edx,[edi] add edi,4 jmp printnum @@: and ebx,ebx jns @F cdq @@: printnum: lea esi, szTmp invoke i64toa, edx::eax, esi, ebx mov esi, eax print_string: ;print string ESI, size EAX mov eax, esi .while byte ptr [esi] inc esi .endw sub esi, eax xchg eax, esi mov ebx,size_ sub ebx,eax .if flag == 1 .while sdword ptr ebx > 0 mov eax, [fillchr] call handle_char ;print leading filler chars dec ebx .endw .endif .while byte ptr [esi] lodsb call handle_char ;print char of string .endw .while sdword ptr ebx > 0 mov eax, [fillchr] call handle_char ;print trailing spaces dec ebx .endw retn handle_char: push ebp mov ebp, [ebp] mov ah, 0Eh mov [ebp].Client_Reg_Struc.Client_EAX, eax mov byte ptr [ebp].Client_Reg_Struc.Client_EBX+1, 0 mov eax, 10h @VMMCall Exec_Int pop ebp retn printf endp ================================================ FILE: Include/VDS.INC ================================================ ;--- VDS definitions ;--- flags in DX used by various VDS functions VDSF_COPY equ 02h ;bit 1 - copy in/out of DMA buffer (8107/8108) VDSF_NOBUFFER equ 04h ;bit 2 - don't alloc DMA buffer (8103) VDSF_NOREMAP equ 08h ;bit 3 - change PTEs; Jemm ignores this flag! VDSF_64KALIGN equ 10h ;bit 4 VDSF_128KALIGN equ 20h ;bit 5 VDSF_PTE equ 40h ;bit 6 - 1=scatter_lock returns PTEs VDSF_NPPTE equ 80h ;bit 7 - allow non-present pages for scatter/gather remap ;--- error codes returned in AL by lock (8103) VDSERR_NOTCONTIGUOUS equ 1 ;region not in contiguous memory VDSERR_CROSSEDBOUNDS equ 2 ;region crossed alignemnt boundary VDSERR_CANTLOCKPAGES equ 3 ; VDSERR_NOBUFFER equ 4 ; 8107 VDSERR_BUFFTOOSMALL equ 5 ; 8107 VDSERR_BUFFINUSE equ 6 ; ;--- other VDS error codes VDSERR_07 equ 7 ; invalid memory region (8107) VDSERR_REG_NOTLOCKED equ 8 ; region wasn't locked (8106) VDSERR_NAVL_TOOSMALL equ 9 ; # of phys pages was > than table length (8105) VDSERR_INVAL_BUFFID equ 10 ; invalid buffer ID (8108,8109,810A) VDSERR_BNDVIOLATION equ 11 ; buffer boundary violated (8109,810A) VDSERR_INVAL_DMACHN equ 12 ; invalid DMA channel # (810B, 810C) VDSERR_DISCNTOVFL equ 13 ; disable cnt overflow (810B) VDSERR_DISCNTUNFL equ 14 ; disable cnt underflow (810C) VDSERR_FUNC_NOTSUPP equ 15 ; function not supported VDSERR_DXRSVDBITSSET equ 16 ; reserved flag bits set in DX (8102,8106,8108,8109,810A,810B,810C) ;--- DDS, used by 03-04, 07-08, 09-0A DDS struct dwSize dd ? ;+0 size of region dwOfs dd ? ;+4 offset virtual start address wSeg dw ? ;+8 segment/selector virtual start address (or 0000) wID dw ? ;+10 buffer ID dwPhys dd ? ;+12 physical address DDS ends ;--- EDDS, used by 05-06 EDDS struct dwSize dd ? ;+0 dwOfs dd ? ;+4 wSeg dw ? ;+8 wRes dw ? ;+10 wNumAvail dw ? ;+12 wNumUsed dw ? ;+14 EDDS ends ;--- EDDS suffix for regions EDDSRG struct dwPhysAddr dd ? ;+16 dwSizeRg dd ? ;+20 EDDSRG ends ;--- EDDS suffix for PTEs EDDSPT struct dwPTE dd ? ;+16 EDDSPT ends ================================================ FILE: Include/X86.INC ================================================ ;--- generic X86 definitions ;--- page table flags PTF_PRESENT equ 001h PTF_RW equ 002h PTF_USER equ 004h PTF_PWT equ 008h ; page write through PTF_PCD equ 010h ; page cache disable PTF_ACCESS equ 020h ; page accessed PTF_DIRTY equ 040h ; page dirty PTF_4MB equ 080h ; PDEs: 4MB PDE PTF_GBL equ 100h ; page global (P3+) ;--- features, returned by CPUID in EDX CF_FPU equ 0001h CF_VME equ 0002h CF_DE equ 0004h;debugging ext CF_PSE equ 0008h;Page Size Ext (4MB) CF_TSC equ 0010h;Time Stamp Counter CF_MSR equ 0020h;RDMSR/WRMSR supported CF_PAE equ 0040h;Physical Address Ext CF_MCE equ 0080h;Machine Check Exceptions CF_CX8 equ 0100h;CMPXCHG8B supported CF_APIC equ 0200h;APIC exists ;rsvd equ 0400h; CF_SEP equ 0800h;SYSENTER/SYSEXIT supported CF_MTRR equ 1000h CF_PGE equ 2000h ;--- flags in CR4 CR4_VME equ 01h CR4_PVI equ 02h CR4_TSD equ 04h;1=time stamp disable CR4_DE equ 08h CR4_PSE equ 10h;1=page size extensions enabled CR4_PAE equ 20h CR4_MCE equ 40h CR4_PGE equ 80h ;--- descriptor DESCRIPTOR struct wLimit dw ? ;+0 wA0015 dw ? ;+2 bA1623 db ? ;+4 bAttr db ? ;+5 bLimEx db ? ;+6 bA2431 db ? ;+7 DESCRIPTOR ends GATE struct wOfsLo dw ? wSeg dw ? wAttr dw ? wOfsHi dw ? GATE ends ;--- TSS structure ;--- the only fields in the TSS which are needed are tsEsp0, tsSS0 ;--- and tsOfs. Jemm386 will never switch tasks. TSSSEG struct dd ? ;+00 selector tsEsp0 dd ? ;+04 tsSS0 dd ? ;+08 dq ? ;+0C dq ? ;+14 tsCR3 dd ? ;+1C tsEip dd ? ;+20 tsEfl dd ? ;+24 tsEax dd ? ;+28 tsEcx dd ? ;+2C tsEdx dd ? ;+30 tsEbx dd ? ;+34 tsEsp dd ? ;+38 tsEbp dd ? ;+3C tsEsi dd ? ;+40 tsEdi dd ? ;+44 tsES dd ? ;+48 tsCS dd ? ;+4C tsSS dd ? ;+50 tsDS dd ? ;+54 tsFS dd ? ;+58 tsGS dd ? ;+5C tsLDTR dd ? ;+60 tsFlags dw ? ;+64 tsOfs dw ? ;+66 TSSSEG ends ;--- stack frame for PUSHAD PUSHADS struct rEDI dd ? rESI dd ? rEBP dd ? dd ? ;reserved rEBX dd ? rEDX dd ? rECX dd ? rEAX dd ? PUSHADS ends ;--- RETF stack frame for 16bit RETFWS struct union struct _Ip dw ? _Cs dw ? ends _CsIp dd ? ends RETFWS ends ;--- IRET stack frame for 16bit IRETWS struct RETFWS <> _Fl dw ? IRETWS ends ;--- IRET stack frame for 32bit protected-mode IRETDS struct _Eip dd ? ;+0 _Cs dd ? ;+4 _Efl dd ? ;+8 IRETDS ends ;--- stack frame expected by ring0-IRETD to switch to v86-mode IRETDV86 struct _Eip dd ? ;+0 _Cs dd ? ;+4 _Efl dd ? ;+8 _Esp dd ? ;+12 _Ss dd ? ;+16 _Es dd ? ;+20 _Ds dd ? ;+24 _Fs dd ? ;+28 _Gs dd ? ;+32 IRETDV86 ends ;--- bit positions of CPUID edx CPUID_VME equ 1 ; =2 CPUID_PGE equ 13 ; =2000h CPUID_PAT equ 16 ; =10000h ================================================ FILE: JLM/AHCICD/AHCICD.ASM ================================================ ;--- JLM to read CD/DVD in AHCI mode. ;--- "cooked" mode is supported only. .386 .model flat option casemap:none option proc:private include jlm.inc ?OPTIONR equ 1 ; 1=enable /R option to relocate Port.CLB, Port.FB, Commandlist[0].CTBA ?IRQWND equ 1 ; 1=use Jemm's MoveMemory() that allows interrupts during copy op REQOFS equ 16h ; must be at least "sizeof DOSDRV" SLCOOKED equ 2048 SLRAW equ 2352 ;CD-ROM "raw" sector length. ;--- macros CStr macro text:vararg local sym .const sym db text,0 .code exitm endm @dprintf macro text,args:vararg local sym ifdef _DEBUG .const sym db text,0 .code ifnb invoke printf, offset sym, args else invoke printf, offset sym endif endif endm ;--- structs DOSDRV struct dd ? wAttr dw ? ofsStr dw ? ofsInt dw ? name_ db 8 dup (?);+10 wRes1 dw ? ;+18 req. for cd drivers, init with 0 bRes2 db ? ;+20 req. for cd drivers, modified by mscdex bUnits db ? ;+21 set by driver DOSDRV ends ;--- DOS "Request Packet" Layouts. RPH struct RPHLen db ? ;+0 Header byte count. bSubU db ? ;+1 Subunit number. bOp db ? ;+2 Command code. wStat dw ? ;+3 Status field. db 8 dup (?);+5 .. +12 (reserved). RPH ends RPERR equ 8003h ;Packet "error" flags. RPDON equ 0100h ;Packet "done" flag. RPBUSY equ 0200h ;Packet "busy" flag. ;--- Init "Request Packet" Layout RPInit struct RPH <> bUnit db ? ;+13 Number of units found. dwFree dd ? ;+14 return: far16 ptr first free byte behind driver CmdLine dd ? ;+18 Command-line data pointer. RPInit ends ;--- IOCTL "Request Packet" Layout RPIOC struct RPH <> db ? ;+13 Media descriptor byte (Unused by us). dwAddr dd ? ;+14 Data-transfer address. wLength dw ? ;+18 Data-transfer length. dw ? ;+20 Starting sector (unused by us). dd ? ;+22 Volume I.D. pointer (unused by us). RPIOC ends ;--- Read Long "Request Packet" Layout RPRL struct RPH <> bAMode db ? ;+13 Addressing mode. dwAddr dd ? ;+14 Data-transfer address. wSecCnt dw ? ;+18 Data-transfer sector count. dwStart dd ? ;+20 Starting sector number. bDMode db ? ;+24 Data-transfer mode (raw/cooked). bILSize db ? ;+25 Interleave size. bILSkip db ? ;+26 Interleave skip factor. RPRL ends ;--- AHCI structures HBA struct dwCAP dd ? ;+0 HBA capabilities dwGHC dd ? ;+4 global HBA control dwIS dd ? ;+8 interrupt status dwPI dd ? ;+12 ports implemented HBA ends PORT struct dqCLB dq ? ;+0 Command list base addr ( 1 kB aligned ) dqFB dq ? ;+8 (received) FIS base addr ( 256 B aligned ) dwIS dd ? ;+16 interrupt status dwIE dd ? ;+20 interrupt enable dwCMD dd ? ;+24 command and status dd ? ;+28 dwTFD dd ? ;+32 task file data dwSIG dd ? ;+36 signature dwSSTS dd ? ;+40 SATA status dwSCTL dd ? ;+44 SATA control dwSERR dd ? ;+48 SATA error dwSACT dd ? ;+52 SATA active dwCI dd ? ;+56 command issued PORT ends ;--- flags dwIS field PIS_CPDS equ 80000000h PIS_TFES equ 40000000h PIS_HBFS equ 20000000h PIS_HBDS equ 10000000h PIS_SDBS equ 00000008h ; set device bits interrupt PIS_DSS equ 00000004h ; DMA setup FIS interrupt PIS_PSS equ 00000002h ; PIO setup FIS interrupt PIS_DHRS equ 00000001h ; device to host register FIS interrupt ;--- flags dwCMD field PCMD_CR equ 8000h ; RO, 1=command list DMA engine running PCMD_FR equ 4000h ; RO, 1=FIS Receive running PCMD_FRE equ 0010h ; RW, 1=FIS Receive Enable PCMD_CLO equ 0008h ; RW, 1=command list override PCMD_POD equ 0004h ; RW, Power On Device PCMD_SUD equ 0002h ; RW, Spin Up Device PCMD_ST equ 0001h ; RW, Start (HBA may process the command list) ;--- AHCI command list header CLH struct flags1 db ? ;+0 P[7]=Prefetchable, W[6]=Write, A[5]=ATAPI, CFL[4:0]=Command FIS Length flags2 db ? ;+1 PMP[15:12]=Port Multiplier Port, R[11]=Reserved, C[10]=Clear Busy, B[9]=BIST, R[8]=Reset PRDTL dw ? ;+2 Physical Region Descriptor Table Length PRDBC dd ? ;+4 Physical Region Descriptor Byte Count dwCTBA dd ? ;+8 Command Table Base Address bits 0-31 (128 byte aligned) dwCTBAU dd ? ;+12 Command Table Base Address bits 32-63 dd 4 dup (?) CLH ends PRDT struct dwBase dd ? ;+0 base dwBaseU dd ? ;+4 base upper dwRsvd dd ? ;+8 reserved dwCnt dd ? ;+12 cnt PRDT ends ;--- AHCI command table CT struct cfis0 dd ? cfis1 dd ? cfis2 dd ? cfis3 dd ? db 30h dup (?) ; 40h=offset ATAPI in command table Pkt0 dd ? Pkt1 dd ? Pkt2 dd ? db 34h dup (?) ; 80h=offset PRDT in command table p0 PRDT <> CT ends ; packet commands: ; - 003h: request sense ; - 01Bh: start/stop unit ; - 01Eh: prevent/allow media removal ; - 025h: read capacity ; - 028h: read (cooked) ; - 02Bh: seek ; - 042h: read sub-channel ; - 043h: read TOC ; - 047h: play audio MSF ; - 04Ah: get event status notification ; - 04Bh: pause/resume ; - 05Ah: mode sense ; - 0BEh: read CD (raw) if 0 Packet struct PktOPC db 0 ;+0 packet command. db 0 ;+1 Unused (LUN and reserved). PktLBA dd 0 ;+2 CD-ROM logical block address. PktLH db 0 ;+6 "Transfer length" (sector count). PktLn dw 0 ;+7 Middle- and low-order sector count. PktRM db 0 ;+9 Read mode ("Raw" Read Long only). dw 0 ;+10 Unused ATAPI "pad" bytes (required). Packet ends endif .data? if ?OPTIONR cmdlst CLH 32 dup (<>) ; must be 1024 B aligned fis db 100h dup (?) ; must be 256 B aligned cmdtab CT <> ; must be 128 B aligned endif buffer db SLCOOKED dup (?) db SLRAW - SLCOOKED dup (?) ppReq dd ? ; linear address where request header is stored pRequest dd ? ; linear address request header pBase dd ? ; linear address driver base pDest dd ? ; linear address destination pHBA dd ? ; linear address HBA pBuff dd ? ; linear address sector buffer dwBuffPh dd ? ; physical address sector buffer dwSector dd ? ; current sector to read if ?OPTIONR pCLB dd ? ; linear address Port[x].CLB pCTBA dd ? ; linear address CL[0].CTBA endif wPort dw 6 dup (?); port offsets of max. 6 CD/DVDs wSecCnt dw ? ; #sectors to read bUnits db ? ; found units (during init) bCntOpt db ? ; /C option default value bQuiet db ? ; /Q option if ?OPTIONR bReloc db ? ; /R option endif bBanner db ? ; 1=banner displayed .const ERRTAB db 12,12,2,4,12,0,15,8,8,12,12,12,12,12,12,12 rmcode label byte db 2Eh, 89h, 1eh, REQOFS+0, 0 ;mov cs:[16h],bx db 2Eh, 8Ch, 06h, REQOFS+2, 0 ;mov cs:[18h],es db 0CBh ;retf SIZERMCODE equ $ - offset rmcode db 0EAh ;jmp ssss:oooo .code ifdef _DEBUG include printf.inc endif DevInt proc .const align 4 ;--- cmds 00-0E Vector label dword dd Init ; 0 Init dd Error3 ; 1 media check (block devices) dd Error3 ; 2 build bpb (block devices) dd IOCtlInp ; 3 ioctl input dd Error3 ; 4 input dd Error3 ; 5 nondestr. input dd Error3 ; 6 input status (char devices) dd Error3 ; 7 input flush (char devices) dd Error3 ; 8 output dd Error3 ; 9 output with verify dd Error3 ;10 output status (char devices) dd Error3 ;11 output flush (char devices) dd Error3 ;12 ioctl output dd Exit ;13 device open dd Exit ;14 device close ; dd Error3 ;15 removable media (block devices) LVECTOR equ ($ - offset Vector) / sizeof dword ;--- cmds 128-13x Vect2 label dword dd ReadL ;128 CD read long dd Error3 ;129 CD reserved dd Exit ;130 CD read long prefetch ; dd Error3 ;131 CD seek ; dd Error3 ;132 CD play audio ; dd Error3 ;133 CD stop audio ; dd Error3 ;134 CD write long ; dd Error3 ;135 CD write long verify ; dd Error3 ;136 CD resume audio LVECT2 equ ($ - offset Vect2) / sizeof dword .code mov ebx, [ppReq] movzx eax, word ptr [ebx+0] movzx ebx, word ptr [ebx+2] shl ebx, 4 add ebx, eax ;Point to DOS request packet. mov [pRequest], ebx ;linear address request header mov al, [ebx].RPH.bOp @dprintf <'DevInt enter, Op=%X, flags=%X',13,10>, eax, [ebp].Client_Reg_Struc.Client_EFlags and al, al jz @F mov al, [bUnits] cmp [ebx].RPH.bSubU, al mov ax, 8101h jnc ExitAX @@: mov esi, offset Vector mov cl, LVECTOR movzx eax, [ebx].RPH.bOp and al, al jns @F mov esi, offset Vect2 and al, 7Fh mov cl, LVECT2 @@: cmp al, cl jnc Error3X push [ebp].Client_Reg_Struc.Client_EFlags or byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, 2 ; set client IF so Yield will enable interrupts call dword ptr [esi+eax*4] pop [ebp].Client_Reg_Struc.Client_EFlags ExitAX: @dprintf <'DevInt exit, ax=%X',13,10>, ax mov ebx, [pRequest] mov [ebx].RPH.wStat, ax @VMMCall Simulate_Far_Ret ret Error3X: push ExitAX Error3: mov ax, 8103h ret Exit: mov ax, RPDON ret DevInt endp ;--- IOCtl Input dispatcher IOCtlInp proc .const align 4 ;--- IOCTL input subcmds IVect label dword dd IOC_GetHdr ; 0 get device driver header address dd IOC_Error3 ; 1 drive head location dd IOC_Error3 ; 2 reserved dd IOC_Error3 ; 3 error statistics dd IOC_Error3 ; 4 audio channel info dd IOC_Error3 ; 5 raw data bytes dd IOC_DevSt ; 6 device status dd IOC_SecSize ; 7 sector size dd IOC_Error3 ; 8 volume size dd IOC_Media ; 9 media change status ; dd IOC_Error3 ;10 audio disk info ; dd IOC_Error3 ;11 audio track info ; dd IOC_Error3 ;12 audio q-channel info ; dd IOC_Error3 ;13 audio sub-channel info ; dd IOC_Error3 ;14 UPC code ; dd IOC_Error3 ;15 audio status info LIVECT equ ($ - offset IVect) shr 2 .code movzx eax, word ptr [ebx].RPIOC.dwAddr+0 movzx esi, word ptr [ebx].RPIOC.dwAddr+2 shl esi, 4 add esi, eax movzx eax, byte ptr [esi] @dprintf <'DevInt, ioctl input, buffer=%X, al=%X',13,10>, esi, eax cmp al, LIVECT jnc IOC_Error3 jmp [eax*4][IVect] IOC_Error3: mov ax, 8103h retn IOC_Error12: mov ax, 810Ch retn IOC_GetHdr: mov eax, [pBase] shl eax, 12 @dprintf <'DevInt, ioctl input, gethdr, return=%X',13,10>, eax mov [esi+1], eax IOC_done: mov ax, RPDON retn ;--- request "get device status" ;--- status flag bits: ;--- 001 - door opened ;--- 002 - door unlocked ;--- 004 - supports cooked AND raw ;--- 008 - read AND write ;--- 010 - data read AND play audio ;--- 020 - supports interleaving ;--- 040 - (res) ;--- 080 - supports prefetching ;--- 100 - supports audio channel manipulation ;--- 200 - supports HSG AND red book addressing modes IOC_DevSt: ; mov dword ptr [esi+1], 2 ;set "door unlocked" mov dword ptr [esi+1], 2+4 ;set "door unlocked" & "cooked AND raw" jmp IOC_done IOC_SecSize: cmp byte ptr [esi+1],1 ;read mode "cooked" or "raw"? ja IOC_Error12 ;No? Post "general failure" & exit. mov ax, SLRAW je @F mov ax, SLCOOKED @@: mov [esi+2], ax jmp IOC_done IOCtlInp endp IOC_Media proc @dprintf <'DevInt, ioctl input, media',13,10> xor edi, edi call Setup ; set EDI=command table, ebx=port offset ;--- write packet data ( 12 bytes ) ;--- 0: 4A - cmd "get event status notification" ;--- 1: 01 - bit 0=1 immediate ;--- 4: 10 - notification class request, bit 4=1 is "media" ;--- 7-8: 0008 - allocation length ( hibyte first ) ;--- ACMD.00: 4A,01,00,00 ;--- ACMD.04: 10,00,00,00 ;--- ACMD.08: 08,00,00,00 mov [edi].CT.Pkt0, 14AH mov [edi].CT.Pkt1, 10H mov [edi].CT.Pkt2, 8 call Req8 and al, al jnz MedChg ;--- request has returned 8 bytes, ;--- 0-3: event header (0-1 event data length, hibyte first) ;--- 4-7: event data ( 0=media event [bit 0-3], 1=media status [bit 0-1]) mov eax, [pBuff] mov ax, [eax+4] and al, al jnz MedChg test ah, 2 ; bit 1=1 if media present jz MedChg mov byte ptr [esi+1], 1 jmp MedEnd MedChg: mov byte ptr [esi+1], 0 MedEnd: call Setup call ReqSense mov eax, [pBuff] mov al, [eax+2] and al, 15 jz @F mov byte ptr [esi+1], 0 cmp al, 6 jz MedEnd @@: mov ax, RPDON ret IOC_Media endp ;--- read sector(s) ;--- in: ebx=RL ReadL proc mov al, 3 cmp [ebx].RPRL.bAMode, 0; High Sierra addressing mode? jnz ErrorAL_AMode cmp [ebx].RPRL.bDMode, 1; 0=cooked, 1=raw ja ErrorAL_DMode movzx eax, word ptr [ebx].RPRL.dwAddr+0 movzx edx, word ptr [ebx].RPRL.dwAddr+2 shl edx, 4 add eax, edx mov [pDest], eax mov cx, [ebx].RPRL.wSecCnt mov eax, [ebx].RPRL.dwStart mov [wSecCnt], cx mov [dwSector], eax @dprintf <'DevInt, readl, start=%X, sectors=%X, dst=%X',13,10>, eax, cx, pDest xor edi, edi mov esi, ebx ReadLP: ;<---- cmp [wSecCnt], 0 jz Exit call Setup ; set EDI=command table, ebx=port offset ;--- write packet data ( 12 bytes ) ;--- 0: cmd 28/0BE ( read cooked/raw ) ;--- 1: flags ;--- 2-5: LBA (high byte first) ;--- 6: group no ;--- 7-8: transfer length [sector count] ( high byte first ) ;--- 9: control (0 for cooked, 0F8h for raw mode) mov eax, [dwSector] shld ecx, eax, 16 ;--- ax=sect[0-15] ;--- cx=sect[16-31] movzx eax, ax xchg al, ah xchg cl, ch ;--- al=sect[8-15], ah=sect[0-7], cl=sect[24-31], ch=sect[16-23] shl ecx, 16 mov cl, 28H ; read cooked ;--- ACMD.00: 28h/0BEh (opcode "read cooked"),00,Sector.04,Sector.03 ;--- ACMD.04: Sector.02, Sector.01, group#, length.02 ;--- ACMD.08: length.01,00,00,00 mov [edi].CT.Pkt0, ecx mov [edi].CT.Pkt1, eax mov [edi].CT.Pkt2, 1 mov ecx, 800007FFH ; PRDT.0C: FF,07,00,80 (PRC=7FF transfer 2048 bytes, 80=I) cmp [esi].RPRL.bDMode, 0 jz @F ;--- ACMD.00: 0BE (opcode "read raw"),00,Sector.04,Sector.03 mov byte ptr [edi].CT.Pkt0, 0BEh mov byte ptr [edi].CT.Pkt2+1, 0F8h mov ecx, 8000092FH ; PRDT.0C: 2F,09,00,80 (PRC=92F transfer 2352 bytes, 80=I) @@: call ReqECX and al, al jnz readerr push edi push esi mov ecx, SLCOOKED cmp [esi].RPRL.bDMode, 0 jz @F mov ecx, SLRAW @@: push ecx mov edi, [pDest] mov esi, [pBuff] cld if ?IRQWND @VMMCall MoveMemory else shr ecx, 2 rep movsd @VMMCall Yield endif pop ecx pop esi pop edi add [pDest], ecx inc [dwSector] dec [wSecCnt] jmp ReadLP readerr: call Setup call ReqSense and al, al jnz ErrorAL mov eax, [pBuff] mov al, [eax+2] and al, 15 mov ebx, offset ERRTAB xlat ErrorAL_AMode: ifdef _DEBUG @dprintf <'DevInt, readl, error, addressing mode',13,10> jmp ErrorAL endif ErrorAL_DMode: ifdef _DEBUG @dprintf <'DevInt, readl, error, data mode',13,10> jmp ErrorAL endif ErrorAL: mov ah, 81h @dprintf <'DevInt, readl, error, ax=%X',13,10>, ax ret Exit: ifdef _DEBUG mov ebx, [pRequest] @dprintf <'DevInt, readl, ok, sector=%X',13,10>, [ebx].RPRL.dwStart endif mov ax, RPDON ret ReadL endp ;--- in: ;--- ebx=RP if edi==0 ;--- out: ;--- ebx=port offset ;--- edi=linear address ATAPI command table Setup proc cmp edi, 0 ; first call? jnz @F movzx eax, byte ptr [ebx+1] movzx ebx, [eax*2][wPort] add ebx, [pHBA] ;--- get command list/table bases. ;--- those are physical addresses and hence "should" be translated to linear ones. if ?OPTIONR mov eax, [pCLB] ; get command list base mov edi, [pCTBA] ; get command table base address @dprintf <'DevInt, setup, cmdlist=%X/%X, cmdtable=%X/%X',13,10>, eax, dword ptr [ebx].PORT.dqCLB, edi, [eax].CLH.dwCTBA else mov eax, dword ptr [ebx].PORT.dqCLB ; get command list base mov edi, [eax].CLH.dwCTBA ; get command table base address @dprintf <'DevInt, setup, cmdlist=%X, cmdtable=%X',13,10>, eax, edi endif ;--- CLH.00: 25,00,01,00 [CFL=5 dwords, A=1, PRD table length=1] ;--- CLH.04: 00,00,00,00 [PRD byte count=0] mov dword ptr [eax].CLH.flags1, 10025H mov [eax].CLH.PRDBC, 0 @@: ;--- CT.00: 27,80,A0,01 [27=H2D register FIS,80=set command register,A0=ATAPI packet command,01=Features] ;--- CT.04: 00,FF,FF,00 [LBA low,mid,high,device] ;--- CT.08: 00,00,00,00 [LBA (exp) low,mid,high,features] ;--- CT.0C: 01,00,00,00 [sector count, sector count,res,control] mov [edi].CT.cfis0, 1A08027H mov [edi].CT.cfis1, 0FFFF00H mov [edi].CT.cfis2, 0 mov [edi].CT.cfis3, 1 ;--- set base; must be physical address mov ecx, [dwBuffPh] mov [edi].CT.p0.dwBase, ecx xor ecx, ecx mov [edi].CT.p0.dwBaseU, ecx mov [edi].CT.p0.dwRsvd, ecx xor ecx, ecx mov [ebx].PORT.dwIE, ecx or ecx, -1 mov [ebx].PORT.dwIS, ecx mov [ebx].PORT.dwSERR, ecx mov ecx, [ebx].PORT.dwCMD or cl, PCMD_FRE ; enable FIS receive mov [ebx].PORT.dwCMD, ecx or cl, PCMD_ST ; start processing command list mov [ebx].PORT.dwCMD, ecx @dprintf <'DevInt, setup, port=%X, cmdtable=%X, waiting',13,10>, ebx, edi @@: mov eax, [ebx].PORT.dwCMD test ax, PCMD_CR ; command list running? if 0 ; changed 04/2023 jz @B else jnz @F @VMMCall Yield jmp @B @@: endif ret Setup endp ;--- initiate "request sense notification" cmd ReqSense proc ;--- write packet data ( 12 bytes ) ;--- 0: cmd 03 ( request sense ) ;--- 1-3: reserved ;--- 4: allocation length ;--- 5: control ;--- ACMD.00: 03,00,00,00 ;--- ACMD.04: 08,00,00,00 ;--- ACMD.08: 00,00,00,00 mov [edi].CT.Pkt0, 3 mov [edi].CT.Pkt1, 8 mov [edi].CT.Pkt2, 0 ReqSense endp ;--- fall thru!!! ;--- Req8: read 8 bytes into buffer Req8: mov ecx, 80000007H ; PRDT.12: 07,00,00,80 (PRC=7; read 8 bytes, 80=I) ;--- ReqECX: ;--- in: ecx: read ECX+1 bytes ( bit 31 = 1, indicating end of table ) ;--- edi: cmd table ;--- ebx: port ;--- out: AL=0 ok, AL=12 error ReqECX proc mov [edi].CT.p0.dwCnt, ecx mov [ebx].PORT.dwCI, 1 ; bitmask, 1=slot 0 command issued contwait: cmp [ebx].PORT.dwCI, 0 jz done test [ebx].PORT.dwIS, PIS_TFES jnz error @VMMCall Yield cmp [ebx].PORT.dwSERR, 0 JZ contwait error: call Stop mov al, 12 ret done: mov eax, [ebx].PORT.dwTFD test al, 1 jnz error call Stop mov al, 0 ret Stop: mov ecx, [ebx].PORT.dwCMD or cl, PCMD_CLO and cl, 0EEH ; good idea to reset PCMD_ST and PCMD_FRE here? mov [ebx].PORT.dwCMD, ecx retn ReqECX endp ;--- read PCI config address/data ports ;--- in: edx = addr+80000000h ;--- out: eax = value GetPCI proc push edx mov eax, edx mov dx, 0cf8h out dx, eax mov dl, 0fch in eax, dx pop edx ret GetPCI endp SavePCI proc mov dx, 0cf8h in eax, dx mov ebx, eax ret SavePCI endp RestPCI proc mov dx, 0cf8h mov eax, ebx out dx, eax ret RestPCI endp if 0 ;--- in: edx = addr+80000000h, eax = value PutPCI proc push edx push eax mov eax, edx mov dx, 0cf8h out dx, eax mov dl, 0fch pop eax out dx, eax pop edx ret PutPCI endp endif ;--- display string EDX dispString proc push esi mov esi, edx @VMMCall Begin_Nest_Exec nextitem: lodsb cmp al,0 jz done mov byte ptr [ebp].Client_Reg_Struc.Client_EDX, al mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 2 mov eax, 21h @VMMCall Exec_Int jmp nextitem done: @VMMCall End_Nest_Exec pop esi ret dispString endp dispBanner proc cmp bQuiet, 0 jnz @F cmp bBanner, 0 jnz @F mov bBanner, 1 push edx mov edx, CStr('AHCI Optical Disk Driver v1.2',13,10,"Inspired by Rudolph R. Loew's AHCICD",13,10) call dispString pop edx @@: ret dispBanner endp ;--- ebp: client reg struct ;--- esi: cmdline Init proc ;--- scan cmdline args nextchar: lodsb cmp al, ' ' jz nextchar cmp al, 9 jz nextchar cmp al, 13 jz donecmdl cmp al, 0 jz donecmdl cmp al, '/' jnz errcmdl lodsb mov ah, [esi] or al, 20h cmp ax, ':c' jz isOptC cmp ax, ':d' jz isOptD cmp AL, 'q' jz isOptQ if ?OPTIONR cmp AL, 'r' jz isOptR endif jmp errcmdl isOptD: inc esi mov edi, [pBase] add edi, DOSDRV.name_ mov dword ptr [edi+0], ' ' mov dword ptr [edi+4], ' ' mov ecx, sizeof DOSDRV.name_ nextdevchar: lodsb cmp al, ' ' jbe doneOptD cmp al, 'a' jb @F cmp al, 'z' ja @F sub al, 20H @@: stosb loop nextdevchar inc esi doneOptD: dec esi jmp nextchar isOptC: inc esi lodsb cmp al, '0' jb errcmdl cmp al, '9' Ja errcmdl sub al, '0' mov [bCntOpt], al jmp nextchar isOptQ: mov [bQuiet], 1 jmp nextchar if ?OPTIONR isOptR: mov [bReloc], 1 jmp nextchar endif donecmdl: ;--- done cmdline processing call dispBanner ;--- scan for AHCI devices call SavePCI mov edx, 80000008H nextHBA: call GetPCI mov al, 0 ; clear bits 0-7 (interface doesn't matter) cmp eax, 1060100H ; AHCI device? jnz @F dec [bCntOpt] ; /C:x option? js foundHBA @@: @VMMCall Yield add edx, 100H ; next "function" cmp edx, 81000008H jc nextHBA call RestPCI jmp errnodisk foundHBA: @dprintf <'found AHCI device at PCI=%X',13,10>, edx mov dl, 24h ;get ABAR5 call GetPCI mov edi, eax call RestPCI ;--- HBA does not necessarily have to start at a page boundary ;--- so always reserve 2 pages. @dprintf <'HBA physical addr=%X',13,10>, edi push 0 push 2 push PR_SYSTEM @VMMCall _PageReserve add esp, 3*4 cmp eax, -1 jz errpr mov ebx, eax shr eax, 12 ; convert linear address to page# mov ecx, edi shr ecx, 12 push PC_WRITEABLE push ecx push 2 ; map 2 pages ( in theory even 3 might be needed ) push eax @VMMCall _PageCommitPhys add esp, 4*4 and edi, 0fffh add ebx, edi mov [pHBA], ebx @dprintf <'HBA mapped at linear addr=%X',13,10>, ebx mov eax, [ebx].HBA.dwGHC ; get Global HBA Control @dprintf <'HBA.GHC=%X',13,10>, eax test eax, 80000000H ; AHCI enabled? jz errnoahci mov ecx, [ebx].HBA.dwPI ; get ports implemented (bit mask) @dprintf <'HBA.Ports=%X',13,10>, ecx mov edx, 100h ; port offsets are 100h, 180h, 200h, 280h, ..., 1080h mov edi, offset wPort nextPort: shr ecx, 1 jnc @F mov eax, [ebx+edx].PORT.dwSIG ; get signature cmp eax, 0EB140101H jnz @F @dprintf <'found ATAPI device at port=%X',13,10>, edx mov eax, [ebx+edx].PORT.dwSSTS ; get SATA status @dprintf <'device status=%X',13,10>, eax and al, 0FH ; bits 0-3: device detection cmp al, 3 ; device detected and communication established? jnz @F mov eax, edx stosw cmp edi, offset wPort + sizeof wPort; table end reached? jz donePort @@: add edx, 80H and ecx, ecx Jnz nextPort donePort: sub edi, offset wPort shr edi, 1 mov ecx, edi cmp ecx, 0 jz errnodisk mov [bUnits], cl if ?OPTIONR mov edi, offset wPort cmp [bReloc], 0 jz noreloc mov esi, offset cmdlst mov [pCLB], esi mov [pCTBA], offset cmdtab mov [pBuff], offset buffer push ecx mov ecx, sizeof cmdlst + sizeof fis + sizeof cmdtab + sizeof buffer mov edx, 1 VxDCall VDMAD_Lock_DMA_Region pop ecx @dprintf <'physical/logical address CLB=%X/%X',13,10>, edx, esi mov esi, edx .while ecx movzx edx, word ptr [edi] mov eax, esi mov dword ptr [ebx+edx].PORT.dqCLB, eax add eax, sizeof cmdlst mov dword ptr [ebx+edx].PORT.dqFB, eax add edi, sizeof WORD dec ecx .endw mov eax, esi add eax, sizeof cmdlst + sizeof fis mov edx, [pCLB] mov [edx].CLH.dwCTBA, eax @dprintf <'CLB.CTBA=%X',13,10>, eax add eax, sizeof cmdtab mov [dwBuffPh], eax jmp buffers_done noreloc: movzx edx, word ptr [edi] mov eax, dword ptr [ebx+edx].PORT.dqCLB mov edx, [eax].CLH.dwCTBA mov [pCLB], eax mov [pCTBA], edx @dprintf <'linear address command list/table=%X/%X',13,10>, eax, edx endif mov esi, offset buffer mov [pBuff], esi mov ecx, SLCOOKED mov edx, 1 VxDCall VDMAD_Lock_DMA_Region mov [dwBuffPh], edx @dprintf <'physical address sector buffer=%X',13,10>, edx buffers_done: mov ebx, [pBase] mov al, [bUnits] mov [ebx].DOSDRV.bUnits, al cmp [bQuiet], 0 jnz @F add al,'0' movzx eax, al push eax mov edx, esp call dispString pop eax mov edx, CStr(' AHCI Optical Disk(s) Found',13,10) call dispString @@: mov ebx, [pRequest] mov word ptr [ebx].RPInit.dwFree+0, REQOFS+4+SIZERMCODE+5 mov ax, RPDON mov [ebx].RPInit.wStat, ax mov [ebx].RPInit.bUnit, 0 ret errpr: mov edx, CStr('_PageReserve() failed',13,10) jmp @F errnoahci: mov edx, CStr('AHCI Controller not in AHCI Mode',13,10) jmp @F errcmdl: if ?OPTIONR mov edx, CStr('SYNTAX: DEVICE=JLOAD.EXE AHCICD.DLL [/C:#] /D:devname [/Q][/R]',13,10) else mov edx, CStr('SYNTAX: DEVICE=JLOAD.EXE AHCICD.DLL [/C:#] /D:devname [/Q]',13,10) endif jmp @F errnodisk: MOV edx, CStr('No AHCI Optical Disk Found',13,10) @@: call dispBanner call dispString mov ax, RPERR ret Init endp DllMain proc stdcall public uses esi edi hModule:dword, dwReason:dword, dwRes:dword local pCmdLine:dword mov eax, dwReason cmp eax, 1 jnz done mov esi, dwRes test [esi].JLCOMM.wFlags, JLF_DRIVER jz failed movzx ecx, [esi].JLCOMM.wLdrCS shl ecx, 4 mov [pBase], ecx lea eax, [ecx+REQOFS] mov [ppReq], eax mov eax, [esi].JLCOMM.lpCmdLine mov [pCmdLine], eax mov eax, [esi].JLCOMM.lpRequest mov [pRequest], eax mov esi, offset DevInt xor edx, edx @VMMCall Allocate_V86_Call_Back jc failed mov edi, [pBase] mov [edi].DOSDRV.wAttr, 0C800h ;driver attributes mov [edi].DOSDRV.ofsStr, REQOFS+4 mov [edi].DOSDRV.ofsInt, REQOFS+4+SIZERMCODE mov [edi].DOSDRV.wRes1, 0 mov [edi].DOSDRV.bRes2, 0 add edi, REQOFS+4 mov esi, offset rmcode mov ecx, SIZERMCODE+1 cld rep movsb stosd @VMMCall Get_Cur_VM_Handle mov esi, [pCmdLine] ;--- set EBP to the client pointer before calling I_Init push ebp mov ebp, [ebx].cb_s.CB_Client_Pointer call Init pop ebp cmp ax,RPDON setz al movzx eax,al done: ret failed: xor eax, eax ret DllMain endp end DllMain ================================================ FILE: JLM/AHCICD/AHCICD.txt ================================================ 1. About AHCICD AHCICD is a JLM to access AHCI optical disks, "inspired" by Rudolph R. Loew's AHCICD.SYS. To load it, add the following line to your CONFIG.SYS: DEVICE=JLOAD.EXE AHCICD.DLL [options] options are: /C:device select AHCI controller if more than one exists. /D:name set device name. Required. /Q no displays unless errors occur. /R relocate AHCI regions [CL/FIS/CT] to extended memory. 2. Hints - AHCI uses tables that may be located in the XBDA - so if the XBDA is to be moved, setting option /R is required. - AHCICD.DLL needs JLOAD v5.83 - older versions won't disable caching of the controller's memory-mapped registers. ================================================ FILE: JLM/AHCICD/MAKE.BAT ================================================ @echo off jwasm -c -coff -nologo -Fl -Sg -I..\..\Include AHCICD.ASM rem jwasm -coff -nologo -D_DEBUG -Fl -Sg -I..\..\Include AHCICD.ASM jwlink format win pe hx dll ru native file AHCICD.obj name AHCICD.DLL op q,map ================================================ FILE: JLM/AHCICD/MAKEM.BAT ================================================ @echo off ml -c -coff -nologo -Fl -Sg -I..\..\Include AHCICD.ASM link /nologo /subsystem:native /dll AHCICD.obj /OUT:AHCICD.DLL /MAP ================================================ FILE: JLM/GENERIC/GENERIC.ASM ================================================ ;--- JLM sample GENERIC ;--- use Makefile to create GENERIC.DLL ;--- GENERIC is a very simple JLM. It doesn't hook v86-interrupt vectors. ;--- To be called from v86-mode, its entry point must be obtained by the ;--- caller. .386 .model flat, stdcall .nolist include jlm.inc .list DEVICE_ID equ 6660h cr equ 13 lf equ 10 DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 .data ;--- the DDB must be make public. The linker will "export" this ;--- symbol. This is the simplest method to make JLoad know the ;--- device id. public ddb ddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,"GENERIC",0, 0, v86_dispatch> szHello db "Hello from JLM GENERIC",cr,lf,0 .code ;--- dispatcher for v86 services v86_dispatch proc @VMMCall Simulate_Far_Ret ;emulate a RETF in v86 and [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;clear Carry flag movzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX cmp eax,0 jz getversion cmp eax,1 jz display_hello or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag ret align 4 v86_dispatch endp getversion proc mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0100h ret align 4 getversion endp display_hello proc uses esi @VMMCall Begin_Nest_Exec ;start nested execution mov esi, offset szHello @@: lodsb and al,al jz done ;--- Call int 21h, ah=2 in v86-mode. ;--- Be aware that in Jemm's context is no DOS extender installed. ;--- So there is no translation for DOS functions which use pointers. mov byte ptr [ebp].Client_Reg_Struc.Client_EDX,al mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,2 mov eax,21h @VMMCall Exec_Int jmp @B done: @VMMCall End_Nest_Exec ;end nested execution ret align 4 display_hello endp ;--- install the JLM: just set eax=1 ;--- this tells JLOAD that it's ok to add GENERIC to the list of ;--- loaded modules. install proc uses esi pcomm:ptr JLCOMM mov eax,1 ret align 4 install endp ;--- deinstall the JLM: just set eax=1. ;--- this tells JLOAD that it's ok to remove the module. deinstall proc pcomm:ptr JLCOMM mov eax,1 ret align 4 deinstall endp DllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword mov eax,dwReason cmp eax,DLL_PROCESS_ATTACH jnz @F invoke install, dwRes jmp exit @@: cmp eax,DLL_PROCESS_DETACH jnz @F invoke deinstall, dwRes @@: exit: ret align 4 DllMain endp end DllMain ================================================ FILE: JLM/GENERIC/MAKEFILE ================================================ # NMake/WMake Makefile to create GENERIC.DLL # # tools alternatives #---------------------------------------------------------- # JWasm Masm v6.x # JWLink Link, WLink, ALink NAME = GENERIC OUTDIR=Release AOPT=-nologo -c -coff -nologo -Fl$*.lst -Fo$*.obj -I..\..\Include ASM=jwasm.exe #LINK=link.exe /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /EXPORT:ddb /Entry:DllMain /OPT:NOWIN98 LINK=jwlink format win nt hx dll ru native file $*.obj name $*.DLL op q,map=$*.MAP export _ddb.1 ALL: $(OUTDIR) $(OUTDIR)\$(NAME).DLL $(OUTDIR)\TESTGEN.COM $(OUTDIR): @mkdir $(OUTDIR) $(OUTDIR)\$(NAME).DLL: $(OUTDIR)\$(NAME).obj Makefile @$(LINK) $(OUTDIR)\$(NAME).obj: $(NAME).asm @$(ASM) $(AOPT) $(NAME).asm $(OUTDIR)\TESTGEN.COM: TESTGEN.ASM @jwasm -nologo -bin -Fo$*.COM -Fl$* testgen.asm ================================================ FILE: JLM/GENERIC/README.TXT ================================================ 1. About GENERIC is a JLM sample which demonstrates how to implement a protected-mode TSR. 2. How to install and uninstall GENERIC GENERIC can be installed either as a device driver in CONFIG.SYS: DEVICE=JLOAD.EXE GENERIC.DLL or as a TSR from the command line: JLOAD GENERIC.DLL To uninstall, use JLOAD's -u option: JLOAD -u GENERIC.DLL 3. How to use GENERIC GENERIC doesn't hook real-mode interrupt vectors. If a real-mode program wants to call a function provided by GENERIC, it first has to get the JLM's entry point. This is done similiar to Windows 9x: a call of Int 2F, AX=1684h with register BX containing the "device id". On return, if the "device" has been found, ES:DI will contain its entry address: mov ax, 1684h ;standard value to get VxD entry points mov bx, 6660h ;6660h is the ID of GENERIC int 2Fh cmp al,0 jnz not_installed mov word ptr [genpm+0],di mov word ptr [genpm+2],es The protected-mode TSR implements two functions, which are selected by the value of register AX: mov ax, 0000 ;0000=get version call dword ptr [genpm] mov ax, 0001 ;0001=display hello call dword ptr [genpm] There's a test program - TESTGEN.EXE - supplied which uses GENERIC. 4. License GENERIC is Public Domain. Japheth ================================================ FILE: JLM/GENERIC/TESTGEN.ASM ================================================ ;--- test app which calls some "services" of JLM GENERIC. ;--- assemble: JWasm -bin -Fo testgen.com testgen.asm .model tiny .data szOK db "GENERIC found!",13,10,'$' szError db "GENERIC is NOT installed!",13,10,'$' .code org 100h start: mov ax, 1684h ;get GENERIC's entry point mov bx, 6660h int 2Fh cmp al,0 jnz not_installed push es push di mov bp,sp mov dx,offset szOk mov ah,9 int 21h mov ax,0000 ;call "get version" call dword ptr [bp] mov ax,0001 ;call "display hello" call dword ptr [bp] add sp,4 int 20h not_installed: mov dx,offset szError mov ah,9 int 21h int 20h end start ================================================ FILE: JLM/HELLO/HELLO.ASM ================================================ ;--- a simple JLM which displays a message onto the screen ;--- with the help of v86-int 21h and nested execution .386 .MODEL FLAT, stdcall include jlm.inc ifdef FMTPE ;--- since v2.19, JWasm's -pe option knows how to interpret linker directives supplied ;--- in the .drectve info section. Thus no extra link step is required. option dotname .drectve segment info ;linker directives db "-subsystem:native -fixed:no" .drectve ends .hdr$1 segment use16 % incbin .hdr$1 ends endif .DATA szText db "Hello, world!", 13,10,0 .CODE _main proc push esi @VMMCall Begin_Nest_Exec ;start nested execution mov esi, offset szText @@nextitem: lodsb and al,al jz @@done ;--- Call int 21h, ah=2 in v86-mode. ;--- Be aware that in Jemm's context is no DOS extender installed. ;--- So there is no translation for DOS functions that use pointers. mov byte ptr [ebp].Client_Reg_Struc.Client_EDX,al mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,2 mov eax,21h @VMMCall Exec_Int jmp @@nextitem @@done: @VMMCall End_Nest_Exec ;end nested execution pop esi mov eax, 80000001h ;bit 31=1: suppress JLoad msg ret _main ENDP END _main ================================================ FILE: JLM/HELLO/MAKE.BAT ================================================ @echo off rem rem use JWasm & JWlink rem jwasm -nologo -coff -Fl=Release\Hello -Fo=Release\Hello -I..\..\Include Hello.asm jwlink format win pe hx ru native file Release\Hello.obj name Release\Hello.exe op q,m=Release\Hello,stack=0x1000 rem use JWasm - since JWasm v2.19, cmdline option -pe supports several linker features; rem also, stub JLSTUB.BIN may be added. Then the program can be launched simply by typing its name. rem rem jwasm -nologo -pe -DFMTPE -DSTUB=..\JLSTUB\Build\jlstub.bin -Fl=Release\Hello -Fo=Release\Hello -I..\..\Include Hello.asm rem patchPE -x -s:0x1000 Release\Hello.exe rem use Masm & Link rem rem ml -c -coff -FlRelease\Hello -FoRelease\Hello -I..\..\Include Hello.asm rem link /NOLOGO Release\HELLO.OBJ /subsystem:native /FIXED:NO /map /out:Release\Hello.exe /ENTRY:_main /OPT:NOWIN98 rem \hx\bin\patchpe -s:0x4000 -x Release\Hello.exe ================================================ FILE: JLM/HELLO/README.TXT ================================================ About This is the inevitable "hello world" JLM, written for Masm/JWasm. It displays a welcome message. After execution the module's resources are released. Usage: C:\>JLOAD -q HELLO.EXE ================================================ FILE: JLM/HELLO2/HELLO2.C ================================================ // for MS VC #include struct Client_Reg_Struc * pcl; struct cb_s * Get_Cur_VM_Handle() { VxDCall(Get_Cur_VM_Handle); _asm mov eax,ebx; } ULONG Begin_Nest_Exec() { _asm mov ebp,pcl; VxDCall(Begin_Nest_Exec); } ULONG End_Nest_Exec() { _asm mov ebp,pcl; VxDCall(End_Nest_Exec); } ULONG Exec_Int(unsigned long intno) { _asm mov eax, intno; _asm mov ebp,pcl; VxDCall(Exec_Int); } int main() { struct cb_s * hVM; unsigned char * psz = "Hello world\r\n"; hVM = Get_Cur_VM_Handle(); pcl = (struct Client_Reg_Struc *)hVM->CB_Client_Pointer; Begin_Nest_Exec(); for (;*psz;psz++) { pcl->Client_EAX = 0x0200; pcl->Client_EDX = *psz; Exec_Int(0x21); } End_Nest_Exec(); return 1; }; ================================================ FILE: JLM/HELLO2/HELLO2W.C ================================================ // for Open Watcom C // OW inline assembly doesn't know values of enums! // therefore it needs external module jlmw.asm #include #include "JLMW.h" struct Client_Reg_Struc * pcl; int main() { struct cb_s * hVM; unsigned char * psz = "Hello world\r\n"; hVM = Get_Cur_VM_Handle(); pcl = (struct Client_Reg_Struc *)hVM->CB_Client_Pointer; Begin_Nest_Exec(pcl); for (;*psz;psz++) { pcl->Client_EAX = 0x0200; pcl->Client_EDX = *psz; Exec_Int(0x21, pcl); } End_Nest_Exec(pcl); return 1; }; ================================================ FILE: JLM/HELLO2/JLMW.ASM ================================================ ;--- helpers for (Watcom) C .386 .model flat include JLM.INC .code Get_Cur_VM_Handle proc stdcall public uses ebx @VMMCall Get_Cur_VM_Handle mov eax, ebx ret align 4 Get_Cur_VM_Handle endp Begin_Nest_Exec proc stdcall public uses ebp pcl:ptr mov ebp,pcl @VMMCall Begin_Nest_Exec ret align 4 Begin_Nest_Exec endp End_Nest_Exec proc stdcall public uses ebp pcl:ptr mov ebp,pcl @VMMCall End_Nest_Exec ret align 4 End_Nest_Exec endp Exec_Int proc stdcall public uses ebp intno:dword, pcl:ptr mov eax, intno mov ebp,pcl @VMMCall Exec_Int ret align 4 Exec_Int endp end ================================================ FILE: JLM/HELLO2/JLMW.h ================================================ // helper procs needed by Open Watcom C extern struct cb_s * _stdcall Get_Cur_VM_Handle( void ); extern ULONG _stdcall Begin_Nest_Exec(struct Client_Reg_Struc * pcl); extern ULONG _stdcall End_Nest_Exec(struct Client_Reg_Struc * pcl); extern ULONG _stdcall Exec_Int( unsigned long intno, struct Client_Reg_Struc * pcl ); ================================================ FILE: JLM/HELLO2/MAKEJLMW.BAT ================================================ @echo off rem assembles helper module jlmw for Open Watcom. jwasm -I ..\..\Include jlmw.asm ================================================ FILE: JLM/HELLO2/MAKEOW.BAT ================================================ @echo off rem uses Open Watcom C and WLink rem OW needs a helper module: jlmw.obj wcc386 -mf -zl -zls -s -I..\..\Include hello2w.c jwlink format win nt hx ru native file hello2w.obj, jlmw.obj name hello2w.exe option start=main_, map ================================================ FILE: JLM/HELLO2/MAKEVC.BAT ================================================ @echo off rem uses MS VC and MS Link \msvc71\bin\cl -c -I..\..\Include hello2.c link /LIBPATH:\msvc71\lib hello2.obj /subsystem:native /fixed:no /out:hello2.exe /entry:main \hx\bin\patchpe hello2.exe ================================================ FILE: JLM/HELLO2/README.TXT ================================================ About Another JLM "hello world", this time written in C. MAKEVC.BAT: for MS VC (or Pelles C, CC386, ...) MAKEOW.BAT: for Open Watcom. to run it: jload /q hello2(w).exe There is room for improvement. ================================================ FILE: JLM/HELLO2/makeow.sh ================================================ #!/bin/sh set -e # uses Open Watcom C, JWAsm and JWLink which must be in PATH # OW needs a helper module: jlmw.obj jwasm -I../../Include JLMW.ASM wcc386 -mf -zl -zls -s -I../../Include HELLO2W.C jwlink format win nt hx ru native file HELLO2W.o, JLMW.o name hello2w.exe option start=main_, map ================================================ FILE: JLM/IOTRAP/IOTRAP.ASM ================================================ ;--- JLM sample IOTRAP ;--- use Makefile to create IOTRAP.DLL ;--- IOTRAP traps IO port 100h when the first client ;--- wants to register a callback. ;--- The client is notified whenever an IO access happens. .386 .model flat, stdcall PORT equ 100h ;port to trap CODE16_SEL equ 20h DATA16_SEL equ 28h .nolist include jlm.inc .list DEVICE_ID equ 6661h cr equ 13 lf equ 10 DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 .data ;--- the DDB must be make public. The linker will "export" this ;--- symbol. This is the simplest method to make JLoad know the ;--- device id. public ddb ddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,"IOTRAP",0,0, v86_dispatch > szHello db "Hello from JLM IOTRAP",cr,lf,0 cntCB dd 0 callbacks label dword dd 8*2 dup (0) ;allow 8 callbacks .code ;--- dispatcher for v86 services v86_dispatch proc @VMMCall Simulate_Far_Ret ;emulate a RETF in v86 and [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;clear Carry flag movzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX cmp eax,0 jz getversion cmp eax,1 jz register_callback or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag ret align 4 v86_dispatch endp getversion proc mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0100h ret align 4 getversion endp register_callback proc .if ( cntCB >= 8 ) or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 8 ret .endif ;--- when the first client registers, install an io handler .if ( cntCB == 0 ) mov esi, offset iocb mov edx, PORT @VMMCall Install_IO_Handler .if ( CARRY? ) or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 9 ret .endif .endif mov cx, word ptr [ebp].Client_Reg_Struc.Client_ECX ;callback's CS shl ecx,16 mov cx, word ptr [ebp].Client_Reg_Struc.Client_EDX ;callback's IP mov eax, cntCB mov [eax*8+callbacks+0], ecx mov ecx, [ebp].Client_Reg_Struc.Client_EBX ;callback's param mov [eax*8+callbacks+4], ecx inc cntCB ret align 4 register_callback endp ;--- io handler proc ;--- ecx=type of io ;--- edx=port ;--- ebp=client struct ;--- eax=io data PF32 typedef ptr far32 iocb proc if 0 ;if the client is to be called in v86-mode push esi push edi sub esp, sizeof Client_Reg_Struc mov edi, esp push ecx push edx push eax @VMMCall Save_Client_State @VMMCall Begin_Nest_Exec ;start nested execution mov esi, offset callbacks mov ecx, cntCB .while ecx push ecx mov eax,[esp+4] mov edx,[esp+8] mov ecx,[esp+12] mov [ebp].Client_Reg_Struc.Client_EDX, edx mov [ebp].Client_Reg_Struc.Client_ECX, ecx mov [ebp].Client_Reg_Struc.Client_EAX, eax lodsd movzx edx,ax shr eax,16 mov ecx, eax @VMMCall Simulate_Far_Call lodsd mov [ebp].Client_Reg_Struc.Client_EBX, eax @VMMCall Resume_Exec ;run the VM pop ecx dec ecx .endw @VMMCall End_Nest_Exec ;end nested execution lea esi, [esp+3*4] @VMMCall Restore_Client_State pop eax pop edx pop ecx add esp, sizeof Client_Reg_Struc pop edi pop esi ret else ;if the client is to be called in protected-mode pushad push ecx push edx push eax sub esp,8 sgdt [esp] mov eax,[esp+2] add esp,8 mov esi, offset callbacks mov ecx, cntCB .while ecx push ecx push eax lodsd push CODE16_SEL pushw 0 push ax shr eax,16 shl eax, 4 mov edx,[esp+2*4] mov [edx+CODE16_SEL+2],ax mov [edx+DATA16_SEL+2],ax shr eax,16 mov [edx+CODE16_SEL+4],al mov [edx+DATA16_SEL+4],al lodsd mov ebx, eax mov eax,[esp+4*4+0] mov edx,[esp+4*4+4] mov ecx,[esp+4*4+8] push ds push DATA16_SEL pop ds call PF32 ptr [esp+4] pop ds add esp,2*4 pop eax pop ecx dec ecx .endw add esp,3*4 popad ret endif align 4 iocb endp ;--- install the JLM: just set eax=1 ;--- this tells JLOAD that it's ok to add IOTRAP to the list of ;--- loaded modules. install proc uses esi pcomm:ptr JLCOMM mov eax,1 ret align 4 install endp ;--- deinstall the JLM: just set eax=1. ;--- this tells JLOAD that it's ok to remove the module. deinstall proc pcomm:ptr JLCOMM .if ( cntCB ) mov edx, PORT @VMMCall Remove_IO_Handler .endif mov eax,1 ret align 4 deinstall endp DllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword mov eax,dwReason cmp eax,DLL_PROCESS_ATTACH jnz @F invoke install, dwRes jmp exit @@: cmp eax,DLL_PROCESS_DETACH jnz @F invoke deinstall, dwRes @@: exit: ret align 4 DllMain endp end DllMain ================================================ FILE: JLM/IOTRAP/MAKEFILE ================================================ # NMake/WMake Makefile to create IOTRAP.DLL # # tools alternatives #---------------------------------------------------------- # JWasm Masm v6.x # JWLink MS Link, WLink, ALink # (PatchPE) # # PatchPE: HX tool to patch a PE binary to PX ( not required for jwlink ). NAME = IOTRAP OUTDIR=Release AOPT=-c -coff -nologo -Fl$*.lst -Fo$*.obj -I..\..\Include ASM=jwasm.exe #LINK=link.exe /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /EXPORT:ddb /Entry:DllMain /OPT:NOWIN98 LINK=jwlink format win nt hx dll ru native file $*.obj name $*.DLL op q,MAP=$*.MAP export _ddb.1 ALL: $(OUTDIR) $(OUTDIR)\$(NAME).DLL $(OUTDIR)\TESTIOT.COM $(OUTDIR): @mkdir $(OUTDIR) $(OUTDIR)\$(NAME).DLL: $(OUTDIR)\$(NAME).obj Makefile @$(LINK) # @..\patchpe $*.DLL $(OUTDIR)\$(NAME).obj: $(NAME).asm @$(ASM) $(AOPT) $(NAME).asm $(OUTDIR)\TESTIOT.COM: TESTIOT.asm @$(ASM) -nologo -bin -Fo=$*.COM TESTIOT.asm ================================================ FILE: JLM/IOTRAP/README.TXT ================================================ 1. About IOTRAP is a JLM sample which demonstrates how to trap IO port access. Note: This is somewhat obsolete now; trapping I/O port access in V86-mode is now better done with the help of JLM QPIEMU. 2. How to install and uninstall IOTRAP IOTRAP can be installed either as a device driver in CONFIG.SYS: DEVICE=JLOAD.EXE IOTRAP.DLL or as a TSR from the command line: JLOAD IOTRAP.DLL To uninstall, use JLOAD's -u option: JLOAD -u IOTRAP.DLL 3. How to test IOTRAP - install IOTRAP: C:\>JLOAD iotrap.dll - install TESTIOT: C:\>testiot - start DEBUG: C:\>debug - read port 100: -i 100 now a colored string '*#!+' should appear on line 25. 4. License IOTRAP is Public Domain. Japheth ================================================ FILE: JLM/IOTRAP/TESTIOT.ASM ================================================ ;--- tsr to test JLM IOTRAP. ;--- it installs a callback which is called whenever port 100h ;--- is accessed. ;--- the callback displays some colored characters on line 25. ;--- assemble: JWasm -bin -Fo testiot.com testiot.asm .286 .model tiny .stack 2048 .dosseg .386 .data szOK1 db "IOTRAP found.",13,10,'$' szOK2 db "TESTIOT installed.",13,10,'$' szErr1 db "IOTRAP is NOT installed!",13,10,'$' szErr2 db "IOTRAP has wrong version!",13,10,'$' szErr3 db "register callback failed!",13,10,'$' .code org 100h start: mov ax, 1684h ;get IOTRAP's entry point mov bx, 6661h int 2Fh cmp al,0 ;IOTRAP installed? jnz not_installed push es push di mov bp,sp mov dx,offset szOK1 mov ah,9 int 21h mov ax,0000 ;call "get version" call dword ptr [bp] ;--- check version and exit if not ok ; cmp ax,??? ; jc wrong_version ;--- API: CX:DX=CS:IP of callback, BX=param mov cx,cs mov dx,offset iocb mov bx,0 mov ax,0001 ;call "register callback" call dword ptr [bp] jc register_failed mov dx,offset szOK2 mov ah,9 int 21h mov dx,offset resident shr dx,4 inc dx mov ax,3100h int 21h iocb: ;--- callback when i/o was detected. ;--- cpu is in 16-bit protected-mode, ring 0. ;--- EAX=data of IO ;--- DX=IO port ;--- CX=type of IO ;--- BX=value when callback was "registered" ;--- DS=data segment ;--- ES=flat segment ;--- SS:ESP=flat host stack! push esi mov esi,0B8000h ;--- display '*#!+' in colors at line 25 mov word ptr es:[esi+160*24+0],'*'+100h*40h mov word ptr es:[esi+160*24+2],'#'+100h*51h mov word ptr es:[esi+160*24+4],'!'+100h*62h mov word ptr es:[esi+160*24+6],'+'+100h*73h pop esi retd ;a 32bit RETF is needed!!! resident label byte not_installed: mov dx,offset szErr1 mov ah,9 int 21h int 20h wrong_version: mov dx,offset szErr2 mov ah,9 int 21h int 20h register_failed: mov dx,offset szErr3 mov ah,9 int 21h int 20h end start ================================================ FILE: JLM/JCLOCK/JCLOCK.ASM ================================================ ; ; JCLOCK 1.1 ; ; JLM example -- CLOCK for JEMM386/JEMMEX ; It is the first JLM with screen input/output :-) ; ; (C) 2007 Alexey Voskov ; ; Compile with FASM for WIN32 ; and change PE signature into PX ; format PE CONSOLE 4.0 DLL entry DllEntryPoint include '%FASMINC%\win32a.inc' ; Constants ; 1. Addresses DispMode = 000449h ; BYTE - display mode ColNum = 00044Ah ; WORD - number of columns Timer = 00046Ch ; DWORD - timer of BIOS CoVideo = 0B8000h ; Video buffer offset for MODE CO80/CO40 MoVideo = 0B0000h ; Video buffer offset for MODE MONO ; 2. Text properties attr = 2Eh ; TEXT ATTRIBUTE mask0 = attr*1000000h + attr*100h ; Masks for text field mask1 = mask0 + ':' ; initialization mask2 = mask0 + ':' * 10000h ; ;********************************************************************* section '.code' code readable executable ; ; This procedure is a new INT 08h subroutine ; proc newint08 ; Initialization pushad mov ebp, esp push ss pop ds push ss pop es ; Timer check mov eax, [Timer] ; Get BIOS timer and eax, 7 ; eax % 8 == 0 test eax, eax ; jnz intexit ; if not -- quit ; (we don't have to do IN/OUT every 1/19 sec!) ; Video mode check mov al, [DispMode] cmp al, 07h ; MODE MONO ? jne color ; if not -- check color mode mov ebx, MoVideo ; Load video buffer offset jmp mono color: cmp al, 03h ; MODE CO80/CO40 ? ja intexit ; if not -- quit mov ebx, CoVideo ; Load video buffer offset mono: ; Set up initial position movzx esi, word [ColNum] ; ESI = 2*ColNum - 16 + EBX shl esi, 1 sub esi, 16 add esi, ebx ; EBX == 0B8000h or 0B0000h ; Clock output clkout: ; 1. Draw mask mov dword [esi], mask0 mov dword [esi+4], mask1 mov dword [esi+8], mask2 mov dword [esi+12], mask0 ; 2. Get current time mov al, 4 ; Hours call printBCDcell add esi, 6 mov al, 2 ; Minutes call printBCDcell add esi, 6 mov al, 0 ; Seconds call printBCDcell ; Call the old INT 08h subroutine intexit: popad ; Restore registers and stack db 0EAh ; ASM command: oldint08: db 6 dup 0 ; jmp far 0000:00000000 ; (48-bit far call) endp ; Procedure prints CMOS cell ; cell must be in BCD format ; ; AL - cell number ; EBX+ESI - video RAM address ; proc printBCDcell ; Get byte from CMOS RTC out 70h, al ; Set cell number in CMOS RTC mov ecx, 02000h ; A short delay @@dl: inc ecx ; CMOS RTC memory is slow dec ecx ; loop @@dl ; in al, 71h ; Get byte from cell ; High half-byte of BCD push ax and al, 0F0h shr al, 4 add al, '0' mov [esi], al pop ax ; Low half-byte of BCD and al, 00Fh add al, '0' mov [esi+2], al ret endp ; This procedure hooks INT 08h ; ; WARNING -- it contains low-level work with IDT ; In newer versions of JEMM it probably ; will be replaced with special API ; intn = 08h ; INTERRUPT NUMBER proc install ; Beginning sub esp,8 ; Reserve memory in stack ; Get pointer to IDT sidt [esp] ; Write register of interrupt ; descriptor table to [ESP] mov esi,[esp+2] ; Get IDT beginning address into ESI ; Save old interrupt descriptor mov ax,[esi+6+intn*8] ; Get bits 31-16 shl eax,16 ; of offset mov ax,[esi+0+intn*8] ; Get bits 15-0 of offset mov dword ptr oldint08, eax ; Save offset mov ax,[esi+2+intn*8] ; Get selector mov word ptr oldint08+4,ax ; Save selector ; Set up new interrupt descriptor mov eax, newint08 ; Get offset of new interrupt procedure mov [esi+0+intn*8],ax ; And write it shr eax, 16 ; into IDT mov [esi+6+intn*8],ax ; (SELECTOR UNCHANGED!) ; Final add esp,8 ; Free memory in stack ret ; Exit endp ; NOTE: ; ; Interrupt descriptor format (gate): ; | OFFSET(bits 31-16) | FLAGS | SEG SELECTOR | OFFSET (bits 15-0) ; ; ; DLL entry point ; proc DllEntryPoint hinstDLL,fdwReason,lpvReserved cmp [fdwReason], DLL_PROCESS_ATTACH jne @@exitDLL call install mov eax,TRUE @@exitDLL: ret endp ;********************************************************************* ; ; * DON'T STRIP RELOCATIONS ! JLM MUST HAVE THEM ! * ; section '.reloc' fixups data discardable ================================================ FILE: JLM/JCLOCK/JCLOCK2.ASM ================================================ ; this is a modified JCLOCK version which uses the Jemm API to hook int 1Ch. ; Thus, it can be unloaded. ; ; (C) 2007 Alexey Voskov ; ; Compile with FASM for WIN32 ; and change PE signature into PX format PE CONSOLE 4.0 DLL entry DllEntryPoint include '%FASMINC%\win32a.inc' include '%JLMINC%\jlmfasm.inc' ; Constants ; 1. Addresses DispMode = 000449h ; BYTE - display mode ColNum = 00044Ah ; WORD - number of columns Timer = 00046Ch ; DWORD - timer of BIOS CoVideo = 0B8000h ; Video buffer offset for MODE CO80/CO40 MoVideo = 0B0000h ; Video buffer offset for MODE MONO ; 2. Text properties attr = 2Eh ; TEXT ATTRIBUTE mask0 = attr*1000000h + attr*100h ; Masks for text field mask1 = mask0 + ':' ; initialization mask2 = mask0 + ':' * 10000h ; ;********************************************************************* section '.code' code readable executable ; ; This procedure is a hook for INT 1Ch ; oldvec dd 0 dd oldvec ; a hook procedure must be preceeded by a dword ; which contains the address where the old vector ; will be stored. proc hookproc ; Initialization ; Timer check pushad mov eax, [Timer] ; Get BIOS timer and eax, 7 ; eax % 8 == 0 test eax, eax ; jnz intexit ; if not -- quit ; (we don't have to do IN/OUT every 1/19 sec!) ; Video mode check mov al, [DispMode] cmp al, 07h ; MODE MONO ? jne color ; if not -- check color mode mov ebx, MoVideo ; Load video buffer offset jmp mono color: cmp al, 03h ; MODE CO80/CO40 ? ja intexit ; if not -- quit mov ebx, CoVideo ; Load video buffer offset mono: ; Set up initial position movzx esi, word [ColNum] ; ESI = 2*ColNum - 16 + EBX shl esi, 1 sub esi, 16 add esi, ebx ; EBX == 0B8000h or 0B0000h ; Clock output clkout: ; 1. Draw mask mov dword [esi], mask0 mov dword [esi+4], mask1 mov dword [esi+8], mask2 mov dword [esi+12], mask0 ; 2. Get current time mov al, 4 ; Hours call printBCDcell add esi, 6 mov al, 2 ; Minutes call printBCDcell add esi, 6 mov al, 0 ; Seconds call printBCDcell intexit: popad stc ; signal this INT hasn't been handled yet ret endp ; Procedure prints CMOS cell ; cell must be in BCD format ; ; AL - cell number ; EBX+ESI - video RAM address ; proc printBCDcell ; Get byte from CMOS RTC out 70h, al ; Set cell number in CMOS RTC mov ecx, 02000h ; A short delay @@dl: inc ecx ; CMOS RTC memory is slow dec ecx ; loop @@dl ; in al, 71h ; Get byte from cell ; High half-byte of BCD if 1 db 0d4h,10h ;AAM 10h add ax,3030h mov [esi+0],ah mov [esi+2],al else push ax and al, 0F0h shr al, 4 add al, '0' mov [esi], al pop ax ; Low half-byte of BCD and al, 00Fh add al, '0' mov [esi+2], al end if ret endp ; This procedure hooks INT 1Ch ; intn = 1Ch ; INTERRUPT NUMBER proc install mov eax,intn mov esi,hookproc @VMMCall Hook_V86_Int_Chain ret ; Exit endp proc deinstall mov eax,intn mov esi,hookproc @VMMCall Unhook_V86_Int_Chain ret ; Exit endp ; ; DLL entry point ; proc DllEntryPoint hinstDLL,fdwReason,lpvReserved cmp [fdwReason], DLL_PROCESS_ATTACH jne @F call install mov eax,TRUE jmp @@exitDLL @@: cmp [fdwReason], DLL_PROCESS_DETACH jne @@exitDLL call deinstall mov eax,TRUE @@exitDLL: ret endp section '.data' data readable writeable ;--- Sorry, no idea how to create an instance of a structure in fasm ... ;ddb VxD_Desc_Block 0,0,6655h,1,0,0,0,0,0, 0, 0, 0, 0 ddb dd 0 dw 0 dw 6655h db 1 db 0 db 0 db "JCLOCK2 " dd 80000000h dd 0 dd 0 dd 0 dd 0 rd 10 ;--- to make the JLM unloadable, it must have a "device id". A "device id" ;--- is obtained by exporting a DDB section '.edata' export data readable export 'JCLOCK2.DLL',\ ddb,1 ;********************************************************************* ; ; * DON'T STRIP RELOCATIONS ! JLM MUST HAVE THEM ! * ; section '.reloc' fixups data discardable ================================================ FILE: JLM/JCLOCK/JCLOCK3.ASM ================================================ ;--- JCLOCK2 in Masm/JWasm syntax .386 .model flat option casemap:none include jlm.inc ; Constants DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 ; 1. Addresses DispMode equ 000449h ; BYTE - display mode ColNum equ 00044Ah ; WORD - number of columns Timer equ 00046Ch ; DWORD - timer of BIOS CoVideo equ 0B8000h ; Video buffer offset for MODE CO80/CO40 MoVideo equ 0B0000h ; Video buffer offset for MODE MONO ; 2. Text properties attr = 2Eh ; TEXT ATTRIBUTE mask0 = attr*1000000h + attr*100h ; Masks for text field mask1 = mask0 + ':' ; initialization mask2 = mask0 + ':' * 10000h ; .data public export ddb ddb VxD_Desc_Block <0,0,6655h,1,0,0,"JCLOCK3 ",80000000h> oldvec dd 0 option dotname .drectve segment info db "-dll -fixed:no -subsystem:native " .drectve ends .hdr$2 segment dword db "PX" .hdr$2 ends .code ; ; This procedure is a hook for INT 1Ch ; HookProc hookproc, oldvec ; Initialization ; Timer check pushad mov eax, ds:[Timer] ; Get BIOS timer and eax, 7 ; eax % 8 == 0 test eax, eax ; jnz intexit ; if not -- quit ; (we don't have to do IN/OUT every 1/19 sec!) ; Video mode check mov al, ds:[DispMode] cmp al, 07h ; MODE MONO ? jne color ; if not -- check color mode mov ebx, MoVideo ; Load video buffer offset jmp mono color: cmp al, 03h ; MODE CO80/CO40 ? ja intexit ; if not -- quit mov ebx, CoVideo ; Load video buffer offset mono: ; Set up initial position movzx esi, word ptr ds:[ColNum] ; ESI = 2*ColNum - 16 + EBX shl esi, 1 sub esi, 16 add esi, ebx ; EBX == 0B8000h or 0B0000h ; Clock output clkout: ; 1. Draw mask mov dword ptr [esi], mask0 mov dword ptr [esi+4], mask1 mov dword ptr [esi+8], mask2 mov dword ptr [esi+12], mask0 ; 2. Get current time mov al, 4 ; Hours call printBCDcell add esi, 6 mov al, 2 ; Minutes call printBCDcell add esi, 6 mov al, 0 ; Seconds call printBCDcell intexit: popad stc ; signal this INT hasn't been handled yet ret hookproc endp ; Procedure prints CMOS cell ; cell must be in BCD format ; ; AL - cell number ; EBX+ESI - video RAM address ; printBCDcell proc ; Get byte from CMOS RTC out 70h, al ; Set cell number in CMOS RTC jmp $+2 jmp $+2 in al, 71h ; Get byte from cell ; High half-byte of BCD if 1 db 0d4h,10h ;AAM 10h add ax,3030h mov [esi+0],ah mov [esi+2],al else push ax and al, 0F0h shr al, 4 add al, '0' mov [esi], al pop ax ; Low half-byte of BCD and al, 00Fh add al, '0' mov [esi+2], al endif ret printBCDcell endp ; This procedure hooks INT 1Ch ; intn = 1Ch ; INTERRUPT NUMBER install proc mov eax,intn mov esi,hookproc @VMMCall Hook_V86_Int_Chain ret ; Exit install endp deinstall proc mov eax,intn mov esi,hookproc @VMMCall Unhook_V86_Int_Chain ret ; Exit deinstall endp ; ; DLL entry point ; DllEntryPoint proc stdcall hinstDLL:dword, fdwReason:dword, lpvReserved:dword cmp [fdwReason], DLL_PROCESS_ATTACH jne @F call install mov eax,1 jmp @@exitDLL @@: cmp [fdwReason], DLL_PROCESS_DETACH jne @@exitDLL call deinstall mov eax,1 @@exitDLL: ret DllEntryPoint endp end DllEntryPoint ================================================ FILE: JLM/JCLOCK/MAKE.BAT ================================================ @echo off set FASMINC=\fasm\Include set JLMINC=..\..\Include fasm JCLOCK.ASM fasm JCLOCK2.ASM \hx\bin\patchpe JCLOCK.DLL \hx\bin\patchpe JCLOCK2.DLL ================================================ FILE: JLM/JCLOCK/MAKE3.BAT ================================================ @echo off rem make jclock3.dll - no link step required. jwasm -pe -I..\..\Include -Fo=JCLOCK3.DLL JCLOCK3.ASM ================================================ FILE: JLM/JCLOCK/README.TXT ================================================ JCLOCK 1.4 -- Second JLM (JemmEx Loadable Module) (C) 2007 Alexey Voskov Shows clock in the text mode. Usage ~~~~~ DEVICE=JEMMEX.EXE DEVICE=JLOAD.EXE JCLOCK.DLL History ~~~~~~~ Ver Date Changed ----------------------------------------------------------------- 1.4 17-APR-2009 JClock2 supports JLoad's -u switch. 1.3 19-FEB-2008 patchpe missed in MAKE.BAT 1.2 30-JAN-2008 environment variables FASMINC and JLMINC used 1.1 05-MAY-2007 VME compatibility added Improved source code (easy change of clock color) 1.0 04-MAY-2007 Original release ================================================ FILE: JLM/JLSTUB/Build/JLSTUB.lst ================================================ JWasm v2.18, May 7 2024 JLSTUB.ASM ;--- MZ stub to run jload.exe ;--- to be assembled with Masm or JWasm ;--- it's a derivate of dpmildxx... = 600 ?MINMEM = 600h ;min free paragraphs for JLOAD.EXE = 44 MAXDIR = 64+4 ;max length of a directory path (including 00h) = 50 MAXPATH = MAXDIR+12 = 1 ?BESAFE = 1 ;1=check if JLoad.exe looks ok. = 1 ?DOSMEMCHK = 1 ;0=check for sufficient DOS memory .286 ifdef __JWASM__ option MZ:40h endif = D cr equ 13 = A lf equ 10 00000000 mzhdr struct 00000000 e_magic WORD ? ;+0 00000002 e_cblp WORD ? ;+2 00000004 e_cp WORD ? ;+4 00000006 e_crlc WORD ? ;+6 number of relocation records 00000008 e_cparhdr WORD ? ;+8 0000000A e_minalloc WORD ? ;+10 0000000C e_maxalloc WORD ? ;+12 0000000E e_ss WORD ? ;+14 00000010 e_sp WORD ? ;+16 00000012 e_csum WORD ? ;+18 00000014 e_ip WORD ? ;+20 00000016 e_cs WORD ? ;+22 00000018 e_lfarlc WORD ? ;+24 begin relocation records 0000001A mzhdr ends 0000 _TEXT segment public 'CODE' = [bp+00h] szPgm equ [bp+00h] ;execute program name ("JLOAD.EXE") = [bp-MAXPATH] szParm equ [bp-MAXPATH] ;application name (from environment) 0000 launch proc 0000 33D2 xor DX,DX 0002 B120 mov cl,20h 0004 B43F mov ah,3Fh ;read the MZ header 0006 CD21 int 21h 0008 7303E9DD00 jc readerror if ?BESAFE ;--- additional tests 000D 33F6 xor si,si 000F 8B04 mov ax, [si].mzhdr.e_magic 0011 3D4D5A cmp ax,"ZM" 0014 7403E9CC00 jnz formerror 0019 8B4406 mov ax, [si].mzhdr.e_crlc ; no of relocation entries 001C 8BF8 mov di,ax 001E 23C0 and ax,ax 0020 7420 jz norelocs 0022 50 push ax 0023 33C9 xor cx,cx 0025 8B5418 mov dx,[si].mzhdr.e_lfarlc ; begin relocations 0028 B80042 mov ax,4200h 002B CD21 int 21h 002D 59 pop cx 002E C1E102 shl cx,2 ; 4 byte each reloc 0031 2BE1 sub sp,cx 0033 8BD4 mov dx,sp 0035 1E push ds 0036 16 push ss 0037 1F pop ds 0038 B43F mov ah,3Fh ; read relocs at SS:SP 003A CD21 int 21h 003C 1F pop ds 003D 7303E9A300 jc formerror 0042 norelocs: 0042 8B4408 mov ax,[si].mzhdr.e_cparhdr ; size of header in paragraphs 0045 FF740E push [si].mzhdr.e_ss 0048 FF7410 push [si].mzhdr.e_sp 004B 8B7414 mov si, [si].mzhdr.e_ip 004E C1E004 shl ax,4 0051 8BD0 mov dx,ax 0053 33C9 xor cx,cx 0055 B80042 mov ax,4200h 0058 CD21 int 21h 005A 33D2 xor dx,dx endif ;--- read JLoad.exe binary 005C B43F MOV AH,3Fh 005E B90060 mov cx,?MINMEM shl 4 0061 CD21 INT 21h 0063 7303E98200 JC readerror ;---> error "read error" if ?BESAFE 0068 3BC1 cmp ax,cx ;JLOAD binary must be < 24 kB 006A 7379 jnc formerror endif 006C B43E mov ah,3Eh 006E CD21 int 21h 0070 5A pop dx 0071 5D pop bp if ?BESAFE 0072 8BCF mov cx,di ;some relocs to resolve? 0074 E317 jcxz norelocs2 0076 8BFC mov di,sp 0078 8CD8 mov ax,ds 007A @@: 007A 368B5D02 mov bx,ss:[di+2] 007E C1E304 shl bx,4 ;size of loader is <= 24 kB, so no overflow possible 0081 36031D add bx,ss:[di+0] 0084 0107 add [bx],ax 0086 83C704 add di,4 0089 E2EF loop @B 008B 8BE7 mov sp,di 008D norelocs2: endif ;--- fill JLoad's PSP - don't overwrite possible cmdline arguments 008D B451 mov ah,51h 008F CD21 int 21h 0091 56 push si 0092 8BF4 mov si, sp 0094 83C602 add si, 2 0097 BF8000 mov di, 80h 009A 268A0D mov cl, es:[di] 009D B500 mov ch, 0 009F 47 inc di 00A0 E312 jcxz nocmdl 00A2 2BE1 sub sp, cx 00A4 8BDC mov bx, sp 00A6 51 push cx 00A7 57 push di 00A8 @@: 00A8 268A05 mov al, es:[di] 00AB 368807 mov ss:[bx], al 00AE 47 inc di 00AF 43 inc bx 00B0 E2F6 loop @B 00B2 5F pop di 00B3 59 pop cx 00B4 nocmdl: 00B4 B020 mov al,' ' 00B6 AA stosb 00B7 @@: 00B7 36AC lodsb ss:[si] 00B9 AA stosb 00BA 22C0 and al, al 00BC 75F9 jnz @B 00BE 4F dec di 00BF E307 jcxz @F 00C1 8BF4 mov si, sp 00C3 F336A4 rep movsb es:[di], ss:[si] 00C6 8BE6 mov sp, si 00C8 @@: 00C8 26C6050D mov byte ptr es:[di],13 00CC 8BC7 mov ax, di 00CE BF8000 mov di, 80h 00D1 2BC7 sub ax, di ; might actually be larger than 127 bytes ... ignore for now. 00D3 AA stosb 00D4 5E pop si ;--- setup SS:SP 00D5 8CC0 mov ax, es 00D7 03C5 add ax, bp 00D9 83C010 add ax, 10h 00DC 8ED0 mov ss, ax 00DE 8BE2 mov sp, dx 00E0 1E push ds 00E1 56 push si 00E2 06 push es 00E3 1F pop ds 00E4 CB retf 00E5 launch endp if ?BESAFE 00E5 formerror: 00E5 BA0000 mov dx,offset dFormError 00E8 EB03 jmp error1 endif 00EA readerror: 00EA BA0000 mov dx,offset dReadError 00ED error1: if 0 endif 00ED errorX: ;<--- errors 00ED 0E push cs 00EE 1F pop ds 00EF E80B00 call DispError 00F2 BA0000 MOV DX,offset dFatal 00F5 E80500 call DispError 00F8 B8F04C mov ax,4CF0h ;error code F0h 00FB CD21 int 21h 00FD DispError: ;display error msg in DX 00FD B409 MOV AH,09h 00FF CD21 INT 21h 0101 C3 ret 0102 202D206C6F6164696E dFatal db ' - loading aborted',cr,lf,'$' 0117 72656164206572726F dReadError db "read error",'$' if ?BESAFE 0122 696E76616C6964204A dFormError db "invalid JLoad.exe",'$' endif 0134 endcopy label byte 0134 start proc ;--- setup stack at end of memory block 0134 FC cld 0135 A10200 mov ax,ds:[0002] ife ?DOSMEMCHK endif 0138 83E830 sub ax,30h 013B 8ED0 mov ss,ax 013D BCB002 mov sp,300h-MAXPATH ;--- setup BP stack frame 0140 8BEC mov BP,SP 0142 83EC50 sub SP,MAXPATH ;--- fill szParm with application name 0145 8BF4 mov SI,SP ;SI=szParm 0147 06 push es ;save PSP 0148 268E062C00 mov es,es:[002Ch] ;ES=environment 014D E82300 CALL GetAppName ;get name to SS:SI ( SI modified! ) 0150 0E push cs 0151 1F pop ds ;--- search "PATH=", DI=NULL if it doesn't exist 0152 E83900 CALL SearchPath 0155 8BF7 MOV SI,DI ;set SI to start of PATH= value ;--- fill szPgm (search JLOAD) 0157 16 push SS 0158 1F pop DS 0159 E84C00 CALL SearchJLoad 015C BA0000 MOV DX,offset dNotFnd 015F 728C JB errorX ;---> error "not found" ;--- copy the final code to end of memory block 0161 B90000 mov cx,offset endcopy 0164 16 push ss 0165 07 pop es 0166 33FF xor di,di 0168 33F6 xor si,si 016A 2E db 2Eh ;CS prefix 016B F3A4 rep movsb 016D 07 pop es ;restore PSP 016E 1E push ds 016F 51 push cx ;CX is 0 0170 0E push cs 0171 1F pop ds 0172 CB retf 0173 start endp ;--- search name of app in environment ;--- the name may be a full path or not ;--- depending on caller ;--- in: ES=environment, SS:SI=dest ;--- modifies AX,CX,SI,DI 0173 GetAppName proc 0173 2BFF SUB DI,DI 0175 B000 mov al,00 0177 B9FFFF mov cx,-1 017A @@: 017A F2AE repnz scasb ;search end of environ (00,00) 017C AE scasb 017D 75FB jnz @B 017F 47 inc di ;skip 0001 0180 47 inc di 0181 @@: 0181 268A05 mov al,es:[di] 0184 368804 mov ss:[si],al 0187 46 inc si 0188 47 inc di 0189 22C0 and al,al 018B 75F4 jnz @B 018D C3 RET 018E GetAppName endp ;--- search PATH= in Environment ;--- In: ES=environment ;--- Out: DI-> behind "PATH=" or NULL ;--- modifies AX,CX,SI,DI 018E SearchPath proc 018E 2BFF SUB DI,DI 0190 nextitem: 0190 BE0000 MOV SI,offset szPath 0193 B90500 MOV CX,SIZPATH 0196 F3A6 REPZ CMPSB 0198 740D JZ found 019A B000 mov al,00 019C B57F mov ch,7Fh 019E F2AE repnz scasb 01A0 263A05 cmp al,es:[di] 01A3 75EB JNZ nextitem 01A5 2BFF sub di,di 01A7 found: 01A7 C3 RET 01A8 SearchPath endp ;--- search JLOAD, first in current Dir, then scan PATH ;--- Input: ES=environ, SI=address PATH variable or 0000 (no PATH exists) ;--- Output: NC if found, C if error ;--- full loader path in szPgm ;--- modifies AX,BX,CX,DX,SI,DI 01A8 SearchJLoad proc 01A8 8D7E00 lea DI,[bp+00h] 01AB 8BD7 mov dx,di 01AD nextentry: ;<---- 01AD 56 PUSH SI 01AE BE0000 mov si,offset ldrname ;Name "JLOAD.EXE" 01B1 B90900 mov cx,SIZLDRNAME 01B4 @@: 01B4 2E db 2Eh ;CS prefix 01B5 AC lodsb 01B6 8805 mov [di],al 01B8 47 inc di 01B9 E2F9 loop @B 01BB 880D mov [di],cl 01BD B8003D MOV AX,3D00h ;open JLOAD.EXE 01C0 CD21 INT 21h 01C2 5E POP SI 01C3 7328 JNB ldrfound ;jmp if found! 01C5 23F6 AND SI,SI 01C7 F9 stc 01C8 7424 JZ notfound ;PATH not defined, so we are done 01CA 8BFA MOV DI,DX 01CC B94400 mov cx,MAXDIR 01CF @@: 01CF 268A04 mov al,es:[si] 01D2 8805 mov [di],al 01D4 46 inc si 01D5 47 inc di 01D6 3C3B CMP AL,';' 01D8 7406 JZ @F 01DA 3C00 CMP AL,00 01DC E0F1 LOOPNZ @B ;PATH done 01DE 33F6 XOR SI,SI 01E0 @@: 01E0 4F DEC DI 01E1 807DFF5C CMP Byte Ptr [DI-01],'\' 01E5 74C6 JZ nextentry 01E7 C6055C MOV Byte Ptr [DI],'\' 01EA 47 INC DI 01EB EBC0 JMP nextentry 01ED ldrfound: 01ED 93 XCHG ax,bx ;=MOV BX,AX ; MOV AH,3Eh ;close file ; INT 21h ; CLC 01EE notfound: 01EE C3 RET 01EF SearchJLoad endp 01EF 504154483D szPath db 'PATH=' = 5 SIZPATH equ $ - szPath 01F4 43616E27742066696E dNotFnd db "Can't find " 01FF ldrname label byte 01FF 4A4C4F41442E455845 db 'JLOAD.EXE' = 9 SIZLDRNAME equ $ - ldrname 0208 24 db '$' ife ?DOSMEMCHK endif 0209 _TEXT ends if ?DOSMEMCHK 0000 _BSS segment public 'DATA' 0000 000000000000000000 db (?MINMEM+14h) * 16 dup (?) 6140 _BSS ends endif 0000 STACK segment stack 'STACK' 0000 STACK ends end start Binary Map: Segment Pos(file) RVA Size(fil) Size(mem) ---------------------------------------------------------------
0 0 40 0 _TEXT 40 0 209 209 _BSS 0 210 0 6140 STACK 0 6350 0 0 --------------------------------------------------------------- 249 6350 Macros: N a m e Type @CatStr . . . . . . . . . . . . Func @Environ . . . . . . . . . . . . Func @InStr . . . . . . . . . . . . . Func @SizeStr . . . . . . . . . . . . Func @SubStr . . . . . . . . . . . . Func Structures and Unions: N a m e Size/Ofs Type mzhdr . . . . . . . . . . . . . 1A e_magic . . . . . . . . . . . 0 Word e_cblp . . . . . . . . . . . . 2 Word e_cp . . . . . . . . . . . . . 4 Word e_crlc . . . . . . . . . . . . 6 Word e_cparhdr . . . . . . . . . . 8 Word e_minalloc . . . . . . . . . . A Word e_maxalloc . . . . . . . . . . C Word e_ss . . . . . . . . . . . . . E Word e_sp . . . . . . . . . . . . . 10 Word e_csum . . . . . . . . . . . . 12 Word e_ip . . . . . . . . . . . . . 14 Word e_cs . . . . . . . . . . . . . 16 Word e_lfarlc . . . . . . . . . . . 18 Word Segments and Groups: N a m e Size Length Align Combine Class STACK . . . . . . . . . . . . . 16 Bit 0000 Para Stack 'STACK' _BSS . . . . . . . . . . . . . . 16 Bit 6140 Para Public 'DATA' _TEXT . . . . . . . . . . . . . 16 Bit 0209 Para Public 'CODE' Procedures, parameters and locals: N a m e Type Value Segment Length GetAppName . . . . . . . . . . . P Near 0173 _TEXT 001B Public L&_0006 . . . . . . . . . . . L Near 0181 _TEXT L&_0005 . . . . . . . . . . . L Near 017A _TEXT SearchJLoad . . . . . . . . . . P Near 01A8 _TEXT 0047 Public L&_0008 . . . . . . . . . . . L Near 01CF _TEXT notfound . . . . . . . . . . . L Near 01EE _TEXT L&_0009 . . . . . . . . . . . L Near 01E0 _TEXT L&_0007 . . . . . . . . . . . L Near 01B4 _TEXT nextentry . . . . . . . . . . L Near 01AD _TEXT ldrfound . . . . . . . . . . . L Near 01ED _TEXT SearchPath . . . . . . . . . . . P Near 018E _TEXT 001A Public found . . . . . . . . . . . . L Near 01A7 _TEXT nextitem . . . . . . . . . . . L Near 0190 _TEXT launch . . . . . . . . . . . . . P Near 0000 _TEXT 00E5 Public L&_0004 . . . . . . . . . . . L Near 00C8 _TEXT L&_0002 . . . . . . . . . . . L Near 00A8 _TEXT L&_0003 . . . . . . . . . . . L Near 00B7 _TEXT L&_0001 . . . . . . . . . . . L Near 007A _TEXT nocmdl . . . . . . . . . . . . L Near 00B4 _TEXT norelocs . . . . . . . . . . . L Near 0042 _TEXT norelocs2 . . . . . . . . . . L Near 008D _TEXT start . . . . . . . . . . . . . P Near 0134 _TEXT 003F Public Symbols: N a m e Type Value Attr ?BESAFE . . . . . . . . . . . . Number 1h ?DOSMEMCHK . . . . . . . . . . . Number 1h ?MINMEM . . . . . . . . . . . . Number 600h DispError . . . . . . . . . . . L Near FDh _TEXT MAXDIR . . . . . . . . . . . . . Number 44h MAXPATH . . . . . . . . . . . . Number 50h SIZLDRNAME . . . . . . . . . . . Number 9h SIZPATH . . . . . . . . . . . . Number 5h cr . . . . . . . . . . . . . . . Number Dh dFatal . . . . . . . . . . . . . Byte[21] 102h _TEXT dFormError . . . . . . . . . . . Byte[18] 122h _TEXT dNotFnd . . . . . . . . . . . . Byte[11] 1F4h _TEXT dReadError . . . . . . . . . . . Byte[11] 117h _TEXT endcopy . . . . . . . . . . . . Byte 134h _TEXT error1 . . . . . . . . . . . . . L Near EDh _TEXT errorX . . . . . . . . . . . . . L Near EDh _TEXT formerror . . . . . . . . . . . L Near E5h _TEXT ldrname . . . . . . . . . . . . Byte 1FFh _TEXT lf . . . . . . . . . . . . . . . Number Ah readerror . . . . . . . . . . . L Near EAh _TEXT szParm . . . . . . . . . . . . . Text [bp-MAXPATH] szPath . . . . . . . . . . . . . Byte[5] 1EFh _TEXT szPgm . . . . . . . . . . . . . Text [bp+00h] JLSTUB.ASM: 418 lines, 2 passes, 2 ms, 0 warnings, 0 errors ================================================ FILE: JLM/JLSTUB/JLSTUB.ASM ================================================ ;--- MZ stub to run jload.exe ;--- to be assembled with Masm or JWasm ;--- it's a derivate of dpmildxx... ?MINMEM = 600h ;min free paragraphs for JLOAD.EXE MAXDIR = 64+4 ;max length of a directory path (including 00h) MAXPATH = MAXDIR+12 ?BESAFE = 1 ;1=check if JLoad.exe looks ok. ?DOSMEMCHK = 1 ;0=check for sufficient DOS memory .286 ifdef __JWASM__ option MZ:40h endif cr equ 13 lf equ 10 mzhdr struct e_magic WORD ? ;+0 e_cblp WORD ? ;+2 e_cp WORD ? ;+4 e_crlc WORD ? ;+6 number of relocation records e_cparhdr WORD ? ;+8 e_minalloc WORD ? ;+10 e_maxalloc WORD ? ;+12 e_ss WORD ? ;+14 e_sp WORD ? ;+16 e_csum WORD ? ;+18 e_ip WORD ? ;+20 e_cs WORD ? ;+22 e_lfarlc WORD ? ;+24 begin relocation records mzhdr ends _TEXT segment public 'CODE' szPgm equ [bp+00h] ;execute program name ("JLOAD.EXE") szParm equ [bp-MAXPATH] ;application name (from environment) launch proc xor DX,DX mov cl,20h mov ah,3Fh ;read the MZ header int 21h jc readerror if ?BESAFE ;--- additional tests xor si,si mov ax, [si].mzhdr.e_magic cmp ax,"ZM" jnz formerror mov ax, [si].mzhdr.e_crlc ; no of relocation entries mov di,ax and ax,ax jz norelocs push ax xor cx,cx mov dx,[si].mzhdr.e_lfarlc ; begin relocations mov ax,4200h int 21h pop cx shl cx,2 ; 4 byte each reloc sub sp,cx mov dx,sp push ds push ss pop ds mov ah,3Fh ; read relocs at SS:SP int 21h pop ds jc formerror norelocs: mov ax,[si].mzhdr.e_cparhdr ; size of header in paragraphs push [si].mzhdr.e_ss push [si].mzhdr.e_sp mov si, [si].mzhdr.e_ip shl ax,4 mov dx,ax xor cx,cx mov ax,4200h int 21h xor dx,dx endif ;--- read JLoad.exe binary MOV AH,3Fh mov cx,?MINMEM shl 4 INT 21h JC readerror ;---> error "read error" if ?BESAFE cmp ax,cx ;JLOAD binary must be < 24 kB jnc formerror endif mov ah,3Eh int 21h pop dx pop bp if ?BESAFE mov cx,di ;some relocs to resolve? jcxz norelocs2 mov di,sp mov ax,ds @@: mov bx,ss:[di+2] shl bx,4 ;size of loader is <= 24 kB, so no overflow possible add bx,ss:[di+0] add [bx],ax add di,4 loop @B mov sp,di norelocs2: endif ;--- fill JLoad's PSP - don't overwrite possible cmdline arguments mov ah,51h int 21h push si mov si, sp add si, 2 mov di, 80h mov cl, es:[di] mov ch, 0 inc di jcxz nocmdl sub sp, cx mov bx, sp push cx push di @@: mov al, es:[di] mov ss:[bx], al inc di inc bx loop @B pop di pop cx nocmdl: mov al,' ' stosb @@: lodsb ss:[si] stosb and al, al jnz @B dec di jcxz @F mov si, sp rep movsb es:[di], ss:[si] mov sp, si @@: mov byte ptr es:[di],13 mov ax, di mov di, 80h sub ax, di ; might actually be larger than 127 bytes ... ignore for now. stosb pop si ;--- setup SS:SP mov ax, es add ax, bp add ax, 10h mov ss, ax mov sp, dx push ds push si push es pop ds retf launch endp if ?BESAFE formerror: mov dx,offset dFormError jmp error1 endif readerror: mov dx,offset dReadError error1: if 0 ;--- close file? Will be done by DOS. mov ah,3Eh int 21h endif errorX: ;<--- errors push cs pop ds call DispError MOV DX,offset dFatal call DispError mov ax,4CF0h ;error code F0h int 21h DispError: ;display error msg in DX MOV AH,09h INT 21h ret dFatal db ' - loading aborted',cr,lf,'$' dReadError db "read error",'$' if ?BESAFE dFormError db "invalid JLoad.exe",'$' endif endcopy label byte start proc ;--- setup stack at end of memory block cld mov ax,ds:[0002] ife ?DOSMEMCHK mov bp,es push ax sub ax,bp cmp ax,?MINMEM+30h ;enough free memory in this block? pop ax mov dx,offset dMemory jc errorX endif sub ax,30h mov ss,ax mov sp,300h-MAXPATH ;--- setup BP stack frame mov BP,SP sub SP,MAXPATH ;--- fill szParm with application name mov SI,SP ;SI=szParm push es ;save PSP mov es,es:[002Ch] ;ES=environment CALL GetAppName ;get name to SS:SI ( SI modified! ) push cs pop ds ;--- search "PATH=", DI=NULL if it doesn't exist CALL SearchPath MOV SI,DI ;set SI to start of PATH= value ;--- fill szPgm (search JLOAD) push SS pop DS CALL SearchJLoad MOV DX,offset dNotFnd JB errorX ;---> error "not found" ;--- copy the final code to end of memory block mov cx,offset endcopy push ss pop es xor di,di xor si,si db 2Eh ;CS prefix rep movsb pop es ;restore PSP push ds push cx ;CX is 0 push cs pop ds retf start endp ;--- search name of app in environment ;--- the name may be a full path or not ;--- depending on caller ;--- in: ES=environment, SS:SI=dest ;--- modifies AX,CX,SI,DI GetAppName proc SUB DI,DI mov al,00 mov cx,-1 @@: repnz scasb ;search end of environ (00,00) scasb jnz @B inc di ;skip 0001 inc di @@: mov al,es:[di] mov ss:[si],al inc si inc di and al,al jnz @B RET GetAppName endp ;--- search PATH= in Environment ;--- In: ES=environment ;--- Out: DI-> behind "PATH=" or NULL ;--- modifies AX,CX,SI,DI SearchPath proc SUB DI,DI nextitem: MOV SI,offset szPath MOV CX,SIZPATH REPZ CMPSB JZ found mov al,00 mov ch,7Fh repnz scasb cmp al,es:[di] JNZ nextitem sub di,di found: RET SearchPath endp ;--- search JLOAD, first in current Dir, then scan PATH ;--- Input: ES=environ, SI=address PATH variable or 0000 (no PATH exists) ;--- Output: NC if found, C if error ;--- full loader path in szPgm ;--- modifies AX,BX,CX,DX,SI,DI SearchJLoad proc lea DI,szPgm mov dx,di nextentry: ;<---- PUSH SI mov si,offset ldrname ;Name "JLOAD.EXE" mov cx,SIZLDRNAME @@: db 2Eh ;CS prefix lodsb mov [di],al inc di loop @B mov [di],cl MOV AX,3D00h ;open JLOAD.EXE INT 21h POP SI JNB ldrfound ;jmp if found! AND SI,SI stc JZ notfound ;PATH not defined, so we are done MOV DI,DX mov cx,MAXDIR @@: mov al,es:[si] mov [di],al inc si inc di CMP AL,';' JZ @F CMP AL,00 LOOPNZ @B ;PATH done XOR SI,SI @@: DEC DI CMP Byte Ptr [DI-01],'\' JZ nextentry MOV Byte Ptr [DI],'\' INC DI JMP nextentry ldrfound: XCHG ax,bx ;=MOV BX,AX ; MOV AH,3Eh ;close file ; INT 21h ; CLC notfound: RET SearchJLoad endp szPath db 'PATH=' SIZPATH equ $ - szPath dNotFnd db "Can't find " ldrname label byte db 'JLOAD.EXE' SIZLDRNAME equ $ - ldrname db '$' ife ?DOSMEMCHK dMemory db "Insufficient memory",'$' endif _TEXT ends if ?DOSMEMCHK _BSS segment public 'DATA' db (?MINMEM+14h) * 16 dup (?) _BSS ends endif STACK segment stack 'STACK' STACK ends end start ================================================ FILE: JLM/JLSTUB/JLStub.txt ================================================ 1. About JLStub JLStub is NOT a JLM. It's a stub, supposed to be added to other JLMs. It's purpose is to avoid having to type "jload" before the JLM. Instead, the stub is added to the JLM. When the JLM is launched by DOS, the stub will run, searching and launching jload.exe. ================================================ FILE: JLM/JLSTUB/Make.BAT ================================================ @echo off if not exist Build\NUL mkdir Build jwasm -nologo -mz -FlBuild\ -Fo=Build\jlstub.bin jlstub.asm ================================================ FILE: JLM/KEYBGR/KEYBGR.ASM ================================================ ;*** german keyboard driver for MF keyboards ;--- the first JLM (Jemm Loadable Module) ever. ;--- v1.0: initial ;--- v1.1: route nontranslated key presses to previous handler ;--- v1.2: now fully compatible with the old KEYBGR.EXE (needs 30h DOS mem) ;--- v1.3: source now compatible with JWasm .386 .model flat option casemap:none include jlm.inc cr equ 13 lf equ 10 kbdstat1 equ 417h; byte kbdstat2 equ 418h; byte bufsta equ 41ah; word bufend equ 41ch; word ebufsta equ 480h; word ebufend equ 482h; word kbdflgs equ 496h; byte LEDflgs equ 497h; byte DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 ifdef FMTPE option dotname .drectve segment info db "-subsystem:native -dll -fixed:no" .drectve ends endif .code ;--- AltGr keys altgrkeytab label byte db 03h ;2 -> db 04h ;3 -> db 08h ;7 -> { db 09h ;8 -> [ db 0Ah ;9 -> ] db 0Bh ;0 -> } db 0Ch ; -> \ db 10h ;Q -> @ db 1Bh ;+ -> ~ db 32h ;M -> db 56h ;< -> | ALTGRTABSIZ equ $ - altgrkeytab db '' db '' db '{' db '[' db ']' db '}' db '\' db '@' db '~' db '' db '|' ;--- numlock keys numpadkeytab label byte db 53h ;',' NUMPADTABSIZ equ $ - numpadkeytab public PLAB1 PLAB1 db ',' ;--- ctrl keys ctrlkeytab label byte db 15h ;ctrl z db 2ch ;ctrl y CTRLTABSIZ equ $ - ctrlkeytab db 1Ah db 19h ;--- standard keys stdkeytab label byte db 15h ;z db 1ah ; db 27h ; db 28h ; db 2ch ;y db 03h ;2 db 04h ;3 db 07h ;6 db 08h ;7 db 09h ;8 db 0ah ;9 db 0bh ;0 db 0ch ;sz db 0dh ;apost db 1bh ;+ db 29h ;^ db 2Bh ;# db 33h ;, db 34h ;. db 35h ;- db 56h ;< STDTABSIZ equ $ - stdkeytab db 'z' db '' db '' db '' db 'y' L02B3X label byte ;CAPS-LOCK insensitive keys db 0ffh db 0ffh db 0ffh db 0ffh db 0ffh db 0ffh db 0ffh db '' db "'" db '+' db '^' db '#' db 0ffh ;, db 0ffh ;. db '-' db '<' db 'Z' db '' db '' db '' db 'Y' db '"' db '' db '&' db '/' db '(' db ')' db '=' db '?' db '`' db '*' db '' db "'" db ';' db ':' db '_' db '>' align 4 introu15 proc ;--- this is the entry from v86-mode when a key has been pressed ;--- (int 15h, ah=4Fh) @VMMCall Simulate_Iret ;emulate an IRET in v86 mov eax,[ebp].Client_Reg_Struc.Client_EAX cmp al,0E0h jnc done call trans done: ret introu15 endp trans proc near mov dx,word ptr ds:[kbdstat1] CLD mov bl,al mov bh,ds:[kbdflgs] and al,7Fh MOV ECX,ALTGRTABSIZ ;ch=0! test bh,08h ;right alt (altgr) pressed? jz @F test dx,0204h ;any ctrl or alt-left pressed? jnz @F MOV EDI,offset altgrkeytab jmp scantabX @@: test dl,08h ;any alt pressed? jnz exit MOV EDI,offset ctrlkeytab mov CL,CTRLTABSIZ test dl,04h ;any ctrl pressed? jnz scantabX MOV EDI,offset stdkeytab MOV cl,STDTABSIZ cmp al,53h ;numpad ','? jnz scantab test bh,2 ;extended key (E0)? jnz scantab test dl,20h ;num lock active? jz exit mov cl,NUMPADTABSIZ mov edi,offset numpadkeytab scantabX: mov dl,0 ;ignore shift state scantab: push ecx repnz scasb pop ecx jnz exit dec edi add edi,ecx cmp edi,offset L02B3X ;caps lock sensitiv? jnb @F test dl,40h ;shift lock? jz @F test dl,3 ;shift pressed? jnz unorm1 jmp unorm2 @@: test dl,3 ;shift pressed? jz unorm1 unorm2: add edi,ecx unorm1: mov ah,al ;save scan code mov al,[edi] cmp al,0FFh jz exit found: test bl,80h ;is key released? jnz dontsave call savekey dontsave: and byte ptr [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;clear carry ret exit: stc ret savekey: MOVzx EDI,word ptr ds:[bufend] MOV eSI,eDI INC eDI INC eDI CMP DI,ds:[ebufend] JNZ @F MOV DI,ds:[ebufsta] @@: CMP DI,ds:[bufsta] JZ @F ;no more room in buffer MOV [ESI+400h],AX MOV ds:[bufend],DI @@: retn trans endp ;--- install the JLM: ;--- + first try to alloc a v86 callback ;--- + save this callback and the previous v86 int 15h vector ;--- in the 16-bit "real-mode" code. ;--- + set driver attributes in JLoad's header ;--- + copy the "real-mode" code to JLoad's begin in DOS memory ;--- (this is ensured to be safe as long as the code size doesn't ;--- exceed 1 kB). ;--- + set the size of the resident driver part in the driver's ;--- request header. install proc uses esi edi test [ecx].JLCOMM.wFlags, JLF_DRIVER ;loaded as device driver? jz failed push ecx mov esi, offset introu15 mov edx, 0 @VMMCall Allocate_V86_Call_Back ;get a v86 callback pop esi jc failed mov edx, offset rmcode mov [edx+newvec-@], eax ;patch the 16-bit code mov eax, ds:[15h*4] mov [edx+oldvec-@], eax movzx edi,[esi].JLCOMM.wLdrCS ;set driver attributes shl edi, 4 mov word ptr [edi+6],18 ;offset strategy (dummy) mov word ptr [edi+8],18 ;offset interrupt (dummy) mov dword ptr [edi+10],"BYEK" ;driver name mov dword ptr [edi+14],"$$RG" mov byte ptr [edi+18],0CBh ;RETF add edi,20 push esi push edi mov esi, offset rmcode ;copy 16-bit code to DOS memory mov ecx, sizermcode rep movsb pop edi pop esi mov ax,[esi].JLCOMM.wLdrCS ;set the new int 15h vector shl eax, 16 mov ax,20 mov ds:[15h*4],eax add ax,sizermcode mov edx,[esi].JLCOMM.lpRequest mov [edx+14],ax ;set resident size mov eax,1 ret failed: xor eax,eax ret install endp ;--- deinstall. Since the module will only load ;--- as a device driver in CONFIG.SYS, it cannot be unloaded deinstall proc xor eax, eax ;refuse to unload ret deinstall endp DllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword mov ecx, dwRes mov eax,dwReason cmp eax,DLL_PROCESS_ATTACH jnz @F call install jmp exit @@: cmp eax,DLL_PROCESS_DETACH jnz @F call deinstall @@: exit: ret DllMain endp ;--- DOS 16-bit "real-mode" code. ;--- The code will be patched and then copied to DOS memory; ;--- it will be the new v86 int 15h handler. ;--- It's assumed that 32-bit and 16-bin code are mixed in section .text; ;--- then 32-bit label rmcode below can be used to address 16-bit segment _TEXT$16. ;--- This works with jwlink or jwasm v2.19+, option -pe. align dword rmcode label near ;--- for jwasm, option -pe, it's necessary for mixing that the segment name starts with _TEXT$! _TEXT$16 segment use16 dword public 'CODE' @: cmp ah, 4Fh jz @F db 0EAh ; jmp SSSS:OOOO oldvec dd 0 ; previous handler @@: db 0EAh ; jmp SSSS:0000 newvec dd 0 ; v86-breakpoint sizermcode equ $ - @ _TEXT$16 ends end DllMain ================================================ FILE: JLM/KEYBGR/MAKE.BAT ================================================ @echo off rem rem JWasm v2.19: cmdline option -pe will create a JLM without external linker rem jwasm -nologo -pe -DFMTPE -Fl=Release\ -Fo=Release\KEYBGR.DLL -I..\..\Include KeybGr.asm patchPE -x -s:0x1000 Release\KEYBGR.DLL ================================================ FILE: JLM/KEYBGR/MAKEFILE ================================================ # Makefile for NMAKE # tools Alternatives #---------------------------------------------------------- # JWasm Masm v6.x # JWLink MS Link, WLink # # PatchPE: HX tool to patch a PE binary to PX (not required for jwlink) NAME = KEYBGR AOPT=-c -nologo -coff -Fl$*.lst -Fo$*.obj -I..\..\Include OUTDIR=Release $(OUTDIR)\$(NAME).DLL: $(OUTDIR)\$(NAME).obj Makefile # @link /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /Entry:DllMain /OPT:NOWIN98 # @\hx\bin\patchpe $*.DLL @jwlink format win pe hx dll ru native file $*.obj name $*.DLL op q,MAP=$*.MAP $(OUTDIR)\$(NAME).obj: $(NAME).asm # @ml $(AOPT) $(NAME).asm @jwasm $(AOPT) $(NAME).asm ================================================ FILE: JLM/KEYBGR/README.TXT ================================================ Deutscher Tastaturtreiber fuer DOS der mit 48 Bytes DOS Speicher auskommt. Funktioniert nur in Verbindung mit Jemm. Wird installiert durch Eintrag in config.sys: DEVICE=JEMMEX.EXE (oder DEVICE=JEMM386.EXE) ... DEVICE=JLOAD.EXE KEYBGR.DLL KEYBGR ist Public Domain. Japheth ================================================ FILE: JLM/QPIEMU/MAKE.BAT ================================================ @echo off rem create QPIEMU.DLL without linker, requires jwasm v2.19+ rem Jemm's src directory has to be included since qpiemu needs ?PAGEDIR equate! jwasm -c -pe -nologo -D?PE -I..\..\src -FlRelease\QPIEMU.LST -FoRelease\QPIEMU.DLL -I..\..\Include QPIEMU.ASM ================================================ FILE: JLM/QPIEMU/MAKEFILE ================================================ # NMake/WMake Makefile to create QPIEMU.DLL # # tools alternatives #---------------------------------------------------------- # JWasm Masm v6.x # JWLink MS Link, WLink, ALink # (PatchPE) # # PatchPE: HX tool to patch a PE binary to PX ( not required for jwlink ). NAME = QPIEMU OUTDIR=Release AOPT=-c -coff -nologo -Fl$*.lst -Fo$*.obj -I..\..\Include ASM=jwasm.exe #LINK=link.exe /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /EXPORT:ddb /Entry:DllMain /OPT:NOWIN98 LINK=jwlink format win nt hx dll ru native file $*.obj name $*.DLL op q,MAP=$*.MAP export _ddb.1 ALL: $(OUTDIR) $(OUTDIR)\$(NAME).DLL $(OUTDIR): @mkdir $(OUTDIR) $(OUTDIR)\$(NAME).DLL: $(OUTDIR)\$(NAME).obj Makefile @$(LINK) # @..\patchpe $*.DLL $(OUTDIR)\$(NAME).obj: $(NAME).asm @$(ASM) $(AOPT) $(NAME).asm ================================================ FILE: JLM/QPIEMU/QPIEMU.ASM ================================================ ;--- JLM sample QPIEMU ;--- use Makefile to create QPIEMU.DLL ;--- QPIEMU installs a small subset of Qemm's QPI, ;--- just enough to trap ports. .386 .model flat, stdcall .nolist include jlm.inc .list DEVICE_ID equ 4354h cr equ 13 lf equ 10 DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 PGTAB0 equ 1 ;support ax=5000h - "get page table 0 physical address" if PGTAB0 ;--- this hack should be cleaned ASAP... include jemm.inc include jemm32.inc ;needed to get linear address of page directory endif .data ;--- the DDB must be make public. The linker will "export" this ;--- symbol. This is the simplest method to make JLoad know the ;--- device id. ifdef ?PE public export ddb ;syntax accepted since jwasm v2.19 option dotname .drectve segment info db "-subsystem:native -dll -fixed:no" .drectve ends .hdr$2 segment flat db "PX" .hdr$2 ends else public ddb endif ddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,"QPIEMU",0,0, v86_dispatch > callback dd 0 ; current far16 real-mode callback address .code ;--- dispatcher for v86 services v86_dispatch proc @VMMCall Simulate_Far_Ret ;emulate a RETF in v86 and [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;clear Carry flag movzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX cmp ah, 3 jz getversion cmp ah, 1Ah jz isio if PGTAB0 cmp ax, 5000h jz ispt0 endif error: or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag ret if PGTAB0 ispt0: mov eax,ds:[?PAGEDIR] and ax, 0F000h mov [ebp].Client_Reg_Struc.Client_EDX, eax ret endif isio: cmp al, 0 jz simin cmp al, 1 jz simout cmp al, 4 jz simio ; generic untrapped IO cmp al, 6 jz gethandler cmp al, 7 jz sethandler cmp al, 8 ; get port status jz getportstat cmp al, 9 ; trap port jz trapport cmp al, 10 ; untrap port jz untrapport jmp error simin: mov edx, [ebp].Client_Reg_Struc.Client_EDX in al, dx mov byte ptr [ebp].Client_Reg_Struc.Client_EBX, al ret simout: mov edx, [ebp].Client_Reg_Struc.Client_EDX mov al, byte ptr [ebp].Client_Reg_Struc.Client_EBX out dx, al ret simio: movzx edx, word ptr [ebp].Client_Reg_Struc.Client_EDX mov ecx, [ebp].Client_Reg_Struc.Client_ECX mov eax, [ebp].Client_Reg_Struc.Client_EBX @VMMCall Simulate_IO test byte ptr [ebp].Client_Reg_Struc.Client_ECX, 24h ;OUT or STRING_IO? jnz @F mov [ebp].Client_Reg_Struc.Client_EBX, eax @@: ret gethandler: mov eax, [callback] mov word ptr [ebp].Client_Reg_Struc.Client_EDI, ax shr eax, 16 mov word ptr [ebp].Client_Reg_Struc.Client_ES, ax ret sethandler: mov ax, word ptr [ebp].Client_Reg_Struc.Client_ES shl eax, 16 mov ax, word ptr [ebp].Client_Reg_Struc.Client_EDI mov [callback], eax ret getportstat: ;--- todo mov byte ptr [ebp].Client_Reg_Struc.Client_EBX, 0 ;0=port not trapped ret trapport: push esi mov esi, offset iocb movzx edx, word ptr [ebp].Client_Reg_Struc.Client_EDX @VMMCall Install_IO_Handler .if ( CARRY? ) or [ebp].Client_Reg_Struc.Client_EFlags, 1 ;set Carry flag .endif pop esi ret untrapport: movzx edx, word ptr [ebp].Client_Reg_Struc.Client_EDX @VMMCall Remove_IO_Handler .if ( CARRY? ) or [ebp].Client_Reg_Struc.Client_EFlags, 1 ;set Carry flag .endif ret align 4 v86_dispatch endp getversion proc mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0703h mov word ptr [ebp].Client_Reg_Struc.Client_EBX, 0703h ret align 4 getversion endp ;--- io handler proc ;--- ecx=type of io, see jemm32.inc: ;--- bit 2: 0=IN, 1=OUT ;--- bit 3+4: 00=byte, 01=word, 10=dword ;--- bit 5: string IO ;--- bit 6: REP ;--- edx=port ;--- ebp=client struct ;--- eax=client's eax ;--- out: EAX=value read ( will be stored in EAX if IN ) iocb proc push ecx push edx push [ebp].Client_Reg_Struc.Client_EFlags push [ebp].Client_Reg_Struc.Client_ECX push [ebp].Client_Reg_Struc.Client_EDX ;--- setup registers ;--- EAX: current client reg ;--- EBX: current client reg ;--- CL: bit 2: 0=IN, 1=OUT ;--- bit 3+4: 00=BYTE, 01=WORD, 10=DWORD ;--- CH: bit 1: 1=IF (QPI, undocumented) ;--- DX=port mov ch, byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1 and ch, 2 and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, not 1+2 ; reset TF & IF! mov word ptr [ebp].Client_Reg_Struc.Client_EDX, dx mov word ptr [ebp].Client_Reg_Struc.Client_ECX, cx @VMMCall Begin_Nest_Exec ;start nested execution movzx edx, word ptr [callback+0] movzx ecx, word ptr [callback+2] @VMMCall Simulate_Far_Call @VMMCall Resume_Exec ;run the VM @VMMCall End_Nest_Exec ;end nested execution pop [ebp].Client_Reg_Struc.Client_EDX pop [ebp].Client_Reg_Struc.Client_ECX pop [ebp].Client_Reg_Struc.Client_EFlags mov eax, [ebp].Client_Reg_Struc.Client_EAX pop edx pop ecx ret align 4 iocb endp ;--- install the JLM: just set eax=1 ;--- this tells JLOAD that it's ok to add IOTRAP to the list of ;--- loaded modules. install proc uses esi pcomm:ptr JLCOMM mov eax,1 ret align 4 install endp ;--- deinstall the JLM: deinstall proc pcomm:ptr JLCOMM cmp [callback], 0 setz al movzx eax, al ret align 4 deinstall endp DllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword mov eax,dwReason cmp eax,DLL_PROCESS_ATTACH jnz @F invoke install, dwRes jmp exit @@: cmp eax,DLL_PROCESS_DETACH jnz @F invoke deinstall, dwRes @@: exit: ret align 4 DllMain endp end DllMain ================================================ FILE: JLM/QPIEMU/QPIEMU.txt ================================================ 1. About QPIEMU is a JLM that partly emulates the Qemm API (QPI). Its purpose is to provide the IO trapping part of QPI, for better support of sound card emulation in DOS. 2. Install/Uninstall QPIEMU QPIEMU can be installed either as a device driver in CONFIG.SYS: DEVICE=JLOAD.EXE QPIEMU.DLL or as a TSR from the command line: JLOAD QPIEMU.DLL To uninstall, use JLOAD's -u option: JLOAD -u QPIEMU.DLL 3. Using QPIEMU Once QPIEMU is installed, tools that use the QPI ( i.e. SBEMU ) and hence usually require Qemm to be installed, should run with Jemm386/JemmEx. 4. Technical Details QPI is a "real-mode" API, hence if addresses are used, they are in segment:offset format. The part of the supported QPI is: AH=03h, QPI_GetVersion AX=1A00h, QPI_UntrappedIORead AX=1A01h, QPI_UntrappedIOWrite AX=1A04h, QPI_UntrappedIO AX=1A06h, QPI_GetIOCallback AX=1A07h, QPI_SetIOCallback AX=1A08h, QPI_GetPortTrap AX=1A09h, QPI_SetPortTrap AX=1A0Ah, QPI_ClearPortTrap Since v1.2, there's also AX=5000h, which isn't part of the original QPI. It returns physical address of Jemm's page table 0 in register EDX. The way to get the address of QPI itself differs for QPIEMU: an INT 2Fh, with AX=1684h, BX=4354h has to be called. If successful, It will return with AL==0 and the QPI entry point in ES:DI. History - v1.0: 03/2023: initial - v1.1: 07/2025: bit 1 of CH holds IF - v1.2: 09/2025: added function ax=5000h 5. License QPIEMU is Public Domain. Japheth ================================================ FILE: JLM/QPIEMU/Test/MAKEFILE ================================================ # NMake/WMake Makefile to create TESTQPI.EXE & TESTDMA.EXE NAME1 = TESTQPI NAME2 = TESTDMA OUTDIR= . ASM=jwasm.exe ALL: $(OUTDIR)\$(NAME1).EXE $(OUTDIR)\$(NAME2).EXE $(OUTDIR)\$(NAME1).EXE: $(NAME1).asm @$(ASM) -nologo -mz -Fl$* -Sg -Fo=$*.EXE $(NAME1).asm $(OUTDIR)\$(NAME2).EXE: $(NAME2).asm @$(ASM) -nologo -mz -Fl$* -Sg -Fo=$*.EXE $(NAME2).asm ================================================ FILE: JLM/QPIEMU/Test/PRINTF16.INC ================================================ ;--- simple printf() implementation handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ;--- outb is expected to be onto stack ;--- hiwords or eax, edx, eax are used, but restored. ltob PROC stdcall uses edi edx number:dword, outb:word, base:word mov ch, 0 movzx edi, base push eax mov eax, number cmp di, -10 jne @F mov di, 10 and eax, eax jns @F neg eax mov ch, '-' @@: mov bx, outb add bx, 10 mov BYTE PTR ss:[bx], 0 dec bx @@nextdigit: xor edx, edx div edi add dl, '0' cmp dl, '9' jbe @F add dl, 7+20h @@: mov ss:[bx], dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx], ch dec bx @@: inc bx pop eax mov ax, bx ret ltob ENDP ;--- ds=dgroup, ss don't need to be dgroup ;--- preserve all registers printf PROC c fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte pusha lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: popa ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx .while byte ptr [si] >= '0' && byte ptr [si] <= '9' lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax .endw mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u cmp al,0 jnz @@L359 pop ax jmp done handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn printf ENDP ================================================ FILE: JLM/QPIEMU/Test/SETARGV.INC ================================================ ;--- read the commandline at PSP:81h ;--- and create an argc/argv structure on the stack. ;--- in: ES=PSP, DS=DGROUP, SS=DGROUP ;--- out: _argc (=[bp-2]) ;--- _argv (=[bp-4]) ;--- all std registers modified (including SP) ?DUMMYFN equ 1 ?QUOTES equ 1 _setargv proc mov bp, sp sub sp, 256 ; just make enough room for argc/argv xor di, di ; init argc xor dx, dx ; init size of mem block mov si, 81H push es pop ds ; assume ds:nothing ; no need for assumes, since no global vars are accessed jmp scanarg ;--- DI = argc ;--- DX = block size (not including null terminators) nextarg: push bx ; save argument size scanarg: @@: lodsb cmp al, ' ' je @B cmp al, 9 je @B cmp al, 13 jz doneargs ; exit if eol inc di ; another argument xor bx, bx ; init argument size if ?QUOTES cmp al, '"' jz handle_quote endif dec si ; back up to reload character push si ; save argument ofs @@: lodsb cmp al, ' ' ; end argument? je nextarg cmp al, 9 je nextarg ; white space terminates argument cmp al, 13 jz doneargs2 ; exit if eol inc bx inc dx jmp @B if ?QUOTES handle_quote: push si @@: lodsb cmp al, 13 jz quoteerr cmp al, '"' jz @F inc dx inc bx jmp @B quoteerr: dec si ; "unread" the CR @@: jmp nextarg endif doneargs2: push bx ; last argument's size doneargs: ;--- address & size of arguments are pushed mov cx, di add dx, di ; DX=size arguments + terminator bytes inc di ; add one for NULL pointer if ?DUMMYFN inc di ; add one for filename endif shl di, 1 ; each ofs needs 2 bytes add dx, di ; DX=size args + size argv and dx, -2 ; ensure stack remains word aligned mov ax, [bp] sub bp, dx ; alloc the really needed space for argc/argv mov [bp-6], ax ; store return address _argc equ _argv equ mov [_argv], bp mov [_argc], cx add di, bp ; di -> behind vector table (strings) xor ax, ax lea bx, [di-2] mov ss:[bx], ax ; terminating 0000 _argv[x] sub bx, 2 jcxz noargs push ss pop es ;--- copy the arguments from PSP onto the stack mov dx, cx @@: pop cx ; size pop si ; address mov ss:[bx], di ; store _argv[x] sub bx, 2 rep movsb stosb ; AL still 0 dec dx jnz @B noargs: push ss pop ds ; assume ds:DGROUP if ?DUMMYFN mov [bx], ax ; store 0 as dummy filename inc word ptr [_argc] endif lea sp, [bp-6] ret _setargv endp ================================================ FILE: JLM/QPIEMU/Test/TESTDMA.ASM ================================================ ;--- test Qemm/QPIEMU ( IO trap part ) ;--- this time test ISA DMA ports .286 .model small .dosseg .stack 1024 PORT equ 0C4h lf equ 10 CStr macro text:vararg local sym .const sym db text, 0 .code exitm endm .data dwQemm dd 0 dwOldIO dd 0 wVersion dw 0 .code dataseg dw 0 .386 include printf16.inc ; this printf() preserves all registers! PUSHAS struct wDI dw ? wSI dw ? wBP dw ? dw ? wBX dw ? wDX dw ? wCX dw ? wAX dw ? PUSHAS ends myio proc far pusha mov bp, sp push ds mov ds, cs:[dataseg] pushf invoke printf, CStr("in myio, eax=%lX, bx=%X, cx=%X, dx=%X, ds=%X, cs=%X, ss=%X, fl=%X",10), eax, bx, cx, dx, [bp-2], cs, ss, [bp-4] add sp, 2 if 0 cmp byte ptr [bp].PUSHAS.wCX, 0 jnz @F mov ax, 1A00h ; untrapped read (byte only) mov dx, [bp].PUSHAS.wDX call [dwQemm] mov byte ptr [bp].PUSHAS.wAX, bl invoke printf, CStr("in myio, untrapped read=%X",10), bl @@: else mov cx, [bp].PUSHAS.wCX ; bits set to allow byte/word/dword read/write mov dx, [bp].PUSHAS.wDX push ebx mov ebx, eax ; for OUT, ebx is to be loaded with value of eax mov bx, [bp].PUSHAS.wAX mov ax, 1A04h ; untrapped IO (generic) call [dwQemm] test [bp].PUSHAS.wCX, 4 ; IN? jnz @F mov eax, ebx ; then load EAX with read value mov [bp].PUSHAS.wAX, ax invoke printf, CStr("in myio, untrapped read=%lX",10), eax @@: pop ebx endif pop ds popa clc ; NC=access handled retf myio endp main proc c args:word, argv:word ;--- 1. test if Qemm is installed push 0 pop es mov eax, es:[67h*4] and eax, eax jz noemm mov cx, "QE" mov dx, "MM" mov ah, 3Fh int 67h cmp ah,0 jz qpi_ok ;--- 2. check for QPIEMU mov ax, 1684h mov bx, 4354h int 2Fh cmp al, 0 jnz noqpi qpi_ok: mov word ptr [dwQemm+0], di mov word ptr [dwQemm+2], es invoke printf, CStr("QEMM/QPI found, entry=%X:%X",10), es, di mov ah, 3 call [dwQemm] mov [wVersion], bx invoke printf, CStr("QPI call AH=3, Qemm version: %X.%02X",10), bh, bl cmp [wVersion], 0703h jb exit mov ax, 1A06h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A06h failed",10) jmp exit .endif mov word ptr [dwOldIO+0], di mov word ptr [dwOldIO+2], es invoke printf, CStr("QPI call AX=1A06h ok, current IO trap callback: %X:%X",10), es, di mov di, offset myio mov ax, seg myio mov es, ax mov ax, 1A07h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A07h failed",10) jmp exit .endif invoke printf, CStr("QPI call AX=1A07h ok, new IO trap callback: %X:%X",10), es, di ;-- ax=1A08h is GetPortTrap() mov dx, PORT mov ax, 1A09h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A09h, port %X failed",10), PORT jmp exit2 .endif invoke printf, CStr("QPI call AX=1A09h ok, port %X now trapped",10), PORT mov dx, PORT+2 mov ax, 1A09h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A09h, port %X failed",10), dx jmp exit3 .endif invoke printf, CStr("QPI call AX=1A09h ok, port %X now trapped",10), dx ;--- access port DX invoke printf, CStr("accessing port 220h",10) mov bx, -1 mov cx, -1 mov dx, PORT mov eax, 55aa55AAh in al, dx invoke printf, CStr("IN al, dx: EAX=%lX",10), eax mov eax, 55aa55AAh in ax, dx invoke printf, CStr("IN ax, dx: EAX=%lX",10), eax mov eax, 12345678h out dx, al out dx, ax invoke printf, CStr("behind IO instructions",10) mov dx, PORT+2 mov ax, 1A0Ah call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A0Ah, port %X failed",10), dx .else invoke printf, CStr("QPI call AX=1A0Ah ok, port %X now untrapped",10), dx .endif exit3: mov dx, PORT mov ax, 1A0Ah call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A0Ah, port %X failed",10), dx .else invoke printf, CStr("QPI call AX=1A0Ah ok, port %X now untrapped",10), dx .endif exit2: les di, [dwOldIO] mov ax, 1A07h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A07h failed, IO callback not restored",10) .else invoke printf, CStr("QPI call AX=1A07h ok, IO callback restored to %X:%X",10), es, di .endif exit: ret noemm: invoke printf, CStr("no EMM found",10) ret noqpi: invoke printf, CStr("QPI not available",10) ret main endp include setargv.inc start: mov ax, @data mov ds, ax mov cs:[dataseg], ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call _setargv invoke main, [_argc], [_argv] mov ah, 4Ch int 21h END start ================================================ FILE: JLM/QPIEMU/Test/TESTQPI.ASM ================================================ ;--- test Qemm/QPIEMU ( IO trap part ) .286 .model small .dosseg .stack 1024 lf equ 10 CStr macro text:vararg local sym .const sym db text, 0 .code exitm endm .data dwQemm dd 0 dwOldIO dd 0 wVersion dw 0 .code dataseg dw 0 .386 include printf16.inc ; this printf() preserves all registers! PUSHAS struct wDI dw ? wSI dw ? wBP dw ? dw ? wBX dw ? wDX dw ? wCX dw ? wAX dw ? PUSHAS ends myio proc far pusha mov bp, sp push ds mov ds, cs:[dataseg] pushf invoke printf, CStr("in myio, eax=%lX, bx=%X, cx=%X, dx=%X, ds=%X, cs=%X, ss=%X, fl=%X",10), eax, bx, cx, dx, [bp-2], cs, ss, [bp-4] add sp, 2 if 0 cmp byte ptr [bp].PUSHAS.wCX, 0 jnz @F mov ax, 1A00h ; untrapped read (byte only) mov dx, [bp].PUSHAS.wDX call [dwQemm] mov byte ptr [bp].PUSHAS.wAX, bl invoke printf, CStr("in myio, untrapped read=%X",10), bl @@: else mov cx, [bp].PUSHAS.wCX ; bits set to allow byte/word/dword read/write mov dx, [bp].PUSHAS.wDX push ebx mov ebx, eax ; for OUT, ebx is to be loaded with value of eax mov bx, [bp].PUSHAS.wAX mov ax, 1A04h ; untrapped IO (generic) call [dwQemm] test [bp].PUSHAS.wCX, 4 ; IN? jnz @F mov eax, ebx ; then load EAX with read value mov [bp].PUSHAS.wAX, ax invoke printf, CStr("in myio, untrapped read=%lX",10), eax @@: pop ebx endif pop ds popa clc ; NC=access handled retf myio endp main proc c args:word, argv:word ;--- 1. test if Qemm is installed push 0 pop es mov eax, es:[67h*4] and eax, eax jz noemm mov cx, "QE" mov dx, "MM" mov ah, 3Fh int 67h cmp ah,0 jz qpi_ok ;--- 2. check for QPIEMU mov ax, 1684h mov bx, 4354h int 2Fh cmp al, 0 jnz noqpi qpi_ok: mov word ptr [dwQemm+0], di mov word ptr [dwQemm+2], es invoke printf, CStr("QEMM/QPI found, entry=%X:%X",10), es, di mov ah, 3 call [dwQemm] mov [wVersion], bx invoke printf, CStr("QPI call AH=3, Qemm version: %X.%02X",10), bh, bl cmp [wVersion], 0703h jb exit mov ax, 1A06h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A06h failed",10) jmp exit .endif mov word ptr [dwOldIO+0], di mov word ptr [dwOldIO+2], es invoke printf, CStr("QPI call AX=1A06h ok, current IO trap callback: %X:%X",10), es, di mov di, offset myio mov ax, seg myio mov es, ax mov ax, 1A07h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A07h failed",10) jmp exit .endif invoke printf, CStr("QPI call AX=1A07h ok, new IO trap callback: %X:%X",10), es, di ;-- ax=1A08h is GetPortTrap() mov dx, 220h mov ax, 1A09h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A09h, port 220h failed",10) jmp exit2 .endif invoke printf, CStr("QPI call AX=1A09h ok, port 220h now trapped",10) mov dx, 007Ch mov ax, 1A09h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A09h, port 7Ch failed",10) jmp exit3 .endif invoke printf, CStr("QPI call AX=1A09h ok, port 7Ch now trapped",10) ;--- access port DX invoke printf, CStr("accessing port 220h",10) mov bx, -1 mov cx, -1 mov dx, 220h mov eax, 55aa55AAh in al, dx invoke printf, CStr("IN al, dx: EAX=%lX",10), eax mov eax, 55aa55AAh in ax, dx invoke printf, CStr("IN ax, dx: EAX=%lX",10), eax mov eax, 55aa55AAh in eax, dx invoke printf, CStr("IN eax, dx: EAX=%lX",10), eax cli mov eax, 55aa55AAh in al, dx invoke printf, CStr("CLI, IN al, dx: EAX=%lX",10), eax cli mov eax, 55aa55AAh in ax, dx invoke printf, CStr("CLI, IN ax, dx: EAX=%lX",10), eax sti mov eax, 12345678h out dx, al out dx, ax out dx, eax ;--- access port 7Ch invoke printf, CStr("accessing port 7C",10) mov bx, -1 mov cx, -1 mov dx, -1 mov eax, 55aa55AAh in al, 7Ch invoke printf, CStr("IN al, 7C: EAX=%lX",10), eax mov eax, 55aa55AAh in ax, 7Ch invoke printf, CStr("IN ax, 7C: EAX=%lX",10), eax mov eax, 55aa55AAh in eax, 7Ch invoke printf, CStr("IN eax, 7C: EAX=%lX",10), eax mov eax, 12345678h out 7Ch, al out 7Ch, ax out 7Ch, eax invoke printf, CStr("behind IO instructions",10) mov dx, 007Ch mov ax, 1A0Ah call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A0Ah, port 7Ch failed",10) .else invoke printf, CStr("QPI call AX=1A0Ah ok, port 7Ch now untrapped",10) .endif exit3: mov dx, 220h mov ax, 1A0Ah call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A0Ah, dx=220h failed",10) .else invoke printf, CStr("QPI call AX=1A0Ah ok, port 220h now untrapped",10) .endif exit2: les di, [dwOldIO] mov ax, 1A07h call [dwQemm] .if CARRY? invoke printf, CStr("QPI call AX=1A07h failed, IO callback not restored",10) .else invoke printf, CStr("QPI call AX=1A07h ok, IO callback restored to %X:%X",10), es, di .endif exit: ret noemm: invoke printf, CStr("no EMM found",10) ret noqpi: invoke printf, CStr("QPI not available",10) ret main endp include setargv.inc start: mov ax, @data mov ds, ax mov cs:[dataseg], ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call _setargv invoke main, [_argc], [_argv] mov ah, 4Ch int 21h END start ================================================ FILE: JLM/README.TXT ================================================ 1. About This directory contains some JLMs: AHCICD: AHCI Optical disk driver. GENERIC: protected-mode TSR sample in ASM (Masm/JWasm). HELLO: "hello world" JLM in ASM (Masm/JWasm) HELLO2: "hello world" JLM in C (MS VC, Open Watcom) IOTRAP: sample how to trap IO port access. JCLOCK: shows clock in text mode. KEYBGR: German keyboard driver. QPIEMU: emulates port trapping part of QPI. REBOOT: shows how control message DEVICE_REBOOT_NOTIFY may be handled. XCDROM32: UltraDMA DVD/CD-ROM driver (GNU GPL 2). XDMA32: UltraDMA HD driver (GNU GPL 2). GENERIC, HELLO, HELLO2, IOTRAP, JCLOCK, QPIEMU are Public Domain. 2. Tools that may be used tools version result ----------------------------------------- assembler: Masm 6.15 ok JWasm 2.15 ok Fasm 1.67 ok C compiler: MS VC 2/5/6 ok Borland C++ 5.5 ok Ladsoft CC386 3.8.1.18 ok Digital Mars C++ 8.49 ok Open Watcom WCC386 1.9 ok COFF linker: JWlink 1.9beta17 ok Open Watcom WLink 1.9 ok MS Link 6.00 ok ================================================ FILE: JLM/REBOOT/FASTBOOT.ASM ================================================ ;--- Sample how to handle DEVICE_REBOOT_NOTIFY control message; ;--- handles fastboot only. ;--- Requires Jemm v5.86+. ;--- To be assembled with JWasm v2.19+; if Masm is to be used, ;--- FASTBRM.BIN must be converted to FASTBRM.INC. ;--- If a link step is added, the linker is supposed to export ddb. .386p .MODEL FLAT, stdcall option casemap:none include jlm.inc ifndef BOOTSECT BOOTSECT equ 1 endif ifndef LDDBG ;support API "load DebugB"? LDDBG equ 1 endif ifndef LDI13EXT ;support API "load Int 13h extension"? LDI13EXT equ 1 endif DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 DEVICE_ID equ 4435h ;--- equates from jemm32.inc REAL_CODE_SEL equ 4 * 8 REAL_DATA_SEL equ 5 * 8 RMCOMM struct db ?,?,? ;mov cr0,ecx db ? ;jmp far16 opcode (0EAh) dw ?,? ;offset, segment of jmp far16 bPartition db ? bDisk db ? if LDDBG or LDI13EXT bFlags db ? ;1:call DebugB init, 1: stop in DebugB; 2: 1=call int13 extension init. endif RMCOMM ends ifdef @pe_file_flags ;defined by JWasm if option -pe is set option dotname .drectve segment info db "-dll " db "-subsystem:native " db "-fixed:no " .drectve ends .hdr$2 segment flat db "PX" .hdr$2 ends EXPORT equ else EXPORT equ <> endif .const public EXPORT ddb ddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,"REBOOT",0, ctrlproc, v86_dispatch > helptxt label byte db "Usage:",13,10 db "jload fastboot.dll [options]",13,10 db "options:",13,10 db " /Dn",9,"set Hard Disk to boot from; n=0..7; default 0",13,10 db " /Pn",9,"set Partition to boot from; n=1..8; no default",13,10 if BOOTSECT db ' /B:file set File to boot from; file=name of file to be used as boot sector',13,10 endif db 0 fileerror label byte db "file access error or invalid format",13,10 db 0 .data if LDDBG dwStartDbg dd 0 dwSizeDbg dd 0 endif if LDI13EXT dwStartI13 dd 0 dwSizeI13 dd 0 endif rmcode label byte ifdef __JWASM__ incbin else include ;translated by bin2inc endif sizermcode equ $ - rmcode if BOOTSECT .data? bsbuffer db 512 dup (?) endif .CODE ;--- V86 API ;--- AH=0: get version ;--- AH=1: set disk (AL=disk, HDs only) ;--- AH=2: set partition (AL=partition, one-based) ;--- AH=3: set boot debugger (DS:SI=address, ECX=size) ;--- AH=4: set int 13h extension (DS:SI=address, ECX=size) v86_dispatch proc @VMMCall Simulate_Far_Ret ;emulate a RETF in v86 and [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;clear Carry flag movzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX cmp ah,0 jz getversion cmp ah,1 jz setdisk cmp ah,2 jz setpart if LDDBG cmp ah,3 jz setdbg endif if LDI13EXT cmp ah,4 jz seti13ext endif error: or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag ret getversion: mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0101h ;bit 0 of AH signals FASTBOOT ret setdisk: mov rmcode.RMCOMM.bDisk,al ret setpart: mov rmcode.RMCOMM.bPartition,al ret if LDDBG setdbg: call ReleaseDbg call StoreFile jc @F mov dwStartDbg, eax mov dwSizeDbg, ecx @@: ret endif if LDI13EXT seti13ext: call ReleaseI13 call StoreFile jc @F mov dwStartI13, eax mov dwSizeI13, ecx @@: ret endif v86_dispatch endp ;--- control proc: handle messages from Jemm ctrlproc proc cmp eax, DEVICE_REBOOT_NOTIFY jz reboot_notify @@: clc ret reboot_notify: test bl,1 ;fastboot flag? jz @B if LDDBG or LDI13EXT ; mov rmcode.RMCOMM.bFlags,0 endif if LDI13EXT mov ecx,dwSizeI13 jecxz no_i13ext mov edi,8000h mov esi,dwStartI13 cld rep movsb or rmcode.RMCOMM.bFlags,4 no_i13ext: endif if LDDBG mov ecx,dwSizeDbg jecxz no_dbginit add ecx,1024-1 movzx edi,word ptr ds:[413h] shl edi,10 ;convert kB to bytes ;--- if already loaded, don't change ds:[413h] cmp byte ptr [edi],0E9h ;jmp near16 opcode? jnz adjmem movzx eax,word ptr [edi+1] cmp dword ptr [edi+eax-1],0DEADBEEFh ;DebugB signature? jz @F adjmem: shr ecx,10 ;convert bytes to kB sub ds:[413h],cx shl ecx,10 sub edi,ecx @@: mov esi,dwStartDbg cld rep movsb or rmcode.RMCOMM.bFlags,1+2 no_dbginit: mov ax,word ptr ds:[4+2] shr ax,6 ;para -> kB cmp ax,ds:[413h] jb @F or rmcode.RMCOMM.bFlags,2 @@: endif ;--- If fastboot is on, one cannot call v86-mode. ;--- IVT vectors have been restored already, VDS bit has been cleared. ;--- So copy 16-bit code to address 07e00h, setup stack and registers ;--- EAX & ECX, finally jump to 7E00h ( still in protected-mode ). mov edi, 7E00h mov esi, offset rmcode mov ecx, sizermcode cld rep movsb if BOOTSECT cmp rmcode.RMCOMM.bPartition,-1 jnz @F mov edi, 7C00h mov esi, offset bsbuffer mov ecx, 512/4 rep movsd @@: endif xor edx, edx push edx pushw -1 LIDT FWORD ptr [esp] ; reset the IDT to 0:ffffh MOV AX,REAL_DATA_SEL ; before returning to real-mode set the MOV DS,EAX ; segment register caches MOV ES,EAX MOV FS,EAX MOV GS,EAX MOV SS,EAX ; set SS:ESP MOV ESP,7C00h MOV ECX,CR0 ; prepare to reset CR0 PE and PG bits AND ECX,7FFFFFFEH XOR EAX,EAX db 66h, 0eah ; jmp far16 20h:7E00h dw 7E00h, REAL_CODE_SEL ctrlproc endp cputs proc uses esi mov esi, eax cld nextitem: lodsb and al,al jz done mov byte ptr [ebp].Client_Reg_Struc.Client_EDX,al mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,2 mov eax,21h @VMMCall Exec_Int jmp nextitem done: ret cputs endp ;--- display usage helpout proc uses ebp @VMMCall Get_Cur_VM_Handle mov ebp,[ebx].cb_s.CB_Client_Pointer @VMMCall Begin_Nest_Exec ;start nested execution mov eax,offset helptxt call cputs @VMMCall End_Nest_Exec ;end nested execution ret helpout endp if LDDBG ReleaseDbg proc uses esi mov ecx,dwSizeDbg jecxz exit add ecx,4096-1 shr ecx,12 mov esi,dwStartDbg @@: push ecx push 0 push esi @VMMCall _PageFree add esp,2*4 pop ecx add esi,1000h loop @B exit: mov dwSizeDbg,0 mov dwStartDbg,0 ret ReleaseDbg endp endif if LDI13EXT ReleaseI13 proc uses esi mov ecx,dwSizeI13 jecxz exit add ecx,4096-1 shr ecx,12 mov esi,dwStartI13 @@: push ecx push 0 push esi @VMMCall _PageFree add esp,2*4 pop ecx add esi,1000h loop @B exit: mov dwSizeI13,0 mov dwStartI13,0 ret ReleaseI13 endp endif if LDDBG or LDI13EXT ;--- v86 API ah=3/4: copy conv. memory block to extended memory ;--- in: v86-ds:si=address ;--- v86-ecx=size of region ;--- out: NC if ok ;--- eax=ext. memory address ;--- ecx=size of region StoreFile proc uses esi edi mov eax, [ebp].Client_Reg_Struc.Client_ECX and eax, eax jz exit add eax,4096-1 shr eax,12 mov esi,eax push 0 push eax push PR_SYSTEM @VMMCall _PageReserve ;allocate address space for debugger add esp,3*4 cmp eax,-1 jz error mov edi, eax shr eax, 12 ;convert linear address to page number push PC_INCR or PC_WRITEABLE push 0 push PD_FIXED ;??? push esi push eax @VMMCall _PageCommit add esp,5*4 and eax,eax jz error movzx esi, word ptr [ebp].Client_Reg_Struc.Client_DS shl esi, 4 movzx eax, word ptr [ebp].Client_Reg_Struc.Client_ESI add esi, eax mov ecx,[ebp].Client_Reg_Struc.Client_ECX mov eax, edi push ecx rep movsb pop ecx exit: clc ret error: or [ebp].Client_Reg_Struc.Client_EFlags,1 ;set Carry flag stc ret StoreFile endp endif if BOOTSECT include fileacc.inc ;--- read "boot sector" file ;--- in: esi = start of name in cmdline ;--- out: esi = behind name ;--- C if error occured ReadBSFile proc uses ebx edi ebp push ecx @VMMCall Get_Cur_VM_Handle mov ebp,[ebx].cb_s.CB_Client_Pointer pop ecx @VMMCall GetDOSBuffer mov edi,eax @VMMCall Begin_Nest_Exec mov edx,edi mov ecx,128 @@: lodsb stosb cmp al,' ' jbe @F loop @B inc esi @@: dec esi mov byte ptr [edi-1],0 mov edi,edx or ebx,-1 call OpenFile jc exit mov ebx,eax call GetFileSize jc exit cmp eax,512 stc jnz exit mov edx,edi mov ecx,eax call ReadFile jc exit cmp eax,512 stc jnz exit push esi mov esi, edi mov edi, offset bsbuffer mov ecx, 512/4 rep movsd pop esi clc exit: pushfd jnc @F mov eax,offset fileerror call cputs @@: cmp ebx,-1 jz @F call CloseFile @@: @VMMCall End_Nest_Exec popfd ret ReadBSFile endp endif ;--- scan cmdline ;--- options /P1 .. /P8 ;--- options /D0 .. /D7 ;--- option /B:filename Init proc uses esi pComm:ptr mov esi,pComm mov esi,[esi].JLCOMM.lpCmdLine cld .while byte ptr [esi] lodsb .if al == '/' || al == '-' call getoption jc error .elseif al == ' ' || al == 9 .continue .else .break .if al == 13 ;if loaded as device driver call helpout jmp error .endif .endw mov eax,1 ret error: xor eax,eax ret getoption: mov ax,[esi] or al,20h .if al == 'p' && ah >= '1' && ah <= '8' mov al,ah sub al,'0' mov rmcode.RMCOMM.bPartition,al add esi,2 .elseif al == 'd' && ah >= '0' && ah <= '7' mov al,ah sub al,'0' or al,80h mov rmcode.RMCOMM.bDisk,al add esi,2 if BOOTSECT .elseif al == 'b' && ah == ':' mov ecx, pComm add esi,2 invoke ReadBSFile;may return with C=error mov rmcode.RMCOMM.bPartition,-1 ;disables boot sector load endif .else call helpout stc .endif retn Init endp Deinit proc if LDDBG call ReleaseDbg endif if LDI13EXT call ReleaseI13 endif mov eax,1 ret Deinit endp DllMain PROC stdcall hModule:dword, dwReason:dword, dwRes:dword mov eax,dwReason cmp eax, DLL_PROCESS_ATTACH jnz @F invoke Init, dwRes jmp exit @@: cmp eax,DLL_PROCESS_DETACH jnz @F call Deinit @@: exit: ret DllMain endp END DllMain ================================================ FILE: JLM/REBOOT/FASTBOOT.txt ================================================ About FastBoot JLM The FastBoot JLM allows some fine-tuning of Jemm's fast reboot behavior (which is activated by Jemm's FASTBOOT option). DEVICE=JLOAD.EXE FASTBOOT.DLL [options] Valid options are: /Dn : select a HD to boot from (n=0-7; 0 is first HD and default). This will work for the DOS variants that are able to boot from any HD, without "remapping" them in the BIOS. /Pn : select a partition to boot from (n=1-8; if n > 4, the partition will be searched in the (first) extended partition. If this option is omitted, the FastBoot JLM will load & run the MBR's boot code. /B:file: select a "boot sector" file to be used for booting. This file is expected to have a size of 512 bytes and contain executable boot code. Additionally, the FastBoot JLM implements an API: 1. to load and activate the "boot variant" of Debug/X - DebugB. This allows to debug the DOS kernel during initialization without modifying the boot sector of the partition. 2. to load an "INT 13h BIOS extension". Sample LDI13Ext.asm is supplied, showing how this is supposed to be done - it just switches HDs 0 and 1. Jemm's FASTBOOT option is not guaranteed to work, please read the chapter about the requirements in Jemm's documentation. ================================================ FILE: JLM/REBOOT/FASTBRM.ASM ================================================ ;--- real-mode part of the fastboot JLM; ;--- this code is moved to address 07E00h by the fastboot JLM; ;--- on entry: ;--- + cpu is still in protected-mode; ;--- + CS, SS, DS, ES, FS, GS: 16-bit selector, limit 0ffffh, base 0 ;--- + ESP: 7C00h ;--- + ECX: value for CR0 ;--- + EAX: 0 ; ;--- To assemble use Masm v6+ or JWasm. ; ;--- To load a partition boot sector will work only if the ;--- BIOS supports LBA access for the disk. .model tiny RESETDSK equ 0 ;1=reset disk via int 13h, ah=0 BOOTDISK equ 80h;default boot HD BOOTPART equ 0 ;may be 1-8 to boot a partition or 0 to boot MBR BOOTADDR equ 7C00h ;address where mbr/boot sector will be loaded CHKJMPS equ 0 ;1=check for "jmp short" at boot sector start ifndef BOOTSECT BOOTSECT equ 1 ;1=support option /B:boot_sector_file endif ifndef LDDBG ;must match setting in fastboot.asm LDDBG equ 1 ;1=check for presence of DebugB and initialize it endif ifndef LDI13EXT ;must match setting in fastboot.asm LDI13EXT equ 1 ;1=initialize int13 extension endif @dbgout macro chr ifdef _DEBUG mov al,chr call printchr endif endm ;--- needed if an extended partition has to be scanned. BDESC struct bBoot db ? chsStart db ?,?,? ; bType db ? chsEnd db ?,?,? lbaStart dd ? lbaSize dd ? BDESC ends .code org 7E00h .386p mov cr0,ecx ;enter real-mode db 0EAh ;jmp far16 0000:7Exx (set CS=0000) dw @F, 0 bPartition db BOOTPART ;offset 8 (contents may be changed by FASTBOOT.ASM!) bDisk db BOOTDISK ;offset 9 (contents may be changed by FASTBOOT.ASM!) if LDDBG or LDI13EXT bDbgFlags db 0 ;bit 0:1=initialize DebugB; bit 1:1=stop in debugger endif @@: mov cr3,eax .386 mov ss,ax mov ds,ax mov es,ax sti if LDI13EXT test cs:[bDbgFlags],4 jz @F db 9ah ;call 0800h:0000 to init int13 extension dw 0,800h @@: endif if LDDBG test cs:[bDbgFlags],1 jz @F @dbgout 'D' mov ax, ds:[413h] shl ax, 6 ;kB -> para push cs push offset @F push ax ;call DebugB init proc push 0 retf @@: endif @dbgout 'R' if RESETDSK mov dl,cs:[bDisk] mov ah,00 ;disk reset int 13h endif @dbgout 'B' mov cx,0001h ;CX+DH=cyl/head/sector movzx dx,cs:[bDisk] mov bx,BOOTADDR ;ES:BX=transfer address push es push bx ;push address for RETF below if BOOTSECT cmp cs:[bPartition],-1 ;boot sector loaded as file? jz bsect_read ;then it has been moved to 7C00h already endif mov ax,201h ;read first sector of HD int 13h jc err @dbgout 'T' mov al,cs:[bPartition] and al,al jz bsect_read dec al ;1..8 -> 0..7 mov ah,0 cmp al,4 ;0..3? jb stdpart ;--- partitions 5-8 are searched in (the first and only) extended partition xor edi,edi sub al,4-1 ;4-7 -> 1-4 nextext: push ax mov si,0 @@: cmp byte ptr [si+BOOTADDR+1BEh].BDESC.bType, 5 ;extended partition? jz @F cmp byte ptr [si+BOOTADDR+1BEh].BDESC.bType, 15 ;extended partition, LBA? jz @F add si,sizeof BDESC cmp si,4*sizeof BDESC jb @B jmp err ;no extended partition found @@: and edi,edi jnz @F mov edi,[si+BOOTADDR+1BEh].BDESC.lbaStart jmp isfirst @@: add [si+BOOTADDR+1BEh].BDESC.lbaStart,edi isfirst: push [si+BOOTADDR+1BEh].BDESC.lbaStart push si call readsect pop si pop ebx jc err cmp word ptr ds:[BOOTADDR+1FEh],0AA55h jnz err pop ax dec al jnz nextext ;--- bootable partitions in extended partitions are supposed to be the first entry! add ds:[BOOTADDR+1BEh+BDESC.lbaStart],ebx stdpart: shl ax,4 mov si,ax call readsect jc err cmp word ptr ds:[BOOTADDR+1FEh],0AA55h jnz err if CHKJMPS cmp byte ptr ds:[BOOTADDR],0EBh ;sector starts with a short jmp? jnz err endif @dbgout 'p' bsect_read: if LDDBG test cs:[bDbgFlags],2 jz @F int 3 @@: endif retf ;jump to boot code err: call errout db 7,"err",13,10 jmp $ ;stop readsect: ;--- si = 0,10h,20h,30h mov bx,055AAh mov ah,41h ;check for int 13h extensions int 13h jc readsectchs1 cmp bx,0AA55h jnz readsectchs1 cmp [si+BOOTADDR+1BEh].BDESC.bType,7 jb readsectchs cmp [si+BOOTADDR+1BEh].BDESC.bType,0Bh ;FAT32 CHS? jz readsectchs readsectlba: ;--- create a "disk address packet" onto stack pushd 0 push [si+BOOTADDR+1BEh].BDESC.lbaStart ;LBA sector# (start of partition) push 0 push BOOTADDR;transfer buffer (0000:7C00) push 1 ;sectors push 10h ;size of packet mov si,sp mov dl,cs:[bDisk] mov ah,42h int 13h lea sp,[si+10h] ret readsectchs: ;--- logical partitions inside extended partitions usually have type 5, although LBA must be used! cmp byte ptr [si+BOOTADDR+1BEh].BDESC.chsStart+2,0ffh jnz @F cmp word ptr [si+BOOTADDR+1BEh].BDESC.chsStart+0,0fffeh jnc readsectlba @@: readsectchs1: mov bx,BOOTADDR mov dh,[si+BOOTADDR+1BEh].BDESC.chsStart+0 mov cx,word ptr [si+BOOTADDR+1BEh].BDESC.chsStart+1 mov ax,201h int 13h ret errout: pop si cld @@: lodsb push ax call printchr pop ax cmp al,10 jnz @B jmp si printchr: push bx mov bh,0 mov ah,0Eh int 10h pop bx ret end ================================================ FILE: JLM/REBOOT/FBOOT.ASM ================================================ ;--- Set /D and /P option for currently loaded fastboot JLM. ;--- This program uses the API installed by FASTBOOT.DLL; ;--- alternatively, one can unload & reload FASTBOOT.DLL. .286 .model tiny .dosseg .stack 2048 option casemap:none .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm DStr macro text:vararg local sym .const sym db text,'$' .code exitm endm .data dopt db -1 popt db -1 .const helptxt label byte db "FBOOT: set FASTBOOT arguments.",13,10 db "Usage: FBOOT [/Dn] [/Pm]",13,10 db " /Dn: set disk to boot from to n [0-9]",13,10 db " /Pm: set partition to boot from to m [0-9]",13,10 db '$' .code getoption proc cld mov si,81h nextchr: lodsb es:[si] cmp al,13 jz cmdl_done cmp al,20h jbe nextchr cmp al,'/' jz is_option cmp al,'-' jnz disp_help is_option: lodsb es:[si] cmp al,13 jz disp_help or al,20h cmp al,'d' jz d_option cmp al,'p' jz p_option cmp al,'b' jz b_option jmp disp_help cmdl_done: clc ret d_option: lodsb es:[si] cmp al,'0' jb disp_help cmp al,'9' ja disp_help sub al,'0' mov dopt,al jmp nextchr p_option: lodsb es:[si] cmp al,'0' jb disp_help cmp al,'9' ja disp_help sub al,'0' mov popt,al jmp nextchr b_option: mov dx,DStr("not implemented yet",13,10) mov ah,9 int 21h jmp nextchr disp_help: mov dx,offset helptxt mov ah,9 int 21h stc ret getoption endp main proc local dwFB:dword call getoption jc exit mov bx,4435h ;FASTBOOT/REBOOT device ID mov ax,1684h ;get API entry point int 2Fh cmp al,0 jnz not_installed mov word ptr dwFB+0,di mov word ptr dwFB+2,es mov ah,0 ;get version call dwFB jc not_installed test ah,1 ;FASTBOOT variant? jz not_installed mov al,dopt cmp al,-1 jz no_dopt mov ah,1 call dwFB jc fb_call_failed mov dx,DStr("setting HD succeeded",13,10) mov ah,9 int 21h no_dopt: mov al,popt cmp al,-1 jz no_popt mov ah,2 call dwFB jc fb_call_failed mov dx,DStr("setting partition succeeded",13,10) mov ah,9 int 21h no_popt: exit: ret not_installed: mov dx,DStr("FASTBOOT not installed or wrong version") mov ah,9 int 21h call lfout ret fb_call_failed: mov dx,DStr("calling FASTBOOT failed") mov ah,9 int 21h call lfout ret main endp lfout proc mov dx,DStr(13,10) mov ah,9 int 21h ret lfout endp start: mov ax,cs mov ds,ax mov dx,ss sub dx,ax shl dx,4 mov ss,ax add sp,dx mov bx,sp shr bx,4 mov cx,es sub ax,cx add bx,ax mov ah,4Ah int 21h call main mov ah,4ch int 21h end start ================================================ FILE: JLM/REBOOT/LDI13EXT.ASM ================================================ ;--- sample how to load an int 13h extension into FASTBOOT JLM. ;--- this sample just swaps HDs 0 and 1 in int 13h; int 41h/46h are NOT modified. ;--- assemble: JWasm -mz LDI13EXT.asm .286 .model tiny .dosseg .stack 2048 option casemap:none .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm DStr macro text:vararg local sym .const sym db text,'$' .code exitm endm IRETS struct wIP dw ? wCS dw ? wFL dw ? IRETS ends .const helptxt label byte db "LDI13EXT: sample how to load an int 13h extension to FASTBOOT JLM.",13,10 db "Required:",13,10 db " - Jemm v5.86+",13,10 db " - Jemm loaded with option FASTBOOT",13,10 db " - FASTBOOT.DLL be loaded",13,10 db '$' .code ;--- int 13h extension initialization ;--- assumes: CS=_TEXT, DS=0000 int13ext proc push es pusha mov eax,ds:[13h*4] mov cs:[oldi13a],eax mov cs:[oldi13b],eax sub word ptr ds:[413h],1 ;decrease conv. memory by 1 kB mov ax,ds:[413h] shl ax,6 mov es,ax shl eax,16 xor di,di mov si,offset myint13 mov cx,size_int13ext2 cld rep movsb es:[di],cs:[si] mov ds:[13h*4],eax popa pop es retf ;--- the new int 13h code myint13: cmp dl,80h ;HD 0? jz is_hd0 cmp dl,81h ;HD 1? jz is_hd1 jmp_oldvec: db 0eah ;opcode jmp ssss:oooo oldi13a dd 0 is_hd0: is_hd1: xor dl,1 ; swap disks cmp ah,8 ; ah=8 is special because DL returns # of HDs jz jmp_oldvec pushf db 09ah ;opcode call ssss:oooo oldi13b dd 0 push bp mov bp,sp push ax pushf xor dl,1 ; restore DL pop ax mov byte ptr [bp+2].IRETS.wFL,al pop ax pop bp iret size_int13ext equ $ - int13ext size_int13ext2 equ $ - myint13 int13ext endp main proc local dwFB:dword cld mov si,81h nextchr: lodsb es:[si] cmp al,13 jz cmdl_done cmp al,20h jbe nextchr mov dx,offset helptxt mov ah,9 int 21h ret cmdl_done: mov bx,4435h ;FASTBOOT/REBOOT device ID mov ax,1684h ;get API entry point int 2Fh cmp al,0 jnz not_installed mov word ptr dwFB+0,di mov word ptr dwFB+2,es mov ah,0 ;get version call dwFB jc not_installed test ah,1 ;FASTBOOT variant? jz not_installed ;--- test that at least 2 HDs are present push ds mov ax,40h mov ds,ax mov al,ds:[75h] pop ds cmp al,2 jb no_hds ;--- FASTBOOT API AH=4: DS:SI=start extension, ECX=size extension in bytes mov si,offset int13ext mov ecx, size_int13ext mov ah,4 call dwFB jc no_int13ext mov dx,DStr("int 13h extension loaded") mov ah,9 int 21h call lfout ret not_installed: mov dx,DStr("FASTBOOT not installed") mov ah,9 int 21h call lfout ret no_hds: mov dx,DStr("at least 2 HDs are needed") mov ah,9 int 21h call lfout ret no_int13ext: mov dx,DStr("int 13h extension not accepted") mov ah,9 int 21h call lfout ret main endp lfout proc mov dx,DStr(13,10) mov ah,9 int 21h ret lfout endp start: mov ax,cs mov ds,ax mov dx,ss sub dx,ax shl dx,4 mov ss,ax add sp,dx mov bx,sp shr bx,4 mov cx,es sub ax,cx add bx,ax mov ah,4Ah int 21h call main mov ah,4ch int 21h end start ================================================ FILE: JLM/REBOOT/MAKE.BAT ================================================ @echo off jwasm -nologo -pe -Fl -Fo=REBOOT.DLL -I..\..\Include REBOOT.ASM jwasm -nologo -bin -Fl FASTBRM.ASM jwasm -nologo -pe -Fl -Fo=FASTBOOT.DLL -I..\..\Include FASTBOOT.ASM ================================================ FILE: JLM/REBOOT/REBOOT.ASM ================================================ ;--- sample how to handle DEVICE_REBOOT_NOTIFY control message ;--- requires Jemm v5.86+ .386 .MODEL FLAT, stdcall option casemap:none include jlm.inc DLL_PROCESS_ATTACH equ 1 DLL_PROCESS_DETACH equ 0 DEVICE_ID equ 4435h ifdef @pe_file_flags ;-pe option set? option dotname .drectve segment info db "-dll " db "-subsystem:native " db "-fixed:no " .drectve ends .hdr$2 segment flat db "PX" .hdr$2 ends endif .const public export ddb ddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,"REBOOT",0, ctrlproc > string1 db 13,10,"rebooting - press a key...",0 .CODE ctrlproc proc cmp eax, DEVICE_REBOOT_NOTIFY jz reboot_notify clc ret reboot_notify: @VMMCall Begin_Nest_Exec ;start nested execution mov eax,offset string1 call printstring mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 0 mov eax,16h @VMMCall Exec_Int @VMMCall End_Nest_Exec ;end nested execution clc ret ctrlproc endp printstring proc uses esi mov esi, eax cld nextitem: lodsb and al,al jz done mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 0Eh mov eax,10h @VMMCall Exec_Int jmp nextitem done: ret printstring endp DllMain PROC stdcall hModule:dword, dwReason:dword, dwRes:dword mov eax,dwReason cmp eax, DLL_PROCESS_ATTACH jnz @F mov eax,1 jmp exit @@: cmp eax,DLL_PROCESS_DETACH jnz @F mov eax,1 @@: exit: ret DllMain endp END DllMain ================================================ FILE: JLM/REBOOT/REBOOT.txt ================================================ About ReBoot Reboot is just a sample, supposed to be modified if Jemm's default way to reboot doesn't work on a machine. To load, add the following line to CONFIG.SYS: DEVICE=JLOAD.EXE REBOOT.DLL ================================================ FILE: JLM/XCDROM32/History.txt ================================================ History 10/2022: v1.5 - fixed: the driver was unable to handle multiple controllers with identical class/subclass/interface values. - enable busmaster feature in PCI controller if not set (Qemu). - timeout for startup audio functions increased to 7 secs. - removed obsolete /P option. - SATA controllers in non-AHCI mode handled correctly. 06/20/2012: v1.4 - function "get q-channel info" did return track number in binary. - function "get q-channel info" did return value of ADR as CONTROL and vice versa. - function "get audio status" did return start and end address as LBA sector numbers. 05/21/2012: v1.3 - scan for native (=SATA) controllers didn't succeed usually. 12/28/2007: v1.2 - /P, /W and /32 options added, /UF option changed to /F - Jemm's VDS DMA buffer used if user buffer cannot be used. - IDE/DMA ports are now displayed for each unit. 12/03/2007: v1.1 - now both "legacy" and "native" IDE controllers are supported. - some bugfixes. 05/24/2007: v1.0 - initial. Ported from XCDROM v1.9. ================================================ FILE: JLM/XCDROM32/MAKE.BAT ================================================ @echo off rem add -D_DEBUG to create a debug version jwasm -coff -nologo -Fl -I..\..\Include XCDROM32.ASM jwlink format win pe hx dll ru native file XCDROM32.obj name XCDROM32.DLL op q,map ================================================ FILE: JLM/XCDROM32/MAKEM.BAT ================================================ @echo off rem add -D_DEBUG to create a debug version rem using MS tools ml -c -coff -Fl -I..\..\Include XCDROM32.ASM link XCDROM32.OBJ /NOLOGO /DLL /out:XCDROM32.DLL /map:XCDROM32.map /subsystem:native /Fixed:no ================================================ FILE: JLM/XCDROM32/XCDROM32.ASM ================================================ ; ; XCDROM32.ASM - a JLM driver for UltraDMA CD-ROMs/DVDs ; based on XCDROM v1.9 by Jack R. Ellis ; released under the GNU GPL license v2 (see GNU_GPL.TXT for details) ; ; The source is to be assembled with JWasm or Masm v6+! ; ; XCDROM32 switch options are as follows: ; ; /32 use 32bit INSD instead of INSW in PIO mode. ; ; /AX Excludes ALL audio functions. This makes the driver report ; on a Device-Status request that it reads DATA tracks only! ; UltraDMA, dual-drives, and other driver features are NOT ; affected! ; ; /D: Specifies the desired "device name" which SHCDX33 or MSCDEX ; will use during their initialization to address the CD-ROM ; drives. Examples are: /D:CDROM1 /D:MYCDROM etc. The ; device name must be from 1 to 8 bytes valid for use in DOS ; filenames. If /D: is omitted, or the "device name" after ; a /D: is missing or invalid, "XCDROM$$" will be the default. ; ; /L Limits UltraDMA to "low memory" below 640K. /L is REQUIRED ; if upper memory blocks supplied by UMBPCI or a similar driver ; cannot do UltraDMA. /L causes I-O requests above 640K to use ; the VDS DMA buffer or, if none is available, to use "PIO mode" ; input. Note that /L will be IGNORED if /UX is also given. ; ; /Mn Specifies the MAXIMUM UltraDMA "mode" to be set for a CD-ROM ; drive, where n is a number between 0 and 7, as follows: ; 0 = ATA-16, 16 MB/sec. ; 1 = ATA-25, 25 MB/sec. ; 2 = ATA-33. 33 MB/sec. ; ... ; 7 = ATA-166. 166 MB/sec. ; A CD-ROM drive designed to use "modes" LESS than the given ; value will be limited to its own highest "mode". /M will ; be IGNORED for CD-ROM drives which cannot do UltraDMA, and ; it will be ignored for ALL drives if /UX is also given. ; ; /Q Quiet mode. ; ; /F "Fast" mode. Data input requests that cross an ; UltraDMA "64K boundary" are executed using a 2-element DMA ; command list, one for data up to the boundary, and one for ; data beyond it. This might increase CD-ROM speed. ; "Buffered" or "PIO mode" input is still needed for user ; buffers that are misaligned (not at an even 4-byte address). ; /F will be IGNORED for CD-ROM drives which cannot do DMA. ; ; --- NOTE --- ; Despite any UltraDMA specs, NOT ALL chipsets or mainboards ; can run multi-element DMA commands properly! Although it ; is valuable, /F must be TESTED on every system, and it ; should be enabled with CARE!! ; ; /UX Disables ALL UltraDMA, even for CD-ROM drives capable of it. ; The driver then uses "PIO mode" for all data input. /UX ; should be needed only for tests and diagnostic work. ; ; /W accept (non-UDMA) devices which can do multiword-DMA. ; ; For each switch, a dash may replace the slash, and lower-case letters ; may be used. .386 .model flat option casemap:none option proc:private include jlm.inc ?EXTDISP equ 0 ; extended displays for error trapping ; ; General Program Equations. ; NUMCTRL equ 3 ;max # of EIDE controllers NUMDSK equ 3 ;max # of CD/DVD devices SETMODE equ 1 ;support /M option, set UDMA mode SETBM equ 1 ;test Busmaster support in "class" MWDMA equ 1 ;support /W option to accept multi-word DMA DWRDIO equ 1 ;support /32 option (PIO DWORD IO) SAVESTAT equ 1 ;call save/restore "client reg struct" on init IDENTPKT equ 1 ;1=issue "identify packet" cmd, 0=normal "identify" VER equ <'V1.5, 06.10.2022'> ;Driver version number and date. MSELECT equ 0A0h ;"Master" device-select bits. SSELECT equ 0B0h ;"Slave" device-select bits. COOKSL equ 2048 ;CD-ROM "cooked" sector length. RAWSL equ 2352 ;CD-ROM "raw" sector length. CMDTO equ 00Ah ;500-msec minimum command timeout. SEEKTO equ 025h ;2-second minimum "seek" timeout. ;STARTTO equ 049h ;4-second minimum startup timeout. STARTTO equ 07Fh ;7-second minimum startup timeout. BIOSTMR equ 0046Ch ;BIOS "tick" timer address. ;VDSFLAG equ 0047Bh ;BIOS "Virtual DMA" flag address. ;IXM equ 2048 ;IOCTL transfer-length multiplier. CR equ 00Dh ;ASCII carriage-return. LF equ 00Ah ;ASCII line-feed. TAB equ 009h ;ASCII "tab". REQOFS equ 16h ; must be at least "sizeof DOSDRV" @byte equ @word equ @dword equ ; ; IDE Controller Register Definitions. ; CDATA equ 0 ;Data port. CSECCT equ 2 ;offset port for I-O sector count. CDSEL equ 6 ;offset port Drive-select and upper LBA. CCMD equ 7 ;offset port Command register (write) CSTAT equ 7 ;Primary status register (read) ; ; Controller Status and Command Definitions. ; BSY equ 080h ;IDE controller is busy. DRQ equ 008h ;IDE data request. ERR equ 001h ;IDE general error flag. DMI equ 004h ;DMA interrupt occured. DME equ 002h ;DMA error occurred. SETM equ 003h ;Set Mode subcommand. SETF equ 0EFh ;Set Features command. LBABITS equ 0E0h ;Fixed LBA command bits. ;--- structures DOSDRV struct dd ? wAttr dw ? ofsStr dw ? ofsInt dw ? name_ db 8 dup (?);+10 wRes1 dw ? ;+18 req. for cd drivers, init with 0 bRes2 db ? ;+20 req. for cd drivers, modified by mscdex bUnits db ? ;+21 set by driver DOSDRV ends ; ; DOS "Request Packet" header layout. ; RPH struc bHLen db ? ;+0 Header byte count. bSubU db ? ;+1 Subunit number. bOp db ? ;+2 Command code. wStat dw ? ;+3 Status field. db 8 dup (?);(Unused by us). RPH ends ;--- wStat values RPERR equ 08003h ;Packet "error" flags. RPDON equ 00100h ;Packet "done" flag. RPBUSY equ 00200h ;Packet "busy" flag. ;--- Init "Request Packet" Layout. RPINIT struc RPH <> bUnit db ? ;+13 Number of units found. RPSize dw ? ;+14 Resident driver offset dw ? dwCL dd ? ;Command-line data pointer. RPINIT ends ; ; IOCTL "Request Packet" Layout. ; IOC struc RPH <> db ? ;+13 Media descriptor byte (Unused by us). IOCAdr dd ? ;+14 Data-transfer address. IOCLen dw ? ;+18 Data-transfer length. dw ? ;+20 Starting sector (unused by us). dd ? ;+22 Volume I.D. pointer (unused by us). IOC ends ; ; Read Long "Request Packet" Layout. ; RL struc RPH <> RLAM db ? ;+13 Addressing mode. RLAddr dd ? ;+14 Data-transfer address. RLSC dw ? ;+18 Data-transfer sector count. RLSec dd ? ;+20 Starting sector number. RLDM db ? ;+24 Data-transfer mode. RLIntlv db ? ;+25 Interleave size. RLISkip db ? ;+26 Interleave skip factor. RL ends ; ; Play "Request Packet" Layout ; RPPlay struc RPH <> bMode db ? ;+13 Addressing mode. dwStart dd ? ;+14 Data-transfer address. dwSize dd ? ;+18 Data-transfer sector count. RPPlay ends IDEPARM struc wIDEBase dw ? wDMABase dw ? bDevSel db ? db ? IDEPARM ends AUDADR struct dwAudSta dd ? ;Current audio-start address - LBA? dwAudEnd dd ? ;Current audio-end address - LBA? dwLSSta dd ? ;Last-session starting LBA. AUDADR ends ;--- Unit Parameter (first 3 fields must match IDEPARM) UPARM struc wIDEBase dw ? ;IDE address (set by Init). wDMABase dw ? ;DMA address (set by Init). bDevSel db ? ;Device-select (set by Init). dw ? ;not used db ? ;Media-change flag (one byte below dwAudSta). AUDADR <> UPARM ends ;--- macros CStr macro text:vararg local sym .code text$01 sym db text,0 .code exitm endm @dprintf macro text,args:vararg local sym ifdef _DEBUG ifnb invoke printf, CStr(text), args else invoke printf, CStr(text) endif endif endm .data pRequest dd 0 ;linear address where request header is stored dwRequest dd 0 ;linear address request header dwBase dd 0 ;linear address driver base dwCmdLine dd 0 ; ; Main I-O Variables ; BufAdr dd 0 ;buffer linear address BufLng dd 0 ;buffer length XFRAdr dd 0 ;I-O data transfer address. XFRLng dd 0 ;I-O data transfer length. ; ; DMA Variables. ; DmaAdr dd 0 ;1st physical address for DMA DmaLng dd 0 ;1st DMA byte count DmaAdr2 dd 0 ;2nd physical address DmaLng2 dw 0 ;2nd DMA byte count LoWord dw 8000h ;2nd DMA byte count HiWord (fix) PRDAd dd 0 ;PRD 32-bit command addr. (Init set). align 4 ; ; ATAPI "Packet" Area (always 12 bytes for a CD-ROM). ; packet commands used by this program: ; - 01Bh: start/stop unit ; - 01Eh: prevent/allow media removal ; - 025h: read capacity ; - 028h: read (cooked) ; - 02Bh: seek ; - 042h: read sub-channel ; - 043h: read TOC ; - 047h: play audio MSF ; - 04Bh: pause/resume ; - 05Ah: mode sense ; - 0BEh: read CD (raw) ; Packet label byte PktOPC db 0 ;+0 Opcode. db 0 ;+1 Unused (LUN and reserved). PktLBA dd 0 ;+2 CD-ROM logical block address. PktLH db 0 ;+6 "Transfer length" (sector count). PktLn dw 0 ;+7 Middle- and low-order sector count. PktRM db 0 ;+9 Read mode ("Raw" Read Long only). dw 0 ;Unused ATAPI "pad" bytes (required). align 4 ; ; Miscellaneous Driver Variables. ; AudAP dd 0 ;Current audio-start address pointer. ;points to curr AUDADR struct bFlags db 0 bDMA db 0 ;DMA input flag (0/1). bTry db 0 ;I-O retry counter. align 4 ;bFlags values FL_Q equ 1 ;/Q option set FL_F equ 2 ;/F option set if MWDMA FL_W equ 4 ;/W option endif FL_UX equ 8 ;/UX option set ; ; Audio Function Buffer (16 bytes) for most CD-ROM "audio" requests. ; The variables below are used only during driver initialization. ; InBuf equ $ pUTbl dd offset UnitTbl ;Initialization unit table pointer. ScanX dd offset ScanLPM ;Scan table ptr (disks) pScan dd offset ScanLPM ;Scan table ptr (controllers) MaxUM db -1 ;UDMA "mode" limit set by /Mn option. UFlag db 0 ;UltraDMA "mode" flags (set by Init). UMode db 0 ;UltraDMA "mode" value (set by Init). bLegacy db 0 ;allow just one "legacy" controller ; ; Unit Parameter Tables. If you want a 4th drive, simply add 1 more ; parameter table -- NO extra code and NO other changes are needed! ; UnitTbl UPARM NUMDSK dup (<-1,-1,-1,0,-1,<-1,-1,-1>>) align 4 ; ; Dispatch Table for DOS CD-ROM request codes 0 through 14. ; DspTbl1 dd DspLmt1 ;Number of valid request codes. dd Try2ndD ;Invalid-request handler address. DspTblA dd UnSupp ;00 -- Initialization (special). dd UnSupp ;01 -- Media Check (unused). dd UnSupp ;02 -- Build BPB (unused). dd IOCTLInput ;03 -- IOCTL Input. dd UnSupp ;04 -- Input (unused). dd UnSupp ;05 -- Input no-wait (unused). dd UnSupp ;06 -- Input Status (unused). dd UnSupp ;07 -- Input flush (unused). dd UnSupp ;08 -- Output (unused). dd UnSupp ;09 -- Output & verify (unused). dd UnSupp ;10 -- Output status (unused). dd UnSupp ;11 -- Output flush (unused). dd IOCTLOutput ;12 -- IOCTL Output. dd Ignored ;13 -- Device Open (ignored). dd Ignored ;14 -- Device Close (ignored). DspLmt1 equ ($ - offset DspTblA)/4 ;Request-code limit for this table. dd DspLmt1 dup (0) ; ; Dispatch Table for DOS CD-ROM request codes 128 through 136. ; DspTbl2 dd DspLmt2 ;Number of valid request codes. dd UnSupp ;Invalid-request handler address. DspTblB dd ReqRL ;128 -- Read Long. dd UnSupp ;129 -- Reserved (unused). @RqPref dd ReqSeek ;130 -- Read Long Prefetch. @RqSeek dd ReqSeek ;131 -- Seek. @RqPlay dd ReqPlay ;132 -- Play Audio. @RqStop dd ReqStop ;133 -- Stop Audio. dd UnSupp ;134 -- Write Long (unused). dd UnSupp ;135 -- Wr. Long Verify (unused). @RqRsum dd ReqRsum ;136 -- Resume Audio. DspLmt2 equ ( $ - offset DspTblB)/4 ;Request-code limit for this table. dd DspLmt2 dup (0) ; ; Dispatch table for IOCTL Input requests. ; DspTbl3 dd DspLmt3 ;Number of valid request codes. dd UnSupp ;Invalid-request handler address. DspTblC dd ReqDHA ;00 -- Device-header address. @RqCHL dd ReqCHL ;01 -- Current head location. dd UnSupp ;02 -- Reserved (unused). dd UnSupp ;03 -- Error Statistics (unused). dd UnSupp ;04 -- Audio chan. info (unused). dd UnSupp ;05 -- Read drive bytes (unused). dd ReqDevStat ;06 -- Device status. dd ReqSecSize ;07 -- Sector size. @RqVS dd ReqVolSize ;08 -- Volume size. dd ReqMCS ;09 -- Media-change status. @RqADI dd ReqADI ;10 -- Audio disk info. @RqATI dd ReqATI ;11 -- Audio track info. @RqAQI dd ReqAQI ;12 -- Audio Q-channel info. dd UnSupp ;13 -- Subchannel info (unused). dd UnSupp ;14 -- Read UPC code (unused). @RqASI dd ReqASI ;15 -- Audio status info. DspLmt3 equ ( $ - offset DspTblC)/4 ;Request-code limit for this table. ;--- expected size of IOCTL input requests dd 5 ;0 dd 6 ;1 dd 0,0,0,0 ;2-5 dd 5 ;6 dd 4 ;7 dd 5 ;8 dd 2 ;9 dd 7 ;10 dd 7 ;11 dd 11 ;12 dd 0,0 ;13-14 dd 11 ;15 ; ; Dispatch table for IOCTL Output requests. ; DspTbl4 dd DspLmt4 ;Number of valid request codes. dd UnSupp ;Invalid-request handler address. DspTblD dd ReqEjct ;00 -- Eject Disk. dd ReqDoor ;01 -- Lock/Unlock Door. dd ReqRS ;02 -- Reset drive. dd UnSupp ;03 -- Audio control (unused). dd UnSupp ;04 -- Write ctl. bytes (unused). dd ReqTray ;05 -- Close tray. DspLmt4 equ ( $ - offset DspTblD)/4 ;Request-code limit for this table. ;--- expected size of IOCTL output requests dd 1 dd 2 dd 1 dd 0,0 dd 1 ; IDE controllers, programming interface bits: ; 7 bus mastering (read-only) ; 6-4 reserved (read-only) ; 3 secondary IDE mode bit is writable (read-only) ; 2 secondary IDE mode (0 = legacy, 1 = native) ; 1 primary IDE mode bit is writable (read-only) ; 0 primary IDE mode (0 = legacy, 1 = native) ; ; Initialization IDE Parameter-Value Table. ; each IDE controller needs 4 entries (primary/secondary, master/slave), ScanLPM IDEPARM NUMCTRL dup (<-1,-1,0A0h>,<-1,-1,0B0h>,<-1,-1,0A0h>,<-1,-1,0B0h>) if MWDMA MWModes label word dw 0004 ;Mode 0 dw 0013 ;Mode 1 dw 0016 ;Mode 2 endif UModes label word dw 0016 ;Mode 0, ATA-16 UDMA mode table dw 0025 ;Mode 1, ATA-25 dw 0033 ;Mode 2, ATA-33 dw 0044 ;Mode 3, ATA-44 (Unusual but possible). dw 0066 ;Mode 4, ATA-66 dw 0100 ;Mode 5, ATA-100 dw 0133 ;Mode 6, ATA-133 dw 0166 ;Mode 7, ATA-166 UnitNo dd 0 wDmaMode dw 0 db -1 DrvName db 40 dup (0) db 0 szDriver db 9 dup (0) rmcode label byte db 2Eh, 89h, 1eh, REQOFS+0, 0 ;mov cs:[16h],bx db 2Eh, 8Ch, 06h, REQOFS+2, 0 ;mov cs:[18h],es db 0CBh ;retf SIZERMCODE equ $ - offset rmcode db 0EAh ;jmp ssss:oooo .code include printf.inc ; ; "Device-Interrupt" routine -- This routine processes DOS requests. ; DevInt: call ZPacket ;Clear our ATAPI packet area. mov esi, [pRequest] movzx eax,@word [esi+0] movzx esi,@word [esi+2] shl esi, 4 add esi, eax ;Point to DOS request packet. mov [dwRequest], esi ;linear address request header mov [esi].RPH.wStat,RPDON ;Init status to "done". mov al,[esi].RPH.bSubU ;Get unit-table offset. mov ah,sizeof UPARM mul ah lea ebx,[eax+offset UnitTbl] ;Set unit's audio-start address ptr. lea eax, [ebx+UPARM.dwAudSta] mov [AudAP],eax push [ebp].Client_Reg_Struc.Client_EFlags ;save client flags or @byte [ebp].Client_Reg_Struc.Client_EFlags+1,2 ;set client's IF mov al,[esi].RPH.bOp ;Get packet request code. mov edi,offset DspTbl1 ;Point to 1st DOS dispatch table. call Dspatch ;Dispatch to desired request handler. pop [ebp].Client_Reg_Struc.Client_EFlags ;restore client flags @VMMCall Simulate_Far_Ret ret ; ; Function-Code "Dispatch" Routines. ; Try2ndD: sub al,080h ;Not request code 0-15: subtract 128. mov edi,offset DspTbl2 ;Point to 2nd DOS dispatch table. jmp Dspatch ;Go try request-dispatch again. IOCTLInput: mov edi,offset DspTbl3 ;Point to IOCTL Input dispatch table. jmp TryIOC IOCTLOutput: mov edi,offset DspTbl4 ;Point to IOCTL Output dispatch table. TryIOC: movzx ecx,@word [esi].IOC.IOCAdr+2 movzx esi,@word [esi].IOC.IOCAdr+0 shl ecx,4 add esi, ecx mov al,[esi] ;Get actual IOCTL request code. mov esi,[dwRequest] ;Reload DOS request-packet address. ;--- Dspatch() ;--- EDI=DTAB ;--- DTAB+0: dd #entries ;--- DTAB+4: dd offset error handler ;--- DTAB+8: start dispatch table ( size #entries ) Dspatch: xor edx,edx mov ecx, [edi] ;get number of table entries add edi,4 ;(Skip past table-limit value). cmp al,cl ;Is request code out-of-bounds? jae @F ;Yes? Dispatch to error handler! add edi,4 ;Skip past error-handler address. movzx eax,al ;Point to request-handler address. lea edi,[edi+eax*4] mov edx,[edi+ecx*4] @@: mov edi,[edi] ;Get handler address from table. and edx, edx jz @F ;--- v1.5: IXM removed ; shr edx,11 ;Ensure correct IOCTL transfer mov [esi].IOC.IOCLen,dx ;length is set in DOS packet. movzx ecx,@word [esi].IOC.IOCAdr+2 movzx esi,@word [esi].IOC.IOCAdr+0 shl ecx,4 ;Get IOCTL data-transfer address. add esi, ecx @@: jmp edi ;Dispatch to desired request handler. GenFail: mov al,12 ;General failure! Get error code. jmp ReqErr ;Go post packet error code & exit. UnSupp: mov al,3 ;Unsupported request! Get error code. jmp ReqErr ;Go post packet error code & exit. SectNF: mov al,8 ;Sector not found! Get error code. ReqErr: mov esi,[dwRequest] ;Reload DOS request-packet address. mov ah,081h ;Post error flags & code in packet. mov [esi].RPH.wStat,ax Ignored: ret ;Exit ("ignored" request handler). ; ; IOCTL Input "Device Header Address" handler ; ReqDHA: mov eax,[dwBase] shl eax,12 ;convert linear address to SSSS:0000 mov [esi+1], eax ret ; ; IOCTL Input "Return Sector Size" handler ; ReqSecSize: cmp @byte [esi+1],1 ;Is read mode "cooked" or "raw" ja GenFail ;No? Post "general failure" & exit. mov ax,RAWSL ;Get "raw" sector length. je @F ;If "raw" mode, set sector length. mov ax,COOKSL ;Get "cooked" sector length. @@: mov [esi+2],ax ;Post sector length in IOCTL packet. RqSSX: ret ; ; DOS "Read Long" handler. ; ReqRL: call ValSN ;Validate starting sector number. call MultiS ;Handle Multi-Session disk if needed. jc ReqErr ;If error, post return code & exit. mov cx,[esi].RL.RLSC ;Get request sector count. jcxz RqSSX ;If zero, simply exit. xchg cl,ch ;Save swapped sector count. mov [PktLn],cx cmp [esi].RL.RLDM,1 ;"Cooked" or "raw" read mode? ja SectNF ;No? Return "sector not found"! mov dl,028h ;Get "cooked" input values. mov ax,COOKSL jb @F ;If "cooked" input, set values. mov dl,0BEh ;Get "raw" input values. mov ax,RAWSL mov [PktRM],0F8h ;Set "raw" input flags. @@: mov [PktOPC],dl ;Set "packet" opcode. mul [esi].RL.RLSC ;Get desired input byte count. test dx,dx ;More than 64K bytes desired? jnz SectNF ;Yes? Return sector not found! movzx eax,ax mov [BufLng],eax ;Set DMA byte counts. mov ecx, eax ;set ECX for VDMAD call below bts eax,31 mov [DmaLng],eax ;Set DMA list "end" flag. movzx edx,@word [esi].RL.RLAddr+0 movzx eax,@word [esi].RL.RLAddr+2 shl eax, 4 add eax, edx mov [BufAdr],eax test @byte [ebx].UPARM.wDMABase,7 ;Is drive using UltraDMA? jnz UsePIO ;No, do "PIO mode" input. test al,3 ;Is user buffer 32-bit aligned? jnz BuffIO ;No, use buffered IO push esi mov esi, eax ;ESI=lin addr, ECX=length test [bFlags],FL_F ;if /F not set, check 64 kB boundary setz dl VxDCall VDMAD_Lock_DMA_Region ;is region physical contiguous? pop esi jc BuffIO ;Error -- use buffered input. mov [DmaAdr],edx mov eax,edx ;Get phys address. cmp @word [DmaAdr+2],-1 ;Is DMA beyond our limit? @DMALmt equ $-1 ;(009h for a 640K limit). ja BuffIO ;Yes, use buffered input test [bFlags],FL_F ;/F option set? jz UseDMA mov ecx,[DmaLng] ;Get lower ending DMA address. dec ecx ;(DmaLng - 1 + DmaAdr). add ax,cx ;Would input cross a 64K boundary? jnc UseDMA ;No, set DMA flag & do transfer. inc eax ;Get bytes above 64K boundary. cmp ax,64 ;Is this at least 64 bytes? jb BuffIO ;No, use buffered input inc ecx ;Get bytes below 64K boundary. sub cx,ax cmp cx,64 ;Is this at least 64 bytes? jb BuffIO ;No, use buffered input mov [DmaLng2],ax ;Set 2nd command-list byte count. movzx eax,cx ;Set 1st command-list byte count. mov [DmaLng],eax add eax,[DmaAdr] ;Set 2nd command-list address. mov [DmaAdr2],eax UseDMA: mov [bDMA],1 ;Set UltraDMA input flag. UsePIO: call DoIO ;Execute desired read request. jnc @F ;If no errors, go exit below. call ReqErr ;Post desired error code. @@: mov [bDMA],0 ;Reset UltraDMA input flag. ret ;--- use the VDS DMA buffer if user buffer cannot be used. ;--- this should be rarely necessary if /F is set. BuffIO: push esi push ebx mov esi,[BufAdr] mov ecx,[BufLng] VxDCall VDMAD_Request_Buffer ;get VDS DMA buffer mov eax,ebx pop ebx pop esi jc UsePIO ;no buffer available, use PIO! mov [DmaAdr],edx push eax call UseDMA pop ebx ;set buffer ID in ebx jc @F mov esi, [BufAdr] mov ecx, [BufLng] xor edi, edi ;buffer offset always zero VxDCall VDMAD_Copy_From_Buffer @@: VxDCall VDMAD_Release_Buffer ;buffer ID still in ebx ret ; ; DOS "Seek" handler. ; DOSSeek: call ValSN ;Validate desired seek address. call MultiS ;Handle Multi-Session disk if needed. jc DOSSkE ;If error, post return code & exit. mov [PktOPC],02Bh ;Set "seek" command code. DOSSk1: call DoIOCmd ;Issue desired command to drive. DOSSkE: jc ReqErr ;If error, post return code & exit. ret ; ; IOCTL Input "Device Status" handler. ; ;--- status flag bits: ;--- 001 - door opened ;--- 002 - door unlocked ;--- 004 - supports cooked AND raw ;--- 008 - read AND write ;--- 010 - data read AND play audio ;--- 020 - supports interleaving ;--- 040 - (res) ;--- 080 - supports prefetching ;--- 100 - supports audio channel manipulation ;--- 200 - supports HSG AND red book addressing modes ReqDevStat: mov @dword [Packet],0002A005Ah ;Set up mode-sense. mov al,16 ;Set input byte count of 16. call DoBufIO ;Issue mode-sense for hardware data. jc DOSSkE ;If error, post return code & exit. mov eax,00214h ;Get our basic driver status flags. @Status equ $-4 ;(Set by Init to 00204h for /AX). cmp @byte [edi+2],071h ;"Unknown CD", i.e. door open? jne @F ;No, check "locked" status. or al,001h ;Post "door open" status flag. @@: test @byte [edi+14],002h ;Drive pushbutton "locked out"? jnz @F ;No, set flags in IOCTL. or al,002h ;Set "door locked" status flag. @@: mov [esi+1],eax ;Set status flags in IOCTL buffer. @RqDSX: jmp ReadAudioSt ;Go post "busy" status and exit. ; ; IOCTL Input "Media-Change Status" handler. ; ReqMCS: call DoIOCmd ;Issue "Test Unit Ready" command. mov edi,[AudAP] ;Get media-change flag from table. mov al,[edi-1] mov [esi+1],al ;Return media-change flag to user. ret ; ; IOCTL Output "Eject Disk" handler. ; ReqEjct: mov @word [Packet],0011Bh ;Set "eject" commands. mov @byte [PktLBA+2],002h ;Set "eject" function. jmp DOSSk1 ;Go do "eject" & exit. ; ; IOCTL Output "Lock/Unlock Door" handler. ; ReqDoor: mov al,[esi+1] ;Get "lock" or "unlock" function. cmp al,001h ;Is function byte too big? ja RqRS1 ;Yes, post "General Failure" & exit. mov cx,0001Eh ;Get "lock" & "unlock" commands. RqDoor1: mov @word [Packet],cx ;Set "packet" command bytes. mov @byte [PktLBA+2],al ;Set "packet" function byte. call DoIOCmd ;Issue desired command to drive. jc DOSSkE ;If error, post return code & exit. jmp @RqDSX ;Go post "busy" status and exit. ; ; IOCTL Output "Reset Drive" handler. ; ReqRS: call StopDMA ;Stop previous DMA & select drive. inc edx ;Point to IDE command register. mov al,008h ;Do an ATAPI "soft reset" command. out dx,al call TestTO ;Await controller-ready. RqRS1: jc GenFail ;Timeout! Return "General Failure". ret ; ; IOCTL Output "Close Tray" handler. ; ReqTray: mov al,003h ;Get "close tray" function byte. mov cx,0011Bh ;Get "eject" & "close" commands. jmp RqDoor1 ;Go do "close tray" command above. ; ; Subroutine to handle a Multi-Session disk for DOS reads and seeks. ; Multi-Session disks require (A) saving the last-session starting ; LBA for a new disk after any media-change and (B) "offsetting" a ; read of the VTOC or initial directory block, sector 16 or 17, to ; access the VTOC/directory of the disk's last session. ; MultiS: mov edi,[AudAP] ;Point to drive variables. cmp @byte [edi+11],0FFh ;Is last-session LBA valid? jne MultiS1 ;Yes, proceed with request. mov [PktOPC],043h ;Set "Read TOC" command. inc @byte [PktLBA] ;Set "format 1" request. mov al,12 ;Set 12-byte allocation ct. call DoBufIO ;Read first & last session. jc MultiSX ;If any error, exit below. mov @byte [PktLBA],0 ;Reset "format 1" request. mov al,[edi+3] ;Set last session in packet. mov [PktLH],al call DoIO ;Read disk info for last session. jc MultiSX ;If error, exit with carry set. mov eax,[edi+8] ;"Swap" & save this disk's last- call Swap32 ; session starting LBA address. mov edi,[AudAP] mov [edi.AUDADR.dwLSSta],eax call ZPacket ;Reset our ATAPI packet area. MultiS1: mov eax,[esi].RL.RLSec ;Get starting sector number. mov edx,eax ;"Mask" sector to an even number. and dl,0FEh cmp edx,16 ;Sector 16 (VTOC) or 17 (directory)? jne MultiS2 ;No, set sector in packet. add eax,[edi+8] ;Offset sector to last-session start. MultiS2: call Swap32 ;"Swap" sector into packet as LBA. mov [PktLBA],eax clc ;Clear carry flag (no errors). MultiSX: ret ;--- read in internal buffer ;--- size in AL (12,16,4) ;--- the ATAPI command to perform is in PktOPC DoBufIO: mov @byte [PktLn+1],al ;Buffered -- set packet count. DoBufIn: ;<--- entry with AL=8 movzx eax,al ;Save data-transfer length. mov [BufLng],eax mov [BufAdr],offset InBuf;Use our buffer for I-O. jmp DoIO ;Go start I-O below. DoIOCmd: mov [BufLng],0 ;Command only -- reset length. ; ;--- I-O Subroutine. ALL of our CD-ROM I-O is executed here! ;--- ebx -> drive params ;--- Packet: ATAPI packet command to be sent ;--- esi must be preserved ;--- returns with C on errors, then AL contains error code ;--- edi is set to offset InBuf on exit DoIO proc cld push esi mov [bTry],4 ;Set request retry count of 4. DoIO1: ;<--- new try call StopDMA ;Stop previous DMA & select drive. call TestTO ;Await controller-ready. jc DoIOErr ;Timeout! Handle as a "hard error". cmp [bDMA],0 ;UltraDMA input request? je @F ;No, output our ATAPI "packet". mov dx,[ebx].UPARM.wDMABase;Point to DMA command register. mov al,008h ;Reset DMA commands & set read mode. out dx,al inc edx ;Point to DMA status register. inc edx in al,dx ;Reset DMA status register. or al,006h ;(Done this way so we do NOT alter out dx,al ; the "DMA capable" status flags!). inc edx ;Set PRD pointer to our DMA address. inc edx mov eax,[PRDAd] out dx,eax @@: mov dx,[ebx].UPARM.wIDEBase;Point to IDE "features" register. inc edx mov al,[bDMA] ;If UltraDMA input, set "DMA" flag. out dx,al add edx,3 ;Point to byte count registers. mov eax,[BufLng] ;Output data-transfer length. out dx,al inc edx mov al,ah out dx,al inc edx ;Point to command register. inc edx mov al,0A0h ;Issue "Packet" command. out dx,al mov cl,DRQ ;Await controller- and data-ready. call TestTO1 jc DoIOErr ;Timeout! Handle as a "hard error". xchg eax,esi ;Save BIOS timer address. mov dx,[ebx].UPARM.wIDEBase;Point to IDE data register. mov ecx,6 ;Output all 12 "Packet" bytes. mov esi,offset Packet rep outsw xchg eax,esi ;Reload BIOS timer address. mov ah,STARTTO ;Allow 4 seconds for drive startup. cmp [bDMA],0 ;UltraDMA input request? je DoPioIO ;No, do "PIO mode" transfer below. mov [XFRLng],0 ;Reset transfer length (DMA does it). add ah,[esi] ;Set 4-second timeout in AH-reg. mov @byte ds:[48Eh],0 ;Reset BIOS disk-interrupt flag. mov dx,[ebx].UPARM.wDMABase;Point to DMA command register. in al,dx ;Set DMA Start/Stop bit (starts DMA). or al,1 out dx,al @@: inc edx ;Point to DMA status register. inc edx in al,dx ;Read DMA controller status. dec edx ;Point back to DMA command register. dec edx and al,DMI+DME ;DMA interrupt or DMA error? jnz @F ;Yes, halt DMA and check results. cmp ah,[esi] ;Has our DMA transfer timed out? jz @F @VMMCall Yield ;run client's ISRs cmp @byte ds:[48Eh],0 ;Did BIOS get a disk interrupt? je @B ;No, loop back & retest status. mov al,DMI ;Set "simulated" interrupt flag. @@: xchg eax,esi ;Save ending DMA status. in al,dx ;Reset DMA Start/Stop bit. and al,0FEh out dx,al xchg eax,esi ;Reload ending DMA status. cmp al,DMI ;Did DMA end with only an interrupt? jne DoIOErr ;No? Handle as a "hard error"! inc edx ;Reread DMA controller status. inc edx in al,dx test al,DME ;Any "late" DMA error after DMA end? jz DoIO18 ;No, go await controller-ready. jmp DoIOErr ;Timeouts and DMA errors are "hard"! align 4 ;--- do PIO mode IO DoPioIO: mov edx,[BufAdr] mov ecx,[BufLng] mov [XFRAdr],edx ;reset data-transfer buffer address. mov [XFRLng],ecx ;reset data-transfer byte count. ContPioIO: ;<--- xor cl,cl call TestTO2 ;await controller-ready (CL=flag, AH=time). jc DoIOErr ;Timeout! Handle as a "hard error". test al,DRQ ;Did we also get a data-request? jz DoIO18 ;No, go await controller-ready. dec edx ;Get number of buffer bytes. dec edx in al,dx mov ah,al dec edx in al,dx inc eax ;Make buffer byte count "even". and eax,not 1 movzx eax,ax mov dx,[ebx].UPARM.wIDEBase;Point to IDE data register. mov esi,[XFRLng] ;Get our data-transfer length. test esi,esi ;Any remaining bytes to transfer? jz DoIO14 ;No, "eat" input or "pad" output. cmp esi,eax ;Buffer count > remaining bytes? jbe @F ;No, save current block count. mov esi,eax ;Set remaining bytes as block count. @@: mov ecx,esi ;Save current block count. mov edi,[XFRAdr] ;Get input data-transfer address. shr ecx,1 ;Get input word count. if DWRDIO @DWOP:: db 8Dh,9Bh,0,0,0,0 ;a 6 byte NOP (lea ebx,[ebx+0]) endif rep insw ;Input all data words. mov [XFRAdr],edi sub [XFRLng],esi ;Decrement data-transfer length. sub eax,esi ;Decrement buffer count. jz DoIO17 DoIO14: xchg eax,ecx ;Any "residual" bytes to handle? jecxz DoIO17 ;No, see if more I-O is needed. shr ecx,1 ;Get residual buffer word count. @@: in ax,dx ;"Eat" residual input bytes. loop @B DoIO17: mov ah,SEEKTO ;Allow 2 seconds if drive must "seek". jmp ContPioIO ;Go see if more I-O is needed. align 4 ;--- Finish DMA/PIO DoIO18: call TestTO ;Await controller-ready. jc DoIOErr ;Timeout! Handle as a "hard error". mov esi,[AudAP] ;Get drive media-change flag pointer. dec esi and eax,1 ;Did controller detect any errors? jz DoIO21 ;No, see if all data was transferred. sub edx,6 ;Get controller's sense key value. in al,dx shr al,4 cmp al,006h ;Is sense key "Unit Attention"? je DoIO23 ;Yes, check for prior media-change. mov ah,0FFh ;Get 0FFh M.C. flag for "Not Ready". cmp al,002h ;Is sense key "Drive Not Ready"? je DoIO24 ;Yes, go set our media-change flag. DoIOErr: mov dx,[ebx].UPARM.wIDEBase;Hard error! Point to command reg. add edx,7 mov al,008h ;Issue ATAPI "soft reset" to drive. out dx,al mov al,11 ;Get "hard error" return code. DoIO20: dec [bTry] ;Do we have more I-O retries left? jnz DoIO1 ;Try re-executing this I-O request. stc jmp DoIOExit ;No, return error code. align 4 ;--- operation ended without error DoIO21: mov ecx,[XFRLng] ;Get remaining data-transfer length. jecxz @F ;If zero, reset media-change & exit. cmp @byte [Packet],028h ;"Cooked" data input req.? je DoIOErr ;Yes, see if we can retry. cmp @byte [Packet],0BEh ;"Raw" data input request? je DoIOErr ;Yes, see if we can retry. mov edi,[XFRAdr] ;Load data-transfer address. rep stosb ;"Pad" remaining bytes to 0. @@: mov @byte [esi],1 ;Set "no media change" flag. clc ;Reset carry flag (no error). DoIOExit: pop esi mov edi,offset InBuf ;For audio, point to our buffer. ret ;--- operation ended with error "Unit Attention" ;--- esi=AudAP-1 ;--- AH= ;--- AL=error code if byte ptr [esi] !=1 DoIO23: mov al,2 ;"Attention": Get "Not Ready" code. cmp @byte [esi],0 ;Is media-change flag already set? jle DoIO20 ;Yes, retry & see if it goes away! DoIO24: xchg ah,[esi] ;Load & set our media-change flag. mov @byte [esi+12],0FFh ;Make last-session LBA invalid. dec ah ;Is media-change flag already set? jnz @F ;Yes, set carry flag and exit. mov al,15 ;Return "Invalid Media Change". ; @dprintf <"DoIO, error 15, esi=%X, eax=%X",13,10>, esi, eax @@: stc ;Set carry flag (error!). jmp DoIOExit align 4 DoIO endp ; ; Subroutine to convert "RedBook" MSF values to an LBA sector number. ; ConvLBA: mov ecx,eax ;Save "seconds" & "frames" in CX-reg. shr eax,16 ;Get "minute" value. cmp ax,99 ;Is "minute" value too large? ja CnvLBAE ;Yes, return -1 error value. cmp ch,60 ;Is "second" value too large? ja CnvLBAE ;Yes, return -1 error value. cmp cl,75 ;Is "frame" value too large? ja CnvLBAE ;Yes, return -1 error value. xor edx,edx ;Zero EDX-reg. for 32-bit math below. mov dl,60 ;Convert "minute" value to "seconds". mul dl ;(Multiply by 60, obviously!). mov dl,ch ;Add in "second" value. add ax,dx mov dl,75 ;Convert "second" value to "frames". mul edx ;(Multiply by 75 "frames"/second). mov dl,150 ;Subtract offset - "frame". sub dl,cl ;("Adds" frame, "subtracts" offset). sub eax,edx ret CnvLBAE: or eax,-1 ;Too large! Set -1 error value. ret ; ; Subroutine to clear our ATAPI "packet" area. ; ZPacket: xor eax, eax mov @dword [Packet+0],eax ;Zero ATAPI packet bytes. mov @dword [Packet+4],eax mov @dword [Packet+8],eax ret ; ; Subroutine to validate the starting disk sector number. ; ValSN proc mov eax,[esi].RL.RLSec ;Get starting sector number. ValSN1:: mov dl,[esi].RL.RLAM ;Get desired addressing mode. cmp dl,001h ;HSG or RedBook addressing? ja ValSNE ;Neither -- set carry and exit! jb @F ;HSG -- check sector limit. call ConvLBA ;Get RedBook starting sector. @@: ; cmp eax,00006DD3Ah cmp eax,001000000h ;Is starting sector too big? jb done ;No, all is well -- exit. ValSNE: pop eax ;Error! Discard our exit address. jmp SectNF ;Post "sector not found" and exit. done: ret ValSN endp ; ;--- Subroutine to test for I-O timeouts. ;--- In: EBX=UPARM ;--- Out: DX=IDE primary-status register. ;--- modifies: eax, esi ; TestTO proc xor cl,cl ;Check for only controller-ready. TestTO1:: ;<--- CL-reg = 008h to test for DRQ mov ah, CMDTO ;Use 500-msec command timeout. TestTO2:: ;<--- AH=STARTTO mov esi,BIOSTMR ;0040:006Ch add ah,[esi] ;Set timeout limit in AH-reg. @@: @VMMCall Yield ;preserves all registers cmp ah,[esi] ;Has our I-O timed out? stc ;(If so, set carry flag). je exit ;Yes? Exit with carry flag on. mov dx,[ebx].UPARM.wIDEBase add edx,7 in al,dx ;Read IDE primary status. test al,BSY ;Is our controller still busy? jnz @B ;Yes, loop back and test again. or cl,cl ;Are we also awaiting I-O data? jz exit ;No, just exit. test al,cl ;Is data-request (DRQ) also set? jz @B ;No, loop back and test again. exit: ret ;Exit -- carry indicates timeout. TestTO endp ; ; Subroutine to ensure UltraDMA is stopped and then select our CD-ROM ; drive. For some older chipsets, if UltraDMA is running, reading ; an IDE register causes the chipset to "HANG"!! ; StopDMA proc mov dx,[ebx].UPARM.wDMABase;Get drive UltraDMA command address. test dl,2+4 ;Is any UltraDMA controller present? jnz @F ;No, select "master" or "slave" unit. and dl,0FEh ;Mask out "DMA disabled" flag. in al,dx ;Ensure any previous DMA is stopped! and al,0FEh out dx,al @@: mov dx,[ebx].UPARM.wIDEBase;Point to IDE device-select register. add edx,6 mov al,[ebx].UPARM.bDevSel ;Select IDE "master" or "slave" unit. out dx,al ret StopDMA endp ; ; Subroutine to "swap" the 4 bytes of a 32-bit value. ; Swap32: xchg al,ah ;"Swap" original low-order bytes. rol eax,16 ;"Exchange" low- and high-order. xchg al,ah ;"Swap" ending low-order bytes. Swap32X: ret ; ; DOS "Audio Seek" handler. All DOS and IOCTL routines beyond this ; point are DISMISSED by driver-init when the /AX switch is given. ; ReqSeek: call ReadAudioSt ;Read current "audio" status. call ZPacket ;Reset our ATAPI packet area. jc RqSK1 ;If status error, do DOS seek. mov al,[edi+1] ;Get "audio" status flag. cmp al,011h ;Is drive in "play audio" mode? je RqSK2 ;Yes, validate seek address. cmp al,012h ;Is drive in "pause" mode? je RqSK2 ;Yes, validate seek address. RqSK1: jmp DOSSeek ;Use DOS seek routine above. RqSK2: call ValSN ;Validate desired seek address. mov edi,[AudAP] ;Point to audio-start address. cmp eax,[edi.AUDADR.dwAudEnd] ;Is address past "play" area? ja RqSK1 ;Yes, do DOS seek above. mov [edi.AUDADR.dwAudSta],eax ;Update "audio" start address. call ConvMSF ;Set "packet" audio-start address. mov @dword [PktLBA+1],eax mov eax,[edi.AUDADR.dwAudEnd] ;Set "packet" audio-end address. call ConvMSF mov @dword [PktLH],eax mov @byte [Packet],047h ;Set "Play Audio" command. call DoIOCmd ;Start drive playing audio. jc RqPLE ;If error, post code & exit. cmp @byte [edi+1],011h ;Playing audio before? je RqPLX ;Yes, post "busy" status and exit. call ZPacket ;Reset our ATAPI packet area. jmp ReqStop ;Go put drive back in "pause" mode. ; ; DOS "Play Audio" handler. ; ReqPlay: cmp [esi].RPPlay.dwSize,0 ;Is sector count zero? je Swap32X ;Yes, just exit above. mov edi,[AudAP] ;Point to audio-start addr. mov @byte [Packet],047h ;Set "Play Audio" command. mov eax,[esi].RPPlay.dwStart;Validate starting address. call ValSN1 mov [edi.AUDADR.dwAudSta],eax ;Save audio-start address. call ConvMSF ;Set MSF start address in packet. mov @dword [PktLBA+1],eax mov eax,[esi].RPPlay.dwSize ;calc "end" address. add eax,[edi.AUDADR.dwAudSta] mov edx,00006DD39h ;Get maximum audio address. jc ReqPL1 ;If "end" WAY too big, use max. cmp eax,edx ;Is "end" address past maximum? jbe ReqPL2 ;No, use "end" address as-is. ReqPL1: mov eax,edx ;Set "end" address to maximum. ReqPL2: mov [edi+4],eax ;Save audio-end address. call ConvMSF ;Set MSF end address in packet. mov @dword [PktLH],eax call DoIOCmd ;Issue "Play Audio" command. RqPLE: jc ReqErr ;Error! Post return code & exit. RqPLX: jmp RdAST3 ;Go post "busy" status and exit. ; ; DOS "Stop Audio" handler. ; ReqStop: mov @byte [Packet],04Bh ;Set "Pause/Resume" cmd. jmp DoIOCmd ;Go pause "audio", then exit. ; ; DOS "Resume Audio" handler. ; ReqRsum: inc @byte [PktLn+1] ;Set "Resume" flag for above. call ReqStop ;Issue "Pause/Resume" command. jmp RqPLE ;Go exit through "ReqPlay" above. ; ; IOCTL Input "Current Head Location" handler. ; ReqCHL: ;--- issue "read sub-channel" command (42h), MSF=0, SubQ=1 ;--- data format to be returned is 01 (=Audio Status) mov @dword [Packet],001400042h ;Set command bytes. mov al,16 ;Set input byte count of 16. call RdAST2 ;Issue "Read Subchannel" request. jc RqPLE ;If error, post return code & exit. mov @byte [esi+1],0 ;Return "HSG" addressing mode. mov eax,[edi+8] ;Return "swapped" head location. call Swap32 mov [esi+2],eax jmp RqATIX ;Go post "busy" status and exit. ; ; IOCTL Input "Return Volume Size" handler. ; ReqVolSize: mov @byte [Packet],025h ;Set "Read Capacity" code. mov al,008h ;Get 8 byte data-transfer length. call DoBufIn ;Issue "Read Capacity" command. jc RqPLE ;If error, post return code & exit. mov eax,[edi] ;Set "swapped" size in IOCTL packet. call Swap32 mov [esi+1],eax jmp RqATIX ;Go post "busy" status and exit. ; ; IOCTL Input "Audio Disk Info" handler. ;--- db ? ;lowest track number (binary) ;--- db ? ;highest track number (binary) ;--- dd ? ;start of lead-out track ; ReqADI: mov al,0AAh ;Specify "lead-out" session number. call ReadTOC ;Read disk table-of-contents (TOC). jc ReqErr ;If error, post return code & exit. mov [esi+3],eax ;Set "lead out" LBA addr. in IOCTL. mov ax,[edi+2] ;Set first & last tracks in IOCTL. mov [esi+1],ax jmp RqATIX ;Go post "busy" status and exit. ; ; IOCTL Input "Audio Track Info" handler. ;--- db ? ;track number (binary) ;--- dd ? ;start of track (red book format) ;--- db ? ;track control info (ADR=low nibble, CONTROL=high nibble) ; ReqATI: mov al,[esi+1] ;Specify desired session (track) no. call ReadTOC ;Read disk table-of-contents (TOC). jc ReqErr ;If error, post return code & exit. mov [esi+2],eax ;Set track LBA address in IOCTL. mov al,[edi+5] shl al,4 mov [esi+6],al RqATIX: jmp ReadAudioSt ;Go post "busy" status and exit. ; ; IOCTL Input "Audio Q-Channel Info" handler. ;--- db ? ;+1 CONTROL (4-7) and ADR (0-3) ;--- db ? ;+2 track number (in BCD?) ;--- db ? ;+3 index (unchanged from disk) ;--- running time within track (binary) ;--- db ? ;+4 min ;--- db ? ;+5 sec ;--- db ? ;+6 frame ;--- db ? ;+7 zero ;--- running time on disk (binary) ;--- db ? ;+8 min ;--- db ? ;+9 sec ;--- db ? ;+10 frame ;--- the sub-channel data that is returned from the ATAPI device ;--- consists of a 4-byte header and 12 bytes data, ;--- the data's format depends on the format code: ;--- 01: CD-ROM current position ;--- 02: UPC/bar code ;--- 03: ISRC code ;--- data header: ;--- db ? ;+0 reserved ;--- db ? ;+1 Audio Status ;--- dw ? ;+2 sub-channel data length ;--- data (CD-ROM current position): ;--- db ? ;+4 data format code ( =01 ) ;--- db ? ;+5 ADR (bits 7-4) CONTROL (bits 3-0) ;--- db ? ;+6 track number ;--- db ? ;+7 index number ;--- dd ? ;+8 absolute CD-ROM address ;--- dd ? ;+12 track-relative CD-ROM address ReqAQI: mov ax,04010h ;read sub-channel, SubQ=1, use 16-byte count. call RdAST1 ;Read sub-channel jc ReqErr ;If error, post return code (AL) & exit. mov edx,[edi+5] ;Set ctrl/track/index in IOCTL. if 1 ;--- xchg ADR and CONTROL rol dl,4 ;--- convert TNO to BCD ;--- ensure that conversion is done only when ADR is 1 ; mov al,dl ; and al,0Fh ; cmp al,1 ; jnz @F mov al,dh aam ;converts binary in AL to BCD in AH,AL db 0d5h,10h ;packs 2 BCD digits in AH,AL into AL mov dh,al @@: endif mov [esi+1],edx mov eax,[edi+13] ;Set time-on-track in IOCTL. mov [esi+4],eax mov edx,[edi+9] ;Get time-on-disk & clear high shl edx,8 ; order time-on-track in IOCTL. mov [esi+7],edx ;Set time-on-disk ret ; ; IOCTL Input "Audio Status Info" handler. ; ReqASI: mov ax,04010h ;Set SubQ=1, use 16-byte count. call RdAST1 ;Read sub-channel jc ReqErr ;If error, post return code & exit. xor eax,eax ;Reset starting audio address. mov [esi+1],ax ;Reset audio "paused" flag. xor edx,edx ;Reset ending audio address. cmp @byte [edi+1],011h ;Is drive now "playing" audio? jne @F ;No, check for audio "pause". mov edi,[AudAP] ;Point to drive's audio data. mov eax,[edi.AUDADR.dwAudSta] ;Get current audio "start" addr. call ConvMSF jmp RqASI2 ;Go get current audio "end" addr. @@: cmp @byte [edi+1],012h ;Is drive now in audio "pause"? jne RqASI3 ;No, return "null" addresses. or @byte [esi+1],1 ;Set audio "paused" flag. mov eax,[edi+8] ;Convert time-on-disk to LBA addr. call Swap32 ;--- do not convert to LBA ; call ConvLBA mov edi,[AudAP] ;Point to drive's audio data. RqASI2: push eax mov eax,[edi.AUDADR.dwAudEnd] ;Get current audio "end" address. call ConvMSF call Swap32 shr eax, 8 mov edx, eax pop eax RqASI3: mov [esi+3],eax ;Set audio "start" addr. in IOCTL. mov [esi+7],edx ;Set audio "end" address in IOCTL. ret ; ; Subroutine to read the current "audio" status and disk address. ; ReadAudioSt: call ZPacket ;Status only -- reset ATAPI packet. mov ax,00004h ;set SubQ=0, use 4-byte count. RdAST1: ;<--- entry ReqAQI, ReqASI (ax=4010h) ;--- issue a "read sub-channel" packet command (42h) ;--- MSF (bit 1 of byte 1) is 1 ;--- data format is 1 (=Audio Status) mov @dword [Packet],001000242h ;Set command bytes. mov @byte [PktLBA],ah ;Set SubQ flag (bit 6) RdAST2: call DoBufIO ;Issue "Read Subchannel" command. jc RdASTX ;If error, exit immediately. cmp @byte [edi+1],011h ;Is a "play audio" in progress? jnz RdTOC1 ;No, clear carry flag and exit. RdAST3: push esi ;Save SI- and ES-regs. mov esi,[dwRequest] ;Reload DOS request-packet addr. or [esi].RPH.wStat,RPBUSY ;Set "busy" status bit. pop esi RdASTX: ret ; ; Subroutine to read disk "Table of Contents" (TOC) values. ; ReadTOC: mov @word [Packet],00243h ;Set TOC and MSF bytes. mov [PktLH],al ;Set desired "session" number. mov al,12 ;Get 12-byte "allocation" count. call DoBufIO ;Issue "Read Table of Contents" cmd. jc RdTOCX ;If error, exit immediately. mov eax,[edi+8] ;Return "swapped" starting address. call Swap32 RdTOC1: clc ;Clear carry flag (no error). RdTOCX: ret align 4 ; ; Subroutine to convert an LBA sector number to "RedBook" MSF format. ; ConvMSF: add eax,150 ;Add in offset. push eax ;Get address in DX:AX-regs. pop ax pop dx mov cx,75 ;Divide by 75 "frames"/second. div cx shl eax,16 ;Set "frames" remainder in upper EAX. mov al,dl ror eax,16 mov cl,60 ;Divide quotient by 60 seconds/min. div cl ret ;Exit -- EAX-reg. contains MSF value. ;--- set device name (/D:xxxx) I_SetName proc mov edi,[dwBase] ;Blank out device name. add edi,10 lea edx,[edi+8] mov eax," " mov [edi+0],eax mov [edi+4],eax I_NameB: mov al,[esi] ;Get next device-name byte. cmp al,TAB ;Is byte a "tab"? je I_NxtCJ ;Yes, handle above, "name" has ended! cmp al,' ' ;Is byte a space? je I_NxtCJ ;Yes, handle above, "name" has ended! cmp al,'/' ;Is byte a slash? je I_NxtCJ ;Yes, handle above, "name" has ended! cmp al,0 ;Is byte the command-line terminator? je I_NxtCJ ;Yes, go test for UltraDMA controller. cmp al,LF ;Is byte an ASCII line-feed? je I_NxtCJ ;Yes, go test for UltraDMA controller. cmp al,CR ;Is byte an ASCII carriage-return? je I_NxtCJ ;Yes, go test for UltraDMA controller. cmp al,'a' ;Ensure letters are upper-case. jc I_Name2 cmp al,'z' ja I_Name2 and al,0DFh I_Name2: cmp al,'!' ;Is this byte an exclamation point? jz I_Name3 ;Yes, store it in device name. cmp al,'#' ;Is byte below a pound-sign? jb I_Name4 ;Yes, Invalid! Blank first byte. cmp al,')' ;Is byte a right-parenthesis or less? jbe I_Name3 ;Yes, store it in device name. cmp al,'-' ;Is byte a dash? jz I_Name3 ;Yes, store it in device name. cmp al,'0' ;Is byte below a zero? jb I_Name4 ;Yes, invalid! Blank first byte. cmp al,'9' ;Is byte a nine or less? jbe I_Name3 ;Yes, store it in device name. cmp al,'@' ;Is byte below an "at sign"? jb I_Name4 ;Yes, invalid! Blank first byte. cmp al,'Z' ;Is byte a "Z" or less? jbe I_Name3 ;Yes, store it in device name. cmp al,'^' ;Is byte below a carat? jb I_Name4 ;Yes, invalid! Blank first byte. cmp al,'~' ;Is byte above a tilde? ja I_Name4 ;Yes, invalid! Blank first byte. cmp al,'|' ;Is byte an "or" symbol? je I_Name4 ;Yes, invalid! Blank first byte. I_Name3: stosb ;Store next byte in device name. inc esi ;Bump command-line pointer. cmp edi,edx ;Have we stored 8 device-name bytes? jb I_NameB ;No, go get next byte. I_NxtCJ: ret I_Name4: mov @byte [edx-8],' ' ret I_SetName endp ;--- handle /AX option I_SetAX proc mov eax,offset UnSupp ;Disable all unwanted dispatches. mov [@RqPlay],eax ; play audio mov [@RqStop],eax ; stop audio mov [@RqRsum],eax ; resume audio mov [@RqCHL],eax ; current head location mov [@RqADI],eax ; audio disk info mov [@RqATI],eax ; audio track info mov [@RqAQI],eax ; audio q-channel info mov [@RqASI],eax ; audio status info mov eax,offset DOSSeek ;Do only LBA-address DOS seeks. mov [@RqPref],eax mov [@RqSeek],eax mov al,004h ; Have "Device Status" declare mov @byte ds:[@Status],al ; we handle DATA reads only ;--- have it NOT update the IOCTL "busy" flag & return. ;--- ["ReadAudioSt" gets DISMISSED]! mov @byte ds:[@RqDSX], 0C3h ret I_SetAX endp ;--- for SATA controllers, check if AHCI mode is disabled ;--- in: EAX=phys. address HBA IsAHCIdisabled proc uses edi ebx @dprintf <"SATA controller at %X",13,10>, eax mov edi, eax push 0 push 1 push PR_SYSTEM @VMMCall _PageReserve ;allocate a 4 kB block of address space add esp,3*4 cmp eax,-1 jz error mov ebx, eax ; save linear address in ebx @dprintf <"HBA mapped at %X",13,10>, ebx shr eax, 12 ;convert linear address to page number push PC_INCR or PC_WRITEABLE mov edx, edi shr edx, 12 push edx push 1 push eax @VMMCall _PageCommitPhys ;backup address space add esp,4*4 and edi, 0fffh mov eax, [ebx+edi+4] @dprintf <"HBA.GHC=%X",13,10>, eax ;--- todo: free address space and eax, eax js error or eax, -1 ret error: xor eax, eax ret IsAHCIdisabled endp ; ; Driver Initialization Routine. ; I_Init proc cld mov esi,[dwRequest] ;Point to DOS request packet. cmp [esi].RPINIT.bOp,0 ;Is this an "Init" packet? jnz I_BadP mov esi,[dwCmdLine] ;Point to command line that loaded us. I_NxtC: lodsb ;Get next command-line byte. cmp al,0 je I_Term cmp al,LF je I_Term cmp al,CR je I_Term cmp al,'-' ; a dash? je @F ; Yes, see what next "switch" byte is. cmp al,'/' ; a slash? jne I_NxtC ; No, check next command-line byte. @@: mov ax,[esi] ;Get next 2 command-line bytes. or ax,2020h ;convert to lower case cmp ax,'xu' ;/UX? jne @F inc esi ;Bump pointer past "UX" switch. inc esi or [bFlags],FL_UX jmp I_NxtC @@: cmp al,'f' ;/F? jnz @F inc esi or [bFlags],FL_F jmp I_NxtC @@: cmp ax,'xa' ;/AX? jne @F inc esi ;Bump pointer past "ax" inc esi call I_SetAX jmp I_NxtC @@: if DWRDIO cmp ax,"23" ;/32? jnz @F inc esi inc esi mov @dword ds:[@DWOP+0],6DF3E9D1h ;shr ecx,1 rep insd mov @word ds:[@DWOP+4],0C911h ;adc ecx,ecx jmp I_NxtC @@: endif cmp al,'l' ;/L? jne @F mov @byte [@DMALmt],009h ;Set 640K "DMA limit" above. inc esi ;Bump pointer past "limit" switch. jmp I_NxtC @@: if SETMODE cmp al,'m' ;Is this byte an "M" or "m"? jne @F inc esi ;Bump pointer past "mode" switch. cmp ah,'7' ja I_NxtC sub ah,'0' jb I_NxtC mov [MaxUM],ah ;Set maximum UltraDMA "mode" above. inc esi ;Bump pointer past "mode" value. jmp I_NxtC @@: endif I_ChkD: cmp al,'d' ;Is switch byte a "D" or "d"? jne @F inc esi ;Bump pointer past "device" switch. cmp ah,':' ;Is following byte a colon? jne I_NxtC inc esi ;Bump pointer past colon. call I_SetName jmp I_NxtC @@: if MWDMA cmp al,'w' ;/W? jnz @F or [bFlags],FL_W inc esi jmp I_NxtC @@: endif cmp al,'q' jnz I_NxtC inc esi or [bFlags],FL_Q jmp I_NxtC getpci: mov dx, 0cf8h out dx, eax add dl, 4 in eax, dx sub dl, 4 ret setpci: mov dx, 0cf8h out dx, eax add dl, 4 mov eax, ecx out dx, eax sub dl, 4 ret ;--- done cmdline parsing I_Term: test [bFlags],FL_Q jnz @F invoke printf, CStr('XCDROM32 ',VER,'.',CR,LF) @@: ;--- check for PCI BIOS mov @word [ebp].Client_Reg_Struc.Client_EAX, 0B101h ; get PCI BIOS version @VMMCall Begin_Nest_Exec mov eax, 1Ah @VMMCall Exec_Int @VMMCall End_Nest_Exec mov edx, [ebp].Client_Reg_Struc.Client_EDX cmp edx," ICP" ;Do we have a V2.0C or newer PCI BIOS? jne I_ChkNm ;No, check for valid driver name. ;--- scan for native/legacy IDE controllers ;--- rewritten for v1.5 mov edi, [pScan] mov esi, 80000008h I_FindC: mov eax, esi call getpci shr eax, 8 mov ebx, eax and al, 80h cmp eax, 10180h ;storage+IDE+busmaster? jz isEIDE cmp ebx, 10601h ;SATA controller? jz isSATA I_ContFindC: @VMMCall Yield add esi, 100h cmp esi, 81000008h jb I_FindC jmp I_DoneScan isSATA: ;--- for SATA controllers, check if AHCI is enabled mov eax, esi mov al, 16+5*4 call getpci and al, 0fch call IsAHCIdisabled and eax, eax jz I_ContFindC ;if AHCI active, ignore device isEIDE: if SETBM mov eax, esi mov al, 4 call getpci bts eax, 2 ;busmaster enabled? jc @F ;--- enable busmaster mov ecx, eax mov eax, esi mov al, 4 call setpci @@: endif mov eax, esi mov al,16+4*4 ;Get DMA base address (register 4). call getpci and al,0FCh mov ecx, eax test bl, 1 ;primary in "native" mode? jnz prim_native mov ax, 1F0h bts dword ptr bLegacy, 0 jnc @F or eax, -1 jmp @F prim_native: mov eax, esi mov al,16+0*4 ;Get primary channel base port call getpci and al,0FCh @@: mov [edi+0*sizeof IDEPARM].IDEPARM.wIDEBase, ax mov [edi+0*sizeof IDEPARM].IDEPARM.wDMABase, cx mov [edi+1*sizeof IDEPARM].IDEPARM.wIDEBase, ax mov [edi+1*sizeof IDEPARM].IDEPARM.wDMABase, cx test bl, 4 ;secondary in "native" mode? jnz sec_native mov ax, 170h bts dword ptr bLegacy, 1 jnc @F or eax, -1 jmp @F sec_native: mov eax, esi mov al,16+2*4 ;Get secondary channel base port call getpci and al,0FCh @@: add ecx, 8 mov [edi+2*sizeof IDEPARM].IDEPARM.wIDEBase, ax mov [edi+2*sizeof IDEPARM].IDEPARM.wDMABase, cx mov [edi+3*sizeof IDEPARM].IDEPARM.wIDEBase, ax mov [edi+3*sizeof IDEPARM].IDEPARM.wDMABase, cx @dprintf <"controller primary base=%X, secondary base=%X, dmabase=%X",13,10>, [edi].IDEPARM.wIDEBase, ax, [edi].IDEPARM.wDMABase test [bFlags], FL_Q jnz @F mov eax, esi shr eax, 16 mov ah, 0 mov edx, esi shr edx, 11 and edx, 1Fh mov ecx, esi shr ecx, 8 and ecx, 7 invoke printf, CStr("IDE controller at bus/dev/fn=%u/%u/%u, P/S/DMA=%X/%X/%X",CR,LF), eax, edx, ecx, [edi].IDEPARM.wIDEBase, [edi+2*sizeof IDEPARM].IDEPARM.wIDEBase, [edi].IDEPARM.wDMABase @@: add edi, 4 * sizeof IDEPARM cmp edi, offset ScanLPM + sizeof ScanLPM jb I_ContFindC ;continue PCI scan I_DoneScan: mov [pScan], edi ;--- done controller scan I_ChkNm: cmp [pScan], offset ScanLPM ; any PCI IDE controller found? jnz @F mov ax, 1F0h ; if no, check legacy ports mov [ScanLPM+0*sizeof IDEPARM].wIDEBase, ax mov [ScanLPM+1*sizeof IDEPARM].wIDEBase, ax mov ax, 170h mov [ScanLPM+2*sizeof IDEPARM].wIDEBase, ax mov [ScanLPM+3*sizeof IDEPARM].wIDEBase, ax add [pScan], 4 * sizeof IDEPARM @@: mov ecx,[dwBase] cmp @byte [ecx+10],' ' ;Is driver "name" valid? jnz @F mov @dword [ecx+10],"RDCX" ;Set our default "name". mov @dword [ecx+10+4],"$$MO" @@: test [bFlags],FL_Q jnz @F mov eax,[ecx+10+0] mov @dword [szDriver+0],eax mov eax,[ecx+10+4] mov @dword [szDriver+4],eax invoke printf, CStr('Driver name is %s.',13,10), offset szDriver @@: mov esi, offset DmaAdr mov ecx, 4*4 xor edx, edx VxDCall VDMAD_Lock_DMA_Region mov [PRDAd],edx ;Set physical PRD address. if 0 test [bFlags],FL_UX ;Did user disable UltraDMA? jnz I_LinkX ;Yes, proceed cmp @byte [@DMALmt],-1 ;Is UltraDMA limited to < 640K? je I_LinkX ;No, proceed mov edx,offset LEMsg ;Point to "/L Invalid" message. cmp @word [DmaAdr+2],009h ;Are we loaded high? ja I_InitE ;Yes? Display message and exit! I_LinkX: endif @dprintf <"Start drive scan",CR,LF> mov [pUTbl], offset UnitTbl ;Reset our unit-table pointer. I_ScanU: ;<--- try next mov ebx,[ScanX] ;Get current parameters index. cmp ebx,[pScan] ;Any more IDE units to check? je I_AnyCD ;No, check for any drives to use. add [ScanX], sizeof IDEPARM mov ax,[ebx].IDEPARM.wIDEBase;Get unit's IDE address cmp ax,-1 ;valid address? je I_ScanU ;no, skip this entry call I_ValDV ;check if device is an ATAPI CD-ROM jnc I_ScnVN ;if yes, display device and update UnitTbl if ?EXTDISP invoke printf, CStr("port %X,dev=%X: v86 ss:sp=%X:%X, %s",13,10), [ebx].IDEPARM.wIDEBase, @word [ebx].IDEPARM.bDevSel, [ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP, edi else @VMMCall Yield ;don't remove, may avoid v86 stack errors endif jmp I_ScanU ;try next unit. I_ScnVN: test [bFlags],FL_UX ;Was the /UX switch given? je @F or @byte [ebx].UPARM.wDMABase,1 ;Post drive "DMA disabled". @@: test [bFlags],FL_Q jnz nodisp mov edi,offset DrvName + sizeof DrvName @@: mov @byte [edi],0 ; skip trailing spaces of drive name dec edi cmp @byte [edi],' ' je @B mov edx, CStr("Master") test [ebx].IDEPARM.bDevSel,10h jz @F mov edx, CStr("Slave") @@: invoke printf, CStr("Unit %u: %s, IDE/DMA ports %X/%X, %s, "), UnitNo, edx, [ebx].IDEPARM.wIDEBase, [ebx].IDEPARM.wDMABase, offset DrvName mov ecx,CStr("PIO mode",13,10) test @byte [ebx].IDEPARM.wDMABase,7 ;Will drive be using UltraDMA? jnz @F mov ecx, CStr("ATA-%u",13,10) @@: invoke printf, ecx, wDmaMode if ?EXTDISP invoke printf, CStr("v86 ss:sp=%X:%X",13,10),[ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP endif nodisp: mov esi,[pUTbl] ;Update all unit-table parameters. mov ax,[ebx].IDEPARM.wIDEBase mov cx,[ebx].IDEPARM.wDMABase mov dl,[ebx].IDEPARM.bDevSel mov [esi].UPARM.wIDEBase,ax mov [esi].UPARM.wDMABase,cx mov [esi].UPARM.bDevSel,dl add esi,sizeof UPARM ;Update unit-table pointer. mov [pUTbl],esi ; inc @byte [Units] ;Bump number of active units. mov eax, [dwBase] inc [eax].DOSDRV.bUnits inc [UnitNo] ;Bump display unit number. cmp esi,offset UnitTbl + sizeof UnitTbl ;end of table reached? jb I_ScanU ;no, loop back & check for more. I_AnyCD: @dprintf <"End drive scan",CR,LF> ; cmp [Units],0 ;Do we have any CD-ROM drives to use? mov edx, [dwBase] cmp [edx].DOSDRV.bUnits,0 ;Do we have any CD-ROM drives to use? ja I_ClrSt ;Yes, success mov edx, CStr('No CD-ROM drive to use') I_InitE: invoke printf, CStr('%s; XCDROM32 not loaded!',CR,LF), edx I_BadP: xor eax,eax ;Get "null" length & error flags. mov dx,RPDON+RPERR jmp I_Exit ;Go set "init" packet values & exit. I_ClrSt: if 0 invoke printf, CStr("xcdrom32: v86 ss:sp=%X:%X",CR,LF), [ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP endif mov dx,RPDON ;Get initialization "success" code. mov ax,12h+4+SIZERMCODE+5 I_Exit: mov esi,[dwRequest] ;Set result values in "init" packet. mov [esi].RPINIT.RPSize,ax mov [esi].RPINIT.wStat,dx mov [esi].RPINIT.bUnit,0 mov eax, edx ret I_Init endp ; ; Subroutine to "validate" an IDE unit as an ATAPI CD-ROM drive. ; in: ebx = IDEPARM ; ebx must be preserved! ; I_ValDV proc call StopDMA ;Stop previous DMA & select drive. mov edi,CStr('Device select timeout') call TestTO ;Await controller-ready (sets DX to ATA cmd port) jc I_Val7 ;If timeout, go post pointer & exit. ;--- check if BSY and DRQ are 0; bit 6 (device ready ) should be 1, ;--- but this is nothing to rely on... and al, BSY or DRQ ;check BSY+DRQ, both must be 0 jnz I_Val7 if IDENTPKT @dprintf <"ValDV: ready, base=%X, executing Identify Packet command",CR,LF>, [ebx].UPARM.wIDEBase mov al,0A1h ;Issue "Identify Packet Device" cmd. else @dprintf <"ValDV: ready, base=%X, executing Identify command",CR,LF>, [ebx].UPARM.wIDEBase mov al,0ECh ;Issue "Identify Device" cmd. endif out dx,al call TestTO ;Await controller-ready. mov edi, CStr('Identify Device error') ifdef _DEBUG pushfd lahf @dprintf <"ValDV: Identify Packet command returned, ax=%X",CR,LF>, ax popfd endif jc I_Val7 ;If timeout, go post pointer & exit. test al,ERR ;non-ATAPI devices will respond with ERR set jnz I_Val6 if 1 @dprintf <"ValDV, identify ok, al=%X, port=%X",CR,LF>, eax, [ebx].UPARM.wIDEBase else invoke printf, CStr("ValDV, identify ok, al=%X, port=%X",CR,LF), eax, [ebx].UPARM.wIDEBase endif sub edx,7 ;Point back to IDE data register. mov ecx,90 sub esp,ecx sub esp,ecx mov edi,esp rep insw mov ecx,256-90 ;skip the rest @@: in ax,dx loop @B mov edi,offset DrvName ;Point to drive "name" input buffer. mov cl,20 ;Read & swap words 27-46 into buffer. lea esi,[esp+27*2] ;(Manufacturer "name" of this drive). @@: lodsw xchg al,ah stosw loop @B mov esi,esp mov al,[esi+53*2] mov [UFlag],al ;Save UltraDMA "valid" flags. mov dx,[esi+88*2] mov [UMode],dh ;Save current UltraDMA "mode" value. ;"supported" UDMA "modes" is in DL mov ax,[esi+0*2] ;Get I.D. word 0, main device flags. if MWDMA mov ch,[esi+63*2+1] ;copy multiword flags to CH endif add esp,90*2 ;restore ESP and ax,0DF03h ;Mask off flags for an ATAPI CD-ROM. cmp ax,08500h ;Do device flags say "ATAPI CD-ROM"? je I_Val9 ;Yes, see about UltraDMA use. I_Val6: mov edi, CStr("No ATAPI CD-ROM") I_Val7: stc ;Set carry flag on (error!). I_Val8: ret I_Val9: @dprintf <"ATAPI CDROM detected",CR,LF> test @byte [ebx].UPARM.wDMABase,7 ;Will we be using UltraDMA? jnz I_Val8 ;No, go exit above. test @byte [UFlag], 4 ;Valid UltraDMA "mode" bits? jz noudma ;No, jump if SETMODE cmp [MaxUM],-1 ;/Mn switch used? jz nomopt movzx edx,dl ;supported UDMA modes in EDX now bsr eax, edx jz nomopt mov dl,[MaxUM] cmp dl,al ;supported mode < /Mn? jb @F mov dl,al @@: bts @word [UMode],dx mov ah,dl mov dx,[ebx].UPARM.wIDEBase ;Point to IDE "features" register. inc edx mov al,SETM ;Set mode-select subcode. out dx,al inc edx ;Point to IDE sector-count register. mov al,ah ;Set desired UltraDMA mode. or al,040h out dx,al add edx,5 ;Point to IDE command register. mov al,SETF ;Issue "set features" command. out dx,al call TestTO ;Await controller-ready. test al, ERR jz nomopt invoke printf, CStr('UltraDMA Set Mode failed',CR,LF) nomopt: endif mov edi,offset UModes mov al,[UMode] ;Get UltraDMA "mode" value. or al,al ;Can drive do mode 0 minimum? jnz I_Val11 ;Yes, set up UltraDMA mode 0. noudma: if MWDMA test [bFlags],FL_W ;handle multiword DMA devices? jz @F mov [MaxUM],-1 mov al,ch mov edi,offset MWModes or al,al ;is a MW-DMA mode set? jnz I_Val11 @@: endif I_Val10: or @byte [ebx].UPARM.wDMABase,1 ;Post drive "DMA disabled". ret ;Exit -- must use "PIO mode"! I_Val11: movzx eax,al bsr ecx, eax cmp cl,[MaxUM] jbe @F mov cl,[MaxUM] @@: mov ax,@word [ecx*2+edi] mov wDmaMode, ax clc ret I_ValDV endp DllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword mov eax,dwReason cmp eax, 1 jnz done ; .if (dwReason == 1) mov esi, dwRes test [esi].JLCOMM.wFlags, JLF_DRIVER jz failed movzx ecx,[esi].JLCOMM.wLdrCS shl ecx, 4 mov dwBase, ecx lea eax, [ecx+REQOFS] mov pRequest, eax mov eax,[esi].JLCOMM.lpCmdLine mov dwCmdLine, eax mov eax,[esi].JLCOMM.lpRequest mov dwRequest, eax mov esi, offset DevInt xor edx, edx @VMMCall Allocate_V86_Call_Back jc failed mov edi, dwBase mov [edi].DOSDRV.wAttr, 0C800h ;driver attributes mov [edi].DOSDRV.ofsStr, REQOFS+4 mov [edi].DOSDRV.ofsInt, REQOFS+4+SIZERMCODE mov [edi].DOSDRV.wRes1, 0 mov [edi].DOSDRV.bRes2, 0 mov [edi].DOSDRV.bUnits, 0 add edi, REQOFS+4 mov esi, offset rmcode mov ecx, SIZERMCODE+1 rep movsb stosd ;--- set EBP to the client pointer before calling I_Init push ebp @VMMCall Get_Cur_VM_Handle mov ebp,[ebx].cb_s.CB_Client_Pointer if SAVESTAT sub esp, sizeof Client_Reg_Struc mov edi, esp @VMMCall Save_Client_State endif call I_Init cmp ax,RPDON setz al if SAVESTAT movzx edi,al mov esi, esp @VMMCall Restore_Client_State add esp, sizeof Client_Reg_Struc mov eax, edi else movzx eax, al endif pop ebp ; .endif done: ret failed: xor eax, eax ret DllMain endp end DllMain ================================================ FILE: JLM/XCDROM32/XCDROM32.txt ================================================ 1. About XCDROM32 XCDROM32 is a JLM based on Jack R. Ellis' XCDROM Ultra-DMA CD-ROM driver. It supports PCI IDE controllers running in "legacy" or "native" mode. 2. Usage To load the driver, add the following line to your CONFIG.SYS: DEVICE=JLOAD.EXE XCDROM32.DLL [options] Options are: /32 use 32bit-IO in "PIO mode". This might not work with all devices, but if it does, it usually gives a speed boost for that mode. /AX disable "audio" functions. /D:name set device name. If this option is missing, the default device name is "XCDROM$$" /F "fast", uses DMA "scatter/gather" lists to allow transfers which cross a physical 64 kB address boundary. Might not work with all controllers. /L limits DMA to "low memory" below 640 kB. /Mn set/restrict UDMA mode to n (0 <= n <= 7). /Q quiet mode. /UX disables UltraDMA for all drives and uses "PIO mode" generally. /W handle (non-UDMA) drives which are capable of Multiword-DMA. 3. Details XCDROM32 has 3 modes of operation: 1. "direct" mode: the target address is used for DMA input. This usually is the fastest way and is used whenever possible. 2. "buffered" mode: the VDS DMA buffer is used for DMA input, then the content is copied to the target address. This mode is used if "direct mode" can't be used because target address is not aligned properly or - if option /F isn't set - because the target region crosses a 64 kB boundary. On modern CPUs the additional time needed for the memory copy should be negligible. 3. "PIO" mode: input is done without DMA. This mode should work with any IDE controller and CDROM/DVD. It is used if - /UX option is set. - the IDE controller doesn't support PCI Busmaster DMA. - the CD-ROM/DVD device is not in UDMA mode or /W option is set, but CD-ROM/DVD device has no valid Multiword-DMA mode set. - "buffered mode" cannot be used because VDS DMA buffer isn't accessible or is too small. 4. License XCDROM32 is released under the GNU GPL v2. See GNU_GPL.TXT for details. ================================================ FILE: JLM/XDMA32/History.txt ================================================ 10/2022: v1.5 - ensure that busmaster flag in PCI command register is set. - created .data segment with variables to separate them from code. - removed obsolete /P option. - fixed: under certain conditions, an IDE controllers might not have been detected. 08/2022: v1.4 - fixed: option /M was implemented in such a way that it became obligatory to set it to make the driver work. 07/26/2011: v1.3 - to determine the number of HDs in the system, an Int 13h, ah=08 is issued. BIOS variable 0040:0075 is ignored. 04/20/2011: v1.2 - bugfix: HDs attached to the "native" secondary channel were ignored. 12/28/2007: v1.1 - bugfix: HDs attached to the "legacy" secondary channel didn't work (wrong DMA ports). - options /B, /M, /W and /P added, /UF option changed to /F. - now max. 8 HDs are supported. - usage of Jemm's new export 'MoveMemory' reduces interrupt latency, and also makes this version incompatible with Jemm v5.68 and below. 12/03/2007: v1.0 - initial. Ported from XDMA v3.3. Added support for both "legacy" and "native" IDE controllers. ================================================ FILE: JLM/XDMA32/IDENTIFY.INC ================================================ ;--- data returned by Identify Device from ATA disks ;--- it's 256 * 16-bit words. IDENTIFY_DEVICE_DATA struct if 0 struct { USHORT Reserved1 : 1; USHORT Retired3 : 1; USHORT ResponseIncomplete : 1; USHORT Retired2 : 3; USHORT FixedDevice : 1; USHORT RemovableMedia : 1; USHORT Retired1 : 7; USHORT DeviceType : 1; } GeneralConfiguration; endif GeneralConfiguration dw ? ;0 NumCylinders dw ? ;1 SpecificConfiguration dw ? ;2 NumHeads dw ? ;3 Retired1 dw 2 dup (?); ;4-5 NumSectorsPerTrack dw ? ;6 VendorUnique1 dw 3 dup (?) ;7-9 SerialNumber db 20 dup (?) ;10-19 Retired dw 2 dup (?) ;20-21 Obsolete1 dw ? ;22 FirmwareRevision db 8 dup (?) ;23-26 ModelNumber db 40 dup (?) ;27-46 MaximumBlockTransfer db ? ;47 VendorUnique2 db ? if 0 struct { USHORT FeatureSupported : 1; USHORT Reserved : 15; } TrustedComputing; endif TrustedComputing dw ? ;48 if 0 struct { UCHAR CurrentLongPhysicalSectorAlignment : 2; UCHAR ReservedByte49 : 6; UCHAR DmaSupported : 1; UCHAR LbaSupported : 1; UCHAR IordyDisable : 1; UCHAR IordySupported : 1; UCHAR Reserved1 : 1; UCHAR StandybyTimerSupport : 1; UCHAR Reserved2 : 2; USHORT ReservedWord50; } Capabilities; endif Capabilities dw ?,? ;49-50 ObsoleteWords51 dw ?,?;51-52 if 0 ; bit 0: ??? ; bit 1: field 64 valid (advanced PIO modes) ; bit 2: field 88 valid (UDMA modes) USHORT TranslationFieldsValid : 3; USHORT Reserved3 : 5; USHORT FreeFallControlSensitivity : 8; endif TranslationFieldsValid db ? ;53 FreeFallControlSensitivity db ? NumberOfCurrentCylinders dw ? ;54 NumberOfCurrentHeads dw ? ;55 CurrentSectorsPerTrack dw ? ;56 CurrentSectorCapacity dd ? ;57-58 CurrentMultiSectorSetting db ? ;59 if 0 UCHAR MultiSectorSettingValid : 1; UCHAR ReservedByte59 : 3; UCHAR SanitizeFeatureSupported : 1; UCHAR CryptoScrambleExtCommandSupported : 1; UCHAR OverwriteExtCommandSupported : 1; UCHAR BlockEraseExtCommandSupported : 1; endif MultiSectorSettingValid equ 0 SupportedFlags db ? UserAddressableSectors dd ? ;60-61 ObsoleteWord62 dw ?;62 MultiWordDMASupport db ?;63 MultiWordDMAActive db ?; AdvancedPIOModes db ? ;64 ReservedByte64 db ?; MinimumMWXferCycleTime dw ?;65 RecommendedMWXferCycleTime dw ?;66 MinimumPIOCycleTime dw ?;67 MinimumPIOCycleTimeIORDY dw ?;68 if 0 struct { USHORT ZonedCapabilities : 2; USHORT NonVolatileWriteCache : 1; USHORT ExtendedUserAddressableSectorsSupported : 1; USHORT DeviceEncryptsAllUserData : 1; USHORT ReadZeroAfterTrimSupported : 1; USHORT Optional28BitCommandsSupported : 1; USHORT IEEE1667 : 1; USHORT DownloadMicrocodeDmaSupported : 1; USHORT SetMaxSetPasswordUnlockDmaSupported : 1; USHORT WriteBufferDmaSupported : 1; USHORT ReadBufferDmaSupported : 1; USHORT DeviceConfigIdentifySetDmaSupported : 1; USHORT LPSAERCSupported : 1; USHORT DeterministicReadAfterTrimSupported : 1; USHORT CFastSpecSupported : 1; } AdditionalSupported; endif ExtendedUserAddressableSectorsSupported equ 3 AdditionalSupported dw ?;69 ReservedWords70 dw 5 dup (?);70-74 if 0 USHORT QueueDepth : 5; USHORT ReservedWord75 : 11; endif QueueDepth dw ? ;75 if 0 struct { USHORT Reserved0 : 1; USHORT SataGen1 : 1; USHORT SataGen2 : 1; USHORT SataGen3 : 1; USHORT Reserved1 : 4; USHORT NCQ : 1; USHORT HIPM : 1; USHORT PhyEvents : 1; USHORT NcqUnload : 1; USHORT NcqPriority : 1; USHORT HostAutoPS : 1; USHORT DeviceAutoPS : 1; USHORT ReadLogDMA : 1; USHORT Reserved2 : 1; USHORT CurrentSpeed : 3; USHORT NcqStreaming : 1; USHORT NcqQueueMgmt : 1; USHORT NcqReceiveSend : 1; USHORT DEVSLPtoReducedPwrState : 1; USHORT Reserved3 : 8; } SerialAtaCapabilities; endif SerialAtaCapabilities dw ?,?;76-77 if 0 struct { USHORT Reserved0 : 1; USHORT NonZeroOffsets : 1; USHORT DmaSetupAutoActivate : 1; USHORT DIPM : 1; USHORT InOrderData : 1; USHORT HardwareFeatureControl : 1; USHORT SoftwareSettingsPreservation : 1; USHORT NCQAutosense : 1; USHORT DEVSLP : 1; USHORT HybridInformation : 1; USHORT Reserved1 : 6; } SerialAtaFeaturesSupported; endif SerialAtaFeaturesSupported dw ?;78 if 0 struct { USHORT Reserved0 : 1; USHORT NonZeroOffsets : 1; USHORT DmaSetupAutoActivate : 1; USHORT DIPM : 1; USHORT InOrderData : 1; USHORT HardwareFeatureControl : 1; USHORT SoftwareSettingsPreservation : 1; USHORT DeviceAutoPS : 1; USHORT DEVSLP : 1; USHORT HybridInformation : 1; USHORT Reserved1 : 6; } SerialAtaFeaturesEnabled; endif SerialAtaFeaturesEnabled dw ?;79 MajorRevision dw ?;80 MinorRevision dw ?;81 if 0 struct { USHORT SmartCommands : 1; USHORT SecurityMode : 1; USHORT RemovableMediaFeature : 1; USHORT PowerManagement : 1; USHORT Reserved1 : 1; USHORT WriteCache : 1; USHORT LookAhead : 1; USHORT ReleaseInterrupt : 1; USHORT ServiceInterrupt : 1; USHORT DeviceReset : 1; USHORT HostProtectedArea : 1; USHORT Obsolete1 : 1; USHORT WriteBuffer : 1; USHORT ReadBuffer : 1; USHORT Nop : 1; USHORT Obsolete2 : 1; USHORT DownloadMicrocode : 1; USHORT DmaQueued : 1; USHORT Cfa : 1; USHORT AdvancedPm : 1; USHORT Msn : 1; USHORT PowerUpInStandby : 1; USHORT ManualPowerUp : 1; USHORT Reserved2 : 1; USHORT SetMax : 1; USHORT Acoustics : 1; USHORT BigLba : 1; USHORT DeviceConfigOverlay : 1; USHORT FlushCache : 1; USHORT FlushCacheExt : 1; USHORT WordValid83 : 2; USHORT SmartErrorLog : 1; USHORT SmartSelfTest : 1; USHORT MediaSerialNumber : 1; USHORT MediaCardPassThrough : 1; USHORT StreamingFeature : 1; USHORT GpLogging : 1; USHORT WriteFua : 1; USHORT WriteQueuedFua : 1; USHORT WWN64Bit : 1; USHORT URGReadStream : 1; USHORT URGWriteStream : 1; USHORT ReservedForTechReport : 2; USHORT IdleWithUnloadFeature : 1; USHORT WordValid : 2; } CommandSetSupport; endif CommandSetSupport dw ?,?,?;82-84 if 0 struct { USHORT SmartCommands : 1; USHORT SecurityMode : 1; USHORT RemovableMediaFeature : 1; USHORT PowerManagement : 1; USHORT Reserved1 : 1; USHORT WriteCache : 1; USHORT LookAhead : 1; USHORT ReleaseInterrupt : 1; USHORT ServiceInterrupt : 1; USHORT DeviceReset : 1; USHORT HostProtectedArea : 1; USHORT Obsolete1 : 1; USHORT WriteBuffer : 1; USHORT ReadBuffer : 1; USHORT Nop : 1; USHORT Obsolete2 : 1; USHORT DownloadMicrocode : 1; USHORT DmaQueued : 1; USHORT Cfa : 1; USHORT AdvancedPm : 1; USHORT Msn : 1; USHORT PowerUpInStandby : 1; USHORT ManualPowerUp : 1; USHORT Reserved2 : 1; USHORT SetMax : 1; USHORT Acoustics : 1; USHORT BigLba : 1; USHORT DeviceConfigOverlay : 1; USHORT FlushCache : 1; USHORT FlushCacheExt : 1; USHORT Resrved3 : 1; USHORT Words119_120Valid : 1; USHORT SmartErrorLog : 1; USHORT SmartSelfTest : 1; USHORT MediaSerialNumber : 1; USHORT MediaCardPassThrough : 1; USHORT StreamingFeature : 1; USHORT GpLogging : 1; USHORT WriteFua : 1; USHORT WriteQueuedFua : 1; USHORT WWN64Bit : 1; USHORT URGReadStream : 1; USHORT URGWriteStream : 1; USHORT ReservedForTechReport : 2; USHORT IdleWithUnloadFeature : 1; USHORT Reserved4 : 2; } CommandSetActive; endif CommandSetActive dw ?,?,?; 85-87 UltraDMASupport db ?; 88 UltraDMAActive db ?; if 0 struct { USHORT TimeRequired : 15; USHORT ExtendedTimeReported : 1; } NormalSecurityEraseUnit; endif NormalSecurityEraseUnit dw ?;89 if 0 struct { USHORT TimeRequired : 15; USHORT ExtendedTimeReported : 1; } EnhancedSecurityEraseUnit; endif EnhancedSecurityEraseUnit dw ?;90 CurrentAPMLevel db ?; 91 ReservedWord91 db ? MasterPasswordID dw ?;92 HardwareResetResult dw ?;93 CurrentAcousticValue db ?;94 RecommendedAcousticValue db ? StreamMinRequestSize dw ? ;95 StreamingTransferTimeDMA dw ?;96 StreamingAccessLatencyDMAPIO dw ?;97 StreamingPerfGranularity dd ?;98-99 Max48BitLBA dd ?,? ;100-103 StreamingTransferTime dw ?;104 DsmCap dw ?;105 if 0 struct { USHORT LogicalSectorsPerPhysicalSector : 4; USHORT Reserved0 : 8; USHORT LogicalSectorLongerThan256Words : 1; USHORT MultipleLogicalSectorsPerPhysicalSector : 1; USHORT Reserved1 : 2; } PhysicalLogicalSectorSize; endif PhysicalLogicalSectorSize dw ? ;106 InterSeekDelay dw ?;107 WorldWideName dw 4 dup (?);108-111 ReservedForWorldWideName128 dw 4 dup (?);112-115 ReservedForTlcTechnicalReport dw ?;116 WordsPerLogicalSector dw 2 dup (?);117-118 if 0 struct { USHORT ReservedForDrqTechnicalReport : 1; USHORT WriteReadVerify : 1; USHORT WriteUncorrectableExt : 1; USHORT ReadWriteLogDmaExt : 1; USHORT DownloadMicrocodeMode3 : 1; USHORT FreefallControl : 1; USHORT SenseDataReporting : 1; USHORT ExtendedPowerConditions : 1; USHORT Reserved0 : 6; USHORT WordValid : 2; } CommandSetSupportExt; endif CommandSetSupportExt dw ?;119 if 0 struct { USHORT ReservedForDrqTechnicalReport : 1; USHORT WriteReadVerify : 1; USHORT WriteUncorrectableExt : 1; USHORT ReadWriteLogDmaExt : 1; USHORT DownloadMicrocodeMode3 : 1; USHORT FreefallControl : 1; USHORT SenseDataReporting : 1; USHORT ExtendedPowerConditions : 1; USHORT Reserved0 : 6; USHORT Reserved1 : 2; } CommandSetActiveExt; endif CommandSetActiveExt dw ?;120 ReservedForExpandedSupportandActive dw 6 dup (?);121-126 if 0 USHORT MsnSupport : 2; USHORT ReservedWord127 : 14; endif MsnSupport dw ?;127 if 0 struct { USHORT SecuritySupported : 1; USHORT SecurityEnabled : 1; USHORT SecurityLocked : 1; USHORT SecurityFrozen : 1; USHORT SecurityCountExpired : 1; USHORT EnhancedSecurityEraseSupported : 1; USHORT Reserved0 : 2; USHORT SecurityLevel : 1; USHORT Reserved1 : 7; } SecurityStatus; endif SecurityStatus dw ?;128 ReservedWord129 dw 31 dup (?);129-159 if 0 struct { USHORT MaximumCurrentInMA : 12; USHORT CfaPowerMode1Disabled : 1; USHORT CfaPowerMode1Required : 1; USHORT Reserved0 : 1; USHORT Word160Supported : 1; } CfaPowerMode1; endif CfaPowerMode1 dw ?;160 ReservedForCfaWord161 dw 7 dup (?);161-167 if 0 USHORT NominalFormFactor : 4; USHORT ReservedWord168 : 12; endif NominalFormFactor dw ?;168 if 0 struct { USHORT SupportsTrim : 1; USHORT Reserved0 : 15; } DataSetManagementFeature; endif DataSetManagementFeature dw ?;169 AdditionalProductID dw 4 dup (?);170-173 ReservedForCfaWord174 dw 2 dup (?);174-175 CurrentMediaSerialNumber dw 30 dup (?);176-205 if 0 struct { USHORT Supported : 1; USHORT Reserved0 : 1; USHORT WriteSameSuported : 1; USHORT ErrorRecoveryControlSupported : 1; USHORT FeatureControlSuported : 1; USHORT DataTablesSuported : 1; USHORT Reserved1 : 6; USHORT VendorSpecific : 4; } SCTCommandTransport; endif SCTCommandTransport dw ?;206 ReservedWord207 dw 2 dup (?);207-208 if 0 struct { USHORT AlignmentOfLogicalWithinPhysical : 14; USHORT Word209Supported : 1; USHORT Reserved0 : 1; } BlockAlignment; endif BlockAlignment dw ?;209 WriteReadVerifySectorCountMode3Only dw 2 dup (?);210-211 WriteReadVerifySectorCountMode2Only dw 2 dup (?);212-213 if 0 struct { USHORT NVCachePowerModeEnabled : 1; USHORT Reserved0 : 3; USHORT NVCacheFeatureSetEnabled : 1; USHORT Reserved1 : 3; USHORT NVCachePowerModeVersion : 4; USHORT NVCacheFeatureSetVersion : 4; } NVCacheCapabilities; endif NVCacheCapabilities dw ?;214 NVCacheSizeLSW dw ?;215 NVCacheSizeMSW dw ?;216 NominalMediaRotationRate dw ?;217 ReservedWord218 dw ?;218 if 0 struct { UCHAR NVCacheEstimatedTimeToSpinUpInSeconds; UCHAR Reserved; } NVCacheOptions; endif NVCacheEstimatedTimeToSpinUpInSeconds db ? ;219 db ? WriteReadVerifySectorCountMode db ? ;220 db ? ReservedWord221 dw ?;221 if 0 struct { USHORT MajorVersion : 12; USHORT TransportType : 4; } TransportMajorVersion; endif TransportMajorVersion dw ?;222 TransportMinorVersion dw ?;223 ReservedWord224 dw 6 dup (?);224-229 ExtendedNumberOfUserAddressableSectors dq ?;230-233 MinBlocksPerDownloadMicrocodeMode03 dw ?;234 MaxBlocksPerDownloadMicrocodeMode03 dw ?;235 ReservedWord236 dw 19 dup (?);236-254 Signature db ?;255 CheckSum db ? IDENTIFY_DEVICE_DATA ends ================================================ FILE: JLM/XDMA32/MAKE.BAT ================================================ @echo off rem Assemble and link file for the XDMA32 JLM. rem jwasm: option -zzs is needed for OW WLink v1.8! jwasm -coff -nologo -Fl -Sg -I..\..\Include -Fo=XDMA32.obj XDMA32.ASM jwlink format win pe hx dll ru native file XDMA32.obj name XDMA32.DLL op q,map ================================================ FILE: JLM/XDMA32/MAKEM.BAT ================================================ @echo off rem using MS tools ml -c -coff -Fl -D_DEBUG -I..\..\Include XDMA32.ASM link /NOLOGO /SUBSYSTEM:native /DLL XDMA32.obj /OUT:XDMA32.DLL /OPT:NOWIN98 /MAP ================================================ FILE: JLM/XDMA32/XDMA32.ASM ================================================ ; ; XDMA32.ASM - a JLM driver for UltraDMA hard-disks ; based on XDMA v3.3 by Jack R. Ellis ; released under the GNU GPL license v2 (see GNU_GPL.TXT for details) ; ; The source is to be assembled with JWasm or Masm v6+! ; ; XDMA32 switch options are as follows: ; ; /B Always use XMS buffer. Without this option, the XMS buffer is ; used only if the user buffer isn't dword-aligned. ; /F Enables "Fast UltraDMA". Data input requests that cross an ; DMA "64K boundary" are executed using a 2-element DMA ; "scatter/gather" list, one for data up to the boundary, and one ; for data beyond it. ; /L Limits DMA to "low memory" below 640K. /L is REQUIRED to use ; UMBPCI or any similar driver whose upper-memory areas do not ; allow DMA. /L causes I-O requests past 640K to go through ; the driver's buffer. ; /Mn set/restrict UDMA mode. ; /Q Quiet mode. ; /W also handle non-UDMA disks which can do multiword-DMA. ; ; On exit from successful I-O requests, the AH-register is zero and the ; carry flag is reset. If an error occurs, the carry flag is SET, and ; the AH-register has one of the following codes: ; ; Code 08h - DMA timed out. ; 0Fh - DMA error. ; 20h - Controller busy before I-O. ; 21h - Controller busy after I-O. ; AAh - Disk not ready before I-O. ; ABh - Disk not ready after I-O. ; CCh - Disk FAULT before I-O. ; CDh - Disk FAULT after I-O. ; E0h - Hard error at I-O end. ; FFh - XMS memory error. .386 .model flat option casemap:none option dotname option proc:private include jlm.inc ifndef ?PCISCAN ?PCISCAN equ 1 endif NUMDSK equ 8 ;max HDs supported NUMCTRL equ 4 ;max IDE controllers supported SAVESTAT equ 1 ;save/restore client state on init SETBM equ 1 ;set busmaster flag in PCI status SCATTER equ 1 ;support /F option to use DMA scatter/gather lists ;if a 64 kb boundary is crossed. IRQWND equ 1 ;allow interrupts during memory copy LBACHECK equ 1 ;check if disk supports LBA MWDMA equ 1 ;support /W option to accept multi-word DMA SETMODE equ 1 ;support /Mn option to set UDMA mode NOHIGHLBA equ 1 ;avoid 4 OUTs for LBA28 BUFONLY equ 1 ;support /B option HDNUM equ 0 ;1=rely on value at 0040:0075 ; ; General Program Equations. ; VERSION equ <' V1.5, 15-10-2022'> RDYTO equ 08h ;389-msec minimum I-O timeout. BIOSTMR equ 046Ch ;BIOS "tick" timer address. HDISKS equ 0475h ;BIOS hard-disk count address. HDIOFS equ 048Eh ;BIOS flag for HD IRQ CR equ 00Dh ;ASCII carriage-return. LF equ 00Ah ;ASCII line-feed. @byte equ @word equ @dword equ ; ; Driver Return Codes. ; DMATIMO equ 0E8h ;DMA timeout code, 008h at exit. DMAERR equ 0EFh ;DMA error code, 00Fh at exit. CTLRERR equ 000h ;Ctlr. busy code, 020h/021h at exit. DISKERR equ 08Ah ;Disk-busy code, 0AAh/0ABh at exit. FAULTED equ 0ACh ;Disk FAULT code, 0CCh/0CDh at exit. HARDERR equ 0BFh ;Hard-error code, 0E0H at exit. XMSERR equ 0FFh ;XMS memory-error code. ; ; IDE Controller Register Definitions. ; CDATA equ 0h ;offset Data port. CSECCT equ CDATA+2 ;offset I-O sector count. CDSEL equ CDATA+6 ;offset Disk-select and upper LBA. CCMD equ CDATA+7 ;offset Command register. CSTAT equ CDATA+7 ;Primary status register. ;CSTAT2 equ CDATA+206h ;Alternate status register. (legacy only) ; ; Controller Status and Command Definitions. ; BSY equ 080h ;IDE controller is busy. RDY equ 040h ;disk device is "ready". FLT equ 020h ;IDE disk has a "fault". DRQ equ 008h ;IDE data request. ERR equ 001h ;IDE general error flag. DRCMD equ 0C8h ;DMA read command (write is 0CAh, LBA48 commands are 025h/035h). SETFEAT equ 0EFh ;Set Features command. SETM equ 003h ;Set Mode subcommand. DMI equ 004h ;DMA interrupt occured. DME equ 002h ;DMA error occurred. ; ; LBA "Disk Address Packet" Layout. ; DAP struc DapPL db ? ;Packet length. db ? ;(Reserved). DapSC db ? ;I-O sector count. db ? ;(Reserved). DapBuf dd ? ;I-O buffer address (segment:offset). LBA01 dw ? ;48-bit logical block address (LBA). LBA25 dd ? LBA67 dw ? ; it's actually a QWORD DAP ends ; ; DPTE "Device Parameter Table Extension" Layout ; that's the structure the lpCfg member in EDD20 points to. ; DPTE struc wIDEBase dw ? ;IDE port base wIDEAlt dw ? ;alternate control port bFlags db ? ;drive flags (bit 4=1 -> drive is slave) db ? ;proprietary info bIRQ db ? ;IRQ for drive ;--- there's some more fields... DPTE ends ;--- structure used by Int 13h, ah=48h EDD10 struc cbSize dw ? ;+0 size (26 for v1.x, 30 for v2.x, 66 for v3.0) flags dw ? ;+2 see below dwCyls dd ? ;+4 number of physical cylinders on drive dwHeads dd ? ;+8 number of physical heads on drive dwSecs dd ? ;+12 number of physical sectors per track numSecs dq ? ;+16 total number of sectors on drive secSize dw ? ;+24 bytes per sector EDD10 ends EDD20 struc EDD10 <> lpCfg dd ? ;+26 EDD configuration parameters ( or -1 if invalid ) EDD20 ends EDD30 struct EDD20 <> wSig dw ? ;+1E bLength db ? ;+20 length of path db 3 dup (?) ; reserved szBus db 4 dup (?) ;+24 szIFType db 8 dup (?) ;+28 qwIFPath db 8 dup (?) ;+30 szDevPath db 8 dup (?) ;+38 db ? ;+40 bChecksum db ? ;+41 db 8 dup (?) ;+42 EDD30 ends ;--- bits for flags above EDDF_DMA_BOUNDARY equ 01h ;DMA boundary errors handled transparently EDDF_CHS_VALUD equ 02h ;cylinder/head/sectors-per-track information is valid EDDF_REMOVABLE equ 04h ;removable drive EDDF_WRITE_VERIFY equ 08h ;write with verify supported EDDF_CHANGE_LINE equ 10h ;drive has change-line support (required if drive >= 80h is removable) EDDF_LOCKABLE equ 20h ;drive can be locked (required if drive >= 80h is removable) EDDF_CHS_IS_MAX equ 40h ;CHS information set to maximum supported values, not current media ;--- 15-7 reserved (0) ;Note: bits 4-6 are only valid if bit 2 is set ; ; DOS "Request Packet" Layout. ; RP struc RPHLen db ? ;Header byte count. RPSubU db ? ;Subunit number. RPOp db ? ;Opcode. RPStat dw ? ;Status word. db 8 dup (?);(Unused by us). RPUnit db ? ;Number of units found. RPSize dd ? ;Resident driver size. RPCL dd ? ;Command-line data pointer. RP ends RPERR equ 08003h ;Packet "error" flags. RPDONE equ 00100h ;Packet "done" flag. CStr macro text:VARARG local sym .code .text$01 sym db text,0 .code exitm endm @dprintf macro text,args:vararg ifdef _DEBUG ifnb invoke printf, CStr(text), args else invoke printf, CStr(text) endif endif endm .data dwBase dd 0 ;linear address driver base ( conv. memory ) dwCmdLine dd 0 dwBufferLin dd 0 ;linear address XMS buffer dwBufferPhy dd 0 ;physical address XMS buffer (64 kb aligned) PrvI13 dd 0 ;old real-mode int 13 vector XMSEntry dd 0 ;XMS "entry" address, set by Init XMSHdl dd 0 ;XMS memory "handle", set by Init wBaseSeg dd 0 ;conventional memory segment ( supplied by loader) PRDAd dd 0 ;PRD 32-bit command addr. (Init set). ;--- values for bFlags FL_Q equ 1 ;/Q option set if SCATTER FL_F equ 2 ;/F option endif if MWDMA FL_W equ 4 ;/W option endif FLB_BUSY equ 7 ;bit 7 is "busy" flag bFlags db 0 ;various flags (cmdline params, busy, ...) if SETMODE MaxUM db -1 ;UDMA "mode" limit set by /Mn option. endif BiosHD db 0 ;(Number of BIOS disks, during Init). HDCount db 0 ;(BIOS hard-disk count, during Init). align 4 ;--- the following is written to the IDE port ;--- SecCt2/LBA2447 and SecCt/LBA0023 must be consecutive SecCt2 db 0 ;IDE "upper" sector count. LBA2447 db 0,0,0 ;IDE "upper" LBA48 bits 24-47. SecCt db 0 ;IDE "lower" sector count. LBA0023 db 0,0,0 ;IDE "lower" LBA bits 0-23. DSIOCmd label word DSCmd db 0 ;IDE disk-select, LBA28 bits 24-27. IOCmd db 0 ;IDE command byte. HDUnit db 0 ;Current BIOS unit number. align 4 VDSLn dd 0 ;buffer length VDSOf dd 0 ;linear address of buffer ;--- values for DMA controller - must be consecutive IOAdr dd 0 ;DMA physical address IOLen dd 80000000h ;DMA byte count and "end" flag. if SCATTER IOAdr2 dd 0 IOLen2 dd 80000000h endif IDEAd dw 1F0h, 1F0h, 170h, 170h if NUMDSK gt 4 dw NUMDSK-4 dup (-1);IDE port base endif IDEAlt dw 3F6h, 3F6h, 376h, 376h if NUMDSK gt 4 dw NUMDSK-4 dup (-1);IDE port base endif DMAAd dw NUMDSK dup (-1) ;DMA port base Units db NUMDSK dup (-1) ;IDE "active units" table (Init set). SelCmd db 0E0h,0F0h,0E0h,0F0h if NUMDSK gt 4 db NUMDSK-4 dup (0) endif CHSSec db NUMDSK dup (0) ;CHS sectors/head table (Init set). CHSHd db NUMDSK dup (0) ;CHS heads/cylinder table (Init set). ;--- stored IDE controllers (bus/dev/fn) by PCI scan controllers dw NUMCTRL dup (-1) ;--- flags for PCI IDE controller programming interface ; 7 bus mastering (read-only) ; 6-4 reserved (read-only) ; 3 secondary IDE mode bit is writable (read-only) ; 2 secondary IDE mode (0 = legacy, 1 = native) ; 1 primary IDE mode bit is writable (read-only) ; 0 primary IDE mode (0 = legacy, 1 = native) ; ; Initialization Tables And Variables. ; if MWDMA MWModes label word dw 004 ;Mode 0 dw 013 ;Mode 1 dw 016 ;Mode 2 endif UModes label word dw 016 ;Mode 0, ATA-16 UDMA mode table dw 025 ;Mode 1, ATA-25 dw 033 ;Mode 2, ATA-33 dw 044 ;Mode 3, ATA-44 (Unusual but possible). dw 066 ;Mode 4, ATA-66 dw 100 ;Mode 5, ATA-100 dw 133 ;Mode 6, ATA-133 dw 166 ;Mode 7, ATA-166 include identify.inc ;SIZENAME equ 40 SIZENAME equ sizeof IDENTIFY_DEVICE_DATA.ModelNumber DNMsg db ', ' DName db 26*2 dup (0) ;the model number is actually 20 words only .code include printf.inc ; ; Driver Entry Routine. For CHS requests, the registers contain: ; ; AH Request code. We handle 002h read and 003h write. ; AL I-O sector count. ; CH Lower 8 bits of starting cylinder. ; CL Starting sector and upper 2 bits of cylinder. ; DH Starting head. ; DL BIOS unit number. We handle 080h and up (hard-disks). ; ES:BX I-O buffer address. ; ; For LBA requests, the registers contain: ; ; AH Request code. We handle 042h read and 043h write. ; DL BIOS unit number. We handle 080h and up (hard-disks). ; DS:SI Pointer to Device Address Packet ("DAP"), described above. ; ;--- for JLMs all registers are in a client structure and EBP will ;--- point to it. Entry: ifdef _DEBUG cmp @byte [ebp].Client_Reg_Struc.Client_EAX+1, 42h jz @F cmp @byte [ebp].Client_Reg_Struc.Client_EAX+1, 43h jnz stdpr1 @@: movzx esi,@word [ebp].Client_Reg_Struc.Client_DS movzx ecx,@word [ebp].Client_Reg_Struc.Client_ESI shl esi, 4 add esi, ecx @dprintf <"Entry(I13), ax=%X, dx=%X, DAP=%X, buffer=%X, sect=%X",13,10>,\ word ptr [ebp].Client_Reg_Struc.Client_EAX,\ word ptr [ebp].Client_Reg_Struc.Client_EDX, esi, [esi].DAP.DapBuf, dword ptr [esi].DAP.LBA01 jmp stdpr2 stdpr1: @dprintf <"Entry(I13), ax=%X, dx=%X, buffer=%X:%X",13,10>,\ word ptr [ebp].Client_Reg_Struc.Client_EAX,\ word ptr [ebp].Client_Reg_Struc.Client_EDX,\ word ptr [ebp].Client_Reg_Struc.Client_ES,\ word ptr [ebp].Client_Reg_Struc.Client_EBX stdpr2: endif mov edx,[ebp].Client_Reg_Struc.Client_EDX mov edi,0 ;Reset active-units table index. @LastU equ $-4 ;(Last-unit index, set by Init). NextU: dec edi ;Any more active units to check? js QuickX ;No, request NOT for us -- exit quick! cmp dl,[edi+Units] ;Does request unit match our table? jne NextU ;No, see if more table entries remain. bts @dword [bFlags],FLB_BUSY;set "busy" flag jc IsBusy ;exit with error AH=01 if driver is busy ; @dprintf <"Entry(I13), IDE/Alt/DMA=%X/%X/%X",13,10>, [edi*2+IDEAd], [edi*2+IDEAlt], [edi*2+DMAAd] mov eax,[ebp].Client_Reg_Struc.Client_EAX mov dl,0BEh ;Mask out LBA and write request bits. and dl,ah ; 02/03/42/43 -> 2 cmp dl,002h ;Is this a CHS or LBA read or write? jne Pass ;No, let BIOS handle this request. shl ah,1 ;02/03 -> 04/06 42/43 -> 84/86 jns ValCHS ;No, handle CHS movzx ecx,@word [ebp].Client_Reg_Struc.Client_DS movzx esi,@word [ebp].Client_Reg_Struc.Client_ESI shl ecx, 4 add esi, ecx movzx ebx, @word [esi].DAP.DapBuf+2 movzx edx, @word [esi].DAP.DapBuf+0 shl ebx, 4 add ebx, edx mov edx,[esi.DAP.LBA25] ;Get DAP LBA bits 16-47 mov al,[esi.DAP.DapSC] ;Get "DAP" I-O sector count. cmp [esi.DAP.DapBuf],-1 ;64-bit buffer address? mov si,[esi.DAP.LBA01] ;(Get "DAP" LBA bits 0-15). jne ValSC ;No, go validate "DAP" sector count. Pass: btr @dword [bFlags],FLB_BUSY ;Reset driver "busy" flag QuickX: mov eax,[PrvI13] mov @word [ebp].Client_Reg_Struc.Client_EIP, ax shr eax, 16 mov @word [ebp].Client_Reg_Struc.Client_CS, ax ret IsBusy: lahf ;store Carry flag in AH mov al,1 ;and error code in AL jmp GoOut align 4 ValCHS: mov ecx,[ebp].Client_Reg_Struc.Client_ECX xchg eax,ecx ;CHS -- save request code and sector count in CX mov si,0003Fh ;Set SI-reg. to starting sector. and esi,eax dec esi shr al,6 ;Set AX-reg. to starting cylinder. xchg al,ah xchg eax,edx ;ah=head, dx=start cyl mov al,[edi+CHSSec];Get disk CHS sectors/head value. or al,al ;Were disk CHS values legitimate? jz Pass ;No? Let BIOS do this request! push eax ;Save CHS sectors/head value. mul ah ;Convert head to sectors. add si,ax ;Add result to starting sector. pop eax ;Reload CHS sectors/head value. mul [edi+CHSHd] ;Convert cylinder to sectors. mul dx add si,ax ;Add to head/sector value. adc edx,0 movzx edx, dx ;Reset upper LBA address bits. xchg eax,ecx ;restore request code and sector count in AX movzx ecx,@word [ebp].Client_Reg_Struc.Client_ES movzx ebx,@word [ebp].Client_Reg_Struc.Client_EBX shl ecx, 4 add ebx, ecx ;set linear buffer address in EBX ;--- here LBA bits in EDX:SI, linear address buffer in EBX ;--- sector count in AL, cmd in AH ValSC: dec al ;Is sector count zero or over 128? js Pass ;Yes? Let BIOS handle this "No-No!". inc eax ;Restore sector count cld ;Ensure FORWARD "string" commands. mov @word [LBA0023],si ;Save LBA bits 0-15 and 24-47. mov [LBA0023+2],dl ;Save LBA bits 16-23, to free DL-reg. mov [LBA2447],dh mov esi, edx shr esi, 16 mov @word [LBA2447+1],si mov [VDSOf],ebx ;Save user buffer linear address. or @byte [ebp].Client_Reg_Struc.Client_EFlags+1,2 ;set client IF shr dx,12 ;Shift out LBA bits 16-27. or si,dx ;Anything in LBA bits 28-47? jnz LBA48 ;Yes, use LBA48 read/write command. xchg dh,[LBA2447] ;LBA28 -- reload & reset bits 24-27. or ah,(DRCMD+1) ;Get LBA28 read/write command + 5. jmp GetAdr ;Go get IDE and LBA address bytes. align 4 LBA48: shl ah,3 ;LBA48 -- translate command 04h/84h -> 20h, 06h/86h -> 30h. GetAdr: mov dl,[edi+SelCmd] or dl,dh ;"Or" in LBA28 bits 24-27 (if any). mov dh,005h ;Get final IDE command byte. xor dh,ah ;(LBA28 = C8h/CAh, LBA48 = 25h/35h). mov [DSIOCmd],dx ;Set IDE command bytes (DSCmd + IOCmd). mov [SecCt],al ;Set I-O sector count. movzx eax,al shl eax,9 mov [VDSLn],eax ;Set buffer lengths. mov ecx,eax bts eax,31 ;Set DMA list "end" flag. mov [IOLen],eax if BUFONLY @BufPatch: endif test bl,3h ;Is user I-O buffer 32-bit aligned? jnz BufIO ;No, use buffered I-O routines below. mov esi,ebx ;get user buffer into ESI if SCATTER test [bFlags],FL_F ;if /F not set, check 64 kB boundary setz dl else mov dl,1 ;check for 64 kB boundary crossing endif VxDCall VDMAD_Lock_DMA_Region ; lock region esi, size ecx, dl[0] check 64 kb crossing jc BufIO ;Error -- do buffered I-O. mov [IOAdr], edx ; @dprintf <"Entry(I13), lock ok, addr=%X",13,10>, edx cmp @word [IOAdr+2],-1 ;DMA I-O above our limit? @DMALmt equ $-1 ;(If 640K limit, ffff is patched to 9fff by Init). ja BufIO ;Yes, use buffered I-O routines below. if SCATTER test [bFlags],FL_F ;/F option set? jz @F mov eax, edx mov ecx,[VDSLn] ;Get lower ending DMA address. dec ecx ;(IOLen - 1 + IOAdr). add ax,cx ;Would input cross a 64K boundary? jnc @F ;No, set DMA flag & do transfer. inc ax ;Get bytes above 64K boundary. cmp ax,64 ;Is this at least 64 bytes? jb BufIO ;No, use buffer inc cx ;Get bytes below 64K boundary. sub cx,ax cmp cx,64 ;Is this at least 64 bytes? jb BufIO ;No, use buffer mov @word [IOLen2],ax ;Set 2nd command-list byte count. movzx eax,cx ;Set 1st command-list byte count. mov [IOLen],eax add eax,[IOAdr] ;Set 2nd command-list address. mov [IOAdr2],eax @@: endif call DoDMA ;Do direct DMA I-O with user's buffer. Done: lahf btr @dword [bFlags],FLB_BUSY ;Reset driver "busy" flag GoOut: mov @byte [ebp].Client_Reg_Struc.Client_EAX+1, al ;Set error code in exiting AH-reg. push eax @VMMCall Simulate_Iret pop eax and @byte [ebp].Client_Reg_Struc.Client_EFlags,not 1 and ah,1 or @byte [ebp].Client_Reg_Struc.Client_EFlags,ah ret align 4 BufIO: ; @dprintf <"Entry(I13), BufIO",13,10> test [IOCmd],012h ;Is this a write request? jnz BufOut ;Yes, use output routine if 0;def _DEBUG push edi mov edi, dwBufferLin mov ecx, VDSLn shr ecx, 2 mov eax, 0f6f6f6f6h rep stosd pop edi endif ;--- buffered read: read into DMA buffer, then copy to conv. memory call BufDMA ;Input all data to driver XMS buffer. jc Done ;If error, post return code & exit! mov ecx, [VDSLn] mov edi, [VDSOf] mov esi, [dwBufferLin] if IRQWND @VMMCall MoveMemory else shr ecx, 2 rep movsd endif clc jmp Done ;Done -- post any return code & exit. align 4 ; ;--- buffered write: copy conv. memory to DMA buffer, then write ; BufOut: push edi ;dont destroy EDI! mov ecx, [VDSLn] mov esi, [VDSOf] mov edi, [dwBufferLin] if IRQWND @VMMCall MoveMemory else shr ecx, 2 rep movsd endif pop edi call BufDMA ;Output all data from XMS buffer. jmp Done ;Done -- post any return code & exit. _ret: ret align 4 ; ; Subroutine to execute read and write commands. ; EDI=drive ; out: AL=errorcode (00=no error) ; NC = no error ; BufDMA: mov eax, [dwBufferPhy] mov [IOAdr],eax ;Buffered -- set physical buffer addr. DoDMA: mov dx,[edi*2+DMAAd] ;Ensure any previous DMA is stopped! in al,dx ;(On some older chipsets, if DMA is and al,0F6h ; running, reading an IDE register out dx,al ; causes the chipset to "HANG"!!). ; mov al,[edi+SelCmd] ;Select our desired disk. mov al,[DSCmd] mov dx,[edi*2+IDEAd] add edx,CDSEL out dx,al mov cx, (RDYTO shl 8) or FLT ;Get timeout & "fault" mask. mov esi,BIOSTMR add ch,[esi] ;Set timeout limit in CH-reg. call ChkRdy ;Await controller- and disk-ready. jc _ret ;If any errors, exit! ; mov ds:[HDIOFS],al ;AL is 0. reset BIOS flag for HD IRQ mov byte ptr ds:[HDIOFS],0 mov dx,[edi*2+DMAAd] ;Reset DMA commands and set DMA mode. test [IOCmd],012h ;Is this a write request? jnz @F ;Yes, reset DMA command register. in al, dx or al, 8 ;set "DMA read" command bit. out dx, al @@: add edx, 2 ;Point to DMA status register. in al, dx ;Reset DMA status register. or al, 6 ;(Done this way so we do NOT alter out dx, al ; the "DMA capable" status flags!). add edx, 2 ;Set PRD pointer to our DMA address. mov eax, [PRDAd] out dx, eax mov eax, dword ptr [SecCt2] mov dx,[edi*2+IDEAd] add edx, 2 if NOHIGHLBA and eax, eax jz skip4 endif mov cl,4 ;(1st 4 overlayed by 2nd 4 if LBA48!). @@: out dx, al inc edx shr eax, 8 dec cl jnz @B sub edx, 4 skip4: mov eax, dword ptr [SecCt] mov cl,4 @@: out dx, al inc edx shr eax, 8 dec cl jnz @B mov al,[IOCmd] inc edx out dx, al if 0 ;--- the DRQ status bit is NOT necessarily set ;--- for DMA transfers, so this code should not run. mov dx,[edi*2+IDEAlt] ; @dprintf <"DoDMA waiting for DRQ=1, esi=%X, dx=%X",13,10>, esi, dx jmp @F waitdrq: @VMMCall Yield @@: cmp ch,[esi] ;Too long without 1st data-request? je DMAEnd ;Yes? Return carry and DMA timeout! in al,dx ;Read IDE alternate status. and al,DRQ ;Has 1st data-request arrived? jz waitdrq ;No, loop back and check again. endif mov dx, [edi*2+DMAAd] in al, dx ;Set DMA Start/Stop bit (starts DMA). or al, 1 out dx, al inc edx ;Point to DMA status register. inc edx @@: @VMMCall Yield in al,dx ;Read DMA controller status. and al,DMI+DME ;DMA interrupt or DMA error? jnz @F ;Yes, halt DMA and check results. cmp ch,[esi] ;Has our DMA transfer timed out? je @F ; then exit loop. test @byte ds:[HDIOFS],80h ; interrupt occured? jz @B mov al,DMI @@: dec edx ;Point back to DMA command register. dec edx push eax ;Save ending DMA status. in al,dx ;Reset DMA Start/Stop bit. and al,0FEh out dx,al pop eax ;Reload ending DMA status. cmp al,DMI ;Did DMA end with only an interrupt? jne ErrDMA ;No? Go see what went wrong. inc edx ;Reread DMA controller status. inc edx in al,dx test al,DME ;Any "late" DMA error after DMA end? jnz DMAEnd ;Yes? Return carry and DMA error! inc cl ;Check "fault" and hard-error at end. ;--- EDI=drive,ESI=BIOS timer counter, CH=max ChkRdy: mov dx,[edi*2+IDEAd] ;Read IDE primary status. add edx,CSTAT in al,dx test al,BSY+RDY ;Controller or disk still busy? jg ChkErr ;No, go check for "fault" or error. if 1 @VMMCall Yield ;yield CPU endif cmp ch,[esi] ;Too long without becoming ready? jne ChkRdy ;No, loop back and check again. test al,BSY ;BAAAD News! Did controller go ready? mov ax,(256*CTLRERR)+DISKERR ;(Get not-ready error codes). jmp WhichE ;Go see which error code to return. ChkErr: and al,cl ;Disk "fault" or hard-error? jz ChkExit ;No, all is well -- go exit below. test al,FLT ;BAAAD News! Is the disk "faulted"? mov ax,(256*FAULTED)+HARDERR ;(Get hardware error codes). WhichE: jz EndErr ;If "zero", use AL-reg. return code. mov al,ah ;Use AH-reg. return code of this pair. EndErr: add al,cl ;Add 1 if error occurred at I-O end. stc ;Set carry flag to denote "error"! ChkExit: ret ErrDMA: test al,DME ;BAAAD News! Did DMA end with error? DMAEnd: mov ax,(256*DMAERR)+DMATIMO ;(Get DMA error codes). jmp WhichE ;Go see which error code to return. ;--- end of "resident" part ;--- (for a JLM, this is irrelevant) ; wait for IDE controller to become "ready" ; out: C if error occured ;--- used for cmds EC (identify), EF (set features) ;--- just the BSY+ERR flags are checked, "device ready" and DRQ are ignored! WaitRdy proc mov esi,BIOSTMR ;Point to low-memory BIOS timer. mov cl,RDYTO ;Set I-O timeout limit in CL-reg. add cl,[esi] @@: @VMMCall Yield cmp cl,[esi] ;Has our command timed out? je Error ;Yes, set CPU carry flag & exit. in al,dx ;Get IDE controller status. test al,BSY ;Controller or disk still busy? jnz @B ;Yes, loop back and check again. test al,ERR ;Did command cause any errors? jz Exit ;No, exit Error: stc ;Set carry flag (error!) and exit. Exit: ret WaitRdy endp ; ; Subroutine to "validate" an UltraDMA hard-disk. ; EDI=drive ; checks for ATA device and whether LBA + UDMA bits are set ; On error, Carry is set and ESI -> error msg ; modifies EAX, EBX, ECX, EDX, ESI, EDI ; I_ValDisk proc mov dx,[edi*2+IDEAd] mov al,[edi+SelCmd] add edx,CDSEL out dx,al inc edx mov al,0ECh ;Issue "Identify Device" command. out dx,al call WaitRdy mov esi,CStr('Identify ERROR') jc I_SErr sub edx,CCMD-CDATA ;Point to IDE data register. mov edi,[dwBufferLin] mov ecx,256 mov esi,edi rep insw push esi mov edi,offset DName mov cl,26 ;copy "modelnumber", lea esi,[esi+27*2] @@: lodsw ;copy ID words 27-52 to name xchg ah,al stosw loop @B pop edi mov esi,CStr(' is no ATA device') test @byte [edi+0*2+1],80h ;ATAPI device? jnz I_SErr if LBACHECK mov esi,CStr(' does not support LBA') mov al,[edi+49*2+1] and al,3 ;mask LBA + DMA bits cmp al,3 jnz I_SErr endif @dprintf <"ValDisk: disk supports DMA & LBA, 53=%X, 88=%X",13,10>, word ptr [edi+53*2], word ptr [edi+88*2] mov bl,[edi+53*2] ;copy "UltraDMA valid" flag to BL mov bh,[edi+88*2+1] ;copy "UltraDMA mode active" flags in BH-reg. if 0;USECFGDPARMS ;--- if LBA sectors in config data exceeds values returned by BIOS, use them if option /E is given. ;--- mov eax, [edi].IDENTIFY_DEVICE_DATA.UserAddressableSectors ; mov [xxx], eax endif if SETMODE test bl,4 ;values in word at pos 88 valid? jz nosetm_udma movzx ecx,@byte [edi+88*2] ;get valid "UltraDMA modes" bsr eax, ecx ;get highest supported UDMA mode in EAX jz nosetm_udma ;jump if no UDMA mode supported mov cl,[MaxUM] cmp cl,-1 ;/Mn switch used? jz @F cmp al,cl ;AL = MIN(highest supported mode, /Mn) jb @F mov al,cl @@: xor ecx,ecx bts ecx,eax cmp cl,bh ;is this mode set already? jz nosetm jmp setm_udma ;--- check/set multi-word DMA if MWDMA nosetm_udma: test [bFlags], FL_W jz nosetm inc edx ;Point to IDE "features" register. mov al,SETM ;Set mode-select subcode. out dx,al inc edx ;Point to IDE sector-count register. ; mov al,[edi+63*2+1] ;get current MW mode movzx ax,byte ptr [edi+63*2] ;get supported MW modes bsr ax, ax jz nosetm mov ch, al or al,020h ; set multi-word DMA mode out dx,al add edx,5 ;Point to IDE cmd register. mov al,SETFEAT ;Issue "set features" command. out dx,al call WaitRdy ;Await controller-ready. mov esi,CStr(' MW-DMA mode set failed') jc I_SErr mov bh,0 jmp I_MWYes endif ;--- v1.4: if any UDMA mode is active and no /M option given, ;--- don't modify mode. setm_udma: cmp bh, 0 jz @F cmp [MaxUM],-1 jz nosetm @@: @dprintf <"ValDisk: setting UDMA mode",13,10> mov bh,cl ;save it in BH for later push eax inc edx ;Point to IDE "features" register. mov al,SETM ;Set mode-select subcode. out dx,al inc edx ;Point to IDE sector-count register. pop eax or al,040h setumw: out dx,al add edx,5 ;Point to IDE cmd register. mov al,SETFEAT ;Issue "set features" command. out dx,al call WaitRdy ;Await controller-ready. mov esi,CStr(' UDMA mode set failed') jc I_SErr @dprintf <"ValDisk: UDMA mode set successful",13,10> nosetm: ife MWDMA nosetm_udma: endif endif mov esi,CStr(' is not UltraDMA') if 0;MWDMA test [bFlags],FL_W ;handle multiword DMA devices? jz @F mov ch,[edi+63*2+1] ;copy multiword flags to AH and ch,ch ;is a MW-DMA mode set? jz I_MWYes mov bh,0 jmp I_MWYes @@: endif test bl,04h ;UltraDMA bit set? jz I_SErr or bh,bh ;any UltraDMA mode set? jz I_SErr ;No? Exit & display message! I_MWYes: if MWDMA mov eax,offset UModes ;Point to UltraDMA mode table. and bh,bh jnz @F mov bh, ch mov eax,offset MWModes ;Point to MW-DMA mode table @@: mov edi, eax else mov edi,offset UModes ;Point to UltraDMA mode table. endif I_NxtM: shr bh,1 ;More UltraDMA modes to check? jz I_GotM inc edi ;Point to next mode table value. inc edi inc ecx ;Get next UltraDMA mode number. jmp I_NxtM I_GotM: test [bFlags],FL_Q jnz I_Exit mov ebx,offset DName+SIZENAME ;Point to end of disk name. @@: cmp ebx,offset DName ;Are we at the disk-name start? je @F ;Yes, disk name is all spaces! dec ebx ;Decrement disk name pointer. cmp @byte [ebx],' ' ;Is this name byte a space? je @B ;No, continue scan for non-space. inc ebx ;Skip non-space character. @@: mov byte ptr [ebx], 0 mov ax,[edi] ;Get disk "mode" value. invoke printf, CStr("%s, ATA-%u",13,10), ebx, ax clc I_Exit: ret I_SErr: stc ret align 4 I_ValDisk endp getpci: mov dx, 0cf8h out dx, eax add dl, 4 in eax, dx sub dl, 4 ret setpci: mov dx, 0cf8h out dx, eax add dl, 4 mov eax, ecx out dx, eax sub dl, 4 ret ;--- get DMA controller port for a HD. ;--- for a "native" controller, IDE port bases are in registers 0-3: ;--- 0+1=primary base+alternate, 2+3=secondary base+alternate ;--- in: BX=bus/device/func ;--- EDI=drive# ;--- [edi][IDEAd]=IDE port ;--- out: CX=DMA port I_GetDMAPort proc uses esi movzx ebx, bx shl ebx, 8 bts ebx, 31 ;--- v1.5: ensure that Busmaster flag is set in command register if SETBM mov eax, ebx mov al, 4 ;read command register (byte) call getpci bts eax, 2 ;busmaster set? jc @F mov ecx, eax mov eax, ebx mov al, 4 call setpci @@: endif mov eax, ebx mov al,16+4*4 ;Get PCI DMA base address (register 4). call getpci mov ecx, eax and cl, 0fch mov eax, ebx mov al, 8 ;get class code call getpci shr eax, 8 or esi, 1111b cmp ah, 6 ;SATA controller? then no "legacy" bits jz @F mov esi, eax @@: mov eax, 1F0h bt esi, 0 ;primary native? jnc @F mov eax, ebx mov al,16+0*4 ;Get primary IDE base address call getpci and al, 0fch @@: @dprintf <"GetPorts: cmp pri base=%X-%X",13,10>, eax, word ptr [edi*2+IDEAd] cmp ax,[edi*2+IDEAd] jz found mov eax, 170h bt esi, 2 ;secondary native? jnc @F mov eax, ebx mov al,16+2*4 ;Get secondary IDE base address call getpci and al, 0fch @@: @dprintf <"GetPorts: cmp sec base=%X-%X",13,10>, eax, word ptr [edi*2+IDEAd] cmp ax,[edi*2+IDEAd] stc jnz exit add ecx, 8 found: @dprintf <"GetPorts: DMA=%X",13,10>, ecx clc exit: ret align 4 I_GetDMAPort endp ;--- get Ultra-DMA port for EDD 2.0 (or 3.0 if no BIOS disk has been found) ;--- inp: EDI=drive ;--- out: NC + DMA controller port base in CX ;--- C on errors ;--- PCI command register ;--- 0001: I/O access enabled ;--- 0002: memory access enabled ;--- 0004: BM access enabled ;--- 0008: I_GetUDMAC proc mov esi, offset controllers NextIF: lodsw cmp ax,-1 jz error movzx ebx, ax ifdef _DEBUG shr eax, 8 movzx eax, al mov edx, ebx shr edx, 3 and edx, 1Fh mov ecx, ebx and ecx, 7 @dprintf <"GetUDMAC: check IDE controller Bus/Dev/Fn=%u/%u/%u",13,10>, eax, edx, ecx endif ife SETBM mov eax, ebx shl eax, 8 bts eax, 31 mov al, 4 ;Get PCI command + status register call getpci mov ecx, eax @dprintf <"GetUDMAC: cmd+status register=%X",13,10>, ecx and ecx,4+1 ;Mask Bus-Master and I-O Space bits. cmp ecx,4+1 ;Is this how our controller is set up? jnz @F endif call I_GetDMAPort jnc exit @@: cmp esi, offset controllers + sizeof controllers jb NextIF ; go try next one. error: stc exit: ret align 4 I_GetUDMAC endp ;--- get cmdline parameters ;--- modifies ESI I_GetParams proc mov esi,[dwCmdLine] I_NxtC: lodsb cmp al,0 je I_Term cmp al,LF je I_Term cmp al,CR je I_Term cmp al,'-' je I_NxtS cmp al,'/' jne I_NxtC ;No, check next command-line byte. I_NxtS: mov ax,[esi] or ax,2020h ;convert to lower-case cmp al,'l' ;/L? jne I_ChkQ ;No, go see if byte is "Q" or "q". mov @byte [@DMALmt],009h ;Set 640K "DMA limit" above. inc esi ;Point to next command-line byte. I_ChkQ: cmp al,'q' ;/Q? jnz @F or [bFlags],FL_Q inc esi @@: if SCATTER cmp al,'f' ;/F? jnz @F or [bFlags],FL_F inc esi @@: endif if MWDMA cmp al,'w' ;/W? jnz @F or [bFlags],FL_W inc esi @@: endif if SETMODE cmp al,'m' ;/M? jne @F inc esi ;Bump pointer past "mode" switch. cmp ah,'7' ja I_NxtC sub ah,'0' jb I_NxtC mov [MaxUM],ah ;Set maximum UltraDMA "mode" above. inc esi ;Bump pointer past "mode" value. @@: endif if BUFONLY cmp al,'b' ;/B? jnz @F inc esi BUFOFS equ offset BufIO - (offset @BufPatch + 5) mov @byte [@BufPatch], 0E9h mov @dword [@BufPatch+1], BUFOFS jmp I_NxtC @@: endif jmp I_NxtC ;Continue scanning for a terminator. I_Term: ret align 4 I_GetParams endp ;--- Init XMS ;--- on errors, set Carry and error msg in ESI InitXMS proc mov ax, 4300h ;Inquire about an XMS manager. push 2Fh call IntXX mov eax, [ebp].Client_Reg_Struc.Client_EAX cmp al,080h ;Is an XMS manager installed? jne I_XErr1 ;No, display message & disable XMS. mov ax, 4310h ;Get XMS manager "entry" address. push 2Fh call IntXX mov bx, @word [ebp].Client_Reg_Struc.Client_ES shl ebx, 16 mov bx, @word [ebp].Client_Reg_Struc.Client_EBX mov [XMSEntry], ebx mov ah,009h ;Ask XMS manager for 128K of memory. mov dx,128 call I_XMS jnz I_XErr2 ;If error, display msg. & disable XMS. mov edx,[ebp].Client_Reg_Struc.Client_EDX mov [XMSHdl],edx ;Save XMS buffer handle number. mov ah,00Ch ;"Lock" our XMS memory. call I_XMS jnz I_XErr3 ;If error, display msg. & disable XMS. mov eax,[ebp].Client_Reg_Struc.Client_EDX shl eax,16 ;Get unaligned XMS buffer address. mov ax,@word [ebp].Client_Reg_Struc.Client_EBX add eax,65536-1 ;Find 1st 64K boundary after start. xor ax,ax mov [dwBufferPhy],eax ;Set physical buffer address ret I_XErr1: mov esi,CStr('No XMS manager') stc ret I_XErr2: mov esi,CStr('XMS 128 kB memory allocation failed') stc ret I_XErr3: mov esi,CStr('XMS lock memory error') stc ret align 4 InitXMS endp ;--- in: EDI=drive# ;--- CX=DMA port ;--- display current values for disk ;--- out: ;--- DMAAd ;--- CHSHd ;--- CHSSec I_SetDisk proc and cl,0FCh mov [edi*2+DMAAd],cx test [bFlags],FL_Q jnz I_NoDskDisp mov esi, CStr('Master') test [edi+SelCmd],10h ;Is this disk the master? jz @F ;Yes, display "master" name. mov esi, CStr('Slave') @@: invoke printf, CStr("HD %u, %s, ATA/DMA ports %X/%X"), edi, esi, [edi*2][IDEAd], [edi*2][DMAAd] I_NoDskDisp: mov ah,08h ;Get BIOS CHS values for this disk. call Int13 jc I_CHSE ;If BIOS error, zero sectors/head. mov ecx,[ebp].Client_Reg_Struc.Client_ECX mov edx,[ebp].Client_Reg_Struc.Client_EDX @dprintf <"SetDisk: int 13, ax=8 ok, cx=%X dx=%X",13,10>, cx, dx and ecx,03Fh ;Get sectors/head value (low 6 bits). inc dh ;Get heads/cylinder (BIOS value + 1). jnz I_SetC ;If non-zero, save disk's CHS values. I_CHSE: xor cl,cl ;CHS error! Zero disk's sectors/head. I_SetC: mov [edi+CHSHd],dh ;Save disk's CHS values in our tables. mov [edi+CHSSec],cl push edi call I_ValDisk pop edi jc I_ErrD ;If any errors, DELETE this disk! cmp [edi+CHSSec],0 ;Were disk's CHS values legitimate? jne exit ;Yes, check for more disks to use. mov esi,CStr("** BIOS must do above disk's CHS I-O") I_ErrD: invoke printf, CStr("%s",13,10), esi ;Display error for this disk. stc exit: ret I_SetDisk endp ; ; initialization ; InitXDMA proc cld @dprintf <"InitXDMA entry, eflags=%X",13,10>, [ebp].Client_Reg_Struc.Client_EFlags call I_GetParams ;get cmdline params test [bFlags],FL_Q jnz @F invoke printf, CStr("XDMA32",VERSION,CR,LF) @@: mov [ebp].Client_Reg_Struc.Client_EDI, 0 ;Get PCI BIOS "I.D." code. mov ax, 0B101h push 1ah call IntXX mov edx, [ebp].Client_Reg_Struc.Client_EDX mov esi,CStr('PCI BIOS Invalid') cmp edx," ICP" ;Is PCI BIOS V2.0C or newer? jne I_Err ;Go display error message and exit. @dprintf <"PCI BIOS detected",13,10> ;--- get all PCI IDE controllers ;--- todo: also get SATA controllers with AHCI disabled. mov esi, 80000008h mov edi, offset controllers nextctrl: mov eax, esi call getpci shr eax, 8 and al, 80h cmp eax, 10180h jnz @F mov eax, esi shr eax, 8 stosw ifdef _DEBUG movzx eax, ax mov ecx, eax shr ecx, 3 and ecx, 1Fh mov edx, eax and edx, 7 shr eax, 8 @dprintf <"PCI IDE controller found at %u/%u/%u",13,10>, eax, ecx, edx endif cmp edi, offset controllers + sizeof controllers jnc donectrl @@: @VMMCall Yield add esi, 100h cmp esi, 81000008h jb nextctrl donectrl: mov ax,03513h ;Get and save current Int 13h vector. push 21h call IntXX mov bx, @word [ebp].Client_Reg_Struc.Client_ES shl ebx, 16 mov bx, @word [ebp].Client_Reg_Struc.Client_EBX mov [PrvI13], ebx call InitXMS ;init XMS jc I_Err @dprintf <"XMS allocated",13,10> push 0 push 16 push PR_SYSTEM @VMMCall _PageReserve ;allocate a 64 kB block of address space add esp,3*4 mov esi,CStr('No address space for buffer') cmp eax,-1 jz I_Err mov dwBufferLin, eax shr eax, 12 ;convert linear address to page number @dprintf <"Init: Buffer Address Space allocated",13,10> push PC_INCR or PC_WRITEABLE mov edx, [dwBufferPhy] shr edx, 12 push edx push 16 push eax @VMMCall _PageCommitPhys ;backup address space with XMS memory add esp,4*4 @dprintf <"Init: Buffer committed",13,10> ; mov ecx,offset Entry - offset startcode ; mov esi,offset startcode mov ecx,4*4 ; max 16 byte mov esi,offset IOAdr xor edx, edx VxDCall VDMAD_Lock_DMA_Region ; add [PRDAd],edx ;Set relocated 32-bit PRD address. mov [PRDAd], edx ;Set relocated 32-bit PRD address. @dprintf <"Init: DMA PRD address=%X",13,10>, edx if HDNUM mov al,ds:[HDISKS] ;Did BIOS find any hard-disks? else mov [HDUnit],80h ;set hard-disk unit number mov ah,08h ;get info call Int13 ;AX,BX,DL set in Int13 jc I_None mov eax,[ebp].Client_Reg_Struc.Client_EDX endif cmp al,0 jz I_None ;No? Display "No disk" and exit! mov [BiosHD],al ;Save BIOS hard-disk count. @dprintf <"Int13 disk scan:",13,10> ;--- scan the HDs, max 2 times ;--- first with int 13h calls (ah=41h) ;--- if none found, try with int 1ah ("PCI disk scan"). mov [HDUnit],80h ;Reset hard-disk unit number mov al,[BiosHD] ;Reset remaining hard-disk count. mov [HDCount],al xor edi,edi ;Init unit table index NextDev: mov ah, 41h ;Get EDD "extensions" for this disk. mov bx, 55AAh call Int13 jc I_SkipDrv ;If none, ignore disk & check for more. @dprintf <"Int 13h, ah=41h ok, ax=%X bx=%X cx=%X",13,10>, word ptr [ebp].Client_Reg_Struc.Client_EAX, word ptr [ebp].Client_Reg_Struc.Client_EBX, word ptr [ebp].Client_Reg_Struc.Client_ECX ;--- in AH EDD version is returned, ah=30 for version EDD 3.0 mov ebx,[ebp].Client_Reg_Struc.Client_EBX mov ecx,[ebp].Client_Reg_Struc.Client_ECX cmp bx,0AA55h ;Did BIOS "reverse" our entry code? jne I_SkipDrv ;No, ignore this disk & check for more. ;--- v1.4: checking bit 2 is not reliable, so checking bit 0 may be sufficient test cl,101b ;Does this disk have "EDD" extensions? jz I_SkipDrv ;No, ignore this disk & check for more. mov eax,[wBaseSeg] mov @word [ebp].Client_Reg_Struc.Client_DS, ax mov eax, 20h ;don't touch the first 32 bytes mov [ebp].Client_Reg_Struc.Client_ESI, eax mov esi, [dwBase] add esi, eax mov eax,[ebp].Client_Reg_Struc.Client_EAX mov @dword [esi].EDD20.cbSize, sizeof EDD30 ;set size and clear flags mov [esi].EDD20.lpCfg, -1 cmp ah, 30h jnc @F mov [esi].EDD20.cbSize, sizeof EDD20 @@: ;--- call int 13h, ah=48h: Get this disk's "EDD" parameters. ;--- v86 DS:SI=EDD, has been set above to wBaseSeg:0020h mov ah, 48h call Int13 jc I_ErED ;Error? Display msg. & ignore! @dprintf <"Int 13h, ah=48h ok, ax=%X, size EDD=%X, DPTE=%X",13,10>, word ptr [ebp].Client_Reg_Struc.Client_EAX, [esi].EDD20.cbSize, [esi].EDD20.lpCfg cmp [esi].EDD10.cbSize, sizeof EDD20 ;is it at least EDD20? jb I_SkipDrv ;No, ignore disk & check more. movzx ecx, @word [esi].EDD20.lpCfg+0 movzx edx, @word [esi].EDD20.lpCfg+2 shl edx, 4 add edx, ecx ;EDX=this disk's "DPTE" pointer. cmp [esi].EDD10.cbSize, sizeof EDD30 ;EDD 3.0? jnc I_IsEDD30 cmp [esi].EDD20.lpCfg,-1;Valid "DPTE" pointer? je I_SkipDrv ;No, ignore disk & check more. mov ebx,15 ;Calculate "DPTE" checksum. xor ecx,ecx @@: add cl,[ebx+edx] dec ebx jns @B jecxz I_IsEDD20 ;If checksum O.K., use parameters. I_ErED: invoke printf, CStr('EDD BIOS error! Unit ignored',CR,LF) jmp I_SkipDrv I_IsEDD20: @dprintf <"drive is EDD20, Port=%X, Stat=%X, flgs=%X",13,10>, [edx].DPTE.wIDEBase, [edx].DPTE.wIDEAlt, word ptr [edx].DPTE.bFlags mov ax,[edx].DPTE.wIDEBase ;Get disk's IDE base address. and ax,ax jz I_ErED mov cx,[edx].DPTE.wIDEAlt ;Get disk's IDE status address. mov bl,[edx].DPTE.bFlags and bl,10h ;use the "slave" flags or bl,0E0h mov [edi*2+IDEAd],ax mov [edi*2+IDEAlt],cx mov [edi*1+SelCmd],bl call I_GetUDMAC jc I_SkipDrv jmp I_EDDdone ;--- BIOS/drive is EDD30 ;--- edx=DPTE (if lpCfg != -1) I_IsEDD30: @dprintf <"drive is EDD30, szIFType=%X",13,10>, dword ptr [esi].EDD30.szIFType mov eax, dword ptr [esi].EDD30.szIFType cmp eax ,"ATA" ;ATA interface? jz @F cmp eax ," ATA" jz @F cmp eax ,"ATAS" ;for SATA, check if AHCI mode is disabled! jnz I_SkipDrv @@: mov al, [esi].EDD30.szDevPath ;for ATA, device path contains master/slave flag shl al, 4 or al,0E0h mov [edi*1+SelCmd],al mov eax, dword ptr [esi].EDD30.szBus and eax, 0FFFFFFh cmp eax ,"ASI" ;ISA host bus? jnz @F mov ax, word ptr [esi].EDD30.qwIFPath ;for ISA, interface path contains IDE port base mov [edi*2+IDEAd],ax call I_GetUDMAC jnc I_EDDdone @dprintf <"GetUDMAC failed for drive",13,10> jmp I_SkipDrv @@: ;--- lpCfg might be -1! ;--- if this happens, we can't distinguish primary and secondary cmp [esi].EDD20.lpCfg,-1 ; Valid "DPTE" pointer? jz I_SkipDrv mov ax,[edx].DPTE.wIDEBase ; the PCI info doesn't tell if prim or secondary! and ax,ax jz I_ErED mov [edi*2+IDEAd],ax ; so we need the DPTE @@: mov bh, [esi].EDD30.qwIFPath ;get interface path (Bus) mov bl, [esi].EDD30.qwIFPath+1 ;get interface path (Device) mov al, [esi].EDD30.qwIFPath+2 ;get interface path (Function) shl bl,3 and al,7 or bl,al call I_GetDMAPort ifdef _DEBUG jnc I_EDDdone @dprintf <"I_GetPorts failed for drive",13,10> jmp I_SkipDrv else jc I_SkipDrv endif I_EDDdone: call I_SetDisk jc I_SkipDrv mov al,[HDUnit] ;Activate this disk in main driver. mov [edi+Units],al inc edi cmp edi,NUMDSK ;free entry in unit table? je I_DiskFound I_SkipDrv: inc [HDUnit] ;Bump BIOS unit dec @byte [HDCount] ;More BIOS disks to check? jnz NextDev ;Yes, loop back and do next one. and edi,edi ;any disk in unit table? jnz I_DiskFound ;--- PCI scan if ?PCISCAN cmp [controllers], -1 ; any EIDE controller found? jz I_None ; if no, the PCI scan is useless. @dprintf <'PCI disk scan:',CR,LF> mov [HDUnit], 80h ;Reset hard-disk unit number mov al,[BiosHD] ;Reset remaining hard-disk count. mov [HDCount], al xor edi, edi ;Init unit table index NextDev2: call I_GetUDMAC jc @F call I_SetDisk jc @F mov al,[HDUnit] ;Activate this disk in main driver. mov [edi+Units],al inc edi cmp edi,NUMDSK ;free entry in unit table? je I_DiskFound @@: inc [HDUnit] ;Bump BIOS unit dec @byte [HDCount] ;More BIOS disks to check? jnz NextDev2 ;Yes, loop back and do next one. endif and edi,edi ;any disk in unit table? jz I_None I_DiskFound: @dprintf <"Scanning done",13,10> mov @dword [@LastU],edi ;Post last-unit index in main driver. mov esi, offset Entry xor edx, edx @VMMCall Allocate_V86_Call_Back mov esi,CStr('No callbacks available anymore',CR,LF) jc I_Err mov @word [ebp].Client_Reg_Struc.Client_EDX, ax shr eax, 16 mov @word [ebp].Client_Reg_Struc.Client_DS, ax mov ax,02513h push 21h call IntXX ;"Hook" this driver into Int 13h. mov ax,RPDONE ;Get initialization "success" code. jmp I_Exit I_None: mov esi,CStr('No disk to use') I_Err: mov edx,[XMSHdl] ;Get XMS memory "handle". or dx,dx ;Did we reserve XMS memory? jz @F ;No, go display error message. call I_ReleaseXMS ;Get rid of our XMS buffer. @@: invoke printf, CStr("%s; XDMA32 not loaded!",CR,LF), esi mov ax,RPDONE+RPERR I_Exit: @dprintf <"Init exit",13,10> ret InitXDMA endp ; ;--- free XMS memory block on errors ; I_ReleaseXMS proc mov ah,00Dh ;Error -- unlock & free XMS buffer. push edx call I_XMS mov ah,00Ah pop edx I_ReleaseXMS endp ;--- fall thru I_XMS proc mov [ebp].Client_Reg_Struc.Client_EAX, eax mov [ebp].Client_Reg_Struc.Client_EDX, edx @VMMCall Begin_Nest_Exec movzx edx, @word [XMSEntry+0] mov cx, @word [XMSEntry+2] @VMMCall Simulate_Far_Call @VMMCall Resume_Exec @VMMCall End_Nest_Exec mov eax,[ebp].Client_Reg_Struc.Client_EAX dec ax ;Zero AX-reg. if success, -1 if error. ret I_XMS endp ;--- call int 13h, ;--- ah=08h, DL=dsk ;--- ah=41h, DL=dsk, BX=55AAh ;--- ah=48h, DL=dsk, DS:SI->EDD1/2/3 Int13 proc mov @word [ebp].Client_Reg_Struc.Client_EBX, bx mov dl,[HDUnit] ;Set BIOS unit in DL-reg. mov @byte [ebp].Client_Reg_Struc.Client_EDX, dl push 13h call IntXX ret Int13 endp ;--- call int 1Ah (ax=B101h) ;--- call int 21h (ah=25h/35h) ;--- call int 2Fh (ax=4300h, 4310h) ;--- 1A, B101h: install check, in: edi=0, out: edx="PCI ", BX=version IntXX proc mov @word [ebp].Client_Reg_Struc.Client_EAX, ax @VMMCall Begin_Nest_Exec mov eax, [esp+4] @VMMCall Exec_Int @VMMCall End_Nest_Exec mov ah,@byte [ebp].Client_Reg_Struc.Client_EFlags sahf ret 4 IntXX endp DllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword .if dwReason == 1 mov esi, dwRes movzx ecx,[esi].JLCOMM.wLdrCS mov wBaseSeg,ecx shl ecx, 4 mov dwBase, ecx mov eax,[esi].JLCOMM.lpCmdLine mov dwCmdLine, eax ;--- set EBP to the client pointer before calling InitXDMA push ebp @VMMCall Get_Cur_VM_Handle ;get VM handle in EBX mov ebp,[ebx].cb_s.CB_Client_Pointer if SAVESTAT sub esp, sizeof Client_Reg_Struc mov edi, esp @VMMCall Save_Client_State endif push esi call InitXDMA pop esi test [esi].JLCOMM.wFlags, JLF_DRIVER ;loaded as DOS device driver? jz @F mov ebx,[esi].JLCOMM.lpRequest mov [ebx].RP.RPStat,ax mov ecx, [wBaseSeg] mov @word [ebx].RP.RPSize+2,cx xor ecx, ecx mov @word [ebx].RP.RPSize+0,cx @@: cmp ax, RPDONE setz al movzx edi, al @dprintf <"exit DllMain",13,10> if SAVESTAT mov esi, esp @VMMCall Restore_Client_State add esp, sizeof Client_Reg_Struc endif pop ebp mov eax, edi .endif ret DllMain endp end DllMain ================================================ FILE: JLM/XDMA32/XDMA32.txt ================================================ 1. About XDMA32 XDMA32 is a JLM based on Jack R. Ellis' XDMA Ultra-DMA HD driver. It supports PCI IDE controllers running in "legacy" or "native" mode. 2. Usage To load XDMA32, add the following line to your CONFIG.SYS: DEVICE=JLOAD.EXE XDMA32.DLL [options] options are: /B always use XMS buffer for file i/o. Might be useful in virtualized environments which can't handle DMA properly for all addresses. This switch is more restrictive than /L, and it disables /F. /F "fast", uses DMA "scatter/gather" method to allow transfers which cross a physical 64 kB boundary. Might not work with all controllers, so use with care! (The "fast" is a historical remnant, on modern systems there will be no significant difference in speed.) /L limits DMA to "low memory" below 640 kB. Addresses above are handled through the XMS buffer. /Mn set/restrict UDMA mode to n (0 <= n <= 7). /Q quiet mode. /W handle (non-UDMA) drives which are capable of Multiword-DMA. 3. Features & Restrictions - XDMA32 supports up to 4 PCI IDE controllers. May be changed in XDMA32.ASM ( NUMCTRL constant ). - XDMA32 supports up to 8 HDs. May be changed in XDMA32.ASM ( NUMDSK constant ). - HDs are accessed using either CHS, LBA28 or LBA48 addressing, so there's no (practical) size restriction. - XDMA32 has no device ID associated with it - hence jload cannot detect currently if the driver is already installed. - SATA controllers running in AHCI mode are ignored. - the XDMA32 driver needs just 48 bytes in conventional or upper memory. - a 128 kB extended memory block is allocated as a buffer to be used if the current transfer address is incompatible with DMA access. 4. License XDMA32 is released under the GNU GPL v2. See GNU_GPL.TXT for details. Japheth ================================================ FILE: JemmExL.mak ================================================ # # builds JemmExL, the "legacy" variant of JemmEx, without support # of XMS v3.5 ( super-extended memory ) NAME3=JEMMEXL !ifndef DEBUG DEBUG=0 !endif # to create kernel debugger aware versions, add "kd=1" to nmake !ifndef KD KD=0 !endif ASM=jwasm.exe # select 32-bit COFF linker, default JWLink !ifndef JWLINK32 JWLINK32=0 !endif !ifndef WLINK32 WLINK32=0 !endif !ifndef MSLINK32 MSLINK32=0 !endif !if $(JWLINK32)+$(WLINK32)+$(MSLINK32)==0 JWLINK32=1 !endif # select 16-bit OMF linker, default JWLink !ifndef JWLINK JWLINK=0 !endif !ifndef WLINK WLINK=0 !endif !ifndef MSLINK MSLINK=0 !endif !if $(JWLINK)+$(WLINK)+$(MSLINK)==0 JWLINK=1 !endif !if $(DEBUG) AOPTD=-D_DEBUG $(DBGOPT) -Sg !else AOPTD= !endif AOPT=-c -nologo -IInclude $(AOPTD) # list of 32bit modules COFFMODS=.\jemm32.obj .\ems.obj .\vcpi.obj .\dev.obj .\xms.obj .\umb.obj .\vdma.obj .\i15.obj .\emu.obj .\vds.obj .\pool.obj .\init.obj .\debug.obj BUILD=build !if $(DEBUG) OUTD3=$(BUILD)\$(NAME3)D COFFDEP3=$(COFFMODS:.\=build\JEMMEXLD\) !else OUTD3=$(BUILD)\$(NAME3) COFFDEP3=$(COFFMODS:.\=build\JEMMEXL\) !endif !if $(JWLINK32) LINK32=jwlink format raw bin file {$(COFFMODS:.\=)} name jemm32.bin disable 1014 option offs=0x110000, start=_start, map=jemm32.map, quiet !elseif $(WLINK32) LINK32= wlink format raw bin file {$(COFFMODS:.\=)} name jemm32.bin option offs=0x110000, start=_start, map=jemm32.map, quiet !else COFFOPT=/fixed /driver /subsystem:native /entry:start /base:0x100000 /align:0x10000 /MAP /nologo # MS link (newer versions won't accept option FILEALIGN anymore) LINK32=link.exe /FileAlign:0x200 $(COFFOPT) $(COFFMODS:.\=) /OUT:jemm32.bin !endif !if $(JWLINK) LINK16=jwlink.exe format dos file jemm16.obj,init16.obj name $(*B).EXE option map=$(*B).MAP, quiet !elseif $(WLINK) LINK16=wlink.exe format dos file jemm16.obj,init16.obj name $@.EXE option map=$@.MAP, quiet #else LINK16=link16.exe /NOLOGO/MAP:FULL/NOD /NOI jemm16.obj init16.obj,$@.EXE,$@.MAP; !endif 32BITDEPS=src\jemm32.inc src\jemm.inc src\extern32.inc src\debug32.inc JemmExL.mak {src\}.asm{$(OUTD3)}.obj: @$(ASM) -coff -D?INTEGRATED=1 -D?XMS35=0 $(AOPT) -Fl$(OUTD3)\ -Fo$(OUTD3)\ $< ALL: $(BUILD) $(OUTD3) $(OUTD3)\$(NAME3).EXE $(BUILD) $(OUTD3): @mkdir $* $(OUTD3)\$(NAME3).EXE: $(OUTD3)\jemm16.obj $(OUTD3)\init16.obj @cd $(OUTD3) @$(LINK16) @cd ..\.. $(OUTD3)\init16.obj: src\init16.asm src\jemm16.inc src\jemm.inc Include\jsystem.inc JemmExL.mak @$(ASM) -D?INTEGRATED=1 -D?XMS35=0 $(AOPT) -Sg -Fl$(OUTD3)\ -Fo$(OUTD3)\ src\init16.asm $(OUTD3)\jemm16.obj: src\jemm16.asm $(OUTD3)\jemm32.bin src\jemm.inc src\jemm16.inc src\debug16.inc JemmExL.mak @$(ASM) -D?INTEGRATED=1 -D?XMS35=0 $(AOPT) -Fl$(OUTD3)\ -Fo$(OUTD3)\ -I$(OUTD3) src\jemm16.asm $(OUTD3)\jemm32.bin: $(COFFDEP3) @cd $(OUTD3) @$(LINK32) @cd ..\.. $(COFFDEP3): $(32BITDEPS) clean: @if exist $(OUTD3)\*.obj erase $(OUTD3)\*.obj @if exist $(OUTD3)\*.lst erase $(OUTD3)\*.lst @if exist $(OUTD3)\*.map erase $(OUTD3)\*.map @if exist $(OUTD3)\*.exe erase $(OUTD3)\*.exe @if exist $(OUTD3)\*.bin erase $(OUTD3)\*.bin @if exist $(OUTD3)\_jemm32.inc erase $(OUTD3)\_jemm32.inc ================================================ FILE: Linux.mak ================================================ # # To build Jemm386 and JemmEx on Linux, you will need: # # Tool #--------------------------- # Assembler: JWasm # OMF Linker: JWlink # COFF Linker: JWlink # Make: GNU make # # since v5.87, JWasm must be v2.21+ (with fixed "negative offset"-bug) # # Important: all include files are referenced in the source with names # in lowercase, but the corresponding file names are uppercase. To fix # this either store the project on a FAT/NTFS file system, or create # according symlinks! # # Jemm consists of 2 parts. which are created separately: the 32-bit # part is the true Jemm application ( the "v86-monitor" program ) - # its sources are assembled and linked to jemm32.bin. The 16-bit part # is (mostly) used during the initialization phase and - except for a # small stub - not necessary to be kept in memory. As a result, the # make process consists of: # # 1. assemble the 32-bit assembly sources. # 2. link 32-bit modules to jemm32.bin (format is "raw"). # 3. assemble the 16-bit assembly sources; jemm32.bin will be included. # 4. link 16-bit modules (JEMM16.obj, INIT16.obj) to Jemm386/JemmEx. # # To enable (selective) debug displays, enter: # make DEBUG=1 DBGOPT=-D?VCPIDBG=1 # This will enable VCPI related displays. For more switches, see DEBUG32.INC. NAME1=JEMM386 NAME2=JEMMEX ifndef DEBUG DEBUG=0 endif # to create kernel debugger aware versions, run "make KD=1" ifndef KD KD=0 endif ASM=jwasm ifeq ($(DEBUG),1) AOPTD=-D_DEBUG $(DBGOPT) -Sg else AOPTD= endif AOPT=-c -nologo -IInclude $(AOPTD) # list of 32bit modules COFFMODS=OUTD/JEMM32.obj OUTD/EMS.obj OUTD/VCPI.obj OUTD/DEV.obj \ OUTD/XMS.obj OUTD/UMB.obj OUTD/VDMA.obj OUTD/I15.obj \ OUTD/EMU.obj OUTD/VDS.obj OUTD/POOL.obj OUTD/INIT.obj \ OUTD/DEBUG.obj OMFMODS=OUTD/JEMM16.obj OUTD/INIT16.obj BUILD=build ifeq ($(DEBUG),1) outd_suffix=D else outd_suffix= endif OUTD1=$(BUILD)/$(NAME1)$(outd_suffix) OUTD2=$(BUILD)/$(NAME2)$(outd_suffix) COFFDEP1=$(subst OUTD,$(OUTD1),$(COFFMODS)) COFFDEP2=$(subst OUTD,$(OUTD2),$(COFFMODS)) OMFDEP1=$(subst OUTD,$(OUTD1),$(OMFMODS)) OMFDEP2=$(subst OUTD,$(OUTD2),$(OMFMODS)) AOPT16=-omf LOPT32=format raw bin name $@ disable 1014 op q, offs=0x110000, start=_start 32BITDEPS=src/JEMM.INC src/JEMM32.INC src/DEBUG32.INC src/EXTERN32.INC Include/JSYSTEM.INC 16BITDEPS=src/JEMM.INC src/JEMM16.INC src/DEBUG16.INC Include/JSYSTEM.INC $(OUTD1)/%.obj: src/%.ASM @$(ASM) -coff -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)/ -Fo$@ $< $(OUTD2)/%.obj: src/%.ASM @$(ASM) -coff -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)/ -Fo$@ $< ALL: $(OUTD1) $(OUTD2) $(OUTD1)/$(NAME1).EXE $(OUTD2)/$(NAME2).EXE $(OUTD1) $(OUTD2): @mkdir -p $@ $(OUTD1)/$(NAME1).EXE: $(OMFDEP1) @jwlink format dos file {$(OMFDEP1)} name $@ option q, m=$(OUTD1)/$(NAME1).map $(OUTD2)/$(NAME2).EXE: $(OMFDEP2) @jwlink format dos file {$(OMFDEP2)} name $@ option q, m=$(OUTD2)/$(NAME2).map $(OUTD1)/INIT16.obj: src/INIT16.ASM $(16BITDEPS) Linux.mak @$(ASM) $(AOPT16) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)/ -Fo$@ src/INIT16.ASM $(OUTD2)/INIT16.obj: src/INIT16.ASM $(16BITDEPS) Linux.mak @$(ASM) $(AOPT16) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)/ -Fo$@ src/INIT16.ASM $(OUTD1)/JEMM16.obj: src/JEMM16.ASM $(OUTD1)/jemm32.bin $(16BITDEPS) Linux.mak @$(ASM) $(AOPT16) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)/ -Fo$@ -I$(OUTD1) src/JEMM16.ASM $(OUTD2)/JEMM16.obj: src/JEMM16.ASM $(OUTD2)/jemm32.bin $(16BITDEPS) Linux.mak @$(ASM) $(AOPT16) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)/ -Fo$@ -I$(OUTD2) src/JEMM16.ASM $(OUTD1)/jemm32.bin: $(COFFDEP1) @jwlink $(LOPT32) file {$(COFFDEP1)} op map=$(OUTD1)/jemm32.map $(OUTD2)/jemm32.bin: $(COFFDEP2) @jwlink $(LOPT32) file {$(COFFDEP2)} op map=$(OUTD2)/jemm32.map $(COFFDEP1): $(32BITDEPS) $(COFFDEP2): $(32BITDEPS) clean: @rm $(OUTD1)/*.obj @rm $(OUTD1)/*.lst @rm $(OUTD1)/*.map @rm $(OUTD1)/$(NAME1).EXE @rm $(OUTD1)/jemm32.bin @rm $(OUTD2)/*.obj @rm $(OUTD2)/*.lst @rm $(OUTD2)/*.map @rm $(OUTD2)/$(NAME2).EXE @rm $(OUTD2)/jemm32.bin ================================================ FILE: Makefile ================================================ # # To build Jemm386 and JemmEx, you will need: # # Tool Default (recommended) Alternatives #----------------------------------------------------------------- # Assembler JWasm Masm v6.1 or better (+Bin2Inc) # OMF Linker JWlink OW Wlink, MS Link (link16.exe) # COFF Linker JWlink OW Wlink # Make MS Nmake OW Wmake # # since v5.87, JWasm must be v2.21+ (with fixed "negative offset"-bug) # # notes: # - OW Wmake must be used with the -ms option! # # - WLink < v1.8 shouldn't be used as COFF linker. It contains a bug # which might cause unexpected results in Jemm. # # - j/wlink warning 1014 ("stack segment not found") is disabled; # warning would be emitted when linking the 32-bit part of Jemm. # # - j/wlink warning 1174 ("target displacement xxxx ignored for segment # fixup") is disabled; it happens if Jemm16.asm is assembled with Masm # and should be regarded as a Masm bug! # # Jemm consists of 2 parts. which are created separately: the 32-bit # part is the true Jemm application ( the "v86-monitor" program ) - # its sources are assembled and linked to Jemm32.bin. The 16-bit part # is (mostly) used during the initialization phase and - except for a # small stub - not necessary to be kept in memory. As a result, the # make process consists of: # # 1. assemble the 32-bit assembly sources # 2. link 32-bit modules to Jemm32.bin (format is "raw"). # 3. assemble the 16-bit assembly sources; Jemm32.bin will be included, # either directly ( JWasm ) or indirectly via tool Bin2Inc ( Masm ); # Bin2Inc can be found in the JWasm package. # 4. link 16-bit modules (Jemm16.obj, Init16.obj) to Jemm386/JemmEx # # To enable (selective) debug displays, enter: # nmake debug=1 dbgopt=-D?VCPIDBG=1 # This will enable VCPI related displays. For more switches, see DEBUG32.INC. NAME1=JEMM386 NAME2=JEMMEX NAME3=JEMMEXL !ifndef DEBUG DEBUG=0 !endif # to create kernel debugger aware versions, run "nmake kd=1" !ifndef KD KD=0 !endif # select assembler, JWasm or Masm, default is JWasm !ifndef MASM MASM=0 !endif !if $(MASM) ASM=ml.exe !else ASM=jwasm.exe !endif # select 32-bit COFF linker, default JWLink !ifndef JWLINK32 JWLINK32=0 !endif !ifndef WLINK32 WLINK32=0 !endif !ifndef MSLINK32 MSLINK32=0 !endif !if $(JWLINK32)+$(WLINK32)+$(MSLINK32)==0 JWLINK32=1 !endif # select 16-bit OMF linker, default JWLink !ifndef JWLINK JWLINK=0 !endif !ifndef WLINK WLINK=0 !endif !ifndef MSLINK MSLINK=0 !endif !if $(JWLINK)+$(WLINK)+$(MSLINK)==0 JWLINK=1 !endif !if $(DEBUG) AOPTD=-D_DEBUG $(DBGOPT) -Sg !else AOPTD= !endif AOPT=-c -nologo -IInclude $(AOPTD) # list of 32bit modules COFFMODS=.\jemm32.obj .\ems.obj .\vcpi.obj .\dev.obj .\xms.obj .\umb.obj .\vdma.obj .\i15.obj .\emu.obj .\vds.obj .\pool.obj .\init.obj .\debug.obj BUILD=build !if $(DEBUG) OUTD1=$(BUILD)\$(NAME1)D OUTD2=$(BUILD)\$(NAME2)D COFFDEP1=$(COFFMODS:.\=build\JEMM386D\) COFFDEP2=$(COFFMODS:.\=build\JEMMEXD\) !else OUTD1=$(BUILD)\$(NAME1) OUTD2=$(BUILD)\$(NAME2) COFFDEP1=$(COFFMODS:.\=build\JEMM386\) COFFDEP2=$(COFFMODS:.\=build\JEMMEX\) !endif !if $(JWLINK32) LINK32=jwlink format raw bin file {$(COFFMODS:.\=)} name jemm32.bin disable 1014 op offs=0x110000, start=_start, map=jemm32.map, quiet !elseif $(WLINK32) LINK32= wlink format raw bin file {$(COFFMODS:.\=)} name jemm32.bin option offs=0x110000, start=_start, map=jemm32.map, quiet !else COFFOPT=/fixed /driver /subsystem:native /entry:start /base:0x100000 /align:0x10000 /MAP /nologo # MS link (newer versions won't accept option FILEALIGN anymore) LINK32=link.exe /FileAlign:0x200 $(COFFOPT) $(COFFMODS:.\=) /OUT:jemm32.bin !endif !if $(JWLINK) LINK16=jwlink.exe format dos file jemm16.obj,init16.obj name $(*B).EXE disable 1174 option map=$(*B).MAP, quiet !elseif $(WLINK) LINK16=wlink.exe format dos file jemm16.obj,init16.obj name $@.EXE option map=$@.MAP, quiet !else LINK16=link16.exe /NOLOGO/MAP:FULL/NOD /NOI jemm16.obj init16.obj,$@.EXE,$@.MAP; !endif 32BITDEPS=src\jemm32.inc src\jemm.inc src\extern32.inc src\debug32.inc Include\jsystem.inc Makefile {src\}.asm{$(OUTD1)}.obj: @$(ASM) -coff -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\ -Fo$(OUTD1)\ $< {src\}.asm{$(OUTD2)}.obj: @$(ASM) -coff -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\ -Fo$(OUTD2)\ $< ALL: $(BUILD) $(OUTD1) $(OUTD2) $(OUTD1)\$(NAME1).EXE $(OUTD2)\$(NAME2).EXE $(BUILD) $(OUTD1) $(OUTD2): @mkdir $* $(OUTD1)\$(NAME1).EXE: $(OUTD1)\jemm16.obj $(OUTD1)\init16.obj @cd $(OUTD1) @$(LINK16) @cd ..\.. $(OUTD2)\$(NAME2).EXE: $(OUTD2)\jemm16.obj $(OUTD2)\init16.obj @cd $(OUTD2) @$(LINK16) @cd ..\.. $(OUTD1)\init16.obj: src\init16.asm src\jemm16.inc src\jemm.inc Include\jsystem.inc Makefile @$(ASM) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\ -Fo$(OUTD1)\ src\init16.asm $(OUTD2)\init16.obj: src\init16.asm src\jemm16.inc src\jemm.inc Include\jsystem.inc Makefile @$(ASM) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\ -Fo$(OUTD2)\ src\init16.asm !if $(MASM) $(OUTD1)\jemm16.obj: src\jemm16.asm $(OUTD1)\_jemm32.inc src\jemm.inc src\jemm16.inc src\debug16.inc Makefile @$(ASM) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\ -Fo$(OUTD1)\ -I$(OUTD1) src\jemm16.asm $(OUTD2)\jemm16.obj: src\jemm16.asm $(OUTD2)\_jemm32.inc src\jemm.inc src\jemm16.inc src\debug16.inc Makefile @$(ASM) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\ -Fo$(OUTD2)\ -I$(OUTD2) src\jemm16.asm $(OUTD1)\_jemm32.inc: $(OUTD1)\jemm32.bin @bin2inc.exe -q $(OUTD1)\jemm32.bin $(OUTD1)\_jemm32.inc $(OUTD2)\_jemm32.inc: $(OUTD2)\jemm32.bin @bin2inc.exe -q $(OUTD2)\jemm32.bin $(OUTD2)\_jemm32.inc !else $(OUTD1)\jemm16.obj: src\jemm16.asm $(OUTD1)\jemm32.bin src\jemm.inc src\jemm16.inc src\debug16.inc Makefile @$(ASM) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\ -Fo$(OUTD1)\ -I$(OUTD1) src\jemm16.asm $(OUTD2)\jemm16.obj: src\jemm16.asm $(OUTD2)\jemm32.bin src\jemm.inc src\jemm16.inc src\debug16.inc Makefile @$(ASM) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\ -Fo$(OUTD2)\ -I$(OUTD2) src\jemm16.asm !endif $(OUTD1)\jemm32.bin: $(COFFDEP1) @cd $(OUTD1) @$(LINK32) @cd ..\.. $(OUTD2)\jemm32.bin: $(COFFDEP2) @cd $(OUTD2) @$(LINK32) @cd ..\.. $(COFFDEP1): $(32BITDEPS) $(COFFDEP2): $(32BITDEPS) clean: @if exist $(OUTD1)\*.obj erase $(OUTD1)\*.obj @if exist $(OUTD1)\*.lst erase $(OUTD1)\*.lst @if exist $(OUTD1)\*.map erase $(OUTD1)\*.map @if exist $(OUTD1)\*.exe erase $(OUTD1)\*.exe @if exist $(OUTD1)\*.bin erase $(OUTD1)\*.bin @if exist $(OUTD1)\_jemm32.inc erase $(OUTD1)\_jemm32.inc @if exist $(OUTD2)\*.obj erase $(OUTD2)\*.obj @if exist $(OUTD2)\*.lst erase $(OUTD2)\*.lst @if exist $(OUTD2)\*.map erase $(OUTD2)\*.map @if exist $(OUTD2)\*.exe erase $(OUTD2)\*.exe @if exist $(OUTD2)\*.bin erase $(OUTD2)\*.bin @if exist $(OUTD2)\_jemm32.inc erase $(OUTD2)\_jemm32.inc ================================================ FILE: Readme.txt ================================================ Contents 1. About Jemm 2. Features 3. Commandline Options 4. Technical Details 4.1 EMS Implementation Notes 4.2 Emulation of privileged Opcodes 4.3 IOPL Sensitive Instructions 4.4 VMWare Detection 4.5 Option FASTBOOT 4.6 Option MAXSEXT 4.7 Option NOEMS 4.8 Option SB 5. Compatibility 6. Errors and Warnings 7. Troubleshooting, Hints 8. Additional Tools 8.1 UMBM 8.2 JEMFBHLP 8.3 CPUSTAT 8.4 MEMSTAT 8.5 XMSSTAT 8.6 EMSSTAT 8.7 VCPI 8.8 MOVEXBDA 8.9 CPUID 9. License 1. About Jemm Jemm is an "Expanded Memory Manager" (EMM), based on the source of FreeDOS Emm386. It should work with MS-DOS and compatible DOSes, including FreeDOS. Like other EMMs it installs the following services: - uses extended memory to simulate expanded memory (EMS) according to EMS v3.2 and EMS v4.0. - upper memory blocks (UMB) where drivers and resident programs may be loaded, thus increasing available free DOS memory. - mapping RAM to the video address segments A000-AFFF and B000-B7FF. - VCPI services to allow DOS applications running in V86-mode to switch to protected mode. VCPI also implements a simple memory management. - VDS API to give drivers/applications some control over DMA and physical addresses in V86-mode. There exist 2 versions of Jemm: - Jemm386: standard version which needs an external eXtended Memory Manager (XMM; examples: Himem[S]X, MS Himem, XMGR ) to be loaded. - JemmEx: extended version which has an XMM already included. JemmEx most likely is the better choice because it will need less DOS memory than an external XMM + Jemm386. 2. Features The main purpose of making Jemm was to make it use less resources than other EMMs, without making compromises regarding speed or compatibility. The results currently are: - Jemm386 needs just 128 bytes DOS upper memory. JemmEx uses more, mostly because it includes the XMS handle array which is located in DOS memory. With 32 XMS handles JemmEx needs 496 bytes DOS memory. - Jemm's extended memory usage is: + 44 kB for Jemm itself + 64 kB for the DMA buffer (default size) + xx kB for UMBs mapped in the first MB + 4 kB fixed amount for EMS handle management. + xx kB variable amount for EMS/VCPI memory management. For each 1.5 MB VCPI memory 64 bytes are needed, for each EMS page 5 bytes are needed. For the default values (120 MB VCPI, 2048 EMS pages) this is 16 kB. - VCPI shared address space in extended memory is just 4 kB. Other features are: - Jemm can be loaded and unloaded from the command line. Disadvantage: DOS won't care about UMBs supplied this way. - CPUs which provide the Virtual-8086 Mode Extensions (Pentium+) are actively supported, which increases the emulation speed. - the FASTBOOT option shortens reboot time. - the SPLIT option can gain additional DOS high memory. - all "lengthy" memory copy operations ( EMS, VDS, Int 15h, JemmEx: XMS ) are done with interrupts enabled. - Exceptions in protected-mode are detected and displayed. - a (rudimentary) API is supplied which allows to extend Jemm. [ JLoad uses this API to add support for 32bit protected-mode extensions (JLMs). ] - JemmEx supports XMS v3.5, allowing access to extended memory beyond the 4 GB barrier. See XMS35.txt for technical details. 3. Commandline Options For a list of available options enter: JEMM386/JEMMEX -? Option Comment ---------------------------------------------------------- A20/NOA20 will set the A20 enable/disable emulation accordingly. ALTBOOT this option is meant to select an alternate reboot handler, if the standard handler doesn't work. The current implementation sends a "system reset" command to the keyboard controller. B=xxxx specify lowest segment address for EMS banking (default=4000, min=1000). D=nnn this option will set the size of the DMA buffer to kB. The default size is 64 kB, max size is 128 kB. Will always be rounded up to next 4 kB. EMX option to prevent EMX DOS extender from quickly terminating with message "out of memory (RM)" on machines with large amounts of RAM (> 256 MB). This is optionally because it makes Jemm behave not fully VCPI compliant, but shouldn't hurt usually. FASTBOOT option might allow a fast reboot. There is no guarantee that it will work, though, see the "Option FASTBOOT" chapter below. FRAME=nnn instructs Jemm to use a certain page frame. Accepted are frame values from 8000 to E000 or NONE. The page frame should start at the beginning of a physical EMS page, that is, the frame address should be divisible by 0x400 without remainder. FRAME=NONE disables the page frame, but there are quite some programs which won't run with this setting. A page frame below A000 should be set only on computers with 512 kB of conventional memory. Usually it's better to let Jemm find a page frame on its own, because choosing an address which is not free might cause troubles. I=mmmm-nnnn force Jemm to use a memory range for UMBs (or page frame). must be >= A000. Specifying a range which is not really free may give "unexpected" results. I=TEST scan ROMs for unused address space. This option will regard any 4 kB page containing identical byte values in read-only memory as "unused". LOAD installs Jemm from the command line. Be aware that UMBs cannot be provided this way, since DOS will ignore them. [MAX=]nnn limit the maximum amount of memory to be allocated for VCPI (and EMS, if is below 32 MB). The MAX= prefix is optional (a number found as option will be handled as if it has a MAX= prefix). Default value is 120 MB. MIN=nnn preallocates kB of XMS memory thus making sure this memory is truly available for EMS/VCPI. If MIN is higher than MAX, MAX will be adjusted accordingly. Default for is 0. MOVEXBDA move XBDA into UMB, thus increasing low DOS memory. This option may cause a system "lock" because the XBDA might contain buffers used for DMA operations. In such cases either use MOVEXBDA.EXE or UMBPCI+UMBM instead. NOCHECK disallows access via Int 15h, AH=87h to address regions which aren't backuped by RAM. As for Jemm386: be aware that some old XMMs return wrong values for highest physical RAM - then this option may cause strange results. Also, this option may prevent real-mode programs from using the VESA LFB. NODYN disables XMS dynamic memory allocation. Jemm will allocate XMS memory for EMS/VCPI on initialization. Default is size of largest XMS block/2, but max. 32MB. With option MIN=xxx one may override this. NOEMS disables EMS support. NOHI this option will prevent Jemm from moving its resident part into upper memory. If no UMBs are installed by Jemm, NOHI has no effect. NOINVLPG disables usage of INVLPG opcode on 80486+ cpus. Might be useful if Jemm runs in a virtual environment (see "Troubleshooting"). NOVCPI disables VCPI. Option can be set from the command line. NOVMW disables VMWare detection. PGE/NOPGE options will enable/disable the Page Global Enable feature on Pentium Pro+ cpus. This allows to mark all PTEs for the real-mode address space 0-110000h as "global", which gives a slight speed benefit for VCPI applications. Option is off by default because some DOS extenders will not work with PGE enabled. Also setting both PGE + NOINVLPG will not work. RAM/NORAM will instruct Jemm to supply UMBs or not. RAM is the default. NORAM is intended to be used when loading Jemm from the cmdline, in which case adding UMBs might be less useful. S=mmmm-nnnn add a memory region (which must be in range C800-EFFF) as UMB. The memory region has to be filled with Shadow-RAM activated by UMBPCI, which must have been loaded *before* Himem/JemmEx. NOTE: since v5.80, this option is virtually obsolete, because either Jemm should find RAM activated by UMBPCI automatically, or, if UMBM.EXE has been loaded in CONFIG.SYS, the RAM is already used by DOS. SB Soundblaster driver compatibility mode on. SPLIT if ROMs are found which don't end exactly at a 4 kB boundary then setting this option will increase available UMB space. ROM sizes are defined in 0.5 kB units, so there might be up to 3.5 kB wasted in the ROM's last 4 kB page. There is a small catch: the full 4k page will be made writeable, including the ROM part. UNLOAD uninstalls Jemm from the command line. Uninstalling the EMM might confuse resident programs which rely on EMS or VCPI but didn't ensure this configuration to keep unchanged (by allocating an EMS page) while they are running. V86EXC0D makes Jemm route General Protection Faults (GPF) that occur in V86-mode to Int 0Dh. Without this option they are routed to Int 06h. This option should only be set if a resident program is to be installed that can handle GPFs in V86-mode. VCPI (re)enables VCPI. Option can be set from the command line. VERBOSE talk a bit more during the load process (abbreviation: /V). VME/NOVME options will enable/disable using the V86 Mode Extensions on Pentium+ CPUs. These options can be set from the command line. NOVME is the default. X=mmmm-nnnn exclude a memory range to be used as UMBs or page frame. must be >= A000. X=TEST will exclude all upper memory regions which contain byte values other than 00 or FF. JemmEx additionally understands: A20METHOD:x select A20 switch method. Possible values for : ALWAYSON Assume that A20 line is permanently ON BIOS Use BIOS to toggle the A20 line FAST Use port 92h, bypass INT 15h test PS2 Use port 92h, bypass PS/2 test KBC Use the keyboard controller PORT92 Use port 92h always HMAMIN=k set minimum amount in kB to get the HMA (default=0, max=63). MAXEXT=l limit extended memory controlled by XMM to kB. MAXSEXT=l limit extended memory beyond 4GB barrier ("super-extended") to kB; setting MAXSEXT=0 will make JemmEx behave like a v3.0 XMM. NOE801 don't use int 15h, ax=E801h to get amount of extended memory. NOE820 don't use int 15h, ax=E820h to get amount of extended memory; option is ignored unless MAXSEXT=0 is set. X2MAX=m limit for free extended memory in kB reported by XMS V2 (default 65535). It is reported that some old applications need a value of =32767. XMSHANDLES=n set number of XMS handles (default=48, min=10, max=128). 4. Technical Details 4.1 EMS Implementation Notes - The number of EMS pages is limited to 2048 (= 32 MB). It can be increased up to 32768 pages (= 512 MB) by setting MIN= to a value higher than 32 MB. However, this memory will then be preallocated, and not be available as XMS memory. Some applications will not work if EMS > 32 MB. - There is one memory pool, which is shared by EMS and VCPI. - The following EMS 4.0 functions aren't implemented. Calling these functions will return error code 84h in register AH: + Int 67h, AH=5Ch, prepare expanded memory manager hardware for warm boot + Int 67h, AH=5Dh, enable/disable OS/E 4.2 Emulation of privileged Opcodes To provide Expanded Memory an EMM Emulator like Jemm runs the cpu in so-called V86-mode. This mode does not allow to run privileged opcodes. Some of these opcodes which might be useful for application programs are emulated by Jemm. These are: - mov , ;special_reg = CRn, DRn, TRn - mov , ;reg = eax, ebx, ecx, edx, esi, edi, ebp - WBINVD - INVD - WRMSR - RDMSR - RDTSC - HLT Those instructions may generate an exception 0x0D if they occur in V86-mode ( for RDTSC, this depends on a bit in register CR4 ). Jemm's exception handler examines the opcode causing the exception and - if the instruction is to be "emulated" - runs it in ring 0. 4.3 IOPL Sensitive Instructions Jemm runs V86-mode code with IOPL 3. That means, the instructions that are sensitive to IOPL in V86-mode - CLI, STI, PUSHF, POPF, INT xx, IRET - will run at full speed, without causing a GPF. 4.4 VMWare Detection As default, Jemm tries to detect if it's running under VMWare. This is done by reading port 0x5658 with value "VMXh" in register EAX. If the detection is successful, Jemm assumes that address range E8000-EFFFF is not to be used. However, range E8000-EBFFF may still be included with the "I=" option. The VMWare detection can be disabled with option NOVMW. 4.5 Option FASTBOOT The FASTBOOT option may work with many versions of DOS. However, there are quite a few restrictions. To speed up the boot process, FASTBOOT tries to avoid resetting the system. This implicates that the interrupt vector table and at least the disk devices can be reset to the state before DOS has been loaded. Resetting the interrupt vector table requires some cooperation from DOS. Other things that may get in the way are: - Moving the XBDA into an UMB with option MOVEXBDA is critical, because to move it back into conventional memory might not work for various reasons. - older FreeDOS versions may need JEMFBHLP.EXE to be installed prior to the XMM (Himem). Possibly this might also be needed for MS-DOS versions < 5. - UMBPCI allows to activate "Shadow RAM", but there's no option to deactivate it. This may confuse Jemm, since it can only detect RAM activated by UMBPCI if UMBPCI's signature is found. - A boot loader (Grub) might have inserted code in conventional memory to modify hard disks numbers. There's no guarantee that this code survives the fastboot process. 4.6 Option MAXSEXT JemmEx only: this option limits amount of "super-extended" memory. Setting MAXSEXT=0 disables this feature. If enabled, JemmEx must exclusively manage this type of memory; to achieve this, interrupt 15h, AX=0xE820 is hooked and any memory region beyond 4 GB that's marked as available is changed to "reserved" ( tool MEMSTAT may be used to verify this behavior ). 4.7 Option NOEMS Despite its name, this option does not fully disable EMS. What's done is: - device name changed from EMMXXXX0 to EMMQXXX0 - no page frame installed - EMS memory pool is limited to 8 MB The change of the device name makes the usual EMM detection routines fail. OTOH, the interrupt 67h API is fully functional. Most of this is pretty similar to what "recent" MS Emm386 versions do - the only difference is that they won't limit the memory pool to 8 MB, but install it in its full size (32 MB). 4.8 Option SB Option SB is supposed to help Creative's SoundBlaster emulation drivers for SB PCI devices. It does 2 things: a) It translates an exception 0Dh with error code 0x1A that may have occured in V86 mode to an INT 3. It's unclear what this is supposed to fix. b) Since v5.86, it "identity maps" region 0-0x3fffff in the first page table. This mapping was done generally until v5.85. With b), Creative's SBINIT/SBEINIT tools should again be compatible with Jemm. However, due to the rather hackish nature of those drivers ( they modify Jemm's IDT/GDT and page tables ), one may experience negative effects on stability. 5. Compatibility Jemm is NOT an MS Emm386 clone. Differences are: - obviously commandline options differ. - the device driver API, accessed by opening file "EMMXXXX0" and then issueing IOCTL cmds, differs. - Jemm doesn't support the so-called GEMMIS API. - Jemm doesn't support the IO port trapping API of MS Emm386. - Jemm supports UMBs (Upper Memory Blocks) accessed via the XMS API, but the management is different: unlike MS Emm386, there's no chain of UMBs, linked with a 16-byte header ( somewhat similar to DOS MCBs ). Instead, the UMB addresses and sizes are stored in a table in extended memory, not accessible by external programs. The table can hold up to 8 blocks. - MS Emm386 tries to map all of physical memory in its linear address space, in such a way that linear and physical addresses are identical. This isn't done by Jemm, because it may consume quite a lot of physical memory for paging tables - at least if 4KB pages are used, as it is the case with MS Emm386. See option SB, though, which triggers this kind of mapping for the first 4 MB. - Both Jemm and MS Emm386 supply the NOVCPI commandline option. However, the effects differ: with Jemm, VCPI function DE01h will be deactivated by this option, thus refusing to start any VCPI client. In contrast, MS Emm386's NOVCPI makes the monitor still offer the full API, but no VCPI memory can be allocated. 6. Errors and Warnings Errors will make Jemm386/JemmEx abort the load process, while warnings will be displayed and then the load process continues. - "Error: no XMM found, required" Jemm386 only. Jemm386 needs an XMM - try Himem(S)X. - "Error: XMM already installed" JemmEx only. Since JemmEx has its very own XMM, it cannot tolerate another one in the system. - "Error: DPMI host detected" Both Jemm386 and JemmEx refuse to load if a DPMI host has been detected. - "Error: No supported A20 method detected" JemmEx only. JemmEx is NOT compatible with this system. - "Error: enable A20 failed" JemmEx only. JemmEx is NOT compatible with this system. - "Error: can't get I15 memory status" JemmEx only. Int 15h, AX=E820h ( and the fallbacks, Int 15h, ax=E801h/ah=88h ) failed or returned an amount of 0 kB extended memory. Tool memstat may help to find the problem. - "Error: can't get XMS memory status" Jemm386 only. The XMS host reports no extended memory. Tool xmsstat may give a hint what's wrong. - "Error: can't allocate enough I15 memory" JemmEx only. Run tool memstat to see how much extended memory the system is reporting. - "Error: can't allocate enough XMS memory" Jemm386 only. Run tool xmsstat to see how much extended memory the XMM is reporting. - "Error: can't lock XMS memory" Jemm386 only. Well, this shouldn't happen. Jemm386 needs the physical address of the memory block where it is to reside. The XMM most likely has to be replaced. - "Warning: unknown A20 method" JemmEx only. Option A20METHOD was given with an invalid method. - "Warning: option 'xxx=' rejected, invalid syntax" The option is ignored. - "Warning: E820 - too many ext memory blocks, block xxxxxxxx ignored!" JemmEx only. Occurs if int 15h, ax=e820h returns more than 10 available memory blocks. It's very well possible that super-extended memory isn't available if this warning has occured. The only cure is to adjust constant ?XMS_STATICHDLS in file jemm16.inc and recompile JemmEx. - "Warning: address of allocated EMB (=XXXXXX) is beyond 16MB" Jemm386 only. Means that Jemm386 is forced to reside beyond the physical 16MB barrier. No problem for Jemm386 itself, but the VDS API requires a DMA buffer that is located below that 16MB barrier. So one is better off not to ignore this warning. To fix it: a) ensure that Jemm386 is loaded just after the XMM; b) replace the XMM, use HimemX2.exe instead of HimemX.exe (if an extended memory block is allocated, HimemX2 tries to return the block with the lowest address that satisfies the request). - "Warning: no suitable page frame found, EMS functions limited." Most programs using EMS won't work without a page frame, so this warning should not be ignored. To analyze the problem, a first step may be to load Jemm from the command line, with the /V option to see why it cannot find a 64 kB region to be used as page frame. Possible reasons are: - UMBPCI is used. Memory regions activated by UMBPCI are detected by Jemm and hence won't be used for mapping the Page Frame. - BIOS marks 128 kB ( E0000-FFFFF ) as reserved in Int 15h, ax=E820h. Using the video segment A000 as page frame is useful only for very specific applications and cannot be recommended. Using regions below A000 are even more problematic on machines with 640 kB conventional memory, since the address range is already "owned" by DOS. If it's not possible to make Jemm use a valid page frame, it should be setup with option NOEMS. - "Warning: EMS banking start too low, set to 0x1000." This warning only occurs if option B=xxxx has been used inappropriately. - "Warning: MIN has been reduced to n kB" Warning occurs if the amount given for MIN exceeds available free memory. - "Warning: wanted DMA buffer size too large, set to 128 kB" Warning occurs if option D=nnn has been used with a value larger than the maximum of 128 (kb). - "Warning: XMS host doesn't provide handle array, dynamic memory allocation off!" Jemm386 only. Jemm wants the XMM to reveal its handle table. If this feature isn't available, Jemm has to allocate its memory for EMS and VCPI statically on startup. 7. Troubleshooting, Hints - If Jemm halts or reboots the machine, the following combinations of parameters may help to find the reason. Generally, Jemm386 should be loaded immediately after the XMM (HIMEM[S]X.EXE, HIMEM.SYS), and the XMM itself should be the first device driver to be loaded. For testing, it might also help to prevent DOS from loading in the HMA and/or not to use UMBs at all. - X=A000-FFFF NOHI NOINVLPG This is the safest combination. If this doesn't work, Jemm most likely isn't compatible with the current DOS/BIOS; for example, the BIOS might try to unconditionally use "unreal" mode to access USB mass storage devices - with Jemm, this will cause an exception. - X=TEST NOHI NOINVLPG This is slightly less safe, since Jemm will scan the upper memory region to find "unused" address ranges usable for UMBs. If this doesn't work, one has to manually set X=xxxx-yyyy to finally find the region which causes the troubles. Tool MEMSTAT may be used to find the address region which is reserved for the ROM-BIOS. - Jemm can be loaded from the command line with option LOAD. This may be helpful if there are so many displays during the boot process that one cannot read them carefully. Also, option /V should be added to make Jemm talkative. To load Jemm386 from the command line, ensure that a XMM has been loaded previously. For JemmEx, no XMM must be loaded, since it is included. - Jemm has been verified to run on the following virtual environments: Qemu, VMware, VirtualPC, Bochs, VirtualBox However, it might be necessary to set option NOINVLPG. - Some DOS programs will not work if EMS is enabled without a page frame. MS CodeView v4.1, for example, may refuse to start then. - Some DOS programs will crash if too much VCPI memory is offered. Jemm's default is 120 MB, it can be changed with option MAX=xxx. Popular programs that may cause troubles are programs that use Borland's DPMI hosts DPMI16BI.OVL/DPMI32VM.OVL (coupled with RTM.EXE/32RTM.EXE). Setting Jemm option MAX=32752K ( that's 32MB minus 16kB) should help; alternately, try setting environment variable "DPMIMEM=MAXMEM 16383". - The JEMM ;-) DOS extender (used for "Strike Commander" and "Privateer") isn't compatible with the VME option. This requirement is a strong sign that this extender switches to V86 mode on its own, which is a bad idea for a VCPI client. - Unlike MS Emm386, Jemm does not try to identity-map extended memory in its address space. This prevents the PL0 debugger 386SWAT from "intruding". - If Jemm is installed from the commandline, loading the CTMOUSE driver v1.9x and v2.0x might cause an exception. Adding option NOHI or NORAM when installing Jemm should avoid that. In CTMOUSE v2.1 the bug has been fixed. - With some BIOSes disk access speed slows down significantly in V86-mode. Most likely this is because the BIOS routines want to avoid using DMA in this mode. Loading XDMA32 ( and XCDROM32 ) might fix that. - FreeDOS regrettably accepts just one UMB provider. This makes it impossible for example to use UMBM.EXE to supply UMB D000-DFFF and then tell Jemm to additionally supply B000-B7FF as UMB. MS-DOS has no such problems. - Although including the region B000-B7FF might work in most cases, one should be aware that this region is not really free, it's used by the VGA "monochrome" video modes. - To make Jemm behave (almost) like MS Emm386 regarding UMBs one should set both options X=TEST and I=TEST. For better MS Emm386 compatibility one might also consider to restrict VCPI memory to 32 MB by adding option MAX=32M. If NODYN is used, one should also set at least MIN=256K. - The NOVCPI option may be used to setup an environment similiar to Windows DOS boxes: - install Jemm with VCPI enabled - install a DPMI host residently (HDPMI, DPMIONE, ...) - disable VCPI with the NOVCPI option This forces any DOS extended application to either use DPMI or abort. - If Jemm displays warning System memory found at XXXX-XXXX, region might be in use then that region is not used by Jemm. If you are sure that the region is ok to be used, include it with 'I=XXXX-XXXX'. - The I=XXXX commandline option may be used to include the VGA "graphics" segment A000h. It might be possible to increase DOS conventional memory up to 736 kB by option I=A000-B7FF. However, there are quite a few hurdles that may cause unexpected results: - conventional memory is increased only if the region to include is adjacent to current memory. On newer machines, there's very often a "hole", caused by the Extended BIOS Data Area (XBDA or EBDA); thus the included region just becomes an UMB and won't increase lower memory. Option MOVEXBDA or tool MOVEXBDA.EXE may fix this issue. - Once address space A000h is remapped, any attempts to run programs that use VGA graphics most likely will cause a crash. - Depending on the VGA-BIOS it may happen that some non-graphics functions won't work anymore and may also cause a crash. Not unusual is that the current text font becomes corrupted ( text font bitmaps must be copied from ROM to VGA memory when a video text mode is set ). - DOS conventional memory cannot be increased anymore once UMB have been added to the DOS memory pool. So you cannot use tool UMBM here! - if MOVEXBDA.EXE causes a system lock during boot, add the /A option to the line in CONFIG.SYS that loads the driver. This aligns the XBDA to a kB boundary, which may cure the lock. - the GRUB boot loader may, under certain conditions, allocate a chunk of conventional memory BELOW the XBDA. In this case conventional memory cannot be increased anymore, even if the XBDA is moved. 8. Additional Tools 8.1 UMBM UMBM is a small tool only useful in conjunction with Uwe Sieber's UMBPCI. The main purpose of UMBM is to allow DOS to load the XMM into upper memory. This driver must be loaded before Jemm386 and therefore DOS can't use UMBs provided by Jemm386. For JemmEx, UMBM is usually not needed, since the XMM is included and needs no low memory. How does UMBM work? It expects to find a "shadow" RAM region activated by UMBPCI. Then it installs inself as a temporary XMS host which just provides support for allocating UMBs. This is enough for most DOSes to grab the memory (note: for this to work line DOS=UMB is required in CONFIG.SYS). After the UMBs have been allocated, UMBM will be removed from memory automatically. Additionally, UMBM understands the /XBDA option. This will cause UMBM to move the XBDA into its first UMB, thus freeing even more low DOS memory. UMBs based on "shadow" RAM, as it is supplied by UMBPCI+UMBM, may have limitations depending on the motherboard's chipset. Sometimes the memory is inaccessible for DMA (read the documentation coming with UMBPCI for more details). OTOH, logical and physical addresses for these UMBs are identical, which may be an advantage, especially for the XBDA. Enter "UMBM" on the command line and read the example how to add UMBM to CONFIG.SYS. UMBM has been tested to run with MS-DOS 6/7 and FreeDOS. 8.2 JEMFBHLP JEMFBHLP is a tiny device driver only needed if both FreeDOS and Jemm's FASTBOOT option are used. FreeDOS v1.0 does not provide the information that Jemm needs for FASTBOOT to work, so this driver tries to cure FreeDOS' incapability. It saves the values for interrupt vectors 15h and 19h at 0070h:0100h, which is the MS-DOS compatible way to do it. In more recent FreeDOS versions this problem has been fixed. 8.3 CPUSTAT CPUSTAT displays some system registers and tables. Most of the registers aren't accessible in v86-mode, so - for Jemm - this program may be seen as a test if the emulation of privileged opcodes works as expected. Optionally, if cpu runs in V86-mode, CPUSTAT may display GDT, IDT, paging tables and informations stored in the task state segment of the v86-monitor. Run "CPUSTAT -?" for more details. 8.4 MEMSTAT MEMSTAT may be used to display the machine's memory layout, as it is returned by the BIOS. The most interesting infos are: - address region reserved for the ROM-BIOS - total amount of free memory 8.5 XMSSTAT XMSSTAT can be used to display the current status of the installed XMM. It allows to check current values of JemmEx options X2MAX, MAXEXT and XMSHANDLES. 8.6 EMSSTAT EMSSTAT can be used to display the current status of the installed EMM. It works with any EMM, not just Jemm. 8.7 VCPI VCPI may be used to display the VCPI status of the installed EMM. With option -p it will display the page table entries for the conventional memory. 8.8 MOVEXBDA MOVEXBDA is a device driver supposed to move the Extended BIOS Data Area ( XBDA or EBDA ) to low DOS memory. If an XBDA exists, it is usually located just below the A000 video segment, with a size of 1-12 kB. Some DOS versions will move the XBDA on their own - at least if its size doesn't exceed 1 kB; then MOVEXBDA is not needed and will do nothing if launched. Also, both JemmEx and UMBM have the ability to move the XBDA as well; those tools move the XBDA to upper memory, thus increasing low DOS memory. However, sometimes MOVEXBDA may be the best option available - because moving the XBDA into an UMB supplied by Jemm (or UMBPCI) may cause the system to "lock". MOVEXBDA should work with any EMM. 8.9 CPUID CPUID displays cpu features returned by the CPUID instruction. 9. License - JEMM386/JEMMEX: partly Artistic License (see ARTISTIC.TXT for details) - UMBM: Public Domain - JEMFBHLP: Public Domain - CPUSTAT: Public Domain - MEMSTAT: Public Domain - XMSSTAT: Public Domain - EMSSTAT: Public Domain - VCPI: Public Domain - MOVEXBDA: Public Domain - CPUID: Public Domain Binaries and source are distributed in separate packages. The binaries' package has a 'B' suffix in its name, the package containing the source has a 'S'. Japheth ================================================ FILE: Test/BMINTRM.ASM ================================================ ;--- benchmark ;*** call int 69h a number of times and count timer ticks ;--- jwasm -mz bmintrm.asm .286 .MODEL tiny .stack 2048 .dosseg cr equ 13 lf equ 10 ?USERDTSC equ 0 include macros.inc .CODE .386 include printf.inc ife ?USERDTSC .586 include timerms.inc endif main proc c local dwStart:dword local rmvec:dword local dwStartTSC:qword push 0 pop es mov ebx,es:[69h*4] mov rmvec,ebx mov es:[69h*4+0],offset intproc mov es:[69h*4+2],cs if ?USERDTSC rdtsc mov dword ptr dwStartTSC+0,eax mov dword ptr dwStartTSC+4,edx else call gettimer mov dwStart,eax endif xor esi,esi @@: int 69h cmp esi,1000000 jnz @B if ?USERDTSC rdtsc sub eax,dword ptr dwStartTSC+0 sbb edx,dword ptr dwStartTSC+4 invoke printf, CStr("TSC diff=%lX%08lX",lf), edx, eax else call gettimer sub eax,dwStart invoke printf, CStr("time for %lu INTs: %lu ms",lf), esi, eax endif push 0 pop es mov ebx,rmvec mov es:[69h*4],ebx ret main endp intproc: inc esi iret start: mov ax, cs mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h end start ================================================ FILE: Test/EMS4E.ASM ================================================ ;*** test EMS get/set page map .286 .model small option casemap:none .stack 1024 .dosseg .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .data mapstruct label byte dw 4 dw 0A000h dw 0A400h dw 0A800h dw 0AC00h .data? buffer db 256 dup (?) .code include printf.inc emsstr1 db 'EMMXXXX0',0 EMScheck proc uses si di mov ax, 3567h int 21h mov ax, es or ax, bx jz exit mov dx,1 mov si,offset emsstr1 mov di,000ah mov cx,8 push ds push cs pop ds cld repz cmpsb pop ds jz found mov dx,0 mov ah,46h int 67h and ah,ah jz found xor ax, ax jmp exit found: mov ax,1 exit: ret EMScheck endp main proc c local handle:word call EMScheck and ax,ax jz sm1 mov ax,4E03h int 67h cmp ah,00 jnz sm2 invoke printf, CStr("size to save all pages: %u",lf), ax mov di,offset buffer push ds pop es mov ax,4E00h int 67h cmp ah,00 jnz sm3 mov si,offset buffer mov ax,4E01h int 67h cmp ah,00 jnz sm4 invoke printf, CStr("Ok",lf) jmp exit sm1: invoke printf, CStr("EMM not found",lf) jmp exit sm2: movzx ax,ah invoke printf, CStr("int 67h, ax=4E03h failed, ah=%X",lf),ax jmp exit sm3: movzx ax,ah invoke printf, CStr("int 67h, ax=4E00h failed, ah=%X",lf),ax jmp exit sm4: movzx ax,ah invoke printf, CStr("int 67h, ax=4E01h failed, ah=%X",lf),ax exit: ret main endp start: mov ax,@data mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx invoke main mov ah,4ch int 21h END start ================================================ FILE: Test/EMS4F.ASM ================================================ ;*** test EMS get/set partial page map .286 .model small option casemap:none .stack 1024 .dosseg CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .data mapstruct label byte dw 4 dw 0A000h dw 0A400h dw 0A800h dw 0AC00h mapstruct2 label byte dw 4 dw 08000h dw 08400h dw 08800h dw 08C00h .data? buffer db 256 dup (?) .code .386 include printf.inc emsstr1 db 'EMMXXXX0',0 EMScheck proc uses si di mov ax, 3567h int 21h mov ax, es or ax, bx jz exit mov dx,1 mov si,offset emsstr1 mov di,000ah mov cx,8 push ds push cs pop ds cld repz cmpsb pop ds jz found mov dx,0 mov ah,46h int 67h and ah,ah jz found xor ax, ax jmp exit found: mov ax,1 exit: ret EMScheck endp main proc c local handle:word call EMScheck and ax,ax jz sm1 ;--- get size to save 4 pages mov bx,4 mov ax,4F02h int 67h cmp ah,00 jnz sm2 mov ah,0 invoke printf, CStr( "size to save 4 pages: %u", lf ), ax ;--- save 4 pages in mapstruct (A000,A400,A800,AC00) mov si,offset mapstruct mov di,offset buffer push ds pop es mov ax,4F00h int 67h cmp ah,00 jz smx movzx ax,ah invoke printf, CStr( "int 67h, ax=4F00h (A000-AFFF) failed, error=%X", lf ), ax smx: ;--- save 4 pages in mapstruct (8000,8400,8800,8C00) mov si,offset mapstruct2 mov di,offset buffer push ds pop es mov ax,4F00h int 67h cmp ah,00 jnz sm31 invoke printf, CStr( "int 67h, ax=4F00h (8000-8FFF) ok", lf ) ;--- restore 4 pages mov si,offset buffer mov ax,4F01h int 67h cmp ah,00 jnz sm4 invoke printf, CStr( "int 67h, ax=4F01h ok",lf ) jmp exit sm1: invoke printf, CStr( "EMM not found", lf ) jmp exit sm2: movzx ax,ah invoke printf, CStr( "int 67h, ax=4F02h failed, error=%X", lf ), ax jmp exit sm31: movzx ax,ah invoke printf, CStr( "int 67h, ax=4F00h (8000-8FFF) failed, error=%X", lf ), ax jmp exit sm4: movzx ax,ah invoke printf, CStr( "int 67h, ax=4F01h failed, error=%X",lf ), ax exit: ret main endp start: mov ax,@data mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx invoke main mov ah,4ch int 21h END start ================================================ FILE: Test/EMS56.ASM ================================================ ;*** test int 67h, ah=56h (alter map and call) .286 .model small .stack 2048 .dosseg option casemap:none .386 lf equ 10 ;extern __acrtused:abs CStr macro text:vararg local x .const x db text,0 .code exitm endm EMS56 struct dwProc dd ? ;far proc to call bSizeNew db ? pMapNew dd ? ;new mapping bSizeOld db ? pMapOld dd ? ;old mapping EMS56 ends log_phys_map struct dwLogPage dw ? ;logical page dwPhysPage dw ? ;segment or phys page log_phys_map ends .data ems56 EMS56 <> MapNew log_phys_map <0,0> log_phys_map <1,1> MapOld log_phys_map <-1,0> log_phys_map <-1,1> .code include main proc c argc:word, argv:word local handle:word local wPages:word local frame:word mov handle,-1 mov ax,3567h int 21h mov ax,bx mov cx,es or ax,cx jz noemm mov bx,2 mov ah,43h ;alloc 2 pages int 67h and ah,ah jnz error2 mov handle,dx invoke printf, CStr("Int 67h, ah=43h, bx=2: Handle %X allocated",lf), handle mov bx,0 mov dx,handle mov ax,4400h ;map to phys page 0 int 67h and ah,ah jnz error4 invoke printf, CStr("Int 67h, ax=4400h, bx=0: log page 0 mapped to phys page 0",lf) mov bx,1 mov dx,handle mov ax,4401h ;map to phys page 1 int 67h and ah,ah jnz error4 invoke printf, CStr("Int 67h, ax=4401h, bx=1: log page 1 mapped to phys page 1",lf) mov ah,41h ;get page frame int 67h and ah,ah jnz error3 mov frame,bx invoke printf, CStr("Int 67h, ah=41h: Page Frame is %X",lf), frame ;--- now copy a small test routine into page 0 of page frame mov es,frame mov di,0 mov si,offset testpr push ds push cs pop ds mov cx,sizeproc rep movsb pop ds ;--- unmap page 0 mov bx,-1 mov dx,handle mov ax,4400h ;unmap page 0 int 67h and ah,ah jnz error4 invoke printf, CStr("Int 67h, ax=4400, bx=-1: phys page 0 unmapped",lf) mov dx,handle mov ax,5602h int 67h mov ax, sp invoke printf, CStr("Int 67h, ax=5602h: additional stack space for call: %X, SP=%X",lf), bx, ax ;--- prepare ax=5600h, DS:SI->EMS56 mov si,offset ems56 mov ax, frame mov word ptr [si].EMS56.dwProc+0, 0 mov word ptr [si].EMS56.dwProc+2, ax mov [si].EMS56.bSizeNew,2 mov word ptr [si].EMS56.pMapNew+0, offset MapNew mov word ptr [si].EMS56.pMapNew+2, ds mov [si].EMS56.bSizeOld,2 mov word ptr [si].EMS56.pMapOld+0, offset MapOld mov word ptr [si].EMS56.pMapOld+2, ds mov dx,handle mov ax,5600h int 67h and ah,ah jnz error5 mov ax,sp invoke printf, CStr("Int 67h, ax=5600h ok, SP after call=%X",lf), ax exit2: invoke printf, CStr(lf) exit: mov dx,handle cmp dx,-1 jz @F mov ah,45h int 67h @@: ret noemm: invoke printf, CStr("EMM not found",lf) jmp exit error2: mov al,ah mov ah,0 invoke printf, CStr("int 67h, ah=43h, bx=%u failed, status=%02X",lf), bx, ax jmp exit error3: mov al,ah mov ah,0 invoke printf, CStr("int 67h, ah=41h failed, status=%02X",lf), ax jmp exit error4: mov al,ah mov ah,0 invoke printf, CStr("int 67h, ah=44h failed, status=%02X",lf), ax jmp exit error5: mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=5600h failed, status=%02X",lf), ax jmp exit main endp ;--- this small routine is copied into page 0 of page frame testpr proc far mov ah,02 mov dl,'*' int 21h ret testpr endp sizeproc equ $ - testpr start: mov ax,@data mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx call main mov ah,4Ch int 21h END start ================================================ FILE: Test/EMS57.ASM ================================================ ;*** EMS move .286 .model tiny option casemap:none option casemap:none .stack 1024 .dosseg CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 ?BUFSIZE equ 10000h ?SIZE equ ?BUFSIZE/4000h ;size in EMS pages .386 EMM57 struct dwSize DD ? ; +0 size of region bSrcTyp DB ? ; +4 src memory type wSrcHdl DW ? ; +5 src handle wSrcOfs DW ? ; +7 src ofs wSrcSeg DW ? ; +9 src segm./log. page bDstTyp DB ? ; +11 dst memory type wDstHdl DW ? ; +12 dst handle wDstOfs DW ? ; +14 dst ofs wDstSeg DW ? ; +16 dst segm./log. page EMM57 ends .data emm57 EMM57 <> .code include printf.inc main proc c local handle:word local wPages:word local wMem:word local frame:word mov ax,3567h int 21h mov ax,bx mov cx,es or ax,cx jnz @F invoke printf, CStr( "EMM not found",lf) jmp exit @@: mov ah,48h mov bx,?BUFSIZE/10h int 21h jnc @F invoke printf, CStr( "DOS memory allocation failed",lf) jmp exit @@: mov wMem, ax mov bx,?SIZE mov ah,43h ;alloc pages int 67h and ah,ah jnz error2 mov handle,dx invoke printf, CStr("Handle %u allocated, clearing content ... ", lf), handle xor di, di mov es, wMem mov eax,12345678h mov cx,?BUFSIZE/4 rep stosd mov si, offset emm57 mov emm57.dwSize, ?BUFSIZE mov emm57.bSrcTyp, 0 mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, es mov emm57.bDstTyp, 1 mov ax,handle mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, 0 mov ax,5700h ;move memory region int 67h and ah,ah jnz error3 invoke printf, CStr( "move to expanded memory ok",lf) xor di, di mov es, wMem xor eax, eax mov cx,?BUFSIZE/4 rep stosd mov si, offset emm57 mov emm57.dwSize, ?BUFSIZE mov ax,handle mov es,wMem mov emm57.wSrcHdl, ax mov emm57.bSrcTyp, 1 mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov emm57.bDstTyp, 0 mov emm57.wDstOfs, 0 mov emm57.wDstSeg, es mov ax,5700h ;move memory region int 67h and ah,ah jnz error3 invoke printf, CStr( "move from expanded memory ok",lf) xor di, di mov es, wMem mov eax, 12345678h mov cx,?BUFSIZE/4 repz scasd jz exit2 invoke printf, CStr( "memory content has changed!",lf) exit2: mov dx,handle mov ah,45h ;free pages int 67h exit: ret error2: movzx ax,ah invoke printf, CStr( "int 67h, ah=43h, bx=%u failed, status=%X",lf), bx, ax jmp exit error3: movzx ax,ah invoke printf, CStr( "int 67h, ah=57h failed, status=%X",lf), ax jmp exit main endp start: mov ax,cs mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx mov dx,es mov ax,ss sub ax,dx mov dx,sp shr dx,4 add ax,dx mov bx,ax mov ah,4ah int 21h invoke main mov ah,4ch int 21h END start ================================================ FILE: Test/EMS57A.ASM ================================================ ;*** test EMS move (int 67h, ah=57h) ;*** move 1024 kB conv to EMS memory ;*** move 1024 kB EMS to EMS memory non-overlapping ;*** move 1024 kB EMS to EMS memory overlapping .286 .model tiny option casemap:none .stack 1024 .dosseg CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 ?SIZE equ 64 ;size in EMS pages (64*16=1024 kB) .386 EMM57 struct dwSize DD ? ; +0 size of region bSrcTyp DB ? ; +4 src memory type wSrcHdl DW ? ; +5 src handle wSrcOfs DW ? ; +7 src ofs wSrcSeg DW ? ; +9 src segm./log. page bDstTyp DB ? ; +11 dst memory type wDstHdl DW ? ; +12 dst handle wDstOfs DW ? ; +14 dst ofs wDstSeg DW ? ; +16 dst segm./log. page EMM57 ends .data emm57 EMM57 <> .code include printf.inc main proc c local handle:word local wPages:word local wMem:word local frame:word mov ax,3567h int 21h mov ax,bx mov cx,es or ax,cx jnz @F invoke printf, CStr("EMM not found",lf) jmp exit @@: xor ax,0 mov wMem, ax mov bx,?SIZE*2 mov ah,43h ;alloc pages int 67h and ah,ah jz @F movzx ax,ah invoke printf, CStr( "int 67h, ah=43h, bx=%X failed, status=%X",lf),bx,ax jmp exit @@: mov handle,dx invoke printf, CStr("Handle %u allocated, size=%u * 16 kB pages",lf), handle, ?SIZE*2 invoke printf, CStr("int 67h, AX=5700h (move), AX=5701h (xchg)",lf) mov si, offset emm57 mov emm57.dwSize, 100000h mov emm57.bSrcTyp, 0 mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov ax,handle mov emm57.bDstTyp, 1 mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, 0 mov ax,5700h ;move memory region int 67h movzx ax,ah invoke printf, CStr( "5700,0->1: move conv to expanded memory, 1MB, status=%X (0 exp)",lf),ax ;--- move from EMS to EMS non-overlapp mov si, offset emm57 mov ax,handle mov emm57.dwSize, ?SIZE*4000h mov emm57.bSrcTyp, 1 mov emm57.wSrcHdl, ax mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov emm57.bDstTyp, 1 mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, ?SIZE mov ax,5700h ;move memory region int 67h movzx ax,ah invoke printf, CStr( "5700,1->1: move EMS to EMS memory (non-overlapp), status=%X (0 exp)",lf),ax ;--- move from EMS to EMS overlapp mov si, offset emm57 mov ax,handle mov emm57.dwSize, ?SIZE*4000h mov emm57.bSrcTyp, 1 mov emm57.wSrcHdl, ax mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov emm57.bDstTyp, 1 mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, ?SIZE/2 mov ax,5700h ;move memory region int 67h movzx ax,ah invoke printf, CStr( "5700,1->1: move EMS to EMS memory (overlapp), status=%X (92 exp)",lf),ax ;--- xchg from EMS to EMS non-overlapp mov si, offset emm57 mov ax,handle mov emm57.dwSize, ?SIZE*4000h mov emm57.bSrcTyp, 1 mov emm57.wSrcHdl, ax mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov emm57.bDstTyp, 1 mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, ?SIZE mov ax,5701h ;xchg memory region int 67h movzx ax,ah invoke printf, CStr( "5701,1->1: xchg EMS to EMS memory (non-overlapp), status=%X (0 exp)",lf),ax ;--- xchg from EMS to EMS overlapp mov si, offset emm57 mov ax,handle mov emm57.dwSize, ?SIZE*4000h mov emm57.bSrcTyp, 1 mov emm57.wSrcHdl, ax mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov emm57.bDstTyp, 1 mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, ?SIZE/2 mov ax,5701h ;xchg memory region int 67h movzx ax,ah invoke printf, CStr( "5701,1->1: xchg EMS to EMS memory (overlapp), status=%X (97 exp)",lf),ax ;--- set invalid region type mov si, offset emm57 mov ax,handle mov emm57.dwSize, ?SIZE*4000h mov emm57.bSrcTyp, 2 mov emm57.wSrcHdl, ax mov emm57.wSrcOfs, 0 mov emm57.wSrcSeg, 0 mov emm57.bDstTyp, 1 mov emm57.wDstHdl, ax mov emm57.wDstOfs, 0 mov emm57.wDstSeg, ?SIZE mov ax,5700h ;move memory region int 67h movzx ax,ah invoke printf, CStr( "5700,2->1: src has invalid type (2), status=%X (98 exp)",lf),ax exit2: mov dx,handle mov ah,45h ;free pages int 67h exit: ret main endp start: mov ax,cs mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx invoke main mov ah,4ch int 21h END start ================================================ FILE: Test/EMS5B.ASM ================================================ ;*** test EMS 5B ;--- 5b00: get alternate map register set ( if BL=0 on ret, es:di will be current pointer to context save area ) ;--- 5b01: set alternate map register set in BL ;--- if BL=0, es:di will be current context save area ;--- 5b02: get size of alternate map register set in dx .286 .model small option casemap:none option casemap:none .stack 1024 .dosseg .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .data wCSize dw 0 .data? buffer db 1024 dup (?) .code include printf.inc memset proc c uses di pMem:ptr, wValue:word, wSize:word mov di, pMem mov ax, wValue mov cx, wSize cld rep stosb ret memset endp main proc c mov ax,3567h int 21h mov ax,bx mov cx,es or ax,cx jnz @F invoke printf, CStr( "EMM not found",lf) jmp exit @@: mov dx, -1 mov ax, 5B02h int 67h mov [wCSize], dx movzx ax, ah invoke printf, CStr("int 67h (ax=5B02h): ah=%X, dx=%X", lf), ax, dx cmp [wCSize], sizeof buffer ja error1 push ds pop es invoke memset, addr buffer, 0, sizeof buffer mov ax, 5B00h int 67h movzx ax, ah movzx bx, bl invoke printf, CStr("int 67h (ax=5B00h): ah=%X, bl=%X, es:di=%X:%X", lf), ax, bx, es, di mov di, offset buffer push ds pop es push es push di mov ax, 5B01h mov bl, 0 int 67h pop cx pop dx movzx ax, ah movzx bx, bl invoke printf, CStr("int 67h (ax=5B01h, bl=0, es:di=%X:%X): ah=%X, bl=%X, es:di=%X:%X", lf), dx, cx, ax, bx, es, di exit: ret error1: invoke printf, CStr("size of context save area too small", lf) jmp exit main endp start: mov ax,@data mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx mov dx,es mov ax,ss sub ax,dx mov dx,sp shr dx,4 add ax,dx mov bx,ax mov ah,4ah int 21h invoke main mov ah,4ch int 21h END start ================================================ FILE: Test/EXC00.ASM ================================================ ;*** EXC 00 .286 .model small .stack 2048 .dosseg .386 .code start: mov dx, -1 mov cx, 8000h div cx mov ah, 4Ch int 21h END start ================================================ FILE: Test/EXC06.ASM ================================================ ;*** EXC 06 - requires a P6+ cpu .286 .model small .stack 2048 .dosseg .686 .code start: ud2 mov ah, 4Ch int 21h END start ================================================ FILE: Test/FRAMERES.ASM ================================================ ;--- create binary: ;--- jwasm -bin -Fo frameres.com frameres.asm .286 .MODEL tiny .CODE org 100h start: mov ax,3567h int 21h mov ax,es ;IV 67h set? or ax,bx jz exit mov ah,41h ;get page frame int 67h and ah,ah ;ok? jz exit mov cx,4 ;cx=size mapping array mov dx,0 ;dx=handle (0=system) mov si,offset map ;ds:si -> mapping array mov ax,5000h int 67h exit: mov ax,4c00h int 21h align 2 map dw -1,0,-1,1,-1,2,-1,3 end start ================================================ FILE: Test/HLTTEST.ASM ================================================ ;*** HLT test (v86) .286 .model tiny .stack 2048 .dosseg .386 include macros.inc .data dwOldInt9 dd 0 bM21 db 0 bIrq db 0 cIrq db 0 .code myint9 proc inc cs:[cIrq] jmp cs:[dwOldInt9] myint9 endp include printf.inc main proc c local mblock:word mov ecx,200000000 @@: dec ecx jnz @B @@: mov ah,1 int 16h jz @F mov ah,0 int 16h jmp @B @@: mov ax, 3509h int 21h mov word ptr dwOldInt9+0, bx mov word ptr dwOldInt9+2, es mov dx, offset myint9 mov ax, 2509h int 21h in al, 21h mov bM21, al mov al, 0FDh out 21h, al mov [cIrq],0 hlt cli nop mov al, [cIrq] mov bIrq, al sti mov al, bM21 out 21h, al push ds lds dx, dwOldInt9 mov ax,2509h int 21h pop ds mov al,bIrq mov ah,0 invoke printf, CStr("%u interrupts counted",10),ax ret main endp start: mov ax, cs mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/HLTTEST2.ASM ================================================ ;*** HLT test (v86) with IF=0 .286 .model tiny .stack 2048 .dosseg .386 include macros.inc .data dwOldInt9 dd 0 cIrq db 0 bM21 db 0 bIrq db 0 .code myint9 proc inc cs:[cIrq] jmp cs:[dwOldInt9] myint9 endp include printf.inc main proc c mov ecx,200000000 @@: dec ecx jnz @B @@: mov ah,1 int 16h jz @F mov ah,0 int 16h jmp @B @@: mov ax, 3509h int 21h mov word ptr dwOldInt9+0, bx mov word ptr dwOldInt9+2, es mov dx, offset myint9 mov ax, 2509h int 21h in al, 21h mov bM21, al mov al, 0FDh out 21h, al mov [cIrq],0 cli nop hlt nop mov al, [cIrq] mov bIrq,al sti mov al,bM21 out 21h,al push ds lds dx, dwOldInt9 mov ax,2509h int 21h pop ds mov al,bIrq mov ah,0 invoke printf, CStr("%u interrupts counted",10),ax ret main endp start: mov ax, cs mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/I15MOVE.ASM ================================================ ;*** move block by int 15h, ah=87h .286 .model tiny .stack 2048 .dosseg .386 lf equ 10 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm .data gdt label byte dq 0 dq 0 wSrcLim dw -1 wSrcA00 dw 0 bSrcA16 db 0 db 093h db 0 db 0 wDstLim dw -1 wDstA00 dw 0 bDstA16 db 0 db 093h db 0 db 0 dq 0 dq 0 .code include printf.inc main proc c local mblock:word mov ah,48h mov bx,4000h ;alloc 256 kB int 21h jc nomem mov mblock,ax ;--- setup descriptors movzx eax,ax shl eax, 4 ;get block's linear address ; add eax, 0FFCh ;??? lea edx, [eax+20000h] push eax push edx mov wSrcA00, ax mov wDstA00, dx shr eax, 16 shr edx, 16 mov bSrcA16, al mov bDstA16, dl pop edx pop eax invoke printf, CStr("src/dst descriptor base = %lXh/%lXh",lf), eax, edx invoke printf, CStr("src/dst descriptor limit = ffffh, ofs GDT=%Xh",lf), offset gdt call clearblock ;--- copy 32kB - should be ok mov cx, 4000h call i15 invoke printf, CStr("int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X",lf), cx, dx, ax call testblock call clearblock ;--- copy 64kB - should be ok mov cx, 8000h call i15 invoke printf, CStr("int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X",lf), cx, dx, ax call testblock call clearblock ;--- this move should fail - max words to copy is 8000h. mov cx, 8001h call i15 invoke printf, CStr("int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X",lf), cx, dx, ax invoke printf, CStr("src/dst descriptor limit = 3fffh",lf) mov wSrcLim, 3fffh mov wDstLim, 3fffh ;--- this move should be ok mov cx, 2000h call i15 invoke printf, CStr("int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X",lf), cx, dx, ax ;--- this move should fail - beyond descriptor limit mov cx, 2001h call i15 invoke printf, CStr("int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X",lf), cx, dx, ax ;--- move with cx=0; MS Emm386 returns with C, AH=2; Jemm accepts a size of 0! mov cx, 0 call i15 invoke printf, CStr("int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X",lf), cx, dx, ax ret nomem: invoke printf, CStr("out of DOS memory",lf) ret i15: push ds pop es mov si, offset gdt mov ah,87h stc int 15h sbb dx,dx and dx,1 movzx ax,ah retn clearblock: cld mov cl,4 mov eax,11111111h mov es, mblock next64kb: push cx xor di, di mov cx, 4000h rep stosd mov dx, es add dx, 1000h mov es, dx add eax,11111111h pop cx dec cl jnz next64kb retn testblock: retn main endp start: mov ax,cs mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx ; tiny model (CS=SS=DS) setup mov bx, sp add bx, 100h shr bx, 4 mov ah, 4Ah int 21h call main mov ax,4c00h int 21h END start ================================================ FILE: Test/INT67.ASM ================================================ ;--- the EMS handler runs slightly differently if IVT vector 67h is hooked! .286 .model tiny .stack 256 .code db 10 dup (0) db "EMMXXXX0" myint67: db 0EAh oldint dd 0 endres equ $ start: mov ax, cs mov ds, ax mov ax, 3567h int 21h mov word ptr oldint+0, bx mov word ptr oldint+2, es mov dx, offset myint67 mov ax, 2567h int 21h mov dx, offset endres add dx, 16-1 shr dx, 4 add dx, 10h mov ax, 3100h int 21h END start ================================================ FILE: Test/INT88.ASM ================================================ ;--- test int# >= 0x80; ;--- the monitor program uses the "PUSH byte const" instruction ;--- to push a sign-extended value as int#; so it has to carefully ;--- use the lowest byte only. .286 .model tiny .stack 256 .dosseg option casemap:none CStr macro text:vararg local sym .const sym db text,0 .code exitm endm .data oldint88 dd 0 .code .386 include printf.inc .286 myint88 proc invoke printf, CStr("inside int 88h, ax=%X bx=%X cx=%X dx=%X si=%X di=%X bp=%X",10), ax, bx, cx, dx, si, di, bp iret myint88 endp start: mov ax,cs mov ds,ax mov ax,3588h int 21h mov word ptr [oldint88+0],bx mov word ptr [oldint88+2],es mov dx, offset myint88 mov ax,2588h int 21h mov ax,0 mov bx,1 mov cx,2 mov dx,3 mov si,4 mov di,5 mov bp,6 int 88h lds dx, [oldint88] mov ax, 2588h int 21h mov ax, 4c00h int 21h END start ================================================ FILE: Test/MACROS.INC ================================================ CStr macro text:VARARG local sym .const sym db text,0 .code exitm endm ================================================ FILE: Test/MAKE.BAT ================================================ @echo off rem the programs may all be created without link step, rem using jwasm's -mz option jwasm -mz %1.asm ================================================ FILE: Test/PRINTF.INC ================================================ ;--- simple printf() implementation ;--- assume ds=dgroup handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ltob PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP printf PROC c uses si di fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push offset @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov [size_],dx mov bx,dx jmp @@L358 @@FC250: cmp BYTE PTR [si],'9' jg @@L362 lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax @@L358: cmp BYTE PTR [si],'0' jge @@FC250 @@L362: mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cbw cmp al,'x' je handle_x ja @@L359 or al,al je done ;\0? sub al,'X' je handle_x sub al,11 je handle_c ;'c' dec al je handle_d ;'d' sub al,5 je handle_i ;'i' sub al,10 je handle_s ;'s' sub al,2 je handle_u ;'u' jmp @@L359 handle_c: ;'c' mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_s: ;'s' mov si,ss:[di] add di,2 jmp @@do_outputstring260 handle_x: ;'X' + 'x' mov bx,16 jmp @@lprt262 handle_d: ;'d' handle_i: ;'i' mov bx,-10 jmp @@lprt262 handle_u: ;'u' mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call @@do_outputstring260 pop ds retn @@do_outputstring260: mov ax,si .while byte ptr [si] inc si .endw xchg ax, si sub ax, si sub [size_],ax cmp BYTE PTR [flag],1 jne @@L360 mov bx,[size_] jmp @@L363 @@F270: mov al,[fill] call handle_char dec bx @@L363: or bx,bx jg @@F270 mov [size_],bx jmp @@L360 @@F273: mov al,[si] call handle_char inc si @@L360: cmp BYTE PTR [si],0 jne @@F273 mov bx,[size_] @@: or bx,bx jle @F mov al,[fill] call handle_char dec bx jmp @B @@: retn printf ENDP ================================================ FILE: Test/README.TXT ================================================ bmintrm: benchmark int 69h real-mode ems4e: get/set page map ems4f: get/set partial page map ems56: alter map and call ems57: EMS copy ems57a: copy EMS to EMS, 1 MB ems5b: alternate map register set exc00: v86 divide error exc06: v86 invalid opcode exception frameres: reset page frame mapping to a known value hlttest: emulation of HLT, IF=1 hlttest2: emulation of HLT, IF=0 i15move: move block using int 15h, ah=87h int67: test monitor if IVT 67h is hooked testdma: read FD in UMB, using int 25h testdma2: read FD in UMB, using int 13h testdma3: read FD in UMB, using int 13h, ensured that DMA buffer/PTE remap must be used. testvds: run various VDS API calls testvds2: test VDS API calls 810B/810C for DMA channel 2 testvds3: test 810B/810C for all channels xmstest: XMS memory moves xmstest2: XMS block allocation xmstest3: test XMS functions 0Eh/8Eh (get handle information) xmstest4: test XMS resize function 8Fh xmstest5: test XMS resize function 0Fh Debug scripts: test0c.deb test stack exception 0Ch test0c1.deb test stack exception 0Ch test0d.deb test GPF test10.deb test floating point exception 10h test11.deb test alignment check exception 11h ================================================ FILE: Test/TEST0C.DEB ================================================ ; test v86 exc 0C a mov sp,ffff pop ax int 3 g=100 q ================================================ FILE: Test/TEST0C1.DEB ================================================ ; test v86 exc 0C a mov sp,1 push ax int 3 g=100 q ================================================ FILE: Test/TEST0D.DEB ================================================ ; test v86 exc 0D a mov ax,[ffff] int 3 g=100 q ================================================ FILE: Test/TEST10.DEB ================================================ ; test v86 exc 10 ; set CR0 NE bit ; enable exceptions in FP control word ; the do an FP pop ; the exception occurs at the NEXT FP instruction (the finit)! a mov eax,cr0 or al,20 mov cr0,eax fstcw [180] and word [180],ffc0 fldcw [180] fst double [200] finit int 3 g=100 q ================================================ FILE: Test/TEST11.DEB ================================================ ; test v86 exc 11 ; set CR0 AM bit ; set EFL AC bit ; then access stack unaligned a mov eax,cr0 or eax,40000 mov cr0,eax and sp,FFFC pushfd pop eax or eax,40000 push eax popfd push ax push eax int 3 g=100 q ================================================ FILE: Test/TESTDMA.ASM ================================================ ;--- test FD read in UMB ;--- using Int 25h .286 .model small .stack 2048 .dosseg .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .code include printf.inc main proc c local stat1:word local stat2:word local wSeg:word mov ax, 5802h ;get status umb int 21h xor ah,ah mov stat1, ax mov ax, 5800h ;get memory alloc strategy int 21h xor ah,ah mov stat2, ax mov bx, 81h ;first high,then low mov ax, 5801h ;set memory alloc strat int 21h mov bx, 1 ;include umbs mov ax, 5803h ;umb link restore int 21h mov bx, 100h ; allocate 4 kB mov ah, 48h int 21h jc exit mov wSeg, ax mov es, ax xor di, di mov cx, 1000h mov al, 00 rep stosb push ds mov ds, wSeg ; ds:bx=transfer buffer mov bx, 0 mov cx, 8 ; 8 sectors to read mov al, 0 ; al=0 -> A: mov dx, 0 ; start sector # int 25h jc error add sp, 2 pop ds invoke printf, CStr("reading drive A: ok",10) jmp exit error: add sp, 2 pop ds invoke printf, CStr("reading drive A: failed, error code=%X",10), ax exit: mov bx, stat1 mov ax, 5803h int 21h mov bx, stat2 mov ax, 5801h ; set memory alloc strag int 21h ret main endp start: mov ax, @data mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/TESTDMA2.ASM ================================================ ;--- test FD read in UMB ;--- using Int 13h .286 .model small .stack 2048 .dosseg .386 ?SIZE equ 1000h CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .code include printf.inc writefile proc c pName:ptr, pBuffer:ptr far16, wSize:word mov dx, pName xor cx, cx mov ax,3c00h int 21h jc fail1 mov bx, ax push ds lds dx, pBuffer mov cx, wSize mov ax, 4000h int 21h pop ds jc fail2 mov ah, 3Eh int 21h ret fail1: invoke printf, CStr("create file failed",10) ret fail2: invoke printf, CStr("write file failed",10) ret writefile endp main proc c local stat1:word local stat2:word local wSeg:word mov ax, 5802h ;get status umb int 21h xor ah,ah mov stat1, ax mov ax, 5800h ;get memory alloc strategy int 21h xor ah,ah mov stat2, ax mov bx, 81h ;first high,then low mov ax, 5801h ;set memory alloc strat int 21h mov bx, 1 ;include umbs mov ax, 5803h ;umb link restore int 21h mov bx, ?SIZE shr 4 ; allocate mem block mov ah, 48h int 21h jc exit mov wSeg, ax mov es, ax xor di, di mov cx, ?SIZE mov al, 00 rep stosb push es mov es, wSeg ; es:bx=transfer buffer mov bx, 0 mov cx, 1 ; cl[0-5]: sector#; ch+cl[6-7]:cylinder mov dh, 0 ; dh=head mov dl, 0 ; dl=0 -> A: mov ax, 0200h or (?SIZE shr 9) ; al=# of sectors to read int 13h pop es jc error invoke printf, CStr("reading drive A: ok",10) mov ax, wSeg invoke writefile, CStr("~XXX.TMP"), ax::bx, (?SIZE shr 9)*200h jmp exit error: movzx ax, ah invoke printf, CStr("reading drive A: failed, error code=%X",10), ax exit: mov bx, stat1 mov ax, 5803h int 21h mov bx, stat2 mov ax, 5801h ; set memory alloc strag int 21h ret main endp start: mov ax, @data mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/TESTDMA3.ASM ================================================ ;--- test FD read in UMB ;--- using Int 13h ;--- this variant ensures that a 64kb border is crossed, ;--- forcing the EMM to either "auto remap" PTEs (EMM386) or ;--- use the DMA buffer (Jemm). .286 .model small .stack 2048 .dosseg option casemap:none .386 ?BSIZE equ 8000h CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 DDS struct dwSize dd ? dwOfs dd ? wSeg dw ? wID dw ? dwPhys dd ? DDS ends .data? dds DDS <> .code include printf.inc writefile proc c pName:ptr, pBuffer:ptr far16, wSize:word mov dx, pName xor cx, cx mov ax,3c00h int 21h jc fail1 mov bx, ax push ds lds dx, pBuffer mov cx, wSize mov ax, 4000h int 21h pop ds jc fail2 mov ah, 3Eh int 21h ret fail1: invoke printf, CStr("create file failed",10) ret fail2: invoke printf, CStr("write file failed",10) ret writefile endp main proc c local stat1:word local stat2:word local wSeg:word ;--- requires VDS xor ax, ax mov es, ax test byte ptr es:[47bh],20h jz novds mov ax,8102h mov dx,0000 int 4bh jnc @F novds: invoke printf, CStr("VDS not installed",lf) jmp exit @@: mov ax, 5802h ;get status umb int 21h xor ah,ah mov stat1, ax mov ax, 5800h ;get memory alloc strategy int 21h xor ah,ah mov stat2, ax mov bx, 81h ;first high,then low mov ax, 5801h ;set memory alloc strat int 21h mov bx, 1 ;include umbs mov ax, 5803h ;umb link restore int 21h mov bx, ?BSIZE shr 4; allocate mem block mov ah, 48h int 21h jnc @F invoke printf, CStr("no free %ukB block found in Upper Memory",10), ?BSIZE shr 10 jmp exit @@: mov wSeg, ax mov es, ax xor di, di mov cx, ?BSIZE mov al, 00 rep stosb ;--- call VDS to see when a 4 KB block isn't contiguous or crosses a 64kb border push ds pop es mov cx, ?BSIZE shr 12 mov dds.wSeg, 0 movzx eax, wSeg shl eax, 4 nexttry: mov di, offset dds mov dds.dwSize, 1000h mov dds.dwOfs, eax push eax mov dx, (1 shl 2) or (1 shl 4) ; b2=1:don't alloc buffer, b4=1:64kb crossing disallowed mov ax, 8103h int 4Bh jc found ; found an address that requires remap/buffer xor dx, dx mov ax, 8104h ; unlock block int 4Bh pop eax add eax, 1000h loop nexttry invoke printf, CStr("no uncontiguous 4kB block found",10) jmp exit found: pop eax shr eax, 4 mov wSeg, ax mov es, wSeg ; es:bx=transfer buffer mov bx, 0 mov cx, 1 ; cl[0-5]: sector#; ch+cl[6-7]:cylinder mov dh, 0 ; dh=head mov dl, 0 ; dl=0 -> A: mov ax, 0208h ; al=# of sectors to read int 13h jc error invoke printf, CStr("reading drive A: ok",10) mov ax, wSeg invoke writefile, CStr("~XXX.TMP"), ax::bx, 200h*8 jmp exit error: movzx ax, ah invoke printf, CStr("reading drive A: failed, error code=%X",10), ax exit: mov bx, stat1 mov ax, 5803h int 21h mov bx, stat2 mov ax, 5801h ; set memory alloc strag int 21h ret main endp start: mov ax, @data mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/TESTVCPI.ASM ================================================ ;--- the purpose of the test is to see if page table 0, returned by int 67h, ax=DE01h ;--- is updated by the VCPI host. That's claimed by the VCPI docs, but of course it isn't. ;--- It's simply not feasible, because the host doesn't know if the table is still "valid". ;--- if segments are defined BEFORE .model, ;--- the alignment can be set differently (JWasm only). _DATA segment para public 'DATA' _DATA ends _BSS segment para public 'BSS' _BSS ends .286 .model tiny .stack 2048 .dosseg .386P option casemap:none ?DISPPTE equ 0 ;1=display PTE entries before/after mapping change. ?ENTRIES equ 0F0h ;PTE entries to display ?DEB386 equ 1 ;1=support (w)deb386 interface ?DISPEXC equ 0 ;display exceptions in ring 0 ;DPMI real mode call structure - int 31h, ax=300h is emulated. RMCS struct union rEDI dd ? ;+0 rDI dw ? ends union rESI dd ? ;+4 rSI dw ? ends union rEBP dd ? ;+8 rBP dw ? ends RESERVE dd ? ;+12 union rEBX dd ? ;+16 rBX dw ? ends union rEDX dd ? ;+20 rDX dw ? ends union rECX dd ? ;+24 rCX dw ? ends union rEAX dd ? ;+28 rAX dw ? ends rFlags dw ? ;+32 rES dw ? ;+34 rDS dw ? ;+36 rFS dw ? ;+38 rGS dw ? ;+40 union rCSIP dd ? ;+42 struct rIP dw ? rCS dw ? ends ends union rSSSP dd ? ;+46 struct rSP dw ? rSS dw ? ends ends RMCS ends include vcpi.inc CStr macro text:vararg local sym .const sym db text,0 .code exitm endm if ?DEB386 D386_Identify equ 43h ; returns debugger identification D386_Prepare_PMode equ 44h ; partially prepare for protected mode operation D386_Real_Mode_Init equ 45h ; tell kd we're done PMINIT_INIT_IDT equ 0 ; (ES:EDI) = pointer to PM IDT D386_Id equ 0F386h ; debugger identification code endif lf equ 10 cr equ 13 .data mygdt label byte ;GDT db 1*8 dup (0) ;+00h null descriptor restab db 3*8 dup (0) ;+08h descriptors reserved for vcpi host pmcs desc <0ffffh,0,0,9Ah,0,0> ;+20h pmds desc <0ffffh,0,0,92h,0,0> ;+28h pmtr desc <068h-1,0,0,89h,0,0> ;selector for TR ;+38h pmflat desc <0ffffh,0,0,92h,8fh,0> ;+30h if ?DEB386 kddesc label dword ;+48 db 3*8 dup (0) KDSEL equ kddesc - mygdt endif SIZGDT equ $ - mygdt CSR0 equ pmcs - mygdt DSR0 equ pmds - mygdt TRSEL equ pmtr - mygdt FLATSEL equ pmflat - mygdt HOSTCS equ restab - mygdt pdgdt label fword dw SIZGDT-1 ;limit GDTR basegdt dd offset mygdt ;base GDTR pdidt label fword dw sizeof myidt-1 ;limit IDTR baseidt dd offset myidt ;base IDTR ;--- far32 address of VCPI protected-mode API vcpiv label fword vcpiofs dd 0 dw HOSTCS ;selector for VCPI host code segment dw 0 msw V86toPM <0, offset pdgdt, offset pdidt, 0, TRSEL, offset pmentry, CSR0> segcs dw 0 ;segment CS segds dw 0 ;segment DS ptadr dw 0 ;segment page table 0 vcpiend dw 0 ;offset in pagetab 0 where free space begins if ?DEB386 KD_DEB386 equ 2 bKrnlDbg db 0 dfDbgEntry df 0 endif .data? myidt db 100h*8 dup (?) ptab db 1000h dup (?) taskseg TSSSEG <> ;task state segment rmcs RMCS <> .code include printf.inc println proc invoke printf, CStr(10) ret println endp if ?DISPEXC ;------ FEDCBA9876543210 FEDCBA9876543210 excv dw 0111110100000000b, 0000000000000110b ?EXC = 0 @defexc macro push ?EXC jmp defexc ?EXC = ?EXC + 1 endm exceptions: rept 32 @defexc endm EXCFRAME1 struct dwEbp dd ? wExc dw ?,? dwErr dd ? dwEip dd ? wCs dw ?,? dwEfl dd ? EXCFRAME1 ends EXCFRAME2 struct dwEbp dd ? wExc dw ?,? dwEip dd ? wCs dw ?,? dwEfl dd ? EXCFRAME2 ends defexc: push ebp mov ebp,esp mov ax, [ebp].EXCFRAME1.wExc bt cs:[excv], ax jnc @F invoke printf, CStr("exc %X at %X:%lX, errc=%lX",lf), [ebp].EXCFRAME1.wExc, [ebp].EXCFRAME1.wCs, [ebp].EXCFRAME1.dwEip, [ebp].EXCFRAME1.dwErr jmp de2 @@: invoke printf, CStr("exc %X at %X:%lX",lf), [ebp].EXCFRAME2.wExc, [ebp].EXCFRAME2.wCs, [ebp].EXCFRAME2.dwEip de2: jmp $ ; just stop, we cannot continue endif defint proc near push eax mov al,0Bh out 0A0h,al in al,0A0h and al,al jz @F mov al,20h out 0a0h,al @@: mov al,0Bh out 20h,al in al,20h and al,al jz @F mov al,20h out 20h,al @@: pop eax iretd defint endp int31 proc cmp ax,300h jz is300 jmp exit is300: pushad push di call pm2rm mov [intno],bl pop di push di mov esi,es:[di].RMCS.rESI mov ebp,es:[di].RMCS.rEBP mov ebx,es:[di].RMCS.rEBX mov ecx,es:[di].RMCS.rECX mov edx,es:[di].RMCS.rEDX mov eax,es:[di].RMCS.rEAX mov ds,es:[di].RMCS.rDS mov edi,es:[di].RMCS.rEDI db 0CDh intno db 0 push esi push eax push ebx call rm2pm pop ebx pop eax pop esi pop di mov es:[di].RMCS.rESI, esi mov es:[di].RMCS.rEBP, ebp mov es:[di].RMCS.rEDX, edx mov es:[di].RMCS.rECX, ecx mov es:[di].RMCS.rEBX, ebx mov es:[di].RMCS.rEAX, eax popad push ax lahf mov [esp+2*4+2],ah pop ax exit: iretd int31 endp int21 proc push es push di mov rmcs.rEAX, eax mov rmcs.rEBX, ebx mov rmcs.rECX, ecx mov rmcs.rEDX, edx mov ax,[segds] mov rmcs.RMCS.rDS, ax mov di, offset rmcs push ds pop es mov bl,21h mov ax,300h int 31h pop di pop es push ax lahf mov [esp+2*4+2],ah pop ax iretd int21 endp ;--- display PTEs of 1. MB DispPTEs proc uses es si invoke printf, CStr("Paging Table",lf) mov es,[ptadr] mov si,0000 mov cx,?ENTRIES nextitem: push cx test si,01Fh jnz @F mov ax,si shr ax,2 invoke printf, CStr("%04X: "), ax @@: mov eax,es:[si+0] push ax invoke printf,CStr("%8lX "),eax pop ax add si,4 test si,1Fh jnz @F invoke println @@: pop cx loop nextitem ret DispPTEs endp ;--- fill VCPI comm structure ;--- set descriptors and IDT setdescriptors proc mov ax,cs movzx eax,ax shl eax, 4 mov pmcs.A0015,ax shr eax,16 mov pmcs.A1623,al mov ax,ds movzx eax,ax shl eax, 4 add basegdt, eax add baseidt, eax add msw._Gdtr, eax add msw._Idtr, eax mov pmds.A0015,ax shr eax,16 mov pmds.A1623,al mov ax,ds movzx eax,ax shl eax, 4 add eax, offset taskseg mov pmtr.A0015,ax shr eax,16 mov pmtr.A1623,al mov dword ptr taskseg.dfStk0, esp mov word ptr taskseg.dfStk0+4, DSR0 mov bx,0 mov di,offset myidt if ?DISPEXC mov ax,offset exceptions nextitem: mov word ptr [di+0],ax mov word ptr [di+2],CSR0 mov word ptr [di+4],0EE00h mov word ptr [di+6],0 add di,8 add ax,4 inc bl cmp bl,20h jnz nextitem endif nextitem2: mov word ptr [di+0],offset defint mov word ptr [di+2],CSR0 mov word ptr [di+4],0EE00h mov word ptr [di+6],0 add di,8 inc bl jnz nextitem2 mov word ptr myidt+21h*8,offset int21 mov word ptr myidt+31h*8,offset int31 ret setdescriptors endp ;--- jump to pm rm2pm proc mov pmtr.attrib,89h cli pushf pop ax and ah,03Fh ;clear NT push ax popf pushf mov ax, ds movzx eax, ax shl eax, 4 lea esi, [eax + offset msw] movzx ebx,sp mov ax,0DE0Ch int 67h pmentry:: ;now in protected mode mov ax,DSR0 mov ss,ax mov esp,ebx mov ds,ax mov es,ax popf ret rm2pm endp ;--- jump to v86 pm2rm proc movzx edx,sp movzx eax, [segds] pushd 0 ;gs pushd 0 ;fs push eax ;ds push eax ;es push eax ;ss push edx ;esp pushd 2 ;efl movzx eax, [segcs] push eax pushd offset rmentr clts ;clear task switched flag mov ax, FLATSEL mov ds,ax ;DS must be FLAT mov ax,0DE0Ch call fword ptr ss:[vcpiv] rmentr: ;back in v86-mode sti ret pm2rm endp ;--- switch to protected-mode ;--- call a real-mode int ;--- finally, switch back to v86-mode pmgo proc cli mov [segcs],cs mov [segds],ds call rm2pm mov ax,FLATSEL ;4G selector -> ES mov es,ax if ?DEB386 cmp bKrnlDbg,KD_DEB386 jnz @F push ds pop es mov edi, offset myidt ;es:edi=idt mov al, PMINIT_INIT_IDT call [dfDbgEntry] int 3 @@: endif invoke printf, CStr("hello from pm",10) ;--- now map another page into physical page 0 mov rmcs.rDX,0 ; system handle mov rmcs.rAX,4400h ; map page into physical page 0 mov rmcs.rBX,0 ; logical page 0 mov di,offset rmcs mov bl,67h mov ax,300h int 31h call pm2rm if ?DEB386 cmp bKrnlDbg,KD_DEB386 jnz @F mov ah, D386_Real_Mode_Init int 68h @@: endif ret pmgo endp ;--- VCPI host was detected. ;--- switch to protected-mode and back runvcpi proc near mov ax,0DE00h ;is vcpi supported? int 67h cmp ah,00 jnz vcpi_err pvcpi1: call setdescriptors push ds pop es if ?DEB386 mov ah,D386_Identify int 68h cmp ax, D386_Id jnz kd_done mov bKrnlDbg, KD_DEB386 mov bx, FLATSEL mov cx, KDSEL mov dx, 0 ; no GDT sel mov si, offset mygdt ; ds:si=gdt mov di, offset myidt ; es:di=idt mov ah, D386_Prepare_PMode int 68h mov dword ptr [dfDbgEntry+0], edi mov word ptr [dfDbgEntry+4], es push ds pop es endif kd_done: mov es,[ptadr] xor di,di ;ES:DI -> page table 0 mov si,offset restab;DS:SI -> 3 free GDT descriptors mov ax,0DE01h ;get protected mode interface int 67h cmp ah,00 jnz vcpi_err1 mov [vcpiofs],ebx mov vcpiend,di if ?DISPPTE call DispPTEs endif call pmgo if ?DISPPTE call DispPTEs endif exit: ret vcpi_err: invoke printf, CStr("no VCPI host found",lf) jmp exit vcpi_err1: invoke printf, CStr("VCPI func DE01 failed",lf) jmp exit runvcpi endp ;--- get cmdline parameters getparm proc near ret getparm endp main proc c mov di, offset ptab mov cx, (sizeof ptab) / 4 push ds pop es xor eax,eax rep stosd mov ax,3567h int 21h mov ax,es or ax,bx jnz main3 invoke printf, CStr("int 67h is zero (no EMM)",lf) mov al,01 jmp exit main3: ;get memory for page tables mov bx,400h mov ah,48h int 21h jc exit mov bx,ax movzx eax,ax shl eax, 4 add eax,4096-1 ;page directory must be page aligned and ax,0f000h ;clear bits 0-11 mov msw._CR3,eax mov edi,eax add edi,1000h shr eax,4 mov es,ax ;page directory 1. entry or di,1+2+4 ;set present, r/w, user mov es:[0000],edi ;set PDE for 0-3FFFFFh mov ax,es add ax,100h mov [ptadr],ax call runvcpi mov al,00 exit: ret main endp start: mov ax,cs mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx ;make SS=DS mov ax,ds mov cx,cs sub ax,cx add ax,10h shr bx,4 add bx,ax mov ah,4ah int 21h ;--- clear uninitialized data push es push ds pop es mov di, offset myidt mov cx, sp sub cx, di shr cx, 1 xor ax, ax cld rep stosw pop es call main mov ax,4c00h int 21h END start ================================================ FILE: Test/TESTVDS.ASM ================================================ ;--- VDS API test ;--- 8103/8104: lock/unlock region ;--- 8107/8108/8109/810A: request/release DMA buffer, copy into/out of DMA buffer ;--- 8105/8106 scatter/gather lock/unlock .286 .model small .stack 2048 .dosseg .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 DDS struct dwSize dd ? dwOfs dd ? wSeg dw ? wID dw ? dwPhys dd ? DDS ends EDDS struct dwSize dd ? dwOfs dd ? wSeg dw ? dw ? numAvl dw ? numUsed dw ? ;after numUsed, PTEs are starting EDDS ends .data dds_ DDS <> edds_ EDDS <> dd 64 dup (0) ;max 32 entries .code include printf.inc RsvdFunction proc ;--- call reserved VDS function 8101 mov dx,0 mov ax,8101h stc int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8101h failed - good, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8101h ok ? - bad",lf) .endif ;--- call reserved VDS function 810D mov dx,0 mov ax,810Dh stc int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=810Dh failed - good, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=810Dh ok ? - bad",lf) .endif ret RsvdFunction endp ;--- 8103: lock a region ;--- 8104: unlock a region LockUnlock proc local bFail:byte ;--- 8103/8104: lock a region with size 1000h, not crossing a page boundary mov di,offset dds_ mov dds_.dwSize,1000h mov dds_.dwOfs,0 mov dds_.wSeg,0C000h mov dx,0 mov ax,8103h ;lock region int 4bh lahf mov bFail,ah .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8103h, dx=0 [size=1000h, wSeg=C000] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8103h, dx=0 [size=1000h, wSeg=C000] ok",lf) .endif invoke printf, CStr("DDS.dwPhys=%lX, DDS.wID=%X",lf), dds_.dwPhys, dds_.wID .if (!(bFail & 1)) mov di,offset dds_ mov dx,0 mov ax,8104h ;unlock region int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8104h [size=1000h, wSeg=C000] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8104h [size=1000h, wSeg=C000] ok",lf) .endif invoke printf, CStr("DDS.dwPhys=%lX, DDS.wID=%X",lf), dds_.dwPhys, dds_.wID .endif ;--- 8103/8104: lock a region with size 8000h in UMB region mov di,offset dds_ mov dds_.dwSize,8000h mov dds_.dwOfs,0 mov dds_.wSeg,0CC00h mov dx,0 mov ax,8103h ;lock region int 4bh lahf mov bFail,ah .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8103h, dx=0 [size=8000h, wSeg=CC00] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8103h, dx=0 [size=8000h, wSeg=CC00] ok",lf) .endif invoke printf, CStr("DDS.dwPhys=%lX, DDS.wID=%X",lf), dds_.dwPhys, dds_.wID .if (!(bFail & 1)) mov di,offset dds_ mov dx,0 mov ax,8104h ;unlock region int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8104h [size=8000h, wSeg=CC00] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8104h [size=8000h, wSeg=CC00] ok",lf) .endif invoke printf, CStr("DDS.dwPhys=%lX, DDS.wID=%X",lf), dds_.dwPhys, dds_.wID .endif ;--- 8104: unlock a region with an invalid buffer ID mov dds_.wID, -1 mov dx,0 mov ax,8104h ;unlock region int 4bh .if (CARRY?) invoke printf, CStr("int 4B, ax=8104h with buffer ID FFFF failed - good",lf) .else invoke printf, CStr("int 4B, ax=8104h with buffer ID FFFF ok ? - bad",lf) .endif ;--- 8103/8104: lock a UMB region with size 20000h - most likely fails mov di,offset dds_ mov dds_.dwSize,20000h mov dds_.dwOfs,0 mov dds_.wSeg,0C000h mov dx,4 ;disable buffering mov ax,8103h ;lock region int 4bh lahf mov bFail, ah .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8103h dx=4 [dds.size=20000h, wSeg=C000] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8103h dx=4 [dds.size=20000h, wSeg=C000] ok",lf) .endif invoke printf, CStr("DDS.dwSize=%lX",lf), dds_.dwSize invoke printf, CStr("DDS.dwOfs=%lX",lf), dds_.dwOfs invoke printf, CStr("DDS.wSeg=%X",lf), dds_.wSeg invoke printf, CStr("DDS.dwPhys=%lX",lf), dds_.dwPhys .if (!(bFail & 1)) mov di,offset dds_ mov dx,0 mov ax,8104h ;unlock region int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8104h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8104h ok",lf) .endif .endif ret LockUnlock endp ;--- 8107 - request a DMA buffer, size 32 kb ;--- 8108 - release DMA buffer ;--- 8109 - copy into DMA buffer ;--- 810A - copy out of DMA buffer DMABuffer proc local bFail:byte mov di,offset dds_ mov dds_.dwSize,8000h ; mov dds_.dwOfs,0 ; needed if copy into buffer is requested ; mov dds_.wSeg,0C000h ; needed if copy into buffer is requested mov dx,0 ;don't copy into buffer mov ax,8107h ;request DMA buffer int 4bh lahf mov bFail, ah .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8107h dx=0 [dds.size=8000h] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8107h dx=0 [dds.size=8000h] ok",lf) .endif invoke printf, CStr("DDS.wID=%X, DDS.dwPhys=%lX",lf), dds_.wID, dds_.dwPhys .if (!(bFail & 1)) if 0 mov dx,0 mov ax,8102h ;"get version" (buffer size in SI::DI) int 4bh invoke printf, CStr("maximum dma buffer size after Int 4B, ax=8107: %lX",lf), si::di endif mov di,offset dds_ mov dds_.dwSize,1000h mov dds_.dwOfs,0 mov dds_.wSeg, ds mov dx,0 ;must be 0 xor bx,bx ;bx:cx == offset in buffer xor cx,cx mov ax,8109h ;copy into DMA buffer int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8109h [size=1000h] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8109h [size=1000h] ok",lf) .endif mov di,offset dds_ mov dx,0 ;must be 0 xor bx,bx ;bx:cx == offset in buffer xor cx,cx mov ax,810Ah ;copy out of DMA buffer int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=810Ah [size=1000h] failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=810Ah [size=1000h] ok",lf) .endif mov dx,0 ;dont copy out of buffer mov ax,8108h ;release DMA buffer int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8108h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8108h ok",lf) .endif .endif ret DMABuffer endp ;--- several variants of 8105/8106: scatter/gather lock ScatterGather proc ;--- 8105/8106: scatter/gather lock with PTEs, sufficient entries mov di,offset edds_ mov [di].EDDS.dwSize,10000h mov [di].EDDS.dwOfs,0h mov [di].EDDS.wSeg,0C800h mov [di].EDDS.numAvl,16 mov dx,40h mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=40h reg=C800:10000 failed, AL=%X, size=%lX, numUsed=%X",lf),ax, es:[di].EDDS.dwSize, es:[di].EDDS.numUsed .else invoke printf, CStr("int 4B, ax=8105h, dx=40h reg=C800:10000 ok",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd push cx invoke printf, CStr("%lX "), eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ;--- 8105/8106: scatter/gather lock with PTEs, insufficient entries mov di,offset edds_ mov [di].EDDS.dwSize,10000h mov [di].EDDS.dwOfs,0h mov [di].EDDS.wSeg,0C800h mov [di].EDDS.numAvl,9h mov dx,40h mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=40h reg=C800:10000 failed [good], AL=%X, size=%lX, numused=%X",lf), ax, es:[di].EDDS.dwSize, es:[di].EDDS.numUsed .else invoke printf, CStr("int 4B, ax=8105h, dx=40h reg=C800:10000 ok [bad]",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd push cx invoke printf, CStr("%lX "), eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ;--- 8105/8106: scatter/gather lock with PTEs, region size 0 mov di,offset edds_ mov [di].EDDS.dwSize,0 mov [di].EDDS.dwOfs,0F000h mov [di].EDDS.wSeg,0C000h mov [di].EDDS.numAvl,4h mov dx,40h mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=40h reg=CF000:0 failed, AL=%X, size=%lX, numUsed=%X",lf), ax, es:[di].EDDS.dwSize, es:[di].EDDS.numUsed .else invoke printf, CStr("int 4B, ax=8105h, dx=40h reg=CF000:0 ok",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd push cx invoke printf, CStr("%lX "), eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ;--- 8105/8106: scatter/gather lock without PTEs, size 64 kB mov di,offset edds_ mov [di].EDDS.dwSize,10000h mov [di].EDDS.dwOfs,00h mov [di].EDDS.wSeg,0C800h mov [di].EDDS.numAvl,8 mov dx,0 mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=C800:10000 failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=C800:10000 ok",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd mov edx, eax lodsd push cx invoke printf, CStr("%lX:%lX "), edx, eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ;--- 8105/8106: scatter/gather lock without PTEs, size 5 byte mov di,offset edds_ mov [di].EDDS.dwSize,5 mov [di].EDDS.dwOfs,00h mov [di].EDDS.wSeg,0C800h mov [di].EDDS.numAvl,4 mov dx,0 mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=C800:5 failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=C800:5 ok",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd mov edx, eax lodsd push cx invoke printf, CStr("%lX:%lX "), edx, eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ;--- 8105/8106: scatter/gather lock without PTEs, size 2 byte, starting at CEFFF mov di,offset edds_ mov [di].EDDS.dwSize,2 mov [di].EDDS.dwOfs,0EFFFh mov [di].EDDS.wSeg,0C000h mov [di].EDDS.numAvl,4 mov dx,0 mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=CEFFF:2 failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=CEFFF:2 ok",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd mov edx, eax lodsd push cx invoke printf, CStr("%lX:%lX "), edx, eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ;--- 8105/8106: scatter/gather lock without PTEs, size 0 byte mov di,offset edds_ mov [di].EDDS.dwSize,0 mov [di].EDDS.dwOfs,0F000h mov [di].EDDS.wSeg,0C000h mov [di].EDDS.numAvl,4 mov dx,0 mov ax,8105h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=CF000:0 failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8105h, dx=0 reg=CF000:0 ok",lf) mov cx, es:[di].EDDS.numUsed lea si,[di+sizeof EDDS] .while cx lodsd mov edx, eax lodsd push cx invoke printf, CStr("%lX:%lX "), edx, eax pop cx dec cx .endw invoke printf, CStr(lf) mov dx,0 mov ax,8106h int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=8106h failed, AL=%X",lf), ax .else invoke printf, CStr("int 4B, ax=8106h ok",lf) .endif .endif ret ScatterGather endp main proc c local version:word local product:word local revision:word local dmasize:dword local flags:word xor ax, ax mov es, ax test byte ptr es:[47bh],20h jz novds mov ax,8102h mov dx,0000 int 4bh jnc @F novds: invoke printf, CStr("VDS not installed",lf) jmp exit @@: mov version,ax mov product,bx mov revision,cx mov word ptr dmasize+0,di mov word ptr dmasize+2,si mov flags,dx invoke printf, CStr("version: %u.%u",lf), byte ptr version+1, byte ptr version+0 invoke printf, CStr("product number/revision: %X/%X",lf), product, revision invoke printf, CStr("maximum dma buffer size: %lX",lf), dmasize invoke printf, CStr("flags: %X",lf), flags mov ax,ds mov es,ax call RsvdFunction invoke printf, CStr(lf) call LockUnlock invoke printf, CStr(lf) call DMABuffer invoke printf, CStr(lf) call ScatterGather exit: ret main endp start: mov ax, @data mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/TESTVDS2.ASM ================================================ ;--- VDS API test; test disable/enable auto DMA translation (810B/810C) .286 .model small .stack 2048 .dosseg .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .code include printf.inc main proc c local channel:word xor ax, ax mov es, ax test byte ptr es:[47bh],20h jz novds mov ax,8102h mov dx,0000 int 4bh jnc @F novds: invoke printf, CStr("VDS not installed",lf) jmp exit @@: ;--- disable DMA translation for channel 2 mov channel, 2 nextchannel: mov dx, 0 mov bx, channel mov ax,810Bh int 4bh .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=810Bh, BX=%u failed, AL=%X",lf), bx, ax .else invoke printf, CStr("int 4B, ax=810Bh, BX=%u ok",lf), bx .endif ;--- reenable DMA translation mov dx, 0 mov bx, channel mov ax,810Ch int 4bh setz cl .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=810Ch, BX=%u failed, AL=%X",lf), bx, ax .else invoke printf, CStr("int 4B, ax=810Ch, BX=%u ok (ZF=%u)",lf), bx, cl .endif ;--- the second call should fail! mov dx, 0 mov bx, channel mov ax,810Ch int 4bh setz cl .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=810Ch, BX=%u failed, AL=%X",lf), bx, ax .else invoke printf, CStr("int 4B, ax=810Ch, BX=%u ok (ZF=%u)",lf), bx, cl .endif inc channel cmp channel, 4 jb nextchannel exit: ret main endp start: mov ax, @data mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/TESTVDS3.ASM ================================================ ;--- VDS API test; display ISA DMA translation status for channels 0-7 .286 .model small .stack 2048 .dosseg .386 CStr macro text:vararg local sym .const sym db text,0 .code exitm endm lf equ 10 .code include printf.inc main proc c local channel:word xor ax, ax mov es, ax test byte ptr es:[47bh],20h jz novds mov ax,8102h mov dx,0000 int 4bh jnc @F novds: invoke printf, CStr("VDS not installed",lf) jmp exit @@: ;--- strategy: ;--- 1. call enable DMA translation ;--- should fail, since disable cnt should be zero mov channel, 0 nextchannel: mov dx, 0 mov bx, channel mov ax,810Ch int 4bh .if (CARRY?) invoke printf, CStr("channel %u: DMA translation is active",lf), bx .else invoke printf, CStr("channel %u: DMA translation is disabled",lf), bx ;--- restore disable cnt mov dx, 0 mov bx, channel mov ax,810Bh int 4bh setz cl .if (CARRY?) cbw invoke printf, CStr("int 4B, ax=810Bh, BX=%u failed, AL=%X",lf), bx, ax .endif .endif inc channel cmp channel, 8 jb nextchannel exit: ret main endp start: mov ax, @data mov ds, ax mov bx, ss sub bx, ax shl bx, 4 mov ss, ax add sp, bx call main mov ah, 4Ch int 21h END start ================================================ FILE: Test/TIMERMS.INC ================================================ ;--- gettimer() returns timer in EAX in ms _GetTimerValue proc push ds push 0 pop ds tryagain: mov edx,ds:[46ch] mov al,0C2h ;read timer 0 status + value low/high out 43h, al xchg edx, edx in al,40h mov cl,al ;CL = status xchg edx, edx in al,40h mov ah, al ;AH = value low xchg edx, edx in al,40h ;AL = value high test cl,40h ;was latch valid? jnz tryagain cmp edx,ds:[046ch] ;did an interrupt occur in the meantime? jnz tryagain ;then do it again! xchg al,ah ;--- usually (counter mode 3) the timer is set to count down *twice*! ;--- however, sometimes counter mode 2 is set! mov ch,cl and ch,0110B ;bit 1+2 relevant cmp ch,0110B ;counter mode 3? jnz @F ;--- in mode 3, PIN status of OUT0 will become bit 15 shr ax,1 and cl,80h or ah, cl @@: ;--- now the counter is in AX (counts from FFFF to 0000) neg ax ;--- now the count is from 0 to FFFF pop ds ret _GetTimerValue endp gettimer proc call _GetTimerValue ;--- the timer ticks are in EDX:AX, timer counts down ;--- a 16bit value with 1,193,180 Hz -> 1193180/65536 = 18.20648 Hz ;--- which are 54.83 ms ;--- to convert in ms: ;--- 1. subticks in ms: AX / 1193 ;--- 2. ticks in ms: EDX * 55 ;--- 3. total 1+2 push edx movzx eax,ax ;step 1 cdq mov ecx, 1193 div ecx mov ecx, eax pop eax ;step 2 mov edx, 55 mul edx add eax, ecx ;step 3 adc edx, 0 ret gettimer endp ================================================ FILE: Test/VCPI.INC ================================================ desc struct limit dw ? A0015 dw ? A1623 db ? attrib db ? lim_gr db ? A2431 db ? desc ends ;--- VCPI structure for switch to protected mode V86toPM struct _CR3 dd ? ;CR3 _Gdtr dd ? ;linear address PD for GDTR _Idtr dd ? ;linear address PD for IDTR _Ldtr dw ? ;LDTR _Tr dw ? ;TR _Eip dd ? ;EIP _Cs dd ? ;CS V86toPM ends IRETV86 struct _Eip dd ? _Cs dd ? _Efl dd ? _Esp dd ? _Ss dd ? _Es dd ? _Ds dd ? _Fs dd ? _Gs dd ? IRETV86 ends TSSSEG struct dwLink dd ? dfStk0 df ? ;+04 dw ? dfStk1 df ? ;+0C dw ? dfStk2 df ? ;+14 dw ? _CR3 dd ? ;+1C _Eip dd ? ;+20 _Efl dd ? ;+24 _Eax dd ? ;+28 _Ecx dd ? ;+2C _Edx dd ? ;+30 _Ebx dd ? ;+34 _Esp dd ? ;+38 _Ebp dd ? ;+3C _Esi dd ? ;+40 _Edi dd ? ;+44 _ES dd ? ;+48 _CS dd ? ;+4C _SS dd ? ;+50 _DS dd ? ;+54 _FS dd ? ;+58 _GS dd ? ;+5C _LDT dd ? ;+60 wFlags dw ? ;+64 wOffs dw ? ;+66 TSSSEG ends ================================================ FILE: Test/XMSTEST.ASM ================================================ ;--- XMSTEST: test xms memory moves ;--- Public Domain. ;--- to be assembled with JWasm or Masm v6. .model tiny .386 .dosseg .stack 2048 lf equ 10 ;--- XMS handle table XMSHT struct db ? bSize db ? wHdls dw ? dwArray dd ? XMSHT ends ;--- XMS handle XMSH struct bFlags db ? ;flags, see below bLocks db ? ;number of locks dwAddr dd ? ;addr in KB dwSize dd ? ;size in KB XMSH ends XMSF_FREEB equ 1 ;free block XMSF_USEDB equ 2 ;used block XMSF_FREEH equ 4 ;free handle ;--- define a string constant CStr macro string:vararg local xxx .const xxx db string db 0 .code exitm endm ;--- display word decimal @wordout_d macro number,format mov cl,format ifidni , else mov ax,number endif call _wordout_d endm ;--- display dword decimal @dwordout_d macro number,format ifnb mov ax,word ptr number+0 mov dx,word ptr number+2 endif mov cl, format call _dwordout_d endm .data xmsadr dd 0 ;XMS host call address dwTotal dd 0 ;total size EMBs freehdls dw 0 ;count free handles wVersion dw 0 ;XMS version bFlags db 0 ;flags FL_NOSIZENULL equ 1 FL_NOUSEDEMBS equ 2 FL_NOFREEEMBS equ 4 FL_FREEHANDLES equ 8 .code assume DS:DGROUP include printf.inc protocol proc push bx invoke printf, CStr("%s"), dx pop ax cmp al,0 jz @F mov ah,0 invoke printf, CStr(", failed, bl=%02X",lf), ax ret @@: invoke printf, CStr(", ok",lf) ret protocol endp xms_move struct len dd ? ; block length in bytes src_handle dw ? ; source handle src_offset dd ? ; offset into source dest_handle dw ? ; destination handle dest_offset dd ? ; offset into destination xms_move ends ;--- test xms move block function movetest proc local handle:word local emm:xms_move mov ah,9 mov dx,64 mov bl,0 call [xmsadr] cmp bl,0 jnz failed mov handle, dx ;--- test 1 mov dx,handle mov emm.len, 10000 mov emm.src_handle, dx mov emm.src_offset, 0 mov emm.dest_handle, dx mov emm.dest_offset, 10000 lea si, emm mov ah,0bh mov bl,0 call [xmsadr] mov dx, CStr("XMS block move, src < dest && src+len < dest") call protocol ;--- test 2 mov dx,handle mov emm.len, 20000 mov emm.src_handle, dx mov emm.src_offset, 0 mov emm.dest_handle, dx mov emm.dest_offset, 10000 lea si, emm mov ah,0bh mov bl,0 call [xmsadr] mov dx, CStr("XMS block move, src < dest && src+len > dest") call protocol ;--- test 3 mov dx,handle mov emm.len, 10000 mov emm.src_handle, dx mov emm.src_offset, 10000 mov emm.dest_handle, dx mov emm.dest_offset, 0 lea si, emm mov ah,0bh mov bl,0 call [xmsadr] mov dx, CStr("XMS block move, src > dest && src > dest+len") call protocol ;--- test 4 mov dx,handle mov emm.len, 20000 mov emm.src_handle, dx mov emm.src_offset, 10000 mov emm.dest_handle, dx mov emm.dest_offset, 0 lea si, emm mov ah,0bh mov bl,0 call [xmsadr] mov dx, CStr("XMS block move, src > dest && src < dest+len") call protocol mov dx,handle mov ah,0ah mov bl,0 call [xmsadr] ret failed: invoke printf, CStr("XMS call AH=08 failed",lf) ret movetest endp ;--- main main proc c mov ax,4300h int 2fh test al,80h ;xms host found? jnz main1 invoke printf, CStr("no XMS host found",lf) jmp exit main1: mov ax,4310h ;get XMS call address int 2fh mov word ptr xmsadr+0,bx mov word ptr xmsadr+2,es invoke printf, CStr("XMS call address: %X:%X",lf), word ptr [xmsadr+2], word ptr [xmsadr+0] call movetest exit: ret main endp ;--- init start proc push cs pop ds mov cx,es mov ax,ss sub ax,cx shl ax,4 add ax,sp push cs pop ss mov sp,ax pushf pushf pop ax or ah,70h ;a 80386 will have bit 15 cleared push ax ;if bits 12-14 are 0, it is a 80286 popf ;or a bad emulation pushf pop ax popf and ah,0f0h js no386 ;bit 15 set? then its a 8086/80186 jnz is386 no386: invoke printf, CStr("a 80386 is needed",lf) jmp done is386: call main done: mov ah,4Ch int 21h start endp END start ================================================ FILE: Test/XMSTEST2.ASM ================================================ ;--- XMSTEST2: test max block allocation ;--- Public Domain. ;--- to be assembled with JWasm or Masm v6. .model tiny .dosseg .stack 2048 .386 lf equ 10 BUFFSIZ equ 10000h ;--- define a string constant CStr macro string:vararg local xxx .const xxx db string db 0 .code exitm endm xms_move struct len dd ? ; block length in bytes src_handle dw ? ; source handle src_offset dd ? ; offset into source dest_handle dw ? ; destination handle dest_offset dd ? ; offset into destination xms_move ends .data xmsadr dd 0 ;XMS host call address .code include printf.inc ;--- test xms move block function movetest proc local handle:word local maxmem:dword local buffer:word local emm:xms_move ;--- allocate DOS memory block mov ah,48h mov bx,BUFFSIZ / 16 int 21h jnc @F invoke printf, CStr("not enough DOS memory",lf) jmp failed @@: mov buffer, ax ;--- allocate XMS block mov ah,88h ;query free xms mem mov bl,0 call [xmsadr] mov maxmem, eax mov bh,0 push bx invoke printf, CStr("xms function 88h returned bl=%X, eax=%lX",lf), bx, eax pop bx cmp bl,0 jnz failed mov edx, maxmem mov ah,89h mov bl,0 call [xmsadr] mov handle, dx mov bh,0 push bx invoke printf, CStr("xms function 89h returned bl=%X, dx=%X",lf), bx, dx pop bx cmp bl,0 jnz failed ;--- test 1 cld mov es, buffer mov di, 0 mov cx, BUFFSIZ / 4 mov eax, 0deadbabeh rep stosd mov emm.len, BUFFSIZ mov emm.src_handle, 0 mov word ptr emm.src_offset+0, 0 mov word ptr emm.src_offset+2, es mov ax, handle mov emm.dest_handle, ax mov emm.dest_offset, 0 mov edi, maxmem shl edi, 10 ;kB -> byte .while edi mov dx,handle lea si, emm ;ds:si->xms move struct mov ah,0bh mov bl,0 call [xmsadr] push ax invoke printf, CStr("XMS block move, ax=%u, dest ofs=%lX",13), ax, emm.dest_offset pop ax .break .if ax == 0 add emm.dest_offset, BUFFSIZ .if edi > BUFFSIZ sub edi, BUFFSIZ .else mov emm.len, edi xor edi, edi .endif .endw invoke printf, CStr(10) ;--- free xms handle mov dx,handle mov ah,0ah mov bl,0 call [xmsadr] failed: ret movetest endp ;--- main main proc c mov ax,4300h int 2fh test al,80h ;xms host found? jnz main1 invoke printf, CStr("no XMS host found",lf) jmp exit main1: mov ax,4310h ;get XMS call address int 2fh mov word ptr xmsadr+0,bx mov word ptr xmsadr+2,es invoke printf, CStr("XMS call address: %X:%X",lf), word ptr [xmsadr+2], word ptr [xmsadr+0] call movetest exit: ret main endp ;--- init start proc mov ax,cs mov ds,ax mov cx,ds mov ax,ss sub ax,cx shl ax,4 add ax,sp push ds pop ss mov sp,ax ;--- free DOS mem mov ax, ds mov cx, es sub ax, cx mov bx, sp add bx, 15 shr bx, 4 add bx, ax mov ah, 4Ah int 21h pushf pushf pop ax or ah,70h ;a 80386 will have bit 15 cleared push ax ;if bits 12-14 are 0, it is a 80286 popf ;or a bad emulation pushf pop ax popf and ah,0f0h js no386 ;bit 15 set? then its a 8086/80186 jnz is386 no386: invoke printf, CStr("a 80386 is needed",lf) jmp done is386: call main done: mov ah,4Ch int 21h start endp END start ================================================ FILE: Test/XMSTEST3.ASM ================================================ ;--- test ah=0Eh (get handle info) ;--- Public Domain. ;--- to be assembled with JWasm or Masm v6. .model tiny .dosseg .stack 2048 .386 cr equ 13 lf equ 10 BUFFSIZ equ 10000h ;--- define a string constant CStr macro string:vararg local xxx .const xxx db string db 0 .code exitm endm .data xmsadr dd 0 ;XMS host call address .code include printf.inc runtest proc local handle:word local dwSize:dword mov edx,10000h ;allocate 65536 kB call runtest1 mov edx,0ffffh ;allocate 65535 kB call runtest1 ret runtest1: mov dwSize,edx mov ah,89h ;alloc ext. memory block (size EDX) mov bl,0 call [xmsadr] cmp ax,1 jz @F invoke printf, CStr("xms function 89h failed (bl=%X, edx=%lX)",lf), bl, edx jmp failed @@: mov handle,dx invoke printf, CStr("xms function 89h(size %lu kB): dx=%X",lf), dwSize, dx ;--- function 0Eh should fail, since size doesn't fit in 16-bit register mov edx,12340000h ;set hiword(edx) to a known value mov dx,handle mov bx,0 ;set bx to a known value mov ah,0eh ;get handle info (v2) call [xmsadr] invoke printf, CStr("xms function 0Eh: ax=%X, bx=%X, edx=%lX",lf), ax, bx, edx ;--- function 8Eh should succeed mov edx,12340000h ;set hiword(edx) to a known value mov dx,handle mov bx,0 ;set bx to a known value mov ah,8eh ;get handle info (v3) call [xmsadr] invoke printf, CStr("xms function 8Eh: ax=%X, bx=%X, edx=%lX",lf), ax, bx, edx mov dx,handle mov ah,0ah ;free handle call [xmsadr] failed: retn runtest endp ;--- main main proc c mov ax,4300h int 2fh test al,80h ;xms host found? jnz main1 invoke printf, CStr("no XMS host found",lf) jmp exit main1: mov ax,4310h ;get XMS call address int 2fh mov word ptr xmsadr+0,bx mov word ptr xmsadr+2,es ; invoke printf, CStr("XMS call address: %X:%X",lf),word ptr [xmsadr+2],word ptr [xmsadr+0] call runtest exit: ret main endp ;--- init start proc mov ax,cs mov ds,ax mov cx,ss sub cx,ax shl cx,4 mov ss,ax add sp,cx call main mov ah,4Ch int 21h start endp END start ================================================ FILE: Test/XMSTEST4.ASM ================================================ ;--- XMSTEST4: test block resize (shrink) ;--- Public Domain. ;--- to be assembled with JWasm or Masm v6. .model small .386 .dosseg .stack 2048 cr equ 13 lf equ 10 BUFFSIZ equ 10000h ;--- define a string constant CStr macro string:vararg local xxx .const xxx db string db 0 .code exitm endm .data xmsadr dd 0 ;XMS host call address .code assume DS:DGROUP include printf.inc ;--- test xms block resize function testproc proc local handle:word ;--- allocate XMS block mov edx, 10000h ;64MB mov ah,89h call [xmsadr] mov handle, dx push ax invoke printf, CStr("xms alloc (ah=89h) of 64MB returned ax=%X, dx=%X",lf), ax, dx pop ax cmp ax,1 jnz failed ;--- lock XMS block mov dx, handle mov ah,0Ch call [xmsadr] push ax invoke printf, CStr("xms lock (ah=0Ch) returned ax=%X, dx:bx=%X%04X",lf), ax, dx, bx pop ax cmp ax,1 jnz failed2 ;--- resize block; should fail since locked mov ebx, 8000h ;shrink block to 32MB mov dx, handle mov ah,8Fh call [xmsadr] mov bh,0 invoke printf, CStr("xms resize (ah=8Fh) to 32MB returned ax=%X, bl=%X",lf), ax, bx ;--- unlock XMS block mov dx, handle mov ah,0Dh call [xmsadr] invoke printf, CStr("xms unlock (ah=0Dh) returned ax=%X",lf), ax failed2: ;--- free xms handle mov dx,handle mov ah,0ah mov bl,0 call [xmsadr] invoke printf, CStr("xms release (ah=0Ah) returned ax=%X",lf), ax failed: ret testproc endp ;--- main main proc c mov ax,4300h int 2fh test al,80h ;xms host found? jnz main1 invoke printf, CStr("no XMS host found",lf) jmp exit main1: mov ax,4310h ;get XMS call address int 2fh mov word ptr xmsadr+0,bx mov word ptr xmsadr+2,es invoke printf, CStr("XMS call address: %X:%X",lf), word ptr [xmsadr+2], word ptr [xmsadr+0] call testproc exit: ret main endp ;--- init start proc mov ax,@data mov ds,ax mov cx,ds mov ax,ss sub ax,cx shl ax,4 add ax,sp push ds pop ss mov sp,ax ;--- free DOS mem mov ax, ds mov cx, es sub ax, cx mov bx, sp add bx, 15 shr bx, 4 add bx, ax mov ah, 4Ah int 21h pushf pushf pop ax or ah,70h ;a 80386 will have bit 15 cleared push ax ;if bits 12-14 are 0, it is a 80286 popf ;or a bad emulation pushf pop ax popf and ah,0f0h js no386 ;bit 15 set? then its a 8086/80186 jnz is386 no386: invoke printf, CStr("a 80386 is needed",lf) jmp done is386: call main done: mov ah,4Ch int 21h start endp END start ================================================ FILE: Test/XMSTEST5.ASM ================================================ ;--- XMSTEST5: test block resize (shrink) ;--- this variant allocates a 64MB block with xms v3, ;--- but tries to resize if with xms v2 to 32MB. ;--- Public Domain. ;--- to be assembled with JWasm or Masm v6. .model small .386 .dosseg .stack 2048 cr equ 13 lf equ 10 BUFFSIZ equ 10000h ;--- define a string constant CStr macro string:vararg local xxx .const xxx db string db 0 .code exitm endm .data xmsadr dd 0 ;XMS host call address .code assume DS:DGROUP include printf.inc ;--- test xms block resize function testproc proc local handle:word local wRC:word ;--- allocate XMS block mov edx, 10000h ;64MB mov ah,89h call [xmsadr] mov handle, dx push ax invoke printf, CStr("xms alloc (ah=89h) of 64MB returned ax=%X, dx=%X",lf), ax, dx pop ax cmp ax,1 jnz failed ;--- lock XMS block mov dx, handle mov ah,0Ch call [xmsadr] push ax invoke printf, CStr("xms lock (ah=0Ch) returned ax=%X, dx:bx=%X%04X",lf), ax, dx, bx pop ax cmp ax,1 jnz failed2 ;--- resize block; should fail since locked mov bx, 8000h ;shrink block to 32MB mov dx, handle mov ah,0Fh call [xmsadr] mov wRC, ax mov bh,0 invoke printf, CStr("xms resize (ah=0Fh) to 32MB returned ax=%X, bl=%X",lf), ax, bx ;--- unlock XMS block mov dx, handle mov ah,0Dh call [xmsadr] invoke printf, CStr("xms unlock (ah=0Dh) returned ax=%X",lf), ax cmp wRC,1 jz failed2 ;--- try again to resize, this time with an unlocked block mov bx, 8000h ;shrink block to 32MB mov dx, handle mov ah,0Fh call [xmsadr] mov wRC, ax mov bh,0 invoke printf, CStr("xms resize (ah=0Fh) to 32MB returned ax=%X, bl=%X",lf), ax, bx failed2: ;--- free xms handle mov dx,handle mov ah,0ah mov bl,0 call [xmsadr] invoke printf, CStr("xms release (ah=0Ah) returned ax=%X",lf), ax failed: ret testproc endp ;--- main main proc c mov ax,4300h int 2fh test al,80h ;xms host found? jnz main1 invoke printf, CStr("no XMS host found",lf) jmp exit main1: mov ax,4310h ;get XMS call address int 2fh mov word ptr xmsadr+0,bx mov word ptr xmsadr+2,es invoke printf, CStr("XMS call address: %X:%X",lf), word ptr [xmsadr+2], word ptr [xmsadr+0] call testproc exit: ret main endp ;--- init start proc mov ax,@data mov ds,ax mov cx,ds mov ax,ss sub ax,cx shl ax,4 add ax,sp push ds pop ss mov sp,ax ;--- free DOS mem mov ax, ds mov cx, es sub ax, cx mov bx, sp add bx, 15 shr bx, 4 add bx, ax mov ah, 4Ah int 21h pushf pushf pop ax or ah,70h ;a 80386 will have bit 15 cleared push ax ;if bits 12-14 are 0, it is a 80286 popf ;or a bad emulation pushf pop ax popf and ah,0f0h js no386 ;bit 15 set? then its a 8086/80186 jnz is386 no386: invoke printf, CStr("a 80386 is needed",lf) jmp done is386: call main done: mov ah,4Ch int 21h start endp END start ================================================ FILE: Tools/CLEAR.BAT ================================================ @echo off erase CPUID\*.EXE erase CPUID\*.lst erase CpuStat\*.EXE erase CpuStat\*.lst erase EMSSTAT\*.EXE erase EMSSTAT\*.lst erase JEMFBHLP\*.EXE erase JEMFBHLP\*.lst cd JLOAD nmake clean cd .. erase MEMSTAT\*.EXE erase MEMSTAT\*.lst erase MoveXBDA\*.EXE erase MoveXBDA\*.lst erase UMBM\*.EXE erase UMBM\*.lst erase VCPI\*.EXE erase VCPI\*.lst erase XMSSTAT\*.EXE erase XMSSTAT\*.lst ================================================ FILE: Tools/CPUID/CPUID.ASM ================================================ ;--- CPUID displays status of CPU. ;--- Public Domain. ;--- Masm syntax. To be assembled with JWasm or Masm. ;--- uses 16-bit printf .286 .model small .dosseg .stack 400h bs equ 8 lf equ 10 printf proto c :ptr BYTE, :VARARG CStr macro y:VARARG local sym .const sym db y,0 .code exitm endm DStr macro text:vararg local sym .const sym db text,0 .data exitm endm .data cpubrand label dword dwCPUIDeax dd 0 dwCPUIDebx dd 0 dwCPUIDedx dd 0 dwCPUIDecx dd 0 db 0 bCpu db 0 ;--- cpuid.1 EDX features ;--- note: bit 10,20,30 not assigned! cpuid1_edx_bits db 0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,19,21,22,23,24,25,26,28,29,31 cpuid1_edx_str label word dw DStr("FPU integrated",lf) dw DStr("VME - Virtual-8086 Mode Enhancement [CR4 VME/PVI & EFL VIF/VIP]",lf) dw DStr("DE - Debugging Extensions [I/O breakpoints & CR4 DE]",lf) dw DStr("PSE - Page Size Extensions [4 MB page size & CR4 PSE]",lf) dw DStr("TSC - Time Stamp Counter [RDTSC & CR4 TSD]",lf) dw DStr("MSR - RDMSR & WRMSR support",lf) dw DStr("PAE - Physical Address Extensions [CR4 PAE]",lf) dw DStr("MCE - Machine Check Exceptions [CR4 MCE]",lf) dw DStr("CX8 - CMPXCHG8B",lf) dw DStr("APIC - on chip APIC exists and enabled",lf) dw DStr("SEP - SYSENTER & SYSEXIT",lf) dw DStr("MTRR - Memory Type Range Registers",lf) dw DStr("PGE - PTE Global Bit [CR4 PGE]",lf) dw DStr("MCA - Machine Check Architecture",lf) dw DStr("CMOV & FCMOV/FCOM1",lf) dw DStr("PAT - Page Attribute Table",lf) dw DStr("PSE-36 - Page Size Extension",lf) dw DStr("CFLSH - CFLUSH instruction",lf) dw DStr("DS - Debug Store",lf) dw DStr("ACPI - Thermal Monitor & Clock Control",lf) dw DStr("MMX - 64-bit MultiMedia Extensions",lf) dw DStr("FXSAVE/FXRSTOR",lf) dw DStr("SSE",lf) dw DStr("SSE2",lf) dw DStr("HTT - Multi Threading",lf) dw DStr("TM - Thermal Monitoring",lf) dw DStr("PBE - Pending Brk Enable",lf) cpuid1_ecx_bits db 0,1,2,3,5,7,8,9,13,19,20,21,23,26,28,30 cpuid1_ecx_str label word dw DStr("SSE3",lf) dw DStr("PCLMULQDQ - Carryless Multiplication",lf) dw DStr("DTES64 - 64 bit DS Area",lf) dw DStr("MONITOR - MONITOR/MWAIT",lf) dw DStr("VMX - Virtual Machine Extensions",lf) dw DStr("EIST - Intel SpeedStep",lf) dw DStr("TM2 - Thermal Monitor 2",lf) dw DStr("SSSE3",lf) dw DStr("CMPXCHG16B",lf) dw DStr("SSE4.1",lf) dw DStr("SSE4.2",lf) dw DStr("x2APIC",lf) dw DStr("POPCNT",lf) dw DStr("XSAVE",lf) dw DStr("AVX - Advanced Vector Extensions",lf) dw DStr("RDRAND",lf) FEATS struct pFeat dw ? ;ptr DWORD pBitNo dw ? ;ptr BYTE pStr dw ? ;ptr NEAR16 string ptr _size dw ? ;size of pBitNo, pStr table FEATS ends cpuid1m label word dw DStr("CPUID 1 features (EDX,ECX):",lf) FEATS FEATS cpuid1x_edx_bits db 11,22,26,29,31 cpuid1x_edx_str label word dw DStr("SYSCALL & SYSRET",lf) dw DStr("MMX Extensions",lf) dw DStr("1 GB pages",lf) dw DStr("long mode (cpu is 64-bit)",lf) dw DStr("3DNow",lf) cpuid1x_ecx_bits db 0,2,6,21 cpuid1x_ecx_str label word dw DStr("LahfSahf - LAHF/SAHF supported in 64-bit",lf) dw DStr("SVM - Secure Virtual Machine",lf) dw DStr("SSE4A",lf) dw DStr("TBM - Trailing Bit Manipulation",lf) cpuid1xm label word dw DStr("CPUID 80000001 features (EDX,ECX):",lf) FEATS FEATS cpuid7_ebx_bits db 0,1,2,3,4,5,7,8,18 cpuid7_ebx_str label word dw DStr("access to base of FS and GS",lf) dw DStr("IA32_TSC_ADJUST",lf) dw DStr("SGX - Software Guard Extensions",lf) dw DStr("BMI1 - Bit Manipulation Instruction Set 1",lf) dw DStr("TSX Hardware Lock Elision",lf) dw DStr("AVX2 - Advanced Vector Extensions 2",lf) dw DStr("SMEP - Supervisor Mode Execution Prevention",lf) dw DStr("BMI2 - Bit Manipulation Instruction Set 2",lf) dw DStr("RDSEED - RDSEED supported",lf) cpuid7m label word dw DStr("CPUID 7 features (EBX):",lf) FEATS .code .586 include printf.inc hascpuid proc push di mov di,sp and sp,0fffch ;make sure we don't get an exc 11 (if AM set in CR0) pushfd ; save EFlags cli pushd 240000h ; set AC bit in eflags, reset IF popfd ; pop extended flags pushfd ; push extended flags pop ax pop ax ; get HiWord(EFlags) into AX popfd ; restore EFlags test al,04 ;AC bit set? je @F test al,20h ;CPUID bit set? jz @F mov sp,di pop di clc ret @@: mov sp,di pop di stc ret hascpuid endp ;--- print features print_cpuid_regs proc stdcall uses ebx si di items:word, pp:ptr mov si, pp lodsw invoke printf, ax nextreg: push si mov bx,[si].FEATS.pFeat mov di,[si].FEATS.pStr mov cx,[si].FEATS._size mov si,[si].FEATS.pBitNo mov ebx,[bx] nextitem: lodsb movzx eax,al mov dx,[di] add di,2 bt ebx, eax jnc @F push cx invoke printf, CStr("%2u %s"), ax, dx pop cx @@: loop nextitem pop si add si,sizeof FEATS dec items jnz nextreg ret print_cpuid_regs endp main proc c pushf mov ax,7000h PUSH AX ; also kept after a POPF POPF ; a 286 always sets it to Null PUSHF POP AX popf and ah,0F0h cmp AH,70H ;on a 80386 (real-mode) 7x is in AH jz is386 invoke printf, CStr("CPU is not 80386 or better",lf) jmp exit is386: invoke hascpuid jnc @F invoke printf, CStr("CPUID not implemented",lf) jmp exit @@: mov eax,0 cpuid mov dwCPUIDeax, eax mov dwCPUIDebx, ebx mov dwCPUIDecx, ecx mov dwCPUIDedx, edx invoke printf, CStr("CPUID.00: EAX=%lX string=%s",lf), \ eax, offset dwCPUIDebx mov eax,1 cpuid mov [bCpu],ah mov dwCPUIDebx, ebx mov dwCPUIDecx, ecx mov dwCPUIDedx, edx invoke printf, CStr("CPUID.01: EAX-EBX-ECX-EDX: %lX-%lX-%lX-%lX",lf), \ dwCPUIDeax, dwCPUIDebx, dwCPUIDecx, dwCPUIDedx mov eax, dwCPUIDebx shr eax,16 mov ah,0 invoke printf, CStr("Logical Processors (EBX[16-23], req. HTT=1): %u",lf), ax invoke print_cpuid_regs, 2, offset cpuid1m ;display cpuid 1 in more detail cmp byte ptr dwCPUIDeax,7 jc @F mov eax,7 ;get extended features (returns features in ebx, ecx, edx) mov ecx,0 cpuid mov dwCPUIDebx, ebx invoke print_cpuid_regs, 1, offset cpuid7m @@: mov eax,80000000h cpuid and eax, eax ;bit 31 set? jns no8ex mov di, ax invoke printf, CStr("CPUID.80000000: EAX=%lX",lf), eax cmp di, 1 jc nobrand mov eax,80000001h cpuid mov dwCPUIDebx, ebx mov dwCPUIDecx, ecx mov dwCPUIDedx, edx invoke printf, CStr("CPUID.80000001: EAX-EBX-ECX-EDX=%lX-%lX-%lX-%lX",lf), \ eax, ebx, ecx, edx invoke print_cpuid_regs, 2, offset cpuid1xm ;display values returned in edx, ecx cmp di, 5 jb nobrand mov eax,80000002h cpuid mov cpubrand+0, eax mov cpubrand+4, ebx mov cpubrand+8, ecx mov cpubrand+12, edx invoke printf, CStr("CPUID.80000002/3/4: brand=%s"), offset cpubrand mov eax,80000003h cpuid mov cpubrand+0, eax mov cpubrand+4, ebx mov cpubrand+8, ecx mov cpubrand+12, edx invoke printf, CStr("%s"), offset cpubrand mov eax,80000004h cpuid mov cpubrand+0, eax mov cpubrand+4, ebx mov cpubrand+8, ecx mov cpubrand+12, edx invoke printf, CStr("%s",lf), offset cpubrand nobrand: cmp di, 8 jc no8ex mov eax, 80000008h cpuid mov dwCPUIDeax, eax invoke printf, CStr("CPUID.80000008: EAX-EBX-ECX-EDX=%lX-%lX-%lX-%lX",lf), \ eax, ebx, ecx, edx mov eax,dwCPUIDeax mov cx, ax and cx,0ffh shr ax,8 and ax,0ffh invoke printf, CStr("physical/linear address bits=%u/%u",lf), cx, ax no8ex: exit: mov al,0 ret main endp start: mov ax,@data mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx call main mov ah,4Ch int 21h END start ================================================ FILE: Tools/CPUID/MAKE.BAT ================================================ @echo off rem creates CPUID.EXE with JWasm jwasm -c -nologo -Fl -Sg -mz CPUID.ASM ================================================ FILE: Tools/CPUID/PRINTF.INC ================================================ ;--- simple printf() implementation handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ;--- outb is expected to be onto stack ltob PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP ;--- ds=dgroup, ss don't need to be dgroup printf PROC c uses si di bx fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx nextdigit: cmp BYTE PTR [si],'0' jb digitsdone cmp BYTE PTR [si],'9' ja digitsdone lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax jmp nextdigit digitsdone: mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u mov al,'%' jmp @@L359 handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn printf ENDP ================================================ FILE: Tools/Cpustat/CPUSTAT.ASM ================================================ ;--- CPUSTAT displays status of CPU. ;--- Public Domain. ;--- Masm syntax. To be assembled with JWasm or Masm. ;--- uses 16-bit printf .286 .model small .dosseg .stack 400h bs equ 8 lf equ 10 printf proto c :ptr BYTE, :VARARG CStr macro y:VARARG local sym .const sym db y,0 .code exitm endm DESCRIPTOR struct wLimit dw ? wBase0015 dw ? bBase1623 db ? bAttrL db ? bAttrH db ? bBase2431 db ? DESCRIPTOR ends TSS struct dd ? dwESP0 dd ? wSS0 dw ? org 64h wFlags dw ? wOffs dw ? TSS ends .data dwCR0 dd 0 _msw dd 0 gdtr label fword gdtl dw 0 gdta dd 0 idtr label fword idtl dw 0 idta dd 0 ;--- gdt for In15 ah=87 move gdti15 label DESCRIPTOR DESCRIPTOR <0,0,0,0,0,0> DESCRIPTOR <0,0,0,0,0,0> i15src DESCRIPTOR <-1,0,0,093h,0,0> i15dst DESCRIPTOR <-1,0,0,093h,0,0> DESCRIPTOR <0,0,0,0,0,0> DESCRIPTOR <0,0,0,0,0,0> modflg dw 0 brk dw 0 O_GDT equ 1 O_IDT equ 2 O_PD equ 4 O_TSS equ 8 O_FPU equ 16 bOpt db 0 ;cmdline options -g -i -p -t bVerbose db 0 ;for -p: 1=display paging tables .code .586 include printf.inc hascpuid proc push di mov di,sp and sp,0fffch ;make sure we don't get an exc 11 (if AM set in CR0) pushfd ; save EFlags cli pushd 240000h ; set AC bit in eflags, reset IF popfd ; pop extended flags pushfd ; push extended flags pop ax pop ax ; get HiWord(EFlags) into AX popfd ; restore EFlags test al,04 ;AC bit set? je @F test al,20h ;CPUID bit set? jz @F mov sp,di pop di clc ret @@: mov sp,di pop di stc ret hascpuid endp myint0c: myint0d: shr ecx,1 iret DispSegLimits proc local limitss:dword local limitds:dword local limites:dword local limitfs:dword local limitgs:dword push ds push es push fs push gs xor ax,ax mov ds,ax mov es,ax mov fs,ax mov gs,ax mov ax,cs shl eax,16 mov ax,offset myint0d cli xchg eax,ds:[13*4] push eax ;--- SS limit violation creates an exception 0Ch! mov ax,cs shl eax,16 mov ax,offset myint0c xchg eax,ds:[12*4] push eax if 0 mov dx,ss mov bx,sp xor ax,ax mov ss,ax mov sp,400h endif mov ecx,-1 mov al,ss:[ecx] if 0 mov ss,dx mov sp,bx endif mov limitss,ecx pop eax mov ds:[12*4],eax mov ecx,-1 mov al,ds:[ecx] mov limitds,ecx mov ecx,-1 mov al,es:[ecx] mov limites,ecx mov ecx,-1 mov al,fs:[ecx] mov limitfs,ecx mov ecx,-1 mov al,gs:[ecx] mov limitgs,ecx pop eax mov ds:[13*4],eax sti pop gs pop fs pop es pop ds invoke printf, CStr("SS-DS-ES-FS-GS limits: %lX-%lX-%lX-%lX-%lX",lf), limitss, limitds, limites, limitfs, limitgs ret DispSegLimits endp getldtr proc .586p cli mov ecx,cr0 inc cx mov cr0,ecx jmp @F @@: sldt ax str dx dec cx mov cr0,ecx sti ret .586 getldtr endp malloc proc stdcall wBytes:word mov ax, wBytes add ax, [brk] jc error xchg ax, [brk] error: ret malloc endp free proc stdcall pMem:ptr mov ax, pMem and ax, ax jz @F mov [brk], ax @@: ret free endp ;--- copy an extended memory region ( physical address in physad ) into buffer copymem proc stdcall uses si buffer:ptr, physad:dword, size_:word mov eax, physad ; and ax, 0F000h mov i15src.wBase0015,ax shr eax, 16 mov i15src.bBase1623,al mov i15src.bBase2431,ah mov ax, buffer movzx eax, ax mov dx, ds movzx edx, dx shl edx, 4 add eax, edx mov i15dst.wBase0015, ax shr eax, 16 mov i15dst.bBase1623, al mov i15dst.bBase2431, ah push ds pop es mov cx, size_ shr cx, 1 mov si, offset gdti15 ;es:si=gdt to use mov ah, 87h stc int 15h ret copymem endp ;--- translate linear address in linad into physical address (returned in eax) getphysaddr proc stdcall uses ebx esi di linad:dword xor di, di mov eax, -1 ;in case the next instr is "emulated" mov eax, cr3 cmp eax, -1 jz error cmp eax, 0 ;NTVDM? jz error and ax, 0F000h mov esi, eax invoke malloc, 1000h jc error mov di, ax invoke copymem, di, esi, 1000h jc error mov ebx, linad shr ebx, 20 and bx, 0FFCh mov eax, dword ptr [bx+di] and ax, 0F000h invoke copymem, di, eax, 1000h jc error mov ebx, linad shr ebx, 10 and bx, 0FFCh mov eax, dword ptr [bx+di] and ax,0f000h mov edx, linad and dx, 0FFFh or ax, dx push eax invoke free, di pop eax clc ret error: invoke free, di stc ret getphysaddr endp getattr proc stdcall buffer:ptr pusha mov di, buffer test dl, 10h jz syssegs mov si, CStr("code") test dl, 8 jnz @F mov si, CStr("data") @@: call cpy jmp done syssegs: and dl, 0Fh cmp dl, 2 jnz @F mov si, CStr("LDT ") call cpy jmp done @@: cmp dl, 5 jnz @F mov si, CStr("task gate ") call cpy jmp done @@: test dl, 111b jnz @F mov si, CStr("undef ") call cpy jmp done @@: mov si, CStr("386 ") test dl, 8 jz @F mov si, CStr("386 ") @@: call cpy mov si, CStr("tss ") test dl, 4 jz @F mov si, CStr("gate ") @@: call cpy done: mov byte ptr [di], 0 dec di cmp byte ptr [di], ' ' jnz @F mov byte ptr [di], 0 @@: popa ret cpy: lodsb stosb and al, al jnz cpy dec di retn getattr endp ;--- the GDT/IDT is read with int 15h, ah=87h ;--- this is not really correct, since this function ;--- is supposed to read from physical addresses, while ;--- the addresses in GDTR/IDTR are linear; ;--- for jemmex, it often works, though, since its ;--- code/data usually are identity-mapped, starting ;--- at 0x110000. DispGDT proc local wSize:word local buffer2[80]:byte xor di, di invoke getphysaddr, gdta jc nogdt mov ebx, eax mov cx,gdtl inc cx mov wSize, cx invoke malloc, cx jc error mov di, ax invoke copymem, di, ebx, wSize jc error mov cx, wSize shr cx, 3 jcxz nogdt mov si, di nextitem: push cx mov cx,[si+0] mov bh,[si+7] mov bl,[si+4] shl ebx,16 mov bx,[si+2] mov dx,[si+5] movzx eax,cx or eax, ebx or ax, dx and eax, eax jz @F invoke getattr, addr buffer2 push si sub si, di invoke printf, CStr("GDT[%4X]: %08lX:%04X %04X (%s)",lf), si, ebx, cx, dx, addr buffer2 pop si @@: add si, sizeof DESCRIPTOR pop cx loop nextitem invoke free, di nogdt: ret error: invoke printf, CStr("Int 15h, ah=87h failed",lf) invoke free, di ret DispGDT endp DispIDT proc local pMem:ptr local wSize:word mov pMem, 0 invoke getphysaddr, idta jc noidt mov esi, eax mov cx,idtl inc cx cmp cx, 8*100h jbe @F mov cx, 8*100h @@: mov wSize, cx invoke malloc, cx jc error mov pMem, ax invoke copymem, pMem, esi, wSize jc error mov cx, wSize shr cx, 3 jcxz noidt mov si, pMem xor di, di nextitem: push cx mov ax,[si+6] shl eax, 16 mov ax,[si+0] mov bx,[si+2] mov dx,[si+4] invoke printf, CStr("IDT[%4X]: %04X:%08lX %04X",lf), di, bx, eax, dx @@: inc di add si, sizeof DESCRIPTOR pop cx loop nextitem invoke free, pMem noidt: ret error: invoke free, pMem invoke printf, CStr("Int 15h, ah=87h failed",lf) ret DispIDT endp ;--- get # of PTEs in a page table ;--- eax->physical address pt getPTEs proc stdcall uses si di ebx pPT:ptr mov ebx, eax mov di, pPT and bx, 0F000h invoke copymem, di, ebx, 1000h jc exit mov si, di xor dx, dx mov cx, 1024 nextitem: lodsd test al,1 jz @F inc dx @@: loop nextitem mov ax, dx clc exit: ret getPTEs endp ;--- print page table ;--- iPD: index in page directory printPT proc stdcall uses si pPT:ptr, iPD:word mov si, pPT mov cx, 1024 nextitem: push cx test cl, 7 jnz @F ;--- index page directory specifies address bits 22-31 ;--- index page table specifies address bits 12-21 movzx eax, di shl eax, 22 mov dx, cx sub dx, 1024 neg dx movzx edx, dx shl edx, 12 or eax, edx invoke printf, CStr(lf,"%8lX: "), eax @@: lodsd .if al & 1 invoke printf, CStr("%8lX "), eax .else invoke printf, CStr("-------- ") .endif pop cx loop nextitem invoke printf, CStr(lf,lf) ret printPT endp ;--- print page directory ;--- if bVerbose==1, call printPT to display PTEs. printpd proc stdcall uses si di pPD:ptr local pPT:ptr invoke malloc, 1000h jc error mov pPT, ax mov si, pPD xor di, di nextitem: lodsd test al, 1 jz skipitem test al, 80h ; perhaps a 4 MB page? jz @F mov dx, di shl edx, 22 ;--- 4MB page PTE: ;--- bits 22-31: address 22-31 ;--- bits 13-20: address 32-39 ;--- bit 21: 0 ;--- bit 12: PAT mov ecx, eax shr ecx, 13 movzx ecx, cl mov ebx, eax and ebx, 0ffc00000h invoke printf, CStr("%8lX: %08lX (4 MB page: phys=%X%08lX)",lf), edx, eax, cx, ebx jmp skipitem @@: push eax invoke getPTEs, pPT pop ecx jc error mov dx, di shl edx, 22 invoke printf, CStr("%8lX: %08lX (%4u pages)",lf), edx, ecx, ax .if bVerbose invoke printpt, pPT, di .endif skipitem: inc di cmp di, 1024 jnz nextitem invoke free, pPT error: ret printpd endp DispPD proc local pMem:ptr mov pMem, 0 invoke malloc, 1000h jc error mov pMem, ax mov eax, -1 ;in case the next instr is "emulated" mov eax, cr3 cmp eax, -1 jz done and eax, eax jz done and ax, 0F000h invoke copymem, pMem, eax, 1000h jc error invoke printf, CStr(lf,"page directory",lf) invoke printf, CStr("-------------------------------",lf) invoke printpd, pMem done: invoke free, pMem ret error: invoke free, pMem invoke printf, CStr("Int 15h, ah=87h failed",lf) ret DispPD endp ;--- display IO permission bitmap DispIOPB proc stdcall uses si di pBitmap:ptr, size_:word local wEnd:word local wItems:word mov ecx, 10000h movzx eax, size_ shl eax, 3 cmp ecx, eax jb @F mov ecx, eax @@: mov wEnd, cx invoke printf, CStr("trapped Ports:",lf) mov si, pBitmap xor edi, edi mov wItems, di nextport: bt [si], edi jnc skipitem invoke printf, CStr("%4X "), di inc wItems cmp wItems, 8 jnz skipitem invoke printf, CStr(lf) mov wItems, 0 skipitem: inc di cmp di, wEnd jnz nextport cmp wItems, 0 jz @F invoke printf, CStr(lf) @@: ret DispIOPB endp ;--- display Interrupt redirection bitmap DispRedir proc stdcall uses si di pBitmap:ptr invoke printf, CStr("Interrupt redirection bitmap:",lf) mov bx, 0 mov di, pBitmap nextbit: bt [di], bx jnc skipitem invoke printf, CStr("%2X "), bx skipitem: inc bx cmp bh, 0 jz nextbit invoke printf, CStr(lf) ret DispRedir endp DispTSS proc stdcall wSel:word, dwBase:dword, wLimit:word local wSize:word local dwPhys:dword local pTSS:ptr local pIOPB:ptr local Redir[32]:byte mov pTSS, 0 mov pIOPB, 0 invoke getphysaddr, dwBase mov dwPhys, eax invoke printf, CStr("TSS %X: address linear/phys=%lX/%lX, limit=%X",lf), wSel, dwBase, eax, wLimit invoke malloc, sizeof TSS jc noiopb mov pTSS, ax invoke copymem, ax, dwPhys, sizeof TSS mov bx, pTSS invoke printf, CStr(" SS:ESP0=%X:%lX, offset IOPB=%X",lf), [bx].TSS.wSS0, [bx].TSS.dwESP0, [bx].TSS.wOffs mov ax, wLimit sub ax, [bx].TSS.wOffs jbe noiopb cmp ax, 8192 jb @F mov ax, 8192 @@: mov wSize, ax invoke malloc, ax jc noiopb mov pIOPB, ax movzx edx, [bx].TSS.wOffs add edx, dwPhys invoke copymem, pIOPB, edx, wSize jc noiopb invoke DispIOPB, pIOPB, wSize if 1 mov eax, cr4 test al, 1 jz noiopb mov bx, pTSS movzx edx, [bx].TSS.wOffs sub edx, 32 add edx, dwPhys invoke copymem, addr Redir, edx, 32 jc noiopb invoke DispRedir, addr Redir endif noiopb: invoke free, pIOPB invoke free, pTSS ret DispTSS endp ;--- scan GDT and display all TSSes DispAllTSS proc local wSize:word local dwPhys:dword xor di, di invoke getphysaddr, gdta jc error mov esi, eax mov cx, gdtl inc cx mov wSize, cx invoke malloc, cx jc error mov di, ax invoke copymem, di, esi, wSize jc error mov si, di nextitem: cmp [si].DESCRIPTOR.bAttrL, 8Bh jnz skipitem mov bh, [si].DESCRIPTOR.bBase2431 mov bl, [si].DESCRIPTOR.bBase1623 shl ebx, 16 mov bx, [si].DESCRIPTOR.wBase0015 mov ax, si sub ax, di invoke DispTSS, ax, ebx, [si].DESCRIPTOR.wLimit skipitem: add si, sizeof DESCRIPTOR mov ax, wSize add ax, di cmp si, ax jb nextitem error: invoke free, di ret DispAllTSS endp ;--- main() main proc c local wSize:word mov si, 80h mov cl, es:[si] inc si .while cl mov al,es:[si] inc si dec cl .if (al == ' ' || al == 9) ; .elseif ( cl > 0 && ( al == '-' || al == '/')) mov al,es:[si] inc si dec cl or al,20h .if (al == 'i') or bOpt, O_IDT .elseif (al == 'g') or bOpt, O_GDT .elseif (al == 'p') or bOpt, O_PD .elseif (al == 't') or bOpt, O_TSS .elseif (al == 'v') or bVerbose, 1 .elseif (al == 'f') or bOpt, O_FPU .else jmp usage .endif .else usage: invoke printf, CStr("usage: CPUSTAT [ options ]",lf) invoke printf, CStr(" -f: clear FPU status",lf) invoke printf, CStr(" -g: display GDT if in V86 mode",lf) invoke printf, CStr(" -i: display IDT if in V86 mode",lf) invoke printf, CStr(" -p: display PD if in V86 mode",lf) invoke printf, CStr(" -t: display TSS+IOPB if in V86 mode",lf) invoke printf, CStr(" -v: make -p display paging tables",lf) jmp exit .endif .endw pushf mov ax,7000h PUSH AX ; also kept after a POPF POPF ; a 286 always sets it to Null PUSHF POP AX popf and ah,0F0h cmp AH,70H ;on a 80386 (real-mode) 7x is in AH jnz is286 db 66h ;MASM doesn't know SMSW EAX smsw ax mov [_msw],eax jmp is386 is286: smsw ax invoke printf, CStr("MSW: %X",lf), ax invoke printf, CStr("CPU is not 80386 or better",lf) jmp exit is386: and ax,1 mov [modflg],ax mov eax,[_msw] bt eax,31 ; PG setc dl movzx si,dl bt eax,18 ; AM setc dl movzx di,dl bt eax,16 ; WP setc cl movzx cx,cl bt ax,5 ; NE setc dl movzx dx,dl .data wMP dw 0 wEM dw 0 wTS dw 0 .code bt ax,3 ; TS setc byte ptr [wTS] bt ax,2 ; EM setc byte ptr [wEM] bt ax,1 ; MP setc byte ptr [wMP] mov bx,CStr('Real') bt ax,0 ; PE setc al jnc @F mov bx,CStr('V86') @@: movzx ax,al invoke printf, CStr("MSW: %lX (PG=%x AM=%x WP=%x NE=%x TS=%u EM=%u MP=%u PE=%x); %s-mode",lf),_msw, si, di, cx, dx, wTS, wEM, wMP, ax, bx db 66h ; probably useless, just lgdt may need operand size prefix sgdt gdtr db 66h ; probably useless, just lidt may need operand size prefix sidt idtr cmp bOpt, 0 jnz optional mov eax, 0 ;in case the next instr is "emulated" mov eax, cr0 ;cr0 (=msw) mov [dwCR0],eax and ax,1 cmp ax,modflg jz @F invoke printf, CStr("'MOV EAX,CR0' emulated incorrectly!",lf) @@: invoke printf, CStr("CR0: %lX",lf),dwCR0 invoke printf, CStr("GDTR: %lX,%X, IDTR: %lX,%X",lf),gdta,gdtl,idta,idtl test [_msw],1 jnz @F call getldtr invoke printf, CStr("LDTR: %X, TR: %X",lf), ax, dx @@: mov eax, -1 ;in case the next instr is "emulated" mov eax, cr2 invoke printf, CStr("CR2: %lX "),eax mov eax, -1 ;in case the next instr is "emulated" mov eax, cr3 invoke printf, CStr("CR3: %lX",lf),eax mov eax, -1 ;in case the next instr is "emulated" call hascpuid ;if CPUID is supported, CR4 exists as well jc nocr4 mov eax, cr4 ;priviledged instruction mov ch,0 push bp mov bp,sp test al,1 ;VME? setnz cl push cx test al,2 ;PVI? setnz cl push cx test al,8 ;DE? setnz cl push cx test al,10h ;PSE? setnz cl push cx test al,20h ;PAE? setnz cl push cx test al,40h ;MCE? setnz cl push cx test al,80h ;PGE? setnz cl push cx test ax,200h ;OSFXSR? setnz cl push cx test ax,400h ;OSXMMEXP? setnz cl push cx invoke printf, CStr("CR4: %lX (VME=%X, PVI=%X, DE=%X, PSE=%X, PAE=%X, MCE=%X, PGE=%X, OSFXSR=%X, OSXMMEX=%X)",lf), eax, word ptr [bp-2],word ptr [bp-4],word ptr [bp-6],word ptr [bp-8],word ptr [bp-10],word ptr [bp-12],word ptr [bp-14],word ptr [bp-16],word ptr [bp-18] mov sp,bp pop bp nocr4: ;--- might be a good idea to check CR0.MP & CR0.EM bits. ;--- if CR0.MP=1, a fwait opcode will raise an exception 07 if CR0.TS=1 ;--- if CR0.EM=1, any fp opcode will raise an exception 07. ;--- MP=bit 1, EM=bit 2, TS=bit 3. test [_msw], 4 jz @F invoke printf, CStr("CR0.EM=1, won't run FPU instructions",lf) jmp nofpu @@: fnstsw ax fnstcw wSize invoke printf, CStr("FCW: %X FSW: %X",lf), wSize, ax nofpu: mov eax, dr0 mov ebx, dr1 mov ecx, dr2 mov edx, dr3 invoke printf, CStr("DR0-DR3: %lX %lX %lX %lX",lf), eax, ebx, ecx, edx mov eax, dr6 mov ecx, dr7 invoke printf, CStr("DR6: %lX DR7: %lX",lf), eax, ecx pushfd pop eax invoke printf, CStr("EFL: %lX, ESP: %lX",lf), eax, esp .if !(byte ptr _msw & 1) call DispSegLimits .endif optional: .if (bOpt & O_GDT) .if byte ptr [_msw] & 1 ;v86 mode? call DispGDT .else invoke printf, CStr("no GDT in real-mode",lf) .endif .endif .if (bOpt & O_IDT) .if byte ptr [_msw] & 1 ;v86 mode? call DispIDT .else invoke printf, CStr("no IDT in real-mode",lf) .endif .endif .if (bOpt & O_PD) .if byte ptr [_msw] & 1 ;v86 mode? call DispPD .else invoke printf, CStr("no paging tables in real-mode",lf) .endif .endif .if (bOpt & O_TSS) .if byte ptr [_msw] & 1 ;v86 mode? call DispAllTSS .else invoke printf, CStr("no TSS in real-mode",lf) .endif .endif .if (bOpt & O_FPU) .if byte ptr [_msw] & 20h ; NE bit set? .else in al, 0A1h ; unmask IRQ 0Dh and al, not 20h out 0A1h, al mov al,0 out 0F0h, al .endif fninit .endif exit: mov al,0 ret main endp start: mov ax,@data mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx mov [brk],sp mov cx, es sub ax, cx mov bx, 1000h ;request a full 64kB dgroup add bx, ax mov ah, 4Ah int 21h jc @F call main @@: mov ah,4Ch int 21h END start ================================================ FILE: Tools/Cpustat/MAKE.BAT ================================================ @echo off rem creates CPUSTAT.EXE with JWasm jwasm -c -nologo -Fl -Sg -mz CPUSTAT.ASM ================================================ FILE: Tools/Cpustat/PRINTF.INC ================================================ ;--- simple printf() implementation handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ;--- outb is expected to be onto stack ltob PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP ;--- ds=dgroup, ss don't need to be dgroup printf PROC c uses si di bx fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx nextdigit: cmp BYTE PTR [si],'0' jb digitsdone cmp BYTE PTR [si],'9' ja digitsdone lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax jmp nextdigit digitsdone: mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u mov al,'%' jmp @@L359 handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn printf ENDP ================================================ FILE: Tools/EMSSTAT/EMSSTAT.ASM ================================================ ;--- EMSSTAT: display EMS status. ;--- Public Domain. ;--- tools: JWasm/Masm v6 and WLink. .286 .model small DGROUP group _TEXT ;use TINY model .386 cr equ 13 lf equ 10 ;--- define a string constant CStr macro string:vararg local xxx CONST segment xxx db string db 0 CONST ends exitm endm .dosseg .stack 1024 .data fc dw 0 ;EMS function code handle dw 0 ;EMS handle totalpg dd 0 totalkb dd 0 bStd db 01 bVCPI db 00 bPages db 00 bDevice db 00 .const emsstr1 db 'EMMXXXX0',0 emsstr2 db 'EMMQXXX0',0 emsstr3 db 'EMMXXXQ0',0 ;qemm .data? namtab db 12 dup (?) gdttab db 8*3 dup (?) buffer db 4096h dup (?) .code include printf.inc ;--- test if EMM is installed ;--- out: ax=0 if no EMM found ;--- ax=1 if EMM found EMScheck proc uses es si di mov ax, 3567h int 21h mov ax, es or ax, bx jz exit mov dx,1 mov si,offset emsstr1 mov di,000ah mov cx,8 cld repz cmpsb jz found mov dx,0 mov ah,46h int 67h and ah,ah jz found xor ax, ax jmp exit found: and dx,dx jnz isems invoke printf, CStr("EMM installed, but NOEMS, device name=") mov bx,10 mov cx,8 @@: mov al,es:[bx] push cx invoke printf, CStr("%c"),ax pop cx inc bx loop @B invoke printf, CStr(lf) isems: mov ax,1 exit: ret EMScheck endp _upper: cmp al,'a' jb @F and al,not 20h @@: ret _skipws: mov al,es:[bx] inc bx cmp al,' ' je _skipws cmp al,9 je _skipws dec bx cmp al,13 ret ;--- ES=PSP getpar proc near mov bx,0080h mov cl,es:[bx] cmp cl,00 jz getp1 getp2: inc bx call _skipws jz getp1 cmp al,'/' jz @F cmp al,'-' jnz parerr @@: inc bx mov al,es:[bx] call _upper cmp al,'V' jz getp3 cmp al,'D' jz getp4 cmp al,'P' jnz parerr mov byte ptr [bStd],0 mov byte ptr [bPages],01 jmp getp2 getp3: mov byte ptr [bStd],0 mov byte ptr [bVCPI],01 jmp getp2 getp4: mov byte ptr [bStd],0 mov byte ptr [bDevice],01 jmp getp2 getp1: clc ret parerr: invoke printf, CStr("usage: EMSSTAT [ options ]",lf) invoke printf, CStr("options are:",lf) invoke printf, CStr(" -p: display physical pages",lf) invoke printf, CStr(" -v: display VCPI info",lf) invoke printf, CStr(" -d: try to open devices EMMXXXX0 or EMMQXXX0",lf) stc ret getpar endp ;--- ES=PSP main proc c call getpar jc exit call EMScheck and ax,ax jnz @F invoke printf, CStr("EMM not found",lf) mov al,01 jmp nointapi @@: .if (bStd) call readd ;read+display EMS standard info call rpages ;display raw pages call xvhandle;display EMS handle infos .endif .if (bPages) call ppages ;display EMS mapping info .endif .if (bVCPI) call vcpi ;display VCPI info .endif .if (bDevice) call deviceinfo;display EMMXXXX0 device info .endif nointapi: mov al,00 exit: ret main endp ;--- read EMS info readd proc near mov ax,4100h call intrt mov si,offset buffer mov [si + 0],bx ;segment address of page frame mov ax,4600h call intrt mov [si + 2],al ;version (3.2 or 4.0) mov ax,4200h call intrt mov [si + 3],dx ;num total 16 kB pages mov [si + 5],bx ;num free 16 kB pages mov ax,16 mul dx mov [si + 11],ax ;total in kb mov [si + 13],dx ;total in kb mov ax,16 mul bx mov [si + 19],ax ;free in kb mov [si + 21],dx ;free in kb mov ax,4b00h call intrt mov [si + 7],bx ;active handles mov ax,5801h call intrt mov [si + 17],cx ;num mappable physical pages mov al,[si+2] mov ah,0 shl ax,4 mov cl,ah shr al,4 mov ah,0 mov ch,0 invoke printf, CStr("EMS version: %u.%u",lf), cx, ax mov ax,[si+3] mov cx,[si+5] mov edx,[si+11] mov ebx,[si+19] invoke printf, CStr('16 kB pages total/free: %u/%u (%lu/%lu kB)',lf),ax,cx,edx,ebx mov ax, [si+0] invoke printf, CStr('EMS page frame: %04X',lf),ax mov ax, [si+7] invoke printf, CStr('Active handles: %u',lf),ax mov ax, [si+17] invoke printf, CStr('physical pages: %u',lf),ax mov ax,5900h lea di,[si+23] push ds pop es int 67h and ah,ah jz ems59 mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=5900h (get EM hardware information) failed, status=%X",lf),ax jmp ems59done ems59: mov ax, [si+23] shl ax, 4 invoke printf, CStr('Raw pages size: %u',lf),ax mov ax, [si+25] invoke printf, CStr('Alternate map register sets: %u',lf),ax mov ax, [si+27] invoke printf, CStr('Mapping context size: %u',lf),ax mov ax, [si+29] invoke printf, CStr('DMA register sets: %u',lf),ax ems59done: ret readd endp ;--- display raw pages rpages proc mov ax,5901h int 67h cmp ah,0 jnz exit invoke printf, CStr("raw pages total/free: %u/%u",lf),dx,bx exit: ret rpages endp ;*** display EMS handle information xvhandle proc near local wHandles:WORD mov ax,5402h ;get number of handles in BX int 67h and ah,ah jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=5402h (get number of handles) failed, status=%X",lf),ax jmp done @@: invoke printf, CStr("(max) size of handle table: %u",lf),bx push ds pop es mov di,offset buffer mov ax,5400h ;get handle directory int 67h and ah,ah jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=5400h (get handle directory) failed, status=%X",lf),ax jmp done @@: movzx ax,al mov wHandles,ax and ax, ax jz done invoke printf, CStr("Handle Name Pages KB",lf) invoke printf, CStr("-----------------------------",lf) mov cx,wHandles nexthdl: ;<------ push cx mov ax,[di] invoke printf, CStr("%6u "), ax lea si,[di+2] mov cx,8 nextchar: lodsb cmp al,0 jnz @F mov al,' ' @@: mov dl,al mov ah,2 int 21h loop nextchar mov dx,[di] mov ax,4c00h ;get handle pages int 67h and ah,ah jz @F invoke printf, CStr(" ? ?",lf) jmp invalhdl @@: movzx ebx,bx add [totalpg],ebx push bx invoke printf, CStr("%5u"), bx pop bx mov ax,16 mul bx push dx push ax pop eax add [totalkb],eax invoke printf, CStr("%8lu",lf), eax invalhdl: pop cx add di,10 loop nexthdl invoke printf, CStr("-----------------------------",lf) mov eax,dword ptr [totalpg+0] mov edx,dword ptr [totalkb+0] invoke printf, CStr(" %5lu%8lu",lf), eax, edx done: ret xvhandle endp searchpage proc push di @@: cmp si,[di] jz @F add di,4 loop @B stc @@: mov ax,[di+2] pop di ret searchpage endp ;*** display mapping segment -> physical page ppages proc c local count:word local numpgs:word mov ax,5801h int 67h cmp ah,00 jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=5801h (get mappable pages) failed, status=%X",lf),ax jmp exit @@: and cx,cx jz nopages mov numpgs,cx mov count,0 mov di,offset buffer push ds pop es mov ax,5800h ;get mappable physical address array int 67h cmp ah,00 jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=5800h (get mappable pages) failed, status=%X",lf),ax jmp exit @@: invoke printf, CStr("Segm:Pg Segm:Pg Segm:Pg Segm:Pg",lf) invoke printf, CStr("-------------------------------",lf) xor si,si ppage3: mov ax,si invoke printf,CStr("%04X:"),ax mov cx,numpgs call searchpage jc @F invoke printf,CStr("%2u"),ax jmp ppage4 @@: invoke printf, CStr("--") ppage4: invoke printf,CStr(" ") inc count test byte ptr count,3 jnz @F invoke printf, CStr(lf) @@: add si,400h jnz ppage3 invoke printf, CStr(lf) exit: ret nopages: invoke printf, CStr("no mappable pages",lf) ret ppages endp intrt proc near mov fc,ax int 67h and ah,ah jz intrt1 push ax mov al,ah mov ah,0 mov cx,fc invoke printf, CStr("function:%X - RC:%X",lf),cx,ax pop ax mov bx,0000 intrt1: ret intrt endp descout proc near mov ah,[di+7] ;base bits 31..24 mov al,[di+4] ;base bits 23..16 mov cx,[di+2] ;base bits 15..0 invoke printf, CStr("%08lX:"), ax::cx mov ax,[di+0] ;limit bits 15..0 mov cx,[di+5] invoke printf, CStr("%04X,%04X",lf),ax,cx add di,8 stc ret descout endp ;*** display vcpi information vcpi proc near mov ax,4300h mov bx,0001h ;get a ems page to ensure vcpi is active call intrt mov [handle],dx mov ax,0DE00h ;vcpi supported? int 67h cmp ah,00 jz @F invoke printf, CStr("VCPI not supported",lf) jmp exit @@: mov al,bh mov ah,0 mov bh,0 invoke printf, CStr("VCPI version: %u.%u",lf),ax,bx push ds pop es mov di,offset buffer mov si,offset gdttab ;DS:SI -> 3 GDT descriptors mov ax,0DE01h ;get protected mode interface int 67h .if (ah == 0) invoke printf, CStr("offset for protected-mode switch:%08lX",lf), ebx sub di,offset buffer movzx edi,di shl edi, 10 ;440h -> 110000 mov eax, edi invoke printf, CStr("Start free address space: %lX",lf), eax mov di,offset gdttab invoke printf, CStr('1. VCPI descriptor: ') call descout invoke printf, CStr('2. VCPI descriptor: ') call descout invoke printf, CStr('3. VCPI descriptor: ') call descout .else mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=DE01 failed, ah=%X",lf),ax .endif mov ax,0DE03h ;num free 4K pages int 67h cmp ah,00 jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=DE03 failed, ah=%X",lf),ax jmp _de02 @@: invoke printf, CStr("free 4K pages: %lu",lf),edx _de02: mov ax,0DE02h ;maxAddr of a 4K page int 67h cmp ah,0 jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=DE02 failed, ah=%X",lf),ax jmp _de0a @@: invoke printf, CStr("highest address of pages: %lX",lf),edx _de0a: mov ax,0DE0Ah ;get interrupt vector mappings int 67h cmp ah,0 jz @F mov al,ah mov ah,0 invoke printf, CStr("int 67h, ax=DE0A failed, ah=%X",lf) jmp exit @@: invoke printf, CStr("master/slave PIC base: %02X/%02X",lf),bx,cx exit: mov dx,[handle] cmp dx,0000 jz @F mov ax,4500h ;release EMS page call intrt @@: ret vcpi endp ;--- BX = handle ;--- CX = size read0 proc push cx invoke printf, CStr("read IOCTL device [") mov al, [buffer] mov ah,0 invoke printf, CStr("%X"),ax invoke printf, CStr("]=") pop cx mov dx, offset buffer mov ax,4402h int 21h .if (CARRY?) invoke printf, CStr(" failed",lf) stc .else invoke printf, CStr(" ok (AX=%X):"),ax clc .endif ret read0 endp ;--- display EMMXXXX0 device info deviceinfo proc mov dx,offset emsstr1 mov ax,3D00h int 21h .if (CARRY?) invoke printf, CStr("open device EMMXXXX0 failed",lf) mov dx,offset emsstr2 mov ax,3D00h int 21h .if (CARRY?) invoke printf, CStr("open device EMMQXXX0 failed",lf) mov dx,offset emsstr3 mov ax,3D00h int 21h .if (CARRY?) invoke printf, CStr("open device EMMXXXQ0 failed",lf) jmp exit .endif .endif .endif push ax push dx invoke printf, CStr("open device ") pop ax invoke printf, ax invoke printf, CStr(" ok",lf) pop bx mov byte ptr [buffer+0], 0 mov byte ptr [buffer+1], -1 mov dword ptr [buffer+2], -1 mov cx,6 call read0 .if (!CARRY?) mov ax, word ptr [buffer+0] mov cx, word ptr [buffer+2] mov dx, word ptr [buffer+4] invoke printf, CStr("%X %X %X",lf),ax,cx,dx .endif mov byte ptr [buffer+0], 2 mov byte ptr [buffer+1], -1 mov cx,2 call read0 .if (!CARRY?) movzx ax, [buffer+0] movzx cx, [buffer+1] invoke printf, CStr("%u.%u",lf),ax,cx .endif mov ah, 3Eh int 21h exit: ret deviceinfo endp start: push sp pop ax cmp ax,sp ;8086? jnz exit ;exit silently pushf pushf pop ax or ah,70h push ax popf pushf pop ax popf and ah,0f0h ;if bits 12-14 are 0, it is a 80286 jz exit ;Z if 80286 push cs pop ds mov ax,ss mov bx,sp shr bx,4 add bx,ax mov ax,es sub bx,ax mov ah,4Ah int 21h sub bx,10h shl bx,4 push ds pop ss mov sp,bx mov di,offset namtab ;clear BSS+stack push es push ds pop es mov cx,sp sub cx,di xor ax,ax shr cx,1 cld rep stosw pop es call main exit: mov ah,4Ch int 21h END start ================================================ FILE: Tools/EMSSTAT/MAKE.BAT ================================================ @echo off rem creates EMSSTAT.EXE with JWasm jwasm -c -nologo -mz EMSSTAT.ASM ================================================ FILE: Tools/EMSSTAT/PRINTF.INC ================================================ ;--- simple printf() implementation handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ;--- outb is expected to be onto stack ltob PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP ;--- ds=dgroup, ss don't need to be dgroup printf PROC c uses si di bx fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx nextdigit: cmp BYTE PTR [si],'0' jb digitsdone cmp BYTE PTR [si],'9' ja digitsdone lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax jmp nextdigit digitsdone: mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u mov al,'%' jmp @@L359 handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn printf ENDP ================================================ FILE: Tools/JEMFBHLP/JEMFBHLP.ASM ================================================ ;*** save some interrupt vectors the way MS-DOS does ;*** needed for FreeDOS if Jemm386's FASTBOOT option is used .286 .MODEL SMALL IODAT struct cmdlen db ? ;+ 0:size unit db ? ;+ 1: cmd db ? ;+ 2 status dw ? ;+ 3 db 8 dup (?); reserved media db ? ;+ 0d trans dd ? ;+ 0e count dw ? ;+ 12 init:offset parameter line start dw ? ;+ 14 init:segment parameter line drive db ? ;+ 16 IODAT ends .CODE dw -1 dw -1 dw 8000h ;attribute dw offset devstrat ;device strategy devc dw offset devintfirst ;device interrupt devname db 'JEMFBHP$' cmdptr dd 1 dup (?) devstrat proc far mov cs:word ptr[cmdptr+0],bx mov cs:word ptr[cmdptr+2],es ret devstrat endp devint proc far push ds push bx lds bx,cs:[cmdptr] mov word ptr [bx.IODAT.status],100h pop bx pop ds ret devint endp int19 proc cld push 70h pop ds xor ax,ax mov es,ax mov si,100h mov cx,NUMVECS nextitem: lodsb mov di,ax shl di,2 movsw movsw loop nextitem int 19h int19 endp resident label byte vecstable db 15h,19h NUMVECS equ $ - vecstable devintfirst proc far pusha push ds push es lds bx,cs:[cmdptr] mov word ptr [bx.IODAT.status],100h cmp [bx.IODAT.cmd],0 ;init call? jnz exit mov word ptr [bx.IODAT.trans+0],0 mov word ptr [bx.IODAT.trans+2],cs mov cs:[devc],offset devint mov ax,4300h int 2fh cmp al,80h ;Himem must not be installed yet jnz @F push cs pop ds mov dx,offset str2 mov ah,9 int 21h jmp exit @@: xor ax,ax mov es,ax mov ax, es:[19h*4+2] ;int 19h must be unmodified (segment >= E000) cmp ax, 0E000h jc exit mov word ptr [bx.IODAT.trans+0],offset resident push 0070h pop es xor ax,ax mov ds,ax mov di,100h mov bx,offset vecstable mov cx,NUMVECS nextitem: mov al,cs:[bx] inc bx mov ah,0 mov si,ax shl si,2 stosb movsw movsw loop nextitem mov word ptr ds:[19h*4+0],offset int19 mov word ptr ds:[19h*4+2],cs exit: pop es pop ds popa ret devintfirst endp str2 db "JEMFBHLP.EXE must be installed *before* Himem",13,10,'$' str1 db "JEMFBHLP is needed for FreeDOS only. It saves some interrupt vectors",13,10 db "the way MS-DOS does, thus allowing to set Jemm386's FASTBOOT option.",13,10 db "It must be loaded in (FD)CONFIG.SYS before the XMS host. Example:",13,10 db "DEVICE=JEMFBHLP.EXE",13,10 db "DEVICE=HIMEM.EXE [or (Q)HIMEM.SYS]",13,10 db "DEVICE=JEMM386.EXE FASTBOOT",13,10 db '$' main: mov dx,offset str1 push cs pop ds mov ah,9 int 21h mov ax,4C00h int 21h .stack 1024 END main ================================================ FILE: Tools/JEMFBHLP/MAKE.BAT ================================================ @echo off rem create JEMFBHLP.EXE with JWasm jwasm -c -nologo -mz -Fl JEMFBHLP.ASM ================================================ FILE: Tools/JLOAD/DEBUG.INC ================================================ ;*** debug macros and equates ?USEMONO equ 0 ;1=use monochrome video mode ifndef ?RMDBG ?RMDBG = 0 ;log real-mode endif ifndef ?INITDBG ?INITDBG = 0 ;log init endif ifndef ?PEDBG ?PEDBG = 0 ;log PE loader endif ifndef ?IODBG ?IODBG = 0 ;log IO trapping endif ifndef ?PAGEDBG ?PAGEDBG = 0 ;log page memory endif ifndef ?JLMDBG ?JLMDBG = 0 ;log jlm handling endif ifndef ?DMADBG ?DMADBG = 0 ;log DMA handling endif ifndef ?V86HOOKDBG ?V86HOOKDBG = 0 ;log v86 hook chain handling endif ifndef ?INT2FHOOKDBG ?INT2FHOOKDBG = 0 ;log Int2F hook handling endif ifdef _DEBUG ?LOG = ?INITDBG + ?PEDBG + ?IODBG + ?PAGEDBG + ?JLMDBG + ?DMADBG + ?V86HOOKDBG + ?INT2FHOOKDBG else ?LOG = 0 endif dprintf proto c :ptr, :vararg ifdef _DEBUG @dprintf macro bCond, fmt, args:vararg if bCond ifnb invoke dprintf, CStr(fmt), args else invoke dprintf, CStr(fmt) endif endif endm else @dprintf textequ <;> endif ================================================ FILE: Tools/JLOAD/DPRINTF.INC ================================================ ;--- printf for debug displays, 32-bit ;--- i64toa(long long n, char * s, int base); ;--- convert 64-bit long long to string i64toa proc stdcall uses edi number:qword, outb:ptr, base:dword mov ch,0 mov edi, base mov eax, dword ptr number+0 mov esi, dword ptr number+4 cmp edi,-10 jne @F neg edi and esi,esi jns @F neg esi neg eax sbb esi,0 mov ch,'-' @@: mov ebx,outb add ebx,22 mov byte ptr ss:[ebx],0 @@nextdigit: dec ebx xor edx,edx xchg eax,esi div edi xchg eax,esi div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[ebx],dl mov edx, eax or edx, esi jne @@nextdigit cmp ch,0 je @F dec ebx mov ss:[ebx],ch @@: mov eax,ebx ret i64toa endp ;--- dprintf has to preserve eflags, but usage of local vars will include a "sub esp,xxx"! ;--- hence a special prologue is required: @dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms push ebp mov ebp,esp lea esp,[esp-localbyte] exitm %localbyte endm OPTION PROLOGUE: @dprologue dprintf proc c public fmt:ptr, args:vararg local flag:byte local longarg:byte local size_:dword local fillchr:dword local szTmp[24]:byte pushad pushfd cld lea edi,args @@L335: mov esi,fmt nextchar: lodsb cs:[esi] or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: popfd popad ret formatitem: push offset @@L335 xor edx,edx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR cs:[esi],'-' jne @F dec bl inc esi @@: mov [flag],bl cmp BYTE PTR cs:[esi],'0' jne @F mov cl,'0' inc esi @@: mov [fillchr],ecx mov ebx,edx .while ( byte ptr cs:[esi] >= '0' && byte ptr cs:[esi] <= '9' ) lodsb cs:[esi] sub al,'0' movzx eax,al imul ecx,ebx,10 ;ecx = ebx * 10 add eax,ecx mov ebx,eax .endw mov [size_],ebx cmp BYTE PTR cs:[esi],'l' jne @F mov [longarg],1 inc esi @@: lodsb cs:[esi] mov [fmt],esi cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'d' je handle_d cmp al,'u' je handle_u cmp al,'s' je handle_s cmp al,'c' je handle_c and al,al jnz @F pop eax jmp done handle_c: mov eax, ss:[edi] add edi, 4 @@: call handle_char retn handle_s: mov esi, ss:[edi] add edi,4 jmp print_string handle_d: handle_i: mov ebx,-10 jmp @F handle_u: mov ebx, 10 jmp @F handle_x: mov ebx, 16 @@: xor edx,edx mov eax, ss:[edi] add edi,4 cmp longarg,1 jnz @F mov edx, ss:[edi] add edi,4 jmp printnum @@: and ebx,ebx jns @F cdq @@: printnum: lea esi, szTmp invoke i64toa, edx::eax, esi, ebx mov esi, eax print_string: ;print string ESI, size EAX mov eax, esi .while byte ptr ss:[esi] inc esi .endw sub esi, eax xchg eax, esi mov ebx,size_ sub ebx,eax .if flag == 1 .while sdword ptr ebx > 0 ;print leading filler chars mov eax, [fillchr] call handle_char dec ebx .endw .endif .while byte ptr ss:[esi] lodsb ss:[esi] call handle_char .endw .while sdword ptr ebx > 0 ;print trailing spaces mov eax, [fillchr] call handle_char dec ebx .endw retn handle_char: cmp al,10 jnz @F mov al,13 call @F mov al,10 @@: call VPUTCHR retn dprintf endp OPTION PROLOGUE: prologuedef ================================================ FILE: Tools/JLOAD/DPRNTF16.INC ================================================ ;--- printf for debug displays, 16-bit ;--- itoa(long n, char * s, int base); ;--- convert 32-bit long to string ltoa PROC stdcall uses edx edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltoa ENDP ;--- dprintf is to preserve registers & flags; needs a special prologue: @dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms push bp mov bp,sp lea esp,[esp-localbyte] ;there exists no 16-bit "lea sp,[sp-xx]" exitm %localbyte endm OPTION PROLOGUE: @dprologue ;--- it's assumed cs=DGROUP (model tiny) ;--- no assume for SS & DS, ES unused. dprintf proc c public uses ds fmt:ptr, args:vararg local flag:byte local longarg:byte local size_:word local fillchr:word local szTmp[12]:byte pusha pushf push ds push cs pop ds cld lea di,args @@L335: mov si,fmt nextchar: lodsb or al,al je done cmp al,'%' je formatitem call VPUTCHR jmp nextchar done: pop ds popf popa ret formatitem: push offset @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bl inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fillchr],cx mov bx,dx .while ( byte ptr [si] >= '0' && byte ptr [si] <= '9' ) lodsb sub al,'0' cbw imul cx,bx,10 ;ecx = ebx * 10 add ax,cx mov bx,ax .endw mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'d' je handle_d cmp al,'u' je handle_u cmp al,'s' je handle_s cmp al,'c' je handle_c and al,al jnz @F pop ax jmp done handle_c: mov ax,ss:[di] add di,2 @@: call VPUTCHR retn handle_s: mov si,ss:[di] add di,2 jmp print_string handle_d: handle_i: mov bx,-10 jmp @F handle_u: mov bx, 10 jmp @F handle_x: mov bx, 16 @@: xor dx,dx mov ax,ss:[di] add di,2 cmp longarg,1 jnz @F mov dx,ss:[di] add di,2 jmp printnum @@: and bx,bx jns @F cdq @@: printnum: lea si, szTmp push eax invoke ltoa, dx::ax, si, bx mov si, ax pop eax push ds push ss pop ds call print_string pop ds retn print_string: ;print string SI mov ax, si .while byte ptr [si] inc si .endw sub si, ax xchg ax, si mov bx,size_ sub bx, ax .if flag == 1 .while sword ptr bx > 0 mov ax, [fillchr] call VPUTCHR ;print leading filler chars dec bx .endw .endif .while byte ptr [si] lodsb [si] call VPUTCHR ;print char of string .endw .while sword ptr bx > 0 mov ax, [fillchr] call VPUTCHR ;print trailing spaces dec bx .endw retn VPUTCHR: cmp al,10 jnz @F mov al,13 call @F mov al,10 @@: push bx xor bx, bx mov ah, 0Eh int 10h pop bx retn dprintf endp OPTION PROLOGUE: prologuedef ================================================ FILE: Tools/JLOAD/History.txt ================================================ v5.87: - Linux.mak added to create JLOAD.EXE on Linux. v5.86: - Jemm's control notifications handled (DEVICE_REBOOT_NOTIFY). - fixed: 16-bit debug displays. - implemented Hook_V86_Fault/Unhook_V86_Fault, using Jemm's service table entry pV86Faults (v5.86). v5.85: - debug displays now done with @dprintf(). - V86 hooks won't modify IDT anymore. v5.84: - trap handling changed: Jemm's IO trap table is no longer imported, instead the old (=Jemm's) handler is called if the port isn't trapped by VMM. This allows to trap ISA DMA ports that are already trapped by Jemm. v5.83: - removed the option to link with MS COFF linker - current versions won't accept a base of 0xf8400000. - released under MIT license. - fixed: import of Jemm's IO port trap ranges did additionally trap "holes" in the DMA16 page register port range D0-DF. - flags PC_CACHEDIS and PC_CACHEWT supported for _PageCommitPhys(). - added _Allocate_GDT_Selector(), _Free_GDT_Selector(), Get_DDB(). - added possibility to suppress JLoad result msgs. v5.82: - fixed: JLMs with API didn't work correctly. - int 2Fh, ax=1684h: when a JLM entry point is returned ( in ES:DI ), AL is set to 0 to indicate success. v5.76: - linker changed from Digital Mars OPTLINK to jwlink - VMM.ASM: bugfix in _PageFree() v5.73: - bugfix: Hook_V86_Int_Chain might have messed the stack. - automatically alloc/free V86 callback if v86 API is to be installed by a device. - when the first v86 API is installed, v86 int 2Fh is hooked to handle ax=1684h. - Option -u added. v5.72: - switched to JWasm assembler v5.71: - bugfix: in v5.70, JLoad's assumtion about location of Jemm's TSS was wrong. v5.69: - both '-' and '/' accepted for switches. - MoveMemory added to Jemm's service table. - DMA copy to/from buffer functions added. v5.68: - bugfix: DMA lock ignored flag to check for 64 kB border crossing. v5.60: - initial. ================================================ FILE: Tools/JLOAD/JLOAD.ASM ================================================ ;--- 16-bit part of JLoad ;--- JLoad loads a 32bit PE binary in Jemm's address space ;--- best viewed with TABSIZE 4 ;--- these segment definitions must be BEFORE .model since alignment is 4 _TEXT segment dword public 'CODE' _TEXT ends _DATA segment dword public 'DATA' _DATA ends .model tiny .dosseg .386 option casemap:none option proc:private include jsystem.inc include jlm.inc include jload.inc include debug.inc include winnt.inc ;--- DStr() define a DOS string in CCONST DStr macro text:vararg local sym CCONST segment ifidni ,<""> sym db '$' else sym db text,'$' endif CCONST ends exitm endm CStr macro text:vararg local sym CCONST segment ifidni ,<""> sym db 0 else sym db text,0 endif CCONST ends exitm endm @tracejmp macro target if ?RMDBG jmp target endif endm ENVIRON equ 2Ch STATUS_OK equ 0100h ; driver is initialized and ok STATUS_BAD equ 8103h ; driver couldn't be installed CMD_INIT equ 0 ; init command (used when installed) FMODE_LFN equ 1 ; fMode flags FOPTION_QUIET equ 1 ; be quiet, don't display information texts ?MAXPATH equ 260 cr equ 0dh lf equ 0ah ?CATCHEXC06 equ 0 ;std=0, 1=catch Exc 06 (pretty useless) ;*** define segments *** .code ; device driver header dd -1 dw 8000h dw offset strategy dw offset interrupt db 'JLOAD$$$' dwRequest dd 0 strategy: mov word ptr cs:[dwRequest+0],bx mov word ptr cs:[dwRequest+2],es retf CCONST segment dword use16 public 'CODE' CCONST ends BEGDATA segment para use16 public 'CODE' BEGDATA ends _DATA segment dword use16 public 'DATA' _DATA ends _BSS segment dword use16 public 'BSS' _BSS ends STACK segment para use16 stack 'STACK' db 1024 dup (?) stacktop label byte STACK ends DGROUP group _TEXT,CCONST,BEGDATA,_DATA,_BSS,STACK ;--- cpu descriptor desc struc limit dw ? ; segment limit base00_15 dw ? ; low word of base address base16_23 db ? ; high byte of base address db ? ; 93h = std ring 0 read/write segment attr db ? ; attributes, limit 16-19 base24_31 db ? desc ends ;--- DOS device driver request header request_hdr struc req_size db ? ; number of bytes stored unit_id db ? ; unit ID code cmd db ? ; command code status dw ? ; status word rsvd db 8 dup (?) ; reserved request_hdr ends ;--- DOS device driver request for INIT init_strc struc init_hdr db size request_hdr dup (?) units db ? ; number of supported units end_addr dd ? ; end address of resident part cmd_line dd ? ; address of command line init_strc ends ;--- VCPI jump to protected mode structure V862PM STRUC swCR3 DD ? ; client's CR3 value swGDTOFFS DD ? ; offset of client's GDTR value swIDTOFFS DD ? ; offset of client's IDTR value swLDTR DW ? ; client's LDTR value swTR DW ? ; client's TR value swCSEIP DF ? ; entry point in client V862PM ENDS ;*** variables *** _DATA segment ;--- GDT used for int 15h, ah=87h i15tab label desc desc <0,0,0,0,0,0> desc <0,0,0,0,0,0> desc <-1,0,0,93h,0CFh,0> desc <-1,0,0,93h,0CFh,0> desc <0,0,0,0,0,0> desc <0,0,0,0,0,0> vcpisw V862PM <0,0,0,0,0,0> align 4 vmms VMMS <<0,0,0,<0>>,<0,0,0,0,0,0,0>,-1,offset szLdrPath, offset szPgmName, offset PE_Hdr, offset buffer, 0> ddb VxD_Desc_Block if ?PMSTACK dwStack dd 0 ;phys page for protected-mode stack endif dwPageTab dd 0 ;phys page for VMM page table dwVmmPage dd 0 ;phys page for VMM code/data dwBuffer dd 0 ;linear address of buffer dwCmdLine dd 0 ;start of cmdline for JLM (terminated by CR or 00) if ?CATCHEXC06 oldint06 dd 0 spsaved dw 0 endif dfIDT df 0 hJemm dw -1 wRet dw 0 fMode db 0 fOption db 0 ;additional option from cmdline ("-q") _DATA ends CCONST segment dUsage db "JLoad v",?VERSIONHIGH+'0',".",@CatStr(!",%?VERSIONLOW/10,%?VERSIONLOW mod 10,!") db " Copyright japheth 2008-2026",13,10 db "JLoad loads JLMs (Jemm Loadable Modules) into Jemm's address space.",13,10 db "It can be run as a DOS device driver or from the command line.",13,10 db "usage:",13,10 db " [DEVICE=] JLOAD.EXE [options] name_of_JLM [arguments]",13,10 db " options:",13,10 db " -q: quiet",13,10 db " -u: unload a JLM",13,10 db '$' szErrExp db "' doesn't export a DDB",13,10,'$' CCONST ends _BSS segment wVersion dw ? ;DOS version major+minor align 4 szLdrPath db ?MAXPATH dup (?) ;JLOAD full path _edata label byte szPgmName db ?MAXPATH dup (?) ;program name from command line MZ_Hdr db 40h dup (?) ;MZ header buffer (same memory as NE header PE_Hdr IMAGE_NT_HEADERS ;--- the buffer should be last. thus it is an additional 4 kB if the default ;--- 1 kB stack is too small. buffer db 4096 dup (?) ;4 kb buffer for copy operations externdef _end:near _BSS ends CCONST segment szLF db lf,0 sig1 db 'EMMXXXX0',0 sig2 db 'EMMQXXX0',0 ; ID if NOEMS specified CCONST ends _TEXT segment assume CS:DGROUP assume DS:DGROUP assume SS:DGROUP assume ES:DGROUP align 4 vmmcode label byte ifdef __JWASM__ ifdef __UNIX__ % incbin else % incbin endif else % include OUTD\JLoad32.inc endif align 4 SIZEVMM equ $ - offset vmmcode _IsJemmInstalled proc @dprintf ?RMDBG,<"IsJemmInstalled enter",lf> mov bx, -1 mov dx, offset sig1 mov ax, 3D00h int 21h jnc @@found mov dx, offset sig2 mov ax, 3D00h int 21h jc @@nojemm @@found: mov bx,ax xor ax,ax push ax push ax push ax mov cx,6 ;read 6 bytes mov dx,sp mov ax,4402h ;read ioctl int 21h pop ax ;version pop cx ;API entry offs pop cx ;API entry segm jc @@nojemm cmp ax,0028h ;this is JEMM! jnz @@nojemm mov ax,bx ;return the file handle clc ret @@nojemm: cmp bx,-1 jz @@noclose mov ah,3Eh int 21h @@noclose: stc ret _IsJemmInstalled endp ;*** read command line parameters ;--- handles jload options /u and /q. ;*** RC: Carry if error ;*** NC: file to load in szPgmName ;--- modifies DS,SI,DI! GetPgmParms proc @dprintf ?RMDBG,<"GetPgmParms enter",lf> lds si,[dwCmdLine] sub cx,cx mov al,0 nextchr: mov ah,al lodsb cmp al,0 jz error cmp al,13 jz error cmp al,' ' ;skip wspaces jbe nextchr cmp al,'/' jz @F cmp al,'-' jnz parmfound @@: mov al,1 jmp nextchr error: stc ret parmfound: cmp ah,1 ;option mode? jnz nooption or al,20h cmp al,'q' jnz @F or es:[fOption], FOPTION_QUIET jmp nextchr @@: cmp al,'u' jnz error or es:[vmms.wFlags], JLF_UNLOAD jmp nextchr nooption: ;must be the file to load mov ah,0 dec si mov dl,0 mov di,offset szPgmName nextchar: lodsb cmp al,0 jz copydone cmp al,13 jz copydone cmp al,'"' jnz @F xor ah,1 jmp skipchar @@: test ah,1 jnz @F cmp al,' ' jz copydone ;copy is done @@: cmp al,'.' jnz @F inc dl @@: stosb cmp al,'/' jz @F cmp al,'\' jnz skipchar @@: mov dl,0 skipchar: jmp nextchar copydone: dec si ;go back to 00 or 0dh byte mov word ptr es:[dwCmdLine],si test ah,1 jnz error mov al,00 stosb clc ret GetPgmParms endp ;--- get path of JLOAD.EXE ;--- DS not set yet GetLdrPath proc uses es si di @dprintf ?RMDBG,<"GetLdrPath enter, CS=%X",lf>, cs cld mov ah,51h int 21h mov es,bx mov bx,es:[ENVIRON] ;get environment and bx,bx jz @@exit mov es,bx xor di,di or cx,-1 xor ax,ax @@: repnz scasb ;search end of environment scasb ;found? jnz @B ;no, continue inc di ;skip 0001 inc di ;now comes current file name mov si,offset szLdrPath @@: mov al,es:[di] mov cs:[si],al inc si inc di and al,al jnz @B @@exit: ret GetLdrPath endp ;--- openfile in DS:DX ;--- returns file handle in AX if NC openfile proc uses si @dprintf ?RMDBG,<"openfile enter",lf> test [fMode], FMODE_LFN jz nolfn mov si,dx MOV AX,716Ch mov dx,1 ;action: fail if not exists xor bx,bx ;read only xor cx,cx ; stc int 21h mov dx,si jnc done cmp ax,7100h stc jnz done nolfn: MOV AX,3D00h ;open a file for read int 21h done: ret openfile endp ;*** read MZ-Header + PE-Header ;*** Input: BX=file handle ;--- DS=DGROUP ;*** C + AX=szNotaPX if not PX format ReadHdrs proc @dprintf ?RMDBG,<"ReadHdrs enter",lf> mov cx,0040h mov dx,offset MZ_Hdr mov ah,3Fh ;read MZ hdr int 21h jc error1 ;DOS read error cmp ax,cx ;could read 40h bytes? jnz error2 mov ax,word ptr [MZ_Hdr] cmp ax,'ZM' jnz error3 mov cx,word ptr [MZ_Hdr+3Eh] mov dx,word ptr [MZ_Hdr+3Ch] and dx,dx jz error4 mov ax,4200h ;lseek PE hdr int 21h jc error5 ;DOS lseek error, no PE binary mov cx,sizeof PE_Hdr mov dx,offset PE_Hdr mov ah,3Fh ;read int 21h jc error6 ;DOS read error cmp ax,cx jnz error7 cmp PE_Hdr.Signature,"XP" jnz error6 cmp PE_Hdr.FileHeader.Machine, IMAGE_FILE_MACHINE_I386 jnz error8 @dprintf ?RMDBG, <"ReadHdrs ok",lf> clc ret error4: @dprintf ?RMDBG, <"offset to new header is zero",lf> @tracejmp @F error2: @dprintf ?RMDBG, <"filesize less than 0x0040 Bytes",lf> @tracejmp @F error5: @dprintf ?RMDBG, <"lseek error",lf> @tracejmp @F error7: @dprintf ?RMDBG, <"PE-Header size too small",lf> @tracejmp @F error8: @dprintf ?RMDBG, <"not a 80386 binary",lf> @tracejmp @F error3: @dprintf ?RMDBG, <"cannot find 'MZ' Header",lf> @tracejmp @F error6: @dprintf ?RMDBG, <"PX not found",lf> @tracejmp @F error1: @dprintf ?RMDBG, <"read error (MZ)",lf> @tracejmp @F @@: mov cx,DStr("' isn't a PX binary",cr,lf) stc ret ReadHdrs endp LoadSectionTable proc stdcall ; mov dx,word ptr secpos+0 ; mov cx,word ptr secpos+2 ; mov ax,4200h ; int 21h mov cx, PE_Hdr.FileHeader.NumberOfSections mov ax, sizeof IMAGE_SECTION_HEADER mul cx and dx,dx jnz error cmp ax,sizeof buffer ja error mov dx, offset buffer mov cx, ax mov ah,3Fh ;read section header int 21h jc error ret error: stc ret LoadSectionTable endp ;--- find section which contains requested RVA, then position file pointer FindRVA proc stdcall uses si rva:dword mov si, offset buffer mov cx, PE_Hdr.FileHeader.NumberOfSections .while (cx) mov eax, [si].IMAGE_SECTION_HEADER.VirtualAddress mov edx, rva cmp edx,eax jc @F add eax, [si].IMAGE_SECTION_HEADER.SizeOfRawData cmp edx,eax cmc jnc found @@: dec cx add si, sizeof IMAGE_SECTION_HEADER .endw stc exit: ret found: sub edx, [si].IMAGE_SECTION_HEADER.VirtualAddress add edx, [si].IMAGE_SECTION_HEADER.PointerToRawData push edx pop dx pop cx mov ax,4200h int 21h jc exit mov ax,si jmp exit FindRVA endp ;--- read a module's DDB into ddb variable ;*** Input: BX=file handle ReadDDB proc local ddbadr:dword local expdir:IMAGE_EXPORT_DIRECTORY cmp PE_Hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,0 jz errexp invoke LoadSectionTable jc errexp invoke FindRVA, PE_Hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress jc errexp ;--- RVA found, now read export directory lea dx,expdir mov cx,sizeof expdir mov ah,3Fh int 21h jc errexp ;--- now read first entry of IAT cmp expdir.NumberOfFunctions,1 jc errexp invoke FindRVA, expdir.AddressOfFunctions jc errexp lea dx,ddbadr mov cx,sizeof ddbadr mov ah,3Fh int 21h jc errexp ;--- now position to ddb and read it invoke FindRVA, ddbadr jc errexp mov dx,offset ddb mov cx,sizeof ddb mov ah,3Fh int 21h jc errexp mov [vmms.pDDB],offset ddb ret errexp: stc ret ReadDDB endp ;--- copy memory to/from extended memory using int 15h, ah=87h CopyExtMem proc stdcall uses si dwDst:DWORD, dwSrc:DWORD, wSize:WORD @dprintf ?RMDBG, <"CopyExtMem dst=%lX, src=%lX",10>, dwDst, dwSrc mov si,offset i15tab mov ax, word ptr [dwSrc+0] mov [si+2*size desc].desc.base00_15,ax mov ax, word ptr [dwSrc+2] mov [si+2*size desc].desc.base16_23,al mov [si+2*size desc].desc.base24_31,ah mov ax, word ptr [dwDst+0] mov [si+3*size desc].desc.base00_15,ax mov ax, word ptr [dwDst+2] mov [si+3*size desc].desc.base16_23,al mov [si+3*size desc].desc.base24_31,ah mov cx,wSize shr cx,1 mov ah,87h int 15h @dprintf ?RMDBG, <"CopyExtMem exit",lf> ret CopyExtMem endp ;--- get linear address of an offset GetLinear proc stdcall wOfs:WORD mov ax,ds movzx eax,ax shl eax,4 push ecx mov cx,wOfs movzx ecx,cx add eax,ecx pop ecx ret GetLinear endp ;--- init VMM if JLOAD runs the first time (and a JLM is to be loaded) ;--- alloc 1 page for page table ;--- alloc 1 or 2 page(s) for VMM code (will be moved to ?SYSTEMADDR) ;--- returns AX==0 on failure InitVMM proc uses di esi @dprintf ?RMDBG, <"InitVMM enter",lf> invoke GetLinear, offset buffer mov [dwBuffer], eax if SIZEVMM gt 1000h xor esi,esi endif mov ecx, cr3 and cx, 0F000h invoke CopyExtMem, eax, ecx, 1000h ;copy page dir to buffer jnc @F mov dx,DStr("unable to read page directory",13,10) jmp error @@: mov edx,dword ptr [buffer+(?SYSTEMADDR shr 20)] and dx,0F000h mov [dwPageTab],edx cmp edx,0 ;VMM installed already? jnz exit mov dx,DStr("no JLMs loaded",13,10) test [vmms.wFlags],JLF_UNLOAD ;error if NO and "unload" mode jnz error mov ax,0DE04h ;get phys page for VMM page table int 67h and ah,ah jz @F mov dx,DStr("cannot allocate 4k page for page table",13,10) jmp error @@: mov [dwPageTab],edx mov ax,0DE04h ;get page for VMM itself int 67h and ah,ah jz @F scmemerr: mov dx,DStr("cannot allocate 4k page for system code",13,10) jmp error @@: mov [dwVmmPage],edx if SIZEVMM gt 1000h mov ax,0DE04h ;get page for VMM itself int 67h and ah,ah jnz scmemerr mov esi,edx endif mov edx, [dwPageTab] ;set PDE for VMM page table or dl,7 mov dword ptr [buffer+(?SYSTEMADDR shr 20)],edx mov ecx,cr3 ;"write" updated page dir and cx, 0F000h invoke CopyExtMem, ecx, [dwBuffer], 1000h mov dx,DStr("error setting content of page dir",13,10) jc error ;--- clear (last) VMM page content mov di,offset buffer mov cx,1000h/4 xor eax,eax rep stosd if SIZEVMM gt 1000h invoke CopyExtMem, esi, [dwBuffer], 1000h else invoke CopyExtMem, [dwVmmPage], [dwBuffer], 1000h endif ;--- set PTE for VMM code/data in VMM page table mov byte ptr [buffer],6 ;in case the first page is "reserved" mov edx, [dwVmmPage] or dl,3 mov dword ptr [buffer+((?SYSTEMADDR and 03FFFFFh) shr 10)],edx if SIZEVMM gt 1000h mov edx, esi or dl,3 mov dword ptr [buffer+((?SYSTEMADDR and 03FFFFFh) shr 10)+4],edx endif ;--- copy content of VMM page table to extended memory invoke CopyExtMem, [dwPageTab], [dwBuffer], 1000h mov dx,DStr("error setting content of system page table",13,10) jc error ;--- copy VMM code to extended memory if SIZEVMM le 1000h invoke GetLinear, offset vmmcode invoke CopyExtMem, [dwVmmPage], eax, SIZEVMM else invoke GetLinear, offset vmmcode invoke CopyExtMem, [dwVmmPage], eax, 1000h invoke GetLinear, offset vmmcode+1000h invoke CopyExtMem, esi, eax, SIZEVMM-1000h endif exit: @dprintf ?RMDBG, <"InitVMM ok",lf> mov ax,1 ret error: call errorout mov edx, [dwPageTab] call freepg mov edx, [dwVmmPage] call freepg if SIZEVMM le 1000h mov edx, esi call freepg endif xor ax,ax ret freepg: and edx, edx jz @F mov ax,0DE05h int 67h @@: retn InitVMM endp ;--- enter 32bit VMM code ;--- returns ax==0 on failure ;--- allocs a 4kB stack RunVMM proc public @dprintf ?RMDBG, <"RunVMM enter, dwCmdLine=%lX",lf>, dwCmdLine ;--- set (common) linear addresses in JLCOMM movzx eax, word ptr dwCmdLine+2 movzx ecx, word ptr dwCmdLine+0 shl eax,4 add eax, ecx mov [vmms.lpCmdLine],eax if ?PMSTACK ;--- allocate a 4k page to be used for protected-mode stack mov ax,0DE04h int 67h and ah,ah jz @F mov dx,DStr("no memory for protected-mode stack",13,10) call errorout xor ax,ax ret @@: mov [dwStack], edx endif ;--- install a handler for "invalid opcodes" ;--- which is the Jemm method of telling that an error has occured if ?CATCHEXC06 push es mov ax,3506h int 21h mov word ptr [oldint06+0],bx mov word ptr [oldint06+2],es pop es mov dx,offset int06 mov ax,2506h int 21h endif @dprintf ?RMDBG, <"RunVMM: preparing switch to protected-mode",lf> mov eax, cr3 mov vcpisw.swCR3, eax invoke GetLinear, offset vmms.emx08.e08_GDTR mov vcpisw.swGDTOFFS, eax invoke GetLinear, offset vmms.emx08.e08_IDTR mov vcpisw.swIDTOFFS, eax mov vcpisw.swLDTR, 0 mov ax, vmms.emx08.e08_TR mov vcpisw.swTR, ax mov eax, ?SYSTEMADDR mov dword ptr vcpisw.swCSEIP, eax mov ax, vmms.emx08.e08_FlatCS mov word ptr vcpisw.swCSEIP+4, ax invoke GetLinear, offset szPgmName mov ebx,eax invoke GetLinear, offset vcpisw mov esi, eax invoke GetLinear, offset vmms mov ecx, eax if ?PMSTACK mov edi, [dwStack] endif if ?CATCHEXC06 mov [spsaved],sp endif @dprintf ?RMDBG, <"RunVMM: switch to protected-mode ...",lf> mov ax,0DE0Ch int 67h sti @dprintf ?RMDBG, <"RunVMM: back in v86-mode",lf> ;--- returncode from JLM is stored in EDX push edx if ?PMSTACK ;--- free the 4kB stack mov edx, [dwStack] mov ax,0DE05h int 67h endif if ?CATCHEXC06 push ds lds dx,[oldint06] mov ax,2506h int 21h pop ds endif pop eax ret if ?CATCHEXC06 int06: push cs pop ds push cs pop es mov sp,[spsaved] mov dx,DStr("exception occured in protected-mode",13,10) call errorout xor ax,ax jmp error_occured endif RunVMM endp ;--- display msg 'JLoad: ' errorout proc push dx mov dx,DStr("JLoad: ") mov ah,9 int 21h pop dx mov ah,9 int 21h ret errorout endp ;--- display an asciiz string dispstr proc stdcall uses si pString:word mov si, pString nextitem: lodsb and al,al jz done mov dl,al mov ah,2 int 21h jmp nextitem done: ret dispstr endp errorXout2: mov dx,DStr("'") ;--- display an error msg of format: ;--- JLoad: errorXout proc push cx call errorout push offset szPgmName call dispstr pop dx mov ah,9 int 21h ret errorXout endp ;--- load a module and run its initialization code LoadModule proc @dprintf ?RMDBG, <"LoadModule enter",lf> mov dx,offset szPgmName call openfile jnc @F mov cx,DStr("' cannot be opened",13,10) call errorXout2 jmp exit @@: mov vmms.hFile,ax mov bx,ax call ReadHdrs jnc @F call errorXout2 jmp exit @@: test PE_Hdr.FileHeader.Characteristics, IMAGE_FILE_RELOCS_STRIPPED jz @F mov cx,DStr("' isn't relocatable",13,10) call errorXout2 jmp exit @@: cmp PE_Hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,0 jz @F mov cx,DStr("' has references to external modules",13,10) call errorXout2 jmp exit @@: call ReadDDB .if (CARRY? && ([vmms.wFlags] & JLF_UNLOAD)) mov cx,offset szErrExp ;doesn't export DDB call errorXout2 stc jmp exit .endif call InitVMM ;init the VMM and ax,ax jz exit .if ([vmms.wFlags] & JLF_UNLOAD) push es mov ah,52h ;get start of driver chain int 21h add bx,22h movzx ebx,bx mov ax,es pop es movzx eax,ax shl eax,4 add eax, ebx mov [vmms.lpDrivers],eax .endif ;--- set linear address movzx eax,word ptr dwRequest+2 movzx ecx,word ptr dwRequest+0 shl eax,4 add eax, ecx mov [vmms.lpRequest],eax ;--- run the VMM call RunVMM if ?CATCHEXC06 error_occured: endif mov [wRet],ax bt eax, 31 jc @@nodispsuccess and ax,ax jz @F ; test byte ptr [vmms.wFlags],JLF_DRIVER ; jz @@nodispsuccess test [fOption],FOPTION_QUIET jnz @@nodispsuccess @@: ;--- todo: register CL has been set somewhere if ax==0. Describe the details! .if ([vmms.wFlags] & JLF_UNLOAD) .if (ax) mov cx,DStr("' unloaded successfully.",13,10) .else .if (cl == 2) test [fOption],FOPTION_QUIET jnz @@nodispsuccess mov cx,DStr("' isn't loaded.",13,10) .else mov cx,DStr("' failed to unload.",13,10) .endif .endif .else .if (ax) mov cx,DStr("' loaded successfully.",13,10) .else .if (cl == 0B7h) test [fOption],FOPTION_QUIET jnz @@nodispsuccess mov cx,DStr("' already loaded.",13,10) .else mov cx,DStr("' failed to load.",13,10) .endif .endif .endif call errorXout2 @@nodispsuccess: exit: mov bx,vmms.hFile cmp bx,-1 jz @F mov ah,3Eh int 21h @@: ret LoadModule endp ;--- common main for .EXE and device driver main proc push cs pop ds push cs pop es mov ax,ss mov bx,sp push ds pop ss mov sp,offset stacktop-2 push ax push bx mov di,offset _edata ; clear BSS segment mov cx,offset _end sub cx,di shr cx,1 xor ax,ax cld rep stosw @dprintf ?RMDBG, <"JLoad main enter",lf> if 0 mov dx,DStr("JLoad full path: '") mov ah,9 int 21h push offset szLdrPath call dispstr mov dx,DStr("'",13,10) mov ah,9 int 21h endif mov ah,30h int 21h mov [wVersion],ax mov [vmms.wLdrCS],cs ;detect if lfn is installed mov ax,7147h mov si,offset szPgmName mov dl,0 stc int 21h jc @F or fMode, FMODE_LFN @@: push ds push si push di call GetPgmParms pop di pop si pop ds jnc @F mov dx,offset dUsage mov ah,9 int 21h jmp exit @@: call _IsJemmInstalled jnc @F mov dx,DStr("Jemm not installed",13,10) call errorout jmp exit @@: mov hJemm, ax mov bx, ax push 2 ;read version mov cx,2 ;returns 1 word mov dx, sp mov ax,4402h ;read ioctl int 21h pop ax cmp ax,?VERSIONHIGH + 256 * ?VERSIONLOW jz @F mov dx,DStr("versions of Jemm and JLoad don't match",13,10) call errorout jmp exit @@: mov dx, offset vmms.emx08 mov byte ptr [vmms.emx08],8 ;read VMM info mov cx,size EMX08 mov ax,4402h ;read ioctl int 21h jnc @F mov dx,DStr("no support for protected mode API",13,10) call errorout jmp exit @@: sidt [dfIDT] mov eax,dword ptr [dfIDT+2] cmp eax,dword ptr vmms.emx08.e08_IDTR+2 jz @F mov dx,DStr("virtualized environment detected",13,10) call errorout @@: call LoadModule ;load / unload module! exit: mov bx,hJemm cmp bx,-1 jz @F mov ah,3Eh int 21h @@: cmp [wRet],0 setz al mov ah,0 @dprintf ?RMDBG, <"JLoad main exit",lf> pop cx pop ss mov sp,cx ret main endp if 0 ;--- bin to ascii conversion ;--- es:di -> output buffer DWORDOUT: push eax shr eax, 16 call WORDOUT pop eax WORDOUT: ;<-- bin2ascii(WORD in AX) (-> ES:DI) push ax mov al,ah call BYTEOUT pop ax BYTEOUT: mov ah,al shr al,4 call NIBOUT mov al,ah NIBOUT: and al,0Fh add al,'0' cmp al,'9' jle @F add al,07h @@: stosb ret endif ;--- entry if loaded as device driver in config.sys interrupt proc far pushad push es push ds les di, cs:[dwRequest] mov es:[di].request_hdr.status,STATUS_OK ; we're alright cmp es:[di].request_hdr.cmd,CMD_INIT jne @@done mov word ptr es:[di+0].init_strc.end_addr,0 mov word ptr es:[di+2].init_strc.end_addr,cs lds si,es:[di].init_strc.cmd_line push cs pop es cld mov di, offset szLdrPath .while (byte ptr [si] > ' ') movsb .endw mov al,0 stosb mov word ptr cs:[dwCmdLine+0],si mov word ptr cs:[dwCmdLine+2],ds or byte ptr cs:[vmms.wFlags],JLF_DRIVER call main @@done: pop ds pop es popad ret interrupt endp ;--- entry for .EXE ;--- if the module returns with dwRequest != 0, ;--- the loader will terminate with ah=31h (stay resident) start: cld mov si,80h lodsb mov bl,al mov bh,0 mov byte ptr [si+bx],0 mov word ptr cs:[dwCmdLine+0],si mov word ptr cs:[dwCmdLine+2],ds call GetLdrPath call main mov ah,4Ch test [vmms.wFlags], JLF_UNLOAD jnz @F mov dx,word ptr [vmms.lpRequest] and dx,dx jz @F mov ah,31h @@: int 21h ifdef _DEBUG include dprntf16.inc endif _TEXT ends end start ================================================ FILE: Tools/JLOAD/JLOAD.INC ================================================ ;--- assembly conditionals ;--- constants ;--- ?SYSBASE is from JSYSTEM.INC (=0F8000000h) ?SYSTEMADDR equ ?SYSBASE+400000h;linear address "system" region ?PRIVATEADDR equ 400000h ;linear address "private" region ?PMSTACK equ 1 ;1=alloc a 4 kB stack space; 0=use Jemm's stack ;--- communication structure 16-bit real-mode -> 32-bit protected-mode VMMS struc JLCOMM emx08 EMX08 hFile dw ? ;file handle of JLM pszLdr dw ? ;points to full path of JLOAD.EXE pszJLM dw ? ;points to JLM name parameter pHdr dw ? ;points to PE header pBuffer dw ? ;points to a 4 kB buffer in conv. memory pDDB dw ? ;points to DDB (if module is to be unloaded) VMMS ends ================================================ FILE: Tools/JLOAD/JLOAD.TXT ================================================ 1. About JLoad and JLMs JLoad is an extension for Jemm (both Jemm386 and JemmEx) which allows to load 32bit flat protected-mode modules ( called "Jemm Loadable Module", JLM ) into Jemm's address space. JLMs can be used for various tasks, for example: - implement a protected-mode version of a DOS device driver. - implement a protected-mode version of a DOS TSR. - provide services for other JLMs (kind of a ring 0 dll). - run as a ring 0 protected-mode application which will be unloaded automatically after it has its job done. - emulate hardware by trapping I/O port access. A JLM is supposed to be loaded by JLoad, either as a device driver in CONFIG.SYS, or from the command line. The syntax is: [DEVICE=]JLOAD.EXE [ options ] name_of_JLM [arguments for JLM] The name of a JLM is always required as parameter. Options are: -q: quiet mode. -u: unload a JLM. To be able to unload a JLM, several conditions must be true. First, the JLM must export a so-called DDB. Second, the DDB must contain a valid "Device Id". And third, the JLM must not refuse to be unloaded. Note: currently the versions of Jemm and JLoad always must match, else JLoad will refuse to do anything. A few JLMs (XDMA32, XCDROM32 and AHCICD) are included in the Jemm binary package. For more details see XDMA32.TXT, XCDROM32.TXT and AHCICD.TXT. 2. Creating the Binaries JLoad.exe is a 16-bit binary. It contains a flat, zero-based 32-bit part that is included as a binary object during assembly time. While the 32-bit part (JLoad32.bin) is generated, the linker may emit warning "stack segment not found", which can safely be ignored. For details about the tools needed for creating the binaries see file Makefile. 3. Technical Details about JLMs JLMs must be non-segmented and linked as Win32 PE binaries. But despite the similarities, JLMs should not be confused with such binaries. Some of the differences are: - JLMs always run in ring 0 protected-mode. - there is no Win32 API (and also no DOS/DPMI API) available. - JLMs cannot be linked with Win32 import libraries. Because of the fundamental differences, JLMs should be linked with subsystem "native". Thus they aren't recognised anymore as Win32 binaries. The only API JLMs can use directly is the one installed by JLoad, which more or less is a small subset of the Windows 3x/9x VMM API. This API usually is invoked by an INT 20h opcode, followed by two words that determine the module and function to be called. The API also allows "nested execution", that is, a JLM can indirectly call BIOS or other software interrupts outside of the client's context. For details about what functions are implemented see files JLM.INC or JLM.H. There is a strong similiarity between JLMs and Windows 3x/9x VxDs, but there are also some major differences: - Jemm does not support Virtual Machines (VMs) currently. - Jemm has no integrated DPMI host. Therefore the client will always be in V86-mode. DPMI applications can run only outside of Jemm's context with the help of an external DPMI host that runs as VCPI client. - JLMs run with interrupts disabled. If they do a lengthy operation or have to wait, they must "yield" to allow interrupts to be serviced. Note that the first invokation of JLoad will actually make JLoad resident in Jemm's address space. Further invokations will call the resident part. This is relevant if multiple versions of JLoad are used, for example both a release and a debug version. A detailed documentation about the Win3x/9x APIs that are partly implemented by JLoad can be found in: - Win95 DDK, file VMM.HLP - Win98 DDK, file OTHER.CHM - Win2k DDK, file OTHER.CHM 4. Debugging JLMs Loading JDeb386 - which is a JLM itself - is the easiest method to debug JLMs. This debugger has a simple line-oriented interface. Once loaded, a breakpoint ( or pressing SysReq ) will activate the debugger. Alternatively, Deb386v.sys may be used. This is a DebugR variant in DOS device driver format. This debugger has the advantage that it can debug Jemm during its initialization phase. Japheth ================================================ FILE: Tools/JLOAD/JLOAD.txt ================================================ 1. About JLoad and JLMs JLoad is an extension for Jemm (both Jemm386 and JemmEx) which allows to load 32bit flat protected-mode modules ( called "Jemm Loadable Module", JLM ) into Jemm's address space. JLMs can be used for various tasks, for example: - implement a protected-mode version of a DOS device driver. - implement a protected-mode version of a DOS TSR. - provide services for other JLMs (kind of a ring 0 dll). - run as a ring 0 protected-mode application which will be unloaded automatically after it has its job done. - emulate hardware by trapping I/O port access. A JLM is supposed to be loaded by JLoad, either as a device driver in CONFIG.SYS, or from the command line. The syntax is: [DEVICE=]JLOAD.EXE [ options ] name_of_JLM [arguments for JLM] The name of a JLM is always required as parameter. Options are: -q: quiet mode. -u: unload a JLM. To be able to unload a JLM, several conditions must be true. First, the JLM must export a so-called DDB. Second, the DDB must contain a valid "Device Id". And third, the JLM must not refuse to be unloaded. Note: currently the versions of Jemm and JLoad always must match, else JLoad will refuse to do anything. A few JLMs (XDMA32, XCDROM32 and AHCICD) are included in the Jemm binary package. For more details see XDMA32.TXT, XCDROM32.TXT and AHCICD.TXT. 2. Creating the Binaries JLoad.exe is a 16-bit binary. It contains a flat, zero-based 32-bit part that is included as a binary object during assembly time. While the 32-bit part (JLoad32.bin) is generated, the linker may emit warning "stack segment not found", which can safely be ignored. For details about the tools needed for creating the binaries see file Makefile. 3. Technical Details about JLMs JLMs must be non-segmented and linked as Win32 PE binaries. But despite the similarities, JLMs should not be confused with such binaries. Some of the differences are: - JLMs always run in ring 0 protected-mode. - there is no Win32 API (and also no DOS/DPMI API) available. - JLMs cannot be linked with Win32 import libraries. Because of the fundamental differences, JLMs should be linked with subsystem "native". Thus they aren't recognised anymore as Win32 binaries. The only API JLMs can use directly is the one installed by JLoad, which more or less is a small subset of the Windows 3x/9x VMM API. This API usually is invoked by an INT 20h opcode, followed by two words that determine the module and function to be called. The API also allows "nested execution", that is, a JLM can indirectly call BIOS or other software interrupts outside of the client's context. For details about what functions are implemented see files JLM.INC or JLM.H. There is a strong similiarity between JLMs and Windows 3x/9x VxDs, but there are also some major differences: - Jemm does not support Virtual Machines (VMs) currently. - Jemm has no integrated DPMI host. Therefore the client will always be in V86-mode. DPMI applications can run only outside of Jemm's context with the help of an external DPMI host that runs as VCPI client. - JLMs run with interrupts disabled. If they do a lengthy operation or have to wait, they must "yield" to allow interrupts to be serviced. Note that the first invokation of JLoad will actually make JLoad resident in Jemm's address space. Further invokations will call the resident part. This is relevant if multiple versions of JLoad are used, for example both a release and a debug version. A detailed documentation about the Win3x/9x APIs that are partly implemented by JLoad can be found in: - Win95 DDK, file VMM.HLP - Win98 DDK, file OTHER.CHM - Win2k DDK, file OTHER.CHM 4. Debugging JLMs Loading JDeb386 - which is a JLM itself - is the easiest method to debug JLMs. This debugger has a simple line-oriented interface. Once loaded, a breakpoint ( or pressing SysReq ) will activate the debugger. Alternatively, Deb386v.sys may be used. This is a DebugR variant in DOS device driver format. This debugger has the advantage that it can debug Jemm during its initialization phase. Japheth ================================================ FILE: Tools/JLOAD/JLOAD32.ASM ================================================ ;--- JLoad's PE file loader ;--- this code is copied to linear address ?SYSTEMADDR when ;--- JLoad is running for the first time. ;--- best viewed with TAB size 4 .386 .model flat option casemap:none option proc:private include x86.inc include jsystem.inc include jlm.inc include jload.inc include jload32.inc include winnt.inc include debug.inc if ?PMSTACK ?SYSTABSTKIDX equ 3ffh endif .data ;--- variables g_dwIDT dd 0 ;linear address IDT .code ;--- entry from real-mode - must be at offset 0. ;--- This is a true VCPI entry, meaning SS:ESP still on host stack. ;--- CS,DS,ES=Flat (v5.86: CS is correct, but DS/ES was just an assumption) ;--- SS:ESP -> top of 128 byte vcpi stack of Jemm ;--- ecx->VMMS structure ;--- edi=phys page for protected-mode stack (if ?PMSTACK==1) ;--- out: EDX=returncode from JLM start proc c public ;--- v5.86: don't assume DS/ES are flat. It's true for Jemm, but ;--- another program may have hooked into int 67h, ax=DE0Ch! mov eax, cs add eax, 8 mov ds, eax mov es, eax if ?PMSTACK ;--- set the 4kB stack allocated in 16-bit init code ;--- to be fixed: don't modify PTEs of the F8000000-F83FFFFF region, it's ;--- reserved for Jemm! or edi, PTF_PRESENT or PTF_RW or PTF_USER xchg edi,ds:[?SYSBASE+?PAGETABSYS+?SYSTABSTKIDX*4] ;set the stack PTE ( =F83FF000h ) ;--- init stack. It's flat; since Jemm's ?SYSBASE is 0F8000000h, the ;--- stack is F8400000h. mov ss, eax mov esp, ?SYSBASE + 400000h push edi ;save old PTE for stack else mov ss, eax mov esp, ?TOS - 200h endif cmp word ptr [dfOldint20+4],0 ;already initialized? jnz @F call InitVMM jc @@exit @@: call Get_Cur_VM_Handle mov ebp,[ebx].cb_s.CB_Client_Pointer .if ([ecx].VMMS.wFlags & JLF_UNLOAD) call SearchJLM jc @@error mov ebx, [eax].VxD_Desc_Block.DDB_Reserved1 .else call LoadJLM jc @@error mov esi,[ebx+3Ch] add esi, ebx ;point to NT header call DoFixups jc @@error call FreeDiscardableSections call SetDDB .endif ;--- call the modules entry point mov esi,[ebx].IMAGE_DOS_HEADER.e_lfanew add esi, ebx ;point to NT header mov edi, ebx mov eax, [esi].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint add eax, edi push edi push esi test [esi].IMAGE_NT_HEADERS.FileHeader.Characteristics, IMAGE_FILE_DLL jz isapp test [ecx].VMMS.wFlags, JLF_UNLOAD jz @@isload push ecx push DLL_PROCESS_DETACH push edi call eax pop esi pop edi and ax, ax ; check AX only! jz @@exit jmp @@unload @@isload: push ecx push DLL_PROCESS_ATTACH push edi isapp: call eax pop esi pop edi and ax, ax ; check AX only! jz @@unload test [esi].IMAGE_NT_HEADERS.FileHeader.Characteristics, IMAGE_FILE_DLL jnz @@exit @@unload: call UnloadJLM jmp @@exit @@error: xor eax,eax @@exit: @dprintf ?INITDBG, <"start exit (returns to v86)",10> mov [ebp].Client_Reg_Struc.Client_EDX, eax if ?PMSTACK pop eax endif mov esp, ebp if ?PMSTACK mov ds:[?SYSBASE+?PAGETABSYS+?SYSTABSTKIDX*4], eax ;restore stack PTE mov eax, cr3 ;flush TLB to validate the just restored stack PTE mov cr3, eax endif popad add esp, 4+4 ; skip int# and errcode iretd align 4 start endp ;--- ecx=VMMS ;--- ebx=module handle SetDDB proc pushad movzx esi, [ecx].VMMS.pDDB and esi, esi jz exit @dprintf ?PEDBG, <"SetDDB: calling VMM_Add_DDB",10> mov eax, [ebx+3Ch] add eax, ebx mov eax,[eax].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress add eax, ebx mov eax,[eax].IMAGE_EXPORT_DIRECTORY.AddressOfFunctions add eax, ebx mov esi, [eax] add esi, ebx mov [esi].VxD_Desc_Block.DDB_Reserved1, ebx ;store module in DDB call VMM_Add_DDB exit: mov [ebx+38h],esi ;store DDB in module popad ret align 4 SetDDB endp ;--- search a JLM ;--- ECX=VMMS ;--- to find a JLM, it must have an associated DDB with an ID or a name ;--- out: NC: EAX=module handle ;--- C: eax=return code ;--- if NC, ECX is preserved SearchJLM proc movzx edi, [ecx].VMMS.pDDB and edi, edi jz error movzx eax, word ptr [ebp].Client_Reg_Struc.Client_DS shl eax, 4 add edi, eax movzx eax,[edi].VxD_Desc_Block.DDB_Req_Device_Number push ecx push edi lea edi, [edi].VxD_Desc_Block.DDB_Name call FindDevice pop edi pop ecx jc error2 @dprintf ?PEDBG, <"SearchJLM: device found",10> ret error2: @dprintf ?PEDBG, <"SearchJLM: device not found",10> jmp @F error: @dprintf ?PEDBG, <"SearchJLM: no DDB",10> @@: mov cx,2 ;not found stc ret align 4 SearchJLM endp ;--- load a JLM ;--- 1. find an address space in system region ;--- 2. commit the pages ;--- 3. read the file with nested execution ;--- fixups aren't resolved yet! ;--- inp: ECX -> VMMS, EBP -> client regs ;--- out: NC if ok ;--- EBX=module handle ;--- ESI=DDB LoadJLM proc @dprintf ?PEDBG, <"LoadJLM enter, ecx=%X, calling SearchJLM",10>, ecx pushad call SearchJLM ;already loaded? jnc error3 mov ecx,[esp].PUSHADS.rECX ;restore ecx movzx edi, [ecx].VMMS.pHdr movzx eax, word ptr [ebp].Client_Reg_Struc.Client_DS shl eax, 4 add edi, eax mov ax, [ecx].VMMS.hFile mov word ptr [ebp].Client_Reg_Struc.Client_EBX, ax mov bx, [ecx].VMMS.pBuffer mov esi, [edi].IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage shr esi, 12 push 0 push esi push PR_SYSTEM call _PageReserve add esp,3*4 cmp eax,-1 jz error mov [esp].PUSHADS.rEBX, eax ;linear address shr eax, 12 push PC_FIXED or PC_WRITEABLE push 0 push PD_ZEROINIT push esi push eax call _PageCommit add esp,5*4 and eax, eax jz error mov esi,[edi].IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeaders .if (esi > 1000h) mov esi, 1000h .endif xor eax, eax mov edi, [esp].PUSHADS.rEBX call ReadFile jc error2a add edi, [edi+3Ch] movzx ecx, [edi].IMAGE_NT_HEADERS.FileHeader.NumberOfSections add edi, sizeof IMAGE_NT_HEADERS .while (ecx) push ecx push edi mov ecx, [edi].IMAGE_SECTION_HEADER.SizeOfRawData mov eax, [edi].IMAGE_SECTION_HEADER.PointerToRawData mov edi, [edi].IMAGE_SECTION_HEADER.VirtualAddress add edi, [esp+2*4].PUSHADS.rEBX .while (ecx) mov esi, ecx .if (esi > 1000h) mov esi, 1000h .endif push ecx push eax call ReadFile jc error2 pop eax pop ecx add eax, esi add edi, esi sub ecx, esi .endw pop edi pop ecx add edi, sizeof IMAGE_SECTION_HEADER dec ecx .endw popad ret error3: @dprintf ?PEDBG, <"LoadJLM: JLM already loaded",10> stc popad mov cx,0B7h ;already exists ret error2: add esp,4*4 error2a: @dprintf ?PEDBG, <"LoadJLM: ReadFile error2",10> error: stc popad mov cx,8 ;memory error ret align 4 LoadJLM endp ;--- unload the JLM's memory block ;--- inp: EDI=linear address UnloadJLM proc pushad @dprintf ?PEDBG, <"UnloadJLM enter",10> mov edi,[edi+38h] ; address DDB stored in header and edi,edi jz @F @dprintf ?PEDBG, <"UnloadJLM: calling VMM_Remove_DDB",10> call VMM_Remove_DDB @@: mov edi,[esp].PUSHADS.rEDI push 0 ;flags push edi call _PageFree add esp, 2*4 popad ret align 4 UnloadJLM endp ;--- read a file portion (4kB) ;--- eax = file pos ;--- si = bytes to read ;--- bx = buffer offset ;--- edi = target addr ;--- Client_BX = file handle ;--- Client_DS = buffer segment ReadFile proc mov word ptr [ebp].Client_Reg_Struc.Client_EDX, ax shr eax, 16 mov word ptr [ebp].Client_Reg_Struc.Client_ECX, ax mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 4200h call Begin_Nest_Exec mov eax, 21h call Exec_Int test [ebp].Client_Reg_Struc.Client_EFlags,1 .if (ZERO?) mov word ptr [ebp].Client_Reg_Struc.Client_ECX, si mov word ptr [ebp].Client_Reg_Struc.Client_EDX, bx mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 3Fh mov eax, 21h call Exec_Int test [ebp].Client_Reg_Struc.Client_EFlags,1 .if (ZERO?) movzx ecx, si push esi movzx esi, word ptr [ebp].Client_Reg_Struc.Client_DS shl esi, 4 movzx ebx, bx add esi, ebx shr ecx, 2 push edi rep movsd pop edi pop esi .endif .endif call End_Nest_Exec mov ah,byte ptr [ebp].Client_Reg_Struc.Client_EFlags sahf ret align 4 ReadFile endp ;--- walk relocations for PE binary loaded at linear address EBX ;--- inp: EBX = module handle, ESI=IMAGE_NT_HEADERS DoFixups proc @dprintf ?INITDBG, <"DoFixups enter",10> pushad mov edx,ebx mov ebx,[esi].IMAGE_NT_HEADERS.OptionalHeader.ImageBase mov ebp, [esi].IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage mov ecx, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof IMAGE_DATA_DIRECTORY].Size_ jecxz done mov esi, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress add esi,edx nextblock: mov edi, [esi].IMAGE_BASE_RELOCATION.VirtualAddress cmp edi, ebp jnc done ;invalid relocations found!!! push ecx push esi push edx push ebx add edi, edx sub edx, ebx mov ecx, [esi].IMAGE_BASE_RELOCATION.SizeOfBlock add ecx, esi add esi, sizeof IMAGE_BASE_RELOCATION xor eax, eax .while (esi < ecx) lods word ptr [esi] mov bl,ah and ah,0Fh shr bl,4 .if (bl == IMAGE_REL_BASED_HIGHLOW) add [edi+eax],edx .endif .endw pop ebx pop edx pop esi pop ecx mov eax,[esi].IMAGE_BASE_RELOCATION.SizeOfBlock add esi, eax sub ecx, eax ja nextblock done: popad @dprintf ?INITDBG, <"DoFixups exit",10> clc ret align 4 DoFixups endp ;--- free all objects marked as discardable ;--- inp: ESI -> IMAGE_NT_HEADERS ;--- inp: EBX = module base FreeDiscardableSections proc @dprintf ?INITDBG, <"FreeDiscardableSections enter",10> pushad movzx ecx,[esi].IMAGE_NT_HEADERS.FileHeader.NumberOfSections lea edi,[esi+size IMAGE_NT_HEADERS] jecxz @@done @@nextitem: test [edi].IMAGE_SECTION_HEADER.Characteristics, IMAGE_SCN_MEM_DISCARDABLE jz @@skipitem mov eax, [edi].IMAGE_SECTION_HEADER.VirtualAddress mov edx, [edi].IMAGE_SECTION_HEADER.Misc.VirtualSize add edx, 1000h-1 shr edx, 12 jz @@skipitem .if (eax) push ecx add eax, ebx shr eax, 12 push 0 push edx push eax call _PageDecommit add esp,3*4 pop ecx .endif @@skipitem: add edi, size IMAGE_SECTION_HEADER loop @@nextitem @@done: popad @dprintf ?INITDBG, <"FreeDiscardableSections exit",10> clc ret align 4 FreeDiscardableSections endp ;--- get address of 4k DOS buffer (during load only) ;--- ecx=JLCOMM ptr GetDOSBuffer proc public movzx eax,[ecx].VMMS.pBuffer movzx edx,word ptr [ebp].Client_Reg_Struc.Client_DS shl edx,4 add eax,edx ret GetDOSBuffer endp if ?LOG include vioout.inc include dprintf.inc endif end start ================================================ FILE: Tools/JLOAD/JLOAD32.INC ================================================ ;--- macros CStr macro text:vararg local sym .const ifidni ,<""> sym db 0 else sym db text,0 endif .code exitm endm ifndef ?KD ?KD equ 0 endif ;--- constants ?PAGEMAP equ 3FEh ;page table for mapping page tables (it's a PDE index) ;--- structs ;--- publics InitVMM proto FindDevice proto VMM_Add_DDB proto VMM_Remove_DDB proto _PageReserve proto _PageCommit proto _PageDecommit proto _PageFree proto Get_Cur_VM_Handle proto externdef Begin_Nest_Exec:dword externdef Exec_Int:dword externdef End_Nest_Exec:dword externdef VDS_Call_Table:dword externdef ddb_0004:VxD_Desc_Block externdef dfOldint20:fword externdef g_dwIDT:dword if ?KD externdef bKDdetected:byte endif ================================================ FILE: Tools/JLOAD/Linux.mak ================================================ # makefile that creates JLOAD.EXE # tools used: #------------------------ # Assembler: jwasm # OMF linker: jwlink # COFF linker: jwlink # the 32-bit part of jload must be linked with base 0xF8400000; # # The source files contain include files references in lower case, # but the name of the files are in upper case. So either store # the project on NTFS/FAT or create appropriate symlinks! ifndef DEBUG DEBUG = 0 endif ifeq (1,$(DEBUG)) OUTD = DEBUG AOPTD=-Sg -D_DEBUG -D?PEDBG=1 -D?RMDBG=1 -D?INITDBG=1 -D?JLMDBG=1 #AOPTD=-Sg -D_DEBUG -D?V86HOOKDBG=1 else OUTD = RELEASE AOPTD= endif NAME = JLOAD NAME32= JLOAD32 VXD1 = VMM VXD4 = VDMAD ASM = jwasm -c -nologo $(AOPTD) -I../../Include -DOUTD=$(OUTD) LINK32= INC32=JLOAD.INC JLOAD32.INC DEBUG.INC ../../Include/JSYSTEM.INC ../../Include/JLM.INC INC16=JLOAD.INC DEBUG.INC ../../Include/JSYSTEM.INC ../../Include/JLM.INC ALL: $(OUTD) $(OUTD)/$(NAME).EXE $(OUTD): @mkdir -p $(OUTD) $(OUTD)/$(NAME).EXE: $(OUTD)/$(NAME).obj Linux.mak @jwlink format dos file $(OUTD)/$(NAME).obj name $@ op q,m=$(OUTD)/$(NAME) $(OUTD)/$(NAME).obj: $(NAME).ASM $(OUTD)/$(NAME32).bin $(INC16) @$(ASM) -omf -D__UNIX__ -Fl$(OUTD)/ -Fo$@ $(NAME).ASM $(OUTD)/$(NAME32).bin: $(OUTD)/$(NAME32).obj $(OUTD)/$(VXD1).obj $(OUTD)/$(VXD4).obj @jwlink format raw bin f { $(OUTD)/$(NAME32).obj $(OUTD)/$(VXD1).obj $(OUTD)/$(VXD4).obj } name $@ disable 1014 op q,map=$(OUTD)/$(NAME32),offset=0xF8400000,start=_start $(OUTD)/$(NAME32).obj: $(NAME32).ASM $(INC32) Linux.mak @$(ASM) -coff -Fl$(OUTD)/ -Fo$@ $(NAME32).ASM $(OUTD)/$(VXD1).obj: $(VXD1).ASM $(INC32) Linux.mak @$(ASM) -coff -Fl$(OUTD)/ -Fo$@ $(VXD1).ASM $(OUTD)/$(VXD4).obj: $(VXD4).ASM $(INC32) Linux.mak @$(ASM) -coff -Fl$(OUTD)/ -Fo$@ $(VXD4).ASM clean: @rm $(OUTD)/*.EXE @rm $(OUTD)/*.bin @rm $(OUTD)/*.obj @rm $(OUTD)/*.lst @rm $(OUTD)/*.map ================================================ FILE: Tools/JLOAD/MAKEFILE ================================================ # nmake makefile that creates JLOAD.EXE # tools used: # default alternate #---------------------------------------------- # Assembler: jwasm MS masm (+Bin2Inc) # OMF linker: jwlink MS link16 # COFF linker: jwlink - # the 32-bit part of jload must be linked with base 0xF8400000; # this isn't possible with (all?) MS link versions! # bin2inc translates binary files to assembly include files. # It's source is supplied with JWasm. !ifndef DEBUG DEBUG = 0 !endif !if $(DEBUG) OUTDIR = DEBUG #AOPTD=-D_DEBUG -D?PEDBG=1 -D?RMDBG=1 -D?INITDBG=1 -D?JLMDBG=1 AOPTD=-Sg -D_DEBUG -D?INITDBG=1 !else OUTDIR = RELEASE AOPTD= !endif AOPT=-c -nologo $(AOPTD) -I..\..\Include -DOUTD=$(OUTDIR) NAME = JLOAD NAME32= JLOAD32 VXD1 = VMM VXD4 = VDMAD !ifndef MASM MASM=0 !endif !if $(MASM) ASM = ml.exe NAMEBIN32=$(OUTDIR)\$(NAME32).inc !else ASM = jwasm.exe NAMEBIN32=$(OUTDIR)\$(NAME32).bin !endif INC32=jload.inc jload32.inc debug.inc ..\..\Include\jsystem.inc ..\..\Include\jlm.inc INC16=jload.inc debug.inc ..\..\Include\jsystem.inc ..\..\Include\jlm.inc ALL: $(OUTDIR) $(OUTDIR)\$(NAME).EXE $(OUTDIR): @mkdir $(OUTDIR) $(OUTDIR)\$(NAME).EXE: $(OUTDIR)\$(NAME).obj MAKEFILE # @link16.exe /MAP:FULL/NOE/NON/ONERROR:NOEXE $*.OBJ, $*.EXE, $*.MAP; @jwlink.exe format dos file $*.obj name $*.EXE op q,m=$* $(OUTDIR)\$(NAME).obj: $(NAME).asm $(NAMEBIN32) $(INC16) @$(ASM) $(AOPT) -Fl$(OUTDIR)\ -Fo=$* $(NAME).asm $(NAMEBIN32): $(OUTDIR)\$(NAME32).obj $(OUTDIR)\$(VXD1).obj $(OUTDIR)\$(VXD4).obj @jwlink.exe format raw bin f { $*.obj $(OUTDIR)\$(VXD1).obj $(OUTDIR)\$(VXD4).obj } name $*.bin disable 1014 op q,map=$*,offset=0xF8400000,start=_start !if $(MASM) @bin2inc.exe -q $*.bin $*.inc !endif $(OUTDIR)\$(NAME32).obj: $(NAME32).asm $(INC32) MAKEFILE @$(ASM) -coff $(AOPT) -Fl$(OUTDIR)\ -Fo=$* $(NAME32).asm $(OUTDIR)\$(VXD1).obj: $(VXD1).asm $(INC32) MAKEFILE @$(ASM) -coff $(AOPT) -Fl$(OUTDIR)\ -Fo=$* $(VXD1).asm $(OUTDIR)\$(VXD4).obj: $(VXD4).asm $(INC32) MAKEFILE @$(ASM) -coff $(AOPT) -Fl$(OUTDIR)\ -Fo=$* $(VXD4).asm clean: @del $(OUTDIR)\*.EXE @if exist $(OUTDIR)\*.inc del $(OUTDIR)\*.inc @del $(OUTDIR)\*.bin @del $(OUTDIR)\*.obj @del $(OUTDIR)\*.lst @del $(OUTDIR)\*.map ================================================ FILE: Tools/JLOAD/VDMAD.ASM ================================================ ;--- VDMAD support ;--- best viewed with TAB size 4 .386 .model flat option casemap:none option proc:private include vds.inc include jlm.inc include jload32.inc include debug.inc ;--- structure of the VDS call table in Jemm VDSCALL struct dd ? ;EBP -> client (not used here) pLock dd ? ;EDI -> DDS, DX (not used here) pUnlock dd ? ;EDI -> DDS, DX, modifies EBX! pScLock dd ? ;EDI -> EDDS, DX, modifies EBX, ESI! pScUnlock dd ? ;EDI -> EDDS, DX (does nothing) pReqBuffer dd ? ;EDI -> DDS, DX, modifies EBX! pRelBuffer dd ? ;EDI -> DDS, DX, modifies EBX! dd ? ;copy in EDI -> DDS, DX, EBP -> client dd ? ;copy out EDI -> DDS, DX, EBP -> client dd ? ;disable translation dd ? ;enable translation pCopyInBuf dd ? ;copy in EDI -> DDS, DX, ECX, modifies EBX! pCopyOutBuf dd ? ;copy out EDI -> DDS, DX, ECX, modifies EBX! VDSCALL ends .data ddb_0004 VxD_Desc_Block < 0,1,4,1,0,0," ",0,0,0,0,0,0,0,offset vdmad_services,?NUMSERV0004> ;--- VDMAD service table vdmad_services label dword dd Get_VDMAD_Version dd Lock_DMA_Region dd Unlock_DMA_Region dd Scatter_Lock dd Scatter_Unlock dd Request_Buffer dd Release_Buffer dd Copy_To_Buffer ;new v5.69 dd Copy_From_Buffer ;new v5.69 ?NUMSERV0004 equ ($ - vdmad_services)/4 .code ;--- VDMA device functions Get_VDMAD_Version proc mov eax,100h clc ret align 4 Get_VDMAD_Version endp ;--- esi = start region (linear address) ;--- ecx = size region (bytes) ;--- dl = flags (bit 0: check for 64 kB crossing, bit 1: check for 128 kB) ;--- returns phys address in EDX if ok ;--- returns size in ECX on error, in AL error code ;--- Jemm is unable to handle addresses > 110000h ;--- therefore do it here. Lock_DMA_Region proc uses edi ebx @dprintf ?DMADBG, <"Lock_DMA_Region, addr=%X, size=%X",10>, esi, ecx push esi lea ebx, [esi+ecx-1] ;let EBX point to the last byte mov eax, esi shr eax, 12 shr ebx, 12 sub ebx, eax jc @@error2 shr esi, 12 lea esi, [esi*4 + (?PAGEMAP shl 22)] mov ecx, ebx lodsd and ax, 0F000h mov bl, dl mov edx, eax jecxz @@done mov edi, edx ; save first phys page in EDX .while ecx lodsd and eax, eax jz @@error and ax, 0F000h add edi,1000h cmp eax, edi jnz @@error ; not contiguous dec ecx .endw test bl, 1 ; 64 kB border crossing test? jz @@done mov eax, edx shr eax, 16 shr edi, 16 cmp eax, edi jnz @@error2 @@done: pop esi mov ax, si and ax, 0FFFh or dx, ax @dprintf ?DMADBG, <"Lock_DMA_Region, returns edx=%X",10>, edx ret @@error: pop esi mov ax, si and ax, 0FFFh or dx, ax sub ebx, ecx mov ecx, ebx shl ecx, 12 stc ret @@error2: pop esi xor ecx, ecx stc ret align 4 Lock_DMA_Region endp ;--- esi = start region ;--- ecx = size region ;--- dl = flags Unlock_DMA_Region proc uses esi edi ebx sub esp, size DDS mov edi, esp mov [edi].DDS.dwSize, ecx mov [edi].DDS.dwOfs, esi mov dword ptr [edi].DDS.wSeg, 0 ;clear wSeg and wID mov dh,0 shr dl,4 mov eax,[VDS_Call_Table] call [eax].VDSCALL.pUnlock ;call vds_unlock lea esp,[esp+size DDS] ret align 4 Unlock_DMA_Region endp ;--- edi = DDS ;--- al = flags Scatter_Lock proc uses esi ebx xor edx,edx test al,1 jz @F or dl,VDSF_PTE @@: mov eax,[VDS_Call_Table] call [eax].VDSCALL.pScLock ret align 4 Scatter_Lock endp ;--- edi = DDS ;--- al = flags Scatter_Unlock proc xor edx,edx mov eax,[VDS_Call_Table] call [eax].VDSCALL.pScUnlock ;this is a dummy ret align 4 Scatter_Unlock endp ;--- esi = region ;--- ecx = size ;--- out: ebx=id, edx=phys. addr Request_Buffer proc uses esi edi sub esp, size DDS mov edi, esp mov [edi].DDS.dwSize, ecx mov [edi].DDS.dwOfs, esi xor edx,edx mov dword ptr [edi].DDS.wSeg, edx mov eax,[VDS_Call_Table] call [eax].VDSCALL.pReqBuffer jc @F movzx ebx,[edi].DDS.wID mov edx,[edi].DDS.dwPhys @@: lea esp,[esp+size DDS] ret align 4 Request_Buffer endp ;--- ebx = id Release_Buffer proc uses ebx edi sub esp, size DDS mov edi, esp mov [edi].DDS.wID, bx xor edx,edx ;no NOT copy out of DMA buffer! mov eax,[VDS_Call_Table] call [eax].VDSCALL.pRelBuffer lea esp,[esp+size DDS] ret align 4 Release_Buffer endp ;--- ebx = id ;--- esi = offset src ;--- edi = buffer offset ;--- ecx = buffer size Copy_To_Buffer proc uses ebx esi edi sub esp, size DDS mov [esp].DDS.dwSize, ecx mov ecx, edi mov edi, esp mov [edi].DDS.dwOfs, esi xor edx,edx mov [edi].DDS.wSeg, dx mov [edi].DDS.wID, bx mov eax,[VDS_Call_Table] call [eax].VDSCALL.pCopyInBuf lea esp,[esp+size DDS] ret align 4 Copy_To_Buffer endp ;--- ebx = id ;--- esi = offset src ;--- edi = buffer offset ;--- ecx = buffer size Copy_From_Buffer proc uses ebx esi edi sub esp, size DDS mov [esp].DDS.dwSize, ecx mov ecx, edi mov edi, esp mov [edi].DDS.dwOfs, esi xor edx,edx mov [edi].DDS.wSeg, dx mov [edi].DDS.wID, bx mov eax,[VDS_Call_Table] call [eax].VDSCALL.pCopyOutBuf lea esp,[esp+size DDS] ret align 4 Copy_From_Buffer endp end ================================================ FILE: Tools/JLOAD/VIOOUT.INC ================================================ ;--- low-level video access ;--- no assumption about ds,es. ;--- ss is supposed to be flat. VPUTCHR PROC push ds pushad push ss pop ds if ?KD test bKDdetected,1 jz @F mov dl, al xor ax, ax int 41h jmp exit @@: endif if ?USEMONO mov edi,0B0000h mov ebx,7 else MOV EDI,0B8000h CMP BYTE ptr DS:[463h],0B4h JNZ @@IS_COLOR XOR DI,DI @@IS_COLOR: movzx EBX, WORD PTR DS:[44Eh] ADD EDI, EBX MOVZX EBX, BYTE PTR DS:[462h] endif mov esi, edi MOVZX ECX, BYTE PTR DS:[EBX*2+450h+1] ;ROW if ?USEMONO MOV EAX, 80 else MOVZX EAX, WORD PTR DS:[44Ah] endif MUL ECX MOVZX EDX, BYTE PTR DS:[EBX*2+450h] ;COL ADD EAX, EDX MOV DH,CL LEA EDI, [EDI+EAX*2] MOV AL, [ESP+1Ch] CMP AL, 13 JZ exit CMP AL, 10 JZ @@LF MOV [EDI], AL MOV byte ptr [EDI+1], 07 INC DL if ?USEMONO cmp dl,80 else CMP DL, BYTE PTR DS:[44Ah] endif JB @@OLDLINE @@LF: MOV DL, 00 INC DH if ?USEMONO CMP DH, 24 else CMP DH, BYTE PTR DS:[484h] endif JBE @@OLDLINE DEC DH CALL SCROLL_SCREEN @@OLDLINE: MOV DS:[EBX*2+450h],DX exit: POPAD pop ds RET align 4 ;--- scroll screen up 1 line ;--- esi -> start screen SCROLL_SCREEN: push es push ds pop es CLD mov edi,esi if ?USEMONO mov eax,80 else movzx eax,word ptr ds:[44Ah] endif push eax lea esi, [esi+2*eax] if ?USEMONO mov CL, 24 else MOV CL, DS:[484h] endif mul cl mov ecx,eax rep movsw pop ecx mov ax,0720h rep stosw pop es retn align 4 VPUTCHR ENDP ================================================ FILE: Tools/JLOAD/VMM.ASM ================================================ ;--- VMM functions ;--- best viewed with TAB size 4 .386 .model flat option casemap:none option proc:private include x86.inc include jsystem.inc include jlm.inc include jload.inc include jload32.inc include debug.inc ?MAXBP equ 32 ?SYSTABSCRATCHIDX equ 3feh ;index PTE for scratch address space ?APIINT equ 2Fh CBDYN struc dwCallback dd ? dwRefData dd ? CBDYN ends ;--- v86-hook HOOKV86 struc dwHook dd ? HOOKV86 ends .data dfOldint20 df 0 ;saved Jemm's int 20h vector protected mode align 4 VDS_Call_Table dd 0 ;Jemm's VDS call table VCPI_Call_Table dd 0 ;Jemm's VCPI call table IO_Trap_Table dd 0 ;Jemm's IO trap table bprm dd 0 ;real-mode address of first bp dwTSSbase dd 0 ;start TSS of monitor lpV86Hooks dd 0 ;page for hooks (v86 int chain) ;lpV86_Monitor dd 0 ;address V86_Monitor() function in Jemm ;lpStackCurr dd 0 ;address of dwStackCurr variable in Jemm ppV86Hooks dd 0 ;v5.85: address of pV86Hooks variable in Jemm retaddr dd 0 ;v5.85: return address to V86_Monitor lpV86Faults dd 0 ;v5.86: original address stored in vmm_service_table ScratchPTE dd 0 ;PTE used to simplify page memory management oldiohandler dd 0 ;saved Jemm's IO handler oldbp1 dd 0 ;saved Jemm's static callback IO_Handler_Region dd 0 ;linear address IO handler array (256 kB) IO_Handler_Pages dq 0 ;64 pages reserved for IO handlers Int2fHooked dd 0 ifndef _DEBUG prevint2f dd 0 else prevint2f dd offset finalint2f trueprevint2f dd 0 endif if ?KD bKDdetected db 0 align 4 endif ;--- generic "control block" returned by Get_Cur_VM_Handle; ;--- the only important part is linear address of the client register struct; vmcb cb_s <0,0,?TOS - sizeof Client_Reg_Struc,1> ;--- start of device list known to the int 20h loader ddb_list label dword d0001 VxD_Desc_Block GetDOSBuffer proto ;defined in JLOAD32 align 4 ;--- VMM service table vmm_services label dword dd Get_VMM_Version ;0=get version dd Get_Cur_VM_Handle ;1 dd Allocate_V86_Call_Back dd Crash_Cur_VM dd Hook_V86_Int_Chain dd Get_V86_Int_Vector dd Set_V86_Int_Vector dd Get_PM_Int_Vector dd Set_PM_Int_Vector Simulate_Int dd 0 Simulate_Iret dd 0 Simulate_Far_Call dd 0 dd Simulate_Far_Jmp Simulate_Far_Ret dd 0 dd Simulate_Far_Ret_N dd Build_Int_Stack_Frame dd Simulate_Push dd Simulate_Pop dd _PageFree dd _PhysIntoV86 dd _LinMapIntoV86 dd Hook_V86_Fault dd Hook_PM_Fault Begin_Nest_Exec dd 0 Exec_Int dd 0 Resume_Exec dd 0 End_Nest_Exec dd 0 dd Save_Client_State dd Restore_Client_State Simulate_IO dd 0 dd Install_Mult_IO_Handlers dd Install_IO_Handler dd VMM_Add_DDB dd VMM_Remove_DDB dd Remove_IO_Handler dd Remove_Mult_IO_Handlers dd Unhook_V86_Int_Chain dd Unhook_V86_Fault dd Unhook_PM_Fault dd _PageReserve dd _PageCommit dd _PageDecommit dd _PageCommitPhys dd Free_V86_Call_Back Yield dd 0 MoveMemory dd 0 dd _Allocate_GDT_Selector ; v5.83 dd _Free_GDT_Selector ; v5.83 dd Get_DDB ; v5.83 dd V86ToPM ; v5.85 dd GetDOSBuffer ; v5.86 ?NUMSERV0001 equ ($ - vmm_services)/4 ;--- VMM callback table v86_cb CBDYN ?MAXBP dup (<>) v86faulthooks dd 32 dup (0) ;v5.86 .code ;--- InitVMM ;--- ECX -> VMMS structure InitVMM proc public @dprintf ?INITDBG,<"Init enter",10> ;--- save the vectors received from Jemm; ;--- e08_ServiceTable is a VMM_SERV_TABLE struct mov esi, [ecx].VMMS.emx08.e08_ServiceTable lodsd mov Simulate_Int, eax lodsd mov Simulate_Iret, eax lodsd mov Simulate_Far_Call, eax lodsd mov Simulate_Far_Ret, eax lodsd mov Begin_Nest_Exec, eax lodsd mov Exec_Int, eax lodsd mov Resume_Exec, eax lodsd mov End_Nest_Exec, eax lodsd mov Simulate_IO, eax lodsd mov Yield, eax lodsd mov VDS_Call_Table, eax lodsd mov VCPI_Call_Table, eax lodsd mov IO_Trap_Table, eax ;--- v5.87: next 2 fields obsolete since v5.85; now removed ; lodsd ; mov lpV86_Monitor, eax ; lodsd ; mov lpStackCurr, eax lodsd mov MoveMemory, eax ;new for v5.69 lodsd mov ppV86Hooks, eax ;new for v5.85 lodsd mov lpV86Faults, eax mov [esi-4],offset V86Faults ;new for v5.86 mov [esi],offset call_control_procs ;new for v5.86 if ?KD mov ax, 4Fh int 41h cmp ax, 0F386h jnz @F or [bKDdetected], 1 @@: endif ;--- init the mapping page for memory management call InitMapPage jc @@exit ;--- modify one of Jemm's static BPs ;--- to become the generic v86 callback BP. mov esi, [ecx].VMMS.emx08.e08_pCallBack mov eax, offset V86_Callback_Handler xchg eax, [esi] mov [oldbp1], eax mov eax, [ecx].VMMS.emx08.e08_CallBackRM mov [bprm],eax ;--- since jemm v5.70, the TSS is no longer at a fix address; movzx edx, [ecx].VMMS.emx08.e08_TR add edx, dword ptr [ecx].VMMS.emx08.e08_GDTR+2 mov ah,[edx].DESCRIPTOR.bA2431 mov al,[edx].DESCRIPTOR.bA1623 shl eax,16 mov ax,[edx].DESCRIPTOR.wA0015 mov [dwTSSbase],eax mov eax, dword ptr [ecx].VMMS.emx08.e08_IDTR+2 mov [g_dwIDT], eax ;--- install an int 20h handler in IDT; ;--- it will install the jlm dynamic linking mechanism (Win3x/9x style) mov esi, eax add esi, 20h*8 mov ax, [esi].GATE.wOfsHi shl eax, 16 mov ax, [esi].GATE.wOfsLo mov dword ptr [dfOldint20+0], eax mov ax, [esi].GATE.wSeg mov word ptr [dfOldint20+4], ax mov eax, offset Int20_Handler mov [esi].GATE.wOfsLo, ax shr eax, 16 mov [esi].GATE.wOfsHi, ax mov eax, cs mov [esi].GATE.wSeg, ax @@exit: @dprintf ?INITDBG, <"Init exit",10> ret align 4 InitVMM Endp ;--- int 20h dynamic linking Int20_Handler proc test byte ptr [esp].IRETDV86._Efl+2,2 ;called from v86-mode? jnz isv86 pushad mov esi,[esp+8*4].IRETDV86._Eip mov eax,[esi] ;it is "INT 20h" followed by a dword sub esi,2 mov [esp+8*4].IRETDV86._Eip, esi movzx ecx, ax shr eax,16 mov edx, offset ddb_list @@nextdevice: cmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number ;is device known? jz @@device_found mov edx, [edx].VxD_Desc_Block.DDB_Next and edx, edx jnz @@nextdevice @@error: ; popad call Crash_Cur_VM ;AX:CX contain VM:Function to link to @@device_found: mov bh,ch mov bl,0FFh and bh,80h and ch,7Fh cmp ecx, [edx].VxD_Desc_Block.DDB_Service_Table_Size jnc @@error mov eax, [edx].VxD_Desc_Block.DDB_Service_Table_Ptr shl ecx, 2 shr bh,3 add bh,15h add eax, ecx mov word ptr [esi],bx ;opcode for "call/jmp dword ptr []" mov [esi+2],eax popad iretd isv86: jmp cs:[dfOldint20] align 4 Int20_Handler endp ;--- generic callback; ;--- has replaced Jemm's static callback on init V86_Callback_Handler proc mov eax,[ebp].Client_Reg_Struc.Client_CS sub ax,word ptr [bprm+2] jz isdefbp inc ax neg ax movzx eax,ax shl eax,3 mov edx, [eax+offset v86_cb].CBDYN.dwRefData jmp [eax+offset v86_cb].CBDYN.dwCallback isdefbp: jmp [oldbp1] align 4 V86_Callback_Handler endp Get_VMM_Version proc mov eax, ?VERSIONLOW shl 16 + ?VERSIONHIGH clc ret align 4 Get_VMM_Version endp Get_Cur_VM_Handle proc public mov ebx, offset vmcb clc ret align 4 Get_Cur_VM_Handle endp ;--- deliberately cause a GPF. This will result in an ;--- invalid opcode exception reported to v86-mode Crash_Cur_VM proc push cs pop ss align 4 Crash_Cur_VM endp ;--- get a v86 callback in EAX ;--- inp: ESI=callback, EDX=dwRefData ;--- C on error Allocate_V86_Call_Back proc push edi mov edi, offset v86_cb mov ecx, ?MAXBP xor eax, eax @@nextitem: cmp eax, [edi].CBDYN.dwCallback jz @@freecb add edi, size CBDYN loop @@nextitem pop edi stc ret @@freecb: mov [edi].CBDYN.dwCallback, esi mov [edi].CBDYN.dwRefData, edx sub edi, offset v86_cb shr edi, 3 inc edi mov ax, word ptr [bprm+2] sub ax, di shl eax, 16 mov ax,di shl ax,4 add ax, word ptr [bprm+0] pop edi ret align 4 Allocate_V86_Call_Back endp ;--- free a v86 callback in EAX Free_V86_Call_Back proc movzx edx, ax shr eax, 16 inc eax movzx ecx, word ptr [bprm+2] sub ecx, eax jc @@fail cmp ecx, ?MAXBP jae @@fail push ecx add ecx, eax shl ecx, 4 dec eax shl eax, 4 add eax, edx movzx edx, word ptr [bprm+0] add ecx, edx cmp eax, ecx pop ecx jnz @@fail mov [v86_cb+ecx*8].CBDYN.dwCallback,0 ret @@fail: stc ret align 4 Free_V86_Call_Back endp ;--- eax = word/dword to push Simulate_Push proc MOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS sub dx,2 SHL ecx, 4 add ecx, edx MOV word ptr [EBP].Client_Reg_Struc.Client_ESP,dx mov [ecx],ax ret align 4 Simulate_Push endp ;--- out: eax = word which has been poped Simulate_Pop proc MOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS SHL ecx, 4 add ecx, edx movzx eax,word ptr [ecx] add dx,2 MOV word ptr [EBP].Client_Reg_Struc.Client_ESP,dx ret align 4 Simulate_Pop endp ;--- cx == segment ;--- edx == offset Simulate_Far_Jmp proc mov [EBP].Client_Reg_Struc.Client_EIP, edx mov word ptr [EBP].Client_Reg_Struc.Client_CS,cx ret align 4 Simulate_Far_Jmp endp ;--- eax == number of bytes to pop Simulate_Far_Ret_N proc push eax call [Simulate_Far_Ret] pop eax add word ptr [EBP].Client_Reg_Struc.Client_ESP, ax ret align 4 Simulate_Far_Ret_N endp ;--- cx=segment, edx=offset to call Build_Int_Stack_Frame proc push ecx push edx mov eax, [EBP].Client_Reg_Struc.Client_EFlags call Simulate_Push pop edx pop ecx call [Simulate_Far_Call] and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1,not (1+2) ret align 4 Build_Int_Stack_Frame endp ;--- in: EDI = save buffer ;--- EBP = client_reg_struc ;--- no return value Save_Client_State proc push edi push esi mov esi, ebp mov ecx, (size Client_Reg_Struc) / 4 rep movsd pop esi pop edi ret align 4 Save_Client_State endp ;--- in: ESI = save buffer ;--- no return value Restore_Client_State proc push edi push esi mov edi, ebp mov ecx, (size Client_Reg_Struc) / 4 rep movsd pop esi pop edi ret align 4 Restore_Client_State endp ;--- Allocate_GDT_Selector( high32 descriptor, low32 descriptor, flags ) ;--- returns selector in eax, GDT selector in loword edx, GDT size in hiword edx ;--- high32 desc: [esp+4][8] ;--- low32 desc: [esp+4][4] ;--- flags: [esp+4][0] _Allocate_GDT_Selector proc mov ecx, 8 sub esp, ecx sgdt [esp] movzx edx, word ptr [esp] mov eax, [esp+2] inc edx add esp, ecx nextdesc: cmp ecx, edx jae notfound cmp byte ptr [eax+ecx+5], 0 jz found add ecx, 8 jmp nextdesc found: push edx mov edx, [esp+8][1*4] mov [eax+ecx+0], edx mov edx, [esp+8][2*4] mov [eax+ecx+4], edx pop edx mov eax, ecx ret notfound: xor eax, eax xor edx, edx ret _Allocate_GDT_Selector endp ;--- Free_GDT_Selector( wSelector, flags ) ;--- todo: limit checks _Free_GDT_Selector proc mov ecx, [esp+4] sub esp, 8 sgdt [esp] mov eax, [esp+2] add esp, 8 mov byte ptr [eax+ecx+5],0 ret _Free_GDT_Selector endp ;--- Get_DDB ;--- find a device ;--- eax=device ID, edi=device name (or NULL) ;--- out: device DDB in ECX! ;--- modifies ECX only. Get_DDB proc uses eax edx call FindDevice mov ecx, eax jnc @F xor ecx, ecx @@: ret Get_DDB endp ;------------------------- ;--- the IO-port trapping is implemented very simple ;--- there is an array of 65536 vectors in a 256 kB memory region ;--- for all possible IO ports. On init the memory is uncommitted, ;--- but if someone installs a port trap in a 400h set which has ;--- no other trap yet, a new page will be allocated. ;--- called with port in EDX, value in EAX, type in CX New_IO_Handler proc movzx edx,dx mov esi, [IO_Handler_Region] cmp dword ptr [esi+edx*4],0 jz @F jmp dword ptr [esi+edx*4] @@: ;--- v5.84: jump to the old trap handler - it will handle ;--- ISA DMA and A20 ; VMMJmp Simulate_IO jmp [oldiohandler] align 4 New_IO_Handler endp ;--- scan Jemm's table of IOTRAPENTRYs ;--- the table defines ranges, but there may be "holes" ( untrapped ports ) ;--- so first check if the port is actually really trapped. ;--- v5.84: Jemm's table is no longer imported. ;--- instead, if port isn't trapped, the old (=Jemm's) handler will be run. ;--- structure of IO_Trap_Table: ;--- dd ? ; the default trap handler ;--- dd ? ; number of IOTRAPENTRIES in the following table ;--- IOTRAPENTRIES[] ImportJemmIOTraps proc uses esi @dprintf ?IODBG, <"ImportJemmIOTraps",10> mov esi, [IO_Trap_Table] if 1 mov eax, offset New_IO_Handler xchg eax, [esi] mov [oldiohandler], eax ret else lodsd mov [esi-4], offset New_IO_Handler mov [oldiohandler],eax lodsd mov ecx, eax jecxz @@done @@nextrange: lodsb ; get bStart field movzx edx, al lodsb ; get bEnd field movzx eax, al sub eax, edx inc eax push ecx mov ecx, eax lodsd ; get dwProc field push esi mov esi, eax @@nextport: push ecx push edx mov eax, [dwTSSbase] movzx ecx, [eax].TSSSEG.tsOfs add eax, ecx bt [eax], edx ; port trapped? jnc @F call CommitTrapPage jc @F mov eax, [IO_Handler_Region] mov [edx*4+eax], esi @@: pop edx pop ecx inc edx ; next port loop @@nextport pop esi pop ecx loop @@nextrange @@done: ret endif align 4 ImportJemmIOTraps endp ;--- dx=port CommitTrapPage proc movzx edx, dx mov eax, edx shr eax, 10 ;0-FFFF -> 0-3F bt dword ptr [IO_Handler_Pages], eax jc @@iscommitted push edx push PC_FIXED or PC_WRITEABLE push 0 push PD_ZEROINIT push 1 mov ecx, [IO_Handler_Region] shr ecx, 12 add eax, ecx push eax call _PageCommit add esp,5*4 pop edx and eax, eax jz @@error mov eax, edx shr eax, 10 bts dword ptr [IO_Handler_Pages], eax @@iscommitted: clc ret @@error: stc ret CommitTrapPage endp ;--- ESI=callback ;--- DX=port Install_IO_Handler proc cld cmp [IO_Handler_Region],0 jnz @@isallocated @dprintf ?IODBG, <"Install_IO_Handler: reserve 256 kB region",10> push edx push 0 push 64 push PR_SYSTEM call _PageReserve ; reserve 64*4=256 kB [ 65536 ports, one dword per port ] add esp,3*4 pop edx cmp eax,-1 jz @@error mov [IO_Handler_Region], eax @dprintf ?IODBG, <"Install_IO_Handler: region=%X",10>, eax push edx call ImportJemmIOTraps pop edx @@isallocated: @dprintf ?IODBG, <"Install_IO_Handler: port=%X",10>, dx call CommitTrapPage jc @@error movzx eax, dx shl eax, 2 add eax, [IO_Handler_Region] cmp dword ptr [eax], 0 ; port already trapped? jnz @@error mov [eax], esi mov eax, [dwTSSbase] movzx ecx, [eax].TSSSEG.tsOfs add eax, ecx bts [eax],edx ; mark port as "trapped" in IOPB clc ret @@error: @dprintf ?IODBG, <"Install_IO_Handler: trap port=%X failed",10>, dx stc ret align 4 Install_IO_Handler endp ;--- when untrapping a port, check if it's a port trapped by Jemm ;--- if yes, it must not be reset in the IOPB. ;--- in: edx=port ;--- out: C if port is trapped by Jemm ;--- register edx must be preserved DefaultTrappedPort proc uses esi ebx mov esi, [IO_Trap_Table] add esi, 4 lodsd mov ecx, eax jecxz done nextrange: lodsb ; get bStart field movzx ebx, al lodsb ; get bEnd field movzx eax, al add esi, 4 ; skip proc field cmp edx, ebx jb @F cmp edx, eax jbe found @@: loop nextrange done: clc ret found: stc ret DefaultTrappedPort endp ;--- untrap port, dx=port Remove_IO_Handler proc @dprintf ?IODBG, <"Remove_IO_Handler: untrap port=%X",10>, dx xor ecx, ecx cmp [IO_Handler_Region],ecx jz @@error movzx eax, dx shr eax, 10 ;0-FFFF -> 0-3F bt dword ptr [IO_Handler_Pages], eax jnc @@error movzx eax, dx shl eax, 2 add eax, [IO_Handler_Region] cmp [eax],ecx jz @@error mov [eax], ecx call DefaultTrappedPort jc @F mov eax, [dwTSSbase] movzx ecx, [eax].TSSSEG.tsOfs add eax, ecx btc [eax],edx @@: clc ret @@error: @dprintf ?IODBG, <"Remove_IO_Handler: untrap port=%X failed",10>, dx stc ret align 4 Remove_IO_Handler endp ;--- not implemented ;--- API? Install_Mult_IO_Handlers proc stc ret align 4 Install_Mult_IO_Handlers endp ;--- not implemented ;--- API? Remove_Mult_IO_Handlers proc stc ret align 4 Remove_Mult_IO_Handlers endp ;--- interrupt hooking ;--- get vector EAX in CX:EDX Get_V86_Int_Vector proc cmp eax,256 jae @@error movzx edx,word ptr [eax*4+0] mov cx,word ptr [eax*4+2] clc ret @@error: stc ret align 4 Get_V86_Int_Vector endp ;--- set vector EAX in CX:EDX Set_V86_Int_Vector proc cmp eax,256 jae @@error mov word ptr [eax*4+0],dx mov word ptr [eax*4+2],cx clc ret @@error: stc ret align 4 Set_V86_Int_Vector endp ;--- the Get_PM/Set_PM API is supposed to set the vectors of the current VM ;--- if it's in protected-mode. So not really needed by Jemm. ;--- in: eax=int# ;--- out: cx=segment ;--- edx=offset Get_PM_Int_Vector proc stc ret align 4 Get_PM_Int_Vector endp ;--- in: eax=int# ;--- cx=segment ;--- edx=offset Set_PM_Int_Vector proc stc ret align 4 Set_PM_Int_Vector endp ;--- hook v86 interrupt chain ;--- eax = int# ;--- esi = hook proc ;--- v5.85: ;--- IDT is no longer touched ;--- instead the new ppV86Hooks variable is used to set V86Hooks ;--- this makes the thunk code generation obsolete. ;--- sizeof HOOKSTR is now 4 (256*4=1024), the following 32 bytes are used to store ;--- the VME bitmap. Hook_V86_Int_Chain proc cmp eax,100h jnc @@error ;--- the "hook" page already allocated? cmp [lpV86Hooks],0 jnz @@isallocated push eax push 0 push 1 push PR_SYSTEM call _PageReserve add esp,3*4 cmp eax, -1 jz @@error2 push eax push PC_FIXED or PC_WRITEABLE push 0 push PD_ZEROINIT push 1 shr eax, 12 push eax call _PageCommit add esp,5*4 pop ecx and eax, eax jz @@error2 mov [lpV86Hooks], ecx ;--- v5.85: set the pV86Hooks variable in Jemm32 mov eax, [ppV86Hooks] mov [eax+0], ecx mov [eax+4], offset CallV86Hooks pop eax @@isallocated: mov edx,[lpV86Hooks] cmp [edx+eax*4].HOOKV86.dwHook,0 ; 4=sizeof HOOKV86 struct jz @@newhook ;--- chain with old hook proc mov ecx,[edx+eax*4].HOOKV86.dwHook mov [edx+eax*4].HOOKV86.dwHook, esi mov eax, [esi-4] mov [eax], ecx jmp @@done @@error2: pop eax @@error: @dprintf ?V86HOOKDBG, <"Hook_V86_Int_chain: exit error",10> stc ret @@newhook: @dprintf ?V86HOOKDBG, <"Hook_V86_Int_chain: new hook, eax=%X",10>, eax mov [edx+eax*4].HOOKV86.dwHook, esi ;--- set the bit in TSS interrupt redirection bitmap for VME. ;--- store the old state in [edx+1024] (behind the HOOKSTR items). ;--- For hardware interrupts, this isn't needed, but shouldn't harm... if 0 cmp al, 8 jb @F cmp al, 10h jb @@done cmp al, 70h jb @F cmp al, 78h jb @@done @@: endif push ebx push esi if 0 mov ecx, [esi-4] mov dword ptr [ecx], 0 endif @dprintf ?V86HOOKDBG, <"Hook_V86_Int_chain: modifying Int bitmap, eax=%X",10>, eax mov ebx, [dwTSSbase] movzx ecx, [ebx].TSSSEG.tsOfs add ebx, ecx sub ebx, 32 ;=256/8 bts [ebx], eax jc @F btr [edx+1024], eax jmp bitset @@: bts [edx+1024], eax bitset: pop esi pop ebx @@done: @dprintf ?V86HOOKDBG, <"Hook_V86_Int_chain: exit ok",10> clc ret align 4 Hook_V86_Int_Chain endp ;--- eax = int# ;--- esi = hook proc Unhook_V86_Int_Chain proc @dprintf ?V86HOOKDBG, <"Unhook_V86_Int_Chain: int=%X",10>, eax cmp eax,100h jnc @@error mov ecx,[lpV86Hooks] jecxz @@error mov edx,[ecx+eax*4].HOOKV86.dwHook and edx,edx jz @@error xor ecx,ecx @@nextitem: cmp edx,esi jz @@found mov ecx,[edx-4] mov edx,[ecx] and edx,edx jnz @@nextitem @@error: stc ret @@found: mov edx,[edx-4] mov edx,[edx] and ecx,ecx jnz @@notfirst mov ecx,[lpV86Hooks] and edx, edx jz @@islast lea ecx,[ecx+eax*4] @@notfirst: mov [ecx],edx ret @@islast: mov [ecx+eax*4].HOOKV86.dwHook,edx bt [ecx+1024], eax jc @@nobitres push ebx mov ebx, [dwTSSbase] movzx edx, [ebx].TSSSEG.tsOfs add ebx, edx sub ebx, 32 btr [ebx],eax pop ebx @@nobitres: clc ret align 4 Unhook_V86_Int_Chain endp ;--- call hooks for an int ;--- must have been ensured that there are hooks! ;--- a hook proc is called with: ;--- eax = int# ;--- ebp = client ;--- ecx = hook proc ;--- out: NC if interrupt has been serviced. CallV86Hooks proc @@nexthook: push ecx push eax call ecx pop eax pop ecx jnc @@done mov edx,[ecx-4] mov ecx,[edx] and ecx, ecx jnz @@nexthook ifdef _DEBUG cmp eax, 2Fh jz @F @dprintf ?V86HOOKDBG, <"CallV86Hooks: no hook proc handled int %X",10>, eax @@: endif stc @@done: ret align 4 CallV86Hooks endp ;--- add a v86 fault hook ;--- eax=fault# ;--- esi=fault proc ;--- out: C if not installed ;--- NC if installed, then ESI=previous handler (win31 compat) Hook_V86_Fault proc @dprintf ?V86HOOKDBG, <"Hook_V86_Fault: int=%X",10>, eax cmp eax,32 cmc jb exit mov edx,[esi-4] xchg esi,[eax*4+v86faulthooks] mov [edx],esi cmp esi,0 jnz exit mov eax,[lpV86Faults] mov [edx],eax exit: ret Hook_V86_Fault endp ;--- remove a v86 fault hook ;--- eax=fault# ;--- esi=fault proc ;--- out: C if not unhooked (invalid fault in eax, esi not a hook proc, hook proc not found,...) Unhook_V86_Fault proc @dprintf ?V86HOOKDBG, <"Unhook_V86_Fault: int=%X",10>, eax cmp eax,32 cmc jb exit xor ecx, ecx mov edx, [eax*4+v86faulthooks] .while edx .if edx == esi mov edx,[esi-4] mov edx,[edx] .if !ecx mov [eax*4+v86faulthooks], edx .else mov eax,[ecx-4] mov [eax], edx .endif .break .endif mov ecx, edx mov edx, [edx-4] mov edx, [edx] .endw cmp edx,1 exit: ret Unhook_V86_Fault endp ;--- call v86 fault hookers ;--- in: ebp=client_reg_struct ;--- (ebx=vm handle in windows) ;--- eax=fault# ;--- the fault handler may either ;--- + pass the fault to the previous handler (all regs preserved then) OR ;--- + do a RET (all GPRs excepts EBP/ESP may be modified) V86Faults proc cmp [eax*4+v86faulthooks],0 jz @F jmp [eax*4+v86faulthooks] @@: jmp [lpV86Faults] V86Faults endp ;--- the "PM" functions are pretty useless, since Jemm has no support ;--- for protected-mode apps. Hook_PM_Fault proc Hook_PM_Fault endp Unhook_PM_Fault proc Unhook_PM_Fault endp stc ret align 4 ;--- _PhysIntoV86(physpg, VM, VMLinPgNum, nPages, flags) ;--- map n physical pages into V86 physpg equ VM equ VMLinPgNum equ nPages equ flags equ _PhysIntoV86 proc mov ecx, [nPages] push esi push edi mov esi, 8[physpg] mov edi, 8[VMLinPgNum] mov eax, esi mov edx, edi cmp eax, 100h jbe @@done lea edi, [edi*4 + (?PAGEMAP shl 22)] cld shl esi,12 @@nextitem: mov eax, esi or eax, PTF_PRESENT or PTF_RW or PTF_USER stosd add esi,1000h loop @@nextitem mov eax,cr3 mov cr3,eax @@done: pop edi pop esi ret align 4 _PhysIntoV86 endp ;--- _LinMapIntoV86(LinPgNum, VM, VMLinPgNum, nPages, flags) ;--- map n linear pages into V86 LinPgNum equ VM equ VMLinPgNum equ nPages equ flags equ _LinMapIntoV86 proc mov ecx, [nPages] push esi push edi mov esi, 8[LinPgNum] mov edi, 8[VMLinPgNum] mov eax, esi mov edx, edi cmp eax, 100h jbe @@done lea esi, [esi*4 + (?PAGEMAP shl 22)] lea edi, [edi*4 + (?PAGEMAP shl 22)] cld rep movsd mov eax,cr3 mov cr3,eax @@done: pop edi pop esi ret align 4 _LinMapIntoV86 endp ;--- page memory ;--- bits in PTE: ;--- 200h = begin of a memory object ;--- 400h = PTE is a mapped phys page (don't free it!) ;--- 800h = not used GetFreePages proc push edi push esi push ecx mov eax, [VCPI_Call_Table] call dword ptr [eax+3*4] ;VCPI_GetFreePages pop ecx pop esi pop edi ret align 4 GetFreePages endp AllocPage proc push edi push esi push ecx mov eax, [VCPI_Call_Table] call dword ptr [eax+4*4] pop ecx pop esi pop edi ret align 4 AllocPage endp FreePage proc push edi push esi push ecx mov eax, [VCPI_Call_Table] call dword ptr [eax+5*4] pop ecx pop esi pop edi ret align 4 FreePage endp ;--- esi=V86TOPM struct ;--- ebp=client reg struc ;--- both structures have to be copied into shared space V86ToPM proc public mov edi, ?BASE+10h mov ecx, 6 ; size of VCPI_V86toPM struct cld rep movsd mov esi, ebp mov cl, sizeof Client_Reg_Struc shr 2 rep movsd mov esi, ?BASE+10h lea edi, [esi+6*4] mov eax, [VCPI_Call_Table] jmp dword ptr [eax+12*4] ; VCPI_V86ToPM align 4 V86ToPM endp ;--- to simplify memory management, ;--- a "mapping" page table is maintained which ;--- maps all page tables in linear address space ;--- at address ?PAGEMAP << 22 (currently 3FEh << 22 = FF800000h) InitMapPage proc pushad call AllocPage cmp ah,0 jnz @@error or edx,PTF_PRESENT or PTF_RW ;set P, R/W mov ds:[?PAGEDIR+?PAGEMAP*4],edx ;set the PDE mov ds:[?SYSBASE+?PAGETABSYS+?SYSTABSCRATCHIDX*4],edx ;map the table in scratch region call AllocPage ;alloc a second page (scratch PTE) cmp ah,0 jnz @@error or edx,PTF_PRESENT or PTF_RW mov [ScratchPTE],edx mov eax, cr3 ;flush TLB mov cr3, eax mov eax, edx ;fill mapping table PDE with scratch PTE mov edi, ?SYSBASE + 3FE000h cld mov ecx, 1000h/4 push edi rep stosd pop edi mov eax,ds:[?PAGEDIR+0] ;copy PDE page table 0 mov [edi+0],eax mov eax,ds:[?PAGEDIR+(?SYSBASE shr 20)] ;copy PDE "jemm" mov [edi+(?SYSBASE shr 20)],eax mov eax,ds:[?PAGEDIR+(?SYSTEMADDR shr 20)] ;copy PDE "jload" mov [edi+(?SYSTEMADDR shr 20)],eax mov eax,ds:[?PAGEDIR+?PAGEMAP*4] ;copy PDE "pagemap" mov [edi+?PAGEMAP*4],eax ;--- now map scratch PTE into scratch region and clear it mov dword ptr ds:[?SYSBASE+?PAGETABSYS+?SYSTABSCRATCHIDX*4], edx mov eax, cr3 mov cr3, eax mov ecx, 1000h/4 xor eax, eax rep stosd mov dword ptr ds:[?SYSBASE+?PAGETABSYS+?SYSTABSCRATCHIDX*4], eax popad clc ret @@error: popad xor eax, eax stc ret align 4 InitMapPage endp ;--- _PageFree(hMem, flags) ;--- this function has no size parameter ;--- _page: linear address of block ;--- flags: 0 or PR_STATIC _page equ flags equ _PageFree proc public push esi mov esi, [_page] shr esi, 12 lea esi, [esi*4 + ?PAGEMAP shl 22] lodsd test ah,2 ;begin of a block? jz @@error @@nextitem: test al,1 ;committed page? jz @F test ah,4 ;mapped page? jnz @F mov edx, eax and dx,0F000h call FreePage @@: mov dword ptr [esi-4],0 ; cmp esi, (?PAGEMAP + 400000h) shl 22 ;end of page map? cmp esi, (?PAGEMAP shl 22 ) + 400000h ;end of page map? jz @@done lodsd and eax, eax jz @@done test ah,2 jz @@nextitem @@done: mov eax,cr3 mov cr3,eax push 1 pop eax pop esi ret @@error: pop esi xor eax,eax ret align 4 _PageFree endp ;--- _PageReserve(page, npages, flags) ;--- page = specific page or ;--- PR_PRIVATE (80000400h), PR_SHARED (80060000h), PR_SYSTEM (80080000h) ;--- flags: ??? ;--- out: linear address in EAX or -1 ;--- "private" starts at 400000h ;--- "shared" possibly at F8000000h downwards? (already used by 4MB page heap!) ;--- system at ?SYSTEMADDR (usually F8400000h) ;--- the first PTE will have the _page equ npages equ flags equ _PageReserve proc public cmp dword ptr [npages],0 jz @@error mov eax, ?SYSTEMADDR shr 10 mov ecx, 400000h/4 - (?SYSTEMADDR shr 12) cmp dword ptr [_page],PR_SYSTEM jz @F cmp dword ptr [_page],PR_PRIVATE ;currently only unspecified regions are supported jnz @@error mov eax, ?PRIVATEADDR shr 10 mov ecx, (?SYSTEMADDR - ?PRIVATEADDR) shr 12 @@: mov edx, [npages] dec edx push edi push ebx lea edi, [eax + (?PAGEMAP shl 22)] cld ;--- the mapping region at FF800000-FFBFFFFF contains no ;--- invalid addresses. A 00000000 entry means it is either an ;--- unused PTE of an partially used PT, or it is a still unused PT. ;--- so all what has to be done is scanning the region for a sequence ;--- of 00000000 dwords. @@continuescan: xor eax, eax repnz scasd jnz @@error2 cmp ecx, edx ;enough potential entries left? jc @@error2 lea ebx, [edi-4] and edx, edx ;size just one page? jz @@found push ecx mov ecx, edx repz scasd pop ecx jz @@found mov eax, edx sub eax, ecx sub ecx, eax jmp @@continuescan @@found: ;--- now backup the PDEs and PTEs with true RAM mov ecx, edx inc ecx mov edi, ebx .while (ecx) mov eax, edi sub eax, ?PAGEMAP shl 22 shr eax, 10 cmp dword ptr ds:[?PAGEDIR + EAX],0 ;does a PDE exist? jnz @F push eax call AllocPage cmp ah,0 pop eax jnz @@error or edx,PTF_PRESENT or PTF_RW or PTF_USER mov ds:[?PAGEDIR + EAX],edx and dl, not PTF_USER mov ds:[(?PAGEMAP shl 22) + (?PAGEMAP shl 12) + EAX],edx push edi push ecx mov edi, eax shl edi, 10 add edi, ?PAGEMAP shl 22 mov eax, cr3 mov cr3, eax xor eax, eax mov ecx, 1000h/4 rep stosd pop ecx pop edi @@: mov eax, PTF_RW or PTF_USER ;set USER + R/W + NP cmp edi,ebx ;first PTE? setz ah shl ah,1 ;set first "available" bit in PTE stosd dec ecx .endw mov eax, ebx sub eax, ?PAGEMAP shl 22 shl eax, 10 pop ebx pop edi ret @@error2: pop ebx pop edi @@error: or eax,-1 ret align 4 _PageReserve endp ;--- _PageCommit(page, npages, hpd, pagerdata, flags) ;--- returns eax != 0 if ok, eax == 0 on failure ;--- page: linear page number ;--- hpd: PD_FIXED | PD_FIXEDZERO ;--- pagerdata: must be ZERO ;--- flags: PC_FIXED, PC_LOCKED, PC_USER, PC_WRITEABLE ;--- if PC_FIXED or PC_LOCKED is set, none of the pages must be committed _page equ npages equ hpd equ pagerdata equ flags equ _PageCommit proc public push esi push ebx mov esi, [_page] shl esi, 2 add esi, ?PAGEMAP shl 22 mov ebx, [flags] mov ecx, [npages] xor edx, edx cld and ecx, ecx jz @@error @@nextitem: lodsd and al,al ;valid address space? jz @@error test al,P_PRES ;page committed? setz al test bl,PC_FIXED or PC_LOCKED jz @F cmp al,0 ;no committed page allowed jz @@error @@: movzx eax,al add edx, eax loop @@nextitem push edx call GetFreePages cmp ah,0 pop eax jnz @@error cmp edx, eax jc @@error ; not enough free pages mov ecx, [npages] mov esi, [_page] shl esi, 2 add esi, ?PAGEMAP shl 22 mov ebx, [flags] shr ebx, 16 ; move PC_WRITEABLE and PC_USER to BL or bl,P_PRES ; also set the PRESENT bit @@nextitem2: lodsd test al,P_PRES jnz @F call AllocPage cmp ah,0 jnz @@error mov eax,[esi-4] mov al,0 or al, bl or eax, edx mov [esi-4], eax test byte ptr [hpd],1 ;zero init? jz @F push ecx push edi mov edi, esi sub edi, 4 + (?PAGEMAP shl 22) shl edi, 10 mov ecx,1000h/4 xor eax, eax rep stosd pop edi pop ecx @@: loop @@nextitem2 pop ebx pop esi push 1 pop eax ret @@error: pop ebx pop esi xor eax, eax ret align 4 _PageCommit endp ;--- _PageDecommit(page, npages, flags) ;--- page: linear page number ;--- flags: must be zero ;--- returns: eax == 0 on failure _page equ npages equ flags equ _PageDecommit proc public mov ecx, [npages] jecxz @@error push esi mov esi, 4[_page] shl esi, 2 add esi, ?PAGEMAP shl 22 @@nextitem: lodsd mov edx, eax test al,P_PRES ;committed page? jz @@notcommitted test ah,4 ;mapped page? jnz @F mov dl,0 and dh,0F0h push eax call FreePage pop edx @@: and edx, 0200h ;preserve the "begin block" bit or dl,P_WRITE or P_USER mov [esi-4],edx @@notcommitted: loop @@nextitem pop esi mov ecx,cr3 mov cr3,ecx ret @@error: xor eax, eax ret align 4 _PageDecommit endp ;--- _PageCommitPhys(page, npages, physpg, flags) ;--- page: linear page number ;--- npages:# of pages (region must NOT be committed) ;--- physpg: physical page # to commit. ;--- flags: use PC_INCR to map a contiguous phys region; ;--- PC_USER and PC_WRITEABLE also relevant; ;--- v5.83: added PC_CACHEWT and PC_CACHEDIS; ;--- returns: eax == 0 on failure _page equ npages equ physpg equ flags equ _PageCommitPhys proc mov ecx, [npages] jecxz @@error push esi mov esi, 4[_page] shl esi, 2 add esi, ?PAGEMAP shl 22 mov edx, esi cld @@nextitem: lodsd cmp al,0 ;valid PTE? jz @@error2 test al,P_PRES ;all PTEs must be non-committed! jnz @@error2 loop @@nextitem push edi push ebx mov esi, edx mov ecx, 12[npages] mov ebx, 12[physpg] mov edi, 12[flags] xor edx, edx test edi,PC_INCR jz @F mov dh,10h @@: shr edi, 16 and edi, (PC_WRITEABLE or PC_USER or PC_CACHEWT or PC_CACHEDIS) shr 16 or edi, 401h ;mark this PTE as a "mapped" page (400h) shl ebx, 12 @@nextitem2: lodsd and ah,0Fh movzx eax,ax or eax, edi or eax, ebx mov [esi-4], eax add ebx, edx loop @@nextitem2 pop ebx pop edi pop esi ret @@error2: pop esi @@error: xor eax, eax ret align 4 _PageCommitPhys endp ;--- notifications from Jemm (SYSTEM_EXIT) ;--- eax, ebx, esi, edi, ebp must not be changed. call_control_procs proc mov edx,offset ddb_list nextdevice: mov ecx, [edx].VxD_Desc_Block.DDB_Control_Proc jecxz @F pushad call ecx popad @@: mov edx,[edx].VxD_Desc_Block.DDB_Next and edx, edx jnz nextdevice ret call_control_procs endp ;--- in: eax->ID to find, if 000, edi=name ;--- out: eax = hModule FindDevice proc public mov edx,offset ddb_list @@: cmp ax, 0 jz cmpname cmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number jz found nextdev: mov edx,[edx].VxD_Desc_Block.DDB_Next and edx, edx jnz @B stc ret cmpname: pushad lea esi, [edx].VxD_Desc_Block.DDB_Name mov ecx, sizeof VxD_Desc_Block.DDB_Name repz cmpsb popad jnz nextdev found: mov eax, edx ret align 4 FindDevice endp ;--- esi -> DDB ;--- C set on failure ;--- new v5.74: alloc v86 callback if v86 api is to be installed VMM_Add_DDB proc public movzx eax, [esi].VxD_Desc_Block.DDB_Req_Device_Number @dprintf ?JLMDBG, <"VMM_Add_DBB, ID=%X",10>, eax mov edx, offset ddb_list @@nextitem: and eax,eax jz @F cmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number jz error @@: mov ecx, edx mov edx,[edx].VxD_Desc_Block.DDB_Next and edx, edx jnz @@nextitem and eax,eax jz done cmp [esi].VxD_Desc_Block.DDB_V86_API_Proc,0 ;v86 API? jz done @dprintf ?JLMDBG, <"VMM_Add_DBB: JLM with API",10> push ecx push esi mov esi,[esi].VxD_Desc_Block.DDB_V86_API_Proc xor edx,edx call Allocate_V86_Call_Back pop esi pop ecx jc error @dprintf ?JLMDBG, <"VMM_Add_DBB: callback allocated",10> mov [esi].VxD_Desc_Block.DDB_V86_API_CSIP, eax cmp [Int2fHooked],0 jnz done inc [Int2fHooked] pushad mov eax, ?APIINT mov esi, offset Int2fHook call Hook_V86_Int_Chain @dprintf ?JLMDBG, <"VMM_Add_DBB: Int 2F hooked, addr=%X",10>, esi popad done: mov [ecx].VxD_Desc_Block.DDB_Next, esi ret error: stc ret align 4 VMM_Add_DDB endp ifdef _DEBUG dd offset trueprevint2f finalint2f proc cmp word ptr [ebp].Client_Reg_Struc.Client_EAX, 168Fh jz done @dprintf ?INT2FHOOKDBG, <"finalint2f: ax=%X",10>, word ptr [ebp].Client_Reg_Struc.Client_EAX done: stc ret finalint2f endp endif ;--- a hook proc must be prefixed by the flat address ;--- of the variable where the previous value is stored ;--- AX=??? dd offset prevint2f Int2fHook proc ifdef _DEBUG cmp word ptr [ebp].Client_Reg_Struc.Client_EAX, 168Fh ;that happens pretty often, so don't log it jz @F endif @dprintf ?INT2FHOOKDBG, <"Int2fHook: ax=%X, client_ax=%X",10>, ax, word ptr [ebp].Client_Reg_Struc.Client_EAX @@: cmp word ptr [ebp].Client_Reg_Struc.Client_EAX, 1684h jnz not_ours @dprintf ?INT2FHOOKDBG, <"Int2FHook: ax=1684h detected, device scan, BX=ID=%X",10>, word ptr [ebp].Client_Reg_Struc.Client_EBX mov edx, offset ddb_list mov ax, word ptr [ebp].Client_Reg_Struc.Client_EBX ;v5.85 added @@nextitem: cmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number jz found mov edx,[edx].VxD_Desc_Block.DDB_Next and edx, edx jnz @@nextitem xor eax, eax jmp @F found: @dprintf ?INT2FHOOKDBG, <"Int2FHook: found Device=%X",10>, word ptr [edx].VxD_Desc_Block.DDB_Req_Device_Number mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 0 ;v5.82: set AL to 0 if ok mov eax, [edx].VxD_Desc_Block.DDB_V86_API_CSIP @@: mov word ptr [ebp].Client_Reg_Struc.Client_EDI, ax shr eax, 16 mov word ptr [ebp].Client_Reg_Struc.Client_ES, ax clc ret not_ours: stc ret align 4 Int2fHook endp ;--- edi -> DDB ;--- C set on failure ;--- new v5.74: free v86 callback if v86 api exists VMM_Remove_DDB proc public xor eax, eax and edi, edi jz @@error mov edx, offset ddb_list @@nextitem: cmp edx, edi jz @@found mov eax, edx mov edx,[edx].VxD_Desc_Block.DDB_Next and edx, edx jnz @@nextitem @@error: stc ret @@found: and eax, eax ;dont remove the first device jz @@error mov ecx,[edx].VxD_Desc_Block.DDB_Next mov [eax].VxD_Desc_Block.DDB_Next, ecx mov eax, [edi].VxD_Desc_Block.DDB_V86_API_CSIP and eax, eax jz @F call Free_V86_Call_Back @@: clc ret align 4 VMM_Remove_DDB endp end ================================================ FILE: Tools/JLOAD/WINNT.INC ================================================ ifndef WINNT_INCLUDED pushcontext listing .nolist .xcref ifdef @list_on .list .cref endif WINNT_INCLUDED equ 1 HANDLE typedef ptr ifndef HINSTANCE HINSTANCE typedef HANDLE endif MINCHAR EQU 80h MAXCHAR EQU 7fh MINSHORT EQU 8000h MAXSHORT EQU 7fffh MINLONG EQU 80000000h MAXLONG EQU 7fffffffh MAXBYTE EQU 0ffh MAXWORD EQU 0ffffh MAXDWORD EQU 0ffffffffh SECTION_QUERY equ 00001h SECTION_MAP_WRITE equ 00002h SECTION_MAP_READ equ 00004h SECTION_MAP_EXECUTE equ 00008h SECTION_EXTEND_SIZE equ 00010h DUPLICATE_CLOSE_SOURCE equ 00000001h DUPLICATE_SAME_ACCESS equ 00000002h PAGE_NOACCESS equ 1h PAGE_READONLY equ 2h PAGE_READWRITE equ 4h PAGE_WRITECOPY equ 8h PAGE_EXECUTE equ 10h PAGE_EXECUTE_READ equ 20h PAGE_EXECUTE_READWRITE equ 40h PAGE_EXECUTE_WRITECOPY equ 80h PAGE_GUARD equ 100h PAGE_NOCACHE equ 200h MEM_COMMIT equ 1000h MEM_RESERVE equ 2000h MEM_DECOMMIT equ 4000h MEM_RELEASE equ 8000h MEM_FREE equ 10000h MEM_PRIVATE equ 20000h MEM_MAPPED equ 40000h MEM_RESET equ 80000h ;#define MEM_TOP_DOWN 0x100000 ;#define SEC_FILE 0x800000 ;#define SEC_IMAGE 0x1000000 ;#define SEC_RESERVE 0x4000000 ;#define SEC_COMMIT 0x8000000 ;#define SEC_NOCACHE 0x10000000 HEAP_NO_SERIALIZE equ 00000001h HEAP_GROWABLE equ 00000002h HEAP_GENERATE_EXCEPTIONS equ 00000004h HEAP_ZERO_MEMORY equ 00000008h HEAP_REALLOC_IN_PLACE_ONLY equ 00000010h MEMORY_BASIC_INFORMATION STRUCT BaseAddress DWORD ? AllocationBase DWORD ? AllocationProtect DWORD ? RegionSize DWORD ? State DWORD ? Protect DWORD ? Type_ DWORD ? MEMORY_BASIC_INFORMATION ENDS RTL_CRITICAL_SECTION struct DebugInfo dd ? LockCount sdword ? RecursionCount sdword ? OwningThread dd ? LockSemaphore dd ? SpinCount dd ? RTL_CRITICAL_SECTION ends DLL_PROCESS_ATTACH equ 1 DLL_THREAD_ATTACH equ 2 DLL_THREAD_DETACH equ 3 DLL_PROCESS_DETACH equ 0 LANG_NEUTRAL equ 0 SUBLANG_NEUTRAL equ 0 SUBLANG_DEFAULT equ 1 SUBLANG_SYS_DEFAULT equ 2 SORT_DEFAULT equ 0 MAKELANGID macro p:req, s:req exitm <(s shl 10) or p> endm MAKELCID macro lgid:req, srtid:req exitm <(srtid shl 16) or lgid> endm CSTR_LESS_THAN equ 1 ; // string 1 less than string 2 CSTR_EQUAL equ 2 ; // string 1 equal to string 2 CSTR_GREATER_THAN equ 3 ; // string 1 greater than string 2 NORM_IGNORECASE equ 000000001h ; /* ignore case */ LANG_SYSTEM_DEFAULT equ MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT) LANG_USER_DEFAULT equ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) LOCALE_SYSTEM_DEFAULT equ MAKELCID(LANG_SYSTEM_DEFAULT, SORT_DEFAULT) LOCALE_USER_DEFAULT equ MAKELCID(LANG_USER_DEFAULT, SORT_DEFAULT) FILE_ATTRIBUTE_READONLY equ 00000001h FILE_ATTRIBUTE_HIDDEN equ 00000002h FILE_ATTRIBUTE_SYSTEM equ 00000004h FILE_ATTRIBUTE_DIRECTORY equ 00000010h FILE_ATTRIBUTE_ARCHIVE equ 00000020h FILE_ATTRIBUTE_NORMAL equ 00000080h FILE_ATTRIBUTE_TEMPORARY equ 00000100h FILE_ATTRIBUTE_COMPRESSED equ 00000800h FILE_ATTRIBUTE_OFFLINE equ 00001000h FILE_SHARE_READ equ 01 FILE_SHARE_WRITE equ 02 FILE_SHARE_DELETE equ 04 STATUS_WAIT_0 equ 000000000h STATUS_ABANDONED_WAIT_0 equ 000000080h STATUS_USER_APC equ 0000000C0h STATUS_TIMEOUT equ 000000102h STATUS_PENDING equ 000000103h STATUS_SEGMENT_NOTIFICATION equ 040000005h STATUS_GUARD_PAGE_VIOLATION equ 080000001h STATUS_DATATYPE_MISALIGNMENT equ 080000002h STATUS_BREAKPOINT equ 080000003h STATUS_SINGLE_STEP equ 080000004h STATUS_ACCESS_VIOLATION equ 0C0000005h STATUS_IN_PAGE_ERROR equ 0C0000006h STATUS_INVALID_HANDLE equ 0C0000008h STATUS_NO_MEMORY equ 0C0000017h STATUS_ILLEGAL_INSTRUCTION equ 0C000001Dh STATUS_NONCONTINUABLE_EXCEPTION equ 0C0000025h STATUS_INVALID_DISPOSITION equ 0C0000026h STATUS_ARRAY_BOUNDS_EXCEEDED equ 0C000008Ch STATUS_FLOAT_DENORMAL_OPERAND equ 0C000008Dh STATUS_FLOAT_DIVIDE_BY_ZERO equ 0C000008Eh STATUS_FLOAT_INEXACT_RESULT equ 0C000008Fh STATUS_FLOAT_INVALID_OPERATION equ 0C0000090h STATUS_FLOAT_OVERFLOW equ 0C0000091h STATUS_FLOAT_STACK_CHECK equ 0C0000092h STATUS_FLOAT_UNDERFLOW equ 0C0000093h STATUS_INTEGER_DIVIDE_BY_ZERO equ 0C0000094h STATUS_INTEGER_OVERFLOW equ 0C0000095h STATUS_PRIVILEGED_INSTRUCTION equ 0C0000096h STATUS_STACK_OVERFLOW equ 0C00000FDh STATUS_CONTROL_C_EXIT equ 0C000013Ah THREAD_BASE_PRIORITY_LOWRT EQU 15 THREAD_BASE_PRIORITY_MAX EQU 2 THREAD_BASE_PRIORITY_MIN EQU - 2 THREAD_BASE_PRIORITY_IDLE EQU - 15 REG_NONE EQU 0 REG_SZ EQU 1 REG_EXPAND_SZ EQU 2 REG_BINARY EQU 3 REG_DWORD EQU 4 REG_DWORD_LITTLE_ENDIAN EQU 4 REG_DWORD_BIG_ENDIAN EQU 5 REG_LINK EQU 6 REG_MULTI_SZ EQU 7 REG_RESOURCE_LIST EQU 8 REG_FULL_RESOURCE_DESCRIPTOR EQU 9 REG_RESOURCE_REQUIREMENTS_LIST EQU 10 REG_QWORD EQU 11 REG_QWORD_LITTLE_ENDIAN EQU 11 CONTEXT_i386 equ 00010000h CONTEXT_i486 equ 00010000h CONTEXT_CONTROL equ CONTEXT_i386 or 00000001h ;// SS:SP, CS:IP, FLAGS, BP CONTEXT_INTEGER equ CONTEXT_i386 or 00000002h ;// AX, BX, CX, DX, SI, DI CONTEXT_SEGMENTS equ CONTEXT_i386 or 00000004h ;// DS, ES, FS, GS CONTEXT_FLOATING_POINT equ CONTEXT_i386 or 00000008h ;// 387 state CONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 or 00000010h ;// DB 0-3,6,7 CONTEXT_FULL equ CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS SIZE_OF_80387_REGISTERS equ 80 FLOATING_SAVE_AREA struct ControlWord dd ? StatusWord dd ? TagWord dd ? ErrorOffset dd ? ErrorSelector dd ? DataOffset dd ? DataSelector dd ? RegisterArea db SIZE_OF_80387_REGISTERS dup (?) Cr0NpxState dd ? FLOATING_SAVE_AREA ends ;/ ;/ Context Frame ;/ ;/ This frame has a several purposes: 1) it is used as an argument to ;/ NtContinue, 2) is is used to constuct a call frame for APC delivery, ;/ and 3) it is used in the user level thread creation routines. ;/ ;/ The layout of the record conforms to a standard call frame. ;/ CONTEXT struct ; // ; // The flags values within this flag control the contents of ; // a CONTEXT record. ; // ; // If the context record is used as an input parameter, then ; // for each portion of the context record controlled by a flag ; // whose value is set, it is assumed that that portion of the ; // context record contains valid context. If the context record ; // is being used to modify a threads context, then only that ; // portion of the threads context will be modified. ; // ; // If the context record is used as an IN OUT parameter to capture ; // the context of a thread, then only those portions of the thread's ; // context corresponding to set flags will be returned. ; // ; // The context record is never used as an OUT only parameter. ; // ContextFlags dd ? ;+0 ; // ; // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is ; // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT ; // included in CONTEXT_FULL. ; // rDr0 dd ? ;+4 rDr1 dd ? rDr2 dd ? rDr3 dd ? rDr6 dd ? rDr7 dd ? ; // ; // This section is specified/returned if the ; // ContextFlags word contains the flag CONTEXT_FLOATING_POINT. ; // FloatSave FLOATING_SAVE_AREA {} ;+28 ; // ; // This section is specified/returned if the ; // ContextFlags word contains the flag CONTEXT_SEGMENTS. ; // SegGs dd ? ;+140 SegFs dd ? SegEs dd ? SegDs dd ? ; // ; // This section is specified/returned if the ; // ContextFlags word contains the flag CONTEXT_INTEGER. ; // rEdi dd ? ;+156 rEsi dd ? rEbx dd ? rEdx dd ? rEcx dd ? rEax dd ? ; // ; // This section is specified/returned if the ; // ContextFlags word contains the flag CONTEXT_CONTROL. ; // rEbp dd ? ;+180 rEip dd ? SegCs dd ? EFlags dd ? rEsp dd ? SegSs dd ? CONTEXT ends ;+204 IMAGE_DOS_HEADER STRUCT e_magic WORD ? ;+0 e_cblp WORD ? ;+2 e_cp WORD ? ;+4 e_crlc WORD ? ;+6 number of relocation records e_cparhdr WORD ? ;+8 e_minalloc WORD ? ;+10 e_maxalloc WORD ? ;+12 e_ss WORD ? ;+14 e_sp WORD ? ;+16 e_csum WORD ? ;+18 e_ip WORD ? ;+20 e_cs WORD ? ;+22 e_lfarlc WORD ? ;+24 begin relocation records e_ovno WORD ? ;+26 e_res WORD 4 dup(?) ;+28 e_oemid WORD ? ;+36 e_oeminfo WORD ? ;+38 e_res2 WORD 10 dup(?) ;+40 e_lfanew DWORD ? ;+60 IMAGE_DOS_HEADER ENDS IMAGE_FILE_HEADER struct ;size=20 Machine dw ? ;0 NumberOfSections dw ? TimeDateStamp dd ? ;4 PointerToSymbolTable dd ? ;8 NumberOfSymbols dd ? ;12 SizeOfOptionalHeader dw ? ;16 Characteristics dw ? ;flags IMAGE_FILE_HEADER ends ;*** flags *** ; 0400: If Image is on removable media, copy and run from the swap file. ; 0800: If Image is on Net, copy and run from the swap file. IMAGE_FILE_RELOCS_STRIPPED equ 0001h IMAGE_FILE_EXECUTABLE_IMAGE equ 0002h IMAGE_FILE_LINE_NUMS_STRIPPED equ 0004h IMAGE_FILE_LOCAL_SYMS_STRIPPED equ 0008h IMAGE_FILE_16BIT_MACHINE equ 0040h IMAGE_FILE_BYTES_REVERSED_LO equ 0080h ; Bytes of machine word are reversed. IMAGE_FILE_32BIT_MACHINE equ 0100h IMAGE_FILE_DEBUG_STRIPPED equ 0200h ; Debugging info stripped from file in .DBG file IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP equ 0400h IMAGE_FILE_NET_RUN_FROM_SWAP equ 0800h IMAGE_FILE_SYSTEM equ 1000h ; System File. IMAGE_FILE_DLL equ 2000h IMAGE_FILE_UP_SYSTEM_ONLY equ 4000h ; File should only be run on a UP machine IMAGE_FILE_BYTES_REVERSED_HI equ 8000h ; Bytes of machine word are reversed. IMAGE_FILE_MACHINE_UNKNOWN equ 0h IMAGE_FILE_MACHINE_I386 equ 14ch ;// Intel 386. IMAGE_FILE_MACHINE_R3000 equ 162h ;// MIPS little-endian, 0x160 big-endian IMAGE_FILE_MACHINE_R4000 equ 166h ;// MIPS little-endian IMAGE_FILE_MACHINE_R10000 equ 168h ;// MIPS little-endian IMAGE_FILE_MACHINE_ALPHA equ 184h ;// Alpha_AXP IMAGE_FILE_MACHINE_POWERPC equ 1F0h ;// IBM PowerPC Little-Endian IMAGE_DATA_DIRECTORY struct VirtualAddress DWORD ? Size_ DWORD ? IMAGE_DATA_DIRECTORY ends IMAGE_NUMBEROF_DIRECTORY_ENTRIES equ 16 IMAGE_OPTIONAL_HEADER struct ;size = 28 + 68 + 128 = 224 ;standard, size=28 Magic dw ? ;0 MajorLinkerVersion db ? MinorLinkerVersion db ? SizeOfCode dd ? ;4 SizeOfInitializedData dd ? ;8 SizeOfUninitializedData dd ? ;12 AddressOfEntryPoint dd ? ;16 BaseOfCode dd ? ;20 BaseOfData dd ? ;24 ;NT specific, size = 68 ImageBase dd ? ;28 SectionAlignment dd ? ;32 FileAlignment dd ? ;36 MajorOperatingSystemVersion dw ? ;40 MinorOperatingSystemVersion dw ? MajorImageVersion dw ? ;44 MinorImageVersion dw ? MajorSubsystemVersion dw ? ;48 MinorSubsystemVersion dw ? Win32VersionValue dd ? ;52 SizeOfImage dd ? ;56 SizeOfHeaders dd ? ;60 CheckSum dd ? ;64 Subsystem dw ? ;68 DllCharacteristics dw ? SizeOfStackReserve dd ? ;72 SizeOfStackCommit dd ? ;76 SizeOfHeapReserve dd ? ;80 SizeOfHeapCommit dd ? ;84 LoaderFlags dd ? ;88 NumberOfRvaAndSizes dd ? ;92 DataDirectory IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup (<>) IMAGE_OPTIONAL_HEADER ends ; Subsystem Values IMAGE_SUBSYSTEM_UNKNOWN equ 0 ;/ Unknown subsystem. IMAGE_SUBSYSTEM_NATIVE equ 1 ;/ Image doesn't require a subsystem. IMAGE_SUBSYSTEM_WINDOWS_GUI equ 2 ;/ Image runs in the Windows GUI subsystem. IMAGE_SUBSYSTEM_WINDOWS_CUI equ 3 ;/ Image runs in the Windows character subsystem. IMAGE_SUBSYSTEM_OS2_CUI equ 5 ;/ image runs in the OS/2 character subsystem. IMAGE_SUBSYSTEM_POSIX_CUI equ 7 ;/ image run in the Posix character subsystem. IMAGE_SUBSYSTEM_RESERVED8 equ 8 ;/ image run in the 8 subsystem. ; Directory Entries IMAGE_DIRECTORY_ENTRY_EXPORT equ 0 ;// Export Directory IMAGE_DIRECTORY_ENTRY_IMPORT equ 1 ;// Import Directory IMAGE_DIRECTORY_ENTRY_RESOURCE equ 2 ;// Resource Directory IMAGE_DIRECTORY_ENTRY_EXCEPTION equ 3 ;// Exception Directory IMAGE_DIRECTORY_ENTRY_SECURITY equ 4 ;// Security Directory IMAGE_DIRECTORY_ENTRY_BASERELOC equ 5 ;// Base Relocation Table IMAGE_DIRECTORY_ENTRY_DEBUG equ 6 ;// Debug Directory IMAGE_DIRECTORY_ENTRY_COPYRIGHT equ 7 ;// Description String IMAGE_DIRECTORY_ENTRY_ARCHITECTURE equ 7 ;// Architecture Specific Data IMAGE_DIRECTORY_ENTRY_GLOBALPTR equ 8 ;// Machine Value (MIPS GP) IMAGE_DIRECTORY_ENTRY_TLS equ 9 ;// TLS Directory IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG equ 10 ;// Load Configuration Directory IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT equ 11 ;// Bound Import Directory in headers IMAGE_DIRECTORY_ENTRY_IAT equ 12 ;// Import Address Table IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT equ 13 ;// Delay Load Import Descriptors IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR equ 14 ;// COM Runtime descriptor IMAGE_NT_HEADERS struct Signature dd ? ;00 "PE" FileHeader IMAGE_FILE_HEADER <> OptionalHeader IMAGE_OPTIONAL_HEADER <> IMAGE_NT_HEADERS ends ;--- section table IMAGE_SECTION_HEADER struct Name_ db 8 dup (?) ;0 union Misc PhysicalAddress dd ? ;8 VirtualSize dd ? ends VirtualAddress dd ? ;12 SizeOfRawData dd ? ;16 PointerToRawData dd ? ;20 PointerToRelocations dd ? ;24 points to array of IMAGE_RELOCATIONs (.OBJ) PointerToLinenumbers dd ? ;28 NumberOfRelocations dw ? ;32 NumberOfLinenumbers dw ? Characteristics dd ? ;36 IMAGE_SECTION_HEADER ends ;// ;// Section characteristics. ;// IMAGE_SCN_TYPE_NO_PAD equ 00000008h ; Reserved. ; IMAGE_SCN_CNT_CODE equ 00000020h ; Section contains code. IMAGE_SCN_CNT_INITIALIZED_DATA equ 00000040h ; Section contains initialized data. IMAGE_SCN_CNT_UNINITIALIZED_DATA equ 00000080h ; Section contains uninitialized data. ; IMAGE_SCN_LNK_OTHER equ 00000100h ; Reserved. IMAGE_SCN_LNK_INFO equ 00000200h ; Section contains comments or some other type of information. ;IMAGE_SCN_TYPE_OVER equ 00000400h ; Reserved. IMAGE_SCN_LNK_REMOVE equ 00000800h ; Section contents will not become part of image. IMAGE_SCN_LNK_COMDAT equ 00001000h ; Section contents comdat. ;// 00002000h ; Reserved. ; ;IMAGE_SCN_MEM_PROTECTED - Obsolete 00004000h ; IMAGE_SCN_MEM_FARDATA equ 00008000h ; ;IMAGE_SCN_MEM_SYSHEAP - Obsolete 00010000h ; IMAGE_SCN_MEM_PURGEABLE equ 00020000h ; IMAGE_SCN_MEM_16BIT equ 00020000h ; IMAGE_SCN_MEM_LOCKED equ 00040000h ; IMAGE_SCN_MEM_PRELOAD equ 00080000h ; ; IMAGE_SCN_ALIGN_1BYTES equ 00100000h ; IMAGE_SCN_ALIGN_2BYTES equ 00200000h ; IMAGE_SCN_ALIGN_4BYTES equ 00300000h ; IMAGE_SCN_ALIGN_8BYTES equ 00400000h ; IMAGE_SCN_ALIGN_16BYTES equ 00500000h ; Default alignment if no others are specified. IMAGE_SCN_ALIGN_32BYTES equ 00600000h ; IMAGE_SCN_ALIGN_64BYTES equ 00700000h ; ;// Unused 00800000h ; ; IMAGE_SCN_LNK_NRELOC_OVFL equ 01000000h ; Section contains extended relocations. IMAGE_SCN_MEM_DISCARDABLE equ 02000000h ; Section can be discarded. IMAGE_SCN_MEM_NOT_CACHED equ 04000000h ; Section is not cachable. IMAGE_SCN_MEM_NOT_PAGED equ 08000000h ; Section is not pageable. IMAGE_SCN_MEM_SHARED equ 10000000h ; Section is shareable. IMAGE_SCN_MEM_EXECUTE equ 20000000h ; Section is executable. IMAGE_SCN_MEM_READ equ 40000000h ; Section is readable. IMAGE_SCN_MEM_WRITE equ 80000000h ; Section is writeable. ;--- relocations IMAGE_RELOCATION struct union VirtualAddress DWORD ? RelocCount DWORD ? ends SymbolTableIndex DWORD ? Type_ WORD ? IMAGE_RELOCATION ends ;// ;// I386 relocation types. ;// IMAGE_REL_I386_ABSOLUTE equ 00000h ;// Reference is absolute, no relocation is necessary IMAGE_REL_I386_DIR16 equ 00001h ;// Direct 16-bit reference to the symbols virtual address IMAGE_REL_I386_REL16 equ 00002h ;// PC-relative 16-bit reference to the symbols virtual address IMAGE_REL_I386_DIR32 equ 00006h ;// Direct 32-bit reference to the symbols virtual address IMAGE_REL_I386_DIR32NB equ 00007h ;// Direct 32-bit reference to the symbols virtual address, base not included IMAGE_REL_I386_SEG12 equ 00009h ;// Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address IMAGE_REL_I386_SECTION equ 0000Ah IMAGE_REL_I386_SECREL equ 0000Bh IMAGE_REL_I386_TOKEN equ 0000Ch ;// clr token IMAGE_REL_I386_SECREL7 equ 0000Dh ;// 7 bit offset from base of section containing target IMAGE_REL_I386_REL32 equ 00014h ;// PC-relative 32-bit reference to the symbols virtual address IMAGE_BASE_RELOCATION struct VirtualAddress DWORD ? SizeOfBlock DWORD ? IMAGE_BASE_RELOCATION ends IMAGE_REL_BASED_ABSOLUTE equ 0 IMAGE_REL_BASED_HIGH equ 1 IMAGE_REL_BASED_LOW equ 2 IMAGE_REL_BASED_HIGHLOW equ 3 IMAGE_REL_BASED_HIGHADJ equ 4 IMAGE_REL_BASED_MIPS_JMPADDR equ 5 IMAGE_REL_BASED_MIPS_JMPADDR16 equ 9 IMAGE_REL_BASED_IA64_IMM64 equ 9 IMAGE_REL_BASED_DIR64 equ 10 ;--- exports IMAGE_EXPORT_DIRECTORY struct Characteristics dd ? TimeDateStamp dd ? MajorVersion dw ? MinorVersion dw ? nName dd ? ;name of module nBase dd ? ;base of ordinal NumberOfFunctions dd ? ;number of entries in EAT table NumberOfNames dd ? ;number of entries in name/ordinals table AddressOfFunctions dd ? ;RVA "export address table" (EAT) AddressOfNames dd ? ;RVA "name table" () AddressOfNameOrdinals dd ? ;RVA "ordinals table" (WORDS) IMAGE_EXPORT_DIRECTORY ends popcontext listing endif ================================================ FILE: Tools/JLOAD/license.txt ================================================ Copyright (c) 2021 Andreas Grech (japheth) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Tools/MAKE.BAT ================================================ @echo off cd CPUID call MAKE cd .. cd CpuStat call MAKE cd .. cd EMSSTAT call MAKE cd .. cd JEMFBHLP call MAKE cd .. cd JLOAD nmake /nologo cd .. cd MEMSTAT call MAKE cd .. cd MoveXBDA call MAKE cd .. cd UMBM call MAKE cd .. cd VCPI call MAKE cd .. cd XMSSTAT call MAKE cd .. ================================================ FILE: Tools/MEMSTAT/MAKE.BAT ================================================ @echo off rem creates MEMSTAT.EXE with JWasm jwasm -c -nologo -mz -Fl MEMSTAT.ASM ================================================ FILE: Tools/MEMSTAT/MEMSTAT.ASM ================================================ ;*** get extended memory status using int 15h ;*** AH=88h ;*** AH=C7h if supported ;*** AX=E801h ;*** AX=E820h .286 .model small .386 DGROUP group _TEXT .dosseg assume ds:dgroup printf proto c :ptr byte, :VARARG CStr macro text:VARARG local sym .const sym db text,0 .code exitm endm I15C7S struct wSize dw ? dwLBelow16 dd ? dwLAbove16 dd ? dwSBelow16 dd ? dwSAbove16 dd ? dwCBelow16 dd ? dwCAbove16 dd ? dwXBelow16 dd ? dwXAbove16 dd ? wUMB dw ? wFree dw ? dd ? I15C7S ends .data? buffer db 20h dup (?) i15c7 I15C7S .data qSum dq 0 qAvl dq 0 qACPI dq 0 qRsvd dq 0 .code include printf.inc .386 ;--- display conventional memory size returned by INT 12h conv_mem proc int 12h invoke printf, CStr("conventional memory (Int 12h): %u kB",10), ax ret conv_mem endp ;--- call int 15h, ah=C7h (later PS/2 machines, usually fails on newer systems) I15C0S struct wBytes dw ? ;+0 bModel db ? ;+2 bSubModel db ? ;+3 bBiosRev db ? ;+4 bFeature1 db ? ;+5 bFeature2 db ? ;+6 bFeature3 db ? bFeature4 db ? bFeature5 db ? I15C0S ends do_c0c7 proc stc mov ah,0C0h int 15h jnc @F invoke printf, CStr("Int 15h, ah=C0h failed",10) jmp no15c0 @@: cmp es:[bx].I15C0S.wBytes,7 jb no15c0 mov al,es:[bx].I15C0S.bFeature2 test al,10h ;int 15h, ah=0C7h supported? jz no15c7 mov ah,0C7h mov si,offset i15c7 int 15h jc no15c7 invoke printf, CStr("Int 15h, ah=C7h:",10) invoke printf, CStr("Local memory (1 MB-16 MB/16 MB-4 GB): %08lX/%08lX kB",10),\ i15c7.dwLBelow16, i15c7.dwLAbove16 invoke printf, CStr("System memory (1 MB-16 MB/16 MB-4 GB): %08lX/%08lX kB",10),\ i15c7.dwSBelow16, i15c7.dwSAbove16 invoke printf, CStr("Cacheable memory (1 MB-16 MB/16 MB-4 GB): %08lX/%08lX kB",10),\ i15c7.dwCBelow16, i15c7.dwCAbove16 no15c7: no15c0: ret do_c0c7 endp ;--- print int 15h, ah=88h info do_88 proc clc mov ax,8800h int 15h jnc @F invoke printf, CStr("Int 15h, ah=88h failed",10) jmp done_88 @@: invoke printf, CStr("Int 15h, ah=88h, extended memory: %u kB",10), ax done_88: ret do_88 endp ;--- print int 15h, ax=e801 info do_e801 proc xor cx,cx xor dx,dx xor bx,bx mov ax,0E801h clc ;the carry flag is not reliably set/reset! int 15h jc error and bx, bx jnz @F and cx, cx jnz @F error: invoke printf, CStr("Int 15h, ax=E801h failed",10) jmp done_e801 @@: .if (!ax) ;some bioses return values in CX:DX mov ax, cx mov bx, dx .endif push bx push ax invoke printf, CStr("Int 15h, ax=E801h:",10) pop ax .if (ax > 3C00h) invoke printf, CStr("ext. memory below 16 MB: %u (0x%X) KB ???",10), ax, ax .else invoke printf, CStr("ext. memory below 16 MB: %u (0x%X) KB",10), ax, ax .endif pop bx mov edx, 1000000h mov ax, bx movzx ecx, bx shl ecx, 16 add ecx, edx dec ecx shr ax, 4 invoke printf, CStr("ext. memory above 16 MB: %u 64 KB blocks = %u MB [%lX-%lX]",10), bx, ax, edx, ecx done_e801: ret do_e801 endp SMAP equ 534d4150h E820MAP struct baselow dd ? basehigh dd ? lenlow dd ? lenhigh dd ? type_ dd ? E820MAP ends ;--- print int 15h, ax=e820 info do_e820 proc push ds pop es mov si,0 mov ebx,0 mov di, offset buffer .while (1) mov ecx, sizeof E820MAP mov edx,"SMAP" mov eax,0E820h clc int 15h .break .if CARRY? .break .if (eax != "SMAP") push ebx .if (!si) invoke printf, CStr("Int 15h, eax=E820h:",10) invoke printf, CStr(" address range size type ",10) invoke printf, CStr("----------------------------------------------------",10) inc si .endif ;--- get size in EDX:EAX mov eax,[di].E820MAP.lenlow mov edx,[di].E820MAP.lenhigh mov ecx, [di].E820MAP.type_ .if (ecx == 1) mov cx, CStr("available") add dword ptr qAvl+0, eax adc dword ptr qAvl+4, edx .elseif (ecx == 2) mov cx, CStr("reserved") add dword ptr qRsvd+0, eax adc dword ptr qRsvd+4, edx .elseif (ecx == 3) mov cx, CStr("ACPI reclaimable") add dword ptr qACPI+0, eax adc dword ptr qACPI+4, edx .elseif (ecx == 4) mov cx, CStr("ACPI NVS") add dword ptr qACPI+0, eax adc dword ptr qACPI+4, edx .elseif (ecx == 5) mov cx, CStr("bad memory") .else mov cx, CStr("unknown") .endif add dword ptr qSum+0, eax adc dword ptr qSum+4, edx add eax, [di].E820MAP.baselow adc edx, [di].E820MAP.basehigh sub eax,1 sbb edx,0 .if (dword ptr [di].E820MAP.lenhigh) invoke printf, CStr("%lX%08lX-%lX%08lX %lX%08lX %lX (%s)",10), [di].E820MAP.basehigh, [di].E820MAP.baselow, edx, eax, [di].E820MAP.lenhigh, [di].E820MAP.lenlow, [di].E820MAP.type_, cx .else invoke printf, CStr("%lX%08lX-%lX%08lX %9lX %lX (%s)",10), [di].E820MAP.basehigh, [di].E820MAP.baselow, edx, eax, [di].E820MAP.lenlow, [di].E820MAP.type_, cx .endif pop ebx .break .if (ebx == 0) .endw .if (!si) invoke printf, CStr("Int 15h, eax=E820h failed",10) .else invoke printf, CStr("----------------------------------------------------",10) mov eax,dword ptr qSum+0 mov edx,dword ptr qSum+4 mov ecx,100000h div ecx mov esi, eax mov ebx, eax mov eax,dword ptr qACPI+0 mov edx,dword ptr qACPI+4 mov ecx,400h div ecx mov ebx, eax mov eax,dword ptr qRsvd+0 mov edx,dword ptr qRsvd+4 mov ecx,100000h div ecx mov ebp, eax mov eax,dword ptr qAvl+0 mov edx,dword ptr qAvl+4 mov ecx,100000h div ecx invoke printf, CStr("available: %lu MB, ACPI: %lu kB, rsvd: %lu MB, total: %lu MB",10), eax, ebx, ebp, esi .endif ret do_e820 endp ;--- display location and size of XBDA xbda proc push 40h pop es mov ax,es:[000Eh] and ax,ax jz noxbda mov es,ax mov bl,es:[0] mov bh,0 invoke printf, CStr("XBDA at segment %X, size %u kB",10), ax, bx ret noxbda: invoke printf, CStr("no XBDA on this system",10) ret xbda endp ;*** main() main proc c call conv_mem call xbda call do_c0c7 call do_88 call do_e801 call do_e820 ret main endp start: mov ax,@data mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx call main mov ah,4Ch int 21h .stack 400h END start ================================================ FILE: Tools/MEMSTAT/PRINTF.INC ================================================ ;--- simple printf() implementation handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ;--- outb is expected to be onto stack ltob PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP ;--- ds=dgroup, ss don't need to be dgroup printf PROC c uses si di bx fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx nextdigit: cmp BYTE PTR [si],'0' jb digitsdone cmp BYTE PTR [si],'9' ja digitsdone lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax jmp nextdigit digitsdone: mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u mov al,'%' jmp @@L359 handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn printf ENDP ================================================ FILE: Tools/MoveXBDA/MAKE.BAT ================================================ @echo off rem create MOVEXBDA.EXE with JWasm jwasm -nologo -mz -Fl MOVEXBDA.ASM ================================================ FILE: Tools/MoveXBDA/MOVEXBDA.ASM ================================================ ;--- MOVEXBDA moves XBDA to low memory. If the XBDA is already moved ;--- or if it is too large, nothing is done. ;--- MOVEXBDA hooks interrupt 19h and will restore the XBDA to its ;--- previous location if this interrupt is called. ;--- MOVEXBDA is Public Domain. .286 .model small DGROUP group _TEXT ;ifndef MAXXBDA ;MAXXBDA equ 4 ;max size of XBDA in KB ;endif XBDAADR equ 000Eh ;address of XBDA segment in BIOS data region MEMSIZE equ 0013h ;size of memory in kB in BIOS data region ?WELCOME equ 0 ;display message at start MCB struct sig db ? psp dw ? _size dw ? MCB ends XBDA struct _size db ? ;size of XBDA in KB XBDA ends IODAT struct cmdlen db ? unit db ? cmd db ? status dw ? db 8 dup (?) media db ? trans dd ? count dw ? ;+ 12 init:offset parameter line start dw ? ;+ 14 init:segment parameter line drive db ? IODAT ends .code dw -1 dw -1 dw 8000h ;attribute dw offset devstrat ;device strategy intproc dw offset devintfirst ;device interrupt devname db 'MOVXBDA#' cmdptr dd 1 dup (?) oldint19 dd ? devstrat proc far mov cs:word ptr[cmdptr],bx mov cs:word ptr[cmdptr+2],es ret devstrat endp devint proc far push bx push ds lds bx,cs:[cmdptr] mov word ptr [bx.IODAT.status],8103h pop ds pop bx ret devint endp ;--- restore XBDA to above low mem myint19 proc push ds push 40h pop ds mov es,ds:[XBDAADR] mov di,ds:[MEMSIZE] shl di,6 ;convert KB to PARA mov cl,es:[XBDA._size] mov ch,0 sub ds:[MEMSIZE],cx push cx shl cx,6 ;convert KB to PARAs sub di,cx mov ds:[XBDAADR],di push es pop ds mov es,di pop cx xor di,di xor si,si shl cx,9 ;convert KB to WORDs rep movsw mov ds,cx ;ds=0000 .386 mov eax, cs:[oldint19] mov ds:[19h*4],eax pop ds push eax .286 retf myint19 endp copyandexit: rep movsw pop es pop ds popa retf endres equ $ devintfirst proc far pusha push ds push es lds bx,cs:[cmdptr] mov al,[bx.IODAT.cmd] mov word ptr [bx.IODAT.status],100h cmp al,00 ;init call? jnz exit if ?WELCOME push ds push cs pop ds mov dx,offset dWelcome mov ah,9 int 21h pop ds endif mov word ptr [bx].IODAT.trans+0,0000 mov word ptr [bx].IODAT.trans+2,cs mov cs:[intproc], offset devint ;--- scan for option /A xor bp,bp lds si,dword ptr [bx].IODAT.count @@: lodsb cmp al,' ' jnz @B @@: lodsb cmp al,' ' jz @B mov ah,al lodsb or al,20h cmp ax,"/a" ;/A option given? jnz @F inc bp ;set flag for option /A @@: push 40h pop es mov ax,es:[XBDAADR] ;check if XBDA is just above low memory mov dx,es:[MEMSIZE] shl dx,6 ;convert KB to PARA cmp ax,dx ;is xbda just above conv memory? jnz err1 ;if no, display msg and do nothing mov ds,ax mov al,ds:[XBDA._size] mov ah,0 ;--- get new XBDA address mov cx,offset endres add cx,15 and cl,0F0h shr cx,4 mov dx,cs add dx,cx cmp bp,0 jz @F add dx,3Fh and dx,0FFC0h @@: ;--- adjust BIOS variables 040Eh and 0413h mov es:[XBDAADR],dx add word ptr es:[MEMSIZE],ax pusha ;--- increase the last DOS MCB mov ah,52h int 21h mov si,es:[bx-2] nextitem: mov es, si mov dx, es:[MCB._size] ;get size of MCB inc dx add dx, si ;dx = next block cmp byte ptr es:[MCB.sig], 'M' jnz @F mov si, dx jmp nextitem @@: popa mov cx,ax shl cx,6 ;convert kB to para add es:[MCB._size],cx push ds ;--- tell DOS the amount of driver's memory to keep resident mov di,ax mov si,dx shl ax,10 ;convert kb to byte mov cx,cs sub dx,cx shl dx,4 add dx,ax lds bx,cs:[cmdptr] mov word ptr [bx].IODAT.trans+0,dx ;--- hook int 19h push 0 pop ds .386 push cs push offset myint19 pop eax xchg eax,ds:[19h*4] mov cs:[oldint19],eax .286 pop ds mov es,si mov cx,di ;--- move contents of XBDA ;--- cx=size in kB ;--- ds=old XBDA ;--- es=new XBDA xor si,si xor di,di shl cx,9 ;convert KB to WORD jmp copyandexit exit: pop es pop ds popa ret err1: mov dx,offset derr1 jmp @F err2: mov dx,offset derr2 @@: push cs pop ds mov ah,9 int 21h jmp exit devintfirst endp derr1 db "XBDA not at the end of low memory",13,10,'$' derr2 db "XBDA too large",13,10,'$' dWelcome db "MOVEXBDA starting",13,10,'$' str1 db "MOVEXBDA moves XBDA to low memory. Must be run before UMBs were provided to DOS.",13,10 db "Usage: DEVICE=MOVEXBDA.EXE [/A]",13,10 db "Optional parameter /A will align the XBDA to a kB boundary, which is",13,10 db "required by some BIOSes.",13,10 db '$' main: mov dx,offset str1 push cs pop ds mov ah,9 int 21h mov ax,4C00h int 21h _TEXT ends STACK segment stack 'STACK' db 400h dup (?) STACK ends END main ================================================ FILE: Tools/UMBM/MAKE.BAT ================================================ @echo off rem BAT to create UMBM.EXE with JWasm jwasm -mz -nologo -Fl -Sg UMBM.ASM ================================================ FILE: Tools/UMBM/UMBM.ASM ================================================ ;*** UMBM is a DOS device driver, but will not install permanently: ;*** if UMBs activated by UMBPCI are found, UMBM will check if ;*** a XMS host is present. If yes, it will hook into the XMS chain ;*** and add support for UMBs there. If no, it will install a micro ;*** XMS host which just provides UMBs. ;*** During the boot process, if line DOS=UMB is present in CONFIG.SYS, ;** DOS will query for UMBs every time a driver has been loaded. If UMBs ;*** are provided, DOS will grab them. So UMBM's lifetime usually should be ;*** very short. Once the UMBs are grabbed, UMBM will remove itself from ;*** the XMS/Int 2Fh chain. ;*** To not use any DOS memory, UMBM copies itself into the first UMB. ;*** This assumes that DOS first allocates all available UMBs *before* ;*** "using" them. All DOSes I know do so, but AFAIK this is not documented ;*** and therefore a small risk remains. ?MOVEINUMB equ 1 ;move in 1. umb ?MOVEXBDA equ 1 ;/XBDA option .286 .MODEL SMALL DGROUP group _TEXT .STACK .dosseg .386 cr equ 13 lf equ 10 ;*** macros and structures *** IODAT struc cmdlen db ? ;+ 0 size of structure unit db ? ;+ 1 cmd db ? ;+ 2 status dw ? ;+ 3 db 8 dup (?); reserved media db ? ;+ 0d trans dd ? ;+ 0e count dw ? ;+ 12 on init:offset parameter line start dw ? ;+ 14 on init:segment parameter line drive db ? ;+ 16 IODAT ends ;--- macro to define a string CStr macro text:vararg local xxxx .const xxxx db text,0 .code exitm endm ;--- display a character on stdout @putchr macro char mov dl,char mov ah,2 int 21h endm ;--- display a 16-bit word on stdout @wordout macro arg ifdif , mov ax,arg endif call _wordout endm .CODE assume ds:DGROUP dw 0ffffh dw 0ffffh dw 8000h ;attribute dw offset devstrat ;device strategy dw offset devint ;device interrupt devname db 'UMBXXXX0' ;device name befptr dd 1 dup (?) memtab label word dw 3 dup (0) ;max. 4 regions (segm,curr size,init size) dw 3 dup (0) dw 3 dup (0) dw 3 dup (0) dw 0000 umbrou proc far ;XMS hook routine jmp umbr1 ;must have this format nop nop nop umbr1: cmp ah,10h ;request umb? jz umb1 cmp ah,11h ;release umb? jz umb2 db 0EAh oldvec dd 0 ;chain to previous handler dummyxmm:: xor ax,ax mov bl,80h retf umb1: ;request umb mov bx,offset memtab mov ax,0000 umb1b: ;<==== check next block cmp dx,cs:[bx+2] ;request block (DX=size in paras) ja short umb1a ;jmp if block too small cmp word ptr cs:[bx+2],0000 jz umb1a3 mov ax,cs:[bx+4] ;original size sub ax,cs:[bx+2] ;subtract rest size add ax,cs:[bx+0] ;+ start address => new start address sub cs:[bx+2],dx ;size of block in DX if ?MOVEINUMB jnz umb1c call deakt umb1c: endif mov bx,ax ;segment address of block mov ax,1 ;no error! ret umb1a: cmp ax,cs:[bx+2] ;remember largest block in AX jnb @F mov ax,cs:[bx+2] @@: add bx,6 cmp word ptr cs:[bx],0 jnz umb1b mov bl,0b0h ;error: smaller UMB available mov dx,ax ;still free space and dx,dx jnz umb1a2 umb1a3: mov bl,0b1h ;error: no UMB free umb1a2: mov ax,0 ;error ret umb2: ;release umb mov bx,offset memtab umb2b: cmp dx,cs:[bx+0] jc umb2a1 mov ax,cs:[bx+4] add ax,cs:[bx+0] cmp dx,ax jnc umb2a sub ax,dx mov cs:[bx+2],ax mov ax,1 ret umb2a: add bx,6 cmp word ptr cs:[bx],0 jnz umb2b umb2a1: mov bl,0b2h ;error: invalid UMB segment address mov ax,0 ret umbrou endp deakt: push ds ;restore xms vector lds bx,cs:[oldvec] ;thus no space is needed at all push eax mov eax,dword ptr cs:[umbrou] mov [bx-5],eax mov al,byte ptr cs:[umbrou+4] mov [bx-1],al mov ax,word ptr cs:[oldint2f+2] and ax,ax jz @F push dx mov dx,word ptr cs:[oldint2f+0] mov ds,ax mov ax,252fh int 21h pop dx @@: pop eax pop ds ret myint2f:: cmp ax,4300h jz int4300 cmp ax,4310h jz int4310 db 0eah ;jmp far16 [oldint2f] oldint2f dd 0 int4300: or al,80h iret int4310: mov bx,offset umbrou push cs pop es iret endres equ $ ;*** end resident part *** devstrat proc far mov word ptr cs:[befptr+0],bx mov word ptr cs:[befptr+2],es ret devstrat endp devint proc far pusha push ds push es lds bx,cs:[befptr] mov word ptr [bx.IODAT.status],100h mov al,[bx.IODAT.cmd] cmp al,00 ;init? jnz devi1 push bx push ds call ignname ;skip device driver name call main ;init pop ds pop bx mov word ptr [bx.IODAT.trans+0],0000 mov word ptr [bx.IODAT.trans+2],ax jmp devi2 devi1: devi2: pop es pop ds popa ret devint endp ignname proc near ;skip device driver name mov es,[bx.IODAT.start] mov si,[bx.IODAT.count] dec si ignn1: inc si cmp byte ptr es:[si],' ' jz ignn1 dec si ignn2: inc si cmp byte ptr es:[si],' ' jnz ignn2 ret ignname endp ;--- display a nul-terminated string ;--- modifies ax _strout proc stdcall uses dx si pStr:word mov si,pStr nextitem: lodsb and al,al jz done mov dl,al mov ah,2 int 21h jmp nextitem done: ret _strout endp ;--- display value in AX in hex ;--- preserves all registers except AX _wordout proc push ax mov al,ah call byteout pop ax byteout: push ax shr al,4 call nibout pop ax nibout: and al,0Fh cmp al,10 sbb al,69H das push dx mov dl,al mov ah,2 int 21h pop dx ret _wordout endp ;--- es:si = cmdline getpar proc nextitem: mov al,es:[si] cmp al,cr jz exit cmp al,lf jz exit cmp al,20h jz getpar_1 or al,20h call getregion jc exit jmp nextitem getpar_1: inc si cmp byte ptr es:[si],' ' jz getpar_1 jmp nextitem exit: ret getpar endp makecaps proc cmp al,'a' jb iscaps cmp al,'z' ja iscaps and al,not 20h iscaps: ret makecaps endp ishex proc cmp al,'0' jb @@no cmp al,'9' jbe @@yes cmp al,'A' jb @@no cmp al,'F' jbe @@yes2 @@no: stc ret @@yes2: sub al,7 @@yes: sub al,'0' clc ret ishex endp gethexnumber proc push dx mov ch,00 mov dx,0000 gethex2: mov al,es:[si] call makecaps call ishex jc gethex1 inc ch mov ah,00 shl dx,4 add dx,ax inc si jmp gethex2 gethex1: cmp ch,1 ;null digits -> invalid mov ax,dx pop dx ret gethexnumber endp ;--- inp: es:[si] -> cmdline ;--- ds:[di] -> memtab getregion proc uses bx cmp byte ptr es:[si],'/' ;allow the "/I=" prefix jnz @F mov eax,es:[si+1] or eax,20202020h if ?MOVEXBDA cmp eax,'adbx' jz is_xbda endif cmp al,'i' jnz @F cmp byte ptr es:[si+2],'=' jnz @F add si,3 @@: call gethexnumber jc getregion_er cmp al,00h jnz getregion_er mov bx,ax mov al,es:[si] cmp al,'-' jnz getregion_er inc si call gethexnumber jc getregion_er inc ax cmp al,00h jnz getregion_er sub ax,bx jbe getregion_er mov [di+0],bx mov [di+2],ax mov [di+4],ax add di,6 clc ret if ?MOVEXBDA is_xbda: inc bp add si,5 clc ret endif getregion_er: stc ret getregion endp ;--- search for UMBPCI blocks in upper memory ;--- DI->memtab MCB struct sig db ? psp dw ? _size dw ? MCB ends searchumbpciblocks proc mov si,0C800h .while si < 0F000h mov es,si cmp dword ptr es:[0],"BMU$" jnz noblock cmp dword ptr es:[4],"!lbT" jnz noblock mov bx,8 .while word ptr es:[bx] mov ax,es:[bx] ;start of (first) block mov [di+0],ax mov ax,es:[bx+2];size of block in paras mov [di+2],ax mov [di+4],ax add di,3*2 add bx,2*2 .break .if di == offset memtab+4*3*2 .endw .break noblock: add si,100h .endw done: ret searchumbpciblocks endp if ?MOVEXBDA movexbda proc push 40h pop es mov dx,es:[0013h] shl dx,6 ;convert kB to para mov ax,es:[000Eh] cmp ax,dx jnz dontmove mov es,ax movzx ax,byte ptr es:[0] ;size of xbda in kb shl ax,6 ;size in paras cmp ax,memtab+2 ja done mov dx,memtab+0 add memtab+0,ax sub memtab+2,ax sub memtab+4,ax push ds push es pop ds mov es,dx mov cx,ax shl cx,3 xor si,si xor di,di rep movsw mov di,ax push 40h pop es mov es:[000eh],dx mov ax,ds pop ds ;--- increase the last DOS MCB mov ah,52h int 21h mov si,es:[bx-2] nextitem: mov es, si mov dx, es:[MCB._size] ;get size of MCB inc dx add dx, si ;dx = next block cmp byte ptr es:[MCB.sig], 'M' jnz @F mov si, dx jmp nextitem @@: add es:[MCB._size],di ;--- increase bios conv memory size push 40h pop es shr di,6 add es:[0013h],di invoke _strout, CStr("UMBM: XBDA moved to ") @wordout word ptr es:[000Eh] invoke _strout, CStr(cr,lf) done: ret dontmove: invoke _strout, CStr("UMBM: XBDA has already been moved - nothing done",cr,lf) ret movexbda endp endif ;--- called as device driver ;--- check cmdline and get the regions to add as UMBs ;--- test each region if it really contains RAM ;--- es:di = cmdline main proc near push cs pop ds xor bp,bp mov di,offset memtab call getpar jc mainex cmp memtab,0 ;any valid /I= option given? jnz @F call searchumbpciblocks @@: mov bx,offset memtab mov ax,[bx] and ax,ax jz mainex invoke _strout, CStr("UMBM: Upper Memory Blocks: ") mn1: ;<---- @wordout [bx] @putchr '-' mov ax,[bx] add ax,[bx+2] dec ax @wordout ax mov dx,[bx+0] mov cx,[bx+2] ;size in paragraphs shr cx,8 jcxz testdone next4k: push ds pushf cli mov ds,dx mov ax,05555h xor si,si xchg ax,[si] xor word ptr [si],0FFFFh xchg ax,[si] popf pop ds cmp ax,0AAAAh jz @F invoke _strout, CStr(" - no RAM found at ") @wordout dx invoke _strout, CStr(". Aborted!",cr,lf) jmp mainex @@: add dx,100h loop next4k testdone: @putchr ' ' add bx,6 cmp word ptr [bx],0 jnz mn1 invoke _strout, CStr(cr,lf) if ?MOVEXBDA and bp,bp jz @F call movexbda @@: endif ;--- check if an XMM is active mov ax,4300h int 2fh xor bp,bp test al,80h ;does xms host exist? jnz @F mov ax,352Fh int 21h mov word ptr oldint2f+0,bx mov word ptr oldint2f+2,es mov word ptr [oldvec+0],offset dummyxmm if ?MOVEINUMB mov ax,[memtab] else mov ax,cs endif mov word ptr [oldvec+2],ax dec bp jmp main_1 @@: mov ax,4310h ;get XMS call address int 2fh mov ax,es:[bx] cmp ax,03ebh ;should begin with a jmp short $+5 jz @F invoke _strout, CStr("UMBM: cannot hook into XMS chain",cr,lf) jmp mainex @@: cli mov ax,offset umbrou if ?MOVEINUMB mov cx,[memtab] ;hook into XMS chain else mov cx,cs endif mov byte ptr es:[bx+0],0eah ;chain mov es:[bx+1],ax mov es:[bx+3],cx add bx,5 mov word ptr [oldvec+0],bx mov word ptr [oldvec+2],es sti main_1: if ?MOVEINUMB ; invoke _strout, CStr("UMBM: copy myself in 1. UMB",cr,lf) mov es,[memtab] xor di,di xor si,si mov cx,offset endres rep movsb mov ax,cs ;end address else mov bx,offset endres shr bx,4 inc bx mov ax,cs add ax,bx ;num paragraphs endif ;if no XMM found, install XMM and bp,bp jz @F push ds push ax invoke _strout, CStr("UMBM: XMS host not found, installing XMS",cr,lf) mov dx,offset myint2f if ?MOVEINUMB mov ds,[memtab] else push cs pop ds endif mov ax,252fh int 21h pop ax pop ds @@: ret mainex: mov ax,cs ret main endp explain proc push cs pop ds mov dx, offset dHowTo mov ah, 9 int 21h ret explain endp ;*** entry if loaded from command line main_exe proc c call explain mov ax,4c00h int 21h main_exe endp dHowTo label byte db "UMBM is assumed to be located behind UMBPCI in CONFIG.SYS,",cr,lf db "and before the XMS driver. This will allow to load the XMS driver",cr,lf db "(and the EMM) into an UMB, thus saving some conventional DOS memory.",cr,lf db cr,lf db "UMBM knows the following options:",cr,lf db " /I=XXXX-YYYY force region XXXX-YYYY to be included. Example: /I=D000-DBFF.",cr,lf db " /XBDA move the XBDA to the first UMB",cr,lf db "Option /I is not recommended, since UMBM will automatically find regions",cr,lf db "activated by UMBPCI. Option /XBDA may cause troubles if the upper memory",cr,lf db "can't be used by DMA. To find out, there's no other way than to try.",cr,lf db "Example:",cr,lf db "DOS=UMB",cr,lf db "DEVICE=UMBPCI.SYS",cr,lf db "DEVICE=UMBM.EXE",cr,lf db "DEVICEHIGH=HIMEMX.EXE",cr,lf db "After DOS has grabbed the UMBs, UMBM will remove itself from DOS memory.",cr,lf db cr,lf db "UMBM is Public Domain. Japheth.",cr,lf db '$' END main_exe ================================================ FILE: Tools/VCPI/MAKE.BAT ================================================ @echo off rem creates VCPI.EXE with JWasm jwasm -mz -nologo -Fl -Sg VCPI.ASM ================================================ FILE: Tools/VCPI/PRINTF.INC ================================================ ;--- ltob(long n, char * s, int base); ;--- convert long to string ltob PROC stdcall uses edi edx number:dword, outb:ptr, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP ;--- hiword(eax) is modified if numbers are rendered! printf PROC c fmt:ptr, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte pusha lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: popa ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx .while byte ptr [si] >= '0' && byte ptr [si] <= '9' lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax .endw mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u cmp al,0 jnz @@L359 pop ax jmp done handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn handle_char: cmp al,lf jnz @F mov al,cr call @F mov al,lf @@: mov dx, cs cmp dx, CSR0 jnz @F call VioPutChar retn @@: mov dl, al mov ah, 2 int 21h retn printf ENDP ================================================ FILE: Tools/VCPI/VCPI.ASM ================================================ ;*** display VCPI information ;*** and use VCPI to switch to protected mode and back ;--- if segments are defined BEFORE .model, ;--- the alignment can be set differently. _DATA segment para public 'DATA' _DATA ends _BSS segment para public 'BSS' _BSS ends .286 .model small .stack 2048 .dosseg .386P option casemap:none ?ENTRIES equ 110h ;PTE entries to display with option -p ?386SWAT equ 0 ;1=support 386SWAT interface (doesn't work!) ?DEB386 equ 1 ;1=support (w)deb386 interface ?DISPEXC equ 0 ;display exceptions in ring 0 include vcpi.inc CStr macro text:vararg local sym .const sym db text,0 .code exitm endm if ?DEB386 D386_Identify equ 43h ; returns debugger identification D386_Prepare_PMode equ 44h ; partially prepare for protected mode operation D386_Real_Mode_Init equ 45h ; tell kd we're done PMINIT_INIT_IDT equ 0 ; (ES:EDI) = pointer to PM IDT D386_Id equ 0F386h ; debugger identification code endif lf equ 10 cr equ 13 .data mygdt label byte ;GDT db 1*8 dup (0) ;+00h null descriptor restab db 3*8 dup (0) ;+08h descriptors reserved for vcpi host pmcs desc <0ffffh,0,0,9Ah,0,0> ;+20h pmds desc <0ffffh,0,0,92h,0,0> ;+28h pmtr desc <068h-1,0,0,89h,0,0> ;selector for TR ;+38h pmflat desc <0ffffh,0,0,92h,8fh,0> ;+30h if ?386SWAT or ?DEB386 kddesc label dword ;+48 if ?386SWAT db 30*8 dup (0) ; 386SWAT needs 30 descriptors free!? else db 3*8 dup (0) endif KDSEL equ kddesc - mygdt endif SIZGDT equ $ - mygdt CSR0 equ pmcs - mygdt DSR0 equ pmds - mygdt TRSEL equ pmtr - mygdt FLATSEL equ pmflat - mygdt HOSTCS equ restab - mygdt pdgdt label fword dw SIZGDT-1 ;limit GDTR basegdt dd offset mygdt ;base GDTR pdidt label fword dw sizeof myidt-1 ;limit IDTR baseidt dd offset myidt ;base IDTR ;--- far32 address of VCPI protected-mode API vcpiv label fword vcpiofs dd 0 dw HOSTCS ;selector for VCPI host code segment dw 0 msw V86toPM <0, offset pdgdt, offset pdidt, 0, TRSEL, offset pmentry, CSR0> dwPage dd 0 ;page to release / allocated dwFreePages dd 0 ;number free pages dwNumPages dd 1 ;no of pages to alloc / free dwAlloced dd 0 ;pages allocated dwFreed dd 0 ;pages freed handle dw 0 ;handle EMS segcs dw 0 ;segment CS segds dw 0 ;segment DS ptadr dw 0 ;segment page table 0 vcpiend dw 0 ;offset in pagetab 0 where free space begins flags db 00 UFLAG equ 1 ;option u - display PTEs for UMBs NFLAG equ 2 ;option n - no switch to pmode QFLAG equ 4 ;option q - query pages in protected-mode PFLAG equ 8 ;option p - display PTEs for conv. memory FFLAG equ 16 ;option f - free pages in pmode AFLAG equ 32 ;option a - alloc pages in pmode RFLAG equ 64 ;option r - free pages in rmode bRet db 00 bNoDisp db 00 if ?386SWAT or ?DEB386 KD_386SWAT equ 1 KD_DEB386 equ 2 bKrnlDbg db 0 dfDbgEntry df 0 endif .data? myidt db 100h*8 dup (?) ptab db 1000h dup (?) taskseg TSSSEG <> ;task state segment drtab dd 8 dup (?) .code include vioout.inc include printf.inc println proc invoke printf, CStr(10) ret println endp if ?DISPEXC ;------ FEDCBA9876543210 FEDCBA9876543210 excv dw 0111110100000000b, 0000000000000110b ?EXC = 0 @defexc macro push ?EXC jmp defexc ?EXC = ?EXC + 1 endm exceptions: rept 32 @defexc endm EXCFRAME1 struct dwEbp dd ? wExc dw ?,? dwErr dd ? dwEip dd ? wCs dw ?,? dwEfl dd ? EXCFRAME1 ends EXCFRAME2 struct dwEbp dd ? wExc dw ?,? dwEip dd ? wCs dw ?,? dwEfl dd ? EXCFRAME2 ends defexc: push ebp mov ebp,esp mov ax, [ebp].EXCFRAME1.wExc bt cs:[excv], ax jnc @F invoke printf, CStr("exc %X at %X:%lX, errc=%lX",lf), [ebp].EXCFRAME1.wExc, [ebp].EXCFRAME1.wCs, [ebp].EXCFRAME1.dwEip, [ebp].EXCFRAME1.dwErr jmp de2 @@: invoke printf, CStr("exc %X at %X:%lX",lf), [ebp].EXCFRAME2.wExc, [ebp].EXCFRAME2.wCs, [ebp].EXCFRAME2.dwEip de2: jmp $ ; just stop, we cannot continue endif defint proc near push eax mov al,0Bh out 0A0h,al in al,0A0h and al,al jz @F mov al,20h out 0a0h,al @@: mov al,0Bh out 20h,al in al,20h and al,al jz @F mov al,20h out 20h,al @@: pop eax iretd defint endp ;--- get descriptor values in EAX (base), DX (limit), CX (attr) getdesc proc near mov ah,[di+7] ;base bits 31..24 mov al,[di+4] ;base bits 23..16 shl eax,16 mov ax,[di+2] ;base bits 15..0 mov dx,[di+0] ;limit bits 15..0 mov cx,[di+5] ret getdesc endp gethex proc xor edx, edx mov ch,0 nextitem: mov al,es:[si] cmp al,'0' jb done cmp al,'9' jbe @F or al,20h cmp al,'a' jb done cmp al,'f' ja done sub al,27h @@: sub al,'0' movzx eax,al shl edx, 4 add edx, eax inc ch inc si jmp nextitem done: mov eax, edx cmp ch,1 ret gethex endp getdec proc xor edx, edx mov ch,0 nextitem: mov al,es:[si] cmp al,'0' jb done cmp al,'9' ja done sub al,'0' movzx eax,al shl edx, 1 lea edx, [edx+edx*4] add edx, eax inc ch inc si jmp nextitem done: mov eax, edx cmp ch,1 ret getdec endp EMScheck proc push ds push si push di mov ax,3567h int 21h push ds pop es mov di,000ah push cs pop ds mov si,offset emsstr mov cx,8 repz cmpsb mov ax,1 jz @F ;1 = EMS exists dec ax ;0 = EMS doesn't exist @@: pop di pop si pop ds ret EMScheck endp emsstr db 'EMMXXXX0' ;--- display UMBs DispUMBs proc uses es si nop ;MASM bug invoke printf, CStr("Pages used for UMBs:",lf) mov es,[ptadr] mov si,0000 mov cx,100h xor edx,edx nextitem: push cx push edx mov eax,es:[si] and ax,0F000h cmp eax,edx jz @F mov eax, edx shr eax,4 invoke printf,CStr("%04X "), ax @@: pop edx add edx,1000h add si,4 pop cx loop nextitem invoke println ret DispUMBs endp ;--- display PTEs of 1. MB DispPTEs proc uses es si nop ;MASM bug invoke printf, CStr("Paging Table",lf) mov es,[ptadr] mov si,0000 mov cx,?ENTRIES nextitem: push cx test si,01Fh jnz @F mov ax,si shr ax,2 invoke printf, CStr("%04X: "), ax @@: mov eax,es:[si+0] push ax invoke printf,CStr("%8lX "),eax pop ax and ax,0FFE7h ;??? mov es:[si+0],ax ;??? add si,4 test si,1Fh jnz @F invoke println @@: pop cx if 1 cmp si, vcpiend jb nextitem else loop nextitem endif ret DispPTEs endp ;--- fill VCPI comm structure ;--- set descriptors and IDT setdescriptors proc mov ax,cs movzx eax,ax shl eax, 4 mov pmcs.A0015,ax shr eax,16 mov pmcs.A1623,al mov ax,ds movzx eax,ax shl eax, 4 add basegdt, eax add baseidt, eax add msw._Gdtr, eax add msw._Idtr, eax mov pmds.A0015,ax shr eax,16 mov pmds.A1623,al mov ax,ds movzx eax,ax shl eax, 4 add eax, offset taskseg mov pmtr.A0015,ax shr eax,16 mov pmtr.A1623,al mov dword ptr taskseg.dfStk0, esp mov word ptr taskseg.dfStk0+4, DSR0 mov bx,0 mov di,offset myidt if ?DISPEXC mov ax,offset exceptions nextitem: mov word ptr [di+0],ax mov word ptr [di+2],CSR0 mov word ptr [di+4],0EE00h mov word ptr [di+6],0 add di,8 add ax,4 inc bl cmp bl,20h jnz nextitem endif nextitem2: mov word ptr [di+0],offset defint mov word ptr [di+2],CSR0 mov word ptr [di+4],0EE00h mov word ptr [di+6],0 add di,8 inc bl jnz nextitem2 ret setdescriptors endp ;--- switch to protected-mode and back to v86-mode throu VCPI pmgo proc if ?386SWAT cmp bKrnlDbg, KD_386SWAT jnz no386swat2 push ds pop es mov di, offset kddesc mov bx, KDSEL mov ax, 0DEF2h int 67h cmp ah,0 jnz no386swat2 mov dword ptr [dfDbgEntry+0], edx mov word ptr [dfDbgEntry+4], bx mov cx, 10h mov bx, 0 mov di, offset myidt @@: mov ax, 0DEF3h int 67h inc bx add di, 8 loop @B no386swat2: endif cli mov [segcs],cs mov [segds],ds mov ax, ds movzx eax, ax shl eax, 4 lea esi, [eax + offset msw] movzx ebx,sp mov ax,0DE0Ch int 67h pmentry:: ;now in protected mode mov ax,DSR0 mov ss,ax mov esp,ebx mov ds,ax mov ax,FLATSEL ;4G selector -> ES mov es,ax if ?386SWAT cmp bKrnlDbg,KD_386SWAT jnz @F int 3 @@: endif if ?DEB386 cmp bKrnlDbg,KD_DEB386 jnz @F push ds pop es mov edi, offset myidt ;es:edi=idt mov al, PMINIT_INIT_IDT call [dfDbgEntry] int 3 @@: endif test flags, QFLAG jz @F call pmquery @@: test flags, AFLAG jz @F call pmalloc @@: test flags, FFLAG jz @F call pmfree @@: ;--- jump back to v86 sub sp, sizeof IRETV86 mov bp, sp xor eax,eax mov [bp].IRETV86._Eip, offset rmentr mov ax, [segcs] mov [bp].IRETV86._Cs, eax mov [bp].IRETV86._Efl, 2 mov [bp].IRETV86._Esp, ebx mov ax, [segds] mov [bp].IRETV86._Ss, eax mov [bp].IRETV86._Es, eax mov [bp].IRETV86._Ds, eax xor ax, ax mov [bp].IRETV86._Fs, eax mov [bp].IRETV86._Gs, eax clts ;clear task switched flag mov ax, FLATSEL mov ds,ax ;DS must be FLAT mov ax,0DE0Ch call fword ptr ss:[vcpiv] rmentr: ;back in v86-mode sti if ?DEB386 cmp bKrnlDbg,KD_DEB386 jnz @F mov ah, D386_Real_Mode_Init int 68h @@: endif ret pmgo endp protocol proc .if (flags & AFLAG) invoke printf, CStr("alloced %lu"), dwAlloced movzx ax,bRet invoke printf, CStr(" page(s) in pm, last status ah=%02X"),ax .if (dwAlloced) invoke printf, CStr(", (first) page=%08lX"), dwPage .endif invoke println .elseif (flags & FFLAG) invoke printf, CStr("freed %lu"), dwFreed movzx ax,bRet invoke printf, CStr(" page(s) in pm, last status ah=%02X"),ax .if (dwFreed) invoke printf, CStr(", (last) page released=%08lX"), dwPage .endif invoke println .elseif (flags & RFLAG) invoke printf, CStr("freed %lu"), dwFreed movzx ax,bRet invoke printf, CStr(" page(s) in rm, last status ah=%02X"),ax .if (dwFreed) invoke printf, CStr(", (last) page released=%08lX"), dwPage .endif invoke println .elseif (flags & QFLAG) movzx ax,bRet invoke printf, CStr("%lu free pages in pm, status ah=%02X",lf), dwFreePages, ax .elseif (flags & (UFLAG or PFLAG)) .elseif (!(flags & NFLAG)) invoke printf, CStr('Protected Mode Switch ok',lf) .endif ret protocol endp ;--- query free VCPI page(s) using protected-mode API pmquery proc pushad mov ax,0DE03h call fword ptr ss:[vcpiv] mov bRet, ah mov dwFreePages, edx popad ret pmquery endp ;--- allocate VCPI page(s) using protected-mode API pmalloc proc pushad mov ecx, dwNumPages nextitem: mov ax,0DE04h call fword ptr ss:[vcpiv] mov bRet, ah and ah,ah jnz allocerr inc dwAlloced cmp ecx,dwNumPages jnz @F mov dwPage, edx @@: dec ecx jnz nextitem allocerr: popad ret pmalloc endp ;--- release VCPI page(s) with protected-mode VCPI API pmfree proc mov edx, dwPage and edx, edx jz exit pushad mov ecx, dwNumPages nextitem: mov ax,0DE05h call fword ptr ss:[vcpiv] and ah,ah jnz done mov dwPage, edx inc dwFreed add edx,1000h dec ecx jnz nextitem done: mov bRet, ah popad exit: ret pmfree endp ;--- free VCPI pages with real-mode VCPI API rmfree proc mov edx, dwPage and edx, edx jz exit mov ecx, dwNumPages nextitem: mov ax,0DE05h int 67h and ah,ah jnz exit inc dwFreed mov dwPage, edx add edx,1000h dec ecx jnz nextitem exit: mov bRet, ah ret rmfree endp ;--- VCPI host was detected, ;--- now call vcpi functions runvcpi proc near mov cl,2 @@: mov ax,0DE00h ;is vcpi supported? int 67h cmp ah,00 jz pvcpi1 push cx call EMScheck pop cx and ax,ax jz pvcpi1x mov ax,4300h mov bx,0001h ;get EMS page to ensure EMM is ON int 67h and ah,ah jnz pvcpi1x mov [handle],dx dec cl jnz @B pvcpi1x: invoke printf, CStr("no VCPI host found",lf) jmp pvcpiex pvcpi1: cmp bNoDisp, 0 jnz @F movzx ax,bh movzx bx,bl invoke printf, CStr("VCPI version: %u.%u",lf),ax,bx @@: call setdescriptors push ds pop es if ?386SWAT ;--- the proper sequence of API calls for 386SWAT are ;--- DEF0, DE01 (VCPI get pm interface), DEF2, DEF3 mov ax,0DEF0h ; returns debugger version in BX int 67h cmp ah,0 jnz @F mov bKrnlDbg, KD_386SWAT jmp kd_done @@: endif if ?DEB386 mov ah,D386_Identify int 68h cmp ax, D386_Id jnz kd_done mov bKrnlDbg, KD_DEB386 mov bx, FLATSEL mov cx, KDSEL mov dx, 0 ; no GDT sel mov si, offset mygdt ; ds:si=gdt mov di, offset myidt ; es:di=idt mov ah, D386_Prepare_PMode int 68h mov dword ptr [dfDbgEntry+0], edi mov word ptr [dfDbgEntry+4], es push ds pop es endif kd_done: mov es,[ptadr] xor di,di ;ES:DI -> page table 0 mov si,offset restab;DS:SI -> 3 free GDT descriptors mov ax,0DE01h ;get protected mode interface int 67h cmp ah,00 jnz de01done mov [vcpiofs],ebx mov vcpiend,di cmp bNoDisp, 0 jnz de01done invoke printf, CStr("Offset of VCPI entry in protected-mode: %08lX",lf),ebx movzx edi,di shl edi, 10 invoke printf, CStr("Start free address space: %08lX",lf), edi mov di,offset restab call getdesc invoke printf, CStr("1. VCPI descriptor: %08lX:%04X,%04X",lf), eax, dx, cx add di,8 call getdesc invoke printf, CStr("2. VCPI descriptor: %08lX:%04X,%04X",lf), eax, dx, cx add di,8 call getdesc invoke printf, CStr("3. VCPI descriptor: %08lX:%04X,%04X",lf), eax, dx, cx de01done: cmp bNoDisp, 0 jnz noprot mov ax,0DE02h ;maxAddr 4K Page int 67h invoke printf, CStr("highest physical memory address: %08lX",lf),edx mov ax,0DE03h ;num free 4K Pages int 67h cmp ah,0 jz @F movzx ax,ah invoke printf, CStr('int 67h, ax=DE03h failed, status AH=%02X',lf),ax jmp de03done @@: mov eax,edx shl eax,2 invoke printf, CStr('Free 4K pages: %lu ( %lu kB)',lf), edx, eax de03done: mov ax,0DE07h ;get CR0 int 67h cmp ah,0 jnz de07done invoke printf, CStr("CR0: %08lX",lf),ebx de07done: mov di,offset drtab push ds pop es mov cx,8 mov eax,-1 mov dx,di rep stosd mov di,dx mov ax,0DE08h ;get DRx int 67h cmp ah,0 jnz de08done invoke printf, CStr("DR0-DR3: %08lX %08lX %08lX %08lX",lf), dword ptr es:[di+0], dword ptr es:[di+4], dword ptr es:[di+8], dword ptr es:[di+12] invoke printf, CStr("DR6+DR7: %08lX %08lX",lf), dword ptr es:[di+24], dword ptr es:[di+28] de08done: mov ax,0DE0Ah ;get interrupt vector mappings int 67h cmp ah,0 jnz de0adone invoke printf, CStr("Master/Slave PIC base: %X/%X",lf), bx, cx de0adone: noprot: test flags,PFLAG jz @F call DispPTEs @@: test flags,UFLAG jz @F call DispUMBs @@: test flags,PFLAG or RFLAG or NFLAG or UFLAG jnz @F call pmgo ;--- call protected mode @@: test flags,RFLAG jz @F call rmfree @@: call protocol pvcpiex: mov dx,[handle] and dx,dx jz pvcpiex1 mov ax,4500h ;release the EMS page again int 67h pvcpiex1: ret runvcpi endp ;--- get cmdline parameters getparm proc near mov si,0080h mov bl,byte ptr es:[si] inc si mov bh,0 mov byte ptr es:[si+bx],0 getp21: mov al,es:[si] inc si and al,al jz getp1 cmp al,'/' jz isoption cmp al,'-' jz isoption cmp al,' ' jbe getp21 jmp parerr getp1: clc ret isoption: mov al,es:[si] and al,al jz parerr inc si or al,20h mov ah,al cmp al,'r' jnz @F or [flags],RFLAG jmp get_hex @@: cmp al,'f' jnz @F or [flags],FFLAG jmp get_hex @@: cmp al,'p' jnz @F or [flags],PFLAG jmp getp21 @@: cmp al,'q' jnz @F or [flags],QFLAG jmp getp21 @@: cmp al,'n' jnz @F or [flags],NFLAG jmp getp21 @@: cmp al,'u' jnz @F or [flags],UFLAG jmp getp21 @@: cmp al,'a' jnz parerr or [flags],AFLAG call skipws cmp al,'0' jb getp21 jmp get_dec ;--- get a decimal number get_dec: call skipws jz parerr call getdec jc parerr and eax, eax jz parerr mov dwNumPages, eax jmp getp21 ;--- get a hex number, then a decimal number get_hex: call skipws jz parerr call gethex jc parerr mov dwPage, eax call skipws jz getp21 cmp al,',' jnz getp21 inc si call skipws jz parerr jmp get_dec parerr: stc ret skipws: mov al,es:[si] and al,al jz @F inc si cmp al,20h jbe skipws dec si @@: ret getparm endp main proc c movzx esp,sp call getparm jnc main_1 invoke printf, CStr('usage: vcpi [ options ]',lf) invoke printf, CStr(' -a : alloc <1|nn> page(s) in protected mode',lf) invoke printf, CStr(' -f page,: free <1|nn contiguous> page(s) in protected mode',lf) invoke printf, CStr(' -n: dont try to switch to protected mode (disables -q -f -a)',lf) invoke printf, CStr(' -p: display PTEs for conventional memory',lf) invoke printf, CStr(' -q: query num pages in protected mode',lf) invoke printf, CStr(' -r page,: free <1|nn contiguous> page(s) in real-mode',lf) invoke printf, CStr(' -u: display regions mapped as UMBs',lf) jmp exit main_1: mov al,flags and al,AFLAG or FFLAG or RFLAG or UFLAG mov bNoDisp,al mov di, offset ptab mov cx, (sizeof ptab) / 4 push ds pop es xor eax,eax rep stosd mov ax,3567h int 21h mov ax,es or ax,bx jnz main3 invoke printf, CStr("int 67h is zero (no EMM)",lf) mov al,01 jmp exit main3: ;get memory for page tables mov bx,400h mov ah,48h int 21h jc exit mov bx,ax movzx eax,ax shl eax, 4 add eax,4096-1 ;page directory must be page aligned and ax,0f000h ;clear bits 0-11 mov msw._CR3,eax mov edi,eax add edi,1000h shr eax,4 mov es,ax ;page directory 1. entry or di,1+2+4 ;set present, r/w, user mov es:[0000],edi ;set PDE for 0-3FFFFFh mov ax,es add ax,100h mov [ptadr],ax call runvcpi mov al,00 exit: ret main endp start: mov ax,@data mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx ;make SS=DS mov ax,ds mov cx,cs sub ax,cx add ax,10h shr bx,4 add bx,ax mov ah,4ah int 21h push es push ds pop es mov di, offset myidt mov cx, sp sub cx, di shr cx, 1 xor ax, ax rep stosw pop es call main mov ax,4c00h int 21h END start ================================================ FILE: Tools/VCPI/VCPI.INC ================================================ desc struct limit dw ? A0015 dw ? A1623 db ? attrib db ? lim_gr db ? A2431 db ? desc ends ;--- VCPI structure for switch to protected mode V86toPM struct _CR3 dd ? ;CR3 _Gdtr dd ? ;linear address PD for GDTR _Idtr dd ? ;linear address PD for IDTR _Ldtr dw ? ;LDTR _Tr dw ? ;TR _Eip dd ? ;EIP _Cs dd ? ;CS V86toPM ends IRETV86 struct _Eip dd ? _Cs dd ? _Efl dd ? _Esp dd ? _Ss dd ? _Es dd ? _Ds dd ? _Fs dd ? _Gs dd ? IRETV86 ends TSSSEG struct dwLink dd ? dfStk0 df ? ;+04 dw ? dfStk1 df ? ;+0C dw ? dfStk2 df ? ;+14 dw ? _CR3 dd ? ;+1C _Eip dd ? ;+20 _Efl dd ? ;+24 _Eax dd ? ;+28 _Ecx dd ? ;+2C _Edx dd ? ;+30 _Ebx dd ? ;+34 _Esp dd ? ;+38 _Ebp dd ? ;+3C _Esi dd ? ;+40 _Edi dd ? ;+44 _ES dd ? ;+48 _CS dd ? ;+4C _SS dd ? ;+50 _DS dd ? ;+54 _FS dd ? ;+58 _GS dd ? ;+5C _LDT dd ? ;+60 wFlags dw ? ;+64 wOffs dw ? ;+66 TSSSEG ends ================================================ FILE: Tools/VCPI/VIOOUT.INC ================================================ ;--- vio output for 16-bit protected-mode @getcursorpos macro movzx ebx, byte ptr ds:[462h];page mov bx, ds:[EBX*2+450h] endm @setcursorpos macro movzx ebx, byte ptr ds:[462h];page mov ds:[EBX*2+450h], ax endm ;--- print a char ;--- no registers modified VioPutChar proc local wCols:word local bChar:byte local bRows:byte push ds pushad mov bChar, al mov dx, FLATSEL mov ds, dx mov ch, ds:[0484h] ; rows-1 mov cl, ds:[044Ah] ; cols mov bRows, ch @getcursorpos ; bh=row, bl=col mov al, bh ;row pos mov ch, 0 mov wCols, cx mul cl add ax, ax mov bh, 00 ; bx=col pos add bx, bx add bx, ax mov si, ds:[044Eh] ; page offset cmp word ptr ds:[0463H],3B4h jz @F add si, 8000h @@: movzx esi, si add esi, 0B0000h mov al, bChar cmp al, cr jnz @F mov ax, bx shr ax, 1 div cl mov al, ah xor ah, ah add ax, ax sub bx, ax jmp char_done @@: cmp al, lf jnz @F add bx, cx add bx, cx jmp char_done @@: movzx ebx, bx mov ds:[ebx+esi], al inc bx inc bx char_done: mov al, bRows inc al mul cl add ax, ax cmp bx, ax jc @F call scrollup mov bx, ax @@: mov ax, bx push ax mov cx, wCols shr ax, 1 div cl xchg al, ah @setcursorpos pop ax call cursorset popad pop ds ret cursorset: add ax, ds:[044EH] ;offset page mov dx, ds:[0463H] shr ax, 1 ;the CRT offset is one plane only, no attribute bytes mov cl, al ;first high byte mov al, 0eh out dx, ax mov ah, cl ;then low byte mov al, 0fh out dx, ax retn scrollup: ;scroll up one line push es push ds pop es mov edi, esi push di movzx esi, wCols lea esi, [esi*2+edi] mov cl, byte ptr wCols mov al, bRows mul cl movzx ecx, ax shr cx,1 rep movsd es:[edi], ds:[esi] push di mov cx, wCols mov eax,07200720h shr cx, 1 rep stosd es:[edi] pop ax pop di sub ax, di pop es retn VioPutChar endp ================================================ FILE: Tools/XMSSTAT/MAKE.BAT ================================================ @echo off rem create XMSSTAT.EXE rem uses JWasm jwasm -nologo -mz XMSSTAT.ASM ================================================ FILE: Tools/XMSSTAT/PRINTF.INC ================================================ ;--- simple printf() implementation handle_char proc mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h ret handle_char endp ;--- ltob(long n, char * s, int base); ;--- convert long to string ;--- outb is expected to be onto stack ltob PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltob ENDP ;--- ds=dgroup, ss don't need to be dgroup printf PROC c uses si di bx fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bx inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl mov bx,dx nextdigit: cmp BYTE PTR [si],'0' jb digitsdone cmp BYTE PTR [si],'9' ja digitsdone lodsb sub al,'0' cbw imul cx,bx,10 ;cx = bx * 10 add ax,cx mov bx,ax jmp nextdigit digitsdone: mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'u' je handle_u mov al,'%' jmp @@L359 handle_c: mov ax,ss:[di] add di,2 @@L359: call handle_char retn handle_x: mov bx,16 jmp @@lprt262 handle_d: handle_i: mov bx,-10 jmp @@lprt262 handle_u: mov bx,10 @@lprt262: mov ax,ss:[di] add di,2 sub dx,dx cmp bx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,ss:[di] add di,2 @@: lea cx,[szTmp] invoke ltob, dx::ax, cx, bx mov si,ax push ds push ss pop ds call output_string pop ds retn handle_s: mov si,ss:[di] add di,2 output_string: ;display string at ds:si mov ax,si mov bx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub bx,ax .if flag == 1 .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr bx > 0 mov al,[fill] call handle_char dec bx .endw retn printf ENDP ================================================ FILE: Tools/XMSSTAT/XMSSTAT.ASM ================================================ ;--- XMSSTAT: display xms status. ;--- Public Domain. ;--- to be assembled with JWasm or Masm v6. .model small DGROUP group _TEXT ;use tiny model .386 .dosseg .stack 2048 cr equ 13 lf equ 10 ;--- XMS handle table XMSHT struct db ? bSize db ? wHdls dw ? dwArray dd ? XMSHT ends ;--- XMS handle XMSH struct bFlags db ? ;flags, see below bLocks db ? ;number of locks dwAddr dd ? ;addr in KB dwSize dd ? ;size in KB XMSH ends XMSF_FREEB equ 1 ;free block XMSF_USEDB equ 2 ;used block XMSF_FREEH equ 4 ;free handle printf proto c :ptr byte, :vararg ;--- define a string constant CStr macro string:vararg local xxx .const xxx db string db 0 .code exitm endm .data xmsadr dd 0 ;XMS host call address dwTotal dd 0 ;total size EMBs < 4GB dwTotalSX dd 0 ;total size EMBs freehdls dw 0 ;count free handles wVersion dw 0 ;XMS version wDriver dw 0 ;driver version bFlags db 0 ;flags FL_NOSIZENULL equ 1 FL_NOUSEDEMBS equ 2 FL_NOFREEEMBS equ 4 FL_FREEHANDLES equ 8 FL_NOSEXTMEM equ 16 .code assume DS:DGROUP include printf.inc ;--- display a EOL _crout proc invoke printf, CStr(lf) ret _crout endp ;--- get cmdline parameter ;--- ES=PSP getparm proc mov bx,0080h mov cl,es:[bx] inc bx mov ch,00 jcxz getparm_ex mov ah,00 getparm_1: mov al,es:[bx] or al,20h cmp ax,'-a' jnz @F or bFlags,FL_NOFREEEMBS @@: cmp ax,'-b' jnz @F or [bFlags], FL_FREEHANDLES @@: cmp ax,'-c' jnz @F or [bFlags], FL_NOSIZENULL @@: cmp ax,'-f' jnz @F or [bFlags], FL_NOUSEDEMBS @@: cmp ax,'-s' jnz @F or [bFlags], FL_NOSEXTMEM @@: cmp ax,'-?' jnz getparm_3 .const szHelp label byte db "XMSSTAT v1.3, Public Domain",lf db "usage: XMSSTAT [ -options ]",lf db " -a: skip free memory blocks",lf db " -b: also display unused handles",lf db " -c: skip memory blocks with size 0",lf db " -f: skip used memory blocks",lf db " -s: skip memory blocks beyond 4GB",lf db 0 .code invoke printf, offset szHelp jmp getparm_er getparm_3: cmp al,'/' jnz @F mov al,'-' @@: mov ah,al inc bx loop getparm_1 getparm_ex: clc ret getparm_er: stc ret getparm endp ;--- check if XMS handle should be displayed checkflags proc test al,XMSF_USEDB ;block used? jnz isused test al,XMSF_FREEB jnz isfree inc [freehdls] test bFlags, FL_FREEHANDLES jz nodisp ret isfree: test bFlags, FL_NOFREEEMBS jnz nodisp ret isused: test bFlags, FL_NOUSEDEMBS jnz nodisp ret nodisp: stc ret checkflags endp ;--- display XMS handle flags getflags proc test al,1 jz @F mov cx,CStr("free") ret @@: test al,2 jz @F mov cx,CStr(<"used">) ret @@: test al,4 jz @F mov cx,CStr(<"unused">) ret @@: mov cx,CStr(" ") ret getflags endp ;--- display 1 XMS handle ;--- ES:BX -> handle ;--- SI=no of handle in array (1-based) hdlout proc mov al,es:[bx].XMSH.bFlags call checkflags jnc @F ret @@: test [bFlags], FL_NOSIZENULL jz @F cmp es:[bx].XMSH.dwSize,0 jnz @F ret @@: test [bFlags], FL_NOSEXTMEM jz @F cmp es:[bx].XMSH.dwAddr,400000h jb @F ret @@: invoke printf, CStr("%3u %4X"), si, bx push si mov ecx,es:[bx].XMSH.dwSize mov eax,es:[bx].XMSH.dwAddr add [dwTotalSX],ecx test eax, 0ffc00000h ;beyond 4GB? jnz @F add [dwTotal],ecx @@: mov edx, eax mov esi, eax shl eax, 10 shr esi, 22 add edx, ecx mov edi, edx shl edx, 10 shr edi, 22 jecxz @F sub edx,1 sbb di,0 @@: mov cl, es:[bx].XMSH.bLocks mov ch, 00 invoke printf, CStr(" %2x%08lx-%2x%08lx %8lu %4u "), si, eax, di, edx, es:[bx].XMSH.dwSize, cx pop si mov al, es:[bx].XMSH.bFlags call getflags movzx ax,al invoke printf, CStr("%2X %s",lf), ax, cx ret hdlout endp ;--- display XMS handle array hdlarray proc stdcall pArray:DWORD mov ah,5 ;enable A20 in case array is in HMA call [xmsadr] mov [freehdls],0 les bx,pArray mov cx,es:[bx].XMSHT.wHdls ;total number of handles mov dl,es:[bx].XMSHT.bSize ;size of element (must be 10) mov dh,00 les bx,es:[bx].XMSHT.dwArray jcxz exit call _crout invoke printf, CStr(" no handle region size(kB) locks flags",lf) invoke printf, CStr("------------------------------------------------------------",lf) mov si,1 ;start with 1 nextitem: pusha call hdlout popa add bx,dx inc si loop nextitem invoke printf, CStr("------------------------------------------------------------",lf) invoke printf, CStr(" %9lu"), dwTotalSX mov ecx,dwTotal cmp ecx, dwTotalSX jz @F invoke printf, CStr(" (%lu kB below 4G)"), ecx @@: call _crout exit: invoke printf, CStr("free handles: %u",lf),freehdls mov ah,6 ;disable A20 call [xmsadr] ret hdlarray endp ;--- display XMS handle info (handle table + handle array) hdlinfo proc near mov ax,4309h int 2Fh cmp al,43h jnz nohandletab mov al,es:[bx].XMSHT.bSize ;size of element mov ah,00 invoke printf, CStr("XMS handle table at %X:%X, handle cnt/size=%u/%u",lf), es, bx, es:[bx].XMSHT.wHdls, ax invoke printf, CStr("XMS handle array at %X:%X",lf), word ptr es:[bx].XMSHT.dwArray+2, word ptr es:[bx].XMSHT.dwArray+0 cmp es:[bx].XMSHT.bSize, sizeof XMSH jnz invalhdlsize invoke hdlarray, es::bx ret nohandletab: invoke printf, CStr("Int 2Fh, ax=4309h failed!",lf) ret invalhdlsize: invoke printf, CStr("XMS handle size isn't 10!",lf) ret hdlinfo endp ;*** display XMS UMB info umbinfo proc near mov ah,10h ;request UMB (upper memory block) mov dx,0ffffh ;get FFFF paras (will fail) call dword ptr [xmsadr] ;but DX contains largest block mov ah,10h ;get this largest block call dword ptr [xmsadr] cmp ax,0001h jz umbchk1 cmp bl,80h jnz umbchk2 invoke printf, CStr("no UMB handler installed",lf) jmp exit umbchk2: cmp bl,0B1h jnz @F invoke printf, CStr("no free UMBs available",lf) jmp exit @@: movzx ax,bl invoke printf, CStr("request for UMB failed, BL=%X",lf),ax jmp exit umbchk1: push bx ;save UMB address push dx ;save size largest invoke printf, CStr("segment of largest UMB: %X",lf), bx pop ax invoke printf, CStr("size of largest UMB (paragraphs): %X",lf), ax pop dx mov ah,11h ;free UMB again call dword ptr [xmsadr] cmp ax,1 jz exit mov ax,bx mov ah,0 invoke printf, CStr("calling free UMB failed, BL=%X",lf),ax exit: ret umbinfo endp ;--- display XMS v2 memory info freememinfo2 proc mov ah,8 call [xmsadr] cmp bl,0 jnz failed invoke printf, CStr("v2 free memory largest/total (kB): %u/%u",lf), ax, dx ret failed: movzx bx,bl invoke printf, CStr("XMS call AH=08 returned bl=%X: %u/%u",lf), bx, ax, dx ret freememinfo2 endp ;--- display XMS v3 memory info freememinfo3 proc mov ah,88h call [xmsadr] cmp bl,0 jnz failed invoke printf, CStr("v3 free memory largest/total (kB): %lu/%lu, highest addr: %lX",lf), eax, edx, ecx ret failed: movzx bx,bl invoke printf, CStr("XMS call AH=88h returned bl=%X: %lu/%lu, highest addr: %lX",lf), bx, eax, edx, ecx ret freememinfo3 endp ;--- display XMS v3.5 (HimemSX) memory info freememinfoSX proc mov ah,0C8h call [xmsadr] cmp bl,80h ;not implemented? jz exit cmp bl,0 jnz failed invoke printf, CStr("v3.5 free memory > 4GB largest/total (kB): %lu/%lu",lf), eax, edx ret failed: movzx bx,bl invoke printf, CStr("XMS call AH=C8h returned bl=%X: %lu/%lu",lf), bx, eax, edx exit: ret freememinfoSX endp versioninfo proc mov ah,00 call [xmsadr] mov [wVersion],ax ;version is BCD coded, major version in AH, minor in AL mov [wDriver],bx push dx movzx ax,byte ptr [wVersion+1] movzx dx,byte ptr [wVersion+0] movzx bx,byte ptr [wDriver+1] movzx cx,byte ptr [wDriver+0] invoke printf, CStr("XMS version: %u.%X, driver version: %u.%u",lf), ax, dx, bx, cx pop dx test dx,1 jz nohma invoke printf, CStr("HMA handled by XMS host, HMA is ") mov ah,01h ;try to reserve HMA mov dx,-1 call [xmsadr] cmp ax,0001 jnz hma_used mov ah,02h ;release HMA call [xmsadr] invoke printf, CStr("free",lf) ret hma_used: invoke printf, CStr("allocated",lf) ret nohma: invoke printf, CStr("HMA NOT handled by XMS host",lf) ret versioninfo endp ;--- main main proc c call getparm jc exit mov ax,4300h int 2fh test al,80h ;xms host found? jnz main1 invoke printf, CStr("no XMS host found",lf) jmp exit main1: mov ax,4310h ;get XMS call address int 2fh mov word ptr xmsadr+0,bx mov word ptr xmsadr+2,es invoke printf, CStr("XMS call address: %X:%X",lf), word ptr [xmsadr+2], word ptr [xmsadr+0] call versioninfo call freememinfo2 cmp byte ptr [wVersion+1],3 jb @F call freememinfo3 cmp byte ptr [wVersion],50h jb @F call freememinfoSX @@: call hdlinfo call umbinfo exit: ret main endp ;--- init start proc push cs pop ds pushf pushf pop ax or ah,70h ;a 80386 will have bit 15 cleared push ax ;if bits 12-14 are 0, it is a 80286 popf ;or a bad emulation pushf pop ax popf and ah,0f0h js no386 ;bit 15 set? then its a 8086/80186 jnz is386 no386: invoke printf, CStr("a 80386 is needed",lf) jmp done is386: mov ax,@data mov ds,ax mov bx,ss mov cx,ds sub bx,cx shl bx,4 add bx,sp mov ss,ax mov sp,bx call main done: mov ah,4Ch int 21h start endp END start ================================================ FILE: XMS35.txt ================================================ 1. XMS v3.5 API XMS v3.5 has been created to allow accessing extended memory beyond the 4 GB barrier. To achieve this, the XMS v3.0 API has been extended: AH=0C8h: query free super-extended memory. Returns in EAX largest free block in kB, in EDX the total amount (in kB) of free super-extended memory. AX=0 indicates an error. AH=0C9h: allocate block of super-extended memory. Expects in EDX the requested amount of memory in kB. Returns AX=0 if an error occured, else AX is 1 and the handle of the block is in DX. AH=0CCh: lock a (super-extended) memory block. Expects handle in DX. Returns 64-bit physical address of locked block in EDX:EBX. Returns AX=0 if an error occured. XMS function 00 (Get Version) will return ax=0350h, that is version 3.50. 2. XMS v3.51 API XMS v3.51 extends the v3.50 API by another function: AH=0CBh: super-extended block move function. Register setup is like XMS function 0Bh. However, the structure that holds source and destination of the memory block to copy has been extended by two 1-byte fields that must be filled with bits 32-39 of the source and destination address. sxms_move struct len dd ? ; +0: block length in bytes src_handle dw ? ; +4: source handle src_offset dd ? ; +6: offset into source dst_handle dw ? ; +10: destination handle dst_offset dd ? ; +12: offset into destination src_high db ? ; +16: NEW: bits 32-39 of source offset dest_high db ? ; +17: NEW: bits 32-39 of destination offset sxms_move ends 3. BIOS Interrupt 15h, AX=E820h Since the memory beyond the 4 GB limit must be managed exclusively, Int 15h, ax=E820h should be intercepted in a way that all memory blocks with addresses >= 100000000h are changed from "available" to "reserved". 4. BIOS Interrupt 15h, AH=87h In V86 mode, the XMM's 'move extended memory' functions (AH=0Bh & AH=0CBh) will need the help of the Expanded Memory Manager (EMM), since privileged code has to be executed. The only EMMs that currently support accessing memory beyond 4 GB are Jemm386/JemmEx v5.80+. Their Int 15h API has been exhanced as well. Register setup for Int 15h, AH=87h: - AH: 87h - EAX[bits 16-31]: F00Fh - CX: F00Fh - ECX[bits 16-31]: size of block in words - DS:SI: same as the standard ( pointing to a GDT ), descriptors 2 & 3 defining address bits 0-31 of source/destination region. - DX: address bits 32-47 of the source region. - BX: address bits 32-47 of the destination region. If the call succeeded, the carry flag is cleared and register AH is 0. If an error occured ( for example, CPU doesn't support PSE ), the carry flag is set and AH is != 0. Japheth ================================================ FILE: src/AUXIO.INC ================================================ ;--- handle AUX I/O ifndef COMNO COMNO equ 1 endif _XONXOFF_ equ 1 XON equ 11h XOFF equ 13h .text$01 segment dword flat public 'CODE' wCsrPos dw 0,0 .text$01 ends ;--- display a char in AL AuxPutChar proc public pushad movzx ebx, word ptr cs:[400h + (COMNO-1)*2] lea edx, [ebx+5] ;LSR - Line Status Register mov cx, -1 movzx ecx, cx xchg al, ah @@: in al, dx test al, 40h ;TEMT - transmitter empty? loopz @B if _XONXOFF_ test al, 1 ;char received jz noxoff mov edx, ebx in al, dx cmp al, XOFF jnz noxoff waitxon: add edx, 5 @@: ;wait till new char arrived in al, dx test al, 1 jz @B mov edx, ebx in al, dx cmp al, XON ;wait till XON received jnz waitxon noxoff: endif xchg al, ah mov edx, ebx out dx, al call setcsrpos popad ret setcsrpos: cmp al, 13 jz col00 cmp al, 10 jz nochg cmp al, 8 jz back inc byte ptr ss:[wCsrPos] retn back: dec byte ptr ss:[wCsrPos] retn col00: mov byte ptr ss:[wCsrPos], 0 nochg: retn AuxPutChar endp ;--- get a char in AL AuxGetChar proc push ebx push ecx push edx movzx ebx, word ptr cs:[400h + (COMNO-1)*2] lea edx, [ebx+6] ;MSR - modem status register in al, dx ;DSR - modem(=DCE) ready? and al, 20h jz error dec edx ;LSR - Line Status Register @@: in al, dx test al, 01h ;DR - Data ready? jz @B mov edx, ebx in al, dx mov ah, 00 jmp exit error: xor eax, eax exit: pop edx pop ecx pop ebx ret AuxGetChar endp ================================================ FILE: src/DEBUG.ASM ================================================ ;*** implements debug displays ;--- written by japheth ;--- public domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ?WAIT equ 0 ;std=0; 1=wait for "SPACE released" on LF ;?SLOWDOWN equ 256 ;slow down factor debug displays (just LF) ;--- publics/externals include extern32.inc if ?DBGOUT .text$03 segment ;--- make no assumptions about DS and ES here! ;--- also don't push/pop segment registers! VPUTCHR PROC public PUSHAD if ?KD cmp cs:[bKD],0 ; kernel debugger detected? jz @F mov edx, eax ; then redirect output to it xor eax, eax int 41h popad ret @@: endif if ?AUXIO call AuxPutChar else if ?USEMONO mov edi,0B0000h mov ebx,7 else MOV EDI,0B8000h CMP BYTE ptr SS:[463h],0B4h JNZ @@IS_COLOR XOR DI,DI @@IS_COLOR: movzx EBX, WORD PTR SS:[44Eh] ADD EDI, EBX MOVZX EBX, BYTE PTR SS:[462h] endif mov esi, edi MOVZX ECX, BYTE PTR SS:[EBX*2+450h+1] ;ROW if ?USEMONO MOV EAX, 80 else MOVZX EAX, WORD PTR SS:[44Ah] endif MUL ECX MOVZX EDX, BYTE PTR SS:[EBX*2+450h] ;COL ADD EAX, EDX MOV DH,CL LEA EDI, [EDI+EAX*2] MOV AL, [ESP+1Ch] CMP AL, 13 JZ done CMP AL, 10 JZ @@NEWLINE MOV SS:[EDI], AL MOV byte ptr SS:[EDI+1], 07 INC DL if ?USEMONO cmp dl,80 else CMP DL, BYTE PTR SS:[44Ah] endif JB @@OLDLINE @@NEWLINE: if ?WAIT @@wait: in al,64h test al,1 jz @@wait mov ah,al in al,60h test ah,20h ;PS/2 mouse? jnz @@wait cmp al,39h+80h;space released? jnz @@wait elseifdef ?SLOWDOWN mov ecx,?SLOWDOWN*100 @@: in al,61h cmp al,ah jz @B mov ah,al loop @B endif MOV DL, 00 INC DH if ?USEMONO CMP DH, 24 else CMP DH, BYTE PTR SS:[484h] endif JBE @@OLDLINE DEC DH CALL @@SCROLL_SCREEN @@OLDLINE: MOV SS:[EBX*2+450h],DX done: endif POPAD RET ife ?AUXIO ;--- scroll screen up 1 line ;--- esi -> start screen @@SCROLL_SCREEN: CLD mov edi,esi if ?USEMONO mov eax,80 else movzx eax,word ptr ss:[44Ah] endif push eax lea esi, [esi+2*eax] if ?USEMONO mov CL, 24 else MOV CL, SS:[484h] endif mul cl mov ecx,eax @@nextcell: lodsw cs:[esi] mov ss:[edi],ax add edi,2 loop @@nextcell pop ecx mov ax,0720h @@nextcell2: mov ss:[edi],ax add edi,2 loop @@nextcell2 retn endif VPUTCHR ENDP ;--- print a string which is hard-coded behind the call to this function ifdef _DEBUG include dprintf.inc endif if 0 ;--- 386SWAT isn't compatible with Jemm, because this debugger ;--- expects that extended memory is mapped into linear address space, ;--- at least those parts of XMS memory that 386SWAT uses for itself. ;--- Jemm simply doesn't do that; it maps the memory that it needs and ;--- nothing else ( unless SB option is set, but even then it's just ;--- the memory of page table 0 ). ;--- check if int 3 vector still points to ;--- the monitor code segment. If no, assume 386SWAT has intruded DebugBreak proc public cmp word ptr cs:[offset V86GDT+3*8+4],FLAT_CODE_SEL jz @@noswat pushfd or byte ptr [esp+1],1 ;set TF popfd @@noswat: ret DebugBreak endp endif .text$03 ends .text$04 segment ;--- init debug ;--- ESI -> Jemminit Debug_Init proc public ret Debug_Init endp .text$04 ends endif END ================================================ FILE: src/DEBUG16.INC ================================================ @dbgdef macro name_ ifndef name_ name_ equ 0 endif ?DBGOUT = ?DBGOUT + name_ endm ?DBGOUT = 0 @dbgdef ?INITRMDBG ;1=log real-mode init @dbgdef ?XMSRMDBG ;1=log XMS calls @dbgdef ?EMXRMDBG ;1=log EMMXXXX0 calls @dbgdef ?UNLRMDBG ;1=log unload ;?RMDBG equ ?INITRMDBG + ?XMSRMDBG + ?EMXRMDBG + ?UNLRMDBG ; debug displays in real-mode ?USEMONO equ 0 ;1=use monochrome monitor for dbg displays ifdef _DEBUG dprintf proto c :ptr, :vararg @dprintf macro bCond, fmt, args:vararg if bCond ifnb invoke dprintf, CStr(fmt), args else invoke dprintf, CStr(fmt) endif endif endm else @dprintf textequ <;> endif ================================================ FILE: src/DEBUG32.INC ================================================ @dbgdef macro name_ ifndef name_ name_ equ 0 endif ?DBGOUT = ?DBGOUT + name_ endm ?DBGOUT = 0 @dbgdef ?V86DBG ;1=enable displays in V86 monitor @dbgdef ?V86XDBG ;1=enable more displays in V86 monitor (IRQs!) @dbgdef ?DMADBG ;1=enable DMA related displays @dbgdef ?VDSDBG ;1=enable VDS related displays @dbgdef ?EMSDBG ;1=enable EMS related displays @dbgdef ?UMBDBG ;1=enable UMB related displays @dbgdef ?A20DBG ;1=enable A20 related displays @dbgdef ?EMBDBG ;1=enable EMB related displays (?INTEGRATED only) @dbgdef ?EMUDBG ;1=enable opcode emulation related displays @dbgdef ?I15DBG ;1=enable INT 15h, AH=87h related displays @dbgdef ?VCPIDBG ;1=enable VCPI related displays @dbgdef ?VCPIXDBG;1=enable VCPI mode switch displays @dbgdef ?EMXDBG ;1=enable EMMXXXX0 related displays @dbgdef ?POOLDBG ;1=enable memory pool related displays @dbgdef ?INITDBG ;1=enable displays in protected-mode initialisation @dbgdef ?HLTDBG ;1=enable displays for true HLT emulation @dbgdef ?UNLDBG ;1=enable display for "unload" @dbgdef ?RBTDBG ;1=enable reboot debug @dbgdef ?EXCDBG ;1=enable displays in exception handler @dbgdef ?PHYSDBG ;1=enable displays in physical memory handling @dbgdef ?MAPDBG ;1=enable mapping handling @dbgdef ?NMIDBG ;1=enable displays for NMI handling ?USEMONO equ 0 ;1=use monochrome monitor for dbg displays ;?DBGOUT equ ?V86DBG + ?DMADBG + ?VDSDBG + ?EMSDBG + ?UMBDBG + ?A20DBG + ?EMBDBG + ?EMUDBG + ?I15DBG + ?VCPIDBG + ?USEMONO + ?POOLDBG + ?INITDBG + ?HLTDBG + ?UNLDBG + ?RBTDBG + ?EXCDBG + ?PHYSDBG + ?MAPDBG + ?NMIDBG CStr macro text:vararg local sym .text$03s segment byte flat 'CODE' sym db text,0 .text$03s ends exitm endm @WaitKey macro keycode, bCond local sm1 if bCond pushfd push eax sm1: in al,64h ;data from keyboard controller? test al,1 jz sm1 mov ah,al in al,60h test ah,20h ;mouse device? jnz sm1 cmp al,keycode+80h ;wait for key released jnz sm1 pop eax popfd endif endm ;--- formatted output ( since v5.83 ) ifdef _DEBUG dprintf proto c :ptr, :vararg @dprintf macro bCond, fmt, args:vararg if bCond ifnb invoke dprintf, CStr(fmt), args else invoke dprintf, CStr(fmt) endif endif endm else @dprintf macro bCond, fmt, args:vararg endm endif @DebugBreak macro ifdef _DEBUG int 3 endif endm @CheckBlockIntegrity macro if ?POOLDBG call CheckBlockIntegrity endif endm ================================================ FILE: src/DEBUGSYS.INC ================================================ ; ; Real mode Debugger services: ; D386_RM_Int equ 68h ; hooked by the debugger in real mode. D386_Id equ 0F386h ; debugger identification code D386_MIN equ 43h ; minimum INT 68 function code D386_Identify equ 43h ; returns debugger identification, if debugger D386_Prepare_PMode equ 44h ; partially prepare for protected mode operation D386_Real_Mode_Init equ 45h ; re-init fro real mode after entering pmode D386_Set_Switches equ 46h ; set debugging switches D386_Execute_Cond equ 47h ; execute conditional BP (/B option) D386_Free_Segment equ 48h ; undefine the real mode segment's symbols D386_Set_Baudrate equ 49h ; set com port baud rate D386_Reinit equ 4ah ; reinitialize debugger for protected mode D386_Def_Deb_Segs equ 4bh ; define debugger's segments D386_Set_Com_Port equ 4ch ; set com port number D386_Link_Sym equ 4dh ; link sym file map D386_Unlink_Sym equ 4eh ; unlink sym file maps D386_Remove_Segs equ 4fh ; remove any undefined segments from the D386_Load_Segment equ 50h ; defines the actual segment/selector for a D386_Display_Char equ 51h ; display a character to the debugging terminal D386_Display_Str equ 52h ; display a string to the debugging terminal D386_IsVxDInstalled equ 53h ; returns if debug VxD has been installed D386_VxDInstall equ 54h ; sets that the debug VxD installed/uninstalled D386_RegisterDotCmd equ 55h ; registers dot command D386_DeRegisterDotCmd equ 56h ; de-registers dot command D386_Printf equ 57h ; Printf D386_Link_Sym_Phys equ 58h ; link symbol file with physical address D386_CheckMap equ 59h ; DX:DI = pointer to module name D386_SetAutoLoadSym equ 5ah ; (BL) != 0, auto load symbols D386_SetTeftiPort equ 5bh ; (BX) = TEFTI port address D386_ExecDebugCommand equ 5ch ; execute debugger command script D386_LoadCodeDataHigh equ 5dh ; makes the debugger copy its code/data high D386_SetWinVersion equ 5eh ; sets Windows version number D386_ScanChar equ 5fh ; scan for character D386_UnGetChar equ 60h ; ungetchar scaned character, AL = char D386_Stop equ 61h ; stop at the CS:IP specified D386_MAX equ 61h ; maximum INT 68 function code ; D386_Load_Segment type equates: ST_code_sel equ 0 ; code selector ST_data_sel equ 1 ; data selector ST_code_seg equ 10h ; code segment ST_data_seg equ 11h ; data segment ST_dual_code equ 40h ; code segment and selector ST_dual_data equ 41h ; data segment and selector ST_device_code equ 80h ; device driver code segment ST_device_data equ 81h ; device driver data segment ; D386_Load_Segment device load parameters structure D386_Device_Params STRUC DD_logical_seg dw ? ; logical segment # from map DD_actual_sel dw ? ; actual selector value DD_base dd ? ; linear address offset for start of segment DD_length dd ? ; actual length of segment DD_name df ? ; 16:32 ptr to null terminated device name DD_sym_name df ? ; 16:32 ptr to null terminated symbolic DD_alias_sel dw ? ; alias selector value (0 = none) D386_Device_Params ENDS WDEB_INT2F_STARTING equ 0 ; first time starting WDEB_INT2F_ENDING equ 1 ; first time ending WDEB_INT2F_NESTED_STARTING equ 2 ; start on level of nesting WDEB_INT2F_NESTED_ENDING equ 3 ; end one level of nesting ; PMINIT routine functions PMINIT_INIT_IDT equ 0 ; (ES:EDI) = pointer to PM IDT PMINIT_INIT_PAGING equ 1 ; (BX) = phys-linear selector PMINIT_ENABLE_DEBUG_QUERYS equ 2 ; enables dot commands, etc. PMINIT_INIT_SPARE_PTE equ 3 ; (EBX) = lin addr of spare PTE PMINIT_SET_ENTER_EXIT_VMM equ 4 ; (EBX) = Enter VMM routine addr PMINIT_GET_SIZE_PHYS equ 5 ; get debugger size/phys addr PMINIT_SET_BASE_SPARE_PTE equ 6 ; set debugger base/spare PTE PMINIT_ENABLE_MEMORY_CONTEXT equ 7 ; enables mem context functions PMINIT_MAX equ 7 if 0 WdebVCPIInfo STRUCT fnVCPI df ? ; VCPI protect mode server entry point rdsVCPI dw ? ; Selector for VCPI server laVTP dd ? ; linear address of data structure containing Port67 dw ? ; Qualitas magic port for emulating INT 67h WdebVCPIInfo ENDS VTP struct zaCr3VTP dd 0 ; physical addr of page directory laGdtrVTP dd 0 ; linear addr in first meg of gdtr laIdtrVTP dd 0 ; linear addr in first meg of idtr selLdtVTP dw 0 ; selector of ldt selTrVTP dw 0 ; selector of tr ipVTP dw 0 ; 48-bit address of protect unusedVTP dw 0 ; mode entry point to xfer to csVTP dw 0 ; VTP ends VCPI_RM_CALLOUT_INT equ 67h ; v86 mode call to VCPI server VCPI_PROT_ENTRY equ 0DE0CH endif ; Protected mode Debugger services: Debug_Serv_Int equ 41h ; Interrupt that calls Deb386 to perform DS_Out_Char equ 0 ; function to display the char in DL DS_In_Char equ 1 ; function to read a char into AL DS_Out_Str equ 2 ; function to display a NUL terminated string DS_Is_Char equ 3 ; Non blocking In_Chr DS_Out_Str16 equ 12h ; function to display a NUL terminated string DS_ForcedGO16 equ 40h ; enter the debugger and perform the equivalent DS_LinkMap equ 45h ; DX:(E)DI = ptr to paragraph in front of map DS_UnlinkMap equ 46h ; DX:(E)DI = ptr to paragraph in front of map DS_CheckMap equ 47h ; DX:(E)DI = pointer to module name DS_IsAutoLoadSym equ 48h ; returns AX != 0, auto load symbols DS_DebLoaded equ 4Fh ; check to see if the debugger is installed and DS_DebPresent equ 0F386h DS_LoadSeg equ 50h ; define a segment value for the DS_LoadSeg_32 equ 0150h ; Define a 32-bit segment for Windows 32 DS_MoveSeg equ 51h ; notify the debugger that a segment has moved DS_FreeSeg equ 52h ; notify the debugger that a segment has been DS_FreeSeg_32 equ 0152h ; notify the debugger that a segment has been DS_DGH equ 56h ; register "dump global heap" handler DS_DFL equ 57h ; register "dump free list" handler DS_DLL equ 58h ; register "dump LRU list" handler DS_StartTask equ 59h ; notify debugger that a new task is starting DS_Kernel_Vars equ 5ah ; Used by the Windows kernel to tell the DS_VCPI_Notify equ 5bh ; notify debugger that DOS extender is DS_ReleaseSeg equ 5ch ; This does the same as a DS_FreeSeg, but DS_User_Vars equ 5dh ; DS:SI = pointer to an array of offsets: DS_POSTLOAD = 60h ; Used by the RegisterPTrace interface DS_EXITCALL = 62h ; Somebody will fill these in if we ever DS_INT2 = 63h ; figure out what they are supposed to do. DS_LOADDLL = 64h DS_DELMODULE = 65h DS_NEWTASK = 0BH DS_FLUSHTASK = 0CH DS_SWITCHOUT = 0DH DS_SWITCHIN = 0EH DS_IntRings equ 20h ; function to tell debugger which INT 1's & 3's DS_IncludeSegs equ 21h ; function to tell debugger to go ahead and MaxDebugSegs = 20 DS_CondBP equ 0F001h ; conditional break pt, if the command line DS_ForcedBP equ 0F002h ; break pt, which accomplishes the same thing DS_ForcedGO equ 0F003h ; enter the debugger and perform the equivalent DS_HardINT1 equ 0F004h ; check to see if INT 1 hooked for all rings DS_Out_Symbol equ 0Fh ; find the symbol nearest to the address in DS_Disasm_Ins equ 10h ; function to disassemble the instruction DS_JumpTableStart equ 70h DS_RegisterDotCommand equ 70h DS_RegisterDotCommand16 equ 71h DS_DeRegisterDotCommand equ 72h DS_Printf equ 73h DS_Printf16 equ 74h DS_GetRegisterSet equ 75h DS_SetAlternateRegisterSet equ 76h DS_GetCommandLineChar equ 77h DS_EvaluateExpression equ 78h DS_VerifyMemory equ 79h DS_PrintRegisters equ 7ah DS_PrintStackDump equ 7bh DS_SetThreadID equ 7ch DS_ExecDebugCommand equ 7dh DS_GetDebuggerInfo equ 7eh DS_CheckFault equ 7fh DS_SetBreak equ 80h DS_RedirectExec equ 81h DS_PassOnDebugCommand equ 82h DS_TrapFault equ 83h DS_SetStackTraceCallBack equ 84h DS_RemoveSegs equ 85h DS_DefineDebugSegs equ 86h DS_SetBaudRate equ 87h DS_SetComPort equ 88h DS_ChangeTaskNum equ 89h DS_ExitCleanup equ 8ah DS_InstallVGAHandler equ 8bh DS_GetComBase equ 8ch DS_GetSymbol equ 8dh DS_CopyMem equ 8eh DS_JumpTableEnd equ 8eh SaveRegs_Struc struc Debug_EAX dd ? Debug_EBX dd ? Debug_ECX dd ? Debug_EDX dd ? Debug_ESP dd ? Debug_EBP dd ? Debug_ESI dd ? Debug_EDI dd ? Debug_ES dw ? Debug_SS dw ? Debug_DS dw ? Debug_FS dw ? Debug_GS dw ? Debug_EIP dd ? Debug_CS dw ? dd ? Debug_EFlags dd ? Debug_CR0 dd ? Debug_GDT dq ? Debug_IDT dq ? Debug_LDT dw ? Debug_TR dw ? Debug_CR2 dd ? Debug_CR3 dd ? Debug_DR0 dd ? Debug_DR1 dd ? Debug_DR2 dd ? Debug_DR3 dd ? Debug_DR6 dd ? Debug_DR7 dd ? Debug_DR7_2 dd ? Debug_TR6 dd ? Debug_TR7 dd ? Debug_TrapNumber dw -1 ; -1 means no trap number Debug_ErrorCode dw 0 ; 0 means no error code SaveRegs_Struc ends DebInfoBuf struc DIB_MajorVersion db 0 DIB_MinorVersion db 0 DIB_Revision db 0 db 0 ; reserved DIB_DebugTrap16 dd 0 ; send 16 bit trap to debugger DIB_DebugTrap32 df 0 ; send 32 bit trap to debugger DIB_DebugBreak16 dd 0 ; 16 bit break in debugger DIB_DebugBreak32 df 0 ; 32 bit break in debugger DIB_DebugCtrlC16 dd 0 ; 16 bit check for ctrl C DIB_DebugCtrlC32 df 0 ; 32 bit check for ctrl C DebInfoBuf ends BreakStruc struc BS_BreakEIP dd 0 ; CS:EIP, SS:ESP to go to BS_BreakCS dw 0 ; on a error or ctrlc break BS_BreakESP dd 0 BS_BreakSS dw 0 BreakStruc ends RedirectExecStruc struc RDE_fpbufDebugCommand df 0 ; debugger command script RDE_cbDebugCommand dw 0 ; debugger command script len RDE_fpszInput df 0 ; input stream pointer RDE_usFlags dw 0 ; reserved (must be 0) RDE_cbOutput dd 0 ; size of output buffer RDE_fpbufOutput df 0 ; output buffer pointer RedirectExecStruc ends REPEAT_FOREVER_CHAR equ 0feh ; send next character until ; end of debugger command AddrS struc ; for printf service AddrOff dd 0 AddrSeg dw 0 AddrType db 0 AddrSize db 0 AddrTask dw 0 AddrS ends AddrTypeSize equ word ptr AddrType EXPR_TYPE_SEG equ 00000001b ; address type segment:offset EXPR_TYPE_SEL equ 00001001b ; address type selector:offset EXPR_TYPE_LIN equ 00000010b ; address type linear EXPR_TYPE_PHY equ 00001010b ; address type physical EXPR_TYPE_LOG equ 00001000b ; logical address (no sel yet) DEBUG_FAULT_TYPE_V86 equ 00000001b DEBUG_FAULT_TYPE_PM equ 00000010b DEBUG_FAULT_TYPE_RING0 equ 00000100b DEBUG_FAULT_TYPE_FIRST equ 00001000b DEBUG_FAULT_TYPE_LAST equ 00010000b ; ; Interrupt and services that Win386 provides to the debugger ; Win386_Query_Int equ 22h ; interrupt for Win386 protected mode Win386_Alive equ 0 ; query Win386 installation; AX=Win386_Q_Ack if installed ;Win386_Q_Ack equ 0F386h ; good response from various debug funcs Win386_Query equ 1 ; query; DS:ESI->SaveRegs struct Win386_PhysToLinr equ 2 ; converts phys addr in ESI (size CX) to linear in ESI (ax=1 if ok, else 0) Win386_AddrValid equ 3 ; get validity of a linear address ( ESI, size CX ) Win386_MapVM equ 4 ; make sure VM's low memory is mapped in Win386_UnmapVM equ 5 ; map out the VM's low memory Win386_GetDLAddr equ 6 ; return offset of dyna-link service; hiword(ebx)=device, loword(ebx)=service Win386_GetVXDName equ 7 ; get vxd owner of a memory address in EDX, DS:ESI=buffer Win386_GetPDE equ 8 ; get pde for a context Win386_GetFrame equ 9 ; get phys addr for not pres ptes Win386_GetLDTAddress equ 10; BX = thread Win386_GetThreadID equ 11; AX = Current Thread ID Win386_GetTSHandler equ 12; return offset of transfer-space Win386_GetArplHandler equ 13; return offset of ARPL fault handler in EAX, EBX=real mode seg:ofs Max_Win386_Services equ 13 ================================================ FILE: src/DEV.ASM ================================================ ;*** implements EMMXXXX0 device ;--- public domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ;--- publics/externals include extern32.inc ;--- DOS device request request_hdr struct rhSize db ? ; +0 number of bytes stored rhUnit_id db ? ; +1 unit ID code rhCmd db ? ; +2 command code rhStatus dw ? ; +3 status word rhReserved db 8 dup (?) ; +5 reserved rhMedia db ? ; +13 rhBuffer dd ? ; +14 transfer buffer (SSSS:OOOO) rhCount dw ? ; +18 buffer size request_hdr ends .text$01 SEGMENT dwReqPtr dd 0 ; device strategy request ptr .text$01 ends .text$03 segment ;--- EMMXXXX0 strategy routine EMMXXXX0_Strategy proc public call Simulate_Far_Ret ; do a RETF in V86 movzx eax,word ptr [ebp].Client_Reg_Struc.Client_ES movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_EBX shl eax, 4 add eax, ecx mov [dwReqPtr], eax ret EMMXXXX0_Strategy endp ;--- EMMXXXX0 interrupt routine EMMXXXX0_Interrupt proc public call Simulate_Far_Ret ; do a RETF in V86 mov ecx, [dwReqPtr] if ?EMMXXXX0 mov al, [ecx].request_hdr.rhCmd @dprintf ?EMXDBG, <"EMMXXXX0 request %X",10>,al cmp al, 3 ;IOCTL input? jz @@ioctl_read cmp al, 8 ;write? jz @@resp_writeerr cmp al, 9 ;write+verify? jz @@resp_writeerr cmp al, 10 ;write status jz @@resp_ok cmp al, 11 ;write+flush? jz @@resp_writeerr cmp al, 12 ;IOCTL output? jz @@ioctl_write cmp al, 13 ;open device? jz @@resp_ok cmp al, 14 ;close device? jz @@resp_ok mov ax, 8103h jmp @@device_done @@ioctl_read: push ecx call IoctlRead pop ecx jnc @@resp_ok @@resp_readerr: mov ax, 810Bh jmp @@device_done @@ioctl_write: push ecx call IoctlWrite pop ecx jnc @@resp_ok @@resp_writeerr: mov ax, 810Ah jmp @@device_done @@resp_ok: mov ax, 100h @@device_done: else mov ax, 8103h endif mov [ecx].request_hdr.rhStatus, ax ret align 4 EMMXXXX0_Interrupt endp if ?EMMXXXX0 ;--- read ioctl EMMXXXX0 device ;--- inp: ECX=request header ;--- modifies eax,ebx,ecx,edx,esi,edi IoctlRead proc mov eax, [ecx].request_hdr.rhBuffer movzx ebx, ax shr eax, 12 and al, 0F0h add ebx, eax ;ebx=buffer linear address movzx edx, [ecx].request_hdr.rhCount mov al, [EBX+0] cmp al, EMMDEV_GETAPI ;get "API" jz @@func00 if 0 cmp al, EMMDEV_GEMMIS ;GEMMIS not supported jz @@func01 endif cmp al, EMMDEV_VERSION ;get version jz @@func02 cmp al, EMMDEV_GETRES ;get Emm386 resident segment/size? jz @@func04 cmp al, EMMDEV_SYSVARS ;get system vars jz @@func06 cmp al, EMMDEV_GETUMBS ;get UMBs jz @@func07 if ?SERVTABLE cmp al, EMMDEV_GETSTAB ;get VMM service table info jz @@func08 endif jmp @@error @@func00: cmp edx,6 ;bytes to read jb @@error mov word ptr [ebx+0], 0028h mov dword ptr [ebx+2], 0 ;API entry jmp @@ok @@func02: cmp edx,2 ;bytes to read jb @@error mov word ptr [ebx+0], ?VERSIONHIGH + ?VERSIONLOW * 256 jmp @@ok @@func04: cmp edx,4 ;bytes to read jb @@error mov eax, [dwRSeg] mov [ebx+0], eax jmp @@ok @@func06: cmp edx,16 ;bytes to read jb @@error mov al, [bNoEMS] mov [ebx].EMX06.e06_NoEMS, al xor eax, eax cmp [bNoFrame],0 jnz @@nopf mov ah, [EMSPage2Segm] ;get mapping of phys page 0 @@nopf: mov [ebx].EMX06.e06_Frame, ax mov al, [bNoVCPI] mov [ebx].EMX06.e06_NoVCPI, al cmp edx,24 ;to get VCPI memory info, we need 24 byte jb @@novcpimem mov eax, [pmem.dwMaxMem4K] ;VCPI 4 kB pages mov [ebx].EMX06.e06_VCPITotal, eax mov eax, [pmem.dwUsedMem4K] mov [ebx].EMX06.e06_VCPIUsed, eax @@novcpimem: if 1;?DMAPT mov eax, [vdsstat.DMABuffStartPhys] else xor eax, eax endif mov [ebx].EMX06.e06_DMABuff, eax if 1;?DMAPT mov eax, [vdsstat.DMABuffSize] shr eax, 10 else xor eax, eax endif mov [ebx].EMX06.e06_DMASize, ax if ?VME mov al,1 test byte ptr [dwFeatures],2 jz @@novme .586p mov eax, CR4 .386 and al,1 xor al,1 @@novme: mov [ebx].EMX06.e06_NoVME,al endif if ?PGE mov al,1 test byte ptr [dwFeatures+1],20h jz @@nopge .586p mov eax, CR4 .386 shr al,7 xor al,1 @@nopge: mov [ebx].EMX06.e06_NoPGE,al endif if ?A20PORTS or ?A20XMS mov al,[bNoA20] mov [ebx].EMX06.e06_NoA20,al endif @@ok: clc ret @@error: stc ret @@func07: cmp edx, UMB_MAX_BLOCKS * size UMBBLK ;buffer large enough to get the UMB entries? jb @@error mov edi, ebx mov esi, offset UMBsegments mov ecx, UMB_MAX_BLOCKS cld rep movsd clc ret if ?SERVTABLE @@func08: cmp edx, size EMX08 jb @@error mov [ebx].EMX08.e08_ServiceTable, offset vmm_service_table mov [ebx].EMX08.e08_pCallBack, offset bptable + BPTABLE.pInt19 mov eax, [dwRSeg] shl eax, 16 mov ecx, [bpstart] inc ecx ;use second static BP ( for INT 19h ) sub ecx, [dwRes] mov ax, cx mov [ebx].EMX08.e08_CallBackRM, eax mov word ptr [ebx].EMX08.e08_GDTR, GDT_SIZE-1 mov dword ptr [ebx].EMX08.e08_GDTR+2, offset V86GDT mov eax,dword ptr [IDT_PTR+2] mov word ptr [ebx].EMX08.e08_IDTR, 7FFh mov dword ptr [ebx].EMX08.e08_IDTR+2, eax mov [ebx].EMX08.e08_TR, V86_TSS_SEL mov [ebx].EMX08.e08_FlatCS, FLAT_CODE_SEL ret endif align 4 IoctlRead endp ;--- all registers may be modified! ;--- inp: ECX=request header ;--- the "update" command is EMMDEV_UPDATE IoctlWrite proc mov eax, [ecx].request_hdr.rhBuffer movzx esi, ax shr eax, 12 and al, 0F0h add esi, eax lods byte ptr [esi] ;function to call cmp al, EMMDEV_UPDATE jz @@func15 @@error: stc ret @@func15: ;--- esi -> EMX15W variable movzx eax, [ecx].request_hdr.rhCount ;buffer size cmp eax, 5 jb @@error lods byte ptr [esi] ;e15_bVME if ?VME cmp al,-1 jz @@novme xor al,1 call SetVME @@novme: endif lods byte ptr [esi] ;e15_bA20 if ?A20PORTS or ?A20XMS cmp al,-1 jz @@noa20 and al, al jz @@a20emuon push eax mov al,1 call A20_Set ;enable A20 gate pop eax @@a20emuon: mov [bNoA20], al @@noa20: endif lods byte ptr [esi] ;e15_bVCPI cmp al,-1 jz @@novcpi mov [bNoVCPI],al @@novcpi: lods byte ptr [esi] ;e15_bPGE if ?PGE cmp al,-1 jz @@nopge test byte ptr [dwFeatures+1], 20h ;PGE supported? jz @@nopge and al,1 xor al,1 mov [bPageMask],al if 0 mov edi, @GetPTEAddr(?PAGETAB0) ;start of pagetab 0 mov ecx,110h+1 ;00000000-00110FFF @@FILL_PAGETAB0: mov edx, [edi] and dh,not 1 ;mask out G or dh,al MOV [EDI],EDX ADD EDI,4 loop @@FILL_PAGETAB0 endif .586p mov ecx, CR4 shl al, 7 and cl, not 80h or cl, al mov CR4, ecx .386 @@nopge: endif clc ret align 4 IoctlWrite endp endif .text$03 ends END ================================================ FILE: src/DMA.INC ================================================ ;--- ports ISA DMA controller DMA_STATUS_CMD equ 8 ; R status / W command DMA_REQUEST equ 9 ; W b0+1: channel, b2: 1=enable?, b3-7:rsvd DMA_SINGLEMASK equ 10 ; W b0+1: channel to mask, b2: 0=clear/1=mask, b3-7:rsvd DMA_MODE equ 11 ; W b0+1: channel, b2-7:see below DMA_FLIPFLOP equ 12 ; W DMA_IMM_RESET equ 13 ; R immediate / W master reset (masks all 4 channels) DMA_MASK_RESET equ 14 ; W master enable (unmasks all 4 channels) DMA_MULTIMASK equ 15 ; W b0-3: 1=channel[0-3] masked, 0=channel[0-3] unmasked ;--- bits in DMA_MODE DMA_MODE_OPERATION equ 1100b; "op" bits 2+3 DMA_MODE_OP_VERIFY equ 0000b DMA_MODE_OP_WRITE equ 0100b DMA_MODE_OP_READ equ 1000b DMA_MODE_AUTO equ 10h ; b4 DMA_MODE_DIRECTION equ 20h ; b5: direction 0=increment, 1=decrement ;--- b6-7: 00=demand,01=single,10=block,11=cascade DMA_BASE_16BIT equ 0C0h DMA_STATUS_CMD16 equ DMA_BASE_16BIT + DMA_STATUS_CMD*2 DMA_SINGLEMASK16 equ DMA_BASE_16BIT + DMA_SINGLEMASK*2 DMA_MODE16 equ DMA_BASE_16BIT + DMA_MODE*2 DMA_FLIPFLOP16 equ DMA_BASE_16BIT + DMA_FLIPFLOP*2 DMA_IMM_RESET16 equ DMA_BASE_16BIT + DMA_IMM_RESET*2 DMA_MASK_RESET16 equ DMA_BASE_16BIT + DMA_MASK_RESET*2 DMA_MULTIMASK16 equ DMA_BASE_16BIT + DMA_MULTIMASK*2 MAXDMACHANNEL equ 8 ================================================ FILE: src/DPRINTF.INC ================================================ ;--- printf for debug displays ;--- assume CS and SS flat; ;--- no assumptions about DS/ES ;--- i64toa(long long n, char * s, int base); ;--- convert 64-bit long long to string i64toa proc stdcall uses edi number:qword, outb:ptr, base:dword mov ch,0 mov edi, base mov eax, dword ptr number+0 mov esi, dword ptr number+4 cmp edi,-10 jne @F neg edi and esi,esi jns @F neg esi neg eax sbb esi,0 mov ch,'-' @@: mov ebx,outb add ebx,22 mov byte ptr ss:[ebx],0 @@nextdigit: dec ebx xor edx,edx xchg eax,esi div edi xchg eax,esi div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[ebx],dl mov edx, eax or edx, esi jne @@nextdigit cmp ch,0 je @F dec ebx mov ss:[ebx],ch @@: mov eax,ebx ret i64toa endp ;--- dprintf has to preserve eflags, but usage of local vars will include a "sub esp,xxx"! ;--- hence dprintf() needs a special prologue: @dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms ;if flag ; if parmbyte + localbyte push ebp mov ebp,esp ; endif ; if localbyte lea esp,[esp-localbyte] ; endif ;endif ; for r,reglist ; push r ; endm exitm %localbyte endm OPTION PROLOGUE: @dprologue dprintf proc c public fmt:ptr, args:vararg local flag:byte local longarg:byte local size_:dword local fillchr:dword local szTmp[24]:byte pushad pushfd cld lea edi,args @@L335: mov esi,fmt nextchar: lodsb cs:[esi] or al,al je done cmp al,'%' je formatitem call dochar jmp nextchar done: popfd popad ret formatitem: push offset @@L335 xor edx,edx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR cs:[esi],'-' jne @F dec bl inc esi @@: mov [flag],bl cmp BYTE PTR cs:[esi],'0' jne @F mov cl,'0' inc esi @@: mov [fillchr],ecx mov ebx,edx .while ( byte ptr cs:[esi] >= '0' && byte ptr cs:[esi] <= '9' ) lodsb cs:[esi] sub al,'0' movzx eax,al imul ecx,ebx,10 ;ecx = ebx * 10 add eax,ecx mov ebx,eax .endw mov [size_],ebx cmp BYTE PTR cs:[esi],'l' jne @F mov [longarg],1 inc esi @@: lodsb cs:[esi] mov [fmt],esi cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'d' je handle_d cmp al,'u' je handle_u cmp al,'s' je handle_s cmp al,'c' je handle_c and al,al jnz @F pop eax jmp done handle_c: mov eax,ss:[edi] add edi, 4 @@: call dochar retn handle_s: mov esi,ss:[edi] add edi,4 jmp print_string handle_d: handle_i: mov ebx,-10 jmp @F handle_u: mov ebx, 10 jmp @F handle_x: mov ebx, 16 @@: xor edx,edx mov eax,ss:[edi] add edi,4 cmp longarg,1 jnz @F mov edx,ss:[edi] add edi,4 jmp printnum @@: and ebx,ebx jns @F cdq @@: printnum: lea esi, szTmp invoke i64toa, edx::eax, esi, ebx mov esi, eax print_string: ;print string ESI, size EAX mov eax, esi .while byte ptr cs:[esi] inc esi .endw sub esi, eax xchg eax, esi mov ebx,size_ sub ebx,eax .if flag == 1 .while sdword ptr ebx > 0 mov eax, [fillchr] call dochar ;print leading filler chars dec ebx .endw .endif .while byte ptr cs:[esi] lodsb cs:[esi] call dochar ;print char of string .endw .while sdword ptr ebx > 0 mov eax, [fillchr] call dochar ;print trailing spaces dec ebx .endw retn dochar: cmp al,10 jnz @F mov al,13 call @F mov al,10 @@: jmp VPUTCHR dprintf endp OPTION PROLOGUE: prologuedef ================================================ FILE: src/DPRNTF16.INC ================================================ ;--- printf for debug displays, 16-bit ;--- itoa(long n, char * s, int base); ;--- convert 32-bit long to string ;--- v5.86: removed eax from uses clause (since AX is used to return string ptr) ltoa PROC stdcall uses edx edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov bx,outb add bx,10 mov BYTE PTR ss:[bx],0 dec bx @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov ss:[bx],dl dec bx and eax, eax jne @@nextdigit cmp ch,0 je @F mov ss:[bx],ch dec bx @@: inc bx mov ax,bx ret ltoa ENDP ;--- dprintf is to preserve registers & flags; needs a special prologue: @dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms push bp mov bp,sp lea esp,[esp-localbyte] ;there exists no 16-bit "lea sp,[sp-xx]" exitm %localbyte endm OPTION PROLOGUE: @dprologue dprintf proc c public fmt:ptr, args:vararg local flag:byte local longarg:byte local size_:word local fillchr:word local szTmp[12]:byte pusha pushf push ds push cs pop ds cld lea di,args @@L335: mov si,fmt nextchar: lodsb [si] or al,al je done cmp al,'%' je formatitem call VPUTCHR jmp nextchar done: pop ds popf popa ret formatitem: push offset @@L335 xor dx,dx mov [longarg],dl mov bl,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec bl inc si @@: mov [flag],bl cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fillchr],cx mov bx,dx .while ( byte ptr [si] >= '0' && byte ptr [si] <= '9' ) lodsb sub al,'0' cbw imul cx,bx,10 ;ecx = ebx * 10 add ax,cx mov bx,ax .endw mov [size_],bx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'d' je handle_d cmp al,'u' je handle_u cmp al,'s' je handle_s cmp al,'c' je handle_c and al,al jnz @F pop ax jmp done handle_c: mov ax,ss:[di] add di,2 @@: call VPUTCHR retn handle_s: mov si,ss:[di] add di,2 jmp print_string handle_d: handle_i: mov bx,-10 jmp @F handle_u: mov bx, 10 jmp @F handle_x: mov bx, 16 @@: xor dx,dx mov ax,ss:[di] add di,2 cmp longarg,1 jnz @F mov dx,ss:[di] add di,2 jmp printnum @@: and bx,bx jns @F cdq @@: printnum: lea si, szTmp push eax ;v5.86: added (to preserve hiword(eax)) invoke ltoa, dx::ax, si, bx mov si, ax pop eax ;v5.86: added push ds push ss pop ds call print_string pop ds retn print_string: ;print string SI mov ax, si .while byte ptr [si] inc si .endw sub si, ax xchg ax, si mov bx,size_ sub bx, ax .if flag == 1 .while sword ptr bx > 0 mov ax, [fillchr] call VPUTCHR ;print leading filler chars dec bx .endw .endif .while byte ptr [si] lodsb call VPUTCHR ;print char of string .endw .while sword ptr bx > 0 mov ax, [fillchr] call VPUTCHR ;print trailing spaces dec bx .endw retn VPUTCHR: cmp al,10 jnz @F mov al,13 call @F mov al,10 @@: push bx xor bx, bx mov ah, 0Eh int 10h pop bx retn dprintf endp OPTION PROLOGUE: prologuedef ================================================ FILE: src/EMS.ASM ================================================ ;--- EMS implementation ;--- EMS 4.0 is Public Domain, originally written by Michael Devore, ;--- extended and modified for Jemm by Japheth; ;--- the EMS 3.2 part is copyrighted and has therefore ;--- been moved into a separate include file (EMS32.INC) ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc include ems.inc ;--- equates DEVICE_NAME equ 000Ah ; offset device name in RSEG ;--- config ?MAXPHYSPG equ 56 ; std=56, max physical pages (don't set below 4!) ?YIELDOPEN equ 0 ; std=0, 1=yield on handle open ?LIM32 equ 0 ; std=0, 1=support LIM EMS 3.2 only ?SUPP5B equ 1 ; std=1, 1=support int 67h, ax=5Bxxh ;--- public/externals include extern32.inc ;--- EMS page descriptor ;--- for 4 GB, there are 4096/1.5 = 2731 possible pool descriptor indices EMSPD struct wNext dw ? ;index next EMSPD wIndex dw ? ;pool descriptor index for EMS page EMSPD ends ;--- descriptor subindex EMSPD2 struct bSubIdx db ? ;pool descriptor subindex for EMS page EMSPD2 ends ;--- alter page map and jump/call structures (55h and 56h) log_phys_map struct wLogPage DW ? ; logical page (handle in DX) wPhysPage DW ? ; physical pages if AL=0, segments if AL=1 log_phys_map ends map_and_jump struct target_address DD ? ; far16 jump address new_page_map_len DB ? ; items in new_page_map_ptr new_page_map_ptr DD ? ; far16 pointer to log_phys_map structure map_and_jump ends map_and_call struct map_and_jump old_page_map_len db ? ; items in old_page_map_ptr old_page_map_ptr dd ? ; far16 pointer to log_phys_map structure map_and_call ends ;--- structure for EMS function 57h EMM57 struct e57_dwSize DD ? ; +0 size of region e57_bSrcTyp DB ? ; +4 src memory type e57_wSrcHdl DW ? ; +5 src handle e57_wSrcOfs DW ? ; +7 src ofs e57_wSrcSeg DW ? ; +9 src segm./log. page e57_bDstTyp DB ? ; +11 dst memory type e57_wDstHdl DW ? ; +12 dst handle e57_wDstOfs DW ? ; +14 dst ofs e57_wDstSeg DW ? ; +16 dst segm./log. page EMM57 ends ;--- structure for EMS function 59h EMM59 struct e59_pgsize dw ? ;raw page size in paragraphs e59_altsets dw ? ;number of alternate register sets e59_sizcont dw ? ;size of mapping context save area in bytes e59_dmasets dw ? ;dma register sets e59_dmaflgs dw ? ;dma flags EMM59 ends .text$01 SEGMENT ;--- EMS variables EMSHandleTable DD 0 ; table of EMS handles (4 bytes/handle) EMSNameTable DD 0 ; table of EMS handle names (8 bytes/handle) EMSStateTable DD 0 ; table of EMS status saves (16 bytes/handle) EMSPagesMax DD 0 ; max EMS pages (default 2048) EMSPagesUsed DD 0 ; used EMS pages EMSPage2Segm DB ?MAXPHYSPG DUP (0) ; segments mapped to phys.pgs ;EMSMappedPages DW ?MAXPHYSPG DUP (-1) ; log. EMS pgs mapped to phys. pgs align 4 ;--- table of EMS page descriptors (EMSPD), one item for each EMS page ;--- initialized with -1 EMSPageAllocationStart DD 0 EMSPageAllocationEnd DD 0 emm59_ EMM59 <1024,0,0,0,0> if ?SUPP5B mapptr DD 0 ; alternate map save area (get/set by ax=5B00h/5B01h) endif bEmsPhysPages DB 0 ; current physical pages bPagesConv DB 0 ; physical pages in conv. memory bNoEMS DB 0 ; flags no EMS services bNoFrame DB 0 ; flags no page frame bNoVCPI DB 0 ; flags no VCPI services ;--- this is 16-bit code which is copied onto the client's stack ;--- to restore the page mapping in int 67h, ah=56h clproc label byte db 1Eh ; 0 push ds db 51h ; 1 push cx db 52h ; 2 push dx db 56h ; 3 push si db 0B9h ; 4 mov cx,zzzz _clcx dw 0 ; 5 db 0BAh ; 7 mov dx,zzzz _cldx dw 0 ; 8 db 0BEh ; A mov si,yyyy _clsi dw 0 ; B db 068h ; D push xxxx _clds dw 0 ; E db 1Fh ; 0 pop ds db 0B8h ; 1 mov ax,50wwh _clal db 00,50h ; 2 db 0CDh, 67h ; 4 int 67h db 5Eh ; 6 pop si db 5Ah ; 7 pop dx db 59h ; 8 pop cx db 1Fh ; 9 pop ds db 0CAh ; A retf sizeclproc dw sizeclproc ; B db 0 ; D alignment byte sizeclproc equ $ - offset clproc .text$01 ends .text$03 segment align 4 EMS_Call_Table label dword Dd EMS_GET_STATUS ; 40h Dd EMS_GET_PAGE_FRAME_ADDRESS ;41h Dd EMS_GET_UNALLOCATED_PAGE_COUNT ;42h Dd EMS_ALLOCATE_PAGES ; 43h Dd EMS_MAP_HANDLE_PAGE ; 44h (map/unmap a page) Dd EMS_DEALLOCATE_PAGES ; 45h Dd EMS_GET_VERSION ; 46h Dd EMS_SAVE_PAGES ; 47h Dd EMS_RESTORE_PAGES ; 48h Dd EMS_NOT_IMPL ; 49h (get io port addresses) Dd EMS_NOT_IMPL ; 4ah (get translation array) Dd EMS_GET_OPEN_HANDLES_COUNT ; 4bh Dd EMS_GET_PAGES_ONE_HANDLE ; 4ch Dd EMS_GET_PAGES_ALL_HANDLES; 4dh Dd EMS_GET_SET_PAGE_MAP ; 4eh ife ?LIM32 Dd ems4_get_set_partial_page_map ; 4fh Dd ems4_map_multiple ; 50h Dd ems4_realloc ; 51h Dd ems4_attribute ; 52h Dd ems4_get_set_handle_name ; 53h Dd ems4_get_handle_info ; 54h Dd ems4_alter_map_jump ; 55h Dd ems4_alter_map_call ; 56h Dd ems4_move_memory ; 57h Dd ems4_get_mappable_info ; 58h Dd ems4_get_config ; 59h Dd ems4_allocate_pages ; 5ah Dd ems4_alt_map_reg_set ; 5bh Dd EMS_NOT_IMPL ; 5ch (4: prepare EMS for warm boot) Dd EMS_NOT_IMPL ; 5dh (4: enable/disable OS functions) endif EMS_MAX equ ($ - EMS_Call_Table) / 4 align 4 ;--- breakpoint: if int 67h vector is hooked in real-mode, the hooker code ;--- is called and will finally met a breakpoint which will get us here. Int67_V86Entry proc public @dprintf ?EMSDBG, <"Int 67h (V86Entry): CS:EIP=%X:%X EBP=%X",10>, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP, ebp call Simulate_Iret add esp,4 ;skip return address mov esi,[ebp].Client_Reg_Struc.Client_ESI mov edx,[ebp].Client_Reg_Struc.Client_EDX mov eax,[ebp].Client_Reg_Struc.Client_EAX JMP EMM_ENTRY_2 Int67_V86Entry endp @emmpushreg macro SUB ESP, CRS_SIZEX PUSHAD MOV EBP, ESP endm @emmpopreg macro setebp if setebp mov esp, ebp endif POPAD ADD ESP, CRS_SIZEX endm ;--- IVT 67h is hooked; enter monitor to run the IVT code... Int67_Indirect: @emmpopreg 0 push 67h jmp V86_Monitor align 4 ; ; Here starts the Expanded Memory Manager (EMM) Version 4.0 ; Int67_Entry is the IDT entry for INT 67h. Int67_Entry PROC public @emmpushreg ; make EBP -> Client_Reg_Struc ;--- must be one source line without concatenation operator \, or Masm complains @dprintf ?EMSDBG, <"Int 67h (IDT): CS:EIP=%X:%X EBP=%X AX=%X",10>, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP, ebp, ax mov ecx, cs:[dwRSeg] cmp cx, cs:[67h*4+2] ;IVT vector 67h modified? jnz Int67_Indirect MOV ECX,SS ; address everything MOV DS,ECX MOV ES,ECX mov esp,[dwStackCurr] EMM_ENTRY_2:: CLD @dprintf ?EMSDBG, <"Int 67h: CS:EIP=%X:%X EBP=%X AX=%X ESP=%X",10>, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP, ebp, ax, esp if ?VCPI cmp ah,0deh ; see if VCPI function jne @@not_vcpi_api cmp al,0Ch jz VCPI_V86toPM jnc @@vcpi_inv_call ; invalid VCPI call movzx ecx,al cmp [bNoVCPI],0 ; check if VCPI turned off jne @@vcpi_inv_call ; yes, return invalid code, flags VCPI not present for 0de00h if ?VCPIDBG bt vcpidbgfilt,cx jnc nodbgout @dprintf ?VCPIDBG, <"VCPI rm, in: ax=%X cx=%X edx=%X">, word ptr [ebp].Client_Reg_Struc.Client_EAX,\ word ptr [ebp].Client_Reg_Struc.Client_ECX, [ebp].Client_Reg_Struc.Client_EDX call [VCPI_Call_Table+ECX*4] @dprintf ?VCPIDBG, <", out: ax=%X edx=%X",10>, ax, edx jmp vcpi_done align 4 ;--- VCPI functions that are to be logged ;------------- FEDCBA9876543210 vcpidbgfilt dw 0000111111111111b nodbgout: endif call [VCPI_Call_Table+ECX*4] vcpi_done: mov [ebp].Client_Reg_Struc.Client_EDX, edx jmp @@byeems align 4 @@not_vcpi_api: endif MOVZX ECX,AH ; check permitted range SUB CL,40H JB @@emm_inv_call CMP CL,EMS_MAX JAE @@emm_inv_call @dprintf ?EMSDBG, <"EMS in: ax=%X bx=%X dx=%X">, word ptr [ebp].Client_Reg_Struc.Client_EAX,\ word ptr [ebp].Client_Reg_Struc.Client_EBX, word ptr [ebp].Client_Reg_Struc.Client_EDX CALL [EMS_Call_Table+ECX*4] @dprintf ?EMSDBG, <", out: ax=%X bx=%X dx=%X",10>, ax, bx, dx mov word ptr [ebp].Client_Reg_Struc.Client_EBX,bx mov word ptr [ebp].Client_Reg_Struc.Client_EDX,dx @@byeems: mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,ah @emmpopreg 1 IRETD @@vcpi_inv_call: @dprintf ?VCPIDBG, <"VCPI invalid function, ax=%X",10>, ax if ?VCPIDBG MOV ah,EMSS_INVALID_FUNCTION JMP @@byeems endif @@emm_inv_call: @dprintf ?EMSDBG, <"EMS invalid function, ax=%X",10>, ax MOV ah,EMSS_INVALID_FUNCTION JMP @@byeems Int67_Entry ENDP include EMS32.INC ;include the copyrighted part (EMS 3.2) _ret: ;this label belongs to EMS_MAP_REL_PAGE ret ; Map EMS page in DX:BX to physical page in AL ; called by EMS function 44h ; BX=-1 -> unmap page EMS_MAP_REL_PAGE proc call EMS_get_abs_page ;get absolute page in BX jc _ret EMS_MAP_REL_PAGE endp ;fall thru!!! ; Map an EMS page to a physical page ; inp: AL = physical EMS page (0 - (bEmsPhysPages - 1)) ; BX = absolute EMS page (or -1 to unmap) ; modifies EDI, ECX, EAX ; the logical page in BX might belong to no handle! EMS_MAP_ABS_PAGE PROC movzx eax,al mov al, [EMSPage2Segm+EAX] lea edi, @GetPTEAddr(EAX*4+?PAGETAB0) ; EDI -> PTE SHL eax, 12 ; C0 -> C0000, C4-> C4000 EMS_MAP_ABS_PAGE ENDP ;fall through!!! ; Map an (absolute) EMS page anywhere in address space ; BX = EMS abs. page ; EDI == ptr to PTE ; EAX == linear address ; modifies edi, ecx, eax EMS_MAP_ABS_PAGE_EX proc AND BH,BH ; unmap the page? JS @@SET MOVZX ECX,BX ; get the EMSPD for the page in ECX shl ecx,2 add ecx, [EMSPageAllocationStart] movzx ecx,[ecx].EMSPD.wIndex; get pool descriptor index cmp cx,-1 ;bad pointer? jz @@SET ;then unmap this page movzx eax, bx add eax,[EMSPageAllocationEnd] movzx eax,[eax].EMSPD2.bSubIdx ;pool descriptor subindex call Pool_GetPhysAddr ;convert index+offset into phys addr @@SET: OR EAX,7 ; Statusbits: R/W=1,U/S=1,P=1 if 0 mov cl,4 @@LOOP: MOV [EDI],EAX ; Register the new physical Address of ADD EDI,4 ; window ADD EAX,4096 ; Process next 4K-page DEC CL JNZ @@LOOP else mov ecx,1000h stosd add eax,ecx stosd add eax,ecx stosd add eax,ecx stosd endif RET align 4 EMS_MAP_ABS_PAGE_EX endp FlushTLB proc if 0 cmp [bNoInvlPg],0 jz @@noinvlpg endif MOV EAX,CR3 ; flush TLB MOV CR3,EAX @@noinvlpg: ret align 4 FlushTLB endp ;--- get EMS absolute page ;--- inp DL:BX = handle:logical page ;--- out NC ok, EBX = absolute page (index into EMSPD array) ;--- C if failure, then error code in AH ;--- modifies ECX, EDI EMS_get_abs_page proc AND BH,BH ; bx < 0 means unmap this phys. page JS @@OK movzx ecx, bx movzx ebx, dl mov edi,[EMSHandleTable] mov bx,[edi+ebx*4].EMSHD.ehd_wIdx MOV EDI,[EMSPageAllocationStart] inc ecx jmp @@test align 4 @@nextitem: mov bx,[edi+ebx*4].EMSPD.wNext @@test: cmp bx,-1 loopnz @@nextitem jz @@fail @@OK: clc ret @@fail: MOV AH,EMSS_LOG_PAGE_INVALID ; "logical page out of reserved area" stc ret align 4 EMS_get_abs_page endp ;--- check if segment in AX is a valid (physical) page ;--- if yes, return NC and convert segment to physical page in AL EMS_Segm2Phys proc and al,al ;must begin on a page boundary jnz @@notvalid mov al,ah push ecx push edi movzx ecx, [bEmsPhysPages] mov edi, offset EMSPage2Segm mov ah,cl repnz scasb pop edi jnz @@notvalid2 mov al, ah dec al sub al, cl pop ecx ret @@notvalid2: pop ecx @@notvalid: mov ah,EMSS_PHYS_PAGE_INVALID stc ret align 4 EMS_Segm2Phys endp ;--- begin EMS 4.0 functions ife ?LIM32 ; ; 674F: AH = 4Fh: Get & Set partial Map ; AL = 0,1,2 ; AL = 0 (get map), DS:SI -> map to get, ES:DI -> status to receive ; AL = 1 (set map), DS:SI -> map to restore ; AL = 2 (get size), items in BX, returns size of map in AL ; the list for sub function 0 DS:SI points to has following structure: ; WORD : items in list ; WORD[] ; segment! addresses for which to get the map info ; the output list ES:DI points to (function 0) has following structure: ; WORD : items in list ; BYTE : page no ; WORD ; abs EMS page mapped (or -1) ems4_get_set_partial_page_map PROC cmp al,2 ja bad_subfunc jz @@getsize movzx ecx,WORD PTR [ebp].Client_Reg_Struc.Client_DS shl ecx,4 movzx esi, si add esi,ecx cmp al,1 jz @@setmap ;--- ax=4F00h (get map) movzx ecx,WORD PTR [ebp].Client_Reg_Struc.Client_ES shl ecx,4 movzx edi, di add edi,ecx lodsw movzx eax,ax movzx ecx, [bEmsPhysPages] cmp eax,ecx ja @@failA3 mov ecx, eax stosw jecxz @@done00 @@nextsegm: lodsw call EMS_Segm2Phys ;convert segment to phys page jc @@fail movzx eax,al push ecx movzx ecx, byte ptr [esi-1] lea ecx, @GetPTEAddr(ECX*4+?PAGETAB0) mov ecx,[ecx] mov cl,0 or eax,ecx stosd pop ecx loop @@nextsegm @@done00: mov ah,EMSS_OK ret @@failA3: @@failA3_1: mov ah,0A3h ;segment count exceeds mappable pages @@fail: ret ;--- ax=4F01h (set map) @@setmap: lodsw movzx eax,ax movzx ecx, [bEmsPhysPages] cmp eax,ecx ja @@failA3 mov ecx, eax jmp EMS_RestorePagesFromEsi ;--- ax=4F02h (get size of save area) @@getsize: cmp bh,0 jnz @@failA3_1 cmp bl,[bEmsPhysPages] ja @@failA3_1 mov al, bl shl al,2 ;4 bytes (makes Kyrandia 3 work [better]!?) add al,2 ;2 extra bytes for size mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al mov ah, EMSS_OK ret align 4 ems4_get_set_partial_page_map ENDP ; 6750: ; AH = 50h: EMS 4.0 map multiple pages ; DX = handle ; DS:SI -> mapping array ; CX = items in array (ecx is destroyed, reload it from stack) ; structure of mapping array: ; WORD logical page (or -1 to unmap page) ; WORD physical page (AL=0) or segment address (AL=1) ems4_map_multiple PROC cmp al,1 ja bad_subfunc movzx ecx, word ptr [ebp].Client_Reg_Struc.Client_ECX ; load EMM entry CX value movzx edi, word ptr [ebp].Client_Reg_Struc.Client_DS shl edi,4 movzx esi,si add edi,esi ; edi -> map address buffer ems4_map_multiple_edi:: ; perform handle check here so that stack return address isn't blown call EMS_TEST_HANDLE mov esi, edi push ebx push eax jecxz @@success @@multi_loop: mov bx,[esi+0] mov ax,[esi+2] add esi,4 cmp byte ptr [esp+0],1 ;subfunction 1? jne @@mappage call EMS_Segm2Phys ;convert segment in AX to a page no in AL jc @@multi_out @@mappage: push ecx call EMS_MAP_REL_PAGE ;map page in DX:BX to phys page in AL pop ecx jc @@multi_out loop @@multi_loop call FlushTLB @@success: MOV ah,EMSS_OK @@multi_out: mov [esp+1],ah pop eax pop ebx ret ems4_map_multiple ENDP ; 6751: ; AH = 51h: EMS 4.0 reallocate pages for handle ; DX = handle ; BX = new page count for handle ; out: BX=pages assigned to handle ; ems4_realloc PROC movzx ecx, bx ; save new pages call EMS_GET_PAGES_ONE_HANDLE ;get curr pages in EBX, ESI->EMSHD and ah,ah jnz exit mov edi,[EMSPageAllocationStart] cmp ecx, ebx ; check new page count against original jb @@shrinkalloc je @@realloc_success ; no change needed sub ecx, ebx ; get no of additional pages call GetFreeEMSPages cmp ecx,eax ja @@toomany movzx eax,[esi].EMSHD.ehd_wIdx jmp @@test @@nextitem: lea esi,[edi+eax*4] mov ax,[esi].EMSPD.wNext @@test: cmp ax,-1 jnz @@nextitem call AllocateEMSPages ; allocate ECX pages jnc @@realloc_success @@toomany: mov ah,EMSS_OUT_OF_FREE_PAGES exit: ret ; trim off the trailing pages ; ECX=new page count, EBX=old page count @@shrinkalloc: xor ebx, ebx mov eax, ebx jmp @@test2 @@nextitem2: lea esi,[edi+eax*4] inc ebx @@test2: mov ax,[esi].EMSPD.wNext cmp ax,-1 jz @@realloc_success cmp ebx, ecx jnz @@nextitem2 jmp @@test3 @@freenext: lea esi,[edi+eax*4] call ReleaseEMSPage ;preserves registers @@test3: mov ax,-1 xchg ax,[esi].EMSPD.wNext cmp ax,-1 jnz @@freenext @@realloc_success: mov ebx,[ebp].Client_Reg_Struc.Client_EBX mov ah,EMSS_OK ret ems4_realloc ENDP ; 6752 ; AH = 52h: EMS 4.0 attribute related ; AL = 0/1/2, DX=handle, BL=0/1 ; AL=2: ; out AL=attr ems4_attribute PROC cmp al,2 jb @@get_set_attribute ja bad_subfunc ; this is an invalid subfunction ;-- AL==2 and AL==0 @@is_volatile: mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 0 ; al == 0, volatile attribute only mov ah,EMSS_OK ret ;-- AL==1 @@get_set_attribute: call EMS_TEST_HANDLE; only valid handles please or al,al ; 0 is get, 1 is set jz @@is_volatile ; only volatile here (none survive warm reboot) or bl,bl ; 0 is "make volatile" (true anyway) jnz @@cannot_make_nonvolatile mov ah,EMSS_OK ret @@cannot_make_nonvolatile: mov ah,EMSS_FEATURE_UNSUPP ; feature not supported ret ems4_attribute ENDP ; 6753: ; AH = 53h: EMS 4.0 get/set handle name ; AL = 0: get handle name in ES:DI ; AL = 1: set handle name in DS:SI ; DX = handle ; ems4_get_set_handle_name PROC cmp al,1 ja bad_subfunc ; this is an invalid subfunction jz @@ems4_setname call EMS_TEST_HANDLE movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES shl esi,4 movzx edi,di add edi,esi ; edi -> handle name buffer address (dest) mov esi, [EMSNameTable] movzx ecx,dl ; handle (index) lea esi, [esi+ecx*8] jmp @@ems4_getsetname @@ems4_setname: movzx edi, si ;ESI will be destroyed by next call call EMS_TEST_HANDLE movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_DS shl esi,4 add esi,edi ; esi -> handle name (source) ;--- resetting the name is always valid mov eax,[esi+0] or eax,[esi+4] jz @@reset_name ;--- check for a handle which already has this name ;--- return status A1h if one exists ;--- don't care if the handle found is the same as the current one mov edi,esi push esi call find_handle_by_name_int pop esi and ah,ah ;was a handle with this name found? mov ah,0A1h jz @@failed @@reset_name: mov edi, [EMSNameTable] movzx ecx,dl ; handle (index) lea edi, [edi+ecx*8] @@ems4_getsetname: movsd movsd mov ah,EMSS_OK @@failed: ret ems4_get_set_handle_name ENDP ; 6754: ; AH = 54h: EMS 4.0 get various handle info ; ; AL = 0: get handle directory into ES:DI ; AL = 1: search handle by name in DS:SI, return handle in DX ; AL = 2: get total handles in BX ems4_get_handle_info PROC cmp al,1 jb getallhand je @@find_handle_by_name cmp al,2 ja bad_subfunc ; this is an invalid subfunction ;-- AL=2 mov bx,EMS_MAX_HANDLES mov ah,EMSS_OK ret ; AL=0, write handle directory to caller buffer ; return in AL number of open handles ; return in ES:DI array of: ; WORD handle ; QWORD name getallhand: movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES shl esi,4 movzx edi,di add esi,edi mov edi, [EMSHandleTable] xor eax, eax ; AL will be count of open handles xor ecx, ecx push edx mov edx, [EMSNameTable] @@scan_handles: test [edi].EMSHD.ehd_bFlags, EMSH_USED jz @@free_handle inc eax mov [esi+0],cx push DWORD PTR [edx+ecx*8+0] ; copy handle name pop DWORD PTR [esi+2] push DWORD PTR [edx+ecx*8+4] pop DWORD PTR [esi+6] add esi,10 @@free_handle: add edi,size EMSHD inc ecx cmp cl, EMS_MAX_HANDLES jb @@scan_handles pop edx mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al mov ah,EMSS_OK ret ;--- AL=1 search handle by name ;--- in: DS:SI->handle name ;--- out: DX=handle @@find_handle_by_name: movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_DS shl edi,4 movzx esi,si add edi,esi find_handle_by_name_int:: ;find handle by name, edi=name push ebx mov eax,[edi+0] ; fetch to-be-searched name mov ebx,[edi+4] ; (8 byte binary string) if 0 ;MS Emm386 allows to search a handle with no name! or eax,eax jnz @@valid_search_term or ebx,ebx jz @@invalid_search_term @@valid_search_term: endif xor ecx,ecx mov edi, [EMSNameTable] mov esi, [EMSHandleTable] @@scan_for_name: test [esi].EMSHD.ehd_bFlags, EMSH_USED jz @@skipitem cmp [edi+0],eax jnz @@skipitem cmp [edi+4],ebx ; Note that open handles do not have jz @@found_handle ; to have a name. @@skipitem: add esi,size EMSHD add edi,8 inc ecx cmp cl,EMS_MAX_HANDLES jb @@scan_for_name pop ebx mov ah,0a0h ; "no corresponding handle found" ret @@found_handle: pop ebx mov edx,ecx mov ah,EMSS_OK ret if 0 @@invalid_search_term: pop ebx mov ah,0a1h ; "handle found had no name" ret endif ems4_get_handle_info ENDP ;--- client DS:SI -> map_and_jump ;--- AL=0 -> physical page numbers ;--- AL=1 -> segment addresses ems4_map_new_pagemap proc movzx edi, word ptr [ebp].Client_Reg_Struc.Client_DS shl edi,4 movzx esi,si add edi,esi ; edi -> map address buffer movzx ecx, word ptr [edi].map_and_jump.new_page_map_ptr+0 movzx esi, word ptr [edi].map_and_jump.new_page_map_ptr+2 push edi shl esi, 4 add esi, ecx movzx ecx, [edi].map_and_jump.new_page_map_len mov edi, esi call ems4_map_multiple_edi ;map ECX pages from EDI, DX=handle, AL=type pop edi and ah,ah ret ems4_map_new_pagemap endp ; 6755: ; AH = 55h: EMS 4.0 alter page map and jump ; AL = 0/1 ; DX = handle ; DS:SI -> map_and_jump structure ems4_alter_map_jump proc call ems4_map_new_pagemap jnz @@failed mov ecx,[edi].map_and_jump.target_address mov word ptr [ebp].Client_Reg_Struc.Client_EIP, cx shr ecx, 16 mov [ebp].Client_Reg_Struc.Client_CS, ecx @@failed: ret ems4_alter_map_jump endp ; 6756: ; AH = 56h: EMS 4.0 alter page map and call ; AL = 0/1/2 ; DX = handle ; if AL=0/1: [in] DS:SI -> map_and_call structure () ; if AL=2: [out] BX = additional stack space required ems4_alter_map_call proc cmp al,2 jz @@getstackspace call ems4_map_new_pagemap jnz @@failed if 0 ; nested execution consumes ring 0 stack, which is to avoid ;--- prepare nested execution ;--- and run the client proc push edx push eax call Begin_Nest_Exec movzx edx,word ptr [edi].map_and_call.target_address+0 movzx ecx,word ptr [edi].map_and_call.target_address+2 call Simulate_Far_Call call Resume_Exec call End_Nest_Exec pop eax pop edx ;--- set old state movzx ecx, [edi].map_and_call.old_page_map_len movzx esi, word ptr [edi].map_and_call.old_page_map_ptr+0 movzx edi, word ptr [edi].map_and_call.old_page_map_ptr+2 shl edi, 4 add edi, esi call ems4_map_multiple_edi @@failed: ret @@getstackspace: mov bx, 4 ret else ;--- this implementation copies 16-bit code onto the client's stack. ;--- this code calls int 67h, ah=50h (map multiple pages). mov esi, offset clproc mov al, byte ptr [ebp].Client_Reg_Struc.Client_EAX mov [esi+(_clal-clproc)],al movzx eax,[edi].map_and_call.old_page_map_len mov [esi+(_clcx-clproc)],ax mov [esi+(_cldx-clproc)],dx mov eax,[edi].map_and_call.old_page_map_ptr mov [esi+(_clsi-clproc)],ax shr eax,16 mov [esi+(_clds-clproc)],ax push edi movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_ESP movzx edi,word ptr [ebp].Client_Reg_Struc.Client_SS push edi shl edi, 4 sub ecx, sizeclproc push ecx mov word ptr [ebp].Client_Reg_Struc.Client_ESP,cx add edi, ecx mov ecx, sizeclproc rep movsb pop eax pop ecx pop edi push edx mov edx,eax call Simulate_Far_Call mov cx,word ptr [edi].map_and_jump.target_address+2 movzx edx,word ptr [edi].map_and_jump.target_address+0 call Simulate_Far_Call pop edx mov ah,EMSS_OK @@failed: ret @@getstackspace: mov bx, sizeclproc+4 ret endif align 4 ems4_alter_map_call endp ;------------------------------------------------------------- ; 6757: ; AH = 57h: EMS 4.0 move/exchange memory region ; AL = 0: move memory region ; AL = 1: exchange memory region ; DS:SI -> EMM57 ;-- this function must work even if no EMS page frame is defined! ;-- EMS regions may overlapp! E57REG struct e57_bTyp DB ? ; +0 memory type e57_wHdl DW ? ; +1 handle e57_wOfs DW ? ; +3 ofs e57_wSeg DW ? ; +5 segm./log. page E57REG ends ;--- memory type: ;--- 00: conv. memory ;--- 01: expanded memory ;--- handle: ;--- == NULL: conv. memory ;--- <> NULL: EMS handle ;--- locations ebp+xx are ok to store temp values ;--- but it isn't safe to store something there ;--- if interrupts are enabled! RegionSrc equ pPTE equ regSize equ linAddr equ ems4_move_memory PROC cmp al,1 ja bad_subfunc ; this is an invalid subfunction movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_DS movzx esi,si shl edi,4 add edi,esi ; edi -> EMS region buffer address ifdef __JWASM__ ;masm emits "string or text literal too long" @dprintf ?EMSDBG, <"EMM 57h: siz=%X src=%X/%X/%X/%X, dst=%X/%X/%X/%X">, [edi].EMM57.e57_dwSize,\ [edi].EMM57.e57_bSrcTyp, [edi].EMM57.e57_wSrcHdl, [edi].EMM57.e57_wSrcSeg, [edi].EMM57.e57_wSrcOfs,\ [edi].EMM57.e57_bDstTyp, [edi].EMM57.e57_wDstHdl, [edi].EMM57.e57_wDstSeg, [edi].EMM57.e57_wDstOfs endif mov ecx,[edi].EMM57.e57_dwSize test ecx,ecx je @@ok ; always succeeds if no bytes are moved mov ah,EMSS_REGLEN_EXCEEDS_1MB cmp ecx,65536*16 ; does region length exceed 1M? ja @@exit mov eax,[PageMapHeap] mov [regSize], ecx mov [pPTE], eax ; process region src + dst information lea edi,[edi+EMM57.e57_bSrcTyp] mov al,2 @@nextreg: mov [RegionSrc],ebx movzx ecx,[edi].E57REG.e57_wOfs movzx ebx,[edi].E57REG.e57_wSeg cmp [edi].E57REG.e57_bTyp,1 ;0 = conv, 1 = expanded jb @@calc_conv mov ah,EMSS_TYPE_UNDEFINED jnz @@exit ; destination is EMS, test if handle is valid mov dx, [edi].E57REG.e57_wHdl call EMS_TEST_HANDLE ; check if specified offset is outside logical page (must be < 4000h) mov ah,EMSS_OFFS_EXCEEDS_PGSIZ ; preload error code test ch,0C0h jnz @@exit ;--- map in EMS pages ;--- ecx = "offset" ;--- DX:BX = EMS page start push eax ;save AL counter add ecx,[regSize] mov esi,[pPTE] mov eax, esi sub eax, ?SYSBASE+?PAGETABSYS shl eax, 10 add eax, ?SYSBASE add ecx, eax ;let ecx point to end of region mov [linAddr],eax @@nextpagetomap: pushad call EMS_get_abs_page;get abs page of DX:BX in BX jc @@map_failed mov edi, esi call EMS_MAP_ABS_PAGE_EX ;requires EAX,BX,EDI to be set popad inc ebx ;next EMS page add eax, 4000h ;proceed with linear address add esi, 4*4 ;proceed with PTE pointer cmp eax,ecx jb @@nextpagetomap mov [pPTE],esi call FlushTLB mov ebx,[linAddr] movzx ecx, [edi].E57REG.e57_wOfs pop eax add ebx, ecx jmp @@regdone @@map_failed: popad pop eax mov ah,EMSS_LENGTH_EXCEEDED jmp @@exit @@calc_conv: shl ebx,4 ; convert seg to memory offset add ebx,ecx mov ah,EMSS_MBBOUNDARY_CROSSED ; conv memory region must not exceed 1 MB boundary mov ecx, [regSize] add ecx, ebx cmp ecx, 100000h ja @@exit @@regdone: add edi,size E57REG dec al jnz @@nextreg lea edx, [edi-sizeof EMM57] ;--- both regions processed ;--- dest address in EBX, src in RegionSrc mov ecx, [regSize] mov esi, [RegionSrc] mov edi, ebx ; if src and dest are EMS, test if they overlapp cmp edi, 100000h ; is dst expanded memory? jb @@nooverlapp cmp esi, 100000h ; is src expanded memory? jb @@nooverlapp mov ax, [edx].EMM57.e57_wSrcHdl cmp ax, [edx].EMM57.e57_wDstHdl ; are handles equal? jnz @@nooverlapp movzx eax, [edx].EMM57.e57_wDstSeg movzx ebx, [edx].EMM57.e57_wSrcSeg shl eax, 14 shl ebx, 14 or ax, [edx].EMM57.e57_wDstOfs or bx, [edx].EMM57.e57_wSrcOfs ;--- the problem case is: ;--- source < destination AND source + size > destination cmp ebx, eax ; source < destination? jnc @@nooverlapp ; no, use std copy add ebx, ecx ; ebx == source + size cmp eax, ebx ; destination >= source + size? jc @@overlapped @@nooverlapp: cmp byte ptr [ebp].Client_Reg_Struc.Client_EAX,0 jne @@xchg call _MoveMemory @@ok: mov ah,EMSS_OK ; zero ah, no error return ; exit with code in AH @@exit: @dprintf ?EMSDBG, <"=%X",10>, ah mov edx,[ebp].Client_Reg_Struc.Client_EDX mov ebx,[ebp].Client_Reg_Struc.Client_EBX ret @@overlapped: mov ah,EMSS_REGIONS_OVERLAPP ;for xchg, overlapp is invalid cmp byte ptr [ebp].Client_Reg_Struc.Client_EAX,0 jne @@exit std lea esi,[esi+ecx-1] lea edi,[edi+ecx-1] mov eax,ecx and ecx,3 REP MOVSB MOV ECX,EAX sub esi,3 sub edi,3 call _MoveMemory cld mov ah,EMSS_OVERLAP_OCCURED ;status "overlapp occured" jmp @@exit ;--- xchange memory regions ;--- esi=src, edi=dst, ecx=size @@xchg: mov ebx, [ebp].Client_Reg_Struc.Client_EFlags test bh,2 jz @@noenable call EnableInts sti @@noenable: mov edx, ecx shr ecx,2 jz @@xchgb @@xdw: mov eax,[edi] movsd mov [esi-4],eax dec ecx jnz @@xdw @@xchgb: mov ecx,edx and ecx,3 je @@xchgdone @@xb: mov al,[edi] movsb mov [esi-1],al dec ecx jnz @@xb @@xchgdone: test bh,2 jz @@ok cli call DisableInts jmp @@ok align 4 _MoveMemory: mov ebx,[PageMapHeap] mov eax,[pPTE] mov [PageMapHeap],eax call MoveMemory mov [PageMapHeap], ebx retn align 4 RegionSrc equ <> pPTE equ <> regSize equ <> linAddr equ <> ems4_move_memory ENDP ; 6758: ; AH = 58h: EMS 4.0 get addresses of mappable pages, number of mappable pages ; AL = 0: ES:DI -> buffer, returns no of pages in CX ; buffer item: ; WORD segment ; WORD physical page ; AL = 1: returns no of pages in CX ; the items in the buffer must be sorted in ascending segment order! ems4_get_mappable_info PROC cmp al,1 ja bad_subfunc ; only 0 and 1 allowed movzx ecx, [bEmsPhysPages] mov WORD PTR [ebp].Client_Reg_Struc.Client_ECX,cx ; mappable pages in CX cmp al,1 je @@nummap jecxz @@nummap movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES shl esi,4 movzx edi,di add edi,esi ; edi -> buffer address mov ah,0 @@mapinfo_loop: call @@getpage ; get phys page in AL movzx edx,al mov ah,0 shl eax,16 mov ah,byte ptr [edx+EMSPage2Segm] mov al,0 stosd loop @@mapinfo_loop @@nummap: mov ah,EMSS_OK ret ;--- get the next segment address to AH @@getpage: push ecx mov esi, offset EMSPage2Segm mov dl,-1 mov cl,[bEmsPhysPages] @@nextitem: lodsb sub al,ah jbe @@skipitem cmp al,dl ja @@skipitem mov dl,al mov dh,cl @@skipitem: loop @@nextitem mov al,[bEmsPhysPages] sub al,dh pop ecx ret ems4_get_mappable_info ENDP ; 6759: ; AH = 59h: EMS 4.0 get hardware config/get number of raw pages ; AL = 1: return raw pages in DX and BX ; AL = 0: get hardware config in ES:DI ems4_get_config PROC cmp al,1 ; only subfunctions 0+1 supported ja bad_subfunc je EMS_GET_UNALLOCATED_PAGE_COUNT movzx esi, WORD PTR [ebp].Client_Reg_Struc.Client_ES shl esi, 4 movzx edi, di add edi, esi mov esi,offset emm59_ mov ecx,size emm59_ shr 1 rep movsw mov ah,EMSS_OK ret ems4_get_config ENDP ; 675A: ; AH = 5ah: EMS 4.0 allocate handle and standard/raw pages ; in AL = 0/1 ; in BX = pages to allocate (may be 0) ; out DX = handle ; ems4_allocate_pages PROC cmp al,1 ; subfunction must be 0 or 1, we don't care if either ja bad_subfunc jmp allocate_pages_plus_zero ems4_allocate_pages ENDP ; 675B: alternate map (AM) and DMA register sets. ; AL=subfunction ; 0=get AM register set ; out: BL=current active AM register set or 00; ; if BL=0, ES:DI= ^ save area, set by subfunction 1 ; 1=set AM register set ; in: BL=new AM register set or 00; ; if BL=0, ES:DI= ^ save area ; 2=get AM save array size ; 3=allocate AM register set ; out: BL=AM register set # or 00 (00 means: no hardware AM register sets) ; 4=deallocate AM register set ; in: BL=AM register set ( 00 is allowed and nothing done ) ; 5=allocate DMA register set ; 6=enable DMA on AM register set ; 7=disable DMA on AM register set ; 8=deallocate DMA register set ems4_alt_map_reg_set PROC if ?SUPP5B cmp al,1 jb @@is00 jz @@is01 cmp al,3 jb @@is02 jz @@is03 cmp al,5 jb @@is04 jz @@is05 cmp al,7 jb @@is06 jz @@is07 cmp al,8 jz @@is08 jmp bad_subfunc @@is00: ; get AM register set mov bl,0 ; always return BL=0, since no hardware AM register sets are supported mov eax,[mapptr] and eax, eax jz @@noptr push eax movzx edi,ax shr eax,16 shl eax,4 add edi, eax mov al,0 call save_page_map_int pop eax @@noptr: mov word ptr [ebp].Client_Reg_Struc.Client_EDI,ax shr eax,16 mov word ptr [ebp].Client_Reg_Struc.Client_ES,ax jmp @@done @@is01: ; set AM register set mov ah,EMSS_ALT_MAPS_UNSUPP ;AM register sets not supported (bl must be 00) cmp bl,0 jnz @@exit ; no hardware AM register sets supported! ;--- v5.84: ;--- if the source array pointer ES:DI is invalid ( checksum invalid), ;--- error A3 (EMSS_SRCARRAY_INVALID) is returned! mov ax, word ptr [ebp].Client_Reg_Struc.Client_ES shl eax, 16 mov ax, word ptr [ebp].Client_Reg_Struc.Client_EDI mov [mapptr], eax and eax, eax jz @@done movzx esi, ax shr eax, 16 shl eax, 4 add esi, eax call restore_page_map_int ; will set AH ( either 00 or error A3h! ) jmp @@exit @@is02: ; get AM save area size mov dx,emm59_.e59_sizcont jmp @@done @@is03: ; allocate AM register set @@is05: ; allocate DMA register set mov bl,0 ; 00 is returned, indicating that hardware AM/DMA register sets aren't supported jmp @@done @@is04: ; deallocate AM register set @@is06: ; enable DMA on AM register set @@is07: ; disable DMA on AM register set @@is08: ; deallocate DMA register set mov ah,EMSS_ALT_MAPS_UNSUPP ;AM/DMA register sets not supported (bl must be 00) cmp bl,0 jnz @@exit @@done: mov ah,EMSS_OK @@exit: else mov ah,EMSS_ACCESS_DENIED ;access denied by OS endif ret ems4_alt_map_reg_set ENDP endif ;?LIM32 ; dynamically compute free EMS pages ; return free pages in EAX ; other registers preserved GetFreeEMSPages PROC push edx call Pool_GetFree16KPages ; free 16k pages in pool in EAX mov edx, [EMSPagesMax] sub edx, [EMSPagesUsed] ; edx == free EMS pages numbers jbe @@nofree cmp edx, eax ; if there is not enough free pages jnc @@eaxok ; to backup free EMS pages, then mov eax, edx ; use the lower value @@eaxok: @@ret: @dprintf ?POOLDBG, <"GetFreeEMSPages: eax=%X EMSMax-EMSUsed=%X",10>, eax, edx pop edx ret @@nofree: xor eax,eax jmp @@ret align 4 GetFreeEMSPages ENDP ; allocate an EMS page; ; IN: edi -> EMSPD for new page to allocate; ; OUT: C if fail; ; modifies EAX AllocateEMSPage PROC push edi push edx call Pool_Allocate16KPage ;return index in AX, subindex in DL jc @@exit ; set EMSPD.wIndex == descriptor index ; set EMSPD.bSubIdx == descriptor subindex mov [edi].EMSPD.wIndex,ax sub edi, [EMSPageAllocationStart] shr edi, 2 @dprintf ?POOLDBG, <"AllocateEMSPage ok, page=%X",10>, di add edi, [EMSPageAllocationEnd] mov [edi].EMSPD2.bSubIdx,dl inc [EMSPagesUsed] clc @@exit: pop edx pop edi ret AllocateEMSPage ENDP ;--- allocate ECX new pages for a handle ;--- inp: ESI=EMSPD ;--- modifies ECX, EDI, EAX, ESI AllocateEMSPages proc push ebx mov ebx,ecx MOV EDI,[EMSPageAllocationStart] ; mark the pages as used MOV ECX,[EMSPagesMax] nextitem: or eax,-1 REPNZ SCASD jnz nofreepg ; out of free pages? sub edi,4 call AllocateEMSPage ; allocate the page jc allocerr mov eax,edi sub eax,[EMSPageAllocationStart] shr eax,2 mov [esi].EMSPD.wNext, ax mov esi, edi add edi,4 if ?YIELDOPEN call Yield endif dec ebx JNZ nextitem pop ebx ret allocerr: nofreepg: pop ebx stc ret AllocateEMSPages endp ; release EMS page in AX ; destroy no registers ReleaseEMSPage PROC push eax push ecx push edx mov edx, [EMSPageAllocationStart] mov ecx, [EMSPageAllocationEnd] movzx eax,ax lea edx, [edx+eax*4] cmp edx, ecx jae @@ret ; out of range movzx ecx,[ecx+eax].EMSPD2.bSubIdx ; get sub index movzx eax,[edx].EMSPD.wIndex ; pool descriptor index cmp ax,-1 je @@ret call Pool_Free16KPage jc @@ret mov [edx].EMSPD.wIndex,-1 @dprintf ?POOLDBG, <"ReleaseEMSPage ok, EMSPD=%X",10>, edx dec [EMSPagesUsed] @@ret: pop edx pop ecx pop eax ret ReleaseEMSPage ENDP .text$03 ends .text$04 segment ;--- init EMS handle status table (255 handles * 8) ;--- inp: EDI -> free memory SetEMSHandleTable proc public mov ecx, EMS_MAX_HANDLES * size EMSHD call HeapMalloc MOV [EMSHandleTable],EDI; set start of EMS handle table. MOV EAX,0FF01FFFFh ; Handle 0 is reserved. STOSD mov eax,0FF00FFFFh MOV ECX, EMS_MAX_HANDLES-1 ; fill the rest REP STOSD ret SetEMSHandleTable endp SetEMSStateTable proc public mov ecx, EMS_MAXSTATE * size EMSSTAT call HeapMalloc mov [EMSStateTable],EDI mov eax, -1 mov ecx, EMS_MAXSTATE*4 rep stosd ret SetEMSStateTable endp SetEMSNameTable proc mov ecx,8*EMS_MAX_HANDLES call HeapMalloc mov [EMSNameTable],EDI mov eax,'TSYS' ; store "SYSTEM" as first handle name stosd mov eax,'ME' stosd mov ecx,8*(EMS_MAX_HANDLES-1)/4 xor eax,eax rep stosd ret SetEMSNameTable endp ;--- init EMS variables ;--- ESI -> JEMMINIT EMS_Init1 proc public movzx eax, [esi].JEMMINIT.MaxEMSPages mov dl, [esi].JEMMINIT.NoFrame mov [EMSPagesMax],eax mov al, [esi].JEMMINIT.NoEMS mov [bNoEMS],al cmp al,0 jz @@emsactive mov dl,1 mov eax,[dwRes] mov byte ptr [eax + DEVICE_NAME + 3],'Q' ;EMMXXXX0 -> EMMQXXX0 @@emsactive: mov [bNoFrame], dl cmp dl, 0 jnz @@noframe mov [bEmsPhysPages], 4 movzx eax, byte ptr [esi].JEMMINIT.Frame+1 mov edx, eax mov dh,dl add dh,4 mov word ptr [EMSPage2Segm+0], dx add dx,0808h mov word ptr [EMSPage2Segm+2], dx if 0 ;not needed. Once the pages are remapped, the bits are cleared ;--- reset the "global page" attribute for the EMS frame PTEs lea eax, @GetPTEAddr(eax*4+?PAGETAB0) mov ecx,16 @@nextitem: and byte ptr [eax+1],not 1 add eax,4 loop @@nextitem endif @@noframe: ;--- calc mappable EMS pages ;--- if a frame is set, physical pages 0-3 have been reserved already. ;--- segments A000-FFFF will get the next page numbers (up to v5.79: only if NORAM is set). ;--- if A000-B7FF is added to conv. memory, then they get high numbers. ife ?LIM32 mov ebx,[esi].JEMMINIT.PageMap if ?INITDBG and ?EMSDBG xor ecx,ecx @dprintf , <"SysMem table:",10> @@nextitemx: @dprintf , <"%c">, byte ptr [ebx+ecx] inc ecx test cl,0Fh jnz @@nextitemx test cl,30h jz @F @dprintf , <'-'> jmp @@nextitemx @@: @dprintf , <10> and cl,cl jnz @@nextitemx endif ;--- up to v5.79: ;--- if NORAM, include 'IIII', then 'RRRR' ;--- if RAM, include 'RRRR' only ;--- since v5.80: NORAM isn't relevant anymore. ;--- if A000 is included, then scan begins with C000-FC00, then 4000-BC00 ;--- if A000 is NOT included, then scan begins with A000-FC00, then 4000-9C00 mov dh,0 ;DH=num mappable pages (below C000/A000?) xor ecx,ecx mov cl,0C0h cmp dword ptr [ebx+0A0h], 'IIII' jz @F mov cl,0A0h @@: mov dl,cl @@nextitem2: cmp dword ptr [ebx+ecx], 'RRRR' jz @F cmp dword ptr [ebx+ecx], 'IIII' jnz @@skipitem @@: movzx eax,[bEmsPhysPages] cmp al,?MAXPHYSPG jnc @@skipitem mov [EMSPage2Segm+eax],cl inc eax inc dh mov [bEmsPhysPages],al if 0 ;is not needed, since once the page is remapped, the bit is reset lea eax, @GetPTEAddr(?PAGETAB0+ecx*4) and byte ptr [eax+00+1],not 1 ;reset "global" attribute and byte ptr [eax+04+1],not 1 ;reset "global" attribute and byte ptr [eax+08+1],not 1 ;reset "global" attribute and byte ptr [eax+12+1],not 1 ;reset "global" attribute endif @@skipitem: add cl,4 jnz @F mov cl,byte ptr [esi].JEMMINIT.Border+1 @@: cmp cl,dl jnz @@nextitem2 mov [bPagesConv],dh movzx edx,dh add word ptr [EMSPagesMax],dx jns @F mov word ptr [EMSPagesMax],MAX_EMS_PAGES_POSSIBLE @@: @dprintf ?INITDBG, <"EMS_Init1: phys EMS pages=%X",10>, edx shl edx,2 add [pmem.dwMaxMem4K],edx endif ;?LIM32 movzx eax,[bEmsPhysPages] inc eax ;+ 1 dword for checksum shl eax,2 mov emm59_.e59_sizcont, ax if ?VCPI mov al, [esi].JEMMINIT.NoVCPI mov [bNoVCPI],al endif ret EMS_Init1 endp ;--- alloc the EMS management tables ;--- ESI -> JEMMINIT ;--- EDI -> free space (linear address) EMS_Init2 proc public ;--- set the EMS tables ; alloc EMS handle/status/name table (4/8/8 bytes) call SetEMSHandleTable call SetEMSStateTable call SetEMSNameTable @dprintf ?INITDBG, <"EMS handle table=%X, state table=%X, name table=%X",10>, EMSHandleTable, EMSStateTable, EMSNameTable ;--- allocate and init EMS page descriptor array (EMSPD) ;--- size of EMSPD is 4 bytes, it ;--- consists of 2 words, first is a pointer to the next EMS page ;--- second is the pool descriptor index of this EMS page mov ecx,[EMSPagesMax] mov [EMSPageAllocationStart],edi @dprintf ?INITDBG, <"EMS pages=%X at %X",10>, ecx, edi push ecx or eax,-1 rep stosd pop ecx mov [EMSPageAllocationEnd],edi @dprintf , <"EMS page alloc start/end=%X/%X",10>, [EMSPageAllocationStart], [EMSPageAllocationEnd] ;--- behind the EMS EMSPD array comes an array of bytes ;--- which are the "nibble" offsets of the descriptor's bit array inc eax rep stosb @dprintf , <"EMS array of subindices end=%X",10>, edi ;--- init EMS SYSTEM handle ife ?LIM32 movzx ecx,[bPagesConv] ;any mappable conv. memory pages? jecxz @@nomap2 pushad push ecx shl ecx, 4 ;16k -> 1k pages movzx edi,[esi].JEMMINIT.Border shr edi,6 ;paragraphs -> 1K mov al, PBF_DONTFREE or PBF_DONTEXPAND call Pool_AllocBlocksForEMB pop ebx MOV EDI,[EMSPageAllocationStart] mov esi,[EMSHandleTable] xor ecx,ecx @@nextpage: call AllocateEMSPage mov [esi].EMSPD.wNext, cx mov esi,edi add edi,4 inc ecx cmp ecx,ebx jnz @@nextpage popad @@nomap2: endif @dprintf ?INITDBG, <"EMSInit2 done",10> ret EMS_Init2 endp ;--- for NoPool: check if EMSPagesMax has to be adjusted ;--- in: eax = 4k pages still free in fixed XMS memory block EMS_CheckMax proc public shr eax, 2 mov edx, [EMSPagesMax] sub edx, [EMSPagesUsed] cmp eax, edx jnc @@isnodecrease sub edx, eax sub [EMSPagesMax],edx @@isnodecrease: ret EMS_CheckMax endp .text$04 ends END ================================================ FILE: src/EMS.INC ================================================ ;--- EMS status codes EMSS_OK equ 00h EMSS_SOFTWARE_ERR equ 80h ;unexpected error EMSS_HARDWARE_ERR equ 81h ;should never occur with EMM emulators EMSS_EMM_BUSY equ 82h ;should never occur with EMS 3.2+ EMSS_INVALID_HANDLE equ 83h EMSS_INVALID_FUNCTION equ 84h ;function code in AH not defined EMSS_NO_MORE_HANDLES equ 85h ;all handles in use EMSS_CONTEXT_EXISTS equ 86h ;45h: try to free a handle with saved context EMSS_OUT_OF_PAGES equ 87h ;43h, 51h, 5A00h, 5A01h EMSS_OUT_OF_FREE_PAGES equ 88h ;43h, 51h, 5A00h, 5A01h EMSS_ZERO_PAGES equ 89h ;43h EMSS_LOG_PAGE_INVALID equ 8Ah ;44h, 50h, 55h, 56h, 57h EMSS_PHYS_PAGE_INVALID equ 8Bh ;44h, 4Fh, 50h, 55h, 56h EMSS_CONTEXT_STACK_FULL equ 8Ch ;46h EMSS_STATE_ALREADY_SAVED equ 8Dh;47h EMSS_NO_STATE_IS_SAVED equ 8Eh ;48h EMSS_INVALID_SUBFUNC equ 8Fh ;4Eh, 4Fh, 50h, 52h, 53h, 54h, 57h, 58h, 59h, 5Bh EMSS_UNDEF_ATTR_TYPE equ 90h ; EMSS_FEATURE_UNSUPP equ 91h ;52h EMSS_OVERLAP_OCCURED equ 92h ;5700h EMSS_LENGTH_EXCEEDED equ 93h ;57h EMSS_CONVEMS_OVERLAPP equ 94h ;57h (jemm never returns this code) EMSS_OFFS_EXCEEDS_PGSIZ equ 95h ;57h EMSS_REGLEN_EXCEEDS_1MB equ 96h ;57h EMSS_REGIONS_OVERLAPP equ 97h ;5701h EMSS_TYPE_UNDEFINED equ 98h ;57h EMSS_ALT_MAPS_UNSUPP equ 9Ch ;5B01h, 5B04h, 5B06h, 5B07h, 5B08h EMSS_INVALID_ALT_MAP equ 9Dh ;5Bh (jemm never returns this code) EMSS_MBBOUNDARY_CROSSED equ 0A2h;57h EMSS_SRC_ARRAY_INVALID equ 0A3h;4E01h, 5B01h EMSS_ACCESS_DENIED equ 0A4h;5Bh ================================================ FILE: src/EMS32.INC ================================================ ;--- this is the Jemm EMS 3.2 implementation (functions 40h - 4Eh) ;--- since this part is copyright Harald Albrecht/Tom Ehlert, ;--- it has been extracted into a separate file. ;--- It's included by EMS.ASM ;--- API: EBP -> client registers ;--- out: BX, DX and AH will be copied to client registers ;--- change of other registers have to be done directly ; ; AH = 40h: return the actual state of the EMM-driver. ; EMS_GET_STATUS PROC MOV AH,EMSS_OK RET EMS_GET_STATUS ENDP ; ; AH = 41h: request the segment address of the EMS-window ; EMS_GET_PAGE_FRAME_ADDRESS PROC mov ah,80h cmp [bNoFrame],0 jnz @@BYE MOV AH,EMSS_OK ; No error occurred MOV BH,[EMSPage2Segm+0] ; Segment address of EMS-Window/Frame MOV BL,0 @@BYE: RET EMS_GET_PAGE_FRAME_ADDRESS ENDP ; ; AH = 42h: Request number of free + total EMS-pages ; out: BX=free pages ; out: DX=total pages EMS_GET_UNALLOCATED_PAGE_COUNT PROC call GetFreeEMSPages MOV EBX,EAX MOV EDX,[EMSPagesMax] ; total EMS pages ; follow MS-DOS EMM386 7.x lead and don't throttle pages on NOEMS ; cmp cs:[bNoEMS],0 ; je @@unalloc_ret ; or bx,bx ; je @@unalloc_ret ; mov bx,1 ; only show maximum of 1 EMS page if NOEMS set ;@@unalloc_ret: MOV AH,EMSS_OK RET EMS_GET_UNALLOCATED_PAGE_COUNT ENDP ; ; AH = 43h: Reserve pages for new EMS handle ; in BX = EMS pages to reserve ; out AH=00, DX = handle EMS_ALLOCATE_PAGES PROC MOV AH,EMSS_ZERO_PAGES ; "Request to reserve null pages" AND BX,BX JZ SHORT @@BYE allocate_pages_plus_zero:: ;this entry allows to alloc zero pages! MOV AH,EMSS_OUT_OF_PAGES ; "Not enough pages available" movzx ebx,bx CMP EBX,[EMSPagesMax] JA SHORT @@BYE call GetFreeEMSPages CMP EBX, EAX MOV AH,EMSS_OUT_OF_FREE_PAGES ; "Not enough free pages available" JA SHORT @@BYE MOV ESI,[EMSHandleTable] ; Now search for a free Handle in the table MOV CL,EMS_MAX_HANDLES @@SEARCH: test [ESI].EMSHD.ehd_bFlags,EMSH_USED; Is there one free ... ? JZ SHORT @@FOUND ADD ESI,size EMSHD dec cl jnz @@SEARCH MOV AH,EMSS_NO_MORE_HANDLES ; "No more free Handles" @@BYE: RET @@FOUND: or [ESI].EMSHD.ehd_bFlags,EMSH_USED MOV DX,EMS_MAX_HANDLES ; Set in DX now the actual Handle- SUB DL,CL ; number or ebx,ebx ; zero page allocation? je @@allocate_exit ; then we're done mov ecx, ebx call AllocateEMSPages jc @@nofind @@allocate_exit: MOV AH,EMSS_OK RET @@nofind: @dprintf ?POOLDBG, <"EMS_ALLOCATE_PAGES: @@nofind reached, BX=%X",10>, bx call EMS_DEALLOCATE_PAGES ;free the handle in DX MOV AH,EMSS_OUT_OF_FREE_PAGES ; "Not enough pages available anymore" ret align 4 EMS_ALLOCATE_PAGES ENDP ; AH = 44h: map logical page DX:BX into physical page AL (the EMS-window) ; AL = physical page (0-3) ; DX = handle ; BX = logical page # (or FFFF to unmap the page) ; EMS_MAP_HANDLE_PAGE PROC CALL EMS_TEST_HANDLE CMP AL,[bEmsPhysPages] ; not" Only pages 0..3 JAE SHORT @@PAGE_TOO_LARGE ; are allowed! PUSH EBX ; save BX (since it is changed) call EMS_MAP_REL_PAGE ; map/unmap the page in DX:BX to AL jc @@done call FlushTLB MOV AH,EMSS_OK @@done: POP EBX RET @@PAGE_TOO_LARGE: @@ERR8B: MOV AH,EMSS_PHYS_PAGE_INVALID ; "physical page does not exist" RET EMS_MAP_HANDLE_PAGE ENDP ; AH = 45h: Release reserved memoryspace again ; DX = handle to release ; any pages of this handle mapped in page frame remain mapped! ; this is same behaviour as MS Emm386 EMS_DEALLOCATE_PAGES PROC CALL EMS_TEST_HANDLE and dx,dx jz ems_release_null MOV AH,EMSS_CONTEXT_EXISTS ; "A saved state still CMP [ESI].EMSHD.ehd_bSS,-1 ; exists" ? JNZ SHORT @@BYE and [esi].EMSHD.ehd_bFlags,not EMSH_USED MOV EDI,[EMSPageAllocationStart] xor eax, eax jmp @@test @@LOOP: lea esi,[edi+eax*4] call ReleaseEMSPage ; preserves registers @@test: mov ax,-1 xchg ax,[esi].EMSPD.wNext cmp ax,-1 jnz @@LOOP ; zero handle name on free mov edi, [EMSNameTable] xor esi, esi movzx ecx, dx mov DWORD PTR [edi+ecx*8+0],esi mov DWORD PTR [edi+ecx*8+4],esi MOV AH,EMSS_OK @@BYE: RET ems_release_null: ife ?LIM32 push ebx xor ebx,ebx call ems4_realloc pop ebx else mov ah,EMSS_OK endif ret EMS_DEALLOCATE_PAGES ENDP ; ; AH = 46h: determine Version-number of EMM ; EMS_GET_VERSION PROC if ?LIM32 mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 32h else mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 40h endif MOV AH,EMSS_OK RET EMS_GET_VERSION ENDP ;--- save the frame window mapping state EMS_SaveFrameToEdi proc mov cl,4 EMS_SaveFrameToEdi endp ;fall through ;--- save CL (0 - bEmsPhysPages) pages to ESI ;--- EDI -> array of DWORDs, where PTEs will be stored ;--- modifies CL EMS_SavePagesToEdi PROC PUSH EAX PUSH EDX PUSH ESI INC CL xor edx,edx mov esi,offset EMSPage2Segm jmp @@test @@NEXTPAGE: lodsb movzx eax,al lea eax, @GetPTEAddr(EAX*4+?PAGETAB0) MOV EAX, [EAX] mov al,dl inc edx STOSD @@test: DEC CL jnz @@NEXTPAGE POP ESI POP EDX POP EAX RET EMS_SavePagesToEdi ENDP ;--- restore the frame window mapping state EMS_RestoreFrameFromEsi PROC mov cl, 4 EMS_RestoreFrameFromEsi ENDP ;fall through ;-- restore CL (0 - bEmsPhysPages) pages ;-- esi -> array of DWORDs, containing PTEs EMS_RestorePagesFromEsi PROC push edi INC CL jmp @@test @@NEXTPAGE: lodsd movzx edi,al movzx edi,[EMSPage2Segm+EDI] lea edi, @GetPTEAddr(EDI*4+?PAGETAB0) MOV al,7 stosd add eax,1000h stosd add eax,1000h stosd add eax,1000h stosd @@test: DEC CL jnz @@NEXTPAGE pop edi call FlushTLB MOV AH,EMSS_OK ; report OK (because of functions $4E01/$4E02) RET EMS_RestorePagesFromEsi ENDP ; AH = 47h: Save status of EMS page frame to internally ; maintained buffers (max 1 for each handle) ; in: DX = handle ; it might be that there are pages mapped into the EMS page frame ; which don't belong to any handles (anymore)! ; EMS_SAVE_PAGES PROC mov ah,EMSS_SOFTWARE_ERR cmp [bNoFrame],0 jnz @@BYE CALL EMS_TEST_HANDLE MOV AH,EMSS_STATE_ALREADY_SAVED ; "State for Handle already saved" CMP [ESI].EMSHD.ehd_bSS,-1 JNZ @@BYE mov edi,[EMSStateTable] xor eax,eax @@nextitem: cmp [edi].EMSSTAT.dPg0,-1 jz @@found add edi,size EMSSTAT inc eax cmp eax,EMS_MAXSTATE jnz @@nextitem mov ah,EMSS_CONTEXT_STACK_FULL ret @@found: mov [esi].EMSHD.ehd_bSS,al call EMS_SaveFrameToEdi MOV AH,EMSS_OK @@BYE: RET EMS_SAVE_PAGES ENDP ; AH = 48h: Restore saved state of the EMS-window ; DX = handle EMS_RESTORE_PAGES PROC mov ah,EMSS_SOFTWARE_ERR cmp [bNoFrame],0 jnz @@BYE CALL EMS_TEST_HANDLE MOV AH,EMSS_NO_STATE_IS_SAVED ; "A saved state does not exist" CMP [ESI].EMSHD.ehd_bSS,-1 JZ SHORT @@BYE mov al,-1 xchg al,[ESI].EMSHD.ehd_bSS movzx eax,al shl eax,4 ;size of EMSSTAT mov esi,[EMSStateTable] add esi, eax push esi CALL EMS_RestoreFrameFromEsi pop esi mov [esi].EMSSTAT.dPg0,-1 MOV AH,EMSS_OK @@BYE: RET EMS_RESTORE_PAGES ENDP ; log the failure EMS_NOT_IMPL PROC @dprintf ?EMSDBG, <"Unimplemented EMS function called, ax=%X",10>, ax MOV AH,EMSS_INVALID_FUNCTION ; "Invalid function code" RET EMS_NOT_IMPL ENDP ; AH = 4Bh: return Number of open Handles in BX EMS_GET_OPEN_HANDLES_COUNT PROC MOV ESI,[EMSHandleTable] ; Search Handle-status-table for MOV ECX,EMS_MAX_HANDLES ; assigned/given handles XOR EBX,EBX XOR EAX,EAX @@LOOP: test [ESI].EMSHD.ehd_bFlags,EMSH_USED; Free ? setnz al ADD ESI,size EMSHD ; Next entry add ebx,eax loop @@LOOP MOV AH,EMSS_OK RET EMS_GET_OPEN_HANDLES_COUNT ENDP ; ; AH = 4Ch: return number of reserved pages for a handle ; inp: handle in DX. ; out: pages in BX. ; modifies ESI, EDI, EBX, EAX EMS_GET_PAGES_ONE_HANDLE PROC CALL EMS_TEST_HANDLE movzx EAX,[esi].EMSHD.ehd_wIdx MOV EDI,[EMSPageAllocationStart] XOR EBX,EBX jmp @@test @@LOOP: mov ax,[edi+eax*4].EMSPD.wNext INC EBX @@test: cmp ax,-1 jnz @@LOOP MOV AH,EMSS_OK RET EMS_GET_PAGES_ONE_HANDLE ENDP ; ; AH = 4Dh: determine Number of reserved pages for all Handles ; ES:DI -> array of 2 WORD entries (handle, pages) ; out: AH=00 success, BX=number of handles stored in array ; EMS_GET_PAGES_ALL_HANDLES PROC MOVZX ESI,WORD PTR [ebp].Client_Reg_Struc.Client_ES SHL ESI,4 MOVZX EDI,DI ADD ESI,EDI ; ESI -> array PUSH EDX MOV EDI,[EMSHandleTable] XOR EAX,EAX ; current Handle-Number XOR EBX,EBX ; count open handles @@NEXT_HANDLE: test [EDI].EMSHD.ehd_bFlags,EMSH_USED ; handle free? JZ SHORT @@NEXT INC EBX ; One more Handle is open... MOV [ESI+0],AX ; Place handle PUSH EDI movzx ecx,[edi].EMSHD.ehd_wIdx MOV EDI,[EMSPageAllocationStart] ; get pages for handle XOR EDX,EDX ; EDX is counter jmp @@test @@LOOP: inc edx mov cx,[edi+ecx*4].EMSPD.wNext @@test: cmp cx,-1 jnz @@LOOP MOV [ESI+2],DX ; Set number of handle's pages add ESI, 4 POP EDI @@NEXT: ADD EDI,size EMSHD INC EAX CMP AL,EMS_MAX_HANDLES ; All Handles processed ? JB @@NEXT_HANDLE POP EDX MOV AH,EMSS_OK RET EMS_GET_PAGES_ALL_HANDLES ENDP ; AH = 4Eh: Get & Set Page Map ; AL = 0,1,2,3 ; AL = 0: ES:DI -> array to get info ; AL = 1: DS:SI -> array to set info ; AL = 2: DS:SI -> array to set info, ES:DI -> array to get info ; AL = 3: AL returns size of array (bytes) @CHKSUM MACRO REG MOV EAX,[REG+04] ; Calculate checksum ADD EAX,[REG+08] ADD EAX,[REG+12] ADD EAX,[REG+16] ENDM EMS_GET_SET_PAGE_MAP PROC CMP AL,3 ; Subfunction 0 to 3 ? JA bad_subfunc JZ SHORT @@SUBF_3 ; Size of field CMP AL,1 JZ SHORT @@SUBF_1 ; Set Page Map ; AL = 2: Get & Set Page Map ; AL = 0: Get Page Map - save Hardwarestatus @@SUBF_0: MOVZX ECX, WORD PTR [ebp].Client_Reg_Struc.Client_ES; ES:DI ^ convert from statusfield in SHL ECX, 4 ; usual REAL-Mode-Format MOVZX EDI, DI ADD EDI, ECX save_page_map_int:: ; <--- internal, EDI=save area, AL=0 ( ax=5B00h ) PUSH EAX ; save Subfunctioncode PUSH edi add edi,4 mov cl, [bEmsPhysPages] CALL EMS_SavePagesToEdi pop edi @CHKSUM EDI ; Calculate checksum and ... stosd POP EAX ; restore and examen subfunctioncode CMP AL,2 ; if subfuction 2 is wanted, JZ SHORT @@SUBF_1 ; then also set new map MOV AH,EMSS_OK RET ; Subf. 1: Set Page Map - restore Hardwarestatus @@SUBF_1: MOVZX ECX,WORD PTR [ebp].Client_Reg_Struc.Client_DS ; DS:SI ^ convert from statusfield in SHL ECX,4 ; usual REAL-Mode-Format MOVZX ESI,SI ADD ESI,ECX restore_page_map_int:: ; <--- internal: ESI=save area ( ax=5B01h ) @CHKSUM ESI ; Calculate checksum and check it CMP [ESI],EAX JNZ @@CHKERR lodsd ; skip checksum mov cl, [bEmsPhysPages] JMP EMS_RestorePagesFromEsi ; Checksum is incorrect ! @@CHKERR: ; MOV AH,0A3H ; data is destroyed ! MOV AH, EMSS_SRC_ARRAY_INVALID RET ; Subf. 3: return size of the field in AL @@SUBF_3: MOV AL, byte ptr emm59_.e59_sizcont MOV byte ptr [ebp].Client_Reg_Struc.Client_EAX, al MOV AH,EMSS_OK RET EMS_GET_SET_PAGE_MAP ENDP bad_subfunc: MOV AH,EMSS_INVALID_SUBFUNC ; Invalid subfunctioncode ! RET align 4 ; Check a given Handle (in DX) for validness. ; In case the Handle is invalid the returnaddress is thrown away ; and afterwards through RET returned to dispatcher. ; Else ESI will point to the handle in EMSHandleTable array EMS_TEST_HANDLE PROC CMP DX,EMS_MAX_HANDLES ; out of area ? JAE @@INVALID MOVZX ESI,DL ; get a pointer to the handle table SHL ESI,2 ; size is 4 ADD ESI, [EMSHandleTable] test [ESI].EMSHD.ehd_bFlags, EMSH_USED JZ @@INVALID RET @@INVALID: ADD ESP,4 ; throw away call(ing)-address MOV AH,EMSS_INVALID_HANDLE ; "Handle invalid" RET align 4 EMS_TEST_HANDLE ENDP ================================================ FILE: src/EMU.ASM ================================================ ;--- privileged opcode emulation ;--- copyright Tom Ehlert ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ?EMUCLTS equ 0 ; 1=emulate CLTS ( resets TS bit in CR0 ) ;--- publics/externals include extern32.inc .text$01 SEGMENT RunEmuInstr: DB 0Fh EmuInstr DB 90h,90h ; run self-modifying code here ret .text$01 ends .text$03 segment @v86popregX macro mov esp,ebp POPAD endm ;--- emulate some privileged 0Fh opcodes (not HLT) ;--- EBP -> client struct ;--- ESP = dwStackCurr ;--- ESI -> linear address of v86 CS:IP ;--- AL = 0Fh ;--- this is not called, but jumped to by V86_Exception. ExtendedOp proc public mov al,[esi+1] if ?EMUCLTS cmp al,6 je @@clts endif cmp al,9 je @@wbinvd cmp al,8 je @@invd cmp al,30h je @@wrmsr cmp al,31h ; whether RDTSC is privileged depends on CR4, bit 2 (TSD) je @@rdtsc cmp al,32h je @@rdmsr cmp al,20h jb V86_Exc0D ; not an opcode we emulate cmp al,23h ja V86_Exc0D ; opcodes 0F 20 xx to 0F 23 xx emulated via self-modifying code mov ah,[esi+2] ; get third byte of opcode mov WORD PTR [EmuInstr+0],ax add [ebp].Client_Reg_Struc.Client_EIP,3 ; jump over emulated instruction @v86popregX call RunEmuInstr add esp, CRS_SIZEX ; skip error code & int# iretd if ?EMUCLTS @@clts: .386p clts .386 jmp @@invdshare endif @@invd: .486p invd .386 jmp @@invdshare @@wbinvd: .486p wbinvd .386 @@invdshare: @v86popregX jmp @@twoeat @@wrmsr: @v86popregX .586p wrmsr .386 jmp @@twoeat ; early pentiums and such will throw an exception on rdtsc instruction in V86 ; regardless of CR4 setting, later CPU versions won't @@rdtsc: @v86popregX .586 rdtsc .386 jmp @@twoeat @@rdmsr: @v86popregX .586p rdmsr .386 @@twoeat: add esp, CRS_SIZEX ; skip error code & int# add [esp].IRETDV86._Eip,2 ; jump over instruction iretd align 4 ExtendedOp endp .text$03 ends END ================================================ FILE: src/EXTERN32.INC ================================================ ;--- publics for Jemm32 @seg macro name_, align_ name_ SEGMENT align_ FLAT public 'CODE' endm ;--- sections: ;--- .text$01: shared page - accessible by VCPI clients (global vars, code ) ;--- .text$01g: GDT/IDT if ?SHAREDGDT/?SHAREDIDT=1 ;--- .text$01y: exception strings ;--- .text$02: VCPI mode switch routines ;--- .text$03: normal code/data ;--- .text$04: init code, not copied to extended memory @seg .text$01, externdef GDT_PTR:fword externdef IDT_PTR:fword externdef V86CR3:dword externdef V86GDT:DESCRIPTOR externdef dwStackCurr:dword if ?DYNTRAPP60 externdef dwTSS:dword endif externdef dwMaxPhysMem:dword externdef PageMapHeap:dword externdef pmem:PAGEMEM externdef vdsstat:DMABUFF externdef dwRes:dword externdef dwRSeg:dword if ?HOOK13 externdef dwRFlags:dword else externdef bDiskIrq:byte endif externdef bpstart:dword externdef wMasterPICBase:word externdef wSlavePICBase:word externdef bNoInvlPg:byte externdef bV86Flags:byte externdef DmaChn:DMAREQ externdef bBpTab:byte externdef bNoPool:byte externdef OldInt06:dword externdef OldInt19:dword externdef OldInt67:dword ;externdef pPg0PartEnd:dword externdef pSavedVecs:dword externdef dwV86IDT:dword externdef dwFeatures:dword externdef bIs486:byte externdef bPageMask:byte externdef bBpBack:byte externdef EMSPageAllocationStart:dword externdef EMSPageAllocationEnd:dword externdef bNoEMS:byte externdef bNoFrame:byte externdef bNoVCPI:byte externdef EMSPage2Segm:byte externdef EMSPagesMax:dword externdef bNoA20:byte externdef XMS_Handle_Table:XMS_HANDLETABLE externdef UMBsegments:UMBBLK if ?INTEGRATED externdef A20Index:BYTE else externdef XMSCtrlHandle:WORD endif if ?KD externdef bKD:byte endif .text$01 ENDS @seg .text$03, ;--- Jemm32 publics externdef bptable:BPTABLE if ?FASTMON externdef int00:near endif externdef V86_Monitor:near externdef Begin_Nest_Exec:near externdef End_Nest_Exec:near externdef Simulate_Far_Call:near externdef Simulate_Far_Ret:near externdef Simulate_Iret:near externdef Resume_Exec:near externdef Yield:near externdef Simulate_IO:near externdef Simulate_IO_trap:near externdef MoveMemoryPhys:near if ?XMS35COMPAT externdef MoveMemoryPhysEx:near endif externdef MapPhysPages:near externdef MapPhysPagesEx:near externdef MoveMemory:near externdef EnableInts:near externdef DisableInts:near if ?EXC10 externdef Int10_Entry:near endif externdef Int15_Entry:near externdef Int15_Entry_Ex:near externdef SetVME:near externdef V86_Exc0D:near if ?I41SUPP externdef Int41_Entry:near endif externdef vmm_service_table:VMM_SERV_TABLE ;--- DEBUG publics externdef VPUTCHR:near ;--- EMS/VCPI publics externdef VCPI_Call_Table:dword externdef VCPI_V86toPM:near externdef Int67_Entry:near externdef Int67_V86Entry:near ;--- XMS/A20 publics externdef xms_handler:near externdef xms_ext_realloc_emb:near externdef A20_Set:near externdef A20_Handle60:near externdef A20_Handle64:near externdef A20_Handle92:near ;--- UMB publics externdef umb_handler:near ;--- VDS publics externdef vds_handler:near externdef VDS_Call_Table:dword externdef VDS_Exit:near ;--- Pool publics externdef Pool_GetFree4KPages:near externdef Pool_GetFree16KPages:near externdef Pool_Allocate4KPage:near externdef Pool_Allocate16KPage:near externdef Pool_Free4KPage:near externdef Pool_Free16KPage:near externdef Pool_FreeAllBlocks:near externdef Pool_AllocBlocksForEMB:near externdef Pool_GetPhysAddr:near ;--- DMA translation publics externdef Dma_CopyBuffer:near externdef Dma_HandleDmaPorts8:near externdef Dma_HandleDmaPorts16:near externdef Dma_HandlePagePorts:near if ?ALT_TRAPHDL externdef ISA_DMA_Traphandler:near endif ;--- EMU publics externdef ExtendedOp:near ;--- I15 publics externdef I15_Simulate87:near ;--- DEV publics externdef EMMXXXX0_Strategy:near externdef EMMXXXX0_Interrupt:near .text$03 ENDS @seg .text$04, externdef HeapMalloc:near externdef SetEMSHandleTable:near externdef SetEMSStateTable:near externdef XMS_Init:near externdef Debug_Init:near externdef EMS_Init1:near externdef EMS_Init2:near externdef VDS_Init:near externdef UMB_Init:near externdef Pool_Init1:near externdef Pool_Init2:near externdef InitMonitor:near externdef IsUMBMemory:near .text$04 ends ================================================ FILE: src/I15.ASM ================================================ ;--- I15, ah=87 extended memory move ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ;--- publics/externals include extern32.inc ;--- expected GDT structure pointed to by ES:SI for int 15h, ah=87h I15MOVE struct dq ? dq ? src DESCRIPTOR dst DESCRIPTOR I15MOVE ends .text$01 SEGMENT .text$01 ends .text$03 segment ;************************************************************ ; simulate INT15/87 ; ;INT 15 - SYSTEM - COPY EXTENDED MEMORY (by RBIL) ; AH = 87h ; CX = number of words to copy (max 8000h) ; ES:SI -> GDT (see I15MOVE) ;Return: CF set on error ; CF clear if successful ; AH = status ; ;Values for extended-memory copy status (RBIL): ; 00h source copied into destination ; 01h parity error ; 02h interrupt error ; 03h address line 20 gating failed ; 80h invalid command (PC,PCjr) ; 86h unsupported function (XT,PS30) ;************************************************************ I15_Simulate87 proc public call Simulate_Iret movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_ECX cld MOVZX edi,WORD PTR [ebp].Client_Reg_Struc.Client_ES ; make edi = linear address of command MOVZX esi,WORD PTR [ebp].Client_Reg_Struc.Client_ESI SHL edi,4 add esi,edi ;-- MS Emm386 returns with error AH=2 if CX > 8000h! cmp cx, 8000h ja @@error02 or ecx,ecx je @@ok ; nothing to do - MS Emm386 returns with error AH=2 if CX == 0! shl ecx,1 ; convert words to bytes ;--- verify that src and dst descriptors are ok. ;--- we don't care about segment access rights lea eax,[ecx-1] cmp ax, [esi].I15MOVE.src.wLimit; 16-bit overflow not an issue (0->ffff) ; ja @@error80 ja @@error02 ; v5.87: error 2 is MS EMM386 compatible cmp ax, [esi].I15MOVE.dst.wLimit ; ja @@error80 ja @@error02 ; v5.87: error 2 is MS EMM386 compatible mov al,[esi].I15MOVE.src.bA1623 mov ah,[esi].I15MOVE.src.bA2431 ; get linear source address mov dl,[esi].I15MOVE.dst.bA1623 mov dh,[esi].I15MOVE.dst.bA2431 ; get linear destination address shl eax,16 shl edx,16 mov ax,[esi].I15MOVE.src.wA0015 mov dx,[esi].I15MOVE.dst.wA0015 mov esi,eax mov edi,edx ;--- here we have: esi=src, edi=dst, ecx=size @dprintf ?I15DBG, <"Int 15h, ah=87h, src=%X, dst=%X, siz=%X",10>, esi, edi, ecx ;-- NOCHECK -> moves for addresses not backuped with RAM/ROM will fail ;-- (cause an exception) test [bV86Flags], V86F_NOCHECK je @@memcheck lea eax, [esi+ecx] lea edx, [edi+ecx] cmp eax, [dwMaxPhysMem] jae @@fail cmp edx, [dwMaxPhysMem] jae @@fail @@memcheck: call MoveMemoryPhys @@ok: mov AH,0 ; everything OK and finished and [ebp].Client_Reg_Struc.Client_EFlags, not 1 ;CF=0 @@i1587_exit: mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, ah ret @@error02: if ?XMS35COMPAT cmp cx,0F00Fh jnz @F cmp cx,word ptr [ebp].Client_Reg_Struc.Client_EAX+2 jnz @F bt [dwFeatures],17 ;PSE-36 supported? jnc @F mov cx,word ptr [ebp].Client_Reg_Struc.Client_ECX+2 shl ecx,1 mov al,[esi].I15MOVE.src.bA1623 mov ah,[esi].I15MOVE.src.bA2431 ; get linear source address mov dl,[esi].I15MOVE.dst.bA1623 mov dh,[esi].I15MOVE.dst.bA2431 ; get linear destination address shl eax,16 shl edx,16 mov ax,[esi].I15MOVE.src.wA0015 mov dx,[esi].I15MOVE.dst.wA0015 mov esi,eax mov edi,edx mov eax,[ebp].Client_Reg_Struc.Client_EDX ;src bits 32-47 mov edx,[ebp].Client_Reg_Struc.Client_EBX ;dst bits 32-47 @dprintf ?I15DBG, <"Int 15h, ah=87h, src=%X.%X, dst=%X.%X siz=%X",10>, ax, esi, dx, edi, cx call MoveMemoryPhysEx jmp @@ok @@: endif mov ah,02h or [ebp].Client_Reg_Struc.Client_EFlags, 1 ;CF=1 jmp @@i1587_exit if 0 ; v5.87: error 80h should never be returned @@error80: mov ah,80h or [ebp].Client_Reg_Struc.Client_EFlags, 1 ;CF=1 jmp @@i1587_exit endif @@fail: jmp V86_Exc0D align 4 I15_Simulate87 endp .text$03 ends END ================================================ FILE: src/INIT.ASM ================================================ ;--- Jemm's initialization part ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc if ?KD include debugsys.inc endif ;--- assembly time constants ?SYSPDE EQU ?SYSBASE shr 20 ; offset of PDE for system space in pagedir ?PAT equ 0 ; std=0, 1=use PAT to change WT to WC ?DMABELOW16M equ 0 ; 1=set DMA buffer size to 0 if physical address of buffer is beyond 16M if ?FASTMON ?INTTABSIZ equ 0E0h * 7 else ?INTTABSIZ equ 100h * 7 endif ;--- publics/externals include extern32.inc .text$01 segment externdef pSmallHeap:dword externdef dwHeapSize:dword .text$01 ends .text$03 segment RestoreEDI proc @dprintf ?INITDBG, <"small heap update ptr=%X",10>, edi mov [pSmallHeap],edi pop edi ret RestoreEDI endp .text$03 ends @seg .text$03z, align 4 V86_ENDRES proc public ;declare a public label so the size is seen in .MAP V86_ENDRES endp .text$03z ends .text$04 segment ;--- here start protected mode initialization code ;--- which is *not* copied to extended memory ;--- IO permission bitmap init values IOBM label byte ; I/O-control of the DMA-port ; * trap ports 0..7, A, B, C, (D, E, F) ; * trap ports 81,82,83,87, 89,8A,8B ; * trap ports c4..cf ; * trap port d4 d6 d8 (da, dc, de) if ?DMAPT or ?A20PORTS if ?DMAPT if ?MMASK ;----- FEDCBA9876543210 DW 1111110011111111b ; DMA-Controller #1 (00-0F) else ;----- FEDCBA9876543210 DW 0001110011111111b ; DMA-Controller #1 (00-0F) endif else DW 0 ; ports 00-0F endif DW 0,0,0,0,0 ; ports 10-5F if ?A20PORTS ;----- FEDCBA9876543210 if ?DYNTRAPP60 DW 0000000000010000b ; ports 60-6F else DW 0000000000010001b ; ports 60-6F endif else DW 0 ; ports 60-6F endif DW 0 ; ports 70-7F if ?DMAPT ;----- FEDCBA9876543210 DW 0000111010001110b ; page register (80-8F) else DW 0 ; ports 80-8F endif if ?A20PORTS ;----- FEDCBA9876543210 DW 0000000000000100b ; ports 90-9F else DW 0 ; ports 90-9F endif DW 0,0 ; ports A0-BF if ?DMAPT ;----- FEDCBA9876543210 DW 1111111111110000b ; DMA-Controller #2 (C0-CF) if ?MMASK ;----- FEDCBA9876543210 DW 0101010101010000b ; DMA-Controller #2 (D8-DF) else ;----- FEDCBA9876543210 DW 0000000101010000b ; DMA-Controller #2 (D8-DF) endif else DW 0,0 ; ports C0-DF endif endif IOBM_COPY_LEN equ $ - IOBM ;--- alloc small memory portions from the small heap (if there is one) ;--- only valid during init phase ;--- ecx=requested size in bytes ;--- out: edi=pointer to memory HeapMalloc proc public cmp [dwHeapSize],ecx jb @@nomem sub [dwHeapSize],ecx @dprintf ?INITDBG, <"small heap used, size=%X, ptr=%X",10>, ecx, [pSmallHeap] xchg edi,[esp] push RestoreEDI push edi mov edi,[pSmallHeap] @@nomem: ret HeapMalloc endp ;--- check cpu type, return features if CPUID is supported Is486 proc pushfd ; save EFlags xor edx,edx push 240000h ; set AC+ID bits in eflags popfd pushfd pop eax popfd ; restore EFlags shr eax, 16 test al,04 ; AC bit set? then it is a 486+ mov ah,0 je @@no_486 inc ah test al,20h ; CPUID supported? jz @@nocpuid xor eax,eax inc eax ; get register 1 .586 cpuid .386 mov ah,1 @@nocpuid: @@no_486: mov al,ah ret Is486 endp if ?FASTBOOT ;--- save IVT vectors ;--- inp: EDI -> free memory SaveIVT proc test [bV86Flags],V86F_FASTBOOT ;additionally 32*4+32*4+16*4=320 jz @@nofastboot mov ecx, 32*4+32*4+16*4 call HeapMalloc mov [pSavedVecs],edi xor esi,esi mov ecx,32 rep movsd ;save vecs 00-1F add esi,32*4 ;skip 20-3F mov cl,32 rep movsd ;save vecs 40-5F add esi,8*4 ;skip 60-67 mov cl,16 rep movsd ;save vecs 68-77h if ?RBTDBG mov esi,[pSavedVecs] mov ecx,20h xor ebx,ebx nextitem: lodsd @dprintf ?RBTDBG, <"%X: %08X ">, bl, eax test cl,1 jz @F @dprintf ?RBTDBG, <10> @@: inc ebx loop nextitem endif @@nofastboot: ret SaveIVT endp endif if ?SHAREDIDT externdef V86IDT:GATE endif ;--- set int EAX to value of ECX ;--- used for Ints 41h, 67h, 15h, 06h, 10h ;--- called during Init(). SetIDTEntry proc if ?SHAREDIDT mov word ptr [eax*8+V86IDT].wOfsLo, cx shr ecx,16 mov word ptr [eax*8+V86IDT].wOfsHi, cx else push ebx mov ebx,[dwV86IDT] mov word ptr [ebx+eax*8].GATE.wOfsLo, cx shr ecx,16 mov word ptr [ebx+eax*8].GATE.wOfsHi, cx pop ebx endif ret SetIDTEntry endp ;--- Monitor initialization ;--- input expected: ;--- CPU in protected-mode, interrupts disabled, paging disabled ;--- CS,SS,DS,ES: FLAT ;--- FS,GS: NULL ;--- EBP -> IRETDV86 ( contains vCS=RSEG ) ;--- EBX -> BRPTAB ;--- ESI : 32-bit code start, flat address ;--- EDI -> JEMMINIT, contains: ;--- MonitorStart : start of EMB for the monitor ;--- MonitorEnd : end of EMB for monitor ;--- MaxMem16k : MAX=xxxx cmdline option given, or 1E00 (120 MB) ;--- MaxPhysMem : highest physical address + 1 ;--- XMS_Handle_Table : returned by int 2f, ax=4309h ;--- MaxEMSPages : maximum EMS 16k pages, default 32 MB ;--- XMS_Control_Handle: XMS handle for monitor extended memory block ;--- DMA_Buffer_Size : size of DMA buffer in kB ;--- Frame : FRAME=xxxx parameter ;--- NoEMS, NoFrame, NoPool, AltBoot, NoVME, NoVDS, NoPGE, NoA20, NoVCPI ;--- memory layout installed by the initialization code: ;--- shared memory (110000 - 11xxxx): ;--- 0-3.0 kB wasted space because of rounding to page border (Jemm386 only) ;--- 0.125 kB monitor help stack for VCPI ;--- 0.5 kB GDT [GDT_PTR] (size 200h) ;--- ~14.0 kB resident monitor data+code (V86_ENDRES - ?BASE) ;--- 2.0 kB IDT [IDT_PTR] ;--- ~1.7 kB interrupt table (size E1h*7) ;--- 0-3.0 kB wasted space because of rounding to page border ;--- (if > 2 kB it is filled with STATUSTABLE) ;--- linear addresses in sys space (F80000000 - F803FFFFF): ;--- 4 kB reserved ;--- 12 kB page directory, page table 0, sys page table ;--- 4 kB ring 0 stack ;--- ~8.2 kB TSS (size ?TSSLEN) ;--- DMABuffStart (0-?DMABUFFMAX kB) ;--- para align ;--- 1 kB EMSHandleTable (size 4*255) ;--- 1 kB EMSStateTable (size 8*128) ;--- 2 kB EMSNameTable (size 8*255) ;--- EMSPageAllocationStart (EMS pages * 4 bytes) ;--- EMSPageAllocationOfs (EMS pages * 1 bytes) ;--- 64 byte align ;--- PoolAllocationTable (128*64 + x) ;--- 0-3.0 kB page align ;--- start of "free" memory in this very first XMS block ;--- (used for UMBs, if NODYN used for EMS/VCPI as well) InitMonitor PROC public ;--- local variables: jemmini equ <[ebp-4]> rsofsinit equ <[ebp-8]> codeadr equ <[ebp-12]> tmpPageMask equ <[ebp-15]> tmpIs486 equ <[ebp-16]> tmpFeatures equ <[ebp-20]> dwSizeV86 equ <[ebp-24]> CLD ;--- no access to global variables possible until paging is enabled ;--- and memory block has been moved to extended memory! ;--- dprintf uses direct addressing, doesn't work yet ; @dprintf ?INITDBG, <"Welcome in PM",10> push edi ;== jemmini [ebp-4] push ebx ;== rsofsinit [ebp-8] push esi ;== codeadr [ebp-12] if 1 pushfd ; clear the NT flag to avoid a "reboot" and byte ptr [esp+1],not 40h; at next IRET in protected-mode popfd endif mov esi, jemmini call Is486 push eax ;== tmpPageMask [ebp-16] push edx ;== tmpFeatures [ebp-20] and al,al jz @F cmp [esi].JEMMINIT.NoInvlPg,-1 jnz @F mov [esi].JEMMINIT.NoInvlPg,0 @@: MOV EDI,[esi].JEMMINIT.MonitorStart ; Start of Monitor-Code if ?INITDBG push edi mov ecx,[esi].JEMMINIT.MonitorEnd sub ecx,edi shr ecx,2 mov eax,0DEADBABEh rep stosd pop edi endif ife ?INTEGRATED ADD EDI,1000h-1 ; Round to the next page border AND DI,NOT 1000h-1 ; may waste up to 3 kB (not for JemmEx) endif ;-- calc size of the items which must be in page table 0: ;-- VCPI stack, GDT, code+data, IDT, INT_TABLE ;-- ?INTTABSIZ is size of INT_TABLE mov eax, offset V86_ENDRES if ?SHAREDIDT sub eax, ?BASE - ( ?INTTABSIZ) ;size of resident code+data else sub eax, ?BASE - ( ?INTTABSIZ + 800h) endif add eax, 1000h-1 and ax, not 1000h-1 ;round to next page (may waste another 3 kB!) mov ebx, edi ;save physical address in EBX add edi, eax push eax ;== dwSizeV86 [ebp-24], size of code+GDT+IDT if 0 mov eax, cr3 and eax, 0FFFh ;don't touch low 12 bits of CR3 or eax, edi MOV CR3, eax ;set CR3 (paging is still disabled) else mov cr3, edi endif ;-- clear pagedir, page table 0 + system page table push edi mov ecx, 3000h/4 xor eax, eax rep stosd pop edi ;-- init page dir (2 PDEs) mov ah,10h ;=mov eax,1000h lea edx, [edi+eax] MOV [EDI+000h],EDX OR DWORD PTR [EDI+000h], PTF_PRESENT or PTF_RW or PTF_USER add edx, eax push edx ;save mapped system page table OR edx, PTF_PRESENT or PTF_RW or PTF_USER mov [EDI+?SYSPDE], EDX add edi, eax ;edi -> mapped page table 0 ;-- init page table 0 address space 0-110000h movzx edx, dl ;just flags u/ser, w/riteable, p/resent if ?PGE test byte ptr tmpFeatures+1,CF_PGE shr 8 ;PGE supported? jz @F or dh,PTF_GBL shr 8 ;set G bit (page global); active only if bit set in CR4 @@: mov tmpPageMask,dh endif mov cx,110h ;hiword ecx is cleared mov eax,edx @@: stosd ADD EAX,1000h loop @B if 0 ;-- give the video region A000-BFFF some special attributes push edi sub edi, (110h*4 - 0A0h*4) mov cl,20h @@: or dword ptr [edi],8h ;set "WT" add edi,4 loop @B pop edi endif ;-- init page table 0 address space 110000h-? mov ecx, dwSizeV86 ;size of space above 110000h (page aligned) shr ecx, 12 ;is just a handful of pages mov eax, ebx ;get physical address of this space or al, PTF_PRESENT or PTF_RW ;set PRESENT + R/W if ?PGE ;--- set first page (which is shared) global or ah, tmpPageMask endif @@: stosd ADD EAX,1000h if 0 and al, not PTF_USER ;all pages except the first are "system" endif if ?PGE and ah, 0F0h endif loop @B ;-- v5.86, option SB: identity map remaining PTEs in page tab 0. test [esi].JEMMINIT.V86Flags, V86F_SB jz nomapping push eax if 1 movzx eax, di and ah, 0Fh shl eax, 10 ;transform offset in page table -> linear address or al, PTF_PRESENT or PTF_RW or PTF_USER endif @@: stosd ADD EAX,1000h test di,0FFFh jnz @B ;--- identity map extended memory 400000h-7fffffffh? ;--- with PSE, there's no big loss, since just the page dir ;--- has to be adjusted. However, possible intruders most ;--- likely won't understand 4MB PDEs. ;--- if activated: add check if CPU supports PSE! if 0 ; sub edi, 2000h-4 ; =pagedir+4 mov edi, ?PAGEDIR+4 mov ecx, 1FFh ;--- bit 7=1: PSE 4MB page ;--- bits 13-21: 0 ;--- bits 22-31: addressbits 22-31 mov eax, 400000h or PTF_4MB or PTF_PRESENT or PTF_RW or PTF_USER @@: stosd add eax, 400000h loop @B endif pop eax nomapping: ;-- init system page table with the rest. ;-- in a first step just 4 PTEs are needed (to map page tables and stack) pop edi ;restore saved mapped system page table (=linear address) add edi, 4 ;skip first page ( remains uncommitted ) mov ecx,3+1 ;3 page tables + 1 page for stack @@: stosd ADD EAX,1000h loop @B push eax ;save physical address free memory ;--- page dir, page tab 000 and sys page tab are now initialized, ;--- paging can be enabled. MOV EAX,CR0 OR EAX,80000000H ; set PE bit MOV CR0,EAX ;--- dprintf uses direct addressing, cannot work yet ; @dprintf ?INITDBG, <"Paging has been enabled",10> ;--- paging enabled, now move monitor code+data in extended memory mov edi, ?BASE mov esi, codeadr if 0 ;--- resolve base fixups ;--- this is usually not needed, since the binary has been linked ;--- for base address 110000h pushad add esi, V86_TOTAL ;the relocs are just behind the 32bit block xor edx, edx xchg dx,[esi] ;get size of header (hack!) nextpage: mov ebx, [esi+0] mov ecx, [esi+4] and ecx, ecx jz reloc_done ; @dprintf ?INITDBG, <"rlcs at %X for page %X size=%X edx=%X",10>, esi, ebx, ecx, edx add ecx, esi add esi, 8 sub ebx, edx ;subtract size of header from RVA add ebx, [esp] ;add conv. base to address xor eax, eax nextreloc: lodsw test ah,0F0h jz @F and ah,0Fh add [eax+ebx], edi @@: cmp esi, ecx jb nextreloc jmp nextpage reloc_done: popad ; @dprintf ?INITDBG, <"base relocs done",10> endif ;-- copy all to extended memory (linear address 110000h) MOV ECX, offset V86_ENDRES sub ecx, edi shr ecx, 2 rep movsd ;--- after code + data has been moved to extended memory ;--- direct addressing is ok, so access to global variables works now @dprintf ?INITDBG, <"monitor code+data moved to extended memory",10> ;--- load final values for GDTR .386p LGDT [GDT_PTR] .386 @dprintf ?INITDBG, <"GDTR set, edi=%X",10>, edi ;-- create INT_TABLE + set IDT ;-- some VCPI intruders won't work if IDT is not in page table 0 !!! mov ebx, 0EE00h shl 16 + FLAT_CODE_SEL mov ecx, 100h if ?SHAREDIDT mov esi, offset V86IDT else mov esi, edi mov [dwV86IDT], esi add edi, 100h*8 endif ;--- esi -> IDT ;--- edi -> code int x entries (x=0..ff) if ?FASTMON mov eax, offset int00 nextfastidtentry: ; @dprintf ?INITDBG, <"."> shld edx, eax, 16 mov [esi+0],ax mov [esi+2],ebx mov [esi+6],dx add eax, 4 add esi, 8 ;next IDT entry dec ecx cmp cl,100h - ?FASTENTRIES jnz nextfastidtentry ; @dprintf ?INITDBG, <10> mov edx, 0E9006Ah or (?FASTENTRIES shl 8) ;push byte ?FASTENTRIES, jmp xxxx else mov edx, 0E9006Ah ;push byte 00h, jmp xxxx endif @@nextidtentry: ; @dprintf ?INITDBG, <"."> mov eax, edi mov [esi+0],ax mov [esi+2],ebx shr eax, 16 mov [esi+6],ax mov [edi+0],edx mov eax, offset V86_Monitor add edi,7 ;7 bytes for each entry in INT_TABLE! sub eax, edi mov [edi-4],eax inc dh ;next INT # add esi,8 loop @@nextidtentry ; @dprintf ?INITDBG, <10> .386p LIDT [IDT_PTR] .386 @dprintf ?INITDBG, <"IDTR, IDT + int table initialized, IDT=%X edi=%X",10>, dword ptr [IDT_PTR+2], edi if ?I41SUPP ;--- set int 41h to an iretd ;--- no need to route this int to v86-mode - it's not executable code. ;--- this must be done BEFORE the callout to the kernel debugger (see below). mov al, 41h movzx eax, al mov ecx, offset Int41_Entry @dprintf ?INITDBG, <"init: int 41h vector set to %X",10>, ecx call SetIDTEntry endif if ?KD mov esi, jemmini cmp [esi].JEMMINIT.kdseg,0 jz @F @dprintf ?INITDBG, <"init: kernel debugger init call",10> push edi mov al, PMINIT_INIT_IDT if ?SHAREDIDT mov edi, offset V86IDT else mov edi, [dwV86IDT] endif call fword ptr [esi].JEMMINIT.kdofs pop edi mov ax, 004Fh int 41h cmp ax, 0F386h setz [bKD] @dprintf ?INITDBG, <"init: kernel debugger response to int 41h: %X",10>, eax @@: endif if 0 ;--- switch to the stack in extended memory mov ebp, ?TOS - size IRETDV86 lea esp, [ebp-24] ;take care of the local variables @dprintf ?INITDBG, <"init: ESP reset",10> endif ;--- edi para aligned again (increased by 700h/690h) ;--- EDI -> free linear memory ;--- EBX -> free space for PTEs in system page table MOV EAX,CR3 MOV [V86CR3],eax mov eax, tmpFeatures mov [dwFeatures], eax mov eax, tmpIs486 mov [bIs486], al if ?PGE mov [bPageMask],ah endif MOV ECX,[ebp].IRETDV86._Cs MOV [dwRSeg], ecx SHL ECX,4 mov [dwRes],ecx mov esi, jemmini mov eax, [esi].JEMMINIT.MaxPhysMem mov [dwMaxPhysMem],eax mov eax, [esi].JEMMINIT.MaxMem16k shl eax, 2 mov [pmem.dwMaxMem4K],eax mov al, [esi].JEMMINIT.NoPool mov [bNoPool],al mov al, [esi].JEMMINIT.NoInvlPg mov [bNoInvlPg],al mov al, [esi].JEMMINIT.V86Flags mov [bV86Flags],al push ecx call XMS_Init ; won't use edi/ebx if ?DBGOUT ;--- this call must be done AFTER callout to kernel debugger call Debug_Init endif call EMS_Init1 ; may modify ebx! pop ecx mov esi, rsofsinit movzx eax,[esi].RSOFS.wBpTab mov [bBpTab],al add [bBpBack],al add eax,ecx mov [bpstart],eax if ?HOOK13 movzx eax,[esi].RSOFS.wRFlags add eax,ecx mov [dwRFlags],eax endif @dprintf ?INITDBG, <"variables copied, edi=%X",10>, edi ;--- use any rest as "small heap" ;--- it can be used by EMS - if it is large enough lea ecx,[edi+1000h-1] and cx,0F000h ; mov [pPg0PartEnd],ecx ;v5.86: store end of monitor in page tab 0 sub ecx, edi mov [pSmallHeap], edi mov [dwHeapSize], ecx ;--- the memory in page tab 0 is initialized ;--- until now are consumed: ;--- 3 pages for pagedir+pagetab ;--- 4-5 pages for monitor (hlp stack,) GDT, data, code, IDT pop ebx ; get free phys mem ptr @dprintf ?INITDBG, <"page table 0 initialised, EDI=%X EBX=%X",10>, edi, ebx if 1 ;--- set bits in VDS DMABuffFree bit array mov esi, jemmini movzx eax, [esi].JEMMINIT.DMABufferSize ;buffer size in kB shl eax, 10 mov ecx, eax shr ecx, 10 inc ecx ;add start and end bit inc ecx xor edx, edx @@: bts [vdsstat.DMABuffFree], edx inc edx loop @B ;-- ensure DMA buffer doesn't cross a 64kb border mov ecx, ebx and cx,0F000h lea edx, [ecx+eax-1] ;edx -> last byte of buffer if ?DMABELOW16M cmp edx, 1000000h ;DMA must be below 16M border jc @F xor eax, eax @@: endif mov [vdsstat.DMABuffSize], eax and eax, eax jz @@buffernull mov eax, edx shr eax, 16 mov edi, ecx shr edi, 16 sub eax, edi ;does buffer cross a 64 kB boundary? jz @@buffergood ;-- align it to the next 64 kB boundary inc edi shl edi,16 mov eax,edi sub eax,ecx add ecx,eax ;in eax now amount of free mem below DMA buffer @@buffergood: ;--- map DMA buffer in linear address space pushad mov eax, ecx mov ecx, [vdsstat.DMABuffSize] ; will be page aligned shr ecx, 12 call MapPhysPagesEx mov [vdsstat.DMABuffStart], eax add edx,4 mov [PageMapHeap], edx if ?INITDBG mov edi, eax mov ecx, [vdsstat.DMABuffSize] shr ecx, 2 mov eax, "BAMD" rep stosd endif popad @@buffernull: MOV [vdsstat.DMABuffStartPhys], ecx @dprintf ?INITDBG, <"DMA buffer linear=%X physical=%X size=%X phys rest=%X",10>, [vdsstat.DMABuffStart], [vdsstat.DMABuffStartPhys], [vdsstat.DMABuffSize], eax endif ;--- dma buffer initialized ;--- fill ring 0 stack with "STAK" push eax ;rest of physical mem below DMA buffer mov edi, ?SYSBASE + 3000h + 1000h ; skip free page, page dir + 2 page tables mov eax,"KATS" mov ecx, 1000h/4 rep stosd @dprintf ?INITDBG, <"stack initialized",10> ;-- now create a heap ;-- set rest of PTEs in system page table ;-- what amount of space is needed ? ;-- tss: 104 + 32 + 8192 + 8 -> 8336 ;-- pool: maxmem16k * 64 / 96 -> 5120 (for 120 MB/30720 4k pages) ;-- pool: xms handles * 64 -> 2048 (for 32 XMS handles) ;-- ems: handles (255*4) -> 1020 (for 255 handles) ;-- ems: state save (64*16) -> 1024 (for 64 states) ;-- ems: name array (255*8) -> 2040 (for 255 names) ;-- ems: page array (2048*5) -> 10240 (for 2048 pages) ;---------------------------------------------------------------- ; 29828 push ebx mov ecx, ?TSSLEN ;8336 ;--- calc number of pages still needed in sys page table ;--- 1. the pages for pool management: ;--- maxmem16k * 64 / 96 mov eax, [esi].JEMMINIT.MaxMem16k shl eax, 6 ;*64 xor edx, edx mov ebx, 96 div ebx add eax, 64*2 add ecx, eax cmp [bNoPool],0 jnz @@isnopool movzx eax,[XMS_Handle_Table.xht_numhandles] shl eax, 6 add ecx, eax @@isnopool: @dprintf ?INITDBG, <"for TSS+pool=%X">, ecx ;--- 2. the pages required for ems handling ;--- a. var space (5 * maxEMSpages) movzx eax, [esi].JEMMINIT.MaxEMSPages lea eax, [eax+eax*4] ;each EMS page needs 5 bytes ;--- b. fix space (EMS handle table, state table, name table) add eax, EMS_MAX_HANDLES*size EMSHD + EMS_MAXSTATE*size EMSSTAT + EMS_MAX_HANDLES*8 @dprintf ?INITDBG, <", for EMS=%X">, eax add ecx, eax @dprintf ?INITDBG, <", total=%X">, ecx ;--- 3. round up to 4k add ecx, 4096-1 ;--- 4. convert to 4k page shr ecx, 12 pop ebx pop eax @dprintf ?INITDBG, <", remaining bytes=%X",10>, eax @LinearFromSysPTE edi, [PageMapHeap] ;--- here: ;--- eax = amount of space below DMA buffer (in bytes) ;--- ebx = physical address of space below DMA buffer ;--- edi = linear address free memory ;--- ecx = amount of memory needed (in pages) mov edx,[PageMapHeap] shr eax, 12 jz @@nospacebelow ;jump if nothing left below DMA buffer push ecx push eax mov ecx, eax mov eax, ebx call MapPhysPages pop eax pop ecx @@nospacebelow: sub ecx, eax ;space above DMA buffer needed? jbe @@nospaceabove mov eax, [vdsstat.DMABuffStartPhys] add eax, [vdsstat.DMABuffSize] @dprintf ?INITDBG, <"heap region above DMA buffer %X, size=%X, pPTE=%X",10>, eax, ecx, edx call MapPhysPages @@nospaceabove: add edx,4 ;leave 1 page in address space reserved mov [PageMapHeap],edx if ?INITDBG lea ebx, [edx-4] @LinearFromSysPTE ebx, ebx dec ebx @dprintf ?INITDBG, <"heap created at %X-%X, free PTEs starting at %X",10>, edi, ebx, edx endif ;--- now create the TSS (will begin on a page boundary, since DMA buffer ;--- size is rounded to 4 kB). if ?DYNTRAPP60 mov [dwTSS], edi endif mov ebx, offset V86GDT mov eax, edi MOV WORD PTR [EBX+V86_TSS_SEL+2],AX SHR EAX,16 MOV BYTE PTR [EBX+V86_TSS_SEL+4],AL MOV BYTE PTR [EBX+V86_TSS_SEL+7],AH ;--- init TSS, the software interrupt redirection bitmap (256 bits) + io-bitmap ;--- it is known by Pentium+ cpus only, but doesn't hurt for previous cpus mov edx, edi mov ecx, size TSSSEG/4 + 32/4 + (65536/8)/4 xor eax, eax rep stosd ;clear TSS, io-bitmap, ... dec eax stosb ;the IO bitmap must be terminated by a FF byte mov dword ptr [edx].TSSSEG.tsEsp0, ?TOS mov dword ptr [edx].TSSSEG.tsSS0, FLAT_DATA_SEL if 1 mov eax, [V86CR3] ; save value for CR3 in TSS (not needed) mov [edx].TSSSEG.tsCR3, eax endif mov [edx].TSSSEG.tsOfs,size TSSSEG+32 ;let 32 bytes space below IO Bitmap ;-- init the io permission bitmap mov esi, codeadr ; add esi, offset IOBM - offset _start add esi, offset IOBM - ?BASE lea edi, [edx+size TSSSEG+32] mov ecx,IOBM_COPY_LEN rep movsb @dprintf ?INITDBG, <"TSS done, esp0=%X",10>, [edx].TSSSEG.tsEsp0 ;-- finally load TR .386p mov ax, V86_TSS_SEL ltr ax .386 ;--- here CR3, GDTR, IDTR, TR and LDTR all have their final values lea edi, [edx+ ?TSSLEN] ;--- modify IDT and vector bitmap ;--- int 67h must be handled by the monitor in any case add edx, size TSSSEG xor eax, eax mov al, 67h mov ecx, offset Int67_Entry bts [edx], eax call SetIDTEntry mov al, 15h mov ecx, offset Int15_Entry bts [edx], eax call SetIDTEntry if ?BPOPC ne 0F4h mov al,6 mov ecx, offset Int06_Entry ; bts [edx], eax ;not required since it is an exception call SetIDTEntry endif if ?EXC10 mov al,10h mov ecx, offset Int10_Entry call SetIDTEntry endif mov esi,jemmini if ?VME mov al,[esi].JEMMINIT.NoVME xor al,1 call SetVME endif if ?PAT test [dwFeatures],10000h jz @@nopat mov ecx,277h @rdmsr mov ah,01 ;change 00-07-04-06 to 00-07-01-06 @wrmsr @@nopat: endif if ?A20PORTS if 0 xor eax, eax test byte ptr wBiosA20,1 ;keyboard controller can switch A20? jnz @@kbdA20 mov al, 60h btr [edx], eax mov al, 64h btr [edx], eax @@kbdA20: test byte ptr wBiosA20,2 ;port 92h can switch A20? jnz @@ps2A20 mov al, 92h btr [edx], eax @@ps2A20: endif endif @dprintf ?INITDBG, <"Jemm initialised, edi=%X",10>, edi if ?FASTBOOT call SaveIVT endif mov esi, jemmini ;--- Pool init ;--- esi->jemminit, edi=linear addr free space ;--- out: edi=free space call Pool_Init1 ;--- EMS/VCPI init, init EMS SYSTEM handle ;--- esi->jemminit, edi=linear addr free space ;--- out: edi=free space call EMS_Init2 ;--- set EAX = index PTE of linear address in EDI in system page table mov eax, edi sub eax, ?SYSBASE shr eax, 12 if 0 ;--- clear the PTEs which are not used in the heap xor ecx,ecx test di,0fffh setnz cl lea esi, @GetPTEAddr(?PAGETABSYS+eax*4) lea esi,[esi+ecx*4] xor ecx, ecx @@: cmp ecx,[esi] jz @F mov [esi],ecx add esi,4 jmp @B @@: endif ;--- convert EDI back into a physical address ;--- use system page table for the conversion mov eax, @GetPTEAddr(?PAGETABSYS+eax*4) and ax, 0F000h and edi, 0FFFh if ?INITDBG lea ecx, [eax+edi] @dprintf ?INITDBG, <"End of monitor: %X",10>, ecx endif ;--- now check if heap's last physical page is < DMA buffer ;--- if yes, all pages below must be skipped and are wasted ;--- since the EMS/VCPI memory managment needs a contiguous block ;--- of physical memory as input. mov ecx, [vdsstat.DMABuffStartPhys] cmp eax, ecx jnc @@abovedma @dprintf ?INITDBG, <"must waste space, phys end of monitor=%X is below DMA buff=%X",10>, eax, ecx add ecx, [vdsstat.DMABuffSize] ;get physical end of the DMA buff mov eax, ecx xor edi, edi @@abovedma: add edi, eax mov esi, jemmini mov eax, [esi].JEMMINIT.MonitorEnd cmp eax, EDI jnc @@nomemproblem ;run out of memory? ; ran out of memory, shouldn't happen, avoid disaster by setting ; max VCPI/EMS memory to 0 @dprintf ?INITDBG, <"ERROR: out of memory condition on init, MonitorEnd=%X EDI=%X",10>,[esi].JEMMINIT.MonitorEnd, edi mov [EMSPagesMax], 0 mov [pmem.dwMaxMem4K], 0 jmp @@initdone @@nomemproblem: @dprintf ?INITDBG, <"end of monitor data, physical=%X, end of XMS memory block=%X",10>, edi, eax ;--- force 4K alignment for EMS/VCPI fixed pages and UMB's ADD EDI,4095 AND DI,NOT 4095 sub eax, edi jnc @F xor eax, eax @@: ;--- eax=rest of monitor memory block ;--- edi=physical address ;--- esi=JEMMINIT ;--- now init the UMBs. these should not affect VCPI/EMS memory cmp [esi].JEMMINIT.NoRAM,0 jnz @@noumbmapping @dprintf ?INITDBG, <"UMB init start, mem to map=%X, remaining mem=%X",10>, edi, eax push ebp mov ebp, eax ;--- map in the UMB pages mov ebx,[esi].JEMMINIT.PageMap mov ecx,0A0h ;check A000-FFFF only! @@nextitem: cmp ebp,1000h ;are 4k still available? jc @@umbmemout mov al,[ebx+ecx] call IsUMBMemory jc @@skippage if ?SPLIT and ah,ah ;is it a "split" page? jz @@isstdumb call CopyROM ;then copy ROM content to RAM jmp @@pageshadowed @@isstdumb: endif lea eax, @GetPTEAddr(?PAGETAB0+ecx*4) mov edx,[eax] and edx,0FFFh ;don't modify the PTE bits or edx,edi mov [eax],edx @@pageshadowed: ; @dprintf ?INITDBG, <"%2X ">, cl add edi,1000h sub ebp,1000h @@skippage: inc cl jnz @@nextitem @@umbmemout: mov eax,ebp pop ebp @dprintf ?INITDBG, <"UMBs mapped, remaining mem=%X",10>, eax @@noumbmapping: ; cmp [esi].JEMMINIT.AltBoot,0 ; jnz @@noshadow ;-- shadow ROM page 0FFh to catch jumps to FFFF:0 ;-- to fill the new page with the content of the ROM, map it ;-- at linear scratch pos and copy the content. cmp eax,1000h ;is enough free space available? jc @@noshadow mov ecx, 0FFh call CopyROM mov WORD PTR ds:[0FFFF0h],19CDh ; set "INT 19h" at FFFF:0000 lea edx, @GetPTEAddr(?PAGETAB0+ecx*4) and byte ptr [edx],not PTF_RW ; make this page R/O add edi,1000h sub eax,1000h @@noshadow: ;--- flush TLB to activate the UMBs and shadow ROMs mov ecx,cr3 mov cr3,ecx if ?PGE test byte ptr tmpFeatures+1,CF_PGE shr 8 ;PGE supported? jz @F cmp [esi].JEMMINIT.NoPGE,0 jnz @F .586p mov ecx, CR4 or cl, CR4_PGE mov CR4, ecx .386 @@: endif MOV [tmpFeatures],EDI ; save phys addr of first EMS-page ;--- EDI -> start free mem ;--- EAX -> free mem size shr eax,12 ; convert bytes to 4k pages call Pool_Init2 @dprintf ?INITDBG, <"EMS/VCPI memory handling initialised, MaxMem4K=%X",10,"end of preallocated EMS, physical=%X",10>, [pmem.dwMaxMem4K], edi if ?INTEGRATED ;--- for the integrated version the rest of the memory can be released now mov ebx, edi sub ebx, [esi].JEMMINIT.MonitorStart shr ebx, 10 mov dx, [esi].JEMMINIT.XMSControlHandle push esi call xms_ext_realloc_emb pop esi endif @@initdone: ;-- clear all dirty + accessed flags in page table 0 mov edx, @GetPTEAddr(?PAGETAB0) mov ecx, 1000h/4 mov al, not (PTF_ACCESS or PTF_DIRTY) @@: and [edx],al add edx, 4 loop @B mov eax, [ecx+06h*4] mov [OldInt06],eax mov eax, [ecx+19h*4] mov [OldInt19],eax mov eax, ds:[67h*4] mov [OldInt67],eax if ?VDS call VDS_Init endif ;--- now clear the UMB pages and fill the UMB segment table call UMB_Init mov ebx, offset UMBsegments if ?ADDCONV ;--- check if first UMB is exactly at the top of ;--- conventional memory (40h:13h). If so, allocate ;--- the UMB and increase conventional memory. ;--- Don't do any DOS-specific work! This is to be ;--- done later by the 16-bit part. ;--- this code would move the XBDA here in protected-mode ;--- instead of in jemm16.asm. ;--- the problem with the code in jemm16.asm is that it can't ;--- control which UMB is used for the new XBDA; so the first ;--- is used, which is bad if it is A000-B7FF. ;--- currently the workaround is to use MOVEXBDA.exe for XBDA ;--- moving. if ?MOVEXBDA and ?MOVEXBDAPM push ebx test bV86Flags,V86F_MOVEXBDA jz nomovexbda movzx eax,word ptr ds:[@XBDA] ;does XBDA exist? and eax,eax jz nomovexbda if 1 ; v5.82: check similiar to jemm16.asm, move_xbda: is XBDA just above conv. memory? movzx ecx, word ptr ds:[@MEM_SIZE] shl ecx, 6 ;kB to para cmp ecx, eax jnz nomovexbda endif shl eax,4 cmp [ebx].UMBBLK.wSegm,0C000h ;first UMB in VGA region A000-BFFF? jae @F cmp [ebx+sizeof UMBBLK].UMBBLK.wSize,0 ;don't use it if there's another UMB. jz @F add ebx,sizeof UMBBLK @@: mov cx, [ebx].UMBBLK.wSize shr cx, 6 ;convert para to kB movzx dx,byte ptr [eax] ;get size of XBDA in kB @dprintf ?INITDBG, <"XBDA/UMB size=%X/%X kB",10>, dx, cx cmp cx,dx ;UMB large enough for XBDA? jb nomovexbda add ds:[@MEM_SIZE],dx push esi mov esi,eax movzx edi,[ebx].UMBBLK.wSegm @dprintf ?INITDBG, <"moving XBDA to %X",10>, di mov ds:[@XBDA],di shl edi,4 shl dx,6 ;covert kb to para sub [ebx].UMBBLK.wSize,dx add [ebx].UMBBLK.wSegm,dx movzx ecx,dx shl ecx,2 rep movsd pop esi nomovexbda: pop ebx endif ;--- is first UMB adjacent to "top of conv memory" in 40:13h? mov ax, ds:[@MEM_SIZE] ;size in KB (640 kB = 280h) shl ax, 6 ;280h -> A000h @dprintf ?INITDBG, <"[40:13]=%X UMB=%X",10>, ax, [ebx].UMBBLK.wSegm cmp ax, [ebx].UMBBLK.wSegm jnz noinc_convmem ;--- then add this UMB to conventional memory and mark UMB as allocated mov cx, [ebx].UMBBLK.wSize shr cx, 6 add ds:[@MEM_SIZE],cx @dprintf ?INITDBG, <"new conv memsize [40:13]=%X",10>, word ptr ds:[@MEM_SIZE] or byte ptr [ebx].UMBBLK.wSize+1, UMB_ALLOCATED add ebx, sizeof UMBBLK noinc_convmem: endif if ?MOVEHIGH cmp [esi].JEMMINIT.NoHigh,0 ;NOHI active? jnz @@nomovehigh mov ecx,[dwRes] cmp ecx,0A0000h ;already loaded high? jnc @@nomovehigh @@anothertry: movzx eax,[ebx].UMBBLK.wSegm and eax,eax jz @@nomovehigh if 0 cmp ah,0C0h ;avoid to move into the video segments jnc @@umbok cmp byte ptr [ebx+sizeof UMBBLK].UMBBLK.wSegm+1,0 jz @@umbok add ebx,sizeof UMBBLK jmp @@anothertry @@umbok: endif mov [esi].JEMMINIT.ResUMB,ax mov esi,ecx movzx edi,ax shl edi,4 push edi mov ecx,rsofsinit movzx ecx,[ecx].RSOFS.wSizeRes mov eax,ecx rep movsb shr eax,4 add [ebx].UMBBLK.wSegm,ax sub [ebx].UMBBLK.wSize,ax pop edi mov esi, [dwRes] mov [dwRes],edi mov eax,edi shr eax,4 mov [dwRSeg], eax sub edi, esi if ?HOOK13 add [dwRFlags],edi endif add [bpstart],edi if ?INTEGRATED add [XMS_Handle_Table.xht_pArray],edi endif @dprintf ?INITDBG, <"resident part moved high, seg=%X",10>, ax @@nomovehigh: endif mov eax, [tmpFeatures] mov esp, ebp @dprintf ?INITDBG, <"activating V86 mode, esp=%X [esp]=%X %X %X",10>, ebp, dword ptr [ebp+0], dword ptr [ebp+4], dword ptr [ebp+8] .386p CLTS ; clear TS (Task Switch) flag .386 IRETD ; switch to v86 mode InitMonitor ENDP ;--- copy ROM content to a shadow page ;--- ecx = linear page number to be shadowed (0=00000,1=01000,...) ;--- (max value 3FFh = address space 3FFxxxh) ;--- edi = physical address to map CopyROM proc pushad push ecx mov cl,1 mov eax,edi call MapPhysPagesEx ;map a free page in PTE heap pop esi lea ebx, @GetPTEAddr(?PAGETAB0+esi*4) mov edx, [ebx] ;get PTE for ROM page and edx, 0FFFh or edx, edi ;copy the PTE attributes mov edi, eax ;copy ROM content shl esi, 12 mov ecx, 1000h/4 rep movsd mov [ebx], edx ;set PTE on old ROM location mov eax,cr3 ;flush TLB mov cr3,eax @dprintf ?INITDBG, <"PTE %X used to shadow page ">, edx popad @dprintf ?INITDBG, <"%X",10>, ecx ret CopyROM endp IsUMBMemory proc public if ?SPLIT cmp al,'1' jb @@isnotumb mov ah,1 cmp al,'8' jb @@isumb dec ah endif cmp al,'U' jz @@isumb cmp al,'I' jz @@isumb cmp al,'P' jnz @@isnotumb cmp [bNoFrame],0 jz @@isnotumb @@isumb: clc ret @@isnotumb: stc ret IsUMBMemory endp .text$04 ENDS if 0 .text$04z segment FLAT public 'CODE' V86_TOTAL equ $ - _start .text$04z ends endif END ================================================ FILE: src/INIT16.ASM ================================================ ;--- 16-bit initialization part ;--- to be assembled with JWasm or Masm v6.1+ .model tiny .386 CStr macro text:VARARG local sym CONST segment sym db text,0 CONST ends exitm endm FALSE equ 0 TRUE equ 1 NULL equ 0 ?USEMEMSET equ 0 ?UMBPCI equ 1 ; 1=be aware of UMBPCI-created UMBs ifndef ?NOVMWARE ?NOVMWARE equ 1 ; 1=support option NOVMW to disable VMWare detection endif include jemm.inc include jsystem.inc include jemm16.inc include debug16.inc include xms.inc ?I15RESNOSCAN equ 0 ;1=don't scan regions marked as reserved by Int 15h, ax=e820 CPUID1_PSE36 equ 17 NAMEGEN equ <"Jemm"> EMS_MAX_PAGES_ALLOWED equ 2048 ;std=2048 ( 2048 * 16kB = 32768 kB ) ife ?INTEGRATED REGS struct union struct _eax dd ? _ebx dd ? _ecx dd ? _edx dd ? ends struct _ax dw ?,? _bx dw ?,? _cx dw ?,? _dx dw ?,? ends ends REGS ends endif .data jemmini JEMMINIT <> dwAllocatedBytes DD 0 ;set by AllocAndInitMem() xms XMSPARAMS <> if ?INTEGRATED bNoE820 DB 0 ;NOE820 option; it's a noop if MAXSEXT != 0 bNoE801 DB 0 endif bVerbose DB FALSE if ?I15RESNOSCAN bI15RsvdChk db FALSE endif if ?INTEGRATED eq 0 xmsspec3 DW 0 endif bFrameWanted DB 0 ;FRAME=XX00 ExcludeTest DB FALSE;X=TEST IncludeTest DB FALSE;I=TEST if ?SPLIT SplitTest DB 0 endif MinRequest DB FALSE;MIN= has been found .const if ?INTEGRATED ENABLE_A20 equ 2 DISABLE_A20 equ 0 a20_methods label byte db 3,"kbc" ;0 (A20_KBC) db 3,"ps2" ;1 (A20_PS2) db 4,"bios" ;2 db 8,"alwayson" ;3 db 4,"fast" ;4 db 6,"port92" ;5 db 0 szKBC db "KBC",0 szPS2 db "PS/2",0 szBIOS db "BIOS",0 szAlwaysOn db "Always On",0 szFast db "Fast",0 szPort92 db "Port 92",0 even ;--- table order must match A20_ switch methods constants A20procs label word dw disable_enable_a20_KBC ;A20_KBC dw disable_enable_a20_fast ;A20_PS2 dw disable_enable_a20_BIOS ;A20_BIOS dw disable_enable_a20_dummy ;A20_ALWAYSON dw disable_enable_a20_fast ;A20_FAST dw disable_enable_a20_fast ;A20_PORT92 A20strings label word dw szKBC dw szPS2 dw szBIOS dw szAlwaysOn dw szFast dw szPort92 endif sig1 db 'EMMXXXX0',0 sig2 db 'EMMQXXX0',0 ; ID if NOEMS specified szError DB 'Error',07H,0 szWarning DB 'Warning',0 szOn DB "On",0 szOff DB "Off",0 szStartup DB "%j v", @CatStr(!",%?VERSIONHIGH,!"), ".", @CatStr(!",%?VERSIONLOW,!")," [", @CatStr(!",%@Date,!"), "]",LF,0 szCopyRight DB '%j. Parts (c) tom ehlert 2001-2006 c''t/H. Albrecht 1990',LF,0 szHelp label byte db "usage: either add a line to CONFIG.SYS: DEVICE=",NAMEEXE,".EXE [ options ]",LF db " or run it from the command line: C:\>",NAMEEXE," [ options ]",LF db "available options are:",LF if ?A20XMS or ?A20PORTS db "+A20/NOA20 A20-disable emulation on/off (default on)",LF endif if ?INTEGRATED db " A20METHOD:m set A20 switch method. Possible values for are",LF db " KBC, PS2, BIOS, FAST, PORT92 and ALWAYSON.",LF endif db " ALTBOOT use alternate reboot strategy", LF db " B=segm specify lowest segment address for EMS banking (default=4000)",LF db " D=n set DMA buffer size in kB (default=64, max is 128)",LF if ?EMX db " EMX increased EMX DOS extender compatibility",LF endif if ?FASTBOOT db " FASTBOOT fast reboot. Requires %j to be loaded in CONFIG.SYS.",LF endif db " FRAME=E000 set EMS page frame (FRAME=NONE disables frame). Any value",LF db " between 8000 and E000 is accepted, but not all will work.",LF if ?INTEGRATED db " HMAMIN=n set minimum amount in Kb to allocate the HMA.",LF endif db " I=start-end force a region to be used for UMBs. Without this option",LF db " range C000-EFFF is scanned for unused pages. May also be used",LF db " to add (parts of) regions A000-BFFF or F000-F7FF as UMBs. Don't",LF db " use this option if you don't know what you are doing!",LF db " I=TEST scan ROMs for unused pages, include found regions as UMBs",LF db " [MAX=]n limit for VCPI (and EMS if < 32M) memory in kB (default 120 MB)",LF if ?INTEGRATED db " MAXEXT=n limit extended memory usage to kB",LF if ?XMS35 db " MAXSEXT=n limit extended memory usage beyond 4 GB to kB",LF endif endif db " MIN=n reserve up to kB for EMS/VCPI memory on init (default=0)",LF if ?MOVEXBDA db " MOVEXBDA move XBDA into UMB. Some BIOSes won't allow this.",LF endif db " NOCHECK disallow access to address space without RAM (MMIO)",LF if ?INTEGRATED db " NOE801 don't use Int 15h, E801h to get amount of ext. memory",LF ;ife ?XMS35 db " NOE820 don't use Int 15h, E820h to get amount of ext. memory",LF ;endif endif db " NOEMS disable EMS handling",LF db " NODYN no dynamic XMS memory allocation (use MIN= to set fix amount)",LF db " NOHI don't move resident part into first UMB",LF db " NOINVLPG don't use INVLPG opcode",LF if ?NOVMWARE db " NOVMW don't try to detect VMWare",LF endif if ?PGE db "+PGE/NOPGE Page Global Enable feature usage on/off (default off)",LF endif db " RAM/NORAM try to supply UMBs on/off (default on)",LF db " S=start-end assume Shadow-RAM activated by UMBPCI, include it as UMB",LF if ?SB db " SB SoundBlaster driver compatibility mode",LF endif if ?SPLIT db " SPLIT regain partially used EPROM 4k pages for UMBs",LF endif db "+VCPI/NOVCPI VCPI Support on/off (default on)",LF db " VDS/NOVDS Virtual DMA Services on/off (default on)",LF if ?VME db "+VME/NOVME V86-Mode Extensions on/off (default off)",LF endif db " VERBOSE display additional details during start (abbr: /V)",LF db " X=start-end exclude region from being touched or used by %j",LF db " X=TEST scan memory region C000-EFFF for UMB exclusion",LF if ?INTEGRATED db " X2MAX=n max. value in Kb for XMS V2 free memory report (default=65535)",LF db " XMSHANDLES=n number of available XMS handles (",@CatStr(!",%?XMS_STATICHDLS,!"),"<=n<=",@CatStr(!",%?XMS_MAXHDLS,!"),", default=",@CatStr(!",%?XMS_DEFHDLS,!"),")",LF endif db LF db " '+': option can be set dynamically by running %j from the command line",LF if ?LOAD db LF db "When invoked from the command line, %j additionally will understand:",LF db " LOAD install",LF endif if ?UNLOAD db " UNLOAD uninstall",LF endif db 0 .data? if ?INTEGRATED eq 0 xmsreg REGS <> endif ;--- 256 entries for real-mode pages [00-FF]xxxh ; 'R' = RAM ; 'E' = EPROM ; 'S' = Shadow-RAM activated by UMBPCI ; 'G' = GRAPHICS region ; 'V' = VMWARE allocated, but possibly re-usable via I= (0e800-0ebffh) ; ; 'U' = possible UMB space, because nothing else found ; 'P' = PAGEFRAME ; 'I' = INCLUDE = forced from commandline ; 'X' = EXCLUDE = forbidden from commandline SystemMemory DB 100H DUP (?) .code ;--- ltoa(long n, char * s, int base); ;--- convert long to string ;--- modifies SI ltoa PROC stdcall uses edi number:dword, outb:word, base:word mov ch,0 movzx edi, base mov eax, number cmp di,-10 jne @F mov di,10 and eax,eax jns @F neg eax mov ch,'-' @@: mov si,outb add si,10 mov BYTE PTR [si],0 dec si @@nextdigit: xor edx, edx div edi add dl,'0' cmp dl,'9' jbe @F add dl,7+20h @@: mov [si],dl dec si and eax, eax jne @@nextdigit cmp ch,0 je @F mov [si],ch dec si @@: inc si mov ax,si ret ltoa ENDP ;--- little printf() implementation. ;--- understands type %c, %x, %d, %i, u%, %s ;--- and modifier l(ong) ;--- assumes DS=SS=DGROUP ;--- preserves BX,SI,DI,ES printf PROC c public uses si di fmt:ptr byte, args:VARARG local size_:word local flag:byte local longarg:byte local fill:byte local szTmp[12]:byte lea di,[fmt+2] @@L335: mov si,[fmt] nextchar: lodsb or al,al je done cmp al,'%' je formatitem call handle_char jmp nextchar done: xor ax,ax ret formatitem: push @@L335 xor dx,dx mov [longarg],dl mov al,1 mov cl,' ' cmp BYTE PTR [si],'-' jne @F dec ax inc si @@: mov [flag],al cmp BYTE PTR [si],'0' jne @F mov cl,'0' inc si @@: mov [fill],cl nextdigit: cmp BYTE PTR [si],'0' jb donenumber cmp BYTE PTR [si],'9' ja donenumber lodsb sub al,'0' cbw imul dx,dx,10 ;dx = dx * 10 add dx,ax jmp nextdigit donenumber: mov [size_],dx cmp BYTE PTR [si],'l' jne @F mov [longarg],1 inc si @@: lodsb mov [fmt],si cmp al,'x' je handle_x cmp al,'X' je handle_x cmp al,'c' je handle_c cmp al,'d' je handle_d cmp al,'i' je handle_i cmp al,'s' je handle_s cmp al,'j' je handle_j cmp al,'u' je handle_u jmp @@L359 handle_c: mov ax,[di] add di,2 @@L359: call handle_char retn handle_j: mov si,CStr(NAMEMOD) jmp @@do_outputstring260 handle_s: mov si,[di] add di,2 jmp @@do_outputstring260 handle_x: mov cx,16 jmp @@lprt262 handle_d: handle_i: mov cx,-10 jmp @@lprt262 handle_u: mov cx,10 @@lprt262: mov ax,[di] add di,2 sub dx,dx cmp cx,0 ;signed or unsigned? jge @F cwd @@: cmp [longarg],0 je @F mov dx,[di] add di,2 @@: lea si,[szTmp] invoke ltoa, dx::ax, si, cx mov si,ax @@do_outputstring260: mov ax,si mov cx,size_ .while byte ptr [si] inc si .endw sub si,ax xchg ax,si sub cx,ax .if flag == 1 .while sword ptr cx > 0 mov al,[fill] call handle_char dec cx .endw .endif .while byte ptr [si] lodsb call handle_char .endw .while sword ptr cx > 0 mov al,[fill] call handle_char dec cx .endw retn handle_char: mov dl,al cmp al,10 jnz @F mov dl,13 call @F mov dl,10 @@: mov ah,2 int 21h retn printf ENDP if 0 memcpy proc c uses si di dst:ptr byte, src:ptr byte, len:word mov di,dst mov si,src mov cx,len rep movsb ret memcpy endp endif if ?USEMEMSET memset proc c uses di dest:ptr BYTE, value:WORD, len:WORD mov di,dest mov ax,value mov cx,len rep stosb ret memset endp endif _memicmp proc c uses si di p1:ptr BYTE, p2:ptr BYTE, len:WORD mov cx,len mov si,p2 mov di,p1 nextitem: lodsb mov ah,[di] inc di or al,20h or ah,20h sub al,ah loopz nextitem cbw ret _memicmp endp skipWhite PROC c src:ptr byte mov bx,src nextitem: mov al,[bx] inc bx cmp al,' ' je nextitem cmp al,9 je nextitem dec bx mov ax,bx ret skipWhite ENDP ;--- set memory type , but honour "EXCLUDE=" and "INCLUDE=" types ;--- address: paragraph SetMemoryType PROC stdcall uses si address:word, mtype:byte mov si,address shr si,8 add si,offset SystemMemory mov al,mtype .if byte ptr [si] != 'I' || al == 'X' mov [si],al .endif ret SetMemoryType ENDP ;--- TestForSystemRAM(void *, int, int *); ;--- 1. argument is "SystemMemory" array (256 * BYTE) ;--- 2. argument is index for "SystemMemory" where to start scan ;--- 3. argument is a pointer to WORD, will return region size in paragraphs ;--- may change memory type from 'U' to 'R' ;--- modifies ES TestForSystemRAM proc c uses si di pv:ptr BYTE, index:WORD, pi:ptr WORD local result:word xor di, di ;init return code mov result,di mov si, pv mov dx, index add si, dx mov cx, 100h sub cx, dx jbe @@done @@nextpage: lodsb if 1 ;v5.80: also test 'E', since 'E' might contain RAM in v5.80 (UMBPCI) cmp al,'E' jz @F endif cmp al,'U' jz @F cmp al,'I' ;'I' is also tested, but not modified jnz @@skipitem ;so a warning can be displayed @@: ;--- test a page of conventional memory mov ax, dx shl ax, 8 mov es, ax if ?UMBPCI cmp dword ptr es:[0],"BMU$" jnz @F cmp dword ptr es:[4],"!lbT" jz isumbpci @@: endif cli mov ax, es:[0] mov bx, ax xor ax, 055AAh mov es:[0], ax cmp ax, es:[0] jnz @@noram xor ax, 0AA55h mov es:[0], ax cmp ax, es:[0] jnz @@noram mov al,[si-1] .if al == 'E' || al == 'U' ;v5.80: change 'E' to 'R' as well mov byte ptr [si-1], 'R' .endif and di, di jnz @F mov di, es @@: add result, 100h ;100h = 4kB in paragraphs jmp @@shared @@noram: and di, di jz @@shared ;skip test now, found a region mov cx,1 ;stop scanning @@shared: cmp bx, es:[0] jz @F mov es:[0],bx @@: sti @@skipitem: inc dx dec cx jnz @@nextpage @@done: mov bx, pi mov ax, result mov [bx], ax mov ax, di ret if ?UMBPCI isumbpci: mov di,es mov bx,8 .while word ptr es:[bx] mov si,es:[bx+0] ;get segment address mov dx,si mov ax,es:[bx+2] ;get size (paragraphs) add dx,ax shr si,8 add si,pv nextitem: mov byte ptr [si],'S' inc si sub ax,100h ja nextitem mov ax,dx dec ax pusha invoke printf, CStr("Memory activated by UMBPCI found at %X-%X, included", LF), word ptr es:[bx+0], ax popa add bx,2+2 .endw sub dx,di mov result,dx jmp @@done endif TestForSystemRAM endp ;--- handle I=TEST option ;--- may change 'E' to 'I' ;--- modifies ES HandleITest proc uses si mov si,0C0h .repeat .if SystemMemory[si] == 'E' mov ax,si xchg al,ah mov es, ax mov bx,0 mov al,es:[bx] inc bx .while bx < 1000h .break .if al != es:[bx] inc bx .endw .if bx == 1000h ; invoke SetMemoryType, es, 'I' invoke SetMemoryType, es, 'U' .endif .endif inc si .until si == 0F8h ret HandleITest endp ; ScanSystemMemory() ; search memory for ROMS, adapters, graphics,... ; ; builds SystemMemory map ; ; the "checks" which are done are: ; ; - memory range C000-EFFF is scanned for ROMs, ; if one is found, and "I=TEST", it is checked if there are pages ; filled with 0x00 or 0xFF. ; - with option "X=TEST", memory range C000-EFFF is also tested if ; content is 0x00 or 0xFF, anything else excludes page ; - memory range C000-EFFF is also checked for RAM. if found pages are ; regarded as "reserved" (must be explicitely included with I= or S=) ScanSystemMemory PROC uses es si di local i:word ;size EPROM in 0.5kB units local romsize:word local mem:word ;--- check for ROMs (should find VGA-Roms) ;--- also handles I=TEST mov mem,0c000h .repeat mov es,mem xor si, si .if ( word ptr es:[si] == 0AA55h ) movzx ax,byte ptr es:[si+2] ;size in 512-byte units mov di,ax mov i,ax mov romsize,ax mov si,es .if bVerbose shr ax,1 sbb cx,cx and cx,5 invoke printf, CStr("EPROM at %X, size %u.%u kB", LF), es, ax, cx .endif .while i mov di, 200H ;512-bytes units mov bx,si movzx bx,bh .if SystemMemory[bx] != 'X' mov ax, i mov cx, si .if ( cl == 0 || ax == romsize ) ;cl=0 means: 4k-page boundary invoke SetMemoryType, si, 'E' .endif .endif mov ax,di shr di,4 ;convert bytes to paras add si,di shr ax,9 ;convert bytes to 0.5kB units sub i,ax .endw mov ax,si mov bx,si movzx bx,bh .if SplitTest && al && SystemMemory[bx] == 'E' shr al,5 add al,'0' invoke SetMemoryType, si, al .endif mov ax, romsize shl ax,5 add ax, mem add ax, 07fh and al, 080h mov mem,ax .continue .endif add mem,0080H ;add 128 paragraphs (=2 kB) .until mem >= 0F000h if ?I15RESNOSCAN ;--- don't scan for RAM if we called Int 15h ax=e820h cmp bI15RsvdChk, FALSE jnz @F endif mov si, 0C0h .repeat lea ax, mem invoke TestForSystemRAM, offset SystemMemory, si, ax mov si,ax .if ax mov ax,mem add ax,si dec ax if ?UMBPCI mov es,si .if dword ptr es:[0] != "BMU$" endif invoke printf, CStr("System memory found at %X-%X, region might be in use", LF), si, ax if ?UMBPCI .endif endif mov ax,mem add ax,si movzx ax,ah mov si,ax .endif .until si == 0 @@: ;--- X=TEST ? .if ExcludeTest mov mem, 0a0H .while mem < 0f0H mov bx, mem .if SystemMemory[bx] == 'U' mov ah,BYTE PTR mem sub al,al mov es,ax sub cx,cx mov si,cx .while cx < 0fffH ;don't check the page's final byte! mov al, es:[si] .if al != 0 && al != 0ffh mov bx, mem mov SystemMemory[bx],'X' .break .endif inc cx inc si .endw .endif inc mem .endw .endif ;--- handle I=TEST .if IncludeTest call HandleITest .endif ret ScanSystemMemory ENDP ;--- find a contiguous area of 64 KB for the EMS page frame. ;--- should handle commandline option like "FRAME=D000" ;--- out: AX=frame segment address LocatePageFrame PROC uses di si local page_:word local bHardWanted:byte local bSearching:byte local bWarning:byte xor al,al mov bSearching,al mov bWarning,al mov bHardWanted,al .if bFrameWanted mov bHardWanted,1 .else mov bFrameWanted,0e0h .endif xor si,si ;frame address movzx di,bFrameWanted ; Line 300 xor bx,bx .repeat mov al, SystemMemory[bx][di] ;--- v5.76: 'I' is also ok ; .if al == 'U' .if al == 'U' || al == 'I' ; .elseif bHardWanted && di >= 80h && ( al == 'R' || al == 'G' ) mov bWarning,1 .else .break .endif inc bx .until bx == 16 .if bx == 16 .if ( di & 3 ) ;v5.78: warn if frame doesn't begin on a 16 kB mov bWarning,1 ;physical page .endif mov si,di jmp $frameset251 .endif .if bHardWanted invoke printf, CStr("Selected page frame %02x00 not accepted, scanning for a valid one...", LF), bFrameWanted .endif mov bSearching,1 ;--- scan memory for a page frame ;--- scan from high to low E800..A000 mov di,0E8H .repeat xor bx,bx ;--- v5.78: accept both 'U' and 'I' ; .while SystemMemory[bx][di] == 'U' .while bx < 16 && (SystemMemory[bx][di] == 'U' || SystemMemory[bx][di] == 'I') inc bx .endw .break .if bx == 16 sub di,4 ;ensure that frame starts at an EMS "physical page" .until di < 0a0h .if bx != 16 invoke printf, CStr("%s: no suitable page frame found, EMS functions limited.", LF), addr szWarning mov jemmini.NoFrame, TRUE xor ax,ax jmp exit .endif mov si, di $frameset251: .if bVerbose || bSearching invoke printf, CStr("Using page frame %02x00", LF), si .endif .if bWarning && (!bSearching) || di < 0C0h invoke printf, CStr("%s: page frame %02x00 might not work reliably", LF), offset szWarning, si .endif if ?USEMEMSET mov ax,offset SystemMemory add ax,si invoke memset, ax, 'P', 16 else mov di,offset SystemMemory add di,si mov cx,16 mov al,'P' rep stosb endif mov ax,si mov ah,al sub al,al exit: ret LocatePageFrame ENDP ;--- check if a page is intended for UMB ;--- out: AX=1 if yes, else AX=0 IsUMBMemory PROC stdcall pg:word cmp jemmini.NoRAM,0 jne notumb mov bx, pg mov al, SystemMemory[bx] cmp al,'U' je isumb cmp al,'I' je isumb if ?SPLIT cmp al,'0' jbe @F cmp al,'8' jb isumb @@: endif cmp jemmini.NoEMS,0 jne @F cmp jemmini.NoFrame, FALSE je notumb @@: cmp al,'P' jne notumb isumb: mov ax,1 exit: ret notumb: xor ax,ax jmp exit IsUMBMemory ENDP ;--- get the number of pages needed for UMBs UMBpageswanted PROC uses di si xor di,di mov si,0A0H .repeat invoke IsUMBMemory, si add di,ax inc si .until si == 0F8h mov ax,di ret UMBpageswanted ENDP if ?INTEGRATED eq 0 xmscall proc stdcall uses si function:BYTE mov si,offset xmsreg mov ebx,[si].REGS._ebx mov edx,[si].REGS._edx mov ah, function @dprintf ?XMSRMDBG, <"xms call: ax=%X ebx=%lX edx=%lX",10>, ax, ebx, edx call [xms.Driver] mov [si].REGS._eax,eax mov [si].REGS._ebx,ebx mov [si].REGS._ecx,ecx mov [si].REGS._edx,edx @dprintf ?XMSRMDBG, <"xms ret : ax=%X ebx=%lX ecx=%lX edx=%lX",10>, ax, edx, ecx, edx ret xmscall endp ;--- get status of XMS memory ;--- sets global variables xms.mem_largest, xms.mem_free XMSGetMemoryStatus PROC c usev3:word mov xmsreg._bx,-1 .if usev3 invoke xmscall, XMS_V3_QUERYMEM ;get v3 total free memory (edx) and largest free block (eax) .if bl == 0 ;BL must be 00 mov xms.mem_highest, ecx jmp ok .endif .endif invoke xmscall, XMS_V2_QUERYMEM ;get total free memory (dx) and largest free block (ax) .if bl == 0 movzx eax, ax movzx edx, dx mov xms.mem_highest, (65535+1024)*1024-1 ;assume max extended memory for v2 (65535 kB) jmp ok .endif xor ax,ax ret ok: mov xms.mem_largest, eax mov xms.mem_free, edx mov ax,1 ret XMSGetMemoryStatus ENDP ;--- alloc extended memory for monitor and preallocated page pool ;--- JEMM386 only XMSAllocMemory PROC c usev3:word, dwSizeKB:dword local xmshandle:word mov eax, dwSizeKB mov xmsreg._edx, eax .if eax >= 10000h || usev3 invoke xmscall, XMS_V3_ALLOCEMB ;allocate EMB .else invoke xmscall, XMS_V2_ALLOCEMB ;allocate EMB .endif .if ax mov ax, dx .endif @dprintf ?INITRMDBG, <"XMSAllocMemory: exit, ax=%X",10>, ax ret XMSAllocMemory ENDP endif I15CheckRsvdRegion proc uses si di local mmap:E820MAP @dprintf ?INITRMDBG, <"I15CheckRsvdRegion: enter",10> xor ebx,ebx lea di,mmap nextitem: mov edx,SMAP mov ecx, sizeof E820MAP xor eax,eax mov mmap.baselow,eax ; insurance against buggy BIOS mov mmap.type_,eax mov mmap.lenlow,eax mov ax,0e820h int 15h cmp eax,SMAP jne done cmp ecx,sizeof E820MAP ; didn't return all the info needed, assume done jb done cmp mmap.type_,2 ; reserved memory ? jne itemdone mov eax, mmap.baselow cmp eax, 0C0000h jb itemdone cmp eax,100000h jnc itemdone mov ecx, mmap.lenlow shr eax, 4 shr ecx, 12 mov si,ax .if bVerbose push cx shl cx,2 invoke printf, CStr("Int15, ax=E820: reserved region=%04X, size=%u kB", LF), si, cx pop cx .endif .while cx invoke SetMemoryType, si, 'E' add si,100h dec cx .endw itemdone: cmp ebx,0 ;was this the last entry? jnz nextitem if ?I15RESNOSCAN mov bI15RsvdChk, TRUE endif done: ret I15CheckRsvdRegion endp if ?INTEGRATED ;--- return size of free extended memory block in EAX ;--- may set global variables xms.mem_largest, xms.mem_free ;--- returns free memory in kB in EAX. ;--- called by AllocAndInitMem() I15GetMemoryStatus proc stdcall uses si di local maxvalue:DWORD local mmap:E820MAP if ?XMS35 cmp xms.smem_used,FALSE jnz @F endif cmp [bNoE820],0 ; NOE820 option set? jne @@e801_check @@: @dprintf ?INITRMDBG, <"I15GetMemoryStatus: get extended memory with int 15, E820",10> ;--- try 0e820h first xor ebx, ebx mov si, 0 mov maxvalue, ebx e820_nextitem: ;--- ebx offset is updated with each successive int 15h mov edx,SMAP mov ecx, sizeof E820MAP lea di,mmap xor eax,eax mov mmap.baselow,eax ; insurance against buggy BIOS mov mmap.type_,eax mov mmap.lenlow,eax mov ax,0e820h clc int 15h setc dl ; keep carry flag status cmp eax,SMAP jne e820_bad ; failure cmp dl,1 je e820_done ; CF doesn't have to signal fail, can just mean done cmp ecx,sizeof E820MAP ; didn't return all the info needed, assume done jb e820_done cmp mmap.type_,1 ; memory available to OS? jne e820_itemdone mov edx, mmap.baselow mov eax, mmap.basehigh cmp eax, 0 ; memory beyond 4 GB? ife ?XMS35 jnz e820_itemdone ; ignore item else jnz @F endif cmp edx, 100000h ; has to live in extended memory jb e820_itemdone @@: if ?XMS35 test eax, xms.maxhigh ;block start beyond 4GB/1TB? jnz e820_itemdone endif @dprintf ?INITRMDBG, <"I15GetMemoryStatus: available memory found at %lX%08lX",10>, eax, edx mov ecx, mmap.lenlow if ?XMS35 shrd edx, eax, 10 mov eax, mmap.lenhigh shrd ecx, eax, 10 mov eax, edx add eax, ecx ;block crossing 4TB (XMS v3 limit)? jc @F dec eax test eax, 0C0000000h;block crossing 1TB barrier (PSE-36 limit)? jz e820_itemok @@: mov ecx, 0C0000000h ;truncate the block to ensure it fits in the first TB sub ecx, edx else shr ecx, 10 shr edx, 10 endif e820_itemok: cmp si,?XMS_STATICHDLS ;reached max capacity of the static handle array? jnz @F invoke printf, CStr("%s: E820 - too many ext memory blocks, block %lX ignored!", LF), offset szWarning, edx jmp e820_itemdone @@: call I15SetHandle ;set xms block, sizeK in ECX, baseK in EDX inc si e820_itemdone: cmp ebx,0 ;was this the last entry? jnz e820_nextitem e820_bad: e820_done: mov eax, xms.mem_largest and eax, eax jnz _ret ;--- try 0e801h, but set up the registers to fail status because not ;--- all BIOS's properly return the carry flag on failure @@e801_check: cmp [bNoE801],0 ;NOE801 option set? jne @@try_88h @dprintf ?INITRMDBG, <"I15GetMemoryStatus: get extended memory with int 15, E801",10> xor ax,ax mov bx,ax mov cx,ax mov dx,ax mov ax,0e801h clc int 15h jc @@try_88h mov ax,cx or ax,dx je @@try_88h ; if dx is > 0, then cx should be 3c00h since that's full 1-16M range ; if cx != 3c00h use cx and not dx cmp cx,3c00h je @F cmp dx,0 je @F xor dx,dx @@: movzx edx,dx shl edx,6 ; convert 64K blocks to 1K movzx eax,cx add eax,edx cmp eax,64 ; only use if useful amount ja @@exit @@try_88h: ;--- e801h didn't do the trick, fall back to old 88h with 64M max @dprintf ?INITRMDBG, <"I15GetMemoryStatus: get extended memory with int 15, 88",10> clc mov ah,88h int 15h movzx eax,ax jnc @@exit xor ax,ax @@exit: mov ecx, eax mov edx, 1024 call I15SetHandle ; set xms block, sizeK in ECX, baseK in EDX _ret: @dprintf ?INITRMDBG, <"I15GetMemoryStatus: exit, xms.mem_free=%lX",10>, xms.mem_free ret I15GetMemoryStatus endp ;--- get A20 method from cmdline; ;--- out: NC ok, AL=method. C if method unknown. GetA20Method PROC stdcall uses si di cmdline:ptr BYTE mov di, offset a20_methods mov ah, 0 cld nextitem: movzx cx, byte ptr [di] stc jcxz @@failed inc di mov si, cmdline @@: lodsb or al, 20h scasb loopz @B jz @@found add di, cx inc ah jmp nextitem @@found: mov di, cmdline ; remove method from cmdline @@: lodsb stosb and al, al jnz @B mov al, ah @@failed: ret GetA20Method ENDP ;--- there are 3 A20 switch procs: ;--- 1. KBC (port 64h/60h) ;--- 2. fast, ps2, port92 (port 92h) ;--- 3. BIOS (int 15h, ax=240xh) ; try turning A20 on or off from current to see if it works ; KBC HIMEM method ; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack disable_enable_a20_KBC proc pushf cli ; shut off interrupts while we twiddle call Sync8042 ; check keyboard controller ready mov al,0D1h ; Send D1h out 64h,al call Sync8042 mov al,0ddh ; or df=dd+2 or al,ah ; disable/enable A20 command (DDh/DFh) out 60h,al call Sync8042 ; wait up to 20 microseconds for A20 line to settle mov al,0FFh ; pulse output port NULL out 64h,al call Sync8042 popf ret Sync8042: xor cx,cx @@: in al,64h and al,2 loopnz @B retn disable_enable_a20_KBC endp ; the so-called 'fast' A20 method replacement code ; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack disable_enable_a20_fast proc pushf in al,92h or ah,ah jne deaf_on ; turning on A20 test al,2 je deaf_done ; already flagged off, don't do it again, might upset something and al,NOT 2 ; set A20 bit off jmp deaf_out ; ah == 2 deaf_on: test al,ah jne deaf_done ; already flagged on or al,ah ; set A20 bit on deaf_out: out 92h,al ; wait until it gets on or off, possibly superfluous, code opinion differs xor cx,cx @@: in al,92h and al,2 cmp al,ah loopne @B deaf_done: popf ret disable_enable_a20_fast endp ; BIOS A20 method ; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack ; don't check for errors, assume BIOS works more than once on same call, ; if it doesn't, not much we can do about it anyway ; disable_enable_a20_BIOS proc pushf push dx sub sp,12 ; give buggy BIOS some stack to chew on without causing problems ; one word might suffice, but let's be really safe cli shr ah,1 ; ah to 0 or 1 mov al,24h xchg ah,al ; ax == 2400h to turn off, 2401h to turn on int 15h add sp,12 ; restore potentially gnawed-on stack pop dx popf ret disable_enable_a20_BIOS endp disable_enable_a20_dummy proc or ah,ah ret disable_enable_a20_dummy endp ;--- get current status of A20; ;--- out: Z = A20 disabled; get_a20_status proc uses ds es cx si di mov cx,-1 mov es,cx mov si,10h inc cx mov ds,cx mov di,20h mov cl,4 repz cmpsd ret get_a20_status endp ;--- test A20 state; ;--- in: SI=offset a20proc for disable/enable A20; ;--- out: C set if failed, NC if success. test_A20_proc proc call get_a20_status setnz dl jz @F ; jmp if A20 disabled mov ah, DISABLE_A20 call si ; try to disable A20 call get_a20_status jnz @@fail ; jmp if A20 not disabled @@: ; try to enable A20 (always disabled at this point) mov ah, ENABLE_A20 call si call get_a20_status jz @@fail ; jmp if A20 not enabled or dl,dl jne @@ok ; A20 was enabled on entry, done mov ah, DISABLE_A20 call si call get_a20_status jnz @@fail ; A20 not disabled @@ok: clc ret @@fail: stc ret test_A20_proc endp ; check if BIOS flags port 92h fast method supported detect_fast proc stc mov ax,2403h int 15h jc @@fail or ah,ah jne @@fail test bl,2 ;PS/2 supported? je @@fail mov si,offset disable_enable_a20_fast call test_A20_proc ret @@fail: stc ret detect_fast endp ; check if BIOS flags PS/2 present, to try port 92h fast method used by PS/2's ; shares enable/disable code with fast detect_PS2 proc uses es mov ah,0c0h ; get system description vector in es:bx stc int 15h jc @@fail ; not a PS/2 ; test feature information byte 1, micro channel implemented bit test BYTE ptr es:[bx+5],2 jz @@fail ; not micro channel mov si,offset disable_enable_a20_fast call test_A20_proc ret @@fail: stc ret detect_PS2 endp ; check if port 92h fast method supported without BIOS or PS/2 test ; shares enable/disable code with fast and PS/2 detect_port92 proc mov si,offset disable_enable_a20_fast call test_A20_proc ret detect_port92 endp detect_BIOS proc stc ; preset carry flag mov ax,2402h ; get gate status int 15h jc @@fail or ah,ah jne @@fail ; mov cl,al ; save status mov si,offset disable_enable_a20_BIOS call test_A20_proc ret @@fail: stc ret detect_BIOS endp detect_KBC proc mov si,offset disable_enable_a20_KBC call test_A20_proc ret detect_KBC endp ;--- get the A20 method to use if not set by cmdline option; ;--- out: NC ok, AL=method#; C failure. InitA20 proc c uses si mov al, jemmini.A20Method cmp al, -1 jnz done call get_a20_status ; check if the A20 line is on, if so assume it's always on mov al, A20_ALWAYSON jnz done ;--- not on, try other methods call detect_fast; see if port 92h (2403h BIOS call) handler supported mov al, A20_FAST jnc done call detect_PS2 ; see if port 92h (PS/2 signature) handler supported mov al, A20_PS2 jnc done call detect_KBC ; see if KBC handler supported mov al, A20_KBC jnc done ; try BIOS here, demoted from first in line because unreliable BIOS ; versions of A20 control exist call detect_BIOS; see if BIOS A20 handler supported mov al, A20_BIOS jnc done ; see if fast port 92h handler supported without BIOS or PS/2 signature ; leave this test until last because messing with port 92h is ; reported to crash some machines which don't support that method call detect_port92 mov al, A20_PORT92 jnc done stc ; out of options to try, return error ret done: clc ret InitA20 endp endif ;--- allocates and initializes extended memory for jemm; ;--- is called after the memory requirements have been calculated. ;--- will set jemmini values Monitorstart, MaxPhysMem, XMSControlHandle. AllocAndInitMem PROC c uses esi di kbneeded:dword local ulcalc:dword local PotentialEmsVcpiMemory:dword local dwMinOriginal:dword @dprintf ?INITRMDBG, <"AllocAndInitMem: enter",10> mov eax, jemmini.MinMem16k mov dwMinOriginal, eax ; the DMA buffer must be 64kb aligned. Since EMS pages *must* be ; allocated from memory physically "behind" the DMA buffer, there may ; be some space wasted, max 60-16=44 kB .if jemmini.DMABufferSize > 4 .if jemmini.DMABufferSize > 64 mov ax, 32 .else mov ax,jemmini.DMABufferSize shr ax,1 .endif movzx eax,ax add kbneeded,eax .if bVerbose invoke printf, CStr("%u kB to account for DMA buffer 64 kB alignment", LF), ax .endif .endif if ?INTEGRATED invoke I15GetMemoryStatus .if !eax invoke printf, CStr("%s: can't get I15 memory status", LF), offset szError $L569: xor ax,ax ret .endif else invoke XMSGetMemoryStatus, xmsspec3 .if !ax invoke printf, CStr("%s: can''t get XMS memory status", LF), offset szError $L569: xor ax,ax ret .endif endif .if bVerbose if ?INTEGRATED invoke printf, CStr("I15: largest free block %lu kB, free memory %lu kB", LF), xms.mem_largest, xms.mem_free else invoke printf, CStr("XMS: largest free block %lu kB, free memory %lu kB", LF), xms.mem_largest, xms.mem_free endif .endif ;--- if pool sharing is disabled (option NODYN ;no XMS handle table for jemm386) ;--- AND option MIN= is not given: ;--- calculate a value for MIN= which will be preallocated. .if jemmini.NoPool && (!MinRequest) .if word ptr xms.mem_largest+2 > 0 ;more than 64 MB? mov ax, 8000H ;then use 32 MB .else mov ax,WORD PTR xms.mem_largest+0 shr ax,1 .endif shr ax,4 movzx eax,ax mov jemmini.MinMem16k,eax .if bVerbose invoke printf, CStr("default preallocated memory=%u 16k-pages", LF), ax .endif .endif ;--- reality check to throttle requests far beyond available XMS, later actual ;--- adjustments are small and need not be compensated for here .if jemmini.MinMem16k mov eax,jemmini.MinMem16k shl eax,4 add eax,[kbneeded] mov ecx, kbneeded mov edx, xms.mem_largest .if eax > edx && ecx < edx sub edx,kbneeded shr edx, 4 mov jemmini.MinMem16k,edx .endif ;--- leave a little extended memory, if possible, for programs that want some XMS mov edx, xms.mem_largest mov eax, kbneeded add eax, 384 mov ecx,jemmini.MinMem16k shl ecx,4 add ecx,384 add ecx, kbneeded .if edx > eax && edx < ecx mov eax, xms.mem_largest sub eax, [kbneeded] sub eax,384 shr eax,4 mov jemmini.MinMem16k,eax .endif .endif ;--- default is: all memory mov eax, xms.mem_free .if eax > kbneeded sub eax, kbneeded .else xor eax,eax .endif mov PotentialEmsVcpiMemory, eax .if jemmini.NoPool ;/* Pool sharing off? */ mov eax, jemmini.MinMem16k shl eax, 4 .if eax < xms.mem_free mov eax, jemmini.MinMem16k shl eax, 4 mov PotentialEmsVcpiMemory, eax .endif .endif ; /* MIN= has higher priority than MAX= */ mov eax, jemmini.MinMem16k .if eax > jemmini.MaxMem16k mov jemmini.MaxMem16k,eax .endif ; /* MaxMem16k may have been set by MAX=, and above the limit */ mov eax,jemmini.MaxMem16k shl eax,4 .if eax > [PotentialEmsVcpiMemory] mov eax,[PotentialEmsVcpiMemory] shr eax,4 mov jemmini.MaxMem16k,eax .endif ; /* MaxMem16k may have been set by MAX=, and below 32 MB! */ ; /* this is valid, but then adjust max EMS pages as well */ movzx eax,jemmini.MaxEMSPages .if jemmini.MaxMem16k < eax mov ax,WORD PTR jemmini.MaxMem16k mov jemmini.MaxEMSPages,ax .endif ; /* if MIN= has been set adjust max. EMS pages */ movzx eax,jemmini.MaxEMSPages .if jemmini.MinMem16k > eax .if jemmini.MinMem16k > MAX_EMS_PAGES_POSSIBLE mov ax, MAX_EMS_PAGES_POSSIBLE .else mov ax, word ptr jemmini.MinMem16k+0 .endif mov jemmini.MaxEMSPages, ax .endif .if bVerbose mov eax,jemmini.MaxMem16k shl eax,4 invoke printf, CStr("potential/max. VCPI memory: %lu/%lu kB", LF), PotentialEmsVcpiMemory, eax .endif ; the memory pooling need ((XMS total / 1.5M) + 1) * 64 bytes ; for pool allocation table entries ; 1.5M is pool allocation maximum memory control, ; 64 is pool block size, ; if dynamic XMS allocation is on, 128 more items are needed, ; which represent the maximum number of XMS handles mov eax, jemmini.MaxMem16k shl eax,4 mov ecx,1536 ;00000600H xor edx,edx div ecx add eax,2 mov ulcalc,eax .if !jemmini.NoPool add [ulcalc],128 .endif mov eax,ulcalc shl eax,6 mov ulcalc,eax ; /* 4+1 bytes for each EMS page needed */ ; /* 255*4 bytes for EMS handle table */ ; /* 255*8 bytes for EMS name table */ ; /* 64*16 bytes for EMS save status table (EMS_MAXSTATE) */ movzx eax, jemmini.MaxEMSPages imul eax, 5 add eax,255*4+255*8+64*4 add ulcalc,eax mov eax,ulcalc ;/* convert bytes back to K */ add eax,1023 shr eax, 10 add eax,3 ;/* 4k page align */ and al,0FCh mov ulcalc,eax .if bVerbose invoke printf, CStr("%lu kB needed for VCPI and EMS handling", LF), eax .endif mov eax, ulcalc add kbneeded, eax mov eax,jemmini.MinMem16k shl eax, 4 add eax, kbneeded mov esi, eax if ?INTEGRATED eq 0 invoke XMSAllocMemory, xmsspec3, eax .if !ax invoke printf, CStr("%s: can't allocate enough XMS memory(%lu kB)", LF), offset szError, esi jmp $L569 ;exit with error .endif mov jemmini.XMSControlHandle, ax mov xmsreg._dx, ax invoke xmscall, XMS_LOCKEMB ;lock EMB (returns phys address in DX:BX) .if !ax invoke printf, CStr("%s: can't lock XMS memory", LF), offset szError invoke xmscall, XMS_FREEEMB jmp $L569 ;exit with error .endif else invoke I15AllocMemory, 0, esi .if !ax invoke printf, CStr("%s: can't allocate enough I15 memory(%lu kB)", LF), offset szError, esi jmp $L569 ;exit with error .endif mov jemmini.XMSControlHandle, ax endif mov eax,jemmini.MinMem16k .if eax < dwMinOriginal shl eax, 4 invoke printf, CStr("%s: MIN has been reduced to %lu kB", LF), offset szWarning, eax .endif mov eax, esi shl eax, 10 mov dwAllocatedBytes, eax if ?INTEGRATED eq 0 ;--- xmsreg contains the locked physical address mov ax,xmsreg._dx shl eax,16 mov ax,xmsreg._bx mov jemmini.MonitorStart+0,eax .if eax >= 1000000h ;v5.80: if EMB is beyond the 16 MB barrier, display a warning invoke printf, CStr("%s: address of allocated EMB (=%lX) is beyond 16MB", LF), offset szWarning, eax .endif else invoke GetEMBBase, jemmini.XMSControlHandle mov jemmini.MonitorStart, eax if ?XMS35 .if xms.smem_used == FALSE mov cs:xms_smax_noe820,0 ;disable INT15, ax=e820h check mov jemmini.xms_version,INTERFACE_VER_MAXSEXT0 ;make XMM report v3.0 interface .endif endif endif if 0 mov ecx, xms.mem_free shl ecx, 10 ;convert kB to bytes add ecx, jemmini.MonitorStart mov jemmini.MaxPhysMem, ecx else mov eax, xms.mem_highest mov jemmini.MaxPhysMem, eax endif mov ax,1 @dprintf ?INITRMDBG, <"AllocAndInitMem: exit, MonitorStart=%lX, MaxPhysMem=%lX",10>, jemmini.MonitorStart,?INITRMDBG, jemmini.MaxPhysMem ret AllocAndInitMem ENDP ;--- toupper(char) returns uppercase character toupper PROC pop cx pop ax push cx cmp al,'a' jb @F cmp al,'z' ja @F sub al,20h @@: ret toupper ENDP NotInstalled proc MOV DX,CStr( NAMEGEN," is not installed. (Enter ",NAMEEXE," -? for help)",CR,LF,'$' ) MOV AH,9 INT 21H ret NotInstalled endp ;--- check if there's an installed Jemm386/JemmEx monitor program IsJemmInstalled proc c @dprintf ?EMXRMDBG, <"IsJemmInstalled enter",10> mov dx, offset sig1 mov ax, 3D00h int 21h jnc @F mov dx, offset sig2 mov ax, 3D00h int 21h jc @@nojemm1 @@: @dprintf ?EMXRMDBG, <"EMM device found",10> mov bx,ax xor ax,ax push ax push ax push ax mov cx,6 ;read 6 bytes mov dx,sp mov ax,4402h ;read ioctl int 21h pop ax ;version pop cx ;API entry offs pop cx ;API entry segm jc @@nojemm2 cmp ax,0028h ;this is JEMM! jnz @@nojemm2 mov ax,bx ;return the file handle @dprintf ?EMXRMDBG, <"Jemm found",10> clc ret @@nojemm2: @dprintf ?EMXRMDBG, <"Jemm not found",10> mov ah,3Eh int 21h @@nojemm1: stc ret IsJemmInstalled endp if ?UNLOAD ;--- try to unload Jemm386 TryUnload proc c uses di si local handle:word local resparm[2]:word if UMB_MAX_BLOCKS le 8 local buff[size UMBBLK * 8]:byte else local buff[size UMBBLK * UMB_MAX_BLOCKS]:byte endif invoke IsJemmInstalled jc @@nojemm mov bx, ax ;EMMXXXX0 handle mov handle,ax @dprintf ?UNLRMDBG, <"TryUnload, Jemm installed",10> mov byte ptr resparm,EMMDEV_GETRES ;get resident segment lea dx,resparm mov cx,sizeof resparm ;returns 2 words mov ax,4402h ;read ioctl (get monitor's resident segment/size) int 21h jc @@nojemm2 invoke printf, CStr("found Jemm instance at segment %x", LF), resparm[0] ;--- check if any INT hooked by jemm has been stolen invoke CheckIntHooks, resparm[0] jc @@nouninst @dprintf ?UNLRMDBG, <"TryUnload, no stolen ints detected",10> ;--- check if any UMB is allocated mov buff,EMMDEV_GETUMBS ;call "get umbs" function lea dx, buff mov cx,UMB_MAX_BLOCKS * size UMBBLK mov bx,handle mov ax,4402h ;read ioctl int 21h jc @@nouninst @dprintf ?UNLRMDBG, <"TryUnload, got UMBs from Jemm",10> mov si,dx xor dx,dx mov cx,UMB_MAX_BLOCKS @@: lodsw ;segment lodsw ;size + flag or dx,ax loop @B ; mov sp,si test dh,80h ;any umb allocated? jnz @@nouninst @dprintf ?UNLRMDBG, <"TryUnload, no allocated UMBs",10> ;-- close EMMXXXX0 handle mov ah,3Eh int 21h if ?INTEGRATED ;-- get current XMS (must be Jemm386) invoke XMSinit les si, xms.Driver cmp byte ptr es:[si],0EBh ;anyone hooked into XMS? jnz @@nouninst @dprintf ?UNLRMDBG, <"TryUnload, no XMS hookers detected",10> ;-- check if any XMS memory is in use push ds lds si, jemmini.XMSHandleTable mov ax, ds or ax, si jz @@xmsused mov cx, [si].XMS_HANDLETABLE.xht_numhandles lds si, [si].XMS_HANDLETABLE.xht_pArray add si, size XMS_HANDLE ;dont check first entry (is Jemm itself) dec cx nextitem: cmp [si].XMS_HANDLE.xh_flags, XMSF_USED jz @@xmsused add si, size XMS_HANDLE loop nextitem @@xmsused: pop ds jz @@nouninst @dprintf ?UNLRMDBG, <"TryUnload, no EMBs are used",10> endif ;-- check if any EMS/VCPI memory is in use mov ah,4Bh ;get number of open handles (handle 0 is ok) int 67h cmp bx,01h ;any handle > 0000 allocated? ja @@nouninst @dprintf ?UNLRMDBG, <"TryUnload, no EMS handles are used",10> ;-- todo: check VCPI ;-- ok to unload. mov ax,resparm[0] ;get current Emm386 segment call UnloadJemm ;this will destroy contents of si and di jc @@nouninst ife ?INTEGRATED mov dx,ax mov ah, XMS_UNLOCKEMB ;unlock block call [xms.Driver] mov ah, XMS_FREEEMB ;free block call [xms.Driver] else movzx bx, al add bx,bx mov ah,DISABLE_A20 call A20procs[bx] endif MOV DX, CStr( NAMEGEN," unloaded",CR,LF,'$' ) @@exit: MOV AH,9 INT 21H @@exit2: ret @@nojemm2: @@nojemm: call NotInstalled jmp @@exit2 @@nouninst: MOV DX, CStr( NAMEGEN," cannot be unloaded.",CR,LF,'$' ) jmp @@exit TryUnload endp endif ;--- display status of a running Jemm386/JemmEx EmmStatus proc c uses di si local handle:word if UMB_MAX_BLOCKS le 8 local buff[size UMBBLK * 8]:byte else local buff[size UMBBLK * UMB_MAX_BLOCKS]:byte endif mov handle,-1 invoke IsJemmInstalled jc jemm_not_found mov handle,ax mov bx, ax lea dx, [buff] ;get version mov byte ptr [buff],EMMDEV_VERSION mov cx,2 mov ax,4402h ;read ioctl (get version) [Emm386 compatible call] int 21h jc jemm_not_found movzx ax, byte ptr [buff+0] movzx cx, byte ptr [buff+1] invoke printf, CStr( "%j v%u.%02u installed.", LF), ax, cx ;--- get EMS, VCPI, UMB, VME, A20 infos lea dx, [buff] mov byte ptr [buff],EMMDEV_SYSVARS mov cx,sizeof EMX06 mov bx, handle mov ax,4402h ;read ioctl int 21h jc done @dprintf ?EMXRMDBG, <"EmmStatus: read ioctl(6) ok",10> mov di, ax lea si, [buff] cmp [si].EMX06.e06_NoEMS, 0 mov dx, offset szOff jnz @F mov dx, offset szOn @@: invoke printf, CStr("EMS is %s"), dx cmp [si].EMX06.e06_NoEMS,0 jnz @@nodispframe ;dont display FRAME status if no EMS mov ah,42h int 67h mov ax, dx sub ax, bx invoke printf, CStr(", %u of max. %u pages allocated"), ax, dx mov ax, CStr(", no Page Frame") mov cx, [si].EMX06.e06_Frame jcxz @F mov ax, CStr(", Page Frame at %04X") @@: invoke printf, ax, cx @@nodispframe: invoke printf, CStr(".", LF) cmp [si].EMX06.e06_NoVCPI, 0 ;_NoVCPI flag jnz @F invoke printf, CStr("VCPI is On, %lu of max. %lu pages allocated.", LF), [si].EMX06.e06_VCPIUsed, [si].EMX06.e06_VCPITotal jmp @@vcpidone @@: invoke printf, CStr("VCPI is Off.", LF) @@vcpidone: mov ax, 64 ;default DMA buffer size cmp di, 16 ;could the DMA buffer size be read? jb @F mov ax, [si].EMX06.e06_DMASize @@: mov ecx, [si].EMX06.e06_DMABuff invoke printf, CStr("DMA buffer at %08lX, size %u kB.", LF), ecx, ax if ?A20PORTS or ?A20XMS mov ax, offset szOff cmp [si].EMX06.e06_NoA20, 0 jnz @F mov ax, offset szOn @@: invoke printf, CStr("A20 emulation is %s.", LF), ax endif if ?VME mov ax, offset szOff cmp [si].EMX06.e06_NoVME, 0 jnz @F mov ax, offset szOn @@: invoke printf, CStr("VME is %s.", LF), ax endif if ?PGE mov ax, offset szOff cmp [si].EMX06.e06_NoPGE, 0 jnz @F mov ax, offset szOn @@: invoke printf, CStr("PGE is %s.", LF), ax endif lea dx, [buff] mov byte ptr [buff],EMMDEV_GETUMBS mov cx, UMB_MAX_BLOCKS * sizeof UMBBLK mov bx, handle mov ax,4402h ;read ioctl int 21h jc done @dprintf ?EMXRMDBG, <"EmmStatus: read ioctl(7) ok",10> lea si,[buff] mov cx, UMB_MAX_BLOCKS .repeat mov ax, [si].UMBBLK.wSegm .break .if !ax push cx mov dx, [si].UMBBLK.wSize mov cx, CStr("allocated") test dh,80h jnz @F mov cx, CStr("free") @@: and dh, 7Fh ;reset highest flag add dx, ax dec dx invoke printf, CStr("UMB supplied at %04X-%04X, %s.", LF), ax, dx, cx pop cx add si, size UMBBLK .untilcxz done: mov bx, handle cmp bx, -1 jz @F mov ah, 3Eh int 21h @@: ret jemm_not_found: @dprintf ?EMXRMDBG, <"EmmStatus: Jemm not found",10> call NotInstalled jmp done EmmStatus endp ;--- pass options to an already installed Jemm386/JemmEx monitor CMD15 struct cmd db ? ;=EMMDEV_UPDATE EMX15W <> CMD15 ends EmmUpdate proc c uses di local buff:CMD15 @dprintf ?EMXRMDBG, <"EmmUpdate enter",10> xor di,di invoke IsJemmInstalled jnc @F call NotInstalled jmp @@exit @@: mov bx, ax ;save file handle ;--- fill structure to be send to running Jemm386/JemmEx mov buff.cmd,EMMDEV_UPDATE if ?VME mov al, [jemmini.NoVME] else mov al,-1 endif mov buff.e15_bVME,al if ?A20PORTS or ?A20XMS mov al, [jemmini.NoA20] else mov al,-1 endif mov buff.e15_bA20,al mov al, [jemmini.NoVCPI] mov buff.e15_bVCPI,al if ?PGE mov al, [jemmini.NoPGE] else mov al,-1 endif mov buff.e15_bPGE,al mov cx,sizeof CMD15 lea dx,[buff] mov ax,4403h ;write ioctl int 21h jc @F @dprintf ?EMXRMDBG, <"EmmUpdate: write ioctl(15) ok",10> inc di @@: mov ah,3Eh ;close EMMXXXX0 handle int 21h @@exit: @dprintf ?EMXRMDBG, <"EmmUpdate exit",10> mov ax,di ret EmmUpdate endp ;--- cpu is in protected-mode, check EMM status IsEmmInstalled proc c invoke IsJemmInstalled mov dx, CStr( NAMEGEN,' already installed',CR,LF,'$' ) jnc @F call EmmInstallcheck jc exit mov dx, CStr( "CPU in protected mode, loading aborted",CR,LF,07,'$' ) @@: mov ah,9 int 21h exit: ret IsEmmInstalled endp ;--- GetValue() ;--- converts a string into a DWORD ;--- out: NC -> value in EAX ;--- C -> no valid digit found GetValue PROC stdcall uses esi di cmdline:ptr BYTE, base:WORD, usesuffix:WORD xor esi, esi ;result mov bx, cmdline @@nextitem: mov al,BYTE PTR [bx] push ax call toupper mov ah,al sub al,'0' jc @@FB316 cmp al,9 jbe @@I318 sub al,7 cmp al,0Ah jb @@FB316 cmp al,0Fh ja @@FB316 @@I318: movzx ecx, base cmp cl,al jle @@FB316 xchg eax,esi mul ecx xchg eax,esi movzx eax,al add esi,eax inc bx jmp @@nextitem @@FB316: cmp bx,cmdline jz nodigit cmp BYTE PTR usesuffix, 0 je @@I322 mov al,ah cmp al,'M' je @@SC328 ja @@I322 sub al,'G' je @@SC327 sub al,4 je @@SC329 ;'K'? jmp @@I322 @@SC327: shl esi,10 @@SC328: shl esi,10 @@SC329: inc bx @@I322: push esi ;--- remove string from cmdline mov si,bx mov di, cmdline @@: lodsb stosb and al,al jnz @B pop eax ;result in EAX exit: ret nodigit: xor eax,eax stc jmp exit GetValue ENDP ;--- FindCommand(searchstring) parses the command line ;--- for a specific command. If found, the command is removed. ;--- in: SI=cmdline ;--- out: AX ->behind the found string ;--- AX = 0 if the string wasn't found FindCommand PROC stdcall uses di si searchstring:ptr BYTE mov di, searchstring mov bx,di .while byte ptr [di] inc di .endw sub di,bx xchg bx,di ; push ds ; pop es nextcmp: xor ax,ax lodsb and al,al je done cmp al,' ' jbe nextcmp ;skip white spaces dec si invoke _memicmp, si, di, bx ;assumes that BX remains unchanged! or ax,ax je found @@: lodsb cmp al,' ' ;skip the word in cmdline ja @B dec si jmp nextcmp found: ;--- remove found string in cmdline push si mov di,si add si,bx @@: lodsb stosb and al,al jnz @B pop ax done: ret FindCommand ENDP ;--- VMware detection ;--- questionable, because Qemu "responds" similar to VMware VMwareDetect proc c mov eax, 564D5868h ;magic number (="VMXh") mov cx, 000ah ;command number (000A=get VMware version) xor ebx,ebx ;command specific parameter mov dx, 5658h ;VMware IO port (="VX") in eax,dx ;"returns" version number in EAX cmp ebx, 564D5868h ;and magic number in EBX (="VMXh") setz al mov ah,0 ret VMwareDetect endp ;--- Display current status of (conventional) memory map ;--- modifies SI, DI! DisplayMemoryMap proc invoke printf, CStr("Memory Map: R/S=system ram, G=graphics, E=rom/rsvd, P=page frame, U/I=umb", LF) sub sp,4*16+4 mov si,offset SystemMemory .repeat mov cx,si sub cx,offset SystemMemory shr cx,4 mov al,' ' mov di,sp movsd movsd movsd movsd stosb movsd movsd movsd movsd stosb movsd movsd movsd movsd stosb movsd movsd movsd movsd mov al,0 stosb invoke printf, CStr("%X0000: %s", LF), cx, sp .until si >= offset SystemMemory + sizeof SystemMemory add sp,4*16+4 ret DisplayMemoryMap endp cprintf PROC c pszText:ptr byte .if bVerbose invoke printf, pszText .endif ret cprintf ENDP IsProtectedMode proc c SMSW AX AND AX,0001H ; PE-Bit (Protect Enable) set ? ret IsProtectedMode ENDP IsDPMI proc c uses es si di mov ax,1687h int 2Fh and ax,ax setz al cbw ret IsDPMI ENDP if ?INITRMDBG ;-- display XBDA size in kB disp_xbda_size proc uses es bx push 0 pop es mov bx,es:[@XBDA] mov es,bx movzx ax,byte ptr es:[0] @dprintf ?INITRMDBG, <"XBDA size=%X",10>, ax ret disp_xbda_size endp endif if ?INTEGRATED if ?XMS35 hascpuid proc push di mov di,sp and sp,0fffch ;make sure we don't get an exc 11 (if AM set in CR0) pushfd ; save EFlags cli pushd 240000h ; set AC bit in eflags, reset IF popfd ; pop extended flags pushfd ; push extended flags pop ax pop ax ; get HiWord(EFlags) into AX popfd ; restore EFlags mov sp,di pop di test al,04 ;AC bit set? je @F test al,20h ;CPUID bit set? jz @F clc ret @@: stc ret hascpuid endp endif endif ;--- mainex: scan cmdline, then either load/unload or update ;--- an already installed Jemm. ;--- DS=ES=SS=dgroup mainex PROC c public uses di si mode:word, cmdline:ptr BYTE local rangestart:word local rangestop:word local found:word local wChkVMWare:word local memtype:byte local bHelp:byte if ?LOAD local bLoad:byte endif .data even cmdopts0 label word if ?UNLOAD dw CStr("UNLOAD"), offset _unload endif if ?LOAD dw CStr("LOAD"), offset _load endif dw CStr("/?"), offset sethelp dw CStr("-?"), offset sethelp dw CStr("/H"), offset sethelp dw CStr("-H"), offset sethelp endcmdopts0 label word cmdopts1 label word dw CStr("NOVCPI"), offset setnovcpi dw CStr("VCPI"), offset setvcpi if ?INTEGRATED ;--- FindCommand() requires that A20METHOD is *before* A20 option! ;--- that's a bit unfortunate, since A20METHOD doesn't belong to cmdopts1. dw CStr("A20METHOD:"), offset seta20method endif if ?A20PORTS or ?A20XMS dw CStr("NOA20"), offset setnoa20 dw CStr("A20"), offset seta20 endif if ?VME dw CStr("NOVME"), offset setnovme dw CStr("VME"), offset setvme endif if ?PGE dw CStr("NOPGE"), offset setnopge dw CStr("PGE"), offset setpge endif endcmdopts1 label word cmdopts2 label word if ?V86EXC0D dw CStr("V86EXC0D"), offset setv86exc0d endif if ?INTEGRATED dw CStr("HMAMIN="), offset sethmamin dw CStr("MAXEXT="), offset setmaxext if ?XMS35 dw CStr("MAXSEXT="), offset setmaxsext endif dw CStr("NOE801"), offset setnoe801 dw CStr("NOE820"), offset setnoe820 dw CStr("XMSHANDLES="), offset setxmshandles dw CStr("X2MAX="), offset setx2max endif dw CStr("VERBOSE"), offset setverbose dw CStr("/V"), offset setverbose dw CStr("NODYN"), offset setnodyn dw CStr("NOINVLPG"), offset setnoinvlpg dw CStr("MIN="), offset setmin dw CStr("MAX="), offset setmax dw CStr("NOEMS"), offset setnoems dw CStr("NOVDS"), offset setnovds dw CStr("VDS"), offset setvds dw CStr("FRAME=NONE"), offset setnoframe dw CStr("/P"), offset setframe dw CStr("FRAME="), offset setframe dw CStr("X=TEST"), offset setxeqtest dw CStr("I=TEST"), offset setieqtest if ?SB dw CStr("SB"), offset setsb endif if ?EMX dw CStr("EMX"), offset setemx endif if ?SPLIT dw CStr("SPLIT"), offset setsplit endif if ?NOVMWARE dw CStr("NOVMW"), offset setnovmw endif if ?MOVEXBDA dw CStr("MOVEXBDA"), offset setmovexbda endif dw CStr("NOCHECK"), offset setnocheck dw CStr("ALTBOOT"), offset setaltboot dw CStr("NOHI"), offset setnohi ;--- NOMOVEXBDA is a no-op, but helps MS EMM386 switch compatibility dw CStr("NOMOVEXBDA"), offset _ret dw CStr("NORAM"), offset setnoram dw CStr("RAM"), offset setram dw CStr("D="), offset setdma dw CStr("B="), offset setborder endcmdopts2 label word .code @dprintf ?INITRMDBG, <"mainex enter",10> cld mov bHelp, FALSE mov wChkVMWare, offset checkvmware mov si,cmdline .if mode != EXECMODE_EXE mov bLoad, TRUE invoke printf, addr szStartup if ?FASTBOOT invoke FindCommand, CStr("FASTBOOT") .if ax or jemmini.V86Flags, V86F_FASTBOOT .endif endif .else mov bLoad, FALSE mov di, offset cmdopts0 .repeat invoke FindCommand, [di+0] .if ax call word ptr [di+2] .endif add di, 2+2 .until di == offset endcmdopts0 .endif ;--- in case jemm isn't to be loaded, reset the dynamic options to "undefined" .if bLoad == FALSE mov jemmini.dwDynSet, -1 .endif ;--- scan the "dynamic" options ( may be set if jemm is already loaded) mov di, offset cmdopts1 .repeat invoke FindCommand, [di+0] .if ax call word ptr [di+2] .endif add di, 2+2 .until di == offset endcmdopts1 cmp bLoad, FALSE je $I395 ;--- initialize conventional memory map ;--- 0000-9FFF: R(am) ;--- A000-BFFF: G(raphics) ;--- C000-EFFF: U(MB) ;--- F000-FFFF: E(PROM) if ?USEMEMSET invoke memset, addr SystemMemory+000h, 'R', 160 invoke memset, addr SystemMemory+0A0h, 'G', 32 invoke memset, addr SystemMemory+0C0h, 'U', 48 invoke memset, addr SystemMemory+0F0h, 'E', 16 else mov di, offset SystemMemory mov cx,160 ;160*4 = 10*64 = 0000-9FFF mov al,'R' ;RAM rep stosb mov al,'G' ;graphics mov cl,32 ;A000-BFFF rep stosb mov al,'U' ;possibly UMB mov cl,48 ;C000-EFFF rep stosb mov al,'E' ;ROM mov cl,16 ;F000-FFFF rep stosb endif if ?INTEGRATED if ?XMS35 call hascpuid jc @F xor eax,eax inc eax .586 cpuid .386 bt edx,CPUID1_PSE36 jnc @F mov byte ptr xms.maxhigh,0; allow memory up to 000000ff.ffffffffh @@: endif endif mov di, offset cmdopts2 .repeat invoke FindCommand, [di+0] .if ax call word ptr [di+2] .endif add di, 2+2 .until di == offset endcmdopts2 invoke I15CheckRsvdRegion ;--- get the "range" options: I|S|X=bbbb-eeee .while 1 mov memtype,'I' invoke FindCommand, CStr("I=") .if !ax mov memtype,'S' invoke FindCommand, CStr("S=") .if !ax mov memtype,'X' invoke FindCommand, CStr("X=") .break .if !ax .endif .endif mov di,ax invoke GetValue, ax, 16, FALSE ;get start of range jc range_invalid mov rangestart,ax .if BYTE PTR [di] == '-' mov BYTE PTR [di],' ' inc di invoke GetValue, di, 16, FALSE jc range_invalid mov rangestop, ax movzx cx, memtype mov dx, rangestart .if dx < 0A000h || dx >= ax invoke printf, CStr("Rejected %c=%x..%x", LF), cx, dx, ax .else .if bVerbose invoke printf, CStr("Accepted %c=%x..%x", LF), cx, dx, ax .endif mov di, rangestart .while di < rangestop invoke SetMemoryType, di, memtype inc di .endw .endif .else range_invalid: invoke printf, CStr("%s: option '%c=' rejected, invalid syntax", LF), offset szWarning, memtype .endif .endw $I395: invoke skipWhite, si mov si,ax cmp BYTE PTR [si],'0' jb @F cmp BYTE PTR [si],'9' ja @F invoke GetValue, ax, 10, TRUE shr eax,4 mov jemmini.MaxMem16k,eax invoke skipWhite, si mov si,ax @@: .if BYTE PTR [si] invoke printf, CStr("%s: ignored commandline '%s'", LF), offset szWarning, si cmp mode,EXECMODE_EXE je exit1 .endif .if bLoad == FALSE invoke printf, addr szStartup .if bHelp invoke printf, addr szCopyRight invoke printf, addr szHelp .else .if jemmini.dwDynSet != -1 invoke EmmUpdate .if ax invoke printf, CStr("option(s) passed to installed instance of %j", LF) .endif .else invoke EmmStatus .endif .endif jmp exit1 .endif ; /******* options set, now process **********/ invoke IsProtectedMode .if ax invoke IsEmmInstalled jmp exit1 .endif invoke XMSinit if ?INTEGRATED eq 0 .if !ax invoke printf, CStr("%s: no XMM found, required", LF), offset szError jmp exit1 .endif else .if ax invoke printf, CStr("%s: XMM already installed", LF), offset szError jmp exit1 .endif endif invoke IsDPMI .if ax invoke printf, CStr("%s: DPMI host detected", LF), offset szError jmp exit1 .endif if ?INTEGRATED invoke InitA20 .if CARRY? invoke printf, CStr("%s: No supported A20 method detected", LF), offset szError jmp exit1 .endif mov jemmini.A20Method,al .if bVerbose movzx bx, al add bx, bx invoke printf, CStr("'%s' A20 method selected", LF), A20strings[bx] .endif movzx bx,jemmini.A20Method add bx,bx mov ah,ENABLE_A20 call A20procs[bx] else invoke xmscall, XMS_GETVERSION .if ax && xmsreg._ax >= 0300h mov xmsspec3, TRUE .endif invoke xmscall, XMS_ENABLEA20 .if !ax invoke printf, CStr("%s: enable A20 failed", LF), offset szError jmp exit1 .endif endif .if jemmini.MaxMem16k == -1 mov jemmini.MaxMem16k, MAXMEM16K_DEFAULT .endif .if jemmini.NoVCPI invoke cprintf, CStr("VCPI disabled", LF) .endif .if jemmini.NoVDS invoke cprintf, CStr("VDS disabled", LF) .endif if ?INTEGRATED eq 0 ; /* if no int 2fh, function 4309h support, disable pool sharing */ .if jemmini.XMSHandleTable == 0 && jemmini.NoPool == FALSE mov jemmini.NoPool, TRUE invoke printf, CStr("%s: XMS host doesn't provide handle array, dynamic memory allocation off!", LF), offset szWarning .endif endif invoke ScanSystemMemory ;/* build up system memory map */ .if jemmini.NoEMS mov jemmini.NoFrame, TRUE .endif .if jemmini.NoFrame == FALSE invoke LocatePageFrame mov jemmini.Frame, ax .endif ; allocate from XMS the memory we need ; this is memory for UMBs, including FF00 ; ; + 20kB for the monitor code, GDT, IDT, stack ; + 12kB for page tables ; + 12kB for TSS + IO-Bitmap (includes 3 kB reserve for rounding) ; + 4kB for mapping FF000 page ; + 64kB +-X for DMA buffering ; + room for other control structures made inside function ; ; + what the user wants for EMS MONITORMIN equ 20+12+12+4 invoke UMBpageswanted shl ax,2 mov si,ax .if bVerbose invoke printf, CStr("Needed: %u kB for monitor, %u kB for UMBs, %u kB for DMA buffer", LF), MONITORMIN, ax, jemmini.DMABufferSize .endif .if jemmini.NoEMS mov ax, 512 .else mov ax, EMS_MAX_PAGES_ALLOWED .endif mov jemmini.MaxEMSPages,ax add si,jemmini.DMABufferSize add si, MONITORMIN movzx esi,si invoke AllocAndInitMem, esi .if ax == 0 if ?INTEGRATED movzx bx,jemmini.A20Method add bx,bx mov ah,DISABLE_A20 call A20procs[bx] else invoke xmscall, XMS_DISABLEA20 ; local disable A20 endif jmp exit1 .endif mov eax, dwAllocatedBytes add eax, jemmini.MonitorStart mov jemmini.MonitorEnd,eax .if bVerbose invoke printf, CStr("XMS memory block for monitor: %lx-%lx, XMS highest=%lx", LF), jemmini.MonitorStart, eax, jemmini.MaxPhysMem invoke DisplayMemoryMap .endif mov ax, ds movzx eax, ax shl eax, 4 lea eax, [eax+offset SystemMemory] mov jemmini.PageMap, eax if ?INITRMDBG call disp_xbda_size if 0 ;this enabled a debug watchpoint (write) for first byte of XBDA push 0 pop es movzx eax,word ptr es:[@XBDA] shl eax,4 mov dr0,eax ;set a watchpoint mov eax,00010002h ;set Write global watchpoint mov dr7,eax endif endif mov si, mode invoke InitJemm .if bVerbose invoke printf, CStr("Physical start address of EMS pages: %lX", LF), eax mov ah,42h int 67h .if ah == 0 movzx eax,bx movzx ecx,dx shl eax,4 shl ecx,4 invoke printf, CStr("Total/available EMS memory: %d/%d pages (= %lu/%lu kB)", LF), dx, bx, ecx, eax .endif .endif invoke printf, CStr( "%j loaded", LF) if ?INTEGRATED eq 0 invoke xmscall, XMS_DISABLEA20 ;local disable A20 endif xor ax,ax $EX338: @dprintf ?INITRMDBG, <"mainex: exit, ax=%X [bp]=%X %X",10>, ax, [bp+0], [bp+2] ret exit1: mov ax,1 jmp $EX338 if ?LOAD _load: mov bLoad, TRUE retn endif if ?UNLOAD _unload: pop ax invoke XMSinit invoke TryUnload jmp exit1 endif sethelp: mov bHelp, TRUE retn setnovcpi: mov jemmini.NoVCPI,TRUE retn setvcpi: mov jemmini.NoVCPI,FALSE retn if ?INTEGRATED seta20method: invoke GetA20Method, ax jc @F mov jemmini.A20Method, al retn @@: invoke printf, CStr("%s: unknown A20 method", LF), offset szWarning retn endif if ?A20PORTS or ?A20XMS setnoa20: mov jemmini.NoA20,TRUE retn seta20: mov jemmini.NoA20,FALSE retn endif if ?VME setnovme: mov jemmini.NoVME,TRUE retn setvme: mov jemmini.NoVME,FALSE retn endif if ?PGE setnopge: mov jemmini.NoPGE,TRUE retn setpge: mov jemmini.NoPGE,FALSE retn endif if ?V86EXC0D setv86exc0d: or jemmini.V86Flags,V86F_V86EXC0D retn endif if ?INTEGRATED sethmamin: invoke GetValue, ax, 10, FALSE .if ax > 63 mov ax,63 .endif mov jemmini.HmaMin, ax retn setmaxext: invoke GetValue, ax, 10, TRUE mov xms.max, eax retn if ?XMS35 setmaxsext: invoke GetValue, ax, 10, TRUE mov xms.smax, eax retn endif setnoe801: mov bNoE801, TRUE retn setnoe820: mov bNoE820, TRUE retn setxmshandles: invoke GetValue, ax, 10, FALSE .if ax < ?XMS_STATICHDLS mov ax, ?XMS_STATICHDLS .elseif ( ax > ?XMS_MAXHDLS ) mov ax, ?XMS_MAXHDLS .endif mov xms.num_handles,ax retn setx2max: invoke GetValue, ax, 10, TRUE mov jemmini.X2Max, ax retn endif setnoinvlpg: mov jemmini.NoInvlPg, TRUE retn setnodyn: mov jemmini.NoPool, TRUE retn setverbose: mov bVerbose, TRUE retn setmin: invoke GetValue, ax, 10, TRUE shr eax, 4 mov jemmini.MinMem16k,eax .if bVerbose shl eax, 4 invoke printf, CStr("Wanted preallocated EMS/VCPI memory: %lu kB", LF), eax .endif mov MinRequest, TRUE retn setnoems: invoke cprintf, CStr("NOEMS: EMS disabled (mostly :-)", LF) mov jemmini.NoEMS, TRUE _ret: retn setmax: invoke GetValue, ax, 10, TRUE shr eax,4 mov jemmini.MaxMem16k+0,eax retn setvds: mov jemmini.NoVDS, FALSE retn setnovds: mov jemmini.NoVDS, TRUE retn setnoframe: mov jemmini.NoFrame, TRUE retn setframe: invoke GetValue, ax, 16, FALSE .if al invoke printf, CStr("Rejected page frame=%x", LF), ax .else mov bFrameWanted, ah .if bVerbose invoke printf, CStr("Wanted page frame=%X", LF), ax .endif .endif retn setxeqtest: mov ExcludeTest, TRUE retn setieqtest: mov IncludeTest, TRUE retn if ?SB setsb: or jemmini.V86Flags, V86F_SB retn endif if ?EMX setemx: or jemmini.V86Flags, V86F_EMX retn endif if ?SPLIT setsplit: mov SplitTest, TRUE retn endif if ?NOVMWARE setnovmw: mov wChkVMWare, offset _ret retn endif if ?MOVEXBDA setmovexbda: or jemmini.V86Flags, V86F_MOVEXBDA retn endif setnocheck: or jemmini.V86Flags, V86F_NOCHECK retn setaltboot: or jemmini.V86Flags, V86F_ALTBOOT retn setnohi: mov jemmini.NoHigh, TRUE retn setnoram: mov jemmini.NoRAM, TRUE retn setram: mov jemmini.NoRAM, FALSE retn setdma: invoke GetValue,ax, 10, FALSE mov bx,ax .if ax <= 128 lea ax,[bx+3] and al,0FCh mov jemmini.DMABufferSize,ax .else mov jemmini.DMABufferSize,128 invoke printf, CStr("%s: wanted DMA buffer size too large, set to 128 kB", LF), addr szWarning .endif retn setborder: invoke GetValue, ax, 16, FALSE .if ax < 1000h invoke printf, CStr("%s: EMS banking start too low, set to 0x1000", LF), addr szWarning mov ax, 1000h .endif mov jemmini.Border,ax retn checkvmware: invoke VMwareDetect .if ax mov dword ptr SystemMemory+0E8h, 'VVVV' mov dword ptr SystemMemory+0ECh, 'XXXX' invoke cprintf, CStr("VMware detected", LF) .endif retn mainex ENDP END ================================================ FILE: src/JEMM.INC ================================================ ;--- common definitions for both Jemm32 and Jemm16 option proc:private option casemap:none LF EQU 0AH CR EQU 0DH ifndef ?INTEGRATED ?INTEGRATED EQU 0 ; 1=integrated version (XMS + EMM) endif ifndef ?KD ?KD EQU 0 ; 1=support for PL0 debugger endif ?DMAPT EQU 1 ; 1=enable DMA port trapping support ?VDS EQU 1 ; 1=enable VDS support ?VCPI EQU 1 ; 1=enable VCPI support ?EMX EQU 1 ; 1=EMX compat switch supported ?SB EQU 1 ; 1=SB compat switch supported (useful?) ?EMMXXXX0 EQU 1 ; 1=implement EMMXXXX0 IOCTL ?A20XMS EQU 1 ; 1=emu A20 by trapping XMS functions ?A20PORTS EQU 1 ; 1=emu A20 by trapping ports (92, 64, 60) ?VME EQU 1 ; 1=support P1+ VME extension ?LOAD EQU 1 ; 1=support LOAD command line option ?PGE EQU 1 ; 1=support PGE on P3+ (requires ?INVLPG!) ?MOVEHIGH EQU 1 ; 1=support moving in first UMB ?UNLOAD EQU 1 ; 1=support UNLOAD option ?FASTBOOT EQU 1 ; 1=support FASTBOOT option ?SPLIT EQU 1 ; 1=support SPLIT option ?HOOK13 EQU 1 ; 1=hook int 13h/40h for DMA access trapping ?V86EXC0D EQU 1 ; 1=support V86EXC0D option ?ADDCONV EQU 1 ; 1=add A000h to conv memory if I=A000-XXXX if ?INTEGRATED ?MOVEXBDA EQU 1 ; 1=support MOVEXBDA option; move XBDA into UMB else ?MOVEXBDA EQU 1 endif ?MOVEXBDAPM equ 1 ; 1=move XBDA in protected-mode ;?GDTOFS equ ?HLPSTKSIZE if ?KD GDT_SEL equ 48h ; GDT selector, used by kernel debugger endif ;--- define v86-monitor breakpoint ( used to switch back to protected-mode ) ;--- the advantage of ARPL is that it's causing an "invalid opcode" exception, ;--- so it won't have to be dealt with in the rather "longish" GPF handler chain. ?BPOPC equ 0F4h ; default is HLT ;?BPOPC equ 063h ; alternately ARPL ?XMS35COMPAT equ 1 ; 1=monitor is XMS v3.5 compatible (is aware of memory blocks beyond 4GB) V86IOPL equ 3 ; IOPL for v86-mode ;--- BIOS constants SMAP equ 534d4150h E820MAP struct baselow dd ? basehigh dd ? lenlow dd ? lenhigh dd ? type_ dd ? E820MAP ends @XBDA equ 40Eh ; segment address XBDA @MEM_SIZE equ 413h ; base memory size in KB if ?INTEGRATED ifndef ?XMS35 ?XMS35 equ 1 ;std=1, 1=support super-extended memory beyond 4GB endif ;--- A20 switch methods (must match order in "methods" table) A20_KBC equ 0 A20_PS2 equ 1 A20_BIOS equ 2 A20_ALWAYSON equ 3 A20_FAST equ 4 A20_PORT92 equ 5 if ?XMS35 DRIVER_VER equ 350h+2 INTERFACE_VER equ 351h INTERFACE_VER_MAXSEXT0 equ 300h ;xms version if MAXSEXT=0 is set else DRIVER_VER equ 300h+29 INTERFACE_VER equ 300h endif endif DMABUFFDEFAULT equ 64 ;/* DMA buffer default size in kB */ MINMEM16K_DEFAULT equ 0 ;/* MIN= default (in 16 kB units) */ ;--- data for monitor initialization JEMMINIT struct 4 MonitorStart dd 0 ;memory block start address (XMS/I15) MonitorEnd dd 0 ;memory block end address (XMS/I15) MaxPhysMem dd 0 ;highest physical memory address (XMS/I15) MaxMem16k dd -1 ;MAX mem in 16 kB units (default 7680) MinMem16k dd MINMEM16K_DEFAULT ;MIN mem in 16 kB units (default 0) XMSHandleTable dd 0 ;XMS handle table (FAR16) PageMap dd 0 ;conventional memory page map (FLAT!) MaxEMSPages dw 0 ;EMS max 16 kB pages (default 2048) XMSControlHandle dw 0 ;XMS memory block handle (both Jemm386 & JemmEx) DMABufferSize dw DMABUFFDEFAULT ;DMA buffer size in kB Frame dw 0E000h ;EMS page frame Border dw 04000h ;EMS border for mappable pages ResUMB dw 0 ;UMB para where the resident part was moved to if ?INTEGRATED HmaMin dw 0 ;min request in KB for HMA X2Max dw -1 if ?XMS35 xms_version dw INTERFACE_VER endif endif if ?KD kdofs dd 0 kdseg dw 0 endif NoEMS db 0 NoFrame db 0 NoPool db 0 ;AltBoot db 0 ;v5.80: has become a bit in V86Flags NoVDS db 0 union struct NoVCPI db 0 NoA20 db 0 NoVME db 1 NoPGE db 1 ends dwDynSet dd ? ends NoInvlPg db -1 V86Flags db 0 NoRAM db 0 NoHigh db 0 NumUMBs db 0 ;number of UMBs installed if ?INTEGRATED A20Method db -1 endif JEMMINIT ends ;--- V86Flags equates V86F_SB equ 1 ; soundblaster driver compat V86F_NOCHECK equ 2 ; flag NOCHECK option if ?EMX V86F_EMX equ 4 ; EMX compat endif if ?FASTBOOT V86F_FASTBOOT equ 8 ; fastboot active V86F_FASTBOOT_B equ 3 ; bit 3 endif if ?V86EXC0D V86F_V86EXC0D equ 16 ; V86EXC0D active endif if ?MOVEXBDA V86F_MOVEXBDA equ 32 ; MOVEXBDA active endif V86F_ALTBOOT equ 64 ; ALTBOOT active ;-- 120 MB max VCPI memory (in 16 kB units) ;-- keep this value low for buggy VCPI clients ;-- that fail with large free amounts MAXMEM16K_DEFAULT EQU 1E00h ; 7680 * 16 = 122880 kB or 120 MB MAX_EMS_PAGES_POSSIBLE equ 8000h ;--- this is the table of RSEG offsets ;--- the values are offsets in the RSEG segment ;--- there is just one instance defined in jemm16.asm. RSOFS struct wSizeRes dw ? ;size resident part wBpTab dw ? ;offset of BP table if ?DMAPT wRFlags dw ? ;offset of DMA flags endif RSOFS ends ;--- bRFlags values RFL_COPYBUFFER equ 1 ; 1=copy out of DMA buffer RFL_DMAOP equ 2 ; 1=DMA op started ;--- max number of UMB blocks ;--- this differs significantly from how UMBs are handled by ;--- MS Himem. With MS Himem, each UMB has a 16-byte header ;--- (quite similarly to MCBs), and there's no UMB block limit. UMB_MAX_BLOCKS equ 8 UMBBLK struct wSegm dw ? ;segment address wSize dw ? ;size in paras, high bit used as flag free/allocated UMBBLK ends UMB_ALLOCATED equ 80h ;flag in wSize+1 if ?EMMXXXX0 ;--- ioctl read functions EMMDEV_GETAPI equ 0 EMMDEV_GEMMIS equ 1 EMMDEV_VERSION equ 2 EMMDEV_GETRES equ 4 ;get EMM resident segment/size? EMMDEV_SYSVARS equ 6 ;get state of jemm32 variables EMMDEV_GETUMBS equ 7 ;get state of UMBs EMMDEV_GETSTAB equ 8 ;get VMM info ;--- ioctl write functions EMMDEV_UPDATE equ 15 ;--- structure returned by Jemm386 if a SYSVARS request was made ;--- for device "EMMXXXX0" EMX06 struct e06_NoEMS db ? ;+0 e06_Frame dw ? ;+1 segment e06_NoVCPI db ? ;+3 e06_DMABuff dd ? ;+4 physical address DMA buffer e06_NoPGE db ? ;+8 db ? db ? db ? e06_DMASize dw ? ;+12 in KB e06_NoVME db ? ;+14 e06_NoA20 db ? ;+15 e06_VCPITotal dd ? ;+16 VCPI pages total (def 120 MB) e06_VCPIUsed dd ? ;+20 VCPI pages allocated EMX06 ends ;--- the structure for an "UPDATE" request with IoctlWrite EMX15W struct e15_bVME db ? e15_bA20 db ? e15_bVCPI db ? e15_bPGE db ? EMX15W ends endif ;--- macros @BPOPC macro db ?BPOPC endm ================================================ FILE: src/JEMM16.ASM ================================================ ;--- 16 bit part of Jemm; ;--- to be assembled with JWasm or Masm v6.1+ ;--- _RTEXT must be first segment; it has to be defined *before* .model. _RTEXT segment para public 'CODE' _RTEXT ends DGROUP group _RTEXT .286 ifdef __JWASM__ .model tiny else .model small ;Masm bug: .model tiny rejects using SEG operator DGROUP group _TEXT endif include jemm.inc include jemm16.inc include debug16.inc include xms.inc if ?KD include debugsys.inc endif GDT_SIZE equ 200h ; is defined in jemm32.inc ;--- interrupt hook list entry INTMOD struct bInt db ? wNew dw ? wOld dw ? INTMOD ends MCB struct sig db ? psp dw ? blsiz dw ? MCB ends IRETWS struct _Ip dw ? _Cs dw ? _Fl dw ? IRETWS ends ;--- @DefineBP: macro to define a v86-"breakpoint" @DefineBP macro _name _name&:: @BPOPC endm CStr macro text:VARARG local sym .const sym db text,0 .code exitm endm .data wLow dw RSEG_END ;resident low memory required by driver ;--- _brptab is the second of two parameters for Jemm32 init ;--- the entries are offsets in RSEG ;--- the most important one is the offset to the v86 breakpoint table if ?HOOK13 _brptab RSOFS < RSEG_END, bptable, bRFlags > else _brptab RSOFS < RSEG_END, bptable > endif if 0;?A20PORTS wBiosA20 DW 1+2 ;default: trap both kbdc + ps2 ports endif .const intvecs label INTMOD INTMOD <15h,offset NEW15, offset OldInt15> if ?INTEGRATED INTMOD <2Fh,offset NEW2F, offset OldInt2F> endif if ?HOOK13 INTMOD <13h,offset NEW13, offset OldInt13> INTMOD <40h,offset NEW40, offset OldInt40> endif INTMOD <67h,offset BP67, -1> INTMOD <06h,offset BP06, -1> INTMOD <19h,offset BP19, -1> db -1 .data? ;ensure _BSS is defined STACK segment para STACK 'STACK' org 1024 ; application stack stacktop label byte STACK ends DGROUP group STACK ;here stack has to be added to dgroup manually .386 _RTEXT SEGMENT ASSUME DS:NOTHING,ES:NOTHING,FS:ERROR,GS:ERROR ;************************************************************************* ; device driver header device_header: dd -1 ; last driver in list dw 0c000h ; driver flags : ; 8000 - character device ; 4000 - supports IOCTL - like EMM386 pStratOfs dw offset strategy ; offset to strategy routine pIntOfs dw offset driver_entry ; offset to interrupt handler device_name label byte db 'EMMXXXX0' ; device driver name ;--- start of real-mode resident data area ;--- v86 breakpoint table ;--- the order must match the one of bptable in Jemm32.asm ! bptable label byte @DefineBP BP06 ; int 06h: entry invalid opcode exception @DefineBP BP19 ; int 19h @DefineBP BP67 ; int 67h: entry real-mode if ?VDS @DefineBP BPVDS ; int 4Bh: entry v86-mode (VDS) endif @DefineBP BPBACK ; BP to return to v86 monitor from a v86 far proc @DefineBP BPI1587 if ?HOOK13 @DefineBP BP1340 ; int 13h/40h: copy out of DMA buffer endif @DefineBP BPXMS ; handle XMS A20+UMB @DefineBP BPSTRAT ; EMMXXXX0 device strategy call @DefineBP BPINTR ; EMMXXXX0 device interrupt call if ?UNLOAD @DefineBP BPUNL ; unload endif if ?HOOK13 bRFlags DB 0 ; bit 0: 1=copy out of DMA buffer ; bit 1: 1=new DMA op started ;--- for DMA, hook int 40h (FD access) ;--- and int 13h (HD access) NEW40 PROC FAR mov cs:[bRFlags],RFL_DMAOP ; tell the monitor that a new DMA op has started pushf db 09Ah OldInt40 dd 0 jmp int1340common NEW40 ENDP align 4 NEW13 PROC FAR mov cs:[bRFlags],RFL_DMAOP ; tell the monitor that a new DMA op has started pushf db 09Ah OldInt13 dd 0 NEW13 ENDP ;fall thru int1340common: jc iret_with_new_CY test cs:[bRFlags],RFL_COPYBUFFER jnz BP1340 ; breakpoint int 13/40h: copy out of DMA buffer endif if ?HOOK13 or ?INTEGRATED iret_with_new_CY: push bp mov bp,sp rcr byte ptr [bp+2].IRETWS._Fl,1 rol byte ptr [bp+2].IRETWS._Fl,1 pop bp iret endif ;****************************************************************************** ; INT15 handler: ; handle AH=87h case (copy extended memory) ; ; AH = 87h ; CX = number of words to copy (max 8000h) ; ES:SI -> GDT (4 descriptors) ;Return: CF set on error, else cleared ; AH = status (00 == ok) ; ; For JemmEx, also handle ah=88h (get extended memory size) ; If XMS v3.5 is supported, ax=E820h must also be intercepted. ;****************************************************************************** align 4 NEW15 PROC FAR ;--- int 15h handlers "should" preserve CF ;--- it's not done here, because CF will be set if ah=4Fh (keyboard) CMP AH,87H ; is it the blockmove? JZ BPI1587 if ?INTEGRATED CMP AH,88H ; ext memory size JZ i1588 if ?XMS35 CMP AX,0E820h ; memmap JZ memmap ifdef __JWASM__ xms_smax_noe820 equ $ - 1 else org $-1 xms_smax_noe820 db ? endif endif endif db 0EAh OldInt15 dd 0 if ?INTEGRATED i1588: xor ax,ax ; no memory available jmp iret_with_new_CY if ?XMS35 memmap: pushf call cs:[OldInt15] jc iret_with_new_CY cmp eax,SMAP clc jnz iret_with_new_CY cmp es:[di].E820MAP.basehigh,0 jz iret_with_new_CY cmp es:[di].E820MAP.type_,1 ;"available" memory? clc jnz iret_with_new_CY mov byte ptr es:[di].E820MAP.type_,2 ;change to "reserved" jmp iret_with_new_CY endif endif NEW15 ENDP ;********************************************************* ; XMS hook - required for UMBs and A20 emulation ;********************************************************* align 4 XMShandler proc jmp short @@XMSHandler ; standard XMS link chain nop ; with 3 required NOPs nop nop @@XMSHandler: if ?INTEGRATED jmp BPXMS else ;-- for A20 disable and enable emulation it is required to hook ;-- the XMS functions as well, even if the A20 ports (92, 60, 64) ;-- are trapped. That's because if certain conditions are true ;-- the XMS host will never access the ports and leave A20 unchanged. if ?A20XMS cmp ah,3 jb @@noa20 cmp ah,6 jbe BPXMS @@noa20: endif XMSUMB:: cmp ah,10h ; 10h=alloc, 11h=free, 12=realloc jb @@xms_prev cmp ah,12h jbe BPXMS @@xms_prev: db 0eah ; jmp far XMS prev handler XMSoldhandler dd 0 endif XMShandler endp if ?INTEGRATED NEW2F proc pushf cmp ah,43h jz is43 @@jmp_old2f: popf db 0EAh OldInt2F dd 0 is43: cmp al,00h ; is it "Installation Check"? je @@get_driver_installed cmp al,10h ; is it "Get Driver Address"? je @@get_xms_address cmp al,09h ; is it "get handle table"? je @@get_xms_handle_table cmp al,08h jne @@jmp_old2f mov al,ah ;al==43h if function supported machine_type label byte mov bx,0002 ; bh=switch time; 0=medium, 1=fast, 2=slow ; bl=machine type; 1=std AT (KBC), 2=PS/2 (port 92) ? popf iret @@get_driver_installed: mov al,80h ; yes, we are installed ;) popf iret @@get_xms_address: mov bx,offset XMShandler @@shared2f: push cs pop es popf iret @@get_xms_handle_table: mov al,ah ;al==43h if function supported mov bx,offset xms_handle_table jmp @@shared2f NEW2F endp endif if ?DBGOUT include dprntf16.inc endif if ?INTEGRATED align 2 ?EXTRAHDL equ 0 xms_handle_table XMS_HANDLETABLE <01, size XMS_HANDLE, 0, xms_handle_array> xms_handle_array XMS_HANDLE ?XMS_STATICHDLS+1+?EXTRAHDL dup () db XMSF_INPOOL endif align 16 RSEG_END equ $ - device_header _RTEXT ends ; ; installation part of the virtual Monitor ; .code assume ds:DGROUP code32: ifdef __JWASM__ ;JWasm allows to include binary data directly incbin else .nolist include _jemm32.inc .list endif if ?UNLOAD CheckIntHooks proc stdcall public uses es si di wResSeg:WORD mov ax, wResSeg shl eax, 16 push 0 pop es mov si, offset intvecs @@nextvect: lodsb cmp al,-1 jz @@ok movzx di,al shl di,2 lodsw scasd stc jnz @@nouninst add si,2 jmp @@nextvect @@ok: if ?VDS test byte ptr es:[47Bh],20h jz @F mov ax,offset BPVDS cmp eax,es:[4Bh*4] stc jnz @@nouninst @dprintf ?UNLRMDBG, <"CheckIntHooks: int 4Bh is ok",10> @@: endif clc @@nouninst: ret CheckIntHooks endp ;--- Jemm can be unloaded. Do it! ;--- in: AX=segment of resident part of installed Jemm ;--- out: C=error, NC=ok. ;--- may change all GPRs except BP/SP. UnloadJemm proc c public mov di,ax @dprintf ?UNLRMDBG, <"UnloadJemm enter, res. segm=%X CS=%X SS:SP=%X:%X",10>, di, cs, ss, sp ;--- remove EMMXXXX0 from driver chain mov ah,52h ;get start of driver chain int 21h add bx,22h @@nextdev: cmp di, es:[bx+2] jz @@found les bx, es:[bx] cmp bx,-1 jnz @@nextdev stc ret @@found: mov ds,di mov ecx, ds:[0] ;remove driver from chain mov es:[bx], ecx @dprintf ?UNLRMDBG, <"UnloadJemm: driver removed from chain",10> ;--- reset IVT vectors mov es,di push 0 pop ds mov si, offset intvecs @@nextvec: lodsb cs:[si] cmp al,-1 jz @@ok movzx bx,al shl bx, 2 mov di,cs:[si+2] cmp di,-1 jz @@skipvec mov ecx, es:[di] mov [bx],ecx @@skipvec: add si,4 jmp @@nextvec @@ok: @dprintf ?UNLRMDBG, <"UnloadJemm: IVT vectors restored",10> ;--- reset XMS hook ife ?INTEGRATED mov bx,offset XMSoldhandler mov ecx, es:[bx] and ecx, ecx jz @@noxms push es push es pop ds mov si,offset XMShandler push ecx pop di pop es mov cx,5 sub di,cx rep movsb pop es @@noxms: @dprintf ?UNLRMDBG, <"UnloadJemm: XMS hook removed",10> endif if ?KD ;--- call int 68h if kernel debugger installed. ;--- if the installed instance didn't initialize the kd, nothing is to be done here! mov ax,es sub ax,10h mov ds,ax test byte ptr ds:[7fh], 1 jz @F mov ah, D386_Real_Mode_Init int D386_RM_Int @@: endif ;--- make sure jemm386's resident memory block is freed ;--- this is to be improved yet mov ax,es sub ax,10h+1 ;size PSP + 10h (to get MCB) mov ds,ax mov cx,ax inc cx mov al,ds:[0] cmp al,'M' jz @@ismcb cmp al,'Z' jnz @@nopsp @@ismcb: cmp cx,ds:[1] jnz @@nopsp ife ?INTEGRATED cmp word ptr ds:[3],10h + RSEG_END/10h jnz @@nopsp else cmp word ptr ds:[10h+18h],-1 ;check if files 0+1 are closed jnz @@nopsp endif cmp word ptr ds:[10h],20CDh jnz @@nopsp mov ax,cs sub ax,10h mov ds:[1],ax @@nopsp: @dprintf ?UNLRMDBG, <"UnloadJemm: resident segment prepared to be released",10> ;--- now call the installed instance of Jemm386 to ;--- do the rest of the clean-up push bp mov bp,sp pushd 0 push 3FFh push es push offset BPUNL call dword ptr [bp-(4+6)] ;--- Jemm386 has exited, but we are still in protected mode! ;--- GDTR no longer valid; ;--- for Jemm386, BX contains XMS handle for Jemm386's memory block ;--- for JemmEx, BL contains A20 method index mov eax, cr0 and eax, 7FFFFFFEh ;disable paging and protected-mode mov cr0, eax ;--- v5.86: for 80486 compatibility, use a JMP far16 instead of RETF db 0EAh ;jmp FAR16 opcode dw offset @F, seg @F ; Masm: SEG operator rejected if .model TINY is used @@: .386p LIDT FWORD ptr [bp-6]; reset IDT to 0:3ffh (v5.86: now done AFTER mode switch) if 1 xor eax,eax ;v5.86: clear TLB mov cr3,eax endif .386 mov sp,bp pop bp mov ax,cs ;v5.86: assume model TINY mov ss,ax mov ds,ax mov es,ax sti @dprintf ?UNLRMDBG, <"UnloadJemm: back from protected-mode",10> @dprintf ?UNLRMDBG, <"UnloadJemm exit",10> mov ax,bx clc ret UnloadJemm endp endif ; check, if this program runs after all on a 386-Computer (o.ae.) ; (... you never know) Is386 PROC NEAR PUSHF mov AX,7000h PUSH AX POPF ; on a 80386 in real-mode, bits 15..12 PUSHF ; should be 7, on a 8086 they are F, POP AX ; on a 80286 they are 0 POPF and ah,0F0h cmp AH,70H stc JNZ @F clc @@: RET Is386 ENDP ;--- test if CPU is 386, display error if not ;--- returns C and modifies DS in case of error ;--- other registers preserved TestCPU proc near push ax call Is386 jnc @F push dx push cs pop ds mov ah,9 mov dx,offset dErrCpu int 21h pop dx stc @@: pop ax ret TestCPU endp dErrCpu db NAMEMOD,": at least a 80386 cpu is required",13,10,'$' request_ptr dd 0 ;--- the original strategy proc, must be 8086 compatible ;--- will be replaced by a v86 BP once Jemm386 is installed strategy: mov word ptr cs:[request_ptr+0],bx mov word ptr cs:[request_ptr+2],es retf ;********************************************** ; driver init part ; this code is only necessary on init and ; will not go resident. It must however be ; in the same physical segment as the ; resident part. The proc must be declared far. ;********************************************** req_hdr struct req_size db ? ;+0 number of bytes stored unit_id db ? ;+1 unit ID code cmd db ? ;+2 command code status dw ? ;+3 status word rsvd db 8 dup(?);+5 reserved req_hdr ends init_req struct req_hdr <> units db ? ;+13 number of supported units endaddr dd ? ;+14 end address of resident part cmdline dd ? ;+18 address of command line init_req ends driver_entry proc far push ds push di lds di, cs:[request_ptr] ; load address of request header mov [di].req_hdr.status,8103h cmp [di].req_hdr.cmd,0 ; init cmd? jne @@noinit call TestCPU jnc @@cpuok @@noinit: pop di pop ds ret @@cpuok: mov [di].req_hdr.status,0100h ; set STATUS_OK pushad mov cx,ss mov bx,sp mov ax, cs mov ss, ax mov sp, offset stacktop push cx push bx push es push ds push di les si, [di].init_req.cmdline mov ds, ax call EmmInstallcheck jnc @F xor ax,ax jmp @@driver_exit @@: add sp,-128 mov di,sp push ds push es pop ds push ss pop es cld if 0 push si nextchar: lodsb cmp al,0 jz donex cmp al,13 jz donex cmp al,10 jz donex mov dl,al mov ah,2 int 21h jmp nextchar donex: mov dl,13 mov ah,2 int 21h mov dl,10 mov ah,2 int 21h pop si endif @@nxtchar1: ;skip program name lodsb and al,al jz done cmp al,13 jz done cmp al,10 jz done cmp al,20h ja @@nxtchar1 @@nxtchar2: lodsb and al,al jz done cmp al,13 jz done cmp al,10 jz done stosb jmp @@nxtchar2 done: mov al,0 stosb pop ds push sp push EXECMODE_SYS call mainex add sp,128+2+2 ; MOV DX,offset dFailed or ax,ax ; error occured? mov ax,0 jnz @@driver_exit mov ax, [wLow] @@driver_exit: pop di pop ds pop es mov word ptr [di].init_req.endaddr+0,ax ; if ax == 0, driver won't be installed mov word ptr [di].init_req.endaddr+2,cs ; set end address pop bx pop ss mov sp,bx popad pop di pop ds ret driver_entry ENDP if ?LOAD ;--- check if there is already an EMM installed ;--- DS=DGROUP ;--- out: NC=no Emm found EmmInstallcheck proc c public push es pusha MOV AX,3567H ; get INT 67h INT 21H MOV AX,ES ; EMM already installed ? OR AX,BX JZ @@ok MOV DI, 10 MOV SI, offset sig1 CLD MOV CX, 8 REPZ CMPSB je @@error ; matched 1st ID string mov di, 10 ; didn't match, check 2nd ID (NOEMS version) mov si, offset sig2 mov cl, 8 repz cmpsb clc jne @@ok ; did match 2nd ID string? @@error: mov dx, CStr('An EMM is already installed',CR,LF,07,'$') mov ah, 9 int 21h stc @@ok: popa pop es ret EmmInstallcheck endp endif ;********************************************* ; startpoint when executing as EXE ;********************************************* start proc mov ax,cs mov ss,ax mov sp, offset stacktop - 128 call TestCPU jc @@exit @dprintf ?INITRMDBG, mov di,sp mov si,0080h lodsb movzx cx,al push ss pop es rep movsb mov al,0 stosb push ss pop ds push sp push EXECMODE_EXE call mainex ;returns 0 if everything ok add sp,128+2+2 if ?LOAD and ax,ax ;error occured? jnz @@exit cmp [wLow],0 ;did we move high? jz @@exit call LinkChain ;link driver in chain for .EXE mov ah,51h int 21h mov es,bx mov es,es:[002Ch] mov ah,49h int 21h mov bx,4 @@nextfile: mov ah,3Eh int 21h dec bx jns @@nextfile mov dx,[wLow] shr dx, 4 add dx, 10h mov ax,3100h int 21h endif @@exit: @dprintf ?INITRMDBG, mov ah,04ch ; that was all int 21h start endp ;--- monitor installation ;--- in: CS=SS=DS=DGROUP ;--- si=mode ;--- out: EAX=first page used for EMS/VCPI InitJemm PROC c public push si push di push bp mov bp, sp ;mode is at [bp+4] if 0; ?A20PORTS ;this info is unreliable, don't activate! mov ax,2403h ;get A20 gate support int 15h cmp ah,00 ;ignore carry flag, it is not reliable jnz @F mov wBIOSA20,bx @@: endif if ?INTEGRATED ;--- the xms handle table must be located in the resident conventional memory ;--- part of jemmex mov cx, xms.num_handles ;is at least 8 mov [xms_handle_table.xht_numhandles], cx mov ax,size XMS_HANDLE mul cx add ax,offset xms_handle_array + 15 ;adjust to paragraph and al,0F0h mov [_brptab.wSizeRes],ax endif ;--- store interrupt vectors in resident memory mov si, offset intvecs @@nextint: mov al,[si].INTMOD.bInt cmp al,-1 jz @@intmoddone mov di,[si].INTMOD.wOld cmp di,-1 jz @@nooldsave mov ah,35h int 21h mov cs:[di+0],bx mov cs:[di+2],es @@nooldsave: add si,size INTMOD jmp @@nextint @@intmoddone: ;-- set new interrupt routine offset in the driver header, so any further ;-- access to device EMMXXXX0 is handled by the monitor mov [pIntOfs],offset BPINTR mov [pStratOfs],offset BPSTRAT if ?KD ;--- check for kernel debugger presence. ;--- issue INT 68h, ah=43h and if debugger responds, ;--- tell it about certain GDT selectors and get the ;--- protected-mode entry. mov ax,3500h or D386_RM_Int int 21 mov ax, es ; int 68h vector 0000:0000? or ax, bx jz @F mov si, word ptr [code32+5] ; DS:SI=GDT mov eax, ds shl eax, 4 movzx esi, si add eax, esi mov [si+GDT_SEL+2], ax shr eax, 16 mov [si+GDT_SEL+4], al mov [si+GDT_SEL+7], ah mov ah, D386_Identify int D386_RM_Int cmp ax, D386_Id jnz @F @dprintf ?INITRMDBG, <"InitJemm: kernel debugger detected",10> mov ah, D386_Prepare_PMode mov al, 0 mov cx, GDT_SEL+8 mov bx, FLAT_DATA_SEL mov dx, GDT_SEL mov di, -1 ; ES:DI=IDT ( there's no IDT yet in Jemm! ) int D386_RM_Int mov jemmini.kdofs, edi mov jemmini.kdseg, es @dprintf ?INITRMDBG, <"InitJemm: kernel debugger pm entry: %X:%lX",10>, es, edi cmp word ptr [bp+4], EXECMODE_EXE jnz @F mov ah, 51h int 21h mov es, bx or byte ptr es:[7fh], 1 ; use PSP:7Fh as "kd flag" (if jemm is loaded as .EXE) @@: endif ;--- prepare running Jemm32. ;--- build an IRETDV86 struct on the stack. mov dx,sp and sp,not 3 ; ensure the 32-bit stack is dword aligned xor ax,ax push ax push gs ; IRETDV86._Gs push ax push fs ; IRETDV86._Fs push ax push ds ; IRETDV86._Ds push ax push es ; IRETDV86._Es push ax push ss ; IRETDV86._Ss push ax push dx ; IRETDV86._Esp pushd 20002h or ( V86IOPL shl 12 ) ;IRETDV86.vEFL (VM=1, NT=0, IF=0) IOPL=3 push ax push cs ; IRETDV86._Cs push ax push offset backinv86 ; IRETDV86._Eip movzx ebp,sp ;--- set a GDTR on the stack mov dx,ds movzx edx,dx shl edx,4 movzx eax, word ptr [code32+5] ; get offset GDT from jemm32.bin ;--- set register parameters for 32-bit monitor init lea edi, [edx+offset jemmini] lea ebx, [edx+offset _brptab] lea esi, [edx+offset code32] add eax, esi push eax push GDT_SIZE-1 CLI .386p LGDT FWORD PTR [bp-6] add sp, 2+4 FLAT_CODE_SEL equ 1*8 FLAT_DATA_SEL equ 2*8 MOV EAX,CR0 ; Set PE-Bit in CR0 OR AL,1 MOV CR0,EAX add EBP, EDX ; EBP = flat address of SS:SP XOR AX,AX LLDT AX ; initialise LDT (it is not used) .386 mov FS,AX mov GS,AX MOV AL,FLAT_DATA_SEL ; Addressing everything MOV DS,AX MOV ES,AX mov SS,AX mov ESP,EBP ;--- SS:ESP -> IRETDV86 struct pushd FLAT_CODE_SEL push esi retd backinv86: ;--- the remaining commands are executed in v86-mode ;--- the virtual monitor init code has returned with ;--- CS, DS, ES, SS, FS, GS = unchanged ;--- SP -> "behind" IRETDV86 ;--- EAX = physical address of start of EMS/VCPI memory ;--- interrupts are still disabled push eax ; FIRSTPAGE value @dprintf ?INITRMDBG, <"InitJemm: V86 mode entered",10> call GetRes ;get Jemm resident part mov bx,ax push es if ?INTEGRATED mov es,bx mov word ptr es:[xms_handle_table.xht_pArray+2],bx mov word ptr [xms.Driver+2],bx ;v5.80 adjust XMS address endif if ?VDS push 0 pop es mov ax,cs cmp ax,word ptr es:[4Bh*4+2] jnz @@novds mov es:[4Bh*4+2],bx @@novds: endif pop es sti @dprintf ?INITRMDBG, <"InitJemm: ints enabled, hooking int 15h, 2Fh, ...",10> mov si, offset intvecs @@nextint2: mov al,[si].INTMOD.bInt cmp al,-1 jz @@intmoddone2 mov ah,25h mov dx,[si].INTMOD.wNew push ds mov ds,bx int 21h pop ds add si,size INTMOD jmp @@nextint2 @@intmoddone2: call endinit ife ?INTEGRATED @dprintf ?INITRMDBG, <"InitJemm: calling InstallXMSHandler",10> call InstallXMSHandler if ?MOVEXBDA and not ?MOVEXBDAPM call move_xbda ;must be called after InstallXMSHandler endif endif pop eax ;load FIRSTPAGE value in EAX @dprintf ?INITRMDBG, <"InitJemm: exit, [bp]=%X %X %X %X",10>,[bp+0],[bp+2],[bp+4],[bp+6] pop bp pop di pop si ret InitJemm ENDP ;--- get Jemm resident segment address ;--- may not be CS if Jemm has been moved in an UMB GetRes proc mov ax,jemmini.ResUMB and ax,ax jz @F ret @@: mov ax,cs stc ret GetRes endp ;--- end of initialization ;--- 1. init XMS handle table for integrated version ;--- 2. link resident part in driver chain if moved high endinit proc if ?INTEGRATED ;--- initialize XMS handles which haven't been set yet call GetRes ;get Jemm resident segment address mov es,ax mov di, offset xms_handle_array mov cx, xms.num_handles mov ax, sizeof XMS_HANDLE mul cx add ax,di mov cx,ax ;--- don't modify the handles that were initialized by v86 monitor code! .while es:[di].XMS_HANDLE.xh_flags != XMSF_INPOOL add di, sizeof XMS_HANDLE .endw .while di < cx mov ax, XMSF_INPOOL stosw xor ax, ax stosw stosw stosw stosw .endw mov ax, di add ax, 16-1 and al, 0F0h mov [wLow], ax @dprintf ?INITRMDBG, <"endinit: XMS handle array intialized",10> endif if ?INTEGRATED if ?MOVEXBDA and not ?MOVEXBDAPM call move_xbda endif endif if ?ADDCONV ;--- if conventional memory has been increased (i.e. by I=A000-B7FF), ;--- increase last DOS MCB mov ah,52h ;get LoL int 21h mov si, es:[bx-2] nextitem: mov es, si mov dx, es:[MCB.blsiz] ;get size of MCB inc dx add dx, si ;dx = next block cmp byte ptr es:[MCB.sig], 'M' jnz @F mov si, dx jmp nextitem @@: push ds push 0 pop ds mov ax,ds:[@MEM_SIZE] pop ds shl ax,6 ;kB to paragraphs ;--- v5.80: do jmp even if ax = dx+1 ;--- this might be the case if UMBs are already included? ;--- if UMBs are already included, the last low-mem DOS MCB ;--- should NOT be increased! inc dx cmp ax,dx jbe noconv_increased ;do nothing if BIOS mem is lower or equal sub ax, si dec ax mov es:[MCB.blsiz],ax noconv_increased: @dprintf ?INITRMDBG, <"endinit: done MCB chain check",10> endif if ?MOVEHIGH call GetRes jc @F ;if carry set, we didn't move high mov [wLow],0 ;add the EMMXXXX0 driver to the driver chain call LinkChain ;if we moved high @@: endif @dprintf ?INITRMDBG, <"endinit: exit",10> ret endinit endp if ?MOVEXBDA and not ?MOVEXBDAPM move_xbda proc test jemmini.V86Flags,V86F_MOVEXBDA jz nomovexbda push 0 pop es mov ax,es:[@XBDA] mov dx,es:[@MEM_SIZE] @dprintf ?INITRMDBG, <"move_xbda: xbda seg=%X, convmem=%X kb",10>, ax, dx shl dx,6 cmp ax,dx ;XBDA must be just above conventional memory jnz nomovexbda ;else it a) has been moved already b) doesn't exist mov es,ax movzx si,byte ptr es:[0] ;byte 0 of XBDA contains size in kB shl si,6 ;convert kb to para mov dx,si mov ah,XMS_ALLOCUMB ;alloc UMB call [xms.Driver] cmp ax,1 jnz nomovexbda @dprintf ?INITRMDBG, <"move_xbda: alloc umb ok, seg=%X",10>, bx ;--- copy content of XBDA to new location mov es,bx pushf cli push ds push 0 pop ds xchg bx,ds:[@XBDA] ;set new location of XBDA mov ax,si shr ax,6 ;convert para to kb add word ptr ds:[@MEM_SIZE],ax mov ds,bx mov cx,si shl cx,3 ;convert para to word push si xor si,si xor di,di rep movsw pop si pop ds popf ;--- get last MCB and increase its size by size of XBDA mov ah,52h int 21h mov ax, es:[bx-2] nxtitem: mov es, ax mov dx, es:[MCB.blsiz] ;get size of MCB inc dx add dx, ax ;dx = next block cmp es:[MCB.sig], 'M' jnz @F mov ax, dx jmp nxtitem @@: add es:[MCB.blsiz],si cmp bVerbose,0 jz @F invoke printf, CStr("XBDA moved to first UMB",10) @@: nomovexbda: ret move_xbda endp endif LinkChain proc mov ah,52h ;get DOS LoL in ES:BX int 21h push ds push 0 pop ds mov ax,ds:[67h*4+2] mov ds,ax add bx,22h ;LoL+22h: NUL device (DOS 3.1+) shl eax,16 xchg eax,es:[bx] mov ds:[0],eax pop ds ret LinkChain endp ife ?INTEGRATED ;--- InstallXMSHandler InstallXMSHandler proc ife ?A20XMS ;if there is no XMS A20 trapping cmp jemmini.NumUMBs,0 ;XMS hook is needed *only* for UMBs. jz @@umbdone ;dont install if no UMBs are supplied endif @dprintf ?INITRMDBG, <"InstallXMSHandler: getting XMS UMB status",10> mov dx, -1 mov ah, XMS_ALLOCUMB call [xms.Driver] and ax, ax jnz @@umbalreadythere @dprintf ?INITRMDBG, <"InstallXMSHandler: hooking into XMS driver chain",10> les bx,[xms.Driver] @@nexttest: mov al,es:[bx] cmp al,0EBh jz @@endofchain les bx,es:[bx+1] cmp al,0EAh jz @@nexttest ;--- unexpected pattern found in XMS hook chain @dprintf ?INITRMDBG, <"InstallXMSHandler: unexpected pattern found in XMS hook chain",10> jmp @@umbdone @@endofchain: @dprintf ?INITRMDBG, <"InstallXMSHandler: end of chain found",10> cli mov byte ptr es:[bx+0],0EAh mov word ptr es:[bx+1],offset XMShandler mov cl,jemmini.NumUMBs push ds if ?MOVEHIGH push 0 pop ds mov ax,ds:[67h*4+2] else mov ax, cs endif mov es:[bx+3], ax add bx,5 mov ds, ax ; assume DS:DGROUP mov word ptr ds:[XMSoldhandler+0],bx mov word ptr ds:[XMSoldhandler+2],es if ?A20XMS cmp cl,0 jnz @@xmswithumb mov byte ptr ds:[XMSUMB], 0EAh ;skip UMB code if no UMBs are supplied mov word ptr ds:[XMSUMB+1], bx mov word ptr ds:[XMSUMB+3], es @@xmswithumb: endif pop ds ; assume DS:DGROUP sti jmp @@umbdone @@umbalreadythere: mov dx, CStr("UMB handler already installed, not installing another one",CR,LF,'$') mov ah,9 int 21h @@noxms: @@umbdone: ret InstallXMSHandler endp endif ;--- init XMS ;--- for the integrated version, init some variables ;--- for the EMM-only version, ensure that an XMS host is found XMSinit proc c public uses es mov ax, 4300h int 2fh cmp al, 80h jne @@not_detected mov ax, 4310h int 2fh mov word ptr [xms.Driver+0], bx mov word ptr [xms.Driver+2], es mov ax, 4309h ; XMS get xms handle table int 2fh cmp al,43h jne @@no_table mov word ptr jemmini.XMSHandleTable+0, bx mov word ptr jemmini.XMSHandleTable+2, es @@no_table: mov ax,1 ret @@not_detected: if ?INTEGRATED mov word ptr [xms.Driver+0], offset XMShandler mov word ptr [xms.Driver+2], cs mov word ptr jemmini.XMSHandleTable+0, offset xms_handle_table mov word ptr jemmini.XMSHandleTable+2, cs endif xor ax,ax ret XMSinit endp if ?INTEGRATED ;--- set entry in XMS handle array, sizeK in ecx, baseK in edx I15SetHandle proc c public uses ds bx ;--- first, do a few checks cmp edx, 1024 ;does the block start at 0x100000? jnz @F add edx, 64 ;then exclude the first 64 kB for HMA sub ecx, 64 jc exit @@: if ?XMS35 cmp edx, 400000h ;beyond 4GB? jc @F sub xms.smax, ecx ;/MAXSEXT option set? jnc smax_ok add ecx, xms.smax ;limit to maximum mov xms.smax,0 jecxz exit smax_ok: mov xms.smem_used, 1 jmp beyond4g @@: endif sub xms.max, ecx ;MAXEXT option set? jnc @F add ecx, xms.max ;limit to maximum mov xms.max,0 jecxz exit @@: ;--- adjust xms.mem_free and xms.mem_largest fields, if needed add xms.mem_free, ecx cmp ecx, xms.mem_largest jb @F mov xms.mem_largest, ecx @@: mov eax, edx add eax, ecx shl eax, 10 cmp eax, xms.mem_highest jb @F mov xms.mem_highest, eax @@: beyond4g: push cs pop ds mov bx, offset xms_handle_array .while [bx].XMS_HANDLE.xh_flags != XMSF_INPOOL add bx, sizeof XMS_HANDLE .endw mov [bx].XMS_HANDLE.xh_flags, XMSF_FREE mov [bx].XMS_HANDLE.xh_baseK, edx mov [bx].XMS_HANDLE.xh_sizeK, ecx exit: ret I15SetHandle endp ;--- I15AllocMemory(int dummy, long kbneeded); I15AllocMemory proc stdcall public uses ds si dummy:WORD, kbneeded:DWORD push cs pop ds mov ecx, kbneeded xor si, si mov bx, offset xms_handle_array ;--- scan handle table for a free block that is large enough .while [bx].XMS_HANDLE.xh_flags != XMSF_INPOOL cmp si,0 jnz @F cmp [bx].XMS_HANDLE.xh_flags, XMSF_FREE jnz @F cmp [bx].XMS_HANDLE.xh_sizeK, ecx jb @F if ?XMS35 test word ptr [bx].XMS_HANDLE.xh_baseK+2, 0FFC0h jnz @F endif mov si,bx @@: add bx,sizeof XMS_HANDLE .endw .if si ;--- a block with sufficient size has been found, and ;--- bx points to a free handle now, which will hold ;--- the remaining memory of the block mov eax, [si].XMS_HANDLE.xh_sizeK sub eax, ecx mov [si].XMS_HANDLE.xh_sizeK, ecx ;--- v5.80: don't lock block, so it can be "reallocated" in monitor ; mov [si].XMS_HANDLE.xh_locks, 1 mov [si].XMS_HANDLE.xh_flags, XMSF_USED add ecx, [si].XMS_HANDLE.xh_baseK ;--- if eax (=size) is zero, no new handle is needed and eax, eax jz @F mov [bx].XMS_HANDLE.xh_flags, XMSF_FREE mov [bx].XMS_HANDLE.xh_sizeK, eax mov [bx].XMS_HANDLE.xh_baseK, ecx @@: .endif mov ax, si ret I15AllocMemory endp ;--- get the base of a XMS handle GetEMBBase proc stdcall public wHdl:word ;--- to be fixed: use the wHdl parameter mov eax, xms_handle_array.xh_baseK shl eax, 10 ret GetEMBBase endp endif end start ================================================ FILE: src/JEMM16.INC ================================================ ;--- globals for jemm16.asm and init16.asm if ?INTEGRATED NAMEEXE equ <"JEMMEX"> NAMEMOD equ <"JemmEx"> else NAMEEXE equ <"JEMM386"> NAMEMOD equ <"Jemm386"> endif ;--- modes for mainex() EXECMODE_SYS equ 0 ;launched as dos device driver EXECMODE_EXE equ 1 ;launched as application PF16 TYPEDEF far16 ptr XMSPARAMS struct Driver PF16 0 mem_largest DD 0 ;largest free block in kB mem_free DD 0 ;total free mem in kB mem_highest dd 0 ;highest physical address; by XMS v3 (Jemm386) or Int15 (JemmEx) if ?INTEGRATED max DD 4095*1024 ;MAXEXT= value if ?XMS35 smax DD 1023*4096*1024 ;MAXSEXT= value maxhigh dd -1 ; mask for upper memory address limit endif num_handles DW ?XMS_DEFHDLS ;XMSHANDLES= value if ?XMS35 smem_used db 0 ;set to 1 if any super-extended block is used endif endif XMSPARAMS ends InitJemm proto c IsJemmInstalled proto c EmmInstallcheck proto c XMSinit proto c CheckIntHooks proto stdcall :WORD UnloadJemm proto c if ?INTEGRATED ?XMS_STATICHDLS equ 10 ;free xms handles to be used for int 15h, ax=e820h I15SetHandle proto c I15AllocMemory proto stdcall :WORD, :DWORD GetEMBBase proto stdcall: word endif mainex proto c :WORD, :ptr BYTE printf proto c :ptr byte, :VARARG _memicmp proto c :ptr BYTE, :ptr BYTE, :WORD externdef c jemmini:JEMMINIT externdef c xms:XMSPARAMS externdef c sig1:BYTE externdef c sig2:BYTE externdef c bVerbose:byte if ?INTEGRATED if ?XMS35 externdef c xms_smax_noe820:byte ?XMS_DEFHDLS equ 48 else ?XMS_DEFHDLS equ 32 endif ?XMS_MAXHDLS equ 128 endif ================================================ FILE: src/JEMM32.ASM ================================================ ;***************************************************************************** ;** This is the main 32bit ASM part of JEMM. ;** ;** JEMM contains code of FreeDOS Emm386, which in turn used the source of ;** an EMM made public in c't (a german IT magazine) in 08/90, page 214, ;** written by Harald Albrecht. ;** ;** some parts the code which is based on FD Emm386 is copyright protected and ;** licensed under the Artistic License version (see LICENSE.TXT for details). ;** ;** 1. EMS 3.2 functions (file EMS32.INC) ;** (c) 1990 c't/Harald Albrecht ;** (c) 2001-2004 tom ehlert ;** 2. DMA support (file DMA.ASM) ;** (c) 1990 c't/Harald Albrecht ;** 3. privileged opcode emulation (file EMU.ASM) ;** (c) 2001-2004 tom ehlert ;** ;** The rest of the 32bit source is Public Domain. ;** ;***************************************************************************** TITLE JEMM - Virtual 8086-Monitor NAME JEMM ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ;--- equates ?STKSIZE EQU 200H ; reserved stack space for v86 entry into monitor. ;--- assembly time constants ?NMI equ 1 ; std=1, 1=allow NMIs inside the monitor; ?SKIPINT06 equ 1 ; std=1, 1=skip routing v86 exc to v86 int06 if noone hooked int 6 ?ISRFLGS equ 0 ; std=0, 1=display PIC ISR flags ;?CLEARBOOTFLGS equ 0 ; std=0, 1=clear boot flags in XBDA ?RESETCR4 equ 1 ; std=1, 1=reset CR4 at reboot ?INT19RBT equ 0 ; std=0, 1=emit INT 19h on reboot if ?INTEGRATED ?FREEXMS equ 1 ; std=1, 0 might work ?NAME equ <"JemmEx"> else ?FREEXMS equ 1 ; std=1, 1=free all XMS on exit ?NAME equ <"Jemm386"> endif ?SELINTLOG equ ?V86DBG MAXBLOCKSIZE equ 40000h ; size for mem moves (XMS, int 15h ah=87h); std=256 kB ;--- publics/externals include extern32.inc ;--- macros @v86popreg macro POPAD endm @v86popregX macro mov esp,ebp POPAD endm .text$01 SEGMENT ;--- start: the binary's entry must be at offset 0! public _start _start: jmp InitMonitor dw lowword offset V86GDT ; must be at .text:5, so 16-bit code can locate the GDT size_startsig equ $ - _start ;--- this is a 128 byte helper stack used for VCPI switches to ;--- protected-mode. db ?HLPSTKSIZE - size_startsig dup (55h) ;--- global monitor data V86CR3 DD 0 ; CR3 for monitor address context ;--- IDT - Interrupt Descriptor Table ;--- as default, the IDT is no longer in the shared region IDT_PTR LABEL FWORD ; size+base IDT DW 7FFH if ?SHAREDIDT DD offset V86IDT else dwV86IDT DD 0 endif wMasterPICBase dw 0008h ; put it here for alignment GDT_PTR LABEL FWORD ; size+base GDT DW GDT_SIZE-1 DD offset V86GDT wSlavePICBase dw 0070h ; put it here for alignment dwFeatures dd 0 ; features from CPUID dwStackCurr dd ?TOS - ?STKSIZE dwStackR0 dd 0 ; current R0 stack to compare if monitor reentered by IRQ if ?DYNTRAPP60 dwTSS dd 0 endif dwMaxPhysMem dd 0 ; XMS highest address (r/o) bpstart dd 0 ; linear address start of static bp array in Jemm16.RSEG PageMapHeap dd ?SYSBASE+?PAGETABSYS+6*4 ;--- address space heap for 4MB pages (PDEs). ;--- v5.85: the heap starts at SYSBASE - 8MB ; it's usually max 4 PTEs ( 16MB ), but is ;--- reentrant and grows downwards. ;--- previously the heap did start at 400000h and grew upwards, ;--- but this address region may be used by VMM's _PageReserve() function (PR_PRIVATE) Heap4MB dd ?PAGEDIR + (?SYSBASE shr 20) - 2*4 ;linear address of 4MB page heap inside page directory pSmallHeap label dword ; used during Init only OldInt06 dd 0 dwHeapSize label dword ; used during Init only OldInt19 dd 0 OldInt67 dd 0 ;pPg0PartEnd dd 0 public pSmallHeap public dwHeapSize dwRes dd 0 ; linear address of resident segment if ?HOOK13 dwRFlags dd 0 ; linear address bRFlags endif dwRSeg dd 0 ; resident segment if ?FASTBOOT pSavedVecs dd 0 ; vectors saved during startup ;dwInt19Org dd 0 ; original int 19 vector saved by DOS endif ;--- next 2 vars must be consecutive pV86Hooks dd 0 ; v86 hook proc installed? ( address of variable exported by VMM_SERV_TABLE ) dwHookProc dd 0 if ?NMI OldESP dd 0 ; v5.87; used to temp. store ESP in NMI handling endif ;-- byte vars (variable) ;-- byte vars (rather constant) bNoPool DB 0 ; flags pooling with XMS memory bNoInvlPg DB -1 ; <> 0 -> dont use INVLPG bV86Flags DB 0 ; other flags bReentered db 0 ; count exception handler reentered bIs486 db 0 ; cpu is 486 or better (INVLPG opcode available) if ?PGE bPageMask db 0 ;mask for PTEs in 0-110000h endif bBpTab db 0 ;offset start bptable in RSEG bBpBack db BPTABLE.pBack shr 2 ife ?HOOK13 bDiskIrq db 0 ; bit0: 1=diskette transfer pending endif if ?KD bKD db 0 ; bit0: 1=kernel debugger detected and initialized endif align 4 RunEmuInstr: EmuInstr DB 90h,90h,90h ; run self-modifying code here _ret: ret ;--- place rarely modified data here ;--- so data and code is separated by at least 64 "constant" bytes if ?ROMRO dwSavedRomPTE dd ? ;saved PTE for FF000 page faults dqSavedInt01 dq ? ;saved INT 01 endif .text$01 ends if ?SHAREDGDT .text$01g segment para flat public 'CODE' else .text$03x segment para flat public 'CODE' endif ;--- GDT - Global Descriptor Table ;--- usually ?SHAREDGDT==1, so the GDT is in the shared region V86GDT LABEL DESCRIPTOR DQ 0 ; +00 NULL-Entry DESCRIPTOR <-1,0,0,9AH,0CFh,0> ; +08 flat 4 GB Code (32 Bit) DESCRIPTOR <-1,0,0,92H,0CFh,0> ; +10 flat 4 GB Data (32 Bit) DESCRIPTOR ; +18 TSS V86 DESCRIPTOR <-1,0,0,9AH,0,0> ; +20 std 64k Code Descriptor DESCRIPTOR <-1,0,0,92H,0,0> ; +28 std 64k Data Descriptor DESCRIPTOR ; helper stack segment DQ 0 ; +38 DESCRIPTOR <2FFh,400h,0,0F2H,0,0> ; +40 if ?KD DESCRIPTOR ; +48 (=?GDTSEL) GDT selector endif ; DESCRIPTOR (GDT_SIZE - 9*8)/8 dup (<0,0,0,0,0,0>) org V86GDT+GDT_SIZE if ?SHAREDGDT .text$01g ends else .text$03x ends endif if ?SHAREDIDT public V86IDT .text$01g segment para flat public 'CODE' V86IDT GATE 100h dup (<0,0,0,0>) .text$01g ends endif .text$03 segment ;--- v86 breakpoint table; ;--- order must match the one defined in Jemm16.asm! bptable BPTABLE <> org bptable dd Int06_V86Entry ; BP06, default v86 int 06 handler dd Int19_V86Entry ; BP19, Reboot dd Int67_V86Entry ; BP67, default v86 int 67 handler if ?VDS dd vds_handler ; BPVDS, VDS handler, v86 int 4Bh endif dd V86_Back ; BPBACK, return to pm after calling v86-code dd I15_Simulate87 ; BP1587, simulate Int 15h, ah=87h if ?HOOK13 dd Dma_CopyBuffer ; BP1340, INT13/40 DMA read op thru buffer endif dd xms_handler ; BPXMS, XMS handler (UMB+A20) dd EMMXXXX0_Strategy ; BPSTRAT, EMMXXXX0 device strategy routine dd EMMXXXX0_Interrupt ; BPDEV, EMMXXXX0 device interrupt routine if ?UNLOAD dd Unload ; BPUNL, unload Jemm, return to real-mode endif NUMBP equ (size BPTABLE)/4 ;--- IO trapping table. ;--- IO_Trap_Table is exported (vmm_service_table) ;--- and may be modified by JLOAD! ;--- Default_IO_Trap_Handler() can handle byte ports only! ;--- JLoad expects exactly this structure: ;--- dd ? ; trap handler ;--- dd ? ; no of IOTRAPENTRY in following array ;--- IOTRAPENTRY[] ;--- v5.84: don't trap ports 80h, 8Ch-8Fh, ( 8Fh = page reg of channel 4 ) ;--- C0h-C3h (channel 4) if ?ALT_TRAPHDL ;v5.85 Alternate_IO_Traphandler dd ISA_DMA_Traphandler endif IO_Trap_Table label dword IO_Trap_Handler dd Default_IO_Trap_Handler dd (offset endportmap - offset portmap) / sizeof IOTRAPENTRY portmap label byte if ?DMAPT IOTRAPENTRY <000h,00Fh,Dma_HandleDmaPorts8> IOTRAPENTRY <081h,08Bh,Dma_HandlePagePorts> IOTRAPENTRY <0C4h,0DFh,Dma_HandleDmaPorts16> endif if ?A20PORTS IOTRAPENTRY <060h,060h,A20_Handle60> IOTRAPENTRY <064h,064h,A20_Handle64> IOTRAPENTRY <092h,092h,A20_Handle92> endif endportmap label byte align 4 if ?ROMRO PageFaultFF proc @dprintf ?V86DBG, <"Write access to FF000",10> mov ebx, @GetPTEAddr(?PAGETAB0+0FFh*4) mov ecx, [ebx] ;get PTE for FF000 mov dword ptr [ebx], 0FF000h + 111B ;set the "original" PTE mov [dwSavedRomPTE], ecx call @@invlpgFF mov ecx, offset @@pagefaultcont mov ax, FLAT_CODE_SEL shl eax, 16 mov ax, cx mov cx, 0EE00h if ?SHAREDIDT mov ebx, offset V86IDT else mov ebx, dwV86IDT endif xchg eax, [ebx+1*8+0] xchg ecx, [ebx+1*8+4] mov dword ptr [dqSavedInt01+0], eax mov dword ptr [dqSavedInt01+4], ecx or byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, 1 ;TF=1 @v86popregX ; return to V86, execute 1 instruction ADD ESP, CRS_SIZEX iretd ;--- returned to monitor after 1 instruction run in v86 mode @@pagefaultcont: pushad push ss pop ds @dprintf ?V86DBG, <"After write access to FF000",10> mov ecx, [dwSavedRomPTE] mov eax, @GetPTEAddr(?PAGETAB0+0FFh*4) mov [eax],ecx xor eax, eax mov cr2, eax mov eax, dword ptr [dqSavedInt01+0] mov ecx, dword ptr [dqSavedInt01+4] if ?SHAREDIDT mov ebx, offset V86IDT else mov ebx, dwV86IDT endif mov [ebx+1*8+0], eax mov [ebx+1*8+4], ecx call @@invlpgFF popad and byte ptr [esp].IRETDV86._Efl+1,not 1 ;TF=0 iretd @@invlpgFF: if ?INVLPG cmp [bNoInvlPg],0 jnz @@noinvlpg .486p invlpg ds:[0FF000h] .386 ret @@noinvlpg: endif mov eax, cr3 mov cr3, eax ret align 4 PageFaultFF endp endif ;--- By setting ?AUXIO one may change this to write to COMx; ;--- this can be handled in protected-mode. PrintString proc push esi mov esi, [esp+4+4] nextchar: lodsb cmp al,0 jz done if ?AUXIO call AuxPutChar else if ?USEINT10 mov ah,0Eh mov word ptr [EBP].Client_Reg_Struc.Client_EAX,ax mov word ptr [EBP].Client_Reg_Struc.Client_EBX,0007 mov eax,10h call Exec_Int else mov byte ptr [EBP].Client_Reg_Struc.Client_EAX,al mov eax,29h call Exec_Int endif endif jmp nextchar done: pop esi ret 4 align 4 PrintString endp ;--- sprintfx - formatted output ;--- edi -> output buffer ;--- edx must be preserved! sprintfx proc push esi mov esi, [esp+4+4] @@nextchar: lodsb and al,al jz @@done cmp al,'%' jz is_fmt stosb jmp @@nextchar @@done: ife ?AUXIO endif pop esi ret 4 is_fmt: push offset @@nextchar lodsb mov bl,al lodsb movsx eax, al mov eax, ss:[ebp+eax] ;use SS prefix here! cmp bl,2 jz b2a cmp bl,4 jz w2a jmp dw2a sprintfx endp ;--- helper routines dw2a proc ; display DWORD in eax into EDI push eax shr eax,16 call w2a pop eax dw2a endp ; fall through w2a proc ; display WORD in ax into EDI push eax mov al,ah call b2a pop eax w2a endp ; fall through b2a proc ; display BYTE in al into EDI push eax shr al,4 call @@nibout pop eax ; fall through @@nibout: ; display NIBBLE in al[0..3] into EDI and al,0Fh cmp al,10 sbb al,69H das stosb ret align 4 b2a endp if ?AUXIO include auxio.inc endif .text$01y segment dword flat public 'CODE' ;--- stack frame for exceptions EXCFRAME struct if ?ISRFLGS org -36 _isr dd ? endif org -32 _cr2 dd ? _cr0 dd ? _esp dd ? _ss dd ? _gs dd ? _fs dd ? _es dd ? _ds dd ? Client_Reg_Struc EXCFRAME ends exc_str label byte DB 13,10, ?NAME db ": exception %", 2, EXCFRAME.Client_Int db " occured at CS:EIP=%", 4, EXCFRAME.Client_CS db ':%', 8, EXCFRAME.Client_EIP db ", ERRC=%", 8, EXCFRAME.Client_Error db 13,10 db "SS:ESP=%", 4, EXCFRAME._ss db ':%', 8, EXCFRAME._esp db " EBP=%", 8, EXCFRAME.Client_EBP db " EFL=%", 8, EXCFRAME.Client_EFlags DB " CR0=%", 8, EXCFRAME._cr0 exc_cr2 label byte DB " CR2=%", 8, EXCFRAME._cr2 db 13,10 db "EAX=%", 8, EXCFRAME.Client_EAX db " EBX=%", 8, EXCFRAME.Client_EBX db " ECX=%", 8, EXCFRAME.Client_ECX db " EDX=%", 8, EXCFRAME.Client_EDX db " ESI=%", 8, EXCFRAME.Client_ESI db " EDI=%", 8, EXCFRAME.Client_EDI db 13,10 db "DS=%", 4, EXCFRAME._ds db " ES=%", 4, EXCFRAME._es db " FS=%", 4, EXCFRAME._fs db " GS=%", 4, EXCFRAME._gs if ?ISRFLGS db " ISR=%", 4, EXCFRAME._isr endif db ' [CS:IP]=' db 0 ;----------------------------------- byte word dword [cs:ip] crlf % sizeexcstr equ ($ - offset exc_str) + 1*2 + 6*4 + 13*8 + 8*3 + 2 - 20*3 + (?ISRFLGS * (4-3)) exc_str2 db 'Press ESC to abort program, F1/F2 for reboot ', 0 exc_str3 db 13, ?NAME, ': unable to continue - press F1/F2 for reboot ', 0 szCRLF db CR, LF, 0 align 4 .text$01y ends ;--- the monitor entry ;--- it should not be at the very beginning of this segment if ?SELINTLOG logintbm label dword ;--- selective logs of INTs. default: log INT 0-7 only ; -------+-------3-------+-------2-------+-------1-------+-------0 dq 0000000000000000000000000000000000000000000000000000000011111111b ; -------+-------7-------+-------6-------+-------5-------+-------4 dq 0000000000000000000000000000000000000000000000000000000000000000b dq 0,0 endif align 4 if ?FASTMON V86_MonitorEx: ;v5.86: label needed because the 32 entries won't fit in 128 bytes anymore push dword ptr [esp] jmp short V86_Monitor align 4 ;--- v5.86: "fast" entries 0-7 get a fake error code now; ;--- thus they are handled as exceptions by the monitor. int00 label near INTNO = 0 REPT ?FASTENTRIES ;32 "fast" entries push INTNO if INTNO LT 8 jmp short V86_MonitorEx else jmp short V86_Monitor endif INTNO = INTNO+1 ENDM endif ;?FASTMON ; all entries in the IDT (except 15h and 67h) jump to V86_Monitor. ; It has to check if an exception has occured. If no, just reflect the ; interrupt to v86-mode. If yes, check what the reason for the exception ; was and do the appropriate actions. ; ; inp: the cpu has set DS,ES,FS,GS==NULL if switch from v86 mode! ; [ESP+0] = INT# ;--- stack frame in V86 monitor for IRQs and software INTs V86FRAME struct PUSHADS <> dd ? _IntNo dd ? ; note that int# in V86FRAME differs from Client_Int in Client_Reg_Struc! IRETDV86 <> V86FRAME ends V86_Monitor PROC public ;--- Three cases must be handled: ;--- case 1: exception (with error code) in V86 mode (08,0A,0B,0C,0D,0E); ;--- on the stack: IRETDV86, error code, int# ;--- v5.86: entry code for int 0-7 does now push a "faked" error code! ;--- so they are now also treated as exceptions. CMP ESP, ?TOS - (sizeof IRETDV86 + 2*4) ; exception with errcode in v86 mode? JZ V86_Exception ;--- case 2: EXC or IRQ (HLT emu, Yield) in v86 monitor; monitor reentered? CMP ESP, ?TOS - (sizeof IRETDV86 + 1*4) JNZ @@Reentered ;--- case 3: normal INT/IRQ (or EXC > 7 without error code) sub esp, 4 ; v5.85: added ( to make V86FRAME "match" Client_Reg_Struc ) PUSHAD MOV EBP,ESP ; ebp -> Client_Reg_Struc ;if ?V86DBG and ?V86XDBG ; @dprintf ?V86DBG, <"."> ;endif ;-- we don't know for sure what size the stack is. ;-- There might exist other ring0 code which intruded in the ;-- monitors address context and modified GDT/IDT entries ;-- But at least they should have established a 32-bit stack or ;-- at the very least cleared HiWord(ESP) so EBP will point to a ;-- valid frame. MOVZX ECX,byte ptr [EBP].V86FRAME._IntNo ;load 1 byte ONLY (the int# is sign-extended)! if ?V86DBG if ?SELINTLOG bt cs:[logintbm],ecx jnc @F endif @dprintf ?V86DBG, <"Int in v86-mode, #=%X, CS:EIP=%X:%X EBP=%X",10>, cx, word ptr [ebp].V86FRAME._Cs,[ebp].V86FRAME._Eip, ebp @@: endif ;--- v5.85: are there hooks installed? ;--- prior to v5.85, Jload did install hooks by directly ;--- modifying the IDT - that was not a very good idea. mov edx, cs:[pV86Hooks] cmp edx, 0 jz @F cmp dword ptr cs:[edx+4*ecx], 0 jz @F mov eax, ss mov ds, eax mov es, eax mov eax, ecx mov ecx, [edx+4*ecx] mov esp, [dwStackCurr] ;v5.87: don't run on top of stack, it's reserved for client state call [dwHookProc] mov esp,ebp jnc int_handled MOVZX ECX,byte ptr [EBP].V86FRAME._IntNo @@: ;--- simulate an int in v86 mode MOV ESI,[EBP].V86FRAME._Eip MOV EDI,[EBP].V86FRAME._Cs MOV EDX,[EBP].V86FRAME._Efl MOVZX EAX, word ptr [EBP].V86FRAME._Esp ; use LOWORD(ESP) only! MOVZX EBX, word ptr [EBP].V86FRAME._Ss shl ecx,2 SHL EBX,4 sub word ptr [EBP].V86FRAME._Esp, sizeof IRETWS ; copy Interrupt frame down. Use SS, since DS is unset ;--- v5.87: emulate 16-bit SP correctly in case there's a wrap-around. sub ax, 2 MOV SS:[EBX+eax],DX sub ax, 2 MOV SS:[EBX+eax],DI sub ax, 2 MOV SS:[EBX+eax],SI MOV EAX,SS:[ECX] ; route call to vector in real-mode IVT AND DH, NOT (1 or 2) ; Clear IF+TF as it is done in real-mode (RF should also be cleared!) MOV word ptr [EBP].V86FRAME._Eip,AX SHR EAX, 16 MOV [EBP].V86FRAME._Cs, EAX MOV [EBP].V86FRAME._Efl, EDX int_handled: @v86popreg add ESP, CRS_SIZEX ; skip fields between PUSHADS and IRETDV86 IRETD ; return to v86-mode align 4 ;--- Monitor has been reentered in PL0: IRQ, NMI or exception; ;--- exceptions without error code will have a faked one here; ;--- IRQs can only occur thru Yield() and hence will have a known ESP (dwStackR0); ;--- ESP -> int#, if exc:errorc, IRETDS ;--- DS,ES=valid selectors (not for NMI) @@Reentered: if ?NMI ;--- v5.87: handle NMIs in PL0 before anything else cmp dword ptr [esp],2 jz handle_nmi_pl0 endif PUSHAD ;--- v5.87: check moved here (was in handle_exception) mov eax, ss ; check SS size - just in case lar eax, eax test eax,400000h jnz @F movzx esp,sp @@: MOV EBP,ESP ; ebp -> Client_Reg_Struc if exception ;--- v5.86: fixed, V86FRAME got another member in v5.85; ;--- so since IRQs don't push error codes, the -4 displacement is necessary! ;--- v5.87: don't use V86FRAME struct here, it's confusing. ; cmp word ptr [ebp-4].V86FRAME._Cs, FLAT_CODE_SEL cmp word ptr [ebp + sizeof PUSHADS + 4].IRETDS._Cs, FLAT_CODE_SEL JNZ handle_exception_r0 ;--- IRQs in ring 0 will only occur controlled - after EnableInts() has been called. ;--- EnableInts will have setup variable dwStackR0 - thus it's possible to identify IRQs; ;--- a ring0 IRQ will have pushed IRETDS, int# and PUSHADS: lea eax,[ebp + sizeof PUSHADS + 4 + sizeof IRETDS] cmp eax,cs:[dwStackR0] jnz handle_exception_r0 ;--- it's an IRQ - DS should be set correctly; ;--- v5.86: fixed, V86FRAME._IntNo isn't correct for IRQs! ; MOVZX ECX,byte ptr [EBP].V86FRAME._IntNo ;load 1 byte ONLY! MOVZX ECX, byte ptr [EBP+sizeof PUSHADS] ; int# is sign-extended! if ?V86DBG if ?SELINTLOG bt [logintbm],ecx jnc @F endif @dprintf ?V86DBG, <"V86 IRQ in PL0, int=%X ebp=%X, cs:eip=%X:%X, efl=%X",10>, ecx,\ ebp, word ptr [ebp-4].V86FRAME._Cs, [ebp-4].V86FRAME._Eip, [ebp-4].V86FRAME._Efl @@: endif push ecx call @@runv86 @v86popreg add ESP,4 ;skip int# IRETD if ?NMI ;--- v5.87: reworked the NMI code for PL0; handle_nmi_pl0: add esp, 4+4 ;skip the dword's pushed by NMI entry code (=CRS_SIZEX) mov ss:[OldESP], esp ;--- since an NMI can occur at any time, ESP may be inside the v86 client register region; ;--- then ESP has to be setup with dwStackCurr. cmp esp, ?TOS jnc @F cmp esp, cs:[dwStackCurr] cmc jnc @F mov esp, cs:[dwStackCurr] @@: push ss:[OldESP] pushad push ds push es mov eax, ss mov ds, eax mov es, eax jnc @F ;--- save IRETD frame mov ebp,[esp+2*4+sizeof PUSHADS] ;get old ESP push [ebp].IRETDS._Efl push [ebp].IRETDS._Cs push [ebp].IRETDS._Eip @@: pushfd ; C=IRETD frame saved on stack ;--- route NMI to v86-mode; ;--- save/restore a few client fields; ;--- clear IF/TF so monitor won't be reentered by an IRQ. mov ebp, ?TOS - sizeof Client_Reg_Struc push [ebp].Client_Reg_Struc.Client_Int push [ebp].Client_Reg_Struc.Client_Error push [ebp].Client_Reg_Struc.Client_EFlags and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, not (1 or 2) call nmiv86 pop [ebp].Client_Reg_Struc.Client_EFlags pop [ebp].Client_Reg_Struc.Client_Error pop [ebp].Client_Reg_Struc.Client_Int popfd jnc @F ;--- restore IRETD frame mov ebp, [esp+sizeof IRETDS+2*4+sizeof PUSHADS] ;get old ESP pop [ebp].IRETDS._Eip pop [ebp].IRETDS._Cs pop [ebp].IRETDS._Efl @@: @dprintf ?NMIDBG, <"NMI PL0 exit, PL3 CS:IP=%X:%X SS:SP=%X:%X",10>, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP, [ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP pop es pop ds ;--- restore GPRs and ESP popad pop esp iretd align 4 nmiv86:: ;--- NMI must be masked - else the IRETD to enter v86 will re-trigger it... mov al,80h out 70h,al in al,71h ;--- run NMI interrupt in v86; registers eax,ecx,edx may be trashed if called by PL0, but shouldn't matter push 2 call @@runv86 ;--- the v86 NMI handler hopefully has reset the NMI request, so let's unmask it... mov al,0 out 70h,al in al,71h ret endif align 4 @@runv86: ;--- running v86-mode will reset registers FS & GS to 0! mov ebp, ?TOS - sizeof Client_Reg_Struc call Begin_Nest_Exec ;simulates a FAR call (CS:IP = RSEG:BPBack) mov eax, [esp+4] call Exec_Int ;runs an INT xx - push current CS:IP & flags; run INT xx call End_Nest_Exec ;simulates a RETF ret 4 align 4 V86_Monitor ENDP ;--- breakpoint: handle exc 06 in v86-mode Int06_V86Entry proc call Simulate_Iret mov [ebp].Client_Reg_Struc.Client_Int,6 jmp handle_exception_ebp align 4 Int06_V86Entry endp ;--- handle exceptions (ring 0 protected-mode and v86-mode); ;--- display current register set; ;--- if it is caused by external protected-mode code, display REBOOT option ;--- else jump to v86-mode and try to abort current PSP ;--- handle_exception_r0: esp & ebp -> PUSHADS + exc# + (faked) errc + IRETDS ;--- v5.87: reworked a) segment registers and [CS:E/IP] are now always displayed. ;--- b) F1/F2 accepted for fastboot/reboot ;--- c) Yield() & port 60h access used instead of INT 16h ;--- d) sprintfx() for formatted output introduced handle_exception proc handle_exception_r0:: ; <--- entry exc in ring 0 ;--- v5.87: display for segment registers added push ds push es push fs push gs jmp @F align 4 handle_exception_ebp:: ; <--- entry exc in v86 mov esp,ebp ;--- v5.87: handle segment registers similar to PL0 push [ebp].Client_Reg_Struc.Client_DS push [ebp].Client_Reg_Struc.Client_ES push [ebp].Client_Reg_Struc.Client_FS push [ebp].Client_Reg_Struc.Client_GS mov edx,0ffff0h+0ffffh @@: mov ax, FLAT_DATA_SEL mov ds, eax mov ES, eax inc [bReentered] cld ;--- if it's an exception in ring0, the Client_Reg_Struc ;--- may miss all fields beyond Client_Reg_EFlags! @dprintf ?EXCDBG, <"Exception dump, exception %X at cs:eip=%X:%X",10>, [ebp].Client_Reg_Struc.Client_Int, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP test byte ptr [ebp].Client_Reg_Struc.Client_EFlags+2,2 ;V86 mode? jnz @@isv86 mov esi, [ebp].Client_Reg_Struc.Client_EIP lar eax, [ebp].Client_Reg_Struc.Client_CS lsl edx, [ebp].Client_Reg_Struc.Client_CS and ah,60h jnz @@isring3 ; ring3 protected-mode code is not supposed to run in Jemm's context... mov ebx, ss lea ecx,[ebp + Client_Reg_Struc.Client_ESP] ;in ring 0, esp & ss are also missing jmp @@isring0 @@isv86: ;--- setup ESI to CS:IP movzx esi,word ptr [ebp].Client_Reg_Struc.Client_CS shl esi,4 add esi,[ebp].Client_Reg_Struc.Client_EIP @@isring3: mov ebx,dword ptr [ebp].Client_Reg_Struc.Client_SS MOV ecx,dword ptr [ebp].Client_Reg_Struc.Client_ESP @@isring0: push ebx ; ebp-20 == SS push ecx ; ebp-24 == ESP .386p mov eax, cr0 push eax ; ebp-28 mov eax, cr2 if 1 ;v5.80: display cr4 instead of cr2 if no page fault ;--- CR4 exists only if cpuid is supported cmp [dwFeatures],0 jz @F mov exc_cr2 + 3, '2' cmp byte ptr [ebp].Client_Reg_Struc.Client_Int, 0Eh jz @F mov exc_cr2 + 3, '4' .586p mov eax, cr4 .386 @@: endif push eax ; ebp-32 if ?ISRFLGS mov al,0Bh ;irq 8-15 in service? out 0A0h,al in al,0A0h mov ah,al mov al,0Bh ;irq 0-7 in service? out 20h,al in al,20h push eax ; ebp-36 endif ;--- setup EDI, used as output buffer sub esp, (sizeexcstr and not 3) + 4 mov edi,esp ;--- v5.87: sprintfx replaced render_items push offset exc_str call sprintfx ;--- render up to 8 bytes at cs:eip (=esi); ;--- edx = segment limit mov cl,8 @@nextitem: cmp edx, esi jb done_cseip ;--- for page faults, check CR2! cmp byte ptr [ebp].Client_Reg_Struc.Client_Int, 0Eh jnz @F mov eax, cr2 cmp eax, esi jz done_cseip @@: lodsb call b2a mov al,' ' stosb dec cl jnz @@nextitem done_cseip: mov eax,0A0Dh stosd mov ebp, ?TOS - sizeof Client_Reg_Struc ;--- reset client's AC flag ;--- since we use v86-mode for displays now and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+2,not 4 ;--- make sure the first 4 entries in the breakpoint table are valid ;--- this will make Jemm work even if severe damage has been done to ;--- the v86 memory. mov ecx, [bpstart] mov dword ptr [ecx], (?BPOPC shl 24) or (?BPOPC shl 16) or (?BPOPC shl 8) or ?BPOPC ;--- display the exception info call Begin_Nest_Exec push esp call PrintString mov eax,ss cmp ax, FLAT_DATA_SEL jz @F inc [bReentered] push ds pop ss @@: mov esp, ?TOS - ?STKSIZE mov [dwStackCurr],esp ; reinit ring 0 stack prompt: mov eax, offset exc_str2 ; "press ESC ..." cmp bReentered, 2 jb @F mov eax, offset exc_str3 ; "press F1/F2 to reboot..." @@: push eax call PrintString @@waitkey: if ?AUXIO call AuxGetChar cmp al,1Bh jnz @@waitkey else cmp [bReentered],2 jnc @F in al, 21h push eax mov al, not 1 ;allow PIT timer irq out 21h,al call Yield pop eax out 21h,al @@: in al, 64h test al, 01h; kbd input buffer full? jz @@waitkey mov ah, al in al, 60h test ah, 20h; is it input from PS/2 mouse? jnz @@waitkey cmp al, 3Bh ; F1? jz softboot cmp al, 3Ch ; F2? jz _Reboot cmp [bReentered],2 jnc @@waitkey cmp al, 1 ; ESC? jnz @@waitkey endif push offset szCRLF ;print a CR/LF call PrintString mov [bReentered],0 mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 4C7Fh mov eax,21h call Exec_Int mov [bReentered],2 jmp prompt softboot: ;--- _SoftBoot expects to be called by int 15h, ax=4F53 (Ctrl-Alt-Del pressed) ;--- this has to be emulated. mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 4F53h or byte ptr [ebp].Client_Reg_Struc.Client_EFlags, 1 ;set carry flag or byte ptr ds:[@KB_FLAG],1100b ; set key status CTRL & ALT jmp _SoftBoot align 4 handle_exception endp if ?BPOPC ne 0F4h ;--- true IDT 06 entry if breakpoint is ARPL Int06_Entry proc push 0 ;exc 06 has no error code, set a dummy one push 6 pushad mov ebp, esp ; ebp -> Client_Reg_Struc mov eax, ss ; might be better to use "mov eax,cs" "add eax,8" mov ds, eax jmp Is_BP align 4 Int06_Entry endp endif ;--- exception in V86 mode with error code. ;--- IDT entry for gate 10h will have pushed a fake error code for float exceptions. ;--- v5.86: exceptions 0-7 will have pushed a fake error code. ;--- esp -> int#, error code, IRETDV86 ;--- DS,ES = NULL V86_Exception proc pushad mov ebp, esp ;--- now ebp -> Client_Reg_Struc mov ecx, [ebp].Client_Reg_Struc.Client_Int if ?V86DBG ;--- this happens quite often, so better activate selectively if ?SELINTLOG bt cs:[logintbm],ecx jnc @F endif @dprintf ?V86DBG, <"exception in v86-mode, #=%X, errc=%X, cs:ip=%X:%X",10>, ecx, [ebp].Client_Reg_Struc.Client_Error, word ptr [ebp].Client_Reg_Struc.Client_CS, word ptr [ebp].Client_Reg_Struc.Client_EIP @@: endif MOV EAX,SS MOV DS,EAX mov esp,[dwStackCurr] if ?NMI cmp ecx,2 jz nmi_pl3 endif ; @dprintf ?V86DBG, <"V86_Exception, esp=%X, cs:eip=%X:%X",10>, esp,[ebp].Client_Reg_Struc.Client_CS,[ebp].Client_Reg_Struc.Client_EIP CMP ECX,0DH ; general protection exception? JNZ @@V86_TEST_EXC ; no, check further if ?BPOPC ne 0F4h Is_BP:: ; <--- entry from INT 06 if breakpoint is ARPL endif ;--- a monitor "bp", used to switch back from v86 to protected-mode? ;--- EAX=flat selector MOV ES,EAX MOVZX ESI,word ptr [EBP].Client_Reg_Struc.Client_CS mov ecx,[EBP].Client_Reg_Struc.Client_EIP SHL ESI,4 ADD ESI,ECX ; ESI = linear CS:EIP ; check what triggered the GPF. Possible reasons are: ; - "bp" opcode (HLT or ARPL), which then might be: ; + a "Breakpoint" if CS:EIP points into the breakpoint table. ; call the breakpoint handler proc then. ; + other HLTs. Is handled by running HLT in ring 0 inside the monitor. ; - trapped I/O command. I/O only causes GPF for masked ports. ; The DMA, KBC and P92 ports are trapped inside Jemm, but external ; modules may take over the whole IO port trapping (JLOAD). ; - other privileged opcode. Some are emulated (mov CRx, reg ...), ; some are not and then are just translated to an int 6, illegal opcode, ; which is then reflected to the V86 task!). ; MOV AL,[ESI] ; check opcode cmp AL, ?BPOPC jnz @@NoBPOPC ; breakpoint? ; mov eax, [bpstart] ; sub esi, eax sub esi, [bpstart] jb @@No_BP cmp esi, NUMBP jae @@No_BP call [esi*4+offset bptable] exc_exit: @v86popregX add ESP, CRS_SIZEX ; skip int# + error code IRETD nmi_pl3: ; v5.87: handle NMI occured in PL3 call nmiv86 jmp exc_exit @@No_BP: if ?BPOPC ne 0F4h jmp V86_Exc0D ; if an ARPL occured at unknown location... endif @@Is_Hlt: @dprintf ?HLTDBG, <"True HLT occured at CS:IP=%X:%X",10>, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP INC [EBP].Client_Reg_Struc.Client_EIP ; Jump over the HLT instruction ; mov esp,[dwStackCurr] ; v5.87: in V86_Exception, register ESP is already setup correctly. ;--- doing a HLT with interrupts disabled will freeze the machine ;--- (or at least wait for a NMI); That's MS Emm386 compatible. if 0 ; activate if HLT should be a NOP if IF==0 test byte ptr [ESP].IRETDV86._Efl+1,2 jz exc_exit endif call EnableInts STI ; give Interrupts free and wait for IRQ HLT CLI call DisableInts jmp exc_exit ;--- exc 0Dh in v86, no BP @@NoBPOPC: if ?BPOPC ne 0F4h CMP AL,0F4H ; HLT instr? JZ @@Is_Hlt endif if ?SB ; see if SoundBlaster INT 3 forced to 1ah error code GPF CMP [EBP].Client_Reg_Struc.Client_Error,1ah jne @@notsb test [bV86Flags],V86F_SB je @@notsb ; SB option not turned on inc [EBP].Client_Reg_Struc.Client_EIP ; skip INT 3 opcode (shouldn't this be checked first?) @v86popregX add esp, CRS_SIZEX ; discard GPF error code & INT# push 3 ; simulate an INT 3 (no exception!) jmp V86_Monitor @@notsb: endif @dprintf ?EMUDBG, <"Opcode %X %X %X caused GPF at %X",10>, byte ptr [esi], byte ptr [esi+1], byte ptr [esi+2], esi cmp al,0Fh ; check if potentially mov ,cr# je ExtendedOp ; ExtendedOp will jmp back to V86_Exc0D if no emulation happened mov cl,0 mov edi, esi cmp al,0F3h ; REP prefix? setz ch jnz @F inc esi mov al,[esi] @@: cmp al,66h ; 66 prefix? setz ah jnz @F inc esi mov al,[esi] @@: ;--- to be fixed: at least OUTS may accept a segment prefix (64,65,26,2E,36,3E)! ;--- and prefix 67h may cause a GPF at least... cmp al,6Ch JB V86_Exc0D cmp al,6Fh ; string IO (opcodes 6C, 6D, 6E, 6F)? JBE @@DoIO_String CMP AL,0E4H JB V86_Exc0D CMP AL,0E7H ; IN AL|AX,xx or OUT xx,AL|AX (opcodes E4, E5, E6, E7)? JBE @@DoIO_Im CMP AL,0ECH JB V86_Exc0D CMP AL,0EFH ; IN/OUT DX (opcodes EC, ED, EE, EF)? JBE @@DoIO_DX JMP V86_Exc0D ;--- exception (not 0Dh) in V86 mode ;--- DS=FLAT, ECX=INT# @@V86_TEST_EXC: if ?ROMRO cmp ECX, 0Eh jnz @@V86_EXC_NOT0D0E mov eax, CR2 shr eax, 12 cmp eax, 0FFh ;it is the FF000 page (which is r/o!) jz PageFaultFF ;--- unhandled v86-mode exception 0Eh occured @@V86_EXC0E: endif ;************************************************************* ; unhandled exception in v86-mode. ;--- this has been modified for v5.86. ;--- previously, just exceptions with error code were handled here; ;--- since such exceptions aren't usually handled by v86-code, ;--- the default behavior was to terminate the application; ;--- exception was exc 0Dh, which was routed to INT 06 unless V86EXC0D ;--- option was set. ; ;--- now, exc 0,1,3,4,5,6 and 7 are also handled here. ;--- however, they are all routed to INTs. ; this notifies any hookers about the problem. If noone has hooked ; v86-int 06, we finally end at the monitor again, display a register ; dump and then try to terminate the current PSP. ;************************************************************* ;--- unhandled v86-mode exception xxh occured ;--- ecx=exc# @@V86_EXC_NOT0D0E: if ?V86EXC0D ;option V86EXC0D supported? (should be 1) jmp noexcrtn endif ;--- unhandled v86-mode exception 0Dh occured. ;--- the emulated "special register" moves, ;--- I/O instructions and HLT have been handled here... ;--- ecx undefined V86_Exc0D:: if ?V86EXC0D ;--- V86EXC0D option set? If yes, route the exception to ;--- v86 int 0Dh instead of int 06h. test [bV86Flags], V86F_V86EXC0D mov eax,0Dh jnz v86excrtn noexcrtn: endif ;--- v5.86: call V86 Fault handler. ;--- they'll either just RET or chain to the default handler (V86_Fault) mov eax,[ebp].Client_Reg_Struc.Client_Int call [vmm_service_table.pV86Faults] jmp v86excrtn2 V86_Fault:: ;--- v5.86: route exc 0-7 to INT cmp eax,8 jb v86excrtn ;--- if "noone" has hooked v86 int 06 vector, there's ;--- no need to route v86 exceptions to v86-mode, since ;--- it just will be thrown back to Jemm. This allows to ;--- display the true exception number (0C/0D/0E/..) if ?SKIPINT06 movzx eax,word ptr ds:[6*4+2] cmp eax, [dwRSeg] jz handle_exception_ebp endif ;--- simulate invalid opcode interrupt in v86 mode mov eax,6 v86excrtn: call [vmm_service_table.pSimulate_Int] v86excrtn2: @v86popregX add ESP, CRS_SIZEX ; skip error code & "int#" IRETD ; return to virtual 86-Mode align 4 ;*************************************** ; IO command has been trapped ;--- opcodes 6C-EF (INS, OUTS), in AL ;--- CH=01 if rep prefix, else 00 ;--- AH=01 if 66h prefix, else 00 @@DoIO_String: rol ecx,16 mov cx,word ptr [ebp].Client_Reg_Struc.Client_ES test al,2 ;OUTS opcode? jz @@isstrin mov cx,word ptr [ebp].Client_Reg_Struc.Client_DS @@isstrin: rol ecx,16 or cl,STRING_IO shl ch,6 or cl,ch ;set REP bit JMP @@DoIO_DX ;--- opcodes E4-E7 (IN AL,XX ; IN AX,XX ; OUT XX, AL ; OUT XX, AX) @@DoIO_Im: INC ESI MOVZX EDX,BYTE PTR [ESI] ; get I/O port in DX ;--- entry opcodes EC-EF (IN AL|AX,DX ; OUT DX,AL|AX) ;--- DX = Client_DX @@DoIO_DX: inc esi and ah,al ;if AH still 1, it is DWORD IO shl ah,4 or cl,ah shr ah,4 xor ah,al and ah,1 shl ah,3 or cl,ah and al,2 shl al,1 or cl,al sub esi, edi add [ebp].Client_Reg_Struc.Client_EIP, esi mov eax, [ebp].Client_Reg_Struc.Client_EAX ;--- now DX, EAX and CL is set for the IO handlers push ecx if ?ALT_TRAPHDL ;--- v5.85: if trap handler has been changed, and port is < 0x100, ;--- call the alternate handler if it has been set cmp [IO_Trap_Handler], Default_IO_Trap_Handler jz @F cmp dh, 0 jnz @F call [Alternate_IO_Traphandler] jc @@io_handled @@: endif call [IO_Trap_Handler] @@io_handled: pop ecx test cl,OUT_INSTR or STRING_IO jnz @@isout mov [ebp].Client_Reg_Struc.Client_EAX, eax @@isout: @v86popregX ADD ESP, CRS_SIZEX IRETD align 4 V86_Exception endp ;--- default IO trap handler; ;--- can handle byte ports only ;--- will scan portmap for internally trapped ports ( DMA, A20 ) ;--- in: ;--- CL=flags ;--- DL=port Default_IO_Trap_Handler proc if 0 ;all internal functions handle byte i/o only test cl,STRING_IO or DWORD_IO or WORD_IO jnz Simulate_IO_trap endif mov esi, offset portmap @@nextport: cmp dl, [esi].IOTRAPENTRY.bStart jb @@skipport cmp dl, [esi].IOTRAPENTRY.bEnd jbe @@portfound @@skipport: add esi,size IOTRAPENTRY cmp esi, offset endportmap jnz @@nextport jmp Simulate_IO @@portfound: jmp dword ptr [esi].IOTRAPENTRY.dwProc align 4 Default_IO_Trap_Handler endp ;--- Simulate_IO: emulate IN/OUT ;--- in: ;--- EBP -> client ptr ;--- CX = flags ;--- Hiword(ECX) = segment of src/dst for string IO ;--- EAX = data (if OUT_INSTR flag is set) ;--- DX = port ;--- it converts: ;--- WORD IO -> BYTE IO ;--- DWORD IO -> WORD IO ;--- STRING IO -> DWORD/WORD/BYTE IO ;--- REP STRING IO -> multiple DWORD/WORD/BYTE IO ;--- v5.84: fixed: Simulate_IO did call [IO_Trap_Handler] ;--- now it just splits I/O to byte accesses. ;--- The new function Simulate_IO_trap() does what Simulate_IO() ;--- did prior to v5.84. Simulate_IO_trap proc public mov ebx, [IO_Trap_Handler] jmp Simulate_IOex Simulate_IO_trap endp Simulate_IO proc public mov ebx, offset Simulate_IOex Simulate_IO endp ;IOPROC textequ <[IO_TrapHandler]> ;IOPROC textequ IOPROC textequ <[ebp].Client_Reg_Struc.Client_res0> Simulate_IOex proc mov IOPROC, ebx ;EBX is a volatile register in DMA, cannot be used test cl,STRING_IO jnz @@isstrio push ecx movzx ecx,cl ; and ecx,1Ch and ecx,DWORD_IO or WORD_IO or OUT_INSTR mov ecx,[ecx + offset iojmp] xchg ecx, [esp] ret align 4 iojmp label dword dd @@bin ;+00 dd @@bout ;+04 (OUT_INSTR is 4) dd @@win ;+08 (WORD_IO is 8) dd @@wout ;+0C dd @@din ;+10 (DWORD_IO is 10h dd @@dout ;+14 ; dd ?? ;+18 either WORD_IO or DWORD_IO are set, never both ; dd ?? ;+1C @@bin: in al,dx ret @@bout: out dx,al ret @@win: and cl,not WORD_IO push edx push ecx call IOPROC pop ecx mov edx,[esp] inc edx push eax call IOPROC mov [esp+1],al pop eax pop edx ret @@wout: and cl,not WORD_IO push edx push ecx call IOPROC pop ecx mov edx,[esp] inc edx mov al,ah call IOPROC pop edx ret @@din: and cl,not DWORD_IO or cl,WORD_IO push edx push ecx call IOPROC pop ecx mov edx,[esp] add edx,2 push eax call IOPROC mov [esp+2],ax pop eax pop edx ret @@dout: and cl,not DWORD_IO or cl,WORD_IO push edx push ecx call IOPROC pop ecx pop edx add edx,2 shr eax,16 jmp IOPROC @@isstrio: test cl, REP_IO jz @@isnorepio and cl, not REP_IO jmp @@teststr @@nextio: push ecx push edx call @@isnorepio pop edx pop ecx dec word ptr [ebp].Client_Reg_Struc.Client_ECX @@teststr: cmp word ptr [ebp].Client_Reg_Struc.Client_ECX,0 jnz @@nextio ret @@isnorepio: and cl,not STRING_IO push ecx movzx ecx, cl ; and ecx,1Ch and ecx,DWORD_IO or WORD_IO or OUT_INSTR mov ecx, [ecx + offset siojmp] xchg ecx, [esp] ret align 4 siojmp label dword dd @@sbin dd @@sbout dd @@swin dd @@swout dd @@sdin dd @@sdout ; dd ?? ; either WORD_IO or DWORD_IO are set, never both ; dd ?? @@sbin: call @@sxin stosb add word ptr [ebp].Client_Reg_Struc.Client_EDI,1 ret @@swin: call @@sxin stosw add word ptr [ebp].Client_Reg_Struc.Client_EDI,2 ret @@sdin: call @@sxin stosd add word ptr [ebp].Client_Reg_Struc.Client_EDI,4 ret @@sxin: call IOPROC rol ecx, 16 movzx esi,cx rol ecx, 16 shl esi, 4 movzx edi, word ptr [ebp].Client_Reg_Struc.Client_EDI add edi, esi ret @@sbout: call @@sxout lodsb add word ptr [ebp].Client_Reg_Struc.Client_ESI,1 jmp IOPROC @@swout: call @@sxout lodsw add word ptr [ebp].Client_Reg_Struc.Client_ESI,2 jmp IOPROC @@sdout: call @@sxout lodsd add word ptr [ebp].Client_Reg_Struc.Client_ESI,4 jmp IOPROC @@sxout: rol ecx, 16 movzx eax,cx rol ecx, 16 shl eax, 4 movzx esi, word ptr [ebp].Client_Reg_Struc.Client_ESI add esi, eax ret align 4 Simulate_IOex endp if ?EXC10 ;--- detect exception 10h. This exception has NO error code. ;--- With VME enabled, it would be no problem, but since the VME bit ;--- in CR4 can be cleared by the user or other programs, Jemm cannot ;--- rely on that. So it checks: ;--- 1. if NE is set. No -> Int 10h ;--- 2. if ([CS:IP] != 9B) && ([CS:IP] != D8..DF) -> Int 10h ;--- 3. check for FP status word bit 7 set: ;--- (a. coprocessor available (00000410, bit 1=1)) ;--- b. FNSTSW AX, check bit 1, if 0 -> Int 10h ;--- ;--- Else it is an exception, and a dummy error code of 0 is pushed, ;--- which will make V86_Monitor recognize it as such. ;--- ;--- Jemm doesn't clear the FP status word by a FNINIT, so any FP instruction ;--- will continue to cause an exception 10h unless it is cleared. ;--- If the NE bit in CR0 isn't set, an IRQ 13 (interrupt 75h) is launched ;--- instead of exception 10h. Int10_Entry proc public push eax mov eax,cr0 test al,20h ;NE bit set? (usually it is 0) jnz maybeexc10 isint10: pop eax push 10h jmp V86_Monitor ;enter monitor as INT 10h maybeexc10: ;--- assuming entry from v86-mode! movzx eax, word ptr [esp+4].IRETDV86._Cs shl eax, 4 add eax, [esp+4].IRETDV86._Eip mov al,cs:[eax] cmp al,9Bh ;WAIT? jz @F and al,0F8h ;or a FPU opcode? cmp al,0D8h jnz isint10 ;no, then it's an Int 10h @@: fnstsw ax test al,80h ;a FPU error pending? jz isint10 ;if no, then it's an Int 10h pop eax push 0 ;push 0 as error code push 10h jmp V86_Monitor ;enter monitor as EXC 10h align 4 Int10_Entry endp endif ;--- copy physical memory ;--- esi=src, edi=dst, ecx=size ;--- addresses > 10FFFFh are regarded as physical addresses ;--- used by XMS block moves and Int 15h, ah=87h ;--- modifies eax, ebx, ecx, edx, esi, edi ;?MEMBORDER equ 110000h ?MEMBORDER equ 100000h MoveMemoryPhys proc public mov eax, ecx @@extmove_loop: mov ecx, eax cmp ecx, MAXBLOCKSIZE jb @F mov ecx, MAXBLOCKSIZE @@: sub eax, ecx push eax push esi push edi push ecx mov edx,[PageMapHeap] push edx push ecx ;--- get no of PTEs involved (max is 16+1) add ecx,1000h-1 ;round up size to page boundary shr ecx,12 ;bytes -> PTEs (10000h -> 10h) inc ecx ;add 1 to account for base not aligned on page boundary mov ch, cl cmp esi, ?MEMBORDER ;src in real-mode address space? jc @@src_is_shared mov eax, esi and esi, 0FFFh call MapPhysPages if ?PHYSDBG push eax @dprintf ?PHYSDBG, <"MoveMemoryPhys: %X pages (src) mapped at %X",10>, ch, eax pop eax endif add esi, eax mov cl, ch @@src_is_shared: cmp edi, ?MEMBORDER jc @@dst_is_shared mov eax, edi and edi, 0FFFh call MapPhysPages if ?PHYSDBG push eax @dprintf ?PHYSDBG, <"MoveMemoryPhys: %X pages (dst) mapped at %X",10>, ch, eax pop eax endif add edi, eax @@dst_is_shared: if ?INVLPG cmp [bNoInvlPg],0 jz @@flushdone endif mov eax, cr3 mov cr3, eax @@flushdone: pop ecx mov [PageMapHeap], edx ;update in case the monitor is reentered call MoveMemory pop [PageMapHeap] pop ecx pop edi pop esi pop eax add edi,ecx add esi,ecx and eax, eax jnz @@extmove_loop ret align 4 MoveMemoryPhys endp ?WT equ 0 ; std=0, 1=set WT bit in PTE for mem moves if ?WT ?PA equ PTF_PRESENT or PTF_RW or PTF_USER or PTF_PWT else ?PA equ PTF_PRESENT or PTF_RW or PTF_USER endif ;--- map physical pages in page map heap MapPhysPagesEx proc public mov edx, [PageMapHeap] MapPhysPagesEx endp ;fall thru ;--- map physical pages in linear address space ;--- in: eax = start of physical region to map ;--- edx -> free entry in page map heap ;--- cl = no of 4kB pages to map ;--- out: eax = linear address where the region has been mapped ;--- edx -> next free entry in page map heap ;--- modifies ebx, cl MapPhysPages proc public mov ebx, edx sub ebx, ?SYSBASE+?PAGETABSYS shl ebx, 10 add ebx, ?SYSBASE and ah, 0F0h mov al,?PA if ?INVLPG cmp [bNoInvlPg],0 jz @@setPTEs486 endif @@nextPTE1: mov [edx], eax add eax, 1000h add edx,4 dec cl jnz @@nextPTE1 mov eax, ebx ret align 4 if ?INVLPG @@setPTEs486: push ebx @@nextPTE2: mov [edx], eax .486p invlpg ds:[ebx] .386 add edx,4 add eax, 1000h add ebx, 1000h dec cl jnz @@nextPTE2 pop eax ret endif align 4 MapPhysPages endp if ?XMS35COMPAT ;--- in: AX:ESI src physical region to map ;--- DX:EDI dst physical region to map ;--- EBX: ptr into page directory where the 4MB pages will be mapped ;--- out: EBX=new Heap4M value ;--- usually 2 4MB pages are set in pagedir, either for src or for dst, ;--- but function is reentrant and also both src and dst may be sext. ;--- start of the mapping region is at end of system space F8000000-800000h ( see Heap4MB ) MapPhysPagesPSE proc uses ecx @dprintf ?I15DBG or ?MAPDBG, <"MapPhysPagesPSE, ax::esi=%X%08X, dx::edi=%X:%08X, ecx=%X",10>,ax,esi,dx,edi,ecx mov ecx, esi cmp ax,0 ; src beyond 4GB border? jnz @F cmp ecx,?MEMBORDER jc donesrc @@: movzx eax,ax shl eax,13 and ecx,0FFC00000h or eax, ecx mov al, PTF_PRESENT or PTF_4MB ;page flags for src: present + 4MB page mov ds:[ebx+0],eax add eax, 1 shl 22 jnc @F add eax, 1 shl 13 @@: mov ds:[ebx+4], eax and esi,3fffffh add esi,@LinearFromPDE(eax,ebx) ; convert PD offset to linear address .486p invlpg ds:[esi] invlpg ds:[esi+400000h] .386 lea ebx,[ebx-2*4] ; move backward 2*4MB donesrc: mov ecx, edi cmp dx,0 ; dst beyond 4GB border? jnz @F cmp ecx,?MEMBORDER jc donedst @@: movzx eax,dx shl eax,13 and ecx,0FFC00000h or eax, ecx mov al, PTF_PRESENT or PTF_RW or PTF_4MB ;page flags for dst: present + writable + 4MB page mov ds:[ebx+0],eax add eax, 1 shl 22 jnc @F add eax, 1 shl 13 @@: mov ds:[ebx+4], eax and edi,3fffffh mov eax,ebx sub eax,?PAGEDIR shl eax,20 add edi,eax .486p invlpg ds:[edi] invlpg ds:[edi+400000h] .386 lea ebx,[ebx-2*4] ; move forward 2*4MB donedst: @dprintf ?I15DBG or ?MAPDBG, <"MapPhysPagesPSE, esi=%X, edi=%X",10>, esi, edi ret align 4 MapPhysPagesPSE endp ;--- in: AX:ESI src physical region to map ;--- in: DX:EDI dst physical region to map ;--- in: ECX size ;--- the first region (src or dst) is mapped at F8000000h - (2*400000h + x) ;--- x is !=0 if both src and dst are sext mem or if jemm has been reentered while the move is running. MoveMemoryPhysEx proc public @dprintf ?I15DBG or ?MAPDBG, <"MoveMemoryPhysEx, ax::esi=%X%08X, dx::edi=%X%08X, ecx=%X",10>,ax,esi,dx,edi,ecx .586p mov ebx,cr4 bts ebx,4 jc move_loop ;set PSE only if it isn't set yet mov cr4,ebx .386 move_loop: push ecx cmp ecx, 400000h jb @F mov ecx, 400000h @@: push eax push edx push esi push edi mov ebx,[Heap4MB] push ebx call MapPhysPagesPSE mov [Heap4MB], ebx call MoveMemory pop [Heap4MB] pop edi pop esi pop edx pop eax pop ecx mov ebx, 400000h add edi, ebx adc dx,0 add esi, ebx adc ax,0 sub ecx, ebx ja move_loop ret MoveMemoryPhysEx endp endif ;--- copy memory block ESI to EDI, size ECX ;--- allow interrupts during move op MoveMemory proc public mov eax, [ebp].Client_Reg_Struc.Client_EFlags test ah,2 jz @@noenable call EnableInts sti @@noenable: mov al,cl shr ecx,2 and al,3 REP MOVSD mov cl,al REP MOVSB test ah,2 jz @@nodisable cli call DisableInts @@nodisable: ret align 4 MoveMemory endp ;--- allow interrupts in the monitor EnableInts proc public push [dwStackR0] mov [dwStackR0],esp jmp dword ptr [esp+4] align 4 EnableInts endp DisableInts proc public pop [esp+4] pop [dwStackR0] ret align 4 DisableInts endp Yield proc public test byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1,2 jz @@noints call EnableInts sti nop ;interrupts are enabled 1 instruction *after* STI cli call DisableInts @@noints: ret align 4 Yield endp ;--- simulate an RETF in v86-mode ;--- INP: EBP -> client struct ;--- modifies EAX,ECX,EDX ;--- v5.87: take care of SP wrap Simulate_Far_Ret proc public MOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS SHL ecx, 4 MOVZX eax, word ptr [ecx+edx] mov [EBP].Client_Reg_Struc.Client_EIP, eax add dx, 2 MOV ax, [ecx+edx] add dx, 2 mov [EBP].Client_Reg_Struc.Client_CS, eax mov word ptr [EBP].Client_Reg_Struc.Client_ESP, dx ; if ESP is to increase, adjust it last! ret align 4 Simulate_Far_Ret endp ;--- simulate a far call in v86-mode ;--- EBP -> client struct ;--- cx: new CS -> client_reg_struct.cs ;--- edx: new IP -> client_reg_struct.eip ;--- current v86 CS:IP is pushed onto the v86 stack ;--- modifies EAX, ECX, EDX ;--- v5.87: take care of SP wrap Simulate_Far_Call proc public movzx ecx, cx movzx edx, dx sub word ptr [EBP].Client_Reg_Struc.Client_ESP, sizeof RETFWS ; if ESP is to decrease, adjust it first! push ecx push edx MOV dx, word ptr [EBP].Client_Reg_Struc.Client_ESP MOV cx, word ptr [EBP].Client_Reg_Struc.Client_SS shl ecx, 4 mov eax, [EBP].Client_Reg_Struc.Client_EIP mov [ecx+edx], ax add dx, 2 mov eax, [EBP].Client_Reg_Struc.Client_CS mov [ecx+edx], ax pop [EBP].Client_Reg_Struc.Client_EIP pop [EBP].Client_Reg_Struc.Client_CS ret align 4 Simulate_Far_Call endp ;--- simulate an IRET in v86-mode ;--- INP: EBP -> Client_Reg_Struc ;--- modifies EAX,ECX,EDX ;--- v5.87: take care of SP wrap Simulate_Iret proc public MOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS SHL ecx, 4 movzx eax, word ptr [ecx+edx] mov [EBP].Client_Reg_Struc.Client_EIP, eax add dx, 2 mov ax, word ptr [ecx+edx] mov [EBP].Client_Reg_Struc.Client_CS, eax add dx, 2 mov ax, word ptr [ecx+edx] if 1 or ah, V86IOPL shl 4 ;to be safe, set IOPL=3 for v86 endif mov word ptr [EBP].Client_Reg_Struc.Client_EFlags, ax add dx, 2 mov word ptr [EBP].Client_Reg_Struc.Client_ESP, dx ; if ESP is to increase, adjust it last! ret align 4 Simulate_Iret endp ;--- simulate an V86 Int ;--- INP: EBP -> Client_Reg_Struc ;--- INP: EAX == INT # ;--- modifies EAX, ECX, EDX ;--- v5.87: take care of SP wrap Simulate_Int proc ;--- v5.86: check if a v86hook is installed and call it first mov edx, [pV86Hooks] cmp edx, 0 jz @F mov ecx, [edx+4*eax] jecxz @F call [dwHookProc] ;expects: eax=int#, ecx=hookproc, ebp->client regs jnc int_handled @@: sub word ptr [EBP].Client_Reg_Struc.Client_ESP, sizeof IRETWS; if ESP is to decrease, adjust it first! shl eax, 2 push dword ptr [eax] movzx edx, word ptr [EBP].Client_Reg_Struc.Client_ESP MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS ; get address of v86 SS:SP SHL ECX,4 MOV EAX,[EBP].Client_Reg_Struc.Client_EIP MOV [ecx+edx], ax ; set IP on the stack frame add dx, 2 MOV EAX,[EBP].Client_Reg_Struc.Client_CS MOV [ecx+edx], ax ; set CS on the stack frame pop eax ; get CS:IP from IVT MOV word ptr [EBP].Client_Reg_Struc.Client_EIP, AX shr eax, 16 MOV [EBP].Client_Reg_Struc.Client_CS, EAX add dx, 2 MOV EAX, [EBP].Client_Reg_Struc.Client_EFlags MOV [ecx+edx], AX ; set FL on the stack frame and AH, not (1+2) ; clear TF+IF; RF? mov [EBP].Client_Reg_Struc.Client_EFlags, eax int_handled: ret align 4 Simulate_Int endp ;--- prepare for nested execution. ;--- store current v86 cs:ip onto the v86 stack, and ;--- set current v86 cs:ip to the "back" bp. ;--- modifies EAX,ECX,EDX Begin_Nest_Exec proc public mov ecx, [dwRSeg] movzx edx, [bBpBack] jmp Simulate_Far_Call align 4 Begin_Nest_Exec endp ;--- pop the "back" bp from the client's stack End_Nest_Exec proc public call Simulate_Far_Ret ret align 4 End_Nest_Exec endp ;--- exec a v86 int immediately ;--- EBP -> Client_Reg_Struc ;--- EAX = INT# to execute Exec_Int proc call [vmm_service_table.pSimulate_Int] Exec_Int endp ;fall through ;--- Resume_Exec actually runs the v86 code Resume_Exec proc public push [dwStackCurr] pushad ; save PL0 GPRs mov [dwStackCurr],esp @v86popregX ; load ESP with EBP and restore v86 GPRs add ESP, CRS_SIZEX ; skip extra fields in Client_Reg_Struc IRETD ; and jump to v86 mode align 4 Resume_Exec endp ;--- this breakpoint is used to continue execution in protected-mode in a ;--- nested execution block after Resume_Exec V86_Back proc add esp,4 ;skip return address popad pop [dwStackCurr] ret align 4 V86_Back endp ;--- save client's state to EDI Save_Client_State proc push esi push edi mov ecx,size Client_Reg_Struc/4 mov esi, ebp rep movsd pop edi pop esi ret align 4 Save_Client_State endp ;--- restore client's state from ESI Restore_Client_State proc push esi push edi mov ecx,size Client_Reg_Struc/4 mov edi, ebp rep movsd pop edi pop esi ret align 4 Restore_Client_State endp if ?VME ;--- set/reset the VME flag in CR4 if supported ;--- INP: AL[0] new state of flag SetVME proc public test byte ptr [dwFeatures], 2 ;VME supported? jz @@novme .586p mov ecx, CR4 and al, 1 and cl, not 1 or cl,al mov CR4, ecx .386 @@novme: ret align 4 SetVME endp endif if ?I41SUPP Int41_Entry proc public iretd ; just don't route it to v86-mode align 4 Int41_Entry endp endif ;--- Int 15h handler ;--- to detect Ctrl-Alt-Del. ;--- it also hooks the BIOS A20 functions ; Int15_Entry PROC public ;--- probably good to check if coming from v86-mode? cmp ax,4F53h ;DEL pressed? jz @@isdel ife ?HOOK13 cmp ax,9101h ;diskette interrupt done? jz @@isfdirq endif if ?A20PORTS cmp ah,24h ;A20? jz @@isa20 endif @@v86mon: push 15h JMP V86_Monitor ;route interrupt to v86 if ?A20PORTS ;--- catch int 15h, ax=2400h and ax=2401h @@isa20: cmp al,2 jnb @@v86mon @dprintf ?A20DBG, <"Int 15h, ax=%X called",10>, ax if 0 call A20_Set ;al=0|1 mov ah,0 and [esp].IRETDV86._Efl, not 1 else mov ah,86h or [esp].IRETDV86._Efl, 1 endif iretd endif @@isdel: PUSH EAX MOV AL,CS:[@KB_FLAG] ; Have the keys CTRL & ALT AND AL,1100B ; been pressed ? CMP AL,1100B ; If not, continue working POP EAX JNZ @@v86mon sub esp, CRS_SIZEX ; space for fields between PUSHADS and IRETDV86 PUSHAD MOV EBP,ESP ; ebp -> Client_Reg_Struc push ss pop ds push ss pop es _SoftBoot:: mov esp,[dwStackCurr] cld call Begin_Nest_Exec mov eax,15h ; call the v86 int15 hookers call Exec_Int ; call End_Nest_Exec ; not needed ;--- possibly one should check if the carry flag has been cleared ;--- by one of the hookers. Then reboot should NOT be done. if 1 ;--- v5.75: int 15h, ah=4Fh usually is called during an IRQ. Is the PIC ;--- waiting for an EOI? Has the keyboard been disabled? and word ptr ds:[@KB_FLAG],not 01100001100b ;reset Ctrl+Alt status and byte ptr ds:[496h],not 1111b ;reset Ctrl,Alt,E0,E1 status mov al,0Bh ;get ISR of MPIC out 20h,al in al,20h test al,02 ;IRQ 1 happened? jz @F mov al,20h ;send EOI to PIC out 20h,al @@: XOR ECX,ECX @@: IN AL,64h ;wait until kbd buffer is free TEST AL,02 LOOPNZW @B MOV AL,0AEh ;enable keyboard OUT 64h,AL endif if ?FASTBOOT test [bV86Flags], V86F_FASTBOOT jnz fastboot endif jmp _Reboot ife ?HOOK13 @@isfdirq: btr word ptr ss:[bDiskIrq],0 ; copy out of DMA buffer required? jnc @@v86mon push ss pop ds push ss pop es call Dma_CopyBuffer jmp @@v86mon endif Int15_Entry ENDP if ?UNLOAD ;--- unload Jemm ;--- this is invoked by a v86 breakpoint called by Jemm16 ;--- EBP must be restored before exiting! ;--- the functions returns with a RETF to Jemm16, but stays ;--- in protected mode Unload proc call Simulate_Far_Ret if ?VME mov al,0 call SetVME endif if ?FREEXMS call Pool_FreeAllBlocks endif @dprintf ?UNLDBG, <"Unload: v86CS:EIP=%X:%X, v86SS:ESP=%X:%X",10>,\ [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP,\ [ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP ;--- set base of REAL_CODE_SEL and REAL_DATA_SEL ;--- to the current v86 CS/SS cld mov edi,offset V86GDT + REAL_CODE_SEL + 2 movzx eax, word ptr [ebp].Client_Reg_Struc.Client_CS shl eax, 4 stosw ;set base(0-15) shr eax, 16 stosb ;set base(16-23) add edi, sizeof DESCRIPTOR - 3 movzx eax, word ptr [ebp].Client_Reg_Struc.Client_SS shl eax, 4 stosw ;set base(0-15) shr eax, 16 stosb ;set base(16-23) ;--- restore v86 interrupt vectors xor ebx, ebx mov eax, [OldInt06] mov [ebx+06h*4],eax mov eax, [OldInt19] mov [ebx+19h*4],eax if ?VDS call VDS_Exit endif mov eax, [OldInt67] mov ds:[67h*4],eax mov edx, [ebp].Client_Reg_Struc.Client_EIP mov ecx, [ebp].Client_Reg_Struc.Client_ESP mov ebp, [ebp].Client_Reg_Struc.Client_EBP ife ?INTEGRATED mov bx, [XMSCtrlHandle] else mov bl, [A20Index] endif if 0 ; now done by Jemm16::UnloadJemm push 0 push word ptr 3FFh LIDT FWORD ptr [esp] ; reset IDT to real-mode endif MOV AX,REAL_DATA_SEL ; before returning, set the MOV DS,EAX ; segment register caches MOV ES,EAX MOV FS,EAX MOV GS,EAX MOV SS,EAX mov ESP,ECX ;--- the rest will be done by the 16bit part push REAL_CODE_SEL push edx retf Unload endp endif if ?FASTBOOT ;--- how is FASTBOOT implemented? ;--- the important thing is to restore the IVT to the values ;--- *before* DOS has been installed. This requires: ;--- a). DOS must hook int 19h and restore the vectors it has modified ;--- (vectors which count are 00-1F, 40-5F and 68-77). It must also ;--- save vector 15h (which is modified by himem.sys). ;--- b). the vectors must be saved at 0070:100h ;--- msdos saves at least 10,13,15,19,1B. ;--- c). jemm must be loaded as a device driver, so it is loaded very ;--- early before other drivers/tsrs. ;--- if these requirements are met, Jemm will do with FASTBOOT: ;--- 1. save int vectors 0-1F, 40-5F, 68-77 (20h+20h+10h = 50h*4=320 bytes) ;--- on init. ;--- 2. on ctrl-alt-del, restore these vectors, save the vector for int 19h ;--- which DOS has saved internally at 0070:0100+x and modify it to point ;--- to a breakpoint. ;--- 3. call v86-int 19h. DOS will restore the vectors it has saved. ;--- 4. Jemm regains control, with DOS already deactivated. Now clear ;--- vectors 20-3F, 60-67 and 78-FF, restore int 19h to the value saved ;--- previously. ;--- 5. jump to real-mode and do an int 19h again. restorevecs proc pushad xor eax, eax mov edi, eax mov esi, [pSavedVecs] push ds pop es @dprintf ?RBTDBG, <"restoring vectors 00-1F, 40-5F, 68-77",10> mov ecx, 20h rep movsd ;set 00-1F add edi, 20h*4 mov cl, 20h rep movsd ;set 40-5F add edi, 8*4 mov cl, 10h rep movsd ;set 68-77 ;--- search the int 19h vector stored at 0070:100h mov esi,700h+100h mov cl,5 @@nextitem: lodsb mov bl,al lodsd cmp bl,19h loopnz @@nextitem stc jnz @@norestore ;not found. no FASTBOOT possible mov edx, [dwRSeg] shl edx, 16 mov dl, [bBpTab] ;use the first BP for returning to the monitor @dprintf ?RBTDBG, <"vector 19h saved by DOS=%X, temp vector=%X",10>, eax, edx mov [OldInt19],eax ;save the vector stored by DOS mov [esi-4],edx ;--- restoring the XBDA should be done by DOS - MS-DOS does, but ;--- only if the XBDA size won't exceed 1? kB! ;--- Problem: size of XBDA (byte at offset 0) may be invalid. if 0;?MOVEXBDA movzx esi,word ptr ds:[@XBDA] cmp si, 0A000h jb @@noxbda shl esi, 4 movzx eax,byte ptr [esi];first byte of XBDA is (rather:should be) size in KB sub ds:[@MEM_SIZE],ax ;restore size of low memory movzx edi,word ptr ds:[@MEM_SIZE] shl edi, 6 mov ds:[@XBDA],di shl edi, 4 @dprintf ?RBTDBG, <"restoring XBDA to %X",10>, di mov ecx, eax shl ecx,8 rep movsd @@noxbda: endif if 0 in al,0A1h or al,03Fh out 0A1h,al in al,021h or al,0F8h out 21h,al endif clc @@norestore: popad ret restorevecs endp fastboot proc @dprintf ?RBTDBG, <"fastboot reached",10> if 0 ;kbd enable has been done in int 15h handler already mov al,0Bh out 20h,al in al,20h and al,al jz @@fb_1 mov al,20h ; send EOI to master PIC (for keyboard) out 20h,al @@fb_1: mov al,0AEh ; (re)enable keyboard out 64h,al endif call Begin_Nest_Exec if 1 @dprintf ?RBTDBG, <"resetting PS/2 mouse",10> mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0C201h ;reset PS/2 mouse mov eax,15h call Exec_Int endif @dprintf ?RBTDBG, <"calling restorevecs",10> call restorevecs jc _Reboot if ?ADDCONV ;--- if base memory has been extended beyond A000h, it must be ;--- restored. cmp word ptr ds:[@MEM_SIZE], 640 jbe @F cmp UMBsegments.wSegm, 0A000h jnz @F mov word ptr ds:[@MEM_SIZE], 640 @@: endif ;--- (re)launch int 19h. this will make DOS restore vectors. ;--- since the int 19h value saved by DOS has been modified by Jemm, ;--- execution will reach fastboot_1 at last. @dprintf ?RBTDBG, <"fastboot: launching Int 19h",10> ;--- it's bptable.pInt06 that has to be modified - ;--- because that's the FIRST breakpoint ( see restorevecs ). mov ds:[bptable.pInt06], offset fastboot_1 mov eax,19h call Exec_Int fastboot_1: ;--- now DOS has restored its vectors ;--- restore the previously modified int 19h vector @dprintf ?RBTDBG, <"reached fastboot_1",10> mov eax,[OldInt19] mov ds:[19h*4],eax if 0 mov dword ptr ds:[1Eh*4],0F000EFC7h ;restore int 1Eh to original value endif ;--- the v86 space is now "without" DOS ;--- set vectors 20-3F, 60-67 and 78-FF to 0000:0000 push ds pop es mov edi,20h*4 mov ecx,20h xor eax,eax rep stosd ;20-3F add edi,20h*4 mov cl,8 rep stosd ;60-67 add edi,10h*4 mov cl,8 rep stosd ;78-7F if 0;?CLEARBOOTFLGS movzx ecx, word ptr ds:[@XBDA] jecxz @F shl ecx, 4 mov word ptr [ecx+90h],0 ;clear boot flags (can't do this, probably BIOS specific) @@: endif if ?RBTDBG mov ecx,20h xor esi,esi xor ebx,ebx nextitem: lodsd @dprintf ?RBTDBG, <"%X: %X ">, bl, eax test cl,1 jz @F @dprintf ?RBTDBG, <10> @@: inc ebx loop nextitem endif mov bl,1 ;set flag for fastboot jmp Reboot_1 fastboot endp endif ;--- Reboot - reboot the machine _Reboot proc @dprintf ?RBTDBG, <"Reboot enter",10> if ?INT19RBT mov eax,19h call Exec_Int ;may not return endif Int19_V86Entry:: if ?FASTBOOT mov bl,0 ;bit 0: 0="full" reboot, 1=fastboot endif mov eax, DEVICE_REBOOT_NOTIFY call vmm_service_table.pControlProc test [bV86Flags], V86F_ALTBOOT jz Reboot_1 MOV WORD PTR DS:[@RESET_FLAG],1234H ; 1234h=warm boot mov al,0FEh out 64h,al ;reset via KBC if 0 in al,92h ;reset via PS/2 port A or al,1 out 92h,al endif ; hlt Reboot_1:: ;<--- entry from FASTBOOT (see above) @dprintf ?RBTDBG, <"Reboot_1 reached, int 19=%X, XBDA segment=%X",10>, dword ptr ds:[19h*4], word ptr ds:[@XBDA] MOV WORD PTR DS:[@RESET_FLAG],1234H ; 1234h=warm boot if 1 MOV AL,0Fh ;set shutdown byte; bit 7: 0=enable NMI out 70h,al MOV AL,0 ;software-reset out 71h,al endif ife ?USETRIPLEFAULT mov edi, 7E00h mov esi, offset rmcode mov ecx, offset endofrmcode - offset rmcode push ds pop es rep movsb if ?RESETCR4 if 0 cmp [dwFeatures],ecx ; does CR4 exist? jz @F .586p ; CR4 exists since Pentium ( not exactly true ) mov cr4, ecx ; reset CR4 .386 @@: else mov al, 0 call SetVME ; just reset CR4.VME xor ecx, ecx endif endif if ?FASTBOOT test bl,1 jz @F sub edi, 5 ;skip the jmp f000:fff0 mov esi, offset rmcode2 mov cl, offset endofrmcode2 - offset rmcode2 rep movsb and byte ptr ds:[47Bh],not 20h ;reset VDS bit mov eax, DEVICE_REBOOT_NOTIFY call vmm_service_table.pControlProc @@: endif xor edx, edx push edx push word ptr 3FFh .386p LIDT FWORD ptr [esp] ; reset the IDT to real-mode .386 MOV AX,REAL_DATA_SEL ; before returning to real-mode set the MOV DS,EAX ; segment register caches MOV ES,EAX MOV FS,EAX MOV GS,EAX MOV SS,EAX ; set SS:ESP MOV ESP,7C00h MOV ECX,CR0 ; prepare to reset CR0 PE and PG bits AND ECX,7FFFFFFEH XOR EAX, EAX db 66h ; jmp REAL_CODE_SEL:7E00h db 0eah ; (this sets CS attributes to 16bit Dw 7E00h ; and FFFF limit) dw REAL_CODE_SEL else xor edx,edx ; cause a triple fault to reboot push edx push edx lidt fword ptr [esp] int 3 endif rmcode: mov cr0,ecx ;7E00: 0F 22 C1 mov cr0, ecx (switches to real-mode) db 0EAh ;7E03: EA jmp 0000:7E08 (set CS=0000) dw 07E08h, 0000h ; 0000:7E08 mov cr3,eax ;7E08: 0F 22 D8 mov cr3, eax mov ss,eax ;7E0B: 8E D0 mov ss, ax (in 16-bit; set SS=0000) db 0EAh ;7E0D: EA jmp F000:FFF0 dw 0FFF0h, 0F000h ; F000:FFF0 endofrmcode: if ?FASTBOOT rmcode2: if ?RBTDBG db 0B8h, 52h, 0Eh ;mov ax,0e52h 'R' db 0BBh, 07h, 00h ;mov bx,0007h int 10h mov al,'B' int 10h mov al,'T' int 10h mov ah,0 int 16h endif if 0 push cs pop ds push cs pop es sti mov ah,00 mov dl,80h int 13h ;disk reset db 0B8h, 01h, 02h ;mov ax,0201h db 0B9h, 01h, 00h ;mov cx,0001h db 0BAh, 80h, 00h ;mov dx,0080h db 0BBh, 00h, 7Ch ;mov bx,7C00h int 13h ;read sector 1 of HD 0 push es push ebx ;in 16-bit: push bx retf ;jump to boot code else int 19h endif endofrmcode2: endif _Reboot endp align 4 if ?SERVTABLE vmm_service_table label VMM_SERV_TABLE dd Simulate_Int dd Simulate_Iret dd Simulate_Far_Call dd Simulate_Far_Ret dd Begin_Nest_Exec dd Exec_Int dd Resume_Exec dd End_Nest_Exec dd Simulate_IO dd Yield dd VDS_Call_Table dd VCPI_Call_Table dd IO_Trap_Table ; dd V86_Monitor ;v5.87: obsolete ; dd offset dwStackCurr ;v5.87: obsolete dd MoveMemory dd offset pV86Hooks ;v5.85 dd offset V86_Fault ;v5.86 dd offset _ret ;v5.86: control proc endif .text$03 ends END _start ================================================ FILE: src/JEMM32.INC ================================================ ;--- these files contains things for 32-bit ONLY ?RING0EXC EQU 1 ; 1=display more infos on exc in ring 0 ?ROMRO EQU 1 ; 1=make ROM page FF000 r/o ;?ALTBOOT EQU 1 ; 1=support ALTBOOT option ?MMASK EQU 1 ; 1=trap DMA master mask port (0F, DE) ?INVLPG EQU 1 ; 1=use INVLPG opcode on 80486+ ?CLEARHIGHESP EQU 1 ; 1=clear hiword(esp) for buggy VCPI clients ifndef ?AUXIO ?AUXIO EQU 0 ; 1=use COMx for I/O, 0=use INT 10h/29h and INT 16h endif ?USEINT10 EQU 1 ; 1=use int 10h for output, 0=use int 29h ?SERVTABLE equ 1 ; 1=expose service table (for JLoad) ?FASTMON equ 1 ; 1=faster monitor jmp for int 00-0F ?SHAREDGDT equ 1 ; 1=place GDT in shared address space ?EXC10 equ 1 ; 1=detect exception 10h (floating point) ?I41SUPP equ 1 ; 1=support Int 41h in protected-mode ?ALT_TRAPHDL equ 1 ; v5.85: 1=call alternate trap handler for system ports ifndef ?SHAREDIDT ?SHAREDIDT equ 0 ; std=?, 1=place IDT in shared address space endif ?CODEIN2PAGE EQU 0 ; 1=move code away from first page ?USETRIPLEFAULT EQU 0 ; 1=use triple fault to reboot ;ifndef ?SAFEKBD ; v5.87: obsolete ;?SAFEKBD equ 0 ; 1=use original vector for int 16h kbd poll ;endif ?DYNTRAPP60 equ 1 ; 1=trap port 60h conditionally only ;ifndef ?MAPEXTMEM ; v5.86: obsolete; now covered by SB option ;?MAPEXTMEM equ 0 ; 1="identity map" extended memory (SBEINIT.COM/386SWAT may need this) ;endif GDT_SIZE equ 200h ; size of GDT include jsystem.inc ;--- the following equates define Jemm's memory layout: ;--- Jemm starts at 110000h with data and code (4-5 pages) ;--- system space is F8000000h-F83FFFFFh: ;--- + 0000h: reserved (uncommitted) ;--- + 1000h: page directory ;--- + 2000h: page table 0 ;--- + 3000h: system page table ;--- + 4000h: ring 0 stack (4k) ;--- + 5000h: task state segment ;--- + a: page pool descriptors, EMS handle/status tables ;--- + b: shadowed HMA (if XMS handle array is in HMA) ;--- + c: DMA buffer ;--- + d: scratch region for physical page mapping ?PAGETAB0 EQU 2000h ; rel. offset (?SYSBASE) for page table 0 ?TSSLEN EQU 68h+32+2000h+8 ; size of TSS "segment" ?HLPSTKSIZE EQU 80h ; size of help stack for VCPI ;--- other constants if ?DMAPT or ?VDS ?DMABUFFMAX EQU 128 ; max DMA-Buffer size in kB endif ?FASTENTRIES equ 32-1 include x86.inc include xms.inc ;--- selector definitions ;--- FLAT_CODE_SEL must be first descriptor in GDT FLAT_CODE_SEL equ 1 * sizeof DESCRIPTOR FLAT_DATA_SEL equ 2 * sizeof DESCRIPTOR V86_TSS_SEL equ 3 * sizeof DESCRIPTOR REAL_CODE_SEL equ 4 * sizeof DESCRIPTOR REAL_DATA_SEL equ 5 * sizeof DESCRIPTOR ;must follow REAL_CODE_SEL if ?CLEARHIGHESP V86_STACK_SEL equ 6 * sizeof DESCRIPTOR endif ;--- BIOS constants @KB_FLAG EQU 417H ; Status SHIFT/CTRL/ALT etcetera. @RESET_FLAG EQU 472H ; Flag for Warmboot (=1234h) ;--- EMS constants EMS_MAX_HANDLES EQU 255 ; std=255, the EMS handle is a byte value ;EMS_MAX_PAGES_ALLOWED EQU 2048; std=2048, 2048 * 16kB = 32768 kB (32 MB) EMS_MAXSTATE EQU 64 ; std=64, number of EMS save state items ;--- EMS handle descriptor EMSHD struct ehd_wIdx dw ? ;index of EMSPD for first page ehd_bFlags db ? ehd_bSS db ? ;saved state index EMSHD ends ;--- values for flags EMSH_USED equ 1 EMSSTAT struct dPg0 dd ? dPg1 dd ? dPg2 dd ? dPg3 dd ? EMSSTAT ends ;--- flags for pool descriptor block PBF_DONTEXPAND EQU 1 ; don't try to expand this block PBF_DONTFREE EQU 2 ; don't try to free this block ;--- equates from JLM.INC ;--- OUT_INSTR, WORD_IO and DWORD_IO are used as offset in tables ;--- and hence cannot be changed; see DMA.ASM! OUT_INSTR equ 04h WORD_IO equ 08h DWORD_IO equ 10h STRING_IO EQU 20h REP_IO EQU 40h DMAREQ struct bMode db ? ;saved "mode" value (register 0Bh or D6h) bFlags db ? ;flags (DMAF_ENABLED) BlockLen dw ? ;saved "block length" value BaseAdr dw ? ;saved "block address" value PageReg db ? ;saved "page register" value cDisable db ? ;is automatic translation disabled? (VDS) DMAREQ ends BPTABLE struct pInt06 dd ? pInt19 dd ? ;this BP will be used by JLOAD and become "dynamic" pInt67 dd ? if ?VDS pInt4B dd ? endif pBack dd ? pI1587 dd ? if ?DMAPT p1340 dd ? endif pXMS dd ? pStrat dd ? pDev dd ? if ?UNLOAD pUnload dd ? endif BPTABLE ends ;--- client register structure in V86 monitor. ;--- consists of: ;--- 1. PUSHAD ;--- 2. Int# ;--- 3. v86-exception frame with error code Client_Reg_Struc struct Client_EDI dd ? ;+0 Client_ESI dd ? ;+4 Client_EBP dd ? ;+8 Client_res0 dd ? ;+12 Client_EBX dd ? ;+16 Client_EDX dd ? ;+20 Client_ECX dd ? ;+24 Client_EAX dd ? ;+28 Client_Int dd ? ;+32 ; pushed as sign-extended byte - so use lowest byte only! Client_Error dd ? ;+36 Client_EIP dd ? ;+40 Client_CS dd ? ;+44 Client_EFlags dd ? ;+48 Client_ESP dd ? ;+52 Client_SS dd ? ;+56 Client_ES dd ? ;+60 Client_DS dd ? ;+64 Client_FS dd ? ;+68 Client_GS dd ? ;+72 Client_Reg_Struc ends ;--- extra bytes in Client_Reg_Struc if PUSHAD and IRETDV86 are removed CRS_SIZEX equ (sizeof Client_Reg_Struc - sizeof PUSHADS - sizeof IRETDV86) ;--- entry in IO trap table ;--- this is for the default port trap handler; ;--- it handles byte ports only. IOTRAPENTRY struct bStart db ? bEnd db ? dwProc dd ? IOTRAPENTRY ends PAGEMEM struct dwMaxMem4K DD ? ; max 4k pages allowed dwTotalMem4K DD ? ; total 4k pool pages dwUsedMem4K DD ? ; used 4k pool pages PAGEMEM ends DMABUFF struct DMABuffStart DD ? ; start of DMA-Buffer (linear address) DMABuffSize DD ? ; max size of the DMA-Buffer in bytes DMABuffStartPhys DD ? ; start of DMA-Buffer (physical address) DMABuffFree DD ?DMABUFFMAX/32 dup (?) ; bits for DMA buffer handling DB ? ; space for 2 additional bits is needed! DMABUFF ends ;--- Jemm Service Table ;--- this structure is filled and returned by ;--- IoCtl call 8. Used by JLoad. if ?SERVTABLE VMM_SERV_TABLE struct pSimulate_Int dd ? pSimulate_Iret dd ? pSimulate_Far_Call dd ? pSimulate_Far_Ret dd ? pBegin_Nest_Exec dd ? pExec_Int dd ? pResume_Exec dd ? pEnd_Nest_Exec dd ? pSimulate_IO dd ? pYield dd ? pVDS_Call_Table dd ? pVCPI_Call_Table dd ? pIO_Trap_Table dd ? ;pV86_Monitor dd ? ; linear address of V86_Monitor() (v5.87: obsolete with ppV86Hooks) ;pStackCurr dd ? ; linear address of dwStackCurr (v5.87: obsolete with ppV86Hooks) pMoveMemory dd ? ; linear address of MoveMemory() ppV86Hooks dd ? ; v5.85: address of pV86Hooks variable pV86Faults dd ? ; v5.86: address of V86Faults pControlProc dd ? ; v5.86: address of ControlProc (called by Jemm on certain events) VMM_SERV_TABLE ends endif ;--- messages for ControlProc callout (matches Win9x VMM values) SYSTEM_EXIT equ 5 DEVICE_REBOOT_NOTIFY equ 17h ;--- macros ;--- return a pointer to PTEs ;--- ofs: offset in page dir/page table 0/sys page table @GetPTEAddr macro ofs:req exitm <[?SYSBASE+ofs]> endm ;--- macro to convert a pointer to PTEs in system page table ;--- to the according linear address @LinearFromSysPTE macro reg, pPT ifdif , mov reg, pPT endif sub reg, ?SYSBASE+?PAGETABSYS shl reg, 10 add reg, ?SYSBASE endm ;--- macro to convert a pointer to PDEs to linear address @LinearFromPDE macro reg, pPD ifdif , mov reg, pPD endif sub reg, ?PAGEDIR shl reg, 20 exitm endm ================================================ FILE: src/POOL.ASM ================================================ ;--- Jemm's memory pool implementation ;--- originally written by Michael Devore ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ;--- equates MINXMSSIZE equ 16 ; ignore XMS blocks smaller 16 kB ;--- assembly time constants if ?INTEGRATED ?FREEXMS equ 1 ; std=1; 1=free XMS blocks if jemm is unloaded; 0 might work ?EXPANDFIRST equ 0 ; std=0, 1 might work else ?FREEXMS equ 1 ; std=1; 0 won't work ?EXPANDFIRST equ 0 ; std=0, 1 won't work endif ;--- publics/externals externdef EMS_CheckMax:near include extern32.inc ;--- Memory pool descriptor. ;--- the pool descriptor array can grow up to 174784 bytes (64*2731) for 4 GB ;--- standard is 80*(16+48) = 5120 bytes for 120 MB ; number of bytes for allocation in a pool allocation descriptor ; that are 48*8=384 bits, -> 384 * 4 kB -> 1536 kB POOLDESC_ALLOCATION_SPACE EQU 48 POOL_DESC struct dwAddressK DD ? ; phys. base address in K (may not be XMS handle base if handle size changed later) pXMSdesc DD ? ; pointer to XMS handle descriptor array entry/pseudo-handle value w4kfree DW ? ; free number of 4K slots (>b16kfree*4 if any partials) b16kfree DB ? ; free number of 16K slots b16kmax DB ? ; maximum number of 16K allocations (used allocation bytes*2) bStartAdj DB ? ; unused K from XMS handle base for 4K alignment (0-3) bEndAdj DB ? ; unused K at XMS handle end as 16K chunks (0-15) bFlags DB ? ; various flag values DB ? ; alignment bits DB POOLDESC_ALLOCATION_SPACE dup (?) POOL_DESC ends ; size a pool descriptor will manage (in kB): 48*8*4 = 1536 POOLDESC_MEMSIZE equ POOLDESC_ALLOCATION_SPACE*8*4 .text$01 SEGMENT pmem PAGEMEM <0,0,0> PoolAllocationTable DD 0 ; start of pool descriptor array PoolAllocationEnd DD 0 ; current end of descriptor array PoolAllocationMax DD 0 ; max end of descriptor array (default: 80 entries = 120 MB) LastBlockAllocated DD 0 ; last pool desc used for alloc LastBlockFreed DD 0 ; last pool desc used for free XMSPoolBlockCount DD 0 ; count of XMS handles allocated for pool descs (not counting initial UMB block) .text$01 ends .text$03 segment ; Pool memory management routines ;--- convert desc index + nibble offset to a physical address ;--- ecx=desc index ;--- eax=nibble offset ;--- out: physical address in EAX Pool_GetPhysAddr proc public .errnz sizeof POOL_DESC - 64 ;--- if sizeof POOL_DESC changes, next line must be adjusted! shl ecx,6 ; convert to 64-byte desc offset shl eax,14 ; 16K to bytes add ecx, [PoolAllocationTable] ; ecx -> pool desc for page mov ecx,[ecx].POOL_DESC.dwAddressK shl ecx,10 ; K address to bytes add eax,ecx ; = physical memory address of page ret align 4 Pool_GetPhysAddr endp ; get free 4K page count in pool ; out: EAX=free pages ; destroys EDX ; do not write segment registers here, ; the function is called from VCPI protected mode API Pool_GetFree4KPages PROC public call Pool_GetFreeXMSPages ; get free 4k XMS pages in EDX mov eax,[pmem.dwTotalMem4K] sub eax,[pmem.dwUsedMem4K] ; eax = free 4k pages in Pool add edx,eax ; edx = true free 4k pages mov eax,[pmem.dwMaxMem4K] sub eax,[pmem.dwUsedMem4K] ; eax = max free 4k pages allowed cmp eax,edx jb @@uselower mov eax,edx @@uselower: ; eax = min(eax, edx) @dprintf ?POOLDBG, <"Pool_GetFree4KPages: %X, dwUsedMem4K=%X, dwMaxMem4K=%X",10>, eax, pmem.dwUsedMem4K, pmem.dwMaxMem4K ret align 4 Pool_GetFree4KPages ENDP ; get free 16K (EMS) page count in pool ; out: EAX = free 16kB pages in pool ; other registers preserved ; the value returned is not just the total of ; the current pool descriptors. ; it is ; total of free 16k pages in current pool ; + Min(free XMS pages, pmem.dwMaxMem4k - pmem.dwTotalMem)/4 Pool_GetFree16KPages PROC public push esi push ecx push edx ;--- get free 16kb pages in pool into EAX xor eax, eax mov ecx, eax mov esi, [PoolAllocationTable] nextitem: mov cl, [esi].POOL_DESC.b16kfree ; high words known zero add eax, ecx add esi, sizeof POOL_DESC cmp esi, [PoolAllocationEnd] jb nextitem ;--- get MIN(free XMS pages, pmem.dwMaxMem4k - dwTotalMem)/4 into ESI call Pool_GetFreeXMSPages ;return free 4k XMS pages in EDX mov esi, [pmem.dwMaxMem4K] sub esi, [pmem.dwTotalMem4K] jnc @@valueok xor esi, esi @@valueok: cmp esi, edx jb @@issmaller mov esi, edx @@issmaller: shr esi, 2 add eax, esi @dprintf ?POOLDBG, <"Pool_GetFree16KPages=%X max4k=%X total4k=%X XMS-4k=%X",10>, eax, [pmem.dwMaxMem4K], [pmem.dwTotalMem4K], edx pop edx pop ecx pop esi ret align 4 Pool_GetFree16KPages ENDP ; locate any adjacent free XMS block to current pool allocation desc; ; if found, try consuming 32K of it for pool allocation block; ; the adjacent XMS block must be at the end of current pool block, ; since the pool block base cannot be changed once set. ; pool block base+block size == owner XMS handle base+handle size (end match end) ; ends must match since you can't span noncontiguous sub-blocks of an owner XMS ; block with a single EMS/VCPI pool allocation block. ; INP: ESI -> Pool block to expand (and is ensured to be expandable) ; OUT: NC if success, C if fail ; all registers preserved Pool_ExpandBlock PROC pushad @dprintf ?POOLDBG, <"Pool_ExpandBlock: esi=%X, addrK=%X, pArray=%X",10>, esi, [esi].POOL_DESC.dwAddressK, [esi].POOL_DESC.pXMSdesc mov edi,[esi].POOL_DESC.pXMSdesc if ?EXPANDFIRST add edi,[dwRes] endif mov ebx,[edi].XMS_HANDLE.xh_baseK add ebx,[edi].XMS_HANDLE.xh_sizeK ; ebx -> end of current pool desc owner XMS ; see if owner XMS for pool allocation desc end matches ; end of pool allocation block movzx ecx,[esi].POOL_DESC.bStartAdj mov eax,[esi].POOL_DESC.dwAddressK sub eax,ecx ; true XMS start when pool allocation desc created mov cl,[esi].POOL_DESC.bEndAdj add eax,ecx ; true XMS end when desc created mov cl,[esi].POOL_DESC.b16kmax shl ecx,4 ; convert to K add eax,ecx cmp eax,ebx jne @@locfail ; owner XMS end no longer matches initial pool desc owner XMS end movzx ecx, [XMS_Handle_Table.xht_numhandles] mov edx, [XMS_Handle_Table.xht_pArray] ; edx -> test XMS block movzx eax, [XMS_Handle_Table.xht_sizeof] @@hanloop: cmp ebx,[edx].XMS_HANDLE.xh_baseK ; see if test block immediately follows current block je @@found add edx,eax ; move to next handle descriptor dec ecx jne @@hanloop @@locfail: popad stc ; flag failure ret @@found: test [edx].XMS_HANDLE.xh_flags,XMSF_FREE ; if block is not free, abort scan je @@locfail movzx eax,[esi].POOL_DESC.bEndAdj add eax,[edx].XMS_HANDLE.xh_sizeK cmp eax,32 ; large enough? jb @@locfail ; transfer 32K of following block to current block - unused end K in current mov eax,32 movzx ecx,[esi].POOL_DESC.bEndAdj sub eax,ecx ; adjust amount to change preceding block add [edx].XMS_HANDLE.xh_baseK,eax ; move changed block address ahead sub [edx].XMS_HANDLE.xh_sizeK,eax ; and adjust size mov edi,[esi].POOL_DESC.pXMSdesc if ?EXPANDFIRST add edi,[dwRes] endif add [edi].XMS_HANDLE.xh_sizeK,eax ; increase pool associated XMS block size mov [esi].POOL_DESC.bEndAdj,0 ; no end overlap add [esi].POOL_DESC.b16kmax,2 ; adjust allocation tracking bytes add [esi].POOL_DESC.b16kfree,2 add [esi].POOL_DESC.w4kfree,2*4 add [pmem.dwTotalMem4K],2*4 ;--- zero tracking allocation byte (not really needed) if 0 movzx eax,[esi].POOL_DESC.b16kmax shr eax,1 ; byte offset in allocation space (32K/byte) mov BYTE PTR [esi].POOL_DESC.bits[eax-1],0 endif ; see if changed contiguous XMS block size went to <16K, ; if so, transfer any remainder to pool block and zero XMS block mov eax,[edx].XMS_HANDLE.xh_sizeK cmp eax,MINXMSSIZE jae @@loc2 mov [esi].POOL_DESC.bEndAdj,al xor eax,eax mov [edx].XMS_HANDLE.xh_baseK,eax ;required for FD Himem mov [edx].XMS_HANDLE.xh_sizeK,eax ; mov [edx].XMS_HANDLE.xh_locks,al mov [edx].XMS_HANDLE.xh_flags,XMSF_INPOOL ; flag: free handle! @@loc2: popad clc ; flag success ret align 4 Pool_ExpandBlock ENDP ; expand any available allocation pool desc by minimum amount, if possible ; return NC on success, then ESI=desc which has been expanded ; return C on failure ; other registers preserved ; called by VCPI, do not write segment registers here! Pool_ExpandAnyBlock PROC cmp [bNoPool],0 ; dynamic memory allocation on? jne fail mov esi, [PoolAllocationTable] nextitem: cmp [esi].POOL_DESC.dwAddressK,0 ; unused/deallocated desc? je skipitem cmp [esi].POOL_DESC.b16kmax, 2 * POOLDESC_ALLOCATION_SPACE - 1 ;32k still free? jae skipitem ; current block is full test [esi].POOL_DESC.bFlags,PBF_DONTEXPAND jne skipitem ; can't expand this block call Pool_ExpandBlock jnc done skipitem: add esi, sizeof POOL_DESC cmp esi, [PoolAllocationEnd] jb nextitem fail: stc ; failure done: ret align 4 Pool_ExpandAnyBlock ENDP ; find and allocate free 4K (VCPI) block in pool blocks ; return NC and EAX == physical address ; C if none found ; modifies ECX, ESI, EDI ; do not write segment registers here! this function is ; called by VCPI protected-mode API Pool_Allocate4KPage PROC public ; first try last block allocated, to avoid searching full blocks if possible xor eax,eax mov esi, [LastBlockAllocated] or esi,esi je @@nolastalloc cmp [esi].POOL_DESC.w4kfree,ax jne @@searchbytes ; try last freed chunk @@nolastalloc: mov esi, [LastBlockFreed] or esi,esi je @@nolastfreed cmp [esi].POOL_DESC.w4kfree,ax jne @@searchbytes @@nolastfreed: mov esi, [PoolAllocationTable] nextitem: cmp [esi].POOL_DESC.w4kfree,ax jne @@searchbytes @@nextblock: add esi, sizeof POOL_DESC cmp esi, [PoolAllocationEnd] jb nextitem call Pool_ExpandAnyBlock ; try to expand a pool block jnc @@searchbytes ; if NC, ESI -> expanded block call Pool_AllocateEMBForPool jnc @@nolastfreed ret @@searchbytes: movzx ecx,[esi].POOL_DESC.b16kmax shr ecx,1 ; count of allocation bytes in block adc ecx,0 ; don't forget the last nibble lea edi,[esi].POOL_DESC.bits mov al,-1 repz scasb jz @@blockbad ; jump should never happen dec edi mov al,[edi] mov cl,al xor al,-1 bsf eax,eax ; find the first '1' bit scanning from right to left bts dword ptr [edi],eax ; flag page as 'used' cmp eax,4 jc @@islownyb shr cl,4 @@islownyb: and cl,0Fh setz cl ; cl will be 1 if low/high nibble became <> 0 sub [esi].POOL_DESC.b16kfree, cl if ?POOLDBG jnc @@blockok @dprintf ?POOLDBG, <"Pool_Allocate4kPage: 16k-free count underflow for block %X",10>, esi @@blockok: endif dec [esi].POOL_DESC.w4kfree mov [LastBlockAllocated],esi ; update "last block allocated from" inc [pmem.dwUsedMem4K] sub edi,esi sub edi,POOL_DESC.bits ; get "byte offset" into EDI shl edi,15 ; each byte covers 32k shl eax,12 ; each bit covers 4k add eax,edi ; compute base address of block addressed by byte mov ecx,[esi].POOL_DESC.dwAddressK shl ecx,10 ; convert from K to bytes add eax,ecx @dprintf ?POOLDBG, <"Pool_Allocate4kPage ok, page=%X, block=%X",10>, eax, esi ret @@blockbad: @dprintf ?POOLDBG, <"Pool_Allocate4kPage: found inconsistent block %X",10>, esi @CheckBlockIntegrity; nothing free, although block indicated there was jmp @@nextblock ; continue search align 4 Pool_Allocate4KPage ENDP ; allocate a 16K page (for EMS) ; out: NC if ok, ; eax == index for descriptor entry ; edx == nibble offset in descriptor ; C on errors ; destroy no other registers Pool_Allocate16KPage PROC public push ecx push esi push edi ; first try last block allocated, to avoid searching full blocks if possible xor edx,edx mov esi, [LastBlockAllocated] or esi,esi je @@nolastalloc cmp [esi].POOL_DESC.b16kfree,dl jne @@searchbytes ; try last freed chunk @@nolastalloc: mov esi, [LastBlockFreed] or esi,esi je @@nolastfreed cmp [esi].POOL_DESC.b16kfree,dl jne @@searchbytes @@nolastfreed: mov esi, [PoolAllocationTable] nextitem: cmp [esi].POOL_DESC.b16kfree,dl jne @@searchbytes @@nextblock: add esi, sizeof POOL_DESC cmp esi, [PoolAllocationEnd] jb nextitem call Pool_ExpandAnyBlock ; expand a block jnc @@searchbytes ; if NC, esi -> expanded block call Pool_AllocateEMBForPool jnc @@nolastfreed jmp @@exit ; error: no 16k page free anymore @@searchbytes: movzx ecx,[esi].POOL_DESC.b16kmax shr ecx,1 ; count of allocation bytes in block adc ecx,0 xor edi,edi @@findbyteloop: mov al,[esi].POOL_DESC.bits[edi] xor al,-1 ; unallocated 4K areas show as bit set mov ah,al mov dl,0fh and al,dl cmp al,dl je @@lowfree ; low nybble unallocated, free 16K area mov dl,0f0h and ah,dl cmp ah,dl je @@highfree ; high nybble unallocated, free 16K area inc edi loop @@findbyteloop ; no free 16K area? should not happen. @CheckBlockIntegrity jmp @@nextblock @@highfree: stc @@lowfree: setc cl or [esi].POOL_DESC.bits[edi],dl dec [esi].POOL_DESC.b16kfree sub [esi].POOL_DESC.w4kfree,4 jnc @@valid2 @CheckBlockIntegrity ; force valid value mov [esi].POOL_DESC.w4kfree,0 ; update ebx pointer @@valid2: add [pmem.dwUsedMem4K],4 ; update pool page counter mov [LastBlockAllocated],esi ; update last block allocated from mov eax,esi sub eax, [PoolAllocationTable] shr eax, 6 ; convert to an index mov edx,edi shl edx,1 or dl,cl ; subindex in dl, also clears Carry @@exit: pop edi pop esi pop ecx ret align 4 Pool_Allocate16KPage ENDP ;--- find pool descriptor for 4k page in EDI ;--- in: EDI = 1K address ;--- out: NC = ok, ESI=PD, EAX=block base ;--- C = failed ;--- destroys eax,ecx,esi Pool_FindBlock proc mov esi, [LastBlockFreed] or esi,esi je @@notlastfreed mov eax,[esi].POOL_DESC.dwAddressK or eax,eax je @@notlastfreed ; unused/deallocated block cmp edi,eax jb @@notlastfreed ; pool block starts after page movzx ecx,[esi].POOL_DESC.b16kmax shl ecx,4 ; convert 16K to 1K add ecx,eax ; ecx == end of block cmp edi,ecx jb @@rightblock ; block found? @@notlastfreed: mov esi, [LastBlockAllocated] or esi,esi je @@notlastalloc mov eax,[esi].POOL_DESC.dwAddressK or eax,eax je @@notlastalloc ; unused/deallocated block cmp edi,eax jb @@notlastalloc ; pool block starts after page movzx ecx,[esi].POOL_DESC.b16kmax shl ecx,4 ; convert 16K to 1K add ecx,eax ; ecx == end of block cmp edi,ecx jb @@rightblock ; block found? @@notlastalloc: mov esi, [PoolAllocationTable] nextitem: mov eax,[esi].POOL_DESC.dwAddressK or eax,eax je @@nextblock ; unused/deallocated block cmp edi,eax jb @@nextblock ; pool block starts after page movzx ecx,[esi].POOL_DESC.b16kmax shl ecx,4 ; convert 16K to 1K add ecx,eax ; ecx == end of block cmp edi,ecx jb @@rightblock ; block found? @@nextblock: add esi, sizeof POOL_DESC cmp esi, [PoolAllocationEnd] jb nextitem @dprintf ?POOLDBG, <"Pool_FindBlock: page %X not in pool",10>, edx stc ret @@rightblock: clc ret align 4 Pool_FindBlock endp ;--- release 4K page. ;--- in: EDX = 4K page physical address to free ;--- out: NC if ok, C if failed ;--- destroys eax,ecx,esi,edi Pool_Free4KPage PROC public mov edi,edx shr edi,10 ; convert bytes to K and edi,NOT 3 ; ensure 4K alignment ; edi == start of 4K page in K after alignment adjustment call Pool_FindBlock ; find descriptor jc @@fail1 ; error "page not in pool" ; the descriptor is in ESI sub edi,eax ; 4K offset from block base in K mov eax,edi shr eax,2 ; K to 4K page mov cl,al ; keep bit offset shr eax,3 ; 4K page to 32K byte offset and ecx,7 btr dword ptr [esi].POOL_DESC.bits[eax],ecx ; see if bit set (was allocated) jnc @@fail2 ; no inc [esi].POOL_DESC.w4kfree mov [LastBlockFreed],esi dec [pmem.dwUsedMem4K] ; check if this frees up a 16K chunk mov al,[esi].POOL_DESC.bits[eax] cmp cl,4 jc @@islow shr al,4 @@islow: test al,0Fh ; see if all bits of nybble cleared jne @@success ; no inc [esi].POOL_DESC.b16kfree call Pool_TryFreeToXMS ; free empty pool allocation block to XMS if appropriate @@success: @dprintf ?POOLDBG, <"Pool_Free4kPage ok, page=%X block=%X",10>, edx, esi clc @@fail1: ret @@fail2: @dprintf ?POOLDBG, <"Pool_Free4kPage: page %X was not allocated (block=%X)",10>, edx, esi stc ret align 4 Pool_Free4KPage ENDP ; in: EAX = descriptor index, ECX = nibble offset ; destroys eax, ecx Pool_Free16KPage PROC public push esi mov esi,eax .errnz sizeof POOL_DESC - 64 shl esi,6 ; convert 64-byte count to byte offset add esi, [PoolAllocationTable] ; esi -> pool allocation block mov ah,0Fh shr ecx,1 ; byte offset jnc @@islow mov ah,0F0h @@islow: mov al,[esi].POOL_DESC.bits[ecx] and al,ah ; mask out bits which dont interest cmp al,ah jne @@fail rol al,4 and [esi].POOL_DESC.bits[ecx],al ; reset all expected bits inc [esi].POOL_DESC.b16kfree add [esi].POOL_DESC.w4kfree,4 sub [pmem.dwUsedMem4K],4; update pool page counter mov [LastBlockFreed],esi call Pool_TryFreeToXMS ; free empty pool allocation block to XMS if appropriate clc @@ret: pop esi ret @@fail: @dprintf ?POOLDBG, <"Pool_Free16kPage failed page=%X, masks=%X",10>, edx, eax @CheckBlockIntegrity stc jmp @@ret align 4 Pool_Free16KPage ENDP ; find an unused Pool descriptor ; out: NC ok, EDX -> Pool block ; C failed - no unused descriptor found ; no other registers modified Pool_GetUnusedBlock PROC mov edx, [PoolAllocationTable] nextitem: cmp [edx].POOL_DESC.dwAddressK,0 ; unused/deallocated block? je @@found add edx, sizeof POOL_DESC cmp edx, [PoolAllocationMax] jb nextitem @dprintf ?POOLDBG, <"Pool_GetUnusedBlock failed, table start/end=%X/%X",10>, [PoolAllocationTable], [PoolAllocationMax] stc @@found: ret align 4 Pool_GetUnusedBlock ENDP ; prepare pool block for use ; upon entry: ; edx -> pool descriptor ; ecx == raw size in K before alignment (max 1536+3) ; edi == raw address in K before alignment ; esi == owner XMS handle pXMSdesc value, do NOT use XMS handle values for ; size and address since this call may be part of a multi-pool block span ; returns memory in 1kB assigned to this descriptor in EAX Pool_PrepareBlock PROC pushad @dprintf ?POOLDBG, <"Pool_PrepareBlock: size in kB=%X addr in kB=%X",10>, ecx, edi mov [edx].POOL_DESC.pXMSdesc,esi mov ebx,edi ; raw address - must be aligned to page boundary add ebx,3 and ebx,not 3 mov [edx].POOL_DESC.dwAddressK, ebx sub ebx, edi ; 00->00, 01->03, 02->02, 03->01 mov [edx].POOL_DESC.bStartAdj,bl mov [edx].POOL_DESC.bEndAdj,0 ; zero allocation entries xor eax,eax lea edi,[edx].POOL_DESC.bits push ecx mov ecx,POOLDESC_ALLOCATION_SPACE / 4 rep stosd pop ecx cmp edi,[PoolAllocationEnd] jb @@notlast mov [PoolAllocationEnd],edi @@notlast: ; block size = (raw size - start adjustment) rounded down to 16K boundary mov eax,ecx ; raw size sub eax,ebx shr eax,4 ; 16K count, known 16-bit value going to 8-bit cmp ax,POOLDESC_ALLOCATION_SPACE * 2 jbe @@setmax mov ax,POOLDESC_ALLOCATION_SPACE * 2 @@setmax: mov [edx].POOL_DESC.b16kmax,al mov [edx].POOL_DESC.b16kfree,al test al,1 jz @@is32kaligned ;is 16kb page count "uneven"? movzx ebx, al shr ebx, 1 mov byte ptr [edx].POOL_DESC.bits[ebx],0F0h ;mark last page "used" @@is32kaligned: shl eax,2 ; 8-bit value potentially going to 16-bit mov [edx].POOL_DESC.w4kfree,ax add [pmem.dwTotalMem4K],eax ; return true memory assigned - (block size + start adjustment) shl eax,2 ; convert mem size to 1K movzx ebx,[edx].POOL_DESC.bStartAdj add eax,ebx mov [esp].PUSHADS.rEAX, eax popad ret align 4 Pool_PrepareBlock ENDP if ?FREEXMS ;--- called if Jemm is unloaded Pool_FreeAllBlocks proc public pushad mov esi, [PoolAllocationTable] ; esi -> pool allocation block nextitem: cmp [esi].POOL_DESC.dwAddressK,0 jz @@skipitem movzx eax, [esi].POOL_DESC.b16kmax mov [esi].POOL_DESC.b16kfree,al shl eax, 2 mov [esi].POOL_DESC.w4kfree,ax call Pool_TryFreeToXMS @@skipitem: add esi, sizeof POOL_DESC cmp esi, [PoolAllocationEnd] jb nextitem popad ret align 4 Pool_FreeAllBlocks endp endif ; upon entry esi -> pool allocation block to check if freeable to XMS ; perform the free if possible ; destroys eax,ecx ; if a pool descriptor is freed, it gets fields dwAddressK, ; w4kfree, b16kfree and b16kmax set to 0. Pool_TryFreeToXMS PROC test [esi].POOL_DESC.bFlags,PBF_DONTFREE jne @@exit ; never free these blocks mov al,[esi].POOL_DESC.b16kfree cmp al,[esi].POOL_DESC.b16kmax ja @@bad ; free more than max, try to fix jne @@exit ; free is less than maximum, used movzx eax,[esi].POOL_DESC.w4kfree shr eax,2 or ah,ah jne @@bad cmp al,[esi].POOL_DESC.b16kmax ja @@bad jne @@exit ; free less than max ; ok, this pool block is not used anymore. ; now scan all pool blocks and check those linked to the same XMS handle ; if they are free too. If yes, then mark XMS handle as free and clear all ; blocks. push esi mov esi,[esi].POOL_DESC.pXMSdesc mov eax,[PoolAllocationTable] nextitem: cmp [eax].POOL_DESC.dwAddressK,0 je @@checknext ; unused block cmp esi,[eax].POOL_DESC.pXMSdesc jne @@checknext test [eax].POOL_DESC.bFlags,PBF_DONTFREE jne @@checkdone ; can't free this block ; see if block empty movzx ecx,[eax].POOL_DESC.b16kmax cmp cl,[eax].POOL_DESC.b16kfree jne @@checkdone shl ecx,2 ; convert to 4K max cmp cx,[eax].POOL_DESC.w4kfree jne @@checkdone @@checknext: add eax, sizeof POOL_DESC cmp eax, [PoolAllocationEnd] jb nextitem ; checked all blocks as empty, go through them again and mark unused. ; also update the PoolAllocationEnd variable if needed. push edx push edi mov edi, [PoolAllocationTable] mov ecx, edi xor eax, eax nextitem2: cmp [edi].POOL_DESC.dwAddressK,0 je @@freenext ; unused block cmp esi,[edi].POOL_DESC.pXMSdesc je @@freenext2 mov ecx, edi ; remember last block not free jmp @@freenext @@freenext2: ; mark the block as free movzx edx, [edi].POOL_DESC.b16kmax shl edx, 2 mov [edi].POOL_DESC.dwAddressK, eax sub [pmem.dwTotalMem4K], edx mov dword ptr [edi].POOL_DESC.w4kfree, eax ;this also clears the 16kb counters @@freenext: add edi, sizeof POOL_DESC cmp edi, [PoolAllocationEnd] jb nextitem2 add ecx, sizeof POOL_DESC mov [PoolAllocationEnd], ecx pop edi pop edx if ?EXPANDFIRST add esi, [dwRes] endif call Pool_FreeEMB @@checkdone: pop esi @@exit: ret @@bad: @CheckBlockIntegrity stc ret align 4 Pool_TryFreeToXMS ENDP ; populate empty pool blocks with XMS owner info ; inp: ; esi -> XMS (pseudo-)handle ; ecx == size in kB XMS block ; edi == physical address (in K, shifted 10 to right!) ; al == flags ; NOTE: ecx and edi may not match owner XMS size/address ; out: NC success, edi=physical address after alloc ; C if insufficient number of empty blocks to cover XMS handle range ; destroys eax,ebx,edx,edi Pool_AllocBlocksForEMB PROC public mov ebx,ecx if ?EXPANDFIRST sub esi,[dwRes] endif @@allocloop: call Pool_GetUnusedBlock jc @@exit ; no more blocks, remainder of XMS is effectively discarded mov [edx].POOL_DESC.bFlags,al push eax mov eax,edi ; compute size of candidate block/offset to new add eax,3 and al,not 3 sub eax,edi add eax,POOLDESC_MEMSIZE ; 1.5M (in K) plus alignment adjustment size cmp eax,ebx jbe @@sizeok mov eax,ebx @@sizeok: mov ecx,eax call Pool_PrepareBlock ; uses esi entry condition add edi,eax ; update pool allocation block address sub ebx,eax ; update size left to allocate pop eax cmp ebx,MINXMSSIZE ; see if should remainder what's left jnb @@allocloop test al,PBF_DONTEXPAND jnz @@exit mov [edx].POOL_DESC.bEndAdj,bl @@exit: ret align 4 Pool_AllocBlocksForEMB ENDP ; walk XMS blocks, find largest XMS block x which has ; max( min(dwMemMax4K-dwMemTotal4K, 1.5M), 32K) >= x >= 32K ; after 4K alignment and allocate it for new pool allocation block. ; if all XMS blocks >1.5M, then pick smallest and try to put remainder ; into a free handle. If no free handle, allocate sufficient new pool ; descriptors to cover full range. If not enough free pool decriptors ; available, the remaining mem is lost until handle is freed. This could ; only happen under very bad XMS fragmentation, if at all. ; return carry clear if success, set if fail ; all registers preserved ; called by VCPI protected-mode API, do not write segment registers here! Pool_AllocateEMBForPool PROC pushad cmp [bNoPool],0 ; check if pool sharing jne @@allocfail mov ebp, POOLDESC_MEMSIZE ;1536 kB mov eax, [pmem.dwMaxMem4K] sub eax, [pmem.dwTotalMem4K] jbe @@allocfail ;if max memory already allocated shl eax, 2 ;convert to 1K units cmp ebp, eax jc @@usesmaller mov ebp, eax cmp ebp, MINXMSSIZE jnc @@usesmaller mov ebp, MINXMSSIZE @@usesmaller: ;ebp = max( min(dwMemMax4K-dwMemTotal4K, 1.5M), 32K) call Pool_GetUnusedBlock; free pool descriptor available? jc @@allocfail ; no need to process further ;--- scan XMS handle table mov esi, [XMS_Handle_Table.xht_pArray] movzx ecx, [XMS_Handle_Table.xht_numhandles] xor edx,edx ; edx -> largest block <= 1.5M or smallest if none <=1.5M nextitem: test [esi].XMS_HANDLE.xh_flags, XMSF_FREE ; free XMS memory block? je skipitem ; no, don't check mov ebx,[esi].XMS_HANDLE.xh_baseK mov eax,[esi].XMS_HANDLE.xh_sizeK if ?XMS35COMPAT test ebx,0ffc00000h ; block beyond 4GB? jnz skipitem endif ife ?INTEGRATED and ebx,ebx je skipitem ; FD Himem bug, ignore blank or zero-sized handle or eax,eax je skipitem endif and bl,3 mov bh,4 sub bh,bl ;bh=4,3,2,1 and bh,3 ;bh=3,2,1,0 bl=1,2,3,0 movzx ebx,bh sub eax,ebx ;eax = page adjusted size in 1kb units cmp eax,MINXMSSIZE ;ignore everything below our limit jb skipitem or edx,edx je @@newcandidate ; auto-match if first xms block available ; eax = test value size, edi = current candidate size, both 4k adjusted cmp eax,edi je skipitem ja @@larger ; test XMS block smaller than candidate block cmp edi,ebp jbe skipitem ; current candidate closer to match size jmp @@newcandidate ; test XMS block larger than candidate block @@larger: cmp edi,ebp jae skipitem ; current candidate closer to match size cmp eax,ebp ja skipitem ; test too large @@newcandidate: mov edx, esi ; new best candidate mov edi, eax ; edi = candidate size in 1 kB units skipitem: movzx eax, [XMS_Handle_Table.xht_sizeof] add esi,eax ; move to next handle descriptor dec ecx jne nextitem or edx,edx ; candidate found? jne @@allocok @@allocfail: popad stc ret ; candidate is ensured to have MINXMSSIZE after 4K alignment adjustment @@allocok: @dprintf ?POOLDBG, <"Pool_AllocateEMBForPool: found XMS block hdl=%X addr=%X siz=%X",10>, edx, [edx].XMS_HANDLE.xh_baseK, [edx].XMS_HANDLE.xh_sizeK mov [edx].XMS_HANDLE.xh_flags,XMSF_USED ; flag candidate as used mov [edx].XMS_HANDLE.xh_locks,1 ; and locked mov esi,ebp ; default allocation maximum size cmp ebp,POOLDESC_MEMSIZE jnz @@adjusted mov eax, [XMSPoolBlockCount] cmp eax,1 jbe @@trailadj ; use standard 1.5M size for first two blocks dec eax ; should never overflow before we hit 4G total allocated and al,0fh ; but ensure that overflow doesn't happen anyway mov cl,al ; shift the block size higher by factor of two shl esi,cl ; if esi (=XMSBlockSize) >= max free, then reduce XMSBlockSize mov eax,[pmem.dwMaxMem4K] sub eax,[pmem.dwTotalMem4K] shl eax,2 ; convert to 1K blocks @@checksize: cmp eax, esi jae @@adjusted cmp esi,ebp ; see if esi(=XMSBlockSize) is at minimum default jbe @@adjusted ; yes, can't reduce it any further shr esi,1 ; reduce block size by one shift and try again jmp @@checksize @@adjusted: ; allow up to MINXMSSIZE-1 trailing bytes @@trailadj: mov eax, esi add eax, MINXMSSIZE-1 ; adjust for possible trail cmp edi, eax jbe @@setblock ; no need to split XMS handle allocation ; search for a free XMS handle mov edi, [XMS_Handle_Table.xht_pArray] movzx ecx, [XMS_Handle_Table.xht_numhandles] movzx eax, [XMS_Handle_Table.xht_sizeof] nextitem2: test [edi].XMS_HANDLE.xh_flags,XMSF_INPOOL jnz @@gotfree ife ?INTEGRATED cmp [edi].XMS_HANDLE.xh_flags,XMSF_USED ; some Himems dont set XMSF_INPOOL, so je skipitem2 ; check FREE items if address/size is NULL cmp [edi].XMS_HANDLE.xh_baseK,0 je @@gotfree cmp [edi].XMS_HANDLE.xh_sizeK,0 je @@gotfree endif skipitem2: add edi,eax ; move to next handle descriptor loop nextitem2 ; no free handle found, try to allocate multiple blocks, discarding excess jmp @@setblock @@gotfree: mov cl,BYTE PTR [edx].XMS_HANDLE.xh_baseK ; compute size of candidate block/offset to new and cl,3 mov ch,4 sub ch,cl and ch,3 movzx ecx,ch add ecx, esi ; maximum size (exceeded) plus alignment adjustment size ; edx -> candidate block being allocated ; edi -> new block receiving remainder ; update candidate XMS block size mov eax,[edx].XMS_HANDLE.xh_sizeK ; keep original size for updating new block mov [edx].XMS_HANDLE.xh_sizeK,ecx ; update new XMS block info sub eax,ecx ; new block size == old block original size - old block new size mov [edi].XMS_HANDLE.xh_sizeK,eax mov [edi].XMS_HANDLE.xh_flags,XMSF_FREE ; explicitly flag free mov [edi].XMS_HANDLE.xh_locks,0 mov eax,[edx].XMS_HANDLE.xh_baseK add eax,ecx mov [edi].XMS_HANDLE.xh_baseK,eax ; new block start == old block start + old block new size @dprintf ?POOLDBG, <"Pool_AllocateEMBForPool: free XMS block hdl=%X addr=%X siz=%X",10>, edi, [edi].XMS_HANDLE.xh_baseK, [edi].XMS_HANDLE.xh_sizeK ; edx -> owner XMS handle for new pool allocation block(s) ; may be multiple blocks due to XMSBlockCount shifter @@setblock: mov esi,edx mov ecx,[esi].XMS_HANDLE.xh_sizeK mov edi,[esi].XMS_HANDLE.xh_baseK xor al,al call Pool_AllocBlocksForEMB inc [XMSPoolBlockCount] popad clc ret align 4 Pool_AllocateEMBForPool ENDP ; count available XMS 4K-aligned 4K pages in 16K chunks ; return count in edx ; destroys no other registers ; do not write segment registers here! function ; is called by VCPI protected mode API. Pool_GetFreeXMSPages PROC push esi push ecx xor edx,edx cmp [bNoPool], 0 ; XMS memory pool? jne @@countdone movzx ecx, [XMS_Handle_Table.xht_numhandles] mov esi, [XMS_Handle_Table.xht_pArray] push eax push ebx nextitem: test [esi].XMS_HANDLE.xh_flags,XMSF_FREE jz skipitem ife ?INTEGRATED xor eax,eax cmp eax,[esi].XMS_HANDLE.xh_baseK ; account for FD Himem bug je skipitem cmp eax,[esi].XMS_HANDLE.xh_sizeK je skipitem endif mov eax,[esi].XMS_HANDLE.xh_baseK if ?XMS35COMPAT test eax,0FFC00000h ; block beyond 4GB? jnz skipitem endif mov ebx,eax add ebx,3 ; round up add eax,[esi].XMS_HANDLE.xh_sizeK and al,0fch ; align to 4K boundary and bl,0fch sub eax,ebx ; compute size of block after alignments jbe skipitem cmp eax,MINXMSSIZE ; ignore free blocks < 16 kB jb skipitem and al,NOT 0fh ; mask to 16K shr eax,2 ; convert 1K to 4K add edx,eax ; update total count skipitem: movzx eax, [XMS_Handle_Table.xht_sizeof] add esi,eax ; move to next handle descriptor dec ecx jne nextitem pop ebx pop eax @@countdone: pop ecx pop esi ret align 4 Pool_GetFreeXMSPages ENDP ; mark an XMS handle as free. ; scan through the XMS handle array ; and try to merge this block with other free blocks ; ESI = handle which just has become free ; preserves all registers Pool_FreeEMB PROC pushad mov [esi].XMS_HANDLE.xh_locks,0 mov [esi].XMS_HANDLE.xh_flags,XMSF_FREE dec [XMSPoolBlockCount] mov edi, [XMS_Handle_Table.xht_pArray] movzx ecx, [XMS_Handle_Table.xht_numhandles] mov edx, [esi].XMS_HANDLE.xh_baseK add edx, [esi].XMS_HANDLE.xh_sizeK movzx eax, [XMS_Handle_Table.xht_sizeof] @@checkloop: cmp [edi].XMS_HANDLE.xh_flags,XMSF_FREE ; see if free jne @@checknext ; anything else is to ignore ife ?INTEGRATED cmp [edi].XMS_HANDLE.xh_baseK,0 ; FD Himem: free + base 0 is "INPOOL" je @@checknext ; can't check blank handle endif cmp edi, esi je @@checknext mov ebx,[edi].XMS_HANDLE.xh_baseK ; which starts at EBX (end of test block) cmp edx,ebx jz @@merge1 add ebx,[edi].XMS_HANDLE.xh_sizeK cmp ebx,[esi].XMS_HANDLE.xh_baseK jz @@merge2 @@checknext: add edi,eax ; move to next handle descriptor loop @@checkloop @@nodefrag: popad ret @@merge2: push edi xchg esi, edi call merge pop edi jmp @@checknext ;there might come just another free block to merge @@merge1: push offset @@checknext merge: mov ebx,[edi].XMS_HANDLE.xh_sizeK add [esi].XMS_HANDLE.xh_sizeK, ebx mov [edi].XMS_HANDLE.xh_flags,XMSF_INPOOL ; flag handle as free xor ebx,ebx mov [edi].XMS_HANDLE.xh_locks,bl mov [edi].XMS_HANDLE.xh_baseK,ebx mov [edi].XMS_HANDLE.xh_sizeK,ebx retn align 4 Pool_FreeEMB ENDP ; hook left for debugging, no current actions taken ;; upon entry esi -> pool allocation block to perform integrity check upon ;; update with valid information if allocation counts mismatch ;; destroy no registers if ?POOLDBG CheckBlockIntegrity PROC ret CheckBlockIntegrity ENDP endif .text$03 ends .text$04 segment ; initialize memory pool block descriptors ; each descriptor describes a memory block <= 1.5M (48*8 * 4K) ; and is 16 + 48 = 64 bytes in size. ; required are: ((pmem.dwMaxMem4K / 1.5M) + x) * 64 bytes bytes for these items ; x is max number of XMS handles ; in: ESI -> JEMMINIT ; in: EDI -> free memory ; out: EDI -> free memory Pool_Init1 proc public add edi,15 ; align on paragraph and edi,not 15 mov [PoolAllocationTable],edi mov [PoolAllocationEnd],edi mov eax,[pmem.dwMaxMem4K] ;= 30720 x 4k = 120 MB cdq mov ecx,POOLDESC_MEMSIZE / 4 ; since pmem.dwMaxMem4k is in 4k units div ecx ; default: 30720/384 = 80 add eax,2 ; round up, not down (and 1 extra for NoPool) cmp [bNoPool],0 jnz @@isnopool movzx ecx,[XMS_Handle_Table.xht_numhandles] dec ecx add eax, ecx @@isnopool: .errnz sizeof POOL_DESC - 64 shl eax,6-2 ; size of 1 descriptor: 64 bytes -> 16 dwords mov ecx,eax xor eax,eax rep stosd mov [PoolAllocationMax],edi @dprintf ?INITDBG, <"pool start/end=%X/%X",10>,PoolAllocationTable, PoolAllocationMax ret Pool_Init1 endp ; Pool Init phase 2 ; ; ESI -> JEMMINIT ; EDI -> free memory fix block (physical, page aligned) ; EAX -> size (in 4kb pages) still free in fix block ; out: ; EDI -> free memory (physical) ; destroys EAX, EBX, ECX, EDX ; first pool allocation block(s) are used to manage remainder of initial XMS ; allocated memory (fixed EMS/VCPI allocations). ; never expand these blocks, the descriptor holds a real XMS handle rather than ; an XMS pseudo-handle/pointer taken from the XMS handle array. Pool_Init2 proc public push esi cmp [bNoPool], 0 je @@setblocks @dprintf ?INITDBG, <"pool sharing is off, 4k pages remaining=%X pmem.dwMaxMem4k=%X",10>, eax, pmem.dwMaxMem4K ;-- pool sharing is off, rest of block is used for EMS/VCPI ;-- make sure current values of MaxMem4K and EMSPagesMax ;-- are not too high mov ecx,[pmem.dwMaxMem4K] sub ecx,[pmem.dwTotalMem4K] cmp eax, ecx jnc @@isnodecrease ;jump if more pages are available sub ecx, eax sub [pmem.dwMaxMem4K],ecx mov ecx, eax ;pmem.dwMaxMem4K has to be adjusted @@isnodecrease: push ecx call EMS_CheckMax pop eax @@setblocks: mov ecx, eax ; count of available 4K pages if ?EXPANDFIRST mov al,PBF_DONTFREE else mov al,PBF_DONTEXPAND or PBF_DONTFREE endif movzx esi, [esi].JEMMINIT.XMSControlHandle ;--- al=flags, edi=phys addr, esi=XMS handle, ecx=size in kB @dprintf ?INITDBG, <"Pool_init2: addr=%X size in 4kb pg=%X",10>, edi, ecx shl ecx, 2 ;convert to 1kb units shr edi, 10 ;must be a 1k-address call Pool_AllocBlocksForEMB shl edi, 10 ;convert back to phys. address @dprintf ?INITDBG, <"Pool_init2: exit, edi=%X",10>, edi pop esi ret Pool_Init2 ENDP .text$04 ENDS END ================================================ FILE: src/UMB.ASM ================================================ ;--- XMS UMB part implementation ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ;--- publics/externals include extern32.inc ;--- for 100% MS compatibility, the UMB handler would have to ;--- implement a linked list of special MCBs: UMBMCB struct sig db ? ;signature 'M' segm dw ? ;segment address of content or 0000 if UMB is free size_ dw ? ;size in paragraps db 3 dup (?) name_ db 8 dup (?) ;name (='UMB',0,0,0,0,0) UMBMCB ends .text$01 SEGMENT UMBsegments UMBBLK UMB_MAX_BLOCKS dup (<0,0>) UMBend label byte .text$01 ends .text$03 segment ;--- XMS UMB handler ;--- EAX, EBX, EDX hold client values umb_handler proc public cmp ah,11h ;free UMB, DX=segment address to release je UMB_free cmp ah,12h ;realloc UMB, DX=segment to resize, BX=new size je UMB_realloc umb_handler endp ;fall thru! ;--- UMBalloc ;--- inp: DX=size of block in paragraphs, size=0 is allowed ;--- out: success: AX=1, BX=segment, DX=size ;--- error: AX=0, BL=error code, DX=largest block UMB_alloc proc @dprintf ?UMBDBG, <"UMBalloc enter, DX=%X",10>, dx mov esi, offset UMBsegments xor ebx,ebx ; holds largest too-small block size @@UMBloop: cmp [esi].UMBBLK.wSegm,0 ; see if valid UMB je @@UMBnext ; no test BYTE PTR [esi].UMBBLK.wSize+1,UMB_ALLOCATED jne @@UMBnext ; yes cmp dx,[esi].UMBBLK.wSize ; dx = requested block size (high bit of UMB size known reset) jbe @@UMBfound ; enough memory available in UMB cmp bx,[esi].UMBBLK.wSize ja @@UMBnext mov bx,[esi].UMBBLK.wSize ; update largest too-small block size @@UMBnext: add esi,size UMBBLK cmp esi,offset UMBend jnz @@UMBloop @dprintf ?UMBDBG, <"UMBalloc failed, bx=%X",10>, bx xor eax,eax ; flag failure or ebx,ebx jne @@umb_too_small mov bl,0B1h ; error "no UMB's are available" xor edx,edx jmp UMB_exit @@umb_too_small: mov edx,ebx ; return largest UMB in DX mov bl,0B0h ; error "only smaller UMB available" jmp UMB_exit @@UMBfound: ; see if actual UMB size exceeds request size by >=2K mov ax,80h ; 128 paras == 2K add ax,dx cmp ax,[esi].UMBBLK.wSize ja @@good_umb ; can't split it, just use it ; 2K or over would be unused, see if we can split the block mov ebx, offset UMBsegments @@splitloop: cmp [ebx].UMBBLK.wSegm,0 je @@freefound add ebx,size UMBBLK cmp ebx,offset UMBend jne @@splitloop jmp @@good_umb ;-- an unused entry found, split the block @@freefound: mov eax, edx add eax, 7Fh and eax,not 7Fh ; round up allocation to next 2K in paras mov cx,[esi].UMBBLK.wSegm add cx,ax mov [ebx].UMBBLK.wSegm,cx ; new block has segment offset of old block+allocation mov cx,[esi].UMBBLK.wSize ; get original UMB block size, in paras sub cx,ax ; subtract allocation mov [ebx].UMBBLK.wSize,cx ; update new block with old block size minus allocation mov [esi].UMBBLK.wSize,ax ; update original UMB block size to allocation @dprintf ?UMBDBG, <"UMB block split, new entry=%X segm=%X size=%X",10>, ebx, [ebx].UMBBLK.wSegm, [ebx].UMBBLK.wSize @@good_umb: mov dx,[esi].UMBBLK.wSize ; return actual block size in dx or BYTE PTR [esi].UMBBLK.wSize+1,UMB_ALLOCATED mov bx,[esi].UMBBLK.wSegm ; get UMB segment address in bx mov word ptr [EBP].Client_Reg_Struc.Client_EBX,bx mov ax,1 UMB_alloc endp ;fall throu ;--- there was a problem with JWasm if there's a jump to a forward ;--- reference which is not a simple label but a PROC! If the ;--- jump cannot be SHORT, the displacement isn't adjusted then! UMB_exit proc ;UMB_exit: @dprintf ?UMBDBG, <"UMB exit, ax=%X, bx=%X, dx=%X",10>, ax, bx, dx mov word ptr [ebp].Client_Reg_Struc.Client_EAX, ax mov word ptr [ebp].Client_Reg_Struc.Client_EDX, dx and al,al jnz @@umbexit_noerror mov byte ptr [ebp].Client_Reg_Struc.Client_EBX, bl @@umbexit_noerror: ret UMB_exit endp ;--- UMBFree ;--- DX=segment of UMB to release. ;--- todo: merge free blocks ;UMB_free proc UMB_free: @dprintf ?UMBDBG, <"UMBfree enter, DX=%X",10>, dx call UMB_findblock ;clears eax jc UMB_exit and BYTE PTR [esi].UMBBLK.wSize+1,not UMB_ALLOCATED ; mark UMB as free inc eax ; flag success jmp UMB_exit ;UMB_free endp ;--- UMBrealloc ;--- currently can only shrink a block ;--- and it does not really shrink, just return success ;--- inp: DX=segment, BX=new size of block in paragraphs UMB_realloc proc ;; mov ebx,[ebp].Client_Reg_Struc.Client_EBX ; restore EBX @dprintf ?UMBDBG, <"UMBrealloc enter, DX=%X, BX=%X",10>, dx, bx call UMB_findblock ;clears eax jc UMB_exit mov cx, [esi].UMBBLK.wSize and ch, not UMB_ALLOCATED cmp bx, cx ja @@umbreal_error inc eax ; flag success jmp UMB_exit @@umbreal_error: ; block is too small mov dx, cx mov bl, 0B0h jmp UMB_exit UMB_realloc endp UMB_findblock proc mov esi,offset UMBsegments xor eax,eax ; flag failure @@freeloop: cmp [esi].UMBBLK.wSegm,dx ; see if matches existing UMB allocation je @@blockfound add esi,size UMBBLK cmp esi,offset UMBend jnz @@freeloop @@blocknotalloced: mov bl,0b2h ; invalid UMB segment number error code stc ret @@blockfound: test byte ptr [esi].UMBBLK.wSize+1,UMB_ALLOCATED jz @@blocknotalloced clc ret align 4 UMB_findblock endp .text$03 ends .text$04 segment ;--- UMB initialization code ;--- in: ESI->JemmIni ;--- preserves esi, ebp UMB_Init proc public mov ebx,[esi].JEMMINIT.PageMap mov ecx,0A0h mov edi,offset UMBsegments - size UMBBLK mov dl,0 @@nextitem2: mov al,[ebx+ecx] call IsShadowRAM jnc @@isshadow cmp [esi].JEMMINIT.NoRAM,0 jnz @@skippage2 call IsUMBMemory jc @@skippage2 @@isshadow: cmp dl,0 jz @@newumb if ?SPLIT cmp al,'8' ;is it a SPLIT ROM? jb @@newumb ;then it must be a new UMB endif add [edi].UMBBLK.wSize,100h call clearpage jmp @@nextpage @@newumb: add edi, size UMBBLK cmp edi, offset UMBsegments + UMB_MAX_BLOCKS * size UMBBLK jnc @@umbdone inc [esi].JEMMINIT.NumUMBs mov byte ptr [edi].UMBBLK.wSegm+1,cl @dprintf ?INITDBG, <"new UMB at %X",10>, [edi].UMBBLK.wSegm mov [edi].UMBBLK.wSize,100h if ?SPLIT call clearpageEx else call clearpage endif mov dl,1 jmp @@nextpage @@skippage2: mov dl,0 @@nextpage: inc ecx cmp cl,0F8h jb @@nextitem2 @@umbdone: @dprintf ?INITDBG, <"UMBs initialized",10> ret UMB_Init endp IsShadowRAM proc cmp al,'S' jnz @@isnotshadow push ecx shl ecx,12 mov ah,[ecx] mov byte ptr [ecx],55h cmp byte ptr [ecx],55h jnz @@isnotshadow2 mov byte ptr [ecx],0AAh cmp byte ptr [ecx],0AAh jnz @@isnotshadow2 mov [ecx],ah pop ecx ret @@isnotshadow2: mov [ecx],ah pop ecx @@isnotshadow: stc ret IsShadowRAM endp ;--- clear an UMB "page" ;--- in: ecx = page number ;--- in: al = page type, if '1' <= al < '8' then split ROM ;--- in: EDI -> UMBBLK if ?SPLIT clearpageEx proc cmp al,'8' jnc clearpage sub al,'0' ;1,2,3,4,5,6,7 movzx eax,al shl eax,5 ;20,40,60,80,A0,C0,E0 mov byte ptr [edi].UMBBLK.wSegm,al sub word ptr [edi].UMBBLK.wSize,ax @dprintf ?INITDBG, <"split page, UMB at %X size=%X",10>, [edi].UMBBLK.wSegm, [edi].UMBBLK.wSize shl eax,4 ;1->200, 2->400, 3->600, 4->800, 5->A00, 6->C00, 7->E00 push edi push ecx mov edi,ecx shl edi,12 add edi,eax sub eax,1000h neg eax shr eax,2 ;added in v5.78 mov ecx,eax xor eax,eax rep stosd pop ecx pop edi ret clearpageEx endp endif ;--- clear a "page" ;--- in: ecx = page number clearpage proc push edi push ecx mov edi,ecx shl edi,12 mov ecx,1000h/4 xor eax,eax rep stosd pop ecx pop edi ret clearpage endp .text$04 ends END ================================================ FILE: src/VCPI.ASM ================================================ ;--- VCPI implementation ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ ;--- originally written by Michael Devore ;--- extended and modified for Jemm by Japheth .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc include ems.inc ;EMS definitions include vcpi.inc ;VCPI definitions ?CLEARTB equ 1 ;std 1, 1=clear "busy" flag in client's TSS descriptor ?VCPITMPSS equ 1 ;std 1, 1=use a tmp SS sel to avoid hiword(ESP) to be <> 0 ?SAFEMODE EQU 0 ;std 0, 1=additionally switch GDTR and IDTR ?SAFETSS equ 0 ;std 0, 1=additionally switch TR (clear "busy" flags!) ?SPSYSTEM equ 1 ;std 1, 1=set shared page at 110000 to "system" instead of r/o include extern32.inc if ?VCPI if ?VCPIXDBG .text$01 segment dbgv IRETDV86 <> .text$01 ends endif if ?CODEIN2PAGE @seg .text$02, else @seg .text$02, endif .text$02 ENDS .text$03 segment ;--- the interface is simple: ;--- inp: EBP -> client_reg_struc ;--- all registers except EBP and ECX still contain the client values. ;--- out: ah and edx will be copied to client_reg_struc, ;--- other client registers must be changed there directly align 4 VCPI_Call_Table label dword Dd VCPI_Presence ; 0 Dd VCPI_GetInterface Dd VCPI_GetMax ; 2 Dd VCPI_GetFreePages Dd VCPI_Allocate4K ; 4 Dd VCPI_Free4K Dd VCPI_GetAddress ; 6 Dd VCPI_GetCR0 Dd VCPI_ReadDR ; 8 Dd VCPI_WriteDR Dd VCPI_GetMappings ; 0ah Dd VCPI_SetMappings ;--- v5.85: VCPI_V86toPM wasn't activated in this table, because the label ;--- is used directly (see EMS.ASM). Now it's part of the table, so JLoad knows ;--- the label and JLMs may callout to a VCPI client. Dd VCPI_V86toPM ; 0ch ;VCPI_MAX equ ($ - VCPI_Call_Table) / 4 ; ; AX=DE00: VCPI presence detection ; return BH = 1 (major version), BL = 0 (minor version) ; VCPI_Presence PROC mov word ptr [ebp].Client_Reg_Struc.Client_EBX,100h mov ah,EMSS_OK ret VCPI_Presence ENDP ; ; AX=DE01: VCPI get protected mode interface ; inp: es:di -> client zero page table (to fill) ; ds:si -> three descriptor table entries in client's GDT (to fill) ; out: [es:di] page table filled ; di: first uninitialized page table entry (advanced by 4K) ; ebx: offset to server's protect mode code segment ; The VCPI docs tell that the physical page that receives this ; zero page table must NOT be altered after this call, to allow ; the server to modify PTEs. However, this seems an error in the ; documentation, since the host does not "manage" VCPI clients, ; hence has no idea what "interface" is the current one. VCPI_GetInterface PROC movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_DS shl edi,4 movzx esi,si add edi,esi ; esi -> client GDT entries mov esi, offset V86GDT + FLAT_CODE_SEL movsd movsd movsd movsd movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_EDI shl esi,4 add edi,esi ; edi -> client zero page table mov esi, @GetPTEAddr(?PAGETAB0) ; page table for first 1M ;--- Jemm must ensure that label VCPI_PM_Entry will be in shared memory. ;--- Since this label is located in .text$2, 1 page should suffice. if ?CODEIN2PAGE mov ecx, (440h+2*4) ;this is offset in page table for 112000h else mov ecx, (440h+1*4) ;this is offset in page table for 111000h endif if 0 ; not needed - the monitor code beyond the shared space isn't touched by SBEINIT test [bV86Flags], V86F_SB jz @F mov ecx, pPg0PartEnd ;end address of monitor part in page table 0 shr ecx, 10 @@: endif add word ptr [ebp].Client_Reg_Struc.Client_EDI,cx shr ecx, 2 @@vgiloop: lods dword ptr [esi] and ah,0F1h ; clear bits 9-11 stos dword ptr [edi] loop @@vgiloop if ?SPSYSTEM and byte ptr es:[edi-4], not 4 ;set shared page 111xxxh to system else or byte ptr es:[edi-4], 4 and byte ptr es:[edi-4], not 2 ;set shared page 111xxxh to r/o endif mov [ebp].Client_Reg_Struc.Client_EBX,OFFSET VCPI_PM_Entry mov ah,EMSS_OK ret VCPI_GetInterface ENDP ; AX=DE02: VCPI get maximum physical memory address ; return edx == physical address of highest 4K page available ; VCPI_GetMax PROC mov edx,[dwMaxPhysMem] if ?EMX test bV86Flags, V86F_EMX;the EMX DOS extender fails if too much memory jz @@noemx ;is available! mov edx,[pmem.dwMaxMem4K] shl edx, 12 ;convert to bytes add edx, [vdsstat.DMABuffStartPhys] @@noemx: endif dec edx and dx,NOT 0fffh mov ah,EMSS_OK ret align 4 VCPI_GetMax ENDP ; function 03,04 and 05 may also be called from protected-mode. ; then SS+DS+ES are flat, but they are NOT FLAT_DATA_SEL. ; A pop of segment registers would cause a GPF, since the ; client's GDT is active, but most likely is not mapped in ; current address context. ; AX=DE03: VCPI get number of free pages ; out: edx == number of free pages VCPI_GetFreePages PROC call Pool_GetFree4KPages ; free 4k pool pages in eax mov edx, eax mov ah, EMSS_OK ret align 4 VCPI_GetFreePages ENDP ; AX=DE04: VCPI allocate a 4K page ; out: edx == physical address of 4K page allocated VCPI_Allocate4K PROC public mov eax,[pmem.dwMaxMem4K] sub eax,[pmem.dwUsedMem4K] jbe @@fail call Pool_Allocate4KPage ; see if any pool block has 4K free jc @@fail mov edx, eax mov ah,EMSS_OK ret @@fail: stc mov ah,EMSS_OUT_OF_FREE_PAGES ret align 4 VCPI_Allocate4K ENDP ; AX=DE05: VCPI free a 4K page ; in: edx == physical address of 4K page to free VCPI_Free4K PROC call Pool_Free4KPage jc @@bad mov ah,EMSS_OK ret @@bad: mov ah,EMSS_LOG_PAGE_INVALID ret VCPI_Free4K ENDP ; ; AX=DE06: VCPI get physical address of 4K page in first megabyte ; entry cx = page number (cx destroyed, use stack copy) ; return edx == physical address of 4K page ; VCPI_GetAddress PROC movzx ecx, word ptr [ebp].Client_Reg_Struc.Client_ECX cmp cx,256 jae @@vga_bad ; page outside of first megabyte mov edx, @GetPTEAddr(ecx*4+?PAGETAB0) and dx,0f000h ; mask to page frame address mov ah,EMSS_OK ret @@vga_bad: mov ah,EMSS_PHYS_PAGE_INVALID ret VCPI_GetAddress ENDP ; ; AX=DE07: VCPI read CR0 ; return EBX == CR0 ; VCPI_GetCR0 PROC mov ebx,cr0 mov [ebp].Client_Reg_Struc.Client_EBX, ebx mov ah,EMSS_OK ret VCPI_GetCR0 ENDP ; ; AX=DE08: VCPI read debug registers ; call with ES:DI buffer pointer. Returns with buffer filled. ; (8 dwords, dr0 first, dr4/5 undefined) ; VCPI_ReadDR PROC movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES shl esi,4 movzx edi,di add edi,esi mov eax,dr0 stosd mov eax,dr1 stosd mov eax,dr2 stosd mov eax,dr3 stosd mov eax,dr6 mov [edi+8], eax stosd mov eax,dr7 mov [edi+8], eax stosd mov ah,EMSS_OK ret VCPI_ReadDR ENDP ; ; AX=DE09: VCPI write debug registers ; call with ES:DI buffer pointer. Updates debug registers. ; (8 dwords, dr0 first, dr4/5 ignored) ; VCPI_WriteDR PROC movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES shl esi,4 movzx edi,di add esi,edi lodsd mov dr0,eax lodsd mov dr1,eax lodsd mov dr2,eax lodsd mov dr3,eax add esi,8 lodsd mov dr6,eax lodsd mov dr7,eax mov ah,EMSS_OK ret VCPI_WriteDR ENDP ; ; AX=DE0A: VCPI get 8259A interrupt vector mappings ; return bx == 1st vector mapping for master 8259A (IRQ0-IRQ7) ; cx == 1st vector mapping for slave 8259A (IRQ8-IRQ15) ; VCPI_GetMappings PROC mov bx, [wMasterPICBase] mov cx, [wSlavePICBase] mov word ptr [ebp].Client_Reg_Struc.Client_EBX,bx mov word ptr [ebp].Client_Reg_Struc.Client_ECX,cx mov ah,EMSS_OK ret VCPI_GetMappings ENDP ; AX=DE0B: VCPI set 8259A interrupt vector mappings ; entry bx == 1st vector mapping for master 8259A (IRQ0-IRQ7) ; cx == 1st vector mapping for slave 8259A (IRQ8-IRQ15) ;-- this is meant just as info, the client has to program the PIC itself VCPI_SetMappings PROC mov ecx, [ebp].Client_Reg_Struc.Client_ECX mov [wMasterPICBase],bx mov [wSlavePICBase],cx mov ah,EMSS_OK ret VCPI_SetMappings ENDP .text$03 ends .text$02 segment ;--- the following 3 procs must be located in the first page. ;--- If this is no longer possible, ;--- the VCPI shared address space has to be increased. ; VCPI switch V86 mode to protected mode ; inp: AX=DE0C ; ESI -> linear address of "v86 to protected-mode switch" data, ; must be in first MB. ; out: GDTR, IDTR, LDTR, TR loaded for client ; SS:ESP=?HLPSTKSIZE (must provide at least 16 byte space for client) ; HIWORD(ESP) is cleared ; modifies EAX, SS:ESP (ESI may also be altered according to VCPI docs) ; .386p VCPI_V86toPM PROC public @dprintf ?VCPIXDBG, <"VCPI rm, ax=DE0C, esi=%X cr3=%X cs:eip=%X:%X",10>, esi, dword ptr [esi].V86TOPM.dwCR3,\ word ptr [esi].V86TOPM.dfCSEIP+4, dword ptr [esi].V86TOPM.dfCSEIP mov ecx,[ebp].Client_Reg_Struc.Client_ECX ;restore client value mov ebp,[ebp].Client_Reg_Struc.Client_EBP ;restore client value if ?VCPITMPSS ;--- some poorly written VCPI clients expect hiword(esp) to be zero! ;--- So we provide a small 128 byte stack. ;--- SS must be loaded BEFORE the address context is changed! mov ax, V86_STACK_SEL mov ss, eax endif mov eax,[esi].V86TOPM.dwCR3 ; set client's context *first* mov cr3,eax mov eax,[esi].V86TOPM.dwIDTOFFS ; set up client's IDT lidt fword ptr [eax] mov eax,[esi].V86TOPM.dwGDTOFFS ; set up client's GDT lgdt fword ptr [eax] lldt [esi].V86TOPM.wLDTR ; set up client's LDT if ?CLEARTB ;--- the "busy" flag of the TSS must be cleared, according to VCPI docs, and ;--- this must be done BEFORE the TR register is loaded! movzx esp,[esi].V86TOPM.wTR and esp,not 111b ; v5.85: don't assume bits 0+1 of new value for TR are 0! mov eax,[eax+2] ; EAX == linear address of client's GDT and ds:[esp+eax].DESCRIPTOR.bAttr,NOT 2; clear task busy bit in TSS descriptor endif ltr [esi].V86TOPM.wTR ; set up client's TSS if ?VCPITMPSS mov esp,?HLPSTKSIZE else mov esp,?BASE+?HLPSTKSIZE endif jmp [esi].V86TOPM.dfCSEIP ; jump to client's entry point VCPI_V86toPM ENDP align 4 ; VCPI switch protected mode to v86 mode ; inp: SS:ESP set for IRETD to V86, PM far call return address to discard; ; stack must be in shared address space. ; AX=DE0C ; DS -> selector containing full shared address space 0-11xxxx ; out: CR3, GDTR, IDTR, LDTR, TR loaded for server, ; IRETD will load SS:ESP and all segment registers, then enable v86 mode. ; modifies: EAX ; ;-- what has to be done is simple: ;-- 1. switch to host context (SS:ESP will stay valid, stack is in 1. MB) ;-- 2. load IDTR, GDTR, LDTR, TR for VCPI host ;-- 3. clear task busy bit in host's TSS descriptor ;-- 4. clear task switch flag in CR0 ;-- 5. IRETD will switch to v86 mode, interrupts disabled VCPI_PMtoV86 PROC ife ?SHAREDGDT mov eax,cs ; if GDT is in nonshared space, the current add eax,8 ; DS cannot be used to access it (TNT DOS extender!) mov ds,eax ; load DS with a copy of FLAT_DATA_SEL endif cli ; client should have disabled interrupts, but not all do mov eax,[V86CR3] ;--- don't use [ESP+x], because SS may be 16-bit add esp,5*4 ; position ESP to EFL+4 on stack push 20002h or (V86IOPL shl 12) ; set flags VM=1, NT=0, IOPL=3, IF=0, TF=0 sub esp,2*4 mov cr3,eax if ?VCPIXDBG mov eax,[esp].IRETDV86._Eip mov [dbgv.vEIP],eax mov eax,[esp].IRETDV86._Cs mov [dbgv.vCS],eax mov eax,[esp].IRETDV86._Efl mov [dbgv.vEFL],eax mov eax,[esp].IRETDV86._Esp mov [dbgv.vESP],eax mov eax,[esp].IRETDV86._Ss mov [dbgv.vSS],eax mov eax,[esp].IRETDV86._Es mov [dbgv.vES],eax mov eax,[esp].IRETDV86._Ds mov [dbgv.vDS],eax mov eax,[esp].IRETDV86._Fs mov [dbgv.vFS],eax mov eax,[esp].IRETDV86._Gs mov [dbgv.vGS],eax endif lgdt [GDT_PTR] xor eax,eax and byte ptr [V86GDT+V86_TSS_SEL+5], NOT 2 lidt [IDT_PTR] lldt ax mov al,V86_TSS_SEL clts ltr ax if ?VCPIXDBG mov eax,FLAT_DATA_SEL mov ss,eax mov esp, [dwStackCurr] @dprintf ?VCPIXDBG, <"VCPI pm, ax=DE0C, rm cs:eip=%X:%X, rm ss:esp=%X:%X", 10>, word ptr dbgv.vCS, dbgv.vEIP, word ptr dbgv.vSS, dbgv.vESP mov esp, offset dbgv endif iretd VCPI_PMtoV86 ENDP ;-- put the VCPI PM entry at the very beginning. This entry ;-- must be in the shared address space, and just the first page ;-- is shared! ;-- entry for EMM routines called from protected mode align 4 VCPI_PM_Entry PROC cmp ax,0de0ch ; see if switch from protected mode to V86 mode je VCPI_PMtoV86 ; yes, give it special handling ; other than de0ch, don't allow anything than de03h,de04h,de05h from PM cmp ax,0DE03h jb @@INV_CALL cmp ax,0DE05h ja @@INV_CALL pushfd push ds ; have to save segments for p-mode entry push es pushad mov ecx,cs add ecx,8 mov ds,ecx ; load DS,ES with a copy of FLAT_DATA_SEL mov es,ecx ;--- segment registers should *all* be set *before* switching contexts! ;--- why? because GDT of client might be located in unreachable space. ;--- get client SS:ESP + CR3, switch to host stack and context ;--- only VCPI functions 03, 04 and 05 are allowed ;--- which use registers EAX (AH) and EDX only. ;--- NMIs are NOT supposed to occur - they will crash the monitor; ;--- the VCPI client is responsible for masking NMIs! cli ; don't allow interruptions mov ebp, ss mov esi, esp mov edi, cr3 mov ebx, [V86CR3] ;--- this sequence cannot be traced, since the new stack is valid only ;--- after CR3 has been set - and the old stack isn't valid with the new CR3. mov ss, ecx ;this move must be before CR3 is switched mov esp, [dwStackCurr] mov cr3, ebx push ebp ;save client's SS push esi ;save client's ESP push edi ;save client's CR3 ;--- SAFEMODE will fully switch to Jemm's context if ?SAFEMODE sub esp,8+8 sgdt [esp+0] if ?SAFETSS str [esp+6] endif sidt [esp+8] lgdt [GDT_PTR] lidt [IDT_PTR] mov cx, FLAT_DATA_SEL mov ss, ecx mov ds, ecx mov es, ecx if ?SAFETSS mov cx, TSS_SEL ltr cx endif endif cld if ?VCPIDBG push eax push edx endif movzx ecx,al call [VCPI_Call_Table+ECX*4] if ?VCPIDBG mov ebp,esp @dprintf ?VCPIDBG, <"VCPI pm, inp: ax=%X edx=%X, out: ax=%X edx=%X",10>, word ptr [ebp+4], dword ptr [ebp+0], ax, edx add esp,8 endif if ?SAFEMODE lgdt fword ptr [esp+0] lidt fword ptr [esp+8] if ?SAFETSS mov cx,[esp+6] ltr cx endif add esp,8+8 endif pop ecx if 1 pop esi pop edi mov cr3, ecx ; restore client context *first* and mov ss, edi ; then restore client SS:ESP, thus client's GDT mov esp, esi ; need not be located in shared address space else lss esp,[esp] ; problem with this code: LSS loads descriptor for SS from client GDT, mov cr3, ecx ; and that GDT might be accessible only AFTER CR3 is restored. endif mov [esp].PUSHADS.rEDX,edx mov byte ptr [esp].PUSHADS.rEAX+1,ah popad pop es pop ds popfd retf @@INV_CALL: @dprintf ?VCPIDBG, <"VCPI pm, invalid call ax=%X",10>, ax mov ah,EMSS_INVALID_SUBFUNC retf VCPI_PM_Entry ENDP .text$02 ends endif ;?VCPI END ================================================ FILE: src/VCPI.INC ================================================ ; structure for VCPI switch from V86 to protected mode V86TOPM STRUCT dwCR3 DD ? ; client's CR3 value dwGDTOFFS DD ? ; offset of client's GDTR value dwIDTOFFS DD ? ; offset of client's IDTR value wLDTR DW ? ; client's LDTR value wTR DW ? ; client's TR value dfCSEIP DF ? ; entry point in client DW ? V86TOPM ENDS ================================================ FILE: src/VDMA.ASM ================================================ ;--- DMA port trapping code ;--- originally written by Harald Albrecht ;--- modified for Jemm by Japheth ;--- copyright (c) 1990 c't/Harald Albrecht ;--- to be assembled with JWasm or Masm v6.1+ ;--- IN, although trapped, returns the true value of the port. ;--- OUT stores the values to be written, and also does an OUT with unmodified ax/al reg. ;--- if a DMA channel is unmasked, Dma_CheckChannel() ;--- is called and will do a translation, if required. .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc include dma.inc ;--- publics/externals include extern32.inc .text$01 SEGMENT if ?DMAPT ;--- DMAREQ.bFlags values DMAF_ENABLED equ 1 ;1=channel is enabled (=unmasked) ;--- bits in DmaGblFlags DMAG_HiLoFlag1 EQU 0 ; addressing Hi-Byte, DMA controller #1 DMAG_HiLoFlag2 EQU 1 ; addressing Hi-Byte, DMA controller #2 ;--- if DMA buffer is required ;--- this flag isn't really used currently; instead, ;--- flag RFL_COPYBUFFER in bRFlags is set & queried. ;DMAG_NeedBuffer EQU 2 ;--- int 13h/40h DMA auto translation DmaTargetAdr DD 0 ; Original adress for DMA DmaTargetLen DD 0 ; Utilized part of the Buffer DmaChn DMAREQ MAXDMACHANNEL dup (<0,0,0,0,0,0>) ; also accessed by VDS code DmaGblFlags DW 0 ; global flags align 4 endif .text$01 ends .text$03 segment if ?DMAPT PAGEREGBASE equ 80h ;--- table to translate page ports (80-8F) to channel no ;------------ 0 1 2 3 4 5 6 7 8 9 A B C D E F PageLookUp DB -1, 2, 3, 1,-1,-1,-1, 0,-1, 6, 7, 5,-1,-1,-1,-1 ;--- page translation table ;--- channels 0 1 2 3 4 5 6 7 PageXLat DB 87H,83H,81H,82H,8Fh,8BH,89H,8AH ;--- Access to a DMA page register (80-8F) ;--- v5.84: now Simulate_IO_trap is called if access isn't "byte" ; DX : port ; AL : value ; CL : type ;--- modifies EDI, BX Dma_HandlePagePorts PROC public test cl,OUT_INSTR jz Simulate_IO ;is input, just translate to byte accesses test cl,STRING_IO or DWORD_IO or WORD_IO jnz Simulate_IO_trap ; split access to bytes @dprintf ?DMADBG, <"Dma_HandlePagePorts: OUT, dx=%X al=%X",10>, dx, al OUT DX,AL MOVZX EBX,DL ; store page value in cache MOVSX EDI,PageLookUp[EBX - PAGEREGBASE] cmp edi, -1 ; v5.85: check added jz @F MOV [DmaChn + EDI*sizeof DMAREQ].PageReg,AL jmp Dma_IsChannelUnmasked @@: ret align 4 Dma_HandlePagePorts ENDP ;--- handle 8-bit DMA ports 00-0F ;--- ports 00-07: addr/count for channels 0-3 ;--- port 08: R=status 0-3, W=cmd register 0-3 ;--- port 09: W=write req 0-3 ;--- port 0A: RW=channel mask register 0-3 ;--- port 0B: W=mode 0-3 ;--- port 0C: W=clear flipflop 0-3 for addr/count ;--- port 0D: R=temp reg, W=reset DMA ;--- port 0E: W=clear mask reg 0-3 ;--- port 0F: W=write mask register ;--- channel 2 is used by floppy controller ;--- v5.84: now Simulate_IO_trap is called if access isn't "byte" ; DX = port ; AL = value ; CL = type ;--- modifies EDI, BX Dma_HandleDmaPorts8 proc public test cl,OUT_INSTR jz Simulate_IO test cl,STRING_IO or WORD_IO or DWORD_IO ;not byte access? jnz Simulate_IO_trap if ?DMADBG push eax @dprintf ?DMADBG, <"Dma_HandleDmaPorts8: OUT, dx=%X al=%X ">, dx, al in al, 8 ; ok to read port 8??? @dprintf ?DMADBG, <"08=%X",10>, al pop eax endif CMP DL, DMA_STATUS_CMD ; ports 0-7 ( channel 0-3 index/count )? JB @@BlockSet8 CMP DL, DMA_SINGLEMASK ; single mask? JZ @@SMask8 CMP DL, DMA_MODE ; "Mode Register" responded ? JZ @@Mode8 CMP DL, DMA_FLIPFLOP ; clear flip-flop? JZ ClearFlipFlop8 if ?MMASK CMP DL, DMA_IMM_RESET JZ @@MMask8On CMP DL, DMA_MASK_RESET JZ @@MMask8Off CMP DL, DMA_MULTIMASK JZ @@MMask8 endif @dprintf ?DMADBG, <"Dma_HandleDmaPorts8: unhandled OUT to port %X",10>, dx out dx, al ret ClearFlipFlop8:: out dx, al BTR [DmaGblFlags], DMAG_HiLoFlag1 ret @@BlockSet8: ; I/O addresses 0-7 (DMA 8bit) MOV BX, DMAG_HiLoFlag1 MOVZX EDI,DL SHR EDI,1 ; get channel# into EDI jc Dma_SetLength JMP Dma_SetAddr @@SMask8: ;single mask mov edi, eax AND EDI,0011B jmp Dma_SetSMask @@Mode8: MOV EDI,EAX AND EDI,011B jmp Dma_SetMode if ?MMASK @@MMask8On: ;mask all 4 channels mov al,1111b jmp @@MMask8 @@MMask8Off: ;unmask all 4 channels mov al,0000b @@MMask8: xor edi, edi jmp Dma_SetMMask endif align 4 Dma_HandleDmaPorts8 endp ;--- handle 16-bit DMA ports C0-DF ;--- v5.84: now Simulate_IO_trap is called if access isn't "byte" ; DX = port ; AL = value ; CL = type ;--- modifies EDI, BX Dma_HandleDmaPorts16 proc public test cl,OUT_INSTR jz Simulate_IO test cl,STRING_IO or WORD_IO or DWORD_IO ;not byte access? jnz Simulate_IO_trap if ?DMADBG push eax @dprintf ?DMADBG, <"Dma_HandleDmaPorts16: OUT, dx=%X ax=%X ">, dx, ax in al, 0D0h @dprintf ?DMADBG, <"D0=%X",10>, al pop eax endif CMP DL, DMA_STATUS_CMD16 ; ports C0-CE ( channel 4-7 index/count )? JB @@BlockSet16 CMP DL, DMA_SINGLEMASK16 ; single mask? JZ @@SMask16 CMP DL, DMA_MODE16 ; mode set? JZ @@Mode16 CMP DL, DMA_FLIPFLOP16 JZ @@Clear16 if ?MMASK CMP DL, DMA_IMM_RESET16 JZ @@MMask16On CMP DL, DMA_MASK_RESET16 JZ @@MMask16Off CMP DL, DMA_MULTIMASK16 JZ @@MMask16 endif @dprintf ?DMADBG, <"Dma_HandleDmaPorts16: unhandled OUT to port %X",10>, dx out dx, al ret @@Clear16: out dx, al BTR [DmaGblFlags], DMAG_HiLoFlag2 ret @@BlockSet16: ; I/O addresses C0-CF (DMA 16bit) MOV BX, DMAG_HiLoFlag2 MOV EDI,EDX SHR EDI,2 ; get channel# into EDI AND EDI,3 ADD EDI,4 TEST DL,02H ; block length or address? JNZ Dma_SetLength jmp Dma_SetAddr @@SMask16: mov edi, eax AND EDI,011B ; mask the DMA channel # add edi,4 jmp Dma_SetSMask @@Mode16: MOV EDI,EAX AND EDI,011B ; mask the DMA channel # ADD EDI,4 JMP Dma_SetMode if ?MMASK @@MMask16On: ;mask all 4 channels mov al,1111b jmp @@MMask16 @@MMask16Off: ;unmask all 4 channels mov al,0000b @@MMask16: mov edi, 4 jmp Dma_SetMMask endif align 4 Dma_HandleDmaPorts16 endp ;--- EDI=channel# Dma_SetAddr: mov ecx,offset DmaChn.BaseAdr jmp Dma_SetLenOrAdr Dma_SetLength: mov ecx,offset DmaChn.BlockLen Dma_SetLenOrAdr: OUT DX,AL BTC [DmaGblFlags],BX ; toggle the Hi/Lo-Flag adc ecx,0 MOV BYTE PTR [ECX+EDI*sizeof DMAREQ],AL test cl,1 jnz Dma_IsChannelUnmasked ret ; ; Monitor "Mode Register" for the Transferdirection DMA <--> I/O ; Dma_SetMode: out dx, al mov [DmaChn+EDI*sizeof DMAREQ].bMode,al Dma_IsChannelUnmasked proc if 0 mov ecx,[dwRFlags] test byte ptr [ecx],RFL_DMAOP ;new Int 13h/40h DMA op? jz @@nonewreq and byte ptr [ecx],not 2 and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED ;wait until channel is enabled @@nonewreq: endif test [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED jz @@Bye CALL Dma_CheckChannel ; Then check if auto translation should be done @@Bye: RET Dma_IsChannelUnmasked endp ;--- Single mask port (000A, 00D4) ;--- bits 0-1 select channel ;--- bit 2=1 -> enable channel mask (=channel inactive) Dma_SetSMask proc and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED test al,4 jnz exit or [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED if 0 mov ecx,[dwRFlags] and byte ptr [ecx],not RFL_DMAOP endif push eax call Dma_CheckChannel pop eax exit: out dx, al ret Dma_SetSMask endp if ?MMASK ;--- Master mask port (000F, 00DE) ;--- this port is declared as W only, but usually it can be read as well. ;--- bit 0 -> 1=channel 0 masked, 0=channel 0 unmasked (=active) ;--- bit 1 -> ... chn 1 ;--- bit 2 -> ... chn 2 ;--- bit 3 -> ... chn 3 ;--- edi=0 or 4 ;--- al=value to be written to port (bits 0-3) Dma_SetMMask proc mov cl,4 push eax @@nextcnl: ; test [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED ; jnz @@skipchannel ;channel is already active and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED shr al,1 jc @@skipchannel ;channel will become/stay inactive or [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED push eax push ecx call Dma_CheckChannel pop ecx pop eax @@skipchannel: inc edi dec cl jnz @@nextcnl pop eax out dx, al ; the OUT must be done AFTER Dma_checkchannel ret Dma_SetMMask ENDP endif if ?ALT_TRAPHDL ;--- v5.85: check some ISA 8-bit DMA ports for channel 2 even ;--- if an external i/o trap handler has been installed. ;--- actually, just ports A,B,C,F are checked (D/E mask/unmask all channels!), ;--- but generally, writes to ports 4 (and 5?) and 81h ( page register for channel 2 ) ;--- should also be checked. ISA_DMA_Traphandler proc public cmp [DmaChn+2*sizeof DMAREQ].cDisable,0 ;ISA DMA translation disabled for channel 2? jnz done test cl,OUT_INSTR ; nothing done if IN (no longer true if ports 4/5/81h will be checked) jz done cmp dl, DMA_SINGLEMASK jz is0A cmp dl, DMA_MODE jz is0B cmp dl, DMA_FLIPFLOP jz is0C if ?MMASK cmp dl, DMA_MULTIMASK jz is0F endif done: clc ret is0C: call ClearFlipFlop8 jmp done is0A: push Dma_SetSMask jmp checkchannel is0B: push Dma_SetMode ; jmp checkchannel checkchannel: push edi mov edi, eax and edi, 11b cmp edi, 2 ; channel 2? clc jnz @F call dword ptr [esp+4] stc ; handle OUT for channel 2 exclusively! @@: pop edi lea esp, [esp+4] ret if ?MMASK is0F: ;--- just channel 2 is of interest; ;--- in AL, the mask-bit is already at pos 2 pushad mov edi, 2 and al, 100b or eax, edi mov dl, DMA_SINGLEMASK call Dma_SetSMask popad clc ret endif ISA_DMA_Traphandler endp endif ; A DMA-Channel is unmasked, so a check can (and has to) take place. ; ; In: EDI: Channelnumber 0..7 ; modifies ECX, ESI, EBX, AL ; ; Generally, this code assumes that DMA writes that are done within an INT13/INT40 ; are done because a FD/HD op uses ISA DMA, no matter what DMA channel is modified. ; This is NOT a valid assumption! ( might be DMA register ports writes during an IRQ ). ;--- better approach: check channel 2 only for FD ;--- optionally, if activated by cmdline option, check channel (3/X) for HD Dma_CheckChannel PROC @dprintf ?DMADBG, <"Dma_CheckChannel enter, edi=%u",10>, edi cmp [DmaChn+EDI*sizeof DMAREQ].cDisable,0 ;translation disabled by VDS? jnz @@Bye test [DmaChn+EDI*sizeof DMAREQ].bMode,DMA_MODE_OPERATION JZ @@Bye ; If a verify is wanted (MODE_OP==00), nothing is to be done if 1 ;--- todo: explain why this is done! and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED endif MOVZX ECX,[DmaChn+EDI*sizeof DMAREQ].BlockLen ; get block length into ECX MOVZX ESI,[DmaChn+EDI*sizeof DMAREQ].PageReg ; and base address into ESI INC ECX CMP EDI,4 ; for 16bit DMA channels, JB @@Only8 ADD ECX,ECX ; words are transferred SHL ESI,15 ; and Bit 0 of the page register MOV SI,[DmaChn+EDI*sizeof DMAREQ].BaseAdr ; will be ignored SHL ESI,1 JMP @@Chk @@Only8: SHL ESI,16 ; for 8bit DMA, address calculation MOV SI,[DmaChn+EDI*sizeof DMAREQ].BaseAdr ; is simple @@Chk: ;--- here: ESI=start address (linear!), ECX=size ;--- check if a buffer is needed. ; BTR [DmaGblFlags], DMAG_NeedBuffer ; Initialise. ;--- Is the block occupying contiguous physical pages? ;--- Or does it cross a 64kB/128 kB boundary? ;--- after the call ESI will hold the physical address (if NC) push esi CALL Dma_IsContiguous pop ebx JNC @@Set ;NC if buffer *is* contiguous ; test [DmaChn+EDI*sizeof DMAREQ].bMode,10h ;auto-init? Then a buffer is useless ; jnz @@Set @dprintf ?DMADBG, <"block not contiguous or crosses 64k border, DMA buffer needed!",10> MOV ESI,[vdsstat.DMABuffStartPhys] ; get DMA Buffer physical address ; BTS [DmaGblFlags], DMAG_NeedBuffer ; flag is set, but nowhere queried, so actually it's not really used cmp ecx, [vdsstat.DMABuffSize] jbe @F mov ecx, [vdsstat.DMABuffSize] ; error: DMA buffer overflow @@: call @@SetX ; use ESI to set DMA registers page & offset MOV AL,[DmaChn+EDI*sizeof DMAREQ].bMode ; copy data into buffer required? and al,DMA_MODE_OPERATION cmp al,1000b ; 1000b = DMA_MODE_OP_READ! JNZ @@IsRead ; todo: explain why "JNZ" is correct here @dprintf ?DMADBG, <"Dma_CheckChannel: copy into DMA buffer",10> push edi MOV ESI,EBX MOV EDI,[vdsstat.DMABuffStart] CLD call MoveMemory pop edi RET @@IsRead: if ?HOOK13 mov eax,[dwRFlags] if 1 test byte ptr [eax],RFL_DMAOP ; is a int 13h/40h read op pending? jz @@Bye mov byte ptr [eax],RFL_COPYBUFFER else or byte ptr [eax],RFL_COPYBUFFER endif else or [bDiskIrq],1 endif ;--- store values, needed for "copy out of buffer" after disk i/o has been done MOV [DmaTargetLen],ECX MOV [DmaTargetAdr],EBX ; save linear address of block @@Bye: RET ;--- use value in ESI to set DMA base address and page register ;--- modifies ESI, EAX @@Set: if ?DMADBG cmp esi, ebx setnz al movzx eax,al @dprintf ?DMADBG, <"target lin. start address=%X length=%X phys=%X chn=%X [%u]",10>,ebx,ecx,esi,edi,eax endif cmp esi, ebx ; has address changed? jz @@Bye @@SetX: push edx CMP EDI,4 ; 8-bit or 16-bit channel ? JB @@Set8 ;-- calc I/O-address from DMA channel: 4->C0, 5->C4, 6->C8, 7->CC lea edx, [edi-4] shl edx, 2 ; edx=0,4,8,C or dl, DMA_BASE_16BIT BTR [DmaGblFlags],DMAG_HiLoFlag2 ; DMA #2, HiLo-FlipFlop-Flag OUT DMA_FLIPFLOP16,AL ; clear Hi/Lo-FlipFlop JMP $+2 SHR ESI,1 MOV EAX,ESI ; reprogram base address SHR ESI,15 JMP @@Cont ;-- calc I/O-address from DMA channel: 0->0, 1->2, 2->4, 3->6 @@Set8: LEA EDX,[EDI+EDI] BTR [DmaGblFlags],DMAG_HiLoFlag1 ; DMA #1, HiLo-FlipFlop-Flag OUT DMA_FLIPFLOP,AL ; clear Hi/Lo-FlipFlop JMP $+2 MOV EAX,ESI ; reprogram base address SHR ESI,16 ; get bits 16-23 into 0-7 @@Cont: OUT DX,AL ; set index/count MOV AL,AH JMP $+2 OUT DX,AL MOV DL,PageXLat[EDI] ; get I/O-Adress of page-register MOV EAX,ESI ; set the page register OUT DX,AL pop edx ret align 4 Dma_CheckChannel ENDP ; ; Check a memory-area to be in physical contiguous memory. ; Furthermore, the physical region must not cross a 64 kB ; (or 128 kB for 16bit) boundary ; ; In: ESI: linear start address (bits 0-23 only) ; ECX: Length of the range (?DMABUFFMAX is max in kB) ; EDI: channel ; Out: NC: contiguous, ESI = physical adress ; C: not contiguous ; modifies: EAX, EBX, ESI Dma_IsContiguous PROC cmp ESI, 400000h-20000h ; don't touch PTEs > 3E0000h jnc @@Ok2 PUSH ECX if 0 ;ecx cannot be 0 (is 1-20000h) cmp ecx,1 ; make sure ecx is at least 1 adc ecx,0 endif PUSH ESI lea ECX,[ecx+esi-1] ; ecx -> last byte SHR ESI,12 ; linear start address -> PTE SHR ECX,12 ; linear end address -> PTE sub ecx, esi lea esi, @GetPTEAddr(ESI*4+?PAGETAB0) ; ESI -> PTE MOV EAX,[ESI] ; get PTE @dprintf ?DMADBG, <"Dma_IsContiguous: PTE=%X ecx=%X",10>, eax, ecx shr eax, 12 ; ignore bits 0-11 of PTE ;--- ESI -> PTE, EAX=1. PTE test eax, 0FF000h ; if physical address is > 16 MB jnz @@Fail ; a buffer is needed in any case JECXZ @@Ok ; exit if region fits in one page PUSH EAX ; save Bits 31-12 @@Loop: ADD ESI,4 ; one entry further INC EAX ; Go one page and MOV EBX,[ESI] shr EBX, 12 CMP EAX,EBX ; contiguous? loopz @@Loop POP EAX JNZ @@Fail mov esi,eax ; don't change eax for compare cmp edi,4 ; DMA channel 0-3? setnc cl add cl,4 shr esi, cl ; shift 4 or 5 bits shr ebx, cl cmp ebx,esi JNZ @@Fail ; a 64/128 kB Border has been crossed!!! @@Ok: pop ESI shl eax,12 AND ESI,0FFFh OR ESI,EAX pop ECX @@Ok2: CLC RET @@Fail: POP ESI POP ECX STC RET align 4 Dma_IsContiguous ENDP ; Copy the DMA-buffercontents after termination of the DISK-I/O to the ; wanted target/location. ; * If ?HOOK13 is 1, this is triggered by an v86-breakpoint in real-mode int 13 ; * and int 40 handlers. First the original int 13 / 40 is done, and ; * then, if the DMA buffer was used, things are copied out of this buffer ; * to the target linear address. ; * if ?HOOK13 is 0, this is called by Int 15h handler, ax=9101h. Dma_CopyBuffer PROC public if ?HOOK13 call Simulate_Iret and [ebp].Client_Reg_Struc.Client_EFlags, not 1 ;CF=0 endif MOV ESI,[vdsstat.DMABuffStart] MOV EDI,[DmaTargetAdr] mov ECX,[DmaTargetLen] @dprintf ?DMADBG, <"Dma_CopyBuffer: dst=%X src=%X siz=%X",10>, edi, esi, ecx CLD call MoveMemory if ?HOOK13 mov eax,[dwRFlags] mov byte ptr [eax],0 ; clear flags RFL_xxx endif ret align 4 Dma_CopyBuffer ENDP endif ;?DMAPT .text$03 ends END ================================================ FILE: src/VDS.ASM ================================================ ;--- Jemm's VDS implementation ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc include dma.inc include vds.inc ;--- publics/externals include extern32.inc .text$01 SEGMENT if ?VDS OldInt4B dd 0 endif if ?DMAPT or ?VDS align 4 vdsstat DMABUFF <0,0,0,?DMABUFFMAX/32 dup (0),0> align 4 endif .text$01 ends if ?VDS .text$03 segment VDS_Call_Table label dword Dd vds_version ; 02 get version Dd vds_lock ; 03 lock region, ES:DI -> DDS Dd vds_unlock ; 04 unlock region, ES:DI -> DDS Dd vds_scatterlock ; 05 scatter lock, ES:DI -> EDDS Dd vds_scatterunlock; 06 scatter unlock, ES:DI -> EDDS Dd vds_reqbuff ; 07 request DMA buffer, ES:DI -> DDS Dd vds_relbuff ; 08 release DMA buffer, ES:DI -> DDS Dd vds_copyinbuff ; 09 copy into DMA buffer, ES:DI -> DDS Dd vds_copyoutbuff ; 0A copy out of DMA buffer, ES:DI -> DDS Dd vds_disabletrans ; 0B disable DMA translation Dd vds_enabletrans ; 0C enable DMA translation VDS_MAX equ ($ - VDS_Call_Table) / 4 Dd vds_copyinbuffEx ; used by VDMA, 32bit version of copyinbuff Dd vds_copyoutbuffEx; used by VDMA, 32bit version of copyoutbuff ;--- bit vector which vds func needs DDS/EDDS translation (ES:DI -> EDI) if 0 ;------ CBA98765432 bDDS dd 00111111110b endif ;--- bit vector which vds func needs "offset" translation (BX:CX -> ECX) if 0 ;------ CBA98765432 bCpB dd 00110000000b endif ;--- VDS API ;--- EBP -> Client_Reg_Struc ;--- DX=flags? vds_handler proc public and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+2, not 1 ;v5.85: clear RF mov eax, [EBP].Client_Reg_Struc.Client_EAX cmp ah,81h jnz @@isnotvds movzx ebx,al @dprintf ?VDSDBG gt 2, <"VDS entry, al=%X",10>, ebx sub ebx,2 jc @@vds_reserved cmp ebx,VDS_MAX jnc @@vds_reserved call Simulate_Iret ;get the real "current" client flags! if 0 bt bDDS, ebx jnc @@nodds endif ;--- make DDS/EDDS accessible with EDI movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_ES movzx edi,di shl ecx, 4 add edi, ecx @@nodds: movzx edx,word ptr [ebp].Client_Reg_Struc.Client_EDX call [VDS_Call_Table + ebx*4] jc @@failed and [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;CF=0 ret @@failed: mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al or [ebp].Client_Reg_Struc.Client_EFlags,1 ;CF=1 ret @@isnotvds: mov ecx, [OldInt4B] jecxz @@novdsoldcall @dprintf ?VDSDBG gt 0, <"unhandled Int 4Bh, jmp to cs:ip=%X",10>, eax mov word ptr [ebp].Client_Reg_Struc.Client_EIP, CX shr ecx, 16 mov [ebp].Client_Reg_Struc.Client_CS, ECX ret @@vds_reserved: mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, VDSERR_FUNC_NOTSUPP ;v5.85 @@novdsoldcall: call Simulate_Iret or [ebp].Client_Reg_Struc.Client_EFlags,1 ;CF=1 ret align 4 vds_handler endp if 0 vds_unsup proc mov al, VDSERR_FUNC_NOTSUPP ; error "function not supported" stc ret vds_unsup endp endif ;--- int 4b, ax=8102h ;--- in: DX=flags - must be 0000 ;--- out: AX/BX/CX see below ;--- SI:DI = DMA buffer size in bytes ;--- DX = flags; ;--- 0: 1=DMA in first MB only (PC/XT arch) ;--- 1: 1=buffer is in first MB ;--- 2: 1=automatic remap supported ;--- 3: 1=all memory physically contiguous vds_version proc and edx,edx ;hiword of EDX cleared by Jemm jnz @@fail mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 100h; major/minor spec version mov word ptr [ebp].Client_Reg_Struc.Client_EBX, 1 ; product number mov word ptr [ebp].Client_Reg_Struc.Client_ECX, 1 ; product revision mov eax, [vdsstat.DMABuffSize] mov word ptr [ebp].Client_Reg_Struc.Client_EDI,ax shr eax, 16 mov word ptr [ebp].Client_Reg_Struc.Client_ESI,ax mov word ptr [ebp].Client_Reg_Struc.Client_EDX, 0 ; flags clc ret @@fail: mov al,VDSERR_DXRSVDBITSSET ;"reserved bit set in dx" stc ret align 4 vds_version endp ;--- int 4b, ax=8107h, request DMA buffer ;--- DX = flags, bit 1: copy data into buffer ;--- EDI -> DDS ;--- modifies ECX, EAX, EBX, DDS.wID, DDS.dwPhys ;--- out: C, al=5/6/16 ;--- NC, DDS.dwPhys + DDS.wID set vds_reqbuff proc mov al, VDSERR_DXRSVDBITSSET ; v5.85: check added test dx, not VDSF_COPY jnz @@fail mov [edi].DDS.wID, 0 mov ecx, [edi].DDS.dwSize @dprintf ?VDSDBG gt 1, <"VDS request buff, flgs=%X siz=%X",10>, dx, ecx and ecx, ecx jz @@ok mov al, VDSERR_BUFFTOOSMALL ; buffer too small for region add ecx, 400h-1 ; align to kB and cx, 0FC00h cmp ecx, [vdsstat.DMABuffSize] ja @@fail ;--- scan for a free region in buffer mov eax, [vdsstat.DMABuffSize] shr eax, 10 ; transform in KB inc eax ; the bit vector is 1-based! shr ecx, 10 inc ecx ; test for 1 bit more! @@rescan: mov ebx, ecx @@nextbit: dec eax js @@fail6 bt [vdsstat.DMABuffFree], eax jnc @@rescan dec ebx jnz @@nextbit dec ecx ; now use the real size inc eax ; skip the last bit found ;--- a free region large enough found @dprintf ?VDSDBG gt 1, <"VDS free region found at %X",10>, ax mov ebx, eax mov [edi].DDS.wID, ax @@marknextbit: btr [vdsstat.DMABuffFree], ebx inc ebx loop @@marknextbit dec eax shl eax, 10 ;convert to byte add eax, [vdsstat.DMABuffStartPhys] mov [edi].DDS.dwPhys, eax @dprintf ?VDSDBG gt 1, <"VDS req buff, phys=%X",10>, eax test dl,VDSF_COPY ; copy into buffer? jz @@ok xor ecx, ecx jmp vds_copyinbuffEx @@fail6: @dprintf ?VDSDBG gt 0, <"VDS req buff aborted, eax=%X, ebx=%X",10>, eax, ebx mov al, VDSERR_BUFFINUSE; error "buffer currently in use" @@fail: stc ret @@ok: clc ret align 4 vds_reqbuff endp ;--- int 4b, ax=8104h, unlock region ;--- DX=Flags, bit 1: copy data out of buffer ;--- EDI -> DDS ;--- "unlock region" is the same as "release buffer" vds_unlock proc @dprintf ?VDSDBG gt 2, <"VDS unlock flgs=%X addr=%X:%X siz=%X id=%X phys=%X",10>, dx, [edi].DDS.wSeg, [edi].DDS.dwOfs, [edi].DDS.dwSize, [edi].DDS.wID, [edi].DDS.dwPhys vds_unlock endp ;fall through ;--- int 4b, ax=8108h, release DMA buffer ;--- DX = flags, bit 1: copy data out of buffer ;--- EDI -> DDS (only buffer ID is needed) ;--- modifies EBX, EAX vds_relbuff proc test dx, not VDSF_COPY ;v5.85 check added jnz fail10 movzx ebx, [edi].DDS.wID @dprintf ?VDSDBG gt 2, <"VDS release buff, flgs=%X DDS=[siz=%X ID=%X addr=%X",10>, dx, [edi].DDS.dwSize, bx, [edi].DDS.dwPhys @dprintf ?VDSDBG gt 2, <"DMA buffer bits:%08X-%08X-%08X-%08X-%X",10>,vdsstat.DMABuffFree+0,vdsstat.DMABuffFree+4,vdsstat.DMABuffFree+8,vdsstat.DMABuffFree+12, byte ptr vdsstat.DMABuffFree+16 and ebx, ebx jz @@ok mov al, VDSERR_INVAL_BUFFID ;"invalid buffer id" cmp bx, ?DMABUFFMAX ja @@error ;--- the bit at position -1 must be "1" ;--- and bit 0 must be "0" ;--- the region ends with a "1" bit movzx ebx, bx dec ebx bt [vdsstat.DMABuffFree], ebx jnc @@error inc ebx bt [vdsstat.DMABuffFree], ebx jc @@error test dl, VDSF_COPY jz @@nextbit push ebx xor ecx, ecx call vds_copyoutbuffEx pop ebx jc @@error @@nextbit: bts [vdsstat.DMABuffFree], ebx inc ebx jnc @@nextbit @@ok: clc ret @@error: stc ret align 4 vds_relbuff endp ;-- test if a buffer is valid and as large as required ;-- used by copy into/out of DMA buffer ;-- EDI -> DDS ;-- ECX = offset in buffer ;-- returns ;-- NC and EBX == linear address of src/dst in DMA-buffer ;-- C and error code in AL ;-- modifies EAX, EBX, ECX testbuffer proc movzx ebx, [edi].DDS.wID @dprintf ?VDSDBG gt 2, <"VDS testbuff id=%X",10>,bx mov al, VDSERR_INVAL_BUFFID ;"invalid buffer ID" and ebx, ebx jz @@fail cmp bx, ?DMABUFFMAX ja @@fail dec ebx bt [vdsstat.DMABuffFree], ebx ;bit at -1 *must* be set jnc @@fail inc ebx bt [vdsstat.DMABuffFree], ebx ;bit at 0 bit *must* be clear jc @@fail mov eax, ecx ;eax == offset in DMA buffer mov ecx, [edi].DDS.dwSize and ecx, ecx jz @@ok lea ecx, [ecx+eax+3FFh] shr ecx, 10 push eax lea eax, [ebx+ecx] cmp eax, ?DMABUFFMAX pop eax ja @@fail3 push ebx @@nextbit: bt [vdsstat.DMABuffFree], ebx jc @@fail2 inc ebx dec ecx jnz @@nextbit pop ebx dec ebx shl ebx, 10 add ebx, [vdsstat.DMABuffStart] add ebx, eax @@ok: @dprintf ?VDSDBG gt 2, <"VDS testbuff ok, buff start (linear)=%X",10>, ebx clc ret @@fail2: @dprintf ?VDSDBG gt 0, <"VDS testbuff @@fail2, ebx=%X ecx=%X",10>, ebx, ecx pop ebx @@fail3: mov al,VDSERR_BNDVIOLATION ;"copy out of buffer range" - buffer boundary violation @@fail: @dprintf ?VDSDBG gt 0, <"VDS testbuff failed, al=%X",10>, al stc ret align 4 testbuffer endp ;--- int 4b, ax=8109h, copy into DMA buffer ;--- DX = flags, must be 0000 ;--- Client->BX:CX = offset in buffer ;--- EDI -> DDS vds_copyinbuff proc cmp dx,0 jnz fail10 mov ecx, [ebp].Client_Reg_Struc.Client_EBX shl ecx, 16 mov cx, word ptr [ebp].Client_Reg_Struc.Client_ECX vds_copyinbuff endp ;fall thru ;--- ECX = DMA buffer offset vds_copyinbuffEx proc call testbuffer ; returns EBX=linear address (inside DMA buffer) jc @@exit mov ecx, [edi].DDS.dwSize movzx eax, [edi].DDS.wSeg shl eax, 4 add eax, [edi].DDS.dwOfs @dprintf ?VDSDBG gt 1, <"VDS copyinbuff src=%X dst=%X siz=%X",10>, eax, ebx, ecx pushad mov esi, eax mov edi, ebx cld call MoveMemory popad clc @@exit: ret align 4 vds_copyinbuffEx endp fail10: mov al, VDSERR_DXRSVDBITSSET stc ret ;--- int 4b, ax=810Ah, copy out of DMA buffer ;--- DX = flags, must be 0000 ;--- Client->BX:CX = offset in buffer ;--- EDI -> DDS vds_copyoutbuff proc cmp dx,0 jnz fail10 mov ecx, [ebp].Client_Reg_Struc.Client_EBX shl ecx, 16 mov cx, word ptr [ebp].Client_Reg_Struc.Client_ECX vds_copyoutbuff endp ;fall thru ;--- ECX = DMA buffer offset vds_copyoutbuffEx proc call testbuffer jc @@exit mov ecx, [edi].DDS.dwSize movzx eax, [edi].DDS.wSeg shl eax, 4 add eax, [edi].DDS.dwOfs pushad mov esi, ebx mov edi, eax cld call MoveMemory popad clc @@exit: ret align 4 vds_copyoutbuffEx endp ;--- subroutines for vds_lock ;--- test if a region is contiguous and crosses 64/128 kB borders ;--- eax = start page, ebx = end page, dx=flags ;--- for first 4 MB only ;--- out: NC if ok, C if failed ;--- NC -> EAX = initial physical address ;--- C -> AL = error code, EDX = size that would be ok ;--- error code: ;--- 01: region not contiguous ;--- 02: region crossed 64kb/128kb boundary ;--- called by vds_lock vds_contiguous proc push esi push edi mov esi, @GetPTEAddr(?PAGETAB0) ;get start of page tab 0 mov ecx, [esi+eax*4] and cx, 0F000h mov edi, ecx push ecx @@nextitem: cmp eax, ebx jz @@iscontiguous inc eax mov ecx, [esi+eax*4] add edi, 1000h and cx, 0F000h cmp edi, ecx je @@nextitem pop ecx mov edx, edi sub edx, ecx mov al, VDSERR_NOTCONTIGUOUS @@failed: pop edi pop esi stc ret @@failed2: ;failed because 64/128 kb boundary crossing mov edx, esi shl edx, cl sub edx, eax mov al, VDSERR_CROSSEDBOUNDS jmp @@failed @@iscontiguous: pop eax ;get start physical address test dl,VDSF_64KALIGN or VDSF_128KALIGN ; boundary check? jz @@nocheck mov edi, eax mov esi, ecx test dl, VDSF_64KALIGN ;check for 64 kb border crossing? setz cl add cl, 16 shr edi, cl shr esi, cl cmp edi, esi jnz @@failed2 @@nocheck: pop edi pop esi ret align 4 vds_contiguous endp ;-- simplified version of vds_contiguous ;-- assumes linear == physical (for regions above 400000h) ;-- eax = linear start address ;-- ebx = size of region in bytes ;-- ret C -> boundary error, AL=error code, EDX==size ok ;-- ret NC -> ok, EAX == physical address [== linear address] ;-- modifies ECX, EBX, EDX, EAX ;-- called by vds_lock vds_contiguous2 proc ;-- see if boundary alignment check test dl,VDSF_64KALIGN or VDSF_128KALIGN ; boundary check? jz @@ok lea ebx,[eax+ebx-1] test dl,VDSF_64KALIGN ;if 64K works, then 128K will too setz cl add cl,16 ; shift 16 (64 kB) or 17 (128 kb) positions mov edx,eax shr ebx,cl ; convert start/end to alignment 64K frames shr edx,cl cmp ebx,edx je @@ok inc edx ; ecx == starting alignment frame+1 shl edx,cl ; convert to next alignment frame address sub edx,eax ; get bytes to next alignment frame address from start mov al,2 ; region crossed alignment boundary error code stc @@ok: ret align 4 vds_contiguous2 endp ;--- int 4b, ax=8103h, lock region ;--- EDI -> DDS ;--- DX=Flags ;--- 0:reserved ;--- 1:1=data should be copied into buffer (if necessary) [requires 2 cleared] ;--- 2:1=buffer disable (no DMA buffer should be allocated if noncontiguous or crosses 64/128 kB) ;--- 3:1=disable automatic remap ;--- 4:1=region must not cross 64 kb border ;--- 5:1=region must not cross 128 kb border ;--- there are several cases: ;--- 1. region is contiguous (and does not cross borders) ;--- out: NC ;--- DDS.wID = 0 ;--- DDS.dwPhys = physical addr ;--- 2. region is not contiguous and/or does cross borders ;--- 2.1 buffer disable flag set ;--- out: C ;--- DDS.wID = 0 ;--- AL = 1/2/3 ;--- 2.2 buffer disable flag cleared ;--- 2.2.1 buffer available and large enough ;--- alloc buffer ;--- if copy requested then copy data into buffer ;--- out: NC ;--- DDS.wID <> 0 ;--- DDS.dwPhys = physical addr of buffer ;--- 2.2.2 buffer too small ;--- out: C ;--- DDS.dwSize = size that would be ok ;--- DDS.wID = 0 ;--- AL = 5 ;--- 2.2.3 buffer not available ;--- out: C ;--- DDS.dwSize = ? ;--- DDS.wID = 0 ;--- AL = 6 ;--- ;--- field DDS.dwPhys may be filled by "Lock Region" vds_lock proc mov esi, edx ;save flags @dprintf ?VDSDBG gt 1, <"VDS lock flgs=%X addr=%X:%X siz=%X",10>, dx, [edi].DDS.wSeg, [edi].DDS.dwOfs, [edi].DDS.dwSize xor eax, eax mov ebx, [edi].DDS.dwSize ; region size cmp ebx, eax if 0 je @@locksuccess ; zero byte-sized region always works else setz al ;size 0 is always ok, but the physical address must be set add ebx, eax ;as well. So handle size 0 as if it is size 1 endif mov ax, [edi].DDS.wSeg shl eax,4 add eax, [edi].DDS.dwOfs ; jc --> overflow error lea ecx, [eax+ebx-1] cmp ecx, 400000h; region in first 4 MB ? jb @@below4MB ;-- assume linear == physical ;-- call a simplified version of vds_contiguous call vds_contiguous2 jnc @@locksuccess jmp @@lenfail @@below4MB: mov ebx, ecx ; ebx == final linear address shr ebx,12 ; convert end to 4K frame push eax shr eax,12 ; convert start to 4K frame call vds_contiguous pop ecx jc @@notcontiguous and ch, 0Fh or ax, cx jmp @@locksuccess ;save EAX in DDS ; physical memory is noncontiguous, error code is 1 or 2 ; return maximum length which would be ok @@notcontiguous: and ecx, 0fffh sub edx, ecx mov ecx, esi ; restore flags test cl, VDSF_NOBUFFER ; buffering disabled? jnz @@nobuffering push edx mov edx, esi call vds_reqbuff ; modifies DDS.wID pop edx jnc @@lockok @@nobuffering: @dprintf ?VDSDBG gt 0, <"VDS lock failed, ret size=%X rc=%X addr=%X:%X siz=%X flags=%X">, edx, al, [edi].DDS.wSeg, [edi].DDS.dwOfs, [edi].DDS.dwSize, si @@lenfail: mov [edi].DDS.dwSize,edx ; update maximum contiguous length mov [edi].DDS.wID,0 ; zero buffer id? stc ret @@locksuccess: @dprintf ?VDSDBG gt 1, <"VDS lock ok, ret size=%X phys=%X",10>, [edi].DDS.dwSize, eax mov [edi].DDS.dwPhys,eax ; physical address mov [edi].DDS.wID,0 ; zero buffer id @@lockok: clc ret align 4 vds_lock endp ;--- int 4b, ax=8105h ;--- inp: EDI -> EDDS ;--- DX=flags (bits 6 & 7) ;--- out: NC ;--- EDDS.wNumUsed: no of entries used to describe region ;--- regions/PTEs behind EDDS are set ;--- err: C set ;--- AL=error code ;--- dwSize: length that can be locked with current entries ;--- EDDS.wNumUsed: number of entries required to fully describe region! ;--- modifies EAX, EBX, ECX, EDX, ESI vds_scatterlock proc @dprintf ?VDSDBG gt 1, <"VDS scatlock flgs=%X addr=%X:%X siz=%X avl=%X",10>, dx, [edi].EDDS.wSeg, [edi].EDDS.dwOfs, [edi].EDDS.dwSize, [edi].EDDS.wNumAvail mov al, VDSERR_DXRSVDBITSSET ; v5.85: check added test dx, not (VDSF_PTE or VDSF_NPPTE) jz @F stc ret @@: mov ebx,[edi].EDDS.dwSize xor ecx,ecx ; cx holds entries used mov eax,ecx ; zero eax for later calcs mov [edi].EDDS.wNumUsed,cx ; EDDS number entries used and ebx,ebx setz al add ebx,eax ; handle size=0 like size=1 mov ax,[edi].EDDS.wSeg shl eax,4 add eax,[edi].EDDS.dwOfs ;eax=linear address ; jc --> overflow error test dl,VDSF_PTE ; PTEs flagged? jne @@getptes mov edx,eax ; edx == start linear address shr eax,12 ; convert start to 4K frame cmp eax,256 jb @@checklock ; inside of UMB remapping range (<1M) ; outside of UMB remapping range, assuming linear == physical mov [edi].EDDS.wNumUsed,1 ; one region used/needed cmp cx,[edi].EDDS.wNumAvail jnc @@notenoughregions mov [edi+size EDDS].EDDSRG.dwPhysAddr,edx ; region physical address mov [edi+size EDDS].EDDSRG.dwSizeRg,ebx ; region size clc ret @@notenoughregions: mov al, VDSERR_NAVL_TOOSMALL ;error "NumAvail too small" stc ret ;--- return regions, 1. MB ;--- eax = linear start page (linear address >> 12) @@checklock: push edx ; save start linear address lea ebx,[ebx+edx-1] ; ebx == final linear address shr ebx,12 ; convert to 4K frame mov edx, @GetPTEAddr(eax*4+?PAGETAB0) and dx, 0F000h mov esi,edx ; current physical address of 4K page cmp cx,[edi].EDDS.wNumAvail jnc @@bumpused mov [edi+size EDDS].EDDSRG.dwPhysAddr,edx mov [edi+size EDDS].EDDSRG.dwSizeRg,0 jmp @@bumpused @@entryloop: cmp cx,[edi].EDDS.wNumAvail ; entries available count jnc @@bumpused mov [edi+ecx*8+size EDDS].EDDSRG.dwSizeRg,1000h ; init region size mov [edi+ecx*8+size EDDS].EDDSRG.dwPhysAddr, esi @@bumpused: inc [edi].EDDS.wNumUsed ; bump count of used/needed entries @@nextframe: inc eax ; next linear page frame cmp eax,ebx ja @@scatdone ; no more regions to map cmp ah,0 ; page below 100h? jz @@next2 ; not at end of first 1M cmp ax,256 ja @@scatdone ; finishing off final region entry cmp esi,0ff000h ; end of 1M, see if final 4K block was identity mapped je @@scatdone ; yes ; start new region for final inc ecx mov esi, 100000h jmp @@entryloop @@next2: add esi, 1000h mov edx, @GetPTEAddr(eax*4+?PAGETAB0) and dx, 0F000h cmp edx, esi je @@samereg inc ecx ; have to move to next region/entry mov esi,edx ; update current physical address jmp @@entryloop @@samereg: cmp cx,[edi].EDDS.wNumAvail ; entries available count jnc @@nextframe add [edi+ecx*8+size EDDS].EDDSRG.dwSizeRg,1000h jmp @@nextframe ; calculate final region byte size or maximum allowed @@scatdone: pop edx cmp [edi].EDDS.wNumAvail,0 ; entries available count jz @@noregions and edx,0fffh add [edi+size EDDS].EDDSRG.dwPhysAddr, edx mov ebx,1000h sub ebx,edx add [edi+size EDDS].EDDSRG.dwSizeRg,ebx @@noregions: xor ebx,ebx mov edx,ebx movzx ecx,[edi].EDDS.wNumUsed ; number regions used (cannot be 0) mov al,0 ; no error cmp cx, [edi].EDDS.wNumAvail jbe @@finalloop mov cx,[edi].EDDS.wNumAvail ; only count up to minimum of available/used mov al, VDSERR_NAVL_TOOSMALL if ?VDSDBG and ecx, ecx jz @@scatfail else jecxz @@scatfail endif @@finalloop: mov esi,edx ; keep previous, last known valid value add edx,[edi+ebx*8+size EDDS].EDDSRG.dwSizeRg inc ebx loop @@finalloop cmp al,0 jne @@scatfail ; not all regions represented, update EDDS.dwSize mov edx,[edi].EDDS.dwSize ; update final region byte count sub edx,esi dec ebx mov [edi+ebx*8+size EDDS].EDDSRG.dwSizeRg, edx @dprintf ?VDSDBG gt 1, <"VDS scatlock exit, rc=0, used=%X, addr=%X, siz=%X",10>, [edi].EDDS.wNumUsed, [edi+size EDDS].EDDSRG.dwPhysAddr, [edi+size EDDS].EDDSRG.dwSizeRg clc ret @@scatfail: mov [edi].EDDS.dwSize,edx @dprintf ?VDSDBG gt 0, <"VDS scatlock exit, rc=%X, siz=%X used=%X",10>, al, [edi].EDDS.dwSize, [edi].EDDS.wNumUsed stc ret ;--- return PTEs ;--- DL & 40h == 40h ;--- eax == linear address ;--- ebx == size in bytes ;--- ecx == count entries used @@getptes: push eax ; save linear address shr eax,12 ; convert linear start to PTE index mov esi, @GetPTEAddr(?PAGETAB0) @@loop: xor edx, edx cmp eax, 400h ; don't cross page table 0 border! jnc @@noPTE mov edx, [esi+eax*4] and dx, 0F001h @@noPTE: cmp cx, [edi].EDDS.wNumAvail jnc @@noPTEupdate mov [edi+ecx*4+size EDDS].EDDSPT.dwPTE, edx @@noPTEupdate: inc ecx inc eax sub ebx, 1000h ja @@loop mov [edi].EDDS.wNumUsed,cx pop eax and ah,0Fh cmp cx, [edi].EDDS.wNumAvail ja @@notenoughregions2 mov word ptr [ebp].Client_Reg_Struc.Client_EBX, ax clc ret @@notenoughregions2: movzx ecx, [edi].EDDS.wNumAvail shl ecx, 12 movzx eax,ax sub ecx, eax mov [edi].EDDS.dwSize, ecx mov al, VDSERR_NAVL_TOOSMALL ;error "NumAvail too small" stc ret vds_scatterlock endp ;--- int 4b, ax=8106h ;--- DX=flags (6 & 7 may be set) ;--- ES:DI=EDDS ;--- errors VDSERR_REG_NOTLOCKED and VDSERR_DXRSVDBITSSET may happen! vds_scatterunlock proc mov al, VDSERR_DXRSVDBITSSET ; v5.85: check added test dx, not (VDSF_PTE or VDSF_NPPTE) jnz @@fail ;--- todo: check if region was locked clc ret @@fail: stc ret vds_scatterunlock endp ;--- int 4b, ax=810B: disable automatic translation for a DMA channel ;--- Client->BX=DMA channel number ;--- DX=flags (all reserved and must be 0, hiword EDX is cleared) vds_disabletrans proc mov ebx, [EBP].Client_Reg_Struc.Client_EBX @dprintf ?VDSDBG gt 1, <"VDS disable translation for channel %X",10>, bx cmp bx, MAXDMACHANNEL mov al, VDSERR_INVAL_DMACHN ;error "invalid channel" jnc @@fail mov al, VDSERR_DXRSVDBITSSET ;error "reserved flags set in DX" and edx, edx jnz @@fail if ?DMAPT mov al, VDSERR_DISCNTOVFL ;error "disable count overflow" movzx ebx, bx cmp [DmaChn+ebx*sizeof DMAREQ].cDisable,255 jz @@fail inc [DmaChn+ebx*sizeof DMAREQ].cDisable endif clc ret @@fail: stc ret vds_disabletrans endp ;--- int 4b, ax=810C: enable automatic translation for a DMA channel ;--- Client->BX=DMA channel number ;--- DX=flags (all reserved and must be 0, hiword EDX cleared) ;--- out: Z if disablecnt is now zero vds_enabletrans proc mov ebx, [EBP].Client_Reg_Struc.Client_EBX @dprintf ?VDSDBG gt 1, <"VDS enable translation for channel %X",10>, bx cmp bx, MAXDMACHANNEL mov al, VDSERR_INVAL_DMACHN ;error "invalid channel" jnc @@fail mov al, VDSERR_DXRSVDBITSSET ;error "reserved flags set in DX" and edx, edx jnz @@fail if ?DMAPT mov al, VDSERR_DISCNTUNFL ;error "disable count underflow" movzx ebx, bx cmp [DmaChn+ebx*sizeof DMAREQ].cDisable,0 jz @@fail dec [DmaChn+ebx*sizeof DMAREQ].cDisable ;--- v5.85: set ZF setz al shl al, 6 ; position of ZF in flags and byte ptr [ebp].Client_Reg_Struc.Client_EFlags,not 40h ;ZF=0 or byte ptr [ebp].Client_Reg_Struc.Client_EFlags,al endif clc ret @@fail: stc ret vds_enabletrans endp VDS_Exit proc public mov eax, [OldInt4B] mov ds:[4Bh*4],eax and byte ptr ds:[47Bh],not 20h ret VDS_Exit endp .text$03 ends .text$04 segment ;--- esi ->JEMMINIT VDS_Init proc public mov eax, ds:[4Bh*4] ;vector may be 0000:0000 mov [OldInt4B],eax cmp [esi].JEMMINIT.NoVDS, 0 jnz @@novds mov eax, [dwRSeg] shl eax, 16 mov al, [bBpTab] if BPTABLE.pInt4B add al, (BPTABLE.pInt4B shr 2) endif mov ds:[4Bh*4], eax or byte ptr ds:[47Bh],20h @@novds: ret VDS_Init endp .text$04 ENDS endif ;?VDS END ================================================ FILE: src/XMS.ASM ================================================ ;--- XMS/A20 implementation ;--- Public Domain ;--- to be assembled with JWasm or Masm v6.1+ .386 .model FLAT option dotname include jemm.inc ;common declarations include jemm32.inc ;declarations for Jemm32 include debug32.inc ;--- publics/externals include extern32.inc ?MERGE0HDL equ 1 ;std=0, 1=try to merge handles even if hdl to free has size 0 ?HINF_MSCOMP equ 1 ;std=1, 1=full MS compatibility for AH=0Eh .text$01 SEGMENT ;-- Jemm's real-mode part will call XMS A20 local disable ;-- after the monitor's initialization. With wA20==1 this might ;-- disable the (emulated) A20. FreeDOS might have problems with ;-- this if MS Himem is used. wA20==2 seems to avoid these problems. if ?A20XMS ;wA20 dw 1 ; local XMS A20 count + global A20 flag wA20 dw 2 endif if ?INTEGRATED x2max dw -1 hmamin dw 0 hma_used db 0 if ?XMS35 xms_version dw -1 endif endif if ?A20PORTS or ?A20XMS bNoA20 DB 0 bA20Stat DB 1 ; default is A20 enabled endif ife ?DYNTRAPP60 if ?A20PORTS bLast64 DB 0 endif endif align 4 XMS_Handle_Table XMS_HANDLETABLE <0,0,0,0> ife ?INTEGRATED XMSCtrlHandle dw 0 else A20Index db 0 endif .text$01 ends .text$03 segment ;--- registers EAX, ECX and ESI dont hold client values! xms_handler proc public call Simulate_Far_Ret ; emulate a RETF in v86, modifies EDX mov eax,[ebp].Client_Reg_Struc.Client_EAX ; restore EAX mov edx,[ebp].Client_Reg_Struc.Client_EDX ; restore EDX if ?INTEGRATED mov esi,[ebp].Client_Reg_Struc.Client_ESI mov ecx,[ebp].Client_Reg_Struc.Client_ECX @dprintf ?EMBDBG, <"xms_handler: ax=%X edx=%X",10>, ax, edx cmp ah,0fh ; 00-0F? jbe @@ok1 mov al,ah shr al,1 cmp al,88h/2 ; 88-89? jz @@ok2 cmp al,8Eh/2 ; 8E-8F? jz @@ok3 if ?XMS35 cmp al,0C8h/2 ; C8-C9? jz @@ok4 cmp ah,0CBh ; CB? jz @@ok5 cmp ah,0CCh ; CC? jz @@ok5 endif cmp ah,12h jbe umb_handler xor ax,ax ; everything else fails mov bl,XMS_NOT_IMPLEMENTED jmp @@dispatcher_end if ?XMS35 @@ok5: dec ah ; CB-CC -> CA-CB @@ok4: sub ah,0C8h-14h ; C8-CC -> 14-17 jmp @@ok1 endif @@ok3: sub ah,4 ; 8E-8F -> 8A-8B @@ok2: sub ah, 88h-10h ; 88-8B -> 10-13 @@ok1: cld movzx edi,ah call [xms_table+edi*4] @@dispatcher_end: mov [ebp].Client_Reg_Struc.Client_EBX, ebx mov [ebp].Client_Reg_Struc.Client_EDX, edx mov [ebp].Client_Reg_Struc.Client_ECX, ecx mov [ebp].Client_Reg_Struc.Client_EAX, eax ret align 4 xms_table label dword dd xms_get_version ;00 - AX, BX, DX dd xms_request_hma ;01 - AX, BL=0/E dd xms_release_hma ;02 - AX, BL=0/E dd xms_global_enable_a20 ;03 - AX, BL=0/E? dd xms_global_disable_a20 ;04 - AX, BL=0/E? dd xms_local_enable_a20 ;05 - AX, BL=0/E? dd xms_local_disable_a20 ;06 - AX, BL=0/E? dd xms_query_a20 ;07 - AX, BL=0/E dd xms_query_free_mem ;08 - AX, DX, BL=0/E dd xms_alloc_emb ;09 - AX, DX, BL=0/E (MS Himem, RBIL: BL=E) dd xms_free_emb ;0A - AX, BL=0/E (MS Himem, RBIL: BL=E) dd xms_move_emb ;0B - AX, BL=E dd xms_lock_emb ;0C - AX, DX, BX dd xms_unlock_emb ;0D - AX, BL=0/E (MS Himem, RBIL: BL=E) dd xms_get_handle_info ;0E - AX, DX, BX dd xms_realloc_emb ;0F - AX, BL=0/E (MS Himem, RBIL: BL=E) dd xms_ext_query_free_mem ; 88 10 dd xms_ext_alloc_emb ; 89 11 dd xms_ext_get_handle_info ; 8E 12 dd xms_ext_realloc_emb ; 8F 13 if ?XMS35 dd xms_sext_query_free_mem ; C8 14 dd xms_sext_alloc_emb ; C9 15 dd xms_move_emb ; CB 16 dd xms_sext_lock_emb ; CC 17 endif else if ?A20XMS cmp ah,10h jae umb_handler call XMS_HandleA20 ; modifies EAX, CX, BL mov word ptr [ebp].Client_Reg_Struc.Client_EAX, ax and ax,ax jnz @@a20exit_noerror mov byte ptr [ebp].Client_Reg_Struc.Client_EBX, bl @@a20exit_noerror: ret else jmp umb_handler endif endif xms_handler endp if ?INTEGRATED xms_get_version proc ;ah=0 if ?XMS35 mov ax,xms_version else mov ax,INTERFACE_VER endif mov bx,DRIVER_VER mov dx,1 ; HMA is always available ret xms_get_version endp xms_request_hma proc ;ah=1 xor ax,ax cmp [hma_used],al ; is HMA already used? mov bl,XMS_HMA_IN_USE jnz @@exit cmp dx,[hmamin] ; is request big enough? mov bl,XMS_HMAREQ_TOO_SMALL jb @@exit inc eax mov [hma_used],al ; assign HMA to caller mov bl,0 @@exit: ret xms_request_hma endp xms_release_hma proc ;ah=2 xor ax,ax cmp [hma_used],al ; is HMA used? mov bl,XMS_HMA_NOT_USED jz @@exit mov [hma_used],al ; now release it inc eax mov bl,0 @@exit: ret xms_release_hma endp xms_query_a20 proc ;ah=7 movzx ax,[bA20Stat] mov bl,0 ret align 4 xms_query_a20 endp ;--- check if handle in DX is valid ;--- let ESI point to handle ;--- this function does NOT return if handle is invalid xms_check_handle_ex: mov esi,edx xms_check_handle proc ;<--- check handle in SI push edx push eax movzx esi,si add esi,[dwRes] jc @@no_valid_handle mov eax,esi sub eax,[XMS_Handle_Table.xht_pArray] jb @@no_valid_handle xor edx,edx push ebx mov ebx,size XMS_HANDLE div ebx pop ebx or edx,edx jnz @@no_valid_handle movzx edx,[XMS_Handle_Table.xht_numhandles] cmp eax,edx jae @@no_valid_handle cmp [esi].XMS_HANDLE.xh_flags,XMSF_USED ; is it in use ?? jne @@no_valid_handle pop eax pop edx ret @@no_valid_handle: pop eax pop edx add esp,4 ;skip return address xor ax,ax mov bl,XMS_INVALID_HANDLE stc ret align 4 xms_check_handle endp ;--- allocate an unused handle in EBX ;--- C if no handle available xms_alloc_handle proc movzx ecx,[XMS_Handle_Table.xht_numhandles] ; check all handles mov ebx,[XMS_Handle_Table.xht_pArray] @@nextitem: cmp [ebx].XMS_HANDLE.xh_flags,XMSF_INPOOL jz @@found_handle ; found a blank handle add ebx,size XMS_HANDLE ; skip to next handle loop @@nextitem stc ; no free block found, error @@found_handle: ret xms_alloc_handle endp ;--- AH=09H: alloc an EMB, size DX kB ;--- out: AX=0/1, BL=0 or error code, DX=handle or 0 xms_alloc_emb proc @dprintf ?EMBDBG, <"xms_alloc_emb",10> push edx movzx edx,dx ; extend alloc request to 32-bits jmp @@xms_alloc2 ;-- 32-bit entry for function AH=89h: alloc EMB, size EDX kB xms_ext_alloc_emb:: if ?XMS35 xms_sext_alloc_emb::;<--- entry, ah=15h endif push edx @dprintf ?EMBDBG, <"xms_ext_alloc_emb",10> @@xms_alloc2: push ecx if 0 ;v5.80 check for size 0 after scan and edx,edx ; a request for 0 kB needs no free mem jz @@nullhandle endif movzx ecx,[XMS_Handle_Table.xht_numhandles] mov edi,[XMS_Handle_Table.xht_pArray] @@nextitem: cmp [edi].XMS_HANDLE.xh_flags,XMSF_FREE jnz @@skipitem ;--- filter blocks if ?XMS35 cmp ah,15h ;15h is xms_sext_alloc_emb # jnz @F test word ptr [edi].XMS_HANDLE.xh_baseK+2,0FFC0h jnz sext_mem jmp @@skipitem @@: test word ptr [edi].XMS_HANDLE.xh_baseK+2,0FFC0h jnz @@skipitem sext_mem: endif cmp edx,[edi].XMS_HANDLE.xh_sizeK ; check if it's large enough jbe @@found_block @@skipitem: add edi,size XMS_HANDLE ; skip to next handle loop @@nextitem if 1 ;v5.80 check for size 0 after scan and edx,edx ; a request for 0 kB needs no free mem jz @@nullhandle endif mov bl,XMS_ALL_MEM_ALLOCATED @@alloc_failed: pop ecx pop edx xor dx,dx ;v5.80: return DX=0 if alloc fails (XMS spec) xor ax,ax ret @@nullhandle: push ebx call xms_alloc_handle ; get a free handle in EBX mov edi,ebx pop ebx mov bl,XMS_NO_HANDLE_LEFT jc @@alloc_failed xor ax,ax ; set ZF to skip code below @@found_block: mov word ptr [edi].XMS_HANDLE.xh_flags,XMSF_USED ;clear locks also jz @@perfect_fit2 ; if it fits perfectly, go on push ebx call xms_alloc_handle ; get a free handle in BX jc @@perfect_fit ; no more handles, use all mem left mov esi,[edi].XMS_HANDLE.xh_sizeK mov [edi].XMS_HANDLE.xh_sizeK,edx sub esi,edx ; calculate resting memory add edx,[edi].XMS_HANDLE.xh_baseK ; calc new base address of free block mov word ptr [ebx].XMS_HANDLE.xh_flags,XMSF_FREE mov [ebx].XMS_HANDLE.xh_baseK,edx mov [ebx].XMS_HANDLE.xh_sizeK,esi @@perfect_fit: pop ebx @@perfect_fit2: pop ecx pop edx @dprintf ?EMBDBG, <"xms_alloc_emb, handle=%X baseK=%X sizeK=%X [res=%X]",10>, edi, [edi].XMS_HANDLE.xh_baseK, [edi].XMS_HANDLE.xh_sizeK, [dwRes] sub edi,[dwRes] mov dx,di ; return handle in DX mov bl,0 mov ax,1 @dprintf ?EMBDBG, <"xms_alloc_emb ok, handle=%X",10>, dx ret xms_alloc_emb endp if ?XMS35 ;****************************************************************************** ; query free super-extended memory ; In: AH=C8h ; Out: EAX=size of largest free super-extended block in kbytes ; EDX=total amount of free super-extended block in kbytes ; BL=0 if ok ; BL=080h -> function not implemented ; BL=081h -> VDISK is detected ; BL=0a0h -> all super-extended memory is allocated xms_sext_query_free_mem proc xor eax,eax ; contains largest free block xor edx,edx ; contains total free block push ecx push ebx movzx ecx,[XMS_Handle_Table.xht_numhandles] mov ebx,[XMS_Handle_Table.xht_pArray] nextitem: test [ebx].XMS_HANDLE.xh_flags,XMSF_FREE ; check if flagged free or in use je @F ;--- filter blocks below 4 GB ;--- 00000000-003fffffh test word ptr [ebx].XMS_HANDLE.xh_baseK+2, 0ffc0h jz @F mov esi, [ebx].XMS_HANDLE.xh_sizeK add edx, esi cmp esi, eax ; check if larger than largest jbe @F mov eax,esi ; larger, update @@: add ebx,sizeof XMS_HANDLE loop nextitem pop ebx pop ecx mov bl,0 and edx,edx jnz @F mov bl,XMS_ALL_MEM_ALLOCATED @@: ret ; success xms_sext_query_free_mem endp endif ;--- AH=88h: get free mem info ;--- out: eax=max free block ;--- edx=total free ;--- ecx=highest phys addr ;--- bl=00 (success) or error code xms_ext_query_free_mem proc @dprintf ?EMBDBG, <"xms_ext_query_free_mem",10> xor edi,edi ; highest ending address of any memory block xor eax,eax ; contains largest free block xor edx,edx ; contains total free XMS push ebx movzx ecx,[XMS_Handle_Table.xht_numhandles] mov ebx,[XMS_Handle_Table.xht_pArray] @@nextitem: test [ebx].XMS_HANDLE.xh_flags,XMSF_USED or XMSF_FREE ; check if flagged free or in use je @@skipitem if ?XMS35 ;--- filter blocks beyond 4 GB ;--- 00000000-003fffffh test word ptr [ebx].XMS_HANDLE.xh_baseK+2, 0ffc0h jnz @@skipitem endif mov esi, [ebx].XMS_HANDLE.xh_sizeK cmp [ebx].XMS_HANDLE.xh_flags,XMSF_FREE jnz @@notfree add edx, esi cmp esi, eax ; check if larger than largest jbe @@notfree mov eax,esi ; larger, update @@notfree: add esi,[ebx].XMS_HANDLE.xh_baseK cmp edi,esi jae @@skipitem mov edi,esi ; higher address, update @@skipitem: add ebx,size XMS_HANDLE loop @@nextitem pop ebx mov bl,0 and edx,edx jnz @@freeblockexists mov bl,XMS_ALL_MEM_ALLOCATED @@freeblockexists: mov ecx,edi ; highest address to ecx return value shl ecx,10 ; convert to bytes dec ecx ; relative zero ret ; success xms_ext_query_free_mem endp ;--- AH=08h: get free mem info ;--- out: AX=largest free block in kBytes ;--- DX=total amount of free XMS in kBytes ;--- BL=00 (success) or error code xms_query_free_mem proc @dprintf ?EMBDBG, <"xms_query_free_mem",10> push ecx push eax push edx call xms_ext_query_free_mem ; the call returned: ; EAX=size of largest free XMS block in kbytes ; ECX=highest ending address of any memory block (not used) ; EDX=total amount of free XMS in kbytes movzx ecx, [x2max] cmp edx,ecx ; dx = min(edx,0ffff | 7fff) jb @@edx_not_larger mov edx,ecx @@edx_not_larger: cmp eax,ecx ; ax = min(eax,0ffff | 7fff) jb @@eax_not_larger mov eax,ecx @@eax_not_larger: ; use LoWords only mov [esp+0],dx mov [esp+4],ax pop edx pop eax pop ecx ret xms_query_free_mem endp ;--- AH=0A, free emb in DX xms_free_emb proc @dprintf ?EMBDBG, <"xms_free_emb, dx=%X",10>, dx call xms_check_handle_ex ; check if DX holds a "used" handle, set ESI mov bl,XMS_BLOCK_LOCKED xor ax,ax cmp [esi].XMS_HANDLE.xh_locks,0 ; is the block locked? jnz @@exit push eax push ebx push ecx push edx ; see if there are blocks to merge mov eax,[esi].XMS_HANDLE.xh_baseK ; get base address mov edx,[esi].XMS_HANDLE.xh_sizeK mov edi, eax ; base in edi add eax, edx ; end-address in EAX ife ?MERGE0HDL mov cl, XMSF_FREE and edx, edx jnz @F mov cl, XMSF_INPOOL @@: mov [esi].XMS_HANDLE.xh_flags,cl;mark this block as free (or inpool if size is 0) jz @@done endif ;--- now scan the handle array for successor/predecessor movzx ecx,[XMS_Handle_Table.xht_numhandles] mov ebx,[XMS_Handle_Table.xht_pArray] @@nextitem: cmp [ebx].XMS_HANDLE.xh_flags,XMSF_FREE jnz @@skipitem mov edx,[ebx].XMS_HANDLE.xh_baseK cmp eax, edx ; is successor also free? je @F add edx,[ebx].XMS_HANDLE.xh_sizeK cmp edi, edx ; is predecessor free? jne @@skipitem @@: ;--- predecessor/successor in EBX cmp ebx,esi jbe @F xchg ebx,esi ; merge into the "lower" handle and free the "higher" handle @@: xor edx, edx ; merge 2 handles, then free one xchg edx, [esi].XMS_HANDLE.xh_sizeK add [ebx].XMS_HANDLE.xh_sizeK, edx ;new size is sum(hdl1.size,hdl2.size) xor edx, edx xchg edx, [esi].XMS_HANDLE.xh_baseK cmp edx, [ebx].XMS_HANDLE.xh_baseK ja @F mov [ebx].XMS_HANDLE.xh_baseK, edx ;new base is min(hdl1.base,hdl2.base) @@: mov [esi].XMS_HANDLE.xh_flags,XMSF_INPOOL mov esi,ebx @@skipitem: add ebx,sizeof XMS_HANDLE loop @@nextitem if ?MERGE0HDL mov cl, XMSF_FREE cmp [esi].XMS_HANDLE.xh_sizeK,0 jnz @F mov cl,XMSF_INPOOL @@: mov [esi].XMS_HANDLE.xh_flags,cl endif @@done: pop edx pop ecx pop ebx pop eax inc eax mov bl,0 @@exit: ret xms_free_emb endp ;--- AH=0C, lock EMB in DX, return base in DX:BX xms_lock_emb proc @dprintf ?EMBDBG, <"xms_lock_emb enter, dx=%X",10>, dx call xms_check_handle_ex ; check if DX holds "used" handle xor ax,ax ; flag error inc [esi].XMS_HANDLE.xh_locks ; increase lock counter jz @@lock_error mov esi,[esi].XMS_HANDLE.xh_baseK shl esi,10 ; calculate linear address push esi pop bx pop dx inc eax ret @@lock_error:: dec [esi].XMS_HANDLE.xh_locks mov bl,XMS_LOCK_COUNT_OVERFLOW ret xms_lock_emb endp if ?XMS35 ;****************************************************************************** ; locks a (super-extended) MB ; In: AH=0cch ; DX=XMS handle to be locked ; Out: AX=1 if block is locked ; EDX:EBX=64-bit linear address of block ; AX=0 if not successful ; BL=080h -> function not implemented ; BL=081h -> VDISK is detected ; BL=0a2h -> handle is invalid ; BL=0ach -> lock count overflow ; BL=0adh -> lock fails xms_sext_lock_emb proc @dprintf ?EMBDBG, <"xms_sext_lock_emb enter",10> call xms_check_handle_ex ; check if dx holds "used" handle xor ax,ax ; flag error inc [esi].XMS_HANDLE.xh_locks; increase lock counter jz @@lock_error mov ebx,[esi].XMS_HANDLE.xh_baseK mov edx,ebx shl ebx,10 ; calculate linear address 0-31 shr edx,22 ; calculate linear address 32-41 inc eax ret xms_sext_lock_emb endp endif ;--- AH=0D, unlock EMB in DX xms_unlock_emb proc @dprintf ?EMBDBG, <"xms_unlock_emb enter",10> call xms_check_handle_ex ; check if DX holds "used" handle xor ax,ax cmp [esi].XMS_HANDLE.xh_locks,al; check if block is locked jz @@is_notlocked dec [esi].XMS_HANDLE.xh_locks ; decrease lock counter inc eax mov bl,0 ret @@is_notlocked: mov bl,XMS_BLOCK_NOT_LOCKED ret xms_unlock_emb endp ;--- AH=8E, dx=handle ;--- out: bh=lock count, cx=free handles, edx=size in kB xms_ext_get_handle_info proc @dprintf ?EMBDBG, <"xms_ext_get_handle_info enter, dx=%X",10>, dx call xms_check_handle_ex; check if handle in DX is valid (="used") xor cx,cx ; reset free handle counter xor ax,ax movzx edx,[XMS_Handle_Table.xht_numhandles] mov edi,[XMS_Handle_Table.xht_pArray] @@nextitem: cmp [edi].XMS_HANDLE.xh_flags,XMSF_INPOOL setz al add cx,ax add edi,size XMS_HANDLE dec edx jnz @@nextitem mov bh,[esi].XMS_HANDLE.xh_locks ; store lock count mov edx,[esi].XMS_HANDLE.xh_sizeK ; store block size ; mov bl,0 ;set BL on exit? mov al,1 ret xms_ext_get_handle_info endp ;--- in: ah=0Eh ;--- in: dx=handle ;--- out: ax=0|1 ;--- out: if ax==1, dx=size in kB ;--- out: if ax==1, bl=free handle count ;--- out: if ax==1, bh=block lock count xms_get_handle_info proc push ecx push edx @dprintf ?EMBDBG, <"xms_get_handle_info enter, dx=%X",10>, dx call xms_ext_get_handle_info ;modifies edx, bx, cx; ax=0|1 or ax,ax jz @@get_handle_info_err ; if invalid handle (bl=A2h then) ;--- in cx, free handles cmp ch,0 ; bl = min(cx,0xff) jz @@handle_count_ok mov cl,0ffh @@handle_count_ok: mov bl,cl cmp edx,010000h jb @@handle_size_ok if ?HINF_MSCOMP dec ax mov bl,XMS_INVALID_HANDLE jmp @@get_handle_info_err else mov dx,0ffffh ; dx = min(edx,0xffff); endif @@handle_size_ok: mov [esp],dx @@get_handle_info_err: pop edx pop ecx ret xms_get_handle_info endp ;--- realloc emb ;--- dx=handle, ebx=new size ;--- modifies esi, edi, ax, bl xms_ext_realloc_emb proc public @dprintf ?EMBDBG, <"xms_ext_realloc_emb enter, dx=%X ebx=%X esp=%X",10>, dx, ebx, esp call xms_check_handle_ex ; dx == "used" handle? push edx ; fail if block is locked cmp [esi].XMS_HANDLE.xh_locks,0 jne @@ext_xms_locked mov edx, ebx if 1;v5.80 ;--- find the successor. movzx ecx,[XMS_Handle_Table.xht_numhandles] mov edi,[XMS_Handle_Table.xht_pArray] mov eax,[esi].XMS_HANDLE.xh_sizeK add eax,[esi].XMS_HANDLE.xh_baseK nextitem: test [edi].XMS_HANDLE.xh_flags,XMSF_FREE ;scan "free embs" only jz skipitem cmp eax,[edi].XMS_HANDLE.xh_baseK ;successor? jnz skipitem mov eax,[esi].XMS_HANDLE.xh_sizeK add eax,[edi].XMS_HANDLE.xh_sizeK ;get the total size cmp edx,eax ;new size > total size? ja @@ext_growing ;then the handle can't grow, have to copy... sub edx,[esi].XMS_HANDLE.xh_sizeK ;get the size which is additionally needed (might be < 0!) mov [esi].XMS_HANDLE.xh_sizeK, ebx add [edi].XMS_HANDLE.xh_baseK, edx sub [edi].XMS_HANDLE.xh_sizeK, edx jnz @@ext_grow_success ;remaining size > 0? mov [edi].XMS_HANDLE.xh_flags, XMSF_INPOOL ;no, so free the handle mov [edi].XMS_HANDLE.xh_baseK, 0 jmp @@ext_grow_success skipitem: add edi,sizeof XMS_HANDLE loop nextitem endif cmp ebx,[esi].XMS_HANDLE.xh_sizeK jbe @@ext_shrink_it @@ext_growing: ; growing, try to allocate a new block mov ah,11h ;11h is xms_ext_alloc_emb # test word ptr [esi].XMS_HANDLE.xh_baseK+2, 0FFC0h jz @F mov ah,15h ;15h is for super-extended memory ; test word ptr [esi].XMS_HANDLE.xh_sizeK+2, 0FFC0h ;old size >= 4G ; jnz @@ext_failed ;then fail for now @@: call xms_ext_alloc_emb ;get a new handle in DX, size EDX and ax,ax jz @@ext_failed ; got new block, copy info from old block to new block ; transfer old handle data to new location ; since the block may be > 4G and max amount to copy is 4G-2, ; the transfer is done with chunks of max. 4G - 1K. MAXCHUNK equ 4096 * 1024 - 1 movzx esi,word ptr [esp] ; get old handle xor edi,edi if ?XMS35 push edi ; bits 32-39 of src/dst.offset (pushed as dword for alignment reasons) endif push edi ; dst.offset push dx ; dst.handle push edi ; src.offset push si ; src.handle add esi,[dwRes] if ?XMS35 mov ecx,[esi].XMS_HANDLE.xh_sizeK nextchk: mov edi, ecx cmp ecx, MAXCHUNK ;remaining more than max.? jb @F mov edi, MAXCHUNK ;load edi with 4G-1K @@: else mov edi,[esi].XMS_HANDLE.xh_sizeK endif shl edi, 10 ; K to byte push edi ; length mov esi, esp if ?XMS35 mov ah, 16h ; always use superext move endif call xms_move_emb_ex if ?XMS35 mov esi, esp pop edi ; get size of current chunk in edi add [esi].XMS_MOVEX.src_offset, edi ; adjust src and dest offsets adc [esi].XMS_MOVEX.src_hi, 0 ; including bits 32-39 add [esi].XMS_MOVEX.dest_offset, edi adc [esi].XMS_MOVEX.dest_hi, 0 shr edi, 10 sub ecx, edi jnz nextchk add esp, sizeof XMS_MOVEX-2 else add esp, sizeof XMS_MOVE endif movzx esi,word ptr [esp] add esi,[dwRes] ; swap handle data so handle pointers remain valid ; handle data is 10 bytes long push edx movzx edi,dx add edi,[dwRes] mov edx,[esi+0] xchg edx,[edi+0] mov [esi+0],edx mov edx,[esi+4] mov ax,[esi+8] xchg edx,[edi+4] xchg ax,[edi+8] mov [esi+4],edx mov [esi+8],ax pop edx ; free newly allocated handle in DX with old handle data in it call xms_free_emb jmp @@ext_grow_success @@ext_no_xms_handles_left: pop ebx mov bl,XMS_NO_HANDLE_LEFT jmp @@ext_failed @@ext_xms_locked: mov bl,XMS_BLOCK_LOCKED @@ext_failed: pop edx xor ax,ax ret ;--- shrink a memory block ;--- esi = handle data ;--- edx,ebx = new size @@ext_shrink_it: mov edi,[esi].XMS_HANDLE.xh_sizeK ; get old size sub edi, edx ; calculate what's left over jz @@ext_dont_need_handle ; jump if we don't need another handle push ebx call xms_alloc_handle ; alloc a handle in EBX (edx not modified) jc @@ext_no_xms_handles_left ; exit if no more handles mov [esi].XMS_HANDLE.xh_sizeK, edx ;set new (reduced) size add edx,[esi].XMS_HANDLE.xh_baseK; calculate base of new "free" block mov [ebx].XMS_HANDLE.xh_baseK,edx mov [ebx].XMS_HANDLE.xh_sizeK,edi if 1;v5.80 ;--- if this branch is active, there's surely NO free successor ;--- so we don't need to merge. mov [ebx].XMS_HANDLE.xh_flags,XMSF_FREE pop ebx else mov word ptr [ebx].XMS_HANDLE.xh_flags,XMSF_USED mov edx,ebx ; and FREE it again - sub edx,[dwRes] ;!!! 2.2.2020: line added for v5.79 pop ebx call xms_free_emb ; to merge it with free block list endif @@ext_dont_need_handle: @@ext_grow_success: pop edx @dprintf ?EMBDBG, <"xms_ext_realloc_emb exit, esp=%X",10>, esp mov ax,1 mov bl,0 ret xms_ext_realloc_emb endp ;--- dx=handle, bx=new size xms_realloc_emb proc @dprintf ?EMBDBG, <"xms_realloc_emb enter, ebx=%X",10>, ebx push ebx ; preserve Hiword(ebx) movzx ebx,bx ; clear top 16 bit call xms_ext_realloc_emb mov [esp],bx ; modify Loword(ebx) pop ebx @dprintf ?EMBDBG, <"xms_realloc_emb exit, ebx=%X",10>, ebx ret xms_realloc_emb endp ; calculate the move src/dst address ; In: SI - handle (0 if EDX should be interpreted as seg:ofs value) ; EDX - offset ; ECX - length ; Out: SI:EAX=linear address 00-39 xms_get_move_addr proc or si,si ; translate address in EDX? jnz @@is_emb ; its segment:offset in EDX ; eax = 16*(edx high) + dx movzx eax,dx ; save offset mov dh,0 shr edx,12 ; convert segment to absolute address add eax,edx ; add offset mov edx,eax ; check that eax(address) + ecx (length) is <= 10fff0 add edx,ecx jc @@wrong_size ; negative length might wrap cmp edx,10fff0h ja @@wrong_size clc ret @@is_emb: ; it's a handle:offset pair call xms_check_handle ;check if SI holds a "used" handle if ?XMS35 push ebx xor ebx,ebx endif mov eax,ecx ; contains length add eax,edx ; assert length + offset < size if ?XMS35 adc ebx,ebx else jc @@wrong_size endif add eax,1024-1 ; round up to kB if ?XMS35 adc ebx,0 shrd eax,ebx,10 ; convert to kB units pop ebx else jc @@wrong_size shr eax,10 ; convert to kB units endif cmp eax,[esi].XMS_HANDLE.xh_sizeK ; compare with max offset ja @@wrong_size mov eax,[esi].XMS_HANDLE.xh_baseK ; get block base address mov esi,eax ; store in source index shl eax,10 ; convert from kb to linear shr esi,22 add eax,edx ; add offset into block if ?XMS35 adc esi,ebx endif ret @@wrong_size: mov bl,XMS_INVALID_LENGTH xor ax,ax stc ret align 4 xms_get_move_addr endp ;--- move extended memory block ;--- v86 DS:SI->XMS_MOVE xms_move_emb proc movzx edi,word ptr [ebp].Client_Reg_Struc.Client_DS shl edi, 4 movzx esi,si add esi,edi xms_move_emb_ex:: ; <--- entry for internal use (realloc) if ?XMS35 mov byte ptr [ebp].Client_Reg_Struc.Client_Error+3,ah ;abuse Error field to store AH endif xor ax,ax ; default to error push ecx push edx push eax push ebx @dprintf ?EMBDBG, <"xms_move_emb: siz=%X src=%X:%X dst=%X:%X",10>,\ [esi].XMS_MOVE.len, [esi].XMS_MOVE.src_handle, [esi].XMS_MOVE.src_offset, [esi].XMS_MOVE.dest_handle, [esi].XMS_MOVE.dest_offset mov ecx,[esi].XMS_MOVE.len ; get length test cl,1 ; is it even? jnz @@move_invalid_length if ?XMS35 xor ebx, ebx cmp byte ptr [ebp].Client_Reg_Struc.Client_Error+3, 16h jnz @F mov bl,[esi].XMS_MOVEX.dest_hi @@: endif push esi mov edx,[esi].XMS_MOVE.dest_offset mov si,[esi].XMS_MOVE.dest_handle call xms_get_move_addr ; get move address mov edx,esi ;save lines 32-39 in DX, since BL must be preserved pop esi jc @@copy_dest_is_wrong mov edi,eax ; store in destination index if ?XMS35 cmp byte ptr [ebp].Client_Reg_Struc.Client_Error+3, 16h jnz @F mov bl,[esi].XMS_MOVEX.src_hi @@: push edx endif mov edx,[esi].XMS_MOVE.src_offset mov si,[esi].XMS_MOVE.src_handle call xms_get_move_addr ; get move address if ?XMS35 pop edx endif jc @@copy_source_is_wrong xchg eax,esi if ?XMS35 mov bh,al mov bl,dl endif ;************************************************** ; setup finished with ; BH.ESI = source A00-A39 ; BL.EDI = destination A00-A39 ; ECX = number of words to move ;************************************************** if ?XMS35 @dprintf ?EMBDBG, <"xms_move_emb: siz(byt)=%X src(40bit)=%X%08X dst(40bit)=%X%08X",10>, ecx, bh, esi, bl, edi else @dprintf ?EMBDBG, <"xms_move_emb: siz(byt)=%X src(32bit)=%X dst(32bit)=%X",10>, ecx, esi, edi endif or ecx,ecx ; nothing to do ?? jz @@xms_exit_copy ; overlap test. start of destination block (BL.EDI) ; must either be <= start of source block (BH.ESI) ; or >= start of source block + block length (BH.ESI+ECX) ; 1. check if BL.EDI <= BH.ESI if ?XMS35 cmp bl,bh jb @@move_ok_to_start ja @F endif cmp edi,esi jbe @@move_ok_to_start if ?XMS35 @@: ; calculate source + block length: DL.EAX = BH.ESI + ECX mov dl,bh endif mov eax, esi add eax, ecx if ?XMS35 adc dl,0 ; 2. check if BL.EDI >= DL.EAX cmp bl,dl ja @@move_ok_to_start jb @@move_invalid_overlap endif cmp edi,eax jb @@move_invalid_overlap @@move_ok_to_start: if ?XMS35 and bx,bx jnz @F endif call MoveMemoryPhys if ?XMS35 jmp @@xms_exit_copy @@: movzx ax,bh movzx dx,bl call MoveMemoryPhysEx ;copy ecx bytes from [ax:esi] to [dx:edi] endif @@xms_exit_copy: pop ebx pop eax pop edx pop ecx inc eax ; success ; mov bl,0 ; BL is not set to 00 by MS Himem ret @@move_invalid_overlap: mov bl,XMS_INVALID_OVERLAP jmp @@xms_exit_copy_failure @@move_invalid_length: mov bl,XMS_INVALID_LENGTH jmp @@xms_exit_copy_failure @@copy_source_is_wrong: cmp bl,XMS_INVALID_LENGTH je @@xms_exit_copy_failure mov bl,XMS_INVALID_SOURCE_HANDLE jmp @@xms_exit_copy_failure @@copy_dest_is_wrong: cmp bl,XMS_INVALID_LENGTH je @@xms_exit_copy_failure mov bl,XMS_INVALID_DESTINATION_HANDLE jmp @@xms_exit_copy_failure @@move_a20_failure: mov bl,XMS_A20_FAILURE ; common error exit routine @@xms_exit_copy_failure: mov al,bl pop ebx mov bl,al pop eax pop edx pop ecx ret xms_move_emb endp align 4 xms_global_enable_a20: ;ah=3 xms_global_disable_a20:;ah=4 xms_local_enable_a20: ;ah=5 xms_local_disable_a20: ;ah=6 call XMS_HandleA20 ; modifies EAX, CX, BL mov word ptr [ebp].Client_Reg_Struc.Client_EAX, ax mov eax, [ebp].Client_Reg_Struc.Client_EAX mov ecx, [ebp].Client_Reg_Struc.Client_ECX ret endif ;?INTEGRATED if ?A20XMS align 4 ;--- handles XMS A20 functions, AH= ;--- 3 = global enable ;--- 4 = global disable ;--- 5 = local enable ;--- 6 = local disable ;--- modifies eax, cx, bl XMS_HandleA20 proc mov al,ah if ?A20DBG push eax @dprintf ?A20DBG, <"XMS A20 emulation, ah=%X, curr cnt=%X, curr state=%X",10>, al,[wA20], [bA20Stat] pop eax endif mov cx, word ptr [wA20] cmp al,4 jb @@glen jz @@gldi cmp al,6 jb @@loen jmp @@lodi @@glen: or ch,1 jmp @@testa20 @@gldi: and ch,not 1 jcxz @@testa20 jmp @@stillenabled @@loen: inc cl jz @@localerr jmp @@testa20 @@lodi: sub cl,1 jc @@localerr2 and cx, cx jnz @@stillenabled @@testa20: and cx, cx setnz al call A20_Set @@notchanged: mov ax,1 mov bl,0 ;BL=0 on success??? mov [wA20],cx jmp @@a20_exit @@localerr2: if 1 ;potential Delay Angel inc cl dec ch jz @@testa20 endif @@localerr: if 1 xor eax,eax mov bl,XMS_A20_FAILURE else mov ax,1 endif jmp @@a20_exit @@stillenabled: mov [wA20],cx xor eax,eax mov bl,XMS_A20_STILL_ENABLED @@a20_exit: ret align 4 XMS_HandleA20 endp endif if ?A20PORTS or ?A20XMS ;--- set PTEs for HMA to emulate enable/disable A20 ;--- in: AL=1 enable, AL=0 disable ;--- ecx+edx must be preserved A20_Set proc public cmp [bNoA20], 0 ;NOA20 option set? jnz @@exit cmp al,[bA20Stat];status change? jz @@exit mov [bA20Stat],al push edi and eax,1 shl eax,20 ;000000h or 100000h or eax, PTF_PRESENT or PTF_RW or PTF_USER mov edi, @GetPTEAddr(?PAGETAB0+256*4) ;EDI = ptr PTE for 0100000h @@spec_loop: stosd add ah,10h ;10000x, 10100x, 10200x, ... jnz @@spec_loop pop edi if 0 ;usually "16 * INVLPG" is slower than 1 * "mov cr3, eax" if ?INVLPG cmp [bNoInvlPg],0 jnz @@noinvlpg mov eax, 100000h @@nextpte: invlpg ds:[eax] add ah, 10h jnz @@nextpte ret @@noinvlpg: endif endif ; flush TLB to update page maps mov eax,CR3 mov CR3,eax @@exit: ret align 4 A20_Set endp endif if ?A20PORTS ;--- port trap handlers ;--- eax=value (for out) ;--- dx=port ;--- cl=type P60BITOFS equ size TSSSEG+32+60h/8 A20_Handle60 proc public if ?DYNTRAPP60 mov ebx, [dwTSS] and dword ptr [ebx+P60BITOFS],not 1 endif test cl,OUT_INSTR ;is it IN or OUT? jz @@input ife ?DYNTRAPP60 cmp [bLast64],0D1h ;last value written to 64 was "write output port"? jnz Simulate_IO mov [bLast64],0 endif push eax @dprintf ?A20DBG, <"A20_Handle60: write to port 60h kbc output port, al=%X",10>, al shr al,1 and al,1 call A20_Set pop eax or al,2 out dx, al ret @@input: ife ?DYNTRAPP60 cmp [bLast64],0D0h ;last value written to 64 was "read output port"? jnz Simulate_IO endif A20_Inp92:: in al,dx and al, not 2 mov ah, [bA20Stat] shl ah, 1 or al, ah @dprintf ?A20DBG, <"A20_Handle60: read port %X kbc output port, al=%X",10>, dx, al ret align 4 A20_Handle60 endp A20_Handle64 proc public test cl, OUT_INSTR jz Simulate_IO if ?DYNTRAPP60 mov ah,al and ah,0FEh cmp ah,0D0h jnz Simulate_IO mov ebx, dwTSS or dword ptr [ebx+P60BITOFS],1 else mov [bLast64],al ;save last value written to port 64h endif if ?A20DBG ; cmp al,0D1h ; jnz @@nokbcout @dprintf ?A20DBG, <"A20_Handle64: write to port 64h, al=%X",10>, al @@nokbcout: endif out dx, al ret align 4 A20_Handle64 endp A20_Handle92 proc public test cl, OUT_INSTR ;is it IN or OUT? jz A20_Inp92 @dprintf ?A20DBG, <"A20_Handle92: write to port 92h, al=%X",10>, al push eax shr al,1 and al,1 call A20_Set pop eax or al, 2 ;dont allow disable if 1 ;--- refuse reset via bit 0 = 1 and al,not 1 endif out dx, al ret align 4 A20_Handle92 endp endif .text$03 ends .text$04 segment ;--- init XMS ;--- esi -> JEMMINIT XMS_Init proc public if ?A20PORTS or ?A20XMS mov al, [esi].JEMMINIT.NoA20 mov [bNoA20],al endif if ?INTEGRATED mov ax, [esi].JEMMINIT.HmaMin mov [hmamin], ax mov ax, [esi].JEMMINIT.X2Max mov [x2max], ax @dprintf ?INITDBG, <"XMS init: x2max=%X",10>, ax mov al, [esi].JEMMINIT.A20Method mov [A20Index],al if ?XMS35 mov ax, [esi].JEMMINIT.xms_version mov xms_version,ax endif else mov ax, [esi].JEMMINIT.XMSControlHandle mov [XMSCtrlHandle],ax endif ;--- is XMS pool on? then direct access to XMS handle table required? ife ?INTEGRATED cmp [bNoPool],0 jne @@noxmsarray endif mov ecx, [esi].JEMMINIT.XMSHandleTable ;get FAR16 address of XMS handle table movzx eax, cx shr ecx, 12 and cl, 0F0h add ecx, eax ;convert to linear address in ecx ; transfer XMS table info to fixed memory location, assume size 8 (sizeof XMS_HANDLETABLE) mov eax,[ecx+0] ;get sig(byte), hdlsiz(byte) in ax and numhandles(word) into HiWord(eax) mov dword ptr [XMS_Handle_Table],eax ife ?INTEGRATED test eax,0FFFF0000h ;if size of array is null, disable pooling jz @@noxmspool endif movzx edx,word ptr [ecx].XMS_HANDLETABLE.xht_pArray+0;offset movzx eax,word ptr [ecx].XMS_HANDLETABLE.xht_pArray+2;segment shl eax,4 add eax,edx ife ?INTEGRATED and eax, eax ;if the array pointer is NULL, disable pooling jz @@noxmspool endif cmp eax, 100000h ;is handle array in HMA? jb @@nohmaadjust push eax mov cl,16 mov eax, 100000h call MapPhysPagesEx mov [PageMapHeap], edx @dprintf ?INITDBG, <"HMA shadowed at %X",10>, eax pop ecx sub ecx, 100000h add eax, ecx @@nohmaadjust: mov [XMS_Handle_Table.xht_pArray],eax @@noxmsarray: ret ife ?INTEGRATED @@noxmspool: mov [bNoPool], 1 ret endif XMS_Init endp .text$04 ends END ================================================ FILE: src/XMS.INC ================================================ ;--- XMS definitions XMS_GETVERSION equ 0h XMS_ENABLEA20 equ 5h XMS_DISABLEA20 equ 6h XMS_V2_QUERYMEM equ 8h XMS_V2_ALLOCEMB equ 9h XMS_FREEEMB equ 0Ah XMS_LOCKEMB equ 0Ch XMS_UNLOCKEMB equ 0Dh XMS_ALLOCUMB equ 10h XMS_V3_QUERYMEM equ 88h XMS_V3_ALLOCEMB equ 89h XMS_HANDLE struct xh_flags DB ? xh_locks DB ? xh_baseK DD ? xh_sizeK DD ? XMS_HANDLE ends LPXMS_HANDLE typedef ptr XMS_HANDLE ;--- XMS handle flags XMSF_FREE equ 1 ;handle describes a free EMB XMSF_USED equ 2 ;handle describes a used EMB XMSF_INPOOL equ 4 ;handle is free XMS_HANDLETABLE struct xht_sig DB ? xht_sizeof DB ? xht_numhandles DW ? xht_pArray DD ? ;converted to linear address for 32bit code! XMS_HANDLETABLE ends XMS_MOVE struct len dd ? ; +0 block length in bytes src_handle dw ? ; +4 source handle src_offset dd ? ; +6 offset into source dest_handle dw ? ; +10 destination handle dest_offset dd ? ; +12 offset into destination XMS_MOVE ends if ?INTEGRATED if ?XMS35 XMS_MOVEX struct XMS_MOVE <> src_hi db ? ; bits 32-39 of src dest_hi db ? ; bits 32-39 of dst XMS_MOVEX ends endif endif ;--- XMS error codes XMS_NOT_IMPLEMENTED equ 80h XMS_VDISK_DETECTED equ 81h XMS_A20_FAILURE equ 82h XMS_DRIVER_FAILURE equ 8eh XMS_DRIVER_FATAL equ 8fh XMS_HMA_NOT_THERE equ 90h XMS_HMA_IN_USE equ 91h XMS_HMAREQ_TOO_SMALL equ 92h XMS_HMA_NOT_USED equ 93h XMS_A20_STILL_ENABLED equ 94h XMS_ALL_MEM_ALLOCATED equ 0a0h XMS_NO_HANDLE_LEFT equ 0a1h XMS_INVALID_HANDLE equ 0a2h XMS_INVALID_SOURCE_HANDLE equ 0a3h XMS_INVALID_SOURCE_OFFSET equ 0a4h XMS_INVALID_DESTINATION_HANDLE equ 0a5h XMS_INVALID_DESTINATION_OFFSET equ 0a6h XMS_INVALID_LENGTH equ 0a7h XMS_INVALID_OVERLAP equ 0a8h XMS_PARITY_ERROR equ 0a9h XMS_BLOCK_NOT_LOCKED equ 0aah XMS_BLOCK_LOCKED equ 0abh XMS_LOCK_COUNT_OVERFLOW equ 0ach XMS_LOCK_FAILED equ 0adh XMS_ONLY_SMALLER_UMB equ 0b0h XMS_NO_UMB_AVAILABLE equ 0b1h XMS_UMB_SEGMENT_NR_INVALID equ 0b2h