16-битная ОС на fasm + Cи. Часть 1

от автора

Данная статья в большей степени является не руководством и не мануалом, а просто моими заметками. Идея этой статьи собрать множество особенностей и знаний в одно целое, надеюсь, она кому-то пригодится =)

Что происходит с ОЗУ при загрузке компьютера

Когда вы нажимаете кнопку старта на компьютере(или замыкаете контакты на материнке) BIOS проверяет оборудование и загружает первый сектор жесткого диска(512 байт), который помечен как загрузочный, по адресу 7C00h (h — hex) и начинает выполнять программу которая лежит в этих 512 байтах. От сюда следует, что у нас в распоряжеии есть только 512 байт.

В конце нашей программы (именуемой загрузчиком) должна быть сигнатура загрузчика — это два байта 55h и AAh, по этим двум байтам BIOS определяет, является ли эта программа загрузчиком. В загрузчике мы должны написать загрузку с жесткого диска либо второго загрузчика, либо сразу ядра ОС, в нашем случае сразу ядра ОС.

Ядро ОС будет располагаться по адресу 0500h, программы по адресу 7E00h, вершина стека 7DFFh.

Структура памяти при запуске компьютера.

Ядро располагается на 3 секторе жесткого диска и будет занимать 4 сектора(4 *512) или 2 Kb.Для загрузки даных с жесткого диска будет использовать прерывание 13h и функция 42h.
У этой функции на вход идет DAPS структура, в которой описано куда, сколько и от куда грузить сектора.

Структура DAPS

  • 1 байт — размер структура(в нашем случае 16 байт)

  • 1 байт — всегда 0, резерв

  • 1 байт — сколько загружать секторов(в нашем случае 4(размер ядра))

  • 1 байт — всегда 0, резерв

  • 2 байта — по какому смещению загружать данные

  • 2 байта — по какому сегменту загружать данные

  • 8 байт — номер сектора с которого начинать загружать данные

#define u_int16 unsigned short int #define u_char8 unsigned char #define u_long_int unsigned long int #define u_long_int64 unsigned long long int struct daps {     u_char8 p_size = 16;     u_char8 p_empty = 0;     u_char8 p_n_setors;     u_char8 p_empty2 = 0;     u_int16 p_adres;     u_int16 p_segment;     u_long_int64 sector;     file data_file; };

На file data_file пока не смотрите, это пригодиться в будущем, для удобства чтения файлов в нашей ФС (файловай системе).

Загрузчик

Код загрузчика

 use16 org 7c00h cli             ;запрещаем прерывания         xor ax,ax       ;обнуляем регистр ах         mov ds,ax       ;настраиваем сегмент данных на нулевой адрес         mov es,ax       ;настраиваем сегмент es на нулевой адрес         mov ss,ax       ;настраиваем сегмент стека на нулевой адрес         mov sp,07DFFh   ;сегмент sp указывает на текущую вершину стека sti         ;разрешаем прерывания  push cs pop ds mov si,paket mov ah,42h int 13h jmp 0000:0500h  jmp $ paket:;DAPS         db 16;const paksize         db 0;null         db 4;кол-во секторов         db 0;null         dw 0500h;смещение         dw 0;сегмент         dq 2;начало times(512-2-($-07C00h)) db 0 db 055h,0AAh ;16 байт 1 сегмент

Ядро ОС

Наше ядро при запуске сохраняет номер диска, который BIOS положил в регистр DL, в переменную BOOT_DISK(она нужна будет для доступа к диску, файлам и тд) и прыгает на метку START_K. То что идет после START_K ставит вектора прерываний 90h(основное API ОС) и 91h(Возврат управления ОС).

Установка векторов прерываний осуществляется с помощью этого макроса, на вход номер прерывания и адрес функции обработчика.

macro SET_INTERRUPT_HANDLER NUM, HANDLER {     pusha     xor ax,ax     push ax     pop es     mov al,NUM     mov bl,4h     mul bl     mov bx,ax     mov si,HANDLER     mov [es:bx],si     add bx,2     push cs     pop ax     mov [es:bx], ax     popa }

Далее загружается таблица файлов, в нашей ОС она находится во втором секторе жесткого диска. Загрузка также происходит через DAPS.

DAPS таблицы файлов

DAPS_TABEL_FILES:     db 16;const paksize     db 0;null     db 1;кол-во секторов     db 0;null     dw TABLE_FILES;смещение     dw 0;сегмент     dq 1;начало

Загрузка с помощью макроса и функции 17h прерывания 90h(которое установило ядро)

macro LOAD_DAPS DAPS {     push cs     pop ds     mov si, DAPS     mov ah, 17h     int 90h }

Функция 17h прерывания 90h(по сути просто бертка над 13h)

cmp ah,17h;-|-in - ds:si - daps je HF_LOAD_DAPS  iret  HF_LOAD_DAPS:     call F_LOAD_DAPS iret ;-|-in - ds:si - daps ; |-out - (load file table on ram) F_LOAD_DAPS: mov dl,[BOOT_DISK];вот и пригодилась наша переменная с номером диска mov ah,42h int 13h ret

Далее идет печать строки приветствия с помощью макроса PRINT.

macro PRINT STR,COLOR {     mov ah,2     push cs     pop ds     mov di,STR     mov bl,COLOR     int 90h }

Он вызывает 2 функцию прерывания 90h, которая вызывает функцию F_PRINT.

;--------------------Печать Форматированной Строки------------------------- F_PRINTSF:;ds:di-str,bl-color     call F_GET_CURSOR xor cx,cx mov cl,[ds:di] inc di MAIN_START_F_PRINTSF: call F_READ_VIDEO mov ah,013h push ds pop es mov bp,di mov al,1 int 10h ret ;--------------------Печать Строки------------------------- F_PRINT:;ds:di-str,bl-color push di push ds call F_GET_CURSOR call F_GET_LEN_STR pop ds pop di call MAIN_START_F_PRINTSF ret ;-------------------Чтение видео режима------------------------------------------------------ F_READ_VIDEO:;out al=video ah=число колонок bh= номер активной страницы дисплея     mov ah,0fh     int 10h ret ;------------Получение курсора; Выход: dh,dl - string,char ch,cl=нач.и кончеч строки курсора ---------------------------------------------------- F_GET_CURSOR:;out= dh,dl - string,char ch,cl=нач.и кончеч строки курсора     call F_READ_VIDEO     mov ah,03h     int 10h ret ;-------------------Фуекция подсчета длины строки.------------------------------------------------------------------------------  ;-|-in - ds:di=str, cx=len ; |-out - cx=len F_GET_LEN_STR: xor cx,cx START_F_GET_LEN_STR: mov al,[ds:di] cmp al,0 je EXIT_F_GET_LET_STR inc di inc cx jmp START_F_GET_LEN_STR EXIT_F_GET_LET_STR: ret

Далее идет поиск файла с именем cmd и его запуск. Реализованно это с помощью макросов SEACH_FILE и LOAD_DAPS.

macro SEACH_FILE TABLE_FILES, FILENAME {     push cs     pop ds     mov bx,TABLE_FILES     mov di, FILENAME     mov ah,10h     int 90h }

Макрос вызывает 10h функцию прерывания 90h.

;-------------------Поиск адреса файла------------------------------------------------------------------------------------------  ;-|-in - ds:bx=tableFiles, ds:di=flename ; |-out - ch-dorogka cl=sector, al=numSectors ah = type ; |-except - not found - ax=0, cx=0 F_SEACH_FILE: jmp startpfseachFile pfseachFilecxsave: db 0 pfseachFilebxsave: db 0,0 pfseachFiledisave: db 0,0 startpfseachFile: xor cx,cx mov cl,32 add bx,4 mov [pfseachFiledisave],di startSeach: mov [pfseachFilecxsave],cl mov [pfseachFilebxsave],bx mov di,[pfseachFiledisave] mov si,[pfseachFilebxsave] call F_CMP_STRING cmp al,0 je pgetDataForstartFile mov cl,[pfseachFilecxsave] mov bx,[pfseachFilebxsave] add bx,16 loop startSeach xor bx,bx xor cx,cx xor ax,ax jmp exitpfseachFile pgetDataForstartFile: mov bx,[pfseachFilebxsave] mov di,bx dec di mov cl,[ds:di] dec di mov ch,[ds:di] mov al,ch dec di mov ch,[ds:di] dec di mov ah,[ds:di] exitpfseachFile: ret

