BIOS INT 10h AH=13h: Page-Aware Text String Output
Use BIOS INT 10h function 13h with correct far pointers, character counts, attributes, page selection, cursor flags, and adapter compatibility.
BIOS video interrupt INT 10h, function AH=13h, writes a bounded string at a text-screen position. It can select a display page, take a far pointer to a caller-owned buffer, apply one attribute to every character or consume an attribute byte per character, and optionally update the page’s cursor. That makes it more structured than a loop of teletype calls, but it remains a screen API: it does not honor shell redirection and it is not a general replacement for DOS standard output.
The IBM BIOS interface documents this service for PC XT BIOS dated January 10, 1986 and later, AT, EGA, PC Convertible, and PS/2 products. It states that unsupported implementations perform no action. Clones may implement it differently, so treat the documented target set as a compatibility boundary rather than a universal promise.
Register contract
The call uses AH=13h and the low two bits of AL to select the input format and cursor behavior. BH is the zero-based display page. BL supplies an attribute in character-only modes. CX is the number of characters, not a byte count for the interleaved character/attribute form. DH and DL specify the starting row and column. ES:BP points to the first byte of the string in the caller’s address space.
The four IBM-defined AL modes are:
| AL | Buffer format | Cursor result |
|---|---|---|
| 00h | Character bytes; BL is the shared attribute |
Not moved |
| 01h | Character bytes; BL is the shared attribute |
Moved |
| 02h | Character, attribute pairs | Not moved; alpha modes only |
| 03h | Character, attribute pairs | Moved; alpha modes only |
In the paired form, a count of n characters requires 2*n input bytes. The buffer is not NUL-terminated for this call; CX determines how many characters the BIOS consumes. An off-by-one count can read beyond a string, and a compiler’s near pointer is not interchangeable with the far ES:BP pointer.
The BIOS reference says carriage return, line feed, backspace, and bell are treated as commands rather than printable characters. That behavior is specific to the documented BIOS service and mode; do not assume those bytes are displayed as glyphs. If a program needs an exact byte-for-byte glyph stream, use a documented character-writing path and validate the control-character behavior of the target BIOS.
Building a safe call
Choose a known text mode first, query its geometry and active page, and check that the requested start row and column are within that mode. Compute the number of characters from the source data, verify the character-pair buffer size with a wider intermediate, then ensure the whole buffer is addressable through ES:BP. A far pointer cannot cross a segment boundary as though it were a flat pointer; arrange the data so its bytes are reachable under the target compiler and memory model.
An illustrative character-only call is:
; DS:message contains exactly message_length bytes.
; The caller has checked row/column and page for the active text mode.
push ds
pop es
mov bp, offset message
mov cx, message_length
mov dh, row
mov dl, column
mov bh, display_page
mov bl, text_attribute
mov ax, 1301h ; AH=13h, AL=01h: text bytes, update cursor
int 10h
This shows the register shape, not complete compiler syntax or a proof that the service exists on a particular machine. If message_length can be zero, handle that case explicitly. If the string length exceeds the remaining cells on the row, test whether the target BIOS wraps, scrolls, clips, or otherwise handles the boundary. Do not depend on behavior that the target’s documentation does not promise.
For per-character color in a text mode, prepare an interleaved buffer such as character, attribute, character, attribute. Set AL=02h or AL=03h, keep CX as the number of character cells, and verify both the pair count and the actual byte count. This makes it easy to accidentally pass twice the intended CX; a small test string with two contrasting attributes is a better validation than a large screen update.
Cursor and page semantics
The selected page is part of the call. A correct string can appear absent if it is written to an inactive page, and the visible page can change between the caller’s query and the write if another resident component is allowed to manipulate video state. Keep page identity in the application’s display model and avoid relying on an implicit current page.
AL bit zero distinguishes cursor-preserving output from output that updates the cursor. The cursor position after the call is page-specific BIOS state. If an application draws its own cursor or performs direct memory writes afterward, it should decide which layer is authoritative. A BIOS string call does not notify a separate application data structure of the cursor position unless the program updates that structure itself.
Cursor movement does not make this service equivalent to teletype output. The BIOS defines distinct functions and may treat control characters specially. Do not infer that every character string will wrap and scroll identically to AH=0Eh, nor that an all-page cursor update occurs. Test the exact target’s line-end and bottom-row behavior if it matters to the interface.
Screen output is not process output
INT 10h draws to a video page. It does not write a DOS handle, standard output, a pipe, or a redirected file. A command-line tool should use DOS handle-based output for logs and machine-readable results. If it also provides a full-screen interface, separate screen rendering from output-stream reporting so redirected execution remains useful and testable.
The BIOS service is useful for boot-time or low-level diagnostics, fixed text screens, and legacy applications that deliberately control a page. It is not a portable terminal protocol. ANSI drivers, DOS console devices, different video BIOSes, virtual terminals, and emulators can interpret or redirect output through different layers. Choose one display owner and document the fallback when the BIOS service is unavailable.
Failure and compatibility cases
No visible output can mean the function is unsupported, the page is not visible, the coordinates are outside the active geometry, the pointer segment is wrong, CX is zero or too large, or the mode is not a text mode compatible with the chosen AL format. If the first character is correct and later cells are garbage, check buffer bounds and paired-byte layout. If the cursor jumps unexpectedly, inspect AL and the page-specific starting coordinate.
Do not treat INT 10h as safe from every interrupt or critical-error context. BIOS code can block or use shared state, and DOS callbacks may have strict execution constraints. In an interrupt handler, record a small event and defer display work until a normal program context unless the specific BIOS and execution environment document an allowed operation.
Acceptance checks
Test one character with each attribute form, multiple characters, both cursor modes, a nonzero page when supported, and a start position near the right and bottom edges. Include control bytes in a disposable buffer and record how the target handles them. Test an unsupported service path if running across BIOS variants. Confirm that the caller buffer canary after the intended string remains intact.
Repeat from more than one text mode or emulator profile if compatibility is claimed. Record BIOS identity, adapter, mode, columns/rows, page, AL, CX, pointer segment:offset, and final cursor position. Also run the utility with stdout redirected and verify that screen drawing and stream output behave as two intentional outputs rather than one presumed channel.
Function 13h is a useful page-aware BIOS text operation when its register and buffer contract is respected. Validate support, distinguish characters from bytes, bound far pointers and coordinates, and keep screen state separate from DOS streams.
Related:
- DOS Video BIOS INT 10h: Modes, Pages, Cursor State, and Compatibility
- ANSI Console Drivers on FreeDOS: Escape Sequences, NANSI, and Compatibility
Sources: