Недавно я перешел в защищенный режим при разработке ОС с нуля. Мне удалось перейти на C и создать функции для вывода символов на экран (спасибо Майклу Петчу за помощь в достижении этого этапа). В любом случае, всякий раз, когда я пытаюсь создать подпрограмму, которая перебирает строковый литерал и печатает в нем каждый символ, возникает небольшая проблема. QEMU просто переходит в загрузочный цикл, перезагружается снова и снова, и я никогда не вижу свой прекрасный видеорежим зеленого на черном. Если я вынесу это из подпрограммы и распечатаю посимвольно в функции kmain()
(той части, которую я удалил), все будет работать нормально и денди. Вот файл, в котором я пытаюсь реализовать функцию печати строк:
vga.c -
#include <vga.h>
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t *terminal_buffer;
volatile uint16_t * const VIDMEM = (volatile uint16_t *) 0xB8000;
size_t strlen(const char *s)
{
size_t len = 0;
while(s[len]) {
len++;
}
return len;
}
void terminal_init(void)
{
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(LGREEN, BLACK);
for(size_t y = 0; y < VGA_HEIGHT; y++) {
for(size_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x;
VIDMEM[index] = vga_entry(' ', terminal_color);
}
}
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
const size_t index = y * VGA_WIDTH + x;
VIDMEM[index] = vga_entry(c, color);
}
void terminal_putchar(char c)
{
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
if(++terminal_column == VGA_WIDTH) {
terminal_column = 0;
if(++terminal_row == VGA_HEIGHT) {
terminal_row = 0;
}
}
}
void terminal_puts(const char *s)
{
size_t n = strlen(s);
for (size_t i=0; i < n; i++) {
terminal_putchar(s[i]);
}
}
Я прочитал свое ядро в память с помощью этого кода загрузчика:
extern kernel_start ; External label for start of kernel
global boot_start ; Make this global to suppress linker warning
bits 16
boot_start:
xor ax, ax ; Set DS to 0. xor register to itselfzeroes register
mov ds, ax
mov ss, ax ; Stack just below bootloader SS:SP=0x0000:0x7c00
mov sp, 0x7c00
mov ah, 0x00
mov al, 0x03
int 0x10
load_kernel:
mov ah, 0x02 ; call function 0x02 of int 13h (read sectors)
mov al, 0x01 ; read one sector (512 bytes)
mov ch, 0x00 ; track 0
mov cl, 0x02 ; sector 2
mov dh, 0x00 ; head 0
; mov dl, 0x00 ; drive 0, floppy 1. Comment out DL passed to bootloader
xor bx, bx ; segment 0x0000
mov es, bx ; segments must be loaded from non immediate data
mov bx, 0x7E00 ; load the kernel right after the bootloader in memory
.readsector:
int 13h ; call int 13h
jc .readsector ; error? try again
jmp 0x0000:kernel_start ; jump to the kernel at 0x0000:0x7e00
У меня есть заглушка сборки в начале моего ядра, которая входит в защищенный режим, обнуляет раздел BSS, выдает CLD и вызывает мой код C:
; These symbols are defined by the linker. We use them to zero BSS section
extern __bss_start
extern __bss_sizel
; Export kernel entry point
global kernel_start
; This is the C entry point defined in kmain.c
extern kmain ; kmain is C entry point
bits 16
section .text
kernel_start:
cli
in al, 0x92
or al, 2
out 0x92, al
lgdt[toc]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:start32 ; The FAR JMP is simplified since our segment is 0
section .rodata
gdt32:
dd 0
dd 0
dw 0x0FFFF
dw 0
db 0
db 0x9A
db 0xCF
db 0
dw 0x0FFFF
dw 0
db 0
db 0x92
db 0xCF
db 0
gdt_end:
toc:
dw gdt_end - gdt32 - 1
dd gdt32 ; The GDT base is simplified since our segment is now 0
bits 32
section .text
start32:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x9c000 ; Set the stack to grow down from area under BDA/Video memory
; We need to zero out the BSS section. We'll do it a DWORD at a time
cld
lea edi, [__bss_start] ; Start address of BSS
lea ecx, [__bss_sizel] ; Lenght of BSS in DWORDS
xor eax, eax ; Set to 0x00000000
rep stosd ; Do clear using string store instruction
call kmain
У меня есть специальный скрипт компоновщика, который размещает загрузчик по адресу 0x7c00, а ядро — по адресу 0x7e00.
В чем проблема и как я могу это исправить? Я сделал свой репозиторий git доступным, если потребуется дополнительная информация.
section .bss resb 33
в нижней части файла kernel.asm можно удалить. Он был там для целей тестирования, и я забыл удалить его перед представлением. - person Michael Petch   schedule 22.12.2017