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?