Assemblea:MUOVERSI tra due indirizzi di memoria
-
18-09-2019 - |
Domanda
Sto cercando di imparare l'assembly (quindi abbi pazienza) e ricevo un errore di compilazione su questa riga:
mov byte [t_last], [t_cur]
L'errore è
error: invalid combination of opcode and operands
Sospetto che la causa di questo errore sia semplicemente che non è possibile per un'istruzione mov spostarsi tra due indirizzi di memoria, ma mezz'ora di ricerca su Google e non sono stato in grado di confermarlo: è così?
Inoltre, supponendo che io abbia ragione, ciò significa che devo utilizzare un registro come punto intermedio per copiare la memoria:
mov cl, [t_cur]
mov [t_last], cl
Qual è il registro consigliato da utilizzare (o dovrei invece utilizzare lo stack)?
Soluzione
Il sospetto è corretto, non è possibile spostare dalla memoria alla memoria.
Ogni registro di uso generale farà. Ricordate di spingere il registro se non siete sicuri di ciò che è al suo interno e per ripristinare indietro una volta fatto.
Altri suggerimenti
E 'davvero semplice in 16 bit, basta fare il seguente:
push di
push si
push cx
mov cx,(number of bytes to move)
lea di,(destination address)
lea si,(source address)
rep movsb
pop cx
pop si
pop di
Nota: le spinte e pop sono neceessary se è necessario salvare il contenuto dei registri
.C'è anche un comando MOVS di muoversi dati dalla memoria alla memoria:
MOV SI, OFFSET variable1
MOV DI, OFFSET variable2
MOVS
È tecnicamente possibile passare dalla memoria alla memoria.
Provare a utilizzare MOVS (stringa movimento), e l'impostazione [E] SI e [E] DI , a seconda che si desidera trasferire byte (s), parola (s), ecc.
mov si, t_cur ; Load SI with address of 't_cur'
mov di, t_last ; Load DI with address of 't_last'
movsb ; Move byte from [SI] to [DI]
; Some dummy data
t_cur db 0x9a ; DB tells NASM that we want to declare a byte
t_last db 0x7f ; (See above)
Questo è meno efficiente rispetto all'utilizzo di un carico normale + negozio con un registro temporaneo, ma lo fa fare la copia reale con una sola istruzione.
Ecco come MOVS dovrebbe essere utilizzato, e come funziona: https://www.felixcloutier.com/x86/movs:movsb: movsw: movsd: movsq
E 'normalmente utilizzato solo con un prefisso rep
per le copie di blocco, non per un singolo elemento. (CPU moderni hanno microcodice abbastanza efficiente per rep movsb
che vicino alla velocità di un ciclo usando AVX vettore istruzioni load / store.)
Esatto, il codice macchina x86 non può codificare un'istruzione con due esplicito operandi di memoria (indirizzi arbitrari specificati in []
)
- Perché non è consentito lo spostamento da una memoria all'altra?
- Quali istruzioni x86 richiedono due (o più) operandi di memoria?
Qual è il registro consigliato?
Qualsiasi registro che non è necessario salvare/ripristinare.
In tutte le principali convenzioni di chiamata a 32 e 64 bit, EAX, ECX ed EDX vengono bloccati nelle chiamate, quindi AL, CL e DL sono buone scelte.Per una copia in byte o word, in genere si desidera un file movzx
caricare in un registro a 32 bit, quindi in un archivio a 8 o 16 bit.Ciò evita una falsa dipendenza dal vecchio valore del registro.Utilizzare solo un formato stretto a 16 o 8 bit mov
caricare se attivamente Volere per fondersi nei bit bassi di un altro valore.x86 movzx
è l'analogo di istruzioni come ARM ldrb
.
movzx ecx, byte [rdi] ; load CL, zero-extending into RCX
mov [rdi+10], cl
Nella modalità a 64 bit, anche SIL, DIL, r8b, r9b e così via sono ottime scelte, ma richiedono un prefisso REX nel codice macchina per il negozio, quindi c'è un motivo minore di dimensione del codice per evitarli.
In genere evita di scrivere AH, BH, CH o DH per motivi di prestazioni, a meno che tu non abbia letto e compreso i seguenti collegamenti e qualsiasi falsa dipendenza o stallo di fusione di registri parziali non sarà un problema o non si verificherà affatto nel tuo codice .
- Perché GCC non utilizza registri parziali?
- Come si comportano esattamente i registri parziali su Haswell/Skylake?La scrittura di AL sembra avere una falsa dipendenza da RAX e AH è incoerente
(o dovrei invece usare lo stack)?
Prima di tutto, non puoi inviare un singolo byte, quindi non è possibile eseguire un caricamento/memorizzazione di byte dallo stack.Per una parola, dword o qword (a seconda della modalità della CPU), potresti push [src]
/ pop [dst]
, ma è molto più lento della copia tramite un registro.Introduce una latenza aggiuntiva di archiviazione/ricaricamento dell'inoltro dello store prima che i dati possano essere letti dalla destinazione finale e richiede più uops.
A meno che non si trovi da qualche parte nello stack È la destinazione desiderata e non è possibile ottimizzare la variabile locale in un registro, nel qual caso push [src]
va bene copiarlo lì e allocare spazio nello stack per esso.
Vedere https://agner.org/optimize/ e altri collegamenti alle prestazioni x86 il wiki dei tag x86
Voglio solo per discutere di "barriera di memoria" con voi. Nel codice C
a = b;//Take data from b and puts it in a
sarebbe assemblato a
mov %eax, b # suppose %eax is used as the temp
mov a, %eax
Il sistema non può garantire l'atomicità della cessione. Ecco perché abbiamo bisogno di un rmb (Barriera di lettura)