Text Mode Cursor: Difference between revisions
[unchecked revision] | [unchecked revision] |
Finished rewriting page, added a few sections |
m →Disabling the Cursor: fixed trivial inconsistency between C and assembly |
||
Line 87:
inc dx
mov al,
out dx, al
|
Revision as of 01:46, 20 October 2017
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(0x3E0) & 0xE0) | cursor_end);
}
Source in Assembly
TODO
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
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.