Source code NASM on Ubuntu 12.04
This demonstrates Input/Output algos using syscall and how they can be implemented in a fairly condensed loop. These routine do not conform to any specific call convention and without modification won't interface with higher level languages.
My code is stand alone assembly and doesn't use standard libraries either. Only dependance is on Linux kernel.
Main.S
STDIN equ 0
SYS_READ equ 0
global GetS
section .text
; ENTER: RCX = Maximum characters
; RDX = Pointer to input buffer
; LEAVE: RAX = Number of characters entered
; < 0 Entry was longer than RCX, excess truncated
; RCX = Amount of space left in buffer
; RDI = Points to next avaliable position
GetS push rsi
push rdx
; Setup registers for syscall. You may ask, why not do this in the caller, but Show
; and this procedure are designed to be companion routines and this facilitates less
; overhead for an algo where inside a loop theres a prompt and then an entry
; immediately afterward.
push rcx
pop rdx ; ARG3 = Maximum number of characters
push rdi
pop rsi ; ARG2 = Pointer to buffer
push 0
pop rdi ; ARG1 = STDIN
push rdi
pop rax ; ARG0 = SYS_READ
push rcx
syscall
pop rcx
; Now registers are setup as specified in LEAVE:
sub ecx, eax ; Determine amount of space left
dec eax
mov rdi, rsi ; Move base pointer back to RDI
add di, rax ; Next avaliable position
push rax ; RAX exit value
jz .Exit ; If nothing entered, all remains the same.
; Ok, operator has entered something, now we need determine if there was overflow
mov al, 0 ; Terminating byte
or ecx, ecx ; Is there space left
jnz .Exit ; If so, we know last character is CR
; At this point we might have overflow, only if last character is not CR
cmp byte [rdi], 10
jz .Exit ; ZF = 1, we used the exact amount of space
; Now we know there was overflow and a negated value will indicate this to caller
.Full inc rdi ; RDI is always after input CR or not
neg qword [rsp]
jmp . Exit + 1 ; No need to post NULL as last char, no room
.Exit stosb ; Replace CR with NULL
pop rax
pop rdx
pop rsi
or rax, rax ; Set appropriate flags
ret
Show.S
SYS_WRITE equ 1
STDOUT equ 1
global Show
section .text
; Display a null terminated string on monitor or to whever stream is directed.
; ENTER: RSI = Pointer to null terminated ASCII string.
; LEAVE: RAX = Character count including NULL (terminator)
; RSI = Next string (if it exists).
Show:
push rdi
push rdx ; Preserve those not to be changed
push rcx
; Determine strings length.
or rcx, -1 ; Hope its not this large
xor eax, eax
push rsi
pop rdi ; ARG2 = Pointer to string
repnz scasb ; Scan until terminator is found (hopefully)
neg rcx
sub ecx, 2
push rdi ; This is the address we want to return in RSI
; Now we can setup for system call.
; RDI = STDOUT
; RSI = Pointer to string
; RDX = Number of characters to display
push rcx ; Push/Pop combinations, less code & faster
pop rdx ; ARG3 = Character count
mov al, SYS_WRITE ; ARG0 = Function #
push rax
pop rdi ; ARG1 = File # (SYS_WRITE & STDOUT are equal)
syscall ; Output ASCII string
; I dont think its really necessary to check for errors here as operator will
; probably be aware without prompting.
pop rsi ; Address of next avaliable position
pop rcx
pop rdx ; Retrive registers
pop rdi
inc rax ; Bump count to include terminator
ret
GetS.S
STDIN equ 0
SYS_READ equ 0
global GetS
section .text
; ENTER: RCX = Maximum characters
; RDX = Pointer to input buffer
; LEAVE: RAX = Number of characters entered
; < 0 Entry was longer than RCX, excess truncated
; RCX = Amount of space left in buffer
; RDI = Points to next avaliable position
GetS push rsi
push rdx
; Setup registers for syscall. You may ask, why not do this in the caller, but Show
; and this procedure are designed to be companion routines and this facilitates less
; overhead for an algo where inside a loop theres a prompt and then an entry
; immediately afterward.
push rcx
pop rdx ; ARG3 = Maximum number of characters
push rdi
pop rsi ; ARG2 = Pointer to buffer
push 0
pop rdi ; ARG1 = STDIN
push rdi
pop rax ; ARG0 = SYS_READ
push rcx
syscall
pop rcx
; Now registers are setup as specified in LEAVE:
sub ecx, eax ; Determine amount of space left
dec eax
mov rdi, rsi ; Move base pointer back to RDI
add di, rax ; Next avaliable position
push rax ; RAX exit value
jz .Exit ; If nothing entered, all remains the same.
; Ok, operator has entered something, now we need determine if there was overflow
mov al, 0 ; Terminating byte
or ecx, ecx ; Is there space left
jnz .Exit ; If so, we know last character is CR
; At this point we might have overflow, only if last character is not CR
cmp byte [rdi], 10
jz .Exit ; ZF = 1, we used the exact amount of space
; Now we know there was overflow and a negated value will indicate this to caller
.Full inc rdi ; RDI is always after input CR or not
neg qword [rsp]
jmp . Exit + 1 ; No need to post NULL as last char, no room
.Exit stosb ; Replace CR with NULL
pop rax
pop rdx
pop rsi
or rax, rax ; Set appropriate flags
ret