汇编逆向分析逆向工程汇编语言

汇编入门(长文多图,流量慎入!!!)

2017-05-27  本文已影响883人  Gibbs基

8086汇编


本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他一样共享资源、帮助他人的筒子们==本文比较长,由于笔者个人能力有限,错漏在所难免,欢迎读者们批评指正。

图文无关

本文首发于我的CSDN博客,同时也发布于我的博客园

一、基础知识

引言

1.1 机器语言

1.2 汇编语言的产生

1.3 汇编语言的组成

1.4 存储器

1.5 指令和数据

1.6 存储单元

1.7 CPU对存储器的读写

1.8 地址总线

1.9 数据总线

1.10 控制总线

小结

检测点 1.1

检测点1.1

1.11 内存地址空间(概述)

1.12 主板

1.13 接口卡

1.14 各类存储器

1.15 内存地址空间


二、寄存器(CPU的工作原理)

引言

2.1 通用寄存器

2.2 字在寄存器中的存储

2.3 几条汇编指令

汇编指令 控制CPU完成的操作 用高级语言的语法描述
mov ax,18 将8送入AX AX=18
mov ah,78 将78送入AH AH=78
add ax,8 将寄存器AX中的数值加上8结果存入AX中 AX=AX+8
mov ax,bx 将寄存器BX中的数据送入寄存器AX AX=BX
add ax,bx 将AX,BX中的内容相加结果存入AX中 AX=AX+BX

检测点 2.1

检测点2.1

2.4 物理地址

2.5 16位结构的CPU

2.6 8086 CPU给出物理地址的方法

8086CPU相关部件的逻辑结构 地址加法器的过程
移位位数 二进制 十六进制 十进制
0 10B 2H 2
1 100B 4H 4
2 1000B 8H 8
3 10000B 10H 16
4 100000B 20H 32

2.7 段地址*16+偏移地址=物理地址

CPU可以通过不同的段地址和偏移地址形成相同的物理地址

段地址*16是移位

2.8 段的概念

检测点 2.2

检测点2.2

2.9 段寄存器

2.10 CS和IP

2.11 修改CS、IP的指令

jmp 段地址:偏移地址;同时修改CS和IP
jmp 某一合法寄存器;则是仅修改IP

2.12 代码段

检测点 2.3

检测点2.3

使用Debug

<font color="red">windows xp系统自带debug,请使用xp以上系统的读者执行自行下载debug.exe和dosbox,使用方法笔者不再赘述,在dosbox中可以使用debug。</font >


debug in dosbox

三、寄存器(内存访问)

3.1 内存中字的存储

3.2 DS和[address]

数据 -> 通用寄存器 -> 段寄存器

mov 寄存器名,内存单元

3.3 字型的传送

3.4 mov、add、sub指令

mov 寄存器,数据;mov ax,8
mov 寄存器,寄存器;mov ax,bx
mov 寄存器,内存单元;mov ax,[0]
mov 内存单元,寄存器;mov [0],ax
mov 段寄存器,寄存器;mov ds,ax
mov 寄存器,段寄存器;mov ax,ds
     ……
add 通用寄存器,数据
add 通用寄存器,通用寄存器
add 通用寄存器,内存单元
add 内存单元,寄存器
sub 通用寄存器,数据
sub 通用寄存器,通用寄存器
sub 通用寄存器,内存单元
sub 内存单元,通用寄存器  

3.5 数据段

检测点 3.1

检测点3.1

3.6 栈

3.7 8086 CPU提供的栈机制

<font color="red">CS和IP存放当前指令的段地址和偏移地址。</font>

3.8 栈顶越界的问题

3.9 push、pop指令

;push和pop格式
push 寄存器
pop 寄存器
push 段寄存器
pop 段寄存器
push 内存单元
pop 内存单元

3.10 栈段

检测点 3.2

检测点3.2

四、第一个程序

引言

编写完成的汇编语言程序,用编译器编译成可执行文件并在操作系统中运行。

4.1 一个源程序从写出到执行的过程

4.2 源程序

assume cs:codesg ;假设代码段的名称为codesg
codesg segment ;定义一个codesg段
mov ax,0123H
mov bx,0456H
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends ;codesg段结束
end ;是个伪指令,程序的结束标记

assume用来加上某一段寄存器和程序中的某一用segment……ends定义的段相关联。通过assume说明这种关联,在需要的情况下编译程序可以将段寄存器和某一个具体的段相联系。

;编程运算2^3
assume cs:abc ;段与寄存器关联

abc segment ;定义一个段,名称为abc
mov ax,2;写入汇编指令
add ax,ax
add ax,ax
 
abd ends      
end ;程序结束处

codesg:放在segment前面,作为一个段的名称,这个段的名称最终将被编译、连接程序,称为一个段的段地址 。

mov ax,4c00H
int 21H ;第21号中断
;这两条指令说实现的功能就是程序返回。

4.3 编辑源程序

    assume cs:ABC
    ABC segment
        mov ax,2
        add ax,ax
        add ax,ax
        mov ax,4c00H
        int 21h
    ABC ends
    end

4.4 编译

4.5 连接

4.6 简化编译和连接

4.7 exe的执行

4.8 可执行文件中的程序转入内存并运行的原理

4.9 程序执行过程的跟踪

使用debug来跟踪程序运行

五、[BX]和loop指令

引言

5.1 [BX]

mov bx,0
mov ax,[bx]
mov al,[bx]

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

mov ax,[0];0对应的字单元,主要单位要看操作对象(寄存器)
mov al,[0];字节

5.2 loop指令

assume cs:code
       code segment
       mov ax,2
       add ax,ax
          
       mov ax,4c00H
       int 21H
code ends
end
;计算2^3
assume cs:code
code segment
     mov ax,2
     add ax,ax
     add,ax,ax
          
     mov ax,4c00H
     int 21h
code ends
end
;计算2^12
assume cs:code
code segment
start:  mov ax,2
        mov cx,11
      p:add,ax,ax       
        loop p;p是标号
              
       mov ax,4c00H;masm默认数字是十进制
       int 21H
code ends
end start
;编程计算123*236,结果放在ax中
assume cs:code
code segment
start:mov ax,0
      mov cx,236
   an:add ax,123
     loop an
      mov ax,4c00H
      int 21H
code ends
end start
assume cs:code
code segment
start:mov ax,0
      mov cx,123
  pa:add ax,236
      loop pa
       
      mov ax,4c00H
      int 21H
 
code ends
end start

5.3 在Debug中跟踪用loop指令实现的循环程序

5.4 Debug和汇编编译器Masm对指令的不同处理

mov al,[0] ;将al赋值0
mov al,ds[0] ;将al赋值段地址为ds,偏移地址为0的内存单元中的内容
mov al,[bx] ;默认段地址为ds,将al赋值偏移地址为bx
mov al,ds:[bx] ;将al赋值段地址为ds,偏移地址为bx

5.5 loop和[BX]的联合应用

5.6 段前缀

5.7 一段安全的空间

在8086模式中,随意向一段内存空间写入数据是危险的,因为这段空间中可能存放着重要的系统数据或代码。

assume cs:code
code segment
    mov ax,0
    mov ds,ax
    mov ds:[26H],ax
    mov ax,4c00H
    int 21H
code ends
end

5.8 段前缀的使用

assume cs:code
code segment
          mov bx,0 ;(bx)=0,偏移地址从0开始
          mov cx,12 ;(cx)=12,循环12次
      s:  mov ax,offffh
          mov ds,ax ;(ds)=0ffffh
          mov dl,[bx] ;(ds)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
          mov ax,0020h
          mov ds,ax ;(ds)=0020h
          mov [bx],dl ;((ds)*16+(bx))=dl,将数据送入0020:bx
          inc bx ;(bx)=(bx)+1
          loop s
      
          mov ax,4c00h
          int 21h
code ends
end
;优化后的代码,优化了两次设置ds
assume cs:code
code segment
           mov ax,offffh
           mov ds,ax ;(ds)=0ffffh
           mov ax,0020h
           mov es,ax ;(es)=0020H
           mov bx,0 ;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0
           mov cx,12 ;(cx)=12,循环12次
       s:  mov dl,[bx] ;(ds)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
           mov es:[bx],dl ;((es)*16+(bx))=dl,将数据送入0020:bx
           inc bx ;(bx)=(bx)+1
           loop s
       
           mov ax,4c00h
           int 21h
code ends
end

六、包含多个段的程序

6.1在代码段中使用数据

assume cs:codesg
codesg segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
        ;dw,define word,定义字型数据,db定义字节型数据
        ;由于数据在代码段中,所以段地址是CS
        ;dw定义的数据在最开始的地方,所以偏移地址是0开始
    start:mov bx,0 ;第一条指令
        mov ax,0
        mov cx,8
    s:      add ax,cs:[bx]
        add bx,2
        loop s
        mov ax,4c00H
        int 21H
codesg ends
end start ;入口找end
定义字型数据

6.2 在代码段中使用栈

assume cs:codesg
codesg segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H;地址0~15
        dw 0,0,0,0,0,0,0,0;定义8个字型空数据,后面当作栈来使用,地址是16~31
    
    start:  
            mov ax,cs
            mov ss,ax
            mov sp,32;设置栈底ss:sp指向cs:32,十进制的32
            mov bx,0
            mov cx,8
           s:push cs:[bx]
            add bx,2
            loop s; 以上代码段0~15个单元中的8个字型数据一次入栈
    
            mov bx,0
            mov cx,8
          s0:pop cs:[bx]
            add bx,2
            loop s0;依次出栈8个执行数据到代码段0~15单元中
    
            mov ax,4c00h
            int 21h
codesg ends
end start;指明程序入口在start处

6.3 将数据、代码、栈放入不同的段

assume cs:codesg,ds:data,ss:stack;在源程序中为三个段进行有意义的名称
data segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
    
stack segment
        dw 0,0,0,0,0,0,0,0;定义8个字型空数据,后面当作栈来使用
stack ends
    
code segment
    start:      
            mov ax,stack
            mov ss,ax
            mov sp,16;设置栈底ss:sp指向stack:16,
            mov ax,data
            mov ds,ax;ds指向data段
            mov bx,0;ds:bx指向data段中的第一个单元
           s:push cs:[bx]
            add bx,2
            loop s; 以上代码段0~16个单元中的8个字型数据一次入栈
    
            mov bx,0
            mov cx,8
          s0:pop cs:[bx]
            add bx,2
            loop s0;依次出栈8个执行数据到代码段0~16单元中
    
            mov ax,4c00h
            int 21h
codesg ends
end start;指明程序入口在start处

检测点 6.1

检测点6.1

实验五

assume cs:codesg,ds:data,ss:stack
data segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
    
stack segment
        dw 0,0,0,0,0,0,0,0
stack ends
    
codesg segment
    start:  mov ax,stack
            mov ss,ax
            mov sp,16
            mov ax,data
            mov ds,ax
            push ds:[0]
            push ds:[2]
            pop ds:[2]
            pop ds:[0]
    
            mov ax,4c00h
            int 21h
codesg ends
end start

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

7.1 and和or指令

mov al,01100011B
and al,00111011B
;执行后 al=00100011B
and al,10111111B;将al第六位设为0
and al,01111111B;将al第七位设为0
and al,11111110B;将al第0位设为0
mov al,01100011B
and al,00111011B
;执行后 al=01111011B
and al,01000000B;将al第六位设为1
and al,10000000B;将al第七位设为1
and al,00000001B;将al第0位设为1

7.2 关于ASCII码

ASCII码表

7.3 以字符形式给出数据

assume cs:code,ds:data

data segment
        db 'unIx'
        db 'foRK'
data ends
    
    code segment
    start:  mov al,'a'
            mov bx,'b'
    
            mov ax,4c00h
            int 21h
code ends
end start
73 ascii 以字符形式给出数据

7.4 大小写转换的问题

大写 二进制 小写 二进制
A 01000001 a 01100001
B 01000010 b 01100010
C 01000011 c 01100011
D 01000100 d 01100100
;大小写转换
assume cs:codesg,ds:datasg
datasg segment
db'BaSiC'
db'iNfOfMaTiOn'
datasg ends
    
codesg segment
    start:  mov ax,datasg
            mov ds,ax;设置ds执行datasg段
            mov bx,0;设置(bx)=0,ds:bx指向'BaSiC'的第一个字母
            mov cx,5;设置循环次数,因为BaSiC有5个字母
              s:mov al,[bx];将ASCII码从ds:bx所指向的单元中取出
            and al,11011111B;口岸al中ASCII码的第5个位置变为0,变为大写字母
            mov [bx],al;转变后将ASCII码写回单元
            inc bx;(bx)加1,ds:bx指向下一个字母
            loop x
            mov bx,5;设置(bx)=5,ds:bx指向'iNfOfMaTiOn'的第一个字母
            mov cx,11
             s0:mov al,[bx]
            or al,00100000B
            mov [bx],al
            inc bx
            loop s0
    
            mov ax,4c00H
            int 21H
codesg ends
end start

7.5 [bx+idata]

;[bx+idata]可以写成以下格式
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
;使用debug查看内存
mov ax,2000H
mov ds:ax
mov bx,1000H
mov ax,[bx]
mov cx,[bx+1]
add cx,[bx+2]

7.6 用[bx+idata]的方式进行数组的处理

;改进大小写转换程序
assume cs:codesg,ds:datasg
datasg segment
db'BaSiC'
db'iNfOfMaTiOn'
datasg ends
    
codesg segment
    start:  mov ax,datasg
            mov ds,ax;设置ds执行datasg段
            mov bx,0;设置(bx)=0,ds:bx指向'BaSiC'的第一个字母
            mov cx,5;设置循环次数,因为BaSiC有5个字母
          s:mov al,[bx+0];将ASCII码从ds:bx所指向的单元中取出
            and al,11011111B;口岸al中ASCII码的第5个位置变为0,变为大写字母
            mov [bx],al;转变后将ASCII码写回单元
            mov [bx+5];定位第二个字符串的字符
            or al,00100000B
            mov [bx+5],al
            inc bx
            loop s
    
            mov ax,4c00H
            int 21H
codesg ends
end start
    include<stdio.h>

    char a[5]="BaSiC";
    char b[11]="iNfOfMaTiOn";
    main()
    {
        int i;
        i=0;
        do
        {
            a[i]=a[i]&0xDF;
            b[i]=b[i]|0x20;
            i++;
        }while(i<5);
    }

7.7 SI和DI

mov bx,0
mov ax,[bx]
    
mov si,0
mov ax,[si]
    
mov di,0
mov ax,[di]
;-------------
;下面的三组指令也实现了另一个组相同的功能
;-------------
mov bx,0
mov ax,[bx+123]
    
mov si,0
mov ax,[si+123]
    
mov di,0
mov ax,[di+123]
;用DI和SI实现复制到它后面的数据区中
assume cs:codesg,ds:datasg
datasg segment
db'welcome to asm!'
db'................'
datasg ends
    
codesg segment
    start  :mov ax,datasg
            mov ds,ax
            mov si,0
            mov di,16
            mov cx,8
          s:mov ax,[si]
            mov [di],ax
            add si,2
            add di,2
            loop s
    
            mov ax,4c00h
            int 21H
;------
;用数组的思维[bx(si或di)+idata]的方式优化程序
;------
assume cs:codesg,ds:datasg
datasg segment
db'welcome to asm!'
db'................'
datasg ends
    
codesg segment
    start  :mov ax,datasg
            mov ds,ax
            mov si,0
            mov cx,8
          s:mov ax,[si];第一个字符串的的第一个元素
            mov [si+16],ax;目标字符串的第二个元素
            add si,2
            loop s
    
            mov ax,4c00h
            int 21H
codesg ends
end start

7.8 [bx+si]和[bx+di]

    mov ax,2000h
    mov ds,ax
    mov bx,1000h
    mov si,0
    mov ax,[bx+si]
    inc si
    mov cx,[bx+si]
    inc si
    mov di,si
    mov ax,[bx+di]

7.9 [bx+si+idata]和[bx+di+idata]

    mov ax,2000h
    mov ds,ax
    mov bx,1000h
    mov si,0
    mov ax,[bx+2+si]
    inc si
    mov cx,[bx+si+2]
    inc si
    mov di,si
    mov ax,[bx+di+2]

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

数据段中的数据存储结构_2
assume cs:codesg,ds:datasg
datasg segment
    db'1. file          ';长度刚好都是16个字节
    db'2. edit          '
    db'3. search        '
    db'4. view          '
    db'5. options       '
    db'6. help          '
datasg ends
    
codesg segment
      start:
            mov ax,datasg
            mov ds,ax
            mov bx,0
            mov cx,6
          s:    
            mov al,[bx+3]
            and al,11011111B
            mov [bx+3],al
            add bx,16
            loop s
            
            mov ax,4c00h
            int 21h
