ASM Conversione in assembly

Stato
Discussione chiusa ad ulteriori risposte.

PandorumTeam

Utente Electrum
26 Giugno 2011
204
30
24
126
Hello, world!
A scuola mi hanno assegnato una codifica in assembly. Io non sono molto ferrato in questo linguaggio, lo odio.
Devo codificare questo:
--
CH <-- 4
Ripeti
Ruota BX 4bit BX,ROL
DX <-- BX AND F
DL <-- DL+30
SE DL>39 allora DL <-- +7
CH <-- CH-1
Finché CH=0
--
Io all'inizio ho fatto così:
MOV CH,4
NOP
Per l'istruzione di spostamento dei bit ho trovato rrf ma non se la prende e qui mi blocco :( e non so andare avanti per tutto il resto del codice.
Chi è l'esperto in asm che può aiutarmi?
 
Le istruzioni di cui hai bisogno sono:
MOV (che conosci).
Le istruzioni di shifting e rotazione dei bit.
AND (operatore logico. Immagino tu sappia usarlo).
ADD (somma. Credo tu la conosca).
CMP (compare. Per fare la comparazione tra due valori. Se non la sai usare dillo e te la spiego).
SUB (sottrazione) oppure DEC (decremento. Meglio usare questo per fare -1).
JMP e Jxx (per i salti. Con Jxx intendo tutti i JMP condizionali. Come JE, JGE, JNZ, JZ, ecc.)

Qui puoi trovare la spiegazione di molte istruzioni (è un manuale completo all'assembly tra l'altro).
Quelle di rotazione e shifting, le trovi (insieme a quelle logiche come AND) nel capitolo 19: Istruzioni logiche
Quelle aritmentiche (ADD, SUB, DEC, ecc.) e di comparazione (CMP) le trovi nel capitolo 17: Istruzioni aritmetiche
Puoi trovare qualsiasi altra istruzione nei vari capitoli. Sono spiegate in modo talmente semplice che anche un bambino di 3 anni lo capirebbe. C'è solo da dire che andrebbe fatto il manuale per intero: partire da capitoli successivi o fare solo alcuni capitoli potrebbe portare problemi nella comprensione.
 
  • Mi piace
Reazioni: PandorumTeam
Ultima modifica:
Le istruzioni di cui hai bisogno sono:
MOV (che conosci).
Le istruzioni di shifting e rotazione dei bit.
AND (operatore logico. Immagino tu sappia usarlo).
ADD (somma. Credo tu la conosca).
CMP (compare. Per fare la comparazione tra due valori. Se non la sai usare dillo e te la spiego).
SUB (sottrazione) oppure DEC (decremento. Meglio usare questo per fare -1).
JMP e Jxx (per i salti. Con Jxx intendo tutti i JMP condizionali. Come JE, JGE, JNZ, JZ, ecc.)

Qui puoi trovare la spiegazione di molte istruzioni (è un manuale completo all'assembly tra l'altro).
Quelle di rotazione e shifting, le trovi (insieme a quelle logiche come AND) nel capitolo 19: Istruzioni logiche
Quelle aritmentiche (ADD, SUB, DEC, ecc.) e di comparazione (CMP) le trovi nel capitolo 17: Istruzioni aritmetiche
Puoi trovare qualsiasi altra istruzione nei vari capitoli. Sono spiegate in modo talmente semplice che anche un bambino di 3 anni lo capirebbe. C'è solo da dire che andrebbe fatto il manuale per intero: partire da capitoli successivi o fare solo alcuni capitoli potrebbe portare problemi nella comprensione.




Mov CH,4
NOP
SHL BX,4
AND BX,DX ||NON L'HO MAI USATO, DALL'ESEMPIO CREDO CHE SI APPLICHI COSì IN QUESTO CASO, CORREGGIMI SE SBAGLIO.
MOV DL,1E ||Inserisco il valore di 30 in esadecimale che corrisponde esattamente ad 1E
CMP DL?? ||Questa non lo so usare, come faccio a fare la condizione del registro dl maggiore di 39? e impostare che se questa fosse vera aggiunga 7 al registro dl?
DEC CH,-1 ||Giusto?
JMP DL,0 ||Ho qualche dubbio su questa


--


Non so se il codice che ho scritto ha qualcosa di corretto ma ho delle difficoltà nei punti che ti ho elencato :\ Puoi aiutarmi? Intanto ti ringrazio per il tuo internvento.
 
