В первой и второй статьях я лишь коротко представил процесс написания загрузчика на ассемблере и C. Для меня это было хоть и непросто, но в то же время интересно, так что я остался доволен. Однако создания загрузчика мне показалось мало, и я увлекся идеей его расширения дополнительной функциональностью. Но так как в итоге размер готовой программы превысил 512 байт, то при попытке запуска системы с несущего ее загрузочного диска я столкнулся с проблемой “This is not a bootable disk”.
0x1000
, а затем перейти к этому адресу из 0x7с00
и запустить kernel.bin.0x7c00
) в другую область памяти, где размещается, например, наш kernel.bin, после чего продолжить выполнение. Но здесь у меня я хочу кое-что уточнить.0x0ff8
и 0x0fff
, значит файл не содержит данных в других секторах, т.е. достигнут его конец./*********************************************************************************
* *
* *
* Name : stage0.S *
* Date : 23-Feb-2014 *
* Version : 0.0.1 *
* Source : assembly language *
* Author : Ashakiran Bhatter *
* *
* Описание: основная логика подразумевает сканирование файла kernel.bin *
* на дискете fat12 и передачу этому файлу права *
* выполнения. *
* Использование: подробности в файле readme.txt *
* *
* *
*********************************************************************************/
.code16
.text
.globl _start;
_start:
jmp _boot
nop
/*блок параметров BIOS описание каждой сущности */
/*-------------------- -------------------------- */
.byte 0x6b,0x69,0x72,0x55,0x58,0x30,0x2e,0x31 /* метка OEM */
.byte 0x00,0x02 /* байтов в секторе */
.byte 0x01 /* секторов в кластере */
.byte 0x01,0x00 /* зарезервированных секторов */
.byte 0x02 /* таблиц fat */
.byte 0xe0,0x00 /* записей в каталоге */
.byte 0x40,0x0b /* всего секторов */
.byte 0xf0 /* описание среды передачи */
.byte 0x09,0x00 /* размер в каждой таблице fat */
.byte 0x02,0x01 /* секторов в дорожке */
.byte 0x02,0x00 /* головок на цилиндр */
.byte 0x00,0x00, 0x00, 0x00 /* скрытых секторов */
.byte 0x00,0x00, 0x00, 0x00 /* больших секторов */
.byte 0x00 /* идентификатор загрузочного диска*/
.byte 0x00 /* неиспользуемых секторов */
.byte 0x29 /* внешняя сигнатура загрузки */
.byte 0x22,0x62,0x79,0x20 /* серийный номер */
.byte 0x41,0x53,0x48,0x41,0x4b,0x49 /* метка тома 6 байт из 11 */
.byte 0x52,0x41,0x4e,0x20,0x42 /* метка тома 5 байт из 11 */
.byte 0x48,0x41,0x54,0x54,0x45,0x52,0x22 /* тип файловой системы */
/* включение макросов */
#include "macros.S"
/* начало основного кода */
_boot:
/* инициализация среды */
initEnvironment
/* загрузка stage2 */
loadFile $fileStage2
/* бесконечный цикл */
_freeze:
jmp _freeze
/* непредвиденное завершение программы */
_abort:
writeString $msgAbort
jmp _freeze
/* включение функций */
#include "routines.S"
/* пользовательские переменные */
bootDrive : .byte 0x0000
msgAbort : .asciz "* * * F A T A L E R R O R * * *"
#fileStage2: .ascii "STAGE2 BIN"
fileStage2: .ascii "KERNEL BIN"
clusterID : .word 0x0000
/* перемещение от начала к 510-му байту */
. = _start + 0x01fe
/* добавление сигнатуры загрузки */
.word BOOT_SIGNATURE
initEnvironment
.loadFile
для загрузки kernel.bin в память по адресу 0x1000:0000
и последующей передачи ему права выполнения./********************************************************************************* * *
* *
* Name : macros.S *
* Date : 23-Feb-2014 *
* Version : 0.0.1 *
* Source : assembly language *
* Author : Ashakiran Bhatter *
* *
* *
*********************************************************************************/
/* предопределенный макрос: загрузчик */
#define BOOT_LOADER_CODE_AREA_ADDRESS 0x7c00
#define BOOT_LOADER_CODE_AREA_ADDRESS_OFFSET 0x0000
/* предопределенный макрос: сегмент стека */
#define BOOT_LOADER_STACK_SEGMENT 0x7c00
#define BOOT_LOADER_ROOT_OFFSET 0x0200
#define BOOT_LOADER_FAT_OFFSET 0x0200
#define BOOT_LOADER_STAGE2_ADDRESS 0x1000
#define BOOT_LOADER_STAGE2_OFFSET 0x0000
/* предопределенный макрос: разметка дискеты */
#define BOOT_DISK_SECTORS_PER_TRACK 0x0012
#define BOOT_DISK_HEADS_PER_CYLINDER 0x0002
#define BOOT_DISK_BYTES_PER_SECTOR 0x0200
#define BOOT_DISK_SECTORS_PER_CLUSTER 0x0001
/* предопределенный макрос: разметка файловой системы */
#define FAT12_FAT_POSITION 0x0001
#define FAT12_FAT_SIZE 0x0009
#define FAT12_ROOT_POSITION 0x0013
#define FAT12_ROOT_SIZE 0x000e
#define FAT12_ROOT_ENTRIES 0x00e0
#define FAT12_END_OF_FILE 0x0ff8
/* предопределенный макрос: загрузчик */
#define BOOT_SIGNATURE 0xaa55
/* пользовательские макросы */
/* макрос для установки среды */
.macro initEnvironment
call _initEnvironment
.endm
/* макрос для отображения строки на экране. */
/* Для выполнения этой операции он вызывает функцию _writeString */
/* параметр: вводная строка */
.macro writeString message
pushw \message
call _writeString
.endm
/* макрос для считывания сектора в памяти */
/* Вызывает функцию _readSector со следующими параметрами */
/* параметры: номер сектора */
/* адрес загрузки */
/* смещение адреса */
/* количество считываемых секторов */
.macro readSector sectorno, address, offset, totalsectors
pushw \sectorno
pushw \address
pushw \offset
pushw \totalsectors
call _readSector
addw $0x0008, %sp
.endm
/* макрос для поиска файла на FAT-диске. */
/* Для этого он вызывает макрос readSector */
/* параметры: адрес корневого каталога */
/* целевой адрес */
/* целевое смещение */
/* размер корневого каталога */
.macro findFile file
/* считывание таблицы FAT в память */
readSector $FAT12_ROOT_POSITION, $BOOT_LOADER_CODE_AREA_ADDRESS, $BOOT_LOADER_ROOT_OFFSET, $FAT12_ROOT_SIZE
pushw \file
call _findFile
addw $0x0002, %sp
.endm
/* макрос для преобразования заданного кластера в номер сектора */
/* Для этого он вызывает _clusterToLinearBlockAddress */
/* параметр: номер кластера */
.macro clusterToLinearBlockAddress cluster
pushw \cluster
call _clusterToLinearBlockAddress
addw $0x0002, %sp
.endm
/* макрос для загрузки целевого файла в память. */
/* Он вызывает findFile и загружает данные соответствующего файла в память */
/* по адресу 0x1000:0x0000 */
/* параметр: имя целевого файла */
.macro loadFile file
/* проверка наличия файла */
findFile \file
pushw %ax
/* считывание таблицы FAT в память */
readSector $FAT12_FAT_POSITION, $BOOT_LOADER_CODE_AREA_ADDRESS, $BOOT_LOADER_FAT_OFFSET, $FAT12_FAT_SIZE
popw %ax
movw $BOOT_LOADER_STAGE2_OFFSET, %bx
_loadCluster:
pushw %bx
pushw %ax
clusterToLinearBlockAddress %ax
readSector %ax, $BOOT_LOADER_STAGE2_ADDRESS, %bx, $BOOT_DISK_SECTORS_PER_CLUSTER
popw %ax
xorw %dx, %dx
movw $0x0003, %bx
mulw %bx
movw $0x0002, %bx
divw %bx
movw $BOOT_LOADER_FAT_OFFSET, %bx
addw %ax, %bx
movw $BOOT_LOADER_CODE_AREA_ADDRESS, %ax
movw %ax, %es
movw %es:(%bx), %ax
orw %dx, %dx
jz _even_cluster
_odd_cluster:
shrw $0x0004, %ax
jmp _done
_even_cluster:
and $0x0fff, %ax
_done:
popw %bx
addw $BOOT_DISK_BYTES_PER_SECTOR, %bx
cmpw $FAT12_END_OF_FILE, %ax
jl _loadCluster
/* выполнение ядра */
initKernel
.endm
/* параметры: имя целевого файла */
/* макрос для передачи права выполнения файлу, загруженному */
/* в память по адресу 0x1000:0x0000 */
/* параметры: none */
.macro initKernel
/* инициализация ядра */
movw $(BOOT_LOADER_STAGE2_ADDRESS), %ax
movw $(BOOT_LOADER_STAGE2_OFFSET) , %bx
movw %ax, %es
movw %ax, %ds
jmp $(BOOT_LOADER_STAGE2_ADDRESS), $(BOOT_LOADER_STAGE2_OFFSET)
.endm
initEnvironment
writeString <строковая переменная>
readSector <номер сектора>, <целевой адрес>, <смещение целевого адреса>, <количество считываемых секторов>
findFile <имя целевого файла>
clusterToLinearBlockAddress <ID кластера>
loadFile <имя целевого файла>
initKernel
/*********************************************************************************
* *
* *
* Name : routines.S *
* Date : 23-Feb-2014 *
* Version : 0.0.1 *
* Source : assembly language *
* Author : Ashakiran Bhatter *
* *
* *
*********************************************************************************/
/* Пользовательские подпрограммы. */
/* функция для настройки регистров и стека */
/* параметры: none */
_initEnvironment:
pushw %bp
movw %sp, %bp
_initEnvironmentIn:
cli
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $BOOT_LOADER_STACK_SEGMENT, %sp
sti
_initEnvironmentOut:
movw %bp, %sp
popw %bp
ret
/* функция для отображения строки на экране */
/* параметр: вводная строка */
_writeString:
pushw %bp
movw %sp , %bp
movw 4(%bp) , %si
jmp _writeStringCheckByte
_writeStringIn:
movb $0x000e, %ah
movb $0x0000, %bh
int $0x0010
incw %si
_writeStringCheckByte:
movb (%si) , %al
orb %al , %al
jnz _writeStringIn
_writeStringOut:
movw %bp , %sp
popw %bp
ret
/* функция для считывания сектора в целевой адрес памяти */
/* параметры: номер сектора */
/* целевой адрес */
/* смещение адреса */
/* количество считываемых секторов */
_readSector:
pushw %bp
movw %sp , %bp
movw 10(%bp), %ax
movw $BOOT_DISK_SECTORS_PER_TRACK, %bx
xorw %dx , %dx
divw %bx
incw %dx
movb %dl , %cl
movw $BOOT_DISK_HEADS_PER_CYLINDER, %bx
xorw %dx , %dx
divw %bx
movb %al , %ch
xchg %dl , %dh
movb $0x02 , %ah
movb 4(%bp) , %al
movb bootDrive, %dl
movw 8(%bp) , %bx
movw %bx , %es
movw 6(%bp) , %bx
int $0x13
jc _abort
cmpb 4(%bp) , %al
jc _abort
movw %bp , %sp
popw %bp
ret
/* функция поиска файла на дискете */
/* параметры: адрес корневого каталога */
/* целевой адрес */
/* целевое смещение */
/* размер корневого каталога */
_findFile:
pushw %bp
movw %sp , %bp
movw $BOOT_LOADER_CODE_AREA_ADDRESS, %ax
movw %ax , %es
movw $BOOT_LOADER_ROOT_OFFSET, %bx
movw $FAT12_ROOT_ENTRIES, %dx
jmp _findFileInitValues
_findFileIn:
movw $0x000b , %cx
movw 4(%bp) , %si
leaw (%bx) , %di
repe cmpsb
je _findFileOut
_findFileDecrementCount:
decw %dx
addw $0x0020, %bx
_findFileInitValues:
cmpw $0x0000, %dx
jne _findFileIn
je _abort
_findFileOut:
addw $0x001a , %bx
movw %es:(%bx), %ax
movw %bp, %sp
popw %bp
ret
/* функция для преобразования заданного кластера в номер сектора */
/* параметры: номер кластера */
_clusterToLinearBlockAddress:
pushw %bp
movw %sp , %bp
movw 4(%bp) , %ax
_clusterToLinearBlockAddressIn:
subw $0x0002, %ax
movw $BOOT_DISK_SECTORS_PER_CLUSTER, %cx
mulw %cx
addw $FAT12_ROOT_POSITION, %ax
addw $FAT12_ROOT_SIZE, %ax
_clusterToLinearBlockAddressOut:
movw %bp , %sp
popw %bp
ret
call _initEnvironment
pushw <строковая переменная>
call _writeString
addw $0x02, %sp
pushw <номер сектора>
pushw <адрес>
pushw <смещение>
pushw <всего секторов>
call _readSector
addw $0x0008, %sp
pushw <target file variable>
call _findFile
addw $0x02, %sp
pushw <ID кластера>
call _clusterToLinearBlockAddress
addw $0x02, %sp
pushw <целевой файл>
call _loadFile
addw $0x02, %sp
stage0.object
./*********************************************************************************
* *
* *
* Name : stage0.ld *
* Date : 23-Feb-2014 *
* Version : 0.0.1 *
* Source : assembly language *
* Author : Ashakiran Bhatter *
* *
* *
*********************************************************************************/
SECTIONS
{
. = 0x7c00;
.text :
{
_ftext = .;
} = 0
}
megs: 32
floppya: 1_44=../iso/stage0.img, status=inserted
boot: a
log: ../log/bochsout.txt
mouse: enabled=0
make file
и проверить, загрузит ли его загрузчик./*********************************************************************************
* *
* *
* Name : kernel.c *
* Date : 23-Feb-2014 *
* Version : 0.0.1 *
* Source : C *
* Author : Ashakiran Bhatter *
* *
* Описание: За загрузку этого файла отвечает stage0.bin, который передает *
* ему право выполнения. Его функциональность *
* заключается в отображении экрана-заставки и командной строки. *
* Внимание : Вводить команды бессмысленно, так как они не запрограммированы*
* *
*********************************************************************************/
/* генерирует 16-битный код */
__asm__(".code16\n");
/* переход к основной функции */
__asm__("jmpl $0x1000, $main\n");
#define TRUE 0x01
#define FALSE 0x00
char str[] = "$> ";
/* функция установки регистров и стека */
/* параметры: none */
void initEnvironment() {
__asm__ __volatile__(
"cli;"
"movw $0x0000, %ax;"
"movw %ax, %ss;"
"movw $0xffff, %sp;"
"cld;"
);
__asm__ __volatile__(
"movw $0x1000, %ax;"
"movw %ax, %ds;"
"movw %ax, %es;"
"movw %ax, %fs;"
"movw %ax, %gs;"
);
}
/* VGA-функции. */
/* функция для установки режима VGA на 80*24 */
void setResolution() {
__asm__ __volatile__(
"int $0x10" : : "a"(0x0003)
);
}
/* функция очистки буфера экрана разделяющими пробелами */
void clearScreen() {
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0200), "b"(0x0000), "d"(0x0000)
);
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0920), "b"(0x0007), "c"(0x2000)
);
}
/* функция установки позиции курсора на заданный столбец и строку */
void setCursor(short col, short row) {
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0200), "d"((row <<= 8) | col)
);
}
/* функция включения и отключения курсора */
void showCursor(short choice) {
if(choice == FALSE) {
__asm__ __volatile__(
"int $0x10" : : "a"(0x0100), "c"(0x3200)
);
} else {
__asm__ __volatile__(
"int $0x10" : : "a"(0x0100), "c"(0x0007)
);
}
}
/* функция инициализации режима VGA на 80*25, */
/* очистки экрана и установки положения курсора на (0,0) */
void initVGA() {
setResolution();
clearScreen();
setCursor(0, 0);
}
/* I/O-функции. */
/* функция для получения символа с клавиатуры без эха*/
void getch() {
__asm__ __volatile__ (
"xorw %ax, %ax\n"
"int $0x16\n"
);
}
/* эта функция аналогична getch(), */
/* но возвращает скан-код клавиши и соответствующее значение ascii */
short getchar() {
short word;
__asm__ __volatile__(
"int $0x16" : : "a"(0x1000)
);
__asm__ __volatile__(
"movw %%ax, %0" : "=r"(word)
);
return word;
}
/* функция для отображения нажатых клавиш на экране*/
void putchar(short ch) {
__asm__ __volatile__(
"int $0x10" : : "a"(0x0e00 | (char)ch)
);
}
/* функция вывода на экран строки с завершающим нулем */
void printString(const char* pStr) {
while(*pStr) {
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0002)
);
++pStr;
}
}
/* функция, вызывающая задержку на несколько секунд */
void delay(int seconds) {
__asm__ __volatile__(
"int $0x15" : : "a"(0x8600), "c"(0x000f * seconds), "d"(0x4240 * seconds)
);
}
/* Строковая функция. */
/* эта функция вычисляет длину строки и возвращает ее */
int strlength(const char* pStr) {
int i = 0;
while(*pStr) {
++i;
}
return i;
}
/* Функция UI. */
/*эта функция отображает логотип */
void splashScreen(const char* pStr) {
showCursor(FALSE);
clearScreen();
setCursor(0, 9);
printString(pStr);
delay(10);
}
/* Оболочка. */
/* функция для отображения фиктивной командной строки. */
/* При нажатии клавиши Ввод выполняется переход на следующую строку */
void shell() {
clearScreen();
showCursor(TRUE);
while(TRUE) {
printString(str);
short byte;
while((byte = getchar())) {
if((byte >> 8) == 0x1c) {
putchar(10);
putchar(13);
break;
} else {
putchar(byte);
}
}
}
}
/* точка входа в ядро */
void main() {
const char msgPicture[] =
" .. \n\r"
" ++` \n\r"
" :ho. `.-/++/. \n\r"
" `/hh+. ``:sds: \n\r"
" `-odds/-` .MNd/` \n\r"
" `.+ydmdyo/:--/yMMMMd/ \n\r"
" `:+hMMMNNNMMMddNMMh:` \n\r"
" `-:/+++/:-:ohmNMMMMMMMMMMMm+-+mMNd` \n\r"
" `-+oo+osdMMMNMMMMMMMMMMMMMMMMMMNmNMMM/` \n\r"
" ``` .+mMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmho:.` \n\r"
" `omMMMMMMMMMMMMMMMMMMNMdydMMdNMMMMMMMMdo+- \n\r"
" .:oymMMMMMMMMMMMMMNdo/hMMd+ds-:h/-yMdydMNdNdNN+ \n\r"
" -oosdMMMMMMMMMMMMMMd:` `yMM+.+h+.- /y `/m.:mmmN \n\r"
" -:` dMMMMMMMMMMMMMd. `mMNo..+y/` . . -/.s \n\r"
" ` -MMMMMMMMMMMMMM- -mMMmo-./s/.` ` \n\r"
" `+MMMMMMMMMMMMMM- .smMy:.``-+oo+//:-.` \n\r"
" .yNMMMMMMMMMMMMMMd. .+dmh+:. `-::/+:. \n\r"
" y+-mMMMMMMMMMMMMMMm/` ./o+-` . \n\r"
" :- :MMMMMMMMMMMMMMMMmy/.` \n\r"
" ` `hMMMMMMMMMMMMMMMMMMNds/.` \n\r"
" sNhNMMMMMMMMMMMMMMMMMMMMNh+. \n\r"
" -d. :mMMMMMMMMMMMMMMMMMMMMMMNh:` \n\r"
" /. .hMMMMMMMMMMMMMMMMMMMMMMMMh. \n\r"
" . `sMMMMMMMMMMMMMMMMMMMMMMMMN. \n\r"
" hMMMMMMMMMMMMMMMMMMMMMMMMy \n\r"
" +MMMMMMMMMMMMMMMMMMMMMMMMh ";
const char msgWelcome[] =
" *******************************************************\n\r"
" * *\n\r"
" * Welcome to kirUX Operating System *\n\r"
" * *\n\r"
" *******************************************************\n\r"
" * *\n\r"
" * *\n\r"
" * Author : Ashakiran Bhatter *\n\r"
" * Version: 0.0.1 *\n\r"
" * Date : 01-Mar-2014 *\n\r"
" * *\n\r"
" ******************************************************";
initEnvironment();
initVGA();
splashScreen(msgPicture);
splashScreen(msgWelcome);
shell();
while(1);
}
sourcecode.tar.gz
находятся все исходные файлы и каталоги, необходимые для генерации исполняемых файлов.cd $(DIRECTORY)/src
make -f Makefile test
bochs
К сожалению, не доступен сервер mySQL