Text Mode Cursor: Difference between revisions
[unchecked revision] | [unchecked revision] |
m (→Source in C: Cleaned up code) |
(Rewrote most of the page) |
||
Line 1: | Line 1: | ||
==Moving the Cursor |
==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: |
|||
To move the cursor with the [[BIOS]], use int 0x10 (the interrupt for screen functions). These are the registers used: |
|||
* AH = 0x02 |
* AH = 0x02 |
||
* BH = |
* BH = display page (usually, if not always 0) |
||
* DH = |
* DH = row |
||
* DL = |
* DL = column |
||
===Without the BIOS=== |
|||
Then, with a quick call to interrupt 0x10, you should have yourself a movable type cursor. |
|||
Without [[BIOS]] access, moving the cursor requires sending data directly to the hardware. |
|||
==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. |
|||
'''Source in C''' |
|||
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]] [[Memory Map (x86)#BIOS Data Area .28BDA.29|data area]]. |
|||
===Source in C=== |
|||
<source lang="c"> |
<source lang="c"> |
||
void update_cursor(int x, int y) |
|||
{ |
|||
* by Dark Fiber |
|||
uint16_t pos = y * VGA_WIDTH + x; |
|||
*/ |
|||
void update_cursor(int row, int col) |
|||
outb(0x3D4, 0x0F); |
|||
{ |
|||
outb(0x3D5, (uint8_t) (pos & 0xFF)); |
|||
unsigned short position=(row*80) + col; |
|||
outb(0x3D4, 0x0E); |
|||
outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); |
|||
// 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)); |
|||
} |
|||
</source> |
</source> |
||
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) |
|||
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. |
|||
===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): |
|||
'''Source in Assembly''' |
|||
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): |
|||
<source lang="asm"> |
<source lang="asm"> |
||
; Set cursor position (text mode 80x25) |
; Set cursor position (text mode 80x25) |
||
; @param BL The row on screen, starts from 0 |
; @param BL The row on screen, starts from 0 |
||
; @param BH The column on screen, starts from 0 |
; @param BH The column on screen, starts from 0 |
||
set_cursor: |
|||
;============================================================================= |
|||
pushfq |
|||
push rax |
push rax |
||
push rbx |
push rbx |
||
Line 45: | Line 44: | ||
push rdx |
push rdx |
||
;unsigned short position = (row*80) + col |
; unsigned short position = (row*80) + col |
||
;AX will contain 'position' |
; AX will contain 'position' |
||
mov ax,bx |
mov ax, bx |
||
and ax,0ffh ;set AX to 'row' |
and ax, 0ffh ; set AX to 'row' |
||
mov cl,80 |
mov cl, 80 |
||
mul cl ;row*80 |
mul cl ; row * 80 |
||
mov cx,bx |
mov cx, bx |
||
shr cx,8 ;set CX to 'col' |
shr cx, 8 ; set CX to 'col' |
||
add ax,cx ;+ col |
add ax, cx ; + col |
||
mov cx,ax ;store 'position' in CX |
mov cx, ax ; store 'position' in CX |
||
;cursor LOW port to vga INDEX register |
; cursor LOW port to vga INDEX register |
||
mov al,0fh |
mov al, 0fh |
||
mov dx,3d4h ;VGA port 3D4h |
mov dx, 3d4h ; VGA port 3D4h |
||
out dx,al |
out dx, al |
||
mov ax,cx ;restore 'postion' back to AX |
mov ax, cx ; restore 'postion' back to AX |
||
mov dx,3d5h ;VGA port 3D5h |
mov dx, 3d5h ; VGA port 3D5h |
||
out dx,al ;send to VGA hardware |
out dx, al ; send to VGA hardware |
||
;cursor HIGH port to vga INDEX register |
; cursor HIGH port to vga INDEX register |
||
mov al,0eh |
mov al, 0eh |
||
mov dx,3d4h ;VGA port 3D4h |
mov dx, 3d4h ; VGA port 3D4h |
||
out dx,al |
out dx, al |
||
mov ax,cx ;restore 'position' back to AX |
mov ax, cx ; restore 'position' back to AX |
||
shr ax,8 ;get high byte in 'position' |
shr ax, 8 ; get high byte in 'position' |
||
mov dx,3d5h ;VGA port 3D5h |
mov dx, 3d5h ; VGA port 3D5h |
||
out dx,al ;send to VGA hardware |
out dx, al ; send to VGA hardware |
||
pop rdx |
pop rdx |
||
Line 85: | Line 84: | ||
==Setting the Cursor Start and End Scanlines== |
==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). |
|||
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). |
|||
===Source in C=== |
|||
'''Source in C''' |
|||
<source lang="c"> |
<source lang="c"> |
||
// enable the cursor and set the cursor start and end scanlines |
|||
void enable_cursor(uint8_t cursor_start, uint8_t cursor_end) |
void enable_cursor(uint8_t cursor_start, uint8_t cursor_end) |
||
{ |
{ |
||
Line 102: | Line 102: | ||
To make the cursor be a block instead of a line, try enable_cursor(0, 15). |
To make the cursor be a block instead of a line, try enable_cursor(0, 15). |
||
==Disabling The Cursor |
==Disabling The Cursor== |
||
===With the BIOS=== |
|||
To move the cursor with the [[BIOS]], use int 0x10 (the interrupt for screen functions). These are the registers used: |
|||
Disabling the cursor with the [[BIOS]] is done also through Int 0x10 with AH set to 0x01. These are the registers used: |
|||
* AH = 0x01 |
* AH = 0x01 |
||
* CH = 0x3F (bits 0-7 unused, bit 5 disables cursor, bits 0-4 control cursor shape) |
|||
* 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.) |
|||
===Without the BIOS=== |
|||
And then Interrupt 0x10 and then vuala , there is no VISIBLE cursor on the screen |
|||
Without [[BIOS]] access, disabling the cursor requires sending data directly to the hardware. |
|||
'''Source in C''' |
|||
==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=== |
|||
<source lang="c"> |
<source lang="c"> |
||
void disable_cursor() |
|||
{ |
|||
* by Elad Ashkcenazi |
|||
outb(0x3D4, 0x0A); |
|||
* year 2017 |
|||
outb(0x3D5, 0x20); |
|||
*/ |
|||
} |
|||
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 disabled, bits 0-4 control the cursor shape |
|||
} |
|||
</source> |
</source> |
||
'''Source in Assembly''' |
|||
<source lang="asm"> |
<source lang="asm"> |
||
disable_cursor: |
|||
;written by Elad Ashkcenazi. |
|||
pushf |
|||
;year - 2017 |
|||
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 |
|||
disable_cursor: pushf |
|||
mov al, 0x3F ; bits 6-7 must be 0, if bit 5 set the cursor is disabled, bits 0-4 control the cursor shape |
|||
push rax |
|||
out dx, al |
|||
push rdx |
|||
pop rdx |
|||
pop rax |
|||
mov dx,0x3d4 ; one of VGA's Index registers controller |
|||
popf |
|||
mov al,0xa ; index 0xa is the LOW cursor shape register |
|||
ret |
|||
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 |
|||
</source> |
</source> |
||
Line 158: | Line 152: | ||
* 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 |
* http://www.osdever.net/FreeVGA/vga/vga.htm |
||
[[Category:Video]] |
[[Category:Video]] |
||
[[Category:Text UI]] |
|||
[[Category:VGA]] |
[[Category:VGA]] |
Revision as of 01:34, 18 October 2017
Moving the Cursor
With the BIOS
To move the cursor with the BIOS, use int 0x10 (the interrupt for screen functions). These are the registers used:
- AH = 0x02
- BH = display page (usually, if not always 0)
- DH = row
- DL = column
Without the BIOS
Without BIOS access, moving the cursor requires sending data directly to the hardware.
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));
}
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.
Source in Assembly
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):
; 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
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).
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);
}
To make the cursor be a block instead of a line, try enable_cursor(0, 15).
Disabling The Cursor
With the BIOS
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
void disable_cursor()
{
outb(0x3D4, 0x0A);
outb(0x3D5, 0x20);
}
Source in Assembly
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