【问题标题】:Wait for keypress Assembly NASM, Linux等待按键组装 NASM,Linux
【发布时间】:2015-11-18 12:43:35
【问题描述】:

我正在为 x86-64 开发一个 Hello World in Assembly。

我已经设法创建了一个在按下 Enter 键时完成的操作,但是当按下 ANY 键时我必须完成它。

这是等待 ENTER 键的代码:

mov rax, 0
mov rdi, 0
mov rdx, 1
syscall

我不能使用任何 int xh 或类似的东西。只有系统调用。

谢谢!

【问题讨论】:

标签: linux assembly nasm x86-64 system-calls


【解决方案1】:

我已经回答了a similar question before,并提供了可以直接与系统调用一起工作的 C 代码来执行您想要的操作。

这是将该代码翻译为 nasm 的版本,稍作改动以反映您只是检查是否按下了任何键,而不是特定键:

fwait:
    ; fetch the current terminal settings
    mov rax, 16    ; __NR_ioctl
    mov rdi, 0     ; fd: stdin
    mov rsi, 21505 ; cmd: TCGETS
    mov rdx, orig  ; arg: the buffer, orig
    syscall

    ; again, but this time for the 'new' buffer
    mov rax, 16
    mov rdi, 0
    mov rsi, 21505
    mov rdx, new
    syscall

    ; change settings
    and dword [new+0], -1516    ; ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
    and dword [new+4], -2       ; ~OPOST
    and dword [new+12], -32844  ; ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN)
    and dword [new+8], -305     ; ~(CSIZE | PARENB)
    or  dword [new+8], 48        ; CS8

    ; set settings (with ioctl again)
    mov rax, 16    ; __NR_ioctl
    mov rdi, 0     ; fd: stdin
    mov rsi, 21506 ; cmd: TCSETS
    mov rdx, new   ; arg: the buffer, new
    syscall

    ; read a character
    mov rax, 0     ; __NR_read
    mov rdi, 0     ; fd: stdin
    mov rsi, char  ; buf: the temporary buffer, char
    mov rdx, 1     ; count: the length of the buffer, 1
    syscall

    ; reset settings (with ioctl again)
    mov rax, 16    ; __NR_ioctl
    mov rdi, 0     ; fd: stdin
    mov rsi, 21506 ; cmd: TCSETS
    mov rdx, orig  ; arg: the buffer, orig
    syscall

    ret

基本思想是您必须编辑终端设置、读取字符并重置设置。

【讨论】:

  • 你可以只复制缓冲区而不是调用TCGETS 两次!您正在修改的部分适合 2 个 64 位寄存器,因此您可以加载它们并将它们保存在 regs 中,而不是实际复制到任何地方。此外,您忘记包含 TCGETS 的 orig: resd something 缓冲区:您必须确保保留足够的空间,以便系统调用不会覆盖其他数据。 (例如,在此代码上扩展的另一个答案使用 times 10000 db 0 大量过度杀伤来保留 10kB。)
【解决方案2】:

我对 Cel Skeggs 的回答中的代码做了一些修改。

这是一个完整的程序,可以一次打印一个读取键,按 ENTER 键退出。

;; Simple Keyboard Reading in x86_64 assembly, using Linux syscalls
;;
;;        nasm -felf64 -o readKey64.o readKey64.asm
;;        ld readKey64.o -o readKey64
;;        ./readKey64
;;
;; Adaptation of original code from:
;;   https://stackoverflow.com/questions/32193374/wait-for-keypress-assembly-nasm-linux
;--------------------------------------------------------------------------




global _start:


section .data
  orig: times 10000 db 0         ; reserve way more space than we need for TCGETS
  new:  times 10000 db 0
  char: db 0,0,0,0,0

 msg1: db "Reading Keyboard... push ENTER to finish",0ah,0
 msglen     equ $ - msg1
 msg2: db 0ah,"END.",0ah,0
 msg2len: equ $ - msg2

section .text

_start:
    mov rsi,msg1
    mov rax,1
    mov rdi,1
    mov rdx,msglen
    syscall

    ; fetch the current terminal settings
    mov rax, 16    ; __NR_ioctl
    mov rdi, 0     ; fd: stdin
    mov rsi, 21505 ; cmd: TCGETS
    mov rdx, orig  ; arg: the buffer, orig
    syscall

    ; again, but this time for the 'new' buffer
    mov rax, 16
    mov rdi, 0
    mov rsi, 21505
    mov rdx, new
    syscall

    ; change settings
    and dword [new+0], -1516    ; ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
    and dword [new+4], -2       ; ~OPOST
    and dword [new+12], -32844  ; ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN)
    and dword [new+8], -305     ; ~(CSIZE | PARENB)
    or  dword [new+8], 48        ; CS8

    ; set settings (with ioctl again)
    mov rax, 16    ; __NR_ioctl
    mov rdi, 0     ; fd: stdin
    mov rsi, 21506 ; cmd: TCSETS
    mov rdx, new   ; arg: the buffer, new
    syscall
.readchar:
    ; read a character
    mov rax, 0     ; __NR_read
    mov rdi, 0     ; fd: stdin
    mov rsi, char  ; buf: the temporary buffer, char
    mov rdx, 1     ; count: the length of the buffer, 1
    syscall
    mov rax,1      ; __NR_write
    mov rdi,1
    mov rdx,1
    syscall        ; sys_write(1, char, 1)   // RSI is still set to buf

    cmp byte[char],13
    jz .end
    jmp .readchar
.end:

    ; reset settings (with ioctl again)
    mov rax, 16    ; __NR_ioctl
    mov rdi, 0     ; fd: stdin
    mov rsi, 21506 ; cmd: TCSETS
    mov rdx, orig  ; arg: the buffer, orig
    syscall

    mov rsi,msg2
    mov rax,1
    mov rdi,1
    mov rdx,msg2len
    syscall            ; sys_write(1, msg2, msg2len)

    mov rax,60
    mov rdi,0
    syscall            ; sys_exit(0)

【讨论】:

  • 您应该使用jnz .readchar 而不是有条件地跳过jmp。另外,不要忘记评论您的新系统调用。
猜你喜欢
  • 2020-01-21
  • 2018-10-30
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
相关资源
最近更新 更多