Last modified: Wed Dec 2 15:25:53 EST 2020
Using QEMU version 5.1.0 on 64-bit Linux.
QEMU="qemu-system-i386 -nodefaults -enable-kvm |
Omit superfluous emulated devices. Use hardware virtualization. |
-audiodev alsa,id=alsa |
Use ALSA audio backend (YMMV; pa = PulseAudio). |
-machine type=pc,accel=kvm,pcspk-audiodev=alsa |
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). |
The deprecated option -soundhw pcspk
can replace both
-audiodev alsa,id=alsa
and pcspk-audiodev=alsa
.
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:
FORMAT /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 just 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 W98SE 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
.
CONFIG.SYS:
DEVICE=C:\LOCAL\DRIVERS\XMGR.SYS DOS=HIGH SHELLHIGH=C:\COMMAND.COM C:\ /E:2048 /P BUFFERSHIGH=30 FILESHIGH=40 STACKSHIGH=9,256 LASTDRIVEHIGH=D IDLEHALT=1
AUTOEXEC.BAT:
C:\LOCAL\DOSLFN\DOSLFNMS.COM C:\LOCAL\BIN\CTMOUSE.EXE SET DIRCMD=/LFN /A /P /O
On actual hardware, DOSLFNMS slows things to a crawl unless a disk cache like XHDD is used. In QEMU, I found no benefit to the DOS cache: things are slowed to a crawl regardless what you do.
Result:
Memory Type Total Used Free ---------------- -------- -------- -------- Conventional 639K 36K 603K Upper 0K 0K 0K Reserved 385K 385K 0K Extended (XMS) 3,144,576K 158K 3,144,418K ---------------- -------- -------- -------- Total memory 3,145,600K 579K 3,145,021K Total under 1 MB 639K 36K 603K Memory accessible using Int 15h 0K ( 0 bytes) Largest executable program size 603K (617,360 bytes) Available space in High Memory Area 8K ( 8,055 bytes) FreeDOS is resident in the high memory area.
Adding:
This configuration is not as well tested since I think it is more complicated than necessary.
The DJGPP FAQ recommends using an expanded memory manager (EMM) like JEMM386, mainly to improve the management of extended memory:
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.
The special case of a DJGPP program invoking a non-DJGPP program that uses extended memory can happen as described in the last line, but I found no benefit to using a complicated EMM instead of a simple extended memory manager (XMM) if you are only using DJGPP and friends to build and run Unix software. There's enough low memory without UMBs.
Manual examination of the upper memory area shows that 0xCA300 to 0xEA7FF (written as CA30-EA7F on the JEMM386 command line) is all zeros and thus probably unused by QEMU's emulation. But automatic detection always excludes part of this range for whatever reason, so take care. JEMM386 furthermore refuses to start any lower than 0xCB000 even if the SPLIT option is given.
CONFIG.SYS:
DEVICE=C:\LOCAL\DRIVERS\XMGR.SYS /B /N24 ; Temporary "boot" DEVICE=C:\LOCAL\JEMM\JEMM386.EXE NOEMS I=A000-B7FF X=B800-CAFF I=CB00-EA7F X=EA80-FFFF DEVICEHIGH=C:\LOCAL\DRIVERS\XMGR.SYS DOS=HIGH,UMB SHELLHIGH=C:\COMMAND.COM C:\ /E:2048 /P BUFFERSHIGH=30 FILESHIGH=40 STACKSHIGH=9,256 LASTDRIVEHIGH=D IDLEHALT=1 SWITCHES=/E:1024
AUTOEXEC.BAT:
LH C:\LOCAL\DOSLFN\DOSLFNMS.COM LH C:\LOCAL\BIN\CTMOUSE.EXE SET DIRCMD=/LFN /A /P /O
Result:
Memory Type Total Used Free ---------------- -------- -------- -------- Conventional 736K 12K 724K Upper 124K 25K 99K Reserved 164K 164K 0K Extended (XMS) 3,144,576K 542K 3,144,034K ---------------- -------- -------- -------- Total memory 3,145,600K 743K 3,144,857K Total under 1 MB 860K 37K 823K Total Expanded (EMS) 8,576K (8,781,824 bytes) Free Expanded (EMS) 8,192K (8,388,608 bytes) Memory accessible using Int 15h 0K ( 0 bytes) Largest executable program size 724K (741,312 bytes) Largest free upper memory block 97K ( 98,960 bytes) Available space in High Memory Area 8K ( 8,055 bytes) FreeDOS is resident in the high memory area.
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: Backspace and paste malfunction on first use (glitch). 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: The first character typed or pasted is eaten (glitch). No scrollback. Logs captured by the script command are full of escape sequences and thus unreadable.