ARM-DOS 4.00 Play it

Technical reference

ARM-DOS 4.00

MS-DOS 4.00, as Microsoft might have ported it to an ARM over a long weekend in 1988.

Type VER and the machine says ARM-DOS Version 4.00, because it follows MS-DOS 4.00 command for command. Microsoft released the MS-DOS 4.00 source under the MIT licence, which made it possible to check every message, every column of a DIR listing and every error code against the real thing. The real MS-DOS 4.00, running in DOSBox-X, was the yardstick throughout: the same keystrokes went into both machines, and the screens had to match.

1From power switch to prompt

Booting splits the work the same way MS-DOS 4.00 does, and for the same reasons:

  1. The ROM BIOS (ARM code and C) runs the power-on self test: it counts the memory, identifies the processor, finds the drives and fitted cards, and beeps. It loads the first sector of the boot disk to 7C00h, checks for 55AAh and jumps there in ARM state.
  2. The boot sector, in ARM assembly, moves itself out of the way, checks that the first two root directory entries are IO.SYS and ARMDOS.SYS, and loads all of IO.SYS to 0070:0000, where DOS has always loaded it. If they are missing, it says DOS 4's own Non-System disk or disk error.
  3. IO.SYS holds the resident device drivers (CON, AUX, PRN, CLOCK$ and the disk driver) and SYSINIT, which loads the kernel, processes CONFIG.SYS line by line, loads the DEVICE= drivers and starts the shell. On a clean boot it prints nothing, like DOS 4.
  4. ARMDOS.SYS is the kernel proper: INT 20h-2Fh, the FAT file system, file handles, processes and memory. It is a relocatable ARM image that SYSINIT places right after IO.SYS and fixes up, the way SYSINIT once moved the DOS kernel to its final address.
  5. COMMAND.COM runs AUTOEXEC.BAT and gives you the prompt.

The machine's CONFIG.SYS loads HIMEM.SYS for extended memory and the CD-ROM driver; AUTOEXEC.BAT sets the BLASTER variable, initialises the Sound Blaster's mixer and starts ARMCDEX, which makes the CD-ROM drive E: (after the two hard disks, C: and D:).

2The kernel

The kernel is new C code, with a little ARM assembly, written to behave the way the MS-DOS 4.00 source does and to say what it says. It is compiled to Thumb code to keep it small, and DOS itself takes about 60 KB of conventional memory, much as DOS 4.00 did on a PC.

It implements nearly all of the DOS 4 INT 21h functions: console input with the DOS line editor and its function-key template, files and directories through handles and FCBs, IOCTL, memory allocation, EXEC and terminate-and-stay-resident, country information and code pages. The file system reads and writes FAT12 and FAT16 with a write-back buffer cache, extended partitions become logical drives, and SUBST and JOIN work through the current directory structure the way DOS 4 did. The in-memory structures keep their DOS 4 layouts (the system file table, the list of lists, the memory control blocks), widened only where a far pointer has to hold a 32-bit address.

Device drivers follow DOS's model: a header with strategy and interrupt entry points, and request packets with DOS's layout. The entry points are ARM functions, and each far pointer becomes a flat 32-bit one.

HIMEM.SYS provides XMS 2.0. On this machine the 32-bit address that "lock block" returns is directly usable, and the C runtime every program is built with uses it to grow its heap past 640 KB. Big programs just work: ARM-DOS's C runtime is its own DOS extender.

3COMMAND.COM

COMMAND.COM is a C re-creation of the DOS 4.00 shell, following the MIT-released assembly source routine by routine, with every message taken from it. Like the original, it splits into a small resident part (the Ctrl-C and critical-error handlers, program loading, and the state that has to outlast a program run) and a transient part (the command processor) that sits at the top of memory. When a program needs that memory, the transient part is dropped and read back from disk afterwards, with DOS 4's Insert disk with \COMMAND.COM in drive A when the disk is not there.

Batch files, FOR, IF, GOTO, CALL, pipes, redirection, PROMPT, PATH and the internal commands all behave like the original, and the test suite checks it by typing the same commands into both shells and comparing the screens.

4The commands

The DOS 4 commands reached the ARM by three routes.

RouteCommands
Microsoft's own C source, compiled for the ARMFC, MEM, ATTRIB, FDISK, JOIN, SUBST
Microsoft's own source, assembledKEYBOARD.SYS (plus a Dvorak layout), COUNTRY.SYS
Re-created in C from Microsoft's assembly source, step by stepANSI.SYS, CHKDSK, COMP, DEBUG, DISKCOMP, DISKCOPY, DISPLAY.SYS, EDLIN, FIND, FORMAT, KEYB, LABEL, MODE, MORE, NLSFUNC, PRINT, REPLACE, SORT, SYS, TREE, XCOPY

When FDISK writes a partition table here, it writes exactly the bytes the real FDISK would, because it is the same code.

DOSSHELL, the DOS 4.00 Shell, wasn't in Microsoft's source release, so it was written fresh from the real Shell's screens, captured character by character. It has the menus, the File System, the colour schemes, help and mouse support.

DEBUG keeps DOS 4's prompt, one-letter commands and output layouts, with an ARM register set, an ARM and Thumb disassembler, a line-by-line ARM assembler and ARM breakpoints underneath:

C:\>DEBUG
-A 100
00027490 mov r0,#0x900          AH=09h: print a string
00027494 adr r3,120             DS:DX, now one flat pointer in r3
00027498 svc 21                 INT 21h
0002749C svc 20                 INT 20h: end the program
000274A0
-E 120 "Hello, world!$"
-G
Hello, world!
Program terminated normally

International support works as in DOS 4: KEYB with sixteen national layouts besides US, plus Dvorak, code pages through DISPLAY.SYS, MODE and NLSFUNC, and COUNTRY= for dates, times and sorting. The page's on-screen keyboard relabels its keys to follow KEYB.

5Ports, natives and x86 guests

The programs on the disk come in three kinds.

Ports are existing programs whose source code was released, compiled for the ARM with a platform layer that talks to the hardware the way the original DOS versions did. DOOM sets VGA mode 13h through the BIOS, loads its palette through the DAC ports, hooks INT 9 to read scan codes from port 60h, reprograms the timer to 140 Hz while chaining the BIOS tick, and plays its effects through the Sound Blaster's DMA and its music on the OPL3 or General MIDI. Quake does its floating point on the VFP. The ports include DOOM, Wolfenstein 3D, Quake, Duke Nukem 3D with Ken Silverman's Build engine, Keen Dreams, the Hexen II engine, Zork I to III, Colossal Cave, Bywater BASIC, TinyCC and Digital Research's GEM/3, whose 8086 assembly and screen driver were rewritten in C and ARM code.

Native programs were written for ARM-DOS: the ARM Commander file manager, EDIT, ARM Turbo C and ARM QuickBASIC (on Turbo Vision), TERM, ONLINE, ARM Paint and Banner, the CD player and CD-ROM extensions, ARMINFO, the talking Dr. ARMitso, POPUP, the demo, The ARM Pit BBS and its door game, and ELBOW. They borrow their looks from programs of the era, but none of their code.

x86 guests are real IBM PC programs, byte for byte as released, run by ELBOW.

FILE tells them apart: FILE C:\DOS\*.EXE shows which files are ARM programs and which are x86.

6Writing programs for ARM-DOS

On the machine

TC is ARM Turbo C, a blue-screen IDE with the TinyCC compiler built in: F9 builds, Ctrl+F9 runs, and errors land in the Message window. TCC is the same compiler on the command line. Every program it produces is real ARM code, and TCC can even compile itself on ARM-DOS. QB is ARM QuickBASIC with the Bywater BASIC interpreter inside; BASIC is a GW-BASIC-style interpreter; DEBUG assembles ARM instructions directly.

C:\>TCC SQUARES.C
C:\>SQUARES
1 squared is 1
...

With the SDK

The source repository has an SDK for building ARM-DOS programs on your own computer with arm-none-eabi-gcc and newlib: start-up code, the DOS system calls behind the C library, Microsoft C style headers (dos.h, conio.h, bios.h and friends), a linker script and elf2exe, which wraps an ELF file in the MZ and AR1 headers. Programs that want hardware floating point can opt into the VFP. The SDK also has helpers for the Sound Blaster and General MIDI.

A DOS call from C looks the way it did with Microsoft C, and underneath it is one SVC instruction:

/* INT 21h AH=02h: write the character in DL */
union REGS r;
r.h.ah = 0x02;
r.h.dl = 'A';
intdos(&r, &r);          r0 = 0x0200, r3 = 65, then svc #0x21

7When things go wrong

A division by zero prints DOS's Divide overflow and ends the program. An ARM exception in a program (an undefined instruction, or a data or prefetch abort) prints the exception, the address and the program's name with a register dump, ends the program and returns to its parent. A fault inside the kernel or an interrupt handler goes to the BIOS's crash screen. CRASH is a guided tour of all of these.