Estoy usando nasm en ubuntu. Por cierto, necesito obtener un solo carácter de entrada del teclado del usuario (como cuando un programa te pregunta por y/n?) Así que al presionar la tecla y sin presionar Enter, necesito leer el carácter ingresado. Lo busqué en Google mucho, pero todo lo que encontré estaba relacionado de alguna manera con esta línea (int 21h
) que resulta en "Falla de Segmentación". Ayúdame a resolver cómo obtener un solo carácter o cómo superar este error de segmentación.¿Cómo leo la entrada de un solo carácter del teclado usando nasm (assembly) debajo de ubuntu?
Respuesta
Se puede hacer desde el ensamblaje, pero no es fácil. No puede usar int 21h, es una llamada al sistema DOS y no está disponible en Linux.
Para obtener caracteres de la terminal en sistemas operativos tipo UNIX (como Linux), lea de STDIN (número de archivo 0). Normalmente, la llamada al sistema de lectura se bloqueará hasta que el usuario presione enter. Esto se llama modo canónico. Para leer un solo carácter sin esperar a que el usuario presione enter, primero debe deshabilitar el modo canónico. Por supuesto, tendrá que volver a habilitarlo si desea ingresar la línea más tarde, y antes de que su programa finalice.
Para deshabilitar el modo canónico en Linux, envía un IOCTL (IO ControL) a STDIN, utilizando ioctl syscall. Supongo que sabes cómo hacer llamadas al sistema Linux desde ensamblador.
El ioctl syscall tiene tres parámetros. El primero es el archivo para enviar el comando a (STDIN), el segundo es el número IOCTL y el tercero es típicamente un puntero a una estructura de datos. ioctl devuelve 0 en caso de éxito o un código de error negativo en error.
La primera IOCTL que necesita es TCGETS (número 0x5401) que obtiene los parámetros de terminal actuales en una estructura termios. El tercer parámetro es un puntero a una estructura termios. Desde la fuente del kernel, se define la estructura termios como:
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 */
};
donde tcflag_t es de 32 bits de longitud, cc_t es un byte de longitud, y NCCS está actualmente define como 19. Consulte el manual de NASM cómo puede definir convenientemente y reservar espacio para estructuras como esta.
Una vez que tenga los términos actuales, debe borrar la marca canónica. Esta bandera está en el campo c_lflag, con la máscara ICANON (0x00000002). Para borrarlo, calcule c_lflag Y (NO ICONO). y almacena el resultado nuevamente en el campo c_lflag.
Ahora necesita notificar al kernel de sus cambios a la estructura termios. Use el TCSETS (número 0x5402) ioctl, con el tercer parámetro establezca la dirección de su estructura termios.
Si todo va bien, el terminal ahora está en modo no canónico. Puede restablecer el modo canónico configurando la bandera canónica (mediante ORing c_lflag con ICANON) y llamando de nuevo al TCSETS ioctl.siempre restablece el modo canónico antes de salir
Como dije, no es fácil.
La manera fácil: Para un programa de modo de texto, use libncurses para acceder al teclado; para un programa gráfico, use Gtk+.
La manera más difícil: suponiendo que se trata de un programa en modo texto, debe decirle al kernel que desea la entrada de un solo carácter, y luego debe realizar una gran cantidad de contabilidad y decodificación. Es realmente complicado. No existe un equivalente de la buena rutina de DOS getch()
. Puede iniciar aprendiendo cómo hacerlo aquí: Terminal I/O. Los programas gráficos son aún más complicados; la API de nivel más bajo para eso es Xlib.
De cualquier manera, te vas a volver loco codificando lo que sea que esté en el ensamblaje; usa C en su lugar.
que tenía que hacer esto recientemente, e inspirado por Callum excellent answer, escribí lo siguiente:
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
A continuación, puede hacer:
call canonical_off
Si está leyendo una línea de texto , probablemente también quiera hacer:
call echo_off
para que cada carácter no se repita a medida que se escribe.
Puede haber mejores formas de hacerlo, pero me funciona en una instalación de Fedora de 64 bits.
Más información se puede encontrar en la página del manual para termios(3)
, o en el termbits.h
source.
- 1. ¿Cómo leo la entrada carácter por carácter en Java?
- 2. ¿Cómo leo un solo carácter de STDIN usando Perl en Windows?
- 3. Manejo efectivo de la entrada del teclado
- 4. Entrada de consola de un solo carácter en java/clojure
- 5. ¿Cómo leo la entrada formateada en Java?
- 6. Ayuda escribiendo programas TSR en NASM Assembly para DOS
- 7. jQuery - presione el carácter - INDEPENDIENTE del teclado
- 8. ¿Cómo puedo acceder a la hora del sistema usando NASM?
- 9. Mostrar un div debajo de un campo de entrada del ancho exacto usando jQuery
- 10. ¿Puedo cambiar la entrada del teclado de un usuario?
- 11. Carácter ilegal del patrón 'Y' en Ubuntu
- 12. Cómo usar Scanf en NASM?
- 13. Android: Detectar inactividad del usuario/Detectar (teclado) entrada de teclado
- 14. Ingresar de forma interactiva el título debajo del cual colocar una entrada usando la captura
- 15. Eclipse: cómo ir a un error usando solo el teclado (atajo de teclado)?
- 16. Cómo escapar del carácter & amp; ampersand en la entrada del formulario usando jQuery
- 17. ¿Cómo leo la entrada de consola/stdin en Dart?
- 18. Consola de teclado concurrente/no bloqueada entrada
- 19. ¿Cómo simular la entrada del teclado en TODAS las aplicaciones?
- 20. cómo leer solo un carácter en el script de shell
- 21. Reemplazar solo la enésima instancia del carácter
- 22. jQuery: ¿Cómo puedo escuchar la entrada general del teclado?
- 23. Carácter opcional en la máscara de entrada
- 24. Inhabilitar la entrada del teclado en Android TimePicker
- 25. Cadena de un solo carácter
- 26. ¿Cómo tomar la entrada del teclado en JavaScript?
- 27. Control deslizante jQuery-UI: cómo desactivar la entrada del teclado?
- 28. ¿Cómo leo las ilustraciones del álbum usando Python?
- 29. Cómo detectar el carácter de entrada del usuario en UITextView
- 30. ¿Cómo puedo saber si la entrada del teclado proviene de un escáner de código de barras?
Si bien todo lo que usted dice es correcto para C, en realidad no es una respuesta relevante si el OP intenta aprender a ensamblar. –
Eso es porque OP * no debe programarse en lenguaje ensamblador *. La única buena razón para codificar manualmente algo en lenguaje ensamblador es si es una subrutina computacional crítica para el rendimiento o una de las pocas piezas de bajo nivel de un kernel de sistema operativo que * no * puede codificarse de otra manera. La interacción del usuario no califica. Lo que el OP intenta hacer no es siquiera un buen * ejercicio de aprendizaje * bajo Unix. – zwol
Habiendo dicho eso, no hay nada que impida que el OP escriba lenguaje ensamblador que llama libncurses, aunque me parece una profunda pérdida de tiempo y cordura (pero no sería tan malo como el lenguaje ensamblador que hace el terminal Unix I/O a mano). – zwol