/*************************************************************************
Copyright (c) 2014 by Şükrü Çınar.

All rights reserved.

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.

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.

                    COOPERATIVE THREADS

 Single file cooperative thread library. Include coth.c in your source files
 to get the interface. Define COTH_CODE before including in order to compile
 code. Alternatively, compile coth.c separately with -DCOTH_CODE. If you 
 don't want message queues, define COTH_NO_MSG.

 This library is compatible with the following architectures:
  - ARM32 hard/soft float on Linux
  - x86-32 and x86-64 on Linux

 You may also define COTH_PTHREAD in order to use an implementation based
 on pthreads. This alternative has no architecture limit but isn't as
 efficient as native implementations. In order to use valgrind with your
 program, you'll need to use this alternative because valgrind gets
 confused when you switch stacks.

 In order to use the native implementations, this file should be compiled
 with GCC because it makes use of asm blocks at the top level.

 If you define COTH_SELF_TEST, this file will compile into an executable
 which tests basic functionality.

 Another available macro is COTH_STATIC. If defined, all functions will
 be static, making this file suitable for directly embedding in another
 C source file. In this case, you may get a warning which says that 
 coth_switch is used but not defined. It's fine, I couldn't find a way
 to disable that warning.

 coth_t is an opaque structure representing a cooperative thread. 

    coth_t* coth_main();

 Creates a context corresponding to the main thread. This context uses the
 stack provided by the OS.
 
    coth_t* coth_new(void *faddr,size_t stack_size);

 Creates a new cothread, to begin execution at the function address faddr.
 This function should never return. When it's done, it should switch to
 another thread and there it will be deallocated. Stack for the created
 thread is allocated from heap, at given fixed size. Cothreads should not
 use excessive recursion or big arrays on the stack. The stack doesn't have
 the ability to grow. 

 The thread doesn't actually start running until a coth_switch is done to it.

    void coth_arg_p(coth_t *ctx, int index, void *arg);

 Provides an argument to the thread function. This works only before the
 thread is actually started (i.e. first coth_switch to it). index is between
 1 and 4. 

    void coth_arg_i(coth_t *ctx, int index, uint64_t value);
    void coth_arg_i(coth_t *ctx, int index, uint32_t value);

 Similar to coth_arg_p, but with integer argument. Type of the argument
 depends on the word size of the CPU.

     void coth_switch(coth_t *self,coth_t *dest);

 Switches to context dest, while saving the state of the current thread
 in self. 

     void coth_free(coth_t *ctx);

 Frees the cothread object. In this library, a cothread is nothing but a
 piece of memory. There are no kernel entries, signal handlers, nothing
 at all. However, if the messaging code is not disabled, there may be
 queued messages for the thread, these should be retrieved and disposed
 of before calling coth_free.

     void coth_msg_put(coth_t *ctx, int cmd, void *data);

 Puts a message on the queue for the given thread. Neither cmd nor data
 is interpreted in any way by the library.

     int coth_msg_get(coth_t *ctx, int *Rcmd, void **Rdata);

 Gets a message from the given thread queue. Returns 0 if the operation
 was successful, 1 if the queue was empty. Rcmd and Rdata can be NULL
 if you're not interested in that part of the messages.
 
 *************************************************************************/
#ifndef COTH_H_INCLUDED
#define COTH_H_INCLUDED

#ifdef COTH_STATIC
#undef COTH_STATIC
#define COTH_STATIC static
#define COTH_STATIC_ASM
#else
#define COTH_STATIC
#endif

#ifndef COTH_PTHREAD

#if defined(__amd_64__) || defined(__amd_64) || \
    defined(__x86_64__) || defined(__x86_64)
#define COTH_X64_LINUX 1
#endif

#if defined(__arm__) 
#if defined(__ARM_PCS_VFP)
#define COTH_ARM32HF 1
#else 
#define COTH_ARM32SF 1
#endif
#endif

#if defined(i386) || defined(__i386) || defined(__i386__)
#define COTH_X32_LINUX 1
#endif

#else

#if defined(__amd_64__) || defined(__amd_64) || \
    defined(__x86_64__) || defined(__x86_64)
#define COTH_PTHREAD_WORD uint64_t
#else
#define COTH_PTHREAD_WORD uint32_t
#endif

#endif // COTH_PTHREAD

#include <stdlib.h>
#include <stdint.h>
#ifdef COTH_PTHREAD
#include <pthread.h>
#include <setjmp.h>
#endif

typedef struct  coth      coth_t;
#ifdef __cplusplus
extern "C"
{
#endif

COTH_STATIC coth_t *    coth_main();
COTH_STATIC coth_t *    coth_new(void *faddr,size_t stack_size);
COTH_STATIC void        coth_switch(coth_t *,coth_t *);
COTH_STATIC void        coth_free(coth_t *);


COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void *arg);

#ifndef COTH_PTHREAD

#if defined(COTH_X64_LINUX)
COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint64_t value);
#else
COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint32_t value);
#endif

#else

COTH_STATIC void coth_arg_i(coth_t *ctx, int index, COTH_PTHREAD_WORD value);

#endif


#ifndef COTH_NO_MSG
COTH_STATIC void coth_msg_put(coth_t *ctx, int cmd, void *data);
COTH_STATIC int  coth_msg_get(coth_t *ctx, int *Rcmd, void **Rdata);
#endif


#ifdef __cplusplus
}
#endif

#endif

#ifdef COTH_CODE

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <limits.h>

#define COTHP_MAXARGS 4

//
// for windows   _M_
// _M_X64    or   _M_AMD64
//

#ifndef COTH_NO_MSG
typedef struct coth_msg
{
  int cmd;
  void *data;
  struct coth_msg *next;
} coth_msg_t;
#endif

struct coth
{
#ifdef COTH_X64_LINUX
   uint64_t register_store[16];
#endif
#ifdef COTH_X32_LINUX
   uint32_t register_store[8];
   uint32_t *arg_area;
#endif
#ifdef COTH_ARM32HF
   uint32_t register_store[31];
#endif
#ifdef COTH_ARM32SF
   uint32_t register_store[15];
#endif
   unsigned char *stack_low;
   size_t stack_size;
#ifndef COTH_NO_MSG
   coth_msg_t *msg_first, *msg_last;
#endif
#ifdef COTH_PTHREAD
   pthread_t thread;
   pthread_mutex_t mutex;
   pthread_cond_t cond;
   int enabled;
   int quit;
   void (*f)(void*,void*,void*,void*);
   void * args[COTHP_MAXARGS];
   jmp_buf jbuf;
   int main;
#endif
};

/****
         X86-64 CALLING CONVENTION ON LINUX
              (only the relevant part) 

  Arguments are passed in registers, in this order from left to right:
    %rdi, %rsi, %rdx, %rcx, %r8, %r9

  These registers are callee-saved:
    %rbx, %rsp, %rbp, %r12, %r13, %r14, %r15, 
    mxcsr (sse2 control word), x87cw (x87 control word)

  All other registers, including the floating point stack and XMMS
  registers are temporary, meaning that they are not preserved across
  a function call.

  Upon entry to a function, the stack looks like (left = high address):

  ... [return address] 

  When coth_switch returns, stack pointer will point to the end of the
  stack area, which is normally aligned to 16 bytes.

  When we enter coth_switch, I will first store the stack pointer and then
  use it to store other registers.

  Doing the reverse, first I will load %rsp from (%rsi), and then
  pop all the registers. 

  Here is the order of registers, from low memory to high memory.
   0:  %r15, %r14, %r13, %r12
   4:  %r11, %r10, %r9,  %r8
   8:  %rdi, %rsi, %rbp, %rdx
  12:  %rcx, %rbx, %rax, %rsp

  I will push all registers, just to make things easier to follow.
  Normally %rax, %r10, %r11 don't need to be saved because they have no
  function in basic parameter passing and are not callee-saved registers.
  However, there are some claims about %r11 having a special function
  regarding dynamic linking etc, so I will save everything just to be sure.

 ****/

#ifdef COTH_X64_LINUX
__asm__ ( "\t"
#ifndef COTH_STATIC_ASM
   ".globl coth_switch\n\t"
#endif
   ".type coth_switch, @function\n"
"coth_switch:\n\t"
   "addq $120, %rdi\n\t"
   "movq %rsp, (%rdi)\n\t"
   "movq %rdi, %rsp\n\t"
   "pushq %rax\n\t"
   "pushq %rbx\n\t"
   "pushq %rcx\n\t"
   "pushq %rdx\n\t"
   "pushq %rbp\n\t"
   "pushq %rsi\n\t"
   "pushq %rdi\n\t"
   "pushq %r8\n\t"
   "pushq %r9\n\t"
   "pushq %r10\n\t"
   "pushq %r11\n\t"
   "pushq %r12\n\t"
   "pushq %r13\n\t"
   "pushq %r14\n\t"
   "pushq %r15\n\t"
   "movq  %rsi, %rsp\n\t"
   "popq  %r15\n\t"
   "popq  %r14\n\t"
   "popq  %r13\n\t"
   "popq  %r12\n\t"
   "popq  %r11\n\t"
   "popq  %r10\n\t"
   "popq  %r9\n\t"
   "popq  %r8\n\t"
   "popq  %rdi\n\t"
   "popq  %rsi\n\t"
   "popq  %rbp\n\t"
   "popq  %rdx\n\t"
   "popq  %rcx\n\t"
   "popq  %rbx\n\t"
   "popq  %rax\n\t"
   "movq  (%rsp), %rsp\n\t"
   "ret\n\t"
   ".size coth_switch, . - coth_switch\n"
);

