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", argv0);
200 static unsigned int atoi_or_die(const char *a)
205 i = strtoul(a, &p, 0);
206 if (p == NULL || *p != 0) {
207 fprintf(stderr, "atoi: can't convert: %s\n", a);
214 static void checked_gzread(gzFile f, void *data, size_t size)
217 ret = gzread(f, data, size);
219 fprintf(stderr, "gzread returned %d/%zu\n", ret, size);
224 int main(int argc, char *argv[])
226 unsigned int addr = 0, size = 0;
227 unsigned int count = 0, i = 0;
235 data = malloc(0x1000000);
237 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
241 /* parse args, read files.. */
242 if (strcmp(argv[1], "send") == 0)
244 if (argc != 4 && argc != 5)
247 file = fopen(argv[2], "rb");
249 fprintf(stderr, "can't open file: %s\n", argv[2]);
253 addr = atoi_or_die(argv[3]);
254 if (argv[4] == NULL) {
255 fseek(file, 0, SEEK_END);
257 fseek(file, 0, SEEK_SET);
260 size = atoi_or_die(argv[4]);
262 ret = fread(data, 1, size, file);
264 fprintf(stderr, "fread returned %d/%d\n", ret, size);
269 else if (strcmp(argv[1], "recv") == 0)
274 file = fopen(argv[2], "wb");
276 fprintf(stderr, "can't open file: %s\n", argv[2]);
280 addr = atoi_or_die(argv[3]);
281 size = atoi_or_die(argv[4]);
283 memset(data, 0, size);
285 else if (strcmp(argv[1], "jump") == 0)
290 addr = atoi_or_die(argv[2]);
292 else if (strcmp(argv[1], "io") == 0)
294 unsigned int cmd = 0, value, iosize;
295 unsigned char *p = data;
297 for (i = 2; i < argc; ) {
298 if (argv[i][0] == 'r')
300 else if (argv[i][0] == 'w')
305 iosize = atoi_or_die(&argv[i][1]);
308 else if (iosize == 16)
310 else if (iosize != 8)
315 addr = atoi_or_die(argv[i]);
321 if (cmd == IOSEQ_W8 || cmd == IOSEQ_W16 || cmd == IOSEQ_W32) {
322 value = atoi_or_die(argv[i]);
338 else if (strcmp(argv[1], "loadstate") == 0)
348 f = gzopen(argv[2], "rb");
354 checked_gzread(f, header, sizeof(header));
355 if (strncmp(header, "PicoSEXT", 8) != 0) {
356 fprintf(stderr, "bad header\n");
362 ret = gzread(f, &chunk, 1);
365 checked_gzread(f, &len, 4);
366 //printf("%2d %x\n", chunk, len);
369 checked_gzread(f, data, len);
373 checked_gzread(f, data + 0x10000, len);
377 checked_gzread(f, data + 0x10080, len);
381 checked_gzread(f, data + 0x10100, len);
382 data[size+0] &= ~1; // no display disable
383 data[size+1] |= 0x40; // no blanking
388 fprintf(stderr, "bad chunk: %d\n", chunk);
391 gzseek(f, len, SEEK_CUR);
396 if (size != 0x10120) {
397 fprintf(stderr, "bad final size: %x\n", size);
400 // unbyteswap *RAMs (stored byteswapped)
401 for (i = 0; i < 0x10100; i += 2) {
403 data[i] = data[i + 1];
410 ret = ioperm(PORT_DATA, 3, 1);
416 signal(SIGINT, inthandler);
418 printf("regs: %02x %02x %02x\n",
419 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
420 outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */
422 if (inb(PORT_STATUS) & 0x40)
423 printf("waiting for TH low..\n");
424 while (inb(PORT_STATUS) & 0x40)
427 outb(0xe0, PORT_CONTROL);
429 if (strcmp(argv[1], "send") == 0)
431 send_cmd(CMD_MD_SEND);
432 send_byte((addr >> 16) & 0xff);
433 send_byte((addr >> 8) & 0xff);
434 send_byte((addr >> 0) & 0xff);
435 send_byte((size >> 16) & 0xff);
436 send_byte((size >> 8) & 0xff);
437 send_byte((size >> 0) & 0xff);
439 for (i = 0; i < size; i++)
441 if ((i & 0xff) == 0) {
442 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
443 printf("%06x/%06x", i, size);
450 else if (strcmp(argv[1], "recv") == 0)
452 send_cmd(CMD_MD_RECV);
453 send_byte((addr >> 16) & 0xff);
454 send_byte((addr >> 8) & 0xff);
455 send_byte((addr >> 0) & 0xff);
456 send_byte((size >> 16) & 0xff);
457 send_byte((size >> 8) & 0xff);
458 send_byte((size >> 0) & 0xff);
461 for (i = 0; i < size; i++)
463 if ((i & 0xff) == 0) {
464 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
465 printf("%06x/%06x", i, size);
469 data[i] = recv_byte();
472 fwrite(data, 1, size, file);
474 else if (strcmp(argv[1], "jump") == 0)
477 send_byte((addr >> 16) & 0xff);
478 send_byte((addr >> 8) & 0xff);
479 send_byte((addr >> 0) & 0xff);
481 else if (strcmp(argv[1], "io") == 0)
483 unsigned char *p = data;
484 unsigned char rdata[4];
486 send_byte((count >> 8) & 0xff);
487 send_byte((count >> 0) & 0xff);
489 for (; count > 0; count--) {
491 send_bytes(p, 4); /* cmd + addr */
496 recv_bytes(rdata, 1);
497 printf("r8 %06x %02x\n", PBE3(p + 1), rdata[0]);
502 recv_bytes(rdata, 2);
503 printf("r16 %06x %04x\n", PBE3(p + 1), PBE2(rdata));
508 recv_bytes(rdata, 4);
509 printf("r32 %06x %08x\n", PBE3(p + 1), PBE4(rdata));
513 send_bytes(&p[4], 1);
514 printf("w8 %06x %02x\n", PBE3(p + 1), p[4]);
518 send_bytes(&p[4], 2);
519 printf("w16 %06x %04x\n", PBE3(p + 1), PBE2(p + 4));
523 send_bytes(&p[4], 4);
524 printf("w32 %06x %08x\n", PBE3(p + 1), PBE4(p + 4));
528 do_exit("error in ioseq data\n", NULL);
533 else if (strcmp(argv[1], "loadstate") == 0)
535 send_cmd(CMD_LOADSTATE);
537 for (i = 0; i < size; i++)
539 if ((i & 0x1f) == 0) {
540 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
541 printf("%06x/%06x", i, size);
550 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
551 printf("%06x/%06x\n", i, size);
556 /* switch TL back to high, disable outputs */
557 outb(0xe0, PORT_CONTROL);