精华内容
下载资源
问答
  • 汇编程序
    千次阅读 多人点赞
    2020-12-30 16:41:06

    每一个汇编老师都会告诉你很多强的亚批的指令,但是不怎么说一个真正的、能跑起来的汇编程序长什么样。
    在我学习的过程中,虽然有基本格式的讲解,但是说实话听着就很难受。
    这也导致在上汇编实验的过程中,是属是难受。

    从一个小程序说起

    不是微信小程序啊,是比较短的汇编程序。
    例:result=a+b

    data segment
      a	db  1
      b	db  2
      result  db  ?
      string  db  'result=$’
    data ends	
    code segment
      assume cs:code, ds:data
    start:  mov   ax,data
            mov   ds,ax
    		mov   al,a
            add   al,b
            mov   result,al
            lea   dx,string
            mov   ah,09
            int   21h
            add   result,30h
            mov   dl,result
            mov   ah,2
            int   21h
        	mov   ah,4ch
        	int   21h
    code    ends
            end   start
    

    是不是看着就很头疼,我们一点点分析。

    指令

    先说一下你到底在写什么。
    一般来是汇编源程序是包括三种基本语句的,指令语句、伪指令语句和宏指令语句
    我们会写的是伪指令,宏指令也有以后再说。
    指令语句则是CPU能看得懂的,也是他要运行的。伪指令最终会形成指令语句,经过汇编才能形成机器目标代码。

    segment——段

    本来是有很多的段的,其实常用的也就三个

    首先是存放数据的data段,这部分数据在内存中,定义一些我们可能会用到的量。

    应该还有一个stack段,是我们用来定义堆栈空间大小的。
    一般只有一句:dw 32 dup(?)
    定义了一个大小为32个dw的没有初始值的空间
    (dup表示循环,可以嵌套,如32 dup(1,2,10 dup(5))

    最后,也是最重要的段就是代码段(code)
    除了代码段之外,剩下的部分都不许有伪指令。

    这里要提一点,我们的所有命名都不是固定的,segment名称都可以变化,只要在后面对应上就行。

    另外我们的segment段可以简化定义成:
    在这里插入图片描述
    这样我们的代码就变成了:

    .MODEL SMALL
    .DATA
       	 ;此处输入数据段代码  
    
    .STACK
       	 ;此处输入堆栈段代码
    .CODE
    START:
        MOV AX, @DATA
        MOV DS, AX
    	;此处输入代码段代码
        MOV AH, 4CH
        INT 21H
        END START
    

    可以看到,没了assume,绑定的方式也发生了改变(这两个在后面有提)
    但使用简化段定义时,必须有存储模式.MODEL语句,且位于所有简化段定义语句之前。
    这个东西说实话感觉一般是用不上,我们一般来说small就够了,下面给出small:
    在这里插入图片描述

    start

    start:
    end start

    组成了程序开始和结尾的部分。
    子程序中,我们的start是应该放在主模块的开始处,而不是整个代码段的开始。
    剩下的情况,无特殊情况就放在code段开始,end start是在code段结束之后。

    这里的start和上面的segment一样,都只是一个标志,可以换成其他的,但是这个比较常用而已。
    另外如果是第一条指令就是程序开始,那么我们就可以省略两个start,变成:

    code segment
    ……
    code ends
    	end
    

    assume伪指令

    assume 段寄存器:段名
    相当于将segment段和我们的数据段进行匹配。

    • data段就匹配ds段
    • stack段就匹配ss段
    • code段就匹配cs段

    其实不止这三个,但是基本上常用到的也就这些了。

    讲一下程序的一些组成部分

    绑定

    mov   ax, data
    mov   ds, ax    ; 数据段段地址 ——> 段寄存器
    

    这个当时迷惑了我很久,其实就是一个绑定段和段寄存器,就是一个套话,但是还不能没有。

    变量

    我们写程序一定少不了变量的存在,毕竟你不能啥都往寄存器里面装吧。
    我们需要将一些东西放在内存中时,就需要变量的存在了。
    变量书写在data段中的,一般格式: 名称+类型(db、dw、dd,分别为字节、单字和双字)+初始值,如果没有初始值就写一个“?"上去。
    (汇编默认为十进制数据,如果需要十六进制记得加h)

    字符串需要用双引号或者单引号括起来,如’12AB’,‘string’,
    当然如果将12AB写成 31h,32h,41h,42h也行,这时不需要单引号。
    (引号问题注意一下内部会不会造成冲突,和python感觉差不多。)

    如果是数组的话:
    num db 10,20,30
    outrec db 10 dup(0)
    记住我们的num和outrec是数组的首地址就行。

    如果我们有这样的一个定义:

    ARRAY  DB  ‘HELLO’
           DB  ‘AB’		;两个数据
           DW  ‘AB’		;双字
    

    那么在内存中,我们看到的是这样的:(注意顺序,按照顺序上面两个是db型,下面dw型)
    我们先要知道,越往下地址越大
    dw作为一个整体,所以是A在高字节,B在低字节,所以A在下B在上;
    对于db类型,则是A、B两个部分组成的字符串,所以需要将字符串的第一个元素放在首地址,然后将剩下的元素向后排,故A在上B在下。
    在这里插入图片描述

    标号

    上面的例子不好就在于没有循环或者条件分支,看不到标号。
    在循环或者分支的过程中,我们需要进行跳转,这时就需要标号了:
    在这里插入图片描述
    红框的部分就是标号,配合跳转指令能跳转到对应标号的位置(红字),其中loop和jnz都有跳转的属性。

    奇奇怪怪的东西

    这部分可能就不是很常用了,但是还是先给出来吧。

    强转

    我们有时候可能在赋值过程中,类型匹配不上,如:

    OPER1  DB  ?, ?
    ……
    MOV  AX, OPER1+1 
    

    字节和字对不上,会报错。这时我们就需要ptr指令了。
    MOV AX, WORD PTR OPER1+1
    这时我们是将oper1数组的第二位和其前面一个字的内容都取出来赋值给ax寄存器。
    如果内存是这样的:
    在这里插入图片描述
    那么ax就为3402h。(oper1+1在2对应的地址)

    如果是 MOV AL, BYTE PTR OPER2,此时al寄存器为34h,因为此时的地址为34h处的地址,我们只需要一个字节。

    label伪指令

    BYTE_ARRAY LABEL BYTE
    这样赋值是没有分配内存的。

    BYTE_ARRAY  LABEL  BYTE
    WORD_ARRAY  DW  50  DUP (?)
    

    在内存中:
    在这里插入图片描述
    这时我们来看两个指令:
    在这里插入图片描述

    equ伪指令

    表达式名 EQU 表达式
    这样就将两者对等,类似于"=",但是还有所不同,这种方式不允许重复定义。

    地址计数器

    “$”:保存当前正在汇编的指令的偏移地址。

    在指令中本指令的第一个字节的地址。
    我们可以使用JNE $+6,转向当前地址+6(个字节)的位置。

    在data段中表述地址计数器的当前值。
    例:ARRAY DW 1, 2 , $+4 , 3 , 4 , $+4
    在这里插入图片描述
    首先是前两个格为0001h,下两个是0002h,然后跳过四个字节,在007A赋值为0003h,然后是4。

    我们可以使用org伪指令来设置地址计数器的值。
    Buffer Label BYTE+ORG $+8 = Buffer DB 8 DUP(?)

    基数控制

    修改默认的进制,
    .RADIX 16 表示修改为16进制。

    更多相关内容
  • 经典汇编程序100例

    2019-04-08 08:00:22
    包含了一系列的经典汇编程序,非常适合一些初学者模仿,从而提升自己的汇编程序的编写能力。 包含了一系列的经典汇编程序,非常适合一些初学者模仿,从而提升自己的汇编程序的编写能力。
  • 3.汇编程序、c/c++程序间的相互调用  汇编程序、C程序及c++程序在相互调用时,特别应注意遵守相应的ATPCS规则。下面举一些例子具体说明在这些混合调用中应注意遵守的ATPCS规则。  (1)C程序调用汇编程序  ...
  • C语言源码仿真实例4 8279汇编程序加仿真(程序+仿真)C语言源码仿真实例4 8279汇编程序加仿真(程序+仿真)C语言源码仿真实例4 8279汇编程序加仿真(程序+仿真)C语言源码仿真实例4 8279汇编程序加仿真(程序+仿真)...
  • 3.汇编程序、c/c++程序间的相互调用  汇编程序、C程序及c++程序在相互调用时,特别应注意遵守相应的ATPCS规则。下面举一些例子具体说明在这些混合调用中应注意遵守的ATPCS规则。  (1)C程序调用汇编程序  ...
  • 汇编(五):第一个汇编程序

    万次阅读 多人点赞 2021-09-20 20:04:50
    文章目录前言一个源程序从写出到执行的过程源程序的主要结构以简化的方式进行汇编和连接 前言   一个源程序从写出到执行的过程 一个汇编语言程序从写出到最终执行的简要过程: 编写->编译连接->执行 ...

    前言

    进入一个全新的阶段,编写我们的第一个汇编程序,Hello World!

     

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

    1. 一个汇编语言程序从写出到最终执行的简要过程:
      编写 -> 编译连接 -> 执行

    2. 对源程序进行编译连接:

      1. 使用汇编语言编译程序(MASM.EXE)对源程序文件中的源程序进行编译,产生目标文件【.obj文件】
      2. 再用连接程序(LINK.EXE)对目标文件进行连接,生成可在操作系统中直接运行的可执行文件【.EXE文件】
         
    3. 可执行文件包含两部分内容:

      1. 程序(从源程序的汇编指令翻译过来的机器码)和数据(源程序中定义的数据);
      2. 相关的描述信息(比如:程序有多大、要占多少内存空间等);
         
    4. 执行可执行文件中的程序:

      1. 在操作系统(如:MSDOS)中,执行可执行文件中的程序;
      2. 操作系统依照可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关的初始化(比如:设置 CS:IP 指向第一条要执行的指令),然后由 CPU 执行程序;
         

    源程序的主要结构


    源程序由“ 汇编指令+伪指令+宏指令 ”组成:
    伪指令:编译器处理;
    汇编指令:编译为机器码;

    1. 伪指令:

      1. 没有对应的机器码的指令,不能由 CPU 直接执行;
      2. 伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作;
         
    2. segment 和 ends【定义一个段】

      1. segment 和 ends 是一对成对使用的伪指令;

      2. 编写汇编程序【必须】使用到的指令;

      3. segment 和 ends 的功能是定义一个段:

        • segment:说明一个段开始;
        • ends:说明一个段结束;
      4. 一个段必须有一个名称来标识,使用格式为

        段名 segment
        段名 ends
        
      5. 一个汇编程序由多个段组成:
        这些段用来存放【代码,数据或当作栈空间】来使用,一个有意义的汇编程序至少要有一个段,这个段用来存放代码。

    3. end【真正的没了】

      1. end 是一个汇编程序的结束标记;
      2. 编译器在编译汇编程序的过程中,如果碰到了伪指令 end,就结束对源程序的编译;
      3. 如果程序写完了,要在结尾处加上伪指令 end,否则,编译器无法知道程序在何处结束;
      4. 【切记】不要把 end 和 ends 搞混了!
        • end:汇编程序的结束标记;
        • ends:与 segment 成对出现,表示一个段结束;
           
    4. assume【寄存器和段的关联假设】

      1. 它假设某一段寄存器和程序中的某一个用 segment…ends 定义的段相关联;
      2. 通过 assume 说明这种关联,在需要的情况下,编译程序可以将段寄存器和某一具体的段相联系;
         
    5. 程序和源程序

      1. 我们将源程序文件中的所有内容称为【源程序】
      2. 将源程序中最终由计算机执行处理的指令或数据称为【程序】
      3. 程序最先以汇编指令的形式,存储在源程序中,然后经过编译、连接后转变为机器码,存储在可执行文件中;
    6. 标号,标号与段名称有所区别:

      1. 一个标号指代了一个地址,即是段名称,类似指针。
      2. 段名称(codesg)放在 segment 的前面,作为一个段的名称,这个段的名称最终将被汇编、连接程序处理为一个段的段地址;

    任务:编程运算 23

    assume cs:abc
    abc segment
    mov ax,2
    add ax,ax
    add ax,ax
    abc ends
    end
    
    1. DOS 中的程序运行:

      1. DOS 是一个单任务操作系统:
         
        1) 一个程序 P2 在可执行文件中,则必须有一个正在运行的程序 P1,将 P2 从可执行文件中加载入内存后,将 CPU 的控制权交给 P2,P2 才能得以运行。P2 开始运行后,P1 暂停运行。
         
        2) 而当 P2 运行完毕后,应该将 CPU 的控制权交还给使它得以运行的程序 P1,此后,P1 继续运行。

      2. 一个程序结束后,将 CPU 的控制权交还给是他得以运行的程序,称这个过程为:程序返回;

    2. 程序返回
      应该在程序的末尾添加返回的程序段。

      mov ax,4c00H
      int 21H
      

      【中断机制】是 DOS 最伟大的机制,Windows 系统上是【消息机制】,这两条指令所实现的功能就是程序返回;

    3. 几个和结束相关的内容:

      1. 段结束:伪指令
        通知编译器一个段的结束【ends】
      2. 程序结束:伪指令
        通知编译器程序的结束【end】
      3. 程序返回:汇编指令
        mov ax,4c00H
        int 21H
        
    4. 语法错误和逻辑错误:

      1. 语法错误
        1. 程序在编译时被编译器发现的错误;
        2. 容易发现;
      2. 逻辑错误
        1. 在编写时不会表现出来的错误、在运行时会发生的错误;
        2. 不容易发现;
           

    以简化的方式进行汇编和连接

    汇编使用的程序:masm.exe
    连接使用的程序:link.exe
    简化方式进行汇编和连接的程序:ml.exe

    MASM下载链接,提取码:gd2c;

    跟之前 汇编(三):DEBUG 中提到的操作一样,修改配置文件,自动挂载 MASM 目录,可以输入 dir 进行验证;

    编写一个 Hello World 程序:

    .model small
    
    .data
        strs DB 'hello world',13,10,'$'
    .code
    start:
        mov ax,@data
        mov ds,ax
        mov dx,offset strs
        mov ah,09h
        int 21h
        mov ah,4ch
        int 21h
    end start
    

    先复制到 txt 文本中,然后将后缀改成 asm,使用 masm 1.asm 命令进行汇编;

    然后通过 link 1.obj 进行链接;


    最后执行所生成的 exe 文件;
     

    汇编和连接的作用

    连接的作用:

    1. 当源程序很大时,可以将他们分成多个源程序文件夹编译,每个源程序编译成为目标文件后,再用连接程序将它们连接在一起,生成一个可执行文件;
    2. 程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件;
    3. 一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。所以在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件;
       

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

    1. 在 DOS 中,可执行文件中的程序 P1 若要运行,必须有一个正在运行的程序 P2,将 P1 从可执行文件中加载入内存,将 CP U的控制权交给P1,P1 才能得以运行;

    2. 当 P1 运行完毕后,应该将 CPU 的控制权交还给使他得以运行的程序;

    3. 操作系统的外壳:

      1. 操作系统是由多个功能模块组成的庞大、复杂的软件系统,任何通用的操作系统,都需要提供一个称为 shell(外壳)的程序,用户(操作人员)使用这个程序来操作计算机系统工作;
      2. DOS 中有一个程序 command.com,这个程序在 DOS 中称为命令解释器,也就是 DOS 系统的 shell;
         
    4. 执行可执行文件 1.exe 时,
      (1)什么程序将 CPU 的控制权交给了 1.exe?
      (2)将程序 1.exe 加载入内存后,如何使程序得以运行?
      (3)1.exe 程序运行结束后,返回到了哪里?

      1. 在 DOS 中直接执行 1.exe 时,是正在运行的 cmd.exe 将 1.exe 中的程序加载入内存;
      2. cmd.exe 设置 CPU 的 CS:IP 指向程序的第一条指令(即,程序的入口),从而使程序得以运行;
      3. 程序运行结束后,返回 cmd.exe 中,CPU 继续运行 cmd.exe;

    汇编程序从写出到执行的过程:
     

    EXE文件中的程序的加载过程

    • 程序加载后,ds 中存放着程序所在内存区的段地址,这个内存区的偏移地址为 0 ,则程序所在的内存区的地址为:ds:0

    • 这个内存区的前256个字节中存放的是 PSP,dos 用来和程序进行通信。

    • 从 256字节处向后的空间存放的是程序。

    • 所以,我们从 ds 中可以得到 PSP 的段地址 SA,PSP 的偏移地址为 0,则物理地址为 SA×16+0。

    • 因为 PSP 占256(100H)字节,所以程序的物理地址是:SA×16+0+256= SA×16+16×16=(SA+16)×16+0,可用段地址和偏移地址表示为:SA+10:0。

    展开全文
  • 汇编程序设计的教程课件资源大小:435.92KB[摘要] 本文档的主要内容详细介绍的是汇编程序设计的教程课件免费下载包括了:汇编语言与汇编器,汇编语言程序规范,伪指令及应用,宏指令及应用,汇编语言程序设计,汇编...
  • 基于51单片机汇编程序波形发生器实训报告.pdf基于51单片机汇编程序波形发生器实训报告.pdf基于51单片机汇编程序波形发生器实训报告.pdf基于51单片机汇编程序波形发生器实训报告.pdf基于51单片机汇编程序波形发生器实...
  • 基于51单片机汇编程序波形发生器实训报告.docx基于51单片机汇编程序波形发生器实训报告.docx基于51单片机汇编程序波形发生器实训报告.docx基于51单片机汇编程序波形发生器实训报告.docx基于51单片机汇编程序波形发生...
  • 基于Keil 5编写汇编程序

    千次阅读 多人点赞 2020-12-28 21:33:08
    本文目的是通过keil 5 编写汇编程序来熟悉汇编语言的相关知识。这里分为两个部分:第一个部分在Keil上练习汇编的编写和调试,同时了解一下Hex文件的格式;第二个部分是使用汇编进行stm32F103的点灯实验,用实战的...

    本文目的是通过keil 5 编写汇编程序来熟悉汇编语言的相关知识。这里分为两个部分:第一个部分在Keil上练习汇编的编写和调试,同时了解一下Hex文件的格式;第二个部分是使用汇编进行stm32F103的点灯实验,用实战的方式来加深理解。

    (一)汇编语言

    1.简介

    • 汇编语言(Assembly Language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。

    2.指令

    • 传送指令
      包括通用数据传送指令MOV、条件传送指令CMOVcc、堆栈操作指令PUSH/PUSHA/PUSHAD/POP/POPA/POPAD、交换指令XCHG/XLAT/BSWAP、地址或段描述符选择子传送指令LEA/LDS/LES/LFS/LGS/LSS等。
    • 逻辑运算
      用于执行算术和逻辑运算,包括加法指令ADD/ADC、减法指令SUB/SBB、加一指令INC、减一指令DEC、比较操作指令CMP、乘法指令MUL/IMUL、除法指令DIV/IDIV、符号扩展指令CBW/CWDE/CDQE、十进制调整指令DAA/DAS/AAA/AAS、逻辑运算指令NOT/AND/OR/XOR/TEST等。
    • 移位指令
      用于将寄存器或内存操作数移动指定的次数,包括逻辑左移指令SHL、逻辑右移指令SHR、算术左移指令SAL、算术右移指令SAR、循环左移指令ROL、循环右移指令ROR等。
    • 位操作
      包括位测试指令BT、位测试并置位指令BTS、位测试并复位指令BTR、位测试并取反指令BTC、位向前扫描指令BSF、位向后扫描指令BSR等。
    • 控制转移
      包括无条件转移指令JMP、条件转移指令JCC/JCXZ、循环指令LOOP/LOOPE/LOOPNE、过程调用指令CALL、子过程返回指令RET、中断指令INTn、INT3、INTO、IRET等。
    • 串操作
      用于对数据串进行操作,包括串传送指令MOVS、串比较指令CMPS、串扫描指令SCANS、串加载指令LODS、串保存指令STOS,这些指令可以有选择地使用REP/REPE/REPZ/REPNE和REPNZ的前缀以连续操作。
    • 输入输出
      用于同外围设备交换数据,包括端口输入指令IN/INS、端口输出指令OUT/OUTS。

    3.优缺点

    • 优点
      • 可以轻松的读取存储器状态以及硬件I/O接口情况
      • 编写的代码因为少了很多编译的环节,可以能够准确的被执行
      • 作为一种低级语言,可扩展性很高
    • 缺点
      • 程序非常单调,特殊指令字符很少,造成了代码的冗长以及编写的困难
      • 仍然需要自己去调用存储器存储数据,很容易出现BUG,而且调试起来也不容易;就算完成了一个程序,后期维护时候也需要耗费大量的时间,因为机器的特殊性造成了代码兼容性差的缺陷。

    (二)使用Keil 5编写汇编程序

    1.新建汇编工程

    • 选择Project->New uVision Project...
      1
    • 给新建的工程命名并保存到到相应的位置,我这里命名为test
      2
    • 由于后面会涉及到STM32的点灯实验,因此这里需要选择使用的硬件支持,这里我使用的是STM32F103ZET6所以选择STM32F103ZE即可
      3
    • 勾选COREStartup两个选项,点击OK
      4
    • 这时左边的Project选项中出现了我们刚才新建好的工程
      5

    2.新建汇编文件

    • Source Group 1选项下右键选择Add New Item to Group Source Group 1...
      6
    • 选择Asm File(.s),输入新建的汇编文件的名字(我这里命名为TEST
      7
    • 编写汇编程序
     AREA MYDATA, DATA
    	
     AREA MYCODE, CODE
    	ENTRY
    	EXPORT __main
    
    __main
    	MOV R0, #10
    	MOV R1, #11
    	MOV R2, #12
    	MOV R3, #13
    	;LDR R0, =func01
    
    	BL	func01
    	;LDR R1, =func02
    	BL	func02
    	
    	BL 	func03
    	LDR LR, =func01
    	LDR PC, =func03
    	B .
    		
    func01
    	MOV R5, #05
    	BX LR
    	
    func02
    	MOV R6, #06
    	BX LR
    	
    func03
    	MOV R7, #07
    	MOV R8, #08	
    	BX LR
    	
    	END
    

    3.调试汇编程序

    • 编译
      10
      没有错误,没有警告。
    • 配置Debug选项
      • 右键工程,选择Options for Target 'Target1'...(或者直接点击图中的魔术棒的图标)
        8
      • Debug选项卡下选择使用仿真调试工具(我这里使用的ST-Link进行程序调试因此选择ST-Link Dehugger
        9
    • 点击图中的调试按钮Start/Stop Debug Session开始调试
      11
    • 进入调试界面如下:
      12
    • 动态调试变量
      • MOV R0, #10这里设置断点,运行程序(F5)
        可以看到程序运行到这里时停了下来,此时MOV R0, #10还未执行,我们可以看到R0、R1、R2、R3的值都为0还没有改变。
        13
      • 取消原来设置的断点并在BL function1这里设置断点,运行程序(F5)
        我们可以看到此时R0、R1、R2、R3的值都相应的变为赋给它的值(R0变为0x0000000A,R1变为0x0000000B,R2变为0x0000000C,R3变为0x0000000D
        14
      • 取消原来设置的断点并在fun03的BX LR这里设置断点,运行程序(F5)
        15
        16
        通过上面的图片我们可以看到R5、R6、R7、R8的值从全零变为相应的值(R5变为0x00000005,R6变为0x00000006,R7变为0x00000007,R8变为```0x00000008 ``)

    4.hex文件简介

    • 生成hex文件
      工程选项中选择Output,然后勾选Create HEX File,再重新编译一下工程即可生成Hex文件
      17
    • Hex文件分析
      • 打开生成的Hex文件如下:
        Hex文件都是由记录(RECORD)组成的。在HEX文件里面,每一行代表一个记录,每个记录由冒号开始。
        18
      • Hex文件分析

    第一个字节 0x02表示本行数据的长度;
    第二、三个字节 0x00 0x00表示本行数据的起始地址;
    第四个字节 0x00表示数据类型,数据类型有:0x00、0x01、0x02、0x03、0x04、0x05。
    后面是数据字节
    最后一个字节 0xD0为校验和。

    数据类型如下:
    00: Data Rrecord 用来记录数据,HEX文件的大部分记录都是数据记录
    01: End of File Record 用来标识文件结束,放在文件的最后,标识HEX文件的结尾
    02: Extended Segment Address Record 用来标识扩展段地址的记录
    03: Start Segment Address Record 开始段地址记录
    04: Extended Linear Address Record 用来标识扩展线性地址的记录
    05: Start Linear Address Record 开始线性地址记录

    (三)汇编点灯实验

    1.实验程序

    LED0 EQU 0x42218194 ;LED0的地址 (PB5)
    RCC_APB2ENR EQU 0x40021018
    ;GPIOA_CRH EQU 0x40010804
    GPIOB_CRL EQU 0x40010C00
    
    Stack_Size      EQU     0x00000400
    
                    AREA    STACK, NOINIT, READWRITE, ALIGN=3
    Stack_Mem       SPACE   Stack_Size
    __initial_sp
    
                    AREA    RESET, DATA, READONLY
    
    __Vectors       DCD     __initial_sp               ; Top of Stack
                    DCD     Reset_Handler              ; Reset Handler
                        
                        
                    AREA    |.text|, CODE, READONLY
                        
                    THUMB
                    REQUIRE8
                    PRESERVE8
                        
                    ENTRY
    Reset_Handler 
                    BL LED_Init
    MainLoop        BL LED_ON
                    BL Delay
                    BL LED_OFF
                    BL Delay
                    
                    B MainLoop
                 
    LED_Init
                    PUSH {R0,R1, LR}
                    
                    LDR R0,=RCC_APB2ENR
                    ORR R0,R0,#0x08		;使能GPIOB管脚时钟
                    LDR R1,=RCC_APB2ENR
                    STR R0,[R1]
                    
                    LDR R0,=GPIOB_CRL
                    BIC R0,R0,#0XFF0FFFFF	;配置为模拟输入模式
                    LDR R1,=GPIOB_CRL
                    STR R0,[R1]
                    
                    LDR R0,=GPIOB_CRL
                    ORR R0,R0,#0X00300000	;配置为通用推挽输出模式
                    LDR R1,=GPIOB_CRL
                    STR R0,[R1]
                    
                    MOV R0,#1 
                    LDR R1,=LED0
                    STR R0,[R1]
                 
                    POP {R0,R1,PC}
    
                 
    LED_ON
                    PUSH {R0,R1, LR}    
                    
                    MOV R0,#0 
                    LDR R1,=LED0
                    STR R0,[R1]
                 
                    POP {R0,R1,PC}
                 
    LED_OFF
                    PUSH {R0,R1, LR}    
                    
                    MOV R0,#1 
                    LDR R1,=LED0
                    STR R0,[R1]
                 
                    POP {R0,R1,PC}             
                 
    Delay
                    PUSH {R0,R1, LR}
                    
                    MOVS R0,#0
                    MOVS R1,#0
                    MOVS R2,#0
                    
    DelayLoop0        
                    ADDS R0,R0,#1
    
                    CMP R0,#330
                    BCC DelayLoop0
                    
                    MOVS R0,#0
                    ADDS R1,R1,#1
                    CMP R1,#330
                    BCC DelayLoop0
    
                    MOVS R0,#0
                    MOVS R1,#0
                    ADDS R2,R2,#1
                    CMP R2,#15
                    BCC DelayLoop0
                    
                    POP {R0,R1,PC}    
    
                    END
    

    2.程序分析

    • 预定义

    LED0 EQU 0x42218194
    RCC_APB2ENR EQU 0x40021018
    GPIOB_CRL EQU 0x40010C00

    这里主要是进行LED管脚位段(bit-band)地址、RCC_APB2ENR外设时钟使能寄存器和GPIOB_CRL端口配置低寄存器地址的设置。其中RCC_APB2ENR外设时钟使能寄存器地址基本上是固定的,不需要改动;而LED管脚和端口配置寄存器的地址需要根据实际情况查阅相关手册进行相应的修改。
    这里根据下面红色方框中的公式进行LED管脚位带地址的计算(参照库函数工程版本中sys.h头文件的宏定义)
    19

    Cortex™-M3存储器映像包括两个位段(bit-band)区。这两个位段区将别名存储器区中的每个字映射到位段存储器区的一个位,在别名存储区写入一个字具有对位段区的目标位执行读-改-写操作的相同效果。在STM32F10xxx里,外设寄存器和SRAM都被映射到一个位段区里,这允许执行单一的位段的写和读操作。
    关于位段的详细信息请参考《Cortex™-M3技术参考手册》

    GPIOB_CRL端口配置低寄存器地址则是在GPIOB的基地址上参照手册上的偏移地址计算得出。

    • 启动初始化

      • 分配栈空间
      Stack_Size EQU 0x00000400
      AREA STACK, NOINIT, READWRITE, ALIGN=3
      Stack_Mem SPACE Stack_Size
      __initial_sp
      

      AREA命令:AREA 命令指示汇编器汇编一个新的代码段或数据段。段是独立的、指定的、不可见的代码或数据块,它们由链接器处理。格式如下:

      AREA 段名,段属性1,段属性2,段属性3。。。
      AREA STACK, NOINIT, READWRITE, ALIGN=3
      NOINIT: = NO Init,不初始化。
      READWRITE : 可读,可写。
      ALIGN =3 : 2^3 对齐,即8字节对齐。

      SPACE命令:SPACE 命令保留一个用零填充的存储器块。
      所以这段代码意思是:分配一个STACK段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。

      • 分配向量表
      AREA RESET, DATA, READONLY
      __Vectors DCD __initial_sp ; Top of Stack
      DCD Reset_Handler ; Reset Handler
      
    • 开始代码段

    AREA |.text|, CODE, READONLY	;通知汇编器,开始代码段,设置为只读
    THUMB
    REQUIRE8
    PRESERVE8
    ;这段的意思是,汇编器支持THUMB指令,代码段按8字节对齐
    ENTRY
    

    ENTRY命令:声明整个程序的入口点,有且仅有一个。

    • 程序正式开始
      相关指令:
      • BL:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器。
      • B:无条件跳转。
      • PUSH/POP:将值存入堆栈/从堆栈取出
      • LDR和STR:寄存器的装载和存储指令。
      • ORR 按位或操作。
      • BIC 先把立即数取反,再按位与。
      • CMP:CMP是比较两个数,相等或大于则将标志位C置位,否则将C清零。
      • BCC是个组合指令,实际为B+CC,意思是如果C=0则跳转。

    3.实验结果

    test

    实验说明
    我这里采用STM32F103ZET6开发板进行LED的点灯实验,并且完全采用纯汇编的方式实现间隔一定时间点亮LED灯,因此我这里将之前添加的启动文件给去掉,避免后续编译时产生类似于error: L6235E: More than one section matches selector - cannot all be FIRST/LAST.的错误,这因为我们这里使用的纯汇编语言编写stm32点灯实验,所以在编写的.s文件中添加了堆栈指针的初始化,而之前仿真调试时没有添加堆栈指针的初始化;如果没有去掉之前添加启动文件,则会因为有两个启动文件导致编译失败。

    (四)总结

    这次在Keil 5上使用汇编语言进行调试和stm32的点灯实验,让我有了一番不一样的体验;感觉虽然相比于C语言来说,汇编确实要更加难以理解和编程实现并且难以移植到其他硬件上去,但是它的执行心率确实要快了不少;与库函数、寄存器编写的点灯实验相比,汇编语言编写的LED点灯其Hex文件只有477字节,而寄存器和库函数编写的点灯其Hex文件大小分别为3.81KB和5.32KB。由此可见,汇编语言执行的高效和简洁。而且在学习的过程中,也让我更加了解程序是如何被机器识别和执行的,感觉收获很大。

    参考文章:
    1.汇编语言 (面向机器的程序设计语言)–百度百科
    2.ARM汇编基础之基于MDK创建纯汇编语言的STM32工程
    3.hex文件说明
    4.简单的STM32 汇编程序—闪烁LED

    展开全文
  • 单片机实验报告一--汇编程序之流水灯.docx单片机实验报告一--汇编程序之流水灯.docx单片机实验报告一--汇编程序之流水灯.docx单片机实验报告一--汇编程序之流水灯.docx单片机实验报告一--汇编程序之流水灯.docx...
  • 单片机实验报告一--汇编程序之流水灯.pdf单片机实验报告一--汇编程序之流水灯.pdf单片机实验报告一--汇编程序之流水灯.pdf单片机实验报告一--汇编程序之流水灯.pdf单片机实验报告一--汇编程序之流水灯.pdf单片机实验...
  • 编写汇编程序流程

    千次阅读 2021-07-13 21:20:28
    第一个汇编程序一个源程序从写出到执行的过程源程序中的"程序"标号编辑源程序编译连接1.exe的执行谁将可执行文件中的程序装载进入内存并使它运行总结程序执行过程的跟踪 一个源程序从写出到执行的过程 第一步:编写...

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

    • 第一步:编写汇编源程序
      使用文本编译器(如 Edit,记事本等),用汇编语言编写汇编源程序
    • 第二步:对源文件进行编译连接
      使用汇编语言编译程序对源程序文件中的源程序进行编译,产生目标文件,再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件
    • 可执行文件包含两部分内容
    • 程序(从源程序中的汇编指令翻译过来的机器码)和数据(源程序中定义的数据)
    • 相关的描述信息(比如,程序有多大,要占用多少内存空间等)
    • 这一步工作的结果,产生了一个可在操作系统中运行的可执行文件
    • 第三步:执行可执行文件中的程序
      在操作系统中,执行可执行文件中的程序
      操作系统按照可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关的初始化(比如设置CS:IP指向第一条要执行的指令),然后由CPU执行程序。

    源程序中的"程序"

    用汇编语言写的源程序,包括伪指令和汇编指令,我们编程的最终目的是让计算机完成一定的任务,源代码中的汇编指令组成了最终由计算机执行的程序,而源程序中的伪指令是由编译器来处理,它们并不实现我们编程的目的,这里所说的程序就是指源程序中最终由计算机执行,处理的指令或数据
    注意,以后可以将源程序文件中的所有内容称为源程序,将源程序中最终由计算机执行,处理的指令或数据,称为程序。程序最先以汇编指令的形式存在源程序中,径编译,连接后转变为机器码,存储在可执行文件中。
    如图所示:
    在这里插入图片描述

    标号

    汇编源程序中,除了汇编指令和伪代码外,还有一些标号,比如"codesg"。一个标号指代了一个地址。比如codesg在segment的前面,作为一个段的名称,这个段的名称最终将被编译,连接程序处理为一个段的段地址

    编辑源程序

    可以用任意的文本编译器来编译源程序,只要最终将其存储为纯文本文件即可,我们使用DOS下的Edit

    • 进入DOS方式,运行Edit
    • 在Edit中编辑程序,如图所示
      在这里插入图片描述
    • 将程序保存为文件c:\1.asm后,退出Edit,结束对源程序的编辑

    编译

    • 进入DOS方式,进入c:\masm目录,运行masm.exe,如图所示
      在这里插入图片描述
      注意,“[.ASM]“提示我们,默认的文件扩展名是asm,
      比如,要编译的源程序文件名是"p1.asm”,只要在这里输入"p1"即可,可如果源程序文件不是以asm为扩展名的话,就要输入它的全名,比如源程序文件名为"p1.txt",就要输入全名
    • 输入要编译的源程序文件名后,按enter键,如图所示
      在这里插入图片描述
      我们直接按enter键,使用编译程序设定的目标文件名
    • 确定目标目标文件名称后,如图所示:
      在这里插入图片描述
    • 忽略了列表文件的生成后,如图所示:

    在这里插入图片描述

    • 忽略了交叉引用文件的生成后,如图所示:
      在这里插入图片描述
      按照上面的过程进行了编译后,在编译器masm.exe运行的目录下,将出现一个新的文件:1.obj,这是对源程序1.asm进行编译所得到的结果,当然如果编译过程中出现错误,那么将得不到目标文件,一般来说,有两类错误使我们得不到所期望的目标文件
    • 程序中有"Severe Errors"
    • 找不到所给出的源程序文件

    连接

    在对源程序进行编译得到目标文件,我们需要对目标文件进行连接,从而得到可执行文件。

    • 进入DOS方式,进入C:\masm目录,运行link.exe,如图所示:

    在这里插入图片描述

    • 确定了可执行文件的名称后,忽略了映像文件的生成后,忽略了库文件的连接后,如图所示:
      在这里插入图片描述
      注意,对于连接的过程,可执行文件是我们要得到的最终结果

    1.exe的执行

    现在,终于将我们的第一个汇编程序加工成了一个可在操作系统下执行的程序文件,我们现在执行一下,如图所示:
    在这里插入图片描述
    程序运行后,就和没有运行一样,那么,程序到底运行了吗?
    程序当然运行了,只是从屏幕上不可能看到任何运行结果,因为,我们的程序根本没有向显示器输出任何信息,程序只是做了一些将数据送入寄存器和加法的操作,而这些事情,我们无法从显示屏中看出来。

    谁将可执行文件中的程序装载进入内存并使它运行

    在DOS中,可执行文件中的程序P1若要运行,必须有一个正在运行的程序P2,将P1从可执行文件中加载入内存,将CPU的控制权交给它,P1才能得以运行,当P1运行完毕后,应该将CPU的控制权交还给使它得以运行的程序P2
    于是得出结论:

    • 在DOS中直接执行1.exe时,是正在运行的comomand,将1.exe中的程序加载入内存
    • command设置CPU的CS:IP指向程序的第一个指令(即程序的入口),从而使程序得以运行
    • 程序运行结束后,返回到command中,CPU继续运行command

    总结

    汇编程序从写出到执行的过程:

    • 编程(Edit)->1.asm->编译(masm)->1.obj->连接(link)->1.exe->加载(command)->内存中的程序->运行(CPU)

    程序执行过程的跟踪

    可以用debug来跟踪一个程序的运行过程,这通常是必须要做的工作,我们写的程序在逻辑上不一定总是正确,对于简单的错误,仔细检查一下源代码就可以发现,而对于影藏较深的错误,就必须对程序的执行过程进行跟踪分析才容易发现
    具体方法:
    在这里插入图片描述
    我们可以用R命令看一下各个寄存器的设置情况:
    在这里插入图片描述
    cx中存放的是程序的长度,1.exe中程序的机器码共有15个字节,则1.exe加载后,cx的内容为000FH
    可以用U命令看一下其他指令:
    在这里插入图片描述
    可以看到,从12AE:0000—12AE:000E都是程序的机器码
    现在,我们可以开始跟踪了,用T命令单步执行程序中的每一个指令,并观察每条指令的执行结果,到了int 21,我们要用P命令执行,如图所示:
    在这里插入图片描述

    需要注意的是在DOS中运行程序时,是command将程序加载入内存中,所以程序运行结束后返回到command中,而在这里是debug将程序加载入内存,所以程序运行结束后要返回到Debug中。
    使用Q命令退出debug,将返回到command中,因为debug是由command加载运行的,在DOS中用“debug 1.exe"运行Debug对1.exe进行跟踪后,程序加载的顺序是:
    command加载Debug,Debug加载1.exe,返回的顺序是:从1.exe中的程序返回到debug,从debug返回到command。

    展开全文
  • C51单片机数字温度计汇编程序及说明书.docxC51单片机数字温度计汇编程序及说明书.docxC51单片机数字温度计汇编程序及说明书.docxC51单片机数字温度计汇编程序及说明书.docxC51单片机数字温度计汇编程序及说明书....
  • 基于STM32的汇编程序

    千次阅读 2021-09-29 22:58:01
    文章目录1.KEIL的下载及安装1.1 mdk531的下载1.2 mdk531的安装2 基于STM32编写汇编程序2.1 在Keil中新建工程2.2 添加源文件2.3 编译并调试程序2.4 HEX文件分析3 总结4 参考文献 1.KEIL的下载及安装 1.1 mdk531的下载...
  • 汇编语言设计程序,完成8个数码管的显示控制。 检查内容:当按下INT 按钮时,数码管开始快速计时,高五位为 秒数,低三位为ms 数,每1ms 刷新一次显示内容。当再次按下 INT 按钮时,停止计数。
  • 简单的STM32汇编程序

    千次阅读 2020-12-30 21:08:06
    目录一、简单的汇编程序二、添加文件四、结果分析 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201230205708227.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9...
  • 通过汇编程序理解汇编和链接过程

    千次阅读 多人点赞 2020-09-17 18:20:17
    通过编写汇编程序,然后分析它的汇编和链接过程,对理解汇编程序中的各种汇编器指令和各种标签很有帮助。 首先介绍以下汇编器指令和标签这两个概念,观察下面一段求最大值的汇编程序代码maxmum.s: #目的:寻找一组...
  • 汇编程序

    2018-06-01 13:48:11
    汇编程序汇编程序汇编程序 反汇编程 反汇编程序汇编程序汇编程序 反汇编程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 397,899
精华内容 159,159
关键字:

汇编程序

友情链接: Drivers AF9015.rar