Skip to content
Андрей Щеглов edited this page Apr 9, 2025 · 20 revisions

DOS

Parent Directory

Installation

DOSBox

MS-DOS 6.22 can see no more than 1024 cylinders, which is roughly 504 MB with 16 heads (C/H/S = 1024/16/63) and 8032.5 MB with 255 heads (C/H/S = 1024/255/63). Sector size is assumed to be 512 bytes.

A disk image file of 504 MB (with 16 "heads") can be created using the bximage utility from bochs. A disk image with 255 "heads" can be created using dd:

dd if=/dev/zero of=path/to/hda.img bs=512 count=16450560

Don't forget to make the image file sparse:

fallocate -d path/to/hda.img

MS-DOS can be installed using the following [autoexec] section in dosbox.conf:

[autoexec]
@echo off
imgmount 2 path/to/hda.img -t hdd -fs none -size 512,63,255,1024

rem Install MS-DOS 6.22, Ctrl+F4 to swap floppy images.
boot path/to/disk01.img path/to/disk02.img path/to/disk03.img

Once MS-DOS is installed, be sure to update the boot command arguments:

[autoexec]
@echo off
imgmount 2 path/to/hda.img -t hdd -fs none -size 512,63,255,1024

cls
boot -l c

If extra partitions (D:, E:, F:) are created in the above 8 GB disk (using DOS fdisk), the image file geometry, as reported by Linux fdisk, will be:

Disk hda.img: 7.84 GiB, 8422686720 bytes, 16450560 sectors
Geometry: 255 heads, 63 sectors/track, 1024 cylinders
Units: cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device     Boot Start   End Cylinders  Size Id Type
hda.img1   *        1   261       261    2G  6 FAT16
hda.img2          262  1024       764  5.8G  5 Extended
hda.img5          262   522       261    2G  6 FAT16
hda.img6          523   783       261    2G  6 FAT16
hda.img7          784  1024       241  1.8G  6 FAT16

Yet, upon the very next reboot, MS-DOS will either hang or run into a "divide overflow" error. This problem can be worked around: if MS-DOS setup is re-run from the floppy disks, D:, E:, and F: drives can be formatted during the setup process, and MS-DOS reinstalled. A run of scandisk will be required, as some or all logical drives will have missing FAT backup copies and/or lost clusters.

Still, such a multi-partition disk image will be impossible to correctly mount as a Linux loop device:

If you have VirtualBox installed, the disk image can be converted to the VHD format:

vbox-img convert --srcformat=RAW --srcfilename=hda.img --dstformat=VHD --dstfilename=hda.vhd

If the VHD image is attached to a Hyper-V virtual machine running Linux, fdisk will show exactly the same geometry:

Running dosfsck against /dev/sda1 will report bad file name errors, consistently with the mounted FAT16 file system view:

Still, after all file system errors are fixed, an attempt to boot MS-DOS from the disk image will hang:

At the same time, if the above VHD image is converted back to RAW and re-attached to DOSBox, MS-DOS boots just fine, and scandisk reports no further errors. Yet, it may be more practical to use a 2 GB disk image (C/H/S = 261/255/63) and create just a single FAT16 partition:

dd if=/dev/zero of=path/to/hda.img bs=512 count=4192965

Mounting a hard drive image in Linux

The image file may be later mounted as a loop device (assuming codepage 866 is compiled into the kernel, modprobe nls_cp866):

sudo mount -t vfat -o loop,offset=32256,codepage=866,iocharset=utf8,fmask=133,uid=$(id -u),gid=$(id -g) path/to/hda.img mountpoint/

Video drivers

By default, DOSBox emulates S3 Trio64 (86C764) video card, but Windows 3.x drivers for S3 Trio64V+ (86C765) or Trio64 V2 (86C775 or 86C785) work just as well (you need a package containing s3trio.drv).

The driver package from S3 replaces some of the bitmap fonts (*.fon) files shipped with a localized (e.g.: Russian) version of Windows 3.x. This messes up Cyrillic fonts.

Be sure to back up your *.fon files immediately after Windows installation, and restore the fonts once you've installed S3 video drivers.

VESA

  • VBE 2.0.
  • vbetest.exe runs.
  • uvconfig.exe lists S3 Trio64 family among the supported chipsets, but doesn’t detect it (univbe.exe unusable).

DOSBox Staging

Mouse

Version 0.82.0, installed from Homebrew (Linux), has problems unlocking the mouse via Ctrl+F10. This doesn’t depend on the window manager, or the driver used (mouse.sys vs ctmouse.exe), or whether the mouse is connected via a COM port, or via PS/2.

Under certain X11 servers (Xvnc, Xnest, XWin, VcXsrv), the mouse pointer immediately slides to the bottom-right corner of the window, and setting/clearing SDL_MOUSE_RELATIVE flag has no effect.

VESA

  • VBE 2.0 (upgradeable to 3.0 via UniVBE).
  • vbetest.exe runs.
  • uvconfig.exe completes successfully.
  • univbe.exe can be loaded as a TSR.

Bochs

