Написание собственной операционной системы. Руководство по созданию простой UNIX-подобной ОС. Работа с портами: чтение и вывод

WikiHow работает по принципу вики, а это значит, что многие наши статьи написаны несколькими авторами. При создании этой статьи над ее редактированием и улучшением работали, в том числе анонимно, 90 человек(а).

Операционные системы, составленные из сотен тысяч строк кода, позволяют пользователям взаимодействовать с компьютерной техникой. Они обычно пишутся на языках программирования C, C++ и ассемблер.

Шаги

    Для начала обучитесь программированию. Знание ассемблера необходимо; настоятельно рекомендуется также иметь понятие о других дополнительных языках программирования более низкого уровня, например, С.

    Определитесь, на какое устройство вы хотите загрузить операционную систему. Это может быть CD-диск, DVD-диск, устройство флэш-памяти, жесткий диск или другой компьютер.

    Решите, какой вы хотите видеть вашу операционную систему. Должна ли это быть полная версия ОС с графическим интерфейсом пользователя (GUI) или, может быть, что-нибудь более минималистичное? Вам необходимо знать, в каком направлении двигаться, еще перед началом процесса.

    Уточните, какую платформу процессора будет поддерживать ваша операционная система. AI-32 и x86_64 являются двумя наиболее распространенными версиями для персональных компьютеров, так что их можно считать наилучшим выбором.

    Определитесь, предпочитаете ли делать все самостоятельно с нуля, или же имеются ядра, на основе которых вы бы хотели надстроить систему. Linux с нуля – проект для тех, кто желает, к примеру, создать свой собственный дистрибутив Linux.

    Выберите, собираетесь вы использовать свой собственный загрузчик или предварительно созданный унифицированный системный загрузчик Grand Unified Bootloader (GRUB). Поскольку кодирование своей собственной программы загрузки требует обширных знаний в области компьютерного обеспечения и BIOS, это может отодвинуть график программирования действующего ядра.

    Примите решение по поводу языка программирования, который собираетесь использовать. Конечно, вполне возможно разработать ОС на таком языке, как Pascal или BASIC, но предпочтительнее писать на С или ассемблере. Ассемблер совершенно необходим, т. к. некоторые важные части операционной системы требуют знания именно этого языка. C++, с другой стороны, содержит ключевые слова, требуемые для запуска полной версии ОС.

    • Чтобы собрать ОС с помощью кодов C или C++, вы, конечно, будете использовать то один компилятор, то другой. Это означает, что вы должны прочесть руководство/инструкции/документацию для выбранного компилятора C/C++, что поставляется в комплекте с программным обеспечением или доступно на веб-сайте дистрибьютора. Вам придется узнать множество сложных вещей о компиляторе, кроме того, для совершенствования C++ предстоит изучить его схему и ABI. Вы, как ожидается, поймете различные форматы исполнительных задач (ELF, PE, COFF, обычные бинарные и т.д.) и заметите, что собственный формат Windows, PE (.exe) защищен авторским правом.
  1. Выберите интерфейс программирования приложений (API). Одной из подборок хорошего API является POSIX, так как она хорошо документирован. Все системы Unix имеют, по крайней мере, частичную поддержку POSIX, так что было бы тривиально пристраивать программы Unix на вашу операционную систему.

    Определитесь с дизайном. Существуют монолитные ядра и микроядра. Монолитные ядра выполняют все службы в ядре, в то время как микроядра имеют маленькое ядро в сочетании с пользовательской реализацией сервисов. В общем, монолитные ядра быстрее, но микроядра имеют лучшую изоляцию и защиту от возможных неисправностей.

    Рассмотрите вопрос о разработке и работе в команде. Таким образом, вам потребуется меньше времени на разрешение больших проблем, что позволит создать операционную систему лучшего качества за более короткие сроки.

    Не стирайте ваш жесткий диск полностью. Помните, форматирование диска необратимо очистит все ваши данные! Используйте GRUB или другой менеджер для дублированной загрузки вашего компьютера с другой ОС, пока ваша версия не будет полностью готова функционально.

    Начните с малого. Обратите внимание в первую очередь на мелочи, такие как отображение текста и прерывания, прежде чем переходить к сложным элементам, к примеру, к управлению памятью и многозадачности.

    Сохраняйте резервную копию последней рабочей версии. Это дает определенное спокойствие в случае, если что-то пойдет абсолютно неправильно в текущей версии вашей ОС или последующих дополнениях. В случае поломки вашего компьютера и невозможности загрузки, как вы и сами понимаете, отличной возможностью станет наличие второй копии для работы, так что вы сможете устранить имеющиеся неисправности.

    Протестируйте вашу новую операционную систему на виртуальной машине. Вместо перезагрузки компьютера каждый раз после внесения изменений или передачи файлов с рабочего компьютера тестовой машине вы можете использовать приложение для запуска ОС на виртуальной машине, в то время как ваша текущая ОС продолжает работать. Приложения VM включают в себя VMWare (которая также имеет сервер в свободном доступе), альтернативный открытый исходный код, Bochs, Microsoft Virtual PC (не совместим с Linux), а также XVM VirtualBox.

    Выпустите релиз-версию. Это позволит пользователям рассказать вам о возможных недостатках в вашей операционной системе.

  2. Операционная система также должна быть удобной для пользователя, так что не забудьте добавить полезные функции, которые станут неотъемлемой частью вашего дизайна.

    • Когда разработка будет закончена, подумайте, хотите ли вы представить код в свободном доступе либо же установить частные права на него.
    • Обязательно сделайте функции безопасности вашим основным приоритетом, если хотите, чтобы ваша система была жизнеспособной.
    • Не начинайте проект разработки операционной системы с целью обучения программированию. Если вы не знаете C, C++, Pascal или какие-нибудь другие подходящие языки и свойства, в том числе типы указателя, операции с битами низкого уровня, переключение битов, встроенный ассемблер и т.д., – значит, еще не готовы для создания ОС.
    • Просматривайте такие порталы, как OSDev и OSDever, которые помогут вам улучшить собственную операционную систему. Обратите особое внимание на то, что по большинству вопросов сообщество OSDev.org предпочитает, чтобы вы самостоятельно обращались к содержанию сайта, а не присоединялись к форуму. Если вы все же решили примкнуть к рядам форумчан, для этого должны быть определенные предпосылки. Вы обязаны досконально знать C или C++ и язык x86 ассамблер. Вы также должны понимать общие и комплексные понятия в программировании, такие как Linked Lists, Queues и т.д. Сообщество OSDev в своих правилах прямо говорит о том, что никто не собирается нянчить новых программистов. Если вы пытаетесь разработать ОС, само собой разумеется, что вы «бог» в области программирования. От вас также требуется прочесть руководство по работе с процессором касательно его архитектуры, выбранной вами; например, x86 (Intel), ARM, MIPS, PPC и т.д. Такой справочник по структуре процессора можно легко найти с помощью поиска в Google («Intel Manuals», «ARM manuals» и т.д.). Не регистрируйтесь на форуме OSDev.org, чтобы задавать очевидные вопросы. Это просто приведет к ответам вроде «Read the f*** ing Manual». Для начала вы должны попробовать почитать Википедию, пособия для различных инструментов, которые собираетесь использовать.
    • Проверьте наличие потенциальных мертвых точек и других ошибок. Недочеты, тупики и другие проблемы могут повлиять на проект вашей операционной системы.
    • Если вы хотите способ попроще, представьте дистрибутивы Linux - типа Fedora Revisor, Custom Nimble X, Puppy Remaster, PCLinuxOS mklivecd или SUSE Studio и SUSE KIWI. Тем не менее, создаваемая ОС принадлежит компании, которая первой представила этот сервис (хотя у вас есть права на ее свободное распространение, изменение и запуск, как вам нравится, под GPL).
    • Хорошим решением будет создание совершенно нового раздела для разрабатываемой операционной системы.

    Предупреждения

    • Небрежное переписывание ОС на жесткий диск может повредить его полностью. Будьте осторожны
    • У вас не получится полностью готовая система за две недели. Начните с загружаемой операционной системы, а затем переходите на более интересный материал.
    • Если вы сделаете что-то опрометчивое, как, например, напишите беспорядочные байты в произвольных портах I/O, то уничтожите вашу ОС и можете (в теории) спалить ваше оборудование.
    • Не ожидайте, что будет легко построить качественную операционную систему. Существует множество сложных взаимозависимостей. Например, для того, чтобы ОС была способна работать с несколькими процессорами, ваш диспетчер памяти должен иметь «блокирующие» механизмы для предотвращения доступа лишних процессоров в один и тот же ресурс одновременно. Используемые «блоки» предполагают наличие планировщика, чтобы убедиться, что только один процессор обращается к критическому ресурсу в любой момент времени, а все остальные находятся в режиме ожидания. Тем не менее, работа планировщика зависит от присутствия диспетчера памяти. Это пример зависимости от взаимоблокировки. Нет стандартного способа разрешить подобные проблемы; каждый создатель операционной системы, как ожидается, достаточно квалифицирован, чтобы найти свой собственный вариант их решения.

Данный цикл статей посвящён низкоуровневому программированию, то есть архитектуре компьютера, устройству операционных систем, программированию на языке ассемблера и смежным областям. Пока что написанием занимаются два хабраюзера - iley и pehat . Для многих старшеклассников, студентов, да и профессиональных программистов эти темы оказываются весьма сложными при обучении. Существует много литературы и курсов, посвящённых низкоуровневому программированию, но по ним сложно составить полную и всеохватывающую картину. Сложно, прочитав одну-две книги по ассемблеру и операционным системам, хотя бы в общих чертах представить, как же на самом деле работает эта сложная система из железа, кремния и множества программ - компьютер.

Каждый решает проблему обучения по-своему. Кто-то читает много литературы, кто-то старается поскорее перейти к практике и разбираться по ходу дела, кто-то пытается объяснять друзьям всё, что сам изучает. А мы решили совместить эти подходы. Итак, в этом курсе статей мы будем шаг за шагом демонстрировать, как пишется простая операционная система. Статьи будут носить обзорный характер, то есть в них не будет исчерпывающих теоретических сведений, однако мы будем всегда стараться предоставить ссылки на хорошие теоретические материалы и ответить на все возникающие вопросы. Чёткого плана у нас нет, так что многие важные решения будут приниматься по ходу дела, с учётом ваших отзывов.

Возможно, мы умышленно будем заводить процесс разработки в тупик, чтобы позволить вам и себе полностью осознать все последствия неверно принятого решения, а также отточить на нем некоторые технические навыки. Так что не стоит воспринимать наши решения как единственно верные и слепо нам верить. Еще раз подчеркнём, что мы ожидаем от читателей активности в обсуждении статей, которая должна сильно влиять на общий процесс разработки и написания последующих статей. В идеале хотелось бы, чтобы со временем некоторые из читателей присоединились к разработке системы.

Мы будем предполагать, что читатель уже знаком с основами языков ассемблер и Си, а также элементарными понятиями архитектуры ЭВМ. То есть, мы не будем объяснять, что такое регистр или, скажем, оперативная память. Если вам не будет хватать знаний, вы всегда можете обратиться к дополнительной литературе. Краткий список литературы и ссылки на сайты с хорошими статьями есть в конце статьи. Также желательно уметь пользоваться Linux, так как все инструкции по компиляции будут приводиться именно для этой системы.

А теперь - ближе к делу. В оставшейся части статьи мы с вами напишем классическую программу «Hello World». Наш хеллоуворлд получится немного специфическим. Он будет запускаться не из какой-либо операционной системы, а напрямую, так сказать «на голом железе». Перед тем, как приступить непосредственно к написанию кода, давайте разберёмся, как же конкретно мы пытаемся это сделать. А для этого надо рассмотреть процесс загрузки компьютера.

Итак, берем свой любимый компьютер и нажимаем самую большую кнопочку на системном блоке. Видим веселую заставку, системный блок радостно пищит спикером и через какое-то время загружается операционная система. Как вы понимаете, операционная система хранится на жёстком диске, и вот тут возникает вопрос: а каким же волшебным образом операционная система загрузилась в ОЗУ и начала выполняться?

