
//#define OLD
#define NEW
#define NEWEST
/*
 * bqueue.c - BYTE queue for serial/parallel interfacing
 * Copyright (c) 1990, 1999 Erick Engelke
 */
#include <stdio.h>
#include "rtos.h"

void bq_msg( bq_str *bq, int type )
{
    if ( type == EMSG_BQ_WAITING ) bq->bq_msg_waiting = kcurthread;
    if ( type == EMSG_BQ_AVAIL   ) bq->bq_msg_avail = kcurthread;

    /* start a siphon */
    if ( bq->bq_msg_waiting && (bq->bq_head != bq->bq_tail))
        ksendmessage( bq->bq_msg_waiting, EMSG_BQ_WAITING, 0 );
    else
        ksendmessage( bq->bq_msg_avail, EMSG_BQ_AVAIL, 0 );
}

bq_str *bq_alloc( int queuesize )
{
    bq_str *b;
    BYTE *p;


    p = kcalloc( queuesize, 1 );
    if ( p == NULL ) return( NULL );

    b = kcalloc( sizeof( bq_str ), 1 );

    if ( b == NULL ) {
        kfree( p );
        return( NULL );
    }
    b->bq_size = queuesize;
    b->bq_queue = p;
    b->bq_head = 0;
    b->bq_tail = 0;
    b->bq_sendwait = NULL;
    b->bq_recvwait = NULL;
    b->bq_msg_waiting = NULL;
    b->bq_msg_avail = NULL;
    return( b );
}

void bq_free( bq_str *bq )
{
    kfree( bq->bq_queue );
    kfree( bq );
}

/*
 * bq_semdbyte - nonblock write of BYTE, does not detect buffer overflow
 */
void bq_sendbyte( bq_str *bq, BYTE b )
{
    WORD i;
    WORD flags, newhead;
    thread_x *t;

    rt_cpu_block( &flags );
    i = bq->bq_head;
    bq->bq_queue[ i ] = b;
    newhead = ( i + 1 ) % bq->bq_size;
    rt_cpu_unblock( &flags );

    if ( newhead == bq->bq_tail ) return;  /* no room */
    bq->bq_head = newhead;

    /*  time to poke receiver if he is waiting */
    if ( bq->bq_head != bq->bq_tail ) {
        if ( bq->bq_msg_waiting )
            ksendmessage( bq->bq_msg_waiting, EMSG_BQ_WAITING, 0 );

        rt_cpu_block( &flags );
        if ( (t = bq->bq_recvwait) != NULL ) {
#ifdef NEW
            bq->bq_recvwait = NULL;
            t->th_bq = NULL;
            ksendmessage( t , EMSG_BQ_WAITING , 0 );
            rt_cpu_unblock( &flags );
#else
            bq->bq_recvwait = NULL;
            t->th_bq = NULL;
            kresume( t );
            rt_cpu_unblock( &flags );
#endif
            return;
        }
        rt_cpu_unblock( &flags );
    }

}
/*
 * bq_getbyte - nonblock read of BYTE
 *            - returns 1 if a byte is available
 */
int bq_getbyte( bq_str *bq, BYTE *b )
{
    WORD i, flags, newtail;
    thread_x *t;

    rt_cpu_block( &flags );
    i = bq->bq_tail;
    if ( i != bq->bq_head ) {
        *b = bq->bq_queue[ i ];
        newtail = i + 1;
        if ( newtail == bq->bq_size ) newtail = 0;
        bq->bq_tail = newtail;
        rt_cpu_unblock( &flags );


        /*  time to poke sender if he is waiting */
    
        rt_cpu_block( &flags );
        if (( t = bq->bq_sendwait) != NULL ) {
#ifdef NEW
            bq->bq_sendwait = NULL;
            t->th_bq = NULL;
            ksendmessage( t, EMSG_BQ_AVAIL, 0 );
#else
            bq->bq_sendwait = NULL;
            t->th_bq = NULL;
            kresume( t );
#endif
        }
        rt_cpu_unblock( &flags );

        /* inform peer it can write more */
        if ( bq->bq_msg_avail )
            ksendmessage( bq->bq_msg_avail, EMSG_BQ_AVAIL, 0 );

        return( 1 );
    }
    rt_cpu_unblock( &flags );
    return( 0 );
}