codesg ends
end start
数据段中的数据存储结构2
;有bug,问题在于cx的使用,进行二重循环,只用一个循环计数器,造成在进行内层的时候覆盖了外层循环的循环计数值。
assume cs:codesg,ds:datasg
datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends
    
codesg segment
      start:mov ax,datasg
            mov ds,ax
            mov bx,0;用bx来定位行
            mov cx,4
         s0:mov si,0;用si来定位列
            mov cx,3
          s:mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            loop s0
            
            mov ax,4c00h
            int 21h
codesg ends
end start
712bug 712bug1 712bug2
loop s;三次循环后cx等于0了
add bx,16
loop s0;先是cx=cx-1再判断时候等于0,此时cx=FFFF不为0再循环,变成死循环了
assume cs:codesg,ds:datasg
    datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends
    
codesg segment
      start:
            mov ax,datasg
            mov ds,ax
            mov bx,0;用bx来定会行
            mov cx,4
         s0:
            mov dx,cx;用dx寄存器来临时存放外层cx的值
            mov si,0;用si来定位列
            mov cx,3
          s:
            mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            mov cx,dx;在进行外层循环的时候回复cx的值
            loop s0
            mov ax,4c00h
            int 21h
codesg ends
end start
assume cs:codesg,ds:datasg
    datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
    dw 0;定义一个字用来保存cx的值
datasg ends
    
codesg segment
      start:mov ax,datasg
            mov ds,ax
            mov bx,0;用bx来定位行
            mov cx,4
         s0:mov ds:[40h],cx;datasg:40h单元存放外层cx的值
            mov si,0;用si来定位列
            mov cx,3
          s:mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            mov cx,ds:[40h];在进行外层循环的时候回复cx的值
            loop s0
            mov ax,4c00h
            int 21h
codesg ends
end start
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends
    
stacksg segment
    dw 0,0,0,0,0,0,0,0;定义一个段,用作栈段,容量为16个字节
stacksg ends
    
codesg segment
      start:mov ax,stacksg
            mov ss,ax
            mov sp,16
            mov ax,datasg
            mov ds,ax
            mov bx,0;用bx来定位行
            mov cx,4
         s0:push cx;datasg:40h单元存放外层cx的值
            mov si,0;用si来定位列
            mov cx,3
          s:mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            pop cx;在进行外层循环的时候回复cx的值
            loop s0
            mov ax,4c00h
            int 21h
codesg ends
end start
数据段中的数据存储结构3
assume cs:codesg,ds:datasg,ss:stacksg
    
stacksg segment
stacksg ends
    
datasg segment
    db '1. display      '
    db '2. brows        '
    db '3. replace      '
    db '4. modify       '
datasg ends
codesg segment
      start:mov ax,stacksg
            mov ss,ax
            mov sp,16
            mov ax,datasg
            mov ds,ax
            mov bx,0
            mov cx,4
         s0:push cx
            mov si,0
            mov cx,4
          s:mov al,[bx+si+3]
            and al,11011111B
            mov [bx+si+3],al
            inc si
            loop s
            add bx,16
            pop cx
            loop s0
            
            mov ax,4c00h
            int 21h
codesg ends
end start

八、数据处理的两个基本问题

引言

8.1 bx、si、di、bp

    ;以下指令是错误的
    mov ax,[ax]
    mov ax,[cx]
    mov ax,[dx]
    mov ax,[ds]
    mov ax,[bx+bp]
    mov ax,[si+di]
    mov ax,[bx]
    mov ax,[si]
    mov ax,[di]
    mov ax,[bp]
    mov ax,[bx+si]
    mov ax,[bx+di]
    mov ax,[bp+si]
    mov ax,[bp+di]
    mov ax,[bx+si+idata]
    mov ax,[bx+di+idata]
    mov ax,[bp+si+idata]
    mov ax,[bp+di+idata]

8.2 机器指令处理的数据所在的位置

机器码 汇编指令 指令执行前数据的位置
89C3 mov bx,[0] 内存,ds:0单元
89C3 mov bx,ax CPU内部,ax寄存器
BB0100 mov bx,1 CPU内部,指令缓冲器

8.3 汇编语言中数据位置的表达

8.4 寻址方式总小结

寻址方式总结_1

8.5 指令要处理的数据有多长

    mov word ptr ds:[0],1
    inc word ptr [bx]
    inc word ptr ds:[0]
    add byte ptr [bx],2
    ;假设内存2000:1000 FF FF FF FF FF FF ……
    ;如果用以下指令
    mov ax,2000H
    mov ds,ax
    mov byte ptr [1000H],1
    ;那么内存中的内容变为
    ;2000:1000 01 FF FF FF FF FF ……
    如果是用以下指令
    mov ax,2000H
    mov ds,ax
    mov word ptr [1000H],1
    ;那么内存中的内容变为
    ;2000:1000 01 00 FF FF FF ……

8.6 寻址方式的综合应用

86题目_1
    mov ax,seg
    mov ds,ax
    mov bx,60h;确定记录物理地址:ds:bx
    mov word ptr [bx+0ch],38;寄存器相对寻址     排名字段改为38
    add word ptr [bx+0eh],70;收入字段增加70
    
    mov si,0;用si来定位产品字符串中的字符
    mov byte ptr [bx+10h+si],'V';相对基址变址寻址
    inc si
    mov byte ptr [bx+10h+si],'A'
    inc si
    mov byte ptr [bx+10h+si],'X'
    struct company  /*定义一个公司记录的结构体*/
    {
        char cn[3]; /*公司名称*/
        char hn[9]; /*总裁姓名*/
        int pm;     /*排名*/
        int sr;     /*收入*/
        char cp[3]; /*著名产品*/
    };
    
    struct compant dec={"DEC","Ken Olsen",137,40,"PDF"};
    /*定义一个公司记录的变量,内存中将存有一条公司的记录*/
    mian()
    {
        int i;
        dec.pm=38;
        dec.sr=dec.sr+70;
        i=0;
        dec.cp[i]='V';
        i++;
        dec.cp[i]='A';
        i++;
        dec.cp[i]='X';
        return 0;
    }
    mov ax,seg
    mov ds,ax
    mov bx,60h;记录首地址送入bx
    
    mov word ptr [bx].och,38;排名字段改为38
    add word ptr [bx].0eh,70;收入字段增加70
    
    ;产品名字段改为字符串'VAX'
    mov si,0
    mov byte ptr [bx].10h[si],'V'
    inc si
    mov byte ptr [bx].10h[si],'A'
    inc si
    mov byte ptr [bx].10h[si],'X'

8.7 div指令

div reg(寄存器)
div 内存单元。
        div byte ptr ds:[0]
        div byte ptr [bx+si+idata]
        ;al放商,ah放余数
        
        div word ptr es:[0]
        div word ptr [bx+si+idata]
        ;ax放商,dx放余数
除数 被除数
8位 16为(AX)
16位 32位(DX高16位+AX低16位)
运算 8位 16位
AL AX
余数 AH DX
    ;被除数1001可用ax寄存器存放,除数100可用8位寄存器存放,要进行8位除法。
    mov ax,1001
    mov bl,100
    div bl
    ;执行后al的值等于0AH(10),ah的值等于1(余数为1)。
    ;被除数100001大于2^16=65535(FFFF),不能用ax来存放,要用dx和ax两个寄存器联合存放。除数小于255,可用一个8位寄存器存放,但是被除数是32位的,除数应为16位,所以要用一个16位寄存器来存放除数。
    ;100001的十六进制为186A1H,100001的高16位(1)存放在dx,低16位(86AH)存放在ax中。
    mov dx,1
    mov ax,86A1H
    mov bx,100
    div bx
    ;执行后ax内容等于03E8H(即1000),dx的值等于1(余数)。

8.8 伪指令dd

data segment
        db 1;第一个数据为01h,在data:0处,占1个字节
        dw 1;第二个数据为0001h,在data:1处,占1个字
        dd 1;第三个数据为00000001h,在data:3处,占2个字
data ends
data segment
    dd 100001H;低16位存储在ax中,高16位存储在dx中
    dw 100
    dw 0
data ends

    mov ax,data
    mov ds,ax
    mov ax,ds:[0];低16位存储在ax中
    mov dx,ds:[2];高16位存储在dx中
    div word ptr ds:[4]
    mov ds:[6],ax

8.9 伪指令dup

db 3 dup(0)
;定义了3个字节,它们的值都是0,等同于db 0,0,0。
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
db 3 dup('abc','ABC')
;定义了18个直接,它们是'abcABCabcABCabcABC'

实验七 寻址方式在结构化数据访问中的应用

实验7
实验7数据
    ;初始化阶段
    mov ax,data
    mov ds,ax
    mov ax,table;data已经被占用
    mov es,ax
    
    mov bx,0
    mov si,0
    mov di,0
    mov cx,21
    
    ;存放年份,每一个bx就是一个字节
    mov al,[bx]
    mov es:[di],al
    mov al,[bx+1]
    mov es:[di+1],al
    mov al,[bx+2]
    mov es:[di+2],al
    mov al,[bx+3]
    mov es:[di+3],al
    
    ;存放公司的总收入
    mov ax,[bx+54H];第一个年收入是dd数据类型,段地址为54H
    mov dx,[bx+54H]
    mov es:[di+5H],ax
    mov es:[di+7H],dx
    
    ;存放公司的人数
    mov ax,[si+0A8H];第一个人数的数据段地址为0A8H
    mov es:[di+0A8H],ax
    
    ;计算人均收入并存放
    mov ax,[bx+54H]
    mov dx,[bx+56H];这两句诗初始化被除数
    div word ptr,ds:[si+0A8H];除以人数
    mov es:[di+0dH],ax;将商放入指定位置
    
    ;为下一次循环时存放数据做准备
    add bx,4;bx确定年份和收入
    add si,2;si确定人数
    add di,16;di确定的是每行的列数
assume cs:codesg,ds:data,es:table
    
data segment
        db '1975','1976' '1977' ……
        dd 16,22,382 ……
        dw 3,7,9 ……
        ;数据在题目中
data ends
    
table segment
        db 21 dup('year summ ne ?? ')
table ends
    
      start:mov ax,data
            mov ds,ax
            mov ax,table
            mov es,ax
    
            mov bx,0
            mov si,0
            mov di,0
            mov cx,21
    
          s:mov al,[bx]
            mov es:[di],al
            mov al,[bx+1]
            mov es:[di+1],al
            mov al,[bx+2]
            mov es:[di+2],al
            mov al,[bx+3]
            mov es:[di+3],al
    
            mov ax,[bx+54H]
            mov dx,[bx+56H]
            mov es:[di+5H],ax
            mov es:[di+7H],dx
    
            mov ax,[si+0A8H]
            mov es:[di+0AH],ax
    
            mov ax,[bx+54H]
            div word ptr ds:[si+0A8H]
            mov es:[di+0dH],ax
    
            add bx,4
            add si,2
        loop s
mov ax,4c00h
int 21h
codesg ends
end start

九、转移指令的原理

引言

9.1 操作符offset

    assume cs:codesg
    codesg segment
    
      start:mov ax,offset start;相当于 mov ax,偏移地址0,段地址是从0开始
          s:mov ax,offset s;相当于 mov ax,3,标记的是代码段中的第二条指令,第一条指令长度为3个字节,则s的偏移地址为3
    
    codesg ends
    end start

9.2 jmp指令

9.3 依据位移进行转移的jmp指令

    assume cs:codesg
    codesg segment
    
      start:mov ax,0
          jmp short s
          add ax,1
          s:inc ax
    
    codesg ends
    end start
转移位移的计算方法

9.4 转移的目的地址在指令中的jmp指令

          assume cs:codesg
          codesg segment
            start:mov ax,0
                mov bx,0
                jmp far ptr s
                db 256 dup(0)
              s:add ax,1
                  inc ax
          codesg ends
          end start
94debug

附注3 汇编编译器(masm.exe)对jmp的相关处理

这里写图片描述

9.5 转移地址在寄存器中的jmp指令

9.6 转移地址在内存中的jmp指令

检测点 9.1

检测点 9.1

9.7 jcxz指令

检测点 9.2

检测点9.2_1

9.8 loop指令

检测点 9.3

检测点 9.3

9.9 根据位移进行转移的意义

    jmp short 标号
    jmp near ptr 标号
    jcxz 标号
    loop 标号

9.10 编译器对转移位移超界的检测

assume cs:code
    
    code segment
    start:  jmp short s
          db 128 dup(0)
          s:mov ax,0FFFFH
    code ends
end start
910err 910err2

实验8

assume cs:codesg
codesg segment
            mov ax,4c00h
            int 21h
    
      start:mov ax,0
          s:nop
            nop;nop占用两个字节,不执行任何操作
    
            mov di,offset s
            mov si,offset s2
            mov ax,cs:[si];jmp short s1的机器码给了ax
            mov cs:[di],ax;覆盖到指令 s:nop nop那
    
         s0:jmp short s;s那已经被jmp short s1机器码覆盖
         s1:mov ax,0
            int 21h
            mov ax,0
         s2:jmp short s1;jmp -8h,向上跳到s1,s1又向上跳-10字节
            nop
codesg ends
end start   
test8debug

实验9

实验9
assume cs:code,ds:data,ss:stack
    
data segment
            db'welcome to masm!';定义要显示的字符串(共16字节)
            db 02H,24H,71H;定义字符的属性
data ends
    
stack segment
            dw 8 dup(0)
stack ends
    
code segment
    
    start:
            mov ax,data
            mov ds,ax
            mov ax,stack
            mov ss,ax
            mov sp,10H
    
            xor bx,bx;bx清零,用来索引颜色
            mov ax,0b872H;算出屏幕第12行中间的显存的段起始位置放入ax中
    
            mov cx,3;s3循环控制行数,要显示三个字符串外循环为3次
        s3: push cx;三个进栈操作为外循环s3保存相关寄存器的值
            push ax;以防止它们的值在内循环中被破坏
            push bx
    
            mov es,ax;此时es为屏幕第12行中间的显存的段起始位置
    
            mov si,0;si用来索引代码列的字符
            mov di,0;di用来定位目标列
    
            mov cx,10H
            ;s1循环控制存放的字符,一个字符串中含有10H个字节内循环为10H次
        s1: mov al,ds:[si]
            mov es:[di],al
            inc si
            add id,2
            loop s1;吃循环实现偶地址中存放字符
    
            mov di,1;设置di的值为1,为在显存奇数地址中存放字符的颜色属性做准备
            pop bx
            mov al.ds:[bx+10H];取消颜色属性
            inc bx
    
            mov cx,10H;第二个内循环也为10H
        s2: mov es:[di],al
            add di 2
            loop s2;此循环实现奇数地址存放字符的颜色属性
    
            ;以下4句为下一趟外循环做准备
            pop ax
            add ax,0AH;将显存的段地址起始地址设置为当前行的下一行
                      ;[在段地址中甲0aH,相当于在偏移地址中加了0a0h(=160d)]
            pop cx
            loop s3
    
            mov ax,4C00H
            int 21H
            
code ends
end start
welcome to masm

十、CALL和RET指令

引言

10.1 ret和retf指令

assume cs:codesg
          
 stack segment
              db 16 dup(0)
stack ends
          
          codesg segment
              mov ax,4c00h
              int 21h
          
          start:
              mov ax,stack
              mov ss,ax
              mov sp,16
              mov ax,0
              
              push ax
              
              mov bx,0
              ret
codesg ends
          
end start
1012debug_1
assume cs:codesg
    
stack segment
        db 16 dup(0)
stack ends
    
codesg segment
        mov ax,4c00h
        int 21h
    
    start:
        mov ax,stack
        mov ss,ax
        mov sp,16
        mov ax,0
        push cs
        push ax
    
        
        mov bx,0
        retf
codesg ends
    
end start

检测点 10.1

检测点10.1

10.2 call指令

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

push IP
jmp near 标号

检测点 10.2

检测点10.2

10.4 转移的目的地址在指令中的call指令

push CS
push IP
jmp far ptr 标号

检测点 10.3

检测点 10.3

10.5 转移地址在寄存器中的call指令

          push IP
          jmp 16位寄存器

检测点 10.4

检测点 10.4

10.6 转移地址在内存中的call指令

       call word ptr 内存单元地址;段内跳转
       call dword ptr 内存单元地址;段间跳转
       push IP
       jmp word ptr 内存单元地址
       mov sp,10h
       mov ax,0123H
       mov ds:[0],ax
       call word ptr ds:[0]
       ;执行后IP的值等于0123H,SP的值等于0EH
       push CS
       push IP
       jmp word ptr 内存单元地址
       mov sp,10h
       mov ax,0123H
       mov ds:[0],ax
       mov word ptr ds:[0],0
       call dword ptr ds:[0]
       ;执行后IP的值等于0123H,SP的值等于0CH,CS的值等于0

