CC BY 4.0 (除特别声明或转载文章外)
如果这篇博客帮助到你,可以请我喝一杯咖啡~
实验题目
加载用户程序的监控程序
实验目的
- 掌握常用的 BIOS 调用
- 掌握 BIOS 编程
- 掌握加载用户程序的方法
实验要求
- 设计四个有输出的用户可执行程序,分别在屏幕 1/4 区域动态输出字符,如将用字符‘A’从屏幕左边某行位置 45 度角下斜射出,保持一个可观察的适当速度直线运动,碰到屏幕相应 1/4 区域的边后产生反射,改变方向运动,如此类推,不断运动;在此基础上,增加你的个性扩展,如同时控制两个运动的轨迹,或炫酷动态变色,个性画面,如此等等,自由不限。还要在屏幕某个区域特别的方式显示你的学号姓名等个人信息。
- 修改参考原型代码,允许键盘输入,用于指定运行这四个有输出的用户可执行程序之一,要确保系统执行代码不超过 512 字节,以便放在引导扇区
- 自行组织映像盘的空间存放四个用户可执行程序。
实验方案
实验环境
软件
- Windows 10, 64-bit (Build 17763) 10.0.17763
- Windows Subsystem for Linux [Ubuntu 18.04.2 LTS]:WSL 是以软件的形式运行在 Windows 下的 Linux 子系统,是近些年微软推出来的新工具,可以在 Windows 系统上原生运行 Linux。
- gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04):C 语言程序编译器,Ubuntu 自带。
- NASM version 2.13.02:汇编程序编译器,通过
sudo apt install nasm
安装在 WSL 上。 - Oracle VM VirtualBox 6.0.4 r128413 (Qt5.6.2):轻量开源的虚拟机软件。
- VSCode - Insiders v1.33.0:好用的文本编辑器,有丰富的插件。
- hexdump for VSCode 1.7.2: VSCode 中一个好用的十六进制显示插件。
大部分开发环境安装在 WSL 上,较之于双系统、虚拟机等其他开发方案,更加方便,也方便直接使用 Linux 下的一些指令。
硬件
开发环境配置
所用机器型号为 VAIO Z Flip 2016
- Intel(R) Core(TM) i7-6567U CPU @3.30GHZ 3.31GHz
- 8.00GB RAM
虚拟机配置
- 处理器内核总数:1
- RAM:4MB
实验原理
与前一个实验(「裸机控制权与引导程序」)差不多。
- 虚拟软盘的第一个扇区用于存储主引导程序(即监控程序),需要保证最后两个字节为
55aa
。验证主引导程序有效后,跳转到7c00h
开始执行。 - 引导程序接受键盘输入,选择程序,随后从软盘加载指定程序扇区的内容到内存并跳转执行。
- 中断标号
0x00
到0x13
已经具有功能,0x14
到0x1f
为保留标号,0x20
到0xff
提供给用户自定义中断。因此定义0x20h
号中断用于从用户程序返回监控程序。 - 在用户程序的循环中需要加入软件中断
int 20h
,用于监测是否有键盘返回,如果有则返回监控程序,否则继续执行。
实验过程
a.asm
本次实验要求写 4 个用户程序,分别是在屏幕的 4 个象限中弹来弹去。四个代码的程序几乎相同,只需要修改某些常量和变量的定义即可。这里只给出其中一个程序的代码。
另外,本想直接用前一个实验的c.asm
,却发现老师只考虑了边反射的情况,没有考虑四个角反射的情况,这会导致显示的内容从角飞出,在屏幕中乱飞。于是再次重构这段代码,使得编译后大小仅 137bytes:
- 加载地址修改成
org 0A100h
,表明用户程序读入内存应从内存物理地址 0A100h 开始。 - 重新修改横纵坐标的变化方式,改为根据运行周期自动变化。
- 修改字符串显示方式,改为调用
int 10h
实现,并写了一个%macro print
来简化这一过程(造轮子),仍然支持根据行号自动变色。 - 屏幕显示空出前两行,用于显示引导程序的提示信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
%macro print 4 ; string, length, x, y
mov ax, cs
mov ds, ax
mov bp, %1
mov ax, ds
mov es, ax
mov cx, %2
mov ah, 13h
mov al, 00h
mov bh, 00h
mov bl, %3 ; 根据行号自动变色
mov dh, %3
mov dl, %4
int 10h
%endmacro
N equ 12 ;显示区域高度
M equ 32 ;显示区域宽度减去字符串长度
TOP equ 2 ;显示区域上端点
LEFT equ 40 ;显示区域左端点
LENGTH equ 8 ;字符串的长度
DELAY equ 99999999
org 0A100h
mov ax,0B800h
mov gs,ax ; GS = 0xB800h,文本窗口显存起始地址
myLoop:
dec dword[count] ; 递减计数变量
jnz myLoop ; >0:跳转
mov dword[count], DELAY
mov word ax, [t] ; ax = t
mov word bx,2*N-2
xor dx, dx ; clear dx and prepare for division
div bx ; dx = t mod (2N - 2)
cmp dx, N ; compare dx and n
jb xok ; if (dx < n) jump xok
sub bx, dx
mov dx, bx ; dx = 2n - 2 - dx
xok:
mov word[x], dx
add word[x], TOP
mov word ax, [t]
mov word bx, 2*M-2
xor dx, dx
div bx
cmp dx, M
jb yok
sub bx, dx
mov dx, bx
yok:
mov word [y],dx
add word [y],LEFT
inc word[t]
print message, LENGTH, [x], [y]
int 20h
jmp myLoop
datadef:
count dd 1
t dw 0
x dw 1
y dw 0
message db ' wu-kan '
wukos.asm
引导扇区的 bootloader,其功能有:
- 显示正在运行的是哪一个程序,或是选择页面提示语。
- 使用
int 13h
读取扇区,并把它放到内存合适的位置上。 - 使用
int 16h
读取键盘输入,用于选择程序。
这里遇到一个问题,就是直接使用上面自动变色的程序时没有显示。因前两行的 x 坐标恰好对应了颜色中的黑底黑字。因此修改显示颜色mov bl, 07h
(黑底白字)即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
OffSetOfUserPrg equ 0A100h
org 7c00h
%macro print 4 ; string, length, x, y
mov ax, cs
mov ds, ax
mov bp, %1
mov ax, ds
mov es, ax
mov cx, %2
mov ah, 13h
mov al, 00h
mov bh, 00h
mov bl, 07h ; 黑底白字
mov dh, %3
mov dl, %4
int 10h
%endmacro
mov ax, 0000h
mov es, ax
mov ax, 20h
mov bx, 4
mul bx
mov si, ax
mov ax, _int20h
mov [es:si], ax
add si, 2
mov ax, cs
mov [es:si], ax
begin:
call cls
print msg, msglen, 0, 0
input:
mov ah, 0
int 16h
cmp al, '1'
jl input
cmp al, '4'
jg input
mov [sectorNum], al
call cls
print msg1, msglen1, 0, 0
print sectorNum, 1, 0, 16
mov cl, [sectorNum]
sub cl, '0'-1 ;从第二个扇区开始
mov ax, cs
mov es, ax
mov ah, 2
mov al, 1
mov dl, 0
mov dh, 0
mov ch, 0
mov bx, OffSetOfUserPrg
int 13H
jmp OffSetOfUserPrg
cls:
mov ax, 0B800h
mov es, ax
mov si, 0
mov cx, 80*25
mov dx, 0
clsLoop:
mov [es:si], dx
add si, 2
loop clsLoop
ret
int20h:
mov ah, 01h
int 16h
jz noclick
mov ah, 00h
int 16h
cmp ax, 2e03h ; 检测Ctrl + C
jne noclick
jmp begin
noclick:
iret
datadef:
msg db 'Welcome to WuKOS, press 1~4 to run a program.'
msglen equ ($-msg)
msg1 db 'This is program 0, press Ctrl + C to return.'
msglen1 equ ($-msg1)
sectorNum db '1'
编译烧盘
在 WSL 终端下按顺序执行下述指令。主引导程序存储在虚拟软盘的第一个扇区,第一个用户程序存储在第二个扇区,第二个用户程序存储在第三个扇区,以此类推。
1
2
3
4
5
6
7
8
9
10
11
nasm wukos.asm -o wukos.com
nasm a.asm -o a.com
nasm b.asm -o b.com
nasm c.asm -o c.com
nasm d.asm -o d.com
/sbin/mkfs.msdos -C wukos.img 1440
dd if=wukos.com of=wukos.img conv=notrunc
dd if=a.com of=wukos.img seek=1 conv=notrunc
dd if=b.com of=wukos.img seek=2 conv=notrunc
dd if=c.com of=wukos.img seek=3 conv=notrunc
dd if=d.com of=wukos.img seek=4 conv=notrunc
运行结果
引导界面
程序 1
显示姓名。
程序 2
显示学号。
程序 3
显示邮箱。
程序 4
显示博客。
实验总结
重写了之前的代码,没有想到本来上次已经很小的 290bytes 还能压到更小的 137bytes…
还遇到了根据行号变色时第一行看不到输出的原因,仔细思考和查阅网上资料后发现 00h 用于表示不显示,在自动变色行号为 0 的时候就会导致看不到输出。
现在对org 7c00h
等语句有了更深的理解了。