2 * Copyright (c) 2011, GraÅžvydas Ignotas
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.
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.
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
48 * TH, TR, TL, D3, D2, D1, D0
56 * 6 TL <-- 14 /AUTO_FD_XT
59 * 9 TR <-- 17 /SLCT_IN
61 * start: TH low/high, TL high
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
69 #define ACK_TIMEOUT 2000000
72 #define PORT_STATUS 889
73 #define PORT_CONTROL 890
75 #define timediff(now, start) \
76 ((now.tv_sec - start.tv_sec) * 1000000 + now.tv_usec - start.tv_usec)
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))
82 static void do_exit(const char *msg, const char *where)
84 /* switch TL back to high */
85 outb(0xe0, PORT_CONTROL);
88 fprintf(stderr, "%s: ", where);
90 fprintf(stderr, "%s", msg);
94 static void inthandler(int u)
99 static void wait_th_low(const char *where)
101 struct timeval start, now;
103 gettimeofday(&start, NULL);
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);
112 static void wait_th_high(const char *where)
114 struct timeval start, now;
116 gettimeofday(&start, NULL);
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);
125 static void output_to_input(void)
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 */
133 static void input_to_output(void)
135 wait_th_low("input_to_output");
136 outb(0xc0, PORT_CONTROL); /* TL high, out mode */
139 static unsigned int recv_byte(void)
143 outb(0xe2, PORT_CONTROL); /* TL low */
145 wait_th_low("recv_byte");
147 byte = inb(PORT_DATA) & 0x0f;
149 outb(0xe0, PORT_CONTROL); /* TL high */
151 wait_th_high("recv_byte");
153 byte |= inb(PORT_DATA) << 4;
158 static void recv_bytes(unsigned char *b, size_t count)
164 static void send_byte(unsigned int byte)
166 wait_th_low("recv_bytes");
168 outb(byte & 0x0f, PORT_DATA);
169 outb(0xc2, PORT_CONTROL); /* TL low */
171 wait_th_high("recv_bytes");
173 outb((byte >> 4) & 0x0f, PORT_DATA);
174 outb(0xc0, PORT_CONTROL); /* TL high */
177 static void send_bytes(unsigned char *b, size_t count)
183 static void send_cmd(unsigned int cmd)
185 send_byte(CMD_PREFIX);
189 static void usage(const char *argv0)
191 fprintf(stderr, "usage:\n%s <cmd> [args]\n"
192 "\tsend <file> <addr> [size]\n"
193 "\trecv <file> <addr> <size>\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);
201 static unsigned int atoi_or_die(const char *a)
206 i = strtoul(a, &p, 0);
207 if (p == NULL || *p != 0) {
208 fprintf(stderr, "atoi: can't convert: %s\n", a);
215 static void checked_gzread(gzFile f, void *data, size_t size)
218 ret = gzread(f, data, size);
220 fprintf(stderr, "gzread returned %d/%zu\n", ret, size);
225 int main(int argc, char *argv[])
227 unsigned int addr = 0, size = 0;
228 unsigned int count = 0, i = 0;
236 data = malloc(0x1000000);
238 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
242 /* parse args, read files.. */
243 if (strcmp(argv[1], "send") == 0)
245 if (argc != 4 && argc != 5)
248 file = fopen(argv[2], "rb");
250 fprintf(stderr, "can't open file: %s\n", argv[2]);
254 addr = atoi_or_die(argv[3]);
255 if (argv[4] == NULL) {
256 fseek(file, 0, SEEK_END);
258 fseek(file, 0, SEEK_SET);
261 size = atoi_or_die(argv[4]);
263 ret = fread(data, 1, size, file);
265 fprintf(stderr, "fread returned %d/%d\n", ret, size);
270 else if (strcmp(argv[1], "recv") == 0)
275 file = fopen(argv[2], "wb");
277 fprintf(stderr, "can't open file: %s\n", argv[2]);
281 addr = atoi_or_die(argv[3]);
282 size = atoi_or_die(argv[4]);
284 memset(data, 0, size);
286 else if (strcmp(argv[1], "jump") == 0)
291 addr = atoi_or_die(argv[2]);
293 else if (strcmp(argv[1], "io") == 0)
295 unsigned int cmd = 0, value, iosize;
296 unsigned char *p = data;
298 for (i = 2; i < argc; ) {
299 if (argv[i][0] == 'r')
301 else if (argv[i][0] == 'w')
306 iosize = atoi_or_die(&argv[i][1]);
309 else if (iosize == 16)
311 else if (iosize != 8)
316 addr = atoi_or_die(argv[i]);
322 if (cmd == IOSEQ_W8 || cmd == IOSEQ_W16 || cmd == IOSEQ_W32) {
323 value = atoi_or_die(argv[i]);
339 else if (strcmp(argv[1], "loadstate") == 0)
349 f = gzopen(argv[2], "rb");
355 checked_gzread(f, header, sizeof(header));
356 if (strncmp(header, "PicoSEXT", 8) != 0) {
357 fprintf(stderr, "bad header\n");
363 ret = gzread(f, &chunk, 1);
366 checked_gzread(f, &len, 4);
367 //printf("%2d %x\n", chunk, len);
370 checked_gzread(f, data, len);
374 checked_gzread(f, data + 0x10000, len);
378 checked_gzread(f, data + 0x10080, len);
382 checked_gzread(f, data + 0x10100, len);
383 data[size+0] &= ~1; // no display disable
384 data[size+1] |= 0x40; // no blanking
389 fprintf(stderr, "bad chunk: %d\n", chunk);
392 gzseek(f, len, SEEK_CUR);
397 if (size != 0x10120) {
398 fprintf(stderr, "bad final size: %x\n", size);
401 // unbyteswap *RAMs (stored byteswapped)
402 for (i = 0; i < 0x10100; i += 2) {
404 data[i] = data[i + 1];
408 else if (strcmp(argv[1], "recvvram") == 0)
413 file = fopen(argv[2], "wb");
415 fprintf(stderr, "can't open file: %s\n", argv[2]);
420 memset(data, 0, size);
425 ret = ioperm(PORT_DATA, 3, 1);
431 signal(SIGINT, inthandler);
433 printf("regs: %02x %02x %02x\n",
434 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
436 /* wait for start condition */
437 if (!(inb(PORT_STATUS) & 0x40))
438 printf("waiting for TH high..\n");
439 while (!(inb(PORT_STATUS) & 0x40))
442 outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */
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) {
453 outb(0xe0, PORT_CONTROL);
455 if (strcmp(argv[1], "send") == 0)
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);
465 for (i = 0; i < size; i++)
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);
476 else if (strcmp(argv[1], "recv") == 0)
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);
487 for (i = 0; i < size; i++)
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);
495 data[i] = recv_byte();
498 fwrite(data, 1, size, file);
500 else if (strcmp(argv[1], "jump") == 0)
503 send_byte((addr >> 16) & 0xff);
504 send_byte((addr >> 8) & 0xff);
505 send_byte((addr >> 0) & 0xff);
507 else if (strcmp(argv[1], "io") == 0)
509 unsigned char *p = data;
510 unsigned char rdata[4];
512 send_byte((count >> 8) & 0xff);
513 send_byte((count >> 0) & 0xff);
515 for (; count > 0; count--) {
517 send_bytes(p, 4); /* cmd + addr */
522 recv_bytes(rdata, 1);
523 printf("r8 %06x %02x\n", PBE3(p + 1), rdata[0]);
528 recv_bytes(rdata, 2);
529 printf("r16 %06x %04x\n", PBE3(p + 1), PBE2(rdata));
534 recv_bytes(rdata, 4);
535 printf("r32 %06x %08x\n", PBE3(p + 1), PBE4(rdata));
539 send_bytes(&p[4], 1);
540 printf("w8 %06x %02x\n", PBE3(p + 1), p[4]);
544 send_bytes(&p[4], 2);
545 printf("w16 %06x %04x\n", PBE3(p + 1), PBE2(p + 4));
549 send_bytes(&p[4], 4);
550 printf("w32 %06x %08x\n", PBE3(p + 1), PBE4(p + 4));
554 do_exit("error in ioseq data\n", NULL);
559 else if (strcmp(argv[1], "loadstate") == 0)
561 send_cmd(CMD_LOADSTATE);
563 for (i = 0; i < size; i++)
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);
574 else if (strcmp(argv[1], "recvvram") == 0)
576 send_cmd(CMD_VRAM_RECV);
579 for (i = 0; i < size; i++)
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);
587 data[i] = recv_byte();
590 fwrite(data, 1, size, file);
594 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
595 printf("%06x/%06x\n", i, size);
600 /* switch TL back to high, disable outputs */
601 outb(0xe0, PORT_CONTROL);