Знайте же: за это отвечает система, которая есть на любом компьютере, и имя ей - нет, не Windows, типун вам на язык - называется она BIOS. Расшифровывается ее название как Basic Input-Output System, то есть базовая система ввода-вывода. Находится BIOS на маленькой микросхемке на материнской плате и запускается сразу после нажатия большой кнопки ВКЛ. У BIOS три главных задачи:

  1. Обнаружить все подключенные устройства (процессор, клавиатуру, монитор, оперативную память, видеокарту, голову, руки, крылья, ноги и хвосты…) и проверить их на работоспособность. Отвечает за это программа POST (Power On Self Test – самотестирование при нажатии ВКЛ). Если жизненно важное железо не обнаружено, то никакой софт помочь не сможет, и на этом месте системный динамик пропищит что-нибудь зловещее и до ОС дело вообще не дойдет. Не будем о печальном, предположим, что у нас есть полностью рабочий компьютер, возрадуемся и перейдем к рассмотрению второй функции BIOS:
  2. Предоставление операционной системе базового набора функций для работы с железом. Например, через функции BIOS можно вывести текст на экране или считать данные с клавиатуры. Потому она и называется базовой системой ввода-вывода. Обычно операционная система получает доступ к этим функциям посредством прерываний.
  3. Запуск загрузчика операционной системы. При этом, как правило, считывается загрузочный сектор - первый сектор носителя информации (дискета, жесткий диск, компакт-диск, флэшка). Порядок опроса носителей можно задать в BIOS SETUP. В загрузочном секторе содержится программа, иногда называемая первичным загрузчиком. Грубо говоря, задача загрузчика - начать запуск операционной системы. Процесс загрузки операционной системы может быть весьма специфичен и сильно зависит от её особенностей. Поэтому первичный загрузчик пишется непосредственно разработчиками ОС и при установке записывается в загрузочный сектор. В момент запуска загрузчика процессор находится в реальном режиме.
Печальная новость: размер начального загрузчика должен быть всего 512 байт. Почему так мало? Для этого нам надо ознакомиться с устройством дискеты. Вот познавательная картинка:

На картинке изображена поверхность дискового накопителя. У дискеты 2 поверхности. На каждой поверхности есть кольцеобразные дорожки (треки). Каждый трек делится на маленькие дугообразные кусочки, называемые секторами. Так вот, исторически сложилось, что сектор дискеты имеет размер 512 байт. Самый первый сектор на диске, загрузочный сектор, читается BIOS"ом в нулевой сегмент памяти по смещению 0x7С00, и дальше по этому адресу передается управление. Начальный загрузчик обычно загружает в память не саму ОС, а другую программу-загрузчик, хранящуюся на диске, но по каким-то причинам (скорее всего, эта причина - размер) не влезающую в один сектор. А поскольку пока что роль нашей ОС выполняет банальный хеллоуворлд, наша главная цель - заставить компьютер поверить в существование нашей ОС, пусть даже и на одном секторе, и запустить её.

Как устроен загрузочный сектор? На PC единственное требование к загрузочному сектору - это содержание в двух его последних байтах значений 0x55 и 0xAA - сигнатуры загрузочного сектора. Итак, уже более-менее понятно, что нам нужно делать. Давайте же писать код! Приведённый код написан для ассемблера yasm .

Section .text use16 org 0x7C00 ; наша программа загружается по адресу 0x7C00 start: mov ax, cs mov ds, ax ; выбираем сегмент данных mov si, message cld ; направление для строковых команд mov ah, 0x0E ; номер функции BIOS mov bh, 0x00 ; страница видеопамяти puts_loop: lodsb ; загружаем очередной символ в al test al, al ; нулевой символ означает конец строки jz puts_loop_exit int 0x10 ; вызываем функцию BIOS jmp puts_loop puts_loop_exit: jmp $ ; вечный цикл message: db "Hello World!", 0 finish: times 0x1FE-finish+start db 0 db 0x55, 0xAA ; сигнатура загрузочного сектора

Эта короткая программа требует ряда важных пояснений. Строка org 0x7C00 нужна для того, чтобы ассемблер (имеется в виду программа, а не язык) правильно рассчитал адреса для меток и переменных (puts_loop, puts_loop_exit, message). Вот мы ему и сообщаем, что программа будет загружена в память по адресу 0x7C00.
В строках
mov ax, cs mov ds, ax
происходит установка сегмента данных (ds) равным сегменту кода (cs), поскольку в нашей программе и данные, и код хранятся в одном сегменте.

Далее в цикле посимвольно выводится сообщение «Hello World!». Для этого используется функция 0x0E прерывания 0x10 . Она имеет следующие параметры:
AH = 0x0E (номер функции)
BH = номер видеостраницы (пока не заморачиваемся, указываем 0)
AL = ASCII-код символа

В строке « jmp $ » программа зависает. И правильно, незачем ей выполнять лишний код. Однако чтобы компьютер опять заработал, придется перезагрузиться.

В строке « times 0x1FE-finish+start db 0 » производится заполнение остатка кода программы (за исключением последних двух байт) нулями. Делается это для того, чтобы после компиляции в последних двух байтах программы оказалась сигнатура загрузочного сектора.

С кодом программы вроде разобрались, давайте теперь попробуем скомпилировать это счастье. Для компиляции нам понадобится, собственно говоря, ассемблер - выше упомянутый yasm . Он есть в большинстве репозиториев Linux. Программу можно откомпилировать следующим образом:

$ yasm -f bin -o hello.bin hello.asm

Полученный файл hello.bin нужно записать в зарузочный сектор дискеты. Делается это примерно так (разумеется, вместо fd нужно подставить имя своего дисковода).

$ dd if=hello.bin of=/dev/fd

Поскольку далеко не у всех остались дисководы и дискеты, можно воспользоваться виртуальной машиной, например, qemu или VirtualBox . Для этого придётся сделать образ дискеты с нашим загрузчиком и вставить его в «виртуальный дисковод».
Создаём образ диска и заполняем его нулями:

$ dd if=/dev/zero of=disk.img bs=1024 count=1440

Записываем в самое начало образа нашу программу:
$ dd if=hello.bin of=disk.img conv=notrunc

Запускаем полученный образ в qemu:
$ qemu -fda disk.img -boot a

После запуска вы должны увидеть окошко qemu с радостной строчкой «Hello World!». На этом первая статья заканчивается. Будем рады увидеть ваши отзывы и пожелания.

рТЙЧЕФУФЧХА ЧУЕИ УЧПЙИ ЮЙФБФЕМЕК!

рТЕДЩДХЭЙЕ ЧЩРХУЛЙ НПЗМЙ ВЩФШ ОЕУЛПМШЛП ЪБРХФБООЩНЙ. оБЮБМШОБС ЪБЗТХЪЛБ, Assembler, BIOS. уЕЗПДОС НЩ ОБЛПОЕГ РЕТЕИПДЙН Л ВПМЕЕ ЙОФЕТЕУОПК Й РПОСФОПК ЮБУФЙ - НЩ ОБЮЙОБЕН РЙУБФШ СДТП. й РЙУБФШ НЩ ЕЗП ВХДЕН ОБ СЪЩЛЕ ЧЩУПЛПЗП ХТПЧОС уЙ.

ч ОБЮБМШОЩК ЪБЗТХЪЮЙЛ ПУФБМПУШ ЧОЕУФЙ ЧУЕЗП РБТХ ДПРПМОЕОЙК Й ПО ВХДЕФ РПМОПУФША ЗПФПЧ ЗТХЪЙФШ МАВЩЕ 32-ВЙФОЩЕ СДТБ.

пРТЕДЕМЕОЙЕ ПВЯЈНБ ПРЕТБФЙЧОПК РБНСФЙ

лПОЕЮОП, НПЦОП РПДУЮЙФБФШ ПВЯЈН РБНСФЙ ЧТХЮОХА Ч СДТЕ - РЕТЕВЙТБФШ БДТЕУБ ПФ 0x100000 Й РЩФБФШУС ЪБРЙУБФШ ФХДБ ЪОБЮЕОЙЕ ПФМЙЮОПЕ ПФ ОХМС Й 0xFF. еУМЙ РТЙ ЮФЕОЙЙ НЩ РПМХЮБЕН РПМХЮЕООПЕ ЪОБЮЕОЙЕ, ФП ЧУЈ ИПТПЫП, ЙОБЮЕ РБНСФШ ЛПОЮЙМБУШ - ЪБРПНЙОБЕН БДТЕУ РПУМЕДОЕЗП ХДБЮОПЗП ЮФЕОЙС, ЬФП Й ВХДЕФ ПВЯЈНПН ПРЕТБФЙЧОПК РБНСФЙ. пДОБЛП ФБЛПК УРПУПВ ЙНЕЕФ ДЧБ ОЕДПУФБФЛБ:

1) еЗП УМЕДХЕФ ЙУРПМШЪПЧБФШ ДП ЧЛМАЮЕОЙС УФТБОЙЮОПК БДТЕУБГЙЙ, ЮФПВЩ ЙНЕФШ ДПУФХР ЛП ЧУЕК ЖЙЪЙЮЕУЛПК РБНСФЙ, МЙВП ХУФТБЙЧБФШ ЪБРЙУШ ЮЕТЕЪ "ПЛОП" ЧТЕНЕООПК УФТБОЙГЩ. мЙЫОСС ФТБФБ ЧТЕНЕОЙ, РТЙ ХУМПЧЙЙ, ЮФП ФЕУФЙТПЧБОЙЕ РБНСФЙ BIOS Й ФБЛ ЧЩРПМОСЕФ РТЙ ОБЮБМШОПК ЙОЙГЙБМЙЪБГЙЙ, Б НЩ ДЕМБЕН ДЧПКОХА ТБВПФХ.

2) чУЈ ИПТПЫП РПЛБ РБНСФШ РТЕДУФБЧМСЕФ УПВПК ОЕРТЕТЩЧОЩК ХЮБУФПЛ БДТЕУПЧ, ОП ОБ УПЧТЕНЕООЩИ УЙУФЕНБИ У ВПМШЫЙН ПВЯЈНПН РБНСФЙ ЬФП РТБЧЙМП НПЦЕФ ВЩФШ ОБТХЫЕОП. л ФПНХ ЦЕ BIOS РЙЫЕФ Ч УБНХА ПВЩЮОХА РБНСФШ ФБВМЙГЩ ACPI, ЛПФПТЩЕ РТЙЗПДСФУС ПРЕТБГЙПООПК УЙУФЕНЕ Й ОЕ УФПЙФ ЙИ ЪБФЙТБФШ ДП РТПЮФЕОЙС.

йЪ ЬФПЗП УМЕДХЕФ, ЮФП МХЮЫЕ УРТПУЙФШ РТП ПВЯЈН ПРЕТБФЙЧОПК РБНСФЙ Х BIOS, ВМБЗП ПО РТЕДПУФБЧМСЕФ ЧУЕ ОЕПВИПДЙНЩЕ ЖХОЛГЙЙ.

йУФПТЙЮЕУЛЙ РЕТЧПК ЖХОЛГЙЕК ПРТЕДЕМЕОЙС ПВЯЈНБ ПРЕТБФЙЧОПК РБНСФЙ ВЩМП РТЕТЩЧБОЙЕ 0x12. пОП ОЕ РТЙОЙНБЕФ ОЙЛБЛЙИ ЧИПДОЩИ РБТБНЕФТПЧ, Ч ОБ ЧЩИПДЕ Ч ТЕЗЙУФТЕ AX УПДЕТЦЙФУС ТБЪНЕТ ВБЪПЧПК РБНСФЙ Ч ЛЙМПВБКФБИ. вБЪПЧБС РБНСФШ - ФЕ УБНЩЕ 640 лв ДПУФХРОЩЕ Ч ТЕБМШОПН ТЕЦЙНЕ. уЕКЮБУ ЧЩ ХЦЕ ОЕ УНПЦЕФЕ ОБКФЙ ЛПНРШАФЕТ, ЗДЕ ВЩ ВЩМП НЕОЕЕ 640 лв РБНСФЙ, ОП НБМП МЙ. йУРПМШЪПЧБФШ ЕЈ ОБН УНЩУМБ ОЕФ - ЕУМЙ РТПГЕУУПТ РПДДЕТЦЙЧБЕФ ЪБЭЙЭЈООЩК ТЕЦЙН, ФП ЧТСД МЙ Х ОЕЗП ВХДЕФ НЕОШЫЕ ОЕУЛПМШЛЙИ НЕЗБВБКФ РБНСФЙ.

пВЯЈНЩ РБНСФЙ ТПУМЙ Й 640 лв УФБМП НБМП. фПЗДБ РПСЧЙМБУШ ОПЧБС ЖХОЛГЙС - РТЕТЩЧБОЙЕ 0x15 AH=0x88. пОБ ЧПЪЧТБЭБЕФ Ч AX ТБЪНЕТ ТБУЫЙТЕООПК РБНСФЙ (УЧЩЫЕ 1 нв) Ч ЛЙМПВБКФБИ Ч AX. ьФБ ЖХОЛГЙС ОЕ НПЦЕФ ЧПЪЧТБЭБФШ ЪОБЮЕОЙС ВПМШЫЕ 15 нв (15 + 1 ЙФПЗП 16 нв).

лПЗДБ Й 16 нв УФБМП ОЕДПУФБФПЮОП РПСЧЙМБУШ ОПЧБС ЖХОЛГЙС - РТЕТЩЧБОЙЕ 0x15, AX=0xE801. пОБ ЧПЪЧТБЭБЕФ ТЕЪХМШФБФЩ БЦ Ч 4 ТЕЗЙУФТБИ:

