[汇编]标志寄存器,端口,内外中断

标志寄存器(flag)

关于flag的两个指令:
pushf和popf:提供了直接访问标志寄存器的一种方式
pushf将标志寄存器的值压栈;
popf从栈中弹出数据送入flag。

  • ZF :0标志位。当前运算结果为0 为真,否则为假

  • PF :奇偶标志位。判断所有bit位中1的个数,偶数为真,奇数为假

  • SF :符号标志位。结果为负 则为真;非负为假

  • CF :进位标志位。记录运算数据最高有效位的进位/借位值。
    配合adc指令

    格式:adc object1,object2;
    功能:object1 = object1 + object2 + CF
    应用:adc配合add(要先进位)
    

    配合sbb指令

    格式:adc object1,object2;
    功能:object1 = object1 - object2 - CF
    应用:sbb配合sub
    
  • OF :溢出标志位。数据超过机器表示范围为真

  • DF :方向标志位。控制每次操作后si、di的增减。
    df = 0,每次操作后si、di递增;
    df = 1,每次操作后si、di递减。
    主要用于串传送。
    关于串传送的3个指令
    movsb指令mov es:[di] , byte ptr ds:[si]
    if(df=0) inc si , inc di
    if(df=1) si=si-1,di=di-1
    应用:将ds:si指向内存单元中的字节送入es:di中,根据df进行增减;
    movsw指令 mov es:[di] ,word ptr ds:[si]
    if(df=0) add si,2 ; add di,2
    if(df=1) sub si,2 ; sub di,2
    应用:同movsb,只是传送的数据为字型。
    (两者配合 rep指令)
    以rep movsb功能为例:
    s:movsb
    loop s
    即,根据cx值,重复执行后面的串传送指令,循环实现cx个字符的传送。

注意
一:数据交换类指令不会引起标志位的变化。

二:数据传送类指令不会引起标志位的变化。

三:算数运算类指令会影响标志位变化。

所以:
有的指令的执行是影响标志寄存器的,比如,add、sub、mul、div、inc、or、and 等,它们大都是运算指令(进行逻辑或算术运算);
有的指令的执行对标志寄存器没有影响,比如,mov、push、pop 等,它们大都是传送指令。

PS:关于OF,CF的区分以及cmp指令的配合,请看这里
PS:关于不同指令对flag的具体影响,请看这里

端口

前言定义

和CPU通过总线相连的芯片除各种存储器,还有:

  • 各种接口卡的接口芯片(网卡、显卡),芯片控制接口卡工作
  • 主板上的接口芯片,cpu通过芯片对外设访问
  • 其他芯片,存储相关系统信息/进行输入输出处理

anyway,CPU操控它们的时候,把这些芯片、存储器统一试做一个巨大无比的逻辑存储区,我们称之为内存地址空间。

这些芯片中,都由一组可由cpu读写的寄存器。
这些寄存器有以下特点:

  1. 都和cpu的总线相连(通过芯片)
  2. CPU对寄存器读写时通过控制线向芯片发送端口读写命令

所以从CPU角度,如同逻辑存储器一样,把这些寄存器都当做端口,统一编制,形成一个端口地址空间。
所以CPU可以直接读写以下3个地方的数据:

  • cpu 内部寄存器
  • 内存单元
  • 端口

端口处理

虽然端口本质是芯片连接的存储器,但你不能像内存地址空间那样用mov、push、pop来操作。
端口读写指令有两条:in和out 。只能使用ax或al来存放从端口中读入/发送的数据。访问8位端口用al,16位用ax。
eg:in al,20h #从20h端口读入一个字节
out 20h ,al #向20h端口写入一个字节

中断

中断信息要求CPU马上进行某种处理,并向所要进行的该种处理提供了必备的参数的通知信息。
使CPU不再接着向下执行指令,而是转去处理这个特殊信息。

内中断

前言

中断来源
中断信息要包含识别来源的编码,来区分接受到的来源
通常用中断类型码(字节型数据)来区分。
中断处理程序
收到终端信息后,通过我们编写的中断处理程序来处理。
而中断程序的入口(中断向量),则要凭借中断向量表来记录。
它通常在程序的内存地址0处。
(中断向量表中的一个表占两个字)
中断过程凭借call指令记录原来的CS:IP值,执行完中断处理程序切换。

中断过程

  1. 取得中断类型码N
  2. pushf
  3. TF=0,IF=0
  4. push CS
  5. push IP
  6. IP = N4 ,CS=N4+2

中断处理程序

  1. 保存用到的寄存器
  2. 处理中断
  3. 恢复用到的寄存器
  4. iret指令返回
    iret指令作用:
    pop IP
    pop CS
    popf

外中断

计算机除了能够运算,还要有I/O能力。
IO能力体现在两个问题上:

CPU从何处得到外设输入

外设输入首先送入相关的接口芯片的端口中,再由端口芯片送到cpu;CPU向外设输出也是端口→芯片→外设。CPU对外设输出的控制指令同理。
关键点在端口

CPU怎样得到外设输入

外设输入随时到达,cpu需要及时收到并处理。
这种情况的处理机制即外中断。
外中断有以下两种类型:

  1. 可屏蔽中断
    可屏蔽,意味着CPU可以不响应这个外中断。响应与否的条件是标志寄存器IF位的设置:IF=1 响应中断,否则不响应。

    可屏蔽中断信息来自cpu外部,中断类型码是通过数据总线送入cpu的。(相比而言,内中断在cpu内部产生)

    IF位指令:
    sti #IF=1
    cli #IF=0

  2. 不可屏蔽中断
    CPU必须响应的外中断。8086cpu中,其中断类型码固定为2,所以不需要取码。其他流程同内中断。


几乎所有外设的外中断都是可屏蔽中断。不可屏蔽中断是系统的紧急情况。

INT指令

系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用。
int指令能够主动触发内中断。
格式:int n,n为中断类型码,功能是引发中断过程。

扩展(BIOS)

系统板的ROM中存放着一套程序,成为BIOS。BIOS主要有以下内容:

  • 硬件系统的检测和初始化程序
  • 外部中断和内部中断的中断例程
  • 用于硬件设备进行I/O操作的中断例程
  • 其他和硬件系统相关的中断例程

DOS也提供了中断例程。从os角度看,DOS的中断例程就是os向程序员提供的编程资源。BIOS和DOS的这些中断例程实现了程序员编程时经常用到的功能。

直接定址表

数据标号

定义示例

定义:包含内存单元地址和内存单元长度的标号。
例子:

assume cs:code
code segment

assume cs:code
code segment

a: db 1,2,3,4,5,6,7,8,9
b: dw 0
start:
mov si,offset a #mov si,0

mov di,offset b
mov cx,8
s: mov al,cs:[si] #mov al,a[si]

mov ah,0
add cs:[bx],ax #mov b,ax

inc si
loop s
mov ax,4c00h
int 21h
code ends
end start

如例子中的绿色字体,他们后面没有‘ :’,同时描述了地址和长度(长度由数据类型定义关键字确定)。

mov ax,b 相当于 mov ax,cs:[8]
mov b,2 相当于 mov word ptr cs:[8] , 2
mov al ,a[3] 相当于 mov al,cs:0[3]

其他段示例

定义示例是在代码段中定义数据。在其他段中使用数据标号还要注意:

assume cs:code,ds:data
data segment
a: db 1,2,3,4,5,6,7,8,9
b: dw 0
data ends
code segment
start:
mov ax,data
mov ds,ax

在代码段code中用data段的数据标号a、b,你就要用assume将一个寄存器和data相联。

想在段中用数据标号直接访问数据,要用伪指令assume将该标号所在的段和某个段寄存器联系起来,以确定标号的段地址所在寄存器。(段寄存器不会真的存放该段地址,只是声明作用。我们还需要在后续代码进行段操作)

标号当做数据定义示例

data segment
a db 1,2,3,4,5,6,7,8,9
b dw 0
c dw a,b #相当于 offset a,offset b
data ends

数据标号c处存储的两个字型数据为标号a、b的偏移地址
也可以是: c dd a,b
存储的两个双字型数据为标号a、b的偏移地址和段地址。
相当于c dw offset a,seg a,offset b, seg b

seg操作符:取得标号段地址

直接定址表

将给出的数据计算得到结果的方法,转化为给出数据作为查表的依据,通过查表得到结果的方法。
像这种可以通过依据数据,直接计算出所要找的元素的位置的表,我们称其为:直接定址表。

示例程序:

以十六进制形式在屏幕中间显示给定的字节型数据

;用al传送要显示的数据

showtyte:       
 jmp short show
 table db ‘0123456789ABCDEF’    ;字符表
 show: 
     push bx
     push es
         
     mov ah,al
     shr ah,1
     shr ah,1
     shr ah,1
     shr ah,1     ;右移4位,ah中得到高4位的值。
     and al,00001111b    ;al中为低4位的值
     
     mov bl,ah
     mov bh,0
     mov ah,table[bx]           ;用高4位的值作为相对table的偏移,取得对应字符。
     
     mov bx,0b800h
     mov es,bx
     mov es:[160*12+40*2],ah            ;显示高4位的十六进制字符码
     
     mov bl,al
     mov bh,0
     mov al,table[bx]            ;用低4位的值作为相对table的偏移,对得对应字符
     
     mov es:[160*12+40*2+2],al ;显示低4位的十六进制字符码
     
     pop es
     pop bx
     ret

定址表配合子程序

可以将子程序的入口地址存储在一个表中,类似一个函数指针数组

下面的示例中,子程序的表中位置和功能号相对应:
功能号*2 = 对应的功能子程序在地址表中的偏移

实现一个子程序setscreen,为显示输出提供如下功能:

清屏;
设置前景色;
设置背景色;
向上滚动一行。

  • 入口参数说明:
  1. 用ah寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;
  2. 对于2、3号功能,用al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}
  • 各种功能的实现:
  1. 清屏:将显存中当前屏幕中的字符设为空格符;
  2. 设置前景色:设置显存中当前屏幕中处于奇地址的属性字节的第0、1、2位;
  3. 设置背景色:设置显存中当前屏幕中处于奇地址的属性字节的第4、5、6位;
  4. 向上滚动一行:依次将第n+1行的内容复制到第n行处,最后一行为空。

解决:

普通方法:
setscreen: jmp short set

       table: dw sub1,sub2,sub3,sub4
       
       set: push bx
       
       cmp ah,3    ;判断功能号是否大于3

       ja sret

       mov bl,ah

       mov bh,0

       add bx,bx              ;根据ah中的功能计算对应子程序在table表中的偏移

       call word ptr table[bx]          ;调用对应的功能子程序
       
       sret: pop bx

       ret
直接定址表配合子程序:
setscreen:   
              cmp ah,0
              je do1
              cmp ah,1
              je do2
              cmp ah,2
              je do3
              cmp ah,3
              je do4
              jmp short sret
              
 do1:     call sub1
             jmp short sret
 do2:     call sub2
             jmp short sret
 do3:     call sub3
             jmp short sret
 do4:     call sub4

 sret:     ret

子程序实现在这里:

;清屏

sub1:       push bx

              push cx

              push es

              mov bx,0b800h

              mov es,bx

              mov bx,0

mov cx,2000

sub1s: mov byte ptr es:[bx],’ ‘

           add bx,2

           loop sub1s

           pop es

           pop cx

           pop bx

           ret

 

;设置前景色

sub2:       push bx

              push cx

              push es

 

              mov bx,0b800h

              mov es,bx

              mov bx,1

              mov cx,2000

 sub2s: and byte ptr es:[bx],11111000b

              or es:[bx],al

              add bx,2

              loop sub2s

 

              pop es

              pop cx

              pop bx

              ret

 

;设置背景色

sub3:       push bx

              push cx

              push es

 

              mov cl,4

              shr al,cl

              mov bx,0b800h

              mov es,bx

              mov bx,1

 sub3s: and byte ptr es:[bx],10001111b

              shl al,1

              shl al,1

              shl al,1

              shl al,1

              or es:[bx],al

              add bx,2

              loop sub3s

              pop es

              pop cx

             pop bx
              pop bx
              ret

 

;向上滚动一行

sub4:       push cx
              push si
              push di
              push es
              push ds
              mov si,0b800h
              mov es,si
              mov ds,si
              mov si,160            ;ds:si指向第n+1行
              mov di,0                ;es:di指向第n行
              cld
              mov cx,24

 sub4s:  push cx
              mov cx,160           ;一行的长度为160个字节。
              rep movsb             ;一次复制一行
              pop cx
              loop sub4s
              
              mov cx,80
              mov si,0

 sub4s1:
 mov byte ptr [160*24+si],’ ‘   ;最后一行清空
              add si,2
              loop sub4s1
              
              pop ds
              pop es
              pop di
              pop si
              pop cx
              ret

实际上直接定址表的方法还能达到解耦的效果,方便扩充维护。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值