The CRTC Interface Essential aspects of display page and cursor control for standard 80-column by 25-row text Motivation • When a UNIX application wishes to write a text message to the display, and then wait for a keyboard-response from the user, the application will want a ‘cursor’ to be visible at the position where the next keystroke is going to be echoed • EXAMPLE: Please type a number: We expect a blinking cursor to appear here But it’s tedious to program… • To write code that draws a blinking cursor on the screen would be a time-consuming chore for both programmer and processor: – You have to repeatedly ‘draw’ and then ‘hide’ – You have to setup careful timing controls – Your program-loop will keep the CPU busy! • Fortunately the video system’s hardware is able to handle this tedious chore for you! Info the CRTC engine needs • The display hardware already knows how to do the timing for normal cursor blinking (i.e., usually about two blinks per second) • Software only needs to tell the hardware a few specific pieces of information: – Where does the cursor appear (row, column)? – How large is the cursor? Where does it start and end within the 8x16 character-cell? – Which region of display-memory is visible? Hardware registers • The CRT Controller is a peripheral chip • It implements 25 standard CRTC registers • Access to these registers is accomplished via a multiplexing scheme which uses just two I/O port-addresses for color text: address-port: 0x03D4 data-port: 0x03D5 How it works • The multiplexing scheme for access to the CRTC registers was designed to function as a two-step operation: – First, specify which register is to be accessed (by writing the register’s index to port 0x3D4) – Then, read (or write) to the specified register (by inputting, or outputting, at port 0x3D5) • But a multi-step scheme is problematic for preemptive multitasking (also it is slow!) A useful optimization • To improve the multiplexing scheme when writing values to CRTC registers, the CPU is able to perform the two steps in a single ‘atomic’ fetch-execute cycle: mov mov outw $0x03D4, %dx $0xFF14, %ax %ax, %dx • Here the ‘outw’ instruction will write AL to port $0x03D4 and AH to port $0x03D5 Cursor-related CRTC registers • For text-mode cursor-control, we are concerned with just 6 (of the 25) standard CRTC registers: • Index 0x0A: CURSOR_START • Index 0x0B: CURSOR_END • Index 0x0C: START_ADDRESS_HI • Index 0x0D: START_ADDRESS_LO • Index 0x0E: CURSOR_LOCATION_HI • Index 0x0F: CURSOR_LOCATION_LO 8x16 character box Cursor_start = 12 Cursor_end = 13 scanline 0 scanline 1 scanline 2 scanline 3 scanline 4 scanline 5 scanline 6 scanline 7 scanline 8 scanline 9 scanline 10 scanline 11 scanline 12 scanline 13 scanline 14 scanline 15 CURSOR START/END 7 6 Index 0x0A: 5 disable 4 3 2 1 0 starting_scanline CURSOR_START REGISTER 7 Index 0x0B: 6 5 skew 4 3 2 1 0 ending_scanline CURSOR_END REGISTER NOTE: The ‘skew’ capability works by delaying the cursor’s appearance for 0, 1, 2, or 3 character-cells (i.e., shifting the cursor toward the right). When Is this useful? We couldn’t find any examples. Recommend skew be zero. Organization of the VRAM 4KB Page 7 4KB Page 6 4KB Page 5 4KB Page 4 4KB Page 3 4KB Page 2 4KB Page 1 4KB Page 0 Base_Address = 0xB8000 Changing the visual page 7 6 5 4 3 2 1 START_ADDRESS_HI register-index 0x0C Programming example: 0 7 6 5 4 3 2 1 START_ADDRESS_LO register-index 0x0D # switches display to vram page 5 # the word-offset for page 5 is 0x2800 (= 5 * 2048) mov $0x03D4, %dx # port-address in register DX mov $0x280C, %ax # value=0x28, register=0x0C out %ax, %dx # write value to CRTC register mov $0x000D, %ax # value=0x00, register=0x0D out %ax, %dx # write value to CRTC register 0 Moving the CRT’s cursor 7 6 5 4 3 2 1 0 CURSOR_LOCATION_HI register-index 0x0E Programming example: mov mov imul add mov mov out mov mov out 7 6 5 4 3 2 1 0 CURSOR_LOCATION_LO register-index 0x0F // moves cursor to row 5, column 9, on page 0 $0x03D4, %dx $5, %bx $80, %bx $9, %bx %bh, %ah $0x0E, %al %ax, %dx %bl, %ah $0x0F, %al %ax, %dx // port-address in register DX // row-number // times cells-per-row // plus column-number // cursor offset’s MSB // CURSOR_HI index // write value to CRTC register // cursor offset’s LSB // CURSOR_LO index // write value to CRTC register Scrolling the screen • Here’s a code-fragment that will scroll the contents of vram page 0 up by one line: mov mov mov mov mov cld mov rep mov mov rep $0xB800, %ax %ax, %ds %ax, %es $0, %di $160, %si $3840, %cx movsb $0x0720, %ax $80, %cx stosw // address vram page 0 // with DS register // also ES register // destination is the top line // source is one line lower // do forward copying // 24 times 160 // perform the copying // blank character w/color // characters on bottom line // fill final line with blanks Linux uses hardware scrolling • The value of the CRT START_ADDRESS is reprogrammed, to change the region of visible vram by one line (i.e., add #80) • So instead of subdividing vram into eight 4KB pages, the entire 32KB vram is one continuous page, but only partially visible • To scroll up by one line, Linux adds $80 to the value of the CRT_START_ADDRESS The ROM-BIOS variables • Several variables in the ROM-BIOS DATA AREA are used by the VIDEO ROM-BIOS routines (i.e., int-0x10) to keep track of the currently visible page and of the positions of the cursors on each of the eight pages • The locations of these variables are part of the IBM-PC ROM-BIOS standard, and as such they are widely documented Standard data-addresses 0x449 (byte) current video mode-number 0x44A (word) number of columns on screen 0x44C (word) current page-size (in bytes) 0x44E (word) current page-address 0x450 (byte array) cursor-positions (col,row) 0x460 byte-pair) cursor type (END, START) 0x462 (byte) current display page-number 0x463 (word) CRTC base i/o port-address Real-mode INT-0x10 services • Function 0x00: set_display_mode • Function 0x01: set_cursor_type • Function 0x02: set_cursor_position • Function 0x03: get_cursor_position_and_type • Function 0x05: select_new_video_page • Function 0x06: scroll_current_page_up • Function 0x07: scroll_current_page_down • Function 0x08: read_char_and_attrib_from_screen • Function 0x09: write_char_and_attrib_to_screen • Function 0x0A: write char_only_to_screen • Function 0x0E: write_teletype_to_active_page • Function 0x0F: return_video_status • Function 0x13: write_string NOTE: These ROM-BIOS services are not available in protected-mode Cursor-movement demo • To illustrate reprogramming of the six CRT controller registers, we wrote ‘arrowpad.s’ • It lets the user control the cursor position and visible page by using arrow-keys • It also changes the height of the cursor • An unusual feature (not recommended) is its use of “polled mode” keyboard deviceprogramming (instead of “interrupt-driven”) Polling the status-register • The keyboard controller’s interrupt is masked • The keyboard controller’s status-register is read and reread in a tight loop until bit #0 gets set, indicating that a key was pressed (or released) and hence the output-buffer register is now “full” • So then the output-buffer register is read by the CPU to get the key’s “scancode” • For the arrow-keys, the cursor will get moved • Other keys are just ignored (except ESCAPE) Disadvantage of polling • Almost all of the CPU’s time is consumed by continually reading the status-register • So this would not be a good design to use in writing a multitasking operating system! • On the other hand, for single-tasking it has the advantage of not requiring an Interrupt Service Routine to be written, so the demo code we write can be shorter and simpler Another noteworthy feature • The ‘arrowpad.s’ demo uses a ‘jump-table’ to dispatch control quickly to appropriate subroutines, based on a variable’s value • This is a similar programming situation to using a ‘switch’ statement in C/C++ • The jump-table avoids the long chain of ‘compare-and-branch’ statements for all the various possible cases that can occur In-Class exercise • To insure your mastery of the jump-table concept, and cement your grasp of how the CRTC START_ADDRESS registers are programmed, try modifying the demo to incorporate these additional actions: when one of the function-keys F1-F7 is pressed, the display is switched to the correspondingly numbered display-page