AX - ТБЪНЕТ ТБУЫЙТЕООПК РБНСФЙ ДП 16 нв Ч ЛЙМПВБКФБИ
BX - ТБЪНЕТ ТБУЫЙТЕООПК РБНСФЙ УЧЕТИ 16 нв Л ВМПЛБИ РП 64 лв
CX - ТБЪНЕТ УЛПОЖЙЗХТЙТПЧБООПК ТБУЫЙТЕООПК РБНСФЙ ДП 16 нв Ч ЛЙМПВБКФБИ
DX - ТБЪНЕТ УЛПОЖЙЗХТЙТПЧБООПК ТБУЫЙТЕООПК РБНСФЙ УЧЕТИ 16 нв Ч ВМПЛБИ РП 64 лв

юФП ФБЛПЕ "УЛПОЖЙЗХТЙТПЧБООБС" РБНСФШ РТПЙЪЧПДЙФЕМЙ BIOS УХДС РП ЧУЕНХ ОЕ ДПЗПЧПТЙМЙУШ, РПЬФПНХ ОБДП РТПУФП, ЕУМЙ Ч AX Й BX ОХМЙ, ВТБФШ ЪОБЮЕОЙЕ ЙЪ CX Й DX.

оП Й ЬФПЗП ПЛБЪБМПУШ НБМП. чЕДШ ЧУЕ РЕТЕЮЙУМЕООЩЕ ЧЩЫЕ ЖХОЛГЙЙ ЙНЕАФ ПЗТБОЙЮЕОЙЕ ПВЯЈНБ РБНСФЙ Ч 4 зв, Л ФПНХ ЦЕ ОЕ ХЮЙФЩЧБАФ ФП, ЮФП РБНСФШ НПЦЕФ ВЩФШ ОЕ ОЕРТЕТЩЧОЩН ВМПЛПН. рПЬФПНХ Ч ОПЧЩИ BIOS РПСЧЙМБУШ ЕЭЈ ПДОБ ЖХОЛГЙС - РТЕТЩЧБОЙЕ 0x15, AX=0xE820. пОБ ЧПЪЧТБЭБЕФ ОЕ РТПУФП ЮЙУМП, Б ЛБТФХ РБНСФЙ. чИПДОЩЕ РБТБНЕФТЩ:

EAX=0xE820
EDX=0x534D4150 ("SMAP")
EBX - УНЕЭЕОЙЕ ПФ ОБЮБМБ ЛБТФЩ РБНСФЙ (ДМС ОБЮБМБ 0)
ECX - ТБЪНЕТ ВХЖЕТБ (ЛБЛ РТБЧЙМП 24 ВБКФБ - ТБЪНЕТ ПДОПЗП ЬМЕНЕОФБ)
ES:DI - БДТЕУ ВХЖЕТБ, ЛХДБ ОБДП ЪБРЙУБФШ ПЮЕТЕДОПК ЬМЕНЕОФ

чЩИПДОЩЕ РБТБНЕФТЩ:

EAX=0x534D4150 ("SMAP")
EBX - ОПЧПЕ УНЕЭЕОЙЕ ДМС УМЕДХАЭЕЗП ЧЩЪПЧБ ЖХОЛГЙЙ. еУМЙ 0, ФП ЧУС ЛБТФБ РБНСФЙ РТПЮЙФБОБ
ECX - ЛПМЙЮЕУФЧП ТЕБМШОП ЧПЪЧТБЭЈООЩИ ВБКФ (20 ЙМЙ 24 ВБКФБ)
ч ХЛБЪБООПН ВХЖЕТЕ УПДЕТЦЙФУС ПЮЕТЕДОПК ЬМЕНЕОФ ЛБТФЩ РБНСФЙ.

лБЦДЩК ЬМЕНЕОФ ЛБТФЩ РБНСФЙ ЙНЕЕФ УМЕДХАЭХА УФТХЛФХТХ (ОБРЙЫХ Ч УЙОФБЛУЙУЕ уЙ, РПФПНХ ЮФП ТБЪВПТ ДБООЩИ НЩ ВХДЕН ДЕМБФШ ХЦЕ Ч СДТЕ):

Struct { unsigned long long base; //вБЪПЧЩК ЖЙЪЙЮЕУЛЙК БДТЕУ ТЕЗЙПОБ unsigned long long length; //тБЪНЕТ ТЕЗЙПОБ Ч ВБКФБИ unsigned long type; // фЙР ТЕЗЙПОБ unsigned long acpi_attrs; //тБУЫЙТЕООЩЕ БФТЙВХФЩ ACPI };

рПУМЕДОЙК ЬМЕНЕОФ УФТХЛФХТЩ ОЕ ПВСЪБФЕМЕО. еЭЈ Ч ПДОПН ЙУФПЮОЙЛЕ ЧЙДЕМ, ЮФП РЕТЕД ЪБРТПУПН ЬМЕНЕОФБ УФПЙФ РПНЕУФЙФШ ФХДБ ЕДЙОЙЮЛХ. лПОЕЮОП, УЕКЮБУ НЩ ОЕ РПДДЕТЦЙЧБЕН ACPI, ОП МХЮЫЕ ЪБТБОЕЕ РПЪБВПФЙФУС П ФПН, ЮФПВЩ РПМХЮЙФШ ЛБЛ НПЦОП ВПМШЫЕ ДБООЩИ. ч ПФМЙЮЙЙ ПФ РБТБНЕФТПЧ РБНСФЙ, ЧУЈ ПУФБМШОПЕ НПЦОП МЕЗЛП ХЪОБФШ Й ЙЪ ЪБЭЙЭЈООПЗП ТЕЦЙНБ ОБРТСНХА, ВЕЪ BIOS.

тЕЗЙПОЩ РБНСФЙ, ПРЙУЩЧБЕНЩЕ ЛБТФПК, НПЗХФ ВЩФШ ОЕУЛПМШЛЙИ ФЙРПЧ:

1 - пВЩЮОБС РБНСФШ. нПЦЕФ ВЩФШ УЧПВПДОП ЙУРПМШЪПЧБОБ пу ДМС УЧПЙИ ГЕМЕК. рПЛБ НЩ ФПМШЛП Л ОЕК Й ВХДЕН ПВТБЭБФШУС, Б ЧУЈ ПУФБМШОПЕ РТПРХУЛБФШ.
2 - ъБТЕЪЕТЧЙТПЧБОП (ОБРТЙНЕТ, ЛПД BIOS). ьФБ РБНСФШ НПЦЕФ ВЩФШ ЛБЛ ЖЙЪЙЮЕУЛЙ ОЕДПУФХРОБ ДМС ЪБРЙУЙ, ФБЛ Й РТПУФП ЪБРЙУШ ФХДБ ОЕЦЕМБФЕМШОБ. фБЛХА РБНСФШ МХЮЫЕ ОЕ ФТПЗБФШ.
3 - дПУФХРОП РПУМЕ РТПЮФЕОЙС ФБВМЙГ ACPI. чЕТПСФОП, ЙНЕООП Ч ЬФЙИ ВМПЛБИ ЬФЙ ФБВМЙГЩ Й ИТБОСФУС. рПЛБ ДТБКЧЕТ ACPI ОЕ РТПЮЙФБЕФ ФБВМЙГЩ, ЬФХ РБНСФШ МХЮЫЕ ОЕ ФТПЗБФШ. рПФПН НПЦОП ЙУРПМШЪПЧБФШ ФБЛ ЦЕ, ЛБЛ Й РБНСФШ ФЙРБ 1.
4 - ьФХ РБНСФШ УМЕДХЕФ УПИТБОСФШ НЕЦДХ NVS УЕУУЙСНЙ. фБЛХА РБНСФШ НЩ ФТПЗБФШ ОЕ ВХДЕН, РПЛБ ОЕ ХЪОБЕН, ЮФП ФБЛПЕ NVS УЕУУЙЙ:-)

оЕ ЧУЕ BIOS НПЗХФ РПДДЕТЦЙЧБФШ ЬФХ ЖХОЛГЙА. еУМЙ ЛБЛБС-ФП ЖХОЛГЙС ОЕ РПДДЕТЦЙЧБЕФУС, ФП РТЙ ЧЩИПДЕ ЙЪ ОЕЈ ХУФБОПЧМЕО ЖМБЗ РЕТЕРПМОЕОЙС Й УМЕДХЕФ ПВТБЭБФШУС Л ВПМЕЕ УФБТПК. нЩ ВХДЕН ЙУРПМШЪПЧБФШ ЖПТНБФ ЛБТФЩ РБНСФЙ ЖХОЛГЙЙ 0xE820. еУМЙ УБНХ ЬФХ ЖХОЛГЙА ЧЩЪЧБФШ ОЕ РПМХЮЙМПУШ - РПМХЮБФШ ПВЯЈН РБНСФЙ ПВЩЮОЩНЙ УТЕДУФЧБНЙ Й УПЪДБЧБФШ УЧПА УПВУФЧЕООХА ЛБТФХ РБНСФЙ ЙЪ ПДОПЗП ЬМЕНЕОФБ. рПУЛПМШЛХ ПРТЕДЕМЕОЙЕ ПВЯЈНБ РБНСФЙ ЪБДБЮБ ОХЦОБС Й ДМС ЪБРХУЛБ 32-ВЙФОПЗП Й ДМС ЪБРХУЛБ 64-ВЙФОПЗП СДТБ, МХЮЫЕ ПЖПТНЙФШ ЕЈ Ч ЧЙДЕ РПДРТПЗТБННЩ. лБТФХ РБНСФЙ ТБЪНЕУФЙН РП БДТЕУХ 0x7000. оЕ ДХНБА, ЮФП ПОБ НПЦЕФ ВЩФШ ВПМШЫЕ РБТЩ ЛЙМПВБКФ. рПУМЕДОЙК ЬМЕНЕОФ ЧТХЮОХА УДЕМБЕН ФЙРБ 0 - ФБЛПЗП ФЙРБ ОЕ ЧПЪЧТБЭБЕФ BIOS Й ЬФП Й ВХДЕФ РТЙЪОБЛПН ЛПОГБ.

; рПМХЮЕОЙЕ ЛБТФЩ РБНСФЙ get_memory_map: mov di, 0x7000 xor ebx, ebx @: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 24 mov dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @: cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax, 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx, dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret

оХ ЧПФ Й ЗПФПЧ ОБЫ ОБЮБМШОЩК ЪБЗТХЪЮЙЛ ДМС 32-ВЙФОЩИ СДЕТ. ч ЪБЛМАЮЕОЙЕ РТЙЧПЦХ ЕЗП РПМОЩК ЛПД Й НЩ РЕТЕКДЈН Л СДТХ.

