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