23

I'm at the early stages of implementing a home-made IBM PC class computer (maybe AT class, I don't know yet). The CPU will be an actual 8088 chip, but everything else (except memory) will be programmable logic.

In order to provide it with a functional BIOS (not copied or ripped from anywhere else) I'd like to know which INT BIOS functions are mandatory and which ones are optional. My goal is to be able to boot, at least, MS DOS 2.0.

I can see that I will need, at least, the following interrupts:

  • 08h : timer

  • 09h: keyboard ISR

  • 1Ch : timer daisy chain

  • 10h : to manage the video chip (do I need to implement all functions?)

  • 11h : equipment (this one is easy)

  • 12h : memory size (this one is easy too)

  • 13h : to manage the disk at the sector level (do I need to implement all functions?)

  • 16h : to manage the keyboard

  • 19h : bootstrap loader

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.

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.

8
  • 8
    Fascinating project. But I would say "maybe AT class" is not compatible with "an actual 8088 chip". AT class needs a 80286. Jun 19, 2020 at 0:20
  • 1
    Well... I think that having two PICs, two DMA chips and a RTC is still compatible with having a 8088 CPU. I guess I should name it "almost AT class" Jun 19, 2020 at 0:30
  • Actually, my first idea involved a 80486SL processor, which is (still) hobbyist friendly, in terms of equipment needed to solder it, and the better of all: it currently runs at 3.3V so it's easier to interface with a FPGA. Sadly, the only source of such chips I knew of ran out of units :( OTOH, I'm afraid the electrical interface for such CPU is likley to be much more complicated than the one needed for a 8088. Jun 19, 2020 at 0:34
  • 4
    I'd suggest going for a 186, as this gives about the highest performance with the least implementation effort. They are still available as NOS. Availible up to 25 MHz (16 for the 3V type), with clock generator, DRAM refresh, 2 DMA, 3 timer, inerrupt controller, programmable CS. as EC186 even with 2 high speed UART, watch-dog and 20+ I/O pins. The only thing it still needs are demultiplexers dor address and data lines. With modern 16 bit wide static RAM (and flash), a basic system needs only a few additional components - plus whatever you want for PC--compatibility.
    – Raffzahn
    Jun 19, 2020 at 2:14
  • I worked with some nice 80186 boards & machines "back in the day". They get short shrift compared to the 80286 because IBM went with the 80286 for the AT. Which is kind of funny because the best features of the AT either didn't really work or required such craziness to change modes that, IMHO, the real goals of the 80286 (a real multitasking OS) ended up largely waiting for the 80386. Jun 19, 2020 at 14:09

2 Answers 2

21

TL;DR:

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?


The Details:

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.

Caveat:

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 :)

5
  • How is that DOS is not using any BIOS interrupts? Then, how is DOS accessing the disk if not by using the supplied INT 13h? Jun 19, 2020 at 8:05
  • I don't like the idea of customizing a DOS very much, really. I'd prefer to be able to run a stock DOS Jun 19, 2020 at 8:08
  • 4
    @mcleod_ideafix There is no Stock-DOS 1/2.0. Only IBM-DOS. And IO.SYS is the hardware abstraction layer - not ROM-BIOS. This part contains the manufacturer specific and device dependent code. And that's what every manufacturer had to do. Unlike popular belief, DOS does not use the ROM BIOS it only uses the manufacturer specific IO.SYS code. Here IBM placed code to access their ROM-BIOS. But that's up to each manufacturer to do on his own. So how much ROM code is used is defined by your IO.SYS code, not DOS. For Disk access DOS uses INT 25h/26h. which are copied from the IO.SYS entry table
    – Raffzahn
    Jun 19, 2020 at 9:16
  • 4
    @mcleod_ideafix The DOS era was not so much about APIs. It was a lot about directly accessing memory and I/O ports. And of course, if you look into how a modern disk driver works, you'll still see the same thing - DMA, I/O ports. That's what you need to focus on - how your ports and memory is mapped. The BIOS interrupts were rarely used in applications, and even more rarely in OSes.
    – Luaan
    Jun 19, 2020 at 9:35
  • Proceed stepwise? Run DOS, trap an unimplemented interrupt, implement. Repeat until done. I know of a couple of emulators that were done something like that. One, the "George 3 exec emulator", emulates an ICL 1900 exec, which is resident proprietary code to provide isolation from model-specific differences and basic system control functions, i.e., just like IO.SYS.
    – dave
    Jun 19, 2020 at 11:11
11

Although it was possible to customize MS-DOS to use machine-specific services, the actually shipping generic versions did need an IBM-compatible BIOS so you will need to provide it if you want to run unmodified DOS distributions.

In addition, various programs running on top of DOS did use additional BIOS services (when they didn’t access hardware directly). Off the top of my head, the following services may be necessary:

  • 10h (at least getting/setting modes and teletype print)
  • 8h/9h/1Ch (games and TSRs)
  • 13h (at least read/write sectors functions)
  • 16h (keyboard reading)

You will also need to implement the BIOS data area (BDA) at 40:0. For example, some programs accessed the keyboard buffer or status bytes directly instead of using 16h.

In the end, there’s no explicit list of minimum or maximum required interface for the BIOS, so you’ll likely need to just run the actual software you care about and implement services as you run into them.

5
  • 1
    Yes, implementing an OEM DOS would only allow DOS and really well-behaved programs to run, so it’s easier in the long run to implement the BIOS functions many programs actually expect (including retail DOS). Jun 19, 2020 at 7:13
  • 1
    Oh! Yes! I forgot about the intra comunication for applications reserved BIOS segment Jun 19, 2020 at 8:07
  • 1
    @StephenKitt An IO.SYS can implement the BIOS interrupts as well, so any BIOS function can be provided, which guarantees 100% software (API) compatibility, making any program run. There is no need for a ROM-BIOS at all. Only that all functions (and memory locations used) are provided. Only software using direct ROM calls would fail - but they do so almost everywhere.
    – Raffzahn
    Jun 19, 2020 at 11:43
  • 2
    @Raffzahn true; my point is that doing all this involves writing the same code, whether it’s for a BIOS or IO.SYS (or IBMBIO.COM), and if one is aiming for PC compatibility, the best place to put it is a BIOS, with the well-known addresses for extra bonus points. And of course IO.SYS could place its own hooks at the well-known addresses in the 0xF000 segment if it really wanted to. In addition, there are already open source BIOS implementations available, as you point out, reducing the work further (other versions have better legal standing). Jun 19, 2020 at 11:48
  • @StephenKitt there aren't many well known addresses, if at all. I mean, each BIOS version broke most entry points. And there never worked on other DOS machines .- which there were a lot. So BIOS-INT compatibility does it. By integrating the BIOS interrupts into IO.SYS one saves not only on ROM space, but gains a lot of flexibility in development. I worked for many years with such a setup, and I never had any software incompatibility issues - the only ones that did come up was direct hardware access, but that's a different story, having a ROM based BIOS wouldn't change at all.
    – Raffzahn
    Jun 19, 2020 at 11:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .