Здравствуйте! В общем пишу ОСь. начальный загрузчик читает файл "kernel" из корня диска в память и передаёт управление ему. Kernel загружается по адресу 0x2000, успешно включает защищённый режим, выводит сообщения на экран через видеопамять. Используя примеры удалось перейти на 32 битный сишный код. Ассемблерная часть ядра: Код (Text): KERNEL_SIZE EQU 0x100 ;(512byte sector unit, 0x100 sector = 128KB) KERNEL_START16 EQU 0x2000;:0 [BITS 16] start: ;setting stack base:offset (SS:SP) mov ax, 0x0 mov ss, ax mov ds, ax mov ax, KERNEL_START16 mov sp, ax ;enabling A20 delay1: in al, 0x64 test al, 2 jnz delay1 mov al, 0xD1 out 0x64, al delay2: in al, 0x64 and ax, byte 2 jnz delay2 mov al, 0xDF out 0x60, al cli ;disable interrupts lgdt [gdt_48] lidt [idt_48] ;enter in protected mode mov eax, cr0 or eax,1 mov cr0, eax ;IA 386 suggest to make a jmp after the ;swith to pmode in order to clear the pipeline jmp 0x8 : protected_mode [BITS 32] [EXTERN kernel_main] protected_mode: ;init data segment to gdt[2] mov ax,0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax ;init stack segment to gdt[2] too mov ss, ax mov esp, 0xA0000 ;use this area for stack (640KB->0KB) mov esi, msg_pm call kputs call kernel_main cursor: dd 240 %define VIDEO_RAM 0xB8000 ;; Функция выполняет прямой вывод в память видеоадаптера, ;; которая находится в VGA-картах по адресу 0xB8000 kputs: pusha .loop: lodsb test al, al mov ah, 2 ;green jz .quit mov ecx, [cursor] mov [VIDEO_RAM+ecx*2], ax inc dword [cursor] jmp short .loop .quit: popa ret ; jmp 0x8 : KERNEL_START32 ;do not use call ; IDT Register 48bit ; _______________________________________ ;| IDT 0-31 | SIZE 0-15 | ; _______________________________________ idt_48: dw 256*8 dd 0x100000 ; GDT Register 48bit ; _______________________________________ ;| GDT 0-31 | SIZE 0-15 | ; _______________________________________ gdt_48: dw gdt_end - gdt - 1 ;gdt size dd gdt ;gdt address ; GDT GATE ; 31 24 19 15 7 0 ; |__________|_________|___________|____________________|__________| ; |BASE 31-24|G|D|0|AVL|LIMIT 16-19|P|DPL|S| Type |BASE 16-23| ; |________________________________________________________________ ; | BASE 15-0 | LIMIT 0-15 | ; ________________________________________________________________ gdt: ; Address for the GDT gdt_null: ; Null Segment dd 0 dd 0 gdt_code: ; Code segment, read/execute dw 0FFFFh ; LIMIT 0-15 = 0xFFFF dw 0 ; BASE 0-15 = 0 db 0 ; BASE 16-23 db 10011010b ; P=1, DPL=00, S=1, Type=1010 (Code Read/Execute) db 11001111b ; G=1 (4kb unit), D=1, 0, AVL=1, LIMIT 16-19 = 0xF db 0 ; BASE 31-24 = 0 gdt_data: ; Data segment, read/write dw 0FFFFh ; LIMIT 0-15 = 0xFFFF dw 0 ; BASE 0-15 = 0 db 0 ; BASE 16-23 db 10010010b ; P=1, DPL=00, S=1, Type=0010 (Data Read/Write) db 11001111b ; G=1 (4kb unit), D=1, 0, AVL=1, LIMIT 16-19 = 0xF db 0 ; BASE 31-24 = 0 gdt_end: ; Used to calculate the size of the GDT msg_pm: db "32-bit Protected Mode run...",0 Сишная часть ядра: Код (Text): #include "ioports.h" #define IDT_TABLE 0x100000 #define IDT_REG 0x100800 #define SYS_CODE_SELECTOR 0x8 //Функция i_install() устанавливает в качестве обработчика vector функцию func. //Тип шлюза (прерывания [0x8e] или ловушки [0x8f]) указывается параметром type. //Фактически, эта функция создает (или изменяет) соответствующий дескриптор в таблице IDT #define IRQ_HANDLER(func) void func (void);\ asm(#func ": pusha \n call _" #func " \n movb $0x20, %al \n outb %al, $0x20 \n popa \n iret \n");\ void _ ## func(void) //Функция 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"); } IRQ_HANDLER(irq_timer) { }; //Здесь мы храним состояние шифта char shift = 0; IRQ_HANDLER(irq_keyboard) { unsigned char scancode, ascii; unsigned char creg; //Прочитаем скан-код из порта 0x60 scancode = inportb(0x60); switch(scancode) { //Скан-коды нажатого шифта case 0x36: case 0x2A: shift = 1; break; //Скан-коды отпущенного шифта case 0x36 + 0x80: case 0x2A + 0x80: shift = 0; break; default: //Если клавиша отпущена... if(scancode >= 0x80) { //То ничего не делать } else { //А если нажата... if(shift){ //Если шифт нажат, но преобразовать скан-код в "шифтнутое" ASCII ascii = scancodes_shifted[scancode]; } else { //А если не нажат - то в обычное ascii = scancodes[scancode]; } //Если в результате преобразования нажата клавиша с каким-либо //символом, то вывести его на экран if(ascii != 0) { putchar(ascii); } } break; } //Считаем байт состояния клавиатуры creg = inportb(0x61); //Установим в нем старший бит creg |= 1; //И запишем обратно outportb(0x61, creg); }; void i_install(unsigned char vector, void (*func)(), unsigned char type) { unsigned 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]; } } void init_interrupts() { i_install(0x20, &irq_timer, 0x8e); i_install(0x21, &irq_keyboard, 0x8e); i_setup(); i_enable(); } При включении прерываний в main система падает с kernel fault(использую vmware 6). Если на все прерывания поставить заглушки, то система не падает. В чём может быть проблема и как правильно написать обработчики прерываний? заранее спасибо
Дело в том что макрос Код (Text): #define IRQ_HANDLER(func) Срабатывает только один раз. Вообщем правильнее сделать так. Создаешь два файла один на Си другой на ассемблере. В ассемблере вызываешь процедуры из Си. Дя этого надо их в ассемблерном файле объявить как внешнии(экспортировать) а в Си шном надо опобликовать(паблик). Поищи на board.sysbin.com пример. Тема поднималась не однократно.
Pavia спасибо! а просто макрос расписать не получится? т.е. вручную в каждый сишный обработчик добавить ассемблерные вставки? с экспортом будет сложнее и файлы расплодятся(
Можно. Только лучше всетаки из ассемблера вызывать Си подпрограмы тогда проблем с локальными переменными не будет. Ничуть. Во первых у тебя уже есть ассемблерный файл в него можно поместить код. А экспорт это всего одна ну две лишнии строчки.