JavascriptProva

Visualizzazione post con etichetta stack. Mostra tutti i post
Visualizzazione post con etichetta stack. Mostra tutti i post

sabato 21 settembre 2013

Passaggio di parametri tramite stack con indicazione dei parametri nella direttiva PROC.

Ripassati CMP, alcuni salti condizionati, diverse flags, e riscoperta la tecnica di Giobe per ottenere la stampa a video dei codici ASCII dei numeri, mediante la quale egli realizza la procedura Byt2Asc, ho pensato di creare una Byt2Asc nella quale il parametro sia passato mediante lo stack, aggiungendo alla direttiva PROC il parametro.
Ecco la procedura:
public Byt2Asc
text segment byte public
Byt2Asc proc near C,num:WORD
 mov ax,num
 push ax
 shr al,1
 shr al,1
 shr al,1
 shr al,1
 
 cmp al,0AH
 jb X1
 add al,7
X1: add al,30h

 mov ah,0eh
 int 10h
 
 pop ax
 and al,0Fh
 
 cmp al,0AH
 jb X2
 add al,7
X2: add al,30h

 mov ah,0eh
 int 10h

 ret 2
Byt2Asc endp
text ends
end
Per specificare i parametri nella direttiva PROC, bisogna dichiarare anche la "convenzione di chiamata", che può essere C o di altro tipo (la ripasserò in seguito) in quanto la procedura deve conoscere l'ordine nel quale ritirare i parametri dallo stack.
Ho dichiarato C, ma mi sono cominciato a scontrare con una persistente segnalazione di errore, che non riuscivo a capire, di "unresolved external". Poi, ricordando l'associazione di idee di convenzione di chiamata C con l'underscore iniziale, ho capito che il problema era che nel programma chiamante avrei dovuto far precedere il nome Byt2Asc da un underscore, così ho fatto e ho risolto il problema:
extrn Cancella:near
extrn _Byt2Asc:near
extrn KeyWait:near

text segment public

.....
Ho assemblato e linkato il tutto, e funziona.
Dal momento che l'aggiunta di parametri automatizza parte del codice, vediamo qual è il vero codice in DEBUG:
AX=00F4  BX=0000  CX=0450  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=001E   NV UP EI PL NZ NA PO NC
1781:001E 55            PUSH    BP
-p

AX=00F4  BX=0000  CX=0450  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=001F   NV UP EI PL NZ NA PO NC
1781:001F 8BEC          MOV     BP,SP
-p

AX=00F4  BX=0000  CX=0450  DX=0000  SP=03FA  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=0021   NV UP EI PL NZ NA PO NC
1781:0021 8B4604        MOV     AX,[BP+04]                         SS:03FE=00F4
-p

........


AX=0E34  BX=0000  CX=0450  DX=0000  SP=03FA  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=0048   NV UP EI PL NZ NA PO NC
1781:0048 5D            POP     BP
-p

AX=0E34  BX=0000  CX=0450  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=0049   NV UP EI PL NZ NA PO NC
1781:0049 C20200        RET     0002
-p
Ecco: Le istruzioni "automatiche" sono:
PUSH BP
MOV BP,SP

.....

POP BP
Ossia il salvataggio nello stack del Base Pointer, l'attribuzione al Base Pointer del valore di SP, e poi, alla fine, presupponendo che SP sia tornato al valore corrispondente a BP mediante il corretto equilibrio dei pushaggi e dei poppaggi, l'istruzione che riprende il valore precedente di BP.

Veriabili locali e stack

Variabili locali.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 
 call Cancella

 call prc

 call KeyWait
 
 mov ah,4ch
 int 21H
 
prc proc near
 push bp
 mov bp,sp
 
 ;crea la variabile locale
 sub sp,2
 
 mov [bp-2],41h
 mov ah,0eh
 mov al,[bp-2]
 int 10h
 
 ;cancella la variabile locale
 mov sp,bp
 pop bp
 ret 
