JavascriptProva

venerdì 20 settembre 2013

Uso di MOV per recuperare i parametri dallo stack

Bene.
Anzichè con il POP, posso prendere i parametri pushati nello stack mediante l'istruzione MOV.
Ho provato (memoria corta!) a porre questi valori in un qualunque registro con l'istruzione MOV, prendendo come riferimento la locazione puntata dal registro SP, secondo il ragionamento che sommando 2 a questo valore si salta l'indirizzo di ritorno e si prende l'ultimo parametro pushato, poi sommando 4 si va a prendere il primo parametro
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov bx,[sp+2]
 mov ax,[sp+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
E ottengo un errore dall'assemblatore:
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm
uno.asm(24) : error A2031: must be index or base register
uno.asm(25) : error A2031: must be index or base register

c:\Assembly>

Andiamo a rivedere i registri.
Ecco: registri puntatore e registri indice

Questo significa che per questo scopo, ossia per trasferire dati prelevati dallo stack, si possono usare solo BP, DI e SI.
Allora, invece di usare BP proviamo a usare DI o SI.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov si,sp
 mov bx,[si+2]
 mov ax,[si+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>
Ancora, con DI:
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov di,sp
 mov bx,[di+2]
 mov ax,[di+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>
Sì, con questi non mi dà errore.

Comunque, siccome classicamente il registro deputato a questa funzione è il BP, seguiamo la via "ortodossa":
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov bp,sp
 mov bx,[bp+2]
 mov ax,[bp+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>
Bene. Adesso assembliamo, linkiamo e vediamo come funziona.
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>link uno

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.

Run File [uno.exe]:
List File [nul.map]:
Libraries [.lib]: libreria
Definitions File [nul.def]:

c:\Assembly>
A
c:\Assembly>
Ottimo.

Non utilizzare SP è fondamentale in modo da poter continuare a usare lo stack anche nel contesto della procedura, ovvio!
Un ulteriore passaggio: BP potrebbe essere "occupato" con un valore importante, quindi per non perderlo lo pushiamo nello stack prima di usarlo come puntatore dei parametri.
Ovviamente, nel puntare i parametri bisogna aggiungere 2 in più a BP perchè oltre all'indirizzo di ritorno va saltato anche il valore precedente di BP pushato per ultimo nello stack.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 push bp
 mov bp,sp
 mov bx,[bp+4]
 mov ax,[bp+6]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 


Adesso "sistemiamo" anche il recupero dello stack...
Intanto assemblo e linko per poi seguirmi tutte le istruzioni con -t e -p in debug. Non c'è bisogno di fare la "dimostrazione pratica" dell'errore, perchè incontro una finestra di messaggio secondo cui NTVDM ha incontrato un errore.

Ovviamente, lo stack non perdona! Finchè l'ultimo valore pushato è l'indirizzo di ritorno, con l'istruzione RET esso viene poppato e usato per far "rientrare" il flusso nel programma principale, ma adesso se non ripoppo il valore in BP questo viene ripoppato dall'istruzione RET e scambiato per l'indirizzo di ritorno, dirottando il programma vattelappesca dove!
Rimedio:
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 push bp
 mov bp,sp
 mov bx,[bp+4]
 mov ax,[bp+6]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 pop bp
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
assemblando e linkando...
A
c:\Assembly>
Perfetto... per il momento.
Ma andiamo a seguire in debug: c'è qualcosa che non mi torna: sono stati pushati due valori che non vengono poppati...
AX=0041  BX=0000  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0007   NV UP EI PL NZ NA PO NC
1781:0007 50            PUSH    AX
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0008   NV UP EI PL NZ NA PO NC
1781:0008 53            PUSH    BX
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-t
Ecco, con il "pushaggio" di AX e BX lo Stack Pointer è giustamente arretrato di 4 bytes.

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 55            PUSH    BP
-p
e adesso è andato ancora più indietro, a 03FAH, per il "pushaggio" dell'indirizzo di ritorno.

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BEC          MOV     BP,SP
-p
E ancora più indietro, in quanto nel contesto della procedura viene pushato anche BP.

Seguiamo lo svolgersi della procedura...
.....

-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0025   NV UP EI PL NZ NA PO NC
1781:0025 5D            POP     BP
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0026   NV UP EI PL NZ NA PO NC
1781:0026 C3            RET
-p
Ecco, con il "poppaggio" di BP per restituire a questo registro il suo valore originario, SP è "risalito" a 03FAH.

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000C   NV UP EI PL NZ NA PO NC
1781:000C E81800        CALL    0027
-p
...e con il "poppaggio" dell'indirizzo di ritorno ad opera dell'istruzione RET SP viene aumentato ancora di 2, andando a 03FCH.

Continuiamo a seguire il programma...
AX=1970  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000F   NV UP EI PL NZ NA PO NC
1781:000F B44C          MOV     AH,4C
-
-p

AX=4C70  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0011   NV UP EI PL NZ NA PO NC
1781:0011 CD21          INT     21
-p

Program terminated normally
-
...laddove c'è un conto in sospeso con lo stack!!!

Come si fa a pareggiare i conti con lo stack?

Nessun commento:

Posta un commento