Source file: /~heha/basteln/PC/Programmiergeräte/PEPS-III/peps4win32.zip/src/peps.c

/*--------------------------------------------------------------
PEPS.c Überspielt eine Datei von Diskette auf PEPS-II oder PEPS-III

Zum Compilieren als PEPSIII Version muss #define PEPSIII in Peps.h
gesetzt werden und peps.o mit sendIII.o gelinkt werden

Zum Compilieren einer PEPSII Version muss peps.o mit sendII.o 
gelinkt werden.

Automatische Formaterkennung: Binär, Intel-HEX ext., Motorola-S, 
Obj-Modul
Version für PC, AMIGA, (ATARI noch nicht verfügbar)

Vers. 4.1   für PEPSIII
Vers. 3.3   für PEPSII (da fehlt jegliche Dokumentation!)

--------------------------------------------------------------

Interpretation der Parameter. <Filename> darf an beliebiger Stelle stehen.

-ennnnn  lädt PEPSe erst ab EPROM-Adresse nnnnnn
-annnnn  (nur AMIGA Obj-Modul) konvertiert Object-Module nach Adr. nnnnnn
-rnnnnn  (nur AMIGA Obj-Modul) legt Daten(RAM-)bereich nach nnnnnn
-snnnnn  (nur AMIGA Obj-Modul) legt System Stack nach nnnnnn
-lnnnnn  Länge der Daten
-w<Name>  speichert die Ausgabe zusätzlich nach	File <Name> ab
	  oder liest aus PEPSIII wenn kein InFile angegeben.
-bn	 bestimmt die Anz. der angeschlossenen PEPSE: 1, 2, oder 4
-cn	 Check-Modus: 0 = kein Daten Rücklesen
		      1 = nur Test-Byte rücklesen(default)
		      2 = alle Daten rücklesen
-on      Reset-Modus nach dem Laden: 0 = nicht aktivieren (default)
				     1 = aktivieren
-inn nn .. Lädt direkt Bytes an die mit -ennnnnn spezifizierte Adresse
-pn      (nur PC) Anschluss an LPT1: (default), LPT2: LPT3: LPT4
-v  	 verbose, mit Anzeige der HEX,SHX Daten beim Überspielen
-z       Delaymodus erzwingen

--------------------------------------------------------------
Änderungen:

120621	Portierung Win32 (unter Beibehaltung der Übersetzbarkeit für Linux)
	heha@hrz.tu-chemnitz.de

SS 07/98 Portierung auf Linux 2.0.33 von
         michael.smutka@telecom.at
         heimo.schoen@telecom.at
	 -- have a lot fun --

AM 12/92 Software ist jetzt für PEPS-III und PEPS-IIa benutzbar.

AM 23.08.93  Beim Laden eines INTEL-Hex-Files wird ein leerer
	     Datenrecord als Endrecord interpretiert.

aMay 18.12.97 Extended Linear Address Record (Record Typ 4) in
	      INTEL-HEX Loader eingefügt.
--------------------------------------------------------------*/
#define STICHPROBEN 10
/*---------------------- Includes ------------------------------*/
#include "peps.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>

/*---------------------- Defines -------------------------------*/
typedef enum {AUTOTYPE,BINARY,INTEL_HEX,MOTOROLA_S,AMIGAOBJ} FILETYPE;
/*---------------------- Globals -------------------------------*/
static FILE *OutFile;

typedef enum {BYTETEST=1,READBACK=2,NOCHECKSUMTEST=4,CLIPPED=8} CHECKMODE;

static CHECKMODE CheckMode=BYTETEST;
static FILETYPE FileType;
static bool ResetMode;
static long TargetSize=0x80000;		// Vorgabe: 512 KByte
static bool tss;			// TargetSize gesetzt
static bool ess;			// EPROM-Offset gesetzt

