It all boils down to the question if you want to build a fully IBM like system with ROM BIOS independent of DOS, with all the bells and whistles attached, or if your goal can be reached with a running DOS with a minimum in BIOS compatibility, just enough to serve whatever application need to run on top of DOS.
Unlike often assumed, MS-DOS does neither use nor need ROM-BIOS. DOS only uses the services offered by it's I/O system, as Microsoft called it, or DOS-BIOS as it was often named. It's corresponding file (*1) is usually called IO.COM/IO.SYS (*2), but may have any name, like IBMBIO.COM for PC-DOS. This file contains manufacturer specific code for hardware access. In case of IBM's version of DOS, PC-DOS, IO.SYS does translate any functions called by DOS into ROM-BIOS calls - adding another layer.
Going the second way would mean to implement only what DOS needs as part of DOS (or drivers), speeding up development a lot - and to a smaller extend DOS as well by reducing overhead. Only functionality for DOS is to be added, everything else only to be followed when needed.
So above question can as well be put as: Do you want to 'steal' PC-DOS' IO.SYS, or adapt IO.SYS like any manufacturer licencing DOS (*3) did?
I'm at the early stages of implementing a home-made IBM PC class computer
Cool, I like the Idea - I hope we'll get some information as the project goes on.
The CPU will be an actual 8088 chip,
Ever thought about using a 186/188? They are already SoCs with lots of perhiperals, although not PC compatible - but at the same time not in the way of PC-compatible I/O.
I'd like to know which INT BIOS functions are mandatory and which ones are optional.
Hard to say, as this depends more on the software you're running than just DOS.
My goal is to be able to boot, at least, MS DOS 2.0.
DOS itself won't use any BIOS interrupt. This is all delegated to OEM files, so it may differ depending on that DOS version you want to use.
Skimming thru the DOS sources for 1.25 and 2.0 on Github shows that the
IO.ASM of DOS 1.25 stored there used direct hardware access via IN/OUT, ignoring the BIOS at all, while DOS 2.0 only includes a
SKELIO.ASMthat seams to be made for some ALTOS machine with direct ROM calls (that is not using any INT at all).
DOS was always designed to be machine independent, not relaying on any prerequisite except the bundled IO system. This independence includes the IBM-PC and its BIOS. All DOS needs is a loader bringing the first sector(s) to load the machine specific IO system (and DOS) which initializes basic components and starts DOS - which it supplies with entry points for all services. While this interface is rather CP/M like in DOS 1, it becomes fairly standard device drivers in DOS 2.0.
Interface between DOS and IO in DOS 1.0
In 1.0 DOS calls IO.SYS functions via a call table located at it's start containing short jumps to each function:
0000: JMP INIT ;entry point du Boot
0003: JMP STATUS ;check for key
0006: JMP INP ;read key from keyboard
0009: JMP OUTP ;send character to screen
000C: JMP PRINT ;send character to printer
000F: JMP AUXIN ;read character from serial
0012: JMP AUXOUT ;send character to serial
0015: JMP READ ;read sector(s) from disk (moved to INT 25h)
0018: JMP WRITE ;write sector(s) to disk (moved to INT 26h)
001B: JMP DSKCHG ;check for disk change
This is the interface DOS uses for all its functions. It's up to IO.SYS how to implement it. Microsoft did not deliver any binary IO.SYS, but only a skeletal source for IO.SYS to be adapted and assembled by the customer. Much like CP/M before.
(Disk) Read and Write are kind of special as their entry points are never called by DOS, but moved into IVT for INT 25h/26h and called that way.
Interface between DOS and IO in DOS 2.0
With DOS 2.0 the I/O system got reworked into using device drivers. To simplify writing and building IO.SYS, it got split in two parts, the (binary) module of SYSINIT, supplied by Microsoft, to be linked with the customer specific code. For the later Microsoft supplied a template called SKELIO. After adapting, assembling and linking it produced IO.SYS with the customer specific part at start. This time there was just a single entry point for initialisation, like before called after IO.SYS got loaded. By default only very BASIC initialisation, was required as most values were handed to SYSINIT by the linker via external references. Of course, depending on the hardware, this would be the point for any generic initialisation not done by the boot ROM - or whatever had to be done to fix problems left by the boot ROM.
After the basic initialisation control was handed to SYSINIT which did setup everything DOS needs - including optional calling a user function (RE_INIT) after DOS was ready to be used, but before any further processing like processing CONFIG.SYS.
The customer specific section of IO.SYS was essentially reduced to a set of standard DOS 2.0 device drivers. The driver table should include the character devices CON, AUX, PRN and CLOCK and, to make DOS worthwhile, at least one disk drive. Of course, any customer/machine specific driver cold be added here, avoiding the need to load it later, thus speeding up boot.
All DOS I/O is done using these devices and their functions. How they deliver them is up to customer code within the driver. It may be calling ROM routines or already optimized code.
Do as all PC manufacturers did
So unless one 'steals' IBMs proprietary IBMBIO.COM to have DOS run on a machine offering a compatible ROM BIOS, you need to write your own IO.SYS (*3).
When doing so, why not make your IO.SYS an exact fit to your system? A DOS compatible system doesn't have to have a ROM BIOS. Oonly a ROM loader. The low level parts could be fully part of DOS-BIOS. This has been done many times, especially early on (*4). One example would be the Siemens PC-D. Originally a Unix work station, it got converted to a DOS PC by removing the MMU and putting the BIOS on disk.
On that machine it had the nice side effect of 960 KiB continous DOS memory, as no ROMs did block address space and graphics was mapped all the way up at F8000h - in fact, there was as well an inofficial version offering 1016 KiB to DOS.
Going that way might simplify bringing up the system a lot. Instead of first writing a BIOS with lots of functions not needed, just supply the functions DOS needs as a package of drivers in IO.SYS and you'll get a running system.
After all, there's no reason why IO.SYS can not include BIOS code and provide all needed function for any other interrupt. That way all BIOS (and whatsoever) services can be added later on by extending IO.SYS. Or any other means like some TSR from DOS prompt :))
If I ever would want to do something alike, that's the way I'd take - at least since DOS sources are available.
I can see that I will need, at least, the following interrupts: [...]
None of these are used by DOS. All functions are accessed via IO.SYS and depend on implementation. 08/09 are hardware functions and need to be handeled by some hardware system, but this can be IO.SYS as well.
All functions are only necessary if you want to run programs using these BIOS calls, as DOS does not (need to).
Do I really need INT 15h? or 14h? or 17h? Can I just point them to an IRET instruction? I won't be implementing serial ports or parallel ports. I guess for INT 18h I can just print a "NO BASIC" message and then halt the computer, as many other BIOSes do.
Again, all of this is only needed to support DOS application that use BIOS functions.
The only part where the strategy to include all BIOS into IO.SYS will fail when trying to boot some other OS. Then again, there are not many for a real mode 8088 beside DOS.
But what if one really wants all the hassles of developing a BIOS first?
(just to circumvent it later)
My implementation strategy involves writting the BIOS in parallel with the design of the system, so for testing it, I will use some IBM PC emulator until the actual computer is ready.
Works as well, but might be more work and less fun.
In that case you may have to analyze whatever IO.SYS you want to use. As said before, they are vendor specific. So if the goal is to clone an IBM, it'll be IBMBIO.COM you need to disassemble - seperate for each DOS version you want to port - and check for INT calls used.
Gladly Michael Steil did this already a few years ago for DOS 1.0. Skimming thru his listing it seams as if IBMBIO.COM for DOS 1 did use
- INT 10h function 14
- INT 11h
- INT 13h function 0, 2, 3
- INT 14h function 0, 1, 2
- INT 16h function 0, 1
- INT 17h function 0, 1
But there are also calls to
- INT 23h
- INT 21h function 15, 26, 39
For DOS 2.0 (and each version thereafter) you need to disassemble and analyze its IBMBIO.COM again.
Is there a (basically) free BIOS
Yes, for example the PCXT25BIOS on Sourceforge which seams rather complete. Then again, quite a lot code cluttering up the limited single megabyte of an 8088
*1 - Well, it looks, or may look, like a file when viewed from DOS, but it's a sequence of sectors to be loaded from a fixed sector. It wasn't until DOS 2.0 that the boot code was meant to take the file location from the directory entry.
*2 - I'll further stick with the name IO.SYS, for all versions
*3 -This included Compaq or any other 'legal' PC-Clone, as not only ROM-BIOS but as well IBMBIO.COM was proprietary code. Microsoft only delivered example code for a IO.SYS to its customers. It wasn't until DOS 5.0 in 1991, that Microsoft did sell a DOS direct to customer, containing an IO.SYS binary ready made for using PC-compatible systems
*4 - And since this is RC.SE, doing so as well, would be quite retro :)