/*************************************************************************
*                                                                        *
*  OCS Server  INMOS B004 Link Adaptor Driver                            *
*                                                                        *
*  12th March 1987.                                                      *
*                                                                        *
*  Copyright INMOS Limited, 1987.                                        *
*                                                                        *
*  Amiga Version by Juergen Sang & Robert Sang                           *
*                                                                        *
*  14th March 1988.                                                      *
*                                                                        *
*  Upgrade to 3L-Server by Gerhard Bartz                                 *
*                                                                        *
*  18th April 1989                                                       *
*                                                                        *
*************************************************************************/
#include <stdio.h>
#include "srvconst.h"
#include "findboard.inc"

#define LINK_READ_OFFSET         0x09L
#define LINK_WRITE_OFFSET        0x0BL
#define LINK_IN_STATUS_OFFSET    0x0DL
#define LINK_OUT_STATUS_OFFSET   0x0FL
#define LINK_RESET_OFFSET        0x01L
#define LINK_ERROR_OFFSET        0x01L
#define ID_ADDRESS                0xffff000e
#define PC_ID                     -1
#define RESET_COUNT              10000
#define ANALYSE_COUNT            10000
#define MAX_LINK_TIME            32000
char 
   *link_base, *link_read, *link_write, *link_in_status,
        *link_out_status, *link_reset, *link_error;

void init_root ()
{
   link_base=find_board(0x2200,1);
   if (link_base==0L) {
      printf("ERROR: SANG- BOARD NOT FOUND !!!\n");
      exit(1);
   }
   link_read=link_base+LINK_READ_OFFSET;
   link_write=link_base+LINK_WRITE_OFFSET;
   link_in_status=link_base+LINK_IN_STATUS_OFFSET;
   link_out_status=link_base+LINK_OUT_STATUS_OFFSET;
   link_reset=link_base+LINK_RESET_OFFSET;
   link_error=link_base+LINK_ERROR_OFFSET;
}

int link_in_test ()
/* Return TRUE if a byte is available on the link, FALSE otherwise. */
{
    return (((*link_in_status) & BIT_0) != 0);
}

int byte_from_link ()
/* Read a byte from the link adaptor. */
{
    while (!((*link_in_status) & BIT_0))
        ;
    return ((int)(*link_read));
}

void test_byte_to_link(ch)
int ch;

{
    int i=5;

    *link_reset=1;
    while((i--)>0);
    i=1;
    *link_reset=0;
    while((i--)>0);
    *link_write=(char)ch;
}

void byte_to_link (ch)
int ch;
/* Write a byte to the link adaptor. */
{
    while (!((*link_out_status) & BIT_0))
        ;
    *link_write= (char)ch;
}

int byte_to_link_t (ch)
int ch;
/* Write a byte to the link adaptor within a specified time, or timeout.
   Return TRUE if the byte was NOT written, FALSE if the byte was written. */
{
    register int not_written = TRUE, i;
    for (i=0; ((i < MAX_LINK_TIME) && not_written); i++)
        if ((*link_out_status) & BIT_0) {
            *link_write= (char)ch;
            not_written = FALSE;
        }
    return (not_written);
}

int error_test ()
/* Test if an error is present in the transputer system. */
{
    return (!((*link_error) & BIT_0));
}

void reset_root ()
/* Reset the root transputer. */
{
    register int i;
    *link_reset= 0;                     /* deassert reset   */
    for (i=0; i < RESET_COUNT; i++)     /* wait awhile      */
        ;
    *link_reset= 1;                     /* assert reset     */
    for (i=0; i < RESET_COUNT; i++)     /* wait awhile      */
        ;
    *link_reset= 0;                     /* deassert reset   */
}

void reset_analyse_root ()
/* Reset the root transputer into analyse mode. */
{
    register int i;
    *link_reset= 0;                     /* deassert reset   */
    for (i=0; i < RESET_COUNT; i++)     /* wait awhile      */
        ;
    *link_reset= 2;                     /* assert analyse   */
    for (i=0; i < ANALYSE_COUNT; i++)   /* wait awhile      */
        ;
    *link_reset= 3;                     /* assert reset     */
    for (i=0; i < RESET_COUNT; i++)     /* wait awhile      */
;
    *link_reset= 0;                     /* deassert reset   */
                              /* and analyse      */
}

int word_from_link ()
/* Read a word from the link. */
/* This routine is host- and transputer- word-length dependent.
   Here we assume a host int length of two bytes, and a transputer int length
   of four bytes.
*/
{
    register int t, ch;
    t = (byte_from_link()) & 0xff;         /* l.s. byte  */
    ch =(byte_from_link()) & 0xff;        /* m.s. byte  */
    t = t | ( ch<<8 );
    ch = byte_from_link();        /* Ignore upper 16-bits sent by transputer */
    ch = byte_from_link();
    return (t);
}

void word_to_link (w)
/* Write a word to the link, least significant byte first. */
/* This routine is host- and transputer- word-length dependent.
   Here we assume a host int length of two bytes, and a transputer int length
   of four bytes.
*/
int w;
{
    byte_to_link ((int)(w & 0xff));         /* l.s. byte */
    byte_to_link ((int)((w >> 8) & 0xff));  /* m.s. byte */
    if (w < 0)
    {
        byte_to_link (0xff);
        byte_to_link (0xff);
    } else
    {
        byte_to_link (0);
        byte_to_link (0);
    }
}

void slice_from_link (length, slice)
int *length;
char *slice;
/* Read a slice from the link.
   The slice consists of a four-byte length followed by a sequence of bytes. */
{
    register int i, real_len;
    *length = real_len = word_from_link();
#ifdef REV_A_FIX
    if (real_len == 1) real_len = 2;
#endif
    for (i = real_len; i>0; i--)
        *slice++ = byte_from_link();
}

int trunc_slice_from_link (max_length, str)
int max_length;
char *str;
/* Read a slice from the link whose maximum length is max_length.
   If the length read in is longer than this, the remainder of the slice is
   read in, but ignored. Return number of bytes read in. */
{
    int length;
    register int real_len;
    length = real_len = word_from_link();
#ifdef REV_A_FIX
    if (real_len == 1) real_len = 2;
#endif
    if (real_len > max_length)
    {
        register int i;
        for (i = 0; i < max_length; i++)
            *str++ = (char)byte_from_link();
        for (i = max_length; i < real_len; i++)
            byte_from_link();
    } else
    {
        register int i;
        for (i = 0; i < real_len; i++)
            *str++ = (char)byte_from_link();
    };
    return (length);
}

void slice_to_link (length, slice)
int length;
char *slice;
/* Write a slice to the link.
   The slice consists of a four-byte length followed by a sequence of bytes. */
{
    register int i, real_len = length;
    word_to_link (real_len);
#ifdef REV_A_FIX
    if (real_len == 1) real_len = 2;
#endif
    for (i = 0; i < real_len; i++)
        byte_to_link (*slice++);
}

void long_word_to_link (w)
/* Write a long word to the link. */
/* This routine is host- and transputer- word-length dependent.
   Here we assume a host long int length of at least four bytes,
   and a transputer int length of four bytes.
*/
long int w;
{
    register int i;
    for (i = 0; i<4; i++)
    {
        byte_to_link ((int)(w & 0xff));
        w >>= 8;
    };
}

long long_word_from_link ()
/* Read a long word from the link. */
/* This routine is host- and transputer- word-length dependent.
   Here we assume a host long int length of at least four bytes,
   and a transputer int length of four bytes.
*/
{
    long int result = 0;
    register int i;
    result |= (((long) (unsigned char) byte_from_link()) << 0);
    result |= (((long) (unsigned char) byte_from_link()) << 8);
    result |= (((long) (unsigned char) byte_from_link()) <<16);
    result |= (((long) (unsigned char) byte_from_link()) <<24);
    return (result);
}

void long_slice_to_link (length, slice)
long int length;
char *slice;
/* Write a slice to the link.
   The slice consists of a four-byte length followed by a sequence of bytes. */
{
    long int i, real_len = length;
    long_word_to_link (real_len);
#ifdef REV_A_FIX
    if (real_len == 1) real_len = 2;
#endif
    for (i = 0; i < real_len; i++)
        byte_to_link (*slice++);
}

void long_slice_from_link (length, slice)
long int *length;
char *slice;
/* Read a slice from the link.
   The slice consists of a four-byte length followed by a sequence of bytes. */
{
    long int i, real_len;
    *length = real_len = long_word_from_link();
#ifdef REV_A_FIX
    if (real_len == 1) real_len = 2;
#endif
    for (i = real_len; i>0; i--)
        *slice++ = byte_from_link();
}

void safe_byte_to_link ( b )
int b;
{
    byte_to_link ( b );
#ifdef REV_A_FIX
    byte_to_link ( b );
#endif
}

int safe_byte_from_link ()
{
    int b, padding;
    b = byte_from_link ();
#ifdef REV_A_FIX
    padding = byte_from_link ();
#endif
    return (b);
}

int boot_root (boot_file_name)
char *boot_file_name;
/**** SUBTRACT1 begin
#define BOOT_BUFFER_LENGTH RECORD_LENGTH
      SUBTRACT1 end ****/
/* Boot the root transputer, ie. reset the root transputer, open the 
   boot file  and write this down the link adaptor as a stream of bytes. */
{
    int result = FALSE;
    FILE *boot_file;
    if ((boot_file = fopen (boot_file_name, "r")) != NULL ) {
register int count = 0;
int anzahl=0;
char file_buffer [BOOT_BUFFER_LENGTH];
int do_boot = TRUE;
while (do_boot)
{
count = fread ( file_buffer, sizeof(char), BOOT_BUFFER_LENGTH, boot_file );
if (count > 0)
{
register int i;
#ifdef DEBUG
for (i = 0; ((i < count) && do_boot); i++)
{
    do_boot = ! byte_to_link_t (file_buffer[i]);
    if (!do_boot) printf("Fehler bei Byte:%d\n",anzahl);
   anzahl++;
}
#else
register char* ptr;
ptr=file_buffer;
for (i = 0; i<count; i++)
{
    while(!((*link_out_status)&BIT_0));
    *link_write=*(ptr+i);
}
#endif

if ( ! do_boot )
    result = OPERATIONFAILED_ERR;
}
else
{
do_boot = FALSE;
if feof (boot_file)
{
    if (fclose (boot_file) == 0)
        result = F_OK;
    else
        result = OPERATIONFAILED_ERR;
}
else
    result = OPERATIONFAILED_ERR;
};
};
    }
    else
        result = OPERATIONFAILED_ERR;
    return (result);
}