:\
Ma l'avete studiato l'assembly prima di fare gli esercizi?
Codice:
Ruota BX 4bit BX,ROL
Devi fare una rotazione. Non un semplice shift. Tra l'altra c'è scritto anche "ROL" che credo sia un'indicazione ad utilizzare l'istruzione ROL (ROtate Left). Tu invece hai usato SHL (SHift logical Left). Quindi intanto:
Codice:
ROL BX, 4
Poi dopo:
Codice:
DX <-- BX AND F
(qui non so se il valore iniziale di BX deve essere preservato. Se sì, puoi utilizzare un altro registro (come AX) dove salvare temporaneamente il contenuto di BX. Dopo riporto anche questo codice...ora andiamo avanti).
Questo si divide in 2 istruzioni differenti: una logica (AND) e una di assegnamento (MOV). Poi ti chiede di fare l'AND tra BX e F (esadecimale). Non tra BX e DX. Quindi
Codice:
AND BX, 0F [COLOR=#00ff00]; Il risultato dell'AND logico tra BX e F viene salvato in BX stesso[/COLOR]
MOV DX, BX [COLOR=#00ff00]; Il risultato viene spostato in DX[/COLOR]
Codice:
DL <-- DL+30
Tu hai "inserito il valore 30 in DL". Ma li che c'è scritto?? di inserire 30 in DL?? se fosse così sarebbe stato:
Codice:
DL <-- 30
Invece devi inserire in DL la somma tra DL e 30. Siccome l'istruzione ADD salva il risultato della somma nell'operando DEST (ovvero il primo), è sufficiente un'unica istruzione:
Codice:
ADD DL, 1E [COLOR=#00ff00]; Aggiungo 30 (1E esadecimale) a DL. Risultato salvato sempre in DL stesso[/COLOR]
Poi hai:
Codice:
SE DL>39 allora DL <-- +7
Queste sono ben 3 istruzioni: 1 di comparazione (tra DL e 39), l'altra di controllo di flusso (salto condizionale) e la terza di assegnamento. CMP (CoMPare two operands) controlla se l'operando DEST (il primo) è maggiore, minore o uguale all'operando SRC (il secondo). In base al risultato della comparazione, modifica adeguatamente i flag (in particolare CF (Carry Flag), ZF (Zero Flag), SF (Sign Flag), OF (Overflow Flag)). Eseguita poi la comparazione tra DL e 39, è sufficiente un'istruzione di controllo del flusso condizionata (Jxx) per modificare l'esecuzione del codice. Devi controllare se è maggiore di 39. In tal caso metti 7 in DL altrimenti esegui solo il codice successivo. Invertendo la logica, possiamo fare un codice più semplice: non saltiamo quando DL è maggiore di 39, ma saltiamo quando DL è minore o uguale a 39. In questo modo possiamo inserire il salto seguito dal codice di assegnamento a DL seguito a sua volta dal resto del codice del programma (a dove punta anche il salto). Quindi, creata una label che identifica il codice successivo (che chiamo "xxx"):
Codice:
CMP DL, 27[COLOR=#00ff00] ; 27 esadecimale corrisponde a 39 decimale.[/COLOR]
JBE xxx[COLOR=#00ff00] ; salto se minore o uguale[/COLOR]
MOV DL, 7
xxx:
[COLOR=#00ff00]; continuo del codice[/COLOR]
Perché abbiamo usato JBE? JBE (Jump if Below or Equal) controlla il risultato dell'ultima operazione effettuata (in questo caso CMP). Dove lo controlla? Tra i Flag. In particolare JBE controlla i Flag ZF e CF. CMP setta ZF su 1 quando i 2 operandi (DEST e SRC) sono uguali. Quindi se DL è uguale a 39, CMP imposta ZF=1. Se ZF è uguale a 1 JBE salta alla label xxx (continuo del codice) altrimenti controlla anche CF: CF viene settato a 1 da CMP se DEST è minore di SRC (ovvero DL minore di 39). Se CF è uguale a 1 JBE salta alla label xxx altrimenti (appunto: se DL è maggiore di 39) esegue il codice sequenzialmente come se niente fosse successo: ovvero prima esegue MOV DL, 7 e poi continua l'esecuzione con il codice dopo la label ecc. ecc.
Codice:
CH <-- CH-1
Finché CH=0
(Qui faccio finta che sia "Finché CH diverso da 0" in quando "Finché CH=0" mi sembra molto strano visto che non ha alcun senso: il codice verrebbe eseguito solo una volta (o zero volte) se fosse così. Quindi il ciclo perderebbe di senso).
CH (più in generale ECX o CX) è il registro contatore (anche se poi, in realtà, è un registro general purpose, viene solitamente impiegato come contatore). Qui infatti sei di fronte a un ciclo e CH viene usato come contatore del ciclo. Il valore di CH, in questo modo, corrisponderà al numero di ripetizioni del ciclo: se CH è 5, tutto il codice verrà ripetuto 5 volte (finché CH non arriva a 0). Il ciclo funziona più o meno come un controllo IF. Intanto impostiamo un'altra label all'inizio di tutto il nostro codice (che chiamiamo "yyy"). Poi vediamo:
Codice:
DEC CH [COLOR=#00ff00]; DECrement by one: decrementa di 1 il valore dell'unico operando DEST[/COLOR]
CMP CH, 0[COLOR=#00ff00] ; confronta CH con 0[/COLOR]
JNZ yyy [COLOR=#00ff00]; se i 2 valori (CH e 0) sono diversi, salta a yyy[/COLOR]
JNZ (Jump if Not Zero) salva se i due valori messi a confronto sono diversi. Se CH è diverso da 0 salta. Se uguale a 0 non salta e il codice continua (uscendo dal ciclo). Per far questo JNZ controlla lo ZF (che viene settato a 1 se i 2 operandi sono uguali, a 0 se sono diversi). Se ZF è 0 allora i due operandi sono diversi (quindi CH non è ancora uguale a 0) e quindi salta a yyy. Altrimenti, se CH=0, esce dal ciclo e continua l'esecuzione normale.
Detto questo, non capisco perché metti un NOP (No OPeration) come seconda istruzione :\

Codice finale e completo:
Codice:
yyy:
[COLOR=#00ff00]; MOV BX, AX --- necessario solo se si vuole mantenere il valore di BX ad ogni ciclo (ROL viene sempre eseguito su BX ad ogni iterazione ma non AND).[/COLOR]
ROL BX, 4
[COLOR=#00ff00]; MOV AX, BC --- preservo il contenuto prima di eseguire l'AND. Necessario solo per mantenere il contenuto di BX[/COLOR]
AND BX, 0F
MOV DX, BX
ADD DL, 1E
CMP DL, 27
JBE xxx
MOV DL, 7
xxx:
DEC CH
CMP CH, 0
JNZ yyy

Un po' di spiegazioni su CMP e salti condizionali:
Come sai, in seguito ad ogni operazione aritmetica, i valori dei vari Flag vengono modificati in base al risultato. Ad esempio: se la sottrazione tra 2 numeri porta al risultato 0, lo ZF viene impostato su 1. Oppure se il risultato di una qualsiasi operazione è negativo, il SF (Sign Flag) viene impostato a 1. Le istruzioni di salto condizionato (e molte altre istruzioni) sfruttano proprio questi Flag per controllare le condizioni che gli interessano.
Ora CMP funziona più o meno in questo modo:
Codice:
CMP DEST, SRC
Viene eseguita la sottrazione tra DEST e SRC e il risultato salvato in una variabile temporanea:
Temp = DEST - SRC
Poi basta ragionare:
-Se Temp è maggiore di 0 (positivo), allora DEST è maggiore di SRC
-Se Temp è uguale a 0, allora DEST è uguale a SRC
-Se Temp è minore di 0 (negativo), allora DEST è minore di SRC
In questo modo, eseguita questa operazione, vengono modificati i valori dei Flag. In particolare vengono modificati:
ZF (Zero Flag) - che è 1 se il risultato dell'operazione è uguale a zero; 0 altrimenti.
SF (Sign Flag) - che è 1 se il risultato dell'operazione è un numero negativo; 0 altrimenti.
CF (Carry Flag) - che è 1 quando si verifica un riporto o un prestito dopo un'addizione o una sottrazione; 0 altrimenti.
OF (Overflow Flag) - che è 1 quando il risultato è troppo grande o troppo piccolo e quindi supera il numero di bit permessi e il segno si inverte (se doveva essere negativo diventa positivo e viceversa); 0 altrimenti.
PF (Parity Flag) - che è 1 quando il risultato contiene un numero pari di bit 1; 0 altrimenti.
I salti condizionali, controllano proprio questi valori. Prendiamo ad esempio:
Codice:
JNBE - Jump if Not Below or Equal - CF=0 AND ZF=0
JA - Jump if Above - CF=0 AND ZF=0
Queste due istruzioni sono uguali (infatti una dice "salta se non è minore o uguale" e l'altra "salta se maggiore", che sono la stessa cosa). Come mai controllano CF e ZF e se sono entrambi contemporaneamente zero allora saltano??
Facciamo finta di eseguire un CMP:
Codice:
CMP X, Y
Viene quindi fatto:
Temp = X - Y
Quindi:
1-Se X>Y allora Temp>0
2-Se X=Y allora Temp=0
3-Se X<Y allora Temp<0
Prendiamo caso per caso:
1) X>Y --> Temp>0
-ZF viene impostato su 0 perché il risultato non è zero.
-SF viene impostato su 0 perché il risultato è positivo.
-CF viene impostato su 0 perché, in una sottrazione, se il primo operando è maggiore, sappiamo dalla matematica che non c'è bisogno di alcun prestito.
-OF viene impostato su 0 perché se il primo operando è maggiore del secondo, sicuramente non manderà tutto in overflow.
-PF bho... dipende da altre condizioni. Ma questo flag lasciamolo stare che viene usato solo da particolari istruzioni di salto.
L'istruzione potrebbe anche controllare i Flag SF e OF, ma visto che il Flag ZF dimostra già che i due operandi sono diversi e CF dimostra già che il primo operando è maggiore del secondo, allora CF e ZF sono condizione necessaria e sufficiente per affermare che X è maggiore di Y.
2)X=Y --> Temp=0
-ZF viene impostato su 1 perché il risultato è zero.
-SF viene impostato su 0 perché il risultato è positivo (lo zero è considerato positivo, in quanto il bit del segno, il più significativo, è 0 e non 1).
-CF viene impostato su 0 perché, in una sottrazione, se il primo operando è uguale al secondo, non c'è bisogno di alcun prestito.
...OF e PF lasciamoli stare.
Anche qui. Poteva ad esempio utilizzare anche ZF e SF per fare la verifica, ma non importa. CF e ZF sono condizione necessaria e sufficiente.
3)X<Y --> Temp<0
-ZF viene impostato su 0 perché il risulato è zero.
-CF viene impostato su 1 perché, in una sottrazione, se il primo operando è minore del secondo, c'è sicuramente bisogno di un prestito.
Quindi è chiaro che: quelle due istruzioni controllano CF e ZF per controllare se un numero è maggiore (o non è minore o uguale) dell'altro. E solo se sia ZF che CF sono impostati a 0 allora saltano perché solo in questo caso (caso 1, nell'esempio) DEST (X) è maggiore di SRC (Y).
 
  • Mi piace
Reazioni: PandorumTeam
Grazie della risposta, molto esaustiva.
Ho riportato il tuo codice, ero l'unico che si era preoccupato di farlo.. pensa un po' te...
Comunque il professore me l'ha corretto e quello finale è:
--
MOV CH,04
MOV CL,04
ROL BX,CL
MOV DX,BX
AND DX,000F
ADD DL,30
CMP DL,39
JBE 0117
ADD DL,07
MOV AH,02
INT 21
DEC CH
JNZ 0102
INT 20
--
L'ho postato se dovesse servire a qualcuno. Comunque ti ringrazio moltissimo della spiegazione che mi hai dato, tu dove hai imparato l'assembly? Sei davvero in gamba. Se non dovessi capire qualcosa posso scocciarti ancora? :asd: Ciao e buona giornata.
 
L'assembly l'ho imparato proprio qui: Ra.M. Software Home Page (prima Assembly Base con MASM, poi Assembly Avanzato, e infine Win32 Assembly). Poi ho continuato con i tutorial di Iczelion per l'Assembly Win32.
Per quanto riguarda le correzioni:
--
MOV CH,04
MOV CL,04
ROL BX,CL
MOV DX,BX
AND DX,000F
ADD DL,30
CMP DL,39
JBE 0117
ADD DL,07
MOV AH,02
INT 21
DEC CH
JNZ 0102
INT 20
--
Prima di tutto, nel primo post non mi hai detto niente di CL. Hai solo messo "CH <-- 4", senza CL. Poi "AND DX, 0F" e "AND DX, 000F" sono la stessa cosa. Da notare che tu mi hai detto di fare l'AND con DL invece che con DX. Poi quel 30 e quel 39, non hai specificato che tu li avevi riportati già in esadecimale, quindi gli ho convertiti. Di AH uguale, non ne hai mai parlato. 07 o 7 è la stessa cosa (solo quando il numero inizia con una lettera ci vuole almeno uno zero davanti). Per i salti, io ho usato delle label, non hai detto che il professore richiedeva proprio gli offset relativi all'indirizzo (di questo si occupa il linker di solito, non il programmatore che può far uso delle label senza problemi). Delle INT non hai parlato.
Il codice che ti ho sviluppato è giusto per ciò che hai chiesto. Era semmai parzialmente sbagliata la consegna che hai riportato tu qui :\
 
Infatti io mi sono meravigliato quando me l'ha corretto perché ha aggiunto delle cose che nel linguaggio di progetto non aveva messo, e quando glie l'ho chiesto ha detto che se n'era scordato .-. ma pensa un po' te. Comunque ora mi metto a studiarlo come hai fatto tu almeno per andare bene ai compiti :) grazie molte.
 
Stato
Discussione chiusa ad ulteriori risposte.