static void coth_prepare_frame(coth_t *ctx, uint64_t *stack, void *faddr)
{
   *(--stack)= (uint64_t) faddr;
   ctx->register_store[15]= (uint64_t) stack;
}

COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void *value)
{
  int K;
  if (index<1 || index>COTHP_MAXARGS) return ;
  switch(index)
  {
  case 1: K= 8; break;  // rdi
  case 2: K= 9; break;  // rsi
  case 3: K= 11; break;  // rdx
  case 4: K= 12; break;  // rcx
  // case 5: K= 7; break;  // r8
  // case 6: K= 6; break;  // r9
  }
  ctx->register_store[K]= (uint64_t) value;
}

COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint64_t value)
{
  coth_arg_p(ctx, index, (void*) value);
}
#endif

/****
         X86-32 CALLING CONVENTION ON LINUX
              (only the relevant part) 

  Normally, the convention called cdecl is used. Other variations exist,
  but the user has to declare them explicitly for each function by using
  function attributes such as __fastcall etc. I can always warn the user
  to not do that for entry functions. Below I discuss the cdecl scheme.

  All parameters are passed on the stack. Upon entry, the stack looks
  like this (left=high address)

   arg(N) arg(N-1) ... arg(1) [return address]

  After that, GCC makes a frame using push %ebp ; mov %esp, %ebp. Now,
  the stack looks like:

   arg(N) arg(N-1) ... arg(1) [return address] [old ebp] 

  The newer GCC versions require 16 byte alignment when the function
  is entered. I will visit this later.

  The following is the list of general purpose registers, from low
  to high memory:

  0: %ebp, %esi, %edi, %edx
  4: %ecx, %ebx, %eax, %esp

  Here, %eax, %ecx and %edx are scratch registers and the rest are 
  callee-saved. %esp is stored at the highest address of the storage
  array. This makes it possible to pop %esp as the last instruction.

  Return address is 4 bytes. if we have 7 4-byte arguments, then that
  would be a total of 28 bytes, bringing the grand total to 32 bytes,
  which is a multiple of 16. So yeah, it will be aligned to 16 bytes 
  upon entry.

  I will do something similar to what I did in x64. However, now we
  need to access memory in order to get the parameters, they aren't
  in registers. So, I will use %eax for from pointer and %ecx for the
  to pointer.

  Here, I could skip saving the scratch registers but I don't want to
  mess with it.
 ****/

#ifdef COTH_X32_LINUX
__asm__( "\t"
   ".text\n\t"
#ifndef COTH_STATIC_ASM
   ".globl coth_switch\n\t"
#endif
   ".type coth_switch, @function\n\t"
"coth_switch:\n"
   "movl 4(%esp), %eax\n\t"
   "movl 8(%esp), %ecx\n\t"
   "addl $28, %eax\n\t"
   "movl %esp, (%eax)\n\t"
   "movl %eax, %esp\n\t"
   "pushl %eax\n\t"
   "pushl %ebx\n\t"
   "pushl %ecx\n\t"
   "pushl %edx\n\t"
   "pushl %edi\n\t"
   "pushl %esi\n\t"
   "pushl %ebp\n\t"

   "movl %ecx, %esp\n\t"
   "popl %ebp\n\t"
   "popl %esi\n\t"
   "popl %edi\n\t"
   "popl %edx\n\t"
   "popl %ecx\n\t"
   "popl %ebx\n\t"
   "popl %eax\n\t"
   "popl %esp\n\t"
   "ret\n\t"
   ".size coth_switch, . - coth_switch\n"
);

static void coth_prepare_frame(coth_t *ctx, uint32_t *stack, void *faddr)
{
  // allocate space for arguments. we use only 4 of them, the extra is 
  // for padding to make the stack align16 at entry to the thread function.
  stack -= 7;     

  ctx->arg_area= stack;

  // the return address for the thread function. it's fake because the
  // thread function should never return and die at this address if
  // it tries to. this entry is still mandatory because the function
  // needs it in order to read arguments properly.
  *(--stack)= 0xdead;

  // building the frame for the coth_switch function. this is used in
  // the second half, when restoring registers and returning from 
  // coth_switch to the  thread function
  *(--stack)= (uint32_t) faddr;

  // only the register %esp has an initial value. since no arguments are
  // passed in registers and almost all registers are callee-save, we
  // don't initialize the registers at all. just random values as a result
  // of malloc() are fine.
  ctx->register_store[7]= (uint32_t) stack;
}

COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void* value)
{
  if (index>COTHP_MAXARGS || index<1) return ;
  index--;
  ctx->arg_area[index]= (uint32_t) value;
}

COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint32_t value)
{
  coth_arg_p(ctx, index, (void*) value);
}
#endif

/****
              ARM-32 CALLING CONVENTION 
              (only the relevant part) 

  The CPU has 16 general purpose registers, along with 16 floating point
  registers if hardware floating point is available. Here is the list 
  of general purpose registers:
 
  r0    Argument / scratch   1
  r1    Argument / scratch   2
  r2    Argument / scratch   3
  r3    Argument / scratch   4
  r4    Callee save
  r5    Callee save
  r6    Callee save
  r7    Callee save
  r8    Callee save
  r9    Callee save
  r10   Callee save
  r11   frame pointer, %fp
  r12   Callee save
  r13   stack pointer, %sp
  r14   link register, %lr
  r15   program counter, %pc

  The general purpose registers %r0,%r1,%r2,%r3 are used for argument 
  passing and these are the only temporary registers. The rest are 
  all callee saved. In fact, some of the remaining registers don't have 
  to be callee saved, but the AAPCS document (explaining all this stuff) 
  mentions that there are variations to the standard which can use those 
  registers for specific functionality. Therefore, I will save all registers.

  Regarding floating point registers, %s16-%s31 are callee-save. 

  When making a function call, the return address is not saved on the 
  stack by the call instruction (bl). Instead, it's stored in the link
  register. Apart from function arguments which spilled over to the
  stack, there is nothing on the stack when a function is entered.

  I will do the same thing I did before, I will save everything. Here
  is the list of registers, from low memory to high memory:

   0: s16 s17 s18 s19 s20 s21 s22 s23 
   8: s24 s25 s26 s27 s28 s29 s30 s31
  16: r0  r1  r2  r3  r4  r5  r6  r7
  24: r8  r9  r10 r11 r12 %lr %sp

  In case of soft-float, we simply don't have the floating point registers:

   0: r0  r1  r2  r3  r4  r5  r6  r7
   8: r8  r9  r10 r11 r12 %lr %sp

  The only register I don't save/restore is the program counter,
  for obvious reasons.

  Here, we have all the registers in order, except for the %lr and
  %sp registers. These are saved such that the stack pointer is at
  bottom of the stack so it can be the last thing to be popped.
 ****/

#ifdef COTH_ARM32HF
__asm__ (
    "\t"
    ".text\n\t"
#ifndef COTH_STATIC_ASM
    ".global coth_switch\n\t"
#endif
    ".type coth_switch, %function\n"
"coth_switch:\n\t"
    "add r0, $120\n\t"
    "str sp, [r0]\n\t"
    "mov sp, r0\n\t"
    "push {lr}\n\t"
    "push {r0-r12}\n\t"
    "vpush {s16-s31}\n\t"
    "mov sp, r1\n\t"
    "vpop {s16-s31}\n\t"
    "pop {r0-r12}\n\t"
    "pop {lr}\n\t"
    "ldr sp, [sp]\n\t"
    "bx lr\n\t"
    ".size coth_switch, . - coth_switch\n"
);

static void coth_prepare_frame(coth_t *ctx, uint32_t *stack, void *faddr)
{
  ctx->register_store[30]= (uint32_t) stack;
  ctx->register_store[29]= (uint32_t) faddr;
}

COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void* value)
{
  if (index>COTHP_MAXARGS || index<1) return ;
  index--;
  ctx->register_store[16 + index]= (uint32_t) value;
}

COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint32_t value)
{
  coth_arg_p(ctx, index, (void*) value);
}

#endif


