Text Mode Cursor: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
Line 161: Line 161:
</source>
</source>


===Get Cursor Data===
===Get Cursor Position===


With this code, you get: <tt>pos = y * VGA_WIDTH + x</tt>. To obtain the coordinates, just calculate: <tt>y = pos / VGA_WIDTH; x = pos % VGA_WIDTH</tt>.
With this code, you get: <tt>pos = y * VGA_WIDTH + x</tt>. To obtain the coordinates, just calculate: <tt>y = pos / VGA_WIDTH; x = pos % VGA_WIDTH;</tt>.


'''Source in C'''
'''Source in C'''

Revision as of 17:51, 12 January 2019

In text mode, the cursor does not work the same way as in high-level languages, automatically moving to one place after the last written character. Instead, it is simply a blinking area that can be resized, shown, hidden, and moved by the OS.

With the BIOS

To manipulate the cursor with the BIOS, use int 0x10, the interrupt for screen functions.

Enabling the Cursor

Enabling the cursor also allows you to set the start and end scanlines, the rows where the cursor starts and ends. The highest scanline is 0 and the lowest scanline is the maximum scanline (usually 15).

  • AH = 0x01
  • CH = start scanline
  • CL = end scanline

Disabling the Cursor

  • AH = 0x01
  • CH = 0x3F (bits 0-7 unused, bit 5 disables cursor, bits 0-4 control cursor shape)

Moving the Cursor

  • AH = 0x02
  • BH = display page (usually, if not always 0)
  • DH = row
  • DL = column

Get Cursor Data

  • AH = 0x03
  • BH = display page (usually, if not always 0)

The return values:

  • CH = start scanline
  • CL = end scanline
  • DH = row
  • DL = column

Without the BIOS

Without BIOS access, manipulating the cursor requires sending data directly to the hardware.

Enabling the Cursor

Enabling the cursor also allows you to set the start and end scanlines, the rows where the cursor starts and ends. The highest scanline is 0 and the lowest scanline is the maximum scanline (usually 15).

Source in C

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(0x3D5) & 0xE0) | cursor_end);
}

Disabling the Cursor

Source in C

void disable_cursor()
{
	outb(0x3D4, 0x0A);
	outb(0x3D5, 0x20);
}

Source in Assembly

disable_cursor:
	pushf
	push eax
	push edx

	mov dx, 0x3D4
	mov al, 0xA	; low cursor shape register
	out dx, al

	inc dx
	mov al, 0x20	; bits 6-7 unused, bit 5 disables the cursor, bits 0-4 control the cursor shape
	out dx, al

	pop edx
	pop eax
	popf
	ret

Moving the Cursor

Keep in mind that you don't need to update the cursor's location every time a new character is displayed. It would be faster to instead only update it after printing an entire string.

Source in C

void update_cursor(int x, int y)
{
	uint16_t pos = y * VGA_WIDTH + x;

	outb(0x3D4, 0x0F);
	outb(0x3D5, (uint8_t) (pos & 0xFF));
	outb(0x3D4, 0x0E);
	outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF));
}

Source in Assembly

; BL = x
; BH = y
update_cursor:
	pushfq
	push eax
	push ebx
	push ecx
	push edx

	; position = x * VGA_HEIGHT + y
	mov ax, bx
	and ax, 0FFh
	mov cl, VGA_HEIGHT
	mul cl
	mov cx, bx
	shr cx, 8
	add ax, cx
	mov cx, ax
 
	; cursor low port to VGA index register
	mov al, 0Fh
	mov dx, 3D4h
	out dx, al

	; cursor low position to VGA data register
	mov ax, cx
	mov dx, 3D5h
	out dx, al

	; cursor high port to VGA index register
	mov al, 0Eh
	mov dx, 3D4h
	out dx, al

	; cursor high position to VGA data register
	mov ax, cx
	shr ax, 8
	mov dx, 3D5h
	out dx, al

	pop edx
	pop ecx
	pop ebx
	pop eax
	popfq
	ret

Get Cursor Position

With this code, you get: pos = y * VGA_WIDTH + x. To obtain the coordinates, just calculate: y = pos / VGA_WIDTH; x = pos % VGA_WIDTH;.

Source in C

uint16_t get_cursor_position(void)
{
    uint16_t pos = 0;
    outb(0x3D4, 0x0F);
    pos |= inb(0x3D5);
    outb(0x3D4, 0x0E);
    pos |= ((uint16_t)inb(0x3D5)) << 8;
    return pos;
}

A Note on GRUB

If the timeout is set to 0 in your grub.cfg, the cursor will be disabled and you will need to enable it yourself. Otherwise, GRUB will enable the cursor for you. Because of this inconsistency, it is a good idea to always enable the cursor. Even if you don't set the timeout to 0, you might want to in the future, or someone might change it on their system.

See Also

External Links