1 先说对变量、标签的使用和命名
汇编中有几种对地址标签的分法,算是在概念上的分类,方便大家的理解。
分别为:
- LABEL
- 变量名
LABEL:
意思是我指向的就是一大块的首地址,使用方法:LABEL_NAME:(注意后面要加冒号)
命名方式,尽量使用Java中的类似于包的命名方式,比如LABEL_SEG_CODE16,LABEL_SEG_CODE32,LABEL_DESC_STACK。
汇编中的LABEL还有一种叫做,局部LABEL,是以(点)开头,比如.begin,.1,.2。在不同的段中可以使用相同的带点开头的LABEL,不会重复。
并且经过实验,最好只用在代码段中,数据段不好使。
变量名:
变量名分为函数变量名和普通变量名。
- 函数变量名:
指的是一个SUB_RUTIN的首地址。比如DispStr,使用的时候是call DispStr,然后在声明时候因为它本来就是一个地址,就是用上面标签的同样的声明方式。DispStr:(注意后面有冒号)
- 普通变量名:
分为2种形式构成的变量名,
第一种使用equ生成的纯的变量,貌似是宏,比如:TopOfStack equ $ - LABEL_STACK - 1,命名方式为首字母大写(也不一定,如果刻意代表DataSeg的存储空间,)。
第二种使用db dd dw生成的变量,其实就是一个地址。生成的各种变量分别指向的是不同的数据类型,那么命名时候前几个字母尽量写清楚类型。
比如如下的命名:(特别是数据段会有这种命名方式的用法,冒号可加可不加)
_dwDispPos: dd (80 * 6 + 0) * 2
_wSPValueInRealMode dw 0
_szRAMSize db "RAM size:", 0 - 补充:db 代码下面我所输入的这一串字符,每一个字符占一个字节。
2 依次对各个问题进行分析,首先为何在16位代码段中会有:mov dword [_dwMCRNumber], 0-----------这个dword在这里的必要性是什么?
答:16位代码段中默认[_dwMCRNumber]这个地址,指向的是一个2个字节1个字,也就是说上面的那个指令,如果没有dword,只会改变内存中的2个字节赋值为0,现在加上dword之后,就会改变4个字节了,相应的word,byte也是这个作用。当然在32位代码段中,默认是改变4个字节的。
3 变量在32位中和16位中所占字节的区别?
答:16位代码段中默认该变量是占2字节,(不论该变量是一个地址还是一个值)
4 mov同一个地址在mov ax,[100]与mov eax,[100]的不同之处?
答:这里不论是在32位代码段还是16位代码段中,虽然100指向的是同一处地址,但是因为mov到寄存器分别是16位的ax和32位的eax,那么最后第一个命令中只会改变ax的16位,第一个命令中会改变eax的整个32位,也就是说在这两个命令中[100]所指向的数据是不一样的,第一个命令中是2字节的,第二个命令中是4字节的。
5 在汇编中不能出现相同的LABEL,否则汇编时候会出现redefine的错误。
6 在汇编中[BITS 16]和[BITS 32]各代表什么意思?
答:[BITS 16]代表下面这个段是按照16位进行编译的,代码地址(比如一个label)都是16位的(2字节的)。[BITS 32]代表下面的地址都是32位的。
7 ALIGN 32在每个代码段首处是什么意思?
ALIGN是数据对齐的意思,就是说将这个代码段的段首地址对齐到32字节处,以前学过对齐到4字节,处理起来访问内存速度会比较快。这里32不知道什么用意,不过反正访问速度变快了。
8 32位中的代码段中的label标签是偏移量还是实地址?
答:这个是不一定的。按理说所有的这些label标签都应该是地址,如果是16位代码就是16位地址,32位代码就是32位地址。但是在使用的时候会发生一些差异,
- 比如在数据段中的标签,全部都是实模式中的地址,并且在使用这些地址的时候,一般都是 mov [ds:addr], ax等等的用法,所以如果是实模式,它可以是编译时产生的纯地址,而如果是保护模式我们希望他是相对数据段首地址的偏移量,所以一般要重新计算一个地址偏移量offset给保护模式使用。
- 如果是函数段的标签地址,如果是在同一个段内调用的话,那就是NASM汇编器会自动计算出偏移量,直接call到偏移量那去了,比如call .3(看过反编译的代码的).如果不是同一个保护模式的段,那么:经过分析,貌似当使用call这种命令的时候,函数地址都会被汇编成离段首的偏移量。这样就可以放心大胆的调用某个段的函数地址了。
9 ret retf iref的不同是什么?
答:
- ret 弹出一个参数,给ip,返回
- retf 弹出2个参数,一个给 ip,一个给 cs
- iref 弹出 3个参数,一个给 ip,一个 给 cs ,一个 个 flag标志位
他们都是返回调用点的,看你调用的时候,用的什么调用的,是 call 段内转移 ,还是call 段间转移,还是int 调用中断
10 假如段A中有个地址LABEL_FUNC_A,段B中有个地址LABEL_FUNC_B,那么在函数保护模式和在实模式下分别该如何被调用?
答:
- 同一个段内:
- 使用call:保护模式下,实模式下均可直接call label。
- 使用jmp:保护模式下,实模式下均可直接jmp label。
- 段间的调用:
- 使用jmp:实模式下可直接cs:label(大部分实模式代码都在同一个一个cs下),保护模式下因为cs中存储的是selector,语法为:cs:offset。
- 使用call:实模式下可直接cs:label(大部分实模式代码都在同一个一个cs下),保护模式下因为cs中存储的是selector,语法为:cs:offset。
11 为什么gdt表中的段基地址非要等到代码初始化的时候才能填入?
答:
因为平时我们只知道实模式下的LABEL_SEG,其实是实模式下的概念cs段的偏移量,实模式下cs,ds,es,ss神马的都在一个段内(至少我现在写的小代码是这个样子),需要在运行的时候将ds与LABEL_SEG相合并形成真正的地址,再赋值到selector中的段基地址处。
12 在写代码的时候,因为好多int中断都会使用到es,所以不能保证在函数中es永远等于自己想要的ds,必须显示的赋值。
答:es在使用的时候,必须确定它的值是否一定是当前想要的段地址。