簡體   English   中英

如何在ubuntu下使用nasm(程序集)從鍵盤讀取單個字符輸入?

[英]How do i read single character input from keyboard using nasm (assembly) under ubuntu?

我在 ubuntu 下使用 nasm。 順便說一下,我需要從用戶的鍵盤上獲取單個輸入字符(比如當程序要求您輸入 y/n 時?)以便按下鍵而不按 Enter 我需要讀取輸入的字符。 我用谷歌搜索了很多,但我發現的所有內容都與這條線( int 21h )有關,這導致了“分段錯誤”。 請幫我弄清楚如何獲取單個字符或如何克服這個分段錯誤。

它可以從組裝中完成,但這並不容易。 你不能使用 int 21h,這是一個 DOS 系統調用,它在 Linux 下不可用。

要在類 UNIX 操作系統(例如 Linux)下從終端獲取字符,您可以從 STDIN(文件編號 0)讀取。 通常, read 系統調用將阻塞,直到用戶按下 Enter 鍵。 這稱為規范模式。 要在不等待用戶按 Enter 的情況下讀取單個字符,您必須首先禁用規范模式。 當然,如果您想稍后在程序退出之前進行行輸入,則必須重新啟用它。

要在 Linux 上禁用規范模式,請使用 ioctl 系統調用向 STDIN 發送 IOCTL (IO Control)。 我假設您知道如何從匯編程序進行 Linux 系統調用。

ioctl 系統調用具有三個參數。 第一個是將命令發送到的文件 (STDIN),第二個是 IOCTL 編號,第三個通常是指向數據結構的指針。 ioctl 成功時返回 0,失敗時返回負錯誤代碼。

您需要的第一個 IOCTL 是 TCGETS(編號 0x5401),它獲取 termios 結構中的當前終端參數。 第三個參數是一個指向 termios 結構的指針。 從內核源代碼來看,termios 結構定義為:

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

其中 tcflag_t 是 32 位長,cc_t 是 1 字節長,NCCS 當前定義為 19。請參閱 NASM 手冊了解如何方便地為此類結構定義和保留空間。

因此,一旦您獲得了當前的 termios,就需要清除規范標志。 此標志位於 c_lflag 字段中,帶有掩碼 ICANON (0x00000002)。 要清除它,請計算 c_lflag AND (NOT ICANON)。 並將結果存儲回 c_lflag 字段。

現在您需要將您對 termios 結構的更改通知內核。 使用 TCSETS(編號 0x5402)ioctl,第三個參數設置 termios 結構的地址。

如果一切順利,終端現在處於非規范模式。 您可以通過設置規范標志(通過將 c_lflag 與 ICANON 進行 ORing)並再次調用 TCSETS ioctl 來恢復規范模式。 在退出之前始終恢復規范模式

正如我所說,這並不容易。

我最近需要這樣做,並受到 Callum 出色答案的啟發,我寫了以下內容(適用於 x86-64 的 NASM):

DEFAULT REL

section .bss
termios:        resb 36

stdin_fd:       equ 0           ; STDIN_FILENO
ICANON:         equ 1<<1
ECHO:           equ 1<<3

section .text
canonical_off:
        call read_stdin_termios

        ; clear canonical bit in local mode flags
        and dword [termios+12], ~ICANON

        call write_stdin_termios
        ret

echo_off:
        call read_stdin_termios

        ; clear echo bit in local mode flags
        and dword [termios+12], ~ECHO

        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

; clobbers RAX, RCX, RDX, R8..11 (by int 0x80 in 64-bit mode)
; allowed by x86-64 System V calling convention    
read_stdin_termios:
        push rbx

        mov eax, 36h
        mov ebx, stdin_fd
        mov ecx, 5401h
        mov edx, termios
        int 80h            ; ioctl(0, 0x5401, termios)

        pop rbx
        ret

write_stdin_termios:
        push rbx

        mov eax, 36h
        mov ebx, stdin_fd
        mov ecx, 5402h
        mov edx, termios
        int 80h            ; ioctl(0, 0x5402, termios)

        pop rbx
        ret

(編者注:不要在 64 位代碼中使用int 0x80如果在 64 位代碼中使用 32 位 int 0x80 Linux ABI 會發生什么? - 它會破壞 PIE 可執行文件(其中靜態地址不是在低 32 位),或者在堆棧上使用 termios 緩沖區。它實際上可以在傳統的非 PIE 可執行文件中工作,並且這個版本可以很容易地移植到 32 位模式。)

然后你可以這樣做:

call canonical_off

如果您正在閱讀一行文本,您可能還想這樣做:

call echo_off

這樣每個字符在輸入時都不會被回顯。

可能有更好的方法來做到這一點,但它適用於 64 位 Fedora 安裝。

更多信息可以在termios(3)的手冊頁或termbits.h源中找到

簡單的方法:對於文本模式程序,使用libncurses訪問鍵盤; 對於圖形程序,請使用Gtk+

困難的方法:假設一個文本模式程序,你必須告訴內核你想要單字符輸入,然后你必須做大量的簿記和解碼。 這真的很復雜。 沒有等效的舊 DOS getch()例程。 您可以在此處開始學習如何操作:終端 I/O 圖形程序更加復雜; 最低級別的 API 是Xlib

無論哪種方式,你都會瘋狂地編碼匯編中的任何東西; 改用 C。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM