23

This is purely academic, out of date, out of curiosity. Let's go back to the 1990s, before Windows, when real-mode DOS programs were common.

The BIOS assigned INT 08H+ for their own interrupt handlers, for instance int 10H for VGA MODE switch, or int 13H for hard drive services. (Table of software-interrupt numbers)

But, as we can see in the Intel® 64 and IA-32 Architectures Software Developer Manuals, we know that there are also many real-address exceptions that can trigger interrupts. For example, #DE (divide exception) raising interrupt vector 0h, or x87 FPU raising 10h (https://wiki.osdev.org/IVT)

If a programmer intentionally or unintentionally makes the CPU raise those exceptions on its own, by running instructions like bound that can trigger a BOUND Range Exceeded exception, how does the BIOS or anything else distinguish that from an INT 5 software interrupt to make a PRINT SCREEN call, for instance? It's the same IVT entry, so the same interrupt handler will be invoked in either case.

What's more, the default IRQ configuration makes it so that if you want to use the keyboard, you can't trigger those interrupts. (OSdev IRQ assignments table)

As so many of Intel's interrupt slots have been reused by the BIOS, things became unreasonable.

(This was originally asked on Stack Overflow).


Additionally: My question is not only focused on how the BIOS/DOS distinguished the clashing interrupts from hard/soft sources, but also, and equally importantly, I want to know the historical reason why the BIOS manufacturers seemingly arbitrarily used the reserved interrupt pointers documented in Intel's October 1979 publication The 8086 Family User's Manual (pdf, source page), which on page 2-26 says:

The dedicated and reserved portions of the interrupt pointer table (locations 0H through 7FH), however, should not be used for any other purpose to insure proper system operation and to preserve compatibility with future Intel hardware and software products.

After the first such clash, the INT 5 clash BUG became widely known, even documented in The MS-DOS Encyclopedia, they should have realized that what they are doing would likely cause more and more clashes in the foreseeable future if Intel continued to add more instructions and internal exceptions in future processors. Then the cost would be much and much higher because the IVT function can't be easily changed due to backward compatibility.

So I think all the BIOS should have abandoned their default IVT configuration at the time the INT5 clash bug first happened, and followed Intel's manual to correct their fault immediately, don't use the reserved interrupt anymore, in order to avoid more interrupt clashes in the future. But obviously, they didn't.

It is absolutely impossible that I am cleverer than all the engineers of the past decades, so I was curious what exactly did the engineers think about this and did the BIOS exception handler really have to check each time on each interrupt with both internal and external, after all, there are so many clash, do they have to check each time? Would the cost be too high?

Fortunately, the Windows operating system became popular, so most computers are running in protected mode. If Windows hadn't evolved to what it was, MS-DOS continued to be widely used for many years, and Intel continued to roll out new real-mode exception codes, would they be facing this conflict all the time?

There are no ifs in history, and it seems that their willful actions did not cause more serious consequences seems to be just the blessing of God. Of course, the above is just my guess, which is why I want to find some people who have lived through that era to discuss this unimportant issue. I was born after 1990, I don't know anything about what happened before, it's all my speculation.

23
  • 8
    That surely is an example of the programmer (of the BIOS) failing to understand the hardware environment. (Not to mention the original sin of failing to understand the meaning of 'reserved'). But don't mind me; I'm just a grouchy programmer.
    – dave
    Commented May 25, 2022 at 23:12
  • 6
    @another-dave : You mean like IBM missing the fact that vectors 0x05-0x01f were CPU reserved (by Intel) and they chose to map the PIC into the middle of it lol. Commented May 25, 2022 at 23:23
  • 3
    Yup. "reserved' does not generally mean 'available for any use you want to make of it' :-)
    – dave
    Commented May 25, 2022 at 23:29
  • 4
    This was posted first on SO, where I edited it to ask what I think was being asked (how does the BIOS disambiguate int 10h from an x87 FPU exception and stuff like that): How does the BIOS distinguish Interrupt(08h-12h) from INT instructions, vs. actual exceptions inside the CPU? Unfortunately the OP hasn't responded to anyone there or even posted a link to the re-ask. Also hasn't updated this version with any clearer phrasing from SO. Commented May 25, 2022 at 23:35
  • 3
    I've deleted the discussion about "lacking research effort". If you think we should change our standards for what counts as "enough research", please take it to Retrocomputing Meta. (And if you do, please be nicer about it.)
    – wizzwizz4
    Commented May 26, 2022 at 12:14

