Input loop is part of Short Circuit's Console Program Basics selection.
Task

Read from a text stream either word-by-word or line-by-line until the stream runs out of data.

The stream will have an unknown amount of data on it.




#define SYS_WRITE   $1
#define SYS_OPEN    $2
#define SYS_CLOSE   $3
#define SYS_FSTAT   $5
#define SYS_MMAP    $9
#define SYS_MUNMAP  $11
#define SYS_EXIT    $60
 
// From experiments:
#define FSIZEOFF    48
#define STATSIZE    144
 
// From Linux source:
#define RDONLY      $00     
#define PROT_READ   $0x1
#define MAP_PRIVATE $0x02
#define STDIN       $0
#define STDOUT      $1
 
.global _start
.text
 
/* Details: */
/* 
    Remember:  %rax(%rdi, %rsi, %rdx, %r10, %r8, %r9)
    - Open a file (get its fd)
        - int fd = open("filename", RDONLY)
    - Get its filesize:
        - fstat(fd, statstruct). 0 if ok. fsize at statstruct+48
    - Then memory map it.
        - void* vmemptr = mmap(vmemptr, fsize, PROT_READ, MAP_PRIVATE, fd, 0)
    - Scan for newlines, print line.
        - Keep going until done. Details at 11.
    - Unmap memory
        - munmap(vmemptr, filesize). 0 if ok.
    - Exit
 */
 
.macro ERRCHECK code
    cmpq    $\code, %rax
    je      fs_error
.endm
 
/* Local stack notes: 
    0: int fd
    4: void* vmemptr
    12: void* head
    20: void* lookahead
    28: void* end
*/
_start:
    // Open:
    movq    RDONLY, %rsi
    // Filename ptr is on stack currently as argv[1]:
    cmpq    $1, (%rsp)          // if argc is 1, default to stdin
    jnz     open_file
    subq    $36, %rsp           // local stack
    movl    STDIN, (%rsp)
    jmp     fstat
 
    open_file:
    movq    16(%rsp), %rdi      // argc(8), argv0(8) => rsp+16. filename
    movq    SYS_OPEN, %rax
    syscall
    ERRCHECK    -1
    subq    $36, %rsp           // local stack
    movl    %eax, (%rsp)        // int fd = open(argv[1], RDONLY)
 
    // fstat to get filesize
    fstat:
    movq    $statstruct, %rsi
    movl    (%rsp), %edi        // fd
    movq    SYS_FSTAT, %rax
    syscall                     // fstat(fd, statstruct)
    ERRCHECK    -1
 
    // mmap - don't forget to munmap.
    mmap:
    movq    $0, %r9             // offset
    movq    (%rsp), %r8         // fd
    movq    MAP_PRIVATE, %r10
    movq    PROT_READ, %rdx
    movq    filesize, %rsi
    movq    (%rsp), %rdi        // vmemptr
    movq    SYS_MMAP, %rax
    syscall
    ERRCHECK    -1
    movq    %rax, 4(%rsp)       // void* vmemptr = mmap(vmemptr, fsize, PROT_READ, MAP_PRIVATE, fd, 0)
 
    /* Print lines */ 
    movq    %rax, 12(%rsp)  // head = vmemptr
    addq    filesize, %rax
    decq    %rax
    movq    %rax, 28(%rsp)  // end = vmemptr+filesize-1
    scan_outer:
        movq    12(%rsp), %rax
        cmpq    28(%rsp), %rax
        jge     cleanup         // if head >= end, done
        movq    %rax, %rbx      // Using rbx as lookahead
        scan_inner:
            cmpq    28(%rsp), %rbx
            jge     writeline       // if lookahead >= end, write the line.
            cmpb    $'\n, (%rbx)
            jz      writeline       // if '\n'==*lookahead, write the line
            incq    %rbx
            jmp     scan_inner
        writeline:
            // write:
            incq    %rbx
            movq    %rbx, %rdx
            subq    12(%rsp), %rdx  // rdx <- lookahead-head
            movq    12(%rsp), %rsi
            movq    STDOUT, %rdi
            movq    SYS_WRITE, %rax
            syscall                 // write(stdout, head, lookahead-head)
            safety:
            movq    %rbx, 12(%rsp)  // head = lookahead.
            jmp     scan_outer
 
    cleanup:
    // munmap
    movq    filesize, %rsi
    movq    4(%rsp), %rdi
    movq    SYS_MUNMAP, %rax
    syscall                     // munmap(vmemptr, filesize)
    cmpq    $-1, %rax
    je      fs_error
    // close
    movl    (%rsp), %edi
    movq    SYS_CLOSE, %rax
    syscall                     // close(fd)
    ERRCHECK    -1
 
exit:
    movq    SYS_EXIT, %rax
    xorq    %rdi, %rdi              // The exit code.
    syscall
 
fs_error:
    movq    SYS_EXIT, %rax
    movq    $-1, %rdi
    syscall                         // exit(-1)
 
.data
statstruct:     // This struct is 144 bytes. Only want size (+48)
    .zero FSIZEOFF
    filesize:  // 8 bytes.
    .quad   0
    .zero   STATSIZE-FSIZEOFF+8