[英]Emu8086 pusha popa
我在尋找匯編程序中pusha popa的簡單方法。 我想使用emu8086在程序中查找錯誤,因此不允許.286。 我試過了:
push_a proc
push ax
push cx
push dx
push bx
push sp
push bp
push si
push di
push_a endp
pop_a proc
pop di
pop si
pop bp
pop sp
pop bx
pop dx
pop cx
pop ax
pop_a endp
但這是行不通的,因此,當我們“調用push_a”時,會將我們現在所在的地址壓入堆棧。 還有另一種簡單的方法嗎? 我不想每次都寫8個push和8個pop。
可以將程序實現為宏
pusha MACRO
push ax
push cx
push dx
push bx
push sp
push bp
push si
push di
pusha ENDM
popa MACRO
pop di
pop si
pop bp
pop sp
pop bx
pop dx
pop cx
pop ax
popa ENDM
但這不是pusha
/ popa
的體系結構行為,並且在真實的8086上是損壞的代碼。
pusha
的真正pusha
pusha
的push sp
應該在pusha
宏的開始處推送堆棧指針的狀態。
溫度←(SP);
推(AX);
推(CX);
推(DX);
推(BX);
推(溫度);
推(BP);
推(SI);
推(DI);用於
pusha
英特爾偽代碼-英特爾手冊2B
使用暫存位置
如果您有暫存內存位置,則可以執行類似的操作:
pusha MACRO
mov WORD PTR [pusha_scratch], sp
push ax
push cx
push dx
push bx
push WORD PTR [pusha_scratch]
push bp
push si
push di
pusha ENDM
確保在任何情況下都可以通過DS
訪問pusha_scratch
可能是一件很努力的事情。
不考慮pusha_scratch
的單獨定義。
使用與位置無關的就地版本
或者,可以用與位置無關並就地操作的某種方式說明sp
的修改值。
如果我正確地進行了數學運算-請仔細檢查-該代碼可以解決問題
pusha MACRO
push ax
push cx
push dx
push bx
push bp ;Top of stack = BP
mov bp, sp ;BP points to TOS
lea bp, [bp+0ah] ;BP is equal to the starting value of SP
xchg bp, [bp-0ah] ;BP = Original BP, [SP] = Starting value of BP
push bp
push si
push di
pusha ENDM
非常感謝Fifoernik通過注意反演數學和防止EFLAGS寄存器更改來糾正破損的代碼。
上面的代碼片段的好處是,它避免完全使用push sp
因為在向后兼容方面存在一些問題。
push sp
的問題 在實際的8086上,指令push sp
行為不同:
P6系列,奔騰,Intel486,Intel386和Intel 286處理器在堆棧中為
PUSH SP
指令推送的值與8086處理器不同。
32位處理器在將SP寄存器的值遞減之前,先將其值遞減,然后再進行推送操作。
8086處理器遞減后將SP寄存器的值壓入 。英特爾兼容性部分-英特爾手冊3-22.17
我不知道emu8086作為8086的仿真器有多准確,但我會避免使用push sp
。
popa
的語義 實施pusha
的特殊方法需要對popa
的特殊實現。
在實際的8086上,一旦執行pop sp
,就會突然pop
的幼稚列表。
實際上,CPU利用了即使在pusha
之后也無法真正修改sp
的事實-否則將無法取回這些值-並注意到在所有pop
之后所有原始值都被隱式恢復。
DI←Pop();
SI←Pop();
BP←Pop();
ESP增加2; (*跳過堆棧的下2個字節*)
BX←Pop();
DX←Pop();
CX←Pop();
AX←Pop();英特爾針對
popa
偽代碼-英特爾手冊2B
因此,更正確地執行popa
是
popa MACRO
pop di
pop si
pop bp
add sp, 02h ;Beware, this affects EFLAGS
pop bx
pop dx
pop cx
pop ax
popa ENDM
請注意 ,您必須避免pop sp
在一個真實的8086作為push sp
后跟一個pop sp
不冪等,對
POP ESP
指令在將堆棧頂部舊數據寫入目標之前,將堆棧指針(ESP)遞增。
pop
Intel說明-英特爾手冊2B
最后,人們希望pusha
/ popa
對於中斷而言是原子的,因此在宏體周圍需要一個cli
/ sti
對。
如果您使用pusha
/ popa
作為保存所有寄存器的簡寫,則可以跳過所有麻煩,並且有足夠的實現方法。
push_all MACRO
push ax
push cx
push dx
push bx
;NOTE: Missing SP
push bp
push si
push di
push_all ENDM
pop_all MACRO
pop di
pop si
pop bp
;NOTE: Missing SP
pop bx
pop dx
pop cx
pop ax
pop_all ENDM
pushuj MACRO
push ax
push cx
push dx
push bx
push sp
push bp
push si
push di
ENDM
當我想調用它時,我寫道:
pushuj
我好嗎?
我在尋找匯編程序中pusha popa的簡單方法。
和
我不想每次都寫8個push和8個pop。
這就是使用建議的MACRO解決方案時將要執行的操作! 每次使用宏的名稱時,它代表的整個代碼塊都會被替換。 這確實將浪費空間。
盡管@MargaretBloom的答案很好地解釋了push sp
和pop sp
所有問題,但它並沒有為您提供最簡單的解決方案。 此外,在保存和恢復所有通用寄存器的情況下,推入和彈出堆棧指針是一個愚蠢的操作。 甚至制造商(Intel)都知道這一點,並在執行popa
時巧妙地繞過了推入的SP
值。 SP
寄存器僅由pusha
推動,因為它簡化了硬件設計。
然后,滿足要求的最佳解決方案在推送/彈出7個有用的GPR之前,暫時從call push_a
/ call pop_a
刪除返回地址。 直接在執行ret
之前,將返回地址放回堆棧中。 Voilà。
push_a proc
pop word ptr [TEMP]
push ax
push cx
push dx
push bx
push bp
push si
push di
push word ptr [TEMP]
push_a endp
pop_a proc
pop word ptr [TEMP]
pop di
pop si
pop bp
pop bx
pop dx
pop cx
pop ax
push word ptr [TEMP]
pop_a endp
此解決方案也不會更改其操作的任何標志。 由@Fifoernik觀察。
這是一個主意:為什么不將FLAGS保留為第八寄存器?
最后一點。 為了使此代碼真正可靠,可以將TEMP變量放在程序的代碼段中,並使用顯式CS:
override前綴解決。 當您需要使用新的push_a / pop_a調用時,您的DS
段寄存器可能並不總是位於正確的位置。 您的CS
寄存器本質上是正確的。 如果不是,則該call
甚至無法運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.