#ifdef COTH_ARM32SF
__asm__ (
    "\t"
    ".text\n\t"
#ifndef COTH_STATIC_ASM
    ".global coth_switch\n\t"
#endif
    ".type coth_switch, %function\n"
"coth_switch:\n\t"
    "add r0, $56\n\t"
    "str sp, [r0]\n\t"
    "mov sp, r0\n\t"
    "push {lr}\n\t"
    "push {r0-r12}\n\t"
    "mov sp, r1\n\t"
    "pop {r0-r12}\n\t"
    "pop {lr}\n\t"
    "ldr sp, [sp]\n\t"
    "bx lr\n\t"
    ".size coth_switch, . - coth_switch\n"
);

static void coth_prepare_frame(coth_t *ctx, uint32_t *stack, void *faddr)
{
  ctx->register_store[14]= (uint32_t) stack;
  ctx->register_store[13]= (uint32_t) faddr;
}

COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void* value)
{
  if (index>COTHP_MAXARGS || index<1) return ;
  index--;
  ctx->register_store[index]= (uint32_t) value;
}

COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint32_t value)
{
  coth_arg_p(ctx, index, (void*) value);
}

#endif



#ifndef COTH_PTHREAD
coth_t *coth_main()
{
   coth_t *ctx;
   ctx= calloc(1,sizeof(*ctx));
  /* we already have a stack, so we don't need to do anything */
   return ctx;
}

coth_t *coth_new(void *faddr,size_t stack_size)
{
   coth_t *ctx;
   unsigned char *stack_high, *stack_low;
   unsigned int page_size;

   page_size= sysconf(_SC_PAGESIZE);

   ctx= calloc(1,sizeof(*ctx));

   if (stack_size<page_size) stack_size= page_size;
   if (stack_size%page_size) stack_size+= page_size-stack_size%page_size;
   stack_size+= page_size ;
   stack_low= mmap(0, stack_size,
        PROT_READ|PROT_WRITE,
        MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
        0,0);

   if (stack_low==MAP_FAILED) { printf("MMAP FAIL!\n"); free(ctx); return 0; }
   ctx->stack_low= stack_low;
   ctx->stack_size= stack_size;

   mprotect(stack_low, page_size, PROT_NONE);

   stack_high= ctx->stack_low + stack_size;

   coth_prepare_frame(ctx, (void*) stack_high, faddr);

   return ctx;
}

COTH_STATIC void coth_free(coth_t *ctx)
{
  munmap(ctx->stack_low,ctx->stack_size);
  free(ctx);
}
#endif

#ifdef COTH_PTHREAD


coth_t *coth_main()
{
   coth_t *ctx;
   ctx= calloc(1,sizeof(*ctx));
   pthread_mutex_init(&ctx->mutex, NULL);
   pthread_cond_init(&ctx->cond, NULL);
   ctx->enabled= 1;
   ctx->main= 1;
   return ctx;
}

static int coth_wait_enabled(coth_t *ctx)
{
  int R;
  pthread_mutex_lock(&ctx->mutex);
  while(!ctx->quit && !ctx->enabled)
    pthread_cond_wait(&ctx->cond, &ctx->mutex);
  if (ctx->quit) R= 1;
            else R= 0;
  pthread_mutex_unlock(&ctx->mutex);
  return R;
}

COTH_STATIC void coth_switch(coth_t *from, coth_t *dst)
{
  pthread_mutex_lock(&from->mutex);
  from->enabled= 0; 
  pthread_mutex_unlock(&from->mutex);

  pthread_mutex_lock(&dst->mutex);
  dst->enabled= 1;
  pthread_cond_signal(&dst->cond);
  pthread_mutex_unlock(&dst->mutex);

  if (coth_wait_enabled(from))
    longjmp(from->jbuf,1);
}

static void* coth_thread_func(void *_ctx)
{
  coth_t *ctx;
  ctx= _ctx;
  if (coth_wait_enabled(ctx)) return NULL;
  if (setjmp(ctx->jbuf)) return NULL;
  (*ctx->f)(ctx->args[0], ctx->args[1], ctx->args[2], ctx->args[3]);
  return NULL;
}

coth_t *coth_new(void *faddr,size_t stack_size)
{
   coth_t *ctx;
   ctx= calloc(1,sizeof(*ctx));
   ctx->f= faddr;
   pthread_mutex_init(&ctx->mutex, NULL);
   pthread_cond_init(&ctx->cond, NULL);
   pthread_create(&ctx->thread, NULL, coth_thread_func, ctx);
   return ctx;
}

