加载用户程序的监控程序 晓风の个人博客

实验题目

加载用户程序的监控程序

实验目的

  • 掌握常用的 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

实验原理

与前一个实验(「裸机控制权与引导程序」)差不多。

  1. 虚拟软盘的第一个扇区用于存储主引导程序(即监控程序),需要保证最后两个字节为55aa。验证主引导程序有效后,跳转到7c00h开始执行。
  2. 引导程序接受键盘输入,选择程序,随后从软盘加载指定程序扇区的内容到内存并跳转执行。
  3. 中断标号0x000x13已经具有功能,0x140x1f为保留标号,0x200xff提供给用户自定义中断。因此定义0x20h号中断用于从用户程序返回监控程序。
  4. 在用户程序的循环中需要加入软件中断int 20h,用于监测是否有键盘返回,如果有则返回监控程序,否则继续执行。

实验过程

a.asm

本次实验要求写 4 个用户程序,分别是在屏幕的 4 个象限中弹来弹去。四个代码的程序几乎相同,只需要修改某些常量和变量的定义即可。这里只给出其中一个程序的代码。

另外,本想直接用前一个实验的c.asm,却发现老师只考虑了边反射的情况,没有考虑四个角反射的情况,这会导致显示的内容从角飞出,在屏幕中乱飞。于是再次重构这段代码,使得编译后大小仅 137bytes:

  • 加载地址修改成org 0A100h,表明用户程序读入内存应从内存物理地址 0A100h 开始。
  • 重新修改横纵坐标的变化方式,改为根据运行周期自动变化。
  • 修改字符串显示方式,改为调用int 10h实现,并写了一个%macro print来简化这一过程(造轮子),仍然支持根据行号自动变色。
  • 屏幕显示空出前两行,用于显示引导程序的提示信息。
%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(黑底白字)即可。

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 终端下按顺序执行下述指令。主引导程序存储在虚拟软盘的第一个扇区,第一个用户程序存储在第二个扇区,第二个用户程序存储在第三个扇区,以此类推。

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等语句有了更深的理解了。