; оБЮБМШОЩК ЪБЗТХЪЮЙЛ СДТБ ДМС БТИЙФЕЛФХТЩ x86 format Binary as "bin" org 0x7C00 jmp boot ; ъБЗПМПЧПЛ ListFS align 4 fs_magic dd ? fs_version dd ? fs_flags dd ? fs_base dq ? fs_size dq ? fs_map_base dq ? fs_map_size dq ? fs_first_file dq ? fs_uid dq ? fs_block_size dd ? ; ъБЗПМПЧПЛ ЖБКМБ virtual at 0x800 f_info: f_name rb 256 f_next dq ? f_prev dq ? f_parent dq ? f_flags dq ? f_data dq ? f_size dq ? f_ctime dq ? f_mtime dq ? f_atime dq ? end virtual ; дБООЩЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ label sector_per_track word at $$ label head_count byte at $$ + 2 label disk_id byte at $$ + 3 reboot_msg db "Press any key...",13,10,0 boot_file_name db "boot.bin",0 ; чЩЧПД УФТПЛЙ DS:SI ОБ ЬЛТБО write_str: push si mov ah, 0x0E @: lodsb test al, al jz @f int 0x10 jmp @b @: pop si ret ; лТЙФЙЮЕУЛБС ПЫЙВЛБ error: pop si call write_str ; рЕТЕЪБЗТХЪЛБ reboot: mov si, reboot_msg call write_str xor ah, ah int 0x16 jmp 0xFFFF:0 ; ъБЗТХЪЛБ УЕЛФПТБ DX:AX Ч ВХЖЕТ ES:DI load_sector: push dx add ax, word adc dx, word cmp byte, 0xFF je .use_EDD push bx cx si div mov cl, dl inc cl div mov dh, ah mov ch, al mov dl, mov bx, di mov al, 1 mov si, 3 @: mov ah, 2 int 0x13 jnc @f xor ah, ah int 0x13 dec si jnz @b .error: call error db "DISK ERROR",13,10,0 @: pop si cx bx dx ret .use_EDD: push si mov byte, 0x10 mov byte, 0 mov word, 1 mov , di push es pop word mov , ax mov , dx mov word, 0 mov word, 0 mov ah, 0x42 mov dl, mov si, 0x600 int 0x13 jc .error pop si dx ret ; рПЙУЛ ЖБКМБ У ЙНЕОЕН DS:SI Ч ЛБФБМПЗЕ DX:AX find_file: push cx dx di .find: cmp ax, -1 jne @f cmp dx, -1 jne @f .not_found: call error db "NOT FOUND",13,10,0 @: mov di, f_info call load_sector push di mov cx, 0xFFFF xor al, al repne scasb neg cx dec cx pop di push si repe cmpsb pop si je .found mov ax, word mov dx, word jmp .find .found: pop di dx cx ret ; ъБЗТХЪЛБ ФЕЛХЭЕЗП ЖБКМБ Ч РБНСФШ РП БДТЕУХ BX:0. лПМЙЮЕУФЧП ЪБЗТХЦЕООЩИ УЕЛФПТПЧ ЧПЪЧТБЭБЕФУС Ч AX load_file_data: push bx cx dx si di mov ax, word mov dx, word .load_list: cmp ax, -1 jne @f cmp dx, -1 jne @f .file_end: pop di si dx cx mov ax, bx pop bx sub ax, bx shr ax, 9 - 4 ret @: mov di, 0x8000 / 16 call load_sector mov si, di mov cx, 512 / 8 - 1 .load_sector: lodsw mov dx, add si, 6 cmp ax, -1 jne @f cmp dx, -1 je .file_end @: push es mov es, bx xor di, di call load_sector add bx, 0x200 / 16 pop es loop .load_sector lodsw mov dx, jmp .load_list ; фПЮЛБ ЧИПДБ Ч ОБЮБМШОЩК ЪБЗТХЪЮЙЛ boot: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ jmp 0:@f @: mov ax, cs mov ds, ax mov es, ax ; оБУФТПЙН УФЕЛ mov ss, ax mov sp, $$ ; тБЪТЕЫЙН РТЕТЩЧБОЙС sti ; ъБРПНОЙН ОПНЕТ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov , dl ; пРТЕДЕМЙН РБТБНЕФТЩ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov ah, 0x41 mov bx, 0x55AA int 0x13 jc @f mov byte, 0xFF jmp .disk_detected @: mov ah, 0x08 xor di, di push es int 0x13 pop es jc load_sector.error inc dh mov , dh and cx, 111111b mov , cx .disk_detected: ; ъБЗТХЪЙН РТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ mov si, boot_file_name mov ax, word mov dx, word call find_file mov bx, 0x7E00 / 16 call load_file_data ; рЕТЕИПДЙН ОБ РТПДПМЦЕОЙЕ jmp boot2 ; рХУФПЕ РТПУФТБОУФЧП Й УЙЗОБФХТБ rb 510 - ($ - $$) db 0x55,0xAA ; дПРПМОЙФЕМШОЩЕ ДБООЩЕ ЪБЗТХЪЮЙЛБ load_msg_preffix db "Loading "",0 load_msg_suffix db ""...",0 ok_msg db "OK",13,10,0 config_file_name db "boot.cfg",0 start16_msg db "Starting 16 bit kernel...",13,10,0 start32_msg db "Starting 32 bit kernel...",13,10,0 label module_list at 0x6000 label memory_map at 0x7000 ; тБЪВЙЕОЙЕ УФТПЛЙ DS:SI РП УЙНЧПМХ УМЕЫБ split_file_name: push si @: lodsb cmp al, "/" je @f test al, al jz @f jmp @b @: mov byte, 0 mov ax, si pop si ret ; ъБЗТХЪЛБ ЖБКМБ У ЙНЕОЕН DS:SI Ч ВХЖЕТ BX:0. тБЪНЕТ ЖБКМБ Ч УЕЛФПТБИ ЧПЪЧТБЭБЕФУС Ч AX load_file: push si mov si, load_msg_preffix call write_str pop si call write_str push si mov si, load_msg_suffix call write_str pop si push si bp mov dx, word mov ax, word @: push ax call split_file_name mov bp, ax pop ax call find_file test byte, 1 jz @f mov si, bp mov dx, word mov ax, word jmp @b @: call load_file_data mov si, ok_msg call write_str pop bp si ret ; рПМХЮЕОЙЕ ЛБТФЩ РБНСФЙ get_memory_map: mov di, memory_map xor ebx, ebx @: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 24 mov dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @: cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax, 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx, dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret ; рТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ boot2: ; ъБЗТХЪЙН ЛПОЖЙЗХТБГЙПООЩК ЖБКМ ЪБЗТХЪЮЙЛБ mov si, config_file_name mov bx, 0x1000 / 16 call load_file ; чЩРПМОЙН ЪБЗТХЪПЮОЩК УЛТЙРФ mov bx, 0x9000 / 16 mov bp, module_list mov dx, 0x1000 .parse_line: mov si, dx .parse_char: lodsb test al, al jz .config_end cmp al, 10 je .run_command cmp al, 13 je .run_command jmp .parse_char .run_command: mov byte, 0 xchg dx, si cmp byte, 0 je .parse_line ; рХУФБС УФТПЛБ cmp byte, "#" je .parse_line ; лПННЕОФБТЙК cmp byte, "L" je .load_file ; ъБЗТХЪЛБ ЖБКМБ cmp byte, "S" je .start ; ъБРХУЛ СДТБ; оЕЙЪЧЕУФОБС ЛПНБОДБ mov al, mov [.cmd], al call error db "Unknown boot script command "" .cmd db ? db ""!",13,10,0 .config_end: ; рТЙ РТБЧЙМШОПН ЛПОЖЙЗХТБГЙПООПН ЖБКМЕ НЩ ОЕ ДПМЦОЩ УАДБ РПРБУФШ; ъБЧЕТЫЕОЙЕ jmp reboot ; ъБЗТХЪЛБ ЖБКМБ.load_file: push dx inc si call load_file push ax mov cx, 512 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 mov ax, bx mov cx, 16 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 pop ax shr ax, 9 - 4 add bx, ax add bp, 16 pop dx jmp .parse_line ; ъБРХУЛ СДТБ.start: ; рТПЧЕТЙН, ЮФП ЪБЗТХЦЕО ИПФС ВЩ ПДЙО ЖБКМ cmp bx, 0x9000 / 16 ja @f call error db "NO KERNEL LOADED",13,10,0 @: ; ъБРПМОСЕН РПУМЕДОЙК ЬМЕНЕОФ УРЙУЛБ ЖБКМПЧ xor ax, ax mov cx, 16 mov di, bp rep stosw ; рЕТЕИПДЙН Л РТПГЕДХТЕ ЙОЙГЙБМЙЪБГЙЙ СДТБ ДМС ОХЦОПК ТБЪТСДОПУФЙ inc si cmp word, "16" je .start16 cmp word, "32" je .start32 ;cmp word, "64" ;je .start64 ; оЕЙЪЧЕУФОБС ТСЪТСДОПУФШ СДТБ call error db "Invalid start command argument",13,10,0 ; ъБРХУЛ 16-ТБЪТСДОПЗП СДТБ.start16: mov si, start16_msg mov bx, module_list mov dl, jmp 0x9000 ; ъБРХУЛ 32-ТБЪТСДОПЗП СДТБ.start32: ; чЩЧПДЙН ХЧЕДПНМЕОЙЕ П ЪБРХУЛЕ 32-ВЙФОПЗП СДТБ mov si, start32_msg call write_str ; рТПЧЕТЙН, ЮФП РТПГЕУУПТ ОЕ ИХЦЕ i386 mov ax, 0x7202 push ax popf pushf pop bx cmp ax, bx je @f call error db "Required i386 or better",13,10,0 @: ; рПМХЮЙН ЛБТФХ РБНСФЙ call get_memory_map ; пЮЙУФЙН ФБВМЙГЩ УФТБОЙГ xor ax, ax mov cx, 3 * 4096 / 2 mov di, 0x1000 rep stosw ; ъБРПМОЙН ЛБФБМПЗ УФТБОЙГ mov word, 0x2000 + 111b mov word, 0x3000 + 111b ; ъБРПМОЙН РЕТЧХА ФБВМЙГХ УФТБОЙГ mov eax, 11b mov cx, 0x100000 / 4096 mov di, 0x2000 @: stosd add eax, 0x1000 loop @b ; ъБРПМОЙН РПУМЕДОАА ФБВМЙГХ УФТБОЙГ mov di, 0x3000 mov eax, dword or eax, 11b mov ecx, dword shr ecx, 12 @: stosd add eax, 0x1000 loop @b mov word, 0x4000 + 11b ; Kernel stack mov word, 0x3000 + 11b ; Kernel page table ; ъБЗТХЪЙН ЪОБЮЕОЙЕ Ч CR3 mov eax, 0x1000 mov cr3, eax ; ъБЗТХЪЙН ЪОБЮЕОЙЕ Ч GDTR lgdt ; ъБРТЕФЙН РТЕТЩЧБОЙС cli ; рЕТЕКДЈН Ч ЪБЭЙЭЈООЩК ТЕЦЙН mov eax, cr0 or eax, 0x80000001 mov cr0, eax ; рЕТЕКДЈН ОБ 32-ВЙФОЩК ЛПД jmp 8:start32 ; фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ДМС 32-ВЙФОПЗП СДТБ align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF ; CODE - 8 dq 0x00CF92000000FFFF ; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32 ; 32-ВЙФОЩК ЛПД use32 start32: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ Й УФЕЛ mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0xFFFFDFFC ; рПНЕУФЙН Ч DL ОПНЕТ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov dl, ; рПНЕУФЙН Ч EBX БДТЕУ УРЙУЛБ ЪБЗТХЦЕООЩИ ЖБКМПЧ mov ebx, module_list ; рПНЕУФЙН Ч ESI БДТЕУ ЛБТФЩ РБНСФЙ mov esi, memory_map ; рЕТЕИПДЙН ОБ СДТП jmp 0xFFC00000

рЕТЧПЕ СДТП

сДТП РПЛБ Х ОБУ ВХДЕФ УПУФПСФШ ЙЪ ДЧХИ ЖБКМПЧ - startup.asm Й main.c. startup.asm ОХЦЕО ДМС ФПЗП, ЮФПВЩ ВЩФШ ХЧЕТЕООЩНЙ, ЮФП ХРТБЧМЕОЙЕ РПРБДЈФ ОБ ЖХОЛГЙА kernel_main. чЕДШ ПОБ НПЦЕФ ВЩФШ ОЕ Ч ОБЮБМЕ ЖБКМБ, Б УПДЕТЦЙНПЕ startup.o НЩ РПМОПУФША ЛПОФТПМЙТХЕН Й ЕУМЙ ХЛБЦЕН ЕЗП РЕТЧЩН МЙОЛЕТХ, ФП ВХДЕН ХРТБЧМСФШ Й РЕТЧЩНЙ ВБКФБНЙ ДЧПЙЮОПЗП ЖБКМБ.

Format ELF public _start extrn kernel_main section ".text" executable _start: movzx edx, dl push edx push esi push ebx lgdt call kernel_main @: ;cli ;hlt jmp @b section ".data" writable gdt: dq 0 dq 0x00CF9A000000FFFF dq 0x00CF92000000FFFF gdtr: dw $ - gdt dd gdt

оХ ЧПФ Й РПУМЕДОЙК ОБЫ ЛПД ОБ ЮЙУФПН Assembler:-). пО ЧЩРПМОСЕФ РТПУФЕКЫХА ЪБДБЮХ - ХМПЦЙФШ Ч УФЕЛ ФТЙ БТЗХНЕОФБ ДМС ЖХОЛГЙЙ kernel_main Й РЕТЕДБФШ ОБ ОЕЈ ХРТБЧМЕОЙЕ. рПУМЕ ЧПЪЧТБФБ ЙЪ ОЕЈ СДТП ХИПДЙФ Ч ВЕУЛПОЕЮОЩК ГЙЛМ. рП УПЗМБЫЕОЙА ЧЩЪПЧБ ЖХОЛГЙК уЙ РБТБНЕФТЩ УМЕДХЕФ РЙИБФШ Ч УФЕЛ Ч ПВТБЪПН РПТСДЛЕ. фБЛЦЕ ЬФПФ ЛПД ЙОЙГЙБМЙЪБГЙЙ ЪБЗТХЦБЕФ ОПЧПЕ ЪОБЮЕОЙЕ Ч GDTR - ФЕРЕТШ ФБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ОБИПДЙФУС Ч РТПУФТБОУФЧЕ СДТБ Й ДБЦЕ ЕУМЙ НЩ ПФНПОФЙТХЕН РЕТЧЩК НЕЗБВБКФ ОЕ РТПЙЪПКДЈФ ОЙЛБЛЙИ ПЫЙВПЛ.

б ФЕРЕТШ УБНПЕ ЧЛХУОПЕ - РТПУФЕКЫЕЕ СДТП ОБ СЪЩЛЕ ЧЩУПЛПЗП ХТПЧОС:

Typedef struct { unsigned long long base; unsigned long long size; } BootModuleInfo; void kernel_main(char boot_disk_id, void *memory_map, BootModuleInfo *boot_module_list) { char *screen_buffer = (void*)0xB8000; char *msg = "Hello world!"; unsigned int i = 24 * 80; while (*msg) { screen_buffer = *msg; msg++; i++; } }

ьФП СДТП ОЕ ДЕМБЕФ ОЙЮЕЗП ПУПВЕООПЗП - РТПУФП ЧЩЧПДЙФ УФТПЛХ "Hello world!" ОБ РПУМЕДОАА УФТПЮЛХ ФЕЛУФПЧПЗП ЬЛТБОБ. уФТХЛФХТБ ПРЙУБООБС Ч ОБЮБМЕ ВХДЕФ ОХЦОБ ДМС ДПУФХРБ Л УРЙУЛХ ЪБЗТХЦЕООЩИ НПДХМЕК.

чБЦОП РПНОЙФШ, ЮФП ОЙЛБЛПК УФБОДБТФОПК ВЙВМЙПФЕЛЙ Х ОБУ ОЕФ - ОБН ДПУФХРОЩ ФПМШЛП ФЕ ЖХОЛГЙЙ, ЛПФПТЩЕ НЩ УДЕМБЕН УБНЙ. чУЕ printf, strcpy, memcpy Й Ф. Р. РТЙДЈФУС ТЕБМЙЪПЧЩЧБФШ УБНПУФПСФЕМШОП, ОЕ РЩФБКФЕУШ ПВТБФЙФШУС Л ОЙН. ч УМЕДХАЭЕН ЧЩРХУЛЕ НЩ ЪБКНЈНУС УПЪДБОЙЕН ОБЫЕЗП УПВУФЧЕООПЗП ЦХФЛП ХТЕЪБООПЗП БОБМПЗБ libc, ЮФПВЩ РТПЗТБННЙТПЧБФШ ВЩМП ХДПВОЕЕ. фХФ ОБЮЙОБЕФУС УБНБС ЙОФЕТЕУОБС ЮБУФШ, Б РТЙОСФЩЕ ТЕЫЕОЙС ЧП НОПЗПН РПЧМЙСАФ ОБ ЧУА УФТХЛФХТХ УЙУФЕНЩ.

уВПТЛБ СДТБ

йУРПМОСЕНЩЕ ЖБКМЩ УПВЙТБАФУС Ч ДЧБ ЬФБРБ - ЛПНРЙМСГЙС, Б РПФПН МЙОЛПЧЛБ. оБ РЕТЧПН ЬФБРЕ ЛПНРЙМСФПТ РТЕПВТБЪХЕФ ЙУИПДОЩК ЛПД Ч ЛПНБОДЩ РТПГЕУУПТБ Й УПИТБОСЕФ ЧУЈ ЬФП Ч ПВЯЕЛФОЩК ЖБКМ. лБЦДЩК НПДХМШ УЙУФЕНЩ УПИТБОСЕФУС Ч ПФДЕМШОПН ЖБКМЕ. ч ЬФПН ЖБКМЕ ФБЛ ЦЕ УПДЕТЦЙФУС ЙОЖПТНБГЙС П ЖХОЛГЙСИ, ПРЙУБООЩИ Ч НПДХМЙ, РПЬФПНХ ЙЪ ПДОПЗП ЖБКМБ НПЦОП УЧПВПДОП ЧЩЪЩЧБФШ ЖХОЛГЙА ЙЪ ДТХЗПЗП. чЕУШ ЛПД Ч ПВЯЕЛФОЩИ ЖБКМБИ ОЕ РТЙЧСЪБО Л ЛПОЛТЕФОЩН БДТЕУБН. оБ ЧФПТПН ЬФБРЕ МЙОЛЕТ УПВЙТБЕФ ЧУЕ ПВЯЕЛФОЩЕ ЖБКМЩ Ч ПДЙО ВЙОБТОЩК. рТЙ ЬФПН ЛПД РТЙЧСЪЩЧБЕФУС Л ЛПОЛТЕФОЩН БДТЕУБН (ЕУМЙ, ЛПОЕЮОП, НЩ ОЕ УПВЙТБЕН ДЙОБНЙЮЕУЛЙ ЪБЗТХЦБЕНХА ВЙВМЙПФЕЛХ), ЧНЕУФП УУЩМПЛ ОБ ЖХОЛГЙЙ РПДУФБЧМСАФУС ОХЦОЩЕ БДТЕУБ. оБН ОХЦОП РПМХЮЙФШ ОБ ЧЩИПДЕ ПУПВЩК ДЧПЙЮОЩК ЖБКМ. ьФП РТПУФП ЛПД Й ДБООЩЕ, ВЕЪ ЛБЛЙИ-МЙВП ЪБЗПМПЧЛПЧ (ФП ЕУФШ ЬФП ОЕ PE Й ОЕ ELF). ч ЛБЮЕУФЧЕ ВБЪПЧПЗП БДТЕУБ ЙУРПМШЪХЕФУС БДТЕУ 0xFFC00000. дМС ХРТПЭЕОЙС ЬФПЗП НЩ ПРЙЫЕН ЧУЈ, ЮФП ОБН ОХЦОП Ч УРЕГЙБМШОПН ЖПТНБФЕ УЛТЙРФБ ld:

OUTPUT_FORMAT("binary") ENTRY(_start) SECTIONS { .text 0xFFC00000: { *(.text) *(.code) *(.rodata*) } .data ALIGN(0x1000) : { *(.data) } .bss ALIGN(0x1000) : { *(.bss) } .empty ALIGN(0x1000) - 1: { BYTE(0) } }

ьФПФ УЛТЙРФ ЗПЧПТЙФ, ЮФП ОБЫ ЖБКМ ВХДЕФ МЕЦБФШ Ч РБНСФЙ ОЕРТЕТЩЧОЩН ВМПЛПН ОБЮЙОБС У БДТЕУБ 0xFFC00000. ч УБНПН ОБЮБМЕ ВХДЕФ ЙДФЙ УЕЛГЙС ЛПДБ, РПФПН УЕЛГЙС read-only ДБООЩИ, ЪБФЕН ПВЩЮОЩИ ДБООЩИ, РПФПН ОЕЙОЙГЙБМЙЪЙТПЧБООЩИ. чУЕ УЕЛГЙЙ ЧЩТПЧОЕОЩ ОБ ТБЪНЕТ УФТБОЙГЩ 4 лв (ЧДТХЗ НЩ РПФПН ЪБИПФЙН ЪБЭЙФЙФШ ОБ ХТПЧОЕ ФБВМЙГЩ УФТБОЙГ ЛПД ПФ ЪБРЙУЙ). рПУМЕДОЕЕ ПРЙУБОЙЕ УЕЛГЙЙ.empty ОЕПВИПДЙНП ДМС ФПЗП, ЮФПВЩ ДБЦЕ ОЕЙОЙГЙБЙМЙЪПТПЧБООЩЕ РЕТЕНЕООЩЕ ЪБОЙНБМЙ НЕУФП Ч ЖБКМЕ (ФБН ВХДХФ ОХМЙ). чЕДШ ОБЮБМШОЩК ЪБЗТХЪЮЙЛ ЧЩДЕМСЕФ РБНСФШ ДМС СДТБ ТХЛПЧПДУФЧХСУШ ТБЪНЕТПН ЖБКМБ.

уПВТБФШ ЧУЈ СДТП НПЦОП УМЕДХАЭЙНЙ ЛПНБОДБНЙ:

Fasm startup.asm startup.o gcc -c -m32 -ffreestanding -o main.o main.c ld --oformat=binary -melf_i386 -T script.ld -o kernel.bin startup.o main.o

рБТБНЕФТ GCC -ffreestanding ХЛБЪЩЧБЕФ ЕНХ ПФЛМАЮЙФШ ЧУЕ УФБОДБТФОЩЕ ВЙВМЙПФЕЛЙ. чЕДШ ПОЙ РТЙЧСЪБОЩ Л ЛПОЛТЕФОПК ПРЕТБГЙПООПК УЙУФЕНЕ, Б НЩ РЙЫЕН ОПЧХА.

уВПТЛБ ПВТБЪБ ДЙУЛБ

пВПКДХУШ ВЕЪ МЙЫОЙИ ЛПННЕОФБТЙЕЧ Й РТПУФП РТЙЧЕДХ МЙОХЛУПЧЩК УЛТЙРФ УВПТЛЙ ПВТБЪБ:

Dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel.bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 size=2880 boot=bin/boot_sector.bin src=./disk

пО РТЕДРПМБЗБЕФ, ЮФП ЧУЕ УЛПНРЙМЙТПЧБООЩЕ ЖБКМЩ МЕЦБФ Ч bin Ч ФЕЛХЭЕН ЛБФБМПЗЕ, Б ЕЭЈ ЙНЕЕФУС ЛБФБМПЗ disk, Ч ЛПФПТПН МЕЦЙФ boot.cfg УМЕДХАЭЕЗП УПДЕТЦБОЙС:

# Loading kernel Lkernel.bin # Boot 32 bit kernel S32

еУМЙ ЧЩ ЧУЈ УДЕМБМЙ РТБЧЙМШОП, РПМХЮЕООЩК ПВТБЪ НПЦОП ЪБРХУФЙФШ Ч ЬНХМСФПТЕ ЙМЙ ДБЦЕ ОБ ТЕБМШОПН ЦЕМЕЪЕ Й ЧЩ РПМХЮЙФЕ РПДПВОХА ЛБТФЙОХ:

ъБЗТХЪЮЙЛ УЮЙФЩЧБЕФ ЛПОЖЙЗХТБГЙПООЩК ЖБКМ, ЪБЗТХЦБЕФ СДТП, РЕТЕИПДЙФ Ч ЪБЭЙЭЈООЩК ТЕЦЙН Й РЕТЕДБЈФ ЕНХ ХРТБЧМЕОЙЕ. рПМХЮЙЧ ЕЗП, ОБЫЕ СДТП ЧЩЧПДЙФ РПУМЕДОАА УФТПЛХ ОБ ЬЛТБО. ьФП МЙЫШ ОБЮБМП ДПМЗПЗП РХФЙ, НЩ РЕТЕИПДЙН Л УБНПК ЙОФЕТЕУОПК ЮБУФЙ ТБЪТБВПФЛЙ. фЕРЕТШ ЧЩРХУЛЙ ВХДХФ ЗПТБЪДП ВПМЕЕ РТПУФЩН ДМС ЧПУРТЙСФЙС, ВМБЗПДБТС ЙУРПМШЪПЧБОЙА СЪЩЛБ ЧЩУПЛПЗП ХТПЧОС, ЛПФПТЩК ЛБЛ С ОБДЕАУШ ЧУЕ Й ФБЛ ЪОБАФ. еУМЙ ЧЩ ОЕ ИПФЙФЕ ТБЪВЙТБФШУС У Assembler, НПЦЕФЕ РТПУФП ЧЪСФШ НПК ЗПФПЧЩК ЪБЗТХЪЮЙЛ Й startup.asm Й ЙЪНЕОСФШ ХЦЕ ФПМШЛП УПДЕТЦЙНПЕ main.c, РПУЛПМШЛХ ЧЕУШ ЛПД ДП ЬФПЗП ОЕ ДЙЛФХЕФ ЦЈУФЛП ЛБЛЙЕ-МЙВП РБТБНЕФТЩ СДТБ (ЛТПНЕ жу У ЛПФПТПК НЩ ЪБЗТХЦБЕНУС) Й РПЪЧПМСЕФ РПУФТПЙФШ ОБ УЧПЕК ВБЪЕ ЮФП ХЗПДОП.

бЧФПНБФЙЪБГЙС УВПТЛЙ ЙМЙ Makefile

чЩ НПЗМЙ ЪБНЕФЙФШ, ЮФП ЧТХЮОХА ОБВЙЧБФШ УФПМШЛП ЛПНБОД ДПУФБФПЮОП ХФПНЙФЕМШОП. л ФПНХ ЦЕ ОЕ ЧУЕЗДБ ЕУФШ ОЕПВИПДЙНПУФШ РЕТЕЛПНРЙМЙТПЧБФШ ЧУЕ ЖБКМЩ. оБРТЙНЕТ, ЕУМЙ startup.asm ОЕ ВЩМ ЙЪНЕОЈО, НПЦОП ОЕ ЧЩЪЩЧБФШ fasm. уРЕГЙБМШОП ДМС ХРТПЭЕОЙС ЛПНРЙМСГЙЙ РТЙМПЦЕОЙК ВЩМБ РТЙДХНБОБ ХФЙМЙФБ make, ЛПФПТБС ЧИПДЙФ Ч УФБОДБТФОХА РПУФБЧЛХ GCC Й MinGW.

мАВПК Makefile УФПЙФ ЙЪ ОБВПТБ РТБЧЙМ У ФБЛПК УФТХЛФХТПК:

ЙНСгЕМЙйМЙжБКМБ: йНСрЕТЧПЗПйУИПДОПЗПжБКМБ йНСчФПТПЗПйУИПДОПЗПжБКМБ... лПНБОДЩлПНРЙМСГЙЙ

рЕТЧПЕ РТБЧЙМП, ЛПФПТПЕ ДПМЦОП ВЩФШ Ч МАВПН Makefile - ГЕМШ all. make УНПФТЙФ ОБ ЪБЧЙУЙНПУФЙ ГЕМЙ all Й ЛПНРЙМЙТХЕФ ЙИ, Б ЪБФЕН ЧЩРПМОСЕФ ЛПНБОДЩ Й ЬФПК ГЕМЙ. дМС ЛБЦДПК ДТХЗПК ГЕМЙ УОБЮБМБ УПВЙТБАФУС ЕЈ ЪБЧЙУЙНПУФЙ. рТЙ ЬФПН ЙНС ГЕМЙ Й ЙНС ЪБЧЙУЙНПУФЕК НПЗХФ УПЧРБДБФШ У ЙНЕОБНЙ ТЕБМШОЩИ ЖБКМПЧ. ч ФБЛПН УМХЮБЕ РЕТЕУВПТЛБ ГЕМЙ РТПЙЪПКДЈФ ФПМШЛП ЕУМЙ ЙУИПДОЙЛЙ ВЩМЙ ЙЪНЕОЕОЩ.

еЭЈ ПДОБ ГЕМШ, ЛПФПТБС ЮБУФП ЙУРПМШЪХЕФУС Ч Makefile - clean. еЈ ЪБДБЮБ ХДБМЙФШ ЧУЕ ВЙОБТОЩЕ ЖБКМЩ, ЮФПВЩ ОБЮБФШ УВПТЛХ "У ЮЙУФПЗП МЙУФБ". чПФ ФБЛ НПЦЕФ ЧЩЗМСДЕФШ Makefile ДМС СДТБ:

All: startup.o main.o script.ld ld --oformat=binary -melf_i386 -T script.ld -o kernel.bin startup.o main.o startup.o: startup.i386.asm fasm startup.i386.asm startup.o main.o: main.c gcc -c -m32 -ffreestanding -o main.o main.c clean: rm -v *.o kernel.bin

ьФПФ ФЕЛУФ ОЕПВИПДЙНП УПИТБОЙФШ Ч ЖБКМ У ЙНЕОЕН Makefile (ВЕЪ ТБУЫЙТЕОЙС) Ч ЛБФБМПЗ У ЙУИПДОЩНЙ ФЕЛУФБНЙ СДТБ. фЕРЕТШ ДПУФБФПЮОП ЧЩРПМОЙФШ ЛПНБОДХ make ВЕЪ РБТБНЕФТПЧ, ОБИПДСУШ Ч ЬФПН ЛБФБМПЗЕ Й НЩ РПМХЮЙН ЖБКМ kernel.bin (МЙВП УППВЭЕОЙС ПВ ПЫЙВЛБИ, ЕУМЙ ЮФП-ФП РПЫМП ОЕ ФБЛ).

б ЧПФ ФБЛ С УПВЙТБА ЪБЗТХЪЮЙЛ:

All: boot.bios.bin boot.bios.bin: boot.bios.asm fasm boot.bios.asm boot.bios.bin clean: rm -v boot.bios.bin

Й make_listfs:

All: compile compile: make_listfs.c gcc -o make_listfs make_listfs.c clean: rm -f make_listfs make_listfs.exe

оХ Й ОБЛПОЕГ ТБУУЛБЦХ РТП ЧЩЪПЧ ДТХЗЙИ Makefile ЙЪ ПДОПЗП. с ДПУФБФПЮОП МЕОЙЧ, ЮФПВЩ ДБЦЕ ЪБИПДЙФШ Ч ЛБФБМПЗЙ У ЛБЦДЩН ЛПНРПОЕОФПН УЙУФЕНЩ, РПЬФПНХ УПЪДБМ 1 Makefile, ЛПФПТЩК УПВЙТБЕФ УТБЪХ ЧУА УЙУФЕНХ. х НЕОС ЕУФШ РБРЛБ src, Ч ОЕК РПДЛБФБМПЗЙ: boot, kernel, make_listfs. ч УБНПК src ОБИПДЙФУС ЧПФ ФБЛПК Makefile:

All: make -C boot/ make -C kernel/ make -C make_listfs/ clean: make -C boot/ clean make -C kernel/ clean make -C make_listfs clean

фЕРЕТШ, ОБИПДСУШ Ч ЛБФБМПЗЕ src С РТПУФП РЙЫХ make Й РПМХЮБА РПМОПУФША УПВТБООХА УЙУФЕНХ, Б ЕУМЙ ОБРЙУБФШ make clean, ФП ЧУЕ ДЧПЙЮОЩЕ ЖБКМЩ ВХДХФ ХДБМЕОЩ Й ПУФБОХФУС ФПМШЛП ЙУИПДОЙЛЙ.

оХ Й Ч ДПЧЕТЫЕОЙЕ РПУМЕДОЙК УЛТЙРФ, ЛПФПТЩК ЧЩРПМОСЕФ РПМОХА ЛПНРЙМСГЙА Й УВПТЛХ ЧУЕИ ЛПНРПОЕОФПЧ Й ПВТБЪБ ДЙУЛБ. ч ПДОПН ЛБФБМПЗЕ У ОЙН ОБДП ТБЪНЕУФЙФШ src, РХУФПК ЛБФБМПЗ bin Й ЛБФБМПЗ disk У ЖБКМПН boot.cfg.

#!/bin/sh make -C src cp src/boot/boot.bios.bin bin/ cp src/kernel/kernel.bin bin/ cp src/make_listfs/make_listfs bin/ dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel.bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 size=2880 boot=bin/boot_sector.bin src=./disk read -p "Press Enter to continue..." dummy

у ФБЛЙН ОБВПТПН УЛТЙРФПЧ УВПТЛБ УЙУФЕНБ УФБОПЧЙФУС РТЕДЕМШОП РТПУФПК, ПУПВЕООП ЕУМЙ ХЮЕУФШ, ЮФП РПУМЕДОЙК УЛТЙРФ НПЦОП ЪБРХУЛБФШ ДЧПКОЩН ЛМЙЛПН ЙЪ ЖБКМПЧПЗП НЕОЕДЦЕТБ. тБЪМЙЮОЩЕ ЛПНБОДЩ ЧТПДЕ dd, cp, rm ОЕ УХЭЕУФЧХАФ РПД Windows, РПЬФПНХ ЕЈ РПМШЪПЧБФЕМСН РТЙЗПДЙФУС РБЛЕФ MSYS ЙМЙ Cygwin. пДОБЛП РТПУФБС УВПТЛБ ЧУЕИ ЛПНРПОЕОФПЧ ВХДЕФ ТБВПФБФШ ДБЦЕ ЕУМЙ Х ЧБУ ЕУФШ ФПМШЛП GCC Й fasm (make_listfs МЕЗЛП УЛПНРЙМЙТХЕФУС Й ЪБРХУФЙФУС Ч ЧЙДЕ Windows-РТЙМПЦЕОЙС).

рТЙНЕЮБОЙЕ ДМС РПМШЪПЧБФЕМЕК пу Windows

ld ДМС Windows ОЕ УПЧУЕН РПМОПГЕООЩК - ПО ОЕ РПДДЕТЦЙЧБЕФ ЧЩЧПД УТБЪХ Ч ВЙОБТОЩК ЖБКМ, ФПМШЛП Ч EXE. йУРТБЧЙФШ ЬФП НПЦОП УПЪДБЧ УОБЮБМБ EXE (ld ОЕ ПВТБФЙФ ЧОЙНБОЙЕ, ЮФП ВБЪПЧЩЕ БДТЕУБ УЕЛГЙК ОЕЧПЪНПЦОЩЕ ДМС ЧЕОДПЧЩИ ВЙОБТОЙЛПЧ), Б РПФПН ЧЩФБЭЙФШ ПФФХДБ ЮЙУФЩЕ ДБООЩЕ У РПНПЭША objcopy. еУМЙ ЧЩ УФПМЛОЈФЕУШ У ФЕН, ЮФП ld ПФЛБЪЩЧБЕФУС УПЪДБЧБФШ ЖБКМ kernel.bin, ЧПУРПМШЪХКФЕУШ ЧПФ ФБЛЙН Makefile ДМС СДТБ:

All: startup.o main.o script.ld ld -melf_i386 -T script.ld -o kernel.bin startup.o main.o objcopy kernel.bin -O binary startup.o: startup.i386.asm fasm startup.i386.asm startup.o main.o: main.c gcc -c -m32 -ffreestanding -o main.o main.c clean: rm -v *.o kernel.bin

ъБПДОП ХВЕТЙФЕ УФТПЛХ OUTPUT_FORMAT("binary") ЙЪ script.ld. фЕРЕТШ Й РПД Windows РПМХЮЙФУС УПВТБФШ СДТП УЙУФЕНЩ.

ъБЗТХЪЛБ УЙУФЕНЩ ОБ ТЕБМШОПК НБЫЙОЕ

рПУМЕ ФБЛЙИ ХУРЕИПЧ Х ОЕЛПФПТЩИ НПЦЕФ ЧПЪОЙЛОХФШ ЦЕМБОЙЕ ПРТПВПЧБФШ ОПЧХА пу ОБ ТЕБМШОПН ЦЕМЕЪЕ. ьФП ОЕ РТЕДУФБЧМСЕФ РТПВМЕН. у РПНПЭША HxD Ч Windows ПФЛТПКФЕ ДЙУЛЕФХ ЙМЙ ЖМЕЫЛХ, ЧЩВТБЧ ЧБТЙБОФ "пФЛТЩФШ ДЙУЛ". рТЙ ПФЛТЩФЙЙ ЖМЕЫЛЙ ЧБЦОП ПФЛТЩФШ ЙНЕООП УБНХ ЖМЕЫЛХ, Б ОЕ ЕЈ ТБЪДЕМ. ч ДТХЗПК ЧЛМБДЛЕ ПФЛТПКФЕ disk.img, ЧЩДЕМЙФЕ ЕЗП УПДЕТЦЙНПЕ РПМОПУФША Й УЛПРЙТХКФЕ ОБ ДЙУЛ У ЕЗП УБНПЗП ОБЮБМБ. рПУМЕ ЬФПЗП НПЦОП ОБЦБФШ "уПИТБОЙФШ" Й ДПЦДБФШУС ПЛПОЮБОЙС ЪБРЙУЙ. чУЕ ДБООЩЕ ОБ ЖМЕЫЛЕ ЙМЙ ДЙУЛЕФЕ РТЙ ЬФПН ВХДХФ ХОЙЮФПЦЕОЩ, Б ДМС ФПЗП, ЮФПВЩ ЕЈ ЙУРПМШЪПЧБФШ УОПЧБ РП ОБЪОБЮЕОЙА, ЕЈ РТЙДЈФУС ЪБОПЧП ПФЖПТНБФЙТПЧБФШ!

рПМШЪПЧБФЕМЙ Linux НПЗХФ РПУФХРЙФШ РТПЭЕ - ЧЩРПМОЙФШ УРЕГЙБМШОХА ЛПНБОДХ Ч ФЕТНЙОБМЕ. дМС ДЙУЛЕФЩ:

Dd if=disk.img of=/dev/fd0

дМС ЖМЕЫЛЙ:

Dd if=disk.img of=/dev/sdX

чНЕУФП sdX ОБДП РПДУФБЧЙФШ ОБУФПСЭЕЕ ЙНС ХУФТПКУФЧБ (sda, sdb, sdc, sdd Й Ф. Д.). зМБЧОПЕ РТЙ ЬФПН ОЕ РЕТЕРХФБФШ Й ОЕ ЪБРЙУБФШ ПВТБЪ ОБ УЙУФЕНОЩК ДЙУЛ, ХОЙЮФПЦЙЧ ЧУЕ ДБООЩЕ. тБЪХНЕЕФУС, ПВЕ ЛПНБОДЩ ДПМЦОЩ ЧЩРПМОСФШУС ПФ ЙНЕОЙ root ЙМЙ У РПНПЭША sudo.

рПУМЕ ЬФПЗП ОБДП ОБУФТПЙФШ Ч BIOS ЪБЗТХЪЛХ У ДЙУЛЕФЩ ЙМЙ ЖМЕЫЛЙ (УФБТЩЕ BIOS ОЕ РПДДЕТЦЙЧБАФ ЖМЕЫЛЙ) Й ОБУМБЦДБФШУС ЧЙДПН "Hello world".

ъБЛМАЮЕОЙЕ

