Last modified: 2026-02-07 08:45
Using QEMU version 10.2.0 on 64-bit Linux.
QEMU="qemu-system-i386 -nodefaults -enable-kvm |
Omit superfluous emulated devices. Use hardware virtualization. |
-audiodev pa,id=pulse |
Use PulseAudio backend. |
-machine type=pc,accel=kvm,pcspk-audiodev=pulse |
Emulate i440FX + PIIX chipset and PC speaker. |
-cpu host |
Use the KVM processor with all supported host features. |
-display gtk |
Use GTK-based QEMU window. |
-vga std -net none |
Emulate "standard" VGA card. No network card. |
-rtc base=localtime |
Calibrate emulated CMOS clock for correct results in DOS. |
-serial stdio |
Connect COM1 to stdin/stdout of the QEMU process. |
-m 4G |
Provide 4 GiB guest RAM. |
-mem-prealloc -mem-path /hugepages/qemu" |
Use 4 × 1 GiB hugepages mounted at /hugepages (optional). |
To create a sparse image file for use as an emulated HDD of size 2 GiB:
qemu-img create -f raw C.img 2G
To boot from a floppy image (A.img) for the purpose of running FDISK or FORMAT on the empty HDD image (C.img):
${QEMU} \
-drive file=C.img,format=raw,if=ide,media=disk,readonly=off,index=0,cache=unsafe \
-drive file=A.img,format=raw,if=floppy,media=disk,readonly=off,index=0,snapshot=on \
-boot a
(cache=unsafe means that changes made to the image by the
guest will be saved, while snapshot=on means that they will be
discarded.)
To prepare a bootable C: drive image:
FDISKFORMAT /S /Q /U /V:DOS C:To prepare a non-bootable empty scratch drive image, repeat these steps
but end with FORMAT /Q /U /V:SCRATCH C:
The easiest way to get files in or out is to mount the image when the VM isn't running.
To mount C: from Linux in such a way that Long File Names (LFN) will be compatible with FreeDOS:
mount -t vfat -o uid=1000,gid=100,shortname=winnt,loop,offset=32256 C.img /mnt
The offset of 32256 is specific to how DOS FDISK partitions the drive, with the file system starting at sector 63. If Linux fdisk was used instead, the offset is 1048576.
Given:
${QEMU} \
-drive file=C.img,format=raw,if=ide,media=disk,readonly=off,index=0,cache=unsafe \
-drive file=D.img,format=raw,if=ide,media=disk,readonly=off,index=1,snapshot=on \
-boot c
To discard guest changes to C:, replace cache=unsafe with
snapshot=on.
CWSDPMI is the extender that DJGPP normally uses. To change its parameters, you use CWSPARAM to modify the executable CWSDPMI.EXE.
The two changes are to move the swap file to the scratch drive and to increase the size limit on the swap file. If the memory configuration for DJGPP works as intended, there should be no need for a swap file at all. This is just a backup in case there's another hidden memory limit that I missed.
Full name of paging file ("" to disable) ? [c:\cwsdpmi.swp] d:\cwsdpmi.swp
Number of page tables to initially allocate (0=auto) ? [0]
Minimum application memory desired before 640K paging ? [512Kb]
Paragraphs of DOS memory to reserve when 640K paging ? [3840]
Paragraphs of memory for extra CWSDPMI internal heap ? [256]
Maximum size of swap file ? [128Mb] 4096Mb
Value of run option flags ? [0]
The DJGPP FAQ says that VCPI is good:
Memory managers provide an API for allocating extended memory called VCPI (the Virtual Control Program Interface). Using that API allows CWSDPMI to allocate only as much extended memory as is needed, leaving the rest for non-DJGPP programs, in case you invoke them from DJGPP programs. In contrast, without a memory manager, CWSDPMI will allocate all of the available extended memory to itself, leaving none of it to non-DJGPP programs. This consideration is especially important if you use some DJGPP program, like Bash or Emacs, as your primary system interface.
To get VCPI, you have to install an Expanded Memory Manager (EMM) like JEMM386. But the CWSDPMI r7 documentation says that VCPI is bad:
If you are using an EMM provider for UMBs, CWSDPMI will need to use VCPI. VCPI memory prevents usage of 4MB pages, so for best performance with an EMM provider I recommend suppressing VCPI memory. For example, with JEMM386 this can be done with: JEMM386 LOAD MAX=0
Now wait, how can CWSDPMI need to use VCPI if it works better after you turn VCPI off?
Reviewing the source of CWSDPMI r7, it clearly supports Extended Memory Specification (XMS) in addition to VCPI. Nothing is said about XMS allowing or preventing use of 4 MiB pages. I assume that means that it allows them.
Another option would be running CWSDPMI with no memory manager installed. Nothing could prevent CWSDPMI from using 4 MiB pages then, but DJ's warning about CWSDPMI taking all of the extended memory would also apply.
A page size performance buff is a distraction when the whole environment is emulated by QEMU, but my guess is it's still better to stay out of virtual 8086 mode if you don't absolutely require it.
Inventory:
Examination of the upper memory area with DEBUG shows that 0xCA300 to
0xE8FFF is all zeros and thus probably unused by QEMU's emulation. (The
upper end of this range has crept downwards with increasing QEMU
versions.) But UMBPCI accepts only 16 KiB blocks, so the range is reduced
to 0xCC000 to 0xE7FFF. This becomes
/I=CC00-E7FF on the command line thanks to the convention of
lopping off the last digit.
CWSDPMI is loaded persistently in a UMB to possibly speed things up. If it is not loaded persistently, it is loaded and unloaded with each DJGPP process.
On actual hardware, DOSLFNMS slows things to a crawl unless XHDD is used to speed up and cache disk accesses. In QEMU, I found no benefit to XHDD: things are slowed to a crawl regardless.
CONFIG.SYS:
DEVICE=C:\LOCAL\UMBPCI\UMBPCI.SYS /I=CC00-E7FF DEVICE=C:\LOCAL\DRIVERS\XMGR.SYS /W DOS=HIGH,UMB SHELLHIGH=C:\COMMAND.COM C:\ /E:2048 /P BUFFERSHIGH=30 FILESHIGH=40 STACKSHIGH=9,256 LASTDRIVEHIGH=D IDLEHALT=1
AUTOEXEC.BAT:
LH C:\LOCAL\BIN\CWSDPMI.EXE -p LH C:\LOCAL\BIN\CTMOUSE.EXE LH C:\LOCAL\DOSLFN\DOSLFNMS.COM SET DIRCMD=/LFN /A /P /O SET PATH=C:\LOCAL\BIN;C:\DJGPP\BIN SET DJGPP=C:\DJGPP\DJGPP.ENV SET TZ=:America/New_York REM TMP is used by Open Watcom. REM TEMP is used by Jove. REM TMPDIR is used by DJGPP and by Jove's recover program if it is run REM directly at the command line. Normally, recover is launched via REM jove -r, which overrides TMPDIR with the value of TEMP. SET TMP=D: SET TEMP=D: SET TMPDIR=D:
Abridged output from MEM /D /A:
Memory Type Total Used Free ---------------- -------- -------- -------- Conventional 639K 11K 628K Upper 109K 66K 43K Reserved 276K 276K 0K Extended (XMS) 3,144,576K 159K 3,144,417K ---------------- -------- -------- -------- Total memory 3,145,600K 512K 3,145,088K Total under 1 MB 748K 77K 671K Memory accessible using Int 15h 0K ( 0 bytes) Largest executable program size 628K (643,056 bytes) Largest free upper memory block 41K ( 41,776 bytes) Available space in High Memory Area 7K ( 6,864 bytes) FreeDOS is resident in the high memory area.
Abridged output from the DPMI test program included with HX:
Cpu is in real-mode Int 15h, ax=e801h, extended memory: 3144576 kB Int 15h, ax=e820h, free ext. memory: below 4GB: total 3144576 kB, largest 3144576 kB at 100000 above 4GB: total 1048576 kB, largest 1048576 kB at 100000000 XMS v3.0 host found, largest free block: 3144417 kB No VCPI host found DPMI host found, version=0.90, cpu: 06, support of 32-bit clients: 1 largest free/lockable memory block (kB): 4189024/4189024 free unlocked (=virtual) memory (kB): 4189024 total/free address space (kB): -1/-1 total/free physical memory (kB): 4189024/4189024 vendor: 'CWSDPMI', version: 7.0
The GTK-based QEMU window doesn't support copy/paste, doesn't respect X's XkbLayout, and doesn't have a scrollback buffer. Unfortunately, the following two ways of bypassing it each have their own limitations.
To redirect console input from and output to the host XTerm (stdin/stdout
of the QEMU process), say CTTY COM1 at the C: prompt.
Plusses: Supports copy/paste. Respects XkbLayout. XTerm's scrollback buffer and the script command can be used to capture logs.
Minuses: Startup glitches: backspace and paste malfunction on first use (XTerm) and/or the term is unresponsive until you pound on both windows (Konsole). Some programs in the DJGPP collection, notably the Bash shell and Emacs, override the redirection and switch back to the GTK window. ^C kills QEMU. Cannot log messages printed before the shell is started.
Another approach is to replace -display gtk with
-display curses in the QEMU command line. The 25 lines of
VGA text are then rendered in the center of the XTerm. This
window-in-a-window can be expanded to 50 lines with the DOS command
MODE CON LINES=50.
Plusses: Supports copy/paste. Respects XkbLayout. Bash and Emacs do the right thing.
Minuses: Startup glitch: the first character typed or pasted is eaten. No scrollback. Logs captured by the script command are full of escape sequences and thus unreadable.