Text Mode Cursor

From OSDev.wiki
Revision as of 15:32, 30 September 2017 by osdev>Davidaylaian (→‎Source in assembly: Cleaned up code)
Jump to navigation Jump to search

Moving the Cursor with the BIOS

Moving the cursor with the BIOS is done through Int 0x10 (The general interrupt for screen functions) with AH set to 0x02. These are the registers used:

  • AH = 0x02
  • BH = Display Page (This is usually, if not always, 0)
  • DH = The row
  • DL = The column

Then, with a quick call to interrupt 0x10, you should have yourself a movable type cursor.

Moving the Cursor without the BIOS

Without access to BIOS calls and functions, moving the cursor requires using video hardware control. Lucky it is a simple procedure.

Note, this quick example assumes 80x25 screen mode. Also note that the base port (here assumed to be 0x3D4) should be read from the BIOS data area.

Source in C

 /* void update_cursor(int row, int col)
  * by Dark Fiber
  */
 void update_cursor(int row, int col)
 {
    unsigned short position=(row*80) + col;
 
    // cursor LOW port to vga INDEX register
    outb(0x3D4, 0x0F);
    outb(0x3D5, (unsigned char)(position&0xFF));
    // cursor HIGH port to vga INDEX register
    outb(0x3D4, 0x0E);
    outb(0x3D5, (unsigned char )((position>>8)&0xFF));
 }

Note that the 2 parameters 'row' & 'col' passed to the function above start from zero, not from 1. And keep in mind that in/out to VGA Hardware is a slow operation. So using the hardware registers to remember of the current character location (row, col) is bad practice -- and updating position after each displayed character is poor practice (updating it only when a line/string is complete is wiser and hiding it until a user prompt is wisest)

Source in assembly

Since BIOS services can't be accessed in 64bit long mode, the following routine shows how to move cursor without BIOS in VGA text 80x25 (can be altered a bit to fit protected mode):

; Set cursor position (text mode 80x25)
; @param BL The row on screen, starts from 0
; @param BH The column on screen, starts from 0
;=============================================================================
set_cursor:     pushfq
                push rax
                push rbx
                push rcx
                push rdx

                ;unsigned short position = (row*80) + col;
                ;AX will contain 'position'
                mov ax,bx
                and ax,0ffh             ;set AX to 'row'
                mov cl,80   
                mul cl                  ;row*80
                
                mov cx,bx               
                shr cx,8                ;set CX to 'col'
                add ax,cx               ;+ col
                mov cx,ax               ;store 'position' in CX
 
                ;cursor LOW port to vga INDEX register
                mov al,0fh             
                mov dx,3d4h             ;VGA port 3D4h
                out dx,al             
                
                mov ax,cx               ;restore 'postion' back to AX  
                mov dx,3d5h             ;VGA port 3D5h
                out dx,al               ;send to VGA hardware
    
                ;cursor HIGH port to vga INDEX register
                mov al,0eh
                mov dx,3d4h             ;VGA port 3D4h
                out dx,al
                
                mov ax,cx               ;restore 'position' back to AX
                shr ax,8                ;get high byte in 'position'
                mov dx,3d5h             ;VGA port 3D5h
                out dx,al               ;send to VGA hardware

                pop rdx
                pop rcx
                pop rbx
                pop rax
                popfq
                ret

Setting the Cursor Start and End Scanlines

Use the Cursor Start Register (0x0A) and the Cursor End Register (0x0B). The highest scanline is 0 and the lowest scanline is the maximum scanline (15 worked for me).

Source in C

// enable the cursor and set the cursor start and end scanlines
void enable_cursor(uint8_t cursor_start, uint8_t cursor_end)
{
        outb(0x3D4, 0x0A);
        outb(0x3D5, (inb(0x3D5) & 0xC0) | cursor_start);

        outb(0x3D4, 0x0B);
        outb(0x3D5, (inb(0x3E0) & 0xE0) | cursor_end);
}

To make the cursor be a block instead of a line, try enable_cursor(0, 15).

Disabling The Cursor With the BIOS

Disabling the cursor with the BIOS is done also through Int 0x10 with AH set to 0x01. These are the registers used:

  • AH = 0x01
  • CH = 0x3f  ; bits 6-7 are unused , if bit 5 set the cursor is disable (this bit may be cleared in the interrupt caller , see "Disabling The Cursor Without the BIOS" for safer way to disable the cursor) , bits 0-4 controll the cursor shape (if bits 0-4 all set the cursor is unvisiable regradless bit 5's state.)

And then Interrupt 0x10 and then vuala , there is no VISIBLE cursor on the screen

  • Even on GUI modes the cursor remains active so don't think about it much.

Disabling The Cursor Without the BIOS

To diable the cursor without the bios we need to use vga's 0x3d4 index port and his data register located one port above it, we need to set bits 5-0 of register index 0x0a , the LOW cursor shape (we don't need to alter the HIGH cursor shape, he doesn't contain the 'cursor disable' bit inside him .), here's how it translate into code:

Source in C

 /* void disable_cursor()
  * by Elad Ashkcenazi
  * year 2017
  */
 void disable_cursor()
 {
    outb(0x3D4, 0x0A); // LOW cursor shape port to vga INDEX register
    outb(0x3D5, 0x3f); //bits 6-7 must be 0 , if bit 5 set the cursor is disable  , bits 0-4 controll the cursor shape .
 }

Source in assembly

;written by Elad Ashkcenazi.
;year - 2017


disable_cursor:  pushf
                push rax
                push rdx

                mov dx,0x3d4 ; one of VGA's Index registers controller
                mov al,0xa   ; index 0xa is the LOW cursor shape register
                out dx,al 

                inc dx       ; one of VGA's data registers, port 0x3d4, allows reads and writes to VGA's registers
                mov al,0x3f  ; bits 6-7 must be 0, if bit 5 set the cursor is disabled, bits 0-4 control the cursor shape
                out dx,al
                pop rdx
                pop rax
                popf
                ret

See Also

External Links