汇编语言实现监控程序
1 介绍监控程序
监控程序说白了就是操作系统。对于特别简单的操作系统而言监控程序就是等待用户的命令然后执行。
这里介绍的监控程序可以运行放在软盘中的用户程序。可以接收ls指令,输出用户程序的信息。基本上什么功能也没有……
所有源代码软盘,提取码:pn49
用户程序的介绍请看:x86入门-接管裸机控制权
2 软件支持
操作系统:Ubuntu 20.04 LTS
虚拟机软件:VirtualBox 6.1.6(VB这个程序有bug,推荐直接使用bochs,目前监控程序还没有触发VB的bug)
Debug软件:bochs x86 Simulator 2.6.11
编译器:nasm 2.14.02
编译器:gcc 9.3.0
3 运行监控程序
传统惯例先上可运行的程序,不然讲一大堆什么效果都没看到。
进入项目目录,然后make生成需要的文件,使用make build制作软盘,然后make run用bochs运行写好的程序。
$ make # 生成所有的二进制文件
$ make build # 使用自己写的程序制作软盘
$ make run # 运行bochs虚拟机
$ make clean # 清除所有的二进制文件,不包括制作好的软盘
对于熟悉Makefile的小伙伴直接看makefile就能理解编译的过程,如果要直接在命令行中编译使用下面的指令。
首先生成所有程序的纯二进制代码,对应make指令
# 生成所有用户程序的二进制文件
$ nasm -f bin soft0.asm -o soft0.img
$ nasm -f bin soft1.asm -o soft1.img
$ nasm -f bin soft2.asm -o soft2.img
$ nasm -f bin soft3.asm -o soft3.img
# 生成监控程序的二进制文件,直接当作软盘使用
$ nasm -f bin load.asm -o flop.img
# 生成用户程序信息表
$ nasm -f bin table.asm -o table.img
# 我编写的制作软盘的C程序
$ gcc -o make_flop write.c
然后使用make_flop将用户程序、用户程序信息写入存有监控程序的软盘,对应make build。
./make_flop
最后直接用bochs运行这个软盘就行了,对应于make run。
bochs
bochs的配置文件bochsrc如下
floppya: 1_44="flop.img", status=inserted #Use FLoppy Disk A
boot: floppy
由于没有实现输入后回显的功能,因此你也看不到我输入了什么,实际上我输入的五个字符依次为s、0、1、2、3 ,不用回车就可以执行命令。
4 监控程序解释
监控程序分为 个模块:输入、运行用户程序、读入软盘、显示用户信息表、清屏
先介绍一下最简单的清屏,除掉压栈等操作只用用两行汇编。我开始的时候真的脑子有病,自己手动写汇编代码清空了整个显示区域。
clean:
push ax;
mov ax, 03h; 功能号
int 10h
pop ax;
ret;
接下来是第二简单的输入和判断输入的过程。基本上就是一个while循环加几个if判断就完了,如果输入的字符是s那么就跳转到显示用户程序的信息表执行,否则就执行调用用户程序的函数,结束之后会返回input接收用户下一个指令。
input:
; 在这里开始从键盘中读取输入,判断到底运行哪一个程序
mov ah, 0; 从键盘中读取一个字符,储存在al
int 16h;
mov ah, 0;
; 如果输入的字符为s,那么显示软盘中的程序信息
cmp al, 's'
je show_table
mov ah, 0;
sub al, '0';
call run;
jmp input
这里主要说一下执行用户程序的过程。这个函数接收一个参数ax,表示用户程序的编号,从0到3(我的参数都是用寄存器传递的因为方便,后来的C和汇编的混合编程就必须压栈了)。
首先每次执行前都会清屏(调用clean)。
MBR监控程序储存在逻辑扇区0,用户程序的信息表储存在逻辑扇区1,用户程序0储存在逻辑扇区2,用户程序1储存在逻辑扇区3,以此类推。因此监控程序先把用户程序0调入到内存的0x7e00处,用户程序1就放在0x8000处,以此类推。(读入软盘的操作有bug,超过逻辑扇区17就读不了了,我之后解决了这个问题,这里就别看了)
; 将程序从软盘中读取出来
add ax, 2;第0个程序在第2个逻辑扇区
call load; 首先使用load将程序调入内存
sub ax, 2; 重新用ax表示第0个程序
然后将用户程序对应的物理地址赋值给ax寄存器,比如用户程序0就是0x7e00。然后直接jmp bx,注意跳转之前,要将返回地址赋值给bx,用户程序也会直接jmp bx返回。整个程序的cs段寄存器都为0,因此所有的跳转都是使用物理地址跳转的。
; 下面计算得到偏移地址: (cx:)ax = ax * 0x200(bx)
mov bx, 0x200;
mul bx;
add ax, 0x8000;
; mov dx, ax;
; 开始传递参数,并且跳转到程序
mov cx, 0;
mov bx, end;
add bx, 0x7c00
jmp ax; 这个相对偏移是cs: ax
跳转完成之后就没监控程序什么事了,用户程序就是上次实验“接管裸机控制权”的用户程序的修改版。执行结束后就jmp bx,然后显示的范围不再是全屏,而是1/4个屏幕。
显示用户程序信息表这个功能到了C内核中会重写,加上之前FAT12文件系统的仿真程序dir指令也是显示这信息,因此这里的具体实现没有太大意义。只说一下显示出来的信息的含义。
在下面的那张图上显示4个用户程序的信息,每一行显示一个用户程序。以第一行为例:
第一个数字表示用户程序的编号:0
第二个数字表示用户程序的首扇区(逻辑扇区):2
第三个数字表示用户程序占有的扇区个数:1
题外话
没想到还有人蹲博客等我的文章更新,只能说你们抄作业真的辛苦……那我以后尽量早点更新文章,争取在大家补交实验之前更新。
目前的源代码和各种二进制文件都放在了百度网盘,毕竟文件特别小下载很快,之后考虑放在github。
上一篇: 那些年实现的炫酷自定义控件库