2010-07-22 3 views
6

Ich benutze Nasm unter Ubuntu. Übrigens muss ich ein einzelnes Eingabezeichen von der Tastatur des Benutzers erhalten (wie wenn ein Programm Sie nach y/n fragt?), So dass die Taste gedrückt wird und ohne die Eingabetaste zu drücken, muss ich das eingegebene Zeichen lesen. Ich googelte es sehr, aber alles, was ich fand, war irgendwie mit dieser Linie verbunden (int 21h), die "Segmentierungs-Störung" ergeben. Bitte helfen Sie mir, herauszufinden, wie man ein einzelnes Zeichen bekommt oder wie man diesen Segmentierungsfehler umgehen kann.Wie lese ich Single-Zeichen-Eingabe von der Tastatur mit Nasm (Assembly) unter Ubuntu?

Antwort

11

Es kann von der Montage durchgeführt werden, aber es ist nicht einfach. Sie können nicht int 21h verwenden, das ist ein DOS-Systemaufruf, der unter Linux nicht verfügbar ist.

Um Zeichen vom Terminal unter UNIX-ähnlichen Betriebssystemen (wie Linux) zu erhalten, lesen Sie von STDIN (Dateinummer 0). Normalerweise blockiert der System-Leseaufruf solange, bis der Benutzer die Eingabetaste drückt. Dies wird als kanonischer Modus bezeichnet. Um ein einzelnes Zeichen zu lesen, ohne darauf zu warten, dass der Benutzer die Eingabetaste drückt, müssen Sie zunächst den kanonischen Modus deaktivieren. Natürlich müssen Sie es später erneut aktivieren, wenn Sie später eine Zeileneingabe durchführen möchten und bevor Ihr Programm beendet wird.

Um den kanonischen Modus unter Linux zu deaktivieren, senden Sie eine IOCTL (IO ControL) an STDIN, wobei Sie den ioctl syscall verwenden. Ich nehme an, Sie wissen, wie Linux-Systemaufrufe von Assembler zu machen.

Der Ioctl-Systemaufruf hat drei Parameter. Die erste ist die Datei zum Senden des Befehls an (STDIN), die zweite ist die IOCTL-Nummer und die dritte ist typischerweise ein Zeiger auf eine Datenstruktur. ioctl gibt bei Erfolg 0 zurück, oder ein negativer Fehlercode schlägt fehl.

Die erste IOCTL, die Sie benötigen, ist TCGETS (Nummer 0x5401), die die aktuellen Terminalparameter in einer Termanstruktur erhält. Der dritte Parameter ist ein Zeiger auf eine Termios-Struktur. Von der Kernel-Quelle wird die termios Struktur wie folgt definiert:

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 */ 
}; 

wo tcflag_t 32 Bit lang ist, CC_tv ein Byte lang ist, und NCCS ist derzeit als 19. Siehe die NASM Handbuch definiert, wie können Sie bequem definieren und Platz für solche Strukturen reservieren.

Sobald Sie die aktuellen Termios haben, müssen Sie das kanonische Flag löschen. Dieses Flag befindet sich im Feld c_flag mit der Maske ICANON (0x00000002). Um es zu löschen, berechne c_lflag UND (NICHT ICANON). und speichern Sie das Ergebnis zurück in das Feld c_flag.

Nun müssen Sie den Kernel über Ihre Änderungen an der termios Struktur informieren. Verwenden Sie die TCSETS (Nummer 0x5402) ioctl, mit dem dritten Parameter setzen Sie die Adresse Ihrer termios Struktur.

Wenn alles gut geht, befindet sich das Terminal jetzt im nicht-kanonischen Modus. Sie können den kanonischen Modus wiederherstellen, indem Sie das kanonische Flag setzen (indem Sie c_lflag mit ICANON verknüpfen) und den TCSETS ioctl erneut aufrufen.immer wiederherstellen kanonischen Modus vor dem Beenden

Wie gesagt, es ist nicht einfach.

0

Der einfache Weg: Verwenden Sie für ein Textmodus-Programm libncurses, um auf die Tastatur zuzugreifen; Verwenden Sie für ein grafisches Programm Gtk+.

Der harte Weg: Unter der Annahme eines Text-Modus-Programms, müssen Sie dem Kernel sagen, dass Sie Single-Zeichen-Eingabe wollen, und dann müssen Sie eine Menge Buchhaltung und Entschlüsselung. Es ist wirklich kompliziert. Es gibt keine Entsprechung der guten alten DOS-Routine getch(). Sie können starten lernen, wie es hier geht: Terminal I/O. Grafische Programme sind noch komplizierter; Die niedrigste API dafür ist Xlib.

Wie auch immer, du wirst verrückt werden, was auch immer das in Assembly ist; Verwenden Sie stattdessen C.

+1

Während alles, was Sie gesagt haben, für C korrekt ist, ist es nicht wirklich eine relevante Antwort, wenn das OP versucht, Montage zu lernen. –

+1

Das liegt daran, dass das OP * nicht in der Assemblersprache programmieren soll *. Der einzige gute Grund, etwas in Assemblersprache von Hand zu codieren, ist, ob es sich um eine performancekritische Unterroutine handelt oder um eines der wenigen Low-Level-Teile eines Betriebssystemkernels, die * nicht anders codiert werden können. Benutzerinteraktion ist nicht qualifiziert. Was das OP versucht, ist nicht einmal eine gute * Lernübung * unter Unix. – zwol

+0

Nichtsdestotrotz gibt es nichts, was das OP daran hindert, eine Assemblersprache zu schreiben, die libncurses aufruft, obwohl es für mich eine tiefe Verschwendung von Zeit und Vernunftpunkten zu sein scheint (aber es wäre nicht so schlecht wie die Assemblersprache)/O von Hand). – zwol

5

Ich brauchte diese vor kurzem zu tun, und excellent answer von Callum inspiriert, schrieb ich folgendes:

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 

Anschließend können Sie tun:

call canonical_off 

Wenn Sie eine Textzeile zu lesen , möchten Sie wahrscheinlich auch tun:

call echo_off 

, so dass jedes Zeichen nicht geäußert wird, wie es eingegeben wird.

Es gibt möglicherweise bessere Möglichkeiten, dies zu tun, aber es funktioniert für mich auf einer 64-Bit-Fedora-Installation.

Weitere Informationen finden Sie in der Manpage für termios(3) oder in der termbits.h source.