零、实验1 实验环境搭建
1.参考教程
2.遇到的问题
(1)dokg: 错误:另外一个进程已经为 dpkg fronted lock 加锁
解决办法:重启,解释
3.补充:查看ubuntu 位数 uname -a
一、实验2的内容
1.实现bootsect.s的屏幕输出功能;
2.bootsect.s读入setup.s;
3.setup.s获取并显示基本硬件参数;
教程:操作系统原理与实践
二、实现bootsect.s的屏幕输出功能
1.代码(和教程有点不同,在2.说明里解释了;按照教程来)
mov ah, #0x03
xor bh, bh
int 0x10
mov ax, #0x1301
mov bx, #0x0007
mov cx, #32
mov bp, #msg1
int 0x10
inf_loop:
jmp inf_loop
msg1:
.byte 13,10
.ascii "Forrest's OS is Loading..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
在原有linux-0.11的bootsect.s代码进行修改
2.解析
!首先读光标位置
!BIOS中断0x10 功能号ah = 0x03, 读光标的位置
!input : bh = 页号
! output : dh = 行号(0x00顶端);dl = 列号(0x00最右端)
mov ah, #0x03
xor bh, bh
int 0x10
!输出字符串 "Forrest's OS is Loading..."
!BIOS中断0x10 功能号ah = 0x13,显示字符串
!input : al = 放置光标的方式及规定属性。0x01表示使用bl中的属性值,光标停在字符串结尾处。
!bh = 显示页面号,bl = 字符属性。
!cx = 显示的字符串字符数,这里是26+6=32,即msg1所占的字符。
! es:bp指向需显示的字符串起始位置处
mov ax, #0x1301
mov bx, #0x0007
mov cx, #32
mov bp, #msg1 !es存的是#INITSEG, 0x9000
int 0x10
!设置一个无限循环(纯粹为了多看一万眼字符串显示)
inf_loop:
jmp inf_loop
!msg1处放置字符串
msg1:
!换行+回车
.byte 13,10
!字符串
.ascii "Forrest's OS is Loading..."
!两对换行+回车
.byte 13,10,13,10
.org 508 !设定起始偏移地址
!原有代码就有
root_dev:
.word ROOT_DEV
!这样boot_flag的2个魔数在最后2个字节
!boot_flag引导扇区标记,0xAA55
boot_flag:
.word 0xAA55
说明:
①教程中,把es设定为#0x07c0,虽然最终结果没出错,但实际上逻辑是有问题的,因为在屏幕显示字符串之前,bootsect所在内存位置发生了移动,而linus已经将es设定成了#INITSEG,所以以下截图的内容可以去掉:
②教程下述做法会报错
注意:看了后面的教程才反应过来,原来是重新写bootsect.s,我以为是在原版基础上进行修改…
实际上不需要修改linus的这块代码。
疑问点:
为什么页号为0?
补充: “回车”,告诉打字机把打印头定位在左边界;另一个叫做 “换行”,告诉打字机把纸向下移一行。
3.编译 + 做Image文件
将完成屏幕显示的代码在开发环境中编译,并将编译后的目标文件做成 Image 文件。
这部分的解释见教程。
(1)编译
as86 -0 -a -o bootsect.o bootsect.s
(2)链接
ld86 -0 -s -o bootsect bootsect.o
(3)生成Image文件
dd bs=1 if=bootsect of=Image skip=32
linux的dd命令,以bootsect为输入,以每次1byte的速度写到文件Image中,并跳过开头的32byte才开始写入
(4)拷贝到linux-0.11目录下
. ./父级目录
4.bootsect引导后的系统启动情况
三、bootsect.s读入setup.s
此处起,按照教程来,而不是在原有代码上修改。
1.编写setup.s的代码
entry _start
_start:
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #25
mov bx, #0x0007
mov ax, cs
mov es, ax !修改es的值为cs
mov ax, #0x1301
mov bp, #msg2
int 0x10
inf_loop:
jmp inf_loop
msg2:
.byte 13,10
.ascii "Now we are in SETUP"
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
2.编写 bootsect.s 中载入 setup.s的代码
BOOTSEG=0x07c0 !左移4位后就是0x7c00,占512B,所以偏移512B(0x0200)后得0x7e00;
SETUPSEG=0x07e0
SETUPLEN=2 !linus写的是4,教程是2
entry _start
_start:
!屏幕输出功能
mov ah,#0x03
xor bh,bh
int 0x10
mov bx,#0x0007
mov cx,#32
mov bp,#msg1
mov ax,#BOOTSEG
mov es,ax
mov ax,#0x1301
int 0x10
!加载setup
load_setup:
mov dx,#0x0000
mov cx,#0x0002 !扇区不是从0开始的,而是从1开始的,1是bootsect所在的扇区,setup从扇区2开始
mov bx,#0x200 !es:bx 指向将要存放的内存地址
mov ax,#0x0200+SETUPLEN !读2个扇区到内存
int 0x13
jnc ok_load_setup !成功就跳转到ok_load_setup执行
mov dx,#0x0000
mov ax,#0x0000 !复位软盘
int 0x13
jmp load_setup
!跳转到setup执行
ok_load_setup:
jmpi 0,SETUPSEG !段间跳转指令 cs = SETUPSEG,ip = 0
!inf_loop:
! jmp inf_loop
msg1:
.byte 13,10
.ascii "Forrest's OS is Loading..."
.byte 13,10,13,10
.org 510
boot_flag:
.word 0xAA55
3.make BootImage
有 Error!这是因为 make 根据 Makefile 的指引执行了 tools/build.c,它是为生成整个内核的镜像文件而设计的,没考虑我们只需要 bootsect.s 和 setup.s 的情况。它在向我们要 “系统” (system)的核心代码。为完成实验,接下来给它打个小补丁。
4.修改build.c
build.c 从命令行参数得到 bootsect、setup 和 system 内核的文件名,将三者做简单的整理后一起写入 Image。
上文因缺乏system的代码,所以报错
改造build.c的思路就忽略所有与system有关的工作:
四、setup.s获取并显示基本硬件参数
1.代码(含关键注释)
INITSEG = 0x9000
entry _start
_start:
!屏幕输出功能
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #25
mov bx, #0x0007
mov ax, 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
2.调试
在oslab目录下 ./dbg-asm
3.结果
0x00CC = 12 * 16 + 12 = 204
疑惑点:保存光标的位置时,光标应该在SETUP的末尾,为什么是1600这个位置?0x16是行号,0x00是列号不太合理啊。(补充:这里想错了,光标应该在Now下2行的最左边,因为有2对回车和换行啊,这也就能解释列号是0x00了)