#ifndef SIG_H
#define SIG_H

#include <unistd.h>
#include <signal.h>
#include <ucontext.h>
#if defined(__linux__)
#include <sys/syscall.h>
#include "sys_vm86.h"  // for SIG define
#endif

/* reserve 1024 uncommitted pages for stack */
#define SIGSTACK_SIZE (1024 * getpagesize())

#if defined(__x86_64__) && defined(__linux__)
#define ARCH_SET_GS 0x1001
#define ARCH_SET_FS 0x1002
#define ARCH_GET_FS 0x1003
#define ARCH_GET_GS 0x1004
/* code arch_prctl() in asm as TLS addr may be invalid upon call */
static inline int dosemu_arch_prctl(int code, void *addr)
{
  int _result;

  asm volatile ("syscall\n"
		   : "=a" (_result)
		   : "0" ((unsigned long int) __NR_arch_prctl),
		     "D" ((unsigned long int) code),
		     "S" (addr)
		   : "memory", "cc", "r11", "cx");
  return _result;
}
#else
#define ARCH_SET_GS 0
#define ARCH_SET_FS 0
#define ARCH_GET_FS 0
#define ARCH_GET_GS 0
static inline int dosemu_arch_prctl(int code, void *addr)
{
  return 0;
}
#endif

extern void add_thread_callback(void (*cb)(void *), void *arg, const char *name);
extern void SIG_init(void);
extern void SIG_close(void);

/* signals for Linux's process control of consoles
   Note: on macOS, SIGRTMIN doesn't exist, but neither do Linux consoles, so
   we can just use SIGUSR1 then for SIG_THREAD_NOTIFY
 */
#ifdef SIGRTMIN
#define SIG_RELEASE     SIGUSR1
#define SIG_ACQUIRE     SIGUSR2
#define SIG_THREAD_NOTIFY (SIGRTMIN + 0)
#else
#define SIG_THREAD_NOTIFY SIGUSR1
#endif

typedef mcontext_t sigcontext_t;

