;name:        sigfpe.asm
;
;build:       nasm "-felf64" sigfpe.asm -l sigfpe.lst -o sigfpe.o
;             ld -s -melf_x86_64 -o sigfpe sigfpe.o 
;
;description: Floating point exception signal handler demo
;
;reference:   Beejs guide to IPC - http://beej.us/guide/bgipc/output/html/multipage/signals.html#catchsig
;             http://syprog.blogspot.be/2011/10/iterfacing-linux-signals.html for the 64 bit structures
;             man 7 signal

bits 64

[list -]
    %include "unistd.inc"
    %include "signals.inc"
[list +]

section .rodata

    ; the messages
    msgDivisionByZero:  db  "Division by zero error", 10
    .len:               equ $-msgDivisionByZero
    ; error message
    msgSignalError:     db  "sigaction error, terminating program.", 10
    .len:               equ $-msgSignalError
    
section .data

    SIGACTION   sigaction

section .txt

global  _start
_start:
    ;Initialize SIGFPE handler
    mov     rax,sigFPEHandler                       ;set handler to pointer to procSigInt
    mov     qword[sigaction.sa_handler],rax         ;in sigaction structure
    mov     rax,SA_RESTORER | SA_SIGINFO            ;sa_flags
    mov     qword [sigaction.sa_flags],rax
    mov     rax,.exit
    mov     qword[sigaction.sa_restorer],rax
    mov     r10,NSIG_WORDS                          ;size of mask in 64 bit words
    syscall rt_sigaction,SIGFPE,sigaction,0
    ;If rax < 0 , we got an error, rax should be zero
    and     rax,rax
    js      .error

    ;Here we will count down from 10 to 0 each time dividing the counter to itself.
    ;once zero is reached we should trigger the SIGFPE signal and execute the signal action
    ;defined in ProcSIGFPE indicating a division by zero occured.
    ;There we can dump register contents as an example.
    mov     rcx,10
.repeat:
    mov     rax,rcx
  .oper:  
    idiv    rax                                     ;rax=rax/rax
    ;If no exception occured continue
    dec     rcx
    ;Here we check that rcx equals zero and we must exit the loop, however we will not exit and
    ;demonstrate a wrong compare with zero.
    and     rcx,rcx
    jge     .repeat            ;must be jg instead of jge

.done:    
    jmp     .exit
    ; in case we have an error
.error:
    syscall write, stdout, msgSignalError, msgSignalError.len
    ; exit the program
.exit:
    syscall exit, 0

; The SIGFPE Floating point exception handler
sigFPEHandler:
    ; rdi=signum, rsi=siginfo_t pointer, rdx=sigcontext*
    mov     rax,[rdx+UCONTEXT_STRUC.uc_mcontext+SIGCONTEXT_STRUC.rip]       ; get rip where error occured
    syscall write,stderr,msgDivisionByZero,msgDivisionByZero.len
    ret