#ifndef RTOS_H
#define RTOS_H

#define ERTOS_VER 0x20010920

/*
 * eRTOS - embedded development system
 *
 * Copyright (c) 1988, 1999 Erick Engelke
 * All Rights Reserved
 */

/**************************************************************************/
/* INCLUDES                                                               */
/**************************************************************************/
#include <time.h>
#include <dos.h>


#if defined(__TURBOC__) || defined(__BORLANDC__)
  #include <cpujmp.h>
#elif defined(__DJGPP__)
  #include <setjmp.h>
  #include <dpmi.h>
#endif

/**************************************************************************/
/* FLAGS                                                                  */
/**************************************************************************/

#define USE_WINDOW


#ifdef USE_WINDOW
#include <conio.h>
#endif

/**************************************************************************/
/* DEFINES                                                                */
/**************************************************************************/

#if defined(__DJGPP__)
    /* DJGPP does not understand type interrupt */
#define interrupt
#endif

#ifndef __WATT_TCP_H  /* Watt-32 */
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long  DWORD;
#endif

#define TSTAT_DEAD      0       /* thread does not exist */
#define TSTAT_RUNNING   1       /* current thread */
#define TSTAT_WILL_RUN  2       /* on the run queue */
#define TSTAT_WAITING   3       /* waiting, not on run queue */
#define TSTAT_CRITICAL  4       /* waiting on critical section */

#define THS_THREAD  0x0001  /* is a thread */
#define THS_SLEEP   0x0002  /* see th_waketime */
#define THS_SYNC    0x0004  /* see th_syncptr */
#define THS_MESG    0x0008  /* waiting for a message */

/* thread sorting for kaddthread */
#define TS_PRIORITY 0x0001  /* order by priority */
#define TS_SOON     0x0002  /* order by next timer */
#define TS_NOSORT   0x0000

#define TH_SIG      0x7f53

#define MEM_STARTSIG 0x532f29ec
#define MEM_ENDSIG   0x2958dacb

/**************************************************************************/
/* STRUCTURES                                                             */
/**************************************************************************/


/*--------------------------------------------------------------------*/
/* messaging                                                          */
/*--------------------------------------------------------------------*/
typedef struct _msg_x {
    struct _msg_x *m_next;
    int     m_value;
    DWORD   m_data;
} msg_x;

/*--------------------------------------------------------------------*/
/* allocs                                                             */
/*--------------------------------------------------------------------*/
typedef struct _mem_x {
    DWORD   mem_startsig;
    WORD    mem_size;
    WORD    mem_mbz;
    void   *mem_thread;     /* create thread */
    BYTE    mem_data[1];
} mem_x;

/*--------------------------------------------------------------------*/
/* threads                                                            */
/*--------------------------------------------------------------------*/

typedef struct _thread_x {
    WORD    th_sig;
    struct _thread_x *th_prev;
    struct _thread_x *th_next;
    struct _thread_x **th_head;
    struct _thread_x *th_parent;    /* creator thread */
    WORD     th_inkernel;

    char    *th_name;
    WORD     th_status;
    BYTE     th_priority;    /* lower is better 0-7f 80 means blocked */
    BYTE     th_main;        /* 1 for main */
    DWORD    th_waketime;    /* 0, or some real time */
    WORD     th_waketime2;   /* second half of 48 bit timer */
    BYTE    *th_syncptr;     /* synchronization byte */
#if defined(__TURBOC__) || defined(__BORLANDC__)
    struct __cpu_jmp_buf  th_ptr;
#elif defined(__DJGPP__)
    struct __jmp_buf  th_ptr;
#endif
    DWORD    th_ptrsig;
    BYTE    *th_stack;
#if defined(__TURBOC__)||defined(__BORLANDC__)
    WORD     th_stacklen;
#elif defined(__DJGPP__)
    DWORD    th_stacklen;
#endif
    void   (*th_fn)(DWORD);        /* function to call on first execution */
    DWORD    th_fnarg;

    WORD     th_messagewait;    /* particular message for which we are waiting */
    msg_x   *th_messagelist;
    msg_x   *th_messagetail;    /* points to last added message */

    struct _crit_x  *th_crit;
    struct _bq_str  *th_bq;

    /* kwindow attributes */
    int      th_left, th_right, th_top, th_bottom, th_x, th_y;
    struct _crit_x  *th_critical;       /* crticial section */

    /* messaging on exit if th_value != 0 */
    struct _thread_x *th_end_thread;
    int     th_end_value;
    DWORD   th_end_data;
} thread_x;

/*--------------------------------------------------------------------*/
/* critical sections                                                  */
/*--------------------------------------------------------------------*/
typedef struct _crit_x {
    thread_x *cs_blocked;
    thread_x *cs_active;
    WORD cs_depth;
} crit_x;

/*--------------------------------------------------------------------*/
/* byte queues                                                        */
/*--------------------------------------------------------------------*/

typedef struct _bq_str {
    WORD    bq_size;
    WORD    bq_head;    /* new bytes go here */
    WORD    bq_tail;    /* next to read is here */
    BYTE   *bq_queue;
    thread_x *bq_sendwait;   /* thread waiting to send a byte */
    thread_x *bq_recvwait;   /* thread waiting to receive a byte */
    thread_x *bq_msg_waiting;   /* tell if data waiting */
    thread_x *bq_msg_avail;     /* tell if room available */
} bq_str;


/*--------------------------------------------------------------------*/

/**************************************************************************/
/* MESSAGES                                                               */
/**************************************************************************/
#define EMSG_BQ_WAITING  0x0001  /* byte waiting in byte queue */
#define EMSG_BQ_AVAIL    0x0002  /* byte space available for writing */
#define EMSG_THREAD_DEAD 0x0003
#define EMSG_TIMER       0x0004  /* wake up */

#define EMSG_RESERVED    0x01ff  /* last of the reserved messages */
#define EMSG_USER        0x0200  /* start of user defined messages */

/**************************************************************************/
/* VARIABLES                                                              */
/**************************************************************************/
extern volatile int kinisr;
extern int kcheckstacksig;
extern int _fastdos;
extern thread_x *krun;  /* running list */
extern thread_x *kwait; /* waiting list */
extern thread_x *kdye; /* dying list */
extern thread_x *kmainthread; /* main thread */
extern int kusewindows;
extern DWORD ktime;    /* system timer */
extern WORD ktime2;
extern DWORD kupticks;  /* clock ticks we've been up */
extern thread_x *kcurthread;
extern WORD kpreemptive;

extern volatile WORD kblocked;
extern int kdebug;

#if !defined(__DJGPP__)
#define k_temp_block()   _asm { pushf ; cli }
#define k_temp_unblock() _asm { popf }
#endif

extern void (*k_user_int8)(void);
extern int kctrlbreak;      /* set to zero if we want to disable ctrl break */
/**************************************************************************/
/* CODE                                                                   */
/**************************************************************************/

void dos_enter( void );
void dos_exit( void );

void kblock(void);

void kunblock(void);
void rt_cpu_block( WORD *p );
void rt_cpu_unblock( WORD *p );

void rt_timerfreq( WORD persec );
void rt_restoretimer( void );

// mimick settime()
void rt_settime( struct time *timep);

// mimick stime
void rt_stime( time_t *t );

int rt_thread_status( thread_x *t );
    /* returns TSTAT_DEAD, TSTAT_RUNNING (curthread), TSTAT_WILL_RUN, TSTAT_WAITING */


DWORD kcorefree( void );
void *kcalloc( WORD n, WORD size );
char *kstrdup( const char *s );
void kfree( void *p );
void *krealloc( void *p, WORD size );

void kmessage_on_exit( void *thread, int value, DWORD data );
int ksendmessage( void *thread , int value, DWORD data );

/* non-blocking version */
int kgetmessage( int *value, DWORD *data );

int kreadmessage( int *value, DWORD *data );
void kwritemessage( void *thread , int value, DWORD data );
void kreadspecialmessage( int value );


DWORD knumthreads( void );

/* add this to new list */
void kaddthread( thread_x **head, thread_x *t,  int howsort );
void ksuspendhow( thread_x *t, thread_x **newlist );
void ksuspend( thread_x *t );
void kresume( thread_x *t);

void kdestroythread( thread_x *t );
void kfinaldeath( thread_x *t );

char *kthreadname( void );

/*
 * sync_test - attempt to set a byte to 1
 */
BYTE sync_test( void *p );

void rt_nextthread(void);

void rt_halt( char *msg );  /* message is optional */
void xrt_yield( char *file, int line );
#define rt_yield() xrt_yield( __FILE__, __LINE__ )

void interrupt kintswitch();

void rt_exit(void);

void rt_init(DWORD msgcount );

void rt_setpriority( thread_x *t, BYTE priority );

void *rt_newthread( void (*ptr)(), DWORD arg, WORD stklength,
        BYTE priority, char *name );
int rt_stackused( thread_x * t );
void xrt_sleep( DWORD ms, char *s, int linnum );
#define rt_sleep( ms )  xrt_sleep( ms, __FILE__, __LINE__  )

void kwindow( int left, int top, int right, int bottom );
crit_x *cs_alloc( void );
void cs_new( crit_x **cs );
void cs_free( crit_x *x );
void cs_enter( crit_x *cs );
void cs_exit( crit_x *cs );



/* bqueue.c */
bq_str *bq_alloc( int queuesize );
void bq_free( bq_str *bq );
void bq_writebyte( bq_str *bq, BYTE b );
int bq_readbyte( bq_str *bq, BYTE *b );
void bq_sendbyte( bq_str *bq, BYTE b );
int bq_getbyte( bq_str *bq, BYTE *b );
void bq_msg( bq_str *bq, int msgtype );

WORD bq_readcount( bq_str *bq );

#if defined(__DJGPP__)
  typedef struct irq_handler_info {
          _go32_dpmi_seginfo old_handler;
          _go32_dpmi_seginfo new_handler;
        } irq_handler_info;

  irq_handler_info *rt_enableirq (int irq, void (*isr)());
  void              rt_disableirq (int irq, irq_handler_info *info);

#else
  void interrupt (far *rt_enableirq (int irq, void interrupt (*isr)() ))();
  void                 rt_disableirq (int irq, void interrupt (*oldisr)() );
#endif

void rt_eoi( int irq );

#endif
