DOS tricks
Contents
- Build an assembly file with NASM and run *.com on DOSBox
- Easy all in one build environment
- Dissasemble a DOS *.com with NASM
- Run FreeDOS with QEmu (for SIMD etc.)
- Run FreeDOS with Virtual Box or VMWare player
- Run FreeDOS with Bochs emu (for SIMD etc.)
- Mode 13h (320x200 256 colors)
- High resolution (VESA) display mode NASM sample
- Debugging
- Video capture
- More
Build an assembly file with NASM and run *.com on DOSBox
Here is a simple shell script to assemble a *.s file with
NASM into a *.com file, it show
the amount of bytes and run it under DOSBox :
#/bin/sh
set -eu
nasm $1.s -fbin -o $1.com
echo "bytes: "$(wc -c $1.com)
dosbox -c "mount c ${PWD}" -c "C:" -c $1.com > out.log 2>
/dev/null
Usage (in same directory as myprogram.s):
sh build.sh
myprogram
Custom DOSBox config
To use a custom config specify it :
-conf
mycustom.conf
Most common stuff is to change machine field and
memsize fields to get different display modes.
The DOSBox config can be found in ~/.dosbox on Linux and can
be used as a template for a custom config file.
For mode
13h stuff (320x200) i prefer that my window fit to the
resolution of the mode for accuracy (so no scale
windowresolution=320x200
viewport_resolution=320x200
, aspect change
aspect=false
) and i like a minimal window so i set
window_decorations=false
Easy all in one build environment
One-click compilation and debugging or auto compile (file
change watcher) integrated with DOSBox-X : DOSBOXBUILD.
Dissasemble a DOS *.com with NASM
Disassemble a 16-bit DOS program (use -b32 for 32-bit
programs) :
ndisasm -o100h prog.com
Run FreeDOS with
QEmu (for SIMD etc.)
Running FreeDOS inside QEmu is easier than Bochs but is
perhaps not as accurate.
- get FreeDOS live CD image
- create a freedos storage image : qemu-img create freedos.img
800M
- launch QEmu / FreeDOS setup (select install) : sudo qemu-system-i386 -hda freedos.img -cdrom FD13LIVE.iso -m 16 -boot order=d -display sdl -enable-kvm
- follow the process, partition the drive, reboot and select install again to continue the process
- once done FreeDOS should be bootable
- it is possible to boot directly to FreeDOS by running : sudo qemu-system-i386 -hda freedos.img -m 16 -boot order=c -display sdl -enable-kvm
For sharing files between host and FreeDOS a guide is
available here.
Run FreeDOS
with Virtual Box or VMWare player
VirtualBox
A step by step guide is available here.
Perhaps the most compatible option
in term of maturity.
VMware Player
Mostly the same setup as VirtualBox but sharing files is a bit
harder, vmsmount can
apparently be used to mount shared folders but it was too
cumbersome for me so i just created an ISO of my directory and
mounted / accessed it as a drive : mkisofs -o share.iso
~/dos/share/
Run FreeDOS with Bochs emu (for SIMD etc.)
SIMD instructions are not available under DOSBox, FreeDOS and Bochs can be used (or QEmu, VirtualBox...) to test x86 code
with SIMD (note that AVX require special setup code), Bochs also
support more display modes than DOSBox so it is probably the way go
to for a slightly more modern setup.
Here is a short setup of FreeDOS + Bochs with a sharable
directory :
- get Bochs from your
package manager (or from sources)
- get
FreeDOS pre-installed disk image for Bochs
- the FreeDOS pre-installed archive contain a bochsrc file
which is basically the Bochs configuration file to be used, Bochs
will automatically use it if you launch it from the directory where
the file is available
bochsrc edition for sharing host directory
Replace floppya line in bochsrc by this (also
replace share path by the directory you want to share) :
floppyb:
1_44=vvfat:/home/julien/dos/share, status=inserted
Then you can access files in your share folder within FreeDOS
by typing : b:
bochsrc edition for other
issues
For some reasons (compiled from sources) i had to edit the
FreeDOS bochsrc file to replace romimage and
vgaromimage by :
romimage:
file="/usr/share/bochs/BIOS-bochs-latest"
vgaromimage:
file="/usr/share/bochs/VGABIOS-lgpl-latest.bin"
The VGA BIOS image might not be available, it can be
downloaded here.
Also had issue with keyboard, just needed to specify
keymap in bochsrc :
keyboard: type=mf, serial_delay=250,
paste_delay=100000,
keymap=/home/julien/bochs-2.7/gui/keymaps/x11-pc-fr.map
Doing code mistakes may output a lot of Bochs logs to the
default log file used by FreeDOS bochsrc configuration so i edited
it to output to the terminal instead: log: -
Install latest FreeDOS under Bochs
The pre-installed FreeDOS disk image for Bochs works but it is
outdated and i had some issues with it (DIR command would show some
errors and i couldn't edit FDCONFIG.SYS)
so i installed the latest FreeDOS under Bochs with the
bochsrc file from the pre-installed disk as template (edited
with stuff above) and using these steps :
- create a big enough (mine was at 200Mb) hd image file using
Bochs tool : bximage
- edit bochsrc and replace ata0-master line by : ata0-master: type=disk, path=path/to/your/disk/image.img, mode=flat
- get latest FreeDOS floppy edition
- copy the 144m directory from the ZIP file somewhere easily accessible (such as ~/144m), important as it save time later
- edit bochsrc and replace floppya line by : floppya: 1_44=~/144m/x86BOOT.img, status=inserted
- edit bochsrc and replace boot: c line by : boot: floppy
- run Bochs, you should get the FreeDOS setup, follow the setup process
- when it ask you for the next floppy go into Bochs CONFIG (icon on the menubar, for the X version at least), select 1, enter the floppy path and continue with the default options
- eject / insert the new floppy by clicking on the A: icon on the menubar and continue the install process
- repeat the floppy insertion until the install process
is completed
- once FreeDOS is installed edit bochsrc and replace boot: floppy line by : boot: c
- you should now have the latest FreeDOS running under
Bochs
If the install step fail it is likely that your drive image
file does not have enough space.
My boshrc
megs: 32
romimage: file="/usr/share/bochs/BIOS-bochs-latest"
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest.bin"
vga: extension=vbe, update_freq=15
floppya: 1_44=/home/julien/144m/x86BOOT.img, status=inserted
floppyb: 1_44=vvfat:/home/julien/dos/share, status=inserted
ata0-master: type=disk, path=/home/julien/freedos/hd1.img,
mode=flat
boot: c
log: -
mouse: enabled=0
cpu: ips=15000000
keyboard: type=mf, serial_delay=250, paste_delay=100000,
keymap=/home/julien/bochs-2.7/gui/keymaps/x11-pc-fr.map
Mode 13h (320x200 256
colors)
A good introduction on mode 13h setup is available here.
Palette
To change the VGA palette with short code see TomCat article, some
of the code shown on this page is optimized to setup 13h mode and
do the palette setup at the same time.
Here
is an online tool (JavaScript code) to prototype a VGA
palette.
Mode 13h switch and
putting pixels
Mode 13h setup might highly depend on the program layout but i
somewhat like this short approach for putting pixels :
mov al,0x13
int 0x10 ; switch to mode 13
les bx,[bx] ; trick to prepare segment register (es), disadvantage
is a 16px offset when drawing
; generic way to put white centered pixel with fixed offset (+16,
due to the trick above), could also be shorter by moving the
calculation to "di" or the use of "stosb" which write al at es:di
and increment di (depending on direction flag)
mov di,160+100*320
mov byte [es:di+16],0x0f
BIOS pixel write (see VESA below) can also be used to put
pixels, it is slow but convenient sometimes due to working with
x and y instead of an index.
High
resolution (VESA) display mode NASM sample
About VESA / real mode
With VESA
modes (high resolution / high bpp) and in real mode (DOS
default) there is a limitation on how you write to the framebuffer
since you cannot address all the framebuffer linearly so there is
three solution, the ideal one involve to get into unreal mode /
protected
mode after display mode change so you can address the
framebuffer linearly, the code to do that can be quite heavy (~40
bytes and perhaps more for the best compatibility), the two other
lighter solution is to either use the BIOS pixel write command
which is slow and only works at low bpp or select the appropriate
active page (banking) to write to which is perhaps the best
compromise here.
Note that unreal mode is active by default on DOSBox so there
is no addressing issues if you target DOSBox, there is several
limitations on DOSBox though such as unavailable SIMD instructions
etc. so if you want to target real DOS with slightly modern legacy
hardware or QEmu / Bochs / VBox with FreeDOS you might need to
address this issue.
All this does not happen with the old modes such as 13h
because it all fit into a 64 KiB segment so you get a linear
framebuffer out of the box in real mode without access
limitations.
BIOS pixel write
(1280x1080 256 colors)
Here is a 1280x1024 256 colors NASM setup which write a
centered white pixel, it avoid the addressing issues of real mode but it is
slow :
org 100h
mov ax,4F02h
mov bx,0x4107 ; 4 is to ask for screen clear 107 is display
mode
int 10h
l:
mov ah,0x0c ; write pixel command
mov al,0xf ; color
mov bh,0 ; page number
mov cx,640 ; x
mov dx,540 ; y
int 10h ; https://en.wikipedia.org/wiki/INT_10H
Paging / banking (1440x900 32bpp)
This has a heavier setup due to the more direct way to write
to the framebuffer but it is faster than the BIOS pixel write when
the bank is switched when necessary.
Some code shortcuts are possible by skipping VESA mode info
and using a hardcoded VRAM write address / window granularity etc.
but may be highly unsafe and may break the compatibility of your
program on some hardware.
Note that this resolution is unusual so you may instead want
to switch to a common high resolution if this fail.
org 100h
; VESA get mode info (to get VRAM write address and window
granularity later)
mov di,$200 ; info block addr (will get written)
mov ax,$4F01
mov cx,$4180
int $10
mov ax,$4f02 ; switch display mode
mov bx,$4180 ; 4 is screen clear 180 is mode (1440x900 32bpp)
int 10h
; compute screen coordinate, bank number and write offset
mov ax, 900/2 ; y
mov bx, 5760 ; BytesPerScanLine (could also be taken from info
block at : [es:di + 0fh])
mul bx
add ax, 1440/2*4 ; x
mov cx,[es:di + 04h] ; get window granularity (32k, 64k etc)
sal cx,10 ; * 1024 (cx = bankSize)
div cx ; index / bankSize -> dx = bank number and ax = bank
offset
; get vram write addr from info block
mov es, [es:di + 08h]
mov di, dx ; write offset
mov dx, ax ; bank number
; switch bank with bank number in dx
xor bx, bx
mov ax, $4f05
int 10h
; plot at di within current bank
mov dword [es:di],0xffffff
Here is a highly unsafe slightly shortened version which makes
many assumptions :
bits 16
org 100h
; switch display mode
mov ax,$4f02
mov bx,$4180 ; 4 is screen clear 180 is mode (1440x900 32bpp)
int 10h
; assume fixed write address
push 0xa000
pop es
; compute coordinate
mov eax,(1440/2+900/2*1440)*4
mov ecx,32*1024 ; assume dual 32k window
div ecx
mov di, dx ; get write offset
mov dx, ax ; get bank
; switch bank
xor bx, bx
mov ax, $4f05
int 10h
; plot at di within current bank
mov dword [es:di],0xffffff
Protected mode
See prods such as this one for a
great example of a "modern" DOS setup. (protected mode,
multi-threaded code, high resolution, FPU)
Debugging
DEBUG.EXE can
be used on DOS.
Here is a generic routine to print the value of a 8-bit
register (AL) to the standard output (DOS console), can also be
used as a crude debugging tool :
; example usage :
; mov al,255
; call printALHexValue
printALHexValue:
pusha
mov ah,2
mov dl,'0'
int 21h
mov dl,'x'
int 21h
mov bp,sp
mov dh,[bp+14]
mov dl,dh
shr dl,4
cmp dl,10
jb l1
add dl,7
l1:
add dl,'0'
int 21h
mov dl,dh
and dl,0xf
cmp dl,10
jb l2
add dl,7
l2:
add dl,'0'
int 21h
mov dl,0
int 21h
popa
ret
Video capture
DOSBox has easy video capture by keys combination Ctrl +
F7.
Then the video can be scaled with ffmpeg at higher resolution
(for stream etc) by using nearest neighbor scaling and padding,
here is the settings i use for 320x200 (5x scale and padding to
1920x1080) :
ffmpeg -r 60 -an -i input.avi -vf
"scale=1600:1000,pad=width=1920:height=1080:x=160:y=40,format=yuv420p"
-sws_flags neighbor -t 59 output.mp4
More
back to top