typedef struct{
 char* typ;	// numerisch, wenn kleiner 4 MByte, sonst String
 WORD size;	// Chip-Größe in KByte, Null für Zwischenüberschriften
 WORD switches;	// DIL-Schalterstellungen (undefiniert für Zwischenüberschriften)
}TYPSWITCH;
#define SW(n) (1<<(n-1)) /* Macro für Schalterstellung der Typtabelle */
static const TYPSWITCH TypSwitch[]={
 {"EPROM", 0, 0},					// Schriftzeile 'EPROM'
 {(char*)2716,     2, SW(14)},						// 2716
 {(char*)2732,     4, SW(1) | SW(14)},					// 2732
 {(char*)2764,     8, SW(1) | SW(15)},					// 2764
 {(char*)27128,   16, SW(1) | SW(2) | SW(15)},				// 27128
 {(char*)27256,   32, SW(1) | SW(2) | SW(4) | SW(15)},			// 27256
 {(char*)27512,   64, SW(1) | SW(2) | SW(4) | SW(5) | SW(15)},		// 27512
 {(char*)27010,  128, SW(1) | SW(2) | SW(4) | SW(5)},			// 27010
 {(char*)27020,  256, SW(1) | SW(2) | SW(4) | SW(5) | SW(7)},		// 27020
 {(char*)27040,  512, SW(1) | SW(2) | SW(4) | SW(5) | SW(7) | SW(8)},	// 27040
 {"RAM", 0, 0},						// Schriftzeile 'RAM'
 {(char*)6116,     2, SW(10)| SW(14)},					// 6116
 {(char*)6164,     8, SW(1) | SW(11)| SW(12)| SW(15)},			// 6164
 {(char*)62256,   32, SW(1) | SW(2) | SW(3) | SW(11)| SW(15)},		// 62265
 {(char*)511001, 128, SW(1) | SW(2) | SW(3) | SW(6) | SW(11)},		// 511001
 {(char*)628512, 512, SW(1) | SW(2) | SW(3) | SW(6) | SW(7) | SW(9) | SW(11)}}; // 628512


/*---------------------- Locals --------------------------------*/

static bool  Verbose = false;
static TCHAR *InName, *OutName;
static DWORD Transferred = 0;

/*---------------------- Functions -----------------------------*/

static void clrline(void) {
 printf("\r%75s\r","");	// Zeile löschen
}

static void PressAnyKey() {
 int c;
 if (!_isatty(_fileno(stdout))) return;	// Umleitung aktiv
 rprintf(39);		// "Taste drücken …" (Cursor sollte bereits auf neuer Zeile stehen!)
 c=mygetch(false);	// Tastenabfrage ohne Zeileneditor, ohne Puffer, ohne Echo
 clrline();		// Zeile wieder löschen
 switch (c) {		// Zeichen auswerten
  case 3:	// ^C
  case 27:	// Escape
  case 'Q':	// Quit
  case 'q': DoExit(0);
 }
}

void newline() {
 printf("\n");
}

/* Ausgabe der DIL-Schalter und der Sockelbelegung */
static void PrintTable(void) {
 int k;

 PressAnyKey();
 rprintf(38);	// Tabellenkopf

 for (k = 0; k<elemof(TypSwitch); k++) {
  const TYPSWITCH *ts = TypSwitch+k;
  if (!ts->size) printf("%s\n", ts->typ);
  else{
   WORD mask;
   printf("%-7lu %3uKB  ", (DWORD_PTR)ts->typ, ts->size);
   for (mask=1; mask; mask<<=1) {
    printf ("  %c", ts->switches & mask ? 'X' : '-');
   }
   newline();
  }
 }
 newline();
 PressAnyKey();
 rprintf(37);
}

// Fehlermeldung ausgeben und Programm beenden (außer bei RCode = 0)
void DoExit(int RCode, ...) {
 static bool once;
#ifndef WIN32
 int save_errno=errno;
#endif
 if (once) return;		// Rekursive Aufrufe verhindern
 once=true;
 iodeviceDone();
 if (InFile) fclose(InFile);
 if (OutFile) fclose(OutFile);
 if (RCode) {
  va_list va;
  if (OutFile) _tunlink(OutName);	// Dateileiche löschen
  va_start(va,RCode);
  vrprintf(RCode,va);		// Fehlermeldung ausspucken
  va_end(va);
  newline();
#ifdef WIN32
  {DWORD e=GetLastError();
   if (e) {
    PTSTR s;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,0,e,LANG_USER_DEFAULT,(PTSTR)&s,0,0);
#ifndef UNICODE
    CharToOem(s,s);
#endif
    _tprintf(T("%s\n"),s);	// hübscher (deutsch; stdout)
    LocalFree(s);
   }
  }
#else
  if (save_errno) errno=save_errno,perror(NULL);	// hässlich (englisch; stderr)
#endif
 }
 exit(RCode ? 1 : 0);
}

static void ManualReset(void) {
 DWORD us=2048;	// Mikrosekunden, also 250 Hz Vorgabe
 bool Wackel=false;
 bool resetState=true;

 rprintf(16);
 for(;;) {
  int input;
  if (Wackel) rprintf(18,us);
  else if (resetState) rprintf(19);
  else rprintf(20);
  while ((input=mygetch(Wackel))==-1) {
   if (resetState) Simulate(); else HoldReset();
   resetState=!resetState;
   usleep(us);
  }
  switch (input) {
   case '\r':	// Win32
   case '\n': if (!Wackel && resetState) {
    Simulate();
    clrline();
    rprintf(20);
   }nobreak;
   case 0x1b:
   case 0x03:
   case 'Q':
   case 'q':
    if (Wackel) {
     Wackel=false;
     if (resetState) Simulate();
     clrline();
     rprintf(20);
    }
    newline();
    return;
   case '<':	// langsamer, Pause verdoppeln
#ifdef WIN32
   case 0x4BE0:	// grau PfeilLinks
   case 0x4B00:	// weiß PfeilLinks
#else
   case 0x441B:	// ^[[D = PfeilLinks
#endif
    us<<=1; if (us > 1<<18) us = 1<<18;
    Wackel=true; 
    break;
   case '>':	// schneller, Pause halbieren
#ifdef WIN32
   case 0x4DE0:	// grau PfeilRechts
   case 0x4D00:	// weiß PfeilRechts
#else
   case 0x431B:	// ^[[C = PfeilRechts
#endif
    us>>=1; if (us<16) us=16;
    Wackel=true; 
    break;
   case 'I':
   case 'i':
    Wackel=true; 
    break;
   case 'R':
   case 'r':
    Wackel=false; 
    if (!resetState) HoldReset(), resetState=true;
    break;
   default:	// jede andere Taste
    Wackel=false;
    HoldReset();
    usleep(1000);
    Simulate();
    resetState=false;
  }
  clrline();
 }
}

// LPT-Nummer (1-basiert) in Portadresse umrechnen.
// Unter Win32 wird der Konfigurationsmanager herangezogen,
// um auch an exotische Portadressen (von PCI- und PCIexpress-Karten) zu gelangen.
// Unter Linux nicht; da ist's weniger kritisch, da der Zugriff via /dev/parportX
// sowieso viel besser ist als (wie früher) mittels ioperm() direkt zuzugreifen.
static void ResolveLogicalPortAddress() {
  if (iodevice<IOLOGICAL && PPort<=9) {
// Typische althergebrachte Parallelport-Adressen
   static const WORD lpport[] = {0x378, 0x278, 0x3bc};
   int i=PPort;
   PPort=GetParportAddr(i);	// Auch unter Win98!
   if (!PPort) PPort= i<elemof(lpport) ? lpport[i] : 0;	// Beim Versagen die Standard-Adressen nehmen
   if (!PPort)
#ifdef WIN32
		DoExit(24,i+1);	// "LPT%d nicht vorhanden!" (1-basiert, NT: SymLink auf \\.\parport%d)
#else
		DoExit(24,i);	// "/dev/parport%d nicht vorhanden!" /0-basiert)
#endif   
  }
 }

