/*++

Copyright (c) 2015 Minoca Corp. All Rights Reserved

Module Name:

    setjmpa.S

Abstract:

    This module implements functionality for non-local goto statements.

Author:

    Evan Green 17-Jan-2015

Environment:

    User Mode C Library

--*/

##
## ------------------------------------------------------------------- Includes
##

#include <minoca/kernel/x64.inc>

##
## ---------------------------------------------------------------- Definitions
##

##
## Define the register offsets within the jump buffer. The first offset is a
## boolean indicating whether or not the mask was saved. The next few are the
## signal mask. If the size of the signal mask changes, these offsets will need
## to be adjusted to make additional room.
##

#define JUMP_BUFFER_SAVE_MASK 0x00
#define JUMP_BUFFER_RBX 0x10
#define JUMP_BUFFER_RBP 0x18
#define JUMP_BUFFER_RSP 0x20
#define JUMP_BUFFER_R10 0x28
#define JUMP_BUFFER_R11 0x30
#define JUMP_BUFFER_R12 0x38
#define JUMP_BUFFER_R13 0x40
#define JUMP_BUFFER_R14 0x48
#define JUMP_BUFFER_R15 0x50
#define JUMP_BUFFER_RIP 0x58

##
## ----------------------------------------------------------------------- Code
##

ASSEMBLY_FILE_HEADER

##
## int
## setjmp (
##     jmp_buf Environment
##     )
##

/*++

Routine Description:

    This routine saves the calling environment into the given buffer for
    later use by longjmp.

Arguments:

    Environment - Supplies the pointer to the environment to save the
        application context in.

Return Value:

    0 if this was the direct call to set jump.

    Non-zero if this was a call from long jump.

--*/

EXPORTED_FUNCTION(_setjmp)
END_FUNCTION(_setjmp)
EXPORTED_FUNCTION(setjmp)
    movq    (%rsp), %rcx        # Load the return address.

    ##
    ## Mark the mask as having not been saved.
    ##

    movl    $0, JUMP_BUFFER_SAVE_MASK(%rdi)

    ##
    ## Save non-volatile registers.
    ##

    movq    %rbx, JUMP_BUFFER_RBX(%rdi)
    movq    %rbp, JUMP_BUFFER_RBP(%rdi)
    movq    %r10, JUMP_BUFFER_R10(%rdi)
    movq    %r11, JUMP_BUFFER_R11(%rdi)
    movq    %r12, JUMP_BUFFER_R12(%rdi)
    movq    %r13, JUMP_BUFFER_R13(%rdi)
    movq    %r14, JUMP_BUFFER_R14(%rdi)
    movq    %r15, JUMP_BUFFER_R15(%rdi)
    movq    %rcx, JUMP_BUFFER_RIP(%rdi)

    ##
    ## Save the stack.
    ##

    leaq    8(%rsp), %rdx       # Load the return address.
    movq    %rdx, JUMP_BUFFER_RSP(%rdi) # Save it to the RSP slot.
    xorq    %rax, %rax          # Return 0 to indicate return from setjmp.
    ret

END_FUNCTION(setjmp)

##
## int
## sigsetjmp (
##     sigjmp_buf Environment,
##     int SaveMask
##     )
##

/*++

Routine Description:

    This routine saves the calling environment into the given buffer for
    later use by longjmp.

Arguments:

    Environment - Supplies the pointer to the environment to save the
        application context in.

    SaveMask - Supplies a value indicating if the caller would like the
        current signal mask to be saved in the environment as well.

Return Value:

    0 if this was the direct call to setjmp.

    Non-zero if this was a call from longjmp.

--*/

EXPORTED_FUNCTION(sigsetjmp)
    movq    (%rsp), %rcx        # Load the return address.

    ##
    ## Mark the mask as having not been saved.
    ##

    movl    $0, JUMP_BUFFER_SAVE_MASK(%rdi)

    ##
    ## Save non-volatile registers.
    ##

    movq    %rbx, JUMP_BUFFER_RBX(%rdi)
    movq    %rbp, JUMP_BUFFER_RBP(%rdi)
    movq    %r10, JUMP_BUFFER_R10(%rdi)
    movq    %r11, JUMP_BUFFER_R11(%rdi)
    movq    %r12, JUMP_BUFFER_R12(%rdi)
    movq    %r13, JUMP_BUFFER_R13(%rdi)
    movq    %r14, JUMP_BUFFER_R14(%rdi)
    movq    %r15, JUMP_BUFFER_R15(%rdi)
    movq    %rcx, JUMP_BUFFER_RIP(%rdi)

    ##
    ## Save the stack.
    ##

    leaq    8(%rsp), %rdx       # Load the return address.
    movq    %rdx, JUMP_BUFFER_RSP(%rdi) # Save it to the RSP slot.

    ##
    ## Call the helper routine (with the same parameters) to save the signal
    ## mask.
    ##

    call    ClpSetJump          # Call helper with same arguments.
    xorq    %rax, %rax          # Return 0 to indicate return from setjmp.
    ret

END_FUNCTION(sigsetjmp)

##
## void
## ClpLongJump (
##     jmp_buf Environment,
##     int Value
##     )
##

/*++

Routine Description:

    This routine restores given environment.

Arguments:

    Environment - Supplies a pointer to the environment to restore.

    Value - Supplies the value to make appear as the return value from the
        set jump function.

Return Value:

    None, the function does not return.

--*/

FUNCTION(ClpLongJump)
    movq    JUMP_BUFFER_R15(%rdi), %r15
    movq    JUMP_BUFFER_R14(%rdi), %r14
    movq    JUMP_BUFFER_R13(%rdi), %r13
    movq    JUMP_BUFFER_R12(%rdi), %r12
    movq    JUMP_BUFFER_R11(%rdi), %r11
    movq    JUMP_BUFFER_R10(%rdi), %r10
    movq    JUMP_BUFFER_RBP(%rdi), %rbp
    movq    JUMP_BUFFER_RBX(%rdi), %rbx

    ##
    ## Restore the stack.
    ##

    movq    JUMP_BUFFER_RSP(%rdi), %rsp

    ##
    ## Move the return value to rax and jump to the original rip.
    ##

    movq    %rsi, %rax
    movq    JUMP_BUFFER_RIP(%rdi), %rdx
    jmp     *%rdx

END_FUNCTION(ClpLongJump)

