i registri

Stato
Discussione chiusa ad ulteriori risposte.

dark shadow

Utente Electrum
11 Giugno 2007
284
42
0
150
allora, premetto:
- Abbiamo introdotto da nemmeno una settimana l'assembler all'uni.
- Sono a pagina 17 della guida di blacklight, quindi all'inizio.
- all'uni è previsto l'utilizzo della sintassi gas e la compilazione tramite gcc.

veniamo al punto, questo programma è stato fatto in laboratorio(non ho potuto seguire la lezione) e dovrebbe servire per determinare se un numero è positivo o negativo:
Codice:
.global _segno



.text



max:



	movl	$1,%eax



	popl	%ebp

	ret



_segno:

	pushl %ebp		#Pre-ambolo

	movl %esp, %ebp



	cmpl $0,%eax		#Confronto con 0

	jmp max

	movl $-1,%eax



	popl %ebp		#Post-ambolo

	ret

Innanzitutto non capisco in base a quale logica utilizzare i registri, i vari eax, ebp ecc
a pagina 7 della guida di blacklight leggo i vari tipi di registro(registri general purpose ecc)
sono quelli che devo prendere come riferimento?mm, sono a due caratteri però.
Per il momento la domanda è una.. ^^grazie.
 
normalmente le istruzioni assembly usano registri particolari per la loro esecuzione....
ad esempio l'istruzione loop decrementa utomaticamente ecx, mul, con un registro a 16 bit (mul r16), moltiplica AX * il registro specificato e lo salva in EAX, mentre con un registro a 32bit (mul r32) moltiplica EAX per il registro specificato e lo salva in EDX:EAX (la parte più significativa in EDX e quella meno in EAX)....

se mi ricordo bene^^...

cmq quando scegli i registri da usare questo è quello di cui devi tenere conto.... ad esempio l'istruzione MOV non accetta alcuni registri per fare composizioni del tipo di EAX*4+ESI.... se metti ebx al posto di esi, se non ricordo male non funziona.....


se non hai di questi problemi, puoi usarli come ti pare
 
Uhm non capisco la logica di quel sorgente...io l'avrei impostato così:

Codice:
.data
msg1:   .string "Inserisci un numero: "
msg2:   .string "%d è negativo\n"
msg3:   .string "%d è positivo\n"
msg4:   .string "%d"
num:            .long   0

.text
        .global main

main:
        push            $msg1
        call            printf
        add             $0x4,%esp

        push            $num
        push            $msg4
        call            scanf
        add             $0x8,%esp

        mov             $0,%eax
        cmp             num,%eax
        jge             min
        jmp             max

min:
        push            num
        push            $msg2
        call            printf
        add             $0x4,%esp
        jmp             end

max:
        push            num
        push            $msg3
        call            printf
        add             $0x4,%esp
        jmp             end

end:
        mov             $1,%eax
        mov             $0,%ebx
        int             $0x80

        leave
        ret
 
@blacklight: ti direi la mia ma purtroppo non ho le competenze necessarie, so solo che quello è stato fatto dal prof in laboratorio.

@whivel: mm, quindi a parte quelle sottogliezze che imparerò con il tempo è indifferente utilizzare l'uno o l'altro registro?! nel senso..se io mettessi tutto in eax(presupponendo di avere spazio) sarebbe sbagliato?(non so se è un esempio paraddossale, ma è per capire).
 
se tu usassi il registro eax per una cosa, e dopo, siccome la prima cosa non ti serve, mettessi una seconda su eax, sarebbe corretto.....

se invece hai 2 byte (1 byte per una cosa e una per un'altra), potresti mettere su AL un byte e su AH un altro....
 
scusami ma pur avendo le basi di programmazione(c++) è abbastanza complicato entrare nell'ottica;
quindi mettiamo che io debba spostare una stringa in un registro:


Codice:
...
...
stringa:   //mia etichetta
.string "ciao"

main:
   mov $stringa//SORGENTE,%BX //DESTINAZIONE,CIOè IL REGISTRO

leggo dalla guida di black che il registro BX è composto da due parti da 8bit, quindi ne deduco che in 16 bit ci sta una stringa?è così che devo ragionare per procedere?
Avrei potuto mettere la stringa indifferentemente anche nei seguenti registri?
- ax
- cx
- dx
grazie e scusami se ho posto domande probabilmente stupide..
 
a parte che è una cosa per me abominevole usare la struttura MOV SORGENTE DESTINAZIONE (visto che di solito è il contrario), cmq in quel caso tu copi i primi byte della stringa su bx, siccome bx contiene 2 byte, copi i primi 2 byte, siccome 1 carattere è 1 byte, copi i primi 2 caratteri....

se avessi usato ax, cx, dx, sarebbe stata la stessa cosa
 
Inoltre la MOV nella convenzione AT&T ha la seguente sintassi:
mov SORGENTE,DESTINAZIONE
by..guida blacklight..
tu intendi abominevole perchè utilizzi di solito la sintassi intel?

ok, quindi copiando i primi 16bit copio di fatto solo due caratteri,cioè 2byte..mmm..quindi c'è qualcosa che non torna, ora come dovrei proseguire?Ponendomi una domanda di questo tipo?
1) Mancandomi 2byte, metto i restanti in un altro registro?
2) Esiste un registro che possa contenermi l'intera stringa?

