📍前言
衔接上文《保姆级》一次成功安装bochs!!附带相应ubuntu版本和bochs版本
同时为了更好地方便之后代码的管理和书写,建议大家在ubuntu里安装vscode,同时配置shell命令和makefile文件,实现快速编译,还请利用几分钟时间配置一下《大大简化每次运行bochs的命令行》ubuntu里安装vscode + makefile文件基本编写 + shell命令,一次配置!终身受益!
📍实验准备
-
在vscode根目录文件夹PUBLIC下创建3-2文件夹,里面装这次实验的所有代码
(可以看到,我的bochs也是安装在PUBLIC下的,所以跟3-2处同一目录,既方便管理也方便运行)
-
创建makefile文件
3-2文件夹里创建makefile文件,先不输入内容,待会再说
-
创建run.sh文件
PUBLIC文件夹里创建run.sh文件(与3-2文件夹同级!)
总之,如下文件结构
📍实验要求
-
编写mbr.S文件
编写MBR主引导程序代码mbr.S,程序中用中断方式显示“mbr+自己的姓名”,如“mbr-zhangsan”。使用自己的配色 -
验证mbr.S对loader.S的初步加载
loader.S是内核加载器,但在本次3-2实验中,暂不实现内核加载功能,而是简单地实现用显存方式打印字符功能,用于验证mbr对它所在的硬盘块的加载。
字符显示“loader+自己的姓名”,如“loader-zhangsan”。使用自己的配色。
将loader.S编译并将loader.bin写入硬盘
📍实验过程
📍📍运行mbr.S文件
在3-2文件夹内创建boot文件夹,再在boot文件夹内创建mbr.S文件
mbr.S内容如下👇
;主引导程序
;------------------------------------------------------------
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
; 清屏 利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
mov ax, 0x600
mov bx, 0x700
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0x184f ; 右下角: (80,25),
; VGA文本模式中,一行只能容纳80个字符,共25行。
; 下标从0开始,所以0x18=24,0x4f=79
int 0x10 ; int 0x10
;;;;;;;;; 下面这三行代码是获取光标位置 ;;;;;;;;;
;.get_cursor获取当前光标位置,在光标位置处打印字符.
mov ah, 3 ; 输入: 3号子功能是获取光标位置,需要存入ah寄存器
mov bh, 0 ; bh寄存器存储的是待获取光标的页号
int 0x10 ; 输出: ch=光标开始行,cl=光标结束行
; dh=光标所在行号,dl=光标所在列号
;;;;;;;;; 获取光标位置结束 ;;;;;;;;;;;;;;;;
;;;;;;;;; 打印字符串 ;;;;;;;;;;;
;还是用10h中断,不过这次是调用13号子功能打印字符串
mov ax, message
mov bp, ax ; es:bp 为串首地址, es此时同cs一致,
; 开头时已经为sreg初始化
; 光标位置要用到dx寄存器中内容,cx中的光标位置可忽略
mov cx, 15 ; cx 为串长度,不包括结束符0的字符个数
mov ax, 0x1301 ; 子功能号13是显示字符及属性,要存入ah寄存器,
; al设置写字符方式 ah=01: 显示字符串,光标跟随移动
mov bx, 0x2 ; bh存储要显示的页号,此处是第0页,
; bl中是字符属性, 属性黑底绿字(bl = 02h)
int 0x10 ; 执行BIOS 0x10 号中断
;;;;;;;;; 打字字符串结束 ;;;;;;;;;;;;;;;
jmp $ ; 使程序悬停在此
message db "mbr - luohaojia"
times 510-($-$$) db 0
db 0x55,0xaa
makefile内容👇
.PHONY:build burn clean
mbr_source=boot/mbr.S
mbr_target=boot/mbr.bin
hard_disk=/home/lhj/Public/bochs/bin/hd60M.img
build:
nasm -o $(mbr_target) $(mbr_source)
burn:
dd if=$(mbr_target) of=$(hard_disk) bs=512 count=1 conv=notrunc
run.sh内容👇
cd 3-2
make build
make burn
cd /home/lhj/Public/bochs/bin
./bochs -f lhjbochsrc.disk
打开vscode内置的终端(快捷键:ctrl + ~),输入:./run.sh
ok,按一个回车(默认选择为6),再按一个c(表示continue)
📍📍验证mbr.S对loader.S的初步加载
在boot文件夹里创建loader.S文件
loader.S内容👇
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
mov byte [gs:0x00],'l'
mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色
mov byte [gs:0x02],'o'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'a'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'d'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'e'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0a],'r'
mov byte [gs:0x0b],0xA4
mov byte [gs:0x0c],'-'
mov byte [gs:0x0d],0xA4
mov byte [gs:0x0e],'l'
mov byte [gs:0x0f],0xA4
mov byte [gs:0x10],'u'
mov byte [gs:0x11],0xA4
mov byte [gs:0x12],'o'
mov byte [gs:0x13],0xA4
mov byte [gs:0x14],'h'
mov byte [gs:0x15],0xA4
mov byte [gs:0x16],'a'
mov byte [gs:0x17],0xA4
mov byte [gs:0x18],'o'
mov byte [gs:0x19],0xA4
mov byte [gs:0x1a],'j'
mov byte [gs:0x1b],0xA4
mov byte [gs:0x1c],'i'
mov byte [gs:0x1d],0xA4
mov byte [gs:0x1e],'a'
mov byte [gs:0x1f],0xA4
jmp $ ; 通过死循环使程序悬停在此
在boot文件夹里创建include文件夹,include文件夹里再创建boot.inc文件
boot.inc内容👇
;------------- loader和kernel ----------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
修改mbr.S内容
;主引导程序
;------------------------------------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
; 清屏
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
mov ax, 0600h
mov bx, 0700h
mov cx, 0 ; 左上角: (0, 0)
mov dx, 184fh ; 右下角: (80,25),
; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
; 下标从0开始,所以0x18=24,0x4f=79
int 10h ; int 10h
mov eax,LOADER_START_SECTOR ; 起始扇区lba地址
mov bx,LOADER_BASE_ADDR ; 写入的地址
mov cx,1 ; 待读入的扇区数
call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区)
jmp LOADER_BASE_ADDR
;-------------------------------------------------------------------------------
;功能:读取硬盘n个扇区
rd_disk_m_16:
;-------------------------------------------------------------------------------
; eax=LBA扇区号
; ebx=将数据写入的内存地址
; ecx=读入的扇区数
mov esi,eax ;备份eax
mov di,cx ;备份cx
;读写硬盘:
;第1步:设置要读取的扇区数
mov dx,0x1f2
mov al,cl
out dx,al ;读取的扇区数
mov eax,esi ;恢复ax
;第2步:将LBA地址存入0x1f3 ~ 0x1f6
;LBA地址7~0位写入端口0x1f3
mov dx,0x1f3
out dx,al
;LBA地址15~8位写入端口0x1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;LBA地址23~16位写入端口0x1f5
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f ;lba第24~27位
or al,0xe0 ; 设置7~4位为1110,表示lba模式
mov dx,0x1f6
out dx,al
;第3步:向0x1f7端口写入读命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;第4步:检测硬盘状态
.not_ready:
;同一端口,写时表示写入命令字,读时表示读入硬盘状态
nop
in al,dx
and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready ;若未准备好,继续等。
;第5步:从0x1f0端口读数据
mov ax, di
mov dx, 256
mul dx
mov cx, ax ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字,
; 共需di*512/2次,所以di*256
mov dx, 0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret
times 510-($-$$) db 0
db 0x55,0xaa
修改makefile文件内容👇
.PHONY:build burn
mbr_source=boot/mbr.S
mbr_target=boot/mbr.bin
loader_source=boot/loader.S
loader_target=boot/loader.bin
hard_disk=/home/lhj/Public/bochs/bin/hd60M.img
build:
nasm -I boot/include/ -o $(mbr_target) $(mbr_source)
nasm -I boot/include/ -o $(loader_target) $(loader_source)
burn:
dd if=$(mbr_target) of=$(hard_disk) bs=512 count=1 conv=notrunc
dd if=$(loader_target) of=$(hard_disk) bs=512 count=1 seek=2 conv=notrunc
ok,打开终端,启动bochs吧!(图中红框为编译命令,红框下为bochs启动界面,是不是很方便?!)
老规矩,按个回车,按个c
本实验所有源码👉👉👉计算机系统结构与操作系统实验三bochs源代码