zoukankan      html  css  js  c++  java
  • 汇编语言学习

    目录

    汇编语言

    • 日期 19.07.19

    软件安装


    1.DOSBox

      1. 无脑下一步
      1. 修改配置文件
        添加以下命令
    mount c: d:asm
      c:
      //d盘下的文件是自行创建其中包含debug.exe就可以了
    

    2.Vim

      1. 安装完后打开其文件位置
      1. 修改配置文件
        在开头写入简单的配置文件
    set number
    color evening
    //保存退出
    

    编译和链接


    将源代码 生成最终 的 exe 文件 然后执行

    • 这一部分刚开始跟上做就好了,不用了解清楚

    DOSBox代码

    masm t1;
    link t1;
    // t1 为自己创建的asm文件
    //在创建txt文件把后缀改为asm
    //用vim编辑
    

    asm文件代码

    assume cs:code
    
      code segment
    
      
    
      		mov bx,0B800H
      		mov es,bx
      		
      		mov bx,160*10 + 40*2
    
      		mov word ptr es:[bx],5535H
    
      		mov ax,4C00H
      		int 21H
    
      
    
      code ends
      end
    

    进制


    10进制

    437->>>

    [4* 100+3* 10+7* 1 ]

    [4*10^2+3*10^1+7*10^0 ]

    0.1.2.3就是他在数字中的位置

    2进制

    111->>

    [1*2^2+1*2^1+1*2^0$$ >>转换成10进制 ]

    数字 1 0
    含义
    位置 2 1 0
    有代表 4 2 1

    进制快速转换

    拆分

    字节转换

    [1 byte = 8 bit ]

    [1 KB = 1024 byte$$ >> $$1KB = 2^{10} byte ]

    [1 MB = 1024 * 1KB$$ 1MB = 1024 * 1024 byte >> $$1MB = 2^{20} byte ]

    [1 GB = 1024 * 1MB$$ >> $$1GB = 2^{30} byte ]

    小结

    1. 汇编指令是 机器指令的 注记符,同机器指令一一对应
    2. 每一种CPU都有自己的汇编指令集
    3. CPU 可以直接使用的 信息 在存储器中 存放
      4.在存储器中指令和数据没用任何区别, 都是二进制数
      5.存储器单元从 0 开始 编号
      6.一个 存储单元 可以存储 8 个 bit 即 8 为二进制数
      7.1byte=8bit 1KB=1024byte 1MB=1024KB 1GB=1024MB
      总线

    地址总线 决定 CPU 的 寻址能力
    数据总线 决定 CPU 的 一次传输数据量
    控制总线 决定 CPU 的 对其他器件的控制能力

    寄存器


    1. 小例子
      1.1 B800:0400 回车
      1.2 1空格 1空格
      1.3 2空格 2空格
      1.4 ...

    2. 汇编程序员 就是 通过 汇编语言 中的 汇编指令 去修改 寄存器的值 从而 控制 CPU 控制整个计算机

    通用寄存器

    AX,BX,CX,DX

    1. 他们各自可分为两个 8 位寄存器(only)

      [ax=ah+al$$ $$(h==high,l==low) ]

    2. 1 byte = 8 bit(8位寄存器)字节型数据
      2 byte =16 bit(16位寄存器)
      字型数据 2个字节
      一个字型数据==2个字节型数据=高位字节+低位字
    3. 数据与寄存器之间 要 保持一致性,8位寄存器给8位数据,16为寄存器给16位数据
      不区分大小写

    (地址寄存器)指令寄存器 CS(段地址)和IP(偏移地址)

    jmp指令 jmp 2000:0 > cs2000,ip=0;

    mov ax,1000
    jmp ax
    ==> ip=1000;

    只能用jmp指令修改cs,ip

    1.CPU从cs:ip 所指的内存单元中读取内容,存取到 指令缓存器当中
    2.然后IP跳转到下一个指令位置,并且在执行指令缓存器当中的指令
    3.重复1。

    段地址寄存器 偏移地址寄存器
    ds(内存),es,ss(栈),cs sp(栈),bp,si,di,ip,bx

    指令的执行过程

    1. CPU从cs:ip所指向的内存单元 读取 指令 然后 存放到 指令缓存器当中
    2. IP = IP + 所读指令的长度,从而指向下一条指令
    3. 执行指令缓存器的内容,回到步骤1

    debug

    -r 查看和修改寄存器中的内容

    -r cs
    cs value
    enter

    -d 查看内存中的内容 段地址加偏移地址

    -d ss:00

    -v 将机器指令翻译成汇编指令
    -a 以汇编指令的格式 在内存中写入一条汇编指令 每次debug都的写
    -t 执行当前 cs:ip 所指的机器指令 代码段
    -e 可以改写 内存中的内容(数据)

    -p 快速执行完loop 指令
    -g 地址 ==== 一直执行到 地址 的 位置

    寄存器(内存访问)


    3个段


    数据段

    1. 字的存储

    一次存放两个字节

    2.

    内存地址由 段地址偏移地址 构成
    其中段地址默认保存在DS寄存器当中
    偏移地址由 [address] 保存告知

    3. mov,add,sub 指令

    mov指令

    4. -d 段地址:偏移地址

    5. 在内存中存放自己定义的数据,通过 ds和[] 来 让CPU访问数据

    代码段

    1. 段地址存放在cs寄存器中

    2. 偏移地址存放在ip寄存器当中

    3. 内存中存放代码

    4. 修改cs:ip中的值就可使CPU执行代码

    栈段

    1. 栈的作用

    1. 临时性保存数据
    2. 进行数据交换
    -a
    mov ax,1000
    mov bx,2000
    push ax
    push bx
    pop ax
    pop bx
    

    2. 栈的寄存器ss:sp

    3. 操作指令push&ip

    push 执行过程

    1.sp=sp-2(栈顶标记)
    2.传入字型数据

    pop 执行过程

    1.传出字或字节
    2.sp=sp+2(栈顶标记)

    栈顶标记 在 数据(内存地址)的上面 的 内存地址
    sp 偏移地址寄存器 ss 段地址寄存器

    4. 处理数据时要 ,临时存放数据

    5. 修改ss:sp中的值,决定栈顶位置,CPU在执行的过程中把我们定义的栈段当作栈使用

    6. 一段连续的内存地址

    7. 栈的容量的最大极限

    sp 的变化范围 0~ffffH 32768 个字型数据
    call 将指令IP 保存到内存的哪里? ret 可以拿回

    保存到栈中 为了让 ret 从栈中取回

    8.每执行 一条 -t 指令 就会将寄存器的值保存到 栈中

    内存的安全访问

    1. 安全空间 0:200~0: 2ffH
    2. 内存分配的时间 1. 系统加载程序的时候 为程序分配的内存。2. 程序执行过程中,向系统再去要内存空间

    承上启下


    • 我们可以把内存任意的划分为 栈,数据,指令 ,他们可以是同一块内存,亦可以是不同的内存
    • cpu 通过 ss:sp 所指向的 内存作为 栈
    • ds:[] 所指向的 内存 作为数据
    • cs:ip 所指向的 内存 作为指令

    指令从哪里?数据从哪来?临时性的数据存放到哪里?

    第一个程序


    编译和链接

    1. 编译 masm .mas --> .obj
    2. 链接 link .obj --> .exe

    exe 文件 的描述信息中 保存的程序入口 地址
    然后 系统 通过 描述文件 来设置 cs:ip 和 其它内存

    asm 文件 -- 汇编语言(1.汇编指令2.伪指令3.符号体系

    1. 汇编指令 由编译器 翻译成010101 的机器指令 最后由 CPU 执行
    2. 伪指令和符号体系 由编译器执行
    • 程序返回功能
      把系统加载程序的时候 给程序分配的内存 , 设置的寄存器 返还给系统,因为 系统资源 是有限的
     mov ax,4c00
     int 21H
    

    程序的跟踪 debug + 程序名

    • p 执行 int 指令

    • q 退出

    • cx == 程序长度

    • PSP区 从 ds:0 开始的 256 个字节

    快速编译

    • 字母型数字前面 必须加 0;

    默认代码(目前)

    assum cs:code
    
    code segment
         
         ;填写内容
         
         mov ax,4c00H
         int 21H
    
    code ends
    
    end
    

    偏移地址寄存器 bx

    自己分配内存

    自己分配内存

    • 一个 segement 最少占据 16 个字节
    • 假设 数据段 有 N个字节 则 实际占用 $$(N/16 + 1)*16$$ 个

    • 都是 16 的倍数
    //实验5
    assume cs:codesg
    
    a segment 
    		db 1,2,3,4,5,6,7,8
    a ends
    
    b segment 
    		db 1,2,3,4,5,6,7,8
    b ends
    
    c segment 
    		db 0,0,0,0,0,0,0,0
    c ends
    
    codesg segment
    	
    start:  
    		
    		
    		mov ax,c
    		mov es,ax
    
    		
    		sub cx,cx
    		sub bx,bx
    		add cx,8
    
    addnum: mov ax,a
    		mov ds,ax
    		
    		sub dx,dx
    	
    		mov dl,ds:[bx] ;拿出第一个数据
    		
    		mov ax,b
    		mov ds,ax
    		
    		add dl,ds:[bx]  ;拿出第二个数据,并且相加
    		
    		mov es:[bx],dl
    		
    		inc bx
    	
    		loop addnum
    
    
    	   
    	mov ax,4c00h
    	int 21h
    
    codesg ends
    
    end start
    

    总结

    • db 字节型
    • dw 字型

    定位内存地址的方法

    and 和 or

    1. and 逻辑与 0 置为0
      1. 全为 1 才出 1 否则全部为 0
      2. 可用于 对 二进制位的数字 设 0
    2. or 逻辑或 1 置为1
      1. 只要有1 就为 1
      2. 可用于对 二进制数字设 1

    以字符的形式给出数据

    • like ‘………’ 其中单引号包含的 内容 编译器将把 其中的内容 转化为相应的 ASCII

    大小写转换

    and 置为大写 1101 1111b

    or 置为小写 0100 0000b

    [bx+idata]

    • idata 是立即数

    常用格式

    1. mov ax,[200+bx]
    2. mov ax,200[bx]
    3. mov ax,[bx].200
    • 可以处理数组

    SI 和 DI

    • 类似于BX 但是 不能 分成两个 8 为寄存器
    • 全为偏移地址寄存器 bx为基址寄存器

    [BX+SI] 和 [BX+DI]

    常用格式

    mov ax,[bx] [si]

    [BX+SI+idata] 和 [bx+di+idata]

    数据处理的两个基本问题

    sreg 段地址寄存器

    reg 寄存器

    bx,si,di和bp

    • bx si/di组合
    • bp si/di组合

    指令要处理的数据

    1. 保存在CPU
    2. 在内存中
    3. 在端口中

    数据位置的表达

    1. 立即数(idata)
    2. 寄存器
    3. 段地址加偏移地址

    数据的长度

    • byte 和 word

    在处理数据的时候要 告知 CPU 要处理的数据有多大可以通过一些方法来告知

    1. 通过寄存器来指明 如 ax,代表对word操作而 al,代表对byte 操作

    2. 无寄存器 则用 X ptr 来表示 X 为byte 或者 word 如 : mov word ptr ds:[0],1

    3. 用 push or pop 就不用 声明 因为 栈就是 对字进行操作

    div 指令

    • 除数 有8位和16位 在一个reg或内存单元中
    • 被除数 默认 放在 ax(16位) 或者 dx(高16位) 和 ax(低16位)
    • 结果 al(商) ah(余数) 或者 ax(商) dx(余数)

    伪指令 dd (占两个字)

    • 相当于 两个 dw
    • 四个 db

    dup

    用来重复数据

    • db 3 dup (0) ==> db 0 ,0, 0

    转移指令

    操作符 offset

    • 取得标号的偏移地址

    jmp 指令


    • 无条件转移指令
    • 可同时修改 cs 和 ip 或者 ip

    jmp short 标号


    • 在编译是就已经处理好 要偏移的地址
    • 无论 本 命令在哪 只有 偏移地址

    jmp near ptr 标号


    • 段内短转移

    jmp far ptr 标号


    • 同时修改 cs 和 ip

    在内存中转移


    jmp word ptr 标号

    • jmp word ptr ds:[0]
    • 只修改 IP
    • 段内转移

    jmp dword ptr 标号

    • 段间转移
    • ip[X+0],cs[X+2]

    jcxz (短转移)


    • jmp cx zero
    • 只有在cx 为0 的情况下 才 执行 转移

    loop(短转移)


    • cx 不为0 执行loop

    ret 和 call


    • 指令执行过程

    通过栈中的数据来修改 cs 和 ip 同时还会 修改栈顶标志

    ret(用栈中的数据)


    • 弹栈
    1. 近转移 ret 修改 IP pop ip
      1. ((ip)=((ss)*16+(sp)))
      2. ((sp)=(sp)+2)
    2. 远转移 retf 修改 cs:ip pop ip,pop cs
      1. ((ip)=((ss)*16+(sp)))
      2. ((sp)=(sp)+2)
      3. ((cs)=((ss)*16+(sp)))
      4. ((sp)=(sp)+2)

    call(不能实现短转移)


    • 类似jmp
    • call程序处理的数据一般要进行压栈

    1.根据位移进行转移


    push ip
    jmp near ptr 标号
    
    • 执行过程 原理
      • call下一条指令的IP压栈后,转到标号处

    2.转移目的地址在指令中


    call far ptr
    
    • 执行过程 原理
      • call下一条指令的CS:IP压栈后,转到标号处

    3.转移地址在寄存器中


    ​```assembly
    call 16 位 reg

    
    - 执行过程 原理
      - call下一条指令的IP压栈后,转到==reg== 处
    
    ### 4. 转移地址在内存中
    
    ----
    
    #### 1
    
    -----
    
    ```assembly
    call word ptr 内存单元地址
    
    • 执行过程 原理
      • call下一条指令的IP压栈后,转到内存单元地址

    2


    call dword ptr 内存单元地址
    
    • 执行过程 原理
      • call下一条指令的CS:IP压栈后,转到标号处

    call 和 ret 共同应用


    • 就像函数调用

    批量数据处理


    assume cs:code,ds:data,ss:stack
    
    data segment	
    	db 'conversation'
    data ends
    
    stack segment
    	db 16 dup(0)
    stack ends
    
    code segment
    
    
    	start:  mov ax,data
    			mov ds,ax
    			mov si,0
    			mov cx,12
    			call capital
    			mov ax,4c00h
    			int 21h
    			
    	capital: and byte ptr ds:[si],11011111b
    			 inc si;
    			 loop capital
    			 ret
    
    
    code ends
    
    
    
    end start
    

    寄存器冲突问题


    • 在子程序执行开头,把所需要用到的寄存器压栈
    • 在子程序完成后,从栈中弹出各个寄存其的值
    assume cs:code,ds:data,ss:stack
    
    data segment	
    	db 'word',0
    	db 'unix',0
    	db 'wind',0
    	db 'good',0
    data ends
    
    stack segment
    	db 128 dup(0)
    stack ends
    
    code segment
    
    
    	start:  mov ax,data
    			mov ds,ax
    			
    			mov cx,4
    			mov bx,0
    			
    	s:      mov di,bx
    			call capital
    			add bx,5
    			loop s
    			
    			mov ax,4c00h
    			int 21h
    	
    	capital: push cx;执行子程序前压栈
    			 push si
    			 
    	change:	 mov cl,ds:[si]
    			 mov ch,0
    			 jcxz ok
    			 and byte ptr ds:[si],11011111b
    			 inc si
    			 jmp change
    			 
    		ok:	 pop si;执行完后弹栈
    			 pop cx
    			 ret
    
    
    code ends
    
    
    
    end start
    

    mul


    1. 8位


    一个默认放在AL,另一个放在内存字节单元或者8位reg

    结果 默认 AX

    2. 16位

    一个默认放在AX,另一个放在内存字单元或者16位reg

    结果 默认 高位在DX ,低位在AX

    模块化程序设计


    • 通过ret,call.

    参数和结果的传递

    assume cs:code,ds:data,ss:stack
    
    data segment	
    	dw 1,2,3,4,5,6,7,8
    	dd 0,0,0,0,0,0,0,0
    	db 'word',0
    	db 'unix',0
    	db 'wind',0
    	db 'good',0
    data ends
    
    stack segment
    	db 128 dup(0)
    stack ends
    
    code segment
    
    
    	start:  mov ax,data
    			mov ds,ax
    			mov si,0
    			mov bp,0
    			call r_start
    			
    			
    			mov ax,4c00h
    			int 21h
    
    			
    	r_start:	mov bx,ds:[si]
    				call cube
    				mov ds:[16+bp],ax
    				add si,2
    				add bp,4
    				loop r_start
    				ret
    			
    		cube:	mov ax,bx
    				mul bx
    				mul bx
    				ret
    
    

    标志寄存器


    1573010421984

    • CF标志位Carry Flag

      • 1573010380365
      • 进位(最高位进位)add
      • 和运算相关的指令会影响标志位 like add , sub
      • 把操作数当作无符号数字
    • ZF标志位Zero Flag

      • 1573010356660
    • 判断相等

    • 最后结果是否为零

    • PF标志位pairty Flag

      • 一的个数是否位偶数0 or 奇数1
      • 1573011155062
    • SF标志位Sign Flag

      • 正0负1
      • 1573011683986
      • 计算的结果看陈整数和负数
      • add sub 影响sf
      • mul 不影响sf
    • OF标志位Overflow

      • 1573087099543
      • 运算过程中看是否溢出
      • 两个操作数都当做有符号 运算过程中决定是否溢出
    • adc 带进位的加法寄存器

      • 可以对更大的数字进行加法运算
    • sbb 带借位减法

      • 实现对更大数的减法
    • cmp 比较指令

      • 类似于减法指令 只是不保存结果,只是影响相关的标志位寄存器

      • 可以判断两个操作数的大小 通过 sf of 标志位

        sf of 大小
        1 0 1<2
        0 1 1<2
        1 1 1>2
        0 0 1>2
    • 检测比较结果的转移指令

      • 和 cmp指令配合使用
        • 指令 含义 检测相关的标志位
          je equal zf=1
          jne not equal zf=0
          jb below cf=0
          jnb not below cf=1
          ja above cf=0 && zf=1
          jna not above cf=1 || zf=1
    • DF 标志和串传送指令

      • movsb

      • movsw

      • 配合rep 使用 rep like loop 由cx 的大小决定 执行 上述 两条指令的 次数

        • exp

          • ;-========movsb=====
            mov cx,16
            rep movsb
            ;循环16次 每次执行完后 si di ++
            
          • ;========movsw
            mov cx,16
            rep movsw
            ;循环16次每次 执行完 si,di --
            
      • cld -> df==0++

      • std -> df==1--

    • pushf && popf

      • 使 标志位寄存器 压栈和出栈

    内中断

    1.产生

    1. 除法溢出
    2. 单步执行
    3. 执行into指令
    4. 执行Int 指令

    2. 中断处理程序

    3. 中断向量表

    • 中断程序的入口地址
    • 存放256个中断程序入口地址
    • 存放在 0000:00000000:03FF
    • 一个表项占两个字,高->段地址CS 低->偏移地址IP

    4.中断的过程

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

    7. 开始执行中断程序

    5.中断处理程序和iret指令

    • 中断程序写法的常规步骤
      • 保存要用的寄存器
      • 处理中断
      • 回复寄存器
      • 用iret指令返回 --> pop ip,pop cs,pop popf(pop psw)

    6. 除法错误中断的处理

    1. 出现溢出
    2. 产生0号中断信息
    3. 执行0号中断
    4. 返回操作系统
    

    7. 编程处理0号中断

    分析:

    1. 当发生除法溢出时,产生0号中断信息,从而引发中断过程
      1. 取得中断类型码N
      2. pushf
      3. TF=0,IF=0
      4. push CS
      5. push IP
      6. [(IP)=(N*4),CS=(N*4)+2 ]

    2. 发生0号中断时,Cpu转去执行中断处理程序
      1. 相关处理
      2. 向显示缓冲区送字符串
      3. 返回DOS
      4. do0
    3. do0的程序应该放在那里
      1. 放在0号中断的向量表中0000:0200-0000:02FF
    4. 中断程序的入口地址放在那里
      1. cs:0000:0002,ip:0000:0000

    总结

    1. 编写中断处理程序do0
    2. 将do0送入0000:0200
    3. 将do0的入口地址送到存储在中断向量表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. 端口读写指令


    • in
    • out

    访问端口

    in al , 60h;把60h中的数据读入al中
    ;1.cpu 通过地址线 将地址信息 60h 发出
    ;2.cpu 通过控制总线发出读命令 选择端口所在的芯片 并通知他 要从中读取数据
    ;3.端口所在的芯片 将60h端口中的数据通过数据线送入cpu
    

    2.CMOS RAM


    • 地址端口70h
    • 数据端口 71h

    3.shl,shr


    移位指令

    左移--> *2

    右移–> 2

  • 相关阅读:
    Ant
    责任链模式
    日志logback
    知识点
    三个实例演示 Java Thread Dump 日志分析
    IDEA运行编译后配置文件无法找到,或配置文件修改后无效的问题
    IDEA创建MAVEN WEB工程
    多线程源码分析ThreadPoolExecutor
    解决
    微博关系服务与Redis的故事
  • 原文地址:https://www.cnblogs.com/bgst007/p/12240237.html
Copyright © 2011-2022 走看看