零、实验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:
	!416进制数进行显示
	mov cx,#4
	!mov dx,(bp)  !bp指向栈顶,将其指向的指存入dx?什么栈顶? bp指向的不是msg2吗?不用管栈

print_digit:
	!使从高位到低位显示416进制数
	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了)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