В статьях Создание образа диска и Создание загрузочного диска было рассмотрено создание загрузочного сектора и создание образа диска операционной системы. Попробуем создать теперь более сложную конструкцию
Наша операционная система будет состоять из трех файлов. которые займут три сектора по 512 байт, и еще один сектор в 512 байт будет использоваться для хранения данных.
Что будет представлять операционная система? Первый файл само собой будет загрузчик, который будет загружать второй файл - ядро. Хотя, конечно, ядром его назвать нельзя, поскольку в данном случае это будет всего лишь оболочка наподобие Shell, куда будут вводиться команды на выполнение тех или иных приложений. И также у нас будет одно приложение - мини текстовый редактор. В нем мы сможем набирать, редактировать и сохранять данные в четвертом секторе диска.
В качестве инструмента программирования выберем FASM по причине большей компактности программ. Тем более FASM поддерживает прямые переходы jmp 0000:0500h. В MASM для этого нам пришлось создавать бы переменную, которая содержала бы адрес. Это бы вело к увеличению размера программы, который итак ограничен 512 байтами.
Немного видоизменим загрузочный сектор, убрав из него все лишнее. Это будет файл fboot.asm.
;===================== Загрузочный сектор. Евгений Попов, 2011===================== ;================================================================================== org 7c00h ;BIOS производит чтение 512 байт первого сектора MBR в ОЗУ по адресу 0x00007C00 ;(0x07C0:0x0000 в формате реального режима), затем прочитанному коду передаётся управление start: cli ;запрещаем прерывания xor ax,ax ;обнуляем регистр ах mov ds,ax ;настраиваем сегмент данных на нулевой адрес mov es,ax ;настраиваем сегмент es на нулевой адрес mov ss,ax ;настраиваем сегмент стека на нулевой адрес mov sp,07C00h ;сегмент sp указывает на текущую вершину стека sti ;разрешаем прерывания mov ax, 0002h ;очищаем экран - функция 00h прерывания 10h int 10h mov dx,0h call SetCursorPos mov bp, msg mov cx, 13 call PrintMes ;Вывод на экран строки msg add dh,1 ;переходим на одну строку ниже call SetCursorPos mov bp, Con ;Вывод на экран строки Con mov cx, 23 call PrintMes mov ah,10h int 16h Continue: cmp al, 0Dh ;Если нажимаем на Enter, то переходим к загрузке ядра jz Kernel jmp Continue ;Если нет, снова ожидаем нажатия клавиши Kernel: mov ax,0000h mov es,ax mov bx,500h mov ch,0 ;номер цилиндра - 0 mov cl,02h ;начальный сектор - 2 mov dh,0 ;номер головки - 0 mov dl,80h ;жесткий диск - 80h mov al,01h ;кол-во читаемых секторов -1 mov ah,02h int 13h jmp 0000:0500h ;переход на 0000:0500h, куда загружен второй сектор ;===================== Подпрограммы =================================== PrintMes: ;в регистре bp - строка, в регистре cx - длина этой строки mov bl,04h ;в регистре bl- атрибут mov ax,1301h функция 13h прерывания 10h int 10h ret ;---------------------------------- SetCursorPos: ;установка курсора : функция 02h прерывания 10h mov ah,2h xor bh,bh int 10h ret ;===================== выводимые сообщения===================== msg db 'OS Loading...',0 Con db 'Press Enter to Continue',0 times(512-2-($-07C00h)) db 0 db 055h,0AAh ;сигнатура, символизирующая о завершении загрузочного сектора
Теперь файл ядра - fkernel.asm. Оболочка будет выводить приглашение к вводу программы. Мы же должны напечатать команду write и после нажатия клавиши Enter перейти в текстовый редактор
org 500h ;этот сектор будет загружаться по адресу 0000:0500h message: mov ax, 0002h ;очищаем экран int 10h mov dx,0h call SetCursorPos mov bp, msg mov cx, 20 mov bl,04h xor bh,bh mov ax,1301h int 10h ;вывод приглашения к вводу команды add dh,2 ;переводим курсор на один пункт вниз для ввода команды call SetCursorPos mov si,0 Command: mov ah,10h int 16h cmp ah, 0Eh ;Если нажата клавиша BackSpase - удалить символ jz Delete_symbol cmp al, 0Dh jz Input_Command mov [string+si],al inc si mov ah,09h mov bx,0004h mov cx,1 int 10h add dl,1 call SetCursorPos jmp Command Input_Command: ;Если нажат Enter, то переходим в третий сектор mov ax,cs mov ds,ax mov es,ax mov di,string push si ;так как содержание регистра si меняется, сохраним в стеке mov si,write mov cx,5 rep cmpsb ;сравниваем строки - если команда write, то переходим je wrt pop si jmp Command Delete_symbol: cmp dl,0 jz Command sub dl,1 ;сдвигаем курсор влево call SetCursorPos mov al,20h ;вместо уже напечатанного символа выводим пробел mov [string + si],al ;стираем символ в строке mov ah,09h mov bx,0004h mov cx,1 int 10h dec si ;уменьшаем кол-во напечатанных символов jmp Command wrt: mov ax,0000h mov es,ax mov bx,700h mov ch,0 ;номер цилиндра - 0 mov cl,03h ;начальный сектор - 3 mov dh,0 ;номер головки - 0 mov dl,80h ;жесткий диск - 80h mov al,01h ;кол-во читаемых секторов -1 mov ah,02h int 13h jmp 0000:0700h SetCursorPos: ;установка курсора mov ah,2h xor bh,bh int 10h ret msg db 'Input the command...',0 write db 'write',0 string db 5 dup(?) ;буфер для ввода команды
Последняя часть - создание текстового редактора (файл fwriter.asm). Поскольку создание текстовых редакторов на ассемблере - не самое легкое дело, его функционал будет минимальным. Редактор позволит сохранять текст, редактировать, загружать сохраненный текст. Основной недостаток - макисмальное количество символов текста - 256. Что конечно не очень хорошо, поскольку в этом случае при сохранении текста у нас заполняется всего половина четвертого сектора.
org 700h start: mov ax,0002h ;очищаем экран int 10h xor dx,dx call SetCursorPos ;устанавливаем курсор mov bp, msg mov cx, 24 call PrintMes ;Вывод на экран строки msg mov dl,0 mov dh,1 call SetCursorPos ;переводим курсор на одну строку вниз mov bp, helper mov cx,77 call PrintMes ;Вывод на экран строки helper Option: ;Выбор - загрузить текст из четвертого сектора или начать новый mov ah,10h int 16h cmp ah, 3Bh ;Если нажата клавиша F1 - загружаем текст jz Load_text cmp al, 0Dh ;Если нажата клавиша Enter - печатаем текст jz Print_text jmp Option Load_text: ;Загрузка текста mov ax,0000h mov es,ax mov bx,string mov ch,0 ;номер цилиндра - 0 mov cl,4 ;начальный сектор - 4 mov dh,0 ;номер головки - 0 mov dl,80h ;жесткий диск - 80h mov al,01h ;кол-во читаемых секторов -1 mov ah,02h int 13h xor dl,dl mov dh,3 call SetCursorPos mov bp, string mov cx, 256 call PrintMes mov si,255 add dl, 15 ;256-80*3=16 add dh,3 call SetCursorPos jmp Command Print_text: xor dx,dx add dh,3 call SetCursorPos ;получаем позицию курсора mov si,0 ;Печать символов Command: mov ah,10h int 16h cmp al, 1Bh ;Если нажата клавиша Esc - выход из приложения jz Esc cmp al, 0Dh ;Если нажата клавиша Enter - переход на новую строку jz Caret cmp ah, 0Eh ;Если нажата клавиша BackSpase - удалить символ jz Delete_symbol cmp ah, 3Ch ;Если нажата клавиша F2- сохранить текст jz Save_text cmp si, 256 jz Command mov [string + si],al inc si mov ah,09h mov bx,0004h mov cx,1 int 10h add dl,1 call SetCursorPos jmp Command Caret: ;переход на новую строку add dh,1 xor dl,dl call SetCursorPos jmp Command Save_text: ;запись текста в 4 сектор mov ax,0000h mov es,ax mov ah, 03h mov al,1 mov ch,0 mov cl,4 mov dh,0 mov dl,80h mov bx, string int 13h jmp Command Delete_symbol: ;удаление символа после нажатия BackSpase cmp dl,0 jne Delete cmp dh,3 jz Command sub dh,1 mov dl,79 jmp Cursor_Pos Delete: sub dl,1 ;сдвигаем курсор влево Cursor_Pos: call SetCursorPos mov al,20h ;вместо уже напечатанного символа выводим пробел mov [string + si],al ;стираем символ в строке mov ah,09h mov bx,0004h mov cx,1 int 10h cmp si,0 jz Command dec si ;уменьшаем кол-во напечатанных символов jmp Command Esc: jmp 0000:0500h ;возвращаемся во второй сектор ;===================== Подпрограммы =================================== PrintMes: ;в регистре bp - строка, в регистре cx - длина этой строки mov bl,04h ;в регистре bl- атрибут mov ax,1301h int 10h ret ;---------------------------------- SetCursorPos: ;установка курсора mov ah,2h xor bh,bh int 10h ret ;===================== выводимые сообщения===================== msg db 'This is a text writer...',0 helper db 'To print text - press Enter, to load text - press F1, to save text - press F2',0 string db 256 dup(?) ;буфер для вводимого сообщения
Теперь скомпилируем три файла и создадим из них образ
macro align value { db value-1 - ($ + value-1) mod (value) dup 0 } HEADS = 1 SPT = 4 ;4 сектора по 512 байт Begin: file "fboot.bin",512 ; загрузчик file "fkernel.bin" ; первый файл, типа оболочка shell align 512 file "fwriter.bin" ; второй файл - текстовый редатор align 512 align HEADS*SPT*512
Загружаем систему через Bochs
Нажимаем Enter и загружаем ядро
Вводим команду write и переходим в текстовый редактор