3 Answers 3

22

As mentioned in the 8086/8088 manuals, even if first 32 interrupt vectors were marked reserved for future use, only the first 5 were actually used by CPU exceptions (Divide Error, Single Step, NMI, Breakpoint and Overflow), so they were basically free for any other use and they were used by BIOS.

And because 80286 introduced new exceptions, approximately first 17 interrupts were used as CPU exceptions for various purposes, such as the Print Screen int 5 as BOUND exception, int 8 (IRQ0) as Limit exception, and int 10h as Processor Extension Error interupt.

So, this meant that the BIOS interface originally made for older CPU now had to cope with the new CPU exceptions.

Any existing OS or program that is well made would never cause or trigger these exceptions. And any OS or program meant for newer CPUs would know that certain operations might trigger certain exceptions so they might handle them properly.

So for example, if an OS or program is so sophisticated that it uses the BOUND instruction, it must also set the int 5 to point to an error handler for BOUND exception.

And if a program tries to access a word at offset address FFFFh, it will trigger the Segment Overrun exception. However, it was likely very dubious program anyway if it did that, and the programming guidelines for making sure how to make sure old 8088/8086 programs run properly on a 80286 did have suggestions how to avoid triggering the exceptions.

The PC/AT BIOS simply used the real mode interrupts as before, and the BIOS was not compatible with protected mode OS or programs anyway, so protected mode programs had to install their custom handlers anyway, and to avoid overlap between the CPU exceptions and hardware interrupts, the interrupt controller could easily be reprogrammed to move the hardware interrupts to start later after the reserved vectors.

It all becomes more complex with 386 and later CPUs. Basically they should run real mode programs just like a 286 anyway, so there should be no problems. However if you introduce a memory manager such as EMM386, it switches the processor into protected mode, and basically EMM386 becomes the protected mode "supervisor" program which creates a Virtual 8086 Mode environment where the DOS continues to execute. So basically, all real mode stuff used by BIOS or DOS or programs run in the sandbox and therefore pressing the Print Screen button just executes the real mode interrupt 5 which is the BIOS-installed print screen routine. If some program tries to execute the BOUND opcode, that will also trigger execution of interrupt 5 in the VM86 sandbox, so unless user has installed a custom handler it will try to print the screen. If such a BOUND instruction is made in the Protected Mode, it will trigger the int 5 of the protectrd mode supervisor task, which is likely to be set up to point to actual BOUND exception handler. Also assumption is that if you make a 386 Protected Mode OS, you know what you are doing and set up the interrupts and exception handlers in a way that they can work, so BIOS int 5 is not callable any more as 16-bit code is not compatible under PMode anyway.

12

So I think all the BIOS should abandoned their default IVT at the time INT5 clash bug first happened, and follow Intel's manual to correct their fault immediately, don't use the reserved interrupt anymore

Backwards compatibility was prioritized over forwards compatibility. Once PCs with that design mistake were widespread, and a 3rd-party software ecosystem sprang up, the damage was done.

