• 小码哥汇编


    小码哥汇编周末班


    总线

    地址总线

    宽度决定CPU寻址能力

    8086地址总线宽度20,寻址能力是1M

    数据总线

    宽度决定了CPU单次数据传送量,也是数据传送速度

    8086数据总线宽度是16,所以单词最大传递是2个字节数据

    控制总线

    它的宽度决定了CPU对其他器件控制能力,能有多少种控制

    image-20220520092402556

    8088数据总线是8,8086是16

    image-20220520093513479

    一些题

    image-20220520093531624

    内存

    所有内存单元都有唯一id地址,物理地址

    8086地址总线宽度20,可以定位220个不同的内存单元(内存地址范围0x00000~ 0xFFFFF),内存空间大小是1MB

    0x00000~ 0xFFFFF 主存储器

    0xA0000~ 0xBFFFF 向显存写数据

    0xC0000~ 0xFFFFF存储各种硬件信息

    CPU访问内存单元是,要给出内存单元的地址

    8086 20位地址总线,可以传送20位地址 1M寻址能力

    16位结构CPU,它内部都能够一次性处理传输存储16位,蒋迪志从内部简单发出,只能送出16位地址,表现出来地址能力只有64KB

    image-20220520094716317

    image-20220520142304922

    image-20220520235521480

    cpu可以用不同段地址和偏移地址形成同一个物理地址

    内存分段管理

    8086是用“起始地址(段地址×16) + 偏移地址 = 物理地址”的方式给出物理地址
    为了开发方便,我们可以采取分段的方法来管理内存,比如:
    地址10000H~100FFH的内存单元组成一个段,该段的起始地址为10000H,段地址为1000H,大小为100H
    地址10000H~1007FH、10080H~100FFH的内存单元组成2个段,它们的起始地址为:10000H和10080H,段地址为1000H和1008H,大小都为80H
    
    

    image-20220520235812161

    偏移地址为16位,16位地址的寻址能力为64KB,所以一个段的长度最大为64KB

    cpu

    寄存器:信息存储

    运算器:信息处理

    控制器:控制其他器件工作

    对程序员来说,CPU中最主要部件是寄存器,可以通过改变寄存器的内容来实现对CPU的控制
    不同的CPU,寄存器的个数、结构是不相同的(8086是16位结构的CPU)
    8086有14个寄存器
    都是16位的寄存器
    可以存放2个字节
    
    

    image-20220521000947920

    AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,称为通用寄存器(有时也有特定用途)
    通常,CPU会先将内存中的数据存储到通用寄存器中,然后再对通用寄存器中的数据进行运算
    假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间
    CPU首先会将红色内存空间的值放到AX寄存器中:mov ax,红色内存空间
    然后让AX寄存器与1相加:add ax,1
    最后将值赋值给内存空间:mov 蓝色内存空间,ax
    
    

    通用寄存器

    AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,称为通用寄存器(有时也有特定用途)
    通常,CPU会先将内存中的数据存储到通用寄存器中,然后再对通用寄存器中的数据进行运算
    假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间
    CPU首先会将红色内存空间的值放到AX寄存器中:mov ax,红色内存空间
    然后让AX寄存器与1相加:add ax,1
    最后将值赋值给内存空间:mov 蓝色内存空间,ax
    
    

    image-20220521001658347

    image-20220521001702116

    字节与字

    在汇编的数据存储中,有2个比较常用的单位
    字节:byte,1个字节由8bit组成,可以存储在8位寄存器中
    字:word,1个字由2个字节组成,这2个字节分别称为字的高字节和低字节
    
    比如数据20000(4E20H,0100111000100000B),高字节的值是78,低字节的值是32
    
    
    
    1个字可以存在1个16位寄存器中,这个字的高字节、低字节分别存储在这个寄存器的高8位寄存器、低8位寄存器中
    
    

    段寄存器

    8086在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址
    是什么部件提供段地址?段地址在8086的段寄存器中存放
    8086有4个段寄存器:CS、DS、SS、ES,当CPU需要访问内存时由这4个段寄存器提供内存单元的段地址
    CS (Code Segment):代码段寄存器
    DS (Data Segment):数据段寄存器
    SS (Stack Segment):堆栈段寄存器
    ES (Extra Segment):附加段寄存器
    每个段寄存器的具体作用是?
    
    
    CS为代码段寄存器,IP为指令指针寄存器,它们指示了CPU当前要读取指令的地址
    
    任意时刻,8086CPU都会将CS:IP指向的指令作为下一条需要取出执行的指令
    
    

    指令执行的过程

    1.从cs:ip指向的内存单元读取指令,读取的指令进入指令缓冲器

    2.IP=IP+读取指令的长度,从而指向下一条指令

    3.执行指令,转到步骤1,重复这个过程

    指令和数据

    在内存或者磁盘上,指令数据没有任何区别,都是二进制

    cpu在工作时候把有信息看做指令,有的信息看做数据,为同样信息赋予不同意义

    image-20220521003431491

    jmp段地址:偏移地址,指令功能,用指令中给出的段地址修改cs,偏移地址修改IP

    若想仅修改IP内容,可以用

    jmp ax, 指令执行前,ax=1000H,CS=2000H,IP=0003H

    指令执行后,ax=1000H,CS=2000H,IP=1000H

    jmp bx,指令执行前,bx=0B16H,CS =2000H,IP=0003H

    指令执行前,bx=0B16H,CS =2000H,IP=0B16H

    另外,也可以“jmp 直接值”来改变IP的值,比如“jmp 0100H”

    练习

    image-20220521132210413

    debug

    image-20220521132536158

    image-20220521132602220

    debug用R命令查看,改变cpu寄存器内容

    R命令查看,改变CPU寄存器内容
    D命令查看内存中内容
    E命令改写内存中内容
    U命令将内存中机器指令翻译成汇编指令
    T命令执行一条机器指令
    A命令以汇编指令个是在内存中写入一条机器指令
    q命令,退出debug
    p命令 类似于step over,可以跳过loop循环
    g命令,跳过前面的代码,停留在指定的代码位置
    
    

    R

    输入“r”可以查看所有寄存器的值
    输入“r 寄存器名称”可以修改寄存器的值
    输入“r ax”将ax寄存器的值改为0100H
    
    

    image-20220521133834262

    D

    输入“d”可以查看内存中的内容
    输入“d 段地址:偏移地址”查看特定位置的内存数据
    输入“d 段地址:起始偏移地址 结尾偏移地址”查看特定位置和特定范围的内存数据
    输入“d 偏移地址”、 “d 起始偏移地址 结尾偏移地址” ,会将DS的内容作为段地址
    
    

    image-20220521133900584

    E

    输入 e段地址,偏移地址:数据串,修改特定位置内存数据image-20220521134355393

    输入e段地址:偏移地址,后按Enter 也可以修改特定位置内幕才能数据,数据用空格隔开

    image-20220521134359191

    U

    输入u可以将内存中内容翻译为对应的汇编指令

    image-20220521134445104

    A

    输入 ‘a’

    a段地址:偏移地址,可以从某位置开始写入汇编指令

    练习

    image-20220521140141702

    image-20220521140250986

    8086 的显存地址空间是 A0000H~BFFFFH,其中 B8000H~BFFFFH 为 80*25
    彩色字符模式显示缓冲区,当向这个地址空间写入数据时,这些数据会立即出现
    在显示器上
    
    
    CPU要读写一个内存单元时,必须要先给出这个内存单元的地址,在8086中,内存地址由段地址和偏移地址组成
    8086中有一个DS段寄存器,通常用来存放要访问数据的段地址
    mov bx,1000H
    mov ds,bx
    mov al,[0]
    上面3条指令的作用将10000H(1000:0)中的内存数据赋值到al寄存器中
    mov al,[address]的意思将DS:address中的内存数据赋值到al寄存器中
    由于al是8位寄存器,所以是将一个字节的数据赋值给al寄存器
    
    8086不支持将数据直接送入段寄存器中,mov ds,1000H是错误的
    
    
    

    image-20220522133413833

    大小端

    大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高低\低高) (Big Endian)
    小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高高\低低) (Little Endian)
    
    

    image-20220522133449519

    image-20220522135920157

    image-20220522135925491

    练习

    image-20220522140016482

    栈:是一种具有特殊的访问方式存储空间,后进先出,

    8086会将CS作为代码段的段地址,将CS:IP指向的指令作为下一条需要取出执行的指令
    8086会将DS作为数据段的段地址,mov ax,[address]就是取出DS:address的内存数据放到ax寄存器中
    8086会将SS作为栈段的段地址,任意时刻,SS:SP指向栈顶元素
    8086提供了PUSH(入栈)和POP (出栈)指令来操作栈段的数据
    比如push ax是将ax的数据入栈,pop ax是将栈顶的数据送入ax
    
    

    push ax

    image-20220522140227083

    image-20220522140235688

    pop ax

    image-20220522140438408

    image-20220522140442028

    问题

    image-20220522140515381

    栈顶超界push

    image-20220522140528142

    栈顶超界 pop

    image-20220522140548359

    image-20220522140609953image-20220522140617373

    在8086中,push和pop都是两个字节

    image-20220522140721346

    练习

    image-20220522140737837

    image-20220522140757944

    image-20220522140802492

    hello,world

    编译步骤

    编写源码

    编译链接

    调试运行

    image-20220522140909392

    汇编指令组成

    mov sub add这些指令可以编译为机器指令,被cpu执行

    伪指令

    assume segment ends 。。。

    没有对应的机器指令,由编译器执行

    image-20220522141035936

    image-20220522141043895

    伪指令

    segment ends 作用是定义一个段,segment 代表是一个段的开始,ends代表结束

    声明以下 code是代码段,ds是数据段

    image-20220522141156991

    退出程序

    mov ah,4c00h
    
    int 21h
    

    中断

    中断
    中断是由于软件的或硬件的信号,使得CPU暂停当前的任务,转而去执行另一段子程序
    也就是说,在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止当前程序的执行转而处理这个新情况的过程就叫做中断
    
    中断的分类
    硬中断(外中断),由外部设备(比如网卡、硬盘)随机引发的,比如当网卡收到数据包的时候,就会发出一个中断
    软中断(内中断),由执行中断指令产生的,可以通过程序控制触发
    从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理
    
    
    可以通过指令int n产生中断
    n是中断码,内存中有一张中断向量表,用来存放中断码对应中断处理程序的入口地址
    CPU在接收到中断信号后,暂停当前正在执行的程序,跳转到中断码对应的中断向量表地址处,去执行中断处理程序
    
    常见中断
    int 10h用于执行BIOS中断
    int 3是“断点中断”,用于调试程序
    int 21h用于执行DOS系统功能调用,AH寄存器存储功能号
    
    
    

    DOS系统功能调用

    DOS系统功能调用
    由DOS提供的一组实现特殊功能的子程序供程序员在编写自己的程序时调用,以减轻编程的工作量
    涉及屏幕显示、文件管理、I/O管理等等
    每个子程序都有一个功能号,所有的功能调用的格式都是一致的。调用的步骤大致如下:
    系统功能号送到寄存器AH中;
    入口参数送到指定的寄存器中;
    用INT 21H指令执行功能调用;
    根据出口参数分析功能调用执行情况。
    
    
    

    image-20220522142429923

    image-20220522142437390

    loop指令

    loop指令和cx配合使用,用于循环执行重复的操作,类似于高级语言中的for、while循环
    使用格式
    
    mov cx ,循环次数
    标号:
    	循环执行的程序段
    	loop 标号
    	
    loop指令的执行流程
    让cx的值减一,即cx = cx – 1
    判断cx的值
    如果不为零转至标号处执行程序,然后重复①
    如果为零则执行loop后面的代码
    
    
    
    

    练习

    image-20220524090211917

    mov ax,[bx] 中 bx 是偏移地址,段地址默认在ds

    我们也可以明确地标明段地址,比如
    mov ax, ds:[bx]
    mov ax, cs:[bx]
    mov ax, ss:[bx]
    mov ax, es:[bx]

    上面的“ds:”、“cs:”、“ss:”、“es:”称为段前缀

    段前缀的使用

    image-20220524090356064

    image-20220524090439225

    练习

    image-20220524090450804

    dw(define word)
    使用dw定义了3个字型数据,数据之间用逗号隔开
    类似的还有db(define byte)、dd(define double word)
    
    start和end start是对应的,end start标记程序的执行入口
    
    
    image-20220524090705542

    在代码段中使用栈

    假设代码中有数据1122h、3344h、5566h、7788h、99aah、0aabbh,利用栈将它们逆序存放

    image-20220524090750423

    多个段程序

    • 如果将代码、数据、栈都放到一个段里面会显得混乱,编程时要随时注意何处是数据、何处是栈、何处是代码
      一个段的大小<=64KB,这样就会让数据、代码、栈的大小受到极大的限制

      所以,一般会考虑使用多个段来存放数据、代码、栈

    image-20220524091115836

    栈如果不平衡,栈空间迟早会被用完

    外平栈

    函数外部调用

     	push 1122h
        push 3344h
        call sum3 
        add sp,4  ;恢复栈平衡
    

    内平栈

    函数内部

    mov bp,sp
    mov ax,ss:[bp+2]
    sub ax,ss:[bp+4]
    
    ret 4     
    

    一般函数是外平栈

    函数调用约定

    __cdecl 外平栈,参数从右从右到左入栈

    __stdcall 内平栈,参数从右到左入栈

    __fastcall 内平栈,eax,edx分别传递前面2个参数,其他参数从右到左入栈

    函数内部局部变量

    image-20220523234317380

    1. 函数调用流程
    2. push 参数
    3. push 函数返回地址
    4. push bp 保留bp之前的值,方便以后恢复
    5. mov bp,sp 保留sp之前的值,方便以后恢复
    6. sub sp,空间大小,分配空间给局部变量
    7. 保护可能用到的寄存器
    8. 使用CC填充局部变量的空间
    9. 执行业务逻辑

    执行业务逻辑

    1. 恢复寄存器
    2. mov sp,bp (恢复sp之前的值)
    3. pop bp 恢复bp之前的值
    4. ret 将函数返回的地址出栈,回到返回地址,执行下一条指令
    5. 恢复栈平衡 add sp,参数所占空间 add sp,4

    image-20220524084215107

    assume cs:code ,ds:data,ss:stack
    
    
    
    stack segment
    
    stack ends     
    
    
    data segment
        
        string db 'hello$'
    data ends
    
    
    code segment                                 
    start:
    
        mov ax,data
        mov ds,ax
        
        mov ax,stack
        mov ss,ax
        
        ;业务逻辑
        
        push 1
        push 2
        call sum
        add sp,4 
        
        ;退出
        mov ax,4c00h
        int 21h  
     
             
    
    sum:       
    
    
        push bp    ; 保护bp
    ;访问栈参数  
        ;保存bp以前的值
        mov bp,sp
        sub sp,10    ; 预留十个字节给局部变量
        
        
        
        ;保护可能会用到的寄存器
        
        push si
        push di
        push bx
                        
         ;业务逻辑               
        ;定义两个局部变量
        
        mov word ptr ss:[bp-2],3
        mov word ptr ss:[bp-4],4 
        mov ax,ss:[bp-2]
        add ax,ss:[bp-4]
        mov ss:[bp-6],ax
                       
                       
                       
        mov si,1
        
        ;访问栈中的参数
        mov ax,ss[bp+2]
        add ax,ss:[bp+4]  
        add ax,ss:[bp-6]         
                               
         ;业务逻辑                       
        
        ; 恢复寄存器值
        pop bx
        pop di
        pop si
                               
        mov sp,bp ; 恢复sp
        pop bp
        
        ret       
        
    code ends  
    end start
    

    给数据起标号

    image-20220524091132263

    image-20220524091142754

    寻址方式

    image-20220524091150332image-20220524091206278

    指令处理的数据长度

    8086指令能处理2种尺寸的数据:byte、word
    
    思考:“mov [0], 20H”指令是否正确?
    mov byte ptr [0], 20H	将20H放入0位置内存的字节单元,占用1个字节
    mov word ptr [0], 20H	将20H放入0位置内存的字单元,占用2个字节
    
    很多指令都可以通过“byte ptr”或者“word ptr”来指明所需要操作内存的数据长度
    inc byte ptr [0]
    add word ptr [0], 2
    
    有些指令有默认的操作数据长度,比如push [0]、pop [0]的操作数据长度只能是2个字节
    
    

    call ret 指令

    call 标号:
    将下一条指令的偏移地址入栈后
    转到标号处执行指令

    ret:将栈顶的值出栈,赋值给ip

    call和ret联合使用的作用其实就是高级语言中的函数调用

    实践,考虑以下几种情况
    有无参数
    有无返回值
    现场保护
    局部变量
    堆栈平衡

    堆栈平衡

    函数调用前后栈顶指针一致

    栈帧

    栈帧(Stack Frame Layout)
    就是一个函数执行的环境
    包括:参数、局部变量、返回地址等

    image-20220524091426379

    image-20220524091444327

  • 相关阅读:
    洛谷 P1578 奶牛浴场 题解
    LOJ167 康托展开 题解
    三校联训 【NOIP模拟】寻找
    洛谷 P1809 过河问题 题解
    有关多边形面积的总结
    LOJ 103 字串查找 题解
    洛谷 P2384 最短路 题解
    POJ 2492 A Bug's Life 题解
    LOJ 10214 计算器 题解
    洛谷 P2868 [USACO07DEC]观光奶牛Sightseeing Cows 题解
  • 原文地址:https://www.cnblogs.com/abldh12/p/16304344.html
Copyright © 2020-2023  润新知