王爽-汇编语言-简易笔记

汇编语言

基础知识

汇编语言的组成

  • 汇编指令

    • 有对应机器码的助记符
  • 伪指令

    • 没有对应机器码,由编译器执行
  • 其他符号

    • 由编译器识别,没有对应机器码,如+、-、*、/等

存储器

  • 内存

    • 内存单元

      • 8bit为一个内存单元(一个字节)

指令和数据的区别

CPU对存储器的读写

  • 需要以下3类信息交互

    • 内存单元的地址(地址信息)
    • 器件的选择,读或写的指令(控制信息)
    • 读或写的数据(数据信息)
  • ·总线

    • 在计算机中专门连接CPU和其他芯片的导线

      • 地址总线

        • 地址总线的宽度决定了CPU寻址能力

          • 2^n
      • 控制总线

        • 控制总线的宽度决定了CPU对系统中其他器件的控制能力
      • 数据总线

        • 数据总线的宽度决定了CPU和外界的数据传送速度

硬件概述

  • 主板

  • 接口卡

    • CPU通过总线向接口卡发出命令,接口卡根据CPU命令来控制外设
  • 各类存储器芯片

    • 随机存储器(RAM)

      • 用于存放供CPU使用的绝大部分程序和数据

        • 主板上的RAM
        • 扩展插槽上的RAM
        • 接口卡上的RAM
      • 必须带电存储,关机后存储内容丢失

    • 只读存储器(ROM)

      • BIOS
      • 只能读取不能写入

注意点

  • CPU可以直接使用的信息在存储器(内存)中存放

寄存器

CPU组成

  • 运算器

    • 进行信息处理
  • 控制器

    • 控制各种器件进行工作
  • 寄存器(CPU工作原理)

    • 进行信息存储
  • 内存总线

    • CPU的总线叫内部总线,连接CPU的各个组成部分,在它们之间进行数据传送

通用寄存器

  • AX、BX、CX、DX

    • 均可分为两个8为寄存器

      • 例如:AX可分为ah和al
  • 一个16位寄存器所能存储的数据的最大值为?

    • 2^16-1

      • 因为2^16 = 100000000,而寄存器只有16位,所以要-1

几条汇编指令

  • mov ax,2000H

    • 把数据2000H放进寄存器AX中
  • add ax,bx

    • 把bx中的数据加到ax中

物理地址

  • 所有的内存单元构成的存储空间是一个一维的线性空间

    • 每一个内存单元在这个空间中都有唯一的地址,我们将这个唯一的地址成为物理地址

8086CPU的地址加法器

  • 8086CPU是16位CPU

    • 运算器一次最多可以处理16位的数据,但它却有20位地址总线

      • 所以我们需要一种特殊的方法来将两个16位地址合成一个20位地址
  • 物理地址 = 段地址*16 + 偏移地址

    • 段地址*16也就是将二进制存放的段地址左移4位

  • 实际中内存并没有分段,这个分段来自CPU的逻辑内存地址(就是说我把你看成一段一段的,便于操作)

段寄存器

  • CS 代码段寄存器

    • 存放指令的段地址
  • IP 指令指针寄存器

    • 存放指令的偏移地址
  • 任何时刻,CPU都会将CS:IP指向的内容作为指令指向

  • 8086CPU的工作原理

    • (1)先把CS、IP中的数据送入地址加法器
    • (2)地址加法器运算物理地址(CS)*16 + (IP)
    • (3)将运算出的物理地址传送进输入输出电路
    • (4)输入输出电路将物理地址送上地址总线
    • (5)从指定的物理内存开始存放的机器指令 通过数据总线送入CPU
    • (6)将这些机器指令加载进指令缓冲器
    • (7)IP自动增加读入的机器指令的长度(字节)
    • (8)指令被加载进执行控制器,执行该指令
  • 修改CS、IP的指令

    • 转移指令 jmp

      • jmp 段地址:偏移地址

        • 修改CS:IP
      • jmp 某一合法寄存器

        • 修改IP

实验1

  • Debug

    • R

      • 查看、改变CPU寄存器的内容
    • D

      • 查看内存中的内容
    • E

      • 改写内存中的内容
    • U

      • 将内存中的机器码翻译成汇编指令
    • T

      • 执行一条汇编指令
    • A

      • 以汇编指令的格式在内存中写入一条机器指令

注意点!

  • 寄存器位数的溢出(越界问题)

    • 两个寄存器相加后不止16位的话,就只保留后16位在寄存器中
  • CPU可以用不同的段地址和偏移地址来形成同一个物理地址

  • 偏移地址16位,其变换范围为0~FFFFH

  • 在汇编中,一个数据不能用字母来开头,必须加个0

寄存器(内存访问)

内存中字的存储

  • 字单元

    • 存放一个字型数据的内存单元,有两个地址连续的内存单元构成

      • 地址小的是低位,大的是高位

DS

  • 用来存放要访问数据的段地址

    • mov ax,[0]

      • 直接指定内存地址时,ds内存放的是段地址,偏移地址由mov传入

  • 核心

    • 先进后出
  • SS:SP

    • 任意时刻,SS:SP指向栈顶元素
  • 栈操作

    • push

      • 入栈

        • (1)SP = SP-2
        • (2)向SS:SP中送入数据
    • pop

      • 出栈

        • (1)从SS:SP指向的字单元中读取数据
        • (2)SP = SP+2
  • 最大容量

    • 栈顶的变换范围是0~FFFFH

      • 64KB
  • 8086CPU只记录栈顶,栈空间的大小需要我们自己管理

  • 数据段

    • DS
  • 代码段

    • CS:IP
  • 栈段

    • SS:SP

第一个程序

源程序

  • assume cs:codesg

codesg segment

    mov ax,0123H
    mov bx,0456H
    add ax,bx
    add ax,ax

    mov ax,4c00H
    int 21

codesg ends

end

编写

  • 伪指令

  • XXX segment

    XXX ends

    • 代码段在这中间
  • end

    • 整个汇编程序结束的标记
  • assume

    • 假设

      • 假设某一段寄存器的程序和程序中的某一个代码段相关联

        • 类似于备注?
  • 标号

    • codesg

      • 标号指代了一个地址

        • CS:codesg
  • 程序返回

    • 概念

      • 把CPU的控制权交还给使它得以运行的程序
    • 代码

      • mov ax,4c00H
        int 21H

        • 固定加入这两段代码就可以实现程序返回

编译

  • (1)运行masm

  • (2)输入asm文件名称

  • (3)输入列表文件名称

  • (4)输入交叉引用文件名称

  • 简易方式

    • masm 文件路径;

      • 有分号!

连接

  • (1)运行link

  • (2)创建obj文件

  • (3)连接映像文件名称

  • (4)连接库文件

  • 简易方式

    • link 文件名;

      • 有分号!

执行

  • masm 程序

  • 谁将我们要执行的程序加载入了内存?

    • command
  • 程序结束后返回到哪里?

    • command

[BX]和loop指令

寄存器

  • bx

    • [bx]

      • 存放偏移地址
  • cx

    • loop的计数器
  • dx

    • 累加寄存器

loop

  • loop的本质是更改IP寄存器,实现循环

  • 代码段

    • s: add dx,ax
      loop s

新debug指令

  • g 内存地址

    • 跳转到指定内存地址开始执行
  • p

    • 一次将loop循环执行完毕

新汇编指令

  • inc

    • 类似 ++ 使目标中内容+1

      • 可用于loop操作,类似其他语言里的 i
      • inc bx

段前缀

  • 用于显式地指明内存单元的段地址的

    • ds:
    • cs:
    • ss:
    • es:

安全的代码段

  • 0:200~0:2ff

    • 这段空间中一般没有其他数据

注意点

  • 汇编源程序中,数据不能以字母开头

    • 错误

      • ffffH
    • 正确

      • 0ffffH
  • idata

    • 代表常量,类似数学中的c
  • 汇编源程序中的 [idata] 问题

    • 错误

      • mov al,[0]
    • 正确

      • mov al,ds:[0]

        • 用[idata]传地址,需要加上段地址寄存器
    • 正确

      • mov al,[bx]

        • 用寄存器传地址可以

包含多个段的程序

dw

  • 定义数据(开辟内存空间)

分段

  • 数据段
  • 代码段
  • 栈段

更灵活的定位内存地址的方法

位操作指令

  • and

    • 两个都是1才是1,其他均为0
  • or

    • 只要有一个是1就是1,都是0就是0

db和dw的区别

  • dw只能定义一个字,小于或大于一个字的都用db

[bx+idata]

  • 常用形式

    • [200+bx]
    • 200[bx]
    • [bx].200
  • 灵活运用

    • [bx+si]和[bx+di]

      • ((ds)*16+(dx0)+(si))
    • [bx+si+idata]和[bx+di+idata]

      • 一个意思

不同的寻址方式的灵活应用

  • 循环可以嵌套

注意点

  • mov ax,[bx] => ax=[bx+1,bx]

    • 也就是ax里的内容就是把目标内存地址的内容当作al,下一个内存地址中的内容当作ah

实验7.9

  • assume cs:codesg,ds:datasg,ss:stack

datasg segment
db '1. display ’
db '2. brows ’
db '3. replace ’
db '4. modify ’
datasg ends

stack segment
dw 0,0,0,0,0,0,0,0
stack ends

codesg segment
start:mov ax,datasg
mov ds,ax

mov ax,stack
mov ss,ax
mov sp,16


mov si,0
mov bx,0
mov cx,4

  s0:push cx
 mov cx,4

s:mov al,[bx+si+3]
  and al,11011111B
  mov [bx+si+3],al
  inc si
loop s

mov si,0
add bx,16
pop cx
  loop s0

mov ax,4c00H
int 21H

codesg ends

end start

数据处理的两个基本问题

问题

  • 处理的数据在什么地方?
  • 要处理的数据有多长?

[…]

  • bx
  • si
  • di
  • bp

特殊点

  • reg

    • 寄存器
  • sreg

    • 段寄存器

寻址方式

  • [idata]

  • [寄存器]

  • [寄存器+idata]

  • [寄存器+寄存器]

    • [bx+si]
    • [bx+di]
    • [bp+si]
    • [dp+di]
  • [寄存器+寄存器+idata]

处理数据的长度格式

  • byte ptr

    • 修改一个单元(位)
  • word ptr

    • 修改两个单元(字)

div(除法)

  • 除数

    • 8位
    • 16位
  • 被除数

    • 除数为8位

      • 被除数为16位

        • 存在AX中
    • 除数为16位

      • 被除数为32位

        • DX存高16位
        • AX存低16位
  • 结果

    • 除数为8位

      • al存商
      • ah存余数
    • 除数为16位

      • AX存商
      • DX存余数

伪指令dd

  • 定义一个双字型数据
  • 和db,dw一个道理

dup

  • db 重复次数 dup (重复的字节型数据)
  • dw 重复次数 dup (重复的字型数据)
  • dd 重复次数 dup (重复的双字型数据)

转移指令的原理

定义

  • 可以修改IP,或同时修改CS和IP的指令统称为转移指令

操作符offset

  • 取得标号的偏移地址

    • mov ax,offset 标号

无条件转移指令

  • jmp

    • 依据位移来进行转移的jmp指令

      • jmp short 标号

        • 段内短转移

          • 8位位移

            • -127~128
      • jmp near ptr 标号

        • 段内近转移

          • 16位位移

            • -32768~32767
    • 转移的目的地址在指令中的jmp指令

      • jmp far ptr 标号

        • 段间转移(远转移)

          • 例如:EA20001000
    • 转移地址在寄存器中的jmp指令

      • jmp 16位reg

        • 最早学的jmp指令

          • 例如:jmp ax
    • 转移地址在内存中的jmp指令

      • jmp word ptr 内存单元地址(段内转移)

        • 把指定内存单元处的一个字作为转移的目的偏移地址
      • jmp dword ptr 内存单元地址(段间转移)

        • 把指定内存单元处的两个字作为转移目的地址

          • (CS) = (内存单元地址+2)
          • (IP) = (内存单元地址)

计算位移的原理

  • 简便算法:取jmp 标号 下一个指令的偏移地址与目标标号偏移地址进行运算

有条件转移指令

  • jcxz

    • 等价于C语言的 if((cx)==0) jmp short 标号

      • cx=0的时候才会执行的指令,cx!=0什么也不做
  • loop

    • (cx)–
      if((cx)!=0) jmp short 标号

      • cx先减1,在判断

注意点

  • 注意各种转移位移的越界问题!
  • 在这里补码知识很重要!
  • 转移标号后的机器码不变 实验8

CALL和RET指令

二者均是转移指令

ret和retf

  • ret

    • pop IP
  • retf

    • pop IP
      pop CS

call

  • call指令的通用操作

    • (1)将当前的IP或CS和IP压入栈中
    • (2)转移
  • call的执行原理

    • (1)先读入call指令,此时call指令的机器码在指令缓冲器中
    • (2)这时候IP自动增加,指向call指令后的下一个字节
    • (2)等IP增加完以后,指令缓冲器中的call机器码就会载入执行控制器,这时候push IP中的IP就指向的就是call指令的下一个字节了
  • call无法实现段内短转移

  • 依据位移进行转移的call指令

    • call 标号

      • 段内转移
      • push IP
        jmp near ptr 标号
  • 转移的目的地址在指令中的call指令

    • call far ptr 标号

      • 段间转移
      • push CS
        push IP
        jmp far ptr 标号
  • 转移地址在寄存器中的call指令

    • call 16位reg

      • 段内转移
      • push IP
        jmp 16位reg
  • 转移地址在内存中的call指令

    • call word ptr 内存单元地址

      • push IP
        jmp word ptr 标号
    • call dword ptr 内存单元地址

      • push CS
        push IP
        jmp dword ptr 标号

