自制操作系统日记(三):加载其他文件执行

简介: 上篇中我们成功将软盘数据读取到内存并显示到屏幕上,接下来我们将加载其他的文件并执行文件代码

代码仓库地址:https://github.com/freedom-xiao007/operating-system

简介

上篇中我们成功将软盘数据读取到内存并显示到屏幕上,接下来我们将加载其他的文件并执行文件代码

新增软件说明

本篇中需要用到相应的软件,Windows相关的下载链接如下:

  • git for windows:在本篇中需要拼接其他文件放到镜像文件后面,本文使用的linux下的dd命令,使用git的bash窗口可以使用dd命令,比较方便

程序整体思路

本次我们将写一个新的nasm文件,其功能是清屏并输出文字:Start Loader

在上篇的程序读入内容后,调用我们新的nasm文件,然后执行对应的代码

经过这些操作后,我们需要运行的命令也相对比较多了,我们将其整合到一个脚本里面,方便一键运行

大致的步骤如下:

    1. 新nasm文件编写
    1. 原启动nasm程序读取软盘数据,执行新的nasm文件代码
    1. 将相关的命令整合成脚本,方便一键运行

1. 新nasm文件编写

想着比较简单,但实际操作还是出现了点问题

首先是ORG的地址问题,原来是跟着《30天写操作系统》书中使用程序加载到内存中的地址,但导致在显示字符串时有问题,经过不断尝试,感觉是地址没有对应上,导致显示了空白字符(知识掌握程度还是不够深,目前还是凭感觉猜测)

后面使用了《一个64位操作系统的设计和实现》书的地址,设置比较大的地址,成功显示了,所以猜测是地址的问题

其他的就是从《汇编语言程序设计》书中抄了一个清屏的代码

loader.asm文件完整代码如下:

;加载到一块无人用的地址
ORG 10000h
JMP print_load_info

print_load_info:
;清屏
MOV AH,6
MOV AL,0
MOV CH,0
MOV CL,0
MOV DH,26
MOV DL,79
MOV BH,7
INT 10H

MOV AX,    CS
MOV DS,    AX
MOV ES,    AX
MOV AX,    0x00
MOV SS,    AX
MOV SP,    0x7c00
;=======    display on screen : Start Loader......
MOV AX,    1301h
MOV BX,    000fh
MOV DX,    0200h        ;row 2
MOV CX,    12
PUSH AX
MOV AX,    DS
MOV ES,    AX
POP AX
MOV BP,    StartLoaderMessage
INT 10h

JMP    fin
fin:
HLT ;CPU停止,等待指令
JMP fin ;无限循环

;=======    display messages
StartLoaderMessage:    db    "Start Loader"

2. 原启动nasm程序读取软盘数据,执行新的nasm文件代码

需要对我们原来的主文件进行调整

首先是对加载扇区的调整,原来我们是从扇区1开始读取的,现在改为2。因为扇区1是启动引导扇区,存放的就是我们的主文件,但不需要这个,所以我们直接从扇区2开始读取我们的新的文件到内存中

对应的就是把read_file_ready中的CL改为2

read_file_ready: ;读软盘准备
MOV AH, 02 ;指明读扇区功能调用
MOV AL, 1 ;指明要读的扇区数为1
MOV DL, 0x00 ;指明要读的驱动为A
MOV DH, 0 ;指定读取的磁头0
MOV CH, 0 ;柱面0
MOV CL, 2 ;原来我们是从扇区1开始读取的,现在改为2。因为扇区1是启动引导扇区,存放的就是我们的主文件,但不需要这个,所以我们直接从扇区2开始读取我们的新的文件到内存中
;下面三句指定读取到内存地址0x0820处
MOV AX, OffSetOfLoader
MOV ES, AX
MOV BX, 0 ;数据读取后放到的内存地址

最后是读取完成后进行调用,汇编里面好像是直接跳转到相关的内存地址后,指令IP就会自动处理

跳转这里的地址问题是需要注意的,需要给出完成的地址

我们直接修改fin即可:

fin:
;下面三句指定读取到内存地址0x0820处
JMP OffSetOfLoader:0

3. 将相关的命令整合成脚本,方便一键运行

我们新建一个run.sh的脚本,然后将我们需要运行的命令都写到里面去,如下:

# !bash
D:\\software\\NASM\\nasm.exe .\\myOS.asm -o .\\myOS.img
D:\\software\\NASM\\nasm.exe .\\loader.asm -o .\\loader.bin
cp myOS.img os.img
dd if=./loader.bin of=./os.img oflag=append conv=notrunc
D:\\software\\qemu\\qemu-system-x86_64.exe -L . -m 64 -fda .\\os.img

上面的脚本功能是:

    1. 将主文件myOS.asm进行编译
    1. 将文件loader.asm进行编译
    1. 将编译后的myOS.img复制一份
    1. 使用dd命令,将loader.bin文件拼接到os.img文件后面
    1. 最后运行即可

