操作系统启动
注意:此部分的代码 /boot/bootsect.s 和 /boot/setup.s是需要重构的,而不是按照源码修改
1.1我对于开机的系统理解
1。就我个人而言开机相当于让整个机器热起来,在通电之后,机器是无知的,这时候需要让源源不断的“内容”加载到内存中,让处理器热起来一直处于工作状态!但是如果一开始不在内存中永久的加入相当于“指路功能的”boot,计算机永远找不到记录在磁盘上的数据,所以我们在内存0xffff0处添加了一直存在的信息就是为了找到操作系统的第一个程序bootsect.s!!!
2。根据0xffff0中的信息我们在磁盘中找到了bootsect.s并把程序加载到实地址0x7c00处,然后该段程序转移至0x90000,而bootsect.s的作用与boot有点相似就是将磁盘中的下一段程序setup.s载入0x90200,注意伏笔0x200正好是512
3。进入setup操作系统逐渐开始掌握计算机的主导权,这时候我们需要得知计算机的各项属性的值,然后从实地址20位寻址方式到了保护模式的32位寻址方式GDT,后面介绍GDT,然后按保护模式的方法将system模块导入
如图所示
1.2bootsect.s实验源码
SETUPLEN=2
SETUPSEG=0x07e0 !bootsect最后跳转到了0x9000处,但是我们现在写的这个代码只是用来模拟linux-0.11所以说
!直接0x07c0+0x0020 这里注意!
entry _start
_start:
mov ah,#0x03 ! INT10 中断读光标位置
xor bh,bh ! page 0
int 0x10
mov cx,#27 ! 字符串长度需要在后面确认
mov bx,#0x0007 ! page--0 line--7
mov bp,#msg1 ! 跳转至.msg1
mov ax,#0x07c0
mov es,ax ! es:bp此寄存器指向要显示的字符串的初始位置
! 但是重构代码的时候,并没有在一开始的时候就给es赋值所以需要在这里赋值,区别于源代码
mov ax,#0x1301 ! ah---13 al---01
int 0x10
load_setup: ! 加载外存中SETUP有关的代码
mov dx,#0x0000 ! 0号磁头---dh 0号驱动---dl
mov cx,#0x0002 ! 0号磁道---ch 第二个扇区开始
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 !跳转至SETUP开始的阶段
msg1:
.byte 13,10
.ascii "to define is to limit"
.byte 13,10,13,10
.org 510 ! 508+2 与ROOT_DEV有关 这里不做详细的解释
boot_flag:
.word 0xAA55 !这里的0xAA55正好是一个字---两字节 bootsect总大小 510+2=512
!也就是开头的0x200 世界线回收QAQ
1.3 setup.s
INITSEG = 0x9000
entry _start
_start:
!屏幕输出功能
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #25
mov bx, #0x0007
mov ax, cs ! cs存储的就是该段的基地址
mov es, ax !修改es的值为cs
mov ax, #0x1301
mov bp, #msg2
int 0x10
!将硬件参数取出来放在内存0x90000处
mov ax,#INITSEG
mov ds,ax !数据段的基地址0x9000
!读光标位置
xor bh,bh
mov ah,#0x03
int 0x10
mov [0],dx !dh=行号,dl=列号
!读扩展内存大小,超过1M则为扩展
mov ah,#0x88
int 0x15
mov [2],ax
!读第1个磁盘参数表,共16个字节大小;其首地址在int 0x41的中断向量位置
!中断向量表的起始地址是0x000, 共1KB大小,并且每个表项占4B
!所以第1个磁盘参数表的首地址的地址:0x41*4=0x104, 此处4B由段地址和偏移地址组成
mov ax,#0x0000
mov ds,ax !中断向量表的起始地址
lds si,[4*0x41] !先存入的是偏移地址,取出存到si中 !取出的4个字节,高位存入ds,低位存入si
mov ax,#INITSEG
mov es,ax
mov di,#0x0004 !光标和内存共占4B; 不是mov di,#0x0080 !目标地址 : 0x9000:0x0080
mov cx,#16
rep
movsb !按字节传送
!打印前的准备
mov ax,cs
mov es,ax !setup所在的代码段
mov ax,#INITSEG
mov ds,ax !数据段,指向参数所在的地方
!读光标位置,显示提示语,并把数值入栈
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#18 !16+2
mov bx,#0x0007
mov bp,#msg_cursor !"Cursor position:" es:bp
mov ax,#0x1301
int 0x10
mov dx,[0] !存好的光标位置读出存到dx中,那没必要再读光标了吧;显示字符串需要放置光标,所以要读
call print_hex !打印光标位置
!显示内存大小
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#14 !12+2
mov bx,#0x0007
mov bp,#msg_memory !"Memory Size:"
mov ax,#0x1301
int 0x10
mov dx,[2]
call print_hex
!补上KB
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#2
mov bx,#0x0007
mov bp,#msg_kb
mov ax,#0x1301
int 0x10
!柱面,cylinder Cyles
mov ah,#0x03
xor bh,bh
int 0x10
mov cx,#8
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
!扇区 sectors
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,[18] !应该是18,而不是12吧
call print_hex
inf_loop:
jmp inf_loop
!显示硬件参数(16位形式)
print_hex:
!以4位16进制数进行显示
mov cx,#4
!mov dx,(bp) !bp指向栈顶,将其指向的指存入dx?什么栈顶? bp指向的不是msg2吗?不用管栈
print_digit:
!使从高位到低位显示4位16进制数
rol dx,#4
!ah=0x0e,显示一个字符;al=要显示的字符的ascii码;BIOS中断为int 0x10
mov ax,#0x0e0f !此时的al未半字节的掩码
and al,dl !取dl的低4位存入al中
add al,#0x30
cmp al,#0x3a !如果是数字,范围是0x30~0x39,即小于0x3a
jl outp !al小于#0x3a跳转,即数字则跳转
add al,#0x07 !字母则加上0x07,a~f的范围0x41~0x46
outp:
int 0x10
loop print_digit !每次执行 loop 指令,cx 减 1,然后判断 cx 是否等于 0。 这里即执行4次
ret
!打印回车换行
print_nl:
!CR 回车
mov ax,#0x0e0d
int 0x10
!LF 换行
mov ax,#0x0e0a
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 "Cyles:"
msg_heads:
.byte 13,10
.ascii "Heads:"
msg_sectors:
.byte 13,10
.ascii "Sectors:"
msg_kb:
.ascii "KB"
.org 510
boot_flag:
.word 0xAA55