
#ifndef ZAN_SYS_H_
#define ZAN_SYS_H_

#if (!defined(__x86_64__) && !defined(__i386__)) || !defined(__linux__)
#error "This demo won't work on your platform! Linux on i386 or x86_64 is required!"
#endif

#include "def.h"

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>

#include "type.h"

/*
 * TODO:
 * * use a macro or something like that for less code duplication
 */

forcedinline uint8_t bsrb(uint8_t x) {
    asm volatile("bsrb %1, %0\n"
        :"=r" (x)
        :"r" (x)
        );

    return x;
}
forcedinline size_t bsrl(size_t x) {
    asm volatile("bsrl %1, %0\n"
        :"=r" (x)
        :"r" (x)
        );

    return x;
}

forcedinline size_t popcnt(size_t x) {
    asm volatile("popcnt %1, %0\n"
        :"=r" (x)
        :"r" (x)
        :);

    return x;
}

// ko na stika lo samselpla .i lo sutygau -- sei xusra -- te samrkompli
// so'iroi malgau lo proga

// ti no'e cmucpe .iku'i mi na kurji
inline static size_t strlen(const char* s) {
    size_t r = 0; //r ^= r;
    for (; *s; ++s, ++r);
    return r;
}

// TODO: syscall helper macro?

forcedinline __attribute__((__noreturn__)) void SYS_int3(void) {
    asm volatile("int3");
    __builtin_unreachable();
}

forcedinline __attribute__((__noreturn__)) ssize_t SYS_exit(int code) {
#if defined(__x86_64__)
    asm volatile("push $60\n"
                 "pop %%rax\n"
                 "syscall\n"
        ::"D" (code)
        :);//"rcx", "r11", "rax");
#else
    asm volatile("xor %%eax, %%eax\n"
                 "inc %%eax\n"
                 "int $0x80\n"
        ::"b" (code)
        :);//"eax");
#endif

    __builtin_unreachable();
}

forcedinline ssize_t SYS_fstat(int filed, struct stat* st) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $5\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (filed), "S" (st)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $108\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (filed), "c" (st)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_write(int filed, const void* buf, size_t len) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $1\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (filed), "S" (buf), "d" (len)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $4\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (filed), "c" (buf), "d" (len)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_read(int filed, const void* buf, size_t len) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("xorq %%rax, %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (filed), "S" (buf), "d" (len)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $3\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (filed), "c" (buf), "d" (len)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_open(const char* path, int flags/*, int mode*/) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $2\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (path), "S" (flags)//, "d" (mode)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $5\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (path), "c" (flags)//, "d" (len)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_close(int filed) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $3\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (filed)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $6\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (filed)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_fcntl(int filed, size_t cmd, size_t arg) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $72\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (filed), "S" (cmd), "d" (arg)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $55\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (filed), "c" (cmd), "d" (arg)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_ioctl(int filed, size_t cmd, void* arg) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $16\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (filed), "S" (cmd), "d" (arg)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $54\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (filed), "c" (cmd), "d" (arg)
        :);
#endif

    return retval;
}

// go home clang, you're drunk
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
forcedinline void* SYS_mmap(void* addr, size_t len, /*unsigned long prot,
        unsigned long flags,*/ int filed/*, off_t off*/) {
#pragma clang diagnostic pop
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");

    asm volatile(//"mov %[rflags], %%r10\n"
                 "mov %[filedr], %%r8\n"
                 //"mov %[offr], %%r9\n"
                 "mov %%r8, %%rdx\n"
                 "mov %%rdx, %%r10\n"
                 "xorl %%r9, %%r9\n"
                 "push $9\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (addr), "S" (len), [filedr] "r" (filed)
        //"d" (prot), [rflags] "r" (flags), [filedr] "r" ((ssize_t)filed), [offr] "r" (off)
        :"rcx","r11","r10","r8","r9");
#else
        asm("eax");

    /*struct mmap_arg_struct args;
    args.addr  = addr ;
    args.len   = len  ;
    args.prot  = prot ;
    args.flags = flags;
    args.fd    = (ssize_t)filed; // TODO: THIS IS BROKEN. FIX THIS.
    args.offset = off;

    asm volatile("push $90\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (&args)
        :);*/

    asm volatile("push %%ebx\n"
                 "xorl %%ebx, %%ebx\n"
                 "push %%ebx\n"
                 "push %%edx\n" // edx == 3
                 "push %%edx\n" // 1|_ -> MAP_SHARED
                 "push %%edx\n" // 3 -> PROT_READ|PROT_WRITE
                 //"push %%ecx\n"
                 //"push %%ebx\n"
                 "push %%eax\n"
                 "push %%ebx\n"
                 "push $90\n"
                 "pop %%eax\n"
                 "mov %%esp, %%ebx\n"
                 "int $0x80\n"
                 "pop %%ebx\n"
                 "pop %%ebx\n"
                 "pop %%ebx\n"
                 "pop %%ebx\n"
                 "pop %%ebx\n"
                 "pop %%ebx\n"
                 "pop %%ebx\n"
        :"=a" (retval)
        :"a" (len), "d" (filed) //"b" (prot), "c" (flags), "d" (filed) // ignore the other args
        :);
#endif

    return (void*)retval;
}

forcedinline ssize_t SYS_munmap(void* addr, size_t size) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $11\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (addr), "S" (size)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $91\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (addr), "c" (size)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_setitimer(int which, const struct itimerval *value, struct itimerval* ovalue) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $38\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (which), "S" (value), "d" (ovalue)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $104\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (which), "c" (value), "d" (ovalue)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_sigaction(int sig, const struct sigaction* act, struct sigaction* oact) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $8\n"
                 "push $13\n"
                 "pop %%rax\n"
                 "pop %%r10\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (sig), "S" (act), "d" (oact)
        :"rcx", "r11", "r10");
#else
        asm("eax");
    asm volatile("push $67\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (sig), "c" (act), "d" (oact)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_timer_create(clockid_t clock, struct sigevent* sev, timer_t* ntimer) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $222\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (clock), "S" (sev), "d" (ntimer)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $259\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (clock), "c" (sev), "d" (ntimer)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_timer_settime(timer_t timer, int flags, const struct itimerspec* spec, struct itimerspec* ospec) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("mov %[ospecr], %%r10\n"
                 "push $223\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (timer), "S" (flags), "d" (spec), [ospecr] "r" (ospec)
        :"rcx", "r11", "r10");
#else
        asm("eax");
    asm volatile("push $264\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (timer), "c" (flags), "d" (spec), "S" (ospec)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_clock_gettime(clockid_t clkid, struct timespec* t) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("push $228\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (clkid), "S" (t)
        :"rcx", "r11");
#else
        asm("eax");
    asm volatile("push $265\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (clkid), "c" (t)
        :);
#endif

    return retval;
}

forcedinline ssize_t SYS_clock_nanosleep(clockid_t clkid, int flags,
        const struct timespec* t, struct timespec* remain) {
    register ssize_t retval
#if defined(__x86_64__)
        asm("rax");
    asm volatile("mov %[remainr], %%r10\n"
                 "push $230\n"
                 "pop %%rax\n"
                 "syscall\n"
        :"=a" (retval)
        :"D" (clkid), "S" (flags), "d" (t), [remainr] "r" (remain)
        :"rcx", "r11", "r10");
#else
        asm("eax");
    asm volatile("push $267\n"
                 "pop %%eax\n"
                 "int $0x80\n"
        :"=a" (retval)
        :"b" (clkid), "c" (flags), "d" (t), "S" (remain)
        :);
#endif

    return retval;
}

#endif

