;name: waitforkeypress.asm
;
;;build: nasm -felf64 waitforkeypress.asm -o waitforkeypress.o
;       ld -melf_x86_64 waitforkeypress.o -o waitforkeypress
;
;description: Displays a message, in this case "press any key to exit..." and wait until the user hits
;             a key. With a buffer, large enough and wich you erases entirely after hitting a key
;             or key sequence (like ALT-[somekey], the remains of a hotkey aren't displayed neither.
;             The program works on most keys however CTRL, SUPER or ALT doesn't give the desired effect.
;             For a solution on that we must use the scancode of a key.
;             A better message should be, press any key except CTRL, SUPER or ALT

bits 64

%include "unistd.inc"
%include "sys/termios.inc"

global _start

section .bss

    buffer:     resb    5           ;one byte isn't enough on my system ...
    .length:    equ     $-buffer    ;(reason are often the hotkey sequences)

section .rodata

    message:	db	"Press any key to exit..."
    .length:	equ	$-message

section .data

    TERMIOS	termios                         ;termios structure
    
section .text

_start:
    ; first write a message to STDOUT
    syscall write,stdout,message,message.length
    call    TermIOS.Canonical.OFF           ;switch canonical mode off
    call    TermIOS.Echo.OFF                ;no echo
    ; read from stdin (usually the keyboard)
    syscall read,stdin,buffer,buffer.length   
    ; clear the buffer
    mov     qword[rsi],10
    syscall write,stdout,buffer,buffer.length
    ; Don't forget to switch canonical mode on
    call    TermIOS.Canonical.ON            ;switch canonical mode back on
    call    TermIOS.Echo.ON                 ;restore echo
    syscall exit,0
    
; **********************************************************************************************    
; TERMIOS functions:
; TermIOS.Canonical.ON        : switch canonical mode on
; TermIOS.Canonical.OFF       : switch canonical mode off
; TermIOS.Echo.ON             : switch echo mode on
; TermIOS.Echo.OFF            : switch echo mode off
; TermIOS.LocalModeFlag.SET   : set the localmode flag described in RAX
; TermIOS.LocalModeFlag.CLEAR : clear the localmode flag described in RAX 
; TermIOS.STDIN.Read          : Read the TERMIO flags
; TermIOS.STDIN.Write         : Write the TERMIO flags
; TermIOS.IOCTL               : Read or write the localmode flags to or from TERMIOS
; **********************************************************************************************

TermIOS.Canonical:
.ON:
    mov     rax,ICANON
    jmp     TermIOS.LocalModeFlag.SET
.OFF:
    mov     rax,ICANON
    jmp     TermIOS.LocalModeFlag.CLEAR
TermIOS.Echo:
.ON:
    mov     rax,ECHO
    jmp     TermIOS.LocalModeFlag.SET
.OFF:
    mov     rax,ECHO
    jmp     TermIOS.LocalModeFlag.CLEAR
TermIOS.LocalModeFlag:
.SET:
    call    TermIOS.STDIN.READ
    or      dword[termios.c_lflag],eax
    call    TermIOS.STDIN.WRITE
    ret
.CLEAR:
    call    TermIOS.STDIN.READ
    not     eax
    and     [termios.c_lflag],eax
    call    TermIOS.STDIN.WRITE
    ret
; subroutine for all TCGETS operation on the syscall IOCTL
; the original value of RCX is restored on exit
TermIOS.STDIN:
.READ:
    mov     rsi,TCGETS
    jmp     TermIOS.IOCTL
; subroutine for all TCSETS operation on the syscall IOCTL
; the original value of RCX is restored on exit
.WRITE:
    mov     rsi,TCSETS
; subroutine for operations on the syscall IOCTL for STDIN
; all registers are restored to their original values on exit of the subroutine
TermIOS.IOCTL:
    push    rax                             ;we need to store RAX or this routine will fail
    mov     rdx,termios
    syscall ioctl,stdin
    pop     rax
    ret