Различные операционные системы поддерживают специальный набор символов, которые еще называются эскейп-последовательностями и которые интерпретируются особым образом. Так, ряд эскейп-последовательностей позволяют нам манипулировать консолью и курсором.
С помощью эскейп-последовательностей мы можем очистить консоль. Так, возьмем следующую программу на 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[0J) |
|
удаление начиная с позиции курсора вплоть до конца консоли |
|
удаление от начала консоли до позиции курсора вплоть |
|
удаление всей консоли |
|
удаление сохраненных строк |
|
удаление в строке (то же самое, что и ESC[0K) |
|
удаление начиная с позиции курсора вплоть до конца строки |
|
удаление с начала строки до позиции курсора |
|
удаление всей строки |
Эскейп-последовательности позволяют установить курсор в консоли на нужную позицию. В частности, последовательность "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
Эскейп-последовательности для управления курсором:
|
Перемещает курсор на начало - в точку (0, 0) |
|
Перемещает курсор на строку line, на позицию column |
|
Перемещает курсор вверх на # строк |
|
Перемещает курсор вниз на # строк |
|
Перемещает курсор вправо на # позиций |
|
Перемещает курсор влево на # позиций |
|
Перемещает курсор на начало строки на # строк вниз |
|
Перемещает курсор на начало строки на # строк вверх |
|
Перемещает курсор на позицию # |
|
запрашивает позицию курсора ( |
|
Перемещает курсор на одну строку вверх |
|
Сохраняет позицию курсора (DEC) |
|
Восстанавливает ранее сохраненную позицию курсора (DEC) |
|
Сохраняет позицию курсора (SCO) |
|
Восстанавливает ранее сохраненную позицию курсора (SCO) |