OSD Home

- # - Assembly-language text output using the BIOS
- # - C-language text output using the BIOS
- # - Text output without the BIOS
- # - Scrolling
- # - Moving the cursor
- # - ANSI escape sequences
- # - Code snippets
- # - Links
- REPORT BUGS OR ERRORS IN THIS DOCUMENT

## Assembly-language text output using the BIOS

For information on using BIOS video functions, see INT 10h in Ralf Brown's interrupt list: http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html

See also the code snippets below.

## C-language text output using the BIOS

First of all, make sure the function you want to use does not use DOS. This can be done by examining the source code for the C library or tracing into the function with a debugger.

The `putch()` and `cputs()` functions may be good choices for text output in a 16-bit real-mode OS. Both the DJGPP and Turbo C versions use BIOS interrupts but not DOS interrupts.

For the DJGPP version of `putch()` to function, a V86 mode monitor (VMM) is required. The VMM handles BIOS interrupts by switching the processor from 32-bit pmode to virtual 8086 mode, re-issuing the interrupt, and switching back to 32-bit pmode when the BIOS interrupt is done.

Which is more difficult: writing a VMM, or writing pmode driver code to replace 16-bit BIOS functions? Opinions vary.

## Text output without the BIOS

Text displayed on the screen is stored in a framebuffer. The location of the framebuffer in memory depends on whether the VGA board is set for monochrome or color emulation:

emulation | framebuffer linear address | framebuffer real-mode address | I/O address of CRTC
--- | --- | --- | ---
color | B8000h | B800h:0000 | 3D4h
monochrome | B0000h | B000h:0000 | 3B4h

Usually, the board is set for color emulation. The CRTC is a functional unit of the VGA, and is discussed below.

Each character in the framebuffer takes 2 bytes: an ASCII value at address N, and an attribute byte at address N+1:

bit in attribute byte | usage
--- | ---
b7 | blink
b6:b4 | background color (0-7)
b3:b0 | foreground color (0-15)

color value | color | color value | color
--- | --- | --- | ---
0 | black | 8 | dark gray
1 | blue | 9 | bright blue
2 | green | 10 | bright green
3 | cyan | 11 | bright cyan
4 | red | 12 | pink
5 | magenta | 13 | bright magenta
6 | brown | 14 | yellow
7 | white | 15 | bright white

## Scrolling

xxx - BIOS scrolling functions?

For both DJGPP and Turbo C, software scrolling of the display can be done with the `movedata()` function. This function works like `memcpy()` but uses far pointers for both source and destination.

`movedata()` may not work properly if src < dst, i.e. for making the display scroll down. If you use near pointers, `memmove()` can be used instead of `movedata()` or `memcpy()`. `memmove()` is guaranteed to work properly if src and dst overlap.

For hardware scrolling, you instruct the VGA to use a different memory location for the framebuffer. CRTC registers 12 and 13 contain the MSB and LSB of the framebuffer offset, relative to B0000h, B8000h, or A0000h. Hardware scrolling can not continue indefinitely (eventually the framebuffer will extend beyond the end of video memory).

The framebuffer memory can also be divided into virtual consoles (VCs). 32K of video memory is enough for eight 80x25 VCs. The VCs can be spaced 4000 bytes apart (80x25x2), and the hardware scrolling code snippet below can be used to select which VC is currently displayed. (Linux VCs use a different method.)

## Moving the cursor

BIOS call INT 10h AH=02h moves the winking cursor.

If you do not want to use the BIOS: CRTC registers 14 and 15 contain the MSB and LSB of the cursor position, relative to B8000h or B0000h, in units of characters. The cursor can be moved with a code snippet similar to the one for scrolling (see below).

## ANSI escape sequences

These escape sequences are used to change the color of text, move the cursor, clear the screen, etc. They were first supported DEC's VT series of dumb terminals, and are still widely supported today:

- Most BBSes support ANSI escapes
- The Linux console emulates a VT102 terminal
- Most 'terminal' or serial communications software can emulate a VT100 terminal
- The DOS driver ANSI.SYS (and similar TSRs that can be loaded from the command line) makes these escape sequences available to DOS programs

