四、实验结论
1. 实验任务1
(1)验证性实验:有些汇编指令会影响到标志寄存器中的一个或多个状态标志位。 在debug环境中,分别实践、观察: ① add指令对标志寄存器中的零标志位ZF(Zero Flag)、进位标志位CF(Carry Flag)是否有影响? ==>add指令对ZF,CF均有影响。 ② inc指令对标志寄存器中的零标志位ZF(Zero Flag)、进位标志位CF(Carry Flag)是否有影响? ==>inc指令对ZF有影响,但对CF无影响。 (2)task1.asm源码:
assume cs:code, ds:data
data segment
x dw 1020h, 2240h, 9522h, 5060h, 3359h, 6652h, 2530h, 7031h
y dw 3210h, 5510h, 6066h, 5121h, 8801h, 6210h, 7119h, 3912h
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, offset x
mov di, offset y
call add128
mov ah, 4ch
int 21h
add128:
push ax
push cx
push si
push di
sub ax, ax ;将CF进位标志位置为0(NC)
mov cx, 8
s: mov ax, [si]
adc ax, [di]
mov [si], ax
inc si
inc si
inc di
inc di
loop s
pop di
pop si
pop cx
pop ax
ret
code ends
end start
(3)回答问题: line31~line34的4条inc指令,能否替换成如下代码?你的结论的依据/理由是什么?
add si, 2
add di, 2
==>不能。因为add指令会修改CF的值。 而inc指令却不会修改CF的值(不影响CF标志位的有2个:inc和dec) (4)在debug中调试,观察数据段中做128位加之前和加之后,数据段的值的变化。给出调试观察截 图。 ①.inc指令的结果——128位加有效,数据段的值成功正确相加。 ②.add指令:结果不正确(因为CF进位标志位被错误修改)
2. 实验任务2
(1)程序task2.asm源码:
assume cs:code, ds:data
data segment
str db 80 dup(?)
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
s1:
mov ah, 1
int 21h
mov [si], al
cmp al, '#'
je next
inc si
jmp s1
next:
mov ah, 2
mov dl, 0ah
int 21h
mov cx, si
mov si, 0
s2: mov ah, 2
mov dl, [si]
int 21h
inc si
loop s2
mov ah, 4ch
int 21h
code ends
end start
(2)运行测试截图:
(3)回答问题 运行程序,从键盘上输入一串字符,以#结束(比如,输入George Orwell, 1984#),观察结 果。结合运行结果,理解代码并回答问题: ① 汇编指令代码line11-18,实现的功能是?
s1:
mov ah, 1 ;调用int 21h的1号子功能
int 21h ;从键盘上输入单个字符
mov [si], al
cmp al, '#' ;将输入的字符与‘#’比较
je next ;若相等,则跳转到next
inc si ;否则si+1
jmp s1 ;继续回到s1输入下一个字符
==>功能:一个一个输入字符直到#结束
② 汇编指令代码line20-22,实现的功能是?
mov ah, 2 ;调用int 21h的2号子功能
mov dl, 0ah ; 0ah是换行符的ascii码值
int 21h ;输出单个字符到屏幕上
==>功能:打印一个换行符
③ 汇编指令代码line24-30,实现的功能是?
mov cx, si ;si中存放了输入字符的个数(从0开始,因此不包括#)
mov si, 0 ;将si置为0,从头开始
s2: mov ah, 2 ;调用int 21h的2号子功能
mov dl, [si] ;一个一个输出,(dl) = 待输出的字符或其ascii码
int 21h ;输出单个字符到屏幕上
inc si ;继续下一个
loop s2 ;回s2遍历,直到输入字符全部输出完
3. 实验任务3
(1)task3.asm源码:
assume cs:code,ds:data,ss:stack
data segment
x dw 91, 792, 8536, 65521, 2021 ;0 1 2 3 4
len equ $ - x ;$指下一个数据项的偏移地址=5
data ends
stack segment
db 16 dup('0')
stack ends
code segment
start:
mov ax,data
mov ds,ax ;送data
mov ax,stack
mov ss,ax
mov sp,16 ;设置ss:sp
mov si,offset x
mov cx,5 ;5个数,执行5次循环
print:
push cx ;将cx存入栈,防止后续冲突
mov cx,0 ;重置cx=0
mov ax,ds:[si]
call printNumber
call printSpace
pop cx ;取出原来的cx
inc si
inc si
loop print
mov ah,4ch
int 21h
;子程序:printNumber
;功能:以十进制形式输出一个任意位数的整数(整数范围0 ~ 65535)
printNumber:
mov dx,0 ;余数先初始为0
mov bx,10 ;除10
div bx ;把十进制除以10,商数存在ax中
inc cx ;位数+1
push dx ;把分离出来的余数先保存在栈
mov dx,0 ;余数重置为0
cmp ax,0 ;ax存放的是商,当商=0时除法结束
je s1 ;跳转到s1输出
jmp printNumber ;否则继续除
s1:
mov ah,2
pop dx
or dl,30h ;将其转为相对应的ascii码
int 21h ;调用2号子功能输出该字符
loop s1
ret
;子程序:printSpace
;功能:打印一个空格
printSpace:
mov ah,2
mov dl,' '
int 21h
ret
code ends
end start
(2)运行测试截图:
4. 实验任务4
(1)task4.asm源码:
assume cs:code,ds:data
data segment
str db "assembly language, it's not difficult but tedious"
len equ $ - str
data ends
code segment
start:
mov ax,data
mov ds,ax
mov cx,len
mov si,0
call strupr
mov ah,4ch
int 21h
strupr:
s:mov al,ds:[si] ;从str中取出一个字符
cmp al,'a' ;与'a'的ascii码进行比较
jb print ;比'a'的ascii码值小,不是字母直接打印
cmp al,'z' ;与'z'的ascii码进行比较
ja print ;比'z'的ascii码值大,不是字符直接打印
mov ah,2
and al,11011111b ;大写字母2进制时从左开始第三位一定是0
mov dl,al ;将转换好的大写字母字符送到dl
int 21h ;调用2号子功能打印字符
inc si ;si+1,继续下一个
loop s
ret
;直接输出不是字母的字符
print:
mov ah,2
mov dl,al ;dl中为待输出的字符
int 21h ;调用2号子功能输出
inc si
loop s ;继续下一个字符
code ends
end start
(2)在debug中调试截图( call strupr 调用之前,数据段的值,以及,调用之后,数据段的值) ①.运行结果: ②.call strupr调用之前数据段的值: ③.调用之后,数据段的值:
5. 实验任务5
(1)task5.asm源码:
assume cs:code, ds:data
data segment
str1 db "yes", '$'
str2 db "no", '$'
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ah, 1
int 21h ;从键盘输入字符
mov ah, 2
mov bh, 0
mov dh, 24 ;设置光标位置在24行
mov dl, 70 ;设置光标位置在70列
int 10h ;设置光标位置
cmp al, '7'
je s1 ;若相等,跳转到s1
mov ah, 9 ;调用9号子功能:显示字符串
mov dx, offset str2
int 21h ;显示'no'
jmp over ;跳转到over处结束程序
s1: mov ah, 9 ;调用9号子功能:显示字符串
mov dx, offset str1
int 21h ;显示'yes'
over:
mov ah, 4ch
int 21h
code ends
end start
注:
;DOS系统功能调用int 21h的9号子功能
;功能:显示字符串
;入口参数:(ah) = 9,(ds:dx) = 字符串的首地址的段地址和偏移地址
;出口参数: 无
;其它要求:字符串必须以$结束
;即:
mov ah, 9
mov ds, ×× ; ××是待输出字符串所在段的段地址
mov dx, ×× ; ××是待输出字符串第一个字符的偏移地址
int 21h
;BIOS中断例程int 10h的2号子功能
;功能:设置光标位置
;入口参数:(ah) = 2, (bh) = 页号(默认取0), (dh) = 行号, (dl) = 列号
;出口参数:无
;即
mov ah, 2
mov bh, ×× ; ××是页号
mov dh, ××
mov dl, ×× ; ××是列号
int 10h
(2)程序运行测试截图(输入7,以及输入其他字符,运行结果截图) ①.输入7的情况: ②.输入其他字符的情况:
(3)程序的功能是? 功能:从键盘输入一个字符,如果为'7',则在指定的24行,70列输出'yes';否则在指定位置输出'no'。
6. 实验任务6
(1)代码: task6_1.asm:
assume cs:code
code segment
start:
; 42 interrupt routine install code
mov ax, cs
mov ds, ax
mov si, offset int42 ; set ds:si
mov ax, 0
mov es, ax
mov di, 200h ; set es:di
mov cx, offset int42_end - offset int42
cld
rep movsb
; set IVT(Interrupt Vector Table)
mov ax, 0
mov es, ax
mov word ptr es:[42*4], 200h
mov word ptr es:[42*4+2], 0
mov ah, 4ch
int 21h
int42:
jmp short int42_start
str db "welcome to 2049!"
len equ $ - str
; display string "welcome to 2049!"
int42_start:
mov ax, cs
mov ds, ax
mov si, 202h
mov ax, 0b800h
mov es, ax
mov di, 24*160 + 32*2
mov cx, len
s: mov al, [si]
mov es:[di], al
mov byte ptr es:[di+1], 2
inc si
add di, 2
loop s
iret
int42_end:
nop
code ends
end start
task6_2.asm:
assume cs:code
code segment
start:
int 42 ; 调用自己实现的42号软中断
mov ah, 4ch
int 21h
code ends
end start
(2)结果: ==>编译链接以后,如图,屏幕底部输出了'welcome to 2049!',表示自己实现的42号软中断被成功调用。
(3)通过此项实现任务,你对中断、软中断实现机制的理解: ①.由书上可知iret指令作用: ②.中断的过程其实很容易理解,概括的说就是CPU自己完成以下操作: a.取得中断类型码N; b.pushf, 标志寄存器压栈; c.设置标志为TF=0,IF=0; d.push CS,将CS压栈; e.push IP,将IP压栈; f.设置IP,CS为中断向量表存放的地址,也就是使(IP) = (N4),(CS) = (N4+2)。
③.至于编写中断处理程序,和编写普通的子程序没有多大区别: a.保存用到的寄存器 b.处理中断 c.恢复寄存器 d.iret 返回 其中,iret 指令相当于先retf ,然后popf。