/*--------------------------------------------------------------
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
}
Vorgefundene Kodierung: UTF-8 | 0
|