怎样写一个简单的操作系统(8)
时间:2026-01-15
时间:2026-01-15
怎样写一个简单的操作系统
text_string db 'This is my cool new OS!', 0
print_string: ; Routine: output string in SI to screen
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
让我们一步步的来分析这段代码。BITS 16这条并不是一个x86系统的指令,这里仅仅是告诉NASM汇编器,接下来我们将工作在16位模式。NASM将把下面的指令翻译成原生的x86二进制代码。然后是一个start: 标签,在文件的开头,这个标签不是必须的,不过是一个好的编码习惯,分号(;)用来进行代码注释,里面可以写任何你觉得对代码注解有用的东西,它不会被执行。
接下来的6行代码对我们来说并不是特别的有意义,这几行代码用来设置段寄存器的值,以便于堆栈指针(SP)能方便的知道我们的堆栈数据在哪儿,我们的数据段又在哪个位置。之前提到过,段是老的16位系统遗留下来一种混乱恶心的管理内存的东西,不过我们这里仅仅是设置段寄存器的值,然后你就忘了它吧(代码中引用的07C0H 是BIOS加载我们的代码的等效段地址,因此我们从这个地方开始)。
接下来的代码开始有意思起来,mov si, text_string这行的意思是,把
text_string的地址复制到si寄存器中。然后是一条call指令,call指令类似于BASIC语言中的Go Sub或者C语言中函数调用。call指令含义是:跳转到到指定的代码块执行,当任务完成后再回到call指令的下一条指令继续执行。
对于call指令,代码是怎么做到跳转执行之后又正常返回的?当我们使用一个call指令的时候CPU增加IP(指令指针)寄存器的值,并把该值压到栈上。你可以回顾一下之前对栈的解释,栈是一种后进先出(译注:传说中的LIFO)的内存存储结构。所有的事情在最开始通过设置堆栈指针(SP)和堆栈段指针(SS)的值做好了,为栈专门开辟了一块空间,因此你能把临时数据存放在这个上面而不会覆盖我们本身的代码。
所以,call print_string的意思就是说,跳转到print_string分支去执行,并且把call指令的下一条指令的地址压入堆栈中,当从分支返回后,再把该地址从堆栈上弹出并从该地址处的指令接着执行。运行到了print_string分支之后,这个