bound didn't exist until 186/286, at which point lots of existing binaries using interfaces like int 10h BIOS keyboard/screen existed, on floppy disks in the hands of users where they couldn't be replaced. (I think a lot of programs used BIOS calls directly, not just making DOS calls, so it's not like changing MS-DOS and the BIOS together would make this pain-free.)

286 manuals were dated February 1982 (see comments: earlier than 186 manuals). Really only 286 is relevant, and manuals (and then hardware) would be the first sign of a problem for BIOS/DOS maintainers. Or any advance details Intel might have put out ahead of a full manual, but this was pre-Internet, less easy to publish a quick advance notice thing the way Intel currently updates their "future extensions" manual ahead of new ISA extensions appearing in real silicon.

Also note that MS-DOS was developed separately from the BIOSes of different vendors, I think, so there wasn't a single corporation in control of both sides of the int xx software interface to even try to coordinate a change or compat mechanism, even considering just the BIOS and DOS itself. Let alone 3rd-party software.


A new computer with a different BIOS in its ROM couldn't run most old software if it didn't handle those and other Intel-reserved interrupts the same way. There isn't a clean migration path; the BIOS would always need to handle old programs, so there's little benefit to having a new style, unless you can come up with some mechanism of disabling compatibility when not needed.

Breaking backwards compatibility would defeat the entire purpose and selling-point of x86 PCs. Software distribution was binary only, and there wasn't an Internet to make it easy to get a new version to run on new hardware.

A new computer that can't run your existing software would not sell well compared to one that could, despite making things easier for developers.


For a long time, x86 PCs they weren't the fastest CPUs around, not until market domination and years of development effort got us to modern x86 having one of the highest per-thread performance, pulling ahead of competitors like MIPS and Alpha.

Obviously changing a few interrupt numbers is vastly different from porting to a new ISA running a new OS, but from the user's point-of-view, you'd need a new version of almost everything either way. Except for a few programs that only used DOS calls or direct hardware access, no BIOS calls.


more clash in the foreseeable future if Intel continued to add more instruction set and internal exceptions in the future processor type.

Intel keeps tabs on how commercially-important software is (ab)using their CPUs and designs CPUs to cater for that. For example, despite not documenting some 8086 instructions like salc (works similarly to sbb al,al), it's still supported in 16/32-bit modes on current CPUs. Same for bsf/bsr leaving the destination unmodified when the source is zero; AMD documents that, Intel just implements but their documentation has been saying for years / decades that the destination gets an undefined value in that case.

Even beyond specific instructions, there's TLB page-walk coherency to cater to Win95. (Something modern Intel CPUs still do, although AMD dropped it with Bulldozer. https://blog.stuffedcow.net/2015/08/pagewalk-coherence/). As Andy Glew (one of the architects of Intel P6) said in a Stack Overflow answer, All or almost all modern Intel processors are stricter than the manual. (For self-modifying code specifically, but that's also true in general.)

Anyway, my point is, with IBM-PC and compatibles becoming one of the major users of Intel CPUs, I think Intel would have tried to avoid creating un-solvable problems in new CPU designs. For example, any exception that was expected to happen during normal operation in real mode (not just as a bug) could avoid the numbers already used for common BIOS calls.

Although maybe Intel in the past wasn't as aware of how software got used, given that x87 FPU error got assigned to int 10h, leaving 0Fh reserved. To be fair, normally you leave x87 exceptions masked, so that particular one wouldn't be much of a problem even for developers.


Later software could avoid the problem, and BIOS ints didn't work in protected mode

Software could remap IRQs to not conflict with CPU exceptions, so later OSes did that to disambiguate hardware device interrupt from CPU exceptions.

After switching to protected mode, int 10h and so on didn't work anyway. OSes that switch to protected mode would often want their own drivers anyway for performance reasons, instead of going through slow BIOS APIs for everything. Some individual programs that ran under a real-mode OS like DOS would I think switch back to real mode around individual BIOS calls; IDK if later BIOSes could have standardized a way to make services available to protected-mode programs, perhaps with a required set of GDT entries, but probably software vendors didn't want to trust possibly-buggy BIOSes.

Clashes possible in real mode

