Registri

i registri sono “parti di memoria” molto vicine alla CPU , infatti la CPU accede a questi registri molto velocemente , come possiamo notare ci sono 8 zeri , ogni zero rappresenta un valore hex . Ora siccome un valore hex è rappresentato da 4 bit avremo bit in un solo registro , [ infatti questo è un processore a 32 bit ] , quindi in un registro possiamo memorizzare 32 bit. Questa grandezza si chiama word , infatti per esempio se abbiamo una macchina a 64-bit allora una word sarà di 64 bit.
Tutti questi registri sono a nostra disposizione a eccezione di alcuni che hanno un ruolo specifico , per esempio il registro r7 ha il ruolo di System Calls , in pratica serve a “parlare” con il sistema quando il nostro programma è in esecuzione , magari gli serve una risorsa o fermare l’esecuzione. Al sistema parliamo settando il registro r7 con alcuni valori , ogni valore rappresenta una roba diversa , per esempio se settiamo a 1 allora vorrà dire che vogliamo fermare l’esecuzione del programma.
Altri registri con ruolo specifico :
sp: stack pointer , che ci dice il prossimo “pezzo” di memoria disponibile nello stack [ in RAM ]lr: link register , che ci dice dove deve andare dopo che una funzione ha fatto il suo doverepc: program counter , memorizza l’indirizzo della prossima istruzione [ mem. in memoria ]cpsr: memorizza informazioni riguardo le operazioni aritmetiche che si fanno , tipo se faccio 5 - 7 = -2 , alloracpsrdeve memorizzare che mi è uscito un numero negativo
First program
CPULator ci mette già della roba nell’editor :
.global _start
_start:
sono dei label e quando facciamo _start: stiamo dicendo : ok qui metti il codice , invece quando facciamo .global _start stiamo dicendo : ok esegui la roba che si trova in _start [starting point] .
Ora proviamo a “spostare” dati in r0 : lo facciamo con l’operazione MOV <destinazione>,<sorgente>
.global _start
_start:
MOV r0,#30
MOV r7,#1
SWI 0- prende 30 [ decimale ] e lo butta dentro
r0, se vogliamo usare hex possiamo fare cosi#0x1einvece che#30 - butta dentro
r7il valore 1 - chiama
swiche sarebbe il software interrupt , diciamo che chiama il OS che poi controllar7[ infatti è 1 e quindi termina l’esecuzione ]
nota che su CPUlator non viene usato SWI 0 , ma questo dovrebbe essere usato in processore ARM.
Addressing Modes
Il modo di indirizzare la roba che abbiamo usato prima con MOV r0,#30 si chiama Immediately addressing , siccome #30 è un valore costante diretto.
Possiamo anche indirizzare tra registri tipo MOV r1,r0 e si chiama Direct Addressing.
Un’altro modo di indirizzare ha a che fare con lo stack e si chiama Indirect Addressing , quindi vediamo prima come mettere/prendere della roba dallo stack :
abbiamo bisogno di una sezione .data che conterrà le dichiarazioni della roba che vogliamo mettere nello stack :
.data
list: //nome dei dati
.word 4,5,-9,1,0,2,-3 // lista di numericon .word stiamo dicendo che ogni valore va assunto come un word ovvero 32 bit , infatti con .word va ad allocare spazio in memoria.
Ora vogliamo prendere questi dati dallo stack e lo facciamo con LDR :
.global _start
_start:
LDR R0 , =listnotiamo quando eseguiamo che il r0 viene memorizzato l’indirizzo di 4 ovvero il primo elemento di list e non il valore 4.
\newpage
Per prendere il valore lo facciamo sempre con LDR ma sta volta usiamo una sintassi diversa per prendere il valore
.global _start
_start:
LDR R0 , =list
LDR r1 , [r0] -- prendo il valoredove infatti con [r0] specifichiamo che siamo interessati al valore associato all’indirizzo che contiene r0.
Ora se voglio prendere il valore successivo basta che aggiungo un certo offset in questo caso 4 , perché se il primo valore è indirizzato a 10 per esempio il secondo sarà indirizzato da 14 perché ogni valore è 32 bit che sono 4 byte :
.global _start
_start:
LDR R0 , =list
LDR r1 , [r0]
LDR r2 , [R0 , #4] //ADD offset per prendere il valore successivo Un’altro modo di indirizzare è il Pre-Increment , che sarebbe in pratica che in un lang di alto livello sarebbe :
R0 = r0+1
list[r0]mentre prima avevamo fatto solo cosi
list[r0+1]per fare il pre-increment basta aggiungere un ! alla fine di LDR <dest>,[<sorgente>,<offset>] quindi abbiamo :
.global _start
_start:
LDR R0 , =list
LDR r1 , [r0]
LDR r2 , [R0 , #4]! //ADD offset pre-incrementUn’altro metodo è Post-Increment che l’unica differenza dal Pre-Increment è che “incrementa” dopo e lo facciamo in questo modo :
.global _start
_start:
LDR R0 , =list
LDR r1 , [r0]
LDR r2 , [r0] , #4 //ADD offset post-increment in questo modo in r2 viene memorizzato il valore di r0 ovvero anche lo stesso di r1 e poi r0 viene incrementato dell’offset = 4 .
Operazioni
Le operazioni aritmetiche elementari che possiamo fare sono :
ADD <dest>,<n1>,<n2>SUB <dest>,<n1>,<n2>MUL <dest>,<n1>,<n2>
dove con n1 e n2 si riferisce ai registri che contengono effettivamente quel numero , un esempio è il seguente :
.global _start
_start:
MOV r0,#5
MOV r1,#7
ADD r2,r0,r1 // r2 = R0 + r1 la cosa interessante è quando il risultato è un numero negativo , facciamo per esempio :
.global _start
_start:
MOV r0,#5
MOV r1,#7
SUB r2,r0,r1 // r2 = R0 - r1 = -2 siccome , però in hex sarà in complemento a due a 32-bit e quindi sarà fffffffe , questo è un problema perché confonde un po , a questo ci viene in aiuto il registro cpsr :

che viene deve essere settato dopo qualsiasi operazioni elementare per sapere se il c’è stato un carry , zero , o è negativo il risultato.
Dobbiamo usare subs [ invece che SUB ] setterà il cspr .
Possiamo avere anche un carry quando facciamo una somma troppo grossa per essere messa in un solo registro e quindi usiamo adds per settare il cpsr :
.global _start
_start:
MOV r0,#0xfffffff
MOV r1,#3
adds r2,r0,r1
e se vogliamo sommare anche il carry usiamo adc :
.global _start
_start:
MOV r0,#0xfffffff
MOV r1,#3
adc r2,r0,r1 // r2 = R0 + r1 + carry
Logical Operators
Abbiamo :
and <dest>,<n1>,<n2>: ANDorr <dest>,<n1>,<n2>: OReor <dest>,<n1>,<n2>: XORmvr <dest>,<source>: butta dentro<dest>la negazione di<source>
Shift and Rotating
Ci sono altre operazioni che possiamo fare :
lsl <reg>,<n>: fa lo shift a sinistra del registroregnvolte → moltiplica pern*2lsr <reg>,<n>: fa lo shift a destra del registroregnvolte → divide pern*2ror <reg>,<n>: fa il rotation a destra del registroregnvolte → significa in pratica che tutto quello che “sparisce” a destra facendo lo shift a destra , “ritorna” da sinistra [ davanti ].
Conditions and Branches
Nel nostro programma vogliamo la possibilità di fare delle comparazioni in base a delle condizioni e “saltare” in un determinato punto del programma se una condizioni si verifichi o no. Per farlo usiamo i comparatori e i branches :
- i comparatori ci permettono di comparare due valori
- i branches ci permettono di muoversi all’interno del nostro programma basandoci sul risultato di una comparazione
.global _start
_start:
MOV R0,#1
MOV R1,#2
CMP R0,R1 // comparazione fa : R0-R1
BGT greater // bigger-than
MOV R2,#2
greater: // qui ci arriva lo stesso
MOV R2,#1 BGT <label> dice che se R0 R1 allora fai la roba che sta dentro <label>
Siccome il flusso di esecuzione è sequenziale , prima o poi arriviamo a greater e quindi esegue la roba che sta la dentro anche se la comparazione non è vera .
Per evitare questo si usa BAL <label> o B <label> che dice cosa deve eseguire se la comparazione non risulta vera e quindi fa lo skip del branch greater :
.global _start
_start:
MOV R0,#1
MOV R1,#2
CMP R0,R1
BGT greater
B end // va subito a end li saltando greater
greater:
MOV R2,#1
end:
MOV R2,#2Ci sono diversi branches :
BGE:BEQ:BNE:BLT:BMI: se negativoBPL: se positivo
Inoltre c’è un’altro metodo di fare le condizioni molto più pulito [ senza l’uso di branches che sporcano un po ] , è quello di fare prima il CMP tra due registri e poi aggiungere la condizione LT , GE , EQ etc proprio accanto alla operazione da fare in caso affermativo :
.global _start
.start:
MOV R0,#2
MOV R1,#4
CMP R0,R1
MOVLT R2,#9 // butta in R2 solo se R0 < R1 Looping
Possiamo fare un loop sfruttando i branches , vediamolo con un esempio :
.global _start
.equ endlist, #0xaaaaaaaa // definiamo una costante "endlist" pari al valore 0xaaaaaaaa ovvero la fine della lista
_start:
LDR R0,=list
LDR R1,[R0]
LDR R3,=endlist // carichiamo il valore endlist in R3
loop: // definizione del loop
LDR R1,[R0,#4]! // carica in R1 il valore successivo e fa il pre-increment
CMP R1,R3 // comparo R1 e R3 [endlist]
BEQ exit // se sono uguali esci
ADD R2,R2,R1 // se non sono uguali viene qua e somma a R2 , R1
BAL loop // ritorna sopra [ripeti]
exit: // nulla
.data
list:
.word: 1,2,3,4,5,6,7,8,9Functions [ Branch with link register and returns ]
Per creare una funzione utilizziamo il registro speciale LR che abbiamo a disposizione , questo registro verrà settato - prima di “saltare” in un branch - con l’indirizzo della prossima istruzione che dobbiamo fare dopo quel “salto” al branch e per farlo non saltiamo al branch con BAL \ B ma con BL :
.global _start
_start:
MOV R0,#1
MOV R1,#2
BL add_branch // vai su add_branch , ma ricordati l'indirizzo della prossima istruzione
MOV R3,#9 // prossima istruzione che deve fare quando finisce in add_branch
B end // finisci senno poi va a leggere sotto add_branch
add_branch:
ADD R2,R1,R0
BX LR // ritorna dove eri secondo il registro LR [LINKED REGISTER]
end:Preserving and Retrieving Data from Stack Memory
Siccome i registri non sono infiniti , possiamo “pushare” il valore dei registri nello stack memory e poi riprenderli quando ci servono ancora e dopo che ci abbiamo lavorato [ magari con una funzione tipo ] :
.global _start
_start:
MOV R0,#1
MOV R1,#2
PUSH {R0,R1} // pusha nello stack R0 e R1
BL add_branch
POP {R0,R1} // riprendi dallo stack R0 e R1 [ come li avevo messi ]
B end
add_branch:
// modifico R0 e R1
MOV R0,#5
MOV R1,#7
ADD R2,R1,R0
BX LR
end: