Написание ОС с нуля: Часть 3 — Терминал

—

от автора

Ссылки:

Вступление

В предыдущей статье мы написали простейший загрузчик, печатающий на экран «Hello, World!» и завершающийся но нажанию клавиши. Сегодня напишем терминал, у которого будет несколько команд, обновим библиотеку и сделаем ещё пару вещей.

Библиотека

По сравнению с библиотекой из предыдущей статьи, до сегодня она стала массивнее, и поддерживать её в таком виде, как на рис. 1 становится почти невозможно. Поэтому было принято решение поменять структуру библиотеки. Итоговая структура изображена на рис. 2:

рис. 1: старая структура проекта и библиотеки
рис. 1: старая структура проекта и библиотеки
рис. 2: новая структура проекта и библиотеки. Немного страшно, но когда я посмотрел на структуру проекта EDK II, я почувствовал куда более сильные эмоции :)
рис. 2: новая структура проекта и библиотеки. Немного страшно, но когда я посмотрел на структуру проекта EDK II, я почувствовал куда более сильные эмоции 🙂

Что там нужно уметь.. Ага, нашел..

Для самого базового терминала нам нужно уметь следующее:

  1. Печатать в консоль

  2. Принимать ввод

  3. определять комманды

  4. выполнять комманды

Печатать мы научились ещё в предыдущей статье. Дальше по списку: «принимать ввод». В предыдущей статье был макрос для ожидания нажатия любой клавиши, но этого точно мало для того, чтобы получать строки с коммандами. Дак сделаем новый макрос! Макросы для взаимодействия с вводом / выводом я определяю в файле ioefi.inc. Для приема строки нужно:

  1. принять клавишу.

  2. если нажат Backspace, то убрать последний символ.

  3. если нажат Enter, то поставить null в буффере и закончить работу макроса.

  4. если это обычная клавиша, то напечатать её на экран, сохранить в буффер, сместить курсор буффера и перейти к п.1.

В коде примерно так:

; libuefi.inc  __scanln: pushaq mScankey __key_buf popaq mov bp, word [__key_buf+2] cmp bp, word kEnter je .exit cmp bp, word kBackspace je .bs mPrint __key_buf+2 mov [rbx], word bp add rbx, 2 jmp __scanln .exit: ;mPrintln mov [rbx], word cNull ret .bs: cmp rbx, rcx je __scanln mPrint __bs_string sub rbx, 2 mov [rbx], word cNull jmp __scanln
; ioefi.inc  macro mScanln _buffer { if ~ buffer eq  mov rbx, _buffer mov rcx, rbx call __scanln end if }

Ура! Но это не всё. если последний пункт выполняется условным прыжком, то сравнение строк ещё не реализовано! Что же, не проблема — я набросал 2 макроса: для сравнения строк целиком и для сравнения, начинается одна строка с другой или нет. Выжло как-то так:

; libuefi.inc  __compare_unicode_strings: rep cmpsw mov rbx, rsi mCall __length_unicode_string mov rcx, rdx mov rbx, rdi mCall __length_unicode_string cmp rcx, rdx ret  __compare_unicode_strings_prefixes: repnz cmpsw ret  __length_unicode_string: mov ax, word [rbx] test ax, ax jz .exit inc rcx add rbx, 2 jmp __length_unicode_string .exit: ret
; macroefi.inc  macro mCompareStrings str1, str2 { lea rsi, [str1] lea rdi, [str2] mCall __compare_unicode_strings }  macro mCompareStringsPrefixes str1, str2 { lea rsi, [str1] lea rdi, [str2] mCall __compare_unicode_strings_prefixes }  macro mLenString _str, _dest { mov rbx, _str xor rcx, rcx mCall __length_unicode_string mov _dest, rcx }  ; так же набросал эти два макроса, но они не так важны и используется ; только в одной функции из файла libuefi.inc  macro pushaq { push rax rbx rcx rdx push rbp rsi rdi;rsp }  macro popaq { pop rdi rsi rbp;rsp pop rdx rcx rbx rax }

Соединяем всё вместе

Итак, напишем программу, которая будет имитировать терминал:

; bootx64.asm  ; импортируем библиотеку include "include/uefi+.inc"   ; определяем макросы mDefMacros  ; простите, не могу без этого :_) nl EQU cNl   ; определяем точку входа mEntry main  ; определяем системные функции mDefSystem  ; секция кода section ".text" code executable readable  ; главная функция main: ; готовим библиотеку     mInit fatal_error     ; пишем, что все ОК     mPrintln str_start_OK     ; внимание: мы - это админ     mPrintln str_admin_WARN     ; переход на новую строку     mNewline 1 .command_loop:     mNewline 1     ; читаем строку     mScanln command     ; сравниваем и исполняем     mCompareStrings command, str_cmd_help     je execute.help     mCompareStrings command, str_cmd_exit     je execute.exit     mCompareStrings command, str_cmd_boot     je execute.boot     mCompareStringsPrefixes command, str_cmd_echo     je execute.echo     jmp .command_loop     ; выход со статусом УСПЕШНО .exit:     ;     mExit EFI_SUCCESS ; фатальная ошибка fatal_error:     mPrint str_fatal_FAIL     mWaitkey kEnter     mExit EFI_ERROR     jmp fatal_error ; функции - комманды execute: .help:     mPrintln str_help_NORM     jmp main.command_loop .echo:     mPrintln command+10     jmp main.command_loop .exit:     mPrint str_exit_NORM @@: ; в цикле ждем [Enter] или [Escape]     mScankey key     cmp word [key.unicode], kEnter     je main.exit     cmp word [key.unicode], kEscape     je main.command_loop     jmp @b .boot:     mPrintln str_boot_FAIL     jmp main.command_loop  ; определяем системные данные mDefLibrary  section ".data" data readable writeable  ; тут всякие строки str_start_OK sString '[  OK  ] Started: /efi/boot/boox64.efi' str_admin_WARN sString "[ WARN ] You are logged in as admin" str_fatal_FAIL sString '[ FAIL ] Fatal error occurred. Press [Enter] for exit...' str_help_NORM sString 'VUS - Very Useless Shell', nl, nl, "help - shows this message", nl, 'echo [message] - echoes typed message', nl, "exit - exit VUS", nl, "boot - not implemented" str_boot_FAIL sString "[ FAIL ] Not implemented" str_exit_NORM sString "Press [Enter] for exit or [Esc] for cancel" str_cmd_help sString "help" str_cmd_echo sString "echo " str_cmd_exit sString "exit" str_cmd_boot sString "boot"  ; буффер для комманды command sStrbuf 512  ; буффер клавиши key sKey  ; объявляем системные поля mEfidata  ; зачем-то нужна `\_(._.)_/` section ".reloc" fixups data discardable 

Итоги

В течении статьи мы обновили библиотеку и создали простой терминал. В целом, из этого уже можно что-нибудь да сделать (там, псевдогравические игры или программы без сохрания изменений и пр.). Надеюсь, эта статья будет кому-нибудь полезна. Не забудьте посетить мой гитхаб! Там есть всякие интересности :).

Поддержка

Я буду рад полезным ссылкам на документацию, рекоммендациям как улучшить статью и всяким советам. Так же приминаются полезные пулл реквесты :). Спасибо за внимание!

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Да или нет?
68.18% Точно да 15
4.55% Да 1
13.64% Скорее да, чем нет 3
4.55% Не знаю 1
0% Скорее нет, чем да 0
0% Нет 0
9.09% Точно нет 2
Проголосовали 22 пользователя. Воздержались 9 пользователей.

ссылка на оригинал статьи https://habr.com/ru/post/681374/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *