汇编笔记(一)

汇编语言

王爽 著


第一章:基础知识

  1. 汇编语言(指令)由三类组成:
    1. 汇编指令:机器码的助记符,有对应的机器码
    2. 伪指令:无对应的机器码,由编译器执行,计算机不执行
    3. 其他符号:如:+,-,*,/等,由编译器识别,无对应的机器码
  2. 总线从逻辑上分为:地址总线、控制总线、数据总线
    • 地址总线:决定寻址大小
    • 控制总线:决定CPU对器件(内存)的控制能力,“读信号输出”的控制线负责由CPU向外传读信号(CPU提供低电平);“写信号输出”的控制线负责传送写信号。
    • 数据总线:决定数据传送一次传送量(速度)。
  3. 对CPU来说,系统中所有存储器中的存储单元都处于一个统一逻辑的存储器(内存地址空间)中,它们统一寻址。存储器的容量受CPU寻址能力的限制。

第二章:寄存器

  1. 8086CPU所有寄存器都是16位的,可以存放两个字节

    AX, BX, CX, DX用于存放一般性数据,称为通用寄存器。它们每个寄存器均可以分为两个8位的寄存器,最大存放255,

    AX = AH + AL,BX =BH + BL

  2. 字节(byte)由8个比特(bit)组成,字(word)由两个字节组成,双字由两个字组成。

  3. 汇编指令:不区分大小写

    MOVAX18将18送入寄存器AXAX = 18
    MOVAH78将78送入寄存器AHAH = 78
    ADDAX8将AX中的值加上8AX=AX+8
    MOVAXBX将AX中的值送入AXAX=AX+BX
    ADDAXBX将AX与BX中的值相加,送AX中AX=AX+BX

    CPU执行指令时认为,AH和AL是不相关的寄存器,AL的进位不会存入AH中,只有16位运算时,AX作为一个整体,AL进位才会传入AH。

    指令的两个操作对象的位数应当一致

  4. 所有内存单元构成一个一维线性存储器空间的地址,称为物理地址。CPU通过地址总线送入存储器的必须是一个完整的物理地址。

  5. 8086CPU地址总线20,而CPU内部是16位结构。由20根地址总线看,可以传送20位数据,CPU的寻址能力达到1MB,CPU内部的两个16位地址(分别成为段地址和偏移地址)将经过合成(输入输出控制器)构成20位的物理地址。
    物 理 地 址 = 段 地 址 ∗ 16 + 偏 移 地 址 物理地址 = 段地址 * 16 + 偏移地址 =16+

  6. 8086CPU有4个段寄存器:CS、DS、SS、ES

  7. CS:代码段寄存器,IP:指令指针寄存器。任意时刻,CPU将CS:IP指向的内容当作指令执行。

    1. 从CS:IP指向的内存单元读指令,读到的指令进入指令缓冲器;

    2. IP = IP + 所读取指令的长度,从而获取指向下一指令的地址;

    3. 执行指令,转到步骤1,重复。

      FFFF:0000H为8086PC机刚启动的第一条指令。

  8. mov指令不能够用于设置CS、IP的值,能够修改CS、IP的内容的指令为转移指令,最简单的为jum指令。

    • 若想同时修改CS、IP内容,用jum 段地址:偏移地址
    • 若仅想修改IP的内容,用jum 某一寄存器,用寄存器中的值修改
      IP
  9. 对8086,不需要将一组内存单元定义为一个段,段地址存放在段寄存器中,为16的倍数,段长最大为64KB(16位)

  10. Debug的用法:

    1. R命令查看、修改寄存器的内容。

    2. D命令查看内存中的内容。

      D 段地址:偏移地址

      段地址可用DS/CS/SS符号代替

    3. U命令将内存中的机器指令翻译成汇编指令。

    4. E命令改写内存中的内容,E也可以进机器码写入。

      E 起始地址 数据 数据……

    5. T命令执行一条机器指令。

    6. P命令类似T命令,不过遇到子程序调用的时候直接执行完子程序代码,不会进入子程序逐条执行,可以理解为step over。另外,在遇到循环指令时,会直接执行到CX=0。

    7. A命令以汇编指令格式在内存中写入一条机器指令。

第三章:寄存器(内存访问)

  1. DS寄存器:通常存放数据段的段地址

  2. mov al, [0] 将DS:0000的内容送入al中,[ ]表示一个内存单元,0表示偏移。

  3. 8086CPU不支持数据直接送入段寄存器,可以通过一个通用寄存器进行中转。

  4. mov sub add都是有两个操作数的指令。

  5. 8086CPU提供相关指令以栈的方式访问内存空间:

    8086CPU的入栈和出栈均已字为单位,任意时刻SS:SP指向栈顶

    1. push ax:将ax中的数据入栈

      第一步:SP = SP - 2

      第二步:ax的内容送入SS:SP指向的内存处

      栈的生长方向沿着内存地址减少的方向

      当栈为零时,SS:SP指向栈底的下一个单元

    2. pop ax:从栈顶取数据送入ax

      第一步:SS:SP指向的内存单元之处数据送入ax

      第二步:SP = SP + 2

  6. 当栈满时在push,或栈零后在pop均会发生栈顶越界问题。

  7. push/pop的操作数可以是普通寄存器、段寄存器、内存单元

第四章:第一个程序

  1. 伪指令segmentends的功能是定义一个段,段必须有一个名标来标识,使用格式:

    段名 segment
    			.
    			.
    段名 ends
    
  2. 伪指令end是一个汇编语言的结束标记。

  3. 伪指令assume假设某一段寄存器与某个段关联(由segment…ends定义)。

    assume cs:codesg
    
    codesg segment
    				.
    				.
    codesg ends
    
  4. 标号指代了一个地址,各段名就是一个标号,代表一个地址即段地址。

  5. 程序返回指令:

    mov ax,4c00H

    int 21H

  6. 编译过程,提供一个输入最多可有3个输出:

    目标文件(.obj)、列表文件(.lst)、交叉引用文件(.crf),后二者为中间结果,可以不要。

  7. debug可以将程序载入内存,然后控制程序的执行。此时C区中存放了程序长度,程序加载后,DS存放着程序所在内存区的段地址,该内存区的前256个字节存放PSP(程序前段),从256字节处的后为程序代码。

    PSP区:SA:C 程序区:SA + 10H:0(SA——DS)

  8. int 21H要用P命令执行

  9. debug命令:g 偏移地址s,表示执行程序到当前代码段的s处,即程序从当前的CS:IP指向的指令执行,一直到IP=s为止。

    P命令可以一直执行完所有循环loop。

  10. 源程序中分号‘;’为注释。

第五章:[BX]和loop指令

  1. 要完整地描述一个内存单元,要两种信息:

    1. 内存单元的地址;
    2. 内存单元的长度(类型)(字节、字……)

    [0]表示一个内存单元时,0表示单元的偏移地址,段地址默认在DS寄存器中,单元长度(类型)可由指令中的其他操作对象(如寄存器)指出。

    [bx]也表示一个内存单元,偏移地址在bx中。

    mov ax,[bx];将一个段地址在DS,偏移地址在bx且长度为两个字节的内存单元送入ax
    
    mov al,[bx];将一个段地址在DS,偏移地址在bx且长度为一个字节的内存单元送入ax
    
  2. loop指令的格式:loop 标号;标号代表了一个地址

    具体操作分为两步:

    1. cx的值减1(一般在cx中存放循环次数)
    2. 判断cx中的值,不为0则转至标号处执行,为0则向下执行
    assume cs:code
    code segment
    		mov ax,2
    		mov cx,11
    s: 	add ax,ax;s为标号
    		loop s
    		mov ax,4c00h
    		int 21h
    code ends
    end
    
  3. 在汇编源程序中,数据不能以字母开头,所以前面要加0。eg:A000h要写成0A000h。

  4. 在debug中与masm源程序中对[0]的处理不同

    1. 在debug中:[0]表示内存单元,相当于DS:0处的内存单元

    2. 在汇编源程序中,[0]表示常量0,若想表示DS:0方法是:

      • 显式给出DS:[0]

        ;访问2000:0单元
        mov ax,2000h
        mov ds,ax
        mov al,ds:[0]
        
      • 将偏移地址送入bx中,以[bx]访问内存单元

        mov ax,2000h
        mov ds,ax
        mov bx,0
        mov al,[bx]
        
  5. 用[bx](内存单元地址变量)访问内存单元的好处:通过改变bx的值,可以在循环中访问一块连续的内存单元

    inc bx;将bx的值增加1

  6. 显式指明内存段地址:CS:、DS:、SS:、ES:,被称为段前缀。

    参考:CS、DS、SS、ES寄存器的区别

  7. 在DOS方式下,一般0:200~0:2ff的256字节的空间一般是安全的,可用debug查看一下,内容全为0则是安全的,也可使用mov ds:[0],al将其改写,如果没有引发错误也是安全的。

第六章:包含多个段的程序

  1. dw的含义是定义字型数据,即define word,数据之间以逗号分开:

    dw 0123h,0456h,0abch
    

    若在代码段开头增加dw数据,则这些数据位于代码段内存起始位置,此时需要在源程序中指明程序入口,否则ip指向代码段起始地址。

    assume cs:code
    code segment
    			dw 0123h,0456h,0789h
    			start:
    				mov ax,0
    				......
    				mov ax,4cooh
    				int 21h
    code ends
    end start;通知编译器程序结束,同时通知编译器程序的入口地址
    
  2. 可执行文件由描述信息和程序组成,描述信息由相关伪指令处理得到的信息组成(如入口地址)。

  3. 使用堆栈:

    codeseg segment
    			dw 0123h,0456h,0789h
    			dw 0,0,0,0,0,0;开辟栈空间
    	start:
    			mov ax,cs
    			mov ss,ax
    			mov sp,12h
    
  4. 定义多个段

    assume cs:code, ds:data, ss:stack
    ;数据段
    data segment
    		dw 0123h, 0456h, 0789h, 0abch, 0defgh
    data ends
    
    ;堆栈段
    stack segment
    		dw 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
    		;16个字,32字节,栈底为20h,栈顶00h,栈从20h向上增长,sp指向栈底,即20h
    		
    ;代码段
    code segment
    start:
    		mov ax,stack
    		mov ss,ax;指向栈的基址
    		mov sp,20h;初始化ss和sp寄存器,SS:SP定位栈的内存内存单元
    		
    		mov ax,data
    		mov da,ax;初始化ds寄存器
    		......
    code ends
    end start
    

第七章:更灵活的定位内存地址的方法

  1. and:逻辑按位与

    and al, 00010011b,对al按位与,结果保存在al中

    or:逻辑按位或

  2. 在汇编程序中,用’’(单引号)的方式以字符形式给出数据,编译器自动将他们转化为对应的ASCII码

    小写的ASCII码值比大写字母的ASCII码值大20h,小写字母减20h变成对应大写字母。

    小写字母的ASCII码值的第五位为1,而大写字母为0。用此可以判断大小写

    assume cs:codesg, ds:datasg
    
    datasg segment
    	db 'BaSiC'
    	db 'iNfOrMaTiOn'
    datasg ends
    
    codesg segment
    	start:
    		mov ax, datasg
    		mov ds, ax;设置ds指向datasg段
    		
    		mov bx, 0;设置(bx)=0,ds:bx指向'BaSiC'的第一个字符
    		
    		mov cx, 5 ;设置循环次数5,因为‘BaSiC’有5个字符
    	s:mov al,[bx] ;将ASCII码从ds:bx指向的单元中取出
    		and al,11011111b ;将al中的ASCII码第五位置为0(从0开始),变成大写字母
    		mov [bx],al ;将转变后的ASCII码写入原内存单元
    		inc bx ;bx加1,ds:bx指向下一个字母
    		loop s
    		
    		mov bx,5 ;设置(bx)=5,ds:bx指向'iNfOrMaTiOn'的第一个字母
    		mov cx,11 ;设置循环计数
    	s0:mov al,[bx]
    		or al,00100000b ;将al中的ASCII码的第五位置为1,边为小写字母
    		mov [bx],al
    		inc bx
    		loop s0
    		
    		mov ax,4c00h
    		int 21h
    codesg ends
    end start
    
  3. [bx + 常量]:段地址为ds,偏移地址为bx的内容加上常量,相当于ds:bx+常量。也可以写为:[常量 + bx]、常量[bx]、[bx].常量。

    这种表达可以实现数组的形式,如:0[bx]或[0 + bx]。0为数组首地址,bx为偏移。

  4. si和di是8086CPU中与bx功能相似的通用寄存器,但是不能跟bx一样分成两8位的寄存器。

  5. [bx + si]表示一个内存单元,其偏移地址为bx+si,si换成di也是一样的形式。也可以写为[bx] [di]或[di] [bx]。

  6. [bx + si + 常量]表示一个内存单元,其偏移地址为bx的值加上si的值加常量。

  7. [常量]:用常量定位内存地址,直接寻址

    [bx]:用一个变量定位内存地址,间接寻址

    [bx + 常量]:用一个变量和一个常量定位,在一个基础地址上加上一个变量间接定位。

    [bx + si]:用两个变量定位内存地址

    [bx + si + idata]:表示一个内存单元,偏移地址为bx中的值加上si中的值再加上idata。

    格式可参考第3条,di的用法与si相似

第八章:数据处理的两个基本问题

  1. 在8086CPU中只有4个寄存器可以用在[…]中来进行内存单元的寻址:bx,si,di,bp,四个寄存器可以单独出现,可以可成对出现,但只能按以下组合出现:

    mov ax,[bx+si]
    mov ax,[bx+di]
    mov ax,[bp+si] 
    mov ax,[bp+di]
    ;成对出现也可以加上idata
    

    bp是基数指针寄存器和堆栈指针SP联合使用的,作为SP校准使用的,只有在寻找堆栈里的数据和使用个别的寻址方式时候才能用到。参考博客

    只要在[…]中使用寄存器bp,而指令中并没有显性给出段地址,默认在SS中

    mov ax,[bp];(ax)= ((ss)*16 +(bp))
    mov ax,[bp+si+idata]; (ax)= ((ss)*16 +(bp)+(si)+ idata)
    
  2. 立即数idata(即常量)在汇编指令在直接给出,执行前位于CPU的指令缓冲器中

  3. 寻址方式:当数据存放在内存中时,有多种方式给定这个内存单元的偏移地址:

    1. 直接寻址:[0]
    2. 寄存器间接寻址:[bx]
    3. 寄存器相对寻址:[bx + 100]
    4. 基址变址寻址:[bx + si]
    5. 相对基址变址寻址:[bx + si + 100]
  4. 通过寄存器名可以指定要处理的数据尺寸(字/字节)

    若无寄存器名,则用操作符X ptr指明内存单元的长度。X为word或byte

    inc word ptr [bx]

    有些指令默认了操作数的长度,如push和pop,默认为字

  5. [bx].idata:bx定位结构体,idata定位结构体成员变量。

    [bx].idata[si]:bx定位结构体,idata定位结构体成员变量数组,si数组内元素偏移。

  6. div指令,除数有16/8位,在一个寄存器/内存单元中。

    若除数为8位,则被除数为16位,被除数默认在ax中,商在al中,余数在ah中;若除数为16位,则被除数为32位,被除数默认在dx和ax中,dx存放高16位,ax存放低16位,商在ax中,余数在dx中。

    div byte ptr [o];8位除法
    div word ptr [0];16位除法
    div bx;16位除法
    
  7. 伪指令dd用于定义双字类型的数据

  8. 伪指令dup用于数据重复,与db dw dd一起使用。

    db 3 dup (0,1,2)定义了9个字节,值为0、1、2、0、1、2

    0、1、2,相当于db 0,1,2,0,1,2,0,1,2。

第九章:转移指令的原理

  1. 可以修改IP,或同时修改CS和IP的指令称为转移指令。

    段内转移:只修改IP,如jmp ax,短转移的IP修改范围时-128~127,近转移的IP修改范围为-32768~32767

    段间转移:同时修改CS和IP,如jmp 1000:0

  2. 8086CPU的转移指令大致分为:

    1. 无条件转移指令(如:jmp)
    2. 条件转移指令
    3. 循环指令(如:loop)
    4. 过程
    5. 中断
  3. 操作符offset:取得符号的偏移地址,由编译器处理。

    assume cs:codesg
    codesg segment
    		start:mov ax,offset start ;相当于mov ax,0,因为start是代码段标号,偏移地址为0
    				s:mov ax,offset s	;相当于mov ax,3,因为第一条指令长度为3个字节,则s偏移地址为3
    codesg ends
    end start
    
  4. jmp为无条件转移指令,可以只修改IP,也可以既修改IP又修改CS

    jmp要给出:转移的目的地址;转移的距离(段间、段内短、段内近转移)

  5. jmp short 标号 :段内短转移,跳转指令结束后,CS:IP指向标号处的指令。它对应机器码中,并不包含转移的目的地址,而包含的是转移的位移,位移是根据标号计算出来的,IP=IP+8位位移

  6. jmp near ptr 标号段内近转移,IP修改范围-32768~32767,IP=IP+16位位移

  7. jmp far ptr 标号段间转移,IP = 标号在段中的偏移地址。

  8. jmp 16reg(通用寄存器) :IP=reg

  9. jmp word ptr 内存单元地址:从内存单元地址处存一个字,该字是住哪一目的的偏移地址,为段内转移,IP=内存单元字。

  10. jmp dword ptr 内存单元地址:从内存单元地址处存双字,高地址处的字是转移的目的的段地址,低地址处是转移的目的偏移地址,为段间转移,CS=内存单元双字的高地址字,IP=内存单元双字的低地址字。

  11. jcxz为条件转移指令,所有的条件转移均为短转移,在对应的机器码中包含转移的位移,格式: jcxz 标号

    当cx = 0时,IP=IP+8位位移

    当cx ≠ 0时,什么也不做,程序向下执行。

  12. loop指令为循环指令,所有的循环指令均为短转移。格式:loop 标号

    首先cx=cx-1,接着若cx≠0,则IP=IP+8位位移,若cx=0,向下执行。

  13. 根据位移转移的指令,其转移范围受到位移大小的限制,若在源程序中出现转移越界,则编译器会报错。

第十章:CALL和RET指令

  1. ret指令用栈中的数据修改IP的内容,实现近转移。IP=栈顶数据(一个字,16位),SP = SP+2(即pop sp)。

    retf指令用栈中的数据修改CS和IP的内容,实现远转移。IP = 栈顶数据(一个字,16位)sp=sp+2;CS =栈顶数据(一个字,16位),sp=sp+2(即pop ip,pop cs)。

  2. CPU行call指令时,将当前的IP或CS和IP压入栈,再转移。call不能短转移,除此之外转移方法与jmp原理相同。

  3. call 标号实现近转移,分为两步:

    push IP
    IP=IP+16位位移 ; 位移=标号地址-call指令后的第一个字节的地址
    ;相当于:
    push IP
    jmp near ptr 标号
    
  4. call far ptr 标号实现段间转移:

    ; 第一步
    push CS
    push IP
    ; 第二步
    CS=标号所在段的段地址
    IP=标号在段中的偏移地址
    
  5. call 16位reg(通用寄存器)转移地址在寄存器中

    push IP
    IP = reg
    
  6. call word ptr 内存单元地址转移地址在内存中,段内转移

    ; 相当于:
    push IP
    jmp word ptr 内存单元地址
    
  7. call dword ptr 内存单元地址转移地址在内存中,段间转移

    push CS
    push IP
    jmp dword ptr 内存单元地址
    
  8. 利用callret来实现子程序机制。子程序的框架如下:

    assume cs:code
    code segment
    	main:
    			...
    			call sub1;调用子程序sub1
    			...
    			mov ax,4c00h
    			int 21h
    			
    	sub1: ;子程序sub1开始
    			...
    			call sub2 ;调用子程序sub2
    			...
    			ret ;子程序返回
    			
    	sub2:
    			...
    			ret ;子程序返回
    code ends
    end main
    
  9. 乘法指令mul:要么都是8位,要么都是16位

    1. 若两个数为8位,则一个默认在al中,另一个在8位reg或内存字节单元
    2. 若两个数为16位,则一个默认在ax中,另一个在16位reg或内存字单元
    3. 结果:8位乘法,结果默认在ax中;16位乘法,结果高位默认在dx中,低位默认在ax中。

    内存单元可以用不同的寻址方式给出

  10. 调用者将参数送入参数寄存器从结果寄存器中取出返回值。

    子程序从参数寄存器中取出参数,将返回值送入结果寄存器。

    若参数过多,则批量数据传于内存中,然后将他们的内存空间首地址放入寄存器,传给子程序(还要指出参数的长度,另外使用栈也可以传参)。

  11. 为了解决主程序与子程序寄存器使用冲突,给出一个标准框架:

    sub:子程序要用到的寄存器入栈
    		子程序内容
    		子程序要用到的寄存器出栈
    		返回(ret/retf)
    

    eg:将一个全是字母,以0结尾的字符串转为大写:

    code segment
    start:mov ax,data
    			mov ds,ax
    			mov bx,0
    			
    			mov cx,4;处理4个字符串
    		s:mov si,bx
    			call capiatl
    			add bx,5
    			loop s
    			
    			mov ax,4c00h
    			int 21h
    ; 方案一
    capital:mov cl,[si]
    				mov ch,0
    				jcxz ok
    				and byte ptr [si],11011111b
    				inc si
    				jmp short capital
    			ok:ret
    ; 方案二
    capital:push cx
    				push si
    				
    change: mov cl,[si]
    				mov ch,0
    				jcxz ok
    				and byte ptr [si],11011111b
    				inc si
    				jmp short change
    				
    		ok: pop si
    				pop cx
    				ret
    				; 注意出栈入栈的顺序
    				
    code ends
    end start
    