/*
 * bq_writebyte - block write of BYTE, does not allow buffer overflow
 */
void bq_writebyte( bq_str *bq, BYTE b )
{
    WORD newhead, head, tail, size, flags;
    thread_x *t;

    size = bq->bq_size;

    do {
        rt_cpu_block( &flags );
        head = bq->bq_head;
        tail = bq->bq_tail;
        rt_cpu_unblock( &flags );

        newhead = head + 1;
        if ( newhead == size ) newhead = 0;

        /* we block if tail was where we want to move into */
        if ( newhead != tail ) break;
#ifdef OLD
        rt_sleep(0);
#else
#ifdef NEW
        rt_cpu_block( &flags );
        kcurthread->th_bq = bq;
        bq->bq_sendwait = kcurthread;
        rt_cpu_unblock( &flags );
#ifdef NEWEST
        kreadspecialmessage( EMSG_BQ_AVAIL );
        bq->bq_sendwait = NULL;     /* clear the messaging requirement */
#else // just NEW
        {
            int msg;
            DWORD data;
            kreadmessage( &msg, &data );
        }
#endif // NEWEST

#else
#error fails
        rt_cpu_block( &flags );
        kcurthread->th_bq = bq;

        ksuspendhow( kcurthread, &bq->bq_sendwait );
        rt_cpu_unblock( &flags );

        rt_yield();
#endif
#endif
        
    } while ( 1 );

    /* don't need to block interrupts because orderred correctly */
    bq->bq_queue[ head ] = b;
    bq->bq_head = newhead;

    /* inform peer */
    if ( bq->bq_msg_waiting )
        ksendmessage( bq->bq_msg_waiting, EMSG_BQ_WAITING, 0 );

    rt_cpu_block( &flags );
    if (( t = bq->bq_recvwait) != NULL ) {
#ifdef NEW
        bq->bq_recvwait = NULL;
        t->th_bq = NULL;
        ksendmessage( t , EMSG_BQ_WAITING , 0 );
#else
        bq->bq_recvwait = NULL;
        t->th_bq = NULL;
        kresume( t );
#endif
    }
    rt_cpu_unblock( &flags );
}

/*
 * bq_readbyte - block of read of BYTE
 */
int bq_readbyte( bq_str *bq, BYTE *b )
{
    WORD head, tail, size;
    WORD flags;

    size = bq->bq_size;
    rt_cpu_block( &flags );
    do {
        tail = bq->bq_tail;
        head = bq->bq_head;

        if ( tail != head ) {
            *b = bq->bq_queue[ tail++ ];
            bq->bq_tail = tail % size;
            rt_cpu_unblock( &flags );

            /* inform peer it can write more */
            if ( bq->bq_msg_avail )
                ksendmessage( bq->bq_msg_avail, EMSG_BQ_AVAIL, 0 );
            if ( bq->bq_sendwait  )
                ksendmessage( bq->bq_sendwait, EMSG_BQ_AVAIL, 0 );

            return( 1 );
        }
        rt_cpu_unblock( &flags );

#ifdef OLD
        rt_sleep( 0 );
#else
#ifdef NEW
        rt_cpu_block( &flags );
        kcurthread->th_bq = bq;
        bq->bq_recvwait = kcurthread;
        rt_cpu_unblock( &flags );
#ifdef NEWEST
        kreadspecialmessage( EMSG_BQ_WAITING );
#else // just NEW
        {
            int msg;
            DWORD data;
            kreadmessage( &msg, &data );
        }
#endif // NEWEST

#else
#error fails
        rt_cpu_block( &flags );
        kcurthread->th_bq = bq;
//      bq->bq_recvwait = kcurthread;
        ksuspendhow( kcurthread, &bq->bq_recvwait );
        rt_cpu_unblock( &flags );
        rt_yield();
#endif
#endif
    } while ( 1 );
}
/*
 * bq_readcount - returns number of BYTEs left to read
 *              - not async
 */
WORD bq_readcount( bq_str *bq )
{
    WORD i;
    WORD flags;

    rt_cpu_block( &flags );
    i = bq->bq_tail;
    if ( bq->bq_head == i )
        i = 0;
    else if ( bq->bq_head > i )
        i = bq->bq_head - i;
    else
        i = bq->bq_size - i + bq->bq_head;
    rt_cpu_unblock( &flags );
    return( i );
}


