Ch3 编译原理 ARM指令集和数据存储表示法

Instructions & Memories

Posted by R1NG on October 27, 2020 Viewed Times

Ch3 ARM指令集和数据存储表示法

Motivation:

  1. ARM 设计思路: 灵活性和简便性

    • 数据如何被存储于内存中?
    • 为何程序计数器每次变化都为 4?
    • ARM 指令是如何存储于内存中的?
  2. 何为汇编语言

    • 指令
    • 实际上并不存在的指令
    • 非指令

1. ARM 存储

在考虑指令集的设计时, 我们必须考虑将所有可能有用的指令用较长的字存储, 以及尽可能地令指令长度变短以简化指令集这两个设计思路之间的平衡.

ARM 指令集的存储操作指令中, 我们共涉及到了两种字长. 第一种是长为 $32$ 位的字 (Word), 第二种是长为 $8$ 位的字节(Byte):

1
2
3
4
5
    STR ;All 32 bits of a register are copied to the mem
    LDR ;All 32 bits of mem copied to a register

    STRB ;BOTTOM 8 bits of a register are copied to mem
    LDRB ;8 bits of mem are copied to the BOTTOM of a register, upper 24 bits are ZEROED
  • 端序

    计算机硬件有两种储存数据的方式:大端序 (big endian)和小端序 (little endian).

    大端序:高位字节在前,低位字节在后,这也是我们人类读写数值的习惯. 小端序:低位字节在前,高位字节在后, 和通常的读写数值习惯相反.

    在计算机硬件中使用违反习惯的小端序是有必要的. 由于计算都是从低位开始, 计算机电路先处理低位字节的效率比较高. 因此, 一般计算机内部处理/存储数据的方式都是小端序。 然而,人类还是习惯读写大端序. 因此,在其它的场合下, 几乎都使用大端序, 比如网络传输和文件储存.

    ARM 架构使用小端序以提高运算效率.

  • 寻址范围

    下面, 我们考虑 ARM 处理器的寻址范围. 如上文所述, 一个标准的 ARM 地址是 $32$ 位的, 并且所有的地址都是 8 位的字节.

  • 内存映射 在真实的计算机硬件中, 不同的内存地址范围可能会被映射到不同类型的存储设备上, 并且有一些内存地址可能是空映射, 因为硬件可能并没有配置那么大的内存空间. 目前我们为了简化, 可以假设所有的内存地址均被映射到某个存储单元中, 并且除了程序计数器以外, 我们可以在任何存储单元中存储数据.

  • ARM 指令编码 在汇编语言中, 程序是使用助记符代表的指令组合而成的. 编译器在将这些人类能读懂的文字转化为机器码时, 首先会基于 ASCII 表将其转换位二进制位 (例: $14$ 个 ASCII符号将占用 $112$ 个位), 并将每条指令编译为 $32$ 位的二进制机器码.

    下面, 我们以分支命令 B 为例, 详细说明这种编码过程: 经过编译器的操作后, 每条指令都会被转为一串长为 $32$ 位的二进制机器码. 它的逻辑分划如下:

    20201105192647

    需要注意的是, $32$ 位的二进制机器码后部的 24 位是被视为 OFFSET (偏移量) 处理的, 因为每条指令的内存地址都长为 $4$ 个位 (32字节), 我们只希望程序计数器跳转到每一条指令头部所对应的内存地址. 因此, 我们实际可以通过这个偏移量访问到 $2^{23}$ 条指令. (为了能够向前/后分支, 必须划出一位以指示方向(符号))


2. ARM 汇编指令

  • ARM 数据操作指令

    下面是 $16$ 种常用的数据操作指令:

    20201105192401

    ARM 指令集提供了 $16$ 种数据操作指令, 上表中加粗的是最有用的常用指令. 需要注意的是, ARM 架构并没有提供硬件除法指令. 要进行除法运算, 则必须通过软编程将其手动实现.

    对数据操作指令的编码逻辑基本如下:

    20201105192600

  • 常数编码

    我们一般使用 ”文字“ (Literal, immediate) 直接表示常数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    ;Define directly as LITERAL:
    MOV R2, #32     ;Put value '32' into R2
    SUB R5, R5, #1  ;Decrement R5 by 1
    
    ;Using a defined memory space
              LDR R3, minimum
    minimun   DEFW 2000
    
    ;Using EQU:
    minimum   EQU 2000
              MOV R3, #minimum
    
    ;EQU is an Assembler Directive (similar to DEFW), while:
    ;1. DO NOT reserve space in mem
    ;2. the value of the constant 'minimum' is placed as a literal
    ;within any instructions that it is used
    ;we can use arithmatics like: 
    ;MOV R3, #minimum + 100
    

    我们还可以直接将 “文字” 加载到寄存器中:

    1
    
    LDR R0, =123    ;load R0 with literal num 123
    

    如果需要加载的 “文字” 过大, 可以使用以下指令:

    1
    2
    3
    
    LDR R0, const
    ......
    const DEFW 114514
    

    注:

    1. 此时, 数值将和其它数值一起, 被存储在 “文字池” (Literal Pool) 中.
    2. LDR , =” 指令可能需要数个字来存储.
  • 监督器呼叫指令 SVC

    在一些特殊情况下, 我们需要令程序和操作系统进行通讯, 以实现一些特定的功能, 如输入和输出. 这些操作被监督器呼叫指令所预定义:

    1
    
      SVC 2   ;HALT
    

    虽然 SVC 也是 ARM 指令, 但它实际执行的行为是由软件层而非处理器本身所定义的. 一般而言, SVC 表现为一个向操作系统发起的, 要执行某些函数或功能的请求.

    在本课程的 Lab

    1
    2
    3
    4
    5
    
      SVC 0 = output a character
      SVC 1 = input a character
      SVC 2 = stop
      SVC 3 = output a string
      SVC 4 = output an integer in decimal format
    
  • 伪指令

    “伪指令” 是指不位于指令集中, 无法被直接执行和编码成机器码的指令.

  • 负值
    ARM 文字是非负的. 但有些时候我们也可以使用它们的负值:
    1
    2
    3
    
      ADD R0, R1, #-1  ; SUB R0, R1, #1
      CMP R2, #-10     ; CMN R2, #10
      MOV R3, #-3      ; MVN R3, #2
    

    注:

    1. CMN is ‘CoMpare Negative’
    2. MVN is ’MoVe Not’, it is NOT ‘MoVe Negative’
  • ADR(L): 内存地址 - 寄存器

    ADR 指令将 内存地址 存入寄存器.


  • 汇编伪操作 (Directives)

    伪操作是用于控制编译器行为的非指令. 其中, DEF 允许数据被定义和初始化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
      DEFW num    
      ;reserve a word (32bit) of store, and put the initial value--
      ;--"num" into it. 
    
    
      DEFB ...
      ;reserve byte(s) (8bit) of store, put --
      ;--the initial value(s) in.
      ;example:
      string DEFB "Hello", 0
    
      DEFS size, fill
      ;reserve a block of store of "size" bytes--
      ;--all initialized to "fill" (can be omitted, then become undefined)
    
      ALIGN 
      ;leave blank bytes as required to make the next --
      ;--item start on a word boundry
    
      ORIGIN addr
      ;put the following code or data in memory starting at address "addr" (default is 0)
    
      ENTRY
      ;the starting point of the program
    
      label EQU expression
      ;define your own names for values(literals)