簡體   English   中英

EMU8086普斯塔波帕

[英]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

pushapush 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 sppop 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM