const builtin = @import("builtin");
const std = @import("../../std.zig");
const SYS = std.os.linux.SYS;

pub const syscall_arg_t = u32;

pub fn syscall0(
    number: SYS,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
        : .{ .memory = true });
}

pub fn syscall1(
    number: SYS,
    arg1: syscall_arg_t,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
          [arg1] "{a6}" (arg1),
        : .{ .memory = true });
}

pub fn syscall2(
    number: SYS,
    arg1: syscall_arg_t,
    arg2: syscall_arg_t,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
          [arg1] "{a6}" (arg1),
          [arg2] "{a3}" (arg2),
        : .{ .memory = true });
}

pub fn syscall3(
    number: SYS,
    arg1: syscall_arg_t,
    arg2: syscall_arg_t,
    arg3: syscall_arg_t,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
          [arg1] "{a6}" (arg1),
          [arg2] "{a3}" (arg2),
          [arg3] "{a4}" (arg3),
        : .{ .memory = true });
}

pub fn syscall4(
    number: SYS,
    arg1: syscall_arg_t,
    arg2: syscall_arg_t,
    arg3: syscall_arg_t,
    arg4: syscall_arg_t,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
          [arg1] "{a6}" (arg1),
          [arg2] "{a3}" (arg2),
          [arg3] "{a4}" (arg3),
          [arg4] "{a5}" (arg4),
        : .{ .memory = true });
}

pub fn syscall5(
    number: SYS,
    arg1: syscall_arg_t,
    arg2: syscall_arg_t,
    arg3: syscall_arg_t,
    arg4: syscall_arg_t,
    arg5: syscall_arg_t,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
          [arg1] "{a6}" (arg1),
          [arg2] "{a3}" (arg2),
          [arg3] "{a4}" (arg3),
          [arg4] "{a5}" (arg4),
          [arg5] "{a8}" (arg5),
        : .{ .memory = true });
}

pub fn syscall6(
    number: SYS,
    arg1: syscall_arg_t,
    arg2: syscall_arg_t,
    arg3: syscall_arg_t,
    arg4: syscall_arg_t,
    arg5: syscall_arg_t,
    arg6: syscall_arg_t,
) u32 {
    return asm volatile ("syscall"
        : [ret] "={a2}" (-> u32),
        : [number] "{a2}" (@backingInt(number)),
          [arg1] "{a6}" (arg1),
          [arg2] "{a3}" (arg2),
          [arg3] "{a4}" (arg3),
          [arg4] "{a5}" (arg4),
          [arg5] "{a8}" (arg5),
          [arg6] "{a9}" (arg6),
        : .{ .memory = true });
}

pub fn clone() callconv(.naked) u32 {
    // __clone(func, stack, flags, arg, ptid, tls, ctid)
    //         a2,   a3,    a4,    a5,  a6,   a7,  +16
    //
    // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
    //         a2         a6,    a3,    a4,   a5,  a8
    if (builtin.abi != .call0) asm volatile (
        \\ entry sp, 16
    );
    asm volatile (
        \\ movi a8, -16
        \\ and a3, a3, a8
        \\
        \\ mov a9, a2
        \\ mov a10, a5
        \\
        \\ mov a5, a7
        \\ mov a8, a6
        \\ mov a6, a4
        \\ mov a4, a8
    );
    if (builtin.abi == .call0) asm volatile (
        \\ l32i a8, sp, 0
    ) else asm volatile (
        \\ l32i a8, sp, 16
    );
    asm volatile (
        \\ movi a2, 116 // SYS_clone
        \\ syscall
    );
    if (builtin.abi == .call0) asm volatile (
        \\ beqz a2, 1f
        \\ // parent
        \\ ret
        \\
        \\ // child
        \\1:
        \\ movi a15, 0
        \\ movi a0, 0
        \\
        \\ mov a2, a10
        \\ callx0 a9
    ) else asm volatile (
        \\ beqz a2, 1f
        \\ // parent
        \\ retw
        \\
        \\ // child
        \\1:
        \\ movi a7, 0
        \\ movi a0, 0
        \\
        \\ mov a6, a10
        \\ callx4 a9
    );
    asm volatile (
        \\ movi a2, 118 // SYS_exit
        \\ syscall
    );
}

pub const restore = restore_rt;

pub fn restore_rt() callconv(.naked) noreturn {
    switch (builtin.zig_backend) {
        .stage2_c => asm volatile (
            \\ movi a2, %[number]
            \\ syscall
            :
            : [number] "I" (@backingInt(SYS.rt_sigreturn)),
        ),
        else => asm volatile (
            \\ syscall
            :
            : [number] "{a2}" (@backingInt(SYS.rt_sigreturn)),
        ),
    }
}

pub const VDSO = void;

pub const time_t = i32;