// Die Fortschrittsanzeige zeigt einen Drehstrich und eine Prozentanzeige.
// Die Aktualisierungshäufigkeit und die Drehzahl wird per Timer
// auf 10 Hz gedrosselt, so ergibt sich gutes visuelles Feedback
// und keine exzessive Zeichenausgabe.
void Fortschritt(long zaehler, long nenner) {
 if (nenner>4) {	// bei Kleinkram (Init) keine Anzeige
  static int idx;
  static DWORD Toc;
  DWORD Tic=GetTickCount();
  if ((Tic-Toc)>=100) {
   printf(" %c%3d%%\b\b\b\b\b\b","/-\\|"[idx],(int)(zaehler*100/nenner));
   idx=(idx+1)&3;	// rechtsherum drehen
   Toc=Tic;
  }
 }
}

static int _cdecl cmpint(const void*a, const void*b) {
 return *(int*)a-*(int*)b;
}

int _cdecl main(int argc, TCHAR *argv[]) {

  int	I,Size=0;
  DWORD Tic, Toc;	// Grobe Zeitmessung

  bool used_p_x_flg = false;
  TCHAR option;
#ifdef WIN32
  srand(GetTickCount());
#endif
  InitRes();
  
  /*--------------------------------------------------------------
			Parameter-Interpretation
	--------------------------------------------------------------*/

  if (!argc) DoExit(0); /* nicht Workbench-fähig */

  if ((argc == 1) || ((argc == 2) && (argv[1][0] == '?'))) goto usage;

 for (I = 1; I < argc; I++) {
  option=0;
  switch (argv[I][0]) {
#ifdef WIN32
   case '/':
#endif 
   case '-': option=argv[I][1];
  }
  if (argv[I][1]=='=') option=argv[I][0];

#ifdef WIN32
#define MORE (argv[I][2] ? argv[I]+2 : argv[++I])
#else
#define MORE (argv[I][2] || I<argc-1 ? argv[I]+2 : argv[++I])
// Seltsam: Unter Linux ist das argv-Array nicht NULL-terminiert!
#endif
  
  if (option) switch (tolower(option)) {
   
   case 'h':
   case '?':
 usage:
    rprintf(22);
    PrintTable();
    DoExit(0);

   case 'z':
    if (argv[I][2]) outcycles = _ttoi(MORE)+1;
    else outcycles = 2;	/* Mindestverzögerung bei Angabe von <z> ohne Parameter */
    break;

   case 'b': 		/* Anzahl der angeschlossenen PEPSe oder Ansprech-Bitmaske */
    Cen_Clk = (BYTE)_tcstoul(MORE,NULL,16);
    if (!(Cen_Clk&0xF0) || Cen_Clk&0x0F) {	// keine Bitmaske?
     if (Cen_Clk<1 || Cen_Clk>4) DoExit (21, argv[I][1]);	// Fehler (muss 1..4 sein)
     Cen_Clk = ((1<<Cen_Clk)-1) << 4;		// „rechtsbündige“ Bitmaske draus machen
    }
    break;

   case 'c':
    CheckMode = _ttoi(MORE); 
    if (CheckMode==3 || (unsigned)CheckMode>6) DoExit (21, argv[I][1]);
    break;

   case 'o': {
    PCTSTR p=argv[I]+2;
    if (!*p) {
     if (I==argc-1 || !isdigit(argv[I+1][0])) ResetMode=true;
     else p=argv[++I];
    }
    if (!ResetMode) ResetMode = _ttoi(p);
    if ((unsigned)ResetMode>1) DoExit(21, argv[I][1]);
   }break;
	/* 27.01.1998 AM */
	/* Parameter 'x' für absolute Angabe der */
	/* zu verwendenden Portadresse. Nötig für */
	/* Workaround unter WINDOWS-NT mit */
	/* Freischalt-Devicetreiber. */
   case 'p':
   case 'x':
    if (used_p_x_flg) DoExit(36);
    used_p_x_flg = true;
    PPort = (WORD)_tcstoul(MORE,NULL,16);
	
    if (iodevice < IOLOGICAL) {	// richtige Portadresse
     if (PPort<=9) {	// muss 1..3 (Windows: 1..9) sein
#ifdef WIN32
      if (PPort) PPort-=1;	// unter Windows 1-basiert, unter Linux 0-basiert
#endif
     }else if (PPort < 0x100) {	// 0..FF = onboard, 100..3FF = ISA, 1000..FFFF = PCI
      DoExit(PPort<=9 ? 24 : 35, PPort);	// keine Onboard-Adressen zulassen!
     }
    }else if (PPort>9) DoExit(23,PPort);	// logische Gerätenummer (0..9)
    break;			// 0-basiert auch unter Windows
     
   case 'd':
    iodevice = _ttoi(MORE);
    if ((unsigned)iodevice>USB2LPT) {debug=true; iodevice-=USB2LPT+1;}
    if ((unsigned)iodevice>USB2LPT) DoExit(21, argv[I][1]);
    break;
	
   case 'f':		/* Filetype forcieren */
    FileType = _ttoi(MORE) + 1;
    break;

   case 'e':		/* EPROM-Adresse festlegen		*/
    LoadOffset = _tcstol(MORE,NULL,16);
    ess=true;
    break;

   case 'i': {		// Einzelbyte in Puffer setzen
    TCHAR *p;
    long o = LoadOffset;
    for (p=MORE; I<argc && *p; p=argv[++I]) {
     TCHAR *q;
     BYTE b = (BYTE)_tcstoul(p,&q,16);
     if (q==p) break;	// Ende der Byte-Kette erkannt
     if ((unsigned long)o >= (unsigned long)TargetSize) DoExit(32);
     buffer[o] = b;
     AddArea(o,o+1);
     o++;
    }
    --I;
   }break;
    
   case 'v':		/* Verbose für detaillierte Ausgabe	*/
    Verbose=true;
    break;

   case 'w':		// Ausgabedateiname festlegen
    OutName = MORE;
    break;
     
   case 'l':		// Datenlänge festlegen
    TargetSize = _tcstoul(MORE,NULL,16);
    tss = true;
    break;

   case 'u': {
    int Unit = _ttoi(MORE) - 1;
    if ((unsigned)Unit > 3) DoExit(21,'u');
    Cen_Clk = 0x10 << Unit;
   }break;

   default:
    DoExit(25,argv[I]);

  }else{	// keine Option, sondern Eingabe-Dateiname ODER Hex-Bytes
   if (ess && tss && !OutName) {
    BYTE fill_list[64];
    int a,i,fill_len=0;
    while (argv[I]) fill_list[fill_len++]=(BYTE)_tcstoul(argv[I++],NULL,16);
    for(a=i=0; i<TargetSize; i++, a+=fill_len) AddBytes(fill_list,a,fill_len);
   }else{
    InName=argv[I];
/*--------------------------------------------------------------*\
 * Dateibehandlung
 *--------------------------------------------------------------*/
    InFile = _tfopen(InName,T("rb"));
    if (!InFile) DoExit(28, InName); /* PC -1 Amiga 0 im Fehlerfall */
  
    if (FileType==AUTOTYPE) {
     DWORD Hunk_Type;
     if (fread(&Hunk_Type,1,4,InFile)<4) FileType=BINARY;	// sehr kurze Datei!
     else if (Hunk_Type==0x000003F3) FileType=AMIGAOBJ;		// AMIGA ObjectModul
     else if (_tcsstr(InName,T(".hex"))) FileType=INTEL_HEX;
     else if (_tcsstr(InName,T(".shx"))) FileType=MOTOROLA_S;
     else FileType=BINARY;
     fseek(InFile,0,SEEK_SET);
    }

    switch (FileType) {
     case AMIGAOBJ: LoadObjModule(); break;
     case BINARY: LoadBinary(); break;
     case INTEL_HEX: LoadIntel(); break;
     case MOTOROLA_S: LoadMotorola(); break;
     default:;
    }
   }
  }
 }
// Wenn Eingabedatei ODER Ausgabedatei (aber nicht beides) oder Spielerei: PEPS initialisieren!
 if ((areas && !OutName) || (!areas && OutName) || ResetMode) {
  CHECK_FLAGS check = CHECK_SRG;
  if (areas && !OutName) check|=CHECK_RAM;
  if (!tss && !areas && OutName) check|=CHECK_SIZE;
  ResolveLogicalPortAddress();
  iodeviceInit();
  InitPeps3(check);
  if (!tss) TargetSize=GetPepsSize();
 }

 if (OutName) {
  OutFile = _tfopen (OutName,T("wb"));
  if (!OutFile) DoExit (26, OutName);
 }

// Zeit für Datentransfer nehmen
 Tic = GetTickCount();

// Entweder PEPS-RAM schreiben
 if (areas && !OutName) {
  MEMAREA *p;			// Einfach verkettete Liste (EVL) durchgehen
  for (p=areas; p; p=p->next) {	// diese ist bereits nach Adressen sortiert
   long len=p->e-p->a;
   rprintf(53,p->a,len);
   SendData(buffer+p->a,len,p->a);
   if (CheckMode&(BYTETEST|READBACK)) {
    rprintf(40);		// "Überprüfung ... "
    if (CheckMode&READBACK) ReadData(buffer+p->a,len,p->a,true);	// komplett
    else{				// Stichproben
     int probe[STICHPROBEN],i,fill=0;
     probe[fill++]=0;			// erstes Byte einbeziehen 	
     if (len>1) probe[fill++]=len-1;	// letztes Byte einbeziehen
     while (fill<STICHPROBEN && fill<len>>1) {
      int o;
      again: o=MulDiv(rand(),len,RAND_MAX);	// zufällige Bytes einbeziehen
      for(i=0;i<fill;i++) if (probe[i]==o) goto again;
      probe[fill++]=o;
     }
     qsort(probe,fill,sizeof(int),cmpint);	// sortieren wegen Adresszähler
     for(i=0; i<fill; i++) {
      BYTE v=buffer[p->a+probe[i]];
#ifndef WIN32			// Nur unter Linux:
      int j;			// TODO: geht manchmal schief (beim UsbPrn),
      for (j=0; j<3; j++) {	// Ursache unbekannt
       BYTE b;
       ReadData(&b,1,p->a+probe[i],false);
       if (b==v) goto raus;
      }
#endif
      ReadData(&v,1,p->a+probe[i],true);	// beim 4. Mal muss es aber klappen!
     }
#ifndef WIN32
raus:;
#endif
    }
   }
   Transferred+=len;
   rprintf(54);
   newline();
  }
 }
// …oder PEPS-RAM auslesen
#ifdef PEPSIII
 if (OutName && !areas) {
  rprintf(33,LoadOffset,TargetSize);
  ReadData(buffer, TargetSize, LoadOffset,false);
  Transferred += (DWORD)fwrite(buffer,1,TargetSize,OutFile);
  rprintf(54);
  newline();
 }
#endif
// …oder Datei-Datei-Transfer (HEX->BIN oder SHX->BIN)
 if (areas && OutName) {
  MEMAREA *p;
  int i;
  for (p=areas,i=1; p->next; p=p->next,i++);	// letztes Element suchen
  if (i>1) {rprintf(8,i); newline();}		// "Mehrere Bereiche!"
  fwrite(buffer+areas->a,1,p->e-areas->a,OutFile);	// Diese Bytes nicht mitzählen (bei <Transferred>)
 }

 if (Transferred) {
  Toc = GetTickCount()-Tic;	// Vergangene Millisekunden
  if (!Toc) Toc++;		// mindestens 1 ms
  Size = (Toc+500)/1000;	// Sekunden (gerundet)
  used_p_x_flg=false;
  I = Transferred/Toc;		// kByte/s
  if (I<10 && Size) {I = Transferred/Size; used_p_x_flg=true;}
  rprintf(29, Transferred, Size, I, used_p_x_flg ? T("Byte/s") : T("kB/s"));
  newline();
 }

 if (GetPepsSize()) {	// wenn PEPS initialisiert…
  if (ResetMode) {
   ManualReset();	// interaktiv bleiben
  }else{
   Simulate();		// !RESET freigeben
  }
 }
 
 DoExit(0);
 return 0;		// Toter Kode, stellt "gcc-4.4 -Wall" ruhig
}

Detected encoding: UTF-80