千哥读书笔记:汇编语言(王爽第四版)第12章 实验12

本章实验12,要求编写0号中断的处理程序,使得在除法溢出时,在屏幕中间显示字符串“divide error!”,然后返回到DOS。要实现的效果如下图所示:

在编写这个实验程序之前,我们先复习一下本章的重点内容。

1、中断向量表,指向的是中断程序的地址入口。

2、CUP根据中断类型码,确定中断向量表的表项号,然后根据表项号,找到中断程序的地址入口。

3、中断类型码,主要分为:

    1)类型码除法错误:0

    2)单步执行:1

    3)执行into指令:4

    4)执行int指令,格式为 int n,指令中的n为节字型立即数,是提供给CUP的中断

4、中断向量表,位于内存地址0处:从内存0000:0000到0000:3FF

5、中断向量表的表项占2个字:高地址字存放段地址,低地址字存放偏移地址。

6、CUP找到中断程序入口地址,然后将原来的CS和IP值保存

7、CPU设置CS和IP,这个过程称为中断过程。

上述内容如下图所示:

其中,CPU将原来的CS和IP保存这步操作,又分为以下几个步骤:

1、标志寄存器入栈(pushf)

2、设置标志寄志器第8位TF和第9位IF值为0

3、CS内容入栈(push cs)

4、IP内容入栈(push ip)

而中断处理程序的编写方法主要是:

1、保存用到的寄存器

2、处理中断

3、恢复用到的寄存器

4、用iret指令返回。iret指令相当于pop ip、pop cs、popf

需注意的是:

1、CPU在收到中断信息后,会由硬件自动执行中断过程,故在编程中不用涉及包括pushf、push cs、push ip等指令。

2、由于本章实验12只涉及除法溢出产生提示信息,并自动返回DOS界面,未涉及中断返回其他程序的问题,故不用iret指令。

3、编写本章实验12整个程序时,可将其中的中断程序代码传送到0000:0200至0000:02FF的内存区。

4、由于除法溢出中断类型码为0,故中断程序入口地址从0*4地址单元有效,段地址在0*4+2的字单元,偏移地址在0*4字单元

在做这个实验之前,王爽老师给出了一个示例程序12.3,代码如下:

=================================================

assume cs:code

code segment

start: mov ax,cs

       mov ds,ax

       mov si,offset do0

       mov ax,0

       mov es,ax

       mov di,200h

       mov cx,offset do0end-offset do0

       cld

       rep movsb

       设置中断向量表

       mov ax,4c00h

       int 21h

do0: jmp short do0start

       db "overflow!"

do0start:     mov ax,cs

                   mov ds,ax

                   mov si,202h

                   mov ax,0b800h

                   mov es,ax

                   mov di,12*160+36*2

                   mov cx,9

      s:  mov al,[si]

          mov es:[di],al

          mov ah,4

          mov es:[di+1],ah

          inc si

          add di,2

          loop s

          mov ax,4c00h

          int 21h

do0end: nop

code ends

end start

=================================================

而我们现在要做的,就是根据本章的知识点对上述代码进行修改,然后实现笔记最开始图示中的最终效果。现在,我们来对相关代码一段一段分析。

首先,看这段代码:

=================================================

start: mov ax,cs

       mov ds,ax

       mov si,offset do0

       mov ax,0

       mov es,ax

       mov di,200h

       mov cx,offset do0end-offset do0

       cld

       rep movsb

=================================================

1、mov ax,cs

      mov ds,ax

将代码段地址CS的值赋给AX,然后将AX的值传送到DS这个寄存器中,使默认的段地址DS的值成为CS的地址;

2、mov si,offset do0,通过offset,获得do0处的偏移地址,然后赋值给si;这样就将ds:si指向了源地址。

3、mov ax,0

       mov es,ax

       mov di,200h

将段地址0,赋值给AX,然后将AX值传送到ES这个寄存器中,并将偏移值200H,赋值给di。这一段代码的目的,是为了后面执行rep movsb时,将do0到do0end之间的中断程序代码复制到0000:0200做寻址的准备。

因为一般情况下,从0000:0200至0000:02FF的256个字节的空间所对应的中断向量表项都是空的,所以可以将do0到do0end之间的中断程序代码复制到0000:0200这个位置,并将0000:0200这个地址做入中断处理程序的入口地址。

4、mov cx,offset do0end-offset do0。这里涉及两个步骤,第一,通过计算offset do0end 减去 offset do0 获得do0的代码长度;第二,将代码长度赋值给CX,将CX设置为传输长度。

5、  cld

       rep movsb

       设置中断向量表

cld,是为了设置传输方向为正。

rep movsb,MOVSB 的英文是 move string byte,意思是搬移一个字节,它是把 DS:SI 所指地址的一个字节搬移到 ES:DI 所指的地址上,搬移后原来的内容不变,但是原来 ES:DI 所指的内容会被覆盖,而且在搬移之后 SI 和 DI 会自动向下一个要搬移的地址。

一般而言,通常程序一般并不会只搬一个字节,通常都会重复许多次,如果要重复的话,就得把重复次数 ( 也就是字串长度 ) 先记录在 CX 寄存器,并且在 MOVSB 之前加上 REP 指令,REP 是重复 (repeat) 的意思。

一般而言汇编语言源文件的每一行都只有一个指令,但 REP MOVSB 却可以在同一行写两个指令,当然分开写也是一样的。

在rep movsb指令之后,出现了一段中文“设置中断向量表”,这是一段用中文表示的伪码,真正的代码在原书249页:

=================================================

mov ax,0

mov es,ax

mov word ptr es:[0*4],200h

mov word ptr es:[0*4+2],0

=================================================

需要我们在编写实验12程序时加上这一段代码。这一段的作用,就是把中断程序的入口地址0000:0200,写入中断向量0号表项中。0号表项的地址为0:0,其中0:0字单元存放偏移地址0200,0:2字单元存放段地址0000。

再来看这段代码:

=================================================

do0: jmp short do0start

       db "overflow!"

do0start:     mov ax,cs

                   mov ds,ax

                   mov si,202h

                   mov ax,0b800h

                   mov es,ax

                   mov di,12*160+36*2

                   mov cx,9

      s:  mov al,[si]

          mov es:[di],al

          mov ah,4

          mov es:[di+1],ah

          inc si

          add di,2

          loop s

          mov ax,4c00h

          int 21h

do0end: nop

=================================================

1、do0: jmp short do0start

       db "overflow!"

do0: jmp short do0start,通过JMP指令,跳转到除法溢出后显示“overflow!”字符串的代码段do0start;并用db定义了9个字节,对应 "overflow!"的字符串长度。

2、在do0start中:

mov ax,cs

mov ds,ax

mov si,202h

先将code代码段的段地址CS赋值给AX,再将AX传送到DS这个寄存器中,使默认的段地址DS的值成为CS的地址;

然后将偏移地址202h赋值给SI,因为jmp short do0start这条指令占了2个字节,所以 "overflow!"的偏移地址为202h。

3、mov ax,0b800h

      mov es,ax

      mov di,12*160+36*2

这段代码,是为了向80X25彩色字符模式(原书第188页)输出“overflow!”字符串,段地址为0b800h,用ES寄存器保存段地址0b800h;mov di,12*160+36*2,是将偏移地址12*160+36*2赋值给DI。

其中12代表第12行;160代表80个字(每个字2个字节),低位字节保存字符的ASCII码,高位字节保存字符的属性。

36代表第12行的36个字(每个字2个字节),低位字节保存字符的ASCII码,高位字节保存字符的属性。

这样就可以让“overflow!”字符串位于屏幕居中的位置并显示相应的颜色。

4、s:  mov al,[si]

          mov es:[di],al

          mov ah,4

          mov es:[di+1],ah

          inc si

          add di,2

          loop s

  这段循环体中,mov al,[si],默认的段寄存器DS结合SI,将“overflow!”字符串读取出来然后通过mov es:[di],al,将每一个字符输送到内存缓冲区的内存低位字节,然后通过 mov ah,4和mov es:[di+1],ah,将高位字节的属性设置为4。SI自加1,DI自加2。通过这段循环体,就完成了“overflow!”字符串向显示缓冲区传送。

有了对上述例程序12.3的理解之后,就可以将其修改为实验12的完整程序,代码如下:

=================================================

assume cs:code

code segment

start: mov ax,cs

       mov ds,ax

       mov si,offset do0

       mov ax,0

       mov es,ax

       mov di,200h

       mov cx,offset do0end-offset do0

       cld

       rep movsb

       mov ax,0

       mov es,ax

       mov word ptr es:[0*4],200h

       mov word ptr es:[0*4+2],0

;----------------除法溢出代码

       mov ax,1000H

       mov bl,1

       div bl

;----------------除法溢出代码

       mov ax,4c00h

       int 21h

do0: jmp short do0start

       db "divide error!"

do0start: mov ax,cs

          mov ds,ax

          mov si,202h

          mov ax,0b800h

          mov es,ax

          mov di,12*160+36*2

          mov cx,13

      s:  mov al,[si]

          mov es:[di],al

          mov ah,4

          mov es:[di+1],ah

          inc si

          add di,2

          loop s

          mov ax,4c00h

          int 21h

do0end: nop

code ends

end start

=================================================

在完整的程序里,把除法溢出代码也加了进去,并且把“overflow!”字符串改成了"divide error!",字符串的长度由9改成了13。

把实验12的完整程序代码编译成p1212.exe,在debug下调试运行,进一步加深对这些代码的理解:

先输入r指令观察,会发现当前的CS在076A这个位置,IP是0000,然后输入u 076A:0000进行观察。

可以看到,左边截图的源代码中,mov si,offset do0,在右边的debug显示中变成了 mov si,0034,这意味着中断处理程序代码的位置是从076A:0034开始。

我们来看看076A:0034处以后的指令是什么,输入u 076A:0034进行观察。

可以看到,左边截图的源代码中,从do0: jmp short do0start开始,对应了右边的debug显示区域中的代码内容。

但这时候,这些代码只是一些数据,在没有被复制到0000:0200处并执行完之前,还不能称之为中断处理程序(原书第243页)。

再来看看在debug中,当前0000:0200处是什么内容,输入d 0000:0200观察:

除了一大堆0,现在是没有其他数据的。接下来,在debug中用t指令,执行 从mov ax,cs到 rep movsb的代码。

当CX归0的时候,再输入d 0000:0200观察:

显然,从076A:0034开始的do0中断处理程序代码已全部复制到0000:0200以后的位置。再输入U 0000:0200观察:

显然中断程序已经在内存中0000:0200安置完成,当除法溢出产生时,CPU会执行这一内存区域的中断处理程序。                                                                                              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值