prc endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
Ecco. Molto semplice.
Una volta create le variabili locali è fondamentale resettare lo SP quando si chiude la procedura riportandolo al valore contenuto in BP.
Quindi la locazione puntata da BP è quella che separa, nello stack, i parametri dalle variabili locali.
Cerchiamo di costruire un programma che usi sia parametri sia variabili locali, in modo da studiarne lo stack.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 
 call Cancella
 
 mov al,'A'

 push ax
 call prc
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
prc proc near
 push bp
 mov bp,sp
 
 ;crea la variabile locale.
 sub sp,2
 mov [bp-2],2
 
 mov ax,[bp+4]
 add ax,[bp-2]
 
 mov ah,0eh
 int 10h
 
 ;cancella la variabile locale
 mov sp,bp
 pop bp
 ret 2
prc endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
Ecco: il parametro passato è la lettera A. La variabile locale è 2.
La procedura aggiunge la variabile locale al parametro ottenendo 42H, ossia la lettera C, che stampa a video.
Funziona:
C
c:\Assembly>

Vediamo un po' come è congegnato lo stack con parametri, variabili locali, indirizzo di ritorno e BP originario salvato:

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

AX=0041  BX=0000  CX=0440  DX=0000  SP=03FA  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 8BEC          MOV     BP,SP
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03FA  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 83EC02        SUB     SP,+02
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03F8  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0016   NV UP EI PL NZ NA PO NC
1781:0016 C746FE0200    MOV     WORD PTR [BP-02],0002              SS:03F8=3302
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03F8  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=001B   NV UP EI PL NZ NA PO NC
1781:001B 8B4604        MOV     AX,[BP+04]                         SS:03FE=0041
-
-d ss:03f0
1785:03F0  FA 03 1E 00 81 17 C4 11-02 00 00 00 09 00 41 00   ..............A.
1785:0400  C2 02 00 00 55 8B EC 57-56 8B 7E 04 39 3E 7C 01   ....U..WV.~.9>|.
1785:0410  75 06 C7 06 7E 01 FF FF-39 3E 80 01 75 05 6A 00   u...~...9>..u.j.
1785:0420  E8 0F 12 6B DF 0E 8B B7-2E 4B C1 E6 02 03 36 B4   ...k.....K....6.
1785:0430  52 EB 18 8B C6 2B 06 B4-52 C1 F8 02 50 E8 66 A1   R....+..R...P.f.
1785:0440  8B 04 C1 E0 02 03 06 B4-52 8B F0 83 3C FF 75 E3   ........R...<.u.
1785:0450  6B DF 0E C7 87 2E 4B FF-FF 33 C0 5E 5F C9 C2 02   k.....K..3.^_...
1785:0460  00 00 55 8B EC 56 8B 5E-04 6B DB 0E 8D 87 2E 4B   ..U..V.^.k.....K
-
Ecco: SP punta la variabile locale, BP punta il suo valore originario salvato nello stack.

Riportare al valore iniziale il puntatore dello stack dopo il passaggio di parametri tramite stack

Nel mio programmino rimane un conto in sospeso con lo stack.
Le due WORD pushate prima di chiamare la procedura, contenenti i parametri, non vengono poi ripoppate o smaltite in qualche modo, cosicchè SP non torna ai valori iniziali fino alla fine del programma.
Seguo in DEBUG i valori prima e dopo la chiamata della procedura Stampa:
AX=0003  BX=0000  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0003   NV UP EI PL NZ NA PO NC
1781:0003 B041          MOV     AL,41
-p

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
-
Il valore di base di SP è 0400.
Con il pushaggio dei parametri diventa poi 03FE e 03FC.

-t

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
-
Con la CALL diventa 03FA perchè viene pushato l'indirizzo di ritorno.

