根據這個虛構的微處理器架構的指令集: https://github.com/mertyildiran/DASM如何在彙編器中實現CALL&RET?
在我們用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 & RET指令?
我知道CALL指令會將PC的當前狀態存儲在堆棧中,這樣程序就能夠通過RET指令返回它離開的地方。但它導致了我兩個子問題:
- CALL後執行,程序如何獲得以前存儲在堆棧地址,如果在子過程,指令壓入一些東西到堆棧或覆蓋調用的返回地址。
- 如何將相關標籤的地址傳遞給機器碼級的CALL?在我們的彙編程序JMP &中,由於相同的原因,jZ指令也未完成。
如果你想看看全貌:
.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
「如果在子程序中,程序如何獲取先前存儲在堆棧中的地址,則指令將某些東西壓入堆棧或覆蓋CALL的返回地址。 ... CPU不關心,下一個「RET」將返回到錯誤的地址(當調用ret時,堆棧前面的新/修改值)。 – Ped7g
@ Ped7g那麼這將是彙編程序的錯誤,而不是彙編程序的錯誤,對吧? – mertyildiran
'call label'例如可以分解爲'push next_instruction'' jmp label'' next_instruction:...'('next_instruction:'是彙編程序內部使用的虛構隱式標籤,實際上你可以做'$ + size_of_call '沒有定義符號)...是的,程序負責保持棧值有意義,就像你必須在寄存器中保存有意義的值並執行指令一樣。 CPU就是這樣做的,它告訴了什麼。所以在子例程中,如果你在堆棧上「推」三個字,在調用ret之前,你應該彈出三個字的大小,或者添加sp,3 *字。 – Ped7g