Esercizio Assembler
Ciao a tutti come esercizio dovevo svolgere questa consegna in linguaggio c e assembly:
acquisire due numeri A e B compresi tra 0 e 64000 e calcolare la somma dei "2" presenti nei numeri compresi tra A e B.
quindi se scegliessi i numeri 2001 e 2003, la catena di numeri da prendere in considerazione sarebbe : 2001 , 2002 , 2003 (A e B compresi ). in questa catena il numero "2" ricorre 4 volte quindi la somma totale dei numeri "2" è 8.
In linguaggio c sono riuscita a svolgere l'esercizio e compila, ma in assembler ho molti problemi.
Secondo me dovrei creare un ciclo che comincia col numero A e finisce quando raggiunge il numero B compreso, all'interno dovrei impostare un altro ciclo che controlli il numero A (che sarà incrementato ogni volta dal primo ciclo) e incrementi di 1 un'altra variabile ogni volta che incontra un 2 . poi basterebbe moltiplicare questa variabile per 2 e stamparla
quello che ho scritto, escludendo la richiesta dei numeri è questo, ma non compila.. potreste aiutarmi???
MOV CX,0 ; lo uso come contatore dei "2"
LOOP1:
MOV AX,SI ; mette il numero da testare in AX
LOOP2:
XOR DX,DX ; il dividendo è in DX:AX (numero a 32 bit) DX è sempre ZERO
MOV BX,10 ; il divisore
DIV BX ; divide DX:AX per BX, quoziente in AX, resto in DX (il resto è la cifra decimale)
CMP DX,2 ; se il resto è '2' allora abbiamo la cifra '2' nel numero
JNE NOT_DUE
INC CX ; incremento il contatore dei 2
NOT_DUE:
CMP AX,0 ; se il quoziente è ZERO ho finito col numero attuale
JNE LOOP2
INC SI ; passa al prossimo numero
CMP SI,DI ; se SI è minore/uguale a DI continua
JBE LOOP1
acquisire due numeri A e B compresi tra 0 e 64000 e calcolare la somma dei "2" presenti nei numeri compresi tra A e B.
quindi se scegliessi i numeri 2001 e 2003, la catena di numeri da prendere in considerazione sarebbe : 2001 , 2002 , 2003 (A e B compresi ). in questa catena il numero "2" ricorre 4 volte quindi la somma totale dei numeri "2" è 8.
In linguaggio c sono riuscita a svolgere l'esercizio e compila, ma in assembler ho molti problemi.
Secondo me dovrei creare un ciclo che comincia col numero A e finisce quando raggiunge il numero B compreso, all'interno dovrei impostare un altro ciclo che controlli il numero A (che sarà incrementato ogni volta dal primo ciclo) e incrementi di 1 un'altra variabile ogni volta che incontra un 2 . poi basterebbe moltiplicare questa variabile per 2 e stamparla
quello che ho scritto, escludendo la richiesta dei numeri è questo, ma non compila.. potreste aiutarmi???
MOV CX,0 ; lo uso come contatore dei "2"
LOOP1:
MOV AX,SI ; mette il numero da testare in AX
LOOP2:
XOR DX,DX ; il dividendo è in DX:AX (numero a 32 bit) DX è sempre ZERO
MOV BX,10 ; il divisore
DIV BX ; divide DX:AX per BX, quoziente in AX, resto in DX (il resto è la cifra decimale)
CMP DX,2 ; se il resto è '2' allora abbiamo la cifra '2' nel numero
JNE NOT_DUE
INC CX ; incremento il contatore dei 2
NOT_DUE:
CMP AX,0 ; se il quoziente è ZERO ho finito col numero attuale
JNE LOOP2
INC SI ; passa al prossimo numero
CMP SI,DI ; se SI è minore/uguale a DI continua
JBE LOOP1
Risposte
Ciao,
forse ti complichi la vita.
Secondo me basta che fai:
- la differenza tra A e B: $C = |A-B|$ il valore assoluto non esiste mi pare in assembler, per evitarlo basta che espliciti che $A>B$
- $C$ lo dividi per 2, cioè uno schift logico a sinistra di una posizione (LSHL mi pare) ed avrai il numero che ti serve.
forse ti complichi la vita.
Secondo me basta che fai:
- la differenza tra A e B: $C = |A-B|$ il valore assoluto non esiste mi pare in assembler, per evitarlo basta che espliciti che $A>B$
- $C$ lo dividi per 2, cioè uno schift logico a sinistra di una posizione (LSHL mi pare) ed avrai il numero che ti serve.
il prof mi ha appena detto , sostanzialmente , che ho fatto un gran casino. l'algoritmo è testato su una piattaforma a 32 bit ma usando registri a 16 bit per i calcoli, e lui mi ha scritto che il programma deve andare sotto dosbox. quindi non riesco a capire che intende? che devo modificare? perdonate se le domande sono stupide ma abbiamo appena iniziato con assembler.
ah scusa ho capito ora che devi fare, sorry in sto periodo capisco tutto il contrario di tutto
mostra il codice C così è più facile farti capire se ci son errori di interpretazione del tuo codice assembly. Il docente se sottolinea che utilizzerai dosbox vuol dire che sara assembly x86.

