2 * Tool for USB serial communication with Krikzz's FlashKit-MD
3 * Copyright (c) 2017 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
31 #include <sys/types.h>
34 #include <arpa/inet.h> // hton
39 #define min(x, y) ((x) < (y) ? (x) : (y))
40 #define max(x, y) ((x) > (y) ? (x) : (y))
51 #define PAR_MODE8 (1 << 4) /* but still drives noth LWR and UWR */
52 #define PAR_DEV_ID (1 << 5)
53 #define PAR_SINGE (1 << 6)
54 #define PAR_INC (1 << 7)
56 static struct flash_info {
70 static int setup(int fd)
75 memset(&tty, 0, sizeof(tty));
77 ret = tcgetattr(fd, &tty);
84 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
85 | INLCR | IGNCR | ICRNL | IXON);
86 tty.c_oflag &= ~OPOST;
87 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
88 tty.c_cflag &= ~(CSIZE | PARENB);
92 //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
94 ret = tcsetattr(fd, TCSANOW, &tty);
103 static int write_serial(int fd, const void *data, size_t size)
107 ret = write(fd, data, size);
109 fprintf(stderr, "write %d/%zd: ", ret, size);
117 static int read_serial(int fd, void *data, size_t size)
123 ret = read(fd, (char *)data + got, size - got);
125 fprintf(stderr, "read %d %zd/%zd: ",
136 /* addr arg is always byte address */
137 static void set_addr8(int fd, uint32_t addr)
140 CMD_ADDR, addr >> 16,
144 write_serial(fd, cmd, sizeof(cmd));
147 static void set_addr16(int fd, uint32_t addr)
149 set_addr8(fd, addr >> 1);
152 static uint16_t read_bus8(int fd, uint32_t addr)
155 CMD_ADDR, addr >> 16,
158 CMD_RD | PAR_SINGE | PAR_MODE8
162 write_serial(fd, cmd, sizeof(cmd));
163 read_serial(fd, &r, sizeof(r));
167 static uint16_t read_bus16(int fd, uint32_t addr)
170 CMD_ADDR, addr >> 17,
177 write_serial(fd, cmd, sizeof(cmd));
178 read_serial(fd, &r, sizeof(r));
182 static void write_bus8(int fd, uint32_t addr, uint16_t d)
185 CMD_ADDR, addr >> 16,
188 CMD_WR | PAR_SINGE | PAR_MODE8,
192 write_serial(fd, cmd, sizeof(cmd));
195 static void write_bus16(int fd, uint32_t addr, uint16_t d)
198 CMD_ADDR, addr >> 17,
205 write_serial(fd, cmd, sizeof(cmd));
208 static void read_block8(int fd, void *dst, uint32_t size)
210 // PAR_MODE8 does not work, so read as 16bit and throw away MSB
211 uint8_t tmp[0x10000], *d8 = dst;
219 assert(size <= 0x10000);
220 write_serial(fd, cmd, sizeof(cmd));
221 read_serial(fd, dst, size);
222 read_serial(fd, tmp, size);
224 for (i = 0; i < size / 2; i++)
225 d8[i] = d8[i * 2 + 1];
227 for (i = 0; i < size / 2; i++)
228 d8[i] = tmp[i * 2 + 1];
231 static void read_block16(int fd, void *dst, uint32_t size)
239 assert(size <= 0x10000);
240 write_serial(fd, cmd, sizeof(cmd));
241 read_serial(fd, dst, size);
244 static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
248 CMD_ADDR, info.prog_addr >> 16,
249 CMD_ADDR, info.prog_addr >> 8,
250 CMD_ADDR, info.prog_addr >> 0,
251 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
252 CMD_ADDR, info.prog_addr >> 17,
253 CMD_ADDR, info.prog_addr >> 9,
254 CMD_ADDR, info.prog_addr >> 1,
255 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
257 CMD_ADDR, info.prog_addr >> 16,
258 CMD_ADDR, info.prog_addr >> 8,
259 CMD_ADDR, info.prog_addr >> 0,
260 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
262 CMD_ADDR, addr >> 16,
265 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
269 assert(info.prog_addr);
270 write_serial(fd, cmd, sizeof(cmd));
273 static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
277 CMD_ADDR, info.prog_addr >> 17,
278 CMD_ADDR, info.prog_addr >> 9,
279 CMD_ADDR, info.prog_addr >> 1,
280 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
281 CMD_ADDR, info.prog_addr >> 18,
282 CMD_ADDR, info.prog_addr >> 10,
283 CMD_ADDR, info.prog_addr >> 2,
284 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
286 CMD_ADDR, info.prog_addr >> 17,
287 CMD_ADDR, info.prog_addr >> 9,
288 CMD_ADDR, info.prog_addr >> 1,
289 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
291 CMD_ADDR, addr >> 17,
294 CMD_WR | PAR_SINGE, d[0], d[1],
298 assert(info.prog_addr);
299 write_serial(fd, cmd, sizeof(cmd));
304 static uint32_t lorom_rom_addr(uint32_t a)
306 return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff);
309 static void set_addr8l(int fd, uint32_t a)
311 set_addr8(fd, lorom_rom_addr(a));
314 static uint16_t read_bus8l(int fd, uint32_t a)
316 return read_bus8(fd, lorom_rom_addr(a));
319 static void write_bus8l(int fd, uint32_t a, uint16_t d)
321 write_bus8(fd, lorom_rom_addr(a), d);
324 static void flash_seq_write8l(int fd, uint32_t a, const uint8_t *d)
326 a = lorom_rom_addr(a);
332 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
336 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
341 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
346 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
350 write_serial(fd, cmd, sizeof(cmd));
353 // -- 8bit+LoROM+adapter --
355 static uint32_t do_flipflops(int fd, uint32_t a)
357 static uint32_t abits_now = ~0u; // A23, A22, A21
358 uint32_t abits = (a >> 21) & 7;
360 if (abits != abits_now) {
361 // printf("flipflops: %x->%x\n", abits_now, abits);
362 write_bus16(fd, 0xa13000, abits);
368 static void set_addr8la(int fd, uint32_t a)
370 set_addr8(fd, do_flipflops(fd, lorom_rom_addr(a)));
373 static uint16_t read_bus8la(int fd, uint32_t a)
375 return read_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)));
378 static void write_bus8la(int fd, uint32_t a, uint16_t d)
380 write_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)), d);
383 static void flash_seq_write8la(int fd, uint32_t a, const uint8_t *d)
385 // we should clear flipflops for the flash commands, but this
386 // doesn't seem to be necessary as the flash chip seems to
387 // ignore the upper bits when looking for commands, and this
388 // extra clearing would slow things down
389 a = do_flipflops(fd, lorom_rom_addr(a));
395 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
399 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
404 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
409 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
413 write_serial(fd, cmd, sizeof(cmd));
416 // -- 8bit+LoROM+adapter+sram --
418 static uint32_t lorom_sram_addr(uint32_t a)
423 static void set_addr8las(int fd, uint32_t a)
425 set_addr8(fd, do_flipflops(fd, lorom_sram_addr(a)));
428 static uint16_t read_bus8las(int fd, uint32_t a)
430 return read_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)));
433 static void write_bus8las(int fd, uint32_t a, uint16_t d)
435 write_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)), d);
438 static void flash_seq_write8las(int fd, uint32_t a, const uint8_t *d)
444 #define N2 "8bit+LoROM"
445 #define N3 "8bit+LoROM+adapter"
446 #define N4 "8bit+LoROM+adapter+sram"
447 static const struct iof
451 void (*set_addr)(int fd, uint32_t addr);
452 uint16_t (*read_bus)(int fd, uint32_t addr);
453 void (*write_bus)(int fd, uint32_t addr, uint16_t d);
454 void (*read_block)(int fd, void *dst, uint32_t size);
455 void (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
459 { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
460 { N1, 0, set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 },
461 { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l },
462 { N3, 1, set_addr8la, read_bus8la, write_bus8la, read_block8, flash_seq_write8la },
463 { N4, 0, set_addr8las, read_bus8las, write_bus8las, read_block8, flash_seq_write8las },
466 static const struct iof *io = &io_ops[0];
468 static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
471 assert(info.prog_addr);
472 io->write_bus(fd, info.prog_addr >> 0, 0xaa);
473 io->write_bus(fd, info.prog_addr >> 1, 0x55);
475 io->write_bus(fd, info.prog_addr >> 0, cmd);
476 return io->read_bus(fd, addr);
479 static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d)
481 // printf("erase %06x\n", addr);
482 assert(info.prog_addr);
483 io->write_bus(fd, info.prog_addr >> 0, 0xaa);
484 io->write_bus(fd, info.prog_addr >> 1, 0x55);
485 io->write_bus(fd, info.prog_addr >> 0, 0x80);
487 io->write_bus(fd, info.prog_addr >> 0, 0xaa);
488 io->write_bus(fd, info.prog_addr >> 1, 0x55);
489 io->write_bus(fd, addr, d);
492 static void flash_seq_erase(int fd, uint32_t addr)
494 flash_seq_erase_d(fd, addr, 0x30);
497 static void flash_seq_erase_full(int fd)
499 flash_seq_erase_d(fd, info.prog_addr, 0x10);
502 // status wait + dummy read to cause a wait?
503 static uint16_t ry_read(int fd)
505 uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
508 write_serial(fd, cmd, sizeof(cmd));
509 read_serial(fd, &rv, sizeof(rv));
513 static void set_delay(int fd, uint8_t delay)
515 uint8_t cmd[2] = { CMD_DELAY, delay };
517 write_serial(fd, cmd, sizeof(cmd));
520 static void read_info(int fd)
522 static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
523 uint16_t resp_cfi[3], sst_mid = ~0;
528 // see if this chip understands CFI (common flash interface)
529 io->write_bus(fd, 0xaa, 0x98);
530 resp_cfi[0] = io->read_bus(fd, 0x20);
531 resp_cfi[1] = io->read_bus(fd, 0x22);
532 resp_cfi[2] = io->read_bus(fd, 0x24);
533 if (memcmp(resp_cfi, qry, sizeof(resp_cfi)) == 0)
535 info.size = 1u << io->read_bus(fd, 0x4e);
536 info.region_cnt = io->read_bus(fd, 0x58);
537 assert(0 < info.region_cnt && info.region_cnt <= 4);
538 for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
539 info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
540 info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
541 info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
542 info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
543 info.region[i].start = total;
544 info.region[i].size =
545 info.region[i].block_size * info.region[i].block_count;
546 assert(info.region[i].size);
547 total += info.region[i].size;
549 if (info.size != total)
550 fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
552 info.prog_addr = 0xaaa;
553 io->write_bus(fd, 0, 0xf0);
554 info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
555 info.did = io->read_bus(fd, 2);
559 // try the SST protocol
560 info.prog_addr = 0x5555;
561 sst_mid = flash_seq_r(fd, 0x90, 0);
565 info.did = io->read_bus(fd, 0x01);
570 info.size = 128 * 1024 << (info.did - 0xb5);
573 fprintf(stderr, "unrecognized SST device %02x\n", info.did);
577 info.region[0].block_count = info.size / 4096;
578 info.region[0].block_size = 4096;
579 info.region[0].start = 0;
580 info.region[0].size = info.size;
586 io->write_bus(fd, 0, 0xf0); // flash reset
588 if (info.prog_addr == 0) {
589 fprintf(stderr, "unable to identify the flash chip :(\n");
590 fprintf(stderr, "CFI response: %02x %02x %02x\n",
591 resp_cfi[0], resp_cfi[1], resp_cfi[2]);
592 fprintf(stderr, "SST MID: %02x\n", sst_mid);
596 printf("Flash info:\n");
597 printf("Manufacturer ID: %04x\n", info.mid);
598 printf("Device ID: %04x\n", info.did);
599 printf("size: %u\n", info.size);
600 printf("Erase Block Regions: %u\n", info.region_cnt);
601 for (i = 0; i < info.region_cnt; i++)
602 printf(" %5u x %u\n", info.region[i].block_size,
603 info.region[i].block_count);
606 static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
608 uint32_t i, base, faddr;
610 assert(info.region_cnt);
613 // get a flash address to allow mapper hardware
614 faddr = addr & (info.size - 1);
615 base = addr & ~(info.size - 1);
617 for (i = 0; i < info.region_cnt; i++) {
618 if (info.region[i].start <= faddr
619 && faddr < info.region[i].start + info.region[i].size)
621 uint32_t blk = (faddr - info.region[i].start)
622 / info.region[i].block_size
624 return base + info.region[i].start
625 + blk * info.region[i].block_size;
629 fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
633 static void print_progress(uint32_t done, uint32_t total)
637 printf("\r%06x/%06x |", done, total);
639 step = (total + 19) / 20;
640 for (i = step; i <= total; i += step)
641 fputc(done >= i ? '=' : '-', stdout);
642 printf("| %3d%%", done * 100 / total);
648 static FILE *open_prep_read(const char *fname, long *size)
650 FILE *f = fopen(fname, "rb");
652 fprintf(stderr, "fopen %s: ", fname);
657 fseek(f, 0, SEEK_END);
659 fseek(f, 0, SEEK_SET);
662 fprintf(stderr, "size of %s is %ld\n", fname, *size);
668 static const char *portname =
670 "/dev/cu.usbserial-AL0254JM";
675 static void usage(const char *argv0)
681 " -d <ttydevice> (default %s)\n"
682 " -r <file> [size] dump the cart (default 4MB)\n"
683 " -w <file> [size] program the flash (def. file size)\n"
684 " -s <file> [size] simple write (SRAM, etc, def. file size)\n"
685 " -e <size> erase (rounds to block size); can specify 'full'\n"
686 " -a <start_address> read/write start address (default 0)\n"
687 " -m <n> use an address mapper n, one of:\n"
689 for (i = 1; i < sizeof(io_ops) / sizeof(io_ops[0]); i++)
691 " %zd: %s\n", i, io_ops[i].name);
692 printf( " -v verify written data\n"
693 " -i get info about the flash chip\n");
697 static void invarg(int argc, char *argv[], int arg)
700 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
702 fprintf(stderr, "missing required argument %d\n", arg);
706 static void *getarg(int argc, char *argv[], int arg)
709 invarg(argc, argv, arg);
713 static long getarg_l(int argc, char *argv[], int arg)
719 invarg(argc, argv, arg);
720 r = strtol(argv[arg], &endp, 0);
721 if (endp == NULL || *endp != 0)
722 invarg(argc, argv, arg);
726 // 32K to easily handle SNES LoROM
727 static uint8_t g_block[0x8000];
728 static uint8_t g_block2[sizeof(g_block)];
730 int main(int argc, char *argv[])
732 const char *fname_w = NULL;
733 const char *fname_r = NULL;
734 const char *fname_ws = NULL;
740 long len, address_in = 0;
748 uint8_t id[2] = { 0, 0 };
754 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
757 for (arg = 1; arg < argc; arg++) {
758 if (!strcmp(argv[arg], "-d")) {
759 portname = getarg(argc, argv, ++arg);
762 if (!strcmp(argv[arg], "-r")) {
763 fname_r = getarg(argc, argv, ++arg);
764 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
765 size_r = getarg_l(argc, argv, ++arg);
767 invarg(argc, argv, arg);
771 if (!strcmp(argv[arg], "-w")) {
772 fname_w = getarg(argc, argv, ++arg);
773 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
774 size_w = getarg_l(argc, argv, ++arg);
776 invarg(argc, argv, arg);
780 if (!strcmp(argv[arg], "-s")) {
781 fname_ws = getarg(argc, argv, ++arg);
782 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
783 size_ws = getarg_l(argc, argv, ++arg);
785 invarg(argc, argv, arg);
789 if (!strcmp(argv[arg], "-a")) {
790 address_in = getarg_l(argc, argv, ++arg);
792 invarg(argc, argv, arg);
795 if (!strcmp(argv[arg], "-e")) {
797 if (!strcmp(getarg(argc, argv, arg), "full"))
800 size_e = getarg_l(argc, argv, arg);
802 invarg(argc, argv, arg);
806 if (!strcmp(argv[arg], "-m")) {
807 long v = getarg_l(argc, argv, ++arg);
808 if ((size_t)v >= sizeof(io_ops) / sizeof(io_ops[0]))
809 invarg(argc, argv, arg);
815 if (!strcmp(argv[arg], "-v")) {
819 if (!strcmp(argv[arg], "-i")) {
823 invarg(argc, argv, arg);
826 if (fname_r && size_r == 0)
830 f_w = open_prep_read(fname_w, &size_w);
831 if (size_e == 0 && io->addrs_remapped)
833 if (size_e != -1 && size_e < size_w)
839 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
841 fprintf(stderr, "open %s: ", portname);
847 f_ws = open_prep_read(fname_ws, &size_ws);
851 cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
852 write_serial(fd, &cmd, sizeof(cmd));
853 read_serial(fd, id, sizeof(id));
854 if (id[0] != id[1] || id[0] == 0) {
855 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
858 printf("flashkit id: %02x\n", id[0]);
862 if (do_info || size_e || f_w)
863 io->write_bus(fd, 0, 0xf0); // flash reset
865 if (do_info || size_e)
869 printf("performing full erase..."); fflush(stdout);
870 flash_seq_erase_full(fd);
873 fprintf(stderr, "\nerase error: %04x\n", rv);
879 // set_delay(fd, 0); // ?
880 a_blk = get_block_addr(address_in, 0);
881 end = address_in + size_e;
883 printf("erasing %ld bytes:\n", size_e);
884 print_progress(0, size_e);
885 for (a = address_in; a < end; ) {
886 flash_seq_erase(fd, a_blk);
889 fprintf(stderr, "\nerase error: %lx %04x\n",
894 a_blk = get_block_addr(a_blk, 1);
896 print_progress(a - address_in, size_e);
902 printf("flashing %ld bytes:\n", size_w);
903 for (a = 0; a < size_w; a += write_step) {
907 len = min(size_w - a, write_step);
908 r = fread(b, 1, len, f_w);
913 io->flash_seq_write(fd, address_in + a, b);
916 print_progress(a, size_w);
918 print_progress(a, size_w);
920 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
921 fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
927 printf("writing %ld bytes:\n", size_ws);
928 for (a = 0; a < size_ws; a += write_step) {
932 len = min(size_ws - a, write_step);
933 r = fread(&b, 1, len, f_ws);
940 io->write_bus(fd, address_in + a, b);
943 print_progress(a, size_ws);
945 print_progress(a, size_ws);
948 if (fname_r || size_v) {
949 long blks, blks_v, done, verify_diff = 0;
951 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
952 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
953 blks = max(blks, blks_v);
955 f_r = fopen(fname_r, "wb");
957 fprintf(stderr, "fopen %s: ", fname_r);
963 printf("reading %ld bytes:\n", max(size_r, size_v));
964 print_progress(0, blks * sizeof(g_block));
965 io->set_addr(fd, address_in);
966 for (done = 0; done < size_r || done < size_v; ) {
967 if (io->addrs_remapped)
968 io->set_addr(fd, address_in + done);
969 io->read_block(fd, g_block, sizeof(g_block));
970 if (f_r && done < size_r) {
971 len = min(size_r - done, sizeof(g_block));
972 if (fwrite(g_block, 1, len, f_r) != len) {
978 len = min(size_v - done, sizeof(g_block));
979 if (fread(g_block2, 1, len, f_w) != len) {
983 verify_diff |= memcmp(g_block, g_block2, len);
985 done += sizeof(g_block);
986 print_progress(done, blks * sizeof(g_block));
989 fprintf(stderr, "verify FAILED\n");