1 /*
2 doctor64.c - Bung Doctor V64 support for uCON64
3
4 Copyright (c) 1999 - 2001 NoisyB
5 Copyright (c) 2015, 2017 - 2018, 2020 dbjh
6
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #ifdef _MSC_VER
26 #pragma warning(push)
27 #pragma warning(disable: 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
28 #endif
29 #include <ctype.h>
30 #ifdef _MSC_VER
31 #pragma warning(pop)
32 #endif
33 #include <stdlib.h>
34 #include <string.h>
35 #include "misc/archive.h"
36 #include "misc/file.h"
37 #include "ucon64.h"
38 #include "ucon64_misc.h"
39 #include "backup/doctor64.h"
40
41
42 #ifdef USE_PARALLEL
43 static st_ucon64_obj_t doctor64_obj[] =
44 {
45 {UCON64_N64, WF_DEFAULT | WF_STOP | WF_NO_ROM}
46 };
47 #endif
48
49 const st_getopt2_t doctor64_usage[] =
50 {
51 {
52 NULL, 0, 0, 0,
53 NULL, "Doctor V64"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
54 NULL
55 },
56 #ifdef USE_PARALLEL
57 {
58 "xv64", 0, 0, UCON64_XV64,
59 NULL, "send/receive ROM to/from Doctor V64; " OPTION_LONG_S "port" OPTARG_S "PORT\n"
60 "receives automatically when ROM does not exist",
61 &doctor64_obj[0]
62 },
63 #endif
64 {NULL, 0, 0, 0, NULL, NULL, NULL}
65 };
66
67
68 #ifdef USE_PARALLEL
69
70 #define SYNC_MAX_CNT 8192
71 #define SYNC_MAX_TRY 32
72 #define SEND_MAX_WAIT 0x300000
73 #define REC_HIGH_NIBBLE 0x80
74 #define REC_LOW_NIBBLE 0x00
75 #define REC_MAX_WAIT SEND_MAX_WAIT
76
77 #define STOCKPPDEV_MSG "WARNING: This will not work with a stock ppdev on PC. See the FAQ, question 55"
78
79
80 static int
parport_write(char src[],size_t len,unsigned short parport)81 parport_write (char src[], size_t len, unsigned short parport)
82 {
83 size_t i;
84
85 for (i = 0; i < len; i++)
86 {
87 int maxwait = SEND_MAX_WAIT;
88
89 if ((inportb (parport + 2) & 1) == 0) // check ~strobe
90 {
91 while (((inportb (parport + 2) & 2) != 0) && maxwait--)
92 ; // wait for
93 if (maxwait <= 0)
94 return 1; // auto feed == 0
95 outportb (parport, src[i]); // write data
96 outportb (parport + 2, 5); // ~strobe = 1
97 }
98 else
99 {
100 while (((inportb (parport + 2) & 2) == 0) && maxwait--)
101 ; // wait for
102 if (maxwait <= 0)
103 return 1; // auto feed == 1
104 outportb (parport, src[i]); // write data
105 outportb (parport + 2, 4); // ~strobe = 0
106 }
107 }
108 return 0;
109 }
110
111
112 static size_t
parport_read(char dest[],size_t len,unsigned short parport)113 parport_read (char dest[], size_t len, unsigned short parport)
114 {
115 size_t i;
116
117 for (i = 0; i < len; i++)
118 {
119 int maxwait = REC_MAX_WAIT;
120 unsigned char c;
121
122 outportb (parport, REC_HIGH_NIBBLE);
123 while (((inportb (parport + 1) & 0x80) == 0) && maxwait--)
124 ; // wait for ~busy=1
125 if (maxwait <= 0)
126 return len - i;
127 c = (inportb (parport + 1) >> 3) & 0x0f; // ~ack, pe, slct, ~error
128
129 outportb (parport, REC_LOW_NIBBLE);
130 maxwait = REC_MAX_WAIT;
131 while (((inportb (parport + 1) & 0x80) != 0) && maxwait--)
132 ; // wait for ~busy=0
133 if (maxwait <= 0)
134 return len - i;
135 c |= (inportb (parport + 1) << 1) & 0xf0; // ~ack, pe, slct, ~error
136
137 dest[i] = c;
138 }
139 outportb (parport, REC_HIGH_NIBBLE);
140 return 0;
141 }
142
143
144 static int
syncHeader(unsigned short baseport)145 syncHeader (unsigned short baseport)
146 {
147 int i = 0;
148
149 outportb (baseport, 0); // data = 00000000
150 outportb (baseport + 2, 4); // ~strobe=0
151 while (i < SYNC_MAX_CNT)
152 {
153 if ((inportb (baseport + 2) & 8) == 0) // wait for select=0
154 {
155 outportb (baseport, 0xaa); // data = 10101010
156 outportb (baseport + 2, 0); // ~strobe=0, ~init=0
157 while (i < SYNC_MAX_CNT)
158 {
159 if ((inportb (baseport + 2) & 8) != 0) // wait for select=1
160 {
161 outportb (baseport + 2, 4); // ~strobe=0
162 while (i < SYNC_MAX_CNT)
163 {
164 if ((inportb (baseport + 2) & 8) == 0) // w for select=0
165 {
166 outportb (baseport, 0x55); // data = 01010101
167 outportb (baseport + 2, 0); // ~strobe=0, ~init=0
168 while (i < SYNC_MAX_CNT)
169 {
170 if ((inportb (baseport + 2) & 8) != 0) // w select=1
171 {
172 outportb (baseport + 2, 4); // ~strobe=0
173 while (i < SYNC_MAX_CNT)
174 {
175 if ((inportb (baseport + 2) & 8) == 0) // select=0
176 return 0;
177 i++;
178 }
179 }
180 i++;
181 }
182 }
183 i++;
184 }
185 }
186 i++;
187 }
188 i++;
189 }
190 i++;
191 }
192 outportb (baseport + 2, 4);
193 return 1;
194 }
195
196
197 static int
initCommunication(unsigned short port)198 initCommunication (unsigned short port)
199 {
200 int i;
201
202 for (i = 0; i < SYNC_MAX_TRY; i++)
203 {
204 if (syncHeader (port) == 0)
205 break;
206 }
207 if (i >= SYNC_MAX_TRY)
208 return -1;
209 return 0;
210 }
211
212
213 static int
checkSync(unsigned short baseport)214 checkSync (unsigned short baseport)
215 {
216 int i, j;
217
218 for (i = 0; i < SYNC_MAX_CNT; i++)
219 {
220 if (((inportb (baseport + 2) & 3) == 3)
221 || ((inportb (baseport + 2) & 3) == 0))
222 {
223 outportb (baseport, 0); // ~strobe, auto feed
224 for (j = 0; j < SYNC_MAX_CNT; j++)
225 {
226 if ((inportb (baseport + 1) & 0x80) == 0) // wait for ~busy=0
227 {
228 return 0;
229 }
230 }
231 return 1;
232 }
233 }
234 return 1;
235 }
236
237
238 static int
sendFilename(unsigned short baseport,char name[])239 sendFilename (unsigned short baseport, char name[])
240 {
241 int i;
242 char *c = strrchr(name, DIR_SEPARATOR), mname[11];
243
244 memset (mname, ' ', 11);
245 if (c == NULL)
246 c = name;
247 else
248 c++;
249 for (i = 0; i < 8 && *c != '.' && *c != '\0'; i++, c++)
250 mname[i] = (char) toupper ((int) *c);
251 if ((c = strrchr(c, '.')) != NULL)
252 {
253 c++;
254 for (i = 8; i < 11 && *c != '\0'; i++, c++)
255 mname[i] = (char) toupper ((int) *c);
256 }
257
258 return parport_write (mname, 11, baseport);
259 }
260
261
262 static int
sendUploadHeader(unsigned short baseport,char name[],int len)263 sendUploadHeader (unsigned short baseport, char name[], int len)
264 {
265 char lenbuffer[4], protocolId[] = "GD6R\1";
266
267 if (parport_write (protocolId, strlen (protocolId), baseport) != 0)
268 return 1;
269
270 lenbuffer[0] = (char) len;
271 lenbuffer[1] = (char) (len >> 8);
272 lenbuffer[2] = (char) (len >> 16);
273 lenbuffer[3] = (char) (len >> 24);
274 if (parport_write (lenbuffer, 4, baseport) != 0)
275 return 1;
276
277 if (sendFilename (baseport, name) != 0)
278 return 1;
279 return 0;
280 }
281
282
283 static int
sendDownloadHeader(unsigned short baseport,int * len)284 sendDownloadHeader (unsigned short baseport, int *len)
285 {
286 char mname[11], protocolId[] = "GD6W";
287 unsigned char recbuffer[15];
288
289 if (parport_write (protocolId, strlen (protocolId), baseport) != 0)
290 return 1;
291 memset (mname, ' ', 11);
292 if (parport_write (mname, 11, baseport) != 0)
293 return 1;
294 if (checkSync (baseport) != 0)
295 return 1;
296
297 if (parport_read ((char *) recbuffer, 1, baseport) != 0)
298 return 1;
299 if (recbuffer[0] != 1)
300 return -1;
301 if (parport_read ((char *) recbuffer, 15, baseport) != 0)
302 return 1;
303 *len = (int) recbuffer[0] |
304 ((int) recbuffer[1] << 8) |
305 ((int) recbuffer[2] << 16) |
306 ((int) recbuffer[3] << 24);
307 return 0;
308 }
309
310
311 int
doctor64_read(const char * filename,unsigned short parport)312 doctor64_read (const char *filename, unsigned short parport)
313 {
314 char buf[MAXBUFSIZE];
315 FILE *fh;
316 int size, bytesreceived = 0;
317 time_t init_time;
318
319 #ifdef USE_PPDEV
320 puts (STOCKPPDEV_MSG);
321 #endif
322
323 parport_print_info ();
324 if (initCommunication (parport) == -1)
325 {
326 fputs (ucon64_msg[PARPORT_ERROR], stderr);
327 exit (1);
328 }
329
330 init_time = time (NULL);
331
332 if (sendDownloadHeader (parport, &size) != 0)
333 {
334 fputs (ucon64_msg[PARPORT_ERROR], stderr);
335 exit (1);
336 }
337 if ((fh = fopen (filename, "wb")) == NULL)
338 {
339 fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
340 exit (1);
341 }
342 printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
343
344 for (;;)
345 {
346 if (parport_read (buf, sizeof buf, parport) != 0)
347 break;
348 bytesreceived += sizeof buf;
349 fwrite (buf, 1, sizeof buf, fh);
350 ucon64_gauge (init_time, bytesreceived, size);
351 }
352 fclose (fh);
353 return 0;
354 }
355
356
357 int
doctor64_write(const char * filename,int start,int len,unsigned short parport)358 doctor64_write (const char *filename, int start, int len, unsigned short parport)
359 {
360 char buf[MAXBUFSIZE];
361 FILE *fh;
362 unsigned int size;
363 size_t bytessent = 0;
364 time_t init_time;
365
366 #ifdef USE_PPDEV
367 puts (STOCKPPDEV_MSG);
368 #endif
369
370 parport_print_info ();
371 size = len - start;
372 if (initCommunication (parport) == -1)
373 {
374 fputs (ucon64_msg[PARPORT_ERROR], stderr);
375 exit (1);
376 }
377 init_time = time (NULL);
378
379 strcpy (buf, filename);
380 if (sendUploadHeader (parport, buf, size) != 0)
381 {
382 fputs (ucon64_msg[PARPORT_ERROR], stderr);
383 exit (1);
384 }
385
386 if ((fh = fopen (filename, "rb")) == NULL)
387 {
388 fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
389 exit (1);
390 }
391
392 printf ("Send: %u Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
393
394 for (;;)
395 {
396 size_t pos;
397
398 if ((pos = fread (buf, 1, sizeof buf, fh)) == 0)
399 break;
400 if (parport_write (buf, pos, parport) != 0)
401 break;
402 bytessent += pos;
403 ucon64_gauge (init_time, bytessent, size);
404 }
405 fclose (fh);
406 return 0;
407 }
408
409 #endif // USE_PARALLEL
410