Stupid OS
Данный проект является примером того , как трудозатраты на низкоуровневое программирование свести к минимуму.
Чистого ассемблера здесь раз-два и обчелся : маленький загрузчик и немного кода для доступа к портам.
Хотя совсем от ассемблера отказаться невозможно , поэтому он перекочевывает в инлайн.
Последовательность событий :
1. Инициализация GDT
2. Инициализация IDT
3. Инициализация PIC
4. Инициализация клавиатуры
5. Инициализвация таймера
GDT
8-байтовый дескриптор описывается с помощью структуры gdt_str_tag :
typedef struct gdt_str_tag
{
unsigned short int seg_limit_low; //segment limit - 2 байта
unsigned short int base_low; //Base Address - 2 байта
unsigned char base_mid; //Base Address - 1 байт
seg_t seg_type:4; //Segment Type - 4 бита
seg_desc_t seg_desc_type:1; //Descriptor type (1 бит, 0 = system, 1 = code/data)
dpl_t dpl:2; //Descriptor Privilege level (2 бита)
present_t present:1; //Segment present (1 бит)
unsigned char seg_limit_high:4; //segment limit 16:19 (4 бита)
unsigned char gran_def_op:4; //avl (1 бит)
//reserved = 0 (1 бит)
//D/B(default operation size / big) (1 бит)
//Granularity (1 бит)
//Для защищенного режима = 0xC
unsigned char base_high; //Base Address - 1 байт
}gdt_st_t;
Заполняем поля :
gdt_temp.seg_limit_low = 0x0FFF; //Code Descriptor
gdt_temp.base_low = 0x0000;
gdt_temp.base_mid = 0x00;
gdt_temp.seg_type = 0xA;
gdt_temp.seg_desc_type = CODE_DATA;
gdt_temp.dpl = RING_0;
gdt_temp.present = PRESENT;
gdt_temp.seg_limit_high = 0x0;
gdt_temp.gran_def_op = GDO;
gdt_temp.base_high = 0x00;
Для дескриптора данных все то же самое , кроме :
gdt_temp.seg_type = 0x2;
Адрес таблицы GDT - GDT_BASE=0x6000.
Таблица состоит из 256 дескрипторов , первый - нулевой , второй - кодовый , третий-данные,
остальные - также нулевые.
Для загрузки GDT в регистр GDTR создадим структуру :
typedef struct gdtr_str_tag
{
unsigned short int gdt_length;
unsigned short int gdt_base_low;
unsigned short int gdt_base_high;
}gdtr_st_t;
Загрузка :
gdtr.gdt_length = (unsigned short int) 256 * 8;
gdtr.gdt_base_low = (unsigned short int) GDT_BASE_LOW;
gdtr.gdt_base_high = (unsigned short int) GDT_BASE_HIGH;
asm("lgdt (%0)": :"p" (&gdtr));
IDT
Опишем исключение с помощью структуры и создадим массив из 32 таких структур :
typedef struct exceptiont_st_tag
{
sint number;
int (* handler)();
}exception_st_t;
extern exception_st_t exceptions[32];
Массив exception_st_t описан в файле handler.c:
exception_st_t exceptions[32]=
{
{0, ÷_error},
{1, &debug},
{2, &nmi_interrupt},
..........
{30, &reserved},
{31, &reserved}
};
При возникновении исключения указатель exception_st_t->handler приводит куда надо -
а именно в функию типа :
int divide_error()
{
kprintf("divide error exception\n", 0xF);
while(1);
return 0;
}
Произведем загрузку IDT начиная с адреса IDTBASE 0x6800:
for(lv0 = 0; lv0 <= 31; lv0++)
{
load_idt_entry(exceptions[lv0].number,
(uint)exceptions[lv0].handler,
CS_SELECTOR,
TRAP_GATE|PRESENT|RING_0|BITS_32);
}
Внутренний дескриптор IDT опишем с помощью 6-байтовой структуры
typedef struct idt_st_tag
{
usint loffset; // 2 байта
usint selector; // 2 байта
byte unused; // 1 байт
byte options;
/* constant:5; 5 бит
dpl:2; 2 бит
present:1; 1 бит */
usint uoffset; // 2 bytes
}idt_st_t;
Загрузка регистра IDTR :
idtr.limit = (usint) (34 * 8);
idtr.lowbase = (usint) IDT_LOWBASE;
idtr.highbase = (usint) IDT_HIGHBASE;
asm("lidt (%0) ": :"p" (idtr));
PIC
Инициализация программируемого контроллера необходима для обслуживания хардварных прерываний.
Определены хардварные хэндлеры в порядке приоритета :
#define TIMER 0x0 - системный таймер
#define KEYBOARD 0x1 - клавиатура
#define SLAVE 0x2 - вторичный PIC
#define TTY1 0x3 - COM2
#define TTY2 0x4 - COM1
#define XT_WINCHESTER 0x5 - LPT2
#define FLOPPY 0x6 - floppy
#define PRINTER 0x7 - printer
В последующем будут проинициализированы клавиатура с таймером.
Для их инициализации будет вызвана функция enable_irq(byte irq_no), где в качестве
параметра будет стоять нужный хэндлер.
Клавиатура
Для загрузки клавиатуры нужно загрузить дескриптор в таблице IDT со следующими параметрами :
load_idt_entry(33,(uint)&keyboard_handler,CS_SELECTOR,INT_GATE|PRESENT|BITS_32);
после чего разрешить прерывание :
enable_irq(KEYBOARD);
В качестве одного из параметров этой загрузки выступает функция keyboard_handler() -
фактически это обработчик нажимаемых клавиш.
Таймер
Для инициализации таймера нужно загрузить дескриптор в таблице IDT со следующими параметрами :
load_idt_entry(32,(uint)&timer_handler,CS_SELECTOR,TRAP_GATE|PRESENT|RING_0|BITS_32);
после чего разрешить прерывание :
enable_irq(TIMER);
На системный таймер повешен хэндлер - timer_handler().
В нем обрабатывается событие - переполнение переменной , при котором на экране
вы увидите появление очередного символа - "точки".
Загрузчик работает из-под граб-а.
Исходники лежат тут.
|