X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hexed%2Fpc_transfer.c;h=bcfb652a0a06947fb8ab1ca00dbccb59fb259130;hb=8d788f3de66aeda55c2518b5e511111face3a82a;hp=844054fc3f25b6f8d5ab3643456d15537b086e4d;hpb=d13f0e289f5e87d65926c09ba46b72d4e94eaeb3;p=megadrive.git diff --git a/hexed/pc_transfer.c b/hexed/pc_transfer.c index 844054f..bcfb652 100644 --- a/hexed/pc_transfer.c +++ b/hexed/pc_transfer.c @@ -1,8 +1,39 @@ +/* + * Copyright (c) 2011, Gražvydas Ignotas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #include #include +#include #include #include #include +#include +#include + +#include "transfer.h" /* * PC: @@ -26,47 +57,372 @@ * 7 TH --> 10 ACK * 8 GND --- 21 GND * 9 TR <-- 17 /SLCT_IN + * + * start: TH low/high, TL high + * + * TH low - lower nibble: MD ready to recv | MD sent to PC + * TL low - lower niblle: sent to MD | ready to recv from MD + * TH high - upper nibble: MD ready to recv | MD sent to PC + * TL high - upper nibble: sent | ready to recv from MD */ -static void inthandler(int u) +#define ACK_TIMEOUT 2000000 + +#define PORT_DATA 888 +#define PORT_STATUS 889 +#define PORT_CONTROL 890 + +#define timediff(now, start) \ + ((now.tv_sec - start.tv_sec) * 1000000 + now.tv_usec - start.tv_usec) + +#define PBE2(p) ((*(p) << 8) | *(p+1)) +#define PBE3(p) ((*(p) << 16) | (*(p + 1) << 8) | *(p + 2)) +#define PBE4(p) ((*(p) << 24) | (*(p + 1) << 16) | (*(p + 2) << 8) | *(p + 3)) + +static void do_exit(const char *msg, const char *where) { /* switch TL back to high */ - outb(0xe0, 890); - printf("\n"); + outb(0xe0, PORT_CONTROL); + + if (where) + fprintf(stderr, "%s: ", where); + if (msg) + fprintf(stderr, "%s", msg); exit(1); } -int main(int argc, char *argv[]) +static void inthandler(int u) { - int size, byte, ret, i; - unsigned char *data; - char *p = NULL; - FILE *file; + do_exit("\n", NULL); +} - if (argc != 4 || argv[1][0] != '-' || (argv[1][1] != 'r' && argv[1][1] != 'w')) { - printf("usage:\n%s {-r,-w} \n", argv[0]); - return 1; +static void wait_th_low(const char *where) +{ + struct timeval start, now; + + gettimeofday(&start, NULL); + + while (inb(PORT_STATUS) & 0x40) { + gettimeofday(&now, NULL); + if (timediff(now, start) > ACK_TIMEOUT) + do_exit("timeout waiting TH low\n", where); } +} - file = fopen(argv[2], argv[1][1] == 'r' ? "wb" : "rb"); - if (file == NULL) { - fprintf(stderr, "can't open file: %s\n", argv[2]); - return 1; +static void wait_th_high(const char *where) +{ + struct timeval start, now; + + gettimeofday(&start, NULL); + + while (!(inb(PORT_STATUS) & 0x40)) { + gettimeofday(&now, NULL); + if (timediff(now, start) > ACK_TIMEOUT) + do_exit("timeout waiting TH high\n", where); } +} + +static void output_to_input(void) +{ + /* TL high, recv mode; also give time + * MD to see TL before we lower it in recv_byte */ + outb(0xe0, PORT_CONTROL); + usleep(4*10); /* must be at least 12+8+8 M68k cycles, 28/7.67M */ +} + +static void input_to_output(void) +{ + wait_th_low("input_to_output"); + outb(0xc0, PORT_CONTROL); /* TL high, out mode */ +} + +static unsigned int recv_byte(void) +{ + unsigned int byte; + + outb(0xe2, PORT_CONTROL); /* TL low */ + + wait_th_low("recv_byte"); + + byte = inb(PORT_DATA) & 0x0f; + + outb(0xe0, PORT_CONTROL); /* TL high */ + + wait_th_high("recv_byte"); + + byte |= inb(PORT_DATA) << 4; - size = (int)strtoul(argv[3], &p, 16); + return byte; +} + +static void recv_bytes(unsigned char *b, size_t count) +{ + while (count-- > 0) + *b++ = recv_byte(); +} + +static void send_byte(unsigned int byte) +{ + wait_th_low("recv_bytes"); + + outb(byte & 0x0f, PORT_DATA); + outb(0xc2, PORT_CONTROL); /* TL low */ + + wait_th_high("recv_bytes"); + + outb((byte >> 4) & 0x0f, PORT_DATA); + outb(0xc0, PORT_CONTROL); /* TL high */ +} + +static void send_bytes(unsigned char *b, size_t count) +{ + while (count-- > 0) + send_byte(*b++); +} + +static void send_cmd(unsigned int cmd) +{ + send_byte(CMD_PREFIX); + send_byte(cmd); +} + +static void usage(const char *argv0) +{ + fprintf(stderr, "usage:\n%s [args]\n" + "\tsend [size]\n" + "\trecv \n" + "\tjump \n" + "\tio {r{8,16,32} ,w{8,16,32} }*\n" + "\tloadstate \n" + "\trecvvram \n", argv0); + exit(1); +} + +static unsigned int atoi_or_die(const char *a) +{ + char *p = NULL; + unsigned int i; + + i = strtoul(a, &p, 0); if (p == NULL || *p != 0) { - fprintf(stderr, "can't convert size %s\n", argv[3]); - return 1; + fprintf(stderr, "atoi: can't convert: %s\n", a); + exit(1); } - data = malloc(size); + return i; +} + +static void checked_gzread(gzFile f, void *data, size_t size) +{ + unsigned int ret; + ret = gzread(f, data, size); + if (ret != size) { + fprintf(stderr, "gzread returned %d/%zu\n", ret, size); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + unsigned int addr = 0, size = 0; + unsigned int count = 0, i = 0; + int ret; + unsigned char *data; + FILE *file = NULL; + + if (argc < 2) + usage(argv[0]); + + data = malloc(0x1000000); if (data == NULL) { - fprintf(stderr, "can't alloc %d bytes\n", size); + fprintf(stderr, "can't alloc %d bytes\n", 0x1000000); return 1; } - ret = ioperm(888, 3, 1); + /* parse args, read files.. */ + if (strcmp(argv[1], "send") == 0) + { + if (argc != 4 && argc != 5) + usage(argv[0]); + + file = fopen(argv[2], "rb"); + if (file == NULL) { + fprintf(stderr, "can't open file: %s\n", argv[2]); + return 1; + } + + addr = atoi_or_die(argv[3]); + if (argv[4] == NULL) { + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + } + else + size = atoi_or_die(argv[4]); + + ret = fread(data, 1, size, file); + if (ret != size) { + fprintf(stderr, "fread returned %d/%d\n", ret, size); + perror(NULL); + return 1; + } + } + else if (strcmp(argv[1], "recv") == 0) + { + if (argc != 5) + usage(argv[0]); + + file = fopen(argv[2], "wb"); + if (file == NULL) { + fprintf(stderr, "can't open file: %s\n", argv[2]); + return 1; + } + + addr = atoi_or_die(argv[3]); + size = atoi_or_die(argv[4]); + + memset(data, 0, size); + } + else if (strcmp(argv[1], "jump") == 0) + { + if (argc != 3) + usage(argv[0]); + + addr = atoi_or_die(argv[2]); + } + else if (strcmp(argv[1], "io") == 0) + { + unsigned int cmd = 0, value, iosize; + unsigned char *p = data; + + for (i = 2; i < argc; ) { + if (argv[i][0] == 'r') + cmd = IOSEQ_R8; + else if (argv[i][0] == 'w') + cmd = IOSEQ_W8; + else + usage(argv[0]); + + iosize = atoi_or_die(&argv[i][1]); + if (iosize == 32) + cmd += 2; + else if (iosize == 16) + cmd += 1; + else if (iosize != 8) + usage(argv[0]); + *p++ = cmd; + i++; + + addr = atoi_or_die(argv[i]); + *p++ = addr >> 16; + *p++ = addr >> 8; + *p++ = addr >> 0; + i++; + + if (cmd == IOSEQ_W8 || cmd == IOSEQ_W16 || cmd == IOSEQ_W32) { + value = atoi_or_die(argv[i]); + switch (iosize) { + case 32: + *p++ = value >> 24; + *p++ = value >> 16; + case 16: + *p++ = value >> 8; + case 8: + *p++ = value >> 0; + } + i++; + } + + count++; + } + } + else if (strcmp(argv[1], "loadstate") == 0) + { + unsigned char chunk; + char header[12]; + gzFile f; + int len; + + if (argc != 3) + usage(argv[0]); + + f = gzopen(argv[2], "rb"); + if (f == NULL) { + perror("gzopen"); + return 1; + } + + checked_gzread(f, header, sizeof(header)); + if (strncmp(header, "PicoSEXT", 8) != 0) { + fprintf(stderr, "bad header\n"); + return 1; + } + + while (!gzeof(file)) + { + ret = gzread(f, &chunk, 1); + if (ret == 0) + break; + checked_gzread(f, &len, 4); + //printf("%2d %x\n", chunk, len); + switch (chunk) { + case 3: // VRAM + checked_gzread(f, data, len); + size += len; + break; + case 5: // CRAM + checked_gzread(f, data + 0x10000, len); + size += len; + break; + case 6: // VSRAM + checked_gzread(f, data + 0x10080, len); + size += len; + break; + case 8: // video + checked_gzread(f, data + 0x10100, len); + data[size+0] &= ~1; // no display disable + data[size+1] |= 0x40; // no blanking + size += 0x20; + break; + default: + if (chunk > 64+8) { + fprintf(stderr, "bad chunk: %d\n", chunk); + return 1; + } + gzseek(f, len, SEEK_CUR); + break; + } + } + gzclose(f); + if (size != 0x10120) { + fprintf(stderr, "bad final size: %x\n", size); + return 1; + } + // unbyteswap *RAMs (stored byteswapped) + for (i = 0; i < 0x10100; i += 2) { + int tmp = data[i]; + data[i] = data[i + 1]; + data[i + 1] = tmp; + } + } + else if (strcmp(argv[1], "recvvram") == 0) + { + if (argc != 3) + usage(argv[0]); + + file = fopen(argv[2], "wb"); + if (file == NULL) { + fprintf(stderr, "can't open file: %s\n", argv[2]); + return 1; + } + + size = 0x10000; + memset(data, 0, size); + } + else + usage(argv[0]); + + ret = ioperm(PORT_DATA, 3, 1); if (ret != 0) { perror("ioperm"); return 1; @@ -74,16 +430,38 @@ int main(int argc, char *argv[]) signal(SIGINT, inthandler); - printf("regs: %02x %02x %02x\n", inb(888), inb(889), inb(890)); - outb(0xe0, 890); + printf("regs: %02x %02x %02x\n", + inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL)); + + /* wait for start condition */ + if (!(inb(PORT_STATUS) & 0x40)) + printf("waiting for TH high..\n"); + while (!(inb(PORT_STATUS) & 0x40)) + usleep(10000); - while (!(inb(889) & 0x40)) { - printf("waiting for TH..\n"); - sleep(5); + outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */ + + /* wait for request ack */ + if (inb(PORT_STATUS) & 0x40) + printf("waiting for TH low..\n"); + for (i = 10000; inb(PORT_STATUS) & 0x40; i += 100) { + if (i > 100000) + i = 100000; + usleep(i); } - if (argv[1][1] == 'r') + outb(0xe0, PORT_CONTROL); + + if (strcmp(argv[1], "send") == 0) { + send_cmd(CMD_PC_SEND); + send_byte((addr >> 16) & 0xff); + send_byte((addr >> 8) & 0xff); + send_byte((addr >> 0) & 0xff); + send_byte((size >> 16) & 0xff); + send_byte((size >> 8) & 0xff); + send_byte((size >> 0) & 0xff); + for (i = 0; i < size; i++) { if ((i & 0xff) == 0) { @@ -92,60 +470,135 @@ int main(int argc, char *argv[]) fflush(stdout); } - outb(0xe2, 890); /* TL low */ - - /* wait for TH low */ - while (inb(889) & 0x40) ; - - byte = inb(888) & 0x0f; - - outb(0xe0, 890); /* TL high */ + send_byte(data[i]); + } + } + else if (strcmp(argv[1], "recv") == 0) + { + send_cmd(CMD_PC_RECV); + send_byte((addr >> 16) & 0xff); + send_byte((addr >> 8) & 0xff); + send_byte((addr >> 0) & 0xff); + send_byte((size >> 16) & 0xff); + send_byte((size >> 8) & 0xff); + send_byte((size >> 0) & 0xff); + output_to_input(); - /* wait for TH high */ - while (!(inb(889) & 0x40)) ; + for (i = 0; i < size; i++) + { + if ((i & 0xff) == 0) { + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); + printf("%06x/%06x", i, size); + fflush(stdout); + } - byte |= inb(888) << 4; - data[i] = byte; + data[i] = recv_byte(); } fwrite(data, 1, size, file); } - else + else if (strcmp(argv[1], "jump") == 0) { - ret = fread(data, 1, size, file); - if (ret < size) - printf("warning: read only %d/%d\n", ret, size); + send_cmd(CMD_JUMP); + send_byte((addr >> 16) & 0xff); + send_byte((addr >> 8) & 0xff); + send_byte((addr >> 0) & 0xff); + } + else if (strcmp(argv[1], "io") == 0) + { + unsigned char *p = data; + unsigned char rdata[4]; + send_cmd(CMD_IOSEQ); + send_byte((count >> 8) & 0xff); + send_byte((count >> 0) & 0xff); - outb(0xc0, 890); /* out mode, TL hi */ - outb(data[0] & 0x0f, 888); - outb(0xc2, 890); /* out mode, TL low (start condition) */ + for (; count > 0; count--) { + input_to_output(); + send_bytes(p, 4); /* cmd + addr */ + + switch (p[0]) { + case IOSEQ_R8: + output_to_input(); + recv_bytes(rdata, 1); + printf("r8 %06x %02x\n", PBE3(p + 1), rdata[0]); + p += 4; + break; + case IOSEQ_R16: + output_to_input(); + recv_bytes(rdata, 2); + printf("r16 %06x %04x\n", PBE3(p + 1), PBE2(rdata)); + p += 4; + break; + case IOSEQ_R32: + output_to_input(); + recv_bytes(rdata, 4); + printf("r32 %06x %08x\n", PBE3(p + 1), PBE4(rdata)); + p += 4; + break; + case IOSEQ_W8: + send_bytes(&p[4], 1); + printf("w8 %06x %02x\n", PBE3(p + 1), p[4]); + p += 5; + break; + case IOSEQ_W16: + send_bytes(&p[4], 2); + printf("w16 %06x %04x\n", PBE3(p + 1), PBE2(p + 4)); + p += 6; + break; + case IOSEQ_W32: + send_bytes(&p[4], 4); + printf("w32 %06x %08x\n", PBE3(p + 1), PBE4(p + 4)); + p += 8; + break; + default: + do_exit("error in ioseq data\n", NULL); + break; + } + } + } + else if (strcmp(argv[1], "loadstate") == 0) + { + send_cmd(CMD_LOADSTATE); for (i = 0; i < size; i++) { - if ((i & 0xff) == 0) { + if ((i & 0x1f) == 0) { printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); printf("%06x/%06x", i, size); fflush(stdout); } - /* wait for TH low */ - while (inb(889) & 0x40) ; + send_byte(data[i]); + } + } + else if (strcmp(argv[1], "recvvram") == 0) + { + send_cmd(CMD_VRAM_RECV); + output_to_input(); - byte = data[i]; + for (i = 0; i < size; i++) + { + if ((i & 0xff) == 0) { + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); + printf("%06x/%06x", i, size); + fflush(stdout); + } - outb(byte & 0x0f, 888); - outb(0xc2, 890); /* TL low */ + data[i] = recv_byte(); + } - /* wait for TH high */ - while (!(inb(889) & 0x40)) ; + fwrite(data, 1, size, file); + } - outb(byte >> 4, 888); - outb(0xc0, 890); /* TL high */ - } + if (size != 0) { + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); + printf("%06x/%06x\n", i, size); } - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); - printf("%06x/%06x\n", i, size); - fclose(file); + if (file != NULL) + fclose(file); + + /* switch TL back to high, disable outputs */ + outb(0xe0, PORT_CONTROL); return 0; }