根据这个组成的微处理器架构的指令集: https://github.com/mertyildiran/DASM
在我们用C编写的玩具汇编程序中,我们已经实现了PUSH& POP说明如下所示:
PUSH:
这基本上是4 DEC和1 ST指令的组合。
else if (strcmp(token,"push")==0) // PUSH instruction: combination of 4 DEC and 1 ST instruction on Stack Pointer (SP)
{
op1 = strtok(NULL,"\n\t\r ");
op2[0] = sp; // Let's say address of SP is 9
printf("\n\t%s\t%s\n",strupr(token),op1);
ch = (op2[0]-48) | ((op2[0]-48)<<3); // Prepare bitwise instruction format for DEC instructions
program[counter]=0x7800+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
program[counter]=0x7800+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
program[counter]=0x7800+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
program[counter]=0x7800+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
ch = ((op1[0]-48) << 2) | ((op2[0]-48) << 6); // Prepare bitwise instruction format for ST instruction
program[counter]=0x3000+((ch)&0x00ff); // Store the value in Stack
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
}
POP:
这基本上是1 LD和4 INC指令的组合。
else if (strcmp(token,"pop")==0) // POP instruction: combination of 1 LD and 4 INC instructions on Stack Pointer (SP)
{
op1 = strtok(NULL,"\n\t\r ");
op2[0] = sp; // Let's say address of SP is 9
printf("\n\t%s\t%s\n",strupr(token),op1);
ch = (op1[0]-48) | ((op2[0]-48) << 3); // Prepare bitwise instruction format for LD instruction
program[counter]=0x2000+((ch)&0x00ff); // Store the value in Stack
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
ch = (op2[0]-48) | ((op2[0]-48)<<3); // Prepare bitwise instruction format for INC instructions
program[counter]=0x7700+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
program[counter]=0x7700+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
program[counter]=0x7700+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
program[counter]=0x7700+((ch)&0x00ff); // Decrease Stack Pointer 4 times
printf("> %d\t%04x\n",counter,program[counter]);
counter++;
}
所以我的问题是我们如何实现CALL&amp;使用堆栈的RET指令?
我知道CALL指令会将PC的当前状态存储在堆栈中,这样程序就可以使用RET指令返回它所在的位置。但它引导我两个子问题:
如果你想看看整个图片:https://github.com/mertyildiran/DASM/blob/master/assembler.c
对于2. subquestion(JMP / CALL),您可以通过此示例解释它,可能jmp lpp
行:
.data
count: 60
array: .space 10
char: 0xfe
.code
ldi 0 count
ld 0 0
ldi 1 array
ldi 2 char
ld 2 2
lpp st 1 2
inc 1
dec 0
jz loop
jmp lpp
loop sub 1 2 3
lp1 jmp lp1
答案 0 :(得分:3)
&#34;汇编程序如何将标签地址传递给JMP指令?&#34;
从机器/ CPU架构中可以明显看出它是如何解码指令的。我对你的问题感到困惑,是否已经将最终的CPU作为最终版本(然后它应该描述,如何加载pc
寄存器=这基本上是什么跳转,加上它可能是有条件的),或者你在一个中做两个项目,同时创建硬件机器规格和汇编程序。而且我懒得再读它,所以我只会告诉你现实世界的常用方法。
在16b(拇指)和32b模式下,操作码的前几位指定指令(B, BL, BLX
,B是纯跳转,BL类似于CALL,但不使用堆栈,&#34;链接& #34;寄存器用于存储返回地址),其余位指定寄存器保存目标地址(因此调用函数foo
可以执行load r0,foo
bl r0
),或者立即值,相对于当前PC
。
这意味着,在16b模式下,您可以无条件地跳转+ -2kiB,或者有条件地跳转-252
到+258
(条件变量在其他位中进行编码,从中立即取出一些)。< / p>
这有时会导致高级编译器使用寄存器变体,或者跳到足够接近另一个跳跃指令的情况,这种情况会进一步跳跃。
在32b模式下,为立即保留的剩余位为您提供更好的范围,对于所有变体约为+ -32MiB。 (还有一个模式,32b&#34;拇指&#34;它有一个不同的编码,但在这个例子中无关紧要。)
有趣的是,地址中的位0确实指定指令是否处于拇指模式或全32b模式,因为所有地址必须在ARM上对齐,因此跳转到地址0x00000001会通过CPU切换跳转到0x00000000到拇指模式。
这个有几乎所有可能的变体(除了那个你真正需要的那个。你在过去3个月写过的。)指令编码具有可变长度,因此指令使用尽可能多的字节,由英特尔决定是否需要。
jmp
操作码字节,然后根据需要编码地址的字节数)jmp
编码,如ARM案例,但具体的操作码)loop
,jcxz/jecxz
等额外内容,甚至还有一些我甚至忘了。因此可以归结为将值加载到某个寄存器中,或者将立即编码到指令操作码中,然后将其视为绝对地址(例如,在32b平台上,您设法为地址编码25位) ,允许你处理32MiB的RAM;而不是4GiB),或作为相对地址(25bits =&gt; pc
+ - 16Mi)。
附录,这可能是你问题的一部分? 喜欢如何&#34;知道&#34;什么地址将在未来的标签上?
大多数汇编程序是双遍的,因此它们首先生成指令操作码(并且可以计算源的每个部分的长度),并收集符号表中的所有符号,然后根据操作码定义所有地址符号长度和org
指令。然后在第二遍中,用特定的符号值填充操作码中的所有立即值。
这也表明,为什么双程组装者不能自动处理jmp rel8/rel16
(由8b或16b立即定义的相对跳跃),但程序员必须指定他想要使用哪一个。 (或者使用多次传递汇编程序,它将首先尝试rel8
,当失败时,它将使用rel16
重新编码jmp并移动+重新编译超出该点的所有内容。)
查看push
编码的示例来源,我觉得有一些工作可以帮助您改变汇编程序的工作方式......(无论如何,它都很丑陋,就像创建一样操作码和屏幕上的打印输出 - 不要再犹豫再写一次,我敢打赌它会变得更干净,更简单。)
编辑:最后,我可能更好地理解那个汇编程序。
所以program
是uint16_t[]
,对吧? (应该是问题的一部分)。
指令机器代码也是固定大小,16b。
但是你的counter
计算16b字......那么目标架构的寻址方式是什么?
地址0
存储16b字,地址1
存储下一个16b字? (并且它没有重叠,如在x86,其中地址以8b字节计数=&gt;在这种情况下,第一个指令字将位于地址0
和1
,第二个字将是在地址2
和3
。确保您的counter
遵循正确的寻址方案(或者在定义标签符号的值时必须转换它)。
你的asm正在产生类似0x0000 0x7802
的输出:地址0,字7802,用params 2做一些指令78(不清楚的字节顺序!要么你很幸运在编译主机上有相同的一个,或者你会以错误的机器代码结束,每个单词中包含交换的字节),下一个操作码0x0001 0x7803
,......等......
所以看起来你已经修复了16b大小的指令编码,不确定jmp
是否会像存储/加载指令一样吃掉整个8b,或者那个是特殊的,保留更多的位用于立即编码。如果只有其他字节可用,则只有有意义的使用方式是签名8b -128..+127
相对跳转。
如果寻址适用于16b字,那么你可以有效地向后/向前跳转-128 .. + 127条指令。如果寻址适用于8b字节,则范围仅为-64 .. + 63指令。很难说,因为我没有弄清楚目标平台的任何细节(你应该添加一些链接或其他东西,至少是如何编码jmp
以及如何映射内存)。