本科生实验报告
实验课程: 操作系统
实验名称: 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启动。
· a1 、if_flag 、my_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中设置双射的效果时只是在输出的时候做了一个对称,即两者关于屏幕中间对称,后续可以改进使得两者是真正是不相关的。