оХ ЧПФ УПВУФЧЕООП Й ЧУЈ ОБ УЕЗПДОС. нЩ ОБЛПОЕГ-ФП ЪБЛПОЮЙМЙ РТПЗТБННЙТПЧБОЙЕ ОБ Assembler (ИПФС Ч у ЧУЈ ТБЧОП РТЙДЈФУС ЙОПЗДБ ДЕМБФШ БУУЕНВМЕТОЩЕ ЧУФБЧЛЙ ДМС ТБВПФЩ У ПВПТХДПЧБОЙЕН) Й РЕТЕЫМЙ ОБ СЪЩЛ ЧЩУПЛПЗП ХТПЧОС. еЭЈ ПЮЕОШ НОПЗП РТЕДУФПЙФ УДЕМБФШ. нЩ НПЦЕФЕ ХЦЕ РТПЧПДЙФШ ТБЪМЙЮОЩЕ ЬЛУРЕТЙНЕОФЩ, ЙЪНЕОСС НПК main.c, ФПМШЛП ХЮФЙФЕ, ЮФП МАВБС ПЫЙВЛБ (ДПУФХР Л ОЕУРТПЕГЙТПЧБООПК РБНСФЙ, ДЕМЕОЙЕ ОБ ОПМШ) РТЙЧЕДЈФ Л РЕТЕЪБЗТХЪЛЕ ЙМЙ ЪБЧЙУБОЙА УЙУФЕНЩ (НЩ РПЛБ ОЕ ПВТБВБФЩЧБЕН ЙУЛМАЮЕОЙС, РПЬФПНХ РТПГЕУУПТ ОЕ НПЦЕФ РТПДПМЦЙФШ ТБВПФХ РПУМЕ ПЫЙВЛЙ). дП ЧУФТЕЮЙ!

мАВЩЕ ЧПРТПУЩ ЧЩ НПЦЕФЕ ЪБДБФШ ОБ НПК БДТЕУ: [email protected] . й ДБ, УЕКЮБУ УБНПЕ ЧТЕНС ДМС ТБЪМЙЮОЩИ ЙДЕК РП ЛПОГЕРГЙЙ пу Й РТЕДМПЦЕОЙК.

Оригинал: AsmSchool: Make an operating system
Автор: Mike Saunders
Дата публикации: 15 апреля 2016 г.
Перевод: А. Панин
Дата перевода: 16 апреля 2016 г.

Часть 4: Располагая навыками, полученными в ходе чтения предыдущих статей серии, вы можете приступить к разработке своей собственной операционной системы!

Для чего это нужно?

  • Для понимания принципов работы компиляторов.
  • Для понимания инструкций центрального процессора.
  • Для оптимизации вашего кода в плане производительности.

В течение нескольких месяцев мы прошли сложный путь, который начался с разработки простых программ на языке ассемблера для Linux и закончился в прошлом статье серии разработкой самодостаточного кода, исполняющегося на персональном компьютере без операционной системы. Ну а сейчас мы попытаемся собрать всю информацию воедино и создать самую настоящую операционную систему. Да, мы пойдем по стопам Линуса Торвальдса, но для начала стоит ответить на следующие вопросы: "Что же представляет собой операционная система? Какие из ее функций нам придется воссоздать?".

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

Разработка системного загрузчика

Мы могли бы попытаться максимально сократить объем бинарного кода нашей операционной системы с целью его размещения в первом 512-байтовом секторе флоппи-диска, том самом, который загружается средствами BIOS, но в таком случае у нас не будет возможности реализовать какие-либо интересные функции. Поэтому мы будем использовать эти 512 байт для размещения бинарного кода простого системного загрузчика, который будет загружать бинарный код ядра ОС в оперативную память и исполнять его. (После этого мы разработаем само ядро ОС, которое будет загружать бинарный код других программ с диска и также исполнять его, но об этом будет сказано чуть позже.)

Вы можете загрузить исходный код рассмотренных в статье примеров по ссылке www.linuxvoice.com/code/lv015/asmschool.zip . А это код нашего системного загрузчика из файла с именем boot.asm:

BITS 16 jmp short start ; Переход к метке с пропуском описания диска nop ; Дополнение перед описанием диска %include "bpb.asm" start: mov ax, 07C0h ; Адрес загрузки mov ds, ax ; Сегмент данных mov ax, 9000h ; Подготовка стека mov ss, ax mov sp, 0FFFFh ; Стек растет вниз! cld ; Установка флага направления mov si, kern_filename call load_file jmp 2000h:0000h ; Переход к загруженному из файла бинарному коду ядра ОС kern_filename db "MYKERNELBIN" %include "disk.asm" times 510-($-$$) db 0 ; Дополнение бинарного кода нулями до 510 байт dw 0AA55h ; Метка окончания бинарного кода системного загрузчика buffer: ; Начало буфера для содержимого диска

В данном коде первой инструкцией центрального процессора является инструкция jmp , которая расположена после директивы BITS , сообщающей ассемблеру NASM о том, что используется 16-битный режим. Как вы наверняка помните из предыдущей статьи серии, исполнение загружаемого средствами BIOS с диска 512-байтного бинарного кода начинается с самого начала, но нам приходится осуществлять переход к метке для пропуска специального набора данных. Очевидно, что в прошлом месяце мы просто записывали код в начало диска (с помощью утилиты dd), а остальное пространство диска оставляли пустым.

Сейчас же нам придется использовать флоппи-диск с подходящей файловой системой MS-DOS (FAT12), а для того, чтобы корректно работать с данной файловой системой, нужно добавить набор специальных данных рядом с началом сектора. Этот набор называется "блоком параметров BIOS" (BIOS Parameter Block - BPB) и содержит такие данные, как метка диска, количество секторов и так далее. Он не должен интересовать нас на данном этапе, так как подобным темам можно посвятить не одну серию статей, именно поэтому мы разместили все связанные с ним инструкции и данные в отдельном файле исходного кода с именем bpb.asm .

Исходя из вышесказанного, данная директива из нашего кода крайне важна:

%include "bpb.asm"

Это директива NASM, позволяющая включить содержимое указанного файла исходного кода в текущий файл исходного кода в процессе ассемблирования. Таким образом мы сможем сделать код нашего системного загрузчика максимально коротким и понятным, вынеся все подробности реализации блока параметров BIOS в отдельный файл. Блок параметров BIOS должен располагаться через три байта после начала сектора, а так как инструкция jmp занимает лишь два байта, нам приходится использовать инструкцию nop (ее название расшифровывается как "no operation" - это инструкция, которая не делает ничего, кроме траты циклов центрального процессора) с целью заполнения оставшегося байта.

Работа со стеком

Далее нам придется использовать инструкции, аналогичные рассмотренным в прошлой статье, для подготовки регистров и стека, а также инструкцию cld (расшифровывается как "clear direction"), позволяющую установить флаг направления для определенных инструкций, таких, как инструкция lodsb , которая после ее исполнения будет увеличивать значение в регистре SI , а не уменьшать его.

После этого мы помещаем адрес строки в регистр SI и вызываем нашу функцию load_file . Но задумайтесь на минуту - мы ведь еще не разработали эту функцию! Да, это правда, но ее реализацию можно найти в другом подключаемом нами файле исходного кода с именем disk.asm .

