任务一
验证性实验:有些汇编指令会影响到标志寄存器中的一个或多个状态标志位。
在debug环境中,分别实践、观察:
① add指令对标志寄存器中的零标志位ZF(Zero Flag)、进位标志位CF(Carry Flag)是否有影响?
② inc指令对标志寄存器中的零标志位ZF(Zero Flag)、进位标志位CF(Carry Flag)是否有影响?
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
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
其中:
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指令,能否替换成如下代码?你的结论的依据/理由是什么?
add si, 2
add di, 2
不能替换为上述代码. add指令会影响进位标志位CF, 从而影响128位加法的进位链.
② 在debug中调试,观察数据段中做128位加之前,和,加之后,数据段的值的变化。
加之前:
加之后:
076A:0000 ~ 076A:0009 的位置(x)存放了x和y的和.
任务二
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
说明:task2.asm中用到的两个DOS系统功能调用:
DOS系统功能调用int 21h的1号子功能
功能:从键盘上输入单个字符
入口参数:(ah) = 1
出口参数: (al)存放输入字符的ASCⅡ码
即:mov ah, 1 int 21h ;(al) <-- 输入字符的ascii码
DOS系统功能调用int 21h的2号子功能
功能:输出单个字符到屏幕上
入口参数:(ah) = 2, (dl) = 待输出的字符或其ascⅡ码
出口参数:无
即:mov ah, 2 mov dl, xx ; xx是待输出的字符, 或, 其ascii码 int 21h
运行程序,从键盘上输入一串字符,以#结束(比如,输入George Orwell, 1984#),观察结果。结合运行结果,理解代码并回答问题:
① 汇编指令代码line11-18,实现的功能是?
持续获取键盘输入字符, 直到获取到的字符为'#'跳转至next.
② 汇编指令代码line20-22,实现的功能是?
0ah是换行符的ascii码, 该段代码功能是输出换行.
③ 汇编指令代码line24-30,实现的功能是?
输出str上存储的字符串(除了'#').
任务三
针对8086CPU,已知逻辑段定义如下:
data segment
x dw 91, 792, 8536, 65521, 2021
len equ $ - x
data ends
编写8086汇编源程序task3.asm,在屏幕上以十进制形式输出data段中这一组连续的数据,数据和数据之间以空格间隔。
要求:
-
编写子程序printNumber
- 功能:以十进制形式输出一个任意位数的整数(整数范围0 ~ 65535)
- 入口参数:寄存器ax(待输出的数据 --> ax)
- 出口参数:无
-
编写子程序printSpace
- 功能:打印一个空格
- 入口参数:无
- 出口参数:无
-
在主体代码中,综合应用寻址方式和循环,调用printNumber和printSpace, 实现题目要求。
assume cs:code, ds:data
data segment
x dw 91, 792, 8536, 65521, 2021
len equ ($ - x)/2
data ends
code segment
main:
mov ax, data
mov ds, ax
mov cx, len
mov si, offset x
print:
mov ax, [si]
inc si
mov dx, [si]
inc si
call printNumber
call printSpace
loop print
mov ah, 4ch
int 21h
;功能:以十进制形式输出一个任意位数的整数(整数范围0 ~ 65535)
;入口参数:寄存器ax(待输出的数据 --> ax) 以及dx(存放高位)
;出口参数:无
;限定数据范围 0 ~ 65535, 故32位除法时无需考虑除法溢出, 直接将dx置0
printNumber:
push bx
push cx ;用来存放数据位数
mov cx, 0
save:
mov dx, 0
mov bx, 10 ;除数
div bx
push dx ;余数入栈
inc cx
cmp ax, 0 ;判断商
jne save
print_save:
; mov bx, dx ;打印余数
pop bx ;打印余数
mov dl, bl
mov ah, 2
or dl, 30h
int 21h
loop print_save
pop cx
pop bx
ret
printSpace:
push ax
push dx
mov ah, 2
mov dl, ' '
int 21h
pop dx
pop ax
ret
code ends
end main
任务四
针对8086CPU,已知逻辑段定义如下:
data segment
str db "assembly language, it's not difficult but tedious"
len equ $ - str
data ends
编写8086汇编源程序task4.asm,将data段中字符串里的小写字符转换成大写。
要求:
- 编写子程序strupr
- 功能:将包含任意字符的字符串中的小写字母变成大写
- 入口参数
- (ds:si ) 字符串首地址的段地址和偏移地址分别送至ds和si
- (cx) 字符串的长度
- 出口参数:无
- 在主体代码中,设置入口参数,调用strupr, 实现题目要求。
assume cs:code
data segment
str db "assembly language, it's not difficult but tedious"
len equ $ - str
data ends
code segment
main:
mov ax, 0b800h
mov es, ax
mov ax, data
mov ds, ax
mov cx, len
mov si, offset str
call strupr
mov cx, len
mov si, offset str
mov bl, 00000010b
mov bh, 0
call printStr
mov ah, 4ch
int 21h
;功能:将包含任意字符的字符串中的小写字母变成大写
;入口参数:
; (ds:si ) 字符串首地址的段地址和偏移地址分别送至ds和si
; (cx) 字符串的长度
;出口参数:无
strupr:
cmp byte ptr [si], 97
jb no_handle
cmp byte ptr [si], 122
ja no_handle
and byte ptr [si], 11011111b
no_handle:
inc si
loop strupr
ret
; 入口参数:
; 字符串首字符地址 --> ds:si(其中,字符串所在段的段地址—> ds, 字符串起始地址的偏移地址—> si)
; 字符串长度 --> cx
; 字符串颜色 --> bl
; 指定行 --> bh (取值:0 ~24)
printStr:
push bp ; 因为要用到bp和di, 先保存现场
push di
mov ah, 0
mov al, 160
mul bh
mov bp, ax ; 计算行数偏移地址存储在bp
mov di, si ; di存储显存中每个字符偏移地址
printChar:
mov al, ds:[si]
mov es:[bp+di], al ; 字符
mov es:[bp+di+1], bl ; 颜色
inc si
inc di
inc di ; di要加两次
loop printChar
pop bp ; 还原现场
pop di
ret
code ends
end main
实验五
使用任意文本编辑器,录入8086汇编源码task5.asm。
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
mov dl, 70
int 10h
cmp al, '7'
je s1
mov ah, 9
mov dx, offset str2
int 21h
jmp over
s1: mov ah, 9
mov dx, offset str1
int 21h
over:
mov ah, 4ch
int 21h
code ends
end start
运行程序,输入7,观察结果。输入其他字符,观察结果。结合运行结果和注释,理解代码实现的功能 .
代码功能: 输入字符为'7'时, 显示标号str1处的字符串. 输入字符为非'7'时, 显示标号str2处的字符串.
说明:task5.asm中,使用用到的DOS系统功能调用和BIOS中断例程
DOS系统功能调用int 21h的1号子功能
功能:从键盘上输入单个字符
入口参数:(ah) = 1
出口参数: (al)存放输入字符的ASCⅡ码
即:mov ah, 1 int 21h ; (al) <-- 输入字符的ascⅡ码
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
任务六
实验任务1、2、3、5中使用了不少系统提供的中断例程。本实验任务中,要求自行实现一个42号软中断例程,使得通过 int 42 或 int 2ah 软中断调用,实现在屏幕最下方中间以黑底绿字打印"welcome to 2049!"。
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
mov ah, 4ch
int 21h
code ends
end start
运行task6_1.exe,实现将
42号中断处理程序安装到0:200开始的连续内存空间,并设置中断向量表,使得将来通过 int 42 ,系统可以跳转到中断处理程序。
对汇编源程序task6_2.asm进行汇编、链接,得到可执行程序task6_2.exe。运行task6_2.exe。
自定义43号中断:
中断执行程序即为 实验三 任务五 : 打印蓝屏 && 输出学号
修改后的task6_1.asm
assume cs:code
code segment
start:
; 43 interrupt routine install code
mov ax, cs
mov ds, ax
mov si, offset int43 ; set ds:si
mov ax, 0
mov es, ax
mov di, 200h ; set es:di
mov cx, offset int43_end - offset int43
cld
rep movsb
; set IVT(Interrupt Vector Table)
mov ax, 0
mov es, ax
mov word ptr es:[43*4], 200h
mov word ptr es:[43*4+2], 0
mov ah, 4ch
int 21h
int43:
jmp short int43_start
stu_no db '201983300514'
len = $ - stu_no
; display string "welcome to 2049!"
int43_start:
main:
call print_blue_screen
call print_stu_no
iret
print_blue_screen:
push ax ; 保存现场
push es
push si
mov ax, 0b800h
mov es, ax
mov cx, 2000
mov si, 1
single_blue:
mov byte ptr es:[si], 00010000b
inc si
inc si
loop single_blue
pop si ; 还原现场
pop es
pop ax
ret
print_stu_no:
push ax
push es
push si
push ds
push di
prefix:
mov ax, 0b800h
mov es, ax
mov cx, 34
mov si, 3840 ; si存放每次显存输出的偏移地址
call print_dash
content:
mov ax, cs
mov ds, ax
mov cx, len
mov di, 202h ; di存放data中每个字符的偏移地址
single_no:
mov al, ds:[di]
inc di
mov byte ptr es:[si], al
inc si
mov byte ptr es:[si], 00010111b
inc si
loop single_no
postfix:
mov cx, 34
call print_dash
pop di
pop ds
pop si
pop es
pop ax
ret
; 输入参数:
; 显示的基地址si
; 输出长度cx
; 输出:
; 迭代后的基地址si
print_dash:
single_dash:
mov byte ptr es:[si], '-'
inc si
mov byte ptr es:[si], 00010111b
inc si
loop single_dash
ret
int43_end:
nop
code ends
end start
修改后的task6_2.asm
assume cs:code
code segment
start:
int 43
mov ah, 4ch
int 21h
code ends
end start