Bochs doesn’t support disk images with 255 heads (its limit is 16 heads maximum). This means that the largest disk image size Bochs can handle when using CHS geometry is 504 MB.

When started with a larger disk image, it prints:

00000000000p[SIM   ] >>PANIC<< numerical parameter 'heads' was set to 255, which is out of range 0 to 16
00000000000e[SIM   ] notify called, but no bxevent_callback function is registered
00000000000e[SIM   ] notify called, but no bxevent_callback function is registered
========================================================================
Bochs is exiting with the following message:
[SIM   ] numerical parameter 'heads' was set to 255, which is out of range 0 to 16

If the CHS disk geometry is specified as 16320/16/63, Bochs indeed translates it to 1024/255/63:

00000840362i[BIOS  ] ata0-0: PCHS=16320/16/63 translation=lba LCHS=1024/255/63

Mouse

Under certain X11 servers (Xvnc, Xnest, XWin, VcXsrv), mouse is unusable.

VESA

vga: extension=vbe

The default VGA ROM image (vgaromimage: file=/usr/share/vgabios/vgabios-stdvga.bin):

  • VBE 2.0 (reported).
  • vbetest.exe crashes and hangs the OS (even after univbe.exe is loaded).
  • Other tests of VESA capabilities (astra.exe, hwinfo.exe) hang.
  • uvconfig.exe completes successfully when run for the 2nd time (detects WinBond W9970CF PCI with 1 MB, can handle 800x600x16bpp).
  • univbe.exe can be loaded as a TSR.

The alternative VGA ROM image from SeaBIOS (vgaromimage: file=/usr/share/seabios/vgabios-stdvga.bin):

  • VBE 3.0.
  • vbetest.exe crashes and hangs the OS.
  • uvconfig.exe terminates with an "out of memory" message.
  • Both hwinfo.exe and nssi.exe report a 256 kB VGA card with a VESA 3.0 implementation by SeaBIOS.

Since the amount of video memory is ridiculously small, it is better to proceed with the Cirrus option.

vga: extension=cirrus
  • Cirrus Logic CL-GD5430 with 2 MB of video RAM.
  • VBE 2.0.
  • Windows 3.x successfully runs with a VESA driver (unless univbe.exe is loaded).
  • uvconfig.exe successfully detects Cirrus Logic CL-GD5430 with 2 MB (when run for the 2nd time).
  • univbe.exe can be loaded as a TSR.
  • vbetest.exe crashes and hangs the OS (regardless of whether univbe.exe is loaded or not).
VGABIOS

QEMU

QEMU boots from a raw disk image just fine:

$ qemu-system-i386 -hda hda.img
$ qemu-system-i386 -drive if=ide,file=hda.img,index=0,media=disk,format=raw

In both cases, however, QEMU will assume the number of heads to be 16, effectively lowering the CHS-addressable hard drive size limit to 504 MB, similarly to Bochs.

It is possible to manually specify the CHS geometry via a separate -device option, but QEMU exhibits the same head limit of 16:

$ qemu-system-i386 \
> -drive if=none,id=hda,file=hda.img,index=0,media=disk,format=raw \
> -device ide-hd,drive=hda,cyls=1024,heads=255,secs=63
qemu-system-i386: -device ide-hd,drive=hda,cyls=1024,heads=255,secs=63: heads must be between 1 and 16

This is because DOSBox and its descendants emulate the BIOS interface, which supports up to 255 heads. Bochs and QEMU emulate the hardware, specifically an ATA interface, and ATA only supports 16 heads.

The discrepancy is supposed to be handled in the BIOS (same as on a real system). For that to happen, one needs to specify a number of cylinders greater than 1024 (16 320 in our case); then the BIOS will increase the number of heads to bring the number of cylinders below 1024 (as supported by the BIOS interface).

See this discussion for reference.

The correct QEMU command line would then be:

qemu-system-i386 \
-drive if=none,id=hda,file=hda.img,index=0,media=disk,format=raw \
-device ide-hd,drive=hda,cyls=16320,heads=16,secs=63

VESA

QEMU, among other video cards, can emulate the following:

  • Standard VGA with Bochs VBE extensions (-vga std, the default)
  • Cirrus Logic CL-GD5446 (-vga cirrus)
  • VMWare SVGA-II (-vga vmware)

In all the above cases, VESA support is provided by SeaBIOS:

  • VBE 3.0.
  • vbetest.exe runs.
  • uvconfig.exe doesn't detect any supported SuperVGA chip (univbe.exe unusable).

Additionally, in case CL-GD5446 is used, VESA implementation can be replaced with that of UniVBE.

SeaBIOS UniVBE

QEMU version 7.2+dfsg-7+deb12u3, included with Debian Linux 12, has issues emulating VESA (vbetest.exe from UniVBE hangs).

Upgrading to 7.2+dfsg-7+deb12u12 results in vbetest.exe finally running, but QEMU may now crash with the following messages:

ERROR:../../accel/tcg/tcg-accel-ops.c:81:tcg_handle_interrupt: assertion failed: (qemu_mutex_iothread_locked())
Bail out! ERROR:../../accel/tcg/tcg-accel-ops.c:81:tcg_handle_interrupt: assertion failed: (qemu_mutex_iothread_locked())

In version 9.2.2 (9.2.2+ds-1~bpo12+1), vbetest.exe switches to the desired VESA mode but eventually hangs.

In all cases, quake.exe with any VESA video mode (640x480 and higher) crashes with message:

Unable: Unable to load VESA palette

Configuration files

Initially, a reasonable config.sys might look like this:

device=c:\windows\himem.sys /testmem:off /verbose
dos=high,umb
device=c:\dos\emm386.exe min=0 noems novcpi verbose highscan

buffers=15,0
fcbs=4,0
files=40
lastdrive=Z
stacks=9,256
switches=/F

devicehigh=c:\dos\setver.exe

country=007,,c:\dos\country.sys
devicehigh=c:\dos\display.sys con=(EGA,866,1)
installhigh=c:\dos\nlsfunc.exe c:\dos\country.sys

devicehigh=c:\dos\ramdrive.sys 8192 512 1024 /E
devicehigh=c:\dos\ansi.sys

Note, however, that himem.sys alone retains the CPU in Real Mode, while emm386.exe, even with EMS disabled (noems novcpi), switches to Virtual 8086 Mode, which may cause compatibility issues with certain software.

autoexec.bat:

@echo off

prompt $p$g
path c:\dos
set TEMP=c:\tmp

loadhigh c:\windows\smartdrv.exe /X /V

mode con codepage prepare=((866) c:\dos\ega.cpi)
mode con codepage select=866
chcp 866
loadhigh keyb ru,866,c:\dos\keyboard.sys

loadhigh c:\dos\doskey /insert

After memmaker runs, it adds /L (and, sometimes, /S) arguments to devicehigh, installhigh, and loadhigh commands. Note, however, that I=... and X=... switches of EMM386 are not portable (hardware-dependent).

config.sys:

device=c:\dos\emm386.exe min=0 noems novcpi verbose highscan I=B000-B7FF X=C800-C9FF

devicehigh /L:1,12048 =c:\dos\setver.exe
devicehigh /L:1,15792 =c:\dos\display.sys con=(EGA,866,1)

devicehigh /L:1,5888 =c:\dos\ramdrive.sys 8192 512 1024 /E
devicehigh /L:1,9072 =c:\dos\ansi.sys

autoexec.bat:

loadhigh /L:0;2,45456 /S c:\windows\smartdrv.exe /X /V

loadhigh /L:2,16208 keyb ru,866,c:\dos\keyboard.sys

loadhigh /L:1,6384 c:\dos\doskey /insert

Now, since loadhigh directives in autoexec.bat can be mirrored with installhigh in config.sys, it's possible to move most of the autoexec.bat content (except for built-in shell interpreter commands) to config.sys.

If the goal is to minimize the number of drivers loaded (either for compatibility reasons or to conserve conventional memory), then the reduced version of config.sys might look like this:

device=c:\windows\himem.sys /testmem:off /verbose
dos=high

buffers=15,0
fcbs=4,0
files=40
lastdrive=Z
stacks=9,256
switches=/F

set prompt=$p$g
set temp=c:\tmp
set comspec=c:\dos\command.com
set path=c:\dos;c:\windows

shell=c:\dos\command.com /P
country=007,866,c:\dos\country.sys

devicehigh=c:\dos\display.sys con=(EGA,866,1)
devicehigh=c:\windows\ifshlp.sys

installhigh=c:\dos\mode.com con codepage prepare=((866) c:\dos\ega.cpi)
installhigh=c:\dos\mode.com con codepage select=866

installhigh=c:\windows\smartdrv.exe /X /V

The above will both select the console code page and install the console font, so that non-ASCII characters are displayed correctly, and Windows no longer complains about missing code page on start.

Neither nlsfunc nor keyb are necessary for correct DOS and Windows operation, so one may consider moving them to a separate config.sys entry:

installhigh=c:\dos\nlsfunc.exe c:\dos\country.sys
installhigh=c:\dos\keyb.com ru,866,c:\dos\keyboard.sys

Yet, if one wants to be able to switch the active codepage via chcp (e.g.: chcp 866), it is better to have nlsfunc loaded.

A structured config.sys example can be found at this link.

DOS memory explained

EMM386.EXE

Virtual 8086 mode

Virtual 8086 mode is a sub-mode of Protected mode. In short, Virtual 8086 mode is whereby the CPU (in protected mode) is running an "Emulated" 16bit Real Mode machine.

DOS extenders

  • DOS/4GW and Protected Mode
  • HX DOS Extender is a free DOS extender with built-in Win32 PE file format support. Usually the purpose of a DOS extender is to make protected-mode features available for DOS applications. HX fully supports this goal, but goes some steps further. A Win32 API emulation layer is part of HX which allows many Win32 console applications to run in DOS. This emulation goes far beyond similiar approaches in other extenders (Borland's PowerPack, WDOSX or Phar Lab TNT). Furthermore HX implements - limited - support for windows, DirectDraw, GDI and even OpenGL graphics. This allows to run "simple" Win32 GUI apps in DOS as well.

Programming