AngoLinux

Visualizzazione di configurazioni rotative sui LED connessi alla parallela

- A cura del Prof. Stefano Salvi -


È ora il momento di affrontare l'assembler 8086.

Il testo dell'esercizio è il seguente:

Scrivere un programma Assembler per 8086 che visalizzi su 8 LED connessi ai dati della porta parallela (indirizzo 0x378) una configurazione con due LED accesi che scorrono in direzioni opposte, visualizzando due configurazioni al secondo ed eseguendo un numero predeterminato di cicli completi.

NOTE:

  • La codifica delle istruzioni in formato AT&T, per l'assemblatore di Linux sono disponibili nel Reference delle istruzioni 80386 in formato AT&T (in inglese)
  • Chiamate di sistema al Kernel di Linux descrive come chiamare il sistema per ottenere servizi e quali sono i servizi che possono essere ottenuti.
  • L'esecuzione di un programma in Linux avviene a partire dalla label _start. Questo simbolo devenir dichiarato .global, in modo che possa venir utilizzato dal sistema.
  • Le intruzioni inb ed outb sono istruzioni privilegiate, che non possono essere eseguite da un programma, se prima il sistema non ha riservato le porte su cui operare al programma stesso.
    Per riservare delle porte al programma occorre utilizzare la chiamata di sistema ioperm (costante __NR_ioperm, codice 101).
    Questa chiamata può essere eseguita solo dall'utente root. Se viene eseguita da un'altro utente, il programma genererà un'errore di vario tipo, come ad esempio Segmentation fault
  • Per poter usare la porta parallela in uscita occorre prima programmarla azzerando i bit da 4 a 7 della porta di controllo 0x37a.
  • Per introdurre un ritardo nell'esecuzione è possibile eseguire un ciclo di operazioni inutili per perdere tempo. Questa soluzione carica inutilmente il sistema e produce ritardi non precisi.
    Una migliore soluzione è quella di invocare la funzione di sistema nanosleep (costante __NR_nanosleep, codice 162).
    Questa funzione richiede due parametri. Il primo, che và messo in %ebx, è l'indirizzo della prima di due variabili, contenente il numero di secondi di attesa, la seguente contenete il numero di nanosecondi.
    Il secondo parametro, da porre in %ecx, è l'indirizzo di una simile coppia di variabili, che potrebbe contenere il ritardo rimanente, nel caso l'attesa venisse interrotta. Dato che questo dato non ci interessa, potremo mettere 0 in %ecx.
  • Per terminare il programma occorre fare una chiamata alla funzione di sistema _exit (costante __NR_exit, codice 1).
    Questa funzione richiede il codice di uscita del programma, che và messo in %ebx.

Una possibile soluzione è la seguente:
# lp.s
# Stefano Salvi - 3/9/02
# Programma che visualizza delle configurazioni rotative sui
# LED connessi alla porta parallela
#.text parte codice
#.data dati inizializzati
#.bss dati non inizializzati

# Costanti
__NR_ioperm	=	101
__NR_nanosleep	=	162
__NR_exit	=	1


KERNEL	=	0x80

PARDOUT	=	0x378
PARCTL	=	0x37a
NUMPORT	=	4
NUMCONF	=	6

.data
# Dati da visualizzare
tab:
    .byte       0x81
    .byte       0x42
    .byte       0x24
    .byte       0x18
    .byte       0x24
    .byte       0x42
#struttura in input per nanosleep
deli:
    .long       0               #secondi
    .long       100000000       #nanosecondi 100 000 000 = 0,1 S

i:    .long     0		# Configurazione corrente
count:.long     10		# Numero ripetizioni

.text
.globl _start
_start:
        movl    $__NR_ioperm,%eax # funzione
        movl    $PARDOUT,%ebx   # port iniziale
        movl    $NUMPORT,%ecx   # numero port
        movl    $0x1,%edx       # azione -> 'riserva'
        int     $KERNEL         # Chiamo il kernel

        nop
        nop
        movb    $0x0f,%al       # metto a 0 i bit di controllo del modo
        movw    $PARCTL,%dx     # dx punta alla porta dati
        outb    %al,%dx         # mando fuori il dato per alimentare i tasti
        
lp:     
        movl    i,%ebx          # Recupero indice configurazione

        movb    tab(%ebx,1),%al # metto in al tab[i]
        movw    $PARDOUT,%dx    # dx punta alla porta dati
        outb    %al,%dx         # mando fuori la configurazione

        movl    $__NR_nanosleep,%eax # nanosleep
        movl    $deli,%ebx      # Ritardo richiesto (input)
        movl    $0,%ecx         # Ritardo rimanente (output) non mi serve

        int     $KERNEL         # chiamo il kernel
        
    
        incl    i               # incremento indice configurazione
        cmpl    $NUMCONF,i      # e' arrivara a 8?
        jne     lp              # se no, resta in ciclo
        movl    $0,i            # se 8 -> riporto i a 0
        
        decl    count           # decremento il numero di volte
        jne     lp              # se non sono a 0, ripeto

        movl    $0,%ebx         # first argument: exit code
        movl    $__NR_exit,%eax # system call number (sys_exit)
        int     $KERNEL         # call kernel

Il sorgente del programma è lp.s.

Per provare il programma, lo si deve assemblare con il comando as -o lp.o lp.s, linkare con ld -o lp lp.o e quindi mandato in esecuzione con il comando ./lp.
Dato che il programma, per utilizzare le porte di I/O, deve invocare la funzione priviligiata IOPERM, deve essere eseguito dal superutente root. Per fare questo, o si lancia il programma da una console nella quale si è fatto login come root, oppure in una finestra terminale aperta normalmente come utente si esegue il comando su root, per eseguire il terminale come root. Verrà richiesta la password di root.


[Home Page dell'ITIS "Fermi"] [Indice Quarta] [Precedente] [Successivo]

© Ing. Stefano Salvi - Released under GPL licence