Le

Le basi in x86-64 Assembly

1 esercizio

Informazioni su Le basi

Commenti

Il track x86-64 in Exercism usa NASM (The Netwide Assembler) come assembler. In NASM, i commenti sono preceduti da un punto e virgola (;).

I commenti possono essere inseriti ovunque nel programma e tutto ciò che segue un ; viene ignorato dall'assembler.

Costanti

Una costante valutata al momento dell'assemblaggio può essere definita in NASM usando equ. Ad esempio, questo definisce una costante chiamata UNIVERSE con il valore 42:

UNIVERSE equ 42

Le costanti vengono valutate una sola volta, al momento della definizione, e non possono essere ridefinite in seguito.

Registri di uso generale (GPR)

In assembly, al posto delle variabili, di solito usiamo i registri per memorizzare i valori. Un registro della CPU è una porzione di memoria veloce all'interno del processore di un computer. La maggior parte dei calcoli viene eseguita nei registri della CPU.

Alcuni registri sono usati per una varietà di calcoli diversi e sono chiamati registri di uso generale (GPR). Altri hanno scopi speciali o dedicati.

In x86-64, ci sono 16 registri di uso generale (GPR) a 64 bit, a cui si può accedere anche come 32 bit, 16 bit o 8 bit. I GPR sono descritti di seguito, dove n in rn va da 8 a 15: r8, r9, r10, r11, r12, r13, r14 e r15.

64-bit 32-bit 16-bit 8-bit
rax eax ax ah/al
rbx ebx bx bh/bl
rcx ecx cx ch/cl
rdx edx dx dh/dl
rsi esi si sil
rdi edi di dil
rbp ebp bp bpl
rsp esp sp spl
rn rnd rnw rnb

Quando si usa meno di 64 bit, i bit a cui si accede sono di solito dalla parte inferiore del registro. Fanno eccezione ah, bh, ch e dh, che accedono agli 8 bit superiori della porzione a 16 bit del registro.

Illustrazione di come si accede ai bit per il registro rax:

+--------+---------------------------------------+
| 64-bit |                  rax                  |
+--------+-------------------+-------------------+
| 32-bit |                   |        eax        |
+--------+-------------------+---------+---------+
| 16-bit |                             |    ax   |
+--------+-----------------------------+----+----+
| 8-bit  |                             | ah | al |
+--------+-----------------------------+----+----+

Alcuni di questi registri devono essere preservati attraverso le chiamate di funzione: rbp, rsp, rbx, r12, r13, r14 e r15. Non preservarli può portare a un errore o a un comportamento indefinito.

Gli altri non vengono preservati e possono essere usati liberamente: rax, rcx, rdx, rdi, rsi, r8, r9, r10 e r11.

Note

I registri vengono di solito preservati sullo stack usando l'istruzione push. Vengono poi ripristinati prima della fine della funzione usando l'istruzione pop.

Istruzioni

Le istruzioni sono operazioni che una CPU può eseguire. Di solito hanno la seguente forma:

name destination, source

Quindi, il nome dell'istruzione viene messo per primo, poi almeno uno spazio bianco, seguito dall'operando di destinazione, una virgola (,) e infine un operando sorgente. L'operando sorgente di solito non viene modificato da un'istruzione, solo l'operando di destinazione.

Entrambi gli operandi devono avere la stessa dimensione, salvo diversa indicazione. Ad esempio, l'operando di destinazione deve avere anch'esso 16 bit se l'operando sorgente ha 16 bit.

Ad esempio, per memorizzare un valore in un registro, possiamo usare l'istruzione mov:

mov rax, rdx  ; rax = rdx
              ; mov is the name, rax is the destination operand and rdx is the source operand

Lo snippet sopra copia il contenuto di tutti i 64 bit di rdx, l'operando sorgente, in tutti i 64 bit di rax, che è l'operando di destinazione per l'istruzione.

È possibile usare numeri interi costanti come operandi sorgente (ma non come operandi di destinazione) nella maggior parte delle istruzioni:

mov rcx, 42 ; rcx = 42
            ; rcx is the destination operand and 42 is the source operand

Nota che scrivere in un registro a 32 bit azzera anche i bit superiori, quindi mov eax, 42 equivale a mov rax, 42. Questo non vale per i registri a 16 bit e a 8 bit.

Per le operazioni aritmetiche di addizione, sottrazione e moltiplicazione, possiamo usare le istruzioni add, sub e imul:

add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8

Funzioni

Le istruzioni sono organizzate in funzioni. Tutte le funzioni sono collocate nella section .text del file sorgente.

La definizione di una funzione consiste in:

  1. Un'etichetta con il nome della funzione, seguita da :.
  2. Le istruzioni che definiscono la funzione.
  3. L'istruzione di ritorno, ret.

Ci sono convenzioni specifiche su come gli argomenti vengono passati a una funzione e restituiti da essa. Le due principali convenzioni di chiamata sono la System V AMD64 ABI e la Microsoft x64.

Questo track usa la convenzione di chiamata System V AMD64 ABI e i primi sei argomenti interi vengono passati a una funzione nei registri. Vengono passati nel seguente ordine: rdi, rsi, rdx, rcx, r8, e r9.

Un valore intero viene restituito dalla funzione nel registro rax.

Per chiamare una funzione, usiamo l'istruzione call. Una funzione può essere chiamata ovunque nello stesso file sorgente. Per rendere una funzione visibile ad altri file sorgente, bisogna usare la direttiva global.

Ad esempio, questo definisce una funzione sum:

section .text ; functions are placed here

global sum ; sum is visible to other source files

sum:
    ; first argument is passed in rdi
    ; second argument is passed in rsi
    ; return value is placed in rax

    mov rax, rdi ; rax is now equal to rdi
    add rax, rsi ; rax = rax + rsi

    ret ; function return

E questo chiama la nostra funzione sum con gli argomenti 3 e 5:

mov rdi, 3  ; First argument in rdi
mov rsi, 5  ; Second argument in rsi
call sum

; The rax register now contains the value 8 (3 + 5), after sum returns
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Le basi