/* C40 loader */
#ifdef DEBUG
#define INFO(x) printf x
#else
#define INFO(x)
#endif

/* TDMB410 I/O ports: */
#define IOP 0x200
#define RST IOP+6
#define TXS IOP+0x16
#define RXS IOP+0x14
#define TXD IOP+0x12
#define RXD IOP+0x10
/* C40 ROM loader specific values: */
#define LMICR 0x1D7C3850L
#define GMICR 0x1B840030L
#define IVTP  0x002FFC00L
#define TVTP  0x002FFE00L
#define IACK  0x002FFBFFL
/*
\ File header, 20 bytes long:
\  00-01: 0093h identifies TMS320C3x/4x COFF file
\  02-03: number of section headers
\  16-17: 0 or 28, size of optional header
\  18-19: flags 1:nonRelocatable 2:executable 10:C40obj 100:littleEndian
\ Optional file header, 28 bytes long:
\  16-19: entry point address (WARNING: C40 doc wrongly says 20-23)
\  20-23: beginning address of .text (: C40 doc wrongly says 16-19)
\ Each section header, 28h bytes long:
\  00-07: section name, padded with nulls
\  12-15: load address
\  16-19: word size
\  20-23: file offset of raw data
\  36-37: flags 1:dummy 2:noload 80:uninitData
*/
#include<conio.h> /* outp outpw inpw */
#include<stdio.h> /* fopen fclose fseek fread perror printf */
#include<stdlib.h> /* exit */
void resetC40s(void)
{ outp(RST,0);
  outp(RST,1);
  outp(RST,0);
}
int load1(char* filename)
{
  FILE* fp;
  int  sect; /* section counter */
  long shfo; /* section's header file offset */
  char buf[40];
#define error(string) {perror(string); exit(1);}
  if((fp=fopen(filename, "rb")) == NULL) error(filename);
  fread(buf,20,1,fp); /* read file header */
  if(*(short*)buf != 0x0093) error("Not a TMS320C3x/4x COFF file.");
#define errorflag(mask,string) if(!(*(short*)(buf+18)&(mask))) error(string)
  errorflag(0x0100,"Big endian files not supported.");
  errorflag(0x0010,"Not a TMS320C40 object code file.");
  errorflag(0x0002,"Not executable (unresolved references).");
  errorflag(0x0001,"Relocatable executable not supported.");
  if(*(short*)(buf+16)!=28) error("Not executable (no optional header).");
#define outplong(val) outpw(TXD,val);outpw(TXD,(val)>>16)
#define outplbuf(o) outpw(TXD,*(short*)(buf+o));outpw(TXD,*(short*)(buf+o+2))
  outplong(GMICR);
  outplong(LMICR);
  sect=*(short*)(buf+2);
  fread(buf,28,1,fp); /* read optional file header */
  INFO( ("entry at %d\n", *(long*)(buf+16)) );
  /* the entry point must be at the beginning of the first loaded section: */
  outplong(1L); /* send first a non-empty (one word) dummy section */
  outplbuf(16); /* at the entry point address */
  outplong(0L); /* will be overwritten by .text section */
  INFO( ("%d sections:\n", sect) );
  for(shfo=20+28; sect--!=0; shfo+=40)
  { fseek(fp,shfo,SEEK_SET);
    fread(buf,40,1,fp); /* read section header */
    INFO( ("2+%ld words, flags=%x\n", *(long*)(buf+16), *(short*)(buf+36)) );
    if(*(long*)(buf+16) != 0 && (*(short*)(buf+36)&0x0083) == 0)
    { outplbuf(16); /* section word size */
      outplbuf(12); /* section load address */
      fseek(fp,*(long*)(buf+20),SEEK_SET);
      while((*(long*)(buf+16))--)
      { fread(buf,4,1,fp);
        outplbuf(0);
  } } }
  outplong(0L); /* end marker */
  outplong(IVTP);
  outplong(TVTP);
  outplong(IACK);
  fclose(fp);
/* while((inpw(RXS)&0x80)==0) kbhit(); */
  return(inpw(RXD)|inpw(RXD));
}
int main(int argc, char** argv)
{ int i=0;
  resetC40s();
  while(--argc && (i=load1(*++argv)));
  if(argc>1) printf("%s last loaded.\n", *argv);
  if(i) printf("Not enough files loaded.\n");
  return(argc>1||i);
}
