Appendice C.   Le operazioni di I/O con l'assembly AT&T

L'ingresso e uscita di dati nel linguaggio assembly riguardano esclusivamente stringhe; in caso si debbano inserire o visualizzare valori numerici occorre operare le necessarie conversioni da programma (più avanti verranno mostrati esempi a tale proposito).

Per gestire l'input e l'output si deve ricorrere a delle funzioni del sistema operativo, dette «chiamate di sistema» o syscall.

Per la lettura e la scrittura si usano rispettivamente le chiamate di sistema corrispondenti alle funzioni read e write del linguaggio c (si ricordi che il sistema operativo GNU/Linux è scritto in c, quindi le chiamate di sistema hanno una stretta corrispondenza con le funzioni a basso livello di tale linguaggio).

Per avere maggiori dettagli circa queste due funzioni si può usare il manuale in linea di GNU/Linux, ad esempio con il comando:

man read

Un esempio di chiamata di sistema molto usata (forse la più usata) è quella per l'uscita dal programma, attivata ponendo il valore 1 nel registro eax prima del richiamo dell'interruzione software 8016 (istruzione: int $0x80).

Per attivare la chiamata di sistema per la lettura occorre ovviamente richiamare la stessa interruzione software, valorizzando però i registri in modo più articolato:

Dopo l'esecuzione della chiamata, nel registro eax, viene ritornata la lunghezza effettiva della stringa letta, comprensiva del carattere «invio» ('\n') con cui si conclude la sua immissione.

Per la scrittura a video la situazione è simile e si devono usare:

Dopo l'esecuzione della chiamata, nel registro eax, viene ritornata la lunghezza effettiva della stringa visualizzata.

Come primo esempio vediamo un programma che stampa a video il messaggio «Ciao a tutti».(1)

/*
Programma:     input-output1.s
Autore:        FF
Data:          gg/mm/aaaa
Descrizione:   Esempio di scrittura a video
*/
.data
messaggio:   .string "Ciao a tutti\n" # \n per andare a capo
.text
.globl _start
_start:
   movl $4, %eax
   movl $1, %ebx
   movl $messaggio, %ecx   # $messaggio contiene ind. di inizio della stringa
   movl $13, %edx          
   int  $0x80
fine:
   movl $1, %eax
   int  $0x80

Essendo questo un programma che prevede una qualche interazione durante la sua esecuzione, nella figura C.2 possiamo vedere, dopo i comandi per la sua traduzione e linking, l'effetto della sua esecuzione con la stampa a video della stringa.

Figura C.2.

figure/asm-esec-io01

Nel prossimo esempio vogliamo invece stampare a video una stringa immessa da tastiera.(2)

/*
Programma:     input-output2.s
Autore:        FF
Data:          gg/mm/aaaa
Descrizione:   Esempio di lettura da tastiera e scrittura a video
*/
.bss
stringa:   .string ""                         # stringa di output
.data
invito:    .string "Immettere una stringa: "  # senza a capo  
msgout:    .string "Stringa immessa: "        # senza a capo
app:       .long   0                          # appoggio per lunghezza effettiva
.text
.globl _start
_start:
// messaggio di invito
   movl $4, %eax
   movl $1, %ebx
   movl $invito, %ecx   
   movl $23, %edx
   int  $0x80
// lettura stringa da tastiera
   movl $3, %eax
   movl $0, %ebx
   movl $stringa, %ecx   
   movl $100, %edx     # 100 = lunghezza massima stringa          
   int  $0x80
   movl %eax, (app)    # lunghezza effettiva in app per la successiva stampa
// stampa a video messaggio preliminare
   movl $4, %eax
   movl $1, %ebx
   movl $msgout, %ecx   
   movl $18, %edx
   int  $0x80
// stampa a video stringa
   movl $4, %eax
   movl $1, %ebx
   movl $stringa, %ecx   
   movl (app), %edx
   int  $0x80
fine:
   movl $1, %eax
   int  $0x80

Notiamo l'utilizzo del segmento bss, nel quale viene dichiarata l'etichetta da usare per l'output.

Nella figura C.4 vediamo il risultato dell'esecuzione del programma.

Figura C.4.

figure/asm-esec-io02

Come detto, in assembly l'input e l'output riguardano solo stringhe: se inseriamo un valore numerico questo viene acquisito come una stringa composta dai simboli corrispondenti alle cifre da cui è costituito; viceversa se vogliamo visualizzare un valore dobbiamo trasformarlo nella corrispondente stringa.

Nel prossimo esempio vengono mostrati entrambi questi procedimenti in un programma che accetta da tastiera due valori interi, ne calcola il prodotto ed emette a video il risultato.

