自制操作系统日记(二):软盘读取

简介: 在上一篇中,我们使用汇编编写了一个直接显示hello的程序,接下来我们继续探索如果使用汇编读取软盘数据

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

简介

在上一篇中,我们使用汇编编写了一个直接显示hello的程序,接下来我们继续探索如果使用汇编读取软盘数据

软盘数据读取准备

在上一篇中,我们使用nasm将程序制作成了img文件,在尝试中,这个就可以视为软盘,使用软盘的读取方式能读取我们这个img文件

文件读取的功能不用自己实现,在BIOS函数中(可以视为系统级的函数库,里面提供了一些通用的函数)有相关的文件读取功能:BIOS INT 13H

其相关的说明如下:

① 13H号功能调用02H号子功能:
读扇区入口参数:

  • AH=02,指明读扇区功能调用
  • AL要读扇区数
  • DL驱动器代号,0和1代表软盘,80H和81H代表硬盘
  • DH所读磁盘磁头号,以软盘来说,只能是0和1
  • CH 10位磁道号的低8位,
  • CL寄存器的第6、第7位存放其高2位CL低5位为要读的第一个扇区的扇区号(注意扇区号从1开始而非从0开始)。高2位表示磁道柱面号的高2位ES:
  • BX指出存放从磁盘所读数据的内存地址出口参数:
  • 读出数据放在ES:BX所指的内存区域中若产生错误,CF置1,AH内为错误代码

更多的细节可以参考:《汇编语言程序设计》第三版的10.2.2和10.3章节,微信读书上有

还需要了解软盘的相关知识,在《30天自制操作系统》中,我们看到其对软盘的描述,如下图:

image.png

我们需要循环读取磁头,柱面,扇区,才能把整个磁盘的数据进行读入

根据两本参考书和相关的资料,我们先把软盘数据读取功能实现,博主能力问题,没有办法步子迈太大

程序整体思路

程序实现的大致思路如下:

  • 1.软盘读取相关的参数定义,也就是:AH、AL、DL、DH、CH、BX、ES
  • 2.循环磁头、柱面、扇区,读取整个软盘数据到内存中
  • 3.为了验证,我们读取内存中的内容,并打印到屏幕上

整体思路有了,我们开始看代码细节

程序细节实现

1.软盘读取的相关的参数定义

我们把软盘读取函数的相关的参数先给定义好,具体的说明如下,代码中已加入相关的注释:

; 定义数据存放的内存开始地址
OffSetOfLoader    equ    0x0820
read_file_ready: ;读软盘准备
    MOV AH, 02 ;指明读扇区功能调用
    MOV AL, 1 ;指明要读的扇区数为1
    MOV DL, 0x00 ;指明要读的驱动为A
    MOV DH, 0 ;指定读取的磁头0
    MOV CH, 0 ;柱面0
    MOV CL, 1 ;读取扇区1(因为目前没有其他文件,就只有扇区1的启动区数据,所以我们这从1开始,不是从2开始)
    ;下面三句指定读取到内存地址0x0820处
    MOV AX, OffSetOfLoader
    MOV ES, AX
    MOV BX, 0 ;数据读取后放到的内存地址

需要注意的是CL,扇区的值。在书中是2,跳过了启动区扇区1的数据,我们这里没有其他的文件,就只有启动区的数据,所以我们从扇区1开始进行加载

2.循环读取软盘数据

我们需要循环扇区、磁头、柱面就行数据的读取,代码如下:

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, 10
    JB read_file
; 相关的函数定义
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

在编写的过程中,问题频发,所以我们这里对循环进行了验证,每次调用BIOS的软盘读取函数,我们就打印一个字符:r

这里我们读取也没有采用书中的重试五次的策略,直接一次到位,读取错误就显示错误即可,感觉方便一点,当然,你们也可以试试加上重试策略

接下来的循环部分,都是参数的书中的代码了,这里就不多说了

3.读取加载到内存中的文件内容,并进行显示

为了验证我们程序的正确性,我们读取内容中的数据,打印到屏幕上,看看是否正确

软盘数据读取到的内容位置是开头我们定义的位置:OffSetOfLoader

而我们的大小就是512,所以我们直接循环512次即可,代码如下:

show_mem_file: ;打印显示刚才加载的文件内容
    MOV AX, OffSetOfLoader
    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
···

这样我们就基本完成,运行看下效果:

; 编译
PS E:\code\other\self\operating-system> D:\software\NASM\nasm.exe .\myOS.asm -o .\myOS.img

:运行
PS E:\code\other\self\operating-system> D:\software\qemu\qemu-system-x86_64.exe -L . -m 64 -fda .\myOS.img
WARNING: Image format was not specified for '.\myOS.img' and probing guessed raw.

     Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
     Specify the 'raw' format explicitly to remove the restrictions.

(qemu:19348): Gtk-WARNING **: 09:01:44.555: Could not load a pixbuf from icon theme.
This may indicate that pixbuf loaders or the mime database could not be found.


结果如下图:

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4dd36fb7dd9a4318b44c64ed6c2daae3~tplv-k3u1fbpfcp-watermark.image?)

## 完整代码
所有的代码如下:

; 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, 1 ;读取扇区1(因为目前没有其他文件,就只有扇区1的启动区数据,所以我们这从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, 10
JB read_file

show_mem_file: ;打印显示刚才加载的文件内容

MOV AX, OffSetOfLoader
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 ;无限循环

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

boot_flag: ;启动区标识

    TIMES 0x1fe-($-$$) DB 0 ;填写0x00,直到0x001fe
    DB 0x55, 0xaa

## 总结
看书感觉挺简单,动手起来困难重重啊。虽然完全使用书中的功能和内容可以跑,但自己动手改造和使用其他工具实现起来就有各种问题,但又不能不去做,就像抄作业,学渣就只抄,原理搞不懂,学霸抄了还理解了原理,题目一变考试也不慌。

这几天真是额头发烫,还好各种翻书和查阅资料,把这部分按照自己的理解给完成了

可能各位在实现的过程也会遇到其他的问题,可以翻翻下面三本书(微信读书上都有)和查阅资料:

- [《三十天自制操作系统》](https://github.com/yourtion/30dayMakeOS)
- 《汇编程序语言设计》
- [《一个64位操作系统的设计与实现》](https://github.com/yifengyou/The-design-and-implementation-of-a-64-bit-os)

上面两本书的相关代码也可以去看看,参考参考,链接点击书名即可

博主的相关实现也可以参考,但没有像他们一样根据天数进行划分,只能回退相关的代码版本查看

- [https://github.com/freedom-xiao007/operating-system](https://github.com/freedom-xiao007/operating-system)

## 参考链接
- [Windows/Linux 制作ISO文件](https://blog.51cto.com/u_8149087/2501212)
- [Can NASM generate a file with machine code hexdump + asm source?](https://stackoverflow.com/questions/44841381/can-nasm-generate-a-file-with-machine-code-hexdump-asm-source)
- [汇编语言指令大全 X86和X87汇编指令大全(带注释)](https://www.jb51.net/article/224613.htm)
- [「30天制作操作系统系列」1~4天从汇编到C语言](https://zhuanlan.zhihu.com/p/372748604)
相关文章
|
26天前
|
存储 Java iOS开发
MacOS环境-手写操作系统-02-读写软盘
MacOS环境-手写操作系统-02-读写软盘
16 3
|
JavaScript Linux
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——2.12 初始化软盘
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第2章,第2.12节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1114 0
|
19天前
|
Unix 物联网 大数据
操作系统的演化与比较:从Unix到Linux
本文将探讨操作系统的历史发展,重点关注Unix和Linux两个主要的操作系统分支。通过分析它们的起源、设计哲学、技术特点以及在现代计算中的影响,我们可以更好地理解操作系统在计算机科学中的核心地位及其未来发展趋势。
|
4月前
|
安全 Linux 网络安全
部署07--远程连接Linux系统,利用FinalShell可以远程连接到我们的操作系统上
部署07--远程连接Linux系统,利用FinalShell可以远程连接到我们的操作系统上
|
4月前
|
Linux 虚拟化 数据安全/隐私保护
部署05-VMwareWorkstation中安装CentOS7 Linux操作系统, VMware部署CentOS系统第一步,下载Linux系统,/不要忘, CentOS -7-x86_64-DVD
部署05-VMwareWorkstation中安装CentOS7 Linux操作系统, VMware部署CentOS系统第一步,下载Linux系统,/不要忘, CentOS -7-x86_64-DVD
|
3月前
|
编解码 安全 Linux
基于arm64架构国产操作系统|Linux下的RTMP|RTSP低延时直播播放器开发探究
这段内容讲述了国产操作系统背景下,大牛直播SDK针对国产操作系统与Linux平台发布的RTMP/RTSP直播播放SDK。此SDK支持arm64架构,基于X协议输出视频,采用PulseAudio和Alsa Lib处理音频,具备实时静音、快照、缓冲时间设定等功能,并支持H.265编码格式。此外,提供了示例代码展示如何实现多实例播放器的创建与管理,包括窗口布局调整、事件监听、视频分辨率变化和实时快照回调等关键功能。这一技术实现有助于提高直播服务的稳定性和响应速度,适应国产操作系统在各行业中的应用需求。
|
4月前
|
Linux 测试技术 数据安全/隐私保护
阿里云Linux os copilot 运维助手初体验
阿里云Linux os copilot 初体验
403 1
阿里云Linux os copilot 运维助手初体验
|
4月前
|
弹性计算 运维 自然语言处理
阿里云OS Copilot测评:重塑Linux运维与开发体验的智能革命
阿里云OS Copilot巧妙地将大语言模型的自然语言处理能力与操作系统团队的深厚经验相结合,支持自然语言问答、辅助命令执行等功能,为Linux用户带来了前所未有的智能运维与开发体验。
|
4月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
166 1
|
4月前
|
缓存 监控 关系型数据库
深入理解Linux操作系统的内存管理机制
【7月更文挑战第11天】在数字时代的浪潮中,Linux操作系统凭借其强大的功能和灵活性,成为了服务器、云计算以及嵌入式系统等领域的首选平台。内存管理作为操作系统的核心组成部分,对于系统的性能和稳定性有着至关重要的影响。本文将深入探讨Linux内存管理的基本原理、关键技术以及性能优化策略,旨在为读者提供一个全面而深入的理解视角,帮助开发者和系统管理员更好地优化和管理Linux系统。