1. 实验任务1
验证性实验:有些汇编指令会影响到标志寄存器中的一个或多个状态标志位。
在debug环境中,分别实践、观察:
① add指令对标志寄存器中的零标志位ZF(Zero Flag)、进位标志位CF(Carry Flag)是否有影响?
② inc指令对标志寄存器中的零标志位ZF(Zero Flag)、进位标志位CF(Carry Flag)是否有影响?

可以看到add对零标志位有影响,因为溢出一位后剩余位为0。对进位标志位也有影响,因为产生了进位。
inc对零标志位有影响,因为溢出一位后剩余位为0。对进位标志位没有影响,可见inc产生的进位不会被记录下来。
使用任意文本编辑器,录入8086汇编源码task1.asm。
task1.asm
1 assume cs:code, ds:data 2 3 data segment 4 x dw 1020h, 2240h, 9522h, 5060h, 3359h, 6652h, 2530h, 7031h 5 y dw 3210h, 5510h, 6066h, 5121h, 8801h, 6210h, 7119h, 3912h 6 data ends 7 code segment 8 start: 9 mov ax, data 10 mov ds, ax 11 mov si, offset x 12 mov di, offset y 13 call add128 14 15 mov ah, 4ch 16 int 21h 17 18 add128: 19 push ax 20 push cx 21 push si 22 push di 23 24 sub ax, ax 25 26 mov cx, 8 27 s: mov ax, [si] 28 adc ax, [di] 29 mov [si], ax 30 31 inc si 32 inc si 33 inc di 34 inc di 35 loop s 36 37 pop di 38 pop si 39 pop cx 40 pop ax 41 ret 42 code ends 43 end start
其中:
add128是子程序子程序。
功能:实现计算两位128位数的加法
入口参数:
ds:si指向存储第一个128位数的存储空间(因为一个数128位,需要8个字节的连续空间)
ds:di指向存储第二个128位数的存储空间
出口参数:
加运算后的结果,保存在第一个数的存储空间中,即:ds:si开始的连续8个字节空间
在代码段种,调用add128实现对标号x和y处存储的两个128位数据相加,结果保存在x处的连续128个字节中。
对程序进行汇编、链接,得到可执行程序task1.exe。在debug中调试程序,并回答问题。

① line31~line34的4条inc指令,能否替换成如下代码?你的结论的依据/理由是什么?
1 add si, 2 2 add di, 2
若替换成如上代码,仍然可以得到正确答案。因为si与di的范围都在0~16内,对其进行自增运算并不会产生进位,而且样例中每个字的加法均没有产生进位,因此不会影响结果。
但是逻辑上并不能够将上述代码替换,如果某个字的相加产生了进位,那么计算结果将产生错误。

② 在debug中调试,观察数据段中做128位加之前,和,加之后,数据段的值的变化。

2. 实验任务2
使用任意文本编辑器,录入8086汇编源码task2.asm。
task2.asm
1 assume cs:code, ds:data 2 data segment 3 str db 80 dup(?) 4 data ends 5 6 code segment 7 start: 8 mov ax, data 9 mov ds, ax 10 mov si, 0 11 s1: 12 mov ah, 1 13 int 21h 14 mov [si], al 15 cmp al, '#' 16 je next 17 inc si 18 jmp s1 19 next: 20 mov ah, 2 21 mov dl, 0ah 22 int 21h 23 24 mov cx, si 25 mov si, 0 26 s2: mov ah, 2 27 mov dl, [si] 28 int 21h 29 inc si 30 loop s2 31 32 mov ah, 4ch 33 int 21h 34 code ends 35 end start
对源程序task2.asm进行汇编、链接,得到可执行文件task2.exe。
运行程序,从键盘上输入一串字符,以#结束(比如,输入George Orwell, 1984#),观察结果。结合运行结果,理解代码并回答问题:
① 汇编指令代码line11-18,实现的功能是?
② 汇编指令代码line20-22,实现的功能是?
③ 汇编指令代码line24-30,实现的功能是?
①汇编指令代码line11-18,实现的功能是从键盘输入一个字符并存入数据段偏移地址为si的位置。每输入一个字符si进行自增运算,并无条件跳转到标记s1处,若字符为"#",则标志寄存器CF值为ZR,此时条件指令je指令执行跳转到标记next处。
② 汇编指令代码line20-22,实现的功能是输出换行符,换行符的十六进制ASCII码值就是0AH。
③ 汇编指令代码line24-30,实现的功能是将数据段中长度为si的字符串输出。

说明:task2.asm中用到的两个DOS系统功能调用:
DOS系统功能调用int 21h的1号子功能
功能:从键盘上输入单个字符
入口参数:(ah) = 1
出口参数: (al)存放输入字符的ASCⅡ码
即:
1 mov ah, 1 2 int 21h ; (al) <-- 输入字符的ascⅡ码
DOS系统功能调用int 21h的2号子功能
功能:输出单个字符到屏幕上
入口参数:(ah) = 2, (dl) = 待输出的字符或其ascⅡ码
出口参数:无
即:
1 mov ah, 2 2 mov dl, ×× ; ××是待输出的字符,或,其ascⅡ码 3 int 21h
3. 实验任务3
针对8086CPU,已知逻辑段定义如下:
1 data segment 2 x dw 91, 792, 8536, 65521, 2021 3 len equ $ - x 4 data ends
编写8086汇编源程序task3.asm,在屏幕上以十进制形式输出data段中这一组连续的数据,数据和数据之间以空格间隔。
要求:
编写子程序printNumber
功能:以十进制形式输出一个任意位数的整数(整数范围0 ~ 65535)
入口参数:寄存器ax(待输出的数据 --> ax)
出口参数:无
编写子程序printSpace
功能:打印一个空格
入口参数:无
出口参数:无
在主体代码中,综合应用寻址方式和循环,调用printNumber和printSpace, 实现题目要求。
1 assume cs:code, ds:data 2 data segment 3 x dw 91, 792, 8536, 65521, 2021 4 len equ $ - x 5 data ends 6 7 stack segment 8 db 16 dup(0) 9 stack ends 10 11 code segment 12 start: 13 mov ax, data 14 mov ds, ax 15 16 mov cx, 5 17 mov si, offset x 18 s: mov ax, ds:[si] 19 call printNumber 20 call printSpace 21 inc si 22 inc si 23 loop s 24 25 mov ah, 4ch 26 int 21h 27 28 printNumber: 29 mov bx, 0AH ;除数 30 mov dx, 0 ;高位置为0,记录余数 31 mov di, 0 32 push cx 33 s1: div bx 34 push dx ;余数入栈 35 inc di 36 mov dx, 0 37 cmp ax, 0 ;被除数为0时结束 38 je next 39 jmp s1 40 next: mov ah, 2 41 mov cx, di 42 s2: pop dx 43 or dl, 30H ;数字转字符 44 int 21h 45 loop s2 46 47 pop cx 48 ret 49 50 printSpace: 51 mov dl, ' ' 52 mov ah, 2 53 int 21h 54 ret 55 code ends 56 end start

4. 实验任务4
针对8086CPU,已知逻辑段定义如下:
1 data segment 2 str db "assembly language, it's not difficult but tedious" 3 len equ $ - str 4 data ends
编写8086汇编源程序task4.asm,将data段中字符串里的小写字符转换成大写。
要求:
编写子程序strupr
功能:将包含任意字符的字符串中的小写字母变成大写
入口参数
(ds:si ) 字符串首地址的段地址和偏移地址分别送至ds和si
(cx) 字符串的长度
出口参数:无
在主体代码中,设置入口参数,调用strupr, 实现题目要求。
1 assume cs:code, ds:data 2 data segment 3 str db "assembly language, it's not difficult but tedious" 4 len equ $ - str 5 data ends 6 7 stack segment 8 db 16 dup(0) 9 stack ends 10 11 code segment 12 start: 13 mov ax, data 14 mov ds, ax 15 16 mov cx, len 17 mov si, offset str 18 call strupr 19 20 mov ah, 4ch 21 int 21h 22 strupr: 23 s: mov al, byte ptr ds:[si] 24 cmp al, 'a' 25 jb next 26 cmp al, 'z' 27 ja next 28 29 mov ah, 2 ;若是小写字母 30 and al, 11011111b ;转大写 31 mov dl, al 32 int 21h ;输出 33 inc si 34 loop s 35 36 cmp cx, 0 37 je return 38 39 next: mov ah, 2 ;若是其他字符,直接输出 40 mov dl, al 41 int 21h 42 inc si 43 loop s 44 return: 45 ret 46 code ends 47 end start
5. 实验任务5
使用任意文本编辑器,录入8086汇编源码task5.asm。
task5.asm
1 assume cs:code, ds:data 2 3 data segment 4 str1 db "yes", '$' 5 str2 db "no", '$' 6 data ends 7 8 code segment 9 start: 10 mov ax, data 11 mov ds, ax 12 13 mov ah, 1 14 int 21h 15 16 mov ah, 2 17 mov bh, 0 18 mov dh, 24 19 mov dl, 70 20 int 10h 21 22 cmp al, '7' 23 je s1 24 mov ah, 9 25 mov dx, offset str2 26 int 21h 27 28 jmp over 29 30 s1: mov ah, 9 31 mov dx, offset str1 32 int 21h 33 over: 34 mov ah, 4ch 35 int 21h 36 code ends 37 end start
对源程序task5.asm进行汇编、链接,得到可执行文件task5.exe。
运行程序,输入7,观察结果。输入其他字符,观察结果。结合运行结果和注释,理解代码实现的功能。
说明:task5.asm中,使用用到的DOS系统功能调用和BIOS中断例程
DOS系统功能调用int 21h的1号子功能
功能:从键盘上输入单个字符
入口参数:(ah) = 1
出口参数: (al)存放输入字符的ASCⅡ码
即:
1 mov ah, 1 2 int 21h ; (al) <-- 输入字符的ascⅡ码
DOS系统功能调用int 21h的9号子功能
功能:显示字符串
入口参数:(ah) = 9,(ds:dx) = 字符串的首地址的段地址和偏移地址
出口参数: 无
其它要求:字符串必须以$结束
即:
1 mov ah, 9 2 mov ds, ×× ; ××是待输出字符串所在段的段地址 3 mov dx, ×× ; ××是待输出字符串第一个字符的偏移地址 4 int 21h
BIOS中断例程int 10h的2号子功能
功能:设置光标位置
入口参数:(ah) = 2, (bh) = 页号(默认取0), (dh) = 行号, (dl) = 列号
出口参数:无
即:
1 mov ah, 2 2 mov bh, ×× ; ××是页号 3 mov dh, ×× 4 mov dl, ×× ; ××是列号 5 int 10h
task5重点在于汇编语言分支结构的运用。使用cmp指令以及对零标志寄存器进行判断以及指令跳转。上述结果表示如果输入的字符不为7,则在屏幕指定位置输出no,若为7,则在相同位置输出yes。
6. 实验任务6
实验任务1、2、3、5中使用了不少系统提供的中断例程。本实验任务中,要求自行实现一个42号软中断例程,使得通过 int 42 或 int 2ah 软中断调用,实现在屏幕最下方中间以黑底绿字打印"welcome to 2049!"。
建议配合教材第12章学习理解并实践。
task6_1.asm
1 assume cs:code 2 3 code segment 4 start: 5 ; 42 interrupt routine install code 6 mov ax, cs 7 mov ds, ax 8 mov si, offset int42 ; set ds:si 9 10 mov ax, 0 11 mov es, ax 12 mov di, 200h ; set es:di 13 14 mov cx, offset int42_end - offset int42 15 cld 16 rep movsb 17 18 ; set IVT(Interrupt Vector Table) 19 mov ax, 0 20 mov es, ax 21 mov word ptr es:[42*4], 200h 22 mov word ptr es:[42*4+2], 0 23 24 mov ah, 4ch 25 int 21h 26 27 int42: 28 jmp short int42_start 29 str db "welcome to 2049!" 30 len equ $ - str 31 32 ; display string "welcome to 2049!" 33 int42_start: 34 mov ax, cs 35 mov ds, ax 36 mov si, 202h 37 38 mov ax, 0b800h 39 mov es, ax 40 mov di, 24*160 + 32*2 41 42 mov cx, len 43 s: mov al, [si] 44 mov es:[di], al 45 mov byte ptr es:[di+1], 2 46 inc si 47 add di, 2 48 loop s 49 50 iret 51 int42_end: 52 nop 53 code ends 54 end start
task6_2.asm
1 assume cs:code 2 3 code segment 4 start: 5 int 42 6 7 mov ah, 4ch 8 int 21h 9 code ends 10 end start
对汇编源程序task6_1.asm进行汇编、链接,得到可执行程序task6_1.exe。运行task6_1.exe,实现将42号中断处理程序安装到0:200开始的连续内存空间,并设置中断向量表,使得将来通过 int 42 ,系统可以跳转到中断处理程序。
对汇编源程序task6_2.asm进行汇编、链接,得到可执行程序task6_2.exe。运行task6_2.exe。

软中断是通过执行中断指令产生的,是软件层面对进程的中断,汇编语言的软中断可以理解为高级语言中函数的调用。上述例子中对中断号的自定义可以灵活地执行编程需要的操作。