写点什么

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

作者:
  • 2022-10-14
    四川
  • 本文字数:3804 字

    阅读完需:约 12 分钟

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

简介

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

新增软件说明

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


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

程序整体思路

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


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


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


大致的步骤如下:


  • 新 nasm 文件编写

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

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

1. 新 nasm 文件编写

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


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


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


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


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


;加载到一块无人用的地址ORG 10000hJMP print_load_info
print_load_info:;清屏MOV AH,6MOV AL,0MOV CH,0MOV CL,0MOV DH,26MOV DL,79MOV BH,7INT 10H
MOV AX, CSMOV DS, AXMOV ES, AXMOV AX, 0x00MOV SS, AXMOV SP, 0x7c00;======= display on screen : Start Loader......MOV AX, 1301hMOV BX, 000fhMOV DX, 0200h ;row 2MOV CX, 12PUSH AXMOV AX, DSMOV ES, AXPOP AXMOV BP, StartLoaderMessageINT 10h
JMP finfin:HLT ;CPU停止,等待指令JMP fin ;无限循环
;======= display messagesStartLoaderMessage: db "Start Loader"
复制代码

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

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


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


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


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


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


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


我们直接修改 fin 即可:


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

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

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


# !bashD:\\software\\NASM\\nasm.exe .\\myOS.asm -o .\\myOS.imgD:\\software\\NASM\\nasm.exe .\\loader.asm -o .\\loader.bincp myOS.img os.imgdd if=./loader.bin of=./os.img oflag=append conv=notruncD:\\software\\qemu\\qemu-system-x86_64.exe -L . -m 64 -fda .\\os.img
复制代码


上面的脚本功能是:


  • 将主文件 myOS.asm 进行编译

  • 将文件 loader.asm 进行编译

  • 将编译后的 myOS.img 复制一份

  • 使用 dd 命令,将 loader.bin 文件拼接到 os.img 文件后面

  • 最后运行即可


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


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




总结

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


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


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

本次完整代码

myOs.asm 文件


; cherry-osORG 0x7c00 ;指定程序装载的位置
;下面用于描述FAT12格式的软盘JMP entryDB 0x90DB "CHRRYIPL" ;启动区的名称可以是任意的字符串,但长度必须是8字节DW 512; 每一个扇区的大小,必须是512字节DB 1 ;簇的大小(必须为1个扇区)DW 1 ;FAT的起始位置(一般从第一个扇区开始)DB 2 ;FAT的个数 必须是2DW 224;根目录的大小 一般是224项DW 2880; 该磁盘的大小 必须是2880扇区DB 0xf0;磁盘的种类 必须是0xf0DW 9;FAT的长度 必须是9扇区DW 18;1个磁道(track) 有几个扇区 必须是18DW 2; 磁头个数 必须是2DD 0; 不使用分区,必须是0DD 2880; 重写一次磁盘大小DB 0,0,0x29 ;扩展引导标记 固定0x29DD 0xffffffff ;卷列序号DB "CHERRY-OS " ;磁盘的名称(11个字节)DB "FAT12 " ;磁盘的格式名称(8字节)TIMES 18 DB 0; 先空出18字节 这里与原文写法不同
OffSetOfLoader equ 0x0820
;程序核心entry:MOV AX,0 ;初始化寄存器MOV SS,AXMOV SP,0x7c00MOV DS,AXMOV ES,AXread_file_ready: ;读软盘准备MOV AH, 02 ;指明读扇区功能调用MOV AL, 1 ;指明要读的扇区数为1MOV DL, 0x00 ;指明要读的驱动为AMOV DH, 0 ;指定读取的磁头0MOV CH, 0 ;柱面0MOV CL, 2 ;原来我们是从扇区1开始读取的,现在改为2。因为扇区1是启动引导扇区,存放的就是我们的主文件,但不需要这个,所以我们直接从扇区2开始读取我们的新的文件到内存中;下面三句指定读取到内存地址0x0820处MOV AX, OffSetOfLoaderMOV ES, AXMOV BX, 0 ;数据读取后放到的内存地址read_file:;调试用,用于查看读取磁盘是否达到了循环次数,call相当于函数调用MOV SI, read_file_msgCALL func_show_msg
;前面调用函数时,改变了有关的寄存器,这里重置回来(也可以使用栈,但这个方便点)MOV AH, 02 ;指明读扇区功能调用MOV AL, 1 ;指明要读的扇区数为1MOV BX, 0 ;数据读取后放到的内存地址
INT 13H ;调用BIOS文件读取JNC read_file_loopMOV SI, read_file_error_msgJMP show_msg_inforead_file_loop: ;循环读取内容,循环的顺序是扇区->磁头->柱面,不知道这里面是否有什么说法?可以调换吗?;把内存地址后移0x200MOV AX, ESADD AX, 0x0020MOV ES, AXADD CL, 1 ;加1,读取下一个扇区CMP CL, 18 ;如果CL扇区大于软盘总扇区数18,则说明读取完成,不再读取JBE read_file; 上面是扇区的循环,完毕后到磁头的循环,重置扇区,增加磁头到反面1MOV CL, 1ADD DH, 1CMP DH, 2JB read_file;上面都完成后,到柱面的读取循环,重置扇区(前面已重置)和磁头,增加柱面(不知道为啥书中不是80而是10)MOV DH, 0ADD CH, 1CMP CH, 19JB read_fileshow_mem_file: ;打印显示刚才加载的文件内容MOV AX, OffSetOfLoader; MOV AX, 0x0a20MOV ES, AXMOV DI, 0show_mem_byte: ;整个数据也就512,所以循环512次即可MOV AL, BYTE [ES:DI]CMP DI, 512JE loader_endADD DI, 1MOV AH,0x0e ;显示一个文字MOV BX,15 ;指定字符的颜色INT 0x10 ;调用显卡BIOSJMP show_mem_byteloader_end: ;启动程序加载完成MOV AL,0x0aMOV AH,0x0e ;显示一个文字MOV BX,15 ;指定字符的颜色INT 0x10 ;调用显卡BIOSMOV SI,msgshow_msg_info: ;加载完成,成功显示MOV AL,[SI]ADD SI,1CMP AL,0JE finMOV AH,0x0e ;显示一个文字MOV BX,15 ;指定字符的颜色INT 0x10 ;调用显卡BIOSJMP show_msg_infofin:; HLT ;CPU停止,等待指令; JMP fin ;无限循环
MOV SI, jmp_msgCALL func_show_msg;下面三句指定读取到内存地址0x0820处JMP OffSetOfLoader:0func_show_msg:MOV AL,[SI]ADD SI,1CMP AL,0JE func_retMOV AH,0x0e ;显示一个文字MOV BX,15 ;指定字符的颜色INT 0x10 ;调用显卡BIOSJMP func_show_msgfunc_ret:RETread_file_msg:DB "r"DB 0read_file_error_msg:DB 0x0a , 0x0a ;换行两次DB "read file error!!!"DB 0x0aDB 0show_mem_info_msg:DB 0x0a , 0x0a ;换行两次DB "start show mem file!!!"DB 0x0aDB 0msg:DB 0x0a , 0x0a ;换行两次DB "hello, my OS, boot loader end"DB 0x0aDB 0jmp_msg:DB 0x0a ;换行两次DB "start jmp loader bin"DB 0boot_flag: ;启动区标识TIMES 0x1fe-($-$$) DB 0 ;填写0x00,直到0x001feDB 0x55, 0xaa
复制代码


loader.asm 文件


;加载到一块无人用的地址ORG 10000hJMP print_load_info
print_load_info:;清屏MOV AH,6MOV AL,0MOV CH,0MOV CL,0MOV DH,26MOV DL,79MOV BH,7INT 10H
MOV AX, CSMOV DS, AXMOV ES, AXMOV AX, 0x00MOV SS, AXMOV SP, 0x7c00;======= display on screen : Start Loader......MOV AX, 1301hMOV BX, 000fhMOV DX, 0200h ;row 2MOV CX, 12PUSH AXMOV AX, DSMOV ES, AXPOP AXMOV BP, StartLoaderMessageINT 10h
JMP finfin:HLT ;CPU停止,等待指令JMP fin ;无限循环
;======= display messagesStartLoaderMessage: db "Start Loader"
复制代码


发布于: 刚刚阅读数: 4
用户头像

关注

还未添加个人签名 2018-09-09 加入

代码是门手艺活,也是门艺术活

评论

发布
暂无评论
自制操作系统系列(三):加载其他文件执行_操作系统_萧_InfoQ写作社区