mostra il codice C così è più facile farti capire se ci son errori di interpretazione del tuo codice assembly. Il docente se sottolinea che utilizzerai dosbox vuol dire che sara assembly x86.
Ahah nn c'è problema ! fai anche troppo ad aiutarmi! il codice che ho scritto in c è questo , a meno che io non abbia una forma di demenza precoce dovrebbe compilare.
#include
#define LIM_INF 0
#define LIM_SUP 64000
#define TARGET_DIGIT 2
int main(void) {
int A, B;
int contatore, curr;
//controllo dell'input
do { //ciclo che controlla se A < B
do { //ciclo che controlla se LIM_INF <= A <= LIM_SUP
printf("Inserisci estremo inferiore: ");
scanf("%d", &A);
} while (A < LIM_INF | A > LIM_SUP);
do { //ciclo che controlla se LIM_INF <= B <= LIM_SUP
printf("Inserisci estremo superiore: ");
scanf("%d", &B);
} while (B < LIM_INF || B > LIM_SUP);
} while (A > B);
for (contatore = 0; A <= B; A++)
//scompone il numero in cifre e cerca la cifra bersaglio (2 in questo caso)
for (curr = A; curr > 0; curr /= 10)
//ottengo cifra meno significativa e faccio quel che devo fare
if (curr % 10 == TARGET_DIGIT)
contatore++;
printf("Sono stati trovati %d cifre uguali a %d\n", contatore, TARGET_DIGIT);
printf("La somma delle %d occorrenze di %d e' %d\n", contatore, TARGET_DIGIT, TARGET_DIGIT * contatore);
return 0;
}
#include
#define LIM_INF 0
#define LIM_SUP 64000
#define TARGET_DIGIT 2
int main(void) {
int A, B;
int contatore, curr;
//controllo dell'input
do { //ciclo che controlla se A < B
do { //ciclo che controlla se LIM_INF <= A <= LIM_SUP
printf("Inserisci estremo inferiore: ");
scanf("%d", &A);
} while (A < LIM_INF | A > LIM_SUP);
do { //ciclo che controlla se LIM_INF <= B <= LIM_SUP
printf("Inserisci estremo superiore: ");
scanf("%d", &B);
} while (B < LIM_INF || B > LIM_SUP);
} while (A > B);
for (contatore = 0; A <= B; A++)
//scompone il numero in cifre e cerca la cifra bersaglio (2 in questo caso)
for (curr = A; curr > 0; curr /= 10)
//ottengo cifra meno significativa e faccio quel che devo fare
if (curr % 10 == TARGET_DIGIT)
contatore++;
printf("Sono stati trovati %d cifre uguali a %d\n", contatore, TARGET_DIGIT);
printf("La somma delle %d occorrenze di %d e' %d\n", contatore, TARGET_DIGIT, TARGET_DIGIT * contatore);
return 0;
}
addirittura per saltarci fuori con il codice in assembler, ho seguito in tutorial che mi spiegava come convertire il codice in c ed arrivare al codice macchina in assembler. sono venuti fuori dei nomi di variabili che sono un misto tra il cinese e l'aramaico. se il mio prof lo scoprisse sarei bandita a vita dal corso :S
FORSE HO CAPITO DOVE HO SBAGLIATO
Per capire se un numero ha, al suo interno, uno o più 2, il modo più semplice è trattarlo come stringa e analizzare carattere per carattere.
Ad esempio, se A=19 e B=31 , si inizia da A e :
- il carattere 1 è uguale al carattere 2? NO
- il carattere 9 è uguale al carattere 2? NO
- fine numero... allora, lo converto in numero (da '19' a 19), lo incremento (da 19 a 20) e poi lo riconverto in stringa (da 20 a '20') e rinizio il ciclo
- il carattere 2 è uguale al carattere 2? SI, allora sommo 2 a una variabile contatore
quindi non è vero, come ho scritto nei commenti, che se un numero è divisibile per 2 il resto della divisione per 10 deve essere 2 (prendi numero=21 e lo vedi...). solo che ora non ho la più pallida idea di come scriverlo
Per capire se un numero ha, al suo interno, uno o più 2, il modo più semplice è trattarlo come stringa e analizzare carattere per carattere.
Ad esempio, se A=19 e B=31 , si inizia da A e :
- il carattere 1 è uguale al carattere 2? NO
- il carattere 9 è uguale al carattere 2? NO
- fine numero... allora, lo converto in numero (da '19' a 19), lo incremento (da 19 a 20) e poi lo riconverto in stringa (da 20 a '20') e rinizio il ciclo
- il carattere 2 è uguale al carattere 2? SI, allora sommo 2 a una variabile contatore
quindi non è vero, come ho scritto nei commenti, che se un numero è divisibile per 2 il resto della divisione per 10 deve essere 2 (prendi numero=21 e lo vedi...). solo che ora non ho la più pallida idea di come scriverlo

Ciao,
allora il programma di per se non è complicato.
Ma una questione da chiarire primea con "acquisire due numeri A e B" sicura che siano da linea di comando o da tastiera?
non è così immediato scrivere il codice assembly che fa tale operazione, c'è da abilitare lo standard input (operazioni di I/O) lo hai fatto nel tuo corso (attesa polling, DIN, ...) leggendo il tuo codice, sembra che sia semplicemente leggere da due rigistri due interi caricati da memoria, non introdotti da tastiera.
allora il programma di per se non è complicato.
Ma una questione da chiarire primea con "acquisire due numeri A e B" sicura che siano da linea di comando o da tastiera?
non è così immediato scrivere il codice assembly che fa tale operazione, c'è da abilitare lo standard input (operazioni di I/O) lo hai fatto nel tuo corso (attesa polling, DIN, ...) leggendo il tuo codice, sembra che sia semplicemente leggere da due rigistri due interi caricati da memoria, non introdotti da tastiera.
purtroppo i numeri sono da introdurre da tastiera, l'ho appena chiesto al professore dato che mi è venuto il dubbio adesso. poi mi ha scritto di prendere in considerazione la seconda interpretazione del codice, e lasciare perdere la divisione per 10 come ho scritto nel codice.quindi sono da capo!

Allora vediamo...
Facciamo a pezzi che è più semplice per me, e soprattutto per te, così comprendi cosa accade.
ti do le istruzioni di assembly Intel per lettura da tastiera semplice non ingrippiamoci in codici ed operandi complicati. Utilizziamo un'iterazione a polling su stdin. Ricorda che si lavora in Byte e non a parole.
questo è per un singolo byte, cioè una singola cifra.
Considera adesso che devi inserire un numero di massimo 5 cifre, ti servono 5 registri da 8 bit. Cioè un registro da 32 bit + una porzione di un secondo.
Dobbiamo perciò fare un LOOP di 5 iterazioni massime (per due cifre). Non sempre questo serve, possono esserci LOOP di 4,3,2,1 cifre. Per questo introduciamo un carattere ASCII che per noi vuol dire fine iterazione, cioè facciamo un LOOP di 6 iterazioni: 5 cifre massime + carattere speciale. Questo comporerà che ci possono essere $<5$ cifre + un carattere (immagina una stringa, qui invece è una array di registri).
Prova a pensare a come fare e che registri utilizzare, poi continuamo
Facciamo a pezzi che è più semplice per me, e soprattutto per te, così comprendi cosa accade.
ti do le istruzioni di assembly Intel per lettura da tastiera semplice non ingrippiamoci in codici ed operandi complicati. Utilizziamo un'iterazione a polling su stdin. Ricorda che si lavora in Byte e non a parole.
TESTB $3, SIN JNCB READ MOVB Din, %al
questo è per un singolo byte, cioè una singola cifra.
Considera adesso che devi inserire un numero di massimo 5 cifre, ti servono 5 registri da 8 bit. Cioè un registro da 32 bit + una porzione di un secondo.
Dobbiamo perciò fare un LOOP di 5 iterazioni massime (per due cifre). Non sempre questo serve, possono esserci LOOP di 4,3,2,1 cifre. Per questo introduciamo un carattere ASCII che per noi vuol dire fine iterazione, cioè facciamo un LOOP di 6 iterazioni: 5 cifre massime + carattere speciale. Questo comporerà che ci possono essere $<5$ cifre + un carattere (immagina una stringa, qui invece è una array di registri).
Prova a pensare a come fare e che registri utilizzare, poi continuamo

per leggere e stampare una sola cifra io farei così :
DOSSEG
.MODEL SMALL .STACK 200H .DATA
CIFRA DB ? .CODE
BEGIN:
MOV AX,@DATA
MOV DS,AX ;stampa del prompt MOV DL,"?"
MOV AH,02H
INT 21H
;lettura della cifra MOV AH,01
INT 21H
SUB AL,30H
MOV CIFRA,AL ;ritorno a capo MOV DL,0AH MOV AH,02H
INT 21H
MOV DL,0DH MOV AH,02H
INT 21H
;stampa della cifra MOV DL,CIFRA ADD DL,30H
MOV AH,02H
INT 21H
MOV AX,4C00H INT 21H
END BEGIN
DOSSEG
.MODEL SMALL .STACK 200H .DATA
CIFRA DB ? .CODE
BEGIN:
MOV AX,@DATA
MOV DS,AX ;stampa del prompt MOV DL,"?"
MOV AH,02H
INT 21H
;lettura della cifra MOV AH,01
INT 21H
SUB AL,30H
MOV CIFRA,AL ;ritorno a capo MOV DL,0AH MOV AH,02H
INT 21H
MOV DL,0DH MOV AH,02H
INT 21H
;stampa della cifra MOV DL,CIFRA ADD DL,30H
MOV AH,02H
INT 21H
MOV AX,4C00H INT 21H
END BEGIN
ho provato a riscrivere il codice in modo da poter stampare a video un numero di 5 cifre.. spero di non avere scritto una cosa aberrante!
DOSSEG
.MODEL SMALL .STACK 200H .DATA
CIFRA DB ? .CODE
BEGIN:
MOV AX,@DATA
MOV DS,AX ;stampa del prompt MOV DL,"?"
MOV AH,02H
INT 21H
mov cx,5
accetta_cifra: MOV AH,01 ;lettura della cifra
INT 21H
SUB AL,30H
MOV CIFRA,AL ;ritorno a capo
MOV DL,0AH
MOV AH,02H
INT 21H
MOV DL,0DH
MOV AH,02H
INT 21H
MOV DL,CIFRA ;stampa della cifra
ADD DL,30H
MOV AH,02H
INT 21H
loop accetta_cifra
MOV AX,4C00H
INT 21H
END BEGIN
DOSSEG
.MODEL SMALL .STACK 200H .DATA
CIFRA DB ? .CODE
BEGIN:
MOV AX,@DATA
MOV DS,AX ;stampa del prompt MOV DL,"?"
MOV AH,02H
INT 21H
mov cx,5
accetta_cifra: MOV AH,01 ;lettura della cifra
INT 21H
SUB AL,30H
MOV CIFRA,AL ;ritorno a capo
MOV DL,0AH
MOV AH,02H
INT 21H
MOV DL,0DH
MOV AH,02H
INT 21H
MOV DL,CIFRA ;stampa della cifra
ADD DL,30H
MOV AH,02H
INT 21H
loop accetta_cifra
MOV AX,4C00H
INT 21H
END BEGIN
ah devi utilizzare gli interrupt, ok.
Le istruzioni non le ho guardate tutte. Ma le operazioni che fai prima di ogni INT mi sembrano ok.
Forse per la tastiera non serve, ma se ricordo bene bisognerebbe abilitare il bit di interrupt (con STI). Ma guardando in rete sembra che non serva...
mi spiegheresti questo pezzo, che fa, penso inizializzi qualcosa...
Le istruzioni non le ho guardate tutte. Ma le operazioni che fai prima di ogni INT mi sembrano ok.
Forse per la tastiera non serve, ma se ricordo bene bisognerebbe abilitare il bit di interrupt (con STI). Ma guardando in rete sembra che non serva...
mi spiegheresti questo pezzo, che fa, penso inizializzi qualcosa...
DOSSEG .MODEL SMALL .STACK 200H .DATA CIFRA DB ? .CODE
Questo pezzo ci è stato assegnato per iniziare la maggior parte dei programmi dal nostro professore. A lezione ci ha spiegato che un programma assembly che prevede l’uso di routine, assume la seguente struttura. cosa faccia nell'insieme questa struttura non lo so di preciso ma prendendo appunti in aula, il professore ha spiegato le diverse funzioni delle varie componenti generiche che ci ha detto a priori di utilizzare.
io in classe ho scritto che :
DOSSEG --> Ordina il segmento usando DOS standard, che è:
1) segmenti di "codice" (in ordine alfabetico)
2) segmenti di "dati" (in ordine alfabetico)
3) 'impilare' segmenti (di nuovo, in ordine alfabetico)
MODEL --> ha bisogno di essere utilizzato se si usano i segmenti semplificati.
In poche parole MODEL Seleziona il modello da utilizzare. Questo viene usato in modo che tale
codice può essere collegato con C, PASCAL, ADA, BASIC, ASSEMBLER oppure altri programmi, e
altre lingue con facilità.ci sono diversi tipi di model ( qui riporto solo la dfinizione che il prof ha dato del model small , presente nel codice )
SMALL: Code & Data hanno un segmento individuale, ma che deve essere meno di 64 KB ciascuno
Sia il codice che dati sono vicino.
STACK 200h -->
Dice al compilatore di creare una pila byte 200h al momento dell'esecuzione del
programma. MA la dimensione scelta per lo stack non cambia la dimensione
del file sul disco. se cambiando la 200h, per esempio,
metto 400h e poi ricompilo , Le dimensioni dei file sono comunque identiche.
tale costrutto potrebbe essere sostituito da :
: MyStack SEGMENT PARA PUBLIC STACK 'STACK'
: Db 200h dup (0)
: MyStack ENDS
MA, facendo in questo modo rende i vostri eseguibili vengono resi 512 byte più grandi.
se avessimo dovuto raddoppiare a 400h, l'eseguibile sarebbe stato altri 512 byte più grande.
DATI -->
segmento senza nome 'dati' e semplificato. Se si dovesse scrivere la dichiarazione di un
segmento di questo tipo in modo regolare, dovrei scrivere qualcosa del genere:
: MyData SEGMENT PARA PUBLIC 'DATA'
:
: ... ; qui metterò i miei dati
:
: MyData ENDS
'MyData' è il nome del segmento. "public" indica invece è appunto di tipo pubblico,
,e PARA è l'allineamento dell'inizio del segmento. 'DATA'
specifica il tipo di segmento. (PARA = si avvierà su di un indirizzo che è un multiplo
di 16 )
CODICE -->
Più o meno la stessa storia, ma questo riguarda il segmento di codice.
Potrebbe essere sostituito con:
- IN MODO MASM -
: 'CODE' mycode SEGMENT PARA PUBLIC
: ...
: Mycode ENDS
- IN MODO IDEAL -
: 'CODE' mycode SEGMENT PARA PUBLIC
: ...
: FINISCE mycode; la 'mycode' è opzionale in modalità IDEAL
io in classe ho scritto che :
DOSSEG --> Ordina il segmento usando DOS standard, che è:
1) segmenti di "codice" (in ordine alfabetico)
2) segmenti di "dati" (in ordine alfabetico)
3) 'impilare' segmenti (di nuovo, in ordine alfabetico)
MODEL --> ha bisogno di essere utilizzato se si usano i segmenti semplificati.
In poche parole MODEL Seleziona il modello da utilizzare. Questo viene usato in modo che tale
codice può essere collegato con C, PASCAL, ADA, BASIC, ASSEMBLER oppure altri programmi, e
altre lingue con facilità.ci sono diversi tipi di model ( qui riporto solo la dfinizione che il prof ha dato del model small , presente nel codice )
SMALL: Code & Data hanno un segmento individuale, ma che deve essere meno di 64 KB ciascuno
Sia il codice che dati sono vicino.
STACK 200h -->
Dice al compilatore di creare una pila byte 200h al momento dell'esecuzione del
programma. MA la dimensione scelta per lo stack non cambia la dimensione
del file sul disco. se cambiando la 200h, per esempio,
metto 400h e poi ricompilo , Le dimensioni dei file sono comunque identiche.
tale costrutto potrebbe essere sostituito da :
: MyStack SEGMENT PARA PUBLIC STACK 'STACK'
: Db 200h dup (0)
: MyStack ENDS
MA, facendo in questo modo rende i vostri eseguibili vengono resi 512 byte più grandi.
se avessimo dovuto raddoppiare a 400h, l'eseguibile sarebbe stato altri 512 byte più grande.
DATI -->
segmento senza nome 'dati' e semplificato. Se si dovesse scrivere la dichiarazione di un
segmento di questo tipo in modo regolare, dovrei scrivere qualcosa del genere:
: MyData SEGMENT PARA PUBLIC 'DATA'
:
: ... ; qui metterò i miei dati
:
: MyData ENDS
'MyData' è il nome del segmento. "public" indica invece è appunto di tipo pubblico,
,e PARA è l'allineamento dell'inizio del segmento. 'DATA'
specifica il tipo di segmento. (PARA = si avvierà su di un indirizzo che è un multiplo
di 16 )
CODICE -->
Più o meno la stessa storia, ma questo riguarda il segmento di codice.
Potrebbe essere sostituito con:
- IN MODO MASM -
: 'CODE' mycode SEGMENT PARA PUBLIC
: ...
: Mycode ENDS
- IN MODO IDEAL -
: 'CODE' mycode SEGMENT PARA PUBLIC
: ...
: FINISCE mycode; la 'mycode' è opzionale in modalità IDEAL
ok capito di cosa si tratta, grazie della spiegazione.
Utilizzi uno standard del mondo Microsoft, cosa che mi è un po' oscura nei dettagli. Conosco la stessa procedura però da legare al C e compilatore gcc (perciò flusso di lavoro di trasformazione del codice assembly in standard di chiamata C).
Per questa parte di inglobazione del codice, non so aiutarti, ma se il codice di input da tastiera è sempre quello che ti ha dato il docente utilizza quello senza tante modifiche, perciò passiamo direttamente a risolvere il problema dei $2$.
Oda fai così: riassumimi i registri dove sono contenuti le cifre dalla più dignificativa alla meno di A e B, non ho voglia di tradurlo (PS: utilizzi i tag CODE).
Utilizzi uno standard del mondo Microsoft, cosa che mi è un po' oscura nei dettagli. Conosco la stessa procedura però da legare al C e compilatore gcc (perciò flusso di lavoro di trasformazione del codice assembly in standard di chiamata C).
Per questa parte di inglobazione del codice, non so aiutarti, ma se il codice di input da tastiera è sempre quello che ti ha dato il docente utilizza quello senza tante modifiche, perciò passiamo direttamente a risolvere il problema dei $2$.
Oda fai così: riassumimi i registri dove sono contenuti le cifre dalla più dignificativa alla meno di A e B, non ho voglia di tradurlo (PS: utilizzi i tag CODE).
non sono sparita
sto cercando una illuminazione divina per fare questa parte del programma guardando vecchi temi svolti!