## Code snippets

Demo of INT 10h AH=0Eh 'teletype' output.

Demo of INT 10h AH=13h.

Code to detect monochrome/color emulation.

Turbo C code (16-bit real mode) to put white on blue 'H' in upper left corner of screen:

            #include <dos.h> /* pokeb() */
            pokeb(0xB800, 0, 'H');
    	pokeb(0xB800, 1, 0x1F);

NASM code (16-bit real mode) to put white on blue 'H' in upper left corner of screen:

            mov bx,0B800h
            mov es,bx
            mov byte [es:0],'H'
            mov byte [es:1],1Fh

DJGPP code (32-bit pmode) to put yellow on red '*' in upper right corner of 80x25 screen, using far pointers:

            #include <sys/farptr.h> /* _farpokeb() */
            #include <go32.h> /* _dos_ds */
    	_farpokeb(_dos_ds, 0xB8000 + 79 * 2 + 0, '*');
    	_farpokeb(_dos_ds, 0xB8000 + 79 * 2 + 1, 0x4E);

DJGPP code (32-bit pmode) to put yellow on red '*' in upper right corner of 80x25 screen, using near pointers:

            #include <sys/nearptr.h>
            #include <crt0.h>
    	#include <stdio.h>

    	unsigned char *fb;

    	if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))
    	{       if(!__djgpp_nearptr_enable())
    		{       printf("Could not enable nearptr access\n");
    			return -1; } } /* probably Windows NT DOS box */
    	fb = (unsigned char *)0xB8000 + __djgpp_conventional_base;
    	fb[79 * 2 + 0] = '*';
    	fb[79 * 2 + 1] = 0x4E;

Turbo C code to scroll 80x25 display up one line (color emulation):

            #include <string.h> /* movedata() */
            movedata(0xB800, 80 * 2,
                    0xB800, 0,
                    80 * (25 - 1) * 2);

DJGPP code to scroll 80x25 display up one line (color emulation):

            #include <string.h> /* movedata() */
    	#include <go32.h> /* _dos_ds */
            movedata(_dos_ds, 0xB8000L + 80 * 2,
                    _dos_ds, 0xB8000L,
                    80 * (25 - 1) * 2);

Hardware scrolling by changing the framebuffer base address:

            /* scroll up one line */
    	#include <dos.h> /* outportb() */
            unsigned short crtc_adr = 0x3D4; /* 0x3B4 for monochrome */
            unsigned short offset = 80;

            /* the CRTC index is at crtc_adr + 0
            select register 12 */
            outportb(crtc_adr + 0, 12);
            /* the selected CRTC register appears at crtc_adr + 1 */
            outportb(crtc_adr + 1, offset >> 8);
            outportb(crtc_adr + 0, 13);
    	outportb(crtc_adr + 1, offset & 0xFF);

Move hardware cursor using INT 10h AH=02h:

            mov ah,2
            mov bh,0 ; video page
            mov dh,1 ; row (y; 0 is top)
            mov dl,10 ; col (x; 0 is left)
    	int 10h

Move hardware cursor by changing VGA registers; without using the BIOS:

            #include <dos.h> /* outportb() */
            unsigned short crtc_adr = 0x3D4; /* 0x3B4 for monochrome */
            unsigned short offset;
            unsigned short x = 20, y = 3;

    	offset = x + y * 80;            /* 80 characters per line */
    	outportb(crtc_adr + 0, 14);     /* MSB of offset to CRTC reg 14 */
    	outportb(crtc_adr + 1, offset >> 8);
    	outportb(crtc_adr + 0, 15);     /* LSB of offset to CRTC reg 15 */
    	outportb(crtc_adr + 1, offset);

## Links

http://home.worldonline.dk/~finth/
http://my.execpc.com/~geezer/os/vga.zip

## TO DO

    Unicode? UTF-8?

## REPORT BUGS OR ERRORS IN THIS DOCUMENT