Автор: lonesome [TSH/Digital Daemons]
Дата: 7.06.2003
Раздел: Разработка ОС
Сегодня в номере:
В первых строках этого номера хотелось бы поздравить вас с выходом юбилейного десятого выпуска рассылки. Часть долгого пути уже пройдена (тем, кто к нам присоединился только сейчас, настоятельно рекомендую посмотреть архив рассылки и узнать, чем мы занимались до этого выпуска).
Мы уже вплотную приближаемся к написанию нашей "самой простейшей из всех возможных" системы и пришло время поговорить о ее архитектуре. Вот краткий перечень того, что в ней будет, и чего в ней не окажется :
| Поддержка клавиатуры и монитора | :) |
| Многозадачность | Обойдемся без нее |
| Разделение системы и приложений | Зачем? |
| Поддержка различных внешних устройств | Смотри первый пункт |
| Поддержка накопителей на магнитных дисках | Слишком сложно |
| Поддержка RAM-дисков | Да |
| Файловая система | Для рамдиска понадобится |
| Системные вызовы | Без них никак |
| Виртуальная память | Пока незачем |
Вот такая вот получается хм.. система. У нее есть одно очень большое и никем не превзойденное преимущество - при наличии соответствующих знаний (а они у нас уже есть) ее можно написать за несколько часов. А это ровно в 24 раза меньше, чем обещанные мною несколько дней :)
Что нам осталось реализовать? (tty-устройство было создано в предыдущем выпуске):
lsПри разработке операционной системы, нам придется (раз уж мы перешли на Си) использовать встроенный ассемблер (Inline Assembler) компилятора GCC. Он имеет формат AT&T (в противоположность обычному формату Intel). Это означает, что:
B (байт), W (слово), L (двойное слово - long). Если размер команды следует из размера операндов, то суффикс необязателен, но лучше все же его добавить'$', регистры - префикс '%'
Вот несколько примеров соответствия синтаксиса AT&T синтаксису Intel:
| AT&T | Intel |
movl $0, %eax |
mov eax, 0 |
movl $variable, %ebx |
mov ebx, variable |
movw variable, %ebx |
mov ebx, word [variable] |
movb $0, (,%eax,) |
mov [eax], byte 0 |
movl %eax, $111(%ebx, %edx, 2) |
mov [111 + eax + edx*2], eax |
Код на ассемблере можно вставить в исходник Си с помощью команды asm(..):
asm("movw %ax, %bx");
Несколько инструкций можно разделить обычным символом новой строки ( По сути дела, constraints - это параметры вызова Inline Assembler'а. В основном они нам понадобятся для того, чтобы связать Inline Assembler с кодом на Си (чтобы использовать одни и те же константы и пр.). Рассмотрим примеры: Как видите, если перед параметром указывается префикс Теперь, с учетом параметров, в общем виде команда Сейчас мы не будем подробно рассматривать различные типы параметров, разберемся в них по ходу работы. Интересующихся же отсылаю к документации на Для пущего удобства указанного в заголовке действа мы напишем несколько функций, которые позволят нам устанавливать обработчики прерываний, а также запрещать и разрешать обработку прерываний:
Теперь эти четыре функции будут предоставлять остальной части ядра удобный высокоуровневый интерфейс для установки обработчиков прерываний Для этого дела мы тоже напишем парочку функций, которые будут использовать команды На сегодня все, уважаемые подписчики.'\n'):
asm("xorl %eax, %eax \n incl %eax");
GCC constraints или расширенные возможности Inline Assembler'а
asm("lidt 0(,%0,)"::"a"(IDT_REG)); - это означает: поместить значение IDT_REG в регистр EAX (который указывается символом 'a', и выполнить команду lidt 0(,%eax,)
asm("cpuid":"=a"(result_eax), - это означает: поместить содержимое переменной
"=b"(result_ebx), "=c"(result_ecx), "=d"(result_edx):"a"(source_eax)source_eax в регистр EAX, выполнить команду CPUID и поместить содержимое регистров EAX, EBX, ECX, EDX в переменные result_eax, result_ebx, result_ecx, result_edx соответственно
'=', то после выполнения инструкции происходит присвоение указанным переменным
asm(..) выглядит так:
asm("ИНСТРУКЦИЯ":"ВЫХОДНЫЕ_ПАРАМЕТРЫ":"ВХОДНЫЕ_ПАРАМЕТРЫ");
GCC.
Обработка прерываний в системе
/*
intslib.c
*/
#define IDT_TABLE 0x100000
#define IDT_REG 0x100800
#define SYS_CODE_SELECTOR 0x8
//Функция i_install() устанавливает в качестве обработчика vector функцию func.
//Тип шлюза (прерывания [0x8e] или ловушки [0x8f]) указывается параметром type.
//Фактически, эта функция создает (или изменяет) соответствующий дескриптор в таблице IDT
void i_install(unsigned char vector, void (*func)(), unsigned char type)
{
char * idt_table=IDT_TABLE;
unsigned char i;
unsigned char b[8];
b[0]= (unsigned int)func & 0x000000FF;
b[1]=( (unsigned int)func & 0x0000FF00) >> 8;
b[2]=SYS_CODE_SELECTOR;
b[3]=0;
b[4]=0;
b[5]=type;
b[6]=( (unsigned int)func & 0x00FF0000) >> 16;
b[7]=( (unsigned int)func & 0xFF000000) >> 24;
for(i=0;i<8;i++){
*(idt_table+vector*8+i)=b[i];
}
}
//Функция i_setup() загружает регистр IDTR
void i_setup()
{
unsigned short *table_limit = IDT_REG;
unsigned int *table_address = IDT_REG+2;
*table_limit = 256*8 - 1;
*table_address = IDT_TABLE;
asm("lidt 0(,%0,)"::"a"(IDT_REG));
asm("sti");
}
//Включение обработки прерываний
void i_enable()
{
asm("sti");
}
//Отключение обработки прерываний
void i_disable()
{
asm("cli");
}
Работа с портами ввода/вывода
IN (чтение из порта) и OUT (запись в порт).
/*
ioports.c - функции работы с портами ввода/вывода
*/
__inline__ void outportb(unsigned short port, unsigned char value)
{
asm("outb %b0,%w1":: "a"(value), "d"(port));
}
__inline__ char inportb(unsigned short port)
{
char value;
asm("inb %w1, %b0": "=a"(value): "d"(port));
return value;
}
Outro
Как всегда, мой почтовый ящик открыт для вас: lonesome@lowlevel.ru
Также вы можете задавать интересующие вас вопросы в форуме lowlevel.ru
Предыдущие выпуски рассылки вы можете найти по этому адресу:
http://subscribe.ru/archive/comp.soft.prog.osdev
Всего наилучшего!
Lonesome
ПРЕДЫДУЩИЙ ВЫПУСК
СЛЕДУЮЩИЙ ВЫПУСК