汇编网首页登录博客注册
unixandlinux的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
学习动态
最新评论
最新留言
好友圈
友情链接

[2023-08-07 20:03] 第 12 章 实验 12  编写 0 号中断的处理程序

实验 12 编写 0 号中断的处理程序

编写 0 号中断的处理程序,使得在除法溢出发生时在屏幕中间显示字符串“divide error!”,然后返回 DOS 操作系统。
要求:仔细跟踪调试,在理解整个过程之前,不要进行后面课程的学习。

具体代码如下:
必须先执行安装 do0 程序的 do0.asm 程序代码,再执行会产生 0 号中断的 test.asm 测试程序代码。

一、不考虑中断处理程序的返回问题

1. 安装 do0 程序的程序代码:

assume cs:code

code segment

; do0 程序(0 号中断处理程序)的安装
start:        mov ax,cs
        mov ds,ax
        mov si,offset do0                                        ; 设置 ds:si 指向源地址
        mov ax,0
        mov es,ax
        mov di,200h                                                        ; 设置 es:di 指向目的地址
        mov cx,offset do0end-offset do0  ; 设置 cx 为传输长度
        cld                                                                        ; 设置传输方向为正
        rep movsb

        ; 设置中断向量表
        mov word ptr es:[0*4],200h
        mov word ptr es:[0*4+2],0

        mov ax,4c00h
        int 21h

do0:        jmp short do0start
        db "divide error!"

do0start:  mov ax,cs
          mov ds,ax
          mov si,202h                                                ; 设置 ds:si 指向字符串

          mov ax,0B800h
          mov es,ax
          mov di,12*160+36*2                                ; 设置 es:di 指向显存空间的中间位置
          mov cx,13                                                ; 设置 cx 为字符串长度
s:          mov al,[si]
          mov es:[di],al
          mov byte ptr es:[di+1],2  ; 设置显示的字符串字体属性:绿色
          inc si
          add di,2
          loop s

          iret

do0end:  nop

code ends

end start

2. 产生 0 号中断的测试程序的代码:

assume cs:codeseg

codeseg segment

; begin 代码选自书本 p240 的 12.6 节的内容
begin:        mov ax,1000h
        mov bh,1
        div bh  ; 执行本行除法运算代码,将产生 0 号中断
                ; 由硬件自动执行的中断过程,其入栈的 CS 和 IP 值,
                ; 不是指向产生中断信息的指令(此处为本行的 div 指令)的下一条指令,
                ; 而就是指向这条产生中断信息的当前指令(也就是本行的 div 指令),
                ; 这将导致从中断处理程序的末尾 iret 指令返回的时,
                ; 会不断执行这条产生中断过程的问题指令,并成为一个死循环。

        mov ax,53A8h  ; 本行代码用于验证引发 0 号中断的 div 指令中断处理程序的执行结果

        mov ax,4c00h
        int 21h

codeseg ends

end begin

二、考虑中断处理程序的返回问题

1. 安装 do0 程序的程序代码:

assume cs:code

code segment

; do0 程序(0 号中断处理程序)的安装
start:        mov ax,cs
        mov ds,ax
        mov si,offset do0                                        ; 设置 ds:si 指向源地址
        mov ax,0
        mov es,ax
        mov di,200h                                                        ; 设置 es:di 指向目的地址
        mov cx,offset do0end-offset do0  ; 设置 cx 为传输长度
        cld                                                                        ; 设置传输方向为正
        rep movsb

        ; 设置中断向量表
        mov word ptr es:[0*4],200h
        mov word ptr es:[0*4+2],0

        mov ax,4c00h
        int 21h

do0:        jmp short do0start
        db "divide error!"

do0start:  mov ax,cs
          mov ds,ax
          mov si,202h                                                ; 设置 ds:si 指向字符串

          mov ax,0B800h
          mov es,ax
          mov di,12*160+36*2                                ; 设置 es:di 指向显存空间的中间位置

        ; 本程序在不考虑中断处理程序返回问题的程序代码基础中添加以下两行代码,
        ; 否则将陷入引发 0 号中断过程的问题指令的死循环 —— 不断执行引发 0 号中断过程的问题指令
        mov bp,sp
        add [bp],cx

        mov cx,13                                                ; 设置 cx 为字符串长度
s:        mov al,[si]
        mov es:[di],al
        mov byte ptr es:[di+1],2  ; 设置显示的字符串字体属性:绿色
        inc si
        add di,2
        loop s

        iret

do0end:  nop

code ends

end start

2. 产生 0 号中断的测试程序的代码:

(1) 测试代码 1

assume cs:codeseg

codeseg segment

; begin 代码选自书本 p240 的 12.6 节的内容
begin:        mov ax,1000h
        mov bh,1
        mov cx,offset s2-offset s1  ; 将产生中断过程的 div 指令长度存入 CX 寄存器,
                                                                        ; 提供给中断处理程序使用
s1:        div bh  ; 执行本行除法运算代码,将产生 0 号中断
                ; 由硬件自动执行的中断过程,其入栈的 CS 和 IP 值,
                ; 不是指向产生中断信息的指令(此处为本行的 div 指令)的下一条指令,
                ; 而就是指向这条产生中断信息的当前指令(也就是本行的 div 指令),
                ; 这将导致从中断处理程序的末尾 iret 指令返回的时,
                ; 会不断执行这条产生中断过程的问题指令,并成为一个死循环。

s2:        mov ax,53A8h

        mov ax,4c00h
        int 21h

codeseg ends

end begin

(2) 测试代码 2

assume cs:codeseg

datasg segment
  db "Test Over!",0
datasg ends

codeseg segment

; begin 代码选自书本 p240 的 12.6 节的内容
begin:        mov ax,1000h
        mov bh,1
        mov cx,offset s2-offset s1  ; 将产生中断过程的 div 指令长度存入 CX 寄存器,
                                                                        ; 提供给中断处理程序使用

s1:        div bh  ; 执行本行除法运算代码,将产生 0 号中断

s2:        call show

        mov ax,4c00h
        int 21h

; 从 show 标号开始到 call show_str(调用 show_str 子程序)都是对 show_str 子程序参数的初始化指令
show:  mov ax,datasg
    mov ds,ax
    mov si,0
    mov ax,0B800h  ; 将显示缓冲区第 0 页的段地址存入 ES 段寄存器中
    mov es,ax
    mov dh,8  ; 行:第 8 行
    mov dl,3  ; 列:第 3 列
    mov cl,2  ; 属性:绿色字体
; 计算列偏移,结果存入 DI 寄存器中
    dec dl
    mov ax,2
    mul dl
    mov di,ax
; 计算行偏移,结果加到 DI 寄存器中
    dec dh
    mov ax,160
    mul dh
    add di,ax
; 将属性编码存入 AH 寄存器
    mov ah,cl
; 将 0 存入 CX 寄存器,以便调用 show_str 子程序时,将字符串字符码赋给 CL 寄存器时,
; 可以通过 jcxz 指令判断是否到达字符串的末尾
    mov cx,0

    call show_str

    ret  ; 返回主调程序 begin

; show_str 子程序从开始到结束都在一个循环之中,可以将它看作是个递归调用程序,
; 功能相当于 C 语言中的递归函数
; 由于 (cx) 被初始化为数值 0,因此将 ds:si 字节单元传送给 CL 寄存器后,使用 jcxz 指令来判断:
; 如果 (cx)=0,说明已经到达字符串的末尾结束符 '\0',则跳至 ok 标号处返回主调程序
; 如果 (cx)≠0,说明未到达字符串的末尾结束符 '\0',则向显示缓冲区输出字符,继续执行 show_str 子程序
show_str:  mov cl,[si]
          jcxz ok
          mov al,cl
          mov es:[di],ax
          inc si
          add di,2
          jmp short show_str  ; 返回 show_str 子程序的开头,循环执行自己 —— 递归调用
ok:    ret                                                ; 返回 show_str 子程序的主调程序 show

codeseg ends

end begin

(3) 测试代码 3

assume cs:codeseg

datasg segment
  db "Test Start:",0,"Test Over!",0
datasg ends

codeseg segment

; begin 代码选自书本 p240 的 12.6 节的内容
begin:        mov si,0                                        ; 令 ds:si 指向第 1 个字符串“Test Start:”
        mov dh,8                                        ; 行:第 8 行
        call show
        mov ax,1000h
        mov bh,1
        mov cx,offset s2-offset s1  ; 将引发中断过程的 div 指令的长度存入 CX 寄存器,
                                                                        ; 提供给中断处理程序使用

s1:        div bh                                                ; 执行本行除法运算代码,将产生 0 号中断

s2:        mov si,12                                        ; 令 ds:si 指向第 2 个字符串“Test Over!”
        mov dh,20                                        ; 行:第 20 行
        call show

        mov ax,4c00h
        int 21h

; 从 show 标号开始到 call show_str(调用 show_str 子程序)都是对 show_str 子程序参数的初始化指令
show:        mov ax,datasg
        mov ds,ax
        mov ax,0B800h  ; 将显示缓冲区第 0 页的段地址存入 ES 段寄存器中
        mov es,ax
        mov dl,3  ; 列:第 3 列
        mov cl,2  ; 属性:绿色字体
; 计算列偏移,结果存入 DI 寄存器中
        dec dl
        mov ax,2
        mul dl
        mov di,ax
; 计算行偏移,结果加到 DI 寄存器中
        dec dh
        mov ax,160
        mul dh
        add di,ax
; 将属性编码存入 AH 寄存器
        mov ah,cl
; 将 0 存入 CX 寄存器,以便调用 show_str 子程序时,将字符串字符码赋给 CL 寄存器时,
; 可以通过 jcxz 指令判断是否到达字符串的末尾
        mov cx,0

        call show_str

        ret                                                ; 返回主调程序 begin

; show_str 子程序从开始到结束都在一个循环之中,可以将它看作是个递归调用程序,
; 功能相当于 C 语言中的递归函数
; 由于 (cx) 被初始化为数值 0,因此将 ds:si 字节单元传送给 CL 寄存器后,使用 jcxz 指令来判断:
; 如果 (cx)=0,说明已经到达字符串的末尾结束符 '\0',则跳至 ok 标号处返回主调程序
; 如果 (cx)≠0,说明未到达字符串的末尾结束符 '\0',则向显示缓冲区输出字符,继续执行 show_str 子程序
show_str:  mov cl,[si]
          jcxz ok
          mov al,cl
          mov es:[di],ax
          inc si
          add di,2
          jmp short show_str  ; 返回 show_str 子程序的开头,循环执行自己 —— 递归调用
ok:        ret                                                ; 返回 show_str 子程序的主调程序 show

codeseg ends

end begin
评论次数(0)  |  浏览次数(101)  |  类型(课程实验) |  收藏此文  | 
 
 请输入验证码  (提示:点击验证码输入框,以获取验证码