qemu 的小知识,x86_64 是 64位,i386 是 32位,但是 x86 是 32,64是64……所以为什么要叫86!哦,i386,我知道了。所以x86_64 指的是基于86的64位!
i386是80386,第一个支持32位的处理器,8086酱才是16位哒
其实 x86是这一系列的统称,8086这孩子是第一个,现在都说32了……因为16的花季太短了吧?
开始征程吧哈哈哈哈!
先是看着 birmingham 的教程做,也有这个
GitHub - cfenollosa/os-tutorial: How to create an OS from scratch
不过先看第一个好了
使用 NASM 的汇编语言!因为一半都是要写汇编所以加油吧
https://zhuanlan.zhihu.com/p/109264787
汇编语法
mov ah , 0 x0e ; int 10/ ah = 0 eh -> scrolling teletype BIOS routine
这个解释可以看 INT 10H - 维基百科,自由的百科全书
显示模式,可以直接输出
总结一下看不懂的,times 是连续赋值多少个,db 是 define byte. $
是当前地址,$$
是开头地址,所以差值是当前 offset,510 - offset 等于距离 510 还有多少位,都填满 0. boot sector 有 514,最后 4byte 是 magic number
nasm bootsect.asm -f bin -o bootsect.bin
hourse’s 下次一定记得经常存盘
初步程序
里面有 reg,一个16bits 一共2word,可以单独定义
print hello 程序
该如何读取?
第三第四个可以读出来,[a],a 存储的东西作为地址去寻找,因为这个是开始于 0x7c00,所以在 the_secret label 里定义的X需要加上0x7c00偏移
org 指令 ORG是Origin的缩写:起始地址,源。在汇编语言源程序的开始通常都用一条ORG伪指令来实现规定程序的起始地址。如果不用ORG规定则汇编得到的目标程序将从0000H开始。例如:
ORG 2000H
这次的 org 就是 0x7c00
def string
my_string :
db ’ Booting OS ’
因为括号起来,所以里面的每个字母转换成 ASCII,虽然是 byte 但是往后 def
加上 null terminate
my_string :
db ’ Booting OS ’ ,0
使用 stack
因为 CPU 自带的 reg 经常不够用,所以我们可以使用内存了 bp(base) 和 sp (top)两个特殊 reg 用来记录地址,底部和顶端。因为这个是 16 bits,所以一次 pop 16 bits
base 会远离敏感程序区域,因此不用担心 stack 太大 overwrite 重要的代码。是向下发展的!sp 比 bp 地址更低(所以不会往上才碰不到),每次 push 会降低 sp 地址
push 和 pop 都是 16 bits,当 push,A的时候前8 bits 填充为0
跳转语句?
跟 cmp 一起使用
然后写一个jmp 程序
1 | mov bx, 30 |
之前写错了,未定义的时候是0,打印出来是 U?这样的话它会继续执行下面的label,所以再加入一个 jmp
1 | mov bx, 0 |
定义 function
1 | mov al, 'H' ; store H in al so func can show it |
int 定义是常量,常量(constant)是程序中使用的一个确定数值,在汇编阶段就可以确定,直接编码于指令代码中,不是保存在存储器中可变的变量,因为是编码在指令中的量,和指令一起存储了,所以不用单独开辟主存空间,所以也就没法动态改变它了,这也正是高级语言常量无法修改的原因。
但是这样会把 reg 数据一起洗掉!怎么办呢
加入 pusha 和 popa,将数据扔到stack再return。虽然我很好奇那么它怎么知道哪个是哪个,但是避免太超纲先不看
修改版本
1 | mov al, 'H' ; store H in al so func can show it |
不过这样改动之后是不是 al 也没有了?试试看
不会的说!然后就 print 了两个h!
卧槽,什么情况
哦,是因为正常向下执行的时候再次执行了 func 所以 print 了两次,普通执行的话并不会改变什么,就算提前 pusha 也能打印出来……嗯……所以
这就是为啥 func 内部改动不会改变数值!因为可能 call func 的时候要改变 reg 去 perform job,所以pusha不会把东西倒出去,数值还是一样的,但是 popa 就会一切还原
所以你才用指针直接修改!!!!!!!!
我!的!天!
我终于看到了这个未解之谜的真相了!!!!!!!!!!!!
include external file
想要 reuse your file?可以直接引用,那么请结合以上自己写作一个打印两个 string 的程序吧~
label 到底是什么?
int 0x10 不是! 那是16进制!第17终止符,ah 0x0e 是这个模式下面支持的func,详情查看 wiki
int 10 是啥?int 13?
CR and LF are control characters, respectively coded 0x0D
(13 decimal) and 0x0A
(10 decimal)
这俩都是早期 tty 留下的产物,CR 是移动到行首但不换行,LF 是往下竖直移动一格,但不水平移动
CR = carriage return,ascii 码 13
LF = line feed,ascii 码 10
现在的话win使用两个移动,就是 \r\n,0x0D 0x0A,unix 只用 LF 了,所以要换行直接在最后加上10,13
附带的 print string程序
1 | ; using for loop to print char, until it reach 0 |
1 | [org 0x7c00] ; start at this point |