最近我对编写自己真正基本的操作系统感兴趣.我写了(嗯,复制)一些基本的程序集,它建立了一个堆栈并做了一些基本的事情,这似乎工作正常,但是试图将C引入混合中已经搞砸了一切.
我有两个主要的项目文件:loader.s,它是一些创建堆栈并调用我的C函数的NASM,以及包含基本C函数的kernel.c.
我现在的问题基本上是当我运行kernel.bin文件时QEMU冻结了.我猜我的代码有很多问题 - 由于它的极端特异性,这个问题可能不适合StackOverflow格式.我的项目文件如下:
loader.s:
BITS 16 ; 16 Bits
extern kmain ; Our 'proper' kernel function in C
loader:
mov ax, 07C0h ; Move the starting address [7C00h] into 'ax'
add ax, 32 ; Leave 32 16 byte blocks [200h] for the 512 code segment
mov ss, ax ; Set 'stack segment' to the start of our stack
mov sp, 4096 ; Set the stack pointer to the end of our stack [4096 bytes in size]
mov ax, 07C0h ; Use 'ax' to set 'ds'
mov ds, ax ; Set data segment to where we're loaded
mov es, ax ; Set our extra segment
call kmain ; Call the kernel proper
cli ; Clear ints
jmp $ ; Hang
; Since putting these in and booting the image without '-kernel' can't find
; a bootable device, we'll comment these out for now and run the ROM with
; the '-kernel' flag in QEMU
;times 510-($-$$) db 0 ; Pad remained of our boot sector with 0s
;dw 0xAA55 ; The standard 'magic word' boot sig
Run Code Online (Sandbox Code Playgroud)
kernel.c:
#include <stdint.h>
void kmain(void)
{
unsigned char *vidmem = (char*)0xB8000; //Video memory address
vidmem[0] = 65; //The character 'A'
vidmem[1] = 0x07; //Light grey (7) on black (0)
}
Run Code Online (Sandbox Code Playgroud)
我编译所有内容如下:
nasm -f elf -o loader.o loader.s
i386-elf-gcc -I/usr/include -o kernel.o -c kernel.c -Wall -nostdlib -fno-builtin -nostartfiles -nodefaultlibs
i386-elf-ld -T linker.ld -o kernel.bin loader.o kernel.o
然后测试如下:
qemu-system-x86_64 -kernel kernel.bin
希望有人可以为我看一下 - 代码片段不是很长.
谢谢.
Dev*_*lar 10
天哪,从哪里开始?(哎呀,是吗?)
代码loader.s进入主引导记录(MBR).但是,MBR还保存硬盘驱动器的分区表.因此,一旦组装完成loader.s,就必须将它与MBR 合并:loader.s来自MBR的分区表中的代码.如果您只是将loader.s代码复制到MBR中,就会杀死硬盘驱动器的分区.要正确执行合并,您必须知道MBR中分区表的确切位置...
loader.s进入MBR 的输出称为"第一级引导加载程序".由于上述情况,您在第一阶段只有436个字节.此时你不能做的一件事就是在它之上打一些C编译器输出(即使你的二进制文件大于一个扇区,MBR)并将其复制到硬盘驱动器.虽然它可能暂时在旧的硬盘驱动器上运行,但是现代的硬盘驱动器在第1扇区中携带更多的分区信息,这会被你的复制破坏.
这个想法是你编译kernel.c成一个单独的二进制文件,即"第二阶段".在第一阶段,在现有的436个字节,然后使用BIOS(或EFI)来加载从硬盘驱动器上的特定点(第二阶段,因为你将不能够分区表和文件系统解析添加到第一阶段),然后跳转到刚刚加载的代码.由于第二阶段不在同一种大小限制之下,它可以继续做正确的事情,即解析分区信息,找到"home"分区,解析其文件系统,然后加载并解析实际内核二进制.
我希望你知道我正在从低地球轨道看这一切.引导加载是涉及过程的一个问题,没有人希望在一个SO帖子中详细说明它.因此,有专门针对这些主题的网站,如OSDev.但要注意:这种发展需要有经验的程序员,能够进行专业级研究的人,以聪明的方式提问,并承担自己的重量.由于这些日子这些技能普遍下降,如果你错误地接近它,操作系统开发网站就会出现脾气暴躁的反应.(*)
(*):或者他们向你扔掉了没有注释的来源,就像我完成这篇文章时dwalter所做的那样.;-)
编辑:当然,这都不是模拟器冻结的实际原因.i386-elf-gcc是一个为32位保护模式生成代码的编译器,假设是"平坦"的内存模型,即从零开始的代码/数据段.你loader.s是16位实模式代码(如BITS 16部分所述),它不会激活保护模式,也不会将段寄存器初始化为GCC所期望的值,然后继续跳转到由GCC生成的代码.假设...... BAM.