Теперь, дабы не путаться со встроенными в glibc функциями, отменим их определение
#undef memcpy
Зададим несколько своих
static void puts(const char *);
static char *vidmem = (char *)0xb8000; /*адрес видеопамати*/
static int vidport; /*видеопорт*/
static int lines, cols; /*количество линий и строк на экран*/
static int curr_x,curr_y; /*текущее положение курсора */
И начнем, наконец, писать код на языке высокого уровня... правда с небольшими ассемблерными вставками.
/*функция перевода курсора в положение (x,y). Работа ведется через ввод/вывод в видеопорт*/
void gotoxy(int x, int y)
{
int pos;
pos = (x + cols * y) * 2;
outb_p(14, vidport);
outb_p(0xff & (pos >> 9), vidport+1);
outb_p(15, vidport);
outb_p(0xff & (pos >> 1), vidport+1);
}
/*функция прокручивания экрана. Работает, используя прямую запись в видеопамять*/
static void scroll()
{
int i;
memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
vidmem[i] = ' ';
}
/*функция вывода строки на экран*/
static void puts(const char *s)
{
int x,y;
char c;
x = curr_x;
y = curr_y;
while ( ( c = *s++ ) != '\0' ) {
if ( c == '\n' ) {
x = 0;
if ( ++y >= lines ) {
scroll();
y--;
}
} else {
vidmem [ ( x + cols * y ) * 2 ] = c;
if ( ++x >= cols ) {
x = 0;
if ( ++y >= lines ) {
scroll();
y--;
}
}
}
}
gotoxy(x,y);
}
/*функция копирования из одной области памяти в другую. Заместитель стандартной функции glibc */
void* memcpy(void* __dest, __const void* __src,
unsigned int __n)
{
int i;
char *d = (char *)__dest, *s = (char *)__src;
for (i=0;i<__n;i++) d[i] = s[i];
}
/*функция издающая долгий и протяжных звук. Использует только ввод/вывод в порты поэтому очень полезна для отладки*/
make_sound()
{
__asm__("
movb $0xB6, %al\n\t
outb %al, $0x43\n\t
movb $0x0D, %al\n\t
outb %al, $0x42\n\t
movb $0x11, %al\n\t
outb %al, $0x42\n\t
inb $0x61, %al\n\t
orb $3, %al\n\t
outb %al, $0x61\n\t
");
}
/*А вот и основная функция*/
int start_my_kernel()
{
/*задаются основные параметры */
vidmem = (char *) 0xb8000;
vidport = 0x3d4;
lines = 25;
cols = 80;
/*считывается предусмотрительно сохраненные координаты курсора*/
curr_x=*(unsigned char *)(0x8000);
curr_y=*(unsigned char *)(0x8001);
/*выводится строка*/
puts("done\n");
/*уходим в бесконечный цикл*/
while(1);
}
Вот и вывели мы этот "Hello World" на экран. Сколько проделано работы, а на экране только две строчки
Booting data ...done
Go to proteсted mode ...done
Немного, но и немало. Закричала новая операционная система. Мир с радостью воспринял ее. Кто знает, может быть это новый Linux ...
5. Подготовка загрузочного образа (floppy.img)
Итак, подготовим загрузочный образ нашей системки.
Для начала соберем загрузочный сектор.
as86 -0 -a -o boot.o boot.S
ld86 -0 -s -o boot.img boot.o
Обрежем 32 битный заголовок и получим таким образом чистый двоичный код.
dd if=boot.img of=boot.bin bs=32 skip=1
Соберем ядро
gcc -traditional -c head.S -o head.o
gcc -O2 -DSTDC_HEADERS -c start.c
При компоновке НЕ ЗАБУДБЬТЕ параметр "-T" он указывает относительно которого смещения вести расчеты, в нашем случае поскольку ядро грузится по адресy 0x1000, то и смещение соотетствующее
ld -m elf_i386 -Ttext 0x1000 -e startup_32 head.o start.o -o head.img
Очистим зерна от плевел, то есть чистый двоичный код от всеческих служебных заголовков и комментариев
objcopy -O binary -R .note -R .comment -S head.img head.bin
И соединяем воедино загрузочный сектор и ядро
cat boot.bin head.bin >floppy.img
Образ готов. Записываем на дискетку (заготовьте несколько для экспериментов, я прикончил три штуки) перезагружаем компьютер и наслаждаемся.
cat floppy.img >/dev/fd0
6. Е-мое, что ж я сделал (...)
Здорово, правда? Приятно почувствовать себя будущим Торвальдсом или кем-то еще. Красная линия намечена, можно смело идти вперед, дописывать и переписывать систему. Описанная процедура пока что едина для множества операционных систем, будь то UNIX или Windows. Что напишете Вы? ... не знает не кто. Ведь это будет Ваша система.