汇编课程实验四 8086标志寄存器及中断

实验目的

  1. 理解标志寄存器用途,理解常用标志位CF, ZF, OF, SF, TF, IF的用途和意义。
  2. 理解条件转移指令je, jz, ja, jb, jg, jl等的跳转原理,掌握组合使用汇编指令cmp和条件转移指令实现
    分支和循环的用法
  3. 了解软中断指令的用法,体验和理解中断原理
  4. 综合应用寻址方式和汇编指令完成简单应用编程

实验准备

复习王爽《汇编语言》「第10章 call和ret指令」、「第11章 标志寄存器」
学习王爽《汇编语言》「第12章 内中断 」、「第13章 int指令」

实验内容

实验任务1

根据实验给出的截图我们可以判断

  • add指令对标志寄存器的零标志位ZF和进位标志位CF都造成了影响
  • inc指令只对CF造成影响

实验源码:

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个字节空间

实验结果截图:
运行前:

运行后:

line31~line34的4条inc指令,能否替换成如下代码?你的结论的依据/理由是什么?

add si, 2
add di, 2

解答:

Add换成inc是可以的,因为该汇编代码运行并不会产生进位,我们由题目给定的条件可以得到当产生进位时,inc不会改变进位标志寄存器CF,而add会改变。

实验任务2

使用任意文本编辑器,录入8086汇编源码task2.asm。
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进行汇编、链接,得到可执行文件task2.exe。

运行程序,从键盘上输入一串字符,以#结束(比如,输入George Orwell, 1984#),观察结果。结合运行结果,理解代码并回答问题:

  • 汇编指令代码line11-18,实现的功能是?
  • 汇编指令代码line20-22,实现的功能是?
  • 汇编指令代码line24-30,实现的功能是?

实验结果截图:

  • Line11-18的作用是从键盘依次读入若干个字符,如果当前读入的字符是#,则停止输入,跳转到下一标号

  • Line20-22的作用是输出换行符

  • Line 24-30的作用是循环将之前读入的字符依次输出不包括#

实验任务3

针对8086CPU,已知逻辑段定义如下

data segment
    x dw 91, 792, 8536, 65521, 2021
    len equ $ - x
data ends

编写8086汇编源程序task3.asm,在屏幕上以十进制形式输出data段中这一组连续的数据,数据和数据之间以空格间隔。

要求:

  • 编写子程序printNumbe

    • 功能:以十进制形式输出一个任意位数的整数(整数范围0 ~ 65535)
    • 入口参数:寄存器ax(待输出的数据 --> ax)
    • 出口参数:无
  • 编写子程序printSpace

    • 功能:打印一个空格
    • 入口参数:无
    • 出口参数:无
  • 在主体代码中,综合应用寻址方式和循环,调用printNumber和printSpace, 实现题目要求

实验代码:

assume cs:code, ds:data 
data segment
	x dw 91, 792, 8536, 65521, 2021
	len equ $ - x
data ends:data

stack segment
	db 64  dup(0)
	top equ $ + 1
stack ends

code segment
start:
	mov ax,data
	mov ds,ax
	mov ax,stack
	mov ss,ax
	mov sp,top
	mov cx,len/2
	mov bx,0

s1: 	
	mov ax,[bx]
	push bx
	push cx
	call printNumber
	call printSpace
	pop cx
	pop bx
	add bx,2
	loop s1
	
	mov ah,4ch
	int 21h

printNumber:
	mov bx,10
	mov cx,0
s2:
	mov dx,0
	div bx
	push dx
	inc cx
	cmp ax,0
	je s3
	jmp s2
s3:
	pop dx
	mov ah,2
	or dl,30h
	int 21h
	loop s3
	ret	

printSpace:
	mov dl," "
	mov ah,2
	int 21h
	ret

code ends
end start

实验结果截图:

实验任务4

针对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,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 si,0
	mov cx,len
	call strupr
	
	mov ah,4ch
	int 21h
strupr:
	
s1:
	cmp byte ptr ds:[si],61h
   	 jb s2
	cmp byte ptr ds:[si],7ah
  	 ja s2
	mov ah,2
	mov dl,[si]
	and dl,11011111B
	int 21h 
	mov ds:[si],dl
	
	
s2:
	inc si
	loop s1
	ret
code ends
end start	

实验结果截图

实验任务5

使用任意文本编辑器,录入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 ; 设置光标位置在第24行
    mov dl, 70 ; 设置光标位置在第70列
    int 10h ; 设置光标位置
    cmp al, '7'
    je s1
    mov ah, 9
    mov dx, offset str2
    int 21h ; 显示标号str2处的字符串
    jmp over
s1: 
    mov ah, 9
    mov dx, offset str1
    int 21h ; 显示标号str2处的字符串
over:
    mov ah, 4ch
    int 21h
code ends
end start

对源程序task5.asm进行汇编、链接,得到可执行文件task5.exe。
运行程序,输入7,观察结果。输入其他字符,观察结果。结合运行结果和注释,理解代码实现的功能

实验结果截图:

代码的作用是读入一个字符判断其是否等于7,若等于则在同行末尾输出YES,否则输出NO

实验任务6

实验任务1、2、3、5中使用了不少系统提供的中断例程。本实验任务中,要求自行实现一个42号软中断例程,使得通过 int 42 或 int 2ah 软中断调用,实现在屏幕最下方中间以黑底绿字打印"welcome to 2049!"。

建议配合教材第12章学习理解并实践。
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_1.asm进行汇编、链接,得到可执行程序task6_1.exe。

运行task6_1.exe,实现将42号中断处理程序安装到0:200开始的连续内存空间,并设置中断向量表,使得将来通过 int 42 ,系统可以跳转到中断处理程序。

对汇编源程序task6_2.asm进行汇编、链接,得到可执行程序task6_2.exe。运行task6_2.exe。

  • 通过此项实现任务,你对中断、软中断的理解
  • 自己选一个未被使用的中断码,实现一个中断例程,并调用测试。给出源码和运行测试截图。(选做*)

8086一共提供了256个中断,中断码为0~255,其中,有些保留作为系统用,有些未使用。可以自行挑选一个未被使用的中断码,程时自己编写中断例程。

实验结果截图:

硬中断和软中断的区别:

硬中断

  • 硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。

  • 处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断。在有多核心的系统上,一个中断通常只能中断一颗CPU(也有一种特殊的情况,就是在大型主机上是有硬件通道的,它可以在没有主CPU的支持下,可以同时处理多个中断。)。

  • 硬中断可以直接中断CPU。它会引起内核中相关的代码被触发。对于那些需要花费一些时间去处理的进程,中断代码本身也可以被其他的硬中断中断。

  • 对于时钟中断,内核调度代码会将当前正在运行的进程挂起,从而让其他的进程来运行。它的存在是为了让调度代码(或称为调度器)可以调度多任务。

软中断

  • 软中断的处理非常像硬中断。然而,它们仅仅是由当前正在运行的进程所产生的。

  • 通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同。

  • 软中断仅与内核相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。

  • 软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行。

posted @ 2021-12-10 17:21  【金陵彭于晏】  阅读(173)  评论(2)    收藏  举报