在安装完git后,可以在根目录中代码git bash,然后运行run.sh脚本即可

如下图:运行git bash、运行脚本、成功显示

image.png

image.png

image.png

总结

终于快要进行到使用C或者其他语句进行操作系统编写的进程了,泪目

写一个引导从这几天看来确实是挺麻烦的,也难怪用rust写操作系统中直接使用grub之类的引导

虽然过程艰辛,但结果是令人振奋的,根据书中的描述内存,后面会相对好点,虽然预感还是艰辛,但加油

本次完整代码

myOs.asm文件

; cherry-os
ORG 0x7c00 ;指定程序装载的位置

;下面用于描述FAT12格式的软盘
JMP entry
DB 0x90
DB "CHRRYIPL" ;启动区的名称可以是任意的字符串,但长度必须是8字节
DW 512; 每一个扇区的大小,必须是512字节
DB 1 ;簇的大小(必须为1个扇区)
DW 1 ;FAT的起始位置(一般从第一个扇区开始)
DB 2 ;FAT的个数 必须是2
DW 224;根目录的大小 一般是224项
DW 2880; 该磁盘的大小 必须是2880扇区
DB 0xf0;磁盘的种类 必须是0xf0
DW 9;FAT的长度 必须是9扇区
DW 18;1个磁道(track) 有几个扇区 必须是18
DW 2; 磁头个数 必须是2
DD 0; 不使用分区,必须是0
DD 2880; 重写一次磁盘大小
DB 0,0,0x29 ;扩展引导标记 固定0x29
DD 0xffffffff ;卷列序号
DB "CHERRY-OS  " ;磁盘的名称(11个字节)
DB "FAT12   " ;磁盘的格式名称(8字节)
TIMES 18 DB 0; 先空出18字节 这里与原文写法不同

OffSetOfLoader    equ    0x0820

;程序核心
entry:
MOV AX,0  ;初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX
read_file_ready: ;读软盘准备
MOV AH, 02 ;指明读扇区功能调用
MOV AL, 1 ;指明要读的扇区数为1
MOV DL, 0x00 ;指明要读的驱动为A
MOV DH, 0 ;指定读取的磁头0
MOV CH, 0 ;柱面0
MOV CL, 2 ;原来我们是从扇区1开始读取的,现在改为2。因为扇区1是启动引导扇区,存放的就是我们的主文件,但不需要这个,所以我们直接从扇区2开始读取我们的新的文件到内存中
;下面三句指定读取到内存地址0x0820处
MOV AX, OffSetOfLoader
MOV ES, AX
MOV BX, 0 ;数据读取后放到的内存地址
read_file:
;调试用,用于查看读取磁盘是否达到了循环次数,call相当于函数调用
MOV SI, read_file_msg
CALL func_show_msg

;前面调用函数时,改变了有关的寄存器,这里重置回来(也可以使用栈,但这个方便点)
MOV AH, 02 ;指明读扇区功能调用
MOV AL, 1 ;指明要读的扇区数为1
MOV BX, 0 ;数据读取后放到的内存地址

INT 13H ;调用BIOS文件读取
JNC read_file_loop
MOV SI, read_file_error_msg
JMP show_msg_info
read_file_loop: ;循环读取内容,循环的顺序是扇区->磁头->柱面,不知道这里面是否有什么说法?可以调换吗?
;把内存地址后移0x200
MOV AX, ES
ADD AX, 0x0020
MOV ES, AX
ADD CL, 1 ;加1,读取下一个扇区
CMP CL, 18 ;如果CL扇区大于软盘总扇区数18,则说明读取完成,不再读取
JBE read_file
; 上面是扇区的循环,完毕后到磁头的循环,重置扇区,增加磁头到反面1
MOV CL, 1
ADD DH, 1
CMP DH, 2
JB read_file
;上面都完成后,到柱面的读取循环,重置扇区(前面已重置)和磁头,增加柱面(不知道为啥书中不是80而是10)
MOV DH, 0
ADD CH, 1
CMP CH, 19
JB read_file
show_mem_file: ;打印显示刚才加载的文件内容
MOV AX, OffSetOfLoader
; MOV AX, 0x0a20
MOV ES, AX
MOV DI, 0
show_mem_byte: ;整个数据也就512,所以循环512次即可
MOV AL, BYTE [ES:DI]
CMP DI, 512
JE loader_end
ADD DI, 1
MOV AH,0x0e ;显示一个文字
MOV BX,15 ;指定字符的颜色
INT 0x10 ;调用显卡BIOS
JMP show_mem_byte
loader_end: ;启动程序加载完成
MOV AL,0x0a
MOV AH,0x0e ;显示一个文字
MOV BX,15 ;指定字符的颜色
INT 0x10 ;调用显卡BIOS
MOV SI,msg
show_msg_info: ;加载完成,成功显示
MOV AL,[SI]
ADD SI,1
CMP AL,0
JE fin
MOV AH,0x0e ;显示一个文字
MOV BX,15 ;指定字符的颜色
INT 0x10 ;调用显卡BIOS
JMP show_msg_info
fin:
; HLT ;CPU停止,等待指令
; JMP fin ;无限循环

MOV SI, jmp_msg
CALL func_show_msg
;下面三句指定读取到内存地址0x0820处
JMP OffSetOfLoader:0
func_show_msg:
MOV AL,[SI]
ADD SI,1
CMP AL,0
JE func_ret
MOV AH,0x0e ;显示一个文字
MOV BX,15 ;指定字符的颜色
INT 0x10 ;调用显卡BIOS
JMP func_show_msg
func_ret:
RET
read_file_msg:
DB "r"
DB 0
read_file_error_msg:
DB 0x0a , 0x0a ;换行两次
DB "read file error!!!"
DB 0x0a
DB 0
show_mem_info_msg:
DB 0x0a , 0x0a ;换行两次
DB "start show mem file!!!"
DB 0x0a
DB 0
msg:
DB 0x0a , 0x0a ;换行两次
DB "hello, my OS, boot loader end"
DB 0x0a
DB 0
jmp_msg:
DB 0x0a ;换行两次
DB "start jmp loader bin"
DB 0
boot_flag: ;启动区标识
TIMES 0x1fe-($-$$) DB 0 ;填写0x00,直到0x001fe
DB 0x55, 0xaa

loader.asm 文件

;加载到一块无人用的地址
ORG 10000h
JMP print_load_info

print_load_info:
;清屏
MOV AH,6
MOV AL,0
MOV CH,0
MOV CL,0
MOV DH,26
MOV DL,79
MOV BH,7
INT 10H

MOV AX,    CS
MOV DS,    AX
MOV ES,    AX
MOV AX,    0x00
MOV SS,    AX
MOV SP,    0x7c00
;=======    display on screen : Start Loader......
MOV AX,    1301h
MOV BX,    000fh
MOV DX,    0200h        ;row 2
MOV CX,    12
PUSH AX
MOV AX,    DS
MOV ES,    AX
POP AX
MOV BP,    StartLoaderMessage
INT 10h

JMP    fin
fin:
HLT ;CPU停止,等待指令
JMP fin ;无限循环

;=======    display messages
StartLoaderMessage:    db    "Start Loader"
相关文章
|
6月前
|
Python
python如何使用os模块进行文件和目录操作?
python如何使用os模块进行文件和目录操作?
116 1
|
Unix Go
Golang 语言的标准库 os 包怎么操作目录和文件?
Golang 语言的标准库 os 包怎么操作目录和文件?
40 0
|
Linux Shell Go
《Linux操作系统编程》 第五章 文件和文件系统: 了解文件和文件系统的概念和特性,掌握Linux文件系统的基本操作
《Linux操作系统编程》 第五章 文件和文件系统: 了解文件和文件系统的概念和特性,掌握Linux文件系统的基本操作
117 0
|
26天前
|
存储 Java iOS开发
MacOS环境-手写操作系统-43-dir命令的实现 和 文件写入
MacOS环境-手写操作系统-43-dir命令的实现 和 文件写入
29 0
|
5月前
|
缓存 Linux Windows
初识Linux操作系统(根目录下的重要文件)(命令提示符的含义)
Linux系统基于"一切皆文件"的理念,重要文件分布在如/root(root用户目录)、/home(普通用户目录)、/etc(应用配置)、/dev(设备文件)、/boot(内核及启动文件)、/proc(动态系统信息)、/lib64(库文件)、/opt(软件存放)、/tmp(临时文件)。"[root@localhost ~]#"代表管理员在root目录,"$"代表普通用户。创建新用户用`useradd`命令。调节终端字体大小:Ctrl+Shift++增大,Ctrl+减号缩小。绝对路径从根目录开始,相对路径从当前目录开始。
|
5月前
|
Unix 关系型数据库 API
Python OS 文件/目录方法
Python OS 文件/目录方法
|
5月前
|
Java 开发工具 Android开发
详细解读Android开发DNK开发将.c文件打包成os
详细解读Android开发DNK开发将.c文件打包成os
30 0
|
5月前
|
消息中间件 Java Kafka
实时计算 Flink版操作报错合集之RocksDB在尝试打开更多文件时达到了操作系统允许的最大打开文件数限制,该怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
114 0
|
6月前
|
Ubuntu Linux iOS开发
LabVIEW在不同操作系统上使VI、可执行文件或安装程序
LabVIEW在不同操作系统上使VI、可执行文件或安装程序
187 3
|
6月前
|
Python
在Python中,利用`os模块`的`path.exists()`函数可判断文件是否存
【5月更文挑战第12天】在Python中,利用`os模块`的`path.exists()`函数可判断文件是否存在,该函数对路径进行检查,存在则返回True,不存在则返回False。示例代码展示了如何检查'example.txt'文件是否存在并相应打印消息。此外,`os.path.isfile()`用于确认路径是否为文件,仅当是文件时返回True,否则返回False,同样配以示例说明其用法。
220 2