8086 Interrupts: Sources, the Vector Table, and Handler Execution

A closer look at 8086 interrupts

Posted by Bruce Lee on 2023-08-01

About Me

Welcome to my blog! This is where I collect my observations and notes on programming and technology. The main subjects range from implementation details to broader ideas about programming.

Main Topics

  • Engineering Projects: Exploring implementation details and how technical systems work.
  • C/C++: Notes on language features and programming techniques.
  • The Programmer’s Perspective: Ideas about developing a career and a way of thinking as a programmer.

For more, visit the categories page.

Contact

If you have questions or would like to discuss something, please get in touch through the About page.

Thank you for reading and for your support. I hope these notes help you on your own technical journey!


Introduction

An interrupt or exception transfers control to a handler in response to an event. This is more specific than the general ability to jump to another instruction address. These notes examine internal events, external requests, the 8086 vector table, and the BIOS/DOS routines built on that mechanism.

Where events originate

An internal condition, such as division by zero or a quotient that does not fit, is detected by processor execution logic. External devices signal requests through hardware interfaces. Maskable external requests are subject to interrupt-enable state.

The original notes ask how the processor physically creates an exception signal and leave detailed circuit design for future study. One clarification is useful: an external programmable interrupt controller is not required to detect a divide error inside the CPU. Those are different event paths.

From a vector number to a handler

In the original 8086 real-mode arrangement, the interrupt vector table occupies physical 00000h through 003FFh: 256 entries of four bytes each. An entry stores a 16-bit instruction offset followed by a 16-bit code segment.

For vector n, the CPU reads the offset from address 4*n and the segment from 4*n + 2. These expressions locate the stored pointer; they are not themselves the values loaded into IP and CS. The processor transfers to the handler addressed by that pointer, rather than executing the table bytes as instructions.

For interrupt entry, the CPU saves the required return state on the stack, including FLAGS and the return code address, and adjusts interrupt/trace state. IRET restores the corresponding return state. Exact saved-PC behavior for exceptions depends on the processor generation and exception class.

Vector zero is associated with divide error. Executing int 0 explicitly invokes that vector too, but software invocation and an actual division fault need not have identical fault/restart semantics.

A familiar DOS service sequence is:

1
2
mov ax,4c00h
int 21h

AH = 4Ch selects process termination and AL = 00h supplies its return code. The original 4chh spelling is a typo.

Interrupt vector table

External devices and I/O ports

Interface cards, motherboard controllers, and devices such as CMOS/RTC hardware expose registers through their interfaces. Depending on the architecture and device, those registers may be memory-mapped or placed in a separate I/O space. x86 supports distinct I/O-port instructions.

In the historical PC keyboard arrangement, the controller exposes data at port 60h and requests service through IRQ1 and the interrupt controller. The external controller and the keyboard’s own scanning electronics have different roles.

Reading and writing ports

1
2
out 60h,al
in al,60h

IN moves data into the accumulator; OUT moves it out to a port. This direction explains the operand order. For the discussed 16-bit forms, AL transfers a byte and AX transfers a word.

Correction: the source associates AL with ports below 256 and AX with higher ports. Data width does not determine port-number range. An immediate encodes a port from 0 to 255; DX supplies a port number from the wider 16-bit space. Either permitted data width can be used with the appropriate form.

Why interrupts can be masked

Masking temporarily prevents delivery of ordinary maskable requests. This can protect a short critical update, avoid unwanted nesting, or maintain priority while handling another event. It does not necessarily erase a pending device request.

Stack initialization is an instructive example:

1
2
3
mov ax,0200h
mov ss,ax
mov sp,0

The SS and SP updates belong together so an interrupt does not use an inconsistent stack. x86 provides a specific interrupt-inhibition window after loading SS; this should not be generalized into a claim that all possible events are excluded or that arbitrary multi-instruction updates become atomic.

Keyboard IRQs are maskable. NMI arrives through a separate mechanism and is not controlled by IF. It is often associated with urgent hardware conditions, though it can also serve other purposes such as debugging. Interrupt priority depends on the processor and controller rules.

IF, STI, and CLI

Flags register

IF is the interrupt-enable flag for maskable hardware interrupts. STI sets it and CLI clears it, subject to the execution mode’s privilege and timing rules. Software interrupts and synchronous exceptions are not simply disabled by clearing IF. Not every instruction changes every flag.

Vector numbers and ownership

An eight-bit vector number has 256 possible values, zero through 255. Later x86 architectures reserve the low range for architectural exceptions, while historical PC firmware and DOS use conventions that can overlap differently. Apply the rules for the actual generation and environment.

The source identifies 0000:0200–0000:02FF as unused space for an exercise. That is not a universal guarantee: this region contains vector entries, and installed software may use them. A real installation must preserve or allocate vectors appropriately.

Familiar internal and software-triggered events

  • Vector 0: divide error.
  • Vector 1: debug/single-step facilities, used by debuggers to regain control.
  • Vector 3: breakpoint, commonly generated by the dedicated breakpoint instruction.
  • Vector 4: overflow when INTO executes with OF set in supported legacy modes. INTO is unavailable in 64-bit mode; calling it generally obsolete because of performance is not the precise distinction.
  • INT n: an explicit software request to invoke vector n.

