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 int
s 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.
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.