Switching to protected mode and setting up your own IDT (without entries pointing to int xx BIOS handlers since they wouldn't work) avoids that side of the clash. And remapping IRQs takes them out of that range.

But what about software that does run in real mode on a 286 or 386? Some of the new exception numbers can only happen in protected mode, like 0x0e #PF Page Fault or 0x0a Invalid TSS. Probably also 0x08 Double Fault - real mode can't fail to load a new CS:IP from the IVT, unlike Protected Mode with its more complex IDT (Interrupt Descriptor Table).

Others only happen if you enable them, like 0x11 #AC Alignment Check, or 0x05 BOUND if you run that instruction. 0x10 x87 FPU and 0x13 SIMD Floating-point exceptions can only happen if you unmask them by changing bits in the x87 control word or MXCSR respectively. (And SIMD FP didn't exist until SSE1 with Pentium 3, although it is usable in real mode if you enable the right bits in CR0 and CR4.)

Presumably 8086 + 8087 couldn't raise int 0x10 even with exceptions unmasked, only asynchronously record FP exception state in the FP status register. (Original 8087 snooped the bus for instruction fetches by the 8086, decoding and executing FP opcodes. An fwait was required after each one to stop the CPU from getting ahead, since there was no way for the 8087 to ask the 8086 to stall if it wasn't done executing the first FP instruction.) I don't know at what point the HW evolved to work like it does now.

