学习,了解汇编语言的工作机制和编写流程,有助于我们对程序的执行有一个比较清晰的认识。
主要的过程就是寻找能够存放数据的内存地址,将数据存放至这个地址之上,取出内存中数据至寄存器,寄存器经过计算后写回内存。
需要清楚的是,调用者知道被调用的函数有几个参数,调用者分配内存空间,而只有被调用的函数知道其内部有几个局部变量,函数分配内存空间。
还需要清楚的是,局部变量的入栈顺序依次为由高到低,而函数参数在内存中的分配(第一个参数总是在最低地址处),即由低至高分配,这样做的好处是,当参数为变长参数时,其栈指针总能指向函数的第一个参数。
以下面的代码为例,我们写出相应的汇编语句,注意,在这里我们假设每一条汇编指令的大小均为4个字节。最终,根据指令集,将汇编语句翻译成计算机能够识别的机器码。
int main() {
int i = 4;
foo(i, &i);
return 0;
}
void foo(int bar, int *baz) {
char snink[4];
short * why;
why = (short *) (snink + 2);
*why = 50;
}
SP: STACK POINTER
SP = SP - 4; // 内存是 由高向低进行扩展的,故需要减去4,来存放int 4,(设定能够存放4 的内存位置)
M[SP] = 4; // 将其内存位置赋值
SP = SP - 8; // i, &i, 一共8个字节
R1 = M[SP + 8]; // 取出i 值
R2 = SP + 8; // 取出i 地址
M[SP] = R1; // 给相应的内存赋值
M[SP + 4] = R2;
CALL <foo>;
SP = SP + 8; // 栈指针返回
RV = 0; // 返回0
<foo> : SP = SP - 8; // 两个局部变量
R1 = SP + 6; // char char char char 4bytes short * 4bytes snink + 2 = short * + 2 个char 的位置(6)
M[SP] = R1; // why 的地址改变
R1 = M[SP]; // 取地址
M[R1] = .2 50; // 解引用 .2表示2个字节(short), 相应的地址 赋相应的值
SP = SP + 8; // 回收局部变量
RET; // 返回
附:一个递归函数的递归写法:
int fact(int a) {
if (n == 0) return 1;
return n * fact(n - 1);
}
SP 的位置为当前函数在栈中的位置,取得参数故需向高地址寻找,(总能从第一个参数开始寻找)
<fact>:
R1 = M[SP + 4];
BNE R1,0, PC + 12; // 12 跳跃三条汇编指令(每条汇编指令假设为4个字节)
RV = 1;
RET;
R1 = M[SP + 4];
R1 = R1 - 1;
SP = SP - 4;
M[SP] = R1;
CALL <fact>; // 使得SP向下延伸4个字节,将这个指令存储至saved PC中
SP = SP + 4;
R1 = M[SP + 4];
RV = RV * R1;
RET;