Software and hardware interrupts differ in their trigger. A program executes INT; an external device raises a request. The handler entry mechanism can still share a vector-table organization.

BIOS boot and handler installation

For the original 8086 reset model, execution begins at FFFF:0000, physical FFFF0h, commonly containing a jump into firmware. Later x86 reset details differ. Firmware performs hardware initialization and installs its service vectors. A legacy bootstrap routine then selects a boot device, loads an appropriate boot sector, and transfers control to a loader that brings in the operating system.

BIOS is firmware stored in ROM or flash, not ordinary writable RAM. The original suggestion of simply rewriting instructions at FFFF:0000 requires a suitable firmware or emulator modification mechanism; a normal memory store does not generally accomplish it.

BIOS int 19h

This is the familiar legacy bootstrap service. It should not be assumed to be literally the first interrupt firmware ever invokes during initialization.

BIOS int 10h: video services

Function 2 sets the cursor position:

1
2
3
4
5
mov ah,2
mov bh,0
mov dh,5
mov dl,12
int 10h

This selects page zero, row five, column twelve. Function 9 writes a character and attribute:

1
2
3
4
5
6
mov ah,9
mov al,'L'
mov bl,7
mov bh,0
mov cx,3
int 10h

The example requests three copies of L with attribute seven. Many BIOS and DOS services select a function through AH, but that is an interface convention to check for each service, not an invariant of all interrupts.

Text attributes

BIOS int 13h: disk services

The traditional CHS interface addresses cylinders, heads, and sectors. Cylinder and head numbering starts at zero; sector numbering starts at one. The source’s two-head description fits a particular disk geometry, not every device.

1
2
3
4
5
6
7
8
; ES:BX points to the destination buffer.
mov ah,2
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov al,1
int 13h

This requests one sector using the indicated drive and CHS values. Function 3 writes instead of reading, with ES:BX pointing to source data. Real code must respect geometry, buffer limits, and the returned status; the snippet illustrates parameter placement only.

BIOS int 9h: keyboard IRQ handling

In the legacy mapping considered here, IRQ1 invokes the keyboard handler at vector 9. Keyboard scanning electronics detect key transitions and send scan codes to the host controller. The host controller makes data available at port 60h and requests an interrupt.

For many ordinary keys in scan-code set 1, pressing produces a make code and release produces the make code plus 80h. Extended keys and other scan-code sets have different sequences, so this is not a universal keyboard encoding rule.

The source’s make-code table, expressed with standard key names, is:

Key Code Key Code Key Code Key Code
Esc 01 U 16 H 23 B 30
1–9 02–0A I 17 J 24 N 31
0 0B O 18 K 25 M 32
Minus 0C P 19 L 26 Comma 33
Equals 0D Left bracket 1A Semicolon 27 Period 34
Backspace 0E Right bracket 1B Quote 28 Slash 35
Tab 0F Enter 1C Backtick 29 Right Shift 36
Q 10 Ctrl 1D Left Shift 2A Keypad asterisk 37
W 11 A 1E Backslash 2B Alt 38
E 12 S 1F Z 2C Space 39
R 13 D 20 X 2D Caps Lock 3A
T 14 F 21 C 2E F1–F10 3B–44
Y 15 G 22 V 2F Num Lock 45
Scroll Lock 46 Keypad minus 4A End 4F Delete 53
Home 47 Left 4B Down 50
Up 48 Right 4D Page Down 51
Page Up 49 Keypad plus 4E Insert 52

Navigation and Print Screen interpretation can involve prefixes and keyboard layout/state; the compact table does not describe every sequence.

The handler reads the scan code, updates modifier/toggle state, and places suitable character events into the BIOS keyboard buffer with their corresponding character codes. A traditional state byte is located at 0040:0017. A custom handler can replace or chain this behavior when installed correctly.

BIOS int 16h: consuming keyboard input

The hardware handler fills the buffer; an application still needs to read it. Function zero waits for a key event:

1
2
mov ah,0
int 16h

It returns the scan code in AH and the corresponding character code in AL for the applicable event. Conceptually, the routine waits while the queue is empty, retrieves its oldest entry, and advances the queue. The traditional sixteen-word circular buffer commonly leaves one slot unused, allowing fifteen queued entries.

Keyboard state

Keyboard state byte

Modifier and toggle keys affect state, although particular key combinations can also generate buffered events. The original article included a ChatGPT explanation of Scroll Lock: historically it influenced scrolling behavior, and some applications still assign it a role. In spreadsheets such as Excel, it can make arrow keys scroll the worksheet instead of moving the selected cell. Behavior is application-specific; consult the application’s documentation rather than assuming one global action.

DOS services

int 21h, function 4Ch

1
2
3
mov ah,4ch
mov al,00h
int 21h

This terminates the DOS process with return code zero.

int 21h, function 9

1
2
3
; DS:DX points to a dollar-terminated string.
mov ah,9
int 21h

This displays a string ending with $, allowing the program to use a DOS service instead of writing directly to video memory. The service’s terminator differs from the zero-terminated strings used in other examples.

For architectural interrupt details across later x86 modes, consult the Intel system programming manuals. The BIOS and DOS conventions above belong to the historical environment described in this article.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !