汇编学习笔记-伪指令

Posted 蹦蹦骑士

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了汇编学习笔记-伪指令相关的知识,希望对你有一定的参考价值。

前言

  编写汇编代码的时候会使用到两种语句,一种就是前面介绍的汇编指令又CPU提供功能支持,另一种呢叫做伪指令,伪指令是由汇编的编译器提供支持。所以伪指令的运行结果都必须实在编译的时候就能确定的,下面介绍的就是伪指令了。

 

数值表达式

  数值表达式不是汇编指令,表达式的值是在汇编代码的汇编过程中就由汇编编译器计算出结果而写到二进制程序中了,并不是在程序运行的过程中才计算的

  (1) 常数表达式

    常数就是直接的数字,直接写数字默认表示10进制数,也可以用符号指定为其他进制

    D = 十进制   ; MOV AL, 1234D

    B = 二进制   ; MOV AL,  0101B

    H = 十六进制  ; MOV AL,  0FFFFH , 注意常数必须是数字开头不能是字母开头,如果16进制数第一个数字是字母的话就要补一个0在前面

    Q = 八进制   ; MOV AL, 123Q

 

    因为在程序中字母也是数字,所以其实也可以直接将字母或者字符串当成数字,比如

    MOV AL, ‘a‘

    MOV AX,"ab"  ; 双引号和单引号都是可以使用的

 

   (2) 算数运算符

    就是简单的 正(+) 负(-) 加(+) 减(-) 乘(*) 除(/) 模(mod)

    mov ax, 100+200

    mov ax, 100/2

 

  (3) 关系运算符

    等于(EQ) 不等(NE) 大于(GT) 小于(LT) 大于等于(GE) 小于等于(LE)

    如果等式成立则实际的值为0FFFF就是补码表示的-1, 如果关系不成立那么结果就是0

    mov ax, 123 gt 234

    mov ax, 1234+5 lt 1235

 

  (4) 逻辑运算 

    与(and) 或(or) 非(not) 异或(xor) 左位移(shl) 右位移(shr)

    mov ax, 1 shl 3

    mov al, 3 and 47

 

  (5) 其他操作符

    HIGH LOW WIDTH MASK 

    HIGH 表示取数据的高八位

    LOW 表示取数据的低八位

  

地址表达式

  地址表达式所表示的是存储器操作数的地址。前面介绍的各种寻址方式实际上也是地址表达式。

  一般格式是

  mov ax, varw+4

 

  注意 varw 变量或者标号的地址,这个是在编译的时候就知道的,所以varw+4 计算出来的就是varw 地址偏移4的地址,所以AX的值不是 varw+4的值,而是varw+4 内存地址处的值,所以AX的值是不确定的。

  当然还有其他的写法

  [varw+bx]

  varw[bx]

  varw[bx+di]

  varw[bx][di]

 

变量和标号

  (1) 数据定义语句

    [变量名]  变量类型 表达式[, 表达式]

    其中变量类型有

    DB  字节

    DW  字

    DD   双字

    

    所以数据定义就可以是这样的

    a db 1,2,3,4,5

    db 7,8,9,10

    b dw 123,123,123

    c dd 123,?,?,?

 

    汇编中的数据定义意义其实就是C中的数组指针。这些数据都是顺序存放在内存中的,所以虽然第二行的数据没有定义名字,但是因为他紧跟在a 数组之后 b数组之前,所以使用a+5,或者 b-1 也是可以访问的,只是需要注意的是数据类型和 大端小端的问题。

    后面跟实际数据的就表示定义的时候就直接初始化了数据,如果只是想留着位置,并不需要初始化的话也可以使用? 表示,?的意思就是只留位置不初始化内容

  

    前面也说过,字符其实也可以表示为数字,所以也之后直接定义字符数组,下面的两种方式都是可行的

    str  db “hello”

    str db "h","e","l","l","0"

  

  (2) 循环定义 DUP

    数字表达式 DUP(数据[, 数据,数据...])

    有时候定义数组只是想预留一些空间,可能需要预留100个字节那么使用

    buff db ?,?,?,?.......? 

    这种方式要打100个? 这样显然很没效率所以有个伪指令可以帮忙

    buff db 100 dup(?);   定义100个DB 数据不用初始化

    也可以这样

    buff  db 5 dup("ab") = buff db ‘a‘,‘b‘,‘a‘,‘b‘,‘a‘,‘b‘,‘a‘,‘b‘,‘a‘,‘b‘

    dup 也是可以嵌套的比如

    buff db 2 dup(1,2,5 dup(?)) =  buff db 1,2,?,?,?,?,?,1,2,?,?,?,?,?

 

  (3) this   

     this 类型

     类型可以是byte word dword this返回下一个将分配的地址的

    my_byte equ this byte  ; equ是等效定义符号,下面或解释

    my_word dw 8 dup(?)

    这样就是定义了一个 byte的地址指针指向了一个 word数组等效的C语言就是

    short[8]  my_word[];

    byte* my_byte = (byte*)my_word;

 

  (4) 等价语句  EQU

    符号名 EQU 表达式

    说白了 EQU 就类似C的 #define 语句 

    用法可以是这样的

    COUNT EQU 100      ; #define COUNT 100

    BUFF_EN EQU 4*COUNT  ; #define BUFF_EN  4*COUNT

    HELLO EQU "how are you"   ; #define HELLO "how are you"

    MOVE EQU MOV        ;#define MOVE MOV

    

    VARW EQU THIS WORD  ; 这个就和#dfine不一样了意义上面说过

    VARB DB 2 DUP(?)

  

  (5) 等于语句 =

     符号名 = 数值表达式

    其实就是专用的 EQU,功能和EQU是一样的,只不过试用范围比EQU小,只能用在数值表达式上

    x = 10 ;  x equ 10

    y =20 +50*60 ; y equ 20+50*60

  

  (6) 定义符号名语句

    符号名 LABEL 类型

    类型可以是 BYTE WORD DWORD NEAR FAR

    他的作用其实应该是包含上面 this 指令中的用法即

      my_byte equ this byte

      my_word dw 8 dup(?)

      换成LABEL的写法就是    

      my_byte label byte 

      my_word dw 8 dup(?)

 

   同时LABLE 还比this强大就是还能作用在标号上

   quit label far

   exit: mov ah,4ch

   这样的用法就 mov ah,4ch这条指令同时拥有 exit的短调指令和quit的长跳指令了。

 

   (7) 变量和标号的属性

    变量和标号的属性无非就是对应的类型 地址 大小等,对应的操作符

    LENGTH SIZE OFFSET SEG TYPE PTR

    seg 返回变量或标号的段地址    假设 VAR 的地址是 100+2   那么mov al, seg var ; al=100

    offset 返回变量或标号的偏移地址  假设 VAR 的地址是 100+2   那么mov al, offset var ; al=2

    type 返回变量或标号的类型   字节=1 字=2 双字=3 近=-1 远=-2,变量类型就是实际占用的字节数

    length 返回的是DUP定义的长度  DUP将在下面讲到, 注意这里返回的是个数,如果数组没用DUP那么总是返回1 如果嵌套了就返回最外层的

    size  返回DUP定义的实际占用字节数

    ptr  强制类型转化 就是C语言中的(int)A;的作用 用法就是     类型 ptr  变量/数据地址  类型可以是 byte word dword near far

        例如:

        mov word ptr [si],1

        jmp far ptr ok

 

优先级

   1. 各种括号,LENGTH SIZE WIDTH MASK

   2. PTR OFFSER SEG TYPE THIS 冒号

   3. * / shl shr

   4. HIGH LOW

   5. + -

   6. eq ne lt le gt ge

   7. not

   8. and

   9. or, xor

   10. short

 

程序组织伪指令

  这些指令是为了方便汇编程序的结构组织的

  (1) 段定义语句

    为了与存储器的分段结构相对应,所以汇编指令也提供了对应的段的组织方式

    段的开始

      段名 SEGMENT [定位类型] [组合类型] [‘类别’]

      。。

    段的结束

      段名 ENDS

 

    例子

    ;数据段

    DSEG SEGMENT

    MESS DB ‘HELLO‘

    DSEG ENDS

    ;代码段

    CSEG SEGMENT

      MOV AX, DSEG

        ...

        ...

    CSEG ENDS

 

  (2) 段使用设定语句

    ASSUME 段寄存器名: 段名 [, 段寄存器名: 段名, 段寄存器名: 段名 ..]

    设定了段之后汇编程序需要知道各自段对应是用来干嘛的,并设置对应的段寄存器。ASSUME就是这个作用

    ASSUME CS: CSEG,DS:DSEG

    ASSUME 是伪指令所以汇编编译器其实是将其转换成了对应的汇编指令,所以ASSUME可以出现在代码段的任何位置。随时进行切换

    这里有个需要注意的是,如下代码

    DSGE1 SEGMENT

        .....

    DSGE ENDS

 

    DSEG2 SEGMENT

        ......

    DSGE2 ENDS

 

    CSEG SEGMENT

    ASSUME CS:CSEG DS:DSEG1 ES:DSEG2

    MOV AX,DSEG1      ; 由于此时数据段就是DSEG1 所以指令就是直接翻译的

    MOV AX,DSEG2      ; 由于此时数据段是DSEG1,所以实际语句会被翻译成 MOV AX, ES:DSEG2

 

  (3) ORG 指令

    ORG 数值表达式

    汇编编译器在汇编的时候使用汇编地址计算器来计算每条指令的偏移地址,而ORG指令就是用于手动修改当前地址的

    比如

    test segment

       org 100h

    begin: mov ax,1234h

    test ends

    mov ax,1234h 是段内的第一条语句偏移应该是0 ,但是由于ORG 100H的缘故,实际的偏移地址就变成了100H

  

  (4) 当前地址 $

    $表示当前指令的第一个字节的地址

    org $+8 表示表示地址计算器从此处开始向后空8个字节出来

    他也可以用在其他指令中,比如

    jmp  $+ 6 ; 转跳到本条指令之后6个字节处,注意计算地址是JMP指令的开始位置不是结束位置,所以这6个自己包含了JMP本身的长度

    ARRAY 1,2,$+4; 这里的% 表示的是ARRAY的地址不是 $ +4的地址

 

以上是关于汇编学习笔记-伪指令的主要内容,如果未能解决你的问题,请参考以下文章

汇编学习笔记-NASM环境搭建(nasm with vs2017)

汇编入门学习笔记 —— int指令port

汇编学习笔记

汇编语言学习笔记

汇编学习笔记-80x86指令集

汇编入门学习笔记 —— call和ret