diff --git a/include/gfx_rules.h b/include/gfx_rules.h index 534c2cc9..54fe3a1a 100644 --- a/include/gfx_rules.h +++ b/include/gfx_rules.h @@ -26,14 +26,14 @@ #define GFX_DISPLAY_RULE_WARNINGS FALSE #endif -#if !GFX_USE_OS_CHIBIOS && !GFX_USE_OS_WIN32 && !GFX_USE_OS_LINUX && !GFX_USE_OS_OSX +#if !GFX_USE_OS_CHIBIOS && !GFX_USE_OS_WIN32 && !GFX_USE_OS_LINUX && !GFX_USE_OS_OSX && !GFX_USE_OS_RAW32 #if GFX_DISPLAY_RULE_WARNINGS #warning "GOS: No Operating System has been defined. ChibiOS (GFX_USE_OS_CHIBIOS) has been turned on for you." #endif #undef GFX_USE_OS_CHIBIOS #define GFX_USE_OS_CHIBIOS TRUE #endif -#if GFX_USE_OS_CHIBIOS + GFX_USE_OS_WIN32 + GFX_USE_OS_LINUX + GFX_USE_OS_OSX != 1 * TRUE +#if GFX_USE_OS_CHIBIOS + GFX_USE_OS_WIN32 + GFX_USE_OS_LINUX + GFX_USE_OS_OSX + GFX_USE_OS_RAW32 != 1 * TRUE #error "GOS: More than one operation system has been defined as TRUE." #endif diff --git a/include/gos/gos.h b/include/gos/gos.h index 8e7840e3..37335fbb 100644 --- a/include/gos/gos.h +++ b/include/gos/gos.h @@ -435,6 +435,8 @@ #include "gos/linux.h" #elif GFX_USE_OS_OSX #include "gos/osx.h" +#elif GFX_USE_OS_RAW32 + #include "gos/raw32.h" #else #error "Your operating system is not supported yet" #endif diff --git a/include/gos/options.h b/include/gos/options.h index f1762134..4edb35d8 100644 --- a/include/gos/options.h +++ b/include/gos/options.h @@ -48,6 +48,13 @@ #ifndef GFX_USE_OS_OSX #define GFX_USE_OS_OSX FALSE #endif + /** + * @brief Use a Raw 32 bit CPU based system + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_RAW32 + #define GFX_USE_OS_RAW32 FALSE + #endif /** * @} * diff --git a/include/gos/raw32.h b/include/gos/raw32.h new file mode 100644 index 00000000..165bc606 --- /dev/null +++ b/include/gos/raw32.h @@ -0,0 +1,124 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file include/gos/raw32.h + * @brief GOS - Operating System Support header file for any 32 bit processor in bare-metal mode + */ + +/** + * The raw32 GOS implementation supports any 32 bit processor with or without an + * underlying operating system. It uses cooperative multi-tasking. Be careful + * when writing device drivers not to disturb the assumptions this creates by performing + * call-backs to uGFX code unless you define the INTERRUPTS_OFF() and INTERRUPTS_ON() macros. + * It still requires some C runtime library support... + * enough startup to initialise the stack, interrupts, static data etc and call main(). + * setjmp() and longjmp() - for threading + * memcpy() - for heap and threading + * malloc(), realloc and free() - if GOS_RAW_HEAP_SIZE == 0 + * + * You must also define the following routines in your own code so that timing functions will work... + * systemticks_t gfxSystemTicks(void); + * systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + */ +#ifndef _GOS_RAW32_H +#define _GOS_RAW32_H + +#if GFX_USE_OS_RAW32 + +/*===========================================================================*/ +/* Special Macros just for a Raw implementation */ +/*===========================================================================*/ + +/** + * @brief Set the maximum size of the heap. + * @note If set to 0 then the C runtime library malloc() and free() are used. + */ +#ifndef GOS_RAW_HEAP_SIZE + #define GOS_RAW_HEAP_SIZE 0 +#endif + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +typedef unsigned char bool_t; +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; + +typedef uint32_t size_t; +typedef uint32_t delaytime_t; +typedef uint32_t systemticks_t; +typedef short semcount_t; +typedef int threadreturn_t; +typedef int threadpriority_t; + +#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) +#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; + +#define TIME_IMMEDIATE 0 +#define TIME_INFINITE ((delaytime_t)-1) +#define MAX_SEMAPHORE_COUNT ((semcount_t)(((unsigned long)((semcount_t)(-1))) >> 1)) +#define LOW_PRIORITY 0 +#define NORMAL_PRIORITY 1 +#define HIGH_PRIORITY 2 + +typedef struct { + semcount_t cnt; + semcount_t limit; + } gfxSem; + +typedef uint32_t gfxMutex; +typedef void * gfxThreadHandle; + +#define gfxThreadClose(thread) +#define gfxMutexDestroy(pmutex) +#define gfxSemDestroy(psem) +#define gfxSemCounter(psem) ((psem)->cnt) +#define gfxSemCounterI(psem) ((psem)->cnt) + +#ifdef __cplusplus +extern "C" { +#endif + + #if GOS_RAW_HEAP_SIZE != 0 + void gfxAddHeapBlock(void *ptr, size_t sz); + #endif + + void gfxHalt(const char *msg); + void gfxExit(void); + void *gfxAlloc(size_t sz); + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); + void gfxFree(void *ptr); + void gfxYield(void); + void gfxSleepMilliseconds(delaytime_t ms); + void gfxSleepMicroseconds(delaytime_t ms); + systemticks_t gfxSystemTicks(void); + systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + void gfxSystemLock(void); + void gfxSystemUnlock(void); + void gfxMutexInit(gfxMutex *pmutex); + void gfxMutexEnter(gfxMutex *pmutex); + void gfxMutexExit(gfxMutex *pmutex); + void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); + bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); + void gfxSemSignal(gfxSem *psem); + void gfxSemSignalI(gfxSem *psem); + gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); + threadreturn_t gfxThreadWait(gfxThreadHandle thread); + gfxThreadHandle gfxThreadMe(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_OS_RAW32 */ +#endif /* _GOS_RAW32_H */ diff --git a/src/gos/gos.mk b/src/gos/gos.mk index 14193883..9db29fe2 100644 --- a/src/gos/gos.mk +++ b/src/gos/gos.mk @@ -2,4 +2,5 @@ GFXSRC += $(GFXLIB)/src/gos/chibios.c \ $(GFXLIB)/src/gos/win32.c \ $(GFXLIB)/src/gos/linux.c \ $(GFXLIB)/src/gos/osx.c \ + $(GFXLIB)/src/gos/raw32.c \ diff --git a/src/gos/raw32.c b/src/gos/raw32.c new file mode 100644 index 00000000..7dd15c27 --- /dev/null +++ b/src/gos/raw32.c @@ -0,0 +1,742 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gos/raw32.c + * @brief GOS Raw (bare metal) support. + */ +#include "gfx.h" + +#if GFX_USE_OS_RAW32 + +#include // Prototype for memcpy() + + +#if GOS_RAW_HEAP_SIZE != 0 + static void _gosHeapInit(void); +#else + #define _gosHeapInit() +#endif +static void _gosThreadsInit(void); + +/********************************************************* + * Initialise + *********************************************************/ + +void _gosInit(void) { + // Set up the heap allocator + _gosHeapInit(); + + // Start the scheduler + _gosThreadsInit(); +} + +/********************************************************* + * For WIn32 emulation - automatically add the tick functions + * the user would normally have to provide for bare metal. + *********************************************************/ + +#if defined(WIN32) + #undef Red + #undef Green + #undef Blue + #define WIN32_LEAN_AND_MEAN + #include + #include + systemticks_t gfxSystemTicks(void) { return GetTickCount(); } + systemticks_t gfxMillisecondsToTicks(delaytime_t ms) { return ms; } +#endif + +/********************************************************* + * Exit everything functions + *********************************************************/ + +void gfxHalt(const char *msg) { + #if defined(WIN32) + fprintf(stderr, "%s\n", msg); + ExitProcess(1); + #else + volatile uint32_t dummy; + (void) msg; + + while(1) + dummy++; + #endif +} + +void gfxExit(void) { + #if defined(WIN32) + ExitProcess(0); + #else + volatile uint32_t dummy; + + while(1) + dummy++; + #endif +} + +/********************************************************* + * Head allocation functions + *********************************************************/ + +#if GOS_RAW_HEAP_SIZE == 0 + #include // Prototype for malloc(), realloc() and free() + + void *gfxAlloc(size_t sz) { + return malloc(sz); + } + + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { + (void) oldsz; + return realloc(ptr, newsz); + } + + void gfxFree(void *ptr) { + free(ptr); + } + +#else + + // Slot structure - user memory follows + typedef struct memslot { + struct memslot *next; // The next memslot + size_t sz; // Includes the size of this memslot. + } memslot; + + // Free Slot - immediately follows the memslot structure + typedef struct freeslot { + memslot *nextfree; // The next free slot + } + + #define GetSlotSize(sz) ((((sz) + (sizeof(freeslot) - 1)) & ~(sizeof(freeslot) - 1)) + sizeof(memslot)) + #define NextFree(pslot) ((freeslot *)Slot2Ptr(pslot))->nextfree + #define Ptr2Slot(p) ((memslot *)(p) - 1) + #define Slot2Ptr(pslot) ((pslot)+1) + + static memslot * firstSlot, *lastSlot, *freeSlots; + static char heap[GOS_RAW_HEAP_SIZE]; + + static void _gosHeapInit(void) { + lastSlot = 0; + gfxAddHeapBlock(heap, GOS_RAW_HEAP_SIZE); + } + + void gfxAddHeapBlock(void *ptr, size_t sz) { + if (sz < sizeof(memslot)+sizeof(freeslot)) + return; + + if (lastSlot) + lastSlot->next = (memslot *)ptr; + else + firstSlot = lastSlot = freeSlot = (memslot *)ptr; + + lastSlot->next = 0; + lastSlot->sz = sz; + NextFree(lastSlot) = 0; + } + void *gfxAlloc(size_t sz) { + register memslot *prev, *p, *new; + + if (!sz) return 0; + sz = GetSlotSize(sz); + for (prev = 0, p = freeSlots; p != 0; prev = p, p = NextFree(p)) { + // Loop till we have a block big enough + if (p->sz < sz) + continue; + // Can we save some memory by splitting this block? + if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { + new = (memslot *)((char *)p + sz); + new->next = p->next; + p->next = new; + new->sz = p->sz - sz; + p->sz = sz; + if (lastSlot == p) + lastSlot = new; + NextFree(new) = freeSlots; + freeSlots = new; + } + // Remove it from the free list + if (prev) + NextFree(prev) = NextFree(p); + else + freeSlots = NextFree(p); + // Return the result found + return Slot2Ptr(p); + } + // No slots large enough + return 0; + } + + void *gfxRealloc(void *ptr, size_t oldsz, size_t sz) { + register memslot *prev, *p, *new; + (void) oldsz; + + if (!ptr) + return gfxAlloc(sz); + if (!sz) { + gfxFree(ptr); + return 0; + } + + p = Ptr2Slot(ptr); + sz = GetSlotSize(sz); + + // If the next slot is free (and contiguous) merge it into this one + if ((char *)p + p->sz == (char *)p->next) { + for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { + if (new == p->next) { + p->next = new->next; + p->sz += new->sz; + if (prev) + NextFree(prev) = NextFree(new); + else + freeSlots = NextFree(new); + if (lastSlot == new) + lastSlot = p; + break; + } + } + } + + // If this block is large enough we are nearly done + if (sz < p->sz) { + // Can we save some memory by splitting this block? + if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { + new = (memslot *)((char *)p + sz); + new->next = p->next; + p->next = new; + new->sz = p->sz - sz; + p->sz = sz; + if (lastSlot == p) + lastSlot = new; + NextFree(new) = freeSlots; + freeSlots = new; + } + return Slot2Ptr(p); + } + + // We need to do this the hard way + if ((new = gfxAlloc(sz))) + return 0; + memcpy(new, ptr, p->sz - sizeof(memslot)); + free(ptr); + return new; + } + + void gfxFree(void *ptr) { + register memslot *prev, *p, *new; + + if (!ptr) + return; + + p = Ptr2Slot(ptr); + + // If the next slot is free (and contiguous) merge it into this one + if ((char *)p + p->sz == (char *)p->next) { + for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { + if (new == p->next) { + p->next = new->next; + p->sz += new->sz; + if (prev) + NextFree(prev) = NextFree(new); + else + freeSlots = NextFree(new); + if (lastSlot == new) + lastSlot = p; + break; + } + } + } + + // Add it into the free chain + NextFree(p) = freeSlots; + freeSlots = p; + } +#endif + +/********************************************************* + * Semaphores and critical region functions + *********************************************************/ + +#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) + #define INTERRUPTS_OFF() + #define INTERRUPTS_ON() +#endif + +void gfxSystemLock(void) { + INTERRUPTS_OFF(); +} +void gfxSystemUnlock(void) { + INTERRUPTS_ON(); +} + +void gfxMutexInit(gfxMutex *pmutex) { + pmutex[0] = 0; +} + +void gfxMutexEnter(gfxMutex *pmutex) { + INTERRUPTS_OFF(); + while (pmutex[0]) { + INTERRUPTS_ON(); + gfxYield(); + INTERRUPTS_OFF(); + } + pmutex[0] = 1; + INTERRUPTS_ON(); +} + +void gfxMutexExit(gfxMutex *pmutex) { + pmutex[0] = 0; +} + +void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { + psem->cnt = val; + psem->limit = limit; +} + +bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { + systemticks_t starttm, delay; + + // Convert our delay to ticks + switch (ms) { + case TIME_IMMEDIATE: + delay = TIME_IMMEDIATE; + break; + case TIME_INFINITE: + delay = TIME_INFINITE; + break; + default: + delay = gfxMillisecondsToTicks(ms); + if (!delay) delay = 1; + starttm = gfxSystemTicks(); + } + + INTERRUPTS_OFF(); + while (psem->cnt <= 0) { + INTERRUPTS_ON(); + // Check if we have exceeded the defined delay + switch (delay) { + case TIME_IMMEDIATE: + return FALSE; + case TIME_INFINITE: + break; + default: + if (gfxSystemTicks() - starttm >= delay) + return FALSE; + break; + } + gfxYield(); + INTERRUPTS_OFF(); + } + psem->cnt--; + INTERRUPTS_ON(); + return TRUE; +} + +void gfxSemSignal(gfxSem *psem) { + INTERRUPTS_OFF(); + gfxSemSignalI(psem); + INTERRUPTS_ON(); +} + +void gfxSemSignalI(gfxSem *psem) { + if (psem->cnt < psem->limit) + psem->cnt++; +} + +/********************************************************* + * Sleep functions + *********************************************************/ + +void gfxSleepMilliseconds(delaytime_t ms) { + systemticks_t starttm, delay; + + // Safety first + switch (ms) { + case TIME_IMMEDIATE: + return; + case TIME_INFINITE: + while(1) + gfxYield(); + return; + } + + // Convert our delay to ticks + delay = gfxMillisecondsToTicks(ms); + starttm = gfxSystemTicks(); + + do { + gfxYield(); + } while (gfxSystemTicks() - starttm < delay); +} + +void gfxSleepMicroseconds(delaytime_t ms) { + systemticks_t starttm, delay; + + // Safety first + switch (ms) { + case TIME_IMMEDIATE: + return; + case TIME_INFINITE: + while(1) + gfxYield(); + return; + } + + // Convert our delay to ticks + delay = gfxMillisecondsToTicks(ms/1000); + starttm = gfxSystemTicks(); + + do { + gfxYield(); + } while (gfxSystemTicks() - starttm < delay); +} + +/********************************************************* + * Threading functions + *********************************************************/ + +/** + * There are some compilers we know how they store the jmpbuf. For those + * we can use the constant MASK1 and MASK2. For others we have to "auto-detect". + * Auto-detection is hairy and there is no guarantee it will work on all architectures. + * For those it doesn't - read the compiler manuals and the library source code to + * work out the correct MASK values. + * Using the debugger to work out the values for your compiler and putting them here + * also saves a nice block of RAM. + */ +#if 0 + #define STACK_DIR_UP FALSE // usually false as most stacks grow down + #define MASK1 somevalue1 + #define MASK2 somevalue2 // optional + #define STACK_BASE someindex +#else + #define AUTO_DETECT_MASK TRUE + #define STACK_DIR_UP stackdirup + #define MASK1 jmpmask1 + #define MASK2 jmpmask2 + #define STACK_BASE stackbase +#endif + +#include /* jmp_buf, setjmp(), longjmp() */ + +/** + * Some compilers define a _setjmp() and a setjmp(). + * The difference between them is that setjmp() saves the signal masks. + * That is of no use to us so prefer to use the _setjmp() methods. + * If they don't exist compile them to be the standard setjmp() function. + * Similarly for longjmp(). + */ +#ifndef setjmp + #ifndef _setjmp + #define _setjmp setjmp + #endif +#endif +#ifndef longjmp + #ifndef _longjmp + #define _longjmp longjmp + #endif +#endif + +typedef struct thread { + struct thread * next; // Next thread + int flags; // Flags + #define FLG_THD_ALLOC 0x0001 + #define FLG_THD_MAIN 0x0002 + #define FLG_THD_DEAD 0x0004 + #define FLG_THD_WAIT 0x0008 + size_t size; // Size of the thread stack (including this structure) + threadreturn_t (*fn)(void *param); // Thread function + void * param; // Parameter for the thread function + jmp_buf cxt; // The current thread context. + // size - sizeof(thread) bytes of stack follow here + } thread; + +typedef struct threadQ { + thread *head; + thread *tail; + } threadQ; + +static threadQ readyQ; // The list of ready threads +static threadQ deadQ; // Where we put threads waiting to be deallocated +static thread * current; // The current running thread +static thread mainthread; // The main thread context + +static void Qinit(threadQ * q) { + q->head = q->tail = 0; +} + +static void Qadd(threadQ * q, thread *t) { + t->next = 0; + if (q->tail) { + q->tail->next = t; + q->tail = t; + } else + q->head = q->tail = t; +} + +static thread *Qpop(threadQ * q) { + struct thread * t; + + if (!q->head) + return 0; + t = q->head; + q->head = t->next; + return t; +} + +#if AUTO_DETECT_MASK + static char * lowptr; // A pointer to somewhere low in the stack frame + static char * highptr; // A pointer to somewhere high in the stack frame + + // The saved information on a stack frame + typedef struct saveloc { + char * localptr; + jmp_buf cxt; + } saveloc; + + static saveloc infn; // The saved stack frame info inside a function + static saveloc outfn; // The saved stack frame info outside a function + static saveloc *saveptr; // Which saved stack frame variable we are currently using + + // Where we save all the information + static bool_t stackdirup; // TRUE if the stack grow up instead of down + static uint32_t jmpmask1; // The 1st mask of jmp_buf elements that need relocation + static uint32_t jmpmask2; // The 2nd mask of jmp_buf elements that need relocation + static size_t stackbase; // The base of the stack frame relative to the local variables + + /* These functions are not static to prevent the compiler removing them as functions */ + + void get_highptr(void) { + char c; + highptr = &c; + } + + void test_stack(void) { + char c; + saveptr->localptr = (char *)&c; + _setjmp(saveptr->cxt); + get_highptr(); + } + + void get_stack_state(void) { + char c; + lowptr = &c; + test_stack(); + } + + void get_stack_state_in_fn(void) { + get_stack_state(); + } +#endif + +static void _gosThreadsInit(void) { + Qinit(&readyQ); + current = &mainthread; + current->next = 0; + current->size = sizeof(thread); + current->flags = FLG_THD_MAIN; + current->fn = 0; + current->param = 0; + + #if AUTO_DETECT_MASK + // Get details of the stack frame from within a function + saveptr = &infn; + get_stack_state_in_fn(); + + // Get details of the stack frame outside the function + saveptr = &outfn; + get_stack_state(); + + /* stack direction */ + stackdirup = highptr > lowptr; // Can we also test infn.localptr > outfn.localptr? + + /* study the jump buffer */ + { + uint32_t i; + char ** pout; + char ** pin; + size_t diff = outfn.localptr - infn.localptr; + char * min_frame = outfn.localptr; + + /* following line views jump buffer as array of long int */ + pout = (char **)outfn.cxt; + pin = (char **)infn.cxt; + + jmpmask1 = jmpmask2 = 0; + if (diff) { + for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { + if ((size_t)(*pout - *pin) == diff) { + /* record frame dependency */ + if (i < 32) + jmpmask1 |= 1 << i; + else + jmpmask2 |= 1 << (i-32); + + if (stackdirup) { + if (min_frame > *pout) { + min_frame = *pout; + } + } else { + if (min_frame < *pout) { + min_frame = *pout; + } + } + } + } + } + + if (stackdirup) { + stackbase = outfn.localptr - min_frame; + } else { + stackbase = min_frame - outfn.localptr; + } + } + #endif +} + +gfxThreadHandle gfxThreadMe(void) { + return (gfxThreadHandle)current; +} + +void gfxYield(void) { + if (!_setjmp(current->cxt)) { + // Add us back to the Queue + Qadd(&readyQ, current); + + // Check if there are dead processes to deallocate + while ((current = Qpop(&deadQ))) + gfxFree(current); + + // Run the next process + current = Qpop(&readyQ); + _longjmp(current->cxt, 1); + } +} + +// This routine is not currently public - but it could be. +void gfxThreadExit(threadreturn_t ret) { + // Save the results + current->param = (void *)ret; + current->flags |= FLG_THD_DEAD; + + // Add us to the dead list if we need deallocation as we can't free ourselves. + // If someone is waiting on the thread they will do the cleanup. + if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) + Qadd(&deadQ, current); + + // Switch to the next task + current = Qpop(&readyQ); + if (!current) + gfxExit(); // Oops - this should never happen! + _longjmp(current->cxt, 1); +} + +gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { + thread * t; + (void) prio; + + // Ensure we have a minimum stack size + if (stacksz < sizeof(thread)+64) { + stacksz = sizeof(thread)+64; + stackarea = 0; + } + + if (stackarea) { + t = (thread *)stackarea; + t->flags = 0; + } else { + t = (thread *)gfxAlloc(stacksz); + if (!t) + return 0; + t->flags = FLG_THD_ALLOC; + } + t->size = stacksz; + t->fn = fn; + t->param = param; + if (_setjmp(t->cxt)) { + // This is the new thread - call the function! + gfxThreadExit(current->fn(current->param)); + + // We never get here + return 0; + } + + // Move the stack frame and relocate the context data + { + char ** s; + char * nf; + int diff; + uint32_t i; + + // Copy the stack frame + #if AUTO_DETECT_MASK + if (STACK_DIR_UP) { // Stack grows up + nf = (char *)(t) + sizeof(thread) + stackbase; + memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); + } else { // Stack grows down + nf = (char *)(t) + stacksz - (stackbase + sizeof(char *)); + memcpy(nf, &t, stackbase+sizeof(char *)); + } + #elif STACK_DIR_UP + // Stack grows up + nf = (char *)(t) + sizeof(thread) + stackbase; + memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); + #else + // Stack grows down + nf = (char *)(t) + size - (stackbase + sizeof(char *)); + memcpy(nf, &t, stackbase+sizeof(char *)); + #endif + + // Relocate the context data + s = (char **)(t->cxt); + diff = nf - (char *)&t; + + // Relocate the elements we know need to be relocated + for (i = 1; i && i < MASK1; i <<= 1, s++) { + if ((MASK1 & i)) + *s += diff; + } + #ifdef MASK2 + for (i = 1; i && i < MASK2; i <<= 1, s++) { + if ((MASK1 & i)) + *s += diff; + } + #endif + } + + // Add this thread to the ready queue + Qadd(&readyQ, t); + return t; +} + +threadreturn_t gfxThreadWait(gfxThreadHandle th) { + thread * t; + + t = th; + if (t == current) + return -1; + + // Mark that we are waiting + t->flags |= FLG_THD_WAIT; + + // Wait for the thread to die + while(!(t->flags & FLG_THD_DEAD)) + gfxYield(); + + // Unmark + t->flags &= ~FLG_THD_WAIT; + + // Clean up resources if needed + if (t->flags & FLG_THD_ALLOC) + gfxFree(t); + + // Return the status left by the dead process + return (threadreturn_t)t->param; +} + +#endif /* GFX_USE_OS_RAW32 */