eccomi! scusa per il ritardo ma oggi e ieri avevo due esami quindi ho dovuto abbandonare un attimo assembler!
ho mostrato il codice all'assistente del mio prof, e lui mi ha detto che la parte che gli ho mostrato ( quella del numero a 5 cifre da prendere in output) è giusta, ma lui la modificherebbe perché , mi ha detto , è unacosa che si può scrivere anche in sei righe -_-' per aiutarmi mi ha lasciato abusivamente un esercizio svolto da cui prendere spunto ( per la prima parte). secondo me però potrebbe essermi utile anche per abbozzare la seconda parte del testo (secondo la mia mente contorta)
e scusa ancora se continuo a romperti le scatole!
ho mostrato il codice all'assistente del mio prof, e lui mi ha detto che la parte che gli ho mostrato ( quella del numero a 5 cifre da prendere in output) è giusta, ma lui la modificherebbe perché , mi ha detto , è unacosa che si può scrivere anche in sei righe -_-' per aiutarmi mi ha lasciato abusivamente un esercizio svolto da cui prendere spunto ( per la prima parte). secondo me però potrebbe essermi utile anche per abbozzare la seconda parte del testo (secondo la mia mente contorta)
;Definizione costanti LUN_STRINGA EQU 05h PAGINA EQU 00h HT EQU 09h ;tabulazione LF EQU 0Ah ;nuova linea CR EQU 0Dh ;invio ; BIOS_VIDEO EQU 10H SET_VIDEO_MODE EQU 00h ;in AH MODO_TESTO EQU 03H ;in AL ; SET_CURSOR EQU 02h ;in AH W_CHR_TTY EQU 0Eh ;in AH ; DOS EQU 21H CLEAR_BUFF EQU 0CH R_KEY_BUFF EQU 0AH PRINT_STRING EQU 09H R_KEY_CHR_NE EQU 07h DSEG SEGMENT PARA PUBLIC 'DATA' TITOLO DB 'CORSO di CALCOLATORI ELETTRONICI$' ISTRUZIONI DB CR,LF,'Inserire due numeri compresi tra 0 e 64000, col primo minore del secondo$' ISTRUZIONE1 DB CR,LF,'Inserire primo numero: $' ISTRUZIONE2 DB CR,LF,'Inserire secondo numero: $' TERMINE DB CR,LF,LF,HT,'Un''altra iterazione? [S/N]$' BUFFER LABEL BYTE LUN_MAX DB LUN_STRINGA+01h LETTI DB 00h STRINGA DB LUN_STRINGA+01h DUP (' '), '$' NUM1 DW 00h NUM2 DW 00h DSEG ENDS STACKM SEGMENT PARA STACK 'STACK' ;Viene allocata una zona di DB 64 DUP('12345678') ; memoria per lo Stack: in STACKM ENDS ; tutto 64*8 bytes. ASSUME CS:CSEG,DS:DSEG,SS:STACKM CSEG SEGMENT PARA PUBLIC 'CODE' ;---------------------------------------------------------------------; ; Corpo principale del programma ; ;---------------------------------------------------------------------; MAIN PROC FAR PUSH DS ;Istruzioni da lasciare SEMPRE MOV AX,00h ; al principio dei programmi! PUSH AX ; CALL INIZIALIZZAZIONE CICLO_PRINCIPALE: CALL PROMPT CALL LETTURA_DATI CALL ELABORAZIONE CALL TEST_FINALE JNZ CICLO_PRINCIPALE RET ;Ritorno al Sistema Operativo MAIN ENDP ;---------------------------------------------------------------------; ; Procedura per leggere un tasto e memorizzarlo in CODICE_TASTO ; ; ; ; PARAMETRI: il numero e' memorizzato in CODICE_TASTO ; ; REGISTRI UTILIZZATI: AX ; ;---------------------------------------------------------------------; LETTURA_DATI PROC NEAR MOV DX,OFFSET ISTRUZIONE1 ;Sceglie la stringa (DS:DX) CALL STAMPA_STRINGA ; e la stampa. MOV AH,CLEAR_BUFF ;Servizio DOS 'Read Keyboard Char' MOV AL,R_KEY_BUFF LEA DX, BUFFER INT DOS ;Memorizza il carattere alla loca- MOV DL,LF MOV AH,02h INT DOS MOV [NUM1],00H MOV [NUM2],00H XOR AX,AX XOR BX,BX XOR CX,CX XOR DX,DX MOV SI, OFFSET STRINGA NUMBER1: MOV AX, [SI+BX] XOR AH,AH CMP AX,CR JE END1 SUB AX,'0' DEC [LETTI] MOV CL,[LETTI] MOL1: MOV DX,10d CMP CL,00h JE PASS1 MUL DX DEC CL JNZ MOL1 PASS1: INC BX ADD [NUM1],AX XOR AX, AX JMP NUMBER1 END1: MOV DL,LF MOV AH,02h INT DOS MOV DX,OFFSET ISTRUZIONE2 ;Sceglie la stringa (DS:DX) CALL STAMPA_STRINGA ; e la stampa. MOV AH,CLEAR_BUFF ;Servizio DOS 'Read Keyboard Char' MOV AL,R_KEY_BUFF LEA DX, BUFFER INT DOS ;Memorizza il carattere alla loca- MOV DL,LF MOV AH,02h INT DOS XOR AX,AX XOR BX,BX XOR CX,CX XOR DX,DX MOV SI, OFFSET STRINGA NUMBER2: MOV AX, [SI+BX] XOR AH,AH CMP AX,CR JE END2 SUB AX,'0' DEC [LETTI] MOV CL,[LETTI] MOL2: MOV DX,10d CMP CL,00h JE PASS2 MUL DX DEC CL JNZ MOL2 PASS2: INC BX ADD [NUM2],AX XOR AX, AX JMP NUMBER2 END2: MOV DL,LF MOV AH,02h INT DOS RET ;Ritorno alla procedura chiamante LETTURA_DATI ENDP ;---------------------------------------------------------------------; ; Procedura di inizializzazione ; ; ; ; REGISTRI UTILIZZATI: AX, DX, DS ; ;---------------------------------------------------------------------; INIZIALIZZAZIONE PROC NEAR MOV AX,DSEG ;Inizializzazione segmento dati MOV DS,AX ; tramite il registro AX. MOV AH,SET_VIDEO_MODE ;Servizio BIOS 'Set Video Mode': MOV AL,MODO_TESTO ; modo testo 80 x 25, colori INT BIOS_VIDEO ; MOV DX,0315h ;Imposta riga (DH) e colonna (DL) CALL SPOSTA_CURSORE ;Muove il cursore nella pos scelta MOV DX,OFFSET TITOLO ;Sceglie la stringa (DS:DX) CALL STAMPA_STRINGA ; e la stampa. MOV DX,0606h ;Imposta riga (DH) e colonna (DL) CALL SPOSTA_CURSORE ;Muove il cursore nella pos scelta RET ;Ritorno alla procedura chiamante INIZIALIZZAZIONE ENDP ;---------------------------------------------------------------------; ; Procedura per l'elaborazione ; ; ; ; REGISTRI UTILIZZATI: AX, DX, DS ; ;---------------------------------------------------------------------; ELABORAZIONE PROC NEAR XOR AX,AX XOR BX,BX XOR CX,CX XOR DX,DX MOV DX,[NUM1] ;inserisco in DX il numero inferiore MOV BX,[NUM2] ;inserisco in BX il numero superiore ;MOV DX,0000000000000000b ;questi sono di prova ;MOV BX,0000111100000110b ;attraverso dei confronti stabilisco qual'Ë il numero massimo di 1 ;(siccome un numero composto da tutti e soli 1 nelle posizioni meno significative ;Ë sicuramente il numero pi˘ basso con quella quantit‡ di 1) MOV CX,1111111111111111b MOV AL,16d PREPARAZIONE: SHR CX,01h DEC AL CMP BX,CX JGE CAMBIA_CONF CMP AL,01d JNE PREPARAZIONE ;se necessario alzando il limite inferiore elimino tutti i numeri ;che sicuramente hanno meno 1 del risultato trovato CAMBIA_CONF: CMP DX,CX JL CAMBIA_INF JMP PRE CAMBIA_INF: MOV DX,CX JMP PRE ;conto il numero di 1 di ogni dato e lo confronto con il precedente risultato PRE: MOV CH,AL JMP CONTA_BITS PEZZA: DEC CH MOV DX,[NUM1] CONTA_BITS: MOV CL,16d XOR AL,AL CONTEGGIO: ROR DX,01d ADC AL,00h DEC CL JNZ CONTEGGIO CMP AL,CH JE STAMPA_NUMERO PREP_SUCCES: ADD DX,01b CMP DX,BX JG FINE JMP CONTA_BITS ;se un numero ha il valore massimo di 1 allora lo stampo STAMPA_NUMERO: MOV CL,16d CICLO_BINARIO5: XOR AL,AL ROL DX,01h ADC AL,AL ADD AL,'0' MOV AH,0Eh INT 10h DEC CL JNZ CICLO_BINARIO5 MOV AL,10d MOV AH,0Eh INT 10h MOV AL,13d MOV AH,0Eh INT 10h JMP PREP_SUCCES FINE: RET ELABORAZIONE ENDP ;---------------------------------------------------------------------; ; Procedura per stampare il messaggio iniziale ; ; ; ; REGISTRI UTILIZZATI: DX, AH ; ;---------------------------------------------------------------------; PROMPT PROC NEAR MOV DX,OFFSET ISTRUZIONI ;Sceglie la stringa (DS:DX) CALL STAMPA_STRINGA ; e la stampa. RET ;Ritorno alla procedura chiamante PROMPT ENDP ;---------------------------------------------------------------------; ; Procedura per determinare se occorre un'altra iterazione ; ; ; ; REGISTRI UTILIZZATI: AX, DX ; ; CODICI DI RITORNO: ; ; Zero flag = 0 -> un'altra iterazione ; ; Zero flag = 1 -> esci dal programma ; ;---------------------------------------------------------------------; TEST_FINALE PROC NEAR MOV DX,OFFSET TERMINE ;Sceglie la stringa (DS:DX) CALL STAMPA_STRINGA ; e la stampa. CALL LETTURA_SN ;Legge da tastiera senza echo. CMP AL,'n' ;Modifica il FLAG 'Z' RET ;Ritorno alla procedura chiamante TEST_FINALE ENDP ;*********************************************************************; ;* Procedure di basso livello di interfaccia *; ;*********************************************************************; ;---------------------------------------------------------------------; ; Procedura per leggere da tastiera uno dei tasti 'S' o 'N' ; ; ritorna solo dopo aver letto almeno uno dei due tasti ; ; ; ; REGISTRI UTILIZZATI: AX ; ; CODICI DI RITORNO: in AL il codice ASCII letto ; ;---------------------------------------------------------------------; LETTURA_SN PROC NEAR NUOVA_LETTURA: MOV AH,R_KEY_CHR_NE ;Servizio DOS 'Read Keyboard Char INT DOS ; Without Echo' OR AL,00100000b ;Converte in minuscolo CMP AL,'n' ;Se il tasto premuto e' 'N' esce JZ FINE_LETTURA ; dalla procedura CMP AL,'s' ;Se non e' 'S', ne legge JNZ NUOVA_LETTURA ; un altro FINE_LETTURA: RET ;Ritorno alla procedura chiamante LETTURA_SN ENDP ;---------------------------------------------------------------------; ; Procedura per spostare il cursore ; ; ; ; PARAMETRI: le coordinate della posizione del cursore sono memoriz- ; ; zate nel registro DX: DH riga, DL colonna. ; ; VARIABILI: il numero della pagina e` memorizzato in PAGINA ; ; REGISTRI UTILIZZATI: AH, BH ; ;---------------------------------------------------------------------; SPOSTA_CURSORE PROC NEAR MOV BH,PAGINA ;Pagina video attiva. MOV AH,SET_CURSOR ;Servizio BIOS 'Set Cursor INT BIOS_VIDEO ; Position' RET ;Ritorno alla procedura chiamante SPOSTA_CURSORE ENDP ;---------------------------------------------------------------------; ; Procedura per stampare una stringa ; ; ; ; PARAMETRI: l'indirizzo della stringa e` memorizzato in DS:DX ; ; REGISTRI UTILIZZATI: AH, DX ; ;---------------------------------------------------------------------; STAMPA_STRINGA PROC NEAR MOV AH,PRINT_STRING ;Servizio DOS 'Print String'; la INT DOS ; stringa e' puntata da DS:DX. RET ;Ritorno alla procedura chiamante STAMPA_STRINGA ENDP CSEG ENDS END MAIN
e scusa ancora se continuo a romperti le scatole!
eeeehh è un po' lunghetto 
ok quando ho tempo vediamo...

ok quando ho tempo vediamo...
@ellosma: Usa il tag code per rendere più chiaro il codice che posti! Questa volta l'ho fatto io.. Prendi l'abitudine di commentare molto di più il tuo codice assembly (scrivendo in particolare la logica del programma invece che commenti sugli aspetti più tecnici).
Nella mia mente contorta ho pensato di risolvere il conteggio dei "2" in questo modo, ma il professore ha detto che è un ragionamento molto contorto e che il problema è risolvibile molto più facilmente di così

CHECK2(B): q = 0; // Inizio l'esame della cifra più significativa di B. LOOP: if (q >= k) j RETURN // Se ho letto tutte le cifre di B passo al prossimo numero. if ((B % 10^(k - q)) / 10^(k - q - 1) == 2) count += 1; // Incremento il contatore se la cifra in esame è un 2. q += 1; // Scorro alla cifra subito meno significativa. j LOOP // Esamino la cifra subito meno significativa. RETURN: if (B == A - 1) j EXIT // Torno al main se ho finito di calcolare le ricorrenze di 2 in tutti i numeri tra B ed A estremi compresi. count += CHECK2(B - 1) // Altrimenti sommo al contatore ottenuto per il numero corrente (già sommato ai contatori dei numeri superiori) il contatore del numero inferiore. EXIT: return count // Restituisco al main il contatore col risultato definitivo
"ellosma":
Nella mia mente contorta ho pensato di risolvere il conteggio dei "2" in questo modo, ma il professore ha detto che è un ragionamento molto contorto e che il problema è risolvibile molto più facilmente di così![]()
CHECK2(B): q = 0; // Inizio l'esame della cifra più significativa di B. LOOP: if (q >= k) j RETURN // Se ho letto tutte le cifre di B passo al prossimo numero. if ((B % 10^(k - q)) / 10^(k - q - 1) == 2) count += 1; // Incremento il contatore se la cifra in esame è un 2. q += 1; // Scorro alla cifra subito meno significativa. j LOOP // Esamino la cifra subito meno significativa. RETURN: if (B == A - 1) j EXIT // Torno al main se ho finito di calcolare le ricorrenze di 2 in tutti i numeri tra B ed A estremi compresi. count += CHECK2(B - 1) // Altrimenti sommo al contatore ottenuto per il numero corrente (già sommato ai contatori dei numeri superiori) il contatore del numero inferiore. EXIT: return count // Restituisco al main il contatore col risultato definitivo
Ciao, un possibile algoritmo che potresti usare è questo (Python):
def count(a, b=2): res = 0 while a > 0: if a % 10 == b: res += 1 a /= 10 return res if __name__ == '__main__': digit = 2 up_lim = 2003 down_lim = 2001 values = range(down_lim, up_lim+1) result = sum(map(count,values)) print result
Purtroppo sono un po' arrugginito per quanto riguarda l'assembly (e all'uni devo ancora farlo)

Comunque,in breve, l'algoritmo che utilizzo incrementa un contatore ogni qualvolta l'ultima cifra del numero è uguale a due.
Ovviamente alla fine di ogni 'giro' del ciclo il numero n viene diviso per 10, così che la cifra appena analizzata sparisca e si possa passare alla successiva.
Il tutto viene ripetuto finchè il risultato della divisione non diventa zero.
Spero di esserti stato di aiuto!
Ciao,
Shulz