But some exceptions really can happen in real mode. Typically due to software bugs, but those can happen while developing software, or if something else corrupts the memory of a running program.

  • 0x06 #UD invalid opcode, always possible if you jump through a wild pointer. Or intentionally generated during CPU detection code, or even by programs that emulate missing instructions on old CPUs.

    (The latter strategy isn't compatible with 8086, though: every byte sequence executed as something, often an existing instruction as in the shl/sal non-standard encoding case. 8086 had no #UD. But it could be used in programs that ran in real-mode on 286/386/486, optimized for a later CPU than 286.)

  • 0x07 Device Not Available (WAIT/FWAIT) - presumably can happen if you try to do x87 stuff on a system without it. In a program that has code paths to use x87 if available, bugs could lead to those running when they shouldn't.

  • 0x0c #SS Stack Segment and 0x0d #GP General Protection Fault: from trying to load a word (2 bytes) at offset 0xffff in SS (#SS) or any other segment (#GP). Or if using 32-bit addressing modes to allow using any register and with scale factors, like [eax + esi*2], bad values (especially garbage in the high half) can easily access outside the 64K segment limit if you didn't use unreal mode. (i.e. switch to protected mode to set a higher limit for that segment, then disable protected mode again.)

    Most other #GP causes are privilege-related, but real mode is like CPL=0 (ring 0 kernel mode). If you want to run something destructive like invd in real mode, it just happens. (Unlike virtual-8086 mode where it does #GP).

  • 0x12 Machine Check - usually only from hardware failure, or like ECC memory detecting an uncorrectable error.

Thanks to folks in comments for discussion of what could still be a problem in real mode.

12
  • 1
    I can’t check now, where was the FPU exception assigned in the original 8087? Was this before the PC architecture was devised? Commented May 26, 2022 at 17:31
  • 1
    As far as the evolution of the PC is concerned, I think it’s fine to ignore the 186 (at least for considerations such as bound & co.) in favour of the 286 which was released at the same time. Commented May 26, 2022 at 19:10
  • 1
    My “Introduction to the …” Intel manuals are dated March 1983 for the 80186 manual, February 1982 for the 80286 manual, both of which are first editions, so if those are the earliest publication dates, the 286 programming documentation was available a year before the corresponding 186 documentation. See also the Bitsavers 186 docs and 286 docs. Commented May 26, 2022 at 19:56
  • 1
    @StephenKitt: Thanks, updated accordingly. If anyone has refinements to my answer, feel free; I'm not super interested in early PC history so this answer is an overview in broad strokes, and guesswork on actual motivation. (The more I read about x86 real mode BIOS/DOS shenanigans, the more it firms up my impression that the whole thing was a rickety mess of not-great API / ABI designs that increasingly accumulated duct tape and twice. Finding out that even the original design conflicted with documented reserved interrupt vectors certainly makes me more glad I had no part in it.) Commented May 26, 2022 at 20:11
  • 2
    Undefined opcode can be used "normally" by programs for CPU detection if they want to know that. Or emulating instructions not available on earlier CPUs.
    – Justme
    Commented May 28, 2022 at 19:56
6

An Interrupt is an Interrupt is an Interrupt

When an interrupt hits, it hits, no matter if initiated

  • by an external interrupt vector provided by a PIC,
  • by software via INT instruction,
  • or internal via an exception.

The later usually as well a side effect of an instruction e.g. out of range for BOUND or page invalid for memory management.

In either case, all the CPU does, is fetching the address associated with that vector and starting execution from there.

After that it's up to whatever software is handling it. Some Handware my or may not support that process and be questioned (checked) in that process.

For example with INT 5, BIOS of 186+ CPUs would

  • check if a PIC (Interrupt Controller) has issued it (not on IBM-PC), if not
  • analyze the calling code for a BOUND instruction, or if not
  • perform print screen

It also doesn't matter if it's a 80186 or a Pentium or anything inbetween.

Isn't it obvious ... in Hindsight!

I want to know the historical reason why the BIOS manufacturers arbitrarily

First of all, it wasn't BIOS manufacturers, but specific the IBM PC's ROM-BIOS. Which of course had to be obeyed by anyone wanting to make a compatible (ROM-)BIOS. Manufacturers of other 8086 machines did not follow this for their default setup - but had to do so as well for the sake of IBM compatibility.

IBM's choices overruled Intels recomendations.

violated Intel's manual,

What a hard word. Manuals are descriptions and recommendations. It's always up to the designer if these are relevant for his project or not. The IBM PC was a rather quick and low profile project, not a strategic planned milestone for years to come.

It's common practice to use resources starting with the lowest available - especially if these resources are used for internal, hardware related low level functions, functions to be most likely changed anyway in future machines (*1). No application software was intended to use the ROM-BIOS interface.

Paterson in turn had a different motivation when crating 86DOS. It was supposed to run on any 86 machine, assuming as little about the environment as possible - all hardware dependence was to be encapsulated by the BIOS (*2). Thus DOS usage of Interrupt Vectors starts at 20h.

After the first such clash, [...]

Which only happened years after the IBM-PC and its ROM-BIOS was introduced.

they should have realized that what they are doing would likely cause more and more clashes in the foreseeable future if Intel continued to add more instructions and internal exceptions in future processors.

I'm pretty sure they did, but that was after the ROM-BIOS was introduced with the IBM PC. The timeline looks a bit like this:

  • 1980: IBM-PC gets designed - including it's ROM-BIOS
  • 1981: IBM-PC gets introduced, software using those vectors was written
  • 1982: Intel introduces the 80186 and 286, both using INT 5 and INT 6
  • 1984: IBM introduces the PC/AT using a 286

At the time the IBM-PC's ROM-BIOS was designed, there was only the 8086/88, and it only used the vectors 0..4 for it's own purpose. 5 was the first unused, so fine for any other use by low level support software. And that's what the ROM BIOS is about. It was never intended to be used by application software. It's purpose is to provide abstraction layer services to any OS, which in turn forwards services to application software.

Any clash only happened years after the ROM-BIOS was out there and due it being used by application software.

Then the cost would be much and much higher because the IVT function can't be easily changed due to backward compatibility.

Wich is exactly the reason why it was kept that way. Otherwise the IBM PC/AT, which is essentially the base for all modern PC, would have been incompatible with the PC and PC/XT. Making it just another incompatible machine like there were quite a lot in the mid 1980s.

It's important to keep in mind that at that point in time (1984) the IBM-PC wasn't the all mighty standard it is today. Not even close. Sirius did sell more 8088 PCs in Europe than IBM, NEC sold more PC-98 in Japan than IBM sold worldwide, not to mention many other manufacturers with x86 based but not ROM-BIOS compatible machines.

So I think all the BIOS should have abandoned their default IVT configuration at the time the INT5 clash bug first happened, and followed Intel's manual to correct their fault immediately,

What fault? It was a valid decision in 1980. Arguing in hindsight one could as well ask why Intel didn't follow IBM's spec.

don't use the reserved interrupt anymore, in order to avoid more interrupt clashes in the future. But obviously, they didn't.

Well, if all had went the way as usual within the professional computing world, noone would have accessed ROM-BIOS services directly, and IBM would have been free to rearrange entry points - with new OS versions following that. But ROM-BIOS has been used directly and IBM's only advantage over competition was to be upward compatible with their prior machine, the PC-XT. No chance IBM would have alienated their user base.

Just check what happened as IBM did the PS/2 line.

It is absolutely impossible that I am cleverer than all the engineers of the past decades,

No need to. the factor is called 'Hindsight'. Afterwards everyone can be more forward looking :=)