call和ret配合使用

  • 子程序

  • 子程序框架

    • assume cs:code

code segment
main: …

call sub1 ;调用子程序1

mov ax,4c00H
int 21H

sub1: …

call sub2

ret ;子程序返回

sub2: …


ret
code ends

end main

mul指令

  • 乘法指令

  • 注意点

    • 两个相乘的数,位数必须相同(都是8位或都是16位)
  • 乘数

    • 8位

      • 一个默认放在al中

      • 另一个

        • 8位reg
        • 内存字节单元
    • 16位

      • 一个默认放在AX中

      • 另一个

        • 16位reg
        • 内存字单元
  • 结果

    • 8位

      • AX
    • 16位

      • 高位默认在DX,低位默认在AX
  • 格式

    • mul reg
      mul 内存单元
  • 例子

    • 100*10

      • mov al,10
        mov bl,100
        mul bl

        • ax=1000(03E8H)
    • 100*10000

      • mov ax,100
        mov bx,10000
        mul bx

        • (ax)=4240H
        • (dx)=000FH

模块化程序设计

  • 参数和结果传递的问题

    • (1)参数N存储在什么地方?
    • (2)计算得到的数值,存储在什么地方?
  • 批量数据的传递

    • 当参数过多的时候我们该怎么办?

      • 将批量数据存放到内存中,再把它们所在内存空间的首地址放在寄存器中,传递给需要的子程序
    • 例子

      • 将data段中的字符串转化为大写
  • 寄存器冲突的问题

实验10

  • 显示字符串

    • pp99.exe
  • 解决除法溢出的问题

    • pp999.exe
  • 十进制转换

    • pp9999.exe
    • int(十六进制数据/10) + 30h = 对应的十进制ASCII码

标志寄存器

flag寄存器

  • 与其他寄存器不同,标志寄存器是按位来起作用的

注意点

  • 传送指令不影响标志寄存器

    • mov、push、pop

ZF标志

  • flag的第6位

  • 操作指令结果

    • 为0

      • ZF = 1
    • 不为0

      • ZF = 0

PF标志

  • flag的第2位

  • 操作指令结果中所有 bit位中 1的个数

    • 偶数

      • PF = 1
    • 非偶数

      • PF = 0

SF标志

  • flag的第7位

  • 操作指令执行后的结果

      • SF = 1
    • 非负

      • SF = 0

CF标志

  • flag的第0位

  • 在进行无符号数运算时,它记录了运算结果的最提高有效位向更高位的进位值,或从更高位的借位值

    • 进位

      • 98H + 98H
    • 退位

      • 97H - 98H

        • 97H要向最高位退位变成197H才能进行运算,此时CF = 1

OF标志

  • flag的第11位

    • 溢出标志位
  • 是否发生了溢出

      • OF = 1
      • OF = 0
  • 溢出是什么?

    • 在进行有符号数运算时,如果结果超出了机器所能表示的范围就称为溢出

    • mov al,98
      add al,99

      • al位8位寄存器,能表示的数的范围是 -128~127
      • 而98 + 99 = 197,溢出
    • mov al 0F0h
      add al,088h

      • 0F0h 为有符号数-16的补码
        088h 为有符号数-120的补码

        • -16-120 = -136 al为8位寄存器,溢出
  • CF和OF的区别

    • CF是对无符号数运算有意义的标志位
    • OF是对有符号数运算有意义的标志位
  • 再来恶补一下计算机码!

    • 计算机码:计算机在实际存储数据的时候,采用的编码规则(二进制规则)
      计算机码:源码、反码、补码
      Ps:数值本身最左边一位是符号位,正数为0,负数为1;

源码:数据本身从十进制转换为二进制的结果(正数0,负数1)
反码:针对负数,符号位不变,其他位取反
补码:针对负数,反码+1
(正数的原码、反码、补码一致)
系统中存在两个0:+0和 -0
+0: 00000000
-0: 10000000 原码
取反:11111111
补码:00000000

	- 取反是不改变符号的..!!!!

		- 所以0F0h(1开头)是负数-16的补码,

而7Dh(0开头)却是个正数

adc指令

  • 带进位加法指令

  • 格式

    • adc 操作对象1,操作对象2

      • adc ax,1
  • 功能

    • 操作对象1 = 操作对象1 + 操作对象2 + (CF)

      • (ax) = (ax) + 1 +CF
  • 相当于多加了个CF的add

  • 我们为什么需要加上CF的值?

    • 分布计算

      • add al,dl
        adc ah,dh

        • add al,dl计算后如果需要进位,CF就等1,这时候再用adc ah,dh来计算,就能求出精确的值

          • add128 128加法器

            • assume cs:code

data segment
db
data ends

stack segment
db 16 dup (0)
stack ends

code segment
start: mov ax,data
mov ds,ax

mov ax,stack
mov ss,ax
mov sp,16

call add128

add128: push ax
push cx
push si
push di

sub ax,ax ;将CF设置为0


mov cx,8

s: mov ax,[si]
adc ax,[di]
mov [si],ax

inc di
inc di
inc si
inc si

loop s

pop di
pop si
pop cx
pop ax

ret 

code ends

end start

			- 这里的4个inc 不能用 2个add来代替

				- 因为add会影响CF、SF、OF
				- 而inc只影响PF

sbb指令

  • 带借位减法指令

  • 格式

    • sbb 操作对象1,操作对象2
  • 功能

    • 操作对象1 = 操作对象1 + 操作对象2 +(CF)
  • 同adc

cmp指令

  • 比较指令

    • 就是用来比大小的!
  • 格式

    • cmp 操作对象1,操作对象2
  • 功能

    • 计算 操作对象1 - 操作对象2

      • 做减法,但不保存结果,只改变标志寄存器的内容
  • 各种比大小的组合

    • cmp ax,bx

      • ZF = 1

        • (ax) = (bx)
      • ZF = 0

        • (ax) != (bx)
      • CF = 1

        • (ax) < (bx)
      • CF = 0

        • (ax) >= (bx)
      • SF

        • 因为存在溢出的问题,所以单个SF标志无法判断ax和bx的关系,所以需要配上of

        • SF = 1, OF = 0

          • 结果正确,为负,ax < bx
        • SF = 0, OF = 0

          • 结果正确 ,为正,ax > bx
        • SF = 1, OF = 1

          • 结果不正确,大小关系应该相反,ax > bx
        • SF = 0, OF = 1

          • 结果不正确,大小关系应该相反,ax < bx

检测比较结果的条件转移指令

  • 根据条件修改IP的指令

  • 因为cmp可以进行两种比较,所以根据cmp指令的比较结果进行转移的指令也分为两种

    • 根据无符号数的比较结果

      • 检测ZF、CF

        • je

          • 等于则转移

            • ZF = 1

              • equal
        • jne

          • 不等于则转移

            • ZF = 0

              • not equal
        • jb

          • 低于则转移

            • CF = 1

              • below
        • jnb

          • 不低于则转移

            • CF = 0

              • not below
        • ja

          • 高于则转移

            • CF = 0 && ZF = 0

              • above
        • jna

          • 不高于则转移

            • CF = 1 || ZF = 1

              • not above
    • 根据有符号数的比较结果

      • 检测SF、OF

DF标志和串传送指令

  • DF标志

    • flag的第10位
  • 串传送指令

    • 原理

      • DF = 0

        • 每次操作后si、di递增
      • DF = 1

        • 每次操作后si、di递减
    • 传送一个字节

      • 格式

        • movsb
      • 功能

        • (1)((es)*16 + (di)) = ((ds)*16 + (si))

        • (2)如果

          • DF = 0

            • (si) = (si) + 1
            • (di) = (di) + 1
          • DF = 1

            • (si) = (si) - 1
            • (di) = (di) - 1
    • 传送一个字

      • 格式

        • movsw
      • 功能

        • (1)((es)*16 + (di)) = ((ds)*16 + (si))

        • (2)如果

          • DF = 0

            • (si) = (si) + 2
            • (di) = (di) + 2
          • DF = 1

            • (si) = (si) - 2
            • (di) = (di) - 2
    • 与rep配合使用

      • rep movsb

        • 用汇编语言描述就是:

          • s: movsb
            loop s
        • rep 是根据 cx 的值重复执行后面的串传送指令

      • rep movsw

        • 同理
  • 如何设置DF?

    • cld指令

      • DF = 0
    • std指令

      • DF = 1

pushf和popf

  • 专门处理标志寄存器flag

标志寄存器在Debug中的表示

  • 就是r指令查看寄存器时,右下角那一排

    • 标志

      • 1

        • 0
    • OF

      • OV

        • NV
    • SF

      • NG

        • PL
    • ZF

      • ZR

        • NZ
    • PF

      • PE

        • PO
    • CF

      • CY

        • NC
    • DF

      • DN

        • UP

注意点

  • ASCII码转换不能用ax寄存器,要用al或ah,因为如果是16位寄存器的话,cf计算ASCII码不管怎么样都是0

实验11

  • pp11.exe

内中断

中断信息

  • 要求CPU马上进行某种处理,并向所要进行的该种处理提供了必备的参数的通知信息

内中断的产生

  • 4大类

    • 除法错误(比如div产生的除法溢出)
    • 单步执行
    • 执行 into 指令
    • 执行 int 指令
  • 中断源

    • 产生中断信息的事件
  • 相对应的中断源

    • 除法错误

      • 0
    • 单步执行

      • 1
    • 执行 into 指令

      • 4
    • int n

      • n

        • n是一个字节型数据

中断处理程序

  • 用来处理中断信息的程序

中断类型码

  • 用来定位中断处理程序

    • 定位程序就需要改变CS:IP的内容,所以我们需要:中断向量表

中断向量表

  • 中断向量

    • 中断处理程序的入口地址
  • 中断处理程序入口地址的列表

    • 在8086PC机中,其存放于0000:0000~0000:03FF,且位置不可更改,内容可更改
  • 与中断类型码 N 的对应关系

    • 偏移地址

      • 4N

        • 低地址
    • 段地址

      • 4N+2

        • 高地址

中断过程

  • CPU完成以下工作的过程

    • (1)(从中断信息中)得到中断类型码
    • (2)找到中断向量
    • (3)用其设置CS:IP
  • 思考

    • CPU执行完中断程序后,是不是该返回原来的执行点继续执行?
    • 答案是必然的,所以我们需要更完整的中断过程
  • 完整的中断过程

    • (1)(从中断信息中)得到中断类型码

    • (2)标志寄存器入栈(因为在中断过程中要改变标志寄存器的值,所以先将其保存在栈中)

    • (3)设置标志寄存器的第8位TF和第9位IF的值为0

      • TF和IF后面才会学到
    • (4)push CS

    • (5)push IP

    • (6)从 4N、4N+2 中读取中断程序地址入口,并设置给CS:IP

  • 代码表示

    • (1)获取中断类型码N
    • (2)pushf
    • (3)TF = 0,IF = 0
    • (4)push CS
    • (5)push IP
    • (6)(IP) = 4N,(CS) = 4N + 2
  • 这是由CPU自动运行的,程序员无法操纵

中断处理程序和 iret 指令

  • 中断处理程序的格式

    • (1)保存用到的寄存器
    • (2)处理中断
    • (3)恢复用到的寄存器
    • (4)用 iret 指令返回
  • iret 指令

    • 功能

      • pop IP
        pop CS
        popf

除法错误中断的处理

  • 返回 Divide overflow

编写中断程序

  • 我们这里用 0 号中断类型码来做例子

  • 整理思路

    • 我们需要做什么才能让取得 0 号中断类型码时运行我们编写的中断程序?

    • (1)要有中断处理程序

    • (2)中断处理程序得要放在内存中

      • 我们一般放在0000:0200~0000:002ff这段安全空间中,这本是中断向量表的空间,但这后面一段一般是空的,并不会存储中断向量
    • (3)更改中断向量表中0号所对应的中断向量 指向 中断处理程序的地址入口

    • 思路整理完毕,我们看框架

  • 框架与流程

    • assume cs:code

code segment

start: ;do0安装程序
;设置中断向量表

mov ax,4c00h
int 21h

do0: ;显示字符串"overflow!"
mov ax,4c00h
int 21h

code ends

end start

	- 拆分过程

		- (1)do0的安装
		- (2)do0的编写
		- (3)设置中断向量
  • 总流程

    • (1)安装

      • 思路

        • 用 rep movsb 来实现内容的快速复制

          • 设置es:di指向目标地址

          • 设置ds:si指向源地址

          • 设置cx为传输长度

            • mov cx,offset do0end - offset do0

              • do0: 显示字符串"overflow!"
                mov ax,4c00h
                int 21h

do0end: nop

				- 我们可以让计算机确定程序长度!

					- 用do0end的位移量 减去 do0的位移量

						- 编译器是可以识别+、-、*、/的哦!!!!

			- 设置传输方向为正

				- cld

					- DF = 0 正向

						- 我们要正向的

				- std

					- DF = 1 逆向

- (2)编写中断处理程序

	- 要注意"overflow!"这样的字符不能放在data段里,因为程序返回有可能会覆盖掉data段,所以我们需要将这些字符放在安全的空间里,那么我们就可以将其放进中断处理程序do0中,一起复制进0:200里

- (3)设置中断向量

	- mov ax,0

mov es,ax
mov word ptr es:[04],200h
mov word ptr es:[0
4 + 2],0

单步中断

  • 中断类型码

    • 1
  • 条件

    • CPU执行完一条指令后,若TF = 1,则产生单步中断
  • 中断过程

    • (1)取得中断类型码1
    • (2)将标志寄存器入栈,TF、IF入栈
    • (3)CS、IP入栈
    • (4)(IP) = 14,(CS) = 14 + 2
  • 为什么要有单步中断???

    • Debug 中的 t 功能就是单步中断的最好应用

      • 执行 t 指令时,TF = 1,经过中断过程后,开始执行中断处理程序,之后返回
      • 得要让TF = 0
      • 明白为什么要把标志寄存器入栈了吧!
        因为我们需要让TF = 0!不然就是个死循环!
    • 实现单步跟踪

响应中断的特殊情况

  • SS:SP的设置

    • SS:SP的设置必须连续,不然CPU中断以后就会使SS:SP指向错误的栈顶

实验12

  • qs2.exe

    • 中断处理程序

int指令

int n

  • n 就是中断类型码

    • 一般情况下,系统将一些有一定功能的子程序,一中断处理程序的方式提供给应用程序调用。
      我们在编程的时候,可以用int指令调用这些子程序。

中断例程

  • 中断处理程序的简称

编写基于 int 指令的中断例程

  • int 和 iret 的配合使用

    • 类似于 call 和 ret 的配合使用

    • iret 的功能相当于

      • pop IP
        pop CS
        popf
    • 基础案例

      • 用int 7ch实现 loop 指令

        • do0: push bp
          mov bp,sp ;让bp指向栈顶
          dec cx ;cx–
          jcxz lpret
          mov [bp + 2],bx ;让栈顶的后一个字变成标号s的偏移地址

ipret: pop bp
iret

BIOS和DOS所提供的中断例程

  • BIOS

    • 主板ROM中存放的一套程序
  • BIOS的内容

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

    • 和硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程

BIOS和DOS中断例程的安装过程

BIOS中断例程的应用

  • 闪烁字符

    • qs4.exe

    • 放置光标(10h 2号)

      • mov ah,2 ;放置光标
        mov bh,0 ;第0页
        mov dh,5 ;dh放行数
        mov dl,12 ;dl放列数
        int 10h
    • 在光标位置重复字符(10h 9号)

      • mov ah,9 ;在光标位置显示字符
        mov al,‘a’ ;字符
        mov bl,02h ;颜色属性
        mov bh,0 ;第0页
        mov cx,3 ;字符重复个数
        int 10h

mov ax,4c00h

int 21h
的真正含义

  • 调用了第 21h 号中断例程中的 4ch 号子程序

    • mov ah,4ch ;程序返回
      mov al,0 ;返回值
      int 21h

DOS中断例程应用

  • mov ah,9
    功能号9,显示字符串

    • qs5.exe
  • ds:dx 指向字符串 ;要显示的字符串需用"$"作为结束符
    mov ah,9
    int 21h

编写、应用中断例程

  • (1)qs6.exe

  • (2)qs7.exe

  • 整套安装中断程序的模板

    • qss.asm
  • (3)qs8.exe

端口

概念

  • 与CPU通过总线相连的芯片上可供CPU读写的寄存器

CPU可以直接读写

  • (1)CPU内部寄存器
  • (2)内存单元
  • (3)端口

端口的读写

  • 只能用 in 和 out,不能用其他的!

  • in

      • in al,60h

        • 从60h号端口读入一个字节到 al 中
  • out

      • mov al,2
        out 70h,al

        • 向70h号端口写入 al 中的内容2

CMOS RAM芯片

  • 端口

    • 70h

      • 存放要访问的CMOS RAM单元的地址
    • 71h

      • 存放从选定的CMOS RAM单元中读取的数据

shl 和 shr 指令

  • 二者均为 逻辑移位指令

  • shl

    • 逻辑左移指令

    • 功能

      • (1)将一个寄存器或者内存单元中的数据向左移位
        (2)将最后移出的一位以为写入CF中
        (3)最低位用0补充

      • 效果相当于

        • X = X*2
    • 注意点

      • 如果移动位数大于 1 时,必须将移动位数放在cl中
  • shr

    • 逻辑右移指令

    • 功能

      • (1)将一个寄存器或者内存单元中的数据向右移位
        (2)将最后移出的一位以为写入CF中
        (3)最搞位用0补充

      • 效果相当于

        • X = X/2
    • 注意点

      • 如果移动位数大于 1 时,必须将移动位数放在cl中
  • 子主题 4

CMOS RAM中存储的时间信息

  • CMOS RAM中存放着当前时间

    • 内存单元为:

      • 秒:0
      • 分:2
      • 时:4
      • 日:7
      • 月:8
      • 年:9
    • 这些数据都是用BCD码来存放的(类似十六进制)

      • 4位二进制数 表示一个十进制数

      • 0100 0010

        • 42
  • 将BCO码转换为十进制数据显示在屏幕上的方法

    • 让ah等于高4位,
      al等于低4位,
      需要用到逻辑移位指令

      • mov al,8
        our 70h,al
        in al,71h ;获取了月份

mov ah,al ;al中存放着月份
mov cl,4
shr ah,cl ;ah中为月份的十位数码值
and al,00001111b ;al中为月份的个位数码值

  • 实验14

    • qs9.exe

      • 完美完成!!!!!!

外中断

CPU通过端口和外部设备联系

可屏蔽中断

  • CPU可以不响应的外中断

  • 两种情况

    • IF = 1

      • CPU在执行完当前指令后响应中断,引发中断过程
    • IF = 0

      • CPU不响应可屏蔽中断
  • 设置 IF

    • sti

      • 设置 IF = 1
    • cli

      • 设置 IF = 0

不可屏蔽中断

  • 当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程

  • 中断过程

    • 对于8086CPU,不可屏蔽中断码固定为2
    • (1)标志寄存器入栈,IF = 0,TF = 0
    • (2)CS,IP入栈
    • (3)(IP) = (8),(CS) = (0AH)

PC机键盘的处理过程

  • 1、键盘输入

    • (1)按下按键时

      • 开关接通,芯片产生一个扫描码,扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h
      • 通码
    • (2)松开按键时

      • 也产生一个扫描码,这个扫描码说明了松开的键在键盘上的位置,同在60h端口
      • 断码
    • 断码 = 通码 + 80h

  • 2、引发9号中断

    • 如果IF = 1就引发中断过程,转去执行 int9 中断例程
  • 3、执行 int9 中断例程

    • (1)读出60h端口中的扫描码

    • (2)扫描码类型识别

      • 字符键

        • 将该扫描码和其对应的ASCII码送入内存中的BIOS键盘缓冲区

        • BIOS键盘缓冲区

          • BIOS用于存放 int 9 中断例程所接收的键盘输入的内存区

          • 高位字节

            • 扫描码
          • 低位字节

            • 字符码(ASCII码)
      • 控制键

        • 将其转变为状态字节,写入内存中存储状态字节的单元

        • 状态字节

          • 用二进制位记录控制键和切换键状态的字节
          • 具体信息看书p275
    • (3)对键盘系统进行相关控制

  • 键盘输入的处理过程总结

    • (1)键盘产生扫描码
    • (2)扫描码送入 60h 端口
    • (3)引发9号中断
    • (4)CPU执行 int 9 中断例程处理键盘输入

编写 int 9 中断例程

  • 在屏幕中间一次显示’a’-‘z’,并可以让人看清 。在显示的过程中,按下Esc键后,改变显示颜色

  • 功能拆分

    • (1)依次显示’a’-‘z’

      • 简单的写入
    • (2)让人看清

      • 用多次循环延迟一小段时间
    • (3)按下ESC改变颜色

      • 需要我们编写 int 9 中断例程

        • 功能

          • (1)从60h端口中读出键盘的输入

            • in al,60h
          • (2)调用BIOS的 int 9 中断例程,处理其他硬件细节

            • 由于 int 9 中存放着一些处理硬件细节的机器码,所以我们编写的 int 9 中断例程还是得要调用 int 9 中断例程

            • 我们写的就是int 9,在这里如何调用 int 9?

              • 模拟 int 指令的功能

                • 功能

                  • (1)获取中断类型码n
                    (2)标志寄存器入栈
                    (3)IF = 0,TF = 0
                    (4)CS、IP入栈
                    (5)(IP) = (n4) CS = (n4 + 2)
                • 模拟功能

                  • 因为我们知道中断类型码,所以我们不需要获取

                  • 功能可以模拟成

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值