Ассемблер несомненно - фантастический язык программирования, который мы можем использовать для работы (в оригинале контроля компонентов) с различными электронными компонентами, это язык близкий к машинному языку. В начале компьютерной эры многие разработчики использовали Ассемблер для компиляции кода. Затем записывали на дискеты. Так, например, компания Microsoft выпустила систему MSDOS.
Из этой статьи вы узнаете, как напечатать строку (вывод символов без операционной системы. прим. переводчика), используя 16-битный Ассемблер и о том, как такая программа спроектирована.
Требования
Перед тем как мы начнём углубляться в суть вопроса, нам потребуется найти инструменты для нашей задачи.
Необходимые инструменты
- NASM - для компиляции кода
- QEMU - для эмуляции скомпилированного кода
Создание кода
Существует два пути. Создайте файл example1.asm и откройте его. Добавим некоторые инструкции в первые строки:
ORG устанавливает загрузочный адрес, значение которого 0x7C00. Любопытно, чтоКод (Text):
ORG 0x7C00 BITS 16
это первый адрес, к которому компьютер обращается при загрузке. Этот адрес также известен как MBR (Master Boot Record - главная загрузочная запись). Также любопытно, что ёмкость этой записи 512 байт.
BITS означает, что мы пишем 16-битный код.
Продолжим писать программу:
mov ah, 0x0E: мы будем использовать функцию MOV, чтобы установить AH 0x0E (AH = 0x0E). AH — старший байтовый регистр, используется для возврата значений из вызываемых функций. 0x0E это функция БИОСа для вывода символов в режиме TTY (Teletype. Телетайп).Код (Text):
mov ah, 0x0E mov al, «O" int 0x10
mov al, "0":установим значение 0 для (AL=0). AL похож на AH, разница в том, что это регистр младшего байта. Мы используем его для хранения символа, который будет напечатан.
int 0x10: добавим для осуществления вызова. INT - инструкция, представленная в процессорах x86, её функция в том, чтобы вызвать прерывание, переведённое в байтовое значение.
Теперь запишем последние строки:
jmp $: перейдём к символу «$», - это говорит о том, что мы зацикливаем программу. Она будет работать без остановки.Код (Text):
jmp $ times 510 - ($-$$) db 0 dw 0xAA55
times 510 — ($-$$) db 0: заполним наш код в 512 байт (видимо, имеется виду заполнение всего загрузочного сектора. прим. переводчика) для корректной работы.
dw 0xAA55: запишем в конец нашего кода «магическое» число 0xAA55.
Запомним, что этот код хранит загрузочный сектор (bootloader - загрузчик).
Полный код выглядит так:
Сохраните этот файл и скомпилируйте, используя следующую команду:Код (Text):
ORG 0x7C00 BITS 16 mov ah, 0x0E mov al, «O" int 0x10 jmp $ times 510 - ($-$$) db 0 dw 0xAA55
После компиляции запустите код следующей командой:Код (Text):
$ nasm -f bin -o example1.bin example1.asm
Если хотите добавить больше символов, можно добавить ещё три строки с символами и изменить символ.Код (Text):
qemu-system-x86_64 example1.bin
Пример:
Мы можем улучшить вывод строки. Ведь не очень приятно повторять код и делать изменения. Поэтому автоматизируем печать.
Создайте файл example2.asm и запишите в него следующий код:
У нас появились еще две строки, которые мы опишем ниже.Код (Text):
ORG 0x7C00 BITS 16 mov si, msg call PrintStr jmp $
mov si, msg: регистр SI мы выделим для сообщения, - это переменная, в которой мы будем хранить сообщение. Запишем остальное.
call PrintStr: мы создадим функцию, которая напечатает нашу строку (переменную msg) и вызовем её.
Соединив все строки вместе, получим "PrintStr(msg);".
Теперь напишем саму функцию:
Изучим наш код. В последней строке мы определяем переменную msg, которая хранит строку «test». Она будет добавлена в регистр SI, как упоминалось выше, поэтому значение хранится в SI.Код (Text):
PrintStr: mov ah, 0x0E mov al, [si] psloop: int 0x10 inc si mov al, [si] cmp al, 0 jne psloop ret ret msg db "test", 13, 10, 0
Первые две строки хранят код, который определяет значение в AH и AL (вызовы BIOS). Регистр SI помещается в AL. Заметим, что SI имеет квадратные скобки. Это означает, что он передает первый символ из SI в AL, а не все символы. Скомпилировать код без скобок невозможно.
Создадим подпрограмму psloop - это цикл для вывода символов переменной SI. Внутри мы начнем выполнение с 0x10 (switch), который напечатает первый символ.
inc si: INC означает инкремент, эта команда нужная, чтобы с помощью SI поместить следующий символ. Это будет символ «e», первым был «t».
mov al, [si]: AL установит символ в из SI (символ «t»).
cmp al, 0: в этой команде 0 сравнивается с AL , это означает, что если значение регистра AL не равно 0, то выполнение перейдет в подпрограмму «psloop». Это сравнение нужно для проверки всех символов в строке. Если символы есть, значение будет равно 1, если символов не осталось, то значение будет равно 0.
На другом языке программирования этот код выглядел бы так:
Если описывать функцию кратко, она напечатает первый символ, потом перейдет к следующему символу. Затем проверит, есть ли ещё символы в переменной. Если символы есть, то вернётся обратно в подпрограмму и напечатает символ, который был определён перед сравнением.Код (Text):
if (al != 0) { psloop(); }
Финальный код:
Код (Text):
ORG 0x7C00 BITS 16 mov si, msg ; char* si = msg; call PrintStr ; PrintStr(si); jmp $ PrintStr: mov ah, 0x0E mov al, [si] psloop: int 0x10 inc si mov al, [si] cmp al, 0 jne psloop ret ret msg db "test", 13, 10, 0 ; char msg[] = "test"; times 510 - ($-$$) db 0 dw 0xAA55
Компиляция:
ВыполнениеКод (Text):
$ nasm -f bin -o example2.bin example2.asm
В итоге мы напечатали сообщение.Код (Text):
$ qemu-system-x86_64 example2.bin
Автор: Mr Empy
Источник: https://mrempy.medium.com/assembly-16-bits-printing-strings-a114c72f6e43
Перевод: Иван Гаврюшин, dcc0@yandex.ru
Бонус, ссылка на интересный репозитарий, в нём ОС с линковщиком (из трёх файлов: загрзчик, линковщик, ядро,):
https://github.com/cirosantilli/x86-bare-metal-examples/blob/master/c_hello_world/run
Post Scriptum
Результаты моих доработок - микросистема:
https://gitflic.ru/project/dcc0/mix-c-89-php/blob?file=cat_on_boot.asm
С наступающим 2023 годом!
Шестнадцатибитный Ассемблер. Печать строки (без операционной системы) [перевод]
Дата публикации 30 дек 2022
| Редактировалось 31 дек 2022