;--------------------------------------------------; ; file jcond.asm ; ; programma di esempio per le istruzioni Jcond ; ;--------------------------------------------------; ; nasm -f obj jcond.asm -l jcond.lst ; ; tlink jcond.obj + exelib.obj ; ; (oppure link /map jcond.obj + exelib.obj) ; ;--------------------------------------------------; ;########### direttive per l'assembler ############ CPU 386 ; set di istruzioni a 32 bit %include "exelib.inc" ; inclusione libreria di I/O ;######### dichiarazione tipi e costanti ########## %assign STACK_SIZE 0400h ; 1024 byte per lo stack ;################ segmento dati ################### SEGMENT DATASEGM ALIGN=16 PUBLIC USE16 CLASS=DATA ;----- inizio definizione variabili statiche ------ vectWord resw 20 strSign1 db "e' positivo.", 0 strSign2 db "e' negativo.", 0 strOverf db "e' in OVERFLOW!", 0 strCmp1 db "e' maggiore di", 0 strCmp2 db "e' minore di", 0 strCmp3 db "e' uguale a", 0 stringaC db 'Stringa alfanumerica da convertire in maiuscolo - [123]', 0 strBuffer resb 128 ;------- fine definizione variabili statiche ------ ;############### segmento codice ################## SEGMENT CODESEGM ALIGN=16 PUBLIC USE16 CLASS=CODE ..start: ; entry point mov ax, DATASEGM ; trasferisce DATASEGM mov ds, ax ; in DS attraverso AX ;------- inizio blocco principale istruzioni ------ call far set80x50 ; modo video 80 righe, 50 colonne call far clearScreen ; pulisce lo schermo push ds ; copia DS pop es ; in ES (per writeString) ; ATTENZIONE! Se si ha la necessita' di modificare ES, bisogna preservarne ; il vecchio contenuto; la procedura writeString presuppone, infatti, che ; ES contenga la componente Seg dell'indirizzo della stringa da visualizzare! ; ------------------------------------------------------------------- ; Un blocco condizionale "IF - ELSE" permette di effettuare una ; scelta basata sul risultato prodotto dalla valutazione di una ; espressione booleana; la struttura generale del blocco ; condizionale e' la seguente: ; IF (espressione) ; blocco istruzioni 1 ; ELSE ; blocco istruzioni 2 ; Innanzi tutto viene valutata "espressione" e, se risulta TRUE, viene ; eseguito il "blocco istruzioni 1", con conseguente uscita dall'intero ; blocco condizionale; se "espressione" e' FALSE, viene eseguito il ; "blocco istruzioni 2" associato all'ELSE. ; I blocchi condizionali IF - ELSE possono essere anche innestati; cio' ; significa che il "blocco istruzioni 1" e/o il "blocco istruzioni 2" ; possono essere, a loro volta, degli ulteriori blocchi condizionali! ; ------------------------------------------------------------------- ; Utilizziamo dei blocchi condizionali IF - ELSE innestati, per conoscere ; il segno del risultato di una somma tra interi con segno a 16 bit in ; complemento a 2; lo zero viene trattato come numero positivo. Il seguente ; codice e' anche in grado di rilevare e segnalare un eventuale overflow; ; inserendo in AX e BX una qualsiasi coppia lecita di numeri interi con ; segno a 16 bit in complemento a 2, possiamo notare che il programma si ; comporta sempre in modo corretto. ; Si noti che il secondo blocco condizionale IF - ELSE, e' innestato ; nella condizione ELSE del primo blocco condizionale. ; In modalita' reale, le istruzioni Jcond non modificano nessun ; campo del Flags Register, per cui possono essere utilizzate anche ; in sequenza! mov ax, -18421 ; ax = primo addendo mov bx, +11934 ; bx = secondo addendo add ax, bx ; ax = ax + bx jo short overflow_label ; IF (OF == 1) no_overflow_label: ; ELSE js short negative_label ; IF (SF == 1) positive_label: ; ELSE mov di, strSign1 ; es:di punta a strSign1 jmp short output_label1 ; salta a output_label1 negative_label: ; il risultato e' negativo mov di, strSign2 ; es:di punta a strSign2 jmp short output_label1 ; salta a output_label1 overflow_label: ; il risultato e' in overflow mov di, strOverf ; es:di punta a strOverf output_label1: ; output dei risultati mov dx, 0400h ; riga 4, colonna 0 call far writeSdec16 ; output somma (ax) mov dx, 0407h ; riga 4, colonna 7 call far writeString ; output stringa selezionata ; Un blocco condizionale "IF - ELSE IF - ELSE" e' la generalizzazione ; del blocco IF - ELSE, e permette quindi di effettuare un numero ; arbitrario di scelte basate sul risultato prodotto dalla valutazione ; di una espressione booleana; la struttura generale del blocco ; condizionale e' la seguente: ; IF (espressione 1) ; blocco istruzioni 1 ; ELSE IF (espressione 2) ; blocco istruzioni 2 ; ELSE IF (espressione 3) ; blocco istruzioni 3 ; ELSE IF (espressione 4) ; .................. ; ELSE ; blocco istruzioni n ; In teoria, si puo' utilizzare un numero illimitato di condizioni ; "ELSE IF". ; Le varie "espressioni" vengono valutate in sequenza e, se una di esse ; risulta TRUE, viene eseguito il relativo "blocco istruzioni", con ; conseguente uscita dall'intero blocco condizionale; se nessuna delle ; "espressioni" risulta TRUE, viene eseguito il "blocco istruzioni" ; associato all'ultimo ELSE. ; Come al solito, i blocchi condizionali possono essere anche innestati; ; cio' significa che il "blocco istruzioni 1", il "blocco istruzioni 2", ; etc, possono essere, a loro volta, degli ulteriori blocchi condizionali! ; ----------------------------------------------------------------------- ; Utilizziamo un blocco condizionale IF - ELSE IF - ELSE per conoscere il ; risultato di una comparazione aritmetica tra due numeri interi senza ; segno contenuti in AX e BX; inserendo in AX e BX qualsiasi coppia lecita ; di numeri interi senza segno a 16 bit, possiamo notare che il programma ; si comporta sempre in modo corretto. mov ax, 16312 ; ax = 3FB8h mov bx, 48173 ; bx = BC2Dh cmp ax, bx ; compara ax con bx ja short above_label ; IF (ax maggiore di bx) jb short below_label ; ELSE IF (ax minore di bx) equal_label1: ; ELSE mov di, strCmp3 ; es:di punta a strCmp3 jmp short output_label2 ; salta a output_label2 above_label: ; ax e' maggiore di bx mov di, strCmp1 ; es:di punta a strCmp1 jmp short output_label2 ; salta a output_label2 below_label: ; ax e' minore di bx mov di, strCmp2 ; es:di punta a strCmp2 output_label2: ; output dei risultati mov dx, 0600h ; riga 6, colonna 0 call far writeUdec16 ; output ax = DEST mov dx, 0606h ; riga 6, colonna 6 call far writeString ; output stringa selezionata mov dx, 0615h ; riga 6, colonna 21 mov ax, bx ; ax = bx = SRC call far writeUdec16 ; output ax = SRC ; Utilizziamo un blocco condizionale IF - ELSE IF - ELSE per conoscere il ; risultato di una comparazione aritmetica tra due numeri interi con segno ; a 16 bit in complemento a 2, contenuti in AX e BX; inserendo in AX e BX ; qualsiasi coppia lecita di numeri interi con segno a 16 bit in complemento ; a 2, possiamo notare che il programma si comporta sempre in modo corretto. mov ax, +16312 ; ax = 3FB8h mov bx, -17363 ; bx = BC2Dh cmp ax, bx ; compara ax con bx jg short greater_label ; IF (ax maggiore di bx) jl short less_label ; ELSE IF (ax minore di bx) equal_label2: ; ELSE mov di, strCmp3 ; es:di punta a strCmp3 jmp short output_label3 ; salta a output_label3 greater_label: ; ax e' maggiore di bx mov di, strCmp1 ; es:di punta a strCmp1 jmp short output_label3 ; salta a output_label3 less_label: ; ax e' minore di bx mov di, strCmp2 ; es:di punta a strCmp2 output_label3: ; output dei risultati mov dx, 0800h ; riga 8, colonna 0 call far writeSdec16 ; output ax = DEST mov dx, 0807h ; riga 8, colonna 7 call far writeString ; output stringa selezionata mov dx, 0816h ; riga 8, colonna 22 mov ax, bx ; ax = bx = SRC call far writeSdec16 ; output ax = SRC ; Si definisce iterazione (o ciclo, o loop), una tecnica che permette di ; ripetere (iterare) per un certo numero di volte consecutive, uno stesso ; blocco di istruzioni; le iterazioni vengono largamente utilizzate per ; gestire vettori di qualsiasi natura. ; Nel caso piu' semplice, il numero n di iterazioni da effettuare e' ; noto in partenza; questa situazione puo' essere gestita facilmente con ; un cosiddetto "ciclo FOR". ; La struttura generale del ciclo FOR e' la seguente: ; RIPETI PER (FOR) i che va da 0 a (n - 1) ; blocco istruzioni ; La variabile "i" viene definita "contatore", mentre "n" rappresenta il ; numero di cicli da effettuare; nel caso piu' frequente, il contatore ; "i" viene inizializzato con il valore 0, e viene incrementato di 1 ad ; ogni ciclo finche' non raggiunge il valore "n" (condizione di uscita ; dal ciclo). ; Si puo' seguire anche un procedimento inverso che consiste ; nell'inizializzare il contatore con il valore "n"; il contatore viene ; poi decrementato di 1 ad ogni ciclo finche' non raggiunge il valore 0 ; (condizione di uscita dal ciclo). ; Molto spesso, il numero di iterazioni da effettuare non e' noto a ; priori; in questo caso si utilizzano i cicli DO - WHILE e WHILE - DO. ; La struttura generale del ciclo DO - WHILE e' la seguente: ; DO ; blocco istruzioni ; WHILE "espressione" ; Innanzi tutto viene eseguito il "blocco istruzioni"; successivamente ; viene valutata "espressione" e, se risulta TRUE, si ricomincia da ; capo (si riesegue cioe', "blocco istruzioni"); questa situazione si ; ripete finche' (while) "espressione" rimane TRUE. ; Quando "espressione" diventa FALSE, il ciclo termina; ovviamente, se ; "espressione" rimane sempre TRUE, il ciclo continua all'infinito ; (loop infinito)! ; Come si puo' notare, anche se "espressione" e' FALSE in partenza, il ; ciclo DO - WHILE viene eseguito almeno una volta! ; (Il ciclo DO - WHILE del C, equivale al ciclo REPEAT - UNTIL del ; Pascal.) ; La struttura generale del ciclo WHILE - DO e' la seguente: ; WHILE (espressione) ; blocco istruzioni ; DO ; Innanzi tutto viene valutata "espressione" e, se risulta TRUE, viene ; eseguito il "blocco istruzioni"; questa situazione si ripete finche' ; (while) "espressione" rimane TRUE. ; Quando "espressione" diventa FALSE, il ciclo termina; ovviamente, se ; "espressione" rimane sempre TRUE, il ciclo continua all'infinito ; (loop infinito)! ; Come si puo' notare, se "espressione" e' FALSE in partenza, il ciclo ; WHILE - DO non viene mai eseguito! ; -------------------------------------------------------------------- ; Utilizziamo un ciclo FOR per inizializzare e visualizzare un vettore ; di 20 elementi di tipo intero senza segno a 16 bit; in questo caso, il ; ciclo FOR e' quello piu' indicato in quanto conosciamo in partenza il ; numero (20) di iterazioni da effettuare. ; Gli elementi del vettore vengono inizializzati con il quadrato ; del loro indice 0, 1, 2, 3, ... ; Come contatore viene utilizzato CX (counter register). %assign NUM_LOOPS 20 ; numero cicli ; inizializzazione del vettore xor bx, bx ; bx = 0 xor si, si ; si = 0 (indice vettore) mov cx, NUM_LOOPS ; cx = numero cicli initvector_loop: ; FOR cx che va da 20 a 0 mov ax, si ; ax = indice vettore mul ax ; dx:ax = ax * ax mov [vectWord+bx], ax ; salva nel vettore inc si ; incremento indice add bx, 2 ; prossima WORD di vectWord dec cx ; decremento contatore jnz short initvector_loop ; controllo loop (CX == 0) ? ; output del vettore xor bx, bx ; bx = 0 xor si, si ; si = 0 (indice vettore) mov dx, 0A00h ; riga, colonna iniziali mov cx, NUM_LOOPS ; cx = numero cicli outputvector_loop: ; FOR cx che va da 20 a 0 mov ax, si ; ax = indice vettore call far writeUdec8 ; output indice add dl, 5 ; colonna 5 mov ax, [vectWord+bx] ; elemento da visualizzare call far writeUdec16 ; output elemento xor dl, dl ; colonna 0 inc dh ; incremento riga inc si ; incremento indice add bx, 2 ; prossima WORD di vectWord dec cx ; decremento contatore jnz short outputvector_loop ; controllo loop (CX == 0) ? ; Utilizziamo un ciclo WHILE - DO per convertire in maiuscolo una stringa C; ; e' necessario utilizzare questo ciclo in quanto non conosciamo in anticipo ; la lunghezza della stringa. ; Il ciclo WHILE - DO e' preferibile al DO - WHILE in quanto la stringa ; potrebbe anche avere una lunghezza nulla; in un caso del genere, il ; "blocco istruzioni" non viene mai eseguito! ; I codici ASCII delle lettere minuscole vanno da 97 a 122 (da 'a' a 'z'), ; mentre i codici ASCII delle lettere maiuscole vanno da 65 a 90 (da 'A' a ; 'Z'); e' importante notare che in entrambi i casi, tali codici sono ; consecutivi e contigui! ; Data una lettera minuscola e la corrispondente lettera maiuscola, la ; differenza tra i rispettivi codici ASCII e' 32; ad esempio: ; 'a' - 'A' = 32 ; Di conseguenza: 'A' = 'a' - 32. ; Il nostro ciclo verifica innanzi tutto che un elemento della stringa sia ; una lettera minuscola; solo in questo caso viene effettuata la conversione ; in maiuscolo che consiste nel sottrarre 32 all'elemento stesso mov di, stringaC ; es:di punta a stringaC mov dx, 2000h ; riga 20h, colonna 00h call far writeString ; output stringa da convertire strconvert_loop: ; WHILE ([ds:di] != 0) mov al, [di] ; al = codice ASCII da esaminare test al, al ; (al == 0) ? jz short exit_loop ; if (al == 0) jump exit_loop cmp al, 'a' ; confronta al con 'a' jb short next_char ; non e' una minuscola cmp al, 'z' ; confronta al con 'z' ja short next_char ; non e' una minuscola sub al, 32 ; converte in maiuscolo mov [di], al ; salva in stringaC next_char: inc di ; prossimo elemento di stringaC jmp short strconvert_loop ; DO exit_loop: mov di, stringaC ; es:di punta a stringaC mov dx, 2200h ; riga 22h, colonna 00h call far writeString ; output stringa convertita ; Utilizziamo un ciclo DO - WHILE per copiare stringaC in strBuffer; e' ; possibile utilizzare questo ciclo in quanto dobbiamo copiare in ; strBuffer anche lo zero finale di stringaC (in sostanza, il ciclo deve ; essere eseguito almeno una volta). ; Naturalmente, strBuffer deve avere lo spazio sufficiente per contenere ; stringaC; in caso contrario, invadiamo aree riservate della memoria! mov di, stringaC ; ds:di punta a stringaC mov si, strBuffer ; ds:si punta a strBuffer strcopy_loop: ; DO mov al, [di] ; al = elemento da copiare mov [si], al ; copia in strBuffer inc di ; prossimo elemento di stringaC inc si ; prossimo elemento di strBuffer test al, al ; (al == 0) ? jnz short strcopy_loop ; WHILE (al != 0) mov di, strBuffer ; es:di punta a strBuffer mov dx, 2400h ; riga 24h, colonna 00h call far writeString ; output strBuffer ;-------- fine blocco principale istruzioni ------- mov ah, 4ch ; servizio Terminate Program mov al, 00h ; exit code = 0 int 21h ; chiama i servizi DOS ;################# segmento stack ################# SEGMENT STACKSEGM ALIGN=16 STACK USE16 CLASS=STACK resb STACK_SIZE ; 1024 byte per lo stack ;##################################################