16-bit OS on fasm + C. Part 1

This article is more than a guide or a manual, but just my notes. The idea of ​​this article is to collect a lot of features and knowledge into one whole, I hope it will be useful to someone =)

What happens to RAM when you boot your computer

When you press the start button on the computer (or close the contacts on the motherboard), the BIOS checks the hardware and loads the first sector of the hard disk (512 bytes), which is marked as bootable, at the address 7C00h (h – hex) and starts executing the program that lies in these 512 bytes. From here it follows that we have only 512 bytes at our disposal.

At the end of our program (called the bootloader) there should be a bootloader signature – these are two bytes 55h and AAh, these two bytes determine whether this program is a bootloader. In the bootloader, we must write the boot from the hard disk of either the second bootloader, or the OS kernel right away, in our case, the OS kernel right away.

The OS kernel will be located at address 0500h, programs at address 7E00h, the top of the stack is 7DFFh.

Memory structure at computer startup.

The kernel is located on the 3rd sector of the hard disk and will occupy 4 sectors (4 * 512) or 2 Kb. To load data from the hard disk, it will use interrupt 13h and function 42h.
This function has a DAPS structure as input, which describes where, how much and from where to load sectors.

DAPS structure

  • 1 byte – structure size (in our case 16 bytes)

  • 1 byte – always 0, reserved

  • 1 byte – how many sectors to load (in our case 4 (kernel size))

  • 1 byte – always 0, reserved

  • 2 bytes – at what offset to load data

  • 2 bytes – on which segment to load data

  • 8 bytes – sector number from which to start downloading data

#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;
};

On the file data_file do not look yet, this will come in handy in the future, for the convenience of reading files in our FS (file system).

Loader

Loader Code


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 сегмент

OS kernel

Our kernel at startup saves the disk number that the BIOS put in the register DLinto a variable BOOT_DISK(it will be needed to access the disk, files, etc.) and jumps to the label START_K. What comes after START_K sets interrupt vectors 90h(main OS API) and 91h(Return OS control).

Interrupt vectors are set using this macro, to the input interrupt number and handler function address.

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
}

Next, the file table is loaded, in our OS it is located in the second sector of the hard disk. The download also takes place via DAPS.

DAPS table files

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

Download using macro and features 17h interrupts 90h(which installed the kernel)

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

Function 17h interrupt 90h (essentially just a wrapper over 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

Next comes the printing of the hello string using a macro PRINT.

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

He calls 2 interrupt function 90hwhich calls the function 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

The next step is to search for a file named cmd and its launch. Implemented using macros SEACH_FILE and LOAD_DAPS.

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

Macro calls 10h interrupt function 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

File launch 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 programs.

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

Core Code

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 code

#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);
            }
        }
    }
}

Conclusion

If you are interested in reading about the file system used in this OS, and the library for SI and add. interrupt functions 90h, I will make the second, third and so on parts. Thank you for reading to the end.

All OS code

Authors: @lllzebralll @aovzerk

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *