2 * Tool for USB communication with Mega Everdrive Pro
3 * Copyright (c) 2023 notaz
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 #include <sys/types.h>
35 #include <arpa/inet.h> // hton
40 /* from krikzz/MEGA-PRO/megalink/megalink/Edio.cs */
50 CMD_FLA_WR_SDC = 0x18,
75 CMD_F_DIR_SIZE = 0xC6,
76 CMD_F_DIR_PATH = 0xC7,
94 #define MAX_ROM_SIZE 0xF80000
96 #define ADDR_ROM 0x0000000
97 #define ADDR_SRAM 0x1000000
98 #define ADDR_BRAM 0x1080000
99 #define ADDR_CFG 0x1800000
100 #define ADDR_SSR 0x1802000
101 #define ADDR_FIFO 0x1810000
103 #define SIZE_ROMX 0x1000000
104 #define SIZE_SRAM 0x80000
105 #define SIZE_BRAM 0x80000
107 #define HOST_RST_OFF 0
108 #define HOST_RST_SOFT 1
109 #define HOST_RST_HARD 2
111 #define fatal(fmt, ...) do { \
112 fprintf(stderr, "\n:%d " fmt, __LINE__, ##__VA_ARGS__); \
116 static int serial_setup(int fd)
121 memset(&tty, 0, sizeof(tty));
123 ret = tcgetattr(fd, &tty);
130 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
131 | INLCR | IGNCR | ICRNL | IXON);
132 tty.c_oflag &= ~OPOST;
133 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
134 tty.c_cflag &= ~(CSIZE | PARENB);
137 //tty.c_cc[VMIN] = 1;
138 //tty.c_cc[VTIME] = 50; // seconds*10 read timeout?
140 ret = tcsetattr(fd, TCSANOW, &tty);
149 static void flush_input(int fd)
151 struct pollfd pfd = { fd, POLLIN, 0 };
152 uint8_t buf[0x10000];
153 int b = 0, total = 0;
155 while (poll(&pfd, 1, 10) > 0) {
156 b = read(fd, buf, sizeof(buf));
162 fprintf(stderr, "flushed %d leftover bytes", total);
164 fprintf(stderr, ", last '%c' %02x\n", buf[b-1], buf[b-1]);
169 static void serial_write(int fd, const void *data, size_t size)
173 ret = write(fd, data, size);
175 fprintf(stderr, "write %d/%zd: ", ret, size);
181 static void serial_read(int fd, void *data, size_t size)
187 ret = read(fd, (char *)data + got, size - got);
189 fprintf(stderr, "read %d %zd/%zd: ",
198 #define serial_read_expect_byte(fd, b) \
199 serial_read_expect_byte_(fd, b, __LINE__)
200 static void serial_read_expect_byte_(int fd, char expected, int line)
202 struct pollfd pfd = { fd, POLLIN, 0 };
206 ret = poll(&pfd, 1, 5000); // 32x reset is > 3s
208 fatal(":%d poll %d,%d\n", line, ret, errno);
210 serial_read(fd, &byte, sizeof(byte));
211 if (byte != expected) {
212 fatal(":%d wrong response: '%c' %02x, expected '%c'\n",
213 line, byte, byte, expected);
217 static void send_cmd_a(int fd, enum megapro_cmd cmd, const void *arg, size_t alen)
219 uint8_t buf[4] = { '+', '+' ^ 0xff, cmd, cmd ^ 0xff };
220 serial_write(fd, buf, sizeof(buf));
222 serial_write(fd, arg, alen);
226 static void send_cmd(int fd, enum megapro_cmd cmd)
228 send_cmd_a(fd, cmd, NULL, 0);
232 static void send_cmd_addr_len(int fd, enum megapro_cmd cmd, uint32_t addr, uint32_t len)
238 } __attribute__((packed)) args;
239 assert(sizeof(args) == 9);
240 args.addr = htonl(addr);
241 args.len = htonl(len);
243 send_cmd_a(fd, cmd, &args, sizeof(args));
246 static void send_reset_cmd(int fd, uint8_t type)
248 static int prev_type = -2;
250 if (type == prev_type)
253 send_cmd_a(fd, CMD_HOST_RST, &type, sizeof(type));
255 //if (type == HOST_RST_OFF && prev_type != HOST_RST_OFF)
256 if (!(type & 1) && (prev_type & 1))
257 serial_read_expect_byte(fd, 'r');
261 static void mem_write(int fd, uint32_t addr, const void *buf, size_t buf_size)
264 send_reset_cmd(fd, HOST_RST_SOFT);
265 send_cmd_addr_len(fd, CMD_MEM_WR, addr, buf_size);
266 serial_write(fd, buf, buf_size);
269 static void mem_read(int fd, uint32_t addr, void *buf, size_t buf_size)
272 send_reset_cmd(fd, HOST_RST_SOFT);
273 send_cmd_addr_len(fd, CMD_MEM_RD, addr, buf_size);
274 serial_read(fd, buf, buf_size);
277 // supposedly fifo communicates with whatever is running on MD side?
278 // (as there is no response to fifo stuff while commercial game runs)
279 static void fifo_write(int fd, const void *buf, size_t buf_size)
281 mem_write(fd, ADDR_FIFO, buf, buf_size);
284 static void fifo_write_u32(int fd, uint32_t u32)
287 fifo_write(fd, &u32, sizeof(u32));
290 static void fifo_write_str(int fd, const char *s)
292 fifo_write(fd, s, strlen(s));
295 static void fifo_write_len_n_str(int fd, const char *s)
297 size_t len = strlen(s);
298 uint16_t len16 = len;
299 assert(len16 == len);
300 len16 = htons(len16);
301 fifo_write(fd, &len16, sizeof(len16));
302 fifo_write(fd, s, len);
305 #define run_t_check(fd) run_t_check_(fd, __LINE__)
306 static void run_t_check_(int fd, int line)
308 fifo_write_str(fd, "*t");
309 serial_read_expect_byte_(fd, 'k', line);
312 // try to recover from messed up states
313 static void run_t_check_initial(int fd)
315 struct pollfd pfd = { fd, POLLIN, 0 };
319 fifo_write_str(fd, "*t");
320 ret = poll(&pfd, 1, 1000);
322 serial_read(fd, &byte, sizeof(byte));
325 fprintf(stderr, "bad response to '*t': '%c' %02x\n", byte, byte);
328 fprintf(stderr, "timeout on initial '*t'\n");
330 fatal("poll %d,%d\n", ret, errno);
332 /*fprintf(stderr, "attempting reset...\n");
333 send_reset_cmd(fd, HOST_RST_OFF);
334 send_reset_cmd(fd, HOST_RST_SOFT);*/
337 static uint32_t send_from_file(int fd, uint32_t addr, uint32_t size_ovr,
345 f = fopen(fname, "rb");
347 fatal("fopen %s: %s\n", fname, strerror(errno));
349 fseek(f, 0, SEEK_END);
351 fseek(f, 0, SEEK_SET);
354 fatal("size override %u > %zd\n", size_ovr, size);
357 if (size > MAX_ROM_SIZE)
358 fatal("size too large: %zd\n", size);
362 ret = fread(buf, 1, size, f);
364 fatal("fread %s: %d/%zd %s\n", fname, ret, size, strerror(errno));
366 mem_write(fd, addr, buf, size);
373 static uint32_t recv_to_file(int fd, uint32_t addr, uint32_t size,
380 if (strcmp(fname, "-") == 0)
383 f = fopen(fname, "wb");
385 fatal("fopen %s: %s", fname, strerror(errno));
391 mem_read(fd, addr, buf, size);
393 ret = fwrite(buf, 1, size, f);
395 fatal("fwrite %s: %d/%d %s\n", fname, ret, size, strerror(errno));
405 // reference: megalink/Usbio::loadGame
406 static void send_run_game(int fd, uint32_t size_ovr, const char *fname)
408 const char *fname_out;
412 fname_out = strrchr(fname, '/');
413 fname_out = fname_out ? fname_out + 1 : fname;
414 snprintf(name_out, sizeof(name_out), "USB:%s", fname_out);
416 size = send_from_file(fd, 0, size_ovr, fname);
417 send_reset_cmd(fd, HOST_RST_OFF);
421 fifo_write_str(fd, "*g");
422 fifo_write_u32(fd, size);
423 fifo_write_len_n_str(fd, name_out);
425 serial_read_expect_byte(fd, 0); // not done by reference
428 static void usage(const char *argv0)
430 fprintf(stderr, "usage:\n"
431 "%s [-d <ttydevice>] [opts]\n"
432 " -rst [type] reset\n"
433 " -l <file> [len] load and run\n"
434 " -w <file> <addr> [len] write to cart\n"
435 " -r <file> <addr> <len> read from cart\n"
440 static void invarg(int argc, char *argv[], int arg)
443 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
445 fprintf(stderr, "missing required argument %d\n", arg);
449 static int goodarg(const char *arg)
451 return arg && arg[0] && (arg[0] != '-' || arg[1] == 0);
454 static void *getarg(int argc, char *argv[], int arg)
456 if (!goodarg(argv[arg]))
457 invarg(argc, argv, arg);
461 int main(int argc, char *argv[])
463 const char *portname = "/dev/ttyACM0";
465 int no_exit_t_check = 0;
470 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
473 if (!strcmp(argv[arg], "-d")) {
475 if (argv[arg] != NULL)
476 portname = argv[arg];
478 invarg(argc, argv, arg);
482 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
484 fprintf(stderr, "open %s: ", portname);
491 run_t_check_initial(fd);
493 for (; arg < argc; arg++)
495 uint32_t addr, size = 0;
496 if (!strcmp(argv[arg], "-rst")) {
497 byte = HOST_RST_SOFT;
498 if (goodarg(argv[arg + 1]))
499 byte = strtol(argv[++arg], NULL, 0);
500 send_reset_cmd(fd, byte);
502 else if (!strcmp(argv[arg], "-l")) {
503 fname = getarg(argc, argv, ++arg);
504 if (goodarg(argv[arg + 1]))
505 size = strtol(argv[++arg], NULL, 0);
506 send_run_game(fd, size, fname);
507 no_exit_t_check = 1; // disturbs game startup
509 else if (!strcmp(argv[arg], "-w")) {
510 fname = getarg(argc, argv, ++arg);
511 addr = strtol(getarg(argc, argv, ++arg), NULL, 0);
512 if (goodarg(argv[arg + 1]))
513 size = strtol(argv[++arg], NULL, 0);
514 send_from_file(fd, addr, size, fname);
516 else if (!strcmp(argv[arg], "-r")) {
517 fname = getarg(argc, argv, ++arg);
518 addr = strtol(getarg(argc, argv, ++arg), NULL, 0);
519 size = strtol(getarg(argc, argv, ++arg), NULL, 0);
520 recv_to_file(fd, addr, size, fname);
523 send_reset_cmd(fd, HOST_RST_OFF);
524 if (!no_exit_t_check)