Question

I'm trying to learn NASM. I want to write a procedure to get one character at a time from the console until a newline (0xA) is encountered using only kernel calls. So far I have

global _start

section .data
    sys_read     equ 3
    sys_write    equ 4
    stdin        equ 0
    stdout       equ 1      
section .bss
    line         resb 11
    index        resb 4

section .text

_start:
    push ebp
    mov ebp, esp

    call _readLine
    afterReadLine:

    call _printLine 

    mov esp, ebp
    pop ebp
    jmp exit

_readLine:
; Reads into line until new line (0xA)
; Number of bytes read will be stored in index when _readLine returns   
    mov eax, sys_read             ; syscall to read
    mov ebx, stdin                ; stdin
    mov edx, [index]              ; put index into edx
    mov ecx, dword line           ; put line addr in ecx
    add ecx, edx                  ; add index to addr in ecx
    mov edx, 1                    ; read one char
    int 0x80                      ; call kernel to read char
    mov ecx, [index]              ; put index into ecx
    cmp dword [line + ecx], 0xA   ; compare value at line + ecx to new line char
    inc byte [index]              ; increment index
    je afterReadLine              ; if last char is newline return
    jne _readLine                 ; if last char is not new line, loop  

_printLine:
    mov eax, sys_write
    mov ebx, stdout
    mov ecx, line
    mov edx, [index]
    int 0x80
    ret

exit:
    mov     eax, 01h        ; exit()
    xor     ebx, ebx        ; errno
    int     80h

When I test against the value stored in the index variable at the end, it always equals 0. I tried moving the value of index into eax, but it's also zero after the jump. I tried using the ret keyword, but that also seemed to overwrite my values. What is the best practice way to return the value of characters read from this procedure?

EDIT:

I tried the following and I'm still not getting any output. With input "abcd[newline]" the program outputs "abcd" if I hardcode the value of 4 in edx in the _printLine procedure, but as written won't output anything.

global _start

section .data
    sys_read     equ 3
    sys_write    equ 4
    stdin        equ 0
    stdout       equ 1
    bytesRead    dd  0
    termios:        times 36 db 0
    ICANON:         equ 1<<1
    ECHO:           equ 1<<3

section .bss
    line         resb 11
    index        resb 4

section .text

_start:
    push ebp
    mov ebp, esp

    call canonical_off
    call echo_off

    call _readLine 
    call _printLine 

    call canonical_on
call echo_on

    mov esp, ebp
    pop ebp
    jmp exit

_readLine:
; Reads into line until new line (0xA)
; Number of bytes read will be stored in bytesRead when _readLine returns   
    mov eax, sys_read             ; syscall to read
    mov ebx, stdin                ; stdin
    mov edx, [index]              ; put index into edx
    mov ecx, dword line           ; put line addr in ecx
    add ecx, edx                  ; add index to addr in ecx
    mov edx, 1                    ; read one char
    int 0x80                      ; call kernel to read char
    mov ecx, [index]              ; put index into ecx
    cmp dword [line + ecx], 0xA   ; compare value at line + ecx to new line char
    inc byte [index]              ; increment index
    jne _readLine                 ; if last char is not new line, loop  
    ret

_printLine:
    mov eax, sys_write
    mov ebx, stdout
    mov ecx, line
    mov edx, [index]  ; Works if hardcoded 4 here!
    int 0x80
    ret

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

exit:
    mov     eax, 01h        ; exit()
    xor     ebx, ebx        ; errno
    int     80h

link here http://ideone.com/Lw3fyV

Was it helpful?

Solution

First off, you are calling _readLine but not returning from it, instead you are jumping to the label after call _readLine conveniently called afterReadLine. You should use ret to exit from your function.

Now to your question. By default, the terminal is in canonical mode (Cooked mode), meaning all input is buffered. The system will fill your buffer until the return key is pressed and adds 0xA to the end of the buffer.

What you want is non-canonical mode (raw mode). This means the system does not process the key presses but passes it on to you.

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

By switching to raw mode, you can get each character pressed until the return key and do what you will with it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top