#if defined(__FreeBSD__)
#ifdef __x86_64__
#define _scp_eax DWORD_(scp->mc_rax)
#define _scp_ebx DWORD_(scp->mc_rbx)
#define _scp_ecx DWORD_(scp->mc_rcx)
#define _scp_edx DWORD_(scp->mc_rdx)
#define _scp_ebp DWORD_(scp->mc_rbp)
#define _scp_esp DWORD_(scp->mc_rsp)
#define _scp_esi DWORD_(scp->mc_rsi)
#define _scp_edi DWORD_(scp->mc_rdi)
#define _scp_eip DWORD_(scp->mc_rip)
#define _scp_eflags (*(unsigned *)&scp->mc_rflags)
#define _scp_eflags_ (*(const unsigned *)&scp->mc_rflags)
#define _scp_cr2 scp->mc_addr
#define _scp_rip scp->mc_rip
#define _scp_rsp scp->mc_rsp
#define PRI_RG PRIx64
#else
#define _scp_eax scp->mc_eax
#define _scp_ebx scp->mc_ebx
#define _scp_ecx scp->mc_ecx
#define _scp_edx scp->mc_edx
#define _scp_ebp scp->mc_ebp
#define _scp_esp scp->mc_esp
#define _scp_esi scp->mc_esi
#define _scp_edi scp->mc_edi
#define _scp_eip scp->mc_eip
#define _scp_eflags scp->mc_eflags
#define _scp_eflags_ scp->mc_eflags
#define _scp_cr2 scp->mc_addr
#define PRI_RG PRIx32
#endif
#define _scp_cs (*(unsigned *)&scp->mc_cs)
#define _scp_ds (*(unsigned *)&scp->mc_ds)
#define _scp_es (*(unsigned *)&scp->mc_es)
#define _scp_ds_ (*(const unsigned *)&scp->mc_ds)
#define _scp_es_ (*(const unsigned *)&scp->mc_es)
#define _scp_fs (*(unsigned *)&scp->mc_fs)
#define _scp_gs (*(unsigned *)&scp->mc_gs)
#define _scp_ss (*(unsigned *)&scp->mc_ss)
#define _scp_trapno scp->mc_trapno
#define _scp_err (*(unsigned *)&scp->mc_err)
#define _scp_fpstate scp->mc_fpstate
#elif defined(__APPLE__)
#define _scp_eax    DWORD_((*scp)->__ss.__rax)
#define _scp_edx    DWORD_((*scp)->__ss.__rdx)
#define _scp_cs     ((*scp)->__ss.__cs)
#define _scp_eflags ((*scp)->__ss.__rflags)
#define _scp_rip    ((*scp)->__ss.__rip)
#define _scp_rsp    ((*scp)->__ss.__rsp)
#define _scp_rbp    ((*scp)->__ss.__rbp)
#define _scp_rdi    ((*scp)->__ss.__rdi)
#define _scp_cr2    ((*scp)->__es.__faultvaddr)
#define _scp_trapno ((*scp)->__es.__trapno)
#define _scp_err    ((*scp)->__es.__err)
#define PRI_RG PRIx64
#elif defined(__linux__)
#define _scp_fpstate (scp->fpregs)
#ifdef __x86_64__
#define _scp_es     HI_WORD(scp->gregs[REG_TRAPNO])
#define _scp_ds     (((union g_reg *)&(scp->gregs[REG_TRAPNO]))->w[2])
#define _scp_rip    (scp->gregs[REG_RIP])
#define _scp_rsp    (scp->gregs[REG_RSP])
#define _scp_rbp    (scp->gregs[REG_RBP])
#define _scp_rdi    (scp->gregs[REG_RDI])
#define _scp_edi    DWORD_(scp->gregs[REG_RDI])
#define _scp_esi    DWORD_(scp->gregs[REG_RSI])
#define _scp_ebp    DWORD_(scp->gregs[REG_RBP])
#define _scp_ebx    DWORD_(scp->gregs[REG_RBX])
#define _scp_edx    DWORD_(scp->gregs[REG_RDX])
#define _scp_ecx    DWORD_(scp->gregs[REG_RCX])
#define _scp_eax    DWORD_(scp->gregs[REG_RAX])
#define _scp_eip    DWORD_(_scp_rip)
#define _scp_esp    DWORD_(_scp_rsp)
#define _scp_err    LO_WORD(scp->gregs[REG_ERR])
#define _scp_trapno LO_WORD(scp->gregs[REG_TRAPNO])
#define _scp_cs     (((union g_reg *)&scp->gregs[REG_CSGSFS])->w[0])
#define _scp_gs     (((union g_reg *)&scp->gregs[REG_CSGSFS])->w[1])
#define _scp_fs     (((union g_reg *)&scp->gregs[REG_CSGSFS])->w[2])
#define _scp_ss     (((union g_reg *)&scp->gregs[REG_CSGSFS])->w[3])
#define _scp_eflags LO_WORD(scp->gregs[REG_EFL])
#define _scp_cr2    (scp->gregs[REG_CR2])
#ifdef __GLIBC__
#define PRI_RG  "llx"
#else
#define PRI_RG  PRIx64
#endif
#else
#define _scp_es     (scp->gregs[REG_ES])
#define _scp_ds     (scp->gregs[REG_DS])
#define _scp_edi    (scp->gregs[REG_EDI])
#define _scp_esi    (scp->gregs[REG_ESI])
#define _scp_ebp    (scp->gregs[REG_EBP])
#define _scp_esp    (scp->gregs[REG_ESP])
#define _scp_ebx    (scp->gregs[REG_EBX])
#define _scp_edx    (scp->gregs[REG_EDX])
#define _scp_ecx    (scp->gregs[REG_ECX])
#define _scp_eax    (scp->gregs[REG_EAX])
#define _scp_eip    (scp->gregs[REG_EIP])
#define _scp_cs     (scp->gregs[REG_CS])
#define _scp_gs     (scp->gregs[REG_GS])
#define _scp_fs     (scp->gregs[REG_FS])
#define _scp_ss     (scp->gregs[REG_SS])
#define _scp_err    (scp->gregs[REG_ERR])
#define _scp_trapno (scp->gregs[REG_TRAPNO])
#define _scp_eflags (scp->gregs[REG_EFL])
#define _scp_cr2    (((union dword *)&scp->cr2)->d)
#define PRI_RG PRIx32
/* some compat */
#define _scp_rip _scp_eip
#define _scp_rsp _scp_esp
#endif
#elif defined(__HAIKU__)

#ifdef __x86_64__
#define _scp_eax scp->rax
#define _scp_edx scp->rdx
#define _scp_edi scp->rdi
#define _scp_rdi scp->rdi
#define _scp_eflags scp->rflags
#define _scp_eip scp->rip
#define _scp_rip scp->rip
#define _scp_esp scp->rsp
#define _scp_rsp scp->rsp
#define _scp_rbp scp->rbp
#define _scp_trapno scp->fpu.fp_fxsave.trap_number
#define _scp_err scp->fpu.fp_fxsave.error_code
#define _scp_cr2 scp->fpu.fp_fxsave.fault_address
#define _scp_cs scp->fpu.fp_fxsave.cs
#define PRI_RG PRIx64
#elif defined(__i386__)
#define _scp_eax scp->eax
#define _scp_edx scp->edx
#define _scp_edi scp->edi
#define _scp_rdi scp->edi
#define _scp_eflags scp->eflags
#define _scp_rip scp->eip
#define _scp_eip scp->eip
#define _scp_esp scp->esp
#define _scp_rsp scp->esp
#define _scp_rbp scp->ebp
#define _scp_trapno scp->xregs.state.new_format.trap_number
#define _scp_err scp->xregs.state.new_format.error_code
#define _scp_cr2 scp->xregs.state.new_format.fault_address
#define _scp_cs scp->xregs.state.new_format.cs
#define PRI_RG PRIx32
#else
#error "Unknown or unupported CPU architecture"
#endif
#endif  // __linux__

extern void SIGNAL_save( void (*signal_call)(void *), void *arg, size_t size,
	const char *name );
extern void handle_signals(void);
extern void reset_signals(void);
extern void do_periodic_stuff(void);
extern void sig_ctx_prepare(int tid, void *arg, void *arg2);
extern void sig_ctx_restore(int tid, void *arg, void *arg2);

extern int sigchld_register_handler(pid_t pid, void (*handler)(void*),
	void *arg);
extern int sigchld_enable_cleanup(pid_t pid);
extern int sigchld_enable_handler(pid_t pid, int on);
extern int sigalrm_register_handler(void (*handler)(void));
extern void registersig(int sig, void (*handler)(siginfo_t *));
extern void registersig_std(int sig, void (*handler)(void *));

void signal_block_async_nosig(sigset_t *old_mask);
void signal_unblock_fatal_sigs(void);
void signal_unblock_async_sigs(void);
void signal_restore_async_sigs(void);
void sigchld_set_critical(void (*handler)(int), struct sigaction *act);
void sigchld_unset_critical(const struct sigaction *act);

void leavedos_sig(int sig);
void handle_fault(int sig, const siginfo_t *si, sigcontext_t *scp);

extern pthread_t dosemu_pthread_self;
extern pid_t dosemu_pid;
extern pid_t dpmi_pid;
extern int in_rdpmi;
extern sigset_t q_mask;
extern sigset_t nonfatal_q_mask;
extern sigset_t all_sigmask;
extern int sig_threads_wa;

/* On glibc SIGRTMAX is not a constant but NSIG covers rt signals.
 * On bsd its all the other way around. */
#if defined(__GLIBC__) || !defined(SIGRTMAX)
#define SIGMAX NSIG
#elif defined(__FreeBSD__)
#define SIGMAX SIGRTMAX
#else
#define SIGMAX 64
#endif

#endif