Fortunately, the Windows operating system became popular, so most computers are running in protected mode. If Windows hadn't evolved to what it was, MS-DOS continued to be widely used for many years,

If it had gone the way IBM planned, not Windows, but OS/2 would have covered everything - eventually way better than Windows. The development started at Microsoft as CP/DOS with the introduction of the PC/AT. It would have virtualized all ROM-BIOS access, removing the clutter. Windows was just a side project at MS, but when MS noticed it's sales, they jumped the OS/2 wagon.


*1 - A practice IBM used since the 1960s with their /360 and other machines. The 'real' hardware is hidden beneath an abstraction layer even an OS, assuming to run on bare metal, doesn't see much of that. It gets a standardized machine view, no matter if there's a 16 or 32 Bit processor. Heck, even features like paging tables are virtualized. Only a small number of hardware dependent patches for OSes were needed for some models/families. Thus hardware designers were free to change and improve.

*2 - The real BIOS supplied with DOS. The mentioned interrupts are not introduced by BIOS, but by IBM's ROM-BIOS

11
  • 2
    Interrupt entry logic is more than fetching an address from the vector table into the program counter. Other universal expectations include saving the current values of registers (at least the program counter which is being overwritten by the ISR address) and changing the interrupt mask level.
    – Ben Voigt
    Commented May 26, 2022 at 21:34
  • 3
    @Rafzahn: An answer that makes wrong claims that don't matter to the question is of course less bad than being wrong in ways that do matter to the question. But wrong is wrong and will earn you comments suggesting that you correct your answer. I think you meant to say that with shared interrupts, the CPU gives them all the same treatment. And it's fine to emphasize that it fetches the same handler address from the same interrupt vector table. But not correct to claim that "this is all that the CPU does".
    – Ben Voigt
    Commented May 29, 2022 at 22:14
  • 4
    That application software was not supposed to use the BIOS but only DOS is not only not common sense, it's ridiculous. You're saying, for example, that PC-DOS applications were not expected ever to use cursor addressing (Int 10h function 2) or even clear the screen (INT 10h function 6) because those were BIOS calls, and PC-DOS did not provide its own API for this. And there were certainly many other computers for years on either side of the IBM PC, from the PET and Apple II to MSX and the Fujitsu FM-7, that expected you to use the ROM BIOS even when running a DOS.
    – cjs
    Commented May 30, 2022 at 0:22
  • 4
    Also, given that you have no evidence that IBM ever told anybody "Don't use BIOS calls in your PC-DOS application programs," the "common sense" here is that they published that entire API because they did expect people to use it. If you still disagree, I suggest you post a question here on RCSE about this which will clarify what you're really saying and soon enough generate some answers from others in the community.
    – cjs
    Commented May 30, 2022 at 0:27
  • 1
    As an aside, IBM did try to replace the ROM BIOS with an incompatible firmware in some PS/2 models - ABIOS was meant to replace the PC-compatible ROM BIOS for PS/2 machines that didn't run DOS, but only ran OS/2. Commented Feb 8, 2023 at 16:22

You must log in to answer this question.

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