testpico: more on timer reload
[megadrive.git] / hexed / pc_transfer.c
1 /*
2  * Copyright (c) 2011, GraÅžvydas Ignotas
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the organization nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/io.h>
32 #include <signal.h>
33 #include <sys/time.h>
34 #include <zlib.h>
35
36 #include "transfer.h"
37
38 /*
39  * PC:
40  * BASE+0 ~ data
41  * BASE+1 ~ status:  BAOSEI??
42  *          /BUSY, ACK, PE, SLCT, ERROR, IRQ
43  * BASE+2 ~ control: ??MISIAS
44  *          bidirrectMODE, IRQ_EN, /SLCT_IN, INIT, /AUTO_FD_XT, /STROBE
45  *
46  * SEGA
47  * ?HRL3210
48  * TH, TR, TL, D3, D2, D1, D0
49  *
50  * SEGA      PC
51  * 1 D0  <->  2 D0
52  * 2 D1  <->  3 D1
53  * 3 D2  <->  4 D2
54  * 4 D3  <->  5 D3
55  * 5 +5V
56  * 6 TL  <-- 14 /AUTO_FD_XT
57  * 7 TH  --> 10 ACK
58  * 8 GND --- 21 GND
59  * 9 TR  <-- 17 /SLCT_IN
60  *
61  * start: TH low/high, TL high
62  *
63  * TH low  - lower nibble: MD ready to recv | MD sent to PC
64  * TL low  - lower niblle: sent to MD       | ready to recv from MD
65  * TH high - upper nibble: MD ready to recv | MD sent to PC
66  * TL high - upper nibble: sent             | ready to recv from MD
67  */
68
69 #define ACK_TIMEOUT     2000000
70
71 #define PORT_DATA       888
72 #define PORT_STATUS     889
73 #define PORT_CONTROL    890
74
75 #define timediff(now, start) \
76         ((now.tv_sec - start.tv_sec) * 1000000 + now.tv_usec - start.tv_usec)
77
78 #define PBE2(p) ((*(p) << 8) | *(p+1))
79 #define PBE3(p) ((*(p) << 16) | (*(p + 1) << 8) | *(p + 2))
80 #define PBE4(p) ((*(p) << 24) | (*(p + 1) << 16) | (*(p + 2) << 8) | *(p + 3))
81
82 static void do_exit(const char *msg, const char *where)
83 {
84         /* switch TL back to high */
85         outb(0xe0, PORT_CONTROL);
86
87         if (where)
88                 fprintf(stderr, "%s: ", where);
89         if (msg)
90                 fprintf(stderr, "%s", msg);
91         exit(1);
92 }
93
94 static void inthandler(int u)
95 {
96         do_exit("\n", NULL);
97 }
98
99 static void wait_th_low(const char *where)
100 {
101         struct timeval start, now;
102
103         gettimeofday(&start, NULL);
104
105         while (inb(PORT_STATUS) & 0x40) {
106                 gettimeofday(&now, NULL);
107                 if (timediff(now, start) > ACK_TIMEOUT)
108                         do_exit("timeout waiting TH low\n", where);
109         }
110 }
111
112 static void wait_th_high(const char *where)
113 {
114         struct timeval start, now;
115
116         gettimeofday(&start, NULL);
117
118         while (!(inb(PORT_STATUS) & 0x40)) {
119                 gettimeofday(&now, NULL);
120                 if (timediff(now, start) > ACK_TIMEOUT)
121                         do_exit("timeout waiting TH high\n", where);
122         }
123 }
124
125 static void output_to_input(void)
126 {
127         /* TL high, recv mode; also give time
128          * MD to see TL before we lower it in recv_byte */
129         outb(0xe0, PORT_CONTROL);
130         usleep(4*10);                   /* must be at least 12+8+8 M68k cycles, 28/7.67M */
131 }
132
133 static void input_to_output(void)
134 {
135         wait_th_low("input_to_output");
136         outb(0xc0, PORT_CONTROL);       /* TL high, out mode */
137 }
138
139 static unsigned int recv_byte(void)
140 {
141         unsigned int byte;
142
143         outb(0xe2, PORT_CONTROL);       /* TL low */
144
145         wait_th_low("recv_byte");
146
147         byte = inb(PORT_DATA) & 0x0f;
148
149         outb(0xe0, PORT_CONTROL);       /* TL high */
150
151         wait_th_high("recv_byte");
152
153         byte |= inb(PORT_DATA) << 4;
154
155         return byte;
156 }
157
158 static void recv_bytes(unsigned char *b, size_t count)
159 {
160         while (count-- > 0)
161                 *b++ = recv_byte();
162 }
163
164 static void send_byte(unsigned int byte)
165 {
166         wait_th_low("recv_bytes");
167
168         outb(byte & 0x0f, PORT_DATA);
169         outb(0xc2, PORT_CONTROL);       /* TL low */
170
171         wait_th_high("recv_bytes");
172
173         outb((byte >> 4) & 0x0f, PORT_DATA);
174         outb(0xc0, PORT_CONTROL);       /* TL high */
175 }
176
177 static void send_bytes(unsigned char *b, size_t count)
178 {
179         while (count-- > 0)
180                 send_byte(*b++);
181 }
182
183 static void send_cmd(unsigned int cmd)
184 {
185         send_byte(CMD_PREFIX);
186         send_byte(cmd);
187 }
188
189 static void usage(const char *argv0)
190 {
191         fprintf(stderr, "usage:\n%s <cmd> [args]\n"
192                 "\tsend <file> <addr> [size]\n"
193                 "\trecv <file> <addr> <size>\n"
194                 "\tjump <addr>\n"
195                 "\tio {r{8,16,32} <addr>,w{8,16,32} <addr> <data>}*\n"
196                 "\tloadstate <picodrive_savestate>\n"
197                 "\trecvvram <file>\n", argv0);
198         exit(1);
199 }
200
201 static unsigned int atoi_or_die(const char *a)
202 {
203         char *p = NULL;
204         unsigned int i;
205
206         i = strtoul(a, &p, 0);
207         if (p == NULL || *p != 0) {
208                 fprintf(stderr, "atoi: can't convert: %s\n", a);
209                 exit(1);
210         }
211
212         return i;
213 }
214
215 static void checked_gzread(gzFile f, void *data, size_t size)
216 {
217         unsigned int ret;
218         ret = gzread(f, data, size);
219         if (ret != size) {
220                 fprintf(stderr, "gzread returned %d/%zu\n", ret, size);
221                 exit(1);
222         }
223 }
224
225 int main(int argc, char *argv[])
226 {
227         unsigned int addr = 0, size = 0;
228         unsigned int count = 0, i = 0;
229         int ret;
230         unsigned char *data;
231         FILE *file = NULL;
232
233         if (argc < 2)
234                 usage(argv[0]);
235
236         data = malloc(0x1000000);
237         if (data == NULL) {
238                 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
239                 return 1;
240         }
241
242         /* parse args, read files.. */
243         if (strcmp(argv[1], "send") == 0)
244         {
245                 if (argc != 4 && argc != 5)
246                         usage(argv[0]);
247
248                 file = fopen(argv[2], "rb");
249                 if (file == NULL) {
250                         fprintf(stderr, "can't open file: %s\n", argv[2]);
251                         return 1;
252                 }
253
254                 addr = atoi_or_die(argv[3]);
255                 if (argv[4] == NULL) {
256                         fseek(file, 0, SEEK_END);
257                         size = ftell(file);
258                         fseek(file, 0, SEEK_SET);
259                 }
260                 else
261                         size = atoi_or_die(argv[4]);
262
263                 ret = fread(data, 1, size, file);
264                 if (ret != size) {
265                         fprintf(stderr, "fread returned %d/%d\n", ret, size);
266                         perror(NULL);
267                         return 1;
268                 }
269         }
270         else if (strcmp(argv[1], "recv") == 0)
271         {
272                 if (argc != 5)
273                         usage(argv[0]);
274
275                 file = fopen(argv[2], "wb");
276                 if (file == NULL) {
277                         fprintf(stderr, "can't open file: %s\n", argv[2]);
278                         return 1;
279                 }
280
281                 addr = atoi_or_die(argv[3]);
282                 size = atoi_or_die(argv[4]);
283
284                 memset(data, 0, size);
285         }
286         else if (strcmp(argv[1], "jump") == 0)
287         {
288                 if (argc != 3)
289                         usage(argv[0]);
290
291                 addr = atoi_or_die(argv[2]);
292         }
293         else if (strcmp(argv[1], "io") == 0)
294         {
295                 unsigned int cmd = 0, value, iosize;
296                 unsigned char *p = data;
297
298                 for (i = 2; i < argc; ) {
299                         if (argv[i][0] == 'r')
300                                 cmd = IOSEQ_R8;
301                         else if (argv[i][0] == 'w')
302                                 cmd = IOSEQ_W8;
303                         else
304                                 usage(argv[0]);
305
306                         iosize = atoi_or_die(&argv[i][1]);
307                         if (iosize == 32)
308                                 cmd += 2;
309                         else if (iosize == 16)
310                                 cmd += 1;
311                         else if (iosize != 8)
312                                 usage(argv[0]);
313                         *p++ = cmd;
314                         i++;
315
316                         addr = atoi_or_die(argv[i]);
317                         *p++ = addr >> 16;
318                         *p++ = addr >> 8;
319                         *p++ = addr >> 0;
320                         i++;
321
322                         if (cmd == IOSEQ_W8 || cmd == IOSEQ_W16 || cmd == IOSEQ_W32) {
323                                 value = atoi_or_die(argv[i]);
324                                 switch (iosize) {
325                                 case 32:
326                                         *p++ = value >> 24;
327                                         *p++ = value >> 16;
328                                 case 16:
329                                         *p++ = value >> 8;
330                                 case 8:
331                                         *p++ = value >> 0;
332                                 }
333                                 i++;
334                         }
335
336                         count++;
337                 }
338         }
339         else if (strcmp(argv[1], "loadstate") == 0)
340         {
341                 unsigned char chunk;
342                 char header[12];
343                 gzFile f;
344                 int len;
345
346                 if (argc != 3)
347                         usage(argv[0]);
348
349                 f = gzopen(argv[2], "rb");
350                 if (f == NULL) {
351                         perror("gzopen");
352                         return 1;
353                 }
354
355                 checked_gzread(f, header, sizeof(header));
356                 if (strncmp(header, "PicoSEXT", 8) != 0) {
357                         fprintf(stderr, "bad header\n");
358                         return 1;
359                 }
360
361                 while (!gzeof(file))
362                 {
363                         ret = gzread(f, &chunk, 1);
364                         if (ret == 0)
365                                 break;
366                         checked_gzread(f, &len, 4);
367                         //printf("%2d %x\n", chunk, len);
368                         switch (chunk) {
369                         case 3: // VRAM
370                                 checked_gzread(f, data, len);
371                                 size += len;
372                                 break;
373                         case 5: // CRAM
374                                 checked_gzread(f, data + 0x10000, len);
375                                 size += len;
376                                 break;
377                         case 6: // VSRAM
378                                 checked_gzread(f, data + 0x10080, len);
379                                 size += len;
380                                 break;
381                         case 8: // video
382                                 checked_gzread(f, data + 0x10100, len);
383                                 data[size+0] &= ~1;   // no display disable
384                                 data[size+1] |= 0x40; // no blanking
385                                 size += 0x20;
386                                 break;
387                         default:
388                                 if (chunk > 64+8) {
389                                         fprintf(stderr, "bad chunk: %d\n", chunk);
390                                         return 1;
391                                 }
392                                 gzseek(f, len, SEEK_CUR);
393                                 break;
394                         }
395                 }
396                 gzclose(f);
397                 if (size != 0x10120) {
398                         fprintf(stderr, "bad final size: %x\n", size);
399                         return 1;
400                 }
401                 // unbyteswap *RAMs (stored byteswapped)
402                 for (i = 0; i < 0x10100; i += 2) {
403                         int tmp = data[i];
404                         data[i] = data[i + 1];
405                         data[i + 1] = tmp;
406                 }
407         }
408         else if (strcmp(argv[1], "recvvram") == 0)
409         {
410                 if (argc != 3)
411                         usage(argv[0]);
412
413                 file = fopen(argv[2], "wb");
414                 if (file == NULL) {
415                         fprintf(stderr, "can't open file: %s\n", argv[2]);
416                         return 1;
417                 }
418
419                 size = 0x10000;
420                 memset(data, 0, size);
421         }
422         else
423                 usage(argv[0]);
424
425         ret = ioperm(PORT_DATA, 3, 1);
426         if (ret != 0) {
427                 perror("ioperm");
428                 return 1;
429         }
430
431         signal(SIGINT, inthandler);
432
433         printf("regs: %02x %02x %02x\n",
434                 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
435
436         /* wait for start condition */
437         if (!(inb(PORT_STATUS) & 0x40))
438                 printf("waiting for TH high..\n");
439         while (!(inb(PORT_STATUS) & 0x40))
440                 usleep(10000);
441
442         outb(0xe8, PORT_CONTROL);       /* TR low - request for transfer */
443
444         /* wait for request ack */
445         if (inb(PORT_STATUS) & 0x40)
446                 printf("waiting for TH low..\n");
447         for (i = 10000; inb(PORT_STATUS) & 0x40; i += 100) {
448                 if (i > 100000)
449                         i = 100000;
450                 usleep(i);
451         }
452
453         outb(0xe0, PORT_CONTROL);
454
455         if (strcmp(argv[1], "send") == 0)
456         {
457                 send_cmd(CMD_PC_SEND);
458                 send_byte((addr >> 16) & 0xff);
459                 send_byte((addr >>  8) & 0xff);
460                 send_byte((addr >>  0) & 0xff);
461                 send_byte((size >> 16) & 0xff);
462                 send_byte((size >>  8) & 0xff);
463                 send_byte((size >>  0) & 0xff);
464
465                 for (i = 0; i < size; i++)
466                 {
467                         if ((i & 0xff) == 0) {
468                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
469                                 printf("%06x/%06x", i, size);
470                                 fflush(stdout);
471                         }
472
473                         send_byte(data[i]);
474                 }
475         }
476         else if (strcmp(argv[1], "recv") == 0)
477         {
478                 send_cmd(CMD_PC_RECV);
479                 send_byte((addr >> 16) & 0xff);
480                 send_byte((addr >>  8) & 0xff);
481                 send_byte((addr >>  0) & 0xff);
482                 send_byte((size >> 16) & 0xff);
483                 send_byte((size >>  8) & 0xff);
484                 send_byte((size >>  0) & 0xff);
485                 output_to_input();
486
487                 for (i = 0; i < size; i++)
488                 {
489                         if ((i & 0xff) == 0) {
490                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
491                                 printf("%06x/%06x", i, size);
492                                 fflush(stdout);
493                         }
494
495                         data[i] = recv_byte();
496                 }
497
498                 fwrite(data, 1, size, file);
499         }
500         else if (strcmp(argv[1], "jump") == 0)
501         {
502                 send_cmd(CMD_JUMP);
503                 send_byte((addr >> 16) & 0xff);
504                 send_byte((addr >>  8) & 0xff);
505                 send_byte((addr >>  0) & 0xff);
506         }
507         else if (strcmp(argv[1], "io") == 0)
508         {
509                 unsigned char *p = data;
510                 unsigned char rdata[4];
511                 send_cmd(CMD_IOSEQ);
512                 send_byte((count >> 8) & 0xff);
513                 send_byte((count >> 0) & 0xff);
514
515                 for (; count > 0; count--) {
516                         input_to_output();
517                         send_bytes(p, 4);       /* cmd + addr */
518
519                         switch (p[0]) {
520                         case IOSEQ_R8:
521                                 output_to_input();
522                                 recv_bytes(rdata, 1);
523                                 printf("r8  %06x       %02x\n", PBE3(p + 1), rdata[0]);
524                                 p += 4;
525                                 break;
526                         case IOSEQ_R16:
527                                 output_to_input();
528                                 recv_bytes(rdata, 2);
529                                 printf("r16 %06x     %04x\n", PBE3(p + 1), PBE2(rdata));
530                                 p += 4;
531                                 break;
532                         case IOSEQ_R32:
533                                 output_to_input();
534                                 recv_bytes(rdata, 4);
535                                 printf("r32 %06x %08x\n", PBE3(p + 1), PBE4(rdata));
536                                 p += 4;
537                                 break;
538                         case IOSEQ_W8:
539                                 send_bytes(&p[4], 1);
540                                 printf("w8  %06x       %02x\n", PBE3(p + 1), p[4]);
541                                 p += 5;
542                                 break;
543                         case IOSEQ_W16:
544                                 send_bytes(&p[4], 2);
545                                 printf("w16 %06x     %04x\n", PBE3(p + 1), PBE2(p + 4));
546                                 p += 6;
547                                 break;
548                         case IOSEQ_W32:
549                                 send_bytes(&p[4], 4);
550                                 printf("w32 %06x %08x\n", PBE3(p + 1), PBE4(p + 4));
551                                 p += 8;
552                                 break;
553                         default:
554                                 do_exit("error in ioseq data\n", NULL);
555                                 break;
556                         }
557                 }
558         }
559         else if (strcmp(argv[1], "loadstate") == 0)
560         {
561                 send_cmd(CMD_LOADSTATE);
562
563                 for (i = 0; i < size; i++)
564                 {
565                         if ((i & 0x1f) == 0) {
566                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
567                                 printf("%06x/%06x", i, size);
568                                 fflush(stdout);
569                         }
570
571                         send_byte(data[i]);
572                 }
573         }
574         else if (strcmp(argv[1], "recvvram") == 0)
575         {
576                 send_cmd(CMD_VRAM_RECV);
577                 output_to_input();
578
579                 for (i = 0; i < size; i++)
580                 {
581                         if ((i & 0xff) == 0) {
582                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
583                                 printf("%06x/%06x", i, size);
584                                 fflush(stdout);
585                         }
586
587                         data[i] = recv_byte();
588                 }
589
590                 fwrite(data, 1, size, file);
591         }
592
593         if (size != 0) {
594                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
595                 printf("%06x/%06x\n", i, size);
596         }
597         if (file != NULL)
598                 fclose(file);
599
600         /* switch TL back to high, disable outputs */
601         outb(0xe0, PORT_CONTROL);
602
603         return 0;
604 }
605