|
Aggiungiamo la gestione dell'input (in modo banale).
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 dieci configurazioni al secondo.
Il ciclo verrà terminato dalla pressione di un tasto connesso ad un
ingresso della porta parallela (porta 0x379, bit da 3 a 7).
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.
- Per garantire uno stato stabile per i pulsanti, essi vengono alimentati da
uno dei bit di controllo della porta parallela (porta 0x37a). Per un corretto
funzionamento dei tasti occorre che le uscite di controllo della porta siano tutte ad 1. I bit
che non sono connessi alle uscite vanno a 0, perchè impostano il modo di funzionamento.
Per ottenere questo occorre scrivere 0x04 sulla porta 0x37a prima dell'uso dei tasti.
- I tasti sono connessi tra i piedino di ingresso della porta 0x379 e la massa, quindi
un tasto rilasciato darà un valore 1 sul relativo ingresso ed un tasto
premuto un valore 0.
Questo è valido per tutti i tasti, salvo quello connesso al bit 7 , che ha un
livello invertito (0 per rialsciato, 1 per premuto).
- 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 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:
# lp1.s
# Stefano Salvi - 7/9/02
# Programma che visualizza delle configurazioni rotative sui
# LED connessi alla porta parallela
# Termina quando viene premuto un tasto a scelta, connesso agli ingressi della 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
PARDIN = 0x379
PARCTL = 0x37a
STOPK = 0x80
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
.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 $0x04,%al # metto il dato per accendere tutti i LED
movw $PARCTL,%dx # dx punta alla porta dati
outb %al,%dx # mando fuori il dato per alimentare i tasti
lp:
movw $PARDIN,%dx # dx punta alla porta di ingresso
inb %dx,%al # leggo la tastiera
testb $STOPK,%al # Controllo il bit 7 (uno dei tasti)
jnz fine # Se lo trovo premuto (e' invertito, quindi premuto ->1)
# termino
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
jmp lp # ciclo infinito
fine:
movl $0,%ebx # first argument: exit code
movl $__NR_exit,%eax # system call number (sys_exit)
int $KERNEL # call kernel
|
Il sorgente del programma è lp1.s.
Per provare il programma, lo si deve assemblare con il comando
as -o lp1.o lp1.s, linkare con ld -o lp1 lp1.o e quindi
mandato in esecuzione con il comando ./lp1.
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
|