** 我自己写了份更详细的6.7的注释代码,我觉得应该已经够详细了罢()**
欢迎来到我的博客:TWind的博客
我的CSDN::Thanwind-CSDN博客
我的掘金:Thanwinde 的个人主页
花了好久终于弄完了第一个操作系统的实验(不知道延时了几次)
激动的我赶紧来写点东西来进食后人()
6.1 开始实验之前
首先,它的linux-0.11是个tar文件,编辑你得先
tar -zxvf ****.tar.gz
解压之后才会出来实验文件
6.2 完成 bootsect.s 的屏幕输出功能
这里其实建议先学一学汇编再来看,不然你会 非 常 坐 牢!
注释虽然写得很多,但还是不是很详细,建议搭配ai食用
我就浅写一下我的理解:
entry _start
_start:
没什么好说的,是汇编文件的开始执行的标志
mov ah,#0x03
xor bh,bh
int 0x10
ah对于int 0x10中断来说相当于一个标记,int 0x10会根据ah的值来执行对应的操作
具体可以查表(懒得贴)这里把0x03给了ah,相当于设定了读取光标的任务
xor bh,bh bh代表页码,给设成0,在默认屏幕输出
int 0x10 执行中断,读取光标位置,默认存储在DH,DL中
mov cx,#36
mov bx,#0x0007
mov bp,#msg1
这里cx是输出字符的字节数(固定的!汇编每一种操作都有固定的寄存器,相当反人类)
bx一般表示输出的属性,颜色这类的
bp就是输出的字符的地址
mov es,ax
mov ax,#0x1301
int 0x10
这里是把ax设成0x1301,这决定了显示的属性:前两位决定了是要输出字符,后两位决定输出的字符跟着光标
接下来调动0x10中断开始输出
inf_loop:
jmp inf_loop
msg1:
.byte 13,10
.ascii "Hello OS world, my name is LZJ"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
前两句无限循环,避免终止程序,OS不能终止
接下来msg1是输出的详细信息,.byte是各种回车换行用的
.org可以理解为占用到510字节,毕竟要求bootsect必须有512字节,我们这几行显然不够,最后的
.word 正好占用了两字节,0xAA55相当于标识符,在512字节的最后面,表示你是正统的操作系统文件()
6.3编译和运行
没啥讲的,照着来即可
6.4 bootsect.s 读入 setup.s
74如果你懂了上面的这些,这篇代码也不是很难,同样的建议搭配GPT食用
我的bootsect.s
SETUPLEN=2
SETUPSEG=0x07e0
entry _start
_start:
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#32
mov bx,#0x0007
mov bp,#msg1
mov ax,#0x07c0
mov es,ax
mov ax,#0x1301
int 0x10
load_setup:
mov dx,#0x0000
mov cx,#0x0002
mov bx,#0x0200
mov ax,#0x0200+SETUPLEN
int 0x13
jnc ok_load_setup
mov dx,#0x0000
mov ax,#0x0000
int 0x13
jmp load_setup
ok_load_setup:
jmpi 0,SETUPSEG
msg1:
.byte 13,10
.ascii "This is my first bootsect~"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
setup.s:
entry _start
_start:
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#17
mov bx,#0x0007
mov bp,#msg2
mov ax,cs
mov es,ax
mov ax,#0x1301
int 0x10
inf_loop:
jmp inf_loop
msg2:
.byte 13,10
.ascii "SETUPING..."
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
6.5 再次编译 6.6 修改 build.c
同样没啥讲的
6.7 setup.s 获取基本硬件参数
这更是重量级
极其南蚌
朴实无华的寄存器大乱斗
具体代码不难理解,就是各种操作的各种不同的寄存器很让人头大
~~ 建议抄一遍就行 ~~
没有执念的话,转化函数那里可以认真看一下,其他的就算了罢()
我自己尽了毕生所学写了份详细的注释:
(没放上去跑过!可能编译不过,就当看翻译罢)
INITSEG = 0x9000 !定义常量,放数据的地址
entry _start !汇编开始的标志
_start:
! Print "NOW we are in SETUP"
mov ah,#0x03 !ah功能号,03表示读取光标位置
xor bh,bh !把页码bh设成0
int 0x10 !执行十号中断,读取光标位置并存放在dh dl中
mov cx,#25 !cx先存输出的长度
mov bx,#0x0007 !同样是类似功能符 设置输出的属性
mov bp,#msg2 !bp是存储输出内容地址的地方
mov ax,cs !和下一句都是设置正确的段地址来让字符串正常加载
mov es,ax
mov ax,#0x1301 !调整功能号,输出模式
int 0x10 !十号中断输出
mov ax,cs !我觉得是多余了,重置了段寄存器
mov es,ax
! init ss:sp
mov ax,#INITSEG
mov ss,ax !把栈段地址设成0x9000
mov sp,#0xFF00 !这是栈指针地址,设置得在本段中比较高的位置,因为栈是先进后出 给call用
! Get Params
mov ax,#INITSEG
mov ds,ax !段地址设成0x9000
mov ah,#0x03 !同上,获取光标地址
xor bh,bh
int 0x10
mov [0],dx ![0]代表ds:0x0000,[2]同理
mov ah,#0x88 !15号中断获取扩展内存大小的功能号
int 0x15 !15号中断执行
mov [2],ax !返回值在ax,存储到[2]
mov ax,#0x0000
mov ds,ax !把栈段地址设成0x0000 开始读硬盘的信息
lds si,[4*0x41] !从内存中 0x0041 * 4 处读取一个双字(32 位)的值,将其加载到 ds:si 就是设置开始地址
mov ax,#INITSEG
mov es,ax
mov di,#0x0004 !从[4]继续写数据
mov cx,#0x10 !执行0x10次 也就是 16次
rep
movsb !重复读16次 从ds:si到es:di
! Be Ready to Print
mov ax,cs
mov es,ax
mov ax,#INITSEG
mov ds,ax !重置段地址 准备输出
! Cursor Position
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#18
mov bx,#0x0007
mov bp,#msg_cursor
mov ax,#0x1301
int 0x10
mov dx,[0] !输出前缀
call print_hex !调用自定义的输出函数
! Memory Size
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#14
mov bx,#0x0007
mov bp,#msg_memory
mov ax,#0x1301
int 0x10
mov dx,[2]
call print_hex !不再赘述
! Add KB
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#2
mov bx,#0x0007
mov bp,#msg_kb
mov ax,#0x1301
int 0x10 !不再赘述
! Cyles
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#7
mov bx,#0x0007
mov bp,#msg_cyles
mov ax,#0x1301
int 0x10
mov dx,[4]
call print_hex !不再赘述
! Heads
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#8
mov bx,#0x0007
mov bp,#msg_heads
mov ax,#0x1301
int 0x10
mov dx,[6]
call print_hex !不再赘述
! Secotrs
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#10
mov bx,#0x0007
mov bp,#msg_sectors
mov ax,#0x1301
int 0x10
mov dx,[12]
call print_hex !不再赘述
inf_loop:
jmp inf_loop !执行完了,不断无限循环
print_hex:
mov cx,#4 !重复4遍(一个地址刚好是四个16进制数)
print_digit:
rol dx,#4 !把dx向左移4位(bit),相当于把最高位放到最右边
mov ax,#0xe0f
and al,dl !和0xe0f与操作 会只剩下最右边的一位 赋给al
add al,#0x30 !把al加上0x30 相当于变成对应的数字的ascii
!SF 符号标志 Sign Flag
!表示减法结果的符号位。
!如果结果为负 最高位为 1 则 SF = 1。
!如果结果为正 最高位为 0 则 SF = 0。
!OF 溢出标志 Overflow Flag
表示有符号数计算是否发生溢出。
如果正数减正数变成负数,或者负数减负数变成正数,就会设置为 1。
否则为 0。
cmp al,#0x3a !根据以上信息 如果al比0x3a小 结果为负 SF = 1 OF = 0 代表这是个数字字符
jl outp !如SF != OF 就会执行跳转 到outp输出
add al,#0x07 !能执行到这里证明al比0x3a大 为字母字符 再额外加上0x07变成ascii
outp:
int 0x10 !输出字符
loop print_digit !没有处理4遍的话 回到头再处理(检测cx) 随后cx--
ret !返回到调用处(return;)
print_nl: !这里是一个输出换行符的功能 但是没用到
mov ax,#0xe0d ! CR
int 0x10
mov al,#0xa ! LF
int 0x10
ret
msg2:
.byte 13,10
.ascii "NOW we are in SETUP"
.byte 13,10,13,10
msg_cursor:
.byte 13,10
.ascii "Cursor position:"
msg_memory:
.byte 13,10
.ascii "Memory Size:"
msg_cyles:
.byte 13,10
.ascii "Cyls:"
msg_heads:
.byte 13,10
.ascii "Heads:"
msg_sectors:
.byte 13,10
.ascii "Sectors:"
msg_kb:
.ascii "KB"
.org 510
boot_flag:
.word 0xAA55