Загрузочный сектор
Хватайте ваш любимый редактор и наберите следующее:
entry start start: mov ax,#0xb800 mov es,ax seg es mov [0],#0x41 seg es mov [1],#0x1f loop1: jmp loop1
Это понятный as86 диалект ассемблера. Первая инструкция описывает точку входа в программу. Мы объявляем, что управление первоначально должно быть передано на метку start. Вторая строка описывает расположение этой метки (не забудьте поставить ":" после "start"). Первая инструкция, которая должна быть выполнена в этой программе, расположена сразу после нее.
0xb800 -- это адрес сегмента видеопамяти в текстовом режиме. Символ # указывает на непосредственное значение. После выполнения команды
mov ax,#0xb800
регистр ax будет содержать значение 0xb800, это адрес расположения видеопамяти. Теперь мы копируем это значение в сегментный регистр es. Помните, что 8086 имеет сегментную архитектуру. У него четыре сегментных регистра, указывающих на: сегмент кода ( CS ), сегмент данных (DS), сегмент стека ( SS ) и дополнительный сегмент (ES ). Фактически, мы сделали видеопамять нашим дополнительным сегментом, так что все записанное в него, будет записано в видеопамять.
Для отображения любого символа на экране, вам нужно записать в видеопамять два байта. Первый -- это значение ascii-кода символа, который вы хотите вывести на экран. Второй -- атрибут символа. Атрибут содержит следующую информацию: цвет символа, цвет фона, мерцание. Инструкция seg es фактически является префиксом, указывающим относительно какого сегмента должна выполняться следующая операция. Итак, мы заносим в первый байт видеопамяти (адрес 0xb800:0) значение 0x41, которое соответствует ascii-коду символу "A" (большая английская A). Затем мы должны прописать значение атрибута символа в следующем байте видеопамяти. Сюда мы записываем значение 0x1f, соответствующее белому символу на синем фоне. (Чтобы понять, где что спрятано в байте атрибута, лучше преобразовать 0x1f в двоичное представление: 00011111. Здесь первые четыре бита [справа налево] -- цвет символа, следующие три -- цвет фона и последний 7-й бит [отсчет ведется с нуля] -- это признак мерцания. Из вышесказанного можно сделать следующий вывод: в текстовом режиме символ может иметь 16 цветов, фон -- 8. Прим.перев.) Теперь, если мы выполним эту программу, то получим белую "A" на синем фоне. В конце стоит программная "петля" (команда jmp указывающая на саму себя). Нам нужно, либо остановить выполнение кода после того, как отобразим символ, либо "намертво" замкнуть цикл. Сохраните файл как boot.s .
Идея с манипуляцией видеопамятью может быть не совсем понятна, поэтому позвольте мне объяснить на будущее. Предположим мы используем экран размером 80 на 25 (80 символов в строке и 25 строк). Для каждой строки нам нужно по 160 байт: 80 байт, содержащие коды символов и столько же, содержащие их атрибуты. Если нам нужно вывести 3-й символ в 1-й строке, то мы должны пропустить байты 0 и 1, как относящиеся к первому символу; байты 2-й и 3-й, как относящиеся ко второму символу и только после этого записать в 4-м байте ascii-код выводимого символа и в 5-м -- его атрибут.
Используя прерывание 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 содержит значение смещения в сегменте, по которому будут загружены данные.
Далее мы записываем...
Итак, мы загружаем второй сектор нулевой дорожки устройства номер 0 (что соответствует приводу флоппи-дисков на 1.44Мб) по адресу 0x500:0.
Значение 2 в регистре ah указывает на номер функции прерывания. Мы выбираем функцию номер 2, которая используется для чтения данных с диска (жёсткого или гибкого).
Теперь мы вызываем прерывание 0x13 и последней командой передаём управление коду, загруженному по адресу 0x500:0.