/*
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright © 2023 Keith Packard
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _NIOS2_SEMIHOST_H_
#define _NIOS2_SEMIHOST_H_

#include <stdio.h>
#include <errno.h>
#include <endian.h>
#include <sys/types.h>
#include <sys/stat.h>

#define HOSTED_EXIT  0
#define HOSTED_INIT_SIM 1
#define HOSTED_OPEN 2
#define HOSTED_CLOSE 3
#define HOSTED_READ 4
#define HOSTED_WRITE 5
#define HOSTED_LSEEK 6
#define HOSTED_RENAME 7
#define HOSTED_UNLINK 8
#define HOSTED_STAT 9
#define HOSTED_FSTAT 10
#define HOSTED_GETTIMEOFDAY 11
#define HOSTED_ISATTY 12
#define HOSTED_SYSTEM 13

#define GDB_O_RDONLY  0
#define GDB_O_WRONLY  1
#define GDB_O_RDWR    2
#define GDB_O_APPEND  8
#define GDB_O_CREAT   0x200
#define GDB_O_TRUNC   0x400
#define GDB_O_EXCL    0x800

typedef uint32_t my_mode_t;
typedef uint32_t my_time_t;

struct nios2_stat
{
    uint32_t   my_dev;     /* device */
    uint32_t   my_ino;     /* inode */
    my_mode_t  my_mode;    /* protection */
    uint32_t   my_nlink;   /* number of hard links */
    uint32_t   my_uid;     /* user ID of owner */
    uint32_t   my_gid;     /* group ID of owner */
    uint32_t   my_rdev;    /* device type (if inode device) */
    uint64_t   my_size;    /* total size, in bytes */
    uint64_t   my_blksize; /* blocksize for filesystem I/O */
    uint64_t   my_blocks;  /* number of blocks allocated */
    my_time_t  my_atime;   /* time of last access */
    my_time_t  my_mtime;   /* time of last modification */
    my_time_t  my_ctime;   /* time of last change */
};

static inline
copy_stat(struct stat *restrict statbuf, struct nios2_stat *nios2_stat)
{
        statbuf->st_dev = be32toh(nios2_stat->my_dev);
        statbuf->st_ino = be32toh(nios2_stat->my_ino);
        statbuf->st_mode = be32toh(nios2_stat->my_mode);
        statbuf->st_nlink = be32toh(nios2_stat->my_nlink);
        statbuf->st_uid = be32toh(nios2_stat->my_uid);
        statbuf->st_gid = be32toh(nios2_stat->my_gid);
        statbuf->st_rdev = be32toh(nios2_stat->my_rdev);
        statbuf->st_size = be32toh(nios2_stat->my_size);
        statbuf->st_blksize = be32toh(nios2_stat->my_blksize);
        statbuf->st_blocks = be32toh(nios2_stat->my_blocks);
        statbuf->st_atime = be32toh(nios2_stat->my_atime);
        statbuf->st_mtime = be32toh(nios2_stat->my_mtime);
        statbuf->st_ctime = be32toh(nios2_stat->my_ctime);
}

struct nios2_semihost {
    uintptr_t   args[4];
};

intptr_t
nios2_semihost(int func, struct nios2_semihost *args);

static inline intptr_t nios2_semihost1_immediate(int func, uintptr_t arg0) {
    return nios2_semihost(func, (struct nios2_semihost *) arg0);
};

static inline intptr_t nios2_semihost1(int func, uintptr_t arg0) {
    struct nios2_semihost args;
    intptr_t ret;

    args.args[0] = arg0;
    nios2_semihost(func, &args);
    ret = args.args[0];
    if (ret < 0)
        errno = args.args[1];
    return ret;
};

static inline intptr_t nios2_semihost2(int func, uintptr_t arg0, uintptr_t arg1) {
    struct nios2_semihost args;
    intptr_t ret;

    args.args[0] = arg0;
    args.args[1] = arg1;
    nios2_semihost(func, &args);
    ret = args.args[0];
    if (ret < 0)
        errno = args.args[1];
    return ret;
};

static inline intptr_t nios2_semihost3(int func, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2) {
    struct nios2_semihost args;
    intptr_t ret;

    args.args[0] = arg0;
    args.args[1] = arg1;
    args.args[2] = arg2;
    nios2_semihost(func, &args);
    ret = args.args[0];
    if (ret < 0)
        errno = args.args[1];
    return ret;
};

static inline uint64_t nios2_semihost4_64(int func, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
    struct nios2_semihost args;
    uint64_t ret;

    args.args[0] = arg0;
    args.args[1] = arg1;
    args.args[2] = arg2;
    args.args[3] = arg3;
    nios2_semihost(func, &args);
    ret = ((uint64_t) args.args[0] << 32) | args.args[1];
    if ((int64_t) ret < 0)
        errno = args.args[2];
    return ret;
};

static inline intptr_t nios2_semihost4(int func, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
    struct nios2_semihost args;
    intptr_t ret;

    args.args[0] = arg0;
    args.args[1] = arg1;
    args.args[2] = arg2;
    args.args[3] = arg3;
    nios2_semihost(func, &args);
    ret = args.args[0];
    if (ret < 0)
        errno = args.args[1];
    return ret;
};

#endif /* _NIOS2_SEMIHOST_H_ */