In questo caso ripristiniamo la numerazione delle righe del listato perché i procedimenti di conversione non sono banali e meritano qualche spiegazione più accurata.(3)

      1 /*
      2 Programma:     input-output3.s
      3 Autore:        FF
      4 Data:          gg/mm/aaaa
      5 Descrizione:   Acquisizione e stampa a video di valori numerici
      6 */
      7 .bss
      8 stringa:   .string ""                         # stringa per input
      9 .data
     10 invito:    .string "Immettere un valore: "    # senza a capo  
     11 msgout:    .string "Risultato del prodotto: " # senza a capo
     12 lun:       .long   0                          # lunghezza input
     13 val1:      .byte   0                          # primo valore di input
     14 val2:      .byte   0                          # secondo valore di input
     15 strout:    .string "     \n"                  # stringa per output (5 car)
     16 .text
     17 .globl _start
     18 _start:
     19 // input primo valore
     20 // messaggio di invito
     21    movl $4, %eax
     22    movl $1, %ebx
     23    movl $invito, %ecx   
     24    movl $21, %edx
     25    int  $0x80
     26 // lettura stringa da tastiera
     27    movl $3, %eax
     28    movl $0, %ebx
     29    movl $stringa, %ecx   
     30    movl $4, %edx       # 4 = lun. max stringa (3 + 1 per invio)          
     31    int  $0x80
     32    movl %eax, (lun)    # lunghezza effettiva immessa
     33 // converti primo input
     34    mov $0, %esi
     35    xorw %ax, %ax
     36    movw $0xa, %bx
     37    movl (lun), %ecx
     38    decl %ecx            # per non contare anche invio
     39 ciclo1:
     40    mulw %bx             # esegue %ax = %ax * %bx ( = 10)
     41    movb stringa(%esi), %dl
     42    subb $0x30, %dl
     43    addb %dl, %al
     44    adcb $0, %ah
     45    inc  %esi 
     46    loopl ciclo1
     47    movb %al, (val1)
     48 // input secondo valore   
     49 // messaggio di invito
     50    movl $4, %eax
     51    movl $1, %ebx
     52    movl $invito, %ecx   
     53    movl $21, %edx
     54    int  $0x80
     55 // lettura stringa da tastiera
     56    movl $3, %eax
     57    movl $0, %ebx
     58    movl $stringa, %ecx   
     59    movl $4, %edx       # 3 = lun. max stringa (3 + 1 per invio)          
     60    int  $0x80
     61    movl %eax, (lun)    # lunghezza effettiva immessa
     62 // converti secondo input
     63    mov $0, %esi
     64    xorw %ax, %ax
     65    movw $0xa, %bx
     66    movl (lun), %ecx
     67    decl %ecx            # per non contare anche invio
     68 ciclo2:
     69    mulw %bx             # esegue %ax = %ax * %bx ( = 10)
     70    movb stringa(%esi), %dl
     71    subb $0x30, %dl
     72    addb %dl, %al
     73    adcb $0, %ah
     74    inc  %esi 
     75    loopl ciclo2
     76    movb %al, (val2)
     77 // elaborazione
     78    movb (val1), %al
     79    mulb (val2)         # il risultato in %ax
     80 // converte valore del ris in stringa
     81    movw $0xa, %bx    
     82    movl $4, %esi
     83 converti:   
     84    xor %dx, %dx
     85    divw %bx             # divido ris per 10 il resto va in %dl
     86    addb $0x30, %dl      # sommo 48 per avere cod. ascii
     87    movb %dl, strout(%esi)
     88    dec %esi
     89    orw %ax, %ax        # se quoziente %ax zero - fine
     90    jne converti
     91 // stampa a video messaggio preliminare
     92    movl $4, %eax
     93    movl $1, %ebx
     94    movl $msgout, %ecx   
     95    movl $25, %edx
     96    int  $0x80
     97 // stampa a video della stringa del risultato
     98    movl $4, %eax
     99    movl $1, %ebx
    100    movl $strout, %ecx   
    101    movl $6, %edx
    102    int  $0x80
    103 fine:
    104    movl $1, %eax
    105    int  $0x80

Nelle righe da 21 a 25 il programma visualizza un messaggio con la richiesta di inserimento di un valore; tale inserimento avviene grazie alle righe da 27 a 31, mentre alla riga 32 si salva la lunghezza effettiva della stringa immessa (compreso l'invio).

Il numero immesso in stringa è, appunto, una stringa e deve essere convertito nel corrispondente valore; il procedimento usato può essere così riassunto:

Le righe da 34 a 46 realizzano quanto appena descritto; in dettaglio:

A questo punto in ax abbiamo il valore da spostare in val1 (riga 47).

Dalla riga 48 alla riga 76 tutto il procedimento (messaggio, input, conversione) viene ripetuto per il secondo valore con l'ovvia differenza che stavolta esso viene spostato in val2.

Le righe 78 e 79 svolgono la moltiplicazione tra i due valori; il risultato, che è in ax, deve essere convertito in stringa.

Per fare ciò si usa il seguente algoritmo:

Le righe da 81 a 90 eseguono quanto appena illustrato; in dettaglio:

Conclusa la conversione del valore da visualizzare, il programma emette un opportuno messaggio (righe da 92 a 96) e poi la stringa contenente il risultato (righe da 98 a 102).

Nella figura C.6 vediamo gli effetti dell'esecuzione del programma.

Figura C.6.

figure/asm-esec-io03


1) una copia di questo file, dovrebbe essere disponibile anche qui: <allegati/programmi-assembly/input-output1.s>.

2) una copia di questo file, dovrebbe essere disponibile anche qui: <allegati/programmi-assembly/input-output2.s>.

3) una copia di questo file, dovrebbe essere disponibile anche qui: <allegati/programmi-assembly/input-output3.s>.