COTH_STATIC void coth_free(coth_t *ctx)
{
  void *dummy;
  if (ctx->main) goto doit;
  pthread_mutex_lock(&ctx->mutex);
  ctx->enabled= 1;
  ctx->quit= 1;
  pthread_cond_signal(&ctx->cond);
  pthread_mutex_unlock(&ctx->mutex);
  pthread_join(ctx->thread, &dummy);
doit:
  pthread_mutex_destroy(&ctx->mutex);
  pthread_cond_destroy(&ctx->cond);
  free(ctx);
}

COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void* value)
{
  if (index>COTHP_MAXARGS || index<1) return ;
  index--;
  ctx->args[index]= value;
}


COTH_STATIC void coth_arg_i(coth_t *ctx, int index, COTH_PTHREAD_WORD value)
{
  coth_arg_p(ctx, index, (void*) value);
}
#endif

#ifndef COTH_NO_MSG
COTH_STATIC void coth_msg_put(coth_t *ctx, int cmd, void *data)
{
  coth_msg_t *msg;
  msg= malloc(sizeof(*msg));
  msg->next= NULL;
  msg->cmd= cmd;
  msg->data= data;
  if (ctx->msg_last) ctx->msg_last->next= msg;
                else ctx->msg_first= msg;
  ctx->msg_last= msg;
}

int  coth_msg_get(coth_t *ctx, int *Rcmd, void **Rdata)
{
  coth_msg_t *msg;
  msg= ctx->msg_first;
  if (!msg) return 1;
  ctx->msg_first= msg->next;
  if (!ctx->msg_first) ctx->msg_last= NULL;
  if (Rcmd) *Rcmd= msg->cmd;
  if (Rdata) *Rdata= msg->data;
  free(msg);
  return 0;
}
#endif // COTH_NO_MSG

#endif // COTH_CODE

/*****
  Template for new architectures

#ifdef ARCHITECTURE
COTH_STATIC void coth_switch(coth_t *from, coth_t *to)
{
  asm volatile (
  : );
}

static void coth_prepare_frame(coth_t *ctx, uint32_t *stack, void *faddr)
{
}
COTH_STATIC void coth_arg_p(coth_t *ctx, int index, void* value)
{
  if (index>COTHP_MAXARGS || index<1) return ;
  index--;
  ctx->arg_area[index]= (uint32_t) value;
}


COTH_STATIC void coth_arg_i(coth_t *ctx, int index, uint32_t value)
{
  coth_arg_p(ctx, index, (void*) value);
}
#endif



MSVC/MSVC++

Microsoft's C/C++ compilers support the naked attribute on functions. They describe this attribute as:

    The naked storage-class attribute is a Microsoft-specific extension to the C language. For functions declared with the naked storage-class attribute, the compiler generates code without prolog and epilog code. You can use this feature to write your own prolog/epilog code sequences using inline assembler code. Naked functions are particularly useful in writing virtual device drivers.

An example Interrupt Service Routine could be done like this:

__declspec(naked) int isr_test(void)
{
    __asm { iret };
}

You'll need to deal with the issues of saving and restoring registers, setting the direction flag yourself in a similar manner to the GCC example above.

***/

#ifdef COTH_SELF_TEST

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <limits.h>


coth_t *main_ctx;
coth_t *child_ctx;

void foo(int i)
{
      printf("C %d\n", i);
      coth_switch(child_ctx,main_ctx);
}

void child_func()
{
  int i;
  for(i=0;;i++)
   {
       foo(i);
   }
}

void args_func(int *a,int *b)
{
  *a= 5;
  *b= 6;

  coth_switch(child_ctx, main_ctx);
}


int main()
{
    int a,b;
    main_ctx= coth_main();
    child_ctx= coth_new(child_func, 40960);

    int i;
    for(i=0;i<20;i++)
    {
       printf("M %d\n", i);
       coth_switch(main_ctx, child_ctx);       
    }
    coth_free(child_ctx);

    printf("addresses: a=%p, b=%p\n", &a, &b);
    a=1; b=2;

    printf("entering child thread\n");
    child_ctx= coth_new(args_func, 8192);
    coth_arg_p(child_ctx, 1, &a);
    coth_arg_p(child_ctx, 2, &b);
    coth_switch(main_ctx, child_ctx);
    printf("exited child thread a= %d b= %d\n",a,b);

    coth_free(child_ctx);
    coth_free(main_ctx);

    return 0;
}

#endif  // COTH_SELF_TEST