Файловая система FAT12, используемая на флоппи-дисках, которые форматируются в MS-DOS, является одной простейших существующих файловых систем, но для работы с ее содержимым также требуется немалый объем кода. Подпрограмма load_file имеет длину около 200 строк и не будет приведена в данной статье, так как мы рассматриваем процесс разработки операционной системы, а не драйвера для определенной файловой системы, следовательно, не очень разумно тратить таким образом место на страницах журнала. В общем, мы подключили файл исходного кода disk.asm практически перед окончанием текущего файла исходного кода и можем забыть про него. (Если же вас все-таки заинтересовала структура файловой системы FAT12, вы можете ознакомиться с отличным обзором по адресу http://tinyurl.com/fat12spec , после чего заглянуть в файл исходного кода disk.asm - код, содержащийся в нем, хорошо прокомментирован.)

В любом случае, подпрограмма load_file загружает бинарный код из файла с именем, заданном в регистре SI , в сегмент 2000 со сдвигом 0, после чего мы осуществляем переход к его началу для исполнения. И это все - ядро операционной системы загружено и системный загрузчик выполнил свою задачу!

Вы наверняка заметили, что в качестве имени файла ядра операционной системы в нашем коде используется MYKERNELBIN вместо MYKERNEL.BIN , которое вполне вписывается в схему имен 8+3, используемую на флоппи-дисках в DOS. На самом деле, в файловой системе FAT12 используется внутреннее представление имен файлов, а мы экономим место, используя имя файла, которое гарантированно не потребует реализации в рамках нашей подпрограммы load_file механизма поиска символа точки и преобразования имени файла во внутреннее представление файловой системы.

После строки с директивой подключения файла исходного кода disk.asm расположены две строки, предназначенные для дополнения бинарного кода системного загрузчика нулями до 512 байт и включения метки окончания его бинарного кода (об этом говорилось в прошлой статье). Наконец, в самом конце кода расположена метка "buffer" , которая используется подпрограммой load_file . В общем, подпрограмме load_file требуется свободное пространство в оперативной памяти для выполнения некоторых промежуточных действий в процессе поиска файла на диске, а у нас есть достаточно свободного пространства после загрузки системного загрузчика, поэтому мы размещаем буфер именно здесь.

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

Nasm -f bin -o boot.bin boot.asm

Теперь нам нужно создать образ виртуального флоппи-диска в формате MS-DOS и добавить бинарный код нашего системного загрузчика в его первые 512 байт с помощью следующих команд:

Mkdosfs -C floppy.img 1440 dd conv=notrunc if=boot.bin of=floppy.img

На этом процесс разработки системного загрузчика можно считать оконченным! Теперь у нас есть образ загрузочного флоппи-диска, который позволяет загрузить бинарный код ядра операционной системы из файла с именем mykernel.bin и исполнить его. Далее нас ждет более интересная часть работы - разработка самого ядра операционной системы

Ядро операционной системы

Мы хотим, чтобы наше ядро операционной системы выполняло множество важных задач: выводило приветствие, принимало ввод от пользователя, устанавливало, является ли ввод поддерживаемой командой, а также исполняло программы с диска после указания пользователем их имен. Это код ядра операционной системы из файла mykernel.asm:

Mov ax, 2000h mov ds, ax mov es, ax loop: mov si, prompt call lib_print_string mov si, user_input call lib_input_string cmp byte , 0 je loop cmp word , "ls" je list_files mov ax, si mov cx, 32768 call lib_load_file jc load_fail call 32768 jmp loop load_fail: mov si, load_fail_msg call lib_print_string jmp loop list_files: mov si, file_list call lib_get_file_list call lib_print_string jmp loop prompt db 13, 10, "MyOS > ", 0 load_fail_msg db 13, 10, "Not found!", 0 user_input times 256 db 0 file_list times 1024 db 0 %include "lib.asm"

Перед рассмотрением кода следует обратить внимание на последнюю строку с директивой подключения файла исходного кода lib.asm , который также находится в архиве asmschool.zip с нашего веб-сайта. Это библиотека полезных подпрограмм для работы с экраном, клавиатурой, строками и дисками, которые вы также можете использовать - в данном случае мы подключаем этот файл исходного кода в самом конце основного файла исходного кода ядра операционной системы для того, чтобы сделать последний максимально компактным и красивым. Обратитесь к разделу "Подпрограммы библиотеки lib.asm" для получения дополнительной информации обо всех доступных подпрограммах.

В первых трех строках кода ядра операционной системы мы осуществляем заполнение регистров сегментов данными для указания на сегмент 2000, в который была осуществлена загрузка бинарного кода. Это важно для гарантированной корректной работы таких инструкций, как lodsb , которые должны читать данные из текущего сегмента, а не из какого-либо другого. После этого мы не будем выполнять каких-либо дополнительных операций с сегментами; наша операционная система будет работать с 64 Кб оперативной памяти!

Далее в коде расположена метка, соответствующая началу цикла. В первую очередь мы используем одну из подпрограмм из библиотеки lib.asm , а именно lib_print_string , для вывода приветствия. Байты 13 и 10 перед строкой приветствия являются символами перехода на новую строку, благодаря которым приветствие будет выводиться не сразу же после вывода какой-либо программы, а всегда на новой строке.

После этого мы используем другую подпрограмму из библиотеки lib.asm под названием lib_input_string , которая принимает введенные пользователем с помощью клавиатуры символы и сохраняет их в буфере, указатель на который находится в регистре SI. В нашем случае буфер объявляется ближе к концу кода ядра операционной системы следующим образом:

User_input times 256 db 0

Данное объявление позволяет создать буфер длиной в 256 символов, заполненный нулями - его длины должно быть достаточно для хранения команд такой простой операционной системы, как наша!

Далее мы выполняем проверку пользовательского ввода. Если первый байт буфера user_input является нулевым, то пользователь просто нажал клавишу Enter, не вводя какой-либо команды; не забывайте о том, что все строки оканчиваются нулевыми символами. Таким образом, в данном случае мы должны просто перейти к началу цикла и снова вывести приветствие. Однако, в том случае, если пользователь вводит какую-либо команду, нам придется сначала проверить, не ввел ли он команду ls . До текущего момента вы могли наблюдать в наших программах на языке ассемблера лишь сравнения отдельных байт, но не стоит забывать о том, что также имеется возможность осуществления сравнения двухбайтовых значений или машинных слов. В данном коде мы сравниваем первое машинное слово из буфера user_input с машинным словом, соответствующим строке ls и в том случае, если они идентичны, перемещаемся к расположенному ниже блоку кода. В рамках этого блока кода мы используем другую подпрограмму из библиотеки lib.asm для получения разделенного запятыми списка расположенных на диске файлов (для хранения которого должен использоваться буфер file_list), выводим этот список на экран и перемещаемся назад в цикл для обработки пользовательского ввода.

Исполнение сторонних программ

Если пользователь не вводит команду ls , мы предполагаем, что он ввел имя программы с диска, поэтому имеет смысл попытаться загрузить ее. Наша библиотека lib.asm содержит реализацию полезной подпрограммы lib_load_file , которая осуществляет разбор таблиц файловой системы FAT12 диска: она принимает указатель на начало строки с именем файла посредством регистра AX , а также значение смещения для загрузки бинарного кода из файла программы посредством регистра CX . Мы уже используем регистр SI для хранения указателя на строку с пользовательским вводом, поэтому мы копируем этот указатель в регистр AX , после чего помещаем значение 32768, используемое в качестве смещения для загрузки бинарного кода из файла программы, в регистр CX .

Но почему мы используем именно это значение в качестве смещения для загрузки бинарного кода из файла программы? Ну, это просто один из вариантов карты распределения памяти для нашей операционной системы. Из-за того, что мы работаем в одном сегменте размером в 64 Кб, а бинарный код нашего ядра загружен со смещением 0, нам приходится использовать первые 32 Кб памяти для данных ядра, а остальные 32 Кб - для данных загружаемых программ. Таким образом, смещение 32768 является серединой нашего сегмента и позволяет предоставить достаточный объем оперативной памяти как ядру операционной системы, так и загружаемым программам.

После этого подпрограмма lib_load_file выполняет крайне важную операцию: если она не может найти файл с заданным именем на диске или по какой-то причине не может считать его с диска, она просто завершает работу и устанавливает специальный флаг переноса (carry flag). Это флаг состояния центрального процессора, который устанавливается в процессе выполнения некоторых математических операций и в данный момент не должен нас интересовать, но при этом мы можем определять наличие этого флага для принятия быстрых решений. Если подпрограмма lib_load_asm устанавливает флаг переноса, мы задействуем инструкцию jc (переход при наличии флага переноса - jump if carry) для перехода к блоку кода, в рамках которого осуществляется вывод сообщения об ошибке и возврат в начало цикла обработки пользовательского ввода.

В том же случае, если флаг переноса не установлен, можно сделать вывод, что подпрограмма lib_load_asm успешно загрузила бинарный код из файла программы в оперативную память по адресу 32768. Все что нам нужно в этом случае - это инициировать исполнение бинарного кода, загруженного по этому адресу, то есть начать исполнение указанной пользователем программы! А после того, как в этой программе будет использована инструкция ret (для возврата в вызывающий код), мы должны будем просто вернуться в цикл обработки пользовательского ввода. Таким образом мы создали операционную систему: она состоит из простейших механизмов разбора команд и загрузки программ, реализованных в рамках примерно 40 строк ассемблерного кода, хотя и с большой помощью со стороны подпрограмм из библиотеки lib.asm .

Для ассемблирования кода ядра операционной системы следует использовать следующую команду:

Nasm -f bin -o mykernel.bin mykernel.asm

После этого нам придется каким-то образом добавить файл mykernel.bin в файл образа флоппи-диска. Если вы знакомы с приемом монтирования образов дисков с помощью loopback-устройств, вы можете получить доступ к содержимому образа диска floppy.img , воспользовавшись им, но существует и более простой способ, заключающийся в использовании инструментария GNU Mtools (www.gnu.org/software/mtools). Это набор программ для работы с флоппи-дисками, на которых используются файловые системы MS-DOS/FAT12, доступный из репозиториев пакетов программного обеспечения всех популярных дистрибутивов Linux, поэтому вам придется лишь воспользоваться утилитой apt-get , yum , pacman или любой другой утилитой, используемой для установки пакетов программного обеспечения в вашем дистрибутиве.

После установки соответствующего пакета программного обеспечения для добавления файла mykernel.bin в файл образа диска floppy.img вам придется выполнить следующую команду:

Mcopy -i floppy.img mykernel.bin::/

Обратите внимание на забавные символы в конце команды: двоеточие, двоеточие и слэш. Теперь мы почти готовы запуску нашей операционной системы, но какой в этом смысл, пока для нее не существует приложений? Давайте исправим это недоразумение, разработав крайне простое приложение. Да, сейчас вы будете разрабатывать приложение для своей собственной операционной системы - просто представьте, насколько поднимется ваш авторитет в рядах гиков. Сохраните следующий код в файле с именем test.asm:

Org 32768 mov ah, 0Eh mov al, "X" int 10h ret

Данный код просто использует функцию BIOS для вывода символа "X" на экран, после чего возвращает управление вызвавшему его коду - в нашем случае этим кодом является код операционной системы. Строка org , с которой начинается исходный код приложения, является не инструкцией центрального процессора, а директивой ассемблера NASM, сообщающей ему о том, что бинарный код будет загружен в оперативную память со смещением 32768, следовательно, необходимо пересчитать все смещения с учетом данного обстоятельства.

Данный код также нуждается в ассемблировании, а получившийся в итоге бинарный файл - в добавлении в файл образа флоппи-диска:

Nasm -f bin -o test.bin test.asm mcopy -i floppy.img test.bin::/

Теперь глубоко вздохните, приготовьтесь к созерцанию непревзойденных результатов собственной работы и загрузите образ флоппи-диска с помощью эмулятора ПК, такого, как Qemu или VirtualBox. Например, для этой цели может использоваться следующая команда:

Qemu-system-i386 -fda floppy.img

Вуаля: системный загрузчик boot.img , который мы интегрировали в первый сектор образа диска, загружает ядро операционной системы mykernel.bin , которое выводит приветствие. Введите команду ls для получения имен двух файлов, расположенных на диске (mykernel.bin и test.bin), после чего введите имя последнего файла для его исполнения и вывода символа X на экран.

Это круто, не правда ли? Теперь вы можете начать дорабатывать командную оболочку вашей операционной системы, добавлять реализации новых команд, а также добавлять файлы дополнительных программ на диск. Если вы желаете запустить данную операционную систему на реальном ПК, вам стоит обратиться к разделу "Запуск системного загрузчика на реальной аппаратной платформе" из предыдущей статьи серии - вам понадобятся точно такие же команды. В следующем месяце мы сделаем нашу операционную систему более мощной, позволив загружаемым программам использовать системные функции и реализовав таким образом концепцию разделения кода, направленную на сокращение его дублирования. Большая часть работы все еще впереди.

Подпрограммы библиотеки lib.asm

Как говорилось ранее, библиотека lib.asm предоставляет большой набор полезных подпрограмм для использования в рамках ваших ядер операционных систем и отдельных программ. Некоторые из них используют инструкции и концепции, которые пока не затрагивались в статьях данной серии, другие (такие, как подпрограммы для работы с дисками) тесно связаны с особенностями устройства файловых систем, но если вы считаете себя компетентным в данных вопросах, вы можете самостоятельно ознакомиться с их реализациями и разобраться в принципе работы. При этом более важно разобраться с тем, как вызывать их из собственного кода:

  • lib_print_string - принимает указатель на завершающуюся нулевым символом строку посредством регистра SI и выводит эту строку на экран.
  • lib_input_string - принимает указатель на буфер посредством регистра SI и заполняет этот буфер символами, введенными пользователем с помощью клавиатуры. После того, как пользователь нажимает клавишу Enter, строка в буфере завершается нулевым символом и управление возвращается коду вызывающей программы.
  • lib_move_cursor - перемещает курсор на экране в позицию с координатами, передаваемыми посредством регистров DH (номер строки) и DL (номер столбца).
  • lib_get_cursor_pos - следует вызывать данную подпрограмму для получения номеров текущей строки и столбца посредством регистров DH и DL соответственно.
  • lib_string_uppercase - принимает указатель на начало завершающейся нулевым символом строки посредством регистра AX и переводит символы строки в верхний регистр.
  • lib_string_length - принимает указатель на начало завершающейся нулевым символом строки посредством регистра AX и возвращает ее длину посредством регистра AX .
  • lib_string_compare - принимает указатели на начала двух завершающихся нулевыми символами строк посредством регистров SI и DI и сравнивает эти строки. Устанавливает флаг переноса в том случае, если строки идентичны (для использования инструкции перехода в зависимости от флага переноса jc) или убирает этот флаг, если строки различаются (для использования инструкции jnc).
  • lib_get_file_list - принимает указатель на начало буфера посредством регистра SI и помещает в этот буфер завершающуюся нулевым символом строку, содержащую разделенный запятыми список имен файлов с диска.
  • lib_load_file - принимает указатель на начало строки, содержащей имя файла, посредством регистра AX и загружает содержимое файла по смещению, переданному посредством регистра CX . Возвращает количество скопированных в память байт (то есть, размер файла) посредством регистра BX или устанавливает флаг переноса, если файл с заданным именем не найден.

Сразу говорю, не закрывайте статью с мыслями «Блин, еще один Попов». У него всего-то слизанная Ubuntu, а у меня все с нуля, включая ядро и приложения. Итак, продолжение под катом.

Группа ОС: вот .
Сначала я кину вам один скриншот.

Больше их нет, а теперь поподробнее о том, зачем я ее пишу.

Был теплый апрельский вечер, четверг. Я еще с детства мечтал написать ОС, как вдруг подумал: «Я же теперь знаю плюсы и асм, чего бы не воплотить мою мечту?». Загуглил сайты по этой тематике и нашел статью с Хабра: "Как начать и не бросить писать ОС ". Спасибо ее автору за ссылку на OSDev Wiki внизу. Я зашел туда и начал работу. Там были в одной статье все данные о минимальной ОС. Я начал собирать кросс-gcc и binutils, а потом все переписал оттуда. Видели вы бы мою радость, когда я увидел надпись «Hello, kernel World!» Я прямо со стула подпрыгнул и понял - я не сдамся. Я написал «консоль» (в кавычках, у меня не было доступа к клавиатуре), но потом решил написать оконную систему. В итоге она заработала, но доступа к клавиатуре у меня не было. А потом я решил придумать название, опираясь на X Window System. Загуглил Y Window System - она есть. В итоге назвал Z Window System 0.1, входящая в OS365 pre-alpha 0.1. И да, ее не видел никто, кроме меня самого. Потом я понял, как реализовать поддержку клавиатуры. Скрин самой первой версии, когда еще не было ничего, даже оконной системы:

В ней даже не двигался курсор текста, как вы видите. Затем я написал парочку простых приложений на основе Z. И вот релиз 1.0.0 alpha. Там были много вещей, даже меню системы. А файловый менеджер и калькулятор просто не работали.

Меня прямо терроризировал друг, которому важны одни красивости (Митрофан, сорри). Говорил «Запили VBE-режим 1024*768*32, запили, запили! Ну запили!». Ну я уже устал его выслушивать и все-таки запилил его. О реализации ниже.

Я сделал все моим загрузчиком, а именно GRUB"ом. С его помощью можно задать графический режим без осложнений путем добавления нескольких магических строчек в заголовок Multiboot.

Set ALIGN, 1<<0 .set MEMINFO, 1<<1 .set GRAPH, 1<<2 .set FLAGS, ALIGN | MEMINFO | GRAPH .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) .align 4 .long MAGIC .long FLAGS .long CHECKSUM .long 0, 0, 0, 0, 0 .long 0 # 0 = set graphics mode .long 1024, 768, 32 # Width, height, depth
А потом из структуры информации Multiboot я беру адрес фреймбуфера и разрешение экрана и пишу туда пиксели. VESA все сделали очень замороченно - цвета RGB надо вводить в обратном порядке (не R G B, а B G R). Я несколько дней не понимал - почему пиксели не выводятся!? В итоге я понял, что забыл поменять значения 16 цветовых констант с 0...15 на их RGB-эквиваленты. В итоге релизнул, заодно запилил градиентный фон. Потом я сделал консоль, 2 приложения и релизнул 1.2. Ах да, чуть не забыл - скачать ОС можно на


Top