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
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.
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.
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.
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. –
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
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