-t

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
-t>
Con il PUSH BP all'interno della procedura diventa 03F8.

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0016   NV UP EI PL NZ NA PO NC
1781:0016 8B5E04        MOV     BX,[BP+04]                         SS:03FC=000C
-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0019   NV UP EI PL NZ NA PO NC
1781:0019 8B4606        MOV     AX,[BP+06]                         SS:03FE=0041
-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0019   NV UP EI PL NZ NA PO NC
1781:0019 8B4606        MOV     AX,[BP+06]                         SS:03FE=0041
-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=001C   NV UP EI PL NZ NA PO NC
1781:001C B409          MOV     AH,09
-t

AX=0941  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=001E   NV UP EI PL NZ NA PO NC
1781:001E B700          MOV     BH,00
-t

AX=0941  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 B90100        MOV     CX,0001
-t

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=0023   NV UP EI PL NZ NA PO NC
1781:0023 CD10          INT     10
-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
E resta tale fino ad ora. Quindi il POP BP lo riporta a 03FA.
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
-


E con il RET, col recupero dell'indirizzo di ritorno, va a 03FC.
-t

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
-


...per poi restare uguale fino alla fine del programma.
-p

AX=1C0D  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=4C0D  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
-
C'è quindi un conto in sospeso con lo stack, che rimane 4 bytes, ossia 2 words, più in basso rispetto a quello che dovrebbe essere.

Come "aggiustarlo" alla fine della procedura?
Come "riassorbire", cioè, i due parametri che erano stati pushati prima della chiamata della procedura?
Mi pare di ricordare che posporre un numero a RET determina un riassestamento di SP.
Controlliamo...

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 4
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 

AX=0941  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 B90100        MOV     CX,0001
-p

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=0023   NV UP EI PL NZ NA PO NC
1781:0023 CD10          INT     10
-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
-t

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 C20400        RET     0004
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=0400  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 E81A00        CALL    0029
-
Ecco. Aggiungendo a RET il numero di bytes che sono stati occupati dai parametri passati, oltre a "rimangiarsi" l'indirizzo di ritorno, si resetta SP 4 bytes più in su, al punto di partenza!

Mi pare che ci fosse anche la possibilità di riportare lo stack al valore di base mediante un'istruzione specifica, ma ciò andrebbe fatto dopo il "rientro" nel programma di base.
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
 
 add sp,4
 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
 
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=0028   NV UP EI PL NZ AC PE NC
1781:0028 5D            POP     BP
-t

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=0029   NV UP EI PL NZ AC PE NC
1781:0029 C3            RET
-t

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 AC PE NC
1781:000C 83C404        ADD     SP,+04
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000F   NV UP EI PL NZ AC PE NC
1781:000F E81800        CALL    002A
-
Ottimo!

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?

Parametri tramite stack: due erroracci che si compensano!

Ecco trattata la procedura Stampa mediante passaggio di parametri per mezzo dello 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 bx
 push ax
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 pop ax
 mov bx,ax
 pop ax
 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
 
Funziona.
Vediamo nel dettaglio cosa accade:
AX=0003  BX=0000  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0003   NV UP EI PL NZ NA PO NC
1781:0003 B041          MOV     AL,41
-p

AX=0041  BX=0000  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-
qui in AL e BL vengono posti rispettivamente il carattere e il colore.
AX=0041  BX=0000  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0007   NV UP EI PL NZ NA PO NC
1781:0007 53            PUSH    BX
-d 1784:03f0
1784:03F0  00 00 00 00 03 00 41 00-00 00 07 00 81 17 C4 11   ......A.........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Prima che i parametri vengano passati tramite stack, ottengo un dump della "cima" dello stack.
Adesso vediamo come cambia dopo il primo push:
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0008   NV UP EI PL NZ NA PO NC
1781:0008 50            PUSH    AX
-d 1784:03f0
1784:03F0  00 00 00 00 41 00 00 00-08 00 81 17 C4 11 0C 00   ....A...........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Ecco: in cima allo stack è stato depositato il colore presente in BH. SP è stato decrementato di 2 (è stata inserita una WORD)
Secondo push:
AX=0041  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-d 1784:03f0
1784:03F0  00 00 41 00 00 00 09 00-81 17 C4 11 41 00 0C 00   ..A.........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
E adesso in cima allo stack è stato depositato anche il carattere (41H, codice ASCII della A), e SP è stato decrementato ancora di 2.

Successivamente, con il CALL (NEAR), viene depositato nello stack l'indirizzo "di ritorno" calcolato:
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-t

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 58            POP     AX
-d1784:03f0
1784:03F0  41 00 00 00 13 00 81 17-C4 11 0C 00 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
E guardacaso, una fortunata (o sfortunata) coincidenza fa sì che casualmente il programma "funzioni"! adesso faccio POP AX:
-p

AX=000C  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BD8          MOV     BX,AX
-d 1784:03f0
1784:03F0  41 00 0C 00 00 00 14 00-81 17 C4 11 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Infatti l'indirizzo di ritorno è 000C, che è uguale alla word pushata che contiene il codice di colore rosso (0CH), e che viene posta in AX per poi essere passata in BX, passando in BL il codice di colore per la procedura Stampa.

Infatti non avevo tenuto conto, nel manipolare lo stack dalla procedura, che bisognava considerare l'indirizzo di ritorno messo in cima allo stack!
Mi meraviglio del fatto che il programma funzioni normalmente, ma forse ho capito perchè. Vado avanti:
-p

AX=000C  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BD8          MOV     BX,AX
-d 1784:03f0
1784:03F0  41 00 0C 00 00 00 14 00-81 17 C4 11 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-p

AX=000C  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0016   NV UP EI PL NZ NA PO NC
1781:0016 58            POP     AX
-d 1784:03f0
1784:03F0  41 00 0C 00 00 00 16 00-81 17 C4 11 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0017   NV UP EI PL NZ NA PO NC
1781:0017 B409          MOV     AH,09
-d 1784:03f0
1784:03F0  41 00 0C 00 41 00 00 00-17 00 81 17 C4 11 0C 00   A...A...........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Il secondo POP AX mette in AX il valore della lettera 41H.
-p

AX=0941  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0019   NV UP EI PL NZ NA PO NC
1781:0019 B700          MOV     BH,00
-p

AX=0941  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=001B   NV UP EI PL NZ NA PO NC
1781:001B B90100        MOV     CX,0001
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=001E   NV UP EI PL NZ NA PO NC
1781:001E CD10          INT     10
-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 C3            RET
-p
E già: ho erroneamente invertito l'ordine dei parametri immessi nello stack! Secondo errore, che ha compensato il primo! Infatti ho pushato prima il colore e dopo la lettera, mentre nella procedura ho poppato prima il colore e dopo la lettera, cosa che se non avessi avuto casualmente l'indirizzo di ritorno contenente esattamente il codice del colore nella parte bassa della WORD avrebbe generato una "stranezza".

Adesso RET riprende quello che nelle mie erronee intenzioni doveva essere il colore, e che coincide con l'indirizzo di ritorno per puro caso, e reindirizza IP nel modo giusto.
-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 C3            RET
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=000C   NV UP EI PL NZ NA PO NC
1781:000C E81200        CALL    0021
-d 1784:03f0
1784:03F0  41 00 0C 00 41 09 41 09-00 00 0C 00 81 17 C4 11   A...A.A.........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
...pronto a chiamare la procedura KeyWait.

Simpatico, questo errore compensato da un altro errore!!!

Così, anziche poppare i parametri dallo stack, ho capito che devo usare istruzioni diverse per poterli richiamare dallo stack nel corpo della procedura, perchè per poppare i parametri non posso fare a meno di poppare anche l'indirizzo di ritorno, creando casini!