Запуск файла cmd.

 START_PROGRAMM:     mov si, DAPS_RUNTIME_FILE     mov [si + 2], al     mov [si + 8], cl     LOAD_DAPS DAPS_RUNTIME_FILE     ;LOAD_FILE ch, cl, al, 0000, 0500h     NEW_LINE jmp 0000:7E00h

DAPS программ.

DAPS_RUNTIME_FILE:     db 16;const paksize     db 0;null     db 1;кол-во секторов     db 0;null     dw 7E00h;смещение     dw 0;сегмент     dq 7;начало

Код Ядра

org 0500h GLOBAL: mov [BOOT_DISK],dl jmp START_K ;-----------------------------------------------------------     include 'INCLUDES\MACROS.INC'     include 'INCLUDES\BASE_FUNCTIONS.INC'     include 'INCLUDES\INTERRUPT_HANDLER_RETURN.INC'     include 'INCLUDES\MAIN_INTERRUPT_HANDLER.INC'     include 'INCLUDES\KEYBOARD.INC'     include 'INCLUDES\CONST.INC' ;----------------------------------------------------------- START_K: SET_INTERRUPT_HANDLER 90H,MAIN_INTERRUPT_HANDLER SET_INTERRUPT_HANDLER 91H,INTERRUPT_HANDLER_RETURN LOAD_DAPS DAPS_TABEL_FILES PRINT HELLO_WORLD, BLACK MAIN: ;NEW_LINE ;PRINT INPUT_STR, BLACK ;GET_STRING BUFFER, 13 SEACH_FILE TABLE_FILES, CMD cmp ax,0 je PRINT_ERROR   cmp ah,1 je START_PROGRAMM jmp MAIN  START_PROGRAMM:     mov si, DAPS_RUNTIME_FILE     mov [si + 2], al     mov [si + 8], cl     LOAD_DAPS DAPS_RUNTIME_FILE     ;LOAD_FILE ch, cl, al, 0000, 0500h     NEW_LINE jmp 0000:7E00h  PRINT_ERROR:     NEW_LINE     PRINT ERROR, RED jmp MAIN   RETURN_INT:     jmp MAIN ;сюда передает управление int 91h jmp $ DAPS_RUNTIME_FILE:     db 16;const paksize     db 0;null     db 1;кол-во секторов     db 0;null     dw 7E00h;смещение     dw 0;сегмент     dq 7;начало DAPS_TABEL_FILES:     db 16;const paksize     db 0;null     db 1;кол-во секторов     db 0;null     dw TABLE_FILES;смещение     dw 0;сегмент     dq 1;начало HELLO_WORLD: string "WaaOS Loaded, Hello! =)" ERROR: string "Command not found :(" CMD: string "cmd" INPUT_STR: string "user:>" BOOT_DISK: db 0 BUFFER: db 13 dup(0) TMP: db 255 dup(0) TABLE_FILES: CALC_SIZE SIZE_KERNEL, GLOBAL

Код CMD

#include "BASE_LIB.H" void clear_str_file_name(u_char8 *str, u_char8 len){     for(u_int16 i = 0; i < len; i++){         str[i] = 0;     } } void main(void) {     u_char8 user[] = "user:>";     u_char8 not_found[] = "Command not found :(";     while (true)     {         print(new_line, Black);         print(user, White);         f_string user_guffer = input();         for(u_char8 i =0 ; i < 254; i++){             if(user_guffer.data[i] == ' '){                 user_guffer.data[i] = 0;             }         }         u_char8 file_name[13];         clear_str_file_name(file_name, 13);         for(u_char8 i = 0; i < 13; i++){             if(user_guffer.data[i] == 0) break;             if(user_guffer.data[i] == ' ') break;             file_name[i] = user_guffer.data[i];         }         if(file_name[0] != ' ' && file_name[0] != 0){             daps daps_file = get_r_daps_file(file_name, (u_int16) 0x07E00);             print(new_line, Black);             if(daps_file.p_empty != 1){                 start_programm(&daps_file, user_guffer.data);             } else {                 print(not_found, Red);             }         }     } }

Заключение

Если вам интересно будет почитать про файловую систему, которая используется в этйо ОС, и библиотеку для СИ и доп. функции прерывания 90h, сделю вторую, третью и тд части. Спасибо что дочитали до конца.

Весь код ОС

Авторы: @lllzebralll @aovzerk


ссылка на оригинал статьи https://habr.com/ru/post/668926/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *