testpico: more on timer reload
[megadrive.git] / hexed / pc_transfer.c
CommitLineData
f28eaaad 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 */
76ba9df5 27#include <stdio.h>
28#include <stdlib.h>
8689c962 29#include <string.h>
76ba9df5 30#include <unistd.h>
31#include <sys/io.h>
32#include <signal.h>
8689c962 33#include <sys/time.h>
4e6ba16d 34#include <zlib.h>
8689c962 35
36#include "transfer.h"
76ba9df5 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
8689c962 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
76ba9df5 67 */
68
8689c962 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
93c5aa8a 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
82static void do_exit(const char *msg, const char *where)
76ba9df5 83{
84 /* switch TL back to high */
8689c962 85 outb(0xe0, PORT_CONTROL);
86
93c5aa8a 87 if (where)
88 fprintf(stderr, "%s: ", where);
8689c962 89 if (msg)
93c5aa8a 90 fprintf(stderr, "%s", msg);
76ba9df5 91 exit(1);
92}
93
8689c962 94static void inthandler(int u)
76ba9df5 95{
93c5aa8a 96 do_exit("\n", NULL);
8689c962 97}
76ba9df5 98
93c5aa8a 99static void wait_th_low(const char *where)
8689c962 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)
93c5aa8a 108 do_exit("timeout waiting TH low\n", where);
76ba9df5 109 }
8689c962 110}
76ba9df5 111
93c5aa8a 112static void wait_th_high(const char *where)
8689c962 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)
93c5aa8a 121 do_exit("timeout waiting TH high\n", where);
76ba9df5 122 }
8689c962 123}
124
93c5aa8a 125static 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
133static void input_to_output(void)
134{
135 wait_th_low("input_to_output");
136 outb(0xc0, PORT_CONTROL); /* TL high, out mode */
137}
138
8689c962 139static unsigned int recv_byte(void)
140{
141 unsigned int byte;
142
143 outb(0xe2, PORT_CONTROL); /* TL low */
144
93c5aa8a 145 wait_th_low("recv_byte");
8689c962 146
147 byte = inb(PORT_DATA) & 0x0f;
148
149 outb(0xe0, PORT_CONTROL); /* TL high */
150
93c5aa8a 151 wait_th_high("recv_byte");
8689c962 152
153 byte |= inb(PORT_DATA) << 4;
154
155 return byte;
156}
157
93c5aa8a 158static void recv_bytes(unsigned char *b, size_t count)
159{
160 while (count-- > 0)
161 *b++ = recv_byte();
162}
163
8689c962 164static void send_byte(unsigned int byte)
165{
93c5aa8a 166 wait_th_low("recv_bytes");
8689c962 167
168 outb(byte & 0x0f, PORT_DATA);
169 outb(0xc2, PORT_CONTROL); /* TL low */
170
93c5aa8a 171 wait_th_high("recv_bytes");
8689c962 172
173 outb((byte >> 4) & 0x0f, PORT_DATA);
174 outb(0xc0, PORT_CONTROL); /* TL high */
175}
176
93c5aa8a 177static void send_bytes(unsigned char *b, size_t count)
178{
179 while (count-- > 0)
180 send_byte(*b++);
181}
182
8689c962 183static void send_cmd(unsigned int cmd)
184{
185 send_byte(CMD_PREFIX);
186 send_byte(cmd);
187}
188
189static void usage(const char *argv0)
190{
191 fprintf(stderr, "usage:\n%s <cmd> [args]\n"
192 "\tsend <file> <addr> [size]\n"
272bd2ec 193 "\trecv <file> <addr> <size>\n"
4e6ba16d 194 "\tjump <addr>\n"
195 "\tio {r{8,16,32} <addr>,w{8,16,32} <addr> <data>}*\n"
f0e06736 196 "\tloadstate <picodrive_savestate>\n"
197 "\trecvvram <file>\n", argv0);
8689c962 198 exit(1);
199}
200
201static unsigned int atoi_or_die(const char *a)
202{
203 char *p = NULL;
204 unsigned int i;
76ba9df5 205
8689c962 206 i = strtoul(a, &p, 0);
76ba9df5 207 if (p == NULL || *p != 0) {
8689c962 208 fprintf(stderr, "atoi: can't convert: %s\n", a);
209 exit(1);
76ba9df5 210 }
211
8689c962 212 return i;
213}
214
4e6ba16d 215static 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
8689c962 225int main(int argc, char *argv[])
226{
93c5aa8a 227 unsigned int addr = 0, size = 0;
228 unsigned int count = 0, i = 0;
8689c962 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);
76ba9df5 237 if (data == NULL) {
8689c962 238 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
76ba9df5 239 return 1;
240 }
241
8689c962 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 }
272bd2ec 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 }
93c5aa8a 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 }
4e6ba16d 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 }
f0e06736 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 }
8689c962 422 else
423 usage(argv[0]);
424
425 ret = ioperm(PORT_DATA, 3, 1);
76ba9df5 426 if (ret != 0) {
427 perror("ioperm");
428 return 1;
429 }
430
431 signal(SIGINT, inthandler);
432
8689c962 433 printf("regs: %02x %02x %02x\n",
434 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
f0e06736 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
8689c962 442 outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */
76ba9df5 443
f0e06736 444 /* wait for request ack */
8689c962 445 if (inb(PORT_STATUS) & 0x40)
446 printf("waiting for TH low..\n");
f0e06736 447 for (i = 10000; inb(PORT_STATUS) & 0x40; i += 100) {
448 if (i > 100000)
449 i = 100000;
450 usleep(i);
451 }
8689c962 452
453 outb(0xe0, PORT_CONTROL);
76ba9df5 454
8689c962 455 if (strcmp(argv[1], "send") == 0)
76ba9df5 456 {
f0e06736 457 send_cmd(CMD_PC_SEND);
8689c962 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
76ba9df5 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
8689c962 473 send_byte(data[i]);
76ba9df5 474 }
76ba9df5 475 }
8689c962 476 else if (strcmp(argv[1], "recv") == 0)
76ba9df5 477 {
f0e06736 478 send_cmd(CMD_PC_RECV);
8689c962 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);
93c5aa8a 485 output_to_input();
76ba9df5 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
8689c962 495 data[i] = recv_byte();
76ba9df5 496 }
8689c962 497
498 fwrite(data, 1, size, file);
76ba9df5 499 }
272bd2ec 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 }
93c5aa8a 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 }
4e6ba16d 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 }
f0e06736 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 }
272bd2ec 592
4e6ba16d 593 if (size != 0) {
272bd2ec 594 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
595 printf("%06x/%06x\n", i, size);
272bd2ec 596 }
4e6ba16d 597 if (file != NULL)
598 fclose(file);
76ba9df5 599
8689c962 600 /* switch TL back to high, disable outputs */
601 outb(0xe0, PORT_CONTROL);
602
76ba9df5 603 return 0;
604}
605