/* This file is a bootloader for ADSP2181 in an embedded system. */
/* If you use the function, you must adapt the hardware parameters. */
/* You must change the I/O adresses for IDMA Port and the reset functionality  */
/* for you ADSP hardware. */
/* Feel free to use the function in you application */
/* Rev. 1  05/04/2000 created by Michael Roch, SPEKTRA GmbH, Germany Dresden */

#include <dos.h>

#define DSP_PORT 0x100			/* subadress for DSP IDMA - Port - must be adapt to your hardware */

#define IDMADATA DSP_PORT		/* IDMA data register */
#define IDMACTL  DSP_PORT+2	/* IDMA adress control register */
#define DSPRESET DSP_PORT+4	/* DSP resets by port access to this adress */

unsigned char far *qpointer;		/* global pointer for data read */
volatile unsigned DSP_access;	/* Flag to prevent access IDMA from interrupts
												while it's used */

/* reads a number of bytes from exe file */
unsigned long dsp_code_read(int number){
unsigned long value;

if(!number)return 0;
value=(unsigned long)*qpointer++;
while(--number){
  value=(value<<8)|*qpointer++;
  }
return value;
}

/* DSP boot
Input - pointer to exe array in memory
return 1=boot ok, 0=boot error */
int dsp_boot(void far *DSP_boot_code){
unsigned long buffer;
unsigned adress,pw0ok=0,number,programspace,i,k;
unsigned long data,programword0,checksum;

DSP_access=1;
outport(DSPRESET,0);	/* DSP resets by port access --> must be adapt to your hardware */
qpointer=DSP_boot_code;
buffer=dsp_code_read(3);
if(buffer!=0x1B1B69L) return 0; /* test for code begin sequence */
buffer=dsp_code_read(3);
do{	/* while valid memory blocks left */
  if(buffer==0x405041L)programspace=1;	/* ==> @PA - program memory*/
  else{
    if(buffer==0x404441L)programspace=0;	/* ==> @DA - data memory*/
    else return 0;
    }
  checksum=adress=(unsigned)dsp_code_read(2)&0x3fff;/* destination adress */
  number=(unsigned)dsp_code_read(2);	/* number of words */
  if(programspace&&!adress){				/* write to adress 0 in pm starts the */
    programword0=dsp_code_read(3);		/* program execution in DSP */
    pw0ok=1; /* pm(0) exist */ 			/* --> write pm(0) as last access */
    adress=1;
    checksum+=programword0;
    number--;
    }
  if(!programspace)adress|=0x4000;		/* dm - access bit */
  outport(IDMACTL,adress);
  if(programspace){
    do{
      checksum+=data=dsp_code_read(3);	/* read a pm word, calculate checksum */
      outport(IDMADATA,(unsigned)(data>>8));	/* write the word to DSP */
      outport(IDMADATA,(unsigned)data&0xff);
      }while(--number);
    }
  else{
    do{
      checksum+=data=dsp_code_read(2);/* read a dm word, calculate checksum */
      outport(IDMADATA,(unsigned)data);	/* write the word to DSP */
      }while(--number);
    }
  buffer=dsp_code_read(4);	/* load source checksum */
  if(buffer!=checksum)return 0;	/* checksum error */
  buffer=dsp_code_read(3);
  }while((buffer==0x405041L)||(buffer==0x404441L));/* while valid memory blocks left */
if(buffer!=0x1B1B6FL)return 0;	/* end sequence of DSP exe file */
if(pw0ok){								/* if pm(0) word available start the DSP */
  i=0;
  do{
    outport(IDMACTL,0);		/* Adress = pm(0) */
    outport(IDMADATA,(unsigned)(programword0>>8));/* write pm(0) */
    outport(IDMADATA,(unsigned)programword0&0xff);/* DSP starts */
    i++;
    outport(IDMACTL,0x4000);		/* Adress = dm(0) */
    k=inport(IDMADATA);			/* i have my dm(0) initialized in DSP source to a value != 0 */
    }while((k!=0)&&(i<10));	/* and change the value after the start to 0 for check the */
  if((k!=0)&&(k!=100))return 0;	/* correct start */
  else{
    DSP_access=0;					/* IDMA access for other tasks enable */
    return 1;
    }
  }
else{
  return 0;	/* no valid pm(0) word - DSP start impossible */
  }
}


