Ma l'avete studiato l'assembly prima di fare gli esercizi?
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:
Poi dopo:
(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]
Tu hai "inserito il valore 30 in DL". Ma li che c'è scritto?? di inserire 30 in DL?? se fosse così sarebbe stato:
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.
(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:
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:
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).