Text Mode Cursor: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(Rewrote most of the page)
(Finished rewriting page, added a few sections)
Line 1: Line 1:
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.
==Moving the Cursor==
===With the BIOS===


==With the BIOS==
To move the cursor with the [[BIOS]], use int 0x10 (the interrupt for screen functions). These are the registers used:


To manipulate the cursor with the [[BIOS]], use int 0x10, the interrupt for screen functions.
* AH = 0x02
* BH = display page (usually, if not always 0)
* DH = row
* DL = column


===Without the BIOS===
===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).
Without [[BIOS]] access, moving the cursor requires sending data directly to the hardware.


* AH = 0x01
'''Source in C'''
* CH = start scanline
* CL = end scanline


===Disabling the Cursor===
<source lang="c">
void update_cursor(int x, int y)
{
uint16_t pos = y * VGA_WIDTH + x;


* AH = 0x01
outb(0x3D4, 0x0F);
* CH = 0x3F (bits 0-7 unused, bit 5 disables cursor, bits 0-4 control cursor shape)
outb(0x3D5, (uint8_t) (pos & 0xFF));
outb(0x3D4, 0x0E);
outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF));
}
</source>


===Moving the Cursor===
Note that the x and y positions start from 0 in the top-left corner. Keep in mind you don't need to update this every time a new character is displayed. It would be faster to instead only update it after printing an entire string or until a user prompt.


* AH = 0x02
'''Source in Assembly'''
* BH = display page (usually, if not always 0)
* DH = row
* DL = column


===Get Cursor Data===
Since [[BIOS]] services cannot be accessed in [[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):


* AH = 0x03
<source lang="asm">
* BH = display page (usually, if not always 0)
; 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


The return values:
; 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


* CH = start scanline
pop rdx
* CL = end scanline
pop rcx
* DH = row
pop rbx
* DL = column
pop rax

popfq
==Without the BIOS==
ret

</source>
Without [[BIOS]] access, manipulating the cursor requires sending data directly to the hardware.


==Setting the Cursor Start and End Scanlines==
===Enabling the Cursor===


To set 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 (usually 15).
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'''
'''Source in C'''
Line 100: Line 58:
</source>
</source>


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


TODO
==Disabling The Cursor==
===With the BIOS===


===Disabling the Cursor===
To move the cursor with the [[BIOS]], use int 0x10 (the interrupt for screen functions). These are the registers used:

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

===Without the BIOS===

Without [[BIOS]] access, disabling the cursor requires sending data directly to the hardware.


'''Source in C'''
'''Source in C'''
Line 129: Line 79:
disable_cursor:
disable_cursor:
pushf
pushf
push rax
push eax
push rdx
push edx


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


inc dx
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
mov al, 0x3F ; bits 6-7 unused, bit 5 disables the cursor, bits 0-4 control the cursor shape
out dx, al
out dx, al

pop rdx
pop rax
pop edx
pop eax
popf
popf
ret
ret
</source>
</source>

===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'''

<source lang="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>

'''Source in Assembly'''

<source lang="asm">
; 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
</source>

==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==
==See Also==
* [http://wiki.osdev.org/VGA_Hardware VGA Hardware]
* [http://wiki.osdev.org/VGA_Hardware VGA Hardware]
* [http://wiki.osdev.org/Text_UI Text UI]


===External Links===
===External Links===
* http://www.bookcase.com/library/dos/ints/int10.html (dead link)
* http://www.osdever.net/FreeVGA/vga/textcur.htm
* https://web.archive.org/web/20080731014051/http://www.bookcase.com:80/library/dos/ints/int10.html
* https://web.archive.org/web/20120324083032/http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_13/CH13-2.html
* https://web.archive.org/web/20120324083032/http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_13/CH13-2.html
* http://www.osdever.net/FreeVGA/vga/vga.htm


[[Category:Video]]
[[Category:Video]]

Revision as of 01:42, 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, 0x3F       ; 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.

See Also

External Links