0%

SYSU-2024操作系统lab2实验

本科生实验报告

实验课程: 操作系统

实验名称: Lab2 实验入门

1. 实验要求

任务1:

1.1

根据Example1教程,复现Example 1。

1.2

修改Example 1的代码,使得MBR被加载到0x7C00后在(12,12)处开始输出你的学号。注意,学号显示的前景色和背景色必须和教程中不同。

任务2:

2.1

请探索实模式下的光标中断int 10h, 实现将光标移动至(8,8),获取并输出光标的位置 。说说你是怎么做的,并将结果截图。

2.2

利用实模式下的中断, (8,8)开始输出你的学号 。说说你是怎么做的,并将结果截图。

2.3

参考《汇编语言》第17章、键盘I/O中断调用。关于键盘扫描码,可以参考键盘扫描码表

在2.1和2.2的知识的基础上,探索实模式下的键盘中断int 16h利用键盘中断,实现任意键盘输入并回显的效果 。说说你是怎么做的,并将结果截图。

任务3:

· 任务3的寄存器请使用32位的寄存器。

· 首先执行命令sudo apt install gcc-multilib g++-multilib安装相应环境。

· 你需要补全的代码文件在assignment/student.asm中。

· 编写好代码后,在目录assignment下使用命令make run即可测试代码,不需要放到mbr中使用qemu启动。

· a1if_flagmy_random等都是预先定义好的变量和函数,直接使用即可。

· 你可以修改test.cpp中的student_setting中的语句来得到你想要的a1,a2

· 最后附上make run的结果截图,并说说你是怎么做的。

即用汇编实现分支逻辑,循环逻辑和函数

任务4:

字符弹射程序。请编写一个字符弹射程序,其从点(2,0)
处开始向右下角45度开始射出,遇到边界反弹,反弹后按45度角射出,方向视反弹位置而定。同时,你可以加入一些其他效果,如变色,双向射出等。注意,你的程序应该不超过510字节,否则无法放入MBR中被加载执行。

2. 实验过程

任务1:

根据lab2中的提示,处理器将显示矩阵映射到内存地址0xB8000~0xBFFFF处,即显示矩阵的点(x,y)对应的显存起始位置=0xB8000+2⋅(80⋅x+y) 我们初始化段寄存器为0xb800在al中放入要输出的字符,ah中的值表示前景色和背景色属性。然后把ax的值移到对应的显存位置进行输出。

在写完程序后,在终端中输入如下指令:

1
2
3
4
5
6
7
nasm -f bin mbr.asm -o mbr.bin

qemu-img create hd.img 10m

dd if=mbr.bin of=hd.img bs=512 count=1 seek=0 conv=notrunc

qemu-system-i386 -hda hd.img -serial null -parallel stdio

观察结果

修改前景色和背景色,使用mov ah, 0b01110100;红色字白色背景

任务2:

2.1

在dh中放行号,dl中放列号,调用int 10h ah=2设置光标位置,将其输出

2.2

按照2.1中的方法设置光标位置,然后在光标处按照assignment1中的方法输出

1
2
3
4
5
6
7
8
9
mov ah,2    ;功能号

mov bh,0 ;第0页

mov dh,8 ;dh中放行号

mov dl,8 ;dl中放列号

int 10h
2.3

了解int 0x16功能,按键操作(即键盘缓冲区不空),则ZF=0,AL中存放的是输入的ASCII码,AH中存放输入字符的扩展码。若无键按下,则标志位ZF=1

如果有键按下则使用ah = 2,int 0x10设置光标位置,在当前位置调用 ah = 9,int 0x10在当前位置写字符

任务3.编写分支,循环和函数

按照题目要求实现即可有一些要注意的地方比如

字符串每个元素使char类型,比较的时候要取的是一个byte进行比较

比如在函数中的循环中有pushad要先popad再进行放在通用寄存器中的循环遍量的自增。

然后makerun运行

任务4:

根据字符弹射的规则,设置了右上右下左上左下四个函数分别用于移动,已经8个函数用于这四个状态之间的转换,print函数用于输出,每次输出后跳转到get_random函数,该函数产生的随机数用于字符和字符的前景色背景色,这个函数的实现参考了网上的代码

3. 关键代码

实验3的代码

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
%include "head.include"
your_if:
mov eax, [a1]
cmp eax, 12
jl less_than_12;左边小则跳转
; Check if a1 < 24
cmp eax, 24
jl less_than_24
;if a1 >24
;if_flag = a1 << 4;右移4位不是乘4shr后面是4不是2
shl eax,4; left move 4bits
mov [if_flag],eax
jmp your_while
less_than_12:
;if_flag = a1 / 2 + 1
sar eax,1
add eax,1
mov [if_flag],eax
jmp your_while
less_than_24:
;if_flag = (24 - a1) * a1
mov ecx,24
sub ecx,eax;OPRD1<-- OPRD1-OPRD2
imul eax,ecx
mov [if_flag],eax
jmp your_while

your_while:

mov ebx,[a2]
;while a2 >= 12 then
;call my_random // my_random将产生一个随机数放到eax中返回
cmp ebx,12
jl end_while
call my_random;jae
;while_flag[a2 - 12] = eax
mov ecx, ebx;不要搞错mov的顺序
sub ecx,12
mov edx,[while_flag]
add edx,ecx
mov byte[edx],al
sub ebx,1 ;--a2
mov [a2],ebx
jmp your_while

your_function:
mov eax, 0
mov edx, [your_string]
mov ebx, 0
cmp [edx], ebx
je endFunction
end_while:
%include "end.include"
forLoop:
pushad
mov edx, [edx + eax]
push edx
call print_a_char
pop edx
popad
inc eax

;; mov ecx, [edx]

cmp byte [edx + eax], 0
jne forLoop
endFunction:

实验四的关键代码:

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
next_position:
    dec word[count]
    cmp word[count], 0
    jne next_position;不为0时count递减
    mov word[count],delay
    call my_random
    cmp byte[status],1
    je DnRt;跳转到往右下走的函数
    cmp byte[status],2
    je UpRt
    cmp byte[status],3
    je DnLt
    cmp byte[status],4
    je UpLt

my_random:;产生随机数
    rdtsc;获取时钟计数器值,高位放edx,低位放eax
    mov ecx, 10
    idiv ecx;ecx/IDIV余数和被除数符号相同.被除数为edx eax,为余数放edx
    mov ebx, edx
    add ebx, 48
    rdtsc
    mov ecx, 256
    idiv ecx
    mov eax,ebx
    mov ebx,edx
    ret
DnRt:;右下
    inc word[x]
    inc word[y]
    cmp word[x],25
    je dr_to_ur
    cmp word[y],80
    je dr_to_dl
jmp print
dr_to_ur:;右下转右上的函数
    mov word[x], 24
    dec word[y]
    mov byte[status], 2
    jmp next_position
dr_to_dl:;右下转左下
    mov word[y],79
    dec word[x]
    mov byte[status], 3
jmp next_position
;其他方位与这里类似,略去了
print:输出字符
;置光标位置

    mov ah,2    ;功能号
    mov bh,0    ;第0页
    mov dh, byte[x]
    mov dl, byte[y]
int 10h
;输出字符
    mov ah, 9
    ;mov al,'a'    ;设置字符
    mov bh, 0
    ;mov bl, 7
    mov cx, 1
    int 10h
   
    mov bh, 0
    mov cl, 80
    sub cl,dl
    mov dl,cl
    mov cx, 25
    sub cl,dh
    mov dh,cl
    mov cl,1
    mov ah, 2
    int 10h
    mov ah,9
    int 10h
    jmp next_position
end:
  jmp $

4.实验结果

1.1

1.2

2.1

设置并输出光标位置

2.2

从光标位置开始输出学号

2.3

键盘回显输出

3.

4.

5. 总结

通过这次实验,我学习了x86汇编,了解了x86寄存器实验,通过汇编实现了基本的判断循环逻辑和函数,学习了显存、光标和输出的原理

实验中有许多需要注意的问题

cmp比较变量和寄存器数字时要把指定变量的字节数,否则回出现error: operation size not specified的错误

要注意call和ret的区别call会把他的下一条指令的地址压入s堆栈,然后跳转到他调用的开始处,同时ret会自动弹出返回地址。

JMP只是简单的跳转

改进:再任务4中设置双射的效果时只是在输出的时候做了一个对称,即两者关于屏幕中间对称,后续可以改进使得两者是真正是不相关的。