Sto usando nasm sotto ubuntu. Tra l'altro ho bisogno di ottenere un singolo carattere di input dalla tastiera dell'utente (come quando un programma ti chiede di y/n?) Così come il tasto premuto e senza premere invio ho bisogno di leggere il carattere inserito. L'ho cercato spesso, ma tutto ciò che ho trovato era in qualche modo collegato a questa riga (int 21h
) che genera "Errore di segmentazione". Per favore aiutami a capire come ottenere un singolo personaggio o come superare questo errore di segmentazione.Come leggo l'immissione di un singolo carattere dalla tastiera usando nasm (assembly) sotto ubuntu?
Come leggo l'immissione di un singolo carattere dalla tastiera usando nasm (assembly) sotto ubuntu?
risposta
Può essere fatto dal montaggio, ma non è facile. Non è possibile utilizzare int 21h, è una chiamata di sistema DOS e non è disponibile sotto Linux.
Per ottenere caratteri dal terminale sotto sistemi operativi UNIX (come Linux), si legge da STDIN (numero di file 0). Normalmente, la chiamata di sistema in lettura si bloccherà fino a quando l'utente non premerà Invio. Questa è chiamata modalità canonica. Per leggere un singolo carattere senza attendere che l'utente prema Invio, è necessario prima disabilitare la modalità canonica. Ovviamente, dovrai riabilitarlo se desideri inserire la linea in un secondo momento e prima che il programma esca.
Per disabilitare la modalità canonica su Linux, si invia un IOCTL (IO ControL) a STDIN, utilizzando ioctl syscall. Presumo che tu sappia come fare chiamate di sistema Linux da assemblatore.
L'ioctl syscall ha tre parametri. Il primo è il file per inviare il comando a (STDIN), il secondo è il numero IOCTL e il terzo è in genere un puntatore a una struttura dati. ioctl restituisce 0 in caso di successo o un codice di errore negativo in caso di errore.
Il primo IOCTL necessario è TCGETS (numero 0x5401) che ottiene i parametri del terminale corrente in una struttura di termios. Il terzo parametro è un puntatore a una struttura di termios. Dalla sorgente del kernel, la struttura termios è definita come:
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
};
dove tcflag_t è lungo 32 bit, cc_t è lunga un byte, e NCCS è attualmente definito come 19. Vedere il manuale NASM per come si può comodamente definire e riserva spazio per strutture come questa.
Quindi, una volta ottenuto il termios corrente, è necessario cancellare la bandiera canonica. Questo flag si trova nel campo c_lflag, con maschera ICANON (0x00000002). Per cancellarlo, calcola c_lflag AND (NON ICANON). e memorizza il risultato nel campo c_lflag.
Ora è necessario notificare al kernel le modifiche apportate alla struttura del termios. Utilizzare l'ioctl TCSETS (numero 0x5402), con il terzo parametro impostare l'indirizzo della struttura del termios.
Se tutto va bene, il terminale è ora in modalità non canonica. È possibile ripristinare la modalità canonica impostando il flag canonico (con ORing c_lflag con ICANON) e chiamando di nuovo l'ioctl TCSETS.ripristina sempre la modalità canonica prima di uscire
Come ho detto, non è facile.
Il modo semplice: per un programma in modalità testo, utilizzare libncurses per accedere alla tastiera; per un programma grafico, utilizzare Gtk+.
Il modo più difficile: supponendo un programma in modalità testo, devi dire al kernel che vuoi l'input a carattere singolo, e quindi devi fare un sacco di contabilità e decodifica. È davvero complicato Non esiste un equivalente della buona vecchia routine DOS getch()
. È possibile start imparare come farlo qui: Terminal I/O. I programmi grafici sono ancora più complicati; l'API di livello più basso è Xlib.
In ogni caso, impazzisci codificando tutto ciò che è in assemblea; usare invece C
avevo bisogno di fare questo di recente, e ispirato da Callum di excellent answer, ho scritto il seguente:
termios: times 36 db 0
stdin: equ 0
ICANON: equ 1<<1
ECHO: equ 1<<3
canonical_off:
call read_stdin_termios
; clear canonical bit in local mode flags
push rax
mov eax, ICANON
not eax
and [termios+12], eax
pop rax
call write_stdin_termios
ret
echo_off:
call read_stdin_termios
; clear echo bit in local mode flags
push rax
mov eax, ECHO
not eax
and [termios+12], eax
pop rax
call write_stdin_termios
ret
canonical_on:
call read_stdin_termios
; set canonical bit in local mode flags
or dword [termios+12], ICANON
call write_stdin_termios
ret
echo_on:
call read_stdin_termios
; set echo bit in local mode flags
or dword [termios+12], ECHO
call write_stdin_termios
ret
read_stdin_termios:
push rax
push rbx
push rcx
push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5401h
mov edx, termios
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
write_stdin_termios:
push rax
push rbx
push rcx
push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5402h
mov edx, termios
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
È quindi possibile fare:
call canonical_off
Se state leggendo una riga di testo , probabilmente anche tu vuoi fare:
call echo_off
in modo che ogni carattere non venga echeggiato come è stato digitato.
Ci possono essere modi migliori per farlo, ma funziona per me su un'installazione Fedora a 64 bit.
Ulteriori informazioni sono disponibili nella pagina di manuale per termios(3)
o nello termbits.h
source.
- 1. esegue un codice assembly su ubuntu
- 2. Hello world using nasm in windows assembly
- 3. Come compilare usando nasm su MacOSX
- 4. ulimit -t sotto ubuntu
- 5. Come testare un singolo file sotto pytest
- 6. Come leggo un albero di dipendenza Maven
- 7. Come restituire un singolo oggetto risultato dalla query di ibernazione?
- 8. Come posso aggiungere un nodo sotto un nodo usando ObjectContentManager?
- 9. Come leggo la posta in arrivo usando C#
- 10. Come si sostituisce un singolo carattere con un backslash usando regex.replace in C#
- 11. Come posso accedere all'ora del sistema usando NASM?
- 12. come leggere un singolo carattere nello script di shell
- 13. Come leggere un singolo carattere dalla console in Java (mentre l'utente lo digita)?
- 14. Ottenere un singolo carattere da una stringa
- 15. Rimuovere un singolo carattere da una stringa?
- 16. Come stampare un numero nell'assemblaggio NASM?
- 17. Convertire un singolo carattere in una stringa?
- 18. Come leggere un singolo carattere dall'input come u8?
- 19. Un buon tutorial NASM/FASM?
- 20. Come caricare dinamicamente un carattere sotto iOS. (per davvero)
- 21. Come posso combinare un carattere seguito da un "accento combinato" in un singolo carattere?
- 22. Come digitare "ज्ञ" nella tastiera hindi di ubuntu 14.04
- 23. Come leggo la documentazione java?
- 24. Come dividere usando un carattere prefisso usando le espressioni regolari?
- 25. Come posso convertire un singolo carattere in una stringa?
- 26. Eclipse aiuto non mostrerà sotto Ubuntu
- 27. Stringa: come sostituire più caratteri possibili con un singolo carattere?
- 28. Come leggo questo diagramma di javascript?
- 29. Sostituisci l'elemento di un singolo carattere di una stringa C
- 30. Come creare un parametro multi-carattere in UNIX usando getopt?
Mentre tutto ciò che hai detto è corretto per C, non è davvero una risposta pertinente se l'OP sta cercando di imparare l'assemblaggio. –
Questo perché l'OP * non deve essere programmato in linguaggio assembly *. L'unica buona ragione per codificare a mano qualsiasi cosa nel linguaggio assembly è se si tratta di una subroutine computazionale critica delle prestazioni, o uno dei pochissimi pezzi di basso livello di un kernel del sistema operativo che * non può * essere codificato in altro modo. L'interazione dell'utente non è idonea. Ciò che l'OP sta cercando di fare non è nemmeno un buon * esercizio di apprendimento * sotto Unix. – zwol
Detto questo, non c'è nulla che impedisca all'OP di scrivere un linguaggio assembly che chiama libncurses, anche se mi sembra un profondo spreco di tempo e punti di sanità mentale (ma non sarebbe male come il linguaggio assembly che fa il terminale Unix I/O a mano). – zwol