Vedi, cioè il mio problema è entrare nella mentalità di una programmazione di questo tipo, poi la sintassi ecc c'è solo da studiarsela..
 
si infatti io ritengo abominevole l'uso della sintassi at&t perchè sia il tasm, nasm (linux e windows), masm, l'asm inline del vc++, ecc. usano la sintassi intel come predefinita (non so se si può cambiare).....

comunque lasciando perdere le mie impressioni, il concetto è che in assembly devi pensare poco a variabili e tipo di variabili, ma a memoria e dimensione del dato a cui accedi.....

cioè la tua stringa si trova in memoria, ed è formata da vari byte.... l'ìindirizzo in cui si trova è indicato attraverso un nome ($stringa). se tu accedi direttamente puoi leggere i primi n byte, in base alla dimensione dell'operando: BYTE, WORD, DWORD, QWORD. Ad esempio la tua istruzione è un'operando che lavora su WORD (2 byte) perchè AX può contenere una WORD.
se vuoi accedere ad un carattere successivo, devi puntare all'indirizzo in cui si trovano.
Ad esempio il terzo carattere è $stringa + 2, il secondo $stringa +1, ecc.
per questo scopo puoi usare il contenuto dei registri (E)SI ed (E)DI...

in parole povere, devi considerare tutte le tue variabili come array di dimensione n (vedi lista di prima)... o meglio tutta la memoria la devi vedere così.
 
Stavo provando a fare qualcosa, vorrei fare una cosa diciamo completa.. metto una stringa in un registro, la richiamo e la stampo a video, per ora ho già problemi a fare il primo pezzo, nn male ^^ lol

Codice:
.text
        .global main
miastringa:
        .string        "LO"
main:
        mov $miastringa,%AL

Intendevo mettere i 2 caratteri "LO", che dovrebbero occupare 2byte nel sottoregistro di AX, nella parte alta(o bassa?) cioè AL.
Il file è stato salvato con estensione .s e cercato di compilare tramite gcc, il quale restituisce quest'errore

/tmp/ccHRMbTw.o: In function `main':
(.text+0x5): relocation truncated to fit: R_386_16 against `.text'
collect2: ld returned 1 exit status

non so ancora interpretarli...
 
purtroppo non uso ne AT&T ne GAS, cmq da quello che ho capito c'è un errore nel riempire (troncato) il registro R_386_16 (dovrebbe indicare un registro dell'architettura 386 a 16 bit)
 
ok..ci siamo quasi, ora mi da solo un warning, però compila ^^
l'errore di prima era dovuto al fatto che dovevo inserire il suffisso a mov

Codice:
.text
message:
.string "LO"
.globl main
main:
	movl $message,%AX

il warning è: using `%eax' instead of `%ax' due to `l' suffix

mi dice di usare il registro eax in presenza del suffisso 'l' applicato a mov, non ho capito il perchè sinceramente..se il registro non cambia, in quanto di fatto compila..
 
Le istruzioni AT&T prevedono un suffisso che può essere:

b se l'istruzione è riferita a byte;

w se l'istruzione è riferita a word (due byte);

l se l'istruzione è riferita a doppia word (l deriva da «long»).

Nella sintassi Intel, allo stesso scopo, esistono le direttive byte ptr, word ptr, dword ptr, ma sono poco usate in quanto è la dimensione degli operandi a stabilire la natura dell'istruzione usata.

quindi basta mettere movw
 
ah, ok quindi in questo caso è più corretto, anche se non sbagliato, mettere il suffisso w, trattandosi di 2byte.
Ora devo caricare tutto quello che c'è nel registro eax e stamparlo a video; per il caricamento pensavo di fare così:
popl %eax

e compila ^^ ora per stampare a video devo per forza richiamare la write del c?
 
se hai impostato l'eseguibile a 32bit, se stai su windows,si perchè il classico int 21h (per windows) funziona nei sottosistemi dos, che, nei sistemi XP, Vista, ecc. viene emulato..... ma se è a 32 bit questo non avviene e ti crasha...

