Linux下NASM汇编实现终端静默输入并跳过隔位辅音
Linux NASM 汇编实现隔位辅音跳过的终端输入程序
我来给你提供一个完全可行的实现方案——因为在Linux用户态下直接重载标准输入根本走不通(用户态程序没权限修改中断向量表),所以咱们采用自定义逐字符无回显输入的方式,类似你说的Windows的getch(),同时处理退格、回车这些特殊操作。
核心思路
- 切换终端模式:把终端从默认的「规范模式」(输入缓冲到回车才处理、自动回显)切换到「原始模式」,这样能逐字符捕获输入,且不会自动显示输入的字符。
- 逐字符处理逻辑:
- 维护一个计数器,记录已经处理过的辅音数量
- 对每个输入字符,先判断是不是辅音:如果是,计数器加1,奇数位的辅音直接跳过,偶数位的才输出;如果不是辅音(元音、数字、符号等),直接输出。
- 特殊按键处理:退格要删除屏幕上的字符并调整计数器,回车则恢复终端模式并退出程序。
- 恢复终端设置:程序退出前必须把终端切回原来的模式,不然你的终端会变得异常(比如输入不回显、回车不换行)。
完整NASM代码示例(x86_64 Linux)
section .data ; 保存原始终端属性 term_attr_old times 36 db 0 term_attr_new times 36 db 0 ; 退格序列:退格+空格+退格,用来清除屏幕上的一个字符 backseq db 0x08, 0x20, 0x08 backseq_len equ $ - backseq section .text global _start _start: ; 获取当前终端属性(stdin是fd 0) mov rax, 16 ; sys_tcgetattr mov rdi, 0 mov rsi, term_attr_old syscall ; 复制原始属性到新属性,然后修改为原始模式 mov rcx, 36 mov rsi, term_attr_old mov rdi, term_attr_new rep movsb ; 关闭规范模式、回显、ICANON/ECHO and dword [term_attr_new + 12], ~(1 << 1) ; 关闭ICANON(规范模式) and dword [term_attr_new + 12], ~(1 << 3) ; 关闭ECHO(回显) ; 设置新的终端属性 mov rax, 17 ; sys_tcsetattr mov rdi, 0 mov rsi, 0x00000002 ; TCSANOW:立即生效 mov rdx, term_attr_new syscall ; 初始化辅音计数器 mov rbx, 0 input_loop: ; 读取单个字符(stdin fd 0) mov rax, 0 ; sys_read mov rdi, 0 mov rsi, rsp ; 把字符存在栈上 mov rdx, 1 syscall cmp rax, 1 jne exit_program ; 读取失败或EOF,退出 mov al, byte [rsp] ; 处理回车(ASCII 0x0D)或换行(0x0A) cmp al, 0x0D je exit_program cmp al, 0x0A je exit_program ; 处理退格(ASCII 0x08) cmp al, 0x08 je handle_backspace ; 判断是不是字母 cmp al, 'A' jl output_char ; 非字母,直接输出 cmp al, 'z' jg output_char cmp al, 'Z' jle check_vowel ; 大写字母,检查元音 cmp al, 'a' jge check_vowel ; 小写字母,检查元音 check_vowel: ; 判断是不是元音(A,E,I,O,U,a,e,i,o,u) cmp al, 'A' je output_char cmp al, 'E' je output_char cmp al, 'I' je output_char cmp al, 'O' je output_char cmp al, 'U' je output_char cmp al, 'a' je output_char cmp al, 'e' je output_char cmp al, 'i' je output_char cmp al, 'o' je output_char cmp al, 'u' je output_char ; 是辅音,计数器加1 inc rbx ; 判断是否是奇数位辅音(跳过) test rbx, 1 jnz input_loop ; 奇数,跳过,回到输入循环 ; 偶数位辅音,输出 jmp output_char handle_backspace: ; 先检查是否有已输出的字符可以删除 cmp rbx, 0 je input_loop ; 没有字符,啥也不做 ; 输出退格序列,清除屏幕上的字符 mov rax, 1 ; sys_write mov rdi, 1 mov rsi, backseq mov rdx, backseq_len syscall ; 调整计数器:简化处理,直接减1(实际可维护输出字符记录优化) dec rbx jmp input_loop output_char: ; 输出当前字符到stdout(fd 1) mov rax, 1 mov rdi, 1 mov rsi, rsp mov rdx, 1 syscall jmp input_loop exit_program: ; 输出换行,让终端提示符回到新行 mov rax, 1 mov rdi, 1 mov rsi, 0x0A mov rdx, 1 syscall ; 恢复原始终端属性 mov rax, 17 mov rdi, 0 mov rsi, 0x00000002 mov rdx, term_attr_old syscall ; 退出程序 mov rax, 60 ; sys_exit mov rdi, 0 syscall
代码说明
- 终端模式切换:用
sys_tcgetattr和sys_tcsetattr系统调用获取、设置终端属性,关闭规范模式(ICANON)和回显(ECHO),这样我们能直接处理每个输入字符。 - 辅音判断:先判断是不是字母,再排除元音字符,剩下的就是辅音。
- 隔位跳过逻辑:用
rbx作为辅音计数器,奇数位的辅音直接跳过不输出,偶数位的才输出。 - 退格处理:输出
0x08(退格)+ 空格 + 0x08(退格)的序列,这样能清除屏幕上的一个字符,同时调整计数器。 - 程序退出:必须恢复终端的原始属性,不然终端会出现输入不回显、无法正常换行等问题。
编译运行方法
用NASM编译成64位可执行文件:
nasm -f elf64 input_filter.asm -o input_filter.o ld input_filter.o -o input_filter ./input_filter
测试的时候,输入比如abcdef,会输出abdf(c是第1个辅音,跳过;e是元音,输出;f是第2个辅音,输出)。退格键也能正常删除已输出的字符。
内容的提问来源于stack exchange,提问作者Nikita Andrasovich
相关产品推荐
相关产品推荐

