Манипуляции с консолью и курсором

Последнее обновление: 17.10.2023

Различные операционные системы поддерживают специальный набор символов, которые еще называются эскейп-последовательностями и которые интерпретируются особым образом. Так, ряд эскейп-последовательностей позволяют нам манипулировать консолью и курсором.

Очистка экрана

С помощью эскейп-последовательностей мы можем очистить консоль. Так, возьмем следующую программу на Linux:

global _start

section .data
clear db 27,"[2J" ; <ESC>[2J
clearlen equ $-clear ; длина строки

section .text
_start:
    ; очищаем консоль
    mov rdi, 1
    mov rsi, clear
    mov rdx, clearlen
    mov rax, 1
    syscall 

    mov rax, 60
    syscall 

Здесь выводится строка clear. Строка начинается с ASCII-символа с кодом 27 - код клавиши ESC. Последующая часть строки интерпретируется как эскейп-последовательность, которая имеет определенный смысл. Например, последовательность ESC[2J будет интерпретироваться как команда очистки всей консоли.

Аналогичная программа на Windows:

; компоновка с помощью линкера от Microsoft:
; link hello.o kernel32.lib /entry:_start /subsystem:console /out:hello2.exe

; компоновка с помощью линкера от GCC:
; ld hello.o -o hello.exe -l kernel32

global _start      
 
extern WriteFile        ; подключем функцию WriteFile
extern GetStdHandle     ; подключем функцию GetStdHandle
 
section .data
clear db 27,"[2J"       ; <ESC>[2J
clearlen equ $-clear
 
section .text
_start:        
    sub  rsp, 40   ; Для параметров функций WriteFile и GetStdHandle резервируем 40 байт (5 параметров по 8 байт)
    mov  rcx, -11  ; Аргумент для GetStdHandle - STD_OUTPUT
    call GetStdHandle ; вызываем функцию GetStdHandle
    mov  rcx, rax     ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консоли
    mov  rdx, clear    ; Второй параметр WriteFile - загружаем указатель на строку в регистр RDX
    mov  r8d, clearlen      ; Третий параметр WriteFile - длина строки для записи в регистре R8D 
    xor  r9, r9       ; Четвертый параметр WriteFile - адрес для получения записанных байтов
    mov  qword [rsp + 32], 0  ; Пятый параметр WriteFile
    call WriteFile ; вызываем функцию WriteFile
    add  rsp, 40
    ret  

Другие используемые последовательности для очистки консоли:

ESC[J

удаление консоли (то же самое, что и ESC[0J)

ESC[0J

удаление начиная с позиции курсора вплоть до конца консоли

ESC[1J

удаление от начала консоли до позиции курсора вплоть

ESC[2J

удаление всей консоли

ESC[3J

удаление сохраненных строк

ESC[K

удаление в строке (то же самое, что и ESC[0K)

ESC[0K

удаление начиная с позиции курсора вплоть до конца строки

ESC[1K

удаление с начала строки до позиции курсора

ESC[2K

удаление всей строки

Управление курсором

Эскейп-последовательности позволяют установить курсор в консоли на нужную позицию. В частности, последовательность "ESC[{line};{column}H" устанавливает курсор на строку с номером line и в позицию с номером column. Например, выведем в программе на Linux некоторый текст во второй строке начиная с позиции 20:

global _start

section .data
clear db 27,"[2J"       ; <ESC>[2J
clearlen equ $-clear
pos db 27,"[02;20H"    ; задает позицию курсора - 20-я позиция во 2-й строке
poslen equ $-pos      ; длина предыдущей строки
msg db "Hello METANIT.COM", 10 ; выводимое сообщение
msglen equ $-msg    ; Длина сообщения

section .text
_start:
    ; очищаем экран
    mov rsi, clear
    mov rdx, clearlen
    call writeConsole
    
    ; устанавливаем курсор на позицию pos
    mov rsi, pos
    mov rdx, poslen
    call writeConsole

    ; выводим сообщение на позицию курсора
    mov rsi, msg
    mov rdx, msglen
    call writeConsole

    mov rax, 60
    syscall 

; Через регистр rsi передается адрес выводимой строки
; Через регистр rdx передается длина выводимой строки
writeConsole:
    mov rdi, 1
    mov rax, 1
    syscall 
    ret

В данном случае строка pos - эскейп-последовательность 27,"[02;20H" устанавливает курсор на вторую строку на позицию 20.

Аналогичная программа в Windows:

; компоновка с помощью линкера от Microsoft:
; link hello.o kernel32.lib /entry:_start /subsystem:console /out:hello2.exe
; компоновка с помощью линкера от GCC:
; ld hello.o -o hello.exe -l kernel32

global _start       ; делаем метку метку _start видимой извне
 
extern WriteFile        ; подключем функцию WriteFile
extern GetStdHandle     ; подключем функцию GetStdHandle
 
section .data
clear db 27,"[2J"       ; <ESC>[2J
clearlen equ $-clear
pos db 27,"[02;20H"    ; задает позицию курсора - 20-я позиция во 2-й строке
poslen equ $-pos      ; длина предыдущей строки
msg db "Hello METANIT.COM", 10 ; выводимое сообщение
msglen equ $-msg    ; Длина сообщения

section .text
_start:
    ; очищаем экран
    mov rdx, clear
    mov r8, clearlen
    call writeConsole
    
    ; устанавливаем курсор на позицию pos
    mov rdx, pos
    mov r8, poslen
    call writeConsole

    ; выводим сообшение на позицию курсора
    mov rdx, msg
    mov r8, msglen
    call writeConsole
    ret  

; Через регистр rdx передается адрес выводимой строки
; Через регистр r8 передается длина выводимой строки
writeConsole:
    sub  rsp, 48   ; Для параметров функций WriteFile и GetStdHandle резервируем 40 байт (5 параметров по 8 байт)
    mov  rcx, -11  ; Аргумент для GetStdHandle - STD_OUTPUT
    call GetStdHandle ; вызываем функцию GetStdHandle
    mov  rcx, rax     ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консоли
    xor  r9, r9       ; Четвертый параметр WriteFile - адрес для получения записанных байтов
    mov  qword [rsp + 32], 0  ; Пятый параметр WriteFile
    call WriteFile ; вызываем функцию WriteFile
    add  rsp, 48
    ret

Эскейп-последовательности для управления курсором:

ESC[H

Перемещает курсор на начало - в точку (0, 0)

ESC[{line};{column}H
ESC[{line};{column}f

Перемещает курсор на строку line, на позицию column

ESC[#A

Перемещает курсор вверх на # строк

ESC[#B

Перемещает курсор вниз на # строк

ESC[#C

Перемещает курсор вправо на # позиций

ESC[#D

Перемещает курсор влево на # позиций

ESC[#E

Перемещает курсор на начало строки на # строк вниз

ESC[#F

Перемещает курсор на начало строки на # строк вверх

ESC[#G

Перемещает курсор на позицию #

ESC[6n

запрашивает позицию курсора (ESC[#;#R)

ESC M

Перемещает курсор на одну строку вверх

ESC 7

Сохраняет позицию курсора (DEC)

ESC 8

Восстанавливает ранее сохраненную позицию курсора (DEC)

ESC[s

Сохраняет позицию курсора (SCO)

ESC[u

Восстанавливает ранее сохраненную позицию курсора (SCO)

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850