Рассылка закрыта
При закрытии подписчики были переданы в рассылку "LinuxCenter News Channel: новости Linux" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Linux Gazette на русском
Информационный Канал Subscribe.Ru |
Здравствуйте!
Очередной перевод, посвященный основам "осестроения". Кроме того, обращаю всеобщее внимание на то, что на нашем сайте появился выполненный Ивано Песиным перевод "Bandwidth Limiting HOWTO", посвященный (как следует из названия:) вопросам ограничения пропускной способности канала доступа в Internet для некоторых особо жадных до траффика пользователей локальной сети:) Читайте: http://gazette.linux.ru.net/rus/articles/bandwidth_limiting_HOWTO.htlm
Как всегда, жду вопросов и замечаний по адресу suralis-s@mtu-net.ru. Если не лень, то сразу пишите, можно ли использовать Ваши письма в рассылке.
Сергей Скороходов
Пишем игрушечную ОС (часть II)
Автор: (C) Krishnakumar
R.
Перевод: (C) Александр Куприн
Часть I (http://gazette.linux.ru.net/lg77/articles/rus-krishnakumar.html) опубликована в апрельском номере журнала.
Следующее, что необходимо разобрать после изучения процесса создания загрузочного сектора и до того, как мы перейдём к описанию приёмов переключения в защищённый режим, это использование прерываний BIOS[1]. Прерывания BIOS представляют собой подпрограммы низкого уровня, облегчающие работу создателю операционной системы. В этой части статьи мы будем иметь дело с ними.
1. Теория
1.1 Почему BIOS ?
BIOS копирует загрузочный сектор в оперативную память и передаёт на него управление. Кроме этого, существует ещё несколько функций, которые могут выполняться BIOS'ом. В момент загрузки операционная система ещё не располагает драйверами для поддержки оборудования. Размещение такого драйвера в загрузочном секторе -- задача практически невыполнимая. Должен существовать другой способ решить эту проблему и в этом нам поможет BIOS. Он содержит множество подпрограмм, служащих для различных целей и которыми мы можем воспользоваться. Например, существуют подпрограммы проверки установленного оборудования, подпрограммы управления выводом на печать, определения размера оперативной памяти и пр. Эти подпрограммы называются подпрограммами или обработчиками прерываний BIOS.
1.2 Как же вызывать прерывания BIOS ?
В "обычных" языках программирования вызов
подпрограммы реализуется через обращение к её имени. К
примеру, если в программе на C у нас есть подпрограмма
display
, получающая в качестве параметров
атрибуты (attr
) и количество отображаемых
символов на экране (noofchar
), то обратиться к
ней мы можем просто указав её имя и необходимые параметры.
Однако, здесь мы будем использовать прерывания, вызов которых
осуществляется посредством инструкции ассемблера
int
.
Например, для вывода символов на экран в C мы используем функцию похожую на:
display(noofchar, attr);
Эквивалентно этому, оперируя средствами ассемблера и BIOS, мы напишем:
int 0x10
1.3 Ладно, а как мы передаём параметры ?
Перед тем как вызвать прерывание BIOS, нам нужно загрузить данные в заранее определённом формате в регистры процессора. Предположим, мы используем прерывание 0x13, предназначенное для чтения/записи с дискеты. Прежде чем вызвать его, мы должны определить адрес в оперативной памяти, куда будут загружены данные. Также мы должны передать информацию о номере устройства (fd0 - 0x00, fd1 - 0x01, hda - 0x80, hdb - 0x81 и т.д.), цилиндре, секторе и количестве копируемых секторов. Эти данные должны быть загружены в определённые регистры. Всё это вам станет понятно после того, как вы прочтёте описание работы кода загрузочного сектора, который мы разработаем чуть позже.
Есть одна очень важная деталь, о которой вы должны знать
-- одно и тоже прерывание может использоваться для различных
целей. Всё это зависит от номера функции, который указывается
в регистре ah
(иногда ax
). К
примеру, прерывание 0x10
может быть использовано
как для вывода на экран строки, так и для получения координат
курсора. Если мы запишем в регистр ah
значение
0x03
, то тем самым при вызове прерывания 0x10 мы
выберем функцию, используемую для получения координат
курсора. Для вывода строки на экран мы записываем в регистр
ah
значение 0x13
, которое является
номером функции вывода строки на экран.
2. Что мы будем делать теперь ?
На этот раз наш исходный код состоит из двух программ на
ассемблере и одной на C. Первый файл (на ассемблере) -- это
код загрузочного сектора. В нём хранятся инструкции,
копирующие второй сектор флоппи-диска в сегмент памяти
0x500
(абсолютный адрес 0x5000)[2]. Операция выполняется при помощи
прерывания BIOS 0x13. После этого код загрузчика передаёт
управление по адресу 0x500:0
(сегмент -- 0x500,
смещение -- 0). Код второго файла на ассемблере выводит на
экран сообщение, используя прерывание BIOS 0x10
.
Программа на C предназначена для записи исполняемого кода
программ на ассемблере в 1-й и во 2-й сектора дискеты.
3. Загрузочный сектор
Используя прерывание 0x13, код загрузочного сектора читает
и копирует второй сектор флоппи-диска в память по адресу
0x5000 (сегментный адрес 0x500). Пример этого показан ниже.
Сохраните его в файле bsect.s
.
LOC1=0x500 entry start start: mov ax,#LOC1 mov es,ax mov bx,#0 mov dl,#0 mov dh,#0 mov ch,#0 mov cl,#2 mov al,#1 mov ah,#2 int 0x13 jmpi 0,#LOC1
Первая строка -- это макро-определение. Следующие две
инструкции уже знакомы вам по предыдущей статье. Записать
данные непосредственно в регистры сегментов нельзя, поэтому
следующие две строки используются для загрузки в регистр
es
значения 0x500. Регистр bx
содержит значение смещения в сегменте, по которому будут
загружены данные.
Далее мы записываем...
- номер устройства в регистр
dl
(принцип кодирования описан в пункте 1.3) - номер головки чтения/записи в регистр
dh
- номер цилиндра в регистр
ch
- номер сектора в регистр
cl
- количество загружаемых секторов в регистр
al
Итак, мы загружаем второй сектор нулевой дорожки
устройства номер 0 (что соответствует приводу флоппи-дисков
на 1.44Мб) по адресу 0x500:0
.
Значение 2 в регистре ah
указывает на номер
функции прерывания. Мы выбираем функцию номер 2, которая
используется для чтения данных с диска (жёсткого или
гибкого).
Теперь мы вызываем прерывание 0x13 и последней командой
передаём управление коду, загруженному по адресу
0x500:0
.
4. Второй сектор
Второй сектор будет содержать следующий код :
entry start start: mov ah,#0x03 xor bh,bh int 0x10 mov cx,#26 mov bx,#0x0007 mov bp,#mymsg mov ax,#0x1301 int 0x10 loop1: jmp loop1 mymsg: .byte 13,10 .ascii "Handling BIOS interrupts"
Этот код загружается и выполняется в сегменте
0x500
. Он использует прерывание
0x10
для получения текущей позиции курсора и
вывода на экран сообщения.
Первые три строки кода (отсчёт начинается с третьей
строки, пропуская инструкции определения точки входа)
используются для получения текущей позиции курсора. Для этого
используется функция 0x03 прерывания 0x10
. Перед
её вызовом мы обнуляем значение регистра bh
[3]. После выполнения прерывания,
интересующий нас результат будет хранится в регистрах
dh
и dl
(номер строки и колонки
соответственно). Переходим ко второй части программы. В
регистр...
cx
записываем количество символов в строке, выводимой на экран[4]bx
заносим значение номера видео страницы и код атрибута выводимых на экран символов (0x00 и 0x07). Мы планируем использовать белый цвет символов (0x7) на чёрном фоне (0x0).bp
пишем адрес строки [5]ax
записываем номер функции для вывода на экран строки и код подфункции, определяющий, что атрибут показываемой строки будет взят из регистраbl
Начало сообщения содержит два байта со значениями 13 и 10,
что соответствует нажатию клавиши enter
. Эти два
кода идут вместе, 13 -- код возврата каретки (Carriage
Return, CR), 10 -- код перевода строки (Line Feed, LF). Сама
строка содержит 24 символа. Символы CR и LF трактуются
функцией 0x13 прерывания 0x10 как управляющие и поэтому не
высвечиваются. Теперь вызываем прерывание. И последней
инструкцией "вешаем" компьютер.
5. Программа на C
Код программы на C, являющийся "ракетоносителем"
(точнее "программоносителем" 8-), приведен ниже.
Сохраните его в файле write.c
.
#include <sys/types.h> /* unistd.h needs this */ #include <unistd.h> /* contains read/write */ #include <fcntl.h> int main() { char boot_buf[512]; int floppy_desc, file_desc; file_desc = open("./bsect", O_RDONLY); read(file_desc, boot_buf, 510); close(file_desc); boot_buf[510] = 0x55; boot_buf[511] = 0xaa; floppy_desc = open("/dev/fd0", O_RDWR); lseek(floppy_desc, 0, SEEK_SET); write(floppy_desc, boot_buf, 512); file_desc = open("./sect2", O_RDONLY); read(file_desc, boot_buf, 510); close(file_desc); lseek(floppy_desc, 512, SEEK_SET); write(floppy_desc, boot_buf, 512); close(floppy_desc); }
В первой части статьи я описал, как создать загрузочную
дискету. В данном примере есть небольшие отличия. Сперва мы
копируем в загрузочный сектор файл bsect
,
исполняемый код которого генерируется из
bsect.s
. Затем наступает очередь
sect2
-- мы записываем его во второй сектор
флоппи-диска. Вот и всё, изменения, делающие дискету
загрузочной, выполнены.[6]
6. Готовые примеры
Вы можете взять исходники примеров здесь
Удалите у файлов расширение txt
и введите
make
в ответ на приглашение оболочки или вы можете откомпилировать файлы самостоятельно. В этом случаем введите
as86 bsect.s -o bsect.o ld86 -d bsect.o -o bsect
и повторите тоже самое для sect2.s
. Сборку
write.c
выполните командой :
cc write.c -o write
и вставив дискету в дисковод выполните программу
write
.
7. Что дальше ?
После загрузки с дискеты вы можете любоваться строкой на экране. И всё это благодаря прерываниям BIOS. В следующей статье из этой серии я надеюсь написать о том как переключать процессор в защищённый режим. А до тех пор, пока!
Krishnakumar R.
Кришнакумар -- студент последнего курса B.Tech в Govt. Engg. College Thrissur, Kerala, Индия. Его путешествие в земли Операционных Систем началось с программирования модулей для Linux. Он создал операционную систему GROS, основная цель которой -- выполнение функций маршрутизатора. (Детали вы можете найти на его домашней странице: www.askus.way.to ) Другие его интересы -- сетевые драйвера, драйвера устройств, портирование компиляторов и встроенные системы.
Примечания переводчика
[1] BIOS -- Basic Input/Output System (Базовая Система Ввода/Вывода), код прошиваемый в ПЗУ и позволяющий работать с оборудованием компьютера.
[2] Чтобы получить абсолютное
значение адреса из адреса в формате сегментной адресации,
нужно умножить значение сегментного регистра на 0x10 и
прибавить величину смещения. В нашем случае это
0x500*0x10=0x5000
.
[3] Автор не стал объяснять почему
в регистр bh
необходимо записать нулевое
значение, поэтому это сделаю я 8-). В текстовом режиме
существует несколько страниц видеопамяти, которые могут
отображаться на экране. Объём каждой из них зависит от
режима. Если это режим 80x25, то объём страницы составляет
4000 байт -- (80 * 25 * 2). Два байта, если вы помните из
предыдущей статьи, отводятся для хранения кода символа и его
атрибутов. Переключение между видео страницами осуществляется
при помощи функции 0x05 прерывания 0x10.
[4] На мой взгляд, необходимо немного изменить эту программу -- убрать явное указание количества символов в строке, заменив его вычислением. Т.е. вместо строки
mov cx,#26
ставим две других
mov cx,#end_mystr
sub cx,#mymsg
и добавляем в конце программы метку
end_mystr
. По крайней мере, теперь у нас не
будет болеть голова о том, чтобы рассчитывать длину строки,
каждый раз, когда меняется её содержимое.
[5] Если быть точным, то на адрес
строки указывает пара регистров -- es:bp
. Но в
нашем случае инициализация регистра es
произошла
до этого момента (см. код загрузчика).
[6] Для любителей простоты исполнения, код программы на C может быть заменён скриптом на bash'е из двух строк (точнее трёх -- есть ещё заголовок:)
#!/bin/bash
dd if=bsect of=/dev/fd0 conv=notrunc
dd if=sect2 of=/dev/fd0 seek=1 conv=notrunc
Хотя опция conv=notrunc
возможно и не нужна в
случае с /dev/fd0.
Copyright (С) 2002, Krishnakumar R.
Copying license http://www.linuxgazette.com/copying.html
Published in Issue 79 of Linux Gazette, June
2002
Команда переводчиков:
Владимир Меренков, Александр Михайлов, Иван Песин, Сергей Скороходов,
Александр Саввин, Роман Шумихин, Александр Куприн, Андрей
Киселев
Со всеми предложениями, идеями и комментариями обращайтесь к Сергею Скороходову (suralis-s@mtu-net.ru). Убедительная просьба: указывайте сразу, не возражаете ли Вы против публикации Ваших отзывов в рассылке.
Сайт рассылки: http://gazette.linux.ru.net
Эту статью можно взять здесь: http://gazette.linux.ru.net/lg79/krishnakumar.html
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||