-
Notifications
You must be signed in to change notification settings - Fork 0
bios services
The firmware's built-in routines — video, disk, and keyboard — that MyOS-Simple calls through software interrupts while it still lives in real mode.
Before an operating system can talk to hardware directly, it has to be an operating system. The boot sector starts with nothing: no drivers, no display code, no disk routines. The BIOS solves this chicken-and-egg problem by exposing a library of ready-made hardware routines, callable through software interrupts, that work the moment your code starts running in real mode.
MyOS-Simple leans on three of them: int 0x10 (video), int 0x13 (disk), and
int 0x16 (keyboard).
The mechanism is the Interrupt Vector Table at
physical 0x00000: 256 entries, each a far pointer to a handler. When you
execute int N, the CPU looks up entry N and calls the routine it points to.
The BIOS fills these entries during POST, so by the time our
code runs, int 0x10, int 0x13, and int 0x16 all resolve to firmware
routines.
You select which service within an interrupt by loading a function number
into AH, then pass arguments in the other registers, and finally execute the
int. The convention is simple but unforgiving — every register matters.
mov ah, 0x00 ; function: set video mode
mov al, 0x03 ; argument: mode 3 (80x25 text)
int 0x10 ; invoke the BIOS video servicehelloworld-os-asm/main.asm:15-17
💡 Tidbit: BIOS interrupt numbers are not the same as CPU exception numbers.
int 0x10,0x13,0x16are firmware conventions — entries in the IVT that the BIOS chose to populate. In protected mode those same vectors get reused for the kernel's own interrupt descriptor table, which is one reason the BIOS services stop working there.
⚠️ Caveat: BIOS services are 16-bit real-mode only. The instant the bootloader setsCR0.PEand enters protected mode, all ofint 0x10/0x13/0x16become unusable. Everything in this article must happen before that switch. After it, the kernel drives hardware directly — e.g. writing the VGA text buffer at0xB8000and reading the PS/2 keyboard through I/O ports.
The video BIOS is the busiest service in MyOS-Simple. It is used by both stage 1 builds and by the bootloader to clear the screen before the mode switch.
mov ah, 0x00
mov al, 0x03 ; AL = mode number
int 0x10AL = 3 selects the standard 80×25, 16-colour text mode. Calling this also
clears the screen as a side effect, so MyOS-Simple uses it as a one-instruction
"reset the display" at the start of the boot sector
(helloworld-os-c/boot.asm:29-31).
mov ah, 0x02
mov bh, 0 ; BH = display page (0)
mov dh, 12 ; DH = row (0-24)
mov dl, 34 ; DL = column (0-79)
int 0x10helloworld-os-asm/main.asm:20-24
Stage 1 uses this to centre its messages: row 12, column 34 for
"Hello, World!", and row 14, column 31 for the signature line. The colour
build uses it both to place each label and, inside its print loop, to advance the
cursor one column after each character (main_color.asm:172-174).
mov ah, 0x06
mov al, 0 ; AL = lines to scroll (0 = clear whole window)
mov bh, WHITE_ON_BLUE ; BH = attribute for blanked cells
mov cx, 0x0000 ; CX = top-left (row 0, col 0)
mov dx, 0x184F ; DX = bottom-right (row 24, col 79)
int 0x10helloworld-os-asm/main_color.asm:28-33
This is how the colour build paints its blue background: scrolling a window by
zero lines with AL = 0 blanks the whole region, filling every cell with the
attribute in BH. The corner 0x184F decodes as row 0x18 = 24, column 0x4F = 79 — the bottom-right of an 80×25 screen.
💡 Tidbit:
DX = 0x184Fpacks two coordinates into one 16-bit register: the high byte (DH = 0x18 = 24) is the row, the low byte (DL = 0x4F = 79) is the column. Many BIOS calls pack row/column intoDH/DLthis way, which is why the bottom-right corner of a text screen is the memorable constant0x184F.
mov ah, 0x09
mov al, <char> ; AL = character to write
mov bh, 0 ; BH = page
mov bl, <attr> ; BL = attribute (colour)
mov cx, 1 ; CX = repeat count
int 0x10helloworld-os-asm/main_color.asm:166-169
Unlike teletype output, AH = 0x09 writes a character with a colour
attribute but does not move the cursor — that is why the colour build
follows each write with an AH = 0x02 cursor advance. The attribute byte is
(background << 4) | foreground; see VGA text mode for the
full colour table.
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
rethelloworld-os-asm/main.asm:55-63
This is the workhorse of the monochrome build. AH = 0x0E prints the character
in AL and advances the cursor, handling line wrap and scrolling for you —
behaving like a simple terminal ("teletype"). Paired with lodsb, it makes a
classic null-terminated string printer.
💡 Tidbit:
lodsb("load string byte") loads the byte atDS:SIintoALand incrementsSIin one instruction. Followed byor al, al / jz(test for the null terminator) it is the beating heart of nearly every real-mode print loop — including both of MyOS-Simple's stage 1 builds and the bootloader'sprint_16error routine (helloworld-os-c/boot.asm:93-104).
From stage 2 onward, the boot sector is
too small to hold the kernel, so it reads the kernel off disk with int 0x13,
function AH = 0x02 (read sectors):
mov ah, 0x02 ; BIOS read sectors
mov al, dh ; Number of sectors
mov cl, 0x02 ; Start from sector 2
mov ch, 0x00 ; Cylinder 0
mov dh, 0x00 ; Head 0
int 0x13
jc disk_errorhelloworld-os-c/boot.asm:70-77
The destination is ES:BX, the geometry is CHS (cylinder/head/sector), and
the carry flag signals an error. This service is involved enough to have its own
page: disk loading with INT 13h.
Both stage 1 builds wait for the user with int 0x16, function AH = 0x00
(blocking read):
.wait_key:
mov ah, 0x00
int 0x16
cmp al, 'q' ; lowercase q
je shutdown
cmp al, 'Q' ; uppercase Q
je shutdown
jmp .wait_keyhelloworld-os-asm/main.asm:40-47
AH = 0x00 blocks until a key is pressed, then returns the BIOS
scancode in AH and the ASCII value in AL. The monochrome build only
inspects AL (looking for q/Q to quit); the colour build compares AL
against '1'–'5' and space to recolour the screen
(helloworld-os-asm/main_color.asm:63-101).
⚠️ Caveat:int 0x16is the easy way to read the keyboard, but it only works in real mode. Once MyOS-Simple reaches its interactive shell in protected mode, it must talk to the 8042 keyboard controller directly through I/O ports and decode raw scancodes itself — there is no firmware to do it anymore.
| Interrupt | AH |
Service | Key registers |
|---|---|---|---|
0x10 |
0x00 |
Set video mode |
AL = mode (3 = 80×25 text) |
0x10 |
0x02 |
Set cursor position |
DH = row, DL = col, BH = page |
0x10 |
0x06 |
Scroll / clear window |
AL = lines, BH = attr, CX = TL, DX = BR |
0x10 |
0x09 |
Write char + attribute |
AL = char, BL = attr, CX = count |
0x10 |
0x0E |
Teletype output |
AL = char (advances cursor) |
0x13 |
0x02 |
Read disk sectors |
AL = count, CH/CL/DH = CHS, ES:BX = dest |
0x16 |
0x00 |
Blocking key read | returns AH = scancode, AL = ASCII |
A fuller list of I/O port and BIOS details is collected in the I/O ports reference.
- Real mode — the only environment where these services exist
- The boot process — where each call fits in the boot timeline
-
Disk loading with INT 13h — the full
int 0x13story -
VGA text mode — the attribute byte and direct
0xB8000access - PS/2 keyboard (8042) — reading the keyboard after the BIOS is gone
- Scancodes — decoding raw key codes in protected mode
- Stage 1: assembly boot — the builds shown above
- I/O ports reference
- Home
Stages
- 1 · Assembly boot
- 2 · C protected mode
- 3 · Interactive shell
- 4 · Clock / processes / calc
- 5 · Stabilized release
Concepts — boot
Concepts — protected mode
Concepts — hardware
Concepts — OS services
Reference
- Memory map
- I/O ports
- GDT descriptor format
- Scancode tables
- Command reference
- Toolchain & build
- Glossary
Guides