第十一章:标志寄存器

  1. 8086CPU的flag reg(标志寄存器)有16位,其内容通常被称为程序状态字(PSW)。

    1514131211109876543210
    OFDFIFTFSFZFAFPFCF
  2. 影响标志寄存器的指令:add sub mul div inc or and等,大多数为运算指令。

    不影响标志寄存器的指令:mov push pop,他们大多为传递指令。

  3. flag的第6位ZF,0标志位,记录相关指令执行后,结果是否为0,若结果为0,则ZF=1;结果不为0,则ZF=0。

  4. flag的第2位PF,奇偶标志位,记录相关指令执行后,结果的所有bit位中1的个数是否为偶数,若为偶数,PF=1;若为奇数,PF=0

  5. flag的第7位SF,符号标志位,纪律相关指令执行后,结果是否为负,若为负,SF=1;若非负,SF=0.

    只有在有符号运算时,才需要参考其值

  6. flag的第0位CF,进位标志位,一般在无符号数运算时,记录进位/借位信息。进位:add指令产生,借位:sub指令产生。

    dec inc loop指令不影响进位标志位,dec执行减1,inc执行加1.

  7. flag的第11位OF,溢出标志位,一般在有符号运算时,记录结果是否溢出。溢出只对有符号运算而言,CF与OF无关。

  8. abc op1,op2带进位加法指令,它利用CF位记录进位值,op1=op1+op2+CF

  9. sbb op1,op2带借位减法指令,它利用CF位记录借位值,op1=op1-op2-CF

  10. cmp op1,op2比较指令,相当于减法,但是不保存结果,只对标志寄存器有影响。无符号比较会影响ZF PF SF CF OF标志位,有符号比较会影响ZF PF SF OF标志位。

  11. 常用的根据无符号数的比较结果进行转移的条件转移指令:

    所有条件转移指令全部为短转移

    指令含义检测的相关标志位
    je等于则转移zf=1
    jne不等于则转移zf=0
    jb低于则转移cf=1
    jnb不低于则转移cf=0
    ja高于则转移cf=0且zf=0
    jna不高于则转移cf=1或zf=1

    通常这些条件转移指令与cmp指令配合使用,因为它们判断的标志恰好是cmp修改的标志位。

    根据有符号数的比较结果进行转移的条件转移指令的工作原理与无符号数相同,只是检测了不同的标志位。

  12. flag的第10位DF,方向标志位,控制串处理指令中,si、di的增减

    1. DF=0,每次操作后,si、di递增
    2. DF=1,每次操作后,si、di递减

    指令:movsb,每次传递一个字节

    相当于执行:es:di = ds:si

    ​ 若df=0,则si=si+1、di=di+1

    ​ 若df=1,则si=si-1、di=di-1

    指令:movsw,每次传递一个字,然后si和di递减或递增2.

    movsbmovsw是串传递操作符中的一步,一般与rep配合使用

    rep movsbrep根据cx中的值重复它后面的指令,类似于loop,传递cx个字节。

    rep movswrep根据cx中的值重复它后面的指令,类似于loop,传递cx个字。

  13. 8086CPU中对df位设置指令:

    1. cld:将df位清零
    2. std:将df位置1
  14. pushf:将标志位寄存器的值入栈。

    popf:将栈中弹出数据送入的flag寄存器中。

  15. 在debug中对已知标志位的影响:

    标志值为1的标记值为0的标记
    ofOVNV
    sfNGPL
    zfZRNZ
    pfPEPO
    cfCFNC
    dfNDUP

第十二章:内中断

  1. 8086CPU产生中断的时机:

    1. 除法错误,如div指令执行后产生的除法溢出,中断类型码0
    2. 单步执行,中断类型码1
    3. 执行int0的命令,中断类型码4
    4. 执行int指令,中断类型码n

    8086CPU用中断类型码标识中断信息来源,为一个字节型数据,256种中断信息来源。CPU用8位的中断类型码,通过中断向量表找到相应的中断处理程序入口。

    中断向量表在内存中存放,对于8086PC机,它位于内存地址0处,从0000:0000到0000:03FF,共1024个字节,每个表项两个字,高地址字存放段地址,低地址字存放偏移地址。

  2. 用中断类型码找到中断向量,并用中断向量设置CS和IP,此步由CPU的硬件自动完成,该过程称为中断过程。

    CPU收到中断信息后,首先引发中断过程,再执行中断处理程序。

    过程步骤:

    1. 从中断信息中取出中断类型码,设为N
    2. flag寄存器入栈,pushf
    3. 设flag寄存器的TF(第八位)和IF(第九位)为0,TF=0,IF=0
    4. CS入栈,push CS
    5. IP入栈,push IP
    6. 从内存地址为0:4N(n为中断类型码的值)和0:4N+2的两个字单元中读取段处理程序的入口地址设置IP和CS(IP=4N,CS=4N+2)
  3. 编写中断处理程序的步骤:

    1. 保存用到的寄存器
    2. 处理中断
    3. 恢复用到的寄存器
    4. iret指令返回(iret一般与中断过程配合使用,iret:pop IP,POP CS,popf)
  4. 实际上系统要处理的中断事件远没有达到256个,在中断向量表中,有许多单元都是空的。eg:0000:0200~0000:02FF

  5. 编译器可以执行两个常数的加减法、乘法、除法

  6. CPU在执行完一条指令后,若检测到TF位为1,则产生单步中断,引发中断过程内容,其中中断类型码为1

    为了防止陷入无穷递归调用单步中断处理程序,在中断过程中有TF=0,否则就会无限中断。

  7. 有些情况下,即使发生中断,CPU也不会马上响应,如在执行完mov ss,数据时,即使发生中断,也要等设置SP的指令结束(CPU设置完ss后,并不影响中断),再响应,对SS,SP的设置必须完成,保证原子性。

    若不是原子的,则中断过程的入栈会出问题。利用这个性质,将设置SS和SP的指令连续存放。

第十三章:int指令

  1. int指令格式:int n(中断类型码)

    执行过程:

    取n
    pushf ; 标志寄存器入栈,IF=0,TF=0
    push CS,push IP
    IP=4n,CS=4n+2
    

    可以用int指令调用任何一个中断的中断处理程序。

  2. BIOS中主要包含一下几个部分:

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

    DOS也提供了中断例程,于硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程。

  3. 中断例程安装过程

  4. int 10h中断例程是BIOS提供的,包含多个与屏幕输出相关的子程序

    assume cs:code
    code segment
    	mov ah,2; 置光标
    	mov bh,0; 第0页
    	mov dh,5; dh中放行号
    	mov dl,12; dl中放列号
    	int 10h
    	
    	mov ah,9;在光标位置显示字符
    	mov al,'a';字符
    	mov bl,11001010b;颜色属性
    	mov bh,0;第0页
    	mov cx,3;字符重复个数
    	int 10h
    	
    	mov ax,4c00h
    	int 21h
    	
    code ends
    end
    
  5. int 21h中断例程是DOS提供的中断例程

    mov ah,4ch;4ch号子程序,表示程序返回
    mov al,0;返回值
    int 21h
    
    ds:dx ;指向字符串,要显示的字符串用‘$’作为结束符
    mov ah,9;9号子程序,表示在光标位置处显示字符串
    int 21h
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值