2016-05-29 37 views
6

Sto provando a creare un sottoprogramma in assembly che disegnerà un quadrato sullo schermo. Non penso di poter passare parametri al sottoprogramma come farei in C++, quindi ho pensato che avrei potuto usare lo stack per memorizzare e accedere ai parametri (non posso usare i registri dati comuni perché ci sono troppe variabili per passaggio).assemblaggio x86: passare il parametro a una funzione tramite lo stack

Il problema è (Ricordo di aver letto da qualche parte) che quando uso il comando di chiamata all'indirizzo del "programma" corrente viene salvato nello stack, in modo che quando viene usato il comando "ret" saprebbe dove ritornare. Ma se immagazzino qualcosa nello stack e poi chiamo la funzione, dovrò salvare da qualche parte l'indirizzo (che è in cima allo stack) e quindi inserire in modo sicuro i parametri. Quindi, dopo che il codice è terminato e prima di chiamare "ret", dovrei respingere l'indirizzo.

Ho ragione? E, in caso affermativo, dove posso memorizzare l'indirizzo (non credo che l'indirizzo sia lungo solo 1 byte in modo che possa essere inserito in AX o BX o in qualsiasi altro registro dati). Posso usare IP per fare questo (anche se so che questo è usato per qualcos'altro)?

Questo è quello che immagino:

[BITS 16] 
.... 
main: 
    mov ax,100b 
    push ax 
    call rectangle ;??--pushes on the stack the current address? 

jml $ 

rectangle: 
    pop ax ;??--this is the addres of main right(where the call was made)? 
    pop bx ;??--this is the real 100b, right? 
    .... 
    push ax 
ret ;-uses the address saved in stack 

risposta

8

In genere, si utilizza il puntatore base (bp su 16 bit, ebp su 32 bit) per fare riferimento a parametri e gente del posto.

L'idea di base è che ogni volta che si entra in una funzione si salva il puntatore dello stack all'interno del puntatore di base, per avere il puntatore dello stack quando la funzione è stata chiamata come "punto di riferimento fisso" durante l'esecuzione della funzione. In questo schema, [ebp-something] è in genere locale, [ebp+something] è un parametro.

Trasposizione della tipica a 32 bit, callee-cleanup convenzioni di chiamata che si può fare in questo modo:

chiamante:

push param1 
push param2 
call subroutine 

subroutine:

push bp  ; save old base pointer 
mov bp,sp  ; use the current stack pointer as new base pointer 
; now the situation of the stack is 
; bp+0 => old base pointer 
; bp+2 => return address 
; bp+4 => param2 
; bp+6 => param1 
mov ax,[bp+4] ; that's param2 
mov bx,[bp+6] ; that's param1 
; ... do your stuff, use the stack all you want, 
; just make sure that by when we get here push/pop have balanced out 
pop bp  ; restore old base pointer 
ret 4   ; return, popping the extra 4 bytes of the arguments in the process 
+0

Non è già utilizzato BP nel sottoprogramma per I don; so quale indirizzo? "Il registro BP a 16 bit aiuta principalmente a fare riferimento alle variabili dei parametri passate a una subroutine. L'indirizzo nel registro SS viene combinato con l'offset in BP per ottenere la posizione del parametro. BP può anche essere combinato con DI e SI come registro di base per indirizzamento speciale. ' (da http://www.tutorialspoint.com/assembly_programming/assembly_registers.htm) –

+0

Perché [BP + 6]? So che [] sono usati quando si fa riferimento ad un indirizzo..e se BP è l'indirizzo della subroutine (credo) allora [BP + 6] punterà ad un comando dalla subroutine giusto? (Sono un po 'nuovo quindi posso sbagliarmi ..). E perché 6? (So ​​che +1 significa ad esempio il prossimo indirizzo che può puntare a una var o qualcos'altro.) –

+0

Quindi ..2 significa in realtà 2 byte, giusto? ... saltare oltre 2 byte non un intero indirizzo (anche se +1 significa saltare sopra un intero indirizzo qualunque sia la lunghezza in byte è ..). –

3

Questo potrebbe funzionare, se non che dal punto di vista del chiamante, la tua funzione modifica sp. Nella maggior parte delle convenzioni di chiamata, le funzioni possono modificare solo eax/ecx/edx e devono salvare/ripristinare altri reg se vogliono usarli. Presumo che 16 bit sia simile. (Anche se ovviamente in asm è possibile scrivere le funzioni con qualsiasi convenzione di chiamata personalizzata che ti piace.)

Alcune convenzioni di chiamata prevedono che il destinatario invii gli argomenti inviati dal chiamante, quindi in questo caso funzionerebbe effettivamente. Lo ret 4 nella risposta di Matteo lo fa. (Vedere il wiki tag per informazioni su convenzioni di chiamata, e tonnellate di altri buoni collegamenti.)


E 'super-strano, e non il modo migliore per fare le cose, è per questo che non viene normalmente utilizzato . Il problema più grande è che consente solo l'accesso ai parametri in ordine, non l'accesso casuale. È possibile accedere solo ai primi 6 o più argomenti, poiché i registri non vengono più aperti.

Si lega anche un registro contenente l'indirizzo di ritorno. x86 (prima di x86-64) ha pochissimi registri, quindi questo è davvero cattivo. Potresti spingere l'indirizzo di ritorno dopo aver saltato l'altra funzione nei registri, credo, per liberarlo per l'uso.

jmp ax sarebbe tecnicamente lavorare invece di push/ret, ma questo sconfigge il predittore di ritorno indirizzo, rallentando future ret istruzioni.


Ma comunque, rendendo una cornice stack con push bp/mov bp, sp è universalmente utilizzato in codice a 16 bit perché è economico e ti dà accesso casuale allo stack. ([sp +/- constant] non è una modalità di indirizzamento valido 16 bit (ma è in 32 e 64 bit). ([bp +/- constant] è valido). Poi si può ricaricare da loro ogni volta che è necessario.

In 32 e il codice a 64 bit, è comune per i compilatori utilizzare le modalità di indirizzamento come [esp + 8] o altro, invece di sprecare istruzioni e legare ebp. (è l'impostazione predefinita). Significa che è necessario tenere traccia delle modifiche a esp per calcolare l'offset corretto per gli stessi dati in diverse istruzioni, quindi non è popolare in asm scritto a mano, esp in tutorial/materiale didattico.Nel codice reale si fa ovviamente tutto ciò che è più efficiente, perché se si fosse disposti a sacrificare l'efficienza si userebbe semplicemente un compilatore C.