tra l'altro non capisco l'avversione generale ad usare printf, che è semplicemente una funzione, anche in assembly, che non si appoggia a niente..
 
no no..sto sotto linux-ubuntu, mm..non sono riuscito a seguire molto il tuo pensiero a dirti la verità ^^, quindi 21h sarebbe una funzione che richiama la finestra dos stampando a video caratteri?! sotto linux?
 
ah giusto.....
cmq int 21h è un interrupt programmabile, che nei sistemi dos era stato programmato dalla microsoft per varie cose.... con ah = 01 mi sa che stampava a video.....

cmq su linux dovresti poter usare le chiamate di sistema (non sono esperto di linux).... cmq non vedo alcun tipo di problema ad usare printf
 
mm, printf direttamente da assembler?!
Codice:
.text
message:
.string "LO"
.globl main
main:
	movl $message,%EAX 
	popl %eax
	call printf
direi che così è nettamente sbagliato?! mmm..come faccio a richiamarla e stampare successivamente a video il contenuto del registro eax?
E sopratutto, tutte le volte devo richiamare la funzione o posso fare una sorta di #include?!
 
questo proprio non te lo so dire....
cmq l'unico consiglio su questo che ti posso dare è di cercare di capire come fare dal post di BlackLight...
il codice dovrebbe aiutari a capire come funziona printf
 
Io odio la sintassi AT&T e tutta quella diversa da quella intel, ovviamente certe volte per forza di cose ci si deve adattare, cmq ti scrivo la soluzione in sintassi intel, poi te la porti tu con qualsiasi altra sintassi.

Allora, per fare una chiamata di sistema ci sono vari valori che identificano ognuno una chiamata diversa, su linux la chiamata di sistema a write (che stampa sullo standard output) è identificata dal valore 4 (trovi una lisa dei valori per le chiamate di sistema nel file unistd.h).

Una volta impostati i registri per effettuare la syscall devi richiamare l'interrupt 0x80 che serve per effettuare una syscall appunto.

Facciamo il solito programma Ciao Mondo però siccome ciao mon mi sta sulle balle scriviamo "Ce l'ho duro".

Codice:
section .data ; segmento dati
szMessage DB 0X0a,"Ce l'ho duro",0x0a ; i bytes 0x0a servono per il ritorno a capo

section .text ; segmento testo
global _start

_start:
; qui inizia il codice per la syscall a write() che chiede in ingresso 3 parametri
; usiamo i registri EAX per impostare il valore della syscall (4 per write)
; usiamo il registro EBX  per specificare stdout (ovvero 1)
; usiamo il registro ECX per impostare l'indirizzo in cui si trova la stringa (o meglio il suo primo byte)
; usiamo EDX per impostare la lunghezza della stringa (la diamo esplicitamente senza fare calcoli per determinare la lunghezza per ora)
; chiamiamo l'interrupt 0x80 (sys call) con l'istruzione int 0x80

MOV eax,4
MOV ebx,1
MOV ecx,szMessage
MOV edx,15
INT 0x80

; ora facciamo una syscall a exit il cuo valore è 1

MOV eax,1 ; imposto il valore della syscall
MOV ebx,0 ; Esce dal programma in modo pulito e restituisce il controllo al sistema operativo
INT 0x80 ; come prima l'interrupt 0x80 specifica di effettuare una syscall

Io uso nasm in genere.

Riepilogando in assembly usi i registri per impostare i parametri delle syscall che vai a eseguire, compreso il tipo di syscall che vuoi effettuare.

in c faresti na roba del genere :

printf(parametri..);

In assembly devi passare i parametri tramite i registri, ovviamente ogni syscall usa un tot di registri, non sono tutte uguali, dipende da quanti argomenti prendono in entrata.

In genere i registri di uso "generico" ti servono a questo, piu tutte le altre operazioni aritmetiche, ovvero per salvare dati temporaneamente.

Spero di essere stato chiaro...Lascia stare la sintassi AT&T ecc...non capisco come mai nella tua uni la facciano usare, molto meglio la sintassi INTEL.

Byez
 
la sintessi at&t si usa perchè i compilatori (tipo gcc su linux) usano quella sintassi.
lo stesso vale per l'assemblatore più usato in ambito linux.
 
concordo pienamente, in quella AT&T ci sono informazioni ridondanti.
Comunque nasm e tasm usano intel, masm puoi scegliere. C'è un altro su linux (che usa anche il gcc se non sbaglio) che dovrebbe essere gas
 
Stato
Discussione chiusa ad ulteriori risposte.