检测点 10.5

检测点10.5

10.7 call和ret的配合使用

assume cs:code
code segment
      start:
            mov ax,1
            mov cx,3
            call s
            mov bx,ax
            mov ax,4c00h
            int 21h
          s:
            add ax,ax
            loop s
            ret
code ends
end start
107call

10.8 mull指令

mull reg
              
mull 内存单元
mull byte ptr ds:[0]

mull word ptr [bx+si+idata]
;(ax)=(ax)*((ds)*16+(bx)+(si)+idata)
;(dx)=(ax)*((ds)*16+(bx)+(si)+idata)
;计算100*10,两个数都小于255,可以做8位乘法
mov ax,100
mov bx,10
mull bl
;结果(ax)=1000(03E8H)
              
;计算100*1000,1000都大于255,要做16位乘法
mov ax,100;高位自动补零
mov bx,10000
mull bx
;结果(ax)=4240H,(dx)=000FH,F4240H=1000000

10.9 模块化程序设计

10.10 参数和结果传递的问题

cube:mov ax,bx
               mul bx
               mul bx
               ret
assume cs:code
          
data segment
          dw 1,2,3,4,5,6,7,8
          dd 8 dup (0)
data ends
          
code segment
          start:
              mov ax,data
              mov ds,ax
              mov si,0;ds:si指向第一组word单元
              mov di,16;ds:di指向第二组dword单元
              
              mov cx,8
          s:  mov bx,[si]
              call cube
              mov [di],ax
              mov [di+2],dx
              add si,2;ds:di指向下一个word单元
              add di,4;ds:di指向下一个dword单元
              loop s
              
              mov ax,4c00h
              int 21h
                  
             cube:mov ax,bx
              mul bx
              mul bx
              ret
code ends
end start

10.11 批量数据的传递

assume cs:code
    
data segment
    db'conversation'
data ends
    
    start:
        mov ax,data
        mov ds,ax
        mov si,0;ds:si指向字符串(批量数据)所在空间的首地址
    
        mov cx,12;cx存放字符串的长度
        call capital
    
        mov ax,4c00h
        int 21h
    
    capital:
        add byte ptr [si],11011111B
        inc si
        loop capital
        ret
code ends

10.12 寄存器冲突的问题

    capital:
        mov cl,[si];低8位
        mov ch,0;高8位设置为0
        jcxz ok;如果(cx)=0则结束,如果不是0则处理
        and byte ptr [si],11011111B
        inc si
        jmp short capital
    ok:
        ret
    assume cs:code
    data segment
    db'word',0
    db'unix',0
    db'wind',0
    db'good',0
    data ends
    ;此程序有bug,cx有问题
    assume cs:code
    
    data segment
    db'word',0
    db'unix',0
    db'wind',0
    db'good',0
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0
    
            mov cx,4
        s:
            mov si,bx
            call capital
            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
    
    code ends
    end start

实验十

test10_1_1
test10_1_2
test10_1_3
assume cs:code
    
data segment
    db 'welcome to masm!',0
data ends
    
code segment
    start:
            mov dh,8;行号
            mov dl,3;列号
            mov cl,2;颜色属性
            mov ax,data
            mov ds,ax
            mov si,0
            call show_str
    
            mov ax,4c00h
            int 21h
        show_str:;子程序
            push cx
            push si
    
            mov al,0A0h;每行有80*2=160个字节=0a0h
            dec dh;行号在显存中下标从0开始,所以减1
            mul dh;相当于从第(n-1)*0a0h个byte单元开始
    
            mov bx,ax;定位好的位置偏移地址存放在bx里(行)
    
            mov al,2;每个字符占2个字节
            mul dl;定位列,结果ax存放的是定位好的列的位置
            sub ax,2;列号在显存中下标从0开始,又因为是偶字节存放字符,所以减2
    
            add bx,ax;此时bx中存放的是行与列的偏移地址
    
            mov ax,0B800h;显存开始的地方
            mov es,ax;es中存放的是显存的第0页的起始地段地址
    
            mov di,0;di指向显存的偏移地址,确定指向下一个要处理的字符的位置
            mov al,cl;cl存放颜色参数,下边cl要用来临时存放要处理的字符
            mov ch,0;下边cx存放的是每次准备处理的字符
        s:
            mov cl,ds:[si];指向'welcome to masm ',0
    
            jcxz ok;cl为0时跳转
            mov es:[bx+di],cl;偶地址存放字符
            mov es:[bx+di+1],al;奇地址存放字符的颜色属性
            inc si
            add di,2;指向了下个字符
            jmp short s ;无条件跳转,jcxz是离开的关键跳
    
        ok:
            pop si
            pop cx
            ret;定义结束
code ends
end start
    

test101
    assume cs:code,ss:stack
    
    stack segment
    dw 8 dup(0)
    stack ends
    
    code segment
    
    start:
            mov ax,stack
            mov ss,ax
            mov sp,10h
            mov ax,4240h
            mov dx,0fh
            mov xx,0ah
    
            call divdw
    
            mov ax,4c00h
            int 21h
    
        divdw:
            push ax;低16位先保存
            mov ax,dx;ax这时是高16位了
            mov dx,0;为了不影响余数位和高位数
            div cx
            mov bx,ax
            pop ax
            div cx
            mov cx,dx
            mov dx,dx
            ret
    code ends
    end start
assume cs:code,ds:data
    
data segment
    db 10 dup(0)
data ends
    
code segment
    start:
            mov ax,12666
            mov bx,data;指向字符串的首地址
            mov ds,bx
            mov si,0
    
            call dtoc;实现将word型整数转化为字符串并存储
    
            mov dh,8;打印初始化
            mov dl,3
            mov cl,0cah
    
            call show_str;开始打印字符串
    
            mov ax,4c00h
            int 21h
        dtoc:
            push dx
            push cx
            push ax
            push si
    
            mov bx,0;bx在子程序中用来存放位数,用栈来临时存放修改后的字符
        s1:
            mov cx,10d;d表示十进制,cx准备被除,用取余法来取出数字
            mov dx,0
    
            div cx;除以十
            mov cx,ax;得到的商复制给cx,要利用jcxz
            jcxz s2;当商为0则跳到s2
            add dx,30h;余数加上30h得到相应的ascii码
            push dx
            inc bx
    
            jmp short s1
        s2:
            add ax,30h;当商为0的时候,余数为个位
            push dx
    
            inc bx;再进行一次栈操作(补充当商为零而余数不为零时的情况)
            mov cx,bx;总共有bx位进栈,所以循环次数为bx
            mov si,0
        s3:
            pop ax;s3实现将栈中的数据依次出栈放到指定的内存中
            mov [si],al
            inc si
            loop s3
    
        okay:
            pop bx
            pop si
            pop ax
            pop dx
    
            ret
        show_str:;子程序
            push bx
            push cx
            push si
    
            mov al,0A0h;每行有80*2=160个字节=0a0h
            dec dh;行号在显存中下标从0开始,所以减1
            mul dh;相当于从第(n-1)*0a0h个byte单元开始
    
            mov bx,ax;定位好的位置偏移地址存放在bx里(行)
            mov al,2;每个字符占2个字节
            mul dl;定位列,结果ax存放的是定位好的列的位置
    
            sub ax,2;列号在显存中下标从0开始,又因为是偶字节存放字符,所以减2
            add bx,ax;此时bx中存放的是行与列的偏移地址
    
            mov ax,0B800h;显存开始的地方
            mov es,ax;es中存放的是显存的第0页的起始地段地址
    
            mov di,0;di指向显存的偏移地址,确定指向下一个要处理的字符的位置
            mov al,cl;cl存放颜色参数,下边cl要用来临时存放要处理的字符
            mov ch,0;下边cx存放的是每次准备处理的字符
        S:
            mov cl,ds:[si]
    
            jcxz ok
    
            mov es:[bx+di],cl
            mov es:[bx+di+i],al
    
            inc si
            add di,2
    
            jmp short s
    
        ok:
            pop si
            pop cx
            pop bx
            ret
code ends
end start

十一、标志寄存器

引言

flag

11.1 ZF(zero flag)标志

    mov ax,1
    sub ax,1
    
    mov ax,1
    and ax,0
    ;指令执行后,结果为0,则ZF=1
    
    mov ax,2
    sub ax,1
    
    mov ax,1
    or ax,0
    ;指令执行后,结果为1,则ZF=0

11.2 PF标志

    mov al,1
    add al,10
    ;执行结果为00001011B,有3个1,则PF=0

    mov al,1
    or al,10
    ;执行后结果为00000011B,有2个1,则PF=1

11.3 SF(sign flag)标志

mov al,10000001B
add al,1
;执行指令后al的值是10000010B,无符号数130,有符号数-126

检测点 11.1

检测点11.1

11.4 CF(carry flag)标志

    mov al.98h
    add al,al;执行后(al)=30h,cf=1,cf记录了从最高有效位向更高位的进位值
    add al,al;执行后(al)=60h,cf=0,cf记录了从更高有效位向更高位的进位值
    
    mov al,97h
    sub al,98h;执行后(al)=ffh,cf=1,cf记录了向更高位的借位值
    sub al,al;执行后(al)=0,cf=0,cf记录了向更高位的借位值

11.5 OF(overflow flag)标志

assume cs:code
    
    code segment
    
    start:
            mov al,01100010b
            add al,01100011b
    
            mov ax,4c00h
            int 21h
    code ends
end start
1150f_1
    assume cs:code
    
    code segment
    
    start:
            mov al,10001000b
            add al,11110000b
    
            mov ax,4c00h
            int 21h
    code ends
    end start
115of2
assume cs:code

    code segment
    start:
            mov al,98h
            add al,al
            add al,al
    
            mov ax,4c00h
            int 21h
    code ends
end start

CFdebug
assume cs:code

    code segment
    start:
            mov al,97h
            sub al,98h
            add al,al
    
            mov ax,4c00h
            int 21h
    code ends
end start

114cf2
    mov al,98d
    add al,99d
    ;对于无符号数运算,98+99没有进位,CF=0
    ;对于有符号数运算,98+99发生溢出,OF=1

检测点 11.2

检测点11.2

11.6 adc指令

    mov ax,2
    mov bx,1
    sub bx,ax
    adx ax,1
    ;执行后 (ax)=4,相当于计算(ax)+1+CF=2+1+1+4
    
    mov ax,1
    add ax,ax
    adc ax,3
    ;执行后(ax)=5,相当于执行(ax)+3+CF=2+3+0=5
    
    mov al,98H
    add al,al
    adx al,3
    ;执行后 (ax)=34H,相当于执行(ax)+3+CF=30H+3+1=34H
    mov ax,001EH
    mov bx,0F000H
    add bx,1000H
    adc ax,0020H
    mov ax,001EH
    mov bx,0F000H
    mov cx,1000H
    add cx,1EF0H
    add bx,1000H
    adc ax,0020H
assume cs:code,ds:data
    data segment
            db 16 dup(88H)
            db 16 dup(11H)
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov si,0
            mov di,16
    
            mov cx,8
            call add128
    
            mov ax,4C00H
            int 21H
    add128:
            push ax
            push cx
            push si
            push di
    
            sub ax,ax;将CF设置为0
        s:
            mov ax,[si]
            adc ax,[di]
            mov [si],ax
            inc si;不能用add si,2代替
            inc si;因为会影响cf位
            inc di;而loop和inc不会影响
            inc di
    
            loop s
    
             pop di
             pop si
             pop cx
             pop ax
             ret
    code ends
end start

11.7 sbb指令

    mov bx,1000H
    mov ax,003EH
    sbb bx,2000H
    sbb ax,0020H

11.8 cmp指令

    cmp ax,ax
    ;执行后结果为0,ZF=1,PF=1,SF=0,CF=0,OF=0

    mov ax,8
    mov bx,3
    cmp ax,bx
    ;执行后ax、bx的值不变,ZF=0,PF=1,SF=0,CF=0,OF=0
    cmp ax,bx
118cmp_1 118cmp2

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

指令 含义 检测的相关标志位
je 等于则转移 ZF=1
jne 不等于则转移 ZF=0
jb 低于则转移 CF=1
jnb 不低于则转移 CF=0
ja 高于则转移 CF=0 and ZF=0
jna 不高于则转移 CF=1 or ZF=1
j e ne b nb a na
jump equal not equal below not below above not above
        cmp ah,bh
        je s;ZF=1则跳转
        add ah,bh
        jmp short ok
    s:
        add ah,bh
    ok:ret
        mov ax,0
        mov ax,0
        je s
        inc ax
    s:
        inc ax
    ;执行后ax的值等于1,add ax,0使得ZF=1,所以je指令将进行转移。

课堂练习

    ;方案一
assume cs:code
    
    data segment
            db 8,11,8,1,8,5,63,38
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0;ds:bx指向第一个字节
            mov ax,0;初始化累加器
            mov cx,0
    
        s:
            cmp byte ptr [bx],8;和8进行比较
            jne next;如果不相等转到next,继续循环
            inc ax;如果相等就计数值加1
    
        next:
            inc bx
            loop s;执行后:(ax)=3
    
            mov ax,4c00h
            int 21h
    code ends
end segment 

</br>

;方案二
assume cs:code
    
    data segment
            db 8,11,8,1,8,5,63,38
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0;ds:bx指向第一个字节
            mov ax,0;初始化累加器
            mov cx,0
    
        s:
            cmp byte ptr [bx],8;和8进行比较
            je ok;如果不相等转到ok,继续循环
            jmp short next;如果不想等就转到next,继续循环
    
        ok:
            inc ax;如果相等就计数值加1
    
        next:
            inc bx
            loop s;执行后:(ax)=3
    
            mov ax,4c00h
            int 21h
    code ends
end segment
assume cs:code
    
    data segment
            db 8,11,8,1,8,5,63,38
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0;ds:bx指向第一个字节
            mov ax,0;初始化累加器
            mov cx,0
    
        s:
            cmp byte ptr [bx],8;和8进行比较
            jna next;如果大于8转到next,继续循环
            inc ax;如果大于就计数值加1
    
        next:
            inc bx
            loop s;执行后:(ax)=3
    
            mov ax,4c00h
            int 21h
    code ends
end segment 

检测点 11.3

检测点11.3

11.10 DF(direction flag)标志和串传送指令

11.11 pushf和popf

    ;下面的程序执行后ax的值是多少?
    mov ax,0
    push ax
    popf
    mov ax,0fff0h
    add ax,0010h
    pushf
    pop ax 
    and al,11000101b
    and ah 00001000b
assume cs:code
    
    data segment
            db'welcome to masm!'
            db 16 dup(0)
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov si,0;指向data:0
            mov es,ax
            mov di,16;指向data:16
            mov cx,16;rep循环16次
    
            cld;设置DF=0,正向传送
            rep movsb
    
            mov ax,4c00h
            int 21h
    code ends
end start
1110
assume cs:code
    
    data segment
            db 16 dup(0)
    data ends
    
    code segment
    start:
            mov ax,0f00h
            mov ds,ax
            mov si,0ffffh;指向f0000:ffff
            mov ax,data
            mov es,ax
            mov di,16;指向data:15
            mov cx,16;rep循环16次
    
            std;设置DF=1,逆向传送
            rep movsb
    
            mov ax,4c00h
            int 21h
    code ends
end start

检测点 11.4

检测点11.4

11.12 标志寄存器在Debug中的表示

debugflag
标志 值为1的标记 值为0的标记
OF OV NV
SF NG PL
ZF ZR NZ
PF PE PO
CF CY NC
DF DN UP

十二、内中断

引言

12.1 内中断的产生

12.2 中断处理程序

12.3 中断向量表

12.4 中断过程

12.5 中断处理程序和iret指令

pop IP
pop CS
popf

12.6 除法错误中断的处理

assume cs:codesg
    
    codesg segment
    start:
            mov ax,1000h
            mov bh,1    
            div bh
    codesg ends
    
end start
126
debug 126

12.7 编程处理0号中断

除法溢出对应的中断类型码为0,它的中断处理程序的入口地址应该从0* 4+2地址单元开始存放,段地址存放在0* 4+2字单元中,偏移地址存放在0*4字单元中。也就是改变后的中断处理程序的段地址0存放在0000:0002字单元中,偏移地址200H存放在0000:0000字单元中。如果要显示的字符串在程序的data段中,那么程序执行完成后返回,它所占用的内存空间被系统释放,在其中存放的信息也可能被别的信息覆盖。

assume cs:code
          
          code segment
          start:
                  mov ax,cs
                  mov ds,ax
                  mov si,offset do0;设置ds:di指向源地址
                  mov ax,0
                  mov es,ax
                  mov di,200h;设置es:si指向目的地址
                  mov cx,offset do0end - offset do0;设置cx为传输长度,编译器可以识别加减乘除运算符
                  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,4c00h
                  int 21h
              do0:
                  jmp short do0start
                  db"welcome to masm!";在代码段中存储数据
              do0start:
                  mov ax,cs
                  mov ds,ax
                  mov si,202h;jmp short do0start这条指令栈两个字节
                  ;显示字符串,设置es:di指向字符串
                  mov ax,0b800h;显存空间,直接显示在显示器上
                  mov es,ax
                  mov di,12*160+36*2;这只es:di指向显存空间的中间位置
                  mov cx,16;设置cx为字符串(welcome to masm!)长度
              s:
                  mov al,[si]
                  mov es:[di],al
                  inc si
                  add di,1
                  mov al,02h
                  mov es:[di],al
                  add di,1
                  loop s
          
                  mov ax,4c00h
                  int 21h
              do0end:
                  nop
          
          code ends
end start
do0 do02

12.8 单步中断

12.9 响应中断的特殊情况

在执行完向ss寄存器传送数据的指令后,即便检测到了中断信号CPU也不会响应。因为ss:sp指向栈顶,对他们的设置应该连续完成。如果在执行完设置ss指令后mCPU响应中断引发中断过程,要在栈中压入标志寄存器、CS和IP的值。而ss改变,sp并未改变则ss:sp指向不是正确的栈顶将引发错误。

应该 不应该
mov ax,1000h mov ax,1000h
mov ss,ax mov ss,ax
mov sp,0 mov ax,0
mov ax,0 mov sp,0

十三、int 指令

引言

13.1 int 指令

13.2 编写供应用程序调用的中断例程

             ;计算
ssume cs:code
             code segment
             start:
                     mov ax,3456
                     int 7ch
                     add ax,ax
                     adc ax,dx
                     mov ax,4c00h
                     int 21h
             code ends
             end start
             
             
             
             ;安装程序
             assume cs:code
             code segment
             start:
                     mov ax,cs
                     mov ds,ax
                     mov si offset sqr;设置ds:si指向源地址
                     mov ax,0
                     mov es,ax
                     mov di,200h;设置es:di指向目的地址
                     mov cx,offset sqrend- offset sqr;设置cx为传输长度
                     cld;设置传输方向为正
                     rep movsb
             
                     mov ax,0
                     mov es,ax
                     mov word ptr es:[7ch*4],200h
                     mov word ptr ws:[7ch*4+2],0
                     mov ax,4c00h
                     int 21h
                 sqr:
                     mul ax
                     iret
                 sqrend:
                     nop
             code ends
end start
assume cs:code
             data segment
                     db'conversation',0
             data ends
             
             code segment
             start:
                     mov ax,data
                     mov ds,ax
                     mov si,0
                     int 7ch
                     mov ax,4c00h
                     int 21h
             code ends 
             end start
             
             
             
             
             
             
             assume cs:code
             code segment
             
             start:
                     mov ax,cs
                     mov ds,ax
                     mov si,offset capital
                     mov ax,0
                     mov es,ax
                     mov di 200h
                     mov cx,offset capitalend - offset capital
                     cld
                     rep movsb
             
                     mov ax,0
                     mov es,ax
                     mov word ptr es:[7ch*4],200h
                     mov word ptr es:[7ch*4+2],0
             
                     mov ax,4c00h
                     int 21h
             
                 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
                     iret
             
                 capitalend:
                     nop
             code ends
end start

13.3 对int、iret和栈的深入理解

loop指令需要循环次数和到标号的位移。为了模拟loop指令7ch中断例程应具备下面dec cx和如果cx的值不等于0则转移到标号s处。

- int 7ch引发中断过程后,进入7ch中断例程在中断过程中当前的标志寄存器、CS和IP都要压栈。此时压入的CS和IP中的内容分别是调用程序的段地址(可以认为是标号s的段地址)和int 7ch后一条指令的偏移地址(即标号se的偏移地址)。使用iret指令用栈中的内容设置CS、IP,从而实现转移到标号s处。

```asm
assume cs:code

    code segment
    start:
            mov ax,0b800h;显存地址
            mov es,ax
            mov di,160*12
            mov bx,offset s- offset se;设置从标号s的转移位移
            mov cx,80
        s:
            mov byte ptr es:[di],'!'
            add di,2
            int 7ch;如果cx的值不等于0则转移到标号s处
        se:
            nop
    
            mov ax,4c00h
            int 21h
code ends
end start
    
    
    
    
    ;7ch中断例程
        lp:
            push bp
            mov bp,sp;
            dec cx
            jcxz lpret
            add [bp+2],bx
        lpret:
            pop bp
            iret
```

13.4 BIOD和DOS所提供的中断例程

13.5 bios和dos中断例程的安装过程

13.6 bios中断例程应用

    mov ah,2;表示调用10h号中断例程的2号子程序,功能为设置光标位置
    mov bh,0;页号
    mov dh,5;行号
    mov dl 12;列号
    int 10h;
;功能为在光标位置显示字符功能
mov ah,9;置光标,调用9号子程序
mov al,'a';字符
mov bl,7;颜色属性,和在显存中的属性字节的格式相同
mov bh,0;第0页
mov cx,3;字符重复个数
int 10h
​```
- 编程:在屏幕的第5行12列显示3个红底高亮闪烁绿色的'a'
​```asm
    assume cs:code
    
    code segment
            mov ah,2;设置光标
            mov bh,0;第0页
            mov dh,5;dh中放行号
            mov dl,12;dl中放列号
            int 10
    
            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
136

13.7 dos中断例程应用

          mov ah,4ch;程序返回
          mov al,0;返回值0是正常返回
          ;合起来写就是 mov ax,4c00h
          int 21h

十四、端口

引言

14.1 端口的读写

;对0~255以内的端口进行读写
in al,20h;从20h端口读入一个字节
out 20h,al;往20h端口写入一个字节
;对256~65535的端口进行读写时,端口放在dx中
mov dx,3f8h;将端口号3f8h送入dx
in al,dx;从3f8端口读入一个字节
out 3f8h,al;往3f8h端口写入一个字节

14.2 CMOS RAM芯片

14.3 shl和shr指令

          mov al,01001000b
          shl al,1;将al中的数据左移一位
          ;执行后al的值是10010000b,CF=0
          mov al,01010001b
          mov cl,3
          shl al,cl
          ;执行后al的值为10001000b,cf=0
mov al,00000001b 执行后al的值等于00000001b=1
shl al,1 执行后al的值等于00000010b=2
shl al,1 执行后al的值等于00000100b=4
shl al,1 执行后al的值等于00001000b=8
mov cl,3
shl al,cl 执行后al的值等于01000000b=64

14.4 CMOS RAM中存储的时间信息

    assume cs:code
    
    code segment
    start:
            ;向地址端口70h写入要访问的单元地址,读取CMOS RAM的信息
            mov al,8
            out 70h,al
            in al,71h;从数据端口中取得指定单元中的数据
            mov ah,al;al中为从CMOS RAM的8号端口读出数据
            mov cl,4
            shr ah,cl;ah中为月份的十位数码值
            and al,00001111b;ah中为月份的个位数值码
            add ah,30h;BCD码值+30h(字符'0')=十进制对应的ASCII码
            add al,30h
            ;用BCD码表示的月份以十进制的形式显示到屏幕上。
            mov bx,0b800h;显存
            mov es,bx
            mov byte ptr es:[160*12+40*2],ah;显示月份的十位数码
            mov byte ptr es:[160*12+40*2+2],al;显示月份的个位数码
    
            mov ax,4c00h
            int 21h
    
    code ends
    end start
144_1

十五、外中断

15.1接口芯片和端口

15.2外中断信息

15.3PC机及键盘的处理过程

键盘上部分键的扫描码

15.4编写int 9中断

    ;显示字符
code segment
    start:
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
        s:
            mov es:[160*12+40*2],ah
            inc ax
            cmp ah,'z'
            jna s
            mov ax,4c00h
            int 21h
    code ends
end start

</br>

    ;延迟显示字符

assume cs:code
    stack segment
            db 128 dup(0)
    stack ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
        s:
            mov es:[160*12+40*2],ah
            call delay
            inc ah
            cmp ah,'z'
            jna s
    
            mov ax,4c00h
            int 21h
    
        delay:
            push ax
            push dx
            mov dx,10h;循环100次,延迟的时间和CPU的计算能力成反比
            mov ax,0
        s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    code ends
end start

</br>

    ;实现IF=0,TF=0步骤
    pushf
    pop ax
    and ah,11111100b
    push ax
    popf
assume cs:code
    
    stack segment
            db 128 dup(0)
    stack ends
    
    data segment
            dw 0,0
    data ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
            mov ax,data
            mov ds,ax
            mov ax,0
            mov es,ax
    
            push es:[9*4]
            pop ds:[0]
            push es:[9*4+2]
            pop ds:[2];将原来的int9中断例程的入口地址保存
    
            mov word ptr es:[9*4+2],offset int9
            mov es:[9*4+2],cs;在中断向量表中设置新的int 9中断例程的入口地址
    
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
    
        s:
            mov es:[160*12+40*2],ah
            call delay
            inc ah
            cmp ah,'z'
            jna s
            mov ax,0
            mov es,ax
    
            push ds:[0]
            pop es:[9*4]
            push ds:[2]
            pop es:[9*4+2];将中断向量表中int9中断例程的入口恢复为原来的地址
    
            mov ax,4c00h
            int 21h
    
        delay:
            push ax
            push dx
            mov dx,10h;循环100次,延迟的时间和CPU的计算能力成反比
            mov ax,0
        s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    
            ;新的int 9中断例程
    
        int9:
            push ax
            push bx
            push es
    
            in al,60h
    
            pushf
            pushf
            pop bx
            and bh,11111100b
            push bx
            popf
            call dword ptr ds:[0];对int指令进行模拟,调用原来的int9中断例程
    
            cmp al,1;esc键盘扫描码
            jne int9ret
    
            mov ax,0b800h
            mov es,ax
            inc byte ptr es:[160*12+40*2+1];改变颜色
    
        int9ret:
            pop es
            pop bx
            pop ax
            iret
    
    code ends
end start
   

15.5安装新的int 9中断例程

assume cs:code
    
    stack segment
            db 128 dup(0)
    stack ends
    
    data segment
            dw 0,0
    data ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
            mov ax,data
            mov ds,ax
            mov ax,0
            mov es,ax
    
            push es:[9*4]
            pop ds:[0]
            push es:[9*4+2]
            pop ds:[2];将原来的int9中断例程的入口地址保存
    
            mov word ptr es:[9*4+2],offset int9
            mov es:[9*4+2],cs;在中断向量表中设置新的int 9中断例程的入口地址
    
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
    
        s:
            mov es:[160*12+40*2],ah
            call delay
            inc ah
            cmp ah,'z'
            jna s
            mov ax,0
            mov es,ax
    
            push ds:[0]
            pop es:[9*4]
            push ds:[2]
            pop es:[9*4+2];将中断向量表中int9中断例程的入口恢复为原来的地址
    
            mov ax,4c00h
            int 21h
    
        delay:
            push ax
            push dx
            mov dx,10000h;循环100次,延迟的时间和CPU的计算能力成反比
            mov ax,0
        s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    
            ;新的int 9中断例程
    
        int9:
            push ax
            push bx
            push es
    
            in al,60h
    
            pushf
            pushf
            pop bx
            and bh,11111100b
            push bx
            popf
            call dword ptr ds:[0];对int指令进行模拟,调用原来的int9中断例程
    
            cmp al,1;esc键盘扫描码
            jne int9ret
    
            mov ax,0b800h
            mov es,ax
            inc byte ptr es:[160*12+40*2+1];改变颜色
    
        int9ret:
            pop es
            pop bx
            pop ax
            iret
    
    code ends
end start
assume cs:code
    
    stack segment
            db 128 dup(0)
    stack ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
    
            push cs
            pop ds
    
            mov ax,0
            mov es,ax
    
            mov si,offset int9;设置ds:si指向源地址
            mov di,204h;设置es:di指向目的地址
            mov cx,offset int9end - offset int9;设置cx为传输长度
            cld;设置传输方向
            rep movsb
    
            push es:[9*4]
            pop es:[200h]
            push es:[9*4+2]
            pop es:[202h]
    
            cli
            mov word ptr es:[9*4],204h
            mov word ptr es:[9*4+2],0
            sti
    
            mov ax,4c00h
            int 21h
    
        int9:
            push ax
            push bx
            push cx
            push es
    
            in al,60h
    
            pushf
            call dword ptr cs:[200h];当此中断例程执行时(CS)=0
    
            cmp al,3bh;f1的扫描码
            jne int9ret
    
            mov ax,0b800h
            mov es,ax
            mov bx,1
            mov cx,2000
    
        s:
            inc byte ptr es:[bx]
            add bx,2
            loop s
    
        int9ret:
            pop es
            pop cx
            pop bx
            pop ax
            iret
    
        int9end:
            nop
    
    code ends
end start

第16章 直接定址表

16.1 描述单元长度的标号

assume cs:code
cod segment
    a:db 1,2,3,4,5,6,7,8
    b:dw 0

start:
        mov si,offset a
        mov bx,offset b
        mov cx,8
    s:
        mov al,cs:[si]
        mov ah,0
        add cs:[bx],ax
        inc si

    loop s
        mov ax,4c00h
        int 21h
code ends
end start
;代码中的 s、start等都是标号,表示了内存的地址

在code段中使用的标号a,b后面没有:,因此他们可以同时描述内存地址和单元长度的标号

assume cs:code
cod segment
    a db 1,2,3,4,5,6,7,8 ;描述了地址code:0,和从这个地址开始以后的内存单元都是直接单元
    b dw 0 ;则b是code[8]
    
start:
        mov si,0
        mov cx,8
    s:
        mov al,a[si] ;相当于mov al,cs:0[si]
        mov ah,0
        add b,ax
        inc si

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

检测点 16.1

检测点16.1

16.2 在其他段中使用数据标号

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

  start:
        mov ax,data
        mov ds,ax
        mov si,0
    s:
        mov al,a[si]
        mov ah,0
        add b,ax
        inc si

    loop s
        mov ax,4c00h
        int 21h
  code ends
  end start
data segment
    a db 1,2,3,4,5,6,7,8
    b dw 0
    c dw a,b
    ;相当于 c dw offset a,offset b
data ends

data segment
    a db 1,2,3,4,5,6,7,8
    b dw 0
    c dd a,b
    ;相当于 c dw offset a,seg a,offset b,seg b
    ;seg操作符,功能是取得某一标号的段地址
data ends

16.3 直接定址表

  assume cs:code
  code:segment
        mov al,0eh

        call showbyte

        mov ax,4c00h
        int 21
        ;子程序,用al传送要显示的数据

  showbyte:
        jmp short show

        table db '1023456789ABCDEF';字符表

  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

        mov bl,al
        mov bh,0
        mov al,table[bx];用低4位的值作为相对于table的偏移,取得对应的字符

        mov es:[160*12+40*2+2],al

        pop es
        pop bx
        ret

  code ends
  end start 

16.4 程序入口地址的直接定址表

  1. 清屏:将显存中当前屏幕中的支付设为空格;
  2. 设置前景色:设置显存中当前屏幕中处于奇地址的属性字节的第0、1、2位;
  3. 设置背景色:设置显存中当前屏幕中处于奇地址的属性字节的第4、5、6位;
  4. 向上滚动一行:依次将第n+行的内容复制到第n行处,最后一行为空。
;================================入口函数1=====================================
;入口函数说明;
;用ah传递功能号,0是清屏,1是设置前景色,2是设置背景色,3是向上滚动一行

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
        iret

;================================入口函数2=====================================
;入口函数说明;
;用ah传递功能号,0是清屏,1是设置前景色,2是设置背景色,3是向上滚动一行

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
        jmp short sret

;子功能==========================================================================

;清屏
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:
        mov 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
        shl al,cl
        mov bx,0b800h
        mov es,bx
        mov bx,1
        mov cx,2000

sub3s:
        mov byte ptr es:[bx],10001111b
        or es:[bx],al
        add bx,2
    loop sub3s

        pop es
        pop cx
        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+行
        mov di,0;es:di指向第n行
        cld
        mov cx,24;共复制24行

sub4s:
        push cx
        mov cx,160
        rep movsb;复制
        pop cx
    loop sub4s

        mov cx,80
        mov si,0

sub4s1:
        mov byte ptr es:[160*24+si],'';最后一行清空
        add si,2
    loop sub4s1

        pop ds
        pop es
        pop di
        pop si
        pop cx
        ret;结束

第十七章 使用BIOS进行键盘输入和磁盘读写

略。。。。。。

笔者看不下去了。。。。有兴趣的读者可以继续找相关的资料看。。。

END

上一篇下一篇

猜你喜欢

热点阅读