400 lines
10 KiB
C
400 lines
10 KiB
C
/*
|
|
doctor64.c - Bung Doctor V64 support for uCON64
|
|
|
|
Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "misc/misc.h"
|
|
#include "misc/file.h"
|
|
#ifdef USE_ZLIB
|
|
#include "misc/archive.h"
|
|
#endif
|
|
#include "misc/getopt2.h" // st_getopt2_t
|
|
#include "ucon64.h"
|
|
#include "ucon64_misc.h"
|
|
#include "doctor64.h"
|
|
#include "misc/parallel.h"
|
|
|
|
|
|
const st_getopt2_t doctor64_usage[] =
|
|
{
|
|
{
|
|
NULL, 0, 0, 0,
|
|
NULL, "Doctor V64"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
|
|
NULL
|
|
},
|
|
#ifdef USE_PARALLEL
|
|
{
|
|
"xv64", 0, 0, UCON64_XV64,
|
|
NULL, "send/receive ROM to/from Doctor V64; " OPTION_LONG_S "port=PORT\n"
|
|
"receives automatically when ROM does not exist",
|
|
&ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM]
|
|
},
|
|
#endif
|
|
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
|
};
|
|
|
|
|
|
#ifdef USE_PARALLEL
|
|
|
|
#define SYNC_MAX_CNT 8192
|
|
#define SYNC_MAX_TRY 32
|
|
#define SEND_MAX_WAIT 0x300000
|
|
#define REC_HIGH_NIBBLE 0x80
|
|
#define REC_LOW_NIBBLE 0x00
|
|
#define REC_MAX_WAIT SEND_MAX_WAIT
|
|
|
|
|
|
static int
|
|
parport_write (char src[], int len, unsigned int parport)
|
|
{
|
|
int maxwait, i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
maxwait = SEND_MAX_WAIT;
|
|
if ((inportb ((unsigned short) (parport + 2)) & 1) == 0) // check ~strobe
|
|
{
|
|
while (((inportb ((unsigned short) (parport + 2)) & 2) != 0) && maxwait--)
|
|
; // wait for
|
|
if (maxwait <= 0)
|
|
return 1; // auto feed == 0
|
|
outportb ((unsigned short) parport, src[i]); // write data
|
|
outportb ((unsigned short) (parport + 2), 5); // ~strobe = 1
|
|
}
|
|
else
|
|
{
|
|
while (((inportb ((unsigned short) (parport + 2)) & 2) == 0) && maxwait--)
|
|
; // wait for
|
|
if (maxwait <= 0)
|
|
return 1; // auto feed == 1
|
|
outportb ((unsigned short) parport, src[i]); // write data
|
|
outportb ((unsigned short) (parport + 2), 4); // ~strobe = 0
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
parport_read (char dest[], int len, unsigned int parport)
|
|
{
|
|
int i, maxwait;
|
|
unsigned char c;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
outportb ((unsigned short) parport, REC_HIGH_NIBBLE);
|
|
maxwait = REC_MAX_WAIT;
|
|
while (((inportb ((unsigned short) (parport + 1)) & 0x80) == 0) && maxwait--)
|
|
; // wait for ~busy=1
|
|
if (maxwait <= 0)
|
|
return len - i;
|
|
c = (inportb ((unsigned short) (parport + 1)) >> 3) & 0x0f; // ~ack, pe, slct, ~error
|
|
|
|
outportb ((unsigned short) parport, REC_LOW_NIBBLE);
|
|
maxwait = REC_MAX_WAIT;
|
|
while (((inportb ((unsigned short) (parport + 1)) & 0x80) != 0) && maxwait--)
|
|
; // wait for ~busy=0
|
|
if (maxwait <= 0)
|
|
return len - i;
|
|
c |= (inportb ((unsigned short) (parport + 1)) << 1) & 0xf0; // ~ack, pe, slct, ~error
|
|
|
|
dest[i] = c;
|
|
}
|
|
outportb ((unsigned short) parport, REC_HIGH_NIBBLE);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
syncHeader (unsigned int baseport)
|
|
{
|
|
int i = 0;
|
|
|
|
outportb ((unsigned short) baseport, 0); // data = 00000000
|
|
outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0
|
|
while (i < SYNC_MAX_CNT)
|
|
{
|
|
if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // wait for select=0
|
|
{
|
|
outportb ((unsigned short) (baseport), 0xaa); // data = 10101010
|
|
outportb ((unsigned short) (baseport + 2), 0); // ~strobe=0, ~init=0
|
|
while (i < SYNC_MAX_CNT)
|
|
{
|
|
if ((inportb ((unsigned short) (baseport + 2)) & 8) != 0) // wait for select=1
|
|
{
|
|
outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0
|
|
while (i < SYNC_MAX_CNT)
|
|
{
|
|
if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // w for select=0
|
|
{
|
|
outportb ((unsigned short) baseport, 0x55); // data = 01010101
|
|
outportb ((unsigned short) (baseport + 2), 0); // ~strobe=0, ~init=0
|
|
while (i < SYNC_MAX_CNT)
|
|
{
|
|
if ((inportb ((unsigned short) (baseport + 2)) & 8) != 0) // w select=1
|
|
{
|
|
outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0
|
|
while (i < SYNC_MAX_CNT)
|
|
{
|
|
if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // select=0
|
|
return 0;
|
|
i++;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
outportb ((unsigned short) (baseport + 2), 4);
|
|
return 1;
|
|
}
|
|
|
|
|
|
int
|
|
initCommunication (unsigned int port)
|
|
{
|
|
int i;
|
|
for (i = 0; i < SYNC_MAX_TRY; i++)
|
|
{
|
|
if (syncHeader (port) == 0)
|
|
break;
|
|
}
|
|
if (i >= SYNC_MAX_TRY)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
checkSync (unsigned int baseport)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < SYNC_MAX_CNT; i++)
|
|
{
|
|
if (((inportb ((unsigned short) (baseport + 2)) & 3) == 3)
|
|
|| ((inportb ((unsigned short) (baseport + 2)) & 3) == 0))
|
|
{
|
|
outportb ((unsigned short) baseport, 0); // ~strobe, auto feed
|
|
for (j = 0; j < SYNC_MAX_CNT; j++)
|
|
{
|
|
if ((inportb ((unsigned short) (baseport + 1)) & 0x80) == 0) // wait for ~busy=0
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
int
|
|
sendFilename (unsigned int baseport, char name[])
|
|
{
|
|
int i;
|
|
char *c, mname[12];
|
|
|
|
memset (mname, ' ', 11);
|
|
c = (strrchr (name, FILE_SEPARATOR));
|
|
if (c == NULL)
|
|
{
|
|
c = name;
|
|
}
|
|
else
|
|
{
|
|
c++;
|
|
}
|
|
for (i = 0; i < 8 && *c != '.' && *c != '\0'; i++, c++)
|
|
mname[i] = toupper (*c);
|
|
c = strrchr (c, '.');
|
|
if (c != NULL)
|
|
{
|
|
c++;
|
|
for (i = 8; i < 11 && *c != '\0'; i++, c++)
|
|
mname[i] = toupper (*c);
|
|
}
|
|
|
|
return parport_write (mname, 11, baseport);
|
|
}
|
|
|
|
|
|
int
|
|
sendUploadHeader (unsigned int baseport, char name[], int len)
|
|
{
|
|
char mname[12], lenbuffer[4];
|
|
static char protocolId[] = "GD6R\1";
|
|
|
|
if (parport_write (protocolId, strlen (protocolId), baseport) != 0)
|
|
return 1;
|
|
|
|
lenbuffer[0] = (char) len;
|
|
lenbuffer[1] = (char) (len >> 8);
|
|
lenbuffer[2] = (char) (len >> 16);
|
|
lenbuffer[3] = (char) (len >> 24);
|
|
if (parport_write (lenbuffer, 4, baseport) != 0)
|
|
return 1;
|
|
|
|
memset (mname, ' ', 11);
|
|
if (sendFilename (baseport, name) != 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
sendDownloadHeader (unsigned int baseport, int *len)
|
|
{
|
|
char mname[12];
|
|
static char protocolId[] = "GD6W";
|
|
unsigned char recbuffer[15];
|
|
|
|
if (parport_write (protocolId, strlen (protocolId), baseport) != 0)
|
|
return 1;
|
|
memset (mname, ' ', 11);
|
|
if (parport_write (mname, 11, baseport) != 0)
|
|
return 1;
|
|
if (checkSync (baseport) != 0)
|
|
return 1;
|
|
|
|
if (parport_read ((char *) recbuffer, 1, baseport) != 0)
|
|
return 1;
|
|
if (recbuffer[0] != 1)
|
|
return -1;
|
|
if (parport_read ((char *) recbuffer, 15, baseport) != 0)
|
|
return 1;
|
|
*len = (int) recbuffer[0] |
|
|
((int) recbuffer[1] << 8) |
|
|
((int) recbuffer[2] << 16) |
|
|
((int) recbuffer[3] << 24);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
doctor64_read (const char *filename, unsigned int parport)
|
|
{
|
|
char buf[MAXBUFSIZE];
|
|
FILE *fh;
|
|
int size, inittime, bytesreceived = 0;
|
|
|
|
parport_print_info ();
|
|
if (initCommunication (parport) == -1)
|
|
{
|
|
fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
|
|
exit (1);
|
|
}
|
|
|
|
inittime = time (0);
|
|
|
|
if (sendDownloadHeader (parport, &size) != 0)
|
|
{
|
|
fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
|
|
exit (1);
|
|
}
|
|
if (!(fh = fopen (filename, "wb")))
|
|
{
|
|
fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
|
|
exit (1);
|
|
}
|
|
printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
|
|
|
|
for (;;)
|
|
{
|
|
if (parport_read (buf, sizeof buf, parport) != 0)
|
|
{
|
|
fclose (fh);
|
|
return 0;
|
|
}
|
|
bytesreceived += sizeof buf;
|
|
fwrite (buf, 1, sizeof buf, fh);
|
|
ucon64_gauge (inittime, bytesreceived, size);
|
|
}
|
|
sync ();
|
|
fclose (fh);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
doctor64_write (const char *filename, int start, int len, unsigned int parport)
|
|
{
|
|
char buf[MAXBUFSIZE];
|
|
FILE *fh;
|
|
unsigned int size, inittime, pos, bytessend = 0;
|
|
|
|
parport_print_info ();
|
|
size = len - start;
|
|
if (initCommunication (parport) == -1)
|
|
{
|
|
fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
|
|
exit (1);
|
|
}
|
|
inittime = time (0);
|
|
|
|
strcpy (buf, filename);
|
|
if (sendUploadHeader (parport, buf, size) != 0)
|
|
{
|
|
fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
|
|
exit (1);
|
|
}
|
|
|
|
if (!(fh = fopen (filename, "rb")))
|
|
{
|
|
fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
|
|
exit (1);
|
|
}
|
|
|
|
printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
|
|
|
|
for (;;)
|
|
{
|
|
if (!(pos = fread (buf, 1, sizeof buf, fh)))
|
|
break;
|
|
if (parport_write (buf, pos, parport) != 0)
|
|
break;
|
|
bytessend += sizeof buf;
|
|
ucon64_gauge (inittime, bytessend, size);
|
|
}
|
|
fclose (fh);
|
|
return 0;
|
|
}
|
|
|
|
#endif // USE_PARALLEL
|