[
  {
    "path": "Artistic.txt",
    "content": "The Artistic License Preamble\r\n\r\nThe intent of this document is to state the conditions under which a Package may \r\nbe copied, such that the Copyright Holder maintains some semblance of artistic \r\ncontrol over the development of the package, while giving the users of the \r\npackage the right to use and distribute the Package in a more-or-less customary \r\nfashion, plus the right to make reasonable modifications.\r\n\r\nDefinitions:\r\n\r\n\"Package\" refers to the collection of files distributed by the Copyright Holder, \r\nand derivatives of that collection of files created through textual \r\nmodification. \"Standard Version\" refers to such a Package if it has not been \r\nmodified, or has been modified in accordance with the wishes of the Copyright \r\nHolder. \"Copyright Holder\" is whoever is named in the copyright or copyrights \r\nfor the package. \"You\" is you, if you're thinking about copying or distributing \r\nthis Package. \"Reasonable copying fee\" is whatever you can justify on the basis \r\nof media cost, duplication charges, time of people involved, and so on. (You \r\nwill not be required to justify it to the Copyright Holder, but only to the \r\ncomputing community at large as a market that must bear the fee.) \"Freely \r\nAvailable\" means that no fee is charged for the item itself, though there may be \r\nfees involved in handling the item. It also means that recipients of the item \r\nmay redistribute it under the same conditions they received it. 1. You may make \r\nand give away verbatim copies of the source form of the Standard Version of this \r\nPackage without restriction, provided that you duplicate all of the original \r\ncopyright notices and associated disclaimers.\r\n\r\n2. You may apply bug fixes, portability fixes and other modifications derived \r\nfrom the Public Domain or from the Copyright Holder. A Package modified in such \r\na way shall still be considered the Standard Version.\r\n\r\n3. You may otherwise modify your copy of this Package in any way, provided that \r\nyou insert a prominent notice in each changed file stating how and when you \r\nchanged that file, and provided that you do at least ONE of the following:\r\n\r\na) place your modifications in the Public Domain or otherwise make them Freely \r\nAvailable, such as by posting said modifications to Usenet or an equivalent \r\nmedium, or placing the modifications on a major archive site such as ftp.uu.net, \r\nor by allowing the Copyright Holder to include your modifications in the \r\nStandard Version of the Package.\r\n\r\nb) use the modified Package only within your corporation or organization.\r\n\r\nc) rename any non-standard executables so the names do not conflict with \r\nstandard executables, which must also be provided, and provide a separate manual \r\npage for each non-standard executable that clearly documents how it differs from \r\nthe Standard Version.\r\n\r\nd) make other distribution arrangements with the Copyright Holder.\r\n\r\n4. You may distribute the programs of this Package in object code or executable \r\nform, provided that you do at least ONE of the following:\r\n\r\na) distribute a Standard Version of the executables and library files, together \r\nwith instructions (in the manual page or equivalent) on where to get the \r\nStandard Version.\r\n\r\nb) accompany the distribution with the machine-readable source of the Package \r\nwith your modifications.\r\n\r\nc) accompany any non-standard executables with their corresponding Standard \r\nVersion executables, giving the non-standard executables non-standard names, and \r\nclearly documenting the differences in manual pages (or equivalent), together \r\nwith instructions on where to get the Standard Version.\r\n\r\nd) make other distribution arrangements with the Copyright Holder.\r\n\r\n5. You may charge a reasonable copying fee for any distribution of this Package. \r\nYou may charge any fee you choose for support of this Package. You may not \r\ncharge a fee for this Package itself. However, you may distribute this Package \r\nin aggregate with other (possibly commercial) programs as part of a larger \r\n(possibly commercial) software distribution provided that you do not advertise \r\nthis Package as a product of your own.\r\n\r\n6. The scripts and library files supplied as input to or produced as output from \r\nthe programs of this Package do not automatically fall under the copyright of \r\nthis Package, but belong to whomever generated them, and may be sold \r\ncommercially, and may be aggregated with this Package.\r\n\r\n7. C or perl subroutines supplied by you and linked into this Package shall not \r\nbe considered part of this Package.\r\n\r\n8. The name of the Copyright Holder may not be used to endorse or promote \r\nproducts derived from this software without specific prior written permission.\r\n\r\n9. THIS PACKAGE IS PROVIDED \"AS IS\" AND WITHOUT ANY EXPRESS OR IMPLIED \r\nWARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF \r\nMERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nThe End\r\n"
  },
  {
    "path": "History.txt",
    "content": "\r\n History\r\n\r\n __.__.____: v5.87\r\n\r\n - fixed regression in v5.86: exceptions in PL0 were displayed incorrectly.\r\n - fixed: NMIs that occured while no full stack frame was setup caused\r\n   a crash.\r\n - segment registers DS/ES/FS/GS and [CS:IP] now also displayed for PL0\r\n   exceptions.\r\n - NMIs are masked while NMI exceptions are handled.\r\n - fixed: Simulate_Int() cleared bits 16-31 of register ESP.\r\n - JemmEx fix: if option XMSHANDLES was > 59 and Jemm wasn't allowed to move\r\n   its resident part into the upper memory region, it crashed.\r\n - Linux.mak: GNU makefile added to create Jemm binaries on Linux.\r\n - fixed JemmEx: unknown A20METHOD value caused a crash.\r\n - AHCICD: driver now supports long reads in \"raw mode\".\r\n\r\n 25.01.2026: v5.86\r\n - service table: added control proc member to allow Jemm to call out on\r\n   certain events; currently \"device_reboot_notify\" is the only call-out.\r\n - service table: added v86faults member - allows programs to hook into\r\n   v86 faults; used by JLOAD.\r\n - Simulate_Int: call an installed v86 hook proc before running the INT.\r\n - JemmEx, XMS AH=0Eh: invalid handle error returned if size > 65535 kB;\r\n   this is MS Himem compatible.\r\n - JLMs Reboot & FastBoot added.\r\n - fixed debug displays in 16-bit part.\r\n - fixed debug displays in Jemm16.asm, UnloadJemm.\r\n - JemmEx: option NOE820 reintroduced; it's a noop unless MAXSEXT is 0.\r\n - fixed option UNLOAD: on 80386/80486 cpus, it wasn't safe.\r\n - JLM QPIEmu: added function ax=5000h.\r\n - reintroduced compatibility with SB emulation drivers SB(E)INIT for SB PCI\r\n   cards by extending functionality of cmdline option SB.\r\n - NMI stability increased.\r\n\r\n 10.01.2025: v5.85\r\n - fixed: VDS func ax=810Ch: set ZF if disable cnt reaches zero.\r\n - fixed: VDS: added several argument checks ( mostly DX ).\r\n - fixed: ISA DMA ports 000A-000F must be handled by Jemm to detect FD accesss\r\n   even if external handler has trapped these ports.\r\n - fixed: it was assumed that value for TR to be loaded in VCPI function\r\n   ax=DE0Ch ( switch from v86- to protected-mode) had bits 0-2 cleared.\r\n - linear address region 110000-3FFFFF is now only mapped as far as needed by\r\n   the monitor program (looses SB(E)INIT compatibility).\r\n - optional kernel-debugger-aware variant calls int 68h for deinitialization.\r\n - cmdline switch NOVMW added.\r\n - easier log displays via macro @dprintf.\r\n - log displays redirected to kernel debugger if one has been detected.\r\n - buffered output of log displays removed.\r\n - switch ?SAFEKBD=1 is now default for JemmEx (restores IVT vector 15h before\r\n   reading a key press via int 16h during system errors).\r\n - JemmDbg renamed to JDeb386 and removed from Jemm source package.\r\n - building JemmExL.exe removed from Makefile; to create it, use JemmExL.mak.\r\n - CPUSTAT: optionally displays interrupt redirection bitmap.\r\n\r\n 12.02.2024: v5.84\r\n - fixed: Simulate_IO() no longer calls trap handler.\r\n - QPIEMU: new JLM that partly implements Qemm's API (QPI).\r\n - fixed: int 67h, ax=5B01h will return error code A3h if checksum invalid.\r\n - JemmDbg: removed from binary package.\r\n\r\n 06.12.2022: v5.83\r\n - AHCICD: new JLM for CD/DVD devices attached to a SATA controller running\r\n   in AHCI mode (cooked mode only).\r\n - XDMA32, XCDROM32: now accepting \"AHCI-disabled\" SATA controllers.\r\n - JemmEx: warning displayed if I15 memory blocks had to be ignored due\r\n   to insufficient XMS \"static\" handles (8); static handles increased to 10.\r\n - JemmDbg: a PL0 debugger, based on Debug/X; may be used to debug JLMs.\r\n - Makefile supports creating variants of Jemm386/JemmEx that are kernel\r\n   debugger aware.\r\n - CPUSTAT: optionally displays IOPB (trapped ports).\r\n\r\n 09.08.2022: v5.82\r\n - JemmEx: minor adjustment to always keep monitor stack dword-aligned.\r\n - fixed: MOVEXBDA option didn't check that XBDA was adjacent to conv. mem.\r\n - JLoad: fixed: hook int 2Fh (used to get JLM API entry) was incorrect.\r\n - CPUID: fixed: feature information incorrect.\r\n - CPUSTAT: fixed: options -g and -i if physical != linear address.\r\n - XDMA32: fixed: did reject many disks if /M option wasn't set.\r\n\r\n 12.04.2022: v5.81\r\n - JemmEx: XMS extension v3.51 (super-extended block moves) implemented.\r\n\r\n 18.06.2021: v5.80\r\n - for exceptions other than page faults, the monitor will display register\r\n   CR4 instead of CR2, if it exists.\r\n - Jemm might have reduced last MCB by 16 byte when loaded while UMBs\r\n   where already available and used by DOS.\r\n - Jemm does again scan the region C000-EFFF for RAM. This avoids\r\n   a system crash if upper memory has been supplied by UMBPCI and is\r\n   used by DOS.\r\n - new option MOVEXBDA for Jemm implemented.\r\n - JemmEx will recognize memory blocks activated by UMBPCI and include them\r\n   into its UMB pool. Makes option S=start-end virtually obsolete.\r\n - a warning is displayed if the extended memory block where Jemm386 will\r\n   reside is beyond the 16MB limit.\r\n - strategy to realloc an EMB to increase its size has been changed and is\r\n   now smart enough to check if the block can be increased without moving\r\n   it.\r\n - UMBM no longer requires the /I parameter; it will scan for blocks with\r\n   the UMBPCI signature.\r\n - UMBM has new option /XBDA.\r\n - UMBM now acts gracefully if line DOS=UMB is missing in CONFIG.SYS.\r\n - MOVEXBDA has got option /A to make it align the new XBDA to a kB boundary.\r\n - tool CPUSTAT split into CPUSTAT and CPUID.\r\n - emulation of Int 15h, ah=87 (move extended memory) has been enhanced\r\n   to allow to access physical memory beyond the 4 GB border via PSE-36.\r\n - JemmEx XMM now implements XMS v3.5 (extended memory > 4 GB). Default XMS\r\n   handles changed to 48 (previously 32). Added MAXSEXT option for JemmEx,\r\n   and also added JemmExL.exe, that still supports XMS v3.0 only.\r\n - JemmEx: return DX=0 if XMS alloc functions fail (XMS spec).\r\n - option I=TEST does now indeed what's documented. Previously it only\r\n   scanned regions of \"external\" ROMs ( i.e., the ROM of the VGA card ).\r\n   This means it's more \"aggressive\" now.\r\n - ALTBOOT option sends \"system reset\" command to keyboard controller.\r\n\r\n 02/02/2020: v5.79\r\n - if physical address of DMA buffer was beyond 16M, its size was reset\r\n   to zero. This is no longer done.\r\n - default linker is now JWlink.\r\n - debug displays added for mode switch calls.\r\n - Jemm will now use info returned by Int 15h, ax=E820h to mark regions\r\n   in the upper memory area as reserved. If the call was successful,\r\n   Jemm won't scan the memory region C000-EFFF for RAM.\r\n - VME (virtual mode extensions) is now off by default; there exist CPUs\r\n   that claim to support VME but actually don't.\r\n - JemmEx's built-in XMS host will now use up to 8 memory blocks listed\r\n   by ACPI (int 0x15, ax=0xe820). Previously it could just use the one\r\n   with base address 0x100000.\r\n - fixed: shrinking an EMB didn't work correctly.\r\n\r\n 07/15/2012: v5.78\r\n\r\n - bugfix: if SPLIT option was set and there were 2 ROMs to be split on\r\n   adjacent 4K-pages, the latter one may have been overwritten with zeros\r\n   (problem found in Qemu v0.15.1).\r\n - bugfix: MOVEXBDA didn't restore the XBDA at reboot. This may have been\r\n   a problem if the FASTBOOT option was active.\r\n - a warning is displayed if the page frame isn't set to a segment address\r\n   divisible by 0x400 without remainder ( i.e, FRAME=DF00 will trigger the\r\n   warning ).\r\n - MEMSTAT additionally displays location and size of XBDA.\r\n - source package only: extrsect.exe replaced by bin2inc.exe; bin2inc.exe\r\n   is needed only if Masm is used to assemble the source.\r\n\r\n 06/20/2012: v5.77\r\n\r\n - commandline option I=A000-B7FF didn't increase DOS low memory, the\r\n   region became just the first UMB.\r\n - tool MOVEXBDA added.\r\n\r\n 05/25/2012: v5.76\r\n\r\n - an 64kB EMS page frame may now be located inside address space included\r\n   with 'I=XXXX'.\r\n\r\n 05/21/2012: v5.75a\r\n\r\n - no changes in JemmEx/Jemm386, just bugfixes in XDMA32.DLL and\r\n   XCDROM32.DLL.\r\n\r\n 05/31/2011: v5.75\r\n\r\n - bugfix: when Jemm detected Ctrl-Alt-Del, it didn't reset the keyboard\r\n   status. This may have caused a locked keyboard if FASTBOOT was set.\r\n\r\n 04/14/2011: v5.74\r\n\r\n - tool MEMSTAT added, which will display the size of the reserved\r\n   BIOS-ROM region.\r\n - Make process simplified, all sources are assembly now.\r\n\r\n 12/02/2010: v5.73\r\n\r\n - bugfix: int 67h, ah=51h didn't complain if handle was invalid in \r\n   v5.61-5.72.\r\n - tool CPUSTAT added.\r\n - for emulation of Int 15h, AH=87h (extended memory block move), the\r\n   region 100000h-10FFFFh is now handled similar to the rest of extended\r\n   memory. Previously it was handled as \"real-mode\" memory, which means\r\n   that access did depend on the A20 state. This new behavior matches\r\n   more closely the BIOS implementation of this interrupt.\r\n - option V86EXC0D added.\r\n\r\n 03/13/2008: v5.72\r\n\r\n - source has been made compatible with JWasm; OTOH, TASM compatibility\r\n   has been abandoned.\r\n\r\n 02/25/2008: v5.71\r\n\r\n - bugfix: DMA buffer size of 0 (D=0) didn't work in v5.70.\r\n - bugfix: JLOAD v5.70 made wrong assumptions about Jemm's TSS location,\r\n   which caused JLM sample JCLOCK2 to fail.\r\n - some minor changes to make it compatible with JWasm.\r\n\r\n 01/30/2008: v5.70\r\n\r\n - bugfix: the \"copy memory\" routine wasn't reentrant, making an XMS\r\n   \"block move\" or a call of Int 15h, ah=87h from within an ISR slightly\r\n   unsafe.\r\n - bugfix: int 67h, ah=57h returned error 8Ah instead of 93h when a \r\n   region exceeded the size of the EMS handle.\r\n - bugfix: int 67h, ah=57h accepted any region type (just 00 and 01 are\r\n   valid ones).\r\n - bugfix: int 67h, ax=5701h accepted overlapping EMS regions. Now status\r\n   97h is returned.\r\n - bugfix: since v5.61 (support for mappable pages in conventional memory)\r\n   option NODYN often failed to make all memory available which was\r\n   requested with the MIN=x option.\r\n - bugfix: exception 11h in v86-mode caused a loop in Jemm's exception\r\n   handler.\r\n - bugfix: due to an alignment problem the low byte of max. free memory\r\n   block for XMS v2 (X2MIN) was 0, as a result the default block size was\r\n   65280 (0xFF00) instead of 65535 kB (JemmEx only, bug since v5.64).\r\n - int 67h,ax=5701h, \"memory exchange\" is now also done with interrupts\r\n   enabled if client's interrupt flag is set.\r\n - port 60h access is now trapped only if the keyboard controller's output\r\n   port is accessed.\r\n - tool XMSSTAT and source of EMSSTAT added.\r\n\r\n 01/07/2008: v5.69\r\n\r\n - descriptions/changelogs for XDMA32 and XCDROM32 extracted from this\r\n   readme into separate files XDMA32.TXT and XCDROM32.TXT.\r\n - memory block copies are now done with interrupts enabled - if client's\r\n   interrupt flag is set. This avoids interrupt latency and makes the\r\n   previously implemented \"interrupt windows\" obsolete. It also avoids\r\n   problems with Jemm's HLT emulation causing IRQs to be misinterpreted\r\n   as exceptions (08, 09, 74, ...) on some cpus.\r\n - bugfix: VDS scatter/gather lock with region size 0 returned 0 entries,\r\n   but 1 entry must be returned in any case (MS Emm386 does as well).\r\n - bugfix: there was a risk that a ROM located contiguously behind another\r\n   ROM in address space wasn't detected.\r\n - bugfix: if SPLIT option was set and a split ROM page followed an UMB\r\n   page (which is possible if ROM size is < 4 kB), the ROM part of this\r\n   page was added to the UMB.\r\n - bugfix: synchronization of VCPI and EMS free memory count did work\r\n   for VCPI only, that is, allocating all VCPI memory didn't reduce free\r\n   EMS memory to zero.\r\n - bugfix: the last XMS memory block allocated for EMS/VCPI memory was\r\n   likely to be too large, thus wasting XMS memory.\r\n - VCPI/EMS memory alloc/release speed-up.\r\n - EMSSTAT -v displays more VCPI infos.\r\n - v86 exceptions >= 8 are routed to v86 interrupt 06h only if this vector\r\n   has been changed by another program.\r\n - exception 10h (floating point) is now detected and displayed, thus avoiding\r\n   the hazardous call of video interrupt 10h (MS Emm386 also detects\r\n   exception 10h, but just allows to reboot then).\r\n\r\n 12/03/2007: v5.68\r\n\r\n - bugfix: JLOAD's \"Lock DMA\" function ignored flag to check for 64 kB\r\n   border crossing.\r\n - NMIs occuring inside the monitor are now silently routed to v86-mode.\r\n   Previous Jemm versions displayed an exception dump.\r\n - XDMA32 v1.0 added.\r\n - XCDROM32 changed to v1.1.\r\n\r\n 10/13/2007: v5.67\r\n\r\n - bugfix: I=TEST always scanned 4 kB chunks, although the last page\r\n   of a ROM might be smaller (512, 1024, ... bytes).\r\n - bugfix: if I=TEST found an \"unused\" page in a ROM, v5.64-5.66 messed\r\n   up an internal counter. As a result, there was a small risk that the\r\n   begin of another ROM following this one in the address space wasn't\r\n   detected.\r\n - error code displayed in exception dump.\r\n - selected A20 method now displayed in \"verbose\" mode only.\r\n - tool EMSSTAT added.\r\n\r\n 08/24/2007: v5.66\r\n\r\n - bugfix: GDT, which has been moved to nonshared space in v5.6, moved\r\n   to shared space again. Required by applications written for the Phar\r\n   Lab TNT DOS extender.\r\n\r\n 08/19/2007: v5.65\r\n\r\n - Standard reboot handler made fully MS Emm386 compatible. The ALTBOOT\r\n   option is now a dummy.\r\n - auto-scanning for a page frame will now ensure that the frame begins\r\n   on a 16 kB boundary. This is MS Emm386 compatible.\r\n\r\n 07/12/2007: v5.64\r\n\r\n - bugfix: for FASTBOOT, IVT vectors 78h-7Fh should be reset to 0000:0000.\r\n   This wasn't done.\r\n - SPLIT option added.\r\n\r\n 06/22/2007: v5.63\r\n\r\n - bugfix: NOEMS didn't disable EMS in v5.62\r\n - bugfix [JemmEx only]: MAXEXT option wasn't displayed in help screen.\r\n\r\n 06/19/2007: v5.62\r\n\r\n - bugfix: int 67h, ah=5301h (set handle name) didn't check if this name\r\n   was already assigned (and return status A1h if yes).\r\n - bugfix: using I=TEST might have reduced amount of UMBs by 4 kB.\r\n - bugfix: in v5.61, Int 67h, ax=5900h still returned 8 as mapping context\r\n   size, which usually is too small now (# of physical pages are > 4).\r\n - bugfix: in v5.61, if Jemm was loaded from the command line and \r\n   remained in low memory, it didn't hook into the driver chain.\r\n - bugfix: in v5.61, there was a risk that Jemm386 didn't install an\r\n   UMB handler (and then the UMBs were \"lost\").\r\n - int 67h, ah=5Bh implemented (alternate map register sets).\r\n - the EMS system handle is now populated with the mappable pages below\r\n   A000h, as described in EMS 4.0 docs (and also done by MS Emm386).\r\n\r\n 06/04/2007: v5.61\r\n\r\n - bugfix: Int 67h, ah=51h (EMS reallocate) might have failed to increase\r\n   the handle's pages although there were enough free pages available.\r\n   This was a design flaw. Now for each EMS handle a linked lists of\r\n   allocated pages is maintained.\r\n - bugfix: trying to release the NULL handle will now reallocate it with\r\n   size 0. This is the behavior specified in the EMS docs.\r\n - bugfix: there was a chance that 4 VCPI pages got lost. They could be\r\n   allocated, but weren't found anymore when trying to release them.\r\n - bugfix: memory for UMBs is no longer contained in EMS system handle 0.\r\n   This handle should only contain pages which \"backfill\" conventional\r\n   memory (segment addresses below A000h).\r\n - bugfix: int 67h, ah=56h (alter page map and call) ignored the \"old\"\r\n   member in the \"map_and_call\" parameter.\r\n - value of GDTR, IDTR and TR exposed by Jemm. This allows JLoad to avoid\r\n   usage of opcodes SGDT/SIDT, which caused problems with most virtualizers.\r\n - allocating a 4kB page with VCPI is now significantly faster.\r\n - B=xxxx parameter supported. Pages 4000-9FFF are mappable by default.\r\n\r\n 05/24/2007: v5.6\r\n\r\n - bugfix: if Jemm had to emulate an IRET in v86-mode and the copy of the\r\n   flag register onto the stack was \"invalid\" (IOPL != 3), a GPF occured\r\n   at the next IO-sensitive instruction in v86-mode.\r\n - bugfix [JemmEx only]: NODYN option caused a GPF on initialization.\r\n - dword and string I/O now simulated correctly for trapped port access.\r\n - GDT and IDT moved to nonshared memory.\r\n - the ring 0 stack has been increased to 4 kB and moved to nonshared space.\r\n   This allows \"nested execution\" without using the client's v86-stack.\r\n - JLOAD.EXE added. Jemm splitted in 2 packages: binaries and source.\r\n   Some sample JLMs added (XCDROM32, KEYBGR, ...).\r\n - new EMMXXXX0 function exposes some of Jemm's internal call tables,\r\n   thus allowing external modules (JLOAD) to implement a 32bit ring 0 API.\r\n - switched to a table-oriented IO-port trapping implementation, which\r\n   allows external modules (JLOAD) to hook/take over port trapping.\r\n\r\n 05/04/2007: v5.52\r\n\r\n - bugfix: a bug introduced in v5.5 caused garbage to be displayed if\r\n   option I=B000-B7FF was used.\r\n\r\n 05/03/2007: v5.51\r\n\r\n - bugfix [JemmEx only]: XMS memory realloc function (ah=0Fh) caused the\r\n   stack not to be dword-aligned, which slows things down and might cause\r\n   troubles if an interrupt occurs during a memory block move.\r\n - JemmEx: EMS block moves (int 67h, ah=57h) now also done with interrupt\r\n   window if client's IF is set.\r\n\r\n 04/29/2007: v5.5\r\n\r\n - bugfix: if Ctrl-Alt-Del was pressed with FASTBOOT active and an event\r\n   proc for the PS/2 mouse was set, a crash might have occured. Now the\r\n   PS/2 mouse is reset by Jemm before rebooting.\r\n - bugfix: S=XXXX was ignored if it was the only region to be added as UMBs.\r\n - in v5.46, FASTBOOT only worked by pressing Ctrl-Alt-Del. Now\r\n   it should work as well if int 19h is called.\r\n - in v5.46, FASTBOOT always needed ALTBOOT to be set and therefore has set\r\n   this option internally. This is no longer done.\r\n - JEMMEX.EXE added.\r\n - JemmEx opens interrupt window in block moves (int 15h, ah=87h and\r\n   XMS function ah=0Bh) each 8 kB if client's IF is set.\r\n\r\n 03/27/2007: v5.46\r\n\r\n - bugfix: free EMS pages not always reported correctly.\r\n - bugfix: translation DMA for 16-bit controller (channels 4-7) didn't work.\r\n - bugfix: Jemm always displayed \"can't continue, please reboot\" on\r\n   invalid opcode exceptions in v5.45.\r\n - MIN= no longer restricted to a maximum of 512 MB (EMS pages still are).\r\n - FASTBOOT option implemented, JEMFBHLP.EXE added.\r\n\r\n 03/02/2007: v5.45\r\n\r\n - bugfix: in v5.40, if no option was entered when installing Jemm, it\r\n   might have displayed some garbage.\r\n - bugfix: invalid opcode generated by Jemm if running on a cpu < 80386.\r\n - bugfix: XMS function 11h (free UMB) always returned an error.\r\n - option UNLOAD added.\r\n - option RAM/NORAM implemented.\r\n - valid range for page frame set with FRAME=xxxx now 8000-E000.\r\n - the MAX= prefix can be omitted now. Makes the cmdline more MS Emm386\r\n   compatible.\r\n - memory required for UMBs will no longer reduce the memory available for\r\n   EMS. This is similiar to what MS Emm386 does.\r\n - installing Jemm from the command line now prohibited if a DPMI host\r\n   is detected.\r\n\r\n 02/20/2007: v5.4\r\n\r\n - some minor bugfixes.\r\n - Jemm now moves its resident part to the first UMB which it supplies.\r\n - NOHI option implemented.\r\n - Jemm now uses the true (=zero-based) flat memory model.\r\n\r\n 01/11/2007: v5.34\r\n\r\n - bugfix: Jemm always hooked int 15h, ah=4Fh, thus always acting as\r\n   if option ALTBOOT was set (bug introduced in v5.25).\r\n\r\n 01/06/2007: v5.33\r\n\r\n - bugfix: Jemm erroneously might have disabled EMS\r\n - reboot code slightly changed, there were still some machines (old 80486?)\r\n   were Ctrl-Alt-Del didn't work.\r\n\r\n 11/13/2006: v5.32\r\n\r\n - removed an optimisation which PKZIP didn't like.\r\n\r\n 11/09/2006: v5.31\r\n\r\n - some minor bugfixes in function int 67h, ah=57h.\r\n - bugfix: wrong register contents were displayed in v5.30 for exceptions\r\n   occuring in protected-mode.\r\n - v86-mode int 06 handler moved to protected-mode, thus further reducing\r\n   Jemm's conventional memory usage.\r\n\r\n 10/27/2006: v5.30\r\n\r\n - bugfix: int 67h, ah=57h (move/exchange memory region) didn't always\r\n   invalidate the TLB cache for the EMS memory to copy/exchange.\r\n - bugfix: int 67h, ax=4F01h didn't work.\r\n - int 67h, ah=55h/56h implemented.\r\n - int 67h, ah=57h now works with overlapping memory regions. Furthermore\r\n   it is now ensured that all EMS pages involved in the copy operation are\r\n   valid before copy actually begins.\r\n\r\n 10/24/2006: v5.29\r\n\r\n - bugfix: the error msg when the EMS call to map UMBs failed contained\r\n   garbage as \"function\".\r\n - int 67h, AH=58h will now return success even with NOEMS.\r\n - now error code 80h, not 91h is returned if functions int 67h,\r\n   ah=41h or 47h or 48h are called and no page frame is defined.\r\n - NOINVLPG option added.\r\n - I=TEST is now a bit less restrictive and will increase compatibility\r\n   with MS-Emm386.\r\n - help text displayed by Jemm adjusted where it was wrong or unclear.\r\n\r\n 10/13/2006: v5.28\r\n\r\n - bugfix, VDS function scatter/gather lock (int 4Bh, ax=8105h): if lower\r\n   12 bits of offset in EDDS were <> 000h, this function always returned\r\n   the first page as a separate region.\r\n - bugfix, VDS function scatter/gather lock: if this function failed with\r\n   AL=9, it did not always set the correct value for items required to\r\n   describe the full region.\r\n - bugfix: int 67h, ah=53h (get/set handle name) and ah=54h (get handle\r\n   directory) wrongly assumed that they won't be called with handles other\r\n   than 0000 if NOEMS is set.\r\n - bugfix: int 67h, ah=57h (move/exchange memory region) didn't work without\r\n   a page frame (NOEMS or FRAME=NONE).\r\n\r\n 10/07/2006: v5.27\r\n\r\n - bugfix: allocating a EMS block with zero pages (int 67h, ah=5Ah, bx=0)\r\n   returned with AH=0, but did not return a valid handle in DX.\r\n - bugfix: calling int 67h, AH=51h modified AL register.\r\n\r\n 09/30/2006: v5.26\r\n\r\n - bugfix: VDS functions 03, 04, 07 and 08 may have failed if bit 1 of DX\r\n   was set (request to copy in/out of DMA buffer) and registers BX,CX\r\n   were <> 0.\r\n - bugfix: 1 kB of DMA buffer may have been inaccessible.\r\n - bugfix: releasing a DMA buffer with size > 0 and < 1 kB always failed.\r\n\r\n 09/22/2006: v5.25\r\n\r\n - bugfix: if option ALTBOOT is set and Ctrl-Alt-Del being pressed has\r\n   been detected, then the real-mode int 15h chain is called first before\r\n   rebooting. This gives other hookers (SmartDrv) a chance to cleanup.\r\n\r\n 09/19/2006: v5.24\r\n\r\n - bugfix: int 67h, ax=5001h (implemented in v5.2) didn't work.\r\n - bugfix: setting Jemm options from the command line did reset\r\n   the options which were not explicitely entered.\r\n - PGE/NOPGE options added.\r\n\r\n 09/14/2006: v5.23\r\n\r\n - bugfix: if the amount of preallocated EMS memory (MIN=xxxx) was\r\n   not below largest XMS memory block - 384 kB, the amount of free/\r\n   available EMS pages reported by Jemm was up to 384 kB too large.\r\n - implemented VDS function 05 (scatter lock) returning PTEs instead of\r\n   \"regions\". This makes Jemm fully support the VDS API.\r\n - some message texts adjusted to make it clear what is an error, a\r\n   warning or just informational.\r\n - warning about detected system memory region now displays all such\r\n   regions, start and size.\r\n - if wanted EMS memory has to be reduced, a warning is displayed.\r\n - UMBM.EXE added.\r\n - EMX option added.\r\n\r\n 09/09/2006: v5.22\r\n\r\n - bugfix: the DMA buffer 64kb alignment may cause up to 32 kB to be lost\r\n   for EMS/VCPI memory. This wasn't taken into account, and may have\r\n   resulted in Jemm unable to alloc UMBs if options NODYN and\r\n   MIN=<value below size of UMBs+32> were set.\r\n - bugfix: in displayed text \"wanted preallocated EMS memory <nnn> kB\"\r\n   <nnn> was always blank.\r\n - space for UMBs now rounded up to next 16 kB boundary (previously it\r\n   was rounded to 32 kB).\r\n\r\n 09/07/2006: v5.21\r\n\r\n - bugfix: in v5.2 if an invalid command line parameter was entered, the\r\n   program displayed garbage.\r\n - bugfix: in v5.2 options setting both options NOEMS and NOVCPI might\r\n   have caused Jemm to run out of memory for UMBs.\r\n - XMS dynamic memory allocation was automatically disabled if NOEMS and\r\n   NOVCPI options were set, but this is no longer useful since option NODYN\r\n   exists and VCPI can be dynamically set/reset from the command line.\r\n - DMA buffer size now always rounded up to a full 4 kB boundary.\r\n - LOAD option added which allows to install Jemm from the command line.\r\n\r\n 09/06/2006: v5.2\r\n\r\n - bugfix: writing to \"ROM\" page FF000 if ALTBOOT wasn't set caused a crash.\r\n - bugfix: if XMS handle array was in HMA and A20 got disabled results may\r\n   have been \"unexpected\".\r\n - bugfix: \"disable A20 local\" emulation did not return an error code if\r\n   count was already 0.\r\n - there was slightly more XMS memory allocated than required.\r\n - support for Int 67h, ax=5001h implemented.\r\n - option MIN= no longer deactivates XMS dynamic memory allocation.\r\n   option NODYN added, option EMM= discarded.\r\n - If XMS memory must be preallocated (because XMS host doesn't export its\r\n   handle table) and MIN= option is not set, Jemm allocates half of\r\n   extended memory (but not more than 32 MB) for EMS/VCPI memory.\r\n - Assembler: MASM may be used instead of TASM.\r\n - Compiler: Open Watcom WCC or MS VC 1.5 may be used instead of TC.\r\n - Linker: Digital Mars OPTLINK may be used instead of TLINK.\r\n - protected-mode code switched to 32-bit.\r\n - port trapping added to the A20 enable/disable emulation. XMS hook is\r\n   still required due to the \"A20 always on\" case, which causes most XMS\r\n   hosts not to touch the ports at all.\r\n\r\n 08/30/2006: v5.1\r\n\r\n - bugfix: the DMA master mask register was read, which doesn't work\r\n   on all machines and may have caused an \"illegal instruction exception\"\r\n   if the floppy disk was accessed. This bug was introduced in v5.00.\r\n - bugfix in EMS function 4Eh.\r\n - bugfix: A20 emulation works now if no UMB handler has been installed.\r\n - DMA buffer size may be set by commandline option.\r\n - VDS functions 07/08 (request/release DMA buffer) and 09/0A (copy\r\n   into/out of DMA buffer) implemented. VDS functions 03/04 (lock/unlock\r\n   region) extended so that they call functions 07/08 if needed.\r\n - NOVCPI does no longer require NOEMS.\r\n - ALTBOOT (re)implemented, it hooks int 15h, ah=4Fh now.\r\n - VME mode may now be set/reset with VME/NOVME options.\r\n - LIBM.LIB no longer needed to create the JEMM386.EXE binary.\r\n\r\n 08/25/2006: v5.01\r\n\r\n - bugfix: \"A20 enabled\"-count was 0 on startup, should have been 1.\r\n - bugfix: unsupported VDS functions caused a debug display, which didn't\r\n   work and may have caused corruption of monitor data. Debug displays\r\n   removed.\r\n - bugfix: original int 4Bh vector may have been 0000:0000 - if this is\r\n   true, it must never be chained to of course. Instead carry flag is set.\r\n - VDS functions 0Bh and 0Ch (disable/enable translation) implemented.\r\n\r\n 08/23/2006: v5.00\r\n\r\n - EMMXXXX0 device communication implemented\r\n - bugfix: DMA buffer is ensured to begin on a 64 kB physical address\r\n   boundary.\r\n - bugfix: XMS handles which are no longer used now get the \"in pool\" flag\r\n   set, not the \"free\" flag.\r\n - Pentium+ VME extensions supported.\r\n - A20 disable emulation activated.\r\n\r\n 08/17/2006: v5.00pre\r\n\r\n - bugfix: in VCPI protected mode entry switch to host stack before\r\n   context is switched.\r\n - command line option S=xxxx added.\r\n - option NOALTBOOT deleted.\r\n - the full physical memory is no longer mapped in the address space.\r\n   This reduces extended memory usage a lot on machines with large amounts\r\n   or RAM, since no page tables are needed.\r\n - VDS code moved to extended memory, reducing conventional memory usage\r\n   by about 1 kB.\r\n\r\n---------------------------------------------------------------------\r\n\r\n  c't/Harald Albrecht: created the v86 monitor part and EMS functions\r\n  1990\r\n\r\n  tom ehlert: implemented UMB support,\r\n  2001-2004   reboot hooks (keyboard ctrl-alt-del, BIOS FFFF:0000)\r\n              int 15h ext memory copy,\r\n              VDS lock function,\r\n              introduced english comments.\r\n\r\n  michael devore: implemented support for VCPI, VDS (partially), dynamic\r\n  2004-2006           XMS memory allocation, EMS v4.0 (partially)\r\n  - Modified for >64M and VCPI support\r\n  - Updated for EMS 4.0, some extended API returns unsupported, this will\r\n    change as requested or needed by other software requirements\r\n  - Documented EMS 4.0 only supports up to 32M (0x800 pages), but some EMS\r\n    drivers allow more than 32M.      The EMS 4.0 spec could be extended to 1G-32K\r\n    (map value of 0xFFFF means unmap) without breaking the spec API,\r\n    but unfortunately breaking unsophisticated programs which get confused.\r\n    I have arbitrarily decided to allow up to 32M of EMS allocations,\r\n    leaving the high bit of page allocation alone since specific drivers\r\n    may make use of high bit values other than 0xFFFF for their own purposes,\r\n    or may only check high bit for unmap -- as FreeDOS EMM386 does.\r\n      - Minor English corrections where useful for comprehension\r\n\r\n    Michael Devore's changes are not copyrighted and are released\r\n    to the public domain.\r\n    This does not affect copyright on the rest of the code.\r\n\r\n"
  },
  {
    "path": "Html/Readme.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 FINAL//EN\">\r\n<HTML>\r\n<HEAD>\r\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=IBM-850\">\r\n<META NAME=\"author\" CONTENT=\"Japheth\">\r\n<META NAME=\"keywords\" content=\"Jemm386, JemmEx\">\r\n</HEAD>\r\n<BODY>\r\n<TABLE BORDER=0 CELLSPACING=4 CELLPADDING=4 WIDTH=\"100%\" HEIGHT=\"100%\">\r\n<TR BGCOLOR=#E0E0E0>\r\n<TD>\r\n<pre>\r\n\r\n Contents\r\n\r\n  1.   About Jemm\r\n  2.   Features\r\n  3.   Commandline Options\r\n  4.   Technical Details\r\n  4.1  EMS Implementation Notes\r\n  4.2  Emulation of privileged Opcodes\r\n  4.3  IOPL Sensitive Instructions\r\n  4.4  VMWare Detection\r\n  4.5  Option FASTBOOT\r\n  4.6  Option MAXSEXT\r\n  4.7  Option NOEMS\r\n  4.8  Option SB\r\n  5.   Compatibility\r\n  6.   Errors and Warnings\r\n  7.   Troubleshooting, Hints\r\n  8.   Additional Tools\r\n  8.1  UMBM\r\n  8.2  JEMFBHLP\r\n  8.3  CPUSTAT\r\n  8.4  MEMSTAT\r\n  8.5  XMSSTAT\r\n  8.6  EMSSTAT\r\n  8.7  VCPI\r\n  8.8  MOVEXBDA\r\n  8.9  CPUID\r\n  9.   License\r\n\r\n\r\n 1. About Jemm\r\n\r\n Jemm is an \"Expanded Memory Manager\" (EMM), based on the source of FreeDOS\r\n Emm386. It should work with MS-DOS and compatible DOSes, including FreeDOS.\r\n Like other EMMs it installs the following services:\r\n\r\n - uses extended memory to simulate expanded memory (EMS) according to \r\n   EMS v3.2 and EMS v4.0.\r\n - upper memory blocks (UMB) where drivers and resident programs may\r\n   be loaded, thus increasing available free DOS memory.\r\n - mapping RAM to the video address segments A000-AFFF and B000-B7FF.\r\n - VCPI services to allow DOS applications running in V86-mode to\r\n   switch to protected mode. VCPI also implements a simple memory management.\r\n - VDS API to give drivers/applications some control over DMA and physical\r\n   addresses in V86-mode.\r\n\r\n There exist 2 versions of Jemm:\r\n\r\n - Jemm386: standard version which needs an external eXtended Memory Manager\r\n            (XMM; examples: Himem[S]X, MS Himem, XMGR ) to be loaded.\r\n - JemmEx:  extended version which has an XMM already included.\r\n\r\n JemmEx most likely is the better choice because it will need less DOS\r\n memory than an external XMM + Jemm386.\r\n\r\n\r\n 2. Features\r\n\r\n The main purpose of making Jemm was to make it use less resources than\r\n other EMMs, without making compromises regarding speed or compatibility.\r\n The results currently are:\r\n\r\n - Jemm386 needs just 128 bytes DOS upper memory. JemmEx uses more, mostly\r\n   because it includes the XMS handle array which is located in DOS memory.\r\n   With 32 XMS handles JemmEx needs 496 bytes DOS memory.\r\n - Jemm's extended memory usage is:\r\n   + 44 kB for Jemm itself\r\n   + 64 kB for the DMA buffer (default size)\r\n   + xx kB for UMBs mapped in the first MB\r\n   +  4 kB fixed amount for EMS handle management.\r\n   + xx kB variable amount for EMS/VCPI memory management. For each 1.5 MB\r\n        VCPI memory 64 bytes are needed, for each EMS page 5 bytes are needed.\r\n        For the default values (120 MB VCPI, 2048 EMS pages) this is 16 kB.\r\n - VCPI shared address space in extended memory is just 4 kB.\r\n\r\n Other features are:\r\n\r\n - Jemm can be loaded and unloaded from the command line. Disadvantage:\r\n   DOS won't care about UMBs supplied this way.\r\n - CPUs which provide the Virtual-8086 Mode Extensions (Pentium+) are\r\n   actively supported, which increases the emulation speed.\r\n - the FASTBOOT option shortens reboot time.\r\n - the SPLIT option can gain additional DOS high memory.\r\n - all \"lengthy\" memory copy operations ( EMS, VDS, Int 15h, JemmEx: XMS )\r\n   are done with interrupts enabled.\r\n - Exceptions in protected-mode are detected and displayed.\r\n - a (rudimentary) API is supplied which allows to extend Jemm. [ JLoad\r\n   uses this API to add support for 32bit protected-mode extensions (JLMs). ]\r\n - JemmEx supports XMS v3.5, allowing access to extended memory beyond the\r\n   4 GB barrier. See XMS35.txt for technical details.\r\n\r\n\r\n 3. Commandline Options\r\n\r\n For a list of available options enter:\r\n\r\n JEMM386/JEMMEX -?\r\n\r\n Option     Comment\r\n ----------------------------------------------------------\r\n A20/NOA20  will set the A20 enable/disable emulation accordingly.\r\n ALTBOOT    this option is meant to select an alternate reboot handler,\r\n            if the standard handler doesn't work. The current implementation\r\n            sends a \"system reset\" command to the keyboard controller.\r\n B=xxxx     specify lowest segment address for EMS banking (default=4000,\r\n            min=1000).\r\n D=nnn      this option will set the size of the DMA buffer to &lt;nnn&gt; kB.\r\n            The default size is 64 kB, max size is 128 kB. Will always be\r\n            rounded up to next 4 kB.\r\n EMX        option to prevent EMX DOS extender from quickly terminating with\r\n            message \"out of memory (RM)\" on machines with large amounts\r\n            of RAM (&gt; 256 MB). This is optionally because it makes Jemm\r\n            behave not fully VCPI compliant, but shouldn't hurt usually.\r\n FASTBOOT   option might allow a fast reboot. There is no guarantee that it\r\n            will work, though, see the \"Option FASTBOOT\" chapter below.\r\n FRAME=nnn  instructs Jemm to use a certain page frame. Accepted are frame\r\n            values from 8000 to E000 or NONE. The page frame should start\r\n            at the beginning of a physical EMS page, that is, the frame \r\n            address should be divisible by 0x400 without remainder. FRAME=NONE\r\n            disables the page frame, but there are quite some programs which\r\n            won't run with this setting. A page frame below A000 should \r\n            be set only on computers with 512 kB of conventional memory.\r\n            Usually it's better to let Jemm find a page frame on its own, \r\n            because choosing an address which is not free might cause troubles.\r\n I=mmmm-nnnn force Jemm to use a memory range for UMBs (or page frame).\r\n            &lt;mmmm&gt; must be &gt;= A000. Specifying a range which is not\r\n            really free may give \"unexpected\" results.\r\n I=TEST     scan ROMs for unused address space. This option will regard any\r\n            4 kB page containing identical byte values in read-only memory as\r\n            \"unused\".\r\n LOAD       installs Jemm from the command line. Be aware that UMBs\r\n            cannot be provided this way, since DOS will ignore them.\r\n [MAX=]nnn  limit the maximum amount of memory to be allocated for VCPI (and\r\n            EMS, if &lt;nnn&gt; is below 32 MB). The MAX= prefix is optional (a\r\n            number found as option will be handled as if it has a MAX=\r\n            prefix). Default value is 120 MB.\r\n MIN=nnn    preallocates &lt;nnn&gt; kB of XMS memory thus making sure this\r\n            memory is truly available for EMS/VCPI. If MIN is higher than\r\n            MAX, MAX will be adjusted accordingly. Default for &lt;nnn&gt; is 0.\r\n MOVEXBDA   move XBDA into UMB, thus increasing low DOS memory. This option\r\n            may cause a system \"lock\" because the XBDA might contain buffers\r\n            used for DMA operations. In such cases either use MOVEXBDA.EXE or\r\n            UMBPCI+UMBM instead.\r\n NOCHECK    disallows access via Int 15h, AH=87h to address regions which\r\n            aren't backuped by RAM. As for Jemm386: be aware that some old \r\n            XMMs return wrong values for highest physical RAM - then this \r\n            option may cause strange results. Also, this option may prevent\r\n            real-mode programs from using the VESA LFB.\r\n NODYN      disables XMS dynamic memory allocation. Jemm will allocate\r\n            XMS memory for EMS/VCPI on initialization. Default is size\r\n            of largest XMS block/2, but max. 32MB. With option MIN=xxx one\r\n            may override this.\r\n NOEMS      disables EMS support.\r\n NOHI       this option will prevent Jemm from moving its resident part\r\n            into upper memory. If no UMBs are installed by Jemm, NOHI\r\n            has no effect.\r\n NOINVLPG   disables usage of INVLPG opcode on 80486+ cpus. Might be useful\r\n            if Jemm runs in a virtual environment (see \"Troubleshooting\").\r\n NOVCPI     disables VCPI. Option can be set from the command line.\r\n NOVMW      disables VMWare detection.\r\n PGE/NOPGE  options will enable/disable the Page Global Enable feature\r\n            on Pentium Pro+ cpus. This allows to mark all PTEs for the\r\n            real-mode address space 0-110000h as \"global\", which gives\r\n            a slight speed benefit for VCPI applications. Option is off by\r\n            default because some DOS extenders will not work with PGE\r\n            enabled. Also setting both PGE + NOINVLPG will not work.\r\n RAM/NORAM  will instruct Jemm to supply UMBs or not. RAM is the default.\r\n            NORAM is intended to be used when loading Jemm from the\r\n            cmdline, in which case adding UMBs might be less useful.\r\n S=mmmm-nnnn add a memory region (which must be in range C800-EFFF) as UMB.\r\n            The memory region has to be filled with Shadow-RAM activated by\r\n            UMBPCI, which must have been loaded *before* Himem/JemmEx.\r\n            NOTE: since v5.80, this option is virtually obsolete, because\r\n            either Jemm should find RAM activated by UMBPCI automatically, or,\r\n            if UMBM.EXE has been loaded in CONFIG.SYS, the RAM is already used\r\n            by DOS.\r\n SB         Soundblaster driver compatibility mode on.\r\n SPLIT      if ROMs are found which don't end exactly at a 4 kB boundary\r\n            then setting this option will increase available UMB space. ROM\r\n            sizes are defined in 0.5 kB units, so there might be up to 3.5 kB\r\n            wasted in the ROM's last 4 kB page. There is a small catch: the\r\n            full 4k page will be made writeable, including the ROM part.\r\n UNLOAD     uninstalls Jemm from the command line. Uninstalling the EMM\r\n            might confuse resident programs which rely on EMS or VCPI but\r\n            didn't ensure this configuration to keep unchanged (by allocating\r\n            an EMS page) while they are running.\r\n V86EXC0D   makes Jemm route General Protection Faults (GPF) that occur in\r\n            V86-mode to Int 0Dh. Without this option they are routed to\r\n            Int 06h. This option should only be set if a resident program\r\n            is to be installed that can handle GPFs in V86-mode.\r\n VCPI       (re)enables VCPI. Option can be set from the command line.\r\n VERBOSE    talk a bit more during the load process (abbreviation: /V).\r\n VME/NOVME  options will enable/disable using the V86 Mode Extensions\r\n            on Pentium+ CPUs. These options can be set from the command line.\r\n            NOVME is the default.\r\n X=mmmm-nnnn exclude a memory range to be used as UMBs or page frame.\r\n            &lt;mmmm&gt; must be &gt;= A000.\r\n X=TEST     will exclude all upper memory regions which contain byte values\r\n            other than 00 or FF.\r\n\r\n JemmEx additionally understands:\r\n\r\n A20METHOD:x   select A20 switch method.\r\n               Possible values for &lt;x&gt;:\r\n               ALWAYSON    Assume that A20 line is permanently ON\r\n               BIOS        Use BIOS to toggle the A20 line\r\n               FAST        Use port 92h, bypass INT 15h test\r\n               PS2         Use port 92h, bypass PS/2 test\r\n               KBC         Use the keyboard controller\r\n               PORT92      Use port 92h always\r\n HMAMIN=k      set minimum amount in kB to get the HMA (default=0, max=63).\r\n MAXEXT=l      limit extended memory controlled by XMM to &lt;l&gt; kB.\r\n MAXSEXT=l     limit extended memory beyond 4GB barrier (\"super-extended\") to\r\n               &lt;l&gt; kB; setting MAXSEXT=0 will make JemmEx behave like a\r\n               v3.0 XMM.\r\n NOE801        don't use int 15h, ax=E801h to get amount of extended memory.\r\n NOE820        don't use int 15h, ax=E820h to get amount of extended memory;\r\n               option is ignored unless MAXSEXT=0 is set.\r\n X2MAX=m       limit for free extended memory in kB reported by XMS V2 \r\n               (default 65535). It is reported that some old applications\r\n               need a value of &lt;m&gt;=32767.\r\n XMSHANDLES=n  set number of XMS handles (default=48, min=10, max=128).\r\n\r\n\r\n 4. Technical Details\r\n\r\n 4.1 EMS Implementation Notes\r\n\r\n - The number of EMS pages is limited to 2048 (= 32 MB). It can be increased\r\n   up to 32768 pages (= 512 MB) by setting MIN=&lt;nnn&gt; to a value higher than\r\n   32 MB. However, this memory will then be preallocated, and not be available\r\n   as XMS memory. Some applications will not work if EMS &gt; 32 MB.\r\n\r\n - There is one memory pool, which is shared by EMS and VCPI.\r\n\r\n - The following EMS 4.0 functions aren't implemented. Calling these\r\n   functions will return error code 84h in register AH:\r\n\r\n   + Int 67h, AH=5Ch, prepare expanded memory manager hardware for warm boot\r\n   + Int 67h, AH=5Dh, enable/disable OS/E\r\n\r\n\r\n 4.2 Emulation of privileged Opcodes\r\n\r\n To provide Expanded Memory an EMM Emulator like Jemm runs the cpu in\r\n so-called V86-mode. This mode does not allow to run privileged opcodes.\r\n Some of these opcodes which might be useful for application programs\r\n are emulated by Jemm. These are:\r\n\r\n - mov &lt;special_reg&gt;, &lt;reg&gt;   ;special_reg = CRn, DRn, TRn\r\n - mov &lt;reg&gt;, &lt;special_reg&gt;   ;reg = eax, ebx, ecx, edx, esi, edi, ebp\r\n - WBINVD\r\n - INVD\r\n - WRMSR\r\n - RDMSR\r\n - RDTSC\r\n - HLT\r\n\r\n Those instructions may generate an exception 0x0D if they occur in V86-mode\r\n ( for RDTSC, this depends on a bit in register CR4 ). Jemm's exception handler\r\n examines the opcode causing the exception and - if the instruction is to be\r\n \"emulated\" - runs it in ring 0.\r\n\r\n\r\n 4.3 IOPL Sensitive Instructions\r\n\r\n Jemm runs V86-mode code with IOPL 3. That means, the instructions that are\r\n sensitive to IOPL in V86-mode - CLI, STI, PUSHF, POPF, INT xx, IRET - will\r\n run at full speed, without causing a GPF.\r\n\r\n\r\n 4.4 VMWare Detection\r\n\r\n As default, Jemm tries to detect if it's running under VMWare. This is done\r\n by reading port 0x5658 with value \"VMXh\" in register EAX. If the detection\r\n is successful, Jemm assumes that address range E8000-EFFFF is not to be used.\r\n However, range E8000-EBFFF may still be included with the \"I=\" option.\r\n\r\n The VMWare detection can be disabled with option NOVMW.\r\n\r\n\r\n 4.5 Option FASTBOOT\r\n\r\n The FASTBOOT option may work with many versions of DOS. However, there are\r\n quite a few restrictions. To speed up the boot process, FASTBOOT tries to\r\n avoid resetting the system. This implicates that the interrupt vector table\r\n and at least the disk devices can be reset to the state before DOS has been\r\n loaded. Resetting the interrupt vector table requires some cooperation from\r\n DOS. Other things that may get in the way are:\r\n\r\n - Moving the XBDA into an UMB with option MOVEXBDA is critical, because to\r\n   move it back into conventional memory might not work for various reasons.\r\n\r\n - older FreeDOS versions may need JEMFBHLP.EXE to be installed prior to the\r\n   XMM (Himem). Possibly this might also be needed for MS-DOS versions &lt; 5.\r\n\r\n - UMBPCI allows to activate \"Shadow RAM\", but there's no option to deactivate\r\n   it. This may confuse Jemm, since it can only detect RAM activated by UMBPCI\r\n   if UMBPCI's signature is found.\r\n\r\n - A boot loader (Grub) might have inserted code in conventional memory to\r\n   modify hard disks numbers. There's no guarantee that this code survives the \r\n   fastboot process.\r\n\r\n\r\n 4.6 Option MAXSEXT\r\n\r\n JemmEx only: this option limits amount of \"super-extended\" memory. Setting\r\n MAXSEXT=0 disables this feature. If enabled, JemmEx must exclusively manage\r\n this type of memory; to achieve this, interrupt 15h, AX=0xE820 is hooked and\r\n any memory region beyond 4 GB that's marked as available is changed to\r\n \"reserved\" ( tool MEMSTAT may be used to verify this behavior ).\r\n\r\n\r\n 4.7 Option NOEMS\r\n\r\n Despite its name, this option does not fully disable EMS. What's done is:\r\n\r\n - device name changed from EMMXXXX0 to EMMQXXX0\r\n - no page frame installed\r\n - EMS memory pool is limited to 8 MB\r\n\r\n The change of the device name makes the usual EMM detection routines fail.\r\n OTOH, the interrupt 67h API is fully functional. Most of this is pretty\r\n similar to what \"recent\" MS Emm386 versions do - the only difference is\r\n that they won't limit the memory pool to 8 MB, but install it in its full\r\n size (32 MB).\r\n\r\n\r\n 4.8 Option SB\r\n\r\n Option SB is supposed to help Creative's SoundBlaster emulation drivers for\r\n SB PCI devices. It does 2 things:\r\n\r\n a) It translates an exception 0Dh with error code 0x1A that may have occured\r\n    in V86 mode to an INT 3. It's unclear what this is supposed to fix.\r\n b) Since v5.86, it \"identity maps\" region 0-0x3fffff in the first page table.\r\n    This mapping was done generally until v5.85.\r\n\r\n With b), Creative's SBINIT/SBEINIT tools should again be compatible with Jemm.\r\n However, due to the rather hackish nature of those drivers ( they modify\r\n Jemm's IDT/GDT and page tables ), one may experience negative effects on\r\n stability.\r\n\r\n\r\n 5. Compatibility\r\n\r\n Jemm is NOT an MS Emm386 clone. Differences are:\r\n<UL> \r\n<LI> - obviously commandline options differ.\r\n</LI>\r\n<LI> - the device driver API, accessed by opening file \"EMMXXXX0\" and\r\n   then issueing IOCTL cmds, differs.\r\n</LI>\r\n<LI> - Jemm doesn't support the so-called GEMMIS API.\r\n</LI>\r\n<LI> - Jemm doesn't support the IO port trapping API of MS Emm386.\r\n</LI>\r\n<LI> - Jemm supports UMBs (Upper Memory Blocks) accessed via the XMS API, but the\r\n   management is different: unlike MS Emm386, there's no chain of UMBs, linked\r\n   with a 16-byte header ( somewhat similar to DOS MCBs ). Instead, the UMB\r\n   addresses and sizes are stored in a table in extended memory, not accessible\r\n   by external programs. The table can hold up to 8 blocks.\r\n</LI>\r\n<LI> - MS Emm386 tries to map all of physical memory in its linear address space,\r\n   in such a way that linear and physical addresses are identical. This isn't\r\n   done by Jemm, because it may consume quite a lot of physical memory for paging\r\n   tables - at least if 4KB pages are used, as it is the case with MS Emm386.\r\n   See option SB, though, which triggers this kind of mapping for the first 4 MB.\r\n</LI>\r\n<LI> - Both Jemm and MS Emm386 supply the NOVCPI commandline option. However, the\r\n   effects differ: with Jemm, VCPI function DE01h will be deactivated by this\r\n   option, thus refusing to start any VCPI client. In contrast, MS Emm386's\r\n   NOVCPI makes the monitor still offer the full API, but no VCPI memory can\r\n   be allocated.\r\n</LI>\r\n</UL>\r\n\r\n\r\n 6. Errors and Warnings\r\n\r\n Errors will make Jemm386/JemmEx abort the load process, while warnings\r\n will be displayed and then the load process continues.\r\n\r\n - \"Error: no XMM found, required\"\r\n   Jemm386 only. Jemm386 needs an XMM - try Himem(S)X.\r\n\r\n - \"Error: XMM already installed\"\r\n   JemmEx only. Since JemmEx has its very own XMM, it cannot tolerate\r\n   another one in the system.\r\n\r\n - \"Error: DPMI host detected\"\r\n   Both Jemm386 and JemmEx refuse to load if a DPMI host has been detected.\r\n\r\n - \"Error: No supported A20 method detected\"\r\n   JemmEx only. JemmEx is NOT compatible with this system.\r\n\r\n - \"Error: enable A20 failed\"\r\n   JemmEx only. JemmEx is NOT compatible with this system.\r\n\r\n - \"Error: can't get I15 memory status\"\r\n   JemmEx only. Int 15h, AX=E820h ( and the fallbacks, Int 15h,\r\n   ax=E801h/ah=88h ) failed or returned an amount of 0 kB extended memory.\r\n   Tool memstat may help to find the problem.\r\n\r\n - \"Error: can't get XMS memory status\"\r\n   Jemm386 only. The XMS host reports no extended memory. Tool xmsstat\r\n   may give a hint what's wrong.\r\n\r\n - \"Error: can't allocate enough I15 memory\"\r\n   JemmEx only. Run tool memstat to see how much extended memory the\r\n   system is reporting.\r\n\r\n - \"Error: can't allocate enough XMS memory\"\r\n   Jemm386 only. Run tool xmsstat to see how much extended memory the\r\n   XMM is reporting.\r\n\r\n - \"Error: can't lock XMS memory\"\r\n   Jemm386 only. Well, this shouldn't happen. Jemm386 needs the physical\r\n   address of the memory block where it is to reside. The XMM most likely has\r\n   to be replaced.\r\n\r\n - \"Warning: unknown A20 method\"\r\n   JemmEx only. Option A20METHOD was given with an invalid method.\r\n\r\n - \"Warning: option 'xxx=' rejected, invalid syntax\"\r\n   The option is ignored.\r\n\r\n - \"Warning: E820 - too many ext memory blocks, block xxxxxxxx ignored!\"\r\n   JemmEx only. Occurs if int 15h, ax=e820h returns more than 10 available\r\n   memory blocks. It's very well possible that super-extended memory isn't\r\n   available if this warning has occured. The only cure is to adjust constant\r\n   ?XMS_STATICHDLS in file jemm16.inc and recompile JemmEx.\r\n\r\n - \"Warning: address of allocated EMB (=XXXXXX) is beyond 16MB\" \r\n   Jemm386 only. Means that Jemm386 is forced to reside beyond the physical\r\n   16MB barrier. No problem for Jemm386 itself, but the VDS API requires a\r\n   DMA buffer that is located below that 16MB barrier. So one is better off not\r\n   to ignore this warning. To fix it: a) ensure that Jemm386 is loaded just\r\n   after the XMM; b) replace the XMM, use HimemX2.exe instead of HimemX.exe\r\n   (if an extended memory block is allocated, HimemX2 tries to return the\r\n   block with the lowest address that satisfies the request).\r\n\r\n - \"Warning: no suitable page frame found, EMS functions limited.\"\r\n   Most programs using EMS won't work without a page frame, so this warning\r\n   should not be ignored. To analyze the problem, a first step may be to load\r\n   Jemm from the command line, with the /V option to see why it cannot find a\r\n   64 kB region to be used as page frame. Possible reasons are:\r\n    - UMBPCI is used. Memory regions activated by UMBPCI are detected by Jemm\r\n      and hence won't be used for mapping the Page Frame.\r\n    - BIOS marks 128 kB ( E0000-FFFFF ) as reserved in Int 15h, ax=E820h.\r\n    Using the video segment A000 as page frame is useful only for very specific\r\n   applications and cannot be recommended. Using regions below A000 are even\r\n   more problematic on machines with 640 kB conventional memory, since the\r\n   address range is already \"owned\" by DOS. \r\n    If it's not possible to make Jemm use a valid page frame, it should be setup\r\n   with option NOEMS.\r\n\r\n - \"Warning: EMS banking start too low, set to 0x1000.\"\r\n   This warning only occurs if option B=xxxx has been used inappropriately.\r\n\r\n - \"Warning: MIN has been reduced to n kB\"\r\n   Warning occurs if the amount given for MIN exceeds available free memory. \r\n\r\n - \"Warning: wanted DMA buffer size too large, set to 128 kB\"\r\n   Warning occurs if option D=nnn has been used with a value larger than\r\n   the maximum of 128 (kb).\r\n\r\n - \"Warning: XMS host doesn't provide handle array, dynamic memory allocation off!\"\r\n   Jemm386 only. Jemm wants the XMM to reveal its handle table. If this feature\r\n   isn't available, Jemm has to allocate its memory for EMS and VCPI statically\r\n   on startup.\r\n\r\n\r\n 7. Troubleshooting, Hints\r\n\r\n - If Jemm halts or reboots the machine, the following combinations\r\n   of parameters may help to find the reason. Generally, Jemm386 should be\r\n   loaded immediately after the XMM (HIMEM[S]X.EXE, HIMEM.SYS), and the XMM\r\n   itself should be the first device driver to be loaded. For testing, it\r\n   might also help to prevent DOS from loading in the HMA and/or not to\r\n   use UMBs at all.\r\n\r\n   - X=A000-FFFF NOHI NOINVLPG\r\n\r\n     This is the safest combination. If this doesn't work, Jemm most\r\n     likely isn't compatible with the current DOS/BIOS; for example,\r\n     the BIOS might try to unconditionally use \"unreal\" mode to access\r\n     USB mass storage devices - with Jemm, this will cause an exception.\r\n\r\n   - X=TEST NOHI NOINVLPG\r\n\r\n     This is slightly less safe, since Jemm will scan the upper memory\r\n     region to find \"unused\" address ranges usable for UMBs. If this\r\n     doesn't work, one has to manually set X=xxxx-yyyy to finally\r\n     find the region which causes the troubles. Tool MEMSTAT may be used\r\n     to find the address region which is reserved for the ROM-BIOS.\r\n\r\n - Jemm can be loaded from the command line with option LOAD. This may be\r\n   helpful if there are so many displays during the boot process that one\r\n   cannot read them carefully. Also, option /V should be added to make\r\n   Jemm talkative. To load Jemm386 from the command line, ensure that a\r\n   XMM has been loaded previously. For JemmEx, no XMM must be loaded, since\r\n   it is included.\r\n\r\n - Jemm has been verified to run on the following virtual environments:\r\n\r\n    Qemu, VMware, VirtualPC, Bochs, VirtualBox\r\n\r\n   However, it might be necessary to set option NOINVLPG.\r\n\r\n - Some DOS programs will not work if EMS is enabled without a page frame.\r\n   MS CodeView v4.1, for example, may refuse to start then.\r\n\r\n - Some DOS programs will crash if too much VCPI memory is offered. Jemm's\r\n   default is 120 MB, it can be changed with option MAX=xxx. Popular\r\n   programs that may cause troubles are programs that use Borland's DPMI hosts\r\n   DPMI16BI.OVL/DPMI32VM.OVL (coupled with RTM.EXE/32RTM.EXE). Setting Jemm\r\n   option MAX=32752K ( that's 32MB minus 16kB) should help; alternately, try\r\n   setting environment variable \"DPMIMEM=MAXMEM 16383\".\r\n\r\n - The JEMM ;-) DOS extender (used for \"Strike Commander\" and \"Privateer\")\r\n   isn't compatible with the VME option. This requirement is a strong sign\r\n   that this extender switches to V86 mode on its own, which is a bad idea\r\n   for a VCPI client.\r\n\r\n - Unlike MS Emm386, Jemm does not try to identity-map extended memory in its\r\n   address space. This prevents the PL0 debugger 386SWAT from \"intruding\".\r\n\r\n - If Jemm is installed from the commandline, loading the CTMOUSE driver\r\n   v1.9x and v2.0x might cause an exception. Adding option NOHI or NORAM\r\n   when installing Jemm should avoid that. In CTMOUSE v2.1 the bug has\r\n   been fixed.\r\n\r\n - With some BIOSes disk access speed slows down significantly in V86-mode.\r\n   Most likely this is because the BIOS routines want to avoid using DMA in\r\n   this mode. Loading XDMA32 ( and XCDROM32 ) might fix that.\r\n\r\n - FreeDOS regrettably accepts just one UMB provider. This makes it impossible\r\n   for example to use UMBM.EXE to supply UMB D000-DFFF and then tell Jemm to\r\n   additionally supply B000-B7FF as UMB. MS-DOS has no such problems.\r\n\r\n - Although including the region B000-B7FF might work in most cases, one\r\n   should be aware that this region is not really free, it's used by the\r\n   VGA \"monochrome\" video modes.\r\n\r\n - To make Jemm behave (almost) like MS Emm386 regarding UMBs one should\r\n   set both options X=TEST and I=TEST. For better MS Emm386 compatibility\r\n   one might also consider to restrict VCPI memory to 32 MB by adding\r\n   option MAX=32M. If NODYN is used, one should also set at least MIN=256K.\r\n\r\n - The NOVCPI option may be used to setup an environment similiar to Windows\r\n   DOS boxes:\r\n   - install Jemm with VCPI enabled\r\n   - install a DPMI host residently (HDPMI, DPMIONE, ...)\r\n   - disable VCPI with the NOVCPI option\r\n   This forces any DOS extended application to either use DPMI or abort.\r\n\r\n - If Jemm displays warning\r\n\r\n     System memory found at XXXX-XXXX, region might be in use\r\n\r\n   then that region is not used by Jemm. If you are sure that the region\r\n   is ok to be used, include it with 'I=XXXX-XXXX'.\r\n\r\n - The I=XXXX commandline option may be used to include the VGA \"graphics\"\r\n   segment A000h. It might be possible to increase DOS conventional memory\r\n   up to 736 kB by option I=A000-B7FF. However, there are quite a few\r\n   hurdles that may cause unexpected results:\r\n   - conventional memory is increased only if the region to include is\r\n     adjacent to current memory. On newer machines, there's very often a\r\n     \"hole\", caused by the Extended BIOS Data Area (XBDA or EBDA); thus\r\n     the included region just becomes an UMB and won't increase lower memory.\r\n     Option MOVEXBDA or tool MOVEXBDA.EXE may fix this issue.\r\n   - Once address space A000h is remapped, any attempts to run programs that\r\n     use VGA graphics most likely will cause a crash.\r\n   - Depending on the VGA-BIOS it may happen that some non-graphics functions\r\n     won't work anymore and may also cause a crash. Not unusual is that the \r\n     current text font becomes corrupted ( text font bitmaps must be copied \r\n     from ROM to VGA memory when a video text mode is set ).\r\n   - DOS conventional memory cannot be increased anymore once UMB have been \r\n     added to the DOS memory pool. So you cannot use tool UMBM here!\r\n   - if MOVEXBDA.EXE causes a system lock during boot, add the /A option to \r\n     the line in CONFIG.SYS that loads the driver. This aligns the XBDA to \r\n     a kB boundary, which may cure the lock.\r\n   - the GRUB boot loader may, under certain conditions, allocate a chunk\r\n     of conventional memory BELOW the XBDA. In this case conventional memory\r\n     cannot be increased anymore, even if the XBDA is moved.\r\n\r\n\r\n 8. Additional Tools\r\n\r\n 8.1 UMBM\r\n\r\n UMBM is a small tool only useful in conjunction with Uwe Sieber's UMBPCI.\r\n The main purpose of UMBM is to allow DOS to load the XMM into upper memory.\r\n This driver must be loaded before Jemm386 and therefore DOS can't use UMBs \r\n provided by Jemm386. For JemmEx, UMBM is usually not needed, since the XMM is \r\n included and needs no low memory.\r\n\r\n How does UMBM work? It expects to find a \"shadow\" RAM region activated by\r\n UMBPCI. Then it installs inself as a temporary XMS host which just provides\r\n support for allocating UMBs. This is enough for most DOSes to grab the\r\n memory (note: for this to work line DOS=UMB is required in CONFIG.SYS). After\r\n the UMBs have been allocated, UMBM will be removed from memory automatically.\r\n\r\n Additionally, UMBM understands the /XBDA option. This will cause UMBM\r\n to move the XBDA into its first UMB, thus freeing even more low DOS memory.\r\n \r\n UMBs based on \"shadow\" RAM, as it is supplied by UMBPCI+UMBM, may have\r\n limitations depending on the motherboard's chipset. Sometimes the memory\r\n is inaccessible for DMA (read the documentation coming with UMBPCI for more\r\n details). OTOH, logical and physical addresses for these UMBs are identical,\r\n which may be an advantage, especially for the XBDA. \r\n\r\n Enter \"UMBM\" on the command line and read the example how to add UMBM\r\n to CONFIG.SYS. UMBM has been tested to run with MS-DOS 6/7 and FreeDOS.\r\n\r\n\r\n 8.2 JEMFBHLP\r\n\r\n JEMFBHLP is a tiny device driver only needed if both FreeDOS and Jemm's\r\n FASTBOOT option are used. FreeDOS v1.0 does not provide the information\r\n that Jemm needs for FASTBOOT to work, so this driver tries to cure FreeDOS'\r\n incapability. It saves the values for interrupt vectors 15h and 19h at\r\n 0070h:0100h, which is the MS-DOS compatible way to do it.\r\n\r\n In more recent FreeDOS versions this problem has been fixed.\r\n\r\n\r\n 8.3 CPUSTAT\r\n\r\n CPUSTAT displays some system registers and tables. Most of the registers\r\n aren't accessible in v86-mode, so - for Jemm - this program may be seen as\r\n a test if the emulation of privileged opcodes works as expected.\r\n\r\n Optionally, if cpu runs in V86-mode, CPUSTAT may display GDT, IDT, paging\r\n tables and informations stored in the task state segment of the v86-monitor.\r\n Run \"CPUSTAT -?\" for more details.\r\n\r\n\r\n 8.4 MEMSTAT\r\n\r\n MEMSTAT may be used to display the machine's memory layout, as it is\r\n returned by the BIOS. The most interesting infos are:\r\n\r\n - address region reserved for the ROM-BIOS\r\n - total amount of free memory\r\n\r\n\r\n 8.5 XMSSTAT\r\n\r\n XMSSTAT can be used to display the current status of the installed XMM.\r\n It allows to check current values of JemmEx options X2MAX, MAXEXT and\r\n XMSHANDLES.\r\n\r\n\r\n 8.6 EMSSTAT\r\n\r\n EMSSTAT can be used to display the current status of the installed EMM.\r\n It works with any EMM, not just Jemm.\r\n\r\n\r\n 8.7 VCPI\r\n\r\n VCPI may be used to display the VCPI status of the installed EMM.\r\n With option -p it will display the page table entries for the conventional\r\n memory.\r\n\r\n\r\n 8.8 MOVEXBDA\r\n\r\n MOVEXBDA is a device driver supposed to move the Extended BIOS Data\r\n Area ( XBDA or EBDA ) to low DOS memory. If an XBDA exists, it is usually\r\n located just below the A000 video segment, with a size of 1-12 kB.\r\n\r\n Some DOS versions will move the XBDA on their own - at least if its size\r\n doesn't exceed 1 kB; then MOVEXBDA is not needed and will do nothing if \r\n launched. Also, both JemmEx and UMBM have the ability to move the XBDA as \r\n well; those tools move the XBDA to upper memory, thus increasing low DOS \r\n memory. However, sometimes MOVEXBDA may be the best option available -\r\n because moving the XBDA into an UMB supplied by Jemm (or UMBPCI) may cause\r\n the system to \"lock\".\r\n\r\n MOVEXBDA should work with any EMM.\r\n\r\n\r\n 8.9 CPUID\r\n\r\n CPUID displays cpu features returned by the CPUID instruction.\r\n\r\n\r\n 9. License\r\n\r\n - JEMM386/JEMMEX: partly Artistic License (see ARTISTIC.TXT for details)\r\n - UMBM:     Public Domain\r\n - JEMFBHLP: Public Domain\r\n - CPUSTAT:  Public Domain\r\n - MEMSTAT:  Public Domain\r\n - XMSSTAT:  Public Domain\r\n - EMSSTAT:  Public Domain\r\n - VCPI:     Public Domain\r\n - MOVEXBDA: Public Domain\r\n - CPUID:    Public Domain\r\n\r\n Binaries and source are distributed in separate packages. The binaries'\r\n package has a 'B' suffix in its name, the package containing the source\r\n has a 'S'.\r\n\r\n Japheth\r\n\r\n</pre>\r\n</TD></TR>\r\n</TABLE>\r\n</BODY>\r\n</HTML>\r\n"
  },
  {
    "path": "Include/FILEACC.INC",
    "content": "\r\n;--- dos file access for JLMs\r\n;--- although addresses are linear, they must\r\n;--- be within 64 kB distance of v86 DS register.\r\n\r\nw\ttextequ <word ptr>\r\n\r\n;--- open file (for reading)\r\n;--- edx = file name\r\n;--- out: ax=handle\r\n\r\nOpenFile proc\r\n\tmovzx eax,word ptr [ebp].Client_Reg_Struc.Client_DS\r\n\tshl eax, 4\r\n\tsub edx, eax\r\n\tmov w [ebp].Client_Reg_Struc.Client_EDX, dx\r\n\tmov w [ebp].Client_Reg_Struc.Client_EAX, 3D00h\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tmov ax,w [ebp].Client_Reg_Struc.Client_EAX\r\n\tbt [ebp].Client_Reg_Struc.Client_EFlags,0\r\n\tret\r\nOpenFile endp\r\n\r\n;--- close file\r\n;--- ebx = file handle\r\n\r\nCloseFile proc\r\n\tmov w [ebp].Client_Reg_Struc.Client_EBX, bx\r\n\tmov w [ebp].Client_Reg_Struc.Client_EAX, 3E00h\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tbt [ebp].Client_Reg_Struc.Client_EFlags,0\r\n\tret\r\nCloseFile endp\r\n\r\n;--- get file size\r\n;--- ebx = file handle\r\n;--- out: eax=file size\r\n\r\nGetFileSize proc\r\n\tmov w [ebp].Client_Reg_Struc.Client_EBX, bx\r\n\tmov w [ebp].Client_Reg_Struc.Client_EAX, 4202h   ;goto EOF\r\n\tmov w [ebp].Client_Reg_Struc.Client_ECX, 0\r\n\tmov w [ebp].Client_Reg_Struc.Client_EDX, 0\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tbt [ebp].Client_Reg_Struc.Client_EFlags,0\r\n\tjc exit\r\n\tmov ax, w [ebp].Client_Reg_Struc.Client_EDX\r\n\tshl eax, 16\r\n\tmov ax, w [ebp].Client_Reg_Struc.Client_EAX\r\n\tpush eax\r\n\tmov w [ebp].Client_Reg_Struc.Client_EAX, 4200h\t ;goto start\r\n\tmov w [ebp].Client_Reg_Struc.Client_ECX, 0\r\n\tmov w [ebp].Client_Reg_Struc.Client_EDX, 0\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tpop eax\r\n\tbt [ebp].Client_Reg_Struc.Client_EFlags,0\r\nexit:\r\n\tret\r\nGetFileSize endp\r\n\r\n;--- Read file into buffer\r\n;--- bx = file handle\r\n;--- cx = bytes to read\r\n;--- edx = buffer (in conv. memory)\r\n;--- out: eax=bytes read\r\n\r\nReadFile proc\r\n\tmovzx eax,w [ebp].Client_Reg_Struc.Client_DS\r\n\tshl eax, 4\r\n\tsub edx, eax\r\n\tmov w [ebp].Client_Reg_Struc.Client_EBX, bx\r\n\tmov w [ebp].Client_Reg_Struc.Client_ECX, cx\r\n\tmov w [ebp].Client_Reg_Struc.Client_EDX, dx\r\n\tmov w [ebp].Client_Reg_Struc.Client_EAX, 3F00h\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tmovzx eax, w [ebp].Client_Reg_Struc.Client_EAX\r\n\tbt [ebp].Client_Reg_Struc.Client_EFlags,0\r\n\tret\r\nReadFile endp\r\n\r\n"
  },
  {
    "path": "Include/FINDRES.INC",
    "content": "\r\n?NOCASEMAP\tequ 0\t;win32 std is 0\r\n\r\n;--- eax = resource directory\r\n;--- id may be an id (HIWORD=0) or a string pointer\r\n\r\n;--- used by EnumResourceXXX as well !!\r\n\r\nScanResDir proc public uses esi pRes:DWORD, dwDir:DWORD, id:DWORD\r\n\r\n\tmov esi, pRes\r\n\tadd esi, dwDir\r\n\tmovzx ecx, [esi].IMAGE_RESOURCE_DIRECTORY.NumberOfNamedEntries\r\n\tmov eax, id\r\n\t.if (eax & 0FFFF0000h)\r\n\t\t.if (byte ptr [eax] == '#')\r\n\t\t\tlea edx,[eax+1]\r\n\t\t\tcall GetNumber\r\n\t\t\tjmp ok\r\n\t\t.endif\r\n\t\tlea esi, [esi + sizeof IMAGE_RESOURCE_DIRECTORY]\r\n\t\t.while (ecx)\r\n\t\t\tmov edx, [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.Name_\r\n\t\t\tand edx, edx\r\n\t\t\t.if (SIGN?)\r\n\t\t\t\tand edx, 7FFFFFFFh\r\n\t\t\t\tadd edx, pRes\r\n\t\t\t\tpushad\r\n\t\t\t\tmov esi, edx\r\n\t\t\t\tmov edi, eax\r\n\t\t\t\tlodsw\r\n\t\t\t\tmovzx ecx, ax\r\n@@:\r\nif ?NOCASEMAP\r\n\t\t\t\tlodsw\r\n\t\t\t\tscasb\r\nelse\r\n\t\t\t\tlodsw\r\n\t\t\t\tcall ToLower\r\n\t\t\t\tmov ah,al\r\n\t\t\t\tmov al,[edi]\r\n\t\t\t\tinc edi\r\n\t\t\t\tcall ToLower\r\n\t\t\t\tcmp al,ah\r\nendif\r\n\t\t\t\tloopz @B\r\n\t\t\t\tpopad\r\n\t\t\t\t.if (ZERO? && (byte ptr [edi] == 0))\r\n\t\t\t\t\tmov eax, [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData\r\n\t\t\t\t\tjmp done\r\n\t\t\t\t.endif\r\n\t\t\t.endif\r\n\t\t\tadd esi, sizeof IMAGE_RESOURCE_DIRECTORY_ENTRY\r\n\t\t\tdec ecx\r\n\t\t.endw\r\n\t\tjmp error\r\n\t.endif\r\nok:\r\n\tpush eax\r\n\tmov eax, sizeof IMAGE_RESOURCE_DIRECTORY_ENTRY\r\n\tmul ecx\r\n;-------------------- let esi point to resources with id entries\r\n\tmovzx ecx, [esi].IMAGE_RESOURCE_DIRECTORY.NumberOfIdEntries\r\n\tlea esi, [esi + eax + sizeof IMAGE_RESOURCE_DIRECTORY]\r\n\tpop eax\r\n\t.while (ecx)\r\n\t\t.if ((ax == [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.Id) || (ax == 0))\r\n\t\t\tmov eax, [esi].IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData\r\n\t\t\tjmp done\r\n\t\t.endif\r\n\t\tadd esi, sizeof IMAGE_RESOURCE_DIRECTORY_ENTRY\r\n\t\tdec ecx\r\n\t.endw\r\nerror:\r\n\txor eax, eax\r\ndone:\r\n\tret\r\nife ?NOCASEMAP\r\nToLower:\r\n\tcmp al,'A'\r\n\tjb @F\r\n\tcmp al,'Z'\r\n\tjae @F\r\n\tor al,20h\r\n@@: \r\n\tretn\r\nendif\r\nGetNumber:\r\n\tpush ebx\r\n\txor eax, eax\r\n\t.while (byte ptr [edx])\r\n\t\tmovzx ebx,byte ptr [edx]\r\n\t\tinc edx\r\n\t\tsub bl,'0'\r\n\t\tadd eax, eax\r\n\t\tpush eax\r\n\t\tshl eax, 2\r\n\t\tadd eax, [esp]\r\n\t\tadd eax, ebx\r\n\t\tpop ebx\r\n\t.endw\r\n\tpop ebx\r\n\tretn\r\n\talign 4\r\n\r\nScanResDir endp\r\n\r\n;--- hiword of lpName and lpType may be NULL\r\n;--- in this case it is an integer identifier\r\n\r\nFindResourceA proc public uses esi edi hModule:DWORD, lpName:DWORD, lpType:DWORD\r\n\r\n\tmov esi, hModule\r\n\t.if (!esi)\r\n\t\tinvoke GetModuleHandle, esi\r\n\t\tmov esi, eax\r\n\t\tmov hModule, eax\r\n\t.endif\r\n\r\n\tadd esi, [esi].IMAGE_DOS_HEADER.e_lfanew\r\n\tmov edi, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE * sizeof IMAGE_DATA_DIRECTORY].VirtualAddress\r\n\tmov edx, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE * sizeof IMAGE_DATA_DIRECTORY].Size_\r\n\t.if (edi)\r\n\t\tadd edi, hModule\r\n\t\tinvoke ScanResDir, edi, 0, lpType\r\n\t\t.if (eax)\r\n\t\t\tand eax, eax\r\n\t\t\t.if (SIGN?)\r\n\t\t\t\tand eax, 7FFFFFFFh\r\n\t\t\t\tinvoke ScanResDir, edi, eax, lpName\r\n\t\t\t\t.if (eax)\r\n\t\t\t\t\tand eax, eax\r\n\t\t\t\t\t.if (SIGN?)\r\n\t\t\t\t\t\tand eax, 7FFFFFFFh\r\n\t\t\t\t\t\tinvoke ScanResDir, edi, eax, 0\r\n\t\t\t\t\t\t.if (eax)\r\n\t\t\t\t\t\t\tadd eax, edi\r\n\t\t\t\t\t\t\tjmp done\r\n\t\t\t\t\t\t.endif\r\n\t\t\t\t\t.else\r\n\t\t\t\t\t\tadd eax, edi\r\n\t\t\t\t\t\tjmp done\r\n\t\t\t\t\t.endif\r\n\t\t\t\t.endif\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t.endif\r\nerror:\r\n\txor eax, eax\r\ndone:\r\nif 0;def _DEBUG\r\n\t@trace <\"FindResourceA(\">\r\n\t@tracedw hModule\r\n\t@trace <\", \">\r\n\t.if (word ptr lpName+2) \r\n\t\t@trace lpName\r\n\t.else\r\n\t\t@tracedw lpName\r\n\t.endif\r\n\t@trace <\", \">\r\n\t.if (word ptr lpType+2) \r\n\t\t@trace lpType\r\n\t.else\r\n\t\t@tracedw lpType\r\n\t.endif\r\n\t@trace <\")=\">\r\n\t@tracedw eax\r\n\t@trace <13,10>\r\nendif\r\n\r\n\tret\r\n\talign 4\r\n\r\nFindResourceA endp\r\n\r\n;--- attention: name and type are exchanged!\r\n\r\nFindResourceExA proc public hModule:DWORD, lpType:DWORD, lpName:DWORD, wLanguage:DWORD\r\n\r\n\tinvoke FindResourceA, hModule, lpName, lpType\r\n;\t@strace <\"FindResourceExA(\", hModule, \", \", lpType, \", \", lpName, \", \", wLanguage, \")=\", eax>\r\n\tret\r\n\talign 4\r\n\r\nFindResourceExA endp\r\n\r\nLoadResource proc public hModule:DWORD, hres:DWORD\r\n\r\n\t.if (!hModule)\r\n\t\tinvoke GetModuleHandle, hModule\r\n\t\tmov hModule, eax\r\n\t.endif\r\n\tmov eax, hres\r\n\t.if (eax)\r\n\t\tmov eax, [eax].IMAGE_RESOURCE_DATA_ENTRY.OffsetToData\r\n\t\tadd eax, hModule\r\n\t.endif\r\n;\t@strace <\"LoadResource(\", hModule, \", \", hres, \")=\", eax>\r\n\tret\r\n\talign 4\r\n\r\nLoadResource endp\r\n\r\nLockResource proc public hres:DWORD\r\n\r\n\tmov eax,hres\r\n;\t@strace <\"LockResource(\", hres, \")=\", eax>\r\n\tret\r\n\talign 4\r\n\r\nLockResource endp\r\n\r\nFreeResource proc public hres:DWORD\r\n\r\n\tmov eax,1\r\n;\t@strace <\"FreeResource(\", hres, \")=\", eax>\r\n\tret\r\n\talign 4\r\n\r\nFreeResource endp\r\n\r\nSizeofResource proc public hModule:DWORD, hres:DWORD\r\n\r\n\tmov eax, hres\r\n\t.if (eax)\r\n\t\tmov eax, [eax].IMAGE_RESOURCE_DATA_ENTRY.Size_\r\n\t.endif\r\n;\t@strace <\"SizeofResource(\", hModule, \", \", hres, \")=\", eax>\r\n\tret\r\n\talign 4\r\n\r\nSizeofResource endp\r\n\r\n"
  },
  {
    "path": "Include/JLM.H",
    "content": "\r\n/* Header file for Jemm JLMs which defines the subset of the Win3x/Win9x */\r\n/* ring 0 API (VMM) that Jemm/JLoad exposes.                             */\r\n/* The parameters and return values of the functions are the same as in  */\r\n/* the Windows implementation, register usage is as in Win32: EAX, ECX   */\r\n/* and EDX may be modified inside functions, the other registers are     */\r\n/* preserved (exception: Get_Cur_VM_Handle).                             */\r\n\r\ntypedef unsigned char   UCHAR;\r\ntypedef unsigned short  USHORT;\r\ntypedef unsigned long   ULONG;\r\n\r\nstruct Pushad_Struc {\r\n    ULONG Pushad_EDI;       /* Client's EDI */\r\n    ULONG Pushad_ESI;       /* Client's ESI */\r\n    ULONG Pushad_EBP;       /* Client's EBP */\r\n    ULONG Pushad_ESP;       /* ESP before pushad */\r\n    ULONG Pushad_EBX;       /* Client's EBX */\r\n    ULONG Pushad_EDX;       /* Client's EDX */\r\n    ULONG Pushad_ECX;       /* Client's ECX */\r\n    ULONG Pushad_EAX;       /* Client's EAX */\r\n};\r\n\r\n#define VMM_DEVICE_ID       0x00001\r\n#define VDMAD_DEVICE_ID     0x00004\r\n\r\n#define GetVxDServiceOrdinal(service)   __ ## service\r\n\r\n\r\n#define Begin_Service_Table(device, seg) \\\r\n    enum device##_SERVICES { \\\r\n    device##_dummy = (device##_DEVICE_ID << 16) - 1,\r\n\r\n#define Declare_Service(service) \\\r\n    GetVxDServiceOrdinal(service),\r\n\r\n#define Declare_SCService(service, args, local) \\\r\n    GetVxDServiceOrdinal(service),\r\n\r\n#define End_Service_Table(device, seg) \\\r\n    Num_##device##_Services};\r\n\r\n#define VXDINLINE static __inline\r\n\r\n/*  */\r\n\r\n#define GetVxDServiceAddress(service)   service\r\n\r\n#ifdef __WATCOMC__\r\n\r\n// Open Watcom C currently can't call the VMM services directly\r\n// because ENUM values aren't accessible by the inline assembler.\r\n\r\n#define VxDCall(service) \\\r\n    _asm int 20h \\\r\n    _asm dd (GetVxDServiceOrdinal(service) ) \\\r\n\r\n#define VxDJmp(service) \\\r\n    _asm int 20h \\\r\n    _asm dd (GetVxDServiceOrdinal(service) | 0x8000 ) \\\r\n\r\n#else\r\n\r\n#define VxDCall(service) \\\r\n    _asm _emit 0xcd \\\r\n    _asm _emit 0x20 \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) & 0xff) \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) >> 8) & 0xff \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) >> 16) & 0xff \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) >> 24) & 0xff \\\r\n\r\n#define VxDJmp(service) \\\r\n    _asm _emit 0xcd \\\r\n    _asm _emit 0x20 \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) & 0xff) \\\r\n    _asm _emit ((GetVxDServiceOrdinal(service) >> 8) & 0xff) | 0x80 \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) >> 16) & 0xff \\\r\n    _asm _emit (GetVxDServiceOrdinal(service) >> 24) & 0xff \\\r\n\r\n#endif\r\n\r\n#define VMMCall VxDCall\r\n\r\n#define VMMJmp  VxDJmp\r\n\r\n#define SERVICE __cdecl\r\n\r\n#define VMM_Service Declare_Service\r\n\r\nBegin_Service_Table(VMM, VMM)\r\n\r\nVMM_Service (Get_VMM_Version)\r\nVMM_Service (Get_Cur_VM_Handle)\r\nVMM_Service (Allocate_V86_Call_Back)\r\nVMM_Service (Crash_Cur_VM)\r\nVMM_Service (Hook_V86_Int_Chain)\r\nVMM_Service (Get_V86_Int_Vector)\r\nVMM_Service (Set_V86_Int_Vector)\r\nVMM_Service (Get_PM_Int_Vector)\r\nVMM_Service (Set_PM_Int_Vector)\r\nVMM_Service (Simulate_Int)\r\nVMM_Service (Simulate_Iret)\r\nVMM_Service (Simulate_Far_Call)\r\nVMM_Service (Simulate_Far_Jmp)\r\nVMM_Service (Simulate_Far_Ret)\r\nVMM_Service (Simulate_Far_Ret_N)\r\nVMM_Service (Build_Int_Stack_Frame)\r\nVMM_Service (Simulate_Push)\r\nVMM_Service (Simulate_Pop)\r\nVMM_Service (_PageFree)\r\nVMM_Service (_PhysIntoV86)\r\nVMM_Service (_LinMapIntoV86)\r\nVMM_Service (Hook_V86_Fault)\r\nVMM_Service (Hook_PM_Fault)\r\nVMM_Service (Begin_Nest_Exec)\r\nVMM_Service (Exec_Int)\r\nVMM_Service (Resume_Exec)\r\nVMM_Service (End_Nest_Exec)\r\nVMM_Service (Save_Client_State)\r\nVMM_Service (Restore_Client_State)\r\nVMM_Service (Simulate_IO)\r\nVMM_Service (Install_Mult_IO_Handlers)\r\nVMM_Service (Install_IO_Handler)\r\nVMM_Service (VMM_Add_DDB)\r\nVMM_Service (VMM_Remove_DDB)\r\nVMM_Service (Remove_IO_Handler)\r\nVMM_Service (Remove_Mult_IO_Handlers)\r\nVMM_Service (Unhook_V86_Int_Chain)\r\nVMM_Service (Unhook_V86_Fault)\r\nVMM_Service (Unhook_PM_Fault)\r\nVMM_Service (_PageReserve)\r\nVMM_Service (_PageCommit)\r\nVMM_Service (_PageDecommit)\r\nVMM_Service (_PageCommitPhys)\r\n\r\nVMM_Service (Free_V86_Call_Back)\r\nVMM_Service (Yield)\r\nVMM_Service (MoveMemory)\r\n\r\nVMM_Service (_Allocate_GDT_Selector)\r\nVMM_Service (_Free_GDT_Selector)\r\nVMM_Service (Get_DDB)\r\n\r\nEnd_Service_Table(VMM, VMM)\r\n\r\nstruct cb_s {\r\n    ULONG CB_VM_Status;     /* VM status flags */\r\n    ULONG CB_High_Linear;   /* Address of VM mapped high */\r\n    ULONG CB_Client_Pointer;\r\n    ULONG CB_VMID;\r\n    ULONG CB_Signature;\r\n};\r\n\r\nstruct Client_Reg_Struc {\r\n    ULONG Client_EDI;       /* Client's EDI */\r\n    ULONG Client_ESI;       /* Client's ESI */\r\n    ULONG Client_EBP;       /* Client's EBP */\r\n    ULONG Client_res0;      /* ESP at pushall */\r\n    ULONG Client_EBX;       /* Client's EBX */\r\n    ULONG Client_EDX;       /* Client's EDX */\r\n    ULONG Client_ECX;       /* Client's ECX */\r\n    ULONG Client_EAX;       /* Client's EAX */\r\n    ULONG Client_IntNo;\r\n    ULONG Client_Error;     /* Dword error code */\r\n    ULONG Client_EIP;       /* EIP */\r\n    USHORT Client_CS;       /* CS */\r\n    USHORT Client_res1;     /*   (padding) */\r\n    ULONG Client_EFlags;    /* EFLAGS */\r\n    ULONG Client_ESP;       /* ESP */\r\n    USHORT Client_SS;       /* SS */\r\n    USHORT Client_res2;     /*   (padding) */\r\n    USHORT Client_ES;       /* ES */\r\n    USHORT Client_res3;     /*   (padding) */\r\n    USHORT Client_DS;       /* DS */\r\n    USHORT Client_res4;     /*   (padding) */\r\n    USHORT Client_FS;       /* FS */\r\n    USHORT Client_res5;     /*   (padding) */\r\n    USHORT Client_GS;       /* GS */\r\n    USHORT Client_res6;     /*   (padding) */\r\n};\r\n"
  },
  {
    "path": "Include/JLM.INC",
    "content": "\r\n;--- this is an assembler include file in MASM format,\r\n;--- to be used for JLMs (Jemm Loadable Modules).\r\n;--- it defines the API exposed by Jemm/JLoad.\r\n\r\n;--- parameters, calling convention and return values are the same as\r\n;--- for Win9x.\r\n;--- the register usage usually is the same as for the Win32 stdcall \r\n;--- convention: registers EAX, ECX and EDX may be changed inside a function,\r\n;---             registers EBX, ESI, EDI, EBP are preserved.\r\n;--- the exception of this rule is Get_Cur_VM_Handle, which returns the\r\n;--- \"handle\" in EBX.\r\n;--------------------------------------------------------------------------\r\n\r\n;--- v5.83: VMMCall renamed to @VMMCall ( VMMCALL is a x86 instruction now )\r\n\r\nifndef __POASM__\r\n@VMMCall macro name_\r\n    int 20h\r\n    dw VMM_&name_\r\n    dw 1\r\n    endm\r\nelse\r\n@VMMCall macro name_\r\n    int 20h\r\n    dw VMM_ # name_\r\n    dw 1\r\n    endm\r\nendif\r\n\r\nifndef __POASM__\r\nVMMJmp macro name_\r\n    int 20h\r\n    dw VMM_&name_ + 8000h\r\n    dw 1\r\n    endm\r\nelse\r\nVMMJmp macro name_\r\n    int 20h\r\n    dw VMM_ # name_ + 8000h\r\n    dw 1\r\n    endm\r\nendif\r\n\r\nVxDCall macro name_\r\n    int 20h\r\n    dd name_\r\n    endm\r\n\r\n;--- macro to define a hook proc (Hook_V86_Int_Chain)\r\n\r\nHookProc macro name_, oldvect\r\n;    jmp $+8   ; v5.85: removed\r\n    jmp dword ptr [oldvect]\r\nname_ proc\r\n    endm\r\n\r\n;--------------------------------------------------------------------------\r\n;--- VMM API. This device is installed by JLoad. The name is misleading,\r\n;--- though, VMs aren't supported.\r\n\r\n;--- the following functions aren't implemented yet and will always return\r\n;--- an error: \r\n;---   Install_Mult_IO_Handlers, Remove_Mult_IO_Handlers\r\n;---   Hook_V86_Fault, Unhook_V86_Fault\r\n;---   Hook_PM_Fault, Unhook_PM_Fault\r\n;---   Get_PM_Int_Vector, Set_PM_Int_Vector\r\n\r\n@@VMM_Service_no = 0\r\n\r\nifndef __POASM__\r\nVMM_Service macro name_\r\nVMM_&name_  equ @@VMM_Service_no\r\n@@VMM_Service_no = @@VMM_Service_no +1\r\n    endm\r\nelse\r\nVMM_Service macro name_\r\nVMM_#name_  equ @@VMM_Service_no\r\n@@VMM_Service_no = @@VMM_Service_no +1\r\n    endm\r\nendif\r\n\r\nVMM_Service Get_VMM_Version\r\nVMM_Service Get_Cur_VM_Handle\r\nVMM_Service Allocate_V86_Call_Back\r\nVMM_Service Crash_Cur_VM\r\nVMM_Service Hook_V86_Int_Chain\r\nVMM_Service Get_V86_Int_Vector\r\nVMM_Service Set_V86_Int_Vector\r\nVMM_Service Get_PM_Int_Vector\r\nVMM_Service Set_PM_Int_Vector\r\nVMM_Service Simulate_Int\r\nVMM_Service Simulate_Iret\r\nVMM_Service Simulate_Far_Call\r\nVMM_Service Simulate_Far_Jmp\r\nVMM_Service Simulate_Far_Ret\r\nVMM_Service Simulate_Far_Ret_N\r\nVMM_Service Build_Int_Stack_Frame\r\nVMM_Service Simulate_Push\r\nVMM_Service Simulate_Pop\r\nVMM_Service _PageFree\r\nVMM_Service _PhysIntoV86\r\nVMM_Service _LinMapIntoV86\r\nVMM_Service Hook_V86_Fault   ;implemented in v5.86 - previously a stub only\r\nVMM_Service Hook_PM_Fault\r\nVMM_Service Begin_Nest_Exec\r\nVMM_Service Exec_Int\r\nVMM_Service Resume_Exec\r\nVMM_Service End_Nest_Exec\r\nVMM_Service Save_Client_State\r\nVMM_Service Restore_Client_State\r\nVMM_Service Simulate_IO\r\nVMM_Service Install_Mult_IO_Handlers\r\nVMM_Service Install_IO_Handler\r\nVMM_Service VMM_Add_DDB\r\nVMM_Service VMM_Remove_DDB\r\nVMM_Service Remove_IO_Handler\r\nVMM_Service Remove_Mult_IO_Handlers\r\nVMM_Service Unhook_V86_Int_Chain\r\nVMM_Service Unhook_V86_Fault  ;implemented in v5.86 - previously a stub only\r\nVMM_Service Unhook_PM_Fault\r\nVMM_Service _PageReserve\r\nVMM_Service _PageCommit\r\nVMM_Service _PageDecommit\r\nVMM_Service _PageCommitPhys\r\n\r\n;--- the following functions are Jemm specific\r\n\r\nVMM_Service Free_V86_Call_Back      ;release v86 callback in EAX\r\nVMM_Service Yield                   ;release cpu for IRQs\r\nVMM_Service MoveMemory              ;copy memory with ints enabled\r\n\r\n;--- added in v5.83\r\n\r\nVMM_Service _Allocate_GDT_Selector\r\nVMM_Service _Free_GDT_Selector\r\nVMM_Service Get_DDB\r\n\r\n;--- added in v5.85\r\n\r\nVMM_Service V86ToPM\t\t; jump to VCPI client, esi->V86toPM struct, ebp->client regs\r\n\r\n;--- added in v5.86\r\n\r\nVMM_Service GetDOSBuffer; get linear address of 4kB DOS buffer in JLOAD (during init phase only)\r\n\r\n;--- equates for the page memory functions\r\n\r\n;_PageReserve() page parameter:\r\nPR_PRIVATE  EQU 80000400H\r\nPR_SHARED   EQU 80060000H\r\nPR_SYSTEM   EQU 80080000H\r\n\r\n;_PageCommit() hpd parameter:\r\nPD_ZEROINIT     EQU 00000001H   ;not supported\r\nPD_NOINIT       EQU 00000002H   ;not supported\r\nPD_FIXEDZERO    EQU 00000003H\r\nPD_FIXED        EQU 00000004H\r\n\r\n;_PageCommit() flags parameter:\r\nPC_FIXED        EQU 00000008H   ;PC_FIXED and/or PC_LOCKED must be set\r\nPC_LOCKED       EQU 00000080H\r\nPC_WRITEABLE    EQU 00020000H   ;also valid for _PageCommitPhys()\r\nPC_USER         EQU 00040000H   ;also valid for _PageCommitPhys()\r\nPC_CACHEWT      EQU 00080000H   ;write thru\r\nPC_CACHEDIS     EQU 00100000H   ;cache disable\r\nPC_INCR         EQU 40000000H   ;also valid for _PageCommitPhys()\r\n\r\n; PTE bits\r\n\r\nP_PRESBIT   EQU 0\r\nP_PRES  EQU (1 SHL P_PRESBIT)\r\nP_WRITEBIT  EQU 1\r\nP_WRITE EQU (1 SHL P_WRITEBIT)\r\nP_USERBIT   EQU 2\r\nP_USER  EQU (1 SHL P_USERBIT)\r\nP_ACCBIT    EQU 5\r\nP_ACC   EQU (1 SHL P_ACCBIT)\r\nP_DIRTYBIT  EQU 6\r\nP_DIRTY EQU (1 SHL P_DIRTYBIT)\r\n\r\n;--- equates for IO trapping functions\r\n\r\nBYTE_INPUT      EQU 000H\r\nBYTE_OUTPUT     EQU 004H\r\nWORD_INPUT      EQU 008H\r\nWORD_OUTPUT     EQU 00CH\r\nDWORD_INPUT     EQU 010H\r\nDWORD_OUTPUT    EQU 014H\r\n\r\nOUTPUT_BIT      EQU 2\r\nWORD_IO_BIT     EQU 3\r\nDWORD_IO_BIT    EQU 4\r\nSTRING_IO_BIT   EQU 5\r\nREP_IO_BIT      EQU 6   ;rep prefix with \"string io\"\r\nADDR_32_IO_BIT  EQU 7   ;ECX used instead of CX for \"rep string io\"\r\nREVERSE_IO_BIT  EQU 8\r\n\r\nOUTPUT      EQU (1 SHL OUTPUT_BIT)\r\nWORD_IO     EQU (1 SHL WORD_IO_BIT)\r\nDWORD_IO    EQU (1 SHL DWORD_IO_BIT)\r\nSTRING_IO   EQU (1 SHL STRING_IO_BIT)\r\nREP_IO      EQU (1 SHL REP_IO_BIT)\r\n\r\n;--- messages\r\nSYSTEM_EXIT EQU 5\r\nDEVICE_REBOOT_NOTIFY EQU 17h\r\n\r\n;--------------------------------------------------------------------------\r\n;--- VDMA device. This device is also installed by JLoad\r\n\r\n@@VDMAD_Service_no = 0\r\nVDMAD_Device equ 4\r\n\r\nVDMAD_Service macro name_\r\nname_   equ @@VDMAD_Service_no  + VDMAD_Device shl 16\r\n@@VDMAD_Service_no = @@VDMAD_Service_no +1\r\n    endm\r\n\r\nVDMAD_Service VDMAD_Get_Version\r\nVDMAD_Service VDMAD_Lock_DMA_Region\r\nVDMAD_Service VDMAD_Unlock_DMA_Region\r\nVDMAD_Service VDMAD_Scatter_Lock\r\nVDMAD_Service VDMAD_Scatter_Unlock\r\nVDMAD_Service VDMAD_Request_Buffer\r\nVDMAD_Service VDMAD_Release_Buffer\r\nVDMAD_Service VDMAD_Copy_To_Buffer      ;new v5.69\r\nVDMAD_Service VDMAD_Copy_From_Buffer    ;new v5.69\r\n\r\n;--------------------------------------------------------------------------\r\n\r\n;--- VM control block structure\r\n;--- since VMs aren't supported yet, the fields aren't that useful.\r\n;--- CB_Client_Pointer will always contain a valid pointer.\r\n\r\ncb_s    STRUC\r\nCB_VM_Status        DD  ?\r\nCB_High_Linear      DD  ?\r\nCB_Client_Pointer   DD  ?\r\nCB_VMID             DD  ?\r\ncb_s    ENDS\r\n\r\n;--- DDB (device description block)\r\n;--- to be used for VMM_Add_DDB / VMM_Remove_DDB\r\n\r\nUNDEFINED_INIT_ORDER\tEQU 080000000H\r\n\r\nVxD_Desc_Block  STRUC\r\nDDB_Next                DD  ?\r\nDDB_Version             DW  ?\r\nDDB_Req_Device_Number   DW  0\r\nDDB_Dev_Major_Version   DB  0\r\nDDB_Dev_Minor_Version   DB  0\r\nDDB_Flags               DW  0\r\nifndef __POASM__\r\nDDB_Name                DB  8 dup (20h)\r\nelse\r\nDDB_Name                DB  8 dup (?)\r\nendif\r\nDDB_Init_Order          DD  UNDEFINED_INIT_ORDER\r\nDDB_Control_Proc        DD  ?\r\nDDB_V86_API_Proc        DD  0\r\nDDB_PM_API_Proc         DD  0\r\nDDB_V86_API_CSIP        DD  0\r\nDDB_PM_API_CSIP         DD  0\r\nDDB_Reference_Data      DD  ?\r\nDDB_Service_Table_Ptr   DD  0\r\nDDB_Service_Table_Size  DD  0\r\nDDB_Win32_Service_Table DD  0\r\nDDB_Prev                DD  0\r\nDDB_Size                DD  SIZE(VxD_Desc_Block)\r\nDDB_Reserved1           DD  0  ;holds PE module handle\r\nDDB_Reserved2           DD  0\r\nDDB_Reserved3           DD  0\r\nVxD_Desc_Block  ENDS\r\n\r\n;--- client register structure\r\n;--- note the \"Client_Int\" member, which is Jemm specific.\r\n\r\nClient_Reg_Struc  struc\r\nClient_EDI  dd ?    ;+0\r\nClient_ESI  dd ?    ;+4\r\nClient_EBP  dd ?    ;+8\r\nClient_res0 dd ?    ;+12\r\nClient_EBX  dd ?    ;+16\r\nClient_EDX  dd ?    ;+20\r\nClient_ECX  dd ?    ;+24\r\nClient_EAX  dd ?    ;+28\r\nClient_Int  dd ?    ;+32 (pushed as sign-extended byte - so check lowest byte only!)\r\nClient_Error dd ?   ;+36\r\nClient_EIP  dd ?    ;+40\r\nClient_CS   dd ?    ;+44    \r\nClient_EFlags dd ?  ;+48\r\nClient_ESP  dd ?    ;+52\r\nClient_SS   dd ?    ;+56\r\nClient_ES   dd ?    ;+60\r\nClient_DS   dd ?    ;+64\r\nClient_FS   dd ?    ;+68\r\nClient_GS   dd ?    ;+72\r\nClient_Reg_Struc ends\r\n\r\n\r\n;--------------------------------------------------------------------------\r\n;--- JLoad init/term structure\r\n;--- this is 3. parameter for a JLM's DllMain entry\r\n;--- lpRequest is set for LOAD\r\n;--- lpDrivers is set for UNLOAD\r\n\r\nJLCOMM  struc\r\nwLdrCS      dw ?    ;CS of JLOAD.EXE\r\nwFlags      dw ?    ;flags (see below)\r\nlpCmdLine   dd ?    ;linear address cmdline\r\nunion\r\nlpRequest   dd ?    ;LOAD:   linear address DOS request hdr\r\nlpDrivers   dd ?    ;UNLOAD: linear address DOS device driver list start\r\nends\r\nJLCOMM  ends\r\n\r\nJLF_UNLOAD  equ 1   ;set if JLM is to be unloaded\r\nJLF_DRIVER  equ 2   ;set if JLoad is loaded as driver in config.sys\r\n\r\nJLF_UNLOAD_BIT  equ 0\r\nJLF_DRIVER_BIT  equ 1\r\n"
  },
  {
    "path": "Include/JLMFASM.INC",
    "content": "\r\n;--- this is an assembler include file in FASM format\r\n;--- to be used for JLMs (Jemm Loadable Modules).\r\n;--- it defines the API exposed by Jemm/JLoad.\r\n\r\n;--- parameters, calling convention and return values are the same as\r\n;--- for Win9x.\r\n;--- the register usage usually is the same as for the Win32 stdcall \r\n;--- convention: registers EAX, ECX and EDX may be changed inside a function,\r\n;---             registers EBX, ESI, EDI, EBP are preserved.\r\n;--- the exception of this rule is Get_Cur_VM_Handle, which returns the\r\n;--- \"handle\" in EBX.\r\n;--------------------------------------------------------------------------\r\n\r\nmacro @VMMCall name_ {\r\n    int 20h\r\n    dw VMM_#name_\r\n    dw 1\r\n}\r\n\r\nmacro VMMJmp name_ {\r\n    int 20h\r\n    dw VMM_#name_ + 8000h\r\n    dw 1\r\n}\r\n\r\nmacro VxDCall name_ {\r\n    int 20h\r\n    dd name_\r\n}\r\n\r\n;--- macro to define a hook proc (Hook_V86_Int_Chain)\r\n\r\nmacro HookProc name_, oldvect {\r\n    jmp $+8\r\n    jmp dword [oldvect]\r\nname_ proc\r\n}\r\n\r\n;--------------------------------------------------------------------------\r\n;--- VMM API. This device is installed by JLoad. The name is misleading,\r\n;--- though, VMs aren't supported.\r\n\r\n;--- not implemented yet:\r\n;---   Install_Mult_IO_Handlers, Remove_Mult_IO_Handlers\r\n;---   Hook_V86_Fault, Unhook_V86_Fault\r\n;---   Hook_PM_Fault, Unhook_PM_Fault\r\n;---   Get_PM_Int_Vector, Set_PM_Int_Vector\r\n\r\n@VMM_Service_no = 0\r\n\r\nmacro VMM_Service name_ {\r\nVMM_#name_  = @VMM_Service_no\r\n@VMM_Service_no = @VMM_Service_no +1\r\n}\r\n\r\nVMM_Service Get_VMM_Version\r\nVMM_Service Get_Cur_VM_Handle\r\nVMM_Service Allocate_V86_Call_Back\r\nVMM_Service Crash_Cur_VM\r\nVMM_Service Hook_V86_Int_Chain\r\nVMM_Service Get_V86_Int_Vector\r\nVMM_Service Set_V86_Int_Vector\r\nVMM_Service Get_PM_Int_Vector\r\nVMM_Service Set_PM_Int_Vector\r\nVMM_Service Simulate_Int\r\nVMM_Service Simulate_Iret\r\nVMM_Service Simulate_Far_Call\r\nVMM_Service Simulate_Far_Jmp\r\nVMM_Service Simulate_Far_Ret\r\nVMM_Service Simulate_Far_Ret_N\r\nVMM_Service Build_Int_Stack_Frame\r\nVMM_Service Simulate_Push\r\nVMM_Service Simulate_Pop\r\nVMM_Service _PageFree\r\nVMM_Service _PhysIntoV86\r\nVMM_Service _LinMapIntoV86\r\nVMM_Service Hook_V86_Fault\r\nVMM_Service Hook_PM_Fault\r\nVMM_Service Begin_Nest_Exec\r\nVMM_Service Exec_Int\r\nVMM_Service Resume_Exec\r\nVMM_Service End_Nest_Exec\r\nVMM_Service Save_Client_State\r\nVMM_Service Restore_Client_State\r\nVMM_Service Simulate_IO\r\nVMM_Service Install_Mult_IO_Handlers\r\nVMM_Service Install_IO_Handler\r\nVMM_Service VMM_Add_DDB\r\nVMM_Service VMM_Remove_DDB\r\nVMM_Service Remove_IO_Handler\r\nVMM_Service Remove_Mult_IO_Handlers\r\nVMM_Service Unhook_V86_Int_Chain\r\nVMM_Service Unhook_V86_Fault\r\nVMM_Service Unhook_PM_Fault\r\nVMM_Service _PageReserve\r\nVMM_Service _PageCommit\r\nVMM_Service _PageDecommit\r\nVMM_Service _PageCommitPhys\r\n\r\n;--- the following functions are Jemm specific\r\n\r\nVMM_Service Free_V86_Call_Back      ;release v86 callback in EAX\r\nVMM_Service Yield                   ;release cpu for IRQs\r\nVMM_Service MoveMemory              ;copy memory with ints enabled\r\n\r\n;--- added in v5.83\r\n\r\nVMM_Service _Allocate_GDT_Selector\r\nVMM_Service _Free_GDT_Selector\r\nVMM_Service Get_DDB\r\n\r\n;--- equates for the page memory functions\r\n\r\n;_PageReserve() page parameter:\r\nPR_PRIVATE  EQU 80000400H\r\nPR_SHARED   EQU 80060000H\r\nPR_SYSTEM   EQU 80080000H\r\n\r\n;_PageCommit() hpd parameter:\r\nPD_ZEROINIT     EQU 00000001H   ;not supported\r\nPD_NOINIT       EQU 00000002H   ;not supported\r\nPD_FIXEDZERO    EQU 00000003H\r\nPD_FIXED        EQU 00000004H\r\n\r\n;_PageCommit() flags parameter:\r\nPC_FIXED        EQU 00000008H   ;PC_FIXED and/or PC_LOCKED must be set\r\nPC_LOCKED       EQU 00000080H\r\nPC_WRITEABLE    EQU 00020000H   ;also valid for _PageCommitPhys()\r\nPC_USER         EQU 00040000H   ;also valid for _PageCommitPhys()\r\nPC_INCR         EQU 40000000H   ;also valid for _PageCommitPhys()\r\n\r\n; PTE bits\r\n\r\nP_PRESBIT   EQU 0\r\nP_PRES  EQU (1 SHL P_PRESBIT)\r\nP_WRITEBIT  EQU 1\r\nP_WRITE EQU (1 SHL P_WRITEBIT)\r\nP_USERBIT   EQU 2\r\nP_USER  EQU (1 SHL P_USERBIT)\r\nP_ACCBIT    EQU 5\r\nP_ACC   EQU (1 SHL P_ACCBIT)\r\nP_DIRTYBIT  EQU 6\r\nP_DIRTY EQU (1 SHL P_DIRTYBIT)\r\n\r\n;--- equates for IO trapping functions\r\n\r\nBYTE_INPUT      EQU 000H\r\nBYTE_OUTPUT     EQU 004H\r\nWORD_INPUT      EQU 008H\r\nWORD_OUTPUT     EQU 00CH\r\nDWORD_INPUT     EQU 010H\r\nDWORD_OUTPUT    EQU 014H\r\n\r\nOUTPUT_BIT      EQU 2\r\nWORD_IO_BIT     EQU 3\r\nDWORD_IO_BIT    EQU 4\r\nSTRING_IO_BIT   EQU 5\r\nREP_IO_BIT      EQU 6   ;rep prefix with \"string io\"\r\nADDR_32_IO_BIT  EQU 7   ;ECX used instead of CX for \"rep string io\"\r\nREVERSE_IO_BIT  EQU 8\r\n\r\nOUTPUT      EQU (1 SHL OUTPUT_BIT)\r\nWORD_IO     EQU (1 SHL WORD_IO_BIT)\r\nDWORD_IO    EQU (1 SHL DWORD_IO_BIT)\r\nSTRING_IO   EQU (1 SHL STRING_IO_BIT)\r\nREP_IO      EQU (1 SHL REP_IO_BIT)\r\n\r\n;--------------------------------------------------------------------------\r\n;--- VDMA device. This device is also installed by JLoad\r\n\r\n@VDMAD_Service_no = 0\r\nVDMAD_Device equ 4\r\n\r\nmacro VDMAD_Service name_ {\r\nname_   equ @VDMAD_Service_no  + VDMAD_Device shl 16\r\n@VDMAD_Service_no = @VDMAD_Service_no +1\r\n}\r\n\r\nVDMAD_Service VDMAD_Get_Version\r\nVDMAD_Service VDMAD_Lock_DMA_Region\r\nVDMAD_Service VDMAD_Unlock_DMA_Region\r\nVDMAD_Service VDMAD_Scatter_Lock\r\nVDMAD_Service VDMAD_Scatter_Unlock\r\nVDMAD_Service VDMAD_Request_Buffer\r\nVDMAD_Service VDMAD_Release_Buffer\r\nVDMAD_Service VDMAD_Copy_To_Buffer      ;new v5.69\r\nVDMAD_Service VDMAD_Copy_From_Buffer    ;new v5.69\r\n\r\n;--------------------------------------------------------------------------\r\n\r\n;--- VM control block structure\r\n;--- since VMs aren't supported yet, the fields aren't that useful.\r\n;--- CB_Client_Pointer will always contain a valid pointer.\r\n\r\nstruct cb_s\r\n  CB_VM_Status        DD  ?\r\n  CB_High_Linear      DD  ?\r\n  CB_Client_Pointer   DD  ?\r\n  CB_VMID             DD  ?\r\nends\r\n\r\nif 0\r\n;--- DDB (device description block)\r\n;--- to be used for VMM_Add_DDB / VMM_Remove_DDB\r\n\r\nstruct VxD_Desc_Block\r\nDDB_Next                dd  ?\r\nDDB_Version             DW  ?\r\nDDB_Req_Device_Number   DW  ?\r\nDDB_Dev_Major_Version   DB  ?\r\nDDB_Dev_Minor_Version   DB  ?\r\nDDB_Flags               DW  ?\r\nDDB_Name                rb  8\r\nDDB_Init_Order          DD  ?\r\nDDB_Control_Proc        DD  ?\r\nDDB_V86_API_Proc        DD  ?\r\nDDB_PM_API_Proc         DD  ?\r\nDDB_V86_API_CSIP        DD  ?\r\nDDB_PM_API_CSIP         DD  ?\r\nDDB_Reference_Data      DD  ?\r\nDDB_Service_Table_Ptr   DD  ?\r\nDDB_Service_Table_Size  DD  ?\r\nDDB_Win32_Service_Table DD  ?\r\nDDB_Prev                DD  ?\r\nDDB_Size                DD  ?\r\nDDB_Reserved1           DD  ?\r\nDDB_Reserved2           DD  ?\r\nDDB_Reserved3           DD  ?\r\nends\r\nend if\r\n\r\n;--- client register structure\r\n;--- note the \"Client_Int\" member, which is Jemm specific.\r\n\r\nstruct Client_Reg_Struc\r\nClient_EDI  dd ?    ;+0\r\nClient_ESI  dd ?    ;+4\r\nClient_EBP  dd ?    ;+8\r\nClient_res0 dd ?    ;+12\r\nClient_EBX  dd ?    ;+16\r\nClient_EDX  dd ?    ;+20\r\nClient_ECX  dd ?    ;+24\r\nClient_EAX  dd ?    ;+28\r\nClient_Int  dd ?    ;+32\r\nClient_Error dd ?   ;+36\r\nClient_EIP  dd ?    ;+40\r\nClient_CS   dd ?    ;+44    \r\nClient_EFlags dd ?  ;+48\r\nClient_ESP  dd ?    ;+52\r\nClient_SS   dd ?    ;+56\r\nClient_ES   dd ?    ;+60\r\nClient_DS   dd ?    ;+64\r\nClient_FS   dd ?    ;+68\r\nClient_GS   dd ?    ;+72\r\nends\r\n\r\n\r\n;--------------------------------------------------------------------------\r\n;--- JLoad init/term structure\r\n;--- this is 3. parameter for a JLM's DllMain entry\r\n;--- lpRequest is set for LOAD\r\n;--- lpDrivers is set for UNLOAD\r\n\r\nstruct JLCOMM\r\nwLdrCS      dw ?    ;CS of JLOAD.EXE\r\nwFlags      dw ?    ;flags (see below)\r\nlpCmdLine   dd ?    ;linear address cmdline\r\nlpRequest   dd ?    ;LOAD:   linear address DOS request hdr\r\nvirtual at JLCOMM.lpRequest\r\nlpDrivers   dd ?    ;UNLOAD: linear address DOS device driver list start\r\nend virtual\r\nends\r\n\r\nJLF_UNLOAD  equ 1   ;set if JLM is to be unloaded\r\nJLF_DRIVER  equ 2   ;set if JLoad is loaded as driver in config.sys\r\n\r\nJLF_UNLOAD_BIT  equ 0\r\nJLF_DRIVER_BIT  equ 1\r\n"
  },
  {
    "path": "Include/JSYSTEM.INC",
    "content": "\r\n;--- constants used by Jemm & JLoad\r\n\r\n?VERSIONHIGH    equ 5\r\n?VERSIONLOW     equ 87\r\n\r\n?BASE           EQU 110000h         ; base of Jemm 32bit code\r\n?SYSBASE        EQU 0F8000000h      ; linear address system space\r\n?PAGEDIR        EQU ?SYSBASE+1000h  ; linear address page dir\r\n?PAGETABSYS     EQU 3000h           ; rel. offset (?SYSBASE) for system page table\r\n;--- 3000 = 3 page tables ( pagedir, page tab0, system page tab )\r\n?TOS            EQU ?SYSBASE+1000h+3000h+1000h  ;top of ring 0 stack\r\n\r\n;--- this is the structure for a \"Get VMM info\" request\r\n;--- v5.87: fields e08_pCallBack and e08_CallBackRM do now\r\n;--- point to the *second* static breakpoint in Jemm32/Jemm16.\r\n;--- Thus JLoad doesn't need to know anything about the breakpoint array.\r\n\r\nEMX08 struct\r\ne08_ServiceTable    dd ?\t;linear address service table\r\ne08_pCallBack       dd ?\t;linear address callback BP in Jemm32\r\ne08_CallBackRM      dd ?\t;segment:offset of callback BP in Jemm16\r\ne08_GDTR            df ?\r\ne08_IDTR            df ?\r\ne08_TR              dw ?\r\ne08_FlatCS          dw ?\r\nEMX08 ends\r\n\r\n"
  },
  {
    "path": "Include/PRINTF.INC",
    "content": "\r\n;--- simple printf\r\n\r\n;--- i64toa(long long n, char * s, int base);\r\n;--- convert 64-bit long long to string\r\n\r\ni64toa proc stdcall uses edi number:qword, outb:ptr, base:dword\r\n\r\n\tmov ch,0\r\n\tmov edi, base\r\n\tmov eax, dword ptr number+0\r\n\tmov esi, dword ptr number+4\r\n\tcmp edi,-10\r\n\tjne @F\r\n\tneg edi\r\n\tand esi,esi\r\n\tjns @F\r\n\tneg esi\r\n\tneg eax\r\n\tsbb esi,0\r\n\tmov ch,'-'\r\n@@:\r\n\tmov ebx,outb\r\n\tadd ebx,22\r\n\tmov byte ptr [ebx],0\r\n@@nextdigit:\r\n\tdec ebx\r\n\txor edx,edx\r\n\txchg eax,esi\r\n\tdiv edi\r\n\txchg eax,esi\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov [ebx],dl\r\n\tmov edx, eax\r\n\tor edx, esi\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tdec ebx\r\n\tmov [ebx],ch\r\n@@:\r\n\tmov eax,ebx\r\n\tret\r\n\r\ni64toa endp\r\n\r\nprintf proc c fmt:ptr, args:vararg\r\n\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal size_:dword\r\nlocal fillchr:dword\r\nlocal szTmp[24]:byte\r\nlocal crs:Client_Reg_Struc\r\n\r\n\tpushad\r\n\tmov ebp, [ebp]\r\n\tlea edi, crs\r\n\t@VMMCall Save_Client_State\r\n\t@VMMCall Begin_Nest_Exec\r\n\tmov ebp, [esp+2*4]\t;restore EBP\r\n\tcld\r\n\tlea edi,args\r\n@@L335:\r\n\tmov esi,fmt\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\tmov ebp, [ebp]\r\n\t@VMMCall End_Nest_Exec\r\n\tlea esi, crs\r\n\t@VMMCall Restore_Client_State\r\n\tpopad\r\n\tret\r\n\r\nformatitem:\r\n\tpush offset @@L335\r\n\txor edx,edx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [esi],'-'\r\n\tjne @F\r\n\tdec bl\r\n\tinc esi\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [esi],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc esi\r\n@@:\r\n\tmov [fillchr],ecx\r\n\tmov ebx,edx\r\n\r\n\t.while ( byte ptr [esi] >= '0' && byte ptr [esi] <= '9' )\r\n\t\tlodsb\r\n\t\tsub al,'0'\r\n\t\tmovzx eax,al\r\n\t\timul ecx,ebx,10\t\t;ecx = ebx * 10\r\n\t\tadd eax,ecx\r\n\t\tmov ebx,eax\r\n\t.endw\r\n\r\n\tmov [size_],ebx\r\n\tcmp BYTE PTR [esi],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc esi\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],esi\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tand al,al\r\n\tjnz @F\r\n\tpop eax\r\n\tjmp done\r\nhandle_c:\r\n\tmov eax,[edi]\r\n\tadd edi, 4\r\n@@:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov esi,[edi]\r\n\tadd edi,4\r\n\tjmp print_string\r\nhandle_d:\r\nhandle_i:\r\n\tmov ebx,-10\r\n\tjmp @F\r\nhandle_u:\r\n\tmov ebx, 10\r\n\tjmp @F\r\nhandle_x:\r\n\tmov ebx, 16\r\n@@:\r\n\txor edx,edx\r\n\tmov eax,[edi]\r\n\tadd edi,4\r\n\tcmp longarg,1\r\n\tjnz @F\r\n\tmov edx,[edi]\r\n\tadd edi,4\r\n\tjmp printnum\r\n@@:\r\n\tand ebx,ebx\r\n\tjns @F\r\n\tcdq\r\n@@:\r\nprintnum:\r\n\tlea esi, szTmp\r\n\tinvoke i64toa, edx::eax, esi, ebx\r\n\tmov esi, eax\r\n\r\nprint_string:\t\t;print string ESI, size EAX\r\n\tmov eax, esi\r\n\t.while byte ptr [esi]\r\n\t\tinc esi\r\n\t.endw\r\n\tsub esi, eax\r\n\txchg eax, esi\r\n\tmov ebx,size_\r\n\tsub ebx,eax\r\n\t.if flag == 1\r\n\t\t.while sdword ptr ebx > 0\r\n\t\t\tmov eax, [fillchr]\r\n\t\t\tcall handle_char\t;print leading filler chars\r\n\t\t\tdec ebx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [esi]\r\n\t\tlodsb\r\n\t\tcall handle_char\t;print char of string\r\n\t.endw\r\n\r\n\t.while sdword ptr ebx > 0\r\n\t\tmov eax, [fillchr]\r\n\t\tcall handle_char\t;print trailing spaces\r\n\t\tdec ebx\r\n\t.endw\r\n\tretn\r\n\r\n\r\nhandle_char:\r\n\tpush ebp\r\n\tmov ebp, [ebp]\r\n\tmov ah, 0Eh\r\n\tmov [ebp].Client_Reg_Struc.Client_EAX, eax\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EBX+1, 0\r\n\tmov eax, 10h\r\n\t@VMMCall Exec_Int\r\n\tpop ebp\r\n\tretn\r\n\r\nprintf endp\r\n"
  },
  {
    "path": "Include/VDS.INC",
    "content": "\r\n;--- VDS definitions\r\n\r\n;--- flags in DX used by various VDS functions\r\n\r\nVDSF_COPY       equ 02h\t;bit 1 - copy in/out of DMA buffer (8107/8108)\r\nVDSF_NOBUFFER   equ 04h\t;bit 2 - don't alloc DMA buffer (8103)\r\nVDSF_NOREMAP    equ 08h\t;bit 3 - change PTEs; Jemm ignores this flag!\r\nVDSF_64KALIGN   equ 10h\t;bit 4\r\nVDSF_128KALIGN  equ 20h\t;bit 5\r\nVDSF_PTE        equ 40h\t;bit 6 - 1=scatter_lock returns PTEs \r\nVDSF_NPPTE      equ 80h\t;bit 7 - allow non-present pages for scatter/gather remap\r\n\r\n;--- error codes returned in AL by lock (8103)\r\n\r\nVDSERR_NOTCONTIGUOUS equ 1\t;region not in contiguous memory\r\nVDSERR_CROSSEDBOUNDS equ 2\t;region crossed alignemnt boundary\r\nVDSERR_CANTLOCKPAGES equ 3\t;\r\nVDSERR_NOBUFFER      equ 4\t; 8107\r\nVDSERR_BUFFTOOSMALL  equ 5\t; 8107\r\nVDSERR_BUFFINUSE     equ 6\t; \r\n\r\n;--- other VDS error codes\r\n\r\nVDSERR_07            equ 7\t; invalid memory region (8107)\r\nVDSERR_REG_NOTLOCKED equ 8\t; region wasn't locked (8106)\r\nVDSERR_NAVL_TOOSMALL equ 9\t; # of phys pages was > than table length (8105)\r\nVDSERR_INVAL_BUFFID  equ 10\t; invalid buffer ID (8108,8109,810A)\r\nVDSERR_BNDVIOLATION  equ 11\t; buffer boundary violated (8109,810A)\r\nVDSERR_INVAL_DMACHN  equ 12\t; invalid DMA channel # (810B, 810C)\r\nVDSERR_DISCNTOVFL    equ 13\t; disable cnt overflow (810B)\r\nVDSERR_DISCNTUNFL    equ 14\t; disable cnt underflow (810C)\r\nVDSERR_FUNC_NOTSUPP  equ 15\t; function not supported\r\nVDSERR_DXRSVDBITSSET equ 16\t; reserved flag bits set in DX (8102,8106,8108,8109,810A,810B,810C)\r\n\r\n;--- DDS, used by 03-04, 07-08, 09-0A\r\n\r\nDDS struct\r\ndwSize  dd ?    ;+0  size of region\r\ndwOfs   dd ?    ;+4  offset virtual start address\r\nwSeg    dw ?    ;+8  segment/selector virtual start address (or 0000)\r\nwID     dw ?    ;+10 buffer ID\r\ndwPhys  dd ?    ;+12 physical address\r\nDDS ends\r\n\r\n;--- EDDS, used by 05-06\r\n\r\nEDDS struct\r\ndwSize  dd ?    ;+0\r\ndwOfs   dd ?    ;+4\r\nwSeg    dw ?    ;+8\r\nwRes    dw ?    ;+10\r\nwNumAvail   dw ?    ;+12\r\nwNumUsed    dw ?    ;+14\r\nEDDS ends\r\n\r\n;--- EDDS suffix for regions\r\n\r\nEDDSRG struct\r\ndwPhysAddr  dd ?    ;+16\r\ndwSizeRg    dd ?    ;+20\r\nEDDSRG ends\r\n\r\n;--- EDDS suffix for PTEs\r\n\r\nEDDSPT struct\r\ndwPTE       dd ?    ;+16\r\nEDDSPT ends\r\n\r\n"
  },
  {
    "path": "Include/X86.INC",
    "content": "\r\n;--- generic X86 definitions\r\n\r\n;--- page table flags\r\n\r\nPTF_PRESENT equ 001h\r\nPTF_RW      equ 002h\r\nPTF_USER    equ 004h\r\nPTF_PWT     equ 008h ; page write through\r\nPTF_PCD     equ 010h ; page cache disable\r\nPTF_ACCESS  equ 020h ; page accessed\r\nPTF_DIRTY   equ 040h ; page dirty\r\nPTF_4MB     equ 080h ; PDEs: 4MB PDE\r\nPTF_GBL     equ 100h ; page global (P3+)\r\n\r\n;--- features, returned by CPUID in EDX\r\nCF_FPU      equ 0001h\r\nCF_VME      equ 0002h\r\nCF_DE       equ 0004h;debugging ext\r\nCF_PSE      equ 0008h;Page Size Ext (4MB)\r\nCF_TSC      equ 0010h;Time Stamp Counter\r\nCF_MSR      equ 0020h;RDMSR/WRMSR supported\r\nCF_PAE      equ 0040h;Physical Address Ext\r\nCF_MCE      equ 0080h;Machine Check Exceptions\r\nCF_CX8      equ 0100h;CMPXCHG8B supported\r\nCF_APIC     equ 0200h;APIC exists\r\n;rsvd       equ 0400h;\r\nCF_SEP      equ 0800h;SYSENTER/SYSEXIT supported\r\nCF_MTRR     equ 1000h\r\nCF_PGE      equ 2000h\r\n\r\n;--- flags in CR4\r\nCR4_VME      equ 01h\r\nCR4_PVI      equ 02h\r\nCR4_TSD      equ 04h;1=time stamp disable\r\nCR4_DE       equ 08h\r\nCR4_PSE      equ 10h;1=page size extensions enabled\r\nCR4_PAE      equ 20h\r\nCR4_MCE      equ 40h\r\nCR4_PGE      equ 80h\r\n\r\n;--- descriptor\r\n\r\nDESCRIPTOR struct\r\nwLimit  dw ?    ;+0\r\nwA0015  dw ?    ;+2\r\nbA1623  db ?    ;+4\r\nbAttr   db ?    ;+5\r\nbLimEx  db ?    ;+6\r\nbA2431  db ?    ;+7\r\nDESCRIPTOR ends        \r\n\r\nGATE struct\r\nwOfsLo dw ?\r\nwSeg   dw ?\r\nwAttr  dw ?\r\nwOfsHi dw ?\r\nGATE ends\r\n\r\n;--- TSS structure\r\n;--- the only fields in the TSS which are needed are tsEsp0, tsSS0\r\n;--- and tsOfs. Jemm386 will never switch tasks.\r\n\r\nTSSSEG  struct\r\n        dd ?    ;+00 selector\r\ntsEsp0  dd ?    ;+04\r\ntsSS0   dd ?    ;+08\r\n        dq ?    ;+0C\r\n        dq ?    ;+14\r\ntsCR3   dd ?    ;+1C\r\ntsEip   dd ?    ;+20\r\ntsEfl   dd ?    ;+24\r\ntsEax   dd ?    ;+28\r\ntsEcx   dd ?    ;+2C\r\ntsEdx   dd ?    ;+30\r\ntsEbx   dd ?    ;+34\r\ntsEsp   dd ?    ;+38\r\ntsEbp   dd ?    ;+3C\r\ntsEsi   dd ?    ;+40\r\ntsEdi   dd ?    ;+44\r\ntsES    dd ?    ;+48\r\ntsCS    dd ?    ;+4C\r\ntsSS    dd ?    ;+50\r\ntsDS    dd ?    ;+54\r\ntsFS    dd ?    ;+58\r\ntsGS    dd ?    ;+5C\r\ntsLDTR  dd ?    ;+60\r\ntsFlags dw ?    ;+64\r\ntsOfs   dw ?    ;+66\r\nTSSSEG  ends\r\n\r\n;--- stack frame for PUSHAD\r\n\r\nPUSHADS struct\r\nrEDI    dd ?\r\nrESI    dd ?\r\nrEBP    dd ?\r\n        dd ?    ;reserved\r\nrEBX    dd ?\r\nrEDX    dd ?\r\nrECX    dd ?\r\nrEAX    dd ?\r\nPUSHADS ends\r\n\r\n;--- RETF stack frame for 16bit\r\n\r\nRETFWS struct\r\nunion\r\n struct\r\n_Ip     dw ?\r\n_Cs     dw ?\r\n ends\r\n_CsIp   dd ?\r\nends\r\nRETFWS ends\r\n\r\n;--- IRET stack frame for 16bit\r\n\r\nIRETWS struct\r\n        RETFWS <>\r\n_Fl     dw ?\r\nIRETWS ends\r\n\r\n;--- IRET stack frame for 32bit protected-mode\r\n\r\nIRETDS struct\r\n_Eip    dd ?    ;+0\r\n_Cs     dd ?    ;+4 \r\n_Efl    dd ?    ;+8\r\nIRETDS ends\r\n\r\n;--- stack frame expected by ring0-IRETD to switch to v86-mode\r\n\r\nIRETDV86 struct\r\n_Eip    dd ?    ;+0\r\n_Cs     dd ?    ;+4 \r\n_Efl    dd ?    ;+8\r\n_Esp    dd ?    ;+12\r\n_Ss     dd ?    ;+16\r\n_Es     dd ?    ;+20\r\n_Ds     dd ?    ;+24\r\n_Fs     dd ?    ;+28\r\n_Gs     dd ?    ;+32\r\nIRETDV86 ends\r\n\r\n;--- bit positions of CPUID edx\r\nCPUID_VME equ 1\t\t; =2\r\nCPUID_PGE equ 13\t; =2000h\r\nCPUID_PAT equ 16\t; =10000h\r\n\r\n"
  },
  {
    "path": "JLM/AHCICD/AHCICD.ASM",
    "content": "\n;--- JLM to read CD/DVD in AHCI mode.\n;--- \"cooked\" mode is supported only.\n\n\t.386\n\t.model flat\n\toption casemap:none\n\toption proc:private\n\n\tinclude jlm.inc\n\n?OPTIONR equ 1      ; 1=enable /R option to relocate Port.CLB, Port.FB, Commandlist[0].CTBA\n?IRQWND  equ 1      ; 1=use Jemm's MoveMemory() that allows interrupts during copy op\nREQOFS   equ 16h\t; must be at least \"sizeof DOSDRV\"\nSLCOOKED equ 2048\nSLRAW    equ 2352   ;CD-ROM \"raw\" sector length.\n\n;--- macros\n\nCStr macro text:vararg\nlocal sym\n\t.const\nsym db text,0\n\t.code\n\texitm <offset sym>\nendm\n\n@dprintf macro text,args:vararg\nlocal sym\nifdef _DEBUG\n\t.const\nsym db text,0\n\t.code\n\tifnb <args>\n\t\tinvoke printf, offset sym, args\n\telse\n\t\tinvoke printf, offset sym\n\tendif\nendif\nendm\n\n;--- structs\n\nDOSDRV struct\n\t\tdd ?\nwAttr\tdw ?\nofsStr\tdw ?\nofsInt\tdw ?\nname_\tdb 8 dup (?);+10\nwRes1\tdw ?\t\t;+18 req. for cd drivers, init with 0\nbRes2\tdb ?\t\t;+20 req. for cd drivers, modified by mscdex\nbUnits\tdb ?\t\t;+21 set by driver\nDOSDRV ends\n\n;--- DOS \"Request Packet\" Layouts.\n\nRPH struct\nRPHLen  db  ?       ;+0 Header byte count.\nbSubU   db  ?       ;+1 Subunit number.\nbOp     db  ?       ;+2 Command code.\nwStat   dw  ?       ;+3 Status field.\n        db 8 dup (?);+5 .. +12 (reserved).\nRPH ends\n\nRPERR   equ 8003h   ;Packet \"error\" flags.\nRPDON   equ 0100h   ;Packet \"done\" flag.\nRPBUSY  equ 0200h   ;Packet \"busy\" flag.\n\n;--- Init \"Request Packet\" Layout\n\nRPInit struct\n        RPH <>\nbUnit   db  ?       ;+13 Number of units found.\ndwFree  dd  ?       ;+14 return: far16 ptr first free byte behind driver\nCmdLine dd  ?       ;+18 Command-line data pointer.\nRPInit  ends\n\n;--- IOCTL \"Request Packet\" Layout\n\nRPIOC struct\n        RPH <>\n        db  ?       ;+13 Media descriptor byte (Unused by us).\ndwAddr  dd  ?       ;+14 Data-transfer address.\nwLength dw  ?       ;+18 Data-transfer length.\n        dw  ?       ;+20 Starting sector (unused by us).\n        dd  ?       ;+22 Volume I.D. pointer (unused by us).\nRPIOC ends\n\n;--- Read Long \"Request Packet\" Layout\n\nRPRL struct\n        RPH <>\nbAMode  db  ?       ;+13 Addressing mode.\ndwAddr  dd  ?       ;+14 Data-transfer address.\nwSecCnt dw  ?       ;+18 Data-transfer sector count.\ndwStart dd  ?       ;+20 Starting sector number.\nbDMode  db  ?       ;+24 Data-transfer mode (raw/cooked).\nbILSize db  ?       ;+25 Interleave size.\nbILSkip db  ?       ;+26 Interleave skip factor.\nRPRL  ends\n\n;--- AHCI structures\n\nHBA struct\ndwCAP   dd ?\t;+0 HBA capabilities\ndwGHC   dd ?\t;+4 global HBA control\ndwIS    dd ?\t;+8 interrupt status\ndwPI    dd ?\t;+12 ports implemented\nHBA ends\n\nPORT struct\ndqCLB   dq ?\t;+0  Command list base addr ( 1 kB aligned )\ndqFB    dq ?\t;+8  (received) FIS base addr ( 256 B aligned )\ndwIS    dd ?\t;+16 interrupt status\ndwIE    dd ?\t;+20 interrupt enable\ndwCMD   dd ?\t;+24 command and status\n        dd ?\t;+28\ndwTFD   dd ?\t;+32 task file data\ndwSIG   dd ?\t;+36 signature\ndwSSTS  dd ?\t;+40 SATA status\ndwSCTL  dd ?\t;+44 SATA control\ndwSERR  dd ?\t;+48 SATA error\ndwSACT  dd ?\t;+52 SATA active\ndwCI    dd ?\t;+56 command issued\nPORT ends\n\n;--- flags dwIS field\nPIS_CPDS equ 80000000h\nPIS_TFES equ 40000000h\nPIS_HBFS equ 20000000h\nPIS_HBDS equ 10000000h\nPIS_SDBS equ 00000008h\t; set device bits interrupt\nPIS_DSS  equ 00000004h\t; DMA setup FIS interrupt\nPIS_PSS  equ 00000002h\t; PIO setup FIS interrupt\nPIS_DHRS equ 00000001h\t; device to host register FIS interrupt\n\n;--- flags dwCMD field\nPCMD_CR  equ 8000h\t; RO, 1=command list DMA engine running\nPCMD_FR  equ 4000h\t; RO, 1=FIS Receive running\nPCMD_FRE equ 0010h\t; RW, 1=FIS Receive Enable\nPCMD_CLO equ 0008h\t; RW, 1=command list override\nPCMD_POD equ 0004h\t; RW, Power On Device\nPCMD_SUD equ 0002h\t; RW, Spin Up Device\nPCMD_ST  equ 0001h\t; RW, Start (HBA may process the command list)\n\n\n;--- AHCI command list header\n\nCLH struct\nflags1  db ?\t;+0  P[7]=Prefetchable, W[6]=Write, A[5]=ATAPI, CFL[4:0]=Command FIS Length\nflags2  db ?\t;+1  PMP[15:12]=Port Multiplier Port, R[11]=Reserved, C[10]=Clear Busy, B[9]=BIST, R[8]=Reset\nPRDTL   dw ?\t;+2  Physical Region Descriptor Table Length\nPRDBC   dd ?\t;+4  Physical Region Descriptor Byte Count\ndwCTBA  dd ?\t;+8  Command Table Base Address bits 0-31 (128 byte aligned)\ndwCTBAU dd ?\t;+12 Command Table Base Address bits 32-63\n\tdd 4 dup (?)\nCLH ends\n\nPRDT struct\ndwBase   dd ?\t;+0 base\ndwBaseU  dd ?\t;+4 base upper\ndwRsvd   dd ?\t;+8 reserved\ndwCnt    dd ?\t;+12 cnt  \nPRDT ends\n\n;--- AHCI command table\n\nCT struct\ncfis0  dd ?\ncfis1  dd ?\ncfis2  dd ?\ncfis3  dd ?\n       db 30h dup (?)\t; 40h=offset ATAPI in command table\nPkt0   dd ?\nPkt1   dd ?\nPkt2   dd ?\n       db 34h dup (?)\t; 80h=offset PRDT in command table\np0     PRDT <>\nCT ends\n\n; packet commands:\n; - 003h:  request sense\n; - 01Bh:  start/stop unit\n; - 01Eh:  prevent/allow media removal\n; - 025h:  read capacity\n; - 028h:  read (cooked)\n; - 02Bh:  seek\n; - 042h:  read sub-channel\n; - 043h:  read TOC\n; - 047h:  play audio MSF\n; - 04Ah:  get event status notification\n; - 04Bh:  pause/resume\n; - 05Ah:  mode sense\n; - 0BEh:  read CD (raw)\n\nif 0\nPacket struct\nPktOPC  db  0       ;+0 packet command.\n        db  0       ;+1 Unused (LUN and reserved).\nPktLBA  dd  0       ;+2 CD-ROM logical block address.\nPktLH   db  0       ;+6 \"Transfer length\" (sector count).\nPktLn   dw  0       ;+7 Middle- and low-order sector count.\nPktRM   db  0       ;+9 Read mode (\"Raw\" Read Long only).\n        dw  0       ;+10 Unused ATAPI \"pad\" bytes (required).\nPacket ends\nendif\n\n\t.data?\n\nif ?OPTIONR\ncmdlst\tCLH 32 dup (<>)\t; must be 1024 B aligned\nfis\t\tdb 100h dup (?)\t; must be 256 B aligned\ncmdtab  CT <>\t\t\t; must be 128 B aligned\nendif\nbuffer db SLCOOKED dup (?)\n       db SLRAW - SLCOOKED dup (?)\n\nppReq       dd ?\t; linear address where request header is stored\npRequest    dd ?\t; linear address request header\npBase       dd ?\t; linear address driver base\npDest       dd ?\t; linear address destination\npHBA        dd ?\t; linear address HBA\npBuff       dd ?\t; linear address sector buffer\ndwBuffPh    dd ?\t; physical address sector buffer\ndwSector    dd ?\t; current sector to read\nif ?OPTIONR\npCLB        dd ?\t; linear address Port[x].CLB\npCTBA       dd ?\t; linear address CL[0].CTBA\nendif\n\nwPort   dw 6 dup (?); port offsets of max. 6 CD/DVDs\nwSecCnt dw ?\t\t; #sectors to read\nbUnits  db ?\t\t; found units (during init)\nbCntOpt db ?\t\t; /C option default value\nbQuiet  db ?\t\t; /Q option\nif ?OPTIONR\nbReloc  db ?\t\t; /R option\nendif\nbBanner db ?\t\t; 1=banner displayed\n\n\t.const\n\nERRTAB  db 12,12,2,4,12,0,15,8,8,12,12,12,12,12,12,12\n\nrmcode label byte\n\tdb 2Eh, 89h, 1eh, REQOFS+0, 0\t;mov cs:[16h],bx\n\tdb 2Eh, 8Ch, 06h, REQOFS+2, 0\t;mov cs:[18h],es\n\tdb 0CBh \t\t\t\t\t\t;retf\nSIZERMCODE equ $ - offset rmcode\t\n\tdb 0EAh \t\t\t\t\t\t;jmp ssss:oooo\n\n\t.code\n\nifdef _DEBUG\n\tinclude printf.inc\nendif\n\nDevInt proc\n\n\t.const\n\n\talign 4\n\n;--- cmds 00-0E\nVector label dword\n\tdd Init\t\t; 0 Init\n\tdd Error3\t; 1 media check (block devices)\n\tdd Error3\t; 2 build bpb (block devices)\n\tdd IOCtlInp\t; 3 ioctl input\n\tdd Error3\t; 4 input\n\tdd Error3\t; 5 nondestr. input\n\tdd Error3\t; 6 input status (char devices)\n\tdd Error3\t; 7 input flush (char devices)\n\tdd Error3\t; 8 output\n\tdd Error3\t; 9 output with verify\n\tdd Error3\t;10 output status (char devices)\n\tdd Error3\t;11 output flush (char devices)\n\tdd Error3\t;12 ioctl output\n\tdd Exit\t\t;13 device open\n\tdd Exit\t\t;14 device close\n;\tdd Error3\t;15 removable media (block devices)\nLVECTOR equ ($ - offset Vector) / sizeof dword\n\n;--- cmds 128-13x\nVect2 label dword\n\tdd ReadL\t;128 CD read long\n\tdd Error3\t;129 CD reserved\n\tdd Exit\t\t;130 CD read long prefetch\n;\tdd Error3\t;131 CD seek\n;\tdd Error3\t;132 CD play audio\n;\tdd Error3\t;133 CD stop audio\n;\tdd Error3\t;134 CD write long\n;\tdd Error3\t;135 CD write long verify\n;\tdd Error3\t;136 CD resume audio\nLVECT2 equ ($ - offset Vect2) / sizeof dword\n\n\t.code\n\n\tmov ebx, [ppReq]\n\tmovzx eax, word ptr [ebx+0]\n\tmovzx ebx, word ptr [ebx+2]\n\tshl ebx, 4\n\tadd ebx, eax\t\t\t;Point to DOS request packet.\n\tmov [pRequest], ebx\t\t;linear address request header\n\n\tmov al, [ebx].RPH.bOp\n\n\t@dprintf <'DevInt enter, Op=%X, flags=%X',13,10>, eax, [ebp].Client_Reg_Struc.Client_EFlags\n\n\tand al, al\n\tjz @F\n\tmov al, [bUnits]\n\tcmp [ebx].RPH.bSubU, al\n\tmov ax, 8101h\n\tjnc ExitAX\n@@:\n\tmov esi, offset Vector\n\tmov cl, LVECTOR\n\tmovzx eax, [ebx].RPH.bOp\n\tand al, al\n\tjns @F\n\tmov esi, offset Vect2\n\tand al, 7Fh\n\tmov cl, LVECT2\n@@:\n\tcmp al, cl\n\tjnc Error3X\n\tpush [ebp].Client_Reg_Struc.Client_EFlags\n\tor byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, 2\t; set client IF so Yield will enable interrupts\n\tcall dword ptr [esi+eax*4]\n\tpop [ebp].Client_Reg_Struc.Client_EFlags\nExitAX:\n\t@dprintf <'DevInt exit, ax=%X',13,10>, ax\n\tmov ebx, [pRequest]\n\tmov [ebx].RPH.wStat, ax\n\t@VMMCall Simulate_Far_Ret\n\tret\nError3X:\n\tpush ExitAX\nError3:\n\tmov ax, 8103h\n\tret\nExit:\n\tmov ax, RPDON\n\tret\n\nDevInt endp\n\n;--- IOCtl Input dispatcher\n\nIOCtlInp proc\n\n\t.const\n\n\talign 4\n\n;--- IOCTL input subcmds\nIVect   label dword\n\tdd IOC_GetHdr\t; 0 get device driver header address\n\tdd IOC_Error3\t; 1 drive head location\n\tdd IOC_Error3\t; 2 reserved\n\tdd IOC_Error3\t; 3 error statistics\n\tdd IOC_Error3\t; 4 audio channel info\n\tdd IOC_Error3\t; 5 raw data bytes\n\tdd IOC_DevSt\t; 6 device status\n\tdd IOC_SecSize\t; 7 sector size\n\tdd IOC_Error3\t; 8 volume size\n\tdd IOC_Media\t; 9 media change status\n;\tdd IOC_Error3\t;10 audio disk info\n;\tdd IOC_Error3\t;11 audio track info\n;\tdd IOC_Error3\t;12 audio q-channel info\n;\tdd IOC_Error3\t;13 audio sub-channel info\n;\tdd IOC_Error3\t;14 UPC code\n;\tdd IOC_Error3\t;15 audio status info\nLIVECT  equ ($ - offset IVect) shr 2\n\n\t.code\n\n\tmovzx eax, word ptr [ebx].RPIOC.dwAddr+0\n\tmovzx esi, word ptr [ebx].RPIOC.dwAddr+2\n\tshl esi, 4\n\tadd esi, eax\n\tmovzx eax, byte ptr [esi]\n\t@dprintf <'DevInt, ioctl input, buffer=%X, al=%X',13,10>, esi, eax\n\tcmp al, LIVECT\n\tjnc IOC_Error3\n\tjmp [eax*4][IVect]\nIOC_Error3:\n\tmov ax, 8103h\n\tretn\nIOC_Error12:\n\tmov ax, 810Ch\n\tretn\n\nIOC_GetHdr:\n\tmov eax, [pBase]\n\tshl eax, 12\n\t@dprintf <'DevInt, ioctl input, gethdr, return=%X',13,10>, eax\n\tmov [esi+1], eax\nIOC_done:\n\tmov ax, RPDON\n\tretn\n\n;--- request \"get device status\"\n;--- status flag bits:\n;--- 001 - door opened\n;--- 002 - door unlocked\n;--- 004 - supports cooked AND raw\n;--- 008 - read AND write\n;--- 010 - data read AND play audio\n;--- 020 - supports interleaving\n;--- 040 - (res)\n;--- 080 - supports prefetching\n;--- 100 - supports audio channel manipulation\n;--- 200 - supports HSG AND red book addressing modes\n\nIOC_DevSt:\n;\tmov dword ptr [esi+1], 2\t;set \"door unlocked\"\n\tmov dword ptr [esi+1], 2+4\t;set \"door unlocked\" & \"cooked AND raw\"\n\tjmp IOC_done\n\nIOC_SecSize:\n\tcmp byte ptr [esi+1],1\t;read mode \"cooked\" or \"raw\"?\n\tja IOC_Error12\t\t\t;No?  Post \"general failure\" & exit.\n\tmov ax, SLRAW\n\tje @F\n\tmov ax, SLCOOKED\n@@:\n\tmov [esi+2], ax\n\tjmp IOC_done\n\nIOCtlInp endp\n\nIOC_Media proc\n\t@dprintf <'DevInt, ioctl input, media',13,10>\n\txor edi, edi\n\tcall Setup\t\t; set EDI=command table, ebx=port offset\n\n;--- write packet data ( 12 bytes )\n;--- 0: 4A - cmd \"get event status notification\"\n;--- 1: 01 - bit 0=1 immediate\n;--- 4: 10 - notification class request, bit 4=1 is \"media\"\n;--- 7-8: 0008 - allocation length ( hibyte first )\n\n;--- ACMD.00: 4A,01,00,00\n;--- ACMD.04: 10,00,00,00\n;--- ACMD.08: 08,00,00,00\n\tmov [edi].CT.Pkt0, 14AH\n\tmov [edi].CT.Pkt1, 10H\n\tmov [edi].CT.Pkt2, 8\n\n\tcall Req8\n\tand al, al\n\tjnz MedChg\n\n;--- request has returned 8 bytes,\n;--- 0-3: event header (0-1 event data length, hibyte first)\n;--- 4-7: event data ( 0=media event [bit 0-3], 1=media status [bit 0-1])\t\n\n\tmov eax, [pBuff]\n\tmov ax, [eax+4]\n\n\tand al, al\n\tjnz MedChg\n\ttest ah, 2  ; bit 1=1 if media present\n\tjz MedChg\n\tmov byte ptr [esi+1], 1\n\tjmp MedEnd\nMedChg:\n\tmov byte ptr [esi+1], 0\nMedEnd:\n\tcall Setup\n\tcall ReqSense\n\n\tmov eax, [pBuff]\n\tmov al, [eax+2]\n\n\tand al, 15\n\tjz @F\n\tmov byte ptr [esi+1], 0\n\tcmp al, 6\n\tjz MedEnd\n@@:\n\tmov ax, RPDON\n\tret\nIOC_Media endp\n\n;--- read sector(s)\n;--- in: ebx=RL\n\nReadL proc\n\tmov al, 3\n\tcmp [ebx].RPRL.bAMode, 0; High Sierra addressing mode?\n\tjnz ErrorAL_AMode\n\tcmp [ebx].RPRL.bDMode, 1; 0=cooked, 1=raw\n\tja ErrorAL_DMode\n\n\tmovzx eax, word ptr [ebx].RPRL.dwAddr+0\n\tmovzx edx, word ptr [ebx].RPRL.dwAddr+2\n\tshl edx, 4\n\tadd eax, edx\n\tmov [pDest], eax\n\n\tmov cx, [ebx].RPRL.wSecCnt\n\tmov eax, [ebx].RPRL.dwStart\n\tmov [wSecCnt], cx\n\tmov [dwSector], eax\n\t@dprintf <'DevInt, readl, start=%X, sectors=%X, dst=%X',13,10>, eax, cx, pDest\n\txor edi, edi\n\tmov esi, ebx\nReadLP:                 ;<----\n\tcmp [wSecCnt], 0\n\tjz Exit\n\tcall Setup\t\t\t; set EDI=command table, ebx=port offset\n\n;--- write packet data ( 12 bytes )\n;--- 0: cmd 28/0BE ( read cooked/raw )\n;--- 1: flags\n;--- 2-5: LBA (high byte first)\n;--- 6: group no\n;--- 7-8: transfer length [sector count] ( high byte first )\n;--- 9: control (0 for cooked, 0F8h for raw mode)\n\n\tmov eax, [dwSector]\n\tshld ecx, eax, 16\n\n;--- ax=sect[0-15]\n;--- cx=sect[16-31]\n\n\tmovzx eax, ax\n\txchg al, ah\n\txchg cl, ch\n\n;--- al=sect[8-15], ah=sect[0-7], cl=sect[24-31], ch=sect[16-23]\n\n\tshl ecx, 16\n\tmov cl, 28H\t\t; read cooked\n;--- ACMD.00: 28h/0BEh (opcode \"read cooked\"),00,Sector.04,Sector.03\n;--- ACMD.04: Sector.02, Sector.01, group#, length.02\n;--- ACMD.08: length.01,00,00,00\n\tmov [edi].CT.Pkt0, ecx\n\tmov [edi].CT.Pkt1, eax\n\tmov [edi].CT.Pkt2, 1\n\n\tmov ecx, 800007FFH\t; PRDT.0C: FF,07,00,80  (PRC=7FF transfer 2048 bytes, 80=I)\n\tcmp [esi].RPRL.bDMode, 0\n\tjz @F\n;--- ACMD.00: 0BE (opcode \"read raw\"),00,Sector.04,Sector.03\n\tmov byte ptr [edi].CT.Pkt0, 0BEh\n\tmov byte ptr [edi].CT.Pkt2+1, 0F8h\n\tmov ecx, 8000092FH\t; PRDT.0C: 2F,09,00,80  (PRC=92F transfer 2352 bytes, 80=I)\n@@:\n\tcall ReqECX\n\tand al, al\n\tjnz readerr\n\tpush edi\n\tpush esi\n\tmov ecx, SLCOOKED\n\tcmp [esi].RPRL.bDMode, 0\n\tjz @F\n\tmov ecx, SLRAW\n@@:\n\tpush ecx\n\tmov edi, [pDest]\n\tmov esi, [pBuff]\n\tcld\nif ?IRQWND\n\t@VMMCall MoveMemory\nelse\n\tshr ecx, 2\n\trep movsd\n\t@VMMCall Yield\nendif\n\tpop ecx\n\tpop esi\n\tpop edi\n\tadd [pDest], ecx\n\tinc [dwSector]\n\tdec [wSecCnt]\n\tjmp ReadLP\nreaderr:\n\tcall Setup\n\tcall ReqSense\n\tand al, al\n\tjnz ErrorAL\n\tmov eax, [pBuff]\n\tmov al, [eax+2]\n\tand al, 15\n\tmov ebx, offset ERRTAB\n\txlat\nErrorAL_AMode:\nifdef _DEBUG\n\t@dprintf <'DevInt, readl, error, addressing mode',13,10>\n\tjmp ErrorAL\nendif\nErrorAL_DMode:\nifdef _DEBUG\n\t@dprintf <'DevInt, readl, error, data mode',13,10>\n\tjmp ErrorAL\nendif\nErrorAL:\n\tmov ah, 81h\n\t@dprintf <'DevInt, readl, error, ax=%X',13,10>, ax\n\tret\nExit:\nifdef _DEBUG\n\tmov ebx, [pRequest]\n\t@dprintf <'DevInt, readl, ok, sector=%X',13,10>, [ebx].RPRL.dwStart\nendif\n\tmov ax, RPDON\n\tret\nReadL endp\n\n;--- in:\n;--- ebx=RP if edi==0\n;--- out:\n;--- ebx=port offset\n;--- edi=linear address ATAPI command table\n\nSetup proc\n\tcmp edi, 0\t\t\t; first call?\n\tjnz @F\n\tmovzx eax, byte ptr [ebx+1]\n\tmovzx ebx, [eax*2][wPort]\n\tadd ebx, [pHBA]\n\n;--- get command list/table bases.\n;--- those are physical addresses and hence \"should\" be translated to linear ones.\n\nif ?OPTIONR\n\tmov eax, [pCLB]\t\t\t\t\t\t; get command list base\n\tmov edi, [pCTBA]\t\t\t\t\t; get command table base address\n\t@dprintf <'DevInt, setup, cmdlist=%X/%X, cmdtable=%X/%X',13,10>, eax, dword ptr [ebx].PORT.dqCLB, edi, [eax].CLH.dwCTBA\nelse\n\tmov eax, dword ptr [ebx].PORT.dqCLB\t; get command list base\n\tmov edi, [eax].CLH.dwCTBA\t\t\t; get command table base address\n\t@dprintf <'DevInt, setup, cmdlist=%X, cmdtable=%X',13,10>, eax, edi\nendif\n\n;--- CLH.00: 25,00,01,00 [CFL=5 dwords, A=1, PRD table length=1]\n;--- CLH.04: 00,00,00,00 [PRD byte count=0]\n\tmov dword ptr [eax].CLH.flags1, 10025H\n\tmov [eax].CLH.PRDBC, 0\n\n@@:\n;--- CT.00: 27,80,A0,01 [27=H2D register FIS,80=set command register,A0=ATAPI packet command,01=Features]\n;--- CT.04: 00,FF,FF,00 [LBA low,mid,high,device]\n;--- CT.08: 00,00,00,00 [LBA (exp) low,mid,high,features]\n;--- CT.0C: 01,00,00,00 [sector count, sector count,res,control]\n\tmov [edi].CT.cfis0, 1A08027H\n\tmov [edi].CT.cfis1, 0FFFF00H\n\tmov [edi].CT.cfis2, 0\n\tmov [edi].CT.cfis3, 1\n\n;--- set base; must be physical address\n\n\tmov ecx, [dwBuffPh]\n\tmov [edi].CT.p0.dwBase, ecx\n\txor ecx, ecx\n\tmov [edi].CT.p0.dwBaseU, ecx\n\tmov [edi].CT.p0.dwRsvd, ecx\n\n\txor ecx, ecx\n\tmov [ebx].PORT.dwIE, ecx\n\tor ecx, -1\n\tmov [ebx].PORT.dwIS, ecx\n\tmov [ebx].PORT.dwSERR, ecx\n\tmov ecx, [ebx].PORT.dwCMD\n\tor cl, PCMD_FRE\t\t; enable FIS receive\n\tmov [ebx].PORT.dwCMD, ecx\n\tor cl, PCMD_ST\t\t; start processing command list\n\tmov [ebx].PORT.dwCMD, ecx\n\t@dprintf <'DevInt, setup, port=%X, cmdtable=%X, waiting',13,10>, ebx, edi\n@@:\n\tmov eax, [ebx].PORT.dwCMD\n\ttest ax, PCMD_CR\t; command list running?\nif 0 ; changed 04/2023\n\tjz @B\nelse\n\tjnz @F\n\t@VMMCall Yield\n\tjmp @B\n@@:\nendif\n\tret\nSetup endp\n\n;--- initiate \"request sense notification\" cmd\n\nReqSense proc\n\n;--- write packet data ( 12 bytes )\n;--- 0: cmd 03 ( request sense )\n;--- 1-3: reserved\n;--- 4: allocation length\n;--- 5: control\n\n;--- ACMD.00: 03,00,00,00\n;--- ACMD.04: 08,00,00,00\n;--- ACMD.08: 00,00,00,00\n\tmov [edi].CT.Pkt0, 3\n\tmov [edi].CT.Pkt1, 8\n\tmov [edi].CT.Pkt2, 0\n\nReqSense endp\n\n;--- fall thru!!!\n\n;--- Req8: read 8 bytes into buffer\n\nReq8:\n\tmov ecx, 80000007H\t; PRDT.12: 07,00,00,80 (PRC=7; read 8 bytes, 80=I)\n\n;--- ReqECX: \n;--- in: ecx: read ECX+1 bytes ( bit 31 = 1, indicating end of table )\n;---     edi: cmd table\n;---     ebx: port\n;--- out: AL=0 ok, AL=12 error\n\nReqECX proc\n\tmov [edi].CT.p0.dwCnt, ecx\n\tmov [ebx].PORT.dwCI, 1\t; bitmask, 1=slot 0 command issued\ncontwait:\n\tcmp [ebx].PORT.dwCI, 0\n\tjz done\n\ttest [ebx].PORT.dwIS, PIS_TFES\n\tjnz error\n\t@VMMCall Yield\n\tcmp [ebx].PORT.dwSERR, 0\n\tJZ contwait\nerror:\n\tcall Stop\n\tmov al, 12\n\tret\ndone:\n\tmov eax, [ebx].PORT.dwTFD\n\ttest al, 1\n\tjnz error\n\tcall Stop\n\tmov al, 0\n\tret\n\nStop:\n\tmov ecx, [ebx].PORT.dwCMD\n\tor cl, PCMD_CLO\n\tand cl, 0EEH\t\t; good idea to reset PCMD_ST and PCMD_FRE here?\n\tmov [ebx].PORT.dwCMD, ecx\n\tretn\n\nReqECX endp\n\n;--- read PCI config address/data ports\n;--- in: edx = addr+80000000h\n;--- out: eax = value\n\nGetPCI proc\n\tpush edx\n\tmov eax, edx\n\tmov dx, 0cf8h\n\tout dx, eax\n\tmov dl, 0fch\n\tin eax, dx\n\tpop edx\n\tret\nGetPCI endp\n\nSavePCI proc\n\tmov dx, 0cf8h\n\tin eax, dx\n\tmov ebx, eax\n\tret\nSavePCI endp\n\nRestPCI proc\n\tmov dx, 0cf8h\n\tmov eax, ebx\n\tout dx, eax\n\tret\nRestPCI endp\n\nif 0\n\n;--- in: edx = addr+80000000h, eax = value\n\nPutPCI proc\n\tpush edx\n\tpush eax\n\tmov eax, edx\n\tmov dx, 0cf8h\n\tout dx, eax\n\tmov dl, 0fch\n\tpop eax\n\tout dx, eax\n\tpop edx\n\tret\nPutPCI endp\n\nendif\n\n;--- display string EDX\n\ndispString proc\n\tpush esi\n\tmov esi, edx\n\t@VMMCall Begin_Nest_Exec\nnextitem:\n\tlodsb\n\tcmp al,0\n\tjz done\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EDX, al\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 2\n\tmov eax, 21h\n\t@VMMCall Exec_Int\n\tjmp nextitem\ndone:\n\t@VMMCall End_Nest_Exec\n\tpop esi\n\tret\ndispString endp\n\ndispBanner proc\n\tcmp bQuiet, 0\n\tjnz @F\n\tcmp bBanner, 0\n\tjnz @F\n\tmov bBanner, 1\n\tpush edx\n\tmov edx, CStr('AHCI Optical Disk Driver v1.2',13,10,\"Inspired by Rudolph R. Loew's AHCICD\",13,10)\n\tcall dispString\n\tpop edx\n@@:\n\tret\ndispBanner endp\n\n;--- ebp: client reg struct\n;--- esi: cmdline\n\nInit proc\n\n;--- scan cmdline args\n\nnextchar:\n\tlodsb\n\tcmp al, ' '\n\tjz nextchar\n\tcmp al, 9\n\tjz nextchar\n\tcmp al, 13\n\tjz donecmdl\n\tcmp al, 0\n\tjz donecmdl\n\tcmp al, '/'\n\tjnz errcmdl\n\tlodsb\n\tmov ah, [esi]\n\tor al, 20h\n\tcmp ax, ':c'\n\tjz isOptC\n\tcmp ax, ':d'\n\tjz isOptD\n\tcmp AL, 'q'\n\tjz isOptQ\nif ?OPTIONR\n\tcmp AL, 'r'\n\tjz isOptR\nendif\n\tjmp errcmdl\n\nisOptD:\n\tinc esi\n\tmov edi, [pBase]\n\tadd edi, DOSDRV.name_\n\tmov dword ptr [edi+0], '    '\n\tmov dword ptr [edi+4], '    '\n\tmov ecx, sizeof DOSDRV.name_\nnextdevchar:\n\tlodsb\n\tcmp al, ' '\n\tjbe doneOptD\n\tcmp al, 'a'\n\tjb @F\n\tcmp al, 'z'\n\tja @F\n\tsub al, 20H\n@@:\n\tstosb\n\tloop nextdevchar\n\tinc esi\ndoneOptD:\n\tdec esi\n\tjmp nextchar\nisOptC:\n\tinc esi\n\tlodsb\n\tcmp al, '0'\n\tjb errcmdl\n\tcmp al, '9'\n\tJa errcmdl\n\tsub al, '0'\n\tmov [bCntOpt], al\n\tjmp nextchar\nisOptQ:\n\tmov [bQuiet], 1\n\tjmp nextchar\nif ?OPTIONR\nisOptR:\n\tmov [bReloc], 1\n\tjmp nextchar\nendif\n\ndonecmdl:\n\n;--- done cmdline processing\n\n\tcall dispBanner\n\n;--- scan for AHCI devices\n\n\tcall SavePCI\n\tmov edx, 80000008H\nnextHBA:\n\tcall GetPCI\n\tmov al, 0\t\t\t; clear bits 0-7 (interface doesn't matter)\n\tcmp eax, 1060100H\t; AHCI device?\n\tjnz @F\n\tdec [bCntOpt]\t\t; /C:x option?\n\tjs  foundHBA\n@@:\n\t@VMMCall Yield\n\tadd edx, 100H\t\t; next \"function\"\n\tcmp edx, 81000008H\n\tjc nextHBA\n\tcall RestPCI\n\tjmp errnodisk\n\nfoundHBA:\n\t@dprintf <'found AHCI device at PCI=%X',13,10>, edx\n\n\tmov dl, 24h\t\t\t;get ABAR5\n\tcall GetPCI\n\tmov edi, eax\n\n\tcall RestPCI\n\n;--- HBA does not necessarily have to start at a page boundary\n;--- so always reserve 2 pages.\n\n\t@dprintf <'HBA physical addr=%X',13,10>, edi\n\n\tpush 0\n\tpush 2\n\tpush PR_SYSTEM\n\t@VMMCall _PageReserve\n\tadd esp, 3*4\n\tcmp eax, -1\n\tjz errpr\n\tmov ebx, eax\n\n\tshr eax, 12\t\t; convert linear address to page#\n\tmov ecx, edi\n\tshr ecx, 12\n\tpush PC_WRITEABLE\n\tpush ecx\n\tpush 2\t\t\t; map 2 pages ( in theory even 3 might be needed )\n\tpush eax\n\t@VMMCall _PageCommitPhys\n\tadd esp, 4*4\n\n\tand edi, 0fffh\n\tadd ebx, edi\n\tmov [pHBA], ebx\n\t@dprintf <'HBA mapped at linear addr=%X',13,10>, ebx\n\tmov eax, [ebx].HBA.dwGHC\t; get Global HBA Control\n\t@dprintf <'HBA.GHC=%X',13,10>, eax\n\ttest eax, 80000000H\t; AHCI enabled?\n\tjz errnoahci\n\n\tmov ecx, [ebx].HBA.dwPI\t; get ports implemented (bit mask)\n\t@dprintf <'HBA.Ports=%X',13,10>, ecx\n\n\tmov edx, 100h\t\t; port offsets are 100h, 180h, 200h, 280h, ..., 1080h\n\tmov edi, offset wPort\nnextPort:\n\tshr ecx, 1\n\tjnc @F\n\tmov eax, [ebx+edx].PORT.dwSIG\t; get signature\n\tcmp eax, 0EB140101H\n\tjnz @F\n\t@dprintf <'found ATAPI device at port=%X',13,10>, edx\n\tmov eax, [ebx+edx].PORT.dwSSTS\t; get SATA status\n\t@dprintf <'device status=%X',13,10>, eax\n\tand al, 0FH\t\t\t; bits 0-3: device detection\n\tcmp al, 3\t\t\t; device detected and communication established?\n\tjnz @F\n\tmov eax, edx\n\tstosw\n\tcmp edi, offset wPort + sizeof wPort; table end reached?\n\tjz donePort\n@@:\n\tadd edx, 80H\n\tand ecx, ecx\n\tJnz nextPort\n\ndonePort:\n\tsub edi, offset wPort\n\tshr edi, 1\n\tmov ecx, edi\n\tcmp ecx, 0\n\tjz errnodisk\n\tmov [bUnits], cl\n\nif ?OPTIONR\n\tmov edi, offset wPort\n\tcmp [bReloc], 0\n\tjz noreloc\n\tmov esi, offset cmdlst\n\tmov [pCLB], esi\n\tmov [pCTBA], offset cmdtab\n\tmov [pBuff], offset buffer\n\tpush ecx\n\tmov ecx, sizeof cmdlst + sizeof fis + sizeof cmdtab + sizeof buffer\n\tmov edx, 1\n\tVxDCall VDMAD_Lock_DMA_Region\n\tpop ecx\n\t@dprintf <'physical/logical address CLB=%X/%X',13,10>, edx, esi\n\tmov esi, edx\n\t.while ecx\n\t\tmovzx edx, word ptr [edi]\n\t\tmov eax, esi\n\t\tmov dword ptr [ebx+edx].PORT.dqCLB, eax\n\t\tadd eax, sizeof cmdlst\n\t\tmov dword ptr [ebx+edx].PORT.dqFB, eax\n\t\tadd edi, sizeof WORD\n\t\tdec ecx\n\t.endw\n\tmov eax, esi\n\tadd eax, sizeof cmdlst + sizeof fis\n\tmov edx, [pCLB]\n\tmov [edx].CLH.dwCTBA, eax\n\t@dprintf <'CLB.CTBA=%X',13,10>, eax\n\tadd eax, sizeof cmdtab\n\tmov [dwBuffPh], eax\n\tjmp buffers_done\nnoreloc:\n\tmovzx edx, word ptr [edi]\n\tmov eax, dword ptr [ebx+edx].PORT.dqCLB\n\tmov edx, [eax].CLH.dwCTBA\n\tmov [pCLB], eax\n\tmov [pCTBA], edx\n\t@dprintf <'linear address command list/table=%X/%X',13,10>, eax, edx\nendif\n\tmov esi, offset buffer\n\tmov [pBuff], esi\n\tmov ecx, SLCOOKED\n\tmov edx, 1\n\tVxDCall VDMAD_Lock_DMA_Region\n\tmov [dwBuffPh], edx\n\t@dprintf <'physical address sector buffer=%X',13,10>, edx\n\nbuffers_done:\n\n\tmov ebx, [pBase]\n\tmov al, [bUnits]\n\tmov [ebx].DOSDRV.bUnits, al\n\tcmp [bQuiet], 0\n\tjnz @F\n\tadd al,'0'\n\tmovzx eax, al\n\tpush eax\n\tmov edx, esp\n\tcall dispString\n\tpop eax\n\tmov edx, CStr(' AHCI Optical Disk(s) Found',13,10)\n\tcall dispString\n@@:\n\tmov ebx, [pRequest]\n\tmov word ptr [ebx].RPInit.dwFree+0, REQOFS+4+SIZERMCODE+5\n\tmov ax, RPDON\n\tmov [ebx].RPInit.wStat, ax\n\tmov [ebx].RPInit.bUnit, 0\n\tret\n\nerrpr:\n\tmov edx, CStr('_PageReserve() failed',13,10)\n\tjmp @F\nerrnoahci:\n\tmov edx, CStr('AHCI Controller not in AHCI Mode',13,10)\n\tjmp @F\nerrcmdl:\nif ?OPTIONR\n\tmov edx, CStr('SYNTAX: DEVICE=JLOAD.EXE AHCICD.DLL [/C:#] /D:devname [/Q][/R]',13,10)\nelse\n\tmov edx, CStr('SYNTAX: DEVICE=JLOAD.EXE AHCICD.DLL [/C:#] /D:devname [/Q]',13,10)\nendif\n\tjmp @F\nerrnodisk:\n\tMOV edx, CStr('No AHCI Optical Disk Found',13,10)\n@@:\n\tcall dispBanner\n\tcall dispString\n\tmov ax, RPERR\n\tret\nInit endp\n\nDllMain proc stdcall public uses esi edi hModule:dword, dwReason:dword, dwRes:dword\n\nlocal pCmdLine:dword\n\n\tmov eax, dwReason\n\tcmp eax, 1\n\tjnz done\n\n\tmov esi, dwRes\n\ttest [esi].JLCOMM.wFlags, JLF_DRIVER\n\tjz failed\n\tmovzx ecx, [esi].JLCOMM.wLdrCS\n\tshl ecx, 4\n\tmov [pBase], ecx\n\tlea eax, [ecx+REQOFS]\n\tmov [ppReq], eax\n\tmov eax, [esi].JLCOMM.lpCmdLine\n\tmov [pCmdLine], eax\n\tmov eax, [esi].JLCOMM.lpRequest\n\tmov [pRequest], eax\n\n\tmov esi, offset DevInt\n\txor edx, edx\n\t@VMMCall Allocate_V86_Call_Back\n\tjc failed\n\n\tmov edi, [pBase]\n\tmov [edi].DOSDRV.wAttr, 0C800h\t\t;driver attributes\n\tmov [edi].DOSDRV.ofsStr, REQOFS+4\n\tmov [edi].DOSDRV.ofsInt, REQOFS+4+SIZERMCODE\n\tmov [edi].DOSDRV.wRes1, 0\n\tmov [edi].DOSDRV.bRes2, 0\n\n\tadd edi, REQOFS+4\n\tmov esi, offset rmcode\n\tmov ecx, SIZERMCODE+1\n\tcld\n\trep movsb\n\tstosd\n\n\t@VMMCall Get_Cur_VM_Handle\n\n\tmov esi, [pCmdLine]\n\n;--- set EBP to the client pointer before calling I_Init\n\n\tpush ebp\n\tmov ebp, [ebx].cb_s.CB_Client_Pointer\n\tcall Init\n\tpop ebp\n\n\tcmp ax,RPDON\n\tsetz al\n\tmovzx eax,al\ndone:\n\tret\nfailed:\n\txor eax, eax\n\tret\n\nDllMain endp\n\n\tend DllMain\n\n"
  },
  {
    "path": "JLM/AHCICD/AHCICD.txt",
    "content": "\r\n  1. About AHCICD\r\n\r\n  AHCICD is a JLM to access AHCI optical disks, \"inspired\" by Rudolph R. Loew's\r\n  AHCICD.SYS. To load it, add the following line to your CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE AHCICD.DLL [options]\r\n\r\n  options are:\r\n\r\n   /C:device select AHCI controller if more than one exists.\r\n   /D:name   set device name. Required.\r\n   /Q        no displays unless errors occur.\r\n   /R        relocate AHCI regions [CL/FIS/CT] to extended memory.\r\n\r\n\r\n  2. Hints\r\n\r\n  - AHCI uses tables that may be located in the XBDA - so if the XBDA is to\r\n    be moved, setting option /R is required.\r\n  - AHCICD.DLL needs JLOAD v5.83 - older versions won't disable caching\r\n    of the controller's memory-mapped registers.\r\n\r\n"
  },
  {
    "path": "JLM/AHCICD/MAKE.BAT",
    "content": "@echo off\r\n\r\njwasm -c -coff -nologo -Fl -Sg -I..\\..\\Include AHCICD.ASM\r\nrem jwasm -coff -nologo -D_DEBUG -Fl -Sg -I..\\..\\Include AHCICD.ASM\r\njwlink format win pe hx dll ru native file AHCICD.obj name AHCICD.DLL op q,map\r\n"
  },
  {
    "path": "JLM/AHCICD/MAKEM.BAT",
    "content": "@echo off\r\n\r\nml -c -coff -nologo -Fl -Sg -I..\\..\\Include AHCICD.ASM\r\nlink /nologo /subsystem:native /dll AHCICD.obj /OUT:AHCICD.DLL /MAP\r\n"
  },
  {
    "path": "JLM/GENERIC/GENERIC.ASM",
    "content": "\r\n;--- JLM sample GENERIC\r\n;--- use Makefile to create GENERIC.DLL\r\n\r\n;--- GENERIC is a very simple JLM. It doesn't hook v86-interrupt vectors.\r\n;--- To be called from v86-mode, its entry point must be obtained by the\r\n;--- caller.\r\n\r\n\t.386\r\n\t.model flat, stdcall\r\n\r\n\t.nolist\r\n\tinclude jlm.inc\r\n\t.list\r\n\r\nDEVICE_ID equ 6660h\r\n\r\ncr equ 13\r\nlf equ 10\r\n\r\nDLL_PROCESS_ATTACH  equ 1\r\nDLL_PROCESS_DETACH  equ 0\r\n\r\n\t.data\r\n\r\n;--- the DDB must be make public. The linker will \"export\" this\r\n;--- symbol. This is the simplest method to make JLoad know the\r\n;--- device id.\r\n\r\n\tpublic ddb\r\n\r\nddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,\"GENERIC\",0, 0, v86_dispatch>\r\n\r\nszHello db \"Hello from JLM GENERIC\",cr,lf,0\r\n\r\n\t.code\r\n\r\n;--- dispatcher for v86 services\r\n\r\nv86_dispatch proc\r\n\r\n\t@VMMCall Simulate_Far_Ret\t;emulate a RETF in v86\r\n\r\n\tand [ebp].Client_Reg_Struc.Client_EFlags,not 1  ;clear Carry flag\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX\r\n\tcmp eax,0\r\n\tjz getversion\r\n\tcmp eax,1\r\n\tjz display_hello\r\n\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\tret\r\n\talign 4\r\n\r\nv86_dispatch endp\r\n\r\ngetversion proc\r\n\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0100h\r\n\tret\r\n\talign 4\r\n\r\ngetversion endp\r\n\r\ndisplay_hello proc uses esi\r\n\r\n\t@VMMCall Begin_Nest_Exec \t;start nested execution\r\n\tmov esi, offset szHello\r\n@@:\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\r\n;--- Call int 21h, ah=2 in v86-mode.\r\n;--- Be aware that in Jemm's context is no DOS extender installed.\r\n;--- So there is no translation for DOS functions which use pointers.\r\n\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EDX,al\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,2\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tjmp @B\r\ndone:\r\n\t@VMMCall End_Nest_Exec\t\t;end nested execution\r\n\tret\r\n\talign 4\r\n\r\ndisplay_hello endp\r\n\r\n;--- install the JLM: just set eax=1\r\n;--- this tells JLOAD that it's ok to add GENERIC to the list of\r\n;--- loaded modules.\r\n\r\ninstall proc uses esi pcomm:ptr JLCOMM\r\n\r\n\tmov eax,1\r\n\tret\r\n\talign 4\r\n\r\ninstall endp\r\n\r\n;--- deinstall the JLM: just set eax=1.\r\n;--- this tells JLOAD that it's ok to remove the module.\r\n\r\ndeinstall proc pcomm:ptr JLCOMM\r\n\r\n\tmov eax,1\r\n\tret\r\n\talign 4\r\n\r\ndeinstall endp\r\n\r\nDllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n\tmov eax,dwReason\r\n\tcmp eax,DLL_PROCESS_ATTACH\r\n\tjnz @F\r\n\tinvoke install, dwRes\r\n\tjmp exit\r\n@@:\r\n\tcmp eax,DLL_PROCESS_DETACH\r\n\tjnz @F\r\n\tinvoke deinstall, dwRes\r\n@@:\r\nexit:\r\n\tret\r\n\talign 4\r\n\r\nDllMain endp\r\n\r\n\tend DllMain\r\n"
  },
  {
    "path": "JLM/GENERIC/MAKEFILE",
    "content": "\r\n# NMake/WMake Makefile to create GENERIC.DLL\r\n#\r\n#  tools                      alternatives\r\n#----------------------------------------------------------\r\n#  JWasm                      Masm v6.x\r\n#  JWLink                     Link, WLink, ALink\r\n\r\nNAME = GENERIC\r\nOUTDIR=Release\r\nAOPT=-nologo -c -coff -nologo -Fl$*.lst -Fo$*.obj -I..\\..\\Include\r\nASM=jwasm.exe\r\n#LINK=link.exe /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /EXPORT:ddb /Entry:DllMain /OPT:NOWIN98\r\nLINK=jwlink format win nt hx dll ru native file $*.obj name $*.DLL op q,map=$*.MAP export _ddb.1\r\n\r\nALL: $(OUTDIR) $(OUTDIR)\\$(NAME).DLL $(OUTDIR)\\TESTGEN.COM\r\n\r\n$(OUTDIR):\r\n\t@mkdir $(OUTDIR)\r\n\r\n$(OUTDIR)\\$(NAME).DLL: $(OUTDIR)\\$(NAME).obj Makefile\r\n\t@$(LINK)\r\n\r\n$(OUTDIR)\\$(NAME).obj: $(NAME).asm\r\n\t@$(ASM) $(AOPT) $(NAME).asm\r\n\r\n$(OUTDIR)\\TESTGEN.COM: TESTGEN.ASM\r\n\t@jwasm -nologo -bin -Fo$*.COM -Fl$* testgen.asm\r\n"
  },
  {
    "path": "JLM/GENERIC/README.TXT",
    "content": "\r\n 1. About\r\n\r\n GENERIC is a JLM sample which demonstrates how to implement a\r\n protected-mode TSR.\r\n\r\n\r\n 2. How to install and uninstall GENERIC\r\n\r\n GENERIC can be installed either as a device driver in CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE GENERIC.DLL\r\n\r\n or as a TSR from the command line:\r\n\r\n   JLOAD GENERIC.DLL\r\n\r\n To uninstall, use JLOAD's -u option:\r\n\r\n   JLOAD -u GENERIC.DLL\r\n\r\n\r\n 3. How to use GENERIC\r\n\r\n GENERIC doesn't hook real-mode interrupt vectors. If a real-mode program\r\n wants to call a function provided by GENERIC, it first has to get the JLM's\r\n entry point. This is done similiar to Windows 9x: a call of Int 2F, \r\n AX=1684h with register BX containing the \"device id\". On return, if the\r\n \"device\" has been found, ES:DI will contain its entry address:\r\n \r\n  mov ax, 1684h   ;standard value to get VxD entry points\r\n  mov bx, 6660h   ;6660h is the ID of GENERIC\r\n  int 2Fh\r\n  cmp al,0\r\n  jnz not_installed\r\n  mov word ptr [genpm+0],di\r\n  mov word ptr [genpm+2],es\r\n\r\n The protected-mode TSR implements two functions, which are selected\r\n by the value of register AX:\r\n\r\n  mov ax, 0000             ;0000=get version\r\n  call dword ptr [genpm]\r\n\r\n  mov ax, 0001             ;0001=display hello\r\n  call dword ptr [genpm]\r\n\r\n There's a test program - TESTGEN.EXE - supplied which uses GENERIC.\r\n\r\n\r\n 4. License\r\n\r\n GENERIC is Public Domain.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "JLM/GENERIC/TESTGEN.ASM",
    "content": "\r\n;--- test app which calls some \"services\" of JLM GENERIC.\r\n;--- assemble: JWasm -bin -Fo testgen.com testgen.asm\r\n\r\n\t.model tiny\r\n\r\n\t.data\r\n\r\nszOK    db \"GENERIC found!\",13,10,'$'\r\nszError db \"GENERIC is NOT installed!\",13,10,'$'\r\n\r\n\t.code\r\n\r\n\torg 100h\r\n\r\nstart:\r\n\tmov ax, 1684h\t;get GENERIC's entry point\r\n\tmov bx, 6660h\r\n\tint 2Fh\r\n\tcmp al,0\r\n\tjnz not_installed\r\n\tpush es\r\n\tpush di\r\n\tmov bp,sp\r\n\tmov dx,offset szOk\r\n\tmov ah,9\r\n\tint 21h\r\n\tmov ax,0000     ;call \"get version\"\r\n\tcall dword ptr [bp]\r\n\tmov ax,0001     ;call \"display hello\"\r\n\tcall dword ptr [bp]\r\n\tadd sp,4\r\n\tint 20h\r\nnot_installed:\r\n\tmov dx,offset szError\r\n\tmov ah,9\r\n\tint 21h\r\n\tint 20h\r\n\r\n\tend start\r\n\r\n"
  },
  {
    "path": "JLM/HELLO/HELLO.ASM",
    "content": "\r\n;--- a simple JLM which displays a message onto the screen\r\n;--- with the help of v86-int 21h and nested execution\r\n\r\n        .386\r\n        .MODEL FLAT, stdcall\r\n\r\n        include jlm.inc\r\n\r\nifdef FMTPE\r\n;--- since v2.19, JWasm's -pe option knows how to interpret linker directives supplied\r\n;--- in the .drectve info section. Thus no extra link step is required.\r\n        option dotname\r\n.drectve segment info\t;linker directives\r\n        db \"-subsystem:native -fixed:no\"\r\n.drectve ends\r\n.hdr$1 segment use16\r\n%       incbin <STUB>\r\n.hdr$1 ends\r\nendif\r\n\r\n        .DATA\r\n\r\nszText  db \"Hello, world!\", 13,10,0\r\n\r\n        .CODE\r\n\r\n_main   proc\r\n\r\n        push esi\r\n        @VMMCall Begin_Nest_Exec     ;start nested execution\r\n        mov esi, offset szText\r\n@@nextitem:\r\n        lodsb\r\n        and al,al\r\n        jz @@done\r\n\r\n;--- Call int 21h, ah=2 in v86-mode.\r\n;--- Be aware that in Jemm's context is no DOS extender installed.\r\n;--- So there is no translation for DOS functions that use pointers.\r\n\r\n        mov byte ptr [ebp].Client_Reg_Struc.Client_EDX,al\r\n        mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,2\r\n        mov eax,21h\r\n        @VMMCall Exec_Int\r\n\r\n        jmp @@nextitem\r\n@@done:\r\n        @VMMCall End_Nest_Exec       ;end nested execution\r\n        pop esi\r\n        mov eax, 80000001h\t;bit 31=1: suppress JLoad msg\r\n        ret\r\n\r\n_main   ENDP\r\n\r\n        END _main\r\n\r\n"
  },
  {
    "path": "JLM/HELLO/MAKE.BAT",
    "content": "@echo off\r\nrem\r\nrem use JWasm & JWlink\r\nrem\r\njwasm -nologo -coff -Fl=Release\\Hello -Fo=Release\\Hello -I..\\..\\Include Hello.asm\r\njwlink format win pe hx ru native file Release\\Hello.obj name Release\\Hello.exe op q,m=Release\\Hello,stack=0x1000\r\n\r\nrem use JWasm - since JWasm v2.19, cmdline option -pe supports several linker features;\r\nrem also, stub JLSTUB.BIN may be added. Then the program can be launched simply by typing its name.\r\nrem\r\nrem jwasm -nologo -pe -DFMTPE -DSTUB=..\\JLSTUB\\Build\\jlstub.bin -Fl=Release\\Hello -Fo=Release\\Hello -I..\\..\\Include Hello.asm\r\nrem patchPE -x -s:0x1000 Release\\Hello.exe\r\n\r\nrem use Masm & Link\r\nrem\r\nrem ml -c -coff -FlRelease\\Hello -FoRelease\\Hello -I..\\..\\Include Hello.asm\r\nrem link /NOLOGO Release\\HELLO.OBJ /subsystem:native /FIXED:NO /map /out:Release\\Hello.exe /ENTRY:_main /OPT:NOWIN98\r\nrem \\hx\\bin\\patchpe -s:0x4000 -x Release\\Hello.exe\r\n"
  },
  {
    "path": "JLM/HELLO/README.TXT",
    "content": "\r\n  About\r\n\r\n  This is the inevitable \"hello world\" JLM, written for Masm/JWasm.\r\n  It displays a welcome message. After execution the module's resources\r\n  are released.\r\n\r\n  Usage: C:\\>JLOAD -q HELLO.EXE\r\n"
  },
  {
    "path": "JLM/HELLO2/HELLO2.C",
    "content": "\r\n// for MS VC\r\n\r\n#include <jlm.h>\r\n\r\nstruct Client_Reg_Struc * pcl;\r\n\r\nstruct cb_s * Get_Cur_VM_Handle()\r\n{\r\n    VxDCall(Get_Cur_VM_Handle);\r\n    _asm mov eax,ebx;\r\n}\r\n\r\nULONG Begin_Nest_Exec()\r\n{\r\n    _asm mov ebp,pcl;\r\n    VxDCall(Begin_Nest_Exec);\r\n}\r\n\r\nULONG End_Nest_Exec()\r\n{\r\n    _asm mov ebp,pcl;\r\n    VxDCall(End_Nest_Exec);\r\n}\r\n\r\nULONG Exec_Int(unsigned long intno)\r\n{\r\n    _asm mov eax, intno;\r\n    _asm mov ebp,pcl;\r\n    VxDCall(Exec_Int);\r\n}\r\n\r\nint main()\r\n{\r\n    struct cb_s * hVM;\r\n    unsigned char * psz = \"Hello world\\r\\n\";\r\n\r\n    hVM = Get_Cur_VM_Handle();\r\n    pcl = (struct Client_Reg_Struc *)hVM->CB_Client_Pointer;\r\n    Begin_Nest_Exec();\r\n\r\n    for (;*psz;psz++) {\r\n        pcl->Client_EAX = 0x0200;\r\n        pcl->Client_EDX = *psz;\r\n        Exec_Int(0x21);\r\n    }\r\n\r\n    End_Nest_Exec();\r\n\r\n    return 1;\r\n};\r\n"
  },
  {
    "path": "JLM/HELLO2/HELLO2W.C",
    "content": "\r\n// for Open Watcom C\r\n// OW inline assembly doesn't know values of enums!\r\n// therefore it needs external module jlmw.asm\r\n\r\n#include <JLM.H>\r\n#include \"JLMW.h\"\r\n\r\nstruct Client_Reg_Struc * pcl;\r\n\r\nint main()\r\n{\r\n    struct cb_s * hVM;\r\n    unsigned char * psz = \"Hello world\\r\\n\";\r\n\r\n    hVM = Get_Cur_VM_Handle();\r\n    pcl = (struct Client_Reg_Struc *)hVM->CB_Client_Pointer;\r\n    Begin_Nest_Exec(pcl);\r\n\r\n    for (;*psz;psz++) {\r\n        pcl->Client_EAX = 0x0200;\r\n        pcl->Client_EDX = *psz;\r\n        Exec_Int(0x21, pcl);\r\n    }\r\n\r\n    End_Nest_Exec(pcl);\r\n\r\n    return 1;\r\n};\r\n"
  },
  {
    "path": "JLM/HELLO2/JLMW.ASM",
    "content": "\r\n;--- helpers for (Watcom) C\r\n\r\n\t.386\r\n    .model flat\r\n    \r\n\tinclude JLM.INC\r\n\r\n\t.code\r\n\r\nGet_Cur_VM_Handle proc stdcall public uses ebx\r\n    @VMMCall Get_Cur_VM_Handle\r\n    mov eax, ebx\r\n    ret\r\n    align 4\r\nGet_Cur_VM_Handle endp\r\n\r\nBegin_Nest_Exec proc stdcall public uses ebp pcl:ptr\r\n    mov ebp,pcl\r\n    @VMMCall Begin_Nest_Exec \r\n    ret\r\n    align 4\r\nBegin_Nest_Exec endp\r\n\r\nEnd_Nest_Exec proc stdcall public uses ebp pcl:ptr\r\n    mov ebp,pcl\r\n    @VMMCall End_Nest_Exec\r\n\tret\r\n    align 4\r\nEnd_Nest_Exec endp\r\n\r\nExec_Int proc stdcall public uses ebp intno:dword, pcl:ptr\r\n    mov eax, intno\r\n    mov ebp,pcl\r\n    @VMMCall Exec_Int\r\n    ret\r\n    align 4\r\nExec_Int endp\r\n\r\n\tend\r\n"
  },
  {
    "path": "JLM/HELLO2/JLMW.h",
    "content": "\r\n// helper procs needed by Open Watcom C\r\n\r\nextern struct cb_s * _stdcall Get_Cur_VM_Handle( void );\r\nextern ULONG _stdcall Begin_Nest_Exec(struct Client_Reg_Struc * pcl);\r\nextern ULONG _stdcall End_Nest_Exec(struct Client_Reg_Struc * pcl);\r\nextern ULONG _stdcall Exec_Int( unsigned long intno, struct Client_Reg_Struc * pcl );\r\n"
  },
  {
    "path": "JLM/HELLO2/MAKEJLMW.BAT",
    "content": "@echo off\r\nrem assembles helper module jlmw for Open Watcom.\r\njwasm -I ..\\..\\Include jlmw.asm\r\n"
  },
  {
    "path": "JLM/HELLO2/MAKEOW.BAT",
    "content": "@echo off\r\nrem uses Open Watcom C and WLink\r\nrem OW needs a helper module: jlmw.obj\r\nwcc386 -mf -zl -zls -s -I..\\..\\Include hello2w.c\r\njwlink format win nt hx ru native file hello2w.obj, jlmw.obj name hello2w.exe option start=main_, map\r\n"
  },
  {
    "path": "JLM/HELLO2/MAKEVC.BAT",
    "content": "@echo off\r\nrem uses MS VC and MS Link\r\n\\msvc71\\bin\\cl -c -I..\\..\\Include hello2.c\r\nlink /LIBPATH:\\msvc71\\lib hello2.obj /subsystem:native /fixed:no /out:hello2.exe /entry:main\r\n\\hx\\bin\\patchpe hello2.exe\r\n"
  },
  {
    "path": "JLM/HELLO2/README.TXT",
    "content": "\r\n About\r\n \r\n Another JLM \"hello world\", this time written in C.\r\n MAKEVC.BAT: for MS VC (or Pelles C, CC386, ...)\r\n MAKEOW.BAT: for Open Watcom.\r\n\r\n to run it: jload /q hello2(w).exe\r\n\r\n There is room for improvement. \r\n"
  },
  {
    "path": "JLM/HELLO2/makeow.sh",
    "content": "#!/bin/sh\nset -e\n# uses Open Watcom C, JWAsm and JWLink which must be in PATH\n# OW needs a helper module: jlmw.obj\njwasm -I../../Include JLMW.ASM\nwcc386 -mf -zl -zls -s -I../../Include HELLO2W.C\njwlink format win nt hx ru native file HELLO2W.o, JLMW.o name hello2w.exe option start=main_, map\n\n"
  },
  {
    "path": "JLM/IOTRAP/IOTRAP.ASM",
    "content": "\r\n;--- JLM sample IOTRAP\r\n;--- use Makefile to create IOTRAP.DLL\r\n\r\n;--- IOTRAP traps IO port 100h when the first client\r\n;--- wants to register a callback.\r\n;--- The client is notified whenever an IO access happens.\r\n\r\n\t.386\r\n\t.model flat, stdcall\r\n\r\nPORT equ 100h\t;port to trap\r\n\r\nCODE16_SEL\tequ 20h\r\nDATA16_SEL\tequ 28h\r\n\r\n\t.nolist\r\n\tinclude jlm.inc\r\n\t.list\r\n\r\nDEVICE_ID equ 6661h\r\n\r\ncr equ 13\r\nlf equ 10\r\n\r\nDLL_PROCESS_ATTACH  equ 1\r\nDLL_PROCESS_DETACH  equ 0\r\n\r\n\t.data\r\n\r\n;--- the DDB must be make public. The linker will \"export\" this\r\n;--- symbol. This is the simplest method to make JLoad know the\r\n;--- device id.\r\n\r\n\tpublic ddb\r\n\r\nddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,\"IOTRAP\",0,0, v86_dispatch >\r\n\r\nszHello db \"Hello from JLM IOTRAP\",cr,lf,0\r\n\r\ncntCB dd 0\r\ncallbacks label dword\r\n\tdd 8*2 dup (0)\t;allow 8 callbacks\r\n\r\n\t.code\r\n\r\n;--- dispatcher for v86 services\r\n\r\nv86_dispatch proc\r\n\r\n\t@VMMCall Simulate_Far_Ret\t;emulate a RETF in v86\r\n\r\n\tand [ebp].Client_Reg_Struc.Client_EFlags,not 1  ;clear Carry flag\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX\r\n\tcmp eax,0\r\n\tjz getversion\r\n\tcmp eax,1\r\n\tjz register_callback\r\n\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\tret\r\n\talign 4\r\n\r\nv86_dispatch endp\r\n\r\ngetversion proc\r\n\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0100h\r\n\tret\r\n\talign 4\r\n\r\ngetversion endp\r\n\r\nregister_callback proc\r\n\t.if ( cntCB >= 8 )\r\n\t\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\t\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 8\r\n\t\tret\r\n\t.endif\r\n;--- when the first client registers, install an io handler \r\n\t.if ( cntCB == 0 )\r\n\t\tmov esi, offset iocb\r\n\t\tmov edx, PORT\r\n\t\t@VMMCall Install_IO_Handler\r\n\t\t.if ( CARRY? )\r\n\t\t\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\t\t\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 9\r\n\t\t\tret\r\n\t\t.endif\r\n\t.endif\r\n\tmov cx, word ptr [ebp].Client_Reg_Struc.Client_ECX\t;callback's CS\r\n\tshl ecx,16\r\n\tmov cx, word ptr [ebp].Client_Reg_Struc.Client_EDX\t;callback's IP\r\n\tmov eax, cntCB\r\n\tmov [eax*8+callbacks+0], ecx\r\n\tmov ecx, [ebp].Client_Reg_Struc.Client_EBX\t\t\t;callback's param\r\n\tmov [eax*8+callbacks+4], ecx\r\n\tinc cntCB\r\n\tret\r\n\talign 4\r\n\r\nregister_callback endp\r\n\r\n;--- io handler proc\r\n;--- ecx=type of io\r\n;--- edx=port\r\n;--- ebp=client struct\r\n;--- eax=io data\r\n\r\nPF32 typedef ptr far32\r\n\r\niocb proc\r\nif 0 ;if the client is to be called in v86-mode\r\n\tpush esi\r\n\tpush edi\r\n\tsub esp, sizeof Client_Reg_Struc\r\n\tmov edi, esp\r\n\tpush ecx\r\n\tpush edx\r\n\tpush eax\r\n\t@VMMCall Save_Client_State\r\n\t@VMMCall Begin_Nest_Exec \t;start nested execution\r\n\tmov esi, offset callbacks\r\n\tmov ecx, cntCB\r\n\r\n\t.while ecx\r\n\t\tpush ecx\r\n\t\tmov eax,[esp+4]\r\n\t\tmov edx,[esp+8]\r\n\t\tmov ecx,[esp+12]\r\n\t\tmov [ebp].Client_Reg_Struc.Client_EDX, edx\r\n\t\tmov [ebp].Client_Reg_Struc.Client_ECX, ecx\r\n\t\tmov [ebp].Client_Reg_Struc.Client_EAX, eax\r\n\t\tlodsd\r\n\t\tmovzx edx,ax\r\n\t\tshr eax,16\r\n\t\tmov ecx, eax\r\n\t\t@VMMCall Simulate_Far_Call\r\n\t\tlodsd\r\n\t\tmov [ebp].Client_Reg_Struc.Client_EBX, eax\r\n\t\t@VMMCall Resume_Exec\t\t;run the VM\r\n\t\tpop ecx\r\n\t\tdec ecx\r\n\t.endw\r\n\r\n\t@VMMCall End_Nest_Exec\t\t;end nested execution\r\n\tlea esi, [esp+3*4]\r\n\t@VMMCall Restore_Client_State\r\n\tpop eax\r\n\tpop edx\r\n\tpop ecx\r\n\tadd esp, sizeof Client_Reg_Struc\r\n\tpop edi\r\n\tpop esi\r\n\tret\r\nelse ;if the client is to be called in protected-mode\r\n\tpushad\r\n\tpush ecx\r\n\tpush edx\r\n\tpush eax\r\n\tsub esp,8\r\n\tsgdt [esp]\r\n\tmov eax,[esp+2]\r\n\tadd esp,8\r\n\tmov esi, offset callbacks\r\n\tmov ecx, cntCB\r\n\r\n\t.while ecx\r\n\t\tpush ecx\r\n\t\tpush eax\r\n\t\tlodsd\r\n\t\tpush CODE16_SEL\r\n\t\tpushw 0\r\n\t\tpush ax\r\n\t\tshr eax,16\r\n\t\tshl eax, 4\r\n\t\tmov edx,[esp+2*4]\r\n\t\tmov [edx+CODE16_SEL+2],ax\r\n\t\tmov [edx+DATA16_SEL+2],ax\r\n\t\tshr eax,16\r\n\t\tmov [edx+CODE16_SEL+4],al\r\n\t\tmov [edx+DATA16_SEL+4],al\r\n\t\tlodsd\r\n\t\tmov ebx, eax\r\n\t\tmov eax,[esp+4*4+0]\r\n\t\tmov edx,[esp+4*4+4]\r\n\t\tmov ecx,[esp+4*4+8]\r\n\t\tpush ds\r\n\t\tpush DATA16_SEL\r\n\t\tpop ds\r\n\t\tcall PF32 ptr [esp+4]\r\n\t\tpop ds\r\n\t\tadd esp,2*4\r\n\t\tpop eax\r\n\t\tpop ecx\r\n\t\tdec ecx\r\n\t.endw\r\n\r\n\tadd esp,3*4\r\n\tpopad\r\n\tret\r\nendif\r\n\talign 4\r\niocb endp\r\n\r\n;--- install the JLM: just set eax=1\r\n;--- this tells JLOAD that it's ok to add IOTRAP to the list of\r\n;--- loaded modules.\r\n\r\ninstall proc uses esi pcomm:ptr JLCOMM\r\n\r\n\tmov eax,1\r\n\tret\r\n\talign 4\r\n\r\ninstall endp\r\n\r\n;--- deinstall the JLM: just set eax=1.\r\n;--- this tells JLOAD that it's ok to remove the module.\r\n\r\ndeinstall proc pcomm:ptr JLCOMM\r\n\r\n\t.if ( cntCB )\r\n\t\tmov edx, PORT\r\n\t\t@VMMCall Remove_IO_Handler\r\n\t.endif\r\n\tmov eax,1\r\n\tret\r\n\talign 4\r\n\r\ndeinstall endp\r\n\r\nDllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n\tmov eax,dwReason\r\n\tcmp eax,DLL_PROCESS_ATTACH\r\n\tjnz @F\r\n\tinvoke install, dwRes\r\n\tjmp exit\r\n@@:\r\n\tcmp eax,DLL_PROCESS_DETACH\r\n\tjnz @F\r\n\tinvoke deinstall, dwRes\r\n@@:\r\nexit:\r\n\tret\r\n\talign 4\r\n\r\nDllMain endp\r\n\r\n\tend DllMain\r\n"
  },
  {
    "path": "JLM/IOTRAP/MAKEFILE",
    "content": "\r\n# NMake/WMake Makefile to create IOTRAP.DLL\r\n#\r\n#  tools                      alternatives\r\n#----------------------------------------------------------\r\n#  JWasm                      Masm v6.x\r\n#  JWLink                     MS Link, WLink, ALink\r\n#  (PatchPE) \r\n# \r\n# PatchPE: HX tool to patch a PE binary to PX ( not required for jwlink ).\r\n\r\nNAME = IOTRAP\r\nOUTDIR=Release\r\nAOPT=-c -coff -nologo -Fl$*.lst -Fo$*.obj -I..\\..\\Include\r\nASM=jwasm.exe\r\n#LINK=link.exe /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /EXPORT:ddb /Entry:DllMain /OPT:NOWIN98\r\nLINK=jwlink format win nt hx dll ru native file $*.obj name $*.DLL op q,MAP=$*.MAP export _ddb.1\r\n\r\nALL: $(OUTDIR) $(OUTDIR)\\$(NAME).DLL $(OUTDIR)\\TESTIOT.COM\r\n\r\n$(OUTDIR):\r\n\t@mkdir $(OUTDIR)\r\n\r\n$(OUTDIR)\\$(NAME).DLL: $(OUTDIR)\\$(NAME).obj Makefile\r\n\t@$(LINK)\r\n#\t@..\\patchpe $*.DLL\r\n\r\n$(OUTDIR)\\$(NAME).obj: $(NAME).asm\r\n\t@$(ASM) $(AOPT) $(NAME).asm\r\n\r\n$(OUTDIR)\\TESTIOT.COM: TESTIOT.asm\r\n\t@$(ASM) -nologo -bin -Fo=$*.COM TESTIOT.asm\r\n"
  },
  {
    "path": "JLM/IOTRAP/README.TXT",
    "content": "\r\n 1. About\r\n\r\n IOTRAP is a JLM sample which demonstrates how to trap IO port access.\r\n\r\n Note: This is somewhat obsolete now; trapping I/O port access in V86-mode\r\n is now better done with the help of JLM QPIEMU.\r\n\r\n\r\n 2. How to install and uninstall IOTRAP\r\n\r\n IOTRAP can be installed either as a device driver in CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE IOTRAP.DLL\r\n\r\n or as a TSR from the command line:\r\n\r\n   JLOAD IOTRAP.DLL\r\n\r\n To uninstall, use JLOAD's -u option:\r\n\r\n   JLOAD -u IOTRAP.DLL\r\n\r\n\r\n 3. How to test IOTRAP\r\n\r\n  - install IOTRAP:  C:\\>JLOAD iotrap.dll\r\n  - install TESTIOT: C:\\>testiot\r\n  - start DEBUG:     C:\\>debug\r\n  - read port 100:   -i 100\r\n\r\n  now a colored string '*#!+' should appear on line 25.\r\n\r\n\r\n 4. License\r\n\r\n IOTRAP is Public Domain.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "JLM/IOTRAP/TESTIOT.ASM",
    "content": "\r\n;--- tsr to test JLM IOTRAP.\r\n;--- it installs a callback which is called whenever port 100h\r\n;--- is accessed.\r\n;--- the callback displays some colored characters on line 25.\r\n;--- assemble: JWasm -bin -Fo testiot.com testiot.asm\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\n\t.data\r\n\r\nszOK1   db \"IOTRAP found.\",13,10,'$'\r\nszOK2   db \"TESTIOT installed.\",13,10,'$'\r\nszErr1  db \"IOTRAP is NOT installed!\",13,10,'$'\r\nszErr2  db \"IOTRAP has wrong version!\",13,10,'$'\r\nszErr3  db \"register callback failed!\",13,10,'$'\r\n\r\n\t.code\r\n\r\n\torg 100h\r\n\r\nstart:\r\n\tmov ax, 1684h\t;get IOTRAP's entry point\r\n\tmov bx, 6661h\r\n\tint 2Fh\r\n\tcmp al,0        ;IOTRAP installed?\r\n\tjnz not_installed\r\n\tpush es\r\n\tpush di\r\n\tmov bp,sp\r\n\tmov dx,offset szOK1\r\n\tmov ah,9\r\n\tint 21h\r\n\tmov ax,0000     ;call \"get version\"\r\n\tcall dword ptr [bp]\r\n;--- check version and exit if not ok\r\n;\tcmp ax,???\r\n;\tjc wrong_version\r\n;--- API: CX:DX=CS:IP of callback, BX=param\r\n\tmov cx,cs\r\n\tmov dx,offset iocb\r\n\tmov bx,0\r\n\tmov ax,0001     ;call \"register callback\"\r\n\tcall dword ptr [bp]\r\n\tjc  register_failed\r\n\tmov dx,offset szOK2\r\n\tmov ah,9\r\n\tint 21h\r\n\tmov dx,offset resident\r\n\tshr dx,4\r\n\tinc dx\r\n\tmov ax,3100h\r\n\tint 21h\r\n\r\niocb:\r\n\r\n;--- callback when i/o was detected.\r\n;--- cpu is in 16-bit protected-mode, ring 0.\r\n;--- EAX=data of IO\r\n;--- DX=IO port\r\n;--- CX=type of IO\r\n;--- BX=value when callback was \"registered\"\r\n;--- DS=data segment\r\n;--- ES=flat segment\r\n;--- SS:ESP=flat host stack!\r\n\r\n\tpush esi\r\n\tmov esi,0B8000h\r\n\r\n;--- display '*#!+' in colors at line 25\r\n\r\n\tmov word ptr es:[esi+160*24+0],'*'+100h*40h\r\n\tmov word ptr es:[esi+160*24+2],'#'+100h*51h\r\n\tmov word ptr es:[esi+160*24+4],'!'+100h*62h\r\n\tmov word ptr es:[esi+160*24+6],'+'+100h*73h\r\n\tpop esi\r\n\tretd    ;a 32bit RETF is needed!!!\r\n\r\nresident label byte\r\n\r\nnot_installed:\r\n\tmov dx,offset szErr1\r\n\tmov ah,9\r\n\tint 21h\r\n\tint 20h\r\nwrong_version:\r\n\tmov dx,offset szErr2\r\n\tmov ah,9\r\n\tint 21h\r\n\tint 20h\r\nregister_failed:\r\n\tmov dx,offset szErr3\r\n\tmov ah,9\r\n\tint 21h\r\n\tint 20h\r\n\r\n\tend start\r\n\r\n"
  },
  {
    "path": "JLM/JCLOCK/JCLOCK.ASM",
    "content": ";\r\n; JCLOCK 1.1\r\n;\r\n; JLM example -- CLOCK for JEMM386/JEMMEX\r\n; It is the first JLM with screen input/output  :-)\r\n;\r\n; (C) 2007 Alexey Voskov\r\n;\r\n; Compile with FASM for WIN32\r\n; and change PE signature into PX\r\n;\r\n\r\nformat PE CONSOLE 4.0 DLL\r\nentry DllEntryPoint\r\n\r\ninclude '%FASMINC%\\win32a.inc'\r\n\r\n; Constants\r\n; 1. Addresses\r\nDispMode = 000449h ; BYTE  - display mode\r\nColNum   = 00044Ah ; WORD  - number of columns \r\nTimer    = 00046Ch ; DWORD - timer of BIOS\r\nCoVideo  = 0B8000h ; Video buffer offset for MODE CO80/CO40\r\nMoVideo  = 0B0000h ; Video buffer offset for MODE MONO\r\n; 2. Text properties\r\nattr  = 2Eh                       ; TEXT ATTRIBUTE\r\nmask0 = attr*1000000h + attr*100h ; Masks for text field\r\nmask1 = mask0 + ':'               ; initialization\r\nmask2 = mask0 + ':' * 10000h      ;\r\n;*********************************************************************\r\nsection '.code' code readable executable\r\n;\r\n; This procedure is a new INT 08h subroutine\r\n;\r\nproc newint08\r\n\t; Initialization\r\n\tpushad\r\n\tmov ebp, esp\r\n\tpush ss\r\n\tpop  ds\r\n\tpush ss\r\n\tpop  es\r\n\t; Timer check\r\n\tmov eax, [Timer] ; Get BIOS timer\r\n\tand eax, 7       ; eax % 8 == 0 \r\n\ttest eax, eax    ; \r\n\tjnz intexit      ; if not -- quit\r\n                         ; (we don't have to do IN/OUT every 1/19 sec!)\r\n\r\n\t; Video mode check\r\n\tmov al, [DispMode]\r\n\tcmp al, 07h      ; MODE MONO ?\r\n\tjne color        ; if not -- check color mode\r\n\tmov ebx, MoVideo ; Load video buffer offset\r\n\tjmp mono\r\n\r\ncolor:\r\n\tcmp al, 03h      ; MODE CO80/CO40 ?\r\n\tja  intexit      ; if not -- quit\r\n\tmov ebx, CoVideo ; Load video buffer offset\r\nmono:\r\n\t; Set up initial position\r\n\tmovzx esi, word [ColNum] ; ESI = 2*ColNum - 16 + EBX\r\n\tshl esi, 1  \r\n\tsub esi, 16\r\n\tadd esi, ebx ; EBX == 0B8000h or 0B0000h\r\n\r\n\t; Clock output\r\nclkout: ; 1. Draw mask\r\n\tmov dword [esi],    mask0\r\n\tmov dword [esi+4],  mask1\r\n\tmov dword [esi+8],  mask2\r\n\tmov dword [esi+12], mask0\r\n\t; 2. Get current time\r\n\tmov al, 4 ; Hours\r\n\tcall printBCDcell\r\n\tadd  esi, 6\r\n\tmov al, 2 ; Minutes\r\n\tcall printBCDcell\r\n\tadd  esi, 6\r\n\tmov al, 0 ; Seconds\r\n\tcall printBCDcell\r\n        ; Call the old INT 08h subroutine\r\nintexit:\r\n\t  popad       ; Restore registers and stack\r\n\t  db 0EAh     ; ASM command:\r\noldint08: db 6 dup 0  ; jmp far 0000:00000000\r\n                      ; (48-bit far call)\r\nendp\r\n\r\n; Procedure prints CMOS cell \r\n; cell must be in BCD format\r\n;\r\n; AL      - cell number\r\n; EBX+ESI - video RAM address\r\n;\r\nproc printBCDcell\r\n\t; Get byte from CMOS RTC\r\n\tout 70h, al     ; Set cell number in CMOS RTC\r\n\tmov ecx, 02000h ; A short delay\r\n@@dl:   inc ecx         ; CMOS RTC memory is slow\r\n\tdec ecx         ;\r\n\tloop @@dl       ;\r\n\tin  al,  71h    ; Get byte from cell\r\n\t; High half-byte of BCD\r\n\tpush ax \r\n\tand  al, 0F0h\r\n\tshr  al, 4\r\n\tadd  al, '0'\r\n\tmov  [esi], al\r\n\tpop  ax \r\n\t; Low half-byte of BCD\r\n\tand  al, 00Fh\r\n\tadd  al, '0'\r\n\tmov  [esi+2], al\r\n\r\n\tret\r\nendp\r\n\r\n; This procedure hooks INT 08h\r\n;\r\n; WARNING -- it contains low-level work with IDT\r\n; In newer versions of JEMM it probably \r\n; will be replaced with special API\r\n;\r\nintn = 08h                          ; INTERRUPT NUMBER\r\nproc install\r\n        ; Beginning\r\n\tsub esp,8                   ; Reserve memory in stack\r\n\t; Get pointer to IDT\r\n        sidt [esp]                  ; Write register of interrupt \r\n                                    ; descriptor table to [ESP]\r\n\tmov esi,[esp+2]             ; Get IDT beginning address into ESI\r\n\t; Save old interrupt descriptor\r\n        mov ax,[esi+6+intn*8]       ; Get bits 31-16\r\n        shl eax,16                  ; of offset\r\n        mov ax,[esi+0+intn*8]       ; Get bits 15-0 of offset\r\n\tmov dword ptr oldint08, eax ; Save offset\r\n        mov ax,[esi+2+intn*8]       ; Get selector\r\n        mov word ptr oldint08+4,ax  ; Save selector\r\n\t; Set up new interrupt descriptor\r\n        mov eax, newint08           ; Get offset of new interrupt procedure\r\n        mov [esi+0+intn*8],ax       ; And write it \r\n        shr eax, 16                 ; into IDT\r\n        mov [esi+6+intn*8],ax       ; (SELECTOR UNCHANGED!)\r\n        ; Final\r\n        add esp,8                   ; Free memory in stack\r\n\tret                         ; Exit \r\nendp\r\n; NOTE:\r\n;\r\n; Interrupt descriptor format (gate):\r\n; | OFFSET(bits 31-16) | FLAGS | SEG SELECTOR | OFFSET (bits 15-0)\r\n;\r\n\r\n\r\n;\r\n; DLL entry point\r\n;\r\nproc DllEntryPoint hinstDLL,fdwReason,lpvReserved\r\n\tcmp\t[fdwReason], DLL_PROCESS_ATTACH\r\n\tjne     @@exitDLL\r\n\tcall    install\r\n\tmov\teax,TRUE\r\n@@exitDLL:\r\n\tret\r\nendp\r\n\r\n;*********************************************************************\r\n;\r\n; * DON'T STRIP RELOCATIONS ! JLM MUST HAVE THEM ! *\r\n;\r\nsection '.reloc' fixups data discardable\r\n"
  },
  {
    "path": "JLM/JCLOCK/JCLOCK2.ASM",
    "content": "\r\n; this is a modified JCLOCK version which uses the Jemm API to hook int 1Ch.\r\n; Thus, it can be unloaded.\r\n;\r\n; (C) 2007 Alexey Voskov\r\n;\r\n; Compile with FASM for WIN32\r\n; and change PE signature into PX\r\n\r\nformat PE CONSOLE 4.0 DLL\r\nentry DllEntryPoint\r\n\r\ninclude '%FASMINC%\\win32a.inc'\r\ninclude '%JLMINC%\\jlmfasm.inc'\r\n\r\n; Constants\r\n; 1. Addresses\r\nDispMode = 000449h ; BYTE  - display mode\r\nColNum   = 00044Ah ; WORD  - number of columns \r\nTimer    = 00046Ch ; DWORD - timer of BIOS\r\nCoVideo  = 0B8000h ; Video buffer offset for MODE CO80/CO40\r\nMoVideo  = 0B0000h ; Video buffer offset for MODE MONO\r\n; 2. Text properties\r\nattr  = 2Eh                       ; TEXT ATTRIBUTE\r\nmask0 = attr*1000000h + attr*100h ; Masks for text field\r\nmask1 = mask0 + ':'               ; initialization\r\nmask2 = mask0 + ':' * 10000h      ;\r\n;*********************************************************************\r\nsection '.code' code readable executable\r\n;\r\n; This procedure is a hook for INT 1Ch\r\n;\r\noldvec dd 0\r\n\r\n\tdd oldvec        ; a hook procedure must be preceeded by a dword\r\n                     ; which contains the address where the old vector\r\n                     ; will be stored.\r\nproc hookproc\r\n\t; Initialization\r\n\t; Timer check\r\n\tpushad\r\n\tmov eax, [Timer] ; Get BIOS timer\r\n\tand eax, 7       ; eax % 8 == 0 \r\n\ttest eax, eax    ; \r\n\tjnz intexit      ; if not -- quit\r\n                         ; (we don't have to do IN/OUT every 1/19 sec!)\r\n\r\n\t; Video mode check\r\n\tmov al, [DispMode]\r\n\tcmp al, 07h      ; MODE MONO ?\r\n\tjne color        ; if not -- check color mode\r\n\tmov ebx, MoVideo ; Load video buffer offset\r\n\tjmp mono\r\n\r\ncolor:\r\n\tcmp al, 03h      ; MODE CO80/CO40 ?\r\n\tja intexit       ; if not -- quit\r\n\tmov ebx, CoVideo ; Load video buffer offset\r\nmono:\r\n\t; Set up initial position\r\n\tmovzx esi, word [ColNum] ; ESI = 2*ColNum - 16 + EBX\r\n\tshl esi, 1  \r\n\tsub esi, 16\r\n\tadd esi, ebx ; EBX == 0B8000h or 0B0000h\r\n\r\n\t; Clock output\r\nclkout: ; 1. Draw mask\r\n\tmov dword [esi],    mask0\r\n\tmov dword [esi+4],  mask1\r\n\tmov dword [esi+8],  mask2\r\n\tmov dword [esi+12], mask0\r\n\t; 2. Get current time\r\n\tmov al, 4 ; Hours\r\n\tcall printBCDcell\r\n\tadd  esi, 6\r\n\tmov al, 2 ; Minutes\r\n\tcall printBCDcell\r\n\tadd  esi, 6\r\n\tmov al, 0 ; Seconds\r\n\tcall printBCDcell\r\nintexit:\r\n\tpopad\r\n\tstc\t\t; signal this INT hasn't been handled yet\r\n\tret\r\nendp\r\n\r\n; Procedure prints CMOS cell \r\n; cell must be in BCD format\r\n;\r\n; AL      - cell number\r\n; EBX+ESI - video RAM address\r\n;\r\nproc printBCDcell\r\n\t; Get byte from CMOS RTC\r\n\tout 70h, al     ; Set cell number in CMOS RTC\r\n\tmov ecx, 02000h ; A short delay\r\n@@dl:   inc ecx     ; CMOS RTC memory is slow\r\n\tdec ecx         ;\r\n\tloop @@dl       ;\r\n\tin  al,  71h    ; Get byte from cell\r\n\t; High half-byte of BCD\r\nif 1\r\n\tdb 0d4h,10h\t\t;AAM 10h\r\n\tadd ax,3030h\r\n\tmov [esi+0],ah\r\n\tmov [esi+2],al\r\nelse\r\n\tpush ax \r\n\tand  al, 0F0h\r\n\tshr  al, 4\r\n\tadd  al, '0'\r\n\tmov  [esi], al\r\n\tpop  ax \r\n\t; Low half-byte of BCD\r\n\tand  al, 00Fh\r\n\tadd  al, '0'\r\n\tmov  [esi+2], al\r\nend if\r\n\tret\r\nendp\r\n\r\n; This procedure hooks INT 1Ch\r\n;\r\nintn = 1Ch                          ; INTERRUPT NUMBER\r\n\r\nproc install\r\n\r\n\tmov eax,intn\r\n\tmov esi,hookproc\r\n\t@VMMCall Hook_V86_Int_Chain\r\n\tret                         ; Exit \r\nendp\r\n\r\nproc deinstall\r\n\r\n\tmov eax,intn\r\n\tmov esi,hookproc\r\n\t@VMMCall Unhook_V86_Int_Chain\r\n\tret                         ; Exit \r\nendp\r\n\r\n;\r\n; DLL entry point\r\n;\r\nproc DllEntryPoint hinstDLL,fdwReason,lpvReserved\r\n\tcmp [fdwReason], DLL_PROCESS_ATTACH\r\n\tjne @F\r\n\tcall install\r\n\tmov eax,TRUE\r\n\tjmp @@exitDLL\r\n@@:\r\n\tcmp [fdwReason], DLL_PROCESS_DETACH\r\n\tjne @@exitDLL\r\n\tcall deinstall\r\n\tmov eax,TRUE\r\n@@exitDLL:\r\n\tret\r\nendp\r\n\r\nsection '.data' data readable writeable\r\n\r\n;--- Sorry, no idea how to create an instance of a structure in fasm ...\r\n\r\n;ddb VxD_Desc_Block 0,0,6655h,1,0,0,0,0,0, 0, 0, 0, 0\r\nddb dd 0\r\n\tdw 0\r\n\tdw 6655h\r\n\tdb 1\r\n\tdb 0\r\n\tdb 0\r\n\tdb \"JCLOCK2 \"\r\n\tdd 80000000h\r\n\tdd 0\r\n\tdd 0\r\n\tdd 0\r\n\tdd 0\r\n\trd 10\r\n\r\n;--- to make the JLM unloadable, it must have a \"device id\". A \"device id\"\r\n;--- is obtained by exporting a DDB\r\n\r\nsection '.edata' export data readable\r\n\r\n\texport 'JCLOCK2.DLL',\\\r\n\tddb,1\r\n\r\n;*********************************************************************\r\n;\r\n; * DON'T STRIP RELOCATIONS ! JLM MUST HAVE THEM ! *\r\n;\r\nsection '.reloc' fixups data discardable\r\n"
  },
  {
    "path": "JLM/JCLOCK/JCLOCK3.ASM",
    "content": "\r\n;--- JCLOCK2 in Masm/JWasm syntax\r\n\r\n\t.386\r\n\t.model flat\r\n\toption casemap:none\r\n\r\n\tinclude jlm.inc\r\n\r\n; Constants\r\n\r\nDLL_PROCESS_ATTACH equ 1\r\nDLL_PROCESS_DETACH equ 0\r\n\r\n; 1. Addresses\r\nDispMode equ 000449h ; BYTE  - display mode\r\nColNum   equ 00044Ah ; WORD  - number of columns \r\nTimer    equ 00046Ch ; DWORD - timer of BIOS\r\nCoVideo  equ 0B8000h ; Video buffer offset for MODE CO80/CO40\r\nMoVideo  equ 0B0000h ; Video buffer offset for MODE MONO\r\n\r\n; 2. Text properties\r\nattr  = 2Eh                       ; TEXT ATTRIBUTE\r\nmask0 = attr*1000000h + attr*100h ; Masks for text field\r\nmask1 = mask0 + ':'               ; initialization\r\nmask2 = mask0 + ':' * 10000h      ;\r\n\r\n\t.data\r\n\r\n\tpublic export ddb\r\n\r\nddb VxD_Desc_Block <0,0,6655h,1,0,0,\"JCLOCK3 \",80000000h>\r\noldvec dd 0\r\n\r\n\toption dotname\r\n\r\n.drectve segment info\r\n\tdb \"-dll -fixed:no -subsystem:native \"\r\n.drectve ends\r\n\r\n.hdr$2 segment dword\r\n\tdb \"PX\"\r\n.hdr$2 ends\r\n\r\n\t.code\r\n;\r\n; This procedure is a hook for INT 1Ch\r\n;\r\nHookProc hookproc, oldvec\r\n\t; Initialization\r\n\t; Timer check\r\n\tpushad\r\n\tmov eax, ds:[Timer] ; Get BIOS timer\r\n\tand eax, 7       ; eax % 8 == 0 \r\n\ttest eax, eax    ; \r\n\tjnz intexit      ; if not -- quit\r\n                         ; (we don't have to do IN/OUT every 1/19 sec!)\r\n\r\n\t; Video mode check\r\n\tmov al, ds:[DispMode]\r\n\tcmp al, 07h      ; MODE MONO ?\r\n\tjne color        ; if not -- check color mode\r\n\tmov ebx, MoVideo ; Load video buffer offset\r\n\tjmp mono\r\n\r\ncolor:\r\n\tcmp al, 03h      ; MODE CO80/CO40 ?\r\n\tja intexit       ; if not -- quit\r\n\tmov ebx, CoVideo ; Load video buffer offset\r\nmono:\r\n\t; Set up initial position\r\n\tmovzx esi, word ptr ds:[ColNum] ; ESI = 2*ColNum - 16 + EBX\r\n\tshl esi, 1  \r\n\tsub esi, 16\r\n\tadd esi, ebx ; EBX == 0B8000h or 0B0000h\r\n\r\n\t; Clock output\r\nclkout: ; 1. Draw mask\r\n\tmov dword ptr [esi],    mask0\r\n\tmov dword ptr [esi+4],  mask1\r\n\tmov dword ptr [esi+8],  mask2\r\n\tmov dword ptr [esi+12], mask0\r\n\t; 2. Get current time\r\n\tmov al, 4 ; Hours\r\n\tcall printBCDcell\r\n\tadd  esi, 6\r\n\tmov al, 2 ; Minutes\r\n\tcall printBCDcell\r\n\tadd  esi, 6\r\n\tmov al, 0 ; Seconds\r\n\tcall printBCDcell\r\nintexit:\r\n\tpopad\r\n\tstc\t\t; signal this INT hasn't been handled yet\r\n\tret\r\nhookproc endp\r\n\r\n; Procedure prints CMOS cell \r\n; cell must be in BCD format\r\n;\r\n; AL      - cell number\r\n; EBX+ESI - video RAM address\r\n;\r\nprintBCDcell proc\r\n\t; Get byte from CMOS RTC\r\n\tout 70h, al     ; Set cell number in CMOS RTC\r\n\tjmp $+2\r\n\tjmp $+2\r\n\tin  al,  71h    ; Get byte from cell\r\n\t; High half-byte of BCD\r\nif 1\r\n\tdb 0d4h,10h\t\t;AAM 10h\r\n\tadd ax,3030h\r\n\tmov [esi+0],ah\r\n\tmov [esi+2],al\r\nelse\r\n\tpush ax \r\n\tand  al, 0F0h\r\n\tshr  al, 4\r\n\tadd  al, '0'\r\n\tmov  [esi], al\r\n\tpop  ax \r\n\t; Low half-byte of BCD\r\n\tand  al, 00Fh\r\n\tadd  al, '0'\r\n\tmov  [esi+2], al\r\nendif\r\n\tret\r\nprintBCDcell endp\r\n\r\n; This procedure hooks INT 1Ch\r\n;\r\nintn = 1Ch                          ; INTERRUPT NUMBER\r\n\r\ninstall proc\r\n\r\n\tmov eax,intn\r\n\tmov esi,hookproc\r\n\t@VMMCall Hook_V86_Int_Chain\r\n\tret                         ; Exit \r\ninstall endp\r\n\r\ndeinstall proc\r\n\r\n\tmov eax,intn\r\n\tmov esi,hookproc\r\n\t@VMMCall Unhook_V86_Int_Chain\r\n\tret                         ; Exit \r\ndeinstall endp\r\n\r\n;\r\n; DLL entry point\r\n;\r\nDllEntryPoint proc stdcall hinstDLL:dword, fdwReason:dword, lpvReserved:dword\r\n\r\n\tcmp [fdwReason], DLL_PROCESS_ATTACH\r\n\tjne @F\r\n\tcall install\r\n\tmov eax,1\r\n\tjmp @@exitDLL\r\n@@:\r\n\tcmp [fdwReason], DLL_PROCESS_DETACH\r\n\tjne @@exitDLL\r\n\tcall deinstall\r\n\tmov eax,1\r\n@@exitDLL:\r\n\tret\r\nDllEntryPoint endp\r\n\r\n\tend DllEntryPoint\r\n"
  },
  {
    "path": "JLM/JCLOCK/MAKE.BAT",
    "content": "@echo off\r\nset FASMINC=\\fasm\\Include\r\nset JLMINC=..\\..\\Include\r\nfasm JCLOCK.ASM\r\nfasm JCLOCK2.ASM\r\n\\hx\\bin\\patchpe JCLOCK.DLL\r\n\\hx\\bin\\patchpe JCLOCK2.DLL\r\n"
  },
  {
    "path": "JLM/JCLOCK/MAKE3.BAT",
    "content": "@echo off\r\nrem make jclock3.dll - no link step required.\r\njwasm -pe -I..\\..\\Include -Fo=JCLOCK3.DLL JCLOCK3.ASM\r\n"
  },
  {
    "path": "JLM/JCLOCK/README.TXT",
    "content": "JCLOCK 1.4 -- Second JLM (JemmEx Loadable Module)\r\n(C) 2007 Alexey Voskov\r\n\r\nShows clock in the text mode.\r\n\r\nUsage\r\n~~~~~\r\nDEVICE=JEMMEX.EXE\r\nDEVICE=JLOAD.EXE JCLOCK.DLL\r\n\r\nHistory\r\n~~~~~~~\r\nVer Date        Changed\r\n-----------------------------------------------------------------\r\n1.4 17-APR-2009 JClock2 supports JLoad's -u switch.\r\n1.3 19-FEB-2008 patchpe missed in MAKE.BAT\r\n1.2 30-JAN-2008 environment variables FASMINC and JLMINC used\r\n1.1 05-MAY-2007 VME compatibility added\r\n                Improved source code (easy change of clock color)\r\n1.0 04-MAY-2007 Original release"
  },
  {
    "path": "JLM/JLSTUB/Build/JLSTUB.lst",
    "content": "JWasm v2.18, May  7 2024\r\nJLSTUB.ASM\r\n\r\n                                ;--- MZ stub to run jload.exe\r\n                                ;--- to be assembled with Masm or JWasm\r\n                                ;--- it's a derivate of dpmildxx... \r\n\r\n = 600                          ?MINMEM\t = 600h\t\t;min free paragraphs for JLOAD.EXE\r\n = 44                           MAXDIR   = 64+4\t\t;max length of a directory path (including 00h)\r\n = 50                           MAXPATH  = MAXDIR+12\r\n = 1                            ?BESAFE  = 1\t\t;1=check if JLoad.exe looks ok.\r\n = 1                            ?DOSMEMCHK = 1\t\t;0=check for sufficient DOS memory\r\n\r\n                                \t.286\r\n\r\n                                ifdef __JWASM__\r\n                                \toption MZ:40h\r\n                                endif\r\n\r\n = D                            cr\t\tequ 13\r\n = A                            lf\t\tequ 10\r\n\r\n00000000                        mzhdr struct\r\n00000000                          e_magic           WORD      ?\t\t;+0\r\n00000002                          e_cblp            WORD      ?\t\t;+2\r\n00000004                          e_cp              WORD      ?\t\t;+4\r\n00000006                          e_crlc            WORD      ?\t\t;+6\t\tnumber of relocation records\r\n00000008                          e_cparhdr         WORD      ?\t\t;+8\r\n0000000A                          e_minalloc        WORD      ?\t\t;+10\r\n0000000C                          e_maxalloc        WORD      ?\t\t;+12\r\n0000000E                          e_ss              WORD      ?\t\t;+14\r\n00000010                          e_sp              WORD      ?\t\t;+16\r\n00000012                          e_csum            WORD      ?\t\t;+18\r\n00000014                          e_ip              WORD      ?\t\t;+20\r\n00000016                          e_cs              WORD      ?\t\t;+22\r\n00000018                          e_lfarlc          WORD      ?\t\t;+24\tbegin relocation records\r\n0000001A                        mzhdr ends\r\n\r\n\r\n0000                            _TEXT   segment public 'CODE'\r\n\r\n = [bp+00h]                     szPgm   equ [bp+00h]     ;execute program name (\"JLOAD.EXE\")\r\n = [bp-MAXPATH]                 szParm  equ [bp-MAXPATH] ;application name (from environment)\r\n\r\n0000                            launch proc\r\n\r\n0000 33D2                       \txor DX,DX\r\n0002 B120                       \tmov cl,20h\r\n0004 B43F                       \tmov ah,3Fh\t\t;read the MZ header\r\n0006 CD21                       \tint 21h\r\n0008 7303E9DD00                 \tjc readerror\r\n\r\n                                if ?BESAFE\r\n\r\n                                ;--- additional tests\r\n\r\n000D 33F6                       \txor si,si\r\n000F 8B04                       \tmov ax, [si].mzhdr.e_magic\r\n0011 3D4D5A                     \tcmp ax,\"ZM\"\r\n0014 7403E9CC00                 \tjnz formerror\r\n0019 8B4406                     \tmov ax, [si].mzhdr.e_crlc\t; no of relocation entries\r\n001C 8BF8                       \tmov di,ax\r\n001E 23C0                       \tand ax,ax\r\n0020 7420                       \tjz norelocs\r\n0022 50                         \tpush ax\r\n0023 33C9                       \txor cx,cx\r\n0025 8B5418                     \tmov dx,[si].mzhdr.e_lfarlc\t; begin relocations\r\n0028 B80042                     \tmov ax,4200h\r\n002B CD21                       \tint 21h\r\n002D 59                         \tpop cx\r\n002E C1E102                     \tshl cx,2\t\t\t\t\t; 4 byte each reloc\r\n0031 2BE1                       \tsub sp,cx\r\n0033 8BD4                       \tmov dx,sp\r\n0035 1E                         \tpush ds\r\n0036 16                         \tpush ss\r\n0037 1F                         \tpop ds\r\n0038 B43F                       \tmov ah,3Fh\t\t\t\t\t; read relocs at SS:SP\r\n003A CD21                       \tint 21h\r\n003C 1F                         \tpop ds\r\n003D 7303E9A300                 \tjc formerror\r\n0042                            norelocs:\r\n0042 8B4408                     \tmov ax,[si].mzhdr.e_cparhdr\t; size of header in paragraphs\r\n0045 FF740E                     \tpush [si].mzhdr.e_ss\r\n0048 FF7410                     \tpush [si].mzhdr.e_sp\r\n004B 8B7414                     \tmov si, [si].mzhdr.e_ip\r\n004E C1E004                     \tshl ax,4\r\n0051 8BD0                       \tmov dx,ax\r\n0053 33C9                       \txor cx,cx\r\n0055 B80042                     \tmov ax,4200h\r\n0058 CD21                       \tint 21h\r\n005A 33D2                       \txor dx,dx\r\n                                endif\r\n\r\n                                ;--- read JLoad.exe binary\r\n\r\n005C B43F                       \tMOV AH,3Fh\r\n005E B90060                     \tmov cx,?MINMEM shl 4\r\n0061 CD21                       \tINT 21h\r\n0063 7303E98200                 \tJC readerror\t;---> error \"read error\"\r\n                                if ?BESAFE        \r\n0068 3BC1                       \tcmp ax,cx\t\t;JLOAD binary must be < 24 kB\r\n006A 7379                       \tjnc formerror\r\n                                endif\r\n006C B43E                       \tmov ah,3Eh\r\n006E CD21                       \tint 21h\r\n\r\n0070 5A                         \tpop dx\r\n0071 5D                         \tpop bp\r\n\r\n                                if ?BESAFE\r\n0072 8BCF                       \tmov cx,di\t\t;some relocs to resolve?\r\n0074 E317                       \tjcxz norelocs2\r\n0076 8BFC                       \tmov di,sp\r\n0078 8CD8                       \tmov ax,ds\r\n007A                            @@:\r\n007A 368B5D02                   \tmov bx,ss:[di+2]\r\n007E C1E304                     \tshl bx,4\t\t;size of loader is <= 24 kB, so no overflow possible\r\n0081 36031D                     \tadd bx,ss:[di+0]\r\n0084 0107                       \tadd [bx],ax\r\n0086 83C704                     \tadd di,4\r\n0089 E2EF                       \tloop @B\r\n008B 8BE7                       \tmov sp,di\r\n008D                            norelocs2:\r\n                                endif\r\n\r\n                                ;--- fill JLoad's PSP - don't overwrite possible cmdline arguments\r\n\r\n008D B451                       \tmov ah,51h\r\n008F CD21                       \tint 21h\r\n0091 56                         \tpush si\r\n0092 8BF4                       \tmov si, sp\r\n0094 83C602                     \tadd si, 2\r\n0097 BF8000                     \tmov di, 80h\r\n009A 268A0D                     \tmov cl, es:[di]\r\n009D B500                       \tmov ch, 0\r\n009F 47                         \tinc di\r\n00A0 E312                       \tjcxz nocmdl\r\n00A2 2BE1                       \tsub sp, cx\r\n00A4 8BDC                       \tmov bx, sp\r\n00A6 51                         \tpush cx\r\n00A7 57                         \tpush di\r\n00A8                            @@:\r\n00A8 268A05                     \tmov al, es:[di]\r\n00AB 368807                     \tmov ss:[bx], al\r\n00AE 47                         \tinc di\r\n00AF 43                         \tinc bx\r\n00B0 E2F6                       \tloop @B\r\n00B2 5F                         \tpop di\r\n00B3 59                         \tpop cx\r\n00B4                            nocmdl:\r\n00B4 B020                       \tmov al,' '\r\n00B6 AA                         \tstosb\r\n00B7                            @@:\r\n00B7 36AC                       \tlodsb ss:[si]\r\n00B9 AA                         \tstosb\r\n00BA 22C0                       \tand al, al\r\n00BC 75F9                       \tjnz @B\r\n00BE 4F                         \tdec di\r\n00BF E307                       \tjcxz @F\r\n00C1 8BF4                       \tmov si, sp\r\n00C3 F336A4                     \trep movsb es:[di], ss:[si]\r\n00C6 8BE6                       \tmov sp, si\r\n00C8                            @@:\r\n00C8 26C6050D                   \tmov byte ptr es:[di],13\r\n00CC 8BC7                       \tmov ax, di\r\n00CE BF8000                     \tmov di, 80h\r\n00D1 2BC7                       \tsub ax, di\t; might actually be larger than 127 bytes ... ignore for now.\r\n00D3 AA                         \tstosb\r\n\r\n00D4 5E                         \tpop si\r\n\r\n                                ;--- setup SS:SP\r\n\r\n00D5 8CC0                       \tmov ax, es\r\n00D7 03C5                       \tadd ax, bp\r\n00D9 83C010                     \tadd ax, 10h\r\n00DC 8ED0                       \tmov ss, ax\r\n00DE 8BE2                       \tmov sp, dx\r\n\r\n00E0 1E                         \tpush ds\r\n00E1 56                         \tpush si\r\n\r\n00E2 06                         \tpush es\r\n00E3 1F                         \tpop ds\r\n00E4 CB                         \tretf\r\n00E5                            launch endp\r\n\r\n                                if ?BESAFE\r\n00E5                            formerror:\r\n00E5 BA0000                     \tmov dx,offset dFormError\r\n00E8 EB03                       \tjmp error1\r\n                                endif\r\n\r\n00EA                            readerror:\r\n00EA BA0000                     \tmov dx,offset dReadError\r\n00ED                            error1:\r\n                                if 0\r\n                                endif\r\n00ED                            errorX: \t\t\t\t;<--- errors\r\n00ED 0E                         \tpush cs\r\n00EE 1F                         \tpop ds\r\n00EF E80B00                     \tcall DispError\r\n00F2 BA0000                     \tMOV DX,offset dFatal\r\n00F5 E80500                     \tcall DispError\r\n00F8 B8F04C                     \tmov ax,4CF0h\t;error code F0h\r\n00FB CD21                       \tint 21h\r\n00FD                            DispError:\t\t\t\t;display error msg in DX\r\n00FD B409                       \tMOV AH,09h\r\n00FF CD21                       \tINT 21h\r\n0101 C3                         \tret\r\n\r\n0102 202D206C6F6164696E         dFatal\t\tdb ' - loading aborted',cr,lf,'$'\r\n0117 72656164206572726F         dReadError\tdb \"read error\",'$'\r\n                                if ?BESAFE\r\n0122 696E76616C6964204A         dFormError\tdb \"invalid JLoad.exe\",'$'\r\n                                endif\r\n\r\n0134                            endcopy label byte\r\n\r\n0134                            start proc\r\n\r\n                                ;--- setup stack at end of memory block\r\n\r\n0134 FC                         \tcld\r\n0135 A10200                     \tmov ax,ds:[0002]\r\n                                ife ?DOSMEMCHK        \r\n                                endif\r\n0138 83E830                     \tsub ax,30h\r\n013B 8ED0                       \tmov ss,ax\r\n013D BCB002                     \tmov sp,300h-MAXPATH\r\n\r\n                                ;--- setup BP stack frame\r\n\r\n0140 8BEC                       \tmov BP,SP\r\n0142 83EC50                     \tsub SP,MAXPATH\r\n\r\n                                ;--- fill szParm with application name\r\n\r\n0145 8BF4                       \tmov SI,SP    \t\t;SI=szParm\r\n0147 06                         \tpush es\t\t\t\t;save PSP\r\n0148 268E062C00                 \tmov es,es:[002Ch]\t;ES=environment\r\n014D E82300                     \tCALL GetAppName\t\t;get name to SS:SI ( SI modified! )\r\n\r\n0150 0E                         \tpush cs\r\n0151 1F                         \tpop ds\r\n\r\n                                ;--- search \"PATH=\", DI=NULL if it doesn't exist\r\n\r\n0152 E83900                     \tCALL SearchPath\r\n0155 8BF7                       \tMOV SI,DI\t\t\t;set SI to start of PATH= value\r\n\r\n                                ;--- fill szPgm (search JLOAD)\r\n\r\n0157 16                         \tpush SS\r\n0158 1F                         \tpop DS\r\n0159 E84C00                     \tCALL SearchJLoad\r\n015C BA0000                     \tMOV DX,offset dNotFnd\r\n015F 728C                       \tJB errorX  \t\t\t;---> error \"not found\"\r\n\r\n                                ;--- copy the final code to end of memory block\r\n\r\n0161 B90000                     \tmov cx,offset endcopy\r\n0164 16                         \tpush ss\r\n0165 07                         \tpop es\r\n0166 33FF                       \txor di,di\r\n0168 33F6                       \txor si,si\r\n016A 2E                         \tdb 2Eh\t\t\t;CS prefix\r\n016B F3A4                       \trep movsb\r\n\r\n016D 07                         \tpop es\t\t\t;restore PSP\r\n\r\n016E 1E                         \tpush ds\r\n016F 51                         \tpush cx\t\t\t;CX is 0\r\n\r\n0170 0E                         \tpush cs\r\n0171 1F                         \tpop ds\r\n0172 CB                         \tretf\r\n\r\n0173                            start endp\r\n\r\n                                ;--- search name of app in environment\r\n                                ;--- the name may be a full path or not\r\n                                ;--- depending on caller\r\n                                ;--- in: ES=environment, SS:SI=dest\r\n                                ;--- modifies AX,CX,SI,DI\r\n\r\n0173                            GetAppName proc\r\n0173 2BFF                       \tSUB DI,DI\r\n0175 B000                       \tmov al,00\r\n0177 B9FFFF                     \tmov cx,-1\r\n017A                            @@:\r\n017A F2AE                       \trepnz scasb\t\t;search end of environ (00,00)\r\n017C AE                         \tscasb\r\n017D 75FB                       \tjnz @B\r\n017F 47                         \tinc di\t\t\t;skip 0001\r\n0180 47                         \tinc di\r\n\r\n0181                            @@:\r\n0181 268A05                     \tmov al,es:[di]\r\n0184 368804                     \tmov ss:[si],al\r\n0187 46                         \tinc si\r\n0188 47                         \tinc di\r\n0189 22C0                       \tand al,al\r\n018B 75F4                       \tjnz @B\r\n018D C3                         \tRET\r\n018E                            GetAppName endp\r\n\r\n                                ;--- search PATH= in Environment\r\n                                ;--- In: ES=environment\r\n                                ;--- Out: DI-> behind \"PATH=\" or NULL\r\n                                ;--- modifies AX,CX,SI,DI\r\n\r\n018E                            SearchPath proc\r\n018E 2BFF                       \tSUB DI,DI\r\n0190                            nextitem:\r\n0190 BE0000                     \tMOV SI,offset szPath\r\n0193 B90500                     \tMOV CX,SIZPATH\r\n0196 F3A6                       \tREPZ CMPSB\r\n0198 740D                       \tJZ found\r\n019A B000                       \tmov al,00\r\n019C B57F                       \tmov ch,7Fh\r\n019E F2AE                       \trepnz scasb\r\n01A0 263A05                     \tcmp al,es:[di]\r\n01A3 75EB                       \tJNZ nextitem\r\n01A5 2BFF                       \tsub di,di\r\n01A7                            found:\r\n01A7 C3                         \tRET\r\n01A8                            SearchPath endp\r\n\r\n                                ;--- search JLOAD, first in current Dir, then scan PATH\r\n                                ;--- Input: ES=environ, SI=address PATH variable or 0000 (no PATH exists)\r\n                                ;--- Output: NC if found, C if error\r\n                                ;--- full loader path in szPgm\r\n                                ;--- modifies AX,BX,CX,DX,SI,DI\r\n\r\n01A8                            SearchJLoad proc\r\n01A8 8D7E00                     \tlea DI,[bp+00h]\r\n01AB 8BD7                       \tmov dx,di\r\n01AD                            nextentry:\t\t\t\t\t;<----\r\n01AD 56                         \tPUSH SI\r\n01AE BE0000                     \tmov si,offset ldrname\t;Name \"JLOAD.EXE\"\r\n01B1 B90900                     \tmov cx,SIZLDRNAME\r\n01B4                            @@:\r\n01B4 2E                         \tdb 2Eh\t\t;CS prefix\r\n01B5 AC                         \tlodsb\r\n01B6 8805                       \tmov [di],al\r\n01B8 47                         \tinc di\r\n01B9 E2F9                       \tloop @B\r\n01BB 880D                       \tmov [di],cl\r\n\r\n01BD B8003D                     \tMOV AX,3D00h\t\t\t;open JLOAD.EXE\r\n01C0 CD21                       \tINT 21h\r\n01C2 5E                         \tPOP SI\r\n01C3 7328                       \tJNB ldrfound\t\t\t;jmp if found!\r\n01C5 23F6                       \tAND SI,SI\r\n01C7 F9                         \tstc\r\n01C8 7424                       \tJZ notfound\t\t\t\t;PATH not defined, so we are done\r\n01CA 8BFA                       \tMOV DI,DX\r\n01CC B94400                     \tmov cx,MAXDIR\r\n01CF                            @@:\r\n01CF 268A04                     \tmov al,es:[si]\r\n01D2 8805                       \tmov [di],al\r\n01D4 46                         \tinc si\r\n01D5 47                         \tinc di\r\n01D6 3C3B                       \tCMP AL,';'\r\n01D8 7406                       \tJZ @F\r\n01DA 3C00                       \tCMP AL,00\r\n01DC E0F1                       \tLOOPNZ @B\t\t\t\t;PATH done\r\n01DE 33F6                       \tXOR SI,SI\r\n01E0                            @@:\r\n01E0 4F                         \tDEC DI\r\n01E1 807DFF5C                   \tCMP Byte Ptr [DI-01],'\\'\r\n01E5 74C6                       \tJZ nextentry\r\n01E7 C6055C                     \tMOV Byte Ptr [DI],'\\'\r\n01EA 47                         \tINC DI\r\n01EB EBC0                       \tJMP nextentry\r\n01ED                            ldrfound:\r\n01ED 93                         \tXCHG ax,bx\t\t\t\t;=MOV BX,AX\r\n                                ;\tMOV AH,3Eh\t\t\t\t;close file\r\n                                ;\tINT 21h\r\n                                ;\tCLC\r\n01EE                            notfound:\r\n01EE C3                         \tRET\r\n01EF                            SearchJLoad endp\r\n\r\n01EF 504154483D                 szPath  db   'PATH='\r\n = 5                            SIZPATH equ $ - szPath\r\n\r\n01F4 43616E27742066696E         dNotFnd db \"Can't find \"\r\n01FF                            ldrname\tlabel byte\r\n01FF 4A4C4F41442E455845         \tdb 'JLOAD.EXE'\r\n = 9                            SIZLDRNAME equ $ - ldrname\r\n0208 24                         \tdb '$'\r\n\r\n                                ife ?DOSMEMCHK\r\n                                endif\r\n\r\n0209                            _TEXT ends\r\n\r\n                                if ?DOSMEMCHK\r\n0000                            _BSS segment public 'DATA'\r\n0000 000000000000000000         \tdb (?MINMEM+14h) * 16 dup (?)\r\n6140                            _BSS ends\r\n                                endif\r\n\r\n0000                            STACK segment stack 'STACK'\r\n0000                            STACK ends\r\n\r\n                                \tend start\r\n\r\n\r\nBinary Map:\r\n\r\nSegment                  Pos(file)     RVA  Size(fil) Size(mem)\r\n---------------------------------------------------------------\r\n<header>                        0        0        40         0\r\n_TEXT                          40        0       209       209\r\n_BSS                            0      210         0      6140\r\nSTACK                           0     6350         0         0\r\n---------------------------------------------------------------\r\n                                                 249      6350\r\n\r\n\r\nMacros:\r\n\r\n                N a m e                 Type\r\n\r\n@CatStr  . . . . . . . . . . . .        Func\r\n@Environ . . . . . . . . . . . .        Func\r\n@InStr . . . . . . . . . . . . .        Func\r\n@SizeStr . . . . . . . . . . . .        Func\r\n@SubStr  . . . . . . . . . . . .        Func\r\n\r\n\r\nStructures and Unions:\r\n\r\n                N a m e                 Size/Ofs   Type\r\n\r\nmzhdr  . . . . . . . . . . . . .              1A\r\n  e_magic  . . . . . . . . . . .               0   Word\r\n  e_cblp . . . . . . . . . . . .               2   Word\r\n  e_cp . . . . . . . . . . . . .               4   Word\r\n  e_crlc . . . . . . . . . . . .               6   Word\r\n  e_cparhdr  . . . . . . . . . .               8   Word\r\n  e_minalloc . . . . . . . . . .               A   Word\r\n  e_maxalloc . . . . . . . . . .               C   Word\r\n  e_ss . . . . . . . . . . . . .               E   Word\r\n  e_sp . . . . . . . . . . . . .              10   Word\r\n  e_csum . . . . . . . . . . . .              12   Word\r\n  e_ip . . . . . . . . . . . . .              14   Word\r\n  e_cs . . . . . . . . . . . . .              16   Word\r\n  e_lfarlc . . . . . . . . . . .              18   Word\r\n\r\n\r\nSegments and Groups:\r\n\r\n                N a m e                 Size     Length   Align   Combine Class\r\n\r\nSTACK  . . . . . . . . . . . . .        16 Bit   0000     Para    Stack   'STACK'\r\n_BSS . . . . . . . . . . . . . .        16 Bit   6140     Para    Public  'DATA'\r\n_TEXT  . . . . . . . . . . . . .        16 Bit   0209     Para    Public  'CODE'\r\n\r\n\r\nProcedures, parameters and locals:\r\n\r\n                N a m e                 Type     Value    Segment  Length\r\n\r\nGetAppName . . . . . . . . . . .        P Near   0173     _TEXT    001B Public   \r\n  L&_0006  . . . . . . . . . . .        L Near   0181     _TEXT\r\n  L&_0005  . . . . . . . . . . .        L Near   017A     _TEXT\r\nSearchJLoad  . . . . . . . . . .        P Near   01A8     _TEXT    0047 Public   \r\n  L&_0008  . . . . . . . . . . .        L Near   01CF     _TEXT\r\n  notfound . . . . . . . . . . .        L Near   01EE     _TEXT\r\n  L&_0009  . . . . . . . . . . .        L Near   01E0     _TEXT\r\n  L&_0007  . . . . . . . . . . .        L Near   01B4     _TEXT\r\n  nextentry  . . . . . . . . . .        L Near   01AD     _TEXT\r\n  ldrfound . . . . . . . . . . .        L Near   01ED     _TEXT\r\nSearchPath . . . . . . . . . . .        P Near   018E     _TEXT    001A Public   \r\n  found  . . . . . . . . . . . .        L Near   01A7     _TEXT\r\n  nextitem . . . . . . . . . . .        L Near   0190     _TEXT\r\nlaunch . . . . . . . . . . . . .        P Near   0000     _TEXT    00E5 Public   \r\n  L&_0004  . . . . . . . . . . .        L Near   00C8     _TEXT\r\n  L&_0002  . . . . . . . . . . .        L Near   00A8     _TEXT\r\n  L&_0003  . . . . . . . . . . .        L Near   00B7     _TEXT\r\n  L&_0001  . . . . . . . . . . .        L Near   007A     _TEXT\r\n  nocmdl . . . . . . . . . . . .        L Near   00B4     _TEXT\r\n  norelocs . . . . . . . . . . .        L Near   0042     _TEXT\r\n  norelocs2  . . . . . . . . . .        L Near   008D     _TEXT\r\nstart  . . . . . . . . . . . . .        P Near   0134     _TEXT    003F Public   \r\n\r\n\r\nSymbols:\r\n\r\n                N a m e                 Type       Value     Attr\r\n\r\n?BESAFE  . . . . . . . . . . . .        Number             1h \r\n?DOSMEMCHK . . . . . . . . . . .        Number             1h \r\n?MINMEM  . . . . . . . . . . . .        Number           600h \r\nDispError  . . . . . . . . . . .        L Near            FDh _TEXT \r\nMAXDIR . . . . . . . . . . . . .        Number            44h \r\nMAXPATH  . . . . . . . . . . . .        Number            50h \r\nSIZLDRNAME . . . . . . . . . . .        Number             9h \r\nSIZPATH  . . . . . . . . . . . .        Number             5h \r\ncr . . . . . . . . . . . . . . .        Number             Dh \r\ndFatal . . . . . . . . . . . . .        Byte[21]         102h _TEXT \r\ndFormError . . . . . . . . . . .        Byte[18]         122h _TEXT \r\ndNotFnd  . . . . . . . . . . . .        Byte[11]         1F4h _TEXT \r\ndReadError . . . . . . . . . . .        Byte[11]         117h _TEXT \r\nendcopy  . . . . . . . . . . . .        Byte             134h _TEXT \r\nerror1 . . . . . . . . . . . . .        L Near            EDh _TEXT \r\nerrorX . . . . . . . . . . . . .        L Near            EDh _TEXT \r\nformerror  . . . . . . . . . . .        L Near            E5h _TEXT \r\nldrname  . . . . . . . . . . . .        Byte             1FFh _TEXT \r\nlf . . . . . . . . . . . . . . .        Number             Ah \r\nreaderror  . . . . . . . . . . .        L Near            EAh _TEXT \r\nszParm . . . . . . . . . . . . .        Text   [bp-MAXPATH]\r\nszPath . . . . . . . . . . . . .        Byte[5]          1EFh _TEXT \r\nszPgm  . . . . . . . . . . . . .        Text   [bp+00h]\r\n\r\nJLSTUB.ASM: 418 lines, 2 passes, 2 ms, 0 warnings, 0 errors\r\n"
  },
  {
    "path": "JLM/JLSTUB/JLSTUB.ASM",
    "content": "\r\n;--- MZ stub to run jload.exe\r\n;--- to be assembled with Masm or JWasm\r\n;--- it's a derivate of dpmildxx... \r\n\r\n?MINMEM\t = 600h\t\t;min free paragraphs for JLOAD.EXE\r\nMAXDIR   = 64+4\t\t;max length of a directory path (including 00h)\r\nMAXPATH  = MAXDIR+12\r\n?BESAFE  = 1\t\t;1=check if JLoad.exe looks ok.\r\n?DOSMEMCHK = 1\t\t;0=check for sufficient DOS memory\r\n\r\n\t.286\r\n\r\nifdef __JWASM__\r\n\toption MZ:40h\r\nendif\r\n\r\ncr\t\tequ 13\r\nlf\t\tequ 10\r\n\r\nmzhdr struct\r\n  e_magic           WORD      ?\t\t;+0\r\n  e_cblp            WORD      ?\t\t;+2\r\n  e_cp              WORD      ?\t\t;+4\r\n  e_crlc            WORD      ?\t\t;+6\t\tnumber of relocation records\r\n  e_cparhdr         WORD      ?\t\t;+8\r\n  e_minalloc        WORD      ?\t\t;+10\r\n  e_maxalloc        WORD      ?\t\t;+12\r\n  e_ss              WORD      ?\t\t;+14\r\n  e_sp              WORD      ?\t\t;+16\r\n  e_csum            WORD      ?\t\t;+18\r\n  e_ip              WORD      ?\t\t;+20\r\n  e_cs              WORD      ?\t\t;+22\r\n  e_lfarlc          WORD      ?\t\t;+24\tbegin relocation records\r\nmzhdr ends\r\n\r\n\r\n_TEXT   segment public 'CODE'\r\n\r\nszPgm   equ [bp+00h]     ;execute program name (\"JLOAD.EXE\")\r\nszParm  equ [bp-MAXPATH] ;application name (from environment)\r\n\r\nlaunch proc\r\n\r\n\txor DX,DX\r\n\tmov cl,20h\r\n\tmov ah,3Fh\t\t;read the MZ header\r\n\tint 21h\r\n\tjc readerror\r\n\r\nif ?BESAFE\r\n\r\n;--- additional tests\r\n\r\n\txor si,si\r\n\tmov ax, [si].mzhdr.e_magic\r\n\tcmp ax,\"ZM\"\r\n\tjnz formerror\r\n\tmov ax, [si].mzhdr.e_crlc\t; no of relocation entries\r\n\tmov di,ax\r\n\tand ax,ax\r\n\tjz norelocs\r\n\tpush ax\r\n\txor cx,cx\r\n\tmov dx,[si].mzhdr.e_lfarlc\t; begin relocations\r\n\tmov ax,4200h\r\n\tint 21h\r\n\tpop cx\r\n\tshl cx,2\t\t\t\t\t; 4 byte each reloc\r\n\tsub sp,cx\r\n\tmov dx,sp\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tmov ah,3Fh\t\t\t\t\t; read relocs at SS:SP\r\n\tint 21h\r\n\tpop ds\r\n\tjc formerror\r\nnorelocs:\r\n\tmov ax,[si].mzhdr.e_cparhdr\t; size of header in paragraphs\r\n\tpush [si].mzhdr.e_ss\r\n\tpush [si].mzhdr.e_sp\r\n\tmov si, [si].mzhdr.e_ip\r\n\tshl ax,4\r\n\tmov dx,ax\r\n\txor cx,cx\r\n\tmov ax,4200h\r\n\tint 21h\r\n\txor dx,dx\r\nendif\r\n\r\n;--- read JLoad.exe binary\r\n\r\n\tMOV AH,3Fh\r\n\tmov cx,?MINMEM shl 4\r\n\tINT 21h\r\n\tJC readerror\t;---> error \"read error\"\r\nif ?BESAFE        \r\n\tcmp ax,cx\t\t;JLOAD binary must be < 24 kB\r\n\tjnc formerror\r\nendif\r\n\tmov ah,3Eh\r\n\tint 21h\r\n\r\n\tpop dx\r\n\tpop bp\r\n\r\nif ?BESAFE\r\n\tmov cx,di\t\t;some relocs to resolve?\r\n\tjcxz norelocs2\r\n\tmov di,sp\r\n\tmov ax,ds\r\n@@:\r\n\tmov bx,ss:[di+2]\r\n\tshl bx,4\t\t;size of loader is <= 24 kB, so no overflow possible\r\n\tadd bx,ss:[di+0]\r\n\tadd [bx],ax\r\n\tadd di,4\r\n\tloop @B\r\n\tmov sp,di\r\nnorelocs2:\r\nendif\r\n\r\n;--- fill JLoad's PSP - don't overwrite possible cmdline arguments\r\n\r\n\tmov ah,51h\r\n\tint 21h\r\n\tpush si\r\n\tmov si, sp\r\n\tadd si, 2\r\n\tmov di, 80h\r\n\tmov cl, es:[di]\r\n\tmov ch, 0\r\n\tinc di\r\n\tjcxz nocmdl\r\n\tsub sp, cx\r\n\tmov bx, sp\r\n\tpush cx\r\n\tpush di\r\n@@:\r\n\tmov al, es:[di]\r\n\tmov ss:[bx], al\r\n\tinc di\r\n\tinc bx\r\n\tloop @B\r\n\tpop di\r\n\tpop cx\r\nnocmdl:\r\n\tmov al,' '\r\n\tstosb\r\n@@:\r\n\tlodsb ss:[si]\r\n\tstosb\r\n\tand al, al\r\n\tjnz @B\r\n\tdec di\r\n\tjcxz @F\r\n\tmov si, sp\r\n\trep movsb es:[di], ss:[si]\r\n\tmov sp, si\r\n@@:\r\n\tmov byte ptr es:[di],13\r\n\tmov ax, di\r\n\tmov di, 80h\r\n\tsub ax, di\t; might actually be larger than 127 bytes ... ignore for now.\r\n\tstosb\r\n\r\n\tpop si\r\n\r\n;--- setup SS:SP\r\n\r\n\tmov ax, es\r\n\tadd ax, bp\r\n\tadd ax, 10h\r\n\tmov ss, ax\r\n\tmov sp, dx\r\n\r\n\tpush ds\r\n\tpush si\r\n\r\n\tpush es\r\n\tpop ds\r\n\tretf\r\nlaunch endp\r\n\r\nif ?BESAFE\r\nformerror:\r\n\tmov dx,offset dFormError\r\n\tjmp error1\r\nendif\r\n\r\nreaderror:\r\n\tmov dx,offset dReadError\r\nerror1:\r\nif 0\r\n;--- close file? Will be done by DOS.\r\n\tmov ah,3Eh\r\n\tint 21h\r\nendif\r\nerrorX: \t\t\t\t;<--- errors\r\n\tpush cs\r\n\tpop ds\r\n\tcall DispError\r\n\tMOV DX,offset dFatal\r\n\tcall DispError\r\n\tmov ax,4CF0h\t;error code F0h\r\n\tint 21h\r\nDispError:\t\t\t\t;display error msg in DX\r\n\tMOV AH,09h\r\n\tINT 21h\r\n\tret\r\n\r\ndFatal\t\tdb ' - loading aborted',cr,lf,'$'\r\ndReadError\tdb \"read error\",'$'\r\nif ?BESAFE\r\ndFormError\tdb \"invalid JLoad.exe\",'$'\r\nendif\r\n\r\nendcopy label byte\r\n\r\nstart proc\r\n\r\n;--- setup stack at end of memory block\r\n\r\n\tcld\r\n\tmov ax,ds:[0002]\r\nife ?DOSMEMCHK        \r\n\tmov bp,es\r\n\tpush ax\r\n\tsub ax,bp\r\n\tcmp ax,?MINMEM+30h\t;enough free memory in this block?\r\n\tpop ax\r\n\tmov dx,offset dMemory\r\n\tjc errorX\r\nendif\r\n\tsub ax,30h\r\n\tmov ss,ax\r\n\tmov sp,300h-MAXPATH\r\n\r\n;--- setup BP stack frame\r\n\r\n\tmov BP,SP\r\n\tsub SP,MAXPATH\r\n\r\n;--- fill szParm with application name\r\n\r\n\tmov SI,SP    \t\t;SI=szParm\r\n\tpush es\t\t\t\t;save PSP\r\n\tmov es,es:[002Ch]\t;ES=environment\r\n\tCALL GetAppName\t\t;get name to SS:SI ( SI modified! )\r\n\r\n\tpush cs\r\n\tpop ds\r\n\r\n;--- search \"PATH=\", DI=NULL if it doesn't exist\r\n\r\n\tCALL SearchPath\r\n\tMOV SI,DI\t\t\t;set SI to start of PATH= value\r\n\r\n;--- fill szPgm (search JLOAD)\r\n\r\n\tpush SS\r\n\tpop DS\r\n\tCALL SearchJLoad\r\n\tMOV DX,offset dNotFnd\r\n\tJB errorX  \t\t\t;---> error \"not found\"\r\n\r\n;--- copy the final code to end of memory block\r\n\r\n\tmov cx,offset endcopy\r\n\tpush ss\r\n\tpop es\r\n\txor di,di\r\n\txor si,si\r\n\tdb 2Eh\t\t\t;CS prefix\r\n\trep movsb\r\n\r\n\tpop es\t\t\t;restore PSP\r\n\r\n\tpush ds\r\n\tpush cx\t\t\t;CX is 0\r\n\r\n\tpush cs\r\n\tpop ds\r\n\tretf\r\n\r\nstart endp\r\n\r\n;--- search name of app in environment\r\n;--- the name may be a full path or not\r\n;--- depending on caller\r\n;--- in: ES=environment, SS:SI=dest\r\n;--- modifies AX,CX,SI,DI\r\n\r\nGetAppName proc\r\n\tSUB DI,DI\r\n\tmov al,00\r\n\tmov cx,-1\r\n@@:\r\n\trepnz scasb\t\t;search end of environ (00,00)\r\n\tscasb\r\n\tjnz @B\r\n\tinc di\t\t\t;skip 0001\r\n\tinc di\r\n\r\n@@:\r\n\tmov al,es:[di]\r\n\tmov ss:[si],al\r\n\tinc si\r\n\tinc di\r\n\tand al,al\r\n\tjnz @B\r\n\tRET\r\nGetAppName endp\r\n\r\n;--- search PATH= in Environment\r\n;--- In: ES=environment\r\n;--- Out: DI-> behind \"PATH=\" or NULL\r\n;--- modifies AX,CX,SI,DI\r\n\r\nSearchPath proc\r\n\tSUB DI,DI\r\nnextitem:\r\n\tMOV SI,offset szPath\r\n\tMOV CX,SIZPATH\r\n\tREPZ CMPSB\r\n\tJZ found\r\n\tmov al,00\r\n\tmov ch,7Fh\r\n\trepnz scasb\r\n\tcmp al,es:[di]\r\n\tJNZ nextitem\r\n\tsub di,di\r\nfound:\r\n\tRET\r\nSearchPath endp\r\n\r\n;--- search JLOAD, first in current Dir, then scan PATH\r\n;--- Input: ES=environ, SI=address PATH variable or 0000 (no PATH exists)\r\n;--- Output: NC if found, C if error\r\n;--- full loader path in szPgm\r\n;--- modifies AX,BX,CX,DX,SI,DI\r\n\r\nSearchJLoad proc\r\n\tlea DI,szPgm\r\n\tmov dx,di\r\nnextentry:\t\t\t\t\t;<----\r\n\tPUSH SI\r\n\tmov si,offset ldrname\t;Name \"JLOAD.EXE\"\r\n\tmov cx,SIZLDRNAME\r\n@@:\r\n\tdb 2Eh\t\t;CS prefix\r\n\tlodsb\r\n\tmov [di],al\r\n\tinc di\r\n\tloop @B\r\n\tmov [di],cl\r\n\r\n\tMOV AX,3D00h\t\t\t;open JLOAD.EXE\r\n\tINT 21h\r\n\tPOP SI\r\n\tJNB ldrfound\t\t\t;jmp if found!\r\n\tAND SI,SI\r\n\tstc\r\n\tJZ notfound\t\t\t\t;PATH not defined, so we are done\r\n\tMOV DI,DX\r\n\tmov cx,MAXDIR\r\n@@:\r\n\tmov al,es:[si]\r\n\tmov [di],al\r\n\tinc si\r\n\tinc di\r\n\tCMP AL,';'\r\n\tJZ @F\r\n\tCMP AL,00\r\n\tLOOPNZ @B\t\t\t\t;PATH done\r\n\tXOR SI,SI\r\n@@:\r\n\tDEC DI\r\n\tCMP Byte Ptr [DI-01],'\\'\r\n\tJZ nextentry\r\n\tMOV Byte Ptr [DI],'\\'\r\n\tINC DI\r\n\tJMP nextentry\r\nldrfound:\r\n\tXCHG ax,bx\t\t\t\t;=MOV BX,AX\r\n;\tMOV AH,3Eh\t\t\t\t;close file\r\n;\tINT 21h\r\n;\tCLC\r\nnotfound:\r\n\tRET\r\nSearchJLoad endp\r\n\r\nszPath  db   'PATH='\r\nSIZPATH equ $ - szPath\r\n\r\ndNotFnd db \"Can't find \"\r\nldrname\tlabel byte\r\n\tdb 'JLOAD.EXE'\r\nSIZLDRNAME equ $ - ldrname\r\n\tdb '$'\r\n\r\nife ?DOSMEMCHK\r\ndMemory\tdb \"Insufficient memory\",'$'\r\nendif\r\n\r\n_TEXT ends\r\n\r\nif ?DOSMEMCHK\r\n_BSS segment public 'DATA'\r\n\tdb (?MINMEM+14h) * 16 dup (?)\r\n_BSS ends\r\nendif\r\n\r\nSTACK segment stack 'STACK'\r\nSTACK ends\r\n\r\n\tend start\r\n"
  },
  {
    "path": "JLM/JLSTUB/JLStub.txt",
    "content": "\r\n 1. About JLStub\r\n\r\n  JLStub is NOT a JLM. It's a stub, supposed to be added to other JLMs.\r\n It's purpose is to avoid having to type \"jload\" before the JLM. Instead,\r\n the stub is added to the JLM. When the JLM is launched by DOS, the stub\r\n will run, searching and launching jload.exe.\r\n\r\n"
  },
  {
    "path": "JLM/JLSTUB/Make.BAT",
    "content": "@echo off\r\nif not exist Build\\NUL mkdir Build\r\njwasm -nologo -mz -FlBuild\\ -Fo=Build\\jlstub.bin jlstub.asm\r\n"
  },
  {
    "path": "JLM/KEYBGR/KEYBGR.ASM",
    "content": "\r\n;*** german keyboard driver for MF keyboards\r\n;--- the first JLM (Jemm Loadable Module) ever.\r\n\r\n;--- v1.0: initial\r\n;--- v1.1: route nontranslated key presses to previous handler\r\n;--- v1.2: now fully compatible with the old KEYBGR.EXE (needs 30h DOS mem)\r\n;--- v1.3: source now compatible with JWasm\r\n\r\n        .386\r\n        .model flat\r\n        option casemap:none\r\n\r\n        include jlm.inc\r\n\r\ncr equ 13\r\nlf equ 10\r\n\r\nkbdstat1 equ 417h;  byte\r\nkbdstat2 equ 418h;  byte\r\nbufsta   equ 41ah;  word\r\nbufend   equ 41ch;  word\r\nebufsta  equ 480h;  word\r\nebufend  equ 482h;  word\r\nkbdflgs  equ 496h;  byte\r\nLEDflgs  equ 497h;  byte\r\n\r\nDLL_PROCESS_ATTACH  equ 1\r\nDLL_PROCESS_DETACH  equ 0\r\n\r\nifdef FMTPE\r\n\toption dotname\r\n.drectve segment info\r\n\tdb \"-subsystem:native -dll -fixed:no\"\r\n.drectve ends\r\nendif\r\n\r\n        .code\r\n\r\n;--- AltGr keys\r\n\r\naltgrkeytab label byte\r\n       db 03h           ;2 -> \r\n       db 04h           ;3 -> \r\n       db 08h           ;7 -> {\r\n       db 09h           ;8 -> [\r\n       db 0Ah           ;9 -> ]\r\n       db 0Bh           ;0 -> }\r\n       db 0Ch           ; -> \\\r\n       db 10h           ;Q -> @\r\n       db 1Bh           ;+ -> ~\r\n       db 32h           ;M -> \r\n       db 56h           ;< -> |\r\nALTGRTABSIZ equ $ - altgrkeytab\r\n       db ''\r\n       db ''\r\n       db '{'\r\n       db '['\r\n       db ']'\r\n       db '}'\r\n       db '\\'\r\n       db '@'\r\n       db '~'\r\n       db ''\r\n       db '|'\r\n\r\n;--- numlock keys\r\n\r\nnumpadkeytab label byte\r\n       db 53h       ;',' \r\nNUMPADTABSIZ equ $ - numpadkeytab\r\n       public PLAB1\r\nPLAB1  db ','\r\n\r\n;--- ctrl keys\r\n\r\nctrlkeytab label byte\r\n       db 15h       ;ctrl z\r\n       db 2ch       ;ctrl y\r\nCTRLTABSIZ equ $ - ctrlkeytab\r\n       db 1Ah\r\n       db 19h\r\n\r\n;--- standard keys\r\n\r\nstdkeytab label byte\r\n       db 15h            ;z\r\n       db 1ah            ;\r\n       db 27h            ;\r\n       db 28h            ;\r\n       db 2ch            ;y\r\n       db 03h            ;2\r\n       db 04h            ;3\r\n       db 07h            ;6\r\n       db 08h            ;7\r\n       db 09h            ;8\r\n       db 0ah            ;9\r\n       db 0bh            ;0\r\n       db 0ch            ;sz\r\n       db 0dh            ;apost\r\n       db 1bh            ;+\r\n       db 29h            ;^\r\n       db 2Bh            ;#\r\n       db 33h            ;,\r\n       db 34h            ;.\r\n       db 35h            ;-\r\n       db 56h            ;<\r\nSTDTABSIZ equ $ - stdkeytab\r\n\r\n       db 'z'\r\n       db ''\r\n       db ''\r\n       db ''\r\n       db 'y'\r\nL02B3X label byte                 ;CAPS-LOCK insensitive keys\r\n       db 0ffh\r\n       db 0ffh\r\n       db 0ffh\r\n       db 0ffh\r\n       db 0ffh\r\n       db 0ffh\r\n       db 0ffh\r\n       db ''\r\n       db \"'\"\r\n       db '+'\r\n       db '^'\r\n       db '#'\r\n       db 0ffh           ;,\r\n       db 0ffh           ;.\r\n       db '-'\r\n       db '<'\r\n\r\n       db 'Z'\r\n       db ''\r\n       db ''\r\n       db ''\r\n       db 'Y'\r\n       db '\"'\r\n       db '\u0015'\r\n       db '&'\r\n       db '/'\r\n       db '('\r\n       db ')'\r\n       db '='\r\n       db '?'\r\n       db '`'\r\n       db '*'\r\n       db ''\r\n       db \"'\"\r\n       db ';'\r\n       db ':'\r\n       db '_'\r\n       db '>'\r\n\r\n        align 4\r\n\r\nintrou15 proc\r\n\r\n;--- this is the entry from v86-mode when a key has been pressed\r\n;--- (int 15h, ah=4Fh)\r\n\r\n        @VMMCall Simulate_Iret   ;emulate an IRET in v86\r\n\r\n        mov     eax,[ebp].Client_Reg_Struc.Client_EAX\r\n        cmp     al,0E0h\r\n        jnc     done\r\n        call    trans\r\ndone:\r\n        ret\r\nintrou15 endp\r\n\r\ntrans   proc near\r\n        mov     dx,word ptr ds:[kbdstat1]\r\n        CLD\r\n        mov     bl,al\r\n        mov     bh,ds:[kbdflgs]\r\n        and     al,7Fh\r\n        MOV     ECX,ALTGRTABSIZ         ;ch=0!\r\n        test    bh,08h                  ;right alt (altgr) pressed?\r\n        jz      @F\r\n        test    dx,0204h                ;any ctrl or alt-left pressed?\r\n        jnz     @F\r\n        MOV     EDI,offset altgrkeytab\r\n        jmp     scantabX\r\n@@:\r\n        test    dl,08h                  ;any alt pressed?\r\n        jnz     exit\r\n        MOV     EDI,offset ctrlkeytab\r\n        mov     CL,CTRLTABSIZ\r\n        test    dl,04h                  ;any ctrl pressed?\r\n        jnz     scantabX\r\n        MOV     EDI,offset stdkeytab\r\n        MOV     cl,STDTABSIZ\r\n        cmp     al,53h                  ;numpad ','?\r\n        jnz     scantab\r\n        test    bh,2                    ;extended key (E0)?\r\n        jnz     scantab\r\n        test    dl,20h                  ;num lock active?\r\n        jz      exit\r\n        mov     cl,NUMPADTABSIZ\r\n        mov     edi,offset numpadkeytab\r\nscantabX:\r\n        mov     dl,0                    ;ignore shift state\r\nscantab:\r\n        push    ecx\r\n        repnz   scasb\r\n        pop     ecx\r\n        jnz     exit\r\n        dec     edi\r\n        add     edi,ecx\r\n        cmp     edi,offset L02B3X    ;caps lock sensitiv?\r\n        jnb     @F\r\n        test    dl,40h              ;shift lock?\r\n        jz      @F\r\n        test    dl,3                ;shift pressed?\r\n        jnz     unorm1\r\n        jmp     unorm2\r\n@@:\r\n        test    dl,3                ;shift pressed?\r\n        jz      unorm1\r\nunorm2:\r\n        add     edi,ecx\r\nunorm1:\r\n        mov     ah,al               ;save scan code\r\n        mov     al,[edi]\r\n        cmp     al,0FFh\r\n        jz      exit\r\nfound:\r\n        test    bl,80h              ;is key released?\r\n        jnz     dontsave\r\n        call    savekey\r\ndontsave:\r\n        and     byte ptr [ebp].Client_Reg_Struc.Client_EFlags,not 1 ;clear carry\r\n        ret\r\nexit:\r\n        stc\r\n        ret\r\nsavekey:\r\n        MOVzx   EDI,word ptr ds:[bufend]\r\n        MOV     eSI,eDI\r\n        INC     eDI\r\n        INC     eDI\r\n        CMP     DI,ds:[ebufend]\r\n        JNZ     @F\r\n        MOV     DI,ds:[ebufsta]\r\n@@:\r\n        CMP     DI,ds:[bufsta]\r\n        JZ      @F                  ;no more room in buffer\r\n        MOV     [ESI+400h],AX\r\n        MOV     ds:[bufend],DI\r\n@@:\r\n        retn\r\n\r\ntrans   endp\r\n\r\n;--- install the JLM:\r\n;--- + first try to alloc a v86 callback\r\n;--- + save this callback and the previous v86 int 15h vector\r\n;---   in the 16-bit \"real-mode\" code.\r\n;--- + set driver attributes in JLoad's header\r\n;--- + copy the \"real-mode\" code to JLoad's begin in DOS memory\r\n;---   (this is ensured to be safe as long as the code size doesn't\r\n;---   exceed 1 kB).\r\n;--- + set the size of the resident driver part in the driver's\r\n;---   request header.\r\n\r\ninstall proc uses esi edi\r\n\r\n        test [ecx].JLCOMM.wFlags, JLF_DRIVER  ;loaded as device driver?\r\n        jz failed\r\n\r\n        push ecx\r\n        mov esi, offset introu15\r\n        mov edx, 0\r\n        @VMMCall Allocate_V86_Call_Back      ;get a v86 callback\r\n        pop esi\r\n        jc failed\r\n        mov edx, offset rmcode\r\n        mov [edx+newvec-@], eax             ;patch the 16-bit code\r\n        mov eax, ds:[15h*4]\r\n        mov [edx+oldvec-@], eax\r\n\r\n        movzx edi,[esi].JLCOMM.wLdrCS       ;set driver attributes\r\n        shl edi, 4\r\n        mov  word ptr [edi+6],18            ;offset strategy (dummy)\r\n        mov  word ptr [edi+8],18            ;offset interrupt (dummy)\r\n        mov dword ptr [edi+10],\"BYEK\"       ;driver name\r\n        mov dword ptr [edi+14],\"$$RG\"\r\n        mov  byte ptr [edi+18],0CBh         ;RETF\r\n        add edi,20\r\n\r\n        push esi\r\n        push edi\r\n        mov esi, offset rmcode              ;copy 16-bit code to DOS memory\r\n        mov ecx, sizermcode\r\n        rep movsb\r\n        pop edi\r\n        pop esi\r\n\r\n        mov ax,[esi].JLCOMM.wLdrCS          ;set the new int 15h vector\r\n        shl eax, 16\r\n        mov ax,20 \r\n        mov ds:[15h*4],eax\r\n\r\n        add ax,sizermcode\r\n        mov edx,[esi].JLCOMM.lpRequest\r\n        mov [edx+14],ax                     ;set resident size\r\n\r\n        mov eax,1\r\n        ret\r\nfailed:\r\n        xor eax,eax\r\n        ret\r\n\r\ninstall endp\r\n\r\n;--- deinstall. Since the module will only load\r\n;--- as a device driver in CONFIG.SYS, it cannot be unloaded\r\n\r\ndeinstall   proc\r\n\r\n        xor eax, eax    ;refuse to unload\r\n        ret\r\n\r\ndeinstall   endp\r\n\r\nDllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n        mov ecx, dwRes\r\n        mov eax,dwReason\r\n        cmp eax,DLL_PROCESS_ATTACH\r\n        jnz @F\r\n        call install\r\n        jmp exit\r\n@@:        \r\n        cmp eax,DLL_PROCESS_DETACH\r\n        jnz @F\r\n        call deinstall\r\n@@:\r\nexit:\r\n        ret\r\n\r\nDllMain endp\r\n\r\n;--- DOS 16-bit \"real-mode\" code.\r\n;--- The code will be patched and then copied to DOS memory;\r\n;--- it will be the new v86 int 15h handler.\r\n\r\n;--- It's assumed that 32-bit and 16-bin code are mixed in section .text;\r\n;--- then 32-bit label rmcode below can be used to address 16-bit segment _TEXT$16.\r\n;--- This works with jwlink or jwasm v2.19+, option -pe.\r\n\r\n    align dword\r\nrmcode label near\r\n\r\n;--- for jwasm, option -pe, it's necessary for mixing that the segment name starts with _TEXT$!\r\n\r\n_TEXT$16 segment use16 dword public 'CODE'\r\n@:\r\n        cmp ah, 4Fh\r\n        jz @F\r\n        db 0EAh     ; jmp SSSS:OOOO\r\noldvec  dd 0\t\t; previous handler\r\n@@:\r\n        db 0EAh     ; jmp SSSS:0000\r\nnewvec  dd 0\t\t; v86-breakpoint\r\nsizermcode equ $ - @\r\n_TEXT$16 ends\r\n\r\n        end DllMain\r\n"
  },
  {
    "path": "JLM/KEYBGR/MAKE.BAT",
    "content": "@echo off\r\nrem\r\nrem JWasm v2.19: cmdline option -pe will create a JLM without external linker\r\nrem\r\njwasm -nologo -pe -DFMTPE -Fl=Release\\ -Fo=Release\\KEYBGR.DLL -I..\\..\\Include KeybGr.asm\r\npatchPE -x -s:0x1000 Release\\KEYBGR.DLL\r\n"
  },
  {
    "path": "JLM/KEYBGR/MAKEFILE",
    "content": "\r\n# Makefile for NMAKE\r\n#  tools                      Alternatives\r\n#----------------------------------------------------------\r\n#  JWasm                      Masm v6.x\r\n#  JWLink                     MS Link, WLink\r\n# \r\n# PatchPE: HX tool to patch a PE binary to PX (not required for jwlink)\r\n\r\nNAME = KEYBGR\r\nAOPT=-c -nologo -coff -Fl$*.lst -Fo$*.obj -I..\\..\\Include\r\nOUTDIR=Release\r\n\r\n$(OUTDIR)\\$(NAME).DLL: $(OUTDIR)\\$(NAME).obj Makefile\r\n#\t@link /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /Entry:DllMain /OPT:NOWIN98 \r\n#\t@\\hx\\bin\\patchpe $*.DLL\r\n\t@jwlink format win pe hx dll ru native file $*.obj name $*.DLL op q,MAP=$*.MAP \r\n\r\n$(OUTDIR)\\$(NAME).obj: $(NAME).asm\r\n#\t@ml $(AOPT) $(NAME).asm\r\n\t@jwasm $(AOPT) $(NAME).asm\r\n"
  },
  {
    "path": "JLM/KEYBGR/README.TXT",
    "content": "\r\n Deutscher Tastaturtreiber fuer DOS der mit 48 Bytes DOS Speicher \r\n auskommt. Funktioniert nur in Verbindung mit Jemm.\r\n\r\n Wird installiert durch Eintrag in config.sys:\r\n \r\n DEVICE=JEMMEX.EXE (oder DEVICE=JEMM386.EXE)\r\n ...\r\n DEVICE=JLOAD.EXE KEYBGR.DLL\r\n\r\n KEYBGR ist Public Domain.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "JLM/QPIEMU/MAKE.BAT",
    "content": "@echo off\r\nrem create QPIEMU.DLL without linker, requires jwasm v2.19+\r\nrem Jemm's src directory has to be included since qpiemu needs ?PAGEDIR equate!\r\njwasm -c -pe -nologo -D?PE -I..\\..\\src -FlRelease\\QPIEMU.LST -FoRelease\\QPIEMU.DLL -I..\\..\\Include QPIEMU.ASM\r\n"
  },
  {
    "path": "JLM/QPIEMU/MAKEFILE",
    "content": "\r\n# NMake/WMake Makefile to create QPIEMU.DLL\r\n#\r\n#  tools                      alternatives\r\n#----------------------------------------------------------\r\n#  JWasm                      Masm v6.x\r\n#  JWLink                     MS Link, WLink, ALink\r\n#  (PatchPE) \r\n# \r\n# PatchPE: HX tool to patch a PE binary to PX ( not required for jwlink ).\r\n\r\nNAME = QPIEMU\r\nOUTDIR=Release\r\nAOPT=-c -coff -nologo -Fl$*.lst -Fo$*.obj -I..\\..\\Include\r\nASM=jwasm.exe\r\n#LINK=link.exe /NOLOGO /SUBSYSTEM:NATIVE /DLL $*.obj /OUT:$*.DLL /MAP:$*.MAP /EXPORT:ddb /Entry:DllMain /OPT:NOWIN98\r\nLINK=jwlink format win nt hx dll ru native file $*.obj name $*.DLL op q,MAP=$*.MAP export _ddb.1\r\n\r\nALL: $(OUTDIR) $(OUTDIR)\\$(NAME).DLL\r\n\r\n$(OUTDIR):\r\n\t@mkdir $(OUTDIR)\r\n\r\n$(OUTDIR)\\$(NAME).DLL: $(OUTDIR)\\$(NAME).obj Makefile\r\n\t@$(LINK)\r\n#\t@..\\patchpe $*.DLL\r\n\r\n$(OUTDIR)\\$(NAME).obj: $(NAME).asm\r\n\t@$(ASM) $(AOPT) $(NAME).asm\r\n\r\n"
  },
  {
    "path": "JLM/QPIEMU/QPIEMU.ASM",
    "content": "\r\n;--- JLM sample QPIEMU\r\n;--- use Makefile to create QPIEMU.DLL\r\n\r\n;--- QPIEMU installs a small subset of Qemm's QPI,\r\n;--- just enough to trap ports.\r\n\r\n\t.386\r\n\t.model flat, stdcall\r\n\r\n\t.nolist\r\n\tinclude jlm.inc\r\n\t.list\r\n\r\nDEVICE_ID equ 4354h\r\n\r\ncr equ 13\r\nlf equ 10\r\n\r\nDLL_PROCESS_ATTACH  equ 1\r\nDLL_PROCESS_DETACH  equ 0\r\n\r\nPGTAB0 equ 1\t;support ax=5000h - \"get page table 0 physical address\"\r\n\r\nif PGTAB0\r\n;--- this hack should be cleaned ASAP...\r\n\tinclude jemm.inc\r\n\tinclude jemm32.inc\t;needed to get linear address of page directory\r\nendif\r\n\r\n\t.data\r\n\r\n;--- the DDB must be make public. The linker will \"export\" this\r\n;--- symbol. This is the simplest method to make JLoad know the\r\n;--- device id.\r\n\r\nifdef ?PE\r\n\tpublic export ddb\t;syntax accepted since jwasm v2.19\r\n\toption dotname\r\n.drectve segment info\r\n\tdb \"-subsystem:native -dll -fixed:no\"\r\n.drectve ends\r\n.hdr$2 segment flat\r\n\tdb \"PX\"\r\n.hdr$2 ends\r\nelse\r\n\tpublic ddb\r\nendif\r\n\r\nddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,\"QPIEMU\",0,0, v86_dispatch >\r\n\r\ncallback dd 0\t; current far16 real-mode callback address\r\n\r\n\t.code\r\n\r\n;--- dispatcher for v86 services\r\n\r\nv86_dispatch proc\r\n\r\n\t@VMMCall Simulate_Far_Ret\t;emulate a RETF in v86\r\n\r\n\tand [ebp].Client_Reg_Struc.Client_EFlags,not 1  ;clear Carry flag\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX\r\n\tcmp ah, 3\r\n\tjz getversion\r\n\tcmp ah, 1Ah\r\n\tjz isio\r\nif PGTAB0\r\n\tcmp ax, 5000h\r\n\tjz ispt0\r\nendif\r\nerror:\r\n\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\tret\r\nif PGTAB0\r\nispt0:\r\n\tmov eax,ds:[?PAGEDIR]\r\n\tand ax, 0F000h\r\n\tmov [ebp].Client_Reg_Struc.Client_EDX, eax\r\n\tret\r\nendif\r\nisio:\r\n\tcmp al, 0\r\n\tjz simin\r\n\tcmp al, 1\r\n\tjz simout\r\n\tcmp al, 4\r\n\tjz simio\t\t; generic untrapped IO\r\n\tcmp al, 6\r\n\tjz gethandler\r\n\tcmp al, 7\r\n\tjz sethandler\r\n\tcmp al, 8\t\t; get port status\r\n\tjz getportstat\r\n\tcmp al, 9\t\t; trap port\r\n\tjz trapport\r\n\tcmp al, 10\t\t; untrap port\r\n\tjz untrapport\r\n\tjmp error\r\nsimin:\r\n\tmov edx, [ebp].Client_Reg_Struc.Client_EDX\r\n\tin al, dx\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EBX, al\r\n\tret\r\nsimout:\r\n\tmov edx, [ebp].Client_Reg_Struc.Client_EDX\r\n\tmov al, byte ptr [ebp].Client_Reg_Struc.Client_EBX\r\n\tout dx, al\r\n\tret\r\nsimio:\r\n\tmovzx edx, word ptr [ebp].Client_Reg_Struc.Client_EDX\r\n\tmov ecx, [ebp].Client_Reg_Struc.Client_ECX\r\n\tmov eax, [ebp].Client_Reg_Struc.Client_EBX\r\n\t@VMMCall Simulate_IO\r\n\ttest byte ptr [ebp].Client_Reg_Struc.Client_ECX, 24h\t;OUT or STRING_IO?\r\n\tjnz @F\r\n\tmov [ebp].Client_Reg_Struc.Client_EBX, eax\r\n@@:\r\n\tret\r\ngethandler:\r\n\tmov eax, [callback]\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EDI, ax\r\n\tshr eax, 16\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_ES, ax\r\n\tret\r\nsethandler:\r\n\tmov ax, word ptr [ebp].Client_Reg_Struc.Client_ES\r\n\tshl eax, 16\r\n\tmov ax, word ptr [ebp].Client_Reg_Struc.Client_EDI\r\n\tmov [callback], eax\r\n\tret\r\ngetportstat:\r\n\r\n;--- todo\r\n\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EBX, 0\t;0=port not trapped\r\n\tret\r\ntrapport:\r\n\tpush esi\r\n\tmov esi, offset iocb\r\n\tmovzx edx, word ptr [ebp].Client_Reg_Struc.Client_EDX\r\n\t@VMMCall Install_IO_Handler\r\n\t.if ( CARRY? )\r\n\t\tor [ebp].Client_Reg_Struc.Client_EFlags, 1  ;set Carry flag\r\n\t.endif\r\n\tpop esi\r\n\tret\r\nuntrapport:\r\n\tmovzx edx, word ptr [ebp].Client_Reg_Struc.Client_EDX\r\n\t@VMMCall Remove_IO_Handler\r\n\t.if ( CARRY? )\r\n\t\tor [ebp].Client_Reg_Struc.Client_EFlags, 1  ;set Carry flag\r\n\t.endif\r\n\tret\r\n\r\n\talign 4\r\n\r\nv86_dispatch endp\r\n\r\ngetversion proc\r\n\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0703h\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EBX, 0703h\r\n\tret\r\n\talign 4\r\n\r\ngetversion endp\r\n\r\n;--- io handler proc\r\n;--- ecx=type of io, see jemm32.inc:\r\n;---  bit 2: 0=IN, 1=OUT\r\n;---  bit 3+4: 00=byte, 01=word, 10=dword\r\n;---  bit 5: string IO\r\n;---  bit 6: REP\r\n;--- edx=port\r\n;--- ebp=client struct\r\n;--- eax=client's eax\r\n;--- out: EAX=value read ( will be stored in EAX if IN )\r\n\r\niocb proc\r\n\r\n\tpush ecx\r\n\tpush edx\r\n\r\n\tpush [ebp].Client_Reg_Struc.Client_EFlags\r\n\tpush [ebp].Client_Reg_Struc.Client_ECX\r\n\tpush [ebp].Client_Reg_Struc.Client_EDX\r\n\r\n;--- setup registers\r\n;--- EAX: current client reg\r\n;--- EBX: current client reg\r\n;--- CL: bit 2: 0=IN, 1=OUT\r\n;---     bit 3+4: 00=BYTE, 01=WORD, 10=DWORD\r\n;--- CH: bit 1: 1=IF (QPI, undocumented)\r\n;--- DX=port\r\n\r\n\tmov ch, byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1\r\n\tand ch, 2\r\n\r\n\tand byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, not 1+2\t; reset TF & IF!\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EDX, dx\r\n\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_ECX, cx\r\n\r\n\t@VMMCall Begin_Nest_Exec \t;start nested execution\r\n\tmovzx edx, word ptr [callback+0]\r\n\tmovzx ecx, word ptr [callback+2]\r\n\t@VMMCall Simulate_Far_Call\r\n\t@VMMCall Resume_Exec\t\t;run the VM\r\n\t@VMMCall End_Nest_Exec\t\t;end nested execution\r\n\r\n\tpop [ebp].Client_Reg_Struc.Client_EDX\r\n\tpop [ebp].Client_Reg_Struc.Client_ECX\r\n\tpop [ebp].Client_Reg_Struc.Client_EFlags\r\n\r\n\tmov eax, [ebp].Client_Reg_Struc.Client_EAX\r\n\r\n\tpop edx\r\n\tpop ecx\r\n\tret\r\n\r\n\talign 4\r\n\r\niocb endp\r\n\r\n;--- install the JLM: just set eax=1\r\n;--- this tells JLOAD that it's ok to add IOTRAP to the list of\r\n;--- loaded modules.\r\n\r\ninstall proc uses esi pcomm:ptr JLCOMM\r\n\r\n\tmov eax,1\r\n\tret\r\n\talign 4\r\n\r\ninstall endp\r\n\r\n;--- deinstall the JLM:\r\n\r\ndeinstall proc pcomm:ptr JLCOMM\r\n\r\n\tcmp [callback], 0\r\n\tsetz al\r\n\tmovzx eax, al\r\n\tret\r\n\talign 4\r\n\r\ndeinstall endp\r\n\r\nDllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n\tmov eax,dwReason\r\n\tcmp eax,DLL_PROCESS_ATTACH\r\n\tjnz @F\r\n\tinvoke install, dwRes\r\n\tjmp exit\r\n@@:\r\n\tcmp eax,DLL_PROCESS_DETACH\r\n\tjnz @F\r\n\tinvoke deinstall, dwRes\r\n@@:\r\nexit:\r\n\tret\r\n\talign 4\r\n\r\nDllMain endp\r\n\r\n\tend DllMain\r\n"
  },
  {
    "path": "JLM/QPIEMU/QPIEMU.txt",
    "content": "\r\n 1. About\r\n\r\n QPIEMU is a JLM that partly emulates the Qemm API (QPI). Its\r\n purpose is to provide the IO trapping part of QPI, for better support\r\n of sound card emulation in DOS.\r\n\r\n\r\n 2. Install/Uninstall QPIEMU\r\n\r\n QPIEMU can be installed either as a device driver in CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE QPIEMU.DLL\r\n\r\n or as a TSR from the command line:\r\n\r\n   JLOAD QPIEMU.DLL\r\n\r\n To uninstall, use JLOAD's -u option:\r\n\r\n   JLOAD -u QPIEMU.DLL\r\n\r\n\r\n 3. Using QPIEMU\r\n\r\n Once QPIEMU is installed, tools that use the QPI ( i.e. SBEMU )\r\n and hence usually require Qemm to be installed, should run with\r\n Jemm386/JemmEx.\r\n\r\n\r\n 4. Technical Details\r\n\r\n QPI is a \"real-mode\" API, hence if addresses are used, they are in\r\n segment:offset format.\r\n\r\n The part of the supported QPI is:\r\n \r\n AH=03h, QPI_GetVersion\r\n AX=1A00h, QPI_UntrappedIORead\r\n AX=1A01h, QPI_UntrappedIOWrite\r\n AX=1A04h, QPI_UntrappedIO\r\n AX=1A06h, QPI_GetIOCallback\r\n AX=1A07h, QPI_SetIOCallback\r\n AX=1A08h, QPI_GetPortTrap\r\n AX=1A09h, QPI_SetPortTrap\r\n AX=1A0Ah, QPI_ClearPortTrap\r\n\r\n Since v1.2, there's also AX=5000h, which isn't part of the original QPI.\r\n It returns physical address of Jemm's page table 0 in register EDX.\r\n\r\n The way to get the address of QPI itself differs for QPIEMU:\r\n an INT 2Fh, with AX=1684h, BX=4354h has to be called. If successful,\r\n It will return with AL==0 and the QPI entry point in ES:DI.\r\n\r\n\r\n History\r\n \r\n - v1.0: 03/2023: initial\r\n - v1.1: 07/2025: bit 1 of CH holds IF\r\n - v1.2: 09/2025: added function ax=5000h\r\n\r\n\r\n 5. License\r\n\r\n QPIEMU is Public Domain.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "JLM/QPIEMU/Test/MAKEFILE",
    "content": "\r\n# NMake/WMake Makefile to create TESTQPI.EXE & TESTDMA.EXE\r\n\r\nNAME1 = TESTQPI\r\nNAME2 = TESTDMA\r\nOUTDIR= .\r\nASM=jwasm.exe\r\n\r\nALL: $(OUTDIR)\\$(NAME1).EXE $(OUTDIR)\\$(NAME2).EXE\r\n\r\n$(OUTDIR)\\$(NAME1).EXE: $(NAME1).asm\r\n\t@$(ASM) -nologo -mz -Fl$* -Sg -Fo=$*.EXE $(NAME1).asm\r\n\r\n$(OUTDIR)\\$(NAME2).EXE: $(NAME2).asm\r\n\t@$(ASM) -nologo -mz -Fl$* -Sg -Fo=$*.EXE $(NAME2).asm\r\n"
  },
  {
    "path": "JLM/QPIEMU/Test/PRINTF16.INC",
    "content": "\r\n;--- simple printf() implementation\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- outb is expected to be onto stack\r\n;--- hiwords or eax, edx, eax are used, but restored.\r\n\r\nltob PROC stdcall uses edi edx number:dword, outb:word, base:word\r\n\r\n\tmov ch, 0\r\n\tmovzx edi, base\r\n\tpush eax\r\n\tmov eax, number\r\n\tcmp di, -10\r\n\tjne @F\r\n\tmov di, 10\r\n\tand eax, eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch, '-'\r\n@@:\r\n\tmov bx, outb\r\n\tadd bx, 10\r\n\tmov BYTE PTR ss:[bx], 0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl, '0'\r\n\tcmp dl, '9'\r\n\tjbe @F\r\n\tadd dl, 7+20h\r\n@@:\r\n\tmov ss:[bx], dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx], ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tpop eax\r\n\tmov ax, bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- ds=dgroup, ss don't need to be dgroup\r\n;--- preserve all registers\r\n\r\nprintf PROC c fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tpusha\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\tpopa\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\n\t.while byte ptr [si] >= '0' && byte ptr [si] <= '9'\r\n\t\tlodsb\r\n\t\tsub al,'0'\r\n\t\tcbw\r\n\t\timul cx,bx,10\t\t;cx = bx * 10\r\n\t\tadd ax,cx\r\n\t\tmov bx,ax\r\n\t.endw\r\n\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,0\r\n\tjnz @@L359\r\n\tpop ax\r\n\tjmp done\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "JLM/QPIEMU/Test/SETARGV.INC",
    "content": "\r\n;--- read the commandline at PSP:81h\r\n;--- and create an argc/argv structure on the stack.\r\n;--- in: ES=PSP, DS=DGROUP, SS=DGROUP\r\n;--- out: _argc (=[bp-2])\r\n;---      _argv (=[bp-4])\r\n;--- all std registers modified (including SP)\r\n\r\n?DUMMYFN equ 1\r\n?QUOTES  equ 1\r\n\r\n_setargv proc\r\n\r\n    mov bp, sp\r\n    sub sp, 256             ; just make enough room for argc/argv\r\n\r\n    xor di, di              ; init argc\r\n    xor dx, dx              ; init size of mem block\r\n    mov si, 81H\r\n    push es\r\n    pop ds\r\n;    assume ds:nothing      ; no need for assumes, since no global vars are accessed\r\n    jmp scanarg\r\n\r\n;--- DI = argc\r\n;--- DX = block size (not including null terminators)\r\nnextarg:\r\n    push bx                 ; save argument size\r\nscanarg:\r\n@@:\r\n    lodsb\r\n    cmp al, ' '\r\n    je @B\r\n    cmp al, 9\r\n    je @B\r\n    cmp al, 13\r\n    jz doneargs             ; exit if eol\r\n    inc di                  ; another argument\r\n    xor bx, bx              ; init argument size\r\nif ?QUOTES\r\n    cmp al, '\"'\r\n    jz handle_quote\r\nendif\r\n    dec si                  ; back up to reload character\r\n    push si                 ; save argument ofs\r\n@@:\r\n    lodsb\r\n    cmp al, ' '             ; end argument?\r\n    je nextarg\r\n    cmp al, 9\r\n    je nextarg              ; white space terminates argument\r\n    cmp al, 13\r\n    jz doneargs2            ; exit if eol\r\n    inc bx\r\n    inc dx\r\n    jmp @B\r\nif ?QUOTES\r\nhandle_quote:\r\n    push si\r\n@@:\r\n    lodsb\r\n    cmp al, 13\r\n    jz quoteerr\r\n    cmp al, '\"'\r\n    jz @F\r\n    inc dx\r\n    inc bx\r\n    jmp @B\r\nquoteerr:\r\n    dec si                  ; \"unread\" the CR\r\n@@:\r\n    jmp nextarg\r\nendif\r\ndoneargs2:\r\n    push bx                 ; last argument's size\r\ndoneargs:\r\n\r\n;--- address & size of arguments are pushed\r\n\r\n    mov cx, di\r\n    add dx, di              ; DX=size arguments + terminator bytes\r\n    inc di                  ; add one for NULL pointer\r\nif ?DUMMYFN\r\n    inc di                  ; add one for filename\r\nendif\r\n    shl di, 1               ; each ofs needs 2 bytes\r\n    add dx, di              ; DX=size args + size argv\r\n    and dx, -2              ; ensure stack remains word aligned\r\n    mov ax, [bp]\r\n    sub bp, dx              ; alloc the really needed space for argc/argv\r\n    mov [bp-6], ax          ; store return address\r\n\r\n_argc equ <bp-2>\r\n_argv equ <bp-4>\r\n\r\n    mov [_argv], bp\r\n    mov [_argc], cx\r\n\r\n    add di, bp              ; di -> behind vector table (strings)\r\n    xor ax, ax\r\n    lea bx, [di-2]\r\n    mov ss:[bx], ax         ; terminating 0000 _argv[x]\r\n    sub bx, 2\r\n    jcxz noargs\r\n    push ss\r\n    pop es\r\n\r\n;--- copy the arguments from PSP onto the stack\r\n\r\n    mov dx, cx\r\n@@:\r\n    pop cx                  ; size\r\n    pop si                  ; address\r\n    mov ss:[bx], di         ; store _argv[x]\r\n    sub bx, 2\r\n    rep movsb\r\n    stosb                   ; AL still 0\r\n    dec dx\r\n    jnz @B\r\n\r\nnoargs:\r\n    push ss\r\n    pop ds\r\n;    assume ds:DGROUP\r\nif ?DUMMYFN\r\n    mov [bx], ax            ; store 0 as dummy filename\r\n    inc word ptr [_argc]\r\nendif\r\n    lea sp, [bp-6]\r\n    ret\r\n_setargv endp\r\n\r\n"
  },
  {
    "path": "JLM/QPIEMU/Test/TESTDMA.ASM",
    "content": "\r\n;--- test Qemm/QPIEMU ( IO trap part )\r\n;--- this time test ISA DMA ports\r\n\r\n\t.286\r\n\t.model small\r\n\t.dosseg\r\n\t.stack 1024\r\n\r\nPORT equ 0C4h\r\n\r\nlf equ 10\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text, 0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n\r\ndwQemm dd 0\r\ndwOldIO dd 0\r\nwVersion dw 0\r\n\r\n\t.code\r\n\r\ndataseg dw 0\r\n\r\n\t.386\r\n\r\n\tinclude printf16.inc\t; this printf() preserves all registers!\r\n\r\nPUSHAS struct\r\nwDI\tdw ?\r\nwSI\tdw ?\r\nwBP\tdw ?\r\n\tdw ?\r\nwBX\tdw ?\r\nwDX\tdw ?\r\nwCX\tdw ?\r\nwAX\tdw ?\r\nPUSHAS ends\r\n\r\nmyio proc far\r\n\tpusha\r\n\tmov bp, sp\r\n\tpush ds\r\n\tmov ds, cs:[dataseg]\r\n\tpushf\r\n\tinvoke 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]\r\n\tadd sp, 2\r\nif 0\r\n\tcmp byte ptr [bp].PUSHAS.wCX, 0\r\n\tjnz @F\r\n\tmov ax, 1A00h\t\t\t; untrapped read (byte only)\r\n\tmov dx, [bp].PUSHAS.wDX\r\n\tcall [dwQemm]\r\n\tmov byte ptr [bp].PUSHAS.wAX, bl\r\n\tinvoke printf, CStr(\"in myio, untrapped read=%X\",10), bl\r\n@@:\r\nelse\r\n\tmov cx, [bp].PUSHAS.wCX\t; bits set to allow byte/word/dword read/write\r\n\tmov dx, [bp].PUSHAS.wDX\r\n\r\n\tpush ebx\r\n\tmov ebx, eax\t\t\t; for OUT, ebx is to be loaded with value of eax\r\n\tmov bx, [bp].PUSHAS.wAX\r\n\r\n\tmov ax, 1A04h\t\t\t; untrapped IO (generic)\r\n\tcall [dwQemm]\r\n\ttest [bp].PUSHAS.wCX, 4\t; IN?\r\n\tjnz @F\r\n\tmov eax, ebx\t\t\t; then load EAX with read value\r\n\tmov [bp].PUSHAS.wAX, ax\r\n\tinvoke printf, CStr(\"in myio, untrapped read=%lX\",10), eax\r\n@@:\r\n\tpop ebx\r\nendif\r\n\tpop ds\r\n\tpopa\r\n\tclc\t\t; NC=access handled\r\n\tretf\r\nmyio endp\r\n\r\nmain proc c args:word, argv:word\r\n\r\n;--- 1. test if Qemm is installed\r\n\tpush 0\r\n\tpop es\r\n\tmov eax, es:[67h*4]\r\n\tand eax, eax\r\n\tjz noemm\r\n\tmov cx, \"QE\"\r\n\tmov dx, \"MM\"\r\n\tmov ah, 3Fh\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjz qpi_ok\r\n\r\n;--- 2. check for QPIEMU\r\n\tmov ax, 1684h\r\n\tmov bx, 4354h\r\n\tint 2Fh\r\n\tcmp al, 0\r\n\tjnz noqpi\r\nqpi_ok:\r\n\tmov word ptr [dwQemm+0], di\r\n\tmov word ptr [dwQemm+2], es\r\n\tinvoke printf, CStr(\"QEMM/QPI found, entry=%X:%X\",10), es, di\r\n\tmov ah, 3\r\n\tcall [dwQemm]\r\n\tmov [wVersion], bx\r\n\tinvoke printf, CStr(\"QPI call AH=3, Qemm version: %X.%02X\",10), bh, bl\r\n\tcmp [wVersion], 0703h\r\n\tjb exit\r\n\tmov ax, 1A06h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A06h failed\",10)\r\n\t\tjmp exit\r\n\t.endif\r\n\tmov word ptr [dwOldIO+0], di\r\n\tmov word ptr [dwOldIO+2], es\r\n\tinvoke printf, CStr(\"QPI call AX=1A06h ok, current IO trap callback: %X:%X\",10), es, di\r\n\tmov di, offset myio\r\n\tmov ax, seg myio\r\n\tmov es, ax\r\n\tmov ax, 1A07h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A07h failed\",10)\r\n\t\tjmp exit\r\n\t.endif\r\n\tinvoke printf, CStr(\"QPI call AX=1A07h ok, new IO trap callback: %X:%X\",10), es, di\r\n\r\n;-- ax=1A08h is GetPortTrap()\r\n\r\n\tmov dx, PORT\r\n\tmov ax, 1A09h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A09h, port %X failed\",10), PORT\r\n\t\tjmp exit2\r\n\t.endif\r\n\tinvoke printf, CStr(\"QPI call AX=1A09h ok, port %X now trapped\",10), PORT\r\n\r\n\tmov dx, PORT+2\r\n\tmov ax, 1A09h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A09h, port %X failed\",10), dx\r\n\t\tjmp exit3\r\n\t.endif\r\n\tinvoke printf, CStr(\"QPI call AX=1A09h ok, port %X now trapped\",10), dx\r\n\r\n;--- access port DX\r\n\r\n\tinvoke printf, CStr(\"accessing port 220h\",10)\r\n\r\n\tmov bx, -1\r\n\tmov cx, -1\r\n\tmov dx, PORT\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin al, dx\r\n\tinvoke printf, CStr(\"IN al, dx: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin ax, dx\r\n\tinvoke printf, CStr(\"IN ax, dx: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 12345678h\r\n\tout dx, al\r\n\tout dx, ax\r\n\r\n\tinvoke printf, CStr(\"behind IO instructions\",10)\r\n\r\n\tmov dx, PORT+2\r\n\tmov ax, 1A0Ah\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah, port %X failed\",10), dx\r\n\t.else\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah ok, port %X now untrapped\",10), dx\r\n\t.endif\r\n\r\nexit3:\r\n\tmov dx, PORT\r\n\tmov ax, 1A0Ah\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah, port %X failed\",10), dx\r\n\t.else\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah ok, port %X now untrapped\",10), dx\r\n\t.endif\r\n\r\nexit2:\r\n\tles di, [dwOldIO]\r\n\tmov ax, 1A07h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A07h failed, IO callback not restored\",10)\r\n\t.else\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A07h ok, IO callback restored to %X:%X\",10), es, di\r\n\t.endif\r\nexit:\r\n\tret\r\nnoemm:\r\n\tinvoke printf, CStr(\"no EMM found\",10)\r\n\tret\r\nnoqpi:\r\n\tinvoke printf, CStr(\"QPI not available\",10)\r\n\tret\r\nmain endp\r\n\r\n\tinclude setargv.inc\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov cs:[dataseg], ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall _setargv\r\n\tinvoke main, [_argc], [_argv]\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "JLM/QPIEMU/Test/TESTQPI.ASM",
    "content": "\r\n;--- test Qemm/QPIEMU ( IO trap part )\r\n\r\n\t.286\r\n\t.model small\r\n\t.dosseg\r\n\t.stack 1024\r\n\r\nlf equ 10\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text, 0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n\r\ndwQemm dd 0\r\ndwOldIO dd 0\r\nwVersion dw 0\r\n\r\n\t.code\r\n\r\ndataseg dw 0\r\n\r\n\t.386\r\n\r\n\tinclude printf16.inc\t; this printf() preserves all registers!\r\n\r\nPUSHAS struct\r\nwDI\tdw ?\r\nwSI\tdw ?\r\nwBP\tdw ?\r\n\tdw ?\r\nwBX\tdw ?\r\nwDX\tdw ?\r\nwCX\tdw ?\r\nwAX\tdw ?\r\nPUSHAS ends\r\n\r\nmyio proc far\r\n\tpusha\r\n\tmov bp, sp\r\n\tpush ds\r\n\tmov ds, cs:[dataseg]\r\n\tpushf\r\n\tinvoke 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]\r\n\tadd sp, 2\r\nif 0\r\n\tcmp byte ptr [bp].PUSHAS.wCX, 0\r\n\tjnz @F\r\n\tmov ax, 1A00h\t\t\t; untrapped read (byte only)\r\n\tmov dx, [bp].PUSHAS.wDX\r\n\tcall [dwQemm]\r\n\tmov byte ptr [bp].PUSHAS.wAX, bl\r\n\tinvoke printf, CStr(\"in myio, untrapped read=%X\",10), bl\r\n@@:\r\nelse\r\n\tmov cx, [bp].PUSHAS.wCX\t; bits set to allow byte/word/dword read/write\r\n\tmov dx, [bp].PUSHAS.wDX\r\n\r\n\tpush ebx\r\n\tmov ebx, eax\t\t\t; for OUT, ebx is to be loaded with value of eax\r\n\tmov bx, [bp].PUSHAS.wAX\r\n\r\n\tmov ax, 1A04h\t\t\t; untrapped IO (generic)\r\n\tcall [dwQemm]\r\n\ttest [bp].PUSHAS.wCX, 4\t; IN?\r\n\tjnz @F\r\n\tmov eax, ebx\t\t\t; then load EAX with read value\r\n\tmov [bp].PUSHAS.wAX, ax\r\n\tinvoke printf, CStr(\"in myio, untrapped read=%lX\",10), eax\r\n@@:\r\n\tpop ebx\r\nendif\r\n\tpop ds\r\n\tpopa\r\n\tclc\t\t; NC=access handled\r\n\tretf\r\nmyio endp\r\n\r\nmain proc c args:word, argv:word\r\n\r\n;--- 1. test if Qemm is installed\r\n\tpush 0\r\n\tpop es\r\n\tmov eax, es:[67h*4]\r\n\tand eax, eax\r\n\tjz noemm\r\n\tmov cx, \"QE\"\r\n\tmov dx, \"MM\"\r\n\tmov ah, 3Fh\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjz qpi_ok\r\n\r\n;--- 2. check for QPIEMU\r\n\tmov ax, 1684h\r\n\tmov bx, 4354h\r\n\tint 2Fh\r\n\tcmp al, 0\r\n\tjnz noqpi\r\nqpi_ok:\r\n\tmov word ptr [dwQemm+0], di\r\n\tmov word ptr [dwQemm+2], es\r\n\tinvoke printf, CStr(\"QEMM/QPI found, entry=%X:%X\",10), es, di\r\n\tmov ah, 3\r\n\tcall [dwQemm]\r\n\tmov [wVersion], bx\r\n\tinvoke printf, CStr(\"QPI call AH=3, Qemm version: %X.%02X\",10), bh, bl\r\n\tcmp [wVersion], 0703h\r\n\tjb exit\r\n\tmov ax, 1A06h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A06h failed\",10)\r\n\t\tjmp exit\r\n\t.endif\r\n\tmov word ptr [dwOldIO+0], di\r\n\tmov word ptr [dwOldIO+2], es\r\n\tinvoke printf, CStr(\"QPI call AX=1A06h ok, current IO trap callback: %X:%X\",10), es, di\r\n\tmov di, offset myio\r\n\tmov ax, seg myio\r\n\tmov es, ax\r\n\tmov ax, 1A07h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A07h failed\",10)\r\n\t\tjmp exit\r\n\t.endif\r\n\tinvoke printf, CStr(\"QPI call AX=1A07h ok, new IO trap callback: %X:%X\",10), es, di\r\n\r\n;-- ax=1A08h is GetPortTrap()\r\n\r\n\tmov dx, 220h\r\n\tmov ax, 1A09h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A09h, port 220h failed\",10)\r\n\t\tjmp exit2\r\n\t.endif\r\n\tinvoke printf, CStr(\"QPI call AX=1A09h ok, port 220h now trapped\",10)\r\n\r\n\tmov dx, 007Ch\r\n\tmov ax, 1A09h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A09h, port 7Ch failed\",10)\r\n\t\tjmp exit3\r\n\t.endif\r\n\tinvoke printf, CStr(\"QPI call AX=1A09h ok, port 7Ch now trapped\",10)\r\n\r\n;--- access port DX\r\n\r\n\tinvoke printf, CStr(\"accessing port 220h\",10)\r\n\r\n\tmov bx, -1\r\n\tmov cx, -1\r\n\tmov dx, 220h\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin al, dx\r\n\tinvoke printf, CStr(\"IN al, dx: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin ax, dx\r\n\tinvoke printf, CStr(\"IN ax, dx: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin eax, dx\r\n\tinvoke printf, CStr(\"IN eax, dx: EAX=%lX\",10), eax\r\n\r\n\tcli\r\n\tmov eax, 55aa55AAh\r\n\tin al, dx\r\n\tinvoke printf, CStr(\"CLI, IN al, dx: EAX=%lX\",10), eax\r\n\r\n\tcli\r\n\tmov eax, 55aa55AAh\r\n\tin ax, dx\r\n\tinvoke printf, CStr(\"CLI, IN ax, dx: EAX=%lX\",10), eax\r\n\tsti\r\n\r\n\tmov eax, 12345678h\r\n\tout dx, al\r\n\tout dx, ax\r\n\tout dx, eax\r\n\r\n;--- access port 7Ch\r\n\r\n\tinvoke printf, CStr(\"accessing port 7C\",10)\r\n\r\n\tmov bx, -1\r\n\tmov cx, -1\r\n\tmov dx, -1\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin al, 7Ch\r\n\tinvoke printf, CStr(\"IN al, 7C: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin ax, 7Ch\r\n\tinvoke printf, CStr(\"IN ax, 7C: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 55aa55AAh\r\n\tin eax, 7Ch\r\n\tinvoke printf, CStr(\"IN eax, 7C: EAX=%lX\",10), eax\r\n\r\n\tmov eax, 12345678h\r\n\tout 7Ch, al\r\n\tout 7Ch, ax\r\n\tout 7Ch, eax\r\n\r\n\tinvoke printf, CStr(\"behind IO instructions\",10)\r\n\r\n\tmov dx, 007Ch\r\n\tmov ax, 1A0Ah\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah, port 7Ch failed\",10)\r\n\t.else\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah ok, port 7Ch now untrapped\",10)\r\n\t.endif\r\n\r\nexit3:\r\n\tmov dx, 220h\r\n\tmov ax, 1A0Ah\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah, dx=220h failed\",10)\r\n\t.else\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A0Ah ok, port 220h now untrapped\",10)\r\n\t.endif\r\n\r\nexit2:\r\n\tles di, [dwOldIO]\r\n\tmov ax, 1A07h\r\n\tcall [dwQemm]\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A07h failed, IO callback not restored\",10)\r\n\t.else\r\n\t\tinvoke printf, CStr(\"QPI call AX=1A07h ok, IO callback restored to %X:%X\",10), es, di\r\n\t.endif\r\nexit:\r\n\tret\r\nnoemm:\r\n\tinvoke printf, CStr(\"no EMM found\",10)\r\n\tret\r\nnoqpi:\r\n\tinvoke printf, CStr(\"QPI not available\",10)\r\n\tret\r\nmain endp\r\n\r\n\tinclude setargv.inc\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov cs:[dataseg], ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall _setargv\r\n\tinvoke main, [_argc], [_argv]\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "JLM/README.TXT",
    "content": "\r\n 1. About\r\n\r\n This directory contains some JLMs:\r\n\r\n AHCICD:   AHCI Optical disk driver.\r\n GENERIC:  protected-mode TSR sample in ASM (Masm/JWasm).\r\n HELLO:    \"hello world\" JLM in ASM (Masm/JWasm)\r\n HELLO2:   \"hello world\" JLM in C (MS VC, Open Watcom)\r\n IOTRAP:   sample how to trap IO port access.\r\n JCLOCK:   shows clock in text mode.\r\n KEYBGR:   German keyboard driver.\r\n QPIEMU:   emulates port trapping part of QPI.  \r\n REBOOT:   shows how control message DEVICE_REBOOT_NOTIFY may be handled.\r\n XCDROM32: UltraDMA DVD/CD-ROM driver (GNU GPL 2).\r\n XDMA32:   UltraDMA HD driver (GNU GPL 2).\r\n\r\n GENERIC, HELLO, HELLO2, IOTRAP, JCLOCK, QPIEMU are Public Domain.\r\n\r\n\r\n 2. Tools that may be used\r\n\r\n tools             version   result\r\n -----------------------------------------\r\n assembler:\r\n Masm               6.15      ok\r\n JWasm              2.15      ok\r\n Fasm               1.67      ok\r\n\r\n C compiler:\r\n MS VC              2/5/6     ok\r\n Borland C++        5.5       ok\r\n Ladsoft CC386      3.8.1.18  ok\r\n Digital Mars C++   8.49      ok\r\n Open Watcom WCC386 1.9       ok\r\n \r\n COFF linker:\r\n JWlink             1.9beta17 ok\r\n Open Watcom WLink  1.9       ok\r\n MS Link            6.00      ok\r\n\r\n"
  },
  {
    "path": "JLM/REBOOT/FASTBOOT.ASM",
    "content": "\r\n;--- Sample how to handle DEVICE_REBOOT_NOTIFY control message;\r\n;--- handles fastboot only.\r\n;--- Requires Jemm v5.86+.\r\n;--- To be assembled with JWasm v2.19+; if Masm is to be used,\r\n;--- FASTBRM.BIN must be converted to FASTBRM.INC.\r\n;--- If a link step is added, the linker is supposed to export ddb.\r\n\r\n\t.386p\r\n\t.MODEL FLAT, stdcall\r\n\toption casemap:none\r\n\r\n\tinclude jlm.inc\r\n\r\nifndef BOOTSECT\r\nBOOTSECT equ 1\r\nendif\r\nifndef LDDBG  ;support API \"load DebugB\"?\r\nLDDBG equ 1\r\nendif\r\nifndef LDI13EXT ;support API \"load Int 13h extension\"?\r\nLDI13EXT equ 1\r\nendif\r\n\r\nDLL_PROCESS_ATTACH equ 1\r\nDLL_PROCESS_DETACH equ 0   \r\n\r\nDEVICE_ID equ 4435h\r\n\r\n;--- equates from jemm32.inc\r\nREAL_CODE_SEL   equ 4 * 8\r\nREAL_DATA_SEL   equ 5 * 8\r\n\r\nRMCOMM struct\r\n\tdb ?,?,?\t;mov cr0,ecx\r\n\tdb ?\t\t;jmp far16 opcode (0EAh)\r\n\tdw ?,?\t\t;offset, segment of jmp far16\r\nbPartition db ?\r\nbDisk db ?\r\nif LDDBG or LDI13EXT\r\nbFlags db ?     ;1:call DebugB init, 1: stop in DebugB; 2: 1=call int13 extension init.\r\nendif\r\nRMCOMM ends\r\n\r\nifdef @pe_file_flags   ;defined by JWasm if option -pe is set\r\n\toption dotname\r\n.drectve segment info\r\n\tdb \"-dll \"\r\n\tdb \"-subsystem:native \"\r\n\tdb \"-fixed:no \"\r\n.drectve ends\r\n\r\n.hdr$2 segment flat\r\n\tdb \"PX\"\r\n.hdr$2 ends\r\nEXPORT equ <export>\r\nelse\r\nEXPORT equ <>\r\nendif\r\n\r\n\t.const\r\n\r\n\tpublic EXPORT ddb\r\n\r\nddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,\"REBOOT\",0, ctrlproc, v86_dispatch >\r\n\r\nhelptxt label byte\r\n\tdb \"Usage:\",13,10\r\n\tdb \"jload fastboot.dll [options]\",13,10\r\n\tdb \"options:\",13,10\r\n\tdb \" /Dn\",9,\"set Hard Disk to boot from; n=0..7; default 0\",13,10\r\n\tdb \" /Pn\",9,\"set Partition to boot from; n=1..8; no default\",13,10\r\nif BOOTSECT\r\n\tdb ' /B:file set File to boot from; file=name of file to be used as boot sector',13,10\r\nendif\r\n\tdb 0\r\nfileerror label byte\r\n\tdb \"file access error or invalid format\",13,10\r\n\tdb 0\r\n\r\n\t.data\r\n\r\nif LDDBG\r\ndwStartDbg dd 0\r\ndwSizeDbg  dd 0\r\nendif\r\nif LDI13EXT\r\ndwStartI13 dd 0\r\ndwSizeI13  dd 0\r\nendif\r\nrmcode label byte\r\nifdef __JWASM__\r\n\tincbin <fastbrm.bin>\r\nelse\r\n\tinclude <fastbrm.inc>\t;translated by bin2inc\r\nendif\r\nsizermcode equ $ - rmcode\r\n\r\nif BOOTSECT\r\n\t.data?\r\nbsbuffer db 512 dup (?)\r\nendif\r\n\r\n\t.CODE\r\n\r\n;--- V86 API\r\n;--- AH=0: get version\r\n;--- AH=1: set disk (AL=disk, HDs only)\r\n;--- AH=2: set partition (AL=partition, one-based)\r\n;--- AH=3: set boot debugger (DS:SI=address, ECX=size)\r\n;--- AH=4: set int 13h extension (DS:SI=address, ECX=size)\r\n\r\nv86_dispatch proc\r\n\r\n\t@VMMCall Simulate_Far_Ret\t;emulate a RETF in v86\r\n\tand [ebp].Client_Reg_Struc.Client_EFlags,not 1  ;clear Carry flag\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_EAX\r\n\tcmp ah,0\r\n\tjz getversion\r\n\tcmp ah,1\r\n\tjz setdisk\r\n\tcmp ah,2\r\n\tjz setpart\r\nif LDDBG\r\n\tcmp ah,3\r\n\tjz setdbg\r\nendif\r\nif LDI13EXT\r\n\tcmp ah,4\r\n\tjz seti13ext\r\nendif\r\nerror:\r\n\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\tret\r\ngetversion:\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0101h   ;bit 0 of AH signals FASTBOOT\r\n\tret\r\nsetdisk:\r\n\tmov rmcode.RMCOMM.bDisk,al\r\n\tret\r\nsetpart:\r\n\tmov rmcode.RMCOMM.bPartition,al\r\n\tret\r\nif LDDBG\r\nsetdbg:\r\n\tcall ReleaseDbg\r\n\tcall StoreFile\r\n\tjc @F\r\n\tmov dwStartDbg, eax\r\n\tmov dwSizeDbg, ecx\r\n@@:\r\n\tret\r\nendif\r\nif LDI13EXT\r\nseti13ext:\r\n\tcall ReleaseI13\r\n\tcall StoreFile\r\n\tjc @F\r\n\tmov dwStartI13, eax\r\n\tmov dwSizeI13, ecx\r\n@@:\r\n\tret\r\nendif\r\nv86_dispatch endp\r\n\r\n;--- control proc: handle messages from Jemm\r\n\r\nctrlproc proc\r\n\tcmp eax, DEVICE_REBOOT_NOTIFY\r\n\tjz reboot_notify\r\n@@:\r\n\tclc\r\n\tret\r\nreboot_notify:\r\n\ttest bl,1      ;fastboot flag?\r\n\tjz @B\r\n\r\nif LDDBG or LDI13EXT\r\n;\tmov rmcode.RMCOMM.bFlags,0\r\nendif\r\n\r\nif LDI13EXT\r\n\tmov ecx,dwSizeI13\r\n\tjecxz no_i13ext\r\n\tmov edi,8000h\r\n\tmov esi,dwStartI13\r\n\tcld\r\n\trep movsb\r\n\tor rmcode.RMCOMM.bFlags,4\r\nno_i13ext:\r\nendif\r\n\r\nif LDDBG\r\n\tmov ecx,dwSizeDbg\r\n\tjecxz no_dbginit\r\n\tadd ecx,1024-1\r\n\tmovzx edi,word ptr ds:[413h]\r\n\tshl edi,10        ;convert kB to bytes\r\n;--- if already loaded, don't change ds:[413h]\r\n\tcmp byte ptr [edi],0E9h  ;jmp near16 opcode?\r\n\tjnz adjmem\r\n\tmovzx eax,word ptr [edi+1]\r\n\tcmp dword ptr [edi+eax-1],0DEADBEEFh\t;DebugB signature?\r\n\tjz @F\r\nadjmem:\r\n\tshr ecx,10        ;convert bytes to kB\r\n\tsub ds:[413h],cx\r\n\tshl ecx,10\r\n\tsub edi,ecx\r\n@@:\r\n\tmov esi,dwStartDbg\r\n\tcld\r\n\trep movsb\r\n\tor rmcode.RMCOMM.bFlags,1+2\r\nno_dbginit:\r\n\tmov ax,word ptr ds:[4+2]\r\n\tshr ax,6\t\t\t\t   ;para -> kB\r\n\tcmp ax,ds:[413h]\r\n\tjb @F\r\n\tor rmcode.RMCOMM.bFlags,2\r\n@@:\r\nendif\r\n\r\n;--- If fastboot is on, one cannot call v86-mode.\r\n;--- IVT vectors have been restored already, VDS bit has been cleared.\r\n;--- So copy 16-bit code to address 07e00h, setup stack and registers\r\n;--- EAX & ECX, finally jump to 7E00h ( still in protected-mode ).\r\n\r\n\tmov edi, 7E00h\r\n\tmov esi, offset rmcode\r\n\tmov ecx, sizermcode\r\n\tcld\r\n\trep movsb\r\nif BOOTSECT\r\n\tcmp rmcode.RMCOMM.bPartition,-1\r\n\tjnz @F\r\n\tmov edi, 7C00h\r\n\tmov esi, offset bsbuffer\r\n\tmov ecx, 512/4\r\n\trep movsd\r\n@@:\r\nendif\r\n\txor edx, edx\r\n\tpush edx\r\n\tpushw -1\r\n\tLIDT FWORD ptr [esp]\t; reset the IDT to 0:ffffh\r\n\tMOV AX,REAL_DATA_SEL\t; before returning to real-mode set the\r\n\tMOV DS,EAX\t\t\t\t; segment register caches\r\n\tMOV ES,EAX\r\n\tMOV FS,EAX\r\n\tMOV GS,EAX\r\n\tMOV SS,EAX\t\t\t\t; set SS:ESP\r\n\tMOV ESP,7C00h\r\n\tMOV ECX,CR0 \t\t\t; prepare to reset CR0 PE and PG bits\r\n\tAND ECX,7FFFFFFEH\r\n\tXOR EAX,EAX\r\n\tdb 66h, 0eah\t\t\t; jmp far16 20h:7E00h\r\n\tdw 7E00h, REAL_CODE_SEL\r\n\r\nctrlproc endp\r\n\r\ncputs proc uses esi\r\n\tmov esi, eax\r\n\tcld\r\nnextitem:\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EDX,al\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,2\r\n\tmov eax,21h\r\n\t@VMMCall Exec_Int\r\n\tjmp nextitem\r\ndone:\r\n\tret\r\ncputs endp\r\n\r\n;--- display usage\r\n\r\nhelpout proc uses ebp\r\n\t@VMMCall Get_Cur_VM_Handle\r\n\tmov ebp,[ebx].cb_s.CB_Client_Pointer\r\n\t@VMMCall Begin_Nest_Exec\t ;start nested execution\r\n\tmov eax,offset helptxt\r\n\tcall cputs\r\n\t@VMMCall End_Nest_Exec\t\t ;end nested execution\r\n\tret\r\nhelpout endp\r\n\r\nif LDDBG\r\n\r\nReleaseDbg proc uses esi\r\n\tmov ecx,dwSizeDbg\r\n\tjecxz exit\r\n\tadd ecx,4096-1\r\n\tshr ecx,12\r\n\tmov esi,dwStartDbg\r\n@@:\r\n\tpush ecx\r\n\tpush 0\r\n\tpush esi\r\n\t@VMMCall _PageFree\r\n\tadd esp,2*4\r\n\tpop ecx\r\n\tadd esi,1000h\r\n\tloop @B\r\nexit:\r\n\tmov dwSizeDbg,0\r\n\tmov dwStartDbg,0\r\n\tret\r\nReleaseDbg endp\r\n\r\nendif\r\n\r\nif LDI13EXT\r\n\r\nReleaseI13 proc uses esi\r\n\tmov ecx,dwSizeI13\r\n\tjecxz exit\r\n\tadd ecx,4096-1\r\n\tshr ecx,12\r\n\tmov esi,dwStartI13\r\n@@:\r\n\tpush ecx\r\n\tpush 0\r\n\tpush esi\r\n\t@VMMCall _PageFree\r\n\tadd esp,2*4\r\n\tpop ecx\r\n\tadd esi,1000h\r\n\tloop @B\r\nexit:\r\n\tmov dwSizeI13,0\r\n\tmov dwStartI13,0\r\n\tret\r\nReleaseI13 endp\r\n\r\nendif\r\n\r\nif LDDBG or LDI13EXT\r\n\r\n;--- v86 API ah=3/4: copy conv. memory block to extended memory\r\n;--- in: v86-ds:si=address\r\n;---     v86-ecx=size of region\r\n;--- out: NC if ok\r\n;---      eax=ext. memory address\r\n;---      ecx=size of region\r\n\r\nStoreFile proc uses esi edi\r\n\tmov eax, [ebp].Client_Reg_Struc.Client_ECX\r\n\tand eax, eax\r\n\tjz exit\r\n\tadd eax,4096-1\r\n\tshr eax,12\r\n\tmov esi,eax\r\n\r\n\tpush 0\r\n\tpush eax\r\n\tpush PR_SYSTEM\r\n\t@VMMCall _PageReserve\t ;allocate address space for debugger\r\n\tadd esp,3*4\r\n\tcmp eax,-1\r\n\tjz error\r\n\tmov edi, eax\r\n\r\n\tshr eax, 12 \t\t\t ;convert linear address to page number\r\n\tpush PC_INCR or PC_WRITEABLE\r\n\tpush 0\r\n\tpush PD_FIXED\t\t\t ;???\r\n\tpush esi\r\n\tpush eax\r\n\t@VMMCall _PageCommit\r\n\tadd esp,5*4\r\n\tand eax,eax\r\n\tjz error\r\n\r\n\tmovzx esi, word ptr [ebp].Client_Reg_Struc.Client_DS\r\n\tshl esi, 4\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_ESI\r\n\tadd esi, eax\r\n\tmov ecx,[ebp].Client_Reg_Struc.Client_ECX\r\n\tmov eax, edi\r\n\tpush ecx\r\n\trep movsb\r\n\tpop ecx\r\nexit:\r\n\tclc\r\n\tret\r\nerror:\r\n\tor [ebp].Client_Reg_Struc.Client_EFlags,1  ;set Carry flag\r\n\tstc\r\n\tret\r\nStoreFile endp\r\n\r\nendif\r\n\r\nif BOOTSECT\r\n\r\n\tinclude fileacc.inc\r\n\r\n;--- read \"boot sector\" file\r\n;--- in: esi = start of name in cmdline\r\n;--- out: esi = behind name\r\n;---      C if error occured\r\n\r\nReadBSFile proc uses ebx edi ebp\r\n\tpush ecx\r\n\t@VMMCall Get_Cur_VM_Handle\r\n\tmov ebp,[ebx].cb_s.CB_Client_Pointer\r\n\tpop ecx\r\n\t@VMMCall GetDOSBuffer\r\n\tmov edi,eax\r\n\r\n\t@VMMCall Begin_Nest_Exec\r\n\r\n\tmov edx,edi\r\n\tmov ecx,128\r\n@@:\r\n\tlodsb\r\n\tstosb\r\n\tcmp al,' '\r\n\tjbe @F\r\n\tloop @B\r\n\tinc esi\r\n@@:\r\n\tdec esi\r\n\tmov byte ptr [edi-1],0\r\n\tmov edi,edx\r\n\tor ebx,-1\r\n\tcall OpenFile\r\n\tjc exit\r\n\tmov ebx,eax\r\n\tcall GetFileSize\r\n\tjc exit\r\n\tcmp eax,512\r\n\tstc\r\n\tjnz exit\r\n\tmov edx,edi\r\n\tmov ecx,eax\r\n\tcall ReadFile\r\n\tjc exit\r\n\tcmp eax,512\r\n\tstc\r\n\tjnz exit\r\n\tpush esi\r\n\tmov esi, edi\r\n\tmov edi, offset bsbuffer\r\n\tmov ecx, 512/4\r\n\trep movsd\r\n\tpop esi\r\n\tclc\r\nexit:\r\n\tpushfd\r\n\tjnc @F\r\n\tmov eax,offset fileerror\r\n\tcall cputs\r\n@@:\r\n\tcmp ebx,-1\r\n\tjz @F\r\n\tcall CloseFile\r\n@@:\r\n\t@VMMCall End_Nest_Exec\r\n\tpopfd\r\n\tret\r\nReadBSFile endp\r\n\r\nendif\r\n\r\n;--- scan cmdline\r\n;--- options /P1 .. /P8\r\n;--- options /D0 .. /D7\r\n;--- option  /B:filename\r\n\r\nInit proc uses esi pComm:ptr\r\n\tmov esi,pComm\r\n\tmov esi,[esi].JLCOMM.lpCmdLine\r\n\tcld\r\n\t.while byte ptr [esi]\r\n\t\tlodsb\r\n\t\t.if al == '/' || al == '-'\r\n\t\t\tcall getoption\r\n\t\t\tjc error\r\n\t\t.elseif al == ' ' || al == 9\r\n\t\t\t.continue\r\n\t\t.else\r\n\t\t\t.break .if al == 13\t;if loaded as device driver\r\n\t\t\tcall helpout\r\n\t\t\tjmp error\r\n\t\t.endif\r\n\t.endw\r\n\tmov eax,1\r\n\tret\r\nerror:\r\n\txor eax,eax\r\n\tret\r\ngetoption:\r\n\tmov ax,[esi]\r\n\tor al,20h\r\n\t.if al == 'p' && ah >= '1' && ah <= '8'\r\n\t\tmov al,ah\r\n\t\tsub al,'0'\r\n\t\tmov rmcode.RMCOMM.bPartition,al\r\n\t\tadd esi,2\r\n\t.elseif al == 'd' && ah >= '0' && ah <= '7'\r\n\t\tmov al,ah\r\n\t\tsub al,'0'\r\n\t\tor al,80h\r\n\t\tmov rmcode.RMCOMM.bDisk,al\r\n\t\tadd esi,2\r\nif BOOTSECT\r\n\t.elseif al == 'b' && ah == ':'\r\n\t\tmov ecx, pComm\r\n\t\tadd esi,2\r\n\t\tinvoke ReadBSFile;may return with C=error\r\n\t\tmov rmcode.RMCOMM.bPartition,-1  ;disables boot sector load\r\nendif\r\n\t.else\r\n\t\tcall helpout\r\n\t\tstc\r\n\t.endif\r\n\tretn\r\nInit endp\r\n\r\nDeinit proc\r\nif LDDBG\r\n\tcall ReleaseDbg\r\nendif\r\nif LDI13EXT\r\n\tcall ReleaseI13\r\nendif\r\n\tmov eax,1\r\n\tret\r\nDeinit endp\r\n\r\nDllMain PROC stdcall hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n\tmov eax,dwReason\r\n\tcmp eax, DLL_PROCESS_ATTACH\r\n\tjnz @F\r\n\tinvoke Init, dwRes\r\n\tjmp exit\r\n@@:\r\n\tcmp eax,DLL_PROCESS_DETACH\r\n\tjnz @F\r\n\tcall Deinit\r\n@@:\r\nexit:\r\n\tret\r\nDllMain endp\r\n\r\n\tEND DllMain\r\n\r\n"
  },
  {
    "path": "JLM/REBOOT/FASTBOOT.txt",
    "content": "\r\n  About FastBoot JLM\r\n\r\n   The FastBoot JLM allows some fine-tuning of Jemm's fast reboot behavior\r\n  (which is activated by Jemm's FASTBOOT option). \r\n\r\n   DEVICE=JLOAD.EXE FASTBOOT.DLL [options]\r\n   \r\n   Valid options are:\r\n   /Dn    : select a HD to boot from (n=0-7; 0 is first HD and default).\r\n            This will work for the DOS variants that are able to boot from\r\n            any HD, without \"remapping\" them in the BIOS.\r\n   /Pn    : select a partition to boot from (n=1-8; if n > 4, the partition \r\n            will be searched in the (first) extended partition. If this option\r\n            is omitted, the FastBoot JLM will load & run the MBR's boot code.\r\n   /B:file: select a \"boot sector\" file to be used for booting. This file is\r\n            expected to have a size of 512 bytes and contain executable boot\r\n            code.\r\n\r\n   Additionally, the FastBoot JLM implements an API:\r\n   1. to load and activate the \"boot variant\" of Debug/X - DebugB. This allows\r\n      to debug the DOS kernel during initialization without modifying the boot\r\n      sector of the partition.\r\n   2. to load an \"INT 13h BIOS extension\". Sample LDI13Ext.asm is supplied,\r\n      showing how this is supposed to be done - it just switches HDs 0 and 1.\r\n\r\n   Jemm's FASTBOOT option is not guaranteed to work, please read the chapter\r\n  about the requirements in Jemm's documentation.\r\n\r\n"
  },
  {
    "path": "JLM/REBOOT/FASTBRM.ASM",
    "content": "\r\n;--- real-mode part of the fastboot JLM;\r\n;--- this code is moved to address 07E00h by the fastboot JLM;\r\n;--- on entry:\r\n;--- + cpu is still in protected-mode;\r\n;--- + CS, SS, DS, ES, FS, GS: 16-bit selector, limit 0ffffh, base 0\r\n;--- + ESP: 7C00h\r\n;--- + ECX: value for CR0\r\n;--- + EAX: 0\r\n;\r\n;--- To assemble use Masm v6+ or JWasm.\r\n;\r\n;--- To load a partition boot sector will work only if the\r\n;--- BIOS supports LBA access for the disk.\r\n\r\n\t.model tiny\r\n\r\nRESETDSK equ 0\t;1=reset disk via int 13h, ah=0\r\nBOOTDISK equ 80h;default boot HD\r\nBOOTPART equ 0\t;may be 1-8 to boot a partition or 0 to boot MBR\r\nBOOTADDR equ 7C00h ;address where mbr/boot sector will be loaded\r\nCHKJMPS  equ 0\t;1=check for \"jmp short\" at boot sector start\r\n\r\nifndef BOOTSECT\r\nBOOTSECT equ 1\t;1=support option /B:boot_sector_file\r\nendif\r\nifndef LDDBG\t;must match setting in fastboot.asm\r\nLDDBG equ 1\t\t;1=check for presence of DebugB and initialize it\r\nendif\r\nifndef LDI13EXT\t;must match setting in fastboot.asm\r\nLDI13EXT equ 1\t;1=initialize int13 extension\r\nendif\r\n\r\n@dbgout macro chr\r\nifdef _DEBUG\r\n\tmov al,chr\r\n\tcall printchr\r\nendif\r\nendm\r\n\r\n;--- needed if an extended partition has to be scanned.\r\nBDESC struct\r\nbBoot    db ?\r\nchsStart db ?,?,?   ;\r\nbType    db ?\r\nchsEnd   db ?,?,?\r\nlbaStart dd ?\r\nlbaSize  dd ?\r\nBDESC ends\r\n\r\n\t.code\r\n\r\n\torg 7E00h\r\n\r\n\t.386p\r\n\tmov cr0,ecx\t\t\t;enter real-mode\r\n\tdb 0EAh \t\t\t;jmp far16 0000:7Exx (set CS=0000)\r\n\tdw @F, 0\r\nbPartition db BOOTPART  ;offset 8 (contents may be changed by FASTBOOT.ASM!)\r\nbDisk      db BOOTDISK  ;offset 9 (contents may be changed by FASTBOOT.ASM!)\r\nif LDDBG or LDI13EXT\r\nbDbgFlags  db 0\t\t\t;bit 0:1=initialize DebugB; bit 1:1=stop in debugger\r\nendif\r\n@@:\r\n\tmov cr3,eax\r\n\t.386\r\n\tmov ss,ax\r\n\tmov ds,ax\r\n\tmov es,ax\r\n\tsti\r\nif LDI13EXT\r\n\ttest cs:[bDbgFlags],4\r\n\tjz @F\r\n\tdb 9ah\t\t\t;call 0800h:0000 to init int13 extension\r\n\tdw 0,800h\r\n@@:\r\nendif\r\nif LDDBG\r\n\ttest cs:[bDbgFlags],1\r\n\tjz @F\r\n\t@dbgout 'D'\r\n\tmov ax, ds:[413h]\r\n\tshl ax, 6   ;kB -> para\r\n\tpush cs\r\n\tpush offset @F\r\n\tpush ax\t\t\t;call DebugB init proc\r\n\tpush 0\r\n\tretf\r\n@@:\r\nendif\r\n\t@dbgout 'R'\r\nif RESETDSK\r\n\tmov dl,cs:[bDisk]\r\n\tmov ah,00 \t\t\t;disk reset\r\n\tint 13h\r\nendif\r\n\t@dbgout 'B'\r\n\r\n\tmov cx,0001h\t\t;CX+DH=cyl/head/sector\r\n\tmovzx dx,cs:[bDisk]\r\n\tmov bx,BOOTADDR\t\t;ES:BX=transfer address\r\n\tpush es\r\n\tpush bx\t\t\t\t;push address for RETF below\r\nif BOOTSECT\r\n\tcmp cs:[bPartition],-1  ;boot sector loaded as file?\r\n\tjz bsect_read           ;then it has been moved to 7C00h already\r\nendif\r\n\tmov ax,201h\t\t\t;read first sector of HD\r\n\tint 13h\r\n\tjc err\r\n\t@dbgout 'T'\r\n\tmov al,cs:[bPartition]\r\n\tand al,al\r\n\tjz bsect_read\r\n\tdec al\t\t\t\t;1..8 -> 0..7\r\n\tmov ah,0\r\n\tcmp al,4\t\t\t;0..3?\r\n\tjb stdpart\r\n\r\n;--- partitions 5-8 are searched in (the first and only) extended partition\r\n\r\n\txor edi,edi\r\n\tsub al,4-1\t\t\t;4-7 -> 1-4\r\nnextext:\r\n\tpush ax\r\n\tmov si,0\r\n@@:\r\n\tcmp byte ptr [si+BOOTADDR+1BEh].BDESC.bType, 5\t;extended partition?\r\n\tjz @F\r\n\tcmp byte ptr [si+BOOTADDR+1BEh].BDESC.bType, 15\t;extended partition, LBA?\r\n\tjz @F\r\n\tadd si,sizeof BDESC\r\n\tcmp si,4*sizeof BDESC\r\n\tjb @B\r\n\tjmp err\t\t\t\t;no extended partition found\r\n@@:\r\n\tand edi,edi\r\n\tjnz @F\r\n\tmov edi,[si+BOOTADDR+1BEh].BDESC.lbaStart\r\n\tjmp isfirst\r\n@@:\r\n\tadd [si+BOOTADDR+1BEh].BDESC.lbaStart,edi\r\nisfirst:\r\n\tpush [si+BOOTADDR+1BEh].BDESC.lbaStart\r\n\tpush si\r\n\tcall readsect\r\n\tpop si\r\n\tpop ebx\r\n\tjc err\r\n\tcmp word ptr ds:[BOOTADDR+1FEh],0AA55h\r\n\tjnz err\r\n\tpop ax\r\n\tdec al\r\n\tjnz nextext\r\n;--- bootable partitions in extended partitions are supposed to be the first entry!\r\n\tadd ds:[BOOTADDR+1BEh+BDESC.lbaStart],ebx\r\nstdpart:\r\n\tshl ax,4\r\n\tmov si,ax\r\n\tcall readsect\r\n\tjc err\r\n\tcmp word ptr ds:[BOOTADDR+1FEh],0AA55h\r\n\tjnz err\r\nif CHKJMPS\r\n\tcmp byte ptr ds:[BOOTADDR],0EBh\t;sector starts with a short jmp?\r\n\tjnz err\r\nendif\r\n\t@dbgout 'p'\r\nbsect_read:\r\nif LDDBG\r\n\ttest cs:[bDbgFlags],2\r\n\tjz @F\r\n\tint 3\r\n@@:\r\nendif\r\n\tretf\t\t\t\t;jump to boot code\r\nerr:\r\n\tcall errout\r\n\tdb 7,\"err\",13,10\r\n\tjmp $\t\t\t\t;stop\r\n\r\nreadsect:\r\n;--- si = 0,10h,20h,30h\r\n\tmov bx,055AAh\r\n\tmov ah,41h\t\t\t;check for int 13h extensions\r\n\tint 13h\r\n\tjc readsectchs1\r\n\tcmp bx,0AA55h\r\n\tjnz readsectchs1\r\n\tcmp [si+BOOTADDR+1BEh].BDESC.bType,7\r\n\tjb readsectchs    \r\n\tcmp [si+BOOTADDR+1BEh].BDESC.bType,0Bh\t;FAT32 CHS?\r\n\tjz readsectchs    \r\nreadsectlba:\r\n;--- create a \"disk address packet\" onto stack\r\n\tpushd 0\r\n\tpush [si+BOOTADDR+1BEh].BDESC.lbaStart\t;LBA sector# (start of partition)\r\n\tpush 0\r\n\tpush BOOTADDR;transfer buffer (0000:7C00)\r\n\tpush 1\t\t;sectors\r\n\tpush 10h\t;size of packet\r\n\tmov si,sp\r\n\tmov dl,cs:[bDisk]\r\n\tmov ah,42h\r\n\tint 13h\r\n\tlea sp,[si+10h]\r\n\tret\r\nreadsectchs:\r\n;--- logical partitions inside extended partitions usually have type 5, although LBA must be used!\r\n\tcmp byte ptr [si+BOOTADDR+1BEh].BDESC.chsStart+2,0ffh\r\n\tjnz @F\r\n\tcmp word ptr [si+BOOTADDR+1BEh].BDESC.chsStart+0,0fffeh\r\n\tjnc readsectlba\r\n@@:\r\nreadsectchs1:\r\n\tmov bx,BOOTADDR\r\n\tmov dh,[si+BOOTADDR+1BEh].BDESC.chsStart+0\r\n\tmov cx,word ptr [si+BOOTADDR+1BEh].BDESC.chsStart+1\r\n\tmov ax,201h\r\n\tint 13h\r\n\tret\r\n\r\nerrout:\r\n\tpop si\r\n\tcld\r\n@@:\r\n\tlodsb\r\n\tpush ax\r\n\tcall printchr\r\n\tpop ax\r\n\tcmp al,10\r\n\tjnz @B\r\n\tjmp si\r\nprintchr:\r\n\tpush bx\r\n\tmov bh,0\r\n\tmov ah,0Eh\r\n\tint 10h\r\n\tpop bx\r\n\tret\r\n\tend\r\n"
  },
  {
    "path": "JLM/REBOOT/FBOOT.ASM",
    "content": "\r\n;--- Set /D and /P option for currently loaded fastboot JLM.\r\n;--- This program uses the API installed by FASTBOOT.DLL;\r\n;--- alternatively, one can unload & reload FASTBOOT.DLL.\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.dosseg\r\n\t.stack 2048\r\n\toption casemap:none\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nDStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,'$'\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n\r\ndopt db -1\r\npopt db -1\r\n\r\n\t.const\r\nhelptxt label byte\r\n\tdb \"FBOOT: set FASTBOOT arguments.\",13,10\r\n\tdb \"Usage: FBOOT [/Dn] [/Pm]\",13,10\r\n\tdb \" /Dn: set disk to boot from to n [0-9]\",13,10\r\n\tdb \" /Pm: set partition to boot from to m [0-9]\",13,10\r\n\tdb '$'\r\n\r\n\t.code\r\n\r\ngetoption proc\r\n\tcld\r\n\tmov si,81h\r\nnextchr:\r\n\tlodsb es:[si]\r\n\tcmp al,13\r\n\tjz cmdl_done\r\n\tcmp al,20h\r\n\tjbe nextchr\r\n\tcmp al,'/'\r\n\tjz is_option\r\n\tcmp al,'-'\r\n\tjnz disp_help\r\nis_option:\r\n\tlodsb es:[si]\r\n\tcmp al,13\r\n\tjz disp_help\r\n\tor al,20h\r\n\tcmp al,'d'\r\n\tjz d_option\r\n\tcmp al,'p'\r\n\tjz p_option\r\n\tcmp al,'b'\r\n\tjz b_option\r\n\tjmp disp_help\r\ncmdl_done:\r\n\tclc\r\n\tret\r\nd_option:\r\n\tlodsb es:[si]\r\n\tcmp al,'0'\r\n\tjb disp_help\r\n\tcmp al,'9'\r\n\tja disp_help\r\n\tsub al,'0'\r\n\tmov dopt,al\r\n\tjmp nextchr\r\np_option:\r\n\tlodsb es:[si]\r\n\tcmp al,'0'\r\n\tjb disp_help\r\n\tcmp al,'9'\r\n\tja disp_help\r\n\tsub al,'0'\r\n\tmov popt,al\r\n\tjmp nextchr\r\nb_option:\r\n\tmov dx,DStr(\"not implemented yet\",13,10)\r\n\tmov ah,9\r\n\tint 21h\r\n\tjmp nextchr\r\ndisp_help:\r\n\tmov dx,offset helptxt\r\n\tmov ah,9\r\n\tint 21h\r\n\tstc\r\n\tret\r\ngetoption endp\r\n\r\nmain proc\r\n\r\nlocal dwFB:dword\r\n\r\n\tcall getoption\r\n\tjc exit\r\n\tmov bx,4435h\t;FASTBOOT/REBOOT device ID\r\n\tmov ax,1684h\t;get API entry point\r\n\tint 2Fh\r\n\tcmp al,0\r\n\tjnz not_installed\r\n\tmov word ptr dwFB+0,di\r\n\tmov word ptr dwFB+2,es\r\n\tmov ah,0\t\t;get version\r\n\tcall dwFB\r\n\tjc not_installed\r\n\ttest ah,1\t\t;FASTBOOT variant?\r\n\tjz not_installed\r\n\r\n\tmov al,dopt\r\n\tcmp al,-1\r\n\tjz no_dopt\r\n\tmov ah,1\r\n\tcall dwFB\r\n\tjc fb_call_failed\r\n\tmov dx,DStr(\"setting HD succeeded\",13,10)\r\n\tmov ah,9\r\n\tint 21h\r\nno_dopt:\r\n\tmov al,popt\r\n\tcmp al,-1\r\n\tjz no_popt\r\n\tmov ah,2\r\n\tcall dwFB\r\n\tjc fb_call_failed\r\n\tmov dx,DStr(\"setting partition succeeded\",13,10)\r\n\tmov ah,9\r\n\tint 21h\r\nno_popt:\r\nexit:\r\n\tret\r\n\r\nnot_installed:\r\n\tmov dx,DStr(\"FASTBOOT not installed or wrong version\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tcall lfout\r\n\tret\r\nfb_call_failed:\r\n\tmov dx,DStr(\"calling FASTBOOT failed\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tcall lfout\r\n\tret\r\nmain endp\r\n\r\nlfout proc\r\n\tmov dx,DStr(13,10)\r\n\tmov ah,9\r\n\tint 21h\r\n\tret\r\nlfout endp\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov dx,ss\r\n\tsub dx,ax\r\n\tshl dx,4\r\n\tmov ss,ax\r\n\tadd sp,dx\r\n\tmov bx,sp\r\n\tshr bx,4\r\n\tmov cx,es\r\n\tsub ax,cx\r\n\tadd bx,ax\r\n\tmov ah,4Ah\r\n\tint 21h\r\n\tcall main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tend start\r\n\r\n"
  },
  {
    "path": "JLM/REBOOT/LDI13EXT.ASM",
    "content": "\r\n;--- sample how to load an int 13h extension into FASTBOOT JLM.\r\n;--- this sample just swaps HDs 0 and 1 in int 13h; int 41h/46h are NOT modified.\r\n;--- assemble: JWasm -mz LDI13EXT.asm\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.dosseg\r\n\t.stack 2048\r\n\toption casemap:none\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nDStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,'$'\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nIRETS struct\r\nwIP\tdw ?\r\nwCS\tdw ?\r\nwFL\tdw ?\r\nIRETS ends\r\n\r\n\t.const\r\nhelptxt label byte\r\n\tdb \"LDI13EXT: sample how to load an int 13h extension to FASTBOOT JLM.\",13,10\r\n\tdb \"Required:\",13,10\r\n\tdb \" - Jemm v5.86+\",13,10\r\n\tdb \" - Jemm loaded with option FASTBOOT\",13,10\r\n\tdb \" - FASTBOOT.DLL be loaded\",13,10\r\n\tdb '$'\r\n\r\n\t.code\r\n\r\n;--- int 13h extension initialization\r\n;--- assumes: CS=_TEXT, DS=0000\r\n\r\nint13ext proc\r\n\r\n\tpush es\r\n\tpusha\r\n\tmov eax,ds:[13h*4]\r\n\tmov cs:[oldi13a],eax\r\n\tmov cs:[oldi13b],eax\r\n\tsub word ptr ds:[413h],1\t;decrease conv. memory by 1 kB\r\n\tmov ax,ds:[413h]\r\n\tshl ax,6\r\n\tmov es,ax\r\n\tshl eax,16\r\n\txor di,di\r\n\tmov si,offset myint13\r\n\tmov cx,size_int13ext2\r\n\tcld\r\n\trep movsb es:[di],cs:[si]\r\n\tmov ds:[13h*4],eax\r\n\tpopa\r\n\tpop es\r\n\tretf\r\n\r\n;--- the new int 13h code\r\n\r\nmyint13:\r\n\tcmp dl,80h\t;HD 0?\r\n\tjz is_hd0\r\n\tcmp dl,81h\t;HD 1?\r\n\tjz is_hd1\r\njmp_oldvec:\r\n\tdb 0eah\t\t;opcode jmp ssss:oooo\r\noldi13a dd 0\r\nis_hd0:\r\nis_hd1:\r\n\txor dl,1\t; swap disks\r\n\tcmp ah,8\t; ah=8 is special because DL returns # of HDs\r\n\tjz jmp_oldvec\r\n\tpushf\r\n\tdb 09ah\t\t;opcode call ssss:oooo\r\noldi13b dd 0\r\n\tpush bp\r\n\tmov bp,sp\r\n\tpush ax\r\n\tpushf\r\n\txor dl,1\t; restore DL\r\n\tpop ax\r\n\tmov byte ptr [bp+2].IRETS.wFL,al\r\n\tpop ax\r\n\tpop bp\r\n\tiret\r\nsize_int13ext equ $ - int13ext\r\nsize_int13ext2 equ $ - myint13\r\nint13ext endp\r\n\r\nmain proc\r\n\r\nlocal dwFB:dword\r\n\r\n\tcld\r\n\tmov si,81h\r\nnextchr:\r\n\tlodsb es:[si]\r\n\tcmp al,13\r\n\tjz cmdl_done\r\n\tcmp al,20h\r\n\tjbe nextchr\r\n\tmov dx,offset helptxt\r\n\tmov ah,9\r\n\tint 21h\r\n\tret\r\ncmdl_done:\r\n\tmov bx,4435h\t;FASTBOOT/REBOOT device ID\r\n\tmov ax,1684h\t;get API entry point\r\n\tint 2Fh\r\n\tcmp al,0\r\n\tjnz not_installed\r\n\tmov word ptr dwFB+0,di\r\n\tmov word ptr dwFB+2,es\r\n\tmov ah,0\t\t;get version\r\n\tcall dwFB\r\n\tjc not_installed\r\n\ttest ah,1\t\t;FASTBOOT variant?\r\n\tjz not_installed\r\n\r\n;--- test that at least 2 HDs are present\r\n\r\n\tpush ds\r\n\tmov ax,40h\r\n\tmov ds,ax\r\n\tmov al,ds:[75h]\r\n\tpop ds\r\n\tcmp al,2\r\n\tjb no_hds\r\n\r\n;--- FASTBOOT API AH=4: DS:SI=start extension, ECX=size extension in bytes\r\n\r\n\tmov si,offset int13ext\r\n\tmov ecx, size_int13ext\r\n\tmov ah,4\r\n\tcall dwFB\r\n\tjc no_int13ext\r\n\tmov dx,DStr(\"int 13h extension loaded\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tcall lfout\r\n\tret\r\n\r\nnot_installed:\r\n\tmov dx,DStr(\"FASTBOOT not installed\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tcall lfout\r\n\tret\r\nno_hds:\r\n\tmov dx,DStr(\"at least 2 HDs are needed\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tcall lfout\r\n\tret\r\nno_int13ext:\r\n\tmov dx,DStr(\"int 13h extension not accepted\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tcall lfout\r\n\tret\r\nmain endp\r\n\r\nlfout proc\r\n\tmov dx,DStr(13,10)\r\n\tmov ah,9\r\n\tint 21h\r\n\tret\r\nlfout endp\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov dx,ss\r\n\tsub dx,ax\r\n\tshl dx,4\r\n\tmov ss,ax\r\n\tadd sp,dx\r\n\tmov bx,sp\r\n\tshr bx,4\r\n\tmov cx,es\r\n\tsub ax,cx\r\n\tadd bx,ax\r\n\tmov ah,4Ah\r\n\tint 21h\r\n\tcall main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tend start\r\n\r\n"
  },
  {
    "path": "JLM/REBOOT/MAKE.BAT",
    "content": "@echo off\r\njwasm -nologo -pe  -Fl -Fo=REBOOT.DLL -I..\\..\\Include REBOOT.ASM \r\njwasm -nologo -bin -Fl FASTBRM.ASM \r\njwasm -nologo -pe  -Fl -Fo=FASTBOOT.DLL -I..\\..\\Include FASTBOOT.ASM \r\n"
  },
  {
    "path": "JLM/REBOOT/REBOOT.ASM",
    "content": "\r\n;--- sample how to handle DEVICE_REBOOT_NOTIFY control message\r\n;--- requires Jemm v5.86+\r\n\r\n\t.386\r\n\t.MODEL FLAT, stdcall\r\n\toption casemap:none\r\n\r\n\tinclude jlm.inc\r\n\r\nDLL_PROCESS_ATTACH equ 1\r\nDLL_PROCESS_DETACH equ 0   \r\n\r\nDEVICE_ID equ 4435h\r\n\r\nifdef @pe_file_flags\t;-pe option set?\r\n\toption dotname\r\n.drectve segment info\r\n\tdb \"-dll \"\r\n\tdb \"-subsystem:native \"\r\n\tdb \"-fixed:no \"\r\n.drectve ends\r\n\r\n.hdr$2 segment flat\r\n\tdb \"PX\"\r\n.hdr$2 ends\r\nendif\r\n\r\n\t.const\r\n\r\n\tpublic export ddb\r\n\r\nddb VxD_Desc_Block <0,0,DEVICE_ID,1,0,0,\"REBOOT\",0, ctrlproc >\r\n\r\nstring1 db 13,10,\"rebooting - press a key...\",0\r\n\r\n\t.CODE\r\n\r\nctrlproc proc\r\n\tcmp eax, DEVICE_REBOOT_NOTIFY\r\n\tjz reboot_notify\r\n\tclc\r\n\tret\r\nreboot_notify:\r\n\t@VMMCall Begin_Nest_Exec\t ;start nested execution\r\n\tmov eax,offset string1\r\n\tcall printstring\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 0\r\n\tmov eax,16h\r\n\t@VMMCall Exec_Int\r\n\t@VMMCall End_Nest_Exec\t\t ;end nested execution\r\n\tclc\r\n\tret\r\nctrlproc endp\r\n\r\nprintstring proc uses esi\r\n\tmov esi, eax\r\n\tcld\r\nnextitem:\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 0Eh\r\n\tmov eax,10h\r\n\t@VMMCall Exec_Int\r\n\tjmp nextitem\r\ndone:\r\n\tret\r\nprintstring endp\r\n\r\nDllMain PROC stdcall hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n\tmov eax,dwReason\r\n\tcmp eax, DLL_PROCESS_ATTACH\r\n\tjnz @F\r\n\tmov eax,1\r\n\tjmp exit\r\n@@:\r\n\tcmp eax,DLL_PROCESS_DETACH\r\n\tjnz @F\r\n\tmov eax,1\r\n@@:\r\nexit:\r\n\tret\r\nDllMain endp\r\n\r\n\tEND DllMain\r\n\r\n"
  },
  {
    "path": "JLM/REBOOT/REBOOT.txt",
    "content": "\r\n  About ReBoot\r\n\r\n  Reboot is just a sample, supposed to be modified if Jemm's default\r\n  way to reboot doesn't work on a machine. To load, add the following line\r\n  to CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE REBOOT.DLL\r\n\r\n"
  },
  {
    "path": "JLM/XCDROM32/History.txt",
    "content": "\r\n  History\r\n\r\n  10/2022: v1.5\r\n   - fixed: the driver was unable to handle multiple controllers with\r\n     identical class/subclass/interface values.\r\n   - enable busmaster feature in PCI controller if not set (Qemu).\r\n   - timeout for startup audio functions increased to 7 secs.\r\n   - removed obsolete /P option.\r\n   - SATA controllers in non-AHCI mode handled correctly.\r\n\r\n  06/20/2012: v1.4\r\n   - function \"get q-channel info\" did return track number in binary.\r\n   - function \"get q-channel info\" did return value of ADR as CONTROL \r\n     and vice versa.\r\n   - function \"get audio status\" did return start and end address as\r\n     LBA sector numbers.\r\n\r\n  05/21/2012: v1.3\r\n   - scan for native (=SATA) controllers didn't succeed usually.\r\n\r\n  12/28/2007: v1.2\r\n   - /P, /W and /32 options added, /UF option changed to /F\r\n   - Jemm's VDS DMA buffer used if user buffer cannot be used.\r\n   - IDE/DMA ports are now displayed for each unit.\r\n\r\n  12/03/2007: v1.1\r\n   - now both \"legacy\" and \"native\" IDE controllers are supported.\r\n   - some bugfixes.\r\n\r\n  05/24/2007: v1.0\r\n   - initial. Ported from XCDROM v1.9.\r\n\r\n"
  },
  {
    "path": "JLM/XCDROM32/MAKE.BAT",
    "content": "@echo off\r\nrem add -D_DEBUG to create a debug version\r\njwasm -coff -nologo -Fl -I..\\..\\Include XCDROM32.ASM\r\njwlink format win pe hx dll ru native file XCDROM32.obj name XCDROM32.DLL op q,map\r\n"
  },
  {
    "path": "JLM/XCDROM32/MAKEM.BAT",
    "content": "@echo off\r\nrem add -D_DEBUG to create a debug version\r\nrem using MS tools\r\nml -c -coff -Fl -I..\\..\\Include XCDROM32.ASM\r\nlink XCDROM32.OBJ /NOLOGO /DLL /out:XCDROM32.DLL /map:XCDROM32.map /subsystem:native /Fixed:no\r\n"
  },
  {
    "path": "JLM/XCDROM32/XCDROM32.ASM",
    "content": ";\r\n; XCDROM32.ASM  - a JLM driver for  UltraDMA CD-ROMs/DVDs\r\n; based on XCDROM v1.9 by Jack R. Ellis\r\n; released under the GNU GPL license v2 (see GNU_GPL.TXT for details)\r\n;\r\n; The source is to be assembled with JWasm or Masm v6+!\r\n;\r\n; XCDROM32 switch options are as follows:\r\n;\r\n;  /32   use 32bit INSD instead of INSW in PIO mode.\r\n;\r\n;  /AX   Excludes ALL audio functions. This makes the driver report\r\n;        on a Device-Status request that it reads DATA tracks only!\r\n;        UltraDMA, dual-drives, and other driver features are NOT\r\n;        affected!\r\n;\r\n;  /D:   Specifies the desired \"device name\" which SHCDX33 or MSCDEX\r\n;        will use during their initialization to address the CD-ROM\r\n;        drives.   Examples are:  /D:CDROM1  /D:MYCDROM  etc.   The\r\n;        device name must be from 1 to 8 bytes valid for use in DOS\r\n;        filenames.   If /D: is omitted, or the \"device name\" after\r\n;        a /D: is missing or invalid, \"XCDROM$$\" will be the default.\r\n;\r\n;  /L    Limits UltraDMA to \"low memory\" below 640K. /L is REQUIRED\r\n;        if upper memory blocks supplied by UMBPCI or a similar driver\r\n;        cannot do UltraDMA. /L causes I-O requests above 640K to use\r\n;        the VDS DMA buffer or, if none is available, to use \"PIO mode\"\r\n;        input.   Note that /L will be IGNORED if /UX is also given.\r\n;\r\n;  /Mn   Specifies the MAXIMUM UltraDMA \"mode\" to be set for a CD-ROM\r\n;        drive, where  n  is a number between 0 and 7, as follows:\r\n;            0 = ATA-16, 16 MB/sec.\r\n;            1 = ATA-25, 25 MB/sec.\r\n;            2 = ATA-33. 33 MB/sec.\r\n;            ...\r\n;            7 = ATA-166. 166 MB/sec.\r\n;        A CD-ROM drive designed to use \"modes\" LESS than the given\r\n;        value will be limited to its own highest \"mode\".   /M will\r\n;        be IGNORED for CD-ROM drives which cannot do UltraDMA, and\r\n;        it will be ignored for ALL drives if /UX is also given.\r\n\r\n;\r\n;  /Q    Quiet mode.\r\n;\r\n;  /F    \"Fast\" mode. Data input requests that cross an\r\n;        UltraDMA \"64K boundary\" are executed using a 2-element DMA\r\n;        command list, one for data up to the boundary, and one for\r\n;        data beyond it.   This might increase CD-ROM speed.\r\n;        \"Buffered\" or \"PIO mode\" input is still needed for user \r\n;        buffers that are misaligned (not at an even 4-byte address).\r\n;        /F will be IGNORED for CD-ROM drives which cannot do DMA.\r\n;\r\n;        --- NOTE ---\r\n;        Despite any UltraDMA specs, NOT ALL chipsets or mainboards\r\n;        can run multi-element DMA commands properly!   Although it\r\n;        is valuable, /F must be TESTED on every system, and it\r\n;        should be enabled with CARE!!\r\n;\r\n;  /UX   Disables ALL UltraDMA, even for CD-ROM drives capable of it.\r\n;        The driver then uses \"PIO mode\" for all data input.    /UX\r\n;        should be needed only for tests and diagnostic work.\r\n;\r\n;  /W    accept (non-UDMA) devices which can do multiword-DMA.\r\n;\r\n; For each switch, a dash may replace the slash, and lower-case letters\r\n; may be used.\r\n\r\n        .386\r\n        .model flat\r\n        option casemap:none\r\n        option proc:private\r\n\r\n        include jlm.inc\r\n\r\n?EXTDISP equ 0 ; extended displays for error trapping\r\n;\r\n; General Program Equations.\r\n;\r\n\r\nNUMCTRL     equ 3       ;max # of EIDE controllers\r\nNUMDSK      equ 3       ;max # of CD/DVD devices\r\nSETMODE     equ 1       ;support /M option, set UDMA mode\r\nSETBM       equ 1       ;test Busmaster support in \"class\"\r\nMWDMA       equ 1       ;support /W option to accept multi-word DMA\r\nDWRDIO      equ 1       ;support /32 option (PIO DWORD IO)\r\nSAVESTAT    equ 1       ;call save/restore \"client reg struct\" on init\r\nIDENTPKT    equ 1       ;1=issue \"identify packet\" cmd, 0=normal \"identify\"\r\n\r\nVER     equ <'V1.5, 06.10.2022'>    ;Driver version number and date.\r\nMSELECT equ 0A0h        ;\"Master\" device-select bits.\r\nSSELECT equ 0B0h        ;\"Slave\"  device-select bits.\r\nCOOKSL  equ 2048        ;CD-ROM \"cooked\" sector length.\r\nRAWSL   equ 2352        ;CD-ROM \"raw\" sector length.\r\nCMDTO   equ 00Ah        ;500-msec minimum command timeout.\r\nSEEKTO  equ 025h        ;2-second minimum \"seek\"  timeout.\r\n;STARTTO equ 049h        ;4-second minimum startup timeout.\r\nSTARTTO equ 07Fh        ;7-second minimum startup timeout.\r\nBIOSTMR equ 0046Ch      ;BIOS \"tick\" timer address.\r\n;VDSFLAG equ 0047Bh      ;BIOS \"Virtual DMA\" flag address.\r\n;IXM     equ 2048        ;IOCTL transfer-length multiplier.\r\nCR      equ 00Dh        ;ASCII carriage-return.\r\nLF      equ 00Ah        ;ASCII line-feed.\r\nTAB     equ 009h        ;ASCII \"tab\".\r\n\r\nREQOFS   equ 16h        ; must be at least \"sizeof DOSDRV\"\r\n\r\n\r\n@byte   equ <byte ptr>\r\n@word   equ <word ptr>\r\n@dword  equ <dword ptr>\r\n;\r\n; IDE Controller Register Definitions.\r\n;\r\nCDATA   equ 0           ;Data port.\r\nCSECCT  equ 2           ;offset port for I-O sector count.\r\nCDSEL   equ 6           ;offset port Drive-select and upper LBA.\r\nCCMD    equ 7           ;offset port Command register (write)\r\nCSTAT   equ 7           ;Primary status register (read)\r\n;\r\n; Controller Status and Command Definitions.\r\n;\r\nBSY     equ 080h        ;IDE controller is busy.\r\nDRQ     equ 008h        ;IDE data request.\r\nERR     equ 001h        ;IDE general error flag.\r\n\r\nDMI     equ 004h        ;DMA interrupt occured.\r\nDME     equ 002h        ;DMA error occurred.\r\nSETM    equ 003h        ;Set Mode subcommand.\r\nSETF    equ 0EFh        ;Set Features command.\r\nLBABITS equ 0E0h        ;Fixed LBA command bits.\r\n\r\n;--- structures\r\n\r\nDOSDRV struct\r\n\t\tdd ?\r\nwAttr\tdw ?\r\nofsStr\tdw ?\r\nofsInt\tdw ?\r\nname_\tdb 8 dup (?);+10\r\nwRes1\tdw ?\t\t;+18 req. for cd drivers, init with 0\r\nbRes2\tdb ?\t\t;+20 req. for cd drivers, modified by mscdex\r\nbUnits\tdb ?\t\t;+21 set by driver\r\nDOSDRV ends\r\n\r\n;\r\n; DOS \"Request Packet\" header layout.\r\n;\r\nRPH     struc\r\nbHLen   db  ?       ;+0 Header byte count.\r\nbSubU   db  ?       ;+1 Subunit number.\r\nbOp     db  ?       ;+2 Command code.\r\nwStat   dw  ?       ;+3 Status field.\r\n        db 8 dup (?);(Unused by us).\r\nRPH     ends\r\n\r\n;--- wStat values\r\nRPERR   equ 08003h      ;Packet \"error\" flags.\r\nRPDON   equ 00100h      ;Packet \"done\" flag.\r\nRPBUSY  equ 00200h      ;Packet \"busy\" flag.\r\n\r\n;--- Init \"Request Packet\" Layout.\r\n\r\nRPINIT  struc\r\n        RPH <> \r\nbUnit   db  ?       ;+13 Number of units found.\r\nRPSize  dw  ?       ;+14 Resident driver offset\r\n        dw  ?\r\ndwCL    dd  ?       ;Command-line data pointer.\r\nRPINIT  ends\r\n\r\n;\r\n; IOCTL \"Request Packet\" Layout.\r\n;\r\nIOC struc\r\n        RPH <>\r\n        db  ?       ;+13 Media descriptor byte (Unused by us).\r\nIOCAdr  dd  ?       ;+14 Data-transfer address.\r\nIOCLen  dw  ?       ;+18 Data-transfer length.\r\n        dw  ?       ;+20 Starting sector (unused by us).\r\n        dd  ?       ;+22 Volume I.D. pointer (unused by us).\r\nIOC ends\r\n;\r\n; Read Long \"Request Packet\" Layout.\r\n;\r\nRL struc\r\n        RPH <>\r\nRLAM    db  ?       ;+13 Addressing mode.\r\nRLAddr  dd  ?       ;+14 Data-transfer address.\r\nRLSC    dw  ?       ;+18 Data-transfer sector count.\r\nRLSec   dd  ?       ;+20 Starting sector number.\r\nRLDM    db  ?       ;+24 Data-transfer mode.\r\nRLIntlv db  ?       ;+25 Interleave size.\r\nRLISkip db  ?       ;+26 Interleave skip factor.\r\nRL  ends\r\n\r\n;\r\n; Play \"Request Packet\" Layout\r\n;\r\nRPPlay struc\r\n        RPH <>\r\nbMode   db  ?       ;+13 Addressing mode.\r\ndwStart dd  ?       ;+14 Data-transfer address.\r\ndwSize  dd  ?       ;+18 Data-transfer sector count.\r\nRPPlay ends\r\n\r\nIDEPARM struc\r\nwIDEBase    dw ?\r\nwDMABase    dw ?\r\nbDevSel     db ?\r\n            db ?\r\nIDEPARM ends\r\n\r\nAUDADR struct\r\ndwAudSta dd ?      ;Current audio-start address - LBA?\r\ndwAudEnd dd ?      ;Current audio-end   address - LBA?\r\ndwLSSta  dd ?      ;Last-session starting LBA.\r\nAUDADR ends\r\n\r\n;--- Unit Parameter (first 3 fields must match IDEPARM)\r\n\r\nUPARM  struc\r\nwIDEBase    dw ?    ;IDE address   (set by Init).\r\nwDMABase    dw ?    ;DMA address   (set by Init).\r\nbDevSel     db ?    ;Device-select (set by Init).\r\n            dw ?    ;not used\r\n            db ?    ;Media-change flag (one byte below dwAudSta).\r\n            AUDADR <>\r\nUPARM ends\r\n\r\n;--- macros\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.code text$01\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n@dprintf macro text,args:vararg\r\nlocal sym\r\nifdef _DEBUG\r\n\tifnb <args>\r\n\t\tinvoke printf, CStr(text), args\r\n\telse\r\n\t\tinvoke printf, CStr(text)\r\n\tendif\r\nendif\r\nendm\r\n\r\n    .data\r\n\r\npRequest    dd 0    ;linear address where request header is stored\r\ndwRequest   dd 0    ;linear address request header\r\ndwBase      dd 0    ;linear address driver base\r\ndwCmdLine   dd 0\r\n;\r\n; Main I-O Variables\r\n;\r\nBufAdr  dd  0       ;buffer linear address\r\nBufLng  dd  0       ;buffer length\r\nXFRAdr  dd  0       ;I-O data transfer address.\r\nXFRLng  dd  0       ;I-O data transfer length.\r\n;\r\n; DMA Variables.\r\n;\r\nDmaAdr  dd  0           ;1st physical address for DMA\r\nDmaLng  dd  0           ;1st DMA byte count\r\nDmaAdr2 dd  0           ;2nd physical address\r\nDmaLng2 dw  0           ;2nd DMA byte count LoWord\r\n        dw  8000h       ;2nd DMA byte count HiWord (fix)\r\nPRDAd   dd  0           ;PRD 32-bit command addr. (Init set).\r\n        align 4\r\n\r\n;\r\n; ATAPI \"Packet\" Area (always 12 bytes for a CD-ROM).\r\n; packet commands used by this program:\r\n; - 01Bh:  start/stop unit\r\n; - 01Eh:  prevent/allow media removal\r\n; - 025h:  read capacity\r\n; - 028h:  read (cooked)\r\n; - 02Bh:  seek\r\n; - 042h:  read sub-channel\r\n; - 043h:  read TOC\r\n; - 047h:  play audio MSF\r\n; - 04Bh:  pause/resume\r\n; - 05Ah:  mode sense\r\n; - 0BEh:  read CD (raw)\r\n;\r\nPacket label byte\r\nPktOPC  db  0       ;+0 Opcode.\r\n        db  0       ;+1 Unused (LUN and reserved).\r\nPktLBA  dd  0       ;+2 CD-ROM logical block address.\r\nPktLH   db  0       ;+6 \"Transfer length\" (sector count).\r\nPktLn   dw  0       ;+7 Middle- and low-order sector count.\r\nPktRM   db  0       ;+9 Read mode (\"Raw\" Read Long only).\r\n        dw  0       ;Unused ATAPI \"pad\" bytes (required).\r\n        align 4\r\n;\r\n; Miscellaneous Driver Variables.\r\n;\r\nAudAP   dd  0       ;Current audio-start address pointer.\r\n                    ;points to curr AUDADR struct\r\nbFlags  db  0\r\nbDMA    db  0       ;DMA input flag (0/1).\r\nbTry    db  0       ;I-O retry counter.\r\n        align 4\r\n\r\n;bFlags values\r\nFL_Q    equ 1       ;/Q option set\r\nFL_F    equ 2       ;/F option set\r\nif MWDMA\r\nFL_W    equ 4       ;/W option\r\nendif\r\nFL_UX   equ 8       ;/UX option set\r\n\r\n;\r\n; Audio Function Buffer (16 bytes) for most CD-ROM \"audio\" requests.\r\n;   The variables below are used only during driver initialization.\r\n;\r\nInBuf   equ $\r\npUTbl   dd  offset UnitTbl      ;Initialization unit table pointer.\r\nScanX   dd  offset ScanLPM      ;Scan table ptr (disks)\r\npScan   dd  offset ScanLPM      ;Scan table ptr (controllers)\r\nMaxUM   db  -1                  ;UDMA \"mode\" limit set by /Mn option.\r\nUFlag   db  0                   ;UltraDMA \"mode\" flags (set by Init).\r\nUMode   db  0                   ;UltraDMA \"mode\" value (set by Init).\r\nbLegacy db  0                   ;allow just one \"legacy\" controller\r\n\r\n;\r\n; Unit Parameter Tables.   If you want a 4th drive, simply add 1 more\r\n;   parameter table -- NO extra code and NO other changes are needed!\r\n;\r\nUnitTbl UPARM NUMDSK dup (<-1,-1,-1,0,-1,<-1,-1,-1>>)\r\n        align 4\r\n;\r\n; Dispatch Table for DOS CD-ROM request codes 0 through 14.\r\n;\r\nDspTbl1 dd  DspLmt1     ;Number of valid request codes.\r\n        dd  Try2ndD     ;Invalid-request handler address.\r\nDspTblA dd  UnSupp      ;00 -- Initialization  (special).\r\n        dd  UnSupp      ;01 -- Media Check  (unused).\r\n        dd  UnSupp      ;02 -- Build BPB    (unused).\r\n        dd  IOCTLInput  ;03 -- IOCTL Input.\r\n        dd  UnSupp      ;04 -- Input        (unused).\r\n        dd  UnSupp      ;05 -- Input no-wait    (unused).\r\n        dd  UnSupp      ;06 -- Input Status (unused).\r\n        dd  UnSupp      ;07 -- Input flush  (unused).\r\n        dd  UnSupp      ;08 -- Output       (unused).\r\n        dd  UnSupp      ;09 -- Output & verify  (unused).\r\n        dd  UnSupp      ;10 -- Output status    (unused).\r\n        dd  UnSupp      ;11 -- Output flush (unused).\r\n        dd  IOCTLOutput ;12 -- IOCTL Output.\r\n        dd  Ignored     ;13 -- Device Open     (ignored).\r\n        dd  Ignored     ;14 -- Device Close    (ignored).\r\nDspLmt1 equ ($ - offset DspTblA)/4  ;Request-code limit for this table.\r\n\r\n        dd DspLmt1 dup (0)\r\n;\r\n; Dispatch Table for DOS CD-ROM request codes 128 through 136.\r\n;\r\nDspTbl2 dd  DspLmt2     ;Number of valid request codes.\r\n        dd  UnSupp      ;Invalid-request handler address.\r\nDspTblB dd  ReqRL       ;128 -- Read Long.\r\n        dd  UnSupp      ;129 -- Reserved    (unused).\r\n@RqPref dd  ReqSeek     ;130 -- Read Long Prefetch.\r\n@RqSeek dd  ReqSeek     ;131 -- Seek.\r\n@RqPlay dd  ReqPlay     ;132 -- Play Audio.\r\n@RqStop dd  ReqStop     ;133 -- Stop Audio.\r\n        dd  UnSupp      ;134 -- Write Long  (unused).\r\n        dd  UnSupp      ;135 -- Wr. Long Verify (unused).\r\n@RqRsum dd  ReqRsum     ;136 -- Resume Audio.\r\nDspLmt2 equ ( $ - offset DspTblB)/4 ;Request-code limit for this table.\r\n\r\n        dd DspLmt2 dup (0)\r\n\r\n;\r\n; Dispatch table for IOCTL Input requests.\r\n;\r\nDspTbl3 dd  DspLmt3     ;Number of valid request codes.\r\n        dd  UnSupp      ;Invalid-request handler address.\r\nDspTblC dd  ReqDHA      ;00 -- Device-header address.\r\n@RqCHL  dd  ReqCHL      ;01 -- Current head location.\r\n        dd  UnSupp      ;02 -- Reserved     (unused).\r\n        dd  UnSupp      ;03 -- Error Statistics (unused).\r\n        dd  UnSupp      ;04 -- Audio chan. info (unused).\r\n        dd  UnSupp      ;05 -- Read drive bytes (unused).\r\n        dd  ReqDevStat  ;06 -- Device status.\r\n        dd  ReqSecSize  ;07 -- Sector size.\r\n@RqVS   dd  ReqVolSize  ;08 -- Volume size.\r\n        dd  ReqMCS      ;09 -- Media-change status.\r\n@RqADI  dd  ReqADI      ;10 -- Audio disk info.\r\n@RqATI  dd  ReqATI      ;11 -- Audio track info.\r\n@RqAQI  dd  ReqAQI      ;12 -- Audio Q-channel info.\r\n        dd  UnSupp      ;13 -- Subchannel info  (unused).\r\n        dd  UnSupp      ;14 -- Read UPC code    (unused).\r\n@RqASI  dd  ReqASI      ;15 -- Audio status info.\r\nDspLmt3 equ ( $ - offset DspTblC)/4 ;Request-code limit for this table.\r\n\r\n;--- expected size of IOCTL input requests\r\n        dd  5       ;0\r\n        dd  6       ;1\r\n        dd  0,0,0,0 ;2-5\r\n        dd  5       ;6\r\n        dd  4       ;7\r\n        dd  5       ;8\r\n        dd  2       ;9\r\n        dd  7       ;10\r\n        dd  7       ;11\r\n        dd  11      ;12\r\n        dd  0,0     ;13-14\r\n        dd  11      ;15\r\n\r\n;\r\n; Dispatch table for IOCTL Output requests.\r\n;\r\nDspTbl4 dd  DspLmt4     ;Number of valid request codes.\r\n        dd  UnSupp      ;Invalid-request handler address.\r\nDspTblD dd  ReqEjct     ;00 -- Eject Disk.\r\n        dd  ReqDoor     ;01 -- Lock/Unlock Door.\r\n        dd  ReqRS       ;02 -- Reset drive.\r\n        dd  UnSupp      ;03 -- Audio control    (unused).\r\n        dd  UnSupp      ;04 -- Write ctl. bytes (unused).\r\n        dd  ReqTray     ;05 -- Close tray.\r\nDspLmt4 equ ( $ - offset DspTblD)/4 ;Request-code limit for this table.\r\n\r\n;--- expected size of IOCTL output requests\r\n        dd  1\r\n        dd  2\r\n        dd  1\r\n        dd  0,0\r\n        dd  1\r\n\r\n; IDE controllers, programming interface bits:\r\n; 7 bus mastering (read-only)\r\n; 6-4   reserved (read-only)\r\n; 3 secondary IDE mode bit is writable (read-only)\r\n; 2 secondary IDE mode (0 = legacy, 1 = native)\r\n; 1 primary IDE mode bit is writable (read-only)\r\n; 0 primary IDE mode (0 = legacy, 1 = native)\r\n;\r\n; Initialization IDE Parameter-Value Table.\r\n; each IDE controller needs 4 entries (primary/secondary, master/slave),\r\n\r\nScanLPM IDEPARM NUMCTRL dup (<-1,-1,0A0h>,<-1,-1,0B0h>,<-1,-1,0A0h>,<-1,-1,0B0h>)\r\n\r\nif MWDMA\r\nMWModes label word\r\n    dw  0004       ;Mode 0\r\n    dw  0013       ;Mode 1\r\n    dw  0016       ;Mode 2\r\nendif\r\nUModes label word\r\n    dw  0016       ;Mode 0, ATA-16  UDMA mode table\r\n    dw  0025       ;Mode 1, ATA-25\r\n    dw  0033       ;Mode 2, ATA-33\r\n    dw  0044       ;Mode 3, ATA-44  (Unusual but possible).\r\n    dw  0066       ;Mode 4, ATA-66\r\n    dw  0100       ;Mode 5, ATA-100\r\n    dw  0133       ;Mode 6, ATA-133\r\n    dw  0166       ;Mode 7, ATA-166\r\n\r\nUnitNo  dd  0\r\nwDmaMode dw 0\r\n        db -1\r\nDrvName db 40 dup (0)\r\n        db 0\r\nszDriver db 9 dup (0)\r\n\r\n\r\nrmcode label byte\r\n    db 2Eh, 89h, 1eh, REQOFS+0, 0    ;mov cs:[16h],bx\r\n    db 2Eh, 8Ch, 06h, REQOFS+2, 0    ;mov cs:[18h],es\r\n    db 0CBh                     ;retf\r\nSIZERMCODE equ $ - offset rmcode    \r\n    db 0EAh                     ;jmp ssss:oooo\r\n\r\n    .code\r\n\r\n\tinclude printf.inc\r\n\r\n;\r\n; \"Device-Interrupt\" routine -- This routine processes DOS requests.\r\n;\r\nDevInt:\r\n\r\n    call ZPacket            ;Clear our ATAPI packet area.\r\n    mov esi, [pRequest]\r\n    movzx eax,@word [esi+0]\r\n    movzx esi,@word [esi+2]\r\n    shl esi, 4\r\n    add esi, eax            ;Point to DOS request packet.\r\n    mov [dwRequest], esi    ;linear address request header\r\n\r\n    mov [esi].RPH.wStat,RPDON ;Init status to \"done\".\r\n    mov al,[esi].RPH.bSubU    ;Get unit-table offset.\r\n    mov ah,sizeof UPARM\r\n    mul ah\r\n    lea ebx,[eax+offset UnitTbl]    ;Set unit's audio-start address ptr.\r\n    lea eax, [ebx+UPARM.dwAudSta]\r\n    mov [AudAP],eax\r\n\r\n    push [ebp].Client_Reg_Struc.Client_EFlags           ;save client flags\r\n    or @byte [ebp].Client_Reg_Struc.Client_EFlags+1,2   ;set client's IF\r\n\r\n    mov al,[esi].RPH.bOp    ;Get packet request code.\r\n    mov edi,offset DspTbl1  ;Point to 1st DOS dispatch table.\r\n    call Dspatch            ;Dispatch to desired request handler.\r\n\r\n    pop [ebp].Client_Reg_Struc.Client_EFlags    ;restore client flags\r\n\r\n    @VMMCall Simulate_Far_Ret\r\n    ret\r\n;\r\n; Function-Code \"Dispatch\" Routines.\r\n;\r\nTry2ndD:\r\n    sub al,080h             ;Not request code 0-15:  subtract 128.\r\n    mov edi,offset DspTbl2  ;Point to 2nd DOS dispatch table.\r\n    jmp Dspatch             ;Go try request-dispatch again.\r\nIOCTLInput:\r\n    mov edi,offset DspTbl3  ;Point to IOCTL Input dispatch table.\r\n    jmp TryIOC\r\nIOCTLOutput:\r\n    mov edi,offset DspTbl4  ;Point to IOCTL Output dispatch table.\r\nTryIOC:\r\n    movzx ecx,@word [esi].IOC.IOCAdr+2\r\n    movzx esi,@word [esi].IOC.IOCAdr+0\r\n    shl ecx,4\r\n    add esi, ecx\r\n    mov al,[esi]            ;Get actual IOCTL request code.\r\n    mov esi,[dwRequest]     ;Reload DOS request-packet address.\r\n\r\n;--- Dspatch()\r\n;--- EDI=DTAB\r\n;--- DTAB+0: dd #entries\r\n;--- DTAB+4: dd offset error handler\r\n;--- DTAB+8: start dispatch table ( size #entries )\r\n\r\nDspatch:\r\n    xor edx,edx\r\n    mov ecx, [edi]          ;get number of table entries\r\n    add edi,4               ;(Skip past table-limit value).\r\n    cmp al,cl               ;Is request code out-of-bounds?\r\n    jae @F                  ;Yes?  Dispatch to error handler!\r\n    add edi,4               ;Skip past error-handler address.\r\n    movzx eax,al            ;Point to request-handler address.\r\n    lea edi,[edi+eax*4]\r\n    mov edx,[edi+ecx*4]\r\n@@:\r\n    mov edi,[edi]           ;Get handler address from table.\r\n    and edx, edx\r\n    jz  @F\r\n\r\n;--- v1.5: IXM removed\r\n;    shr edx,11              ;Ensure correct IOCTL transfer\r\n\r\n    mov [esi].IOC.IOCLen,dx ;length is set in DOS packet.\r\n    movzx ecx,@word [esi].IOC.IOCAdr+2\r\n    movzx esi,@word [esi].IOC.IOCAdr+0\r\n    shl ecx,4               ;Get IOCTL data-transfer address.\r\n    add esi, ecx\r\n@@:\r\n    jmp edi                 ;Dispatch to desired request handler.\r\n\r\nGenFail:\r\n    mov al,12               ;General failure!  Get error code.\r\n    jmp ReqErr              ;Go post packet error code & exit.\r\nUnSupp:\r\n    mov al,3                ;Unsupported request!  Get error code.\r\n    jmp ReqErr              ;Go post packet error code & exit.\r\nSectNF:\r\n    mov al,8                ;Sector not found!  Get error code.\r\nReqErr:\r\n    mov esi,[dwRequest]     ;Reload DOS request-packet address.\r\n    mov ah,081h             ;Post error flags & code in packet.\r\n    mov [esi].RPH.wStat,ax\r\nIgnored:\r\n    ret                     ;Exit (\"ignored\" request handler).\r\n;\r\n; IOCTL Input \"Device Header Address\" handler\r\n;\r\nReqDHA:\r\n    mov eax,[dwBase]\r\n    shl eax,12              ;convert linear address to SSSS:0000\r\n    mov [esi+1], eax\r\n    ret\r\n;\r\n; IOCTL Input \"Return Sector Size\" handler\r\n;\r\nReqSecSize:\r\n    cmp @byte [esi+1],1     ;Is read mode \"cooked\" or \"raw\"\r\n    ja  GenFail             ;No?  Post \"general failure\" & exit.\r\n    mov ax,RAWSL            ;Get \"raw\" sector length.\r\n    je  @F                  ;If \"raw\" mode, set sector length.\r\n    mov ax,COOKSL           ;Get \"cooked\" sector length.\r\n@@:\r\n    mov [esi+2],ax          ;Post sector length in IOCTL packet.\r\nRqSSX:\r\n    ret\r\n;\r\n; DOS \"Read Long\" handler.\r\n;\r\nReqRL:\r\n    call ValSN                      ;Validate starting sector number.\r\n    call MultiS                     ;Handle Multi-Session disk if needed.\r\n    jc  ReqErr                      ;If error, post return code & exit.\r\n    mov cx,[esi].RL.RLSC            ;Get request sector count.\r\n    jcxz RqSSX                      ;If zero, simply exit.\r\n    xchg cl,ch                      ;Save swapped sector count.\r\n    mov [PktLn],cx\r\n    cmp [esi].RL.RLDM,1             ;\"Cooked\" or \"raw\" read mode?\r\n    ja  SectNF                      ;No?  Return \"sector not found\"!\r\n    mov dl,028h                     ;Get \"cooked\" input values.\r\n    mov ax,COOKSL\r\n    jb  @F                          ;If \"cooked\" input, set values.\r\n    mov dl,0BEh                     ;Get \"raw\" input values.\r\n    mov ax,RAWSL\r\n    mov [PktRM],0F8h                ;Set \"raw\" input flags.\r\n@@:\r\n    mov [PktOPC],dl                 ;Set \"packet\" opcode.\r\n    mul [esi].RL.RLSC               ;Get desired input byte count.\r\n    test dx,dx                      ;More than 64K bytes desired?\r\n    jnz SectNF                      ;Yes?  Return sector not found!\r\n    movzx eax,ax\r\n    mov [BufLng],eax                ;Set DMA byte counts.\r\n    mov ecx, eax                    ;set ECX for VDMAD call below\r\n    bts eax,31\r\n    mov [DmaLng],eax                ;Set DMA list \"end\" flag.\r\n\r\n    movzx edx,@word [esi].RL.RLAddr+0\r\n    movzx eax,@word [esi].RL.RLAddr+2\r\n    shl eax, 4\r\n    add eax, edx\r\n    mov [BufAdr],eax\r\n\r\n    test @byte [ebx].UPARM.wDMABase,7  ;Is drive using UltraDMA?\r\n    jnz UsePIO                      ;No, do \"PIO mode\" input.\r\n    test al,3                       ;Is user buffer 32-bit aligned?\r\n    jnz BuffIO                      ;No, use buffered IO\r\n\r\n    push esi\r\n    mov esi, eax                    ;ESI=lin addr, ECX=length\r\n    test [bFlags],FL_F              ;if /F not set, check 64 kB boundary\r\n    setz dl\r\n    VxDCall VDMAD_Lock_DMA_Region   ;is region physical contiguous?\r\n    pop esi\r\n    jc  BuffIO                      ;Error -- use buffered input.\r\n    mov [DmaAdr],edx\r\n    mov eax,edx                     ;Get phys address.\r\n    cmp @word [DmaAdr+2],-1         ;Is DMA beyond our limit?\r\n@DMALmt equ $-1                     ;(009h for a 640K limit).\r\n    ja  BuffIO                      ;Yes, use buffered input\r\n    test [bFlags],FL_F              ;/F option set?\r\n    jz UseDMA\r\n    mov ecx,[DmaLng]                ;Get lower ending DMA address.\r\n    dec ecx                         ;(DmaLng - 1 + DmaAdr).\r\n    add ax,cx                       ;Would input cross a 64K boundary?\r\n    jnc UseDMA                      ;No, set DMA flag & do transfer.\r\n    inc eax                         ;Get bytes above 64K boundary.\r\n    cmp ax,64                       ;Is this at least 64 bytes?\r\n    jb  BuffIO                      ;No, use buffered input\r\n    inc ecx                         ;Get bytes below 64K boundary.\r\n    sub cx,ax\r\n    cmp cx,64                       ;Is this at least 64 bytes?\r\n    jb  BuffIO                      ;No, use buffered input\r\n    mov [DmaLng2],ax                ;Set 2nd command-list byte count.\r\n    movzx eax,cx                    ;Set 1st command-list byte count.\r\n    mov [DmaLng],eax\r\n    add eax,[DmaAdr]                ;Set 2nd command-list address.\r\n    mov [DmaAdr2],eax\r\nUseDMA:\r\n    mov [bDMA],1                    ;Set UltraDMA input flag.\r\nUsePIO:\r\n    call DoIO                       ;Execute desired read request.\r\n    jnc @F                          ;If no errors, go exit below.\r\n    call ReqErr                     ;Post desired error code.\r\n@@:\r\n    mov [bDMA],0                    ;Reset UltraDMA input flag.\r\n    ret\r\n\r\n;--- use the VDS DMA buffer if user buffer cannot be used.\r\n;--- this should be rarely necessary if /F is set.\r\n\r\nBuffIO:\r\n    push esi\r\n    push ebx\r\n    mov esi,[BufAdr]\r\n    mov ecx,[BufLng]\r\n    VxDCall VDMAD_Request_Buffer    ;get VDS DMA buffer\r\n    mov eax,ebx\r\n    pop ebx\r\n    pop esi\r\n    jc UsePIO                       ;no buffer available, use PIO!\r\n    mov [DmaAdr],edx\r\n    push eax\r\n    call UseDMA\r\n    pop ebx                         ;set buffer ID in ebx\r\n    jc @F\r\n    mov esi, [BufAdr]\r\n    mov ecx, [BufLng]\r\n    xor edi, edi                    ;buffer offset always zero\r\n    VxDCall VDMAD_Copy_From_Buffer\r\n@@:\r\n    VxDCall VDMAD_Release_Buffer    ;buffer ID still in ebx\r\n    ret\r\n\r\n;\r\n; DOS \"Seek\" handler.\r\n;\r\nDOSSeek:\r\n    call ValSN                      ;Validate desired seek address.\r\n    call MultiS                     ;Handle Multi-Session disk if needed.\r\n    jc  DOSSkE                      ;If error, post return code & exit.\r\n    mov [PktOPC],02Bh               ;Set \"seek\" command code.\r\nDOSSk1:\r\n    call DoIOCmd                    ;Issue desired command to drive.\r\nDOSSkE:\r\n    jc ReqErr   ;If error, post return code & exit.\r\n    ret\r\n;\r\n; IOCTL Input \"Device Status\" handler.\r\n;\r\n;--- status flag bits:\r\n;--- 001 - door opened\r\n;--- 002 - door unlocked\r\n;--- 004 - supports cooked AND raw\r\n;--- 008 - read AND write\r\n;--- 010 - data read AND play audio\r\n;--- 020 - supports interleaving\r\n;--- 040 - (res)\r\n;--- 080 - supports prefetching\r\n;--- 100 - supports audio channel manipulation\r\n;--- 200 - supports HSG AND red book addressing modes\r\n\r\nReqDevStat:\r\n    mov @dword [Packet],0002A005Ah  ;Set up mode-sense.\r\n    mov al,16           ;Set input byte count of 16.\r\n    call DoBufIO        ;Issue mode-sense for hardware data.\r\n    jc  DOSSkE          ;If error, post return code & exit.\r\n    mov eax,00214h      ;Get our basic driver status flags.\r\n@Status equ $-4         ;(Set by Init to 00204h for /AX).\r\n    cmp @byte [edi+2],071h  ;\"Unknown CD\", i.e. door open?\r\n    jne @F              ;No, check \"locked\" status.\r\n    or  al,001h         ;Post \"door open\" status flag.\r\n@@:\r\n    test @byte [edi+14],002h ;Drive pushbutton \"locked out\"?\r\n    jnz @F              ;No, set flags in IOCTL.\r\n    or  al,002h         ;Set \"door locked\" status flag.\r\n@@:\r\n    mov [esi+1],eax     ;Set status flags in IOCTL buffer.\r\n@RqDSX:\r\n    jmp ReadAudioSt     ;Go post \"busy\" status and exit.\r\n;\r\n; IOCTL Input \"Media-Change Status\" handler.\r\n;\r\nReqMCS:\r\n    call DoIOCmd        ;Issue \"Test Unit Ready\" command.\r\n    mov edi,[AudAP]     ;Get media-change flag from table.\r\n    mov al,[edi-1]\r\n    mov [esi+1],al      ;Return media-change flag to user.\r\n    ret\r\n;\r\n; IOCTL Output \"Eject Disk\" handler.\r\n;\r\nReqEjct:\r\n    mov @word [Packet],0011Bh   ;Set \"eject\" commands.\r\n    mov @byte [PktLBA+2],002h   ;Set \"eject\" function.\r\n    jmp DOSSk1                  ;Go do \"eject\" & exit.\r\n;\r\n; IOCTL Output \"Lock/Unlock Door\" handler.\r\n;\r\nReqDoor:\r\n    mov al,[esi+1]          ;Get \"lock\" or \"unlock\" function.\r\n    cmp al,001h             ;Is function byte too big?\r\n    ja  RqRS1               ;Yes, post \"General Failure\" & exit.\r\n    mov cx,0001Eh           ;Get \"lock\" & \"unlock\" commands.\r\nRqDoor1:\r\n    mov @word [Packet],cx   ;Set \"packet\" command bytes.\r\n    mov @byte [PktLBA+2],al ;Set \"packet\" function byte.\r\n    call DoIOCmd            ;Issue desired command to drive.\r\n    jc  DOSSkE              ;If error, post return code & exit.\r\n    jmp @RqDSX              ;Go post \"busy\" status and exit.\r\n;\r\n; IOCTL Output \"Reset Drive\" handler.\r\n;\r\nReqRS:\r\n    call StopDMA            ;Stop previous DMA & select drive.\r\n    inc edx                 ;Point to IDE command register.\r\n    mov al,008h             ;Do an ATAPI \"soft reset\" command.\r\n    out dx,al\r\n    call TestTO             ;Await controller-ready.\r\nRqRS1:\r\n    jc GenFail              ;Timeout!  Return \"General Failure\".\r\n    ret\r\n;\r\n; IOCTL Output \"Close Tray\" handler.\r\n;\r\nReqTray:\r\n    mov al,003h             ;Get \"close tray\" function byte.\r\n    mov cx,0011Bh           ;Get \"eject\" & \"close\" commands.\r\n    jmp RqDoor1             ;Go do \"close tray\" command above.\r\n;\r\n; Subroutine to handle a Multi-Session disk for DOS reads and seeks.\r\n;   Multi-Session disks require (A) saving the last-session starting\r\n;   LBA for a new disk after any media-change and (B) \"offsetting\" a\r\n;   read of the VTOC or initial directory block, sector 16 or 17, to\r\n;   access the VTOC/directory of the disk's last session.\r\n;\r\nMultiS:\r\n    mov edi,[AudAP]             ;Point to drive variables.\r\n    cmp @byte [edi+11],0FFh     ;Is last-session LBA valid?\r\n    jne MultiS1                 ;Yes, proceed with request.\r\n    mov [PktOPC],043h           ;Set \"Read TOC\" command.\r\n    inc @byte [PktLBA]          ;Set \"format 1\" request.\r\n    mov al,12                   ;Set 12-byte allocation ct.\r\n    call DoBufIO                ;Read first & last session.\r\n    jc  MultiSX                 ;If any error, exit below.\r\n    mov @byte [PktLBA],0        ;Reset \"format 1\" request.\r\n    mov al,[edi+3]              ;Set last session in packet.\r\n    mov [PktLH],al\r\n    call DoIO                   ;Read disk info for last session.\r\n    jc  MultiSX                 ;If error, exit with carry set.\r\n    mov eax,[edi+8]             ;\"Swap\" & save this disk's last-\r\n    call Swap32                 ;  session starting LBA address.\r\n    mov edi,[AudAP]\r\n    mov [edi.AUDADR.dwLSSta],eax\r\n    call ZPacket                ;Reset our ATAPI packet area.\r\nMultiS1:\r\n    mov eax,[esi].RL.RLSec      ;Get starting sector number.\r\n    mov edx,eax                 ;\"Mask\" sector to an even number.\r\n    and dl,0FEh\r\n    cmp edx,16                  ;Sector 16 (VTOC) or 17 (directory)?\r\n    jne MultiS2                 ;No, set sector in packet.\r\n    add eax,[edi+8]             ;Offset sector to last-session start.\r\nMultiS2:\r\n    call Swap32                 ;\"Swap\" sector into packet as LBA.\r\n    mov [PktLBA],eax\r\n    clc                         ;Clear carry flag (no errors).\r\nMultiSX:\r\n    ret\r\n\r\n;--- read in internal buffer\r\n;--- size in AL (12,16,4)\r\n;--- the ATAPI command to perform is in PktOPC\r\n\r\nDoBufIO:\r\n    mov @byte [PktLn+1],al  ;Buffered -- set packet count.\r\nDoBufIn:                    ;<--- entry with AL=8\r\n    movzx eax,al            ;Save data-transfer length.\r\n    mov [BufLng],eax\r\n    mov [BufAdr],offset InBuf;Use our buffer for I-O.\r\n    jmp DoIO                ;Go start I-O below.\r\nDoIOCmd:\r\n    mov [BufLng],0          ;Command only -- reset length.\r\n\r\n;\r\n;--- I-O Subroutine.   ALL of our CD-ROM I-O is executed here!\r\n;--- ebx -> drive params\r\n;--- Packet: ATAPI packet command to be sent\r\n;--- esi must be preserved\r\n;--- returns with C on errors, then AL contains error code\r\n;--- edi is set to offset InBuf on exit\r\n    \r\nDoIO proc\r\n    cld\r\n    push esi\r\n    mov [bTry],4            ;Set request retry count of 4.\r\nDoIO1:                      ;<--- new try\r\n    call StopDMA            ;Stop previous DMA & select drive.\r\n    call TestTO             ;Await controller-ready.\r\n    jc  DoIOErr             ;Timeout!  Handle as a \"hard error\".\r\n    cmp [bDMA],0            ;UltraDMA input request?\r\n    je  @F                  ;No, output our ATAPI \"packet\".\r\n    mov dx,[ebx].UPARM.wDMABase;Point to DMA command register.\r\n    mov al,008h             ;Reset DMA commands & set read mode.\r\n    out dx,al\r\n    inc edx                 ;Point to DMA status register.\r\n    inc edx\r\n    in  al,dx               ;Reset DMA status register.\r\n    or  al,006h             ;(Done this way so we do NOT alter\r\n    out dx,al               ;  the \"DMA capable\" status flags!).\r\n    inc edx                 ;Set PRD pointer to our DMA address.\r\n    inc edx\r\n    mov eax,[PRDAd]\r\n    out dx,eax\r\n@@:\r\n    mov dx,[ebx].UPARM.wIDEBase;Point to IDE \"features\" register.\r\n    inc edx\r\n    mov al,[bDMA]           ;If UltraDMA input, set \"DMA\" flag.\r\n    out dx,al\r\n    add edx,3               ;Point to byte count registers.\r\n    mov eax,[BufLng]        ;Output data-transfer length.\r\n    out dx,al\r\n    inc edx\r\n    mov al,ah\r\n    out dx,al\r\n    inc edx                 ;Point to command register.\r\n    inc edx\r\n    mov al,0A0h             ;Issue \"Packet\" command.\r\n    out dx,al\r\n    mov cl,DRQ              ;Await controller- and data-ready.\r\n    call TestTO1\r\n    jc  DoIOErr             ;Timeout!  Handle as a \"hard error\".\r\n    xchg eax,esi            ;Save BIOS timer address.\r\n    mov dx,[ebx].UPARM.wIDEBase;Point to IDE data register.\r\n    mov ecx,6               ;Output all 12 \"Packet\" bytes.\r\n    mov esi,offset Packet\r\n    rep outsw\r\n    xchg eax,esi            ;Reload BIOS timer address.\r\n    mov ah,STARTTO          ;Allow 4 seconds for drive startup.\r\n    cmp [bDMA],0            ;UltraDMA input request?\r\n    je  DoPioIO             ;No, do \"PIO mode\" transfer below.\r\n    mov [XFRLng],0          ;Reset transfer length (DMA does it).\r\n    add ah,[esi]            ;Set 4-second timeout in AH-reg.\r\n\r\n    mov @byte ds:[48Eh],0   ;Reset BIOS disk-interrupt flag.\r\n\r\n    mov dx,[ebx].UPARM.wDMABase;Point to DMA command register.\r\n    in  al,dx               ;Set DMA Start/Stop bit (starts DMA).\r\n    or  al,1\r\n    out dx,al\r\n\r\n@@:\r\n    inc edx                 ;Point to DMA status register.\r\n    inc edx\r\n    in  al,dx               ;Read DMA controller status.\r\n    dec edx                 ;Point back to DMA command register.\r\n    dec edx\r\n    and al,DMI+DME          ;DMA interrupt or DMA error?\r\n    jnz @F                  ;Yes, halt DMA and check results.\r\n    cmp ah,[esi]            ;Has our DMA transfer timed out?\r\n    jz  @F\r\n    @VMMCall Yield           ;run client's ISRs\r\n    cmp @byte ds:[48Eh],0   ;Did BIOS get a disk interrupt?\r\n    je  @B                  ;No, loop back & retest status.\r\n    mov al,DMI              ;Set \"simulated\" interrupt flag.\r\n@@:\r\n    xchg eax,esi            ;Save ending DMA status.\r\n    in  al,dx               ;Reset DMA Start/Stop bit.\r\n    and al,0FEh\r\n    out dx,al\r\n    xchg eax,esi            ;Reload ending DMA status.\r\n    cmp al,DMI              ;Did DMA end with only an interrupt?\r\n    jne DoIOErr             ;No?  Handle as a \"hard error\"!\r\n    inc edx                 ;Reread DMA controller status.\r\n    inc edx\r\n    in  al,dx\r\n    test al,DME             ;Any \"late\" DMA error after DMA end?\r\n    jz  DoIO18              ;No, go await controller-ready.\r\n    jmp DoIOErr             ;Timeouts and DMA errors are \"hard\"!\r\n    align 4\r\n\r\n;--- do PIO mode IO\r\n\r\nDoPioIO:\r\n    mov edx,[BufAdr]\r\n    mov ecx,[BufLng]\r\n    mov [XFRAdr],edx        ;reset data-transfer buffer address.\r\n    mov [XFRLng],ecx        ;reset data-transfer byte count.\r\nContPioIO:                  ;<---\r\n    xor cl,cl\r\n    call TestTO2            ;await controller-ready (CL=flag, AH=time).\r\n    jc  DoIOErr             ;Timeout!  Handle as a \"hard error\".\r\n    test al,DRQ             ;Did we also get a data-request?\r\n    jz  DoIO18              ;No, go await controller-ready.\r\n    dec edx                 ;Get number of buffer bytes.\r\n    dec edx\r\n    in  al,dx\r\n    mov ah,al\r\n    dec edx\r\n    in  al,dx\r\n    inc eax                 ;Make buffer byte count \"even\".\r\n    and eax,not 1\r\n    movzx eax,ax\r\n    mov dx,[ebx].UPARM.wIDEBase;Point to IDE data register.\r\n    mov esi,[XFRLng]        ;Get our data-transfer length.\r\n    test esi,esi            ;Any remaining bytes to transfer?\r\n    jz  DoIO14              ;No, \"eat\" input or \"pad\" output.\r\n    cmp esi,eax             ;Buffer count > remaining bytes?\r\n    jbe @F                  ;No, save current block count.\r\n    mov esi,eax             ;Set remaining bytes as block count.\r\n@@:\r\n    mov ecx,esi             ;Save current block count.\r\n    mov edi,[XFRAdr]        ;Get input data-transfer address.\r\n    shr ecx,1               ;Get input word count.\r\nif DWRDIO\r\n@DWOP::\r\n    db 8Dh,9Bh,0,0,0,0      ;a 6 byte NOP (lea ebx,[ebx+0])\r\nendif\r\n    rep insw                ;Input all data words.\r\n    mov [XFRAdr],edi\r\n    sub [XFRLng],esi        ;Decrement data-transfer length.\r\n    sub eax,esi             ;Decrement buffer count.\r\n    jz DoIO17\r\nDoIO14:\r\n    xchg eax,ecx            ;Any \"residual\" bytes to handle?\r\n    jecxz DoIO17            ;No, see if more I-O is needed.\r\n    shr ecx,1               ;Get residual buffer word count.\r\n@@:\r\n    in  ax,dx               ;\"Eat\" residual input bytes.\r\n    loop @B\r\nDoIO17:\r\n    mov ah,SEEKTO           ;Allow 2 seconds if drive must \"seek\".\r\n    jmp ContPioIO           ;Go see if more I-O is needed.\r\n    align 4\r\n\r\n;--- Finish DMA/PIO\r\n    \r\nDoIO18:\r\n    call TestTO             ;Await controller-ready.\r\n    jc  DoIOErr             ;Timeout!  Handle as a \"hard error\".\r\n    mov esi,[AudAP]         ;Get drive media-change flag pointer.\r\n    dec esi\r\n    and eax,1               ;Did controller detect any errors?\r\n    jz  DoIO21              ;No, see if all data was transferred.\r\n    sub edx,6               ;Get controller's sense key value.\r\n    in  al,dx\r\n    shr al,4\r\n    cmp al,006h             ;Is sense key \"Unit Attention\"?\r\n    je  DoIO23              ;Yes, check for prior media-change.\r\n    mov ah,0FFh             ;Get 0FFh M.C. flag for \"Not Ready\".\r\n    cmp al,002h             ;Is sense key \"Drive Not Ready\"?\r\n    je  DoIO24              ;Yes, go set our media-change flag.\r\nDoIOErr:\r\n    mov dx,[ebx].UPARM.wIDEBase;Hard error!  Point to command reg.\r\n    add edx,7\r\n    mov al,008h             ;Issue ATAPI \"soft reset\" to drive.\r\n    out dx,al\r\n    mov al,11               ;Get \"hard error\" return code.\r\nDoIO20:\r\n    dec [bTry]              ;Do we have more I-O retries left?\r\n    jnz DoIO1               ;Try re-executing this I-O request.\r\n    stc\r\n    jmp DoIOExit            ;No, return error code.\r\n    align 4\r\n    \r\n;--- operation ended without error    \r\n    \r\nDoIO21:\r\n    mov ecx,[XFRLng]        ;Get remaining data-transfer length.\r\n    jecxz @F                ;If zero, reset media-change & exit.\r\n    cmp @byte [Packet],028h ;\"Cooked\" data input req.?\r\n    je  DoIOErr             ;Yes, see if we can retry.\r\n    cmp @byte [Packet],0BEh ;\"Raw\" data input request?\r\n    je  DoIOErr             ;Yes, see if we can retry.\r\n    mov edi,[XFRAdr]        ;Load data-transfer address.\r\n    rep stosb               ;\"Pad\" remaining bytes to 0.\r\n@@:\r\n    mov @byte [esi],1       ;Set \"no media change\" flag.\r\n    clc                     ;Reset carry flag (no error).\r\nDoIOExit:\r\n    pop esi\r\n    mov edi,offset InBuf    ;For audio, point to our buffer.\r\n    ret\r\n\r\n;--- operation ended with error \"Unit Attention\"\r\n;--- esi=AudAP-1\r\n;--- AH=\r\n;--- AL=error code if byte ptr [esi] !=1\r\n    \r\nDoIO23:\r\n    mov al,2                ;\"Attention\":  Get \"Not Ready\" code.\r\n    cmp @byte [esi],0       ;Is media-change flag already set?\r\n    jle DoIO20              ;Yes, retry & see if it goes away!\r\nDoIO24:\r\n    xchg ah,[esi]           ;Load & set our media-change flag.\r\n    mov @byte [esi+12],0FFh ;Make last-session LBA invalid.\r\n    dec ah                  ;Is media-change flag already set?\r\n    jnz @F                  ;Yes, set carry flag and exit.\r\n    mov al,15               ;Return \"Invalid Media Change\".\r\n;\t@dprintf <\"DoIO, error 15, esi=%X, eax=%X\",13,10>, esi, eax\r\n@@:\r\n    stc                     ;Set carry flag (error!).\r\n    jmp DoIOExit\r\n    align 4\r\nDoIO endp\r\n;\r\n; Subroutine to convert \"RedBook\" MSF values to an LBA sector number.\r\n;\r\nConvLBA:\r\n    mov ecx,eax     ;Save \"seconds\" & \"frames\" in CX-reg.\r\n    shr eax,16      ;Get \"minute\" value.\r\n    cmp ax,99       ;Is \"minute\" value too large?\r\n    ja  CnvLBAE     ;Yes, return -1 error value.\r\n    cmp ch,60       ;Is \"second\" value too large?\r\n    ja  CnvLBAE     ;Yes, return -1 error value.\r\n    cmp cl,75       ;Is \"frame\" value too large?\r\n    ja  CnvLBAE     ;Yes, return -1 error value.\r\n    xor edx,edx     ;Zero EDX-reg. for 32-bit math below.\r\n    mov dl,60       ;Convert \"minute\" value to \"seconds\".\r\n    mul dl          ;(Multiply by 60, obviously!).\r\n    mov dl,ch       ;Add in \"second\" value.\r\n    add ax,dx\r\n    mov dl,75       ;Convert \"second\" value to \"frames\".\r\n    mul edx         ;(Multiply by 75 \"frames\"/second).\r\n    mov dl,150      ;Subtract offset - \"frame\".\r\n    sub dl,cl       ;(\"Adds\" frame, \"subtracts\" offset).\r\n    sub eax,edx\r\n    ret\r\nCnvLBAE:\r\n    or  eax,-1      ;Too large!  Set -1 error value.\r\n    ret\r\n;\r\n; Subroutine to clear our ATAPI \"packet\" area.\r\n;\r\nZPacket:\r\n    xor eax, eax\r\n    mov @dword [Packet+0],eax   ;Zero ATAPI packet bytes.\r\n    mov @dword [Packet+4],eax\r\n    mov @dword [Packet+8],eax\r\n    ret\r\n;\r\n; Subroutine to validate the starting disk sector number.\r\n;\r\nValSN proc\r\n    mov eax,[esi].RL.RLSec  ;Get starting sector number.\r\nValSN1::\r\n    mov dl,[esi].RL.RLAM    ;Get desired addressing mode.\r\n    cmp dl,001h             ;HSG or RedBook addressing?\r\n    ja  ValSNE              ;Neither -- set carry and exit!\r\n    jb  @F                  ;HSG -- check sector limit.\r\n    call ConvLBA            ;Get RedBook starting sector.\r\n@@: \r\n;   cmp eax,00006DD3Ah\r\n    cmp eax,001000000h      ;Is starting sector too big?\r\n    jb  done                ;No, all is well -- exit.\r\nValSNE:\r\n    pop eax                 ;Error!  Discard our exit address.\r\n    jmp SectNF              ;Post \"sector not found\" and exit.\r\ndone:\r\n\tret\r\nValSN endp\r\n;\r\n;--- Subroutine to test for I-O timeouts.\r\n;--- In: EBX=UPARM\r\n;--- Out: DX=IDE primary-status register.\r\n;--- modifies: eax, esi\r\n;\r\nTestTO proc\r\n    xor cl,cl           ;Check for only controller-ready.\r\nTestTO1::               ;<--- CL-reg = 008h to test for DRQ\r\n    mov ah, CMDTO       ;Use 500-msec command timeout.\r\nTestTO2::               ;<--- AH=STARTTO\r\n    mov esi,BIOSTMR     ;0040:006Ch\r\n    add ah,[esi]        ;Set timeout limit in AH-reg.\r\n@@:\r\n    @VMMCall Yield       ;preserves all registers\r\n    cmp ah,[esi]        ;Has our I-O timed out?\r\n    stc                 ;(If so, set carry flag).\r\n    je  exit            ;Yes?  Exit with carry flag on.\r\n    mov dx,[ebx].UPARM.wIDEBase\r\n    add edx,7\r\n    in  al,dx           ;Read IDE primary status.\r\n    test al,BSY         ;Is our controller still busy?\r\n    jnz @B              ;Yes, loop back and test again.\r\n    or  cl,cl           ;Are we also awaiting I-O data?\r\n    jz  exit            ;No, just exit.\r\n    test al,cl          ;Is data-request (DRQ) also set?\r\n    jz  @B              ;No, loop back and test again.\r\nexit:\r\n    ret                 ;Exit -- carry indicates timeout.\r\nTestTO endp\r\n;\r\n; Subroutine to ensure UltraDMA is stopped and then select our CD-ROM\r\n;   drive.   For some older chipsets, if UltraDMA is running, reading\r\n;   an IDE register causes the chipset to \"HANG\"!!\r\n;\r\nStopDMA proc\r\n    mov dx,[ebx].UPARM.wDMABase;Get drive UltraDMA command address.\r\n    test dl,2+4             ;Is any UltraDMA controller present?\r\n    jnz @F                  ;No, select \"master\" or \"slave\" unit.\r\n    and dl,0FEh             ;Mask out \"DMA disabled\" flag.\r\n    in  al,dx               ;Ensure any previous DMA is stopped!\r\n    and al,0FEh\r\n    out dx,al\r\n@@:\r\n    mov dx,[ebx].UPARM.wIDEBase;Point to IDE device-select register.\r\n    add edx,6\r\n    mov al,[ebx].UPARM.bDevSel ;Select IDE \"master\" or \"slave\" unit.\r\n    out dx,al\r\n    ret\r\nStopDMA endp\r\n;\r\n; Subroutine to \"swap\" the 4 bytes of a 32-bit value.\r\n;\r\nSwap32:\r\n    xchg al,ah      ;\"Swap\" original low-order bytes.\r\n    rol eax,16      ;\"Exchange\" low- and high-order.\r\n    xchg al,ah      ;\"Swap\" ending low-order bytes.\r\nSwap32X:\r\n    ret\r\n\r\n;\r\n; DOS \"Audio Seek\" handler.   All DOS and IOCTL routines beyond this\r\n;   point are DISMISSED by driver-init when the /AX switch is given.\r\n;\r\nReqSeek:\r\n    call ReadAudioSt    ;Read current \"audio\" status.\r\n    call ZPacket        ;Reset our ATAPI packet area.\r\n    jc  RqSK1           ;If status error, do DOS seek.\r\n    mov al,[edi+1]      ;Get \"audio\" status flag.\r\n    cmp al,011h         ;Is drive in \"play audio\" mode?\r\n    je  RqSK2           ;Yes, validate seek address.\r\n    cmp al,012h         ;Is drive in \"pause\" mode?\r\n    je  RqSK2           ;Yes, validate seek address.\r\nRqSK1:\r\n    jmp DOSSeek         ;Use DOS seek routine above.\r\nRqSK2:\r\n    call ValSN          ;Validate desired seek address.\r\n    mov edi,[AudAP]     ;Point to audio-start address.\r\n    cmp eax,[edi.AUDADR.dwAudEnd]     ;Is address past \"play\" area?\r\n    ja  RqSK1           ;Yes, do DOS seek above.\r\n    mov [edi.AUDADR.dwAudSta],eax     ;Update \"audio\" start address.\r\n    call ConvMSF        ;Set \"packet\" audio-start address.\r\n    mov @dword [PktLBA+1],eax\r\n    mov eax,[edi.AUDADR.dwAudEnd]     ;Set \"packet\" audio-end address.\r\n    call ConvMSF\r\n    mov @dword [PktLH],eax\r\n    mov @byte [Packet],047h ;Set \"Play Audio\" command.\r\n    call DoIOCmd        ;Start drive playing audio.\r\n    jc  RqPLE           ;If error, post code & exit.\r\n    cmp @byte [edi+1],011h  ;Playing audio before?\r\n    je  RqPLX           ;Yes, post \"busy\" status and exit.\r\n    call ZPacket        ;Reset our ATAPI packet area.\r\n    jmp ReqStop         ;Go put drive back in \"pause\" mode.\r\n;\r\n; DOS \"Play Audio\" handler.\r\n;\r\nReqPlay:\r\n    cmp [esi].RPPlay.dwSize,0   ;Is sector count zero?\r\n    je  Swap32X                 ;Yes, just exit above.\r\n    mov edi,[AudAP]             ;Point to audio-start addr.\r\n    mov @byte [Packet],047h     ;Set \"Play Audio\" command.\r\n    mov eax,[esi].RPPlay.dwStart;Validate starting address.\r\n    call ValSN1\r\n    mov [edi.AUDADR.dwAudSta],eax       ;Save audio-start address.\r\n    call ConvMSF        ;Set MSF start address in packet.\r\n    mov @dword [PktLBA+1],eax\r\n    mov eax,[esi].RPPlay.dwSize ;calc \"end\" address.\r\n    add eax,[edi.AUDADR.dwAudSta]\r\n    mov edx,00006DD39h  ;Get maximum audio address.\r\n    jc  ReqPL1          ;If \"end\" WAY too big, use max.\r\n    cmp eax,edx         ;Is \"end\" address past maximum?\r\n    jbe ReqPL2          ;No, use \"end\" address as-is.\r\nReqPL1:\r\n    mov eax,edx         ;Set \"end\" address to maximum.\r\nReqPL2:\r\n    mov [edi+4],eax     ;Save audio-end address.\r\n    call ConvMSF        ;Set MSF end address in packet.\r\n    mov @dword [PktLH],eax\r\n    call DoIOCmd        ;Issue \"Play Audio\" command.\r\nRqPLE:\r\n    jc  ReqErr          ;Error!  Post return code & exit.\r\nRqPLX:\r\n    jmp RdAST3          ;Go post \"busy\" status and exit.\r\n;\r\n; DOS \"Stop Audio\" handler.\r\n;\r\nReqStop:\r\n    mov @byte [Packet],04Bh     ;Set \"Pause/Resume\" cmd.\r\n    jmp DoIOCmd                 ;Go pause \"audio\", then exit.\r\n;\r\n; DOS \"Resume Audio\" handler.\r\n;\r\nReqRsum:\r\n    inc @byte [PktLn+1]         ;Set \"Resume\" flag for above.\r\n    call ReqStop                ;Issue \"Pause/Resume\" command.\r\n    jmp RqPLE                   ;Go exit through \"ReqPlay\" above.\r\n;\r\n; IOCTL Input \"Current Head Location\" handler.\r\n;\r\nReqCHL:\r\n;--- issue \"read sub-channel\" command (42h), MSF=0, SubQ=1\r\n;--- data format to be returned is 01 (=Audio Status)\r\n    mov @dword [Packet],001400042h   ;Set command bytes.\r\n    mov al,16           ;Set input byte count of 16.\r\n    call RdAST2         ;Issue \"Read Subchannel\" request.\r\n    jc  RqPLE           ;If error, post return code & exit.\r\n    mov @byte [esi+1],0 ;Return \"HSG\" addressing mode.\r\n    mov eax,[edi+8]     ;Return \"swapped\" head location.\r\n    call Swap32\r\n    mov [esi+2],eax\r\n    jmp RqATIX          ;Go post \"busy\" status and exit.\r\n;\r\n; IOCTL Input \"Return Volume Size\" handler.\r\n;\r\nReqVolSize:\r\n    mov @byte [Packet],025h  ;Set \"Read Capacity\" code.\r\n    mov al,008h         ;Get 8 byte data-transfer length.\r\n    call DoBufIn        ;Issue \"Read Capacity\" command.\r\n    jc  RqPLE           ;If error, post return code & exit.\r\n    mov eax,[edi]       ;Set \"swapped\" size in IOCTL packet.\r\n    call Swap32\r\n    mov [esi+1],eax\r\n    jmp RqATIX          ;Go post \"busy\" status and exit.\r\n;\r\n; IOCTL Input \"Audio Disk Info\" handler.\r\n;--- db ? ;lowest track number (binary)\r\n;--- db ? ;highest track number (binary)\r\n;--- dd ? ;start of lead-out track\r\n;\r\nReqADI:\r\n    mov al,0AAh         ;Specify \"lead-out\" session number.\r\n    call ReadTOC        ;Read disk table-of-contents (TOC).\r\n    jc  ReqErr          ;If error, post return code & exit.\r\n    mov [esi+3],eax     ;Set \"lead out\" LBA addr. in IOCTL.\r\n    mov ax,[edi+2]      ;Set first & last tracks in IOCTL.\r\n    mov [esi+1],ax\r\n    jmp RqATIX          ;Go post \"busy\" status and exit.\r\n;\r\n; IOCTL Input \"Audio Track Info\" handler.\r\n;--- db ? ;track number (binary)\r\n;--- dd ? ;start of track (red book format)\r\n;--- db ? ;track control info (ADR=low nibble, CONTROL=high nibble)\r\n;\r\nReqATI:\r\n    mov al,[esi+1]      ;Specify desired session (track) no.\r\n    call ReadTOC        ;Read disk table-of-contents (TOC).\r\n    jc  ReqErr          ;If error, post return code & exit.\r\n    mov [esi+2],eax     ;Set track LBA address in IOCTL.\r\n    mov al,[edi+5]\r\n    shl al,4\r\n    mov [esi+6],al\r\nRqATIX:\r\n    jmp ReadAudioSt     ;Go post \"busy\" status and exit.\r\n;\r\n; IOCTL Input \"Audio Q-Channel Info\" handler.\r\n;--- db ? ;+1 CONTROL (4-7) and ADR (0-3)\r\n;--- db ? ;+2 track number (in BCD?)\r\n;--- db ? ;+3 index (unchanged from disk)\r\n;--- running time within track (binary)\r\n;--- db ? ;+4 min\r\n;--- db ? ;+5 sec\r\n;--- db ? ;+6 frame\r\n;--- db ? ;+7 zero\r\n;--- running time on disk (binary)\r\n;--- db ? ;+8 min\r\n;--- db ? ;+9 sec\r\n;--- db ? ;+10 frame\r\n\r\n;--- the sub-channel data that is returned from the ATAPI device\r\n;--- consists of a 4-byte header and 12 bytes data,\r\n;--- the data's format depends on the format code:\r\n;---  01: CD-ROM current position\r\n;---  02: UPC/bar code\r\n;---  03: ISRC code \r\n;--- data header:\r\n;---  db ? ;+0 reserved\r\n;---  db ? ;+1 Audio Status\r\n;---  dw ? ;+2 sub-channel data length\r\n;--- data (CD-ROM current position):\r\n;---  db ? ;+4 data format code ( =01 )\r\n;---  db ? ;+5 ADR (bits 7-4) CONTROL (bits 3-0)\r\n;---  db ? ;+6 track number\r\n;---  db ? ;+7 index number\r\n;---  dd ? ;+8 absolute CD-ROM address\r\n;---  dd ? ;+12 track-relative CD-ROM address\r\n\r\nReqAQI:\r\n    mov ax,04010h       ;read sub-channel, SubQ=1, use 16-byte count.\r\n    call RdAST1         ;Read sub-channel\r\n    jc  ReqErr          ;If error, post return code (AL) & exit.\r\n    mov edx,[edi+5]     ;Set ctrl/track/index in IOCTL.\r\nif 1\r\n;--- xchg ADR and CONTROL\r\n    rol dl,4\r\n;--- convert TNO to BCD\r\n;--- ensure that conversion is done only when ADR is 1\r\n;    mov al,dl\r\n;    and al,0Fh\r\n;    cmp al,1\r\n;    jnz @F\r\n    mov al,dh\r\n    aam                 ;converts binary in AL to BCD in AH,AL\r\n    db 0d5h,10h         ;packs 2 BCD digits in AH,AL into AL\r\n    mov dh,al\r\n@@:\r\nendif\r\n    mov [esi+1],edx\r\n    mov eax,[edi+13]    ;Set time-on-track in IOCTL.\r\n    mov [esi+4],eax\r\n    mov edx,[edi+9]     ;Get time-on-disk & clear high\r\n    shl edx,8           ;  order time-on-track in IOCTL.\r\n    mov [esi+7],edx     ;Set time-on-disk\r\n    ret\r\n;\r\n; IOCTL Input \"Audio Status Info\" handler.\r\n;\r\nReqASI:\r\n    mov ax,04010h       ;Set SubQ=1, use 16-byte count.\r\n    call RdAST1         ;Read sub-channel\r\n    jc  ReqErr          ;If error, post return code & exit.\r\n    xor eax,eax         ;Reset starting audio address.\r\n    mov [esi+1],ax      ;Reset audio \"paused\" flag.\r\n    xor edx,edx         ;Reset ending audio address.\r\n    cmp @byte [edi+1],011h  ;Is drive now \"playing\" audio?\r\n    jne @F              ;No, check for audio \"pause\".\r\n    mov edi,[AudAP]     ;Point to drive's audio data.\r\n    mov eax,[edi.AUDADR.dwAudSta]     ;Get current audio \"start\" addr.\r\n    call ConvMSF\r\n    jmp RqASI2          ;Go get current audio \"end\" addr.\r\n@@:\r\n    cmp @byte [edi+1],012h  ;Is drive now in audio \"pause\"?\r\n    jne RqASI3              ;No, return \"null\" addresses.\r\n    or @byte [esi+1],1      ;Set audio \"paused\" flag.\r\n    mov eax,[edi+8]         ;Convert time-on-disk to LBA addr.\r\n    call Swap32\r\n;--- do not convert to LBA\r\n;    call ConvLBA\r\n    mov edi,[AudAP]     ;Point to drive's audio data.\r\nRqASI2:\r\n    push eax\r\n    mov eax,[edi.AUDADR.dwAudEnd]     ;Get current audio \"end\" address.\r\n    call ConvMSF\r\n    call Swap32\r\n    shr eax, 8\r\n    mov edx, eax\r\n    pop eax\r\nRqASI3:\r\n    mov [esi+3],eax ;Set audio \"start\" addr. in IOCTL.\r\n    mov [esi+7],edx ;Set audio \"end\" address in IOCTL.\r\n    ret\r\n;\r\n; Subroutine to read the current \"audio\" status and disk address.\r\n;\r\nReadAudioSt:\r\n    call ZPacket        ;Status only -- reset ATAPI packet.\r\n    mov ax,00004h       ;set SubQ=0, use 4-byte count.\r\nRdAST1:                 ;<--- entry ReqAQI, ReqASI (ax=4010h)\r\n;--- issue a \"read sub-channel\" packet command (42h)\r\n;--- MSF (bit 1 of byte 1) is 1\r\n;--- data format is 1 (=Audio Status)\r\n    mov @dword [Packet],001000242h  ;Set command bytes.\r\n    mov @byte [PktLBA],ah           ;Set SubQ flag (bit 6)\r\nRdAST2:\r\n    call DoBufIO                ;Issue \"Read Subchannel\" command.\r\n    jc  RdASTX                  ;If error, exit immediately.\r\n    cmp @byte [edi+1],011h      ;Is a \"play audio\" in progress?\r\n    jnz RdTOC1                  ;No, clear carry flag and exit.\r\nRdAST3:\r\n    push esi                    ;Save SI- and ES-regs.\r\n    mov esi,[dwRequest]         ;Reload DOS request-packet addr.\r\n    or [esi].RPH.wStat,RPBUSY   ;Set \"busy\" status bit.\r\n    pop esi\r\nRdASTX:\r\n    ret\r\n;\r\n; Subroutine to read disk \"Table of Contents\" (TOC) values.\r\n;\r\nReadTOC:\r\n    mov @word [Packet],00243h   ;Set TOC and MSF bytes.\r\n    mov [PktLH],al              ;Set desired \"session\" number.\r\n    mov al,12                   ;Get 12-byte \"allocation\" count.\r\n    call DoBufIO                ;Issue \"Read Table of Contents\" cmd.\r\n    jc  RdTOCX                  ;If error, exit immediately.\r\n    mov eax,[edi+8]             ;Return \"swapped\" starting address.\r\n    call Swap32\r\nRdTOC1:\r\n    clc         ;Clear carry flag (no error).\r\nRdTOCX:\r\n    ret\r\n    align 4\r\n;\r\n; Subroutine to convert an LBA sector number to \"RedBook\" MSF format.\r\n;\r\nConvMSF:\r\n    add eax,150     ;Add in offset.\r\n    push eax        ;Get address in DX:AX-regs.\r\n    pop ax\r\n    pop dx\r\n    mov cx,75       ;Divide by 75 \"frames\"/second.\r\n    div cx\r\n    shl eax,16      ;Set \"frames\" remainder in upper EAX.\r\n    mov al,dl\r\n    ror eax,16\r\n    mov cl,60       ;Divide quotient by 60 seconds/min.\r\n    div cl\r\n    ret             ;Exit -- EAX-reg. contains MSF value.\r\n\r\n;--- set device name (/D:xxxx)\r\n\r\nI_SetName proc    \r\n    mov edi,[dwBase]    ;Blank out device name.\r\n    add edi,10\r\n    lea edx,[edi+8]\r\n    mov eax,\"    \"\r\n    mov [edi+0],eax\r\n    mov [edi+4],eax\r\nI_NameB:\r\n    mov al,[esi]    ;Get next device-name byte.\r\n    cmp al,TAB      ;Is byte a \"tab\"?\r\n    je  I_NxtCJ     ;Yes, handle above, \"name\" has ended!\r\n    cmp al,' '      ;Is byte a space?\r\n    je  I_NxtCJ     ;Yes, handle above, \"name\" has ended!\r\n    cmp al,'/'      ;Is byte a slash?\r\n    je  I_NxtCJ     ;Yes, handle above, \"name\" has ended!\r\n    cmp al,0        ;Is byte the command-line terminator?\r\n    je  I_NxtCJ     ;Yes, go test for UltraDMA controller.\r\n    cmp al,LF       ;Is byte an ASCII line-feed?\r\n    je  I_NxtCJ     ;Yes, go test for UltraDMA controller.\r\n    cmp al,CR       ;Is byte an ASCII carriage-return?\r\n    je  I_NxtCJ     ;Yes, go test for UltraDMA controller.\r\n    cmp al,'a'      ;Ensure letters are upper-case.\r\n    jc  I_Name2\r\n    cmp al,'z'\r\n    ja  I_Name2\r\n    and al,0DFh\r\nI_Name2:\r\n    cmp al,'!'      ;Is this byte an exclamation point?\r\n    jz  I_Name3     ;Yes, store it in device name.\r\n    cmp al,'#'      ;Is byte below a pound-sign?\r\n    jb  I_Name4     ;Yes, Invalid!  Blank first byte.\r\n    cmp al,')'      ;Is byte a right-parenthesis or less?\r\n    jbe I_Name3     ;Yes, store it in device name.\r\n    cmp al,'-'      ;Is byte a dash?\r\n    jz  I_Name3     ;Yes, store it in device name.\r\n    cmp al,'0'      ;Is byte below a zero?\r\n    jb  I_Name4     ;Yes, invalid!  Blank first byte.\r\n    cmp al,'9'      ;Is byte a nine or less?\r\n    jbe I_Name3     ;Yes, store it in device name.\r\n    cmp al,'@'      ;Is byte below an \"at sign\"?\r\n    jb  I_Name4     ;Yes, invalid!  Blank first byte.\r\n    cmp al,'Z'      ;Is byte a \"Z\" or less?\r\n    jbe I_Name3     ;Yes, store it in device name.\r\n    cmp al,'^'      ;Is byte below a carat?\r\n    jb  I_Name4     ;Yes, invalid!  Blank first byte.\r\n    cmp al,'~'      ;Is byte above a tilde?\r\n    ja  I_Name4     ;Yes, invalid!  Blank first byte.\r\n    cmp al,'|'      ;Is byte an \"or\" symbol?\r\n    je  I_Name4     ;Yes, invalid!  Blank first byte.\r\nI_Name3:\r\n    stosb           ;Store next byte in device name.\r\n    inc esi         ;Bump command-line pointer.\r\n    cmp edi,edx     ;Have we stored 8 device-name bytes?\r\n    jb  I_NameB     ;No, go get next byte.\r\nI_NxtCJ:\r\n    ret\r\nI_Name4:\r\n    mov @byte [edx-8],' '\r\n    ret\r\nI_SetName endp    \r\n\r\n;--- handle /AX option\r\n\r\nI_SetAX proc\r\n    mov eax,offset UnSupp   ;Disable all unwanted dispatches.\r\n    mov [@RqPlay],eax       ; play audio\r\n    mov [@RqStop],eax       ; stop audio\r\n    mov [@RqRsum],eax       ; resume audio\r\n    mov [@RqCHL],eax        ; current head location\r\n    mov [@RqADI],eax        ; audio disk info\r\n    mov [@RqATI],eax        ; audio track info\r\n    mov [@RqAQI],eax        ; audio q-channel info\r\n    mov [@RqASI],eax        ; audio status info\r\n    mov eax,offset DOSSeek  ;Do only LBA-address DOS seeks.\r\n    mov [@RqPref],eax\r\n    mov [@RqSeek],eax\r\n    mov al,004h                     ; Have \"Device Status\" declare\r\n    mov @byte ds:[@Status],al       ; we handle DATA reads only\r\n\r\n;---  have it NOT update the IOCTL \"busy\" flag & return.\r\n;---  [\"ReadAudioSt\" gets DISMISSED]!\r\n\r\n    mov @byte ds:[@RqDSX], 0C3h\r\n    ret\r\nI_SetAX endp\r\n\r\n;--- for SATA controllers, check if AHCI mode is disabled\r\n;--- in: EAX=phys. address HBA\r\n\r\nIsAHCIdisabled proc uses edi ebx\r\n\r\n\t@dprintf <\"SATA controller at %X\",13,10>, eax\r\n\tmov edi, eax\r\n    push 0\r\n    push 1\r\n    push PR_SYSTEM\r\n    @VMMCall _PageReserve    ;allocate a 4 kB block of address space\r\n    add esp,3*4\r\n    cmp eax,-1\r\n    jz error\r\n    mov ebx, eax            ; save linear address in ebx\r\n    @dprintf <\"HBA mapped at %X\",13,10>, ebx\r\n\r\n    shr eax, 12             ;convert linear address to page number\r\n\r\n    push PC_INCR or PC_WRITEABLE\r\n    mov edx, edi\r\n    shr edx, 12\r\n    push edx\r\n    push 1\r\n    push eax\r\n    @VMMCall _PageCommitPhys ;backup address space\r\n    add esp,4*4\r\n\r\n\tand edi, 0fffh\r\n\tmov eax, [ebx+edi+4]\r\n\r\n\t@dprintf <\"HBA.GHC=%X\",13,10>, eax\r\n\r\n;--- todo: free address space\r\n\r\n\tand eax, eax\r\n\tjs error\r\n\tor eax, -1\r\n\tret\r\nerror:\r\n\txor eax, eax\r\n\tret\r\nIsAHCIdisabled endp\r\n\r\n;\r\n; Driver Initialization Routine.\r\n;\r\nI_Init proc\r\n    cld\r\n    mov esi,[dwRequest] ;Point to DOS request packet.\r\n    cmp [esi].RPINIT.bOp,0 ;Is this an \"Init\" packet?\r\n    jnz I_BadP\r\n    mov esi,[dwCmdLine] ;Point to command line that loaded us.\r\nI_NxtC:\r\n    lodsb           ;Get next command-line byte.\r\n    cmp al,0\r\n    je  I_Term\r\n    cmp al,LF\r\n    je  I_Term\r\n    cmp al,CR\r\n    je  I_Term\r\n    cmp al,'-'      ; a dash?\r\n    je  @F          ; Yes, see what next \"switch\" byte is.\r\n    cmp al,'/'      ; a slash?\r\n    jne I_NxtC      ; No, check next command-line byte.\r\n@@:\r\n    mov ax,[esi]    ;Get next 2 command-line bytes.\r\n    or  ax,2020h    ;convert to lower case\r\n    cmp ax,'xu'     ;/UX?\r\n    jne @F\r\n    inc esi         ;Bump pointer past \"UX\" switch.\r\n    inc esi\r\n    or [bFlags],FL_UX\r\n    jmp I_NxtC\r\n@@:\r\n    cmp al,'f'      ;/F?\r\n    jnz @F\r\n    inc esi\r\n    or [bFlags],FL_F\r\n    jmp I_NxtC\r\n@@:\r\n    cmp ax,'xa'     ;/AX?\r\n    jne @F\r\n    inc esi         ;Bump pointer past \"ax\"\r\n    inc esi\r\n    call I_SetAX\r\n    jmp I_NxtC\r\n@@:\r\nif DWRDIO\r\n    cmp ax,\"23\"     ;/32?\r\n    jnz @F\r\n    inc esi\r\n    inc esi\r\n    mov @dword ds:[@DWOP+0],6DF3E9D1h   ;shr ecx,1  rep insd\r\n    mov @word ds:[@DWOP+4],0C911h       ;adc ecx,ecx\r\n    jmp I_NxtC\r\n@@:\r\nendif\r\n    cmp al,'l'      ;/L?\r\n    jne @F\r\n    mov @byte [@DMALmt],009h  ;Set 640K \"DMA limit\" above.\r\n    inc esi         ;Bump pointer past \"limit\" switch.\r\n    jmp I_NxtC\r\n@@:\r\nif SETMODE\r\n    cmp al,'m'      ;Is this byte an \"M\" or \"m\"?\r\n    jne @F\r\n    inc esi         ;Bump pointer past \"mode\" switch.\r\n    cmp ah,'7'\r\n    ja  I_NxtC\r\n    sub ah,'0'\r\n    jb  I_NxtC\r\n    mov [MaxUM],ah  ;Set maximum UltraDMA \"mode\" above.\r\n    inc esi         ;Bump pointer past \"mode\" value.\r\n    jmp I_NxtC\r\n@@:\r\nendif\r\n\r\nI_ChkD:\r\n    cmp al,'d'      ;Is switch byte a \"D\" or \"d\"?\r\n    jne @F\r\n    inc esi         ;Bump pointer past \"device\" switch.\r\n    cmp ah,':'      ;Is following byte a colon?\r\n    jne I_NxtC\r\n    inc esi         ;Bump pointer past colon.\r\n    call I_SetName\r\n    jmp I_NxtC\r\n@@:\r\nif MWDMA\r\n    cmp al,'w'      ;/W?\r\n    jnz @F\r\n    or [bFlags],FL_W\r\n    inc esi\r\n    jmp I_NxtC\r\n@@:\r\nendif\r\n    cmp al,'q'\r\n    jnz I_NxtC\r\n    inc esi\r\n    or [bFlags],FL_Q\r\n    jmp I_NxtC\r\n\r\ngetpci:\r\n\tmov dx, 0cf8h\r\n\tout dx, eax\r\n\tadd dl, 4\r\n\tin eax, dx\r\n\tsub dl, 4\r\n\tret\r\nsetpci:\r\n\tmov dx, 0cf8h\r\n\tout dx, eax\r\n\tadd dl, 4\r\n\tmov eax, ecx\r\n\tout dx, eax\r\n\tsub dl, 4\r\n\tret\r\n\r\n;--- done cmdline parsing\r\n\r\nI_Term:\r\n\r\n\ttest [bFlags],FL_Q\r\n\tjnz @F\r\n    invoke printf, CStr('XCDROM32 ',VER,'.',CR,LF)\r\n@@:\r\n\r\n;--- check for PCI BIOS\r\n\r\n\tmov @word [ebp].Client_Reg_Struc.Client_EAX, 0B101h ; get PCI BIOS version\r\n\t@VMMCall Begin_Nest_Exec\r\n\tmov eax, 1Ah\r\n\t@VMMCall Exec_Int\r\n\t@VMMCall End_Nest_Exec\r\n    mov edx, [ebp].Client_Reg_Struc.Client_EDX\r\n\r\n    cmp edx,\" ICP\"          ;Do we have a V2.0C or newer PCI BIOS?\r\n    jne I_ChkNm             ;No, check for valid driver name.\r\n\r\n;--- scan for native/legacy IDE controllers\r\n;--- rewritten for v1.5\r\n\r\n\tmov edi, [pScan]\r\n\tmov esi, 80000008h\r\nI_FindC:\r\n\tmov eax, esi\r\n\tcall getpci\r\n\tshr eax, 8\r\n\tmov ebx, eax\r\n\tand al, 80h\r\n\tcmp eax, 10180h         ;storage+IDE+busmaster?\r\n\tjz isEIDE\r\n\tcmp ebx, 10601h         ;SATA controller?\r\n\tjz isSATA\r\nI_ContFindC:\r\n\t@VMMCall Yield\r\n\tadd esi, 100h\r\n\tcmp esi, 81000008h\r\n\tjb I_FindC\r\n\tjmp I_DoneScan\r\nisSATA:\r\n;--- for SATA controllers, check if AHCI is enabled\r\n\tmov eax, esi\r\n\tmov al, 16+5*4\r\n\tcall getpci\r\n\tand al, 0fch\r\n\tcall IsAHCIdisabled\r\n\tand eax, eax\r\n\tjz I_ContFindC          ;if AHCI active, ignore device\r\nisEIDE:\r\nif SETBM\r\n\tmov eax, esi\r\n\tmov al, 4\r\n\tcall getpci\r\n\tbts eax, 2              ;busmaster enabled?\r\n\tjc @F\r\n;--- enable busmaster\r\n\tmov ecx, eax\r\n\tmov eax, esi\r\n\tmov al, 4\r\n\tcall setpci\r\n@@:\r\nendif\r\n\r\n\tmov eax, esi\r\n\tmov al,16+4*4           ;Get DMA base address (register 4).\r\n\tcall getpci\r\n\tand al,0FCh\r\n\tmov ecx, eax\r\n\r\n\ttest bl, 1              ;primary in \"native\" mode?\r\n\tjnz prim_native\r\n\tmov ax, 1F0h\r\n\tbts dword ptr bLegacy, 0\r\n\tjnc @F\r\n\tor eax, -1\r\n\tjmp @F\r\nprim_native:\r\n\tmov eax, esi\r\n\tmov al,16+0*4           ;Get primary channel base port\r\n\tcall getpci\r\n\tand al,0FCh\r\n@@:\r\n\tmov [edi+0*sizeof IDEPARM].IDEPARM.wIDEBase, ax\r\n\tmov [edi+0*sizeof IDEPARM].IDEPARM.wDMABase, cx\r\n\tmov [edi+1*sizeof IDEPARM].IDEPARM.wIDEBase, ax\r\n\tmov [edi+1*sizeof IDEPARM].IDEPARM.wDMABase, cx\r\n\r\n\ttest bl, 4              ;secondary in \"native\" mode?\r\n\tjnz sec_native\r\n\tmov ax, 170h\r\n\tbts dword ptr bLegacy, 1\r\n\tjnc @F\r\n\tor eax, -1\r\n\tjmp @F\r\nsec_native:\r\n\tmov eax, esi\r\n\tmov al,16+2*4           ;Get secondary channel base port\r\n\tcall getpci\r\n\tand al,0FCh\r\n@@:\r\n\tadd ecx, 8\r\n\tmov [edi+2*sizeof IDEPARM].IDEPARM.wIDEBase, ax\r\n\tmov [edi+2*sizeof IDEPARM].IDEPARM.wDMABase, cx\r\n\tmov [edi+3*sizeof IDEPARM].IDEPARM.wIDEBase, ax\r\n\tmov [edi+3*sizeof IDEPARM].IDEPARM.wDMABase, cx\r\n\r\n\t@dprintf <\"controller primary base=%X, secondary base=%X, dmabase=%X\",13,10>, [edi].IDEPARM.wIDEBase, ax, [edi].IDEPARM.wDMABase\r\n\ttest [bFlags], FL_Q\r\n\tjnz @F\r\n\tmov eax, esi\r\n\tshr eax, 16\r\n\tmov ah, 0\r\n\tmov edx, esi\r\n\tshr edx, 11\r\n\tand edx, 1Fh\r\n\tmov ecx, esi\r\n\tshr ecx, 8\r\n\tand ecx, 7\r\n\tinvoke 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\r\n@@:\r\n\tadd edi, 4 * sizeof IDEPARM\r\n\tcmp edi, offset ScanLPM + sizeof ScanLPM\r\n\tjb I_ContFindC              ;continue PCI scan\r\nI_DoneScan:\r\n\tmov [pScan], edi\r\n\r\n;--- done controller scan\r\n\r\nI_ChkNm:\r\n\tcmp [pScan], offset ScanLPM ; any PCI IDE controller found?\r\n\tjnz @F\r\n\tmov ax, 1F0h                ; if no, check legacy ports\r\n\tmov [ScanLPM+0*sizeof IDEPARM].wIDEBase, ax\r\n\tmov [ScanLPM+1*sizeof IDEPARM].wIDEBase, ax\r\n\tmov ax, 170h\r\n\tmov [ScanLPM+2*sizeof IDEPARM].wIDEBase, ax\r\n\tmov [ScanLPM+3*sizeof IDEPARM].wIDEBase, ax\r\n\tadd [pScan], 4 * sizeof IDEPARM\r\n@@:\r\n\r\n    mov ecx,[dwBase]\r\n    cmp @byte [ecx+10],' '      ;Is driver \"name\" valid?\r\n    jnz @F\r\n    mov @dword [ecx+10],\"RDCX\"  ;Set our default \"name\".\r\n    mov @dword [ecx+10+4],\"$$MO\"\r\n@@:\r\n\ttest [bFlags],FL_Q\r\n\tjnz @F\r\n    mov eax,[ecx+10+0]\r\n    mov @dword [szDriver+0],eax\r\n    mov eax,[ecx+10+4]\r\n    mov @dword [szDriver+4],eax\r\n    invoke printf, CStr('Driver name is %s.',13,10), offset szDriver\r\n@@:\r\n    \r\n    mov esi, offset DmaAdr\r\n    mov ecx, 4*4\r\n    xor edx, edx\r\n    VxDCall VDMAD_Lock_DMA_Region\r\n    mov [PRDAd],edx             ;Set physical PRD address.\r\n\r\nif 0\r\n    test [bFlags],FL_UX         ;Did user disable UltraDMA?\r\n    jnz I_LinkX                 ;Yes, proceed\r\n    cmp @byte [@DMALmt],-1      ;Is UltraDMA limited to < 640K?\r\n    je  I_LinkX                 ;No, proceed\r\n    mov edx,offset LEMsg        ;Point to \"/L Invalid\" message.\r\n    cmp @word [DmaAdr+2],009h   ;Are we loaded high?\r\n    ja  I_InitE                 ;Yes?  Display message and exit!\r\nI_LinkX:\r\nendif\r\n\r\n    @dprintf <\"Start drive scan\",CR,LF>\r\n    mov [pUTbl], offset UnitTbl ;Reset our unit-table pointer.\r\n\r\nI_ScanU:                    ;<--- try next\r\n    mov ebx,[ScanX]         ;Get current parameters index.\r\n    cmp ebx,[pScan]         ;Any more IDE units to check?\r\n    je  I_AnyCD             ;No, check for any drives to use.\r\n    add [ScanX], sizeof IDEPARM\r\n\r\n    mov ax,[ebx].IDEPARM.wIDEBase;Get unit's IDE address\r\n    cmp ax,-1               ;valid address?\r\n    je  I_ScanU             ;no, skip this entry\r\n\r\n    call I_ValDV            ;check if device is an ATAPI CD-ROM\r\n    jnc  I_ScnVN            ;if yes, display device and update UnitTbl\r\nif ?EXTDISP\r\n\tinvoke printf, CStr(\"port %X,dev=%X: v86 ss:sp=%X:%X, %s\",13,10),\r\n\t\t[ebx].IDEPARM.wIDEBase,\r\n\t\t@word [ebx].IDEPARM.bDevSel,\r\n\t\t[ebp].Client_Reg_Struc.Client_SS,\r\n\t\t[ebp].Client_Reg_Struc.Client_ESP, edi\r\nelse\r\n\t@VMMCall Yield           ;don't remove, may avoid v86 stack errors\r\nendif\r\n    jmp I_ScanU             ;try next unit.\r\nI_ScnVN:\r\n    test [bFlags],FL_UX     ;Was the /UX switch given?\r\n    je  @F\r\n    or  @byte [ebx].UPARM.wDMABase,1   ;Post drive \"DMA disabled\".\r\n@@:\r\n\ttest [bFlags],FL_Q\r\n\tjnz nodisp\r\n\r\n    mov edi,offset DrvName + sizeof DrvName\r\n@@:\r\n    mov @byte [edi],0       ; skip trailing spaces of drive name\r\n    dec edi\r\n    cmp @byte [edi],' '\r\n    je  @B\r\n\r\n    mov edx, CStr(\"Master\")\r\n    test [ebx].IDEPARM.bDevSel,10h\r\n    jz  @F\r\n    mov edx, CStr(\"Slave\")\r\n@@:\r\n\tinvoke printf, CStr(\"Unit %u: %s, IDE/DMA ports %X/%X, %s, \"),\r\n\t\tUnitNo, edx, [ebx].IDEPARM.wIDEBase, [ebx].IDEPARM.wDMABase, offset DrvName\r\n\tmov ecx,CStr(\"PIO mode\",13,10)\r\n\ttest @byte [ebx].IDEPARM.wDMABase,7  ;Will drive be using UltraDMA?\r\n\tjnz @F\r\n\tmov ecx, CStr(\"ATA-%u\",13,10)\r\n@@:\r\n\tinvoke printf, ecx, wDmaMode\r\nif ?EXTDISP\r\n\tinvoke printf, CStr(\"v86 ss:sp=%X:%X\",13,10),[ebp].Client_Reg_Struc.Client_SS,\r\n\t\t[ebp].Client_Reg_Struc.Client_ESP\r\nendif\r\nnodisp:\r\n\r\n    mov esi,[pUTbl]         ;Update all unit-table parameters.\r\n    mov ax,[ebx].IDEPARM.wIDEBase\r\n    mov cx,[ebx].IDEPARM.wDMABase\r\n    mov dl,[ebx].IDEPARM.bDevSel\r\n    mov [esi].UPARM.wIDEBase,ax\r\n    mov [esi].UPARM.wDMABase,cx\r\n    mov [esi].UPARM.bDevSel,dl\r\n    add esi,sizeof UPARM    ;Update unit-table pointer.\r\n    mov [pUTbl],esi\r\n\r\n;   inc @byte [Units]       ;Bump number of active units.\r\n    mov eax, [dwBase]\r\n    inc [eax].DOSDRV.bUnits\r\n\r\n    inc [UnitNo]            ;Bump display unit number.\r\n    cmp esi,offset UnitTbl + sizeof UnitTbl ;end of table reached?\r\n    jb  I_ScanU             ;no, loop back & check for more.\r\nI_AnyCD:\r\n\r\n    @dprintf <\"End drive scan\",CR,LF>\r\n\r\n;   cmp [Units],0           ;Do we have any CD-ROM drives to use?\r\n    mov edx, [dwBase]\r\n    cmp [edx].DOSDRV.bUnits,0 ;Do we have any CD-ROM drives to use?\r\n\r\n    ja  I_ClrSt             ;Yes, success\r\n    mov edx, CStr('No CD-ROM drive to use')\r\nI_InitE:\r\n    invoke printf, CStr('%s; XCDROM32 not loaded!',CR,LF), edx\r\nI_BadP:\r\n    xor eax,eax             ;Get \"null\" length & error flags.\r\n    mov dx,RPDON+RPERR\r\n    jmp I_Exit              ;Go set \"init\" packet values & exit.\r\nI_ClrSt:\r\n\r\nif 0\r\n\tinvoke printf, CStr(\"xcdrom32: v86 ss:sp=%X:%X\",CR,LF),\r\n\t\t[ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP\r\nendif\r\n\r\n    mov dx,RPDON            ;Get initialization \"success\" code.\r\n    mov ax,12h+4+SIZERMCODE+5\r\nI_Exit:\r\n    mov esi,[dwRequest]     ;Set result values in \"init\" packet.\r\n    mov [esi].RPINIT.RPSize,ax\r\n    mov [esi].RPINIT.wStat,dx\r\n    mov [esi].RPINIT.bUnit,0\r\n    mov eax, edx\r\n    ret\r\nI_Init endp\r\n;\r\n; Subroutine to \"validate\" an IDE unit as an ATAPI CD-ROM drive.\r\n; in: ebx = IDEPARM\r\n; ebx must be preserved!\r\n;\r\nI_ValDV proc\r\n\r\n    call StopDMA        ;Stop previous DMA & select drive.\r\n    mov edi,CStr('Device select timeout')\r\n    call TestTO         ;Await controller-ready (sets DX to ATA cmd port)\r\n    jc  I_Val7          ;If timeout, go post pointer & exit.\r\n\r\n;--- check if BSY and DRQ are 0; bit 6 (device ready ) should be 1,\r\n;--- but this is nothing to rely on...\r\n    and al, BSY or DRQ  ;check BSY+DRQ, both must be 0\r\n    jnz I_Val7\r\n\r\nif IDENTPKT\r\n    @dprintf <\"ValDV: ready, base=%X, executing Identify Packet command\",CR,LF>, [ebx].UPARM.wIDEBase\r\n    mov al,0A1h         ;Issue \"Identify Packet Device\" cmd.\r\nelse\r\n    @dprintf <\"ValDV: ready, base=%X, executing Identify command\",CR,LF>, [ebx].UPARM.wIDEBase\r\n    mov al,0ECh         ;Issue \"Identify Device\" cmd.\r\nendif\r\n    out dx,al\r\n    call TestTO         ;Await controller-ready.\r\n    mov edi, CStr('Identify Device error')\r\nifdef _DEBUG\r\n    pushfd\r\n    lahf\r\n    @dprintf <\"ValDV: Identify Packet command returned, ax=%X\",CR,LF>, ax\r\n    popfd\r\nendif\r\n    jc  I_Val7          ;If timeout, go post pointer & exit.\r\n    test al,ERR         ;non-ATAPI devices will respond with ERR set\r\n    jnz I_Val6\r\n\r\nif 1\r\n\t@dprintf <\"ValDV, identify ok, al=%X, port=%X\",CR,LF>, eax, [ebx].UPARM.wIDEBase\r\nelse\r\n\tinvoke printf, CStr(\"ValDV, identify ok, al=%X, port=%X\",CR,LF), eax, [ebx].UPARM.wIDEBase\r\nendif\r\n\r\n    sub edx,7           ;Point back to IDE data register.\r\n    mov ecx,90\r\n    sub esp,ecx\r\n    sub esp,ecx\r\n    mov edi,esp\r\n    rep insw\r\n    mov ecx,256-90      ;skip the rest\r\n@@:\r\n    in  ax,dx\r\n    loop @B\r\n    \r\n    mov edi,offset DrvName ;Point to drive \"name\" input buffer.\r\n    mov cl,20           ;Read & swap words 27-46 into buffer.\r\n    lea esi,[esp+27*2]  ;(Manufacturer \"name\" of this drive).\r\n@@:\r\n    lodsw\r\n    xchg al,ah\r\n    stosw\r\n    loop @B\r\n\r\n    mov esi,esp\r\n    mov al,[esi+53*2]\r\n    mov [UFlag],al      ;Save UltraDMA \"valid\" flags.\r\n    mov dx,[esi+88*2]\r\n    mov [UMode],dh      ;Save current UltraDMA \"mode\" value.\r\n                        ;\"supported\" UDMA \"modes\" is in DL\r\n\r\n    mov ax,[esi+0*2]    ;Get I.D. word 0, main device flags.\r\nif MWDMA\r\n    mov ch,[esi+63*2+1] ;copy multiword flags to CH\r\nendif    \r\n    add esp,90*2        ;restore ESP\r\n    \r\n    and ax,0DF03h       ;Mask off flags for an ATAPI CD-ROM.\r\n    cmp ax,08500h       ;Do device flags say \"ATAPI CD-ROM\"?\r\n    je  I_Val9          ;Yes, see about UltraDMA use.\r\nI_Val6:\r\n    mov edi, CStr(\"No ATAPI CD-ROM\")\r\nI_Val7:\r\n    stc                 ;Set carry flag on (error!).\r\nI_Val8:\r\n    ret\r\nI_Val9:\r\n\r\n    @dprintf <\"ATAPI CDROM detected\",CR,LF>\r\n\r\n    test @byte [ebx].UPARM.wDMABase,7  ;Will we be using UltraDMA?\r\n    jnz I_Val8                      ;No, go exit above.\r\n    test @byte [UFlag], 4           ;Valid UltraDMA \"mode\" bits?\r\n    jz  noudma                      ;No, jump\r\n\r\nif SETMODE\r\n    cmp [MaxUM],-1      ;/Mn switch used?\r\n    jz nomopt\r\n    movzx edx,dl        ;supported UDMA modes in EDX now\r\n    bsr eax, edx\r\n    jz nomopt\r\n    mov dl,[MaxUM]\r\n    cmp dl,al           ;supported mode < /Mn?\r\n    jb @F\r\n    mov dl,al\r\n@@:\r\n    bts @word [UMode],dx\r\n    mov ah,dl\r\n    mov dx,[ebx].UPARM.wIDEBase    ;Point to IDE \"features\" register.\r\n    inc edx\r\n    mov al,SETM         ;Set mode-select subcode.\r\n    out dx,al\r\n    inc edx             ;Point to IDE sector-count register.\r\n    mov al,ah           ;Set desired UltraDMA mode.\r\n    or  al,040h\r\n    out dx,al\r\n    add edx,5           ;Point to IDE command register.\r\n    mov al,SETF         ;Issue \"set features\" command.\r\n    out dx,al\r\n    call TestTO         ;Await controller-ready.\r\n    test al, ERR\r\n    jz nomopt\r\n    invoke printf, CStr('UltraDMA Set Mode failed',CR,LF)\r\nnomopt:\r\nendif\r\n    mov edi,offset UModes\r\n    mov al,[UMode]              ;Get UltraDMA \"mode\" value.\r\n    or  al,al                   ;Can drive do mode 0 minimum?\r\n    jnz I_Val11                 ;Yes, set up UltraDMA mode 0.\r\nnoudma:\r\nif MWDMA\r\n    test [bFlags],FL_W          ;handle multiword DMA devices?\r\n    jz @F\r\n    mov [MaxUM],-1\r\n    mov al,ch\r\n    mov edi,offset MWModes\r\n    or al,al                    ;is a MW-DMA mode set?\r\n    jnz I_Val11\r\n@@:\r\nendif\r\nI_Val10:\r\n    or  @byte [ebx].UPARM.wDMABase,1   ;Post drive \"DMA disabled\".\r\n    ret                         ;Exit -- must use \"PIO mode\"!\r\nI_Val11:\r\n    movzx eax,al\r\n    bsr ecx, eax\r\n    cmp cl,[MaxUM]\r\n    jbe @F\r\n    mov cl,[MaxUM]\r\n@@:\r\n    mov ax,@word [ecx*2+edi]\r\n    mov wDmaMode, ax\r\n    clc\r\n    ret\r\nI_ValDV endp\r\n\r\n\r\nDllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n    mov eax,dwReason\r\n    cmp eax, 1\r\n    jnz done\r\n;   .if (dwReason == 1)\r\n\r\n        mov esi, dwRes\r\n        test [esi].JLCOMM.wFlags, JLF_DRIVER\r\n        jz failed\r\n        movzx ecx,[esi].JLCOMM.wLdrCS\r\n        shl ecx, 4\r\n        mov dwBase, ecx\r\n        lea eax, [ecx+REQOFS]\r\n        mov pRequest, eax\r\n        mov eax,[esi].JLCOMM.lpCmdLine\r\n        mov dwCmdLine, eax\r\n        mov eax,[esi].JLCOMM.lpRequest\r\n        mov dwRequest, eax\r\n\r\n        mov esi, offset DevInt\r\n        xor edx, edx\r\n        @VMMCall Allocate_V86_Call_Back\r\n        jc failed\r\n\r\n        mov edi, dwBase\r\n        mov [edi].DOSDRV.wAttr, 0C800h    ;driver attributes\r\n        mov [edi].DOSDRV.ofsStr, REQOFS+4\r\n        mov [edi].DOSDRV.ofsInt, REQOFS+4+SIZERMCODE\r\n        mov [edi].DOSDRV.wRes1, 0\r\n        mov [edi].DOSDRV.bRes2, 0\r\n        mov [edi].DOSDRV.bUnits, 0\r\n\r\n        add edi, REQOFS+4\r\n        mov esi, offset rmcode\r\n        mov ecx, SIZERMCODE+1\r\n        rep movsb\r\n        stosd\r\n\r\n;--- set EBP to the client pointer before calling I_Init\r\n\r\n        push ebp\r\n        @VMMCall Get_Cur_VM_Handle\r\n        mov ebp,[ebx].cb_s.CB_Client_Pointer\r\nif SAVESTAT\r\n        sub esp, sizeof Client_Reg_Struc\r\n        mov edi, esp\r\n        @VMMCall Save_Client_State\r\nendif\r\n\r\n        call I_Init\r\n\r\n        cmp ax,RPDON\r\n        setz al\r\nif SAVESTAT\r\n        movzx edi,al\r\n        mov esi, esp\r\n        @VMMCall Restore_Client_State\r\n        add esp, sizeof Client_Reg_Struc\r\n        mov eax, edi\r\nelse\r\n        movzx eax, al\r\nendif\r\n        pop ebp\r\n;    .endif\r\ndone:\r\n    ret\r\nfailed:\r\n    xor eax, eax\r\n    ret\r\n\r\nDllMain endp\r\n\r\n    end DllMain\r\n\r\n"
  },
  {
    "path": "JLM/XCDROM32/XCDROM32.txt",
    "content": "\r\n 1. About XCDROM32\r\n\r\n XCDROM32 is a JLM based on Jack R. Ellis' XCDROM Ultra-DMA CD-ROM driver.\r\n It supports PCI IDE controllers running in \"legacy\" or \"native\" mode.\r\n\r\n\r\n 2. Usage\r\n\r\n To load the driver, add the following line to your CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE XCDROM32.DLL [options]\r\n\r\n Options are:\r\n\r\n  /32  use 32bit-IO in \"PIO mode\". This might not work with all devices,\r\n       but if it does, it usually gives a speed boost for that mode.\r\n  /AX  disable \"audio\" functions.\r\n  /D:name  set device name. If this option is missing, the default device\r\n       name is \"XCDROM$$\"\r\n  /F   \"fast\", uses DMA \"scatter/gather\" lists to allow transfers which \r\n       cross a physical 64 kB address boundary. Might not work with all\r\n       controllers.\r\n  /L   limits DMA to \"low memory\" below 640 kB.\r\n  /Mn  set/restrict UDMA mode to n (0 <= n <= 7).\r\n  /Q   quiet mode.\r\n  /UX  disables UltraDMA for all drives and uses \"PIO mode\" generally.\r\n  /W   handle (non-UDMA) drives which are capable of Multiword-DMA.\r\n\r\n\r\n 3. Details\r\n\r\n  XCDROM32 has 3 modes of operation:\r\n\r\n  1. \"direct\" mode: the target address is used for DMA input. This usually\r\n     is the fastest way and is used whenever possible.\r\n  2. \"buffered\" mode: the VDS DMA buffer is used for DMA input, then the\r\n     content is copied to the target address. This mode is used if \"direct\r\n     mode\" can't be used because target address is not aligned properly\r\n     or - if option /F isn't set - because the target region crosses a 64 kB\r\n     boundary. On modern CPUs the additional time needed for the memory copy \r\n     should be negligible.\r\n  3. \"PIO\" mode: input is done without DMA. This mode should work with any\r\n     IDE controller and CDROM/DVD. It is used if\r\n     - /UX option is set.\r\n     - the IDE controller doesn't support PCI Busmaster DMA.\r\n     - the CD-ROM/DVD device is not in UDMA mode or /W option is set, but\r\n       CD-ROM/DVD device has no valid Multiword-DMA mode set.\r\n     - \"buffered mode\" cannot be used because VDS DMA buffer isn't\r\n       accessible or is too small.\r\n\r\n\r\n  4. License\r\n\r\n  XCDROM32 is released under the GNU GPL v2. See GNU_GPL.TXT for details.\r\n\r\n"
  },
  {
    "path": "JLM/XDMA32/History.txt",
    "content": "\r\n  10/2022: v1.5\r\n\r\n   - ensure that busmaster flag in PCI command register is set.\r\n   - created .data segment with variables to separate them from code.\r\n   - removed obsolete /P option.\r\n   - fixed: under certain conditions, an IDE controllers might not have\r\n     been detected.\r\n \r\n  08/2022: v1.4\r\n\r\n   - fixed: option /M was implemented in such a way that it became obligatory\r\n     to set it to make the driver work.\r\n\r\n  07/26/2011: v1.3\r\n\r\n   - to determine the number of HDs in the system, an Int 13h, ah=08\r\n     is issued. BIOS variable 0040:0075 is ignored.\r\n\r\n  04/20/2011: v1.2\r\n\r\n   - bugfix: HDs attached to the \"native\" secondary channel were\r\n     ignored.\r\n\r\n  12/28/2007: v1.1\r\n\r\n   - bugfix: HDs attached to the \"legacy\" secondary channel didn't\r\n     work (wrong DMA ports).\r\n   - options /B, /M, /W and /P added, /UF option changed to /F.\r\n   - now max. 8 HDs are supported.\r\n   - usage of Jemm's new export 'MoveMemory' reduces interrupt latency,\r\n     and also makes this version incompatible with Jemm v5.68 and below.\r\n\r\n  12/03/2007: v1.0\r\n\r\n   - initial. Ported from XDMA v3.3. Added support for both \"legacy\"\r\n     and \"native\" IDE controllers.\r\n"
  },
  {
    "path": "JLM/XDMA32/IDENTIFY.INC",
    "content": "\r\n;--- data returned by Identify Device from ATA disks\r\n;--- it's 256 * 16-bit words.\r\n\r\nIDENTIFY_DEVICE_DATA struct \r\nif 0\r\n  struct {\r\n    USHORT Reserved1 : 1;\r\n    USHORT Retired3 : 1;\r\n    USHORT ResponseIncomplete : 1;\r\n    USHORT Retired2 : 3;\r\n    USHORT FixedDevice : 1;\r\n    USHORT RemovableMedia : 1;\r\n    USHORT Retired1 : 7;\r\n    USHORT DeviceType : 1;\r\n  } GeneralConfiguration;\r\nendif\r\n  GeneralConfiguration dw ?\t\t;0\r\n  NumCylinders dw ?\t\t\t\t;1\r\n  SpecificConfiguration dw ?\t;2\r\n  NumHeads dw ?\t\t\t\t\t;3\r\n  Retired1 dw 2 dup (?);        ;4-5 \r\n  NumSectorsPerTrack dw ?\t\t;6\r\n  VendorUnique1 dw 3 dup (?)\t;7-9\r\n  SerialNumber db 20 dup (?)    ;10-19\r\n  Retired dw 2 dup (?)\t\t\t;20-21\r\n  Obsolete1 dw ?                ;22\r\n  FirmwareRevision db 8 dup (?) ;23-26\r\n  ModelNumber db 40 dup (?)     ;27-46\r\n  MaximumBlockTransfer db ?     ;47\r\n  VendorUnique2 db ?\r\nif 0\r\n  struct {\r\n    USHORT FeatureSupported : 1;\r\n    USHORT Reserved : 15;\r\n  } TrustedComputing;\r\nendif\r\n  TrustedComputing dw ?         ;48 \r\nif 0\r\n  struct {\r\n    UCHAR  CurrentLongPhysicalSectorAlignment : 2;\r\n    UCHAR  ReservedByte49 : 6;\r\n    UCHAR  DmaSupported : 1;\r\n    UCHAR  LbaSupported : 1;\r\n    UCHAR  IordyDisable : 1;\r\n    UCHAR  IordySupported : 1;\r\n    UCHAR  Reserved1 : 1;\r\n    UCHAR  StandybyTimerSupport : 1;\r\n    UCHAR  Reserved2 : 2;\r\n    USHORT ReservedWord50;\r\n  } Capabilities;\r\nendif\r\n  Capabilities dw ?,?\t;49-50\r\n  ObsoleteWords51 dw ?,?;51-52\r\nif 0\r\n  ; bit 0: ???\r\n  ; bit 1: field 64 valid (advanced PIO modes)\r\n  ; bit 2: field 88 valid (UDMA modes)\r\n  USHORT TranslationFieldsValid : 3;\r\n  USHORT Reserved3 : 5;\r\n  USHORT FreeFallControlSensitivity : 8;\r\nendif\r\n  TranslationFieldsValid db ?      ;53\r\n  FreeFallControlSensitivity db ?\r\n  \r\n  NumberOfCurrentCylinders dw ? ;54\r\n  NumberOfCurrentHeads dw ? ;55\r\n  CurrentSectorsPerTrack dw ? ;56\r\n  CurrentSectorCapacity dd ?  ;57-58\r\n  CurrentMultiSectorSetting db ? ;59\r\nif 0\r\n  UCHAR  MultiSectorSettingValid : 1;\r\n  UCHAR  ReservedByte59 : 3;\r\n  UCHAR  SanitizeFeatureSupported : 1;\r\n  UCHAR  CryptoScrambleExtCommandSupported : 1;\r\n  UCHAR  OverwriteExtCommandSupported : 1;\r\n  UCHAR  BlockEraseExtCommandSupported : 1;\r\nendif\r\n  MultiSectorSettingValid equ 0\r\n  SupportedFlags db ?\r\n \r\n  UserAddressableSectors dd ? ;60-61\r\n  ObsoleteWord62 dw ?;62\r\n  MultiWordDMASupport db ?;63\r\n  MultiWordDMAActive db ?;\r\n\r\n  AdvancedPIOModes db ? ;64\r\n  ReservedByte64 db ?;\r\n\r\n  MinimumMWXferCycleTime dw ?;65\r\n  RecommendedMWXferCycleTime dw ?;66\r\n  MinimumPIOCycleTime dw ?;67\r\n  MinimumPIOCycleTimeIORDY dw ?;68\r\nif 0\r\n  struct {\r\n    USHORT ZonedCapabilities : 2;\r\n    USHORT NonVolatileWriteCache : 1;\r\n    USHORT ExtendedUserAddressableSectorsSupported : 1;\r\n    USHORT DeviceEncryptsAllUserData : 1;\r\n    USHORT ReadZeroAfterTrimSupported : 1;\r\n    USHORT Optional28BitCommandsSupported : 1;\r\n    USHORT IEEE1667 : 1;\r\n    USHORT DownloadMicrocodeDmaSupported : 1;\r\n    USHORT SetMaxSetPasswordUnlockDmaSupported : 1;\r\n    USHORT WriteBufferDmaSupported : 1;\r\n    USHORT ReadBufferDmaSupported : 1;\r\n    USHORT DeviceConfigIdentifySetDmaSupported : 1;\r\n    USHORT LPSAERCSupported : 1;\r\n    USHORT DeterministicReadAfterTrimSupported : 1;\r\n    USHORT CFastSpecSupported : 1;\r\n  } AdditionalSupported;\r\nendif\r\n  ExtendedUserAddressableSectorsSupported equ 3\r\n  AdditionalSupported dw ?;69\r\n  ReservedWords70 dw 5 dup (?);70-74\r\nif 0\r\n  USHORT QueueDepth : 5;\r\n  USHORT ReservedWord75 : 11;\r\nendif\r\n  QueueDepth dw ? ;75\r\nif 0\r\n  struct {\r\n    USHORT Reserved0 : 1;\r\n    USHORT SataGen1 : 1;\r\n    USHORT SataGen2 : 1;\r\n    USHORT SataGen3 : 1;\r\n    USHORT Reserved1 : 4;\r\n    USHORT NCQ : 1;\r\n    USHORT HIPM : 1;\r\n    USHORT PhyEvents : 1;\r\n    USHORT NcqUnload : 1;\r\n    USHORT NcqPriority : 1;\r\n    USHORT HostAutoPS : 1;\r\n    USHORT DeviceAutoPS : 1;\r\n    USHORT ReadLogDMA : 1;\r\n    USHORT Reserved2 : 1;\r\n    USHORT CurrentSpeed : 3;\r\n    USHORT NcqStreaming : 1;\r\n    USHORT NcqQueueMgmt : 1;\r\n    USHORT NcqReceiveSend : 1;\r\n    USHORT DEVSLPtoReducedPwrState : 1;\r\n    USHORT Reserved3 : 8;\r\n  } SerialAtaCapabilities;\r\nendif\r\n  SerialAtaCapabilities dw ?,?;76-77\r\nif 0\r\n  struct {\r\n    USHORT Reserved0 : 1;\r\n    USHORT NonZeroOffsets : 1;\r\n    USHORT DmaSetupAutoActivate : 1;\r\n    USHORT DIPM : 1;\r\n    USHORT InOrderData : 1;\r\n    USHORT HardwareFeatureControl : 1;\r\n    USHORT SoftwareSettingsPreservation : 1;\r\n    USHORT NCQAutosense : 1;\r\n    USHORT DEVSLP : 1;\r\n    USHORT HybridInformation : 1;\r\n    USHORT Reserved1 : 6;\r\n  } SerialAtaFeaturesSupported;\r\nendif\r\n  SerialAtaFeaturesSupported dw ?;78\r\nif 0\r\n  struct {\r\n    USHORT Reserved0 : 1;\r\n    USHORT NonZeroOffsets : 1;\r\n    USHORT DmaSetupAutoActivate : 1;\r\n    USHORT DIPM : 1;\r\n    USHORT InOrderData : 1;\r\n    USHORT HardwareFeatureControl : 1;\r\n    USHORT SoftwareSettingsPreservation : 1;\r\n    USHORT DeviceAutoPS : 1;\r\n    USHORT DEVSLP : 1;\r\n    USHORT HybridInformation : 1;\r\n    USHORT Reserved1 : 6;\r\n  } SerialAtaFeaturesEnabled;\r\nendif\r\n  SerialAtaFeaturesEnabled dw ?;79\r\n  MajorRevision dw ?;80\r\n  MinorRevision dw ?;81\r\nif 0\r\n  struct {\r\n    USHORT SmartCommands : 1;\r\n    USHORT SecurityMode : 1;\r\n    USHORT RemovableMediaFeature : 1;\r\n    USHORT PowerManagement : 1;\r\n    USHORT Reserved1 : 1;\r\n    USHORT WriteCache : 1;\r\n    USHORT LookAhead : 1;\r\n    USHORT ReleaseInterrupt : 1;\r\n    USHORT ServiceInterrupt : 1;\r\n    USHORT DeviceReset : 1;\r\n    USHORT HostProtectedArea : 1;\r\n    USHORT Obsolete1 : 1;\r\n    USHORT WriteBuffer : 1;\r\n    USHORT ReadBuffer : 1;\r\n    USHORT Nop : 1;\r\n    USHORT Obsolete2 : 1;\r\n    USHORT DownloadMicrocode : 1;\r\n    USHORT DmaQueued : 1;\r\n    USHORT Cfa : 1;\r\n    USHORT AdvancedPm : 1;\r\n    USHORT Msn : 1;\r\n    USHORT PowerUpInStandby : 1;\r\n    USHORT ManualPowerUp : 1;\r\n    USHORT Reserved2 : 1;\r\n    USHORT SetMax : 1;\r\n    USHORT Acoustics : 1;\r\n    USHORT BigLba : 1;\r\n    USHORT DeviceConfigOverlay : 1;\r\n    USHORT FlushCache : 1;\r\n    USHORT FlushCacheExt : 1;\r\n    USHORT WordValid83 : 2;\r\n    USHORT SmartErrorLog : 1;\r\n    USHORT SmartSelfTest : 1;\r\n    USHORT MediaSerialNumber : 1;\r\n    USHORT MediaCardPassThrough : 1;\r\n    USHORT StreamingFeature : 1;\r\n    USHORT GpLogging : 1;\r\n    USHORT WriteFua : 1;\r\n    USHORT WriteQueuedFua : 1;\r\n    USHORT WWN64Bit : 1;\r\n    USHORT URGReadStream : 1;\r\n    USHORT URGWriteStream : 1;\r\n    USHORT ReservedForTechReport : 2;\r\n    USHORT IdleWithUnloadFeature : 1;\r\n    USHORT WordValid : 2;\r\n  } CommandSetSupport;\r\nendif\r\n  CommandSetSupport dw ?,?,?;82-84\r\nif 0\r\n  struct {\r\n    USHORT SmartCommands : 1;\r\n    USHORT SecurityMode : 1;\r\n    USHORT RemovableMediaFeature : 1;\r\n    USHORT PowerManagement : 1;\r\n    USHORT Reserved1 : 1;\r\n    USHORT WriteCache : 1;\r\n    USHORT LookAhead : 1;\r\n    USHORT ReleaseInterrupt : 1;\r\n    USHORT ServiceInterrupt : 1;\r\n    USHORT DeviceReset : 1;\r\n    USHORT HostProtectedArea : 1;\r\n    USHORT Obsolete1 : 1;\r\n    USHORT WriteBuffer : 1;\r\n    USHORT ReadBuffer : 1;\r\n    USHORT Nop : 1;\r\n    USHORT Obsolete2 : 1;\r\n    USHORT DownloadMicrocode : 1;\r\n    USHORT DmaQueued : 1;\r\n    USHORT Cfa : 1;\r\n    USHORT AdvancedPm : 1;\r\n    USHORT Msn : 1;\r\n    USHORT PowerUpInStandby : 1;\r\n    USHORT ManualPowerUp : 1;\r\n    USHORT Reserved2 : 1;\r\n    USHORT SetMax : 1;\r\n    USHORT Acoustics : 1;\r\n    USHORT BigLba : 1;\r\n    USHORT DeviceConfigOverlay : 1;\r\n    USHORT FlushCache : 1;\r\n    USHORT FlushCacheExt : 1;\r\n    USHORT Resrved3 : 1;\r\n    USHORT Words119_120Valid : 1;\r\n    USHORT SmartErrorLog : 1;\r\n    USHORT SmartSelfTest : 1;\r\n    USHORT MediaSerialNumber : 1;\r\n    USHORT MediaCardPassThrough : 1;\r\n    USHORT StreamingFeature : 1;\r\n    USHORT GpLogging : 1;\r\n    USHORT WriteFua : 1;\r\n    USHORT WriteQueuedFua : 1;\r\n    USHORT WWN64Bit : 1;\r\n    USHORT URGReadStream : 1;\r\n    USHORT URGWriteStream : 1;\r\n    USHORT ReservedForTechReport : 2;\r\n    USHORT IdleWithUnloadFeature : 1;\r\n    USHORT Reserved4 : 2;\r\n  } CommandSetActive;\r\nendif\r\n  CommandSetActive dw ?,?,?; 85-87\r\n  UltraDMASupport db ?; 88\r\n  UltraDMAActive db ?;\r\nif 0\r\n  struct {\r\n    USHORT TimeRequired : 15;\r\n    USHORT ExtendedTimeReported : 1;\r\n  } NormalSecurityEraseUnit;\r\nendif\r\n  NormalSecurityEraseUnit dw ?;89\r\nif 0\r\n  struct {\r\n    USHORT TimeRequired : 15;\r\n    USHORT ExtendedTimeReported : 1;\r\n  } EnhancedSecurityEraseUnit;\r\nendif\r\n  EnhancedSecurityEraseUnit dw ?;90\r\n  CurrentAPMLevel db ?; 91\r\n  ReservedWord91 db ?\r\n  MasterPasswordID dw ?;92\r\n  HardwareResetResult dw ?;93\r\n  CurrentAcousticValue db ?;94\r\n  RecommendedAcousticValue db ?\r\n  StreamMinRequestSize dw ?\t;95\r\n  StreamingTransferTimeDMA dw ?;96\r\n  StreamingAccessLatencyDMAPIO dw ?;97\r\n  StreamingPerfGranularity dd ?;98-99\r\n  Max48BitLBA dd ?,?\t;100-103\r\n  StreamingTransferTime dw ?;104\r\n  DsmCap dw ?;105\r\nif 0\r\n  struct {\r\n    USHORT LogicalSectorsPerPhysicalSector : 4;\r\n    USHORT Reserved0 : 8;\r\n    USHORT LogicalSectorLongerThan256Words : 1;\r\n    USHORT MultipleLogicalSectorsPerPhysicalSector : 1;\r\n    USHORT Reserved1 : 2;\r\n  } PhysicalLogicalSectorSize;\r\nendif\r\n  PhysicalLogicalSectorSize dw ? ;106\r\n  InterSeekDelay dw ?;107\r\n  WorldWideName dw 4 dup (?);108-111\r\n  ReservedForWorldWideName128 dw 4 dup (?);112-115\r\n  ReservedForTlcTechnicalReport dw ?;116\r\n  WordsPerLogicalSector dw 2 dup (?);117-118\r\nif 0\r\n  struct {\r\n    USHORT ReservedForDrqTechnicalReport : 1;\r\n    USHORT WriteReadVerify : 1;\r\n    USHORT WriteUncorrectableExt : 1;\r\n    USHORT ReadWriteLogDmaExt : 1;\r\n    USHORT DownloadMicrocodeMode3 : 1;\r\n    USHORT FreefallControl : 1;\r\n    USHORT SenseDataReporting : 1;\r\n    USHORT ExtendedPowerConditions : 1;\r\n    USHORT Reserved0 : 6;\r\n    USHORT WordValid : 2;\r\n  } CommandSetSupportExt;\r\nendif\r\n  CommandSetSupportExt dw ?;119\r\nif 0\r\n  struct {\r\n    USHORT ReservedForDrqTechnicalReport : 1;\r\n    USHORT WriteReadVerify : 1;\r\n    USHORT WriteUncorrectableExt : 1;\r\n    USHORT ReadWriteLogDmaExt : 1;\r\n    USHORT DownloadMicrocodeMode3 : 1;\r\n    USHORT FreefallControl : 1;\r\n    USHORT SenseDataReporting : 1;\r\n    USHORT ExtendedPowerConditions : 1;\r\n    USHORT Reserved0 : 6;\r\n    USHORT Reserved1 : 2;\r\n  } CommandSetActiveExt;\r\nendif\r\n  CommandSetActiveExt dw ?;120\r\n  ReservedForExpandedSupportandActive dw 6 dup (?);121-126\r\nif 0\r\n  USHORT MsnSupport : 2;\r\n  USHORT ReservedWord127 : 14;\r\nendif\r\n  MsnSupport dw ?;127\r\nif 0\r\n  struct {\r\n    USHORT SecuritySupported : 1;\r\n    USHORT SecurityEnabled : 1;\r\n    USHORT SecurityLocked : 1;\r\n    USHORT SecurityFrozen : 1;\r\n    USHORT SecurityCountExpired : 1;\r\n    USHORT EnhancedSecurityEraseSupported : 1;\r\n    USHORT Reserved0 : 2;\r\n    USHORT SecurityLevel : 1;\r\n    USHORT Reserved1 : 7;\r\n  } SecurityStatus;\r\nendif\r\n  SecurityStatus dw ?;128\r\n  ReservedWord129 dw 31 dup (?);129-159\r\nif 0\r\n  struct {\r\n    USHORT MaximumCurrentInMA : 12;\r\n    USHORT CfaPowerMode1Disabled : 1;\r\n    USHORT CfaPowerMode1Required : 1;\r\n    USHORT Reserved0 : 1;\r\n    USHORT Word160Supported : 1;\r\n  } CfaPowerMode1;\r\nendif\r\n  CfaPowerMode1 dw ?;160\r\n  ReservedForCfaWord161 dw 7 dup (?);161-167\r\nif 0\r\n  USHORT NominalFormFactor : 4;\r\n  USHORT ReservedWord168 : 12;\r\nendif  \r\n  NominalFormFactor dw ?;168\r\nif 0\r\n  struct {\r\n    USHORT SupportsTrim : 1;\r\n    USHORT Reserved0 : 15;\r\n  } DataSetManagementFeature;\r\nendif  \r\n  DataSetManagementFeature dw ?;169\r\n  AdditionalProductID dw 4 dup (?);170-173\r\n  ReservedForCfaWord174 dw 2 dup (?);174-175\r\n  CurrentMediaSerialNumber dw 30 dup (?);176-205\r\nif 0\r\n  struct {\r\n    USHORT Supported : 1;\r\n    USHORT Reserved0 : 1;\r\n    USHORT WriteSameSuported : 1;\r\n    USHORT ErrorRecoveryControlSupported : 1;\r\n    USHORT FeatureControlSuported : 1;\r\n    USHORT DataTablesSuported : 1;\r\n    USHORT Reserved1 : 6;\r\n    USHORT VendorSpecific : 4;\r\n  } SCTCommandTransport;\r\nendif\r\n  SCTCommandTransport dw ?;206\r\n  ReservedWord207 dw 2 dup (?);207-208\r\nif 0\r\n  struct {\r\n    USHORT AlignmentOfLogicalWithinPhysical : 14;\r\n    USHORT Word209Supported : 1;\r\n    USHORT Reserved0 : 1;\r\n  } BlockAlignment;\r\nendif  \r\n  BlockAlignment dw ?;209\r\n  WriteReadVerifySectorCountMode3Only dw 2 dup (?);210-211\r\n  WriteReadVerifySectorCountMode2Only dw 2 dup (?);212-213\r\nif 0\r\n  struct {\r\n    USHORT NVCachePowerModeEnabled : 1;\r\n    USHORT Reserved0 : 3;\r\n    USHORT NVCacheFeatureSetEnabled : 1;\r\n    USHORT Reserved1 : 3;\r\n    USHORT NVCachePowerModeVersion : 4;\r\n    USHORT NVCacheFeatureSetVersion : 4;\r\n  } NVCacheCapabilities;\r\nendif  \r\n  NVCacheCapabilities dw ?;214\r\n  NVCacheSizeLSW dw ?;215\r\n  NVCacheSizeMSW dw ?;216\r\n  NominalMediaRotationRate dw ?;217\r\n  ReservedWord218 dw ?;218\r\nif 0\r\n  struct {\r\n    UCHAR NVCacheEstimatedTimeToSpinUpInSeconds;\r\n    UCHAR Reserved;\r\n  } NVCacheOptions;\r\nendif\r\n  NVCacheEstimatedTimeToSpinUpInSeconds db ? ;219\r\n  db ?\r\n  WriteReadVerifySectorCountMode db ? ;220\r\n  db ?\r\n  ReservedWord221 dw ?;221\r\nif 0\r\n  struct {\r\n    USHORT MajorVersion : 12;\r\n    USHORT TransportType : 4;\r\n  } TransportMajorVersion;\r\nendif  \r\n  TransportMajorVersion dw ?;222\r\n  TransportMinorVersion dw ?;223\r\n  ReservedWord224 dw 6 dup (?);224-229\r\n  ExtendedNumberOfUserAddressableSectors dq ?;230-233\r\n  MinBlocksPerDownloadMicrocodeMode03 dw ?;234\r\n  MaxBlocksPerDownloadMicrocodeMode03 dw ?;235\r\n  ReservedWord236 dw 19 dup (?);236-254\r\n  Signature db ?;255\r\n  CheckSum db ?\r\nIDENTIFY_DEVICE_DATA ends\r\n"
  },
  {
    "path": "JLM/XDMA32/MAKE.BAT",
    "content": "@echo off\r\nrem Assemble and link file for the XDMA32 JLM.\r\nrem jwasm: option -zzs is needed for OW WLink v1.8!\r\n\r\njwasm -coff -nologo -Fl -Sg -I..\\..\\Include -Fo=XDMA32.obj XDMA32.ASM\r\njwlink format win pe hx dll ru native file XDMA32.obj name XDMA32.DLL op q,map\r\n"
  },
  {
    "path": "JLM/XDMA32/MAKEM.BAT",
    "content": "@echo off\r\nrem using MS tools\r\nml -c -coff -Fl -D_DEBUG -I..\\..\\Include XDMA32.ASM\r\nlink /NOLOGO /SUBSYSTEM:native /DLL XDMA32.obj /OUT:XDMA32.DLL /OPT:NOWIN98 /MAP\r\n"
  },
  {
    "path": "JLM/XDMA32/XDMA32.ASM",
    "content": ";\r\n; XDMA32.ASM  - a JLM driver for UltraDMA hard-disks\r\n; based on XDMA v3.3 by Jack R. Ellis\r\n; released under the GNU GPL license v2 (see GNU_GPL.TXT for details)\r\n;\r\n; The source is to be assembled with JWasm or Masm v6+!\r\n;\r\n; XDMA32 switch options are as follows:\r\n;\r\n; /B   Always use XMS buffer. Without this option, the XMS buffer is\r\n;      used only if the user buffer isn't dword-aligned.\r\n; /F   Enables \"Fast UltraDMA\". Data input requests that cross an\r\n;      DMA \"64K boundary\" are executed using a 2-element DMA\r\n;      \"scatter/gather\" list, one for data up to the boundary, and one\r\n;      for data beyond it.\r\n; /L   Limits DMA to \"low memory\" below 640K. /L is REQUIRED to use\r\n;      UMBPCI or any similar driver whose upper-memory areas do not\r\n;      allow DMA.    /L causes I-O requests past 640K to go through\r\n;      the driver's buffer.\r\n; /Mn  set/restrict UDMA mode.\r\n; /Q   Quiet mode.\r\n; /W   also handle non-UDMA disks which can do multiword-DMA.\r\n;\r\n; On exit from successful I-O requests, the AH-register is zero and the\r\n; carry flag is reset.   If an error occurs, the carry flag is SET, and\r\n; the AH-register has one of the following codes:\r\n;\r\n;   Code 08h - DMA timed out.\r\n;        0Fh - DMA error.\r\n;        20h - Controller busy before I-O.\r\n;        21h - Controller busy after I-O.\r\n;        AAh - Disk not ready before I-O.\r\n;        ABh - Disk not ready after I-O.\r\n;        CCh - Disk FAULT before I-O.\r\n;        CDh - Disk FAULT after I-O.\r\n;        E0h - Hard error at I-O end.\r\n;        FFh - XMS memory error.\r\n\r\n    .386\r\n    .model flat\r\n    option casemap:none\r\n    option dotname\r\n    option proc:private\r\n\r\n    include jlm.inc\r\n\r\nifndef ?PCISCAN\r\n?PCISCAN equ 1\r\nendif\r\n\r\nNUMDSK  equ 8           ;max HDs supported\r\nNUMCTRL equ 4           ;max IDE controllers supported\r\nSAVESTAT equ 1          ;save/restore client state on init\r\nSETBM   equ 1           ;set busmaster flag in PCI status\r\nSCATTER equ 1           ;support /F option to use DMA scatter/gather lists\r\n                        ;if a 64 kb boundary is crossed.\r\nIRQWND  equ 1           ;allow interrupts during memory copy\r\nLBACHECK equ 1          ;check if disk supports LBA\r\nMWDMA   equ 1           ;support /W option to accept multi-word DMA\r\nSETMODE equ 1           ;support /Mn option to set UDMA mode\r\nNOHIGHLBA equ 1         ;avoid 4 OUTs for LBA28\r\nBUFONLY equ 1           ;support /B option\r\nHDNUM   equ 0           ;1=rely on value at 0040:0075\r\n;\r\n; General Program Equations.\r\n;\r\nVERSION equ <' V1.5, 15-10-2022'>\r\nRDYTO   equ 08h         ;389-msec minimum I-O timeout.\r\nBIOSTMR equ 046Ch       ;BIOS \"tick\" timer address.\r\nHDISKS  equ 0475h       ;BIOS hard-disk count address.\r\nHDIOFS  equ 048Eh       ;BIOS flag for HD IRQ\r\n\r\nCR      equ 00Dh        ;ASCII carriage-return.\r\nLF      equ 00Ah        ;ASCII line-feed.\r\n\r\n@byte   equ <byte ptr>\r\n@word   equ <word ptr>\r\n@dword  equ <dword ptr>\r\n;\r\n; Driver Return Codes.\r\n;\r\nDMATIMO equ 0E8h        ;DMA timeout code, 008h at exit.\r\nDMAERR  equ 0EFh        ;DMA error   code, 00Fh at exit.\r\nCTLRERR equ 000h        ;Ctlr. busy  code, 020h/021h at exit.\r\nDISKERR equ 08Ah        ;Disk-busy   code, 0AAh/0ABh at exit.\r\nFAULTED equ 0ACh        ;Disk FAULT  code, 0CCh/0CDh at exit.\r\nHARDERR equ 0BFh        ;Hard-error  code, 0E0H at exit.\r\nXMSERR  equ 0FFh        ;XMS memory-error code.\r\n;\r\n; IDE Controller Register Definitions.\r\n;\r\nCDATA   equ 0h          ;offset Data port.\r\nCSECCT  equ CDATA+2     ;offset I-O sector count.\r\nCDSEL   equ CDATA+6     ;offset Disk-select and upper LBA.\r\nCCMD    equ CDATA+7     ;offset Command register.\r\n\r\nCSTAT   equ CDATA+7     ;Primary status register.\r\n;CSTAT2  equ CDATA+206h  ;Alternate status register. (legacy only)\r\n;\r\n; Controller Status and Command Definitions.\r\n;\r\nBSY     equ 080h        ;IDE controller is busy.\r\nRDY     equ 040h        ;disk device is \"ready\".\r\nFLT     equ 020h        ;IDE disk has a \"fault\".\r\nDRQ     equ 008h        ;IDE data request.\r\nERR     equ 001h        ;IDE general error flag.\r\n\r\nDRCMD   equ 0C8h        ;DMA read command (write is 0CAh, LBA48 commands are 025h/035h).\r\nSETFEAT equ 0EFh        ;Set Features command.\r\nSETM    equ 003h        ;Set Mode subcommand.\r\n\r\nDMI     equ 004h        ;DMA interrupt occured.\r\nDME     equ 002h        ;DMA error occurred.\r\n;\r\n; LBA \"Disk Address Packet\" Layout.\r\n;\r\nDAP struc\r\nDapPL   db  ?       ;Packet length.\r\n        db  ?       ;(Reserved).\r\nDapSC   db  ?       ;I-O sector count.\r\n        db  ?       ;(Reserved).\r\nDapBuf  dd  ?       ;I-O buffer address (segment:offset).\r\nLBA01   dw  ?       ;48-bit logical block address (LBA).\r\nLBA25   dd  ?\r\nLBA67   dw  ?       ; it's actually a QWORD\r\nDAP ends\r\n;\r\n; DPTE \"Device Parameter Table Extension\" Layout\r\n; that's the structure the lpCfg member in EDD20 points to.\r\n;\r\nDPTE struc\r\nwIDEBase    dw ?    ;IDE port base\r\nwIDEAlt     dw ?    ;alternate control port\r\nbFlags      db ?    ;drive flags (bit 4=1 -> drive is slave)\r\n            db ?    ;proprietary info\r\nbIRQ        db ?    ;IRQ for drive\r\n;--- there's some more fields...\r\nDPTE ends\r\n\r\n;--- structure used by Int 13h, ah=48h\r\n\r\nEDD10 struc\r\ncbSize  dw ?\t;+0 size (26 for v1.x, 30 for v2.x, 66 for v3.0)\r\nflags   dw ?\t;+2 see below\r\ndwCyls  dd ?\t;+4 number of physical cylinders on drive\r\ndwHeads dd ?\t;+8 number of physical heads on drive\r\ndwSecs  dd ?\t;+12 number of physical sectors per track\r\nnumSecs dq ?\t;+16 total number of sectors on drive\r\nsecSize\tdw ?\t;+24 bytes per sector\r\nEDD10 ends\r\n\r\nEDD20 struc\r\n      EDD10 <>\r\nlpCfg dd ?\t;+26 EDD configuration parameters ( or -1 if invalid )\r\nEDD20 ends\r\n\r\nEDD30 struct\r\n        EDD20 <>\r\nwSig      dw ?\t\t\t;+1E\r\nbLength   db ?\t\t\t;+20 length of path\r\n          db 3 dup (?)\t;    reserved        \r\nszBus     db 4 dup (?)\t;+24\r\nszIFType  db 8 dup (?)\t;+28\r\nqwIFPath  db 8 dup (?)\t;+30\r\nszDevPath db 8 dup (?)\t;+38\r\n          db ?\t\t\t;+40\r\nbChecksum db ?\t\t\t;+41\r\n          db 8 dup (?)  ;+42\r\nEDD30 ends\r\n\r\n;--- bits for flags above\r\nEDDF_DMA_BOUNDARY   equ 01h ;DMA boundary errors handled transparently\r\nEDDF_CHS_VALUD      equ 02h ;cylinder/head/sectors-per-track information is valid\r\nEDDF_REMOVABLE      equ 04h ;removable drive\r\nEDDF_WRITE_VERIFY   equ 08h ;write with verify supported\r\nEDDF_CHANGE_LINE    equ 10h ;drive has change-line support (required if drive >= 80h is removable)\r\nEDDF_LOCKABLE       equ 20h ;drive can be locked (required if drive >= 80h is removable)\r\nEDDF_CHS_IS_MAX     equ 40h ;CHS information set to maximum supported values, not current media\r\n;--- 15-7\treserved (0)\r\n;Note:\tbits 4-6 are only valid if bit 2 is set\r\n\r\n\r\n;\r\n; DOS \"Request Packet\" Layout.\r\n;\r\nRP struc\r\nRPHLen  db ?        ;Header byte count.\r\nRPSubU  db ?        ;Subunit number.\r\nRPOp    db ?        ;Opcode.\r\nRPStat  dw ?        ;Status word.\r\n        db 8 dup (?);(Unused by us).\r\nRPUnit  db ?        ;Number of units found.\r\nRPSize  dd ?        ;Resident driver size.\r\nRPCL    dd ?        ;Command-line data pointer.\r\nRP ends\r\n\r\nRPERR   equ 08003h      ;Packet \"error\" flags.\r\nRPDONE  equ 00100h      ;Packet \"done\" flag.\r\n\r\nCStr macro text:VARARG\r\nlocal sym\r\n\t.code .text$01\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n@dprintf macro text,args:vararg\r\nifdef _DEBUG\r\n\tifnb <args>\r\n\t\tinvoke printf, CStr(text), args\r\n\telse\r\n\t\tinvoke printf, CStr(text)\r\n\tendif\r\nendif\r\nendm\r\n\r\n\t.data\r\n\r\ndwBase      dd 0    ;linear address driver base ( conv. memory )\r\ndwCmdLine   dd 0\r\ndwBufferLin dd 0    ;linear address XMS buffer\r\ndwBufferPhy dd 0    ;physical address XMS buffer (64 kb aligned)\r\nPrvI13      dd 0    ;old real-mode int 13 vector\r\nXMSEntry    dd 0    ;XMS \"entry\" address, set by Init\r\nXMSHdl      dd 0    ;XMS memory \"handle\", set by Init\r\nwBaseSeg    dd 0    ;conventional memory segment ( supplied by loader)\r\nPRDAd       dd 0    ;PRD 32-bit command addr. (Init set).\r\n\r\n;--- values for bFlags\r\n\r\nFL_Q    equ 1       ;/Q option set\r\nif SCATTER\r\nFL_F    equ 2       ;/F option\r\nendif\r\nif MWDMA\r\nFL_W    equ 4       ;/W option\r\nendif\r\nFLB_BUSY equ 7      ;bit 7 is \"busy\" flag\r\n\r\nbFlags  db  0       ;various flags (cmdline params, busy, ...)\r\nif SETMODE\r\nMaxUM   db  -1      ;UDMA \"mode\" limit set by /Mn option.\r\nendif\r\nBiosHD  db  0       ;(Number of BIOS disks, during Init).\r\nHDCount db  0       ;(BIOS hard-disk count, during Init).\r\n\r\n\talign 4\r\n\r\n;--- the following is written to the IDE port\r\n;--- SecCt2/LBA2447 and SecCt/LBA0023 must be consecutive\r\n\r\nSecCt2  db  0       ;IDE \"upper\" sector count.\r\nLBA2447 db  0,0,0   ;IDE \"upper\" LBA48 bits 24-47.\r\nSecCt   db  0       ;IDE \"lower\" sector count.\r\nLBA0023 db  0,0,0   ;IDE \"lower\" LBA bits 0-23.\r\n\r\nDSIOCmd label word\r\nDSCmd   db  0       ;IDE disk-select, LBA28 bits 24-27.\r\nIOCmd   db  0       ;IDE command byte.\r\n\r\nHDUnit  db  0       ;Current BIOS unit number.\r\n\r\n\talign 4\r\n\r\nVDSLn   dd  0           ;buffer length\r\nVDSOf   dd  0           ;linear address of buffer\r\n\r\n;--- values for DMA controller - must be consecutive\r\nIOAdr   dd  0           ;DMA physical address\r\nIOLen   dd  80000000h   ;DMA byte count and \"end\" flag.\r\nif SCATTER\r\nIOAdr2  dd  0\r\nIOLen2  dd  80000000h\r\nendif\r\n\r\nIDEAd   dw  1F0h, 1F0h, 170h, 170h\r\nif NUMDSK gt 4\r\n        dw  NUMDSK-4 dup (-1);IDE port base\r\nendif\r\n\r\nIDEAlt  dw  3F6h, 3F6h, 376h, 376h\r\nif NUMDSK gt 4\r\n        dw  NUMDSK-4 dup (-1);IDE port base\r\nendif\r\n\r\nDMAAd   dw  NUMDSK dup (-1) ;DMA port base\r\n\r\nUnits   db  NUMDSK dup (-1) ;IDE \"active units\" table (Init set).\r\nSelCmd  db  0E0h,0F0h,0E0h,0F0h\r\nif NUMDSK gt 4\r\n        db  NUMDSK-4 dup (0)\r\nendif\r\nCHSSec  db  NUMDSK dup (0)  ;CHS sectors/head   table (Init set).\r\nCHSHd   db  NUMDSK dup (0)  ;CHS heads/cylinder table (Init set).\r\n\r\n;--- stored IDE controllers (bus/dev/fn) by PCI scan\r\ncontrollers dw NUMCTRL dup (-1)\r\n\r\n;--- flags for PCI IDE controller programming interface\r\n; 7 bus mastering (read-only)\r\n; 6-4   reserved (read-only)\r\n; 3 secondary IDE mode bit is writable (read-only)\r\n; 2 secondary IDE mode (0 = legacy, 1 = native)\r\n; 1 primary IDE mode bit is writable (read-only)\r\n; 0 primary IDE mode (0 = legacy, 1 = native)\r\n\r\n;\r\n; Initialization Tables And Variables.\r\n;\r\n\r\nif MWDMA\r\nMWModes label word\r\n    dw 004       ;Mode 0\r\n    dw 013       ;Mode 1\r\n    dw 016       ;Mode 2\r\nendif\r\nUModes label word\r\n    dw 016       ;Mode 0, ATA-16  UDMA mode table\r\n    dw 025       ;Mode 1, ATA-25\r\n    dw 033       ;Mode 2, ATA-33\r\n    dw 044       ;Mode 3, ATA-44  (Unusual but possible).\r\n    dw 066       ;Mode 4, ATA-66\r\n    dw 100       ;Mode 5, ATA-100\r\n    dw 133       ;Mode 6, ATA-133\r\n    dw 166       ;Mode 7, ATA-166\r\n\r\n\tinclude identify.inc\r\n;SIZENAME    equ 40\r\nSIZENAME    equ sizeof IDENTIFY_DEVICE_DATA.ModelNumber\r\n\r\nDNMsg   db  ', '\r\nDName   db 26*2 dup (0)    ;the model number is actually 20 words only\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\n;\r\n; Driver Entry Routine.  For CHS requests, the registers contain:\r\n;\r\n;   AH      Request code.  We handle 002h read and 003h write.\r\n;   AL      I-O sector count.\r\n;   CH      Lower 8 bits of starting cylinder.\r\n;   CL      Starting sector and upper 2 bits of cylinder.\r\n;   DH      Starting head.\r\n;   DL      BIOS unit number.  We handle 080h and up (hard-disks).\r\n;   ES:BX   I-O buffer address.\r\n;\r\n; For LBA requests, the registers contain:\r\n;\r\n;   AH      Request code.  We handle 042h read and 043h write.\r\n;   DL      BIOS unit number.  We handle 080h and up (hard-disks).\r\n;   DS:SI   Pointer to Device Address Packet (\"DAP\"), described above.\r\n;\r\n;--- for JLMs all registers are in a client structure and EBP will\r\n;--- point to it.\r\n\r\nEntry:\r\n\r\nifdef _DEBUG\r\n\tcmp @byte [ebp].Client_Reg_Struc.Client_EAX+1, 42h\r\n\tjz @F\r\n\tcmp @byte [ebp].Client_Reg_Struc.Client_EAX+1, 43h\r\n\tjnz stdpr1\r\n@@:\r\n\tmovzx esi,@word [ebp].Client_Reg_Struc.Client_DS\r\n\tmovzx ecx,@word [ebp].Client_Reg_Struc.Client_ESI\r\n\tshl esi, 4\r\n\tadd esi, ecx\r\n\t@dprintf <\"Entry(I13), ax=%X, dx=%X, DAP=%X, buffer=%X, sect=%X\",13,10>,\\\r\n\t\tword ptr [ebp].Client_Reg_Struc.Client_EAX,\\\r\n\t\tword ptr [ebp].Client_Reg_Struc.Client_EDX, esi, [esi].DAP.DapBuf, dword ptr [esi].DAP.LBA01\r\n\tjmp stdpr2\r\nstdpr1:\r\n\t@dprintf <\"Entry(I13), ax=%X, dx=%X, buffer=%X:%X\",13,10>,\\\r\n\t\tword ptr [ebp].Client_Reg_Struc.Client_EAX,\\\r\n\t\tword ptr [ebp].Client_Reg_Struc.Client_EDX,\\\r\n\t\tword ptr [ebp].Client_Reg_Struc.Client_ES,\\\r\n\t\tword ptr [ebp].Client_Reg_Struc.Client_EBX\r\nstdpr2:\r\nendif\r\n\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EDX\r\n    mov edi,0                   ;Reset active-units table index.\r\n@LastU  equ $-4                 ;(Last-unit index, set by Init).\r\nNextU:\r\n    dec edi                     ;Any more active units to check?\r\n    js QuickX                   ;No, request NOT for us -- exit quick!\r\n    cmp dl,[edi+Units]          ;Does request unit match our table?\r\n    jne NextU                   ;No, see if more table entries remain.\r\n    bts @dword [bFlags],FLB_BUSY;set \"busy\" flag\r\n    jc IsBusy                   ;exit with error AH=01 if driver is busy\r\n;\t@dprintf <\"Entry(I13), IDE/Alt/DMA=%X/%X/%X\",13,10>, [edi*2+IDEAd], [edi*2+IDEAlt], [edi*2+DMAAd]\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EAX\r\n    mov dl,0BEh     ;Mask out LBA and write request bits.\r\n    and dl,ah       ; 02/03/42/43 -> 2\r\n    cmp dl,002h     ;Is this a CHS or LBA read or write?\r\n    jne Pass        ;No, let BIOS handle this request.\r\n    shl ah,1        ;02/03 -> 04/06   42/43 -> 84/86\r\n    jns ValCHS      ;No, handle CHS\r\n\r\n    movzx ecx,@word [ebp].Client_Reg_Struc.Client_DS\r\n    movzx esi,@word [ebp].Client_Reg_Struc.Client_ESI\r\n    shl ecx, 4\r\n    add esi, ecx\r\n\r\n    movzx ebx, @word [esi].DAP.DapBuf+2\r\n    movzx edx, @word [esi].DAP.DapBuf+0\r\n    shl ebx, 4\r\n    add ebx, edx\r\n    mov edx,[esi.DAP.LBA25]         ;Get DAP LBA bits 16-47\r\n    mov al,[esi.DAP.DapSC]          ;Get \"DAP\" I-O sector count.\r\n    \r\n    cmp [esi.DAP.DapBuf],-1         ;64-bit buffer address?\r\n    mov si,[esi.DAP.LBA01]          ;(Get \"DAP\" LBA bits 0-15).\r\n    jne ValSC                       ;No, go validate \"DAP\" sector count.\r\nPass:\r\n    btr @dword [bFlags],FLB_BUSY    ;Reset driver \"busy\" flag\r\nQuickX:\r\n    mov eax,[PrvI13]\r\n    mov @word [ebp].Client_Reg_Struc.Client_EIP, ax\r\n    shr eax, 16\r\n    mov @word [ebp].Client_Reg_Struc.Client_CS, ax\r\n    ret\r\nIsBusy:\r\n    lahf            ;store Carry flag in AH\r\n    mov al,1        ;and error code in AL\r\n    jmp GoOut\r\n    align 4\r\nValCHS:\r\n    mov ecx,[ebp].Client_Reg_Struc.Client_ECX\r\n    xchg eax,ecx    ;CHS -- save request code and sector count in CX\r\n\r\n    mov si,0003Fh   ;Set SI-reg. to starting sector.\r\n    and esi,eax\r\n    dec esi\r\n    shr al,6        ;Set AX-reg. to starting cylinder.\r\n    xchg al,ah\r\n    xchg eax,edx    ;ah=head, dx=start cyl\r\n    mov al,[edi+CHSSec];Get disk CHS sectors/head value.\r\n    or al,al        ;Were disk CHS values legitimate?\r\n    jz Pass         ;No?  Let BIOS do this request!\r\n    push eax        ;Save CHS sectors/head value.\r\n    mul ah          ;Convert head to sectors.\r\n    add si,ax       ;Add result to starting sector.\r\n    pop eax         ;Reload CHS sectors/head value.\r\n    mul [edi+CHSHd] ;Convert cylinder to sectors.\r\n    mul dx\r\n    add si,ax       ;Add to head/sector value.\r\n    adc edx,0\r\n    movzx edx, dx   ;Reset upper LBA address bits.\r\n\r\n    xchg eax,ecx    ;restore request code and sector count in AX\r\n\r\n    movzx ecx,@word [ebp].Client_Reg_Struc.Client_ES\r\n    movzx ebx,@word [ebp].Client_Reg_Struc.Client_EBX\r\n    shl ecx, 4\r\n    add ebx, ecx    ;set linear buffer address in EBX\r\n\r\n;--- here LBA bits in EDX:SI, linear address buffer in EBX\r\n;--- sector count in AL, cmd in AH\r\n\r\nValSC:\r\n    dec al                  ;Is sector count zero or over 128?\r\n    js Pass                 ;Yes?  Let BIOS handle this \"No-No!\".\r\n    inc eax                 ;Restore sector count\r\n    cld                     ;Ensure FORWARD \"string\" commands.\r\n    mov @word [LBA0023],si  ;Save LBA bits 0-15 and 24-47.\r\n    mov [LBA0023+2],dl      ;Save LBA bits 16-23, to free DL-reg.\r\n    mov [LBA2447],dh\r\n    mov esi, edx\r\n    shr esi, 16\r\n    mov @word [LBA2447+1],si\r\n    mov [VDSOf],ebx         ;Save user buffer linear address.\r\n\r\n    or @byte [ebp].Client_Reg_Struc.Client_EFlags+1,2   ;set client IF\r\n\r\n\r\n    shr dx,12           ;Shift out LBA bits 16-27.\r\n    or si,dx            ;Anything in LBA bits 28-47?\r\n    jnz LBA48           ;Yes, use LBA48 read/write command.\r\n    xchg dh,[LBA2447]   ;LBA28 -- reload & reset bits 24-27.\r\n\r\n    or ah,(DRCMD+1)     ;Get LBA28 read/write command + 5.\r\n    jmp GetAdr          ;Go get IDE and LBA address bytes.\r\n    align 4\r\nLBA48:\r\n    shl ah,3            ;LBA48 -- translate command 04h/84h -> 20h, 06h/86h -> 30h.\r\nGetAdr:\r\n    mov dl,[edi+SelCmd]\r\n    or dl,dh            ;\"Or\" in LBA28 bits 24-27 (if any).\r\n    mov dh,005h         ;Get final IDE command byte.\r\n    xor dh,ah           ;(LBA28 = C8h/CAh, LBA48 = 25h/35h).\r\n    mov [DSIOCmd],dx    ;Set IDE command bytes (DSCmd + IOCmd).\r\n    mov [SecCt],al      ;Set I-O sector count.\r\n    movzx eax,al\r\n    shl eax,9\r\n    mov [VDSLn],eax     ;Set buffer lengths.\r\n    mov ecx,eax\r\n    bts eax,31          ;Set DMA list \"end\" flag.\r\n    mov [IOLen],eax\r\n\r\nif BUFONLY\r\n@BufPatch:\r\nendif\r\n    test bl,3h          ;Is user I-O buffer 32-bit aligned?\r\n    jnz BufIO           ;No, use buffered I-O routines below.\r\n\r\n    mov esi,ebx         ;get user buffer into ESI\r\nif SCATTER\r\n    test [bFlags],FL_F  ;if /F not set, check 64 kB boundary\r\n    setz dl\r\nelse\r\n    mov dl,1            ;check for 64 kB boundary crossing\r\nendif\r\n    VxDCall VDMAD_Lock_DMA_Region   ; lock region esi, size ecx, dl[0] check 64 kb crossing\r\n    jc BufIO            ;Error -- do buffered I-O.\r\n    mov [IOAdr], edx\r\n;\t@dprintf <\"Entry(I13), lock ok, addr=%X\",13,10>, edx\r\n\r\n    cmp @word [IOAdr+2],-1  ;DMA I-O above our limit?\r\n@DMALmt equ $-1         ;(If 640K limit, ffff is patched to 9fff by Init).\r\n    ja BufIO            ;Yes, use buffered I-O routines below.\r\nif SCATTER\r\n    test [bFlags],FL_F  ;/F option set?\r\n    jz @F\r\n    mov eax, edx\r\n    mov ecx,[VDSLn]     ;Get lower ending DMA address.\r\n    dec ecx             ;(IOLen - 1 + IOAdr).\r\n    add ax,cx           ;Would input cross a 64K boundary?\r\n    jnc @F              ;No, set DMA flag & do transfer.\r\n    inc ax              ;Get bytes above 64K boundary.\r\n    cmp ax,64           ;Is this at least 64 bytes?\r\n    jb BufIO            ;No, use buffer\r\n    inc cx              ;Get bytes below 64K boundary.\r\n    sub cx,ax\r\n    cmp cx,64           ;Is this at least 64 bytes?\r\n    jb BufIO            ;No, use buffer\r\n    mov @word [IOLen2],ax   ;Set 2nd command-list byte count.\r\n    movzx eax,cx        ;Set 1st command-list byte count.\r\n    mov [IOLen],eax\r\n    add eax,[IOAdr]     ;Set 2nd command-list address.\r\n    mov [IOAdr2],eax\r\n@@:\r\nendif\r\n    call DoDMA          ;Do direct DMA I-O with user's buffer.\r\nDone:\r\n    lahf\r\n    btr @dword [bFlags],FLB_BUSY    ;Reset driver \"busy\" flag\r\nGoOut:\r\n    mov @byte [ebp].Client_Reg_Struc.Client_EAX+1, al   ;Set error code in exiting AH-reg.\r\n    push eax\r\n    @VMMCall Simulate_Iret\r\n    pop eax\r\n    and @byte [ebp].Client_Reg_Struc.Client_EFlags,not 1\r\n    and ah,1\r\n    or @byte [ebp].Client_Reg_Struc.Client_EFlags,ah\r\n    ret\r\n    align 4\r\n\r\nBufIO:\r\n;\t@dprintf <\"Entry(I13), BufIO\",13,10>\r\n    test [IOCmd],012h   ;Is this a write request?\r\n    jnz BufOut          ;Yes, use output routine\r\n\r\nif 0;def _DEBUG\r\n\tpush edi\r\n\tmov edi, dwBufferLin\r\n\tmov ecx, VDSLn\r\n\tshr ecx, 2\r\n\tmov eax, 0f6f6f6f6h\r\n\trep stosd\r\n\tpop edi\r\nendif\r\n\r\n;--- buffered read: read into DMA buffer, then copy to conv. memory\r\n\r\n    call BufDMA     ;Input all data to driver XMS buffer.\r\n    jc Done         ;If error, post return code & exit!\r\n    mov ecx, [VDSLn]\r\n    mov edi, [VDSOf]\r\n    mov esi, [dwBufferLin]\r\nif IRQWND\r\n    @VMMCall MoveMemory\r\nelse\r\n    shr ecx, 2\r\n    rep movsd\r\nendif\r\n    clc\r\n    jmp Done        ;Done -- post any return code & exit.\r\n    align 4\r\n;\r\n;--- buffered write: copy conv. memory to DMA buffer, then write\r\n;\r\nBufOut:\r\n    push edi       ;dont destroy EDI!\r\n    mov ecx, [VDSLn]\r\n    mov esi, [VDSOf]\r\n    mov edi, [dwBufferLin]\r\nif IRQWND\r\n    @VMMCall MoveMemory\r\nelse\r\n    shr ecx, 2\r\n    rep movsd\r\nendif\r\n    pop edi\r\n    call BufDMA     ;Output all data from XMS buffer.\r\n    jmp Done        ;Done -- post any return code & exit.\r\n_ret:\r\n    ret\r\n\r\n    align 4\r\n\r\n;\r\n; Subroutine to execute read and write commands.\r\n; EDI=drive\r\n; out: AL=errorcode (00=no error)\r\n;  NC = no error\r\n;\r\nBufDMA:\r\n    mov eax, [dwBufferPhy]\r\n    mov [IOAdr],eax         ;Buffered -- set physical buffer addr.\r\nDoDMA:\r\n    mov dx,[edi*2+DMAAd]    ;Ensure any previous DMA is stopped!\r\n    in al,dx                ;(On some older chipsets, if DMA is\r\n    and al,0F6h             ;  running, reading an IDE register\r\n    out dx,al               ;  causes the chipset to \"HANG\"!!).\r\n\r\n;   mov al,[edi+SelCmd]     ;Select our desired disk.\r\n    mov al,[DSCmd]\r\n    mov dx,[edi*2+IDEAd]\r\n    add edx,CDSEL\r\n    out dx,al\r\n\r\n    mov cx, (RDYTO shl 8) or FLT  ;Get timeout & \"fault\" mask.\r\n    mov esi,BIOSTMR\r\n    add ch,[esi]            ;Set timeout limit in CH-reg.\r\n    call ChkRdy             ;Await controller- and disk-ready.\r\n    jc _ret                 ;If any errors, exit!\r\n\r\n;    mov ds:[HDIOFS],al      ;AL is 0. reset BIOS flag for HD IRQ\r\n    mov byte ptr ds:[HDIOFS],0\r\n\r\n    mov dx,[edi*2+DMAAd]    ;Reset DMA commands and set DMA mode.\r\n    test [IOCmd],012h       ;Is this a write request?\r\n    jnz @F                  ;Yes, reset DMA command register.\r\n    in al, dx\r\n    or al, 8                ;set \"DMA read\" command bit.\r\n    out dx, al\r\n@@:\r\n    add edx, 2      ;Point to DMA status register.\r\n    in al, dx       ;Reset DMA status register.\r\n    or al, 6        ;(Done this way so we do NOT alter\r\n    out dx, al      ;  the \"DMA capable\" status flags!).\r\n    add edx, 2      ;Set PRD pointer to our DMA address.\r\n    mov eax, [PRDAd]\r\n    out dx, eax\r\n\r\n    mov eax, dword ptr [SecCt2]\r\n    mov dx,[edi*2+IDEAd]\r\n    add edx, 2\r\nif NOHIGHLBA\r\n    and eax, eax\r\n    jz skip4\r\nendif\r\n    mov cl,4         ;(1st 4 overlayed by 2nd 4 if LBA48!).\r\n@@:\r\n    out dx, al\r\n    inc edx\r\n    shr eax, 8\r\n    dec cl\r\n    jnz @B\r\n    sub edx, 4\r\nskip4:\r\n    mov eax, dword ptr [SecCt]\r\n    mov cl,4\r\n@@:\r\n    out dx, al\r\n    inc edx\r\n    shr eax, 8\r\n    dec cl\r\n    jnz @B\r\n\r\n    mov al,[IOCmd]\r\n    inc edx\r\n    out dx, al\r\n\r\nif 0\r\n\r\n;--- the DRQ status bit is NOT necessarily set\r\n;--- for DMA transfers, so this code should not run.\r\n\r\n    mov dx,[edi*2+IDEAlt]\r\n;\t@dprintf <\"DoDMA waiting for DRQ=1, esi=%X, dx=%X\",13,10>, esi, dx\r\n\tjmp @F\r\nwaitdrq:\r\n\t@VMMCall Yield\r\n@@:\r\n    cmp ch,[esi]    ;Too long without 1st data-request?\r\n    je DMAEnd       ;Yes?  Return carry and DMA timeout!\r\n    in al,dx        ;Read IDE alternate status.\r\n    and al,DRQ      ;Has 1st data-request arrived?\r\n    jz waitdrq      ;No, loop back and check again.\r\nendif\r\n\r\n    mov dx, [edi*2+DMAAd]\r\n    in al, dx       ;Set DMA Start/Stop bit (starts DMA).\r\n    or al, 1\r\n    out dx, al\r\n    inc edx         ;Point to DMA status register.\r\n    inc edx\r\n@@:\r\n    @VMMCall Yield\r\n    in al,dx        ;Read DMA controller status.\r\n    and al,DMI+DME  ;DMA interrupt or DMA error?\r\n    jnz @F          ;Yes, halt DMA and check results.\r\n    cmp ch,[esi]    ;Has our DMA transfer timed out?\r\n    je @F           ; then exit loop.\r\n    test @byte ds:[HDIOFS],80h\t; interrupt occured?\r\n    jz @B\r\n    mov al,DMI\r\n@@:\r\n    dec edx         ;Point back to DMA command register.\r\n    dec edx\r\n    push eax        ;Save ending DMA status.\r\n    in al,dx        ;Reset DMA Start/Stop bit.\r\n    and al,0FEh\r\n    out dx,al\r\n    pop eax         ;Reload ending DMA status.\r\n    cmp al,DMI      ;Did DMA end with only an interrupt?\r\n    jne ErrDMA      ;No?  Go see what went wrong.\r\n    inc edx         ;Reread DMA controller status.\r\n    inc edx\r\n    in al,dx\r\n    test al,DME     ;Any \"late\" DMA error after DMA end?\r\n    jnz DMAEnd      ;Yes?  Return carry and DMA error!\r\n    inc cl          ;Check \"fault\" and hard-error at end.\r\n\r\n;--- EDI=drive,ESI=BIOS timer counter, CH=max\r\n\r\nChkRdy:\r\n    mov dx,[edi*2+IDEAd]  ;Read IDE primary status.\r\n    add edx,CSTAT\r\n    in al,dx\r\n    test al,BSY+RDY ;Controller or disk still busy?\r\n    jg ChkErr       ;No, go check for \"fault\" or error.\r\nif 1\r\n    @VMMCall Yield   ;yield CPU \r\nendif\r\n    cmp ch,[esi]    ;Too long without becoming ready?\r\n    jne ChkRdy      ;No, loop back and check again.\r\n    test al,BSY     ;BAAAD News!  Did controller go ready?\r\n    mov ax,(256*CTLRERR)+DISKERR ;(Get not-ready error codes).\r\n    jmp WhichE      ;Go see which error code to return.\r\nChkErr:\r\n    and al,cl       ;Disk \"fault\" or hard-error?\r\n    jz ChkExit      ;No, all is well -- go exit below.\r\n    test al,FLT     ;BAAAD News!  Is the disk \"faulted\"?\r\n    mov ax,(256*FAULTED)+HARDERR ;(Get hardware error codes).\r\nWhichE:\r\n    jz EndErr       ;If \"zero\", use AL-reg. return code.\r\n    mov al,ah       ;Use AH-reg. return code of this pair.\r\nEndErr:\r\n    add al,cl       ;Add 1 if error occurred at I-O end.\r\n    stc             ;Set carry flag to denote \"error\"!\r\nChkExit:\r\n    ret\r\nErrDMA:\r\n    test al,DME     ;BAAAD News!  Did DMA end with error?\r\nDMAEnd:\r\n    mov ax,(256*DMAERR)+DMATIMO  ;(Get DMA error codes).\r\n    jmp WhichE      ;Go see which error code to return.\r\n\r\n;--- end of \"resident\" part\r\n;--- (for a JLM, this is irrelevant)\r\n\r\n; wait for IDE controller to become \"ready\"\r\n; out: C if error occured\r\n;--- used for cmds EC (identify), EF (set features)\r\n;--- just the BSY+ERR flags are checked, \"device ready\" and DRQ are ignored!\r\n\r\nWaitRdy proc\r\n\r\n    mov esi,BIOSTMR         ;Point to low-memory BIOS timer.\r\n    mov cl,RDYTO            ;Set I-O timeout limit in CL-reg.\r\n    add cl,[esi]\r\n@@:\r\n    @VMMCall Yield\r\n    cmp cl,[esi]            ;Has our command timed out?\r\n    je Error                ;Yes, set CPU carry flag & exit.\r\n    in al,dx                ;Get IDE controller status.\r\n    test al,BSY             ;Controller or disk still busy?\r\n    jnz @B                  ;Yes, loop back and check again.\r\n    test al,ERR             ;Did command cause any errors?\r\n    jz Exit                 ;No, exit\r\nError:\r\n    stc                     ;Set carry flag (error!) and exit.\r\nExit:\r\n    ret\r\nWaitRdy endp\r\n\r\n;\r\n; Subroutine to \"validate\" an UltraDMA hard-disk.\r\n; EDI=drive\r\n; checks for ATA device and whether LBA + UDMA bits are set\r\n; On error, Carry is set and ESI -> error msg\r\n; modifies EAX, EBX, ECX, EDX, ESI, EDI\r\n;\r\nI_ValDisk proc\r\n    mov dx,[edi*2+IDEAd]\r\n    mov al,[edi+SelCmd]\r\n    add edx,CDSEL\r\n    out dx,al\r\n    inc edx\r\n    mov al,0ECh             ;Issue \"Identify Device\" command.\r\n    out dx,al\r\n    call WaitRdy\r\n    mov esi,CStr('Identify ERROR')\r\n    jc I_SErr\r\n    sub edx,CCMD-CDATA      ;Point to IDE data register.\r\n    mov edi,[dwBufferLin]\r\n    mov ecx,256\r\n    mov esi,edi\r\n    rep insw\r\n\r\n    push esi\r\n    mov edi,offset DName\r\n    mov cl,26               ;copy \"modelnumber\", \r\n    lea esi,[esi+27*2]\r\n@@:\r\n    lodsw                   ;copy ID words 27-52 to name\r\n    xchg ah,al\r\n    stosw\r\n    loop @B\r\n    pop edi\r\n\r\n    mov esi,CStr(' is no ATA device')\r\n    test @byte [edi+0*2+1],80h  ;ATAPI device?\r\n    jnz I_SErr\r\nif LBACHECK\r\n    mov esi,CStr(' does not support LBA')\r\n    mov al,[edi+49*2+1]\r\n    and al,3                ;mask LBA + DMA bits\r\n    cmp al,3\r\n    jnz I_SErr\r\nendif\r\n    @dprintf <\"ValDisk: disk supports DMA & LBA, 53=%X, 88=%X\",13,10>, word ptr [edi+53*2], word ptr [edi+88*2]\r\n\r\n    mov bl,[edi+53*2]       ;copy \"UltraDMA valid\" flag to BL\r\n    mov bh,[edi+88*2+1]     ;copy \"UltraDMA mode active\" flags in BH-reg.\r\n\r\nif 0;USECFGDPARMS\r\n;--- if LBA sectors in config data exceeds values returned by BIOS, use them if option /E is given.\r\n;---\r\n\tmov eax, [edi].IDENTIFY_DEVICE_DATA.UserAddressableSectors\r\n;    mov [xxx], eax\r\nendif\r\n\r\nif SETMODE\r\n    test bl,4           ;values in word at pos 88 valid?\r\n    jz nosetm_udma\r\n    movzx ecx,@byte [edi+88*2]  ;get valid \"UltraDMA modes\"\r\n    bsr eax, ecx        ;get highest supported UDMA mode in EAX\r\n    jz nosetm_udma      ;jump if no UDMA mode supported\r\n    mov cl,[MaxUM]\r\n    cmp cl,-1           ;/Mn switch used?\r\n    jz @F\r\n    cmp al,cl           ;AL = MIN(highest supported mode, /Mn)\r\n    jb @F\r\n    mov al,cl\r\n@@:\r\n    xor ecx,ecx\r\n    bts ecx,eax\r\n    cmp cl,bh           ;is this mode set already?\r\n    jz nosetm\r\n\tjmp setm_udma\r\n\r\n;--- check/set multi-word DMA\r\n if MWDMA\r\nnosetm_udma:\r\n\ttest [bFlags], FL_W\r\n\tjz nosetm\r\n    inc edx             ;Point to IDE \"features\" register.\r\n    mov al,SETM         ;Set mode-select subcode.\r\n    out dx,al\r\n    inc edx\t\t\t\t;Point to IDE sector-count register.\r\n;    mov al,[edi+63*2+1] ;get current MW mode\r\n    movzx ax,byte ptr [edi+63*2]   ;get supported MW modes\r\n\tbsr ax, ax\r\n\tjz nosetm\r\n\tmov ch, al\r\n    or al,020h          ; set multi-word DMA mode\r\n    out dx,al\r\n    add edx,5           ;Point to IDE cmd register.\r\n    mov al,SETFEAT      ;Issue \"set features\" command.\r\n    out dx,al\r\n    call WaitRdy        ;Await controller-ready.\r\n    mov esi,CStr(' MW-DMA mode set failed')\r\n    jc I_SErr\r\n\tmov bh,0\r\n\tjmp I_MWYes\r\n endif\r\n;--- v1.4: if any UDMA mode is active and no /M option given,\r\n;--- don't modify mode.\r\nsetm_udma:\r\n\tcmp bh, 0\r\n\tjz @F\r\n\tcmp [MaxUM],-1\r\n\tjz nosetm\r\n@@:\r\n    @dprintf <\"ValDisk: setting UDMA mode\",13,10>\r\n\r\n    mov bh,cl           ;save it in BH for later\r\n    push eax\r\n    inc edx             ;Point to IDE \"features\" register.\r\n    mov al,SETM         ;Set mode-select subcode.\r\n    out dx,al\r\n    inc edx             ;Point to IDE sector-count register.\r\n    pop eax\r\n    or al,040h\r\nsetumw:\r\n    out dx,al\r\n    add edx,5           ;Point to IDE cmd register.\r\n    mov al,SETFEAT      ;Issue \"set features\" command.\r\n    out dx,al\r\n    call WaitRdy        ;Await controller-ready.\r\n    mov esi,CStr(' UDMA mode set failed')\r\n    jc I_SErr\r\n    @dprintf <\"ValDisk: UDMA mode set successful\",13,10>\r\nnosetm:\r\n ife MWDMA\r\nnosetm_udma:\r\n endif\r\nendif\r\n\r\n    mov esi,CStr(' is not UltraDMA')\r\nif 0;MWDMA\r\n    test [bFlags],FL_W      ;handle multiword DMA devices?\r\n    jz @F \r\n    mov ch,[edi+63*2+1]     ;copy multiword flags to AH\r\n    and ch,ch               ;is a MW-DMA mode set?\r\n    jz I_MWYes\r\n    mov bh,0\r\n    jmp I_MWYes\r\n@@:\r\nendif\r\n    test bl,04h             ;UltraDMA bit set?\r\n    jz I_SErr\r\n    or bh,bh                ;any UltraDMA mode set?\r\n    jz I_SErr               ;No?  Exit & display message!\r\nI_MWYes:\r\n\r\nif MWDMA\r\n    mov eax,offset UModes   ;Point to UltraDMA mode table.\r\n    and bh,bh\r\n    jnz @F\r\n    mov bh, ch\r\n    mov eax,offset MWModes  ;Point to MW-DMA mode table\r\n@@:\r\n    mov edi, eax\r\nelse\r\n    mov edi,offset UModes   ;Point to UltraDMA mode table.\r\nendif\r\nI_NxtM:\r\n    shr bh,1                ;More UltraDMA modes to check?\r\n    jz I_GotM\r\n    inc edi                 ;Point to next mode table value.\r\n    inc edi\r\n    inc ecx                 ;Get next UltraDMA mode number.\r\n    jmp I_NxtM\r\nI_GotM:\r\n    test [bFlags],FL_Q\r\n    jnz I_Exit\r\n    mov ebx,offset DName+SIZENAME   ;Point to end of disk name.\r\n@@:\r\n    cmp ebx,offset DName    ;Are we at the disk-name start?\r\n    je @F                   ;Yes, disk name is all spaces!\r\n    dec ebx                 ;Decrement disk name pointer.\r\n    cmp @byte [ebx],' '     ;Is this name byte a space?\r\n    je @B                   ;No, continue scan for non-space.\r\n    inc ebx                 ;Skip non-space character.\r\n@@:\r\n\tmov byte ptr [ebx], 0\r\n\tmov ax,[edi]            ;Get disk \"mode\" value.\r\n\tinvoke printf, CStr(\"%s, ATA-%u\",13,10), ebx, ax\r\n    clc\r\nI_Exit:\r\n    ret\r\nI_SErr:\r\n    stc\r\n    ret\r\n    align 4\r\n\r\nI_ValDisk endp\r\n\r\ngetpci:\r\n\tmov dx, 0cf8h\r\n\tout dx, eax\r\n\tadd dl, 4\r\n\tin eax, dx\r\n\tsub dl, 4\r\n\tret\r\nsetpci:\r\n\tmov dx, 0cf8h\r\n\tout dx, eax\r\n\tadd dl, 4\r\n\tmov eax, ecx\r\n\tout dx, eax\r\n\tsub dl, 4\r\n\tret\r\n\r\n;--- get DMA controller port for a HD.\r\n;--- for a \"native\" controller, IDE port bases are in registers 0-3:\r\n;--- 0+1=primary base+alternate, 2+3=secondary base+alternate\r\n;--- in: BX=bus/device/func\r\n;---     EDI=drive#\r\n;---     [edi][IDEAd]=IDE port\r\n;--- out: CX=DMA port\r\n\r\nI_GetDMAPort proc uses esi\r\n\r\n\tmovzx ebx, bx\r\n\tshl ebx, 8\r\n\tbts ebx, 31\r\n\r\n;--- v1.5: ensure that Busmaster flag is set in command register\r\nif SETBM\r\n\tmov eax, ebx\r\n\tmov al, 4               ;read command register (byte)\r\n\tcall getpci\r\n\tbts eax, 2              ;busmaster set?\r\n\tjc @F\r\n\tmov ecx, eax\r\n\tmov eax, ebx\r\n\tmov al, 4\r\n\tcall setpci\r\n@@:\r\nendif\r\n\tmov eax, ebx\r\n\tmov al,16+4*4           ;Get PCI DMA base address (register 4).\r\n\tcall getpci\r\n\tmov ecx, eax\r\n\tand cl, 0fch\r\n\r\n\tmov eax, ebx\r\n\tmov al, 8               ;get class code\r\n\tcall getpci\r\n\tshr eax, 8\r\n\tor esi, 1111b\r\n\tcmp ah, 6               ;SATA controller? then no \"legacy\" bits\r\n\tjz @F\r\n\tmov esi, eax\r\n@@:\r\n\tmov eax, 1F0h\r\n\tbt esi, 0               ;primary native?\r\n\tjnc @F\r\n\tmov eax, ebx\r\n\tmov al,16+0*4           ;Get primary IDE base address\r\n\tcall getpci\r\n\tand al, 0fch\r\n@@:\r\n\t@dprintf <\"GetPorts: cmp pri base=%X-%X\",13,10>, eax, word ptr [edi*2+IDEAd]\r\n\tcmp ax,[edi*2+IDEAd]\r\n\tjz found\r\n\tmov eax, 170h\r\n\tbt esi, 2               ;secondary native?\r\n\tjnc @F\r\n\tmov eax, ebx\r\n\tmov al,16+2*4           ;Get secondary IDE base address\r\n\tcall getpci\r\n\tand al, 0fch\r\n@@:\r\n\t@dprintf <\"GetPorts: cmp sec base=%X-%X\",13,10>, eax, word ptr [edi*2+IDEAd]\r\n\tcmp ax,[edi*2+IDEAd]\r\n\tstc\r\n\tjnz exit \r\n\tadd ecx, 8\r\nfound:\r\n\t@dprintf <\"GetPorts: DMA=%X\",13,10>, ecx\r\n\tclc\r\nexit:\r\n\tret\r\n\talign 4\r\nI_GetDMAPort endp\r\n\r\n;--- get Ultra-DMA port for EDD 2.0 (or 3.0 if no BIOS disk has been found)\r\n;--- inp: EDI=drive\r\n;--- out: NC + DMA controller port base in CX\r\n;--- C on errors\r\n\r\n;--- PCI command register\r\n;--- 0001: I/O access enabled\r\n;--- 0002: memory access enabled\r\n;--- 0004: BM access enabled\r\n;--- 0008:\r\n\r\nI_GetUDMAC proc\r\n\tmov esi, offset controllers\r\nNextIF:\r\n\tlodsw\r\n\tcmp ax,-1\r\n\tjz error\r\n\tmovzx ebx, ax\r\nifdef _DEBUG\r\n\tshr eax, 8\r\n\tmovzx eax, al\r\n\tmov edx, ebx\r\n\tshr edx, 3\r\n\tand edx, 1Fh\r\n\tmov ecx, ebx\r\n\tand ecx, 7\r\n\t@dprintf <\"GetUDMAC: check IDE controller Bus/Dev/Fn=%u/%u/%u\",13,10>, eax, edx, ecx\r\nendif\r\nife SETBM\r\n\tmov eax, ebx\r\n\tshl eax, 8\r\n\tbts eax, 31\r\n\tmov al, 4               ;Get PCI command + status register\r\n\tcall getpci\r\n\tmov ecx, eax\r\n\t@dprintf <\"GetUDMAC: cmd+status register=%X\",13,10>, ecx\r\n\tand ecx,4+1             ;Mask Bus-Master and I-O Space bits.\r\n\tcmp ecx,4+1             ;Is this how our controller is set up?\r\n\tjnz @F\r\nendif\r\n\tcall I_GetDMAPort\r\n\tjnc exit\r\n@@:\r\n\tcmp esi, offset controllers + sizeof controllers\r\n\tjb NextIF               ; go try next one.\r\nerror:\r\n\tstc\r\nexit:\r\n\tret\r\n\talign 4\r\nI_GetUDMAC endp\r\n\r\n;--- get cmdline parameters\r\n;--- modifies ESI\r\n\r\nI_GetParams proc\r\n    mov esi,[dwCmdLine]\r\nI_NxtC:\r\n    lodsb\r\n    cmp al,0\r\n    je I_Term\r\n    cmp al,LF\r\n    je I_Term\r\n    cmp al,CR\r\n    je I_Term\r\n    cmp al,'-'\r\n    je I_NxtS\r\n    cmp al,'/'\r\n    jne I_NxtC      ;No, check next command-line byte.\r\nI_NxtS:\r\n    mov ax,[esi]\r\n    or ax,2020h     ;convert to lower-case\r\n    cmp al,'l'      ;/L?\r\n    jne I_ChkQ      ;No, go see if byte is \"Q\" or \"q\".\r\n    mov @byte [@DMALmt],009h ;Set 640K \"DMA limit\" above.\r\n    inc esi         ;Point to next command-line byte.\r\nI_ChkQ:\r\n    cmp al,'q'      ;/Q?\r\n    jnz @F\r\n    or [bFlags],FL_Q\r\n    inc esi\r\n@@:\r\nif SCATTER\r\n    cmp al,'f'      ;/F?\r\n    jnz @F\r\n    or [bFlags],FL_F\r\n    inc esi\r\n@@:\r\nendif\r\nif MWDMA\r\n    cmp al,'w'      ;/W?\r\n    jnz @F\r\n    or [bFlags],FL_W\r\n    inc esi\r\n@@: \r\nendif\r\nif SETMODE\r\n    cmp al,'m'      ;/M?\r\n    jne @F\r\n    inc esi         ;Bump pointer past \"mode\" switch.\r\n    cmp ah,'7'\r\n    ja I_NxtC\r\n    sub ah,'0'\r\n    jb I_NxtC\r\n    mov [MaxUM],ah  ;Set maximum UltraDMA \"mode\" above.\r\n    inc esi         ;Bump pointer past \"mode\" value.\r\n@@:\r\nendif\r\nif BUFONLY\r\n    cmp al,'b'      ;/B?\r\n    jnz @F\r\n    inc esi\r\nBUFOFS equ offset BufIO - (offset @BufPatch + 5)\r\n    mov @byte [@BufPatch], 0E9h\r\n    mov @dword [@BufPatch+1], BUFOFS\r\n    jmp I_NxtC\r\n@@:\r\nendif\r\n    jmp I_NxtC      ;Continue scanning for a terminator.\r\nI_Term:\r\n    ret\r\n    align 4\r\nI_GetParams endp\r\n\r\n;--- Init XMS\r\n;--- on errors, set Carry and error msg in ESI\r\n\r\nInitXMS proc\r\n    mov ax, 4300h           ;Inquire about an XMS manager.\r\n    push 2Fh\r\n    call IntXX\r\n    mov eax, [ebp].Client_Reg_Struc.Client_EAX\r\n    cmp al,080h             ;Is an XMS manager installed?\r\n    jne I_XErr1             ;No, display message & disable XMS.\r\n    mov ax, 4310h           ;Get XMS manager \"entry\" address.\r\n    push 2Fh\r\n    call IntXX\r\n    mov bx, @word [ebp].Client_Reg_Struc.Client_ES\r\n    shl ebx, 16\r\n    mov bx, @word [ebp].Client_Reg_Struc.Client_EBX\r\n    mov [XMSEntry], ebx\r\n\r\n    mov ah,009h             ;Ask XMS manager for 128K of memory.\r\n    mov dx,128\r\n    call I_XMS\r\n    jnz I_XErr2              ;If error, display msg. & disable XMS.\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EDX\r\n    mov [XMSHdl],edx        ;Save XMS buffer handle number.\r\n\r\n    mov ah,00Ch             ;\"Lock\" our XMS memory.\r\n    call I_XMS\r\n    jnz I_XErr3             ;If error, display msg. & disable XMS.\r\n\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EDX\r\n    shl eax,16              ;Get unaligned XMS buffer address.\r\n    mov ax,@word [ebp].Client_Reg_Struc.Client_EBX\r\n    add eax,65536-1         ;Find 1st 64K boundary after start.\r\n    xor ax,ax\r\n    mov [dwBufferPhy],eax   ;Set physical buffer address\r\n    ret\r\nI_XErr1:\r\n    mov esi,CStr('No XMS manager')\r\n    stc\r\n    ret\r\nI_XErr2:\r\n    mov esi,CStr('XMS 128 kB memory allocation failed')\r\n    stc\r\n    ret\r\nI_XErr3:\r\n    mov esi,CStr('XMS lock memory error')\r\n    stc\r\n    ret\r\n    align 4\r\nInitXMS endp\r\n\r\n;--- in: EDI=drive#\r\n;---     CX=DMA port\r\n;--- display current values for disk\r\n;--- out: \r\n;---     DMAAd\r\n;---     CHSHd\r\n;---     CHSSec\r\n\r\nI_SetDisk proc\r\n    and cl,0FCh\r\n    mov [edi*2+DMAAd],cx\r\n\r\n    test [bFlags],FL_Q\r\n    jnz I_NoDskDisp\r\n\tmov esi, CStr('Master')\r\n\ttest [edi+SelCmd],10h   ;Is this disk the master?\r\n\tjz @F                   ;Yes, display \"master\" name.\r\n\tmov esi, CStr('Slave')\r\n@@:\r\n\tinvoke printf, CStr(\"HD %u, %s, ATA/DMA ports %X/%X\"), edi, esi, [edi*2][IDEAd], [edi*2][DMAAd]\r\n\r\nI_NoDskDisp:\r\n\r\n    mov ah,08h              ;Get BIOS CHS values for this disk.\r\n    call Int13\r\n    jc  I_CHSE              ;If BIOS error, zero sectors/head.\r\n    mov ecx,[ebp].Client_Reg_Struc.Client_ECX\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EDX\r\n    @dprintf <\"SetDisk: int 13, ax=8 ok, cx=%X dx=%X\",13,10>, cx, dx\r\n    and ecx,03Fh            ;Get sectors/head value (low 6 bits).\r\n    inc dh                  ;Get heads/cylinder (BIOS value + 1).\r\n    jnz I_SetC              ;If non-zero, save disk's CHS values.\r\nI_CHSE:\r\n    xor cl,cl               ;CHS error!  Zero disk's sectors/head.\r\nI_SetC:\r\n    mov [edi+CHSHd],dh      ;Save disk's CHS values in our tables.\r\n    mov [edi+CHSSec],cl\r\n    push edi\r\n    call I_ValDisk\r\n    pop edi\r\n    jc I_ErrD               ;If any errors, DELETE this disk!\r\n    cmp [edi+CHSSec],0      ;Were disk's CHS values legitimate?\r\n    jne exit                ;Yes, check for more disks to use.\r\n    mov esi,CStr(\"** BIOS must do above disk's CHS I-O\")\r\nI_ErrD:\r\n    invoke printf, CStr(\"%s\",13,10), esi ;Display error for this disk.\r\n    stc\r\nexit:\r\n    ret\r\nI_SetDisk endp\r\n\r\n;\r\n; initialization\r\n;\r\nInitXDMA proc\r\n    cld\r\n    @dprintf <\"InitXDMA entry, eflags=%X\",13,10>, [ebp].Client_Reg_Struc.Client_EFlags\r\n    call I_GetParams        ;get cmdline params\r\n\ttest [bFlags],FL_Q\r\n\tjnz @F\r\n\tinvoke printf, CStr(\"XDMA32\",VERSION,CR,LF)\r\n@@:\r\n    mov [ebp].Client_Reg_Struc.Client_EDI, 0    ;Get PCI BIOS \"I.D.\" code.\r\n    mov ax, 0B101h\r\n    push 1ah\r\n    call IntXX\r\n    mov edx, [ebp].Client_Reg_Struc.Client_EDX\r\n    mov esi,CStr('PCI BIOS Invalid')\r\n    cmp edx,\" ICP\"          ;Is PCI BIOS V2.0C or newer?\r\n    jne I_Err               ;Go display error message and exit.\r\n\r\n    @dprintf <\"PCI BIOS detected\",13,10>\r\n\r\n;--- get all PCI IDE controllers\r\n;--- todo: also get SATA controllers with AHCI disabled.\r\n\r\n\tmov esi, 80000008h\r\n\tmov edi, offset controllers\r\nnextctrl:\r\n\tmov eax, esi\r\n\tcall getpci\r\n\tshr eax, 8\r\n\tand al, 80h\r\n\tcmp eax, 10180h\r\n\tjnz @F\r\n\tmov eax, esi\r\n\tshr eax, 8\r\n\tstosw\r\nifdef _DEBUG\r\n\tmovzx eax, ax\r\n\tmov ecx, eax\r\n\tshr ecx, 3\r\n\tand ecx, 1Fh\r\n\tmov edx, eax\r\n\tand edx, 7\r\n\tshr eax, 8\r\n\t@dprintf <\"PCI IDE controller found at %u/%u/%u\",13,10>, eax, ecx, edx\r\nendif\r\n\tcmp edi, offset controllers + sizeof controllers\r\n\tjnc donectrl\r\n@@:\r\n\t@VMMCall Yield\r\n\tadd esi, 100h\r\n\tcmp esi, 81000008h\r\n\tjb nextctrl\r\ndonectrl:\r\n\r\n    mov ax,03513h           ;Get and save current Int 13h vector.\r\n    push 21h\r\n    call IntXX\r\n    mov bx, @word [ebp].Client_Reg_Struc.Client_ES\r\n    shl ebx, 16\r\n    mov bx, @word [ebp].Client_Reg_Struc.Client_EBX\r\n    mov [PrvI13], ebx\r\n\r\n    call InitXMS            ;init XMS\r\n    jc I_Err\r\n\r\n    @dprintf <\"XMS allocated\",13,10>\r\n\r\n    push 0\r\n    push 16\r\n    push PR_SYSTEM\r\n    @VMMCall _PageReserve    ;allocate a 64 kB block of address space\r\n    add esp,3*4\r\n    mov esi,CStr('No address space for buffer')\r\n    cmp eax,-1\r\n    jz I_Err\r\n    mov dwBufferLin, eax\r\n    shr eax, 12             ;convert linear address to page number\r\n\r\n    @dprintf <\"Init: Buffer Address Space allocated\",13,10>\r\n\r\n    push PC_INCR or PC_WRITEABLE\r\n    mov edx, [dwBufferPhy]\r\n    shr edx, 12\r\n    push edx\r\n    push 16\r\n    push eax\r\n    @VMMCall _PageCommitPhys ;backup address space with XMS memory\r\n    add esp,4*4\r\n\r\n    @dprintf <\"Init: Buffer committed\",13,10>\r\n\r\n;    mov ecx,offset Entry - offset startcode\r\n;    mov esi,offset startcode\r\n    mov ecx,4*4             ; max 16 byte\r\n    mov esi,offset IOAdr\r\n    xor edx, edx\r\n    VxDCall VDMAD_Lock_DMA_Region\r\n;    add [PRDAd],edx         ;Set relocated 32-bit PRD address.\r\n    mov [PRDAd], edx        ;Set relocated 32-bit PRD address.\r\n    @dprintf <\"Init: DMA PRD address=%X\",13,10>, edx\r\n\r\nif HDNUM\r\n    mov al,ds:[HDISKS]      ;Did BIOS find any hard-disks?\r\nelse\r\n    mov [HDUnit],80h        ;set hard-disk unit number\r\n    mov ah,08h              ;get info\r\n    call Int13              ;AX,BX,DL set in Int13\r\n    jc I_None\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EDX\r\nendif\r\n    cmp al,0\r\n    jz I_None               ;No?  Display \"No disk\" and exit!\r\n    mov [BiosHD],al         ;Save BIOS hard-disk count.\r\n\r\n    @dprintf <\"Int13 disk scan:\",13,10>\r\n\r\n;--- scan the HDs, max 2 times\r\n;--- first with int 13h calls (ah=41h)\r\n;--- if none found, try with int 1ah (\"PCI disk scan\").\r\n\r\n    mov [HDUnit],80h        ;Reset hard-disk unit number\r\n    mov al,[BiosHD]         ;Reset remaining hard-disk count.\r\n    mov [HDCount],al\r\n    xor edi,edi             ;Init unit table index\r\nNextDev:\r\n    mov ah, 41h             ;Get EDD \"extensions\" for this disk.\r\n    mov bx, 55AAh\r\n    call Int13\r\n    jc I_SkipDrv            ;If none, ignore disk & check for more.\r\n    @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 \r\n\r\n;--- in AH EDD version is returned, ah=30 for version EDD 3.0\r\n\r\n    mov ebx,[ebp].Client_Reg_Struc.Client_EBX\r\n    mov ecx,[ebp].Client_Reg_Struc.Client_ECX\r\n    cmp bx,0AA55h           ;Did BIOS \"reverse\" our entry code?\r\n    jne I_SkipDrv           ;No, ignore this disk & check for more.\r\n\r\n;--- v1.4: checking bit 2 is not reliable, so checking bit 0 may be sufficient\r\n\r\n    test cl,101b            ;Does this disk have \"EDD\" extensions?\r\n    jz I_SkipDrv            ;No, ignore this disk & check for more.\r\n\r\n    mov eax,[wBaseSeg]\r\n    mov @word [ebp].Client_Reg_Struc.Client_DS, ax\r\n    mov eax, 20h            ;don't touch the first 32 bytes\r\n    mov [ebp].Client_Reg_Struc.Client_ESI, eax\r\n    mov esi, [dwBase]\r\n    add esi, eax\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EAX\r\n    mov @dword [esi].EDD20.cbSize, sizeof EDD30  ;set size and clear flags\r\n    mov [esi].EDD20.lpCfg, -1\r\n    cmp ah, 30h\r\n    jnc @F\r\n    mov [esi].EDD20.cbSize, sizeof EDD20\r\n@@:\r\n\r\n;--- call int 13h, ah=48h: Get this disk's \"EDD\" parameters.\r\n;--- v86 DS:SI=EDD, has been set above to wBaseSeg:0020h\r\n\r\n    mov ah, 48h\r\n    call Int13\r\n    jc I_ErED               ;Error?  Display msg. & ignore!\r\n    @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\r\n    cmp [esi].EDD10.cbSize, sizeof EDD20   ;is it at least EDD20?\r\n    jb I_SkipDrv             ;No, ignore disk & check more.\r\n    movzx ecx, @word [esi].EDD20.lpCfg+0\r\n    movzx edx, @word [esi].EDD20.lpCfg+2\r\n    shl edx, 4\r\n    add edx, ecx            ;EDX=this disk's \"DPTE\" pointer.\r\n    cmp [esi].EDD10.cbSize, sizeof EDD30   ;EDD 3.0?\r\n    jnc I_IsEDD30\r\n    cmp [esi].EDD20.lpCfg,-1;Valid \"DPTE\" pointer?\r\n    je I_SkipDrv            ;No, ignore disk & check more.\r\n    mov ebx,15              ;Calculate \"DPTE\" checksum.\r\n    xor ecx,ecx\r\n@@:\r\n    add cl,[ebx+edx]\r\n    dec ebx\r\n    jns @B\r\n    jecxz I_IsEDD20         ;If checksum O.K., use parameters.\r\nI_ErED:\r\n    invoke printf, CStr('EDD BIOS error!  Unit ignored',CR,LF)\r\n    jmp I_SkipDrv\r\n\r\nI_IsEDD20:\r\n    @dprintf <\"drive is EDD20, Port=%X, Stat=%X, flgs=%X\",13,10>, [edx].DPTE.wIDEBase, [edx].DPTE.wIDEAlt, word ptr [edx].DPTE.bFlags\r\n    mov ax,[edx].DPTE.wIDEBase  ;Get disk's IDE base address.\r\n    and ax,ax\r\n    jz I_ErED\r\n    mov cx,[edx].DPTE.wIDEAlt   ;Get disk's IDE status address.\r\n    mov bl,[edx].DPTE.bFlags\r\n    and bl,10h              ;use the \"slave\" flags\r\n    or bl,0E0h\r\n\r\n    mov [edi*2+IDEAd],ax\r\n    mov [edi*2+IDEAlt],cx\r\n    mov [edi*1+SelCmd],bl\r\n\r\n    call I_GetUDMAC\r\n    jc I_SkipDrv\r\n    jmp I_EDDdone\r\n\r\n;--- BIOS/drive is EDD30\r\n;--- edx=DPTE (if lpCfg != -1)\r\n\r\nI_IsEDD30:\r\n    @dprintf <\"drive is EDD30, szIFType=%X\",13,10>, dword ptr [esi].EDD30.szIFType\r\n    mov eax, dword ptr [esi].EDD30.szIFType\r\n    cmp eax ,\"ATA\"          ;ATA interface?\r\n    jz @F\r\n    cmp eax ,\" ATA\"\r\n    jz @F\r\n    cmp eax ,\"ATAS\"                ;for SATA, check if AHCI mode is disabled!\r\n    jnz I_SkipDrv\r\n@@:\r\n    mov al, [esi].EDD30.szDevPath  ;for ATA, device path contains master/slave flag\r\n    shl al, 4\r\n    or al,0E0h\r\n    mov [edi*1+SelCmd],al\r\n    mov eax, dword ptr [esi].EDD30.szBus\r\n    and eax, 0FFFFFFh\r\n    cmp eax ,\"ASI\"                 ;ISA host bus?\r\n    jnz @F\r\n    mov ax, word ptr [esi].EDD30.qwIFPath   ;for ISA, interface path contains IDE port base\r\n    mov [edi*2+IDEAd],ax\r\n    call I_GetUDMAC\r\n    jnc I_EDDdone\r\n    @dprintf <\"GetUDMAC failed for drive\",13,10>\r\n    jmp I_SkipDrv\r\n\r\n@@:\r\n;--- lpCfg might be -1!\r\n;--- if this happens, we can't distinguish primary and secondary\r\n\r\n    cmp [esi].EDD20.lpCfg,-1    ; Valid \"DPTE\" pointer?\r\n    jz I_SkipDrv\r\n    mov ax,[edx].DPTE.wIDEBase  ; the PCI info doesn't tell if prim or secondary!\r\n    and ax,ax\r\n    jz I_ErED\r\n    mov [edi*2+IDEAd],ax        ; so we need the DPTE\r\n@@:\r\n    mov bh, [esi].EDD30.qwIFPath   ;get interface path (Bus)\r\n    mov bl, [esi].EDD30.qwIFPath+1 ;get interface path (Device)\r\n    mov al, [esi].EDD30.qwIFPath+2 ;get interface path (Function)\r\n    shl bl,3\r\n    and al,7\r\n    or bl,al\r\n    call I_GetDMAPort\r\n\r\nifdef _DEBUG\r\n    jnc I_EDDdone\r\n    @dprintf <\"I_GetPorts failed for drive\",13,10>\r\n    jmp I_SkipDrv\r\nelse\r\n    jc I_SkipDrv\r\nendif\r\nI_EDDdone:\r\n    call I_SetDisk\r\n    jc I_SkipDrv\r\n    mov al,[HDUnit]         ;Activate this disk in main driver.\r\n    mov [edi+Units],al\r\n    inc edi\r\n    cmp edi,NUMDSK          ;free entry in unit table?\r\n    je I_DiskFound\r\nI_SkipDrv:\r\n    inc [HDUnit]            ;Bump BIOS unit\r\n    dec @byte [HDCount]     ;More BIOS disks to check?\r\n    jnz NextDev             ;Yes, loop back and do next one.\r\n    and edi,edi             ;any disk in unit table?\r\n    jnz I_DiskFound\r\n\r\n;--- PCI scan\r\n\r\nif ?PCISCAN\r\n\tcmp [controllers], -1   ; any EIDE controller found?\r\n\tjz I_None               ; if no, the PCI scan is useless.\r\n\r\n    @dprintf <'PCI disk scan:',CR,LF>\r\n    mov [HDUnit], 80h       ;Reset hard-disk unit number\r\n    mov al,[BiosHD]         ;Reset remaining hard-disk count.\r\n    mov [HDCount], al\r\n    xor edi, edi            ;Init unit table index\r\nNextDev2:\r\n    call I_GetUDMAC\r\n    jc @F\r\n    call I_SetDisk\r\n    jc @F\r\n    mov al,[HDUnit]         ;Activate this disk in main driver.\r\n    mov [edi+Units],al\r\n    inc edi\r\n    cmp edi,NUMDSK          ;free entry in unit table?\r\n    je I_DiskFound\r\n@@:\r\n    inc [HDUnit]            ;Bump BIOS unit\r\n    dec @byte [HDCount]     ;More BIOS disks to check?\r\n    jnz NextDev2            ;Yes, loop back and do next one.\r\nendif\r\n    and edi,edi             ;any disk in unit table?\r\n    jz I_None\r\n\r\nI_DiskFound:\r\n\r\n    @dprintf <\"Scanning done\",13,10>\r\n\r\n    mov @dword [@LastU],edi ;Post last-unit index in main driver.\r\n\r\n    mov esi, offset Entry\r\n    xor edx, edx\r\n    @VMMCall Allocate_V86_Call_Back\r\n    mov esi,CStr('No callbacks available anymore',CR,LF)\r\n    jc I_Err\r\n    mov @word [ebp].Client_Reg_Struc.Client_EDX, ax\r\n    shr eax, 16\r\n    mov @word [ebp].Client_Reg_Struc.Client_DS, ax\r\n    mov ax,02513h\r\n    push 21h\r\n    call IntXX              ;\"Hook\" this driver into Int 13h.\r\n\r\n    mov ax,RPDONE           ;Get initialization \"success\" code.\r\n    jmp I_Exit\r\nI_None:\r\n    mov esi,CStr('No disk to use')\r\nI_Err:\r\n    mov edx,[XMSHdl]        ;Get XMS memory \"handle\".\r\n    or dx,dx                ;Did we reserve XMS memory?\r\n    jz @F                   ;No, go display error message.\r\n    call I_ReleaseXMS       ;Get rid of our XMS buffer.\r\n@@:\r\n    invoke printf, CStr(\"%s; XDMA32 not loaded!\",CR,LF), esi\r\n    mov ax,RPDONE+RPERR\r\nI_Exit:\r\n    @dprintf <\"Init exit\",13,10>\r\n    ret\r\nInitXDMA endp\r\n;\r\n;--- free XMS memory block on errors\r\n;\r\nI_ReleaseXMS proc\r\n    mov ah,00Dh     ;Error -- unlock & free XMS buffer.\r\n    push edx\r\n    call I_XMS\r\n    mov ah,00Ah\r\n    pop edx\r\nI_ReleaseXMS endp\r\n\r\n;--- fall thru\r\n\r\nI_XMS proc\r\n    mov [ebp].Client_Reg_Struc.Client_EAX, eax\r\n    mov [ebp].Client_Reg_Struc.Client_EDX, edx\r\n    @VMMCall Begin_Nest_Exec\r\n    movzx edx, @word [XMSEntry+0]\r\n    mov cx, @word [XMSEntry+2]\r\n    @VMMCall Simulate_Far_Call\r\n    @VMMCall Resume_Exec\r\n    @VMMCall End_Nest_Exec\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EAX\r\n    dec ax              ;Zero AX-reg. if success, -1 if error.\r\n    ret\r\nI_XMS endp\r\n\r\n;--- call int 13h,\r\n;--- ah=08h, DL=dsk\r\n;--- ah=41h, DL=dsk, BX=55AAh\r\n;--- ah=48h, DL=dsk, DS:SI->EDD1/2/3\r\n\r\nInt13 proc\r\n    mov @word [ebp].Client_Reg_Struc.Client_EBX, bx\r\n    mov dl,[HDUnit] ;Set BIOS unit in DL-reg.\r\n    mov @byte [ebp].Client_Reg_Struc.Client_EDX, dl\r\n    push 13h\r\n    call IntXX\r\n    ret\r\nInt13 endp\r\n\r\n;--- call int 1Ah (ax=B101h)\r\n;--- call int 21h (ah=25h/35h)    \r\n;--- call int 2Fh (ax=4300h, 4310h)\r\n;--- 1A, B101h: install check, in: edi=0, out: edx=\"PCI \", BX=version\r\n\r\nIntXX proc\r\n    mov @word [ebp].Client_Reg_Struc.Client_EAX, ax\r\n    @VMMCall Begin_Nest_Exec\r\n    mov eax, [esp+4]\r\n    @VMMCall Exec_Int\r\n    @VMMCall End_Nest_Exec\r\n    mov ah,@byte [ebp].Client_Reg_Struc.Client_EFlags\r\n    sahf\r\n    ret 4\r\nIntXX endp\r\n\r\nDllMain proc stdcall public hModule:dword, dwReason:dword, dwRes:dword\r\n\r\n    .if dwReason == 1\r\n        mov esi, dwRes\r\n        movzx ecx,[esi].JLCOMM.wLdrCS\r\n        mov wBaseSeg,ecx\r\n        shl ecx, 4\r\n        mov dwBase, ecx\r\n        mov eax,[esi].JLCOMM.lpCmdLine\r\n        mov dwCmdLine, eax\r\n\r\n;--- set EBP to the client pointer before calling InitXDMA\r\n\r\n        push ebp\r\n        @VMMCall Get_Cur_VM_Handle   ;get VM handle in EBX\r\n        mov ebp,[ebx].cb_s.CB_Client_Pointer\r\nif SAVESTAT\r\n        sub esp, sizeof Client_Reg_Struc\r\n        mov edi, esp\r\n        @VMMCall Save_Client_State\r\nendif\r\n        push esi\r\n        call InitXDMA\r\n        pop esi\r\n        test [esi].JLCOMM.wFlags, JLF_DRIVER    ;loaded as DOS device driver?\r\n        jz @F\r\n        mov ebx,[esi].JLCOMM.lpRequest\r\n        mov [ebx].RP.RPStat,ax\r\n        mov ecx, [wBaseSeg]\r\n        mov @word [ebx].RP.RPSize+2,cx\r\n        xor ecx, ecx\r\n        mov @word [ebx].RP.RPSize+0,cx\r\n@@:\r\n        cmp ax, RPDONE\r\n        setz al\r\n        movzx edi, al\r\n        @dprintf <\"exit DllMain\",13,10>\r\nif SAVESTAT\r\n        mov esi, esp\r\n        @VMMCall Restore_Client_State\r\n        add esp, sizeof Client_Reg_Struc\r\nendif\r\n        pop ebp\r\n        mov eax, edi\r\n    .endif\r\n    ret\r\n\r\nDllMain endp\r\n\r\n    end DllMain\r\n"
  },
  {
    "path": "JLM/XDMA32/XDMA32.txt",
    "content": "\r\n 1. About XDMA32\r\n\r\n XDMA32 is a JLM based on Jack R. Ellis' XDMA Ultra-DMA HD driver.\r\n It supports PCI IDE controllers running in \"legacy\" or \"native\" mode.\r\n\r\n\r\n 2. Usage\r\n \r\n To load XDMA32, add the following line to your CONFIG.SYS:\r\n\r\n   DEVICE=JLOAD.EXE XDMA32.DLL [options]\r\n\r\n options are:\r\n\r\n  /B   always use XMS buffer for file i/o. Might be useful in virtualized\r\n       environments which can't handle DMA properly for all addresses.\r\n       This switch is more restrictive than /L, and it disables /F.\r\n  /F   \"fast\", uses DMA \"scatter/gather\" method to allow transfers\r\n       which cross a physical 64 kB boundary. Might not work with all\r\n       controllers, so use with care! (The \"fast\" is a historical\r\n       remnant, on modern systems there will be no significant difference\r\n       in speed.)\r\n  /L   limits DMA to \"low memory\" below 640 kB. Addresses above are \r\n       handled through the XMS buffer.\r\n  /Mn  set/restrict UDMA mode to n (0 <= n <= 7).\r\n  /Q   quiet mode.\r\n  /W   handle (non-UDMA) drives which are capable of Multiword-DMA.\r\n\r\n\r\n  3. Features & Restrictions\r\n\r\n  - XDMA32 supports up to 4 PCI IDE controllers. May be changed in XDMA32.ASM\r\n    ( NUMCTRL constant ).\r\n  - XDMA32 supports up to 8 HDs. May be changed in XDMA32.ASM\r\n    ( NUMDSK constant ).\r\n  - HDs are accessed using either CHS, LBA28 or LBA48 addressing, so there's\r\n    no (practical) size restriction.\r\n  - XDMA32 has no device ID associated with it - hence jload cannot detect\r\n    currently if the driver is already installed.\r\n  - SATA controllers running in AHCI mode are ignored.\r\n  - the XDMA32 driver needs just 48 bytes in conventional or upper memory.\r\n  - a 128 kB extended memory block is allocated as a buffer to be used if the\r\n    current transfer address is incompatible with DMA access.\r\n\r\n\r\n  4. License\r\n\r\n  XDMA32 is released under the GNU GPL v2. See GNU_GPL.TXT for details.\r\n\r\n  Japheth\r\n"
  },
  {
    "path": "JemmExL.mak",
    "content": "#\r\n# builds JemmExL, the \"legacy\" variant of JemmEx, without support\r\n# of XMS v3.5 ( super-extended memory )\r\n\r\nNAME3=JEMMEXL\r\n\r\n!ifndef DEBUG\r\nDEBUG=0\r\n!endif\r\n\r\n# to create kernel debugger aware versions, add \"kd=1\" to nmake\r\n!ifndef KD\r\nKD=0\r\n!endif\r\n\r\nASM=jwasm.exe\r\n\r\n# select 32-bit COFF linker, default JWLink\r\n\r\n!ifndef JWLINK32\r\nJWLINK32=0\r\n!endif\r\n!ifndef WLINK32\r\nWLINK32=0\r\n!endif\r\n!ifndef MSLINK32\r\nMSLINK32=0\r\n!endif\r\n!if $(JWLINK32)+$(WLINK32)+$(MSLINK32)==0\r\nJWLINK32=1\r\n!endif\r\n\r\n# select 16-bit OMF linker, default JWLink\r\n\r\n!ifndef JWLINK\r\nJWLINK=0\r\n!endif\r\n!ifndef WLINK\r\nWLINK=0\r\n!endif\r\n!ifndef MSLINK\r\nMSLINK=0\r\n!endif\r\n!if $(JWLINK)+$(WLINK)+$(MSLINK)==0\r\nJWLINK=1\r\n!endif\r\n\r\n!if $(DEBUG)\r\nAOPTD=-D_DEBUG $(DBGOPT) -Sg\r\n!else\r\nAOPTD=\r\n!endif\r\n\r\nAOPT=-c -nologo -IInclude $(AOPTD)\r\n\r\n# list of 32bit modules\r\nCOFFMODS=.\\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\r\n\r\nBUILD=build\r\n\r\n!if $(DEBUG)\r\nOUTD3=$(BUILD)\\$(NAME3)D\r\nCOFFDEP3=$(COFFMODS:.\\=build\\JEMMEXLD\\)\r\n!else\r\nOUTD3=$(BUILD)\\$(NAME3)\r\nCOFFDEP3=$(COFFMODS:.\\=build\\JEMMEXL\\)\r\n!endif\r\n\r\n\r\n!if $(JWLINK32)\r\nLINK32=jwlink format raw bin file {$(COFFMODS:.\\=)} name jemm32.bin disable 1014 option offs=0x110000, start=_start, map=jemm32.map, quiet\r\n!elseif $(WLINK32)\r\nLINK32= wlink format raw bin file {$(COFFMODS:.\\=)} name jemm32.bin option offs=0x110000, start=_start, map=jemm32.map, quiet\r\n!else\r\nCOFFOPT=/fixed /driver /subsystem:native /entry:start /base:0x100000 /align:0x10000 /MAP /nologo\r\n# MS link (newer versions won't accept option FILEALIGN anymore)\r\nLINK32=link.exe /FileAlign:0x200 $(COFFOPT) $(COFFMODS:.\\=) /OUT:jemm32.bin \r\n!endif\r\n\r\n!if $(JWLINK)\r\nLINK16=jwlink.exe format dos file jemm16.obj,init16.obj name $(*B).EXE option map=$(*B).MAP, quiet\r\n!elseif $(WLINK)\r\nLINK16=wlink.exe format dos file jemm16.obj,init16.obj name $@.EXE option map=$@.MAP, quiet\r\n#else\r\nLINK16=link16.exe /NOLOGO/MAP:FULL/NOD /NOI jemm16.obj init16.obj,$@.EXE,$@.MAP;\r\n!endif\r\n\r\n32BITDEPS=src\\jemm32.inc src\\jemm.inc src\\extern32.inc src\\debug32.inc JemmExL.mak\r\n\r\n{src\\}.asm{$(OUTD3)}.obj:\r\n\t@$(ASM) -coff -D?INTEGRATED=1 -D?XMS35=0 $(AOPT) -Fl$(OUTD3)\\ -Fo$(OUTD3)\\ $<\r\n\r\nALL: $(BUILD) $(OUTD3) $(OUTD3)\\$(NAME3).EXE\r\n\r\n$(BUILD) $(OUTD3):\r\n\t@mkdir $*\r\n\r\n$(OUTD3)\\$(NAME3).EXE: $(OUTD3)\\jemm16.obj $(OUTD3)\\init16.obj\r\n\t@cd $(OUTD3)\r\n\t@$(LINK16)\r\n\t@cd ..\\..\r\n\r\n$(OUTD3)\\init16.obj: src\\init16.asm src\\jemm16.inc src\\jemm.inc Include\\jsystem.inc JemmExL.mak\r\n\t@$(ASM) -D?INTEGRATED=1 -D?XMS35=0 $(AOPT) -Sg -Fl$(OUTD3)\\ -Fo$(OUTD3)\\ src\\init16.asm\r\n\r\n$(OUTD3)\\jemm16.obj: src\\jemm16.asm $(OUTD3)\\jemm32.bin src\\jemm.inc src\\jemm16.inc src\\debug16.inc JemmExL.mak\r\n\t@$(ASM) -D?INTEGRATED=1 -D?XMS35=0 $(AOPT) -Fl$(OUTD3)\\ -Fo$(OUTD3)\\ -I$(OUTD3) src\\jemm16.asm\r\n\r\n$(OUTD3)\\jemm32.bin: $(COFFDEP3)\r\n\t@cd $(OUTD3)\r\n\t@$(LINK32)\r\n\t@cd ..\\..\r\n\r\n$(COFFDEP3): $(32BITDEPS)\r\n\r\nclean:\r\n\t@if exist $(OUTD3)\\*.obj erase $(OUTD3)\\*.obj\r\n\t@if exist $(OUTD3)\\*.lst erase $(OUTD3)\\*.lst\r\n\t@if exist $(OUTD3)\\*.map erase $(OUTD3)\\*.map\r\n\t@if exist $(OUTD3)\\*.exe erase $(OUTD3)\\*.exe\r\n\t@if exist $(OUTD3)\\*.bin erase $(OUTD3)\\*.bin\r\n\t@if exist $(OUTD3)\\_jemm32.inc erase $(OUTD3)\\_jemm32.inc\r\n\r\n"
  },
  {
    "path": "Linux.mak",
    "content": "#\r\n# To build Jemm386 and JemmEx on Linux, you will need:\r\n#\r\n# Tool\r\n#---------------------------\r\n# Assembler:   JWasm\r\n# OMF Linker:  JWlink\r\n# COFF Linker: JWlink\r\n# Make:        GNU make\r\n#\r\n# since v5.87, JWasm must be v2.21+ (with fixed \"negative offset\"-bug)\r\n#\r\n# Important: all include files are referenced in the source with names\r\n# in lowercase, but the corresponding file names are uppercase. To fix\r\n# this either store the project on a FAT/NTFS file system, or create\r\n# according symlinks!\r\n#\r\n# Jemm consists of 2 parts. which are created separately: the 32-bit\r\n# part is the true Jemm application ( the \"v86-monitor\" program ) -\r\n# its sources are assembled and linked to jemm32.bin. The 16-bit part\r\n# is (mostly) used during the initialization phase and - except for a \r\n# small stub - not necessary to be kept in memory. As a result, the\r\n# make process consists of:\r\n#\r\n#  1. assemble the 32-bit assembly sources.\r\n#  2. link 32-bit modules to jemm32.bin (format is \"raw\").\r\n#  3. assemble the 16-bit assembly sources; jemm32.bin will be included.\r\n#  4. link 16-bit modules (JEMM16.obj, INIT16.obj) to Jemm386/JemmEx.\r\n#\r\n# To enable (selective) debug displays, enter:\r\n#   make DEBUG=1 DBGOPT=-D?VCPIDBG=1\r\n# This will enable VCPI related displays. For more switches, see DEBUG32.INC.\r\n\r\nNAME1=JEMM386\r\nNAME2=JEMMEX\r\n\r\nifndef DEBUG\r\nDEBUG=0\r\nendif\r\n\r\n# to create kernel debugger aware versions, run \"make KD=1\"\r\nifndef KD\r\nKD=0\r\nendif\r\n\r\nASM=jwasm\r\n\r\nifeq ($(DEBUG),1)\r\nAOPTD=-D_DEBUG $(DBGOPT) -Sg\r\nelse\r\nAOPTD=\r\nendif\r\n\r\nAOPT=-c -nologo -IInclude $(AOPTD)\r\n\r\n# list of 32bit modules\r\nCOFFMODS=OUTD/JEMM32.obj OUTD/EMS.obj OUTD/VCPI.obj OUTD/DEV.obj \\\r\n OUTD/XMS.obj OUTD/UMB.obj OUTD/VDMA.obj OUTD/I15.obj \\\r\n OUTD/EMU.obj OUTD/VDS.obj OUTD/POOL.obj OUTD/INIT.obj \\\r\n OUTD/DEBUG.obj\r\n\r\nOMFMODS=OUTD/JEMM16.obj OUTD/INIT16.obj\r\n\r\nBUILD=build\r\n\r\nifeq ($(DEBUG),1)\r\noutd_suffix=D\r\nelse\r\noutd_suffix=\r\nendif\r\n\r\nOUTD1=$(BUILD)/$(NAME1)$(outd_suffix)\r\nOUTD2=$(BUILD)/$(NAME2)$(outd_suffix)\r\nCOFFDEP1=$(subst OUTD,$(OUTD1),$(COFFMODS))\r\nCOFFDEP2=$(subst OUTD,$(OUTD2),$(COFFMODS))\r\nOMFDEP1=$(subst OUTD,$(OUTD1),$(OMFMODS))\r\nOMFDEP2=$(subst OUTD,$(OUTD2),$(OMFMODS))\r\nAOPT16=-omf\r\n\r\nLOPT32=format raw bin name $@ disable 1014 op q, offs=0x110000, start=_start\r\n\r\n32BITDEPS=src/JEMM.INC src/JEMM32.INC src/DEBUG32.INC src/EXTERN32.INC Include/JSYSTEM.INC\r\n16BITDEPS=src/JEMM.INC src/JEMM16.INC src/DEBUG16.INC Include/JSYSTEM.INC\r\n\r\n$(OUTD1)/%.obj: src/%.ASM\r\n\t@$(ASM) -coff -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)/ -Fo$@ $<\r\n\r\n$(OUTD2)/%.obj: src/%.ASM\r\n\t@$(ASM) -coff -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)/ -Fo$@ $<\r\n\r\nALL: $(OUTD1) $(OUTD2) $(OUTD1)/$(NAME1).EXE $(OUTD2)/$(NAME2).EXE\r\n\r\n$(OUTD1) $(OUTD2):\r\n\t@mkdir -p $@\r\n\r\n$(OUTD1)/$(NAME1).EXE: $(OMFDEP1)\r\n\t@jwlink format dos file {$(OMFDEP1)} name $@ option q, m=$(OUTD1)/$(NAME1).map\r\n\r\n$(OUTD2)/$(NAME2).EXE: $(OMFDEP2)\r\n\t@jwlink format dos file {$(OMFDEP2)} name $@ option q, m=$(OUTD2)/$(NAME2).map\r\n\r\n$(OUTD1)/INIT16.obj: src/INIT16.ASM $(16BITDEPS) Linux.mak\r\n\t@$(ASM) $(AOPT16) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)/ -Fo$@ src/INIT16.ASM\r\n\r\n$(OUTD2)/INIT16.obj: src/INIT16.ASM $(16BITDEPS) Linux.mak\r\n\t@$(ASM) $(AOPT16) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)/ -Fo$@ src/INIT16.ASM\r\n\r\n$(OUTD1)/JEMM16.obj: src/JEMM16.ASM $(OUTD1)/jemm32.bin $(16BITDEPS) Linux.mak\r\n\t@$(ASM) $(AOPT16) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)/ -Fo$@ -I$(OUTD1) src/JEMM16.ASM\r\n\r\n$(OUTD2)/JEMM16.obj: src/JEMM16.ASM $(OUTD2)/jemm32.bin $(16BITDEPS) Linux.mak\r\n\t@$(ASM) $(AOPT16) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)/ -Fo$@ -I$(OUTD2) src/JEMM16.ASM\r\n\r\n$(OUTD1)/jemm32.bin: $(COFFDEP1)\r\n\t@jwlink $(LOPT32) file {$(COFFDEP1)} op map=$(OUTD1)/jemm32.map\r\n\r\n$(OUTD2)/jemm32.bin: $(COFFDEP2)\r\n\t@jwlink $(LOPT32) file {$(COFFDEP2)} op map=$(OUTD2)/jemm32.map\r\n\r\n$(COFFDEP1): $(32BITDEPS)\r\n\r\n$(COFFDEP2): $(32BITDEPS)\r\n\r\nclean:\r\n\t@rm $(OUTD1)/*.obj\r\n\t@rm $(OUTD1)/*.lst\r\n\t@rm $(OUTD1)/*.map\r\n\t@rm $(OUTD1)/$(NAME1).EXE\r\n\t@rm $(OUTD1)/jemm32.bin\r\n\t@rm $(OUTD2)/*.obj\r\n\t@rm $(OUTD2)/*.lst\r\n\t@rm $(OUTD2)/*.map\r\n\t@rm $(OUTD2)/$(NAME2).EXE\r\n\t@rm $(OUTD2)/jemm32.bin\r\n\r\n"
  },
  {
    "path": "Makefile",
    "content": "#\r\n# To build Jemm386 and JemmEx, you will need:\r\n#\r\n# Tool        Default (recommended) Alternatives\r\n#-----------------------------------------------------------------\r\n# Assembler   JWasm                 Masm v6.1 or better (+Bin2Inc)\r\n# OMF Linker  JWlink                OW Wlink, MS Link (link16.exe)\r\n# COFF Linker JWlink                OW Wlink\r\n# Make        MS Nmake              OW Wmake              \r\n#\r\n# since v5.87, JWasm must be v2.21+ (with fixed \"negative offset\"-bug)\r\n#\r\n# notes:\r\n#  - OW Wmake must be used with the -ms option!\r\n#\r\n#  - WLink < v1.8 shouldn't be used as COFF linker. It contains a bug \r\n#    which might cause unexpected results in Jemm.\r\n#\r\n#  - j/wlink warning 1014 (\"stack segment not found\") is disabled;\r\n#    warning would be emitted when linking the 32-bit part of Jemm.\r\n#\r\n# -  j/wlink warning 1174 (\"target displacement xxxx ignored for segment\r\n#    fixup\") is disabled; it happens if Jemm16.asm is assembled with Masm\r\n#    and should be regarded as a Masm bug!\r\n#\r\n# Jemm consists of 2 parts. which are created separately: the 32-bit\r\n# part is the true Jemm application ( the \"v86-monitor\" program ) -\r\n# its sources are assembled and linked to Jemm32.bin. The 16-bit part\r\n# is (mostly) used during the initialization phase and - except for a \r\n# small stub - not necessary to be kept in memory. As a result, the\r\n# make process consists of:\r\n#\r\n#  1. assemble the 32-bit assembly sources\r\n#  2. link 32-bit modules to Jemm32.bin (format is \"raw\").\r\n#  3. assemble the 16-bit assembly sources; Jemm32.bin will be included,\r\n#     either directly ( JWasm ) or indirectly via tool Bin2Inc ( Masm );\r\n#     Bin2Inc can be found in the JWasm package.\r\n#  4. link 16-bit modules (Jemm16.obj, Init16.obj) to Jemm386/JemmEx\r\n#\r\n# To enable (selective) debug displays, enter:\r\n#   nmake debug=1 dbgopt=-D?VCPIDBG=1\r\n# This will enable VCPI related displays. For more switches, see DEBUG32.INC.\r\n\r\nNAME1=JEMM386\r\nNAME2=JEMMEX\r\nNAME3=JEMMEXL\r\n\r\n!ifndef DEBUG\r\nDEBUG=0\r\n!endif\r\n\r\n# to create kernel debugger aware versions, run \"nmake kd=1\"\r\n!ifndef KD\r\nKD=0\r\n!endif\r\n\r\n# select assembler, JWasm or Masm, default is JWasm\r\n!ifndef MASM\r\nMASM=0\r\n!endif\r\n\r\n!if $(MASM)\r\nASM=ml.exe  \r\n!else\r\nASM=jwasm.exe\r\n!endif\r\n\r\n# select 32-bit COFF linker, default JWLink\r\n\r\n!ifndef JWLINK32\r\nJWLINK32=0\r\n!endif\r\n!ifndef WLINK32\r\nWLINK32=0\r\n!endif\r\n!ifndef MSLINK32\r\nMSLINK32=0\r\n!endif\r\n!if $(JWLINK32)+$(WLINK32)+$(MSLINK32)==0\r\nJWLINK32=1\r\n!endif\r\n\r\n# select 16-bit OMF linker, default JWLink\r\n\r\n!ifndef JWLINK\r\nJWLINK=0\r\n!endif\r\n!ifndef WLINK\r\nWLINK=0\r\n!endif\r\n!ifndef MSLINK\r\nMSLINK=0\r\n!endif\r\n!if $(JWLINK)+$(WLINK)+$(MSLINK)==0\r\nJWLINK=1\r\n!endif\r\n\r\n!if $(DEBUG)\r\nAOPTD=-D_DEBUG $(DBGOPT) -Sg\r\n!else\r\nAOPTD=\r\n!endif\r\n\r\nAOPT=-c -nologo -IInclude $(AOPTD)\r\n\r\n# list of 32bit modules\r\nCOFFMODS=.\\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\r\n\r\nBUILD=build\r\n\r\n!if $(DEBUG)\r\nOUTD1=$(BUILD)\\$(NAME1)D\r\nOUTD2=$(BUILD)\\$(NAME2)D\r\nCOFFDEP1=$(COFFMODS:.\\=build\\JEMM386D\\)\r\nCOFFDEP2=$(COFFMODS:.\\=build\\JEMMEXD\\)\r\n!else\r\nOUTD1=$(BUILD)\\$(NAME1)\r\nOUTD2=$(BUILD)\\$(NAME2)\r\nCOFFDEP1=$(COFFMODS:.\\=build\\JEMM386\\)\r\nCOFFDEP2=$(COFFMODS:.\\=build\\JEMMEX\\)\r\n!endif\r\n\r\n\r\n!if $(JWLINK32)\r\nLINK32=jwlink format raw bin file {$(COFFMODS:.\\=)} name jemm32.bin disable 1014 op offs=0x110000, start=_start, map=jemm32.map, quiet\r\n!elseif $(WLINK32)\r\nLINK32= wlink format raw bin file {$(COFFMODS:.\\=)} name jemm32.bin option offs=0x110000, start=_start, map=jemm32.map, quiet\r\n!else\r\nCOFFOPT=/fixed /driver /subsystem:native /entry:start /base:0x100000 /align:0x10000 /MAP /nologo\r\n# MS link (newer versions won't accept option FILEALIGN anymore)\r\nLINK32=link.exe /FileAlign:0x200 $(COFFOPT) $(COFFMODS:.\\=) /OUT:jemm32.bin \r\n!endif\r\n\r\n!if $(JWLINK)\r\nLINK16=jwlink.exe format dos file jemm16.obj,init16.obj name $(*B).EXE disable 1174 option map=$(*B).MAP, quiet\r\n!elseif $(WLINK)\r\nLINK16=wlink.exe format dos file jemm16.obj,init16.obj name $@.EXE option map=$@.MAP, quiet\r\n!else\r\nLINK16=link16.exe /NOLOGO/MAP:FULL/NOD /NOI jemm16.obj init16.obj,$@.EXE,$@.MAP;\r\n!endif\r\n\r\n32BITDEPS=src\\jemm32.inc src\\jemm.inc src\\extern32.inc src\\debug32.inc Include\\jsystem.inc Makefile\r\n\r\n{src\\}.asm{$(OUTD1)}.obj:\r\n\t@$(ASM) -coff -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\\ -Fo$(OUTD1)\\ $<\r\n\r\n{src\\}.asm{$(OUTD2)}.obj:\r\n\t@$(ASM) -coff -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\\ -Fo$(OUTD2)\\ $<\r\n\r\nALL: $(BUILD) $(OUTD1) $(OUTD2) $(OUTD1)\\$(NAME1).EXE $(OUTD2)\\$(NAME2).EXE\r\n\r\n$(BUILD) $(OUTD1) $(OUTD2):\r\n\t@mkdir $*\r\n\r\n$(OUTD1)\\$(NAME1).EXE: $(OUTD1)\\jemm16.obj $(OUTD1)\\init16.obj\r\n\t@cd $(OUTD1)\r\n\t@$(LINK16)\r\n\t@cd ..\\..\r\n\r\n$(OUTD2)\\$(NAME2).EXE: $(OUTD2)\\jemm16.obj $(OUTD2)\\init16.obj\r\n\t@cd $(OUTD2)\r\n\t@$(LINK16)\r\n\t@cd ..\\..\r\n\r\n$(OUTD1)\\init16.obj: src\\init16.asm src\\jemm16.inc src\\jemm.inc Include\\jsystem.inc Makefile\r\n\t@$(ASM) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\\ -Fo$(OUTD1)\\ src\\init16.asm\r\n\r\n$(OUTD2)\\init16.obj: src\\init16.asm src\\jemm16.inc src\\jemm.inc Include\\jsystem.inc Makefile\r\n\t@$(ASM) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\\ -Fo$(OUTD2)\\ src\\init16.asm\r\n\r\n!if $(MASM)\r\n$(OUTD1)\\jemm16.obj: src\\jemm16.asm $(OUTD1)\\_jemm32.inc src\\jemm.inc src\\jemm16.inc src\\debug16.inc Makefile\r\n\t@$(ASM) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\\ -Fo$(OUTD1)\\ -I$(OUTD1) src\\jemm16.asm\r\n\r\n$(OUTD2)\\jemm16.obj: src\\jemm16.asm $(OUTD2)\\_jemm32.inc src\\jemm.inc src\\jemm16.inc src\\debug16.inc Makefile\r\n\t@$(ASM) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\\ -Fo$(OUTD2)\\ -I$(OUTD2) src\\jemm16.asm\r\n\r\n$(OUTD1)\\_jemm32.inc: $(OUTD1)\\jemm32.bin\r\n\t@bin2inc.exe -q $(OUTD1)\\jemm32.bin $(OUTD1)\\_jemm32.inc\r\n\r\n$(OUTD2)\\_jemm32.inc: $(OUTD2)\\jemm32.bin\r\n\t@bin2inc.exe -q $(OUTD2)\\jemm32.bin $(OUTD2)\\_jemm32.inc\r\n\r\n!else\r\n$(OUTD1)\\jemm16.obj: src\\jemm16.asm $(OUTD1)\\jemm32.bin src\\jemm.inc src\\jemm16.inc src\\debug16.inc Makefile\r\n\t@$(ASM) -D?INTEGRATED=0 -D?KD=$(KD) $(AOPT) -Fl$(OUTD1)\\ -Fo$(OUTD1)\\ -I$(OUTD1) src\\jemm16.asm\r\n\r\n$(OUTD2)\\jemm16.obj: src\\jemm16.asm $(OUTD2)\\jemm32.bin src\\jemm.inc src\\jemm16.inc src\\debug16.inc Makefile\r\n\t@$(ASM) -D?INTEGRATED=1 -D?KD=$(KD) $(AOPT) -Fl$(OUTD2)\\ -Fo$(OUTD2)\\ -I$(OUTD2) src\\jemm16.asm\r\n\r\n!endif\r\n\r\n$(OUTD1)\\jemm32.bin: $(COFFDEP1)\r\n\t@cd $(OUTD1)\r\n\t@$(LINK32)\r\n\t@cd ..\\..\r\n\r\n$(OUTD2)\\jemm32.bin: $(COFFDEP2)\r\n\t@cd $(OUTD2)\r\n\t@$(LINK32)\r\n\t@cd ..\\..\r\n\r\n$(COFFDEP1): $(32BITDEPS)\r\n\r\n$(COFFDEP2): $(32BITDEPS)\r\n\r\nclean:\r\n\t@if exist $(OUTD1)\\*.obj erase $(OUTD1)\\*.obj\r\n\t@if exist $(OUTD1)\\*.lst erase $(OUTD1)\\*.lst\r\n\t@if exist $(OUTD1)\\*.map erase $(OUTD1)\\*.map\r\n\t@if exist $(OUTD1)\\*.exe erase $(OUTD1)\\*.exe\r\n\t@if exist $(OUTD1)\\*.bin erase $(OUTD1)\\*.bin\r\n\t@if exist $(OUTD1)\\_jemm32.inc erase $(OUTD1)\\_jemm32.inc\r\n\t@if exist $(OUTD2)\\*.obj erase $(OUTD2)\\*.obj\r\n\t@if exist $(OUTD2)\\*.lst erase $(OUTD2)\\*.lst\r\n\t@if exist $(OUTD2)\\*.map erase $(OUTD2)\\*.map\r\n\t@if exist $(OUTD2)\\*.exe erase $(OUTD2)\\*.exe\r\n\t@if exist $(OUTD2)\\*.bin erase $(OUTD2)\\*.bin\r\n\t@if exist $(OUTD2)\\_jemm32.inc erase $(OUTD2)\\_jemm32.inc\r\n"
  },
  {
    "path": "Readme.txt",
    "content": "\r\n Contents\r\n\r\n  1.   About Jemm\r\n  2.   Features\r\n  3.   Commandline Options\r\n  4.   Technical Details\r\n  4.1  EMS Implementation Notes\r\n  4.2  Emulation of privileged Opcodes\r\n  4.3  IOPL Sensitive Instructions\r\n  4.4  VMWare Detection\r\n  4.5  Option FASTBOOT\r\n  4.6  Option MAXSEXT\r\n  4.7  Option NOEMS\r\n  4.8  Option SB\r\n  5.   Compatibility\r\n  6.   Errors and Warnings\r\n  7.   Troubleshooting, Hints\r\n  8.   Additional Tools\r\n  8.1  UMBM\r\n  8.2  JEMFBHLP\r\n  8.3  CPUSTAT\r\n  8.4  MEMSTAT\r\n  8.5  XMSSTAT\r\n  8.6  EMSSTAT\r\n  8.7  VCPI\r\n  8.8  MOVEXBDA\r\n  8.9  CPUID\r\n  9.   License\r\n\r\n\r\n 1. About Jemm\r\n\r\n Jemm is an \"Expanded Memory Manager\" (EMM), based on the source of FreeDOS\r\n Emm386. It should work with MS-DOS and compatible DOSes, including FreeDOS.\r\n Like other EMMs it installs the following services:\r\n\r\n - uses extended memory to simulate expanded memory (EMS) according to \r\n   EMS v3.2 and EMS v4.0.\r\n - upper memory blocks (UMB) where drivers and resident programs may\r\n   be loaded, thus increasing available free DOS memory.\r\n - mapping RAM to the video address segments A000-AFFF and B000-B7FF.\r\n - VCPI services to allow DOS applications running in V86-mode to\r\n   switch to protected mode. VCPI also implements a simple memory management.\r\n - VDS API to give drivers/applications some control over DMA and physical\r\n   addresses in V86-mode.\r\n\r\n There exist 2 versions of Jemm:\r\n\r\n - Jemm386: standard version which needs an external eXtended Memory Manager\r\n            (XMM; examples: Himem[S]X, MS Himem, XMGR ) to be loaded.\r\n - JemmEx:  extended version which has an XMM already included.\r\n\r\n JemmEx most likely is the better choice because it will need less DOS\r\n memory than an external XMM + Jemm386.\r\n\r\n\r\n 2. Features\r\n\r\n The main purpose of making Jemm was to make it use less resources than\r\n other EMMs, without making compromises regarding speed or compatibility.\r\n The results currently are:\r\n\r\n - Jemm386 needs just 128 bytes DOS upper memory. JemmEx uses more, mostly\r\n   because it includes the XMS handle array which is located in DOS memory.\r\n   With 32 XMS handles JemmEx needs 496 bytes DOS memory.\r\n - Jemm's extended memory usage is:\r\n   + 44 kB for Jemm itself\r\n   + 64 kB for the DMA buffer (default size)\r\n   + xx kB for UMBs mapped in the first MB\r\n   +  4 kB fixed amount for EMS handle management.\r\n   + xx kB variable amount for EMS/VCPI memory management. For each 1.5 MB\r\n        VCPI memory 64 bytes are needed, for each EMS page 5 bytes are needed.\r\n        For the default values (120 MB VCPI, 2048 EMS pages) this is 16 kB.\r\n - VCPI shared address space in extended memory is just 4 kB.\r\n\r\n Other features are:\r\n\r\n - Jemm can be loaded and unloaded from the command line. Disadvantage:\r\n   DOS won't care about UMBs supplied this way.\r\n - CPUs which provide the Virtual-8086 Mode Extensions (Pentium+) are\r\n   actively supported, which increases the emulation speed.\r\n - the FASTBOOT option shortens reboot time.\r\n - the SPLIT option can gain additional DOS high memory.\r\n - all \"lengthy\" memory copy operations ( EMS, VDS, Int 15h, JemmEx: XMS )\r\n   are done with interrupts enabled.\r\n - Exceptions in protected-mode are detected and displayed.\r\n - a (rudimentary) API is supplied which allows to extend Jemm. [ JLoad\r\n   uses this API to add support for 32bit protected-mode extensions (JLMs). ]\r\n - JemmEx supports XMS v3.5, allowing access to extended memory beyond the\r\n   4 GB barrier. See XMS35.txt for technical details.\r\n\r\n\r\n 3. Commandline Options\r\n\r\n For a list of available options enter:\r\n\r\n JEMM386/JEMMEX -?\r\n\r\n Option     Comment\r\n ----------------------------------------------------------\r\n A20/NOA20  will set the A20 enable/disable emulation accordingly.\r\n ALTBOOT    this option is meant to select an alternate reboot handler,\r\n            if the standard handler doesn't work. The current implementation\r\n            sends a \"system reset\" command to the keyboard controller.\r\n B=xxxx     specify lowest segment address for EMS banking (default=4000,\r\n            min=1000).\r\n D=nnn      this option will set the size of the DMA buffer to <nnn> kB.\r\n            The default size is 64 kB, max size is 128 kB. Will always be\r\n            rounded up to next 4 kB.\r\n EMX        option to prevent EMX DOS extender from quickly terminating with\r\n            message \"out of memory (RM)\" on machines with large amounts\r\n            of RAM (> 256 MB). This is optionally because it makes Jemm\r\n            behave not fully VCPI compliant, but shouldn't hurt usually.\r\n FASTBOOT   option might allow a fast reboot. There is no guarantee that it\r\n            will work, though, see the \"Option FASTBOOT\" chapter below.\r\n FRAME=nnn  instructs Jemm to use a certain page frame. Accepted are frame\r\n            values from 8000 to E000 or NONE. The page frame should start\r\n            at the beginning of a physical EMS page, that is, the frame \r\n            address should be divisible by 0x400 without remainder. FRAME=NONE\r\n            disables the page frame, but there are quite some programs which\r\n            won't run with this setting. A page frame below A000 should \r\n            be set only on computers with 512 kB of conventional memory.\r\n            Usually it's better to let Jemm find a page frame on its own, \r\n            because choosing an address which is not free might cause troubles.\r\n I=mmmm-nnnn force Jemm to use a memory range for UMBs (or page frame).\r\n            <mmmm> must be >= A000. Specifying a range which is not\r\n            really free may give \"unexpected\" results.\r\n I=TEST     scan ROMs for unused address space. This option will regard any\r\n            4 kB page containing identical byte values in read-only memory as\r\n            \"unused\".\r\n LOAD       installs Jemm from the command line. Be aware that UMBs\r\n            cannot be provided this way, since DOS will ignore them.\r\n [MAX=]nnn  limit the maximum amount of memory to be allocated for VCPI (and\r\n            EMS, if <nnn> is below 32 MB). The MAX= prefix is optional (a\r\n            number found as option will be handled as if it has a MAX=\r\n            prefix). Default value is 120 MB.\r\n MIN=nnn    preallocates <nnn> kB of XMS memory thus making sure this\r\n            memory is truly available for EMS/VCPI. If MIN is higher than\r\n            MAX, MAX will be adjusted accordingly. Default for <nnn> is 0.\r\n MOVEXBDA   move XBDA into UMB, thus increasing low DOS memory. This option\r\n            may cause a system \"lock\" because the XBDA might contain buffers\r\n            used for DMA operations. In such cases either use MOVEXBDA.EXE or\r\n            UMBPCI+UMBM instead.\r\n NOCHECK    disallows access via Int 15h, AH=87h to address regions which\r\n            aren't backuped by RAM. As for Jemm386: be aware that some old \r\n            XMMs return wrong values for highest physical RAM - then this \r\n            option may cause strange results. Also, this option may prevent\r\n            real-mode programs from using the VESA LFB.\r\n NODYN      disables XMS dynamic memory allocation. Jemm will allocate\r\n            XMS memory for EMS/VCPI on initialization. Default is size\r\n            of largest XMS block/2, but max. 32MB. With option MIN=xxx one\r\n            may override this.\r\n NOEMS      disables EMS support.\r\n NOHI       this option will prevent Jemm from moving its resident part\r\n            into upper memory. If no UMBs are installed by Jemm, NOHI\r\n            has no effect.\r\n NOINVLPG   disables usage of INVLPG opcode on 80486+ cpus. Might be useful\r\n            if Jemm runs in a virtual environment (see \"Troubleshooting\").\r\n NOVCPI     disables VCPI. Option can be set from the command line.\r\n NOVMW      disables VMWare detection.\r\n PGE/NOPGE  options will enable/disable the Page Global Enable feature\r\n            on Pentium Pro+ cpus. This allows to mark all PTEs for the\r\n            real-mode address space 0-110000h as \"global\", which gives\r\n            a slight speed benefit for VCPI applications. Option is off by\r\n            default because some DOS extenders will not work with PGE\r\n            enabled. Also setting both PGE + NOINVLPG will not work.\r\n RAM/NORAM  will instruct Jemm to supply UMBs or not. RAM is the default.\r\n            NORAM is intended to be used when loading Jemm from the\r\n            cmdline, in which case adding UMBs might be less useful.\r\n S=mmmm-nnnn add a memory region (which must be in range C800-EFFF) as UMB.\r\n            The memory region has to be filled with Shadow-RAM activated by\r\n            UMBPCI, which must have been loaded *before* Himem/JemmEx.\r\n            NOTE: since v5.80, this option is virtually obsolete, because\r\n            either Jemm should find RAM activated by UMBPCI automatically, or,\r\n            if UMBM.EXE has been loaded in CONFIG.SYS, the RAM is already used\r\n            by DOS.\r\n SB         Soundblaster driver compatibility mode on.\r\n SPLIT      if ROMs are found which don't end exactly at a 4 kB boundary\r\n            then setting this option will increase available UMB space. ROM\r\n            sizes are defined in 0.5 kB units, so there might be up to 3.5 kB\r\n            wasted in the ROM's last 4 kB page. There is a small catch: the\r\n            full 4k page will be made writeable, including the ROM part.\r\n UNLOAD     uninstalls Jemm from the command line. Uninstalling the EMM\r\n            might confuse resident programs which rely on EMS or VCPI but\r\n            didn't ensure this configuration to keep unchanged (by allocating\r\n            an EMS page) while they are running.\r\n V86EXC0D   makes Jemm route General Protection Faults (GPF) that occur in\r\n            V86-mode to Int 0Dh. Without this option they are routed to\r\n            Int 06h. This option should only be set if a resident program\r\n            is to be installed that can handle GPFs in V86-mode.\r\n VCPI       (re)enables VCPI. Option can be set from the command line.\r\n VERBOSE    talk a bit more during the load process (abbreviation: /V).\r\n VME/NOVME  options will enable/disable using the V86 Mode Extensions\r\n            on Pentium+ CPUs. These options can be set from the command line.\r\n            NOVME is the default.\r\n X=mmmm-nnnn exclude a memory range to be used as UMBs or page frame.\r\n            <mmmm> must be >= A000.\r\n X=TEST     will exclude all upper memory regions which contain byte values\r\n            other than 00 or FF.\r\n\r\n JemmEx additionally understands:\r\n\r\n A20METHOD:x   select A20 switch method.\r\n               Possible values for <x>:\r\n               ALWAYSON    Assume that A20 line is permanently ON\r\n               BIOS        Use BIOS to toggle the A20 line\r\n               FAST        Use port 92h, bypass INT 15h test\r\n               PS2         Use port 92h, bypass PS/2 test\r\n               KBC         Use the keyboard controller\r\n               PORT92      Use port 92h always\r\n HMAMIN=k      set minimum amount in kB to get the HMA (default=0, max=63).\r\n MAXEXT=l      limit extended memory controlled by XMM to <l> kB.\r\n MAXSEXT=l     limit extended memory beyond 4GB barrier (\"super-extended\") to\r\n               <l> kB; setting MAXSEXT=0 will make JemmEx behave like a\r\n               v3.0 XMM.\r\n NOE801        don't use int 15h, ax=E801h to get amount of extended memory.\r\n NOE820        don't use int 15h, ax=E820h to get amount of extended memory;\r\n               option is ignored unless MAXSEXT=0 is set.\r\n X2MAX=m       limit for free extended memory in kB reported by XMS V2 \r\n               (default 65535). It is reported that some old applications\r\n               need a value of <m>=32767.\r\n XMSHANDLES=n  set number of XMS handles (default=48, min=10, max=128).\r\n\r\n\r\n 4. Technical Details\r\n\r\n 4.1 EMS Implementation Notes\r\n\r\n - The number of EMS pages is limited to 2048 (= 32 MB). It can be increased\r\n   up to 32768 pages (= 512 MB) by setting MIN=<nnn> to a value higher than\r\n   32 MB. However, this memory will then be preallocated, and not be available\r\n   as XMS memory. Some applications will not work if EMS > 32 MB.\r\n\r\n - There is one memory pool, which is shared by EMS and VCPI.\r\n\r\n - The following EMS 4.0 functions aren't implemented. Calling these\r\n   functions will return error code 84h in register AH:\r\n\r\n   + Int 67h, AH=5Ch, prepare expanded memory manager hardware for warm boot\r\n   + Int 67h, AH=5Dh, enable/disable OS/E\r\n\r\n\r\n 4.2 Emulation of privileged Opcodes\r\n\r\n To provide Expanded Memory an EMM Emulator like Jemm runs the cpu in\r\n so-called V86-mode. This mode does not allow to run privileged opcodes.\r\n Some of these opcodes which might be useful for application programs\r\n are emulated by Jemm. These are:\r\n\r\n - mov <special_reg>, <reg>   ;special_reg = CRn, DRn, TRn\r\n - mov <reg>, <special_reg>   ;reg = eax, ebx, ecx, edx, esi, edi, ebp\r\n - WBINVD\r\n - INVD\r\n - WRMSR\r\n - RDMSR\r\n - RDTSC\r\n - HLT\r\n\r\n Those instructions may generate an exception 0x0D if they occur in V86-mode\r\n ( for RDTSC, this depends on a bit in register CR4 ). Jemm's exception handler\r\n examines the opcode causing the exception and - if the instruction is to be\r\n \"emulated\" - runs it in ring 0.\r\n\r\n\r\n 4.3 IOPL Sensitive Instructions\r\n\r\n Jemm runs V86-mode code with IOPL 3. That means, the instructions that are\r\n sensitive to IOPL in V86-mode - CLI, STI, PUSHF, POPF, INT xx, IRET - will\r\n run at full speed, without causing a GPF.\r\n\r\n\r\n 4.4 VMWare Detection\r\n\r\n As default, Jemm tries to detect if it's running under VMWare. This is done\r\n by reading port 0x5658 with value \"VMXh\" in register EAX. If the detection\r\n is successful, Jemm assumes that address range E8000-EFFFF is not to be used.\r\n However, range E8000-EBFFF may still be included with the \"I=\" option.\r\n\r\n The VMWare detection can be disabled with option NOVMW.\r\n\r\n\r\n 4.5 Option FASTBOOT\r\n\r\n The FASTBOOT option may work with many versions of DOS. However, there are\r\n quite a few restrictions. To speed up the boot process, FASTBOOT tries to\r\n avoid resetting the system. This implicates that the interrupt vector table\r\n and at least the disk devices can be reset to the state before DOS has been\r\n loaded. Resetting the interrupt vector table requires some cooperation from\r\n DOS. Other things that may get in the way are:\r\n\r\n - Moving the XBDA into an UMB with option MOVEXBDA is critical, because to\r\n   move it back into conventional memory might not work for various reasons.\r\n\r\n - older FreeDOS versions may need JEMFBHLP.EXE to be installed prior to the\r\n   XMM (Himem). Possibly this might also be needed for MS-DOS versions < 5.\r\n\r\n - UMBPCI allows to activate \"Shadow RAM\", but there's no option to deactivate\r\n   it. This may confuse Jemm, since it can only detect RAM activated by UMBPCI\r\n   if UMBPCI's signature is found.\r\n\r\n - A boot loader (Grub) might have inserted code in conventional memory to\r\n   modify hard disks numbers. There's no guarantee that this code survives the \r\n   fastboot process.\r\n\r\n\r\n 4.6 Option MAXSEXT\r\n\r\n JemmEx only: this option limits amount of \"super-extended\" memory. Setting\r\n MAXSEXT=0 disables this feature. If enabled, JemmEx must exclusively manage\r\n this type of memory; to achieve this, interrupt 15h, AX=0xE820 is hooked and\r\n any memory region beyond 4 GB that's marked as available is changed to\r\n \"reserved\" ( tool MEMSTAT may be used to verify this behavior ).\r\n\r\n\r\n 4.7 Option NOEMS\r\n\r\n Despite its name, this option does not fully disable EMS. What's done is:\r\n\r\n - device name changed from EMMXXXX0 to EMMQXXX0\r\n - no page frame installed\r\n - EMS memory pool is limited to 8 MB\r\n\r\n The change of the device name makes the usual EMM detection routines fail.\r\n OTOH, the interrupt 67h API is fully functional. Most of this is pretty\r\n similar to what \"recent\" MS Emm386 versions do - the only difference is\r\n that they won't limit the memory pool to 8 MB, but install it in its full\r\n size (32 MB).\r\n\r\n\r\n 4.8 Option SB\r\n\r\n Option SB is supposed to help Creative's SoundBlaster emulation drivers for\r\n SB PCI devices. It does 2 things:\r\n\r\n a) It translates an exception 0Dh with error code 0x1A that may have occured\r\n    in V86 mode to an INT 3. It's unclear what this is supposed to fix.\r\n b) Since v5.86, it \"identity maps\" region 0-0x3fffff in the first page table.\r\n    This mapping was done generally until v5.85.\r\n\r\n With b), Creative's SBINIT/SBEINIT tools should again be compatible with Jemm.\r\n However, due to the rather hackish nature of those drivers ( they modify\r\n Jemm's IDT/GDT and page tables ), one may experience negative effects on\r\n stability.\r\n\r\n\r\n 5. Compatibility\r\n\r\n Jemm is NOT an MS Emm386 clone. Differences are:\r\n \r\n - obviously commandline options differ.\r\n - the device driver API, accessed by opening file \"EMMXXXX0\" and\r\n   then issueing IOCTL cmds, differs.\r\n - Jemm doesn't support the so-called GEMMIS API.\r\n - Jemm doesn't support the IO port trapping API of MS Emm386.\r\n - Jemm supports UMBs (Upper Memory Blocks) accessed via the XMS API, but the\r\n   management is different: unlike MS Emm386, there's no chain of UMBs, linked\r\n   with a 16-byte header ( somewhat similar to DOS MCBs ). Instead, the UMB\r\n   addresses and sizes are stored in a table in extended memory, not accessible\r\n   by external programs. The table can hold up to 8 blocks.\r\n - MS Emm386 tries to map all of physical memory in its linear address space,\r\n   in such a way that linear and physical addresses are identical. This isn't\r\n   done by Jemm, because it may consume quite a lot of physical memory for paging\r\n   tables - at least if 4KB pages are used, as it is the case with MS Emm386.\r\n   See option SB, though, which triggers this kind of mapping for the first 4 MB.\r\n - Both Jemm and MS Emm386 supply the NOVCPI commandline option. However, the\r\n   effects differ: with Jemm, VCPI function DE01h will be deactivated by this\r\n   option, thus refusing to start any VCPI client. In contrast, MS Emm386's\r\n   NOVCPI makes the monitor still offer the full API, but no VCPI memory can\r\n   be allocated.\r\n\r\n\r\n 6. Errors and Warnings\r\n\r\n Errors will make Jemm386/JemmEx abort the load process, while warnings\r\n will be displayed and then the load process continues.\r\n\r\n - \"Error: no XMM found, required\"\r\n   Jemm386 only. Jemm386 needs an XMM - try Himem(S)X.\r\n\r\n - \"Error: XMM already installed\"\r\n   JemmEx only. Since JemmEx has its very own XMM, it cannot tolerate\r\n   another one in the system.\r\n\r\n - \"Error: DPMI host detected\"\r\n   Both Jemm386 and JemmEx refuse to load if a DPMI host has been detected.\r\n\r\n - \"Error: No supported A20 method detected\"\r\n   JemmEx only. JemmEx is NOT compatible with this system.\r\n\r\n - \"Error: enable A20 failed\"\r\n   JemmEx only. JemmEx is NOT compatible with this system.\r\n\r\n - \"Error: can't get I15 memory status\"\r\n   JemmEx only. Int 15h, AX=E820h ( and the fallbacks, Int 15h,\r\n   ax=E801h/ah=88h ) failed or returned an amount of 0 kB extended memory.\r\n   Tool memstat may help to find the problem.\r\n\r\n - \"Error: can't get XMS memory status\"\r\n   Jemm386 only. The XMS host reports no extended memory. Tool xmsstat\r\n   may give a hint what's wrong.\r\n\r\n - \"Error: can't allocate enough I15 memory\"\r\n   JemmEx only. Run tool memstat to see how much extended memory the\r\n   system is reporting.\r\n\r\n - \"Error: can't allocate enough XMS memory\"\r\n   Jemm386 only. Run tool xmsstat to see how much extended memory the\r\n   XMM is reporting.\r\n\r\n - \"Error: can't lock XMS memory\"\r\n   Jemm386 only. Well, this shouldn't happen. Jemm386 needs the physical\r\n   address of the memory block where it is to reside. The XMM most likely has\r\n   to be replaced.\r\n\r\n - \"Warning: unknown A20 method\"\r\n   JemmEx only. Option A20METHOD was given with an invalid method.\r\n\r\n - \"Warning: option 'xxx=' rejected, invalid syntax\"\r\n   The option is ignored.\r\n\r\n - \"Warning: E820 - too many ext memory blocks, block xxxxxxxx ignored!\"\r\n   JemmEx only. Occurs if int 15h, ax=e820h returns more than 10 available\r\n   memory blocks. It's very well possible that super-extended memory isn't\r\n   available if this warning has occured. The only cure is to adjust constant\r\n   ?XMS_STATICHDLS in file jemm16.inc and recompile JemmEx.\r\n\r\n - \"Warning: address of allocated EMB (=XXXXXX) is beyond 16MB\" \r\n   Jemm386 only. Means that Jemm386 is forced to reside beyond the physical\r\n   16MB barrier. No problem for Jemm386 itself, but the VDS API requires a\r\n   DMA buffer that is located below that 16MB barrier. So one is better off not\r\n   to ignore this warning. To fix it: a) ensure that Jemm386 is loaded just\r\n   after the XMM; b) replace the XMM, use HimemX2.exe instead of HimemX.exe\r\n   (if an extended memory block is allocated, HimemX2 tries to return the\r\n   block with the lowest address that satisfies the request).\r\n\r\n - \"Warning: no suitable page frame found, EMS functions limited.\"\r\n   Most programs using EMS won't work without a page frame, so this warning\r\n   should not be ignored. To analyze the problem, a first step may be to load\r\n   Jemm from the command line, with the /V option to see why it cannot find a\r\n   64 kB region to be used as page frame. Possible reasons are:\r\n    - UMBPCI is used. Memory regions activated by UMBPCI are detected by Jemm\r\n      and hence won't be used for mapping the Page Frame.\r\n    - BIOS marks 128 kB ( E0000-FFFFF ) as reserved in Int 15h, ax=E820h.\r\n    Using the video segment A000 as page frame is useful only for very specific\r\n   applications and cannot be recommended. Using regions below A000 are even\r\n   more problematic on machines with 640 kB conventional memory, since the\r\n   address range is already \"owned\" by DOS. \r\n    If it's not possible to make Jemm use a valid page frame, it should be setup\r\n   with option NOEMS.\r\n\r\n - \"Warning: EMS banking start too low, set to 0x1000.\"\r\n   This warning only occurs if option B=xxxx has been used inappropriately.\r\n\r\n - \"Warning: MIN has been reduced to n kB\"\r\n   Warning occurs if the amount given for MIN exceeds available free memory. \r\n\r\n - \"Warning: wanted DMA buffer size too large, set to 128 kB\"\r\n   Warning occurs if option D=nnn has been used with a value larger than\r\n   the maximum of 128 (kb).\r\n\r\n - \"Warning: XMS host doesn't provide handle array, dynamic memory allocation off!\"\r\n   Jemm386 only. Jemm wants the XMM to reveal its handle table. If this feature\r\n   isn't available, Jemm has to allocate its memory for EMS and VCPI statically\r\n   on startup.\r\n\r\n\r\n 7. Troubleshooting, Hints\r\n\r\n - If Jemm halts or reboots the machine, the following combinations\r\n   of parameters may help to find the reason. Generally, Jemm386 should be\r\n   loaded immediately after the XMM (HIMEM[S]X.EXE, HIMEM.SYS), and the XMM\r\n   itself should be the first device driver to be loaded. For testing, it\r\n   might also help to prevent DOS from loading in the HMA and/or not to\r\n   use UMBs at all.\r\n\r\n   - X=A000-FFFF NOHI NOINVLPG\r\n\r\n     This is the safest combination. If this doesn't work, Jemm most\r\n     likely isn't compatible with the current DOS/BIOS; for example,\r\n     the BIOS might try to unconditionally use \"unreal\" mode to access\r\n     USB mass storage devices - with Jemm, this will cause an exception.\r\n\r\n   - X=TEST NOHI NOINVLPG\r\n\r\n     This is slightly less safe, since Jemm will scan the upper memory\r\n     region to find \"unused\" address ranges usable for UMBs. If this\r\n     doesn't work, one has to manually set X=xxxx-yyyy to finally\r\n     find the region which causes the troubles. Tool MEMSTAT may be used\r\n     to find the address region which is reserved for the ROM-BIOS.\r\n\r\n - Jemm can be loaded from the command line with option LOAD. This may be\r\n   helpful if there are so many displays during the boot process that one\r\n   cannot read them carefully. Also, option /V should be added to make\r\n   Jemm talkative. To load Jemm386 from the command line, ensure that a\r\n   XMM has been loaded previously. For JemmEx, no XMM must be loaded, since\r\n   it is included.\r\n\r\n - Jemm has been verified to run on the following virtual environments:\r\n\r\n    Qemu, VMware, VirtualPC, Bochs, VirtualBox\r\n\r\n   However, it might be necessary to set option NOINVLPG.\r\n\r\n - Some DOS programs will not work if EMS is enabled without a page frame.\r\n   MS CodeView v4.1, for example, may refuse to start then.\r\n\r\n - Some DOS programs will crash if too much VCPI memory is offered. Jemm's\r\n   default is 120 MB, it can be changed with option MAX=xxx. Popular\r\n   programs that may cause troubles are programs that use Borland's DPMI hosts\r\n   DPMI16BI.OVL/DPMI32VM.OVL (coupled with RTM.EXE/32RTM.EXE). Setting Jemm\r\n   option MAX=32752K ( that's 32MB minus 16kB) should help; alternately, try\r\n   setting environment variable \"DPMIMEM=MAXMEM 16383\".\r\n\r\n - The JEMM ;-) DOS extender (used for \"Strike Commander\" and \"Privateer\")\r\n   isn't compatible with the VME option. This requirement is a strong sign\r\n   that this extender switches to V86 mode on its own, which is a bad idea\r\n   for a VCPI client.\r\n\r\n - Unlike MS Emm386, Jemm does not try to identity-map extended memory in its\r\n   address space. This prevents the PL0 debugger 386SWAT from \"intruding\".\r\n\r\n - If Jemm is installed from the commandline, loading the CTMOUSE driver\r\n   v1.9x and v2.0x might cause an exception. Adding option NOHI or NORAM\r\n   when installing Jemm should avoid that. In CTMOUSE v2.1 the bug has\r\n   been fixed.\r\n\r\n - With some BIOSes disk access speed slows down significantly in V86-mode.\r\n   Most likely this is because the BIOS routines want to avoid using DMA in\r\n   this mode. Loading XDMA32 ( and XCDROM32 ) might fix that.\r\n\r\n - FreeDOS regrettably accepts just one UMB provider. This makes it impossible\r\n   for example to use UMBM.EXE to supply UMB D000-DFFF and then tell Jemm to\r\n   additionally supply B000-B7FF as UMB. MS-DOS has no such problems.\r\n\r\n - Although including the region B000-B7FF might work in most cases, one\r\n   should be aware that this region is not really free, it's used by the\r\n   VGA \"monochrome\" video modes.\r\n\r\n - To make Jemm behave (almost) like MS Emm386 regarding UMBs one should\r\n   set both options X=TEST and I=TEST. For better MS Emm386 compatibility\r\n   one might also consider to restrict VCPI memory to 32 MB by adding\r\n   option MAX=32M. If NODYN is used, one should also set at least MIN=256K.\r\n\r\n - The NOVCPI option may be used to setup an environment similiar to Windows\r\n   DOS boxes:\r\n   - install Jemm with VCPI enabled\r\n   - install a DPMI host residently (HDPMI, DPMIONE, ...)\r\n   - disable VCPI with the NOVCPI option\r\n   This forces any DOS extended application to either use DPMI or abort.\r\n\r\n - If Jemm displays warning\r\n\r\n     System memory found at XXXX-XXXX, region might be in use\r\n\r\n   then that region is not used by Jemm. If you are sure that the region\r\n   is ok to be used, include it with 'I=XXXX-XXXX'.\r\n\r\n - The I=XXXX commandline option may be used to include the VGA \"graphics\"\r\n   segment A000h. It might be possible to increase DOS conventional memory\r\n   up to 736 kB by option I=A000-B7FF. However, there are quite a few\r\n   hurdles that may cause unexpected results:\r\n   - conventional memory is increased only if the region to include is\r\n     adjacent to current memory. On newer machines, there's very often a\r\n     \"hole\", caused by the Extended BIOS Data Area (XBDA or EBDA); thus\r\n     the included region just becomes an UMB and won't increase lower memory.\r\n     Option MOVEXBDA or tool MOVEXBDA.EXE may fix this issue.\r\n   - Once address space A000h is remapped, any attempts to run programs that\r\n     use VGA graphics most likely will cause a crash.\r\n   - Depending on the VGA-BIOS it may happen that some non-graphics functions\r\n     won't work anymore and may also cause a crash. Not unusual is that the \r\n     current text font becomes corrupted ( text font bitmaps must be copied \r\n     from ROM to VGA memory when a video text mode is set ).\r\n   - DOS conventional memory cannot be increased anymore once UMB have been \r\n     added to the DOS memory pool. So you cannot use tool UMBM here!\r\n   - if MOVEXBDA.EXE causes a system lock during boot, add the /A option to \r\n     the line in CONFIG.SYS that loads the driver. This aligns the XBDA to \r\n     a kB boundary, which may cure the lock.\r\n   - the GRUB boot loader may, under certain conditions, allocate a chunk\r\n     of conventional memory BELOW the XBDA. In this case conventional memory\r\n     cannot be increased anymore, even if the XBDA is moved.\r\n\r\n\r\n 8. Additional Tools\r\n\r\n 8.1 UMBM\r\n\r\n UMBM is a small tool only useful in conjunction with Uwe Sieber's UMBPCI.\r\n The main purpose of UMBM is to allow DOS to load the XMM into upper memory.\r\n This driver must be loaded before Jemm386 and therefore DOS can't use UMBs \r\n provided by Jemm386. For JemmEx, UMBM is usually not needed, since the XMM is \r\n included and needs no low memory.\r\n\r\n How does UMBM work? It expects to find a \"shadow\" RAM region activated by\r\n UMBPCI. Then it installs inself as a temporary XMS host which just provides\r\n support for allocating UMBs. This is enough for most DOSes to grab the\r\n memory (note: for this to work line DOS=UMB is required in CONFIG.SYS). After\r\n the UMBs have been allocated, UMBM will be removed from memory automatically.\r\n\r\n Additionally, UMBM understands the /XBDA option. This will cause UMBM\r\n to move the XBDA into its first UMB, thus freeing even more low DOS memory.\r\n \r\n UMBs based on \"shadow\" RAM, as it is supplied by UMBPCI+UMBM, may have\r\n limitations depending on the motherboard's chipset. Sometimes the memory\r\n is inaccessible for DMA (read the documentation coming with UMBPCI for more\r\n details). OTOH, logical and physical addresses for these UMBs are identical,\r\n which may be an advantage, especially for the XBDA. \r\n\r\n Enter \"UMBM\" on the command line and read the example how to add UMBM\r\n to CONFIG.SYS. UMBM has been tested to run with MS-DOS 6/7 and FreeDOS.\r\n\r\n\r\n 8.2 JEMFBHLP\r\n\r\n JEMFBHLP is a tiny device driver only needed if both FreeDOS and Jemm's\r\n FASTBOOT option are used. FreeDOS v1.0 does not provide the information\r\n that Jemm needs for FASTBOOT to work, so this driver tries to cure FreeDOS'\r\n incapability. It saves the values for interrupt vectors 15h and 19h at\r\n 0070h:0100h, which is the MS-DOS compatible way to do it.\r\n\r\n In more recent FreeDOS versions this problem has been fixed.\r\n\r\n\r\n 8.3 CPUSTAT\r\n\r\n CPUSTAT displays some system registers and tables. Most of the registers\r\n aren't accessible in v86-mode, so - for Jemm - this program may be seen as\r\n a test if the emulation of privileged opcodes works as expected.\r\n\r\n Optionally, if cpu runs in V86-mode, CPUSTAT may display GDT, IDT, paging\r\n tables and informations stored in the task state segment of the v86-monitor.\r\n Run \"CPUSTAT -?\" for more details.\r\n\r\n\r\n 8.4 MEMSTAT\r\n\r\n MEMSTAT may be used to display the machine's memory layout, as it is\r\n returned by the BIOS. The most interesting infos are:\r\n\r\n - address region reserved for the ROM-BIOS\r\n - total amount of free memory\r\n\r\n\r\n 8.5 XMSSTAT\r\n\r\n XMSSTAT can be used to display the current status of the installed XMM.\r\n It allows to check current values of JemmEx options X2MAX, MAXEXT and\r\n XMSHANDLES.\r\n\r\n\r\n 8.6 EMSSTAT\r\n\r\n EMSSTAT can be used to display the current status of the installed EMM.\r\n It works with any EMM, not just Jemm.\r\n\r\n\r\n 8.7 VCPI\r\n\r\n VCPI may be used to display the VCPI status of the installed EMM.\r\n With option -p it will display the page table entries for the conventional\r\n memory.\r\n\r\n\r\n 8.8 MOVEXBDA\r\n\r\n MOVEXBDA is a device driver supposed to move the Extended BIOS Data\r\n Area ( XBDA or EBDA ) to low DOS memory. If an XBDA exists, it is usually\r\n located just below the A000 video segment, with a size of 1-12 kB.\r\n\r\n Some DOS versions will move the XBDA on their own - at least if its size\r\n doesn't exceed 1 kB; then MOVEXBDA is not needed and will do nothing if \r\n launched. Also, both JemmEx and UMBM have the ability to move the XBDA as \r\n well; those tools move the XBDA to upper memory, thus increasing low DOS \r\n memory. However, sometimes MOVEXBDA may be the best option available -\r\n because moving the XBDA into an UMB supplied by Jemm (or UMBPCI) may cause\r\n the system to \"lock\".\r\n\r\n MOVEXBDA should work with any EMM.\r\n\r\n\r\n 8.9 CPUID\r\n\r\n CPUID displays cpu features returned by the CPUID instruction.\r\n\r\n\r\n 9. License\r\n\r\n - JEMM386/JEMMEX: partly Artistic License (see ARTISTIC.TXT for details)\r\n - UMBM:     Public Domain\r\n - JEMFBHLP: Public Domain\r\n - CPUSTAT:  Public Domain\r\n - MEMSTAT:  Public Domain\r\n - XMSSTAT:  Public Domain\r\n - EMSSTAT:  Public Domain\r\n - VCPI:     Public Domain\r\n - MOVEXBDA: Public Domain\r\n - CPUID:    Public Domain\r\n\r\n Binaries and source are distributed in separate packages. The binaries'\r\n package has a 'B' suffix in its name, the package containing the source\r\n has a 'S'.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "Test/BMINTRM.ASM",
    "content": "\r\n;--- benchmark\r\n;*** call int 69h a number of times and count timer ticks\r\n;--- jwasm -mz bmintrm.asm\r\n\r\n\t.286\r\n\t.MODEL tiny\r\n\t.stack 2048\r\n\t.dosseg\r\n\r\ncr\tequ 13\r\nlf\tequ 10\r\n\r\n?USERDTSC equ 0\r\n\r\n\tinclude macros.inc\r\n\r\n\t.CODE\r\n\r\n\t.386\r\n\tinclude printf.inc\r\nife ?USERDTSC\r\n\t.586\r\n\tinclude timerms.inc\r\nendif\r\n\r\nmain proc c\r\n\r\nlocal\tdwStart:dword\r\nlocal\trmvec:dword\r\nlocal\tdwStartTSC:qword\r\n\r\n\tpush 0\r\n\tpop es\r\n\tmov ebx,es:[69h*4]\r\n\tmov rmvec,ebx\r\n\r\n\tmov es:[69h*4+0],offset intproc\r\n\tmov es:[69h*4+2],cs\r\n\r\nif ?USERDTSC\r\n\trdtsc\r\n\tmov dword ptr dwStartTSC+0,eax\r\n\tmov dword ptr dwStartTSC+4,edx\r\nelse\r\n\tcall gettimer\r\n\tmov dwStart,eax\r\nendif\r\n\r\n\txor esi,esi\r\n@@:\r\n\tint 69h\r\n\tcmp esi,1000000\r\n\tjnz @B\r\n\r\nif ?USERDTSC\r\n\trdtsc\r\n\tsub eax,dword ptr dwStartTSC+0\r\n\tsbb edx,dword ptr dwStartTSC+4\r\n\tinvoke printf, CStr(\"TSC diff=%lX%08lX\",lf), edx, eax\r\nelse\r\n\tcall gettimer\r\n\tsub eax,dwStart\r\n\tinvoke printf, CStr(\"time for %lu INTs: %lu ms\",lf), esi, eax\r\nendif\r\n\tpush 0\r\n\tpop es\r\n\tmov ebx,rmvec\r\n\tmov es:[69h*4],ebx\r\n\tret\r\n\r\nmain endp\r\n\r\nintproc:\r\n\tinc esi\r\n\tiret\r\n\r\nstart:\r\n\tmov ax, cs\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tend start\r\n\r\n"
  },
  {
    "path": "Test/EMS4E.ASM",
    "content": "\r\n;*** test EMS get/set page map \r\n\r\n\t.286\r\n\t.model small\r\n\toption casemap:none\r\n\t.stack 1024\r\n\t.dosseg\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n\t.data\r\nmapstruct label byte\r\n\tdw 4\r\n\tdw 0A000h\r\n\tdw 0A400h\r\n\tdw 0A800h\r\n\tdw 0AC00h\r\n\r\n\t.data?\r\n\r\nbuffer\tdb 256 dup (?)\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nemsstr1 db 'EMMXXXX0',0\r\n\r\nEMScheck proc uses si di\r\n\r\n\tmov ax, 3567h\r\n\tint 21h\r\n\tmov ax, es\r\n\tor  ax, bx\r\n\tjz  exit\r\n\r\n\tmov dx,1\r\n\tmov si,offset emsstr1\r\n\tmov di,000ah\r\n\tmov cx,8\r\n\tpush ds\r\n\tpush cs\r\n\tpop ds\r\n\tcld\r\n\trepz cmpsb\r\n\tpop ds\r\n\tjz  found\r\n\r\n\tmov dx,0\r\n\tmov ah,46h\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz  found\r\n\r\n\txor ax, ax\r\n\tjmp exit\r\nfound:\r\n\tmov ax,1\r\nexit:\r\n\tret\r\n\r\nEMScheck endp\r\n\r\nmain proc c\r\n\r\nlocal handle:word\r\n\r\n\tcall EMScheck\r\n\tand ax,ax\r\n\tjz sm1\r\n\r\n\tmov ax,4E03h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz sm2\r\n\tinvoke printf, CStr(\"size to save all pages: %u\",lf), ax\r\n\r\n\tmov di,offset buffer\r\n\tpush ds\r\n\tpop es\r\n\tmov ax,4E00h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz sm3\r\n\r\n\tmov si,offset buffer\r\n\tmov ax,4E01h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz sm4\r\n\tinvoke printf, CStr(\"Ok\",lf)\r\n\tjmp exit\r\n\r\nsm1:\r\n\tinvoke printf, CStr(\"EMM not found\",lf)\r\n\tjmp exit\r\nsm2:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr(\"int 67h, ax=4E03h failed, ah=%X\",lf),ax\r\n\tjmp exit\r\nsm3:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr(\"int 67h, ax=4E00h failed, ah=%X\",lf),ax\r\n\tjmp exit\r\nsm4:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr(\"int 67h, ax=4E01h failed, ah=%X\",lf),ax\r\nexit:\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx\r\n\tinvoke main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EMS4F.ASM",
    "content": "\r\n;*** test EMS get/set partial page map \r\n\r\n\t.286\r\n\t.model small\r\n\toption casemap:none\r\n\t.stack 1024\r\n\t.dosseg\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n\t.data\r\nmapstruct label byte\r\n\tdw 4\r\n\tdw 0A000h\r\n\tdw 0A400h\r\n\tdw 0A800h\r\n\tdw 0AC00h\r\n\r\nmapstruct2 label byte\r\n\tdw 4\r\n\tdw 08000h\r\n\tdw 08400h\r\n\tdw 08800h\r\n\tdw 08C00h\r\n\r\n\t.data?\r\n\r\nbuffer\tdb 256 dup (?)\r\n\r\n\t.code\r\n\r\n\t.386\r\n\tinclude printf.inc\r\n\r\nemsstr1 db 'EMMXXXX0',0\r\n\r\nEMScheck proc uses si di\r\n\r\n\tmov ax, 3567h\r\n\tint 21h\r\n\tmov ax, es\r\n\tor  ax, bx\r\n\tjz  exit\r\n\r\n\tmov dx,1\r\n\tmov si,offset emsstr1\r\n\tmov di,000ah\r\n\tmov cx,8\r\n\tpush ds\r\n\tpush cs\r\n\tpop ds\r\n\tcld\r\n\trepz cmpsb\r\n\tpop ds\r\n\tjz  found\r\n\r\n\tmov dx,0\r\n\tmov ah,46h\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz  found\r\n\r\n\txor ax, ax\r\n\tjmp exit\r\nfound:\r\n\tmov ax,1\r\nexit:\r\n\tret\r\n\r\nEMScheck endp\r\n\r\nmain proc c\r\n\r\nlocal handle:word\r\n\r\n\tcall EMScheck\r\n\tand ax,ax\r\n\tjz sm1\r\n\r\n;--- get size to save 4 pages\r\n\r\n\tmov bx,4\r\n\tmov ax,4F02h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz sm2\r\n\tmov ah,0\r\n\tinvoke printf, CStr( \"size to save 4 pages: %u\", lf ), ax\r\n\r\n;--- save 4 pages in mapstruct (A000,A400,A800,AC00) \r\n\r\n\tmov si,offset mapstruct\r\n\tmov di,offset buffer\r\n\tpush ds\r\n\tpop es\r\n\tmov ax,4F00h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjz smx\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ax=4F00h (A000-AFFF) failed, error=%X\", lf ), ax\r\nsmx:\r\n\r\n;--- save 4 pages in mapstruct (8000,8400,8800,8C00) \r\n\r\n\tmov si,offset mapstruct2\r\n\tmov di,offset buffer\r\n\tpush ds\r\n\tpop es\r\n\tmov ax,4F00h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz sm31\r\n\tinvoke printf, CStr( \"int 67h, ax=4F00h (8000-8FFF) ok\", lf )\r\n\r\n;--- restore 4 pages\r\n\r\n\tmov si,offset buffer\r\n\tmov ax,4F01h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz sm4\r\n\tinvoke printf, CStr( \"int 67h, ax=4F01h ok\",lf )\r\n\tjmp exit\r\n\r\nsm1:\r\n\tinvoke printf, CStr( \"EMM not found\", lf )\r\n\tjmp exit\r\nsm2:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ax=4F02h failed, error=%X\", lf ), ax\r\n\tjmp exit\r\nsm31:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ax=4F00h (8000-8FFF) failed, error=%X\", lf ), ax\r\n\tjmp exit\r\nsm4:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ax=4F01h failed, error=%X\",lf ), ax\r\nexit:\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx\r\n\tinvoke main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EMS56.ASM",
    "content": "\r\n;*** test int 67h, ah=56h (alter map and call)\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\toption casemap:none\r\n\t.386\r\n\r\nlf equ 10\r\n;extern\t__acrtused:abs\r\n\r\nCStr macro text:vararg\r\nlocal x\r\n\t.const\r\nx\tdb text,0\r\n\t.code\r\n\texitm <offset x>\r\nendm\r\n\r\nEMS56\tstruct\r\ndwProc\t dd ?       ;far proc to call\r\nbSizeNew db ?\r\npMapNew\t dd ?       ;new mapping\r\nbSizeOld db ?\r\npMapOld\t dd ?       ;old mapping\r\nEMS56\tends\r\n\r\nlog_phys_map struct\r\ndwLogPage\tdw ?\t;logical page\r\ndwPhysPage\tdw ?\t;segment or phys page\r\nlog_phys_map ends\r\n\r\n\t.data\r\n\r\nems56 \tEMS56 <>\r\n\r\nMapNew\tlog_phys_map <0,0>\r\n\t\tlog_phys_map <1,1>\r\n\r\nMapOld\tlog_phys_map <-1,0>\r\n\t\tlog_phys_map <-1,1>\r\n\r\n\t.code\r\n\r\n\tinclude <printf.inc>\r\n\r\nmain proc c argc:word, argv:word\r\n\r\nlocal handle:word\r\nlocal wPages:word\r\nlocal frame:word\r\n\r\n\tmov handle,-1\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,bx\r\n\tmov cx,es\r\n\tor ax,cx\r\n\tjz noemm\r\n\r\n\tmov bx,2\r\n\tmov ah,43h\t   ;alloc 2 pages\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error2\r\n\tmov handle,dx\r\n\tinvoke printf, CStr(\"Int 67h, ah=43h, bx=2: Handle %X allocated\",lf), handle\r\n\r\n\tmov bx,0\r\n\tmov dx,handle\r\n\tmov ax,4400h   ;map to phys page 0\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error4\r\n\tinvoke printf, CStr(\"Int 67h, ax=4400h, bx=0: log page 0 mapped to phys page 0\",lf)\r\n\r\n\tmov bx,1\r\n\tmov dx,handle\r\n\tmov ax,4401h   ;map to phys page 1\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error4\r\n\tinvoke printf, CStr(\"Int 67h, ax=4401h, bx=1: log page 1 mapped to phys page 1\",lf)\r\n\r\n\tmov ah,41h\t   ;get page frame\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error3\r\n\tmov frame,bx\r\n\tinvoke printf, CStr(\"Int 67h, ah=41h: Page Frame is %X\",lf), frame\r\n\r\n;--- now copy a small test routine into page 0 of page frame\r\n\r\n\tmov es,frame\r\n\tmov di,0\r\n\tmov si,offset testpr\r\n\tpush ds\r\n\tpush cs\r\n\tpop ds\r\n\tmov cx,sizeproc\r\n\trep movsb\r\n\tpop ds\r\n\r\n;--- unmap page 0\r\n\r\n\tmov bx,-1\r\n\tmov dx,handle\r\n\tmov ax,4400h   ;unmap page 0\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error4\r\n\tinvoke printf, CStr(\"Int 67h, ax=4400, bx=-1: phys page 0 unmapped\",lf)\r\n\r\n\tmov dx,handle\r\n\tmov ax,5602h\r\n\tint 67h\r\n\tmov ax, sp\r\n\tinvoke printf, CStr(\"Int 67h, ax=5602h: additional stack space for call: %X, SP=%X\",lf), bx, ax\r\n\r\n;--- prepare ax=5600h, DS:SI->EMS56\r\n\r\n\tmov si,offset ems56\r\n\tmov ax, frame\r\n\tmov word ptr [si].EMS56.dwProc+0, 0\r\n\tmov word ptr [si].EMS56.dwProc+2, ax\r\n\tmov [si].EMS56.bSizeNew,2\r\n\tmov word ptr [si].EMS56.pMapNew+0, offset MapNew\r\n\tmov word ptr [si].EMS56.pMapNew+2, ds\r\n\tmov [si].EMS56.bSizeOld,2\r\n\tmov word ptr [si].EMS56.pMapOld+0, offset MapOld\r\n\tmov word ptr [si].EMS56.pMapOld+2, ds\r\n\tmov dx,handle\r\n\tmov ax,5600h\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error5\r\n\r\n\tmov ax,sp\r\n\tinvoke printf, CStr(\"Int 67h, ax=5600h ok, SP after call=%X\",lf), ax\r\n\r\nexit2:\r\n\tinvoke printf, CStr(lf)\r\nexit:\r\n\tmov dx,handle\r\n\tcmp dx,-1\r\n\tjz @F\r\n\tmov ah,45h\r\n\tint 67h\r\n@@:\r\n\tret\r\nnoemm:\r\n\tinvoke printf, CStr(\"EMM not found\",lf)\r\n\tjmp exit\r\nerror2:\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ah=43h, bx=%u failed, status=%02X\",lf), bx, ax\r\n\tjmp exit\r\nerror3:\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ah=41h failed, status=%02X\",lf), ax\r\n\tjmp exit\r\nerror4:\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ah=44h failed, status=%02X\",lf), ax\r\n\tjmp exit\r\nerror5:\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=5600h failed, status=%02X\",lf), ax\r\n\tjmp exit\r\nmain endp\r\n\r\n;--- this small routine is copied into page 0 of page frame\r\n\r\ntestpr proc far\r\n\tmov ah,02\r\n\tmov dl,'*'\r\n\tint 21h\r\n\tret\r\ntestpr endp\r\n\r\nsizeproc equ $ - testpr\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx\r\n\tcall main\r\n\tmov ah,4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EMS57.ASM",
    "content": "\r\n;*** EMS move\r\n\r\n\t.286\r\n\t.model tiny\r\n\toption casemap:none\r\n\toption casemap:none\r\n\t.stack 1024\r\n\t.dosseg\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n?BUFSIZE equ 10000h\r\n?SIZE    equ ?BUFSIZE/4000h\t;size in EMS pages\r\n\r\n\t.386\r\n\r\nEMM57 struct\r\ndwSize  DD ?\t; +0  size of region\r\nbSrcTyp DB ?\t; +4  src memory type\r\nwSrcHdl DW ?\t; +5  src handle\r\nwSrcOfs DW ?\t; +7  src ofs\r\nwSrcSeg DW ?\t; +9  src segm./log. page\r\nbDstTyp DB ?\t; +11 dst memory type\r\nwDstHdl DW ?\t; +12 dst handle\r\nwDstOfs DW ?\t; +14 dst ofs\r\nwDstSeg DW ?\t; +16 dst segm./log. page\r\nEMM57 ends\r\n\r\n\t.data\r\n\r\nemm57   EMM57 <>\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal handle:word\r\nlocal wPages:word\r\nlocal wMem:word\r\nlocal frame:word\r\n\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,bx\r\n\tmov cx,es\r\n\tor ax,cx\r\n\tjnz @F\r\n\tinvoke printf, CStr( \"EMM not found\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmov ah,48h\r\n\tmov bx,?BUFSIZE/10h\r\n\tint 21h\r\n\tjnc @F\r\n\tinvoke printf, CStr( \"DOS memory allocation failed\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmov wMem, ax\r\n\tmov bx,?SIZE\r\n\tmov ah,43h\t   ;alloc pages\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error2\r\n\tmov handle,dx\r\n\r\n\tinvoke printf, CStr(\"Handle %u allocated, clearing content ... \", lf), handle\r\n\r\n\txor di, di\r\n\tmov es, wMem\r\n\tmov eax,12345678h\r\n\tmov cx,?BUFSIZE/4\r\n\trep stosd\r\n\r\n\tmov si, offset emm57\r\n\tmov emm57.dwSize, ?BUFSIZE\r\n\tmov emm57.bSrcTyp, 0\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, es\r\n\tmov emm57.bDstTyp, 1\r\n\tmov ax,handle\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, 0\r\n\tmov ax,5700h\t;move memory region\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error3\r\n\tinvoke printf, CStr( \"move to expanded memory ok\",lf)\r\n\r\n\txor di, di\r\n\tmov es, wMem\r\n\txor eax, eax\r\n\tmov cx,?BUFSIZE/4\r\n\trep stosd\r\n\r\n\tmov si, offset emm57\r\n\tmov emm57.dwSize, ?BUFSIZE\r\n\tmov ax,handle\r\n\tmov es,wMem\r\n\tmov emm57.wSrcHdl, ax\r\n\tmov emm57.bSrcTyp, 1\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\tmov emm57.bDstTyp, 0\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, es\r\n\tmov ax,5700h\t;move memory region\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz error3\r\n\tinvoke printf, CStr( \"move from expanded memory ok\",lf)\r\n\r\n\txor di, di\r\n\tmov es, wMem\r\n\tmov eax, 12345678h\r\n\tmov cx,?BUFSIZE/4\r\n\trepz scasd\r\n\tjz exit2\r\n\tinvoke printf, CStr( \"memory content has changed!\",lf)\r\n\r\nexit2:\r\n\tmov dx,handle\r\n\tmov ah,45h\t   ;free pages\r\n\tint 67h\r\nexit:\r\n\tret\r\nerror2:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ah=43h, bx=%u failed, status=%X\",lf), bx, ax\r\n\tjmp exit\r\nerror3:\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ah=57h failed, status=%X\",lf), ax\r\n\tjmp exit\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx\r\n\tmov dx,es\r\n\tmov ax,ss\r\n\tsub ax,dx\r\n\tmov dx,sp\r\n\tshr dx,4\r\n\tadd ax,dx\r\n\tmov bx,ax\r\n\tmov ah,4ah\r\n\tint 21h\r\n\tinvoke main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EMS57A.ASM",
    "content": "\r\n;*** test EMS move (int 67h, ah=57h)\r\n;*** move 1024 kB conv to EMS memory\r\n;*** move 1024 kB EMS to EMS memory non-overlapping\r\n;*** move 1024 kB EMS to EMS memory overlapping\r\n\r\n\t.286\r\n\t.model tiny\r\n\toption casemap:none\r\n\t.stack 1024\r\n\t.dosseg\r\n        \r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n?SIZE\tequ 64\t\t;size in EMS pages (64*16=1024 kB)\r\n        \r\n\r\n\t.386\r\n\r\nEMM57 struct\r\ndwSize\tDD ?\t; +0  size of region\r\nbSrcTyp DB ?\t; +4  src memory type\r\nwSrcHdl DW ?\t; +5  src handle\r\nwSrcOfs DW ?\t; +7  src ofs\r\nwSrcSeg DW ?\t; +9  src segm./log. page\r\nbDstTyp DB ?\t; +11 dst memory type\r\nwDstHdl\tDW ?\t; +12 dst handle\r\nwDstOfs\tDW ?\t; +14 dst ofs\r\nwDstSeg\tDW ?\t; +16 dst segm./log. page\r\nEMM57 ends\r\n\r\n\t.data\r\n\r\nemm57   EMM57 <>\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal handle:word\r\nlocal wPages:word\r\nlocal wMem:word\r\nlocal frame:word\r\n\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,bx\r\n\tmov cx,es\r\n\tor ax,cx\r\n\tjnz @F\r\n\tinvoke printf, CStr(\"EMM not found\",lf)\r\n\tjmp exit\r\n@@:\r\n\txor ax,0\r\n\tmov wMem, ax\r\n\tmov bx,?SIZE*2\r\n\tmov ah,43h\t   ;alloc pages\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"int 67h, ah=43h, bx=%X failed, status=%X\",lf),bx,ax\r\n\tjmp exit\r\n@@:\r\n\tmov handle,dx\r\n\tinvoke printf, CStr(\"Handle %u allocated, size=%u * 16 kB pages\",lf), handle, ?SIZE*2\r\n\tinvoke printf, CStr(\"int 67h, AX=5700h (move), AX=5701h (xchg)\",lf)\r\n\r\n\tmov si, offset emm57\r\n\tmov emm57.dwSize, 100000h\r\n\tmov emm57.bSrcTyp, 0\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\r\n\tmov ax,handle\r\n\tmov emm57.bDstTyp, 1\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, 0\r\n\tmov ax,5700h\t;move memory region\r\n\tint 67h\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"5700,0->1: move conv to expanded memory, 1MB, status=%X (0 exp)\",lf),ax\r\n\r\n;--- move from EMS to EMS non-overlapp\r\n\r\n\tmov si, offset emm57\r\n\tmov ax,handle\r\n\tmov emm57.dwSize, ?SIZE*4000h\r\n\tmov emm57.bSrcTyp, 1\r\n\tmov emm57.wSrcHdl, ax\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\r\n\tmov emm57.bDstTyp, 1\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, ?SIZE\r\n\tmov ax,5700h\t;move memory region\r\n\tint 67h\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"5700,1->1: move EMS to EMS memory (non-overlapp), status=%X (0 exp)\",lf),ax\r\n\r\n;--- move from EMS to EMS overlapp\r\n\r\n\tmov si, offset emm57\r\n\tmov ax,handle\r\n\tmov emm57.dwSize, ?SIZE*4000h\r\n\tmov emm57.bSrcTyp, 1\r\n\tmov emm57.wSrcHdl, ax\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\r\n\tmov emm57.bDstTyp, 1\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, ?SIZE/2\r\n\tmov ax,5700h\t;move memory region\r\n\tint 67h\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"5700,1->1: move EMS to EMS memory (overlapp), status=%X (92 exp)\",lf),ax\r\n\r\n;--- xchg from EMS to EMS non-overlapp\r\n\r\n\tmov si, offset emm57\r\n\tmov ax,handle\r\n\tmov emm57.dwSize, ?SIZE*4000h\r\n\tmov emm57.bSrcTyp, 1\r\n\tmov emm57.wSrcHdl, ax\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\r\n\tmov emm57.bDstTyp, 1\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, ?SIZE\r\n\tmov ax,5701h\t;xchg memory region\r\n\tint 67h\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"5701,1->1: xchg EMS to EMS memory (non-overlapp), status=%X (0 exp)\",lf),ax\r\n\r\n;--- xchg from EMS to EMS overlapp\r\n\r\n\tmov si, offset emm57\r\n\tmov ax,handle\r\n\tmov emm57.dwSize, ?SIZE*4000h\r\n\tmov emm57.bSrcTyp, 1\r\n\tmov emm57.wSrcHdl, ax\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\r\n\tmov emm57.bDstTyp, 1\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, ?SIZE/2\r\n\tmov ax,5701h\t;xchg memory region\r\n\tint 67h\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"5701,1->1: xchg EMS to EMS memory (overlapp), status=%X (97 exp)\",lf),ax\r\n\r\n;--- set invalid region type\r\n\r\n\tmov si, offset emm57\r\n\tmov ax,handle\r\n\tmov emm57.dwSize, ?SIZE*4000h\r\n\tmov emm57.bSrcTyp, 2\r\n\tmov emm57.wSrcHdl, ax\r\n\tmov emm57.wSrcOfs, 0\r\n\tmov emm57.wSrcSeg, 0\r\n\r\n\tmov emm57.bDstTyp, 1\r\n\tmov emm57.wDstHdl, ax\r\n\tmov emm57.wDstOfs, 0\r\n\tmov emm57.wDstSeg, ?SIZE\r\n\tmov ax,5700h\t;move memory region\r\n\tint 67h\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr( \"5700,2->1: src has invalid type (2), status=%X (98 exp)\",lf),ax\r\n\r\nexit2:\r\n\tmov dx,handle\r\n\tmov ah,45h\t   ;free pages\r\n\tint 67h\r\nexit:\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx\r\n\tinvoke main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EMS5B.ASM",
    "content": "\r\n;*** test EMS 5B\r\n;--- 5b00: get alternate map register set ( if BL=0 on ret, es:di will be current pointer to context save area )\r\n;--- 5b01: set alternate map register set in BL\r\n;---       if BL=0, es:di will be current context save area\r\n;--- 5b02: get size of alternate map register set in dx\r\n\r\n\t.286\r\n\t.model small\r\n\toption casemap:none\r\n\toption casemap:none\r\n\t.stack 1024\r\n\t.dosseg\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n        \r\n\t.data\r\n\r\nwCSize dw 0\r\n\r\n\t.data?\r\n\r\nbuffer db 1024 dup (?)\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmemset proc c uses di pMem:ptr, wValue:word, wSize:word\r\n\tmov di, pMem\r\n\tmov ax, wValue\r\n\tmov cx, wSize\r\n\tcld\r\n\trep stosb\r\n\tret\r\nmemset endp\r\n\r\nmain proc c\r\n\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,bx\r\n\tmov cx,es\r\n\tor ax,cx\r\n\tjnz @F\r\n\tinvoke printf, CStr( \"EMM not found\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmov dx, -1\r\n\tmov ax, 5B02h\r\n\tint 67h\r\n\tmov [wCSize], dx\r\n\tmovzx ax, ah\r\n\tinvoke printf, CStr(\"int 67h (ax=5B02h): ah=%X, dx=%X\", lf), ax, dx\r\n\tcmp [wCSize], sizeof buffer\r\n\tja error1\r\n\r\n\tpush ds\r\n\tpop es\r\n\tinvoke memset, addr buffer, 0, sizeof buffer\r\n\r\n\tmov ax, 5B00h\r\n\tint 67h\r\n\tmovzx ax, ah\r\n\tmovzx bx, bl\r\n\tinvoke printf, CStr(\"int 67h (ax=5B00h): ah=%X, bl=%X, es:di=%X:%X\", lf), ax, bx, es, di\r\n\r\n\tmov di, offset buffer\r\n\tpush ds\r\n\tpop es\r\n\tpush es\r\n\tpush di\r\n\tmov ax, 5B01h\r\n\tmov bl, 0\r\n\tint 67h\r\n\tpop cx\r\n\tpop dx\r\n\tmovzx ax, ah\r\n\tmovzx bx, bl\r\n\tinvoke 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\r\nexit:\r\n\tret\r\nerror1:\r\n\tinvoke printf, CStr(\"size of context save area too small\", lf)\r\n\tjmp exit\r\n\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx\r\n\tmov dx,es\r\n\tmov ax,ss\r\n\tsub ax,dx\r\n\tmov dx,sp\r\n\tshr dx,4\r\n\tadd ax,dx\r\n\tmov bx,ax\r\n\tmov ah,4ah\r\n\tint 21h\r\n\tinvoke main\r\n\tmov ah,4ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EXC00.ASM",
    "content": "\r\n;*** EXC 00\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\n\t.code\r\n\r\nstart:\r\n\tmov dx, -1\r\n\tmov cx, 8000h\r\n\tdiv cx\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/EXC06.ASM",
    "content": "\r\n;*** EXC 06 - requires a P6+ cpu\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.686\r\n\r\n\t.code\r\n\r\nstart:\r\n\tud2\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/FRAMERES.ASM",
    "content": "\r\n;--- create binary:\r\n;--- jwasm -bin -Fo frameres.com frameres.asm\r\n\r\n\t.286\r\n\t.MODEL tiny\r\n\r\n\t.CODE\r\n\r\n\torg 100h\r\n\r\nstart:\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,es      ;IV 67h set?\r\n\tor ax,bx\r\n\tjz exit\r\n\tmov ah,41h     ;get page frame\r\n\tint 67h\r\n\tand ah,ah      ;ok?\r\n\tjz exit\r\n\tmov cx,4           ;cx=size mapping array\r\n\tmov dx,0           ;dx=handle (0=system)\r\n\tmov si,offset map  ;ds:si -> mapping array\r\n\tmov ax,5000h\r\n\tint 67h\r\nexit:\r\n\tmov ax,4c00h\r\n\tint 21h\r\n\talign 2\r\nmap dw -1,0,-1,1,-1,2,-1,3\r\n\r\n\tend start\r\n\r\n"
  },
  {
    "path": "Test/HLTTEST.ASM",
    "content": "\r\n;*** HLT test (v86)\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\n\tinclude macros.inc\r\n\r\n\t.data\r\n\r\ndwOldInt9 dd 0\r\nbM21 db 0\r\nbIrq db 0\r\ncIrq db 0\r\n\r\n\t.code\r\n\r\nmyint9 proc\r\n\tinc cs:[cIrq]\r\n\tjmp cs:[dwOldInt9]\r\nmyint9 endp\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal\tmblock:word\r\n\r\n\tmov ecx,200000000\r\n@@:\r\n\tdec ecx\r\n\tjnz @B\r\n@@:\r\n\tmov ah,1\r\n\tint 16h\r\n\tjz @F\r\n\tmov ah,0\r\n\tint 16h\r\n\tjmp @B\r\n@@:\r\n\tmov ax, 3509h\r\n\tint 21h\r\n\tmov word ptr dwOldInt9+0, bx\r\n\tmov word ptr dwOldInt9+2, es\r\n\tmov dx, offset myint9\r\n\tmov ax, 2509h\r\n\tint 21h\r\n\r\n\tin al, 21h\r\n\tmov bM21, al\r\n\tmov al, 0FDh\r\n\tout 21h, al\r\n\r\n\tmov [cIrq],0\r\n\r\n\thlt\r\n\tcli\r\n\tnop\r\n\r\n\tmov al, [cIrq]\r\n\tmov bIrq, al\r\n\r\n\tsti\r\n\r\n\tmov al, bM21\r\n\tout 21h, al\r\n\r\n\tpush ds\r\n\tlds dx, dwOldInt9\r\n\tmov ax,2509h\r\n\tint 21h\r\n\tpop ds\r\n\r\n\tmov al,bIrq\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"%u interrupts counted\",10),ax\r\n\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, cs\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/HLTTEST2.ASM",
    "content": "\r\n;*** HLT test (v86) with IF=0\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\n\tinclude macros.inc\r\n\r\n\t.data\r\n\r\ndwOldInt9 dd 0\r\ncIrq\tdb 0\r\nbM21\tdb 0\r\nbIrq\tdb 0\r\n\r\n\t.code\r\n\r\nmyint9 proc\r\n\tinc cs:[cIrq]\r\n\tjmp cs:[dwOldInt9]\r\nmyint9 endp\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\n\tmov ecx,200000000\r\n@@:\r\n\tdec ecx\r\n\tjnz @B\r\n\r\n@@:\r\n\tmov ah,1\r\n\tint 16h\r\n\tjz @F\r\n\tmov ah,0\r\n\tint 16h\r\n\tjmp @B\r\n@@:\r\n\r\n\tmov ax, 3509h\r\n\tint 21h\r\n\tmov word ptr dwOldInt9+0, bx\r\n\tmov word ptr dwOldInt9+2, es\r\n\tmov dx, offset myint9\r\n\tmov ax, 2509h\r\n\tint 21h\r\n\r\n\tin al, 21h\r\n\tmov bM21, al\r\n\tmov al, 0FDh\r\n\tout 21h, al\r\n\r\n\tmov [cIrq],0 \t   \r\n\r\n\tcli\r\n\tnop\r\n\thlt\r\n\tnop\r\n\r\n\tmov al, [cIrq]\r\n\tmov bIrq,al\r\n\r\n\tsti\r\n\r\n\tmov al,bM21\r\n\tout 21h,al\r\n\r\n\tpush ds\r\n\tlds dx, dwOldInt9\r\n\tmov ax,2509h\r\n\tint 21h\r\n\tpop ds\r\n\r\n\tmov al,bIrq\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"%u interrupts counted\",10),ax\r\n\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, cs\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/I15MOVE.ASM",
    "content": "\r\n;*** move block by int 15h, ah=87h\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\nlf equ 10\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n        \r\ngdt label byte\r\n\tdq 0\r\n\tdq 0\r\n\r\nwSrcLim dw -1\r\nwSrcA00 dw 0\r\nbSrcA16 db 0\r\n\tdb 093h\r\n\tdb 0\r\n\tdb 0\r\n\r\nwDstLim dw -1\r\nwDstA00 dw 0\r\nbDstA16 db 0\r\n\tdb 093h\r\n\tdb 0\r\n\tdb 0\r\n\r\n\tdq 0\r\n\tdq 0\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal\tmblock:word\r\n\r\n\tmov ah,48h\r\n\tmov bx,4000h\t;alloc 256 kB\r\n\tint 21h\r\n\tjc nomem\r\n\tmov mblock,ax\r\n\r\n;--- setup descriptors\r\n\r\n\tmovzx eax,ax\r\n\tshl eax, 4\t\t;get block's linear address\r\n;\tadd eax, 0FFCh  ;???\r\n\tlea edx, [eax+20000h]\r\n\tpush eax\r\n\tpush edx\r\n\tmov wSrcA00, ax\r\n\tmov wDstA00, dx\r\n\tshr eax, 16\r\n\tshr edx, 16\r\n\tmov bSrcA16, al\r\n\tmov bDstA16, dl\r\n\tpop edx\r\n\tpop eax\r\n\r\n\tinvoke printf, CStr(\"src/dst descriptor base = %lXh/%lXh\",lf), eax, edx\r\n\tinvoke printf, CStr(\"src/dst descriptor limit = ffffh, ofs GDT=%Xh\",lf), offset gdt\r\n\r\n\tcall clearblock\r\n\r\n;--- copy 32kB - should be ok\r\n\r\n\tmov cx, 4000h\r\n\tcall i15\r\n\tinvoke printf, CStr(\"int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X\",lf), cx, dx, ax\r\n\tcall testblock\r\n\tcall clearblock\r\n\r\n;--- copy 64kB - should be ok\r\n\r\n\tmov cx, 8000h\r\n\tcall i15\r\n\tinvoke printf, CStr(\"int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X\",lf), cx, dx, ax\r\n\tcall testblock\r\n\tcall clearblock\r\n\r\n;--- this move should fail - max words to copy is 8000h.\r\n\r\n\tmov cx, 8001h\r\n\tcall i15\r\n\tinvoke printf, CStr(\"int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X\",lf), cx, dx, ax\r\n\r\n\tinvoke printf, CStr(\"src/dst descriptor limit = 3fffh\",lf)\r\n\tmov wSrcLim, 3fffh\r\n\tmov wDstLim, 3fffh\r\n\r\n;--- this move should be ok\r\n\r\n\tmov cx, 2000h\r\n\tcall i15\r\n\tinvoke printf, CStr(\"int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X\",lf), cx, dx, ax\r\n\r\n;--- this move should fail - beyond descriptor limit\r\n\r\n\tmov cx, 2001h\r\n\tcall i15\r\n\tinvoke printf, CStr(\"int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X\",lf), cx, dx, ax\r\n\r\n;--- move with cx=0; MS Emm386 returns with C, AH=2; Jemm accepts a size of 0!\r\n\r\n\tmov cx, 0\r\n\tcall i15\r\n\tinvoke printf, CStr(\"int 15h, ah=87h, cx=%Xh returned with C=%u, ah=%X\",lf), cx, dx, ax\r\n\r\n\tret\r\nnomem:\r\n\tinvoke printf, CStr(\"out of DOS memory\",lf)\r\n\tret\r\ni15:\r\n\tpush ds\r\n\tpop es\r\n\tmov si, offset gdt\r\n\tmov ah,87h\r\n\tstc\r\n\tint 15h\r\n\tsbb dx,dx\r\n\tand dx,1\r\n\tmovzx ax,ah\r\n\tretn\r\n\r\nclearblock:\r\n\tcld\r\n\tmov cl,4\r\n\tmov eax,11111111h\r\n\tmov es, mblock\r\nnext64kb:\r\n\tpush cx\r\n\txor di, di\r\n\tmov cx, 4000h\r\n\trep stosd\r\n\tmov dx, es\r\n\tadd dx, 1000h\r\n\tmov es, dx\r\n\tadd eax,11111111h\r\n\tpop cx\r\n\tdec cl\r\n\tjnz next64kb\r\n\tretn\r\ntestblock:\r\n\tretn\r\n\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx   ; tiny model (CS=SS=DS) setup\r\n\r\n\tmov bx, sp\r\n\tadd bx, 100h\r\n\tshr bx, 4\r\n\tmov ah, 4Ah\r\n\tint 21h\r\n\tcall main\r\n\tmov ax,4c00h\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/INT67.ASM",
    "content": "\r\n;--- the EMS handler runs slightly differently if IVT vector 67h is hooked!\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 256\r\n\r\n\t.code\r\n\r\n\tdb 10 dup (0)\r\n\tdb \"EMMXXXX0\"\r\n\r\nmyint67:\r\n\tdb 0EAh\r\noldint dd 0\r\n\r\nendres equ $\r\n\r\nstart:\r\n\tmov ax, cs\r\n\tmov ds, ax\r\n\tmov ax, 3567h\r\n\tint 21h\r\n\tmov word ptr oldint+0, bx\r\n\tmov word ptr oldint+2, es\r\n\tmov dx, offset myint67\r\n\tmov ax, 2567h\r\n\tint 21h\r\n\tmov dx, offset endres\r\n\tadd dx, 16-1\r\n\tshr dx, 4\r\n\tadd dx, 10h\r\n\tmov ax, 3100h\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/INT88.ASM",
    "content": "\r\n;--- test int# >= 0x80;\r\n;--- the monitor program uses the \"PUSH byte const\" instruction\r\n;--- to push a sign-extended value as int#; so it has to carefully\r\n;--- use the lowest byte only.\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 256\r\n\t.dosseg\r\n\toption casemap:none\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n\r\noldint88 dd 0\r\n\r\n\t.code\r\n\r\n\t.386\r\n\tinclude printf.inc\r\n\t.286\r\n\r\nmyint88 proc\r\n\tinvoke 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\r\n\tiret\r\nmyint88 endp\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov ax,3588h\r\n\tint 21h\r\n\tmov word ptr [oldint88+0],bx\r\n\tmov word ptr [oldint88+2],es\r\n\tmov dx, offset myint88\r\n\tmov ax,2588h\r\n\tint 21h\r\n\r\n\tmov ax,0\r\n\tmov bx,1\r\n\tmov cx,2\r\n\tmov dx,3\r\n\tmov si,4\r\n\tmov di,5\r\n\tmov bp,6\r\n\tint 88h\r\n\r\n\tlds dx, [oldint88]\r\n\tmov ax, 2588h\r\n\tint 21h\r\n\r\n\tmov ax, 4c00h\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/MACROS.INC",
    "content": "\r\nCStr macro text:VARARG\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n"
  },
  {
    "path": "Test/MAKE.BAT",
    "content": "@echo off\r\nrem the programs may all be created without link step,\r\nrem using jwasm's -mz option\r\njwasm -mz %1.asm\r\n"
  },
  {
    "path": "Test/PRINTF.INC",
    "content": "\r\n;--- simple printf() implementation\r\n;--- assume ds=dgroup\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n\r\nltob PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\nprintf PROC c uses si di fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush offset @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov [size_],dx\r\n\tmov bx,dx\r\n\tjmp @@L358\r\n@@FC250:\r\n\tcmp BYTE PTR [si],'9'\r\n\tjg\t@@L362\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul cx,bx,10\t\t;cx = bx * 10\r\n\tadd ax,cx\r\n\tmov bx,ax\r\n@@L358:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjge @@FC250\r\n@@L362:\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcbw\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tja @@L359\r\n\tor al,al\r\n\tje done \t;\\0?\r\n\tsub al,'X'\r\n\tje handle_x\r\n\tsub al,11\r\n\tje handle_c\t;'c'\r\n\tdec al\r\n\tje handle_d\t;'d'\r\n\tsub al,5\r\n\tje handle_i\t;'i'\r\n\tsub al,10\r\n\tje handle_s\t;'s'\r\n\tsub al,2\r\n\tje handle_u\t;'u'\r\n\tjmp @@L359\r\nhandle_c:\t\t\t\t;'c'\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_s:\t\t\t\t;'s'\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\tjmp @@do_outputstring260\r\n\r\nhandle_x:\t\t\t\t;'X' + 'x'\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\t\t\t\t;'d'\r\nhandle_i:\t\t\t\t;'i'\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\t\t\t\t;'u'\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall @@do_outputstring260\r\n\tpop ds\r\n\tretn\r\n\r\n@@do_outputstring260:\r\n\tmov ax,si\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\txchg ax, si\r\n\tsub ax, si\r\n\tsub [size_],ax\r\n\tcmp BYTE PTR [flag],1\r\n\tjne @@L360\r\n\tmov bx,[size_]\r\n\tjmp @@L363\r\n@@F270:\r\n\tmov al,[fill]\r\n\tcall handle_char\r\n\tdec bx\r\n@@L363:\r\n\tor bx,bx\r\n\tjg @@F270\r\n\tmov [size_],bx\r\n\tjmp @@L360\r\n@@F273:\r\n\tmov al,[si]\r\n\tcall handle_char\r\n\tinc si\r\n@@L360:\r\n\tcmp BYTE PTR [si],0\r\n\tjne @@F273\r\n\tmov bx,[size_]\r\n@@:\r\n\tor bx,bx\r\n\tjle @F\r\n\tmov al,[fill]\r\n\tcall handle_char\r\n\tdec bx\r\n\tjmp @B\r\n@@:\r\n\tretn\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Test/README.TXT",
    "content": "\r\nbmintrm:    benchmark int 69h real-mode\r\nems4e:      get/set page map\r\nems4f:      get/set partial page map\r\nems56:      alter map and call\r\nems57:      EMS copy\r\nems57a:     copy EMS to EMS, 1 MB\r\nems5b:      alternate map register set\r\nexc00:      v86 divide error\r\nexc06:      v86 invalid opcode exception\r\nframeres:   reset page frame mapping to a known value\r\nhlttest:    emulation of HLT, IF=1\r\nhlttest2:   emulation of HLT, IF=0\r\ni15move:    move block using int 15h, ah=87h\r\nint67:      test monitor if IVT 67h is hooked\r\ntestdma:    read FD in UMB, using int 25h\r\ntestdma2:   read FD in UMB, using int 13h\r\ntestdma3:   read FD in UMB, using int 13h, ensured that DMA buffer/PTE remap must be used.\r\ntestvds:    run various VDS API calls\r\ntestvds2:   test VDS API calls 810B/810C for DMA channel 2\r\ntestvds3:   test 810B/810C for all channels\r\nxmstest:    XMS memory moves\r\nxmstest2:   XMS block allocation\r\nxmstest3:   test XMS functions 0Eh/8Eh (get handle information)\r\nxmstest4:   test XMS resize function 8Fh\r\nxmstest5:   test XMS resize function 0Fh\r\n\r\nDebug scripts:\r\ntest0c.deb  test stack exception 0Ch\r\ntest0c1.deb test stack exception 0Ch\r\ntest0d.deb  test GPF\r\ntest10.deb  test floating point exception 10h\r\ntest11.deb  test alignment check exception 11h\r\n"
  },
  {
    "path": "Test/TEST0C.DEB",
    "content": "; test v86 exc 0C\r\na\r\nmov sp,ffff\r\npop ax\r\nint 3\r\n\r\ng=100\r\nq\r\n"
  },
  {
    "path": "Test/TEST0C1.DEB",
    "content": "; test v86 exc 0C\r\na\r\nmov sp,1\r\npush ax\r\nint 3\r\n\r\ng=100\r\nq\r\n"
  },
  {
    "path": "Test/TEST0D.DEB",
    "content": "; test v86 exc 0D\r\na\r\nmov ax,[ffff]\r\nint 3\r\n\r\ng=100\r\nq\r\n"
  },
  {
    "path": "Test/TEST10.DEB",
    "content": "; test v86 exc 10\r\n; set CR0 NE bit\r\n; enable exceptions in FP control word\r\n; the do an FP pop\r\n; the exception occurs at the NEXT FP instruction (the finit)!\r\na\r\nmov eax,cr0\r\nor al,20\r\nmov cr0,eax\r\nfstcw [180]\r\nand word [180],ffc0\r\nfldcw [180]\r\nfst double [200]\r\nfinit\r\nint 3\r\n\r\ng=100\r\nq\r\n"
  },
  {
    "path": "Test/TEST11.DEB",
    "content": "; test v86 exc 11\r\n; set CR0 AM bit\r\n; set EFL AC bit\r\n; then access stack unaligned\r\na\r\nmov eax,cr0\r\nor eax,40000\r\nmov cr0,eax\r\nand sp,FFFC\r\npushfd\r\npop eax\r\nor eax,40000\r\npush eax\r\npopfd\r\npush ax\r\npush eax\r\nint 3\r\n\r\ng=100\r\nq\r\n"
  },
  {
    "path": "Test/TESTDMA.ASM",
    "content": "\r\n;--- test FD read in UMB\r\n;--- using Int 25h\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal stat1:word\r\nlocal stat2:word\r\nlocal wSeg:word\r\n\r\n\tmov ax, 5802h\t\t;get status umb\r\n\tint 21h\r\n\txor ah,ah\r\n\tmov stat1, ax\r\n\r\n\tmov ax, 5800h\t\t;get memory alloc strategy\r\n\tint 21h\r\n\txor ah,ah\r\n\tmov stat2, ax\r\n\r\n\tmov bx, 81h\t\t\t;first high,then low\r\n\tmov ax, 5801h\t\t;set memory alloc strat\r\n\tint 21h\r\n\tmov bx, 1\t\t\t;include umbs\r\n\tmov ax, 5803h\t\t;umb link restore\r\n\tint 21h\r\n\r\n\r\n\tmov bx, 100h\t\t; allocate 4 kB\r\n\tmov ah, 48h\r\n\tint 21h\r\n\tjc exit\r\n\tmov wSeg, ax\r\n\r\n\tmov es, ax\r\n\txor di, di\r\n\tmov cx, 1000h\r\n\tmov al, 00\r\n\trep stosb\r\n\r\n\tpush ds\r\n\tmov ds, wSeg\t\t; ds:bx=transfer buffer\r\n\tmov bx, 0\r\n\tmov cx, 8\t\t\t; 8 sectors to read\r\n\tmov al, 0\t\t\t; al=0 -> A:\r\n\tmov dx, 0\t\t\t; start sector #\r\n\tint 25h\r\n\tjc error\r\n\tadd sp, 2\r\n\tpop ds\r\n\tinvoke printf, CStr(\"reading drive A: ok\",10)\r\n\tjmp exit\r\nerror:\r\n\tadd sp, 2\r\n\tpop ds\r\n\tinvoke printf, CStr(\"reading drive A: failed, error code=%X\",10), ax\r\nexit:\r\n\tmov bx, stat1\r\n\tmov ax, 5803h\r\n\tint 21h\r\n\tmov bx, stat2\r\n\tmov ax, 5801h\t\t; set memory alloc strag\r\n\tint 21h\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TESTDMA2.ASM",
    "content": "\r\n;--- test FD read in UMB\r\n;--- using Int 13h\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\n?SIZE equ 1000h\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nwritefile proc c pName:ptr, pBuffer:ptr far16, wSize:word\r\n\tmov dx, pName\r\n\txor cx, cx\r\n\tmov ax,3c00h\r\n\tint 21h\r\n\tjc fail1\r\n\tmov bx, ax\r\n\tpush ds\r\n\tlds dx, pBuffer\r\n\tmov cx, wSize\r\n\tmov ax, 4000h\r\n\tint 21h\r\n\tpop ds\r\n\tjc fail2\r\n\tmov ah, 3Eh\r\n\tint 21h\r\n\tret\r\nfail1:\r\n\tinvoke printf, CStr(\"create file failed\",10)\r\n\tret\r\nfail2:\r\n\tinvoke printf, CStr(\"write file failed\",10)\r\n\tret\r\nwritefile endp\r\n\r\nmain proc c\r\n\r\nlocal stat1:word\r\nlocal stat2:word\r\nlocal wSeg:word\r\n\r\n\tmov ax, 5802h\t\t;get status umb\r\n\tint 21h\r\n\txor ah,ah\r\n\tmov stat1, ax\r\n\r\n\tmov ax, 5800h\t\t;get memory alloc strategy\r\n\tint 21h\r\n\txor ah,ah\r\n\tmov stat2, ax\r\n\r\n\tmov bx, 81h\t\t\t;first high,then low\r\n\tmov ax, 5801h\t\t;set memory alloc strat\r\n\tint 21h\r\n\tmov bx, 1\t\t\t;include umbs\r\n\tmov ax, 5803h\t\t;umb link restore\r\n\tint 21h\r\n\r\n\tmov bx, ?SIZE shr 4\t; allocate mem block\r\n\tmov ah, 48h\r\n\tint 21h\r\n\tjc exit\r\n\tmov wSeg, ax\r\n\r\n\tmov es, ax\r\n\txor di, di\r\n\tmov cx, ?SIZE\r\n\tmov al, 00\r\n\trep stosb\r\n\r\n\tpush es\r\n\tmov es, wSeg\t\t; es:bx=transfer buffer\r\n\tmov bx, 0\r\n\tmov cx, 1\t\t\t; cl[0-5]: sector#; ch+cl[6-7]:cylinder\r\n\tmov dh, 0\t\t\t; dh=head\r\n\tmov dl, 0\t\t\t; dl=0 -> A:\r\n\tmov ax, 0200h or (?SIZE shr 9)\t; al=# of sectors to read\r\n\tint 13h\r\n\tpop es\r\n\tjc error\r\n\tinvoke printf, CStr(\"reading drive A: ok\",10)\r\n\tmov ax, wSeg\r\n\tinvoke writefile, CStr(\"~XXX.TMP\"), ax::bx, (?SIZE shr 9)*200h\r\n\tjmp exit\r\nerror:\r\n\tmovzx ax, ah\r\n\tinvoke printf, CStr(\"reading drive A: failed, error code=%X\",10), ax\r\nexit:\r\n\tmov bx, stat1\r\n\tmov ax, 5803h\r\n\tint 21h\r\n\tmov bx, stat2\r\n\tmov ax, 5801h\t\t; set memory alloc strag\r\n\tint 21h\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TESTDMA3.ASM",
    "content": "\r\n;--- test FD read in UMB\r\n;--- using Int 13h\r\n;--- this variant ensures that a 64kb border is crossed,\r\n;--- forcing the EMM to either \"auto remap\" PTEs (EMM386) or\r\n;--- use the DMA buffer (Jemm).\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\toption casemap:none\r\n\t.386\r\n\r\n?BSIZE equ 8000h\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\nDDS struct\r\ndwSize dd ?\r\ndwOfs  dd ?\r\nwSeg   dw ?\r\nwID    dw ?\r\ndwPhys dd ?\r\nDDS ends\r\n\r\n\t.data?\r\n\r\ndds DDS <>\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nwritefile proc c pName:ptr, pBuffer:ptr far16, wSize:word\r\n\tmov dx, pName\r\n\txor cx, cx\r\n\tmov ax,3c00h\r\n\tint 21h\r\n\tjc fail1\r\n\tmov bx, ax\r\n\tpush ds\r\n\tlds dx, pBuffer\r\n\tmov cx, wSize\r\n\tmov ax, 4000h\r\n\tint 21h\r\n\tpop ds\r\n\tjc fail2\r\n\tmov ah, 3Eh\r\n\tint 21h\r\n\tret\r\nfail1:\r\n\tinvoke printf, CStr(\"create file failed\",10)\r\n\tret\r\nfail2:\r\n\tinvoke printf, CStr(\"write file failed\",10)\r\n\tret\r\nwritefile endp\r\n\r\nmain proc c\r\n\r\nlocal stat1:word\r\nlocal stat2:word\r\nlocal wSeg:word\r\n\r\n;--- requires VDS\r\n\r\n\txor ax, ax\r\n\tmov es, ax\r\n\ttest byte ptr es:[47bh],20h\r\n\tjz novds\r\n\tmov ax,8102h\r\n\tmov dx,0000\r\n\tint 4bh\r\n\tjnc @F\r\nnovds:\r\n\tinvoke printf, CStr(\"VDS not installed\",lf)\r\n\tjmp exit\r\n@@:\r\n\r\n\tmov ax, 5802h\t\t;get status umb\r\n\tint 21h\r\n\txor ah,ah\r\n\tmov stat1, ax\r\n\r\n\tmov ax, 5800h\t\t;get memory alloc strategy\r\n\tint 21h\r\n\txor ah,ah\r\n\tmov stat2, ax\r\n\r\n\tmov bx, 81h\t\t\t;first high,then low\r\n\tmov ax, 5801h\t\t;set memory alloc strat\r\n\tint 21h\r\n\tmov bx, 1\t\t\t;include umbs\r\n\tmov ax, 5803h\t\t;umb link restore\r\n\tint 21h\r\n\r\n\tmov bx, ?BSIZE shr 4; allocate mem block\r\n\tmov ah, 48h\r\n\tint 21h\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"no free %ukB block found in Upper Memory\",10), ?BSIZE shr 10\r\n\tjmp exit\r\n@@:\r\n\tmov wSeg, ax\r\n\tmov es, ax\r\n\txor di, di\r\n\tmov cx, ?BSIZE\r\n\tmov al, 00\r\n\trep stosb\r\n\r\n;--- call VDS to see when a 4 KB block isn't contiguous or crosses a 64kb border\r\n\tpush ds\r\n\tpop es\r\n\r\n\tmov cx, ?BSIZE shr 12\r\n\tmov dds.wSeg, 0\r\n\tmovzx eax, wSeg\r\n\tshl eax, 4\r\nnexttry:\r\n\tmov di, offset dds\r\n\tmov dds.dwSize, 1000h\r\n\tmov dds.dwOfs, eax\r\n\tpush eax\r\n\tmov dx, (1 shl 2) or (1 shl 4)\t; b2=1:don't alloc buffer, b4=1:64kb crossing disallowed\r\n\tmov ax, 8103h\r\n\tint 4Bh\r\n\tjc found\t\t\t; found an address that requires remap/buffer\r\n\txor dx, dx\r\n\tmov ax, 8104h\t\t; unlock block\r\n\tint 4Bh\r\n\tpop eax\r\n\tadd eax, 1000h\r\n\tloop nexttry\r\n\tinvoke printf, CStr(\"no uncontiguous 4kB block found\",10)\r\n\tjmp exit\r\nfound:\r\n\tpop eax\r\n\tshr eax, 4\r\n\tmov wSeg, ax\r\n\r\n\tmov es, wSeg\t\t; es:bx=transfer buffer\r\n\tmov bx, 0\r\n\tmov cx, 1\t\t\t; cl[0-5]: sector#; ch+cl[6-7]:cylinder\r\n\tmov dh, 0\t\t\t; dh=head\r\n\tmov dl, 0\t\t\t; dl=0 -> A:\r\n\tmov ax, 0208h\t\t; al=# of sectors to read\r\n\tint 13h\r\n\tjc error\r\n\tinvoke printf, CStr(\"reading drive A: ok\",10)\r\n\tmov ax, wSeg\r\n\tinvoke writefile, CStr(\"~XXX.TMP\"), ax::bx, 200h*8\r\n\tjmp exit\r\nerror:\r\n\tmovzx ax, ah\r\n\tinvoke printf, CStr(\"reading drive A: failed, error code=%X\",10), ax\r\nexit:\r\n\tmov bx, stat1\r\n\tmov ax, 5803h\r\n\tint 21h\r\n\tmov bx, stat2\r\n\tmov ax, 5801h\t\t; set memory alloc strag\r\n\tint 21h\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TESTVCPI.ASM",
    "content": "\r\n;--- the purpose of the test is to see if page table 0, returned by int 67h, ax=DE01h\r\n;--- is updated by the VCPI host. That's claimed by the VCPI docs, but of course it isn't.\r\n;--- It's simply not feasible, because the host doesn't know if the table is still \"valid\".\r\n\r\n\r\n;--- if segments are defined BEFORE .model,\r\n;--- the alignment can be set differently (JWasm only).\r\n\r\n_DATA segment para public 'DATA'\r\n_DATA ends\r\n_BSS segment para public 'BSS'\r\n_BSS ends\r\n\r\n\t.286\r\n\t.model tiny\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386P\r\n\toption casemap:none\r\n\r\n?DISPPTE equ 0\t\t;1=display PTE entries before/after mapping change.\r\n?ENTRIES equ 0F0h\t;PTE entries to display\r\n?DEB386  equ 1\t\t;1=support (w)deb386 interface\r\n?DISPEXC equ 0\t\t;display exceptions in ring 0\r\n\r\n;DPMI real mode call structure - int 31h, ax=300h is emulated.\r\n\r\nRMCS    struct\r\nunion\r\nrEDI    dd ?            ;+0\r\nrDI\t\tdw ?\r\nends\r\nunion\r\nrESI    dd ?            ;+4\r\nrSI\t\tdw ?\r\nends\r\nunion\r\nrEBP    dd ?            ;+8\r\nrBP\t\tdw ?\r\nends\r\nRESERVE dd ?            ;+12\r\nunion\r\nrEBX    dd ?            ;+16\r\nrBX     dw ?\r\nends\r\nunion\r\nrEDX    dd ?            ;+20\r\nrDX     dw ?\r\nends\r\nunion\r\nrECX    dd ?            ;+24\r\nrCX\t\tdw ?\r\nends\r\nunion\r\nrEAX    dd ?            ;+28\r\nrAX\t\tdw ?\r\nends\r\nrFlags  dw ?            ;+32\r\nrES     dw ?            ;+34\r\nrDS     dw ?            ;+36\r\nrFS     dw ?            ;+38    \r\nrGS     dw ?            ;+40  \r\nunion\r\nrCSIP\tdd ?            ;+42\r\nstruct\r\nrIP     dw ?\r\nrCS     dw ?\r\nends\r\nends\r\nunion\r\nrSSSP\tdd ?            ;+46\r\nstruct\r\nrSP     dw ?\r\nrSS     dw ?\r\nends\r\nends\r\nRMCS    ends\r\n\r\n\tinclude vcpi.inc\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nif ?DEB386\r\nD386_Identify       equ 43h\t; returns debugger identification\r\nD386_Prepare_PMode  equ 44h\t; partially prepare for protected mode operation\r\nD386_Real_Mode_Init equ 45h\t; tell kd we're done\r\nPMINIT_INIT_IDT     equ 0\t; (ES:EDI) = pointer to PM IDT\r\nD386_Id             equ 0F386h ; debugger identification code\r\nendif\r\n\r\nlf\tequ 10\r\ncr\tequ 13\r\n\r\n\t.data\r\n\r\nmygdt label byte                ;GDT\r\n        db 1*8 dup (0)          ;+00h null descriptor\r\nrestab  db 3*8 dup (0)          ;+08h descriptors reserved for vcpi host\r\npmcs    desc <0ffffh,0,0,9Ah,0,0>\t;+20h\r\npmds    desc <0ffffh,0,0,92h,0,0>\t;+28h\r\npmtr    desc <068h-1,0,0,89h,0,0> ;selector for TR\t;+38h\r\npmflat  desc <0ffffh,0,0,92h,8fh,0>\t;+30h\r\nif ?DEB386\r\nkddesc label dword\t\t\t;+48\r\n\t\tdb 3*8 dup (0)\r\nKDSEL   equ kddesc - mygdt\r\nendif\r\nSIZGDT\tequ $      - mygdt\r\n\r\nCSR0\tequ pmcs   - mygdt\r\nDSR0\tequ pmds   - mygdt\r\nTRSEL \tequ pmtr   - mygdt\r\nFLATSEL\tequ pmflat - mygdt\r\nHOSTCS\tequ restab - mygdt\r\n\r\npdgdt    label fword\r\n         dw SIZGDT-1            ;limit GDTR\r\nbasegdt  dd offset mygdt        ;base  GDTR\r\n\r\npdidt    label fword\r\n         dw sizeof myidt-1      ;limit IDTR\r\nbaseidt  dd offset myidt        ;base  IDTR\r\n\r\n;--- far32 address of VCPI protected-mode API\r\n\r\nvcpiv   label fword\r\nvcpiofs dd 0\r\n\t\tdw HOSTCS\t\t;selector for VCPI host code segment\r\n\t\tdw 0\r\n\r\nmsw V86toPM <0, offset pdgdt, offset pdidt, 0, TRSEL, offset pmentry, CSR0>\r\n\r\nsegcs   dw 0\t\t;segment CS\r\nsegds   dw 0\t\t;segment DS\r\nptadr   dw 0\t\t;segment page table 0\r\nvcpiend dw 0\t\t;offset in pagetab 0 where free space begins\r\n\r\nif ?DEB386\r\nKD_DEB386  equ 2\r\nbKrnlDbg db 0\r\ndfDbgEntry df 0\r\nendif\r\n\r\n\t.data?\r\n\r\nmyidt   db 100h*8 dup (?)\r\nptab    db 1000h dup (?)\r\ntaskseg TSSSEG <>   ;task state segment\r\nrmcs RMCS <>\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nprintln proc\r\n\tinvoke printf, CStr(10)\r\n\tret\r\nprintln endp\r\n\r\nif ?DISPEXC\r\n\r\n;------ FEDCBA9876543210   FEDCBA9876543210\r\nexcv dw 0111110100000000b, 0000000000000110b\r\n\r\n?EXC = 0\r\n\r\n@defexc macro\r\n\tpush ?EXC\r\n\tjmp defexc\r\n?EXC = ?EXC + 1\r\nendm\r\n\r\nexceptions:\r\n\trept 32\r\n\t@defexc\r\n\tendm\r\n\r\nEXCFRAME1 struct\r\ndwEbp dd ?\r\nwExc  dw ?,?\r\ndwErr dd ?\r\ndwEip dd ?\r\nwCs   dw ?,?\r\ndwEfl dd ?\r\nEXCFRAME1 ends\r\n\r\nEXCFRAME2 struct\r\ndwEbp dd ?\r\nwExc  dw ?,?\r\ndwEip dd ?\r\nwCs   dw ?,?\r\ndwEfl dd ?\r\nEXCFRAME2 ends\r\n\r\ndefexc:\r\n\tpush ebp\r\n\tmov ebp,esp\r\n\tmov ax, [ebp].EXCFRAME1.wExc\r\n\tbt cs:[excv], ax\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"exc %X at %X:%lX, errc=%lX\",lf), [ebp].EXCFRAME1.wExc, [ebp].EXCFRAME1.wCs, [ebp].EXCFRAME1.dwEip, [ebp].EXCFRAME1.dwErr\r\n\tjmp de2\r\n@@:\r\n\tinvoke printf, CStr(\"exc %X at %X:%lX\",lf), [ebp].EXCFRAME2.wExc, [ebp].EXCFRAME2.wCs, [ebp].EXCFRAME2.dwEip\r\nde2:\r\n\tjmp $\t; just stop, we cannot continue\r\n\r\nendif\r\n\r\ndefint proc near\r\n\tpush eax\r\n\tmov al,0Bh\r\n\tout 0A0h,al\r\n\tin al,0A0h\r\n\tand al,al\r\n\tjz @F\r\n\tmov al,20h\r\n\tout 0a0h,al\r\n@@:\r\n\tmov al,0Bh\r\n\tout 20h,al\r\n\tin al,20h\r\n\tand al,al\r\n\tjz @F\r\n\tmov al,20h\r\n\tout 20h,al\r\n@@:\r\n\tpop eax\r\n\tiretd\r\ndefint endp\r\n\r\nint31 proc\r\n\tcmp ax,300h\r\n\tjz is300\r\n\tjmp exit\r\nis300:\r\n\tpushad\r\n\tpush di\r\n\tcall pm2rm\r\n\tmov [intno],bl\r\n\tpop di\r\n\tpush di\r\n\tmov esi,es:[di].RMCS.rESI\r\n\tmov ebp,es:[di].RMCS.rEBP\r\n\tmov ebx,es:[di].RMCS.rEBX\r\n\tmov ecx,es:[di].RMCS.rECX\r\n\tmov edx,es:[di].RMCS.rEDX\r\n\tmov eax,es:[di].RMCS.rEAX\r\n\tmov ds,es:[di].RMCS.rDS\r\n\tmov edi,es:[di].RMCS.rEDI\r\n\tdb 0CDh\r\nintno db 0\r\n\tpush esi\r\n\tpush eax\r\n\tpush ebx\r\n\tcall rm2pm\r\n\tpop ebx\r\n\tpop eax\r\n\tpop esi\r\n\tpop di\r\n\tmov es:[di].RMCS.rESI, esi\r\n\tmov es:[di].RMCS.rEBP, ebp\r\n\tmov es:[di].RMCS.rEDX, edx\r\n\tmov es:[di].RMCS.rECX, ecx\r\n\tmov es:[di].RMCS.rEBX, ebx\r\n\tmov es:[di].RMCS.rEAX, eax\r\n\tpopad\r\n\tpush ax\r\n\tlahf\r\n\tmov [esp+2*4+2],ah\r\n\tpop ax\r\nexit:\r\n\tiretd\r\nint31 endp\r\n\r\nint21 proc\r\n\r\n    push es\r\n    push di\r\n    mov rmcs.rEAX, eax\r\n    mov rmcs.rEBX, ebx\r\n    mov rmcs.rECX, ecx\r\n    mov rmcs.rEDX, edx\r\n    mov ax,[segds]\r\n    mov rmcs.RMCS.rDS, ax\r\n    mov di, offset rmcs\r\n    push ds\r\n    pop es\r\n    mov bl,21h\r\n    mov ax,300h\r\n    int 31h\r\n    pop di\r\n    pop es\r\n\r\n    push ax\r\n    lahf\r\n    mov [esp+2*4+2],ah\r\n    pop ax\r\n    iretd\r\nint21 endp\r\n\r\n;--- display PTEs of 1. MB\r\n\r\nDispPTEs proc uses es si\r\n\r\n\tinvoke printf, CStr(\"Paging Table\",lf)\r\n\tmov es,[ptadr]\r\n\tmov si,0000\r\n\tmov cx,?ENTRIES\r\nnextitem:\r\n\tpush cx\r\n\ttest si,01Fh\r\n\tjnz @F\r\n\tmov ax,si\r\n\tshr ax,2\r\n\tinvoke printf, CStr(\"%04X: \"), ax\r\n@@:\r\n\tmov eax,es:[si+0]\r\n\tpush ax\r\n\tinvoke printf,CStr(\"%8lX \"),eax\r\n\tpop ax\r\n\tadd si,4\r\n\ttest si,1Fh\r\n\tjnz @F\r\n\tinvoke println\r\n@@:\r\n\tpop cx\r\n\tloop nextitem\r\n\tret\r\nDispPTEs endp\r\n\r\n;--- fill VCPI comm structure\r\n;--- set descriptors and IDT\r\n\r\nsetdescriptors proc\r\n\tmov ax,cs\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tmov pmcs.A0015,ax\r\n\tshr eax,16\r\n\tmov pmcs.A1623,al\r\n\r\n\tmov ax,ds\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tadd basegdt, eax\r\n\tadd baseidt, eax\r\n\tadd msw._Gdtr, eax\r\n\tadd msw._Idtr, eax\r\n\r\n\tmov pmds.A0015,ax\r\n\tshr eax,16\r\n\tmov pmds.A1623,al\r\n\r\n\tmov ax,ds\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tadd eax, offset taskseg\r\n\tmov pmtr.A0015,ax\r\n\tshr eax,16\r\n\tmov pmtr.A1623,al\r\n\tmov dword ptr taskseg.dfStk0, esp\r\n\tmov word ptr taskseg.dfStk0+4, DSR0\r\n\r\n\tmov bx,0\r\n\tmov di,offset myidt\r\nif ?DISPEXC\r\n\tmov ax,offset exceptions\r\nnextitem:\r\n\tmov word ptr [di+0],ax\r\n\tmov word ptr [di+2],CSR0\r\n\tmov word ptr [di+4],0EE00h\r\n\tmov word ptr [di+6],0\r\n\tadd di,8\r\n\tadd ax,4\r\n\tinc bl\r\n\tcmp bl,20h\r\n\tjnz nextitem\r\nendif\r\nnextitem2:\r\n\tmov word ptr [di+0],offset defint\r\n\tmov word ptr [di+2],CSR0\r\n\tmov word ptr [di+4],0EE00h\r\n\tmov word ptr [di+6],0\r\n\tadd di,8\r\n\tinc bl\r\n\tjnz nextitem2\r\n\r\n\tmov word ptr myidt+21h*8,offset int21\r\n\tmov word ptr myidt+31h*8,offset int31\r\n\r\n\tret\r\nsetdescriptors endp\r\n\r\n;--- jump to pm\r\n\r\nrm2pm proc\r\n\tmov pmtr.attrib,89h\r\n\tcli\r\n\tpushf\r\n\tpop ax\r\n\tand ah,03Fh  ;clear NT\r\n\tpush ax\r\n\tpopf\r\n\tpushf\r\n\tmov ax, ds\r\n\tmovzx eax, ax\r\n\tshl eax, 4\r\n\tlea esi, [eax + offset msw]\r\n\tmovzx ebx,sp\r\n\tmov ax,0DE0Ch\r\n\tint 67h\r\npmentry::\t\t\t\t\t\t;now in protected mode\r\n\tmov ax,DSR0\r\n\tmov ss,ax\r\n\tmov esp,ebx\r\n\tmov ds,ax\r\n\tmov es,ax\r\n\tpopf\r\n\tret\r\nrm2pm endp\r\n\r\n;--- jump to v86\r\n\r\npm2rm proc\r\n\r\n\tmovzx edx,sp\r\n\tmovzx eax, [segds]\r\n\tpushd 0\t\t;gs\r\n\tpushd 0\t\t;fs\r\n\tpush eax\t;ds\r\n\tpush eax\t;es\r\n\tpush eax\t;ss\r\n\tpush edx\t;esp\r\n\tpushd 2\t\t;efl\r\n\tmovzx eax, [segcs]\r\n\tpush eax\r\n\tpushd offset rmentr\r\n\tclts\t\t\t\t\t\t;clear task switched flag\r\n\tmov ax, FLATSEL\r\n\tmov ds,ax\t\t\t\t\t;DS must be FLAT\r\n\tmov ax,0DE0Ch\r\n\tcall fword ptr ss:[vcpiv]\r\nrmentr: \t\t\t\t\t\t;back in v86-mode\r\n\tsti\r\n\tret\r\npm2rm endp\r\n\r\n;--- switch to protected-mode\r\n;--- call a real-mode int\r\n;--- finally, switch back to v86-mode\r\n\r\npmgo proc\r\n\r\n\tcli\r\n\r\n\tmov [segcs],cs\r\n\tmov [segds],ds\r\n\r\n\tcall rm2pm\r\n\r\n\tmov ax,FLATSEL\t\t;4G selector -> ES\r\n\tmov es,ax\r\n\r\nif ?DEB386\r\n\tcmp bKrnlDbg,KD_DEB386\r\n\tjnz @F\r\n\tpush ds\r\n\tpop es\r\n\tmov edi, offset myidt\t;es:edi=idt\r\n\tmov al, PMINIT_INIT_IDT\r\n\tcall [dfDbgEntry]\r\n\tint 3\r\n@@:\r\nendif\r\n\r\n\tinvoke printf, CStr(\"hello from pm\",10)\r\n\r\n;--- now map another page into physical page 0\r\n\r\n\tmov rmcs.rDX,0\t\t; system handle\r\n\tmov rmcs.rAX,4400h\t; map page into physical page 0\r\n\tmov rmcs.rBX,0\t\t; logical page 0\r\n\tmov di,offset rmcs\r\n\tmov bl,67h\r\n\tmov ax,300h\r\n\tint 31h\r\n\r\n\tcall pm2rm\r\n    \r\nif ?DEB386\r\n\tcmp bKrnlDbg,KD_DEB386\r\n\tjnz @F\r\n\tmov ah, D386_Real_Mode_Init\r\n\tint 68h\r\n@@:\r\nendif\r\n\tret\r\npmgo endp\r\n\r\n;--- VCPI host was detected.\r\n;--- switch to protected-mode and back\r\n\r\nrunvcpi proc near\r\n\r\n\tmov ax,0DE00h\t\t;is vcpi supported?\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz vcpi_err\r\npvcpi1:\r\n\tcall setdescriptors\r\n\tpush ds\r\n\tpop es\r\n\r\nif ?DEB386\r\n\tmov ah,D386_Identify\r\n\tint 68h\r\n\tcmp ax, D386_Id\r\n\tjnz kd_done\r\n\tmov bKrnlDbg, KD_DEB386\r\n\tmov bx, FLATSEL\r\n\tmov cx, KDSEL\r\n\tmov dx, 0\t; no GDT sel\r\n\tmov si, offset mygdt\t; ds:si=gdt\r\n\tmov di, offset myidt\t; es:di=idt\r\n\tmov ah, D386_Prepare_PMode\r\n\tint 68h\r\n\tmov dword ptr [dfDbgEntry+0], edi\r\n\tmov  word ptr [dfDbgEntry+4], es\r\n\tpush ds\r\n\tpop es\r\nendif\r\nkd_done:\r\n\r\n\tmov es,[ptadr]\r\n\txor di,di\t\t\t;ES:DI -> page table 0\r\n\tmov si,offset restab;DS:SI -> 3 free GDT descriptors\r\n\tmov ax,0DE01h\t\t;get protected mode interface\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz vcpi_err1\r\n\tmov [vcpiofs],ebx\r\n\tmov vcpiend,di\r\nif ?DISPPTE\r\n\tcall DispPTEs\r\nendif\r\n\tcall pmgo\r\nif ?DISPPTE\r\n\tcall DispPTEs\r\nendif\r\nexit:\r\n\tret\r\n\r\nvcpi_err:\r\n\tinvoke printf, CStr(\"no VCPI host found\",lf)\r\n\tjmp exit\r\nvcpi_err1:\r\n\tinvoke printf, CStr(\"VCPI func DE01 failed\",lf)\r\n\tjmp exit\r\nrunvcpi endp\r\n\r\n;--- get cmdline parameters\r\n\r\ngetparm proc near\r\n\tret\r\ngetparm endp\r\n\r\nmain proc c\r\n\r\n\tmov di, offset ptab\r\n\tmov cx, (sizeof ptab) / 4\r\n\tpush ds\r\n\tpop es\r\n\txor eax,eax\r\n\trep stosd\r\n\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,es\r\n\tor ax,bx\r\n\tjnz main3\r\n\tinvoke printf, CStr(\"int 67h is zero (no EMM)\",lf)\r\n\tmov al,01\r\n\tjmp exit\r\nmain3:\r\n\t\t\t\t\t\t;get memory for page tables\r\n\tmov bx,400h\r\n\tmov ah,48h\r\n\tint 21h\r\n\tjc exit\r\n\tmov bx,ax\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tadd eax,4096-1\t\t;page directory must be page aligned\r\n\tand ax,0f000h\t\t;clear bits 0-11 \r\n\r\n\tmov msw._CR3,eax\r\n\tmov edi,eax\r\n\tadd edi,1000h\r\n\tshr eax,4\r\n\tmov es,ax\r\n\t\t\t\t\t\t;page directory 1. entry\r\n\tor di,1+2+4\t\t\t;set present, r/w, user\r\n\tmov es:[0000],edi\t;set PDE for 0-3FFFFFh\r\n\tmov ax,es\r\n\tadd ax,100h\r\n\tmov [ptadr],ax\r\n\r\n\tcall runvcpi\r\n\r\n\tmov al,00\r\nexit:\r\n\tret\r\nmain endp\r\n\r\n\r\nstart:\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx       ;make SS=DS\r\n\r\n\tmov ax,ds\r\n\tmov cx,cs\r\n\tsub ax,cx\r\n\tadd ax,10h\r\n\tshr bx,4\r\n\tadd bx,ax\r\n\tmov ah,4ah\r\n\tint 21h\r\n\r\n;--- clear uninitialized data\r\n\tpush es\r\n\tpush ds\r\n\tpop es\r\n\tmov di, offset myidt\r\n\tmov cx, sp\r\n\tsub cx, di\r\n\tshr cx, 1\r\n\txor ax, ax\r\n\tcld\r\n\trep stosw\r\n\tpop es\r\n\r\n\tcall main\r\n\tmov ax,4c00h\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TESTVDS.ASM",
    "content": "\r\n;--- VDS API test\r\n;--- 8103/8104:      lock/unlock region\r\n;--- 8107/8108/8109/810A: request/release DMA buffer, copy into/out of DMA buffer\r\n;--- 8105/8106       scatter/gather lock/unlock\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\nDDS struct\r\ndwSize\tdd ?\r\ndwOfs\tdd ?\r\nwSeg\tdw ?\r\nwID\t\tdw ?\r\ndwPhys\tdd ?\r\nDDS ends\r\n\r\nEDDS struct\r\ndwSize\tdd ?\r\ndwOfs\tdd ?\r\nwSeg\tdw ?\r\n\t\tdw ?\r\nnumAvl\tdw ?\r\nnumUsed dw ?\t;after numUsed, PTEs are starting\r\nEDDS ends\r\n\r\n\t.data\r\ndds_\tDDS <>\r\nedds_\tEDDS <>\r\n\t\tdd 64 dup (0)\t;max 32 entries\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nRsvdFunction proc\r\n\r\n;--- call reserved VDS function 8101\r\n\r\n\tmov dx,0\r\n\tmov ax,8101h\r\n\tstc\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8101h failed - good, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8101h ok ? - bad\",lf)\r\n\t.endif\r\n\r\n;--- call reserved VDS function 810D\r\n\r\n\tmov dx,0\r\n\tmov ax,810Dh\r\n\tstc\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Dh failed - good, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Dh ok ? - bad\",lf)\r\n\t.endif\r\n\tret\r\nRsvdFunction endp\r\n\r\n;--- 8103: lock a region\r\n;--- 8104: unlock a region\r\n\r\nLockUnlock proc\r\n\r\nlocal bFail:byte\r\n\r\n;--- 8103/8104: lock a region with size 1000h, not crossing a page boundary\r\n\r\n\tmov di,offset dds_\r\n\tmov dds_.dwSize,1000h\r\n\tmov dds_.dwOfs,0\r\n\tmov dds_.wSeg,0C000h\r\n\tmov dx,0\r\n\tmov ax,8103h\t;lock region\r\n\tint 4bh\r\n\tlahf\r\n\tmov bFail,ah\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8103h, dx=0 [size=1000h, wSeg=C000] failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8103h, dx=0 [size=1000h, wSeg=C000] ok\",lf)\r\n\t.endif\r\n\tinvoke printf, CStr(\"DDS.dwPhys=%lX, DDS.wID=%X\",lf), dds_.dwPhys, dds_.wID\r\n\r\n\t.if (!(bFail & 1))\r\n\t\tmov di,offset dds_\r\n\t\tmov dx,0\r\n\t\tmov ax,8104h\t;unlock region\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8104h [size=1000h, wSeg=C000] failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8104h [size=1000h, wSeg=C000] ok\",lf)\r\n\t\t.endif\r\n\t\tinvoke printf, CStr(\"DDS.dwPhys=%lX, DDS.wID=%X\",lf), dds_.dwPhys, dds_.wID\r\n\t.endif\r\n\r\n;--- 8103/8104: lock a region with size 8000h in UMB region\r\n\r\n\tmov di,offset dds_\r\n\tmov dds_.dwSize,8000h\r\n\tmov dds_.dwOfs,0\r\n\tmov dds_.wSeg,0CC00h\r\n\tmov dx,0\r\n\tmov ax,8103h\t;lock region\r\n\tint 4bh\r\n\tlahf\r\n\tmov bFail,ah\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8103h, dx=0 [size=8000h, wSeg=CC00] failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8103h, dx=0 [size=8000h, wSeg=CC00] ok\",lf)\r\n\t.endif\r\n\tinvoke printf, CStr(\"DDS.dwPhys=%lX, DDS.wID=%X\",lf), dds_.dwPhys, dds_.wID\r\n\r\n\t.if (!(bFail & 1))\r\n\t\tmov di,offset dds_\r\n\t\tmov dx,0\r\n\t\tmov ax,8104h\t;unlock region\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8104h [size=8000h, wSeg=CC00] failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8104h [size=8000h, wSeg=CC00] ok\",lf)\r\n\t\t.endif\r\n\t\tinvoke printf, CStr(\"DDS.dwPhys=%lX, DDS.wID=%X\",lf), dds_.dwPhys, dds_.wID\r\n\t.endif\r\n\r\n;--- 8104: unlock a region with an invalid buffer ID\r\n\r\n\tmov dds_.wID, -1\r\n\tmov dx,0\r\n\tmov ax,8104h\t;unlock region\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8104h with buffer ID FFFF failed - good\",lf)\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8104h with buffer ID FFFF ok ? - bad\",lf)\r\n\t.endif\r\n\r\n;--- 8103/8104: lock a UMB region with size 20000h - most likely fails\r\n\r\n\tmov di,offset dds_\r\n\tmov dds_.dwSize,20000h\r\n\tmov dds_.dwOfs,0\r\n\tmov dds_.wSeg,0C000h\r\n\tmov dx,4\t\t;disable buffering\r\n\tmov ax,8103h\t;lock region\r\n\tint 4bh\r\n\tlahf\r\n\tmov bFail, ah\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8103h dx=4 [dds.size=20000h, wSeg=C000] failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8103h dx=4 [dds.size=20000h, wSeg=C000] ok\",lf)\r\n\t.endif\r\n\r\n\tinvoke printf, CStr(\"DDS.dwSize=%lX\",lf), dds_.dwSize\r\n\tinvoke printf, CStr(\"DDS.dwOfs=%lX\",lf), dds_.dwOfs\r\n\tinvoke printf, CStr(\"DDS.wSeg=%X\",lf), dds_.wSeg\r\n\tinvoke printf, CStr(\"DDS.dwPhys=%lX\",lf), dds_.dwPhys\r\n\t.if (!(bFail & 1))\r\n\t\tmov di,offset dds_\r\n\t\tmov dx,0\r\n\t\tmov ax,8104h\t;unlock region\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8104h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8104h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\tret\r\n\r\nLockUnlock endp\r\n\r\n;--- 8107 - request a DMA buffer, size 32 kb\r\n;--- 8108 - release DMA buffer\r\n;--- 8109 - copy into DMA buffer\r\n;--- 810A - copy out of DMA buffer\r\n\r\nDMABuffer proc\r\n\r\nlocal bFail:byte\r\n\r\n\tmov di,offset dds_\r\n\tmov dds_.dwSize,8000h\r\n;\tmov dds_.dwOfs,0\t\t; needed if copy into buffer is requested\r\n;\tmov dds_.wSeg,0C000h\t; needed if copy into buffer is requested\r\n\tmov dx,0\t\t;don't copy into buffer\r\n\tmov ax,8107h\t;request DMA buffer\r\n\tint 4bh\r\n\tlahf\r\n\tmov bFail, ah\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8107h dx=0 [dds.size=8000h] failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8107h dx=0 [dds.size=8000h] ok\",lf)\r\n\t.endif\r\n\tinvoke printf, CStr(\"DDS.wID=%X, DDS.dwPhys=%lX\",lf), dds_.wID, dds_.dwPhys\r\n\r\n\t.if (!(bFail & 1))\r\n\r\nif 0\r\n\t\tmov dx,0\r\n\t\tmov ax,8102h\t;\"get version\" (buffer size in SI::DI)\r\n\t\tint 4bh\r\n\t\tinvoke printf, CStr(\"maximum dma buffer size after Int 4B, ax=8107: %lX\",lf), si::di\r\nendif\r\n\r\n\t\tmov di,offset dds_\r\n\t\tmov dds_.dwSize,1000h\r\n\t\tmov dds_.dwOfs,0\r\n\t\tmov dds_.wSeg, ds\r\n\t\tmov dx,0\t\t;must be 0\r\n\t\txor bx,bx\t\t;bx:cx == offset in buffer\r\n\t\txor cx,cx\r\n\t\tmov ax,8109h\t;copy into DMA buffer\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8109h [size=1000h] failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8109h [size=1000h] ok\",lf)\r\n\t\t.endif\r\n\r\n\t\tmov di,offset dds_\r\n\t\tmov dx,0\t\t;must be 0\r\n\t\txor bx,bx\t\t;bx:cx == offset in buffer\r\n\t\txor cx,cx\r\n\t\tmov ax,810Ah\t;copy out of DMA buffer\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=810Ah [size=1000h] failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=810Ah [size=1000h] ok\",lf)\r\n\t\t.endif\r\n\r\n\t\tmov dx,0\t\t;dont copy out of buffer\r\n\t\tmov ax,8108h\t;release DMA buffer\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8108h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8108h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\tret\r\nDMABuffer endp\r\n\r\n;--- several variants of 8105/8106: scatter/gather lock\r\n\r\nScatterGather proc\r\n\r\n;--- 8105/8106: scatter/gather lock with PTEs, sufficient entries\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,10000h\r\n\tmov [di].EDDS.dwOfs,0h\r\n\tmov [di].EDDS.wSeg,0C800h\r\n\tmov [di].EDDS.numAvl,16\r\n\tmov dx,40h\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke 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\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=40h reg=C800:10000 ok\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX \"), eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- 8105/8106: scatter/gather lock with PTEs, insufficient entries\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,10000h\r\n\tmov [di].EDDS.dwOfs,0h\r\n\tmov [di].EDDS.wSeg,0C800h\r\n\tmov [di].EDDS.numAvl,9h\r\n\tmov dx,40h\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke 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\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=40h reg=C800:10000 ok [bad]\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX \"), eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- 8105/8106: scatter/gather lock with PTEs, region size 0\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,0\r\n\tmov [di].EDDS.dwOfs,0F000h\r\n\tmov [di].EDDS.wSeg,0C000h\r\n\tmov [di].EDDS.numAvl,4h\r\n\tmov dx,40h\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke 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\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=40h reg=CF000:0 ok\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX \"), eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- 8105/8106: scatter/gather lock without PTEs, size 64 kB\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,10000h\r\n\tmov [di].EDDS.dwOfs,00h\r\n\tmov [di].EDDS.wSeg,0C800h\r\n\tmov [di].EDDS.numAvl,8\r\n\tmov dx,0\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=C800:10000 failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=C800:10000 ok\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tmov edx, eax\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX:%lX \"), edx, eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- 8105/8106: scatter/gather lock without PTEs, size 5 byte\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,5\r\n\tmov [di].EDDS.dwOfs,00h\r\n\tmov [di].EDDS.wSeg,0C800h\r\n\tmov [di].EDDS.numAvl,4\r\n\tmov dx,0\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=C800:5 failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=C800:5 ok\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tmov edx, eax\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX:%lX \"), edx, eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- 8105/8106: scatter/gather lock without PTEs, size 2 byte, starting at CEFFF\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,2\r\n\tmov [di].EDDS.dwOfs,0EFFFh\r\n\tmov [di].EDDS.wSeg,0C000h\r\n\tmov [di].EDDS.numAvl,4\r\n\tmov dx,0\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=CEFFF:2 failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=CEFFF:2 ok\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tmov edx, eax\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX:%lX \"), edx, eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- 8105/8106: scatter/gather lock without PTEs, size 0 byte\r\n\r\n\tmov di,offset edds_\r\n\tmov [di].EDDS.dwSize,0\r\n\tmov [di].EDDS.dwOfs,0F000h\r\n\tmov [di].EDDS.wSeg,0C000h\r\n\tmov [di].EDDS.numAvl,4\r\n\tmov dx,0\r\n\tmov ax,8105h\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=CF000:0 failed, AL=%X\",lf), ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=8105h, dx=0 reg=CF000:0 ok\",lf)\r\n\t\tmov cx, es:[di].EDDS.numUsed\r\n\t\tlea si,[di+sizeof EDDS]\r\n\t\t.while cx\r\n\t\t\tlodsd\r\n\t\t\tmov edx, eax\r\n\t\t\tlodsd\r\n\t\t\tpush cx\r\n\t\t\tinvoke printf, CStr(\"%lX:%lX \"), edx, eax\r\n\t\t\tpop cx\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t\tinvoke printf, CStr(lf)\r\n\t\tmov dx,0\r\n\t\tmov ax,8106h\r\n\t\tint 4bh\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h failed, AL=%X\",lf), ax\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=8106h ok\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\tret\r\nScatterGather endp\r\n\r\nmain proc c\r\n\r\nlocal\tversion:word\r\nlocal\tproduct:word\r\nlocal\trevision:word\r\nlocal\tdmasize:dword\r\nlocal\tflags:word\r\n\r\n\txor ax, ax\r\n\tmov es, ax\r\n\ttest byte ptr es:[47bh],20h\r\n\tjz novds\r\n\tmov ax,8102h\r\n\tmov dx,0000\r\n\tint 4bh\r\n\tjnc @F\r\nnovds:\r\n\tinvoke printf, CStr(\"VDS not installed\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmov version,ax\r\n\tmov product,bx\r\n\tmov revision,cx\r\n\tmov word ptr dmasize+0,di\r\n\tmov word ptr dmasize+2,si\r\n\tmov flags,dx\r\n\tinvoke printf, CStr(\"version: %u.%u\",lf), byte ptr version+1, byte ptr version+0\r\n\tinvoke printf, CStr(\"product number/revision: %X/%X\",lf), product, revision\r\n\tinvoke printf, CStr(\"maximum dma buffer size: %lX\",lf), dmasize\r\n\tinvoke printf, CStr(\"flags: %X\",lf), flags\r\n\r\n\tmov ax,ds\r\n\tmov es,ax\r\n\r\n\tcall RsvdFunction\r\n\tinvoke printf, CStr(lf)\r\n\tcall LockUnlock\r\n\tinvoke printf, CStr(lf)\r\n\tcall DMABuffer\r\n\tinvoke printf, CStr(lf)\r\n\tcall ScatterGather\r\n\r\nexit:\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TESTVDS2.ASM",
    "content": "\r\n;--- VDS API test; test disable/enable auto DMA translation (810B/810C)\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal\tchannel:word\r\n\r\n\txor ax, ax\r\n\tmov es, ax\r\n\ttest byte ptr es:[47bh],20h\r\n\tjz novds\r\n\tmov ax,8102h\r\n\tmov dx,0000\r\n\tint 4bh\r\n\tjnc @F\r\nnovds:\r\n\tinvoke printf, CStr(\"VDS not installed\",lf)\r\n\tjmp exit\r\n@@:\r\n\r\n;--- disable DMA translation for channel 2\r\n\r\n\tmov channel, 2\r\nnextchannel:\r\n\tmov dx, 0\r\n\tmov bx, channel\r\n\tmov ax,810Bh\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Bh, BX=%u failed, AL=%X\",lf), bx, ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Bh, BX=%u ok\",lf), bx\r\n\t.endif\r\n\r\n;--- reenable DMA translation\r\n\r\n\tmov dx, 0\r\n\tmov bx, channel\r\n\tmov ax,810Ch\r\n\tint 4bh\r\n\tsetz cl\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Ch, BX=%u failed, AL=%X\",lf), bx, ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Ch, BX=%u ok (ZF=%u)\",lf), bx, cl\r\n\t.endif\r\n\r\n;--- the second call should fail!\r\n\r\n\tmov dx, 0\r\n\tmov bx, channel\r\n\tmov ax,810Ch\r\n\tint 4bh\r\n\tsetz cl\r\n\t.if (CARRY?)\r\n\t\tcbw\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Ch, BX=%u failed, AL=%X\",lf), bx, ax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"int 4B, ax=810Ch, BX=%u ok (ZF=%u)\",lf), bx, cl\r\n\t.endif\r\n\r\n\tinc channel\r\n\tcmp channel, 4\r\n\tjb nextchannel\r\n\r\nexit:\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TESTVDS3.ASM",
    "content": "\r\n;--- VDS API test; display ISA DMA translation status for channels 0-7\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nlf equ 10\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nmain proc c\r\n\r\nlocal\tchannel:word\r\n\r\n\txor ax, ax\r\n\tmov es, ax\r\n\ttest byte ptr es:[47bh],20h\r\n\tjz novds\r\n\tmov ax,8102h\r\n\tmov dx,0000\r\n\tint 4bh\r\n\tjnc @F\r\nnovds:\r\n\tinvoke printf, CStr(\"VDS not installed\",lf)\r\n\tjmp exit\r\n@@:\r\n\r\n;--- strategy: \r\n;--- 1. call enable DMA translation\r\n;---    should fail, since disable cnt should be zero\r\n\r\n\tmov channel, 0\r\nnextchannel:\r\n\tmov dx, 0\r\n\tmov bx, channel\r\n\tmov ax,810Ch\r\n\tint 4bh\r\n\t.if (CARRY?)\r\n\t\tinvoke printf, CStr(\"channel %u: DMA translation is active\",lf), bx\r\n\t.else\r\n\t\tinvoke printf, CStr(\"channel %u: DMA translation is disabled\",lf), bx\r\n\r\n;--- restore disable cnt\r\n\r\n\t\tmov dx, 0\r\n\t\tmov bx, channel\r\n\t\tmov ax,810Bh\r\n\t\tint 4bh\r\n\t\tsetz cl\r\n\t\t.if (CARRY?)\r\n\t\t\tcbw\r\n\t\t\tinvoke printf, CStr(\"int 4B, ax=810Bh, BX=%u failed, AL=%X\",lf), bx, ax\r\n\t\t.endif\r\n\t.endif\r\n\r\n\tinc channel\r\n\tcmp channel, 8\r\n\tjb nextchannel\r\n\r\nexit:\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax, @data\r\n\tmov ds, ax\r\n\tmov bx, ss\r\n\tsub bx, ax\r\n\tshl bx, 4\r\n\tmov ss, ax\r\n\tadd sp, bx\r\n\tcall main\r\n\tmov ah, 4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/TIMERMS.INC",
    "content": "\r\n;--- gettimer() returns timer in EAX in ms\r\n\r\n_GetTimerValue proc\r\n\tpush ds\r\n\tpush 0\r\n\tpop ds\r\ntryagain:\r\n\tmov edx,ds:[46ch] \r\n\tmov al,0C2h\t\t;read timer 0 status + value low/high\r\n\tout 43h, al\r\n\txchg edx, edx\r\n\tin al,40h\r\n\tmov cl,al\t\t;CL = status\r\n\txchg edx, edx\r\n\tin al,40h\r\n\tmov ah, al\t\t;AH = value low\r\n\txchg edx, edx\r\n\tin al,40h\t\t;AL = value high\r\n\ttest cl,40h\t\t;was latch valid?\r\n\tjnz tryagain\r\n\tcmp edx,ds:[046ch]\t;did an interrupt occur in the meantime?\r\n\tjnz tryagain\t\t\t;then do it again!\r\n\txchg al,ah\r\n\r\n;--- usually (counter mode 3) the timer is set to count down *twice*! \r\n;--- however, sometimes counter mode 2 is set!\r\n\r\n\tmov ch,cl\r\n\tand ch,0110B\t;bit 1+2 relevant\r\n\tcmp ch,0110B\t;counter mode 3?\r\n\tjnz @F\r\n\r\n;--- in mode 3, PIN status of OUT0 will become bit 15\r\n\r\n\tshr ax,1\r\n\tand cl,80h\r\n\tor ah, cl\r\n@@:\r\n\r\n;--- now the counter is in AX (counts from FFFF to 0000)\r\n\r\n\tneg ax\r\n\r\n;--- now the count is from 0 to FFFF\r\n\r\n\tpop ds\r\n\tret\r\n_GetTimerValue endp\r\n\r\ngettimer proc\r\n\tcall _GetTimerValue\r\n\r\n;--- the timer ticks are in EDX:AX, timer counts down \r\n;--- a 16bit value with 1,193,180 Hz -> 1193180/65536 = 18.20648 Hz\r\n;--- which are 54.83 ms\r\n;--- to convert in ms:\r\n;--- 1. subticks in ms: AX / 1193\r\n;--- 2. ticks in ms: EDX * 55\r\n;--- 3. total 1+2\r\n\r\n\tpush edx\r\n\tmovzx eax,ax\t;step 1\r\n\tcdq\r\n\tmov ecx, 1193\r\n\tdiv ecx\r\n\tmov ecx, eax\r\n\tpop eax \t\t;step 2\r\n\tmov edx, 55\r\n\tmul edx\r\n\tadd eax, ecx\t;step 3\r\n\tadc edx, 0\r\n\tret\r\ngettimer endp\r\n"
  },
  {
    "path": "Test/VCPI.INC",
    "content": "\r\ndesc struct\r\nlimit   dw ?\r\nA0015   dw ?\r\nA1623   db ?\r\nattrib  db ?\r\nlim_gr  db ?\r\nA2431   db ?\r\ndesc ends\r\n\r\n;--- VCPI structure for switch to protected mode\r\n\r\nV86toPM struct\r\n_CR3    dd ?\t;CR3\r\n_Gdtr   dd ?\t;linear address PD for GDTR\r\n_Idtr   dd ?\t;linear address PD for IDTR\r\n_Ldtr   dw ?\t;LDTR\r\n_Tr     dw ?\t;TR\r\n_Eip\tdd ?\t;EIP\r\n_Cs     dd ?\t;CS\r\nV86toPM ends\r\n\r\nIRETV86 struct\r\n_Eip    dd ?\r\n_Cs     dd ?\r\n_Efl    dd ?\r\n_Esp    dd ?\r\n_Ss     dd ?\r\n_Es     dd ?\r\n_Ds     dd ?\r\n_Fs     dd ?\r\n_Gs     dd ?\r\nIRETV86 ends\r\n\r\nTSSSEG struct\r\ndwLink\tdd ?\r\ndfStk0\tdf ?\t;+04\r\n\t\tdw ?\r\ndfStk1\tdf ?\t;+0C\r\n\t\tdw ?\r\ndfStk2\tdf ?\t;+14\r\n\t\tdw ?\r\n_CR3\tdd ?\t;+1C\r\n_Eip\tdd ?\t;+20\r\n_Efl\tdd ?\t;+24\r\n_Eax\tdd ?\t;+28\r\n_Ecx\tdd ?\t;+2C\r\n_Edx\tdd ?\t;+30\r\n_Ebx\tdd ?\t;+34\r\n_Esp\tdd ?\t;+38\r\n_Ebp\tdd ?\t;+3C\r\n_Esi\tdd ?\t;+40\r\n_Edi\tdd ?\t;+44\r\n_ES\t\tdd ?\t;+48\r\n_CS\t\tdd ?\t;+4C\r\n_SS\t\tdd ?\t;+50\r\n_DS\t\tdd ?\t;+54\r\n_FS\t\tdd ?\t;+58\r\n_GS\t\tdd ?\t;+5C\r\n_LDT\tdd ?\t;+60\r\nwFlags  dw ?\t;+64\r\nwOffs   dw ?\t;+66\r\nTSSSEG ends\r\n\r\n"
  },
  {
    "path": "Test/XMSTEST.ASM",
    "content": "\r\n;--- XMSTEST: test xms memory moves\r\n;--- Public Domain.\r\n;--- to be assembled with JWasm or Masm v6.\r\n\r\n\t.model tiny\r\n\t.386\r\n\r\n\t.dosseg\r\n\t.stack 2048\r\n\r\nlf  equ 10\r\n\r\n;--- XMS handle table\r\n\r\nXMSHT struct\r\n        db ?\r\nbSize   db ?\r\nwHdls   dw ?\r\ndwArray dd ?\r\nXMSHT ends\r\n\r\n;--- XMS handle\r\n\r\nXMSH struct\r\nbFlags db ? ;flags, see below \r\nbLocks db ? ;number of locks\r\ndwAddr dd ? ;addr in KB\r\ndwSize dd ? ;size in KB\r\nXMSH ends\r\n\r\nXMSF_FREEB  equ 1   ;free block\r\nXMSF_USEDB  equ 2   ;used block\r\nXMSF_FREEH  equ 4   ;free handle\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\n\t.const\r\nxxx db string\r\n\tdb 0\r\n\t.code\r\n\texitm <offset xxx>\r\nendm\r\n\r\n;--- display word decimal\r\n\r\n@wordout_d macro number,format\r\n\tmov cl,format\r\n\tifidni <number>,<ax>\r\n\telse\r\n\t  mov ax,number\r\n\tendif\r\n\tcall _wordout_d\r\n\tendm\r\n\r\n;--- display dword decimal\r\n\r\n@dwordout_d macro number,format\r\n\tifnb <number>\r\n\t\tmov ax,word ptr number+0\r\n\t\tmov dx,word ptr number+2\r\n\tendif\r\n\tmov cl, format\r\n\tcall _dwordout_d\r\n\tendm\r\n\r\n\t.data\r\n\r\nxmsadr    dd 0      ;XMS host call address\r\ndwTotal   dd 0      ;total size EMBs\r\nfreehdls  dw 0      ;count free handles\r\nwVersion  dw 0      ;XMS version\r\nbFlags    db 0      ;flags\r\n\r\nFL_NOSIZENULL   equ 1\r\nFL_NOUSEDEMBS   equ 2\r\nFL_NOFREEEMBS   equ 4\r\nFL_FREEHANDLES  equ 8\r\n\r\n\t.code\r\n\r\n\tassume DS:DGROUP\r\n\r\n\tinclude printf.inc\r\n\r\nprotocol proc\r\n\tpush bx\r\n\tinvoke printf, CStr(\"%s\"), dx\r\n\tpop ax\r\n\tcmp al,0\r\n\tjz @F\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\", failed, bl=%02X\",lf), ax\r\n\tret\r\n@@:\r\n\tinvoke printf, CStr(\", ok\",lf)\r\n\tret\r\nprotocol endp\r\n\r\nxms_move struct \r\n  len           dd  ?       ; block length in bytes\r\n  src_handle    dw  ?       ; source handle\r\n  src_offset    dd  ?       ; offset into source\r\n  dest_handle   dw  ?       ; destination handle\r\n  dest_offset   dd  ?       ; offset into destination\r\nxms_move ends \r\n\r\n;--- test xms move block function\r\n\r\nmovetest proc\r\n\r\nlocal handle:word\r\nlocal emm:xms_move\r\n\r\n\tmov ah,9\r\n\tmov dx,64\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tcmp bl,0\r\n\tjnz failed\r\n\tmov handle, dx\r\n\r\n;--- test 1\r\n\r\n\tmov dx,handle\r\n\tmov emm.len, 10000\r\n\tmov emm.src_handle, dx\r\n\tmov emm.src_offset, 0\r\n\tmov emm.dest_handle, dx\r\n\tmov emm.dest_offset, 10000\r\n\tlea si, emm\r\n\tmov ah,0bh\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tmov dx, CStr(\"XMS block move, src < dest && src+len < dest\")\r\n\tcall protocol\r\n\r\n;--- test 2\r\n\r\n\tmov dx,handle\r\n\tmov emm.len, 20000\r\n\tmov emm.src_handle, dx\r\n\tmov emm.src_offset, 0\r\n\tmov emm.dest_handle, dx\r\n\tmov emm.dest_offset, 10000\r\n\tlea si, emm\r\n\tmov ah,0bh\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tmov dx, CStr(\"XMS block move, src < dest && src+len > dest\")\r\n\tcall protocol\r\n\r\n;--- test 3\r\n\r\n\tmov dx,handle\r\n\tmov emm.len, 10000\r\n\tmov emm.src_handle, dx\r\n\tmov emm.src_offset, 10000\r\n\tmov emm.dest_handle, dx\r\n\tmov emm.dest_offset, 0\r\n\tlea si, emm\r\n\tmov ah,0bh\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tmov dx, CStr(\"XMS block move, src > dest && src > dest+len\")\r\n\tcall protocol\r\n\r\n;--- test 4\r\n\r\n\tmov dx,handle\r\n\tmov emm.len, 20000\r\n\tmov emm.src_handle, dx\r\n\tmov emm.src_offset, 10000\r\n\tmov emm.dest_handle, dx\r\n\tmov emm.dest_offset, 0\r\n\tlea si, emm\r\n\tmov ah,0bh\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tmov dx, CStr(\"XMS block move, src > dest && src < dest+len\")\r\n\tcall protocol\r\n\r\n\tmov dx,handle\r\n\tmov ah,0ah\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tret\r\nfailed:\r\n\tinvoke printf, CStr(\"XMS call AH=08 failed\",lf)\r\n\tret\r\nmovetest endp\r\n\r\n;--- main\r\n\r\nmain proc c\r\n\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\ttest al,80h \t\t ;xms host found?\r\n\tjnz main1\r\n\tinvoke printf, CStr(\"no XMS host found\",lf)\r\n\tjmp exit\r\nmain1:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov word ptr xmsadr+0,bx\r\n\tmov word ptr xmsadr+2,es\r\n\tinvoke printf, CStr(\"XMS call address: %X:%X\",lf), word ptr [xmsadr+2], word ptr [xmsadr+0]\r\n\r\n\tcall movetest\r\n\r\nexit:\r\n\tret\r\nmain endp\r\n\r\n;--- init\r\n\r\nstart proc\r\n\r\n\tpush cs\r\n\tpop ds\r\n\r\n\tmov cx,es\r\n\tmov ax,ss\r\n\tsub ax,cx\r\n\tshl ax,4\r\n\tadd ax,sp\r\n\tpush cs\r\n\tpop ss\r\n\tmov sp,ax\r\n\r\n\tpushf\r\n\tpushf\r\n\tpop ax\r\n\tor ah,70h\t\t;a 80386 will have bit 15 cleared\r\n\tpush ax \t\t;if bits 12-14 are 0, it is a 80286\r\n\tpopf\t\t\t;or a bad emulation\r\n\tpushf\r\n\tpop ax\r\n\tpopf\r\n\tand ah,0f0h\r\n\tjs no386\t\t;bit 15 set? then its a 8086/80186\r\n\tjnz is386\r\nno386:\r\n\tinvoke printf, CStr(\"a 80386 is needed\",lf)\r\n\tjmp done\r\nis386:\r\n\tcall main\r\ndone:\r\n\tmov ah,4Ch\r\n\tint 21h\r\nstart endp\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/XMSTEST2.ASM",
    "content": "\r\n;--- XMSTEST2: test max block allocation\r\n;--- Public Domain.\r\n;--- to be assembled with JWasm or Masm v6.\r\n\r\n\t.model tiny\r\n\t.dosseg\r\n\t.stack 2048\r\n\t.386\r\n\r\nlf  equ 10\r\n\r\nBUFFSIZ equ 10000h\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\n\t.const\r\nxxx db string\r\n\tdb 0\r\n\t.code\r\n\texitm <offset xxx>\r\nendm\r\n\r\nxms_move struct\r\n  len           dd  ?       ; block length in bytes\r\n  src_handle    dw  ?       ; source handle\r\n  src_offset    dd  ?       ; offset into source\r\n  dest_handle   dw  ?       ; destination handle\r\n  dest_offset   dd  ?       ; offset into destination\r\nxms_move ends\r\n\r\n\t.data\r\n\r\nxmsadr dd 0      ;XMS host call address\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\n;--- test xms move block function\r\n\r\nmovetest proc\r\n\r\nlocal handle:word\r\nlocal maxmem:dword\r\nlocal buffer:word\r\nlocal emm:xms_move\r\n\r\n;--- allocate DOS memory block\r\n\tmov ah,48h\r\n\tmov bx,BUFFSIZ / 16\r\n\tint 21h\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"not enough DOS memory\",lf)\r\n\tjmp failed\r\n@@:\r\n\tmov buffer, ax\r\n\r\n;--- allocate XMS block\r\n\tmov ah,88h\t;query free xms mem\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tmov maxmem, eax\r\n\tmov bh,0\r\n\tpush bx\r\n\tinvoke printf, CStr(\"xms function 88h returned bl=%X, eax=%lX\",lf), bx, eax\r\n\tpop bx\r\n\tcmp bl,0\r\n\tjnz failed\r\n\r\n\tmov edx, maxmem\r\n\tmov ah,89h\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tmov handle, dx\r\n\tmov bh,0\r\n\tpush bx\r\n\tinvoke printf, CStr(\"xms function 89h returned bl=%X, dx=%X\",lf), bx, dx\r\n\tpop bx\r\n\tcmp bl,0\r\n\tjnz failed\r\n\r\n;--- test 1\r\n\r\n\tcld\r\n\tmov es, buffer\r\n\tmov di, 0\r\n\tmov cx, BUFFSIZ / 4\r\n\tmov eax, 0deadbabeh\r\n\trep stosd\r\n\r\n\tmov emm.len, BUFFSIZ\r\n\tmov emm.src_handle, 0\r\n\tmov word ptr emm.src_offset+0, 0\r\n\tmov word ptr emm.src_offset+2, es\r\n\tmov ax, handle\r\n\tmov emm.dest_handle, ax\r\n\tmov emm.dest_offset, 0\r\n\tmov edi, maxmem\r\n\tshl edi, 10\t\t;kB -> byte\r\n\t.while edi\r\n\t\tmov dx,handle\r\n\t\tlea si, emm\t;ds:si->xms move struct\r\n\t\tmov ah,0bh\r\n\t\tmov bl,0\r\n\t\tcall [xmsadr]\r\n\t\tpush ax\r\n\t\tinvoke printf, CStr(\"XMS block move, ax=%u, dest ofs=%lX\",13), ax, emm.dest_offset\r\n\t\tpop ax\r\n\t\t.break .if ax == 0\r\n\t\tadd emm.dest_offset, BUFFSIZ\r\n\t\t.if edi > BUFFSIZ\r\n\t\t\tsub edi, BUFFSIZ\r\n\t\t.else\r\n\t\t\tmov emm.len, edi\r\n\t\t\txor edi, edi\r\n\t\t.endif\r\n\t.endw\r\n\tinvoke printf, CStr(10)\r\n\r\n;--- free xms handle\r\n\tmov dx,handle\r\n\tmov ah,0ah\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\nfailed:\r\n\tret\r\nmovetest endp\r\n\r\n;--- main\r\n\r\nmain proc c\r\n\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\ttest al,80h \t\t ;xms host found?\r\n\tjnz main1\r\n\tinvoke printf, CStr(\"no XMS host found\",lf)\r\n\tjmp exit\r\nmain1:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov word ptr xmsadr+0,bx\r\n\tmov word ptr xmsadr+2,es\r\n\tinvoke printf, CStr(\"XMS call address: %X:%X\",lf),\r\n\t\tword ptr [xmsadr+2], word ptr [xmsadr+0]\r\n\r\n\tcall movetest\r\n\r\nexit:\r\n\tret\r\nmain endp\r\n\r\n;--- init\r\n\r\nstart proc\r\n\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\r\n\tmov cx,ds\r\n\tmov ax,ss\r\n\tsub ax,cx\r\n\tshl ax,4\r\n\tadd ax,sp\r\n\tpush ds\r\n\tpop ss\r\n\tmov sp,ax\r\n\r\n;--- free DOS mem\r\n\tmov ax, ds\r\n\tmov cx, es\r\n\tsub ax, cx\r\n\tmov bx, sp\r\n\tadd bx, 15\r\n\tshr bx, 4\r\n\tadd bx, ax\r\n\tmov ah, 4Ah\r\n\tint 21h\r\n\r\n\tpushf\r\n\tpushf\r\n\tpop ax\r\n\tor\tah,70h\t\t\t;a 80386 will have bit 15 cleared\r\n\tpush ax \t\t\t;if bits 12-14 are 0, it is a 80286\r\n\tpopf\t\t\t\t;or a bad emulation\r\n\tpushf\r\n\tpop ax\r\n\tpopf\r\n\tand ah,0f0h\r\n\tjs no386\t\t\t;bit 15 set? then its a 8086/80186\r\n\tjnz is386\r\nno386:\r\n\tinvoke printf, CStr(\"a 80386 is needed\",lf)\r\n\tjmp done\r\nis386:\r\n\tcall main\r\ndone:\r\n\tmov ah,4Ch\r\n\tint 21h\r\nstart endp\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/XMSTEST3.ASM",
    "content": "\r\n;--- test ah=0Eh (get handle info)\r\n;--- Public Domain.\r\n;--- to be assembled with JWasm or Masm v6.\r\n\r\n\t.model tiny\r\n\t.dosseg\r\n\t.stack 2048\r\n\t.386\r\n\r\ncr  equ 13\r\nlf  equ 10\r\n\r\nBUFFSIZ equ 10000h\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\n\t.const\r\nxxx db string\r\n\tdb 0\r\n\t.code\r\n\texitm <offset xxx>\r\nendm\r\n\r\n\t.data\r\n\r\nxmsadr dd 0      ;XMS host call address\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\nruntest proc\r\n\r\nlocal handle:word\r\nlocal dwSize:dword\r\n\r\n\tmov edx,10000h   ;allocate 65536 kB\r\n\tcall runtest1\r\n\tmov edx,0ffffh   ;allocate 65535 kB\r\n\tcall runtest1\r\n\tret\r\nruntest1:\r\n\tmov dwSize,edx\r\n\tmov ah,89h       ;alloc ext. memory block (size EDX)\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tcmp ax,1\r\n\tjz @F\r\n\tinvoke printf, CStr(\"xms function 89h failed (bl=%X, edx=%lX)\",lf), bl, edx\r\n\tjmp failed\r\n@@:\r\n\tmov handle,dx\r\n\tinvoke printf, CStr(\"xms function 89h(size %lu kB): dx=%X\",lf), dwSize, dx\r\n\r\n;--- function 0Eh should fail, since size doesn't fit in 16-bit register\r\n\tmov edx,12340000h\t;set hiword(edx) to a known value\r\n\tmov dx,handle\r\n\tmov bx,0            ;set bx to a known value\r\n\tmov ah,0eh          ;get handle info (v2)\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"xms function 0Eh: ax=%X, bx=%X, edx=%lX\",lf), ax, bx, edx\r\n\r\n;--- function 8Eh should succeed\r\n\tmov edx,12340000h\t;set hiword(edx) to a known value\r\n\tmov dx,handle\r\n\tmov bx,0            ;set bx to a known value\r\n\tmov ah,8eh          ;get handle info (v3)\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"xms function 8Eh: ax=%X, bx=%X, edx=%lX\",lf), ax, bx, edx\r\n\r\n\tmov dx,handle\r\n\tmov ah,0ah          ;free handle\r\n\tcall [xmsadr]\r\nfailed:\r\n\tretn\r\nruntest endp\r\n\r\n;--- main\r\n\r\nmain proc c\r\n\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\ttest al,80h \t\t ;xms host found?\r\n\tjnz main1\r\n\tinvoke printf, CStr(\"no XMS host found\",lf)\r\n\tjmp exit\r\nmain1:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov word ptr xmsadr+0,bx\r\n\tmov word ptr xmsadr+2,es\r\n;\tinvoke printf, CStr(\"XMS call address: %X:%X\",lf),word ptr [xmsadr+2],word ptr [xmsadr+0]\r\n\tcall runtest\r\nexit:\r\n\tret\r\nmain endp\r\n\r\n;--- init\r\n\r\nstart proc\r\n\r\n\tmov ax,cs\r\n\tmov ds,ax\r\n\tmov cx,ss\r\n\tsub cx,ax\r\n\tshl cx,4\r\n\tmov ss,ax\r\n\tadd sp,cx\r\n\tcall main\r\n\tmov ah,4Ch\r\n\tint 21h\r\nstart endp\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/XMSTEST4.ASM",
    "content": "\r\n;--- XMSTEST4: test block resize (shrink)\r\n;--- Public Domain.\r\n;--- to be assembled with JWasm or Masm v6.\r\n\r\n\t.model small\r\n\t.386\r\n\t.dosseg\r\n\t.stack 2048\r\n\r\ncr  equ 13\r\nlf  equ 10\r\n\r\nBUFFSIZ equ 10000h\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\n\t.const\r\nxxx db string\r\n\tdb 0\r\n\t.code\r\n\texitm <offset xxx>\r\nendm\r\n\r\n\t.data\r\n\r\nxmsadr dd 0      ;XMS host call address\r\n\r\n\t.code\r\n\r\n\tassume DS:DGROUP\r\n\r\n\tinclude printf.inc\r\n\r\n;--- test xms block resize function\r\n\r\ntestproc proc\r\n\r\nlocal handle:word\r\n\r\n;--- allocate XMS block\r\n\r\n\tmov edx, 10000h\t;64MB\r\n\tmov ah,89h\r\n\tcall [xmsadr]\r\n\tmov handle, dx\r\n\tpush ax\r\n\tinvoke printf, CStr(\"xms alloc (ah=89h) of 64MB returned ax=%X, dx=%X\",lf), ax, dx\r\n\tpop ax\r\n\tcmp ax,1\r\n\tjnz failed\r\n\r\n;--- lock XMS block\r\n\r\n\tmov dx, handle\r\n\tmov ah,0Ch\r\n\tcall [xmsadr]\r\n\tpush ax\r\n        invoke printf, CStr(\"xms lock (ah=0Ch) returned ax=%X, dx:bx=%X%04X\",lf), ax, dx, bx\r\n\tpop ax\r\n\tcmp ax,1\r\n\tjnz failed2\r\n\r\n;--- resize block; should fail since locked\r\n\r\n\tmov ebx, 8000h\t;shrink block to 32MB\r\n\tmov dx, handle\r\n\tmov ah,8Fh\r\n\tcall [xmsadr]\r\n\tmov bh,0\r\n\tinvoke printf, CStr(\"xms resize (ah=8Fh) to 32MB returned ax=%X, bl=%X\",lf), ax, bx\r\n\r\n;--- unlock XMS block\r\n\r\n\tmov dx, handle\r\n\tmov ah,0Dh\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"xms unlock (ah=0Dh) returned ax=%X\",lf), ax\r\n\r\nfailed2:\r\n;--- free xms handle\r\n\tmov dx,handle\r\n\tmov ah,0ah\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"xms release (ah=0Ah) returned ax=%X\",lf), ax\r\nfailed:\r\n\tret\r\ntestproc endp\r\n\r\n;--- main\r\n\r\nmain    proc c\r\n\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\ttest al,80h \t\t ;xms host found?\r\n\tjnz main1\r\n\tinvoke printf, CStr(\"no XMS host found\",lf)\r\n\tjmp exit\r\nmain1:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov word ptr xmsadr+0,bx\r\n\tmov word ptr xmsadr+2,es\r\n\tinvoke printf, CStr(\"XMS call address: %X:%X\",lf),\r\n\t\tword ptr [xmsadr+2], word ptr [xmsadr+0]\r\n\r\n\tcall testproc\r\n\r\nexit:\r\n\tret\r\nmain    endp\r\n\r\n;--- init\r\n\r\nstart   proc\r\n\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\r\n\tmov cx,ds\r\n\tmov ax,ss\r\n\tsub ax,cx\r\n\tshl ax,4\r\n\tadd ax,sp\r\n\tpush ds\r\n\tpop ss\r\n\tmov sp,ax\r\n\r\n;--- free DOS mem\r\n\tmov ax, ds\r\n\tmov cx, es\r\n\tsub ax, cx\r\n\tmov bx, sp\r\n\tadd bx, 15\r\n\tshr bx, 4\r\n\tadd bx, ax\r\n\tmov ah, 4Ah\r\n\tint 21h\r\n\r\n\tpushf\r\n\tpushf\r\n\tpop ax\r\n\tor\tah,70h\t\t\t;a 80386 will have bit 15 cleared\r\n\tpush ax \t\t\t;if bits 12-14 are 0, it is a 80286\r\n\tpopf\t\t\t\t;or a bad emulation\r\n\tpushf\r\n\tpop ax\r\n\tpopf\r\n\tand ah,0f0h\r\n\tjs no386\t\t\t;bit 15 set? then its a 8086/80186\r\n\tjnz is386\r\nno386:\r\n\tinvoke printf, CStr(\"a 80386 is needed\",lf)\r\n\tjmp done\r\nis386:\r\n\tcall main\r\ndone:\r\n\tmov ah,4Ch\r\n\tint 21h\r\nstart   endp\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Test/XMSTEST5.ASM",
    "content": "\r\n;--- XMSTEST5: test block resize (shrink)\r\n;--- this variant allocates a 64MB block with xms v3,\r\n;--- but tries to resize if with xms v2 to 32MB.\r\n;--- Public Domain.\r\n;--- to be assembled with JWasm or Masm v6.\r\n\r\n\t.model small\r\n\t.386\r\n\t.dosseg\r\n\t.stack 2048\r\n\r\ncr  equ 13\r\nlf  equ 10\r\n\r\nBUFFSIZ equ 10000h\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\n\t.const\r\nxxx db string\r\n\tdb 0\r\n\t.code\r\n\texitm <offset xxx>\r\nendm\r\n\r\n\t.data\r\n\r\nxmsadr dd 0      ;XMS host call address\r\n\r\n\t.code\r\n\r\n\tassume DS:DGROUP\r\n\r\n\tinclude printf.inc\r\n\r\n;--- test xms block resize function\r\n\r\ntestproc proc\r\n\r\nlocal handle:word\r\nlocal wRC:word\r\n\r\n;--- allocate XMS block\r\n\r\n\tmov edx, 10000h\t;64MB\r\n\tmov ah,89h\r\n\tcall [xmsadr]\r\n\tmov handle, dx\r\n\tpush ax\r\n\tinvoke printf, CStr(\"xms alloc (ah=89h) of 64MB returned ax=%X, dx=%X\",lf), ax, dx\r\n\tpop ax\r\n\tcmp ax,1\r\n\tjnz failed\r\n\r\n;--- lock XMS block\r\n\r\n\tmov dx, handle\r\n\tmov ah,0Ch\r\n\tcall [xmsadr]\r\n\tpush ax\r\n        invoke printf, CStr(\"xms lock (ah=0Ch) returned ax=%X, dx:bx=%X%04X\",lf), ax, dx, bx\r\n\tpop ax\r\n\tcmp ax,1\r\n\tjnz failed2\r\n\r\n;--- resize block; should fail since locked\r\n\r\n        mov bx, 8000h  ;shrink block to 32MB\r\n\tmov dx, handle\r\n        mov ah,0Fh\r\n\tcall [xmsadr]\r\n        mov wRC, ax\r\n\tmov bh,0\r\n        invoke printf, CStr(\"xms resize (ah=0Fh) to 32MB returned ax=%X, bl=%X\",lf), ax, bx\r\n\r\n;--- unlock XMS block\r\n\r\n\tmov dx, handle\r\n\tmov ah,0Dh\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"xms unlock (ah=0Dh) returned ax=%X\",lf), ax\r\n\r\n        cmp wRC,1\r\n        jz failed2\r\n\r\n;--- try again to resize, this time with an unlocked block\r\n\r\n        mov bx, 8000h  ;shrink block to 32MB\r\n\tmov dx, handle\r\n        mov ah,0Fh\r\n\tcall [xmsadr]\r\n        mov wRC, ax\r\n\tmov bh,0\r\n        invoke printf, CStr(\"xms resize (ah=0Fh) to 32MB returned ax=%X, bl=%X\",lf), ax, bx\r\n\r\nfailed2:\r\n;--- free xms handle\r\n\tmov dx,handle\r\n\tmov ah,0ah\r\n\tmov bl,0\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"xms release (ah=0Ah) returned ax=%X\",lf), ax\r\nfailed:\r\n\tret\r\ntestproc endp\r\n\r\n;--- main\r\n\r\nmain    proc c\r\n\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\ttest al,80h \t\t ;xms host found?\r\n\tjnz main1\r\n\tinvoke printf, CStr(\"no XMS host found\",lf)\r\n\tjmp exit\r\nmain1:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov word ptr xmsadr+0,bx\r\n\tmov word ptr xmsadr+2,es\r\n\tinvoke printf, CStr(\"XMS call address: %X:%X\",lf),\r\n\t\tword ptr [xmsadr+2], word ptr [xmsadr+0]\r\n\r\n\tcall testproc\r\n\r\nexit:\r\n\tret\r\nmain    endp\r\n\r\n;--- init\r\n\r\nstart   proc\r\n\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\r\n\tmov cx,ds\r\n\tmov ax,ss\r\n\tsub ax,cx\r\n\tshl ax,4\r\n\tadd ax,sp\r\n\tpush ds\r\n\tpop ss\r\n\tmov sp,ax\r\n\r\n;--- free DOS mem\r\n\tmov ax, ds\r\n\tmov cx, es\r\n\tsub ax, cx\r\n\tmov bx, sp\r\n\tadd bx, 15\r\n\tshr bx, 4\r\n\tadd bx, ax\r\n\tmov ah, 4Ah\r\n\tint 21h\r\n\r\n\tpushf\r\n\tpushf\r\n\tpop ax\r\n\tor\tah,70h\t\t\t;a 80386 will have bit 15 cleared\r\n\tpush ax \t\t\t;if bits 12-14 are 0, it is a 80286\r\n\tpopf\t\t\t\t;or a bad emulation\r\n\tpushf\r\n\tpop ax\r\n\tpopf\r\n\tand ah,0f0h\r\n\tjs no386\t\t\t;bit 15 set? then its a 8086/80186\r\n\tjnz is386\r\nno386:\r\n\tinvoke printf, CStr(\"a 80386 is needed\",lf)\r\n\tjmp done\r\nis386:\r\n\tcall main\r\ndone:\r\n\tmov ah,4Ch\r\n\tint 21h\r\nstart   endp\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Tools/CLEAR.BAT",
    "content": "@echo off\r\nerase CPUID\\*.EXE\r\nerase CPUID\\*.lst\r\nerase CpuStat\\*.EXE\r\nerase CpuStat\\*.lst\r\nerase EMSSTAT\\*.EXE\r\nerase EMSSTAT\\*.lst\r\nerase JEMFBHLP\\*.EXE\r\nerase JEMFBHLP\\*.lst\r\ncd JLOAD\r\nnmake clean\r\ncd ..\r\nerase MEMSTAT\\*.EXE\r\nerase MEMSTAT\\*.lst\r\nerase MoveXBDA\\*.EXE\r\nerase MoveXBDA\\*.lst\r\nerase UMBM\\*.EXE\r\nerase UMBM\\*.lst\r\nerase VCPI\\*.EXE\r\nerase VCPI\\*.lst\r\nerase XMSSTAT\\*.EXE\r\nerase XMSSTAT\\*.lst\r\n"
  },
  {
    "path": "Tools/CPUID/CPUID.ASM",
    "content": "\r\n;--- CPUID displays status of CPU. \r\n;--- Public Domain.\r\n;--- Masm syntax. To be assembled with JWasm or Masm.\r\n;--- uses 16-bit printf\r\n\r\n\t.286 \r\n\t.model small\r\n\t.dosseg\r\n\t.stack 400h\r\n\r\nbs\tequ 8\r\nlf\tequ 10\r\n\r\nprintf proto c :ptr BYTE, :VARARG\r\n\r\nCStr macro y:VARARG\r\nlocal sym\r\n\t.const\r\nsym db y,0\r\n\t.code\r\n\texitm <offset sym>\r\n\tendm\r\n\r\nDStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.data\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n\r\ncpubrand label dword\r\ndwCPUIDeax dd 0\r\ndwCPUIDebx dd 0\r\ndwCPUIDedx dd 0\r\ndwCPUIDecx dd 0\r\n\t\tdb 0\r\nbCpu\tdb 0\r\n\r\n;--- cpuid.1 EDX features\r\n;--- note: bit 10,20,30 not assigned!\r\n\r\ncpuid1_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\r\ncpuid1_edx_str label word\r\n\tdw DStr(\"FPU integrated\",lf)\r\n\tdw DStr(\"VME - Virtual-8086 Mode Enhancement [CR4 VME/PVI & EFL VIF/VIP]\",lf)\r\n\tdw DStr(\"DE - Debugging Extensions [I/O breakpoints & CR4 DE]\",lf)\r\n\tdw DStr(\"PSE - Page Size Extensions [4 MB page size & CR4 PSE]\",lf)\r\n\tdw DStr(\"TSC - Time Stamp Counter [RDTSC & CR4 TSD]\",lf)\r\n\tdw DStr(\"MSR - RDMSR & WRMSR support\",lf)\r\n\tdw DStr(\"PAE - Physical Address Extensions [CR4 PAE]\",lf)\r\n\tdw DStr(\"MCE - Machine Check Exceptions [CR4 MCE]\",lf)\r\n\tdw DStr(\"CX8 - CMPXCHG8B\",lf)\r\n\tdw DStr(\"APIC - on chip APIC exists and enabled\",lf)\r\n\tdw DStr(\"SEP - SYSENTER & SYSEXIT\",lf)\r\n\tdw DStr(\"MTRR -  Memory Type Range Registers\",lf)\r\n\tdw DStr(\"PGE - PTE Global Bit [CR4 PGE]\",lf)\r\n\tdw DStr(\"MCA - Machine Check Architecture\",lf)\r\n\tdw DStr(\"CMOV & FCMOV/FCOM1\",lf)\r\n\tdw DStr(\"PAT - Page Attribute Table\",lf)\r\n\tdw DStr(\"PSE-36 - Page Size Extension\",lf)\r\n\tdw DStr(\"CFLSH - CFLUSH instruction\",lf)\r\n\tdw DStr(\"DS - Debug Store\",lf)\r\n\tdw DStr(\"ACPI - Thermal Monitor & Clock Control\",lf)\r\n\tdw DStr(\"MMX -  64-bit MultiMedia Extensions\",lf)\r\n\tdw DStr(\"FXSAVE/FXRSTOR\",lf)\r\n\tdw DStr(\"SSE\",lf)\r\n\tdw DStr(\"SSE2\",lf)\r\n\tdw DStr(\"HTT - Multi Threading\",lf)\r\n\tdw DStr(\"TM - Thermal Monitoring\",lf)\r\n\tdw DStr(\"PBE - Pending Brk Enable\",lf)\r\n\r\ncpuid1_ecx_bits db 0,1,2,3,5,7,8,9,13,19,20,21,23,26,28,30\r\ncpuid1_ecx_str label word\r\n\tdw DStr(\"SSE3\",lf)\r\n\tdw DStr(\"PCLMULQDQ - Carryless Multiplication\",lf)\r\n\tdw DStr(\"DTES64 - 64 bit DS Area\",lf)\r\n\tdw DStr(\"MONITOR - MONITOR/MWAIT\",lf)\r\n\tdw DStr(\"VMX - Virtual Machine Extensions\",lf)\r\n\tdw DStr(\"EIST - Intel SpeedStep\",lf)\r\n\tdw DStr(\"TM2 - Thermal Monitor 2\",lf)\r\n\tdw DStr(\"SSSE3\",lf)\r\n\tdw DStr(\"CMPXCHG16B\",lf)\r\n\tdw DStr(\"SSE4.1\",lf)\r\n\tdw DStr(\"SSE4.2\",lf)\r\n\tdw DStr(\"x2APIC\",lf)\r\n\tdw DStr(\"POPCNT\",lf)\r\n\tdw DStr(\"XSAVE\",lf)\r\n\tdw DStr(\"AVX - Advanced Vector Extensions\",lf)\r\n\tdw DStr(\"RDRAND\",lf)\r\n\r\nFEATS struct\r\npFeat  dw ?\t;ptr DWORD\r\npBitNo dw ?\t;ptr BYTE\r\npStr   dw ?\t;ptr NEAR16 string ptr\r\n_size  dw ?\t;size of pBitNo, pStr table\r\nFEATS ends\r\n\r\ncpuid1m label word\r\n\tdw DStr(\"CPUID 1 features (EDX,ECX):\",lf)\r\n\tFEATS <offset dwCPUIDedx,offset cpuid1_edx_bits,offset cpuid1_edx_str,sizeof cpuid1_edx_bits>\r\n\tFEATS <offset dwCPUIDecx,offset cpuid1_ecx_bits,offset cpuid1_ecx_str,sizeof cpuid1_ecx_bits>\r\n\r\ncpuid1x_edx_bits db 11,22,26,29,31\r\ncpuid1x_edx_str label word\r\n\tdw DStr(\"SYSCALL & SYSRET\",lf)\r\n\tdw DStr(\"MMX Extensions\",lf)\r\n\tdw DStr(\"1 GB pages\",lf)\r\n\tdw DStr(\"long mode (cpu is 64-bit)\",lf)\r\n\tdw DStr(\"3DNow\",lf)\r\n\r\ncpuid1x_ecx_bits db 0,2,6,21\r\ncpuid1x_ecx_str label word\r\n\tdw DStr(\"LahfSahf -  LAHF/SAHF supported in 64-bit\",lf)\r\n\tdw DStr(\"SVM -  Secure Virtual Machine\",lf)\r\n\tdw DStr(\"SSE4A\",lf)\r\n\tdw DStr(\"TBM - Trailing Bit Manipulation\",lf)\r\n\r\ncpuid1xm label word\r\n\tdw DStr(\"CPUID 80000001 features (EDX,ECX):\",lf)\r\n\tFEATS <offset dwCPUIDedx,offset cpuid1x_edx_bits,offset cpuid1x_edx_str,sizeof cpuid1x_edx_bits>\r\n\tFEATS <offset dwCPUIDecx,offset cpuid1x_ecx_bits,offset cpuid1x_ecx_str,sizeof cpuid1x_ecx_bits>\r\n\r\ncpuid7_ebx_bits db 0,1,2,3,4,5,7,8,18\r\ncpuid7_ebx_str label word\r\n\tdw DStr(\"access to base of FS and GS\",lf)\r\n\tdw DStr(\"IA32_TSC_ADJUST\",lf)\r\n\tdw DStr(\"SGX - Software Guard Extensions\",lf)\r\n\tdw DStr(\"BMI1 - Bit Manipulation Instruction Set 1\",lf)\r\n\tdw DStr(\"TSX Hardware Lock Elision\",lf)\r\n\tdw DStr(\"AVX2 - Advanced Vector Extensions 2\",lf)\r\n\tdw DStr(\"SMEP - Supervisor Mode Execution Prevention\",lf)\r\n\tdw DStr(\"BMI2 - Bit Manipulation Instruction Set 2\",lf)\r\n\tdw DStr(\"RDSEED - RDSEED supported\",lf)\r\n\r\ncpuid7m label word\r\n\tdw DStr(\"CPUID 7 features (EBX):\",lf)\r\n\tFEATS <offset dwCPUIDebx,offset cpuid7_ebx_bits,offset cpuid7_ebx_str,sizeof cpuid7_ebx_bits>\r\n\r\n\t.code\r\n\r\n\t.586\r\n\r\n\tinclude printf.inc\r\n\r\nhascpuid proc\r\n\tpush di\r\n\tmov di,sp\r\n\tand sp,0fffch\t;make sure we don't get an exc 11 (if AM set in CR0)\r\n\tpushfd\t\t\t\t\t\t; save EFlags\r\n\tcli\r\n\tpushd 240000h\t\t\t\t; set AC bit in eflags, reset IF\r\n\tpopfd\t\t\t\t\t\t; pop extended flags\r\n\tpushfd\t\t\t\t\t\t; push extended flags\r\n\tpop ax\r\n\tpop ax\t\t\t\t\t\t; get HiWord(EFlags) into AX\r\n\tpopfd\t\t\t\t\t\t; restore EFlags\r\n\ttest al,04\t;AC bit set?\r\n\tje @F\r\n\ttest al,20h\t;CPUID bit set?\r\n\tjz @F\r\n\tmov sp,di\r\n\tpop di\r\n\tclc\r\n\tret\r\n@@:\r\n\tmov sp,di\r\n\tpop di\r\n\tstc\r\n\tret\r\nhascpuid endp\r\n\r\n;--- print features\r\n\r\nprint_cpuid_regs proc stdcall uses ebx si di items:word, pp:ptr\r\n\r\n\tmov si, pp\r\n\tlodsw\r\n\tinvoke printf, ax\r\nnextreg:\r\n\tpush si\r\n\tmov bx,[si].FEATS.pFeat\r\n\tmov di,[si].FEATS.pStr\r\n\tmov cx,[si].FEATS._size\r\n\tmov si,[si].FEATS.pBitNo\r\n\r\n\tmov ebx,[bx]\r\nnextitem:\r\n\tlodsb\r\n\tmovzx eax,al\r\n\tmov dx,[di]\r\n\tadd di,2\r\n\tbt ebx, eax\r\n\tjnc @F\r\n\tpush cx\r\n\tinvoke printf, CStr(\"%2u %s\"), ax, dx\r\n\tpop cx\r\n@@:\r\n\tloop nextitem\r\n\tpop si\r\n\tadd si,sizeof FEATS\r\n\tdec items\r\n\tjnz nextreg\r\n\tret\r\n\r\nprint_cpuid_regs endp\r\n\r\nmain proc c\r\n\r\n\tpushf\r\n\tmov ax,7000h\r\n\tPUSH AX\t\t\t\t\t ; also kept after a POPF\r\n\tPOPF\t\t\t\t\t ; a 286 always sets it to Null\r\n\tPUSHF\r\n\tPOP AX\r\n\tpopf\r\n\tand ah,0F0h\r\n\tcmp AH,70H\t\t\t\t;on a 80386 (real-mode) 7x is in AH\r\n\tjz is386\r\n\tinvoke printf, CStr(\"CPU is not 80386 or better\",lf)\r\n\tjmp exit\r\nis386:\r\n\tinvoke hascpuid\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"CPUID not implemented\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmov eax,0\r\n\tcpuid\r\n\tmov dwCPUIDeax, eax\r\n\tmov dwCPUIDebx, ebx\r\n\tmov dwCPUIDecx, ecx\r\n\tmov dwCPUIDedx, edx\r\n\tinvoke printf, CStr(\"CPUID.00: EAX=%lX string=%s\",lf), \\\r\n\t\teax, offset dwCPUIDebx\r\n\r\n\tmov eax,1\r\n\tcpuid\r\n\tmov [bCpu],ah\r\n\tmov dwCPUIDebx, ebx\r\n\tmov dwCPUIDecx, ecx\r\n\tmov dwCPUIDedx, edx\r\n\tinvoke printf, CStr(\"CPUID.01: EAX-EBX-ECX-EDX: %lX-%lX-%lX-%lX\",lf), \\\r\n\t\tdwCPUIDeax, dwCPUIDebx, dwCPUIDecx, dwCPUIDedx\r\n\tmov eax, dwCPUIDebx\r\n\tshr eax,16\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"Logical Processors (EBX[16-23], req. HTT=1): %u\",lf), ax\r\n\r\n\tinvoke print_cpuid_regs, 2, offset cpuid1m\t;display cpuid 1 in more detail\r\n\r\n\tcmp byte ptr dwCPUIDeax,7\r\n\tjc @F\r\n\tmov eax,7\t\t;get extended features (returns features in ebx, ecx, edx)\r\n\tmov ecx,0\r\n\tcpuid\r\n\tmov dwCPUIDebx, ebx\r\n\tinvoke print_cpuid_regs, 1, offset cpuid7m\r\n@@:\r\n\tmov eax,80000000h\r\n\tcpuid\r\n\tand eax, eax ;bit 31 set?\r\n\tjns no8ex\r\n\tmov di, ax\r\n\tinvoke printf, CStr(\"CPUID.80000000: EAX=%lX\",lf), eax\r\n\r\n\tcmp di, 1\r\n\tjc nobrand\r\n\tmov eax,80000001h\r\n\tcpuid\r\n\tmov dwCPUIDebx, ebx\r\n\tmov dwCPUIDecx, ecx\r\n\tmov dwCPUIDedx, edx\r\n\tinvoke printf, CStr(\"CPUID.80000001: EAX-EBX-ECX-EDX=%lX-%lX-%lX-%lX\",lf), \\\r\n\t\teax, ebx, ecx, edx\r\n\tinvoke print_cpuid_regs, 2, offset cpuid1xm\t;display values returned in edx, ecx\r\n\r\n\tcmp di, 5\r\n\tjb nobrand\r\n\tmov eax,80000002h\r\n\tcpuid\r\n\tmov cpubrand+0, eax\r\n\tmov cpubrand+4, ebx\r\n\tmov cpubrand+8, ecx\r\n\tmov cpubrand+12, edx\r\n\tinvoke printf, CStr(\"CPUID.80000002/3/4: brand=%s\"), offset cpubrand\r\n\tmov eax,80000003h\r\n\tcpuid\r\n\tmov cpubrand+0, eax\r\n\tmov cpubrand+4, ebx\r\n\tmov cpubrand+8, ecx\r\n\tmov cpubrand+12, edx\r\n\tinvoke printf, CStr(\"%s\"), offset cpubrand\r\n\tmov eax,80000004h\r\n\tcpuid\r\n\tmov cpubrand+0, eax\r\n\tmov cpubrand+4, ebx\r\n\tmov cpubrand+8, ecx\r\n\tmov cpubrand+12, edx\r\n\tinvoke printf, CStr(\"%s\",lf), offset cpubrand\r\nnobrand:\r\n\tcmp di, 8\r\n\tjc no8ex\r\n\tmov eax, 80000008h\r\n\tcpuid\r\n\tmov dwCPUIDeax, eax\r\n\tinvoke printf, CStr(\"CPUID.80000008: EAX-EBX-ECX-EDX=%lX-%lX-%lX-%lX\",lf), \\\r\n\t\teax, ebx, ecx, edx\r\n\tmov eax,dwCPUIDeax\r\n\tmov cx, ax\r\n\tand cx,0ffh\r\n\tshr ax,8\r\n\tand ax,0ffh\r\n\tinvoke printf, CStr(\"physical/linear address bits=%u/%u\",lf), cx, ax\r\nno8ex:\r\nexit:\r\n\tmov al,0\r\n\tret\r\n\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx\r\n\tcall main\r\n\tmov ah,4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Tools/CPUID/MAKE.BAT",
    "content": "@echo off\r\nrem creates CPUID.EXE with JWasm\r\njwasm -c -nologo -Fl -Sg -mz CPUID.ASM\r\n"
  },
  {
    "path": "Tools/CPUID/PRINTF.INC",
    "content": "\r\n;--- simple printf() implementation\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- outb is expected to be onto stack\r\n\r\nltob PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- ds=dgroup, ss don't need to be dgroup\r\n\r\nprintf PROC c uses si di bx fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\nnextdigit:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb digitsdone\r\n\tcmp BYTE PTR [si],'9'\r\n\tja digitsdone\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul cx,bx,10\t\t;cx = bx * 10\r\n\tadd ax,cx\r\n\tmov bx,ax\r\n\tjmp nextdigit\r\n\r\ndigitsdone:\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tmov al,'%'\r\n\tjmp @@L359\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Tools/Cpustat/CPUSTAT.ASM",
    "content": "\r\n;--- CPUSTAT displays status of CPU. \r\n;--- Public Domain.\r\n;--- Masm syntax. To be assembled with JWasm or Masm.\r\n;--- uses 16-bit printf\r\n\r\n\t.286 \r\n\t.model small\r\n\t.dosseg\r\n\t.stack 400h\r\n\r\nbs\tequ 8\r\nlf\tequ 10\r\n\r\nprintf proto c :ptr BYTE, :VARARG\r\n\r\nCStr macro y:VARARG\r\nlocal sym\r\n\t.const\r\nsym db y,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nDESCRIPTOR struct\r\nwLimit\t\tdw ?\r\nwBase0015\tdw ?\r\nbBase1623\tdb ?\r\nbAttrL\t\tdb ?\r\nbAttrH\t\tdb ?\r\nbBase2431\tdb ?\r\nDESCRIPTOR ends\r\n\r\nTSS struct\r\n       dd ?\r\ndwESP0 dd ?\r\nwSS0   dw ?\r\n       org 64h\r\nwFlags dw ?\r\nwOffs  dw ?  \r\nTSS ends\r\n\r\n\t.data\r\n\r\ndwCR0\tdd 0\r\n_msw\tdd 0\r\n\r\ngdtr\tlabel fword\r\ngdtl\tdw 0\r\ngdta\tdd 0\r\n\r\nidtr\tlabel fword\r\nidtl\tdw 0\r\nidta\tdd 0\r\n\r\n;--- gdt for In15 ah=87 move\r\n\r\ngdti15 label DESCRIPTOR\r\n\t\tDESCRIPTOR <0,0,0,0,0,0>\r\n\t\tDESCRIPTOR <0,0,0,0,0,0>\r\ni15src\tDESCRIPTOR <-1,0,0,093h,0,0>\r\ni15dst\tDESCRIPTOR <-1,0,0,093h,0,0>\r\n\t\tDESCRIPTOR <0,0,0,0,0,0>\r\n\t\tDESCRIPTOR <0,0,0,0,0,0>\r\n\r\nmodflg\tdw 0\r\nbrk\t\tdw 0\r\n\r\nO_GDT\tequ 1\r\nO_IDT\tequ 2\r\nO_PD\tequ 4\r\nO_TSS\tequ 8\r\nO_FPU\tequ 16\r\n\r\nbOpt\tdb 0\t;cmdline options -g -i -p -t\r\nbVerbose db 0\t;for -p: 1=display paging tables\r\n\r\n\t.code\r\n\r\n\t.586\r\n\r\n\tinclude printf.inc\r\n\r\nhascpuid proc\r\n\tpush di\r\n\tmov di,sp\r\n\tand sp,0fffch\t;make sure we don't get an exc 11 (if AM set in CR0)\r\n\tpushfd\t\t\t\t\t\t; save EFlags\r\n\tcli\r\n\tpushd 240000h\t\t\t\t; set AC bit in eflags, reset IF\r\n\tpopfd\t\t\t\t\t\t; pop extended flags\r\n\tpushfd\t\t\t\t\t\t; push extended flags\r\n\tpop ax\r\n\tpop ax\t\t\t\t\t\t; get HiWord(EFlags) into AX\r\n\tpopfd\t\t\t\t\t\t; restore EFlags\r\n\ttest al,04\t;AC bit set?\r\n\tje @F\r\n\ttest al,20h\t;CPUID bit set?\r\n\tjz @F\r\n\tmov sp,di\r\n\tpop di\r\n\tclc\r\n\tret\r\n@@:\r\n\tmov sp,di\r\n\tpop di\r\n\tstc\r\n\tret\r\nhascpuid endp\r\n\r\nmyint0c:\r\nmyint0d:\r\n\tshr ecx,1\r\n\tiret\r\n\r\nDispSegLimits proc\r\n\r\nlocal limitss:dword\r\nlocal limitds:dword\r\nlocal limites:dword\r\nlocal limitfs:dword\r\nlocal limitgs:dword\r\n\r\n\tpush ds\r\n\tpush es\r\n\tpush fs\r\n\tpush gs\r\n\txor ax,ax\r\n\tmov ds,ax\r\n\tmov es,ax\r\n\tmov fs,ax\r\n\tmov gs,ax\r\n\r\n\tmov ax,cs\r\n\tshl eax,16\r\n\tmov ax,offset myint0d\r\n\tcli\r\n\txchg eax,ds:[13*4]\r\n\tpush eax\r\n\r\n;--- SS limit violation creates an exception 0Ch!\r\n\tmov ax,cs\r\n\tshl eax,16\r\n\tmov ax,offset myint0c\r\n\txchg eax,ds:[12*4]\r\n\tpush eax\r\nif 0\r\n\tmov dx,ss\r\n\tmov bx,sp\r\n\txor ax,ax\r\n\tmov ss,ax\r\n\tmov sp,400h\r\nendif\r\n\tmov ecx,-1\r\n\tmov al,ss:[ecx]\r\nif 0\r\n\tmov ss,dx\r\n\tmov sp,bx\r\nendif\r\n\tmov limitss,ecx\r\n\tpop eax\r\n\tmov ds:[12*4],eax\r\n\r\n\tmov ecx,-1\r\n\tmov al,ds:[ecx]\r\n\tmov limitds,ecx\r\n\r\n\tmov ecx,-1\r\n\tmov al,es:[ecx]\r\n\tmov limites,ecx\r\n\r\n\tmov ecx,-1\r\n\tmov al,fs:[ecx]\r\n\tmov limitfs,ecx\r\n\r\n\tmov ecx,-1\r\n\tmov al,gs:[ecx]\r\n\tmov limitgs,ecx\r\n\r\n\tpop eax\r\n\tmov ds:[13*4],eax\r\n\tsti\r\n\tpop gs\r\n\tpop fs\r\n\tpop es\r\n\tpop ds\r\n\tinvoke printf, CStr(\"SS-DS-ES-FS-GS limits: %lX-%lX-%lX-%lX-%lX\",lf), limitss, limitds, limites, limitfs, limitgs\r\n\tret\r\n\r\nDispSegLimits endp\r\n\r\ngetldtr proc\r\n\t.586p\r\n\tcli\r\n\tmov ecx,cr0\r\n\tinc cx\r\n\tmov cr0,ecx\r\n\tjmp @F\r\n@@:\r\n\tsldt ax\r\n\tstr dx\r\n\tdec cx\r\n\tmov cr0,ecx\r\n\tsti\r\n\tret\r\n\t.586\r\ngetldtr endp\r\n\r\nmalloc proc stdcall wBytes:word\r\n\tmov ax, wBytes\r\n\tadd ax, [brk]\r\n\tjc error\r\n\txchg ax, [brk]\r\nerror:\r\n\tret\r\nmalloc endp\r\n\r\nfree proc stdcall pMem:ptr\r\n\tmov ax, pMem\r\n\tand ax, ax\r\n\tjz @F\r\n\tmov [brk], ax\r\n@@:\r\n\tret\r\nfree endp\r\n\r\n;--- copy an extended memory region ( physical address in physad ) into buffer\r\n\r\ncopymem proc stdcall uses si buffer:ptr, physad:dword, size_:word\r\n\r\n\tmov eax, physad\r\n;\tand ax, 0F000h\r\n\tmov i15src.wBase0015,ax\r\n\tshr eax, 16\r\n\tmov i15src.bBase1623,al\r\n\tmov i15src.bBase2431,ah\r\n\tmov ax, buffer\r\n\tmovzx eax, ax\r\n\tmov dx, ds\r\n\tmovzx edx, dx\r\n\tshl edx, 4\r\n\tadd eax, edx\r\n\tmov i15dst.wBase0015, ax\r\n\tshr eax, 16\r\n\tmov i15dst.bBase1623, al\r\n\tmov i15dst.bBase2431, ah\r\n\tpush ds\r\n\tpop es\r\n\tmov cx, size_\r\n\tshr cx, 1\r\n\tmov si, offset gdti15\t;es:si=gdt to use\r\n\tmov ah, 87h\r\n\tstc\r\n\tint 15h\r\n\tret\r\n\r\ncopymem endp\r\n\r\n;--- translate linear address in linad into physical address (returned in eax)\r\n\r\ngetphysaddr proc stdcall uses ebx esi di linad:dword\r\n\txor di, di\r\n\tmov eax, -1\t\t;in case the next instr is \"emulated\"\r\n\tmov eax, cr3\r\n\tcmp eax, -1\r\n\tjz error\r\n\tcmp eax, 0\t\t;NTVDM?\r\n\tjz error\r\n\tand ax, 0F000h\r\n\tmov esi, eax\r\n\tinvoke malloc, 1000h\r\n\tjc error\r\n\tmov di, ax\r\n\tinvoke copymem, di, esi, 1000h\r\n\tjc error\r\n\tmov ebx, linad\r\n\tshr ebx, 20\r\n\tand bx, 0FFCh\r\n\tmov eax, dword ptr [bx+di]\r\n\tand ax, 0F000h\r\n\tinvoke copymem, di, eax, 1000h\r\n\tjc error\r\n\tmov ebx, linad\r\n\tshr ebx, 10\r\n\tand bx, 0FFCh\r\n\tmov eax, dword ptr [bx+di]\r\n\tand ax,0f000h\r\n\tmov edx, linad\r\n\tand dx, 0FFFh\r\n\tor ax, dx\r\n\tpush eax\r\n\tinvoke free, di\r\n\tpop eax\r\n\tclc\r\n\tret\r\nerror:\r\n\tinvoke free, di\r\n\tstc\r\n\tret\r\ngetphysaddr endp\r\n\r\ngetattr proc stdcall buffer:ptr\r\n\tpusha\r\n\tmov di, buffer\r\n\ttest dl, 10h\r\n\tjz syssegs\r\n\tmov si, CStr(\"code\")\r\n\ttest dl, 8\r\n\tjnz @F\r\n\tmov si, CStr(\"data\")\r\n@@:\r\n\tcall cpy\r\n\tjmp done\r\nsyssegs:\r\n\tand dl, 0Fh\r\n\tcmp dl, 2\r\n\tjnz @F\r\n\tmov si, CStr(\"LDT \")\r\n\tcall cpy\r\n\tjmp done\r\n@@:\r\n\tcmp dl, 5\r\n\tjnz @F\r\n\tmov si, CStr(\"task gate \")\r\n\tcall cpy\r\n\tjmp done\r\n@@:\r\n\ttest dl, 111b\r\n\tjnz @F\r\n\tmov si, CStr(\"undef \")\r\n\tcall cpy\r\n\tjmp done\r\n@@:\r\n\tmov si, CStr(\"386 \")\r\n\ttest dl, 8\r\n\tjz @F\r\n\tmov si, CStr(\"386 \")\r\n@@:\r\n\tcall cpy\r\n\r\n\tmov si, CStr(\"tss \")\r\n\ttest dl, 4\r\n\tjz @F\r\n\tmov si, CStr(\"gate \")\r\n@@:\r\n\tcall cpy\r\n\r\ndone:\r\n\tmov byte ptr [di], 0\r\n\tdec di\r\n\tcmp byte ptr [di], ' '\r\n\tjnz @F\r\n\tmov byte ptr [di], 0\r\n@@:\r\n\tpopa\r\n\tret\r\ncpy:\r\n\tlodsb\r\n\tstosb\r\n\tand al, al\r\n\tjnz cpy\r\n\tdec di\r\n\tretn\r\n\r\ngetattr endp\r\n\r\n;--- the GDT/IDT is read with int 15h, ah=87h\r\n;--- this is not really correct, since this function\r\n;--- is supposed to read from physical addresses, while\r\n;--- the addresses in GDTR/IDTR are linear;\r\n;--- for jemmex, it often works, though, since its\r\n;--- code/data usually are identity-mapped, starting\r\n;--- at 0x110000.\r\n\r\nDispGDT proc\r\n\r\nlocal wSize:word\r\nlocal buffer2[80]:byte\r\n\r\n\txor di, di\r\n\tinvoke getphysaddr, gdta\r\n\tjc nogdt\r\n\tmov ebx, eax\r\n\r\n\tmov cx,gdtl \r\n\tinc cx\r\n\tmov wSize, cx\r\n\r\n\tinvoke malloc, cx\r\n\tjc error\r\n\tmov di, ax\r\n\r\n\tinvoke copymem, di, ebx, wSize\r\n\tjc error\r\n\r\n\tmov cx, wSize\r\n\tshr cx, 3\r\n\tjcxz nogdt\r\n\tmov si, di\r\nnextitem:\r\n\tpush cx\r\n\tmov cx,[si+0]\r\n\tmov bh,[si+7]\r\n\tmov bl,[si+4]\r\n\tshl ebx,16\r\n\tmov bx,[si+2]\r\n\tmov dx,[si+5]\r\n\tmovzx eax,cx\r\n\tor eax, ebx\r\n\tor ax, dx\r\n\tand eax, eax\r\n\tjz @F\r\n\tinvoke getattr, addr buffer2\r\n\tpush si\r\n\tsub si, di\r\n\tinvoke printf, CStr(\"GDT[%4X]: %08lX:%04X %04X (%s)\",lf), si, ebx, cx, dx, addr buffer2\r\n\tpop si\r\n@@:\r\n\tadd si, sizeof DESCRIPTOR\r\n\tpop cx\r\n\tloop nextitem\r\n\tinvoke free, di\r\nnogdt:\r\n\tret\r\nerror:\r\n\tinvoke printf, CStr(\"Int 15h, ah=87h failed\",lf)\r\n\tinvoke free, di\r\n\tret\r\nDispGDT endp\r\n\r\nDispIDT proc\r\n\r\nlocal pMem:ptr\r\nlocal wSize:word\r\n\r\n\tmov pMem, 0\r\n\tinvoke getphysaddr, idta\r\n\tjc noidt\r\n\tmov esi, eax\r\n\r\n\tmov cx,idtl \r\n\tinc cx\r\n\tcmp cx, 8*100h\r\n\tjbe @F\r\n\tmov cx, 8*100h\r\n@@:\r\n\tmov wSize, cx\r\n\tinvoke malloc, cx\r\n\tjc error\r\n\tmov pMem, ax\r\n\tinvoke copymem, pMem, esi, wSize\r\n\tjc error\r\n\r\n\tmov cx, wSize\r\n\tshr cx, 3\r\n\tjcxz noidt\r\n\tmov si, pMem\r\n\txor di, di\r\nnextitem:\r\n\tpush cx\r\n\tmov ax,[si+6]\r\n\tshl eax, 16\r\n\tmov ax,[si+0]\r\n\tmov bx,[si+2]\r\n\tmov dx,[si+4]\r\n\tinvoke printf, CStr(\"IDT[%4X]: %04X:%08lX %04X\",lf), di, bx, eax, dx\r\n@@:\r\n\tinc di\r\n\tadd si, sizeof DESCRIPTOR\r\n\tpop cx\r\n\tloop nextitem\r\n\tinvoke free, pMem\r\nnoidt:\r\n\tret\r\nerror:\r\n\tinvoke free, pMem\r\n\tinvoke printf, CStr(\"Int 15h, ah=87h failed\",lf)\r\n\tret\r\nDispIDT endp\r\n\r\n;--- get # of PTEs in a page table\r\n;--- eax->physical address pt\r\n\r\ngetPTEs proc stdcall uses si di ebx pPT:ptr\r\n\r\n\tmov ebx, eax\r\n\tmov di, pPT\r\n\tand bx, 0F000h\r\n\tinvoke copymem, di, ebx, 1000h\r\n\tjc exit\r\n\tmov si, di\r\n\txor dx, dx\r\n\tmov cx, 1024\r\nnextitem:\r\n\tlodsd\r\n\ttest al,1\r\n\tjz @F\r\n\tinc dx\r\n@@:\r\n\tloop nextitem\r\n\tmov ax, dx\r\n\tclc\r\nexit:\r\n\tret\r\ngetPTEs endp\r\n\r\n;--- print page table\r\n;--- iPD: index in page directory\r\n\r\nprintPT proc stdcall uses si pPT:ptr, iPD:word\r\n\r\n\tmov si, pPT\r\n\tmov cx, 1024\r\nnextitem:\r\n\tpush cx\r\n\ttest cl, 7\r\n\tjnz @F\r\n;--- index page directory specifies address bits 22-31\r\n;--- index page table specifies address bits 12-21\r\n\tmovzx eax, di\r\n\tshl eax, 22\r\n\tmov dx, cx\r\n\tsub dx, 1024\r\n\tneg dx\r\n\tmovzx edx, dx\r\n\tshl edx, 12\r\n\tor eax, edx\r\n\tinvoke printf, CStr(lf,\"%8lX: \"), eax\r\n@@:\r\n\tlodsd\r\n\t.if al & 1\r\n\t\tinvoke printf, CStr(\"%8lX \"), eax\r\n\t.else\r\n\t\tinvoke printf, CStr(\"-------- \")\r\n\t.endif\r\n\tpop cx\r\n\tloop nextitem\r\n\tinvoke printf, CStr(lf,lf)\r\n\tret\r\nprintPT endp\r\n\r\n;--- print page directory\r\n;--- if bVerbose==1, call printPT to display PTEs.\r\n\r\nprintpd proc stdcall uses si di pPD:ptr\r\n\r\nlocal pPT:ptr\r\n\r\n\tinvoke malloc, 1000h\r\n\tjc error\r\n\tmov pPT, ax\r\n\r\n\tmov si, pPD\r\n\txor di, di\r\nnextitem:\r\n\tlodsd\r\n\ttest al, 1\r\n\tjz skipitem\r\n\ttest al, 80h  ; perhaps a 4 MB page?\r\n\tjz @F\r\n\tmov dx, di\r\n\tshl edx, 22\r\n\r\n;--- 4MB page PTE:\r\n;--- bits 22-31: address 22-31\r\n;--- bits 13-20: address 32-39\r\n;--- bit 21: 0\r\n;--- bit 12: PAT\r\n\r\n\tmov ecx, eax\r\n\tshr ecx, 13\r\n\tmovzx ecx, cl\r\n\tmov ebx, eax\r\n\tand ebx, 0ffc00000h\r\n\tinvoke printf, CStr(\"%8lX: %08lX (4 MB page: phys=%X%08lX)\",lf), edx, eax, cx, ebx\r\n\tjmp skipitem\r\n@@:\r\n\tpush eax\r\n\tinvoke getPTEs, pPT\r\n\tpop ecx\r\n\tjc error\r\n\tmov dx, di\r\n\tshl edx, 22\r\n\tinvoke printf, CStr(\"%8lX: %08lX (%4u pages)\",lf), edx, ecx, ax\r\n\t.if bVerbose\r\n\t\tinvoke printpt, pPT, di\r\n\t.endif\r\nskipitem:\r\n\tinc di\r\n\tcmp di, 1024\r\n\tjnz nextitem\r\n\tinvoke free, pPT\r\nerror:\r\n\tret\r\nprintpd endp\r\n\r\nDispPD proc\r\n\r\nlocal pMem:ptr\r\n\r\n\tmov pMem, 0\r\n\tinvoke malloc, 1000h\r\n\tjc error\r\n\tmov pMem, ax\r\n\tmov eax, -1\t\t;in case the next instr is \"emulated\"\r\n\tmov eax, cr3\r\n\tcmp eax, -1\r\n\tjz done\r\n\tand eax, eax\r\n\tjz done\r\n\tand ax, 0F000h\r\n\tinvoke copymem, pMem, eax, 1000h\r\n\tjc error\r\n\tinvoke printf, CStr(lf,\"page directory\",lf)\r\n\tinvoke printf, CStr(\"-------------------------------\",lf)\r\n\tinvoke printpd, pMem\r\ndone:\r\n\tinvoke free, pMem\r\n\tret\r\nerror:\r\n\tinvoke free, pMem\r\n\tinvoke printf, CStr(\"Int 15h, ah=87h failed\",lf)\r\n\tret\r\nDispPD endp\r\n\r\n;--- display IO permission bitmap\r\n\r\nDispIOPB proc stdcall uses si di pBitmap:ptr, size_:word\r\n\r\nlocal wEnd:word\r\nlocal wItems:word\r\n\r\n\tmov ecx, 10000h\r\n\tmovzx eax, size_\r\n\tshl eax, 3\r\n\tcmp ecx, eax\r\n\tjb @F\r\n\tmov ecx, eax\r\n@@:\r\n\tmov wEnd, cx\r\n\tinvoke printf, CStr(\"trapped Ports:\",lf)\r\n\tmov si, pBitmap\r\n\txor edi, edi\r\n\tmov wItems, di\r\nnextport:\r\n\tbt [si], edi \r\n\tjnc skipitem\r\n\tinvoke printf, CStr(\"%4X \"), di\r\n\tinc wItems\r\n\tcmp wItems, 8\r\n\tjnz skipitem\r\n\tinvoke printf, CStr(lf)\r\n\tmov wItems, 0\r\nskipitem:\r\n\tinc di\r\n\tcmp di, wEnd\r\n\tjnz nextport\r\n\tcmp wItems, 0\r\n\tjz @F\r\n\tinvoke printf, CStr(lf)\r\n@@:\r\n\tret\r\nDispIOPB endp\r\n\r\n;--- display Interrupt redirection bitmap\r\n\r\nDispRedir proc stdcall uses si di pBitmap:ptr\r\n\tinvoke printf, CStr(\"Interrupt redirection bitmap:\",lf)\r\n\tmov bx, 0\r\n\tmov di, pBitmap\r\nnextbit:\r\n\tbt [di], bx\r\n\tjnc skipitem\r\n\tinvoke printf, CStr(\"%2X \"), bx\r\nskipitem:\r\n\tinc bx\r\n\tcmp bh, 0\r\n\tjz nextbit\r\n\tinvoke printf, CStr(lf)\r\n\tret\r\nDispRedir endp\r\n\r\nDispTSS proc stdcall wSel:word, dwBase:dword, wLimit:word\r\n\r\nlocal wSize:word\r\nlocal dwPhys:dword\r\nlocal pTSS:ptr\r\nlocal pIOPB:ptr\r\nlocal Redir[32]:byte\r\n\r\n\tmov pTSS, 0\r\n\tmov pIOPB, 0\r\n\tinvoke getphysaddr, dwBase\r\n\tmov dwPhys, eax\r\n\tinvoke printf, CStr(\"TSS %X: address linear/phys=%lX/%lX, limit=%X\",lf), wSel, dwBase, eax, wLimit\r\n\tinvoke malloc, sizeof TSS\r\n\tjc noiopb\r\n\tmov pTSS, ax\r\n\tinvoke copymem, ax, dwPhys, sizeof TSS\r\n\tmov bx, pTSS\r\n\tinvoke printf, CStr(\"  SS:ESP0=%X:%lX, offset IOPB=%X\",lf), [bx].TSS.wSS0, [bx].TSS.dwESP0, [bx].TSS.wOffs\r\n\tmov ax, wLimit\r\n\tsub ax, [bx].TSS.wOffs\r\n\tjbe noiopb\r\n\tcmp ax, 8192\r\n\tjb @F\r\n\tmov ax, 8192\r\n@@:\r\n\tmov wSize, ax\r\n\tinvoke malloc, ax\r\n\tjc noiopb\r\n\tmov pIOPB, ax\r\n\tmovzx edx, [bx].TSS.wOffs\r\n\tadd edx, dwPhys\r\n\tinvoke copymem, pIOPB, edx, wSize\r\n\tjc noiopb\r\n\tinvoke DispIOPB, pIOPB, wSize\r\nif 1\r\n\tmov eax, cr4\r\n\ttest al, 1\r\n\tjz noiopb\r\n\tmov bx, pTSS\r\n\tmovzx edx, [bx].TSS.wOffs\r\n\tsub edx, 32\r\n\tadd edx, dwPhys\r\n\tinvoke copymem, addr Redir, edx, 32\r\n\tjc noiopb\r\n\tinvoke DispRedir, addr Redir\r\nendif\r\nnoiopb:\r\n\tinvoke free, pIOPB\r\n\tinvoke free, pTSS\r\n\tret\r\nDispTSS endp\r\n\r\n;--- scan GDT and display all TSSes\r\n\r\nDispAllTSS proc\r\n\r\nlocal wSize:word\r\nlocal dwPhys:dword\r\n\r\n\txor di, di\r\n\tinvoke getphysaddr, gdta\r\n\tjc error\r\n\tmov esi, eax\r\n\tmov cx, gdtl \r\n\tinc cx\r\n\tmov wSize, cx\r\n\tinvoke malloc, cx\r\n\tjc error\r\n\tmov di, ax\r\n\tinvoke copymem, di, esi, wSize\r\n\tjc error\r\n\tmov si, di\r\nnextitem:\r\n\tcmp [si].DESCRIPTOR.bAttrL, 8Bh\r\n\tjnz skipitem\r\n\tmov bh, [si].DESCRIPTOR.bBase2431\r\n\tmov bl, [si].DESCRIPTOR.bBase1623\r\n\tshl ebx, 16\r\n\tmov bx, [si].DESCRIPTOR.wBase0015\r\n\tmov ax, si\r\n\tsub ax, di\r\n\tinvoke DispTSS, ax, ebx, [si].DESCRIPTOR.wLimit\r\nskipitem:\r\n\tadd si, sizeof DESCRIPTOR\r\n\tmov ax, wSize\r\n\tadd ax, di\r\n\tcmp si, ax\r\n\tjb nextitem\r\nerror:\r\n\tinvoke free, di\r\n\tret\r\nDispAllTSS endp\r\n\r\n;--- main()\r\n\r\nmain proc c\r\n\r\nlocal wSize:word\r\n\r\n\tmov si, 80h\r\n\tmov cl, es:[si]\r\n\tinc si\r\n\r\n\t.while cl\r\n\t\tmov al,es:[si]\r\n\t\tinc si\r\n\t\tdec cl\r\n\t\t.if (al == ' ' || al == 9)\r\n\t\t\t;\r\n\t\t.elseif ( cl > 0 && ( al == '-' || al == '/'))\r\n\t\t\tmov al,es:[si]\r\n\t\t\tinc si\r\n\t\t\tdec cl\r\n\t\t\tor al,20h\r\n\t\t\t.if (al == 'i')\r\n\t\t\t\tor bOpt, O_IDT\r\n\t\t\t.elseif (al == 'g')\r\n\t\t\t\tor bOpt, O_GDT\r\n\t\t\t.elseif (al == 'p')\r\n\t\t\t\tor bOpt, O_PD\r\n\t\t\t.elseif (al == 't')\r\n\t\t\t\tor bOpt, O_TSS\r\n\t\t\t.elseif (al == 'v')\r\n\t\t\t\tor bVerbose, 1\r\n\t\t\t.elseif (al == 'f')\r\n\t\t\t\tor bOpt, O_FPU\r\n\t\t\t.else\r\n\t\t\t\tjmp usage\r\n\t\t\t.endif\r\n\t\t.else\r\nusage:\r\n\t\t\tinvoke printf, CStr(\"usage: CPUSTAT [ options ]\",lf)\r\n\t\t\tinvoke printf, CStr(\"    -f: clear FPU status\",lf)\r\n\t\t\tinvoke printf, CStr(\"    -g: display GDT if in V86 mode\",lf)\r\n\t\t\tinvoke printf, CStr(\"    -i: display IDT if in V86 mode\",lf)\r\n\t\t\tinvoke printf, CStr(\"    -p: display PD if in V86 mode\",lf)\r\n\t\t\tinvoke printf, CStr(\"    -t: display TSS+IOPB if in V86 mode\",lf)\r\n\t\t\tinvoke printf, CStr(\"    -v: make -p display paging tables\",lf)\r\n\t\t\tjmp exit\r\n\t\t.endif\r\n\t.endw\r\n\r\n\tpushf\r\n\tmov ax,7000h\r\n\tPUSH AX\t\t\t\t\t ; also kept after a POPF\r\n\tPOPF\t\t\t\t\t ; a 286 always sets it to Null\r\n\tPUSHF\r\n\tPOP AX\r\n\tpopf\r\n\tand ah,0F0h\r\n\tcmp AH,70H\t\t\t\t;on a 80386 (real-mode) 7x is in AH\r\n\tjnz is286\r\n\tdb 66h\t\t\t\t\t;MASM doesn't know SMSW EAX\r\n\tsmsw ax\r\n\tmov [_msw],eax\r\n\tjmp is386\r\nis286:\r\n\tsmsw ax\r\n\tinvoke printf, CStr(\"MSW: %X\",lf), ax\r\n\tinvoke printf, CStr(\"CPU is not 80386 or better\",lf)\r\n\tjmp exit\r\nis386:\r\n\tand ax,1\r\n\tmov [modflg],ax\r\n\r\n\tmov eax,[_msw]\r\n\tbt eax,31\t; PG\r\n\tsetc dl\r\n\tmovzx si,dl\r\n\tbt eax,18\t; AM\r\n\tsetc dl\r\n\tmovzx di,dl\r\n\tbt eax,16\t; WP\r\n\tsetc cl\r\n\tmovzx cx,cl\r\n\tbt ax,5\t\t; NE   \r\n\tsetc dl\r\n\tmovzx dx,dl\r\n\r\n\t.data\r\nwMP dw 0\r\nwEM dw 0\r\nwTS dw 0\r\n\t.code\r\n\r\n\tbt ax,3\t\t; TS\r\n\tsetc byte ptr [wTS]\r\n\tbt ax,2\t\t; EM\r\n\tsetc byte ptr [wEM]\r\n\tbt ax,1\t\t; MP\r\n\tsetc byte ptr [wMP]\r\n\r\n\tmov bx,CStr('Real')\r\n\tbt ax,0\t\t; PE\r\n\tsetc al\r\n\tjnc @F\r\n\tmov bx,CStr('V86')\r\n@@:    \r\n\tmovzx ax,al\r\n\tinvoke 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 \r\n\r\n\tdb 66h\t\t\t; probably useless, just lgdt may need operand size prefix\r\n\tsgdt gdtr     \r\n\tdb 66h\t\t\t; probably useless, just lidt may need operand size prefix\r\n\tsidt idtr\r\n\r\n\tcmp bOpt, 0\r\n\tjnz optional\r\n\r\n\tmov eax, 0\t\t;in case the next instr is \"emulated\"\r\n\tmov eax, cr0 \t;cr0 (=msw)\r\n\tmov [dwCR0],eax\r\n\tand ax,1\r\n\r\n\tcmp ax,modflg\r\n\tjz @F\r\n\tinvoke printf, CStr(\"'MOV EAX,CR0' emulated incorrectly!\",lf)\r\n@@:\r\n\tinvoke printf, CStr(\"CR0: %lX\",lf),dwCR0\r\n\r\n\tinvoke printf, CStr(\"GDTR: %lX,%X, IDTR: %lX,%X\",lf),gdta,gdtl,idta,idtl\r\n\r\n\ttest [_msw],1\r\n\tjnz @F\r\n\tcall getldtr\r\n\tinvoke printf, CStr(\"LDTR: %X, TR: %X\",lf), ax, dx\r\n@@:\r\n\r\n\tmov eax, -1\t\t;in case the next instr is \"emulated\"\r\n\tmov eax, cr2\r\n\tinvoke printf, CStr(\"CR2: %lX  \"),eax\r\n\r\n\tmov eax, -1\t\t;in case the next instr is \"emulated\"\r\n\tmov eax, cr3\r\n\tinvoke printf, CStr(\"CR3: %lX\",lf),eax\r\n\r\n\tmov eax, -1\t\t;in case the next instr is \"emulated\"\r\n\r\n\tcall hascpuid\t;if CPUID is supported, CR4 exists as well\r\n\tjc nocr4\r\n\r\n\tmov eax, cr4\t;priviledged instruction\r\n\tmov ch,0\r\n\tpush bp\r\n\tmov bp,sp\r\n\ttest al,1\t\t;VME?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest al,2\t\t;PVI?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest al,8\t\t;DE?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest al,10h\t\t;PSE?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest al,20h\t\t;PAE?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest al,40h\t\t;MCE?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest al,80h\t\t;PGE?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest ax,200h\t;OSFXSR?\r\n\tsetnz cl\r\n\tpush cx\r\n\ttest ax,400h\t;OSXMMEXP?\r\n\tsetnz cl\r\n\tpush cx\r\n\tinvoke printf, CStr(\"CR4: %lX (VME=%X, PVI=%X, DE=%X, PSE=%X, PAE=%X, MCE=%X, PGE=%X, OSFXSR=%X, OSXMMEX=%X)\",lf), eax,\r\n\t\tword 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]\r\n\tmov sp,bp\r\n\tpop bp\r\n\r\nnocr4:\r\n\r\n;--- might be a good idea to check CR0.MP & CR0.EM bits.\r\n;--- if CR0.MP=1, a fwait opcode will raise an exception 07 if CR0.TS=1\r\n;--- if CR0.EM=1, any fp opcode will raise an exception 07.\r\n;--- MP=bit 1, EM=bit 2, TS=bit 3.\r\n\r\n\ttest [_msw], 4\r\n\tjz @F\r\n\tinvoke printf, CStr(\"CR0.EM=1, won't run FPU instructions\",lf)\r\n\tjmp nofpu\r\n@@:\r\n\tfnstsw ax\r\n\tfnstcw wSize\r\n\tinvoke printf, CStr(\"FCW: %X  FSW: %X\",lf), wSize, ax\r\nnofpu:\r\n\r\n\tmov eax, dr0\r\n\tmov ebx, dr1\r\n\tmov ecx, dr2\r\n\tmov edx, dr3\r\n\tinvoke printf, CStr(\"DR0-DR3: %lX %lX %lX %lX\",lf), eax, ebx, ecx, edx\r\n\tmov eax, dr6\r\n\tmov ecx, dr7\r\n\tinvoke printf, CStr(\"DR6: %lX  DR7: %lX\",lf), eax, ecx\r\n\tpushfd\r\n\tpop eax\r\n\tinvoke printf, CStr(\"EFL: %lX, ESP: %lX\",lf), eax, esp\r\n\r\n\t.if !(byte ptr _msw & 1)\r\n\t\tcall DispSegLimits\r\n\t.endif\r\n\r\noptional:\r\n\t.if (bOpt & O_GDT)\r\n\t\t.if byte ptr [_msw] & 1\t;v86 mode?\r\n\t\t\tcall DispGDT\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"no GDT in real-mode\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\t.if (bOpt & O_IDT)\r\n\t\t.if byte ptr [_msw] & 1\t;v86 mode?\r\n\t\t\tcall DispIDT\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"no IDT in real-mode\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\t.if (bOpt & O_PD)\r\n\t\t.if byte ptr [_msw] & 1\t;v86 mode?\r\n\t\t\tcall DispPD\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"no paging tables in real-mode\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\t.if (bOpt & O_TSS)\r\n\t\t.if byte ptr [_msw] & 1\t;v86 mode?\r\n\t\t\tcall DispAllTSS\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"no TSS in real-mode\",lf)\r\n\t\t.endif\r\n\t.endif\r\n\t.if (bOpt & O_FPU)\r\n\t\t.if byte ptr [_msw] & 20h ; NE bit set?\r\n\t\t.else\r\n\t\t\tin al, 0A1h\t\t; unmask IRQ 0Dh\r\n\t\t\tand al, not 20h\r\n\t\t\tout 0A1h, al\r\n\t\t\tmov al,0\r\n\t\t\tout 0F0h, al\r\n\t\t.endif\r\n\t\tfninit\r\n\t.endif\r\n\r\nexit:\r\n\tmov al,0\r\n\tret\r\n\r\nmain endp\r\n\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx\r\n\tmov [brk],sp\r\n\tmov cx, es\r\n\tsub ax, cx\r\n\tmov bx, 1000h\t;request a full 64kB dgroup\r\n\tadd bx, ax\r\n\tmov ah, 4Ah\r\n\tint 21h\r\n\tjc @F\r\n\tcall main\r\n@@:\r\n\tmov ah,4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Tools/Cpustat/MAKE.BAT",
    "content": "@echo off\r\nrem creates CPUSTAT.EXE with JWasm\r\njwasm -c -nologo -Fl -Sg -mz CPUSTAT.ASM\r\n"
  },
  {
    "path": "Tools/Cpustat/PRINTF.INC",
    "content": "\r\n;--- simple printf() implementation\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- outb is expected to be onto stack\r\n\r\nltob PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- ds=dgroup, ss don't need to be dgroup\r\n\r\nprintf PROC c uses si di bx fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\nnextdigit:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb digitsdone\r\n\tcmp BYTE PTR [si],'9'\r\n\tja digitsdone\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul cx,bx,10\t\t;cx = bx * 10\r\n\tadd ax,cx\r\n\tmov bx,ax\r\n\tjmp nextdigit\r\n\r\ndigitsdone:\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tmov al,'%'\r\n\tjmp @@L359\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Tools/EMSSTAT/EMSSTAT.ASM",
    "content": "\r\n;--- EMSSTAT: display EMS status.\r\n;--- Public Domain.\r\n;--- tools: JWasm/Masm v6 and WLink.\r\n\r\n\t.286\r\n\t.model small\r\nDGROUP group _TEXT\t\t;use TINY model\r\n\t.386\r\n\r\ncr\tequ 13\r\nlf\tequ 10\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\nCONST segment\r\nxxx db string\r\n\tdb 0\r\nCONST ends\r\n\texitm <offset xxx>\r\n\tendm\r\n\r\n\t.dosseg\r\n\t.stack 1024 \r\n\r\n\t.data\r\n\r\nfc\t\tdw 0\t;EMS function code\r\nhandle\tdw 0\t;EMS handle\r\ntotalpg\tdd 0\r\ntotalkb\tdd 0\r\nbStd\tdb 01\r\nbVCPI\tdb 00\r\nbPages\tdb 00\r\nbDevice\tdb 00\r\n\r\n\t.const\r\n\r\nemsstr1 db 'EMMXXXX0',0\r\nemsstr2 db 'EMMQXXX0',0\r\nemsstr3 db 'EMMXXXQ0',0 ;qemm\r\n\r\n\r\n\t.data?\r\n\r\nnamtab\tdb 12 dup (?)\r\ngdttab\tdb 8*3 dup (?)\r\nbuffer\tdb 4096h dup (?)\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\n;--- test if EMM is installed\r\n;--- out: ax=0 if no EMM found\r\n;---      ax=1 if EMM found\r\n\r\nEMScheck proc uses es si di\r\n\r\n\tmov ax, 3567h\r\n\tint 21h\r\n\tmov ax, es\r\n\tor ax, bx\r\n\tjz exit\r\n\r\n\tmov dx,1\r\n\tmov si,offset emsstr1\r\n\tmov di,000ah\r\n\tmov cx,8\r\n\tcld\r\n\trepz cmpsb\r\n\tjz found\r\n\r\n\tmov dx,0\r\n\tmov ah,46h\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz found\r\n\r\n\txor ax, ax\r\n\tjmp exit\r\nfound:\r\n\tand dx,dx\r\n\tjnz isems\r\n\tinvoke printf, CStr(\"EMM installed, but NOEMS, device name=\")\r\n\tmov bx,10\r\n\tmov cx,8\r\n@@:\r\n\tmov al,es:[bx]\r\n\tpush cx\r\n\tinvoke printf, CStr(\"%c\"),ax\r\n\tpop cx\r\n\tinc bx\r\n\tloop @B\r\n\tinvoke printf, CStr(lf)\r\nisems:\r\n\tmov ax,1\r\nexit:\r\n\tret\r\n\r\nEMScheck endp\r\n\r\n_upper:\r\n\tcmp al,'a'\r\n\tjb @F\r\n\tand al,not 20h\r\n@@:\r\n\tret\r\n_skipws:\r\n\tmov al,es:[bx]\r\n\tinc bx\r\n\tcmp al,' '\r\n\tje _skipws\r\n\tcmp al,9\r\n\tje _skipws\r\n\tdec bx\r\n\tcmp al,13\r\n\tret\r\n\r\n;--- ES=PSP\r\n\r\ngetpar proc near\r\n\tmov bx,0080h\r\n\tmov cl,es:[bx]\r\n\tcmp cl,00\r\n\tjz getp1\r\ngetp2:\r\n\tinc bx\r\n\tcall _skipws\r\n\tjz getp1\r\n\tcmp al,'/'\r\n\tjz @F\r\n\tcmp al,'-'\r\n\tjnz parerr\r\n@@:\r\n\tinc bx\r\n\tmov al,es:[bx]\r\n\tcall _upper\r\n\tcmp al,'V'\r\n\tjz getp3\r\n\tcmp al,'D'\r\n\tjz getp4\r\n\tcmp al,'P'\r\n\tjnz parerr\r\n\tmov byte ptr [bStd],0\r\n\tmov byte ptr [bPages],01\r\n\tjmp getp2\r\ngetp3:\r\n\tmov byte ptr [bStd],0\r\n\tmov byte ptr [bVCPI],01\r\n\tjmp getp2\r\ngetp4:\r\n\tmov byte ptr [bStd],0\r\n\tmov byte ptr [bDevice],01\r\n\tjmp getp2\r\ngetp1:\r\n\tclc\r\n\tret\r\nparerr:\r\n\tinvoke printf, CStr(\"usage: EMSSTAT [ options ]\",lf)\r\n\tinvoke printf, CStr(\"options are:\",lf)\r\n\tinvoke printf, CStr(\"    -p: display physical pages\",lf)\r\n\tinvoke printf, CStr(\"    -v: display VCPI info\",lf)\r\n\tinvoke printf, CStr(\"    -d: try to open devices EMMXXXX0 or EMMQXXX0\",lf)\r\n\tstc\r\n\tret\r\ngetpar endp\r\n\r\n;--- ES=PSP\r\n\r\nmain proc c\r\n\r\n\tcall getpar\r\n\tjc exit\r\n\r\n\tcall EMScheck\r\n\tand ax,ax\r\n\tjnz @F\r\n\tinvoke printf, CStr(\"EMM not found\",lf)\r\n\tmov al,01\r\n\tjmp nointapi\r\n@@:\r\n\t.if (bStd)\r\n\t\tcall readd\t;read+display EMS standard info\r\n\t\tcall rpages\t;display raw pages\r\n\t\tcall xvhandle;display EMS handle infos\t \r\n\t.endif\r\n\r\n\t.if (bPages)\r\n\t\tcall ppages\t;display EMS mapping info\r\n\t.endif\r\n\r\n\t.if (bVCPI)\r\n\t\tcall vcpi\t;display VCPI info\r\n\t.endif\r\n\r\n\t.if (bDevice)\r\n\t\tcall deviceinfo;display EMMXXXX0 device info\r\n\t.endif\r\n\r\nnointapi:\r\n\tmov al,00\r\nexit:\r\n\tret\r\nmain endp\r\n\r\n;--- read EMS info\r\n\r\nreadd proc near\r\n\tmov ax,4100h\r\n\tcall intrt\r\n\tmov si,offset buffer\r\n\tmov [si + 0],bx \t;segment address of page frame\r\n\tmov ax,4600h\r\n\tcall intrt\r\n\tmov [si + 2],al \t;version (3.2 or 4.0)\r\n\tmov ax,4200h\r\n\tcall intrt\r\n\tmov [si + 3],dx \t;num total 16 kB pages\r\n\tmov [si + 5],bx \t;num free 16 kB pages\r\n\tmov ax,16\r\n\tmul dx\r\n\tmov [si + 11],ax\t;total in kb\r\n\tmov [si + 13],dx\t;total in kb\r\n\tmov ax,16\r\n\tmul bx\r\n\tmov [si + 19],ax\t;free in kb\r\n\tmov [si + 21],dx\t;free in kb\r\n\tmov ax,4b00h\r\n\tcall intrt\r\n\tmov [si + 7],bx \t;active handles\r\n\tmov ax,5801h\r\n\tcall intrt\r\n\tmov [si + 17],cx\t;num mappable physical pages\r\n\r\n\tmov al,[si+2]\r\n\tmov ah,0\r\n\tshl ax,4\r\n\tmov cl,ah\r\n\tshr al,4\r\n\tmov ah,0\r\n\tmov ch,0\r\n\tinvoke printf, CStr(\"EMS version: %u.%u\",lf), cx, ax\r\n\tmov ax,[si+3]\r\n\tmov cx,[si+5]\r\n\tmov edx,[si+11]\r\n\tmov ebx,[si+19]\r\n\tinvoke printf, CStr('16 kB pages total/free: %u/%u (%lu/%lu kB)',lf),ax,cx,edx,ebx\r\n\tmov ax,  [si+0]\r\n\tinvoke printf, CStr('EMS page frame: %04X',lf),ax\r\n\tmov ax,  [si+7]\r\n\tinvoke printf, CStr('Active handles: %u',lf),ax\r\n\tmov ax,  [si+17]\r\n\tinvoke printf, CStr('physical pages: %u',lf),ax\r\n\r\n\tmov ax,5900h\r\n\tlea di,[si+23]\r\n\tpush ds\r\n\tpop es\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz ems59\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=5900h (get EM hardware information) failed, status=%X\",lf),ax\r\n\tjmp ems59done\r\nems59:\r\n\tmov ax, [si+23]\r\n\tshl ax, 4\r\n\tinvoke printf, CStr('Raw pages size: %u',lf),ax\r\n\tmov ax, [si+25]\r\n\tinvoke printf, CStr('Alternate map register sets: %u',lf),ax\r\n\tmov ax, [si+27]\r\n\tinvoke printf, CStr('Mapping context size: %u',lf),ax\r\n\tmov ax, [si+29]\r\n\tinvoke printf, CStr('DMA register sets: %u',lf),ax\r\nems59done:\r\n\tret\r\nreadd endp\r\n\r\n;--- display raw pages\r\n\r\nrpages proc\r\n\tmov ax,5901h\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjnz exit\r\n\tinvoke printf, CStr(\"raw pages total/free: %u/%u\",lf),dx,bx\r\nexit:\r\n\tret\r\nrpages endp\r\n\r\n;*** display EMS handle information\r\n\r\nxvhandle proc near\r\n\r\nlocal\twHandles:WORD\r\n\r\n\tmov ax,5402h\t\t;get number of handles in BX\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=5402h (get number of handles) failed, status=%X\",lf),ax\r\n\tjmp done\r\n@@:\r\n\tinvoke printf, CStr(\"(max) size of handle table: %u\",lf),bx\r\n\r\n\tpush ds\r\n\tpop es\r\n\tmov di,offset buffer\r\n\tmov ax,5400h\t\t;get handle directory\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=5400h (get handle directory) failed, status=%X\",lf),ax\r\n\tjmp done\r\n@@:\r\n\tmovzx ax,al\r\n\tmov wHandles,ax\r\n\tand ax, ax\r\n\tjz done\r\n\tinvoke printf, CStr(\"Handle Name     Pages     KB\",lf)\r\n\tinvoke printf, CStr(\"-----------------------------\",lf)\r\n\tmov cx,wHandles\r\nnexthdl:\t\t\t\t\t\t;<------\r\n\tpush cx\r\n\tmov ax,[di]\r\n\tinvoke printf, CStr(\"%6u \"), ax\r\n\tlea si,[di+2]\r\n\tmov cx,8\r\nnextchar:\r\n\tlodsb\r\n\tcmp al,0\r\n\tjnz @F\r\n\tmov al,' '\r\n@@:\r\n\tmov dl,al\r\n\tmov ah,2\r\n\tint 21h\r\n\tloop nextchar\r\n\t\r\n\tmov dx,[di]\r\n\tmov ax,4c00h\t\t;get handle pages\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\n\tinvoke printf, CStr(\"    ?      ?\",lf)\r\n\tjmp invalhdl\r\n@@:\r\n\tmovzx ebx,bx\r\n\tadd [totalpg],ebx\r\n\tpush bx\r\n\tinvoke printf, CStr(\"%5u\"), bx\r\n\tpop bx\r\n\tmov ax,16\r\n\tmul bx\r\n\tpush dx\r\n\tpush ax\r\n\tpop eax\r\n\tadd [totalkb],eax\r\n\tinvoke printf, CStr(\"%8lu\",lf), eax\r\ninvalhdl:\r\n\tpop cx\r\n\tadd di,10\r\n\tloop nexthdl\r\n\tinvoke printf, CStr(\"-----------------------------\",lf)\r\n\tmov eax,dword ptr [totalpg+0]\r\n\tmov edx,dword ptr [totalkb+0]\r\n\tinvoke printf, CStr(\"               %5lu%8lu\",lf), eax, edx\r\ndone:\r\n\tret\r\nxvhandle endp\r\n\r\nsearchpage proc\r\n\tpush di\r\n@@:\r\n\tcmp si,[di]\r\n\tjz @F\r\n\tadd di,4\r\n\tloop @B\r\n\tstc\r\n@@:\r\n\tmov ax,[di+2]\r\n\tpop di\r\n\tret\r\nsearchpage endp\r\n\r\n;*** display mapping segment -> physical page\r\n\r\nppages proc c\r\n\r\nlocal\tcount:word\r\nlocal\tnumpgs:word\r\n\r\n\tmov ax,5801h\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=5801h (get mappable pages) failed, status=%X\",lf),ax\r\n\tjmp exit\r\n@@:\r\n\tand cx,cx\r\n\tjz nopages\r\n\tmov numpgs,cx\r\n\tmov count,0\r\n\tmov di,offset buffer\r\n\tpush ds\r\n\tpop es\r\n\tmov ax,5800h\t\t\t\t;get mappable physical address array\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=5800h (get mappable pages) failed, status=%X\",lf),ax\r\n\tjmp exit\r\n@@:\r\n\tinvoke printf, CStr(\"Segm:Pg Segm:Pg Segm:Pg Segm:Pg\",lf)\r\n\tinvoke printf, CStr(\"-------------------------------\",lf)\r\n\txor si,si\r\nppage3:\r\n\tmov ax,si\r\n\tinvoke printf,CStr(\"%04X:\"),ax\r\n\tmov cx,numpgs\r\n\tcall searchpage\r\n\tjc @F\r\n\tinvoke printf,CStr(\"%2u\"),ax\r\n\tjmp ppage4\r\n@@:\r\n\tinvoke printf, CStr(\"--\")\r\nppage4:\r\n\r\n\tinvoke printf,CStr(\" \")\r\n\tinc count\r\n\ttest byte ptr count,3\r\n\tjnz @F\r\n\tinvoke printf, CStr(lf)\r\n@@:\r\n\tadd si,400h\r\n\tjnz ppage3\r\n\tinvoke printf, CStr(lf)\r\nexit:\r\n\tret\r\nnopages:\r\n\tinvoke printf, CStr(\"no mappable pages\",lf)\r\n\tret\r\nppages endp\r\n\r\nintrt proc near\r\n\tmov fc,ax\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz intrt1\r\n\tpush ax\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tmov cx,fc\r\n\tinvoke printf, CStr(\"function:%X - RC:%X\",lf),cx,ax\r\n\tpop ax\r\n\tmov bx,0000\r\nintrt1:\r\n\tret\r\nintrt endp\r\n\r\ndescout proc near\r\n\tmov ah,[di+7]\t\t;base bits 31..24\r\n\tmov al,[di+4]\t\t;base bits 23..16\r\n\tmov cx,[di+2]\t\t;base bits 15..0\r\n\tinvoke printf, CStr(\"%08lX:\"), ax::cx\r\n\tmov ax,[di+0]\t\t;limit bits 15..0\r\n\tmov cx,[di+5]\r\n\tinvoke printf, CStr(\"%04X,%04X\",lf),ax,cx\r\n\tadd di,8\r\n\tstc\r\n\tret\r\ndescout endp\r\n\r\n;*** display vcpi information\r\n\r\nvcpi proc near\r\n\r\n\tmov ax,4300h\r\n\tmov bx,0001h\t\t;get a ems page to ensure vcpi is active\r\n\tcall intrt\r\n\tmov [handle],dx\r\n\r\n\tmov ax,0DE00h\t\t;vcpi supported?\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjz @F\r\n\tinvoke printf, CStr(\"VCPI not supported\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmov al,bh\r\n\tmov ah,0\r\n\tmov bh,0\r\n\tinvoke printf, CStr(\"VCPI version: %u.%u\",lf),ax,bx\r\n\r\n\tpush ds\r\n\tpop es\r\n\tmov di,offset buffer\r\n\tmov si,offset gdttab\t;DS:SI -> 3 GDT descriptors\r\n\tmov ax,0DE01h\t\t\t;get protected mode interface\r\n\tint 67h\r\n\t.if (ah == 0)\r\n\t\tinvoke printf, CStr(\"offset for protected-mode switch:%08lX\",lf), ebx\r\n\t\tsub di,offset buffer\r\n\t\tmovzx edi,di\r\n\t\tshl edi, 10 \t;440h -> 110000\r\n\t\tmov eax, edi\r\n\t\tinvoke printf, CStr(\"Start free address space: %lX\",lf), eax\r\n\t\tmov di,offset gdttab\r\n\t\tinvoke printf, CStr('1. VCPI descriptor: ')\r\n\t\tcall descout\r\n\t\tinvoke printf, CStr('2. VCPI descriptor: ')\r\n\t\tcall descout\r\n\t\tinvoke printf, CStr('3. VCPI descriptor: ')\r\n\t\tcall descout\r\n\t.else\r\n\t\tmov al,ah\r\n\t\tmov ah,0\r\n\t\tinvoke printf, CStr(\"int 67h, ax=DE01 failed, ah=%X\",lf),ax\r\n\t.endif\r\n\tmov ax,0DE03h\t\t;num free 4K pages\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=DE03 failed, ah=%X\",lf),ax\r\n\tjmp _de02\r\n@@:\r\n\tinvoke printf, CStr(\"free 4K pages: %lu\",lf),edx\r\n_de02:\r\n\tmov ax,0DE02h\t\t;maxAddr of a 4K page\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=DE02 failed, ah=%X\",lf),ax\r\n\tjmp _de0a\r\n@@:\r\n\tinvoke printf, CStr(\"highest address of pages: %lX\",lf),edx\r\n_de0a:\r\n\tmov ax,0DE0Ah\t\t;get interrupt vector mappings\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjz @F\r\n\tmov al,ah\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"int 67h, ax=DE0A failed, ah=%X\",lf)\r\n\tjmp exit\r\n@@:\r\n\tinvoke printf, CStr(\"master/slave PIC base: %02X/%02X\",lf),bx,cx\r\nexit:\r\n\tmov dx,[handle]\r\n\tcmp dx,0000\r\n\tjz @F\r\n\tmov ax,4500h\t\t;release EMS page\r\n\tcall intrt\r\n@@:\r\n\tret\r\nvcpi endp\r\n\r\n;--- BX = handle\r\n;--- CX = size\r\n\r\nread0 proc\r\n\tpush cx\r\n\tinvoke printf, CStr(\"read IOCTL device [\")\r\n\tmov al, [buffer]\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"%X\"),ax\r\n\tinvoke printf, CStr(\"]=\")\r\n\tpop cx\r\n\tmov dx, offset buffer\r\n\tmov ax,4402h\r\n\tint 21h\r\n\t.if (CARRY?)\r\n\t\tinvoke printf, CStr(\" failed\",lf)\r\n\t\tstc\r\n\t.else\r\n\t\tinvoke printf, CStr(\" ok (AX=%X):\"),ax\r\n\t\tclc\r\n\t.endif\r\n\tret\r\nread0 endp\r\n\r\n;--- display EMMXXXX0 device info\r\n\r\ndeviceinfo proc\r\n\tmov dx,offset emsstr1\r\n\tmov ax,3D00h\r\n\tint 21h\r\n\t.if (CARRY?)\r\n\t\tinvoke printf, CStr(\"open device EMMXXXX0 failed\",lf)\r\n\t\tmov dx,offset emsstr2\r\n\t\tmov ax,3D00h\r\n\t\tint 21h\r\n\t\t.if (CARRY?)\r\n\t\t\tinvoke printf, CStr(\"open device EMMQXXX0 failed\",lf)\r\n\t\t\tmov dx,offset emsstr3\r\n\t\t\tmov ax,3D00h\r\n\t\t\tint 21h\r\n\t\t\t.if (CARRY?)\r\n\t\t\t\tinvoke printf, CStr(\"open device EMMXXXQ0 failed\",lf)\r\n\t\t\t\tjmp exit\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t.endif\r\n\tpush ax\r\n\tpush dx\r\n\tinvoke printf, CStr(\"open device \")\r\n\tpop ax\r\n\tinvoke printf, ax\r\n\tinvoke printf, CStr(\" ok\",lf)\r\n\tpop bx\r\n\tmov byte ptr [buffer+0], 0\r\n\tmov byte ptr [buffer+1], -1\r\n\tmov dword ptr [buffer+2], -1\r\n\tmov cx,6\r\n\tcall read0\r\n\t.if (!CARRY?)\r\n\t\tmov ax, word ptr [buffer+0]\r\n\t\tmov cx, word ptr [buffer+2]\r\n\t\tmov dx, word ptr [buffer+4]\r\n\t\tinvoke printf, CStr(\"%X %X %X\",lf),ax,cx,dx\r\n\t.endif\r\n\tmov byte ptr [buffer+0], 2\r\n\tmov byte ptr [buffer+1], -1\r\n\tmov cx,2\r\n\tcall read0\r\n\t.if (!CARRY?)\r\n\t\tmovzx ax, [buffer+0]\r\n\t\tmovzx cx, [buffer+1]\r\n\t\tinvoke printf, CStr(\"%u.%u\",lf),ax,cx\r\n\t.endif\r\n\tmov ah, 3Eh\r\n\tint 21h\r\nexit:\r\n\tret\r\ndeviceinfo endp\r\n\r\nstart:\r\n\tpush sp\r\n\tpop ax\r\n\tcmp ax,sp\t;8086?\r\n\tjnz exit\t;exit silently\r\n\tpushf\r\n\tpushf\r\n\tpop ax\r\n\tor ah,70h\r\n\tpush ax\r\n\tpopf\r\n\tpushf\r\n\tpop ax\r\n\tpopf\r\n\tand ah,0f0h ;if bits 12-14 are 0, it is a 80286\r\n\tjz exit \t;Z if 80286\r\n\tpush cs\r\n\tpop ds\r\n\tmov ax,ss\r\n\tmov bx,sp\r\n\tshr bx,4\r\n\tadd bx,ax\r\n\tmov ax,es\r\n\tsub bx,ax\r\n\tmov ah,4Ah\r\n\tint 21h\r\n\tsub bx,10h\r\n\tshl bx,4\r\n\tpush ds\r\n\tpop ss\r\n\tmov sp,bx\r\n\r\n\tmov di,offset namtab\t;clear BSS+stack\r\n\tpush es\r\n\tpush ds\r\n\tpop es\r\n\tmov cx,sp\r\n\tsub cx,di\r\n\txor ax,ax\r\n\tshr cx,1\r\n\tcld\r\n\trep stosw\r\n\tpop es\r\n\r\n\tcall main\r\nexit:\r\n\tmov ah,4Ch\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Tools/EMSSTAT/MAKE.BAT",
    "content": "@echo off\r\nrem creates EMSSTAT.EXE with JWasm\r\njwasm -c -nologo -mz EMSSTAT.ASM\r\n"
  },
  {
    "path": "Tools/EMSSTAT/PRINTF.INC",
    "content": "\r\n;--- simple printf() implementation\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- outb is expected to be onto stack\r\n\r\nltob PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- ds=dgroup, ss don't need to be dgroup\r\n\r\nprintf PROC c uses si di bx fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\nnextdigit:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb digitsdone\r\n\tcmp BYTE PTR [si],'9'\r\n\tja digitsdone\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul cx,bx,10\t\t;cx = bx * 10\r\n\tadd ax,cx\r\n\tmov bx,ax\r\n\tjmp nextdigit\r\n\r\ndigitsdone:\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tmov al,'%'\r\n\tjmp @@L359\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Tools/JEMFBHLP/JEMFBHLP.ASM",
    "content": "\r\n;*** save some interrupt vectors the way MS-DOS does\r\n;*** needed for FreeDOS if Jemm386's FASTBOOT option is used\r\n\r\n\t.286\r\n\t.MODEL SMALL\r\n\r\nIODAT   struct\r\ncmdlen  db      ?       ;+ 0:size\r\nunit    db      ?       ;+ 1:\r\ncmd     db      ?       ;+ 2\r\nstatus  dw      ?       ;+ 3\r\n        db      8 dup (?); reserved\r\nmedia   db      ?       ;+ 0d\r\ntrans   dd      ?       ;+ 0e\r\ncount   dw      ?       ;+ 12   init:offset parameter line\r\nstart   dw      ?       ;+ 14   init:segment parameter line\r\ndrive   db      ?       ;+ 16\r\nIODAT   ends\r\n\r\n\t.CODE\r\n\r\n\tdw -1\r\n\tdw -1\r\n\tdw 8000h\t\t\t\t  ;attribute\r\n\tdw offset devstrat\t\t  ;device strategy\r\ndevc dw offset devintfirst\t  ;device interrupt\r\ndevname db 'JEMFBHP$'\r\n\r\ncmdptr\tdd 1 dup (?)\r\n\r\ndevstrat proc far\r\n\tmov cs:word ptr[cmdptr+0],bx\r\n\tmov cs:word ptr[cmdptr+2],es\r\n\tret\r\ndevstrat endp\r\n\r\ndevint proc far\r\n\tpush ds\r\n\tpush bx\r\n\tlds bx,cs:[cmdptr]\r\n\tmov word ptr [bx.IODAT.status],100h\r\n\tpop bx\r\n\tpop ds\r\n\tret\r\ndevint endp\r\n\r\nint19 proc\r\n\tcld\r\n\tpush 70h\r\n\tpop ds\r\n\txor ax,ax\r\n\tmov es,ax\r\n\tmov si,100h\r\n\tmov cx,NUMVECS\r\nnextitem:\r\n\tlodsb\r\n\tmov di,ax\r\n\tshl di,2\r\n\tmovsw\r\n\tmovsw\r\n\tloop nextitem\r\n\tint 19h\r\nint19 endp\r\n\r\nresident label byte\r\n\r\nvecstable db 15h,19h\r\nNUMVECS equ $ - vecstable\r\n\r\ndevintfirst proc far\r\n\tpusha\r\n\tpush ds\r\n\tpush es\r\n\tlds bx,cs:[cmdptr]\r\n\tmov word ptr [bx.IODAT.status],100h\r\n\tcmp [bx.IODAT.cmd],0\t;init call?\r\n\tjnz exit\r\n\tmov word ptr [bx.IODAT.trans+0],0\r\n\tmov word ptr [bx.IODAT.trans+2],cs\r\n\tmov cs:[devc],offset devint\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\tcmp al,80h\t\t;Himem must not be installed yet\r\n\tjnz @F\r\n\tpush cs\r\n\tpop ds\r\n\tmov dx,offset str2\r\n\tmov ah,9\r\n\tint 21h\r\n\tjmp exit  \r\n@@:\r\n\txor ax,ax\r\n\tmov es,ax\r\n\tmov ax, es:[19h*4+2]\t;int 19h must be unmodified (segment >= E000)\r\n\tcmp ax, 0E000h\r\n\tjc exit\r\n\tmov word ptr [bx.IODAT.trans+0],offset resident\r\n\r\n\tpush 0070h\r\n\tpop es\r\n\txor ax,ax\r\n\tmov ds,ax\r\n\tmov di,100h\r\n\tmov bx,offset vecstable\r\n\tmov cx,NUMVECS\r\nnextitem:\r\n\tmov al,cs:[bx]\r\n\tinc bx\r\n\tmov ah,0\r\n\tmov si,ax\r\n\tshl si,2\r\n\tstosb\r\n\tmovsw\r\n\tmovsw\r\n\tloop nextitem\r\n\tmov word ptr ds:[19h*4+0],offset int19\r\n\tmov word ptr ds:[19h*4+2],cs\r\nexit:\r\n\tpop es\r\n\tpop ds\r\n\tpopa\r\n\tret\r\ndevintfirst endp\r\n\r\nstr2 db \"JEMFBHLP.EXE must be installed *before* Himem\",13,10,'$'\r\n\r\nstr1 db \"JEMFBHLP is needed for FreeDOS only. It saves some interrupt vectors\",13,10\r\n\tdb \"the way MS-DOS does, thus allowing to set Jemm386's FASTBOOT option.\",13,10\r\n\tdb \"It must be loaded in (FD)CONFIG.SYS before the XMS host. Example:\",13,10\r\n\tdb \"DEVICE=JEMFBHLP.EXE\",13,10\r\n\tdb \"DEVICE=HIMEM.EXE [or (Q)HIMEM.SYS]\",13,10\r\n\tdb \"DEVICE=JEMM386.EXE FASTBOOT\",13,10\r\n\tdb '$'\r\nmain:\r\n\tmov dx,offset str1\r\n\tpush cs\r\n\tpop ds\r\n\tmov ah,9\r\n\tint 21h\r\n\tmov ax,4C00h\r\n\tint 21h\r\n\r\n\t.stack 1024\r\n\r\n\tEND main\r\n\r\n"
  },
  {
    "path": "Tools/JEMFBHLP/MAKE.BAT",
    "content": "@echo off\r\nrem create JEMFBHLP.EXE with JWasm\r\njwasm -c -nologo -mz -Fl JEMFBHLP.ASM\r\n"
  },
  {
    "path": "Tools/JLOAD/DEBUG.INC",
    "content": "\r\n;*** debug macros and equates\r\n\r\n?USEMONO equ 0\t;1=use monochrome video mode\r\n\r\nifndef ?RMDBG\r\n?RMDBG   = 0\t;log real-mode\r\nendif\r\nifndef ?INITDBG\r\n?INITDBG = 0\t;log init\r\nendif\r\nifndef ?PEDBG\r\n?PEDBG   = 0\t;log PE loader\r\nendif\r\nifndef ?IODBG\r\n?IODBG   = 0\t;log IO trapping\r\nendif\r\nifndef ?PAGEDBG\r\n?PAGEDBG = 0\t;log page memory\r\nendif\r\nifndef ?JLMDBG\r\n?JLMDBG  = 0\t;log jlm handling\r\nendif\r\nifndef ?DMADBG\r\n?DMADBG  = 0\t;log DMA handling\r\nendif\r\nifndef ?V86HOOKDBG\r\n?V86HOOKDBG  = 0\t;log v86 hook chain handling\r\nendif\r\nifndef ?INT2FHOOKDBG\r\n?INT2FHOOKDBG  = 0\t;log Int2F hook handling\r\nendif\r\n\r\nifdef _DEBUG\r\n?LOG = ?INITDBG + ?PEDBG + ?IODBG + ?PAGEDBG + ?JLMDBG + ?DMADBG + ?V86HOOKDBG + ?INT2FHOOKDBG\r\nelse\r\n?LOG = 0\r\nendif\r\n\r\ndprintf proto c :ptr, :vararg\r\n\r\nifdef _DEBUG\r\n\r\n@dprintf macro bCond, fmt, args:vararg\r\n if bCond\r\n  ifnb <args>\r\n\tinvoke dprintf, CStr(fmt), args\r\n  else\r\n\tinvoke dprintf, CStr(fmt)\r\n  endif\r\n endif\r\nendm\r\n\r\nelse\r\n\r\n@dprintf textequ <;>\r\n\r\nendif\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/DPRINTF.INC",
    "content": "\r\n;--- printf for debug displays, 32-bit\r\n\r\n;--- i64toa(long long n, char * s, int base);\r\n;--- convert 64-bit long long to string\r\n\r\ni64toa proc stdcall uses edi number:qword, outb:ptr, base:dword\r\n\r\n\tmov ch,0\r\n\tmov edi, base\r\n\tmov eax, dword ptr number+0\r\n\tmov esi, dword ptr number+4\r\n\tcmp edi,-10\r\n\tjne @F\r\n\tneg edi\r\n\tand esi,esi\r\n\tjns @F\r\n\tneg esi\r\n\tneg eax\r\n\tsbb esi,0\r\n\tmov ch,'-'\r\n@@:\r\n\tmov ebx,outb\r\n\tadd ebx,22\r\n\tmov byte ptr ss:[ebx],0\r\n@@nextdigit:\r\n\tdec ebx\r\n\txor edx,edx\r\n\txchg eax,esi\r\n\tdiv edi\r\n\txchg eax,esi\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[ebx],dl\r\n\tmov edx, eax\r\n\tor edx, esi\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tdec ebx\r\n\tmov ss:[ebx],ch\r\n@@:\r\n\tmov eax,ebx\r\n\tret\r\n\r\ni64toa endp\r\n\r\n;--- dprintf has to preserve eflags, but usage of local vars will include a \"sub esp,xxx\"!\r\n;--- hence a special prologue is required:\r\n\r\n@dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms\r\n\tpush ebp\r\n\tmov ebp,esp\r\n\tlea esp,[esp-localbyte]\r\n\texitm %localbyte\r\nendm\r\n\r\n\tOPTION PROLOGUE: @dprologue\r\n\r\ndprintf proc c public fmt:ptr, args:vararg\r\n\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal size_:dword\r\nlocal fillchr:dword\r\nlocal szTmp[24]:byte\r\n\r\n\tpushad\r\n\tpushfd\r\n\tcld\r\n\tlea edi,args\r\n@@L335:\r\n\tmov esi,fmt\r\nnextchar:\r\n\tlodsb cs:[esi]\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\tpopfd\r\n\tpopad\r\n\tret\r\n\r\nformatitem:\r\n\tpush offset @@L335\r\n\txor edx,edx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR cs:[esi],'-'\r\n\tjne @F\r\n\tdec bl\r\n\tinc esi\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR cs:[esi],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc esi\r\n@@:\r\n\tmov [fillchr],ecx\r\n\tmov ebx,edx\r\n\r\n\t.while ( byte ptr cs:[esi] >= '0' && byte ptr cs:[esi] <= '9' )\r\n\t\tlodsb cs:[esi]\r\n\t\tsub al,'0'\r\n\t\tmovzx eax,al\r\n\t\timul ecx,ebx,10\t\t;ecx = ebx * 10\r\n\t\tadd eax,ecx\r\n\t\tmov ebx,eax\r\n\t.endw\r\n\r\n\tmov [size_],ebx\r\n\tcmp BYTE PTR cs:[esi],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc esi\r\n@@:\r\n\tlodsb cs:[esi]\r\n\tmov [fmt],esi\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tand al,al\r\n\tjnz @F\r\n\tpop eax\r\n\tjmp done\r\nhandle_c:\r\n\tmov eax, ss:[edi]\r\n\tadd edi, 4\r\n@@:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov esi, ss:[edi]\r\n\tadd edi,4\r\n\tjmp print_string\r\nhandle_d:\r\nhandle_i:\r\n\tmov ebx,-10\r\n\tjmp @F\r\nhandle_u:\r\n\tmov ebx, 10\r\n\tjmp @F\r\nhandle_x:\r\n\tmov ebx, 16\r\n@@:\r\n\txor edx,edx\r\n\tmov eax, ss:[edi]\r\n\tadd edi,4\r\n\tcmp longarg,1\r\n\tjnz @F\r\n\tmov edx, ss:[edi]\r\n\tadd edi,4\r\n\tjmp printnum\r\n@@:\r\n\tand ebx,ebx\r\n\tjns @F\r\n\tcdq\r\n@@:\r\nprintnum:\r\n\tlea esi, szTmp\r\n\tinvoke i64toa, edx::eax, esi, ebx\r\n\tmov esi, eax\r\n\r\nprint_string:\t\t;print string ESI, size EAX\r\n\tmov eax, esi\r\n\t.while byte ptr ss:[esi]\r\n\t\tinc esi\r\n\t.endw\r\n\tsub esi, eax\r\n\txchg eax, esi\r\n\tmov ebx,size_\r\n\tsub ebx,eax\r\n\t.if flag == 1\r\n\t\t.while sdword ptr ebx > 0\t;print leading filler chars\r\n\t\t\tmov eax, [fillchr]\r\n\t\t\tcall handle_char\r\n\t\t\tdec ebx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr ss:[esi]\r\n\t\tlodsb ss:[esi]\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sdword ptr ebx > 0\t\t;print trailing spaces\r\n\t\tmov eax, [fillchr]\r\n\t\tcall handle_char\r\n\t\tdec ebx\r\n\t.endw\r\n\tretn\r\n\r\nhandle_char:\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov al,13\r\n\tcall @F\r\n\tmov al,10\r\n@@:\r\n\tcall VPUTCHR\r\n\tretn\r\n\r\ndprintf endp\r\n\r\nOPTION PROLOGUE: prologuedef\r\n"
  },
  {
    "path": "Tools/JLOAD/DPRNTF16.INC",
    "content": "\r\n;--- printf for debug displays, 16-bit\r\n\r\n;--- itoa(long n, char * s, int base);\r\n;--- convert 32-bit long to string\r\n\r\nltoa PROC stdcall uses edx edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltoa ENDP\r\n\r\n;--- dprintf is to preserve registers & flags; needs a special prologue:\r\n\r\n@dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms\r\n\tpush bp\r\n\tmov bp,sp\r\n\tlea esp,[esp-localbyte]   ;there exists no 16-bit \"lea sp,[sp-xx]\"\r\n\texitm %localbyte\r\nendm\r\n\r\n\tOPTION PROLOGUE: @dprologue\r\n\r\n;--- it's assumed cs=DGROUP (model tiny)\r\n;--- no assume for SS & DS, ES unused.\r\n\r\ndprintf proc c public uses ds fmt:ptr, args:vararg\r\n\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal size_:word\r\nlocal fillchr:word\r\nlocal szTmp[12]:byte\r\n\r\n\tpusha\r\n\tpushf\r\n\tpush ds\r\n\tpush cs\r\n\tpop ds\r\n\tcld\r\n\tlea di,args\r\n@@L335:\r\n\tmov si,fmt\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall VPUTCHR\r\n\tjmp nextchar\r\ndone:\r\n\tpop ds\r\n\tpopf\r\n\tpopa\r\n\tret\r\n\r\nformatitem:\r\n\tpush offset @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bl\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fillchr],cx\r\n\tmov bx,dx\r\n\r\n\t.while ( byte ptr [si] >= '0' && byte ptr [si] <= '9' )\r\n\t\tlodsb\r\n\t\tsub al,'0'\r\n\t\tcbw\r\n\t\timul cx,bx,10\t\t;ecx = ebx * 10\r\n\t\tadd ax,cx\r\n\t\tmov bx,ax\r\n\t.endw\r\n\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tand al,al\r\n\tjnz @F\r\n\tpop ax\r\n\tjmp done\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tcall VPUTCHR\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\tjmp print_string\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @F\r\nhandle_u:\r\n\tmov bx, 10\r\n\tjmp @F\r\nhandle_x:\r\n\tmov bx, 16\r\n@@:\r\n\txor dx,dx\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tcmp longarg,1\r\n\tjnz @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n\tjmp printnum\r\n@@:\r\n\tand bx,bx\r\n\tjns @F\r\n\tcdq\r\n@@:\r\nprintnum:\r\n\tlea si, szTmp\r\n\tpush eax\r\n\tinvoke ltoa, dx::ax, si, bx\r\n\tmov si, ax\r\n\tpop eax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall print_string\r\n\tpop ds\r\n\tretn\r\n\r\nprint_string:\t\t;print string SI\r\n\tmov ax, si\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si, ax\r\n\txchg ax, si\r\n\tmov bx,size_\r\n\tsub bx, ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov ax, [fillchr]\r\n\t\t\tcall VPUTCHR\t;print leading filler chars\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb [si]\r\n\t\tcall VPUTCHR\t\t;print char of string\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov ax, [fillchr]\r\n\t\tcall VPUTCHR\t\t;print trailing spaces\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nVPUTCHR:\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov al,13\r\n\tcall @F\r\n\tmov al,10\r\n@@:\r\n\tpush bx\r\n\txor bx, bx\r\n\tmov ah, 0Eh\r\n\tint 10h\r\n\tpop bx\r\n\tretn\r\n\r\ndprintf endp\r\n\r\n\tOPTION PROLOGUE: prologuedef\r\n"
  },
  {
    "path": "Tools/JLOAD/History.txt",
    "content": "\r\n v5.87:\r\n   - Linux.mak added to create JLOAD.EXE on Linux.\r\n\r\n v5.86:\r\n   - Jemm's control notifications handled (DEVICE_REBOOT_NOTIFY).\r\n   - fixed: 16-bit debug displays.\r\n   - implemented Hook_V86_Fault/Unhook_V86_Fault, using Jemm's service\r\n     table entry pV86Faults (v5.86).\r\n\r\n v5.85:\r\n   - debug displays now done with @dprintf().\r\n   - V86 hooks won't modify IDT anymore.\r\n\r\n v5.84:\r\n   - trap handling changed: Jemm's IO trap table is no longer imported,\r\n     instead the old (=Jemm's) handler is called if the port isn't trapped\r\n     by VMM. This allows to trap ISA DMA ports that are already trapped\r\n     by Jemm.\r\n\r\n v5.83:\r\n   - removed the option to link with MS COFF linker - current versions\r\n     won't accept a base of 0xf8400000.\r\n   - released under MIT license.\r\n   - fixed: import of Jemm's IO port trap ranges did additionally trap\r\n     \"holes\" in the DMA16 page register port range D0-DF.\r\n   - flags PC_CACHEDIS and PC_CACHEWT supported for _PageCommitPhys().\r\n   - added _Allocate_GDT_Selector(), _Free_GDT_Selector(), Get_DDB().\r\n   - added possibility to suppress JLoad result msgs.\r\n v5.82:\r\n   - fixed: JLMs with API didn't work correctly.\r\n   - int 2Fh, ax=1684h: when a JLM entry point is returned ( in ES:DI ),\r\n     AL is set to 0 to indicate success.\r\n v5.76:\r\n   - linker changed from Digital Mars OPTLINK to jwlink\r\n   - VMM.ASM: bugfix in _PageFree()\r\n v5.73:\r\n   - bugfix: Hook_V86_Int_Chain might have messed the stack.\r\n   - automatically alloc/free V86 callback if v86 API is to be\r\n     installed by a device. \r\n   - when the first v86 API is installed, v86 int 2Fh is hooked to\r\n     handle ax=1684h.\r\n   - Option -u added.\r\n v5.72:\r\n   - switched to JWasm assembler\r\n v5.71:\r\n   - bugfix: in v5.70, JLoad's assumtion about location of Jemm's TSS\r\n     was wrong.\r\n v5.69:\r\n   - both '-' and '/' accepted for switches.\r\n   - MoveMemory added to Jemm's service table.\r\n   - DMA copy to/from buffer functions added.\r\n v5.68:\r\n   - bugfix: DMA lock ignored flag to check for 64 kB border crossing.\r\n v5.60:\r\n   - initial.\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/JLOAD.ASM",
    "content": "\r\n;--- 16-bit part of JLoad\r\n;--- JLoad loads a 32bit PE binary in Jemm's address space\r\n;--- best viewed with TABSIZE 4\r\n\r\n;--- these segment definitions must be BEFORE .model since alignment is 4\r\n\r\n_TEXT segment dword public 'CODE'\r\n_TEXT ends\r\n_DATA segment dword public 'DATA'\r\n_DATA ends\r\n\r\n\t.model tiny\r\n\t.dosseg\r\n\t.386\r\n\toption casemap:none\r\n\toption proc:private\r\n\r\n\tinclude jsystem.inc\r\n\tinclude jlm.inc\r\n\tinclude jload.inc\r\n\tinclude debug.inc\r\n\tinclude winnt.inc\r\n\r\n;--- DStr() define a DOS string in CCONST\r\n\r\nDStr macro text:vararg\r\nlocal sym\r\nCCONST segment\r\n ifidni <text>,<\"\">\r\nsym db '$'\r\n else\r\nsym db text,'$'\r\n endif\r\nCCONST ends\r\n\texitm <offset sym>\r\nendm\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\nCCONST segment\r\n ifidni <text>,<\"\">\r\nsym db 0\r\n else\r\nsym db text,0\r\n endif\r\nCCONST ends\r\n\texitm <offset sym>\r\nendm\r\n\r\n\r\n@tracejmp macro target\r\nif ?RMDBG\r\n\tjmp target\r\nendif\r\n\tendm\r\n\r\nENVIRON \t\tequ 2Ch\r\n\r\nSTATUS_OK       equ 0100h   ; driver is initialized and ok\r\nSTATUS_BAD      equ 8103h   ; driver couldn't be installed\r\n\r\nCMD_INIT\t\tequ 0\t\t; init command (used when installed)\r\n\r\nFMODE_LFN\t\tequ 1\t\t; fMode flags\r\n\r\nFOPTION_QUIET\tequ 1\t\t; be quiet, don't display information texts\r\n\r\n?MAXPATH\t\tequ 260\r\n\r\ncr\t\t\t\tequ 0dh\r\nlf\t\t\t\tequ 0ah\r\n\r\n?CATCHEXC06\t\tequ 0\t\t;std=0, 1=catch Exc 06 (pretty useless)\r\n\r\n;*** define segments ***\r\n\r\n\t.code\r\n\r\n; device driver header\r\n\r\n\tdd -1\r\n\tdw 8000h\r\n\tdw offset strategy\r\n\tdw offset interrupt\r\n\tdb 'JLOAD$$$'\r\ndwRequest dd 0\r\nstrategy:\r\n\tmov word ptr cs:[dwRequest+0],bx\r\n\tmov word ptr cs:[dwRequest+2],es\r\n\tretf\r\n\r\nCCONST segment dword use16 public 'CODE'\r\nCCONST ends\r\nBEGDATA segment para use16 public 'CODE'\r\nBEGDATA ends\r\n_DATA segment dword use16 public 'DATA'\r\n_DATA ends\r\n_BSS segment dword use16 public 'BSS'\r\n_BSS ends\r\nSTACK segment para use16 stack  'STACK'\r\n\tdb 1024 dup (?)\r\nstacktop label byte\r\nSTACK ends\r\n\r\nDGROUP group _TEXT,CCONST,BEGDATA,_DATA,_BSS,STACK\r\n\r\n;--- cpu descriptor\r\n\r\ndesc struc\r\n  limit     dw ?       ; segment limit\r\n  base00_15 dw ?       ; low word of base address\r\n  base16_23 db ?       ; high byte of base address\r\n            db ?       ; 93h = std ring 0 read/write segment\r\n  attr      db ?       ; attributes, limit 16-19\r\n  base24_31 db ?\r\ndesc ends\r\n\r\n;--- DOS device driver request header\r\n\r\nrequest_hdr struc\r\n  req_size  db ?       ; number of bytes stored\r\n  unit_id   db ?       ; unit ID code\r\n  cmd       db ?       ; command code\r\n  status    dw ?       ; status word\r\n  rsvd      db 8 dup (?)   ; reserved\r\nrequest_hdr ends\r\n\r\n;--- DOS device driver request for INIT\r\n\r\ninit_strc struc\r\n  init_hdr  db size request_hdr dup (?)\r\n  units     db ?       ; number of supported units\r\n  end_addr  dd ?       ; end address of resident part\r\n  cmd_line  dd ?       ; address of command line\r\ninit_strc ends\r\n\r\n;--- VCPI jump to protected mode structure\r\n\r\nV862PM\tSTRUC\r\nswCR3\t\tDD ? ; client's CR3 value\r\nswGDTOFFS\tDD ? ; offset of client's GDTR value\r\nswIDTOFFS\tDD ? ; offset of client's IDTR value\r\nswLDTR\t\tDW ? ; client's LDTR value\r\nswTR\t\tDW ? ; client's TR value\r\nswCSEIP \tDF ? ; entry point in client\r\nV862PM\tENDS\r\n\r\n;*** variables ***\r\n\r\n_DATA segment\r\n\r\n;--- GDT used for int 15h, ah=87h\r\n\r\ni15tab  label desc\r\n\tdesc <0,0,0,0,0,0>\r\n\tdesc <0,0,0,0,0,0>\r\n\tdesc <-1,0,0,93h,0CFh,0>\r\n\tdesc <-1,0,0,93h,0CFh,0>\r\n\tdesc <0,0,0,0,0,0>\r\n\tdesc <0,0,0,0,0,0>\r\n\r\nvcpisw\tV862PM <0,0,0,0,0,0>\r\n\r\n\talign 4\r\n\r\nvmms\tVMMS <<0,0,0,<0>>,<0,0,0,0,0,0,0>,-1,offset szLdrPath, offset szPgmName, offset PE_Hdr, offset buffer, 0>\r\nddb\t\tVxD_Desc_Block <?>\r\n\r\nif ?PMSTACK\r\ndwStack   dd 0\t\t\t;phys page for protected-mode stack\r\nendif\r\ndwPageTab dd 0\t\t\t;phys page for VMM page table\r\ndwVmmPage dd 0\t\t\t;phys page for VMM code/data\r\ndwBuffer  dd 0\t\t\t;linear address of buffer\r\ndwCmdLine dd 0\t\t\t;start of cmdline for JLM (terminated by CR or 00)\r\nif ?CATCHEXC06\r\noldint06  dd 0\r\nspsaved dw 0\r\nendif\r\ndfIDT\tdf 0\r\nhJemm\tdw -1\r\nwRet\tdw 0\r\nfMode\tdb 0\r\nfOption\tdb 0\t\t\t;additional option from cmdline (\"-q\")\r\n\r\n_DATA ends\r\n\r\nCCONST segment\r\n\r\ndUsage  db \"JLoad v\",?VERSIONHIGH+'0',\".\",@CatStr(!\",%?VERSIONLOW/10,%?VERSIONLOW mod 10,!\")\r\n\t\tdb \" Copyright japheth 2008-2026\",13,10\r\n\t\tdb \"JLoad loads JLMs (Jemm Loadable Modules) into Jemm's address space.\",13,10\r\n\t\tdb \"It can be run as a DOS device driver or from the command line.\",13,10\r\n\t\tdb \"usage:\",13,10\r\n\t\tdb \"  [DEVICE=] JLOAD.EXE [options] name_of_JLM [arguments]\",13,10\r\n\t\tdb \"  options:\",13,10\r\n\t\tdb \"  -q: quiet\",13,10\r\n\t\tdb \"  -u: unload a JLM\",13,10\r\n\t\tdb '$'\r\nszErrExp db \"' doesn't export a DDB\",13,10,'$'\r\n\r\nCCONST ends\r\n\r\n_BSS segment\r\n\r\nwVersion\tdw ?\t\t\t;DOS version major+minor\r\n\r\n\talign 4\r\n\r\nszLdrPath\tdb ?MAXPATH dup (?)\t;JLOAD full path\r\n\r\n_edata\tlabel byte\r\n\r\nszPgmName\tdb ?MAXPATH dup (?)\t;program name from command line\r\n\r\nMZ_Hdr\t\tdb 40h dup (?)\t\t;MZ header buffer (same memory as NE header\r\nPE_Hdr\t\tIMAGE_NT_HEADERS <?>\r\n\r\n;--- the buffer should be last. thus it is an additional 4 kB if the default\r\n;--- 1 kB stack is too small.\r\n\r\nbuffer\t\tdb 4096 dup (?) \t;4 kb buffer for copy operations\r\n\r\nexterndef _end:near\r\n\r\n_BSS ends\r\n\r\nCCONST segment\r\n\r\nszLF\tdb lf,0\r\n\r\nsig1\tdb 'EMMXXXX0',0\r\nsig2\tdb 'EMMQXXX0',0\t; ID if NOEMS specified\r\n\r\nCCONST ends\r\n\r\n_TEXT segment\r\n\r\n\tassume CS:DGROUP\r\n\tassume DS:DGROUP\r\n\tassume SS:DGROUP\r\n\tassume ES:DGROUP\r\n    \r\n\talign 4\r\n\r\nvmmcode label byte\r\nifdef __JWASM__\r\n ifdef __UNIX__\r\n%\tincbin <OUTD/JLoad32.bin>  \r\n else\r\n%\tincbin <OUTD\\JLoad32.bin>  \r\n endif\r\nelse\r\n%\tinclude OUTD\\JLoad32.inc\r\nendif\r\n\talign 4\r\n        \r\nSIZEVMM equ $ - offset vmmcode\r\n\r\n_IsJemmInstalled proc\r\n\r\n\t@dprintf ?RMDBG,<\"IsJemmInstalled enter\",lf>\r\n\tmov bx, -1\r\n\tmov dx, offset sig1\r\n\tmov ax, 3D00h\r\n\tint 21h\r\n\tjnc @@found\r\n\tmov dx, offset sig2\r\n\tmov ax, 3D00h\r\n\tint 21h\r\n\tjc @@nojemm\r\n@@found:\r\n\tmov bx,ax\r\n\txor ax,ax\r\n\tpush ax\r\n\tpush ax\r\n\tpush ax\r\n\tmov cx,6\t\t;read 6 bytes\r\n\tmov dx,sp\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tpop ax\t\t\t;version\r\n\tpop cx\t\t\t;API entry offs\r\n\tpop cx\t\t\t;API entry segm\r\n\tjc @@nojemm\r\n\tcmp ax,0028h\t;this is JEMM!\r\n\tjnz @@nojemm\r\n\tmov ax,bx\t\t;return the file handle\r\n\tclc\r\n\tret\r\n@@nojemm:\r\n\tcmp bx,-1\r\n\tjz @@noclose\r\n\tmov ah,3Eh\r\n\tint 21h\r\n@@noclose:\r\n\tstc\r\n\tret\r\n_IsJemmInstalled endp\r\n\r\n;*** read command line parameters\r\n;--- handles jload options /u and /q.\r\n;*** RC: Carry if error\r\n;***     NC: file to load in szPgmName\r\n;--- modifies DS,SI,DI!\r\n\r\nGetPgmParms proc\r\n\r\n\t@dprintf ?RMDBG,<\"GetPgmParms enter\",lf>\r\n\tlds si,[dwCmdLine]\r\n\tsub cx,cx\r\n\tmov al,0\r\nnextchr:\r\n\tmov ah,al\r\n\tlodsb\r\n\tcmp al,0\r\n\tjz error\r\n\tcmp al,13\r\n\tjz error\r\n\tcmp al,' '\t\t;skip wspaces\r\n\tjbe nextchr\r\n\tcmp al,'/'\r\n\tjz @F\r\n\tcmp al,'-'\r\n\tjnz parmfound\r\n@@:\r\n\tmov al,1\r\n\tjmp nextchr\r\nerror:\r\n\tstc\r\n\tret\r\nparmfound:\r\n\tcmp ah,1\t\t;option mode?\r\n\tjnz nooption\r\n\tor al,20h\r\n\tcmp al,'q'\r\n\tjnz @F\r\n\tor es:[fOption], FOPTION_QUIET\r\n\tjmp nextchr\r\n@@:\r\n\tcmp al,'u'\r\n\tjnz error\r\n\tor es:[vmms.wFlags], JLF_UNLOAD\r\n\tjmp nextchr\r\nnooption:\t\t\t;must be the file to load\r\n\tmov ah,0\r\n\tdec si\r\n\tmov dl,0\r\n\tmov di,offset szPgmName\r\nnextchar:\r\n\tlodsb\r\n\tcmp al,0\r\n\tjz copydone\r\n\tcmp al,13\r\n\tjz copydone\r\n\tcmp al,'\"'\r\n\tjnz @F\r\n\txor ah,1\r\n\tjmp skipchar\r\n@@:\r\n\ttest ah,1\r\n\tjnz @F\r\n\tcmp al,' '\r\n\tjz copydone\t   ;copy is done\r\n@@:\r\n\tcmp al,'.'\r\n\tjnz @F\r\n\tinc dl\r\n@@:\r\n\tstosb\r\n\tcmp al,'/'\r\n\tjz @F\r\n\tcmp al,'\\'\r\n\tjnz skipchar\r\n@@:\r\n\tmov dl,0\r\nskipchar:\r\n\tjmp nextchar\r\ncopydone:\r\n\tdec si   ;go back to 00 or 0dh byte\r\n\tmov word ptr es:[dwCmdLine],si\r\n\ttest ah,1\r\n\tjnz error\r\n\tmov al,00\r\n\tstosb\r\n\tclc\r\n\tret\r\nGetPgmParms endp\r\n\r\n;--- get path of JLOAD.EXE\r\n;--- DS not set yet\r\n\r\nGetLdrPath proc uses es si di\r\n\r\n\t@dprintf ?RMDBG,<\"GetLdrPath enter, CS=%X\",lf>, cs\r\n\tcld\r\n\tmov ah,51h\r\n\tint 21h\r\n\tmov es,bx\r\n\tmov bx,es:[ENVIRON] ;get environment\r\n\tand bx,bx\r\n\tjz @@exit\r\n\tmov es,bx\r\n\txor di,di\r\n\tor cx,-1\r\n\txor ax,ax\r\n@@:\r\n\trepnz scasb\t\t\t;search end of environment\r\n\tscasb\t\t\t\t;found?\r\n\tjnz @B\t\t\t\t;no, continue\r\n\tinc di\t\t\t\t;skip 0001\r\n\tinc di\t\t\t\t;now comes current file name\r\n\tmov si,offset szLdrPath\r\n@@:\r\n\tmov al,es:[di]\r\n\tmov cs:[si],al\r\n\tinc si\r\n\tinc di\r\n\tand al,al\r\n\tjnz @B\r\n@@exit:\r\n\tret\r\nGetLdrPath endp\r\n\r\n;--- openfile in DS:DX\r\n;--- returns file handle in AX if NC\r\n\r\nopenfile proc uses si\r\n\r\n\t@dprintf ?RMDBG,<\"openfile enter\",lf>\r\n\r\n\ttest [fMode], FMODE_LFN\r\n\tjz nolfn\r\n\tmov si,dx\r\n\tMOV AX,716Ch\r\n\tmov dx,1\t\t;action: fail if not exists\r\n\txor bx,bx\t\t;read only \r\n\txor cx,cx\t\t;\r\n\tstc\r\n\tint 21h\r\n\tmov dx,si\r\n\tjnc done\r\n\tcmp ax,7100h\r\n\tstc\r\n\tjnz done\r\nnolfn:\r\n\tMOV AX,3D00h \t;open a file for read\r\n\tint 21h\r\ndone:\r\n\tret\r\nopenfile endp\r\n\r\n;*** read MZ-Header + PE-Header\r\n;*** Input: BX=file handle\r\n;---        DS=DGROUP\r\n;*** C + AX=szNotaPX if not PX format\r\n\r\nReadHdrs proc\r\n\r\n\t@dprintf ?RMDBG,<\"ReadHdrs enter\",lf>\r\n\r\n\tmov cx,0040h\r\n\tmov dx,offset MZ_Hdr\r\n\tmov ah,3Fh\t\t\t;read MZ hdr\r\n\tint 21h\r\n\tjc error1\t\t\t;DOS read error\r\n\tcmp ax,cx\t\t\t;could read 40h bytes?\r\n\tjnz error2\r\n\tmov ax,word ptr [MZ_Hdr]\r\n\tcmp ax,'ZM'\r\n\tjnz error3\r\n\tmov cx,word ptr [MZ_Hdr+3Eh]\r\n\tmov dx,word ptr [MZ_Hdr+3Ch]\r\n\tand dx,dx\r\n\tjz error4\r\n\tmov ax,4200h\t\t;lseek PE hdr\r\n\tint 21h\r\n\tjc error5\t\t\t;DOS lseek error, no PE binary\r\n\r\n\tmov cx,sizeof PE_Hdr\r\n\tmov dx,offset PE_Hdr\r\n\tmov ah,3Fh\t\t\t;read\r\n\tint 21h\r\n\tjc error6\t\t\t;DOS read error\r\n\tcmp ax,cx\r\n\tjnz error7\r\n\tcmp PE_Hdr.Signature,\"XP\"\r\n\tjnz error6\r\n\tcmp PE_Hdr.FileHeader.Machine, IMAGE_FILE_MACHINE_I386 \r\n\tjnz error8\r\n\t@dprintf ?RMDBG, <\"ReadHdrs ok\",lf>\r\n\tclc\r\n\tret\r\nerror4:\r\n\t@dprintf ?RMDBG, <\"offset to new header is zero\",lf>\r\n\t@tracejmp @F\r\nerror2:\r\n\t@dprintf ?RMDBG, <\"filesize less than 0x0040 Bytes\",lf>\r\n\t@tracejmp @F\r\nerror5:\r\n\t@dprintf ?RMDBG, <\"lseek error\",lf>\r\n\t@tracejmp @F\r\nerror7:\r\n\t@dprintf ?RMDBG, <\"PE-Header size too small\",lf>\r\n\t@tracejmp @F\r\nerror8:\r\n\t@dprintf ?RMDBG, <\"not a 80386 binary\",lf>\r\n\t@tracejmp @F\r\nerror3:\r\n\t@dprintf ?RMDBG, <\"cannot find 'MZ' Header\",lf>\r\n\t@tracejmp @F\r\nerror6:\r\n\t@dprintf ?RMDBG, <\"PX not found\",lf>\r\n\t@tracejmp @F\r\nerror1:\r\n\t@dprintf ?RMDBG, <\"read error (MZ)\",lf>\r\n\t@tracejmp @F\r\n@@:\r\n\tmov cx,DStr(\"' isn't a PX binary\",cr,lf)\r\n\tstc\r\n\tret\r\n\r\nReadHdrs endp\r\n\r\nLoadSectionTable proc stdcall\r\n\r\n;\tmov dx,word ptr secpos+0\r\n;\tmov cx,word ptr secpos+2\r\n;\tmov ax,4200h\r\n;\tint 21h\r\n\r\n\tmov cx, PE_Hdr.FileHeader.NumberOfSections\r\n\tmov ax, sizeof IMAGE_SECTION_HEADER\r\n\tmul cx\r\n\tand dx,dx\r\n\tjnz error\r\n\tcmp ax,sizeof buffer\r\n\tja error\r\n\tmov dx, offset buffer\r\n\tmov cx, ax\r\n\tmov ah,3Fh\t\t\t;read section header\r\n\tint 21h\r\n\tjc error\r\n\tret\r\nerror:\r\n\tstc\r\n\tret\r\n\r\nLoadSectionTable endp\r\n\r\n;--- find section which contains requested RVA, then position file pointer\r\n\r\nFindRVA proc stdcall uses si rva:dword\r\n\r\n\tmov si, offset buffer\r\n\tmov cx, PE_Hdr.FileHeader.NumberOfSections\r\n\t.while (cx)\r\n\t\tmov eax, [si].IMAGE_SECTION_HEADER.VirtualAddress\r\n\t\tmov edx, rva\r\n\t\tcmp edx,eax\r\n\t\tjc @F\r\n\t\tadd eax, [si].IMAGE_SECTION_HEADER.SizeOfRawData\r\n\t\tcmp edx,eax\r\n\t\tcmc\r\n\t\tjnc found\r\n@@:\r\n\t\tdec cx\r\n\t\tadd si, sizeof IMAGE_SECTION_HEADER\r\n\t.endw\r\n\tstc\r\nexit:\r\n\tret\r\nfound:\r\n\tsub edx, [si].IMAGE_SECTION_HEADER.VirtualAddress\r\n\tadd edx, [si].IMAGE_SECTION_HEADER.PointerToRawData\r\n\tpush edx\r\n\tpop dx\r\n\tpop cx\r\n\tmov ax,4200h\r\n\tint 21h\r\n\tjc exit\r\n\tmov ax,si\r\n\tjmp exit\r\n\r\nFindRVA endp\r\n\r\n;--- read a module's DDB into ddb variable\r\n;*** Input: BX=file handle\r\n\r\nReadDDB proc\r\n\r\nlocal ddbadr:dword\r\nlocal expdir:IMAGE_EXPORT_DIRECTORY\r\n\r\n\tcmp PE_Hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,0\r\n\tjz errexp\r\n\r\n\tinvoke LoadSectionTable\r\n\tjc errexp\r\n\r\n\tinvoke FindRVA, PE_Hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress\r\n\tjc errexp\r\n\r\n;--- RVA found, now read export directory\r\n\r\n\tlea dx,expdir\r\n\tmov cx,sizeof expdir\r\n\tmov ah,3Fh\r\n\tint 21h\r\n\tjc errexp\r\n\r\n;--- now read first entry of IAT\r\n\r\n\tcmp expdir.NumberOfFunctions,1\r\n\tjc errexp\r\n\tinvoke FindRVA, expdir.AddressOfFunctions\r\n\tjc errexp\r\n\tlea dx,ddbadr\r\n\tmov cx,sizeof ddbadr\r\n\tmov ah,3Fh\r\n\tint 21h\r\n\tjc errexp\r\n\r\n;--- now position to ddb and read it\r\n\r\n\tinvoke FindRVA, ddbadr\r\n\tjc errexp\r\n\tmov dx,offset ddb\r\n\tmov cx,sizeof ddb\r\n\tmov ah,3Fh\r\n\tint 21h\r\n\tjc errexp\r\n\tmov [vmms.pDDB],offset ddb\r\n\tret\r\nerrexp:\r\n\tstc\r\n\tret\r\n\r\nReadDDB endp\r\n\r\n;--- copy memory to/from extended memory using int 15h, ah=87h\r\n\r\nCopyExtMem proc stdcall uses si dwDst:DWORD, dwSrc:DWORD, wSize:WORD\r\n\r\n\t@dprintf ?RMDBG, <\"CopyExtMem dst=%lX, src=%lX\",10>, dwDst, dwSrc\r\n\r\n\tmov si,offset i15tab\r\n\tmov ax, word ptr [dwSrc+0]\r\n\tmov [si+2*size desc].desc.base00_15,ax\r\n\tmov ax, word ptr [dwSrc+2]\r\n\tmov [si+2*size desc].desc.base16_23,al\r\n\tmov [si+2*size desc].desc.base24_31,ah\r\n\tmov ax, word ptr [dwDst+0]\r\n\tmov [si+3*size desc].desc.base00_15,ax\r\n\tmov ax, word ptr [dwDst+2]\r\n\tmov [si+3*size desc].desc.base16_23,al\r\n\tmov [si+3*size desc].desc.base24_31,ah\r\n\tmov cx,wSize\r\n\tshr cx,1\r\n\tmov ah,87h\r\n\tint 15h\r\n\t@dprintf ?RMDBG, <\"CopyExtMem exit\",lf>\r\n\tret\r\nCopyExtMem endp\r\n\r\n;--- get linear address of an offset\r\n\r\nGetLinear proc stdcall wOfs:WORD\r\n\tmov ax,ds\r\n\tmovzx eax,ax\r\n\tshl eax,4\r\n\tpush ecx\r\n\tmov cx,wOfs\r\n\tmovzx ecx,cx\r\n\tadd eax,ecx\r\n\tpop ecx\r\n\tret\r\nGetLinear endp\r\n\r\n;--- init VMM if JLOAD runs the first time (and a JLM is to be loaded)\r\n;--- alloc 1 page for page table\r\n;--- alloc 1 or 2 page(s) for VMM code (will be moved to ?SYSTEMADDR)\r\n;--- returns AX==0 on failure\r\n\r\nInitVMM proc uses di esi\r\n\r\n\t@dprintf ?RMDBG, <\"InitVMM enter\",lf>\r\n\r\n\tinvoke  GetLinear, offset buffer\r\n\tmov [dwBuffer], eax\r\nif SIZEVMM gt 1000h\r\n\txor esi,esi\r\nendif\r\n\tmov ecx, cr3\r\n\tand cx, 0F000h\r\n\tinvoke CopyExtMem, eax, ecx, 1000h\t;copy page dir to buffer\r\n\tjnc @F\r\n\tmov dx,DStr(\"unable to read page directory\",13,10)\r\n\tjmp error\r\n@@:\r\n\tmov edx,dword ptr [buffer+(?SYSTEMADDR shr 20)]\r\n\tand dx,0F000h\r\n\tmov [dwPageTab],edx\r\n\tcmp edx,0\t\t\t\t\t\t;VMM installed already?\r\n\tjnz exit\r\n\tmov dx,DStr(\"no JLMs loaded\",13,10)\r\n\r\n\ttest [vmms.wFlags],JLF_UNLOAD\t;error if NO and \"unload\" mode\r\n\tjnz error\r\n\t\r\n\tmov ax,0DE04h\t\t\t\t\t;get phys page for VMM page table\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\n\tmov dx,DStr(\"cannot allocate 4k page for page table\",13,10)\r\n\tjmp error\r\n@@:\r\n\tmov [dwPageTab],edx\r\n\r\n\tmov ax,0DE04h\t\t\t\t\t;get page for VMM itself\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\nscmemerr:\r\n\tmov dx,DStr(\"cannot allocate 4k page for system code\",13,10)\r\n\tjmp error\r\n@@:\r\n\tmov [dwVmmPage],edx\r\nif SIZEVMM gt 1000h\r\n\tmov ax,0DE04h\t\t\t\t\t;get page for VMM itself\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz scmemerr\r\n\tmov esi,edx\r\nendif\r\n\tmov edx, [dwPageTab]\t\t\t;set PDE for VMM page table\r\n\tor dl,7\r\n\tmov dword ptr [buffer+(?SYSTEMADDR shr 20)],edx\r\n\r\n\tmov ecx,cr3 \t\t\t\t\t;\"write\" updated page dir\r\n\tand cx, 0F000h\r\n\tinvoke CopyExtMem, ecx, [dwBuffer], 1000h\r\n\tmov dx,DStr(\"error setting content of page dir\",13,10)\r\n\tjc error\r\n\r\n;--- clear (last) VMM page content\r\n\r\n\tmov di,offset buffer\r\n\tmov cx,1000h/4\r\n\txor eax,eax\r\n\trep stosd\r\nif SIZEVMM gt 1000h\r\n\tinvoke CopyExtMem, esi, [dwBuffer], 1000h\r\nelse\r\n\tinvoke CopyExtMem, [dwVmmPage], [dwBuffer], 1000h\r\nendif\r\n\r\n;--- set PTE for VMM code/data in VMM page table\r\n\r\n\tmov byte ptr [buffer],6\t;in case the first page is \"reserved\"\r\n\tmov edx, [dwVmmPage]\r\n\tor dl,3\r\n\tmov dword ptr [buffer+((?SYSTEMADDR and 03FFFFFh) shr 10)],edx\r\nif SIZEVMM gt 1000h\r\n\tmov edx, esi\r\n\tor dl,3\r\n\tmov dword ptr [buffer+((?SYSTEMADDR and 03FFFFFh) shr 10)+4],edx\r\nendif\r\n\r\n;--- copy content of VMM page table to extended memory\r\n\r\n\tinvoke CopyExtMem, [dwPageTab], [dwBuffer], 1000h\r\n\tmov dx,DStr(\"error setting content of system page table\",13,10)\r\n\tjc error\r\n\r\n;--- copy VMM code to extended memory\r\n\r\nif SIZEVMM le 1000h\r\n\tinvoke GetLinear, offset vmmcode\r\n\tinvoke CopyExtMem, [dwVmmPage], eax, SIZEVMM\r\nelse\r\n\tinvoke GetLinear, offset vmmcode\r\n\tinvoke CopyExtMem, [dwVmmPage], eax, 1000h\r\n\tinvoke GetLinear, offset vmmcode+1000h\r\n\tinvoke CopyExtMem, esi, eax, SIZEVMM-1000h\r\nendif\r\nexit:\r\n\t@dprintf ?RMDBG, <\"InitVMM ok\",lf>\r\n\tmov ax,1\r\n\tret\r\nerror:\r\n\tcall errorout\r\n\tmov edx, [dwPageTab]\r\n\tcall freepg\r\n\tmov edx, [dwVmmPage]\r\n\tcall freepg\r\nif SIZEVMM le 1000h\r\n\tmov edx, esi\r\n\tcall freepg\r\nendif\r\n\txor ax,ax\r\n\tret\r\nfreepg:\r\n\tand edx, edx\r\n\tjz @F\r\n\tmov ax,0DE05h\r\n\tint 67h\r\n@@:\r\n\tretn\r\n\r\nInitVMM endp\r\n\r\n;--- enter 32bit VMM code\r\n;--- returns ax==0 on failure\r\n;--- allocs a 4kB stack\r\n\r\nRunVMM proc public\r\n\r\n\t@dprintf ?RMDBG, <\"RunVMM enter, dwCmdLine=%lX\",lf>, dwCmdLine\r\n\r\n;--- set (common) linear addresses in JLCOMM\r\n\r\n\tmovzx eax, word ptr dwCmdLine+2\r\n\tmovzx ecx, word ptr dwCmdLine+0\r\n\tshl eax,4\r\n\tadd eax, ecx\r\n\tmov [vmms.lpCmdLine],eax\r\n\r\nif ?PMSTACK\r\n;--- allocate a 4k page to be used for protected-mode stack\r\n\r\n\tmov ax,0DE04h\r\n\tint 67h\r\n\tand ah,ah\r\n\tjz @F\r\n\tmov dx,DStr(\"no memory for protected-mode stack\",13,10)\r\n\tcall errorout\r\n\txor ax,ax\r\n\tret\r\n@@:\r\n\tmov [dwStack], edx\r\nendif\r\n\r\n;--- install a handler for \"invalid opcodes\"\r\n;--- which is the Jemm method of telling that an error has occured\r\n\r\nif ?CATCHEXC06\r\n\tpush es\r\n\tmov ax,3506h\r\n\tint 21h\r\n\tmov word ptr [oldint06+0],bx\r\n\tmov word ptr [oldint06+2],es\r\n\tpop es\r\n\tmov dx,offset int06\r\n\tmov ax,2506h\r\n\tint 21h\r\nendif\r\n\r\n\t@dprintf ?RMDBG, <\"RunVMM: preparing switch to protected-mode\",lf>\r\n\r\n\tmov eax, cr3\r\n\tmov vcpisw.swCR3, eax\r\n\tinvoke GetLinear, offset vmms.emx08.e08_GDTR\r\n\tmov vcpisw.swGDTOFFS, eax\r\n\tinvoke GetLinear, offset vmms.emx08.e08_IDTR\r\n\tmov vcpisw.swIDTOFFS, eax\r\n\tmov vcpisw.swLDTR, 0\r\n\tmov ax, vmms.emx08.e08_TR\r\n\tmov vcpisw.swTR, ax\r\n\tmov eax, ?SYSTEMADDR\r\n\tmov dword ptr vcpisw.swCSEIP, eax\r\n\tmov ax, vmms.emx08.e08_FlatCS\r\n\tmov word ptr vcpisw.swCSEIP+4, ax\r\n\r\n\tinvoke GetLinear, offset szPgmName\r\n\tmov ebx,eax\r\n\r\n\tinvoke GetLinear, offset vcpisw\r\n\tmov esi, eax\r\n\r\n\tinvoke GetLinear, offset vmms\r\n\tmov ecx, eax\r\n\r\nif ?PMSTACK\r\n\tmov edi, [dwStack]\r\nendif\r\nif ?CATCHEXC06\r\n\tmov [spsaved],sp\r\nendif\r\n\t@dprintf ?RMDBG, <\"RunVMM: switch to protected-mode ...\",lf>\r\n\tmov ax,0DE0Ch\r\n\tint 67h\r\n\tsti\r\n\t@dprintf ?RMDBG, <\"RunVMM: back in v86-mode\",lf>\r\n\r\n;--- returncode from JLM is stored in EDX\r\n\tpush edx\r\nif ?PMSTACK\r\n;--- free the 4kB stack\r\n\tmov edx, [dwStack]\r\n\tmov ax,0DE05h\r\n\tint 67h\r\nendif\r\nif ?CATCHEXC06\r\n\tpush ds\r\n\tlds dx,[oldint06]\r\n\tmov ax,2506h\r\n\tint 21h\r\n\tpop ds\r\nendif\r\n\tpop eax\r\n\tret\r\nif ?CATCHEXC06\r\nint06:\r\n\tpush cs\r\n\tpop ds\r\n\tpush cs\r\n\tpop es\r\n\tmov sp,[spsaved]\r\n\tmov dx,DStr(\"exception occured in protected-mode\",13,10)\r\n\tcall errorout\r\n\txor ax,ax\r\n\tjmp error_occured\r\nendif\r\n\r\nRunVMM endp\r\n\r\n;--- display msg 'JLoad: <DX>'\r\n\r\nerrorout proc\r\n\tpush dx\r\n\tmov dx,DStr(\"JLoad: \")\r\n\tmov ah,9\r\n\tint 21h\r\n\tpop dx\r\n\tmov ah,9\r\n\tint 21h\r\n\tret\r\nerrorout endp\r\n\r\n;--- display an asciiz string\r\n\r\ndispstr proc stdcall uses si pString:word\r\n\tmov si, pString\r\nnextitem:\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\tmov dl,al\r\n\tmov ah,2\r\n\tint 21h\r\n\tjmp nextitem\r\ndone:\r\n\tret\r\ndispstr endp\r\n\r\nerrorXout2:\r\n\tmov dx,DStr(\"'\")\r\n\r\n;--- display an error msg of format:\r\n;--- JLoad: <dx><filename><cx>\r\n\r\nerrorXout proc\r\n\tpush cx\r\n\tcall errorout\r\n\tpush offset szPgmName\r\n\tcall dispstr\r\n\tpop dx\r\n\tmov ah,9\r\n\tint 21h\r\n\tret\r\nerrorXout endp\r\n\r\n;--- load a module and run its initialization code\r\n\r\nLoadModule proc\r\n\r\n\t@dprintf ?RMDBG, <\"LoadModule enter\",lf>\r\n\r\n\tmov dx,offset szPgmName\r\n\tcall openfile\r\n\tjnc @F\r\n\tmov cx,DStr(\"' cannot be opened\",13,10)\r\n\tcall errorXout2\r\n\tjmp exit\r\n@@:\r\n\tmov vmms.hFile,ax\r\n\tmov bx,ax\r\n\tcall ReadHdrs\r\n\tjnc @F\r\n\tcall errorXout2\r\n\tjmp exit\r\n@@:\r\n\ttest PE_Hdr.FileHeader.Characteristics, IMAGE_FILE_RELOCS_STRIPPED\r\n\tjz @F\r\n\tmov cx,DStr(\"' isn't relocatable\",13,10)\r\n\tcall errorXout2\r\n\tjmp exit\r\n@@:\r\n\tcmp PE_Hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,0\r\n\tjz @F\r\n\tmov cx,DStr(\"' has references to external modules\",13,10)\r\n\tcall errorXout2\r\n\tjmp exit\r\n@@:\r\n\tcall ReadDDB\r\n\t.if (CARRY? && ([vmms.wFlags] & JLF_UNLOAD))\r\n\t\tmov cx,offset szErrExp\t;doesn't export DDB\r\n\t\tcall errorXout2\r\n\t\tstc\r\n\t\tjmp exit\r\n\t.endif\r\n\r\n\tcall InitVMM\t;init the VMM\r\n\tand ax,ax\r\n\tjz exit\r\n\r\n\t.if ([vmms.wFlags] & JLF_UNLOAD)\r\n\t\tpush es\r\n\t\tmov ah,52h\t\t\t;get start of driver chain\r\n\t\tint 21h\r\n\t\tadd bx,22h\r\n\t\tmovzx ebx,bx\r\n\t\tmov ax,es\r\n\t\tpop es\r\n\t\tmovzx eax,ax\r\n\t\tshl eax,4\r\n\t\tadd eax, ebx\r\n\t\tmov [vmms.lpDrivers],eax\r\n\t.endif\r\n\r\n;--- set linear address \r\n\r\n\tmovzx eax,word ptr dwRequest+2\r\n\tmovzx ecx,word ptr dwRequest+0\r\n\tshl eax,4\r\n\tadd eax, ecx\r\n\tmov [vmms.lpRequest],eax\r\n\r\n;--- run the VMM\r\n\r\n\tcall RunVMM\r\nif ?CATCHEXC06\r\nerror_occured:\r\nendif\r\n\tmov [wRet],ax\r\n\tbt eax, 31\r\n\tjc @@nodispsuccess\r\n\tand ax,ax\r\n\tjz @F\r\n;\ttest byte ptr [vmms.wFlags],JLF_DRIVER\r\n;\tjz @@nodispsuccess\r\n\ttest [fOption],FOPTION_QUIET\r\n\tjnz @@nodispsuccess\r\n@@:\r\n;--- todo: register CL has been set somewhere if ax==0. Describe the details!\r\n\t.if ([vmms.wFlags] & JLF_UNLOAD)\r\n\t\t.if (ax)\r\n\t\t\tmov cx,DStr(\"' unloaded successfully.\",13,10)\r\n\t\t.else\r\n\t\t\t.if (cl == 2)\r\n\t\t\t\ttest [fOption],FOPTION_QUIET\r\n\t\t\t\tjnz @@nodispsuccess\r\n\t\t\t\tmov cx,DStr(\"' isn't loaded.\",13,10)\r\n\t\t\t.else\r\n\t\t\t\tmov cx,DStr(\"' failed to unload.\",13,10)\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t.else\r\n\t\t.if (ax)\r\n\t\t\tmov cx,DStr(\"' loaded successfully.\",13,10)\r\n\t\t.else\r\n\t\t\t.if (cl == 0B7h)\r\n\t\t\t\ttest [fOption],FOPTION_QUIET\r\n\t\t\t\tjnz @@nodispsuccess\r\n\t\t\t\tmov cx,DStr(\"' already loaded.\",13,10)\r\n\t\t\t.else\r\n\t\t\t\tmov cx,DStr(\"' failed to load.\",13,10)\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t.endif\r\n\tcall errorXout2\r\n@@nodispsuccess:\r\nexit:\r\n\tmov bx,vmms.hFile\r\n\tcmp bx,-1\r\n\tjz @F\r\n\tmov ah,3Eh\r\n\tint 21h\r\n@@:\r\n\tret\r\n\r\nLoadModule endp\r\n\r\n;--- common main for .EXE and device driver\r\n\r\nmain proc\r\n\tpush cs\r\n\tpop ds\r\n\tpush cs\r\n\tpop es\r\n\tmov ax,ss\r\n\tmov bx,sp\r\n\tpush ds\r\n\tpop ss\r\n\tmov sp,offset stacktop-2\r\n\tpush ax\r\n\tpush bx\r\n\tmov di,offset _edata\t; clear BSS segment\r\n\tmov cx,offset _end\r\n\tsub cx,di\r\n\tshr cx,1\r\n\txor ax,ax\r\n\tcld\r\n\trep stosw\r\n\t@dprintf ?RMDBG, <\"JLoad main enter\",lf>\r\nif 0\r\n\tmov dx,DStr(\"JLoad full path: '\")\r\n\tmov ah,9\r\n\tint 21h\r\n\tpush offset szLdrPath\r\n\tcall dispstr\r\n\tmov dx,DStr(\"'\",13,10)\r\n\tmov ah,9\r\n\tint 21h\r\nendif\r\n\tmov ah,30h\r\n\tint 21h\r\n\tmov [wVersion],ax\r\n\r\n\tmov [vmms.wLdrCS],cs\r\n\t\t\t\t\t\t\t;detect if lfn is installed\r\n\tmov ax,7147h\r\n\tmov si,offset szPgmName\r\n\tmov dl,0\r\n\tstc\r\n\tint 21h\r\n\tjc @F\r\n\tor fMode, FMODE_LFN\r\n@@:\r\n\tpush ds\r\n\tpush si\r\n\tpush di\r\n\tcall GetPgmParms\r\n\tpop di\r\n\tpop si\r\n\tpop ds\r\n\tjnc @F\r\n\tmov dx,offset dUsage\r\n\tmov ah,9\r\n\tint 21h\r\n\tjmp exit\r\n@@:\r\n\tcall _IsJemmInstalled\r\n\tjnc @F\r\n\tmov dx,DStr(\"Jemm not installed\",13,10)\r\n\tcall errorout\r\n\tjmp exit\r\n@@:\r\n\tmov hJemm, ax\r\n\tmov bx, ax\r\n\tpush 2\t\t\t;read version\r\n\tmov cx,2\t\t;returns 1 word\r\n\tmov dx, sp\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tpop ax\r\n\tcmp ax,?VERSIONHIGH + 256 * ?VERSIONLOW\r\n\tjz @F\r\n\tmov dx,DStr(\"versions of Jemm and JLoad don't match\",13,10)\r\n\tcall errorout\r\n\tjmp exit\r\n@@:\r\n\tmov dx, offset vmms.emx08\r\n\tmov byte ptr [vmms.emx08],8  ;read VMM info\r\n\tmov cx,size EMX08\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tjnc @F\r\n\tmov dx,DStr(\"no support for protected mode API\",13,10)\r\n\tcall errorout\r\n\tjmp exit\r\n@@:\r\n\tsidt [dfIDT]\r\n\tmov eax,dword ptr [dfIDT+2]\r\n\tcmp eax,dword ptr vmms.emx08.e08_IDTR+2\r\n\tjz @F\r\n\tmov dx,DStr(\"virtualized environment detected\",13,10)\r\n\tcall errorout\r\n@@:\r\n\tcall LoadModule\t\t;load / unload module!\r\nexit:\r\n\tmov bx,hJemm\r\n\tcmp bx,-1\r\n\tjz @F\r\n\tmov ah,3Eh\r\n\tint 21h\r\n@@:\r\n\tcmp [wRet],0\r\n\tsetz al\r\n\tmov ah,0\r\n\t@dprintf ?RMDBG, <\"JLoad main exit\",lf>\r\n\tpop cx\r\n\tpop ss\r\n\tmov sp,cx\r\n\tret\r\nmain endp\r\n\r\nif 0\r\n\r\n;--- bin to ascii conversion\r\n;--- es:di -> output buffer\r\n\r\nDWORDOUT:\r\n\tpush eax\r\n\tshr eax, 16\r\n\tcall WORDOUT\r\n\tpop eax\r\nWORDOUT:\t\t\t;<-- bin2ascii(WORD in AX) (-> ES:DI)\r\n\tpush ax\r\n\tmov al,ah\r\n\tcall BYTEOUT\r\n\tpop ax\r\nBYTEOUT:\r\n\tmov ah,al\r\n\tshr al,4\r\n\tcall NIBOUT\r\n\tmov al,ah\r\nNIBOUT:\r\n\tand al,0Fh\r\n\tadd al,'0'\r\n\tcmp al,'9'\r\n\tjle @F\r\n\tadd al,07h\r\n@@:\r\n\tstosb\r\n\tret\r\nendif\r\n\r\n;--- entry if loaded as device driver in config.sys\r\n\r\ninterrupt proc far\r\n\tpushad\r\n\tpush es\r\n\tpush ds\r\n\tles di, cs:[dwRequest]\r\n\tmov es:[di].request_hdr.status,STATUS_OK\t\t; we're alright\r\n\tcmp es:[di].request_hdr.cmd,CMD_INIT\r\n\tjne @@done\r\n\tmov word ptr es:[di+0].init_strc.end_addr,0\r\n\tmov word ptr es:[di+2].init_strc.end_addr,cs\r\n\tlds si,es:[di].init_strc.cmd_line\r\n\tpush cs\r\n\tpop es\r\n\tcld\r\n\tmov di, offset szLdrPath\r\n\t.while (byte ptr [si] > ' ')\r\n\t\tmovsb\r\n\t.endw\r\n\tmov al,0\r\n\tstosb\r\n\tmov word ptr cs:[dwCmdLine+0],si\r\n\tmov word ptr cs:[dwCmdLine+2],ds\r\n\tor byte ptr cs:[vmms.wFlags],JLF_DRIVER\r\n\tcall main\r\n@@done:\r\n\tpop ds\r\n\tpop es\r\n\tpopad\r\n\tret\r\ninterrupt endp\r\n\r\n;--- entry for .EXE\r\n;--- if the module returns with dwRequest != 0,\r\n;--- the loader will terminate with ah=31h (stay resident)\r\n\r\nstart:\r\n\tcld\r\n\tmov si,80h\r\n\tlodsb\r\n\tmov bl,al\r\n\tmov bh,0\r\n\tmov byte ptr [si+bx],0\r\n\tmov word ptr cs:[dwCmdLine+0],si\r\n\tmov word ptr cs:[dwCmdLine+2],ds\r\n\tcall GetLdrPath\r\n\tcall main\r\n\tmov ah,4Ch\r\n\ttest [vmms.wFlags], JLF_UNLOAD\r\n\tjnz @F\r\n\tmov dx,word ptr [vmms.lpRequest]\r\n\tand dx,dx\r\n\tjz @F\r\n\tmov ah,31h\r\n@@:\r\n\tint 21h\r\n\r\nifdef _DEBUG\r\n\tinclude dprntf16.inc\r\nendif\r\n\r\n_TEXT ends\r\n\r\n\tend  start\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/JLOAD.INC",
    "content": "\r\n;--- assembly conditionals\r\n\r\n;--- constants\r\n;--- ?SYSBASE is from JSYSTEM.INC (=0F8000000h)\r\n\r\n?SYSTEMADDR\t\tequ ?SYSBASE+400000h;linear address \"system\" region\r\n?PRIVATEADDR\tequ 400000h\t\t\t;linear address \"private\" region\r\n\r\n?PMSTACK\tequ 1\t;1=alloc a 4 kB stack space; 0=use Jemm's stack\r\n\r\n;--- communication structure 16-bit real-mode -> 32-bit protected-mode\r\n\r\nVMMS\tstruc\r\n\t\tJLCOMM <?>\r\nemx08\tEMX08 <?>\r\nhFile\tdw ?    ;file handle of JLM\r\npszLdr\tdw ?\t;points to full path of JLOAD.EXE\r\npszJLM\tdw ?\t;points to JLM name parameter\r\npHdr\tdw ?\t;points to PE header\r\npBuffer\tdw ?\t;points to a 4 kB buffer in conv. memory\r\npDDB\tdw ?\t;points to DDB (if module is to be unloaded)\r\nVMMS\tends\r\n\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/JLOAD.TXT",
    "content": "\r\n 1. About JLoad and JLMs\r\n\r\n  JLoad is an extension for Jemm (both Jemm386 and JemmEx) which allows to\r\n load 32bit flat protected-mode modules ( called \"Jemm Loadable Module\",\r\n JLM ) into Jemm's address space.\r\n \r\n JLMs can be used for various tasks, for example:\r\n\r\n - implement a protected-mode version of a DOS device driver.\r\n - implement a protected-mode version of a DOS TSR.\r\n - provide services for other JLMs (kind of a ring 0 dll).\r\n - run as a ring 0 protected-mode application which will\r\n   be unloaded automatically after it has its job done.\r\n - emulate hardware by trapping I/O port access.\r\n\r\n  A JLM is supposed to be loaded by JLoad, either as a device driver in\r\n CONFIG.SYS, or from the command line. The syntax is:\r\n\r\n [DEVICE=]JLOAD.EXE [ options ] name_of_JLM [arguments for JLM]\r\n\r\n The name of a JLM is always required as parameter. Options are:\r\n\r\n   -q:  quiet mode.\r\n   -u:  unload a JLM.\r\n\r\n  To be able to unload a JLM, several conditions must be true. First, the\r\n JLM must export a so-called DDB. Second, the DDB must contain a valid\r\n \"Device Id\". And third, the JLM must not refuse to be unloaded.\r\n\r\n  Note: currently  the versions of Jemm and JLoad always must match, else\r\n JLoad will refuse to do anything.\r\n\r\n  A few JLMs (XDMA32, XCDROM32 and AHCICD) are included in the Jemm binary\r\n package. For more details see XDMA32.TXT, XCDROM32.TXT and AHCICD.TXT.\r\n\r\n\r\n 2. Creating the Binaries\r\n\r\n  JLoad.exe is a 16-bit binary. It contains a flat, zero-based 32-bit part\r\n that is included as a binary object during assembly time. While the 32-bit\r\n part (JLoad32.bin) is generated, the linker may emit warning \"stack segment\r\n not found\", which can safely be ignored. For details about the tools needed\r\n for creating the binaries see file Makefile.\r\n\r\n\r\n 3. Technical Details about JLMs\r\n\r\n  JLMs must be non-segmented and linked as Win32 PE binaries. But despite the\r\n similarities, JLMs should not be confused with such binaries. Some of the\r\n differences are:\r\n\r\n - JLMs always run in ring 0 protected-mode.\r\n - there is no Win32 API (and also no DOS/DPMI API) available.\r\n - JLMs cannot be linked with Win32 import libraries.\r\n\r\n  Because of the fundamental differences, JLMs should be linked with subsystem\r\n \"native\". Thus they aren't recognised anymore as Win32 binaries.\r\n\r\n  The only API JLMs can use directly is the one installed by JLoad, which more\r\n or less is a small subset of the Windows 3x/9x VMM API. This API usually is\r\n invoked by an INT 20h opcode, followed by two words that determine the\r\n module and function to be called. The API also allows \"nested execution\",\r\n that is, a JLM can indirectly call BIOS or other software interrupts outside\r\n of the client's context. For details about what functions are implemented\r\n see files JLM.INC or JLM.H.\r\n\r\n  There is a strong similiarity between JLMs and Windows 3x/9x VxDs, but there\r\n are also some major differences:\r\n\r\n - Jemm does not support Virtual Machines (VMs) currently.\r\n - Jemm has no integrated DPMI host. Therefore the client will always be in\r\n   V86-mode. DPMI applications can run only outside of Jemm's context with\r\n   the help of an external DPMI host that runs as VCPI client.\r\n - JLMs run with interrupts disabled. If they do a lengthy operation or have\r\n   to wait, they must \"yield\" to allow interrupts to be serviced.\r\n\r\n  Note that the first invokation of JLoad will actually make JLoad resident\r\n in Jemm's address space. Further invokations will call the resident part.\r\n This is relevant if multiple versions of JLoad are used, for example both\r\n a release and a debug version.\r\n\r\n  A detailed documentation about the Win3x/9x APIs that are partly implemented\r\n by JLoad can be found in:\r\n\r\n - Win95 DDK, file VMM.HLP\r\n - Win98 DDK, file OTHER.CHM\r\n - Win2k DDK, file OTHER.CHM\r\n\r\n\r\n 4. Debugging JLMs\r\n \r\n  Loading JDeb386 - which is a JLM itself - is the easiest method to debug JLMs.\r\n This debugger has a simple line-oriented interface. Once loaded, a breakpoint\r\n ( or pressing SysReq ) will activate the debugger.\r\n\r\n  Alternatively, Deb386v.sys may be used. This is a DebugR variant in DOS\r\n device driver format. This debugger has the advantage that it can debug Jemm\r\n during its initialization phase.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/JLOAD.txt",
    "content": "\r\n 1. About JLoad and JLMs\r\n\r\n  JLoad is an extension for Jemm (both Jemm386 and JemmEx) which allows to\r\n load 32bit flat protected-mode modules ( called \"Jemm Loadable Module\",\r\n JLM ) into Jemm's address space.\r\n \r\n JLMs can be used for various tasks, for example:\r\n\r\n - implement a protected-mode version of a DOS device driver.\r\n - implement a protected-mode version of a DOS TSR.\r\n - provide services for other JLMs (kind of a ring 0 dll).\r\n - run as a ring 0 protected-mode application which will\r\n   be unloaded automatically after it has its job done.\r\n - emulate hardware by trapping I/O port access.\r\n\r\n  A JLM is supposed to be loaded by JLoad, either as a device driver in\r\n CONFIG.SYS, or from the command line. The syntax is:\r\n\r\n [DEVICE=]JLOAD.EXE [ options ] name_of_JLM [arguments for JLM]\r\n\r\n The name of a JLM is always required as parameter. Options are:\r\n\r\n   -q:  quiet mode.\r\n   -u:  unload a JLM.\r\n\r\n  To be able to unload a JLM, several conditions must be true. First, the\r\n JLM must export a so-called DDB. Second, the DDB must contain a valid\r\n \"Device Id\". And third, the JLM must not refuse to be unloaded.\r\n\r\n  Note: currently  the versions of Jemm and JLoad always must match, else\r\n JLoad will refuse to do anything.\r\n\r\n  A few JLMs (XDMA32, XCDROM32 and AHCICD) are included in the Jemm binary\r\n package. For more details see XDMA32.TXT, XCDROM32.TXT and AHCICD.TXT.\r\n\r\n\r\n 2. Creating the Binaries\r\n\r\n  JLoad.exe is a 16-bit binary. It contains a flat, zero-based 32-bit part\r\n that is included as a binary object during assembly time. While the 32-bit\r\n part (JLoad32.bin) is generated, the linker may emit warning \"stack segment\r\n not found\", which can safely be ignored. For details about the tools needed\r\n for creating the binaries see file Makefile.\r\n\r\n\r\n 3. Technical Details about JLMs\r\n\r\n  JLMs must be non-segmented and linked as Win32 PE binaries. But despite the\r\n similarities, JLMs should not be confused with such binaries. Some of the\r\n differences are:\r\n\r\n - JLMs always run in ring 0 protected-mode.\r\n - there is no Win32 API (and also no DOS/DPMI API) available.\r\n - JLMs cannot be linked with Win32 import libraries.\r\n\r\n  Because of the fundamental differences, JLMs should be linked with subsystem\r\n \"native\". Thus they aren't recognised anymore as Win32 binaries.\r\n\r\n  The only API JLMs can use directly is the one installed by JLoad, which more\r\n or less is a small subset of the Windows 3x/9x VMM API. This API usually is\r\n invoked by an INT 20h opcode, followed by two words that determine the\r\n module and function to be called. The API also allows \"nested execution\",\r\n that is, a JLM can indirectly call BIOS or other software interrupts outside\r\n of the client's context. For details about what functions are implemented\r\n see files JLM.INC or JLM.H.\r\n\r\n  There is a strong similiarity between JLMs and Windows 3x/9x VxDs, but there\r\n are also some major differences:\r\n\r\n - Jemm does not support Virtual Machines (VMs) currently.\r\n - Jemm has no integrated DPMI host. Therefore the client will always be in\r\n   V86-mode. DPMI applications can run only outside of Jemm's context with\r\n   the help of an external DPMI host that runs as VCPI client.\r\n - JLMs run with interrupts disabled. If they do a lengthy operation or have\r\n   to wait, they must \"yield\" to allow interrupts to be serviced.\r\n\r\n  Note that the first invokation of JLoad will actually make JLoad resident\r\n in Jemm's address space. Further invokations will call the resident part.\r\n This is relevant if multiple versions of JLoad are used, for example both\r\n a release and a debug version.\r\n\r\n  A detailed documentation about the Win3x/9x APIs that are partly implemented\r\n by JLoad can be found in:\r\n\r\n - Win95 DDK, file VMM.HLP\r\n - Win98 DDK, file OTHER.CHM\r\n - Win2k DDK, file OTHER.CHM\r\n\r\n\r\n 4. Debugging JLMs\r\n \r\n  Loading JDeb386 - which is a JLM itself - is the easiest method to debug JLMs.\r\n This debugger has a simple line-oriented interface. Once loaded, a breakpoint\r\n ( or pressing SysReq ) will activate the debugger.\r\n\r\n  Alternatively, Deb386v.sys may be used. This is a DebugR variant in DOS\r\n device driver format. This debugger has the advantage that it can debug Jemm\r\n during its initialization phase.\r\n\r\n Japheth\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/JLOAD32.ASM",
    "content": "\r\n;--- JLoad's PE file loader\r\n;--- this code is copied to linear address ?SYSTEMADDR when\r\n;--- JLoad is running for the first time.\r\n;--- best viewed with TAB size 4\r\n\r\n\t.386\r\n\t.model flat\r\n\r\n\toption casemap:none\r\n\toption proc:private\r\n\r\n\tinclude x86.inc\r\n\tinclude jsystem.inc\r\n\tinclude jlm.inc\r\n\tinclude jload.inc\r\n\tinclude jload32.inc\r\n\tinclude winnt.inc\r\n\tinclude debug.inc\r\n\r\nif ?PMSTACK\r\n?SYSTABSTKIDX equ 3ffh\r\nendif\r\n\r\n\t.data\r\n\r\n;--- variables\r\n\r\ng_dwIDT\t\tdd 0\t;linear address IDT\r\n\r\n\t.code\r\n\r\n;--- entry from real-mode - must be at offset 0.\r\n;--- This is a true VCPI entry, meaning SS:ESP still on host stack.\r\n;--- CS,DS,ES=Flat (v5.86: CS is correct, but DS/ES was just an assumption)\r\n;--- SS:ESP -> top of 128 byte vcpi stack of Jemm\r\n;--- ecx->VMMS structure\r\n;--- edi=phys page for protected-mode stack (if ?PMSTACK==1)\r\n;--- out: EDX=returncode from JLM\r\n\r\nstart proc c public\r\n\r\n;--- v5.86: don't assume DS/ES are flat. It's true for Jemm, but\r\n;--- another program may have hooked into int 67h, ax=DE0Ch!\r\n\tmov eax, cs\r\n\tadd eax, 8\r\n\tmov ds, eax\r\n\tmov es, eax\r\n\r\nif ?PMSTACK\r\n;--- set the 4kB stack allocated in 16-bit init code\r\n;--- to be fixed: don't modify PTEs of the F8000000-F83FFFFF region, it's\r\n;--- reserved for Jemm!\r\n\r\n\tor edi, PTF_PRESENT or PTF_RW or PTF_USER\r\n\txchg edi,ds:[?SYSBASE+?PAGETABSYS+?SYSTABSTKIDX*4]  ;set the stack PTE ( =F83FF000h )\r\n;--- init stack. It's flat; since Jemm's ?SYSBASE is 0F8000000h, the\r\n;--- stack is F8400000h.\r\n\tmov ss, eax\r\n\tmov esp, ?SYSBASE + 400000h\r\n\tpush edi\t\t\t;save old PTE for stack\r\nelse\r\n\tmov ss, eax\r\n\tmov esp, ?TOS - 200h\r\nendif\r\n\r\n\tcmp word ptr [dfOldint20+4],0\t;already initialized?\r\n\tjnz @F\r\n\tcall InitVMM\r\n\tjc @@exit\r\n@@:\r\n\r\n\tcall Get_Cur_VM_Handle\r\n\tmov ebp,[ebx].cb_s.CB_Client_Pointer\r\n\r\n\t.if ([ecx].VMMS.wFlags & JLF_UNLOAD)\r\n\t\tcall SearchJLM\r\n\t\tjc @@error\r\n\t\tmov ebx, [eax].VxD_Desc_Block.DDB_Reserved1\r\n\t.else\r\n\t\tcall LoadJLM\r\n\t\tjc @@error\r\n\t\tmov esi,[ebx+3Ch]\r\n\t\tadd esi, ebx\t;point to NT header\r\n\t\tcall DoFixups\r\n\t\tjc @@error\r\n\t\tcall FreeDiscardableSections\r\n\t\tcall SetDDB\r\n\t.endif\r\n\r\n;--- call the modules entry point\r\n\r\n\tmov esi,[ebx].IMAGE_DOS_HEADER.e_lfanew\r\n\tadd esi, ebx\t;point to NT header\r\n\r\n\tmov edi, ebx\r\n\tmov eax, [esi].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint\r\n\tadd eax, edi\r\n\tpush edi\r\n\tpush esi\r\n\ttest [esi].IMAGE_NT_HEADERS.FileHeader.Characteristics, IMAGE_FILE_DLL\r\n\tjz isapp\r\n\ttest [ecx].VMMS.wFlags, JLF_UNLOAD\r\n\tjz @@isload\r\n\tpush ecx\r\n\tpush DLL_PROCESS_DETACH\r\n\tpush edi\r\n\tcall eax\r\n\tpop esi\r\n\tpop edi\r\n\tand ax, ax\t\t; check AX only!\r\n\tjz @@exit\r\n\tjmp @@unload\r\n@@isload:\r\n\tpush ecx\r\n\tpush DLL_PROCESS_ATTACH\r\n\tpush edi\r\nisapp:\r\n\tcall eax\r\n\tpop esi\r\n\tpop edi\r\n\tand ax, ax\t\t; check AX only!\r\n\tjz @@unload\r\n\ttest [esi].IMAGE_NT_HEADERS.FileHeader.Characteristics, IMAGE_FILE_DLL\r\n\tjnz @@exit\r\n@@unload:\r\n\tcall UnloadJLM\r\n\tjmp @@exit\r\n@@error:\r\n\txor eax,eax\r\n@@exit:\r\n\t@dprintf ?INITDBG, <\"start exit (returns to v86)\",10>\r\n\tmov [ebp].Client_Reg_Struc.Client_EDX, eax\r\nif ?PMSTACK\r\n\tpop eax\r\nendif\r\n\tmov esp, ebp\r\nif ?PMSTACK\r\n\tmov ds:[?SYSBASE+?PAGETABSYS+?SYSTABSTKIDX*4], eax\t;restore stack PTE\r\n\tmov eax, cr3\t;flush TLB to validate the just restored stack PTE\r\n\tmov cr3, eax\r\nendif\r\n\tpopad\r\n\tadd esp, 4+4   ; skip int# and errcode\r\n\tiretd\r\n\r\n\talign 4\r\nstart endp\r\n\r\n;--- ecx=VMMS\r\n;--- ebx=module handle\r\n\r\nSetDDB proc\r\n\tpushad\r\n\tmovzx esi, [ecx].VMMS.pDDB\r\n\tand esi, esi\r\n\tjz exit\r\n\t@dprintf ?PEDBG, <\"SetDDB: calling VMM_Add_DDB\",10>\r\n\tmov eax, [ebx+3Ch]\r\n\tadd eax, ebx\r\n\tmov eax,[eax].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress\r\n\tadd eax, ebx\r\n\tmov eax,[eax].IMAGE_EXPORT_DIRECTORY.AddressOfFunctions\r\n\tadd eax, ebx\r\n\tmov esi, [eax]\r\n\tadd esi, ebx\r\n\tmov [esi].VxD_Desc_Block.DDB_Reserved1, ebx\t;store module in DDB\r\n\tcall VMM_Add_DDB\r\nexit:\r\n\tmov [ebx+38h],esi\t;store DDB in module\r\n\tpopad\r\n\tret\r\n\talign 4\r\nSetDDB endp\r\n\r\n;--- search a JLM\r\n;--- ECX=VMMS\r\n;--- to find a JLM, it must have an associated DDB with an ID or a name\r\n;--- out: NC: EAX=module handle\r\n;---       C: eax=return code\r\n;--- if NC, ECX is preserved\r\n\r\nSearchJLM proc\r\n\r\n\tmovzx edi, [ecx].VMMS.pDDB\r\n\tand edi, edi\r\n\tjz error\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_DS\r\n\tshl eax, 4\r\n\tadd edi, eax\r\n\tmovzx eax,[edi].VxD_Desc_Block.DDB_Req_Device_Number\r\n\tpush ecx\r\n\tpush edi\r\n\tlea edi, [edi].VxD_Desc_Block.DDB_Name\r\n\tcall FindDevice\r\n\tpop edi\r\n\tpop ecx\r\n\tjc error2\r\n\t@dprintf ?PEDBG, <\"SearchJLM: device found\",10>\r\n\tret\r\nerror2:\r\n\t@dprintf ?PEDBG, <\"SearchJLM: device not found\",10>\r\n\tjmp @F\r\nerror:\r\n\t@dprintf ?PEDBG, <\"SearchJLM: no DDB\",10>\r\n@@:\r\n\tmov cx,2      ;not found\r\n\tstc\r\n\tret\r\n\talign 4\r\n\r\nSearchJLM endp\r\n\r\n;--- load a JLM\r\n;--- 1. find an address space in system region\r\n;--- 2. commit the pages\r\n;--- 3. read the file with nested execution\r\n;---    fixups aren't resolved yet!\r\n;--- inp: ECX -> VMMS, EBP -> client regs\r\n;--- out: NC if ok\r\n;---    EBX=module handle\r\n;---    ESI=DDB\r\n\r\nLoadJLM proc\r\n\t@dprintf ?PEDBG, <\"LoadJLM enter, ecx=%X, calling SearchJLM\",10>, ecx\r\n\tpushad\r\n\tcall SearchJLM\t;already loaded?\r\n\tjnc error3\r\n\tmov ecx,[esp].PUSHADS.rECX ;restore ecx\r\n\tmovzx edi, [ecx].VMMS.pHdr\r\n\tmovzx eax, word ptr [ebp].Client_Reg_Struc.Client_DS\r\n\tshl eax, 4\r\n\tadd edi, eax\r\n\tmov ax, [ecx].VMMS.hFile\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EBX, ax\r\n\tmov bx, [ecx].VMMS.pBuffer\r\n\tmov esi, [edi].IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage\r\n\tshr esi, 12\r\n\tpush 0\r\n\tpush esi\r\n\tpush PR_SYSTEM\r\n\tcall _PageReserve\r\n\tadd esp,3*4\r\n\tcmp eax,-1\r\n\tjz error\r\n\tmov [esp].PUSHADS.rEBX, eax   ;linear address\r\n\tshr eax, 12\r\n\tpush PC_FIXED or PC_WRITEABLE\r\n\tpush 0\r\n\tpush PD_ZEROINIT\r\n\tpush esi\r\n\tpush eax\r\n\tcall _PageCommit\r\n\tadd esp,5*4\r\n\tand eax, eax\r\n\tjz error\r\n\tmov esi,[edi].IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeaders\r\n\t.if (esi > 1000h)\r\n\t\tmov esi, 1000h\r\n\t.endif\r\n\txor eax, eax\r\n\tmov edi, [esp].PUSHADS.rEBX\r\n\tcall ReadFile\r\n\tjc error2a\r\n\tadd edi, [edi+3Ch]\r\n\tmovzx ecx, [edi].IMAGE_NT_HEADERS.FileHeader.NumberOfSections\r\n\tadd edi, sizeof IMAGE_NT_HEADERS\r\n\t.while (ecx)\r\n\t\tpush ecx\r\n\t\tpush edi\r\n\t\tmov ecx, [edi].IMAGE_SECTION_HEADER.SizeOfRawData\r\n\t\tmov eax, [edi].IMAGE_SECTION_HEADER.PointerToRawData\r\n\t\tmov edi, [edi].IMAGE_SECTION_HEADER.VirtualAddress\r\n\t\tadd edi, [esp+2*4].PUSHADS.rEBX\r\n\t\t.while (ecx)\r\n\t\t\tmov esi, ecx\r\n\t\t\t.if (esi > 1000h)\r\n\t\t\t\tmov esi, 1000h\r\n\t\t\t.endif\r\n\t\t\tpush ecx\r\n\t\t\tpush eax\r\n\t\t\tcall ReadFile\r\n\t\t\tjc error2\r\n\t\t\tpop eax\r\n\t\t\tpop ecx\r\n\t\t\tadd eax, esi\r\n\t\t\tadd edi, esi\r\n\t\t\tsub ecx, esi\r\n\t\t.endw\r\n\t\tpop edi\r\n\t\tpop ecx\r\n\t\tadd edi, sizeof IMAGE_SECTION_HEADER\r\n\t\tdec ecx\r\n\t.endw\r\n\tpopad\r\n\tret\r\nerror3:\r\n\t@dprintf ?PEDBG, <\"LoadJLM: JLM already loaded\",10>\r\n\tstc\r\n\tpopad\r\n\tmov cx,0B7h\t;already exists\r\n\tret\r\nerror2:\r\n\tadd esp,4*4\r\nerror2a:\r\n\t@dprintf ?PEDBG, <\"LoadJLM: ReadFile error2\",10>\r\nerror:\r\n\tstc\r\n\tpopad\r\n\tmov cx,8\t;memory error\r\n\tret\r\n\talign 4\r\nLoadJLM endp\r\n\r\n;--- unload the JLM's memory block\r\n;--- inp: EDI=linear address\r\n\r\nUnloadJLM proc\r\n\tpushad\r\n\t@dprintf ?PEDBG, <\"UnloadJLM enter\",10>\r\n\tmov edi,[edi+38h]\t; address DDB stored in header\r\n\tand edi,edi\r\n\tjz @F\r\n\t@dprintf ?PEDBG, <\"UnloadJLM: calling VMM_Remove_DDB\",10>\r\n\tcall VMM_Remove_DDB\r\n@@:\r\n\tmov edi,[esp].PUSHADS.rEDI\r\n\tpush 0     ;flags\r\n\tpush edi\r\n\tcall _PageFree\r\n\tadd esp, 2*4\r\n\tpopad\r\n\tret\r\n\talign 4\r\nUnloadJLM endp\r\n\r\n;--- read a file portion (4kB)\r\n;--- eax = file pos\r\n;--- si = bytes to read\r\n;--- bx = buffer offset\r\n;--- edi = target addr\r\n;--- Client_BX = file handle\r\n;--- Client_DS = buffer segment\r\n\r\nReadFile proc\r\n\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EDX, ax\r\n\tshr eax, 16\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_ECX, ax\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EAX, 4200h\r\n\tcall Begin_Nest_Exec\r\n\tmov eax, 21h\r\n\tcall Exec_Int\r\n\ttest [ebp].Client_Reg_Struc.Client_EFlags,1\r\n\t.if (ZERO?)\r\n\t\tmov word ptr [ebp].Client_Reg_Struc.Client_ECX, si\r\n\t\tmov word ptr [ebp].Client_Reg_Struc.Client_EDX, bx\r\n\t\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, 3Fh\r\n\t\tmov eax, 21h\r\n\t\tcall Exec_Int\r\n\t\ttest [ebp].Client_Reg_Struc.Client_EFlags,1\r\n\t\t.if (ZERO?)\r\n\t\t\tmovzx ecx, si\r\n\t\t\tpush esi\r\n\t\t\tmovzx esi, word ptr [ebp].Client_Reg_Struc.Client_DS\r\n\t\t\tshl esi, 4\r\n\t\t\tmovzx ebx, bx\r\n\t\t\tadd esi, ebx\r\n\t\t\tshr ecx, 2\r\n\t\t\tpush edi\r\n\t\t\trep movsd\r\n\t\t\tpop edi\r\n\t\t\tpop esi\r\n\t\t.endif\r\n\t.endif\r\n\tcall End_Nest_Exec\r\n\tmov ah,byte ptr [ebp].Client_Reg_Struc.Client_EFlags\r\n\tsahf\r\n\tret\r\n\talign 4\r\nReadFile endp\r\n\r\n;--- walk relocations for PE binary loaded at linear address EBX\r\n;--- inp: EBX = module handle, ESI=IMAGE_NT_HEADERS\r\n\r\nDoFixups proc \r\n\r\n\t@dprintf ?INITDBG, <\"DoFixups enter\",10>\r\n\tpushad\r\n\tmov edx,ebx\r\n\tmov ebx,[esi].IMAGE_NT_HEADERS.OptionalHeader.ImageBase\r\n\r\n\tmov ebp, [esi].IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage\r\n\tmov ecx, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof IMAGE_DATA_DIRECTORY].Size_\r\n\tjecxz done\r\n\tmov esi, [esi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof IMAGE_DATA_DIRECTORY].VirtualAddress\r\n\tadd esi,edx\r\nnextblock:\r\n\r\n\tmov edi, [esi].IMAGE_BASE_RELOCATION.VirtualAddress\r\n\tcmp edi, ebp\r\n\tjnc done\t\t\t;invalid relocations found!!!\r\n\r\n\tpush ecx\r\n\tpush esi\r\n\tpush edx\r\n\tpush ebx\r\n\r\n\tadd edi, edx\r\n\tsub edx, ebx\r\n\tmov ecx, [esi].IMAGE_BASE_RELOCATION.SizeOfBlock\r\n\tadd ecx, esi\r\n\tadd esi, sizeof IMAGE_BASE_RELOCATION\r\n\txor eax, eax\r\n\t.while (esi < ecx)\r\n\t\tlods word ptr [esi]\r\n\t\tmov bl,ah\r\n\t\tand ah,0Fh\r\n\t\tshr bl,4\r\n\t\t.if (bl == IMAGE_REL_BASED_HIGHLOW)\r\n\t\t\tadd [edi+eax],edx\r\n\t\t.endif\r\n\t.endw\r\n\tpop ebx\r\n\tpop edx\r\n\tpop esi\r\n\tpop ecx\r\n\tmov eax,[esi].IMAGE_BASE_RELOCATION.SizeOfBlock\r\n\tadd esi, eax\r\n\tsub ecx, eax\r\n\tja nextblock\r\ndone:\r\n\tpopad\r\n\t@dprintf ?INITDBG, <\"DoFixups exit\",10>\r\n\tclc\r\n\tret\r\n\talign 4\r\nDoFixups endp\r\n\r\n;--- free all objects marked as discardable\r\n;--- inp: ESI -> IMAGE_NT_HEADERS\r\n;--- inp: EBX = module base\r\n\r\nFreeDiscardableSections proc \r\n\r\n\t@dprintf ?INITDBG, <\"FreeDiscardableSections enter\",10>\r\n\tpushad\r\n\tmovzx ecx,[esi].IMAGE_NT_HEADERS.FileHeader.NumberOfSections\r\n\tlea edi,[esi+size IMAGE_NT_HEADERS]\r\n\tjecxz @@done\r\n@@nextitem:\r\n\ttest [edi].IMAGE_SECTION_HEADER.Characteristics, IMAGE_SCN_MEM_DISCARDABLE\r\n\tjz @@skipitem\r\n\tmov eax, [edi].IMAGE_SECTION_HEADER.VirtualAddress\r\n\tmov edx, [edi].IMAGE_SECTION_HEADER.Misc.VirtualSize\r\n\tadd edx, 1000h-1\r\n\tshr edx, 12\r\n\tjz @@skipitem\r\n\t.if (eax)\r\n\t\tpush ecx\r\n\r\n\t\tadd eax, ebx\r\n\t\tshr eax, 12\r\n\t\tpush 0\r\n\t\tpush edx\r\n\t\tpush eax\r\n\t\tcall _PageDecommit\r\n\t\tadd esp,3*4\r\n\r\n\t\tpop ecx\r\n\t.endif\r\n@@skipitem:\r\n\tadd edi, size IMAGE_SECTION_HEADER\r\n\tloop @@nextitem\r\n@@done:\r\n\tpopad\r\n\t@dprintf ?INITDBG, <\"FreeDiscardableSections exit\",10>\r\n\tclc\r\n\tret\r\n\talign 4\r\nFreeDiscardableSections endp\r\n\r\n;--- get address of 4k DOS buffer (during load only)\r\n;--- ecx=JLCOMM ptr\r\n\r\nGetDOSBuffer proc public\r\n\tmovzx eax,[ecx].VMMS.pBuffer\r\n\tmovzx edx,word ptr [ebp].Client_Reg_Struc.Client_DS\r\n\tshl edx,4\r\n\tadd eax,edx\r\n\tret\r\nGetDOSBuffer endp\r\n\r\nif ?LOG\r\n\r\n\tinclude vioout.inc \r\n\tinclude dprintf.inc \r\n\r\nendif\r\n\r\n\tend start\r\n\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/JLOAD32.INC",
    "content": "\r\n;--- macros\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\n ifidni <text>,<\"\">\r\nsym db 0\r\n else\r\nsym db text,0\r\n endif\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nifndef ?KD\r\n?KD equ 0\r\nendif\r\n\r\n;--- constants\r\n\r\n?PAGEMAP\t\tequ\t3FEh\t;page table for mapping page tables (it's a PDE index)\r\n\r\n;--- structs\r\n\r\n;--- publics\r\n\r\nInitVMM\tproto\r\nFindDevice proto\r\nVMM_Add_DDB proto\r\nVMM_Remove_DDB proto\r\n_PageReserve proto\r\n_PageCommit proto\r\n_PageDecommit proto\r\n_PageFree proto\r\nGet_Cur_VM_Handle proto\r\n\r\nexterndef Begin_Nest_Exec:dword\r\nexterndef Exec_Int:dword\r\nexterndef End_Nest_Exec:dword\r\n\r\nexterndef VDS_Call_Table:dword\r\nexterndef ddb_0004:VxD_Desc_Block\r\nexterndef dfOldint20:fword\r\nexterndef g_dwIDT:dword\r\nif ?KD\r\nexterndef bKDdetected:byte\r\nendif\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/Linux.mak",
    "content": "\r\n# makefile that creates JLOAD.EXE\r\n# tools used:\r\n#------------------------\r\n# Assembler:    jwasm\r\n# OMF linker:   jwlink\r\n# COFF linker:  jwlink\r\n\r\n# the 32-bit part of jload must be linked with base 0xF8400000;\r\n#\r\n# The source files contain include files references in lower case,\r\n# but the name of the files are in upper case. So either store\r\n# the project on NTFS/FAT or create appropriate symlinks!\r\n\r\nifndef DEBUG\r\nDEBUG = 0\r\nendif\r\n\r\nifeq (1,$(DEBUG))\r\nOUTD = DEBUG\r\nAOPTD=-Sg -D_DEBUG -D?PEDBG=1 -D?RMDBG=1 -D?INITDBG=1 -D?JLMDBG=1\r\n#AOPTD=-Sg -D_DEBUG -D?V86HOOKDBG=1\r\nelse\r\nOUTD = RELEASE\r\nAOPTD=\r\nendif\r\n\r\nNAME  = JLOAD\r\nNAME32= JLOAD32\r\nVXD1  = VMM\r\nVXD4  = VDMAD\r\n\r\nASM   = jwasm -c -nologo $(AOPTD) -I../../Include -DOUTD=$(OUTD)\r\n\r\nLINK32=\r\nINC32=JLOAD.INC JLOAD32.INC DEBUG.INC ../../Include/JSYSTEM.INC ../../Include/JLM.INC\r\nINC16=JLOAD.INC             DEBUG.INC ../../Include/JSYSTEM.INC ../../Include/JLM.INC\r\n\r\nALL: $(OUTD) $(OUTD)/$(NAME).EXE\r\n\r\n$(OUTD):\r\n\t@mkdir -p $(OUTD)\r\n\r\n$(OUTD)/$(NAME).EXE: $(OUTD)/$(NAME).obj Linux.mak\r\n\t@jwlink format dos file $(OUTD)/$(NAME).obj name $@ op q,m=$(OUTD)/$(NAME)\r\n\r\n$(OUTD)/$(NAME).obj: $(NAME).ASM $(OUTD)/$(NAME32).bin $(INC16)\r\n\t@$(ASM) -omf -D__UNIX__ -Fl$(OUTD)/ -Fo$@ $(NAME).ASM\r\n\r\n$(OUTD)/$(NAME32).bin: $(OUTD)/$(NAME32).obj $(OUTD)/$(VXD1).obj $(OUTD)/$(VXD4).obj\r\n\t@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\r\n\r\n$(OUTD)/$(NAME32).obj: $(NAME32).ASM $(INC32) Linux.mak\r\n\t@$(ASM) -coff -Fl$(OUTD)/ -Fo$@ $(NAME32).ASM\r\n\r\n$(OUTD)/$(VXD1).obj: $(VXD1).ASM $(INC32) Linux.mak\r\n\t@$(ASM) -coff -Fl$(OUTD)/ -Fo$@ $(VXD1).ASM\r\n\r\n$(OUTD)/$(VXD4).obj: $(VXD4).ASM $(INC32) Linux.mak\r\n\t@$(ASM) -coff -Fl$(OUTD)/ -Fo$@ $(VXD4).ASM\r\n\r\nclean:\r\n\t@rm $(OUTD)/*.EXE\r\n\t@rm $(OUTD)/*.bin\r\n\t@rm $(OUTD)/*.obj\r\n\t@rm $(OUTD)/*.lst\r\n\t@rm $(OUTD)/*.map\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/MAKEFILE",
    "content": "\r\n# nmake makefile that creates JLOAD.EXE\r\n# tools used:\r\n#               default      alternate\r\n#----------------------------------------------\r\n# Assembler:    jwasm        MS masm (+Bin2Inc)\r\n# OMF linker:   jwlink       MS link16\r\n# COFF linker:  jwlink       -\r\n\r\n# the 32-bit part of jload must be linked with base 0xF8400000;\r\n# this isn't possible with (all?) MS link versions!\r\n\r\n# bin2inc translates binary files to assembly include files.\r\n# It's source is supplied with JWasm.\r\n\r\n!ifndef DEBUG\r\nDEBUG = 0\r\n!endif\r\n\r\n!if $(DEBUG)\r\nOUTDIR = DEBUG\r\n#AOPTD=-D_DEBUG -D?PEDBG=1 -D?RMDBG=1 -D?INITDBG=1 -D?JLMDBG=1\r\nAOPTD=-Sg -D_DEBUG -D?INITDBG=1\r\n!else\r\nOUTDIR = RELEASE\r\nAOPTD=\r\n!endif\r\n\r\nAOPT=-c -nologo $(AOPTD) -I..\\..\\Include -DOUTD=$(OUTDIR)\r\n\r\nNAME  = JLOAD\r\nNAME32= JLOAD32\r\nVXD1  = VMM\r\nVXD4  = VDMAD\r\n\r\n!ifndef MASM\r\nMASM=0\r\n!endif\r\n\r\n!if $(MASM)\r\nASM = ml.exe\r\nNAMEBIN32=$(OUTDIR)\\$(NAME32).inc\r\n!else\r\nASM = jwasm.exe\r\nNAMEBIN32=$(OUTDIR)\\$(NAME32).bin\r\n!endif\r\n\r\nINC32=jload.inc jload32.inc debug.inc ..\\..\\Include\\jsystem.inc ..\\..\\Include\\jlm.inc\r\nINC16=jload.inc             debug.inc ..\\..\\Include\\jsystem.inc ..\\..\\Include\\jlm.inc\r\n\r\nALL: $(OUTDIR) $(OUTDIR)\\$(NAME).EXE\r\n\r\n$(OUTDIR):\r\n\t@mkdir $(OUTDIR)\r\n\r\n$(OUTDIR)\\$(NAME).EXE: $(OUTDIR)\\$(NAME).obj MAKEFILE\r\n#\t@link16.exe /MAP:FULL/NOE/NON/ONERROR:NOEXE $*.OBJ, $*.EXE, $*.MAP;\r\n\t@jwlink.exe format dos file $*.obj name $*.EXE op q,m=$*\r\n\r\n$(OUTDIR)\\$(NAME).obj: $(NAME).asm $(NAMEBIN32) $(INC16)\r\n\t@$(ASM) $(AOPT) -Fl$(OUTDIR)\\ -Fo=$* $(NAME).asm\r\n\r\n$(NAMEBIN32): $(OUTDIR)\\$(NAME32).obj $(OUTDIR)\\$(VXD1).obj $(OUTDIR)\\$(VXD4).obj\r\n\t@jwlink.exe format raw bin f { $*.obj $(OUTDIR)\\$(VXD1).obj $(OUTDIR)\\$(VXD4).obj } name $*.bin disable 1014 op q,map=$*,offset=0xF8400000,start=_start\r\n!if $(MASM)\r\n\t@bin2inc.exe -q $*.bin $*.inc\r\n!endif\r\n\r\n$(OUTDIR)\\$(NAME32).obj: $(NAME32).asm $(INC32) MAKEFILE\r\n\t@$(ASM) -coff $(AOPT) -Fl$(OUTDIR)\\ -Fo=$* $(NAME32).asm\r\n\r\n$(OUTDIR)\\$(VXD1).obj: $(VXD1).asm $(INC32) MAKEFILE\r\n\t@$(ASM) -coff $(AOPT) -Fl$(OUTDIR)\\ -Fo=$* $(VXD1).asm\r\n\r\n$(OUTDIR)\\$(VXD4).obj: $(VXD4).asm $(INC32) MAKEFILE\r\n\t@$(ASM) -coff $(AOPT) -Fl$(OUTDIR)\\ -Fo=$* $(VXD4).asm\r\n\r\nclean:\r\n\t@del $(OUTDIR)\\*.EXE\r\n\t@if exist $(OUTDIR)\\*.inc del $(OUTDIR)\\*.inc\r\n\t@del $(OUTDIR)\\*.bin\r\n\t@del $(OUTDIR)\\*.obj\r\n\t@del $(OUTDIR)\\*.lst\r\n\t@del $(OUTDIR)\\*.map\r\n"
  },
  {
    "path": "Tools/JLOAD/VDMAD.ASM",
    "content": "\r\n;--- VDMAD support\r\n;--- best viewed with TAB size 4\r\n\r\n\t.386\r\n\t.model flat\r\n\r\n\toption casemap:none\r\n\toption proc:private\r\n\r\n\tinclude vds.inc\r\n\tinclude jlm.inc\r\n\tinclude jload32.inc\r\n\tinclude debug.inc\r\n\r\n;--- structure of the VDS call table in Jemm\r\n\r\nVDSCALL struct\r\n\t\t\tdd ?\t;EBP -> client (not used here)\r\npLock\t\tdd ?\t;EDI -> DDS, DX (not used here)\r\npUnlock\t\tdd ?\t;EDI -> DDS, DX, modifies EBX!\r\npScLock\t\tdd ?\t;EDI -> EDDS, DX, modifies EBX, ESI!\r\npScUnlock\tdd ?\t;EDI -> EDDS, DX (does nothing)\r\npReqBuffer\tdd ?\t;EDI -> DDS, DX, modifies EBX!\r\npRelBuffer\tdd ?\t;EDI -> DDS, DX, modifies EBX!\r\n\t\t\tdd ?\t;copy in  EDI -> DDS, DX, EBP -> client\r\n\t\t\tdd ?\t;copy out EDI -> DDS, DX, EBP -> client\r\n\t\t\tdd ?\t;disable translation\r\n\t\t\tdd ?\t;enable translation\r\npCopyInBuf\tdd ?\t;copy in  EDI -> DDS, DX, ECX, modifies EBX!\r\npCopyOutBuf dd ?\t;copy out EDI -> DDS, DX, ECX, modifies EBX!\r\nVDSCALL ends\r\n\r\n\t.data\r\n\r\nddb_0004 VxD_Desc_Block <    0,1,4,1,0,0,\"        \",0,0,0,0,0,0,0,offset vdmad_services,?NUMSERV0004>\r\n    \r\n;--- VDMAD service table\r\n\r\nvdmad_services label dword\r\n\tdd Get_VDMAD_Version\r\n\tdd Lock_DMA_Region\r\n\tdd Unlock_DMA_Region\r\n\tdd Scatter_Lock\r\n\tdd Scatter_Unlock\r\n\tdd Request_Buffer\r\n\tdd Release_Buffer\r\n\tdd Copy_To_Buffer\t\t;new v5.69\r\n\tdd Copy_From_Buffer\t\t;new v5.69\r\n?NUMSERV0004 equ ($ - vdmad_services)/4        \r\n\r\n\t.code\r\n\r\n;--- VDMA device functions\r\n\r\nGet_VDMAD_Version proc\r\n\tmov eax,100h\r\n\tclc\r\n\tret\r\n\talign 4\r\n\r\nGet_VDMAD_Version endp\r\n\r\n;--- esi = start region (linear address)\r\n;--- ecx = size region (bytes)\r\n;--- dl = flags (bit 0: check for 64 kB crossing, bit 1: check for 128 kB)\r\n;--- returns phys address in EDX if ok\r\n;--- returns size in ECX on error, in AL error code\r\n\r\n;--- Jemm is unable to handle addresses > 110000h\r\n;--- therefore do it here.\r\n\r\nLock_DMA_Region proc uses edi ebx\r\n\r\n\t@dprintf ?DMADBG, <\"Lock_DMA_Region, addr=%X, size=%X\",10>, esi, ecx\r\n\tpush esi\r\n\tlea ebx, [esi+ecx-1]\t;let EBX point to the last byte\r\n\tmov eax, esi\r\n\tshr eax, 12\r\n\tshr ebx, 12\r\n\tsub ebx, eax\r\n\tjc @@error2\r\n\r\n\tshr esi, 12\r\n\tlea esi, [esi*4 + (?PAGEMAP shl 22)]\r\n\tmov ecx, ebx\r\n\tlodsd\r\n\tand ax, 0F000h\r\n\tmov bl, dl\r\n\tmov edx, eax\r\n\tjecxz @@done\r\n\tmov edi, edx\t; save first phys page in EDX\r\n\t.while ecx\r\n\t\tlodsd\r\n\t\tand eax, eax\r\n\t\tjz @@error\r\n\t\tand ax, 0F000h\r\n\t\tadd edi,1000h\r\n\t\tcmp eax, edi\r\n\t\tjnz @@error\t; not contiguous\r\n\t\tdec ecx\r\n\t.endw\r\n\ttest bl, 1\t\t; 64 kB border crossing test?\r\n\tjz @@done\r\n\tmov eax, edx\r\n\tshr eax, 16\r\n\tshr edi, 16\r\n\tcmp eax, edi\r\n\tjnz @@error2\r\n@@done:\r\n\tpop esi\r\n\tmov ax, si\r\n\tand ax, 0FFFh\r\n\tor dx, ax\r\n\t@dprintf ?DMADBG, <\"Lock_DMA_Region, returns edx=%X\",10>, edx\r\n\tret\r\n@@error:\r\n\tpop esi\r\n\tmov ax, si\r\n\tand ax, 0FFFh\r\n\tor dx, ax\r\n\tsub ebx, ecx\r\n\tmov ecx, ebx\r\n\tshl ecx, 12\r\n\tstc\r\n\tret\r\n@@error2:\r\n\tpop esi\r\n\txor ecx, ecx\r\n\tstc\r\n\tret\r\n\talign 4\r\nLock_DMA_Region endp\r\n\r\n;--- esi = start region\r\n;--- ecx = size region\r\n;--- dl = flags\r\n\r\nUnlock_DMA_Region proc uses esi edi ebx\r\n\tsub esp, size DDS\r\n\tmov edi, esp\r\n\tmov [edi].DDS.dwSize, ecx\r\n\tmov [edi].DDS.dwOfs, esi\r\n\tmov dword ptr [edi].DDS.wSeg, 0\t\t;clear wSeg and wID\r\n\tmov dh,0\r\n\tshr dl,4\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pUnlock\t\t\t;call vds_unlock\r\n\tlea esp,[esp+size DDS]\r\n\tret\r\n\talign 4\r\n\r\nUnlock_DMA_Region endp\r\n\r\n;--- edi = DDS\r\n;--- al = flags\r\n\r\nScatter_Lock proc uses esi ebx\r\n\txor edx,edx\r\n\ttest al,1\r\n\tjz @F\r\n\tor dl,VDSF_PTE\r\n@@:\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pScLock\r\n\tret\r\n\talign 4\r\nScatter_Lock endp\r\n\r\n;--- edi = DDS\r\n;--- al = flags\r\n\r\nScatter_Unlock proc\r\n\txor edx,edx\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pScUnlock\t;this is a dummy\r\n\tret\r\n\talign 4\r\nScatter_Unlock endp\r\n\r\n;--- esi = region\r\n;--- ecx = size\r\n;--- out: ebx=id, edx=phys. addr\r\n\r\nRequest_Buffer proc uses esi edi\r\n\tsub esp, size DDS\r\n\tmov edi, esp\r\n\tmov [edi].DDS.dwSize, ecx\r\n\tmov [edi].DDS.dwOfs, esi\r\n\txor edx,edx\r\n\tmov dword ptr [edi].DDS.wSeg, edx\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pReqBuffer\r\n\tjc @F\r\n\tmovzx ebx,[edi].DDS.wID\r\n\tmov edx,[edi].DDS.dwPhys\r\n@@:\r\n\tlea esp,[esp+size DDS]\r\n\tret\r\n\talign 4\r\nRequest_Buffer endp\r\n\r\n;--- ebx = id\r\n\r\nRelease_Buffer proc uses ebx edi\r\n\tsub esp, size DDS\r\n\tmov edi, esp\r\n\tmov [edi].DDS.wID, bx\r\n\txor edx,edx\t\t\t\t\t;no NOT copy out of DMA buffer!\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pRelBuffer\r\n\tlea esp,[esp+size DDS]\r\n\tret\r\n\talign 4\r\nRelease_Buffer endp\r\n\r\n;--- ebx = id\r\n;--- esi = offset src\r\n;--- edi = buffer offset \r\n;--- ecx = buffer size\r\n\r\nCopy_To_Buffer proc uses ebx esi edi\r\n\r\n\tsub esp, size DDS\r\n\tmov [esp].DDS.dwSize, ecx\r\n\tmov ecx, edi\r\n\tmov edi, esp\r\n\tmov [edi].DDS.dwOfs, esi\r\n\txor edx,edx\r\n\tmov [edi].DDS.wSeg, dx\r\n\tmov [edi].DDS.wID, bx\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pCopyInBuf\r\n\tlea esp,[esp+size DDS]\r\n\tret\r\n\talign 4\r\nCopy_To_Buffer endp\r\n\r\n;--- ebx = id\r\n;--- esi = offset src\r\n;--- edi = buffer offset \r\n;--- ecx = buffer size\r\n\r\nCopy_From_Buffer proc uses ebx esi edi\r\n\tsub esp, size DDS\r\n\tmov [esp].DDS.dwSize, ecx\r\n\tmov ecx, edi\r\n\tmov edi, esp\r\n\tmov [edi].DDS.dwOfs, esi\r\n\txor edx,edx\r\n\tmov [edi].DDS.wSeg, dx\r\n\tmov [edi].DDS.wID, bx\r\n\tmov eax,[VDS_Call_Table]\r\n\tcall [eax].VDSCALL.pCopyOutBuf\r\n\tlea esp,[esp+size DDS]\r\n\tret\r\n\talign 4\r\nCopy_From_Buffer endp\r\n\r\n\tend\r\n\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/VIOOUT.INC",
    "content": "\r\n;--- low-level video access\r\n;--- no assumption about ds,es.\r\n;--- ss is supposed to be flat.\r\n\r\nVPUTCHR PROC\r\n\tpush ds\r\n\tpushad\r\n\tpush ss\r\n\tpop ds\r\n if ?KD\r\n\ttest bKDdetected,1\r\n\tjz @F\r\n\tmov dl, al\r\n\txor ax, ax\r\n\tint 41h\r\n\tjmp exit\r\n@@:\r\n endif\r\nif ?USEMONO    \r\n\tmov edi,0B0000h\r\n\tmov ebx,7\r\nelse\r\n\tMOV EDI,0B8000h\r\n\tCMP BYTE ptr DS:[463h],0B4h\r\n\tJNZ @@IS_COLOR\r\n\tXOR DI,DI\r\n@@IS_COLOR:\r\n\tmovzx EBX, WORD PTR DS:[44Eh]\r\n\tADD EDI, EBX\r\n\tMOVZX EBX, BYTE PTR DS:[462h]\r\nendif\r\n\tmov esi, edi\r\n\tMOVZX ECX, BYTE PTR DS:[EBX*2+450h+1]\t;ROW\r\nif ?USEMONO\r\n\tMOV EAX, 80\r\nelse\r\n\tMOVZX EAX, WORD PTR DS:[44Ah]\r\nendif\r\n\tMUL ECX\r\n\tMOVZX EDX, BYTE PTR DS:[EBX*2+450h]\t;COL\r\n\tADD EAX, EDX\r\n\tMOV DH,CL\r\n\tLEA EDI, [EDI+EAX*2]\r\n\tMOV AL, [ESP+1Ch]\r\n\tCMP AL, 13\r\n\tJZ exit\r\n\tCMP AL, 10\r\n\tJZ @@LF\r\n\tMOV [EDI], AL\r\n\tMOV byte ptr [EDI+1], 07\r\n\tINC DL\r\nif ?USEMONO\r\n\tcmp dl,80\r\nelse\r\n\tCMP DL, BYTE PTR DS:[44Ah]\r\nendif\r\n\tJB @@OLDLINE\r\n@@LF:\r\n\tMOV DL, 00\r\n\tINC DH\r\nif ?USEMONO\r\n\tCMP DH, 24\r\nelse\r\n\tCMP DH, BYTE PTR DS:[484h]\r\nendif\r\n\tJBE @@OLDLINE\r\n\tDEC DH\r\n\tCALL SCROLL_SCREEN\r\n@@OLDLINE:\r\n\tMOV DS:[EBX*2+450h],DX\r\nexit:\r\n\tPOPAD\r\n\tpop ds\r\n\tRET\r\n\talign 4\r\n\r\n;--- scroll screen up 1 line\r\n;--- esi -> start screen\r\n\r\nSCROLL_SCREEN:\r\n\tpush es\r\n\tpush ds\r\n\tpop es\r\n\tCLD\r\n\tmov edi,esi\r\nif ?USEMONO\r\n\tmov eax,80\r\nelse\r\n\tmovzx eax,word ptr ds:[44Ah]\r\nendif\r\n\tpush eax\r\n\tlea esi, [esi+2*eax]\r\nif ?USEMONO\r\n\tmov CL, 24\r\nelse\r\n\tMOV CL, DS:[484h]\r\nendif\r\n\tmul cl\r\n\tmov ecx,eax\r\n\trep movsw\r\n\tpop ecx\r\n\tmov ax,0720h\r\n\trep stosw\r\n\tpop es\r\n\tretn\r\n\talign 4\r\n\r\nVPUTCHR ENDP\r\n\r\n"
  },
  {
    "path": "Tools/JLOAD/VMM.ASM",
    "content": "\r\n;--- VMM functions\r\n;--- best viewed with TAB size 4\r\n\r\n\t.386\r\n\t.model flat\r\n\r\n\toption casemap:none\r\n\toption proc:private\r\n\r\n\tinclude x86.inc\r\n\tinclude jsystem.inc\r\n\tinclude jlm.inc\r\n\tinclude jload.inc\r\n\tinclude jload32.inc\r\n\tinclude debug.inc\r\n\r\n?MAXBP\tequ 32\r\n?SYSTABSCRATCHIDX equ 3feh\t;index PTE for scratch address space\r\n\r\n?APIINT equ 2Fh\r\n\r\nCBDYN\tstruc\r\ndwCallback\tdd ?\r\ndwRefData\tdd ?\r\nCBDYN\tends\r\n\r\n;--- v86-hook\r\n\r\nHOOKV86 struc\r\ndwHook\t dd ?\r\nHOOKV86 ends\r\n\r\n\t.data\r\n\r\ndfOldint20 \t\tdf 0\t;saved Jemm's int 20h vector protected mode\r\n\talign 4\r\nVDS_Call_Table\tdd 0\t;Jemm's VDS call table\r\nVCPI_Call_Table dd 0\t;Jemm's VCPI call table\r\nIO_Trap_Table\tdd 0\t;Jemm's IO trap table\r\nbprm\t \t\tdd 0\t;real-mode address of first bp\r\ndwTSSbase\t\tdd 0\t;start TSS of monitor\r\n\r\nlpV86Hooks\t\tdd 0\t;page for hooks (v86 int chain)\r\n;lpV86_Monitor\tdd 0\t;address V86_Monitor() function in Jemm\r\n;lpStackCurr\t\tdd 0\t;address of dwStackCurr variable in Jemm\r\nppV86Hooks\t\tdd 0\t;v5.85: address of pV86Hooks variable in Jemm\r\nretaddr\t\t\tdd 0\t;v5.85: return address to V86_Monitor\r\nlpV86Faults\t\tdd 0\t;v5.86: original address stored in vmm_service_table\r\n\r\nScratchPTE\t\tdd 0\t;PTE used to simplify page memory management\r\noldiohandler\tdd 0\t;saved Jemm's IO handler\r\noldbp1\t\t\tdd 0\t;saved Jemm's static callback\r\nIO_Handler_Region dd 0\t;linear address IO handler array (256 kB)\r\nIO_Handler_Pages dq 0\t;64 pages reserved for IO handlers\r\nInt2fHooked     dd 0\r\nifndef _DEBUG\r\nprevint2f       dd 0\r\nelse\r\nprevint2f       dd offset finalint2f\r\ntrueprevint2f   dd 0\r\nendif\r\nif ?KD\r\nbKDdetected\t\tdb 0\r\n\talign 4\r\nendif\r\n\r\n;--- generic \"control block\" returned by Get_Cur_VM_Handle;\r\n;--- the only important part is linear address of the client register struct;\r\n\r\nvmcb\tcb_s <0,0,?TOS - sizeof Client_Reg_Struc,1>\r\n\r\n;--- start of device list known to the int 20h loader\r\n\r\nddb_list label dword\r\nd0001\tVxD_Desc_Block <ddb_0004,1,1,1,0,0,\"        \",0,0,0,0,0,0,0,offset vmm_services,  ?NUMSERV0001>\r\n\r\nGetDOSBuffer proto\t;defined in JLOAD32\r\n\r\n\talign 4\r\n\r\n;--- VMM service table\r\n\r\nvmm_services label dword\r\n\r\n\t\t\t\t\tdd Get_VMM_Version\t\t\t\t;0=get version\r\n\t\t\t\t\tdd Get_Cur_VM_Handle            ;1\r\n\t\t\t\t\tdd Allocate_V86_Call_Back\r\n\t\t\t\t\tdd Crash_Cur_VM\r\n\t\t\t\t\tdd Hook_V86_Int_Chain\r\n\t\t\t\t\tdd Get_V86_Int_Vector\r\n\t\t\t\t\tdd Set_V86_Int_Vector\r\n\t\t\t\t\tdd Get_PM_Int_Vector\r\n\t\t\t\t\tdd Set_PM_Int_Vector\r\nSimulate_Int\t\tdd 0\r\nSimulate_Iret\t\tdd 0\r\nSimulate_Far_Call\tdd 0\r\n\t\t\t\t\tdd Simulate_Far_Jmp\r\nSimulate_Far_Ret\tdd 0\r\n\t\t\t\t\tdd Simulate_Far_Ret_N\r\n\t\t\t\t\tdd Build_Int_Stack_Frame\r\n\t\t\t\t\tdd Simulate_Push\r\n\t\t\t\t\tdd Simulate_Pop\r\n\t\t\t\t\tdd _PageFree\r\n\t\t\t\t\tdd _PhysIntoV86\r\n\t\t\t\t\tdd _LinMapIntoV86\r\n\t\t\t\t\tdd Hook_V86_Fault\r\n\t\t\t\t\tdd Hook_PM_Fault\r\nBegin_Nest_Exec\t\tdd 0\r\nExec_Int\t\t\tdd 0\r\nResume_Exec\t\t\tdd 0\r\nEnd_Nest_Exec\t\tdd 0\r\n\t\t\t\t\tdd Save_Client_State\r\n\t\t\t\t\tdd Restore_Client_State\r\nSimulate_IO \t\tdd 0\r\n\t\t\t\t\tdd Install_Mult_IO_Handlers\r\n\t\t\t\t\tdd Install_IO_Handler\r\n\t\t\t\t\tdd VMM_Add_DDB\r\n\t\t\t\t\tdd VMM_Remove_DDB\r\n\t\t\t\t\tdd Remove_IO_Handler\r\n\t\t\t\t\tdd Remove_Mult_IO_Handlers\r\n\t\t\t\t\tdd Unhook_V86_Int_Chain\r\n\t\t\t\t\tdd Unhook_V86_Fault\r\n\t\t\t\t\tdd Unhook_PM_Fault\r\n\t\t\t\t\tdd _PageReserve\r\n\t\t\t\t\tdd _PageCommit\r\n\t\t\t\t\tdd _PageDecommit\r\n\t\t\t\t\tdd _PageCommitPhys\r\n\t\t\t\t\tdd Free_V86_Call_Back\r\nYield\t\t\t\tdd 0        \r\nMoveMemory  \t\tdd 0        \r\n\t\t\t\t\tdd _Allocate_GDT_Selector ; v5.83\r\n\t\t\t\t\tdd _Free_GDT_Selector ; v5.83\r\n\t\t\t\t\tdd Get_DDB ; v5.83\r\n\t\t\t\t\tdd V86ToPM ; v5.85\r\n\t\t\t\t\tdd GetDOSBuffer ; v5.86\r\n\r\n?NUMSERV0001 equ ($ - vmm_services)/4\r\n\r\n;--- VMM callback table\r\n\r\nv86_cb\tCBDYN ?MAXBP dup (<>)\r\nv86faulthooks dd 32 dup (0)\t;v5.86\r\n\r\n\t.code\r\n\r\n;--- InitVMM\r\n;--- ECX -> VMMS structure\r\n\r\nInitVMM proc public\r\n\r\n\t@dprintf ?INITDBG,<\"Init enter\",10>\r\n\r\n;--- save the vectors received from Jemm;\r\n;--- e08_ServiceTable is a VMM_SERV_TABLE struct\r\n\r\n\tmov esi, [ecx].VMMS.emx08.e08_ServiceTable\r\n\tlodsd\r\n\tmov Simulate_Int, eax\r\n\tlodsd\r\n\tmov Simulate_Iret, eax\r\n\tlodsd\r\n\tmov Simulate_Far_Call, eax\r\n\tlodsd\r\n\tmov Simulate_Far_Ret, eax\r\n\tlodsd\r\n\tmov Begin_Nest_Exec, eax\r\n\tlodsd\r\n\tmov Exec_Int, eax\r\n\tlodsd\r\n\tmov Resume_Exec, eax\r\n\tlodsd\r\n\tmov End_Nest_Exec, eax\r\n\tlodsd\r\n\tmov Simulate_IO, eax\r\n\tlodsd\r\n\tmov Yield, eax\r\n\tlodsd\r\n\tmov VDS_Call_Table, eax\r\n\tlodsd\r\n\tmov VCPI_Call_Table, eax\r\n\tlodsd\r\n\tmov IO_Trap_Table, eax\r\n;--- v5.87: next 2 fields obsolete since v5.85; now removed\r\n;\tlodsd\r\n;\tmov lpV86_Monitor, eax\r\n;\tlodsd\r\n;\tmov lpStackCurr, eax\r\n\tlodsd\r\n\tmov MoveMemory, eax\t\t;new for v5.69\r\n\tlodsd\r\n\tmov ppV86Hooks, eax\t\t;new for v5.85\r\n\tlodsd\r\n\tmov lpV86Faults, eax\r\n\tmov [esi-4],offset V86Faults\t;new for v5.86\r\n\tmov [esi],offset call_control_procs\t;new for v5.86\r\nif ?KD\r\n\tmov ax, 4Fh\r\n\tint 41h\r\n\tcmp ax, 0F386h\r\n\tjnz @F\r\n\tor [bKDdetected], 1\r\n@@:\r\nendif\r\n\r\n;--- init the mapping page for memory management\r\n\r\n\tcall InitMapPage\r\n\tjc @@exit\r\n\r\n;--- modify one of Jemm's static BPs\r\n;--- to become the generic v86 callback BP.\r\n\r\n\tmov esi, [ecx].VMMS.emx08.e08_pCallBack\r\n\tmov eax, offset V86_Callback_Handler\r\n\txchg eax, [esi]\r\n\tmov [oldbp1], eax\r\n\tmov eax, [ecx].VMMS.emx08.e08_CallBackRM\r\n\tmov [bprm],eax\r\n\r\n;--- since jemm v5.70, the TSS is no longer at a fix address;\r\n\r\n\tmovzx edx, [ecx].VMMS.emx08.e08_TR\r\n\tadd edx, dword ptr [ecx].VMMS.emx08.e08_GDTR+2\r\n\tmov ah,[edx].DESCRIPTOR.bA2431\r\n\tmov al,[edx].DESCRIPTOR.bA1623\r\n\tshl eax,16\r\n\tmov ax,[edx].DESCRIPTOR.wA0015\r\n\tmov [dwTSSbase],eax\r\n\r\n\tmov eax, dword ptr [ecx].VMMS.emx08.e08_IDTR+2\r\n\tmov [g_dwIDT], eax\r\n\r\n;--- install an int 20h handler in IDT;\r\n;--- it will install the jlm dynamic linking mechanism (Win3x/9x style)\r\n\r\n\tmov esi, eax\r\n\tadd esi, 20h*8\r\n\tmov ax, [esi].GATE.wOfsHi\r\n\tshl eax, 16\r\n\tmov ax, [esi].GATE.wOfsLo\r\n\tmov dword ptr [dfOldint20+0], eax\r\n\tmov ax, [esi].GATE.wSeg\r\n\tmov word ptr [dfOldint20+4], ax\r\n\tmov eax, offset Int20_Handler\r\n\tmov [esi].GATE.wOfsLo, ax\r\n\tshr eax, 16\r\n\tmov [esi].GATE.wOfsHi, ax\r\n\tmov eax, cs\r\n\tmov [esi].GATE.wSeg, ax\r\n@@exit:\r\n\t@dprintf ?INITDBG, <\"Init exit\",10>\r\n\tret\r\n\talign 4\r\n\r\nInitVMM Endp\r\n\r\n;--- int 20h dynamic linking\r\n\r\nInt20_Handler proc\r\n\r\n\ttest byte ptr [esp].IRETDV86._Efl+2,2\t;called from v86-mode?\r\n\tjnz isv86\r\n\tpushad\r\n\tmov esi,[esp+8*4].IRETDV86._Eip\r\n\tmov eax,[esi]\t\t\t\t\t;it is \"INT 20h\" followed by a dword\r\n\tsub esi,2\r\n\tmov [esp+8*4].IRETDV86._Eip, esi\r\n\tmovzx ecx, ax\r\n\tshr eax,16\r\n\tmov edx, offset ddb_list\r\n@@nextdevice:\r\n\tcmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number\t;is device known?\r\n\tjz @@device_found\r\n\tmov edx, [edx].VxD_Desc_Block.DDB_Next\r\n\tand edx, edx\r\n\tjnz @@nextdevice\r\n@@error:\r\n;\tpopad\r\n\tcall Crash_Cur_VM\t;AX:CX contain VM:Function to link to\r\n@@device_found:\r\n\tmov bh,ch\r\n\tmov bl,0FFh\r\n\tand bh,80h\r\n\tand ch,7Fh\r\n\tcmp ecx, [edx].VxD_Desc_Block.DDB_Service_Table_Size\r\n\tjnc @@error\r\n\tmov eax, [edx].VxD_Desc_Block.DDB_Service_Table_Ptr\r\n\tshl ecx, 2\r\n\tshr bh,3\r\n\tadd bh,15h\r\n\tadd eax, ecx\r\n\tmov word ptr [esi],bx\t;opcode for \"call/jmp dword ptr []\"\r\n\tmov [esi+2],eax\r\n\tpopad\r\n\tiretd\r\nisv86:\r\n\tjmp cs:[dfOldint20]\r\n\talign 4\r\n\r\nInt20_Handler endp\r\n\r\n;--- generic callback;\r\n;--- has replaced Jemm's static callback on init\r\n\r\nV86_Callback_Handler  proc\r\n\tmov eax,[ebp].Client_Reg_Struc.Client_CS \r\n\tsub ax,word ptr [bprm+2]\r\n\tjz isdefbp\r\n\tinc ax\r\n\tneg ax\r\n\tmovzx eax,ax\r\n\tshl eax,3\r\n\tmov edx, [eax+offset v86_cb].CBDYN.dwRefData\r\n\tjmp [eax+offset v86_cb].CBDYN.dwCallback\r\nisdefbp:\r\n\tjmp [oldbp1]\r\n\talign 4\r\nV86_Callback_Handler endp\r\n\r\nGet_VMM_Version proc\r\n\tmov eax, ?VERSIONLOW shl 16 + ?VERSIONHIGH\r\n\tclc\r\n\tret\r\n\talign 4\r\nGet_VMM_Version endp\r\n\r\nGet_Cur_VM_Handle proc public \r\n\tmov ebx, offset vmcb\r\n\tclc\r\n\tret\r\n\talign 4\r\nGet_Cur_VM_Handle endp\r\n\r\n;--- deliberately cause a GPF. This will result in an \r\n;--- invalid opcode exception reported to v86-mode\r\n\r\nCrash_Cur_VM proc\r\n\tpush cs\r\n\tpop ss\r\n\talign 4\r\nCrash_Cur_VM endp\r\n\r\n;--- get a v86 callback in EAX\r\n;--- inp: ESI=callback, EDX=dwRefData\r\n;--- C on error\r\n\r\nAllocate_V86_Call_Back proc\r\n\tpush edi\r\n\tmov edi, offset v86_cb\r\n\tmov ecx, ?MAXBP\r\n\txor eax, eax\r\n@@nextitem:\r\n\tcmp eax, [edi].CBDYN.dwCallback\r\n\tjz @@freecb\r\n\tadd edi, size CBDYN\r\n\tloop @@nextitem\r\n\tpop edi\r\n\tstc\r\n\tret\r\n@@freecb:\r\n\tmov [edi].CBDYN.dwCallback, esi\r\n\tmov [edi].CBDYN.dwRefData, edx\r\n\tsub edi, offset v86_cb\r\n\tshr edi, 3\r\n\tinc edi\r\n\tmov ax, word ptr [bprm+2]\r\n\tsub ax, di\r\n\tshl eax, 16\r\n\tmov ax,di\r\n\tshl ax,4\r\n\tadd ax, word ptr [bprm+0]\r\n\tpop edi\r\n\tret\r\n\talign 4\r\nAllocate_V86_Call_Back endp\r\n\r\n;--- free a v86 callback in EAX\r\n\r\nFree_V86_Call_Back proc\r\n\tmovzx edx, ax\r\n\tshr eax, 16\r\n\tinc eax\r\n\tmovzx ecx, word ptr [bprm+2]\r\n\tsub ecx, eax\r\n\tjc @@fail\r\n\tcmp ecx, ?MAXBP\r\n\tjae @@fail\r\n\tpush ecx\r\n\tadd ecx, eax\r\n\tshl ecx, 4\r\n\tdec eax\r\n\tshl eax, 4\r\n\tadd eax, edx\r\n\tmovzx edx, word ptr [bprm+0]\r\n\tadd ecx, edx\r\n\tcmp eax, ecx\r\n\tpop ecx\r\n\tjnz @@fail\r\n\tmov [v86_cb+ecx*8].CBDYN.dwCallback,0\r\n\tret\r\n@@fail:\r\n\tstc\r\n\tret\r\n\talign 4\r\nFree_V86_Call_Back endp\r\n\r\n;--- eax = word/dword to push\r\n\r\nSimulate_Push proc\r\n\tMOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP\r\n\tMOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS\r\n\tsub dx,2\r\n\tSHL ecx, 4\r\n\tadd ecx, edx\r\n\tMOV word ptr [EBP].Client_Reg_Struc.Client_ESP,dx\r\n\tmov [ecx],ax\r\n\tret\r\n\talign 4\r\nSimulate_Push endp\r\n\r\n;--- out: eax = word which has been poped\r\n\r\nSimulate_Pop proc\r\n\tMOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP\r\n\tMOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS\r\n\tSHL ecx, 4\r\n\tadd ecx, edx\r\n\tmovzx eax,word ptr [ecx]\r\n\tadd dx,2\r\n\tMOV word ptr [EBP].Client_Reg_Struc.Client_ESP,dx\r\n\tret\r\n\talign 4\r\nSimulate_Pop endp\r\n\r\n;--- cx == segment\r\n;--- edx == offset\r\n\r\nSimulate_Far_Jmp proc\r\n\tmov [EBP].Client_Reg_Struc.Client_EIP, edx\r\n\tmov word ptr [EBP].Client_Reg_Struc.Client_CS,cx\r\n\tret\r\n\talign 4\r\nSimulate_Far_Jmp endp\r\n\r\n;--- eax == number of bytes to pop \r\n\r\nSimulate_Far_Ret_N proc\r\n\tpush eax\r\n\tcall [Simulate_Far_Ret]\r\n\tpop eax\r\n\tadd word ptr [EBP].Client_Reg_Struc.Client_ESP, ax\r\n\tret\r\n\talign 4\r\nSimulate_Far_Ret_N endp\r\n\r\n;--- cx=segment, edx=offset to call\r\n\r\nBuild_Int_Stack_Frame proc\r\n\tpush ecx\r\n\tpush edx\r\n\tmov eax, [EBP].Client_Reg_Struc.Client_EFlags\r\n\tcall Simulate_Push\r\n\tpop edx\r\n\tpop ecx\r\n\tcall [Simulate_Far_Call]\r\n\tand byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1,not (1+2)\r\n\tret\r\n\talign 4\r\nBuild_Int_Stack_Frame endp\r\n\r\n;--- in: EDI = save buffer\r\n;---     EBP = client_reg_struc\r\n;--- no return value\r\n\r\nSave_Client_State proc\r\n\tpush edi\r\n\tpush esi\r\n\tmov esi, ebp\r\n\tmov ecx, (size Client_Reg_Struc) / 4\r\n\trep movsd\r\n\tpop esi\r\n\tpop edi\r\n\tret\r\n\talign 4\r\nSave_Client_State endp\r\n\r\n;--- in: ESI = save buffer\r\n;--- no return value\r\n\r\nRestore_Client_State proc\r\n\tpush edi\r\n\tpush esi\r\n\tmov edi, ebp\r\n\tmov ecx, (size Client_Reg_Struc) / 4\r\n\trep movsd\r\n\tpop esi\r\n\tpop edi\r\n\tret\r\n\talign 4\r\nRestore_Client_State endp\r\n\r\n;--- Allocate_GDT_Selector( high32 descriptor, low32 descriptor, flags )\r\n;--- returns selector in eax, GDT selector in loword edx, GDT size in hiword edx\r\n;--- high32 desc: [esp+4][8]\r\n;--- low32  desc: [esp+4][4]\r\n;--- flags:       [esp+4][0]\r\n\r\n_Allocate_GDT_Selector proc\r\n\tmov ecx, 8\r\n\tsub esp, ecx\r\n\tsgdt [esp]\r\n\tmovzx edx, word ptr [esp]\r\n\tmov eax, [esp+2]\r\n\tinc edx\r\n\tadd esp, ecx\r\nnextdesc:\r\n\tcmp ecx, edx\r\n\tjae notfound\r\n\tcmp byte ptr [eax+ecx+5], 0\r\n\tjz found\r\n\tadd ecx, 8\r\n\tjmp nextdesc\r\nfound:\r\n\tpush edx\r\n\tmov edx, [esp+8][1*4]\r\n\tmov [eax+ecx+0], edx\r\n\tmov edx, [esp+8][2*4]\r\n\tmov [eax+ecx+4], edx\r\n\tpop edx\r\n\tmov eax, ecx\r\n\tret\r\nnotfound:\r\n\txor eax, eax\r\n\txor edx, edx\r\n\tret\r\n_Allocate_GDT_Selector endp\r\n\r\n;--- Free_GDT_Selector( wSelector, flags )\r\n;--- todo: limit checks\r\n\r\n_Free_GDT_Selector proc\r\n\tmov ecx, [esp+4]\r\n\tsub esp, 8\r\n\tsgdt [esp]\r\n\tmov eax, [esp+2]\r\n\tadd esp, 8\r\n\tmov byte ptr [eax+ecx+5],0\r\n\tret\r\n_Free_GDT_Selector endp\r\n\r\n;--- Get_DDB\r\n;--- find a device\r\n;--- eax=device ID, edi=device name (or NULL)\r\n;--- out: device DDB in ECX!\r\n;--- modifies ECX only.\r\n\r\nGet_DDB proc uses eax edx\r\n\tcall FindDevice\r\n\tmov ecx, eax\r\n\tjnc @F\r\n\txor ecx, ecx\r\n@@:\r\n\tret\r\nGet_DDB endp\r\n\r\n;-------------------------\r\n;--- the IO-port trapping is implemented very simple\r\n;--- there is an array of 65536 vectors in a 256 kB memory region\r\n;--- for all possible IO ports. On init the memory is uncommitted,\r\n;--- but if someone installs a port trap in a 400h set which has\r\n;--- no other trap yet, a new page will be allocated.\r\n\r\n;--- called with port in EDX, value in EAX, type in CX\r\n\r\nNew_IO_Handler proc\r\n\tmovzx edx,dx\r\n\tmov esi, [IO_Handler_Region]\r\n\tcmp dword ptr [esi+edx*4],0\r\n\tjz @F\r\n\tjmp dword ptr [esi+edx*4]\r\n@@:\r\n;--- v5.84: jump to the old trap handler - it will handle\r\n;---        ISA DMA and A20\r\n;\tVMMJmp Simulate_IO\r\n\tjmp [oldiohandler]\r\n\talign 4\r\n\r\nNew_IO_Handler endp\r\n\r\n;--- scan Jemm's table of IOTRAPENTRYs\r\n;--- the table defines ranges, but there may be \"holes\" ( untrapped ports )\r\n;--- so first check if the port is actually really trapped.\r\n\r\n;--- v5.84: Jemm's table is no longer imported.\r\n;---        instead, if port isn't trapped, the old (=Jemm's) handler will be run.\r\n\r\n;--- structure of IO_Trap_Table:\r\n;---   dd ?  ; the default trap handler\r\n;---   dd ?  ; number of IOTRAPENTRIES in the following table\r\n;---   IOTRAPENTRIES[]\r\n\r\nImportJemmIOTraps proc uses esi\r\n\r\n\t@dprintf ?IODBG, <\"ImportJemmIOTraps\",10>\r\n\tmov esi, [IO_Trap_Table]\r\nif 1\r\n\tmov eax, offset New_IO_Handler\r\n\txchg eax, [esi]\r\n\tmov [oldiohandler], eax\r\n\tret\r\nelse\r\n\tlodsd\r\n\tmov [esi-4], offset New_IO_Handler\r\n\tmov [oldiohandler],eax\r\n\tlodsd\r\n\tmov ecx, eax\r\n\tjecxz @@done\r\n@@nextrange:\r\n\tlodsb\t\t\t; get bStart field\r\n\tmovzx edx, al\r\n\tlodsb\t\t\t; get bEnd field\r\n\tmovzx eax, al\r\n\tsub eax, edx\r\n\tinc eax\r\n\tpush ecx\r\n\tmov ecx, eax\r\n\tlodsd\t\t\t; get dwProc field\r\n\tpush esi\r\n\tmov esi, eax\r\n@@nextport:\r\n\tpush ecx\r\n\tpush edx\r\n\r\n\tmov eax, [dwTSSbase]\r\n\tmovzx ecx, [eax].TSSSEG.tsOfs\r\n\tadd eax, ecx\r\n\tbt [eax], edx\t\t\t\t; port trapped?\r\n\tjnc @F\r\n\tcall CommitTrapPage\r\n\tjc @F\r\n\tmov eax, [IO_Handler_Region]\r\n\tmov [edx*4+eax], esi\r\n@@:\r\n\tpop edx\r\n\tpop ecx\r\n\tinc edx\t\t\t; next port\r\n\tloop @@nextport\r\n\tpop esi\r\n\tpop ecx\r\n\tloop @@nextrange\r\n@@done:\r\n\tret\r\nendif\r\n\talign 4\r\n\r\nImportJemmIOTraps endp\r\n\r\n;--- dx=port\r\n\r\nCommitTrapPage proc\r\n\tmovzx edx, dx\r\n\tmov eax, edx\r\n\tshr eax, 10\t\t;0-FFFF -> 0-3F\r\n\tbt dword ptr [IO_Handler_Pages], eax\r\n\tjc @@iscommitted\r\n\tpush edx\r\n\tpush PC_FIXED or PC_WRITEABLE\r\n\tpush 0\r\n\tpush PD_ZEROINIT\r\n\tpush 1\r\n\tmov ecx, [IO_Handler_Region]\r\n\tshr ecx, 12\r\n\tadd eax, ecx\r\n\tpush eax\r\n\tcall _PageCommit\r\n\tadd esp,5*4\r\n\tpop edx\r\n\tand eax, eax\r\n\tjz @@error\r\n\tmov eax, edx\r\n\tshr eax, 10\r\n\tbts dword ptr [IO_Handler_Pages], eax\r\n@@iscommitted:\r\n\tclc\r\n\tret\r\n@@error:\r\n\tstc\r\n\tret\r\nCommitTrapPage endp\r\n\r\n;--- ESI=callback\r\n;--- DX=port\r\n\r\nInstall_IO_Handler proc\r\n\tcld\r\n\tcmp [IO_Handler_Region],0\r\n\tjnz @@isallocated\r\n\t@dprintf ?IODBG, <\"Install_IO_Handler: reserve 256 kB region\",10>\r\n\tpush edx\r\n\tpush 0\r\n\tpush 64\r\n\tpush PR_SYSTEM\r\n\tcall _PageReserve\t; reserve 64*4=256 kB [ 65536 ports, one dword per port ]\r\n\tadd esp,3*4\r\n\tpop edx\r\n\tcmp eax,-1\r\n\tjz @@error\r\n\tmov [IO_Handler_Region], eax\r\n\t@dprintf ?IODBG, <\"Install_IO_Handler: region=%X\",10>, eax\r\n\tpush edx\r\n\tcall ImportJemmIOTraps\r\n\tpop edx\r\n@@isallocated:\r\n\t@dprintf ?IODBG, <\"Install_IO_Handler: port=%X\",10>, dx\r\n\tcall CommitTrapPage\r\n\tjc @@error\r\n\tmovzx eax, dx\r\n\tshl eax, 2\r\n\tadd eax, [IO_Handler_Region]\r\n\tcmp dword ptr [eax], 0\t\t; port already trapped?\r\n\tjnz @@error\r\n\tmov [eax], esi\r\n\tmov eax, [dwTSSbase]\r\n\tmovzx ecx, [eax].TSSSEG.tsOfs\r\n\tadd eax, ecx\r\n\tbts [eax],edx\t\t\t\t; mark port as \"trapped\" in IOPB\r\n\tclc\r\n\tret\r\n@@error:\r\n\t@dprintf ?IODBG, <\"Install_IO_Handler: trap port=%X failed\",10>, dx\r\n\tstc\r\n\tret\r\n\talign 4\r\nInstall_IO_Handler endp\r\n\r\n;--- when untrapping a port, check if it's a port trapped by Jemm\r\n;--- if yes, it must not be reset in the IOPB.\r\n;--- in: edx=port\r\n;--- out: C if port is trapped by Jemm\r\n;--- register edx must be preserved\r\n\r\nDefaultTrappedPort proc uses esi ebx\r\n\tmov esi, [IO_Trap_Table]\r\n\tadd esi, 4\r\n\tlodsd\r\n\tmov ecx, eax\r\n\tjecxz done\r\nnextrange:\r\n\tlodsb\t\t\t; get bStart field\r\n\tmovzx ebx, al\r\n\tlodsb\t\t\t; get bEnd field\r\n\tmovzx eax, al\r\n\tadd esi, 4\t\t; skip proc field\r\n\tcmp edx, ebx\r\n\tjb @F\r\n\tcmp edx, eax\r\n\tjbe found\r\n@@:\r\n\tloop nextrange\r\ndone:\r\n\tclc\r\n\tret\r\nfound:\r\n\tstc\r\n\tret\r\nDefaultTrappedPort endp\r\n\r\n;--- untrap port, dx=port\r\n\r\nRemove_IO_Handler proc\r\n\t@dprintf ?IODBG, <\"Remove_IO_Handler: untrap port=%X\",10>, dx\r\n\txor ecx, ecx\r\n\tcmp [IO_Handler_Region],ecx\r\n\tjz @@error\r\n\tmovzx eax, dx\r\n\tshr eax, 10\t\t;0-FFFF -> 0-3F\r\n\tbt dword ptr [IO_Handler_Pages], eax\r\n\tjnc @@error\r\n\tmovzx eax, dx\r\n\tshl eax, 2\r\n\tadd eax, [IO_Handler_Region]\r\n\tcmp [eax],ecx\r\n\tjz @@error\r\n\tmov [eax], ecx\r\n\tcall DefaultTrappedPort\r\n\tjc @F\r\n\tmov eax, [dwTSSbase]\r\n\tmovzx ecx, [eax].TSSSEG.tsOfs\r\n\tadd eax, ecx\r\n\tbtc [eax],edx\r\n@@:\r\n\tclc\r\n\tret\r\n@@error:\r\n\t@dprintf ?IODBG, <\"Remove_IO_Handler: untrap port=%X failed\",10>, dx\r\n\tstc\r\n\tret\r\n\talign 4\r\nRemove_IO_Handler endp\r\n\r\n;--- not implemented\r\n;--- API?\r\n\r\nInstall_Mult_IO_Handlers proc\r\n\tstc\r\n\tret\r\n\talign 4\r\nInstall_Mult_IO_Handlers endp\r\n\r\n;--- not implemented\r\n;--- API?\r\n\r\nRemove_Mult_IO_Handlers proc\r\n\tstc\r\n\tret\r\n\talign 4\r\nRemove_Mult_IO_Handlers endp\r\n\r\n;--- interrupt hooking\r\n\r\n;--- get vector EAX in CX:EDX\r\n\r\nGet_V86_Int_Vector proc\r\n\tcmp eax,256\r\n\tjae @@error\r\n\tmovzx edx,word ptr [eax*4+0]\r\n\tmov cx,word ptr [eax*4+2]\r\n\tclc\r\n\tret\r\n@@error:\r\n\tstc\r\n\tret\r\n\talign 4\r\nGet_V86_Int_Vector endp\r\n\r\n;--- set vector EAX in CX:EDX\r\n\r\nSet_V86_Int_Vector proc\r\n\tcmp eax,256\r\n\tjae @@error\r\n\tmov word ptr [eax*4+0],dx\r\n\tmov word ptr [eax*4+2],cx\r\n\tclc\r\n\tret\r\n@@error:\r\n\tstc\r\n\tret\r\n\talign 4\r\nSet_V86_Int_Vector endp\r\n\r\n;--- the Get_PM/Set_PM API is supposed to set the vectors of the current VM\r\n;--- if it's in protected-mode. So not really needed by Jemm.\r\n\r\n;--- in: eax=int#\r\n;--- out: cx=segment\r\n;---     edx=offset\r\n\r\nGet_PM_Int_Vector proc\r\n\tstc\r\n\tret\r\n\talign 4\r\nGet_PM_Int_Vector endp\r\n\r\n;--- in: eax=int#\r\n;---      cx=segment\r\n;---     edx=offset\r\n\r\nSet_PM_Int_Vector proc\r\n\tstc\r\n\tret\r\n\talign 4\r\nSet_PM_Int_Vector endp\r\n\r\n;--- hook v86 interrupt chain\r\n;--- eax = int#\r\n;--- esi = hook proc\r\n\r\n;--- v5.85:\r\n;---  IDT is no longer touched\r\n;---  instead the new ppV86Hooks variable is used to set V86Hooks\r\n;---  this makes the thunk code generation obsolete.\r\n;--- sizeof HOOKSTR is now 4 (256*4=1024), the following 32 bytes are used to store\r\n;--- the VME bitmap.\r\n\r\n\r\nHook_V86_Int_Chain proc\r\n\tcmp eax,100h\r\n\tjnc @@error\r\n\r\n;--- the \"hook\" page already allocated?\r\n\r\n\tcmp [lpV86Hooks],0\r\n\tjnz @@isallocated\r\n\tpush eax\r\n\tpush 0\r\n\tpush 1\r\n\tpush PR_SYSTEM\r\n\tcall _PageReserve\r\n\tadd esp,3*4\r\n\tcmp eax, -1\r\n\tjz @@error2\r\n\tpush eax\r\n\tpush PC_FIXED or PC_WRITEABLE\r\n\tpush 0\r\n\tpush PD_ZEROINIT\r\n\tpush 1\r\n\tshr eax, 12\r\n\tpush eax\r\n\tcall _PageCommit\r\n\tadd esp,5*4\r\n\tpop ecx\r\n\tand eax, eax\r\n\tjz @@error2\r\n\tmov [lpV86Hooks], ecx\r\n\r\n;--- v5.85: set the pV86Hooks variable in Jemm32\r\n\tmov eax, [ppV86Hooks]\r\n\tmov [eax+0], ecx\r\n\tmov [eax+4], offset CallV86Hooks\r\n\r\n\tpop eax\r\n@@isallocated:\r\n\tmov edx,[lpV86Hooks]\r\n\tcmp [edx+eax*4].HOOKV86.dwHook,0\t; 4=sizeof HOOKV86 struct\r\n\tjz @@newhook\r\n\r\n;--- chain with old hook proc\r\n\r\n\tmov ecx,[edx+eax*4].HOOKV86.dwHook\r\n\tmov [edx+eax*4].HOOKV86.dwHook, esi\r\n\tmov eax, [esi-4]\r\n\tmov [eax], ecx\r\n\tjmp @@done\r\n@@error2:\r\n\tpop eax\r\n@@error:\r\n\t@dprintf ?V86HOOKDBG, <\"Hook_V86_Int_chain: exit error\",10>\r\n\tstc\r\n\tret\r\n@@newhook:\r\n\t@dprintf ?V86HOOKDBG, <\"Hook_V86_Int_chain: new hook, eax=%X\",10>, eax\r\n\tmov [edx+eax*4].HOOKV86.dwHook, esi\r\n\r\n;--- set the bit in TSS interrupt redirection bitmap for VME.\r\n;--- store the old state in [edx+1024] (behind the HOOKSTR items).\r\n\r\n;--- For hardware interrupts, this isn't needed, but shouldn't harm...\r\nif 0\r\n\tcmp al, 8\r\n\tjb @F\r\n\tcmp al, 10h\r\n\tjb @@done\r\n\tcmp al, 70h\r\n\tjb @F\r\n\tcmp al, 78h\r\n\tjb @@done\r\n@@:\r\nendif\r\n\r\n\tpush ebx\r\n\tpush esi\r\nif 0\r\n\tmov ecx, [esi-4]\r\n\tmov dword ptr [ecx], 0\r\nendif\r\n\r\n\t@dprintf ?V86HOOKDBG, <\"Hook_V86_Int_chain: modifying Int bitmap, eax=%X\",10>, eax\r\n\tmov ebx, [dwTSSbase]\r\n\tmovzx ecx, [ebx].TSSSEG.tsOfs\r\n\tadd ebx, ecx\r\n\tsub ebx, 32    ;=256/8\r\n\tbts [ebx], eax\r\n\tjc @F\r\n\tbtr [edx+1024], eax\r\n\tjmp bitset\r\n@@:\r\n\tbts [edx+1024], eax\r\nbitset:\r\n\tpop esi\r\n\tpop ebx\r\n@@done:\r\n\t@dprintf ?V86HOOKDBG, <\"Hook_V86_Int_chain: exit ok\",10>\r\n\tclc\r\n\tret\r\n\talign 4\r\nHook_V86_Int_Chain endp\r\n\r\n;--- eax = int#\r\n;--- esi = hook proc\r\n\r\nUnhook_V86_Int_Chain proc\r\n\t@dprintf ?V86HOOKDBG, <\"Unhook_V86_Int_Chain: int=%X\",10>, eax\r\n\tcmp eax,100h\r\n\tjnc @@error\r\n\tmov ecx,[lpV86Hooks]\r\n\tjecxz @@error\r\n\tmov edx,[ecx+eax*4].HOOKV86.dwHook\r\n\tand edx,edx\r\n\tjz @@error\r\n\txor ecx,ecx\r\n@@nextitem:\r\n\tcmp edx,esi\r\n\tjz @@found\r\n\tmov ecx,[edx-4]\r\n\tmov edx,[ecx]\r\n\tand edx,edx\r\n\tjnz @@nextitem\r\n@@error:\r\n\tstc\r\n\tret\r\n@@found:\r\n\tmov edx,[edx-4]\r\n\tmov edx,[edx]\r\n\tand ecx,ecx\r\n\tjnz @@notfirst\r\n\tmov ecx,[lpV86Hooks]\r\n\tand edx, edx\r\n\tjz @@islast\r\n\tlea ecx,[ecx+eax*4]\r\n@@notfirst:\r\n\tmov [ecx],edx\r\n\tret\r\n@@islast:\r\n\tmov [ecx+eax*4].HOOKV86.dwHook,edx\r\n\tbt [ecx+1024], eax\r\n\tjc @@nobitres\r\n\tpush ebx\r\n\tmov ebx, [dwTSSbase]\r\n\tmovzx edx, [ebx].TSSSEG.tsOfs\r\n\tadd ebx, edx\r\n\tsub ebx, 32    \r\n\tbtr [ebx],eax\r\n\tpop ebx\r\n@@nobitres:\r\n\tclc\r\n\tret\r\n\talign 4\r\nUnhook_V86_Int_Chain endp\r\n\r\n;--- call hooks for an int\r\n;--- must have been ensured that there are hooks!\r\n;--- a hook proc is called with:\r\n;--- eax = int#\r\n;--- ebp = client\r\n;--- ecx = hook proc\r\n;--- out: NC if interrupt has been serviced.\r\n\r\nCallV86Hooks proc\r\n@@nexthook:\r\n\tpush ecx\r\n\tpush eax\r\n\tcall ecx\r\n\tpop eax\r\n\tpop ecx\r\n\tjnc @@done\r\n\tmov edx,[ecx-4]\r\n\tmov ecx,[edx]\r\n\tand ecx, ecx\r\n\tjnz @@nexthook\r\nifdef _DEBUG\r\n\tcmp eax, 2Fh\r\n\tjz @F\r\n\t@dprintf ?V86HOOKDBG, <\"CallV86Hooks: no hook proc handled int %X\",10>, eax\r\n@@:\r\nendif\r\n\tstc\r\n@@done:\r\n\tret\r\n\talign 4\r\nCallV86Hooks endp\r\n\r\n;--- add a v86 fault hook\r\n;--- eax=fault#\r\n;--- esi=fault proc\r\n;--- out: C if not installed\r\n;---  NC if installed, then ESI=previous handler (win31 compat)\r\n\r\nHook_V86_Fault proc\r\n\t@dprintf ?V86HOOKDBG, <\"Hook_V86_Fault: int=%X\",10>, eax\r\n\tcmp eax,32\r\n\tcmc\r\n\tjb exit\r\n\tmov edx,[esi-4]\r\n\txchg esi,[eax*4+v86faulthooks]\r\n\tmov [edx],esi\r\n\tcmp esi,0\r\n\tjnz exit\r\n\tmov eax,[lpV86Faults]\r\n\tmov [edx],eax\r\nexit:    \r\n\tret\r\nHook_V86_Fault endp\r\n\r\n;--- remove a v86 fault hook\r\n;--- eax=fault#\r\n;--- esi=fault proc\r\n;--- out: C if not unhooked (invalid fault in eax, esi not a hook proc, hook proc not found,...)\r\n\r\nUnhook_V86_Fault proc\r\n\t@dprintf ?V86HOOKDBG, <\"Unhook_V86_Fault: int=%X\",10>, eax\r\n\tcmp eax,32\r\n\tcmc\r\n\tjb exit\r\n\txor ecx, ecx\r\n\tmov edx, [eax*4+v86faulthooks]\r\n\t.while edx\r\n\t\t.if edx == esi\r\n\t\t\tmov edx,[esi-4]\r\n\t\t\tmov edx,[edx]\r\n\t\t\t.if !ecx\r\n\t\t\t\tmov [eax*4+v86faulthooks], edx\r\n\t\t\t.else\r\n\t\t\t\tmov eax,[ecx-4]\r\n\t\t\t\tmov [eax], edx\r\n\t\t\t.endif\r\n\t\t\t.break\r\n\t\t.endif\r\n\t\tmov ecx, edx\r\n\t\tmov edx, [edx-4]\r\n\t\tmov edx, [edx]\r\n\t.endw\r\n\tcmp edx,1\r\nexit:\r\n\tret\r\nUnhook_V86_Fault endp\r\n\r\n;--- call v86 fault hookers\r\n;--- in: ebp=client_reg_struct\r\n;---    (ebx=vm handle in windows)\r\n;---     eax=fault#\r\n;--- the fault handler may either\r\n;--- + pass the fault to the previous handler (all regs preserved then) OR\r\n;--- + do a RET (all GPRs excepts EBP/ESP may be modified)\r\n\r\nV86Faults proc\r\n\tcmp [eax*4+v86faulthooks],0\r\n\tjz @F\r\n\tjmp [eax*4+v86faulthooks]\r\n@@:\r\n\tjmp [lpV86Faults]\r\nV86Faults endp\r\n\r\n;--- the \"PM\" functions are pretty useless, since Jemm has no support\r\n;--- for protected-mode apps.\r\n\r\nHook_PM_Fault proc\r\nHook_PM_Fault endp\r\nUnhook_PM_Fault proc\r\nUnhook_PM_Fault endp\r\n\r\n\tstc\r\n\tret\r\n\r\n\talign 4\r\n\r\n;--- _PhysIntoV86(physpg, VM, VMLinPgNum, nPages, flags)\r\n;--- map n physical pages into V86\r\n\r\nphyspg     equ <esp+1*4>\r\nVM         equ <esp+2*4>\r\nVMLinPgNum equ <esp+3*4>\r\nnPages     equ <esp+4*4>\r\nflags      equ <esp+5*4>\r\n\r\n_PhysIntoV86 proc\r\n\tmov ecx, [nPages]\r\n\tpush esi\r\n\tpush edi\r\n\tmov esi, 8[physpg]\r\n\tmov edi, 8[VMLinPgNum]\r\n\tmov eax, esi\r\n\tmov edx, edi\r\n\tcmp eax, 100h\r\n\tjbe @@done\r\n\tlea edi, [edi*4 + (?PAGEMAP shl 22)]\r\n\tcld\r\n\tshl esi,12\r\n@@nextitem:\r\n\tmov eax, esi\r\n\tor eax, PTF_PRESENT or PTF_RW or PTF_USER\r\n\tstosd\r\n\tadd esi,1000h\r\n\tloop @@nextitem\r\n\tmov eax,cr3\r\n\tmov cr3,eax\r\n@@done:\r\n\tpop edi\r\n\tpop esi\r\n\tret\r\n\talign 4\r\n_PhysIntoV86 endp\r\n\r\n;--- _LinMapIntoV86(LinPgNum, VM, VMLinPgNum, nPages, flags)\r\n;--- map n linear pages into V86\r\n\r\nLinPgNum  equ <esp+1*4>\r\nVM        equ <esp+2*4>\r\nVMLinPgNum equ <esp+3*4>\r\nnPages     equ <esp+4*4>\r\nflags      equ <esp+5*4>\r\n\r\n_LinMapIntoV86 proc\r\n\tmov ecx, [nPages]\r\n\tpush esi\r\n\tpush edi\r\n\tmov esi, 8[LinPgNum]\r\n\tmov edi, 8[VMLinPgNum]\r\n\tmov eax, esi\r\n\tmov edx, edi\r\n\tcmp eax, 100h\r\n\tjbe @@done\r\n\tlea esi, [esi*4 + (?PAGEMAP shl 22)]\r\n\tlea edi, [edi*4 + (?PAGEMAP shl 22)]\r\n\tcld\r\n\trep movsd\r\n\tmov eax,cr3\r\n\tmov cr3,eax\r\n@@done:\r\n\tpop edi\r\n\tpop esi\r\n\tret\r\n\talign 4\r\n_LinMapIntoV86 endp\r\n\r\n;--- page memory\r\n\r\n;--- bits in PTE:\r\n;--- 200h = begin of a memory object\r\n;--- 400h = PTE is a mapped phys page (don't free it!)\r\n;--- 800h = not used\r\n\r\nGetFreePages proc\r\n\tpush edi\r\n\tpush esi\r\n\tpush ecx\r\n\tmov eax, [VCPI_Call_Table]\r\n\tcall dword ptr [eax+3*4]\t;VCPI_GetFreePages\r\n\tpop ecx\r\n\tpop esi\r\n\tpop edi\r\n\tret\r\n\talign 4\r\nGetFreePages endp\r\n\r\nAllocPage proc\r\n\tpush edi\r\n\tpush esi\r\n\tpush ecx\r\n\tmov eax, [VCPI_Call_Table]\r\n\tcall dword ptr [eax+4*4]\r\n\tpop ecx\r\n\tpop esi\r\n\tpop edi\r\n\tret\r\n\talign 4\r\nAllocPage endp\r\n\r\nFreePage proc\r\n\tpush edi\r\n\tpush esi\r\n\tpush ecx\r\n\tmov eax, [VCPI_Call_Table]\r\n\tcall dword ptr [eax+5*4]\r\n\tpop ecx\r\n\tpop esi\r\n\tpop edi\r\n\tret\r\n\talign 4\r\nFreePage endp\r\n\r\n;--- esi=V86TOPM struct\r\n;--- ebp=client reg struc\r\n;--- both structures have to be copied into shared space\r\n\r\nV86ToPM proc public\r\n\tmov edi, ?BASE+10h\r\n\tmov ecx, 6\t; size of VCPI_V86toPM struct\r\n\tcld\r\n\trep movsd\r\n\tmov esi, ebp\r\n\tmov cl, sizeof Client_Reg_Struc shr 2\r\n\trep movsd\r\n\tmov esi, ?BASE+10h\r\n\tlea edi, [esi+6*4]\r\n\tmov eax, [VCPI_Call_Table]\r\n\tjmp dword ptr [eax+12*4]\t; VCPI_V86ToPM\r\n\talign 4\r\nV86ToPM endp\r\n\r\n;--- to simplify memory management,\r\n;--- a \"mapping\" page table is maintained which\r\n;--- maps all page tables in linear address space\r\n;--- at address ?PAGEMAP << 22 (currently 3FEh << 22 = FF800000h)\r\n\r\nInitMapPage proc\r\n\tpushad\r\n\tcall AllocPage\r\n\tcmp ah,0\r\n\tjnz @@error\r\n\tor edx,PTF_PRESENT or PTF_RW\t\t;set P, R/W\r\n\tmov ds:[?PAGEDIR+?PAGEMAP*4],edx\t;set the PDE\r\n\tmov ds:[?SYSBASE+?PAGETABSYS+?SYSTABSCRATCHIDX*4],edx  ;map the table in scratch region\r\n\r\n\tcall AllocPage\t\t\t;alloc a second page (scratch PTE)\r\n\tcmp ah,0\r\n\tjnz @@error\r\n\tor edx,PTF_PRESENT or PTF_RW\r\n\tmov [ScratchPTE],edx\r\n\r\n\tmov eax, cr3\t\t\t;flush TLB\r\n\tmov cr3, eax\r\n\r\n\tmov eax, edx\t\t\t;fill mapping table PDE with scratch PTE\r\n\tmov edi, ?SYSBASE + 3FE000h\r\n\tcld\r\n\tmov ecx, 1000h/4\r\n\tpush edi\r\n\trep stosd\r\n\tpop edi\r\n\r\n\tmov eax,ds:[?PAGEDIR+0] \t\t\t;copy PDE page table 0\r\n\tmov [edi+0],eax\r\n\tmov eax,ds:[?PAGEDIR+(?SYSBASE shr 20)]\t\t;copy PDE \"jemm\"\r\n\tmov [edi+(?SYSBASE shr 20)],eax\r\n\tmov eax,ds:[?PAGEDIR+(?SYSTEMADDR shr 20)]\t;copy PDE \"jload\"\r\n\tmov [edi+(?SYSTEMADDR shr 20)],eax\r\n\tmov eax,ds:[?PAGEDIR+?PAGEMAP*4]\t;copy PDE \"pagemap\"\r\n\tmov [edi+?PAGEMAP*4],eax\r\n\t\r\n;--- now map scratch PTE into scratch region and clear it\r\n\t\r\n\tmov dword ptr ds:[?SYSBASE+?PAGETABSYS+?SYSTABSCRATCHIDX*4], edx\r\n\r\n\tmov eax, cr3\r\n\tmov cr3, eax\r\n\r\n\tmov ecx, 1000h/4\r\n\txor eax, eax\r\n\trep stosd\r\n\tmov dword ptr ds:[?SYSBASE+?PAGETABSYS+?SYSTABSCRATCHIDX*4], eax\r\n\r\n\tpopad\r\n\tclc\r\n\tret\r\n@@error:\r\n\tpopad\r\n\txor eax, eax\r\n\tstc\r\n\tret\r\n\talign 4\r\nInitMapPage endp\r\n\r\n;--- _PageFree(hMem, flags)\r\n;--- this function has no size parameter\r\n;--- _page: linear address of block\r\n;--- flags: 0 or PR_STATIC\r\n\r\n_page  equ <esp+1*4+4>\r\nflags  equ <esp+2*4+4>\r\n\r\n_PageFree proc public\r\n\r\n\tpush esi\r\n\tmov esi, [_page]\r\n\tshr esi, 12\r\n\tlea esi, [esi*4 + ?PAGEMAP shl 22]\r\n\tlodsd\r\n\ttest ah,2\t;begin of a block?\r\n\tjz\t@@error\r\n@@nextitem:\r\n\ttest al,1\t;committed page?\t\r\n\tjz @F\r\n\ttest ah,4\t;mapped page?\r\n\tjnz @F\r\n\tmov edx, eax\r\n\tand dx,0F000h\r\n\tcall FreePage\r\n@@:\r\n\tmov dword ptr [esi-4],0\r\n;\tcmp esi, (?PAGEMAP + 400000h) shl 22\t;end of page map?\r\n\tcmp esi, (?PAGEMAP shl 22 ) + 400000h\t;end of page map?\r\n\tjz @@done\r\n\tlodsd\r\n\tand eax, eax\r\n\tjz @@done\r\n\ttest ah,2\r\n\tjz @@nextitem\r\n@@done:\r\n\tmov eax,cr3\r\n\tmov cr3,eax\r\n\tpush 1\r\n\tpop eax\r\n\tpop esi\r\n\tret\r\n@@error:\r\n\tpop esi\r\n\txor eax,eax\r\n\tret\r\n\talign 4\r\n_PageFree endp\r\n\r\n;--- _PageReserve(page, npages, flags)\r\n;--- page = specific page or \r\n;--- PR_PRIVATE (80000400h), PR_SHARED (80060000h), PR_SYSTEM (80080000h)\r\n;--- flags: ???\r\n;--- out: linear address in EAX or -1\r\n;--- \"private\" starts at 400000h\r\n;--- \"shared\" possibly at F8000000h downwards? (already used by 4MB page heap!)\r\n;--- system at ?SYSTEMADDR (usually F8400000h)\r\n;--- the first PTE will have the \r\n\r\n_page  equ <esp+1*4>\r\nnpages equ <esp+2*4>\r\nflags  equ <esp+3*4>\r\n\r\n_PageReserve proc public\r\n\tcmp dword ptr [npages],0\r\n\tjz @@error\r\n\tmov eax, ?SYSTEMADDR shr 10\r\n\tmov ecx, 400000h/4 - (?SYSTEMADDR shr 12)\r\n\tcmp dword ptr [_page],PR_SYSTEM\r\n\tjz @F\r\n\tcmp dword ptr [_page],PR_PRIVATE   ;currently only unspecified regions are supported\r\n\tjnz @@error\r\n\tmov eax, ?PRIVATEADDR shr 10\r\n\tmov ecx, (?SYSTEMADDR - ?PRIVATEADDR) shr 12\r\n@@:\r\n\tmov edx, [npages]\r\n\tdec edx\r\n\tpush edi\r\n\tpush ebx\r\n\tlea edi, [eax + (?PAGEMAP shl 22)]\r\n\tcld\r\n\r\n;--- the mapping region at FF800000-FFBFFFFF contains no\r\n;--- invalid addresses. A 00000000 entry means it is either an\r\n;--- unused PTE of an partially used PT, or it is a still unused PT.\r\n;--- so all what has to be done is scanning the region for a sequence\r\n;--- of 00000000 dwords.\r\n\r\n@@continuescan:    \r\n\txor eax, eax\r\n\trepnz scasd\r\n\tjnz @@error2\r\n\tcmp ecx, edx\t;enough potential entries left?\r\n\tjc @@error2\r\n\tlea ebx, [edi-4]\r\n\tand edx, edx\t;size just one page?\r\n\tjz @@found\r\n\tpush ecx\r\n\tmov ecx, edx\r\n\trepz scasd\r\n\tpop ecx\r\n\tjz @@found\r\n\tmov eax, edx\r\n\tsub eax, ecx\r\n\tsub ecx, eax\r\n\tjmp @@continuescan\r\n@@found:\r\n\r\n;--- now backup the PDEs and PTEs with true RAM\r\n\r\n\tmov ecx, edx\r\n\tinc ecx\r\n\tmov edi, ebx\r\n\t.while (ecx)\r\n\t\tmov eax, edi\r\n\t\tsub eax, ?PAGEMAP shl 22\r\n\t\tshr eax, 10\r\n\t\tcmp dword ptr ds:[?PAGEDIR + EAX],0\t;does a PDE exist?\r\n\t\tjnz @F\r\n\t\tpush eax\r\n\t\tcall AllocPage\r\n\t\tcmp ah,0\r\n\t\tpop eax\r\n\t\tjnz @@error\r\n\t\tor edx,PTF_PRESENT or PTF_RW or PTF_USER\r\n\t\tmov ds:[?PAGEDIR + EAX],edx\r\n\t\tand dl, not PTF_USER\r\n\t\tmov ds:[(?PAGEMAP shl 22) + (?PAGEMAP shl 12) + EAX],edx\r\n\t\tpush edi\r\n\t\tpush ecx\r\n\t\tmov edi, eax\r\n\t\tshl edi, 10\r\n\t\tadd edi, ?PAGEMAP shl 22\r\n\t\tmov eax, cr3\r\n\t\tmov cr3, eax\r\n\t\txor eax, eax\r\n\t\tmov ecx, 1000h/4\r\n\t\trep stosd\r\n\t\tpop ecx\r\n\t\tpop edi\r\n@@:\r\n\t\tmov eax, PTF_RW or PTF_USER\t;set USER + R/W + NP\r\n\t\tcmp edi,ebx\t\t;first PTE?\r\n\t\tsetz ah\r\n\t\tshl ah,1\t\t;set first \"available\" bit in PTE\r\n\t\tstosd\r\n\t\tdec ecx\r\n\t.endw\r\n\tmov eax, ebx\r\n\tsub eax, ?PAGEMAP shl 22\r\n\tshl eax, 10\r\n\tpop ebx\r\n\tpop edi\r\n\tret\r\n@@error2:\r\n\tpop ebx\r\n\tpop edi\r\n@@error:\r\n\tor eax,-1\r\n\tret\r\n\talign 4\r\n_PageReserve endp\r\n\r\n;--- _PageCommit(page, npages, hpd, pagerdata, flags)\r\n;--- returns eax != 0 if ok, eax == 0 on failure\r\n;--- page: linear page number\r\n;--- hpd: PD_FIXED | PD_FIXEDZERO\r\n;--- pagerdata: must be ZERO\r\n;--- flags: PC_FIXED, PC_LOCKED, PC_USER, PC_WRITEABLE\r\n;--- if PC_FIXED or PC_LOCKED is set, none of the pages must be committed\r\n\r\n_page      equ <esp+1*4+8>\r\nnpages     equ <esp+2*4+8>\r\nhpd        equ <esp+3*4+8>\r\npagerdata  equ <esp+4*4+8>\r\nflags      equ <esp+5*4+8>\r\n\r\n_PageCommit proc public\r\n\tpush esi\r\n\tpush ebx\r\n\tmov esi, [_page]\r\n\tshl esi, 2\r\n\tadd esi, ?PAGEMAP shl 22\r\n\tmov ebx, [flags]\r\n\tmov ecx, [npages]\r\n\txor edx, edx\r\n\tcld\r\n\tand ecx, ecx\r\n\tjz @@error\r\n@@nextitem:\r\n\tlodsd\r\n\tand al,al\t\t\t;valid address space?\r\n\tjz @@error\r\n\ttest al,P_PRES\t\t;page committed?\r\n\tsetz al\r\n\ttest bl,PC_FIXED or PC_LOCKED\r\n\tjz @F\r\n\tcmp al,0\t\t\t;no committed page allowed\r\n\tjz @@error\r\n@@:\r\n\tmovzx eax,al\r\n\tadd edx, eax\r\n\tloop @@nextitem\r\n\t\r\n\tpush edx\r\n\tcall GetFreePages\r\n\tcmp ah,0\r\n\tpop eax\r\n\tjnz @@error\r\n\tcmp edx, eax\r\n\tjc\t@@error \t\t\t\t; not enough free pages\r\n\r\n\tmov ecx, [npages]\r\n\tmov esi, [_page]\r\n\tshl esi, 2\r\n\tadd esi, ?PAGEMAP shl 22\r\n\tmov ebx, [flags]\r\n\tshr ebx, 16 \t\t\t\t; move PC_WRITEABLE and PC_USER to BL\r\n\tor bl,P_PRES\t\t\t\t; also set the PRESENT bit\r\n@@nextitem2:\r\n\tlodsd\r\n\ttest al,P_PRES\r\n\tjnz @F\r\n\tcall AllocPage\r\n\tcmp ah,0\r\n\tjnz @@error\r\n\tmov eax,[esi-4]\r\n\tmov al,0\r\n\tor al, bl\r\n\tor eax, edx\r\n\tmov [esi-4], eax\r\n\ttest byte ptr [hpd],1\t;zero init?\r\n\tjz @F\r\n\tpush ecx\r\n\tpush edi\r\n\tmov edi, esi\r\n\tsub edi, 4 + (?PAGEMAP shl 22)\r\n\tshl edi, 10\r\n\tmov ecx,1000h/4\r\n\txor eax, eax\r\n\trep stosd\r\n\tpop edi\r\n\tpop ecx\r\n@@:\r\n\tloop @@nextitem2\r\n\tpop ebx\r\n\tpop esi\r\n\tpush 1\r\n\tpop eax\r\n\tret\r\n@@error:\r\n\tpop ebx\r\n\tpop esi\r\n\txor eax, eax\r\n\tret\r\n\talign 4\r\n_PageCommit endp\r\n\r\n;--- _PageDecommit(page, npages, flags)\r\n;--- page: linear page number\r\n;--- flags: must be zero\r\n;--- returns: eax == 0 on failure\r\n\r\n_page      equ <esp+1*4>\r\nnpages     equ <esp+2*4>\r\nflags      equ <esp+3*4>\r\n\r\n_PageDecommit proc public\r\n\r\n\tmov ecx, [npages]\r\n\tjecxz @@error\r\n\tpush esi\r\n\tmov esi, 4[_page]\r\n\tshl esi, 2\r\n\tadd esi, ?PAGEMAP shl 22\r\n@@nextitem:\r\n\tlodsd\r\n\tmov edx, eax\r\n\ttest al,P_PRES\t\t;committed page?\r\n\tjz @@notcommitted\r\n\ttest ah,4\t\t\t;mapped page?\r\n\tjnz @F\r\n\tmov dl,0\r\n\tand dh,0F0h\r\n\tpush eax\r\n\tcall FreePage\r\n\tpop edx\r\n@@:\r\n\tand edx, 0200h\t\t;preserve the \"begin block\" bit\r\n\tor dl,P_WRITE or P_USER\r\n\tmov [esi-4],edx\r\n@@notcommitted:\r\n\tloop @@nextitem\r\n\tpop esi\r\n\tmov ecx,cr3\r\n\tmov cr3,ecx\r\n\tret\r\n@@error:\r\n\txor eax, eax\r\n\tret\r\n\talign 4\r\n_PageDecommit endp\r\n\r\n;--- _PageCommitPhys(page, npages, physpg, flags)\r\n;--- page: linear page number\r\n;--- npages:# of pages (region must NOT be committed)\r\n;--- physpg: physical page # to commit.\r\n;--- flags: use PC_INCR to map a contiguous phys region;\r\n;--- PC_USER and PC_WRITEABLE also relevant;\r\n;--- v5.83: added PC_CACHEWT and PC_CACHEDIS;\r\n;--- returns: eax == 0 on failure\r\n\r\n_page      equ <esp+1*4>\r\nnpages     equ <esp+2*4>\r\nphyspg     equ <esp+3*4>\r\nflags      equ <esp+4*4>\r\n\r\n_PageCommitPhys proc\r\n\r\n\tmov ecx, [npages]\r\n\tjecxz @@error\r\n\tpush esi\r\n\tmov esi, 4[_page]\r\n\tshl esi, 2\r\n\tadd esi, ?PAGEMAP shl 22\r\n\tmov edx, esi\r\n\tcld\r\n@@nextitem:\r\n\tlodsd\r\n\tcmp al,0\t\t\t;valid PTE?\r\n\tjz @@error2\r\n\ttest al,P_PRES\t\t;all PTEs must be non-committed!\r\n\tjnz @@error2\r\n\tloop @@nextitem\r\n\tpush edi\r\n\tpush ebx\r\n\tmov esi, edx\r\n\tmov ecx, 12[npages]\r\n\tmov ebx, 12[physpg]\r\n\tmov edi, 12[flags]\r\n\txor edx, edx\r\n\ttest edi,PC_INCR\r\n\tjz @F\r\n\tmov dh,10h\r\n@@:\r\n\tshr edi, 16\r\n\tand edi, (PC_WRITEABLE or PC_USER or PC_CACHEWT or PC_CACHEDIS) shr 16\r\n\tor edi, 401h\t\t;mark this PTE as a \"mapped\" page (400h)\r\n\tshl ebx, 12\r\n@@nextitem2:\r\n\tlodsd\r\n\tand ah,0Fh\r\n\tmovzx eax,ax\r\n\tor eax, edi\r\n\tor eax, ebx\r\n\tmov [esi-4], eax\r\n\tadd ebx, edx\r\n\tloop @@nextitem2\r\n\tpop ebx\r\n\tpop edi\r\n\tpop esi\r\n\tret\r\n@@error2:\r\n\tpop esi\r\n@@error:\r\n\txor eax, eax\r\n\tret\r\n\talign 4\r\n_PageCommitPhys endp\r\n\r\n;--- notifications from Jemm (SYSTEM_EXIT)\r\n;--- eax, ebx, esi, edi, ebp must not be changed.\r\n\r\ncall_control_procs proc\r\n\tmov edx,offset ddb_list\r\nnextdevice:\r\n\tmov ecx, [edx].VxD_Desc_Block.DDB_Control_Proc\r\n\tjecxz @F\r\n\tpushad\r\n\tcall ecx\r\n\tpopad\r\n@@:\r\n\tmov edx,[edx].VxD_Desc_Block.DDB_Next\r\n\tand edx, edx\r\n\tjnz nextdevice\r\n\tret\r\ncall_control_procs endp\r\n\r\n;--- in: eax->ID to find, if 000, edi=name\r\n;--- out: eax = hModule\r\n\r\nFindDevice proc public\r\n\tmov edx,offset ddb_list\r\n@@:\r\n\tcmp ax, 0\r\n\tjz cmpname\r\n\tcmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number\r\n\tjz found\r\nnextdev:\r\n\tmov edx,[edx].VxD_Desc_Block.DDB_Next\r\n\tand edx, edx\r\n\tjnz @B\r\n\tstc\r\n\tret\r\ncmpname:\r\n\tpushad\r\n\tlea esi, [edx].VxD_Desc_Block.DDB_Name\r\n\tmov ecx, sizeof VxD_Desc_Block.DDB_Name\r\n\trepz cmpsb\r\n\tpopad\r\n\tjnz nextdev\r\nfound:\r\n\tmov eax, edx\r\n\tret\r\n\talign 4\r\n\r\nFindDevice endp\r\n\r\n;--- esi -> DDB\r\n;--- C set on failure\r\n;--- new v5.74: alloc v86 callback if v86 api is to be installed\r\n\r\nVMM_Add_DDB proc public\r\n\tmovzx eax, [esi].VxD_Desc_Block.DDB_Req_Device_Number\r\n\t@dprintf ?JLMDBG, <\"VMM_Add_DBB, ID=%X\",10>, eax\r\n\tmov edx, offset ddb_list\r\n@@nextitem:\r\n\tand eax,eax\r\n\tjz @F\r\n\tcmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number\r\n\tjz error\r\n@@:\r\n\tmov ecx, edx\r\n\tmov edx,[edx].VxD_Desc_Block.DDB_Next\r\n\tand edx, edx\r\n\tjnz @@nextitem\r\n\tand eax,eax\r\n\tjz done\r\n\tcmp [esi].VxD_Desc_Block.DDB_V86_API_Proc,0\t\t;v86 API?\r\n\tjz done\r\n\t@dprintf ?JLMDBG, <\"VMM_Add_DBB: JLM with API\",10>\r\n\tpush ecx\r\n\tpush esi\r\n\tmov esi,[esi].VxD_Desc_Block.DDB_V86_API_Proc\r\n\txor edx,edx\r\n\tcall Allocate_V86_Call_Back\r\n\tpop esi\r\n\tpop ecx\r\n\tjc error\r\n\t@dprintf ?JLMDBG, <\"VMM_Add_DBB: callback allocated\",10>\r\n\tmov [esi].VxD_Desc_Block.DDB_V86_API_CSIP, eax\r\n\tcmp [Int2fHooked],0\r\n\tjnz done\r\n\tinc [Int2fHooked]\r\n\tpushad\r\n\tmov eax, ?APIINT\r\n\tmov esi, offset Int2fHook\r\n\tcall Hook_V86_Int_Chain\r\n\t@dprintf ?JLMDBG, <\"VMM_Add_DBB: Int 2F hooked, addr=%X\",10>, esi\r\n\tpopad\r\ndone:\r\n\tmov [ecx].VxD_Desc_Block.DDB_Next, esi\r\n\tret\r\nerror:\r\n\tstc\r\n\tret\r\n\talign 4\r\nVMM_Add_DDB endp\r\n\r\nifdef _DEBUG\r\n\tdd offset trueprevint2f\r\nfinalint2f proc\r\n\tcmp word ptr [ebp].Client_Reg_Struc.Client_EAX, 168Fh\r\n\tjz done\r\n\t@dprintf ?INT2FHOOKDBG, <\"finalint2f: ax=%X\",10>, word ptr [ebp].Client_Reg_Struc.Client_EAX\r\ndone:\r\n\tstc\r\n\tret\r\nfinalint2f endp\r\nendif\r\n\r\n;--- a hook proc must be prefixed by the flat address\r\n;--- of the variable where the previous value is stored\r\n;--- AX=???\r\n\r\n\tdd offset prevint2f\r\nInt2fHook proc\r\n\r\nifdef _DEBUG\r\n\tcmp word ptr [ebp].Client_Reg_Struc.Client_EAX, 168Fh\t;that happens pretty often, so don't log it\r\n\tjz @F\r\nendif\r\n\t@dprintf ?INT2FHOOKDBG, <\"Int2fHook: ax=%X, client_ax=%X\",10>, ax, word ptr [ebp].Client_Reg_Struc.Client_EAX\r\n@@:\r\n\tcmp word ptr [ebp].Client_Reg_Struc.Client_EAX, 1684h\r\n\tjnz not_ours\r\n\t@dprintf ?INT2FHOOKDBG, <\"Int2FHook: ax=1684h detected, device scan, BX=ID=%X\",10>, word ptr [ebp].Client_Reg_Struc.Client_EBX\r\n\tmov edx, offset ddb_list\r\n\tmov ax, word ptr [ebp].Client_Reg_Struc.Client_EBX \t;v5.85 added\r\n@@nextitem:\r\n\tcmp ax, [edx].VxD_Desc_Block.DDB_Req_Device_Number\r\n\tjz found\r\n\tmov edx,[edx].VxD_Desc_Block.DDB_Next\r\n\tand edx, edx\r\n\tjnz @@nextitem\r\n\txor eax, eax\r\n\tjmp @F\r\nfound:\r\n\t@dprintf ?INT2FHOOKDBG, <\"Int2FHook: found Device=%X\",10>, word ptr [edx].VxD_Desc_Block.DDB_Req_Device_Number\r\n\tmov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 0\t;v5.82: set AL to 0 if ok\r\n\tmov eax, [edx].VxD_Desc_Block.DDB_V86_API_CSIP\r\n@@:\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_EDI, ax\r\n\tshr eax, 16\r\n\tmov word ptr [ebp].Client_Reg_Struc.Client_ES, ax\r\n\r\n\tclc\r\n\tret\r\nnot_ours:\r\n\tstc\r\n\tret\r\n\talign 4\r\nInt2fHook endp\r\n\r\n;--- edi -> DDB\r\n;--- C set on failure\r\n;--- new v5.74: free v86 callback if v86 api exists\r\n\r\nVMM_Remove_DDB proc public\r\n\txor eax, eax\r\n\tand edi, edi\r\n\tjz @@error\r\n\tmov edx, offset ddb_list\r\n@@nextitem:\r\n\tcmp edx, edi\r\n\tjz @@found\r\n\tmov eax, edx\r\n\tmov edx,[edx].VxD_Desc_Block.DDB_Next\r\n\tand edx, edx\r\n\tjnz @@nextitem\r\n@@error:\r\n\tstc\r\n\tret\r\n@@found:\r\n\tand eax, eax\t;dont remove the first device\r\n\tjz @@error\r\n\tmov ecx,[edx].VxD_Desc_Block.DDB_Next\r\n\tmov [eax].VxD_Desc_Block.DDB_Next, ecx\r\n\tmov eax, [edi].VxD_Desc_Block.DDB_V86_API_CSIP\r\n\tand eax, eax\r\n\tjz @F\r\n\tcall Free_V86_Call_Back\r\n@@:\r\n\tclc\r\n\tret\r\n\talign 4\r\nVMM_Remove_DDB endp\r\n\r\n\tend\r\n"
  },
  {
    "path": "Tools/JLOAD/WINNT.INC",
    "content": "\r\nifndef WINNT_INCLUDED\r\n\r\n\tpushcontext listing\r\n\t.nolist\r\n\t.xcref\r\n\r\nifdef @list_on\r\n\t.list\r\n\t.cref\r\nendif\r\n\r\nWINNT_INCLUDED equ 1\r\n\r\nHANDLE\t\ttypedef ptr\r\nifndef HINSTANCE\r\nHINSTANCE\ttypedef HANDLE\r\nendif\r\n\r\nMINCHAR\t\tEQU\t80h\r\nMAXCHAR\t\tEQU\t7fh\r\nMINSHORT\tEQU\t8000h\r\nMAXSHORT\tEQU\t7fffh\r\nMINLONG\t\tEQU\t80000000h\r\nMAXLONG\t\tEQU\t7fffffffh\r\nMAXBYTE\t\tEQU\t0ffh\r\nMAXWORD\t\tEQU\t0ffffh\r\nMAXDWORD\tEQU\t0ffffffffh\r\n\r\nSECTION_QUERY       equ 00001h\r\nSECTION_MAP_WRITE   equ 00002h\r\nSECTION_MAP_READ    equ 00004h\r\nSECTION_MAP_EXECUTE equ 00008h\r\nSECTION_EXTEND_SIZE equ 00010h\r\n\r\nDUPLICATE_CLOSE_SOURCE      equ 00000001h\r\nDUPLICATE_SAME_ACCESS       equ 00000002h\r\n\r\nPAGE_NOACCESS           equ     1h\r\nPAGE_READONLY           equ     2h\r\nPAGE_READWRITE          equ     4h\r\nPAGE_WRITECOPY          equ     8h\r\nPAGE_EXECUTE            equ    10h\r\nPAGE_EXECUTE_READ       equ    20h\r\nPAGE_EXECUTE_READWRITE  equ    40h\r\nPAGE_EXECUTE_WRITECOPY  equ    80h\r\nPAGE_GUARD              equ   100h\r\nPAGE_NOCACHE            equ   200h\r\nMEM_COMMIT              equ  1000h\r\nMEM_RESERVE             equ  2000h\r\nMEM_DECOMMIT            equ  4000h\r\nMEM_RELEASE             equ  8000h\r\nMEM_FREE                equ 10000h\r\nMEM_PRIVATE             equ 20000h\r\nMEM_MAPPED              equ 40000h\r\nMEM_RESET               equ 80000h\r\n;#define MEM_TOP_DOWN       0x100000\r\n;#define SEC_FILE           0x800000\r\n;#define SEC_IMAGE         0x1000000\r\n;#define SEC_RESERVE       0x4000000\r\n;#define SEC_COMMIT        0x8000000\r\n;#define SEC_NOCACHE      0x10000000\r\n\r\nHEAP_NO_SERIALIZE           equ 00000001h\r\nHEAP_GROWABLE               equ 00000002h\r\nHEAP_GENERATE_EXCEPTIONS    equ 00000004h\r\nHEAP_ZERO_MEMORY            equ 00000008h\r\nHEAP_REALLOC_IN_PLACE_ONLY  equ 00000010h\r\n\r\nMEMORY_BASIC_INFORMATION STRUCT\r\n  BaseAddress       DWORD      ?\r\n  AllocationBase    DWORD      ?\r\n  AllocationProtect DWORD      ?\r\n  RegionSize        DWORD      ?\r\n  State             DWORD      ?\r\n  Protect           DWORD      ?\r\n  Type_             DWORD      ?\r\nMEMORY_BASIC_INFORMATION ENDS\r\n\r\n\r\n\r\nRTL_CRITICAL_SECTION struct\r\nDebugInfo       dd ?\r\nLockCount       sdword ?\r\nRecursionCount  sdword ?\r\nOwningThread    dd ?\r\nLockSemaphore   dd ?\r\nSpinCount\t\tdd ?\r\nRTL_CRITICAL_SECTION ends\r\n\r\nDLL_PROCESS_ATTACH      equ 1\r\nDLL_THREAD_ATTACH       equ 2\r\nDLL_THREAD_DETACH       equ 3\r\nDLL_PROCESS_DETACH      equ 0\r\n\r\nLANG_NEUTRAL\t\tequ 0\r\nSUBLANG_NEUTRAL\t\tequ 0\r\nSUBLANG_DEFAULT\t\tequ 1\r\nSUBLANG_SYS_DEFAULT\tequ 2\r\nSORT_DEFAULT\t\tequ 0\r\n\r\nMAKELANGID\tmacro p:req, s:req\r\n\texitm <(s shl 10) or p>\r\n\tendm\r\nMAKELCID macro lgid:req, srtid:req\r\n\texitm <(srtid shl 16) or lgid>\r\n\tendm\r\n\r\nCSTR_LESS_THAN      equ      1          ; // string 1 less than string 2\r\nCSTR_EQUAL          equ      2          ; // string 1 equal to string 2\r\nCSTR_GREATER_THAN   equ      3          ; // string 1 greater than string 2\r\n\r\nNORM_IGNORECASE     equ      000000001h ; /* ignore case */\r\n\r\n\r\n\r\nLANG_SYSTEM_DEFAULT    equ MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT)\r\nLANG_USER_DEFAULT      equ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)\r\nLOCALE_SYSTEM_DEFAULT  equ MAKELCID(LANG_SYSTEM_DEFAULT, SORT_DEFAULT)\r\nLOCALE_USER_DEFAULT    equ MAKELCID(LANG_USER_DEFAULT, SORT_DEFAULT)\r\n\r\n\r\nFILE_ATTRIBUTE_READONLY    equ  00000001h\r\nFILE_ATTRIBUTE_HIDDEN      equ  00000002h\r\nFILE_ATTRIBUTE_SYSTEM      equ  00000004h\r\nFILE_ATTRIBUTE_DIRECTORY   equ  00000010h\r\nFILE_ATTRIBUTE_ARCHIVE     equ  00000020h\r\nFILE_ATTRIBUTE_NORMAL      equ  00000080h\r\nFILE_ATTRIBUTE_TEMPORARY   equ  00000100h\r\nFILE_ATTRIBUTE_COMPRESSED  equ  00000800h\r\nFILE_ATTRIBUTE_OFFLINE     equ  00001000h\r\n\r\nFILE_SHARE_READ            equ  01\r\nFILE_SHARE_WRITE           equ  02\r\nFILE_SHARE_DELETE          equ  04\r\n\r\n\r\nSTATUS_WAIT_0                   equ 000000000h\r\nSTATUS_ABANDONED_WAIT_0         equ 000000080h\r\nSTATUS_USER_APC                 equ 0000000C0h\r\nSTATUS_TIMEOUT                  equ 000000102h\r\nSTATUS_PENDING                  equ 000000103h\r\nSTATUS_SEGMENT_NOTIFICATION     equ 040000005h\r\nSTATUS_GUARD_PAGE_VIOLATION     equ 080000001h\r\nSTATUS_DATATYPE_MISALIGNMENT    equ 080000002h\r\nSTATUS_BREAKPOINT               equ 080000003h\r\nSTATUS_SINGLE_STEP              equ 080000004h\r\nSTATUS_ACCESS_VIOLATION         equ 0C0000005h\r\nSTATUS_IN_PAGE_ERROR            equ 0C0000006h\r\nSTATUS_INVALID_HANDLE           equ 0C0000008h\r\nSTATUS_NO_MEMORY                equ 0C0000017h\r\nSTATUS_ILLEGAL_INSTRUCTION      equ 0C000001Dh\r\nSTATUS_NONCONTINUABLE_EXCEPTION equ 0C0000025h\r\nSTATUS_INVALID_DISPOSITION      equ 0C0000026h\r\nSTATUS_ARRAY_BOUNDS_EXCEEDED    equ 0C000008Ch\r\nSTATUS_FLOAT_DENORMAL_OPERAND   equ 0C000008Dh\r\nSTATUS_FLOAT_DIVIDE_BY_ZERO     equ 0C000008Eh\r\nSTATUS_FLOAT_INEXACT_RESULT     equ 0C000008Fh\r\nSTATUS_FLOAT_INVALID_OPERATION  equ 0C0000090h\r\nSTATUS_FLOAT_OVERFLOW           equ 0C0000091h\r\nSTATUS_FLOAT_STACK_CHECK        equ 0C0000092h\r\nSTATUS_FLOAT_UNDERFLOW          equ 0C0000093h\r\nSTATUS_INTEGER_DIVIDE_BY_ZERO   equ 0C0000094h\r\nSTATUS_INTEGER_OVERFLOW         equ 0C0000095h\r\nSTATUS_PRIVILEGED_INSTRUCTION   equ 0C0000096h\r\nSTATUS_STACK_OVERFLOW           equ 0C00000FDh\r\nSTATUS_CONTROL_C_EXIT           equ 0C000013Ah\r\n\r\nTHREAD_BASE_PRIORITY_LOWRT\tEQU\t15\r\nTHREAD_BASE_PRIORITY_MAX\tEQU\t2\r\nTHREAD_BASE_PRIORITY_MIN\tEQU\t- 2\r\nTHREAD_BASE_PRIORITY_IDLE\tEQU\t- 15\r\n\r\nREG_NONE\t\t\t\t\t\tEQU\t0\r\nREG_SZ\t\t\t\t\t\t\tEQU\t1\r\nREG_EXPAND_SZ\t\t\t\t\tEQU\t2\r\nREG_BINARY\t\t\t\t\t\tEQU\t3\r\nREG_DWORD\t\t\t\t\t\tEQU\t4\r\nREG_DWORD_LITTLE_ENDIAN\t\t\tEQU\t4\r\nREG_DWORD_BIG_ENDIAN\t\t\tEQU\t5\r\nREG_LINK\t\t\t\t\t\tEQU\t6\r\nREG_MULTI_SZ\t\t\t\t\tEQU\t7\r\nREG_RESOURCE_LIST\t\t\t\tEQU\t8\r\nREG_FULL_RESOURCE_DESCRIPTOR\tEQU\t9\r\nREG_RESOURCE_REQUIREMENTS_LIST\tEQU\t10\r\nREG_QWORD\t\t\t\t\t\tEQU\t11\r\nREG_QWORD_LITTLE_ENDIAN\t\t\tEQU\t11\r\n\r\nCONTEXT_i386 equ   00010000h\r\nCONTEXT_i486 equ   00010000h\r\n\r\nCONTEXT_CONTROL         equ CONTEXT_i386 or 00000001h ;// SS:SP, CS:IP, FLAGS, BP\r\nCONTEXT_INTEGER         equ CONTEXT_i386 or 00000002h ;// AX, BX, CX, DX, SI, DI\r\nCONTEXT_SEGMENTS        equ CONTEXT_i386 or 00000004h ;// DS, ES, FS, GS\r\nCONTEXT_FLOATING_POINT  equ CONTEXT_i386 or 00000008h ;// 387 state\r\nCONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 or 00000010h ;// DB 0-3,6,7\r\n\r\nCONTEXT_FULL equ CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS\r\n\r\nSIZE_OF_80387_REGISTERS equ  80\r\n\r\nFLOATING_SAVE_AREA struct\r\nControlWord    dd ?\r\nStatusWord     dd ?\r\nTagWord        dd ?\r\nErrorOffset    dd ?\r\nErrorSelector  dd ?\r\nDataOffset     dd ?\r\nDataSelector   dd ?\r\nRegisterArea   db SIZE_OF_80387_REGISTERS dup (?)\r\nCr0NpxState    dd ?\r\nFLOATING_SAVE_AREA ends\r\n\r\n;/\r\n;/ Context Frame\r\n;/\r\n;/  This frame has a several purposes: 1) it is used as an argument to\r\n;/  NtContinue, 2) is is used to constuct a call frame for APC delivery,\r\n;/  and 3) it is used in the user level thread creation routines.\r\n;/\r\n;/  The layout of the record conforms to a standard call frame.\r\n;/\r\n\r\nCONTEXT struct\r\n\r\n;   //\r\n;   // The flags values within this flag control the contents of\r\n;   // a CONTEXT record.\r\n;   //\r\n;   // If the context record is used as an input parameter, then\r\n;   // for each portion of the context record controlled by a flag\r\n;   // whose value is set, it is assumed that that portion of the\r\n;   // context record contains valid context. If the context record\r\n;   // is being used to modify a threads context, then only that\r\n;   // portion of the threads context will be modified.\r\n;   //\r\n;   // If the context record is used as an IN OUT parameter to capture\r\n;   // the context of a thread, then only those portions of the thread's\r\n;   // context corresponding to set flags will be returned.\r\n;   //\r\n;   // The context record is never used as an OUT only parameter.\r\n;   //\r\n\r\nContextFlags dd ?\t;+0\r\n\r\n;   //\r\n;   // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is\r\n;   // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT\r\n;   // included in CONTEXT_FULL.\r\n;   //\r\n\r\nrDr0 dd ?\t;+4\r\nrDr1 dd ?\r\nrDr2 dd ?\r\nrDr3 dd ?\r\nrDr6 dd ?\r\nrDr7 dd ?\r\n\r\n;   //\r\n;   // This section is specified/returned if the\r\n;   // ContextFlags word contains the flag CONTEXT_FLOATING_POINT.\r\n;   //\r\n\r\nFloatSave FLOATING_SAVE_AREA {}\t;+28\r\n\r\n;   //\r\n;   // This section is specified/returned if the\r\n;   // ContextFlags word contains the flag CONTEXT_SEGMENTS.\r\n;   //\r\n\r\nSegGs dd ?\t;+140\r\nSegFs dd ?\r\nSegEs dd ?\r\nSegDs dd ?\r\n\r\n;   //\r\n;   // This section is specified/returned if the\r\n;   // ContextFlags word contains the flag CONTEXT_INTEGER.\r\n;   //\r\n\r\nrEdi dd ?\t;+156\r\nrEsi dd ?\r\nrEbx dd ?\r\nrEdx dd ?\r\nrEcx dd ?\r\nrEax dd ?\r\n\r\n;   //\r\n;   // This section is specified/returned if the\r\n;   // ContextFlags word contains the flag CONTEXT_CONTROL.\r\n;   //\r\n\r\nrEbp   dd ?\t;+180\r\nrEip   dd ?\r\nSegCs  dd ?\r\nEFlags dd ?\r\nrEsp   dd ?\r\nSegSs  dd ?\r\n\r\nCONTEXT ends\t;+204\r\n\r\nIMAGE_DOS_HEADER STRUCT\r\n  e_magic           WORD      ?\t\t;+0\r\n  e_cblp            WORD      ?\t\t;+2\r\n  e_cp              WORD      ?\t\t;+4\r\n  e_crlc            WORD      ?\t\t;+6\t\tnumber of relocation records\r\n  e_cparhdr         WORD      ?\t\t;+8\r\n  e_minalloc        WORD      ?\t\t;+10\r\n  e_maxalloc        WORD      ?\t\t;+12\r\n  e_ss              WORD      ?\t\t;+14\r\n  e_sp              WORD      ?\t\t;+16\r\n  e_csum            WORD      ?\t\t;+18\r\n  e_ip              WORD      ?\t\t;+20\r\n  e_cs              WORD      ?\t\t;+22\r\n  e_lfarlc          WORD      ?\t\t;+24\tbegin relocation records\r\n  e_ovno            WORD      ?\t\t;+26\r\n  e_res             WORD   4 dup(?)\t;+28\r\n  e_oemid           WORD      ?\t\t;+36\r\n  e_oeminfo         WORD      ?\t\t;+38\r\n  e_res2            WORD  10 dup(?)\t;+40\r\n  e_lfanew          DWORD      ?\t;+60\r\nIMAGE_DOS_HEADER ENDS\r\n\r\n\r\nIMAGE_FILE_HEADER struct           ;size=20\r\nMachine\t\t\t\tdw ?\t\t\t;0\r\nNumberOfSections\tdw ?\r\nTimeDateStamp\t\tdd ?\t\t\t;4\r\nPointerToSymbolTable dd ?\t\t\t;8\r\nNumberOfSymbols\t\tdd ?\t\t\t;12\r\nSizeOfOptionalHeader dw ?\t\t\t;16\r\nCharacteristics\t\tdw ?           ;flags\r\nIMAGE_FILE_HEADER ends\r\n\r\n;*** flags ***\r\n\r\n; 0400: If Image is on removable media, copy and run from the swap file.\r\n; 0800: If Image is on Net, copy and run from the swap file.\r\n\r\nIMAGE_FILE_RELOCS_STRIPPED     equ 0001h\r\nIMAGE_FILE_EXECUTABLE_IMAGE    equ 0002h\r\nIMAGE_FILE_LINE_NUMS_STRIPPED  equ 0004h\r\nIMAGE_FILE_LOCAL_SYMS_STRIPPED equ 0008h\r\nIMAGE_FILE_16BIT_MACHINE       equ 0040h\r\nIMAGE_FILE_BYTES_REVERSED_LO   equ 0080h  ; Bytes of machine word are reversed.\r\nIMAGE_FILE_32BIT_MACHINE       equ 0100h\r\nIMAGE_FILE_DEBUG_STRIPPED      equ 0200h  ; Debugging info stripped from file in .DBG file\r\nIMAGE_FILE_REMOVABLE_RUN_FROM_SWAP equ 0400h\r\nIMAGE_FILE_NET_RUN_FROM_SWAP   equ 0800h\r\nIMAGE_FILE_SYSTEM              equ 1000h  ; System File.\r\nIMAGE_FILE_DLL                 equ 2000h\r\nIMAGE_FILE_UP_SYSTEM_ONLY      equ 4000h  ; File should only be run on a UP machine\r\nIMAGE_FILE_BYTES_REVERSED_HI   equ 8000h  ; Bytes of machine word are reversed.\r\n\r\nIMAGE_FILE_MACHINE_UNKNOWN    equ      0h\r\nIMAGE_FILE_MACHINE_I386       equ    14ch   ;// Intel 386.\r\nIMAGE_FILE_MACHINE_R3000      equ    162h   ;// MIPS little-endian, 0x160 big-endian\r\nIMAGE_FILE_MACHINE_R4000      equ    166h   ;// MIPS little-endian\r\nIMAGE_FILE_MACHINE_R10000     equ    168h   ;// MIPS little-endian\r\nIMAGE_FILE_MACHINE_ALPHA      equ    184h   ;// Alpha_AXP\r\nIMAGE_FILE_MACHINE_POWERPC    equ    1F0h   ;// IBM PowerPC Little-Endian\r\n\r\n\r\nIMAGE_DATA_DIRECTORY struct\r\nVirtualAddress    DWORD   ?\r\nSize_\t\t\t  DWORD   ?\r\nIMAGE_DATA_DIRECTORY ends\r\n\r\nIMAGE_NUMBEROF_DIRECTORY_ENTRIES    equ 16\r\n\r\n\r\nIMAGE_OPTIONAL_HEADER  struct           ;size = 28 + 68 + 128 = 224\r\n                         ;standard, size=28\r\nMagic\t\t\t\t\tdw ?\t;0\r\nMajorLinkerVersion\t\tdb ?\r\nMinorLinkerVersion\t\tdb ?\r\nSizeOfCode\t\t\t\tdd ?\t;4\t\r\nSizeOfInitializedData\tdd ?\t;8\r\nSizeOfUninitializedData dd ?\t;12\r\nAddressOfEntryPoint     dd ?\t;16\r\nBaseOfCode\t\t\t\tdd ?\t;20\r\nBaseOfData\t\t\t\tdd ?\t;24\r\n                         ;NT specific, size = 68\r\nImageBase\t\t\t\tdd ?\t;28\r\nSectionAlignment\t\tdd ?\t;32\r\nFileAlignment\t\t\tdd ?\t;36\r\nMajorOperatingSystemVersion   dw ?\t;40\r\nMinorOperatingSystemVersion   dw ?\r\nMajorImageVersion\t\tdw ?\t;44\r\nMinorImageVersion\t\tdw ?\t\r\nMajorSubsystemVersion\tdw ?\t;48\r\nMinorSubsystemVersion\tdw ?\r\nWin32VersionValue\t\tdd ?\t;52\r\nSizeOfImage\t\t\t\tdd ?\t;56\r\nSizeOfHeaders\t\t\tdd ?\t;60\r\nCheckSum\t\t\t\tdd ?\t;64\r\nSubsystem\t\t\t\tdw ?\t;68\r\nDllCharacteristics\t\tdw ?\r\nSizeOfStackReserve\t\tdd ?\t;72\r\nSizeOfStackCommit\t\tdd ?\t;76\r\nSizeOfHeapReserve\t\tdd ?\t;80\r\nSizeOfHeapCommit\t\tdd ?\t;84\r\nLoaderFlags\t\t\t\tdd ?\t;88\r\nNumberOfRvaAndSizes     dd ?\t;92\r\nDataDirectory IMAGE_DATA_DIRECTORY\tIMAGE_NUMBEROF_DIRECTORY_ENTRIES dup (<>)\t\r\nIMAGE_OPTIONAL_HEADER  ends\r\n\r\n; Subsystem Values\r\n\r\nIMAGE_SUBSYSTEM_UNKNOWN       equ    0   ;/ Unknown subsystem.\r\nIMAGE_SUBSYSTEM_NATIVE        equ    1   ;/ Image doesn't require a subsystem.\r\nIMAGE_SUBSYSTEM_WINDOWS_GUI   equ    2   ;/ Image runs in the Windows GUI subsystem.\r\nIMAGE_SUBSYSTEM_WINDOWS_CUI   equ    3   ;/ Image runs in the Windows character subsystem.\r\nIMAGE_SUBSYSTEM_OS2_CUI       equ    5   ;/ image runs in the OS/2 character subsystem.\r\nIMAGE_SUBSYSTEM_POSIX_CUI     equ    7   ;/ image run  in the Posix character subsystem.\r\nIMAGE_SUBSYSTEM_RESERVED8     equ    8   ;/ image run  in the 8 subsystem.\r\n\r\n\r\n; Directory Entries\r\n\r\nIMAGE_DIRECTORY_ENTRY_EXPORT       equ   0  ;// Export Directory\r\nIMAGE_DIRECTORY_ENTRY_IMPORT       equ   1  ;// Import Directory\r\nIMAGE_DIRECTORY_ENTRY_RESOURCE     equ   2  ;// Resource Directory\r\nIMAGE_DIRECTORY_ENTRY_EXCEPTION    equ   3  ;// Exception Directory\r\nIMAGE_DIRECTORY_ENTRY_SECURITY     equ   4  ;// Security Directory\r\nIMAGE_DIRECTORY_ENTRY_BASERELOC    equ   5  ;// Base Relocation Table\r\nIMAGE_DIRECTORY_ENTRY_DEBUG        equ   6  ;// Debug Directory\r\nIMAGE_DIRECTORY_ENTRY_COPYRIGHT    equ   7  ;// Description String\r\nIMAGE_DIRECTORY_ENTRY_ARCHITECTURE   equ   7   ;// Architecture Specific Data\r\nIMAGE_DIRECTORY_ENTRY_GLOBALPTR    equ   8  ;// Machine Value (MIPS GP)\r\nIMAGE_DIRECTORY_ENTRY_TLS          equ   9  ;// TLS Directory\r\nIMAGE_DIRECTORY_ENTRY_LOAD_CONFIG  equ  10  ;// Load Configuration Directory\r\nIMAGE_DIRECTORY_ENTRY_BOUND_IMPORT equ  11  ;// Bound Import Directory in headers\r\nIMAGE_DIRECTORY_ENTRY_IAT          equ  12  ;// Import Address Table\r\nIMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   equ  13   ;// Delay Load Import Descriptors\r\nIMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR equ  14   ;// COM Runtime descriptor\r\n\r\n\r\nIMAGE_NT_HEADERS struct\r\nSignature\t\tdd ?\t\t           ;00 \"PE\"\r\nFileHeader\t\tIMAGE_FILE_HEADER <>\r\nOptionalHeader\tIMAGE_OPTIONAL_HEADER <>\r\nIMAGE_NT_HEADERS ends\r\n\r\n;--- section table\r\n\r\nIMAGE_SECTION_HEADER struct\r\nName_\t\t\tdb 8 dup (?)\t;0\r\nunion Misc\r\nPhysicalAddress dd ?\t\t\t;8\r\nVirtualSize     dd ?\r\nends\r\nVirtualAddress  dd ?\t\t\t;12\r\nSizeOfRawData   dd ?\t\t\t;16\r\nPointerToRawData dd ?\t\t\t;20\r\nPointerToRelocations dd ?\t\t;24\tpoints to array of IMAGE_RELOCATIONs (.OBJ)\r\nPointerToLinenumbers  dd ?\t\t;28\r\nNumberOfRelocations dw ?\t\t;32\r\nNumberOfLinenumbers  dw ?\r\nCharacteristics  dd ?\t\t\t;36\r\nIMAGE_SECTION_HEADER ends\r\n\r\n;//\r\n;// Section characteristics.\r\n;//\r\n\r\nIMAGE_SCN_TYPE_NO_PAD            equ 00000008h ; Reserved.\r\n                                               ;\r\nIMAGE_SCN_CNT_CODE               equ 00000020h ; Section contains code.\r\nIMAGE_SCN_CNT_INITIALIZED_DATA   equ 00000040h ; Section contains initialized data.\r\nIMAGE_SCN_CNT_UNINITIALIZED_DATA equ 00000080h ; Section contains uninitialized data.\r\n                                               ;\r\nIMAGE_SCN_LNK_OTHER              equ 00000100h ; Reserved.\r\nIMAGE_SCN_LNK_INFO               equ 00000200h ; Section contains comments or some other type of information.\r\n;IMAGE_SCN_TYPE_OVER             equ 00000400h ; Reserved.\r\nIMAGE_SCN_LNK_REMOVE             equ 00000800h ; Section contents will not become part of image.\r\nIMAGE_SCN_LNK_COMDAT             equ 00001000h ; Section contents comdat.\r\n;//                                  00002000h ; Reserved.\r\n                                               ;\r\n;IMAGE_SCN_MEM_PROTECTED - Obsolete  00004000h ;\r\nIMAGE_SCN_MEM_FARDATA            equ 00008000h ;\r\n;IMAGE_SCN_MEM_SYSHEAP  - Obsolete   00010000h ;\r\nIMAGE_SCN_MEM_PURGEABLE          equ 00020000h ;\r\nIMAGE_SCN_MEM_16BIT              equ 00020000h ;\r\nIMAGE_SCN_MEM_LOCKED             equ 00040000h ;\r\nIMAGE_SCN_MEM_PRELOAD            equ 00080000h ;\r\n                                               ;\r\nIMAGE_SCN_ALIGN_1BYTES           equ 00100000h ;\r\nIMAGE_SCN_ALIGN_2BYTES           equ 00200000h ;\r\nIMAGE_SCN_ALIGN_4BYTES           equ 00300000h ;\r\nIMAGE_SCN_ALIGN_8BYTES           equ 00400000h ;\r\nIMAGE_SCN_ALIGN_16BYTES          equ 00500000h ; Default alignment if no others are specified.\r\nIMAGE_SCN_ALIGN_32BYTES          equ 00600000h ;\r\nIMAGE_SCN_ALIGN_64BYTES          equ 00700000h ;\r\n;// Unused                           00800000h ;\r\n                                               ;\r\nIMAGE_SCN_LNK_NRELOC_OVFL        equ 01000000h ; Section contains extended relocations.\r\nIMAGE_SCN_MEM_DISCARDABLE        equ 02000000h ; Section can be discarded.\r\nIMAGE_SCN_MEM_NOT_CACHED         equ 04000000h ; Section is not cachable.\r\nIMAGE_SCN_MEM_NOT_PAGED          equ 08000000h ; Section is not pageable.\r\nIMAGE_SCN_MEM_SHARED             equ 10000000h ; Section is shareable.\r\nIMAGE_SCN_MEM_EXECUTE            equ 20000000h ; Section is executable.\r\nIMAGE_SCN_MEM_READ               equ 40000000h ; Section is readable.\r\nIMAGE_SCN_MEM_WRITE              equ 80000000h ; Section is writeable.\r\n\r\n;--- relocations\r\n\r\nIMAGE_RELOCATION struct\r\nunion \r\nVirtualAddress\t\tDWORD   ?\r\nRelocCount\t\t\tDWORD   ?\r\nends\r\nSymbolTableIndex\tDWORD   ?\r\nType_\t\t\t\tWORD    ?\r\nIMAGE_RELOCATION ends\r\n\r\n;//\r\n;// I386 relocation types.\r\n;//\r\nIMAGE_REL_I386_ABSOLUTE    equ   00000h  ;// Reference is absolute, no relocation is necessary\r\nIMAGE_REL_I386_DIR16       equ   00001h  ;// Direct 16-bit reference to the symbols virtual address\r\nIMAGE_REL_I386_REL16       equ   00002h  ;// PC-relative 16-bit reference to the symbols virtual address\r\nIMAGE_REL_I386_DIR32       equ   00006h  ;// Direct 32-bit reference to the symbols virtual address\r\nIMAGE_REL_I386_DIR32NB     equ   00007h  ;// Direct 32-bit reference to the symbols virtual address, base not included\r\nIMAGE_REL_I386_SEG12       equ   00009h  ;// Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address\r\nIMAGE_REL_I386_SECTION     equ   0000Ah\r\nIMAGE_REL_I386_SECREL      equ   0000Bh\r\nIMAGE_REL_I386_TOKEN       equ   0000Ch  ;// clr token\r\nIMAGE_REL_I386_SECREL7     equ   0000Dh  ;// 7 bit offset from base of section containing target\r\nIMAGE_REL_I386_REL32       equ   00014h  ;// PC-relative 32-bit reference to the symbols virtual address\r\n\r\n\r\nIMAGE_BASE_RELOCATION struct\r\nVirtualAddress\tDWORD   ?\r\nSizeOfBlock\t\tDWORD   ?\r\nIMAGE_BASE_RELOCATION ends\r\n\r\nIMAGE_REL_BASED_ABSOLUTE             equ 0\r\nIMAGE_REL_BASED_HIGH                 equ 1\r\nIMAGE_REL_BASED_LOW                  equ 2\r\nIMAGE_REL_BASED_HIGHLOW              equ 3\r\nIMAGE_REL_BASED_HIGHADJ              equ 4\r\nIMAGE_REL_BASED_MIPS_JMPADDR         equ 5\r\nIMAGE_REL_BASED_MIPS_JMPADDR16       equ 9\r\nIMAGE_REL_BASED_IA64_IMM64           equ 9\r\nIMAGE_REL_BASED_DIR64                equ 10\r\n\r\n;--- exports\r\n\r\nIMAGE_EXPORT_DIRECTORY  struct\r\nCharacteristics \t\tdd ?\r\nTimeDateStamp\t\t\tdd ?\r\nMajorVersion\t\t\tdw ?\r\nMinorVersion\t\t\tdw ?\r\nnName\t\t\t\t\tdd ?\t;name of module\r\nnBase\t\t\t\t\tdd ?\t;base of ordinal\r\nNumberOfFunctions\t\tdd ?\t;number of entries in EAT table\r\nNumberOfNames\t\t\tdd ?\t;number of entries in name/ordinals table\r\nAddressOfFunctions\t\tdd ?\t;RVA \"export address table\" (EAT)\r\nAddressOfNames\t\t\tdd ?\t;RVA \"name table\" ()\r\nAddressOfNameOrdinals\tdd ?\t;RVA \"ordinals table\" (WORDS)\r\nIMAGE_EXPORT_DIRECTORY  ends\r\n\r\n        popcontext listing\r\n\r\nendif\r\n"
  },
  {
    "path": "Tools/JLOAD/license.txt",
    "content": "\nCopyright (c) 2021 Andreas Grech (japheth)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "Tools/MAKE.BAT",
    "content": "@echo off\r\ncd CPUID\r\ncall MAKE\r\ncd ..\r\ncd CpuStat\r\ncall MAKE\r\ncd ..\r\ncd EMSSTAT\r\ncall MAKE\r\ncd ..\r\ncd JEMFBHLP\r\ncall MAKE\r\ncd ..\r\ncd JLOAD\r\nnmake /nologo\r\ncd ..\r\ncd MEMSTAT\r\ncall MAKE\r\ncd ..\r\ncd MoveXBDA\r\ncall MAKE\r\ncd ..\r\ncd UMBM\r\ncall MAKE\r\ncd ..\r\ncd VCPI\r\ncall MAKE\r\ncd ..\r\ncd XMSSTAT\r\ncall MAKE\r\ncd ..\r\n"
  },
  {
    "path": "Tools/MEMSTAT/MAKE.BAT",
    "content": "@echo off\r\nrem creates MEMSTAT.EXE with JWasm\r\njwasm -c -nologo -mz -Fl MEMSTAT.ASM\r\n"
  },
  {
    "path": "Tools/MEMSTAT/MEMSTAT.ASM",
    "content": "\r\n;*** get extended memory status using int 15h\r\n;*** AH=88h\r\n;*** AH=C7h if supported\r\n;*** AX=E801h\r\n;*** AX=E820h\r\n\r\n\t.286\r\n\t.model small\r\n\t.386\r\n\r\nDGROUP group _TEXT\r\n\t.dosseg\r\n\tassume ds:dgroup\r\n\r\nprintf proto c :ptr byte, :VARARG\r\n\r\nCStr macro text:VARARG\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nI15C7S struct\r\nwSize\tdw ?\r\ndwLBelow16 dd ?\r\ndwLAbove16 dd ?\r\ndwSBelow16 dd ?\r\ndwSAbove16 dd ?\r\ndwCBelow16 dd ?\r\ndwCAbove16 dd ?\r\ndwXBelow16 dd ?\r\ndwXAbove16 dd ?\r\nwUMB\tdw ?\r\nwFree\tdw ?\r\n\t\tdd ?\r\nI15C7S ends\r\n\r\n\r\n\t.data?\r\n\r\nbuffer\tdb 20h dup (?)\r\ni15c7\tI15C7S <?>\r\n\r\n\t.data\r\nqSum\tdq 0\r\nqAvl\tdq 0\r\nqACPI\tdq 0\r\nqRsvd\tdq 0\r\n\r\n\t.code\r\n\r\n\tinclude printf.inc\r\n\r\n\t.386\r\n\r\n;--- display conventional memory size returned by INT 12h\r\n\r\nconv_mem proc\r\n\tint 12h\r\n\tinvoke printf, CStr(\"conventional memory (Int 12h): %u kB\",10), ax\r\n\tret\r\nconv_mem endp\r\n\r\n;--- call int 15h, ah=C7h (later PS/2 machines, usually fails on newer systems)\r\n\r\nI15C0S struct\r\nwBytes    dw ?\t;+0\r\nbModel    db ?\t;+2\r\nbSubModel db ?\t;+3\r\nbBiosRev  db ?\t;+4\r\nbFeature1 db ?\t;+5\r\nbFeature2 db ?\t;+6\r\nbFeature3 db ?\r\nbFeature4 db ?\r\nbFeature5 db ?\r\nI15C0S ends\r\n\r\ndo_c0c7 proc\r\n\tstc\r\n\tmov ah,0C0h\r\n\tint 15h\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"Int 15h, ah=C0h failed\",10)\r\n\tjmp no15c0\r\n@@:\r\n\tcmp es:[bx].I15C0S.wBytes,7\r\n\tjb no15c0\r\n\tmov al,es:[bx].I15C0S.bFeature2\r\n\ttest al,10h\t;int 15h, ah=0C7h supported?\r\n\tjz no15c7\r\n\tmov ah,0C7h\r\n\tmov si,offset i15c7\r\n\tint 15h\r\n\tjc no15c7\r\n\tinvoke printf, CStr(\"Int 15h, ah=C7h:\",10)\r\n\tinvoke printf, CStr(\"Local memory (1 MB-16 MB/16 MB-4 GB): %08lX/%08lX kB\",10),\\\r\n\t\ti15c7.dwLBelow16, i15c7.dwLAbove16\r\n\tinvoke printf, CStr(\"System memory (1 MB-16 MB/16 MB-4 GB): %08lX/%08lX kB\",10),\\\r\n\t\ti15c7.dwSBelow16, i15c7.dwSAbove16\r\n\tinvoke printf, CStr(\"Cacheable memory (1 MB-16 MB/16 MB-4 GB): %08lX/%08lX kB\",10),\\\r\n\t\ti15c7.dwCBelow16, i15c7.dwCAbove16\r\nno15c7:\r\nno15c0:\r\n\tret\r\ndo_c0c7 endp\r\n\r\n;--- print int 15h, ah=88h info\r\n\r\ndo_88 proc\r\n\tclc\r\n\tmov ax,8800h\r\n\tint 15h\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"Int 15h, ah=88h failed\",10)\r\n\tjmp done_88\r\n@@:\r\n\tinvoke printf, CStr(\"Int 15h, ah=88h, extended memory: %u kB\",10), ax\r\ndone_88:\r\n\tret\r\ndo_88 endp\r\n\r\n;--- print int 15h, ax=e801 info\r\n\r\ndo_e801 proc\r\n\txor cx,cx\r\n\txor dx,dx\r\n\txor bx,bx\r\n\tmov ax,0E801h\r\n\tclc\t\t\t;the carry flag is not reliably set/reset!\r\n\tint 15h\r\n\tjc error\r\n\tand bx, bx\r\n\tjnz @F\r\n\tand cx, cx\r\n\tjnz @F\r\nerror:\r\n\tinvoke printf, CStr(\"Int 15h, ax=E801h failed\",10)\r\n\tjmp done_e801\r\n@@:\r\n\t.if (!ax)\t\t\t;some bioses return values in CX:DX\r\n\t\tmov ax, cx\r\n\t\tmov bx, dx\r\n\t.endif\r\n\r\n\tpush bx\r\n\tpush ax\r\n\tinvoke printf, CStr(\"Int 15h, ax=E801h:\",10)\r\n\tpop ax\r\n\t.if (ax > 3C00h)\r\n\t   invoke printf, CStr(\"ext. memory below 16 MB: %u (0x%X) KB ???\",10), ax, ax\r\n\t.else\r\n\t   invoke printf, CStr(\"ext. memory below 16 MB: %u (0x%X) KB\",10), ax, ax\r\n\t.endif\r\n\tpop bx\r\n\tmov edx, 1000000h\r\n\tmov ax, bx\r\n\tmovzx ecx, bx\r\n\tshl ecx, 16\r\n\tadd ecx, edx\r\n\tdec ecx\r\n\r\n\tshr ax, 4\r\n\tinvoke printf, CStr(\"ext. memory above 16 MB: %u 64 KB blocks = %u MB [%lX-%lX]\",10), bx, ax, edx, ecx\r\ndone_e801:\r\n\tret\r\ndo_e801 endp\r\n\r\nSMAP equ 534d4150h\r\n\r\nE820MAP struct\r\nbaselow  dd ?\r\nbasehigh dd ?\r\nlenlow   dd ?\r\nlenhigh  dd ?\r\ntype_    dd ?\r\nE820MAP ends\r\n\r\n;--- print int 15h, ax=e820 info\r\n\r\ndo_e820 proc\r\n\tpush ds\r\n\tpop es\r\n\tmov si,0\r\n\tmov ebx,0\r\n\tmov di, offset buffer\r\n\t.while (1)\r\n\t\tmov ecx, sizeof E820MAP\r\n\t\tmov edx,\"SMAP\"\r\n\t\tmov eax,0E820h\r\n\t\tclc\r\n\t\tint 15h\r\n\t\t.break .if CARRY?\r\n\t\t.break .if (eax != \"SMAP\")\r\n\t\tpush ebx\r\n\t\t.if (!si)\r\n\t\t\tinvoke printf, CStr(\"Int 15h, eax=E820h:\",10)\r\n\t\t\tinvoke printf, CStr(\" address range          size       type \",10)\r\n\t\t\tinvoke printf, CStr(\"----------------------------------------------------\",10)\r\n\t\t\tinc si\r\n\t\t.endif\r\n;--- get size in EDX:EAX\r\n\t\tmov eax,[di].E820MAP.lenlow\r\n\t\tmov edx,[di].E820MAP.lenhigh\r\n\t\tmov ecx, [di].E820MAP.type_\r\n\t\t.if (ecx == 1)\r\n\t\t\tmov cx, CStr(\"available\")\r\n\t\t\tadd dword ptr qAvl+0, eax\r\n\t\t\tadc dword ptr qAvl+4, edx\r\n\t\t.elseif (ecx == 2)\r\n\t\t\tmov cx, CStr(\"reserved\")\r\n\t\t\tadd dword ptr qRsvd+0, eax\r\n\t\t\tadc dword ptr qRsvd+4, edx\r\n\t\t.elseif (ecx == 3)\r\n\t\t\tmov cx, CStr(\"ACPI reclaimable\")\r\n\t\t\tadd dword ptr qACPI+0, eax\r\n\t\t\tadc dword ptr qACPI+4, edx\r\n\t\t.elseif (ecx == 4)\r\n\t\t\tmov cx, CStr(\"ACPI NVS\")\r\n\t\t\tadd dword ptr qACPI+0, eax\r\n\t\t\tadc dword ptr qACPI+4, edx\r\n\t\t.elseif (ecx == 5)\r\n\t\t\tmov cx, CStr(\"bad memory\")\r\n\t\t.else\r\n\t\t\tmov cx, CStr(\"unknown\")\r\n\t\t.endif\r\n\t\tadd dword ptr qSum+0, eax\r\n\t\tadc dword ptr qSum+4, edx\r\n\t\tadd eax, [di].E820MAP.baselow\r\n\t\tadc edx, [di].E820MAP.basehigh\r\n\t\tsub eax,1\r\n\t\tsbb edx,0\r\n\t\t.if (dword ptr [di].E820MAP.lenhigh)\r\n\t\t\tinvoke printf, CStr(\"%lX%08lX-%lX%08lX  %lX%08lX  %lX (%s)\",10),\r\n\t\t\t\t[di].E820MAP.basehigh, [di].E820MAP.baselow, edx, eax,\r\n\t\t\t\t[di].E820MAP.lenhigh, [di].E820MAP.lenlow,\r\n\t\t\t\t[di].E820MAP.type_, cx\r\n\t\t.else\r\n\t\t\tinvoke printf, CStr(\"%lX%08lX-%lX%08lX  %9lX  %lX (%s)\",10),\r\n\t\t\t\t[di].E820MAP.basehigh, [di].E820MAP.baselow, edx, eax,\r\n\t\t\t\t[di].E820MAP.lenlow,\r\n\t\t\t\t[di].E820MAP.type_, cx\r\n\t\t.endif\r\n\t\tpop ebx\r\n\t\t.break .if (ebx == 0)\r\n\t.endw\r\n\t.if (!si)\r\n\t\tinvoke printf, CStr(\"Int 15h, eax=E820h failed\",10)\r\n\t.else\r\n\t\tinvoke printf, CStr(\"----------------------------------------------------\",10)\r\n\t\tmov eax,dword ptr qSum+0\r\n\t\tmov edx,dword ptr qSum+4\r\n\t\tmov ecx,100000h\r\n\t\tdiv ecx\r\n\t\tmov esi, eax\r\n\t\tmov ebx, eax\r\n\t\tmov eax,dword ptr qACPI+0\r\n\t\tmov edx,dword ptr qACPI+4\r\n\t\tmov ecx,400h\r\n\t\tdiv ecx\r\n\t\tmov ebx, eax\r\n\t\tmov eax,dword ptr qRsvd+0\r\n\t\tmov edx,dword ptr qRsvd+4\r\n\t\tmov ecx,100000h\r\n\t\tdiv ecx\r\n\t\tmov ebp, eax\r\n\t\tmov eax,dword ptr qAvl+0\r\n\t\tmov edx,dword ptr qAvl+4\r\n\t\tmov ecx,100000h\r\n\t\tdiv ecx\r\n\t\tinvoke printf, CStr(\"available: %lu MB, ACPI: %lu kB, rsvd: %lu MB, total: %lu MB\",10),\r\n\t\t\teax, ebx, ebp, esi\r\n\t.endif\r\n\tret\r\ndo_e820 endp\r\n\r\n;--- display location and size of XBDA\r\n\r\nxbda proc\r\n\tpush 40h\r\n\tpop es\r\n\tmov ax,es:[000Eh]\r\n\tand ax,ax\r\n\tjz noxbda\r\n\tmov es,ax\r\n\tmov bl,es:[0]\r\n\tmov bh,0\r\n\tinvoke printf, CStr(\"XBDA at segment %X, size %u kB\",10), ax, bx\r\n\tret\r\nnoxbda:\r\n\tinvoke printf, CStr(\"no XBDA on this system\",10)\r\n\tret\r\nxbda endp\r\n\r\n;*** main()\r\n\r\nmain proc c\r\n\r\n\tcall conv_mem\r\n\tcall xbda\r\n\tcall do_c0c7\r\n\tcall do_88\r\n\tcall do_e801\r\n\tcall do_e820\r\n\tret\r\nmain endp\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx\r\n\tcall main\r\n\tmov ah,4Ch\r\n\tint 21h\r\n\r\n\t.stack 400h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Tools/MEMSTAT/PRINTF.INC",
    "content": "\r\n;--- simple printf() implementation\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- outb is expected to be onto stack\r\n\r\nltob PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- ds=dgroup, ss don't need to be dgroup\r\n\r\nprintf PROC c uses si di bx fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\nnextdigit:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb digitsdone\r\n\tcmp BYTE PTR [si],'9'\r\n\tja digitsdone\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul cx,bx,10\t\t;cx = bx * 10\r\n\tadd ax,cx\r\n\tmov bx,ax\r\n\tjmp nextdigit\r\n\r\ndigitsdone:\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tmov al,'%'\r\n\tjmp @@L359\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Tools/MoveXBDA/MAKE.BAT",
    "content": "@echo off\r\nrem create MOVEXBDA.EXE with JWasm\r\njwasm -nologo -mz -Fl MOVEXBDA.ASM\r\n"
  },
  {
    "path": "Tools/MoveXBDA/MOVEXBDA.ASM",
    "content": "\r\n;--- MOVEXBDA moves XBDA to low memory. If the XBDA is already moved\r\n;--- or if it is too large, nothing is done.\r\n;--- MOVEXBDA hooks interrupt 19h and will restore the XBDA to its\r\n;--- previous location if this interrupt is called.\r\n;--- MOVEXBDA is Public Domain.\r\n\r\n\t.286\r\n\t.model small\r\nDGROUP group _TEXT\r\n\r\n;ifndef MAXXBDA\r\n;MAXXBDA equ 4\t\t;max size of XBDA in KB\r\n;endif\r\n\r\nXBDAADR equ 000Eh\t;address of XBDA segment in BIOS data region\r\nMEMSIZE equ 0013h\t;size of memory in kB in BIOS data region\r\n?WELCOME equ 0\t\t;display message at start\r\n\r\nMCB struct\r\nsig   db ?\r\npsp   dw ?\r\n_size dw ?\r\nMCB ends\r\n\r\nXBDA struct\r\n_size db ? ;size of XBDA in KB\r\nXBDA ends\r\n\r\nIODAT   struct\r\ncmdlen\tdb ?\r\nunit\tdb ?\r\ncmd \tdb ?\r\nstatus\tdw ?\r\n\t\tdb 8 dup (?)\r\nmedia\tdb ?\r\ntrans\tdd ?\r\ncount\tdw ?\t;+ 12 init:offset parameter line\r\nstart\tdw ?\t;+ 14 init:segment parameter line\r\ndrive\tdb ?\r\nIODAT   ends\r\n\r\n\t.code\r\n\r\n\tdw -1\r\n\tdw -1\r\n\tdw 8000h\t\t\t\t  ;attribute\r\n\tdw offset devstrat\t\t  ;device strategy\r\nintproc dw offset devintfirst\t  ;device interrupt\r\ndevname db 'MOVXBDA#'\r\n\r\ncmdptr  dd 1 dup (?)\r\noldint19 dd ?\r\n\r\ndevstrat proc far\r\n\tmov cs:word ptr[cmdptr],bx\r\n\tmov cs:word ptr[cmdptr+2],es\r\n\tret\r\ndevstrat endp\r\n\r\ndevint proc far\r\n\tpush bx\r\n\tpush ds\r\n\tlds bx,cs:[cmdptr]\r\n\tmov word ptr [bx.IODAT.status],8103h\r\n\tpop ds\r\n\tpop bx\r\n\tret\r\ndevint endp\r\n\r\n;--- restore XBDA to above low mem\r\n\r\nmyint19 proc\r\n\tpush ds\r\n\tpush 40h\r\n\tpop ds\r\n\tmov es,ds:[XBDAADR]\r\n\tmov di,ds:[MEMSIZE]\r\n\tshl di,6    ;convert KB to PARA\r\n\tmov cl,es:[XBDA._size]\r\n\tmov ch,0\r\n\tsub ds:[MEMSIZE],cx\r\n\tpush cx\r\n\tshl cx,6    ;convert KB to PARAs\r\n\tsub di,cx\r\n\tmov ds:[XBDAADR],di\r\n\tpush es\r\n\tpop ds\r\n\tmov es,di\r\n\tpop cx\r\n\txor di,di\r\n\txor si,si\r\n\tshl cx,9\t;convert KB to WORDs\r\n\trep movsw\r\n\tmov ds,cx   ;ds=0000\r\n\t.386\r\n\tmov eax, cs:[oldint19]\r\n\tmov ds:[19h*4],eax\r\n\tpop ds\r\n\tpush eax\r\n\t.286\r\n\tretf\r\nmyint19 endp\r\n\r\ncopyandexit:\r\n\trep movsw\r\n\tpop es\r\n\tpop ds\r\n\tpopa\r\n\tretf\r\n\r\nendres equ $\r\n\r\ndevintfirst proc far\r\n\tpusha\r\n\tpush ds\r\n\tpush es\r\n\tlds bx,cs:[cmdptr]\r\n\tmov al,[bx.IODAT.cmd]\r\n\tmov word ptr [bx.IODAT.status],100h\r\n\tcmp al,00\t\t\t;init call?\r\n\tjnz exit\r\nif ?WELCOME\r\n\tpush ds\r\n\tpush cs\r\n\tpop ds\r\n\tmov dx,offset dWelcome\r\n\tmov ah,9\r\n\tint 21h\r\n\tpop ds\r\nendif\r\n\tmov word ptr [bx].IODAT.trans+0,0000\r\n\tmov word ptr [bx].IODAT.trans+2,cs\r\n\tmov cs:[intproc], offset devint\r\n\r\n;--- scan for option /A\r\n\txor bp,bp\r\n\tlds si,dword ptr [bx].IODAT.count\r\n@@:\r\n\tlodsb\r\n\tcmp al,' '\r\n\tjnz @B\r\n@@:\r\n\tlodsb\r\n\tcmp al,' '\r\n\tjz @B\r\n\tmov ah,al\r\n\tlodsb\r\n\tor al,20h\r\n\tcmp ax,\"/a\"\t;/A option given?\r\n\tjnz @F\r\n\tinc bp      ;set flag for option /A\r\n@@:\r\n\r\n\tpush 40h\r\n\tpop es\r\n\tmov ax,es:[XBDAADR]\t;check if XBDA is just above low memory\r\n\tmov dx,es:[MEMSIZE]\r\n\tshl dx,6    ;convert KB to PARA\r\n\tcmp ax,dx\t;is xbda just above conv memory?\r\n\tjnz err1\t;if no, display msg and do nothing\r\n\tmov ds,ax\r\n\tmov al,ds:[XBDA._size]\r\n\tmov ah,0\r\n\r\n;--- get new XBDA address\r\n\tmov cx,offset endres\r\n\tadd cx,15\r\n\tand cl,0F0h\r\n\tshr cx,4\r\n\tmov dx,cs\r\n\tadd dx,cx\r\n\tcmp bp,0\r\n\tjz @F\r\n\tadd dx,3Fh\r\n\tand dx,0FFC0h\r\n@@:\r\n\r\n;--- adjust BIOS variables 040Eh and 0413h\r\n\r\n\tmov es:[XBDAADR],dx\r\n\tadd word ptr es:[MEMSIZE],ax\r\n\r\n\tpusha\r\n\r\n;--- increase the last DOS MCB\r\n\r\n\tmov ah,52h\r\n\tint 21h\r\n\tmov si,es:[bx-2]\r\nnextitem:\r\n\tmov es, si\r\n\tmov dx, es:[MCB._size]\t;get size of MCB\r\n\tinc dx\r\n\tadd dx, si\t\t;dx = next block\r\n\tcmp byte ptr es:[MCB.sig], 'M'\r\n\tjnz @F\r\n\tmov si, dx\r\n\tjmp nextitem\r\n@@:\r\n\tpopa\r\n\tmov cx,ax\r\n\tshl cx,6\t;convert kB to para\r\n\tadd es:[MCB._size],cx\r\n\r\n\tpush ds\r\n\r\n;--- tell DOS the amount of driver's memory to keep resident\r\n\r\n\tmov di,ax\r\n\tmov si,dx\r\n\r\n\tshl ax,10\t;convert kb to byte\r\n\tmov cx,cs\r\n\tsub dx,cx\r\n\tshl dx,4\r\n\tadd dx,ax\r\n\tlds bx,cs:[cmdptr]\r\n\tmov word ptr [bx].IODAT.trans+0,dx\r\n\r\n;--- hook int 19h\r\n\r\n\tpush 0\r\n\tpop ds\r\n\t.386\r\n\tpush cs\r\n\tpush offset myint19\r\n\tpop eax\r\n\txchg eax,ds:[19h*4]\r\n\tmov cs:[oldint19],eax\r\n\t.286\r\n\r\n\tpop ds\r\n\r\n\tmov es,si\r\n\tmov cx,di\r\n\r\n;--- move contents of XBDA\r\n;--- cx=size in kB\r\n;--- ds=old XBDA\r\n;--- es=new XBDA\r\n\r\n\txor si,si\r\n\txor di,di\r\n\tshl cx,9\t;convert KB to WORD\r\n\tjmp copyandexit\r\nexit:\r\n\tpop es\r\n\tpop ds\r\n\tpopa\r\n\tret\r\nerr1:\r\n\tmov dx,offset derr1\r\n\tjmp @F\r\nerr2:\r\n\tmov dx,offset derr2\r\n@@:\r\n\tpush cs\r\n\tpop ds\r\n\tmov ah,9\r\n\tint 21h\r\n\tjmp exit\r\ndevintfirst endp\r\n\r\nderr1 db \"XBDA not at the end of low memory\",13,10,'$'\r\nderr2 db \"XBDA too large\",13,10,'$'\r\ndWelcome db \"MOVEXBDA starting\",13,10,'$'\r\n\r\nstr1 db \"MOVEXBDA moves XBDA to low memory. Must be run before UMBs were provided to DOS.\",13,10\r\n\tdb \"Usage: DEVICE=MOVEXBDA.EXE [/A]\",13,10\r\n\tdb \"Optional parameter /A will align the XBDA to a kB boundary, which is\",13,10\r\n\tdb \"required by some BIOSes.\",13,10\r\n\tdb '$'\r\n\r\nmain:\r\n\tmov dx,offset str1\r\n\tpush cs\r\n\tpop ds\r\n\tmov ah,9\r\n\tint 21h\r\n\tmov ax,4C00h\r\n\tint 21h\r\n\r\n_TEXT ends\r\n\r\nSTACK segment stack 'STACK'\r\n\tdb 400h dup (?)\r\nSTACK ends\r\n\r\n\tEND main\r\n"
  },
  {
    "path": "Tools/UMBM/MAKE.BAT",
    "content": "@echo off\r\nrem BAT to create UMBM.EXE with JWasm\r\njwasm -mz -nologo -Fl -Sg UMBM.ASM\r\n"
  },
  {
    "path": "Tools/UMBM/UMBM.ASM",
    "content": "\r\n;***   UMBM is a DOS device driver, but will not install permanently:\r\n;***   if UMBs activated by UMBPCI are found, UMBM will check if\r\n;***   a XMS host is present. If yes, it will hook into the XMS chain\r\n;***   and add support for UMBs there. If no, it will install a micro\r\n;***   XMS host which just provides UMBs.\r\n;***   During the boot process, if line DOS=UMB is present in CONFIG.SYS,\r\n;**    DOS will query for UMBs every time a driver has been loaded. If UMBs\r\n;***   are provided, DOS will grab them. So UMBM's lifetime usually should be\r\n;***   very short. Once the UMBs are grabbed, UMBM will remove itself from\r\n;***   the XMS/Int 2Fh chain.\r\n;***   To not use any DOS memory, UMBM copies itself into the first UMB.\r\n;***   This assumes that DOS first allocates all available UMBs *before*\r\n;***   \"using\" them. All DOSes I know do so, but AFAIK this is not documented\r\n;***   and therefore a small risk remains. \r\n\r\n?MOVEINUMB equ 1        ;move in 1. umb\r\n?MOVEXBDA  equ 1        ;/XBDA option\r\n\r\n\t.286\r\n\t.MODEL SMALL\r\nDGROUP group _TEXT\r\n\t.STACK\r\n\t.dosseg\r\n\t.386\r\n\r\ncr\tequ 13\r\nlf\tequ 10\r\n\r\n;*** macros and structures ***\r\n\r\nIODAT   struc\r\ncmdlen  db      ?       ;+ 0    size of structure\r\nunit    db      ?       ;+ 1\r\ncmd     db      ?       ;+ 2\r\nstatus  dw      ?       ;+ 3\r\n        db      8 dup (?); reserved\r\nmedia   db      ?       ;+ 0d\r\ntrans   dd      ?       ;+ 0e\r\ncount   dw      ?       ;+ 12   on init:offset parameter line\r\nstart   dw      ?       ;+ 14   on init:segment parameter line\r\ndrive   db      ?       ;+ 16\r\nIODAT   ends\r\n\r\n;--- macro to define a string\r\n\r\nCStr macro text:vararg\r\nlocal xxxx\r\n\t.const\r\nxxxx db text,0\r\n\t.code\r\n\texitm <offset xxxx>\r\nendm\r\n\r\n;--- display a character on stdout\r\n\r\n@putchr macro char\r\n\tmov dl,char\r\n\tmov ah,2\r\n\tint 21h\r\n\tendm\r\n\r\n;--- display a 16-bit word on stdout\r\n\r\n@wordout macro arg\r\n\tifdif <arg>,<ax>\r\n\t\tmov ax,arg\r\n\tendif\r\n\tcall _wordout\r\nendm\r\n\r\n\t.CODE\r\n\r\n\tassume ds:DGROUP\r\n\r\n\tdw 0ffffh\r\n\tdw 0ffffh\r\n\tdw 8000h\t\t\t\t;attribute\r\n\tdw offset devstrat\t\t;device strategy\r\n\tdw offset devint\t\t;device interrupt\r\ndevname db 'UMBXXXX0'\t\t;device name\r\n\r\nbefptr  dd 1 dup (?)\r\n\r\nmemtab  label word\r\n\tdw 3 dup (0)\t\t\t;max. 4 regions (segm,curr size,init size)\r\n\tdw 3 dup (0)\r\n\tdw 3 dup (0)\r\n\tdw 3 dup (0)\r\n\tdw 0000\r\n\r\numbrou  proc far        ;XMS hook routine\r\n\tjmp umbr1 \t\t\t;must have this format\r\n\tnop\r\n\tnop\r\n\tnop\r\numbr1:\r\n\tcmp ah,10h\t\t\t;request umb?\r\n\tjz umb1\r\n\tcmp ah,11h\t\t\t;release umb?\r\n\tjz umb2\r\n\tdb 0EAh\r\noldvec\tdd 0\t\t\t;chain to previous handler\r\ndummyxmm::\r\n\txor ax,ax\r\n\tmov bl,80h\r\n\tretf\r\n\r\numb1:\t\t\t\t\t;request umb\r\n\tmov bx,offset memtab\r\n\tmov ax,0000\r\numb1b:\t\t\t\t\t\t;<==== check next block\r\n\tcmp dx,cs:[bx+2]\t\t;request block (DX=size in paras)\r\n\tja short umb1a \t\t\t;jmp if block too small\r\n\tcmp word ptr cs:[bx+2],0000\r\n\tjz umb1a3\r\n\tmov ax,cs:[bx+4]\t\t;original size\r\n\tsub ax,cs:[bx+2]\t\t;subtract rest size\r\n\tadd ax,cs:[bx+0]\t\t;+ start address => new start address\r\n\tsub cs:[bx+2],dx\t\t;size of block in DX\r\nif ?MOVEINUMB\r\n\tjnz umb1c\r\n\tcall deakt\r\numb1c:\r\nendif\r\n\tmov bx,ax\t\t\t\t\t;segment address of block\r\n\tmov ax,1\t\t\t\t\t;no error!\r\n\tret\r\numb1a:\r\n\tcmp ax,cs:[bx+2]\t\t\t;remember largest block in AX\r\n\tjnb @F\r\n\tmov ax,cs:[bx+2]\r\n@@:\r\n\tadd bx,6\r\n\tcmp word ptr cs:[bx],0\r\n\tjnz umb1b\r\n\tmov bl,0b0h \t\t\t\t;error: smaller UMB available\r\n\tmov dx,ax\t\t\t\t\t;still free space\r\n\tand dx,dx\r\n\tjnz umb1a2\r\numb1a3:\r\n\tmov bl,0b1h \t\t\t\t;error: no UMB free\r\numb1a2:\r\n\tmov ax,0\t\t\t\t\t;error\r\n\tret\r\n\r\numb2:\t\t\t\t\t\t\t;release umb\r\n\tmov bx,offset memtab\r\numb2b:\r\n\tcmp dx,cs:[bx+0]\r\n\tjc umb2a1\r\n\tmov ax,cs:[bx+4]\r\n\tadd ax,cs:[bx+0]\r\n\tcmp dx,ax\r\n\tjnc umb2a\r\n\tsub ax,dx\r\n\tmov cs:[bx+2],ax\r\n\tmov ax,1\r\n\tret\r\numb2a:\r\n\tadd bx,6\r\n\tcmp word ptr cs:[bx],0\r\n\tjnz umb2b\r\numb2a1:\r\n\tmov bl,0b2h \t\t\t\t;error: invalid UMB segment address\r\n\tmov ax,0\r\n\tret\r\numbrou endp\r\n\r\ndeakt:\r\n\tpush ds\t\t\t\t\t\t;restore xms vector\r\n\tlds bx,cs:[oldvec]\t\t\t;thus no space is needed at all\r\n\tpush eax\r\n\tmov eax,dword ptr cs:[umbrou]\r\n\tmov [bx-5],eax\r\n\tmov al,byte ptr cs:[umbrou+4]\r\n\tmov [bx-1],al\r\n\tmov ax,word ptr cs:[oldint2f+2]\r\n\tand ax,ax\r\n\tjz @F\r\n\tpush dx\r\n\tmov dx,word ptr cs:[oldint2f+0]\r\n\tmov ds,ax\r\n\tmov ax,252fh\r\n\tint 21h\r\n\tpop dx\r\n@@:\r\n\tpop eax\r\n\tpop ds\r\n\tret\r\n\r\nmyint2f::\r\n\tcmp ax,4300h\r\n\tjz int4300\r\n\tcmp ax,4310h\r\n\tjz int4310\r\n\tdb 0eah\t;jmp far16 [oldint2f]\r\noldint2f dd 0\r\nint4300:\r\n\tor al,80h\r\n\tiret\r\nint4310:\r\n\tmov bx,offset umbrou\r\n\tpush cs\r\n\tpop es\r\n\tiret\r\n\r\nendres equ $\r\n\r\n;*** end resident part ***\r\n\r\ndevstrat proc far\r\n\tmov word ptr cs:[befptr+0],bx\r\n\tmov word ptr cs:[befptr+2],es\r\n\tret\r\ndevstrat endp\r\n\r\ndevint proc far\r\n\tpusha\r\n\tpush ds\r\n\tpush es\r\n\tlds bx,cs:[befptr]\r\n\tmov word ptr [bx.IODAT.status],100h\r\n\tmov al,[bx.IODAT.cmd]\r\n\tcmp al,00\t\t\t;init?\r\n\tjnz devi1\r\n\tpush bx\r\n\tpush ds\r\n\tcall ignname \t\t;skip device driver name\r\n\tcall main\t\t\t;init\r\n\tpop ds\r\n\tpop bx\r\n\tmov word ptr [bx.IODAT.trans+0],0000\r\n\tmov word ptr [bx.IODAT.trans+2],ax\r\n\tjmp devi2\r\ndevi1:\r\ndevi2:\r\n\tpop es\r\n\tpop ds\r\n\tpopa\r\n\tret\r\ndevint endp\r\n\r\nignname proc near\t\t\t;skip device driver name\r\n\tmov es,[bx.IODAT.start]\r\n\tmov si,[bx.IODAT.count]\r\n\tdec si\r\nignn1:\r\n\tinc si\r\n\tcmp byte ptr es:[si],' '\r\n\tjz ignn1\r\n\tdec si\r\nignn2:\r\n\tinc si\r\n\tcmp byte ptr es:[si],' '\r\n\tjnz ignn2\r\n\tret\r\nignname endp\r\n\r\n;--- display a nul-terminated string\r\n;--- modifies ax\r\n\r\n_strout proc stdcall uses dx si pStr:word\r\n\tmov si,pStr\r\nnextitem:\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\tmov dl,al\r\n\tmov ah,2\r\n\tint 21h\r\n\tjmp nextitem\r\ndone:\r\n\tret\r\n_strout endp\r\n\r\n;--- display value in AX in hex\r\n;--- preserves all registers except AX\r\n\r\n_wordout proc\r\n\r\n\tpush ax\r\n\tmov al,ah\r\n\tcall byteout\r\n\tpop ax\r\nbyteout:\r\n\tpush ax\r\n\tshr al,4\r\n\tcall nibout\r\n\tpop ax\r\nnibout:\r\n\tand al,0Fh\r\n\tcmp al,10\r\n\tsbb al,69H\r\n\tdas\r\n\tpush dx\r\n\tmov dl,al\r\n\tmov ah,2\r\n\tint 21h\r\n\tpop dx\r\n\tret\r\n_wordout endp\r\n\r\n;--- es:si = cmdline\r\n\r\ngetpar proc\r\nnextitem:\r\n\tmov al,es:[si]\r\n\tcmp al,cr\r\n\tjz exit\r\n\tcmp al,lf\r\n\tjz exit\r\n\tcmp al,20h\r\n\tjz getpar_1\r\n\tor al,20h\r\n\tcall getregion\r\n\tjc exit\r\n\tjmp nextitem\r\ngetpar_1:\r\n\tinc si\r\n\tcmp byte ptr es:[si],' '\r\n\tjz getpar_1 \r\n\tjmp nextitem\r\nexit:\r\n\tret\r\ngetpar endp\r\n\r\nmakecaps proc\r\n\tcmp al,'a'\r\n\tjb iscaps\r\n\tcmp al,'z'\r\n\tja iscaps\r\n\tand al,not 20h\r\niscaps:\r\n\tret\r\nmakecaps endp\r\n\r\nishex proc\r\n\tcmp al,'0'\r\n\tjb @@no\r\n\tcmp al,'9'\r\n\tjbe @@yes\r\n\tcmp al,'A'\r\n\tjb @@no\r\n\tcmp al,'F'\r\n\tjbe @@yes2\r\n@@no:\r\n\tstc\r\n\tret\r\n@@yes2:\r\n\tsub al,7\r\n@@yes:\r\n\tsub al,'0'\r\n\tclc\r\n\tret\r\nishex endp\r\n\r\ngethexnumber proc\r\n\tpush dx\r\n\tmov ch,00\r\n\tmov dx,0000\r\ngethex2:\r\n\tmov al,es:[si]\r\n\tcall makecaps\r\n\tcall ishex\r\n\tjc gethex1\r\n\tinc ch\r\n\tmov ah,00\r\n\tshl dx,4\r\n\tadd dx,ax\r\n\tinc si\r\n\tjmp gethex2\r\ngethex1:\r\n\tcmp ch,1   ;null digits -> invalid\r\n\tmov ax,dx\r\n\tpop dx\r\n\tret\r\ngethexnumber endp\r\n\r\n;--- inp: es:[si] -> cmdline\r\n;--- ds:[di] -> memtab\r\n\r\ngetregion proc uses bx\r\n\tcmp byte ptr es:[si],'/'\t;allow the \"/I=\" prefix\r\n\tjnz @F\r\n\tmov eax,es:[si+1]\r\n\tor eax,20202020h\r\nif ?MOVEXBDA\r\n\tcmp eax,'adbx'\r\n\tjz is_xbda\r\nendif\r\n\tcmp al,'i'\r\n\tjnz @F\r\n\tcmp byte ptr es:[si+2],'='\r\n\tjnz @F\r\n\tadd si,3\r\n@@:\r\n\tcall gethexnumber\r\n\tjc getregion_er\r\n\tcmp al,00h\r\n\tjnz getregion_er\r\n\tmov bx,ax\r\n\tmov al,es:[si]\r\n\tcmp al,'-'\r\n\tjnz getregion_er\r\n\tinc si\r\n\tcall gethexnumber\r\n\tjc getregion_er\r\n\tinc ax\r\n\tcmp al,00h\r\n\tjnz getregion_er\r\n\tsub ax,bx\r\n\tjbe getregion_er\r\n\tmov [di+0],bx\r\n\tmov [di+2],ax\r\n\tmov [di+4],ax\r\n\tadd di,6\r\n\tclc\r\n\tret\r\nif ?MOVEXBDA\r\nis_xbda:\r\n\tinc bp\r\n\tadd si,5\r\n\tclc\r\n\tret\r\nendif\r\ngetregion_er:\r\n\tstc\r\n\tret\r\ngetregion endp\r\n\r\n;--- search for UMBPCI blocks in upper memory\r\n;--- DI->memtab\r\n\r\nMCB struct\r\nsig   db ?\r\npsp   dw ?\r\n_size dw ?\r\nMCB ends\r\n\r\nsearchumbpciblocks proc\r\n\tmov si,0C800h\r\n\t.while si < 0F000h\r\n\t\tmov es,si\r\n\t\tcmp dword ptr es:[0],\"BMU$\"\r\n\t\tjnz noblock\r\n\t\tcmp dword ptr es:[4],\"!lbT\"\r\n\t\tjnz noblock\r\n\t\tmov bx,8\r\n\t\t.while word ptr es:[bx]\r\n\t\t\tmov ax,es:[bx] ;start of (first) block\r\n\t\t\tmov [di+0],ax\r\n\t\t\tmov ax,es:[bx+2];size of block in paras\r\n\t\t\tmov [di+2],ax\r\n\t\t\tmov [di+4],ax\r\n\t\t\tadd di,3*2\r\n\t\t\tadd bx,2*2\r\n\t\t\t.break .if di == offset memtab+4*3*2\r\n\t\t.endw\r\n\t\t.break\r\nnoblock:\r\n\t\tadd si,100h\r\n\t.endw\r\ndone:\r\n\tret\r\nsearchumbpciblocks endp\r\n\r\nif ?MOVEXBDA\r\nmovexbda proc\r\n\tpush 40h\r\n\tpop es\r\n\tmov dx,es:[0013h]\r\n\tshl dx,6\t\t;convert kB to para\r\n\tmov ax,es:[000Eh]\r\n\tcmp ax,dx\r\n\tjnz dontmove\r\n\t\r\n\tmov es,ax\r\n\tmovzx ax,byte ptr es:[0]\t;size of xbda in kb\r\n\tshl ax,6\t\t;size in paras\r\n\tcmp ax,memtab+2\r\n\tja done\r\n\tmov dx,memtab+0\r\n\tadd memtab+0,ax\r\n\tsub memtab+2,ax\r\n\tsub memtab+4,ax\r\n\tpush ds\r\n\tpush es\r\n\tpop ds\r\n\tmov es,dx\r\n\tmov cx,ax\r\n\tshl cx,3\r\n\txor si,si\r\n\txor di,di\r\n\trep movsw\r\n\tmov di,ax\r\n\tpush 40h\r\n\tpop es\r\n\tmov es:[000eh],dx\r\n\tmov ax,ds\r\n\tpop ds\r\n\r\n;--- increase the last DOS MCB\r\n\r\n\tmov ah,52h\r\n\tint 21h\r\n\tmov si,es:[bx-2]\r\nnextitem:\r\n\tmov es, si\r\n\tmov dx, es:[MCB._size]\t;get size of MCB\r\n\tinc dx\r\n\tadd dx, si\t\t;dx = next block\r\n\tcmp byte ptr es:[MCB.sig], 'M'\r\n\tjnz @F\r\n\tmov si, dx\r\n\tjmp nextitem\r\n@@:\r\n\tadd es:[MCB._size],di\r\n\r\n;--- increase bios conv memory size\r\n\r\n\tpush 40h\r\n\tpop es\r\n\tshr di,6\r\n\tadd es:[0013h],di\r\n\r\n\tinvoke _strout, CStr(\"UMBM: XBDA moved to \")\r\n\t@wordout word ptr es:[000Eh]\r\n\tinvoke _strout, CStr(cr,lf)\r\ndone:\r\n\tret\r\ndontmove:\r\n\tinvoke _strout, CStr(\"UMBM: XBDA has already been moved - nothing done\",cr,lf)\r\n\tret\r\n\r\nmovexbda endp\r\nendif\r\n\r\n;--- called as device driver\r\n;--- check cmdline and get the regions to add as UMBs\r\n;--- test each region if it really contains RAM\r\n;--- es:di = cmdline\r\n\r\nmain proc near\r\n\r\n\tpush cs\r\n\tpop ds\r\n\r\n\txor bp,bp\r\n\tmov di,offset memtab\r\n\tcall getpar\r\n\tjc mainex\r\n\tcmp memtab,0  ;any valid /I= option given?\r\n\tjnz @F\r\n\tcall searchumbpciblocks\r\n@@:\r\n\tmov bx,offset memtab\r\n\tmov ax,[bx]\r\n\tand ax,ax\r\n\tjz mainex\r\n\tinvoke _strout, CStr(\"UMBM: Upper Memory Blocks: \")\r\nmn1:\t\t\t\t\t\t;<----\r\n\t@wordout [bx]\r\n\t@putchr '-'\r\n\tmov ax,[bx]\r\n\tadd ax,[bx+2]\r\n\tdec ax\r\n\t@wordout ax\r\n\tmov dx,[bx+0]\r\n\tmov cx,[bx+2]\t;size in paragraphs\r\n\tshr cx,8\r\n\tjcxz testdone\r\nnext4k: \r\n\tpush ds\r\n\tpushf\r\n\tcli\r\n\tmov ds,dx\r\n\tmov ax,05555h\r\n\txor si,si\r\n\txchg ax,[si]\r\n\txor word ptr [si],0FFFFh\r\n\txchg ax,[si]\r\n\tpopf\r\n\tpop ds\r\n\tcmp ax,0AAAAh\r\n\tjz @F\r\n\tinvoke _strout, CStr(\" - no RAM found at \")\r\n\t@wordout dx\r\n\tinvoke _strout, CStr(\". Aborted!\",cr,lf)\r\n\tjmp mainex\r\n@@:\r\n\tadd dx,100h\r\n\tloop next4k\r\ntestdone:\r\n\r\n\t@putchr ' '\r\n\tadd bx,6\r\n\tcmp word ptr [bx],0\r\n\tjnz mn1\r\n\tinvoke _strout, CStr(cr,lf)\r\n\r\nif ?MOVEXBDA\r\n\tand bp,bp\r\n\tjz @F\r\n\tcall movexbda\r\n@@:\r\nendif\r\n\r\n;--- check if an XMM is active\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\txor bp,bp\r\n\ttest al,80h\t\t\t;does xms host exist?\r\n\tjnz @F\r\n\tmov ax,352Fh\r\n\tint 21h\r\n\tmov word ptr oldint2f+0,bx\r\n\tmov word ptr oldint2f+2,es\r\n\tmov word ptr [oldvec+0],offset dummyxmm\r\nif ?MOVEINUMB\r\n\tmov ax,[memtab]\r\nelse\r\n\tmov ax,cs\r\nendif\r\n\tmov word ptr [oldvec+2],ax\r\n\tdec bp\r\n\tjmp main_1\r\n@@:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov ax,es:[bx]\r\n\tcmp ax,03ebh\t\t;should begin with a jmp short $+5\r\n\tjz  @F\r\n\tinvoke _strout, CStr(\"UMBM: cannot hook into XMS chain\",cr,lf)\r\n\tjmp mainex\r\n@@:\r\n\tcli\r\n\tmov ax,offset umbrou\r\nif ?MOVEINUMB\r\n\tmov cx,[memtab] \t;hook into XMS chain\r\nelse\r\n\tmov cx,cs\r\nendif\r\n\tmov byte ptr es:[bx+0],0eah ;chain\r\n\tmov es:[bx+1],ax\r\n\tmov es:[bx+3],cx\r\n\tadd bx,5\r\n\tmov word ptr [oldvec+0],bx\r\n\tmov word ptr [oldvec+2],es\r\n\tsti\r\nmain_1:\r\nif ?MOVEINUMB\r\n;\tinvoke _strout, CStr(\"UMBM: copy myself in 1. UMB\",cr,lf)\r\n\tmov es,[memtab]\r\n\txor di,di\r\n\txor si,si\r\n\tmov cx,offset endres\r\n\trep movsb\r\n\tmov ax,cs\t\t   ;end address\r\nelse\r\n\tmov bx,offset endres\r\n\tshr bx,4\r\n\tinc bx\r\n\tmov ax,cs\r\n\tadd ax,bx\t\t\t;num paragraphs\r\nendif\r\n\t\t\t\t\t\t;if no XMM found, install XMM\r\n\tand bp,bp\r\n\tjz @F\r\n\tpush ds\r\n\tpush ax\r\n\tinvoke _strout, CStr(\"UMBM: XMS host not found, installing XMS\",cr,lf)\r\n\tmov dx,offset myint2f\r\nif ?MOVEINUMB\r\n\tmov ds,[memtab]\r\nelse\r\n\tpush cs\r\n\tpop ds\r\nendif\r\n\tmov ax,252fh\r\n\tint 21h\r\n\tpop ax\r\n\tpop ds\r\n@@:\r\n\tret\r\nmainex:\r\n\tmov ax,cs\r\n\tret\r\nmain endp\r\n\r\nexplain proc\r\n\tpush cs\r\n\tpop ds\r\n\tmov dx, offset dHowTo\r\n\tmov ah, 9\r\n\tint 21h\r\n\tret\r\nexplain endp\r\n\r\n;*** entry if loaded from command line\r\n\r\nmain_exe proc c\r\n\r\n\tcall explain\r\n\tmov ax,4c00h\r\n\tint 21h\r\n\r\nmain_exe endp\r\n\r\ndHowTo  label byte\r\n\tdb \"UMBM is assumed to be located behind UMBPCI in CONFIG.SYS,\",cr,lf\r\n\tdb \"and before the XMS driver. This will allow to load the XMS driver\",cr,lf\r\n\tdb \"(and the EMM) into an UMB, thus saving some conventional DOS memory.\",cr,lf\r\n\tdb cr,lf\r\n\tdb \"UMBM knows the following options:\",cr,lf\r\n\tdb \"  /I=XXXX-YYYY   force region XXXX-YYYY to be included. Example: /I=D000-DBFF.\",cr,lf\r\n\tdb \"  /XBDA          move the XBDA to the first UMB\",cr,lf\r\n\tdb \"Option /I is not recommended, since UMBM will automatically find regions\",cr,lf\r\n\tdb \"activated by UMBPCI. Option /XBDA may cause troubles if the upper memory\",cr,lf\r\n\tdb \"can't be used by DMA. To find out, there's no other way than to try.\",cr,lf\r\n\tdb \"Example:\",cr,lf\r\n\tdb \"DOS=UMB\",cr,lf\r\n\tdb \"DEVICE=UMBPCI.SYS\",cr,lf\r\n\tdb \"DEVICE=UMBM.EXE\",cr,lf\r\n\tdb \"DEVICEHIGH=HIMEMX.EXE\",cr,lf\r\n\tdb \"After DOS has grabbed the UMBs, UMBM will remove itself from DOS memory.\",cr,lf\r\n\tdb cr,lf\r\n\tdb \"UMBM is Public Domain. Japheth.\",cr,lf\r\n\tdb '$'\r\n\r\n\tEND main_exe\r\n"
  },
  {
    "path": "Tools/VCPI/MAKE.BAT",
    "content": "@echo off\r\nrem creates VCPI.EXE with JWasm\r\njwasm -mz -nologo -Fl -Sg VCPI.ASM\r\n"
  },
  {
    "path": "Tools/VCPI/PRINTF.INC",
    "content": "\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n\r\nltob PROC stdcall uses edi edx number:dword, outb:ptr, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- hiword(eax) is modified if numbers are rendered!\r\n\r\nprintf PROC c fmt:ptr, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tpusha\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\tpopa\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\n\t.while byte ptr [si] >= '0' && byte ptr [si] <= '9'\r\n\t\tlodsb\r\n\t\tsub al,'0'\r\n\t\tcbw\r\n\t\timul cx,bx,10\t\t;cx = bx * 10\r\n\t\tadd ax,cx\r\n\t\tmov bx,ax\r\n\t.endw\r\n\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,0\r\n\tjnz @@L359\r\n\tpop ax\r\n\tjmp done\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nhandle_char:\r\n\tcmp al,lf\r\n\tjnz @F\r\n\tmov al,cr\r\n\tcall @F\r\n\tmov al,lf\r\n@@:\r\n\tmov dx, cs\r\n\tcmp dx, CSR0\r\n\tjnz @F\r\n\tcall VioPutChar\r\n\tretn\r\n@@:\r\n\tmov dl, al\r\n\tmov ah, 2\r\n\tint 21h\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Tools/VCPI/VCPI.ASM",
    "content": "\r\n;*** display VCPI information\r\n;*** and use VCPI to switch to protected mode and back\r\n\r\n;--- if segments are defined BEFORE .model,\r\n;--- the alignment can be set differently.\r\n_DATA segment para public 'DATA'\r\n_DATA ends\r\n_BSS segment para public 'BSS'\r\n_BSS ends\r\n\r\n\t.286\r\n\t.model small\r\n\t.stack 2048\r\n\t.dosseg\r\n\t.386P\r\n\toption casemap:none\r\n\r\n?ENTRIES equ 110h\t;PTE entries to display with option -p\r\n?386SWAT equ 0\t\t;1=support 386SWAT interface (doesn't work!)\r\n?DEB386  equ 1\t\t;1=support (w)deb386 interface\r\n?DISPEXC equ 0\t\t;display exceptions in ring 0\r\n\r\n\tinclude vcpi.inc\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\nif ?DEB386\r\nD386_Identify       equ 43h\t; returns debugger identification\r\nD386_Prepare_PMode  equ 44h\t; partially prepare for protected mode operation\r\nD386_Real_Mode_Init equ 45h\t; tell kd we're done\r\nPMINIT_INIT_IDT     equ 0\t; (ES:EDI) = pointer to PM IDT\r\nD386_Id             equ 0F386h ; debugger identification code\r\nendif\r\n\r\nlf\tequ 10\r\ncr\tequ 13\r\n\r\n\t.data\r\n\r\nmygdt label byte                ;GDT\r\n        db 1*8 dup (0)          ;+00h null descriptor\r\nrestab  db 3*8 dup (0)          ;+08h descriptors reserved for vcpi host\r\npmcs    desc <0ffffh,0,0,9Ah,0,0>\t;+20h\r\npmds    desc <0ffffh,0,0,92h,0,0>\t;+28h\r\npmtr    desc <068h-1,0,0,89h,0,0> ;selector for TR\t;+38h\r\npmflat  desc <0ffffh,0,0,92h,8fh,0>\t;+30h\r\nif ?386SWAT or ?DEB386\r\nkddesc label dword\t\t\t;+48\r\n if ?386SWAT\r\n\t\tdb 30*8 dup (0)\t\t\t; 386SWAT needs 30 descriptors free!?\r\n else\r\n\t\tdb 3*8 dup (0)\r\n endif\r\nKDSEL   equ kddesc - mygdt\r\nendif\r\nSIZGDT\tequ $      - mygdt\r\n\r\nCSR0\tequ pmcs   - mygdt\r\nDSR0\tequ pmds   - mygdt\r\nTRSEL \tequ pmtr   - mygdt\r\nFLATSEL\tequ pmflat - mygdt\r\nHOSTCS\tequ restab - mygdt\r\n\r\npdgdt    label fword\r\n         dw SIZGDT-1            ;limit GDTR\r\nbasegdt  dd offset mygdt        ;base  GDTR\r\n\r\npdidt    label fword\r\n         dw sizeof myidt-1      ;limit IDTR\r\nbaseidt  dd offset myidt        ;base  IDTR\r\n\r\n;--- far32 address of VCPI protected-mode API\r\n\r\nvcpiv   label fword\r\nvcpiofs dd 0\r\n\t\tdw HOSTCS\t\t;selector for VCPI host code segment\r\n\t\tdw 0\r\n\r\nmsw V86toPM <0, offset pdgdt, offset pdidt, 0, TRSEL, offset pmentry, CSR0>\r\n\r\ndwPage\tdd 0\t\t;page to release / allocated\r\ndwFreePages dd 0\t;number free pages\r\ndwNumPages dd 1\t\t;no of pages to alloc / free\r\ndwAlloced dd 0\t\t;pages allocated\r\ndwFreed dd 0\t\t;pages freed\r\n\r\nhandle  dw 0\t\t;handle EMS \r\nsegcs   dw 0\t\t;segment CS\r\nsegds   dw 0\t\t;segment DS\r\nptadr   dw 0\t\t;segment page table 0\r\nvcpiend dw 0\t\t;offset in pagetab 0 where free space begins\r\n\r\nflags   db 00\r\n\r\nUFLAG\tequ 1\t;option u - display PTEs for UMBs\r\nNFLAG\tequ 2\t;option n - no switch to pmode\r\nQFLAG\tequ 4\t;option q - query pages in protected-mode\r\nPFLAG\tequ 8\t;option p - display PTEs for conv. memory\r\nFFLAG\tequ 16\t;option f - free pages in pmode\r\nAFLAG\tequ 32\t;option a - alloc pages in pmode\r\nRFLAG\tequ 64\t;option r - free pages in rmode\r\n\r\nbRet\tdb 00\r\nbNoDisp\tdb 00\r\n\r\nif ?386SWAT or ?DEB386\r\nKD_386SWAT equ 1\r\nKD_DEB386  equ 2\r\nbKrnlDbg db 0\r\ndfDbgEntry df 0\r\nendif\r\n\r\n\t.data?\r\n\r\nmyidt   db 100h*8 dup (?)\r\nptab    db 1000h dup (?)\r\ntaskseg TSSSEG <>   ;task state segment\r\ndrtab\tdd 8 dup (?)\r\n\r\n\t.code\r\n\r\n\tinclude vioout.inc\r\n\tinclude printf.inc\r\n\r\nprintln proc\r\n\tinvoke printf, CStr(10)\r\n\tret\r\nprintln endp\r\n\r\nif ?DISPEXC\r\n\r\n;------ FEDCBA9876543210   FEDCBA9876543210\r\nexcv dw 0111110100000000b, 0000000000000110b\r\n\r\n?EXC = 0\r\n\r\n@defexc macro\r\n\tpush ?EXC\r\n\tjmp defexc\r\n?EXC = ?EXC + 1\r\nendm\r\n\r\nexceptions:\r\n\trept 32\r\n\t@defexc\r\n\tendm\r\n\r\nEXCFRAME1 struct\r\ndwEbp dd ?\r\nwExc  dw ?,?\r\ndwErr dd ?\r\ndwEip dd ?\r\nwCs   dw ?,?\r\ndwEfl dd ?\r\nEXCFRAME1 ends\r\n\r\nEXCFRAME2 struct\r\ndwEbp dd ?\r\nwExc  dw ?,?\r\ndwEip dd ?\r\nwCs   dw ?,?\r\ndwEfl dd ?\r\nEXCFRAME2 ends\r\n\r\ndefexc:\r\n\tpush ebp\r\n\tmov ebp,esp\r\n\tmov ax, [ebp].EXCFRAME1.wExc\r\n\tbt cs:[excv], ax\r\n\tjnc @F\r\n\tinvoke printf, CStr(\"exc %X at %X:%lX, errc=%lX\",lf), [ebp].EXCFRAME1.wExc, [ebp].EXCFRAME1.wCs, [ebp].EXCFRAME1.dwEip, [ebp].EXCFRAME1.dwErr\r\n\tjmp de2\r\n@@:\r\n\tinvoke printf, CStr(\"exc %X at %X:%lX\",lf), [ebp].EXCFRAME2.wExc, [ebp].EXCFRAME2.wCs, [ebp].EXCFRAME2.dwEip\r\nde2:\r\n\tjmp $\t; just stop, we cannot continue\r\n\r\nendif\r\n\r\ndefint proc near\r\n\tpush eax\r\n\tmov al,0Bh\r\n\tout 0A0h,al\r\n\tin al,0A0h\r\n\tand al,al\r\n\tjz @F\r\n\tmov al,20h\r\n\tout 0a0h,al\r\n@@:\r\n\tmov al,0Bh\r\n\tout 20h,al\r\n\tin al,20h\r\n\tand al,al\r\n\tjz @F\r\n\tmov al,20h\r\n\tout 20h,al\r\n@@:\r\n\tpop eax\r\n\tiretd\r\ndefint endp\r\n\r\n;--- get descriptor values in EAX (base), DX (limit), CX (attr)\r\n\r\ngetdesc proc near\r\n\tmov ah,[di+7]\t\t;base bits 31..24\r\n\tmov al,[di+4]\t\t;base bits 23..16\r\n\tshl eax,16\r\n\tmov ax,[di+2]\t\t;base bits 15..0\r\n\tmov dx,[di+0]\t\t;limit bits 15..0\r\n\tmov cx,[di+5]\r\n\tret\r\ngetdesc endp\r\n\r\ngethex proc\r\n\txor edx, edx\r\n\tmov ch,0\r\nnextitem:\r\n\tmov al,es:[si]\r\n\tcmp al,'0'\r\n\tjb done\r\n\tcmp al,'9'\r\n\tjbe @F\r\n\tor al,20h\r\n\tcmp al,'a'\r\n\tjb done\r\n\tcmp al,'f'\r\n\tja done\r\n\tsub al,27h\r\n@@:\r\n\tsub al,'0'\r\n\tmovzx eax,al\r\n\tshl edx, 4\r\n\tadd edx, eax\r\n\tinc ch\r\n\tinc si\r\n\tjmp nextitem\r\ndone:\r\n\tmov eax, edx\r\n\tcmp ch,1\r\n\tret\r\ngethex endp\r\n\r\ngetdec  proc\r\n\txor edx, edx\r\n\tmov ch,0\r\nnextitem:\r\n\tmov al,es:[si]\r\n\tcmp al,'0'\r\n\tjb done\r\n\tcmp al,'9'\r\n\tja done\r\n\tsub al,'0'\r\n\tmovzx eax,al\r\n\tshl edx, 1\r\n\tlea edx, [edx+edx*4]\r\n\tadd edx, eax\r\n\tinc ch\r\n\tinc si\r\n\tjmp nextitem\r\ndone:\r\n\tmov eax, edx\r\n\tcmp ch,1\r\n\tret\r\ngetdec  endp\r\n\r\nEMScheck proc\r\n\r\n\tpush ds\r\n\tpush si\r\n\tpush di\r\n\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tpush ds\r\n\tpop es\r\n\r\n\tmov di,000ah\r\n\tpush cs\r\n\tpop ds\r\n\tmov si,offset emsstr\r\n\tmov cx,8\r\n\trepz cmpsb\r\n\tmov ax,1\r\n\tjz @F\t\t\t   ;1 = EMS exists\r\n\tdec ax\t\t\t   ;0 = EMS doesn't exist\r\n@@:\r\n\tpop di\r\n\tpop si\r\n\tpop ds\r\n\tret\r\nEMScheck endp\r\n\r\nemsstr  db 'EMMXXXX0'\r\n\r\n;--- display UMBs\r\n\r\nDispUMBs proc uses es si\r\n\r\n\tnop ;MASM bug\r\n\tinvoke printf, CStr(\"Pages used for UMBs:\",lf)\r\n\tmov es,[ptadr]\r\n\tmov si,0000\r\n\tmov cx,100h\r\n\txor edx,edx\r\nnextitem:\r\n\tpush cx\r\n\tpush edx\r\n\tmov eax,es:[si]\r\n\tand ax,0F000h\r\n\tcmp eax,edx\r\n\tjz @F\r\n\tmov eax, edx\r\n\tshr eax,4\r\n\tinvoke printf,CStr(\"%04X \"), ax\r\n@@:\r\n\tpop edx\r\n\tadd edx,1000h\r\n\tadd si,4\r\n\tpop cx\r\n\tloop nextitem\r\n\tinvoke println\r\n\tret\r\nDispUMBs endp        \r\n\r\n;--- display PTEs of 1. MB\r\n\r\nDispPTEs proc uses es si\r\n\r\n\tnop ;MASM bug\r\n\tinvoke printf, CStr(\"Paging Table\",lf)\r\n\tmov es,[ptadr]\r\n\tmov si,0000\r\n\tmov cx,?ENTRIES\r\nnextitem:\r\n\tpush cx\r\n\ttest si,01Fh\r\n\tjnz @F\r\n\tmov ax,si\r\n\tshr ax,2\r\n\tinvoke printf, CStr(\"%04X: \"), ax\r\n@@:\r\n\tmov eax,es:[si+0]\r\n\tpush ax\r\n\tinvoke printf,CStr(\"%8lX \"),eax\r\n\tpop ax\r\n\tand ax,0FFE7h\t\t;???\r\n\tmov es:[si+0],ax\t;???\r\n\tadd si,4\r\n\ttest si,1Fh\r\n\tjnz @F\r\n\tinvoke println\r\n@@:\r\n\tpop cx\r\nif 1\r\n\tcmp si, vcpiend\r\n\tjb nextitem\r\nelse\r\n\tloop nextitem\r\nendif\r\n\tret\r\nDispPTEs endp\r\n\r\n;--- fill VCPI comm structure\r\n;--- set descriptors and IDT\r\n\r\nsetdescriptors proc\r\n\tmov ax,cs\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tmov pmcs.A0015,ax\r\n\tshr eax,16\r\n\tmov pmcs.A1623,al\r\n\r\n\tmov ax,ds\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tadd basegdt, eax\r\n\tadd baseidt, eax\r\n\tadd msw._Gdtr, eax\r\n\tadd msw._Idtr, eax\r\n\r\n\tmov pmds.A0015,ax\r\n\tshr eax,16\r\n\tmov pmds.A1623,al\r\n\r\n\tmov ax,ds\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tadd eax, offset taskseg\r\n\tmov pmtr.A0015,ax\r\n\tshr eax,16\r\n\tmov pmtr.A1623,al\r\n\tmov dword ptr taskseg.dfStk0, esp\r\n\tmov word ptr taskseg.dfStk0+4, DSR0\r\n\r\n\tmov bx,0\r\n\tmov di,offset myidt\r\nif ?DISPEXC\r\n\tmov ax,offset exceptions\r\nnextitem:\r\n\tmov word ptr [di+0],ax\r\n\tmov word ptr [di+2],CSR0\r\n\tmov word ptr [di+4],0EE00h\r\n\tmov word ptr [di+6],0\r\n\tadd di,8\r\n\tadd ax,4\r\n\tinc bl\r\n\tcmp bl,20h\r\n\tjnz nextitem\r\nendif\r\nnextitem2:\r\n\tmov word ptr [di+0],offset defint\r\n\tmov word ptr [di+2],CSR0\r\n\tmov word ptr [di+4],0EE00h\r\n\tmov word ptr [di+6],0\r\n\tadd di,8\r\n\tinc bl\r\n\tjnz nextitem2\r\n\tret\r\nsetdescriptors endp\r\n\r\n;--- switch to protected-mode and back to v86-mode throu VCPI\r\n\r\npmgo proc\r\n\r\nif ?386SWAT\r\n\tcmp bKrnlDbg, KD_386SWAT\r\n\tjnz no386swat2\r\n\tpush ds\r\n\tpop es\r\n\tmov di, offset kddesc\r\n\tmov bx, KDSEL\r\n\tmov ax, 0DEF2h\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjnz no386swat2\r\n\tmov dword ptr [dfDbgEntry+0], edx\r\n\tmov  word ptr [dfDbgEntry+4], bx\r\n\r\n\tmov cx, 10h\r\n\tmov bx, 0\r\n\tmov di, offset myidt\r\n@@:\r\n\tmov ax, 0DEF3h\r\n\tint 67h\r\n\tinc bx\r\n\tadd di, 8\r\n\tloop @B\r\nno386swat2:\r\nendif\r\n\r\n\tcli\r\n\r\n\tmov [segcs],cs\r\n\tmov [segds],ds\r\n\r\n\tmov ax, ds\r\n\tmovzx eax, ax\r\n\tshl eax, 4\r\n\tlea esi, [eax + offset msw]\r\n\r\n\tmovzx ebx,sp\r\n\r\n\tmov ax,0DE0Ch\r\n\tint 67h\r\npmentry::\t\t\t\t\t\t;now in protected mode\r\n\tmov ax,DSR0\r\n\tmov ss,ax\r\n\tmov esp,ebx\r\n\tmov ds,ax\r\n\tmov ax,FLATSEL\t\t;4G selector -> ES\r\n\tmov es,ax\r\n\r\nif ?386SWAT\r\n\tcmp bKrnlDbg,KD_386SWAT\r\n\tjnz @F\r\n\tint 3\r\n@@:\r\nendif\r\nif ?DEB386\r\n\tcmp bKrnlDbg,KD_DEB386\r\n\tjnz @F\r\n\tpush ds\r\n\tpop es\r\n\tmov edi, offset myidt\t;es:edi=idt\r\n\tmov al, PMINIT_INIT_IDT\r\n\tcall [dfDbgEntry]\r\n\tint 3\r\n@@:\r\nendif\r\n\ttest flags, QFLAG\r\n\tjz @F\r\n\tcall pmquery\r\n@@:\r\n\ttest flags, AFLAG\r\n\tjz @F\r\n\tcall pmalloc\r\n@@:\r\n\ttest flags, FFLAG\r\n\tjz @F\r\n\tcall pmfree\r\n@@:\r\n\r\n;--- jump back to v86\r\n\r\n\tsub sp, sizeof IRETV86\r\n\tmov bp, sp\r\n\txor eax,eax\r\n\tmov [bp].IRETV86._Eip, offset rmentr\r\n\tmov ax, [segcs]\r\n\tmov [bp].IRETV86._Cs, eax\r\n\tmov [bp].IRETV86._Efl, 2\r\n\tmov [bp].IRETV86._Esp, ebx\r\n\tmov ax, [segds]\r\n\tmov [bp].IRETV86._Ss, eax\r\n\tmov [bp].IRETV86._Es, eax\r\n\tmov [bp].IRETV86._Ds, eax\r\n\txor ax, ax\r\n\tmov [bp].IRETV86._Fs, eax\r\n\tmov [bp].IRETV86._Gs, eax\r\n\r\n\tclts\t\t\t\t\t\t;clear task switched flag\r\n\tmov ax, FLATSEL\r\n\tmov ds,ax\t\t\t\t\t;DS must be FLAT\r\n\tmov ax,0DE0Ch\r\n\tcall fword ptr ss:[vcpiv]\r\nrmentr: \t\t\t\t\t\t;back in v86-mode\r\n\tsti\r\nif ?DEB386\r\n\tcmp bKrnlDbg,KD_DEB386\r\n\tjnz @F\r\n\tmov ah, D386_Real_Mode_Init\r\n\tint 68h\r\n@@:\r\nendif\r\n\tret\r\npmgo endp\r\n\r\nprotocol proc\r\n\t.if (flags & AFLAG)\r\n\t\tinvoke printf, CStr(\"alloced %lu\"), dwAlloced\r\n\t\tmovzx ax,bRet\r\n\t\tinvoke printf, CStr(\" page(s) in pm, last status ah=%02X\"),ax\r\n\t\t.if (dwAlloced)\r\n\t\t\tinvoke printf, CStr(\", (first) page=%08lX\"), dwPage\r\n\t\t.endif\r\n\t\tinvoke println\r\n\t.elseif (flags & FFLAG)\r\n\t\tinvoke printf, CStr(\"freed %lu\"), dwFreed\r\n\t\tmovzx ax,bRet\r\n\t\tinvoke printf, CStr(\" page(s) in pm, last status ah=%02X\"),ax\r\n\t\t.if (dwFreed)\r\n\t\t\tinvoke printf, CStr(\", (last) page released=%08lX\"), dwPage\r\n\t\t.endif\r\n\t\tinvoke println\r\n\t.elseif (flags & RFLAG)\r\n\t\tinvoke printf, CStr(\"freed %lu\"), dwFreed\r\n\t\tmovzx ax,bRet\r\n\t\tinvoke printf, CStr(\" page(s) in rm, last status ah=%02X\"),ax\r\n\t\t.if (dwFreed)\r\n\t\t\tinvoke printf, CStr(\", (last) page released=%08lX\"), dwPage\r\n\t\t.endif\r\n\t\tinvoke println\r\n\t.elseif (flags & QFLAG)\r\n\t\tmovzx ax,bRet\r\n\t\tinvoke printf, CStr(\"%lu free pages in pm, status ah=%02X\",lf), dwFreePages, ax\r\n\t.elseif (flags & (UFLAG or PFLAG))\r\n\t.elseif (!(flags & NFLAG))\r\n\t\tinvoke printf, CStr('Protected Mode Switch ok',lf)\r\n\t.endif\r\n\tret\r\nprotocol endp\r\n\r\n;--- query free VCPI page(s) using protected-mode API\r\n\r\npmquery proc\r\n\tpushad\r\n\tmov ax,0DE03h\r\n\tcall fword ptr ss:[vcpiv]\r\n\tmov bRet, ah\r\n\tmov dwFreePages, edx\r\n\tpopad\r\n\tret\r\npmquery endp\r\n\r\n;--- allocate VCPI page(s) using protected-mode API\r\n\r\npmalloc proc\r\n\tpushad\r\n\tmov ecx, dwNumPages\r\nnextitem:\r\n\tmov ax,0DE04h\r\n\tcall fword ptr ss:[vcpiv]\r\n\tmov bRet, ah\r\n\tand ah,ah\r\n\tjnz allocerr\r\n\tinc dwAlloced\r\n\tcmp ecx,dwNumPages\r\n\tjnz @F\r\n\tmov dwPage, edx\r\n@@:\r\n\tdec ecx\r\n\tjnz nextitem\r\nallocerr:\r\n\tpopad\r\n\tret\r\npmalloc endp\r\n\r\n;--- release VCPI page(s) with protected-mode VCPI API\r\n\r\npmfree proc\r\n\tmov edx, dwPage\r\n\tand edx, edx\r\n\tjz exit\r\n\tpushad\r\n\tmov ecx, dwNumPages\r\nnextitem:\r\n\tmov ax,0DE05h\r\n\tcall fword ptr ss:[vcpiv]\r\n\tand ah,ah\r\n\tjnz done\r\n\tmov dwPage, edx\r\n\tinc dwFreed\r\n\tadd edx,1000h\r\n\tdec ecx\r\n\tjnz nextitem\r\ndone:\r\n\tmov bRet, ah\r\n\tpopad\r\nexit:\r\n\tret\r\npmfree endp\r\n\r\n;--- free VCPI pages with real-mode VCPI API\r\n\r\nrmfree proc\r\n\tmov edx, dwPage\r\n\tand edx, edx\r\n\tjz exit\r\n\tmov ecx, dwNumPages\r\nnextitem:\r\n\tmov ax,0DE05h\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz exit\r\n\tinc dwFreed\r\n\tmov dwPage, edx\r\n\tadd edx,1000h\r\n\tdec ecx\r\n\tjnz nextitem\r\nexit:\r\n\tmov bRet, ah\r\n\tret\r\nrmfree endp\r\n\r\n;--- VCPI host was detected,\r\n;--- now call vcpi functions\r\n\r\nrunvcpi proc near\r\n\r\n\tmov cl,2\r\n@@:\r\n\tmov ax,0DE00h\t\t;is vcpi supported?\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjz pvcpi1\r\n\tpush cx\r\n\tcall EMScheck\r\n\tpop cx\r\n\tand ax,ax\r\n\tjz pvcpi1x\r\n\tmov ax,4300h\r\n\tmov bx,0001h\t\t;get EMS page to ensure EMM is ON\r\n\tint 67h\r\n\tand ah,ah\r\n\tjnz pvcpi1x\r\n\tmov [handle],dx\r\n\tdec cl\r\n\tjnz @B\r\npvcpi1x:\r\n\tinvoke printf, CStr(\"no VCPI host found\",lf)\r\n\tjmp pvcpiex\r\npvcpi1:\r\n\tcmp bNoDisp, 0\r\n\tjnz @F\r\n\tmovzx ax,bh\r\n\tmovzx bx,bl\r\n\tinvoke printf, CStr(\"VCPI version: %u.%u\",lf),ax,bx\r\n@@:\r\n\r\n\tcall setdescriptors\r\n\tpush ds\r\n\tpop es\r\n\r\nif ?386SWAT\r\n;--- the proper sequence of API calls for 386SWAT are\r\n;--- DEF0, DE01 (VCPI get pm interface), DEF2, DEF3\r\n\r\n\tmov ax,0DEF0h\t; returns debugger version in BX\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjnz @F\r\n\tmov bKrnlDbg, KD_386SWAT\r\n\tjmp kd_done\r\n@@:\r\nendif\r\nif ?DEB386\r\n\tmov ah,D386_Identify\r\n\tint 68h\r\n\tcmp ax, D386_Id\r\n\tjnz kd_done\r\n\tmov bKrnlDbg, KD_DEB386\r\n\tmov bx, FLATSEL\r\n\tmov cx, KDSEL\r\n\tmov dx, 0\t; no GDT sel\r\n\tmov si, offset mygdt\t; ds:si=gdt\r\n\tmov di, offset myidt\t; es:di=idt\r\n\tmov ah, D386_Prepare_PMode\r\n\tint 68h\r\n\tmov dword ptr [dfDbgEntry+0], edi\r\n\tmov  word ptr [dfDbgEntry+4], es\r\n\tpush ds\r\n\tpop es\r\nendif\r\nkd_done:\r\n\r\n\tmov es,[ptadr]\r\n\txor di,di\t\t\t;ES:DI -> page table 0\r\n\tmov si,offset restab;DS:SI -> 3 free GDT descriptors\r\n\tmov ax,0DE01h\t\t;get protected mode interface\r\n\tint 67h\r\n\tcmp ah,00\r\n\tjnz de01done\r\n\tmov [vcpiofs],ebx\r\n\tmov vcpiend,di\r\n\tcmp bNoDisp, 0\r\n\tjnz de01done\r\n\tinvoke printf, CStr(\"Offset of VCPI entry in protected-mode: %08lX\",lf),ebx\r\n\tmovzx edi,di\r\n\tshl edi, 10\r\n\tinvoke printf, CStr(\"Start free address space: %08lX\",lf), edi\r\n\tmov di,offset restab\r\n\tcall getdesc\r\n\tinvoke printf, CStr(\"1. VCPI descriptor: %08lX:%04X,%04X\",lf), eax, dx, cx\r\n\tadd di,8\r\n\tcall getdesc\r\n\tinvoke printf, CStr(\"2. VCPI descriptor: %08lX:%04X,%04X\",lf), eax, dx, cx\r\n\tadd di,8\r\n\tcall getdesc\r\n\tinvoke printf, CStr(\"3. VCPI descriptor: %08lX:%04X,%04X\",lf), eax, dx, cx\r\nde01done:\r\n\r\n\tcmp bNoDisp, 0\r\n\tjnz noprot\r\n\r\n\tmov ax,0DE02h\t\t;maxAddr 4K Page\r\n\tint 67h\r\n\tinvoke printf, CStr(\"highest physical memory address: %08lX\",lf),edx\r\n\r\n\tmov ax,0DE03h\t\t;num free 4K Pages\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjz @F\r\n\tmovzx ax,ah\r\n\tinvoke printf, CStr('int 67h, ax=DE03h failed, status AH=%02X',lf),ax\r\n\tjmp de03done\r\n@@:\r\n\tmov eax,edx\r\n\tshl eax,2\r\n\tinvoke printf, CStr('Free 4K pages: %lu ( %lu kB)',lf), edx, eax\r\nde03done:\r\n\tmov ax,0DE07h\t\t;get CR0\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjnz de07done\r\n\tinvoke printf, CStr(\"CR0: %08lX\",lf),ebx\r\nde07done:\r\n\tmov di,offset drtab\r\n\tpush ds\r\n\tpop es\r\n\tmov cx,8\r\n\tmov eax,-1\r\n\tmov dx,di\r\n\trep stosd\r\n\tmov di,dx\r\n\r\n\tmov ax,0DE08h\t\t;get DRx\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjnz de08done\r\n\tinvoke printf, CStr(\"DR0-DR3: %08lX %08lX %08lX %08lX\",lf), dword ptr es:[di+0],\r\n\t\tdword ptr es:[di+4], dword ptr es:[di+8], dword ptr es:[di+12]\r\n\tinvoke printf, CStr(\"DR6+DR7: %08lX %08lX\",lf), dword ptr es:[di+24], dword ptr es:[di+28]\r\nde08done:\r\n\tmov ax,0DE0Ah\t\t;get interrupt vector mappings\r\n\tint 67h\r\n\tcmp ah,0\r\n\tjnz de0adone\r\n\tinvoke printf, CStr(\"Master/Slave PIC base: %X/%X\",lf), bx, cx\r\nde0adone:\r\n\r\nnoprot:\r\n\ttest flags,PFLAG\r\n\tjz @F\r\n\tcall DispPTEs\r\n@@:\r\n\ttest flags,UFLAG\r\n\tjz @F\r\n\tcall DispUMBs\r\n@@:\r\n\ttest flags,PFLAG or RFLAG or NFLAG or UFLAG\r\n\tjnz @F\r\n\tcall pmgo\t\t\t;--- call protected mode\r\n@@:\r\n\ttest flags,RFLAG\r\n\tjz @F\r\n\tcall rmfree\r\n@@:\r\n\tcall protocol\r\npvcpiex:\r\n\tmov dx,[handle]\r\n\tand dx,dx\r\n\tjz pvcpiex1\r\n\tmov ax,4500h\t\t;release the EMS page again\r\n\tint 67h\r\npvcpiex1:\r\n\tret\r\nrunvcpi endp\r\n\r\n;--- get cmdline parameters\r\n\r\ngetparm proc near\r\n\tmov si,0080h\r\n\tmov bl,byte ptr es:[si]\r\n\tinc si\r\n\tmov bh,0\r\n\tmov byte ptr es:[si+bx],0\r\ngetp21:\r\n\tmov al,es:[si]\r\n\tinc si\r\n\tand al,al\r\n\tjz getp1\r\n\tcmp al,'/'\r\n\tjz isoption\r\n\tcmp al,'-'\r\n\tjz isoption\r\n\tcmp al,' '\r\n\tjbe getp21\r\n\tjmp parerr\r\ngetp1:\r\n\tclc\r\n\tret\r\nisoption:\r\n\tmov al,es:[si]\r\n\tand al,al\r\n\tjz parerr\r\n\tinc si\r\n\tor al,20h\r\n\tmov ah,al\r\n\tcmp al,'r'\r\n\tjnz @F\r\n\tor [flags],RFLAG\r\n\tjmp get_hex\r\n@@:\r\n\tcmp al,'f'\r\n\tjnz @F\r\n\tor [flags],FFLAG\r\n\tjmp get_hex\r\n@@:\r\n\tcmp al,'p'\r\n\tjnz @F\r\n\tor [flags],PFLAG\r\n\tjmp getp21\r\n@@:\r\n\tcmp al,'q'\r\n\tjnz @F\r\n\tor [flags],QFLAG\r\n\tjmp getp21\r\n@@:\r\n\tcmp al,'n'\r\n\tjnz @F\r\n\tor [flags],NFLAG\r\n\tjmp getp21\r\n@@:\r\n\tcmp al,'u'\r\n\tjnz @F\r\n\tor [flags],UFLAG\r\n\tjmp getp21\r\n@@:\r\n\tcmp al,'a'\r\n\tjnz parerr\r\n\tor [flags],AFLAG\r\n\tcall skipws\r\n\tcmp al,'0'\r\n\tjb getp21\r\n\tjmp get_dec\r\n\r\n;--- get a decimal number\r\n\r\nget_dec:\r\n\tcall skipws\r\n\tjz parerr\r\n\tcall getdec\r\n\tjc parerr\r\n\tand eax, eax\r\n\tjz parerr\r\n\tmov dwNumPages, eax\r\n\tjmp getp21\r\n\r\n;--- get a hex number, then a decimal number\r\n\r\nget_hex:\r\n\tcall skipws\r\n\tjz parerr\r\n\tcall gethex\r\n\tjc parerr\r\n\tmov dwPage, eax\r\n\tcall skipws\r\n\tjz getp21\r\n\tcmp al,','\r\n\tjnz getp21\r\n\tinc si\r\n\tcall skipws\r\n\tjz parerr\r\n\tjmp get_dec\r\nparerr:\r\n\tstc\r\n\tret\r\nskipws:\r\n\tmov al,es:[si]\r\n\tand al,al\r\n\tjz @F\r\n\tinc si\r\n\tcmp al,20h\r\n\tjbe skipws\r\n\tdec si\r\n@@:\r\n\tret\r\ngetparm endp\r\n\r\nmain proc c\r\n\r\n\tmovzx esp,sp\r\n\r\n\tcall getparm\r\n\tjnc main_1\r\n\tinvoke printf, CStr('usage: vcpi [ options ]',lf)\r\n\tinvoke printf, CStr('    -a <nn>: alloc <1|nn> page(s) in protected mode',lf)\r\n\tinvoke printf, CStr('    -f page,<nn>: free <1|nn contiguous> page(s) in protected mode',lf)\r\n\tinvoke printf, CStr('    -n: dont try to switch to protected mode (disables -q -f -a)',lf)\r\n\tinvoke printf, CStr('    -p: display PTEs for conventional memory',lf)\r\n\tinvoke printf, CStr('    -q: query num pages in protected mode',lf)\r\n\tinvoke printf, CStr('    -r page,<nn>: free <1|nn contiguous> page(s) in real-mode',lf)\r\n\tinvoke printf, CStr('    -u: display regions mapped as UMBs',lf)\r\n\tjmp exit\r\nmain_1:\r\n\tmov al,flags\r\n\tand al,AFLAG or FFLAG or RFLAG or UFLAG\r\n\tmov bNoDisp,al\r\n\r\n\tmov di, offset ptab\r\n\tmov cx, (sizeof ptab) / 4\r\n\tpush ds\r\n\tpop es\r\n\txor eax,eax\r\n\trep stosd\r\n\r\n\tmov ax,3567h\r\n\tint 21h\r\n\tmov ax,es\r\n\tor ax,bx\r\n\tjnz main3\r\n\tinvoke printf, CStr(\"int 67h is zero (no EMM)\",lf)\r\n\tmov al,01\r\n\tjmp exit\r\nmain3:\r\n\t\t\t\t\t\t;get memory for page tables\r\n\tmov bx,400h\r\n\tmov ah,48h\r\n\tint 21h\r\n\tjc exit\r\n\tmov bx,ax\r\n\tmovzx eax,ax\r\n\tshl eax, 4\r\n\tadd eax,4096-1\t\t;page directory must be page aligned\r\n\tand ax,0f000h\t\t;clear bits 0-11 \r\n\r\n\tmov msw._CR3,eax\r\n\tmov edi,eax\r\n\tadd edi,1000h\r\n\tshr eax,4\r\n\tmov es,ax\r\n\t\t\t\t\t\t;page directory 1. entry\r\n\tor di,1+2+4\t\t\t;set present, r/w, user\r\n\tmov es:[0000],edi\t;set PDE for 0-3FFFFFh\r\n\tmov ax,es\r\n\tadd ax,100h\r\n\tmov [ptadr],ax\r\n\r\n\tcall runvcpi\r\n\tmov al,00\r\nexit:\r\n\tret\r\nmain endp\r\n\r\n\r\nstart:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx       ;make SS=DS\r\n\r\n\tmov ax,ds\r\n\tmov cx,cs\r\n\tsub ax,cx\r\n\tadd ax,10h\r\n\tshr bx,4\r\n\tadd bx,ax\r\n\tmov ah,4ah\r\n\tint 21h\r\n\r\n\tpush es\r\n\tpush ds\r\n\tpop es\r\n\tmov di, offset myidt\r\n\tmov cx, sp\r\n\tsub cx, di\r\n\tshr cx, 1\r\n\txor ax, ax\r\n\trep stosw\r\n\tpop es\r\n\tcall main\r\n\tmov ax,4c00h\r\n\tint 21h\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "Tools/VCPI/VCPI.INC",
    "content": "\r\ndesc struct\r\nlimit   dw ?\r\nA0015   dw ?\r\nA1623   db ?\r\nattrib  db ?\r\nlim_gr  db ?\r\nA2431   db ?\r\ndesc ends\r\n\r\n;--- VCPI structure for switch to protected mode\r\n\r\nV86toPM struct\r\n_CR3    dd ?\t;CR3\r\n_Gdtr   dd ?\t;linear address PD for GDTR\r\n_Idtr   dd ?\t;linear address PD for IDTR\r\n_Ldtr   dw ?\t;LDTR\r\n_Tr     dw ?\t;TR\r\n_Eip\tdd ?\t;EIP\r\n_Cs     dd ?\t;CS\r\nV86toPM ends\r\n\r\nIRETV86 struct\r\n_Eip    dd ?\r\n_Cs     dd ?\r\n_Efl    dd ?\r\n_Esp    dd ?\r\n_Ss     dd ?\r\n_Es     dd ?\r\n_Ds     dd ?\r\n_Fs     dd ?\r\n_Gs     dd ?\r\nIRETV86 ends\r\n\r\nTSSSEG struct\r\ndwLink\tdd ?\r\ndfStk0\tdf ?\t;+04\r\n\t\tdw ?\r\ndfStk1\tdf ?\t;+0C\r\n\t\tdw ?\r\ndfStk2\tdf ?\t;+14\r\n\t\tdw ?\r\n_CR3\tdd ?\t;+1C\r\n_Eip\tdd ?\t;+20\r\n_Efl\tdd ?\t;+24\r\n_Eax\tdd ?\t;+28\r\n_Ecx\tdd ?\t;+2C\r\n_Edx\tdd ?\t;+30\r\n_Ebx\tdd ?\t;+34\r\n_Esp\tdd ?\t;+38\r\n_Ebp\tdd ?\t;+3C\r\n_Esi\tdd ?\t;+40\r\n_Edi\tdd ?\t;+44\r\n_ES\t\tdd ?\t;+48\r\n_CS\t\tdd ?\t;+4C\r\n_SS\t\tdd ?\t;+50\r\n_DS\t\tdd ?\t;+54\r\n_FS\t\tdd ?\t;+58\r\n_GS\t\tdd ?\t;+5C\r\n_LDT\tdd ?\t;+60\r\nwFlags  dw ?\t;+64\r\nwOffs   dw ?\t;+66\r\nTSSSEG ends\r\n\r\n"
  },
  {
    "path": "Tools/VCPI/VIOOUT.INC",
    "content": "\r\n;--- vio output for 16-bit protected-mode\r\n\r\n@getcursorpos macro\r\n\tmovzx ebx, byte ptr ds:[462h];page\r\n\tmov bx, ds:[EBX*2+450h]\r\nendm\r\n\r\n@setcursorpos macro\r\n\tmovzx ebx, byte ptr ds:[462h];page\r\n\tmov ds:[EBX*2+450h], ax\r\nendm\r\n\r\n;--- print a char\r\n;--- no registers modified\r\n\r\nVioPutChar proc\r\n\r\nlocal\twCols:word\r\nlocal\tbChar:byte\r\nlocal\tbRows:byte\r\n\r\n\tpush ds\r\n\tpushad\r\n\r\n\tmov bChar, al\r\n\tmov dx, FLATSEL\r\n\tmov ds, dx\r\n\tmov ch, ds:[0484h]\t\t\t; rows-1\r\n\tmov cl, ds:[044Ah]\t\t\t; cols\r\n\tmov bRows, ch\r\n\t@getcursorpos\t\t\t\t; bh=row, bl=col\r\n\tmov al, bh\t\t;row pos\r\n\tmov ch, 0\r\n\tmov wCols, cx\r\n\tmul cl\r\n\tadd ax, ax\r\n\tmov bh, 00  ; bx=col pos\r\n\tadd bx, bx\r\n\tadd bx, ax\r\n\tmov si, ds:[044Eh]\t\t\t; page offset\r\n\tcmp word ptr ds:[0463H],3B4h\r\n\tjz @F\r\n\tadd si, 8000h\r\n@@:\r\n\tmovzx esi, si\r\n\tadd esi, 0B0000h\r\n\r\n\tmov al, bChar\r\n\r\n\tcmp al, cr\r\n\tjnz @F\r\n\tmov ax, bx\r\n\tshr ax, 1\r\n\tdiv cl\r\n\tmov al, ah\r\n\txor ah, ah\r\n\tadd ax, ax\r\n\tsub bx, ax\r\n\tjmp char_done\r\n@@:\r\n\tcmp al, lf\r\n\tjnz @F\r\n\tadd bx, cx\r\n\tadd bx, cx\r\n\tjmp char_done\r\n@@:\r\n\tmovzx ebx, bx\r\n\tmov ds:[ebx+esi], al\r\n\tinc bx\r\n\tinc bx\r\nchar_done:\r\n\tmov al, bRows\r\n\tinc al\r\n\tmul cl\r\n\tadd ax, ax\r\n\tcmp bx, ax\r\n\tjc @F\r\n\tcall scrollup\r\n\tmov bx, ax\r\n@@:\r\n\tmov ax, bx\r\n\tpush ax\r\n\tmov cx, wCols\r\n\tshr ax, 1\r\n\tdiv cl\r\n\txchg al, ah\r\n\t@setcursorpos\r\n\tpop ax\r\n\tcall cursorset\r\n\tpopad\r\n\tpop ds\r\n\tret\r\n\r\ncursorset:\r\n\tadd ax, ds:[044EH]\t;offset page\r\n\tmov dx, ds:[0463H]\r\n\tshr ax, 1\t\t\t;the CRT offset is one plane only, no attribute bytes \r\n\tmov cl, al\t\t\t;first high byte\r\n\tmov al, 0eh\r\n\tout dx, ax\r\n\tmov ah, cl\t\t\t;then low byte\r\n\tmov al, 0fh\r\n\tout dx, ax\r\n\tretn\r\n\r\nscrollup:\t\t\t;scroll up one line\r\n\tpush es\r\n\tpush ds\r\n\tpop es\r\n\tmov edi, esi\r\n\tpush di\r\n\tmovzx esi, wCols\r\n\tlea esi, [esi*2+edi]\r\n\tmov cl, byte ptr wCols\r\n\tmov al, bRows\r\n\tmul cl\r\n\tmovzx ecx, ax\r\n\tshr cx,1\r\n\trep movsd es:[edi], ds:[esi]\r\n\tpush di\r\n\tmov cx, wCols\r\n\tmov eax,07200720h\r\n\tshr cx, 1\r\n\trep stosd es:[edi]\r\n\tpop ax\r\n\tpop di\r\n\tsub ax, di\r\n\tpop es\r\n\tretn\r\n\r\nVioPutChar endp\r\n\r\n"
  },
  {
    "path": "Tools/XMSSTAT/MAKE.BAT",
    "content": "@echo off\r\nrem  create XMSSTAT.EXE\r\nrem  uses JWasm\r\njwasm -nologo -mz XMSSTAT.ASM\r\n"
  },
  {
    "path": "Tools/XMSSTAT/PRINTF.INC",
    "content": "\r\n;--- simple printf() implementation\r\n\r\nhandle_char proc\r\n\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tret\r\n\r\nhandle_char endp\r\n\r\n;--- ltob(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- outb is expected to be onto stack\r\n\r\nltob PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltob ENDP\r\n\r\n;--- ds=dgroup, ss don't need to be dgroup\r\n\r\nprintf PROC c uses si di bx fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bx\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\n\tmov bx,dx\r\n\r\nnextdigit:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb digitsdone\r\n\tcmp BYTE PTR [si],'9'\r\n\tja digitsdone\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul cx,bx,10\t\t;cx = bx * 10\r\n\tadd ax,cx\r\n\tmov bx,ax\r\n\tjmp nextdigit\r\n\r\ndigitsdone:\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tmov al,'%'\r\n\tjmp @@L359\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_x:\r\n\tmov bx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov bx,10\r\n@@lprt262:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp bx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tlea cx,[szTmp]\r\n\tinvoke ltob, dx::ax, cx, bx\r\n\tmov si,ax\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall output_string\r\n\tpop ds\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\r\noutput_string:\t;display string at ds:si\r\n\tmov ax,si\r\n\tmov bx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub bx,ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\n"
  },
  {
    "path": "Tools/XMSSTAT/XMSSTAT.ASM",
    "content": "\r\n;--- XMSSTAT: display xms status.\r\n;--- Public Domain.\r\n;--- to be assembled with JWasm or Masm v6.\r\n\r\n\t.model small\r\nDGROUP group _TEXT\t;use tiny model\r\n\t.386\r\n\r\n\t.dosseg\r\n\t.stack 2048\r\n\r\ncr  equ 13\r\nlf  equ 10\r\n\r\n;--- XMS handle table\r\n\r\nXMSHT struct\r\n        db ?\r\nbSize   db ?\r\nwHdls   dw ?\r\ndwArray dd ?\r\nXMSHT ends\r\n\r\n;--- XMS handle\r\n\r\nXMSH struct\r\nbFlags db ? ;flags, see below \r\nbLocks db ? ;number of locks\r\ndwAddr dd ? ;addr in KB\r\ndwSize dd ? ;size in KB\r\nXMSH ends\r\n\r\nXMSF_FREEB  equ 1   ;free block\r\nXMSF_USEDB  equ 2   ;used block\r\nXMSF_FREEH  equ 4   ;free handle\r\n\r\nprintf proto c :ptr byte, :vararg\r\n\r\n;--- define a string constant\r\n\r\nCStr macro string:vararg\r\nlocal xxx\r\n\t.const\r\nxxx db string\r\n\tdb 0\r\n\t.code\r\n\texitm <offset xxx>\r\n\tendm\r\n\r\n\t.data\r\n\r\nxmsadr    dd 0      ;XMS host call address\r\ndwTotal   dd 0      ;total size EMBs < 4GB\r\ndwTotalSX dd 0      ;total size EMBs\r\nfreehdls  dw 0      ;count free handles\r\nwVersion  dw 0      ;XMS version\r\nwDriver   dw 0      ;driver version\r\nbFlags    db 0      ;flags\r\n\r\nFL_NOSIZENULL   equ 1\r\nFL_NOUSEDEMBS   equ 2\r\nFL_NOFREEEMBS   equ 4\r\nFL_FREEHANDLES  equ 8\r\nFL_NOSEXTMEM    equ 16\r\n\r\n\t.code\r\n\r\n\tassume DS:DGROUP\r\n\r\n\tinclude printf.inc\r\n\r\n;--- display a EOL\r\n\r\n_crout proc\r\n\tinvoke printf, CStr(lf)\r\n\tret\r\n_crout endp\r\n\r\n;--- get cmdline parameter\r\n;--- ES=PSP\r\n\r\ngetparm proc\r\n\tmov bx,0080h\r\n\tmov cl,es:[bx]\r\n\tinc bx\r\n\tmov ch,00\r\n\tjcxz getparm_ex\r\n\tmov ah,00\r\ngetparm_1:\r\n\tmov al,es:[bx]\r\n\tor al,20h\r\n\tcmp ax,'-a'\r\n\tjnz @F\r\n\tor bFlags,FL_NOFREEEMBS\r\n@@:\r\n\tcmp ax,'-b'\r\n\tjnz @F\r\n\tor [bFlags], FL_FREEHANDLES\r\n@@:\r\n\tcmp ax,'-c'\r\n\tjnz @F\r\n\tor [bFlags], FL_NOSIZENULL\r\n@@:\r\n\tcmp ax,'-f'\r\n\tjnz @F\r\n\tor [bFlags], FL_NOUSEDEMBS\r\n@@:\r\n\tcmp ax,'-s'\r\n\tjnz @F\r\n\tor [bFlags], FL_NOSEXTMEM\r\n@@:\r\n\tcmp ax,'-?'\r\n\tjnz getparm_3\r\n\t.const\r\nszHelp label byte\r\n\tdb \"XMSSTAT v1.3, Public Domain\",lf\r\n\tdb \"usage: XMSSTAT [ -options ]\",lf\r\n\tdb \"  -a: skip free memory blocks\",lf\r\n\tdb \"  -b: also display unused handles\",lf\r\n\tdb \"  -c: skip memory blocks with size 0\",lf\r\n\tdb \"  -f: skip used memory blocks\",lf\r\n\tdb \"  -s: skip memory blocks beyond 4GB\",lf\r\n\tdb 0\r\n\t.code\r\n\tinvoke printf, offset szHelp\r\n\tjmp getparm_er\r\ngetparm_3:\r\n\tcmp al,'/'\r\n\tjnz @F\r\n\tmov al,'-'\r\n@@:\r\n\tmov ah,al\r\n\tinc bx\r\n\tloop getparm_1\r\ngetparm_ex:\r\n\tclc\r\n\tret\r\ngetparm_er:\r\n\tstc\r\n\tret\r\ngetparm endp\r\n\r\n;--- check if XMS handle should be displayed\r\n\r\ncheckflags proc\r\n\ttest al,XMSF_USEDB\t ;block used?\r\n\tjnz isused\r\n\ttest al,XMSF_FREEB\r\n\tjnz isfree\r\n\tinc [freehdls]\r\n\ttest bFlags, FL_FREEHANDLES\r\n\tjz nodisp\r\n\tret\r\nisfree:\r\n\ttest bFlags, FL_NOFREEEMBS\r\n\tjnz nodisp\r\n\tret\r\nisused:\r\n\ttest bFlags, FL_NOUSEDEMBS\r\n\tjnz nodisp\r\n\tret\r\nnodisp:\r\n\tstc\r\n\tret\r\ncheckflags endp\r\n\r\n;--- display XMS handle flags\r\n\r\ngetflags proc\r\n\ttest al,1\r\n\tjz @F\r\n\tmov cx,CStr(\"free\")\r\n\tret\r\n@@:\r\n\ttest al,2\r\n\tjz @F\r\n\tmov cx,CStr(<\"used\">)\r\n\tret\r\n@@:\r\n\ttest al,4\r\n\tjz @F\r\n\tmov cx,CStr(<\"unused\">)\r\n\tret\r\n@@:\r\n\tmov cx,CStr(\" \")\r\n\tret\r\ngetflags endp\r\n\r\n;--- display 1 XMS handle\r\n;--- ES:BX -> handle\r\n;--- SI=no of handle in array (1-based)\r\n\r\nhdlout proc\r\n\tmov al,es:[bx].XMSH.bFlags\r\n\tcall checkflags\r\n\tjnc @F\r\n\tret\r\n@@:\r\n\ttest [bFlags], FL_NOSIZENULL\r\n\tjz @F\r\n\tcmp es:[bx].XMSH.dwSize,0\r\n\tjnz @F\r\n\tret\r\n@@:\r\n\ttest [bFlags], FL_NOSEXTMEM\r\n\tjz @F\r\n\tcmp es:[bx].XMSH.dwAddr,400000h\r\n\tjb @F\r\n\tret\r\n@@:\r\n\tinvoke printf, CStr(\"%3u %4X\"), si, bx\r\n\tpush si\r\n\tmov ecx,es:[bx].XMSH.dwSize\r\n\tmov eax,es:[bx].XMSH.dwAddr\r\n\tadd [dwTotalSX],ecx\r\n\ttest eax, 0ffc00000h    ;beyond 4GB?\r\n\tjnz @F\r\n\tadd [dwTotal],ecx\r\n@@:\r\n\tmov edx, eax\r\n\tmov esi, eax\r\n\tshl eax, 10\r\n\tshr esi, 22\r\n\r\n\tadd edx, ecx\r\n\tmov edi, edx\r\n\tshl edx, 10\r\n\tshr edi, 22\r\n\tjecxz @F\r\n\tsub edx,1\r\n\tsbb di,0\r\n@@:\r\n\tmov cl, es:[bx].XMSH.bLocks\r\n\tmov ch, 00\r\n\tinvoke printf, CStr(\"   %2x%08lx-%2x%08lx %8lu %4u  \"), si, eax, di, edx, es:[bx].XMSH.dwSize, cx\r\n\tpop si\r\n\tmov al, es:[bx].XMSH.bFlags\r\n\tcall getflags\r\n\tmovzx ax,al\r\n\tinvoke printf, CStr(\"%2X %s\",lf), ax, cx\r\n\tret\r\n\r\nhdlout endp\r\n\r\n;--- display XMS handle array\r\n\r\nhdlarray proc stdcall pArray:DWORD\r\n\tmov ah,5\t\t\t\t\t;enable A20 in case array is in HMA\r\n\tcall [xmsadr]\r\n\tmov [freehdls],0\r\n\tles bx,pArray\r\n\tmov cx,es:[bx].XMSHT.wHdls\t;total number of handles\r\n\tmov dl,es:[bx].XMSHT.bSize\t;size of element (must be 10)\r\n\tmov dh,00\r\n\tles bx,es:[bx].XMSHT.dwArray\r\n\tjcxz exit\r\n\tcall _crout\r\n\tinvoke printf, CStr(\" no handle   region              size(kB) locks  flags\",lf)\r\n\tinvoke printf, CStr(\"------------------------------------------------------------\",lf)\r\n\tmov si,1                    ;start with 1\r\nnextitem:\r\n\tpusha\r\n\tcall hdlout\r\n\tpopa\r\n\tadd bx,dx\r\n\tinc si\r\n\tloop nextitem\r\n\tinvoke printf, CStr(\"------------------------------------------------------------\",lf)\r\n\tinvoke printf, CStr(\"                                %9lu\"), dwTotalSX\r\n\tmov ecx,dwTotal\r\n\tcmp ecx, dwTotalSX\r\n\tjz @F\r\n\tinvoke printf, CStr(\" (%lu kB below 4G)\"), ecx\r\n@@:\r\n\tcall _crout\r\nexit:\r\n\tinvoke printf, CStr(\"free handles: %u\",lf),freehdls\r\n\tmov ah,6\t\t\t\t\t;disable A20\r\n\tcall [xmsadr]\r\n\tret\r\n\r\nhdlarray endp\r\n\r\n;--- display XMS handle info (handle table + handle array)\r\n\r\nhdlinfo proc near\r\n\r\n\tmov ax,4309h\r\n\tint 2Fh\r\n\tcmp al,43h\r\n\tjnz nohandletab\r\n\tmov al,es:[bx].XMSHT.bSize\t;size of element\r\n\tmov ah,00\r\n\tinvoke printf, CStr(\"XMS handle table at %X:%X, handle cnt/size=%u/%u\",lf), es, bx, es:[bx].XMSHT.wHdls, ax\r\n\tinvoke printf, CStr(\"XMS handle array at %X:%X\",lf), word ptr es:[bx].XMSHT.dwArray+2, word ptr es:[bx].XMSHT.dwArray+0\r\n\tcmp es:[bx].XMSHT.bSize, sizeof XMSH\r\n\tjnz invalhdlsize\r\n\tinvoke hdlarray, es::bx\r\n\tret\r\nnohandletab:\r\n\tinvoke printf, CStr(\"Int 2Fh, ax=4309h failed!\",lf)\r\n\tret\r\ninvalhdlsize:\r\n\tinvoke printf, CStr(\"XMS handle size isn't 10!\",lf)\r\n\tret\r\nhdlinfo endp\r\n\r\n;*** display XMS UMB info\r\n\r\numbinfo  proc near\r\n\tmov ah,10h\t\t\t\t;request UMB (upper memory block)\r\n\tmov dx,0ffffh\t\t\t;get FFFF paras (will fail)\r\n\tcall dword ptr [xmsadr]  ;but DX contains largest block\r\n\tmov ah,10h\t\t\t\t;get this largest block\r\n\tcall dword ptr [xmsadr]\r\n\tcmp ax,0001h\r\n\tjz umbchk1\r\n\tcmp bl,80h\r\n\tjnz umbchk2\r\n\tinvoke printf, CStr(\"no UMB handler installed\",lf)\r\n\tjmp exit\r\numbchk2:\r\n\tcmp bl,0B1h\r\n\tjnz @F\r\n\tinvoke printf, CStr(\"no free UMBs available\",lf)\r\n\tjmp exit\r\n@@:\r\n\tmovzx ax,bl\r\n\tinvoke printf, CStr(\"request for UMB failed, BL=%X\",lf),ax\r\n\tjmp exit\r\numbchk1:\r\n\tpush bx \t ;save UMB address\r\n\tpush dx \t ;save size largest\r\n\tinvoke printf, CStr(\"segment of largest UMB: %X\",lf), bx\r\n\tpop ax\r\n\tinvoke printf, CStr(\"size of largest UMB (paragraphs): %X\",lf), ax\r\n\tpop dx\r\n\tmov ah,11h\t\t\t;free UMB again\r\n\tcall dword ptr [xmsadr]\r\n\tcmp ax,1\r\n\tjz exit\r\n\tmov ax,bx\r\n\tmov ah,0\r\n\tinvoke printf, CStr(\"calling free UMB failed, BL=%X\",lf),ax\r\nexit:\r\n\tret\r\numbinfo  endp\r\n\r\n;--- display XMS v2 memory info\r\n\r\nfreememinfo2 proc\r\n\tmov ah,8\r\n\tcall [xmsadr]\r\n\tcmp bl,0\r\n\tjnz failed\r\n\tinvoke printf, CStr(\"v2 free memory largest/total (kB): %u/%u\",lf), ax, dx\r\n\tret\r\nfailed:\r\n\tmovzx bx,bl\r\n\tinvoke printf, CStr(\"XMS call AH=08 returned bl=%X: %u/%u\",lf), bx, ax, dx\r\n\tret\r\nfreememinfo2 endp\r\n\r\n;--- display XMS v3 memory info\r\n\r\nfreememinfo3 proc\r\n\tmov ah,88h\r\n\tcall [xmsadr]\r\n\tcmp bl,0\r\n\tjnz failed\r\n\tinvoke printf, CStr(\"v3 free memory largest/total (kB): %lu/%lu, highest addr: %lX\",lf), eax, edx, ecx\r\n\tret\r\nfailed:\r\n\tmovzx bx,bl\r\n\tinvoke printf, CStr(\"XMS call AH=88h returned bl=%X: %lu/%lu, highest addr: %lX\",lf), bx, eax, edx, ecx\r\n\tret\r\nfreememinfo3 endp\r\n\r\n;--- display XMS v3.5 (HimemSX) memory info\r\n\r\nfreememinfoSX proc\r\n\tmov ah,0C8h\r\n\tcall [xmsadr]\r\n\tcmp bl,80h\t;not implemented?\r\n\tjz exit\r\n\tcmp bl,0\r\n\tjnz failed\r\n\tinvoke printf, CStr(\"v3.5 free memory > 4GB largest/total (kB): %lu/%lu\",lf), eax, edx\r\n\tret\r\nfailed:\r\n\tmovzx bx,bl\r\n\tinvoke printf, CStr(\"XMS call AH=C8h returned bl=%X: %lu/%lu\",lf), bx, eax, edx\r\nexit:\r\n\tret\r\nfreememinfoSX endp\r\n\r\nversioninfo proc\r\n\tmov ah,00\r\n\tcall [xmsadr]\r\n\tmov [wVersion],ax\t;version is BCD coded, major version in AH, minor in AL\r\n\tmov [wDriver],bx\r\n\tpush dx\r\n\tmovzx ax,byte ptr [wVersion+1]\r\n\tmovzx dx,byte ptr [wVersion+0]\r\n\tmovzx bx,byte ptr [wDriver+1]\r\n\tmovzx cx,byte ptr [wDriver+0]\r\n\tinvoke printf, CStr(\"XMS version: %u.%X, driver version: %u.%u\",lf), ax, dx, bx, cx\r\n\tpop dx\r\n\ttest dx,1\r\n\tjz nohma\r\n\tinvoke printf, CStr(\"HMA handled by XMS host, HMA is \")\r\n\tmov ah,01h\t\t\t\t;try to reserve HMA\r\n\tmov dx,-1\r\n\tcall [xmsadr]\r\n\tcmp ax,0001\r\n\tjnz hma_used\r\n\tmov ah,02h\t\t\t\t;release HMA\r\n\tcall [xmsadr]\r\n\tinvoke printf, CStr(\"free\",lf)\r\n\tret\r\nhma_used:\r\n\tinvoke printf, CStr(\"allocated\",lf)\r\n\tret\r\nnohma:\r\n\tinvoke printf, CStr(\"HMA NOT handled by XMS host\",lf)\r\n\tret\r\nversioninfo endp\r\n\r\n;--- main\r\n\r\nmain    proc c\r\n\r\n\tcall getparm\r\n\tjc exit\r\n\tmov ax,4300h\r\n\tint 2fh\r\n\ttest al,80h \t\t ;xms host found?\r\n\tjnz main1\r\n\tinvoke printf, CStr(\"no XMS host found\",lf)\r\n\tjmp exit\r\nmain1:\r\n\tmov ax,4310h\t\t;get XMS call address\r\n\tint 2fh\r\n\tmov word ptr xmsadr+0,bx\r\n\tmov word ptr xmsadr+2,es\r\n\tinvoke printf, CStr(\"XMS call address: %X:%X\",lf), word ptr [xmsadr+2], word ptr [xmsadr+0]\r\n\r\n\tcall versioninfo\r\n\tcall freememinfo2\r\n\tcmp byte ptr [wVersion+1],3\r\n\tjb @F\r\n\tcall freememinfo3\r\n\tcmp byte ptr [wVersion],50h\r\n\tjb @F\r\n\tcall freememinfoSX\r\n@@:\r\n\tcall hdlinfo\r\n\tcall umbinfo\r\nexit:\r\n\tret\r\nmain    endp\r\n\r\n;--- init\r\n\r\nstart   proc\r\n\r\n\tpush cs\r\n\tpop ds\r\n\r\n\tpushf\r\n\tpushf\r\n\tpop ax\r\n\tor\tah,70h\t\t\t;a 80386 will have bit 15 cleared\r\n\tpush ax \t\t\t;if bits 12-14 are 0, it is a 80286\r\n\tpopf\t\t\t\t;or a bad emulation\r\n\tpushf\r\n\tpop ax\r\n\tpopf\r\n\tand ah,0f0h\r\n\tjs no386\t\t\t;bit 15 set? then its a 8086/80186\r\n\tjnz is386\r\nno386:\r\n\tinvoke printf, CStr(\"a 80386 is needed\",lf)\r\n\tjmp done\r\nis386:\r\n\tmov ax,@data\r\n\tmov ds,ax\r\n\tmov bx,ss\r\n\tmov cx,ds\r\n\tsub bx,cx\r\n\tshl bx,4\r\n\tadd bx,sp\r\n\tmov ss,ax\r\n\tmov sp,bx\r\n\tcall main\r\ndone:\r\n\tmov ah,4Ch\r\n\tint 21h\r\nstart   endp\r\n\r\n\tEND start\r\n"
  },
  {
    "path": "XMS35.txt",
    "content": "\r\n  1. XMS v3.5 API\r\n  \r\n   XMS v3.5 has been created to allow accessing extended memory beyond the 4 GB\r\n  barrier. \r\n\r\n  To achieve this, the XMS v3.0 API has been extended:\r\n  \r\n  AH=0C8h: query free super-extended memory. Returns in EAX largest free block\r\n           in kB, in EDX the total amount (in kB) of free super-extended memory.\r\n           AX=0 indicates an error.\r\n  \r\n  AH=0C9h: allocate block of super-extended memory. Expects in EDX the\r\n           requested amount of memory in kB. Returns AX=0 if an error occured,\r\n           else AX is 1 and the handle of the block is in DX.\r\n\r\n  AH=0CCh: lock a (super-extended) memory block. Expects handle in DX. Returns\r\n           64-bit physical address of locked block in EDX:EBX. Returns AX=0\r\n           if an error occured.\r\n\r\n  XMS function 00 (Get Version) will return ax=0350h, that is version 3.50.\r\n\r\n\r\n  2. XMS v3.51 API\r\n  \r\n  XMS v3.51 extends the v3.50 API by another function:\r\n\r\n  AH=0CBh: super-extended block move function. Register setup is like XMS\r\n           function 0Bh. However, the structure that holds source and\r\n           destination of the memory block to copy has been extended by two\r\n           1-byte fields that must be filled with bits 32-39 of the source\r\n           and destination address.\r\n\r\n           sxms_move struct\r\n           len           dd  ?   ;  +0: block length in bytes\r\n           src_handle    dw  ?   ;  +4: source handle\r\n           src_offset    dd  ?   ;  +6: offset into source\r\n           dst_handle    dw  ?   ; +10: destination handle\r\n           dst_offset    dd  ?   ; +12: offset into destination\r\n           src_high      db  ?   ; +16: NEW: bits 32-39 of source offset\r\n           dest_high     db  ?   ; +17: NEW: bits 32-39 of destination offset\r\n           sxms_move ends\r\n\r\n\r\n  3. BIOS Interrupt 15h, AX=E820h\r\n\r\n   Since the memory beyond the 4 GB limit must be managed exclusively,\r\n  Int 15h, ax=E820h should be intercepted in a way that all memory blocks with\r\n  addresses >= 100000000h are changed from \"available\" to \"reserved\".\r\n\r\n\r\n  4. BIOS Interrupt 15h, AH=87h\r\n\r\n   In V86 mode, the XMM's 'move extended memory' functions (AH=0Bh & AH=0CBh)\r\n  will need the help of the Expanded Memory Manager (EMM), since privileged\r\n  code has to be executed. The only EMMs that currently support accessing\r\n  memory beyond 4 GB are Jemm386/JemmEx v5.80+. Their Int 15h API has been\r\n  exhanced as well.\r\n  \r\n  Register setup for Int 15h, AH=87h: \r\n   \r\n  - AH: 87h\r\n  - EAX[bits 16-31]: F00Fh\r\n  - CX: F00Fh\r\n  - ECX[bits 16-31]: size of block in words\r\n  - DS:SI: same as the standard ( pointing to a GDT ), descriptors 2 & 3\r\n           defining address bits 0-31 of source/destination region.\r\n  - DX: address bits 32-47 of the source region.\r\n  - BX: address bits 32-47 of the destination region.\r\n\r\n   If the call succeeded, the carry flag is cleared and register AH is 0.\r\n   If an error occured ( for example, CPU doesn't support PSE ), the carry\r\n   flag is set and AH is != 0.\r\n\r\n  Japheth\r\n"
  },
  {
    "path": "src/AUXIO.INC",
    "content": "\r\n;--- handle AUX I/O\r\n\r\nifndef COMNO\r\nCOMNO       equ 1\r\nendif\r\n_XONXOFF_   equ 1\r\nXON         equ 11h\r\nXOFF        equ 13h\r\n\r\n.text$01 segment dword flat public 'CODE'\r\nwCsrPos dw 0,0\r\n.text$01 ends\r\n\r\n;--- display a char in AL\r\n\r\nAuxPutChar proc public\r\n\r\n\tpushad\r\n\tmovzx ebx, word ptr cs:[400h + (COMNO-1)*2]\r\n\tlea edx, [ebx+5]\t;LSR - Line Status Register\r\n\tmov cx, -1\r\n\tmovzx ecx, cx\r\n\txchg al, ah\r\n@@:\r\n\tin al, dx\r\n\ttest al, 40h\t\t;TEMT - transmitter empty?\r\n\tloopz @B\r\nif _XONXOFF_\r\n\ttest al, 1\t\t\t;char received\r\n\tjz noxoff\r\n\tmov edx, ebx\r\n\tin al, dx\r\n\tcmp al, XOFF\r\n\tjnz noxoff\r\nwaitxon:\r\n\tadd edx, 5\r\n@@: \t\t\t\t\t;wait till new char arrived\r\n\tin al, dx\r\n\ttest al, 1\r\n\tjz @B\r\n\tmov edx, ebx\r\n\tin al, dx\r\n\tcmp al, XON\t\t\t;wait till XON received\r\n\tjnz waitxon\r\nnoxoff:\r\nendif\r\n\txchg al, ah\r\n\tmov edx, ebx\r\n\tout dx, al\r\n\tcall setcsrpos\r\n\tpopad\r\n\tret\r\n\r\nsetcsrpos:\r\n\tcmp al, 13\r\n\tjz col00\r\n\tcmp al, 10\r\n\tjz nochg\r\n\tcmp al, 8\r\n\tjz back\r\n\tinc byte ptr ss:[wCsrPos]\r\n\tretn\r\nback:\r\n\tdec byte ptr ss:[wCsrPos]\r\n\tretn\r\ncol00:\r\n\tmov byte ptr ss:[wCsrPos], 0\r\nnochg:\r\n\tretn\r\n\r\nAuxPutChar endp\r\n\r\n;--- get a char in AL\r\n\r\nAuxGetChar proc\r\n\r\n\tpush ebx\r\n\tpush ecx\r\n\tpush edx\r\n\r\n\tmovzx ebx, word ptr cs:[400h + (COMNO-1)*2]\r\n\tlea edx, [ebx+6]\t;MSR - modem status register\r\n\tin al, dx\t\t\t;DSR - modem(=DCE) ready?\r\n\tand al, 20h\r\n\tjz error\r\n\tdec edx\t\t\t\t;LSR - Line Status Register\r\n@@:\r\n\tin al, dx\r\n\ttest al, 01h\t\t;DR - Data ready?\r\n\tjz @B\r\n\tmov edx, ebx\r\n\tin al, dx\r\n\tmov ah, 00\r\n\tjmp exit\r\nerror:\r\n\txor eax, eax\r\nexit:\r\n\tpop edx\r\n\tpop ecx\r\n\tpop ebx\r\n\tret\r\nAuxGetChar endp\r\n\r\n"
  },
  {
    "path": "src/DEBUG.ASM",
    "content": "\r\n;*** implements debug displays\r\n;--- written by japheth\r\n;--- public domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n\t.386\r\n\t.model FLAT\r\n\toption dotname\r\n\r\n\tinclude jemm.inc\t\t;common declarations\r\n\tinclude jemm32.inc\t\t;declarations for Jemm32\r\n\tinclude debug32.inc\r\n\r\n?WAIT     equ 0             ;std=0; 1=wait for \"SPACE released\" on LF\r\n;?SLOWDOWN equ 256           ;slow down factor debug displays (just LF)\r\n\r\n;--- publics/externals\r\n\r\n\tinclude extern32.inc\r\n\r\nif ?DBGOUT\r\n\r\n.text$03 segment\r\n\r\n;--- make no assumptions about DS and ES here!\r\n;--- also don't push/pop segment registers!\r\n\r\nVPUTCHR PROC public\r\n\tPUSHAD\r\nif ?KD\r\n\tcmp cs:[bKD],0\t; kernel debugger detected?\r\n\tjz @F\r\n\tmov edx, eax\t; then redirect output to it\r\n\txor eax, eax\r\n\tint 41h\r\n\tpopad\r\n\tret\r\n@@:\r\nendif\r\nif ?AUXIO\r\n\tcall AuxPutChar\r\nelse\r\n if ?USEMONO\r\n\tmov edi,0B0000h\r\n\tmov ebx,7\r\n else\r\n\tMOV EDI,0B8000h\r\n\tCMP BYTE ptr SS:[463h],0B4h\r\n\tJNZ @@IS_COLOR\r\n\tXOR DI,DI\r\n@@IS_COLOR:\r\n\tmovzx EBX, WORD PTR SS:[44Eh]\r\n\tADD EDI, EBX\r\n\tMOVZX EBX, BYTE PTR SS:[462h]\r\n endif\r\n\tmov esi, edi\r\n\tMOVZX ECX, BYTE PTR SS:[EBX*2+450h+1] ;ROW\r\n if ?USEMONO\r\n\tMOV EAX, 80\r\n else\r\n\tMOVZX EAX, WORD PTR SS:[44Ah]\r\n endif\r\n\tMUL ECX\r\n\tMOVZX EDX, BYTE PTR SS:[EBX*2+450h]   ;COL\r\n\tADD EAX, EDX\r\n\tMOV DH,CL\r\n\tLEA EDI, [EDI+EAX*2]\r\n\tMOV AL, [ESP+1Ch]\r\n\tCMP AL, 13\r\n\tJZ done\r\n\tCMP AL, 10\r\n\tJZ @@NEWLINE\r\n\tMOV SS:[EDI], AL\r\n\tMOV byte ptr SS:[EDI+1], 07\r\n\tINC DL\r\n if ?USEMONO\r\n\tcmp dl,80\r\n else\r\n\tCMP DL, BYTE PTR SS:[44Ah]\r\n endif\r\n\tJB @@OLDLINE\r\n@@NEWLINE:\r\n if ?WAIT\r\n@@wait:\r\n\tin al,64h\r\n\ttest al,1\r\n\tjz @@wait\r\n\tmov ah,al\r\n\tin al,60h\r\n\ttest ah,20h   ;PS/2 mouse?\r\n\tjnz @@wait\r\n\tcmp al,39h+80h;space released?\r\n\tjnz @@wait\r\n elseifdef ?SLOWDOWN\r\n\tmov ecx,?SLOWDOWN*100\r\n@@:\r\n\tin al,61h\r\n\tcmp al,ah\r\n\tjz @B\r\n\tmov ah,al\r\n\tloop @B\r\n endif\r\n\tMOV DL, 00\r\n\tINC DH\r\n if ?USEMONO\r\n\tCMP DH, 24\r\n else\r\n\tCMP DH, BYTE PTR SS:[484h]\r\n endif\r\n\tJBE @@OLDLINE\r\n\tDEC DH\r\n\tCALL @@SCROLL_SCREEN\r\n@@OLDLINE:\r\n\tMOV SS:[EBX*2+450h],DX\r\ndone:\r\nendif\r\n\tPOPAD\r\n\tRET\r\n\r\nife ?AUXIO\r\n\r\n;--- scroll screen up 1 line\r\n;--- esi -> start screen\r\n\r\n@@SCROLL_SCREEN:\r\n\tCLD\r\n\tmov edi,esi\r\n if ?USEMONO\r\n\tmov eax,80\r\n else\r\n\tmovzx eax,word ptr ss:[44Ah]\r\n endif\r\n\tpush eax\r\n\tlea esi, [esi+2*eax]\r\n if ?USEMONO\r\n\tmov CL, 24\r\n else\r\n\tMOV CL, SS:[484h]\r\n endif\r\n\tmul cl\r\n\tmov ecx,eax\r\n@@nextcell:\r\n\tlodsw cs:[esi]\r\n\tmov ss:[edi],ax\r\n\tadd edi,2\r\n\tloop @@nextcell\r\n\tpop ecx\r\n\tmov ax,0720h\r\n@@nextcell2:\r\n\tmov ss:[edi],ax\r\n\tadd edi,2\r\n\tloop @@nextcell2\r\n\tretn\r\nendif\r\n\r\nVPUTCHR ENDP\r\n\r\n;--- print a string which is hard-coded behind the call to this function\r\n\r\nifdef _DEBUG\r\n\tinclude dprintf.inc\r\nendif\r\n\r\nif 0\r\n\r\n;--- 386SWAT isn't compatible with Jemm, because this debugger\r\n;--- expects that extended memory is mapped into linear address space,\r\n;--- at least those parts of XMS memory that 386SWAT uses for itself.\r\n;--- Jemm simply doesn't do that; it maps the memory that it needs and\r\n;--- nothing else ( unless SB option is set, but even then it's just\r\n;--- the memory of page table 0 ).\r\n\r\n;--- check if int 3 vector still points to\r\n;--- the monitor code segment. If no, assume 386SWAT has intruded\r\n\r\nDebugBreak proc public\r\n\tcmp word ptr cs:[offset V86GDT+3*8+4],FLAT_CODE_SEL\r\n\tjz @@noswat\r\n\tpushfd\r\n\tor byte ptr [esp+1],1  ;set TF\r\n\tpopfd\r\n@@noswat:\r\n\tret\r\nDebugBreak endp\r\n\r\nendif\r\n\r\n.text$03 ends\r\n\r\n.text$04 segment\r\n\r\n;--- init debug\r\n;--- ESI -> Jemminit\r\n\r\nDebug_Init proc public\r\n\tret\r\nDebug_Init endp\r\n\r\n.text$04 ends\r\n\r\nendif\r\n\r\n\tEND\r\n"
  },
  {
    "path": "src/DEBUG16.INC",
    "content": "\r\n@dbgdef macro name_\r\nifndef name_\r\nname_ equ 0\r\nendif\r\n?DBGOUT = ?DBGOUT + name_\r\nendm\r\n\r\n?DBGOUT = 0\r\n@dbgdef ?INITRMDBG  ;1=log real-mode init\r\n@dbgdef ?XMSRMDBG   ;1=log XMS calls\r\n@dbgdef ?EMXRMDBG   ;1=log EMMXXXX0 calls\r\n@dbgdef ?UNLRMDBG   ;1=log unload\r\n\r\n;?RMDBG      equ ?INITRMDBG + ?XMSRMDBG + ?EMXRMDBG + ?UNLRMDBG  ; debug displays in real-mode\r\n\r\n?USEMONO equ 0  ;1=use monochrome monitor for dbg displays\r\n\r\nifdef _DEBUG\r\n\r\ndprintf proto c :ptr, :vararg\r\n\r\n@dprintf macro bCond, fmt, args:vararg\r\n if bCond\r\n  ifnb <args>\r\n\tinvoke dprintf, CStr(fmt), args\r\n  else\r\n\tinvoke dprintf, CStr(fmt)\r\n  endif\r\n endif\r\nendm\r\nelse\r\n@dprintf textequ <;>\r\nendif\r\n"
  },
  {
    "path": "src/DEBUG32.INC",
    "content": "\r\n@dbgdef macro name_\r\nifndef name_\r\nname_ equ 0\r\nendif\r\n?DBGOUT = ?DBGOUT + name_\r\nendm\r\n\r\n?DBGOUT = 0\r\n@dbgdef ?V86DBG  ;1=enable displays in V86 monitor\r\n@dbgdef ?V86XDBG ;1=enable more displays in V86 monitor (IRQs!)\r\n@dbgdef ?DMADBG  ;1=enable DMA related displays\r\n@dbgdef ?VDSDBG  ;1=enable VDS related displays\r\n@dbgdef ?EMSDBG  ;1=enable EMS related displays\r\n@dbgdef ?UMBDBG  ;1=enable UMB related displays\r\n@dbgdef ?A20DBG  ;1=enable A20 related displays\r\n@dbgdef ?EMBDBG  ;1=enable EMB related displays (?INTEGRATED only)\r\n@dbgdef ?EMUDBG  ;1=enable opcode emulation related displays \r\n@dbgdef ?I15DBG  ;1=enable INT 15h, AH=87h related displays\r\n@dbgdef ?VCPIDBG ;1=enable VCPI related displays\r\n@dbgdef ?VCPIXDBG;1=enable VCPI mode switch displays\r\n@dbgdef ?EMXDBG  ;1=enable EMMXXXX0 related displays \r\n@dbgdef ?POOLDBG ;1=enable memory pool related displays\r\n@dbgdef ?INITDBG ;1=enable displays in protected-mode initialisation\r\n@dbgdef ?HLTDBG  ;1=enable displays for true HLT emulation\r\n@dbgdef ?UNLDBG  ;1=enable display for \"unload\"\r\n@dbgdef ?RBTDBG  ;1=enable reboot debug\r\n@dbgdef ?EXCDBG  ;1=enable displays in exception handler\r\n@dbgdef ?PHYSDBG ;1=enable displays in physical memory handling\r\n@dbgdef ?MAPDBG  ;1=enable mapping handling\r\n@dbgdef ?NMIDBG  ;1=enable displays for NMI handling\r\n\r\n?USEMONO equ 0  ;1=use monochrome monitor for dbg displays\r\n\r\n;?DBGOUT  equ ?V86DBG + ?DMADBG + ?VDSDBG + ?EMSDBG + ?UMBDBG + ?A20DBG + ?EMBDBG + ?EMUDBG + ?I15DBG + ?VCPIDBG + ?USEMONO + ?POOLDBG + ?INITDBG + ?HLTDBG + ?UNLDBG + ?RBTDBG + ?EXCDBG + ?PHYSDBG + ?MAPDBG + ?NMIDBG\r\n\r\nCStr macro text:vararg\r\nlocal sym\r\n.text$03s segment byte flat 'CODE'\r\nsym db text,0\r\n.text$03s ends\r\n\texitm <offset sym>\r\nendm\r\n\r\n@WaitKey macro keycode, bCond\r\nlocal sm1\r\nif bCond\r\n        pushfd\r\n        push    eax\r\nsm1:\r\n        in      al,64h      ;data from keyboard controller?\r\n        test    al,1\r\n        jz      sm1\r\n        mov     ah,al\r\n        in      al,60h\r\n        test    ah,20h      ;mouse device?\r\n        jnz     sm1\r\n        cmp     al,keycode+80h  ;wait for key released\r\n        jnz     sm1\r\n        pop     eax\r\n        popfd\r\nendif\r\nendm\r\n\r\n;--- formatted output ( since v5.83 )\r\nifdef _DEBUG\r\n\r\ndprintf proto c :ptr, :vararg\r\n\r\n@dprintf macro bCond, fmt, args:vararg\r\n if bCond\r\n  ifnb <args>\r\n\tinvoke dprintf, CStr(fmt), args\r\n  else\r\n\tinvoke dprintf, CStr(fmt)\r\n  endif\r\n endif\r\nendm\r\nelse\r\n@dprintf macro bCond, fmt, args:vararg\r\nendm\r\nendif\r\n\r\n@DebugBreak macro\r\nifdef _DEBUG\r\n        int 3\r\nendif\r\nendm\r\n\r\n@CheckBlockIntegrity macro\r\nif ?POOLDBG\r\n        call CheckBlockIntegrity\r\nendif\r\nendm\r\n\r\n"
  },
  {
    "path": "src/DEBUGSYS.INC",
    "content": "\r\n;\r\n;  Real mode Debugger services:\r\n;\r\n\r\nD386_RM_Int\t\tequ 68h\t; hooked by the debugger in real mode.\r\n\r\nD386_Id\t\t\tequ 0F386h ; debugger identification code\r\n\r\nD386_MIN\t\t\tequ 43h\t; minimum INT 68 function code\r\nD386_Identify\t\tequ 43h\t; returns debugger identification, if debugger\r\nD386_Prepare_PMode\tequ 44h\t; partially prepare for protected mode operation\r\nD386_Real_Mode_Init\tequ 45h\t; re-init fro real mode after entering pmode\r\nD386_Set_Switches\tequ 46h\t; set debugging switches\r\nD386_Execute_Cond\tequ 47h\t; execute conditional BP (/B option)\r\nD386_Free_Segment\tequ 48h\t; undefine the real mode segment's symbols\r\nD386_Set_Baudrate\tequ 49h\t; set com port baud rate\r\nD386_Reinit\t\t\tequ 4ah\t; reinitialize debugger for protected mode\r\nD386_Def_Deb_Segs\tequ 4bh\t; define debugger's segments\r\nD386_Set_Com_Port\tequ 4ch\t; set com port number\r\nD386_Link_Sym\t\tequ 4dh\t; link sym file map\r\nD386_Unlink_Sym\t\tequ 4eh\t; unlink sym file maps\r\nD386_Remove_Segs\tequ 4fh\t; remove any undefined segments from the\r\nD386_Load_Segment\tequ 50h\t; defines the actual segment/selector for a\r\nD386_Display_Char\tequ 51h\t; display a character to the debugging terminal\r\nD386_Display_Str\tequ 52h\t; display a string to the debugging terminal\r\nD386_IsVxDInstalled\tequ 53h\t; returns if debug VxD has been installed\r\nD386_VxDInstall\t\tequ 54h\t; sets that the debug VxD installed/uninstalled\r\nD386_RegisterDotCmd\tequ 55h\t; registers dot command\r\nD386_DeRegisterDotCmd\tequ 56h\t; de-registers dot command\r\nD386_Printf\t\t\tequ 57h\t; Printf\r\nD386_Link_Sym_Phys\tequ 58h\t; link symbol file with physical address\r\nD386_CheckMap\t\tequ 59h\t; DX:DI = pointer to module name\r\nD386_SetAutoLoadSym\tequ 5ah\t; (BL) != 0, auto load symbols\r\nD386_SetTeftiPort\tequ 5bh\t; (BX) = TEFTI port address\r\nD386_ExecDebugCommand\tequ 5ch\t; execute debugger command script\r\nD386_LoadCodeDataHigh\tequ 5dh\t; makes the debugger copy its code/data high\r\nD386_SetWinVersion\tequ 5eh\t; sets Windows version number\r\nD386_ScanChar\t\tequ 5fh ; scan for character\r\nD386_UnGetChar\t\tequ 60h ; ungetchar scaned character, AL = char\r\nD386_Stop\t\t\tequ 61h\t; stop at the CS:IP specified\r\nD386_MAX\t\t\tequ 61h\t; maximum INT 68 function code\r\n\r\n; D386_Load_Segment type equates:\r\n\r\nST_code_sel\tequ 0\t\t; code selector\r\nST_data_sel\tequ 1\t\t; data selector\r\n\r\nST_code_seg\tequ 10h\t\t; code segment\r\nST_data_seg\tequ 11h\t\t; data segment\r\n\r\nST_dual_code\tequ 40h\t\t; code segment and selector\r\nST_dual_data\tequ 41h\t\t; data segment and selector\r\n\r\nST_device_code\tequ 80h\t\t; device driver code segment\r\nST_device_data\tequ 81h\t\t; device driver data segment\r\n\r\n; D386_Load_Segment device load parameters structure\r\n\r\nD386_Device_Params STRUC\r\nDD_logical_seg\tdw  ?\t; logical segment # from map\r\nDD_actual_sel\tdw  ?\t; actual selector value\r\nDD_base\t\t\tdd  ?\t; linear address offset for start of segment\r\nDD_length\t\tdd  ?\t; actual length of segment\r\nDD_name\t\t\tdf  ?\t; 16:32 ptr to null terminated device name\r\nDD_sym_name\t\tdf  ?\t; 16:32 ptr to null terminated symbolic\r\nDD_alias_sel\tdw  ?\t; alias selector value (0 = none)\r\nD386_Device_Params ENDS\r\n\r\nWDEB_INT2F_STARTING\t\t\tequ\t0\t; first time starting\r\nWDEB_INT2F_ENDING\t\t\tequ\t1\t; first time ending\r\nWDEB_INT2F_NESTED_STARTING\tequ\t2\t; start on level of nesting\r\nWDEB_INT2F_NESTED_ENDING\tequ\t3\t; end one level of nesting\r\n\r\n; PMINIT routine functions\r\n\r\nPMINIT_INIT_IDT\t\t\t\t\tequ\t0\t; (ES:EDI) = pointer to PM IDT\r\nPMINIT_INIT_PAGING\t\t\t\tequ\t1\t; (BX) = phys-linear selector \r\nPMINIT_ENABLE_DEBUG_QUERYS\t\tequ\t2\t; enables dot commands, etc.\r\nPMINIT_INIT_SPARE_PTE\t\t\tequ\t3\t; (EBX) = lin addr of spare PTE\r\nPMINIT_SET_ENTER_EXIT_VMM\t\tequ\t4\t; (EBX) = Enter VMM routine addr\r\nPMINIT_GET_SIZE_PHYS\t\t\tequ\t5\t; get debugger size/phys addr\r\nPMINIT_SET_BASE_SPARE_PTE\t\tequ\t6\t; set debugger base/spare PTE\r\nPMINIT_ENABLE_MEMORY_CONTEXT\tequ\t7\t; enables mem context functions\r\nPMINIT_MAX\t\t\t\t\t\tequ\t7\r\n\r\nif 0\r\nWdebVCPIInfo\t  STRUCT\r\n\tfnVCPI\tdf\t?\t; VCPI protect mode server entry point\r\n\trdsVCPI\tdw\t?\t; Selector for VCPI server\r\n\tlaVTP\tdd\t?\t; linear address of data structure containing\r\n\tPort67\tdw\t?\t; Qualitas magic port for emulating INT 67h\r\nWdebVCPIInfo\t  ENDS\r\n\r\nVTP\tstruct\r\n\tzaCr3VTP\tdd\t0\t; physical addr of page directory\r\n\tlaGdtrVTP\tdd\t0\t; linear addr in first meg of gdtr\r\n\tlaIdtrVTP\tdd\t0\t; linear addr in first meg of idtr\r\n\tselLdtVTP\tdw\t0\t; selector of ldt\r\n\tselTrVTP\tdw\t0\t; selector of tr\r\n\tipVTP\t\tdw\t0\t; 48-bit address of protect\r\n\tunusedVTP\tdw\t0\t;   mode entry point to xfer to\r\n\tcsVTP\t\tdw\t0\t;\r\nVTP\tends\r\n\r\nVCPI_RM_CALLOUT_INT\tequ\t67h\t; v86 mode call to VCPI server\r\nVCPI_PROT_ENTRY\t\tequ\t0DE0CH\r\nendif\r\n\r\n\r\n;  Protected mode Debugger services:\r\n\r\nDebug_Serv_Int\t     equ 41h\t; Interrupt that calls Deb386 to perform\r\n\r\nDS_Out_Char\t\tequ     0\t; function to display the char in DL\r\nDS_In_Char\t\tequ     1\t; function to read a char into AL\r\nDS_Out_Str\t\tequ     2\t; function to display a NUL terminated string\r\nDS_Is_Char\t\tequ     3\t; Non blocking In_Chr\r\nDS_Out_Str16\tequ    12h\t; function to display a NUL terminated string\r\nDS_ForcedGO16\tequ    40h\t; enter the debugger and perform the equivalent\r\nDS_LinkMap\t\tequ    45h\t; DX:(E)DI = ptr to paragraph in front of map\r\nDS_UnlinkMap\tequ    46h\t; DX:(E)DI = ptr to paragraph in front of map\r\nDS_CheckMap\t\tequ    47h\t; DX:(E)DI = pointer to module name\r\nDS_IsAutoLoadSym equ   48h\t; returns AX != 0, auto load symbols\r\nDS_DebLoaded\tequ    4Fh\t; check to see if the debugger is installed and\r\nDS_DebPresent\tequ   0F386h\r\nDS_LoadSeg\t\tequ    50h\t; define a segment value for the\r\nDS_LoadSeg_32\tequ  0150h\t; Define a 32-bit segment for Windows 32\r\nDS_MoveSeg\t\tequ    51h\t; notify the debugger that a segment has moved\r\nDS_FreeSeg\t\tequ    52h\t; notify the debugger that a segment has been\r\nDS_FreeSeg_32\tequ  0152h\t; notify the debugger that a segment has been\r\nDS_DGH\t\t\tequ    56h\t; register \"dump global heap\" handler\r\nDS_DFL\t\t\tequ    57h\t; register \"dump free list\" handler\r\nDS_DLL\t\t\tequ    58h\t; register \"dump LRU list\" handler\r\nDS_StartTask\tequ    59h\t; notify debugger that a new task is starting\r\nDS_Kernel_Vars\tequ    5ah\t; Used by the Windows kernel to tell the\r\nDS_VCPI_Notify\tequ    5bh\t; notify debugger that DOS extender is\r\nDS_ReleaseSeg\tequ    5ch\t; This does the same as a DS_FreeSeg, but\r\nDS_User_Vars    equ    5dh\t; DS:SI = pointer to an array of offsets:\r\nDS_POSTLOAD\t\t=\t60h\t; Used by the RegisterPTrace interface\r\nDS_EXITCALL\t\t=\t62h\t; Somebody will fill these in if we ever\r\nDS_INT2\t\t\t=\t63h\t; figure out what they are supposed to do.\r\nDS_LOADDLL\t\t=\t64h\r\nDS_DELMODULE\t=\t65h\r\n\r\nDS_NEWTASK\t\t=\t0BH\r\nDS_FLUSHTASK\t=\t0CH\r\nDS_SWITCHOUT\t=\t0DH\r\nDS_SWITCHIN\t\t=\t0EH\r\n\r\nDS_IntRings\t\tequ    20h\t; function to tell debugger which INT 1's & 3's\r\nDS_IncludeSegs\tequ    21h\t; function to tell debugger to go ahead and\r\nMaxDebugSegs \t= 20\r\n\r\nDS_CondBP\t\tequ 0F001h\t; conditional break pt, if the command line\r\nDS_ForcedBP\t\tequ 0F002h\t; break pt, which accomplishes the same thing\r\nDS_ForcedGO\t\tequ 0F003h\t; enter the debugger and perform the equivalent\r\nDS_HardINT1\t\tequ 0F004h\t; check to see if INT 1 hooked for all rings\r\nDS_Out_Symbol\tequ    0Fh\t; find the symbol nearest to the address in\r\nDS_Disasm_Ins\tequ    10h\t; function to disassemble the instruction\r\n\r\nDS_JumpTableStart       equ 70h\r\nDS_RegisterDotCommand   equ 70h\r\nDS_RegisterDotCommand16 equ 71h\r\nDS_DeRegisterDotCommand equ 72h\r\nDS_Printf\t            equ\t73h\r\nDS_Printf16             equ 74h\r\nDS_GetRegisterSet       equ 75h\r\nDS_SetAlternateRegisterSet equ 76h\r\nDS_GetCommandLineChar   equ 77h\r\nDS_EvaluateExpression   equ 78h\r\nDS_VerifyMemory\t        equ 79h\r\nDS_PrintRegisters       equ 7ah\r\nDS_PrintStackDump       equ 7bh\r\nDS_SetThreadID\t        equ 7ch\r\nDS_ExecDebugCommand     equ 7dh\r\nDS_GetDebuggerInfo      equ 7eh\r\nDS_CheckFault           equ 7fh\r\nDS_SetBreak             equ 80h\r\nDS_RedirectExec         equ 81h\r\nDS_PassOnDebugCommand   equ 82h\r\nDS_TrapFault            equ 83h\r\nDS_SetStackTraceCallBack equ 84h\r\nDS_RemoveSegs           equ 85h\r\nDS_DefineDebugSegs      equ 86h\r\nDS_SetBaudRate          equ 87h\r\nDS_SetComPort           equ 88h\r\nDS_ChangeTaskNum        equ 89h\r\nDS_ExitCleanup          equ 8ah\r\nDS_InstallVGAHandler    equ 8bh\r\nDS_GetComBase\t        equ\t8ch\r\nDS_GetSymbol            equ 8dh\r\nDS_CopyMem              equ 8eh\r\nDS_JumpTableEnd         equ 8eh\r\n\r\nSaveRegs_Struc\tstruc\r\n\tDebug_EAX\t\tdd\t?\r\n\tDebug_EBX\t\tdd\t?\r\n\tDebug_ECX\t\tdd\t?\r\n\tDebug_EDX\t\tdd\t?\r\n\tDebug_ESP\t\tdd\t?\r\n\tDebug_EBP\t\tdd\t?\r\n\tDebug_ESI\t\tdd\t?\r\n\tDebug_EDI\t\tdd\t?\r\n\tDebug_ES\t\tdw\t?\r\n\tDebug_SS\t\tdw\t?\r\n\tDebug_DS\t\tdw\t?\r\n\tDebug_FS\t\tdw\t?\r\n\tDebug_GS\t\tdw\t?\r\n\tDebug_EIP\t\tdd\t?\r\n\tDebug_CS\t\tdw\t?\r\n\t\t\t\tdd\t?\r\n\tDebug_EFlags\t\tdd\t?\r\n\tDebug_CR0\t\tdd\t?\r\n\tDebug_GDT\t\tdq\t?\r\n\tDebug_IDT\t\tdq\t?\r\n\tDebug_LDT\t\tdw\t?\r\n\tDebug_TR\t\tdw\t?\r\n\tDebug_CR2\t\tdd\t?\r\n\tDebug_CR3\t\tdd\t?\r\n\tDebug_DR0\t\tdd\t?\r\n\tDebug_DR1\t\tdd\t?\r\n\tDebug_DR2\t\tdd\t?\r\n\tDebug_DR3\t\tdd\t?\r\n\tDebug_DR6\t\tdd\t?\r\n\tDebug_DR7\t\tdd\t?\r\n\tDebug_DR7_2\t\tdd\t?\r\n\tDebug_TR6\t\tdd\t?\r\n\tDebug_TR7\t\tdd\t?\r\n\tDebug_TrapNumber\tdw\t-1\t; -1 means no trap number\r\n\tDebug_ErrorCode\t\tdw\t0\t; 0 means no error code\r\nSaveRegs_Struc ends\r\n\r\nDebInfoBuf\tstruc\r\n\tDIB_MajorVersion\tdb\t0\r\n\tDIB_MinorVersion\tdb\t0\r\n\tDIB_Revision\t\tdb\t0\r\n\t\t\t\tdb\t0\t; reserved\r\n\tDIB_DebugTrap16\t\tdd\t0\t; send 16 bit trap to debugger\r\n\tDIB_DebugTrap32\t\tdf\t0\t; send 32 bit trap to debugger\r\n\tDIB_DebugBreak16\tdd\t0\t; 16 bit break in debugger\r\n\tDIB_DebugBreak32\tdf\t0\t; 32 bit break in debugger\r\n\tDIB_DebugCtrlC16\tdd\t0\t; 16 bit check for ctrl C\r\n\tDIB_DebugCtrlC32\tdf\t0\t; 32 bit check for ctrl C\r\nDebInfoBuf\tends\r\n\r\nBreakStruc\tstruc\r\n\tBS_BreakEIP\t\tdd\t0\t; CS:EIP, SS:ESP to go to\r\n\tBS_BreakCS\t\tdw\t0\t;  on a error or ctrlc break\r\n\tBS_BreakESP\t\tdd\t0\r\n\tBS_BreakSS\t\tdw\t0\r\nBreakStruc\tends\r\n\r\nRedirectExecStruc\tstruc\r\n\tRDE_fpbufDebugCommand\tdf\t0\t; debugger command script\r\n\tRDE_cbDebugCommand\tdw\t0\t; debugger command script len\r\n\tRDE_fpszInput\t\tdf\t0\t; input stream pointer\r\n\tRDE_usFlags\t\tdw\t0\t; reserved (must be 0)\r\n\tRDE_cbOutput\t\tdd\t0\t; size of output buffer\r\n\tRDE_fpbufOutput\t\tdf\t0\t; output buffer pointer\r\nRedirectExecStruc\tends\r\n\r\nREPEAT_FOREVER_CHAR\tequ\t0feh\t\t; send next character until\r\n\t\t\t\t\t\t;  end of debugger command\r\n\r\nAddrS\tstruc\t\t\t\t\t; for printf service\r\n\tAddrOff\t\tdd\t0\r\n\tAddrSeg\t\tdw\t0\r\n\tAddrType\tdb\t0\r\n\tAddrSize\tdb\t0\r\n\tAddrTask\tdw\t0\r\nAddrS\tends\r\n\r\nAddrTypeSize\tequ\tword ptr AddrType\r\n\r\nEXPR_TYPE_SEG\t\tequ\t00000001b\t; address type segment:offset\r\nEXPR_TYPE_SEL\t\tequ\t00001001b\t; address type selector:offset\r\nEXPR_TYPE_LIN\t\tequ\t00000010b\t; address type linear\r\nEXPR_TYPE_PHY\t\tequ\t00001010b\t; address type physical\r\nEXPR_TYPE_LOG\t\tequ\t00001000b\t; logical address (no sel yet)\r\n\r\nDEBUG_FAULT_TYPE_V86\t\tequ\t00000001b\r\nDEBUG_FAULT_TYPE_PM\t\tequ\t00000010b\r\nDEBUG_FAULT_TYPE_RING0\t\tequ\t00000100b\r\nDEBUG_FAULT_TYPE_FIRST\t\tequ\t00001000b\r\nDEBUG_FAULT_TYPE_LAST\t\tequ\t00010000b\r\n\r\n;\r\n;   Interrupt and services that Win386 provides to the debugger\r\n;\r\n\r\nWin386_Query_Int      equ 22h\t; interrupt for Win386 protected mode\r\n\r\nWin386_Alive\t      equ 0\t; query Win386 installation; AX=Win386_Q_Ack if installed \r\n;Win386_Q_Ack\t      equ 0F386h\t; good response from various debug funcs\r\nWin386_Query\t      equ 1\t; query; DS:ESI->SaveRegs struct\r\nWin386_PhysToLinr     equ 2\t; converts phys addr in ESI (size CX) to linear in ESI (ax=1 if ok, else 0)\r\nWin386_AddrValid      equ 3\t; get validity of a linear address ( ESI, size CX )\r\nWin386_MapVM\t      equ 4\t; make sure VM's low memory is mapped in\r\nWin386_UnmapVM\t      equ 5\t; map out the VM's low memory\r\nWin386_GetDLAddr      equ 6\t; return offset of dyna-link service; hiword(ebx)=device, loword(ebx)=service\r\nWin386_GetVXDName     equ 7\t; get vxd owner of a memory address in EDX, DS:ESI=buffer\r\nWin386_GetPDE         equ 8 ; get pde for a context\r\nWin386_GetFrame       equ 9 ; get phys addr for not pres ptes\r\nWin386_GetLDTAddress  equ 10; BX = thread\r\nWin386_GetThreadID    equ 11; AX = Current Thread ID\r\nWin386_GetTSHandler   equ 12; return offset of transfer-space\r\nWin386_GetArplHandler equ 13; return offset of ARPL fault handler in EAX, EBX=real mode seg:ofs\r\nMax_Win386_Services   equ 13\r\n"
  },
  {
    "path": "src/DEV.ASM",
    "content": "\r\n;*** implements EMMXXXX0 device\r\n;--- public domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n;--- DOS device request\r\n\r\nrequest_hdr struct\r\nrhSize      db  ?           ; +0 number of bytes stored\r\nrhUnit_id   db  ?           ; +1 unit ID code\r\nrhCmd       db  ?           ; +2 command code\r\nrhStatus    dw  ?           ; +3 status word\r\nrhReserved  db  8 dup (?)   ; +5 reserved\r\nrhMedia     db  ?           ; +13\r\nrhBuffer    dd  ?           ; +14 transfer buffer (SSSS:OOOO)\r\nrhCount     dw  ?           ; +18 buffer size\r\nrequest_hdr ends\r\n\r\n.text$01 SEGMENT\r\n\r\ndwReqPtr        dd  0   ; device strategy request ptr\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n;--- EMMXXXX0 strategy routine\r\n\r\nEMMXXXX0_Strategy proc public\r\n    call Simulate_Far_Ret                ; do a RETF in V86\r\n    movzx eax,word ptr [ebp].Client_Reg_Struc.Client_ES\r\n    movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_EBX\r\n    shl eax, 4\r\n    add eax, ecx\r\n    mov [dwReqPtr], eax\r\n    ret\r\nEMMXXXX0_Strategy endp\r\n\r\n;--- EMMXXXX0 interrupt routine\r\n\r\nEMMXXXX0_Interrupt proc public\r\n    call Simulate_Far_Ret                ; do a RETF in V86\r\n    mov ecx, [dwReqPtr]\r\nif ?EMMXXXX0\r\n    mov al, [ecx].request_hdr.rhCmd\r\n    @dprintf ?EMXDBG, <\"EMMXXXX0 request %X\",10>,al\r\n    cmp al, 3           ;IOCTL input?\r\n    jz @@ioctl_read\r\n    cmp al, 8           ;write?\r\n    jz @@resp_writeerr\r\n    cmp al, 9           ;write+verify?\r\n    jz @@resp_writeerr\r\n    cmp al, 10          ;write status\r\n    jz @@resp_ok\r\n    cmp al, 11          ;write+flush?\r\n    jz @@resp_writeerr\r\n    cmp al, 12          ;IOCTL output?\r\n    jz @@ioctl_write\r\n    cmp al, 13          ;open device?\r\n    jz @@resp_ok\r\n    cmp al, 14          ;close device?\r\n    jz @@resp_ok\r\n    mov ax, 8103h\r\n    jmp @@device_done\r\n@@ioctl_read:\r\n    push ecx\r\n    call IoctlRead\r\n    pop ecx\r\n    jnc @@resp_ok\r\n@@resp_readerr:\r\n    mov ax, 810Bh\r\n    jmp @@device_done\r\n@@ioctl_write:\r\n    push ecx\r\n    call IoctlWrite\r\n    pop ecx\r\n    jnc @@resp_ok\r\n@@resp_writeerr:\r\n    mov ax, 810Ah\r\n    jmp @@device_done\r\n@@resp_ok:\r\n    mov ax, 100h\r\n@@device_done:\r\nelse\r\n    mov ax, 8103h\r\nendif\r\n    mov [ecx].request_hdr.rhStatus, ax\r\n    ret\r\n    align 4\r\nEMMXXXX0_Interrupt endp\r\n\r\nif ?EMMXXXX0\r\n\r\n;--- read ioctl EMMXXXX0 device\r\n;--- inp: ECX=request header\r\n;--- modifies eax,ebx,ecx,edx,esi,edi\r\n\r\nIoctlRead proc\r\n    mov eax, [ecx].request_hdr.rhBuffer\r\n    movzx ebx, ax\r\n    shr eax, 12\r\n    and al, 0F0h\r\n    add ebx, eax        ;ebx=buffer linear address\r\n\r\n    movzx edx, [ecx].request_hdr.rhCount\r\n    mov al, [EBX+0]\r\n    cmp al, EMMDEV_GETAPI  ;get \"API\"\r\n    jz  @@func00\r\nif 0\r\n    cmp al, EMMDEV_GEMMIS  ;GEMMIS not supported\r\n    jz @@func01\r\nendif\r\n    cmp al, EMMDEV_VERSION ;get version\r\n    jz @@func02\r\n    cmp al, EMMDEV_GETRES  ;get Emm386 resident segment/size?\r\n    jz @@func04\r\n    cmp al, EMMDEV_SYSVARS ;get system vars\r\n    jz @@func06\r\n    cmp al, EMMDEV_GETUMBS ;get UMBs\r\n    jz @@func07\r\nif ?SERVTABLE\r\n    cmp al, EMMDEV_GETSTAB ;get VMM service table info\r\n    jz @@func08\r\nendif\r\n    jmp @@error\r\n@@func00:\r\n    cmp edx,6   ;bytes to read\r\n    jb @@error\r\n    mov word ptr [ebx+0], 0028h\r\n    mov dword ptr [ebx+2], 0    ;API entry\r\n    jmp @@ok\r\n@@func02:\r\n    cmp edx,2   ;bytes to read\r\n    jb @@error\r\n    mov word ptr [ebx+0], ?VERSIONHIGH + ?VERSIONLOW * 256\r\n    jmp @@ok\r\n@@func04:\r\n    cmp edx,4   ;bytes to read\r\n    jb @@error\r\n    mov eax, [dwRSeg]\r\n    mov [ebx+0], eax\r\n    jmp @@ok\r\n@@func06:\r\n\r\n    cmp edx,16  ;bytes to read\r\n    jb @@error\r\n    mov al, [bNoEMS]\r\n    mov [ebx].EMX06.e06_NoEMS, al\r\n    xor eax, eax\r\n    cmp [bNoFrame],0\r\n    jnz @@nopf\r\n    mov ah, [EMSPage2Segm]      ;get mapping of phys page 0\r\n@@nopf:\r\n    mov [ebx].EMX06.e06_Frame, ax\r\n    mov al, [bNoVCPI]\r\n    mov [ebx].EMX06.e06_NoVCPI, al\r\n    cmp edx,24                  ;to get VCPI memory info, we need 24 byte\r\n    jb @@novcpimem\r\n    mov eax, [pmem.dwMaxMem4K]  ;VCPI 4 kB pages\r\n    mov [ebx].EMX06.e06_VCPITotal, eax\r\n    mov eax, [pmem.dwUsedMem4K]\r\n    mov [ebx].EMX06.e06_VCPIUsed, eax\r\n@@novcpimem:\r\nif 1;?DMAPT\r\n    mov eax, [vdsstat.DMABuffStartPhys]\r\nelse\r\n    xor eax, eax\r\nendif\r\n    mov [ebx].EMX06.e06_DMABuff, eax\r\nif 1;?DMAPT\r\n    mov eax, [vdsstat.DMABuffSize]\r\n    shr eax, 10\r\nelse\r\n    xor eax, eax\r\nendif\r\n    mov [ebx].EMX06.e06_DMASize, ax\r\nif ?VME\r\n    mov al,1\r\n    test byte ptr [dwFeatures],2\r\n    jz @@novme\r\n    .586p\r\n    mov eax, CR4\r\n    .386\r\n    and al,1\r\n    xor al,1\r\n@@novme:\r\n    mov [ebx].EMX06.e06_NoVME,al\r\nendif\r\nif ?PGE\r\n    mov al,1\r\n    test byte ptr [dwFeatures+1],20h\r\n    jz @@nopge\r\n    .586p\r\n    mov eax, CR4\r\n    .386\r\n    shr al,7\r\n    xor al,1\r\n@@nopge:\r\n    mov [ebx].EMX06.e06_NoPGE,al\r\nendif\r\nif ?A20PORTS or ?A20XMS\r\n    mov al,[bNoA20]\r\n    mov [ebx].EMX06.e06_NoA20,al\r\nendif\r\n@@ok:\r\n    clc\r\n    ret\r\n@@error:\r\n    stc\r\n    ret\r\n@@func07:\r\n    cmp edx, UMB_MAX_BLOCKS * size UMBBLK   ;buffer large enough to get the UMB entries?\r\n    jb @@error\r\n    mov edi, ebx\r\n    mov esi, offset UMBsegments\r\n    mov ecx, UMB_MAX_BLOCKS\r\n    cld\r\n    rep movsd\r\n    clc\r\n    ret\r\nif ?SERVTABLE\r\n@@func08:\r\n    cmp edx, size EMX08\r\n    jb  @@error\r\n    mov [ebx].EMX08.e08_ServiceTable, offset vmm_service_table\r\n    mov [ebx].EMX08.e08_pCallBack, offset bptable + BPTABLE.pInt19\r\n    mov eax, [dwRSeg]\r\n    shl eax, 16\r\n    mov ecx, [bpstart]\r\n    inc ecx     ;use second static BP ( for INT 19h )\r\n    sub ecx, [dwRes]\r\n    mov ax, cx\r\n    mov [ebx].EMX08.e08_CallBackRM, eax\r\n    mov word ptr [ebx].EMX08.e08_GDTR, GDT_SIZE-1\r\n    mov dword ptr [ebx].EMX08.e08_GDTR+2, offset V86GDT\r\n    mov eax,dword ptr [IDT_PTR+2]\r\n    mov word ptr [ebx].EMX08.e08_IDTR, 7FFh\r\n    mov dword ptr [ebx].EMX08.e08_IDTR+2, eax\r\n    mov [ebx].EMX08.e08_TR, V86_TSS_SEL\r\n    mov [ebx].EMX08.e08_FlatCS, FLAT_CODE_SEL\r\n    ret\r\nendif\r\n    align 4\r\n\r\nIoctlRead endp\r\n\r\n;--- all registers may be modified!\r\n;--- inp: ECX=request header\r\n;--- the \"update\" command is EMMDEV_UPDATE\r\n\r\nIoctlWrite proc\r\n\r\n    mov eax, [ecx].request_hdr.rhBuffer\r\n    movzx esi, ax\r\n    shr eax, 12\r\n    and al, 0F0h\r\n    add esi, eax\r\n\r\n    lods byte ptr [esi]  ;function to call\r\n    cmp al, EMMDEV_UPDATE\r\n    jz @@func15\r\n@@error:\r\n    stc\r\n    ret\r\n@@func15:\r\n\r\n;--- esi -> EMX15W variable\r\n\r\n    movzx eax, [ecx].request_hdr.rhCount  ;buffer size\r\n    cmp eax, 5\r\n    jb @@error\r\n\r\n    lods byte ptr [esi]  ;e15_bVME\r\nif ?VME\r\n    cmp al,-1\r\n    jz @@novme\r\n    xor al,1\r\n    call SetVME\r\n@@novme:\r\nendif\r\n    lods byte ptr [esi]  ;e15_bA20\r\nif ?A20PORTS or ?A20XMS\r\n    cmp al,-1\r\n    jz @@noa20\r\n    and al, al\r\n    jz @@a20emuon\r\n    push eax\r\n    mov al,1\r\n    call A20_Set     ;enable A20 gate\r\n    pop eax\r\n@@a20emuon:\r\n    mov [bNoA20], al\r\n@@noa20:\r\nendif\r\n    lods byte ptr [esi]  ;e15_bVCPI\r\n    cmp al,-1\r\n    jz @@novcpi\r\n    mov [bNoVCPI],al\r\n@@novcpi:\r\n    lods byte ptr [esi]  ;e15_bPGE\r\nif ?PGE\r\n    cmp al,-1\r\n    jz @@nopge\r\n    test byte ptr [dwFeatures+1], 20h   ;PGE supported?\r\n    jz @@nopge\r\n    and al,1\r\n    xor al,1\r\n    mov [bPageMask],al\r\n if 0\r\n    mov edi, @GetPTEAddr(?PAGETAB0) ;start of pagetab 0\r\n    mov ecx,110h+1          ;00000000-00110FFF\r\n@@FILL_PAGETAB0:\r\n    mov edx, [edi]\r\n    and dh,not 1        ;mask out G\r\n    or dh,al\r\n    MOV [EDI],EDX\r\n    ADD EDI,4\r\n    loop @@FILL_PAGETAB0\r\n endif\r\n    .586p\r\n    mov ecx, CR4\r\n    shl al, 7\r\n    and cl, not 80h\r\n    or cl, al\r\n    mov CR4, ecx\r\n    .386\r\n@@nopge:\r\nendif\r\n    clc\r\n    ret\r\n    align 4\r\nIoctlWrite endp\r\n\r\nendif\r\n\r\n.text$03    ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/DMA.INC",
    "content": "\n;--- ports ISA DMA controller\n\nDMA_STATUS_CMD     equ 8\t; R status / W command\nDMA_REQUEST        equ 9\t; W b0+1: channel, b2: 1=enable?, b3-7:rsvd\nDMA_SINGLEMASK     equ 10\t; W b0+1: channel to mask, b2: 0=clear/1=mask, b3-7:rsvd\nDMA_MODE           equ 11\t; W b0+1: channel, b2-7:see below\nDMA_FLIPFLOP       equ 12\t; W\nDMA_IMM_RESET      equ 13\t; R immediate / W master reset (masks all 4 channels)\nDMA_MASK_RESET     equ 14\t; W master enable (unmasks all 4 channels)\nDMA_MULTIMASK      equ 15\t; W b0-3: 1=channel[0-3] masked, 0=channel[0-3] unmasked\n\n;--- bits in DMA_MODE\nDMA_MODE_OPERATION equ 1100b; \"op\" bits 2+3\nDMA_MODE_OP_VERIFY equ 0000b\nDMA_MODE_OP_WRITE  equ 0100b\nDMA_MODE_OP_READ   equ 1000b\n\nDMA_MODE_AUTO      equ 10h\t; b4\nDMA_MODE_DIRECTION equ 20h\t; b5: direction 0=increment, 1=decrement\n;--- b6-7: 00=demand,01=single,10=block,11=cascade\n\nDMA_BASE_16BIT     equ 0C0h\n\nDMA_STATUS_CMD16   equ DMA_BASE_16BIT + DMA_STATUS_CMD*2\nDMA_SINGLEMASK16   equ DMA_BASE_16BIT + DMA_SINGLEMASK*2\nDMA_MODE16         equ DMA_BASE_16BIT + DMA_MODE*2\nDMA_FLIPFLOP16     equ DMA_BASE_16BIT + DMA_FLIPFLOP*2\nDMA_IMM_RESET16    equ DMA_BASE_16BIT + DMA_IMM_RESET*2\nDMA_MASK_RESET16   equ DMA_BASE_16BIT + DMA_MASK_RESET*2\nDMA_MULTIMASK16    equ DMA_BASE_16BIT + DMA_MULTIMASK*2\n\nMAXDMACHANNEL equ 8\n"
  },
  {
    "path": "src/DPRINTF.INC",
    "content": "\r\n;--- printf for debug displays\r\n;--- assume CS and SS flat;\r\n;--- no assumptions about DS/ES\r\n\r\n;--- i64toa(long long n, char * s, int base);\r\n;--- convert 64-bit long long to string\r\n\r\ni64toa proc stdcall uses edi number:qword, outb:ptr, base:dword\r\n\r\n\tmov ch,0\r\n\tmov edi, base\r\n\tmov eax, dword ptr number+0\r\n\tmov esi, dword ptr number+4\r\n\tcmp edi,-10\r\n\tjne @F\r\n\tneg edi\r\n\tand esi,esi\r\n\tjns @F\r\n\tneg esi\r\n\tneg eax\r\n\tsbb esi,0\r\n\tmov ch,'-'\r\n@@:\r\n\tmov ebx,outb\r\n\tadd ebx,22\r\n\tmov byte ptr ss:[ebx],0\r\n@@nextdigit:\r\n\tdec ebx\r\n\txor edx,edx\r\n\txchg eax,esi\r\n\tdiv edi\r\n\txchg eax,esi\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[ebx],dl\r\n\tmov edx, eax\r\n\tor edx, esi\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tdec ebx\r\n\tmov ss:[ebx],ch\r\n@@:\r\n\tmov eax,ebx\r\n\tret\r\n\r\ni64toa endp\r\n\r\n;--- dprintf has to preserve eflags, but usage of local vars will include a \"sub esp,xxx\"!\r\n;--- hence dprintf() needs a special prologue:\r\n\r\n@dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms\r\n;if flag\r\n;  if parmbyte + localbyte\r\n\tpush ebp\r\n\tmov ebp,esp\r\n;  endif\r\n;  if localbyte\r\n\tlea esp,[esp-localbyte]\r\n;  endif\r\n;endif\r\n;  for r,reglist\r\n;\tpush r\r\n;  endm\r\n  exitm %localbyte\r\nendm\r\n\r\n\tOPTION PROLOGUE: @dprologue\r\n\r\ndprintf proc c public fmt:ptr, args:vararg\r\n\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal size_:dword\r\nlocal fillchr:dword\r\nlocal szTmp[24]:byte\r\n\r\n\tpushad\r\n\tpushfd\r\n\tcld\r\n\tlea edi,args\r\n@@L335:\r\n\tmov esi,fmt\r\nnextchar:\r\n\tlodsb cs:[esi]\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall dochar\r\n\tjmp nextchar\r\ndone:\r\n\tpopfd\r\n\tpopad\r\n\tret\r\n\r\nformatitem:\r\n\tpush offset @@L335\r\n\txor edx,edx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR cs:[esi],'-'\r\n\tjne @F\r\n\tdec bl\r\n\tinc esi\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR cs:[esi],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc esi\r\n@@:\r\n\tmov [fillchr],ecx\r\n\tmov ebx,edx\r\n\r\n\t.while ( byte ptr cs:[esi] >= '0' && byte ptr cs:[esi] <= '9' )\r\n\t\tlodsb cs:[esi]\r\n\t\tsub al,'0'\r\n\t\tmovzx eax,al\r\n\t\timul ecx,ebx,10\t\t;ecx = ebx * 10\r\n\t\tadd eax,ecx\r\n\t\tmov ebx,eax\r\n\t.endw\r\n\r\n\tmov [size_],ebx\r\n\tcmp BYTE PTR cs:[esi],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc esi\r\n@@:\r\n\tlodsb cs:[esi]\r\n\tmov [fmt],esi\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tand al,al\r\n\tjnz @F\r\n\tpop eax\r\n\tjmp done\r\nhandle_c:\r\n\tmov eax,ss:[edi]\r\n\tadd edi, 4\r\n@@:\r\n\tcall dochar\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov esi,ss:[edi]\r\n\tadd edi,4\r\n\tjmp print_string\r\nhandle_d:\r\nhandle_i:\r\n\tmov ebx,-10\r\n\tjmp @F\r\nhandle_u:\r\n\tmov ebx, 10\r\n\tjmp @F\r\nhandle_x:\r\n\tmov ebx, 16\r\n@@:\r\n\txor edx,edx\r\n\tmov eax,ss:[edi]\r\n\tadd edi,4\r\n\tcmp longarg,1\r\n\tjnz @F\r\n\tmov edx,ss:[edi]\r\n\tadd edi,4\r\n\tjmp printnum\r\n@@:\r\n\tand ebx,ebx\r\n\tjns @F\r\n\tcdq\r\n@@:\r\nprintnum:\r\n\tlea esi, szTmp\r\n\tinvoke i64toa, edx::eax, esi, ebx\r\n\tmov esi, eax\r\n\r\nprint_string:\t\t;print string ESI, size EAX\r\n\tmov eax, esi\r\n\t.while byte ptr cs:[esi]\r\n\t\tinc esi\r\n\t.endw\r\n\tsub esi, eax\r\n\txchg eax, esi\r\n\tmov ebx,size_\r\n\tsub ebx,eax\r\n\t.if flag == 1\r\n\t\t.while sdword ptr ebx > 0\r\n\t\t\tmov eax, [fillchr]\r\n\t\t\tcall dochar\t;print leading filler chars\r\n\t\t\tdec ebx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr cs:[esi]\r\n\t\tlodsb cs:[esi]\r\n\t\tcall dochar\t\t;print char of string\r\n\t.endw\r\n\r\n\t.while sdword ptr ebx > 0\r\n\t\tmov eax, [fillchr]\r\n\t\tcall dochar\t\t;print trailing spaces\r\n\t\tdec ebx\r\n\t.endw\r\n\tretn\r\n\r\ndochar:\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov al,13\r\n\tcall @F\r\n\tmov al,10\r\n@@:\r\n\tjmp VPUTCHR\r\n\r\ndprintf endp\r\n\r\n\tOPTION PROLOGUE: prologuedef\r\n"
  },
  {
    "path": "src/DPRNTF16.INC",
    "content": "\r\n;--- printf for debug displays, 16-bit\r\n\r\n;--- itoa(long n, char * s, int base);\r\n;--- convert 32-bit long to string\r\n;--- v5.86: removed eax from uses clause (since AX is used to return string ptr)\r\n\r\nltoa PROC stdcall uses edx edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov bx,outb\r\n\tadd bx,10\r\n\tmov BYTE PTR ss:[bx],0\r\n\tdec bx\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov ss:[bx],dl\r\n\tdec bx\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov ss:[bx],ch\r\n\tdec bx\r\n@@:\r\n\tinc bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nltoa ENDP\r\n\r\n;--- dprintf is to preserve registers & flags; needs a special prologue:\r\n\r\n@dprologue macro procname,flag,parmbyte,localbyte,reglist,userparms\r\n\tpush bp\r\n\tmov bp,sp\r\n\tlea esp,[esp-localbyte]   ;there exists no 16-bit \"lea sp,[sp-xx]\"\r\n\texitm %localbyte\r\nendm\r\n\r\n\tOPTION PROLOGUE: @dprologue\r\n\r\ndprintf proc c public fmt:ptr, args:vararg\r\n\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal size_:word\r\nlocal fillchr:word\r\nlocal szTmp[12]:byte\r\n\r\n\tpusha\r\n\tpushf\r\n\tpush ds\r\n\tpush cs\r\n\tpop ds\r\n\tcld\r\n\tlea di,args\r\n@@L335:\r\n\tmov si,fmt\r\nnextchar:\r\n\tlodsb [si]\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall VPUTCHR\r\n\tjmp nextchar\r\ndone:\r\n\tpop ds\r\n\tpopf\r\n\tpopa\r\n\tret\r\n\r\nformatitem:\r\n\tpush offset @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov bl,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec bl\r\n\tinc si\r\n@@:\r\n\tmov [flag],bl\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fillchr],cx\r\n\tmov bx,dx\r\n\r\n\t.while ( byte ptr [si] >= '0' && byte ptr [si] <= '9' )\r\n\t\tlodsb\r\n\t\tsub al,'0'\r\n\t\tcbw\r\n\t\timul cx,bx,10\t\t;ecx = ebx * 10\r\n\t\tadd ax,cx\r\n\t\tmov bx,ax\r\n\t.endw\r\n\r\n\tmov [size_],bx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tand al,al\r\n\tjnz @F\r\n\tpop ax\r\n\tjmp done\r\nhandle_c:\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n@@:\r\n\tcall VPUTCHR\r\n\tretn\r\n\r\nhandle_s:\r\n\tmov si,ss:[di]\r\n\tadd di,2\r\n\tjmp print_string\r\nhandle_d:\r\nhandle_i:\r\n\tmov bx,-10\r\n\tjmp @F\r\nhandle_u:\r\n\tmov bx, 10\r\n\tjmp @F\r\nhandle_x:\r\n\tmov bx, 16\r\n@@:\r\n\txor dx,dx\r\n\tmov ax,ss:[di]\r\n\tadd di,2\r\n\tcmp longarg,1\r\n\tjnz @F\r\n\tmov dx,ss:[di]\r\n\tadd di,2\r\n\tjmp printnum\r\n@@:\r\n\tand bx,bx\r\n\tjns @F\r\n\tcdq\r\n@@:\r\nprintnum:\r\n\tlea si, szTmp\r\n\tpush eax       ;v5.86: added (to preserve hiword(eax))\r\n\tinvoke ltoa, dx::ax, si, bx\r\n\tmov si, ax\r\n\tpop eax        ;v5.86: added\r\n\tpush ds\r\n\tpush ss\r\n\tpop ds\r\n\tcall print_string\r\n\tpop ds\r\n\tretn\r\n\r\nprint_string:\t\t;print string SI\r\n\tmov ax, si\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si, ax\r\n\txchg ax, si\r\n\tmov bx,size_\r\n\tsub bx, ax\r\n\t.if flag == 1\r\n\t\t.while sword ptr bx > 0\r\n\t\t\tmov ax, [fillchr]\r\n\t\t\tcall VPUTCHR\t;print leading filler chars\r\n\t\t\tdec bx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall VPUTCHR\t\t;print char of string\r\n\t.endw\r\n\r\n\t.while sword ptr bx > 0\r\n\t\tmov ax, [fillchr]\r\n\t\tcall VPUTCHR\t\t;print trailing spaces\r\n\t\tdec bx\r\n\t.endw\r\n\tretn\r\n\r\nVPUTCHR:\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov al,13\r\n\tcall @F\r\n\tmov al,10\r\n@@:\r\n\tpush bx\r\n\txor bx, bx\r\n\tmov ah, 0Eh\r\n\tint 10h\r\n\tpop bx\r\n\tretn\r\n\r\ndprintf endp\r\n\r\n\tOPTION PROLOGUE: prologuedef\r\n"
  },
  {
    "path": "src/EMS.ASM",
    "content": "\r\n;--- EMS implementation\r\n;--- EMS 4.0 is Public Domain, originally written by Michael Devore,\r\n;--- extended and modified for Jemm by Japheth;\r\n;--- the EMS 3.2 part is copyrighted and has therefore\r\n;--- been moved into a separate include file (EMS32.INC)\r\n\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n    include ems.inc\r\n\r\n;--- equates\r\n\r\nDEVICE_NAME equ 000Ah   ; offset device name in RSEG\r\n\r\n;--- config\r\n\r\n?MAXPHYSPG  equ 56      ; std=56, max physical pages (don't set below 4!)\r\n?YIELDOPEN  equ 0       ; std=0, 1=yield on handle open\r\n?LIM32      equ 0       ; std=0, 1=support LIM EMS 3.2 only\r\n?SUPP5B     equ 1       ; std=1, 1=support int 67h, ax=5Bxxh\r\n\r\n;--- public/externals\r\n\r\n    include extern32.inc\r\n\r\n;--- EMS page descriptor\r\n;--- for 4 GB, there are 4096/1.5 = 2731 possible pool descriptor indices\r\n\r\nEMSPD struct\r\nwNext   dw ?        ;index next EMSPD\r\nwIndex  dw ?        ;pool descriptor index for EMS page\r\nEMSPD ends\r\n\r\n;--- descriptor subindex\r\n\r\nEMSPD2 struct\r\nbSubIdx db ?        ;pool descriptor subindex for EMS page \r\nEMSPD2 ends\r\n\r\n;--- alter page map and jump/call structures (55h and 56h)\r\n\r\nlog_phys_map struct\r\nwLogPage       DW ? ; logical page (handle in DX)\r\nwPhysPage      DW ? ; physical pages if AL=0, segments if AL=1\r\nlog_phys_map ends\r\n\r\nmap_and_jump struct\r\ntarget_address      DD ? ; far16 jump address\r\nnew_page_map_len    DB ? ; items in new_page_map_ptr\r\nnew_page_map_ptr    DD ? ; far16 pointer to log_phys_map structure\r\nmap_and_jump ends\r\n\r\nmap_and_call struct\r\n                    map_and_jump <?>\r\nold_page_map_len    db ? ; items in old_page_map_ptr\r\nold_page_map_ptr    dd ? ; far16 pointer to log_phys_map structure\r\nmap_and_call ends\r\n\r\n;--- structure for EMS function 57h\r\n\r\nEMM57 struct\r\ne57_dwSize  DD ?    ; +0  size of region\r\ne57_bSrcTyp DB ?    ; +4  src memory type\r\ne57_wSrcHdl DW ?    ; +5  src handle\r\ne57_wSrcOfs DW ?    ; +7  src ofs\r\ne57_wSrcSeg DW ?    ; +9  src segm./log. page\r\ne57_bDstTyp DB ?    ; +11 dst memory type\r\ne57_wDstHdl DW ?    ; +12 dst handle\r\ne57_wDstOfs DW ?    ; +14 dst ofs\r\ne57_wDstSeg DW ?    ; +16 dst segm./log. page\r\nEMM57 ends\r\n\r\n;--- structure for EMS function 59h\r\n\r\nEMM59 struct\r\ne59_pgsize  dw ?    ;raw page size in paragraphs\r\ne59_altsets dw ?    ;number of alternate register sets\r\ne59_sizcont dw ?    ;size of mapping context save area in bytes\r\ne59_dmasets dw ?    ;dma register sets\r\ne59_dmaflgs dw ?    ;dma flags\r\nEMM59 ends\r\n\r\n.text$01 SEGMENT\r\n\r\n;--- EMS variables\r\n\r\nEMSHandleTable  DD  0   ; table of EMS handles (4 bytes/handle)\r\nEMSNameTable    DD  0   ; table of EMS handle names (8 bytes/handle)\r\nEMSStateTable   DD  0   ; table of EMS status saves (16 bytes/handle)\r\nEMSPagesMax     DD  0   ; max EMS pages (default 2048)\r\nEMSPagesUsed    DD  0   ; used EMS pages\r\n\r\nEMSPage2Segm    DB  ?MAXPHYSPG DUP (0)  ; segments mapped to phys.pgs\r\n;EMSMappedPages DW  ?MAXPHYSPG DUP (-1) ; log. EMS pgs mapped to phys. pgs\r\n\r\n    align 4\r\n\r\n;--- table of EMS page descriptors (EMSPD), one item for each EMS page\r\n;--- initialized with -1\r\n\r\nEMSPageAllocationStart DD   0\r\nEMSPageAllocationEnd   DD   0\r\n\r\nemm59_ EMM59 <1024,0,0,0,0>\r\nif ?SUPP5B\r\nmapptr          DD 0    ; alternate map save area (get/set by ax=5B00h/5B01h)\r\nendif\r\n\r\nbEmsPhysPages   DB  0   ; current physical pages\r\nbPagesConv      DB  0   ; physical pages in conv. memory\r\nbNoEMS          DB  0   ; flags no EMS services\r\nbNoFrame        DB  0   ; flags no page frame\r\nbNoVCPI         DB  0   ; flags no VCPI services\r\n\r\n;--- this is 16-bit code which is copied onto the client's stack\r\n;--- to restore the page mapping in int 67h, ah=56h\r\n\r\nclproc label byte\r\n    db 1Eh          ; 0 push ds\r\n    db 51h          ; 1 push cx\r\n    db 52h          ; 2 push dx\r\n    db 56h          ; 3 push si\r\n    db 0B9h         ; 4 mov cx,zzzz\r\n_clcx dw 0          ; 5\r\n    db 0BAh         ; 7 mov dx,zzzz\r\n_cldx dw 0          ; 8\r\n    db 0BEh         ; A mov si,yyyy\r\n_clsi dw 0          ; B\r\n    db 068h         ; D push xxxx\r\n_clds dw 0          ; E\r\n    db 1Fh          ; 0 pop ds\r\n    db 0B8h         ; 1 mov ax,50wwh\r\n_clal db 00,50h     ; 2\r\n    db 0CDh, 67h    ; 4 int 67h\r\n    db 5Eh          ; 6 pop si\r\n    db 5Ah          ; 7 pop dx\r\n    db 59h          ; 8 pop cx\r\n    db 1Fh          ; 9 pop ds\r\n    db 0CAh         ; A retf sizeclproc\r\n    dw sizeclproc   ; B\r\n    db 0            ; D alignment byte\r\nsizeclproc equ $ - offset clproc\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n    align 4\r\n\r\nEMS_Call_Table label dword\r\n    Dd EMS_GET_STATUS           ; 40h\r\n    Dd EMS_GET_PAGE_FRAME_ADDRESS       ;41h\r\n    Dd EMS_GET_UNALLOCATED_PAGE_COUNT   ;42h\r\n    Dd EMS_ALLOCATE_PAGES       ; 43h\r\n    Dd EMS_MAP_HANDLE_PAGE      ; 44h (map/unmap a page)\r\n    Dd EMS_DEALLOCATE_PAGES     ; 45h\r\n    Dd EMS_GET_VERSION          ; 46h\r\n    Dd EMS_SAVE_PAGES           ; 47h\r\n    Dd EMS_RESTORE_PAGES        ; 48h\r\n    Dd EMS_NOT_IMPL             ; 49h (get io port addresses)\r\n    Dd EMS_NOT_IMPL             ; 4ah (get translation array)\r\n    Dd EMS_GET_OPEN_HANDLES_COUNT       ; 4bh\r\n    Dd EMS_GET_PAGES_ONE_HANDLE ; 4ch\r\n    Dd EMS_GET_PAGES_ALL_HANDLES; 4dh\r\n    Dd EMS_GET_SET_PAGE_MAP     ; 4eh\r\nife ?LIM32\r\n    Dd ems4_get_set_partial_page_map    ; 4fh\r\n    Dd ems4_map_multiple        ; 50h\r\n    Dd ems4_realloc             ; 51h\r\n    Dd ems4_attribute           ; 52h\r\n    Dd ems4_get_set_handle_name ; 53h\r\n    Dd ems4_get_handle_info     ; 54h\r\n    Dd ems4_alter_map_jump      ; 55h\r\n    Dd ems4_alter_map_call      ; 56h\r\n    Dd ems4_move_memory         ; 57h\r\n    Dd ems4_get_mappable_info   ; 58h\r\n    Dd ems4_get_config          ; 59h\r\n    Dd ems4_allocate_pages      ; 5ah\r\n    Dd ems4_alt_map_reg_set     ; 5bh\r\n    Dd EMS_NOT_IMPL             ; 5ch (4: prepare EMS for warm boot)\r\n    Dd EMS_NOT_IMPL             ; 5dh (4: enable/disable OS functions)\r\nendif\r\nEMS_MAX equ ($ - EMS_Call_Table) / 4\r\n\r\n    align 4\r\n\r\n;--- breakpoint: if int 67h vector is hooked in real-mode, the hooker code\r\n;--- is called and will finally met a breakpoint which will get us here.\r\n\r\nInt67_V86Entry proc public\r\n    @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\r\n    call Simulate_Iret\r\n    add esp,4       ;skip return address\r\n    mov esi,[ebp].Client_Reg_Struc.Client_ESI\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EDX\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EAX\r\n    JMP EMM_ENTRY_2\r\nInt67_V86Entry endp\r\n\r\n@emmpushreg macro\r\n    SUB ESP, CRS_SIZEX\r\n    PUSHAD\r\n    MOV EBP, ESP\r\nendm\r\n\r\n@emmpopreg macro setebp\r\nif setebp\r\n    mov esp, ebp\r\nendif\r\n    POPAD\r\n    ADD ESP, CRS_SIZEX\r\nendm\r\n\r\n;--- IVT 67h is hooked; enter monitor to run the IVT code...\r\n\r\nInt67_Indirect:\r\n    @emmpopreg 0\r\n    push 67h\r\n    jmp V86_Monitor\r\n\r\n    align 4\r\n;\r\n; Here starts the Expanded Memory Manager (EMM) Version 4.0\r\n; Int67_Entry is the IDT entry for INT 67h.\r\n\r\nInt67_Entry PROC public\r\n\r\n    @emmpushreg     ; make EBP -> Client_Reg_Struc\r\n\r\n;--- must be one source line without concatenation operator \\, or Masm complains\r\n    @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\r\n\r\n    mov ecx, cs:[dwRSeg]\r\n    cmp cx, cs:[67h*4+2] ;IVT vector 67h modified?\r\n    jnz Int67_Indirect\r\n\r\n    MOV ECX,SS      ; address everything\r\n    MOV DS,ECX\r\n    MOV ES,ECX\r\n    mov esp,[dwStackCurr]\r\n\r\nEMM_ENTRY_2::\r\n\r\n    CLD\r\n    @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\r\n\r\nif ?VCPI\r\n    cmp ah,0deh         ; see if VCPI function\r\n    jne @@not_vcpi_api\r\n    cmp al,0Ch\r\n    jz VCPI_V86toPM\r\n    jnc @@vcpi_inv_call ; invalid VCPI call\r\n    movzx ecx,al\r\n    cmp [bNoVCPI],0     ; check if VCPI turned off\r\n    jne @@vcpi_inv_call ; yes, return invalid code, flags VCPI not present for 0de00h\r\n  if ?VCPIDBG\r\n    bt vcpidbgfilt,cx\r\n    jnc nodbgout\r\n    @dprintf ?VCPIDBG, <\"VCPI rm, in: ax=%X cx=%X edx=%X\">, word ptr [ebp].Client_Reg_Struc.Client_EAX,\\\r\n        word ptr [ebp].Client_Reg_Struc.Client_ECX, [ebp].Client_Reg_Struc.Client_EDX\r\n    call [VCPI_Call_Table+ECX*4]\r\n    @dprintf ?VCPIDBG, <\", out: ax=%X edx=%X\",10>, ax, edx\r\n    jmp vcpi_done\r\n    align 4\r\n;--- VCPI functions that are to be logged\r\n;------------- FEDCBA9876543210\r\nvcpidbgfilt dw 0000111111111111b\r\nnodbgout:\r\n  endif\r\n    call [VCPI_Call_Table+ECX*4]\r\nvcpi_done:\r\n    mov [ebp].Client_Reg_Struc.Client_EDX, edx\r\n    jmp @@byeems\r\n    align 4\r\n@@not_vcpi_api:\r\nendif\r\n\r\n    MOVZX ECX,AH              ; check permitted range\r\n    SUB CL,40H\r\n    JB @@emm_inv_call\r\n    CMP CL,EMS_MAX\r\n    JAE @@emm_inv_call\r\n\r\n    @dprintf ?EMSDBG, <\"EMS in: ax=%X bx=%X dx=%X\">, word ptr [ebp].Client_Reg_Struc.Client_EAX,\\\r\n        word ptr [ebp].Client_Reg_Struc.Client_EBX, word ptr [ebp].Client_Reg_Struc.Client_EDX\r\n\r\n    CALL [EMS_Call_Table+ECX*4]\r\n    @dprintf ?EMSDBG, <\", out: ax=%X bx=%X dx=%X\",10>, ax, bx, dx\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EBX,bx\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EDX,dx\r\n@@byeems:\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1,ah\r\n    @emmpopreg 1\r\n    IRETD\r\n\r\n@@vcpi_inv_call:\r\n    @dprintf ?VCPIDBG, <\"VCPI invalid function, ax=%X\",10>, ax\r\nif ?VCPIDBG\r\n    MOV ah,EMSS_INVALID_FUNCTION\r\n    JMP @@byeems\r\nendif\r\n\r\n@@emm_inv_call:\r\n    @dprintf ?EMSDBG, <\"EMS invalid function, ax=%X\",10>, ax\r\n    MOV ah,EMSS_INVALID_FUNCTION\r\n    JMP @@byeems\r\n        \r\nInt67_Entry ENDP\r\n\r\n    include EMS32.INC   ;include the copyrighted part (EMS 3.2)\r\n\r\n_ret:   ;this label belongs to EMS_MAP_REL_PAGE\r\n    ret\r\n\r\n; Map EMS page in DX:BX to physical page in AL\r\n; called by EMS function 44h\r\n; BX=-1 -> unmap page\r\n\r\nEMS_MAP_REL_PAGE proc\r\n    call EMS_get_abs_page ;get absolute page in BX\r\n    jc _ret\r\nEMS_MAP_REL_PAGE endp   ;fall thru!!!\r\n\r\n; Map an EMS page to a physical page\r\n; inp: AL = physical EMS page (0 - (bEmsPhysPages - 1))\r\n;      BX = absolute EMS page (or -1 to unmap)\r\n; modifies EDI, ECX, EAX\r\n; the logical page in BX might belong to no handle!\r\n\r\nEMS_MAP_ABS_PAGE PROC\r\n    movzx eax,al\r\n    mov al, [EMSPage2Segm+EAX]\r\n\r\n    lea edi, @GetPTEAddr(EAX*4+?PAGETAB0) ; EDI -> PTE\r\n    SHL eax, 12             ; C0 -> C0000, C4-> C4000\r\n\r\nEMS_MAP_ABS_PAGE ENDP   ;fall through!!!\r\n\r\n; Map an (absolute) EMS page anywhere in address space\r\n; BX = EMS abs. page\r\n; EDI == ptr to PTE\r\n; EAX == linear address\r\n; modifies edi, ecx, eax\r\n\r\nEMS_MAP_ABS_PAGE_EX proc\r\n\r\n    AND BH,BH               ; unmap the page?\r\n    JS @@SET\r\n\r\n    MOVZX ECX,BX            ; get the EMSPD for the page in ECX\r\n    shl ecx,2\r\n    add ecx, [EMSPageAllocationStart]\r\n    movzx ecx,[ecx].EMSPD.wIndex; get pool descriptor index\r\n\r\n    cmp cx,-1   ;bad pointer?\r\n    jz @@SET    ;then unmap this page\r\n\r\n    movzx eax, bx\r\n    add eax,[EMSPageAllocationEnd]\r\n    movzx eax,[eax].EMSPD2.bSubIdx  ;pool descriptor subindex\r\n    call Pool_GetPhysAddr           ;convert index+offset into phys addr\r\n\r\n@@SET:\r\n    OR EAX,7           ; Statusbits: R/W=1,U/S=1,P=1\r\nif 0\r\n    mov cl,4\r\n@@LOOP:\r\n    MOV [EDI],EAX       ; Register the new physical Address of\r\n    ADD EDI,4           ; window\r\n    ADD EAX,4096        ; Process next 4K-page\r\n    DEC CL\r\n    JNZ @@LOOP\r\nelse\r\n    mov ecx,1000h\r\n    stosd\r\n    add eax,ecx\r\n    stosd\r\n    add eax,ecx\r\n    stosd\r\n    add eax,ecx\r\n    stosd\r\nendif\r\n    RET\r\n    align 4\r\n    \r\nEMS_MAP_ABS_PAGE_EX endp\r\n\r\nFlushTLB proc\r\nif 0\r\n    cmp [bNoInvlPg],0\r\n    jz @@noinvlpg\r\nendif\r\n    MOV EAX,CR3         ; flush TLB\r\n    MOV CR3,EAX\r\n@@noinvlpg:\r\n    ret\r\n    align 4\r\nFlushTLB endp\r\n\r\n;--- get EMS absolute page\r\n;--- inp DL:BX = handle:logical page\r\n;--- out NC ok, EBX = absolute page (index into EMSPD array)\r\n;--- C if failure, then error code in AH\r\n;--- modifies ECX, EDI\r\n\r\nEMS_get_abs_page proc\r\n    AND BH,BH                   ; bx < 0 means unmap this phys. page\r\n    JS @@OK\r\n    movzx ecx, bx\r\n    movzx ebx, dl\r\n    mov edi,[EMSHandleTable]\r\n    mov bx,[edi+ebx*4].EMSHD.ehd_wIdx\r\n    MOV EDI,[EMSPageAllocationStart]\r\n    inc ecx\r\n    jmp @@test\r\n    align 4\r\n@@nextitem:\r\n    mov bx,[edi+ebx*4].EMSPD.wNext\r\n@@test:\r\n    cmp bx,-1\r\n    loopnz @@nextitem\r\n    jz @@fail\r\n@@OK:\r\n    clc\r\n    ret\r\n@@fail:\r\n    MOV AH,EMSS_LOG_PAGE_INVALID    ; \"logical page out of reserved area\"\r\n    stc\r\n    ret\r\n    align 4\r\nEMS_get_abs_page endp\r\n\r\n;--- check if segment in AX is a valid (physical) page\r\n;--- if yes, return NC and convert segment to physical page in AL\r\n\r\nEMS_Segm2Phys proc\r\n    and al,al           ;must begin on a page boundary\r\n    jnz @@notvalid\r\n    mov al,ah\r\n    push ecx\r\n    push edi\r\n    movzx ecx, [bEmsPhysPages]\r\n    mov edi, offset EMSPage2Segm\r\n    mov ah,cl\r\n    repnz scasb\r\n    pop edi\r\n    jnz @@notvalid2\r\n    mov al, ah\r\n    dec al\r\n    sub al, cl\r\n    pop ecx\r\n    ret\r\n@@notvalid2:\r\n    pop ecx\r\n@@notvalid:\r\n    mov ah,EMSS_PHYS_PAGE_INVALID\r\n    stc\r\n    ret\r\n    align 4\r\nEMS_Segm2Phys endp\r\n    \r\n;--- begin EMS 4.0 functions\r\n\r\nife ?LIM32\r\n\r\n;\r\n; 674F: AH = 4Fh: Get & Set partial Map\r\n; AL = 0,1,2 \r\n; AL = 0 (get map), DS:SI -> map to get, ES:DI -> status to receive\r\n; AL = 1 (set map), DS:SI -> map to restore\r\n; AL = 2 (get size), items in BX, returns size of map in AL\r\n; the list for sub function 0 DS:SI points to has following structure:\r\n; WORD   : items in list\r\n; WORD[] ; segment! addresses for which to get the map info \r\n; the output list ES:DI points to (function 0) has following structure:\r\n; WORD   : items in list\r\n;  BYTE  : page no\r\n;  WORD  ; abs EMS page mapped (or -1)\r\n\r\nems4_get_set_partial_page_map PROC\r\n\r\n    cmp al,2\r\n    ja bad_subfunc\r\n    jz @@getsize\r\n    movzx ecx,WORD PTR [ebp].Client_Reg_Struc.Client_DS\r\n    shl ecx,4\r\n    movzx esi, si\r\n    add esi,ecx\r\n    \r\n    cmp al,1\r\n    jz @@setmap\r\n\r\n;--- ax=4F00h (get map)\r\n\r\n    movzx ecx,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl ecx,4\r\n    movzx edi, di\r\n    add edi,ecx\r\n\r\n    lodsw\r\n    movzx eax,ax\r\n    movzx ecx, [bEmsPhysPages]\r\n    cmp eax,ecx\r\n    ja @@failA3\r\n    mov ecx, eax\r\n    stosw\r\n    jecxz @@done00\r\n@@nextsegm:\r\n    lodsw\r\n    call EMS_Segm2Phys  ;convert segment to phys page\r\n    jc @@fail\r\n    movzx eax,al\r\n    push ecx\r\n    movzx ecx, byte ptr [esi-1]\r\n    lea ecx, @GetPTEAddr(ECX*4+?PAGETAB0)\r\n    mov ecx,[ecx]\r\n    mov cl,0\r\n    or eax,ecx\r\n    stosd\r\n    pop ecx\r\n    loop @@nextsegm\r\n@@done00:\r\n    mov ah,EMSS_OK\r\n    ret\r\n@@failA3:\r\n@@failA3_1:\r\n    mov ah,0A3h ;segment count exceeds mappable pages\r\n@@fail:\r\n    ret\r\n\r\n;--- ax=4F01h (set map)\r\n\r\n@@setmap:\r\n    lodsw\r\n    movzx eax,ax\r\n    movzx ecx, [bEmsPhysPages]\r\n    cmp eax,ecx\r\n    ja @@failA3\r\n    mov ecx, eax\r\n    jmp EMS_RestorePagesFromEsi\r\n\r\n;--- ax=4F02h (get size of save area)\r\n\r\n@@getsize:\r\n    cmp bh,0\r\n    jnz @@failA3_1\r\n    cmp bl,[bEmsPhysPages]\r\n    ja @@failA3_1\r\n    mov al, bl\r\n    shl al,2        ;4 bytes (makes Kyrandia 3 work [better]!?)\r\n    add al,2        ;2 extra bytes for size\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al\r\n    mov ah, EMSS_OK\r\n    ret\r\n    align 4\r\n\r\nems4_get_set_partial_page_map ENDP\r\n\r\n; 6750:\r\n; AH = 50h: EMS 4.0 map multiple pages\r\n; DX = handle\r\n;  DS:SI -> mapping array\r\n;  CX = items in array (ecx is destroyed, reload it from stack)\r\n;  structure of mapping array:\r\n;  WORD logical page (or -1 to unmap page)\r\n;  WORD physical page (AL=0) or segment address (AL=1)\r\n\r\nems4_map_multiple PROC\r\n\r\n    cmp al,1\r\n    ja  bad_subfunc\r\n\r\n    movzx ecx, word ptr [ebp].Client_Reg_Struc.Client_ECX   ; load EMM entry CX value\r\n    movzx edi, word ptr [ebp].Client_Reg_Struc.Client_DS\r\n    shl edi,4\r\n    movzx esi,si\r\n    add edi,esi     ; edi -> map address buffer\r\n\r\nems4_map_multiple_edi::\r\n\r\n; perform handle check here so that stack return address isn't blown\r\n\r\n    call EMS_TEST_HANDLE\r\n    mov esi, edi\r\n\r\n    push ebx\r\n    push eax\r\n    jecxz @@success\r\n@@multi_loop:\r\n    mov bx,[esi+0]\r\n    mov ax,[esi+2]\r\n    add esi,4\r\n    cmp byte ptr [esp+0],1  ;subfunction 1?\r\n    jne @@mappage\r\n    call EMS_Segm2Phys      ;convert segment in AX to a page no in AL\r\n    jc  @@multi_out\r\n@@mappage:\r\n    push ecx\r\n    call EMS_MAP_REL_PAGE   ;map page in DX:BX to phys page in AL\r\n    pop ecx\r\n    jc @@multi_out\r\n    loop @@multi_loop\r\n    call FlushTLB\r\n@@success:\r\n    MOV ah,EMSS_OK\r\n@@multi_out:\r\n    mov [esp+1],ah\r\n    pop eax\r\n    pop ebx\r\n    ret\r\n\r\nems4_map_multiple ENDP\r\n\r\n; 6751:\r\n; AH = 51h: EMS 4.0 reallocate pages for handle\r\n; DX = handle\r\n; BX = new page count for handle\r\n; out: BX=pages assigned to handle\r\n;\r\nems4_realloc PROC\r\n\r\n    movzx ecx, bx           ; save new pages\r\n    call EMS_GET_PAGES_ONE_HANDLE   ;get curr pages in EBX, ESI->EMSHD\r\n    and ah,ah\r\n    jnz exit\r\n\r\n    mov edi,[EMSPageAllocationStart]\r\n\r\n    cmp ecx, ebx            ; check new page count against original\r\n    jb @@shrinkalloc\r\n    je @@realloc_success    ; no change needed\r\n\r\n    sub ecx, ebx            ; get no of additional pages\r\n\r\n    call GetFreeEMSPages\r\n    cmp ecx,eax\r\n    ja @@toomany\r\n\r\n    movzx eax,[esi].EMSHD.ehd_wIdx\r\n    jmp @@test\r\n@@nextitem:\r\n    lea esi,[edi+eax*4]\r\n    mov ax,[esi].EMSPD.wNext\r\n@@test:\r\n    cmp ax,-1\r\n    jnz @@nextitem\r\n\r\n    call AllocateEMSPages   ; allocate ECX pages\r\n    jnc @@realloc_success\r\n@@toomany:\r\n    mov ah,EMSS_OUT_OF_FREE_PAGES\r\nexit:\r\n    ret\r\n\r\n; trim off the trailing pages\r\n; ECX=new page count, EBX=old page count\r\n\r\n@@shrinkalloc:\r\n    xor ebx, ebx\r\n    mov eax, ebx\r\n    jmp @@test2\r\n@@nextitem2:    \r\n    lea esi,[edi+eax*4]\r\n    inc ebx\r\n@@test2:    \r\n    mov ax,[esi].EMSPD.wNext\r\n    cmp ax,-1\r\n    jz @@realloc_success\r\n    cmp ebx, ecx\r\n    jnz @@nextitem2\r\n    jmp @@test3\r\n@@freenext:\r\n    lea esi,[edi+eax*4]\r\n    call ReleaseEMSPage     ;preserves registers\r\n@@test3:    \r\n    mov ax,-1\r\n    xchg ax,[esi].EMSPD.wNext\r\n    cmp ax,-1\r\n    jnz @@freenext\r\n\r\n@@realloc_success:\r\n    mov ebx,[ebp].Client_Reg_Struc.Client_EBX\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\nems4_realloc ENDP\r\n\r\n; 6752\r\n; AH = 52h: EMS 4.0 attribute related\r\n; AL = 0/1/2, DX=handle, BL=0/1\r\n; AL=2:\r\n; out AL=attr\r\n\r\nems4_attribute PROC\r\n    cmp al,2\r\n    jb  @@get_set_attribute\r\n    ja  bad_subfunc ; this is an invalid subfunction\r\n\r\n;-- AL==2 and AL==0\r\n\r\n@@is_volatile:\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 0   ; al == 0, volatile attribute only\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\n;-- AL==1\r\n\r\n@@get_set_attribute:\r\n    call EMS_TEST_HANDLE; only valid handles please\r\n    or al,al            ; 0 is get, 1 is set\r\n    jz @@is_volatile    ; only volatile here (none survive warm reboot)\r\n    or bl,bl            ; 0 is \"make volatile\" (true anyway)\r\n    jnz @@cannot_make_nonvolatile\r\n    mov ah,EMSS_OK\r\n    ret\r\n@@cannot_make_nonvolatile:\r\n    mov ah,EMSS_FEATURE_UNSUPP  ; feature not supported\r\n    ret\r\nems4_attribute ENDP\r\n\r\n; 6753:\r\n; AH = 53h: EMS 4.0 get/set handle name\r\n; AL = 0: get handle name in ES:DI\r\n; AL = 1: set handle name in DS:SI\r\n; DX = handle\r\n;\r\nems4_get_set_handle_name PROC\r\n    cmp al,1\r\n    ja  bad_subfunc ; this is an invalid subfunction\r\n    jz  @@ems4_setname\r\n\r\n    call EMS_TEST_HANDLE\r\n    movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl esi,4\r\n    movzx edi,di\r\n    add edi,esi     ; edi -> handle name buffer address (dest)\r\n    mov esi, [EMSNameTable]\r\n    movzx ecx,dl    ; handle (index)\r\n    lea esi, [esi+ecx*8]\r\n    jmp @@ems4_getsetname\r\n\r\n@@ems4_setname:\r\n    movzx edi, si   ;ESI will be destroyed by next call\r\n    call EMS_TEST_HANDLE\r\n    movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_DS\r\n    shl esi,4\r\n    add esi,edi     ; esi -> handle name (source)\r\n\r\n;--- resetting the name is always valid\r\n\r\n    mov eax,[esi+0]\r\n    or eax,[esi+4]\r\n    jz @@reset_name\r\n\r\n;--- check for a handle which already has this name    \r\n;--- return status A1h if one exists\r\n;--- don't care if the handle found is the same as the current one\r\n\r\n    mov edi,esi\r\n    push esi\r\n    call find_handle_by_name_int\r\n    pop esi\r\n    and ah,ah   ;was a handle with this name found?\r\n    mov ah,0A1h\r\n    jz @@failed\r\n@@reset_name:    \r\n\r\n    mov edi, [EMSNameTable]\r\n    movzx ecx,dl    ; handle (index)\r\n    lea edi, [edi+ecx*8]\r\n\r\n@@ems4_getsetname:\r\n    movsd\r\n    movsd\r\n    mov ah,EMSS_OK\r\n@@failed:    \r\n    ret\r\n\r\nems4_get_set_handle_name ENDP\r\n\r\n; 6754:\r\n; AH = 54h: EMS 4.0 get various handle info\r\n;\r\n; AL = 0: get handle directory into ES:DI\r\n; AL = 1: search handle by name in DS:SI, return handle in DX\r\n; AL = 2: get total handles in BX\r\n\r\nems4_get_handle_info PROC\r\n    cmp al,1\r\n    jb getallhand\r\n    je @@find_handle_by_name\r\n    cmp al,2\r\n    ja bad_subfunc ; this is an invalid subfunction\r\n\r\n;-- AL=2\r\n\r\n    mov bx,EMS_MAX_HANDLES\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\n; AL=0, write handle directory to caller buffer\r\n; return in AL number of open handles\r\n; return in ES:DI array of:\r\n;   WORD handle\r\n;   QWORD name\r\n\r\ngetallhand:\r\n    movzx   esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl esi,4\r\n    movzx edi,di\r\n    add esi,edi\r\n    mov edi, [EMSHandleTable]\r\n    xor eax, eax        ; AL will be count of open handles\r\n    xor ecx, ecx\r\n    push edx\r\n    mov edx, [EMSNameTable]\r\n@@scan_handles:\r\n    test [edi].EMSHD.ehd_bFlags, EMSH_USED\r\n    jz @@free_handle\r\n    inc eax\r\n\r\n    mov [esi+0],cx\r\n    push DWORD PTR [edx+ecx*8+0]    ; copy handle name\r\n    pop DWORD PTR [esi+2]\r\n    push DWORD PTR [edx+ecx*8+4]\r\n    pop DWORD PTR [esi+6]\r\n    add esi,10\r\n\r\n@@free_handle:\r\n    add edi,size EMSHD\r\n    inc ecx\r\n    cmp cl, EMS_MAX_HANDLES\r\n    jb @@scan_handles\r\n    pop edx\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\n;--- AL=1 search handle by name\r\n;--- in: DS:SI->handle name\r\n;--- out: DX=handle\r\n\r\n@@find_handle_by_name:\r\n\r\n    movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_DS\r\n    shl edi,4\r\n    movzx esi,si\r\n    add edi,esi\r\nfind_handle_by_name_int::   ;find handle by name, edi=name\r\n    push ebx\r\n    mov eax,[edi+0]     ; fetch to-be-searched name\r\n    mov ebx,[edi+4]     ; (8 byte binary string)\r\nif 0 ;MS Emm386 allows to search a handle with no name!\r\n    or eax,eax\r\n    jnz @@valid_search_term\r\n    or ebx,ebx\r\n    jz @@invalid_search_term\r\n@@valid_search_term:\r\nendif\r\n    xor ecx,ecx\r\n    mov edi, [EMSNameTable]\r\n    mov esi, [EMSHandleTable]\r\n@@scan_for_name:\r\n    test [esi].EMSHD.ehd_bFlags, EMSH_USED\r\n    jz @@skipitem\r\n    cmp [edi+0],eax\r\n    jnz @@skipitem\r\n    cmp [edi+4],ebx         ; Note that open handles do not have\r\n    jz @@found_handle      ; to have a name.\r\n@@skipitem:\r\n    add esi,size EMSHD\r\n    add edi,8\r\n    inc ecx\r\n    cmp cl,EMS_MAX_HANDLES\r\n    jb @@scan_for_name\r\n    pop ebx\r\n    mov ah,0a0h             ; \"no corresponding handle found\"\r\n    ret\r\n@@found_handle:\r\n    pop ebx\r\n    mov edx,ecx\r\n    mov ah,EMSS_OK\r\n    ret\r\nif 0\r\n@@invalid_search_term:\r\n    pop ebx\r\n    mov ah,0a1h         ; \"handle found had no name\"\r\n    ret\r\nendif\r\n\r\nems4_get_handle_info ENDP\r\n\r\n;--- client DS:SI -> map_and_jump\r\n;--- AL=0 -> physical page numbers\r\n;--- AL=1 -> segment addresses\r\n\r\nems4_map_new_pagemap proc\r\n    movzx edi, word ptr [ebp].Client_Reg_Struc.Client_DS\r\n    shl edi,4\r\n    movzx esi,si\r\n    add edi,esi     ; edi -> map address buffer\r\n    movzx ecx, word ptr [edi].map_and_jump.new_page_map_ptr+0\r\n    movzx esi, word ptr [edi].map_and_jump.new_page_map_ptr+2\r\n    push edi\r\n    shl esi, 4\r\n    add esi, ecx\r\n    movzx ecx, [edi].map_and_jump.new_page_map_len\r\n    mov edi, esi\r\n    call ems4_map_multiple_edi  ;map ECX pages from EDI, DX=handle, AL=type\r\n    pop edi\r\n    and ah,ah\r\n    ret\r\nems4_map_new_pagemap endp\r\n\r\n; 6755:\r\n; AH = 55h: EMS 4.0 alter page map and jump\r\n; AL = 0/1\r\n; DX = handle\r\n; DS:SI -> map_and_jump structure\r\n\r\nems4_alter_map_jump proc\r\n    call ems4_map_new_pagemap\r\n    jnz @@failed\r\n    mov ecx,[edi].map_and_jump.target_address\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EIP, cx\r\n    shr ecx, 16\r\n    mov [ebp].Client_Reg_Struc.Client_CS, ecx\r\n@@failed:\r\n    ret\r\nems4_alter_map_jump endp\r\n\r\n; 6756:\r\n; AH = 56h: EMS 4.0 alter page map and call\r\n; AL = 0/1/2\r\n; DX = handle\r\n; if AL=0/1: [in] DS:SI -> map_and_call structure ()\r\n; if AL=2: [out] BX = additional stack space required\r\n\r\nems4_alter_map_call proc\r\n    cmp al,2\r\n    jz @@getstackspace\r\n\r\n    call ems4_map_new_pagemap\r\n    jnz @@failed\r\n\r\nif 0    ; nested execution consumes ring 0 stack, which is to avoid\r\n\r\n;--- prepare nested execution\r\n;--- and run the client proc\r\n\r\n    push edx\r\n    push eax\r\n    call Begin_Nest_Exec\r\n    movzx edx,word ptr [edi].map_and_call.target_address+0\r\n    movzx ecx,word ptr [edi].map_and_call.target_address+2\r\n    call Simulate_Far_Call\r\n    call Resume_Exec\r\n    call End_Nest_Exec\r\n    pop eax\r\n    pop edx\r\n\r\n;--- set old state\r\n\r\n    movzx ecx, [edi].map_and_call.old_page_map_len\r\n    movzx esi, word ptr [edi].map_and_call.old_page_map_ptr+0\r\n    movzx edi, word ptr [edi].map_and_call.old_page_map_ptr+2\r\n    shl edi, 4\r\n    add edi, esi\r\n    call ems4_map_multiple_edi\r\n@@failed:\r\n    ret\r\n@@getstackspace:\r\n    mov bx, 4\r\n    ret\r\n\r\nelse\r\n\r\n;--- this implementation copies 16-bit code onto the client's stack.\r\n;--- this code calls int 67h, ah=50h (map multiple pages).\r\n\r\n    mov esi, offset clproc\r\n    mov al, byte ptr [ebp].Client_Reg_Struc.Client_EAX\r\n    mov [esi+(_clal-clproc)],al\r\n    movzx eax,[edi].map_and_call.old_page_map_len\r\n    mov [esi+(_clcx-clproc)],ax\r\n    mov [esi+(_cldx-clproc)],dx\r\n    mov eax,[edi].map_and_call.old_page_map_ptr\r\n    mov [esi+(_clsi-clproc)],ax\r\n    shr eax,16\r\n    mov [esi+(_clds-clproc)],ax\r\n\r\n    push edi\r\n    movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_ESP\r\n    movzx edi,word ptr [ebp].Client_Reg_Struc.Client_SS\r\n    push edi\r\n    shl edi, 4\r\n    sub ecx, sizeclproc\r\n    push ecx\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_ESP,cx\r\n    add edi, ecx\r\n    mov ecx, sizeclproc\r\n    rep movsb\r\n    pop eax\r\n    pop ecx\r\n    pop edi\r\n    push edx\r\n    mov edx,eax\r\n    call Simulate_Far_Call\r\n    mov cx,word ptr [edi].map_and_jump.target_address+2\r\n    movzx edx,word ptr [edi].map_and_jump.target_address+0\r\n    call Simulate_Far_Call\r\n    pop edx\r\n    mov ah,EMSS_OK\r\n@@failed:    \r\n    ret\r\n\r\n@@getstackspace:\r\n    mov bx, sizeclproc+4\r\n    ret\r\n\r\nendif\r\n    align 4\r\n\r\nems4_alter_map_call endp\r\n\r\n;-------------------------------------------------------------\r\n; 6757:\r\n; AH = 57h: EMS 4.0 move/exchange memory region\r\n; AL = 0: move memory region\r\n; AL = 1: exchange memory region\r\n; DS:SI -> EMM57\r\n\r\n;-- this function must work even if no EMS page frame is defined!\r\n;-- EMS regions may overlapp!\r\n\r\nE57REG struct\r\ne57_bTyp DB ?   ; +0  memory type\r\ne57_wHdl DW ?   ; +1  handle\r\ne57_wOfs DW ?   ; +3  ofs\r\ne57_wSeg DW ?   ; +5  segm./log. page\r\nE57REG ends\r\n\r\n;--- memory type:\r\n;--- 00: conv. memory\r\n;--- 01: expanded memory\r\n;--- handle:\r\n;--- == NULL: conv. memory\r\n;--- <> NULL: EMS handle\r\n\r\n;--- locations ebp+xx are ok to store temp values\r\n;--- but it isn't safe to store something there\r\n;--- if interrupts are enabled!\r\n\r\nRegionSrc   equ <ebp.Client_Reg_Struc.Client_Error>\r\npPTE        equ <ebp-4>\r\nregSize     equ <ebp-8>\r\nlinAddr     equ <ebp-12>\r\n\r\nems4_move_memory PROC\r\n\r\n    cmp al,1\r\n    ja bad_subfunc ; this is an invalid subfunction\r\n    movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_DS\r\n    movzx esi,si\r\n    shl edi,4\r\n    add edi,esi     ; edi -> EMS region buffer address\r\n\r\nifdef __JWASM__ ;masm emits \"string or text literal too long\"\r\n    @dprintf ?EMSDBG, <\"EMM 57h: siz=%X src=%X/%X/%X/%X, dst=%X/%X/%X/%X\">, [edi].EMM57.e57_dwSize,\\\r\n        [edi].EMM57.e57_bSrcTyp, [edi].EMM57.e57_wSrcHdl, [edi].EMM57.e57_wSrcSeg, [edi].EMM57.e57_wSrcOfs,\\\r\n        [edi].EMM57.e57_bDstTyp, [edi].EMM57.e57_wDstHdl, [edi].EMM57.e57_wDstSeg, [edi].EMM57.e57_wDstOfs\r\nendif\r\n    mov ecx,[edi].EMM57.e57_dwSize\r\n    test ecx,ecx\r\n    je @@ok                        ; always succeeds if no bytes are moved\r\n    mov ah,EMSS_REGLEN_EXCEEDS_1MB\r\n    cmp ecx,65536*16                ; does region length exceed 1M?\r\n    ja @@exit\r\n\r\n    mov eax,[PageMapHeap]\r\n    mov [regSize], ecx\r\n    mov [pPTE], eax\r\n\r\n; process region src + dst information\r\n\r\n    lea edi,[edi+EMM57.e57_bSrcTyp]\r\n    mov al,2    \r\n@@nextreg:\r\n    mov [RegionSrc],ebx\r\n    movzx ecx,[edi].E57REG.e57_wOfs\r\n    movzx ebx,[edi].E57REG.e57_wSeg\r\n    cmp [edi].E57REG.e57_bTyp,1     ;0 = conv, 1 = expanded\r\n    jb @@calc_conv\r\n    mov ah,EMSS_TYPE_UNDEFINED\r\n    jnz @@exit\r\n\r\n; destination is EMS, test if handle is valid\r\n\r\n    mov dx, [edi].E57REG.e57_wHdl\r\n    call EMS_TEST_HANDLE\r\n\r\n; check if specified offset is outside logical page (must be < 4000h)\r\n\r\n    mov ah,EMSS_OFFS_EXCEEDS_PGSIZ  ; preload error code\r\n    test ch,0C0h\r\n    jnz @@exit\r\n\r\n;--- map in EMS pages \r\n;--- ecx = \"offset\"\r\n;--- DX:BX = EMS page start\r\n\r\n    push eax             ;save AL counter\r\n    add ecx,[regSize]\r\n    mov esi,[pPTE]\r\n    mov eax, esi\r\n    sub eax, ?SYSBASE+?PAGETABSYS\r\n    shl eax, 10\r\n    add eax, ?SYSBASE\r\n    add ecx, eax        ;let ecx point to end of region\r\n    mov [linAddr],eax\r\n@@nextpagetomap:\r\n    pushad\r\n    call EMS_get_abs_page;get abs page of DX:BX in BX\r\n    jc @@map_failed\r\n    mov edi, esi\r\n    call EMS_MAP_ABS_PAGE_EX     ;requires EAX,BX,EDI to be set\r\n    popad\r\n    inc ebx             ;next EMS page\r\n    add eax, 4000h      ;proceed with linear address\r\n    add esi, 4*4        ;proceed with PTE pointer\r\n    cmp eax,ecx\r\n    jb @@nextpagetomap\r\n    mov [pPTE],esi\r\n    call FlushTLB\r\n    mov ebx,[linAddr]\r\n    movzx ecx, [edi].E57REG.e57_wOfs\r\n    pop eax\r\n    add ebx, ecx\r\n    jmp @@regdone\r\n\r\n@@map_failed:\r\n    popad\r\n    pop eax\r\n    mov ah,EMSS_LENGTH_EXCEEDED\r\n    jmp @@exit\r\n\r\n@@calc_conv:\r\n    shl ebx,4       ; convert seg to memory offset\r\n    add ebx,ecx\r\n    mov ah,EMSS_MBBOUNDARY_CROSSED  ; conv memory region must not exceed 1 MB boundary\r\n    mov ecx, [regSize]\r\n    add ecx, ebx\r\n    cmp ecx, 100000h\r\n    ja  @@exit\r\n@@regdone:\r\n    add edi,size E57REG\r\n    dec al\r\n    jnz @@nextreg\r\n    lea edx, [edi-sizeof EMM57]\r\n\r\n;--- both regions processed\r\n;--- dest address in EBX, src in RegionSrc\r\n\r\n    mov ecx, [regSize]\r\n    mov esi, [RegionSrc]\r\n    mov edi, ebx\r\n\r\n; if src and dest are EMS, test if they overlapp\r\n\r\n    cmp edi, 100000h       ; is dst expanded memory?\r\n    jb @@nooverlapp\r\n    cmp esi, 100000h       ; is src expanded memory?\r\n    jb @@nooverlapp\r\n\r\n    mov ax, [edx].EMM57.e57_wSrcHdl\r\n    cmp ax, [edx].EMM57.e57_wDstHdl ; are handles equal?\r\n    jnz @@nooverlapp\r\n\r\n    movzx eax, [edx].EMM57.e57_wDstSeg\r\n    movzx ebx, [edx].EMM57.e57_wSrcSeg\r\n    shl eax, 14\r\n    shl ebx, 14\r\n    or ax, [edx].EMM57.e57_wDstOfs\r\n    or bx, [edx].EMM57.e57_wSrcOfs\r\n\r\n;--- the problem case is:\r\n;--- source < destination AND source + size > destination\r\n\r\n    cmp ebx, eax        ; source < destination?\r\n    jnc @@nooverlapp    ; no, use std copy\r\n    add ebx, ecx        ; ebx == source + size\r\n    cmp eax, ebx        ; destination >= source + size?\r\n    jc @@overlapped\r\n\r\n@@nooverlapp:\r\n    cmp byte ptr [ebp].Client_Reg_Struc.Client_EAX,0\r\n    jne @@xchg\r\n    call _MoveMemory\r\n@@ok:\r\n    mov ah,EMSS_OK  ; zero ah, no error return\r\n\r\n; exit with code in AH\r\n\r\n@@exit:\r\n    @dprintf ?EMSDBG, <\"=%X\",10>, ah\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EDX\r\n    mov ebx,[ebp].Client_Reg_Struc.Client_EBX\r\n    ret\r\n\r\n@@overlapped:\r\n    mov ah,EMSS_REGIONS_OVERLAPP    ;for xchg, overlapp is invalid\r\n    cmp byte ptr [ebp].Client_Reg_Struc.Client_EAX,0\r\n    jne @@exit\r\n    std\r\n    lea esi,[esi+ecx-1]\r\n    lea edi,[edi+ecx-1]\r\n    mov eax,ecx\r\n    and ecx,3\r\n    REP MOVSB\r\n    MOV ECX,EAX\r\n    sub esi,3\r\n    sub edi,3\r\n    call _MoveMemory\r\n    cld\r\n    mov ah,EMSS_OVERLAP_OCCURED ;status \"overlapp occured\"\r\n    jmp @@exit\r\n\r\n;--- xchange memory regions\r\n;--- esi=src, edi=dst, ecx=size\r\n\r\n@@xchg:\r\n    mov ebx, [ebp].Client_Reg_Struc.Client_EFlags\r\n    test bh,2\r\n    jz @@noenable\r\n    call EnableInts\r\n    sti\r\n@@noenable:\r\n    mov edx, ecx\r\n    shr ecx,2\r\n    jz @@xchgb\r\n@@xdw:\r\n    mov eax,[edi]\r\n    movsd\r\n    mov [esi-4],eax\r\n    dec ecx\r\n    jnz @@xdw\r\n@@xchgb:\r\n    mov ecx,edx\r\n    and ecx,3\r\n    je @@xchgdone\r\n@@xb:\r\n    mov al,[edi]\r\n    movsb\r\n    mov [esi-1],al\r\n    dec ecx\r\n    jnz @@xb\r\n@@xchgdone:\r\n    test bh,2\r\n    jz @@ok\r\n    cli\r\n    call DisableInts\r\n    jmp @@ok\r\n\r\n    align 4\r\n\r\n_MoveMemory:\r\n    mov ebx,[PageMapHeap]\r\n    mov eax,[pPTE]\r\n    mov [PageMapHeap],eax\r\n    call MoveMemory\r\n    mov [PageMapHeap], ebx\r\n    retn\r\n    align 4\r\n\r\nRegionSrc   equ <>\r\npPTE        equ <>\r\nregSize     equ <>\r\nlinAddr     equ <>\r\n\r\nems4_move_memory ENDP\r\n\r\n; 6758:\r\n; AH = 58h: EMS 4.0 get addresses of mappable pages, number of mappable pages\r\n; AL = 0: ES:DI -> buffer, returns no of pages in CX\r\n; buffer item:\r\n;  WORD segment\r\n;  WORD physical page\r\n; AL = 1: returns no of pages in CX\r\n; the items in the buffer must be sorted in ascending segment order!\r\n\r\nems4_get_mappable_info PROC\r\n    cmp al,1\r\n    ja bad_subfunc ; only 0 and 1 allowed\r\n\r\n    movzx ecx, [bEmsPhysPages]\r\n    mov WORD PTR [ebp].Client_Reg_Struc.Client_ECX,cx   ; mappable pages in CX\r\n\r\n    cmp al,1\r\n    je @@nummap\r\n    jecxz @@nummap\r\n    \r\n    movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl esi,4\r\n    movzx edi,di\r\n    add edi,esi     ; edi -> buffer address\r\n    mov ah,0\r\n@@mapinfo_loop:\r\n    call @@getpage  ; get phys page in AL\r\n    movzx edx,al\r\n    mov ah,0\r\n    shl eax,16\r\n    mov ah,byte ptr [edx+EMSPage2Segm]\r\n    mov al,0\r\n    stosd\r\n    loop @@mapinfo_loop\r\n@@nummap:\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\n;--- get the next segment address to AH    \r\n\r\n@@getpage:\r\n    push ecx\r\n    mov esi, offset EMSPage2Segm\r\n    mov dl,-1\r\n    mov cl,[bEmsPhysPages]\r\n@@nextitem:\r\n    lodsb\r\n    sub al,ah\r\n    jbe @@skipitem\r\n    cmp al,dl\r\n    ja @@skipitem\r\n    mov dl,al\r\n    mov dh,cl\r\n@@skipitem:\r\n    loop @@nextitem\r\n    mov al,[bEmsPhysPages]\r\n    sub al,dh\r\n    pop ecx\r\n    ret\r\nems4_get_mappable_info ENDP\r\n\r\n; 6759:\r\n; AH = 59h: EMS 4.0 get hardware config/get number of raw pages\r\n; AL = 1: return raw pages in DX and BX\r\n; AL = 0: get hardware config in ES:DI\r\n\r\nems4_get_config PROC\r\n    cmp al,1    ; only subfunctions 0+1 supported\r\n    ja bad_subfunc\r\n    je EMS_GET_UNALLOCATED_PAGE_COUNT\r\n\r\n    movzx esi, WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl esi, 4\r\n    movzx edi, di\r\n    add edi, esi\r\n    mov esi,offset emm59_\r\n    mov ecx,size emm59_ shr 1\r\n    rep movsw\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\nems4_get_config ENDP\r\n\r\n; 675A:\r\n; AH = 5ah: EMS 4.0 allocate handle and standard/raw pages\r\n; in  AL = 0/1\r\n; in  BX = pages to allocate (may be 0)\r\n; out DX = handle\r\n;\r\nems4_allocate_pages PROC\r\n\r\n    cmp al,1    ; subfunction must be 0 or 1, we don't care if either\r\n    ja bad_subfunc\r\n    jmp allocate_pages_plus_zero\r\n\r\nems4_allocate_pages ENDP\r\n\r\n; 675B: alternate map (AM) and DMA register sets.\r\n; AL=subfunction\r\n;  0=get AM register set\r\n;   out: BL=current active AM register set or 00;\r\n;        if BL=0, ES:DI= ^ save area, set by subfunction 1\r\n;  1=set AM register set\r\n;   in: BL=new AM register set or 00;\r\n;       if BL=0, ES:DI= ^ save area\r\n;  2=get AM save array size\r\n;  3=allocate AM register set\r\n;   out: BL=AM register set # or 00 (00 means: no hardware AM register sets)\r\n;  4=deallocate AM register set\r\n;   in: BL=AM register set ( 00 is allowed and nothing done )\r\n;  5=allocate DMA register set\r\n;  6=enable DMA on AM register set\r\n;  7=disable DMA on AM register set\r\n;  8=deallocate DMA register set\r\n\r\nems4_alt_map_reg_set PROC\r\n\r\nif ?SUPP5B\r\n    cmp al,1\r\n    jb @@is00\r\n    jz @@is01\r\n    cmp al,3\r\n    jb @@is02\r\n    jz @@is03\r\n    cmp al,5\r\n    jb @@is04\r\n    jz @@is05\r\n    cmp al,7\r\n    jb @@is06\r\n    jz @@is07\r\n    cmp al,8\r\n    jz @@is08\r\n    jmp bad_subfunc\r\n@@is00:               ; get AM register set\r\n    mov bl,0          ; always return BL=0, since no hardware AM register sets are supported\r\n    mov eax,[mapptr]\r\n    and eax, eax\r\n    jz  @@noptr\r\n    push eax\r\n    movzx edi,ax\r\n    shr eax,16\r\n    shl eax,4\r\n    add edi, eax\r\n    mov al,0\r\n    call save_page_map_int\r\n    pop eax\r\n@@noptr:    \r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EDI,ax\r\n    shr eax,16\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_ES,ax\r\n    jmp @@done\r\n\r\n@@is01:               ; set AM register set\r\n    mov ah,EMSS_ALT_MAPS_UNSUPP ;AM register sets not supported (bl must be 00)\r\n    cmp bl,0\r\n    jnz @@exit        ; no hardware AM register sets supported!\r\n\r\n;--- v5.84:\r\n;--- if the source array pointer ES:DI is invalid ( checksum invalid),\r\n;--- error A3 (EMSS_SRCARRAY_INVALID) is returned!\r\n\r\n    mov ax, word ptr [ebp].Client_Reg_Struc.Client_ES\r\n    shl eax, 16\r\n    mov ax, word ptr [ebp].Client_Reg_Struc.Client_EDI\r\n    mov [mapptr], eax\r\n    and eax, eax\r\n    jz  @@done\r\n    movzx esi, ax\r\n    shr eax, 16\r\n    shl eax, 4\r\n    add esi, eax\r\n    call restore_page_map_int ; will set AH ( either 00 or error A3h! )\r\n    jmp @@exit\r\n@@is02:               ; get AM save area size\r\n    mov dx,emm59_.e59_sizcont\r\n    jmp @@done\r\n@@is03:               ; allocate AM register set\r\n@@is05:               ; allocate DMA register set\r\n    mov bl,0          ; 00 is returned, indicating that hardware AM/DMA register sets aren't supported\r\n    jmp @@done\r\n@@is04:               ; deallocate AM register set\r\n@@is06:               ; enable DMA on AM register set\r\n@@is07:               ; disable DMA on AM register set\r\n@@is08:               ; deallocate DMA register set\r\n    mov ah,EMSS_ALT_MAPS_UNSUPP ;AM/DMA register sets not supported (bl must be 00)\r\n    cmp bl,0\r\n    jnz @@exit\r\n@@done:\r\n    mov ah,EMSS_OK\r\n@@exit:\r\nelse\r\n    mov ah,EMSS_ACCESS_DENIED   ;access denied by OS\r\nendif\r\n    ret\r\n\r\nems4_alt_map_reg_set ENDP\r\n\r\nendif   ;?LIM32\r\n\r\n; dynamically compute free EMS pages\r\n; return free pages in EAX\r\n; other registers preserved\r\n\r\nGetFreeEMSPages PROC\r\n\r\n    push edx\r\n    call Pool_GetFree16KPages   ; free 16k pages in pool in EAX\r\n\r\n    mov edx, [EMSPagesMax]\r\n    sub edx, [EMSPagesUsed]     ; edx == free EMS pages numbers\r\n    jbe @@nofree\r\n    cmp edx, eax                ; if there is not enough free pages\r\n    jnc @@eaxok                 ; to backup free EMS pages, then\r\n    mov eax, edx                ; use the lower value\r\n@@eaxok:\r\n\r\n@@ret:\r\n    @dprintf ?POOLDBG, <\"GetFreeEMSPages: eax=%X EMSMax-EMSUsed=%X\",10>, eax, edx\r\n    pop edx\r\n    ret\r\n\r\n@@nofree:\r\n    xor eax,eax\r\n    jmp @@ret\r\n    align 4\r\n\r\nGetFreeEMSPages ENDP\r\n\r\n; allocate an EMS page;\r\n; IN: edi -> EMSPD for new page to allocate;\r\n; OUT: C if fail;\r\n; modifies EAX\r\n\r\nAllocateEMSPage PROC\r\n\r\n    push edi\r\n    push edx\r\n\r\n    call Pool_Allocate16KPage   ;return index in AX, subindex in DL\r\n    jc @@exit\r\n\r\n;  set EMSPD.wIndex  == descriptor index\r\n;  set EMSPD.bSubIdx == descriptor subindex \r\n\r\n    mov [edi].EMSPD.wIndex,ax\r\n    sub edi, [EMSPageAllocationStart]\r\n    shr edi, 2\r\n\r\n    @dprintf ?POOLDBG, <\"AllocateEMSPage ok, page=%X\",10>, di\r\n\r\n    add edi, [EMSPageAllocationEnd]\r\n    mov [edi].EMSPD2.bSubIdx,dl\r\n    inc [EMSPagesUsed]\r\n    clc\r\n@@exit:\r\n    pop edx\r\n    pop edi\r\n    ret\r\n\r\nAllocateEMSPage ENDP\r\n\r\n;--- allocate ECX new pages for a handle\r\n;--- inp: ESI=EMSPD\r\n;--- modifies ECX, EDI, EAX, ESI\r\n\r\nAllocateEMSPages proc\r\n\r\n    push ebx\r\n    mov ebx,ecx\r\n    MOV EDI,[EMSPageAllocationStart]    ; mark the pages as used\r\n    MOV ECX,[EMSPagesMax]\r\nnextitem:\r\n    or eax,-1\r\n    REPNZ SCASD\r\n    jnz nofreepg             ; out of free pages?\r\n    sub edi,4\r\n    call AllocateEMSPage     ; allocate the page\r\n    jc allocerr\r\n    mov eax,edi\r\n    sub eax,[EMSPageAllocationStart]\r\n    shr eax,2\r\n    mov [esi].EMSPD.wNext, ax\r\n    mov esi, edi\r\n    add edi,4\r\nif ?YIELDOPEN\r\n    call Yield\r\nendif\r\n    dec ebx\r\n    JNZ nextitem\r\n    pop ebx\r\n    ret\r\nallocerr:\r\nnofreepg:\r\n    pop ebx\r\n    stc\r\n    ret\r\nAllocateEMSPages endp\r\n\r\n\r\n; release EMS page in AX\r\n; destroy no registers\r\n\r\nReleaseEMSPage  PROC\r\n    push eax\r\n    push ecx\r\n    push edx\r\n    mov edx, [EMSPageAllocationStart]\r\n    mov ecx, [EMSPageAllocationEnd]\r\n    movzx eax,ax\r\n    lea edx, [edx+eax*4]\r\n    cmp edx, ecx\r\n    jae @@ret                           ; out of range\r\n    movzx ecx,[ecx+eax].EMSPD2.bSubIdx  ; get sub index\r\n    movzx eax,[edx].EMSPD.wIndex        ; pool descriptor index\r\n    cmp ax,-1\r\n    je  @@ret\r\n\r\n    call Pool_Free16KPage\r\n    jc @@ret\r\n    mov [edx].EMSPD.wIndex,-1\r\n\r\n    @dprintf ?POOLDBG, <\"ReleaseEMSPage ok, EMSPD=%X\",10>, edx\r\n\r\n    dec [EMSPagesUsed]\r\n@@ret:\r\n    pop edx\r\n    pop ecx\r\n    pop eax\r\n    ret\r\nReleaseEMSPage  ENDP\r\n\r\n.text$03 ends\r\n\r\n.text$04 segment\r\n\r\n;--- init EMS handle status table (255 handles * 8)\r\n;--- inp: EDI -> free memory\r\n\r\nSetEMSHandleTable proc public\r\n\r\n    mov ecx, EMS_MAX_HANDLES * size EMSHD\r\n    call HeapMalloc\r\n    MOV [EMSHandleTable],EDI; set start of EMS handle table.\r\n    MOV EAX,0FF01FFFFh      ; Handle 0 is reserved.\r\n    STOSD\r\n    mov eax,0FF00FFFFh\r\n    MOV ECX, EMS_MAX_HANDLES-1  ; fill the rest\r\n    REP STOSD\r\n    ret\r\nSetEMSHandleTable endp\r\n\r\nSetEMSStateTable proc public\r\n    mov ecx, EMS_MAXSTATE * size EMSSTAT\r\n    call HeapMalloc\r\n    mov [EMSStateTable],EDI\r\n    mov eax, -1\r\n    mov ecx, EMS_MAXSTATE*4\r\n    rep stosd\r\n    ret\r\nSetEMSStateTable endp\r\n\r\nSetEMSNameTable proc\r\n    mov ecx,8*EMS_MAX_HANDLES\r\n    call HeapMalloc\r\n    mov [EMSNameTable],EDI\r\n    mov eax,'TSYS'      ; store \"SYSTEM\" as first handle name\r\n    stosd\r\n    mov eax,'ME'\r\n    stosd\r\n    mov ecx,8*(EMS_MAX_HANDLES-1)/4\r\n    xor eax,eax\r\n    rep stosd\r\n    ret\r\nSetEMSNameTable endp\r\n\r\n;--- init EMS variables\r\n;--- ESI -> JEMMINIT\r\n\r\nEMS_Init1 proc public\r\n    movzx eax, [esi].JEMMINIT.MaxEMSPages\r\n    mov dl, [esi].JEMMINIT.NoFrame\r\n    mov [EMSPagesMax],eax\r\n    mov al, [esi].JEMMINIT.NoEMS\r\n    mov [bNoEMS],al\r\n    cmp al,0\r\n    jz @@emsactive\r\n    mov dl,1\r\n    mov eax,[dwRes]\r\n    mov byte ptr [eax + DEVICE_NAME + 3],'Q' ;EMMXXXX0 -> EMMQXXX0\r\n@@emsactive:\r\n    mov [bNoFrame], dl\r\n    cmp dl, 0\r\n    jnz @@noframe\r\n    mov [bEmsPhysPages], 4\r\n    movzx eax, byte ptr [esi].JEMMINIT.Frame+1\r\n    mov edx, eax\r\n    mov dh,dl\r\n    add dh,4\r\n    mov word ptr [EMSPage2Segm+0], dx\r\n    add dx,0808h\r\n    mov word ptr [EMSPage2Segm+2], dx\r\n\r\nif 0    ;not needed. Once the pages are remapped, the bits are cleared\r\n;--- reset the \"global page\" attribute for the EMS frame PTEs\r\n\r\n    lea eax, @GetPTEAddr(eax*4+?PAGETAB0)\r\n    mov ecx,16\r\n@@nextitem:\r\n    and byte ptr [eax+1],not 1\r\n    add eax,4\r\n    loop @@nextitem\r\nendif\r\n\r\n@@noframe:\r\n\r\n;--- calc mappable EMS pages\r\n;--- if a frame is set, physical pages 0-3 have been reserved already.\r\n;--- segments A000-FFFF will get the next page numbers (up to v5.79: only if NORAM is set).\r\n;--- if A000-B7FF is added to conv. memory, then they get high numbers.\r\n\r\nife ?LIM32\r\n    mov ebx,[esi].JEMMINIT.PageMap\r\n\r\nif ?INITDBG and ?EMSDBG\r\n    xor ecx,ecx\r\n    @dprintf <?INITDBG or ?EMSDBG>, <\"SysMem table:\",10>\r\n@@nextitemx:\r\n    @dprintf <?INITDBG or ?EMSDBG>, <\"%c\">, byte ptr [ebx+ecx]\r\n    inc ecx\r\n    test cl,0Fh\r\n    jnz @@nextitemx\r\n    test cl,30h\r\n    jz @F\r\n    @dprintf <?INITDBG or ?EMSDBG>, <'-'>\r\n    jmp @@nextitemx\r\n@@:\r\n    @dprintf <?INITDBG or ?EMSDBG>, <10>\r\n    and cl,cl\r\n    jnz @@nextitemx\r\nendif\r\n\r\n;--- up to v5.79:\r\n;--- if NORAM, include 'IIII', then 'RRRR'\r\n;--- if RAM,   include 'RRRR' only\r\n;--- since v5.80: NORAM isn't relevant anymore.\r\n;--- if A000 is included, then scan begins with C000-FC00, then 4000-BC00\r\n;--- if A000 is NOT included, then scan begins with A000-FC00, then 4000-9C00\r\n\r\n    mov dh,0            ;DH=num mappable pages (below C000/A000?)\r\n    xor ecx,ecx\r\n    mov cl,0C0h\r\n    cmp dword ptr [ebx+0A0h], 'IIII'\r\n    jz @F\r\n    mov cl,0A0h\r\n@@:\r\n\tmov dl,cl\r\n@@nextitem2:\r\n    cmp dword ptr [ebx+ecx], 'RRRR'\r\n    jz @F\r\n    cmp dword ptr [ebx+ecx], 'IIII'\r\n    jnz @@skipitem\r\n@@:\r\n    movzx eax,[bEmsPhysPages]\r\n    cmp al,?MAXPHYSPG\r\n    jnc @@skipitem\r\n    mov [EMSPage2Segm+eax],cl\r\n    inc eax\r\n    inc dh\r\n    mov [bEmsPhysPages],al\r\nif 0        ;is not needed, since once the page is remapped, the bit is reset    \r\n    lea eax, @GetPTEAddr(?PAGETAB0+ecx*4)\r\n    and byte ptr [eax+00+1],not 1   ;reset \"global\" attribute\r\n    and byte ptr [eax+04+1],not 1   ;reset \"global\" attribute\r\n    and byte ptr [eax+08+1],not 1   ;reset \"global\" attribute\r\n    and byte ptr [eax+12+1],not 1   ;reset \"global\" attribute\r\nendif\r\n@@skipitem:\r\n    add cl,4\r\n    jnz @F\r\n    mov cl,byte ptr [esi].JEMMINIT.Border+1\r\n@@:\r\n    cmp cl,dl\r\n    jnz @@nextitem2\r\n    mov [bPagesConv],dh\r\n    movzx edx,dh\r\n    add word ptr [EMSPagesMax],dx\r\n    jns @F\r\n    mov word ptr [EMSPagesMax],MAX_EMS_PAGES_POSSIBLE\r\n@@:\r\n    @dprintf ?INITDBG, <\"EMS_Init1: phys EMS pages=%X\",10>, edx\r\n    shl edx,2\r\n    add [pmem.dwMaxMem4K],edx\r\nendif   ;?LIM32\r\n\r\n    movzx eax,[bEmsPhysPages]\r\n    inc eax     ;+ 1 dword for checksum\r\n    shl eax,2\r\n    mov emm59_.e59_sizcont, ax\r\n\r\nif ?VCPI\r\n    mov al, [esi].JEMMINIT.NoVCPI\r\n    mov [bNoVCPI],al\r\nendif\r\n    ret\r\nEMS_Init1 endp\r\n\r\n;--- alloc the EMS management tables\r\n;--- ESI -> JEMMINIT\r\n;--- EDI -> free space (linear address)\r\n\r\nEMS_Init2 proc public\r\n\r\n;--- set the EMS tables    \r\n\r\n; alloc EMS handle/status/name table (4/8/8 bytes)\r\n\r\n    call SetEMSHandleTable\r\n    call SetEMSStateTable\r\n    call SetEMSNameTable\r\n\r\n    @dprintf ?INITDBG, <\"EMS handle table=%X, state table=%X, name table=%X\",10>, EMSHandleTable, EMSStateTable, EMSNameTable\r\n\r\n;--- allocate and init EMS page descriptor array (EMSPD)\r\n;--- size of EMSPD is 4 bytes, it\r\n;--- consists of 2 words, first is a pointer to the next EMS page\r\n;--- second is the pool descriptor index of this EMS page\r\n\r\n    mov ecx,[EMSPagesMax]\r\n    mov [EMSPageAllocationStart],edi\r\n\r\n    @dprintf ?INITDBG, <\"EMS pages=%X at %X\",10>, ecx, edi\r\n\r\n    push ecx\r\n    or eax,-1\r\n    rep stosd\r\n    pop ecx\r\n    mov [EMSPageAllocationEnd],edi\r\n\r\n    @dprintf <?EMSDBG or ?INITDBG>, <\"EMS page alloc start/end=%X/%X\",10>, [EMSPageAllocationStart], [EMSPageAllocationEnd]\r\n\r\n;--- behind the EMS EMSPD array comes an array of bytes\r\n;--- which are the \"nibble\" offsets of the descriptor's bit array\r\n\r\n    inc eax\r\n    rep stosb\r\n\r\n    @dprintf <?EMSDBG or ?INITDBG>, <\"EMS array of subindices end=%X\",10>, edi\r\n\r\n;--- init EMS SYSTEM handle\r\n\r\nife ?LIM32\r\n    movzx ecx,[bPagesConv]    ;any mappable conv. memory pages?\r\n    jecxz @@nomap2\r\n\r\n    pushad\r\n\r\n    push ecx\r\n    shl ecx, 4                  ;16k -> 1k pages\r\n    movzx edi,[esi].JEMMINIT.Border\r\n    shr edi,6                   ;paragraphs -> 1K\r\n    mov al, PBF_DONTFREE or PBF_DONTEXPAND\r\n    call Pool_AllocBlocksForEMB\r\n    pop ebx\r\n\r\n    MOV EDI,[EMSPageAllocationStart]\r\n    mov esi,[EMSHandleTable]\r\n    xor ecx,ecx\r\n@@nextpage:\r\n    call AllocateEMSPage\r\n    mov [esi].EMSPD.wNext, cx\r\n    mov esi,edi\r\n    add edi,4\r\n    inc ecx\r\n    cmp ecx,ebx\r\n    jnz @@nextpage\r\n    popad\r\n@@nomap2:\r\nendif\r\n\r\n    @dprintf ?INITDBG, <\"EMSInit2 done\",10>\r\n    ret\r\nEMS_Init2 endp\r\n\r\n;--- for NoPool: check if EMSPagesMax has to be adjusted\r\n;--- in: eax = 4k pages still free in fixed XMS memory block\r\n\r\nEMS_CheckMax proc public\r\n    shr eax, 2\r\n    mov edx, [EMSPagesMax]\r\n    sub edx, [EMSPagesUsed]\r\n    cmp eax, edx\r\n    jnc @@isnodecrease\r\n    sub edx, eax\r\n    sub [EMSPagesMax],edx\r\n@@isnodecrease:\r\n    ret\r\nEMS_CheckMax endp\r\n\r\n.text$04 ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/EMS.INC",
    "content": "\r\n;--- EMS status codes\r\n\r\nEMSS_OK                 equ 00h\r\nEMSS_SOFTWARE_ERR       equ 80h ;unexpected error\r\nEMSS_HARDWARE_ERR       equ 81h ;should never occur with EMM emulators\r\nEMSS_EMM_BUSY           equ 82h ;should never occur with EMS 3.2+\r\nEMSS_INVALID_HANDLE     equ 83h\r\nEMSS_INVALID_FUNCTION   equ 84h ;function code in AH not defined\r\nEMSS_NO_MORE_HANDLES    equ 85h ;all handles in use\r\nEMSS_CONTEXT_EXISTS     equ 86h ;45h: try to free a handle with saved context\r\nEMSS_OUT_OF_PAGES       equ 87h ;43h, 51h, 5A00h, 5A01h\r\nEMSS_OUT_OF_FREE_PAGES  equ 88h ;43h, 51h, 5A00h, 5A01h\r\nEMSS_ZERO_PAGES         equ 89h ;43h\r\nEMSS_LOG_PAGE_INVALID   equ 8Ah ;44h, 50h, 55h, 56h, 57h\r\nEMSS_PHYS_PAGE_INVALID  equ 8Bh ;44h, 4Fh, 50h, 55h, 56h\r\nEMSS_CONTEXT_STACK_FULL equ 8Ch ;46h\r\nEMSS_STATE_ALREADY_SAVED equ 8Dh;47h\r\nEMSS_NO_STATE_IS_SAVED  equ 8Eh ;48h\r\nEMSS_INVALID_SUBFUNC    equ 8Fh ;4Eh, 4Fh, 50h, 52h, 53h, 54h, 57h, 58h, 59h, 5Bh\r\nEMSS_UNDEF_ATTR_TYPE    equ 90h ;\r\nEMSS_FEATURE_UNSUPP     equ 91h ;52h\r\nEMSS_OVERLAP_OCCURED    equ 92h ;5700h\r\nEMSS_LENGTH_EXCEEDED    equ 93h ;57h\r\nEMSS_CONVEMS_OVERLAPP   equ 94h ;57h (jemm never returns this code)\r\nEMSS_OFFS_EXCEEDS_PGSIZ equ 95h ;57h\r\nEMSS_REGLEN_EXCEEDS_1MB equ 96h ;57h\r\nEMSS_REGIONS_OVERLAPP   equ 97h ;5701h\r\nEMSS_TYPE_UNDEFINED     equ 98h ;57h\r\nEMSS_ALT_MAPS_UNSUPP    equ 9Ch ;5B01h, 5B04h, 5B06h, 5B07h, 5B08h\r\nEMSS_INVALID_ALT_MAP    equ 9Dh ;5Bh (jemm never returns this code)\r\nEMSS_MBBOUNDARY_CROSSED equ 0A2h;57h\r\nEMSS_SRC_ARRAY_INVALID  equ 0A3h;4E01h, 5B01h\r\nEMSS_ACCESS_DENIED      equ 0A4h;5Bh\r\n\r\n"
  },
  {
    "path": "src/EMS32.INC",
    "content": "\r\n;--- this is the Jemm EMS 3.2 implementation (functions 40h - 4Eh)\r\n;--- since this part is copyright Harald Albrecht/Tom Ehlert,\r\n;--- it has been extracted into a separate file.\r\n;--- It's included by EMS.ASM\r\n\r\n;--- API: EBP -> client registers\r\n;--- out: BX, DX and AH will be copied to client registers\r\n;--- change of other registers have to be done directly\r\n\r\n;\r\n; AH = 40h: return the actual state of the EMM-driver.\r\n;\r\nEMS_GET_STATUS PROC\r\n    MOV AH,EMSS_OK\r\n    RET\r\nEMS_GET_STATUS ENDP\r\n;\r\n; AH = 41h: request the segment address of the EMS-window\r\n;\r\nEMS_GET_PAGE_FRAME_ADDRESS PROC\r\n    mov ah,80h\r\n    cmp [bNoFrame],0\r\n    jnz @@BYE\r\n    MOV AH,EMSS_OK          ; No error occurred\r\n    MOV BH,[EMSPage2Segm+0] ; Segment address of EMS-Window/Frame\r\n    MOV BL,0\r\n@@BYE:\r\n    RET\r\nEMS_GET_PAGE_FRAME_ADDRESS ENDP\r\n\r\n;\r\n; AH = 42h: Request number of free + total EMS-pages\r\n; out: BX=free pages\r\n; out: DX=total pages\r\n\r\nEMS_GET_UNALLOCATED_PAGE_COUNT PROC\r\n\r\n    call GetFreeEMSPages\r\n    MOV EBX,EAX\r\n    MOV EDX,[EMSPagesMax]       ; total EMS pages\r\n\r\n; follow MS-DOS EMM386 7.x lead and don't throttle pages on NOEMS\r\n;   cmp cs:[bNoEMS],0\r\n;   je  @@unalloc_ret\r\n;   or  bx,bx\r\n;   je  @@unalloc_ret\r\n;   mov bx,1            ; only show maximum of 1 EMS page if NOEMS set\r\n;@@unalloc_ret:\r\n\r\n    MOV AH,EMSS_OK\r\n    RET\r\nEMS_GET_UNALLOCATED_PAGE_COUNT ENDP\r\n\r\n;\r\n; AH = 43h: Reserve pages for new EMS handle\r\n; in  BX = EMS pages to reserve\r\n; out AH=00, DX = handle\r\n\r\nEMS_ALLOCATE_PAGES PROC\r\n    MOV AH,EMSS_ZERO_PAGES  ; \"Request to reserve null pages\"\r\n    AND BX,BX\r\n    JZ SHORT @@BYE\r\n\r\nallocate_pages_plus_zero::  ;this entry allows to alloc zero pages!\r\n\r\n    MOV AH,EMSS_OUT_OF_PAGES    ; \"Not enough pages available\"\r\n    movzx ebx,bx\r\n    CMP EBX,[EMSPagesMax]\r\n    JA SHORT @@BYE\r\n\r\n    call GetFreeEMSPages\r\n    CMP EBX, EAX\r\n    MOV AH,EMSS_OUT_OF_FREE_PAGES   ; \"Not enough free pages available\"\r\n    JA SHORT @@BYE\r\n\r\n    MOV ESI,[EMSHandleTable]    ; Now search for a free Handle in the table\r\n    MOV CL,EMS_MAX_HANDLES\r\n@@SEARCH:\r\n    test [ESI].EMSHD.ehd_bFlags,EMSH_USED; Is there one free ... ?\r\n    JZ SHORT @@FOUND\r\n    ADD ESI,size EMSHD\r\n    dec cl\r\n    jnz @@SEARCH\r\n    MOV AH,EMSS_NO_MORE_HANDLES ; \"No more free Handles\"\r\n@@BYE:\r\n    RET\r\n\r\n@@FOUND:\r\n    or [ESI].EMSHD.ehd_bFlags,EMSH_USED\r\n\r\n    MOV DX,EMS_MAX_HANDLES      ; Set in DX now the actual Handle-\r\n    SUB DL,CL                   ; number\r\n\r\n    or ebx,ebx                 ; zero page allocation?\r\n    je @@allocate_exit         ; then we're done\r\n    mov ecx, ebx\r\n    call AllocateEMSPages\r\n    jc @@nofind\r\n@@allocate_exit:\r\n    MOV AH,EMSS_OK\r\n    RET\r\n@@nofind:\r\n    @dprintf ?POOLDBG, <\"EMS_ALLOCATE_PAGES: @@nofind reached, BX=%X\",10>, bx\r\n    call EMS_DEALLOCATE_PAGES       ;free the handle in DX  \r\n    MOV AH,EMSS_OUT_OF_FREE_PAGES   ; \"Not enough pages available anymore\"\r\n    ret\r\n    align 4\r\n\r\nEMS_ALLOCATE_PAGES ENDP\r\n\r\n; AH = 44h: map logical page DX:BX into physical page AL (the EMS-window)\r\n; AL = physical page (0-3)\r\n; DX = handle\r\n; BX = logical page # (or FFFF to unmap the page)\r\n;\r\nEMS_MAP_HANDLE_PAGE PROC\r\n\r\n    CALL EMS_TEST_HANDLE\r\n\r\n    CMP AL,[bEmsPhysPages]      ; not\" Only pages 0..3\r\n    JAE SHORT @@PAGE_TOO_LARGE  ; are allowed!\r\n\r\n    PUSH EBX                     ; save BX  (since it is changed)\r\n    call EMS_MAP_REL_PAGE        ; map/unmap the page in DX:BX to AL\r\n    jc @@done\r\n    call FlushTLB\r\n    MOV AH,EMSS_OK\r\n@@done:\r\n    POP EBX\r\n    RET\r\n@@PAGE_TOO_LARGE:\r\n@@ERR8B:\r\n    MOV AH,EMSS_PHYS_PAGE_INVALID   ; \"physical page does not exist\"\r\n    RET\r\n\r\nEMS_MAP_HANDLE_PAGE ENDP\r\n\r\n; AH = 45h: Release reserved memoryspace again\r\n; DX = handle to release\r\n; any pages of this handle mapped in page frame remain mapped!\r\n; this is same behaviour as MS Emm386\r\n\r\nEMS_DEALLOCATE_PAGES PROC\r\n    CALL EMS_TEST_HANDLE\r\n    and dx,dx\r\n    jz ems_release_null\r\n    MOV AH,EMSS_CONTEXT_EXISTS  ; \"A saved state still\r\n    CMP [ESI].EMSHD.ehd_bSS,-1      ; exists\" ?\r\n    JNZ SHORT @@BYE\r\n\r\n    and [esi].EMSHD.ehd_bFlags,not EMSH_USED\r\n    MOV EDI,[EMSPageAllocationStart]\r\n    xor eax, eax\r\n    jmp @@test\r\n@@LOOP:\r\n    lea esi,[edi+eax*4]\r\n    call ReleaseEMSPage          ; preserves registers\r\n@@test:\r\n    mov ax,-1\r\n    xchg ax,[esi].EMSPD.wNext\r\n    cmp ax,-1\r\n    jnz @@LOOP\r\n\r\n; zero handle name on free\r\n    mov edi, [EMSNameTable]\r\n    xor esi, esi\r\n    movzx ecx, dx\r\n    mov DWORD PTR [edi+ecx*8+0],esi\r\n    mov DWORD PTR [edi+ecx*8+4],esi\r\n\r\n    MOV AH,EMSS_OK\r\n@@BYE:\r\n    RET\r\nems_release_null:\r\nife ?LIM32\r\n    push ebx\r\n    xor ebx,ebx\r\n    call ems4_realloc\r\n    pop ebx\r\nelse\r\n    mov ah,EMSS_OK\r\nendif\r\n    ret\r\nEMS_DEALLOCATE_PAGES ENDP\r\n\r\n;\r\n; AH = 46h: determine Version-number of EMM\r\n;\r\n\r\nEMS_GET_VERSION PROC\r\nif ?LIM32\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 32h\r\nelse\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, 40h\r\nendif\r\n    MOV AH,EMSS_OK\r\n    RET\r\nEMS_GET_VERSION ENDP\r\n\r\n;--- save the frame window mapping state\r\n\r\nEMS_SaveFrameToEdi proc\r\n    mov cl,4\r\nEMS_SaveFrameToEdi endp ;fall through\r\n\r\n;--- save CL (0 - bEmsPhysPages) pages to ESI\r\n;--- EDI -> array of DWORDs, where PTEs will be stored\r\n;--- modifies CL\r\n\r\nEMS_SavePagesToEdi PROC\r\n    PUSH EAX\r\n    PUSH EDX\r\n    PUSH ESI\r\n    INC CL\r\n    xor edx,edx\r\n    mov esi,offset EMSPage2Segm\r\n    jmp @@test\r\n@@NEXTPAGE:\r\n    lodsb\r\n    movzx eax,al\r\n    lea eax, @GetPTEAddr(EAX*4+?PAGETAB0)\r\n    MOV EAX, [EAX]\r\n    mov al,dl\r\n    inc edx\r\n    STOSD\r\n@@test:\r\n    DEC CL\r\n    jnz @@NEXTPAGE\r\n    POP ESI\r\n    POP EDX\r\n    POP EAX\r\n    RET\r\nEMS_SavePagesToEdi ENDP\r\n\r\n;--- restore the frame window mapping state\r\n\r\nEMS_RestoreFrameFromEsi PROC\r\n    mov cl, 4\r\nEMS_RestoreFrameFromEsi ENDP        ;fall through\r\n\r\n;-- restore CL (0 - bEmsPhysPages) pages\r\n;-- esi -> array of DWORDs, containing PTEs\r\n\r\nEMS_RestorePagesFromEsi PROC\r\n\r\n    push edi\r\n    INC CL\r\n    jmp @@test\r\n@@NEXTPAGE:\r\n    lodsd\r\n    movzx edi,al\r\n    movzx edi,[EMSPage2Segm+EDI]\r\n    lea edi, @GetPTEAddr(EDI*4+?PAGETAB0)\r\n    MOV al,7\r\n    stosd\r\n    add eax,1000h\r\n    stosd\r\n    add eax,1000h\r\n    stosd\r\n    add eax,1000h\r\n    stosd\r\n@@test:\r\n    DEC CL\r\n    jnz @@NEXTPAGE\r\n    pop edi\r\n    call FlushTLB\r\n    MOV AH,EMSS_OK      ; report OK (because of functions $4E01/$4E02)\r\n    RET\r\n\r\nEMS_RestorePagesFromEsi ENDP\r\n\r\n; AH = 47h: Save status of EMS page frame to internally\r\n;           maintained buffers (max 1 for each handle)\r\n; in: DX = handle\r\n; it might be that there are pages mapped into the EMS page frame\r\n; which don't belong to any handles (anymore)!\r\n;\r\nEMS_SAVE_PAGES PROC\r\n    mov ah,EMSS_SOFTWARE_ERR\r\n    cmp [bNoFrame],0\r\n    jnz @@BYE\r\n    CALL EMS_TEST_HANDLE\r\n    MOV AH,EMSS_STATE_ALREADY_SAVED   ; \"State for Handle already saved\"\r\n    CMP [ESI].EMSHD.ehd_bSS,-1\r\n    JNZ @@BYE\r\n    mov edi,[EMSStateTable]\r\n    xor eax,eax\r\n@@nextitem:\r\n    cmp [edi].EMSSTAT.dPg0,-1\r\n    jz @@found\r\n    add edi,size EMSSTAT\r\n    inc eax\r\n    cmp eax,EMS_MAXSTATE\r\n    jnz @@nextitem\r\n    mov ah,EMSS_CONTEXT_STACK_FULL\r\n    ret\r\n@@found:\r\n    mov [esi].EMSHD.ehd_bSS,al\r\n    call EMS_SaveFrameToEdi\r\n    MOV AH,EMSS_OK\r\n@@BYE:\r\n    RET\r\nEMS_SAVE_PAGES ENDP\r\n\r\n; AH = 48h: Restore saved state of the EMS-window\r\n; DX = handle\r\n\r\nEMS_RESTORE_PAGES PROC\r\n    mov ah,EMSS_SOFTWARE_ERR\r\n    cmp [bNoFrame],0\r\n    jnz @@BYE\r\n    CALL EMS_TEST_HANDLE\r\n    MOV AH,EMSS_NO_STATE_IS_SAVED ; \"A saved state does not exist\"\r\n    CMP [ESI].EMSHD.ehd_bSS,-1\r\n    JZ SHORT @@BYE\r\n    mov al,-1\r\n    xchg al,[ESI].EMSHD.ehd_bSS\r\n    movzx eax,al\r\n    shl eax,4           ;size of EMSSTAT\r\n    mov esi,[EMSStateTable]\r\n    add esi, eax\r\n    push esi\r\n    CALL EMS_RestoreFrameFromEsi\r\n    pop esi\r\n    mov [esi].EMSSTAT.dPg0,-1\r\n    MOV AH,EMSS_OK\r\n@@BYE:\r\n    RET\r\n\r\nEMS_RESTORE_PAGES ENDP\r\n\r\n; log the failure\r\n\r\nEMS_NOT_IMPL PROC\r\n\r\n    @dprintf ?EMSDBG, <\"Unimplemented EMS function called, ax=%X\",10>, ax\r\n    MOV AH,EMSS_INVALID_FUNCTION      ; \"Invalid function code\"\r\n    RET\r\n\r\nEMS_NOT_IMPL ENDP\r\n\r\n; AH = 4Bh: return Number of open Handles in BX\r\n\r\nEMS_GET_OPEN_HANDLES_COUNT PROC\r\n    MOV ESI,[EMSHandleTable] ; Search Handle-status-table for\r\n    MOV ECX,EMS_MAX_HANDLES      ; assigned/given handles\r\n    XOR EBX,EBX\r\n    XOR EAX,EAX\r\n@@LOOP:\r\n    test [ESI].EMSHD.ehd_bFlags,EMSH_USED; Free ?\r\n    setnz al\r\n    ADD ESI,size EMSHD              ; Next entry\r\n    add ebx,eax\r\n    loop @@LOOP\r\n    MOV AH,EMSS_OK\r\n    RET\r\nEMS_GET_OPEN_HANDLES_COUNT ENDP\r\n;\r\n; AH = 4Ch: return number of reserved pages for a handle\r\n; inp: handle in DX.\r\n; out: pages in BX.\r\n; modifies ESI, EDI, EBX, EAX\r\n\r\nEMS_GET_PAGES_ONE_HANDLE PROC\r\n    CALL EMS_TEST_HANDLE\r\n    movzx EAX,[esi].EMSHD.ehd_wIdx\r\n    MOV EDI,[EMSPageAllocationStart]\r\n    XOR EBX,EBX\r\n    jmp @@test\r\n@@LOOP:\r\n    mov ax,[edi+eax*4].EMSPD.wNext\r\n    INC EBX\r\n@@test:\r\n    cmp ax,-1\r\n    jnz @@LOOP\r\n    MOV AH,EMSS_OK\r\n    RET\r\nEMS_GET_PAGES_ONE_HANDLE ENDP\r\n;\r\n; AH = 4Dh: determine Number of reserved pages for all Handles\r\n; ES:DI -> array of 2 WORD entries (handle, pages)\r\n; out: AH=00 success, BX=number of handles stored in array\r\n;\r\nEMS_GET_PAGES_ALL_HANDLES PROC\r\n    MOVZX ESI,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    SHL ESI,4\r\n    MOVZX EDI,DI\r\n    ADD ESI,EDI                      ; ESI -> array\r\n    PUSH EDX\r\n    MOV EDI,[EMSHandleTable]\r\n    XOR EAX,EAX                      ; current Handle-Number\r\n    XOR EBX,EBX                      ; count open handles\r\n@@NEXT_HANDLE:\r\n    test [EDI].EMSHD.ehd_bFlags,EMSH_USED ; handle free?\r\n    JZ SHORT @@NEXT\r\n    INC EBX                          ; One more Handle is open...\r\n    MOV [ESI+0],AX                   ; Place handle\r\n    PUSH EDI\r\n    movzx ecx,[edi].EMSHD.ehd_wIdx\r\n    MOV EDI,[EMSPageAllocationStart] ; get pages for handle\r\n    XOR EDX,EDX                      ; EDX is counter\r\n    jmp @@test\r\n@@LOOP:\r\n    inc edx\r\n    mov cx,[edi+ecx*4].EMSPD.wNext\r\n@@test:\r\n    cmp cx,-1\r\n    jnz @@LOOP\r\n    MOV [ESI+2],DX                   ; Set number of handle's pages\r\n    add ESI, 4\r\n    POP EDI\r\n@@NEXT:\r\n    ADD EDI,size EMSHD\r\n    INC EAX\r\n    CMP AL,EMS_MAX_HANDLES               ; All Handles processed ?\r\n    JB @@NEXT_HANDLE\r\n    POP EDX\r\n    MOV AH,EMSS_OK\r\n    RET\r\nEMS_GET_PAGES_ALL_HANDLES ENDP\r\n\r\n; AH = 4Eh: Get & Set Page Map\r\n; AL = 0,1,2,3\r\n; AL = 0: ES:DI -> array to get info\r\n; AL = 1: DS:SI -> array to set info\r\n; AL = 2: DS:SI -> array to set info, ES:DI -> array to get info\r\n; AL = 3: AL returns size of array (bytes)\r\n\r\n@CHKSUM MACRO REG\r\n    MOV EAX,[REG+04]        ; Calculate checksum\r\n    ADD EAX,[REG+08]\r\n    ADD EAX,[REG+12]\r\n    ADD EAX,[REG+16]\r\nENDM\r\n\r\nEMS_GET_SET_PAGE_MAP PROC\r\n    CMP AL,3                    ; Subfunction 0 to 3 ?\r\n    JA bad_subfunc\r\n    JZ SHORT @@SUBF_3          ; Size of field\r\n    CMP AL,1\r\n    JZ SHORT @@SUBF_1          ; Set Page Map\r\n\r\n; AL = 2: Get & Set Page Map\r\n; AL = 0: Get Page Map - save Hardwarestatus\r\n\r\n@@SUBF_0:\r\n    MOVZX ECX, WORD PTR [ebp].Client_Reg_Struc.Client_ES; ES:DI ^ convert from statusfield in\r\n    SHL ECX, 4                  ; usual REAL-Mode-Format\r\n    MOVZX EDI, DI\r\n    ADD EDI, ECX\r\nsave_page_map_int::                 ; <--- internal, EDI=save area, AL=0 ( ax=5B00h )\r\n    PUSH EAX                     ; save Subfunctioncode\r\n    PUSH edi\r\n    add edi,4\r\n    mov cl, [bEmsPhysPages]\r\n    CALL EMS_SavePagesToEdi\r\n    pop edi\r\n    @CHKSUM EDI                     ; Calculate checksum and ...\r\n    stosd\r\n    POP EAX                     ; restore and examen subfunctioncode\r\n    CMP AL,2                    ; if subfuction 2 is wanted,\r\n    JZ SHORT @@SUBF_1          ; then also set new map\r\n    MOV AH,EMSS_OK\r\n    RET\r\n\r\n; Subf. 1: Set Page Map - restore Hardwarestatus\r\n\r\n@@SUBF_1:\r\n    MOVZX ECX,WORD PTR [ebp].Client_Reg_Struc.Client_DS   ; DS:SI ^ convert from statusfield in\r\n    SHL ECX,4                   ; usual REAL-Mode-Format\r\n    MOVZX ESI,SI\r\n    ADD ESI,ECX\r\nrestore_page_map_int::              ; <--- internal: ESI=save area ( ax=5B01h )\r\n    @CHKSUM ESI                     ; Calculate checksum and check it\r\n    CMP [ESI],EAX\r\n    JNZ @@CHKERR\r\n    lodsd                           ; skip checksum\r\n    mov cl, [bEmsPhysPages]\r\n    JMP EMS_RestorePagesFromEsi\r\n\r\n; Checksum is incorrect !\r\n@@CHKERR:\r\n;    MOV AH,0A3H                 ; data is destroyed !\r\n    MOV AH, EMSS_SRC_ARRAY_INVALID\r\n    RET\r\n\r\n; Subf. 3: return size of the field in AL\r\n\r\n@@SUBF_3:\r\n    MOV AL, byte ptr emm59_.e59_sizcont\r\n    MOV byte ptr [ebp].Client_Reg_Struc.Client_EAX, al\r\n    MOV AH,EMSS_OK\r\n    RET\r\n\r\nEMS_GET_SET_PAGE_MAP ENDP\r\n\r\nbad_subfunc:\r\n    MOV AH,EMSS_INVALID_SUBFUNC ; Invalid subfunctioncode !\r\n    RET\r\n    align 4\r\n    \r\n; Check a given Handle (in DX) for validness.\r\n; In case the Handle is invalid the returnaddress is thrown away\r\n; and afterwards through RET returned to dispatcher.\r\n; Else ESI will point to the handle in EMSHandleTable array\r\n\r\nEMS_TEST_HANDLE PROC\r\n    CMP DX,EMS_MAX_HANDLES          ; out of area ?\r\n    JAE @@INVALID\r\n    MOVZX ESI,DL                      ; get a pointer to the handle table\r\n    SHL ESI,2                       ; size is 4\r\n    ADD ESI, [EMSHandleTable]\r\n    test [ESI].EMSHD.ehd_bFlags, EMSH_USED\r\n    JZ @@INVALID\r\n    RET\r\n@@INVALID:\r\n    ADD ESP,4                       ; throw away call(ing)-address\r\n    MOV AH,EMSS_INVALID_HANDLE      ; \"Handle invalid\"\r\n    RET\r\n    align 4\r\nEMS_TEST_HANDLE ENDP\r\n\r\n"
  },
  {
    "path": "src/EMU.ASM",
    "content": "\r\n;--- privileged opcode emulation\r\n;--- copyright Tom Ehlert\r\n\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n?EMUCLTS equ 0\t; 1=emulate CLTS ( resets TS bit in CR0 )\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n.text$01 SEGMENT\r\n\r\nRunEmuInstr:\r\n    DB 0Fh\r\nEmuInstr DB 90h,90h  ; run self-modifying code here\r\n    ret\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n@v86popregX macro\r\n    mov esp,ebp\r\n    POPAD\r\n    endm\r\n\r\n;--- emulate some privileged 0Fh opcodes (not HLT)\r\n;--- EBP -> client struct\r\n;--- ESP = dwStackCurr\r\n;--- ESI -> linear address of v86 CS:IP\r\n;--- AL = 0Fh\r\n;--- this is not called, but jumped to by V86_Exception.\r\n\r\nExtendedOp proc public\r\n    mov al,[esi+1]\r\nif ?EMUCLTS\r\n    cmp al,6\r\n    je @@clts\r\nendif\r\n    cmp al,9\r\n    je @@wbinvd\r\n    cmp al,8\r\n    je @@invd\r\n    cmp al,30h\r\n    je @@wrmsr\r\n    cmp al,31h\t; whether RDTSC is privileged depends on CR4, bit 2 (TSD)\r\n    je @@rdtsc\r\n    cmp al,32h\r\n    je @@rdmsr\r\n    cmp al,20h\r\n    jb V86_Exc0D ; not an opcode we emulate\r\n    cmp al,23h\r\n    ja V86_Exc0D\r\n\r\n; opcodes 0F 20 xx to 0F 23 xx emulated via self-modifying code\r\n\r\n    mov ah,[esi+2]  ; get third byte of opcode\r\n    mov WORD PTR [EmuInstr+0],ax\r\n    add [ebp].Client_Reg_Struc.Client_EIP,3 ; jump over emulated instruction\r\n    @v86popregX\r\n    call RunEmuInstr\r\n    add esp, CRS_SIZEX  ; skip error code & int#\r\n    iretd\r\nif ?EMUCLTS\r\n@@clts:\r\n    .386p\r\n    clts\r\n    .386\r\n    jmp @@invdshare\r\nendif\r\n@@invd:\r\n    .486p\r\n    invd\r\n    .386\r\n    jmp @@invdshare\r\n@@wbinvd:\r\n    .486p\r\n    wbinvd\r\n    .386\r\n@@invdshare:\r\n    @v86popregX\r\n    jmp @@twoeat\r\n\r\n@@wrmsr:\r\n    @v86popregX\r\n    .586p\r\n    wrmsr\r\n    .386\r\n    jmp @@twoeat\r\n\r\n; early pentiums and such will throw an exception on rdtsc instruction in V86\r\n;  regardless of CR4 setting, later CPU versions won't\r\n\r\n@@rdtsc:\r\n    @v86popregX\r\n    .586\r\n    rdtsc\r\n    .386\r\n    jmp @@twoeat\r\n\r\n@@rdmsr:\r\n    @v86popregX\r\n    .586p\r\n    rdmsr\r\n    .386\r\n\r\n@@twoeat:\r\n    add esp, CRS_SIZEX          ; skip error code & int#\r\n    add [esp].IRETDV86._Eip,2   ; jump over instruction\r\n    iretd\r\n\r\n    align 4\r\n\r\nExtendedOp endp\r\n\r\n.text$03 ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/EXTERN32.INC",
    "content": "\r\n;--- publics for Jemm32\r\n\r\n@seg macro name_, align_\r\nname_ SEGMENT align_ FLAT public 'CODE'\r\nendm\r\n\r\n;--- sections:\r\n;--- .text$01: shared page - accessible by VCPI clients (global vars, code )\r\n;--- .text$01g: GDT/IDT if ?SHAREDGDT/?SHAREDIDT=1\r\n;--- .text$01y: exception strings\r\n;--- .text$02: VCPI mode switch routines\r\n;--- .text$03: normal code/data\r\n;--- .text$04: init code, not copied to extended memory\r\n\r\n@seg .text$01,<DWORD>\r\nexterndef GDT_PTR:fword\r\nexterndef IDT_PTR:fword\r\nexterndef V86CR3:dword\r\nexterndef V86GDT:DESCRIPTOR\r\nexterndef dwStackCurr:dword\r\nif ?DYNTRAPP60\r\nexterndef dwTSS:dword\r\nendif\r\nexterndef dwMaxPhysMem:dword\r\nexterndef PageMapHeap:dword\r\nexterndef pmem:PAGEMEM\r\nexterndef vdsstat:DMABUFF\r\nexterndef dwRes:dword\r\nexterndef dwRSeg:dword\r\nif ?HOOK13\r\nexterndef dwRFlags:dword\r\nelse\r\nexterndef bDiskIrq:byte\r\nendif\r\nexterndef bpstart:dword\r\nexterndef wMasterPICBase:word\r\nexterndef wSlavePICBase:word\r\nexterndef bNoInvlPg:byte\r\nexterndef bV86Flags:byte\r\nexterndef DmaChn:DMAREQ\r\nexterndef bBpTab:byte\r\nexterndef bNoPool:byte\r\nexterndef OldInt06:dword\r\nexterndef OldInt19:dword\r\nexterndef OldInt67:dword\r\n\r\n;externdef pPg0PartEnd:dword\r\nexterndef pSavedVecs:dword\r\nexterndef dwV86IDT:dword\r\nexterndef dwFeatures:dword\r\nexterndef bIs486:byte\r\nexterndef bPageMask:byte\r\nexterndef bBpBack:byte\r\n\r\nexterndef EMSPageAllocationStart:dword\r\nexterndef EMSPageAllocationEnd:dword\r\nexterndef bNoEMS:byte\r\nexterndef bNoFrame:byte\r\nexterndef bNoVCPI:byte\r\nexterndef EMSPage2Segm:byte\r\nexterndef EMSPagesMax:dword\r\n\r\nexterndef bNoA20:byte\r\nexterndef XMS_Handle_Table:XMS_HANDLETABLE\r\nexterndef UMBsegments:UMBBLK\r\nif ?INTEGRATED\r\nexterndef A20Index:BYTE\r\nelse\r\nexterndef XMSCtrlHandle:WORD\r\nendif\r\nif ?KD\r\nexterndef bKD:byte\r\nendif\r\n.text$01 ENDS\r\n\r\n@seg .text$03,<DWORD>\r\n\r\n;--- Jemm32 publics\r\n\r\nexterndef bptable:BPTABLE\r\nif ?FASTMON\r\nexterndef int00:near\r\nendif\r\nexterndef V86_Monitor:near\r\nexterndef Begin_Nest_Exec:near\r\nexterndef End_Nest_Exec:near\r\nexterndef Simulate_Far_Call:near\r\nexterndef Simulate_Far_Ret:near\r\nexterndef Simulate_Iret:near\r\nexterndef Resume_Exec:near\r\nexterndef Yield:near\r\nexterndef Simulate_IO:near\r\nexterndef Simulate_IO_trap:near\r\nexterndef MoveMemoryPhys:near\r\nif ?XMS35COMPAT\r\nexterndef MoveMemoryPhysEx:near\r\nendif\r\nexterndef MapPhysPages:near\r\nexterndef MapPhysPagesEx:near\r\nexterndef MoveMemory:near\r\nexterndef EnableInts:near\r\nexterndef DisableInts:near\r\nif ?EXC10\r\nexterndef Int10_Entry:near\r\nendif\r\nexterndef Int15_Entry:near\r\nexterndef Int15_Entry_Ex:near\r\nexterndef SetVME:near\r\nexterndef V86_Exc0D:near\r\nif ?I41SUPP\r\nexterndef Int41_Entry:near\r\nendif\r\nexterndef vmm_service_table:VMM_SERV_TABLE\r\n\r\n;--- DEBUG publics\r\n\r\nexterndef VPUTCHR:near\r\n\r\n;--- EMS/VCPI publics\r\n\r\nexterndef VCPI_Call_Table:dword\r\nexterndef VCPI_V86toPM:near\r\nexterndef Int67_Entry:near\r\nexterndef Int67_V86Entry:near\r\n\r\n;--- XMS/A20 publics\r\n\r\nexterndef xms_handler:near\r\nexterndef xms_ext_realloc_emb:near\r\nexterndef A20_Set:near\r\nexterndef A20_Handle60:near\r\nexterndef A20_Handle64:near\r\nexterndef A20_Handle92:near\r\n\r\n;--- UMB publics\r\nexterndef umb_handler:near\r\n\r\n;--- VDS publics\r\n\r\nexterndef vds_handler:near\r\nexterndef VDS_Call_Table:dword\r\nexterndef VDS_Exit:near\r\n\r\n;--- Pool publics\r\n\r\nexterndef Pool_GetFree4KPages:near\r\nexterndef Pool_GetFree16KPages:near\r\nexterndef Pool_Allocate4KPage:near\r\nexterndef Pool_Allocate16KPage:near\r\nexterndef Pool_Free4KPage:near\r\nexterndef Pool_Free16KPage:near\r\nexterndef Pool_FreeAllBlocks:near\r\nexterndef Pool_AllocBlocksForEMB:near\r\nexterndef Pool_GetPhysAddr:near\r\n\r\n;--- DMA translation publics\r\n\r\nexterndef Dma_CopyBuffer:near\r\nexterndef Dma_HandleDmaPorts8:near\r\nexterndef Dma_HandleDmaPorts16:near\r\nexterndef Dma_HandlePagePorts:near\r\nif ?ALT_TRAPHDL\r\nexterndef ISA_DMA_Traphandler:near\r\nendif\r\n\r\n;--- EMU publics\r\n\r\nexterndef ExtendedOp:near\r\n\r\n;--- I15 publics\r\n\r\nexterndef I15_Simulate87:near\r\n\r\n;--- DEV publics\r\n\r\nexterndef EMMXXXX0_Strategy:near\r\nexterndef EMMXXXX0_Interrupt:near\r\n\r\n.text$03 ENDS\r\n\r\n@seg .text$04,<DWORD>\r\nexterndef HeapMalloc:near\r\nexterndef SetEMSHandleTable:near\r\nexterndef SetEMSStateTable:near\r\nexterndef XMS_Init:near\r\nexterndef Debug_Init:near\r\nexterndef EMS_Init1:near\r\nexterndef EMS_Init2:near\r\nexterndef VDS_Init:near\r\nexterndef UMB_Init:near\r\nexterndef Pool_Init1:near\r\nexterndef Pool_Init2:near\r\nexterndef InitMonitor:near\r\nexterndef IsUMBMemory:near\r\n.text$04 ends\r\n\r\n"
  },
  {
    "path": "src/I15.ASM",
    "content": "\r\n;--- I15, ah=87 extended memory move \r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n;--- expected GDT structure pointed to by ES:SI for int 15h, ah=87h\r\n\r\nI15MOVE struct\r\n    dq ?\r\n    dq ?\r\nsrc DESCRIPTOR <?>\r\ndst DESCRIPTOR <?>\r\nI15MOVE ends\r\n\r\n.text$01 SEGMENT\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n;************************************************************\r\n; simulate INT15/87\r\n;\r\n;INT 15 - SYSTEM - COPY EXTENDED MEMORY (by RBIL)\r\n;        AH = 87h\r\n;        CX = number of words to copy (max 8000h)\r\n;        ES:SI -> GDT (see I15MOVE)\r\n;Return: CF set on error\r\n;        CF clear if successful\r\n;        AH = status \r\n;\r\n;Values for extended-memory copy status (RBIL):\r\n; 00h    source copied into destination\r\n; 01h    parity error\r\n; 02h    interrupt error\r\n; 03h    address line 20 gating failed\r\n; 80h    invalid command (PC,PCjr)\r\n; 86h    unsupported function (XT,PS30)\r\n;************************************************************\r\n\r\nI15_Simulate87 proc public\r\n\r\n    call Simulate_Iret\r\n    movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_ECX\r\n\r\n    cld\r\n\r\n    MOVZX edi,WORD PTR [ebp].Client_Reg_Struc.Client_ES   ; make edi = linear address of command\r\n    MOVZX esi,WORD PTR [ebp].Client_Reg_Struc.Client_ESI\r\n    SHL edi,4\r\n    add esi,edi\r\n\r\n;-- MS Emm386 returns with error AH=2 if CX > 8000h!\r\n\r\n    cmp cx, 8000h\r\n    ja @@error02\r\n    or ecx,ecx\r\n    je @@ok            ; nothing to do - MS Emm386 returns with error AH=2 if CX == 0!\r\n    shl ecx,1          ; convert words to bytes\r\n\r\n;--- verify that src and dst descriptors are ok.\r\n;--- we don't care about segment access rights\r\n\r\n    lea eax,[ecx-1]\r\n    cmp ax, [esi].I15MOVE.src.wLimit; 16-bit overflow not an issue (0->ffff)\r\n;    ja @@error80\r\n    ja @@error02                    ; v5.87: error 2 is MS EMM386 compatible\r\n    cmp ax, [esi].I15MOVE.dst.wLimit\r\n;    ja @@error80\r\n    ja @@error02                    ; v5.87: error 2 is MS EMM386 compatible\r\n\r\n    mov al,[esi].I15MOVE.src.bA1623\r\n    mov ah,[esi].I15MOVE.src.bA2431 ; get linear source address\r\n    mov dl,[esi].I15MOVE.dst.bA1623\r\n    mov dh,[esi].I15MOVE.dst.bA2431 ; get linear destination address\r\n    shl eax,16\r\n    shl edx,16\r\n    mov ax,[esi].I15MOVE.src.wA0015\r\n    mov dx,[esi].I15MOVE.dst.wA0015\r\n    mov esi,eax\r\n    mov edi,edx\r\n\r\n;--- here we have: esi=src, edi=dst, ecx=size\r\n\r\n    @dprintf ?I15DBG, <\"Int 15h, ah=87h, src=%X, dst=%X, siz=%X\",10>, esi, edi, ecx\r\n\r\n;-- NOCHECK -> moves for addresses not backuped with RAM/ROM will fail\r\n;-- (cause an exception)\r\n\r\n    test [bV86Flags], V86F_NOCHECK\r\n    je @@memcheck\r\n\r\n    lea eax, [esi+ecx]\r\n    lea edx, [edi+ecx]\r\n    cmp eax, [dwMaxPhysMem]\r\n    jae @@fail\r\n    cmp edx, [dwMaxPhysMem]\r\n    jae @@fail\r\n\r\n@@memcheck:\r\n    call MoveMemoryPhys\r\n@@ok:\r\n    mov AH,0    ; everything OK and finished\r\n    and [ebp].Client_Reg_Struc.Client_EFlags, not 1 ;CF=0\r\n@@i1587_exit:\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX+1, ah\r\n    ret\r\n@@error02:\r\nif ?XMS35COMPAT\r\n    cmp cx,0F00Fh\r\n    jnz @F\r\n    cmp cx,word ptr [ebp].Client_Reg_Struc.Client_EAX+2\r\n    jnz @F\r\n    bt [dwFeatures],17  ;PSE-36 supported?\r\n    jnc @F\r\n    mov cx,word ptr [ebp].Client_Reg_Struc.Client_ECX+2\r\n    shl ecx,1\r\n    mov al,[esi].I15MOVE.src.bA1623\r\n    mov ah,[esi].I15MOVE.src.bA2431 ; get linear source address\r\n    mov dl,[esi].I15MOVE.dst.bA1623\r\n    mov dh,[esi].I15MOVE.dst.bA2431 ; get linear destination address\r\n    shl eax,16\r\n    shl edx,16\r\n    mov ax,[esi].I15MOVE.src.wA0015\r\n    mov dx,[esi].I15MOVE.dst.wA0015\r\n    mov esi,eax\r\n    mov edi,edx\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EDX   ;src bits 32-47\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EBX   ;dst bits 32-47\r\n    @dprintf ?I15DBG, <\"Int 15h, ah=87h, src=%X.%X, dst=%X.%X siz=%X\",10>, ax, esi, dx, edi, cx\r\n    call MoveMemoryPhysEx\r\n    jmp @@ok\r\n@@:\r\nendif\r\n    mov ah,02h\r\n    or [ebp].Client_Reg_Struc.Client_EFlags, 1 ;CF=1\r\n    jmp @@i1587_exit\r\nif 0 ; v5.87: error 80h should never be returned\r\n@@error80:\r\n    mov ah,80h\r\n    or [ebp].Client_Reg_Struc.Client_EFlags, 1 ;CF=1\r\n    jmp @@i1587_exit\r\nendif\r\n@@fail:\r\n    jmp V86_Exc0D\r\n\r\n    align 4\r\n\r\nI15_Simulate87 endp\r\n\r\n.text$03 ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/INIT.ASM",
    "content": "\r\n;--- Jemm's initialization part\r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\nif ?KD\r\n    include debugsys.inc\r\nendif\r\n\r\n;--- assembly time constants\r\n\r\n?SYSPDE     EQU ?SYSBASE shr 20    ; offset of PDE for system space in pagedir\r\n\r\n?PAT        equ 0       ; std=0, 1=use PAT to change WT to WC\r\n?DMABELOW16M equ 0      ; 1=set DMA buffer size to 0 if physical address of buffer is beyond 16M\r\n\r\nif ?FASTMON\r\n?INTTABSIZ  equ 0E0h * 7\r\nelse\r\n?INTTABSIZ  equ 100h * 7\r\nendif\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n.text$01 segment\r\nexterndef pSmallHeap:dword\r\nexterndef dwHeapSize:dword\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\nRestoreEDI proc\r\n\r\n    @dprintf ?INITDBG, <\"small heap update ptr=%X\",10>, edi\r\n    mov [pSmallHeap],edi\r\n    pop edi\r\n    ret\r\nRestoreEDI endp\r\n\r\n.text$03 ends\r\n\r\n@seg .text$03z,<PARA>\r\n\r\n    align 4\r\nV86_ENDRES proc public  ;declare a public label so the size is seen in .MAP\r\nV86_ENDRES endp\r\n\r\n.text$03z ends\r\n\r\n.text$04 segment\r\n\r\n;--- here start protected mode initialization code\r\n;--- which is *not* copied to extended memory\r\n\r\n;--- IO permission bitmap init values\r\n\r\nIOBM label byte\r\n\r\n; I/O-control of the DMA-port\r\n; * trap ports 0..7, A, B, C, (D, E, F)\r\n; * trap ports 81,82,83,87, 89,8A,8B\r\n; * trap ports c4..cf\r\n; * trap port d4 d6 d8 (da, dc, de)\r\n\r\nif ?DMAPT or ?A20PORTS\r\n if ?DMAPT\r\n  if ?MMASK\r\n;----- FEDCBA9876543210\r\n    DW 1111110011111111b    ; DMA-Controller #1 (00-0F)\r\n  else\r\n;----- FEDCBA9876543210\r\n    DW 0001110011111111b    ; DMA-Controller #1 (00-0F)\r\n  endif\r\n else\r\n    DW 0                    ; ports 00-0F\r\n endif\r\n    DW 0,0,0,0,0            ; ports 10-5F\r\n if ?A20PORTS    \r\n;----- FEDCBA9876543210\r\n  if ?DYNTRAPP60\r\n    DW 0000000000010000b    ; ports 60-6F\r\n  else\r\n    DW 0000000000010001b    ; ports 60-6F\r\n  endif\r\n else\r\n    DW 0                    ; ports 60-6F\r\n endif\r\n    DW 0                    ; ports 70-7F\r\n if ?DMAPT\r\n;----- FEDCBA9876543210\r\n    DW 0000111010001110b    ; page register (80-8F)\r\n else\r\n    DW 0                    ; ports 80-8F\r\n endif\r\n if ?A20PORTS\r\n;----- FEDCBA9876543210\r\n    DW 0000000000000100b    ; ports 90-9F\r\n else\r\n    DW 0                    ; ports 90-9F\r\n endif\r\n    DW 0,0                  ; ports A0-BF\r\n if ?DMAPT\r\n;----- FEDCBA9876543210\r\n    DW 1111111111110000b    ; DMA-Controller #2 (C0-CF)\r\n  if ?MMASK\r\n;----- FEDCBA9876543210\r\n    DW 0101010101010000b    ; DMA-Controller #2 (D8-DF)\r\n  else\r\n;----- FEDCBA9876543210\r\n    DW 0000000101010000b    ; DMA-Controller #2 (D8-DF)\r\n  endif\r\n else\r\n    DW 0,0                  ; ports C0-DF\r\n endif\r\nendif\r\n\r\nIOBM_COPY_LEN equ $ - IOBM\r\n\r\n;--- alloc small memory portions from the small heap (if there is one)\r\n;--- only valid during init phase\r\n;--- ecx=requested size in bytes\r\n;--- out: edi=pointer to memory\r\n\r\nHeapMalloc proc public\r\n    cmp [dwHeapSize],ecx\r\n    jb @@nomem\r\n    sub [dwHeapSize],ecx\r\n    @dprintf ?INITDBG, <\"small heap used, size=%X, ptr=%X\",10>, ecx, [pSmallHeap]\r\n    xchg edi,[esp]\r\n    push RestoreEDI\r\n    push edi\r\n    mov edi,[pSmallHeap]\r\n@@nomem:\r\n    ret\r\nHeapMalloc endp\r\n\r\n;--- check cpu type, return features if CPUID is supported\r\n\r\nIs486 proc\r\n    pushfd                      ; save EFlags\r\n    xor edx,edx\r\n    push 240000h                ; set AC+ID bits in eflags\r\n    popfd\r\n    pushfd\r\n    pop eax\r\n    popfd                       ; restore EFlags\r\n    shr eax, 16\r\n    test al,04                  ; AC bit set? then it is a 486+\r\n    mov ah,0\r\n    je @@no_486\r\n    inc ah\r\n    test al,20h                 ; CPUID supported?\r\n    jz @@nocpuid\r\n    xor eax,eax\r\n    inc eax                     ; get register 1\r\n    .586\r\n    cpuid\r\n    .386\r\n    mov ah,1\r\n@@nocpuid:\r\n@@no_486:\r\n    mov al,ah\r\n    ret\r\nIs486 endp\r\n\r\nif ?FASTBOOT\r\n\r\n;--- save IVT vectors\r\n;--- inp: EDI -> free memory\r\n\r\nSaveIVT proc\r\n    test [bV86Flags],V86F_FASTBOOT    ;additionally 32*4+32*4+16*4=320\r\n    jz  @@nofastboot\r\n    mov ecx, 32*4+32*4+16*4\r\n    call HeapMalloc\r\n    mov [pSavedVecs],edi \r\n    xor esi,esi\r\n    mov ecx,32\r\n    rep movsd      ;save vecs 00-1F\r\n    add esi,32*4   ;skip 20-3F\r\n    mov cl,32\r\n    rep movsd      ;save vecs 40-5F\r\n    add esi,8*4    ;skip 60-67\r\n    mov cl,16\r\n    rep movsd      ;save vecs 68-77h\r\nif ?RBTDBG\r\n    mov esi,[pSavedVecs]\r\n    mov ecx,20h\r\n    xor ebx,ebx\r\nnextitem: \r\n    lodsd\r\n    @dprintf ?RBTDBG, <\"%X: %08X        \">, bl, eax\r\n    test cl,1\r\n    jz @F\r\n    @dprintf ?RBTDBG, <10>\r\n@@:\r\n    inc ebx\r\n    loop nextitem\r\nendif\r\n@@nofastboot:\r\n    ret\r\nSaveIVT endp\r\nendif\r\n\r\nif ?SHAREDIDT\r\nexterndef V86IDT:GATE\r\nendif\r\n\r\n;--- set int EAX to value of ECX\r\n;--- used for Ints 41h, 67h, 15h, 06h, 10h\r\n;--- called during Init().\r\n\r\nSetIDTEntry proc\r\nif ?SHAREDIDT\r\n    mov word ptr [eax*8+V86IDT].wOfsLo, cx\r\n    shr ecx,16\r\n    mov word ptr [eax*8+V86IDT].wOfsHi, cx\r\nelse\r\n    push ebx\r\n    mov ebx,[dwV86IDT]\r\n    mov word ptr [ebx+eax*8].GATE.wOfsLo, cx\r\n    shr ecx,16\r\n    mov word ptr [ebx+eax*8].GATE.wOfsHi, cx\r\n    pop ebx\r\nendif\r\n    ret\r\nSetIDTEntry endp\r\n\r\n;--- Monitor initialization\r\n\r\n;--- input expected:\r\n;--- CPU in protected-mode, interrupts disabled, paging disabled\r\n;--- CS,SS,DS,ES: FLAT\r\n;--- FS,GS: NULL  \r\n;--- EBP -> IRETDV86 ( contains vCS=RSEG )\r\n;--- EBX -> BRPTAB\r\n;--- ESI : 32-bit code start, flat address\r\n;--- EDI -> JEMMINIT, contains:\r\n;---  MonitorStart      : start of EMB for the monitor\r\n;---  MonitorEnd        : end of EMB for monitor\r\n;---  MaxMem16k         : MAX=xxxx cmdline option given, or 1E00 (120 MB)\r\n;---  MaxPhysMem        : highest physical address + 1\r\n;---  XMS_Handle_Table  : returned by int 2f, ax=4309h\r\n;---  MaxEMSPages       : maximum EMS 16k pages, default 32 MB\r\n;---  XMS_Control_Handle: XMS handle for monitor extended memory block\r\n;---  DMA_Buffer_Size   : size of DMA buffer in kB\r\n;---  Frame             : FRAME=xxxx parameter\r\n;---  NoEMS, NoFrame, NoPool, AltBoot, NoVME, NoVDS, NoPGE, NoA20, NoVCPI\r\n\r\n;--- memory layout installed by the initialization code:\r\n;--- shared memory (110000 - 11xxxx):\r\n;---  0-3.0 kB    wasted space because of rounding to page border (Jemm386 only)\r\n;---    0.125 kB  monitor help stack for VCPI\r\n;---    0.5 kB    GDT  [GDT_PTR] (size 200h)\r\n;---  ~14.0 kB    resident monitor data+code (V86_ENDRES - ?BASE)\r\n;---    2.0 kB    IDT  [IDT_PTR]\r\n;---   ~1.7 kB    interrupt table (size E1h*7)\r\n;---  0-3.0 kB    wasted space because of rounding to page border\r\n;---              (if > 2 kB it is filled with STATUSTABLE)\r\n\r\n;--- linear addresses in sys space (F80000000 - F803FFFFF):\r\n\r\n;---    4   kB    reserved\r\n;---   12   kB    page directory, page table 0, sys page table\r\n;---    4   kB    ring 0 stack\r\n;---   ~8.2 kB    TSS (size ?TSSLEN)\r\n;---              DMABuffStart (0-?DMABUFFMAX kB)\r\n;---              para align\r\n;---    1   kB    EMSHandleTable (size 4*255)\r\n;---    1   kB    EMSStateTable (size 8*128)\r\n;---    2   kB    EMSNameTable (size 8*255)\r\n;---              EMSPageAllocationStart (EMS pages * 4 bytes) \r\n;---              EMSPageAllocationOfs (EMS pages * 1 bytes) \r\n;---              64 byte align\r\n;---              PoolAllocationTable (128*64 + x)\r\n;---  0-3.0 kB    page align\r\n;---              start of \"free\" memory in this very first XMS block\r\n;---              (used for UMBs, if NODYN used for EMS/VCPI as well)\r\n\r\nInitMonitor PROC public\r\n\r\n;--- local variables:\r\n\r\njemmini     equ <[ebp-4]>\r\nrsofsinit   equ <[ebp-8]>\r\ncodeadr     equ <[ebp-12]>\r\ntmpPageMask equ <[ebp-15]>\r\ntmpIs486    equ <[ebp-16]>\r\ntmpFeatures equ <[ebp-20]>\r\ndwSizeV86   equ <[ebp-24]>\r\n\r\n    CLD\r\n\r\n;--- no access to global variables possible until paging is enabled\r\n;--- and memory block has been moved to extended memory!\r\n\r\n;--- dprintf uses direct addressing, doesn't work yet\r\n;    @dprintf ?INITDBG, <\"Welcome in PM\",10>\r\n\r\n    push edi                ;== jemmini [ebp-4]\r\n    push ebx                ;== rsofsinit [ebp-8]\r\n    push esi                ;== codeadr [ebp-12]\r\n\r\nif 1\r\n    pushfd                      ; clear the NT flag to avoid a \"reboot\"\r\n    and byte ptr [esp+1],not 40h; at next IRET in protected-mode\r\n    popfd\r\nendif\r\n\r\n    mov esi, jemmini\r\n\r\n    call Is486\r\n    push eax                 ;== tmpPageMask [ebp-16]\r\n    push edx                 ;== tmpFeatures [ebp-20]\r\n    and al,al\r\n    jz @F\r\n    cmp [esi].JEMMINIT.NoInvlPg,-1\r\n    jnz @F\r\n    mov [esi].JEMMINIT.NoInvlPg,0\r\n@@:\r\n\r\n    MOV EDI,[esi].JEMMINIT.MonitorStart ; Start of Monitor-Code\r\nif ?INITDBG\r\n    push edi\r\n    mov ecx,[esi].JEMMINIT.MonitorEnd\r\n    sub ecx,edi\r\n    shr ecx,2\r\n    mov eax,0DEADBABEh\r\n    rep stosd\r\n    pop edi\r\nendif\r\n\r\nife ?INTEGRATED\r\n    ADD EDI,1000h-1     ; Round to the next page border\r\n    AND DI,NOT 1000h-1  ; may waste up to 3 kB (not for JemmEx)\r\nendif\r\n\r\n;-- calc size of the items which must be in page table 0:\r\n;-- VCPI stack, GDT, code+data, IDT, INT_TABLE\r\n\r\n;-- ?INTTABSIZ is size of INT_TABLE\r\n\r\n    mov eax, offset V86_ENDRES\r\nif ?SHAREDIDT\r\n    sub eax, ?BASE - ( ?INTTABSIZ)  ;size of resident code+data\r\nelse\r\n    sub eax, ?BASE - ( ?INTTABSIZ + 800h)\r\nendif\r\n    add eax, 1000h-1\r\n    and ax, not 1000h-1 ;round to next page (may waste another 3 kB!)\r\n\r\n    mov ebx, edi        ;save physical address in EBX\r\n    add edi, eax\r\n\r\n    push eax            ;== dwSizeV86 [ebp-24], size of code+GDT+IDT\r\n\r\nif 0\r\n    mov eax, cr3\r\n    and eax, 0FFFh      ;don't touch low 12 bits of CR3\r\n    or eax, edi\r\n    MOV CR3, eax        ;set CR3 (paging is still disabled)\r\nelse\r\n    mov cr3, edi\r\nendif\r\n\r\n;-- clear pagedir, page table 0 + system page table\r\n\r\n    push edi\r\n    mov ecx, 3000h/4\r\n    xor eax, eax\r\n    rep stosd\r\n    pop edi\r\n\r\n;-- init page dir (2 PDEs)\r\n\r\n    mov ah,10h  ;=mov eax,1000h\r\n\r\n    lea edx, [edi+eax]\r\n    MOV [EDI+000h],EDX\r\n    OR DWORD PTR [EDI+000h], PTF_PRESENT or PTF_RW or PTF_USER\r\n    add edx, eax\r\n\r\n    push edx         ;save mapped system page table\r\n\r\n    OR edx, PTF_PRESENT or PTF_RW or PTF_USER\r\n    mov [EDI+?SYSPDE], EDX\r\n\r\n    add edi, eax     ;edi -> mapped page table 0\r\n\r\n;-- init page table 0 address space 0-110000h\r\n\r\n    movzx edx, dl  ;just flags u/ser, w/riteable, p/resent\r\nif ?PGE\r\n    test byte ptr tmpFeatures+1,CF_PGE shr 8  ;PGE supported?\r\n    jz @F\r\n    or dh,PTF_GBL shr 8              ;set G bit (page global); active only if bit set in CR4\r\n@@:\r\n    mov tmpPageMask,dh\r\nendif\r\n    mov cx,110h     ;hiword ecx is cleared\r\n    mov eax,edx\r\n@@:\r\n    stosd\r\n    ADD EAX,1000h\r\n    loop @B\r\n\r\nif 0\r\n;-- give the video region A000-BFFF some special attributes\r\n    push edi\r\n    sub edi, (110h*4 - 0A0h*4)\r\n    mov cl,20h\r\n@@:\r\n    or dword ptr [edi],8h  ;set \"WT\"\r\n    add edi,4\r\n    loop @B\r\n    pop edi\r\nendif\r\n\r\n;-- init page table 0 address space 110000h-?\r\n\r\n    mov ecx, dwSizeV86  ;size of space above 110000h (page aligned)\r\n    shr ecx, 12         ;is just a handful of pages\r\n    mov eax, ebx        ;get physical address of this space\r\n    or al, PTF_PRESENT or PTF_RW  ;set PRESENT + R/W\r\nif ?PGE\r\n;--- set first page (which is shared) global\r\n    or ah, tmpPageMask\r\nendif\r\n@@:\r\n    stosd\r\n    ADD EAX,1000h\r\nif 0\r\n    and al, not PTF_USER  ;all pages except the first are \"system\"\r\nendif\r\nif ?PGE\r\n    and ah, 0F0h\r\nendif\r\n    loop @B\r\n\r\n;-- v5.86, option SB: identity map remaining PTEs in page tab 0.\r\n\r\n\ttest [esi].JEMMINIT.V86Flags, V86F_SB\r\n\tjz nomapping\r\n    push eax\r\nif 1\r\n    movzx eax, di\r\n    and ah, 0Fh\r\n    shl eax, 10     ;transform offset in page table -> linear address\r\n    or al, PTF_PRESENT or PTF_RW or PTF_USER\r\nendif\r\n@@:\r\n    stosd\r\n    ADD EAX,1000h\r\n    test di,0FFFh\r\n    jnz @B\r\n\r\n;--- identity map extended memory 400000h-7fffffffh?\r\n;--- with PSE, there's no big loss, since just the page dir\r\n;--- has to be adjusted. However, possible intruders most\r\n;--- likely won't understand 4MB PDEs.\r\n;--- if activated: add check if CPU supports PSE!\r\nif 0\r\n;    sub edi, 2000h-4\t; =pagedir+4\r\n\tmov edi, ?PAGEDIR+4\r\n    mov ecx, 1FFh\r\n;--- bit 7=1: PSE 4MB page\r\n;--- bits 13-21: 0\r\n;--- bits 22-31: addressbits 22-31\r\n    mov eax, 400000h or PTF_4MB or PTF_PRESENT or PTF_RW or PTF_USER\r\n@@:\r\n    stosd\r\n    add eax, 400000h\r\n    loop @B\r\nendif\r\n    pop eax\r\nnomapping:\r\n\r\n;-- init system page table with the rest.\r\n;-- in a first step just 4 PTEs are needed (to map page tables and stack)\r\n\r\n    pop edi         ;restore saved mapped system page table (=linear address)\r\n\r\n    add edi, 4      ;skip first page ( remains uncommitted )    \r\n\r\n    mov ecx,3+1     ;3 page tables + 1 page for stack\r\n@@:\r\n    stosd\r\n    ADD EAX,1000h\r\n    loop @B\r\n\r\n    push eax         ;save physical address free memory\r\n\r\n;--- page dir, page tab 000 and sys page tab are now initialized,\r\n;--- paging can be enabled.\r\n\r\n    MOV EAX,CR0\r\n    OR EAX,80000000H       ; set PE bit\r\n    MOV CR0,EAX\r\n\r\n;--- dprintf uses direct addressing, cannot work yet\r\n;    @dprintf ?INITDBG, <\"Paging has been enabled\",10>\r\n\r\n;--- paging enabled, now move monitor code+data in extended memory\r\n\r\n    mov edi, ?BASE\r\n    mov esi, codeadr\r\n\r\nif 0\r\n;--- resolve base fixups\r\n;--- this is usually not needed, since the binary has been linked\r\n;--- for base address 110000h\r\n\r\n    pushad\r\n    add esi, V86_TOTAL  ;the relocs are just behind the 32bit block\r\n    xor edx, edx\r\n    xchg dx,[esi]        ;get size of header (hack!)\r\nnextpage:\r\n    mov ebx, [esi+0]\r\n    mov ecx, [esi+4]\r\n    and ecx, ecx\r\n    jz reloc_done\r\n;    @dprintf ?INITDBG, <\"rlcs at %X for page %X size=%X edx=%X\",10>, esi, ebx, ecx, edx\r\n    add ecx, esi\r\n    add esi, 8\r\n    sub ebx, edx        ;subtract size of header from RVA\r\n    add ebx, [esp]      ;add conv. base to address\r\n    xor eax, eax\r\nnextreloc:\r\n    lodsw\r\n    test ah,0F0h\r\n    jz @F\r\n    and ah,0Fh\r\n    add [eax+ebx], edi\r\n@@:\r\n    cmp esi, ecx\r\n    jb nextreloc\r\n    jmp nextpage\r\nreloc_done:\r\n    popad\r\n;    @dprintf ?INITDBG, <\"base relocs done\",10>\r\nendif\r\n\r\n;-- copy all to extended memory (linear address 110000h)\r\n\r\n    MOV ECX, offset V86_ENDRES\r\n    sub ecx, edi\r\n    shr ecx, 2\r\n    rep movsd\r\n\r\n;--- after code + data has been moved to extended memory\r\n;--- direct addressing is ok, so access to global variables works now\r\n\r\n    @dprintf ?INITDBG, <\"monitor code+data moved to extended memory\",10>\r\n\r\n;--- load final values for GDTR\r\n\r\n    .386p\r\n    LGDT [GDT_PTR]\r\n    .386\r\n\r\n    @dprintf ?INITDBG, <\"GDTR set, edi=%X\",10>, edi\r\n\r\n;-- create INT_TABLE + set IDT\r\n;-- some VCPI intruders won't work if IDT is not in page table 0 !!!\r\n\r\n    mov ebx, 0EE00h shl 16 + FLAT_CODE_SEL\r\n    mov ecx, 100h\r\nif ?SHAREDIDT\r\n    mov esi, offset V86IDT\r\nelse\r\n    mov esi, edi\r\n    mov [dwV86IDT], esi\r\n    add edi, 100h*8\r\nendif\r\n\r\n;--- esi -> IDT\r\n;--- edi -> code int x entries (x=0..ff)\r\n\r\nif ?FASTMON\r\n    mov eax, offset int00\r\nnextfastidtentry:\r\n;   @dprintf ?INITDBG, <\".\">\r\n    shld edx, eax, 16\r\n    mov [esi+0],ax\r\n    mov [esi+2],ebx\r\n    mov [esi+6],dx\r\n    add eax, 4\r\n    add esi, 8\t;next IDT entry\r\n    dec ecx\r\n    cmp cl,100h - ?FASTENTRIES\r\n    jnz nextfastidtentry\r\n;   @dprintf ?INITDBG, <10>\r\n    mov edx, 0E9006Ah or (?FASTENTRIES shl 8) ;push byte ?FASTENTRIES, jmp xxxx\r\nelse\r\n    mov edx, 0E9006Ah       ;push byte 00h, jmp xxxx\r\nendif\r\n@@nextidtentry:\r\n;   @dprintf ?INITDBG, <\".\">\r\n    mov eax, edi\r\n    mov [esi+0],ax\r\n    mov [esi+2],ebx\r\n    shr eax, 16\r\n    mov [esi+6],ax\r\n    mov [edi+0],edx\r\n    mov eax, offset V86_Monitor\r\n    add edi,7       ;7 bytes for each entry in INT_TABLE!\r\n    sub eax, edi\r\n    mov [edi-4],eax\r\n    inc dh          ;next INT #\r\n    add esi,8\r\n    loop @@nextidtentry\r\n;   @dprintf ?INITDBG, <10>\r\n\r\n    .386p\r\n    LIDT [IDT_PTR]\r\n    .386\r\n    @dprintf ?INITDBG, <\"IDTR, IDT + int table initialized, IDT=%X edi=%X\",10>, dword ptr [IDT_PTR+2], edi\r\n\r\nif ?I41SUPP\r\n;--- set int 41h to an iretd\r\n;--- no need to route this int to v86-mode - it's not executable code.\r\n;--- this must be done BEFORE the callout to the kernel debugger (see below).\r\n    mov al, 41h\r\n    movzx eax, al\r\n    mov ecx, offset Int41_Entry\r\n    @dprintf ?INITDBG, <\"init: int 41h vector set to %X\",10>, ecx\r\n    call SetIDTEntry\r\nendif\r\n\r\nif ?KD\r\n    mov esi, jemmini\r\n    cmp [esi].JEMMINIT.kdseg,0\r\n    jz @F\r\n    @dprintf ?INITDBG, <\"init: kernel debugger init call\",10>\r\n    push edi\r\n    mov al, PMINIT_INIT_IDT\r\n if ?SHAREDIDT\r\n    mov edi, offset V86IDT\r\n else\r\n    mov edi, [dwV86IDT]\r\n endif\r\n    call fword ptr [esi].JEMMINIT.kdofs\r\n    pop edi\r\n    mov ax, 004Fh\r\n    int 41h\r\n    cmp ax, 0F386h\r\n    setz [bKD]\r\n    @dprintf ?INITDBG, <\"init: kernel debugger response to int 41h: %X\",10>, eax\r\n@@:\r\nendif\r\n\r\nif 0\r\n;--- switch to the stack in extended memory\r\n    mov ebp, ?TOS - size IRETDV86\r\n    lea esp, [ebp-24]   ;take care of the local variables\r\n    @dprintf ?INITDBG, <\"init: ESP reset\",10>\r\nendif\r\n\r\n;--- edi para aligned again (increased by 700h/690h)\r\n;--- EDI -> free linear memory\r\n;--- EBX -> free space for PTEs in system page table\r\n\r\n    MOV EAX,CR3\r\n    MOV [V86CR3],eax\r\n\r\n    mov eax, tmpFeatures\r\n    mov [dwFeatures], eax\r\n    mov eax, tmpIs486\r\n    mov [bIs486], al\r\n\r\nif ?PGE\r\n    mov [bPageMask],ah\r\nendif\r\n\r\n    MOV ECX,[ebp].IRETDV86._Cs\r\n    MOV [dwRSeg], ecx\r\n    SHL ECX,4\r\n    mov [dwRes],ecx\r\n\r\n    mov esi, jemmini\r\n\r\n    mov eax, [esi].JEMMINIT.MaxPhysMem\r\n    mov [dwMaxPhysMem],eax\r\n    mov eax, [esi].JEMMINIT.MaxMem16k\r\n    shl eax, 2\r\n    mov [pmem.dwMaxMem4K],eax\r\n    mov al, [esi].JEMMINIT.NoPool\r\n    mov [bNoPool],al\r\n\r\n    mov al, [esi].JEMMINIT.NoInvlPg\r\n    mov [bNoInvlPg],al\r\n    mov al, [esi].JEMMINIT.V86Flags\r\n    mov [bV86Flags],al\r\n\r\n    push ecx\r\n\r\n    call XMS_Init\t; won't use edi/ebx\r\n\r\nif ?DBGOUT\r\n;--- this call must be done AFTER callout to kernel debugger\r\n    call Debug_Init\r\nendif\r\n\r\n    call EMS_Init1\t; may modify ebx!\r\n\r\n    pop ecx\r\n\r\n    mov esi, rsofsinit\r\n\r\n    movzx eax,[esi].RSOFS.wBpTab\r\n    mov [bBpTab],al\r\n    add [bBpBack],al\r\n    add eax,ecx\r\n    mov [bpstart],eax\r\n\r\nif ?HOOK13\r\n    movzx eax,[esi].RSOFS.wRFlags\r\n    add eax,ecx\r\n    mov [dwRFlags],eax\r\nendif\r\n    @dprintf ?INITDBG, <\"variables copied, edi=%X\",10>, edi\r\n\r\n;--- use any rest as \"small heap\"\r\n;--- it can be used by EMS - if it is large enough\r\n\r\n    lea ecx,[edi+1000h-1]\r\n    and cx,0F000h\r\n;    mov [pPg0PartEnd],ecx   ;v5.86: store end of monitor in page tab 0\r\n    sub ecx, edi\r\n    mov [pSmallHeap], edi\r\n    mov [dwHeapSize], ecx\r\n\r\n;--- the memory in page tab 0 is initialized\r\n\r\n;--- until now are consumed:\r\n;--- 3 pages for pagedir+pagetab\r\n;--- 4-5 pages for monitor (hlp stack,) GDT, data, code, IDT\r\n\r\n    pop ebx ; get free phys mem ptr\r\n\r\n    @dprintf ?INITDBG, <\"page table 0 initialised, EDI=%X EBX=%X\",10>, edi, ebx\r\n\r\nif 1\r\n\r\n;--- set bits in VDS DMABuffFree bit array\r\n\r\n    mov esi, jemmini\r\n    movzx eax, [esi].JEMMINIT.DMABufferSize ;buffer size in kB\r\n    shl eax, 10\r\n    mov ecx, eax\r\n    shr ecx, 10\r\n    inc ecx     ;add start and end bit\r\n    inc ecx\r\n    xor edx, edx\r\n@@:\r\n    bts [vdsstat.DMABuffFree], edx\r\n    inc edx\r\n    loop @B\r\n\r\n;-- ensure DMA buffer doesn't cross a 64kb border\r\n\r\n    mov ecx, ebx\r\n    and cx,0F000h\r\n    lea edx, [ecx+eax-1]    ;edx -> last byte of buffer\r\n\r\n if ?DMABELOW16M\r\n    cmp edx, 1000000h       ;DMA must be below 16M border\r\n    jc @F\r\n    xor eax, eax\r\n@@:\r\n endif\r\n    mov [vdsstat.DMABuffSize], eax\r\n    and eax, eax\r\n    jz @@buffernull\r\n\r\n    mov eax, edx\r\n    shr eax, 16\r\n    mov edi, ecx\r\n    shr edi, 16\r\n    sub eax, edi    ;does buffer cross a 64 kB boundary?\r\n    jz @@buffergood\r\n\r\n;-- align it to the next 64 kB boundary\r\n\r\n    inc edi\r\n    shl edi,16\r\n    mov eax,edi\r\n    sub eax,ecx\r\n    add ecx,eax     ;in eax now amount of free mem below DMA buffer\r\n@@buffergood:\r\n\r\n;--- map DMA buffer in linear address space\r\n\r\n    pushad\r\n    mov eax, ecx\r\n    mov ecx, [vdsstat.DMABuffSize]  ; will be page aligned\r\n    shr ecx, 12\r\n    call MapPhysPagesEx\r\n    mov [vdsstat.DMABuffStart], eax\r\n    add edx,4\r\n    mov [PageMapHeap], edx\r\n\r\n if ?INITDBG\r\n    mov edi, eax\r\n    mov ecx, [vdsstat.DMABuffSize]\r\n    shr ecx, 2\r\n    mov eax, \"BAMD\"\r\n    rep stosd\r\n endif\r\n\r\n    popad\r\n\r\n@@buffernull:\r\n    MOV [vdsstat.DMABuffStartPhys], ecx\r\n\r\n    @dprintf ?INITDBG, <\"DMA buffer linear=%X physical=%X size=%X phys rest=%X\",10>, [vdsstat.DMABuffStart], [vdsstat.DMABuffStartPhys], [vdsstat.DMABuffSize], eax\r\n\r\nendif\r\n\r\n;--- dma buffer initialized\r\n;--- fill ring 0 stack with \"STAK\"\r\n\r\n    push eax     ;rest of physical mem below DMA buffer\r\n\r\n    mov edi, ?SYSBASE + 3000h + 1000h ; skip free page, page dir + 2 page tables\r\n    mov eax,\"KATS\"\r\n    mov ecx, 1000h/4\r\n    rep stosd\r\n\r\n    @dprintf ?INITDBG, <\"stack initialized\",10>\r\n\r\n;-- now create a heap\r\n;-- set rest of PTEs in system page table\r\n\r\n;-- what amount of space is needed ?\r\n;--   tss:   104 + 32 + 8192 + 8  ->   8336\r\n;--  pool:  maxmem16k * 64 / 96   ->   5120 (for 120 MB/30720 4k pages)\r\n;--  pool:  xms handles * 64      ->   2048 (for 32 XMS handles)\r\n;--   ems:  handles (255*4)       ->   1020 (for 255 handles)\r\n;--   ems:  state save (64*16)    ->   1024 (for 64 states)\r\n;--   ems:  name array (255*8)    ->   2040 (for 255 names)\r\n;--   ems:  page array (2048*5)   ->  10240 (for 2048 pages)\r\n;----------------------------------------------------------------\r\n;                                     29828\r\n\r\n    push ebx\r\n    mov ecx, ?TSSLEN    ;8336\r\n\r\n;--- calc number of pages still needed in sys page table\r\n;--- 1. the pages for pool management:\r\n;--- maxmem16k * 64 / 96\r\n\r\n    mov eax, [esi].JEMMINIT.MaxMem16k\r\n    shl eax, 6      ;*64\r\n    xor edx, edx\r\n    mov ebx, 96\r\n    div ebx\r\n    add eax, 64*2\r\n    add ecx, eax\r\n\r\n    cmp [bNoPool],0\r\n    jnz @@isnopool\r\n    movzx eax,[XMS_Handle_Table.xht_numhandles]\r\n    shl eax, 6\r\n    add ecx, eax\r\n@@isnopool:    \r\n    @dprintf ?INITDBG, <\"for TSS+pool=%X\">, ecx\r\n\r\n;--- 2. the pages required for ems handling\r\n\r\n;--- a. var space (5 * maxEMSpages)\r\n\r\n    movzx eax, [esi].JEMMINIT.MaxEMSPages\r\n    lea eax, [eax+eax*4]    ;each EMS page needs 5 bytes\r\n    \r\n;--- b. fix space (EMS handle table, state table, name table)\r\n\r\n    add eax, EMS_MAX_HANDLES*size EMSHD + EMS_MAXSTATE*size EMSSTAT + EMS_MAX_HANDLES*8\r\n\r\n    @dprintf ?INITDBG, <\", for EMS=%X\">, eax\r\n\r\n    add ecx, eax\r\n\r\n    @dprintf ?INITDBG, <\", total=%X\">, ecx\r\n\r\n;--- 3. round up to 4k\r\n\r\n    add ecx, 4096-1\r\n\r\n;--- 4. convert to 4k page\r\n\r\n    shr ecx, 12\r\n\r\n    pop ebx\r\n    pop eax\r\n\r\n    @dprintf ?INITDBG, <\", remaining bytes=%X\",10>, eax\r\n\r\n    @LinearFromSysPTE edi, [PageMapHeap]\r\n\r\n;--- here:\r\n;--- eax = amount of space below DMA buffer (in bytes)\r\n;--- ebx = physical address of space below DMA buffer\r\n;--- edi = linear address free memory\r\n;--- ecx = amount of memory needed (in pages)\r\n\r\n    mov edx,[PageMapHeap]\r\n    shr eax, 12\r\n    jz @@nospacebelow   ;jump if nothing left below DMA buffer\r\n    push ecx\r\n    push eax\r\n    mov ecx, eax\r\n    mov eax, ebx\r\n    call MapPhysPages\r\n    pop eax\r\n    pop ecx\r\n@@nospacebelow:\r\n    sub ecx, eax        ;space above DMA buffer needed?\r\n    jbe @@nospaceabove\r\n    mov eax, [vdsstat.DMABuffStartPhys]\r\n    add eax, [vdsstat.DMABuffSize]\r\n    @dprintf ?INITDBG, <\"heap region above DMA buffer %X, size=%X, pPTE=%X\",10>, eax, ecx, edx\r\n    call MapPhysPages\r\n@@nospaceabove:\r\n    add edx,4           ;leave 1 page in address space reserved\r\n    mov [PageMapHeap],edx\r\n\r\nif ?INITDBG\r\n    lea ebx, [edx-4]\r\n    @LinearFromSysPTE ebx, ebx\r\n    dec ebx\r\n    @dprintf ?INITDBG, <\"heap created at %X-%X, free PTEs starting at %X\",10>, edi, ebx, edx\r\nendif\r\n\r\n;--- now create the TSS (will begin on a page boundary, since DMA buffer\r\n;--- size is rounded to 4 kB).\r\n\r\nif ?DYNTRAPP60\r\n    mov [dwTSS], edi\r\nendif\r\n    mov ebx, offset V86GDT\r\n    mov eax, edi\r\n    MOV WORD PTR [EBX+V86_TSS_SEL+2],AX\r\n    SHR EAX,16\r\n    MOV BYTE PTR [EBX+V86_TSS_SEL+4],AL\r\n    MOV BYTE PTR [EBX+V86_TSS_SEL+7],AH\r\n\r\n;--- init TSS, the software interrupt redirection bitmap (256 bits) + io-bitmap\r\n;--- it is known by Pentium+ cpus only, but doesn't hurt for previous cpus\r\n\r\n    mov edx, edi    \r\n    mov ecx, size TSSSEG/4 + 32/4 + (65536/8)/4\r\n    xor eax, eax\r\n    rep stosd   ;clear TSS, io-bitmap, ...\r\n    dec eax\r\n    stosb           ;the IO bitmap must be terminated by a FF byte\r\n\r\n    mov dword ptr [edx].TSSSEG.tsEsp0, ?TOS\r\n    mov dword ptr [edx].TSSSEG.tsSS0, FLAT_DATA_SEL\r\nif 1\r\n    mov eax, [V86CR3]       ; save value for CR3 in TSS (not needed)\r\n    mov [edx].TSSSEG.tsCR3, eax\r\nendif\r\n    mov [edx].TSSSEG.tsOfs,size TSSSEG+32 ;let 32 bytes space below IO Bitmap\r\n\r\n;-- init the io permission bitmap\r\n\r\n    mov esi, codeadr\r\n;   add esi, offset IOBM - offset _start\r\n    add esi, offset IOBM - ?BASE\r\n    lea edi, [edx+size TSSSEG+32]\r\n    mov ecx,IOBM_COPY_LEN\r\n    rep movsb\r\n\r\n    @dprintf ?INITDBG, <\"TSS done, esp0=%X\",10>, [edx].TSSSEG.tsEsp0\r\n\r\n;-- finally load TR\r\n\r\n    .386p\r\n    mov ax, V86_TSS_SEL\r\n    ltr ax\r\n    .386\r\n\r\n;--- here CR3, GDTR, IDTR, TR and LDTR all have their final values\r\n\r\n    lea edi, [edx+ ?TSSLEN]\r\n\r\n;--- modify IDT and vector bitmap\r\n;--- int 67h must be handled by the monitor in any case\r\n\r\n    add edx, size TSSSEG\r\n\r\n    xor eax, eax\r\n    mov al, 67h\r\n    mov ecx, offset Int67_Entry\r\n    bts [edx], eax\r\n    call SetIDTEntry\r\n\r\n    mov al, 15h\r\n    mov ecx, offset Int15_Entry\r\n    bts [edx], eax\r\n    call SetIDTEntry\r\n\r\nif ?BPOPC ne 0F4h\r\n    mov al,6\r\n    mov ecx, offset Int06_Entry\r\n;   bts [edx], eax          ;not required since it is an exception\r\n    call SetIDTEntry\r\nendif\r\n\r\nif ?EXC10\r\n    mov al,10h\r\n    mov ecx, offset Int10_Entry\r\n    call SetIDTEntry\r\nendif\r\n\r\n    mov esi,jemmini\r\n\r\nif ?VME\r\n    mov al,[esi].JEMMINIT.NoVME\r\n    xor al,1\r\n    call SetVME\r\nendif\r\nif ?PAT\r\n    test [dwFeatures],10000h\r\n    jz @@nopat\r\n    mov ecx,277h\r\n    @rdmsr\r\n    mov ah,01   ;change 00-07-04-06 to 00-07-01-06\r\n    @wrmsr\r\n@@nopat:\r\nendif\r\n\r\nif ?A20PORTS\r\n if 0\r\n    xor eax, eax\r\n    test byte ptr wBiosA20,1 ;keyboard controller can switch A20?\r\n    jnz @@kbdA20\r\n    mov al, 60h\r\n    btr [edx], eax\r\n    mov al, 64h\r\n    btr [edx], eax\r\n@@kbdA20:\r\n    test byte ptr wBiosA20,2 ;port 92h can switch A20?\r\n    jnz @@ps2A20\r\n    mov al, 92h\r\n    btr [edx], eax\r\n@@ps2A20:\r\n endif\r\nendif\r\n\r\n    @dprintf ?INITDBG, <\"Jemm initialised, edi=%X\",10>, edi\r\n\r\nif ?FASTBOOT\r\n    call SaveIVT\r\nendif\r\n\r\n    mov esi, jemmini\r\n\r\n;--- Pool init\r\n;--- esi->jemminit, edi=linear addr free space\r\n;--- out: edi=free space\r\n\r\n    call Pool_Init1\r\n\r\n;--- EMS/VCPI init, init EMS SYSTEM handle\r\n;--- esi->jemminit, edi=linear addr free space\r\n;--- out: edi=free space\r\n\r\n    call EMS_Init2\r\n\r\n;--- set EAX = index PTE of linear address in EDI in system page table\r\n\r\n    mov eax, edi\r\n    sub eax, ?SYSBASE\r\n    shr eax, 12\r\n\r\nif 0\r\n;--- clear the PTEs which are not used in the heap\r\n    xor ecx,ecx\r\n    test di,0fffh\r\n    setnz cl\r\n    lea esi, @GetPTEAddr(?PAGETABSYS+eax*4)\r\n    lea esi,[esi+ecx*4]\r\n    xor ecx, ecx\r\n@@:\r\n    cmp ecx,[esi]\r\n    jz @F\r\n    mov [esi],ecx\r\n    add esi,4\r\n    jmp @B\r\n@@:\r\nendif\r\n\r\n;--- convert EDI back into a physical address\r\n;--- use system page table for the conversion\r\n\r\n    mov eax, @GetPTEAddr(?PAGETABSYS+eax*4)\r\n    and ax, 0F000h\r\n    and edi, 0FFFh\r\n\r\nif ?INITDBG\r\n    lea ecx, [eax+edi]\r\n    @dprintf ?INITDBG, <\"End of monitor: %X\",10>, ecx\r\nendif\r\n\r\n;--- now check if heap's last physical page is < DMA buffer\r\n;--- if yes, all pages below must be skipped and are wasted\r\n;--- since the EMS/VCPI memory managment needs a contiguous block\r\n;--- of physical memory as input.\r\n\r\n    mov ecx, [vdsstat.DMABuffStartPhys]\r\n    cmp eax, ecx\r\n    jnc @@abovedma\r\n    @dprintf ?INITDBG, <\"must waste space, phys end of monitor=%X is below DMA buff=%X\",10>, eax, ecx\r\n    add ecx, [vdsstat.DMABuffSize]      ;get physical end of the DMA buff\r\n    mov eax, ecx\r\n    xor edi, edi\r\n@@abovedma:\r\n    add edi, eax\r\n    mov esi, jemmini\r\n    mov eax, [esi].JEMMINIT.MonitorEnd\r\n\r\n    cmp eax, EDI\r\n    jnc @@nomemproblem      ;run out of memory?\r\n\r\n; ran out of memory, shouldn't happen, avoid disaster by setting\r\n; max VCPI/EMS memory to 0\r\n\r\n    @dprintf ?INITDBG, <\"ERROR: out of memory condition on init, MonitorEnd=%X EDI=%X\",10>,[esi].JEMMINIT.MonitorEnd, edi\r\n\r\n    mov [EMSPagesMax], 0\r\n    mov [pmem.dwMaxMem4K], 0\r\n    jmp @@initdone\r\n\r\n@@nomemproblem:\r\n    @dprintf ?INITDBG, <\"end of monitor data, physical=%X, end of XMS memory block=%X\",10>, edi, eax\r\n\r\n;--- force 4K alignment for EMS/VCPI fixed pages and UMB's\r\n\r\n    ADD EDI,4095\r\n    AND DI,NOT 4095\r\n    \r\n    sub eax, edi\r\n    jnc @F\r\n    xor eax, eax\r\n@@:\r\n\r\n;--- eax=rest of monitor memory block\r\n;--- edi=physical address\r\n;--- esi=JEMMINIT\r\n\r\n;--- now init the UMBs. these should not affect VCPI/EMS memory \r\n\r\n    cmp [esi].JEMMINIT.NoRAM,0\r\n    jnz @@noumbmapping\r\n\r\n    @dprintf ?INITDBG, <\"UMB init start, mem to map=%X, remaining mem=%X\",10>, edi, eax\r\n\r\n    push ebp\r\n    mov ebp, eax\r\n\r\n;--- map in the UMB pages\r\n\r\n    mov ebx,[esi].JEMMINIT.PageMap\r\n    mov ecx,0A0h        ;check A000-FFFF only!\r\n@@nextitem:\r\n    cmp ebp,1000h       ;are 4k still available?\r\n    jc @@umbmemout\r\n    mov al,[ebx+ecx]\r\n    call IsUMBMemory\r\n    jc @@skippage\r\nif ?SPLIT\r\n    and ah,ah           ;is it a \"split\" page?\r\n    jz @@isstdumb\r\n    call CopyROM        ;then copy ROM content to RAM\r\n    jmp @@pageshadowed\r\n@@isstdumb:\r\nendif\r\n    lea eax, @GetPTEAddr(?PAGETAB0+ecx*4)\r\n    mov edx,[eax]\r\n    and edx,0FFFh       ;don't modify the PTE bits\r\n    or edx,edi\r\n    mov [eax],edx\r\n@@pageshadowed:\r\n;    @dprintf ?INITDBG, <\"%2X \">, cl\r\n    add edi,1000h\r\n    sub ebp,1000h\r\n@@skippage:\r\n    inc cl\r\n    jnz @@nextitem\r\n@@umbmemout:\r\n    mov eax,ebp\r\n    pop ebp\r\n\r\n    @dprintf ?INITDBG, <\"UMBs mapped, remaining mem=%X\",10>, eax\r\n\r\n@@noumbmapping:\r\n\r\n;   cmp [esi].JEMMINIT.AltBoot,0\r\n;   jnz @@noshadow\r\n\r\n;-- shadow ROM page 0FFh to catch jumps to FFFF:0\r\n;-- to fill the new page with the content of the ROM, map it\r\n;-- at linear scratch pos and copy the content.\r\n\r\n    cmp eax,1000h   ;is enough free space available?\r\n    jc @@noshadow\r\n    mov ecx, 0FFh\r\n    call CopyROM\r\n    mov WORD PTR ds:[0FFFF0h],19CDh     ; set \"INT 19h\" at FFFF:0000\r\n    lea edx, @GetPTEAddr(?PAGETAB0+ecx*4)\r\n    and byte ptr [edx],not PTF_RW       ; make this page R/O\r\n    add edi,1000h\r\n    sub eax,1000h\r\n@@noshadow:\r\n\r\n;--- flush TLB to activate the UMBs and shadow ROMs\r\n\r\n    mov ecx,cr3\r\n    mov cr3,ecx\r\n\r\nif ?PGE\r\n    test byte ptr tmpFeatures+1,CF_PGE shr 8  ;PGE supported?\r\n    jz @F\r\n    cmp [esi].JEMMINIT.NoPGE,0\r\n    jnz @F\r\n    .586p\r\n    mov ecx, CR4\r\n    or cl, CR4_PGE\r\n    mov CR4, ecx\r\n    .386\r\n@@:\r\nendif\r\n\r\n    MOV [tmpFeatures],EDI   ; save phys addr of first EMS-page\r\n\r\n;--- EDI -> start free mem\r\n;--- EAX -> free mem size\r\n\r\n    shr eax,12                  ; convert bytes to 4k pages\r\n    call Pool_Init2\r\n\r\n    @dprintf ?INITDBG, <\"EMS/VCPI memory handling initialised, MaxMem4K=%X\",10,\"end of preallocated EMS, physical=%X\",10>, [pmem.dwMaxMem4K], edi\r\n\r\nif ?INTEGRATED\r\n\r\n;--- for the integrated version the rest of the memory can be released now\r\n\r\n\tmov ebx, edi\r\n\tsub ebx, [esi].JEMMINIT.MonitorStart\r\n\tshr ebx, 10\r\n\tmov dx, [esi].JEMMINIT.XMSControlHandle\r\n\tpush esi\r\n\tcall xms_ext_realloc_emb\r\n\tpop esi\r\n\r\nendif\r\n\r\n@@initdone:\r\n\r\n;-- clear all dirty + accessed flags in page table 0\r\n\r\n    mov edx, @GetPTEAddr(?PAGETAB0)\r\n    mov ecx, 1000h/4\r\n    mov al, not (PTF_ACCESS or PTF_DIRTY)\r\n@@:\r\n    and [edx],al\r\n    add edx, 4\r\n    loop @B\r\n\r\n    mov eax, [ecx+06h*4]\r\n    mov [OldInt06],eax\r\n    mov eax, [ecx+19h*4]\r\n    mov [OldInt19],eax\r\n    mov eax, ds:[67h*4]\r\n    mov [OldInt67],eax\r\n\r\nif ?VDS\r\n    call VDS_Init\r\nendif\r\n\r\n;--- now clear the UMB pages and fill the UMB segment table\r\n    call UMB_Init\r\n\r\n    mov ebx, offset UMBsegments\r\n\r\nif ?ADDCONV\r\n;--- check if first UMB is exactly at the top of\r\n;--- conventional memory (40h:13h). If so, allocate\r\n;--- the UMB and increase conventional memory.\r\n;--- Don't do any DOS-specific work! This is to be\r\n;--- done later by the 16-bit part.\r\n\r\n;--- this code would move the XBDA here in protected-mode\r\n;--- instead of in jemm16.asm.\r\n;--- the problem with the code in jemm16.asm is that it can't\r\n;--- control which UMB is used for the new XBDA; so the first\r\n;--- is used, which is bad if it is A000-B7FF.\r\n;--- currently the workaround is to use MOVEXBDA.exe for XBDA\r\n;--- moving.\r\nif ?MOVEXBDA and ?MOVEXBDAPM\r\n    push ebx\r\n    test bV86Flags,V86F_MOVEXBDA\r\n    jz nomovexbda\r\n    movzx eax,word ptr ds:[@XBDA]   ;does XBDA exist?\r\n    and eax,eax \r\n    jz nomovexbda\r\n if 1 ; v5.82: check similiar to jemm16.asm, move_xbda: is XBDA just above conv. memory?\r\n    movzx ecx, word ptr ds:[@MEM_SIZE]\r\n    shl ecx, 6                      ;kB to para\r\n    cmp ecx, eax\r\n    jnz nomovexbda\r\n endif\r\n    shl eax,4\r\n    cmp [ebx].UMBBLK.wSegm,0C000h   ;first UMB in VGA region A000-BFFF?\r\n    jae @F\r\n    cmp [ebx+sizeof UMBBLK].UMBBLK.wSize,0  ;don't use it if there's another UMB.\r\n    jz @F\r\n    add ebx,sizeof UMBBLK\r\n@@:\r\n    mov cx, [ebx].UMBBLK.wSize\r\n    shr cx, 6                       ;convert para to kB\r\n    movzx dx,byte ptr [eax]         ;get size of XBDA in kB\r\n    @dprintf ?INITDBG, <\"XBDA/UMB size=%X/%X kB\",10>, dx, cx\r\n    cmp cx,dx                       ;UMB large enough for XBDA?\r\n    jb nomovexbda\r\n    add ds:[@MEM_SIZE],dx\r\n    push esi\r\n    mov esi,eax\r\n    movzx edi,[ebx].UMBBLK.wSegm\r\n    @dprintf ?INITDBG, <\"moving XBDA to %X\",10>, di\r\n    mov ds:[@XBDA],di\r\n    shl edi,4\r\n    shl dx,6    ;covert kb to para\r\n    sub [ebx].UMBBLK.wSize,dx\r\n    add [ebx].UMBBLK.wSegm,dx\r\n    movzx ecx,dx\r\n    shl ecx,2\r\n    rep movsd\r\n    pop esi\r\nnomovexbda:\r\n    pop ebx\r\nendif\r\n\r\n;--- is first UMB adjacent to \"top of conv memory\" in 40:13h?\r\n\r\n    mov ax, ds:[@MEM_SIZE]  ;size in KB (640 kB = 280h)\r\n    shl ax, 6           ;280h -> A000h\r\n\r\n    @dprintf ?INITDBG, <\"[40:13]=%X UMB=%X\",10>, ax, [ebx].UMBBLK.wSegm\r\n\r\n    cmp ax, [ebx].UMBBLK.wSegm\r\n    jnz noinc_convmem\r\n\r\n;--- then add this UMB to conventional memory and mark UMB as allocated\r\n    mov cx, [ebx].UMBBLK.wSize\r\n    shr cx, 6\r\n    add ds:[@MEM_SIZE],cx\r\n    @dprintf ?INITDBG, <\"new conv memsize [40:13]=%X\",10>, word ptr ds:[@MEM_SIZE]\r\n    or byte ptr [ebx].UMBBLK.wSize+1, UMB_ALLOCATED\r\n    add ebx, sizeof UMBBLK\r\nnoinc_convmem:\r\nendif\r\n\r\nif ?MOVEHIGH\r\n    cmp [esi].JEMMINIT.NoHigh,0   ;NOHI active?\r\n    jnz @@nomovehigh\r\n    mov ecx,[dwRes]\r\n    cmp ecx,0A0000h                 ;already loaded high?\r\n    jnc @@nomovehigh\r\n@@anothertry:\r\n    movzx eax,[ebx].UMBBLK.wSegm\r\n    and eax,eax\r\n    jz @@nomovehigh\r\nif 0\r\n    cmp ah,0C0h                     ;avoid to move into the video segments\r\n    jnc @@umbok\r\n    cmp byte ptr [ebx+sizeof UMBBLK].UMBBLK.wSegm+1,0\r\n    jz @@umbok\r\n    add ebx,sizeof UMBBLK\r\n    jmp @@anothertry\r\n@@umbok:\r\nendif\r\n    mov [esi].JEMMINIT.ResUMB,ax\r\n    mov esi,ecx\r\n    movzx edi,ax\r\n    shl edi,4\r\n    push edi\r\n    mov ecx,rsofsinit\r\n    movzx ecx,[ecx].RSOFS.wSizeRes\r\n    mov eax,ecx\r\n    rep movsb\r\n    shr eax,4\r\n    add [ebx].UMBBLK.wSegm,ax\r\n    sub [ebx].UMBBLK.wSize,ax\r\n    pop edi\r\n\r\n    mov esi, [dwRes]\r\n    mov [dwRes],edi\r\n    mov eax,edi\r\n    shr eax,4\r\n    mov [dwRSeg], eax\r\n    sub edi, esi\r\n  if ?HOOK13\r\n    add [dwRFlags],edi\r\n  endif\r\n    add [bpstart],edi\r\n  if ?INTEGRATED\r\n    add [XMS_Handle_Table.xht_pArray],edi\r\n  endif\r\n    @dprintf ?INITDBG, <\"resident part moved high, seg=%X\",10>, ax\r\n@@nomovehigh:\r\nendif\r\n\r\n    mov eax, [tmpFeatures]\r\n    mov esp, ebp\r\n\r\n    @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]\r\n\r\n    .386p\r\n    CLTS       ; clear TS (Task Switch) flag\r\n    .386\r\n    IRETD      ; switch to v86 mode\r\n\r\nInitMonitor ENDP\r\n\r\n;--- copy ROM content to a shadow page\r\n;--- ecx = linear page number to be shadowed (0=00000,1=01000,...)\r\n;--- (max value 3FFh = address space 3FFxxxh)\r\n;--- edi = physical address to map\r\n\r\nCopyROM proc\r\n    pushad\r\n    push ecx\r\n    mov cl,1\r\n    mov eax,edi\r\n    call MapPhysPagesEx  ;map a free page in PTE heap\r\n    pop esi\r\n    lea ebx, @GetPTEAddr(?PAGETAB0+esi*4)\r\n    mov edx, [ebx]      ;get PTE for ROM page\r\n    and edx, 0FFFh\r\n    or edx, edi        ;copy the PTE attributes\r\n\r\n    mov edi, eax        ;copy ROM content\r\n    shl esi, 12\r\n    mov ecx, 1000h/4\r\n    rep movsd\r\n\r\n    mov [ebx], edx      ;set PTE on old ROM location\r\n\r\n    mov eax,cr3         ;flush TLB\r\n    mov cr3,eax\r\n\r\n    @dprintf ?INITDBG, <\"PTE %X used to shadow page \">, edx\r\n    popad\r\n    @dprintf ?INITDBG, <\"%X\",10>, ecx\r\n    ret\r\nCopyROM endp\r\n\r\nIsUMBMemory proc public\r\nif ?SPLIT\r\n    cmp al,'1'\r\n    jb @@isnotumb\r\n    mov ah,1\r\n    cmp al,'8'\r\n    jb @@isumb\r\n    dec ah\r\nendif\r\n    cmp al,'U'\r\n    jz @@isumb\r\n    cmp al,'I'\r\n    jz @@isumb\r\n    cmp al,'P'\r\n    jnz @@isnotumb\r\n    cmp [bNoFrame],0\r\n    jz @@isnotumb\r\n@@isumb:\r\n    clc\r\n    ret\r\n@@isnotumb:\r\n    stc\r\n    ret\r\nIsUMBMemory endp\r\n\r\n.text$04 ENDS\r\n\r\nif 0\r\n.text$04z segment FLAT public 'CODE'\r\nV86_TOTAL equ $ - _start\r\n.text$04z ends\r\nendif\r\n\r\n    END\r\n"
  },
  {
    "path": "src/INIT16.ASM",
    "content": "\r\n;--- 16-bit initialization part\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n\t.model tiny\r\n\t.386\r\n\r\nCStr macro text:VARARG\r\nlocal sym\r\nCONST segment\r\nsym db text,0\r\nCONST ends\r\n\texitm <offset sym>\r\nendm\r\n\r\nFALSE equ 0\r\nTRUE equ 1\r\nNULL equ 0\r\n\r\n?USEMEMSET equ 0\r\n?UMBPCI    equ 1   ; 1=be aware of UMBPCI-created UMBs\r\nifndef ?NOVMWARE\r\n?NOVMWARE equ 1    ; 1=support option NOVMW to disable VMWare detection\r\nendif\r\n\r\n\tinclude jemm.inc\r\n\tinclude jsystem.inc\r\n\tinclude jemm16.inc\r\n\tinclude debug16.inc\r\n\tinclude xms.inc\r\n\r\n?I15RESNOSCAN equ 0\t;1=don't scan regions marked as reserved by Int 15h, ax=e820\r\nCPUID1_PSE36 equ 17\r\n\r\nNAMEGEN equ <\"Jemm\">\r\n\r\nEMS_MAX_PAGES_ALLOWED equ 2048\t;std=2048 ( 2048 * 16kB = 32768 kB )\r\n\r\nife ?INTEGRATED\r\nREGS struct\r\nunion\r\nstruct\r\n_eax  dd ?\r\n_ebx  dd ?\r\n_ecx  dd ?\r\n_edx  dd ?\r\nends\r\nstruct\r\n_ax  dw ?,?\r\n_bx  dw ?,?\r\n_cx  dw ?,?\r\n_dx  dw ?,?\r\nends\r\nends\r\nREGS ends\r\nendif\r\n\r\n\t.data\r\n\r\njemmini JEMMINIT <>\r\ndwAllocatedBytes DD 0\t;set by AllocAndInitMem()\r\n\r\nxms XMSPARAMS <>\r\n\r\nif ?INTEGRATED\r\nbNoE820\tDB 0            ;NOE820 option; it's a noop if MAXSEXT != 0\r\nbNoE801\tDB 0\r\nendif\r\nbVerbose\tDB FALSE\r\n\r\nif ?I15RESNOSCAN\r\nbI15RsvdChk db FALSE\r\nendif\r\n\r\nif ?INTEGRATED eq 0\r\nxmsspec3\tDW 0\r\nendif\r\nbFrameWanted DB 0   ;FRAME=XX00\r\nExcludeTest\tDB FALSE;X=TEST\r\nIncludeTest\tDB FALSE;I=TEST\r\nif ?SPLIT\r\nSplitTest\tDB 0\r\nendif\r\nMinRequest\tDB FALSE;MIN= has been found\r\n\r\n\t.const\r\n\r\nif ?INTEGRATED\r\n\r\nENABLE_A20 equ 2\r\nDISABLE_A20 equ 0\r\n\r\na20_methods label byte\r\n\tdb 3,\"kbc\"\t\t;0 (A20_KBC)\r\n\tdb 3,\"ps2\"\t\t;1 (A20_PS2)\r\n\tdb 4,\"bios\" \t;2\r\n\tdb 8,\"alwayson\" ;3\r\n\tdb 4,\"fast\" \t;4\r\n\tdb 6,\"port92\"\t;5\r\n\tdb 0\r\n\r\nszKBC   db \"KBC\",0\r\nszPS2   db \"PS/2\",0\r\nszBIOS  db \"BIOS\",0\r\nszAlwaysOn db \"Always On\",0\r\nszFast  db \"Fast\",0\r\nszPort92 db \"Port 92\",0\r\n\r\n\teven\r\n\r\n;--- table order must match A20_ switch methods constants\r\nA20procs label word\r\n\tdw disable_enable_a20_KBC\t;A20_KBC\r\n\tdw disable_enable_a20_fast\t;A20_PS2\r\n\tdw disable_enable_a20_BIOS\t;A20_BIOS\r\n\tdw disable_enable_a20_dummy\t;A20_ALWAYSON\r\n\tdw disable_enable_a20_fast\t;A20_FAST\r\n\tdw disable_enable_a20_fast\t;A20_PORT92\r\n\r\nA20strings label word\r\n\tdw szKBC\r\n\tdw szPS2\r\n\tdw szBIOS\r\n\tdw szAlwaysOn\r\n\tdw szFast\r\n\tdw szPort92\r\n\r\nendif\r\n\r\nsig1        db 'EMMXXXX0',0\r\nsig2        db 'EMMQXXX0',0 ; ID if NOEMS specified\r\n\r\nszError\t\tDB 'Error',07H,0\r\nszWarning\tDB 'Warning',0\r\nszOn\t\tDB \"On\",0\r\nszOff\t\tDB \"Off\",0\r\nszStartup\tDB \"%j v\", @CatStr(!\",%?VERSIONHIGH,!\"), \".\", @CatStr(!\",%?VERSIONLOW,!\"),\" [\", @CatStr(!\",%@Date,!\"), \"]\",LF,0\r\nszCopyRight\tDB '%j. Parts (c) tom ehlert 2001-2006 c''t/H. Albrecht 1990',LF,0\r\n\r\nszHelp label byte\r\n\tdb \"usage: either add a line to CONFIG.SYS: DEVICE=\",NAMEEXE,\".EXE [ options ]\",LF\r\n\tdb \" or run it from the command line: C:\\>\",NAMEEXE,\" [ options ]\",LF\r\n\tdb \"available options are:\",LF\r\nif ?A20XMS or ?A20PORTS\r\n\tdb \"+A20/NOA20     A20-disable emulation on/off (default on)\",LF\r\nendif\r\nif ?INTEGRATED\r\n\tdb \" A20METHOD:m   set A20 switch method. Possible values for <m> are\",LF\r\n\tdb \"               KBC, PS2, BIOS, FAST, PORT92 and ALWAYSON.\",LF\r\nendif\r\n\tdb \" ALTBOOT       use alternate reboot strategy\", LF\r\n\tdb \" B=segm        specify lowest segment address for EMS banking (default=4000)\",LF\r\n\tdb \" D=n           set DMA buffer size in kB (default=64, max is 128)\",LF\r\nif ?EMX\r\n\tdb \" EMX           increased EMX DOS extender compatibility\",LF\r\nendif\r\nif ?FASTBOOT\r\n\tdb \" FASTBOOT      fast reboot. Requires %j to be loaded in CONFIG.SYS.\",LF\r\nendif\r\n\tdb \" FRAME=E000    set EMS page frame (FRAME=NONE disables frame). Any value\",LF\r\n\tdb \"               between 8000 and E000 is accepted, but not all will work.\",LF\r\nif ?INTEGRATED\r\n\tdb \" HMAMIN=n      set minimum amount in Kb to allocate the HMA.\",LF\r\nendif\r\n    db \" I=start-end   force a region to be used for UMBs. Without this option\",LF\r\n    db \"               range C000-EFFF is scanned for unused pages. May also be used\",LF\r\n    db \"               to add (parts of) regions A000-BFFF or F000-F7FF as UMBs. Don't\",LF\r\n    db \"               use this option if you don't know what you are doing!\",LF\r\n    db \" I=TEST        scan ROMs for unused pages, include found regions as UMBs\",LF\r\n    db \" [MAX=]n       limit for VCPI (and EMS if < 32M) memory in kB (default 120 MB)\",LF\r\nif ?INTEGRATED\r\n    db \" MAXEXT=n      limit extended memory usage to <n> kB\",LF\r\n if ?XMS35\r\n    db \" MAXSEXT=n     limit extended memory usage beyond 4 GB to <n> kB\",LF\r\n endif\r\nendif\r\n    db \" MIN=n         reserve up to <n> kB for EMS/VCPI memory on init (default=0)\",LF\r\nif ?MOVEXBDA\r\n    db \" MOVEXBDA      move XBDA into UMB. Some BIOSes won't allow this.\",LF\r\nendif\r\n    db \" NOCHECK       disallow access to address space without RAM (MMIO)\",LF\r\nif ?INTEGRATED\r\n    db \" NOE801        don't use Int 15h, E801h to get amount of ext. memory\",LF\r\n ;ife ?XMS35\r\n    db \" NOE820        don't use Int 15h, E820h to get amount of ext. memory\",LF\r\n ;endif\r\nendif\r\n    db \" NOEMS         disable EMS handling\",LF\r\n    db \" NODYN         no dynamic XMS memory allocation (use MIN= to set fix amount)\",LF\r\n    db \" NOHI          don't move resident part into first UMB\",LF\r\n    db \" NOINVLPG      don't use INVLPG opcode\",LF\r\nif ?NOVMWARE\r\n    db \" NOVMW         don't try to detect VMWare\",LF\r\nendif\r\nif ?PGE\r\n    db \"+PGE/NOPGE     Page Global Enable feature usage on/off (default off)\",LF\r\nendif\r\n    db \" RAM/NORAM     try to supply UMBs on/off (default on)\",LF\r\n    db \" S=start-end   assume Shadow-RAM activated by UMBPCI, include it as UMB\",LF\r\nif ?SB\r\n    db \" SB            SoundBlaster driver compatibility mode\",LF\r\nendif\r\nif ?SPLIT\r\n    db \" SPLIT         regain partially used EPROM 4k pages for UMBs\",LF\r\nendif\r\n    db \"+VCPI/NOVCPI   VCPI Support on/off (default on)\",LF\r\n    db \" VDS/NOVDS     Virtual DMA Services on/off (default on)\",LF\r\nif ?VME\r\n    db \"+VME/NOVME     V86-Mode Extensions on/off (default off)\",LF\r\nendif\r\n    db \" VERBOSE       display additional details during start (abbr: /V)\",LF\r\n    db \" X=start-end   exclude region from being touched or used by %j\",LF\r\n    db \" X=TEST        scan memory region C000-EFFF for UMB exclusion\",LF\r\nif ?INTEGRATED\r\n    db \" X2MAX=n       max. value in Kb for XMS V2 free memory report (default=65535)\",LF\r\n    db \" XMSHANDLES=n  number of available XMS handles (\",@CatStr(!\",%?XMS_STATICHDLS,!\"),\"<=n<=\",@CatStr(!\",%?XMS_MAXHDLS,!\"),\", default=\",@CatStr(!\",%?XMS_DEFHDLS,!\"),\")\",LF\r\nendif\r\n    db LF\r\n    db \" '+': option can be set dynamically by running %j from the command line\",LF\r\nif ?LOAD\r\n    db LF\r\n    db \"When invoked from the command line, %j additionally will understand:\",LF\r\n    db \" LOAD          install\",LF\r\nendif\r\nif ?UNLOAD\r\n    db \" UNLOAD        uninstall\",LF\r\nendif\r\n    db 0\r\n\r\n\t.data?\r\n\r\nif ?INTEGRATED eq 0\r\nxmsreg REGS <>\r\nendif\r\n\r\n;--- 256 entries for real-mode pages [00-FF]xxxh\r\n;    'R' = RAM\r\n;    'E' = EPROM\r\n;    'S' = Shadow-RAM activated by UMBPCI\r\n;    'G' = GRAPHICS region\r\n;    'V' = VMWARE allocated, but possibly re-usable via I= (0e800-0ebffh)\r\n;\r\n;    'U' = possible UMB space, because nothing else found\r\n;    'P' = PAGEFRAME\r\n;    'I' = INCLUDE = forced from commandline\r\n;    'X' = EXCLUDE = forbidden from commandline\r\n\r\nSystemMemory DB 100H DUP (?)\r\n\r\n\t.code\r\n\r\n;--- ltoa(long n, char * s, int base);\r\n;--- convert long to string\r\n;--- modifies SI\r\n\r\nltoa PROC stdcall uses edi number:dword, outb:word, base:word\r\n\r\n\tmov ch,0\r\n\tmovzx edi, base\r\n\tmov eax, number\r\n\tcmp di,-10\r\n\tjne @F\r\n\tmov di,10\r\n\tand eax,eax\r\n\tjns @F\r\n\tneg eax\r\n\tmov ch,'-'\r\n@@:\r\n\tmov si,outb\r\n\tadd si,10\r\n\tmov BYTE PTR [si],0\r\n\tdec si\r\n@@nextdigit:\r\n\txor edx, edx\r\n\tdiv edi\r\n\tadd dl,'0'\r\n\tcmp dl,'9'\r\n\tjbe @F\r\n\tadd dl,7+20h\r\n@@:\r\n\tmov [si],dl\r\n\tdec si\r\n\tand eax, eax\r\n\tjne @@nextdigit\r\n\tcmp ch,0\r\n\tje @F\r\n\tmov [si],ch\r\n\tdec si\r\n@@:\r\n\tinc si\r\n\tmov ax,si\r\n\tret\r\n\r\nltoa ENDP\r\n\r\n;--- little printf() implementation.\r\n;--- understands type %c, %x, %d, %i, u%, %s \r\n;--- and modifier l(ong)\r\n;--- assumes DS=SS=DGROUP\r\n;--- preserves BX,SI,DI,ES\r\n\r\nprintf PROC c public uses si di fmt:ptr byte, args:VARARG\r\n\r\nlocal size_:word\r\nlocal flag:byte\r\nlocal longarg:byte\r\nlocal fill:byte\r\nlocal szTmp[12]:byte\r\n\r\n\tlea di,[fmt+2]\r\n@@L335:\r\n\tmov si,[fmt]\r\nnextchar:\r\n\tlodsb\r\n\tor al,al\r\n\tje done\r\n\tcmp al,'%'\r\n\tje formatitem\r\n\tcall handle_char\r\n\tjmp nextchar\r\ndone:\r\n\txor ax,ax\r\n\tret\r\n\r\nformatitem:\r\n\tpush @@L335\r\n\txor dx,dx\r\n\tmov [longarg],dl\r\n\tmov al,1\r\n\tmov cl,' '\r\n\tcmp BYTE PTR [si],'-'\r\n\tjne @F\r\n\tdec ax\r\n\tinc si\r\n@@:\r\n\tmov [flag],al\r\n\tcmp BYTE PTR [si],'0'\r\n\tjne @F\r\n\tmov cl,'0'\r\n\tinc si\r\n@@:\r\n\tmov [fill],cl\r\nnextdigit:\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb donenumber\r\n\tcmp BYTE PTR [si],'9'\r\n\tja donenumber\r\n\tlodsb\r\n\tsub al,'0'\r\n\tcbw\r\n\timul dx,dx,10\t\t;dx = dx * 10\r\n\tadd dx,ax\r\n\tjmp nextdigit\r\ndonenumber:\r\n\tmov [size_],dx\r\n\tcmp BYTE PTR [si],'l'\r\n\tjne @F\r\n\tmov [longarg],1\r\n\tinc si\r\n@@:\r\n\tlodsb\r\n\tmov [fmt],si\r\n\tcmp al,'x'\r\n\tje handle_x\r\n\tcmp al,'X'\r\n\tje handle_x\r\n\tcmp al,'c'\r\n\tje handle_c\r\n\tcmp al,'d'\r\n\tje handle_d\r\n\tcmp al,'i'\r\n\tje handle_i\r\n\tcmp al,'s'\r\n\tje handle_s\r\n\tcmp al,'j'\r\n\tje handle_j\r\n\tcmp al,'u'\r\n\tje handle_u\r\n\tjmp @@L359\r\nhandle_c:\r\n\tmov ax,[di]\r\n\tadd di,2\r\n@@L359:\r\n\tcall handle_char\r\n\tretn\r\n\r\nhandle_j:\r\n\tmov si,CStr(NAMEMOD)\r\n\tjmp @@do_outputstring260\r\n\r\nhandle_s:\r\n\tmov si,[di]\r\n\tadd di,2\r\n\tjmp @@do_outputstring260\r\n\r\nhandle_x:\r\n\tmov cx,16\r\n\tjmp @@lprt262\r\nhandle_d:\r\nhandle_i:\r\n\tmov cx,-10\r\n\tjmp @@lprt262\r\nhandle_u:\r\n\tmov cx,10\r\n@@lprt262:\r\n\tmov ax,[di]\r\n\tadd di,2\r\n\tsub dx,dx\r\n\tcmp cx,0\t\t;signed or unsigned?\r\n\tjge @F\r\n\tcwd\r\n@@:\r\n\tcmp [longarg],0\r\n\tje @F\r\n\tmov dx,[di]\r\n\tadd di,2\r\n@@:\r\n\tlea si,[szTmp]\r\n\tinvoke ltoa, dx::ax, si, cx\r\n\tmov si,ax\r\n\r\n@@do_outputstring260:\r\n\tmov ax,si\r\n\tmov cx,size_\r\n\t.while byte ptr [si]\r\n\t\tinc si\r\n\t.endw\r\n\tsub si,ax\r\n\txchg ax,si\r\n\tsub cx,ax\r\n\r\n\t.if flag == 1\r\n\t\t.while sword ptr cx > 0\r\n\t\t\tmov al,[fill]\r\n\t\t\tcall handle_char\r\n\t\t\tdec cx\r\n\t\t.endw\r\n\t.endif\r\n\r\n\t.while byte ptr [si]\r\n\t\tlodsb\r\n\t\tcall handle_char\r\n\t.endw\r\n\t\r\n\t.while sword ptr cx > 0\r\n\t\tmov al,[fill]\r\n\t\tcall handle_char\r\n\t\tdec cx\r\n\t.endw\r\n\tretn\r\n\r\nhandle_char:\r\n\tmov dl,al\r\n\tcmp al,10\r\n\tjnz @F\r\n\tmov dl,13\r\n\tcall @F\r\n\tmov dl,10\r\n@@:\r\n\tmov ah,2\r\n\tint 21h\r\n\tretn\r\n\r\nprintf ENDP\r\n\r\nif 0\r\nmemcpy proc c uses si di dst:ptr byte, src:ptr byte, len:word\r\n\tmov di,dst\r\n\tmov si,src\r\n\tmov cx,len\r\n\trep movsb\r\n\tret\r\nmemcpy endp\r\nendif\r\n\r\nif ?USEMEMSET\r\nmemset proc c uses di dest:ptr BYTE, value:WORD, len:WORD\r\n\r\n\tmov di,dest\r\n\tmov ax,value\r\n\tmov cx,len\r\n\trep stosb\r\n\tret\r\n\r\nmemset endp\r\nendif\r\n\r\n_memicmp proc c uses si di p1:ptr BYTE, p2:ptr BYTE, len:WORD\r\n\r\n\tmov cx,len\r\n\tmov si,p2\r\n\tmov di,p1\r\nnextitem:\r\n\tlodsb\r\n\tmov ah,[di]\r\n\tinc di\r\n\tor al,20h\r\n\tor ah,20h\r\n\tsub al,ah\r\n\tloopz nextitem\r\n\tcbw\r\n\tret\r\n\r\n_memicmp endp\r\n\r\nskipWhite PROC c src:ptr byte\r\n\r\n\tmov bx,src\r\nnextitem:\r\n\tmov al,[bx]\r\n\tinc bx\r\n\tcmp al,' '\r\n\tje nextitem\r\n\tcmp al,9\r\n\tje nextitem\r\n\tdec bx\r\n\tmov ax,bx\r\n\tret\r\n\r\nskipWhite ENDP\r\n\r\n;--- set memory type , but honour \"EXCLUDE=\" and \"INCLUDE=\" types\r\n;--- address: paragraph\r\n\r\nSetMemoryType PROC stdcall uses si address:word, mtype:byte\r\n\r\n\tmov si,address\r\n\tshr si,8\r\n\tadd si,offset SystemMemory\r\n\tmov al,mtype\r\n\t.if byte ptr [si] != 'I' || al == 'X'\r\n\t\tmov [si],al\r\n\t.endif\r\n\tret\r\n\r\nSetMemoryType ENDP\r\n\r\n;--- TestForSystemRAM(void *, int, int *);\r\n;--- 1. argument is \"SystemMemory\" array (256 * BYTE)\r\n;--- 2. argument is index for \"SystemMemory\" where to start scan\r\n;--- 3. argument is a pointer to WORD, will return region size in paragraphs\r\n;--- may change memory type from 'U' to 'R'\r\n;--- modifies ES\r\n\r\nTestForSystemRAM proc c uses si di pv:ptr BYTE, index:WORD, pi:ptr WORD\r\n\r\nlocal result:word\r\n\r\n\txor di, di\t\t\t;init return code\r\n\tmov result,di\r\n\tmov si, pv\r\n\tmov dx, index\r\n\tadd si, dx\r\n\tmov cx, 100h\r\n\tsub cx, dx\r\n\tjbe @@done\r\n@@nextpage:\r\n\tlodsb\r\nif 1 ;v5.80: also test 'E', since 'E' might contain RAM in v5.80 (UMBPCI)\r\n\tcmp al,'E'\r\n\tjz @F\r\nendif\r\n\tcmp al,'U'\r\n\tjz @F\r\n\tcmp al,'I'\t\t\t;'I' is also tested, but not modified\r\n\tjnz @@skipitem\t\t;so a warning can be displayed \r\n@@:\r\n;--- test a page of conventional memory\r\n\tmov ax, dx\r\n\tshl ax, 8\r\n\tmov es, ax\r\nif ?UMBPCI\r\n\tcmp dword ptr es:[0],\"BMU$\"\r\n\tjnz @F\r\n\tcmp dword ptr es:[4],\"!lbT\"\r\n\tjz isumbpci\r\n@@:\r\nendif\r\n\tcli\r\n\tmov ax, es:[0]\r\n\tmov bx, ax\r\n\txor ax, 055AAh\r\n\tmov es:[0], ax\r\n\tcmp ax, es:[0]\r\n\tjnz @@noram\r\n\txor ax, 0AA55h\r\n\tmov es:[0], ax\r\n\tcmp ax, es:[0]\r\n\tjnz @@noram\r\n\tmov al,[si-1]\r\n\t.if al == 'E' || al == 'U'\t;v5.80: change 'E' to 'R' as well\r\n\t\tmov byte ptr [si-1], 'R'\r\n\t.endif\r\n\tand di, di\r\n\tjnz @F\r\n\tmov di, es\r\n@@:\r\n\tadd result, 100h\t;100h = 4kB in paragraphs\r\n\tjmp @@shared\r\n\r\n@@noram:\r\n\tand di, di\r\n\tjz @@shared \t;skip test now, found a region\r\n\tmov cx,1\t\t;stop scanning\r\n@@shared:\r\n\tcmp bx, es:[0]\r\n\tjz @F\r\n\tmov es:[0],bx\r\n@@:\r\n\tsti\r\n\r\n@@skipitem:\r\n\tinc dx\r\n\tdec cx\r\n\tjnz @@nextpage\r\n@@done:\r\n\tmov bx, pi\r\n\tmov ax, result\r\n\tmov [bx], ax\r\n\tmov ax, di\r\n\tret\r\nif ?UMBPCI\r\nisumbpci:\r\n\tmov di,es\r\n\tmov bx,8\r\n\t.while word ptr es:[bx]\r\n\t\tmov si,es:[bx+0]\t;get segment address\r\n\t\tmov dx,si\r\n\t\tmov ax,es:[bx+2]\t;get size (paragraphs)\r\n\t\tadd dx,ax\r\n\t\tshr si,8\r\n\t\tadd si,pv\r\nnextitem:\r\n\t\tmov byte ptr [si],'S'\r\n\t\tinc si\r\n\t\tsub ax,100h\r\n\t\tja nextitem\r\n\t\tmov ax,dx\r\n\t\tdec ax\r\n\t\tpusha\r\n\t\tinvoke printf, CStr(\"Memory activated by UMBPCI found at %X-%X, included\", LF), word ptr es:[bx+0], ax\r\n\t\tpopa\r\n\t\tadd bx,2+2\r\n\t.endw\r\n\tsub dx,di\r\n\tmov result,dx\r\n\tjmp @@done\r\nendif\r\n\r\nTestForSystemRAM endp\r\n\r\n;--- handle I=TEST option\r\n;--- may change 'E' to 'I'\r\n;--- modifies ES\r\n\r\nHandleITest proc uses si\r\n\tmov si,0C0h\r\n\t.repeat\r\n\t\t.if SystemMemory[si] == 'E'\r\n\t\t\tmov ax,si\r\n\t\t\txchg al,ah\r\n\t\t\tmov es, ax\r\n\t\t\tmov bx,0\r\n\t\t\tmov al,es:[bx]\r\n\t\t\tinc bx\r\n\t\t\t.while bx < 1000h\r\n\t\t\t\t.break .if al != es:[bx]\r\n\t\t\t\tinc bx\r\n\t\t\t.endw\r\n\t\t\t.if bx == 1000h\r\n;\t\t\t\tinvoke SetMemoryType, es, 'I'\r\n\t\t\t\tinvoke SetMemoryType, es, 'U'\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t\tinc si\r\n\t.until si == 0F8h\r\n\tret\r\nHandleITest endp\r\n\r\n;    ScanSystemMemory()\r\n;    search memory for ROMS, adapters, graphics,...\r\n;\r\n;    builds SystemMemory map\r\n;\r\n;    the \"checks\" which are done are:\r\n;\r\n;    - memory range C000-EFFF is scanned for ROMs,\r\n;      if one is found, and \"I=TEST\", it is checked if there are pages\r\n;      filled with 0x00 or 0xFF.\r\n;    - with option \"X=TEST\", memory range C000-EFFF is also tested if\r\n;      content is 0x00 or 0xFF, anything else excludes page\r\n;    - memory range C000-EFFF is also checked for RAM. if found pages are\r\n;      regarded as \"reserved\" (must be explicitely included with I= or S=)\r\n\r\nScanSystemMemory PROC uses es si di\r\n\r\nlocal i:word       ;size EPROM in 0.5kB units\r\nlocal romsize:word\r\nlocal mem:word\r\n\r\n;--- check for ROMs (should find VGA-Roms)\r\n;--- also handles I=TEST\r\n\tmov mem,0c000h\r\n\t.repeat\r\n\t\tmov es,mem\r\n\t\txor si, si\r\n\t\t.if ( word ptr es:[si] == 0AA55h )\r\n\t\t\tmovzx ax,byte ptr es:[si+2]\t;size in 512-byte units\r\n\t\t\tmov di,ax\r\n\t\t\tmov i,ax\r\n\t\t\tmov romsize,ax\r\n\t\t\tmov si,es\r\n\t\t\t.if bVerbose\r\n\t\t\t\tshr ax,1\r\n\t\t\t\tsbb cx,cx\r\n\t\t\t\tand cx,5\r\n\t\t\t\tinvoke printf, CStr(\"EPROM at %X, size %u.%u kB\", LF), es, ax, cx\r\n\t\t\t.endif\r\n\r\n\t\t\t.while i\r\n\t\t\t\tmov di, 200H\t;512-bytes units\r\n\t\t\t\tmov bx,si\r\n\t\t\t\tmovzx bx,bh\r\n\t\t\t\t.if SystemMemory[bx] != 'X'\r\n\t\t\t\t\tmov ax, i\r\n\t\t\t\t\tmov cx, si\r\n\t\t\t\t\t.if ( cl == 0 || ax == romsize )\t;cl=0 means: 4k-page boundary\r\n\t\t\t\t\t\tinvoke SetMemoryType, si, 'E'\r\n\t\t\t\t\t.endif\r\n\t\t\t\t.endif\r\n\t\t\t\tmov ax,di\r\n\t\t\t\tshr di,4\t;convert bytes to paras\r\n\t\t\t\tadd si,di\r\n\t\t\t\tshr ax,9\t;convert bytes to 0.5kB units\r\n\t\t\t\tsub i,ax\r\n\t\t\t.endw\r\n\r\n\t\t\tmov ax,si\r\n\t\t\tmov bx,si\r\n\t\t\tmovzx bx,bh\r\n\t\t\t.if SplitTest && al && SystemMemory[bx] == 'E'\r\n\t\t\t\tshr al,5\r\n\t\t\t\tadd al,'0'\r\n\t\t\t\tinvoke SetMemoryType, si, al\r\n\t\t\t.endif\r\n\t\t\tmov ax, romsize\r\n\t\t\tshl ax,5\r\n\t\t\tadd ax, mem\r\n\t\t\tadd ax, 07fh\r\n\t\t\tand al, 080h\r\n\t\t\tmov mem,ax\r\n\t\t\t.continue\r\n\t\t.endif\r\n\t\tadd mem,0080H\t;add 128 paragraphs (=2 kB)\r\n\t.until mem >= 0F000h\r\n\r\nif ?I15RESNOSCAN\r\n;--- don't scan for RAM if we called Int 15h ax=e820h\r\n\tcmp bI15RsvdChk, FALSE\r\n\tjnz @F\r\nendif\r\n\r\n\tmov si, 0C0h\r\n\t.repeat\r\n\t\tlea ax, mem\r\n\t\tinvoke TestForSystemRAM, offset SystemMemory, si, ax\r\n\t\tmov si,ax\r\n\t\t.if ax\r\n\t\t\tmov ax,mem\r\n\t\t\tadd ax,si\r\n\t\t\tdec ax\r\nif ?UMBPCI\r\n\t\t\tmov es,si\r\n\t\t\t.if dword ptr es:[0] != \"BMU$\"\r\nendif\r\n\t\t\t\tinvoke printf, CStr(\"System memory found at %X-%X, region might be in use\", LF), si, ax\r\nif ?UMBPCI\r\n\t\t\t.endif\r\nendif\r\n\t\t\tmov ax,mem\r\n\t\t\tadd ax,si\r\n\t\t\tmovzx ax,ah\r\n\t\t\tmov si,ax\r\n\t\t.endif\r\n\t.until si == 0\r\n@@:\r\n\r\n;--- X=TEST ?\r\n\t.if ExcludeTest\r\n\t\tmov mem, 0a0H\r\n\t\t.while mem < 0f0H\r\n\t\t\tmov bx, mem\r\n\t\t\t.if SystemMemory[bx] == 'U'\r\n\t\t\t\tmov ah,BYTE PTR mem\r\n\t\t\t\tsub al,al\r\n\t\t\t\tmov es,ax\r\n\t\t\t\tsub cx,cx\r\n\t\t\t\tmov si,cx\r\n\t\t\t\t.while cx < 0fffH\t;don't check the page's final byte!\r\n\t\t\t\t\tmov al, es:[si]\r\n\t\t\t\t\t.if al != 0 && al != 0ffh\r\n\t\t\t\t\t\tmov bx, mem\r\n\t\t\t\t\t\tmov SystemMemory[bx],'X'\r\n\t\t\t\t\t\t.break\r\n\t\t\t\t\t.endif\r\n\t\t\t\t\tinc cx\r\n\t\t\t\t\tinc si\r\n\t\t\t\t.endw\r\n\t\t\t.endif\r\n\t\t\tinc mem\r\n\t\t.endw\r\n\t.endif\r\n\r\n;--- handle I=TEST\r\n\t.if IncludeTest\r\n\t\tcall HandleITest\r\n\t.endif\r\n\r\n\tret\r\n\r\nScanSystemMemory ENDP\r\n\r\n;--- find a contiguous area of 64 KB for the EMS page frame.\r\n;--- should handle commandline option like \"FRAME=D000\"\r\n;--- out: AX=frame segment address\r\n\r\nLocatePageFrame PROC uses di si\r\n\r\nlocal page_:word\r\nlocal bHardWanted:byte\r\nlocal bSearching:byte\r\nlocal bWarning:byte\r\n\r\n\txor al,al\r\n\tmov bSearching,al\r\n\tmov bWarning,al\r\n\tmov bHardWanted,al\r\n\r\n\t.if bFrameWanted\r\n\t\tmov bHardWanted,1\r\n\t.else\r\n\t\tmov bFrameWanted,0e0h\r\n\t.endif\r\n\r\n\txor si,si\t;frame address\r\n\tmovzx di,bFrameWanted\r\n\r\n; Line 300\r\n\txor bx,bx\r\n\t.repeat\r\n\t\tmov al, SystemMemory[bx][di]\r\n;--- v5.76: 'I' is also ok\r\n;\t\t.if al == 'U'\r\n\t\t.if al == 'U' || al == 'I'\r\n\t\t\t;\r\n\t\t.elseif bHardWanted && di >= 80h && ( al == 'R' || al == 'G' )\r\n\t\t\tmov bWarning,1\r\n\t\t.else\r\n\t\t\t.break\r\n\t\t.endif\r\n\t\tinc bx\r\n\t.until bx == 16\r\n\r\n\t.if bx == 16\r\n\t\t.if ( di & 3 )         ;v5.78: warn if frame doesn't begin on a 16 kB\r\n\t\t\tmov bWarning,1     ;physical page\r\n\t\t.endif\r\n\t\tmov si,di\r\n\t\tjmp $frameset251\r\n\t.endif\r\n\r\n\t.if bHardWanted\r\n\t\tinvoke printf, CStr(\"Selected page frame %02x00 not accepted, scanning for a valid one...\", LF), bFrameWanted\r\n\t.endif\r\n\r\n\tmov bSearching,1\r\n\r\n;--- scan memory for a page frame\r\n;--- scan from high to low E800..A000\r\n\tmov di,0E8H\r\n\t.repeat\r\n\t\txor bx,bx\r\n;--- v5.78: accept both 'U' and 'I'\r\n;\t\t.while SystemMemory[bx][di] == 'U'\r\n\t\t.while bx < 16 && (SystemMemory[bx][di] == 'U' || SystemMemory[bx][di] == 'I')\r\n\t\t\tinc bx\r\n\t\t.endw\r\n\r\n\t\t.break .if bx == 16\r\n\r\n\t\tsub di,4        ;ensure that frame starts at an EMS \"physical page\"\r\n\t.until di < 0a0h\r\n\r\n\t.if bx != 16\r\n\t\tinvoke printf, CStr(\"%s: no suitable page frame found, EMS functions limited.\", LF), addr szWarning\r\n\t\tmov jemmini.NoFrame, TRUE\r\n\t\txor ax,ax\r\n\t\tjmp exit\r\n\t.endif\r\n\tmov si, di\r\n\r\n$frameset251:\r\n\r\n\t.if bVerbose || bSearching\r\n\t\tinvoke printf, CStr(\"Using page frame %02x00\", LF), si\r\n\t.endif\r\n\t.if bWarning && (!bSearching) || di < 0C0h\r\n\t\tinvoke printf, CStr(\"%s: page frame %02x00 might not work reliably\", LF), offset szWarning, si\r\n\t.endif\r\n\r\nif ?USEMEMSET\r\n\tmov ax,offset SystemMemory\r\n\tadd ax,si\r\n\tinvoke memset, ax, 'P', 16\r\nelse\r\n\tmov di,offset SystemMemory\r\n\tadd di,si\r\n\tmov cx,16\r\n\tmov al,'P'\r\n\trep stosb\r\nendif\r\n\r\n\tmov ax,si\r\n\tmov ah,al\r\n\tsub al,al\r\n\r\nexit:\r\n\tret\r\n\r\nLocatePageFrame ENDP\r\n\r\n;--- check if a page is intended for UMB\r\n;--- out: AX=1 if yes, else AX=0\r\n\r\nIsUMBMemory PROC stdcall pg:word\r\n\r\n\tcmp jemmini.NoRAM,0\r\n\tjne notumb\r\n\tmov bx, pg\r\n\tmov al, SystemMemory[bx]\r\n\tcmp al,'U'\r\n\tje isumb\r\n\tcmp al,'I'\r\n\tje isumb\r\nif ?SPLIT\r\n\tcmp al,'0'\r\n\tjbe @F\r\n\tcmp al,'8'\r\n\tjb isumb\r\n@@:\r\nendif\r\n\tcmp jemmini.NoEMS,0\r\n\tjne @F\r\n\tcmp jemmini.NoFrame, FALSE\r\n\tje notumb\r\n@@:\r\n\tcmp al,'P'\r\n\tjne notumb\r\nisumb:\r\n\tmov ax,1\r\nexit:\r\n\tret\r\nnotumb:\r\n\txor ax,ax\r\n\tjmp exit\r\n\r\nIsUMBMemory ENDP\r\n\r\n;--- get the number of pages needed for UMBs\r\n\r\nUMBpageswanted PROC uses di si\r\n\r\n\txor di,di\r\n\tmov si,0A0H\r\n\t.repeat\r\n\t\tinvoke IsUMBMemory, si\r\n\t\tadd di,ax\r\n\t\tinc si\r\n\t.until si == 0F8h\r\n\tmov ax,di\r\n\tret\r\n\r\nUMBpageswanted ENDP\r\n\r\nif ?INTEGRATED eq 0\r\n\r\nxmscall proc stdcall uses si function:BYTE\r\n\r\n\tmov si,offset xmsreg\r\n\tmov ebx,[si].REGS._ebx\r\n\tmov edx,[si].REGS._edx\r\n\tmov ah, function\r\n\t@dprintf ?XMSRMDBG, <\"xms call: ax=%X ebx=%lX edx=%lX\",10>, ax, ebx, edx\r\n\tcall [xms.Driver]\r\n\tmov [si].REGS._eax,eax\r\n\tmov [si].REGS._ebx,ebx\r\n\tmov [si].REGS._ecx,ecx\r\n\tmov [si].REGS._edx,edx\r\n\t@dprintf ?XMSRMDBG, <\"xms ret : ax=%X ebx=%lX ecx=%lX edx=%lX\",10>, ax, edx, ecx, edx\r\n\tret\r\nxmscall endp\r\n\r\n;--- get status of XMS memory\r\n;--- sets global variables xms.mem_largest, xms.mem_free\r\n\r\nXMSGetMemoryStatus PROC c usev3:word\r\n\r\n\tmov xmsreg._bx,-1\r\n\t.if usev3\r\n\t\tinvoke xmscall, XMS_V3_QUERYMEM\t;get v3 total free memory (edx) and largest free block (eax)\r\n\t\t.if bl == 0\t;BL must be 00\r\n\t\t\tmov xms.mem_highest, ecx\r\n\t\t\tjmp ok\r\n\t\t.endif\r\n\t.endif\r\n\tinvoke xmscall, XMS_V2_QUERYMEM\t\t;get total free memory (dx) and largest free block (ax)\r\n\t.if bl == 0\r\n\t\tmovzx eax, ax\r\n\t\tmovzx edx, dx\r\n\t\tmov xms.mem_highest, (65535+1024)*1024-1 ;assume max extended memory for v2 (65535 kB)\r\n\t\tjmp ok\r\n\t.endif\r\n\txor ax,ax\r\n\tret\r\nok:\r\n\tmov xms.mem_largest, eax\r\n\tmov xms.mem_free, edx\r\n\tmov ax,1\r\n\tret\r\n\r\nXMSGetMemoryStatus ENDP\r\n\r\n;--- alloc extended memory for monitor and preallocated page pool\r\n;--- JEMM386 only\r\n\r\nXMSAllocMemory PROC c usev3:word, dwSizeKB:dword\r\n\r\nlocal xmshandle:word\r\n\r\n\tmov eax, dwSizeKB\r\n\tmov xmsreg._edx, eax\r\n\t.if eax >= 10000h || usev3\r\n\t\tinvoke xmscall, XMS_V3_ALLOCEMB\t;allocate EMB\r\n\t.else\r\n\t\tinvoke xmscall, XMS_V2_ALLOCEMB\t;allocate EMB\r\n\t.endif\r\n\t.if ax\r\n\t\tmov ax, dx\r\n\t.endif\r\n\t@dprintf ?INITRMDBG, <\"XMSAllocMemory: exit, ax=%X\",10>, ax\r\n\tret\r\n\r\nXMSAllocMemory ENDP\r\n\r\nendif\r\n\r\nI15CheckRsvdRegion proc uses si di\r\nlocal mmap:E820MAP\r\n\t@dprintf ?INITRMDBG, <\"I15CheckRsvdRegion: enter\",10>\r\n\txor ebx,ebx\r\n\tlea di,mmap\r\nnextitem:\r\n\tmov edx,SMAP\r\n\tmov ecx, sizeof E820MAP\r\n\txor eax,eax\r\n\tmov mmap.baselow,eax   ; insurance against buggy BIOS\r\n\tmov mmap.type_,eax\r\n\tmov mmap.lenlow,eax\r\n\tmov ax,0e820h\r\n\tint 15h\r\n\tcmp eax,SMAP\r\n\tjne done\r\n\r\n\tcmp ecx,sizeof E820MAP\t; didn't return all the info needed, assume done\r\n\tjb done\r\n\r\n\tcmp mmap.type_,2\t; reserved memory ?\r\n\tjne itemdone\r\n\tmov eax, mmap.baselow\r\n\tcmp eax, 0C0000h\r\n\tjb itemdone\r\n\tcmp eax,100000h\r\n\tjnc itemdone\r\n\tmov ecx, mmap.lenlow\r\n\tshr eax, 4\r\n\tshr ecx, 12\r\n\tmov si,ax\r\n\t.if bVerbose\r\n\t\tpush cx\r\n\t\tshl cx,2\r\n\t\tinvoke printf, CStr(\"Int15, ax=E820: reserved region=%04X, size=%u kB\", LF), si, cx\r\n\t\tpop cx\r\n\t.endif\r\n\t.while cx\r\n\t\tinvoke SetMemoryType, si, 'E'\r\n\t\tadd si,100h\r\n\t\tdec cx\r\n\t.endw\r\nitemdone:\r\n\tcmp ebx,0\t\t;was this the last entry?\r\n\tjnz nextitem\r\nif ?I15RESNOSCAN\r\n\tmov bI15RsvdChk, TRUE\r\nendif\r\ndone:\r\n\tret\r\nI15CheckRsvdRegion endp\r\n\r\nif ?INTEGRATED\r\n\r\n;--- return size of free extended memory block in EAX\r\n;--- may set global variables xms.mem_largest, xms.mem_free\r\n;--- returns free memory in kB in EAX.\r\n;--- called by AllocAndInitMem()\r\n\r\nI15GetMemoryStatus proc stdcall uses si di\r\n\r\nlocal maxvalue:DWORD\r\nlocal mmap:E820MAP\r\n\r\nif ?XMS35\r\n\tcmp xms.smem_used,FALSE\r\n\tjnz @F\r\nendif\r\n\tcmp [bNoE820],0    ; NOE820 option set?\r\n\tjne @@e801_check\r\n@@:\r\n\r\n\t@dprintf ?INITRMDBG, <\"I15GetMemoryStatus: get extended memory with int 15, E820\",10>\r\n\r\n;--- try 0e820h first\r\n\r\n\txor ebx, ebx\r\n\tmov si, 0\r\n\tmov maxvalue, ebx\r\n\r\ne820_nextitem:\r\n\r\n;--- ebx offset is updated with each successive int 15h\r\n\r\n\tmov edx,SMAP\r\n\tmov ecx, sizeof E820MAP\r\n\tlea di,mmap\r\n\txor eax,eax\r\n\tmov mmap.baselow,eax   ; insurance against buggy BIOS\r\n\tmov mmap.type_,eax\r\n\tmov mmap.lenlow,eax\r\n\tmov ax,0e820h\r\n\tclc\r\n\tint 15h\r\n\tsetc dl \t\t; keep carry flag status\r\n\tcmp eax,SMAP\r\n\tjne e820_bad\t; failure\r\n\tcmp dl,1\r\n\tje e820_done\t; CF doesn't have to signal fail, can just mean done\r\n\r\n\tcmp ecx,sizeof E820MAP\t; didn't return all the info needed, assume done\r\n\tjb e820_done\r\n\r\n\tcmp mmap.type_,1\t; memory available to OS?\r\n\tjne e820_itemdone\r\n\tmov edx, mmap.baselow\r\n\tmov eax, mmap.basehigh\r\n\tcmp eax, 0\t\t\t; memory beyond 4 GB?\r\nife ?XMS35\r\n\tjnz e820_itemdone\t; ignore item\r\nelse\r\n\tjnz @F\r\nendif\r\n\tcmp edx, 100000h ; has to live in extended memory\r\n\tjb e820_itemdone\r\n@@:\r\nif ?XMS35\r\n\ttest eax, xms.maxhigh\t;block start beyond 4GB/1TB?\r\n\tjnz e820_itemdone\r\nendif\r\n\t@dprintf ?INITRMDBG, <\"I15GetMemoryStatus: available memory found at %lX%08lX\",10>, eax, edx\r\n\tmov ecx, mmap.lenlow\r\nif ?XMS35\r\n\tshrd edx, eax, 10\r\n\tmov eax, mmap.lenhigh\r\n\tshrd ecx, eax, 10\r\n\tmov eax, edx\r\n\tadd eax, ecx\t\t;block crossing 4TB (XMS v3 limit)?\r\n\tjc @F\r\n\tdec eax\r\n\ttest eax, 0C0000000h;block crossing 1TB barrier (PSE-36 limit)?\r\n\tjz e820_itemok\r\n@@:\r\n\tmov ecx, 0C0000000h\t;truncate the block to ensure it fits in the first TB \r\n\tsub ecx, edx\r\nelse\r\n\tshr ecx, 10\r\n\tshr edx, 10\r\nendif\r\ne820_itemok:\r\n\tcmp si,?XMS_STATICHDLS\t;reached max capacity of the static handle array?\r\n\tjnz @F\r\n\tinvoke printf, CStr(\"%s: E820 - too many ext memory blocks, block %lX ignored!\", LF), offset szWarning, edx\r\n\tjmp e820_itemdone\r\n@@:\r\n\tcall I15SetHandle\t;set xms block, sizeK in ECX, baseK in EDX\r\n\tinc si\r\ne820_itemdone:\r\n\tcmp ebx,0\t\t;was this the last entry?\r\n\tjnz e820_nextitem\r\ne820_bad:\r\ne820_done:\r\n\tmov eax, xms.mem_largest\r\n\tand eax, eax\r\n\tjnz _ret\r\n\r\n;--- try 0e801h, but set up the registers to fail status because not\r\n;--- all BIOS's properly return the carry flag on failure\r\n\r\n@@e801_check:\r\n\tcmp [bNoE801],0 ;NOE801 option set?\r\n\tjne @@try_88h\r\n\r\n\t@dprintf ?INITRMDBG, <\"I15GetMemoryStatus: get extended memory with int 15, E801\",10>\r\n\r\n\txor ax,ax\r\n\tmov bx,ax\r\n\tmov cx,ax\r\n\tmov dx,ax\r\n\tmov ax,0e801h\r\n\tclc\r\n\tint 15h\r\n\tjc @@try_88h\r\n\tmov ax,cx\r\n\tor ax,dx\r\n\tje @@try_88h\r\n\r\n; if dx is > 0, then cx should be 3c00h since that's full 1-16M range\r\n;  if cx != 3c00h use cx and not dx\r\n\tcmp cx,3c00h\r\n\tje @F\r\n\tcmp dx,0\r\n\tje @F\r\n\txor dx,dx\r\n@@:\r\n\tmovzx edx,dx\r\n\tshl edx,6\t\t\t; convert 64K blocks to 1K\r\n\tmovzx eax,cx\r\n\tadd eax,edx\r\n\tcmp eax,64\t\t; only use if useful amount\r\n\tja @@exit\r\n\r\n@@try_88h:\r\n;--- e801h didn't do the trick, fall back to old 88h with 64M max\r\n\r\n\t@dprintf ?INITRMDBG, <\"I15GetMemoryStatus: get extended memory with int 15, 88\",10>\r\n\r\n\tclc\r\n\tmov ah,88h\r\n\tint 15h\r\n\tmovzx eax,ax\r\n\tjnc @@exit\r\n\txor ax,ax\r\n@@exit:\r\n\tmov ecx, eax\r\n\tmov edx, 1024\r\n\tcall I15SetHandle\t\t; set xms block, sizeK in ECX, baseK in EDX\r\n_ret:\r\n\t@dprintf ?INITRMDBG, <\"I15GetMemoryStatus: exit, xms.mem_free=%lX\",10>, xms.mem_free\r\n\tret\r\nI15GetMemoryStatus endp\r\n\r\n;--- get A20 method from cmdline;\r\n;--- out: NC ok, AL=method. C if method unknown.\r\n\r\nGetA20Method PROC stdcall uses si di cmdline:ptr BYTE\r\n\r\n\tmov di, offset a20_methods\r\n\tmov ah, 0\r\n\tcld\r\nnextitem:\r\n\tmovzx cx, byte ptr [di]\r\n\tstc\r\n\tjcxz @@failed\r\n\tinc di\r\n\tmov si, cmdline\r\n@@:\r\n\tlodsb\r\n\tor al, 20h\r\n\tscasb\r\n\tloopz @B\r\n\tjz @@found\r\n\tadd di, cx\r\n\tinc ah\r\n\tjmp nextitem\r\n\r\n@@found:\r\n\tmov di, cmdline\t; remove method from cmdline\r\n@@:\r\n\tlodsb\r\n\tstosb\r\n\tand al, al\r\n\tjnz @B\r\n\tmov al, ah\r\n@@failed:\r\n\tret\r\n\r\nGetA20Method ENDP\r\n\r\n;--- there are 3 A20 switch procs:\r\n;--- 1. KBC (port 64h/60h)\r\n;--- 2. fast, ps2, port92 (port 92h)\r\n;--- 3. BIOS (int 15h, ax=240xh)\r\n\r\n; try turning A20 on or off from current to see if it works\r\n; KBC HIMEM method\r\n; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack\r\n\r\ndisable_enable_a20_KBC proc\r\n\r\n\tpushf\r\n\tcli \t\t\t; shut off interrupts while we twiddle\r\n\r\n\tcall Sync8042\t; check keyboard controller ready\r\n\tmov al,0D1h \t; Send D1h\r\n\tout 64h,al\r\n\tcall Sync8042\r\n\tmov al,0ddh \t; or df=dd+2\r\n\tor al,ah\t   ; disable/enable A20 command (DDh/DFh)\r\n\tout 60h,al\r\n\tcall Sync8042\r\n\r\n; wait up to 20 microseconds for A20 line to settle\r\n\tmov al,0FFh \t; pulse output port NULL\r\n\tout 64h,al\r\n\tcall Sync8042\r\n\tpopf\r\n\tret\r\n\r\nSync8042:\r\n\txor cx,cx\r\n@@:\r\n\tin al,64h\r\n\tand al,2\r\n\tloopnz @B\r\n\tretn\r\ndisable_enable_a20_KBC endp\r\n\r\n; the so-called 'fast' A20 method replacement code\r\n; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack\r\n\r\ndisable_enable_a20_fast proc\r\n\tpushf\r\n\tin al,92h\r\n\tor ah,ah\r\n\tjne deaf_on \t; turning on A20\r\n\ttest al,2\r\n\tje deaf_done   ; already flagged off, don't do it again, might upset something\r\n\tand al,NOT 2\t; set A20 bit off\r\n\tjmp deaf_out\r\n\r\n; ah == 2\r\ndeaf_on:\r\n\ttest al,ah\r\n\tjne deaf_done\t; already flagged on\r\n\tor al,ah\t\t; set A20 bit on\r\n\r\ndeaf_out:\r\n\tout 92h,al\r\n\r\n; wait until it gets on or off, possibly superfluous, code opinion differs\r\n\txor cx,cx\r\n@@:\r\n\tin al,92h\r\n\tand al,2\r\n\tcmp al,ah\r\n\tloopne @B\r\n\r\ndeaf_done:\r\n\tpopf\r\n\tret\r\ndisable_enable_a20_fast endp\r\n\r\n; BIOS A20 method\r\n; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack\r\n; don't check for errors, assume BIOS works more than once on same call,\r\n;  if it doesn't, not much we can do about it anyway\r\n;\r\ndisable_enable_a20_BIOS proc\r\n\tpushf\r\n\tpush dx\r\n\tsub sp,12\t; give buggy BIOS some stack to chew on without causing problems\r\n\t\t\t\t; one word might suffice, but let's be really safe\r\n\tcli\r\n\tshr ah,1\t; ah to 0 or 1\r\n\tmov al,24h\r\n\txchg ah,al\t; ax == 2400h to turn off, 2401h to turn on\r\n\tint 15h\r\n\r\n\tadd sp,12\t; restore potentially gnawed-on stack\r\n\tpop dx\r\n\tpopf\r\n\tret\r\ndisable_enable_a20_BIOS endp\r\n\r\ndisable_enable_a20_dummy proc\r\n\tor ah,ah\r\n\tret\r\ndisable_enable_a20_dummy endp\r\n\r\n;--- get current status of A20;\r\n;--- out: Z = A20 disabled;\r\n\r\nget_a20_status proc uses ds es cx si di\r\n\tmov cx,-1\r\n\tmov es,cx\r\n\tmov si,10h\r\n\tinc cx\r\n\tmov ds,cx\r\n\tmov di,20h\r\n\tmov cl,4\r\n\trepz cmpsd\r\n\tret\r\nget_a20_status endp\r\n\r\n;--- test A20 state;\r\n;--- in: SI=offset a20proc for disable/enable A20;\r\n;--- out: C set if failed, NC if success.\r\n\r\ntest_A20_proc proc\r\n\tcall get_a20_status\r\n\tsetnz dl\r\n\tjz @F\t\t\t; jmp if A20 disabled\r\n\tmov ah, DISABLE_A20\r\n\tcall si \t\t; try to disable A20\r\n\tcall get_a20_status\r\n\tjnz @@fail\t\t; jmp if A20 not disabled\r\n@@:\r\n\r\n; try to enable A20 (always disabled at this point)\r\n\r\n\tmov ah, ENABLE_A20\r\n\tcall si\r\n\tcall get_a20_status\r\n\tjz @@fail\t\t; jmp if A20 not enabled\r\n\tor dl,dl\r\n\tjne @@ok\t\t; A20 was enabled on entry, done\r\n\tmov ah, DISABLE_A20\r\n\tcall si\r\n\tcall get_a20_status\r\n\tjnz @@fail\t\t; A20 not disabled\r\n@@ok:\r\n\tclc\r\n\tret\r\n@@fail:\r\n\tstc\r\n\tret\r\n\r\ntest_A20_proc endp\r\n\r\n; check if BIOS flags port 92h fast method supported\r\n\r\ndetect_fast proc\r\n\tstc\r\n\tmov ax,2403h\r\n\tint 15h\r\n\tjc @@fail\r\n\tor ah,ah\r\n\tjne @@fail\r\n\ttest bl,2\t\t;PS/2 supported?\r\n\tje @@fail\r\n\tmov si,offset disable_enable_a20_fast\r\n\tcall test_A20_proc\r\n\tret\r\n@@fail:\r\n\tstc\r\n\tret\r\ndetect_fast endp\r\n\r\n; check if BIOS flags PS/2 present, to try port 92h fast method used by PS/2's\r\n;  shares enable/disable code with fast\r\n\r\ndetect_PS2 proc uses es\r\n\r\n\tmov ah,0c0h \t; get system description vector in es:bx\r\n\tstc\r\n\tint 15h\r\n\tjc @@fail\t\t; not a PS/2\r\n\r\n; test feature information byte 1, micro channel implemented bit\r\n\ttest BYTE ptr es:[bx+5],2\r\n\tjz @@fail\t\t; not micro channel\r\n\r\n\tmov si,offset disable_enable_a20_fast\r\n\tcall test_A20_proc\r\n\tret\r\n\r\n@@fail:\r\n\tstc\r\n\tret\r\n\r\ndetect_PS2 endp\r\n\r\n; check if port 92h fast method supported without BIOS or PS/2 test\r\n;  shares enable/disable code with fast and PS/2\r\n\r\ndetect_port92 proc\r\n\r\n\tmov si,offset disable_enable_a20_fast\r\n\tcall test_A20_proc\r\n\tret\r\n\r\ndetect_port92 endp\r\n\r\n\r\ndetect_BIOS proc\r\n\tstc \t\t\t; preset carry flag\r\n\tmov ax,2402h\t; get gate status\r\n\tint 15h\r\n\tjc @@fail\r\n\tor ah,ah\r\n\tjne @@fail\r\n;\tmov cl,al\t\t; save status\r\n\r\n\tmov si,offset disable_enable_a20_BIOS\r\n\tcall test_A20_proc\r\n\tret\r\n@@fail:\r\n\tstc\r\n\tret\r\n\r\ndetect_BIOS endp\r\n\r\n\r\ndetect_KBC proc\r\n\r\n\tmov si,offset disable_enable_a20_KBC\r\n\tcall test_A20_proc\r\n\tret\r\n\r\ndetect_KBC endp\r\n\r\n;--- get the A20 method to use if not set by cmdline option;\r\n;--- out: NC ok, AL=method#; C failure.\r\n\r\nInitA20 proc c uses si\r\n\r\n\tmov al, jemmini.A20Method\r\n\tcmp al, -1\r\n\tjnz done\r\n\tcall get_a20_status  ; check if the A20 line is on, if so assume it's always on\r\n\tmov al, A20_ALWAYSON\r\n\tjnz done\r\n\r\n;--- not on, try other methods\r\n\r\n\tcall detect_fast; see if port 92h (2403h BIOS call) handler supported\r\n\tmov al, A20_FAST\r\n\tjnc done\r\n\tcall detect_PS2 ; see if port 92h (PS/2 signature) handler supported\r\n\tmov al, A20_PS2\r\n\tjnc done\r\n\tcall detect_KBC ; see if KBC handler supported\r\n\tmov al, A20_KBC\r\n\tjnc done\r\n\r\n; try BIOS here, demoted from first in line because unreliable BIOS\r\n;  versions of A20 control exist\r\n\r\n\tcall detect_BIOS; see if BIOS A20 handler supported\r\n\tmov al, A20_BIOS\r\n\tjnc done\r\n\r\n; see if fast port 92h handler supported without BIOS or PS/2 signature\r\n;  leave this test until last because messing with port 92h is\r\n;  reported to crash some machines which don't support that method\r\n\r\n\tcall detect_port92\r\n\tmov al, A20_PORT92\r\n\tjnc done\r\n\r\n\tstc\t; out of options to try, return error\r\n\tret\r\ndone:\r\n\tclc\r\n\tret\r\n\r\nInitA20 endp\r\n\r\nendif\r\n\r\n;--- allocates and initializes extended memory for jemm;\r\n;--- is called after the memory requirements have been calculated.\r\n;--- will set jemmini values Monitorstart, MaxPhysMem, XMSControlHandle.\r\n\r\nAllocAndInitMem PROC c uses esi di kbneeded:dword\r\n\r\nlocal ulcalc:dword\r\nlocal PotentialEmsVcpiMemory:dword\r\nlocal dwMinOriginal:dword\r\n\r\n\t@dprintf ?INITRMDBG, <\"AllocAndInitMem: enter\",10>\r\n\tmov eax, jemmini.MinMem16k\r\n\tmov dwMinOriginal, eax\r\n\r\n;     the DMA buffer must be 64kb aligned. Since EMS pages *must* be\r\n;     allocated from memory physically \"behind\" the DMA buffer, there may\r\n;     be some space wasted, max 60-16=44 kB\r\n\r\n\t.if jemmini.DMABufferSize > 4\r\n\t\t.if jemmini.DMABufferSize > 64\r\n\t\t\tmov ax, 32\r\n\t\t.else\r\n\t\t\tmov ax,jemmini.DMABufferSize\r\n\t\t\tshr ax,1\r\n\t\t.endif\r\n\t\tmovzx eax,ax\r\n\t\tadd kbneeded,eax\r\n\t\t.if bVerbose\r\n\t\t\tinvoke printf, CStr(\"%u kB to account for DMA buffer 64 kB alignment\", LF), ax\r\n\t\t.endif\r\n\t.endif\r\n\r\nif ?INTEGRATED\r\n\tinvoke I15GetMemoryStatus\r\n\t.if !eax\r\n\t\tinvoke printf, CStr(\"%s: can't get I15 memory status\", LF), offset szError\r\n$L569:\r\n\t\txor ax,ax\r\n\t\tret\r\n\t.endif\r\nelse\r\n\tinvoke XMSGetMemoryStatus, xmsspec3\r\n\t.if !ax\r\n\t\tinvoke printf, CStr(\"%s: can''t get XMS memory status\", LF), offset szError\r\n$L569:\r\n\t\txor ax,ax\r\n\t\tret\r\n\t.endif\r\nendif\r\n\t.if bVerbose\r\nif ?INTEGRATED\r\n\t\tinvoke printf, CStr(\"I15: largest free block %lu kB, free memory %lu kB\", LF), xms.mem_largest, xms.mem_free\r\nelse\r\n\t\tinvoke printf, CStr(\"XMS: largest free block %lu kB, free memory %lu kB\", LF), xms.mem_largest, xms.mem_free\r\nendif\r\n\t.endif\r\n\r\n;--- if pool sharing is disabled (option NODYN ;no XMS handle table for jemm386)\r\n;--- AND option MIN= is not given:\r\n;--- calculate a value for MIN= which will be preallocated.\r\n\r\n\t.if jemmini.NoPool && (!MinRequest)\r\n\t\t.if word ptr xms.mem_largest+2 > 0\t;more than 64 MB?\r\n\t\t\tmov ax, 8000H\t\t\t\t;then use 32 MB\r\n\t\t.else\r\n\t\t\tmov ax,WORD PTR xms.mem_largest+0\r\n\t\t\tshr ax,1\r\n\t\t.endif\r\n\t\tshr ax,4\r\n\t\tmovzx eax,ax\r\n\t\tmov jemmini.MinMem16k,eax\r\n\t\t.if bVerbose\r\n\t\t\tinvoke printf, CStr(\"default preallocated memory=%u 16k-pages\", LF), ax\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- reality check to throttle requests far beyond available XMS, later actual\r\n;--- adjustments are small and need not be compensated for here\r\n\r\n\t.if jemmini.MinMem16k\r\n\t\tmov eax,jemmini.MinMem16k\r\n\t\tshl eax,4\r\n\t\tadd eax,[kbneeded]\r\n\t\tmov ecx, kbneeded\r\n\t\tmov edx, xms.mem_largest\r\n\t\t.if eax > edx && ecx < edx\r\n\t\t\tsub edx,kbneeded\r\n\t\t\tshr edx, 4\r\n\t\t\tmov jemmini.MinMem16k,edx\r\n\t\t.endif\r\n\r\n;--- leave a little extended memory, if possible, for programs that want some XMS\r\n\r\n\t\tmov edx, xms.mem_largest\r\n\t\tmov eax, kbneeded\r\n\t\tadd eax, 384\r\n\r\n\t\tmov ecx,jemmini.MinMem16k\r\n\t\tshl ecx,4\r\n\t\tadd ecx,384\r\n\t\tadd ecx, kbneeded\r\n\r\n\t\t.if edx > eax && edx < ecx\r\n\t\t\tmov eax, xms.mem_largest\r\n\t\t\tsub eax, [kbneeded]\r\n\t\t\tsub eax,384\r\n\t\t\tshr eax,4\r\n\t\t\tmov jemmini.MinMem16k,eax\r\n\t\t.endif\r\n\t.endif\r\n\r\n;--- default is: all memory\r\n\r\n\tmov eax, xms.mem_free\r\n\t.if eax > kbneeded\r\n\t\tsub eax, kbneeded\r\n\t.else\r\n\t\txor eax,eax\r\n\t.endif\r\n\tmov PotentialEmsVcpiMemory, eax\r\n\r\n\t.if jemmini.NoPool\t;/* Pool sharing off? */\r\n\t\tmov eax, jemmini.MinMem16k\r\n\t\tshl eax, 4\r\n\t\t.if eax < xms.mem_free\r\n\t\t\tmov eax, jemmini.MinMem16k\r\n\t\t\tshl eax, 4\r\n\t\t\tmov PotentialEmsVcpiMemory, eax\r\n\t\t.endif\r\n\t.endif\r\n\r\n;   /* MIN= has higher priority than MAX= */\r\n\tmov eax, jemmini.MinMem16k\r\n\t.if  eax > jemmini.MaxMem16k\r\n\t\tmov jemmini.MaxMem16k,eax\r\n\t.endif\r\n\r\n;   /* MaxMem16k may have been set by MAX=, and above the limit */\r\n\tmov eax,jemmini.MaxMem16k\r\n\tshl eax,4\r\n\t.if eax > [PotentialEmsVcpiMemory]\r\n\t\tmov eax,[PotentialEmsVcpiMemory]\r\n\t\tshr eax,4\r\n\t\tmov jemmini.MaxMem16k,eax\r\n\t.endif\r\n\r\n;   /* MaxMem16k may have been set by MAX=, and below 32 MB! */\r\n;   /* this is valid, but then adjust max EMS pages as well */\r\n\tmovzx eax,jemmini.MaxEMSPages\r\n\t.if  jemmini.MaxMem16k < eax\r\n\t\tmov ax,WORD PTR jemmini.MaxMem16k\r\n\t\tmov jemmini.MaxEMSPages,ax\r\n\t.endif\r\n\r\n;   /* if MIN= has been set adjust max. EMS pages */\r\n\tmovzx eax,jemmini.MaxEMSPages\r\n\t.if jemmini.MinMem16k > eax\r\n\t\t.if jemmini.MinMem16k > MAX_EMS_PAGES_POSSIBLE\r\n\t\t\tmov ax, MAX_EMS_PAGES_POSSIBLE\r\n\t\t.else\r\n\t\t\tmov ax, word ptr jemmini.MinMem16k+0\r\n\t\t.endif\r\n\t\tmov jemmini.MaxEMSPages, ax\r\n\t.endif\r\n\r\n\t.if bVerbose\r\n\t\tmov eax,jemmini.MaxMem16k\r\n\t\tshl eax,4\r\n\t\tinvoke printf, CStr(\"potential/max. VCPI memory: %lu/%lu kB\", LF), PotentialEmsVcpiMemory, eax\r\n\t.endif\r\n\r\n;   the memory pooling need ((XMS total / 1.5M) + 1) * 64 bytes\r\n;   for pool allocation table entries\r\n;   1.5M is pool allocation maximum memory control,\r\n;   64 is pool block size,\r\n;   if dynamic XMS allocation is on, 128 more items are needed,\r\n;   which represent the maximum number of XMS handles\r\n\r\n\tmov eax, jemmini.MaxMem16k\r\n\tshl eax,4\r\n\tmov ecx,1536\t;00000600H\r\n\txor edx,edx\r\n\tdiv ecx\r\n\tadd eax,2\r\n\tmov ulcalc,eax\r\n\r\n\t.if !jemmini.NoPool\r\n\t\tadd [ulcalc],128\r\n\t.endif\r\n\r\n\tmov eax,ulcalc\r\n\tshl eax,6\r\n\tmov ulcalc,eax\r\n\r\n;   /* 4+1 bytes for each EMS page needed */\r\n;   /* 255*4 bytes for EMS handle table */\r\n;   /* 255*8 bytes for EMS name table */\r\n;   /* 64*16 bytes for EMS save status table (EMS_MAXSTATE) */\r\n\r\n\tmovzx eax, jemmini.MaxEMSPages\r\n\timul eax, 5\r\n\tadd eax,255*4+255*8+64*4\r\n\tadd ulcalc,eax\r\n\r\n\tmov eax,ulcalc\t;/* convert bytes back to K */\r\n\tadd eax,1023\r\n\tshr eax, 10\r\n\r\n\tadd eax,3\t;/* 4k page align */\r\n\tand al,0FCh\r\n\tmov ulcalc,eax\r\n\r\n\t.if bVerbose\r\n\t\tinvoke printf, CStr(\"%lu kB needed for VCPI and EMS handling\", LF), eax\r\n\t.endif\r\n\r\n\tmov eax, ulcalc\r\n\tadd kbneeded, eax\r\n\r\n\tmov eax,jemmini.MinMem16k\r\n\tshl eax, 4\r\n\tadd eax, kbneeded\r\n\tmov esi, eax\r\nif ?INTEGRATED eq 0\r\n\tinvoke XMSAllocMemory, xmsspec3, eax\r\n\t.if !ax\r\n\t\tinvoke printf, CStr(\"%s: can't allocate enough XMS memory(%lu kB)\", LF), offset szError, esi\r\n\t\tjmp $L569\t;exit with error\r\n\t.endif\r\n\tmov jemmini.XMSControlHandle, ax\r\n\r\n\tmov xmsreg._dx, ax\r\n\tinvoke xmscall, XMS_LOCKEMB\t;lock EMB (returns phys address in DX:BX)\r\n\t.if !ax\r\n\t\tinvoke printf, CStr(\"%s: can't lock XMS memory\", LF), offset szError\r\n\t\tinvoke xmscall, XMS_FREEEMB\r\n\t\tjmp $L569\t;exit with error\r\n\t.endif\r\n\r\nelse\r\n\tinvoke I15AllocMemory, 0, esi\r\n\t.if !ax\r\n\t\tinvoke printf, CStr(\"%s: can't allocate enough I15 memory(%lu kB)\", LF), offset szError, esi\r\n\t\tjmp $L569\t;exit with error\r\n\t.endif\r\n\tmov jemmini.XMSControlHandle, ax\r\nendif\r\n\tmov eax,jemmini.MinMem16k\r\n\t.if eax < dwMinOriginal\r\n\t\tshl eax, 4\r\n\t\tinvoke printf, CStr(\"%s: MIN has been reduced to %lu kB\", LF), offset szWarning, eax\r\n\t.endif\r\n\r\n\tmov eax, esi\r\n\tshl eax, 10\r\n\tmov dwAllocatedBytes, eax\r\n\r\nif ?INTEGRATED eq 0\r\n\r\n;--- xmsreg contains the locked physical address\r\n\tmov ax,xmsreg._dx\r\n\tshl eax,16\r\n\tmov ax,xmsreg._bx\r\n\tmov jemmini.MonitorStart+0,eax\r\n\t.if eax >= 1000000h\t\t;v5.80: if EMB is beyond the 16 MB barrier, display a warning\r\n\t\tinvoke printf, CStr(\"%s: address of allocated EMB (=%lX) is beyond 16MB\", LF), offset szWarning, eax\r\n\t.endif\r\nelse\r\n\tinvoke GetEMBBase, jemmini.XMSControlHandle\r\n\tmov jemmini.MonitorStart, eax\r\n if ?XMS35\r\n\t.if xms.smem_used == FALSE\r\n\t\tmov cs:xms_smax_noe820,0\t;disable INT15, ax=e820h check\r\n\t\tmov jemmini.xms_version,INTERFACE_VER_MAXSEXT0\t;make XMM report v3.0 interface\r\n\t.endif\r\n endif\r\nendif\r\nif 0\r\n\tmov ecx, xms.mem_free\r\n\tshl ecx, 10\t\t\t\t;convert kB to bytes\r\n\tadd ecx, jemmini.MonitorStart\r\n\tmov jemmini.MaxPhysMem, ecx\r\nelse\r\n\tmov eax, xms.mem_highest\r\n\tmov jemmini.MaxPhysMem, eax\r\nendif\r\n\r\n\tmov ax,1\r\n\t@dprintf ?INITRMDBG, <\"AllocAndInitMem: exit, MonitorStart=%lX, MaxPhysMem=%lX\",10>, jemmini.MonitorStart,?INITRMDBG, jemmini.MaxPhysMem\r\n\tret\r\n\r\nAllocAndInitMem ENDP\r\n\r\n;--- toupper(char) returns uppercase character\r\n\r\ntoupper PROC\r\n\tpop cx\r\n\tpop ax\r\n\tpush cx\r\n\tcmp al,'a'\r\n\tjb @F\r\n\tcmp al,'z'\r\n\tja @F\r\n\tsub al,20h\r\n@@:\r\n\tret\r\n\r\ntoupper ENDP\r\n\r\nNotInstalled proc\r\n\tMOV DX,CStr( NAMEGEN,\" is not installed. (Enter \",NAMEEXE,\" -? for help)\",CR,LF,'$' )\r\n\tMOV AH,9\r\n\tINT 21H\r\n\tret\r\nNotInstalled endp\r\n\r\n;--- check if there's an installed Jemm386/JemmEx monitor program\r\n\r\nIsJemmInstalled proc c\r\n\t@dprintf ?EMXRMDBG, <\"IsJemmInstalled enter\",10>\r\n\tmov dx, offset sig1\r\n\tmov ax, 3D00h\r\n\tint 21h\r\n\tjnc @F\r\n\tmov dx, offset sig2\r\n\tmov ax, 3D00h\r\n\tint 21h\r\n\tjc @@nojemm1\r\n@@:\r\n\t@dprintf ?EMXRMDBG, <\"EMM device found\",10>\r\n\tmov bx,ax\r\n\txor ax,ax\r\n\tpush ax\r\n\tpush ax\r\n\tpush ax\r\n\tmov cx,6\t\t;read 6 bytes\r\n\tmov dx,sp\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tpop ax\t\t\t;version\r\n\tpop cx\t\t\t;API entry offs\r\n\tpop cx\t\t\t;API entry segm\r\n\tjc @@nojemm2\r\n\tcmp ax,0028h\t;this is JEMM!\r\n\tjnz @@nojemm2\r\n\tmov ax,bx\t\t;return the file handle\r\n\t@dprintf ?EMXRMDBG, <\"Jemm found\",10>\r\n\tclc\r\n\tret\r\n@@nojemm2:\r\n\t@dprintf ?EMXRMDBG, <\"Jemm not found\",10>\r\n\tmov ah,3Eh\r\n\tint 21h\r\n@@nojemm1:\r\n\tstc\r\n\tret\r\nIsJemmInstalled endp\r\n\r\nif ?UNLOAD\r\n\r\n;--- try to unload Jemm386\r\n\r\nTryUnload proc c uses di si\r\n\r\nlocal handle:word\r\nlocal resparm[2]:word\r\nif UMB_MAX_BLOCKS le 8\r\nlocal buff[size UMBBLK * 8]:byte\r\nelse\r\nlocal buff[size UMBBLK * UMB_MAX_BLOCKS]:byte\r\nendif\r\n\r\n\tinvoke IsJemmInstalled\r\n\tjc @@nojemm\r\n\tmov bx, ax\t\t;EMMXXXX0 handle\r\n\tmov handle,ax\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, Jemm installed\",10>\r\n\tmov byte ptr resparm,EMMDEV_GETRES\t;get resident segment\r\n\tlea dx,resparm\r\n\tmov cx,sizeof resparm\t;returns 2 words\r\n\tmov ax,4402h\t;read ioctl (get monitor's resident segment/size)\r\n\tint 21h\r\n\tjc @@nojemm2\r\n\r\n\tinvoke printf, CStr(\"found Jemm instance at segment %x\", LF), resparm[0]\r\n\r\n;--- check if any INT hooked by jemm has been stolen\r\n\r\n\tinvoke CheckIntHooks, resparm[0]\r\n\tjc @@nouninst\r\n\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, no stolen ints detected\",10>\r\n\r\n;--- check if any UMB is allocated\r\n\r\n\tmov buff,EMMDEV_GETUMBS\t\t;call \"get umbs\" function\r\n\tlea dx, buff\r\n\tmov cx,UMB_MAX_BLOCKS * size UMBBLK\r\n\tmov bx,handle\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tjc @@nouninst\r\n\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, got UMBs from Jemm\",10>\r\n\tmov si,dx\r\n\txor dx,dx\r\n\tmov cx,UMB_MAX_BLOCKS\r\n@@:\r\n\tlodsw\t\t\t;segment\r\n\tlodsw\t\t\t;size + flag\r\n\tor dx,ax\r\n\tloop @B\r\n;\tmov sp,si\r\n\ttest dh,80h \t;any umb allocated?\r\n\tjnz @@nouninst\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, no allocated UMBs\",10>\r\n\r\n;-- close EMMXXXX0 handle\r\n\r\n\tmov ah,3Eh\r\n\tint 21h\r\n\r\nif ?INTEGRATED\r\n\r\n;-- get current XMS (must be Jemm386)\r\n\r\n\tinvoke XMSinit\r\n\tles si, xms.Driver\r\n\tcmp byte ptr es:[si],0EBh\t;anyone hooked into XMS?\r\n\tjnz @@nouninst\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, no XMS hookers detected\",10>\r\n\r\n;-- check if any XMS memory is in use\r\n\r\n\tpush ds\r\n\tlds si, jemmini.XMSHandleTable\r\n\tmov ax, ds\r\n\tor ax, si\r\n\tjz @@xmsused\r\n\tmov cx, [si].XMS_HANDLETABLE.xht_numhandles\r\n\tlds si, [si].XMS_HANDLETABLE.xht_pArray\r\n\tadd si, size XMS_HANDLE ;dont check first entry (is Jemm itself)\r\n\tdec cx\r\nnextitem:\r\n\tcmp [si].XMS_HANDLE.xh_flags, XMSF_USED\r\n\tjz @@xmsused\r\n\tadd si, size XMS_HANDLE\r\n\tloop nextitem\r\n@@xmsused:\r\n\tpop ds\r\n\tjz @@nouninst\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, no EMBs are used\",10>\r\nendif\r\n\r\n;-- check if any EMS/VCPI memory is in use\r\n\r\n\tmov ah,4Bh\t\t;get number of open handles (handle 0 is ok)\r\n\tint 67h\r\n\tcmp bx,01h\t\t;any handle > 0000 allocated?\r\n\tja @@nouninst\r\n\t@dprintf ?UNLRMDBG, <\"TryUnload, no EMS handles are used\",10>\r\n\r\n;-- todo: check VCPI\r\n\r\n;-- ok to unload.\r\n\r\n\tmov ax,resparm[0]\t\t;get current Emm386 segment \r\n\tcall UnloadJemm\t\t\t;this will destroy contents of si and di\r\n\tjc @@nouninst\r\n\r\nife ?INTEGRATED\r\n\tmov dx,ax\r\n\tmov ah, XMS_UNLOCKEMB\t;unlock block\r\n\tcall [xms.Driver]\r\n\tmov ah, XMS_FREEEMB\t\t;free block\r\n\tcall [xms.Driver]\r\nelse\r\n\tmovzx bx, al\r\n\tadd bx,bx\r\n\tmov ah,DISABLE_A20\r\n\tcall A20procs[bx]\r\nendif\r\n\tMOV DX, CStr( NAMEGEN,\" unloaded\",CR,LF,'$' )\r\n@@exit:\r\n\tMOV AH,9\r\n\tINT 21H\r\n@@exit2:\r\n\tret\r\n@@nojemm2:\r\n@@nojemm:\r\n\tcall NotInstalled\r\n\tjmp @@exit2\r\n@@nouninst:\r\n\tMOV DX, CStr( NAMEGEN,\" cannot be unloaded.\",CR,LF,'$' )\r\n\tjmp @@exit\r\nTryUnload endp\r\n\r\nendif\r\n\r\n;--- display status of a running Jemm386/JemmEx\r\n\r\nEmmStatus proc c uses di si\r\n\r\nlocal handle:word\r\nif UMB_MAX_BLOCKS le 8\r\nlocal buff[size UMBBLK * 8]:byte\r\nelse\r\nlocal buff[size UMBBLK * UMB_MAX_BLOCKS]:byte\r\nendif\r\n\r\n\tmov handle,-1\r\n\tinvoke IsJemmInstalled\r\n\tjc jemm_not_found\r\n\tmov handle,ax\r\n\tmov bx, ax\r\n\tlea dx, [buff]\t;get version\r\n\tmov byte ptr [buff],EMMDEV_VERSION\r\n\tmov cx,2\r\n\tmov ax,4402h\t;read ioctl (get version) [Emm386 compatible call]\r\n\tint 21h\r\n\tjc jemm_not_found\r\n\tmovzx ax, byte ptr [buff+0]\r\n\tmovzx cx, byte ptr [buff+1]\r\n\tinvoke printf, CStr( \"%j v%u.%02u installed.\", LF), ax, cx\r\n\r\n;--- get EMS, VCPI, UMB, VME, A20 infos\r\n\r\n\tlea dx, [buff]\r\n\tmov byte ptr [buff],EMMDEV_SYSVARS\r\n\tmov cx,sizeof EMX06\r\n\tmov bx, handle\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tjc done\r\n\t@dprintf ?EMXRMDBG, <\"EmmStatus: read ioctl(6) ok\",10>\r\n\r\n\tmov di, ax\r\n\tlea si, [buff]\r\n\tcmp [si].EMX06.e06_NoEMS, 0\r\n\tmov dx, offset szOff\r\n\tjnz @F\r\n\tmov dx, offset szOn\r\n@@:\r\n\tinvoke printf, CStr(\"EMS is %s\"), dx\r\n\r\n\tcmp [si].EMX06.e06_NoEMS,0\r\n\tjnz @@nodispframe\t\t\t;dont display FRAME status if no EMS\r\n\r\n\tmov ah,42h\r\n\tint 67h\r\n\tmov ax, dx\r\n\tsub ax, bx\r\n\tinvoke printf, CStr(\", %u of max. %u pages allocated\"), ax, dx\r\n\r\n\tmov ax, CStr(\", no Page Frame\")\r\n\tmov cx, [si].EMX06.e06_Frame\r\n\tjcxz @F\r\n\tmov ax, CStr(\", Page Frame at %04X\")\r\n@@:\r\n\tinvoke printf, ax, cx\r\n@@nodispframe:\r\n\tinvoke printf, CStr(\".\", LF)\r\n\r\n\tcmp [si].EMX06.e06_NoVCPI, 0\t;_NoVCPI flag\r\n\tjnz @F\r\n\tinvoke printf, CStr(\"VCPI is On, %lu of max. %lu pages allocated.\", LF), [si].EMX06.e06_VCPIUsed, [si].EMX06.e06_VCPITotal\r\n\tjmp @@vcpidone\r\n@@:\r\n\tinvoke printf, CStr(\"VCPI is Off.\", LF)\r\n@@vcpidone:\r\n\r\n\tmov ax, 64\t\t;default DMA buffer size\r\n\tcmp di, 16\t\t;could the DMA buffer size be read?\r\n\tjb @F\r\n\tmov ax, [si].EMX06.e06_DMASize\r\n@@:\r\n\tmov ecx, [si].EMX06.e06_DMABuff\r\n\tinvoke printf, CStr(\"DMA buffer at %08lX, size %u kB.\", LF), ecx, ax\r\n\r\nif ?A20PORTS or ?A20XMS\r\n\tmov ax, offset szOff\r\n\tcmp [si].EMX06.e06_NoA20, 0\r\n\tjnz @F\r\n\tmov ax, offset szOn\r\n@@:\r\n\tinvoke printf, CStr(\"A20 emulation is %s.\", LF), ax\r\nendif\r\n\r\nif ?VME\r\n\tmov ax, offset szOff\r\n\tcmp [si].EMX06.e06_NoVME, 0\r\n\tjnz @F\r\n\tmov ax, offset szOn\r\n@@:\r\n\tinvoke printf, CStr(\"VME is %s.\", LF), ax\r\nendif\r\n\r\nif ?PGE\r\n\tmov ax, offset szOff\r\n\tcmp [si].EMX06.e06_NoPGE, 0\r\n\tjnz @F\r\n\tmov ax, offset szOn\r\n@@:\r\n\tinvoke printf, CStr(\"PGE is %s.\", LF), ax\r\nendif\r\n\r\n\tlea dx, [buff]\r\n\tmov byte ptr [buff],EMMDEV_GETUMBS\r\n\tmov cx, UMB_MAX_BLOCKS * sizeof UMBBLK\r\n\tmov bx, handle\r\n\tmov ax,4402h\t;read ioctl\r\n\tint 21h\r\n\tjc done\r\n\t@dprintf ?EMXRMDBG, <\"EmmStatus: read ioctl(7) ok\",10>\r\n\r\n\tlea si,[buff]\r\n\tmov cx, UMB_MAX_BLOCKS\r\n\t.repeat\r\n\t\tmov ax, [si].UMBBLK.wSegm\r\n\t\t.break .if !ax\r\n\t\tpush cx\r\n\t\tmov dx, [si].UMBBLK.wSize\r\n\t\tmov cx, CStr(\"allocated\")\r\n\t\ttest dh,80h\r\n\t\tjnz @F\r\n\t\tmov cx, CStr(\"free\")\r\n@@:\r\n\t\tand dh, 7Fh ;reset highest flag\r\n\t\tadd dx, ax\r\n\t\tdec dx\r\n\t\tinvoke printf, CStr(\"UMB supplied at %04X-%04X, %s.\", LF), ax, dx, cx\r\n\t\tpop cx\r\n\t\tadd si, size UMBBLK\r\n\t.untilcxz\r\n\r\ndone:\r\n\tmov bx, handle\r\n\tcmp bx, -1\r\n\tjz @F\r\n\tmov ah, 3Eh\r\n\tint 21h\r\n@@:\r\n\tret\r\njemm_not_found:\r\n\t@dprintf ?EMXRMDBG, <\"EmmStatus: Jemm not found\",10>\r\n\tcall NotInstalled\r\n\tjmp done\r\nEmmStatus endp\r\n\r\n;--- pass options to an already installed Jemm386/JemmEx monitor\r\n\r\nCMD15 struct\r\ncmd db ?\t;=EMMDEV_UPDATE\r\n\tEMX15W <>\r\nCMD15 ends\r\n\r\nEmmUpdate proc c uses di\r\n\r\nlocal buff:CMD15\r\n\r\n\t@dprintf ?EMXRMDBG, <\"EmmUpdate enter\",10>\r\n\txor di,di\r\n\tinvoke IsJemmInstalled\r\n\tjnc @F\r\n\tcall NotInstalled\r\n\tjmp @@exit\r\n@@:\r\n\tmov bx, ax       ;save file handle\r\n\r\n;--- fill structure to be send to running Jemm386/JemmEx\r\n\r\n\tmov buff.cmd,EMMDEV_UPDATE\r\n\r\nif ?VME\r\n\tmov al, [jemmini.NoVME]\r\nelse\r\n\tmov al,-1\r\nendif\r\n\tmov buff.e15_bVME,al\r\nif ?A20PORTS or ?A20XMS\r\n\tmov al, [jemmini.NoA20]\r\nelse\r\n\tmov al,-1\r\nendif\r\n\tmov buff.e15_bA20,al\r\n\tmov al, [jemmini.NoVCPI]\r\n\tmov buff.e15_bVCPI,al\r\nif ?PGE\r\n\tmov al, [jemmini.NoPGE]\r\nelse\r\n\tmov al,-1\r\nendif\r\n\tmov buff.e15_bPGE,al\r\n\tmov cx,sizeof CMD15\r\n\tlea dx,[buff]\r\n\tmov ax,4403h\t;write ioctl\r\n\tint 21h\r\n\tjc @F\r\n\t@dprintf ?EMXRMDBG, <\"EmmUpdate: write ioctl(15) ok\",10>\r\n\tinc di\r\n@@:\r\n\tmov ah,3Eh\t;close EMMXXXX0 handle\r\n\tint 21h\r\n@@exit:\r\n\t@dprintf ?EMXRMDBG, <\"EmmUpdate exit\",10>\r\n\tmov ax,di\r\n\tret\r\nEmmUpdate endp\r\n\r\n;--- cpu is in protected-mode, check EMM status\r\n\r\nIsEmmInstalled proc c\r\n\tinvoke IsJemmInstalled\r\n\tmov dx, CStr( NAMEGEN,' already installed',CR,LF,'$' )\r\n\tjnc @F\r\n\tcall EmmInstallcheck\r\n\tjc exit\r\n\tmov dx, CStr( \"CPU in protected mode, loading aborted\",CR,LF,07,'$' )\r\n@@:\r\n\tmov ah,9\r\n\tint 21h\r\nexit:\r\n\tret\r\nIsEmmInstalled endp\r\n\r\n;--- GetValue()\r\n;--- converts a string into a DWORD\r\n;--- out: NC -> value in EAX\r\n;---      C -> no valid digit found\r\n\r\nGetValue PROC stdcall uses esi di cmdline:ptr BYTE, base:WORD, usesuffix:WORD\r\n\r\n\txor esi, esi\t\t;result\r\n\tmov bx, cmdline\r\n@@nextitem:\r\n\tmov al,BYTE PTR [bx]\r\n\tpush ax\r\n\tcall toupper\r\n\tmov ah,al\r\n\tsub al,'0'\r\n\tjc @@FB316\r\n\tcmp al,9\r\n\tjbe @@I318\r\n\tsub al,7\r\n\tcmp al,0Ah\r\n\tjb @@FB316\r\n\tcmp al,0Fh\r\n\tja @@FB316\r\n@@I318:\r\n\tmovzx ecx, base\r\n\tcmp cl,al\r\n\tjle @@FB316\r\n\txchg eax,esi\r\n\tmul ecx\r\n\txchg eax,esi\r\n\tmovzx eax,al\r\n\tadd esi,eax\r\n\tinc bx\r\n\tjmp @@nextitem\r\n\r\n@@FB316:\r\n\tcmp bx,cmdline\r\n\tjz nodigit\r\n\tcmp BYTE PTR usesuffix, 0\r\n\tje @@I322\r\n\tmov al,ah\r\n\tcmp al,'M'\r\n\tje @@SC328\r\n\tja @@I322\r\n\tsub al,'G'\r\n\tje @@SC327\r\n\tsub al,4\r\n\tje @@SC329 \t;'K'?\r\n\tjmp @@I322\r\n@@SC327:\r\n\tshl esi,10\r\n@@SC328:\r\n\tshl esi,10\r\n@@SC329:\r\n\tinc bx\r\n@@I322:\r\n\tpush esi\r\n;--- remove string from cmdline\r\n\tmov si,bx\r\n\tmov di, cmdline\r\n@@:\r\n\tlodsb\r\n\tstosb\r\n\tand al,al\r\n\tjnz @B\r\n\tpop eax\t\t;result in EAX\r\nexit:\r\n\tret\r\nnodigit:\r\n\txor eax,eax\r\n\tstc\r\n\tjmp exit\r\n\r\nGetValue ENDP\r\n\r\n;--- FindCommand(searchstring) parses the command line\r\n;--- for a specific command. If found, the command is removed.\r\n;--- in: SI=cmdline\r\n;--- out: AX ->behind the found string\r\n;---      AX = 0 if the string wasn't found\r\n\r\nFindCommand PROC stdcall uses di si searchstring:ptr BYTE\r\n\r\n\tmov di, searchstring\r\n\tmov bx,di\r\n\t.while byte ptr [di]\r\n\t\tinc di\r\n\t.endw\r\n\tsub di,bx\r\n\txchg bx,di\r\n;\tpush ds\r\n;\tpop es\r\nnextcmp:\r\n\txor ax,ax\r\n\tlodsb\r\n\tand al,al\r\n\tje done\r\n\tcmp al,' '\r\n\tjbe nextcmp   ;skip white spaces\r\n\tdec si\r\n\tinvoke _memicmp, si, di, bx   ;assumes that BX remains unchanged!\r\n\tor ax,ax\r\n\tje found\r\n@@:\r\n\tlodsb\r\n\tcmp al,' '  ;skip the word in cmdline\r\n\tja @B\r\n\tdec si\r\n\tjmp nextcmp\r\nfound:\r\n;--- remove found string in cmdline\r\n\tpush si\r\n\tmov di,si\r\n\tadd si,bx\r\n@@:\r\n\tlodsb\r\n\tstosb\r\n\tand al,al\r\n\tjnz @B\r\n\tpop ax\r\ndone:\r\n\tret\r\n\r\nFindCommand ENDP\r\n\r\n;--- VMware detection\r\n;--- questionable, because Qemu \"responds\" similar to VMware\r\n\r\nVMwareDetect proc c\r\n\r\n\tmov eax, 564D5868h\t;magic number (=\"VMXh\")\r\n\tmov cx, 000ah\t\t;command number (000A=get VMware version)\r\n\txor ebx,ebx \t\t;command specific parameter\r\n\tmov dx, 5658h\t\t;VMware IO port (=\"VX\")\r\n\tin eax,dx\t\t\t;\"returns\" version number in EAX\r\n\tcmp ebx, 564D5868h\t;and magic number in EBX (=\"VMXh\")\r\n\tsetz al\r\n\tmov ah,0\r\n\tret\r\n\r\nVMwareDetect endp\r\n\r\n;--- Display current status of (conventional) memory map\r\n;--- modifies SI, DI!\r\n\r\nDisplayMemoryMap proc\r\n\tinvoke printf, CStr(\"Memory Map: R/S=system ram, G=graphics, E=rom/rsvd, P=page frame, U/I=umb\", LF)\r\n\tsub sp,4*16+4\r\n\tmov si,offset SystemMemory\r\n\t.repeat\r\n\t\tmov cx,si\r\n\t\tsub cx,offset SystemMemory\r\n\t\tshr cx,4\r\n\t\tmov al,' '\r\n\t\tmov di,sp\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tstosb\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tstosb\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tstosb\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmovsd\r\n\t\tmov al,0\r\n\t\tstosb\r\n\t\tinvoke printf, CStr(\"%X0000: %s\", LF), cx, sp\r\n\t.until si >= offset SystemMemory + sizeof SystemMemory\r\n\tadd sp,4*16+4\r\n\tret\r\nDisplayMemoryMap endp\r\n\r\ncprintf PROC c pszText:ptr byte\r\n\r\n\t.if bVerbose\r\n\t\tinvoke printf, pszText\r\n\t.endif\r\n\tret\r\n\r\ncprintf ENDP\r\n\r\nIsProtectedMode proc c\r\n\tSMSW AX\r\n\tAND AX,0001H\t; PE-Bit (Protect Enable) set ?\r\n\tret\r\nIsProtectedMode ENDP\r\n\r\nIsDPMI proc c uses es si di\r\n\tmov ax,1687h\r\n\tint 2Fh\r\n\tand ax,ax\r\n\tsetz al\r\n\tcbw\r\n\tret\r\nIsDPMI ENDP\r\n\r\nif ?INITRMDBG\r\n\r\n;-- display XBDA size in kB\r\n\r\ndisp_xbda_size proc uses es bx\r\n\tpush 0\r\n\tpop es\r\n\tmov bx,es:[@XBDA]\r\n\tmov es,bx\r\n\tmovzx ax,byte ptr es:[0]\r\n\t@dprintf ?INITRMDBG, <\"XBDA size=%X\",10>, ax\r\n\tret\r\ndisp_xbda_size endp\r\nendif\r\n\r\nif ?INTEGRATED\r\n if ?XMS35\r\nhascpuid proc\r\n\tpush di\r\n\tmov di,sp\r\n\tand sp,0fffch\t;make sure we don't get an exc 11 (if AM set in CR0)\r\n\tpushfd\t\t\t; save EFlags\r\n\tcli\r\n\tpushd 240000h\t; set AC bit in eflags, reset IF\r\n\tpopfd\t\t\t; pop extended flags\r\n\tpushfd\t\t\t; push extended flags\r\n\tpop ax\r\n\tpop ax\t\t\t; get HiWord(EFlags) into AX\r\n\tpopfd\t\t\t; restore EFlags\r\n\tmov sp,di\r\n\tpop di\r\n\ttest al,04\t;AC bit set?\r\n\tje @F\r\n\ttest al,20h\t;CPUID bit set?\r\n\tjz @F\r\n\tclc\r\n\tret\r\n@@:\r\n\tstc\r\n\tret\r\nhascpuid endp\r\n endif\r\nendif\r\n\r\n;--- mainex: scan cmdline, then either load/unload or update\r\n;---         an already installed Jemm.\r\n;--- DS=ES=SS=dgroup\r\n\r\nmainex PROC c public uses di si mode:word, cmdline:ptr BYTE\r\n\r\nlocal rangestart:word\r\nlocal rangestop:word\r\nlocal found:word\r\nlocal wChkVMWare:word\r\nlocal memtype:byte\r\nlocal bHelp:byte\r\nif ?LOAD\r\nlocal bLoad:byte\r\nendif\r\n\r\n\t.data\r\n\teven\r\n\r\ncmdopts0 label word\r\nif ?UNLOAD\r\n\tdw CStr(\"UNLOAD\"), offset _unload\r\nendif\r\nif ?LOAD\r\n\tdw CStr(\"LOAD\"), offset _load\r\nendif\r\n\tdw CStr(\"/?\"), offset sethelp\r\n\tdw CStr(\"-?\"), offset sethelp\r\n\tdw CStr(\"/H\"), offset sethelp\r\n\tdw CStr(\"-H\"), offset sethelp\r\nendcmdopts0 label word\r\n\r\ncmdopts1 label word\r\n\tdw CStr(\"NOVCPI\"), offset setnovcpi\r\n\tdw CStr(\"VCPI\"),   offset setvcpi\r\nif ?INTEGRATED\r\n;--- FindCommand() requires that A20METHOD is *before* A20 option!\r\n;--- that's a bit unfortunate, since A20METHOD doesn't belong to cmdopts1.\r\n\tdw CStr(\"A20METHOD:\"), offset seta20method\r\nendif\r\nif ?A20PORTS or ?A20XMS\r\n\tdw CStr(\"NOA20\"),  offset setnoa20\r\n\tdw CStr(\"A20\"),    offset seta20\r\nendif\r\nif ?VME\r\n\tdw CStr(\"NOVME\"),  offset setnovme\r\n\tdw CStr(\"VME\"),    offset setvme\r\nendif\r\nif ?PGE\r\n\tdw CStr(\"NOPGE\"),  offset setnopge\r\n\tdw CStr(\"PGE\"),    offset setpge\r\nendif\r\nendcmdopts1 label word\r\n\r\ncmdopts2 label word\r\nif ?V86EXC0D\r\n\tdw CStr(\"V86EXC0D\"), offset setv86exc0d\r\nendif\r\nif ?INTEGRATED\r\n\tdw CStr(\"HMAMIN=\"), offset sethmamin\r\n\tdw CStr(\"MAXEXT=\"), offset setmaxext\r\n if ?XMS35\r\n\tdw CStr(\"MAXSEXT=\"), offset setmaxsext\r\n endif\r\n\tdw CStr(\"NOE801\"),  offset setnoe801\r\n\tdw CStr(\"NOE820\"),  offset setnoe820\r\n\tdw CStr(\"XMSHANDLES=\"), offset setxmshandles\r\n\tdw CStr(\"X2MAX=\"),  offset setx2max\r\nendif\r\n\tdw CStr(\"VERBOSE\"), offset setverbose\r\n\tdw CStr(\"/V\"),     offset setverbose\r\n\tdw CStr(\"NODYN\"),  offset setnodyn\r\n\tdw CStr(\"NOINVLPG\"), offset setnoinvlpg\r\n\tdw CStr(\"MIN=\"),   offset setmin\r\n\tdw CStr(\"MAX=\"),   offset setmax\r\n\tdw CStr(\"NOEMS\"),  offset setnoems\r\n\tdw CStr(\"NOVDS\"),  offset setnovds\r\n\tdw CStr(\"VDS\"),    offset setvds\r\n\tdw CStr(\"FRAME=NONE\"), offset setnoframe\r\n\tdw CStr(\"/P\"),     offset setframe\r\n\tdw CStr(\"FRAME=\"), offset setframe\r\n\tdw CStr(\"X=TEST\"), offset setxeqtest\r\n\tdw CStr(\"I=TEST\"), offset setieqtest\r\nif ?SB\r\n\tdw CStr(\"SB\"),     offset setsb\r\nendif\r\nif ?EMX\r\n\tdw CStr(\"EMX\"),    offset setemx\r\nendif\r\nif ?SPLIT\r\n\tdw CStr(\"SPLIT\"),  offset setsplit\r\nendif\r\nif ?NOVMWARE\r\n\tdw CStr(\"NOVMW\"),  offset setnovmw\r\nendif\r\nif ?MOVEXBDA\r\n\tdw CStr(\"MOVEXBDA\"), offset setmovexbda\r\nendif\r\n\tdw CStr(\"NOCHECK\"), offset setnocheck\r\n\tdw CStr(\"ALTBOOT\"), offset setaltboot\r\n\tdw CStr(\"NOHI\"),    offset setnohi\r\n;--- NOMOVEXBDA is a no-op, but helps MS EMM386 switch compatibility\r\n\tdw CStr(\"NOMOVEXBDA\"), offset _ret\r\n\tdw CStr(\"NORAM\"),   offset setnoram\r\n\tdw CStr(\"RAM\"),     offset setram\r\n\tdw CStr(\"D=\"),      offset setdma\r\n\tdw CStr(\"B=\"),      offset setborder\r\nendcmdopts2 label word\r\n\r\n\t.code\r\n\r\n\t@dprintf ?INITRMDBG, <\"mainex enter\",10>\r\n\tcld\r\n\tmov bHelp, FALSE\r\n\tmov wChkVMWare, offset checkvmware\r\n\r\n\tmov si,cmdline\r\n\t.if mode != EXECMODE_EXE\r\n\t\tmov bLoad, TRUE\r\n\t\tinvoke printf, addr szStartup\r\nif ?FASTBOOT\r\n\t\tinvoke FindCommand, CStr(\"FASTBOOT\")\r\n\t\t.if ax\r\n\t\t\tor jemmini.V86Flags, V86F_FASTBOOT\r\n\t\t.endif\r\nendif\r\n\t.else\r\n\t\tmov bLoad, FALSE\r\n\t\tmov di, offset cmdopts0\r\n\t\t.repeat\r\n\t\t\tinvoke FindCommand, [di+0]\r\n\t\t\t.if ax\r\n\t\t\t\tcall word ptr [di+2]\r\n\t\t\t.endif\r\n\t\t\tadd di, 2+2\r\n\t\t.until di == offset endcmdopts0\r\n\t.endif\r\n\r\n;--- in case jemm isn't to be loaded, reset the dynamic options to \"undefined\"\r\n\t.if bLoad == FALSE\r\n\t\tmov jemmini.dwDynSet, -1\r\n\t.endif\r\n\r\n;--- scan the \"dynamic\" options ( may be set if jemm is already loaded)\r\n\tmov di, offset cmdopts1\r\n\t.repeat\r\n\t\tinvoke FindCommand, [di+0]\r\n\t\t.if ax\r\n\t\t\tcall word ptr [di+2]\r\n\t\t.endif\r\n\t\tadd di, 2+2\r\n\t.until di == offset endcmdopts1\r\n\r\n\tcmp bLoad, FALSE\r\n\tje $I395\r\n\r\n;--- initialize conventional memory map\r\n;--- 0000-9FFF: R(am)\r\n;--- A000-BFFF: G(raphics)\r\n;--- C000-EFFF: U(MB)\r\n;--- F000-FFFF: E(PROM)\r\n\r\nif ?USEMEMSET\r\n\tinvoke memset, addr SystemMemory+000h, 'R', 160\r\n\tinvoke memset, addr SystemMemory+0A0h, 'G', 32\r\n\tinvoke memset, addr SystemMemory+0C0h, 'U', 48\r\n\tinvoke memset, addr SystemMemory+0F0h, 'E', 16\r\nelse\r\n\tmov di, offset SystemMemory\r\n\tmov cx,160\t;160*4 = 10*64 = 0000-9FFF\r\n\tmov al,'R'\t;RAM\r\n\trep stosb\r\n\tmov al,'G'\t;graphics\r\n\tmov cl,32\t;A000-BFFF\r\n\trep stosb\r\n\tmov al,'U'\t;possibly UMB\r\n\tmov cl,48\t;C000-EFFF\r\n\trep stosb\r\n\tmov al,'E'\t;ROM\r\n\tmov cl,16\t;F000-FFFF\r\n\trep stosb\r\nendif\r\n\r\nif ?INTEGRATED\r\n if ?XMS35\r\n\tcall hascpuid\r\n\tjc @F\r\n\txor eax,eax\r\n\tinc eax\r\n\t.586\r\n\tcpuid\r\n\t.386\r\n\tbt edx,CPUID1_PSE36\r\n\tjnc @F\r\n\tmov byte ptr xms.maxhigh,0; allow memory up to 000000ff.ffffffffh   \r\n@@:\r\n endif\r\nendif\r\n\r\n\tmov di, offset cmdopts2\r\n\t.repeat\r\n\t\tinvoke FindCommand, [di+0]\r\n\t\t.if ax\r\n\t\t\tcall word ptr [di+2]\r\n\t\t.endif\r\n\t\tadd di, 2+2\r\n\t.until di == offset endcmdopts2\r\n\r\n\tinvoke I15CheckRsvdRegion\r\n\r\n;--- get the \"range\" options: I|S|X=bbbb-eeee\r\n\t.while 1\r\n\t\tmov memtype,'I'\r\n\t\tinvoke FindCommand, CStr(\"I=\")\r\n\t\t.if !ax\r\n\t\t\tmov memtype,'S'\r\n\t\t\tinvoke FindCommand, CStr(\"S=\")\r\n\t\t\t.if !ax\r\n\t\t\t\tmov memtype,'X'\r\n\t\t\t\tinvoke FindCommand, CStr(\"X=\")\r\n\t\t\t\t.break .if !ax\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t\tmov di,ax\r\n\t\tinvoke GetValue, ax, 16, FALSE\t;get start of range\r\n\t\tjc range_invalid\r\n\t\tmov rangestart,ax\r\n\t\t.if BYTE PTR [di] == '-'\r\n\t\t\tmov BYTE PTR [di],' '\r\n\t\t\tinc di\r\n\t\t\tinvoke GetValue, di, 16, FALSE\r\n\t\t\tjc range_invalid\r\n\t\t\tmov rangestop, ax\r\n\t\t\tmovzx cx, memtype\r\n\t\t\tmov dx, rangestart\r\n\t\t\t.if dx < 0A000h || dx >= ax\r\n\t\t\t\tinvoke printf, CStr(\"Rejected %c=%x..%x\", LF), cx, dx, ax\r\n\t\t\t.else\r\n\t\t\t\t.if bVerbose\r\n\t\t\t\t\tinvoke printf, CStr(\"Accepted %c=%x..%x\", LF), cx, dx, ax\r\n\t\t\t\t.endif\r\n\t\t\t\tmov di, rangestart\r\n\t\t\t\t.while di < rangestop\r\n\t\t\t\t\tinvoke SetMemoryType, di, memtype\r\n\t\t\t\t\tinc di\r\n\t\t\t\t.endw\r\n\t\t\t.endif\r\n\t\t.else\r\nrange_invalid:\r\n\t\t\tinvoke printf, CStr(\"%s: option '%c=' rejected, invalid syntax\", LF), offset szWarning, memtype\r\n\t\t.endif\r\n\t.endw\r\n\r\n$I395:\r\n\tinvoke skipWhite, si\r\n\tmov si,ax\r\n\tcmp BYTE PTR [si],'0'\r\n\tjb @F\r\n\tcmp BYTE PTR [si],'9'\r\n\tja @F\r\n\tinvoke GetValue, ax, 10, TRUE\r\n\tshr eax,4\r\n\tmov jemmini.MaxMem16k,eax\r\n\tinvoke skipWhite, si\r\n\tmov si,ax\r\n@@:\r\n\t.if BYTE PTR [si]\r\n\t\tinvoke printf, CStr(\"%s: ignored commandline '%s'\", LF), offset szWarning, si\r\n\t\tcmp mode,EXECMODE_EXE\r\n\t\tje exit1\r\n\t.endif\r\n\r\n\t.if bLoad == FALSE\r\n\t\tinvoke printf, addr szStartup\r\n\t\t.if bHelp\r\n\t\t\tinvoke printf, addr szCopyRight\r\n\t\t\tinvoke printf, addr szHelp\r\n\t\t.else\r\n\t\t\t.if jemmini.dwDynSet != -1\r\n\t\t\t\tinvoke EmmUpdate\r\n\t\t\t\t.if ax\r\n\t\t\t\t\tinvoke printf, CStr(\"option(s) passed to installed instance of %j\", LF)\r\n\t\t\t\t.endif\r\n\t\t\t.else\r\n\t\t\t\tinvoke EmmStatus\r\n\t\t\t.endif\r\n\t\t.endif\r\n\t\tjmp exit1\r\n\t.endif\r\n\r\n; /******* options set, now process **********/\r\n\r\n\tinvoke IsProtectedMode\r\n\t.if ax\r\n\t\tinvoke IsEmmInstalled\r\n\t\tjmp exit1\r\n\t.endif\r\n\tinvoke XMSinit\r\n\r\nif ?INTEGRATED eq 0\r\n\t.if !ax\r\n\t\tinvoke printf, CStr(\"%s: no XMM found, required\", LF), offset szError\r\n\t\tjmp exit1\r\n\t.endif\r\nelse\r\n\t.if ax\r\n\t\tinvoke printf, CStr(\"%s: XMM already installed\", LF), offset szError\r\n\t\tjmp exit1\r\n\t.endif\r\nendif\r\n\r\n\tinvoke IsDPMI\r\n\t.if ax\r\n\t\tinvoke printf, CStr(\"%s: DPMI host detected\", LF), offset szError\r\n\t\tjmp exit1\r\n\t.endif\r\n\r\nif ?INTEGRATED\r\n\tinvoke InitA20\r\n\t.if CARRY?\r\n\t\tinvoke printf, CStr(\"%s: No supported A20 method detected\", LF), offset szError\r\n\t\tjmp exit1\r\n\t.endif\r\n\tmov jemmini.A20Method,al\r\n\t.if bVerbose\r\n\t\tmovzx bx, al\r\n\t\tadd bx, bx\r\n\t\tinvoke printf, CStr(\"'%s' A20 method selected\", LF), A20strings[bx]\r\n\t.endif\r\n\tmovzx bx,jemmini.A20Method\r\n\tadd bx,bx\r\n\tmov ah,ENABLE_A20\r\n\tcall A20procs[bx]\r\nelse\r\n\tinvoke xmscall, XMS_GETVERSION\r\n\t.if ax && xmsreg._ax >= 0300h\r\n\t\tmov xmsspec3, TRUE\r\n\t.endif\r\n\tinvoke xmscall, XMS_ENABLEA20\r\n\t.if !ax\r\n\t\tinvoke printf, CStr(\"%s: enable A20 failed\", LF), offset szError\r\n\t\tjmp exit1\r\n\t.endif\r\nendif\r\n\r\n\t.if jemmini.MaxMem16k == -1\r\n\t\tmov jemmini.MaxMem16k, MAXMEM16K_DEFAULT\r\n\t.endif\r\n\t.if jemmini.NoVCPI\r\n\t\tinvoke cprintf, CStr(\"VCPI disabled\", LF)\r\n\t.endif\r\n\t.if jemmini.NoVDS\r\n\t\tinvoke cprintf, CStr(\"VDS disabled\", LF)\r\n\t.endif\r\nif ?INTEGRATED eq 0\r\n;    /* if no int 2fh, function 4309h support, disable pool sharing */\r\n\t.if jemmini.XMSHandleTable == 0 && jemmini.NoPool == FALSE\r\n\t\tmov jemmini.NoPool, TRUE\r\n\t\tinvoke printf, CStr(\"%s: XMS host doesn't provide handle array, dynamic memory allocation off!\", LF), offset szWarning\r\n\t.endif\r\nendif\r\n\r\n\tinvoke ScanSystemMemory\t;/* build up system memory map */\r\n\r\n\t.if  jemmini.NoEMS\r\n\t\tmov jemmini.NoFrame, TRUE\r\n\t.endif\r\n\t.if jemmini.NoFrame == FALSE\r\n\t\tinvoke LocatePageFrame\r\n\t\tmov jemmini.Frame, ax\r\n\t.endif\r\n\r\n; allocate from XMS the memory we need\r\n; this is memory for UMBs, including FF00\r\n;\r\n;  + 20kB for the monitor code, GDT, IDT, stack\r\n;  + 12kB for page tables\r\n;  + 12kB for TSS + IO-Bitmap (includes 3 kB reserve for rounding)\r\n;  +  4kB for mapping FF000 page\r\n;  + 64kB +-X for DMA buffering\r\n;  + room for other control structures made inside function\r\n;                               \r\n;  + what the user wants for EMS\r\n\r\nMONITORMIN equ 20+12+12+4\r\n\r\n\tinvoke UMBpageswanted\r\n\tshl ax,2\r\n\tmov si,ax\r\n\r\n\t.if bVerbose\r\n\t\tinvoke printf, CStr(\"Needed: %u kB for monitor, %u kB for UMBs, %u kB for DMA buffer\", LF), MONITORMIN, ax, jemmini.DMABufferSize\r\n\t.endif\r\n\r\n\t.if jemmini.NoEMS\r\n\t\tmov ax, 512\r\n\t.else\r\n\t\tmov ax, EMS_MAX_PAGES_ALLOWED\r\n\t.endif\r\n\tmov jemmini.MaxEMSPages,ax\r\n\r\n\tadd si,jemmini.DMABufferSize\r\n\tadd si, MONITORMIN\r\n\tmovzx esi,si\r\n\tinvoke AllocAndInitMem, esi\r\n\t.if ax == 0\r\nif ?INTEGRATED\r\n\t\tmovzx bx,jemmini.A20Method\r\n\t\tadd bx,bx\r\n\t\tmov ah,DISABLE_A20\r\n\t\tcall A20procs[bx]\r\nelse\r\n\t\tinvoke xmscall, XMS_DISABLEA20 ; local disable A20\r\nendif\r\n\t\tjmp exit1\r\n\t.endif\r\n\r\n\tmov eax, dwAllocatedBytes\r\n\tadd eax, jemmini.MonitorStart\r\n\tmov jemmini.MonitorEnd,eax\r\n\r\n\t.if bVerbose\r\n\t\tinvoke printf, CStr(\"XMS memory block for monitor: %lx-%lx, XMS highest=%lx\", LF), jemmini.MonitorStart, eax, jemmini.MaxPhysMem\r\n\t\tinvoke DisplayMemoryMap\r\n\t.endif\r\n\r\n\tmov ax, ds\r\n\tmovzx eax, ax\r\n\tshl eax, 4\r\n\tlea eax, [eax+offset SystemMemory]\r\n\tmov jemmini.PageMap, eax\r\n\r\nif ?INITRMDBG\r\n\tcall disp_xbda_size\r\n if 0  ;this enabled a debug watchpoint (write) for first byte of XBDA\r\n\tpush 0\r\n\tpop es\r\n\tmovzx eax,word ptr es:[@XBDA]\r\n\tshl eax,4\r\n\tmov dr0,eax\t;set a watchpoint\r\n\tmov eax,00010002h   ;set Write global watchpoint\r\n\tmov dr7,eax\r\n endif\r\nendif\r\n\r\n\tmov si, mode\r\n\tinvoke InitJemm\r\n\t.if bVerbose\r\n\t\tinvoke printf, CStr(\"Physical start address of EMS pages: %lX\", LF), eax\r\n\t\tmov ah,42h\r\n\t\tint 67h\r\n\t\t.if ah == 0 \r\n\t\t\tmovzx eax,bx\r\n\t\t\tmovzx ecx,dx\r\n\t\t\tshl eax,4\r\n\t\t\tshl ecx,4\r\n\t\t\tinvoke printf, CStr(\"Total/available EMS memory: %d/%d pages (= %lu/%lu kB)\", LF), dx, bx, ecx, eax\r\n\t\t.endif\r\n\t.endif\r\n\tinvoke printf, CStr( \"%j loaded\", LF)\r\nif ?INTEGRATED eq 0\r\n\tinvoke xmscall, XMS_DISABLEA20\t;local disable A20\r\nendif\r\n\txor ax,ax\r\n$EX338:\r\n\t@dprintf ?INITRMDBG, <\"mainex: exit, ax=%X [bp]=%X %X\",10>, ax, [bp+0], [bp+2]\r\n\tret\r\nexit1:\r\n\tmov ax,1\r\n\tjmp $EX338\r\n\r\nif ?LOAD\r\n_load:\r\n\tmov bLoad, TRUE\r\n\tretn\r\nendif\r\nif ?UNLOAD\r\n_unload:\r\n\tpop ax\r\n\tinvoke XMSinit\r\n\tinvoke TryUnload\r\n\tjmp exit1\r\nendif\r\nsethelp:\r\n\tmov bHelp, TRUE\r\n\tretn\r\nsetnovcpi:\r\n\tmov jemmini.NoVCPI,TRUE\r\n\tretn\r\nsetvcpi:\r\n\tmov jemmini.NoVCPI,FALSE\r\n\tretn\r\nif ?INTEGRATED\r\nseta20method:\r\n\tinvoke GetA20Method, ax\r\n\tjc @F\r\n\tmov jemmini.A20Method, al\r\n\tretn\r\n@@:\r\n\tinvoke printf, CStr(\"%s: unknown A20 method\", LF), offset szWarning\r\n\tretn\r\nendif\r\nif ?A20PORTS or ?A20XMS\r\nsetnoa20:\r\n\tmov jemmini.NoA20,TRUE\r\n\tretn\r\nseta20:\r\n\tmov jemmini.NoA20,FALSE\r\n\tretn\r\nendif\r\nif ?VME\r\nsetnovme:\r\n\tmov jemmini.NoVME,TRUE\r\n\tretn\r\nsetvme:\r\n\tmov jemmini.NoVME,FALSE\r\n\tretn\r\nendif\r\nif ?PGE\r\nsetnopge:\r\n\tmov jemmini.NoPGE,TRUE\r\n\tretn\r\nsetpge:\r\n\tmov jemmini.NoPGE,FALSE\r\n\tretn\r\nendif\r\n\r\nif ?V86EXC0D\r\nsetv86exc0d:\r\n\tor jemmini.V86Flags,V86F_V86EXC0D\r\n\tretn\r\nendif\r\nif ?INTEGRATED\r\nsethmamin:\r\n\tinvoke GetValue, ax, 10, FALSE\r\n\t.if ax > 63\r\n\t\tmov ax,63\r\n\t.endif\r\n\tmov jemmini.HmaMin, ax\r\n\tretn\r\nsetmaxext:\r\n\tinvoke GetValue, ax, 10, TRUE\r\n\tmov xms.max, eax\r\n\tretn\r\n if ?XMS35\r\nsetmaxsext:\r\n\tinvoke GetValue, ax, 10, TRUE\r\n\tmov xms.smax, eax\r\n\tretn\r\n endif\r\nsetnoe801:\r\n\tmov bNoE801, TRUE\r\n\tretn\r\nsetnoe820:\r\n\tmov bNoE820, TRUE\r\n\tretn\r\nsetxmshandles:\r\n\tinvoke GetValue, ax, 10, FALSE\r\n\t.if ax < ?XMS_STATICHDLS\r\n\t\tmov ax, ?XMS_STATICHDLS\r\n\t.elseif ( ax > ?XMS_MAXHDLS )\r\n\t\tmov ax, ?XMS_MAXHDLS\r\n\t.endif\r\n\tmov xms.num_handles,ax\r\n\tretn\r\nsetx2max:\r\n\tinvoke GetValue, ax, 10, TRUE\r\n\tmov jemmini.X2Max, ax\r\n\tretn\r\nendif\r\nsetnoinvlpg:\r\n\tmov jemmini.NoInvlPg, TRUE\r\n\tretn\r\nsetnodyn:\r\n\tmov jemmini.NoPool, TRUE\r\n\tretn\r\nsetverbose:\r\n\tmov bVerbose, TRUE\r\n\tretn\r\nsetmin:\r\n\tinvoke GetValue, ax, 10, TRUE\r\n\tshr eax, 4\r\n\tmov jemmini.MinMem16k,eax\r\n\t.if bVerbose\r\n\t\tshl eax, 4\r\n\t\tinvoke printf, CStr(\"Wanted preallocated EMS/VCPI memory: %lu kB\", LF), eax\r\n\t.endif\r\n\tmov MinRequest, TRUE\r\n\tretn\r\nsetnoems:\r\n\tinvoke cprintf, CStr(\"NOEMS: EMS disabled (mostly :-)\", LF)\r\n\tmov jemmini.NoEMS, TRUE\r\n_ret:\r\n\tretn\r\nsetmax:\r\n\tinvoke GetValue, ax, 10, TRUE\r\n\tshr eax,4\r\n\tmov jemmini.MaxMem16k+0,eax\r\n\tretn\r\nsetvds:\r\n\tmov jemmini.NoVDS, FALSE\r\n\tretn\r\nsetnovds:\r\n\tmov jemmini.NoVDS, TRUE\r\n\tretn\r\nsetnoframe:\r\n\tmov jemmini.NoFrame, TRUE\r\n\tretn\r\nsetframe:\r\n\tinvoke GetValue, ax, 16, FALSE\r\n\t.if al\r\n\t\tinvoke printf, CStr(\"Rejected page frame=%x\", LF), ax\r\n\t.else\r\n\t\tmov bFrameWanted, ah\r\n\t\t.if bVerbose\r\n\t\t\tinvoke printf, CStr(\"Wanted page frame=%X\", LF), ax\r\n\t\t.endif\r\n\t.endif\r\n\tretn\r\nsetxeqtest:\r\n\tmov ExcludeTest, TRUE\r\n\tretn\r\nsetieqtest:\r\n\tmov IncludeTest, TRUE\r\n\tretn\r\nif ?SB\r\nsetsb:\r\n\tor jemmini.V86Flags, V86F_SB\r\n\tretn\r\nendif\r\nif ?EMX\r\nsetemx:\r\n\tor jemmini.V86Flags, V86F_EMX\r\n\tretn\r\nendif\r\nif ?SPLIT\r\nsetsplit:\r\n\tmov SplitTest, TRUE\r\n\tretn\r\nendif\r\nif ?NOVMWARE\r\nsetnovmw:\r\n\tmov wChkVMWare, offset _ret\r\n\tretn\r\nendif\r\nif ?MOVEXBDA\r\nsetmovexbda:\r\n\tor jemmini.V86Flags, V86F_MOVEXBDA\r\n\tretn\r\nendif\r\nsetnocheck:\r\n\tor jemmini.V86Flags, V86F_NOCHECK\r\n\tretn\r\nsetaltboot:\r\n\tor jemmini.V86Flags, V86F_ALTBOOT\r\n\tretn\r\nsetnohi:\r\n\tmov jemmini.NoHigh, TRUE\r\n\tretn\r\nsetnoram:\r\n\tmov jemmini.NoRAM, TRUE\r\n\tretn\r\nsetram:\r\n\tmov jemmini.NoRAM, FALSE\r\n\tretn\r\nsetdma:\r\n\tinvoke GetValue,ax, 10, FALSE\r\n\tmov bx,ax\r\n\t.if ax <= 128\r\n\t\tlea ax,[bx+3]\r\n\t\tand al,0FCh\r\n\t\tmov jemmini.DMABufferSize,ax\r\n\t.else\r\n\t\tmov jemmini.DMABufferSize,128\r\n\t\tinvoke printf, CStr(\"%s: wanted DMA buffer size too large, set to 128 kB\", LF), addr szWarning\r\n\t.endif\r\n\tretn\r\nsetborder:\r\n\tinvoke GetValue, ax, 16, FALSE\r\n\t.if ax < 1000h\r\n\t\tinvoke printf, CStr(\"%s: EMS banking start too low, set to 0x1000\", LF), addr szWarning\r\n\t\tmov ax, 1000h\r\n\t.endif\r\n\tmov jemmini.Border,ax\r\n\tretn\r\ncheckvmware:\r\n\tinvoke VMwareDetect\r\n\t.if ax\r\n\t\tmov dword ptr SystemMemory+0E8h, 'VVVV'\r\n\t\tmov dword ptr SystemMemory+0ECh, 'XXXX'\r\n\t\tinvoke cprintf, CStr(\"VMware detected\", LF)\r\n\t.endif\r\n\tretn\r\n\r\nmainex ENDP\r\n\r\nEND\r\n"
  },
  {
    "path": "src/JEMM.INC",
    "content": "\r\n;--- common definitions for both Jemm32 and Jemm16\r\n\r\n\toption proc:private\r\n\toption casemap:none\r\n\r\nLF              EQU 0AH\r\nCR              EQU 0DH\r\n\r\nifndef ?INTEGRATED\r\n?INTEGRATED     EQU 0       ; 1=integrated version (XMS + EMM)\r\nendif\r\nifndef ?KD\r\n?KD             EQU 0       ; 1=support for PL0 debugger\r\nendif\r\n?DMAPT          EQU 1       ; 1=enable DMA port trapping support\r\n?VDS            EQU 1       ; 1=enable VDS support\r\n?VCPI           EQU 1       ; 1=enable VCPI support\r\n?EMX            EQU 1       ; 1=EMX compat switch supported\r\n?SB             EQU 1       ; 1=SB compat switch supported (useful?)\r\n?EMMXXXX0       EQU 1       ; 1=implement EMMXXXX0 IOCTL\r\n?A20XMS         EQU 1       ; 1=emu A20 by trapping XMS functions\r\n?A20PORTS       EQU 1       ; 1=emu A20 by trapping ports (92, 64, 60)\r\n?VME            EQU 1       ; 1=support P1+ VME extension\r\n?LOAD           EQU 1       ; 1=support LOAD command line option\r\n?PGE            EQU 1       ; 1=support PGE on P3+ (requires ?INVLPG!) \r\n?MOVEHIGH       EQU 1       ; 1=support moving in first UMB\r\n?UNLOAD         EQU 1       ; 1=support UNLOAD option\r\n?FASTBOOT       EQU 1       ; 1=support FASTBOOT option\r\n?SPLIT          EQU 1       ; 1=support SPLIT option\r\n?HOOK13         EQU 1       ; 1=hook int 13h/40h for DMA access trapping\r\n?V86EXC0D       EQU 1       ; 1=support V86EXC0D option\r\n?ADDCONV        EQU 1       ; 1=add A000h to conv memory if I=A000-XXXX\r\n\r\nif ?INTEGRATED\r\n?MOVEXBDA       EQU 1       ; 1=support MOVEXBDA option; move XBDA into UMB\r\nelse\r\n?MOVEXBDA       EQU 1\r\nendif\r\n?MOVEXBDAPM     equ 1       ; 1=move XBDA in protected-mode\r\n\r\n;?GDTOFS         equ ?HLPSTKSIZE\r\nif ?KD\r\nGDT_SEL         equ 48h     ; GDT selector, used by kernel debugger\r\nendif\r\n\r\n;--- define v86-monitor breakpoint ( used to switch back to protected-mode )\r\n;--- the advantage of ARPL is that it's causing an \"invalid opcode\" exception,\r\n;--- so it won't have to be dealt with in the rather \"longish\" GPF handler chain.\r\n?BPOPC          equ 0F4h    ; default is HLT\r\n;?BPOPC         equ 063h    ; alternately ARPL\r\n\r\n?XMS35COMPAT    equ 1       ; 1=monitor is XMS v3.5 compatible (is aware of memory blocks beyond 4GB)\r\n\r\nV86IOPL         equ 3       ; IOPL for v86-mode\r\n\r\n;--- BIOS constants\r\n\r\nSMAP equ 534d4150h\r\n\r\nE820MAP struct\r\nbaselow  dd ?\r\nbasehigh dd ?\r\nlenlow   dd ?\r\nlenhigh  dd ?\r\ntype_    dd ?\r\nE820MAP ends\r\n\r\n@XBDA     equ 40Eh ; segment address XBDA\r\n@MEM_SIZE equ 413h ; base memory size in KB\r\n\r\nif ?INTEGRATED\r\n\r\n ifndef ?XMS35\r\n?XMS35     equ 1    ;std=1, 1=support super-extended memory beyond 4GB\r\n endif\r\n\r\n;--- A20 switch methods (must match order in \"methods\" table)\r\n\r\nA20_KBC     equ 0\r\nA20_PS2     equ 1\r\nA20_BIOS    equ 2\r\nA20_ALWAYSON equ 3\r\nA20_FAST    equ 4\r\nA20_PORT92  equ 5\r\n\r\n if ?XMS35\r\nDRIVER_VER      equ 350h+2\r\nINTERFACE_VER   equ 351h\r\nINTERFACE_VER_MAXSEXT0 equ 300h\t;xms version if MAXSEXT=0 is set\r\n else\r\nDRIVER_VER      equ 300h+29\r\nINTERFACE_VER   equ 300h\r\n endif\r\n\r\nendif\r\n\r\nDMABUFFDEFAULT equ 64 ;/* DMA buffer default size in kB */\r\nMINMEM16K_DEFAULT equ 0 ;/* MIN= default (in 16 kB units) */\r\n\r\n;--- data for monitor initialization\r\n\r\nJEMMINIT struct 4\r\nMonitorStart      dd 0    ;memory block start address (XMS/I15)\r\nMonitorEnd        dd 0    ;memory block end address (XMS/I15)\r\nMaxPhysMem        dd 0    ;highest physical memory address (XMS/I15)\r\nMaxMem16k         dd -1   ;MAX mem in 16 kB units (default 7680)\r\nMinMem16k         dd MINMEM16K_DEFAULT    ;MIN mem in 16 kB units (default 0)\r\nXMSHandleTable    dd 0    ;XMS handle table (FAR16)\r\nPageMap           dd 0    ;conventional memory page map (FLAT!)\r\nMaxEMSPages       dw 0    ;EMS max 16 kB pages (default 2048)\r\nXMSControlHandle  dw 0    ;XMS memory block handle (both Jemm386 & JemmEx)\r\nDMABufferSize     dw DMABUFFDEFAULT    ;DMA buffer size in kB\r\nFrame             dw 0E000h    ;EMS page frame\r\nBorder            dw 04000h    ;EMS border for mappable pages\r\nResUMB            dw 0    ;UMB para where the resident part was moved to\r\nif ?INTEGRATED\r\nHmaMin            dw 0    ;min request in KB for HMA\r\nX2Max             dw -1\r\n if ?XMS35\r\nxms_version       dw INTERFACE_VER\r\n endif\r\nendif\r\nif ?KD\r\nkdofs             dd 0\r\nkdseg             dw 0\r\nendif\r\nNoEMS             db 0\r\nNoFrame           db 0\r\nNoPool            db 0\r\n;AltBoot           db 0   ;v5.80: has become a bit in V86Flags\r\nNoVDS             db 0\r\nunion \r\n struct\r\nNoVCPI            db 0\r\nNoA20             db 0\r\nNoVME             db 1\r\nNoPGE             db 1\r\n ends\r\ndwDynSet          dd ?\r\nends\r\nNoInvlPg          db -1\r\nV86Flags          db 0\r\nNoRAM             db 0\r\nNoHigh            db 0\r\nNumUMBs           db 0    ;number of UMBs installed\r\nif ?INTEGRATED\r\nA20Method         db -1\r\nendif\r\nJEMMINIT ends\r\n\r\n;--- V86Flags equates\r\n\r\nV86F_SB         equ 1   ; soundblaster driver compat\r\nV86F_NOCHECK    equ 2   ; flag NOCHECK option\r\nif ?EMX\r\nV86F_EMX        equ 4   ; EMX compat\r\nendif\r\nif ?FASTBOOT\r\nV86F_FASTBOOT   equ 8   ; fastboot active\r\nV86F_FASTBOOT_B equ 3   ; bit 3\r\nendif\r\nif ?V86EXC0D\r\nV86F_V86EXC0D   equ 16  ; V86EXC0D active\r\nendif\r\nif ?MOVEXBDA\r\nV86F_MOVEXBDA   equ 32  ; MOVEXBDA active\r\nendif\r\nV86F_ALTBOOT    equ 64  ; ALTBOOT active\r\n\r\n;-- 120 MB max VCPI memory (in 16 kB units)\r\n;-- keep this value low for buggy VCPI clients\r\n;-- that fail with large free amounts\r\n\r\nMAXMEM16K_DEFAULT   EQU 1E00h   ; 7680 * 16 = 122880 kB or 120 MB\r\n\r\nMAX_EMS_PAGES_POSSIBLE  equ 8000h\r\n\r\n;--- this is the table of RSEG offsets \r\n;--- the values are offsets in the RSEG segment\r\n;--- there is just one instance defined in jemm16.asm.\r\n\r\nRSOFS struct\r\nwSizeRes    dw ?    ;size resident part\r\nwBpTab      dw ?    ;offset of BP table\r\nif ?DMAPT\r\nwRFlags     dw ?    ;offset of DMA flags\r\nendif\r\nRSOFS ends\r\n\r\n;--- bRFlags values\r\nRFL_COPYBUFFER  equ 1   ; 1=copy out of DMA buffer\r\nRFL_DMAOP       equ 2   ; 1=DMA op started\r\n\r\n;--- max number of UMB blocks\r\n;--- this differs significantly from how UMBs are handled by\r\n;--- MS Himem. With MS Himem, each UMB has a 16-byte header\r\n;--- (quite similarly to MCBs), and there's no UMB block limit.\r\n\r\nUMB_MAX_BLOCKS  equ 8\r\n\r\nUMBBLK struct\r\nwSegm   dw ?    ;segment address\r\nwSize   dw ?    ;size in paras, high bit used as flag free/allocated\r\nUMBBLK ends\r\n\r\nUMB_ALLOCATED equ 80h\t;flag in wSize+1\r\n\r\nif ?EMMXXXX0\r\n\r\n;--- ioctl read functions\r\nEMMDEV_GETAPI  equ 0\r\nEMMDEV_GEMMIS  equ 1\r\nEMMDEV_VERSION equ 2\r\nEMMDEV_GETRES  equ 4    ;get EMM resident segment/size?\r\n\r\nEMMDEV_SYSVARS equ 6    ;get state of jemm32 variables\r\nEMMDEV_GETUMBS equ 7    ;get state of UMBs\r\nEMMDEV_GETSTAB equ 8    ;get VMM info\r\n\r\n;--- ioctl write functions\r\nEMMDEV_UPDATE  equ 15\r\n\r\n;--- structure returned by Jemm386 if a SYSVARS request was made\r\n;--- for device \"EMMXXXX0\"\r\n\r\nEMX06 struct\r\ne06_NoEMS       db ? ;+0\r\ne06_Frame       dw ? ;+1 segment\r\ne06_NoVCPI      db ? ;+3\r\ne06_DMABuff     dd ? ;+4 physical address DMA buffer\r\ne06_NoPGE       db ? ;+8\r\n                db ?\r\n                db ?\r\n                db ?\r\ne06_DMASize     dw ? ;+12 in KB\r\ne06_NoVME       db ? ;+14\r\ne06_NoA20       db ? ;+15\r\ne06_VCPITotal   dd ? ;+16 VCPI pages total (def 120 MB)\r\ne06_VCPIUsed    dd ? ;+20 VCPI pages allocated\r\nEMX06 ends\r\n\r\n;--- the structure for an \"UPDATE\" request with IoctlWrite\r\n\r\nEMX15W struct\r\ne15_bVME    db ?\r\ne15_bA20    db ?\r\ne15_bVCPI   db ?\r\ne15_bPGE    db ?\r\nEMX15W ends\r\n\r\nendif\r\n\r\n;--- macros\r\n\r\n@BPOPC macro\r\n    db ?BPOPC\r\nendm\r\n\r\n"
  },
  {
    "path": "src/JEMM16.ASM",
    "content": "\r\n;--- 16 bit part of Jemm;\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n;--- _RTEXT must be first segment; it has to be defined *before* .model.\r\n_RTEXT segment para public 'CODE'\r\n_RTEXT ends\r\nDGROUP group _RTEXT\r\n\r\n\t.286\r\nifdef __JWASM__\r\n\t.model tiny\r\nelse\r\n\t.model small\t;Masm bug: .model tiny rejects using SEG operator\r\nDGROUP group _TEXT\r\nendif\r\n\r\n\tinclude jemm.inc\r\n\tinclude jemm16.inc\r\n\tinclude debug16.inc\r\n\tinclude xms.inc\r\nif ?KD\r\n\tinclude debugsys.inc\r\nendif\r\n\r\nGDT_SIZE equ 200h\t; is defined in jemm32.inc\r\n\r\n;--- interrupt hook list entry \r\n\r\nINTMOD struct\r\nbInt\tdb ?\r\nwNew\tdw ?\r\nwOld\tdw ?\r\nINTMOD ends\r\n\r\nMCB struct\r\nsig db ?\r\npsp dw ?\r\nblsiz dw ?\r\nMCB ends\r\n\r\nIRETWS struct\r\n_Ip  dw ?\r\n_Cs  dw ?\r\n_Fl  dw ?\r\nIRETWS ends\r\n\r\n;--- @DefineBP: macro to define a v86-\"breakpoint\"\r\n\r\n@DefineBP macro _name\r\n_name&::\r\n\t@BPOPC\r\nendm\r\n\r\nCStr macro text:VARARG\r\nlocal sym\r\n\t.const\r\nsym db text,0\r\n\t.code\r\n\texitm <offset sym>\r\nendm\r\n\r\n\t.data\r\n\r\nwLow    dw RSEG_END     ;resident low memory required by driver\r\n\r\n;--- _brptab is the second of two parameters for Jemm32 init\r\n;--- the entries are offsets in RSEG\r\n;--- the most important one is the offset to the v86 breakpoint table\r\n\r\nif ?HOOK13\r\n_brptab RSOFS < RSEG_END, bptable, bRFlags >\r\nelse\r\n_brptab RSOFS < RSEG_END, bptable >\r\nendif\r\n\r\nif 0;?A20PORTS\r\nwBiosA20    DW 1+2  ;default: trap both kbdc + ps2 ports\r\nendif\r\n\r\n\t.const\r\n\r\nintvecs label INTMOD\r\n\tINTMOD <15h,offset NEW15, offset OldInt15>\r\nif ?INTEGRATED\r\n\tINTMOD <2Fh,offset NEW2F, offset OldInt2F>\r\nendif\r\nif ?HOOK13\r\n\tINTMOD <13h,offset NEW13, offset OldInt13>\r\n\tINTMOD <40h,offset NEW40, offset OldInt40>\r\nendif\r\n\tINTMOD <67h,offset BP67,  -1>\r\n\tINTMOD <06h,offset BP06,  -1>\r\n\tINTMOD <19h,offset BP19,  -1>\r\n\tdb -1\r\n\r\n\t.data?\t;ensure _BSS is defined\r\n\r\nSTACK segment para STACK 'STACK'\r\n\torg 1024\t\t; application stack\r\nstacktop label byte\r\nSTACK ends\r\n\r\nDGROUP group STACK\t;here stack has to be added to dgroup manually\r\n\r\n\t.386\r\n\r\n_RTEXT SEGMENT\r\n\r\n\tASSUME DS:NOTHING,ES:NOTHING,FS:ERROR,GS:ERROR\r\n\r\n;*************************************************************************\r\n; device driver header\r\n\r\ndevice_header:\r\n\tdd -1\t\t\t\t; last driver in list\r\n\tdw 0c000h\t\t\t; driver flags :\r\n\t\t\t\t\t\t; 8000 - character device\r\n\t\t\t\t\t\t; 4000 - supports IOCTL - like EMM386\r\npStratOfs dw offset strategy\t; offset to strategy routine\r\npIntOfs dw offset driver_entry\t; offset to interrupt handler\r\n\r\ndevice_name label byte\r\n\tdb\t'EMMXXXX0'\t\t; device driver name\r\n\r\n;--- start of real-mode resident data area\r\n\r\n;--- v86 breakpoint table\r\n;--- the order must match the one of bptable in Jemm32.asm !\r\n\r\nbptable label byte\r\n\t@DefineBP BP06\t\t; int 06h: entry invalid opcode exception\r\n\t@DefineBP BP19\t\t; int 19h\r\n\t@DefineBP BP67\t\t; int 67h: entry real-mode\r\nif ?VDS\r\n\t@DefineBP BPVDS\t\t; int 4Bh: entry v86-mode (VDS)\r\nendif\r\n\t@DefineBP BPBACK\t; BP to return to v86 monitor from a v86 far proc\r\n\t@DefineBP BPI1587\r\nif ?HOOK13\r\n\t@DefineBP BP1340\t; int 13h/40h: copy out of DMA buffer\r\nendif\r\n\t@DefineBP BPXMS \t; handle XMS A20+UMB\r\n\t@DefineBP BPSTRAT\t; EMMXXXX0 device strategy call\r\n\t@DefineBP BPINTR\t; EMMXXXX0 device interrupt call\r\nif ?UNLOAD\r\n\t@DefineBP BPUNL\t\t; unload\r\nendif\r\n\r\nif ?HOOK13\r\n\r\nbRFlags DB 0\t\t\t; bit 0: 1=copy out of DMA buffer ; bit 1: 1=new DMA op started\r\n\r\n;--- for DMA, hook int 40h (FD access)\r\n;--- and int 13h (HD access)\r\n\r\nNEW40 PROC FAR\r\n\tmov cs:[bRFlags],RFL_DMAOP\t; tell the monitor that a new DMA op has started\r\n\tpushf\r\n\tdb 09Ah\r\nOldInt40 dd 0\r\n\tjmp int1340common\r\nNEW40 ENDP\r\n\r\n\talign 4\r\n\r\nNEW13 PROC FAR\r\n\tmov cs:[bRFlags],RFL_DMAOP\t; tell the monitor that a new DMA op has started\r\n\tpushf\r\n\tdb 09Ah\r\nOldInt13 dd 0\r\nNEW13 ENDP\t;fall thru\r\n\r\nint1340common:\r\n\tjc iret_with_new_CY\r\n\ttest cs:[bRFlags],RFL_COPYBUFFER\r\n\tjnz BP1340\t\t\t; breakpoint int 13/40h: copy out of DMA buffer\r\nendif\r\nif ?HOOK13 or ?INTEGRATED\r\niret_with_new_CY:\r\n\tpush bp\r\n\tmov bp,sp\r\n\trcr byte ptr [bp+2].IRETWS._Fl,1\r\n\trol byte ptr [bp+2].IRETWS._Fl,1\r\n\tpop bp\r\n\tiret\r\n\r\nendif\r\n\r\n;******************************************************************************\r\n; INT15 handler:\r\n;    handle AH=87h case (copy extended memory)\r\n;\r\n;        AH = 87h\r\n;        CX = number of words to copy (max 8000h)\r\n;        ES:SI -> GDT (4 descriptors)\r\n;Return: CF set on error, else cleared\r\n;        AH = status (00 == ok)\r\n;\r\n; For JemmEx, also handle ah=88h (get extended memory size)\r\n; If XMS v3.5 is supported, ax=E820h must also be intercepted.\r\n;******************************************************************************\r\n\r\n\talign 4\r\n\r\nNEW15 PROC FAR\r\n\r\n;--- int 15h handlers \"should\" preserve CF\r\n;--- it's not done here, because CF will be set if ah=4Fh (keyboard)\r\n\r\n\tCMP AH,87H\t\t; is it the blockmove?\r\n\tJZ BPI1587\r\nif ?INTEGRATED\r\n\tCMP AH,88H\t\t; ext memory size\r\n\tJZ i1588\r\n if ?XMS35\r\n\tCMP AX,0E820h\t; memmap\r\n\tJZ memmap\r\n  ifdef __JWASM__\r\nxms_smax_noe820 equ $ - 1\r\n  else\r\n    org $-1\r\nxms_smax_noe820 db ?\r\n  endif\r\n endif\r\nendif\r\n\tdb 0EAh\r\nOldInt15 dd 0\r\nif ?INTEGRATED\r\ni1588:\r\n\txor ax,ax\t\t\t; no memory available\r\n\tjmp iret_with_new_CY\r\n if ?XMS35\r\nmemmap:\r\n\tpushf\r\n\tcall cs:[OldInt15]\r\n\tjc iret_with_new_CY\r\n\tcmp eax,SMAP\r\n\tclc\r\n\tjnz iret_with_new_CY\r\n\tcmp es:[di].E820MAP.basehigh,0\r\n\tjz iret_with_new_CY\r\n\tcmp es:[di].E820MAP.type_,1\t\t;\"available\" memory?\r\n\tclc\r\n\tjnz iret_with_new_CY\r\n\tmov byte ptr es:[di].E820MAP.type_,2\t\t;change to \"reserved\"\r\n\tjmp iret_with_new_CY\r\n endif\r\nendif\r\nNEW15 ENDP\r\n\r\n;*********************************************************\r\n; XMS hook - required for UMBs and A20 emulation\r\n;*********************************************************\r\n\r\n\talign 4\r\n\r\nXMShandler proc\r\n\r\n\tjmp short @@XMSHandler\t; standard XMS link chain\r\n\tnop \t\t\t\t\t; with 3 required NOPs\r\n\tnop\r\n\tnop\r\n@@XMSHandler:\r\nif ?INTEGRATED\r\n\tjmp BPXMS\r\nelse\r\n\r\n;-- for A20 disable and enable emulation it is required to hook\r\n;-- the XMS functions as well, even if the A20 ports (92, 60, 64)\r\n;-- are trapped. That's because if certain conditions are true\r\n;-- the XMS host will never access the ports and leave A20 unchanged.\r\n\r\n if ?A20XMS\r\n\tcmp ah,3\r\n\tjb @@noa20\r\n\tcmp ah,6\r\n\tjbe BPXMS\r\n@@noa20:\r\n endif\r\n\r\nXMSUMB::\r\n\tcmp ah,10h\t\t\t; 10h=alloc, 11h=free, 12=realloc\r\n\tjb @@xms_prev\r\n\tcmp ah,12h\r\n\tjbe BPXMS\r\n@@xms_prev:\r\n\tdb 0eah \t\t\t; jmp far XMS prev handler\r\nXMSoldhandler dd 0\r\nendif\r\n\r\nXMShandler endp\r\n\r\nif ?INTEGRATED\r\n\r\nNEW2F proc\r\n\tpushf\r\n\tcmp ah,43h\r\n\tjz is43\r\n@@jmp_old2f:\r\n\tpopf\r\n\tdb 0EAh\r\nOldInt2F dd 0\r\nis43:\r\n\tcmp al,00h\t\t\t; is it \"Installation Check\"?\r\n\tje @@get_driver_installed\r\n\tcmp al,10h\t\t\t; is it \"Get Driver Address\"?\r\n\tje @@get_xms_address\r\n\tcmp al,09h\t\t\t; is it \"get handle table\"?\r\n\tje @@get_xms_handle_table\r\n\tcmp al,08h\r\n\tjne @@jmp_old2f\r\n\tmov al,ah\t\t;al==43h if function supported\r\nmachine_type label byte    \r\n\tmov bx,0002 \t; bh=switch time; 0=medium, 1=fast, 2=slow\r\n\t\t\t\t\t; bl=machine type; 1=std AT (KBC), 2=PS/2 (port 92) ?\r\n\tpopf\r\n\tiret\r\n@@get_driver_installed:\r\n\tmov al,80h\t\t; yes, we are installed ;)\r\n\tpopf\r\n\tiret\r\n@@get_xms_address:\r\n\tmov bx,offset XMShandler\r\n@@shared2f:\r\n\tpush cs\r\n\tpop es\r\n\tpopf\r\n\tiret\r\n@@get_xms_handle_table:\r\n\tmov al,ah\t\t;al==43h if function supported\r\n\tmov bx,offset xms_handle_table\r\n\tjmp @@shared2f\r\n\r\nNEW2F endp\r\nendif\r\n\r\nif ?DBGOUT\r\n\r\n\tinclude dprntf16.inc\r\n\r\nendif\r\n\r\nif ?INTEGRATED\r\n\r\n\talign 2\r\n\r\n?EXTRAHDL equ 0\r\n\r\nxms_handle_table XMS_HANDLETABLE <01, size XMS_HANDLE, 0, xms_handle_array>\r\nxms_handle_array XMS_HANDLE ?XMS_STATICHDLS+1+?EXTRAHDL dup (<XMSF_INPOOL,0,0,0>)\r\n\tdb XMSF_INPOOL\r\n\r\nendif\r\n\talign 16\r\n\r\nRSEG_END equ $ - device_header\r\n\r\n_RTEXT ends\r\n;\r\n; installation part of the virtual Monitor\r\n;\r\n\t.code\r\n\r\n\tassume ds:DGROUP\r\n\r\ncode32:\r\nifdef __JWASM__ ;JWasm allows to include binary data directly\r\n\tincbin <jemm32.bin>\r\nelse\r\n\t.nolist\r\n\tinclude _jemm32.inc\r\n\t.list\r\nendif\r\n\r\nif ?UNLOAD\r\n\r\nCheckIntHooks proc stdcall public uses es si di wResSeg:WORD\r\n\r\n\tmov ax, wResSeg\r\n\tshl eax, 16\r\n\tpush 0\r\n\tpop es\r\n\tmov si, offset intvecs\r\n@@nextvect:\r\n\tlodsb\r\n\tcmp al,-1\r\n\tjz @@ok\r\n\tmovzx di,al\r\n\tshl di,2\r\n\tlodsw\r\n\tscasd\r\n\tstc\r\n\tjnz @@nouninst\r\n\tadd si,2\r\n\tjmp @@nextvect\r\n@@ok:\r\nif ?VDS\r\n\ttest byte ptr es:[47Bh],20h\r\n\tjz @F\r\n\tmov ax,offset BPVDS\r\n\tcmp eax,es:[4Bh*4]\r\n\tstc\r\n\tjnz @@nouninst\r\n\t@dprintf ?UNLRMDBG, <\"CheckIntHooks: int 4Bh is ok\",10>\r\n@@:\r\nendif\r\n\tclc\r\n@@nouninst:\r\n\tret\r\nCheckIntHooks endp\r\n\r\n;--- Jemm can be unloaded. Do it!\r\n;--- in: AX=segment of resident part of installed Jemm\r\n;--- out: C=error, NC=ok. \r\n;--- may change all GPRs except BP/SP.\r\n\r\nUnloadJemm proc c public\r\n\r\n\tmov di,ax\r\n\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm enter, res. segm=%X CS=%X SS:SP=%X:%X\",10>, di, cs, ss, sp\r\n\r\n;--- remove EMMXXXX0 from driver chain\r\n\r\n\tmov ah,52h\t\t\t;get start of driver chain\r\n\tint 21h\r\n\tadd bx,22h\r\n@@nextdev:\r\n\tcmp di, es:[bx+2]\r\n\tjz @@found\r\n\tles bx, es:[bx]\r\n\tcmp bx,-1\r\n\tjnz @@nextdev\r\n\tstc\r\n\tret\r\n@@found:\r\n\tmov ds,di\r\n\tmov ecx, ds:[0]    ;remove driver from chain\r\n\tmov es:[bx], ecx\r\n\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm: driver removed from chain\",10>\r\n\r\n;--- reset IVT vectors\r\n\r\n\tmov es,di\r\n\tpush 0\r\n\tpop ds\r\n\tmov si, offset intvecs\r\n@@nextvec:\r\n\tlodsb cs:[si]\r\n\tcmp al,-1\r\n\tjz @@ok\r\n\tmovzx bx,al\r\n\tshl bx, 2\r\n\tmov di,cs:[si+2]\r\n\tcmp di,-1\r\n\tjz @@skipvec\r\n\tmov ecx, es:[di]\r\n\tmov [bx],ecx\r\n@@skipvec:\r\n\tadd si,4\r\n\tjmp @@nextvec\r\n@@ok:\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm: IVT vectors restored\",10>\r\n\r\n;--- reset XMS hook\r\n\r\nife ?INTEGRATED\r\n\tmov bx,offset XMSoldhandler\r\n\tmov ecx, es:[bx]\r\n\tand ecx, ecx\r\n\tjz @@noxms\r\n\r\n\tpush es\r\n\tpush es\r\n\tpop ds\r\n\tmov si,offset XMShandler\r\n\tpush ecx\r\n\tpop di\r\n\tpop es\r\n\tmov cx,5\r\n\tsub di,cx\r\n\trep movsb\r\n\tpop es\r\n@@noxms:\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm: XMS hook removed\",10>\r\n\r\nendif\r\n\r\nif ?KD\r\n;--- call int 68h if kernel debugger installed.\r\n;--- if the installed instance didn't initialize the kd, nothing is to be done here!\r\n\tmov ax,es\r\n\tsub ax,10h\r\n\tmov ds,ax\r\n\ttest byte ptr ds:[7fh], 1\r\n\tjz @F\r\n\tmov ah, D386_Real_Mode_Init\r\n\tint D386_RM_Int\r\n@@:\r\nendif\r\n\r\n;--- make sure jemm386's resident memory block is freed\r\n;--- this is to be improved yet\r\n\r\n\tmov ax,es\r\n\tsub ax,10h+1\t;size PSP + 10h (to get MCB)\r\n\tmov ds,ax\r\n\tmov cx,ax\r\n\tinc cx\r\n\tmov al,ds:[0]\r\n\tcmp al,'M'\r\n\tjz @@ismcb\r\n\tcmp al,'Z'\r\n\tjnz @@nopsp\r\n@@ismcb:\r\n\tcmp cx,ds:[1]\r\n\tjnz @@nopsp\r\nife ?INTEGRATED\r\n\tcmp word ptr ds:[3],10h + RSEG_END/10h\r\n\tjnz @@nopsp\r\nelse\r\n\tcmp word ptr ds:[10h+18h],-1\t;check if files 0+1 are closed\r\n\tjnz @@nopsp\r\nendif\r\n\tcmp word ptr ds:[10h],20CDh\r\n\tjnz @@nopsp\r\n\tmov ax,cs\r\n\tsub ax,10h\r\n\tmov ds:[1],ax\r\n@@nopsp:\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm: resident segment prepared to be released\",10>\r\n\r\n;--- now call the installed instance of Jemm386 to\r\n;--- do the rest of the clean-up\r\n\r\n\tpush bp\r\n\tmov bp,sp\r\n\tpushd 0\r\n\tpush 3FFh\r\n\tpush es\r\n\tpush offset BPUNL\r\n\tcall dword ptr [bp-(4+6)]\r\n\r\n;--- Jemm386 has exited, but we are still in protected mode!\r\n;--- GDTR no longer valid;\r\n;--- for Jemm386, BX contains XMS handle for Jemm386's memory block\r\n;--- for JemmEx, BL contains A20 method index\r\n\r\n\tmov eax, cr0\r\n\tand eax, 7FFFFFFEh\t;disable paging and protected-mode\r\n\tmov cr0, eax\r\n;--- v5.86: for 80486 compatibility, use a JMP far16 instead of RETF\r\n\tdb 0EAh\t\t\t\t;jmp FAR16 opcode\r\n\tdw offset @F, seg @F ; Masm: SEG operator rejected if .model TINY is used\r\n@@:\r\n\t.386p\r\n\tLIDT FWORD ptr [bp-6]; reset IDT to 0:3ffh (v5.86: now done AFTER mode switch)\r\n if 1\r\n\txor eax,eax\t;v5.86: clear TLB\r\n\tmov cr3,eax\r\n endif\r\n\t.386\r\n\tmov sp,bp\r\n\tpop bp\r\n\tmov ax,cs\t;v5.86: assume model TINY\r\n\tmov ss,ax\r\n\tmov ds,ax\r\n\tmov es,ax\r\n\tsti\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm: back from protected-mode\",10>\r\n\t@dprintf ?UNLRMDBG, <\"UnloadJemm exit\",10>\r\n\tmov ax,bx\r\n\tclc\r\n\tret\r\nUnloadJemm endp\r\n\r\nendif\r\n\r\n; check, if this program runs after all on a 386-Computer (o.ae.)\r\n; (... you never know)\r\n\r\nIs386 PROC NEAR\r\n\tPUSHF\r\n\tmov AX,7000h\r\n\tPUSH AX\r\n\tPOPF\t\t\t\t; on a 80386 in real-mode, bits 15..12\r\n\tPUSHF\t\t\t\t; should be 7, on a 8086 they are F,\r\n\tPOP AX\t\t\t\t; on a 80286 they are 0\r\n\tPOPF\r\n\tand ah,0F0h\r\n\tcmp AH,70H\r\n\tstc\r\n\tJNZ @F\r\n\tclc\r\n@@:\r\n\tRET\r\nIs386 ENDP\r\n\r\n;--- test if CPU is 386, display error if not\r\n;--- returns C and modifies DS in case of error\r\n;--- other registers preserved\r\n\r\nTestCPU proc near\r\n\tpush ax\r\n\tcall Is386\r\n\tjnc @F\r\n\tpush dx\r\n\tpush cs\r\n\tpop ds\r\n\tmov ah,9\r\n\tmov dx,offset dErrCpu\r\n\tint 21h\r\n\tpop dx\r\n\tstc\r\n@@:\r\n\tpop ax\r\n\tret\r\nTestCPU endp\r\n\r\ndErrCpu  db NAMEMOD,\": at least a 80386 cpu is required\",13,10,'$'\r\n\r\nrequest_ptr dd 0\r\n\r\n;--- the original strategy proc, must be 8086 compatible\r\n;--- will be replaced by a v86 BP once Jemm386 is installed\r\n\r\nstrategy:\r\n\tmov word ptr cs:[request_ptr+0],bx\r\n\tmov word ptr cs:[request_ptr+2],es\r\n\tretf\r\n\r\n;**********************************************\r\n; driver init part\r\n; this code is only necessary on init and\r\n; will not go resident. It must however be\r\n; in the same physical segment as the\r\n; resident part. The proc must be declared far.\r\n;**********************************************\r\n\r\nreq_hdr struct\r\nreq_size db ?\t;+0 number of bytes stored\r\nunit_id  db ?\t;+1 unit ID code\r\ncmd \t db ?\t;+2 command code\r\nstatus\t dw ?\t;+3 status word\r\nrsvd     db 8 dup(?);+5 reserved\r\nreq_hdr ends\r\n\r\ninit_req struct\r\n\treq_hdr <>\r\nunits\t db ?\t;+13 number of supported units\r\nendaddr  dd ?\t;+14 end address of resident part\r\ncmdline  dd ?\t;+18 address of command line\r\ninit_req ends\r\n\r\ndriver_entry proc far\r\n\r\n\tpush ds\r\n\tpush di\r\n\tlds di, cs:[request_ptr]\t; load address of request header\r\n\tmov [di].req_hdr.status,8103h\r\n\tcmp [di].req_hdr.cmd,0\t\t; init cmd?\r\n\tjne @@noinit\r\n\tcall TestCPU\r\n\tjnc @@cpuok\r\n@@noinit:\r\n\tpop di\r\n\tpop ds\r\n\tret\r\n@@cpuok:\r\n\tmov [di].req_hdr.status,0100h\t; set STATUS_OK\r\n\tpushad\r\n\tmov cx,ss\r\n\tmov bx,sp\r\n\tmov ax, cs\r\n\tmov ss, ax\r\n\tmov sp, offset stacktop\r\n\tpush cx\r\n\tpush bx\r\n\tpush es\r\n\tpush ds\r\n\tpush di\r\n\tles si, [di].init_req.cmdline\r\n\tmov ds, ax\r\n\tcall EmmInstallcheck\r\n\tjnc @F\r\n\txor ax,ax\r\n\tjmp @@driver_exit\r\n@@:\r\n\tadd sp,-128\r\n\tmov di,sp\r\n\tpush ds\r\n\tpush es\r\n\tpop ds\r\n\tpush ss\r\n\tpop es\r\n\r\n\tcld\r\nif 0\r\n\tpush si\r\nnextchar:\r\n\tlodsb\r\n\tcmp al,0\r\n\tjz donex\r\n\tcmp al,13\r\n\tjz donex\r\n\tcmp al,10\r\n\tjz donex\r\n\tmov dl,al\r\n\tmov ah,2\r\n\tint 21h\r\n\tjmp nextchar\r\ndonex:\r\n\tmov dl,13\r\n\tmov ah,2\r\n\tint 21h\r\n\tmov dl,10\r\n\tmov ah,2\r\n\tint 21h\r\n\tpop si\r\nendif\r\n\r\n@@nxtchar1: \t\t\t;skip program name\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\tcmp al,13\r\n\tjz done\r\n\tcmp al,10\r\n\tjz done\r\n\tcmp al,20h\r\n\tja @@nxtchar1\r\n@@nxtchar2:\r\n\tlodsb\r\n\tand al,al\r\n\tjz done\r\n\tcmp al,13\r\n\tjz done\r\n\tcmp al,10\r\n\tjz done\r\n\tstosb\r\n\tjmp @@nxtchar2\r\ndone:\r\n\tmov al,0\r\n\tstosb\r\n\tpop ds\r\n\tpush sp\r\n\tpush EXECMODE_SYS\r\n\tcall mainex\r\n\tadd sp,128+2+2\r\n;\tMOV DX,offset dFailed\r\n\tor ax,ax\t\t\t; error occured?\r\n\tmov ax,0\r\n\tjnz @@driver_exit\r\n\tmov ax, [wLow]\r\n@@driver_exit:\r\n\tpop di\r\n\tpop ds\r\n\tpop es\r\n\tmov word ptr [di].init_req.endaddr+0,ax\t; if ax == 0, driver won't be installed\r\n\tmov word ptr [di].init_req.endaddr+2,cs\t; set end address\r\n\tpop bx\r\n\tpop ss\r\n\tmov sp,bx\r\n\tpopad\r\n\tpop di\r\n\tpop ds\r\n\tret\r\n\r\ndriver_entry ENDP\r\n\r\nif ?LOAD\r\n\r\n;--- check if there is already an EMM installed\r\n;--- DS=DGROUP\r\n;--- out: NC=no Emm found\r\n\r\nEmmInstallcheck proc c public\r\n\tpush es\r\n\tpusha\r\n\tMOV AX,3567H\t\t; get INT 67h\r\n\tINT 21H\r\n\tMOV AX,ES\t\t\t; EMM already installed ?\r\n\tOR AX,BX\r\n\tJZ @@ok\r\n\tMOV DI, 10\r\n\tMOV SI, offset sig1\r\n\tCLD\r\n\tMOV CX, 8\r\n\tREPZ CMPSB\r\n\tje @@error\t\t\t; matched 1st ID string\r\n\tmov di, 10\t\t\t; didn't match, check 2nd ID (NOEMS version)\r\n\tmov si, offset sig2\r\n\tmov cl, 8\r\n\trepz cmpsb\r\n\tclc\r\n\tjne @@ok\t\t\t; did match 2nd ID string?\r\n@@error:\r\n\tmov dx, CStr('An EMM is already installed',CR,LF,07,'$')\r\n\tmov ah, 9\r\n\tint 21h\r\n\tstc\r\n@@ok:\r\n\tpopa\r\n\tpop es\r\n\tret\r\nEmmInstallcheck endp\r\n\r\nendif\r\n\r\n;*********************************************\r\n; startpoint when executing as EXE\r\n;*********************************************\r\n\r\nstart proc\r\n\r\n\tmov ax,cs\r\n\tmov ss,ax\r\n\tmov sp, offset stacktop - 128\r\n\tcall TestCPU\r\n\tjc @@exit\r\n\t@dprintf ?INITRMDBG, <NAMEMOD,\" enter\",10>\r\n\r\n\tmov di,sp\r\n\tmov si,0080h\r\n\tlodsb\r\n\tmovzx cx,al\r\n\tpush ss\r\n\tpop es\r\n\trep movsb\r\n\tmov al,0\r\n\tstosb\r\n\tpush ss\r\n\tpop ds\r\n\r\n\tpush sp\r\n\tpush EXECMODE_EXE\r\n\tcall mainex\t\t;returns 0 if everything ok\r\n\tadd sp,128+2+2\r\n\r\nif ?LOAD\r\n\tand ax,ax\t\t;error occured?\r\n\tjnz @@exit\r\n\tcmp [wLow],0\t;did we move high?\r\n\tjz @@exit\r\n\tcall LinkChain\t;link driver in chain for .EXE\r\n\tmov ah,51h\r\n\tint 21h\r\n\tmov es,bx\r\n\tmov es,es:[002Ch]\r\n\tmov ah,49h\r\n\tint 21h\r\n\tmov bx,4\r\n@@nextfile:\r\n\tmov ah,3Eh\r\n\tint 21h\r\n\tdec bx\r\n\tjns @@nextfile\r\n\tmov dx,[wLow]\r\n\tshr dx, 4\r\n\tadd dx, 10h\r\n\tmov ax,3100h\r\n\tint 21h\r\nendif\r\n\r\n@@exit:\r\n\t@dprintf ?INITRMDBG, <NAMEMOD, \" exit\",10>\r\n\tmov ah,04ch \t\t; that was all\r\n\tint 21h\r\nstart endp\r\n\r\n;--- monitor installation\r\n;--- in: CS=SS=DS=DGROUP\r\n;--- si=mode\r\n;--- out: EAX=first page used for EMS/VCPI\r\n\r\nInitJemm PROC c public\r\n\r\n\tpush si\r\n\tpush di\r\n\tpush bp\r\n\tmov bp, sp\t\t;mode is at [bp+4]\r\n\r\nif 0; ?A20PORTS \t;this info is unreliable, don't activate!\r\n\tmov ax,2403h\t;get A20 gate support\r\n\tint 15h\r\n\tcmp ah,00\t\t;ignore carry flag, it is not reliable\r\n\tjnz @F\r\n\tmov wBIOSA20,bx\r\n@@:\r\nendif\r\n\r\nif ?INTEGRATED\r\n\r\n;--- the xms handle table must be located in the resident conventional memory\r\n;--- part of jemmex\r\n\r\n\tmov cx, xms.num_handles\t\t;is at least 8\r\n\tmov [xms_handle_table.xht_numhandles], cx\r\n\tmov ax,size XMS_HANDLE\r\n\tmul cx\r\n\tadd ax,offset xms_handle_array + 15\t;adjust to paragraph\r\n\tand al,0F0h\r\n\tmov [_brptab.wSizeRes],ax\r\nendif\r\n\r\n;--- store interrupt vectors in resident memory\r\n\r\n\tmov si, offset intvecs\r\n@@nextint:\r\n\tmov al,[si].INTMOD.bInt\r\n\tcmp al,-1\r\n\tjz @@intmoddone\r\n\tmov di,[si].INTMOD.wOld\r\n\tcmp di,-1\r\n\tjz @@nooldsave\r\n\tmov ah,35h\r\n\tint 21h\r\n\tmov cs:[di+0],bx\r\n\tmov cs:[di+2],es\r\n@@nooldsave:\r\n\tadd si,size INTMOD\r\n\tjmp @@nextint\r\n@@intmoddone:\r\n\r\n;-- set new interrupt routine offset in the driver header, so any further\r\n;-- access to device EMMXXXX0 is handled by the monitor\r\n\r\n\tmov [pIntOfs],offset BPINTR\r\n\tmov [pStratOfs],offset BPSTRAT\r\n\r\nif ?KD\r\n\r\n;--- check for kernel debugger presence.\r\n;--- issue INT 68h, ah=43h and if debugger responds,\r\n;--- tell it about certain GDT selectors and get the\r\n;--- protected-mode entry.\r\n\r\n\tmov ax,3500h or D386_RM_Int\r\n\tint 21\r\n\tmov ax, es\t\t; int 68h vector 0000:0000?\r\n\tor ax, bx\r\n\tjz @F\r\n\tmov si, word ptr [code32+5]\t; DS:SI=GDT\r\n\tmov eax, ds\r\n\tshl eax, 4\r\n\tmovzx esi, si\r\n\tadd eax, esi\r\n\tmov [si+GDT_SEL+2], ax\r\n\tshr eax, 16\r\n\tmov [si+GDT_SEL+4], al\r\n\tmov [si+GDT_SEL+7], ah\r\n\r\n\tmov ah, D386_Identify\r\n\tint D386_RM_Int\r\n\tcmp ax, D386_Id\r\n\tjnz @F\r\n\t@dprintf ?INITRMDBG, <\"InitJemm: kernel debugger detected\",10>\r\n\tmov ah, D386_Prepare_PMode\r\n\tmov al, 0\r\n\tmov cx, GDT_SEL+8\r\n\tmov bx, FLAT_DATA_SEL\r\n\tmov dx, GDT_SEL\r\n\tmov di, -1 \t\t\t\t\t; ES:DI=IDT ( there's no IDT yet in Jemm! )\r\n\tint D386_RM_Int\r\n\tmov jemmini.kdofs, edi\r\n\tmov jemmini.kdseg, es\r\n\t@dprintf ?INITRMDBG, <\"InitJemm: kernel debugger pm entry: %X:%lX\",10>, es, edi\r\n\tcmp word ptr [bp+4], EXECMODE_EXE\r\n\tjnz @F\r\n\tmov ah, 51h\r\n\tint 21h\r\n\tmov es, bx\r\n\tor byte ptr es:[7fh], 1\t\t; use PSP:7Fh as \"kd flag\" (if jemm is loaded as .EXE)\r\n@@:\r\nendif\r\n\r\n;--- prepare running Jemm32.\r\n;--- build an IRETDV86 struct on the stack.\r\n\r\n\tmov dx,sp\r\n\tand sp,not 3\t; ensure the 32-bit stack is dword aligned\r\n\txor ax,ax\r\n\tpush ax\r\n\tpush gs\t; IRETDV86._Gs\r\n\tpush ax\r\n\tpush fs\t; IRETDV86._Fs\r\n\tpush ax\r\n\tpush ds\t; IRETDV86._Ds\r\n\tpush ax\r\n\tpush es\t; IRETDV86._Es\r\n\tpush ax\r\n\tpush ss\t; IRETDV86._Ss\r\n\tpush ax\r\n\tpush dx\t; IRETDV86._Esp\r\n\tpushd 20002h or ( V86IOPL shl 12 ) ;IRETDV86.vEFL (VM=1, NT=0, IF=0) IOPL=3\r\n\tpush ax\r\n\tpush cs\t; IRETDV86._Cs\r\n\tpush ax\r\n\tpush offset backinv86\t; IRETDV86._Eip\r\n\tmovzx ebp,sp\r\n\r\n;--- set a GDTR on the stack\r\n\r\n\tmov dx,ds\r\n\tmovzx edx,dx\r\n\tshl edx,4\r\n\tmovzx eax, word ptr [code32+5]\t; get offset GDT from jemm32.bin\r\n;--- set register parameters for 32-bit monitor init\r\n\tlea edi, [edx+offset jemmini]\r\n\tlea ebx, [edx+offset _brptab]\r\n\tlea esi, [edx+offset code32]\r\n\tadd eax, esi\r\n\r\n\tpush eax\r\n\tpush GDT_SIZE-1\r\n\tCLI\r\n\t.386p\r\n\tLGDT FWORD PTR [bp-6]\r\n\tadd sp, 2+4\r\n\r\nFLAT_CODE_SEL equ 1*8\r\nFLAT_DATA_SEL equ 2*8\r\n\r\n\tMOV EAX,CR0 \t\t\t; Set PE-Bit in CR0\r\n\tOR AL,1\r\n\tMOV CR0,EAX\r\n\r\n\tadd EBP, EDX\t\t\t; EBP = flat address of SS:SP\r\n\r\n\tXOR AX,AX\r\n\tLLDT AX \t\t\t\t; initialise LDT (it is not used)\r\n\t.386\r\n\tmov FS,AX\r\n\tmov GS,AX\r\n\tMOV AL,FLAT_DATA_SEL\t; Addressing everything\r\n\tMOV DS,AX\r\n\tMOV ES,AX\r\n\tmov SS,AX\r\n\tmov ESP,EBP \t;--- SS:ESP -> IRETDV86 struct\r\n\tpushd FLAT_CODE_SEL\r\n\tpush esi\r\n\tretd\r\n\r\nbackinv86:\r\n\r\n;--- the remaining commands are executed in v86-mode\r\n;--- the virtual monitor init code has returned with\r\n;--- CS, DS, ES, SS, FS, GS = unchanged\r\n;--- SP -> \"behind\" IRETDV86\r\n;--- EAX = physical address of start of EMS/VCPI memory\r\n;--- interrupts are still disabled\r\n\r\n\tpush eax\t\t\t\t; FIRSTPAGE value\r\n\r\n\t@dprintf ?INITRMDBG, <\"InitJemm: V86 mode entered\",10>\r\n\r\n\tcall GetRes\t\t;get Jemm resident part\r\n\tmov bx,ax\r\n\tpush es\r\nif ?INTEGRATED\r\n\tmov es,bx\r\n\tmov word ptr es:[xms_handle_table.xht_pArray+2],bx\r\n\tmov word ptr [xms.Driver+2],bx\t;v5.80 adjust XMS address\r\nendif\r\nif ?VDS\r\n\tpush 0\r\n\tpop es\r\n\tmov ax,cs\r\n\tcmp ax,word ptr es:[4Bh*4+2]\r\n\tjnz @@novds\r\n\tmov es:[4Bh*4+2],bx\r\n@@novds:\r\nendif\r\n\tpop es\r\n\r\n\tsti\r\n\r\n\t@dprintf ?INITRMDBG, <\"InitJemm: ints enabled, hooking int 15h, 2Fh, ...\",10>\r\n\tmov si, offset intvecs\r\n@@nextint2:\r\n\tmov al,[si].INTMOD.bInt\r\n\tcmp al,-1\r\n\tjz @@intmoddone2\r\n\tmov ah,25h\r\n\tmov dx,[si].INTMOD.wNew\r\n\tpush ds\r\n\tmov ds,bx\r\n\tint 21h\r\n\tpop ds\r\n\tadd si,size INTMOD\r\n\tjmp @@nextint2\r\n\r\n@@intmoddone2:\r\n\r\n\tcall endinit\r\n\r\nife ?INTEGRATED\r\n\t@dprintf ?INITRMDBG, <\"InitJemm: calling InstallXMSHandler\",10>\r\n\tcall InstallXMSHandler\r\n if ?MOVEXBDA and not ?MOVEXBDAPM\r\n\tcall move_xbda ;must be called after InstallXMSHandler\r\n endif\r\nendif\r\n\r\n\tpop eax  ;load FIRSTPAGE value in EAX\r\n\r\n\t@dprintf ?INITRMDBG, <\"InitJemm: exit, [bp]=%X %X %X %X\",10>,[bp+0],[bp+2],[bp+4],[bp+6]\r\n\r\n\tpop bp\r\n\tpop di\r\n\tpop si\r\n\tret\r\n\r\nInitJemm ENDP\r\n\r\n;--- get Jemm resident segment address\r\n;--- may not be CS if Jemm has been moved in an UMB\r\n\r\nGetRes proc\r\n\tmov ax,jemmini.ResUMB\r\n\tand ax,ax\r\n\tjz @F\r\n\tret\r\n@@:\r\n\tmov ax,cs\r\n\tstc\r\n\tret\r\nGetRes endp\r\n\r\n;--- end of initialization\r\n;--- 1. init XMS handle table for integrated version\r\n;--- 2. link resident part in driver chain if moved high\r\n\r\nendinit proc\r\n\r\nif ?INTEGRATED\r\n\r\n;--- initialize XMS handles which haven't been set yet\r\n\r\n\tcall GetRes\t;get Jemm resident segment address\r\n\tmov es,ax\r\n\tmov di, offset xms_handle_array\r\n\tmov cx, xms.num_handles\r\n\tmov ax, sizeof XMS_HANDLE\r\n\tmul cx\r\n\tadd ax,di\r\n\tmov cx,ax\r\n;--- don't modify the handles that were initialized by v86 monitor code!\r\n\t.while es:[di].XMS_HANDLE.xh_flags != XMSF_INPOOL \r\n\t\tadd di, sizeof XMS_HANDLE\r\n\t.endw\r\n\t.while di < cx\r\n\t\tmov ax, XMSF_INPOOL\r\n\t\tstosw\r\n\t\txor ax, ax\r\n\t\tstosw\r\n\t\tstosw\r\n\t\tstosw\r\n\t\tstosw\r\n\t.endw\r\n\tmov ax, di\r\n\tadd ax, 16-1\r\n\tand al, 0F0h\r\n\tmov [wLow], ax\r\n\t@dprintf ?INITRMDBG, <\"endinit: XMS handle array intialized\",10>\r\nendif\r\n\r\nif ?INTEGRATED\r\n if ?MOVEXBDA and not ?MOVEXBDAPM\r\n\tcall move_xbda\r\n endif\r\nendif\r\n\r\nif ?ADDCONV\r\n\r\n;--- if conventional memory has been increased (i.e. by I=A000-B7FF),\r\n;--- increase last DOS MCB\r\n\tmov ah,52h\t\t;get LoL\r\n\tint 21h\r\n\tmov si, es:[bx-2]\r\nnextitem:\r\n\tmov es, si\r\n\tmov dx, es:[MCB.blsiz]\t;get size of MCB\r\n\tinc dx\r\n\tadd dx, si\t\t;dx = next block\r\n\tcmp byte ptr es:[MCB.sig], 'M'\r\n\tjnz @F\r\n\tmov si, dx\r\n\tjmp nextitem\r\n@@:\r\n\tpush ds\r\n\tpush 0\r\n\tpop ds\r\n\tmov ax,ds:[@MEM_SIZE]\r\n\tpop ds\r\n\tshl ax,6\t;kB to paragraphs\r\n\r\n;--- v5.80: do jmp even if ax = dx+1\r\n;--- this might be the case if UMBs are already included?\r\n;--- if UMBs are already included, the last low-mem DOS MCB\r\n;--- should NOT be increased!\r\n\tinc dx\r\n\r\n\tcmp ax,dx\r\n\tjbe noconv_increased\t;do nothing if BIOS mem is lower or equal\r\n\tsub ax, si\r\n\tdec ax\r\n\tmov es:[MCB.blsiz],ax\r\nnoconv_increased:\r\n\t@dprintf ?INITRMDBG, <\"endinit: done MCB chain check\",10>\r\nendif\r\n\r\nif ?MOVEHIGH\r\n\tcall GetRes\r\n\tjc @F               ;if carry set, we didn't move high\r\n\tmov [wLow],0\t\t;add the EMMXXXX0 driver to the driver chain\r\n\tcall LinkChain\t\t;if we moved high\r\n@@:\r\nendif\r\n\t@dprintf ?INITRMDBG, <\"endinit: exit\",10>\r\n\tret\r\nendinit endp\r\n\r\nif ?MOVEXBDA and not ?MOVEXBDAPM\r\nmove_xbda proc\r\n\ttest jemmini.V86Flags,V86F_MOVEXBDA\r\n\tjz nomovexbda\r\n\tpush 0\r\n\tpop es\r\n\tmov ax,es:[@XBDA]\r\n\tmov dx,es:[@MEM_SIZE]\r\n\t@dprintf ?INITRMDBG, <\"move_xbda: xbda seg=%X, convmem=%X kb\",10>, ax, dx\r\n\tshl dx,6\r\n\tcmp ax,dx\t\t;XBDA must be just above conventional memory\r\n\tjnz nomovexbda\t;else it a) has been moved already b) doesn't exist\r\n\tmov es,ax\r\n\tmovzx si,byte ptr es:[0]\t;byte 0 of XBDA contains size in kB\r\n\tshl si,6\t\t;convert kb to para\r\n\tmov dx,si\r\n\tmov ah,XMS_ALLOCUMB\t;alloc UMB\r\n\tcall [xms.Driver]\r\n\tcmp ax,1\r\n\tjnz nomovexbda\r\n\t@dprintf ?INITRMDBG, <\"move_xbda: alloc umb ok, seg=%X\",10>, bx\r\n;--- copy content of XBDA to new location\r\n\tmov es,bx\r\n\tpushf\r\n\tcli\r\n\tpush ds\r\n\tpush 0\r\n\tpop ds\r\n\txchg bx,ds:[@XBDA]\t;set new location of XBDA\r\n\tmov ax,si\r\n\tshr ax,6\t;convert para to kb\r\n\tadd word ptr ds:[@MEM_SIZE],ax\r\n\tmov ds,bx\r\n\tmov cx,si\r\n\tshl cx,3    ;convert para to word\r\n\tpush si\r\n\txor si,si\r\n\txor di,di\r\n\trep movsw\r\n\tpop si\r\n\tpop ds\r\n\tpopf\r\n\r\n;--- get last MCB and increase its size by size of XBDA\r\n\tmov ah,52h\r\n\tint 21h\r\n\tmov ax, es:[bx-2]\r\nnxtitem:\r\n\tmov es, ax\r\n\tmov dx, es:[MCB.blsiz]\t;get size of MCB\r\n\tinc dx\r\n\tadd dx, ax\t\t;dx = next block\r\n\tcmp es:[MCB.sig], 'M'\r\n\tjnz @F\r\n\tmov ax, dx\r\n\tjmp nxtitem\r\n@@:\r\n\tadd es:[MCB.blsiz],si\r\n\r\n\tcmp bVerbose,0\r\n\tjz @F\r\n\tinvoke printf, CStr(\"XBDA moved to first UMB\",10)\r\n@@:\r\nnomovexbda:\r\n\tret\r\nmove_xbda endp\r\nendif\r\n\r\nLinkChain proc\r\n\tmov ah,52h   ;get DOS LoL in ES:BX\r\n\tint 21h\r\n\tpush ds\r\n\tpush 0\r\n\tpop ds\r\n\tmov ax,ds:[67h*4+2]\r\n\tmov ds,ax\r\n\tadd bx,22h   ;LoL+22h: NUL device (DOS 3.1+)\r\n\tshl eax,16\r\n\txchg eax,es:[bx]\r\n\tmov ds:[0],eax\r\n\tpop ds\r\n\tret\r\nLinkChain endp\r\n\r\nife ?INTEGRATED\r\n\r\n;--- InstallXMSHandler\r\n\r\nInstallXMSHandler proc\r\nife ?A20XMS \t\t\t\t\t;if there is no XMS A20 trapping\r\n\tcmp jemmini.NumUMBs,0\t\t;XMS hook is needed *only* for UMBs.\r\n\tjz @@umbdone\t\t\t\t;dont install if no UMBs are supplied\r\nendif\r\n\t@dprintf ?INITRMDBG, <\"InstallXMSHandler: getting XMS UMB status\",10>\r\n\tmov dx, -1\r\n\tmov ah, XMS_ALLOCUMB\r\n\tcall [xms.Driver]\r\n\tand ax, ax\r\n\tjnz @@umbalreadythere\r\n\t@dprintf ?INITRMDBG, <\"InstallXMSHandler: hooking into XMS driver chain\",10>\r\n\tles bx,[xms.Driver]\r\n@@nexttest:\r\n\tmov al,es:[bx]\r\n\tcmp al,0EBh\r\n\tjz @@endofchain\r\n\tles bx,es:[bx+1]\r\n\tcmp al,0EAh\r\n\tjz @@nexttest\r\n;--- unexpected pattern found in XMS hook chain\r\n\t@dprintf ?INITRMDBG, <\"InstallXMSHandler: unexpected pattern found in XMS hook chain\",10>\r\n\tjmp @@umbdone\r\n@@endofchain:\r\n\t@dprintf ?INITRMDBG, <\"InstallXMSHandler: end of chain found\",10>\r\n\tcli\r\n\tmov byte ptr es:[bx+0],0EAh\r\n\tmov word ptr es:[bx+1],offset XMShandler\r\n\tmov cl,jemmini.NumUMBs\r\n\tpush ds\r\nif ?MOVEHIGH\r\n\tpush 0\r\n\tpop ds\r\n\tmov ax,ds:[67h*4+2]\r\nelse\r\n\tmov ax, cs\r\nendif\r\n\tmov es:[bx+3], ax\r\n\tadd bx,5\r\n\tmov ds, ax\r\n;\tassume DS:DGROUP\r\n\tmov word ptr ds:[XMSoldhandler+0],bx\r\n\tmov word ptr ds:[XMSoldhandler+2],es\r\nif ?A20XMS\r\n\tcmp cl,0\r\n\tjnz @@xmswithumb\r\n\tmov byte ptr ds:[XMSUMB], 0EAh\t;skip UMB code if no UMBs are supplied\r\n\tmov word ptr ds:[XMSUMB+1], bx\r\n\tmov word ptr ds:[XMSUMB+3], es\r\n@@xmswithumb:\r\nendif\r\n\tpop ds\r\n;\tassume DS:DGROUP\r\n\tsti\r\n\tjmp @@umbdone\r\n@@umbalreadythere:\r\n\tmov dx, CStr(\"UMB handler already installed, not installing another one\",CR,LF,'$')\r\n\tmov ah,9\r\n\tint 21h\r\n@@noxms:\r\n@@umbdone:\r\n\tret\r\nInstallXMSHandler endp\r\n\r\nendif\r\n\r\n;--- init XMS\r\n;--- for the integrated version, init some variables\r\n;--- for the EMM-only version, ensure that an XMS host is found\r\n\r\nXMSinit proc c public uses es\r\n\tmov ax, 4300h\r\n\tint 2fh\r\n\tcmp al, 80h\r\n\tjne @@not_detected\r\n\tmov ax, 4310h\r\n\tint 2fh\r\n\tmov word ptr [xms.Driver+0], bx\r\n\tmov word ptr [xms.Driver+2], es\r\n\r\n\tmov ax, 4309h\t\t;  XMS get xms handle table\r\n\tint 2fh\r\n\tcmp al,43h\r\n\tjne @@no_table\r\n\tmov word ptr jemmini.XMSHandleTable+0, bx\r\n\tmov word ptr jemmini.XMSHandleTable+2, es\r\n@@no_table:\r\n\tmov ax,1\r\n\tret\r\n@@not_detected:\r\nif ?INTEGRATED\r\n\tmov word ptr [xms.Driver+0], offset XMShandler\r\n\tmov word ptr [xms.Driver+2], cs\r\n\tmov word ptr jemmini.XMSHandleTable+0, offset xms_handle_table\r\n\tmov word ptr jemmini.XMSHandleTable+2, cs\r\nendif\r\n\txor ax,ax\r\n\tret\r\nXMSinit endp\r\n\r\nif ?INTEGRATED\r\n\r\n;--- set entry in XMS handle array, sizeK in ecx, baseK in edx\r\n\r\nI15SetHandle proc c public uses ds bx\r\n\r\n;--- first, do a few checks\r\n\tcmp edx, 1024\t\t;does the block start at 0x100000?\r\n\tjnz @F\r\n\tadd edx, 64\t\t\t;then exclude the first 64 kB for HMA\r\n\tsub ecx, 64\r\n\tjc exit\r\n@@:\r\nif ?XMS35\r\n\tcmp edx, 400000h    ;beyond 4GB?\r\n\tjc @F\r\n\tsub xms.smax, ecx\t;/MAXSEXT option set?\r\n\tjnc smax_ok\r\n\tadd ecx, xms.smax\t;limit to maximum\r\n\tmov xms.smax,0\r\n\tjecxz exit\r\nsmax_ok:\r\n\tmov xms.smem_used, 1\r\n\tjmp beyond4g\r\n@@:\r\nendif\r\n\tsub xms.max, ecx\t;MAXEXT option set?\r\n\tjnc @F\r\n\tadd ecx, xms.max\t;limit to maximum\r\n\tmov xms.max,0\r\n\tjecxz exit\r\n@@:\r\n;--- adjust xms.mem_free and xms.mem_largest fields, if needed\r\n\tadd xms.mem_free, ecx\r\n\tcmp ecx, xms.mem_largest\r\n\tjb @F\r\n\tmov xms.mem_largest, ecx\r\n@@:\r\n\tmov eax, edx\r\n\tadd eax, ecx\r\n\tshl eax, 10\r\n\tcmp eax, xms.mem_highest\r\n\tjb @F\r\n\tmov xms.mem_highest, eax\r\n@@:\r\nbeyond4g:\r\n\tpush cs\r\n\tpop ds\r\n\tmov bx, offset xms_handle_array\r\n\t.while [bx].XMS_HANDLE.xh_flags != XMSF_INPOOL\r\n\t\tadd bx, sizeof XMS_HANDLE\r\n\t.endw\r\n\tmov [bx].XMS_HANDLE.xh_flags, XMSF_FREE\r\n\tmov [bx].XMS_HANDLE.xh_baseK, edx\r\n\tmov [bx].XMS_HANDLE.xh_sizeK, ecx\r\nexit:\r\n\tret\r\nI15SetHandle endp\r\n\r\n;--- I15AllocMemory(int dummy, long kbneeded);\r\n\r\nI15AllocMemory proc stdcall public uses ds si dummy:WORD, kbneeded:DWORD\r\n\r\n\tpush cs\r\n\tpop ds\r\n\tmov ecx, kbneeded\r\n\txor si, si\r\n\tmov bx, offset xms_handle_array\r\n\r\n;--- scan handle table for a free block that is large enough\r\n\t.while [bx].XMS_HANDLE.xh_flags != XMSF_INPOOL\r\n\t\tcmp si,0\r\n\t\tjnz @F\r\n\t\tcmp [bx].XMS_HANDLE.xh_flags, XMSF_FREE\r\n\t\tjnz @F\r\n\t\tcmp [bx].XMS_HANDLE.xh_sizeK, ecx\r\n\t\tjb @F\r\nif ?XMS35\r\n\t\ttest word ptr [bx].XMS_HANDLE.xh_baseK+2, 0FFC0h\r\n\t\tjnz @F\r\nendif\r\n\t\tmov si,bx\r\n@@:\r\n\t\tadd bx,sizeof XMS_HANDLE\r\n\t.endw\r\n\t.if si\r\n;--- a block with sufficient size has been found, and\r\n;--- bx points to a free handle now, which will hold\r\n;--- the remaining memory of the block\r\n\t\tmov eax, [si].XMS_HANDLE.xh_sizeK\r\n\t\tsub eax, ecx\r\n\t\tmov [si].XMS_HANDLE.xh_sizeK, ecx\r\n;--- v5.80: don't lock block, so it can be \"reallocated\" in monitor\r\n;\t\tmov [si].XMS_HANDLE.xh_locks, 1\r\n\t\tmov [si].XMS_HANDLE.xh_flags, XMSF_USED\r\n\t\tadd ecx, [si].XMS_HANDLE.xh_baseK\r\n;--- if eax (=size) is zero, no new handle is needed\r\n\t\tand eax, eax\r\n\t\tjz @F\r\n\t\tmov [bx].XMS_HANDLE.xh_flags, XMSF_FREE\r\n\t\tmov [bx].XMS_HANDLE.xh_sizeK, eax\r\n\t\tmov [bx].XMS_HANDLE.xh_baseK, ecx\r\n@@:\r\n\t.endif\r\n\tmov ax, si\r\n\tret\r\nI15AllocMemory endp\r\n\r\n;--- get the base of a XMS handle\r\n\r\nGetEMBBase proc stdcall public wHdl:word\r\n;--- to be fixed: use the wHdl parameter\r\n\tmov eax, xms_handle_array.xh_baseK\r\n\tshl eax, 10\r\n\tret\r\nGetEMBBase endp\r\n\r\nendif\r\n\r\n\tend start\r\n"
  },
  {
    "path": "src/JEMM16.INC",
    "content": "\r\n;--- globals for jemm16.asm and init16.asm\r\n\r\nif ?INTEGRATED\r\nNAMEEXE equ <\"JEMMEX\">\r\nNAMEMOD equ <\"JemmEx\">\r\nelse\r\nNAMEEXE equ <\"JEMM386\">\r\nNAMEMOD equ <\"Jemm386\">\r\nendif\r\n\r\n;--- modes for mainex()\r\nEXECMODE_SYS equ 0\t;launched as dos device driver\r\nEXECMODE_EXE equ 1\t;launched as application\r\n\r\nPF16 TYPEDEF far16 ptr\r\n\r\nXMSPARAMS struct\r\nDriver      PF16 0\r\nmem_largest DD 0\t;largest free block in kB\r\nmem_free    DD 0\t;total free mem in kB\r\nmem_highest dd 0\t;highest physical address; by XMS v3 (Jemm386) or Int15 (JemmEx)\r\nif ?INTEGRATED\r\nmax         DD 4095*1024\t;MAXEXT= value\r\n if ?XMS35\r\nsmax        DD 1023*4096*1024\t;MAXSEXT= value\r\nmaxhigh     dd -1\t; mask for upper memory address limit   \r\n endif\r\nnum_handles DW ?XMS_DEFHDLS\t;XMSHANDLES= value\r\n if ?XMS35\r\nsmem_used   db 0\t;set to 1 if any super-extended block is used\r\n endif\r\nendif\r\nXMSPARAMS ends\r\n\r\nInitJemm        proto c\r\nIsJemmInstalled proto c\r\nEmmInstallcheck proto c\r\nXMSinit         proto c\r\nCheckIntHooks   proto stdcall :WORD\r\nUnloadJemm      proto c\r\nif ?INTEGRATED\r\n?XMS_STATICHDLS equ 10\t;free xms handles to be used for int 15h, ax=e820h\r\nI15SetHandle    proto c\r\nI15AllocMemory  proto stdcall :WORD, :DWORD\r\nGetEMBBase      proto stdcall: word\r\nendif\r\nmainex          proto c :WORD, :ptr BYTE\r\nprintf          proto c :ptr byte, :VARARG\r\n_memicmp        proto c :ptr BYTE, :ptr BYTE, :WORD\r\n\r\nexterndef c jemmini:JEMMINIT\r\nexterndef c xms:XMSPARAMS\r\nexterndef c sig1:BYTE\r\nexterndef c sig2:BYTE\r\nexterndef c bVerbose:byte\r\n\r\nif ?INTEGRATED\r\n if ?XMS35\r\nexterndef c xms_smax_noe820:byte\r\n?XMS_DEFHDLS equ 48\r\n else\r\n?XMS_DEFHDLS equ 32\r\n endif\r\n?XMS_MAXHDLS equ 128\r\nendif\r\n"
  },
  {
    "path": "src/JEMM32.ASM",
    "content": ";*****************************************************************************\r\n;** This is the main 32bit ASM part of JEMM.\r\n;**\r\n;** JEMM contains code of FreeDOS Emm386, which in turn used the source of\r\n;** an EMM made public in c't (a german IT magazine) in 08/90, page 214,\r\n;** written by Harald Albrecht.\r\n;**\r\n;** some parts the code which is based on FD Emm386 is copyright protected and\r\n;** licensed under the Artistic License version (see LICENSE.TXT for details).\r\n;**\r\n;** 1. EMS 3.2 functions (file EMS32.INC)\r\n;**   (c) 1990       c't/Harald Albrecht\r\n;**   (c) 2001-2004  tom ehlert\r\n;** 2. DMA support (file DMA.ASM)\r\n;**   (c) 1990       c't/Harald Albrecht\r\n;** 3. privileged opcode emulation (file EMU.ASM)\r\n;**   (c) 2001-2004  tom ehlert\r\n;**\r\n;** The rest of the 32bit source is Public Domain.\r\n;**\r\n;*****************************************************************************\r\n    TITLE JEMM - Virtual 8086-Monitor\r\n    NAME JEMM\r\n\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n;--- equates\r\n\r\n?STKSIZE    EQU 200H    ; reserved stack space for v86 entry into monitor.\r\n\r\n;--- assembly time constants\r\n\r\n?NMI        equ 1       ; std=1, 1=allow NMIs inside the monitor;\r\n?SKIPINT06  equ 1       ; std=1, 1=skip routing v86 exc to v86 int06 if noone hooked int 6\r\n?ISRFLGS    equ 0       ; std=0, 1=display PIC ISR flags\r\n;?CLEARBOOTFLGS equ 0    ; std=0, 1=clear boot flags in XBDA\r\n?RESETCR4   equ 1       ; std=1, 1=reset CR4 at reboot\r\n?INT19RBT   equ 0       ; std=0, 1=emit INT 19h on reboot\r\n\r\nif ?INTEGRATED\r\n?FREEXMS    equ 1       ; std=1, 0 might work\r\n?NAME       equ <\"JemmEx\">\r\nelse\r\n?FREEXMS    equ 1       ; std=1, 1=free all XMS on exit\r\n?NAME       equ <\"Jemm386\">\r\nendif\r\n\r\n?SELINTLOG equ ?V86DBG\r\n\r\nMAXBLOCKSIZE equ 40000h ; size for mem moves (XMS, int 15h ah=87h); std=256 kB\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n;--- macros\r\n\r\n@v86popreg macro\r\n    POPAD\r\n    endm\r\n@v86popregX macro\r\n    mov esp,ebp\r\n    POPAD\r\n    endm\r\n\r\n.text$01 SEGMENT\r\n\r\n;--- start: the binary's entry must be at offset 0!\r\n\r\n    public _start\r\n\r\n_start:\r\n    jmp InitMonitor\r\n    dw lowword offset V86GDT ; must be at .text:5, so 16-bit code can locate the GDT\r\nsize_startsig equ $ - _start\r\n\r\n;--- this is a 128 byte helper stack used for VCPI switches to\r\n;--- protected-mode.\r\n\r\n    db ?HLPSTKSIZE - size_startsig dup (55h)\r\n\r\n;--- global monitor data\r\n\r\nV86CR3  DD 0                ; CR3 for monitor address context\r\n\r\n;--- IDT - Interrupt Descriptor Table\r\n;--- as default, the IDT is no longer in the shared region\r\n\r\nIDT_PTR LABEL FWORD         ; size+base IDT\r\n    DW 7FFH\r\nif ?SHAREDIDT\r\n    DD offset V86IDT\r\nelse\r\ndwV86IDT DD 0\r\nendif\r\nwMasterPICBase  dw 0008h    ; put it here for alignment\r\n\r\nGDT_PTR LABEL FWORD         ; size+base GDT\r\n    DW GDT_SIZE-1\r\n    DD offset V86GDT\r\n\r\nwSlavePICBase   dw 0070h    ; put it here for alignment\r\n\r\ndwFeatures      dd 0        ; features from CPUID \r\ndwStackCurr     dd ?TOS - ?STKSIZE\r\ndwStackR0       dd 0        ; current R0 stack to compare if monitor reentered by IRQ\r\nif ?DYNTRAPP60\r\ndwTSS           dd 0\r\nendif\r\ndwMaxPhysMem    dd 0        ; XMS highest address (r/o)\r\nbpstart         dd 0        ; linear address start of static bp array in Jemm16.RSEG\r\nPageMapHeap     dd ?SYSBASE+?PAGETABSYS+6*4\r\n\r\n;--- address space heap for 4MB pages (PDEs).\r\n;--- v5.85: the heap starts at SYSBASE - 8MB ; it's usually max 4 PTEs ( 16MB ), but is\r\n;--- reentrant and grows downwards.\r\n;--- previously the heap did start at 400000h and grew upwards,\r\n;--- but this address region may be used by VMM's _PageReserve() function (PR_PRIVATE)\r\nHeap4MB         dd ?PAGEDIR + (?SYSBASE shr 20) - 2*4\t;linear address of 4MB page heap inside page directory\r\n\r\npSmallHeap      label dword ; used during Init only\r\nOldInt06        dd 0\r\ndwHeapSize      label dword ; used during Init only\r\nOldInt19        dd 0\r\nOldInt67        dd 0\r\n;pPg0PartEnd     dd 0\r\n\r\n    public  pSmallHeap\r\n    public  dwHeapSize\r\n\r\ndwRes           dd  0   ; linear address of resident segment\r\nif ?HOOK13\r\ndwRFlags        dd  0   ; linear address bRFlags\r\nendif\r\ndwRSeg          dd  0   ; resident segment\r\nif ?FASTBOOT\r\npSavedVecs      dd 0    ; vectors saved during startup\r\n;dwInt19Org     dd 0    ; original int 19 vector saved by DOS\r\nendif\r\n\r\n;--- next 2 vars must be consecutive\r\npV86Hooks       dd 0    ; v86 hook proc installed? ( address of variable exported by VMM_SERV_TABLE )\r\ndwHookProc      dd 0\r\nif ?NMI\r\nOldESP          dd 0    ; v5.87; used to temp. store ESP in NMI handling\r\nendif\r\n\r\n;-- byte vars (variable)\r\n\r\n;-- byte vars (rather constant)\r\n\r\nbNoPool         DB  0   ; flags pooling with XMS memory\r\nbNoInvlPg       DB  -1  ; <> 0 -> dont use INVLPG\r\nbV86Flags       DB  0   ; other flags \r\nbReentered      db  0   ; count exception handler reentered\r\n\r\nbIs486          db  0   ; cpu is 486 or better (INVLPG opcode available)\r\nif ?PGE\r\nbPageMask       db  0   ;mask for PTEs in 0-110000h\r\nendif\r\nbBpTab          db  0   ;offset start bptable in RSEG\r\nbBpBack         db  BPTABLE.pBack shr 2\r\nife ?HOOK13\r\nbDiskIrq        db  0   ; bit0: 1=diskette transfer pending\r\nendif\r\nif ?KD\r\nbKD             db  0   ; bit0: 1=kernel debugger detected and initialized\r\nendif\r\n\r\n    align 4\r\n\r\nRunEmuInstr:\r\nEmuInstr    DB  90h,90h,90h ; run self-modifying code here\r\n_ret:\r\n            ret\r\n\r\n;--- place rarely modified data here\r\n;--- so data and code is separated by at least 64 \"constant\" bytes\r\n\r\nif ?ROMRO\r\ndwSavedRomPTE dd ?  ;saved PTE for FF000 page faults\r\ndqSavedInt01  dq ?  ;saved INT 01\r\nendif\r\n\r\n.text$01 ends\r\n\r\nif ?SHAREDGDT\r\n.text$01g segment para flat public 'CODE'\r\nelse\r\n.text$03x segment para flat public 'CODE'\r\nendif\r\n\r\n;--- GDT - Global Descriptor Table\r\n;--- usually ?SHAREDGDT==1, so the GDT is in the shared region\r\n\r\nV86GDT LABEL DESCRIPTOR\r\n    DQ 0                                ; +00 NULL-Entry\r\n    DESCRIPTOR <-1,0,0,9AH,0CFh,0>      ; +08 flat 4 GB Code (32 Bit)\r\n    DESCRIPTOR <-1,0,0,92H,0CFh,0>      ; +10 flat 4 GB Data (32 Bit)\r\n    DESCRIPTOR <?TSSLEN-1,0,0,89H,0,0>  ; +18 TSS V86\r\n    DESCRIPTOR <-1,0,0,9AH,0,0>         ; +20 std 64k Code Descriptor\r\n    DESCRIPTOR <-1,0,0,92H,0,0>         ; +28 std 64k Data Descriptor\r\n    DESCRIPTOR <?HLPSTKSIZE-1,0,?BASE shr 16,92H,040h,?BASE shr 24>; helper stack segment\r\n    DQ 0                                ; +38\r\n    DESCRIPTOR <2FFh,400h,0,0F2H,0,0>   ; +40\r\nif ?KD\r\n    DESCRIPTOR <GDT_SIZE-1,0,0,0F2H,0,0>; +48 (=?GDTSEL) GDT selector\r\nendif\r\n\r\n;    DESCRIPTOR (GDT_SIZE - 9*8)/8 dup (<0,0,0,0,0,0>)\r\n\torg V86GDT+GDT_SIZE\r\n\r\nif ?SHAREDGDT\r\n.text$01g ends\r\nelse\r\n.text$03x ends\r\nendif\r\n\r\nif ?SHAREDIDT\r\n    public V86IDT\r\n.text$01g segment para flat public 'CODE'\r\nV86IDT  GATE 100h dup (<0,0,0,0>)\r\n.text$01g ends\r\nendif\r\n\r\n.text$03 segment\r\n\r\n;--- v86 breakpoint table;\r\n;--- order must match the one defined in Jemm16.asm!\r\n\r\nbptable BPTABLE <>\r\n    org bptable\r\n    dd Int06_V86Entry       ; BP06, default v86 int 06 handler\r\n    dd Int19_V86Entry       ; BP19, Reboot\r\n    dd Int67_V86Entry       ; BP67, default v86 int 67 handler\r\nif ?VDS\r\n    dd vds_handler          ; BPVDS, VDS handler, v86 int 4Bh\r\nendif\r\n    dd V86_Back             ; BPBACK, return to pm after calling v86-code\r\n    dd I15_Simulate87       ; BP1587, simulate Int 15h, ah=87h\r\nif ?HOOK13\r\n    dd Dma_CopyBuffer       ; BP1340, INT13/40 DMA read op thru buffer\r\nendif\r\n    dd xms_handler          ; BPXMS, XMS handler (UMB+A20)\r\n    dd EMMXXXX0_Strategy    ; BPSTRAT, EMMXXXX0 device strategy routine\r\n    dd EMMXXXX0_Interrupt   ; BPDEV, EMMXXXX0 device interrupt routine\r\nif ?UNLOAD\r\n    dd Unload               ; BPUNL, unload Jemm, return to real-mode\r\nendif\r\n\r\nNUMBP equ (size BPTABLE)/4\r\n\r\n;--- IO trapping table.\r\n;--- IO_Trap_Table is exported (vmm_service_table)\r\n;--- and may be modified by JLOAD!\r\n;--- Default_IO_Trap_Handler() can handle byte ports only!\r\n\r\n;--- JLoad expects exactly this structure:\r\n;---  dd ?   ; trap handler\r\n;---  dd ?   ; no of IOTRAPENTRY in following array\r\n;---  IOTRAPENTRY[]\r\n;--- v5.84: don't trap ports 80h, 8Ch-8Fh, ( 8Fh = page reg of channel 4 )\r\n;---        C0h-C3h (channel 4)\r\n\r\nif ?ALT_TRAPHDL\t;v5.85\r\nAlternate_IO_Traphandler dd ISA_DMA_Traphandler\r\nendif\r\nIO_Trap_Table label dword\r\nIO_Trap_Handler dd Default_IO_Trap_Handler\r\n    dd (offset endportmap - offset portmap) / sizeof IOTRAPENTRY\r\n\r\nportmap label byte\r\nif ?DMAPT\r\n    IOTRAPENTRY <000h,00Fh,Dma_HandleDmaPorts8>\r\n    IOTRAPENTRY <081h,08Bh,Dma_HandlePagePorts>\r\n    IOTRAPENTRY <0C4h,0DFh,Dma_HandleDmaPorts16>\r\nendif\r\nif ?A20PORTS\r\n    IOTRAPENTRY <060h,060h,A20_Handle60>\r\n    IOTRAPENTRY <064h,064h,A20_Handle64>\r\n    IOTRAPENTRY <092h,092h,A20_Handle92>\r\nendif\r\nendportmap label byte\r\n\r\n    align 4\r\n\r\nif ?ROMRO\r\n\r\nPageFaultFF proc\r\n\r\n    @dprintf ?V86DBG, <\"Write access to FF000\",10>\r\n    mov ebx, @GetPTEAddr(?PAGETAB0+0FFh*4)\r\n    mov ecx, [ebx]  ;get PTE for FF000\r\n    mov dword ptr [ebx], 0FF000h + 111B ;set the \"original\" PTE\r\n    mov [dwSavedRomPTE], ecx\r\n    call @@invlpgFF\r\n\r\n    mov ecx, offset @@pagefaultcont\r\n    mov ax, FLAT_CODE_SEL\r\n    shl eax, 16\r\n    mov ax, cx\r\n    mov cx, 0EE00h\r\n\r\nif ?SHAREDIDT\r\n    mov ebx, offset V86IDT\r\nelse\r\n    mov ebx, dwV86IDT\r\nendif\r\n    xchg eax, [ebx+1*8+0]\r\n    xchg ecx, [ebx+1*8+4]\r\n    mov dword ptr [dqSavedInt01+0], eax\r\n    mov dword ptr [dqSavedInt01+4], ecx\r\n    or byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, 1  ;TF=1\r\n    @v86popregX             ; return to V86, execute 1 instruction\r\n    ADD ESP, CRS_SIZEX\r\n    iretd\r\n\r\n;--- returned to monitor after 1 instruction run in v86 mode\r\n\r\n@@pagefaultcont:\r\n    pushad\r\n    push ss\r\n    pop ds\r\n    @dprintf ?V86DBG, <\"After write access to FF000\",10>\r\n    mov ecx, [dwSavedRomPTE]\r\n    mov eax, @GetPTEAddr(?PAGETAB0+0FFh*4)\r\n    mov [eax],ecx\r\n    xor eax, eax\r\n    mov cr2, eax\r\n    mov eax, dword ptr [dqSavedInt01+0]\r\n    mov ecx, dword ptr [dqSavedInt01+4]\r\nif ?SHAREDIDT\r\n    mov ebx, offset V86IDT\r\nelse\r\n    mov ebx, dwV86IDT\r\nendif\r\n    mov [ebx+1*8+0], eax\r\n    mov [ebx+1*8+4], ecx\r\n    call @@invlpgFF\r\n    popad\r\n    and byte ptr [esp].IRETDV86._Efl+1,not 1    ;TF=0\r\n    iretd\r\n@@invlpgFF:\r\nif ?INVLPG\r\n    cmp [bNoInvlPg],0\r\n    jnz @@noinvlpg\r\n    .486p\r\n    invlpg ds:[0FF000h]\r\n    .386\r\n    ret\r\n@@noinvlpg:\r\nendif\r\n    mov eax, cr3\r\n    mov cr3, eax\r\n    ret\r\n    align 4\r\n\r\nPageFaultFF endp\r\n\r\nendif\r\n\r\n;--- By setting ?AUXIO one may change this to write to COMx;\r\n;--- this can be handled in protected-mode.\r\n\r\nPrintString proc\r\n    push esi\r\n    mov esi, [esp+4+4]\r\nnextchar:\r\n    lodsb\r\n    cmp al,0\r\n    jz done\r\n if ?AUXIO\r\n    call AuxPutChar\r\n else\r\n  if ?USEINT10\r\n    mov ah,0Eh\r\n    mov word ptr [EBP].Client_Reg_Struc.Client_EAX,ax\r\n    mov word ptr [EBP].Client_Reg_Struc.Client_EBX,0007\r\n    mov eax,10h\r\n    call Exec_Int\r\n  else\r\n    mov byte ptr [EBP].Client_Reg_Struc.Client_EAX,al\r\n    mov eax,29h\r\n    call Exec_Int\r\n  endif\r\n endif\r\n    jmp nextchar\r\ndone:\r\n    pop esi\r\n    ret 4\r\n    align 4\r\n\r\nPrintString endp\r\n\r\n;--- sprintfx - formatted output\r\n;--- edi -> output buffer\r\n;--- edx must be preserved!\r\n\r\nsprintfx proc\r\n    push esi\r\n    mov esi, [esp+4+4]\r\n@@nextchar:\r\n    lodsb\r\n    and al,al\r\n    jz @@done\r\n    cmp al,'%'\r\n    jz is_fmt\r\n    stosb\r\n    jmp @@nextchar\r\n@@done:\r\nife ?AUXIO\r\nendif\r\n    pop esi\r\n    ret 4\r\n\r\nis_fmt:\r\n    push offset @@nextchar\r\n    lodsb\r\n    mov bl,al\r\n    lodsb\r\n    movsx eax, al\r\n    mov eax, ss:[ebp+eax]   ;use SS prefix here!\r\n    cmp bl,2\r\n    jz b2a\r\n    cmp bl,4\r\n    jz w2a\r\n    jmp dw2a\r\n\r\nsprintfx endp\r\n\r\n;--- helper routines\r\n\r\ndw2a proc           ; display DWORD in eax into EDI\r\n    push eax\r\n    shr eax,16\r\n    call w2a\r\n    pop eax\r\ndw2a endp           ; fall through\r\nw2a proc            ; display WORD in ax into EDI\r\n    push eax\r\n    mov al,ah\r\n    call b2a\r\n    pop eax\r\nw2a endp            ; fall through\r\nb2a proc            ; display BYTE in al into EDI\r\n    push eax\r\n    shr al,4\r\n    call @@nibout\r\n    pop eax         ; fall through\r\n@@nibout:           ; display NIBBLE in al[0..3] into EDI\r\n    and al,0Fh\r\n    cmp al,10\r\n    sbb al,69H\r\n    das\r\n    stosb\r\n    ret\r\n    align 4\r\nb2a endp\r\n\r\nif ?AUXIO\r\n\tinclude auxio.inc\r\nendif\r\n\r\n.text$01y segment dword flat public 'CODE'\r\n\r\n;--- stack frame for exceptions\r\n\r\nEXCFRAME struct\r\nif ?ISRFLGS\r\n\torg -36\r\n_isr      dd ?\r\nendif\r\n\torg -32\r\n_cr2      dd ?\r\n_cr0      dd ?\r\n_esp      dd ?\r\n_ss       dd ?\r\n_gs       dd ?\r\n_fs       dd ?\r\n_es       dd ?\r\n_ds       dd ?\r\n\tClient_Reg_Struc <?>\r\nEXCFRAME ends\r\n\r\nexc_str label byte\r\n    DB 13,10, ?NAME\r\n    db \": exception %\",        2, EXCFRAME.Client_Int\r\n    db \" occured at CS:EIP=%\", 4, EXCFRAME.Client_CS\r\n    db ':%',                   8, EXCFRAME.Client_EIP\r\n    db \", ERRC=%\",             8, EXCFRAME.Client_Error\r\n    db 13,10\r\n    db \"SS:ESP=%\",             4, EXCFRAME._ss\r\n    db ':%',                   8, EXCFRAME._esp\r\n    db \" EBP=%\",               8, EXCFRAME.Client_EBP\r\n    db \" EFL=%\",               8, EXCFRAME.Client_EFlags\r\n    DB \" CR0=%\",               8, EXCFRAME._cr0\r\nexc_cr2 label byte\r\n    DB \" CR2=%\",               8, EXCFRAME._cr2\r\n    db 13,10\r\n    db \"EAX=%\",                8, EXCFRAME.Client_EAX\r\n    db \" EBX=%\",               8, EXCFRAME.Client_EBX\r\n    db \" ECX=%\",               8, EXCFRAME.Client_ECX\r\n    db \" EDX=%\",               8, EXCFRAME.Client_EDX\r\n    db \" ESI=%\",               8, EXCFRAME.Client_ESI\r\n    db \" EDI=%\",               8, EXCFRAME.Client_EDI\r\n    db 13,10\r\n    db \"DS=%\",                 4, EXCFRAME._ds\r\n    db \" ES=%\",                4, EXCFRAME._es\r\n    db \" FS=%\",                4, EXCFRAME._fs\r\n    db \" GS=%\",                4, EXCFRAME._gs\r\nif ?ISRFLGS\r\n    db \" ISR=%\",               4, EXCFRAME._isr\r\nendif\r\n    db ' [CS:IP]='\r\n    db 0\r\n;----------------------------------- byte  word  dword [cs:ip] crlf    %\r\nsizeexcstr equ ($ - offset exc_str) + 1*2 + 6*4 + 13*8  + 8*3   + 2 - 20*3 + (?ISRFLGS * (4-3))\r\n\r\nexc_str2 db 'Press ESC to abort program, F1/F2 for reboot ', 0\r\nexc_str3 db 13, ?NAME, ': unable to continue - press F1/F2 for reboot ', 0\r\nszCRLF db CR, LF, 0\r\n\r\n    align 4\r\n\r\n.text$01y ends\r\n\r\n;--- the monitor entry\r\n;--- it should not be at the very beginning of this segment\r\n\r\nif ?SELINTLOG\r\nlogintbm label dword\r\n;--- selective logs of INTs. default: log INT 0-7 only\r\n;      -------+-------3-------+-------2-------+-------1-------+-------0\r\n    dq 0000000000000000000000000000000000000000000000000000000011111111b\r\n;      -------+-------7-------+-------6-------+-------5-------+-------4\r\n    dq 0000000000000000000000000000000000000000000000000000000000000000b\r\n    dq 0,0\r\nendif\r\n\r\n    align 4\r\n    \r\nif ?FASTMON\r\n\r\nV86_MonitorEx:   ;v5.86: label needed because the 32 entries won't fit in 128 bytes anymore\r\n\tpush dword ptr [esp]\r\n\tjmp short V86_Monitor\r\n\talign 4\r\n\r\n;--- v5.86: \"fast\" entries 0-7 get a fake error code now;\r\n;---        thus they are handled as exceptions by the monitor.\r\n\r\nint00 label near\r\nINTNO = 0\r\nREPT ?FASTENTRIES\t;32 \"fast\" entries\r\n\tpush INTNO\r\n if INTNO LT 8\r\n\tjmp short V86_MonitorEx\r\n else\r\n\tjmp short V86_Monitor\r\n endif\r\nINTNO = INTNO+1\r\nENDM\r\n\r\nendif ;?FASTMON\r\n\r\n; all entries in the IDT (except 15h and 67h) jump to V86_Monitor.\r\n; It has to check if an exception has occured. If no, just reflect the \r\n; interrupt to v86-mode. If yes, check what the reason for the exception\r\n; was and do the appropriate actions.\r\n;\r\n; inp: the cpu has set DS,ES,FS,GS==NULL if switch from v86 mode!\r\n;  [ESP+0] = INT#\r\n\r\n;--- stack frame in V86 monitor for IRQs and software INTs\r\n\r\nV86FRAME struct\r\n        PUSHADS <>\r\n        dd ?\r\n_IntNo  dd ?\t; note that int# in V86FRAME differs from Client_Int in Client_Reg_Struc!\r\n        IRETDV86 <>\r\nV86FRAME ends\r\n\r\nV86_Monitor PROC public\r\n\r\n;--- Three cases must be handled:\r\n\r\n;--- case 1: exception (with error code) in V86 mode (08,0A,0B,0C,0D,0E);\r\n;--- on the stack: IRETDV86, error code, int#\r\n;--- v5.86: entry code for int 0-7 does now push a \"faked\" error code!\r\n;--- so they are now also treated as exceptions.\r\n\r\n    CMP ESP, ?TOS - (sizeof IRETDV86 + 2*4) ; exception with errcode in v86 mode?\r\n    JZ V86_Exception\r\n\r\n;--- case 2: EXC or IRQ (HLT emu, Yield) in v86 monitor; monitor reentered?\r\n\r\n    CMP ESP, ?TOS - (sizeof IRETDV86 + 1*4)\r\n    JNZ @@Reentered\r\n\r\n;--- case 3: normal INT/IRQ (or EXC > 7 without error code)\r\n\r\n    sub esp, 4      ; v5.85: added ( to make V86FRAME \"match\" Client_Reg_Struc )\r\n    PUSHAD\r\n    MOV EBP,ESP\t\t; ebp -> Client_Reg_Struc\r\n\r\n;if ?V86DBG and ?V86XDBG\r\n;    @dprintf ?V86DBG, <\".\">\r\n;endif\r\n\r\n;-- we don't know for sure what size the stack is.\r\n;-- There might exist other ring0 code which intruded in the\r\n;-- monitors address context and modified GDT/IDT entries\r\n;-- But at least they should have established a 32-bit stack or\r\n;-- at the very least cleared HiWord(ESP) so EBP will point to a\r\n;-- valid frame.\r\n\r\n    MOVZX ECX,byte ptr [EBP].V86FRAME._IntNo ;load 1 byte ONLY (the int# is sign-extended)!\r\n\r\nif ?V86DBG\r\n  if ?SELINTLOG\r\n    bt cs:[logintbm],ecx\r\n    jnc @F\r\n  endif\r\n    @dprintf ?V86DBG, <\"Int in v86-mode, #=%X, CS:EIP=%X:%X EBP=%X\",10>, cx, word ptr [ebp].V86FRAME._Cs,[ebp].V86FRAME._Eip, ebp\r\n@@:\r\nendif\r\n\r\n;--- v5.85: are there hooks installed?\r\n;--- prior to v5.85, Jload did install hooks by directly \r\n;--- modifying the IDT - that was not a very good idea.\r\n\r\n\tmov edx, cs:[pV86Hooks]\r\n\tcmp edx, 0\r\n\tjz @F\r\n\tcmp dword ptr cs:[edx+4*ecx], 0\r\n\tjz @F\r\n\tmov eax, ss\r\n\tmov ds, eax\r\n\tmov es, eax\r\n\tmov eax, ecx\r\n\tmov ecx, [edx+4*ecx]\r\n\tmov esp, [dwStackCurr]  ;v5.87: don't run on top of stack, it's reserved for client state\r\n\tcall [dwHookProc]\r\n\tmov esp,ebp\r\n\tjnc int_handled\r\n\tMOVZX ECX,byte ptr [EBP].V86FRAME._IntNo\r\n@@:\r\n;--- simulate an int in v86 mode\r\n\r\n    MOV ESI,[EBP].V86FRAME._Eip\r\n    MOV EDI,[EBP].V86FRAME._Cs\r\n    MOV EDX,[EBP].V86FRAME._Efl\r\n    MOVZX EAX, word ptr [EBP].V86FRAME._Esp ; use LOWORD(ESP) only!\r\n    MOVZX EBX, word ptr [EBP].V86FRAME._Ss\r\n    shl ecx,2\r\n    SHL EBX,4\r\n    sub word ptr [EBP].V86FRAME._Esp, sizeof IRETWS\r\n\r\n; copy Interrupt frame down. Use SS, since DS is unset\r\n;--- v5.87: emulate 16-bit SP correctly in case there's a wrap-around.\r\n\r\n    sub ax, 2\r\n    MOV SS:[EBX+eax],DX\r\n    sub ax, 2\r\n    MOV SS:[EBX+eax],DI\r\n    sub ax, 2\r\n    MOV SS:[EBX+eax],SI\r\n    MOV EAX,SS:[ECX]            ; route call to vector in real-mode IVT\r\n    AND DH, NOT (1 or 2)        ; Clear IF+TF as it is done in real-mode (RF should also be cleared!)\r\n    MOV word ptr [EBP].V86FRAME._Eip,AX\r\n    SHR EAX, 16\r\n    MOV [EBP].V86FRAME._Cs, EAX\r\n    MOV [EBP].V86FRAME._Efl, EDX\r\nint_handled:\r\n    @v86popreg\r\n    add ESP, CRS_SIZEX  ; skip fields between PUSHADS and IRETDV86\r\n    IRETD               ; return to v86-mode\r\n\r\n    align 4\r\n\r\n;--- Monitor has been reentered in PL0: IRQ, NMI or exception;\r\n;--- exceptions without error code will have a faked one here;\r\n;--- IRQs can only occur thru Yield() and hence will have a known ESP (dwStackR0);\r\n;--- ESP -> int#, if exc:errorc, IRETDS\r\n;--- DS,ES=valid selectors (not for NMI)\r\n\r\n@@Reentered:\r\n\r\nif ?NMI\r\n;--- v5.87: handle NMIs in PL0 before anything else\r\n\tcmp dword ptr [esp],2\r\n\tjz handle_nmi_pl0\r\nendif\r\n    PUSHAD\r\n;--- v5.87: check moved here (was in handle_exception)\r\n    mov eax, ss                 ; check SS size - just in case\r\n    lar eax, eax\r\n    test eax,400000h\r\n    jnz @F\r\n    movzx esp,sp\r\n@@:\r\n    MOV EBP,ESP\t\t; ebp -> Client_Reg_Struc if exception\r\n\r\n;--- v5.86: fixed, V86FRAME got another member in v5.85;\r\n;--- so since IRQs don't push error codes, the -4 displacement is necessary!\r\n;--- v5.87: don't use V86FRAME struct here, it's confusing.\r\n;    cmp word ptr [ebp-4].V86FRAME._Cs, FLAT_CODE_SEL\r\n    cmp word ptr [ebp + sizeof PUSHADS + 4].IRETDS._Cs, FLAT_CODE_SEL\r\n    JNZ handle_exception_r0\r\n\r\n;--- IRQs in ring 0 will only occur controlled - after EnableInts() has been called.\r\n;--- EnableInts will have setup variable dwStackR0 - thus it's possible to identify IRQs;\r\n;--- a ring0 IRQ will have pushed IRETDS, int# and PUSHADS:\r\n    lea eax,[ebp + sizeof PUSHADS + 4 + sizeof IRETDS]\r\n    cmp eax,cs:[dwStackR0]\r\n    jnz handle_exception_r0\r\n\r\n;--- it's an IRQ - DS should be set correctly;\r\n;--- v5.86: fixed, V86FRAME._IntNo isn't correct for IRQs!\r\n;    MOVZX ECX,byte ptr [EBP].V86FRAME._IntNo ;load 1 byte ONLY!\r\n    MOVZX ECX, byte ptr [EBP+sizeof PUSHADS]  ; int# is sign-extended!\r\n\r\nif ?V86DBG\r\n  if ?SELINTLOG\r\n    bt [logintbm],ecx\r\n    jnc @F\r\n  endif\r\n    @dprintf ?V86DBG, <\"V86 IRQ in PL0, int=%X ebp=%X, cs:eip=%X:%X, efl=%X\",10>, ecx,\\\r\n        ebp, word ptr [ebp-4].V86FRAME._Cs, [ebp-4].V86FRAME._Eip, [ebp-4].V86FRAME._Efl\r\n@@:\r\nendif\r\n    push ecx\r\n    call @@runv86\r\n    @v86popreg\r\n    add ESP,4      ;skip int#\r\n    IRETD\r\n\r\nif ?NMI\r\n\r\n;--- v5.87: reworked the NMI code for PL0;\r\n\r\nhandle_nmi_pl0:\r\n\r\n    add esp, 4+4    ;skip the dword's pushed by NMI entry code (=CRS_SIZEX)\r\n    mov ss:[OldESP], esp\r\n\r\n;--- since an NMI can occur at any time, ESP may be inside the v86 client register region;\r\n;--- then ESP has to be setup with dwStackCurr.\r\n\r\n    cmp esp, ?TOS\r\n    jnc @F\r\n    cmp esp, cs:[dwStackCurr]\r\n    cmc\r\n    jnc @F\r\n    mov esp, cs:[dwStackCurr]\r\n@@:\r\n    push ss:[OldESP]\r\n    pushad\r\n    push ds\r\n    push es\r\n    mov eax, ss\r\n    mov ds, eax\r\n    mov es, eax\r\n    jnc @F\r\n;--- save IRETD frame\r\n    mov ebp,[esp+2*4+sizeof PUSHADS]  ;get old ESP\r\n    push [ebp].IRETDS._Efl\r\n    push [ebp].IRETDS._Cs\r\n    push [ebp].IRETDS._Eip\r\n@@:\r\n    pushfd  ; C=IRETD frame saved on stack\r\n\r\n;--- route NMI to v86-mode;\r\n;--- save/restore a few client fields;\r\n;--- clear IF/TF so monitor won't be reentered by an IRQ.\r\n\r\n    mov ebp, ?TOS - sizeof Client_Reg_Struc\r\n    push [ebp].Client_Reg_Struc.Client_Int\r\n    push [ebp].Client_Reg_Struc.Client_Error\r\n    push [ebp].Client_Reg_Struc.Client_EFlags\r\n    and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1, not (1 or 2)\r\n    call nmiv86\r\n    pop [ebp].Client_Reg_Struc.Client_EFlags\r\n    pop [ebp].Client_Reg_Struc.Client_Error\r\n    pop [ebp].Client_Reg_Struc.Client_Int\r\n\r\n    popfd\r\n    jnc @F\r\n;--- restore IRETD frame\r\n    mov ebp, [esp+sizeof IRETDS+2*4+sizeof PUSHADS]   ;get old ESP\r\n    pop [ebp].IRETDS._Eip\r\n    pop [ebp].IRETDS._Cs\r\n    pop [ebp].IRETDS._Efl\r\n@@:\r\n    @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\r\n    pop es\r\n    pop ds\r\n;--- restore GPRs and ESP\r\n    popad\r\n    pop esp\r\n    iretd\r\n\r\n    align 4\r\n\r\nnmiv86::\r\n;--- NMI must be masked - else the IRETD to enter v86 will re-trigger it...\r\n    mov al,80h\r\n    out 70h,al\r\n    in al,71h\r\n\r\n;--- run NMI interrupt in v86; registers eax,ecx,edx may be trashed if called by PL0, but shouldn't matter\r\n    push 2\r\n    call @@runv86\r\n\r\n;--- the v86 NMI handler hopefully has reset the NMI request, so let's unmask it...\r\n    mov al,0\r\n    out 70h,al\r\n    in al,71h\r\n    ret\r\n\r\nendif\r\n\r\n    align 4\r\n\r\n@@runv86:\r\n;--- running v86-mode will reset registers FS & GS to 0!\r\n    mov ebp, ?TOS - sizeof Client_Reg_Struc\r\n    call Begin_Nest_Exec  ;simulates a FAR call (CS:IP = RSEG:BPBack)\r\n    mov eax, [esp+4]\r\n    call Exec_Int         ;runs an INT xx - push current CS:IP & flags; run INT xx\r\n    call End_Nest_Exec    ;simulates a RETF\r\n    ret 4\r\n\r\n    align 4\r\n\r\nV86_Monitor ENDP\r\n\r\n;--- breakpoint: handle exc 06 in v86-mode\r\n\r\nInt06_V86Entry proc\r\n    call Simulate_Iret\r\n    mov [ebp].Client_Reg_Struc.Client_Int,6\r\n    jmp handle_exception_ebp\r\n    align 4\r\nInt06_V86Entry endp\r\n\r\n;--- handle exceptions (ring 0 protected-mode and v86-mode);\r\n;--- display current register set;\r\n;--- if it is caused by external protected-mode code, display REBOOT option\r\n;--- else jump to v86-mode and try to abort current PSP\r\n;--- handle_exception_r0: esp & ebp -> PUSHADS + exc# + (faked) errc + IRETDS\r\n;--- v5.87: reworked a) segment registers and [CS:E/IP] are now always displayed.\r\n;---        b) F1/F2 accepted for fastboot/reboot\r\n;---        c) Yield() & port 60h access used instead of INT 16h\r\n;---        d) sprintfx() for formatted output introduced\r\n\r\nhandle_exception proc\r\n\r\nhandle_exception_r0::           ; <--- entry exc in ring 0\r\n;--- v5.87: display for segment registers added\r\n\tpush ds\r\n\tpush es\r\n\tpush fs\r\n\tpush gs\r\n\tjmp @F\r\n    align 4\r\n\r\nhandle_exception_ebp::          ; <--- entry exc in v86\r\n    mov esp,ebp\r\n;--- v5.87: handle segment registers similar to PL0\r\n\tpush [ebp].Client_Reg_Struc.Client_DS\r\n\tpush [ebp].Client_Reg_Struc.Client_ES\r\n\tpush [ebp].Client_Reg_Struc.Client_FS\r\n\tpush [ebp].Client_Reg_Struc.Client_GS\r\n\tmov edx,0ffff0h+0ffffh\r\n@@:\r\n    mov ax, FLAT_DATA_SEL\r\n    mov ds, eax\r\n    mov ES, eax\r\n    inc [bReentered]\r\n    cld\r\n\r\n;--- if it's an exception in ring0, the Client_Reg_Struc\r\n;--- may miss all fields beyond Client_Reg_EFlags!\r\n\r\n    @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\r\n\r\n    test byte ptr [ebp].Client_Reg_Struc.Client_EFlags+2,2   ;V86 mode?\r\n    jnz @@isv86\r\n    mov esi, [ebp].Client_Reg_Struc.Client_EIP\r\n    lar eax, [ebp].Client_Reg_Struc.Client_CS\r\n    lsl edx, [ebp].Client_Reg_Struc.Client_CS\r\n    and ah,60h\r\n    jnz @@isring3\t; ring3 protected-mode code is not supposed to run in Jemm's context...\r\n    mov ebx, ss\r\n    lea ecx,[ebp + Client_Reg_Struc.Client_ESP] ;in ring 0, esp & ss are also missing\r\n    jmp @@isring0\r\n\r\n@@isv86:\r\n\r\n;--- setup ESI to CS:IP\r\n\r\n    movzx esi,word ptr [ebp].Client_Reg_Struc.Client_CS\r\n    shl esi,4\r\n    add esi,[ebp].Client_Reg_Struc.Client_EIP\r\n\r\n@@isring3:\r\n    mov ebx,dword ptr [ebp].Client_Reg_Struc.Client_SS\r\n    MOV ecx,dword ptr [ebp].Client_Reg_Struc.Client_ESP\r\n@@isring0:\r\n    push ebx                     ; ebp-20 == SS\r\n    push ecx                     ; ebp-24 == ESP\r\n\r\n    .386p\r\n    mov eax, cr0\r\n    push eax                     ; ebp-28\r\n    mov eax, cr2\r\nif 1 ;v5.80: display cr4 instead of cr2 if no page fault\r\n;--- CR4 exists only if cpuid is supported\r\n\tcmp [dwFeatures],0\r\n\tjz @F\r\n\tmov exc_cr2 + 3, '2'\r\n\tcmp byte ptr [ebp].Client_Reg_Struc.Client_Int, 0Eh\r\n\tjz @F\r\n\tmov exc_cr2 + 3, '4'\r\n\t.586p\r\n\tmov eax, cr4\r\n\t.386\r\n@@:\r\nendif\r\n    push eax                     ; ebp-32\r\n\r\nif ?ISRFLGS\r\n    mov al,0Bh      ;irq 8-15 in service?\r\n    out 0A0h,al\r\n    in al,0A0h\r\n    mov ah,al\r\n    mov al,0Bh      ;irq 0-7 in service?\r\n    out 20h,al\r\n    in al,20h\r\n    push eax        ; ebp-36\r\nendif\r\n\r\n;--- setup EDI, used as output buffer\r\n\r\n    sub esp, (sizeexcstr and not 3) + 4\r\n    mov edi,esp\r\n\r\n;--- v5.87: sprintfx replaced render_items\r\n    push offset exc_str\r\n    call sprintfx\r\n\r\n;--- render up to 8 bytes at cs:eip (=esi);\r\n;--- edx = segment limit\r\n    mov cl,8\r\n@@nextitem:\r\n    cmp edx, esi\r\n    jb done_cseip\r\n;--- for page faults, check CR2!\r\n\tcmp byte ptr [ebp].Client_Reg_Struc.Client_Int, 0Eh\r\n\tjnz @F\r\n\tmov eax, cr2\r\n\tcmp eax, esi\r\n\tjz done_cseip\r\n@@:\r\n    lodsb\r\n    call b2a\r\n    mov al,' '\r\n    stosb\r\n    dec cl\r\n    jnz @@nextitem\r\ndone_cseip:\r\n    mov eax,0A0Dh\r\n    stosd\r\n\r\n    mov ebp, ?TOS - sizeof Client_Reg_Struc\r\n\r\n;--- reset client's AC flag\r\n;--- since we use v86-mode for displays now\r\n\r\n    and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+2,not 4\r\n    \r\n;--- make sure the first 4 entries in the breakpoint table are valid\r\n;--- this will make Jemm work even if severe damage has been done to \r\n;--- the v86 memory.\r\n\r\n    mov ecx, [bpstart]\r\n    mov dword ptr [ecx], (?BPOPC shl 24) or (?BPOPC shl 16) or (?BPOPC shl 8) or ?BPOPC\r\n\r\n;--- display the exception info\r\n    call Begin_Nest_Exec\r\n    push esp\r\n    call PrintString\r\n\r\n    mov eax,ss\r\n    cmp ax, FLAT_DATA_SEL\r\n    jz @F\r\n    inc [bReentered]\r\n    push ds\r\n    pop ss\r\n@@:\r\n    mov esp, ?TOS - ?STKSIZE\r\n    mov [dwStackCurr],esp    ; reinit ring 0 stack\r\n\r\nprompt:\r\n    mov eax, offset exc_str2 ; \"press ESC ...\"\r\n    cmp bReentered, 2\r\n    jb @F\r\n    mov eax, offset exc_str3 ; \"press F1/F2 to reboot...\"\r\n@@:\r\n    push eax\r\n    call PrintString\r\n@@waitkey:\r\nif ?AUXIO\r\n    call AuxGetChar\r\n    cmp al,1Bh\r\n    jnz @@waitkey\r\nelse\r\n    cmp [bReentered],2\r\n    jnc @F\r\n    in al, 21h\r\n    push eax\r\n    mov al, not 1 ;allow PIT timer irq\r\n    out 21h,al\r\n    call Yield\r\n    pop eax\r\n    out 21h,al\r\n@@:\r\n    in al, 64h\r\n    test al, 01h; kbd input buffer full?\r\n    jz @@waitkey\r\n    mov ah, al\r\n    in al, 60h\r\n    test ah, 20h; is it input from PS/2 mouse?\r\n    jnz @@waitkey\r\n    cmp al, 3Bh ; F1?\r\n    jz softboot\r\n    cmp al, 3Ch ; F2?\r\n    jz _Reboot\r\n    cmp [bReentered],2\r\n    jnc @@waitkey\r\n    cmp al, 1   ; ESC?\r\n    jnz @@waitkey\r\nendif\r\n    push offset szCRLF       ;print a CR/LF\r\n    call PrintString\r\n    mov [bReentered],0\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 4C7Fh\r\n    mov eax,21h\r\n    call Exec_Int\r\n    mov [bReentered],2\r\n    jmp prompt\r\nsoftboot:\r\n;--- _SoftBoot expects to be called by int 15h, ax=4F53 (Ctrl-Alt-Del pressed)\r\n;--- this has to be emulated.\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 4F53h\r\n    or byte ptr [ebp].Client_Reg_Struc.Client_EFlags, 1 ;set carry flag\r\n    or byte ptr ds:[@KB_FLAG],1100b    ; set key status CTRL & ALT\r\n    jmp _SoftBoot\r\n    align 4\r\n\r\nhandle_exception endp\r\n\r\nif ?BPOPC ne 0F4h\r\n\r\n;--- true IDT 06 entry if breakpoint is ARPL\r\n\r\nInt06_Entry proc\r\n    push 0              ;exc 06 has no error code, set a dummy one\r\n    push 6\r\n    pushad\r\n    mov ebp, esp        ; ebp -> Client_Reg_Struc\r\n    mov eax, ss         ; might be better to use \"mov eax,cs\" \"add eax,8\"\r\n    mov ds, eax\r\n    jmp Is_BP\r\n    align 4\r\nInt06_Entry endp\r\nendif\r\n\r\n;--- exception in V86 mode with error code.\r\n;--- IDT entry for gate 10h will have pushed a fake error code for float exceptions.\r\n;--- v5.86: exceptions 0-7 will have pushed a fake error code.\r\n;--- esp -> int#, error code, IRETDV86\r\n;--- DS,ES = NULL\r\n\r\nV86_Exception proc\r\n\r\n    pushad\r\n    mov ebp, esp\r\n\r\n;--- now ebp -> Client_Reg_Struc\r\n\r\n    mov ecx, [ebp].Client_Reg_Struc.Client_Int\r\nif ?V86DBG\r\n;--- this happens quite often, so better activate selectively\r\n  if ?SELINTLOG\r\n    bt cs:[logintbm],ecx\r\n    jnc @F\r\n  endif\r\n    @dprintf ?V86DBG, <\"exception in v86-mode, #=%X, errc=%X, cs:ip=%X:%X\",10>, ecx,\r\n        [ebp].Client_Reg_Struc.Client_Error, word ptr [ebp].Client_Reg_Struc.Client_CS, word ptr [ebp].Client_Reg_Struc.Client_EIP\r\n@@:\r\nendif\r\n    MOV EAX,SS\r\n    MOV DS,EAX\r\n\r\n    mov esp,[dwStackCurr]\r\n\r\nif ?NMI\r\n    cmp ecx,2\r\n    jz nmi_pl3\r\nendif\r\n\r\n;    @dprintf ?V86DBG, <\"V86_Exception, esp=%X, cs:eip=%X:%X\",10>, esp,[ebp].Client_Reg_Struc.Client_CS,[ebp].Client_Reg_Struc.Client_EIP\r\n\r\n    CMP ECX,0DH             ; general protection exception?\r\n    JNZ @@V86_TEST_EXC      ; no, check further\r\nif ?BPOPC ne 0F4h\r\nIs_BP::                     ; <--- entry from INT 06 if breakpoint is ARPL\r\nendif\r\n;--- a monitor \"bp\", used to switch back from v86 to protected-mode?\r\n;--- EAX=flat selector\r\n\r\n    MOV ES,EAX\r\n    MOVZX ESI,word ptr [EBP].Client_Reg_Struc.Client_CS\r\n    mov ecx,[EBP].Client_Reg_Struc.Client_EIP\r\n    SHL ESI,4\r\n    ADD ESI,ECX             ; ESI = linear CS:EIP\r\n\r\n; check what triggered the GPF. Possible reasons are:\r\n\r\n; - \"bp\" opcode (HLT or ARPL), which then might be:\r\n;   + a \"Breakpoint\" if CS:EIP points into the breakpoint table.\r\n;     call the breakpoint handler proc then.\r\n;   + other HLTs. Is handled by running HLT in ring 0 inside the monitor.\r\n; - trapped I/O command. I/O only causes GPF for masked ports.\r\n;   The DMA, KBC and P92 ports are trapped inside Jemm, but external\r\n;   modules may take over the whole IO port trapping (JLOAD).\r\n; - other privileged opcode. Some are emulated (mov CRx, reg ...),\r\n;   some are not and then are just translated to an int 6, illegal opcode,\r\n;   which is then reflected to the V86 task!).\r\n;\r\n    MOV AL,[ESI]                ; check opcode\r\n    cmp AL, ?BPOPC\r\n    jnz @@NoBPOPC               ; breakpoint?\r\n;    mov eax, [bpstart]\r\n;    sub esi, eax\r\n    sub esi, [bpstart]\r\n    jb @@No_BP\r\n    cmp esi, NUMBP\r\n    jae @@No_BP\r\n    call [esi*4+offset bptable]\r\nexc_exit:\r\n    @v86popregX\r\n    add ESP, CRS_SIZEX          ; skip int# + error code\r\n    IRETD\r\nnmi_pl3:                        ; v5.87: handle NMI occured in PL3\r\n    call nmiv86\r\n    jmp exc_exit\r\n\r\n@@No_BP:\r\nif ?BPOPC ne 0F4h\r\n    jmp V86_Exc0D               ; if an ARPL occured at unknown location...\r\nendif\r\n@@Is_Hlt:\r\n\r\n    @dprintf ?HLTDBG, <\"True HLT occured at CS:IP=%X:%X\",10>, [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP\r\n\r\n    INC [EBP].Client_Reg_Struc.Client_EIP   ; Jump over the HLT instruction\r\n\r\n;    mov esp,[dwStackCurr]     ; v5.87: in V86_Exception, register ESP is already setup correctly.\r\n;--- doing a HLT with interrupts disabled will freeze the machine\r\n;--- (or at least wait for a NMI); That's MS Emm386 compatible.\r\n\r\nif 0 ; activate if HLT should be a NOP if IF==0\r\n    test byte ptr [ESP].IRETDV86._Efl+1,2\r\n    jz exc_exit\r\nendif\r\n    call EnableInts\r\n    STI                         ; give Interrupts free and wait for IRQ\r\n    HLT\r\n    CLI\r\n    call DisableInts\r\n    jmp exc_exit\r\n\r\n;--- exc 0Dh in v86, no BP\r\n\r\n@@NoBPOPC:\r\n\r\nif ?BPOPC ne 0F4h\r\n    CMP AL,0F4H                 ; HLT instr?\r\n    JZ @@Is_Hlt\r\nendif\r\n\r\nif ?SB\r\n; see if SoundBlaster INT 3 forced to 1ah error code GPF\r\n\r\n    CMP [EBP].Client_Reg_Struc.Client_Error,1ah\r\n    jne @@notsb\r\n    test [bV86Flags],V86F_SB\r\n    je @@notsb                  ; SB option not turned on\r\n    inc [EBP].Client_Reg_Struc.Client_EIP   ; skip INT 3 opcode (shouldn't this be checked first?)\r\n    @v86popregX\r\n    add esp, CRS_SIZEX          ; discard GPF error code & INT#\r\n    push 3                      ; simulate an INT 3 (no exception!)\r\n    jmp V86_Monitor\r\n@@notsb:\r\nendif\r\n\r\n    @dprintf ?EMUDBG, <\"Opcode %X %X %X caused GPF at %X\",10>, byte ptr [esi], byte ptr [esi+1], byte ptr [esi+2], esi\r\n\r\n    cmp al,0Fh                  ; check if potentially mov <reg>,cr#\r\n    je ExtendedOp               ; ExtendedOp will jmp back to V86_Exc0D if no emulation happened\r\n    mov cl,0\r\n    mov edi, esi\r\n    cmp al,0F3h                 ; REP prefix?\r\n    setz ch\r\n    jnz @F\r\n    inc esi\r\n    mov al,[esi]\r\n@@:\r\n    cmp al,66h                  ; 66 prefix?\r\n    setz ah\r\n    jnz @F\r\n    inc esi\r\n    mov al,[esi]\r\n@@:\r\n\r\n;--- to be fixed: at least OUTS may accept a segment prefix (64,65,26,2E,36,3E)!\r\n;--- and prefix 67h may cause a GPF at least...\r\n\r\n    cmp al,6Ch\r\n    JB V86_Exc0D\r\n    cmp al,6Fh                  ; string IO (opcodes 6C, 6D, 6E, 6F)?\r\n    JBE @@DoIO_String\r\n    CMP AL,0E4H\r\n    JB V86_Exc0D\r\n    CMP AL,0E7H                 ; IN AL|AX,xx or OUT xx,AL|AX (opcodes E4, E5, E6, E7)?\r\n    JBE @@DoIO_Im\r\n    CMP AL,0ECH\r\n    JB V86_Exc0D\r\n    CMP AL,0EFH                 ; IN/OUT DX (opcodes EC, ED, EE, EF)?\r\n    JBE @@DoIO_DX\r\n    JMP V86_Exc0D\r\n\r\n;--- exception (not 0Dh) in V86 mode\r\n;--- DS=FLAT, ECX=INT#\r\n\r\n@@V86_TEST_EXC:\r\n\r\nif ?ROMRO\r\n    cmp ECX, 0Eh\r\n    jnz @@V86_EXC_NOT0D0E\r\n    mov eax, CR2\r\n    shr eax, 12\r\n    cmp eax, 0FFh      ;it is the FF000 page (which is r/o!)\r\n    jz PageFaultFF\r\n\r\n;--- unhandled v86-mode exception 0Eh occured \r\n\r\n@@V86_EXC0E:\r\n\r\nendif\r\n\r\n;*************************************************************\r\n; unhandled exception in v86-mode.\r\n;--- this has been modified for v5.86.\r\n;--- previously, just exceptions with error code were handled here;\r\n;--- since such exceptions aren't usually handled by v86-code,\r\n;--- the default behavior was to terminate the application;\r\n;--- exception was exc 0Dh, which was routed to INT 06 unless V86EXC0D\r\n;--- option was set.\r\n;\r\n;--- now, exc 0,1,3,4,5,6 and 7 are also handled here.\r\n;--- however, they are all routed to INTs.\r\n\r\n; this notifies any hookers about the problem. If noone has hooked\r\n; v86-int 06, we finally end at the monitor again, display a register\r\n; dump and then try to terminate the current PSP.\r\n;*************************************************************\r\n\r\n;--- unhandled v86-mode exception xxh occured\r\n;--- ecx=exc#\r\n\r\n@@V86_EXC_NOT0D0E:\r\nif ?V86EXC0D  ;option V86EXC0D supported? (should be 1)\r\n\tjmp noexcrtn\r\nendif\r\n\r\n;--- unhandled v86-mode exception 0Dh occured.\r\n;--- the emulated \"special register\" moves,\r\n;--- I/O instructions and HLT have been handled here...\r\n;--- ecx undefined\r\n\r\nV86_Exc0D::\r\n\r\nif ?V86EXC0D\r\n\r\n;--- V86EXC0D option set? If yes, route the exception to\r\n;--- v86 int 0Dh instead of int 06h.\r\n\r\n    test [bV86Flags], V86F_V86EXC0D\r\n    mov eax,0Dh\r\n    jnz v86excrtn\r\nnoexcrtn:\r\nendif\r\n;--- v5.86: call V86 Fault handler.\r\n;--- they'll either just RET or chain to the default handler (V86_Fault)\r\n\tmov eax,[ebp].Client_Reg_Struc.Client_Int\r\n\tcall [vmm_service_table.pV86Faults]\r\n\tjmp v86excrtn2\r\nV86_Fault::\r\n\r\n;--- v5.86: route exc 0-7 to INT\r\n\tcmp eax,8\r\n\tjb v86excrtn\r\n\r\n;--- if \"noone\" has hooked v86 int 06 vector, there's\r\n;--- no need to route v86 exceptions to v86-mode, since\r\n;--- it just will be thrown back to Jemm. This allows to\r\n;--- display the true exception number (0C/0D/0E/..)\r\n\r\nif ?SKIPINT06\r\n    movzx eax,word ptr ds:[6*4+2]\r\n    cmp eax, [dwRSeg]\r\n    jz handle_exception_ebp\r\nendif\r\n\r\n;--- simulate invalid opcode interrupt in v86 mode\r\n\r\n    mov eax,6\r\nv86excrtn:\r\n    call [vmm_service_table.pSimulate_Int]\r\nv86excrtn2:\r\n    @v86popregX\r\n    add ESP, CRS_SIZEX  ; skip error code & \"int#\" \r\n    IRETD               ; return to virtual 86-Mode\r\n\r\n    align 4\r\n\r\n;***************************************\r\n\r\n; IO command has been trapped\r\n\r\n\r\n;--- opcodes 6C-EF (INS, OUTS), in AL\r\n;--- CH=01 if rep prefix, else 00\r\n;--- AH=01 if 66h prefix, else 00\r\n\r\n@@DoIO_String:\r\n    rol ecx,16\r\n    mov cx,word ptr [ebp].Client_Reg_Struc.Client_ES\r\n    test al,2    ;OUTS opcode?\r\n    jz @@isstrin\r\n    mov cx,word ptr [ebp].Client_Reg_Struc.Client_DS\r\n@@isstrin:\r\n    rol ecx,16\r\n    or cl,STRING_IO\r\n    shl ch,6\r\n    or cl,ch   ;set REP bit\r\n    JMP @@DoIO_DX\r\n\r\n;--- opcodes E4-E7 (IN AL,XX ; IN AX,XX ; OUT XX, AL ; OUT XX, AX)\r\n\r\n@@DoIO_Im:\r\n    INC ESI\r\n    MOVZX EDX,BYTE PTR [ESI]              ; get I/O port in DX\r\n\r\n;--- entry opcodes EC-EF (IN AL|AX,DX ; OUT DX,AL|AX)\r\n;--- DX = Client_DX\r\n\r\n@@DoIO_DX:\r\n    inc esi\r\n    and ah,al   ;if AH still 1, it is DWORD IO\r\n    shl ah,4\r\n    or cl,ah\r\n    shr ah,4\r\n    xor ah,al\r\n    and ah,1\r\n    shl ah,3\r\n    or cl,ah\r\n    and al,2\r\n    shl al,1\r\n    or cl,al\r\n\r\n    sub esi, edi\r\n    add [ebp].Client_Reg_Struc.Client_EIP, esi\r\n    mov eax, [ebp].Client_Reg_Struc.Client_EAX\r\n\r\n;--- now DX, EAX and CL is set for the IO handlers    \r\n\r\n    push ecx\r\nif ?ALT_TRAPHDL\r\n;--- v5.85: if trap handler has been changed, and port is < 0x100,\r\n;---        call the alternate handler if it has been set\r\n\tcmp [IO_Trap_Handler], Default_IO_Trap_Handler\r\n\tjz @F\r\n\tcmp dh, 0\r\n\tjnz @F\r\n\tcall [Alternate_IO_Traphandler]\r\n\tjc @@io_handled\r\n@@:\r\nendif\r\n    call [IO_Trap_Handler]\r\n@@io_handled:\r\n    pop ecx\r\n    test cl,OUT_INSTR or STRING_IO\r\n    jnz @@isout\r\n    mov [ebp].Client_Reg_Struc.Client_EAX, eax\r\n@@isout:\r\n    @v86popregX\r\n    ADD ESP, CRS_SIZEX\r\n    IRETD\r\n\r\n    align 4\r\n\r\nV86_Exception endp\r\n\r\n;--- default IO trap handler;\r\n;--- can handle byte ports only\r\n;--- will scan portmap for internally trapped ports ( DMA, A20 )\r\n;--- in:\r\n;--- CL=flags\r\n;--- DL=port\r\n\r\nDefault_IO_Trap_Handler proc\r\n\r\nif 0    ;all internal functions handle byte i/o only\r\n    test cl,STRING_IO or DWORD_IO or WORD_IO\r\n    jnz Simulate_IO_trap\r\nendif\r\n    mov esi, offset portmap\r\n@@nextport:\r\n    cmp dl, [esi].IOTRAPENTRY.bStart\r\n    jb @@skipport\r\n    cmp dl, [esi].IOTRAPENTRY.bEnd\r\n    jbe @@portfound\r\n@@skipport:\r\n    add esi,size IOTRAPENTRY\r\n    cmp esi, offset endportmap\r\n    jnz @@nextport\r\n    jmp Simulate_IO\r\n@@portfound:\r\n    jmp dword ptr [esi].IOTRAPENTRY.dwProc\r\n    align 4\r\n\r\nDefault_IO_Trap_Handler endp\r\n\r\n;--- Simulate_IO: emulate IN/OUT\r\n;--- in:\r\n;--- EBP -> client ptr\r\n;--- CX = flags\r\n;--- Hiword(ECX) = segment of src/dst for string IO\r\n;--- EAX = data (if OUT_INSTR flag is set)\r\n;--- DX = port\r\n\r\n;--- it converts:\r\n;--- WORD IO -> BYTE IO\r\n;--- DWORD IO -> WORD IO\r\n;--- STRING IO -> DWORD/WORD/BYTE IO\r\n;--- REP STRING IO -> multiple DWORD/WORD/BYTE IO\r\n\r\n;--- v5.84: fixed: Simulate_IO did call [IO_Trap_Handler]\r\n;---        now it just splits I/O to byte accesses.\r\n;---        The new function Simulate_IO_trap() does what Simulate_IO()\r\n;---        did prior to v5.84.\r\n\r\nSimulate_IO_trap proc public\r\n\tmov ebx, [IO_Trap_Handler]\r\n\tjmp Simulate_IOex\r\nSimulate_IO_trap endp\r\n\r\nSimulate_IO proc public\r\n\tmov ebx, offset Simulate_IOex\r\nSimulate_IO endp\r\n\r\n;IOPROC textequ <[IO_TrapHandler]>\r\n;IOPROC textequ <ebx>\r\nIOPROC  textequ <[ebp].Client_Reg_Struc.Client_res0>\r\n\r\nSimulate_IOex proc\r\n\r\n    mov IOPROC, ebx ;EBX is a volatile register in DMA, cannot be used\r\n    test cl,STRING_IO\r\n    jnz @@isstrio\r\n    push ecx\r\n    movzx ecx,cl\r\n;    and ecx,1Ch\r\n    and ecx,DWORD_IO or WORD_IO or OUT_INSTR\r\n    mov ecx,[ecx + offset iojmp]\r\n    xchg ecx, [esp]\r\n    ret\r\n    align 4\r\n\r\niojmp label dword\r\n    dd @@bin    ;+00\r\n    dd @@bout   ;+04 (OUT_INSTR is 4)   \r\n    dd @@win    ;+08 (WORD_IO is 8)\r\n    dd @@wout   ;+0C\r\n    dd @@din    ;+10 (DWORD_IO is 10h\r\n    dd @@dout   ;+14\r\n;   dd ??       ;+18 either WORD_IO or DWORD_IO are set, never both\r\n;   dd ??       ;+1C\r\n\r\n@@bin:\r\n    in al,dx\r\n    ret\r\n@@bout:\r\n    out dx,al\r\n    ret\r\n@@win:\r\n    and cl,not WORD_IO\r\n    push edx\r\n    push ecx\r\n    call IOPROC\r\n    pop ecx\r\n    mov edx,[esp]\r\n    inc edx\r\n    push eax    \r\n    call IOPROC\r\n    mov [esp+1],al\r\n    pop eax\r\n    pop edx\r\n    ret\r\n@@wout:\r\n    and cl,not WORD_IO\r\n    push edx\r\n    push ecx\r\n    call IOPROC\r\n    pop ecx\r\n    mov edx,[esp]\r\n    inc edx\r\n    mov al,ah\r\n    call IOPROC\r\n    pop edx\r\n    ret\r\n@@din:\r\n    and cl,not DWORD_IO\r\n    or cl,WORD_IO\r\n    push edx\r\n    push ecx\r\n    call IOPROC\r\n    pop ecx\r\n    mov edx,[esp]\r\n    add edx,2\r\n    push eax\r\n    call IOPROC\r\n    mov [esp+2],ax\r\n    pop eax\r\n    pop edx\r\n    ret\r\n@@dout:\r\n    and cl,not DWORD_IO\r\n    or cl,WORD_IO\r\n    push edx\r\n    push ecx\r\n    call IOPROC\r\n    pop ecx\r\n    pop edx\r\n    add edx,2\r\n    shr eax,16\r\n    jmp IOPROC\r\n\r\n@@isstrio:\r\n    test cl, REP_IO\r\n    jz @@isnorepio\r\n    and cl, not REP_IO\r\n    jmp @@teststr\r\n@@nextio:\r\n    push ecx\r\n    push edx\r\n    call @@isnorepio\r\n    pop edx\r\n    pop ecx\r\n    dec word ptr [ebp].Client_Reg_Struc.Client_ECX\r\n@@teststr:\r\n    cmp word ptr [ebp].Client_Reg_Struc.Client_ECX,0\r\n    jnz @@nextio\r\n    ret\r\n\r\n@@isnorepio:\r\n    and cl,not STRING_IO\r\n    push ecx\r\n    movzx ecx, cl\r\n;    and ecx,1Ch\r\n    and ecx,DWORD_IO or WORD_IO or OUT_INSTR\r\n    mov ecx, [ecx + offset siojmp]\r\n    xchg ecx, [esp]\r\n    ret\r\n    align 4\r\n\r\nsiojmp label dword\r\n    dd @@sbin\r\n    dd @@sbout\r\n    dd @@swin\r\n    dd @@swout\r\n    dd @@sdin\r\n    dd @@sdout\r\n;   dd ??       ; either WORD_IO or DWORD_IO are set, never both\r\n;   dd ??\r\n\r\n@@sbin:\r\n    call @@sxin\r\n    stosb\r\n    add word ptr [ebp].Client_Reg_Struc.Client_EDI,1\r\n    ret\r\n@@swin:\r\n    call @@sxin\r\n    stosw\r\n    add word ptr [ebp].Client_Reg_Struc.Client_EDI,2\r\n    ret\r\n@@sdin:\r\n    call @@sxin\r\n    stosd\r\n    add word ptr [ebp].Client_Reg_Struc.Client_EDI,4\r\n    ret\r\n@@sxin:\r\n    call IOPROC\r\n    rol ecx, 16\r\n    movzx esi,cx\r\n    rol ecx, 16\r\n    shl esi, 4\r\n    movzx edi, word ptr [ebp].Client_Reg_Struc.Client_EDI\r\n    add edi, esi\r\n    ret\r\n\r\n@@sbout:\r\n    call @@sxout\r\n    lodsb\r\n    add word ptr [ebp].Client_Reg_Struc.Client_ESI,1\r\n\tjmp IOPROC\r\n@@swout:\r\n    call @@sxout\r\n    lodsw\r\n    add word ptr [ebp].Client_Reg_Struc.Client_ESI,2\r\n\tjmp IOPROC\r\n@@sdout:\r\n    call @@sxout\r\n    lodsd\r\n    add word ptr [ebp].Client_Reg_Struc.Client_ESI,4\r\n\tjmp IOPROC\r\n@@sxout:\r\n    rol ecx, 16\r\n    movzx eax,cx\r\n    rol ecx, 16\r\n    shl eax, 4\r\n    movzx esi, word ptr [ebp].Client_Reg_Struc.Client_ESI\r\n    add esi, eax\r\n    ret\r\n\r\n    align 4\r\n\r\nSimulate_IOex endp\r\n\r\nif ?EXC10\r\n\r\n;--- detect exception 10h. This exception has NO error code.\r\n;--- With VME enabled, it would be no problem, but since the VME bit\r\n;--- in CR4 can be cleared by the user or other programs, Jemm cannot\r\n;--- rely on that. So it checks:\r\n;--- 1. if NE is set. No -> Int 10h\r\n;--- 2. if ([CS:IP] != 9B) && ([CS:IP] != D8..DF) -> Int 10h\r\n;--- 3. check for FP status word bit 7 set:\r\n;---   (a. coprocessor available (00000410, bit 1=1))\r\n;---   b. FNSTSW AX, check bit 1, if 0 -> Int 10h\r\n;---\r\n;--- Else it is an exception, and a dummy error code of 0 is pushed,\r\n;--- which will make V86_Monitor recognize it as such.\r\n;---\r\n;--- Jemm doesn't clear the FP status word by a FNINIT, so any FP instruction\r\n;--- will continue to cause an exception 10h unless it is cleared.\r\n;--- If the NE bit in CR0 isn't set, an IRQ 13 (interrupt 75h) is launched\r\n;--- instead of exception 10h.\r\n\r\nInt10_Entry proc public\r\n    push eax\r\n    mov eax,cr0\r\n    test al,20h     ;NE bit set? (usually it is 0)\r\n    jnz maybeexc10\r\nisint10:\r\n    pop eax\r\n    push 10h\r\n    jmp V86_Monitor ;enter monitor as INT 10h\r\nmaybeexc10:\r\n;--- assuming entry from v86-mode!\r\n    movzx eax, word ptr [esp+4].IRETDV86._Cs\r\n    shl eax, 4\r\n    add eax, [esp+4].IRETDV86._Eip\r\n    mov al,cs:[eax]\r\n    cmp al,9Bh      ;WAIT?\r\n    jz @F\r\n    and al,0F8h     ;or a FPU opcode?\r\n    cmp al,0D8h\r\n    jnz isint10     ;no, then it's an Int 10h\r\n@@:\r\n    fnstsw ax\r\n    test al,80h     ;a FPU error pending?\r\n    jz isint10      ;if no, then it's an Int 10h\r\n    pop eax\r\n    push 0          ;push 0 as error code \r\n    push 10h\r\n    jmp V86_Monitor ;enter monitor as EXC 10h\r\n    align 4\r\nInt10_Entry endp\r\n\r\nendif\r\n\r\n;--- copy physical memory\r\n;--- esi=src, edi=dst, ecx=size\r\n;--- addresses > 10FFFFh are regarded as physical addresses\r\n;--- used by XMS block moves and Int 15h, ah=87h\r\n;--- modifies eax, ebx, ecx, edx, esi, edi\r\n\r\n;?MEMBORDER equ 110000h\r\n?MEMBORDER equ 100000h\r\n\r\nMoveMemoryPhys proc public\r\n\r\n    mov eax, ecx\r\n@@extmove_loop:\r\n    mov ecx, eax\r\n    cmp ecx, MAXBLOCKSIZE\r\n    jb @F\r\n    mov ecx, MAXBLOCKSIZE\r\n@@:\r\n    sub eax, ecx\r\n    push eax\r\n    push esi\r\n    push edi\r\n    push ecx\r\n\r\n    mov edx,[PageMapHeap]\r\n    push edx\r\n\r\n    push ecx\r\n\r\n;--- get no of PTEs involved (max is 16+1)\r\n\r\n    add ecx,1000h-1 ;round up size to page boundary\r\n    shr ecx,12      ;bytes -> PTEs (10000h -> 10h)\r\n    inc ecx         ;add 1 to account for base not aligned on page boundary\r\n    mov ch, cl\r\n\r\n    cmp esi, ?MEMBORDER   ;src in real-mode address space?\r\n    jc  @@src_is_shared\r\n    mov eax, esi\r\n    and esi, 0FFFh\r\n    call MapPhysPages\r\n\r\nif ?PHYSDBG\r\n    push eax\r\n    @dprintf ?PHYSDBG, <\"MoveMemoryPhys: %X pages (src) mapped at %X\",10>, ch, eax\r\n    pop eax\r\nendif\r\n\r\n    add esi, eax\r\n    mov cl, ch\r\n@@src_is_shared:\r\n    cmp edi, ?MEMBORDER\r\n    jc  @@dst_is_shared\r\n    mov eax, edi\r\n    and edi, 0FFFh\r\n    call MapPhysPages\r\nif ?PHYSDBG\r\n    push eax\r\n    @dprintf ?PHYSDBG, <\"MoveMemoryPhys: %X pages (dst) mapped at %X\",10>, ch, eax\r\n    pop eax\r\nendif\r\n    add edi, eax\r\n@@dst_is_shared:\r\nif ?INVLPG\r\n    cmp [bNoInvlPg],0\r\n    jz @@flushdone\r\nendif\r\n    mov eax, cr3\r\n    mov cr3, eax\r\n@@flushdone:\r\n    pop ecx\r\n    mov [PageMapHeap], edx  ;update in case the monitor is reentered\r\n    call MoveMemory\r\n    pop [PageMapHeap]\r\n    pop ecx\r\n    pop edi\r\n    pop esi\r\n    pop eax\r\n    add edi,ecx\r\n    add esi,ecx\r\n    and eax, eax\r\n    jnz @@extmove_loop\r\n    ret\r\n    align 4\r\n\r\nMoveMemoryPhys endp\r\n\r\n?WT equ 0       ; std=0, 1=set WT bit in PTE for mem moves\r\n\r\nif ?WT\r\n?PA equ PTF_PRESENT or PTF_RW or PTF_USER or PTF_PWT\r\nelse\r\n?PA equ PTF_PRESENT or PTF_RW or PTF_USER\r\nendif\r\n\r\n;--- map physical pages in page map heap\r\n\r\nMapPhysPagesEx proc public\r\n    mov edx, [PageMapHeap]\r\nMapPhysPagesEx endp     ;fall thru\r\n\r\n;--- map physical pages in linear address space\r\n;--- in:  eax = start of physical region to map\r\n;---      edx -> free entry in page map heap\r\n;---      cl = no of 4kB pages to map\r\n;--- out: eax = linear address where the region has been mapped\r\n;---      edx -> next free entry in page map heap\r\n;--- modifies ebx, cl\r\n\r\nMapPhysPages proc public\r\n\r\n    mov ebx, edx\r\n    sub ebx, ?SYSBASE+?PAGETABSYS\r\n    shl ebx, 10\r\n    add ebx, ?SYSBASE\r\n    and ah, 0F0h\r\n    mov al,?PA\r\nif ?INVLPG    \r\n    cmp [bNoInvlPg],0\r\n    jz @@setPTEs486\r\nendif\r\n\r\n@@nextPTE1:\r\n    mov [edx], eax\r\n    add eax, 1000h\r\n    add edx,4\r\n    dec cl\r\n    jnz  @@nextPTE1\r\n    mov eax, ebx\r\n    ret\r\n    align 4\r\n\r\nif ?INVLPG\r\n@@setPTEs486:\r\n    push ebx\r\n@@nextPTE2:\r\n    mov [edx], eax\r\n    .486p\r\n    invlpg ds:[ebx]\r\n    .386\r\n    add edx,4\r\n    add eax, 1000h\r\n    add ebx, 1000h\r\n    dec cl\r\n    jnz  @@nextPTE2\r\n    pop eax\r\n    ret\r\nendif\r\n    align 4\r\n\r\nMapPhysPages endp\r\n\r\nif ?XMS35COMPAT\r\n;--- in: AX:ESI src physical region to map\r\n;---     DX:EDI dst physical region to map\r\n;---     EBX: ptr into page directory where the 4MB pages will be mapped\r\n;--- out: EBX=new Heap4M value\r\n;--- usually 2 4MB pages are set in pagedir, either for src or for dst,\r\n;--- but function is reentrant and also both src and dst may be sext.\r\n;--- start of the mapping region is at end of system space F8000000-800000h ( see Heap4MB )\r\n\r\nMapPhysPagesPSE proc uses ecx\r\n\r\n    @dprintf ?I15DBG or ?MAPDBG, <\"MapPhysPagesPSE, ax::esi=%X%08X, dx::edi=%X:%08X, ecx=%X\",10>,ax,esi,dx,edi,ecx\r\n\tmov ecx, esi\r\n\tcmp ax,0\t\t; src beyond 4GB border?\r\n\tjnz @F\r\n\tcmp ecx,?MEMBORDER\r\n\tjc donesrc\r\n@@:\r\n\tmovzx eax,ax\r\n\tshl eax,13\r\n\tand ecx,0FFC00000h\r\n\tor eax, ecx\r\n\tmov al, PTF_PRESENT or PTF_4MB\t;page flags for src: present + 4MB page\r\n\tmov ds:[ebx+0],eax\r\n\tadd eax, 1 shl 22\r\n\tjnc @F\r\n\tadd eax, 1 shl 13\r\n@@:\r\n\tmov ds:[ebx+4], eax\r\n\tand esi,3fffffh\r\n\tadd esi,@LinearFromPDE(eax,ebx)\t; convert PD offset to linear address\r\n\t.486p\r\n\tinvlpg ds:[esi]\r\n\tinvlpg ds:[esi+400000h]\r\n\t.386\r\n\tlea ebx,[ebx-2*4]\t; move backward 2*4MB\r\ndonesrc:\r\n\tmov ecx, edi\r\n\tcmp dx,0\t\t\t; dst beyond 4GB border?\r\n\tjnz @F\r\n\tcmp ecx,?MEMBORDER\r\n\tjc donedst\r\n@@:\r\n\tmovzx eax,dx\r\n\tshl eax,13\r\n\tand ecx,0FFC00000h\r\n\tor eax, ecx\r\n\tmov al, PTF_PRESENT or PTF_RW or PTF_4MB\t;page flags for dst: present + writable + 4MB page\r\n\tmov ds:[ebx+0],eax\r\n\tadd eax, 1 shl 22\r\n\tjnc @F\r\n\tadd eax, 1 shl 13\r\n@@:\r\n\tmov ds:[ebx+4], eax\r\n\tand edi,3fffffh\r\n\tmov eax,ebx\r\n\tsub eax,?PAGEDIR\r\n\tshl eax,20\r\n\tadd edi,eax\r\n\t.486p\r\n\tinvlpg ds:[edi]\r\n\tinvlpg ds:[edi+400000h]\r\n\t.386\r\n\tlea ebx,[ebx-2*4]\t; move forward 2*4MB\r\ndonedst:\r\n\t@dprintf ?I15DBG or ?MAPDBG, <\"MapPhysPagesPSE, esi=%X, edi=%X\",10>, esi, edi\r\n\tret\r\n\talign 4\r\nMapPhysPagesPSE endp\r\n\r\n;--- in: AX:ESI src physical region to map\r\n;--- in: DX:EDI dst physical region to map\r\n;--- in: ECX size\r\n;--- the first region (src or dst) is mapped at F8000000h - (2*400000h + x) \r\n;--- x is !=0 if both src and dst are sext mem or if jemm has been reentered while the move is running.\r\n\r\nMoveMemoryPhysEx proc public\r\n\t@dprintf ?I15DBG or ?MAPDBG, <\"MoveMemoryPhysEx, ax::esi=%X%08X, dx::edi=%X%08X, ecx=%X\",10>,ax,esi,dx,edi,ecx\r\n\t.586p\r\n\tmov ebx,cr4\r\n\tbts ebx,4\r\n\tjc move_loop\t;set PSE only if it isn't set yet\r\n\tmov cr4,ebx\r\n\t.386\r\nmove_loop:\r\n\tpush ecx\r\n\tcmp ecx, 400000h\r\n\tjb @F\r\n\tmov ecx, 400000h\r\n@@:\r\n\tpush eax\r\n\tpush edx\r\n\tpush esi\r\n\tpush edi\r\n\tmov ebx,[Heap4MB]\r\n\tpush ebx\r\n\tcall MapPhysPagesPSE\r\n\tmov [Heap4MB], ebx\r\n\tcall MoveMemory\r\n\tpop [Heap4MB]\r\n\tpop edi\r\n\tpop esi\r\n\tpop edx\r\n\tpop eax\r\n\tpop ecx\r\n\tmov ebx, 400000h\r\n\tadd edi, ebx\r\n\tadc dx,0\r\n\tadd esi, ebx\r\n\tadc ax,0\r\n\tsub ecx, ebx\r\n\tja move_loop\r\n\tret\r\nMoveMemoryPhysEx endp\r\n\r\nendif\r\n\r\n;--- copy memory block ESI to EDI, size ECX\r\n;--- allow interrupts during move op\r\n\r\nMoveMemory proc public\r\n    mov eax, [ebp].Client_Reg_Struc.Client_EFlags\r\n    test ah,2\r\n    jz @@noenable\r\n    call EnableInts\r\n    sti\r\n@@noenable:\r\n\r\n    mov al,cl\r\n    shr ecx,2\r\n    and al,3\r\n    REP MOVSD\r\n    mov cl,al\r\n    REP MOVSB\r\n\r\n    test ah,2\r\n    jz @@nodisable\r\n    cli\r\n    call DisableInts\r\n@@nodisable:\r\n    ret\r\n    align 4\r\n\r\nMoveMemory endp\r\n\r\n;--- allow interrupts in the monitor\r\n\r\nEnableInts proc public\r\n    push [dwStackR0]\r\n    mov [dwStackR0],esp\r\n    jmp dword ptr [esp+4]\r\n    align 4\r\nEnableInts endp\r\n\r\nDisableInts proc public\r\n    pop [esp+4]\r\n    pop [dwStackR0]\r\n    ret\r\n    align 4\r\nDisableInts endp\r\n\r\nYield proc public\r\n    test byte ptr [ebp].Client_Reg_Struc.Client_EFlags+1,2\r\n    jz @@noints\r\n    call EnableInts\r\n    sti\r\n    nop            ;interrupts are enabled 1 instruction *after* STI\r\n    cli\r\n    call DisableInts\r\n@@noints:\r\n    ret\r\n    align 4\r\nYield endp\r\n\r\n;--- simulate an RETF in v86-mode\r\n;--- INP: EBP -> client struct\r\n;--- modifies EAX,ECX,EDX\r\n;--- v5.87: take care of SP wrap\r\n\r\nSimulate_Far_Ret proc public\r\n    MOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP\r\n    MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS\r\n    SHL ecx, 4\r\n    MOVZX eax, word ptr [ecx+edx]\r\n    mov [EBP].Client_Reg_Struc.Client_EIP, eax\r\n    add dx, 2\r\n    MOV ax, [ecx+edx]\r\n    add dx, 2\r\n    mov [EBP].Client_Reg_Struc.Client_CS, eax\r\n    mov word ptr [EBP].Client_Reg_Struc.Client_ESP, dx ; if ESP is to increase, adjust it last!\r\n    ret\r\n    align 4\r\nSimulate_Far_Ret endp\r\n\r\n;--- simulate a far call in v86-mode\r\n;--- EBP -> client struct\r\n;--- cx: new CS -> client_reg_struct.cs\r\n;--- edx: new IP -> client_reg_struct.eip\r\n;--- current v86 CS:IP is pushed onto the v86 stack\r\n;--- modifies EAX, ECX, EDX\r\n;--- v5.87: take care of SP wrap\r\n\r\nSimulate_Far_Call proc public\r\n    movzx ecx, cx\r\n    movzx edx, dx\r\n    sub word ptr [EBP].Client_Reg_Struc.Client_ESP, sizeof RETFWS ; if ESP is to decrease, adjust it first!\r\n    push ecx\r\n    push edx\r\n    MOV dx, word ptr [EBP].Client_Reg_Struc.Client_ESP\r\n    MOV cx, word ptr [EBP].Client_Reg_Struc.Client_SS\r\n    shl ecx, 4\r\n    mov eax, [EBP].Client_Reg_Struc.Client_EIP\r\n    mov [ecx+edx], ax\r\n    add dx, 2\r\n    mov eax, [EBP].Client_Reg_Struc.Client_CS\r\n    mov [ecx+edx], ax\r\n    pop [EBP].Client_Reg_Struc.Client_EIP\r\n    pop [EBP].Client_Reg_Struc.Client_CS\r\n    ret\r\n    align 4\r\nSimulate_Far_Call endp\r\n\r\n;--- simulate an IRET in v86-mode\r\n;--- INP: EBP -> Client_Reg_Struc\r\n;--- modifies EAX,ECX,EDX\r\n;--- v5.87: take care of SP wrap\r\n\r\nSimulate_Iret proc public\r\n    MOVZX edx, word ptr [EBP].Client_Reg_Struc.Client_ESP\r\n    MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS\r\n    SHL ecx, 4\r\n    movzx eax, word ptr [ecx+edx]\r\n    mov [EBP].Client_Reg_Struc.Client_EIP, eax\r\n    add dx, 2\r\n    mov ax, word ptr [ecx+edx]\r\n    mov [EBP].Client_Reg_Struc.Client_CS, eax\r\n    add dx, 2\r\n    mov ax, word ptr [ecx+edx]\r\nif 1\r\n    or ah, V86IOPL shl 4  ;to be safe, set IOPL=3 for v86\r\nendif\r\n    mov word ptr [EBP].Client_Reg_Struc.Client_EFlags, ax\r\n    add dx, 2\r\n    mov word ptr [EBP].Client_Reg_Struc.Client_ESP, dx   ; if ESP is to increase, adjust it last!\r\n    ret\r\n    align 4\r\nSimulate_Iret endp\r\n\r\n;--- simulate an V86 Int\r\n;--- INP: EBP -> Client_Reg_Struc\r\n;--- INP: EAX == INT #\r\n;--- modifies EAX, ECX, EDX\r\n;--- v5.87: take care of SP wrap\r\n\r\nSimulate_Int proc\r\n\r\n;--- v5.86: check if a v86hook is installed and call it first\r\n\tmov edx, [pV86Hooks]\r\n\tcmp edx, 0\r\n\tjz @F\r\n\tmov ecx, [edx+4*eax]\r\n\tjecxz @F\r\n\tcall [dwHookProc]   ;expects: eax=int#, ecx=hookproc, ebp->client regs\r\n\tjnc int_handled\r\n@@:\r\n    sub word ptr [EBP].Client_Reg_Struc.Client_ESP, sizeof IRETWS; if ESP is to decrease, adjust it first!\r\n    shl eax, 2\r\n    push dword ptr [eax]\r\n    movzx edx, word ptr [EBP].Client_Reg_Struc.Client_ESP\r\n    MOVZX ecx, word ptr [EBP].Client_Reg_Struc.Client_SS   ; get address of v86 SS:SP\r\n    SHL ECX,4\r\n\r\n    MOV EAX,[EBP].Client_Reg_Struc.Client_EIP\r\n    MOV [ecx+edx], ax                           ; set IP on the stack frame\r\n    add dx, 2\r\n    MOV EAX,[EBP].Client_Reg_Struc.Client_CS\r\n    MOV [ecx+edx], ax                           ; set CS on the stack frame\r\n\r\n    pop eax  ; get CS:IP from IVT\r\n    MOV word ptr [EBP].Client_Reg_Struc.Client_EIP, AX\r\n    shr eax, 16\r\n    MOV [EBP].Client_Reg_Struc.Client_CS, EAX\r\n    add dx, 2\r\n\r\n    MOV EAX, [EBP].Client_Reg_Struc.Client_EFlags\r\n    MOV [ecx+edx], AX                           ; set FL on the stack frame\r\n    and AH, not (1+2)                           ; clear TF+IF; RF?\r\n    mov [EBP].Client_Reg_Struc.Client_EFlags, eax\r\nint_handled:\r\n    ret\r\n    align 4\r\nSimulate_Int endp\r\n\r\n;--- prepare for nested execution.\r\n;--- store current v86 cs:ip onto the v86 stack, and\r\n;--- set current v86 cs:ip to the \"back\" bp.\r\n;--- modifies EAX,ECX,EDX\r\n\r\nBegin_Nest_Exec proc public\r\n    mov ecx, [dwRSeg]\r\n    movzx edx, [bBpBack]\r\n    jmp Simulate_Far_Call\r\n    align 4\r\n\r\nBegin_Nest_Exec endp\r\n\r\n;--- pop the \"back\" bp from the client's stack\r\n\r\nEnd_Nest_Exec proc public\r\n    call Simulate_Far_Ret\r\n    ret\r\n    align 4\r\n\r\nEnd_Nest_Exec endp\r\n\r\n;--- exec a v86 int immediately\r\n;--- EBP -> Client_Reg_Struc\r\n;--- EAX = INT# to execute\r\n\r\nExec_Int proc\r\n\r\n    call [vmm_service_table.pSimulate_Int]\r\n\r\nExec_Int endp   ;fall through\r\n\r\n;--- Resume_Exec actually runs the v86 code\r\n\r\nResume_Exec proc public\r\n    push [dwStackCurr]\r\n    pushad              ; save PL0 GPRs\r\n    mov [dwStackCurr],esp\r\n    @v86popregX         ; load ESP with EBP and restore v86 GPRs\r\n    add ESP, CRS_SIZEX  ; skip extra fields in Client_Reg_Struc\r\n    IRETD               ; and jump to v86 mode\r\n    align 4\r\nResume_Exec endp\r\n\r\n;--- this breakpoint is used to continue execution in protected-mode in a\r\n;--- nested execution block after Resume_Exec\r\n\r\nV86_Back proc\r\n    add esp,4   ;skip return address\r\n    popad\r\n    pop     [dwStackCurr]\r\n    ret\r\n    align 4\r\n\r\nV86_Back endp\r\n\r\n;--- save client's state to EDI\r\n\r\nSave_Client_State proc\r\n    push esi\r\n    push edi\r\n    mov ecx,size Client_Reg_Struc/4\r\n    mov esi, ebp\r\n    rep movsd\r\n    pop edi\r\n    pop esi\r\n    ret\r\n    align 4\r\nSave_Client_State endp\r\n\r\n;--- restore client's state from ESI\r\n\r\nRestore_Client_State proc\r\n    push esi\r\n    push edi\r\n    mov ecx,size Client_Reg_Struc/4\r\n    mov edi, ebp\r\n    rep movsd\r\n    pop edi\r\n    pop esi\r\n    ret\r\n    align 4\r\nRestore_Client_State endp\r\n\r\nif ?VME\r\n;--- set/reset the VME flag in CR4 if supported\r\n;--- INP: AL[0] new state of flag\r\n\r\nSetVME proc public\r\n    test byte ptr [dwFeatures], 2        ;VME supported?\r\n    jz @@novme\r\n    .586p\r\n    mov ecx, CR4\r\n    and al, 1\r\n    and cl, not 1\r\n    or cl,al\r\n    mov CR4, ecx\r\n    .386\r\n@@novme:\r\n    ret\r\n    align 4\r\nSetVME endp\r\n\r\nendif\r\n\r\nif ?I41SUPP\r\nInt41_Entry proc public\r\n    iretd           ; just don't route it to v86-mode\r\n    align 4\r\nInt41_Entry endp\r\nendif\r\n\r\n;--- Int 15h handler\r\n;--- to detect Ctrl-Alt-Del.\r\n;--- it also hooks the BIOS A20 functions\r\n;\r\nInt15_Entry PROC public\r\n\r\n;--- probably good to check if coming from v86-mode?\r\n\r\n    cmp ax,4F53h    ;DEL pressed?\r\n    jz @@isdel\r\nife ?HOOK13\r\n    cmp ax,9101h    ;diskette interrupt done?\r\n    jz @@isfdirq\r\nendif\r\nif ?A20PORTS\r\n    cmp ah,24h      ;A20?\r\n    jz @@isa20\r\nendif\r\n@@v86mon:\r\n    push 15h\r\n    JMP V86_Monitor ;route interrupt to v86\r\n\r\nif ?A20PORTS\r\n\r\n;--- catch int 15h, ax=2400h and ax=2401h\r\n\r\n@@isa20:\r\n    cmp al,2\r\n    jnb @@v86mon\r\n    @dprintf ?A20DBG, <\"Int 15h, ax=%X called\",10>, ax\r\n if 0\r\n    call A20_Set ;al=0|1\r\n    mov ah,0\r\n    and [esp].IRETDV86._Efl, not 1\r\n else\r\n    mov ah,86h\r\n    or [esp].IRETDV86._Efl, 1\r\n endif\r\n    iretd\r\nendif\r\n\r\n@@isdel:\r\n    PUSH EAX\r\n    MOV AL,CS:[@KB_FLAG]    ; Have the keys CTRL & ALT\r\n    AND AL,1100B            ; been pressed ?\r\n    CMP AL,1100B            ; If not,  continue working\r\n    POP EAX\r\n    JNZ @@v86mon\r\n    sub esp, CRS_SIZEX      ; space for fields between PUSHADS and IRETDV86\r\n    PUSHAD\r\n    MOV EBP,ESP             ; ebp -> Client_Reg_Struc\r\n\r\n    push ss\r\n    pop ds\r\n    push ss\r\n    pop es\r\n_SoftBoot::\r\n    mov esp,[dwStackCurr]\r\n    cld\r\n    call Begin_Nest_Exec\r\n    mov eax,15h             ; call the v86 int15 hookers\r\n    call Exec_Int\r\n;   call End_Nest_Exec       ; not needed\r\n;--- possibly one should check if the carry flag has been cleared\r\n;--- by one of the hookers. Then reboot should NOT be done.\r\n\r\nif 1\r\n;--- v5.75: int 15h, ah=4Fh usually is called during an IRQ. Is the PIC\r\n;---        waiting for an EOI? Has the keyboard been disabled?\r\n    and word ptr ds:[@KB_FLAG],not 01100001100b ;reset Ctrl+Alt status\r\n    and byte ptr ds:[496h],not 1111b            ;reset Ctrl,Alt,E0,E1 status\r\n    mov al,0Bh      ;get ISR of MPIC\r\n    out 20h,al\r\n    in al,20h\r\n    test al,02      ;IRQ 1 happened?\r\n    jz @F\r\n    mov al,20h      ;send EOI to PIC\r\n    out 20h,al\r\n@@:\r\n    XOR ECX,ECX\r\n@@: IN AL,64h       ;wait until kbd buffer is free\r\n    TEST AL,02\r\n    LOOPNZW @B\r\n    MOV AL,0AEh     ;enable keyboard\r\n    OUT 64h,AL\r\nendif\r\nif ?FASTBOOT\r\n    test [bV86Flags], V86F_FASTBOOT\r\n    jnz fastboot\r\nendif\r\n    jmp _Reboot\r\n\r\nife ?HOOK13\r\n@@isfdirq:\r\n    btr word ptr ss:[bDiskIrq],0\t; copy out of DMA buffer required?\r\n    jnc @@v86mon\r\n    push ss\r\n    pop ds\r\n    push ss\r\n    pop es\r\n    call Dma_CopyBuffer\r\n    jmp @@v86mon\r\nendif\r\n\r\nInt15_Entry ENDP\r\n\r\nif ?UNLOAD\r\n\r\n;--- unload Jemm\r\n;--- this is invoked by a v86 breakpoint called by Jemm16\r\n;--- EBP must be restored before exiting!\r\n;--- the functions returns with a RETF to Jemm16, but stays\r\n;--- in protected mode\r\n\r\nUnload proc\r\n\r\n    call Simulate_Far_Ret\r\n\r\nif ?VME\r\n    mov  al,0\r\n    call SetVME\r\nendif\r\n\r\nif ?FREEXMS\r\n    call Pool_FreeAllBlocks\r\nendif\r\n\r\n    @dprintf ?UNLDBG, <\"Unload: v86CS:EIP=%X:%X, v86SS:ESP=%X:%X\",10>,\\\r\n        [ebp].Client_Reg_Struc.Client_CS, [ebp].Client_Reg_Struc.Client_EIP,\\\r\n        [ebp].Client_Reg_Struc.Client_SS, [ebp].Client_Reg_Struc.Client_ESP\r\n\r\n;--- set base of REAL_CODE_SEL and REAL_DATA_SEL\r\n;--- to the current v86 CS/SS\r\n\r\n    cld\r\n    mov edi,offset V86GDT + REAL_CODE_SEL + 2\r\n    movzx eax, word ptr [ebp].Client_Reg_Struc.Client_CS\r\n    shl eax, 4\r\n    stosw         ;set base(0-15)\r\n    shr eax, 16\r\n    stosb         ;set base(16-23)\r\n    add edi, sizeof DESCRIPTOR - 3\r\n    movzx eax, word ptr [ebp].Client_Reg_Struc.Client_SS\r\n    shl eax, 4\r\n    stosw         ;set base(0-15)\r\n    shr eax, 16\r\n    stosb         ;set base(16-23)\r\n\r\n;--- restore v86 interrupt vectors\r\n\r\n    xor ebx, ebx\r\n    mov eax, [OldInt06]\r\n    mov [ebx+06h*4],eax\r\n    mov eax, [OldInt19]\r\n    mov [ebx+19h*4],eax\r\nif ?VDS\r\n    call VDS_Exit\r\nendif\r\n    mov eax, [OldInt67]\r\n    mov ds:[67h*4],eax\r\n\r\n    mov edx, [ebp].Client_Reg_Struc.Client_EIP\r\n    mov ecx, [ebp].Client_Reg_Struc.Client_ESP\r\n    mov ebp, [ebp].Client_Reg_Struc.Client_EBP\r\nife ?INTEGRATED\r\n    mov bx, [XMSCtrlHandle]\r\nelse\r\n    mov bl, [A20Index]\r\nendif\r\nif 0                        ; now done by Jemm16::UnloadJemm\r\n    push 0\r\n    push word ptr 3FFh\r\n    LIDT FWORD ptr [esp]    ; reset IDT to real-mode\r\nendif    \r\n    MOV AX,REAL_DATA_SEL    ; before returning, set the\r\n    MOV DS,EAX              ; segment register caches\r\n    MOV ES,EAX\r\n    MOV FS,EAX\r\n    MOV GS,EAX\r\n    MOV SS,EAX\r\n    mov ESP,ECX\r\n\r\n;--- the rest will be done by the 16bit part\r\n\r\n    push REAL_CODE_SEL\r\n    push edx\r\n    retf\r\n\r\nUnload endp\r\nendif\r\n\r\nif ?FASTBOOT\r\n\r\n;--- how is FASTBOOT implemented?\r\n;--- the important thing is to restore the IVT to the values\r\n;--- *before* DOS has been installed. This requires:\r\n;--- a). DOS must hook int 19h and restore the vectors it has modified\r\n;---     (vectors which count are 00-1F, 40-5F and 68-77). It must also\r\n;---     save vector 15h (which is modified by himem.sys).\r\n;--- b). the vectors must be saved at 0070:100h\r\n;---     msdos saves at least 10,13,15,19,1B.\r\n;--- c). jemm must be loaded as a device driver, so it is loaded very \r\n;---     early before other drivers/tsrs.\r\n;--- if these requirements are met, Jemm will do with FASTBOOT: \r\n;--- 1. save int vectors 0-1F, 40-5F, 68-77 (20h+20h+10h = 50h*4=320 bytes)\r\n;---    on init.\r\n;--- 2. on ctrl-alt-del, restore these vectors, save the vector for int 19h\r\n;---    which DOS has saved internally at 0070:0100+x and modify it to point\r\n;---    to a breakpoint.\r\n;--- 3. call v86-int 19h. DOS will restore the vectors it has saved.\r\n;--- 4. Jemm regains control, with DOS already deactivated. Now clear\r\n;---    vectors 20-3F, 60-67 and 78-FF, restore int 19h to the value saved \r\n;---    previously.\r\n;--- 5. jump to real-mode and do an int 19h again.\r\n\r\nrestorevecs proc\r\n    pushad\r\n    xor eax, eax\r\n    mov edi, eax\r\n    mov esi, [pSavedVecs]\r\n    push ds\r\n    pop es\r\n    @dprintf ?RBTDBG, <\"restoring vectors 00-1F, 40-5F, 68-77\",10>\r\n    mov ecx, 20h\r\n    rep movsd           ;set 00-1F\r\n    add edi, 20h*4\r\n    mov cl, 20h\r\n    rep movsd           ;set 40-5F\r\n    add edi, 8*4\r\n    mov cl, 10h\r\n    rep movsd           ;set 68-77\r\n\r\n;--- search the int 19h vector stored at 0070:100h\r\n\r\n    mov esi,700h+100h\r\n    mov cl,5\r\n@@nextitem:\r\n    lodsb\r\n    mov bl,al\r\n    lodsd\r\n    cmp bl,19h\r\n    loopnz @@nextitem\r\n    stc\r\n    jnz @@norestore     ;not found. no FASTBOOT possible\r\n\r\n    mov edx, [dwRSeg]\r\n    shl edx, 16\r\n    mov dl, [bBpTab]    ;use the first BP for returning to the monitor\r\n    @dprintf ?RBTDBG, <\"vector 19h saved by DOS=%X, temp vector=%X\",10>, eax, edx\r\n    mov [OldInt19],eax  ;save the vector stored by DOS\r\n    mov [esi-4],edx\r\n\r\n;--- restoring the XBDA should be done by DOS - MS-DOS does, but\r\n;--- only if the XBDA size won't exceed 1? kB!\r\n;--- Problem: size of XBDA (byte at offset 0) may be invalid.\r\nif 0;?MOVEXBDA\r\n    movzx esi,word ptr ds:[@XBDA]\r\n    cmp si, 0A000h\r\n    jb @@noxbda\r\n    shl esi, 4\r\n    movzx eax,byte ptr [esi];first byte of XBDA is (rather:should be) size in KB\r\n    sub ds:[@MEM_SIZE],ax   ;restore size of low memory\r\n    movzx edi,word ptr ds:[@MEM_SIZE]\r\n    shl edi, 6\r\n    mov ds:[@XBDA],di\r\n    shl edi, 4\r\n    @dprintf ?RBTDBG, <\"restoring XBDA to %X\",10>, di\r\n    mov ecx, eax\r\n    shl ecx,8\r\n    rep movsd\r\n@@noxbda:\r\nendif\r\n\r\nif 0\r\n    in al,0A1h\r\n    or al,03Fh\r\n    out 0A1h,al\r\n    in al,021h\r\n    or al,0F8h\r\n    out 21h,al\r\nendif\r\n\r\n    clc\r\n@@norestore:\r\n    popad\r\n    ret\r\nrestorevecs endp\r\n\r\nfastboot proc\r\n\r\n    @dprintf ?RBTDBG, <\"fastboot reached\",10>\r\n\r\nif 0  ;kbd enable has been done in int 15h handler already\r\n    mov al,0Bh\r\n    out 20h,al\r\n    in al,20h\r\n    and al,al\r\n    jz @@fb_1\r\n    mov al,20h              ; send EOI to master PIC (for keyboard)\r\n    out 20h,al\r\n@@fb_1:\r\n    mov al,0AEh             ; (re)enable keyboard\r\n    out 64h,al\r\nendif\r\n\r\n    call Begin_Nest_Exec\r\nif 1\r\n    @dprintf ?RBTDBG, <\"resetting PS/2 mouse\",10>\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 0C201h  ;reset PS/2 mouse\r\n    mov eax,15h\r\n    call Exec_Int\r\nendif\r\n    @dprintf ?RBTDBG, <\"calling restorevecs\",10>\r\n    call restorevecs\r\n    jc _Reboot\r\n\r\nif ?ADDCONV\r\n;--- if base memory has been extended beyond A000h, it must be\r\n;--- restored.\r\n    cmp word ptr ds:[@MEM_SIZE], 640\r\n    jbe @F\r\n    cmp UMBsegments.wSegm, 0A000h\r\n    jnz @F\r\n    mov word ptr ds:[@MEM_SIZE], 640\r\n@@:\r\nendif\r\n\r\n;--- (re)launch int 19h. this will make DOS restore vectors.\r\n;--- since the int 19h value saved by DOS has been modified by Jemm,\r\n;--- execution will reach fastboot_1 at last.\r\n\r\n    @dprintf ?RBTDBG, <\"fastboot: launching Int 19h\",10>\r\n\r\n;--- it's bptable.pInt06 that has to be modified -\r\n;--- because that's the FIRST breakpoint ( see restorevecs ).\r\n    mov ds:[bptable.pInt06], offset fastboot_1\r\n    mov eax,19h\r\n    call Exec_Int\r\nfastboot_1:\r\n\r\n;--- now DOS has restored its vectors\r\n;--- restore the previously modified int 19h vector\r\n\r\n    @dprintf ?RBTDBG, <\"reached fastboot_1\",10>\r\n    mov eax,[OldInt19]\r\n    mov ds:[19h*4],eax\r\nif 0\r\n    mov dword ptr ds:[1Eh*4],0F000EFC7h\t;restore int 1Eh to original value\r\nendif\r\n\r\n;--- the v86 space is now \"without\" DOS\r\n\r\n;--- set vectors 20-3F, 60-67 and 78-FF to 0000:0000\r\n\r\n    push ds\r\n    pop es\r\n    mov edi,20h*4\r\n    mov ecx,20h\r\n    xor eax,eax\r\n    rep stosd       ;20-3F\r\n    add edi,20h*4\r\n    mov cl,8\r\n    rep stosd       ;60-67\r\n    add edi,10h*4\r\n    mov cl,8\r\n    rep stosd       ;78-7F\r\n\r\nif 0;?CLEARBOOTFLGS\r\n    movzx ecx, word ptr ds:[@XBDA]\r\n    jecxz @F\r\n    shl ecx, 4\r\n    mov word ptr [ecx+90h],0    ;clear boot flags (can't do this, probably BIOS specific)\r\n@@:\r\nendif\r\n\r\nif ?RBTDBG\r\n    mov ecx,20h\r\n    xor esi,esi\r\n    xor ebx,ebx\r\nnextitem:\r\n    lodsd\r\n    @dprintf ?RBTDBG, <\"%X: %X        \">, bl, eax\r\n    test cl,1\r\n    jz @F\r\n    @dprintf ?RBTDBG, <10>\r\n@@:\r\n    inc ebx\r\n    loop nextitem\r\nendif\r\n\r\n    mov bl,1        ;set flag for fastboot\r\n    jmp Reboot_1\r\n\r\nfastboot endp\r\nendif\r\n\r\n;--- Reboot - reboot the machine\r\n\r\n_Reboot proc\r\n\r\n    @dprintf ?RBTDBG, <\"Reboot enter\",10>\r\nif ?INT19RBT\r\n    mov eax,19h\r\n    call Exec_Int   ;may not return\r\nendif\r\nInt19_V86Entry::\r\nif ?FASTBOOT\r\n    mov bl,0    ;bit 0: 0=\"full\" reboot, 1=fastboot\r\nendif\r\n    mov eax, DEVICE_REBOOT_NOTIFY\r\n    call vmm_service_table.pControlProc\r\n    test [bV86Flags], V86F_ALTBOOT\r\n    jz Reboot_1\r\n\r\n    MOV WORD PTR DS:[@RESET_FLAG],1234H ; 1234h=warm boot\r\n    mov al,0FEh\r\n    out 64h,al\t;reset via KBC\r\nif 0\r\n    in al,92h   ;reset via PS/2 port A\r\n    or al,1\r\n    out 92h,al\r\nendif\r\n;    hlt\r\n\r\nReboot_1::      ;<--- entry from FASTBOOT (see above)\r\n\r\n    @dprintf ?RBTDBG, <\"Reboot_1 reached, int 19=%X, XBDA segment=%X\",10>, dword ptr ds:[19h*4], word ptr ds:[@XBDA]\r\n\r\n    MOV WORD PTR DS:[@RESET_FLAG],1234H ; 1234h=warm boot\r\n\r\nif 1\r\n    MOV AL,0Fh  ;set shutdown byte; bit 7: 0=enable NMI \r\n    out 70h,al\r\n    MOV AL,0    ;software-reset\r\n    out 71h,al\r\nendif\r\n\r\nife ?USETRIPLEFAULT\r\n    mov edi, 7E00h\r\n    mov esi, offset rmcode\r\n    mov ecx, offset endofrmcode - offset rmcode\r\n    push ds\r\n    pop es\r\n    rep movsb\r\n\r\n  if ?RESETCR4\r\n   if 0\r\n    cmp [dwFeatures],ecx    ; does CR4 exist?\r\n    jz @F\r\n    .586p                   ; CR4 exists since Pentium ( not exactly true )\r\n    mov cr4, ecx            ; reset CR4\r\n    .386\r\n@@:\r\n   else\r\n    mov al, 0\r\n    call SetVME             ; just reset CR4.VME \r\n    xor ecx, ecx\r\n   endif\r\n  endif\r\n\r\n  if ?FASTBOOT\r\n    test bl,1\r\n    jz @F\r\n    sub edi, 5      ;skip the jmp f000:fff0\r\n    mov esi, offset rmcode2\r\n    mov cl, offset endofrmcode2 - offset rmcode2\r\n    rep movsb\r\n    and byte ptr ds:[47Bh],not 20h  ;reset VDS bit\r\n    mov eax, DEVICE_REBOOT_NOTIFY\r\n    call vmm_service_table.pControlProc\r\n@@:\r\n  endif\r\n    xor edx, edx\r\n    push edx\r\n    push word ptr 3FFh\r\n    .386p\r\n    LIDT FWORD ptr [esp]     ; reset the IDT to real-mode\r\n    .386\r\n\r\n    MOV AX,REAL_DATA_SEL    ; before returning to real-mode set the\r\n    MOV DS,EAX              ; segment register caches\r\n    MOV ES,EAX\r\n    MOV FS,EAX\r\n    MOV GS,EAX\r\n    MOV SS,EAX              ; set SS:ESP\r\n    MOV ESP,7C00h\r\n    MOV ECX,CR0             ; prepare to reset CR0 PE and PG bits\r\n    AND ECX,7FFFFFFEH\r\n    XOR EAX, EAX\r\n    db 66h                 ; jmp REAL_CODE_SEL:7E00h\r\n    db 0eah                ; (this sets CS attributes to 16bit\r\n    Dw 7E00h               ; and FFFF limit)\r\n    dw REAL_CODE_SEL\r\nelse\r\n    xor edx,edx             ; cause a triple fault to reboot\r\n    push edx\r\n    push edx\r\n    lidt fword ptr [esp]\r\n    int 3\r\nendif\r\n\r\nrmcode:\r\n    mov cr0,ecx             ;7E00: 0F 22 C1   mov cr0, ecx (switches to real-mode)\r\n    db 0EAh                 ;7E03: EA         jmp 0000:7E08 (set CS=0000)\r\n    dw 07E08h, 0000h        ;      0000:7E08  \r\n    mov cr3,eax             ;7E08: 0F 22 D8   mov cr3, eax\r\n    mov ss,eax              ;7E0B: 8E D0      mov ss, ax (in 16-bit; set SS=0000)\r\n    db 0EAh                 ;7E0D: EA         jmp F000:FFF0\r\n    dw 0FFF0h, 0F000h       ;      F000:FFF0  \r\nendofrmcode:\r\n\r\nif ?FASTBOOT\r\nrmcode2:\r\n if ?RBTDBG\r\n    db 0B8h, 52h, 0Eh       ;mov ax,0e52h 'R'\r\n    db 0BBh, 07h, 00h       ;mov bx,0007h\r\n    int 10h\r\n    mov al,'B'\r\n    int 10h\r\n    mov al,'T'\r\n    int 10h\r\n    mov ah,0\r\n    int 16h\r\n endif\r\n if 0\r\n    push cs\r\n    pop ds\r\n    push cs\r\n    pop es\r\n    sti\r\n    mov ah,00\r\n    mov dl,80h\r\n    int 13h                 ;disk reset\r\n    db 0B8h, 01h, 02h       ;mov ax,0201h\r\n    db 0B9h, 01h, 00h       ;mov cx,0001h\r\n    db 0BAh, 80h, 00h       ;mov dx,0080h\r\n    db 0BBh, 00h, 7Ch       ;mov bx,7C00h\r\n    int 13h                 ;read sector 1 of HD 0\r\n    push es\r\n    push ebx                ;in 16-bit: push bx\r\n    retf                    ;jump to boot code\r\n else\r\n    int 19h\r\n endif\r\nendofrmcode2:\r\nendif\r\n\r\n_Reboot endp\r\n\r\n    align 4\r\n\r\nif ?SERVTABLE\r\n\r\nvmm_service_table label VMM_SERV_TABLE\r\n    dd Simulate_Int\r\n    dd Simulate_Iret\r\n    dd Simulate_Far_Call\r\n    dd Simulate_Far_Ret\r\n    dd Begin_Nest_Exec\r\n    dd Exec_Int\r\n    dd Resume_Exec\r\n    dd End_Nest_Exec\r\n    dd Simulate_IO\r\n    dd Yield\r\n    dd VDS_Call_Table\r\n    dd VCPI_Call_Table\r\n    dd IO_Trap_Table\r\n;    dd V86_Monitor        ;v5.87: obsolete\r\n;    dd offset dwStackCurr ;v5.87: obsolete\r\n    dd MoveMemory\r\n    dd offset pV86Hooks\t;v5.85\r\n    dd offset V86_Fault\t;v5.86\r\n    dd offset _ret\t\t;v5.86: control proc\r\n\r\nendif\r\n\r\n.text$03 ends\r\n\r\n    END _start\r\n"
  },
  {
    "path": "src/JEMM32.INC",
    "content": "\r\n;--- these files contains things for 32-bit ONLY\r\n\r\n?RING0EXC       EQU 1       ; 1=display more infos on exc in ring 0\r\n?ROMRO          EQU 1       ; 1=make ROM page FF000 r/o\r\n;?ALTBOOT       EQU 1       ; 1=support ALTBOOT option\r\n?MMASK          EQU 1       ; 1=trap DMA master mask port (0F, DE)\r\n?INVLPG         EQU 1       ; 1=use INVLPG opcode on 80486+\r\n?CLEARHIGHESP   EQU 1       ; 1=clear hiword(esp) for buggy VCPI clients\r\nifndef ?AUXIO\r\n?AUXIO          EQU 0       ; 1=use COMx for I/O, 0=use INT 10h/29h and INT 16h\r\nendif\r\n?USEINT10       EQU 1       ; 1=use int 10h for output, 0=use int 29h\r\n?SERVTABLE      equ 1       ; 1=expose service table (for JLoad)\r\n?FASTMON        equ 1       ; 1=faster monitor jmp for int 00-0F\r\n?SHAREDGDT      equ 1       ; 1=place GDT in shared address space\r\n?EXC10          equ 1       ; 1=detect exception 10h (floating point)\r\n?I41SUPP        equ 1       ; 1=support Int 41h in protected-mode\r\n?ALT_TRAPHDL    equ 1       ; v5.85: 1=call alternate trap handler for system ports\r\nifndef ?SHAREDIDT\r\n?SHAREDIDT      equ 0       ; std=?, 1=place IDT in shared address space\r\nendif\r\n\r\n?CODEIN2PAGE    EQU 0       ; 1=move code away from first page\r\n?USETRIPLEFAULT EQU 0       ; 1=use triple fault to reboot\r\n;ifndef ?SAFEKBD            ; v5.87: obsolete\r\n;?SAFEKBD        equ 0       ; 1=use original vector for int 16h kbd poll\r\n;endif\r\n?DYNTRAPP60     equ 1       ; 1=trap port 60h conditionally only\r\n;ifndef ?MAPEXTMEM          ; v5.86: obsolete; now covered by SB option\r\n;?MAPEXTMEM      equ 0       ; 1=\"identity map\" extended memory (SBEINIT.COM/386SWAT may need this)\r\n;endif\r\n\r\nGDT_SIZE        equ 200h    ; size of GDT\r\n\r\n\tinclude jsystem.inc\r\n\r\n;--- the following equates define Jemm's memory layout:\r\n;--- Jemm starts at 110000h with data and code (4-5 pages)\r\n;--- system space is F8000000h-F83FFFFFh:\r\n;---  + 0000h: reserved (uncommitted)\r\n;---  + 1000h: page directory\r\n;---  + 2000h: page table 0\r\n;---  + 3000h: system page table\r\n;---  + 4000h: ring 0 stack (4k)\r\n;---  + 5000h: task state segment\r\n;---  + a: page pool descriptors, EMS handle/status tables\r\n;---  + b: shadowed HMA (if XMS handle array is in HMA)\r\n;---  + c: DMA buffer\r\n;---  + d: scratch region for physical page mapping\r\n\r\n?PAGETAB0       EQU 2000h           ; rel. offset (?SYSBASE) for page table 0\r\n?TSSLEN         EQU 68h+32+2000h+8  ; size of TSS \"segment\"\r\n\r\n?HLPSTKSIZE     EQU 80h     ; size of help stack for VCPI\r\n\r\n;--- other constants\r\n\r\nif ?DMAPT or ?VDS\r\n?DMABUFFMAX     EQU 128             ; max DMA-Buffer size in kB\r\nendif\r\n\r\n?FASTENTRIES    equ 32-1\r\n\r\n\tinclude x86.inc\r\n\tinclude xms.inc\r\n\r\n;--- selector definitions\r\n;--- FLAT_CODE_SEL must be first descriptor in GDT\r\n\r\nFLAT_CODE_SEL   equ 1 * sizeof DESCRIPTOR\r\nFLAT_DATA_SEL   equ 2 * sizeof DESCRIPTOR\r\nV86_TSS_SEL     equ 3 * sizeof DESCRIPTOR\r\nREAL_CODE_SEL   equ 4 * sizeof DESCRIPTOR\r\nREAL_DATA_SEL   equ 5 * sizeof DESCRIPTOR ;must follow REAL_CODE_SEL\r\nif ?CLEARHIGHESP\r\nV86_STACK_SEL   equ 6 * sizeof DESCRIPTOR\r\nendif\r\n\r\n;--- BIOS constants\r\n\r\n@KB_FLAG        EQU 417H            ; Status SHIFT/CTRL/ALT etcetera.\r\n@RESET_FLAG     EQU 472H            ; Flag for Warmboot (=1234h)\r\n\r\n;--- EMS constants\r\n\r\nEMS_MAX_HANDLES       EQU 255 ; std=255, the EMS handle is a byte value\r\n;EMS_MAX_PAGES_ALLOWED EQU 2048; std=2048, 2048 * 16kB = 32768 kB (32 MB)\r\nEMS_MAXSTATE          EQU 64  ; std=64, number of EMS save state items\r\n\r\n;--- EMS handle descriptor\r\n\r\nEMSHD struct\r\nehd_wIdx    dw ?    ;index of EMSPD for first page\r\nehd_bFlags  db ?\r\nehd_bSS     db ?    ;saved state index\r\nEMSHD ends\r\n\r\n;--- values for flags\r\n\r\nEMSH_USED   equ 1\r\n\r\nEMSSTAT struct\r\ndPg0    dd ?\r\ndPg1    dd ?\r\ndPg2    dd ?\r\ndPg3    dd ?\r\nEMSSTAT ends\r\n\r\n;--- flags for pool descriptor block\r\n\r\nPBF_DONTEXPAND  EQU 1   ; don't try to expand this block\r\nPBF_DONTFREE    EQU 2   ; don't try to free this block\r\n\r\n;--- equates from JLM.INC\r\n;--- OUT_INSTR, WORD_IO and DWORD_IO are used as offset in tables\r\n;--- and hence cannot be changed; see DMA.ASM!\r\n\r\nOUT_INSTR   equ 04h\r\nWORD_IO     equ 08h\r\nDWORD_IO    equ 10h\r\nSTRING_IO   EQU 20h\r\nREP_IO      EQU 40h\r\n\r\nDMAREQ struct\r\nbMode       db ?    ;saved \"mode\" value (register 0Bh or D6h)\r\nbFlags      db ?    ;flags (DMAF_ENABLED) \r\nBlockLen    dw ?    ;saved \"block length\" value\r\nBaseAdr     dw ?    ;saved \"block address\" value\r\nPageReg     db ?    ;saved \"page register\" value\r\ncDisable    db ?    ;is automatic translation disabled? (VDS)\r\nDMAREQ ends\r\n\r\nBPTABLE struct\r\npInt06  dd ?\r\npInt19  dd ?\t;this BP will be used by JLOAD and become \"dynamic\"\r\npInt67  dd ?\r\nif ?VDS\r\npInt4B  dd ?\r\nendif\r\npBack   dd ?\r\npI1587  dd ?\r\nif ?DMAPT\r\np1340   dd ?\r\nendif\r\npXMS    dd ?\r\npStrat  dd ?\r\npDev    dd ?\r\nif ?UNLOAD\r\npUnload dd ?\r\nendif\r\nBPTABLE ends\r\n\r\n;--- client register structure in V86 monitor.\r\n;--- consists of:\r\n;--- 1. PUSHAD\r\n;--- 2. Int#\r\n;--- 3. v86-exception frame with error code\r\n\r\nClient_Reg_Struc  struct\r\nClient_EDI  dd ?    ;+0\r\nClient_ESI  dd ?    ;+4\r\nClient_EBP  dd ?    ;+8\r\nClient_res0 dd ?    ;+12\r\nClient_EBX  dd ?    ;+16\r\nClient_EDX  dd ?    ;+20\r\nClient_ECX  dd ?    ;+24\r\nClient_EAX  dd ?    ;+28\r\nClient_Int  dd ?    ;+32 ; pushed as sign-extended byte - so use lowest byte only!\r\nClient_Error dd ?   ;+36\r\nClient_EIP  dd ?    ;+40\r\nClient_CS   dd ?    ;+44    \r\nClient_EFlags dd ?  ;+48\r\nClient_ESP  dd ?    ;+52\r\nClient_SS   dd ?    ;+56\r\nClient_ES   dd ?    ;+60\r\nClient_DS   dd ?    ;+64\r\nClient_FS   dd ?    ;+68\r\nClient_GS   dd ?    ;+72\r\nClient_Reg_Struc ends\r\n\r\n;--- extra bytes in Client_Reg_Struc if PUSHAD and IRETDV86 are removed\r\nCRS_SIZEX equ (sizeof Client_Reg_Struc - sizeof PUSHADS - sizeof IRETDV86)\r\n\r\n\r\n;--- entry in IO trap table\r\n;--- this is for the default port trap handler;\r\n;--- it handles byte ports only.\r\n\r\nIOTRAPENTRY struct\r\nbStart  db ?\r\nbEnd    db ?\r\ndwProc  dd ?\r\nIOTRAPENTRY ends\r\n\r\nPAGEMEM struct\r\ndwMaxMem4K          DD ?    ; max 4k pages allowed\r\ndwTotalMem4K        DD ?    ; total 4k pool pages\r\ndwUsedMem4K         DD ?    ; used 4k pool pages\r\nPAGEMEM ends\r\n\r\nDMABUFF struct\r\nDMABuffStart    DD  ?           ; start of DMA-Buffer (linear address)\r\nDMABuffSize     DD  ?           ; max size of the DMA-Buffer in bytes\r\nDMABuffStartPhys DD ?           ; start of DMA-Buffer (physical address)\r\nDMABuffFree     DD ?DMABUFFMAX/32 dup (?)   ; bits for DMA buffer handling\r\n                DB ?    ; space for 2 additional bits is needed!\r\nDMABUFF ends\r\n\r\n;--- Jemm Service Table\r\n;--- this structure is filled and returned by \r\n;--- IoCtl call 8. Used by JLoad.\r\n\r\nif ?SERVTABLE\r\nVMM_SERV_TABLE struct\r\npSimulate_Int           dd ?\r\npSimulate_Iret          dd ?\r\npSimulate_Far_Call      dd ?\r\npSimulate_Far_Ret       dd ?\r\npBegin_Nest_Exec        dd ?\r\npExec_Int               dd ?\r\npResume_Exec            dd ?\r\npEnd_Nest_Exec          dd ?\r\npSimulate_IO            dd ?\r\npYield                  dd ?\r\npVDS_Call_Table         dd ?\r\npVCPI_Call_Table        dd ?\r\npIO_Trap_Table          dd ?\r\n;pV86_Monitor            dd ?\t; linear address of V86_Monitor() (v5.87: obsolete with ppV86Hooks)\r\n;pStackCurr              dd ?\t; linear address of dwStackCurr (v5.87: obsolete with ppV86Hooks)\r\npMoveMemory             dd ?\t; linear address of MoveMemory()\r\nppV86Hooks              dd ?\t; v5.85: address of pV86Hooks variable\r\npV86Faults              dd ?\t; v5.86: address of V86Faults\r\npControlProc            dd ?\t; v5.86: address of ControlProc (called by Jemm on certain events)\r\nVMM_SERV_TABLE ends\r\nendif\r\n\r\n;--- messages for ControlProc callout (matches Win9x VMM values)\r\nSYSTEM_EXIT equ 5\r\nDEVICE_REBOOT_NOTIFY equ 17h\r\n\r\n;--- macros\r\n\r\n;--- return a pointer to PTEs\r\n;--- ofs: offset in page dir/page table 0/sys page table\r\n\r\n@GetPTEAddr macro ofs:req\r\n    exitm <[?SYSBASE+ofs]>\r\nendm\r\n\r\n;--- macro to convert a pointer to PTEs in system page table\r\n;--- to the according linear address\r\n\r\n@LinearFromSysPTE macro reg, pPT\r\nifdif <reg>,<pPT>\r\n    mov reg, pPT\r\nendif\r\n    sub reg, ?SYSBASE+?PAGETABSYS\r\n    shl reg, 10\r\n    add reg, ?SYSBASE\r\nendm\r\n\r\n;--- macro to convert a pointer to PDEs to linear address\r\n\r\n@LinearFromPDE macro reg, pPD\r\nifdif <reg>,<pPD>\r\n    mov reg, pPD\r\nendif\r\n    sub reg, ?PAGEDIR\r\n    shl reg, 20\r\n    exitm <reg>\r\nendm\r\n"
  },
  {
    "path": "src/POOL.ASM",
    "content": "\r\n;--- Jemm's memory pool implementation\r\n;--- originally written by Michael Devore\r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n;--- equates\r\n\r\nMINXMSSIZE  equ 16      ; ignore XMS blocks smaller 16 kB\r\n\r\n;--- assembly time constants\r\n\r\nif ?INTEGRATED\r\n?FREEXMS    equ 1       ; std=1; 1=free XMS blocks if jemm is unloaded; 0 might work\r\n?EXPANDFIRST equ 0      ; std=0, 1 might work\r\nelse\r\n?FREEXMS    equ 1       ; std=1; 0 won't work\r\n?EXPANDFIRST equ 0      ; std=0, 1 won't work\r\nendif\r\n\r\n;--- publics/externals\r\n\r\nexterndef EMS_CheckMax:near\r\n\r\n    include extern32.inc\r\n\r\n;--- Memory pool descriptor.\r\n;--- the pool descriptor array can grow up to 174784 bytes (64*2731) for 4 GB\r\n;--- standard is 80*(16+48) = 5120 bytes for 120 MB\r\n\r\n; number of bytes for allocation in a pool allocation descriptor\r\n; that are 48*8=384 bits, -> 384 * 4 kB -> 1536 kB\r\n\r\nPOOLDESC_ALLOCATION_SPACE  EQU 48\r\n\r\nPOOL_DESC struct\r\ndwAddressK      DD ? ; phys. base address in K (may not be XMS handle base if handle size changed later)\r\npXMSdesc        DD ? ; pointer to XMS handle descriptor array entry/pseudo-handle value\r\nw4kfree         DW ? ; free number of 4K slots (>b16kfree*4 if any partials)\r\nb16kfree        DB ? ; free number of 16K slots\r\nb16kmax         DB ? ; maximum number of 16K allocations (used allocation bytes*2)\r\nbStartAdj       DB ? ; unused K from XMS handle base for 4K alignment (0-3)\r\nbEndAdj         DB ? ; unused K at XMS handle end as 16K chunks (0-15)\r\nbFlags          DB ? ; various flag values\r\n                DB ? ; alignment\r\nbits            DB POOLDESC_ALLOCATION_SPACE dup (?)\r\nPOOL_DESC ends\r\n\r\n; size a pool descriptor will manage (in kB): 48*8*4 = 1536\r\n\r\nPOOLDESC_MEMSIZE equ POOLDESC_ALLOCATION_SPACE*8*4\r\n\r\n.text$01 SEGMENT\r\n\r\npmem PAGEMEM <0,0,0>\r\n\r\nPoolAllocationTable DD 0    ; start of pool descriptor array\r\nPoolAllocationEnd   DD 0    ; current end of descriptor array\r\nPoolAllocationMax   DD 0    ; max end of descriptor array (default: 80 entries = 120 MB)\r\n\r\nLastBlockAllocated  DD 0    ; last pool desc used for alloc\r\nLastBlockFreed      DD 0    ; last pool desc used for free\r\nXMSPoolBlockCount   DD 0    ; count of XMS handles allocated for pool descs (not counting initial UMB block)\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n; Pool memory management routines\r\n\r\n;--- convert desc index + nibble offset to a physical address\r\n;--- ecx=desc index\r\n;--- eax=nibble offset\r\n;--- out: physical address in EAX\r\n\r\nPool_GetPhysAddr proc public\r\n    .errnz sizeof POOL_DESC - 64\r\n;--- if sizeof POOL_DESC changes, next line must be adjusted!\r\n    shl ecx,6                       ; convert to 64-byte desc offset\r\n    shl eax,14                      ; 16K to bytes\r\n    add ecx, [PoolAllocationTable]  ; ecx -> pool desc for page\r\n\r\n    mov ecx,[ecx].POOL_DESC.dwAddressK\r\n    shl ecx,10                      ; K address to bytes\r\n    add eax,ecx                     ; = physical memory address of page\r\n    ret \r\n    align 4\r\nPool_GetPhysAddr endp\r\n\r\n; get free 4K page count in pool\r\n; out: EAX=free pages\r\n; destroys EDX\r\n; do not write segment registers here,\r\n; the function is called from VCPI protected mode API\r\n\r\nPool_GetFree4KPages PROC public\r\n\r\n    call Pool_GetFreeXMSPages   ; get free 4k XMS pages in EDX\r\n    mov eax,[pmem.dwTotalMem4K]\r\n    sub eax,[pmem.dwUsedMem4K]  ; eax = free 4k pages in Pool\r\n    add edx,eax                 ; edx = true free 4k pages\r\n    mov eax,[pmem.dwMaxMem4K]\r\n    sub eax,[pmem.dwUsedMem4K]  ; eax = max free 4k pages allowed\r\n    cmp eax,edx\r\n    jb @@uselower\r\n    mov eax,edx\r\n@@uselower:                     ; eax = min(eax, edx)\r\n\r\n    @dprintf ?POOLDBG, <\"Pool_GetFree4KPages: %X, dwUsedMem4K=%X, dwMaxMem4K=%X\",10>, eax, pmem.dwUsedMem4K, pmem.dwMaxMem4K\r\n    ret\r\n    align 4\r\n\r\nPool_GetFree4KPages ENDP\r\n\r\n; get free 16K (EMS) page count in pool\r\n; out: EAX = free 16kB pages in pool\r\n; other registers preserved\r\n; the value returned is not just the total of\r\n; the current pool descriptors.\r\n; it is\r\n;     total of free 16k pages in current pool\r\n;   + Min(free XMS pages, pmem.dwMaxMem4k - pmem.dwTotalMem)/4\r\n\r\nPool_GetFree16KPages PROC public\r\n    push esi\r\n    push ecx\r\n    push edx\r\n\r\n;--- get free 16kb pages in pool into EAX\r\n\r\n    xor eax, eax\r\n    mov ecx, eax\r\n    mov esi, [PoolAllocationTable]\r\nnextitem:\r\n    mov cl, [esi].POOL_DESC.b16kfree   ; high words known zero\r\n    add eax, ecx\r\n    add esi, sizeof POOL_DESC\r\n    cmp esi, [PoolAllocationEnd]\r\n    jb  nextitem\r\n\r\n;--- get MIN(free XMS pages, pmem.dwMaxMem4k - dwTotalMem)/4 into ESI\r\n\r\n    call Pool_GetFreeXMSPages   ;return free 4k XMS pages in EDX\r\n\r\n    mov esi, [pmem.dwMaxMem4K]\r\n    sub esi, [pmem.dwTotalMem4K]\r\n    jnc @@valueok\r\n    xor esi, esi\r\n@@valueok:\r\n    cmp esi, edx\r\n    jb @@issmaller\r\n    mov esi, edx\r\n@@issmaller:\r\n    shr esi, 2\r\n\r\n    add eax, esi\r\n\r\n    @dprintf ?POOLDBG, <\"Pool_GetFree16KPages=%X max4k=%X total4k=%X XMS-4k=%X\",10>, eax, [pmem.dwMaxMem4K], [pmem.dwTotalMem4K], edx\r\n    pop edx\r\n    pop ecx\r\n    pop esi\r\n    ret\r\n    align 4\r\n\r\nPool_GetFree16KPages ENDP\r\n\r\n\r\n; locate any adjacent free XMS block to current pool allocation desc;\r\n; if found, try consuming 32K of it for pool allocation block;\r\n; the adjacent XMS block must be at the end of current pool block,\r\n; since the pool block base cannot be changed once set.\r\n; pool block base+block size == owner XMS handle base+handle size (end match end)\r\n; ends must match since you can't span noncontiguous sub-blocks of an owner XMS\r\n; block with a single EMS/VCPI pool allocation block.\r\n; INP: ESI -> Pool block to expand (and is ensured to be expandable)\r\n; OUT: NC if success, C if fail\r\n; all registers preserved\r\n\r\nPool_ExpandBlock PROC\r\n\r\n    pushad\r\n\r\n    @dprintf ?POOLDBG, <\"Pool_ExpandBlock: esi=%X, addrK=%X, pArray=%X\",10>, esi, [esi].POOL_DESC.dwAddressK, [esi].POOL_DESC.pXMSdesc\r\n\r\n    mov edi,[esi].POOL_DESC.pXMSdesc\r\nif ?EXPANDFIRST\r\n    add edi,[dwRes]\r\nendif\r\n    \r\n    mov ebx,[edi].XMS_HANDLE.xh_baseK\r\n    add ebx,[edi].XMS_HANDLE.xh_sizeK   ; ebx -> end of current pool desc owner XMS\r\n\r\n; see if owner XMS for pool allocation desc end matches\r\n; end of pool allocation block\r\n\r\n    movzx ecx,[esi].POOL_DESC.bStartAdj\r\n    mov eax,[esi].POOL_DESC.dwAddressK\r\n    sub eax,ecx                 ; true XMS start when pool allocation desc created\r\n    mov cl,[esi].POOL_DESC.bEndAdj\r\n    add eax,ecx                 ; true XMS end when desc created\r\n    mov cl,[esi].POOL_DESC.b16kmax\r\n    shl ecx,4                   ; convert to K\r\n    add eax,ecx\r\n    cmp eax,ebx\r\n    jne @@locfail               ; owner XMS end no longer matches initial pool desc owner XMS end\r\n\r\n    movzx ecx, [XMS_Handle_Table.xht_numhandles]\r\n    mov edx, [XMS_Handle_Table.xht_pArray]\r\n\r\n; edx -> test XMS block\r\n\r\n    movzx eax, [XMS_Handle_Table.xht_sizeof]\r\n@@hanloop:\r\n    cmp ebx,[edx].XMS_HANDLE.xh_baseK   ; see if test block immediately follows current block   \r\n    je  @@found\r\n    add edx,eax     ; move to next handle descriptor\r\n    dec ecx\r\n    jne @@hanloop\r\n@@locfail:\r\n    popad\r\n    stc             ; flag failure\r\n    ret\r\n\r\n@@found:\r\n    test [edx].XMS_HANDLE.xh_flags,XMSF_FREE    ; if block is not free, abort scan\r\n    je  @@locfail\r\n    movzx eax,[esi].POOL_DESC.bEndAdj\r\n    add eax,[edx].XMS_HANDLE.xh_sizeK\r\n    cmp eax,32                  ; large enough?\r\n    jb @@locfail\r\n\r\n; transfer 32K of following block to current block - unused end K in current\r\n    mov eax,32\r\n    movzx ecx,[esi].POOL_DESC.bEndAdj\r\n    sub eax,ecx                       ; adjust amount to change preceding block\r\n    add [edx].XMS_HANDLE.xh_baseK,eax ; move changed block address ahead\r\n    sub [edx].XMS_HANDLE.xh_sizeK,eax ; and adjust size\r\n    mov edi,[esi].POOL_DESC.pXMSdesc\r\nif ?EXPANDFIRST\r\n    add edi,[dwRes]\r\nendif\r\n    add [edi].XMS_HANDLE.xh_sizeK,eax ; increase pool associated XMS block size\r\n    mov [esi].POOL_DESC.bEndAdj,0     ; no end overlap\r\n\r\n    add [esi].POOL_DESC.b16kmax,2     ; adjust allocation tracking bytes\r\n    add [esi].POOL_DESC.b16kfree,2\r\n    add [esi].POOL_DESC.w4kfree,2*4\r\n    add [pmem.dwTotalMem4K],2*4\r\n    \r\n;--- zero tracking allocation byte (not really needed)\r\n\r\nif 0\r\n    movzx eax,[esi].POOL_DESC.b16kmax\r\n    shr eax,1                   ; byte offset in allocation space (32K/byte)\r\n    mov BYTE PTR [esi].POOL_DESC.bits[eax-1],0\r\nendif\r\n\r\n; see if changed contiguous XMS block size went to <16K,\r\n;  if so, transfer any remainder to pool block and zero XMS block\r\n\r\n    mov eax,[edx].XMS_HANDLE.xh_sizeK\r\n    cmp eax,MINXMSSIZE\r\n    jae @@loc2\r\n    mov [esi].POOL_DESC.bEndAdj,al\r\n\r\n    xor eax,eax\r\n    mov [edx].XMS_HANDLE.xh_baseK,eax           ;required for FD Himem\r\n    mov [edx].XMS_HANDLE.xh_sizeK,eax\r\n;   mov [edx].XMS_HANDLE.xh_locks,al\r\n    mov [edx].XMS_HANDLE.xh_flags,XMSF_INPOOL   ; flag: free handle!\r\n@@loc2:\r\n    popad\r\n    clc                 ; flag success\r\n    ret\r\n    align 4\r\n\r\nPool_ExpandBlock ENDP\r\n\r\n; expand any available allocation pool desc by minimum amount, if possible\r\n; return NC on success, then ESI=desc which has been expanded\r\n; return C on failure\r\n; other registers preserved\r\n; called by VCPI, do not write segment registers here!\r\n\r\nPool_ExpandAnyBlock PROC\r\n\r\n    cmp [bNoPool],0 ; dynamic memory allocation on?\r\n    jne fail\r\n    mov esi, [PoolAllocationTable]\r\nnextitem:\r\n    cmp [esi].POOL_DESC.dwAddressK,0   ; unused/deallocated desc?\r\n    je  skipitem\r\n    cmp [esi].POOL_DESC.b16kmax, 2 * POOLDESC_ALLOCATION_SPACE - 1  ;32k still free?\r\n    jae skipitem              ; current block is full\r\n    test [esi].POOL_DESC.bFlags,PBF_DONTEXPAND\r\n    jne skipitem              ; can't expand this block\r\n    call Pool_ExpandBlock\r\n    jnc done\r\nskipitem:\r\n    add esi, sizeof POOL_DESC\r\n    cmp esi, [PoolAllocationEnd]\r\n    jb nextitem\r\nfail:\r\n    stc     ; failure\r\ndone:\r\n    ret\r\n    align 4\r\n\r\nPool_ExpandAnyBlock ENDP\r\n\r\n; find and allocate free 4K (VCPI) block in pool blocks\r\n; return NC and EAX == physical address\r\n;         C if none found\r\n; modifies ECX, ESI, EDI\r\n; do not write segment registers here! this function is\r\n; called by VCPI protected-mode API\r\n\r\nPool_Allocate4KPage PROC public\r\n\r\n; first try last block allocated, to avoid searching full blocks if possible\r\n\r\n    xor eax,eax\r\n    mov esi, [LastBlockAllocated]\r\n    or esi,esi\r\n    je @@nolastalloc\r\n    cmp [esi].POOL_DESC.w4kfree,ax\r\n    jne @@searchbytes\r\n\r\n; try last freed chunk\r\n\r\n@@nolastalloc:\r\n    mov esi, [LastBlockFreed]\r\n    or esi,esi\r\n    je @@nolastfreed\r\n    cmp [esi].POOL_DESC.w4kfree,ax\r\n    jne @@searchbytes\r\n@@nolastfreed:\r\n\r\n    mov esi, [PoolAllocationTable]\r\nnextitem:\r\n    cmp [esi].POOL_DESC.w4kfree,ax\r\n    jne @@searchbytes\r\n@@nextblock:\r\n    add esi, sizeof POOL_DESC\r\n    cmp esi, [PoolAllocationEnd]\r\n    jb  nextitem\r\n    call Pool_ExpandAnyBlock    ; try to expand a pool block \r\n    jnc @@searchbytes           ; if NC, ESI -> expanded block\r\n    call Pool_AllocateEMBForPool\r\n    jnc @@nolastfreed\r\n    ret\r\n\r\n@@searchbytes:\r\n    movzx ecx,[esi].POOL_DESC.b16kmax\r\n    shr ecx,1                   ; count of allocation bytes in block\r\n    adc ecx,0                   ; don't forget the last nibble\r\n\r\n    lea edi,[esi].POOL_DESC.bits\r\n    mov al,-1\r\n    repz scasb\r\n    jz @@blockbad           ; jump should never happen\r\n    dec edi\r\n    mov al,[edi]\r\n    mov cl,al\r\n    xor al,-1\r\n    bsf eax,eax             ; find the first '1' bit scanning from right to left\r\n    bts dword ptr [edi],eax ; flag page as 'used'\r\n    cmp eax,4\r\n    jc @@islownyb\r\n    shr cl,4\r\n@@islownyb:\r\n    and cl,0Fh\r\n    setz cl                 ; cl will be 1 if low/high nibble became <> 0\r\n    sub [esi].POOL_DESC.b16kfree, cl\r\nif ?POOLDBG    \r\n    jnc @@blockok\r\n    @dprintf ?POOLDBG, <\"Pool_Allocate4kPage: 16k-free count underflow for block %X\",10>, esi\r\n@@blockok:\r\nendif\r\n    dec [esi].POOL_DESC.w4kfree\r\n    mov [LastBlockAllocated],esi    ; update \"last block allocated from\"\r\n\r\n    inc [pmem.dwUsedMem4K]\r\n\r\n    sub edi,esi\r\n    sub edi,POOL_DESC.bits          ; get \"byte offset\" into EDI\r\n    shl edi,15      ; each byte covers 32k\r\n    shl eax,12      ; each bit covers 4k\r\n    add eax,edi     ; compute base address of block addressed by byte\r\n    mov ecx,[esi].POOL_DESC.dwAddressK\r\n    shl ecx,10      ; convert from K to bytes\r\n    add eax,ecx\r\n    @dprintf ?POOLDBG, <\"Pool_Allocate4kPage ok, page=%X, block=%X\",10>, eax, esi\r\n    ret\r\n\r\n@@blockbad:\r\n    @dprintf ?POOLDBG, <\"Pool_Allocate4kPage: found inconsistent block %X\",10>, esi\r\n    @CheckBlockIntegrity; nothing free, although block indicated there was\r\n    jmp @@nextblock     ; continue search\r\n    align 4\r\n\r\nPool_Allocate4KPage ENDP\r\n\r\n; allocate a 16K page (for EMS)\r\n; out: NC if ok,\r\n;     eax == index for descriptor entry\r\n;     edx == nibble offset in descriptor\r\n;      C on errors\r\n; destroy no other registers\r\n\r\nPool_Allocate16KPage PROC public\r\n    push ecx\r\n    push esi\r\n    push edi\r\n\r\n; first try last block allocated, to avoid searching full blocks if possible\r\n\r\n    xor edx,edx\r\n    mov esi, [LastBlockAllocated]\r\n    or esi,esi\r\n    je @@nolastalloc\r\n    cmp [esi].POOL_DESC.b16kfree,dl\r\n    jne @@searchbytes\r\n\r\n; try last freed chunk\r\n@@nolastalloc:\r\n    mov esi, [LastBlockFreed]\r\n    or esi,esi\r\n    je @@nolastfreed\r\n    cmp [esi].POOL_DESC.b16kfree,dl\r\n    jne @@searchbytes\r\n\r\n@@nolastfreed:\r\n    mov esi, [PoolAllocationTable]\r\nnextitem:\r\n    cmp [esi].POOL_DESC.b16kfree,dl\r\n    jne @@searchbytes\r\n@@nextblock:    \r\n    add esi, sizeof POOL_DESC\r\n    cmp esi, [PoolAllocationEnd]\r\n    jb nextitem\r\n    call Pool_ExpandAnyBlock    ; expand a block\r\n    jnc @@searchbytes           ; if NC, esi -> expanded block\r\n    call Pool_AllocateEMBForPool\r\n    jnc @@nolastfreed\r\n    jmp @@exit                  ; error: no 16k page free anymore\r\n\r\n@@searchbytes:\r\n    movzx ecx,[esi].POOL_DESC.b16kmax\r\n    shr ecx,1           ; count of allocation bytes in block\r\n    adc ecx,0\r\n\r\n    xor edi,edi\r\n\r\n@@findbyteloop:\r\n    mov al,[esi].POOL_DESC.bits[edi]\r\n    xor al,-1           ; unallocated 4K areas show as bit set\r\n    mov ah,al\r\n    mov dl,0fh\r\n    and al,dl\r\n    cmp al,dl\r\n    je  @@lowfree       ; low nybble unallocated, free 16K area\r\n    mov dl,0f0h\r\n    and ah,dl\r\n    cmp ah,dl\r\n    je  @@highfree      ; high nybble unallocated, free 16K area\r\n    inc edi\r\n    loop @@findbyteloop\r\n\r\n; no free 16K area? should not happen.\r\n\r\n    @CheckBlockIntegrity\r\n    jmp @@nextblock\r\n\r\n@@highfree:\r\n    stc\r\n@@lowfree:\r\n    setc cl\r\n    or [esi].POOL_DESC.bits[edi],dl\r\n\r\n    dec [esi].POOL_DESC.b16kfree\r\n    sub [esi].POOL_DESC.w4kfree,4\r\n    jnc @@valid2\r\n\r\n    @CheckBlockIntegrity    ; force valid value\r\n\r\n    mov [esi].POOL_DESC.w4kfree,0    \r\n\r\n; update ebx pointer\r\n\r\n@@valid2:\r\n\r\n    add [pmem.dwUsedMem4K],4        ; update pool page counter\r\n\r\n    mov [LastBlockAllocated],esi    ; update last block allocated from\r\n    mov eax,esi\r\n    sub eax, [PoolAllocationTable]\r\n    shr eax, 6                      ; convert to an index\r\n    mov edx,edi\r\n    shl edx,1\r\n    or dl,cl                        ; subindex in dl, also clears Carry\r\n@@exit:\r\n    pop edi\r\n    pop esi\r\n    pop ecx\r\n    ret\r\n    align 4\r\n\r\nPool_Allocate16KPage    ENDP\r\n\r\n;--- find pool descriptor for 4k page in EDI\r\n;--- in: EDI = 1K address\r\n;--- out: NC = ok, ESI=PD, EAX=block base\r\n;---      C  = failed\r\n;--- destroys eax,ecx,esi\r\n\r\nPool_FindBlock proc\r\n\r\n    mov esi, [LastBlockFreed]\r\n    or esi,esi\r\n    je @@notlastfreed\r\n    mov eax,[esi].POOL_DESC.dwAddressK\r\n    or eax,eax\r\n    je @@notlastfreed  ; unused/deallocated block\r\n\r\n    cmp edi,eax\r\n    jb @@notlastfreed  ; pool block starts after page\r\n    movzx ecx,[esi].POOL_DESC.b16kmax\r\n    shl ecx,4           ; convert 16K to 1K\r\n    add ecx,eax         ; ecx == end of block\r\n    cmp edi,ecx\r\n    jb @@rightblock    ; block found?\r\n\r\n@@notlastfreed:\r\n    mov esi, [LastBlockAllocated]\r\n    or esi,esi\r\n    je @@notlastalloc\r\n    mov eax,[esi].POOL_DESC.dwAddressK\r\n    or eax,eax\r\n    je @@notlastalloc  ; unused/deallocated block\r\n\r\n    cmp edi,eax\r\n    jb @@notlastalloc  ; pool block starts after page\r\n    movzx ecx,[esi].POOL_DESC.b16kmax\r\n    shl ecx,4           ; convert 16K to 1K\r\n    add ecx,eax         ; ecx == end of block\r\n    cmp edi,ecx\r\n    jb @@rightblock    ; block found?\r\n\r\n@@notlastalloc:\r\n\r\n    mov esi, [PoolAllocationTable]\r\nnextitem:\r\n    mov eax,[esi].POOL_DESC.dwAddressK\r\n    or eax,eax\r\n    je @@nextblock     ; unused/deallocated block\r\n\r\n    cmp edi,eax\r\n    jb @@nextblock     ; pool block starts after page\r\n    movzx ecx,[esi].POOL_DESC.b16kmax\r\n    shl ecx,4           ; convert 16K to 1K\r\n    add ecx,eax         ; ecx == end of block\r\n    cmp edi,ecx\r\n    jb @@rightblock    ; block found?\r\n@@nextblock:\r\n    add esi, sizeof POOL_DESC\r\n    cmp esi, [PoolAllocationEnd]\r\n    jb nextitem\r\n    @dprintf ?POOLDBG, <\"Pool_FindBlock: page %X not in pool\",10>, edx\r\n    stc\r\n    ret\r\n@@rightblock:\r\n    clc\r\n    ret\r\n    align 4\r\nPool_FindBlock endp\r\n\r\n;--- release 4K page.\r\n;--- in: EDX = 4K page physical address to free\r\n;--- out: NC if ok, C if failed\r\n;--- destroys eax,ecx,esi,edi\r\n\r\nPool_Free4KPage PROC public\r\n\r\n    mov edi,edx\r\n    shr edi,10          ; convert bytes to K\r\n    and edi,NOT 3       ; ensure 4K alignment\r\n\r\n; edi == start of 4K page in K after alignment adjustment\r\n\r\n    call Pool_FindBlock ; find descriptor \r\n    jc @@fail1          ; error \"page not in pool\"\r\n\r\n; the descriptor is in ESI\r\n\r\n    sub edi,eax         ; 4K offset from block base in K\r\n    mov eax,edi\r\n    shr eax,2           ; K to 4K page\r\n    mov cl,al           ; keep bit offset\r\n    shr eax,3           ; 4K page to 32K byte offset\r\n    and ecx,7\r\n\r\n    btr dword ptr [esi].POOL_DESC.bits[eax],ecx  ; see if bit set (was allocated)\r\n    jnc @@fail2         ; no\r\n\r\n    inc [esi].POOL_DESC.w4kfree\r\n    mov [LastBlockFreed],esi\r\n    \r\n    dec [pmem.dwUsedMem4K]\r\n\r\n; check if this frees up a 16K chunk\r\n\r\n    mov al,[esi].POOL_DESC.bits[eax]\r\n    cmp cl,4\r\n    jc @@islow\r\n    shr al,4\r\n@@islow:\r\n    test al,0Fh         ; see if all bits of nybble cleared\r\n    jne @@success       ; no\r\n    inc [esi].POOL_DESC.b16kfree\r\n    call Pool_TryFreeToXMS  ; free empty pool allocation block to XMS if appropriate\r\n@@success:\r\n\r\n    @dprintf ?POOLDBG, <\"Pool_Free4kPage ok, page=%X block=%X\",10>, edx, esi\r\n    clc\r\n@@fail1:\r\n    ret\r\n@@fail2:\r\n    @dprintf ?POOLDBG, <\"Pool_Free4kPage: page %X was not allocated (block=%X)\",10>, edx, esi\r\n    stc\r\n    ret\r\n    align 4\r\n\r\nPool_Free4KPage ENDP\r\n\r\n\r\n; in: EAX = descriptor index, ECX = nibble offset\r\n; destroys eax, ecx\r\n\r\nPool_Free16KPage PROC public\r\n\r\n    push esi\r\n\r\n    mov esi,eax\r\n    .errnz sizeof POOL_DESC - 64\r\n    shl esi,6                       ; convert 64-byte count to byte offset\r\n    add esi, [PoolAllocationTable]  ; esi -> pool allocation block\r\n\r\n    mov ah,0Fh\r\n    shr ecx,1           ; byte offset\r\n    jnc @@islow\r\n    mov ah,0F0h\r\n@@islow:\r\n    mov al,[esi].POOL_DESC.bits[ecx]\r\n    and al,ah           ; mask out bits which dont interest\r\n    cmp al,ah\r\n    jne @@fail\r\n    rol al,4\r\n    and [esi].POOL_DESC.bits[ecx],al ; reset all expected bits\r\n\r\n    inc [esi].POOL_DESC.b16kfree\r\n    add [esi].POOL_DESC.w4kfree,4\r\n\r\n    sub [pmem.dwUsedMem4K],4; update pool page counter \r\n\r\n    mov [LastBlockFreed],esi\r\n    call Pool_TryFreeToXMS  ; free empty pool allocation block to XMS if appropriate\r\n    clc\r\n@@ret:\r\n    pop esi\r\n    ret\r\n\r\n@@fail:\r\n    @dprintf ?POOLDBG, <\"Pool_Free16kPage failed page=%X, masks=%X\",10>, edx, eax\r\n    @CheckBlockIntegrity\r\n    stc\r\n    jmp @@ret\r\n    align 4\r\n\r\nPool_Free16KPage ENDP\r\n\r\n\r\n; find an unused Pool descriptor\r\n; out: NC ok, EDX -> Pool block\r\n;       C failed - no unused descriptor found\r\n; no other registers modified\r\n\r\nPool_GetUnusedBlock PROC\r\n\r\n    mov edx, [PoolAllocationTable]\r\nnextitem:\r\n    cmp [edx].POOL_DESC.dwAddressK,0   ; unused/deallocated block?\r\n    je @@found\r\n    add edx, sizeof POOL_DESC\r\n    cmp edx, [PoolAllocationMax]\r\n    jb nextitem\r\n    @dprintf ?POOLDBG, <\"Pool_GetUnusedBlock failed, table start/end=%X/%X\",10>, [PoolAllocationTable], [PoolAllocationMax]\r\n    stc\r\n@@found:\r\n    ret\r\n    align 4\r\n\r\nPool_GetUnusedBlock ENDP\r\n\r\n; prepare pool block for use\r\n; upon entry:\r\n;  edx -> pool descriptor\r\n;  ecx == raw size in K before alignment (max 1536+3)\r\n;  edi == raw address in K before alignment\r\n;  esi == owner XMS handle pXMSdesc value, do NOT use XMS handle values for\r\n;   size and address since this call may be part of a multi-pool block span\r\n;  returns memory in 1kB assigned to this descriptor in EAX\r\n\r\nPool_PrepareBlock PROC\r\n\r\n    pushad\r\n\r\n    @dprintf ?POOLDBG, <\"Pool_PrepareBlock: size in kB=%X addr in kB=%X\",10>, ecx, edi\r\n\r\n    mov [edx].POOL_DESC.pXMSdesc,esi\r\n\r\n    mov ebx,edi         ; raw address - must be aligned to page boundary\r\n    add ebx,3\r\n    and ebx,not 3\r\n    mov [edx].POOL_DESC.dwAddressK, ebx\r\n    sub ebx, edi        ; 00->00, 01->03, 02->02, 03->01\r\n    mov [edx].POOL_DESC.bStartAdj,bl\r\n    mov [edx].POOL_DESC.bEndAdj,0\r\n\r\n; zero allocation entries\r\n    xor eax,eax\r\n    lea edi,[edx].POOL_DESC.bits\r\n    push ecx\r\n    mov ecx,POOLDESC_ALLOCATION_SPACE / 4\r\n    rep stosd\r\n    pop ecx\r\n    cmp edi,[PoolAllocationEnd]\r\n    jb @@notlast\r\n    mov [PoolAllocationEnd],edi\r\n@@notlast:\r\n\r\n; block size = (raw size - start adjustment) rounded down to 16K boundary\r\n\r\n    mov eax,ecx         ; raw size\r\n    sub eax,ebx\r\n    shr eax,4           ; 16K count, known 16-bit value going to 8-bit\r\n\r\n    cmp ax,POOLDESC_ALLOCATION_SPACE * 2\r\n    jbe @@setmax\r\n    mov ax,POOLDESC_ALLOCATION_SPACE * 2\r\n\r\n@@setmax:\r\n    mov [edx].POOL_DESC.b16kmax,al\r\n    mov [edx].POOL_DESC.b16kfree,al\r\n    test al,1\r\n    jz @@is32kaligned   ;is 16kb page count \"uneven\"?\r\n    movzx ebx, al\r\n    shr ebx, 1\r\n    mov byte ptr [edx].POOL_DESC.bits[ebx],0F0h  ;mark last page \"used\"\r\n@@is32kaligned:    \r\n    shl eax,2           ; 8-bit value potentially going to 16-bit\r\n    mov [edx].POOL_DESC.w4kfree,ax\r\n\r\n    add [pmem.dwTotalMem4K],eax\r\n\r\n; return true memory assigned - (block size + start adjustment)\r\n\r\n    shl eax,2           ; convert mem size to 1K\r\n    movzx ebx,[edx].POOL_DESC.bStartAdj\r\n    add eax,ebx\r\n    mov [esp].PUSHADS.rEAX, eax\r\n    popad\r\n    ret\r\n    align 4\r\n\r\nPool_PrepareBlock ENDP\r\n\r\nif ?FREEXMS\r\n\r\n;--- called if Jemm is unloaded\r\n\r\nPool_FreeAllBlocks proc public\r\n\r\n    pushad\r\n    mov esi, [PoolAllocationTable]  ; esi -> pool allocation block\r\nnextitem:\r\n    cmp [esi].POOL_DESC.dwAddressK,0\r\n    jz @@skipitem\r\n    movzx eax, [esi].POOL_DESC.b16kmax\r\n    mov [esi].POOL_DESC.b16kfree,al\r\n    shl eax, 2\r\n    mov [esi].POOL_DESC.w4kfree,ax\r\n    call Pool_TryFreeToXMS\r\n@@skipitem:    \r\n    add esi, sizeof POOL_DESC\r\n    cmp esi, [PoolAllocationEnd]\r\n    jb nextitem\r\n    popad\r\n    ret\r\n    align 4\r\n    \r\nPool_FreeAllBlocks endp\r\n\r\nendif\r\n\r\n; upon entry esi -> pool allocation block to check if freeable to XMS\r\n; perform the free if possible\r\n; destroys eax,ecx\r\n; if a pool descriptor is freed, it gets fields dwAddressK,\r\n; w4kfree, b16kfree and b16kmax set to 0.\r\n\r\nPool_TryFreeToXMS PROC\r\n\r\n    test [esi].POOL_DESC.bFlags,PBF_DONTFREE\r\n    jne @@exit          ; never free these blocks\r\n\r\n    mov al,[esi].POOL_DESC.b16kfree\r\n    cmp al,[esi].POOL_DESC.b16kmax\r\n    ja @@bad            ; free more than max, try to fix\r\n    jne @@exit          ; free is less than maximum, used\r\n\r\n    movzx eax,[esi].POOL_DESC.w4kfree\r\n    shr eax,2\r\n    or ah,ah\r\n    jne @@bad\r\n    cmp al,[esi].POOL_DESC.b16kmax\r\n    ja @@bad\r\n    jne @@exit          ; free less than max\r\n\r\n; ok, this pool block is not used anymore.\r\n; now scan all pool blocks and check those linked to the same XMS handle\r\n; if they are free too. If yes, then mark XMS handle as free and clear all\r\n; blocks.\r\n\r\n    push esi\r\n    mov esi,[esi].POOL_DESC.pXMSdesc\r\n\r\n    mov eax,[PoolAllocationTable]\r\nnextitem:\r\n    cmp [eax].POOL_DESC.dwAddressK,0\r\n    je @@checknext         ; unused block\r\n\r\n    cmp esi,[eax].POOL_DESC.pXMSdesc\r\n    jne @@checknext\r\n\r\n    test [eax].POOL_DESC.bFlags,PBF_DONTFREE\r\n    jne @@checkdone     ; can't free this block\r\n\r\n; see if block empty\r\n    movzx ecx,[eax].POOL_DESC.b16kmax\r\n    cmp cl,[eax].POOL_DESC.b16kfree\r\n    jne @@checkdone\r\n\r\n    shl ecx,2               ; convert to 4K max\r\n    cmp cx,[eax].POOL_DESC.w4kfree\r\n    jne @@checkdone\r\n\r\n@@checknext:\r\n    add eax, sizeof POOL_DESC\r\n    cmp eax, [PoolAllocationEnd]\r\n    jb nextitem\r\n\r\n; checked all blocks as empty, go through them again and mark unused.\r\n; also update the PoolAllocationEnd variable if needed.\r\n\r\n    push edx\r\n    push edi\r\n    mov edi, [PoolAllocationTable]\r\n    mov ecx, edi\r\n\r\n    xor eax, eax\r\n\r\nnextitem2:\r\n    cmp [edi].POOL_DESC.dwAddressK,0\r\n    je @@freenext          ; unused block\r\n    cmp esi,[edi].POOL_DESC.pXMSdesc\r\n    je @@freenext2\r\n    mov ecx, edi            ; remember last block not free\r\n    jmp @@freenext\r\n@@freenext2:\r\n\r\n; mark the block as free\r\n\r\n    movzx edx, [edi].POOL_DESC.b16kmax \r\n    shl edx, 2\r\n    mov [edi].POOL_DESC.dwAddressK, eax\r\n    sub [pmem.dwTotalMem4K], edx\r\n    mov dword ptr [edi].POOL_DESC.w4kfree, eax    ;this also clears the 16kb counters\r\n\r\n@@freenext:\r\n    add edi, sizeof POOL_DESC\r\n    cmp edi, [PoolAllocationEnd]\r\n    jb nextitem2\r\n\r\n    add ecx, sizeof POOL_DESC\r\n    mov [PoolAllocationEnd], ecx\r\n\r\n    pop edi\r\n    pop edx\r\n\r\nif ?EXPANDFIRST\r\n    add esi, [dwRes]\r\nendif\r\n    call Pool_FreeEMB\r\n\r\n@@checkdone:\r\n    pop esi\r\n@@exit:\r\n    ret\r\n\r\n@@bad:\r\n    @CheckBlockIntegrity\r\n    stc\r\n    ret\r\n    align 4\r\n\r\nPool_TryFreeToXMS ENDP\r\n\r\n; populate empty pool blocks with XMS owner info\r\n; inp:\r\n;  esi -> XMS (pseudo-)handle\r\n;  ecx == size in kB XMS block \r\n;  edi == physical address (in K, shifted 10 to right!)\r\n;  al == flags\r\n; NOTE: ecx and edi may not match owner XMS size/address\r\n; out: NC success, edi=physical address after alloc\r\n;       C if insufficient number of empty blocks to cover XMS handle range\r\n; destroys eax,ebx,edx,edi\r\n\r\nPool_AllocBlocksForEMB  PROC public\r\n\r\n    mov ebx,ecx\r\nif ?EXPANDFIRST\r\n    sub esi,[dwRes]\r\nendif\r\n@@allocloop:\r\n    call Pool_GetUnusedBlock\r\n    jc @@exit      ; no more blocks, remainder of XMS is effectively discarded\r\n\r\n    mov [edx].POOL_DESC.bFlags,al\r\n\r\n    push eax\r\n    mov eax,edi     ; compute size of candidate block/offset to new\r\n    add eax,3\r\n    and al,not 3\r\n    sub eax,edi\r\n    add eax,POOLDESC_MEMSIZE   ; 1.5M (in K) plus alignment adjustment size\r\n    cmp eax,ebx\r\n    jbe @@sizeok\r\n    mov eax,ebx\r\n@@sizeok:\r\n    mov ecx,eax\r\n    call Pool_PrepareBlock  ; uses esi entry condition\r\n    add edi,eax         ; update pool allocation block address\r\n    sub ebx,eax         ; update size left to allocate\r\n    pop eax\r\n    cmp ebx,MINXMSSIZE  ; see if should remainder what's left\r\n    jnb @@allocloop\r\n    test al,PBF_DONTEXPAND\r\n    jnz @@exit\r\n    mov [edx].POOL_DESC.bEndAdj,bl\r\n@@exit:\r\n    ret\r\n    align 4\r\n\r\nPool_AllocBlocksForEMB  ENDP\r\n\r\n;  walk XMS blocks, find largest XMS block x which has \r\n; max( min(dwMemMax4K-dwMemTotal4K, 1.5M), 32K) >= x >= 32K\r\n; after 4K alignment and allocate it for new pool allocation block.\r\n;  if all XMS blocks >1.5M, then pick smallest and try to put remainder\r\n; into a free handle. If no free handle, allocate sufficient new pool\r\n; descriptors to cover full range. If not enough free pool decriptors\r\n; available, the remaining mem is lost until handle is freed. This could \r\n; only happen under very bad XMS fragmentation, if at all.\r\n\r\n; return carry clear if success, set if fail\r\n; all registers preserved\r\n; called by VCPI protected-mode API, do not write segment registers here!\r\n\r\nPool_AllocateEMBForPool PROC\r\n\r\n    pushad\r\n\r\n    cmp [bNoPool],0 ; check if pool sharing\r\n    jne @@allocfail\r\n\r\n    mov ebp, POOLDESC_MEMSIZE  ;1536 kB\r\n    mov eax, [pmem.dwMaxMem4K]\r\n    sub eax, [pmem.dwTotalMem4K] \r\n    jbe @@allocfail     ;if max memory already allocated\r\n    shl eax, 2          ;convert to 1K units\r\n    cmp ebp, eax\r\n    jc @@usesmaller\r\n    mov ebp, eax\r\n    cmp ebp, MINXMSSIZE\r\n    jnc @@usesmaller\r\n    mov ebp, MINXMSSIZE\r\n@@usesmaller:           ;ebp = max( min(dwMemMax4K-dwMemTotal4K, 1.5M), 32K)\r\n\r\n    call Pool_GetUnusedBlock; free pool descriptor available?\r\n    jc @@allocfail         ; no need to process further\r\n\r\n;--- scan XMS handle table\r\n\r\n    mov esi, [XMS_Handle_Table.xht_pArray]\r\n    movzx ecx, [XMS_Handle_Table.xht_numhandles]\r\n    xor edx,edx             ; edx -> largest block <= 1.5M or smallest if none <=1.5M\r\nnextitem:\r\n    test [esi].XMS_HANDLE.xh_flags, XMSF_FREE   ; free XMS memory block?\r\n    je skipitem                                 ; no, don't check\r\n    mov ebx,[esi].XMS_HANDLE.xh_baseK\r\n    mov eax,[esi].XMS_HANDLE.xh_sizeK\r\nif ?XMS35COMPAT\r\n\ttest ebx,0ffc00000h    ; block beyond 4GB?\r\n\tjnz skipitem\r\nendif\r\nife ?INTEGRATED    \r\n    and ebx,ebx\r\n    je skipitem            ; FD Himem bug, ignore blank or zero-sized handle\r\n    or eax,eax\r\n    je skipitem\r\nendif\r\n    and bl,3\r\n    mov bh,4\r\n    sub bh,bl       ;bh=4,3,2,1\r\n    and bh,3        ;bh=3,2,1,0 bl=1,2,3,0\r\n    movzx ebx,bh\r\n    sub eax,ebx     ;eax = page adjusted size in 1kb units\r\n    cmp eax,MINXMSSIZE  ;ignore everything below our limit\r\n    jb skipitem\r\n\r\n    or edx,edx\r\n    je @@newcandidate      ; auto-match if first xms block available\r\n\r\n; eax = test value size, edi = current candidate size, both 4k adjusted\r\n\r\n    cmp eax,edi\r\n    je skipitem\r\n    ja @@larger\r\n\r\n; test XMS block smaller than candidate block\r\n    cmp edi,ebp\r\n    jbe skipitem           ; current candidate closer to match size\r\n    jmp @@newcandidate\r\n\r\n; test XMS block larger than candidate block\r\n@@larger:\r\n    cmp edi,ebp\r\n    jae skipitem           ; current candidate closer to match size\r\n    cmp eax,ebp\r\n    ja skipitem            ; test too large\r\n@@newcandidate:\r\n    mov edx, esi        ; new best candidate\r\n    mov edi, eax        ; edi = candidate size in 1 kB units\r\nskipitem:\r\n    movzx eax, [XMS_Handle_Table.xht_sizeof]\r\n    add esi,eax         ; move to next handle descriptor\r\n    dec ecx\r\n    jne nextitem\r\n    or  edx,edx         ; candidate found?\r\n    jne @@allocok\r\n@@allocfail:\r\n    popad\r\n    stc\r\n    ret\r\n\r\n; candidate is ensured to have MINXMSSIZE after 4K alignment adjustment\r\n\r\n@@allocok:\r\n\r\n    @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\r\n\r\n    mov [edx].XMS_HANDLE.xh_flags,XMSF_USED ; flag candidate as used\r\n    mov [edx].XMS_HANDLE.xh_locks,1         ; and locked\r\n\r\n    mov esi,ebp                     ; default allocation maximum size\r\n    cmp ebp,POOLDESC_MEMSIZE\r\n    jnz @@adjusted\r\n\r\n    mov eax, [XMSPoolBlockCount]\r\n    cmp eax,1\r\n    jbe @@trailadj          ; use standard 1.5M size for first two blocks\r\n    dec eax                 ; should never overflow before we hit 4G total allocated\r\n    and al,0fh              ; but ensure that overflow doesn't happen anyway\r\n    mov cl,al               ; shift the block size higher by factor of two\r\n    shl esi,cl\r\n\r\n; if esi (=XMSBlockSize) >= max free, then reduce XMSBlockSize\r\n\r\n    mov eax,[pmem.dwMaxMem4K]\r\n    sub eax,[pmem.dwTotalMem4K]\r\n    shl eax,2               ; convert to 1K blocks\r\n\r\n@@checksize:\r\n    cmp eax, esi\r\n    jae @@adjusted\r\n    cmp esi,ebp             ; see if esi(=XMSBlockSize) is at minimum default\r\n    jbe @@adjusted          ; yes, can't reduce it any further\r\n    shr esi,1               ; reduce block size by one shift and try again\r\n    jmp @@checksize\r\n@@adjusted:\r\n\r\n; allow up to MINXMSSIZE-1 trailing bytes\r\n\r\n@@trailadj:\r\n    mov eax, esi\r\n    add eax, MINXMSSIZE-1   ; adjust for possible trail\r\n    cmp edi, eax\r\n    jbe @@setblock          ; no need to split XMS handle allocation\r\n\r\n; search for a free XMS handle\r\n\r\n    mov edi, [XMS_Handle_Table.xht_pArray]\r\n    movzx ecx, [XMS_Handle_Table.xht_numhandles]\r\n    movzx eax, [XMS_Handle_Table.xht_sizeof]\r\nnextitem2:\r\n    test [edi].XMS_HANDLE.xh_flags,XMSF_INPOOL\r\n    jnz @@gotfree\r\nife ?INTEGRATED\r\n    cmp [edi].XMS_HANDLE.xh_flags,XMSF_USED ; some Himems dont set XMSF_INPOOL, so\r\n    je skipitem2                            ; check FREE items if address/size is NULL\r\n    cmp [edi].XMS_HANDLE.xh_baseK,0\r\n    je @@gotfree\r\n    cmp [edi].XMS_HANDLE.xh_sizeK,0\r\n    je @@gotfree\r\nendif\r\nskipitem2:\r\n    add edi,eax         ; move to next handle descriptor\r\n    loop nextitem2\r\n\r\n; no free handle found, try to allocate multiple blocks, discarding excess\r\n\r\n    jmp @@setblock\r\n\r\n@@gotfree:\r\n    mov cl,BYTE PTR [edx].XMS_HANDLE.xh_baseK   ; compute size of candidate block/offset to new\r\n    and cl,3\r\n    mov ch,4\r\n    sub ch,cl\r\n    and ch,3\r\n    movzx ecx,ch\r\n\r\n    add ecx, esi                ; maximum size (exceeded) plus alignment adjustment size\r\n\r\n; edx -> candidate block being allocated\r\n; edi -> new block receiving remainder\r\n; update candidate XMS block size\r\n\r\n    mov eax,[edx].XMS_HANDLE.xh_sizeK       ; keep original size for updating new block\r\n    mov [edx].XMS_HANDLE.xh_sizeK,ecx\r\n\r\n; update new XMS block info\r\n    sub eax,ecx                 ; new block size == old block original size - old block new size\r\n    mov [edi].XMS_HANDLE.xh_sizeK,eax\r\n    mov [edi].XMS_HANDLE.xh_flags,XMSF_FREE ; explicitly flag free\r\n    mov [edi].XMS_HANDLE.xh_locks,0\r\n    mov eax,[edx].XMS_HANDLE.xh_baseK\r\n    add eax,ecx\r\n    mov [edi].XMS_HANDLE.xh_baseK,eax   ; new block start == old block start + old block new size\r\n\r\n    @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\r\n\r\n; edx -> owner XMS handle for new pool allocation block(s)\r\n; may be multiple blocks due to XMSBlockCount shifter\r\n\r\n@@setblock:\r\n    mov esi,edx\r\n    mov ecx,[esi].XMS_HANDLE.xh_sizeK\r\n    mov edi,[esi].XMS_HANDLE.xh_baseK\r\n    xor al,al\r\n    call Pool_AllocBlocksForEMB\r\n\r\n    inc [XMSPoolBlockCount]\r\n    popad\r\n    clc\r\n    ret\r\n    align 4\r\n\r\nPool_AllocateEMBForPool ENDP\r\n\r\n;  count available XMS 4K-aligned 4K pages in 16K chunks\r\n;  return count in edx\r\n;  destroys no other registers\r\n;  do not write segment registers here! function\r\n;  is called by VCPI protected mode API.\r\n\r\nPool_GetFreeXMSPages PROC\r\n    push esi\r\n    push ecx\r\n    xor edx,edx\r\n    cmp [bNoPool], 0    ; XMS memory pool?\r\n    jne @@countdone\r\n    movzx ecx, [XMS_Handle_Table.xht_numhandles]\r\n    mov esi, [XMS_Handle_Table.xht_pArray]\r\n\r\n    push eax\r\n    push ebx\r\n\r\nnextitem:\r\n    test [esi].XMS_HANDLE.xh_flags,XMSF_FREE\r\n    jz skipitem\r\nife ?INTEGRATED\r\n    xor eax,eax\r\n    cmp eax,[esi].XMS_HANDLE.xh_baseK   ; account for FD Himem bug\r\n    je skipitem\r\n    cmp eax,[esi].XMS_HANDLE.xh_sizeK\r\n    je skipitem\r\nendif\r\n    mov eax,[esi].XMS_HANDLE.xh_baseK\r\nif ?XMS35COMPAT\r\n\ttest eax,0FFC00000h    ; block beyond 4GB?\r\n\tjnz skipitem\r\nendif\r\n    mov ebx,eax\r\n    add ebx,3       ; round up\r\n    add eax,[esi].XMS_HANDLE.xh_sizeK\r\n    and al,0fch     ; align to 4K boundary\r\n    and bl,0fch\r\n    sub eax,ebx     ; compute size of block after alignments\r\n    jbe skipitem\r\n    cmp eax,MINXMSSIZE  ; ignore free blocks < 16 kB\r\n    jb skipitem\r\n    and al,NOT 0fh  ; mask to 16K\r\n    shr eax,2       ; convert 1K to 4K\r\n    add edx,eax     ; update total count\r\n\r\nskipitem:\r\n    movzx eax, [XMS_Handle_Table.xht_sizeof]\r\n    add esi,eax ; move to next handle descriptor\r\n    dec ecx\r\n    jne nextitem\r\n\r\n    pop ebx\r\n    pop eax\r\n@@countdone:\r\n    pop ecx\r\n    pop esi\r\n    ret\r\n    align 4\r\n\r\nPool_GetFreeXMSPages ENDP\r\n\r\n\r\n; mark an XMS handle as free.\r\n; scan through the XMS handle array\r\n; and try to merge this block with other free blocks\r\n; ESI = handle which just has become free\r\n; preserves all registers\r\n\r\nPool_FreeEMB PROC\r\n\r\n    pushad\r\n\r\n    mov [esi].XMS_HANDLE.xh_locks,0\r\n    mov [esi].XMS_HANDLE.xh_flags,XMSF_FREE\r\n\r\n    dec [XMSPoolBlockCount]\r\n\r\n    mov edi, [XMS_Handle_Table.xht_pArray]\r\n    movzx ecx, [XMS_Handle_Table.xht_numhandles]\r\n\r\n    mov edx, [esi].XMS_HANDLE.xh_baseK\r\n    add edx, [esi].XMS_HANDLE.xh_sizeK\r\n\r\n    movzx eax, [XMS_Handle_Table.xht_sizeof]\r\n@@checkloop:\r\n    cmp [edi].XMS_HANDLE.xh_flags,XMSF_FREE ; see if free\r\n    jne @@checknext                 ; anything else is to ignore\r\nife ?INTEGRATED\r\n    cmp [edi].XMS_HANDLE.xh_baseK,0         ; FD Himem: free + base 0 is \"INPOOL\"\r\n    je @@checknext                 ; can't check blank handle\r\nendif\r\n    cmp edi, esi\r\n    je @@checknext\r\n    mov ebx,[edi].XMS_HANDLE.xh_baseK           ; which starts at EBX (end of test block)\r\n    cmp edx,ebx\r\n    jz @@merge1\r\n    add ebx,[edi].XMS_HANDLE.xh_sizeK\r\n    cmp ebx,[esi].XMS_HANDLE.xh_baseK\r\n    jz @@merge2\r\n@@checknext:\r\n    add edi,eax         ; move to next handle descriptor\r\n    loop @@checkloop\r\n@@nodefrag:\r\n    popad\r\n    ret\r\n\r\n@@merge2:\r\n    push edi\r\n    xchg esi, edi\r\n    call merge\r\n    pop edi\r\n    jmp @@checknext     ;there might come just another free block to merge\r\n@@merge1:\r\n    push offset @@checknext\r\nmerge:\r\n    mov ebx,[edi].XMS_HANDLE.xh_sizeK\r\n    add [esi].XMS_HANDLE.xh_sizeK, ebx\r\n    mov [edi].XMS_HANDLE.xh_flags,XMSF_INPOOL   ; flag handle as free\r\n    xor ebx,ebx\r\n    mov [edi].XMS_HANDLE.xh_locks,bl\r\n    mov [edi].XMS_HANDLE.xh_baseK,ebx\r\n    mov [edi].XMS_HANDLE.xh_sizeK,ebx\r\n    retn\r\n    align 4\r\n\r\nPool_FreeEMB ENDP\r\n\r\n; hook left for debugging, no current actions taken\r\n;; upon entry esi -> pool allocation block to perform integrity check upon\r\n;;  update with valid information if allocation counts mismatch\r\n;; destroy no registers\r\n\r\nif ?POOLDBG\r\nCheckBlockIntegrity PROC\r\n    ret\r\nCheckBlockIntegrity ENDP\r\nendif\r\n\r\n.text$03 ends\r\n\r\n.text$04 segment\r\n\r\n; initialize memory pool block descriptors\r\n; each descriptor describes a memory block <= 1.5M (48*8 * 4K)\r\n; and is 16 + 48 = 64 bytes in size.\r\n; required are: ((pmem.dwMaxMem4K / 1.5M) + x) * 64 bytes bytes for these items\r\n; x is max number of XMS handles\r\n; in: ESI -> JEMMINIT\r\n; in: EDI -> free memory\r\n; out: EDI -> free memory\r\n\r\nPool_Init1 proc public\r\n\r\n    add edi,15          ; align on paragraph\r\n    and edi,not 15\r\n\r\n    mov [PoolAllocationTable],edi\r\n    mov [PoolAllocationEnd],edi\r\n\r\n    mov eax,[pmem.dwMaxMem4K]    ;= 30720 x 4k = 120 MB\r\n    cdq\r\n    mov ecx,POOLDESC_MEMSIZE / 4 ; since pmem.dwMaxMem4k is in 4k units\r\n    div ecx             ; default: 30720/384 = 80\r\n    add eax,2           ; round up, not down (and 1 extra for NoPool)\r\n    cmp [bNoPool],0\r\n    jnz @@isnopool\r\n    movzx ecx,[XMS_Handle_Table.xht_numhandles]\r\n    dec ecx\r\n    add eax, ecx\r\n@@isnopool:    \r\n    .errnz sizeof POOL_DESC - 64\r\n    shl eax,6-2         ; size of 1 descriptor: 64 bytes -> 16 dwords\r\n    mov ecx,eax\r\n    xor eax,eax\r\n    rep stosd\r\n\r\n    mov [PoolAllocationMax],edi\r\n\r\n    @dprintf ?INITDBG, <\"pool start/end=%X/%X\",10>,PoolAllocationTable, PoolAllocationMax\r\n    ret\r\n\r\nPool_Init1 endp\r\n\r\n; Pool Init phase 2\r\n;\r\n; ESI -> JEMMINIT\r\n; EDI -> free memory fix block (physical, page aligned)\r\n; EAX -> size (in 4kb pages) still free in fix block \r\n\r\n; out:\r\n; EDI -> free memory (physical)\r\n; destroys EAX, EBX, ECX, EDX\r\n\r\n; first pool allocation block(s) are used to manage remainder of initial XMS\r\n; allocated memory (fixed EMS/VCPI allocations).\r\n; never expand these blocks, the descriptor holds a real XMS handle rather than\r\n; an XMS pseudo-handle/pointer taken from the XMS handle array.\r\n\r\nPool_Init2 proc public\r\n\r\n    push esi\r\n\r\n    cmp [bNoPool], 0\r\n    je @@setblocks\r\n\r\n    @dprintf ?INITDBG, <\"pool sharing is off, 4k pages remaining=%X pmem.dwMaxMem4k=%X\",10>, eax, pmem.dwMaxMem4K\r\n\r\n;-- pool sharing is off, rest of block is used for EMS/VCPI\r\n;-- make sure current values of MaxMem4K and EMSPagesMax\r\n;-- are not too high\r\n\r\n    mov ecx,[pmem.dwMaxMem4K] \r\n    sub ecx,[pmem.dwTotalMem4K]\r\n    cmp eax, ecx\r\n    jnc @@isnodecrease      ;jump if more pages are available\r\n    sub ecx, eax\r\n    sub [pmem.dwMaxMem4K],ecx\r\n    mov ecx, eax            ;pmem.dwMaxMem4K has to be adjusted\r\n@@isnodecrease:\r\n    push ecx\r\n    call EMS_CheckMax\r\n    pop eax\r\n\r\n@@setblocks:\r\n\r\n    mov ecx, eax            ; count of available 4K pages\r\n\r\n\r\nif ?EXPANDFIRST\r\n    mov al,PBF_DONTFREE\r\nelse\r\n    mov al,PBF_DONTEXPAND or PBF_DONTFREE\r\nendif\r\n    movzx esi, [esi].JEMMINIT.XMSControlHandle\r\n\r\n;--- al=flags, edi=phys addr, esi=XMS handle, ecx=size in kB\r\n\r\n    @dprintf ?INITDBG, <\"Pool_init2: addr=%X size in 4kb pg=%X\",10>, edi, ecx\r\n    shl ecx, 2  ;convert to 1kb units\r\n    shr edi, 10 ;must be a 1k-address\r\n    call Pool_AllocBlocksForEMB\r\n    shl edi, 10 ;convert back to phys. address\r\n\r\n    @dprintf ?INITDBG, <\"Pool_init2: exit, edi=%X\",10>, edi\r\n\r\n    pop esi\r\n    ret\r\n\r\nPool_Init2 ENDP\r\n\r\n.text$04 ENDS\r\n\r\n    END\r\n"
  },
  {
    "path": "src/UMB.ASM",
    "content": "\r\n;--- XMS UMB part implementation\r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n;--- for 100% MS compatibility, the UMB handler would have to\r\n;--- implement a linked list of special MCBs:\r\n\r\nUMBMCB struct\r\nsig   db ?   ;signature 'M'\r\nsegm  dw ?   ;segment address of content or 0000 if UMB is free\r\nsize_ dw ?   ;size in paragraps\r\n      db 3 dup (?)\r\nname_ db 8 dup (?) ;name (='UMB',0,0,0,0,0)\r\nUMBMCB ends\r\n\r\n.text$01 SEGMENT\r\n\r\nUMBsegments UMBBLK UMB_MAX_BLOCKS dup (<0,0>)\r\nUMBend  label byte\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n;--- XMS UMB handler\r\n;--- EAX, EBX, EDX hold client values\r\n\r\numb_handler proc public\r\n\r\n    cmp ah,11h          ;free UMB, DX=segment address to release\r\n    je UMB_free\r\n    cmp ah,12h          ;realloc UMB, DX=segment to resize, BX=new size\r\n    je UMB_realloc\r\n\r\numb_handler endp        ;fall thru!\r\n\r\n;--- UMBalloc\r\n;--- inp: DX=size of block in paragraphs, size=0 is allowed\r\n;--- out: success: AX=1, BX=segment, DX=size\r\n;---      error:   AX=0, BL=error code, DX=largest block\r\n\r\nUMB_alloc proc\r\n\r\n    @dprintf ?UMBDBG, <\"UMBalloc enter, DX=%X\",10>, dx\r\n    mov esi, offset UMBsegments\r\n    xor ebx,ebx     ; holds largest too-small block size\r\n\r\n@@UMBloop:\r\n    cmp [esi].UMBBLK.wSegm,0    ; see if valid UMB\r\n    je @@UMBnext                ; no\r\n    test BYTE PTR [esi].UMBBLK.wSize+1,UMB_ALLOCATED\r\n    jne @@UMBnext               ;  yes\r\n    cmp dx,[esi].UMBBLK.wSize   ; dx = requested block size (high bit of UMB size known reset)\r\n    jbe @@UMBfound              ; enough memory available in UMB\r\n    cmp bx,[esi].UMBBLK.wSize\r\n    ja @@UMBnext\r\n    mov bx,[esi].UMBBLK.wSize   ; update largest too-small block size\r\n@@UMBnext:\r\n    add esi,size UMBBLK\r\n    cmp esi,offset UMBend\r\n    jnz @@UMBloop\r\n    @dprintf ?UMBDBG, <\"UMBalloc failed, bx=%X\",10>, bx\r\n    xor eax,eax     ; flag failure\r\n    or ebx,ebx\r\n    jne @@umb_too_small\r\n    mov bl,0B1h     ; error \"no UMB's are available\"\r\n    xor edx,edx\r\n    jmp UMB_exit\r\n@@umb_too_small:\r\n    mov edx,ebx     ; return largest UMB in DX\r\n    mov bl,0B0h     ; error \"only smaller UMB available\"\r\n    jmp UMB_exit\r\n\r\n@@UMBfound:\r\n\r\n; see if actual UMB size exceeds request size by >=2K\r\n\r\n    mov ax,80h              ; 128 paras == 2K\r\n    add ax,dx\r\n    cmp ax,[esi].UMBBLK.wSize\r\n    ja @@good_umb          ; can't split it, just use it\r\n\r\n;  2K or over would be unused, see if we can split the block\r\n\r\n    mov ebx, offset UMBsegments\r\n@@splitloop:\r\n    cmp [ebx].UMBBLK.wSegm,0\r\n    je @@freefound\r\n    add ebx,size UMBBLK\r\n    cmp ebx,offset UMBend\r\n    jne @@splitloop\r\n    jmp @@good_umb\r\n    \r\n;-- an unused entry found, split the block\r\n\r\n@@freefound:\r\n    mov eax, edx\r\n    add eax, 7Fh\r\n    and eax,not 7Fh             ; round up allocation to next 2K in paras\r\n    mov cx,[esi].UMBBLK.wSegm\r\n    add cx,ax\r\n    mov [ebx].UMBBLK.wSegm,cx   ; new block has segment offset of old block+allocation\r\n    mov cx,[esi].UMBBLK.wSize   ; get original UMB block size, in paras\r\n    sub cx,ax                   ; subtract allocation\r\n    mov [ebx].UMBBLK.wSize,cx   ; update new block with old block size minus allocation\r\n    mov [esi].UMBBLK.wSize,ax   ; update original UMB block size to allocation\r\n    @dprintf ?UMBDBG, <\"UMB block split, new entry=%X segm=%X size=%X\",10>, ebx, [ebx].UMBBLK.wSegm, [ebx].UMBBLK.wSize\r\n\r\n@@good_umb:\r\n    mov dx,[esi].UMBBLK.wSize               ; return actual block size in dx\r\n    or BYTE PTR [esi].UMBBLK.wSize+1,UMB_ALLOCATED\r\n    mov bx,[esi].UMBBLK.wSegm               ; get UMB segment address in bx\r\n    mov word ptr [EBP].Client_Reg_Struc.Client_EBX,bx\r\n    mov ax,1\r\nUMB_alloc endp  ;fall throu\r\n\r\n;--- there was a problem with JWasm if there's a jump to a forward\r\n;--- reference which is not a simple label but a PROC! If the \r\n;--- jump cannot be SHORT, the displacement isn't adjusted then!\r\n\r\nUMB_exit proc\r\n;UMB_exit:\r\n    @dprintf ?UMBDBG, <\"UMB exit, ax=%X, bx=%X, dx=%X\",10>, ax, bx, dx\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, ax\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EDX, dx\r\n    and al,al\r\n    jnz @@umbexit_noerror\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EBX, bl\r\n@@umbexit_noerror:\r\n    ret\r\nUMB_exit endp\r\n\r\n;--- UMBFree\r\n;--- DX=segment of UMB to release.\r\n;--- todo: merge free blocks\r\n\r\n;UMB_free proc\r\nUMB_free:\r\n    @dprintf ?UMBDBG, <\"UMBfree enter, DX=%X\",10>, dx\r\n\r\n    call UMB_findblock  ;clears eax\r\n    jc  UMB_exit\r\n    and BYTE PTR [esi].UMBBLK.wSize+1,not UMB_ALLOCATED  ; mark UMB as free\r\n    inc eax         ; flag success\r\n    jmp UMB_exit\r\n\r\n;UMB_free endp\r\n\r\n;--- UMBrealloc\r\n;--- currently can only shrink a block\r\n;--- and it does not really shrink, just return success\r\n;--- inp: DX=segment, BX=new size of block in paragraphs\r\n\r\nUMB_realloc proc\r\n\r\n;;  mov ebx,[ebp].Client_Reg_Struc.Client_EBX   ; restore EBX\r\n\r\n    @dprintf ?UMBDBG, <\"UMBrealloc enter, DX=%X, BX=%X\",10>, dx, bx\r\n\r\n    call UMB_findblock  ;clears eax\r\n    jc UMB_exit\r\n    mov cx, [esi].UMBBLK.wSize\r\n    and ch, not UMB_ALLOCATED\r\n    cmp bx, cx\r\n    ja @@umbreal_error\r\n    inc eax         ; flag success\r\n    jmp UMB_exit\r\n@@umbreal_error:    ; block is too small\r\n    mov dx, cx\r\n    mov bl, 0B0h\r\n    jmp UMB_exit\r\n\r\nUMB_realloc endp\r\n\r\nUMB_findblock proc\r\n    mov esi,offset UMBsegments\r\n    xor eax,eax                 ; flag failure\r\n@@freeloop:\r\n    cmp [esi].UMBBLK.wSegm,dx   ; see if matches existing UMB allocation\r\n    je  @@blockfound\r\n    add esi,size UMBBLK\r\n    cmp esi,offset UMBend\r\n    jnz @@freeloop\r\n@@blocknotalloced:\r\n    mov bl,0b2h                 ; invalid UMB segment number error code\r\n    stc\r\n    ret\r\n@@blockfound:\r\n    test byte ptr [esi].UMBBLK.wSize+1,UMB_ALLOCATED\r\n    jz @@blocknotalloced\r\n    clc\r\n    ret\r\n    align 4\r\nUMB_findblock endp\r\n\r\n.text$03 ends\r\n\r\n.text$04 segment\r\n\r\n;--- UMB initialization code\r\n;--- in: ESI->JemmIni\r\n;--- preserves esi, ebp\r\n\r\nUMB_Init proc public\r\n    mov ebx,[esi].JEMMINIT.PageMap\r\n    mov ecx,0A0h\r\n    mov edi,offset UMBsegments - size UMBBLK\r\n    mov dl,0\r\n@@nextitem2:\r\n    mov al,[ebx+ecx]\r\n    call IsShadowRAM\r\n    jnc @@isshadow\r\n    cmp [esi].JEMMINIT.NoRAM,0\r\n    jnz @@skippage2\r\n    call IsUMBMemory\r\n    jc @@skippage2\r\n@@isshadow:\r\n    cmp dl,0\r\n    jz @@newumb\r\nif ?SPLIT\r\n    cmp al,'8'      ;is it a SPLIT ROM?\r\n    jb @@newumb    ;then it must be a new UMB\r\nendif\r\n    add [edi].UMBBLK.wSize,100h\r\n    call clearpage\r\n    jmp @@nextpage\r\n@@newumb:\r\n    add edi, size UMBBLK\r\n    cmp edi, offset UMBsegments + UMB_MAX_BLOCKS * size UMBBLK\r\n    jnc @@umbdone\r\n    inc [esi].JEMMINIT.NumUMBs\r\n    mov byte ptr [edi].UMBBLK.wSegm+1,cl\r\n    @dprintf ?INITDBG, <\"new UMB at %X\",10>, [edi].UMBBLK.wSegm\r\n    mov [edi].UMBBLK.wSize,100h\r\nif ?SPLIT\r\n    call clearpageEx\r\nelse\r\n    call clearpage\r\nendif\r\n    mov dl,1\r\n    jmp @@nextpage\r\n@@skippage2:\r\n    mov dl,0\r\n@@nextpage:\r\n    inc ecx\r\n    cmp cl,0F8h\r\n    jb @@nextitem2\r\n@@umbdone:\r\n    @dprintf ?INITDBG, <\"UMBs initialized\",10>\r\n    ret\r\nUMB_Init endp\r\n\r\nIsShadowRAM proc\r\n    cmp al,'S'\r\n    jnz @@isnotshadow\r\n    push ecx\r\n    shl ecx,12\r\n    mov ah,[ecx]\r\n    mov byte ptr [ecx],55h\r\n    cmp byte ptr [ecx],55h\r\n    jnz @@isnotshadow2\r\n    mov byte ptr [ecx],0AAh\r\n    cmp byte ptr [ecx],0AAh\r\n    jnz @@isnotshadow2\r\n    mov [ecx],ah\r\n    pop ecx\r\n    ret\r\n@@isnotshadow2:\r\n    mov [ecx],ah\r\n    pop ecx\r\n@@isnotshadow:\r\n    stc\r\n    ret\r\nIsShadowRAM endp\r\n\r\n;--- clear an UMB \"page\"\r\n;--- in: ecx = page number\r\n;--- in: al = page type, if '1' <= al < '8' then split ROM\r\n;--- in: EDI -> UMBBLK \r\n\r\nif ?SPLIT\r\n\r\nclearpageEx proc\r\n    cmp al,'8'\r\n    jnc clearpage\r\n    sub al,'0'  ;1,2,3,4,5,6,7\r\n    movzx eax,al\r\n    shl eax,5   ;20,40,60,80,A0,C0,E0\r\n    mov byte ptr [edi].UMBBLK.wSegm,al\r\n    sub word ptr [edi].UMBBLK.wSize,ax\r\n    @dprintf ?INITDBG, <\"split page, UMB at %X size=%X\",10>, [edi].UMBBLK.wSegm, [edi].UMBBLK.wSize\r\n    shl eax,4   ;1->200, 2->400, 3->600, 4->800, 5->A00, 6->C00, 7->E00\r\n    push edi\r\n    push ecx\r\n    mov edi,ecx\r\n    shl edi,12\r\n    add edi,eax\r\n    sub eax,1000h\r\n    neg eax\r\n    shr eax,2      ;added in v5.78\r\n    mov ecx,eax\r\n    xor eax,eax\r\n    rep stosd\r\n    pop ecx\r\n    pop edi\r\n    ret\r\nclearpageEx endp\r\n\r\nendif\r\n\r\n;--- clear a \"page\"\r\n;--- in: ecx = page number\r\n\r\nclearpage proc\r\n    push edi\r\n    push ecx\r\n    mov edi,ecx\r\n    shl edi,12\r\n    mov ecx,1000h/4\r\n    xor eax,eax\r\n    rep stosd\r\n    pop ecx\r\n    pop edi\r\n    ret\r\nclearpage endp\r\n\r\n.text$04 ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/VCPI.ASM",
    "content": "\r\n;--- VCPI implementation\r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n;--- originally written by Michael Devore\r\n;--- extended and modified for Jemm by Japheth \r\n\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n    include ems.inc         ;EMS definitions\r\n    include vcpi.inc        ;VCPI definitions\r\n\r\n?CLEARTB    equ 1   ;std 1, 1=clear \"busy\" flag in client's TSS descriptor\r\n?VCPITMPSS  equ 1   ;std 1, 1=use a tmp SS sel to avoid hiword(ESP) to be <> 0\r\n?SAFEMODE   EQU 0   ;std 0, 1=additionally switch GDTR and IDTR\r\n?SAFETSS    equ 0   ;std 0, 1=additionally switch TR (clear \"busy\" flags!)\r\n?SPSYSTEM   equ 1   ;std 1, 1=set shared page at 110000 to \"system\" instead of r/o\r\n\r\n    include extern32.inc\r\n\r\nif ?VCPI\r\n\r\nif ?VCPIXDBG\r\n.text$01 segment\r\ndbgv IRETDV86 <>\r\n.text$01 ends\r\nendif\r\n\r\nif ?CODEIN2PAGE\r\n    @seg .text$02,<PAGE>\r\nelse\r\n    @seg .text$02,<PARA>\r\nendif\r\n.text$02 ENDS\r\n\r\n.text$03 segment\r\n\r\n;--- the interface is simple:\r\n;--- inp: EBP -> client_reg_struc\r\n;--- all registers except EBP and ECX still contain the client values.\r\n;--- out: ah and edx will be copied to client_reg_struc,\r\n;--- other client registers must be changed there directly\r\n\r\n    align 4\r\n\r\nVCPI_Call_Table label dword\r\n    Dd VCPI_Presence    ; 0\r\n    Dd VCPI_GetInterface\r\n    Dd VCPI_GetMax      ; 2\r\n    Dd VCPI_GetFreePages\r\n    Dd VCPI_Allocate4K  ; 4\r\n    Dd VCPI_Free4K\r\n    Dd VCPI_GetAddress  ; 6\r\n    Dd VCPI_GetCR0\r\n    Dd VCPI_ReadDR      ; 8\r\n    Dd VCPI_WriteDR\r\n    Dd VCPI_GetMappings ; 0ah\r\n    Dd VCPI_SetMappings\r\n\r\n;--- v5.85: VCPI_V86toPM wasn't activated in this table, because the label\r\n;--- is used directly (see EMS.ASM). Now it's part of the table, so JLoad knows\r\n;--- the label and JLMs may callout to a VCPI client. \r\n    Dd VCPI_V86toPM     ; 0ch\r\n;VCPI_MAX equ ($ - VCPI_Call_Table) / 4\r\n\r\n;\r\n; AX=DE00: VCPI presence detection\r\n;  return BH = 1 (major version), BL = 0 (minor version)\r\n;\r\nVCPI_Presence   PROC\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EBX,100h\r\n    mov ah,EMSS_OK\r\n    ret\r\nVCPI_Presence   ENDP\r\n\r\n;\r\n; AX=DE01: VCPI get protected mode interface\r\n;  inp: es:di -> client zero page table (to fill)\r\n;       ds:si -> three descriptor table entries in client's GDT (to fill)\r\n;  out: [es:di] page table filled\r\n;       di: first uninitialized page table entry (advanced by 4K)\r\n;      ebx: offset to server's protect mode code segment\r\n; The VCPI docs tell that the physical page that receives this\r\n; zero page table must NOT be altered after this call, to allow\r\n; the server to modify PTEs. However, this seems an error in the\r\n; documentation, since the host does not \"manage\" VCPI clients,\r\n; hence has no idea what \"interface\" is the current one.\r\n\r\nVCPI_GetInterface   PROC\r\n    movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_DS\r\n    shl edi,4\r\n    movzx esi,si\r\n    add edi,esi         ; esi -> client GDT entries\r\n    mov esi, offset V86GDT + FLAT_CODE_SEL\r\n    movsd\r\n    movsd\r\n    movsd\r\n    movsd\r\n\r\n    movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    movzx edi,WORD PTR [ebp].Client_Reg_Struc.Client_EDI\r\n    shl esi,4\r\n    add edi,esi             ; edi -> client zero page table\r\n    mov esi, @GetPTEAddr(?PAGETAB0) ; page table for first 1M\r\n\r\n;--- Jemm must ensure that label VCPI_PM_Entry will be in shared memory.\r\n;--- Since this label is located in .text$2, 1 page should suffice.\r\n\r\nif ?CODEIN2PAGE\r\n    mov ecx, (440h+2*4) ;this is offset in page table for 112000h\r\nelse\r\n    mov ecx, (440h+1*4) ;this is offset in page table for 111000h\r\nendif\r\nif 0 ; not needed - the monitor code beyond the shared space isn't touched by SBEINIT\r\n\ttest [bV86Flags], V86F_SB\r\n\tjz @F\r\n\tmov ecx, pPg0PartEnd     ;end address of monitor part in page table 0\r\n\tshr ecx, 10\r\n@@:\r\nendif\r\n    add word ptr [ebp].Client_Reg_Struc.Client_EDI,cx\r\n    shr ecx, 2\r\n@@vgiloop:\r\n    lods dword ptr [esi]\r\n    and ah,0F1h     ; clear bits 9-11\r\n    stos dword ptr [edi]\r\n    loop @@vgiloop\r\n\r\nif ?SPSYSTEM\r\n    and byte ptr es:[edi-4], not 4  ;set shared page 111xxxh to system\r\nelse\r\n    or byte ptr es:[edi-4], 4\r\n    and byte ptr es:[edi-4], not 2  ;set shared page 111xxxh to r/o\r\nendif\r\n\r\n    mov [ebp].Client_Reg_Struc.Client_EBX,OFFSET VCPI_PM_Entry\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\nVCPI_GetInterface   ENDP\r\n\r\n; AX=DE02: VCPI get maximum physical memory address\r\n;  return edx == physical address of highest 4K page available\r\n;\r\nVCPI_GetMax PROC\r\n    mov edx,[dwMaxPhysMem]\r\nif ?EMX\r\n    test bV86Flags, V86F_EMX;the EMX DOS extender fails if too much memory\r\n    jz @@noemx              ;is available!\r\n    mov edx,[pmem.dwMaxMem4K]\r\n    shl edx, 12             ;convert to bytes\r\n    add edx, [vdsstat.DMABuffStartPhys]\r\n@@noemx:\r\nendif\r\n    dec edx\r\n    and dx,NOT 0fffh\r\n\r\n    mov ah,EMSS_OK\r\n    ret\r\n    align 4\r\n\r\nVCPI_GetMax ENDP\r\n\r\n; function 03,04 and 05 may also be called from protected-mode.\r\n; then SS+DS+ES are flat, but they are NOT FLAT_DATA_SEL.\r\n; A pop of segment registers would cause a GPF, since the\r\n; client's GDT is active, but most likely is not mapped in\r\n; current address context.\r\n\r\n; AX=DE03: VCPI get number of free pages\r\n;  out: edx == number of free pages\r\n\r\nVCPI_GetFreePages PROC\r\n\r\n    call Pool_GetFree4KPages    ; free 4k pool pages in eax\r\n    mov edx, eax\r\n    mov ah, EMSS_OK\r\n    ret\r\n    align 4\r\n\r\nVCPI_GetFreePages ENDP\r\n\r\n; AX=DE04: VCPI allocate a 4K page\r\n;  out: edx == physical address of 4K page allocated\r\n\r\nVCPI_Allocate4K PROC public\r\n\r\n    mov eax,[pmem.dwMaxMem4K]\r\n    sub eax,[pmem.dwUsedMem4K]\r\n    jbe @@fail\r\n    call Pool_Allocate4KPage    ; see if any pool block has 4K free\r\n    jc @@fail\r\n    mov edx, eax\r\n    mov ah,EMSS_OK\r\n    ret\r\n@@fail:\r\n    stc\r\n    mov ah,EMSS_OUT_OF_FREE_PAGES\r\n    ret\r\n    align 4\r\n\r\nVCPI_Allocate4K ENDP\r\n\r\n; AX=DE05: VCPI free a 4K page\r\n;  in: edx == physical address of 4K page to free\r\n\r\nVCPI_Free4K PROC\r\n    call Pool_Free4KPage\r\n    jc @@bad\r\n    mov ah,EMSS_OK\r\n    ret\r\n@@bad:\r\n    mov ah,EMSS_LOG_PAGE_INVALID\r\n    ret\r\n\r\nVCPI_Free4K ENDP\r\n\r\n;\r\n; AX=DE06: VCPI get physical address of 4K page in first megabyte\r\n;  entry cx = page number (cx destroyed, use stack copy)\r\n;  return edx == physical address of 4K page\r\n;\r\nVCPI_GetAddress PROC\r\n    movzx ecx, word ptr [ebp].Client_Reg_Struc.Client_ECX\r\n    cmp cx,256\r\n    jae @@vga_bad       ; page outside of first megabyte\r\n\r\n    mov edx, @GetPTEAddr(ecx*4+?PAGETAB0)\r\n    and dx,0f000h       ; mask to page frame address\r\n    mov ah,EMSS_OK\r\n    ret\r\n\r\n@@vga_bad:\r\n    mov ah,EMSS_PHYS_PAGE_INVALID\r\n    ret\r\n\r\nVCPI_GetAddress ENDP\r\n\r\n;\r\n; AX=DE07: VCPI read CR0\r\n;  return EBX == CR0\r\n;\r\nVCPI_GetCR0 PROC\r\n    mov ebx,cr0\r\n    mov [ebp].Client_Reg_Struc.Client_EBX, ebx\r\n    mov ah,EMSS_OK\r\n    ret\r\nVCPI_GetCR0 ENDP\r\n\r\n;\r\n; AX=DE08: VCPI read debug registers\r\n;  call with ES:DI buffer pointer. Returns with buffer filled.\r\n;  (8 dwords, dr0 first, dr4/5 undefined)\r\n;\r\nVCPI_ReadDR PROC\r\n    movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl esi,4\r\n    movzx edi,di\r\n    add edi,esi\r\n    mov eax,dr0\r\n    stosd\r\n    mov eax,dr1\r\n    stosd\r\n    mov eax,dr2\r\n    stosd\r\n    mov eax,dr3\r\n    stosd\r\n    mov eax,dr6\r\n    mov [edi+8], eax\r\n    stosd\r\n    mov eax,dr7\r\n    mov [edi+8], eax\r\n    stosd\r\n    mov ah,EMSS_OK\r\n    ret\r\nVCPI_ReadDR ENDP\r\n\r\n;\r\n; AX=DE09: VCPI write debug registers\r\n;  call with ES:DI buffer pointer. Updates debug registers.\r\n;  (8 dwords, dr0 first, dr4/5 ignored)\r\n;\r\nVCPI_WriteDR PROC\r\n    movzx esi,WORD PTR [ebp].Client_Reg_Struc.Client_ES\r\n    shl esi,4\r\n    movzx edi,di\r\n    add esi,edi\r\n    lodsd\r\n    mov dr0,eax\r\n    lodsd\r\n    mov dr1,eax\r\n    lodsd\r\n    mov dr2,eax\r\n    lodsd\r\n    mov dr3,eax\r\n    add esi,8\r\n    lodsd\r\n    mov dr6,eax\r\n    lodsd\r\n    mov dr7,eax\r\n    mov ah,EMSS_OK\r\n    ret\r\nVCPI_WriteDR ENDP\r\n\r\n;\r\n; AX=DE0A: VCPI get 8259A interrupt vector mappings\r\n;  return bx == 1st vector mapping for master 8259A (IRQ0-IRQ7)\r\n;    cx == 1st vector mapping for slave 8259A (IRQ8-IRQ15)\r\n;\r\nVCPI_GetMappings PROC\r\n    mov bx, [wMasterPICBase]\r\n    mov cx, [wSlavePICBase]\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EBX,bx\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_ECX,cx\r\n    mov ah,EMSS_OK\r\n    ret\r\nVCPI_GetMappings ENDP\r\n\r\n; AX=DE0B: VCPI set 8259A interrupt vector mappings\r\n;  entry bx == 1st vector mapping for master 8259A (IRQ0-IRQ7)\r\n;    cx == 1st vector mapping for slave 8259A (IRQ8-IRQ15)\r\n;-- this is meant just as info, the client has to program the PIC itself\r\n\r\nVCPI_SetMappings PROC\r\n\r\n    mov ecx, [ebp].Client_Reg_Struc.Client_ECX\r\n    mov [wMasterPICBase],bx\r\n    mov [wSlavePICBase],cx\r\n    mov ah,EMSS_OK\r\n    ret\r\nVCPI_SetMappings ENDP\r\n\r\n.text$03 ends\r\n\r\n.text$02 segment\r\n\r\n;--- the following 3 procs must be located in the first page.\r\n;--- If this is no longer possible,\r\n;--- the VCPI shared address space has to be increased.\r\n\r\n; VCPI switch V86 mode to protected mode\r\n; inp: AX=DE0C\r\n;     ESI -> linear address of \"v86 to protected-mode switch\" data,\r\n;            must be in first MB.\r\n; out: GDTR, IDTR, LDTR, TR loaded for client\r\n;     SS:ESP=?HLPSTKSIZE (must provide at least 16 byte space for client)\r\n;     HIWORD(ESP) is cleared\r\n; modifies EAX, SS:ESP (ESI may also be altered according to VCPI docs)\r\n;\r\n\r\n    .386p\r\n\r\nVCPI_V86toPM PROC public\r\n\r\n    @dprintf ?VCPIXDBG, <\"VCPI rm, ax=DE0C, esi=%X cr3=%X cs:eip=%X:%X\",10>, esi, dword ptr [esi].V86TOPM.dwCR3,\\\r\n        word ptr [esi].V86TOPM.dfCSEIP+4, dword ptr [esi].V86TOPM.dfCSEIP\r\n    mov ecx,[ebp].Client_Reg_Struc.Client_ECX   ;restore client value\r\n    mov ebp,[ebp].Client_Reg_Struc.Client_EBP   ;restore client value\r\n\r\nif ?VCPITMPSS\r\n;--- some poorly written VCPI clients expect hiword(esp) to be zero!\r\n;--- So we provide a small 128 byte stack.\r\n;--- SS must be loaded BEFORE the address context is changed!\r\n    mov ax, V86_STACK_SEL\r\n    mov ss, eax\r\nendif\r\n\r\n    mov eax,[esi].V86TOPM.dwCR3     ; set client's context *first*\r\n    mov cr3,eax\r\n    mov eax,[esi].V86TOPM.dwIDTOFFS ; set up client's IDT\r\n    lidt fword ptr [eax]\r\n    mov eax,[esi].V86TOPM.dwGDTOFFS ; set up client's GDT\r\n    lgdt fword ptr [eax]\r\n    lldt [esi].V86TOPM.wLDTR        ; set up client's LDT\r\nif ?CLEARTB    \r\n\r\n;--- the \"busy\" flag of the TSS must be cleared, according to VCPI docs, and\r\n;--- this must be done BEFORE the TR register is loaded!\r\n\r\n    movzx esp,[esi].V86TOPM.wTR\r\n    and esp,not 111b                ; v5.85: don't assume bits 0+1 of new value for TR are 0!\r\n\r\n    mov eax,[eax+2]                 ; EAX == linear address of client's GDT\r\n    and ds:[esp+eax].DESCRIPTOR.bAttr,NOT 2; clear task busy bit in TSS descriptor\r\nendif\r\n    ltr [esi].V86TOPM.wTR           ; set up client's TSS\r\n\r\nif ?VCPITMPSS\r\n    mov esp,?HLPSTKSIZE\r\nelse\r\n    mov esp,?BASE+?HLPSTKSIZE\r\nendif\r\n    jmp [esi].V86TOPM.dfCSEIP       ; jump to client's entry point\r\n\r\n\r\nVCPI_V86toPM ENDP\r\n\r\n    align 4\r\n\r\n; VCPI switch protected mode to v86 mode\r\n;  inp: SS:ESP set for IRETD to V86, PM far call return address to discard;\r\n;       stack must be in shared address space.\r\n;       AX=DE0C\r\n;       DS -> selector containing full shared address space 0-11xxxx\r\n;  out: CR3, GDTR, IDTR, LDTR, TR loaded for server,\r\n;    IRETD will load SS:ESP and all segment registers, then enable v86 mode.\r\n;  modifies: EAX\r\n;\r\n;-- what has to be done is simple:\r\n;-- 1. switch to host context (SS:ESP will stay valid, stack is in 1. MB)\r\n;-- 2. load IDTR, GDTR, LDTR, TR for VCPI host\r\n;-- 3. clear task busy bit in host's TSS descriptor\r\n;-- 4. clear task switch flag in CR0\r\n;-- 5. IRETD will switch to v86 mode, interrupts disabled\r\n\r\nVCPI_PMtoV86 PROC\r\n\r\nife ?SHAREDGDT\r\n    mov eax,cs          ; if GDT is in nonshared space, the current\r\n    add eax,8           ; DS cannot be used to access it (TNT DOS extender!)\r\n    mov ds,eax          ; load DS with a copy of FLAT_DATA_SEL\r\nendif\r\n    cli                 ; client should have disabled interrupts, but not all do\r\n    mov eax,[V86CR3]\r\n;--- don't use [ESP+x], because SS may be 16-bit\r\n    add esp,5*4         ; position ESP to EFL+4 on stack\r\n    push 20002h or (V86IOPL shl 12)  ; set flags VM=1, NT=0, IOPL=3, IF=0, TF=0\r\n    sub esp,2*4\r\n    mov cr3,eax\r\nif ?VCPIXDBG\r\n\tmov eax,[esp].IRETDV86._Eip\r\n\tmov [dbgv.vEIP],eax\r\n\tmov eax,[esp].IRETDV86._Cs\r\n\tmov [dbgv.vCS],eax\r\n\tmov eax,[esp].IRETDV86._Efl\r\n\tmov [dbgv.vEFL],eax\r\n\tmov eax,[esp].IRETDV86._Esp\r\n\tmov [dbgv.vESP],eax\r\n\tmov eax,[esp].IRETDV86._Ss\r\n\tmov [dbgv.vSS],eax\r\n\tmov eax,[esp].IRETDV86._Es\r\n\tmov [dbgv.vES],eax\r\n\tmov eax,[esp].IRETDV86._Ds\r\n\tmov [dbgv.vDS],eax\r\n\tmov eax,[esp].IRETDV86._Fs\r\n\tmov [dbgv.vFS],eax\r\n\tmov eax,[esp].IRETDV86._Gs\r\n\tmov [dbgv.vGS],eax\r\nendif\r\n    lgdt [GDT_PTR]\r\n    xor eax,eax\r\n    and byte ptr [V86GDT+V86_TSS_SEL+5], NOT 2\r\n    lidt [IDT_PTR]\r\n    lldt ax\r\n    mov al,V86_TSS_SEL\r\n    clts\r\n    ltr ax\r\nif ?VCPIXDBG\r\n    mov eax,FLAT_DATA_SEL\r\n    mov ss,eax\r\n    mov esp, [dwStackCurr]\r\n    @dprintf ?VCPIXDBG, <\"VCPI pm, ax=DE0C, rm cs:eip=%X:%X, rm ss:esp=%X:%X\", 10>,\r\n        word ptr dbgv.vCS, dbgv.vEIP, word ptr dbgv.vSS, dbgv.vESP\r\n    mov esp, offset dbgv\r\nendif\r\n    iretd\r\n\r\nVCPI_PMtoV86 ENDP\r\n\r\n;-- put the VCPI PM entry at the very beginning. This entry\r\n;-- must be in the shared address space, and just the first page\r\n;-- is shared!\r\n;-- entry for EMM routines called from protected mode\r\n\r\n    align 4\r\n\r\nVCPI_PM_Entry PROC\r\n\r\n    cmp ax,0de0ch       ; see if switch from protected mode to V86 mode\r\n    je VCPI_PMtoV86    ; yes, give it special handling\r\n\r\n; other than de0ch, don't allow anything than de03h,de04h,de05h from PM\r\n\r\n    cmp ax,0DE03h\r\n    jb @@INV_CALL\r\n    cmp ax,0DE05h\r\n    ja @@INV_CALL\r\n\r\n    pushfd\r\n    push ds             ; have to save segments for p-mode entry\r\n    push es\r\n\r\n    pushad\r\n\r\n    mov ecx,cs\r\n    add ecx,8\r\n    mov ds,ecx          ; load DS,ES with a copy of FLAT_DATA_SEL\r\n    mov es,ecx\r\n\r\n;--- segment registers should *all* be set *before* switching contexts!\r\n;--- why? because GDT of client might be located in unreachable space.\r\n;--- get client SS:ESP + CR3, switch to host stack and context\r\n\r\n;--- only VCPI functions 03, 04 and 05 are allowed\r\n;--- which use registers EAX (AH) and EDX only.\r\n;--- NMIs are NOT supposed to occur - they will crash the monitor;\r\n;--- the VCPI client is responsible for masking NMIs!\r\n\r\n    cli                 ; don't allow interruptions\r\n    mov ebp, ss\r\n    mov esi, esp\r\n    mov edi, cr3\r\n    mov ebx, [V86CR3]\r\n;--- this sequence cannot be traced, since the new stack is valid only\r\n;--- after CR3 has been set - and the old stack isn't valid with the new CR3.\r\n    mov ss, ecx         ;this move must be before CR3 is switched\r\n    mov esp, [dwStackCurr]\r\n    mov cr3, ebx\r\n\r\n    push ebp            ;save client's SS\r\n    push esi            ;save client's ESP\r\n    push edi            ;save client's CR3\r\n\r\n;--- SAFEMODE will fully switch to Jemm's context\r\n\r\nif ?SAFEMODE\r\n    sub esp,8+8\r\n    sgdt [esp+0]\r\n if ?SAFETSS\r\n    str [esp+6]\r\n endif\r\n    sidt [esp+8]\r\n    lgdt [GDT_PTR]\r\n    lidt [IDT_PTR]\r\n    mov cx, FLAT_DATA_SEL\r\n    mov ss, ecx\r\n    mov ds, ecx\r\n    mov es, ecx\r\n if ?SAFETSS\r\n    mov cx, TSS_SEL\r\n    ltr cx\r\n endif\r\nendif\r\n\r\n    cld\r\n\r\nif ?VCPIDBG\r\n    push eax\r\n    push edx\r\nendif\r\n\r\n    movzx ecx,al\r\n    call [VCPI_Call_Table+ECX*4]\r\nif ?VCPIDBG\r\n    mov ebp,esp\r\n    @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\r\n    add esp,8\r\nendif\r\n\r\nif ?SAFEMODE\r\n    lgdt fword ptr [esp+0]\r\n    lidt fword ptr [esp+8]\r\n if ?SAFETSS\r\n    mov cx,[esp+6]\r\n    ltr cx\r\n endif\r\n    add esp,8+8\r\nendif\r\n    pop ecx\r\nif 1\r\n    pop esi\r\n    pop edi\r\n    mov cr3, ecx    ; restore client context *first* and\r\n    mov ss, edi     ; then restore client SS:ESP, thus client's GDT\r\n    mov esp, esi    ; need not be located in shared address space\r\nelse\r\n    lss esp,[esp]   ; problem with this code: LSS loads descriptor for SS from client GDT,\r\n    mov cr3, ecx\t; and that GDT might be accessible only AFTER CR3 is restored.\r\nendif\r\n    mov [esp].PUSHADS.rEDX,edx\r\n    mov byte ptr [esp].PUSHADS.rEAX+1,ah\r\n    popad\r\n    pop es\r\n    pop ds\r\n    popfd\r\n    retf\r\n\r\n@@INV_CALL:\r\n    @dprintf ?VCPIDBG, <\"VCPI pm, invalid call ax=%X\",10>, ax\r\n    mov ah,EMSS_INVALID_SUBFUNC\r\n    retf\r\n\r\nVCPI_PM_Entry   ENDP\r\n\r\n.text$02 ends\r\n\r\nendif   ;?VCPI\r\n\r\n    END\r\n"
  },
  {
    "path": "src/VCPI.INC",
    "content": "\r\n; structure for VCPI switch from V86 to protected mode\r\n\r\nV86TOPM  STRUCT\r\ndwCR3       DD  ?   ; client's CR3 value\r\ndwGDTOFFS   DD  ?   ; offset of client's GDTR value\r\ndwIDTOFFS   DD  ?   ; offset of client's IDTR value\r\nwLDTR       DW  ?   ; client's LDTR value\r\nwTR         DW  ?   ; client's TR value\r\ndfCSEIP     DF  ?   ; entry point in client\r\n            DW  ?\r\nV86TOPM  ENDS\r\n\r\n"
  },
  {
    "path": "src/VDMA.ASM",
    "content": "\r\n;--- DMA port trapping code\r\n;--- originally written by Harald Albrecht\r\n;--- modified for Jemm by Japheth\r\n;--- copyright (c) 1990 c't/Harald Albrecht\r\n\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n;--- IN, although trapped, returns the true value of the port.\r\n;--- OUT stores the values to be written, and also does an OUT with unmodified ax/al reg.\r\n;--- if a DMA channel is unmasked, Dma_CheckChannel()\r\n;--- is called and will do a translation, if required.\r\n\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n    include dma.inc\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n.text$01 SEGMENT\r\n\r\nif ?DMAPT\r\n\r\n;--- DMAREQ.bFlags values\r\nDMAF_ENABLED    equ 1   ;1=channel is enabled (=unmasked)\r\n\r\n;--- bits in DmaGblFlags\r\n\r\nDMAG_HiLoFlag1  EQU 0   ; addressing Hi-Byte, DMA controller #1\r\nDMAG_HiLoFlag2  EQU 1   ; addressing Hi-Byte, DMA controller #2\r\n\r\n;--- if DMA buffer is required\r\n;--- this flag isn't really used currently; instead,\r\n;--- flag RFL_COPYBUFFER in bRFlags is set & queried.\r\n;DMAG_NeedBuffer EQU 2\r\n\r\n;--- int 13h/40h DMA auto translation\r\nDmaTargetAdr    DD  0   ; Original adress for DMA\r\nDmaTargetLen    DD  0   ; Utilized part of the Buffer\r\n\r\nDmaChn          DMAREQ MAXDMACHANNEL dup (<0,0,0,0,0,0>)\t; also accessed by VDS code\r\nDmaGblFlags     DW 0    ; global flags\r\n                align 4\r\n\r\nendif\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\nif ?DMAPT\r\n\r\nPAGEREGBASE equ 80h\r\n\r\n;--- table to translate page ports (80-8F) to channel no\r\n;------------  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F\r\nPageLookUp DB -1, 2, 3, 1,-1,-1,-1, 0,-1, 6, 7, 5,-1,-1,-1,-1\r\n\r\n;--- page translation table\r\n;--- channels   0   1   2   3   4   5   6   7\r\nPageXLat DB    87H,83H,81H,82H,8Fh,8BH,89H,8AH\r\n\r\n;--- Access to a DMA page register (80-8F)\r\n;--- v5.84: now Simulate_IO_trap is called if access isn't \"byte\"\r\n; DX : port\r\n; AL : value\r\n; CL : type\r\n;--- modifies EDI, BX\r\n\r\nDma_HandlePagePorts PROC public\r\n\r\n    test cl,OUT_INSTR\r\n    jz Simulate_IO   ;is input, just translate to byte accesses\r\n    test cl,STRING_IO or DWORD_IO or WORD_IO\r\n    jnz Simulate_IO_trap\t; split access to bytes\r\n\r\n    @dprintf ?DMADBG, <\"Dma_HandlePagePorts: OUT, dx=%X al=%X\",10>, dx, al\r\n\r\n    OUT DX,AL\r\n    MOVZX EBX,DL                  ; store page value in cache\r\n    MOVSX EDI,PageLookUp[EBX - PAGEREGBASE]\r\n\tcmp edi, -1\t; v5.85: check added\r\n\tjz @F\r\n    MOV [DmaChn + EDI*sizeof DMAREQ].PageReg,AL\r\n    jmp Dma_IsChannelUnmasked\r\n@@:\r\n\tret\r\n    align 4\r\n\r\nDma_HandlePagePorts ENDP\r\n\r\n;--- handle 8-bit DMA ports 00-0F\r\n;--- ports 00-07: addr/count for channels 0-3\r\n;--- port 08: R=status 0-3, W=cmd register 0-3\r\n;--- port 09: W=write req 0-3\r\n;--- port 0A: RW=channel mask register 0-3\r\n;--- port 0B: W=mode 0-3\r\n;--- port 0C: W=clear flipflop 0-3 for addr/count\r\n;--- port 0D: R=temp reg, W=reset DMA\r\n;--- port 0E: W=clear mask reg 0-3\r\n;--- port 0F: W=write mask register\r\n;--- channel 2 is used by floppy controller\r\n\r\n;--- v5.84: now Simulate_IO_trap is called if access isn't \"byte\"\r\n\r\n; DX = port\r\n; AL = value\r\n; CL = type\r\n;--- modifies EDI, BX\r\n\r\nDma_HandleDmaPorts8 proc public\r\n\r\n    test cl,OUT_INSTR\r\n    jz Simulate_IO\r\n    test cl,STRING_IO or WORD_IO or DWORD_IO ;not byte access?\r\n    jnz Simulate_IO_trap\r\n\r\nif ?DMADBG\r\n    push eax\r\n    @dprintf ?DMADBG, <\"Dma_HandleDmaPorts8: OUT, dx=%X al=%X \">, dx, al\r\n    in al, 8\t\t\t; ok to read port 8???\r\n    @dprintf ?DMADBG, <\"08=%X\",10>, al\r\n    pop eax\r\nendif\r\n\r\n    CMP DL, DMA_STATUS_CMD     ; ports 0-7 ( channel 0-3 index/count )?\r\n    JB @@BlockSet8\r\n    CMP DL, DMA_SINGLEMASK     ; single mask?\r\n    JZ @@SMask8\r\n    CMP DL, DMA_MODE           ; \"Mode Register\" responded ?\r\n    JZ @@Mode8\r\n    CMP DL, DMA_FLIPFLOP       ; clear flip-flop?\r\n    JZ ClearFlipFlop8\r\nif ?MMASK\r\n    CMP DL, DMA_IMM_RESET\r\n    JZ @@MMask8On\r\n    CMP DL, DMA_MASK_RESET\r\n    JZ @@MMask8Off\r\n    CMP DL, DMA_MULTIMASK\r\n    JZ @@MMask8\r\nendif\r\n    @dprintf ?DMADBG, <\"Dma_HandleDmaPorts8: unhandled OUT to port %X\",10>, dx\r\n    out dx, al\r\n    ret\r\nClearFlipFlop8::\r\n    out dx, al\r\n    BTR [DmaGblFlags], DMAG_HiLoFlag1\r\n    ret\r\n@@BlockSet8:            ; I/O addresses 0-7 (DMA 8bit)\r\n    MOV BX, DMAG_HiLoFlag1\r\n    MOVZX EDI,DL\r\n    SHR EDI,1           ; get channel# into EDI\r\n    jc Dma_SetLength\r\n    JMP Dma_SetAddr\r\n@@SMask8:               ;single mask\r\n    mov edi, eax\r\n    AND EDI,0011B\r\n    jmp Dma_SetSMask\r\n@@Mode8:\r\n    MOV EDI,EAX\r\n    AND EDI,011B\r\n    jmp Dma_SetMode\r\nif ?MMASK\r\n@@MMask8On:             ;mask all 4 channels\r\n    mov al,1111b\r\n    jmp @@MMask8\r\n@@MMask8Off:            ;unmask all 4 channels\r\n    mov al,0000b\r\n@@MMask8:\r\n    xor edi, edi\r\n    jmp Dma_SetMMask\r\nendif\r\n    align 4\r\n\r\nDma_HandleDmaPorts8 endp\r\n\r\n;--- handle 16-bit DMA ports C0-DF\r\n;--- v5.84: now Simulate_IO_trap is called if access isn't \"byte\"\r\n; DX = port\r\n; AL = value\r\n; CL = type\r\n;--- modifies EDI, BX\r\n\r\nDma_HandleDmaPorts16 proc public\r\n\r\n    test cl,OUT_INSTR\r\n    jz Simulate_IO\r\n    test cl,STRING_IO or WORD_IO or DWORD_IO ;not byte access?\r\n    jnz Simulate_IO_trap\r\n\r\nif ?DMADBG\r\n    push eax\r\n    @dprintf ?DMADBG, <\"Dma_HandleDmaPorts16: OUT, dx=%X ax=%X \">, dx, ax\r\n    in al, 0D0h\r\n    @dprintf ?DMADBG, <\"D0=%X\",10>, al\r\n    pop eax\r\nendif\r\n\r\n    CMP DL, DMA_STATUS_CMD16    ; ports C0-CE ( channel 4-7 index/count )? \r\n    JB @@BlockSet16\r\n    CMP DL, DMA_SINGLEMASK16    ; single mask?\r\n    JZ @@SMask16\r\n    CMP DL, DMA_MODE16          ; mode set?\r\n    JZ @@Mode16\r\n    CMP DL, DMA_FLIPFLOP16\r\n    JZ @@Clear16\r\nif ?MMASK\r\n    CMP DL, DMA_IMM_RESET16\r\n    JZ @@MMask16On\r\n    CMP DL, DMA_MASK_RESET16\r\n    JZ @@MMask16Off\r\n    CMP DL, DMA_MULTIMASK16\r\n    JZ @@MMask16\r\nendif\r\n    @dprintf ?DMADBG, <\"Dma_HandleDmaPorts16: unhandled OUT to port %X\",10>, dx\r\n    out dx, al\r\n    ret\r\n@@Clear16:\r\n    out dx, al\r\n    BTR [DmaGblFlags], DMAG_HiLoFlag2\r\n    ret\r\n@@BlockSet16:           ; I/O addresses C0-CF (DMA 16bit)\r\n    MOV BX, DMAG_HiLoFlag2\r\n    MOV EDI,EDX\r\n    SHR EDI,2           ; get channel# into EDI\r\n    AND EDI,3\r\n    ADD EDI,4\r\n    TEST DL,02H         ; block length or address?\r\n    JNZ Dma_SetLength\r\n    jmp Dma_SetAddr\r\n@@SMask16:\r\n    mov edi, eax\r\n    AND EDI,011B        ; mask the DMA channel #\r\n    add edi,4\r\n    jmp Dma_SetSMask\r\n@@Mode16:\r\n    MOV EDI,EAX\r\n    AND EDI,011B        ; mask the DMA channel #\r\n    ADD EDI,4\r\n    JMP Dma_SetMode\r\nif ?MMASK\r\n@@MMask16On:            ;mask all 4 channels\r\n    mov al,1111b\r\n    jmp @@MMask16\r\n@@MMask16Off:           ;unmask all 4 channels\r\n    mov al,0000b\r\n@@MMask16:\r\n    mov edi, 4\r\n    jmp Dma_SetMMask\r\nendif\r\n    align 4\r\n\r\nDma_HandleDmaPorts16 endp\r\n\r\n;--- EDI=channel#\r\n\r\nDma_SetAddr:\r\n    mov ecx,offset DmaChn.BaseAdr\r\n    jmp Dma_SetLenOrAdr\r\nDma_SetLength:\r\n    mov ecx,offset DmaChn.BlockLen\r\nDma_SetLenOrAdr:\r\n    OUT DX,AL\r\n    BTC [DmaGblFlags],BX        ; toggle the Hi/Lo-Flag\r\n    adc ecx,0\r\n    MOV BYTE PTR [ECX+EDI*sizeof DMAREQ],AL\r\n    test cl,1\r\n    jnz Dma_IsChannelUnmasked\r\n    ret\r\n;\r\n; Monitor \"Mode Register\" for the Transferdirection DMA <--> I/O\r\n;\r\nDma_SetMode:\r\n    out dx, al\r\n    mov [DmaChn+EDI*sizeof DMAREQ].bMode,al\r\n\r\nDma_IsChannelUnmasked proc\r\nif 0\r\n    mov ecx,[dwRFlags]\r\n    test byte ptr [ecx],RFL_DMAOP ;new Int 13h/40h DMA op?\r\n    jz @@nonewreq\r\n    and byte ptr [ecx],not 2\r\n    and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED  ;wait until channel is enabled\r\n@@nonewreq:\r\nendif\r\n    test [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED\r\n    jz @@Bye\r\n    CALL Dma_CheckChannel    ; Then check if auto translation should be done\r\n@@Bye:\r\n    RET\r\nDma_IsChannelUnmasked endp\r\n\r\n;--- Single mask port (000A, 00D4)\r\n;--- bits 0-1 select channel\r\n;--- bit 2=1 -> enable channel mask (=channel inactive)\r\n\r\nDma_SetSMask proc\r\n    and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED\r\n    test al,4\r\n    jnz exit\r\n    or [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED\r\nif 0\r\n    mov ecx,[dwRFlags]\r\n    and byte ptr [ecx],not RFL_DMAOP\r\nendif\r\n    push eax\r\n    call Dma_CheckChannel\r\n    pop eax\r\nexit:\r\n    out dx, al\r\n    ret\r\nDma_SetSMask endp\r\n\r\nif ?MMASK\r\n;--- Master mask port (000F, 00DE)\r\n;--- this port is declared as W only, but usually it can be read as well.\r\n;--- bit 0 -> 1=channel 0 masked, 0=channel 0 unmasked (=active)\r\n;--- bit 1 -> ... chn 1\r\n;--- bit 2 -> ... chn 2\r\n;--- bit 3 -> ... chn 3\r\n\r\n;--- edi=0 or 4\r\n;--- al=value to be written to port (bits 0-3)\r\n\r\nDma_SetMMask proc\r\n    mov cl,4\r\n    push eax\r\n@@nextcnl:\r\n;   test [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED\r\n;   jnz @@skipchannel       ;channel is already active\r\n    and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED\r\n    shr al,1\r\n    jc @@skipchannel       ;channel will become/stay inactive\r\n    or [DmaChn+EDI*sizeof DMAREQ].bFlags,DMAF_ENABLED\r\n    push eax\r\n    push ecx\r\n    call Dma_CheckChannel\r\n    pop ecx\r\n    pop eax\r\n@@skipchannel:\r\n    inc edi\r\n    dec cl\r\n    jnz @@nextcnl\r\n    pop eax\r\n    out dx, al\t\t; the OUT must be done AFTER Dma_checkchannel\r\n    ret\r\n\r\nDma_SetMMask ENDP\r\n\r\nendif\r\n\r\nif ?ALT_TRAPHDL\r\n\r\n;--- v5.85: check some ISA 8-bit DMA ports for channel 2 even\r\n;--- if an external i/o trap handler has been installed.\r\n;--- actually, just ports A,B,C,F are checked (D/E mask/unmask all channels!),\r\n;--- but generally, writes to ports 4 (and 5?) and 81h ( page register for channel 2 )\r\n;--- should also be checked. \r\n\r\nISA_DMA_Traphandler proc public\r\n\tcmp [DmaChn+2*sizeof DMAREQ].cDisable,0\t;ISA DMA translation disabled for channel 2?\r\n\tjnz done\r\n\ttest cl,OUT_INSTR\t\t; nothing done if IN (no longer true if ports 4/5/81h will be checked)\r\n\tjz done\r\n\tcmp dl, DMA_SINGLEMASK\r\n\tjz is0A\r\n\tcmp dl, DMA_MODE\r\n\tjz is0B\r\n\tcmp dl, DMA_FLIPFLOP\r\n\tjz is0C\r\nif ?MMASK\r\n\tcmp dl, DMA_MULTIMASK\r\n\tjz is0F\r\nendif\r\ndone:\r\n\tclc\r\n\tret\r\nis0C:\r\n\tcall ClearFlipFlop8\r\n\tjmp done\r\nis0A:\r\n\tpush Dma_SetSMask\r\n\tjmp checkchannel\r\nis0B:\r\n\tpush Dma_SetMode\r\n;\tjmp checkchannel\r\ncheckchannel:\r\n\tpush edi\r\n\tmov edi, eax\r\n\tand edi, 11b\r\n\tcmp edi, 2\t\t; channel 2?\r\n\tclc\r\n\tjnz @F\r\n\tcall dword ptr [esp+4]\r\n\tstc\t\t\t\t; handle OUT for channel 2 exclusively!\r\n@@:\r\n\tpop edi\r\n\tlea esp, [esp+4]\r\n\tret\r\nif ?MMASK\r\nis0F:\r\n;--- just channel 2 is of interest;\r\n;--- in AL, the mask-bit is already at pos 2\r\n\tpushad\r\n\tmov edi, 2\r\n\tand al, 100b\r\n\tor eax, edi\r\n\tmov dl, DMA_SINGLEMASK\r\n\tcall Dma_SetSMask\r\n\tpopad\r\n\tclc\r\n\tret\r\nendif\r\n\r\nISA_DMA_Traphandler endp\r\n\r\nendif\r\n\r\n; A DMA-Channel is unmasked, so a check can (and has to) take place.\r\n;\r\n; In: EDI: Channelnumber 0..7\r\n; modifies ECX, ESI, EBX, AL\r\n;\r\n; Generally, this code assumes that DMA writes that are done within an INT13/INT40\r\n; are done because a FD/HD op uses ISA DMA, no matter what DMA channel is modified.\r\n; This is NOT a valid assumption! ( might be DMA register ports writes during an IRQ ).\r\n\r\n;--- better approach: check channel 2 only for FD\r\n;--- optionally, if activated by cmdline option, check channel (3/X) for HD\r\n\r\nDma_CheckChannel PROC\r\n\r\n    @dprintf ?DMADBG, <\"Dma_CheckChannel enter, edi=%u\",10>, edi\r\n\r\n    cmp [DmaChn+EDI*sizeof DMAREQ].cDisable,0   ;translation disabled by VDS?\r\n    jnz @@Bye\r\n    test [DmaChn+EDI*sizeof DMAREQ].bMode,DMA_MODE_OPERATION\r\n    JZ @@Bye                          ; If a verify is wanted (MODE_OP==00), nothing is to be done\r\n\r\nif 1\r\n;--- todo: explain why this is done!\r\n    and [DmaChn+EDI*sizeof DMAREQ].bFlags,not DMAF_ENABLED\r\nendif\r\n\r\n    MOVZX ECX,[DmaChn+EDI*sizeof DMAREQ].BlockLen ; get block length into ECX\r\n    MOVZX ESI,[DmaChn+EDI*sizeof DMAREQ].PageReg  ; and base address into ESI\r\n    INC ECX\r\n    CMP EDI,4                       ; for 16bit DMA channels,\r\n    JB @@Only8\r\n    ADD ECX,ECX                     ; words are transferred\r\n    SHL ESI,15                      ; and Bit 0 of the page register\r\n    MOV SI,[DmaChn+EDI*sizeof DMAREQ].BaseAdr   ; will be ignored\r\n    SHL ESI,1\r\n    JMP @@Chk\r\n@@Only8:\r\n    SHL ESI,16                      ; for 8bit DMA, address calculation\r\n    MOV SI,[DmaChn+EDI*sizeof DMAREQ].BaseAdr   ; is simple\r\n@@Chk:\r\n\r\n;--- here: ESI=start address (linear!), ECX=size\r\n;--- check if a buffer is needed.\r\n\r\n;    BTR [DmaGblFlags], DMAG_NeedBuffer  ; Initialise.\r\n\r\n;--- Is the block occupying contiguous physical pages?\r\n;--- Or does it cross a 64kB/128 kB boundary?\r\n;--- after the call ESI will hold the physical address (if NC)\r\n\r\n    push esi\r\n    CALL Dma_IsContiguous\r\n    pop ebx\r\n    JNC @@Set   ;NC if buffer *is* contiguous               \r\n\r\n;   test [DmaChn+EDI*sizeof DMAREQ].bMode,10h ;auto-init? Then a buffer is useless\r\n;   jnz @@Set\r\n\r\n    @dprintf ?DMADBG, <\"block not contiguous or crosses 64k border, DMA buffer needed!\",10>\r\n\r\n    MOV ESI,[vdsstat.DMABuffStartPhys] ; get DMA Buffer physical address\r\n;    BTS [DmaGblFlags], DMAG_NeedBuffer\t; flag is set, but nowhere queried, so actually it's not really used\r\n    cmp ecx, [vdsstat.DMABuffSize]\r\n    jbe @F\r\n    mov ecx, [vdsstat.DMABuffSize]     ; error: DMA buffer overflow    \r\n@@:\r\n    call @@SetX                     ; use ESI to set DMA registers page & offset\r\n    MOV AL,[DmaChn+EDI*sizeof DMAREQ].bMode  ; copy data into buffer required?\r\n    and al,DMA_MODE_OPERATION\r\n    cmp al,1000b\t; 1000b = DMA_MODE_OP_READ!\r\n    JNZ @@IsRead\t; todo: explain why \"JNZ\" is correct here\r\n\r\n    @dprintf ?DMADBG, <\"Dma_CheckChannel: copy into DMA buffer\",10>\r\n\r\n    push edi\r\n    MOV ESI,EBX\r\n    MOV EDI,[vdsstat.DMABuffStart]\r\n    CLD\r\n    call MoveMemory\r\n    pop edi\r\n    RET\r\n\r\n@@IsRead:\r\n\r\nif ?HOOK13\r\n    mov eax,[dwRFlags]\r\n  if 1\r\n    test byte ptr [eax],RFL_DMAOP ; is a int 13h/40h read op pending?\r\n    jz @@Bye\r\n    mov byte ptr [eax],RFL_COPYBUFFER\r\n  else\r\n    or byte ptr [eax],RFL_COPYBUFFER\r\n  endif\r\nelse\r\n    or [bDiskIrq],1\r\nendif\r\n\r\n;--- store values, needed for \"copy out of buffer\" after disk i/o has been done\r\n\r\n    MOV [DmaTargetLen],ECX\r\n    MOV [DmaTargetAdr],EBX          ; save linear address of block\r\n@@Bye:\r\n    RET\r\n\r\n;--- use value in ESI to set DMA base address and page register\r\n;--- modifies ESI, EAX\r\n\r\n@@Set:\r\n\r\nif ?DMADBG\r\n    cmp esi, ebx\r\n    setnz al\r\n    movzx eax,al\r\n    @dprintf ?DMADBG, <\"target lin. start address=%X length=%X phys=%X chn=%X [%u]\",10>,ebx,ecx,esi,edi,eax\r\nendif\r\n\r\n    cmp esi, ebx                ; has address changed?\r\n    jz @@Bye\r\n@@SetX:\r\n    push edx\r\n    CMP EDI,4                   ; 8-bit or 16-bit channel ?\r\n    JB @@Set8\r\n\r\n;-- calc I/O-address from DMA channel: 4->C0, 5->C4, 6->C8, 7->CC\r\n\r\n    lea edx, [edi-4]\r\n    shl edx, 2                  ; edx=0,4,8,C\r\n    or dl, DMA_BASE_16BIT\r\n    BTR [DmaGblFlags],DMAG_HiLoFlag2 ; DMA #2, HiLo-FlipFlop-Flag\r\n    OUT DMA_FLIPFLOP16,AL       ; clear Hi/Lo-FlipFlop\r\n    JMP $+2\r\n    SHR ESI,1\r\n    MOV EAX,ESI                 ; reprogram base address\r\n    SHR ESI,15\r\n    JMP @@Cont\r\n\r\n;-- calc I/O-address from DMA channel: 0->0, 1->2, 2->4, 3->6\r\n\r\n@@Set8:\r\n    LEA EDX,[EDI+EDI]\r\n    BTR [DmaGblFlags],DMAG_HiLoFlag1 ; DMA #1, HiLo-FlipFlop-Flag\r\n    OUT DMA_FLIPFLOP,AL         ; clear Hi/Lo-FlipFlop\r\n    JMP $+2\r\n    MOV EAX,ESI                 ; reprogram base address\r\n    SHR ESI,16                  ; get bits 16-23 into 0-7\r\n@@Cont:\r\n    OUT DX,AL                   ; set index/count\r\n    MOV AL,AH\r\n    JMP $+2\r\n    OUT DX,AL\r\n    MOV DL,PageXLat[EDI]        ; get I/O-Adress of page-register\r\n    MOV EAX,ESI                 ; set the page register\r\n    OUT DX,AL\r\n    pop edx\r\n    ret\r\n    align 4\r\n\r\nDma_CheckChannel ENDP\r\n\r\n;\r\n; Check a memory-area to be in physical contiguous memory.\r\n; Furthermore, the physical region must not cross a 64 kB\r\n; (or 128 kB for 16bit) boundary\r\n;\r\n; In:  ESI: linear start address (bits 0-23 only)\r\n;      ECX: Length of the range (?DMABUFFMAX is max in kB)\r\n;      EDI: channel\r\n; Out: NC: contiguous, ESI = physical adress\r\n;       C: not contiguous\r\n; modifies: EAX, EBX, ESI \r\n\r\nDma_IsContiguous PROC\r\n    cmp ESI, 400000h-20000h     ; don't touch PTEs > 3E0000h\r\n    jnc @@Ok2\r\n    PUSH ECX\r\n\r\nif 0    ;ecx cannot be 0 (is 1-20000h)\r\n    cmp ecx,1                   ; make sure ecx is at least 1\r\n    adc ecx,0\r\nendif\r\n\r\n    PUSH ESI\r\n    lea ECX,[ecx+esi-1]         ; ecx -> last byte \r\n\r\n    SHR ESI,12                  ; linear start address -> PTE\r\n    SHR ECX,12                  ; linear end   address -> PTE\r\n    sub ecx, esi\r\n    \r\n    lea esi, @GetPTEAddr(ESI*4+?PAGETAB0) ; ESI -> PTE\r\n    MOV EAX,[ESI]               ; get PTE\r\n\r\n    @dprintf ?DMADBG, <\"Dma_IsContiguous: PTE=%X ecx=%X\",10>, eax, ecx\r\n\r\n    shr eax, 12                 ; ignore bits 0-11 of PTE\r\n\r\n;--- ESI -> PTE, EAX=1. PTE\r\n\r\n    test eax, 0FF000h            ; if physical address is > 16 MB        \r\n    jnz @@Fail                  ; a buffer is needed in any case\r\n\r\n    JECXZ @@Ok                    ; exit if region fits in one page\r\n\r\n    PUSH EAX                     ; save Bits 31-12\r\n@@Loop:\r\n    ADD ESI,4                   ; one entry further\r\n    INC EAX                     ; Go one page and\r\n    MOV EBX,[ESI]\r\n    shr EBX, 12\r\n    CMP EAX,EBX                 ; contiguous?\r\n    loopz @@Loop\r\n    POP EAX\r\n    JNZ @@Fail\r\n    mov esi,eax                 ; don't change eax for compare\r\n    cmp edi,4                   ; DMA channel 0-3?\r\n    setnc cl\r\n    add cl,4\r\n    shr esi, cl                 ; shift 4 or 5 bits\r\n    shr ebx, cl\r\n    cmp ebx,esi\r\n    JNZ @@Fail                  ; a 64/128 kB Border has been crossed!!!\r\n@@Ok:\r\n    pop ESI\r\n    shl eax,12\r\n    AND ESI,0FFFh\r\n    OR ESI,EAX\r\n    pop ECX\r\n@@Ok2:\r\n    CLC\r\n    RET\r\n@@Fail:\r\n    POP ESI\r\n    POP ECX\r\n    STC\r\n    RET\r\n    align 4\r\n\r\nDma_IsContiguous ENDP\r\n\r\n\r\n; Copy the DMA-buffercontents after termination of the DISK-I/O to the\r\n; wanted target/location.\r\n; * If ?HOOK13 is 1, this is triggered by an v86-breakpoint in real-mode int 13\r\n; * and int 40 handlers. First the original int 13 / 40 is done, and\r\n; * then, if the DMA buffer was used, things are copied out of this buffer\r\n; * to the target linear address.\r\n; * if ?HOOK13 is 0, this is called by Int 15h handler, ax=9101h.\r\n\r\nDma_CopyBuffer PROC public\r\n\r\nif ?HOOK13\r\n    call Simulate_Iret\r\n    and [ebp].Client_Reg_Struc.Client_EFlags, not 1 ;CF=0\r\nendif\r\n\r\n    MOV ESI,[vdsstat.DMABuffStart]\r\n    MOV EDI,[DmaTargetAdr]\r\n    mov ECX,[DmaTargetLen]\r\n\r\n    @dprintf ?DMADBG, <\"Dma_CopyBuffer: dst=%X src=%X siz=%X\",10>, edi, esi, ecx\r\n\r\n    CLD\r\n    call MoveMemory\r\nif ?HOOK13\r\n    mov eax,[dwRFlags]\r\n    mov byte ptr [eax],0\t; clear flags RFL_xxx\r\nendif\r\n    ret\r\n    align 4\r\n\r\nDma_CopyBuffer ENDP\r\n\r\nendif   ;?DMAPT\r\n\r\n.text$03    ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/VDS.ASM",
    "content": "\r\n;--- Jemm's VDS implementation\r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n    include dma.inc\r\n    include vds.inc\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n.text$01 SEGMENT\r\n\r\nif ?VDS\r\nOldInt4B    dd 0\r\nendif\r\n\r\nif ?DMAPT or ?VDS\r\n\r\n    align 4\r\n\r\nvdsstat DMABUFF <0,0,0,?DMABUFFMAX/32 dup (0),0>\r\n\r\n    align 4\r\nendif\r\n\r\n.text$01 ends\r\n\r\nif ?VDS\r\n\r\n.text$03 segment\r\n\r\nVDS_Call_Table label dword\r\n    Dd vds_version      ; 02 get version\r\n    Dd vds_lock         ; 03 lock region, ES:DI -> DDS  \r\n    Dd vds_unlock       ; 04 unlock region, ES:DI -> DDS\r\n    Dd vds_scatterlock  ; 05 scatter lock, ES:DI -> EDDS\r\n    Dd vds_scatterunlock; 06 scatter unlock, ES:DI -> EDDS\r\n    Dd vds_reqbuff      ; 07 request DMA buffer, ES:DI -> DDS\r\n    Dd vds_relbuff      ; 08 release DMA buffer, ES:DI -> DDS\r\n    Dd vds_copyinbuff   ; 09 copy into DMA buffer, ES:DI -> DDS\r\n    Dd vds_copyoutbuff  ; 0A copy out of DMA buffer, ES:DI -> DDS\r\n    Dd vds_disabletrans ; 0B disable DMA translation\r\n    Dd vds_enabletrans  ; 0C enable DMA translation\r\nVDS_MAX equ ($ - VDS_Call_Table) / 4\r\n    Dd vds_copyinbuffEx ; used by VDMA, 32bit version of copyinbuff\r\n    Dd vds_copyoutbuffEx; used by VDMA, 32bit version of copyoutbuff\r\n\r\n;--- bit vector which vds func needs DDS/EDDS translation (ES:DI -> EDI)\r\n\r\nif 0\r\n;------ CBA98765432\r\nbDDS dd 00111111110b\r\nendif\r\n\r\n;--- bit vector which vds func needs \"offset\" translation (BX:CX -> ECX)\r\nif 0\r\n;------ CBA98765432\r\nbCpB dd 00110000000b\r\nendif\r\n\r\n;--- VDS API\r\n;--- EBP -> Client_Reg_Struc\r\n;--- DX=flags?\r\n\r\nvds_handler proc public\r\n\r\n    and byte ptr [ebp].Client_Reg_Struc.Client_EFlags+2, not 1  ;v5.85: clear RF\r\n\r\n    mov eax, [EBP].Client_Reg_Struc.Client_EAX\r\n    cmp ah,81h\r\n    jnz @@isnotvds\r\n    movzx ebx,al\r\n    @dprintf ?VDSDBG gt 2, <\"VDS entry, al=%X\",10>, ebx\r\n    sub ebx,2\r\n    jc @@vds_reserved\r\n    cmp ebx,VDS_MAX\r\n    jnc @@vds_reserved\r\n    call Simulate_Iret   ;get the real \"current\" client flags!\r\nif 0\r\n    bt bDDS, ebx\r\n    jnc @@nodds\r\nendif\r\n;--- make DDS/EDDS accessible with EDI\r\n    movzx ecx,word ptr [ebp].Client_Reg_Struc.Client_ES \r\n    movzx edi,di\r\n    shl ecx, 4\r\n    add edi, ecx\r\n@@nodds:\r\n    movzx edx,word ptr [ebp].Client_Reg_Struc.Client_EDX\r\n    call [VDS_Call_Table + ebx*4]\r\n    jc @@failed\r\n    and [ebp].Client_Reg_Struc.Client_EFlags,not 1  ;CF=0\r\n    ret\r\n@@failed:\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, al\r\n    or [ebp].Client_Reg_Struc.Client_EFlags,1      ;CF=1\r\n    ret\r\n@@isnotvds:\r\n    mov ecx, [OldInt4B]\r\n    jecxz @@novdsoldcall\r\n    @dprintf ?VDSDBG gt 0, <\"unhandled Int 4Bh, jmp to cs:ip=%X\",10>, eax\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EIP, CX\r\n    shr ecx, 16\r\n    mov [ebp].Client_Reg_Struc.Client_CS, ECX\r\n    ret\r\n@@vds_reserved:\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EAX, VDSERR_FUNC_NOTSUPP\t;v5.85\r\n@@novdsoldcall:    \r\n    call Simulate_Iret\r\n    or  [ebp].Client_Reg_Struc.Client_EFlags,1      ;CF=1\r\n    ret\r\n    align 4\r\n\r\nvds_handler endp\r\n\r\nif 0\r\nvds_unsup proc\r\n    mov al, VDSERR_FUNC_NOTSUPP ; error \"function not supported\"\r\n    stc\r\n    ret\r\nvds_unsup endp\r\nendif\r\n\r\n;--- int 4b, ax=8102h\r\n;--- in: DX=flags - must be 0000\r\n;--- out: AX/BX/CX see below\r\n;---      SI:DI = DMA buffer size in bytes\r\n;---      DX = flags;\r\n;---       0: 1=DMA in first MB only (PC/XT arch)\r\n;---       1: 1=buffer is in first MB\r\n;---       2: 1=automatic remap supported\r\n;---       3: 1=all memory physically contiguous\r\n\r\nvds_version proc\r\n    and edx,edx\t;hiword of EDX cleared by Jemm\r\n    jnz @@fail\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, 100h; major/minor spec version\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EBX, 1   ; product number\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_ECX, 1   ; product revision\r\n    mov eax, [vdsstat.DMABuffSize]\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EDI,ax\r\n    shr eax, 16\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_ESI,ax\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EDX, 0   ; flags\r\n    clc\r\n    ret\r\n@@fail:\r\n    mov al,VDSERR_DXRSVDBITSSET  ;\"reserved bit set in dx\"\r\n    stc\r\n    ret\r\n    align 4\r\n\r\nvds_version endp\r\n\r\n;--- int 4b, ax=8107h, request DMA buffer\r\n;--- DX = flags, bit 1: copy data into buffer\r\n;--- EDI -> DDS\r\n;--- modifies ECX, EAX, EBX, DDS.wID, DDS.dwPhys\r\n;--- out: C, al=5/6/16\r\n;---      NC, DDS.dwPhys + DDS.wID set\r\n\r\nvds_reqbuff proc\r\n\r\n\tmov al, VDSERR_DXRSVDBITSSET\t; v5.85: check added\r\n\ttest dx, not VDSF_COPY\r\n\tjnz @@fail\r\n    mov [edi].DDS.wID, 0\r\n    mov ecx, [edi].DDS.dwSize\r\n    @dprintf ?VDSDBG gt 1, <\"VDS request buff, flgs=%X siz=%X\",10>, dx, ecx\r\n    and ecx, ecx\r\n    jz @@ok\r\n    mov al, VDSERR_BUFFTOOSMALL  ; buffer too small for region\r\n    add ecx, 400h-1 ; align to kB\r\n    and cx, 0FC00h\r\n    cmp ecx, [vdsstat.DMABuffSize]\r\n    ja @@fail\r\n\r\n;--- scan for a free region in buffer\r\n\r\n    mov eax, [vdsstat.DMABuffSize]\r\n    shr eax, 10         ; transform in KB\r\n    inc eax             ; the bit vector is 1-based!\r\n    shr ecx, 10\r\n    inc ecx             ; test for 1 bit more!\r\n@@rescan:    \r\n    mov ebx, ecx\r\n@@nextbit:\r\n    dec eax\r\n    js @@fail6\r\n    bt [vdsstat.DMABuffFree], eax\r\n    jnc @@rescan\r\n    dec ebx\r\n    jnz @@nextbit\r\n\r\n    dec ecx     ; now use the real size\r\n    inc eax     ; skip the last bit found\r\n\r\n;--- a free region large enough found\r\n\r\n    @dprintf ?VDSDBG gt 1, <\"VDS free region found at %X\",10>, ax\r\n\r\n    mov ebx, eax\r\n    mov [edi].DDS.wID, ax\r\n@@marknextbit:\r\n    btr [vdsstat.DMABuffFree], ebx\r\n    inc ebx\r\n    loop @@marknextbit\r\n    dec eax\r\n    shl eax, 10     ;convert to byte\r\n    add eax, [vdsstat.DMABuffStartPhys]\r\n    mov [edi].DDS.dwPhys, eax\r\n    @dprintf ?VDSDBG gt 1, <\"VDS req buff, phys=%X\",10>, eax\r\n    test dl,VDSF_COPY       ; copy into buffer?\r\n    jz @@ok\r\n    xor ecx, ecx\r\n    jmp vds_copyinbuffEx\r\n@@fail6:    \r\n    @dprintf ?VDSDBG gt 0, <\"VDS req buff aborted, eax=%X, ebx=%X\",10>, eax, ebx\r\n    mov al, VDSERR_BUFFINUSE; error \"buffer currently in use\"\r\n@@fail:\r\n    stc\r\n    ret\r\n@@ok:\r\n    clc\r\n    ret\r\n    align 4\r\n\r\nvds_reqbuff endp\r\n\r\n;--- int 4b, ax=8104h, unlock region\r\n;--- DX=Flags, bit 1: copy data out of buffer\r\n;--- EDI -> DDS\r\n;--- \"unlock region\" is the same as \"release buffer\"\r\n\r\nvds_unlock proc\r\n\r\n    @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\r\n\r\nvds_unlock endp ;fall through\r\n\r\n\r\n;--- int 4b, ax=8108h, release DMA buffer\r\n;--- DX = flags, bit 1: copy data out of buffer\r\n;--- EDI -> DDS (only buffer ID is needed)\r\n;--- modifies EBX, EAX\r\n\r\nvds_relbuff proc\r\n\r\n\ttest dx, not VDSF_COPY\t;v5.85 check added\r\n\tjnz fail10\r\n\r\n    movzx ebx, [edi].DDS.wID\r\n    @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\r\n    @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\r\n    and ebx, ebx\r\n    jz @@ok\r\n\r\n    mov al, VDSERR_INVAL_BUFFID  ;\"invalid buffer id\"\r\n    cmp bx, ?DMABUFFMAX\r\n    ja @@error\r\n\r\n;--- the bit at position -1 must be \"1\"\r\n;--- and bit 0 must be \"0\"\r\n;--- the region ends with a \"1\" bit\r\n\r\n    movzx ebx, bx\r\n    dec ebx\r\n    bt [vdsstat.DMABuffFree], ebx\r\n    jnc @@error\r\n    inc ebx\r\n    bt [vdsstat.DMABuffFree], ebx\r\n    jc @@error\r\n\r\n    test dl, VDSF_COPY\r\n    jz @@nextbit\r\n    push ebx\r\n    xor  ecx, ecx\r\n    call vds_copyoutbuffEx\r\n    pop ebx    \r\n    jc @@error\r\n\r\n@@nextbit:\r\n    bts [vdsstat.DMABuffFree], ebx\r\n    inc ebx\r\n    jnc @@nextbit\r\n@@ok:\r\n    clc\r\n    ret\r\n@@error:\r\n    stc\r\n    ret\r\n    align 4\r\n\r\nvds_relbuff endp\r\n\r\n;-- test if a buffer is valid and as large as required\r\n;-- used by copy into/out of DMA buffer\r\n;-- EDI -> DDS\r\n;-- ECX = offset in buffer\r\n;-- returns \r\n;--  NC and EBX == linear address of src/dst in DMA-buffer\r\n;--  C and error code in AL\r\n;-- modifies EAX, EBX, ECX\r\n\r\ntestbuffer proc\r\n\r\n    movzx ebx, [edi].DDS.wID\r\n    @dprintf ?VDSDBG gt 2, <\"VDS testbuff id=%X\",10>,bx\r\n    mov al, VDSERR_INVAL_BUFFID ;\"invalid buffer ID\"\r\n    and ebx, ebx\r\n    jz  @@fail\r\n    cmp bx, ?DMABUFFMAX\r\n    ja  @@fail\r\n    dec ebx\r\n    bt [vdsstat.DMABuffFree], ebx   ;bit at -1 *must* be set\r\n    jnc @@fail\r\n    inc ebx\r\n    bt [vdsstat.DMABuffFree], ebx   ;bit at 0 bit *must* be clear\r\n    jc @@fail\r\n    mov eax, ecx            ;eax == offset in DMA buffer\r\n\r\n    mov ecx, [edi].DDS.dwSize\r\n    and ecx, ecx\r\n    jz @@ok\r\n\r\n    lea ecx, [ecx+eax+3FFh]\r\n    shr ecx, 10\r\n\r\n    push eax\r\n    lea eax, [ebx+ecx]\r\n    cmp eax, ?DMABUFFMAX\r\n    pop eax\r\n    ja @@fail3\r\n\r\n    push ebx\r\n@@nextbit:\r\n    bt [vdsstat.DMABuffFree], ebx\r\n    jc @@fail2\r\n    inc ebx\r\n    dec ecx\r\n    jnz @@nextbit\r\n    pop ebx\r\n\r\n    dec ebx\r\n    shl ebx, 10\r\n    add ebx, [vdsstat.DMABuffStart]\r\n    add ebx, eax\r\n@@ok:\r\n    @dprintf ?VDSDBG gt 2, <\"VDS testbuff ok, buff start (linear)=%X\",10>, ebx\r\n    clc\r\n    ret\r\n@@fail2:\r\n    @dprintf ?VDSDBG gt 0, <\"VDS testbuff @@fail2, ebx=%X ecx=%X\",10>, ebx, ecx\r\n    pop ebx\r\n@@fail3:\r\n    mov al,VDSERR_BNDVIOLATION  ;\"copy out of buffer range\" - buffer boundary violation\r\n@@fail:\r\n    @dprintf ?VDSDBG gt 0, <\"VDS testbuff failed, al=%X\",10>, al\r\n    stc\r\n    ret\r\n    align 4\r\n\r\ntestbuffer endp\r\n\r\n;--- int 4b, ax=8109h, copy into DMA buffer\r\n;--- DX = flags, must be 0000\r\n;--- Client->BX:CX = offset in buffer\r\n;--- EDI -> DDS\r\n\r\nvds_copyinbuff proc\r\n\r\n    cmp dx,0\r\n    jnz fail10\r\n    mov ecx, [ebp].Client_Reg_Struc.Client_EBX\r\n    shl ecx, 16\r\n    mov cx, word ptr [ebp].Client_Reg_Struc.Client_ECX\r\n\r\nvds_copyinbuff endp ;fall thru\r\n\r\n;--- ECX = DMA buffer offset\r\n\r\nvds_copyinbuffEx proc\r\n\r\n    call testbuffer\t\t; returns EBX=linear address (inside DMA buffer)\r\n    jc @@exit\r\n    mov ecx, [edi].DDS.dwSize\r\n    movzx eax, [edi].DDS.wSeg\r\n    shl eax, 4\r\n    add eax, [edi].DDS.dwOfs\r\n    @dprintf ?VDSDBG gt 1, <\"VDS copyinbuff src=%X dst=%X siz=%X\",10>, eax, ebx, ecx\r\n    pushad\r\n    mov esi, eax\r\n    mov edi, ebx\r\n    cld\r\n    call MoveMemory\r\n    popad\r\n    clc\r\n@@exit:    \r\n    ret\r\n    align 4\r\n\r\nvds_copyinbuffEx endp\r\n\r\nfail10:\r\n\tmov al, VDSERR_DXRSVDBITSSET\r\n\tstc\r\n\tret\r\n\r\n;--- int 4b, ax=810Ah, copy out of DMA buffer\r\n;--- DX = flags, must be 0000\r\n;--- Client->BX:CX = offset in buffer\r\n;--- EDI -> DDS\r\n\r\nvds_copyoutbuff proc\r\n\r\n    cmp dx,0\r\n    jnz fail10\r\n    mov ecx, [ebp].Client_Reg_Struc.Client_EBX\r\n    shl ecx, 16\r\n    mov cx, word ptr [ebp].Client_Reg_Struc.Client_ECX\r\n    \r\nvds_copyoutbuff endp    ;fall thru\r\n\r\n;--- ECX = DMA buffer offset\r\n\r\nvds_copyoutbuffEx proc\r\n\r\n    call testbuffer\r\n    jc @@exit\r\n    mov ecx, [edi].DDS.dwSize\r\n    movzx eax, [edi].DDS.wSeg\r\n    shl eax, 4\r\n    add eax, [edi].DDS.dwOfs\r\n    pushad\r\n    mov esi, ebx\r\n    mov edi, eax\r\n    cld\r\n    call MoveMemory\r\n    popad\r\n    clc\r\n@@exit:\r\n    ret\r\n    align 4\r\n\r\nvds_copyoutbuffEx endp\r\n\r\n;--- subroutines for vds_lock\r\n\r\n;--- test if a region is contiguous and crosses 64/128 kB borders\r\n;--- eax = start page, ebx = end page, dx=flags\r\n;--- for first 4 MB only\r\n;--- out: NC if ok, C if failed\r\n;---      NC -> EAX = initial physical address\r\n;---      C -> AL = error code, EDX = size that would be ok\r\n;--- error code:\r\n;---  01: region not contiguous\r\n;---  02: region crossed 64kb/128kb boundary\r\n;--- called by vds_lock\r\n\r\nvds_contiguous proc\r\n\r\n    push esi\r\n    push edi\r\n\r\n    mov esi, @GetPTEAddr(?PAGETAB0)   ;get start of page tab 0\r\n\r\n    mov ecx, [esi+eax*4]\r\n    and cx, 0F000h\r\n    mov edi, ecx\r\n    push ecx\r\n@@nextitem:    \r\n    cmp eax, ebx\r\n    jz @@iscontiguous\r\n    inc eax\r\n    mov ecx, [esi+eax*4]\r\n    add edi, 1000h\r\n    and cx, 0F000h\r\n    cmp edi, ecx\r\n    je @@nextitem\r\n    pop ecx\r\n    mov edx, edi\r\n    sub edx, ecx\r\n    mov al, VDSERR_NOTCONTIGUOUS\r\n@@failed:\r\n    pop edi\r\n    pop esi\r\n    stc\r\n    ret\r\n@@failed2:          ;failed because 64/128 kb boundary crossing\r\n    mov edx, esi\r\n    shl edx, cl\r\n    sub edx, eax\r\n    mov al, VDSERR_CROSSEDBOUNDS\r\n    jmp @@failed\r\n@@iscontiguous:\r\n    pop eax         ;get start physical address\r\n    test dl,VDSF_64KALIGN or VDSF_128KALIGN ; boundary check?\r\n    jz @@nocheck\r\n    mov edi, eax\r\n    mov esi, ecx\r\n    test dl, VDSF_64KALIGN  ;check for 64 kb border crossing?\r\n    setz cl\r\n    add cl, 16\r\n    shr edi, cl\r\n    shr esi, cl\r\n    cmp edi, esi\r\n    jnz @@failed2\r\n@@nocheck:\r\n    pop edi\r\n    pop esi\r\n    ret\r\n    align 4\r\n\r\nvds_contiguous endp\r\n\r\n;-- simplified version of vds_contiguous\r\n;-- assumes linear == physical (for regions above 400000h)\r\n;-- eax = linear start address\r\n;-- ebx = size of region in bytes\r\n;-- ret C -> boundary error, AL=error code, EDX==size ok\r\n;-- ret NC -> ok, EAX == physical address [== linear address]\r\n;-- modifies ECX, EBX, EDX, EAX\r\n;-- called by vds_lock\r\n\r\nvds_contiguous2 proc\r\n\r\n;-- see if boundary alignment check\r\n\r\n    test dl,VDSF_64KALIGN or VDSF_128KALIGN ; boundary check?\r\n    jz @@ok\r\n    lea ebx,[eax+ebx-1]\r\n    test dl,VDSF_64KALIGN   ;if 64K works, then 128K will too\r\n    setz cl\r\n    add cl,16       ; shift 16 (64 kB) or 17 (128 kb) positions\r\n    mov edx,eax\r\n    shr ebx,cl      ; convert start/end to alignment 64K frames\r\n    shr edx,cl\r\n    cmp ebx,edx\r\n    je @@ok\r\n    inc edx         ; ecx == starting alignment frame+1\r\n    shl edx,cl      ; convert to next alignment frame address\r\n    sub edx,eax     ; get bytes to next alignment frame address from start\r\n    mov al,2        ; region crossed alignment boundary error code\r\n    stc\r\n@@ok:\r\n    ret\r\n    align 4\r\n\r\nvds_contiguous2 endp\r\n\r\n;--- int 4b, ax=8103h, lock region\r\n;--- EDI -> DDS\r\n;--- DX=Flags\r\n;--- 0:reserved\r\n;--- 1:1=data should be copied into buffer (if necessary) [requires 2 cleared]\r\n;--- 2:1=buffer disable (no DMA buffer should be allocated if noncontiguous or crosses 64/128 kB)\r\n;--- 3:1=disable automatic remap\r\n;--- 4:1=region must not cross 64 kb border\r\n;--- 5:1=region must not cross 128 kb border\r\n\r\n;--- there are several cases:\r\n;--- 1. region is contiguous (and does not cross borders)\r\n;---    out: NC\r\n;---         DDS.wID = 0\r\n;---         DDS.dwPhys = physical addr\r\n;--- 2. region is not contiguous and/or does cross borders\r\n;---    2.1 buffer disable flag set\r\n;---      out: C\r\n;---           DDS.wID = 0\r\n;---           AL = 1/2/3 \r\n;---    2.2 buffer disable flag cleared\r\n;---       2.2.1 buffer available and large enough\r\n;---             alloc buffer\r\n;---             if copy requested then copy data into buffer\r\n;---         out: NC\r\n;---              DDS.wID <> 0\r\n;---              DDS.dwPhys = physical addr of buffer\r\n;---       2.2.2 buffer too small\r\n;---         out: C\r\n;---              DDS.dwSize = size that would be ok\r\n;---              DDS.wID = 0\r\n;---              AL = 5\r\n;---       2.2.3 buffer not available\r\n;---         out: C\r\n;---              DDS.dwSize = ?\r\n;---              DDS.wID = 0\r\n;---              AL = 6\r\n;---\r\n;---     field DDS.dwPhys may be filled by \"Lock Region\"\r\n\r\nvds_lock proc\r\n\r\n    mov esi, edx    ;save flags\r\n\r\n    @dprintf ?VDSDBG gt 1, <\"VDS lock flgs=%X addr=%X:%X siz=%X\",10>, dx, [edi].DDS.wSeg, [edi].DDS.dwOfs, [edi].DDS.dwSize\r\n\r\n    xor eax, eax\r\n    mov ebx, [edi].DDS.dwSize   ; region size\r\n    cmp ebx, eax\r\nif 0\r\n    je @@locksuccess       ; zero byte-sized region always works\r\nelse\r\n    setz al         ;size 0 is always ok, but the physical address must be set\r\n    add ebx, eax    ;as well. So handle size 0 as if it is size 1\r\nendif\r\n    mov ax, [edi].DDS.wSeg\r\n    shl eax,4\r\n    add eax, [edi].DDS.dwOfs\r\n;    jc --> overflow error\r\n\r\n    lea ecx, [eax+ebx-1]\r\n    cmp ecx, 400000h; region in first 4 MB ?\r\n    jb @@below4MB\r\n\r\n;-- assume linear == physical\r\n;-- call a simplified version of vds_contiguous\r\n\r\n    call vds_contiguous2\r\n    jnc @@locksuccess\r\n    jmp @@lenfail\r\n\r\n@@below4MB:\r\n    mov ebx, ecx    ; ebx == final linear address\r\n    shr ebx,12      ; convert end to 4K frame\r\n    push eax\r\n    shr eax,12      ; convert start to 4K frame\r\n    call vds_contiguous\r\n    pop ecx\r\n    jc @@notcontiguous\r\n    and ch, 0Fh\r\n    or ax, cx\r\n    jmp @@locksuccess ;save EAX in DDS\r\n\r\n; physical memory is noncontiguous, error code is 1 or 2\r\n; return maximum length which would be ok\r\n\r\n@@notcontiguous:\r\n\r\n    and ecx, 0fffh\r\n    sub edx, ecx\r\n    mov ecx, esi            ; restore flags\r\n    test cl, VDSF_NOBUFFER  ; buffering disabled?\r\n    jnz @@nobuffering\r\n    push edx\r\n    mov edx, esi\r\n    call vds_reqbuff        ; modifies DDS.wID\r\n    pop edx\r\n    jnc @@lockok\r\n@@nobuffering:    \r\n\r\n    @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\r\n\r\n@@lenfail:\r\n    mov [edi].DDS.dwSize,edx    ; update maximum contiguous length\r\n    mov [edi].DDS.wID,0         ; zero buffer id?\r\n    stc\r\n    ret\r\n\r\n@@locksuccess:\r\n    @dprintf ?VDSDBG gt 1, <\"VDS lock ok, ret size=%X phys=%X\",10>, [edi].DDS.dwSize, eax\r\n    mov [edi].DDS.dwPhys,eax ; physical address\r\n    mov [edi].DDS.wID,0      ; zero buffer id\r\n\r\n@@lockok:\r\n    clc\r\n    ret\r\n\r\n    align 4\r\n\r\nvds_lock endp\r\n\r\n;--- int 4b, ax=8105h\r\n;--- inp: EDI -> EDDS\r\n;---      DX=flags (bits 6 & 7)\r\n;--- out: NC\r\n;---      EDDS.wNumUsed: no of entries used to describe region\r\n;---      regions/PTEs behind EDDS are set\r\n;--- err: C set\r\n;---      AL=error code\r\n;---      dwSize: length that can be locked with current entries\r\n;---      EDDS.wNumUsed: number of entries required to fully describe region!\r\n;--- modifies EAX, EBX, ECX, EDX, ESI\r\n\r\nvds_scatterlock proc\r\n\r\n    @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\r\n\r\n\tmov al, VDSERR_DXRSVDBITSSET\t\t; v5.85: check added\r\n\ttest dx, not (VDSF_PTE or VDSF_NPPTE)\r\n\tjz @F\r\n    stc\r\n    ret\r\n@@:\r\n    mov ebx,[edi].EDDS.dwSize\r\n    xor ecx,ecx     ; cx holds entries used\r\n    mov eax,ecx     ; zero eax for later calcs\r\n    mov [edi].EDDS.wNumUsed,cx  ; EDDS number entries used\r\n    and ebx,ebx\r\n    setz al\r\n    add ebx,eax     ; handle size=0 like size=1\r\n\r\n    mov ax,[edi].EDDS.wSeg\r\n    shl eax,4\r\n    add eax,[edi].EDDS.dwOfs    ;eax=linear address\r\n\r\n;    jc --> overflow error\r\n\r\n    test dl,VDSF_PTE    ; PTEs flagged?\r\n    jne @@getptes\r\n\r\n    mov edx,eax         ; edx == start linear address\r\n    shr eax,12          ; convert start to 4K frame\r\n    cmp eax,256\r\n    jb @@checklock     ; inside of UMB remapping range (<1M)\r\n\r\n; outside of UMB remapping range, assuming linear == physical\r\n    mov [edi].EDDS.wNumUsed,1   ; one region used/needed\r\n    cmp cx,[edi].EDDS.wNumAvail\r\n    jnc @@notenoughregions\r\n    mov [edi+size EDDS].EDDSRG.dwPhysAddr,edx   ; region physical address\r\n    mov [edi+size EDDS].EDDSRG.dwSizeRg,ebx     ; region size\r\n    clc\r\n    ret\r\n@@notenoughregions:\r\n    mov al, VDSERR_NAVL_TOOSMALL   ;error \"NumAvail too small\"\r\n    stc\r\n    ret\r\n\r\n;--- return regions, 1. MB\r\n;--- eax = linear start page (linear address >> 12)\r\n\r\n@@checklock:\r\n    push    edx         ; save start linear address\r\n\r\n    lea ebx,[ebx+edx-1] ; ebx == final linear address\r\n    shr ebx,12          ; convert to 4K frame\r\n    mov edx, @GetPTEAddr(eax*4+?PAGETAB0)\r\n    and dx, 0F000h\r\n    mov esi,edx         ; current physical address of 4K page\r\n    cmp cx,[edi].EDDS.wNumAvail\r\n    jnc @@bumpused\r\n    mov [edi+size EDDS].EDDSRG.dwPhysAddr,edx\r\n    mov [edi+size EDDS].EDDSRG.dwSizeRg,0\r\n    jmp @@bumpused\r\n\r\n@@entryloop:\r\n    cmp cx,[edi].EDDS.wNumAvail ; entries available count\r\n    jnc @@bumpused\r\n    mov [edi+ecx*8+size EDDS].EDDSRG.dwSizeRg,1000h ; init region size\r\n    mov [edi+ecx*8+size EDDS].EDDSRG.dwPhysAddr, esi\r\n\r\n@@bumpused:\r\n    inc [edi].EDDS.wNumUsed ; bump count of used/needed entries\r\n\r\n@@nextframe:\r\n    inc eax             ; next linear page frame\r\n    cmp eax,ebx\r\n    ja @@scatdone      ; no more regions to map\r\n    cmp ah,0            ; page below 100h?\r\n    jz @@next2         ; not at end of first 1M\r\n    cmp ax,256\r\n    ja @@scatdone      ; finishing off final region entry\r\n    cmp esi,0ff000h     ; end of 1M, see if final 4K block was identity mapped\r\n    je @@scatdone      ; yes\r\n\r\n; start new region for final\r\n    inc ecx\r\n    mov esi, 100000h\r\n    jmp @@entryloop\r\n\r\n@@next2:\r\n    add esi, 1000h\r\n    mov edx, @GetPTEAddr(eax*4+?PAGETAB0)\r\n    and dx, 0F000h\r\n    cmp edx, esi\r\n    je @@samereg\r\n    inc ecx             ; have to move to next region/entry\r\n    mov esi,edx         ; update current physical address\r\n    jmp @@entryloop\r\n\r\n@@samereg:\r\n    cmp cx,[edi].EDDS.wNumAvail ; entries available count\r\n    jnc @@nextframe\r\n    add [edi+ecx*8+size EDDS].EDDSRG.dwSizeRg,1000h\r\n    jmp @@nextframe\r\n\r\n; calculate final region byte size or maximum allowed\r\n@@scatdone:\r\n    pop edx\r\n    cmp [edi].EDDS.wNumAvail,0  ; entries available count\r\n    jz @@noregions\r\n    and edx,0fffh\r\n    add [edi+size EDDS].EDDSRG.dwPhysAddr, edx\r\n    mov ebx,1000h\r\n    sub ebx,edx \r\n    add [edi+size EDDS].EDDSRG.dwSizeRg,ebx\r\n@@noregions:\r\n    xor ebx,ebx\r\n    mov edx,ebx\r\n    movzx ecx,[edi].EDDS.wNumUsed   ; number regions used (cannot be 0)\r\n    mov al,0  ; no error\r\n    cmp cx, [edi].EDDS.wNumAvail\r\n    jbe @@finalloop\r\n    mov cx,[edi].EDDS.wNumAvail ; only count up to minimum of available/used\r\n    mov al, VDSERR_NAVL_TOOSMALL\r\nif ?VDSDBG\r\n    and ecx, ecx\r\n    jz @@scatfail\r\nelse\r\n    jecxz @@scatfail \r\nendif\r\n@@finalloop:\r\n    mov esi,edx         ; keep previous, last known valid value\r\n    add edx,[edi+ebx*8+size EDDS].EDDSRG.dwSizeRg\r\n    inc ebx\r\n    loop @@finalloop\r\n    cmp al,0\r\n    jne @@scatfail      ; not all regions represented, update EDDS.dwSize\r\n\r\n    mov edx,[edi].EDDS.dwSize   ; update final region byte count\r\n    sub edx,esi\r\n    dec ebx\r\n    mov [edi+ebx*8+size EDDS].EDDSRG.dwSizeRg, edx\r\n    @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\r\n    clc\r\n    ret\r\n@@scatfail:\r\n    mov [edi].EDDS.dwSize,edx\r\n    @dprintf ?VDSDBG gt 0, <\"VDS scatlock exit, rc=%X, siz=%X used=%X\",10>, al, [edi].EDDS.dwSize, [edi].EDDS.wNumUsed\r\n    stc\r\n    ret\r\n\r\n;--- return PTEs\r\n;--- DL & 40h == 40h\r\n;--- eax == linear address\r\n;--- ebx == size in bytes\r\n;--- ecx == count entries used\r\n\r\n@@getptes:\r\n    push eax            ; save linear address\r\n\r\n    shr eax,12          ; convert linear start to PTE index\r\n    mov esi, @GetPTEAddr(?PAGETAB0)\r\n@@loop:\r\n    xor edx, edx\r\n    cmp eax, 400h       ; don't cross page table 0 border!\r\n    jnc @@noPTE\r\n    mov edx, [esi+eax*4]\r\n    and dx, 0F001h\r\n@@noPTE:    \r\n    cmp cx, [edi].EDDS.wNumAvail\r\n    jnc @@noPTEupdate\r\n    mov [edi+ecx*4+size EDDS].EDDSPT.dwPTE, edx\r\n@@noPTEupdate:\r\n    inc ecx\r\n    inc eax\r\n    sub ebx, 1000h\r\n    ja @@loop\r\n    mov [edi].EDDS.wNumUsed,cx\r\n    pop eax\r\n\r\n    and ah,0Fh\r\n    cmp cx, [edi].EDDS.wNumAvail\r\n    ja @@notenoughregions2\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EBX, ax\r\n    clc\r\n    ret\r\n@@notenoughregions2:\r\n    movzx ecx, [edi].EDDS.wNumAvail\r\n    shl ecx, 12\r\n    movzx eax,ax\r\n    sub ecx, eax\r\n    mov [edi].EDDS.dwSize, ecx\r\n    mov al, VDSERR_NAVL_TOOSMALL ;error \"NumAvail too small\"\r\n    stc\r\n    ret\r\n\r\nvds_scatterlock endp\r\n\r\n;--- int 4b, ax=8106h\r\n;--- DX=flags (6 & 7 may be set)\r\n;--- ES:DI=EDDS\r\n;--- errors VDSERR_REG_NOTLOCKED and VDSERR_DXRSVDBITSSET may happen!\r\n\r\nvds_scatterunlock proc\r\n\tmov al, VDSERR_DXRSVDBITSSET\t\t; v5.85: check added\r\n\ttest dx, not (VDSF_PTE or VDSF_NPPTE)\r\n\tjnz @@fail\r\n\t;--- todo: check if region was locked\r\n    clc\r\n    ret\r\n@@fail:\r\n\tstc\r\n\tret\r\nvds_scatterunlock endp\r\n\r\n;--- int 4b, ax=810B: disable automatic translation for a DMA channel\r\n;--- Client->BX=DMA channel number\r\n;--- DX=flags (all reserved and must be 0, hiword EDX is cleared)\r\n\r\nvds_disabletrans proc\r\n\r\n    mov ebx, [EBP].Client_Reg_Struc.Client_EBX\r\n    @dprintf ?VDSDBG gt 1, <\"VDS disable translation for channel %X\",10>, bx\r\n    cmp bx, MAXDMACHANNEL\r\n    mov al, VDSERR_INVAL_DMACHN  ;error \"invalid channel\"\r\n    jnc @@fail\r\n    mov al, VDSERR_DXRSVDBITSSET ;error \"reserved flags set in DX\"\r\n    and edx, edx\r\n    jnz @@fail\r\nif ?DMAPT\r\n    mov al, VDSERR_DISCNTOVFL    ;error \"disable count overflow\"\r\n    movzx ebx, bx\r\n    cmp [DmaChn+ebx*sizeof DMAREQ].cDisable,255\r\n    jz @@fail\r\n    inc [DmaChn+ebx*sizeof DMAREQ].cDisable\r\nendif\r\n    clc\r\n    ret\r\n@@fail:\r\n    stc\r\n    ret\r\n\r\nvds_disabletrans endp\r\n\r\n;--- int 4b, ax=810C: enable automatic translation for a DMA channel\r\n;--- Client->BX=DMA channel number\r\n;--- DX=flags (all reserved and must be 0, hiword EDX cleared)\r\n;--- out: Z if disablecnt is now zero\r\n\r\nvds_enabletrans proc\r\n\r\n    mov ebx, [EBP].Client_Reg_Struc.Client_EBX\r\n    @dprintf ?VDSDBG gt 1, <\"VDS enable translation for channel %X\",10>, bx\r\n    cmp bx, MAXDMACHANNEL\r\n    mov al, VDSERR_INVAL_DMACHN  ;error \"invalid channel\"\r\n    jnc @@fail\r\n    mov al, VDSERR_DXRSVDBITSSET ;error \"reserved flags set in DX\"\r\n    and edx, edx\r\n    jnz @@fail\r\nif ?DMAPT\r\n    mov al, VDSERR_DISCNTUNFL    ;error \"disable count underflow\"\r\n    movzx ebx, bx\r\n    cmp [DmaChn+ebx*sizeof DMAREQ].cDisable,0\r\n    jz @@fail\r\n    dec [DmaChn+ebx*sizeof DMAREQ].cDisable\r\n;--- v5.85: set ZF\r\n    setz al\r\n    shl al, 6   ; position of ZF in flags\r\n    and byte ptr [ebp].Client_Reg_Struc.Client_EFlags,not 40h  ;ZF=0\r\n    or  byte ptr [ebp].Client_Reg_Struc.Client_EFlags,al\r\nendif\r\n    clc\r\n    ret\r\n@@fail:\r\n    stc\r\n    ret\r\n\r\nvds_enabletrans endp\r\n\r\nVDS_Exit proc public\r\n    mov eax, [OldInt4B]\r\n    mov ds:[4Bh*4],eax\r\n    and byte ptr ds:[47Bh],not 20h\r\n    ret\r\nVDS_Exit endp\r\n\r\n.text$03 ends\r\n\r\n.text$04 segment\r\n\r\n;--- esi ->JEMMINIT\r\n\r\nVDS_Init proc public\r\n    mov eax, ds:[4Bh*4] ;vector may be 0000:0000\r\n    mov [OldInt4B],eax\r\n    cmp [esi].JEMMINIT.NoVDS, 0\r\n    jnz @@novds\r\n    mov eax, [dwRSeg]\r\n    shl eax, 16\r\n    mov al, [bBpTab]\r\nif BPTABLE.pInt4B\r\n    add al, (BPTABLE.pInt4B shr 2)\r\nendif\r\n    mov ds:[4Bh*4], eax\r\n    or byte ptr ds:[47Bh],20h\r\n@@novds:\r\n    ret\r\nVDS_Init endp\r\n\r\n.text$04 ENDS\r\n\r\nendif   ;?VDS\r\n\r\n    END\r\n"
  },
  {
    "path": "src/XMS.ASM",
    "content": "\r\n;--- XMS/A20 implementation\r\n;--- Public Domain\r\n;--- to be assembled with JWasm or Masm v6.1+\r\n\r\n    .386\r\n    .model FLAT\r\n    option dotname\r\n\r\n    include jemm.inc        ;common declarations\r\n    include jemm32.inc      ;declarations for Jemm32\r\n    include debug32.inc\r\n\r\n;--- publics/externals\r\n\r\n    include extern32.inc\r\n\r\n?MERGE0HDL equ 1\t;std=0, 1=try to merge handles even if hdl to free has size 0\r\n?HINF_MSCOMP equ 1\t;std=1, 1=full MS compatibility for AH=0Eh\r\n\r\n.text$01 SEGMENT\r\n\r\n;-- Jemm's real-mode part will call XMS A20 local disable\r\n;-- after the monitor's initialization. With wA20==1 this might\r\n;-- disable the (emulated) A20. FreeDOS might have problems with\r\n;-- this if MS Himem is used. wA20==2 seems to avoid these problems.\r\n\r\nif ?A20XMS\r\n;wA20           dw 1    ; local XMS A20 count + global A20 flag\r\nwA20            dw 2\r\nendif\r\n\r\nif ?INTEGRATED\r\nx2max           dw  -1\r\nhmamin          dw  0\r\nhma_used        db  0\r\n if ?XMS35\r\nxms_version     dw -1\r\n endif\r\nendif\r\n\r\nif ?A20PORTS or ?A20XMS\r\nbNoA20          DB  0\r\nbA20Stat        DB  1   ; default is A20 enabled\r\nendif\r\n\r\nife ?DYNTRAPP60\r\nif ?A20PORTS\r\nbLast64         DB  0\r\nendif\r\nendif\r\n\r\n        align 4\r\n\r\nXMS_Handle_Table XMS_HANDLETABLE <0,0,0,0>\r\n\r\nife ?INTEGRATED\r\nXMSCtrlHandle dw 0\r\nelse\r\nA20Index db 0\r\nendif\r\n\r\n.text$01 ends\r\n\r\n.text$03 segment\r\n\r\n;--- registers EAX, ECX and ESI dont hold client values!\r\n\r\nxms_handler proc public\r\n    call Simulate_Far_Ret                       ; emulate a RETF in v86, modifies EDX\r\n    mov eax,[ebp].Client_Reg_Struc.Client_EAX   ; restore EAX\r\n    mov edx,[ebp].Client_Reg_Struc.Client_EDX   ; restore EDX\r\nif ?INTEGRATED\r\n    mov esi,[ebp].Client_Reg_Struc.Client_ESI\r\n    mov ecx,[ebp].Client_Reg_Struc.Client_ECX\r\n    @dprintf ?EMBDBG, <\"xms_handler: ax=%X edx=%X\",10>, ax, edx\r\n    cmp ah,0fh          ; 00-0F?\r\n    jbe @@ok1\r\n    mov al,ah\r\n    shr al,1\r\n    cmp al,88h/2        ; 88-89?\r\n    jz @@ok2\r\n    cmp al,8Eh/2        ; 8E-8F?\r\n    jz @@ok3\r\n if ?XMS35\r\n    cmp al,0C8h/2       ; C8-C9?\r\n    jz @@ok4\r\n    cmp ah,0CBh         ; CB?\r\n    jz @@ok5\r\n    cmp ah,0CCh         ; CC?\r\n    jz @@ok5\r\n endif\r\n    cmp ah,12h\r\n    jbe umb_handler\r\n    xor ax,ax           ; everything else fails\r\n    mov bl,XMS_NOT_IMPLEMENTED\r\n    jmp @@dispatcher_end\r\n if ?XMS35\r\n@@ok5:\r\n\tdec ah              ; CB-CC -> CA-CB\r\n@@ok4:\r\n\tsub ah,0C8h-14h\t\t; C8-CC -> 14-17\r\n\tjmp @@ok1\r\n endif\r\n@@ok3:\r\n    sub ah,4            ; 8E-8F -> 8A-8B\r\n@@ok2:\r\n    sub ah, 88h-10h     ; 88-8B -> 10-13\r\n@@ok1:\r\n    cld\r\n    movzx edi,ah\r\n    call [xms_table+edi*4]\r\n@@dispatcher_end:\r\n    mov [ebp].Client_Reg_Struc.Client_EBX, ebx\r\n    mov [ebp].Client_Reg_Struc.Client_EDX, edx\r\n    mov [ebp].Client_Reg_Struc.Client_ECX, ecx\r\n    mov [ebp].Client_Reg_Struc.Client_EAX, eax\r\n    ret\r\n\r\n    align 4\r\n\r\nxms_table label dword  \r\n    dd xms_get_version          ;00 - AX, BX, DX\r\n    dd xms_request_hma          ;01 - AX, BL=0/E\r\n    dd xms_release_hma          ;02 - AX, BL=0/E\r\n    dd xms_global_enable_a20    ;03 - AX, BL=0/E?\r\n    dd xms_global_disable_a20   ;04 - AX, BL=0/E?\r\n    dd xms_local_enable_a20     ;05 - AX, BL=0/E?\r\n    dd xms_local_disable_a20    ;06 - AX, BL=0/E?\r\n    dd xms_query_a20            ;07 - AX, BL=0/E\r\n    dd xms_query_free_mem       ;08 - AX, DX, BL=0/E\r\n    dd xms_alloc_emb            ;09 - AX, DX, BL=0/E (MS Himem, RBIL: BL=E)\r\n    dd xms_free_emb             ;0A - AX, BL=0/E (MS Himem, RBIL: BL=E)\r\n    dd xms_move_emb             ;0B - AX, BL=E\r\n    dd xms_lock_emb             ;0C - AX, DX, BX\r\n    dd xms_unlock_emb           ;0D - AX, BL=0/E (MS Himem, RBIL: BL=E)\r\n    dd xms_get_handle_info      ;0E - AX, DX, BX\r\n    dd xms_realloc_emb          ;0F - AX, BL=0/E (MS Himem, RBIL: BL=E)\r\n\r\n    dd xms_ext_query_free_mem   ; 88 10\r\n    dd xms_ext_alloc_emb        ; 89 11\r\n    dd xms_ext_get_handle_info  ; 8E 12\r\n    dd xms_ext_realloc_emb      ; 8F 13\r\n if ?XMS35\r\n    dd xms_sext_query_free_mem  ; C8 14\r\n    dd xms_sext_alloc_emb       ; C9 15\r\n    dd xms_move_emb             ; CB 16\r\n    dd xms_sext_lock_emb        ; CC 17\r\n endif\r\nelse\r\n  if ?A20XMS\r\n    cmp ah,10h\r\n    jae umb_handler\r\n    call XMS_HandleA20 ; modifies EAX, CX, BL\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, ax\r\n    and ax,ax\r\n    jnz @@a20exit_noerror\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_EBX, bl\r\n@@a20exit_noerror:\r\n    ret\r\n  else\r\n    jmp umb_handler\r\n  endif\r\nendif\r\n\r\nxms_handler endp\r\n\r\nif ?INTEGRATED\r\n\r\nxms_get_version proc  ;ah=0\r\n if ?XMS35\r\n    mov ax,xms_version\r\n else\r\n    mov ax,INTERFACE_VER\r\n endif\r\n    mov bx,DRIVER_VER\r\n    mov dx,1                    ; HMA is always available\r\n    ret\r\nxms_get_version endp\r\n\r\nxms_request_hma proc  ;ah=1\r\n    xor ax,ax\r\n    cmp [hma_used],al           ; is HMA already used?\r\n    mov bl,XMS_HMA_IN_USE\r\n    jnz @@exit\r\n    cmp dx,[hmamin]             ; is request big enough?\r\n    mov bl,XMS_HMAREQ_TOO_SMALL\r\n    jb @@exit\r\n    inc eax\r\n    mov [hma_used],al           ; assign HMA to caller\r\n    mov bl,0 \r\n@@exit:\r\n    ret\r\nxms_request_hma endp\r\n\r\nxms_release_hma proc  ;ah=2\r\n    xor ax,ax\r\n    cmp [hma_used],al           ; is HMA used?\r\n    mov bl,XMS_HMA_NOT_USED\r\n    jz @@exit\r\n    mov [hma_used],al           ; now release it\r\n    inc eax\r\n    mov bl,0\r\n@@exit:\r\n    ret\r\nxms_release_hma endp\r\n\r\nxms_query_a20 proc  ;ah=7\r\n    movzx ax,[bA20Stat]\r\n    mov bl,0\r\n    ret\r\n    align 4\r\nxms_query_a20 endp\r\n\r\n;--- check if handle in DX is valid\r\n;--- let ESI point to handle\r\n;--- this function does NOT return if handle is invalid\r\n\r\nxms_check_handle_ex:\r\n    mov esi,edx\r\nxms_check_handle proc\t;<--- check handle in SI\r\n    push edx\r\n    push eax\r\n\r\n    movzx esi,si\r\n    add esi,[dwRes]\r\n    jc @@no_valid_handle\r\n    mov eax,esi\r\n    sub eax,[XMS_Handle_Table.xht_pArray]\r\n    jb @@no_valid_handle\r\n    xor edx,edx\r\n\r\n    push ebx\r\n    mov ebx,size XMS_HANDLE\r\n    div ebx\r\n    pop ebx\r\n\r\n    or edx,edx\r\n    jnz @@no_valid_handle\r\n    movzx edx,[XMS_Handle_Table.xht_numhandles]\r\n\r\n    cmp eax,edx\r\n    jae @@no_valid_handle\r\n\r\n    cmp [esi].XMS_HANDLE.xh_flags,XMSF_USED   ; is it in use ??\r\n    jne @@no_valid_handle\r\n    pop eax\r\n    pop edx\r\n    ret\r\n@@no_valid_handle:\r\n    pop eax\r\n    pop edx\r\n    add esp,4   ;skip return address\r\n    xor ax,ax\r\n    mov bl,XMS_INVALID_HANDLE\r\n    stc\r\n    ret\r\n    align 4\r\n\r\nxms_check_handle endp\r\n\r\n;--- allocate an unused handle in EBX\r\n;--- C if no handle available\r\n\r\nxms_alloc_handle proc\r\n    movzx ecx,[XMS_Handle_Table.xht_numhandles]    ; check all handles\r\n    mov ebx,[XMS_Handle_Table.xht_pArray]\r\n@@nextitem:\r\n    cmp [ebx].XMS_HANDLE.xh_flags,XMSF_INPOOL\r\n    jz @@found_handle          ; found a blank handle\r\n    add ebx,size XMS_HANDLE     ; skip to next handle\r\n    loop @@nextitem\r\n    stc                         ; no free block found, error\r\n@@found_handle:\r\n    ret\r\nxms_alloc_handle endp\r\n\r\n;--- AH=09H: alloc an EMB, size DX kB\r\n;--- out: AX=0/1, BL=0 or error code, DX=handle or 0\r\n\r\nxms_alloc_emb proc\r\n    @dprintf ?EMBDBG, <\"xms_alloc_emb\",10>\r\n    push edx\r\n    movzx edx,dx  ; extend alloc request to 32-bits\r\n    jmp @@xms_alloc2\r\n\r\n;-- 32-bit entry for function AH=89h: alloc EMB, size EDX kB\r\n\r\nxms_ext_alloc_emb::\r\n if ?XMS35\r\nxms_sext_alloc_emb::;<--- entry, ah=15h\r\n endif\r\n    push edx\r\n    @dprintf ?EMBDBG, <\"xms_ext_alloc_emb\",10>\r\n\r\n@@xms_alloc2:\r\n    push ecx\r\n if 0\t;v5.80 check for size 0 after scan\r\n    and edx,edx              ; a request for 0 kB needs no free mem\r\n    jz @@nullhandle\r\n endif\r\n    movzx ecx,[XMS_Handle_Table.xht_numhandles]\r\n    mov edi,[XMS_Handle_Table.xht_pArray] \r\n@@nextitem:\r\n    cmp [edi].XMS_HANDLE.xh_flags,XMSF_FREE\r\n    jnz @@skipitem\r\n;--- filter blocks \r\n if ?XMS35\r\n\tcmp ah,15h\t;15h is xms_sext_alloc_emb #\r\n\tjnz @F\r\n\ttest word ptr [edi].XMS_HANDLE.xh_baseK+2,0FFC0h\r\n\tjnz sext_mem\r\n\tjmp @@skipitem\r\n@@:\r\n\ttest word ptr [edi].XMS_HANDLE.xh_baseK+2,0FFC0h\r\n\tjnz @@skipitem\r\nsext_mem:\r\n endif\r\n    cmp edx,[edi].XMS_HANDLE.xh_sizeK   ; check if it's large enough\r\n    jbe @@found_block\r\n@@skipitem:\r\n    add edi,size XMS_HANDLE   ; skip to next handle\r\n    loop @@nextitem\r\n if 1\t;v5.80 check for size 0 after scan\r\n    and edx,edx              ; a request for 0 kB needs no free mem\r\n    jz @@nullhandle\r\n endif\r\n    mov bl,XMS_ALL_MEM_ALLOCATED\r\n@@alloc_failed:\r\n    pop ecx\r\n    pop edx\r\n    xor dx,dx    ;v5.80: return DX=0 if alloc fails (XMS spec)\r\n    xor ax,ax\r\n    ret\r\n@@nullhandle:\r\n    push ebx\r\n    call xms_alloc_handle    ; get a free handle in EBX\r\n    mov edi,ebx\r\n    pop ebx\r\n    mov bl,XMS_NO_HANDLE_LEFT\r\n    jc @@alloc_failed\r\n    xor ax,ax                ; set ZF to skip code below\r\n\r\n@@found_block:\r\n    mov word ptr [edi].XMS_HANDLE.xh_flags,XMSF_USED ;clear locks also\r\n    jz @@perfect_fit2               ; if it fits perfectly, go on\r\n    push ebx\r\n    call xms_alloc_handle           ; get a free handle in BX\r\n    jc  @@perfect_fit               ; no more handles, use all mem left\r\n    mov esi,[edi].XMS_HANDLE.xh_sizeK\r\n    mov [edi].XMS_HANDLE.xh_sizeK,edx\r\n    sub esi,edx                     ; calculate resting memory\r\n    add edx,[edi].XMS_HANDLE.xh_baseK   ; calc new base address of free block \r\n    mov word ptr [ebx].XMS_HANDLE.xh_flags,XMSF_FREE\r\n    mov [ebx].XMS_HANDLE.xh_baseK,edx\r\n    mov [ebx].XMS_HANDLE.xh_sizeK,esi\r\n@@perfect_fit:\r\n    pop ebx\r\n@@perfect_fit2:\r\n    pop ecx\r\n    pop edx\r\n    @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]\r\n    sub edi,[dwRes]\r\n    mov dx,di                       ; return handle in DX\r\n    mov bl,0\r\n    mov ax,1\r\n    @dprintf ?EMBDBG, <\"xms_alloc_emb ok, handle=%X\",10>, dx\r\n    ret\r\nxms_alloc_emb endp\r\n\r\n if ?XMS35\r\n;******************************************************************************\r\n; query free super-extended memory\r\n; In:   AH=C8h\r\n; Out:  EAX=size of largest free super-extended block in kbytes\r\n;   EDX=total amount of free super-extended block in kbytes\r\n;   BL=0 if ok\r\n;   BL=080h -> function not implemented\r\n;   BL=081h -> VDISK is detected\r\n;   BL=0a0h -> all super-extended memory is allocated\r\n\r\nxms_sext_query_free_mem proc\r\n\r\n\txor eax,eax \t; contains largest free block\r\n\txor edx,edx \t; contains total free block\r\n\r\n\tpush ecx\r\n\tpush ebx\r\n\tmovzx ecx,[XMS_Handle_Table.xht_numhandles]\r\n\tmov ebx,[XMS_Handle_Table.xht_pArray]\r\nnextitem:\r\n\ttest [ebx].XMS_HANDLE.xh_flags,XMSF_FREE\t; check if flagged free or in use\r\n\tje @F\r\n\r\n;--- filter blocks below 4 GB\r\n;--- 00000000-003fffffh\r\n\ttest word ptr [ebx].XMS_HANDLE.xh_baseK+2, 0ffc0h\r\n\tjz @F\r\n\r\n\tmov esi, [ebx].XMS_HANDLE.xh_sizeK\r\n\tadd edx, esi\r\n\tcmp esi, eax\t\t\t  ; check if larger than largest\r\n\tjbe @F\r\n\tmov eax,esi \t\t\t  ; larger, update\r\n@@:\r\n\tadd ebx,sizeof XMS_HANDLE\r\n\tloop nextitem\r\n\tpop ebx\r\n\tpop ecx\r\n\tmov bl,0\r\n\tand edx,edx\r\n\tjnz @F\r\n\tmov bl,XMS_ALL_MEM_ALLOCATED\r\n@@:\r\n\tret \t\t\t; success\r\n\r\nxms_sext_query_free_mem endp\r\n endif\r\n\r\n;--- AH=88h: get free mem info\r\n;--- out: eax=max free block\r\n;---      edx=total free\r\n;---      ecx=highest phys addr\r\n;---      bl=00 (success) or error code\r\n\r\nxms_ext_query_free_mem proc\r\n\r\n    @dprintf ?EMBDBG, <\"xms_ext_query_free_mem\",10>\r\n    xor edi,edi     ; highest ending address of any memory block\r\n    xor eax,eax     ; contains largest free block\r\n    xor edx,edx     ; contains total free XMS\r\n\r\n    push ebx\r\n    movzx ecx,[XMS_Handle_Table.xht_numhandles]\r\n    mov ebx,[XMS_Handle_Table.xht_pArray]\r\n@@nextitem:\r\n    test [ebx].XMS_HANDLE.xh_flags,XMSF_USED or XMSF_FREE   ; check if flagged free or in use\r\n    je  @@skipitem\r\n\r\n if ?XMS35\r\n;--- filter blocks beyond 4 GB\r\n;--- 00000000-003fffffh\r\n\ttest word ptr [ebx].XMS_HANDLE.xh_baseK+2, 0ffc0h\r\n\tjnz @@skipitem\r\n endif\r\n\r\n    mov esi, [ebx].XMS_HANDLE.xh_sizeK\r\n    cmp  [ebx].XMS_HANDLE.xh_flags,XMSF_FREE\r\n    jnz @@notfree\r\n    add edx, esi\r\n    cmp esi, eax              ; check if larger than largest\r\n    jbe @@notfree\r\n    mov eax,esi               ; larger, update\r\n@@notfree:\r\n    add esi,[ebx].XMS_HANDLE.xh_baseK\r\n    cmp edi,esi\r\n    jae @@skipitem\r\n    mov edi,esi             ; higher address, update\r\n@@skipitem:\r\n    add ebx,size XMS_HANDLE\r\n    loop @@nextitem\r\n    pop ebx\r\n    mov bl,0\r\n    and edx,edx\r\n    jnz @@freeblockexists\r\n    mov bl,XMS_ALL_MEM_ALLOCATED\r\n@@freeblockexists:\r\n    mov ecx,edi     ; highest address to ecx return value\r\n    shl ecx,10      ; convert to bytes\r\n    dec ecx         ; relative zero\r\n    ret             ; success\r\n\r\nxms_ext_query_free_mem endp    \r\n\r\n;--- AH=08h: get free mem info\r\n;--- out: AX=largest free block in kBytes\r\n;---      DX=total amount of free XMS in kBytes\r\n;---      BL=00 (success) or error code\r\n\r\nxms_query_free_mem proc\r\n           \r\n    @dprintf ?EMBDBG, <\"xms_query_free_mem\",10>\r\n\r\n    push ecx\r\n    push eax\r\n    push edx\r\n\r\n    call xms_ext_query_free_mem \r\n\r\n    ; the call returned:\r\n    ;   EAX=size of largest free XMS block in kbytes\r\n    ;   ECX=highest ending address of any memory block (not used)\r\n    ;   EDX=total amount of free XMS in kbytes\r\n\r\n    movzx ecx, [x2max]\r\n    cmp edx,ecx             ; dx = min(edx,0ffff | 7fff)\r\n    jb @@edx_not_larger\r\n    mov edx,ecx\r\n@@edx_not_larger:\r\n    cmp eax,ecx             ; ax = min(eax,0ffff | 7fff)\r\n    jb @@eax_not_larger\r\n    mov eax,ecx\r\n@@eax_not_larger:\r\n                ; use LoWords only\r\n    mov [esp+0],dx\r\n    mov [esp+4],ax\r\n    pop edx\r\n    pop eax\r\n    pop ecx\r\n    ret\r\n\r\nxms_query_free_mem endp\r\n\r\n;--- AH=0A, free emb in DX\r\n\r\nxms_free_emb proc\r\n\r\n    @dprintf ?EMBDBG, <\"xms_free_emb, dx=%X\",10>, dx\r\n\r\n    call xms_check_handle_ex     ; check if DX holds a \"used\" handle, set ESI\r\n    mov bl,XMS_BLOCK_LOCKED\r\n    xor ax,ax\r\n    cmp [esi].XMS_HANDLE.xh_locks,0     ; is the block locked?\r\n    jnz @@exit\r\n    push eax\r\n    push ebx\r\n    push ecx\r\n    push edx\r\n                                    ; see if there are blocks to merge\r\n    mov eax,[esi].XMS_HANDLE.xh_baseK   ; get base address\r\n    mov edx,[esi].XMS_HANDLE.xh_sizeK\r\n    mov edi, eax                    ; base in edi\r\n    add eax, edx                    ; end-address in EAX\r\n ife ?MERGE0HDL\r\n    mov cl, XMSF_FREE\r\n    and edx, edx\r\n    jnz @F\r\n    mov cl, XMSF_INPOOL\r\n@@:\r\n    mov [esi].XMS_HANDLE.xh_flags,cl;mark this block as free (or inpool if size is 0)\r\n    jz @@done\r\n endif\r\n\r\n;--- now scan the handle array for successor/predecessor\r\n\r\n    movzx ecx,[XMS_Handle_Table.xht_numhandles]\r\n    mov ebx,[XMS_Handle_Table.xht_pArray] \r\n@@nextitem:\r\n    cmp [ebx].XMS_HANDLE.xh_flags,XMSF_FREE\r\n    jnz @@skipitem\r\n    mov edx,[ebx].XMS_HANDLE.xh_baseK\r\n    cmp eax, edx                    ; is successor also free?\r\n    je @F\r\n    add edx,[ebx].XMS_HANDLE.xh_sizeK\r\n    cmp edi, edx                    ; is predecessor free?\r\n    jne @@skipitem\r\n@@:\r\n;--- predecessor/successor in EBX\r\n    cmp ebx,esi\r\n    jbe @F\r\n    xchg ebx,esi                    ; merge into the \"lower\" handle and free the \"higher\" handle\r\n@@:\r\n    xor edx, edx                    ; merge 2 handles, then free one\r\n    xchg edx, [esi].XMS_HANDLE.xh_sizeK\r\n    add [ebx].XMS_HANDLE.xh_sizeK, edx  ;new size is sum(hdl1.size,hdl2.size)\r\n    xor edx, edx\r\n    xchg edx, [esi].XMS_HANDLE.xh_baseK\r\n    cmp edx, [ebx].XMS_HANDLE.xh_baseK\r\n    ja @F\r\n    mov [ebx].XMS_HANDLE.xh_baseK, edx  ;new base is min(hdl1.base,hdl2.base)\r\n@@:\r\n    mov [esi].XMS_HANDLE.xh_flags,XMSF_INPOOL\r\n    mov esi,ebx\r\n@@skipitem:\r\n    add ebx,sizeof XMS_HANDLE\r\n    loop @@nextitem\r\n if ?MERGE0HDL\r\n    mov cl, XMSF_FREE\r\n    cmp [esi].XMS_HANDLE.xh_sizeK,0\r\n    jnz @F\r\n    mov cl,XMSF_INPOOL\r\n@@:\r\n    mov [esi].XMS_HANDLE.xh_flags,cl\r\n endif\r\n@@done:\r\n    pop edx\r\n    pop ecx\r\n    pop ebx\r\n    pop eax\r\n    inc eax\r\n    mov bl,0\r\n@@exit:\r\n    ret\r\n\r\nxms_free_emb endp\r\n\r\n;--- AH=0C, lock EMB in DX, return base in DX:BX\r\n\r\nxms_lock_emb proc\r\n\r\n    @dprintf ?EMBDBG, <\"xms_lock_emb enter, dx=%X\",10>, dx\r\n\r\n    call xms_check_handle_ex    ; check if DX holds \"used\" handle\r\n    xor ax,ax                   ; flag error \r\n    inc [esi].XMS_HANDLE.xh_locks   ; increase lock counter\r\n    jz @@lock_error\r\n    mov esi,[esi].XMS_HANDLE.xh_baseK\r\n    shl esi,10                  ; calculate linear address\r\n    push esi\r\n    pop bx\r\n    pop dx\r\n    inc eax\r\n    ret\r\n@@lock_error::\r\n    dec [esi].XMS_HANDLE.xh_locks\r\n    mov bl,XMS_LOCK_COUNT_OVERFLOW\r\n    ret\r\nxms_lock_emb endp\r\n\r\n if ?XMS35\r\n;******************************************************************************\r\n; locks a (super-extended) MB\r\n; In:   AH=0cch\r\n;   DX=XMS handle to be locked\r\n; Out:  AX=1 if block is locked\r\n;     EDX:EBX=64-bit linear address of block\r\n;   AX=0 if not successful\r\n;     BL=080h -> function not implemented\r\n;     BL=081h -> VDISK is detected\r\n;     BL=0a2h -> handle is invalid\r\n;     BL=0ach -> lock count overflow\r\n;     BL=0adh -> lock fails\r\n\r\nxms_sext_lock_emb proc\r\n\r\n\t@dprintf ?EMBDBG, <\"xms_sext_lock_emb enter\",10>\r\n\tcall xms_check_handle_ex\t; check if dx holds \"used\" handle\r\n    xor ax,ax                   ; flag error \r\n\tinc [esi].XMS_HANDLE.xh_locks; increase lock counter\r\n\tjz @@lock_error\r\n\tmov ebx,[esi].XMS_HANDLE.xh_baseK\r\n\tmov edx,ebx\r\n\tshl ebx,10\t\t\t\t\t; calculate linear address 0-31\r\n\tshr edx,22\t\t\t\t\t; calculate linear address 32-41\r\n\tinc eax\r\n\tret\r\nxms_sext_lock_emb endp\r\n endif\r\n\r\n;--- AH=0D, unlock EMB in DX\r\n\r\nxms_unlock_emb proc\r\n\r\n    @dprintf ?EMBDBG, <\"xms_unlock_emb enter\",10>\r\n    call xms_check_handle_ex       ; check if DX holds \"used\" handle\r\n    xor ax,ax\r\n    cmp [esi].XMS_HANDLE.xh_locks,al; check if block is locked\r\n    jz @@is_notlocked\r\n    dec [esi].XMS_HANDLE.xh_locks   ; decrease lock counter\r\n    inc eax\r\n    mov bl,0\r\n    ret\r\n@@is_notlocked:\r\n    mov bl,XMS_BLOCK_NOT_LOCKED\r\n    ret\r\nxms_unlock_emb endp\r\n\r\n;--- AH=8E, dx=handle\r\n;--- out: bh=lock count, cx=free handles, edx=size in kB\r\n\r\nxms_ext_get_handle_info proc\r\n\r\n    @dprintf ?EMBDBG, <\"xms_ext_get_handle_info enter, dx=%X\",10>, dx\r\n\r\n    call xms_check_handle_ex; check if handle in DX is valid (=\"used\")\r\n    xor cx,cx               ; reset free handle counter\r\n    xor ax,ax\r\n    movzx edx,[XMS_Handle_Table.xht_numhandles]\r\n    mov edi,[XMS_Handle_Table.xht_pArray]\r\n@@nextitem:\r\n    cmp [edi].XMS_HANDLE.xh_flags,XMSF_INPOOL\r\n    setz al\r\n    add cx,ax\r\n    add edi,size XMS_HANDLE\r\n    dec edx\r\n    jnz @@nextitem\r\n    mov bh,[esi].XMS_HANDLE.xh_locks     ; store lock count\r\n    mov edx,[esi].XMS_HANDLE.xh_sizeK    ; store block size\r\n;   mov bl,0   ;set BL on exit?\r\n    mov al,1\r\n    ret\r\nxms_ext_get_handle_info endp\r\n\r\n;--- in: ah=0Eh\r\n;--- in: dx=handle\r\n;--- out: ax=0|1\r\n;--- out: if ax==1, dx=size in kB\r\n;--- out: if ax==1, bl=free handle count\r\n;--- out: if ax==1, bh=block lock count\r\n\r\nxms_get_handle_info proc\r\n\r\n    push ecx\r\n    push edx\r\n    @dprintf ?EMBDBG, <\"xms_get_handle_info enter, dx=%X\",10>, dx\r\n    \r\n    call xms_ext_get_handle_info ;modifies edx, bx, cx; ax=0|1\r\n    or ax,ax\r\n    jz @@get_handle_info_err    ; if invalid handle (bl=A2h then)\r\n\r\n;--- in cx, free handles\r\n    cmp ch,0                    ; bl = min(cx,0xff)\r\n    jz @@handle_count_ok\r\n    mov cl,0ffh\r\n@@handle_count_ok:\r\n    mov bl,cl\r\n\r\n    cmp edx,010000h\r\n    jb @@handle_size_ok\r\n if ?HINF_MSCOMP\r\n    dec ax\r\n    mov bl,XMS_INVALID_HANDLE\r\n    jmp @@get_handle_info_err\r\n else\r\n    mov dx,0ffffh               ; dx = min(edx,0xffff);\r\n endif\r\n@@handle_size_ok:\r\n    mov [esp],dx\r\n@@get_handle_info_err:\r\n    pop edx\r\n    pop ecx\r\n    ret\r\n\r\nxms_get_handle_info endp\r\n\r\n;--- realloc emb\r\n;--- dx=handle, ebx=new size\r\n;--- modifies esi, edi, ax, bl\r\n\r\nxms_ext_realloc_emb proc public\r\n\r\n    @dprintf ?EMBDBG, <\"xms_ext_realloc_emb enter, dx=%X ebx=%X esp=%X\",10>, dx, ebx, esp\r\n    call xms_check_handle_ex   ; dx == \"used\" handle?\r\n    push edx\r\n\r\n; fail if block is locked\r\n    cmp [esi].XMS_HANDLE.xh_locks,0\r\n    jne @@ext_xms_locked\r\n\r\n    mov edx, ebx\r\n if 1;v5.80\r\n;--- find the successor.\r\n\tmovzx ecx,[XMS_Handle_Table.xht_numhandles]\r\n\tmov edi,[XMS_Handle_Table.xht_pArray]\r\n\tmov eax,[esi].XMS_HANDLE.xh_sizeK\r\n\tadd eax,[esi].XMS_HANDLE.xh_baseK\r\nnextitem:\r\n\ttest [edi].XMS_HANDLE.xh_flags,XMSF_FREE\t;scan \"free embs\" only\r\n\tjz skipitem\r\n\tcmp eax,[edi].XMS_HANDLE.xh_baseK\t;successor?\r\n\tjnz skipitem\r\n\tmov eax,[esi].XMS_HANDLE.xh_sizeK\r\n\tadd eax,[edi].XMS_HANDLE.xh_sizeK\t;get the total size\r\n\tcmp edx,eax                         ;new size > total size?\r\n\tja @@ext_growing                    ;then the handle can't grow, have to copy...\r\n\tsub edx,[esi].XMS_HANDLE.xh_sizeK\t;get the size which is additionally needed (might be < 0!)\r\n\tmov [esi].XMS_HANDLE.xh_sizeK, ebx\r\n\tadd [edi].XMS_HANDLE.xh_baseK, edx\r\n\tsub [edi].XMS_HANDLE.xh_sizeK, edx\r\n\tjnz @@ext_grow_success              ;remaining size > 0?\r\n\tmov [edi].XMS_HANDLE.xh_flags, XMSF_INPOOL\t;no, so free the handle\r\n\tmov [edi].XMS_HANDLE.xh_baseK, 0\r\n\tjmp @@ext_grow_success\r\nskipitem:\r\n\tadd edi,sizeof XMS_HANDLE\r\n\tloop nextitem\r\n endif\r\n    cmp ebx,[esi].XMS_HANDLE.xh_sizeK\r\n    jbe @@ext_shrink_it\r\n\r\n@@ext_growing:\r\n; growing, try to allocate a new block\r\n\r\n    mov ah,11h              ;11h is xms_ext_alloc_emb #\r\n    test word ptr [esi].XMS_HANDLE.xh_baseK+2, 0FFC0h\r\n    jz @F\r\n    mov ah,15h              ;15h is for super-extended memory\r\n;\ttest word ptr [esi].XMS_HANDLE.xh_sizeK+2, 0FFC0h   ;old size >= 4G\r\n;\tjnz @@ext_failed                                    ;then fail for now\r\n@@:\r\n    call xms_ext_alloc_emb  ;get a new handle in DX, size EDX\r\n    and ax,ax\r\n    jz @@ext_failed\r\n\r\n; got new block, copy info from old block to new block\r\n\r\n\r\n; transfer old handle data to new location\r\n; since the block may be > 4G and max amount to copy is 4G-2,\r\n; the transfer is done with chunks of max. 4G - 1K.\r\n\r\nMAXCHUNK equ 4096 * 1024 - 1\r\n\r\n    movzx esi,word ptr [esp] ; get old handle\r\n    xor edi,edi\r\n if ?XMS35\r\n    push edi            ; bits 32-39 of src/dst.offset (pushed as dword for alignment reasons)\r\n endif\r\n    push edi            ; dst.offset\r\n    push dx             ; dst.handle\r\n    push edi            ; src.offset\r\n    push si             ; src.handle\r\n    add esi,[dwRes]\r\n if ?XMS35\r\n    mov ecx,[esi].XMS_HANDLE.xh_sizeK\r\nnextchk:\r\n    mov edi, ecx\r\n    cmp ecx, MAXCHUNK   ;remaining more than max.?\r\n    jb @F\r\n    mov edi, MAXCHUNK   ;load edi with 4G-1K\r\n@@:\r\n else\r\n    mov edi,[esi].XMS_HANDLE.xh_sizeK\r\n endif\r\n    shl edi, 10         ; K to byte\r\n    push edi            ; length\r\n    mov esi, esp\r\n if ?XMS35\r\n    mov ah, 16h         ; always use superext move\r\n endif\r\n    call xms_move_emb_ex\r\n if ?XMS35\r\n    mov esi, esp\r\n    pop edi                                 ; get size of current chunk in edi\r\n    add [esi].XMS_MOVEX.src_offset, edi     ; adjust src and dest offsets\r\n    adc [esi].XMS_MOVEX.src_hi, 0           ; including bits 32-39\r\n    add [esi].XMS_MOVEX.dest_offset, edi\r\n    adc [esi].XMS_MOVEX.dest_hi, 0\r\n    shr edi, 10\r\n    sub ecx, edi\r\n    jnz nextchk\r\n    add esp, sizeof XMS_MOVEX-2\r\n else\r\n    add esp, sizeof XMS_MOVE\r\n endif\r\n    movzx esi,word ptr [esp]\r\n    add esi,[dwRes]\r\n\r\n; swap handle data so handle pointers remain valid\r\n; handle data is 10 bytes long\r\n\r\n    push edx\r\n    movzx edi,dx\r\n    add edi,[dwRes]\r\n    mov edx,[esi+0]\r\n    xchg edx,[edi+0]\r\n    mov [esi+0],edx\r\n    mov edx,[esi+4]\r\n    mov ax,[esi+8]\r\n    xchg edx,[edi+4]\r\n    xchg ax,[edi+8]\r\n    mov [esi+4],edx\r\n    mov [esi+8],ax\r\n    pop edx\r\n\r\n; free newly allocated handle in DX with old handle data in it\r\n\r\n    call xms_free_emb\r\n    jmp @@ext_grow_success\r\n\r\n@@ext_no_xms_handles_left:\r\n    pop ebx\r\n    mov bl,XMS_NO_HANDLE_LEFT\r\n    jmp @@ext_failed\r\n\r\n@@ext_xms_locked:\r\n    mov bl,XMS_BLOCK_LOCKED\r\n\r\n@@ext_failed:\r\n    pop edx\r\n    xor ax,ax\r\n    ret\r\n\r\n;--- shrink a memory block\r\n;--- esi = handle data\r\n;--- edx,ebx = new size\r\n\r\n@@ext_shrink_it:\r\n    mov edi,[esi].XMS_HANDLE.xh_sizeK    ; get old size\r\n    sub edi, edx                     ; calculate what's left over\r\n    jz @@ext_dont_need_handle        ; jump if we don't need another handle\r\n    push ebx\r\n    call xms_alloc_handle            ; alloc a handle in EBX (edx not modified)\r\n    jc @@ext_no_xms_handles_left     ; exit if no more handles\r\n    mov [esi].XMS_HANDLE.xh_sizeK, edx ;set new (reduced) size\r\n    add edx,[esi].XMS_HANDLE.xh_baseK; calculate base of new \"free\" block\r\n    mov [ebx].XMS_HANDLE.xh_baseK,edx\r\n    mov [ebx].XMS_HANDLE.xh_sizeK,edi\r\n if 1;v5.80\r\n;--- if this branch is active, there's surely NO free successor\r\n;--- so we don't need to merge.\r\n\tmov [ebx].XMS_HANDLE.xh_flags,XMSF_FREE\r\n\tpop ebx\r\n else\r\n    mov word ptr [ebx].XMS_HANDLE.xh_flags,XMSF_USED\r\n    mov edx,ebx                      ; and FREE it again -\r\n    sub edx,[dwRes]                  ;!!! 2.2.2020: line added for v5.79\r\n    pop ebx\r\n    call xms_free_emb                ; to merge it with free block list\r\n endif\r\n@@ext_dont_need_handle:\r\n@@ext_grow_success:\r\n    pop edx\r\n    @dprintf ?EMBDBG, <\"xms_ext_realloc_emb exit, esp=%X\",10>, esp\r\n    mov ax,1\r\n    mov bl,0\r\n    ret\r\nxms_ext_realloc_emb endp\r\n\r\n;--- dx=handle, bx=new size\r\n\r\nxms_realloc_emb proc\r\n\r\n    @dprintf ?EMBDBG, <\"xms_realloc_emb enter, ebx=%X\",10>, ebx\r\n    push ebx                        ; preserve Hiword(ebx)\r\n    movzx ebx,bx                    ; clear top 16 bit\r\n    call xms_ext_realloc_emb\r\n    mov [esp],bx                   ; modify Loword(ebx)\r\n    pop ebx\r\n    @dprintf ?EMBDBG, <\"xms_realloc_emb exit, ebx=%X\",10>, ebx\r\n    ret                                 \r\n\r\nxms_realloc_emb endp\r\n\r\n; calculate the move src/dst address\r\n; In: SI - handle (0 if EDX should be interpreted as seg:ofs value)\r\n;   EDX - offset\r\n;   ECX - length\r\n; Out: SI:EAX=linear address 00-39\r\n\r\nxms_get_move_addr proc\r\n    or si,si           ; translate address in EDX?\r\n    jnz @@is_emb\r\n\r\n                        ; its segment:offset in EDX\r\n\r\n                        ; eax = 16*(edx high) + dx\r\n    movzx eax,dx        ; save offset\r\n    mov dh,0\r\n    shr edx,12          ; convert segment to absolute address\r\n    add eax,edx         ; add offset\r\n\r\n    mov edx,eax         ; check that eax(address) + ecx (length) is <= 10fff0\r\n    add edx,ecx\r\n    jc @@wrong_size     ; negative length might wrap\r\n    cmp edx,10fff0h\r\n    ja @@wrong_size\r\n    clc\r\n    ret\r\n\r\n@@is_emb:               ; it's a handle:offset pair\r\n    call xms_check_handle   ;check if SI holds a \"used\" handle\r\n\r\n if ?XMS35\r\n    push ebx\r\n    xor ebx,ebx\r\n endif\r\n    mov eax,ecx         ; contains length\r\n    add eax,edx         ; assert length + offset < size    \r\n if ?XMS35\r\n    adc ebx,ebx\r\n else\r\n    jc @@wrong_size\r\n endif\r\n    add eax,1024-1      ; round up to kB\r\n if ?XMS35\r\n    adc ebx,0\r\n    shrd eax,ebx,10     ; convert to kB units\r\n    pop ebx\r\n else\r\n    jc @@wrong_size\r\n    shr eax,10          ; convert to kB units\r\n endif\r\n    cmp eax,[esi].XMS_HANDLE.xh_sizeK    ; compare with max offset\r\n    ja @@wrong_size\r\n\r\n    mov eax,[esi].XMS_HANDLE.xh_baseK   ; get block base address\r\n    mov esi,eax         ; store in source index\r\n    shl eax,10          ; convert from kb to linear\r\n    shr esi,22\r\n    add eax,edx         ; add offset into block\r\n if ?XMS35\r\n    adc esi,ebx\r\n endif\r\n    ret\r\n\r\n@@wrong_size:\r\n    mov bl,XMS_INVALID_LENGTH\r\n    xor ax,ax\r\n    stc\r\n    ret\r\n    align 4\r\nxms_get_move_addr endp\r\n\r\n;--- move extended memory block\r\n;--- v86 DS:SI->XMS_MOVE\r\n\r\nxms_move_emb proc\r\n\r\n    movzx edi,word ptr [ebp].Client_Reg_Struc.Client_DS\r\n    shl edi, 4\r\n    movzx esi,si\r\n    add esi,edi\r\nxms_move_emb_ex::           ; <--- entry for internal use (realloc)\r\n if ?XMS35\r\n    mov byte ptr [ebp].Client_Reg_Struc.Client_Error+3,ah  ;abuse Error field to store AH\r\n endif\r\n    xor ax,ax               ; default to error\r\n    push ecx\r\n    push edx\r\n    push eax\r\n    push ebx\r\n\r\n    @dprintf ?EMBDBG, <\"xms_move_emb: siz=%X src=%X:%X dst=%X:%X\",10>,\\\r\n        [esi].XMS_MOVE.len, [esi].XMS_MOVE.src_handle, [esi].XMS_MOVE.src_offset, [esi].XMS_MOVE.dest_handle, [esi].XMS_MOVE.dest_offset\r\n\r\n    mov ecx,[esi].XMS_MOVE.len      ; get length\r\n    test cl,1                       ; is it even?\r\n    jnz @@move_invalid_length\r\n\r\n if ?XMS35\r\n    xor ebx, ebx\r\n    cmp byte ptr [ebp].Client_Reg_Struc.Client_Error+3, 16h\r\n    jnz @F\r\n    mov bl,[esi].XMS_MOVEX.dest_hi\r\n@@:\r\n endif\r\n    push esi\r\n    mov edx,[esi].XMS_MOVE.dest_offset\r\n    mov si,[esi].XMS_MOVE.dest_handle\r\n    call xms_get_move_addr          ; get move address\r\n    mov edx,esi ;save lines 32-39 in DX, since BL must be preserved\r\n    pop esi\r\n    jc @@copy_dest_is_wrong\r\n    mov edi,eax                     ; store in destination index\r\n if ?XMS35\r\n    cmp byte ptr [ebp].Client_Reg_Struc.Client_Error+3, 16h\r\n    jnz @F\r\n    mov bl,[esi].XMS_MOVEX.src_hi\r\n@@:\r\n    push edx\r\n endif\r\n    mov edx,[esi].XMS_MOVE.src_offset\r\n    mov si,[esi].XMS_MOVE.src_handle\r\n    call xms_get_move_addr          ; get move address\r\n if ?XMS35\r\n    pop edx\r\n endif\r\n    jc @@copy_source_is_wrong\r\n    xchg eax,esi\r\n if ?XMS35\r\n    mov bh,al\r\n    mov bl,dl\r\n endif\r\n\r\n;**************************************************\r\n; setup finished with\r\n;   BH.ESI = source A00-A39\r\n;   BL.EDI = destination A00-A39\r\n;   ECX = number of words to move\r\n;**************************************************\r\n if ?XMS35\r\n    @dprintf ?EMBDBG, <\"xms_move_emb: siz(byt)=%X src(40bit)=%X%08X dst(40bit)=%X%08X\",10>, ecx, bh, esi, bl, edi\r\n else\r\n    @dprintf ?EMBDBG, <\"xms_move_emb: siz(byt)=%X src(32bit)=%X dst(32bit)=%X\",10>, ecx, esi, edi\r\n endif\r\n\r\n    or ecx,ecx                 ; nothing to do ??\r\n    jz @@xms_exit_copy\r\n\r\n; overlap test. start of destination block (BL.EDI)\r\n; must either be <= start of source block (BH.ESI) \r\n; or  >= start of source block + block length (BH.ESI+ECX)\r\n\r\n; 1. check if BL.EDI <= BH.ESI\r\n if ?XMS35\r\n    cmp bl,bh\r\n    jb @@move_ok_to_start\r\n    ja @F\r\n endif\r\n    cmp edi,esi\r\n    jbe @@move_ok_to_start\r\n if ?XMS35\r\n@@:\r\n; calculate source + block length: DL.EAX = BH.ESI + ECX\r\n    mov dl,bh\r\n endif\r\n    mov eax, esi\r\n    add eax, ecx\r\n if ?XMS35\r\n    adc dl,0\r\n; 2. check if BL.EDI >= DL.EAX\r\n    cmp bl,dl\r\n    ja @@move_ok_to_start\r\n    jb @@move_invalid_overlap\r\n endif\r\n    cmp edi,eax\r\n    jb @@move_invalid_overlap\r\n\r\n@@move_ok_to_start:\r\n\r\n if ?XMS35\r\n\tand bx,bx\r\n\tjnz @F\r\n endif\r\n    call MoveMemoryPhys\r\n if ?XMS35\r\n    jmp @@xms_exit_copy\r\n@@:\r\n    movzx ax,bh\r\n    movzx dx,bl\r\n    call MoveMemoryPhysEx ;copy ecx bytes from [ax:esi] to [dx:edi]\r\n endif\r\n@@xms_exit_copy:\r\n    pop ebx\r\n    pop eax\r\n    pop edx\r\n    pop ecx\r\n    inc eax         ; success\r\n;   mov bl,0        ; BL is not set to 00 by MS Himem    \r\n    ret\r\n\r\n@@move_invalid_overlap:\r\n    mov bl,XMS_INVALID_OVERLAP\r\n    jmp @@xms_exit_copy_failure\r\n\r\n@@move_invalid_length:\r\n    mov bl,XMS_INVALID_LENGTH\r\n    jmp @@xms_exit_copy_failure\r\n\r\n@@copy_source_is_wrong:\r\n    cmp bl,XMS_INVALID_LENGTH\r\n    je @@xms_exit_copy_failure\r\n    mov bl,XMS_INVALID_SOURCE_HANDLE\r\n    jmp @@xms_exit_copy_failure\r\n\r\n@@copy_dest_is_wrong:\r\n    cmp bl,XMS_INVALID_LENGTH\r\n    je @@xms_exit_copy_failure\r\n    mov bl,XMS_INVALID_DESTINATION_HANDLE\r\n    jmp @@xms_exit_copy_failure\r\n\r\n@@move_a20_failure:\r\n    mov bl,XMS_A20_FAILURE\r\n\r\n                            ; common error exit routine\r\n@@xms_exit_copy_failure:\r\n    mov al,bl\r\n    pop ebx\r\n    mov bl,al\r\n    pop eax\r\n    pop edx\r\n    pop ecx\r\n    ret\r\n\r\nxms_move_emb endp\r\n\r\n    align 4\r\nxms_global_enable_a20: ;ah=3\r\nxms_global_disable_a20:;ah=4\r\nxms_local_enable_a20:  ;ah=5\r\nxms_local_disable_a20: ;ah=6\r\n    call XMS_HandleA20 ; modifies EAX, CX, BL\r\n    mov word ptr [ebp].Client_Reg_Struc.Client_EAX, ax\r\n    mov eax, [ebp].Client_Reg_Struc.Client_EAX\r\n    mov ecx, [ebp].Client_Reg_Struc.Client_ECX\r\n    ret\r\n\r\nendif ;?INTEGRATED\r\n\r\n\r\nif ?A20XMS\r\n\r\n    align 4\r\n\r\n;--- handles XMS A20 functions, AH=\r\n;--- 3 = global enable\r\n;--- 4 = global disable\r\n;--- 5 = local enable\r\n;--- 6 = local disable\r\n;--- modifies eax, cx, bl\r\n\r\nXMS_HandleA20 proc\r\n\r\n    mov al,ah\r\nif ?A20DBG\r\n    push eax\r\n    @dprintf ?A20DBG, <\"XMS A20 emulation, ah=%X, curr cnt=%X, curr state=%X\",10>, al,[wA20], [bA20Stat]\r\n    pop eax\r\nendif\r\n\r\n    mov cx, word ptr [wA20]\r\n    cmp al,4\r\n    jb @@glen\r\n    jz @@gldi\r\n    cmp al,6\r\n    jb @@loen\r\n    jmp @@lodi\r\n\r\n@@glen:\r\n    or ch,1\r\n    jmp @@testa20\r\n@@gldi:\r\n    and ch,not 1\r\n    jcxz @@testa20\r\n    jmp @@stillenabled\r\n@@loen:\r\n    inc cl\r\n    jz @@localerr\r\n    jmp @@testa20\r\n@@lodi:\r\n    sub cl,1\r\n    jc @@localerr2\r\n    and cx, cx\r\n    jnz @@stillenabled\r\n@@testa20:\r\n    and cx, cx\r\n    setnz al\r\n    call A20_Set\r\n@@notchanged:\r\n    mov ax,1\r\n    mov bl,0        ;BL=0 on success???\r\n    mov [wA20],cx\r\n    jmp @@a20_exit\r\n@@localerr2:\r\nif 1        ;potential Delay Angel\r\n    inc cl\r\n    dec ch\r\n    jz @@testa20\r\nendif\r\n@@localerr:\r\nif 1\r\n    xor eax,eax\r\n    mov bl,XMS_A20_FAILURE\r\nelse\r\n    mov ax,1\r\nendif\r\n    jmp @@a20_exit\r\n@@stillenabled:    \r\n    mov [wA20],cx\r\n    xor eax,eax\r\n    mov bl,XMS_A20_STILL_ENABLED\r\n\r\n@@a20_exit:\r\n    ret\r\n    align 4\r\n\r\nXMS_HandleA20 endp\r\n\r\nendif\r\n\r\nif ?A20PORTS or ?A20XMS\r\n\r\n;--- set PTEs for HMA to emulate enable/disable A20\r\n;--- in: AL=1 enable, AL=0 disable\r\n;--- ecx+edx must be preserved\r\n\r\nA20_Set proc public\r\n    cmp [bNoA20], 0 ;NOA20 option set?\r\n    jnz @@exit\r\n    cmp al,[bA20Stat];status change?    \r\n    jz @@exit\r\n    mov [bA20Stat],al\r\n    push edi\r\n    and eax,1\r\n    shl eax,20     ;000000h or 100000h\r\n    or eax, PTF_PRESENT or PTF_RW or PTF_USER\r\n    mov edi, @GetPTEAddr(?PAGETAB0+256*4)  ;EDI = ptr PTE for 0100000h\r\n@@spec_loop:\r\n    stosd\r\n    add ah,10h      ;10000x, 10100x, 10200x, ...\r\n    jnz @@spec_loop\r\n    pop edi\r\n if 0  ;usually \"16 * INVLPG\" is slower than 1 * \"mov cr3, eax\"\r\n  if ?INVLPG\r\n    cmp [bNoInvlPg],0\r\n    jnz @@noinvlpg\r\n    mov eax, 100000h\r\n@@nextpte:\r\n    invlpg ds:[eax]\r\n    add ah, 10h\r\n    jnz @@nextpte\r\n    ret\r\n@@noinvlpg:\r\n  endif\r\n endif\r\n; flush TLB to update page maps\r\n    mov eax,CR3\r\n    mov CR3,eax\r\n@@exit:\r\n    ret\r\n    align 4\r\n\r\nA20_Set endp\r\n\r\nendif\r\n\r\nif ?A20PORTS\r\n\r\n;--- port trap handlers\r\n;--- eax=value (for out)\r\n;--- dx=port\r\n;--- cl=type\r\n\r\nP60BITOFS   equ size TSSSEG+32+60h/8\r\n\r\nA20_Handle60 proc public\r\n if ?DYNTRAPP60\r\n    mov ebx, [dwTSS]\r\n    and dword ptr [ebx+P60BITOFS],not 1\r\n endif\r\n    test cl,OUT_INSTR   ;is it IN or OUT?\r\n    jz @@input\r\n ife ?DYNTRAPP60\r\n    cmp [bLast64],0D1h  ;last value written to 64 was \"write output port\"?\r\n    jnz Simulate_IO\r\n    mov [bLast64],0\r\n endif\r\n    push eax\r\n    @dprintf ?A20DBG, <\"A20_Handle60: write to port 60h kbc output port, al=%X\",10>, al\r\n    shr al,1\r\n    and al,1\r\n    call A20_Set\r\n    pop eax\r\n    or al,2\r\n    out dx, al\r\n    ret\r\n@@input:\r\n ife ?DYNTRAPP60\r\n    cmp [bLast64],0D0h  ;last value written to 64 was \"read output port\"?\r\n    jnz Simulate_IO\r\n endif\r\nA20_Inp92::\r\n    in al,dx\r\n    and al, not 2\r\n    mov ah, [bA20Stat]\r\n    shl ah, 1\r\n    or al, ah\r\n    @dprintf ?A20DBG, <\"A20_Handle60: read port %X kbc output port, al=%X\",10>, dx, al\r\n    ret\r\n    align 4\r\nA20_Handle60 endp\r\n\r\nA20_Handle64 proc public\r\n    test cl, OUT_INSTR\r\n    jz Simulate_IO\r\n if ?DYNTRAPP60\r\n    mov ah,al\r\n    and ah,0FEh\r\n    cmp ah,0D0h\r\n    jnz Simulate_IO\r\n    mov ebx, dwTSS\r\n    or dword ptr [ebx+P60BITOFS],1\r\n else\r\n    mov [bLast64],al    ;save last value written to port 64h\r\n endif\r\n if ?A20DBG\r\n;   cmp al,0D1h\r\n;   jnz @@nokbcout\r\n    @dprintf ?A20DBG, <\"A20_Handle64: write to port 64h, al=%X\",10>, al\r\n@@nokbcout:\r\n endif\r\n    out dx, al\r\n    ret\r\n    align 4\r\nA20_Handle64 endp\r\n\r\nA20_Handle92 proc public\r\n    test cl, OUT_INSTR  ;is it IN or OUT?\r\n    jz A20_Inp92\r\n    @dprintf ?A20DBG, <\"A20_Handle92: write to port 92h, al=%X\",10>, al\r\n    push eax\r\n    shr al,1\r\n    and al,1\r\n    call A20_Set\r\n    pop eax\r\n    or al, 2       ;dont allow disable\r\n if 1\r\n;--- refuse reset via bit 0 = 1\r\n    and al,not 1\r\n endif\r\n    out dx, al\r\n    ret\r\n    align 4\r\nA20_Handle92 endp\r\n\r\nendif\r\n\r\n.text$03 ends\r\n\r\n.text$04 segment\r\n\r\n;--- init XMS\r\n;--- esi -> JEMMINIT\r\n\r\nXMS_Init proc public\r\n\r\nif ?A20PORTS or ?A20XMS\r\n    mov al, [esi].JEMMINIT.NoA20\r\n    mov [bNoA20],al\r\nendif\r\n\r\nif ?INTEGRATED\r\n    mov ax, [esi].JEMMINIT.HmaMin\r\n    mov [hmamin], ax\r\n    mov ax, [esi].JEMMINIT.X2Max\r\n    mov [x2max], ax\r\n    @dprintf ?INITDBG, <\"XMS init: x2max=%X\",10>, ax\r\n    mov al, [esi].JEMMINIT.A20Method\r\n    mov [A20Index],al\r\n if ?XMS35\r\n    mov ax, [esi].JEMMINIT.xms_version\r\n    mov xms_version,ax\r\n endif\r\nelse\r\n    mov ax, [esi].JEMMINIT.XMSControlHandle\r\n    mov [XMSCtrlHandle],ax\r\nendif\r\n\r\n;---  is XMS pool on? then direct access to XMS handle table required?\r\n\r\nife ?INTEGRATED\r\n    cmp [bNoPool],0\r\n    jne @@noxmsarray\r\nendif\r\n    mov ecx, [esi].JEMMINIT.XMSHandleTable\t;get FAR16 address of XMS handle table\r\n    movzx eax, cx\r\n    shr ecx, 12\r\n    and cl, 0F0h\r\n    add ecx, eax\t;convert to linear address in ecx\r\n    \r\n; transfer XMS table info to fixed memory location, assume size 8 (sizeof XMS_HANDLETABLE)\r\n\r\n    mov eax,[ecx+0]     ;get sig(byte), hdlsiz(byte) in ax and numhandles(word) into HiWord(eax)\r\n    mov dword ptr [XMS_Handle_Table],eax\r\nife ?INTEGRATED\r\n    test eax,0FFFF0000h      ;if size of array is null, disable pooling\r\n    jz @@noxmspool\r\nendif\r\n    movzx edx,word ptr [ecx].XMS_HANDLETABLE.xht_pArray+0;offset\r\n    movzx eax,word ptr [ecx].XMS_HANDLETABLE.xht_pArray+2;segment\r\n    shl eax,4\r\n    add eax,edx\r\nife ?INTEGRATED\r\n    and eax, eax        ;if the array pointer is NULL, disable pooling\r\n    jz @@noxmspool\r\nendif\r\n    cmp eax, 100000h    ;is handle array in HMA?\r\n    jb @@nohmaadjust\r\n\r\n    push eax\r\n    mov cl,16\r\n    mov eax, 100000h\r\n    call MapPhysPagesEx\r\n    mov [PageMapHeap], edx\r\n    \r\n    @dprintf ?INITDBG, <\"HMA shadowed at %X\",10>, eax\r\n    pop ecx\r\n    sub ecx, 100000h\r\n    add eax, ecx\r\n\r\n@@nohmaadjust:\r\n    mov [XMS_Handle_Table.xht_pArray],eax\r\n@@noxmsarray:\r\n    ret\r\nife ?INTEGRATED\r\n@@noxmspool:\r\n    mov [bNoPool], 1\r\n    ret\r\nendif\r\nXMS_Init endp\r\n\r\n.text$04 ends\r\n\r\n    END\r\n"
  },
  {
    "path": "src/XMS.INC",
    "content": "\r\n;--- XMS definitions\r\n\r\nXMS_GETVERSION   equ  0h\r\nXMS_ENABLEA20    equ  5h\r\nXMS_DISABLEA20   equ  6h\r\nXMS_V2_QUERYMEM  equ  8h\r\nXMS_V2_ALLOCEMB  equ  9h\r\nXMS_FREEEMB      equ 0Ah\r\nXMS_LOCKEMB      equ 0Ch\r\nXMS_UNLOCKEMB    equ 0Dh\r\nXMS_ALLOCUMB     equ 10h\r\nXMS_V3_QUERYMEM  equ 88h\r\nXMS_V3_ALLOCEMB  equ 89h\r\n\r\nXMS_HANDLE struct\r\nxh_flags    DB  ?\r\nxh_locks    DB  ?\r\nxh_baseK    DD  ?\r\nxh_sizeK    DD  ?\r\nXMS_HANDLE ends\r\n\r\nLPXMS_HANDLE typedef ptr XMS_HANDLE\r\n\r\n;--- XMS handle flags\r\n\r\nXMSF_FREE   equ 1   ;handle describes a free EMB\r\nXMSF_USED   equ 2   ;handle describes a used EMB\r\nXMSF_INPOOL equ 4   ;handle is free\r\n\r\n\r\nXMS_HANDLETABLE struct\r\nxht_sig         DB  ?\r\nxht_sizeof      DB  ?\r\nxht_numhandles  DW  ?\r\nxht_pArray      DD  ?   ;converted to linear address for 32bit code!\r\nXMS_HANDLETABLE ends\r\n\r\nXMS_MOVE struct\r\n  len           dd  ?       ;  +0 block length in bytes\r\n  src_handle    dw  ?       ;  +4 source handle\r\n  src_offset    dd  ?       ;  +6 offset into source\r\n  dest_handle   dw  ?       ; +10 destination handle\r\n  dest_offset   dd  ?       ; +12 offset into destination\r\nXMS_MOVE ends\r\n\r\nif ?INTEGRATED\r\n if ?XMS35\r\nXMS_MOVEX struct\r\n                XMS_MOVE <>\r\n  src_hi        db  ?       ; bits 32-39 of src\r\n  dest_hi       db  ?       ; bits 32-39 of dst\r\nXMS_MOVEX ends\r\n endif\r\nendif\r\n\r\n;--- XMS error codes\r\n\r\nXMS_NOT_IMPLEMENTED             equ 80h\r\nXMS_VDISK_DETECTED              equ 81h\r\nXMS_A20_FAILURE                 equ 82h\r\nXMS_DRIVER_FAILURE              equ 8eh\r\nXMS_DRIVER_FATAL                equ 8fh\r\nXMS_HMA_NOT_THERE               equ 90h\r\nXMS_HMA_IN_USE                  equ 91h\r\nXMS_HMAREQ_TOO_SMALL            equ 92h\r\nXMS_HMA_NOT_USED                equ 93h\r\nXMS_A20_STILL_ENABLED           equ 94h\r\nXMS_ALL_MEM_ALLOCATED           equ 0a0h\r\nXMS_NO_HANDLE_LEFT              equ 0a1h\r\nXMS_INVALID_HANDLE              equ 0a2h\r\nXMS_INVALID_SOURCE_HANDLE       equ 0a3h\r\nXMS_INVALID_SOURCE_OFFSET       equ 0a4h\r\nXMS_INVALID_DESTINATION_HANDLE  equ 0a5h\r\nXMS_INVALID_DESTINATION_OFFSET  equ 0a6h\r\nXMS_INVALID_LENGTH              equ 0a7h\r\nXMS_INVALID_OVERLAP             equ 0a8h\r\nXMS_PARITY_ERROR                equ 0a9h\r\nXMS_BLOCK_NOT_LOCKED            equ 0aah\r\nXMS_BLOCK_LOCKED                equ 0abh\r\nXMS_LOCK_COUNT_OVERFLOW         equ 0ach\r\nXMS_LOCK_FAILED                 equ 0adh\r\nXMS_ONLY_SMALLER_UMB            equ 0b0h\r\nXMS_NO_UMB_AVAILABLE            equ 0b1h\r\nXMS_UMB_SEGMENT_NR_INVALID      equ 0b2h\r\n\r\n"
  }
]