don't try to reset flash unconditionally
[flashkit-mdc.git] / flashkit.c
CommitLineData
d9502b8d 1/*
2 * Tool for USB serial communication with Krikzz's FlashKit-MD
3 * Copyright (c) 2017 notaz
4 *
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:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
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
23 * SOFTWARE.
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <assert.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <arpa/inet.h> // hton
35#include <termios.h>
36#include <unistd.h>
37
38#ifndef min
39#define min(x, y) ((x) < (y) ? (x) : (y))
40#define max(x, y) ((x) > (y) ? (x) : (y))
41#endif
42
43enum dev_cmd {
44 CMD_ADDR = 0,
45 CMD_LEN = 1,
46 CMD_RD = 2,
47 CMD_WR = 3,
48 CMD_RY = 4,
49 CMD_DELAY = 5,
50};
11d3b79e 51#define PAR_MODE8 (1 << 4) /* but still drives noth LWR and UWR */
d9502b8d 52#define PAR_DEV_ID (1 << 5)
53#define PAR_SINGE (1 << 6)
54#define PAR_INC (1 << 7)
55
56static int setup(int fd)
57{
58 struct termios tty;
59 int ret;
60
61 memset(&tty, 0, sizeof(tty));
62
63 ret = tcgetattr(fd, &tty);
64 if (ret != 0)
65 {
66 perror("tcgetattr");
67 return 1;
68 }
69
70 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
71 | INLCR | IGNCR | ICRNL | IXON);
72 tty.c_oflag &= ~OPOST;
73 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
74 tty.c_cflag &= ~(CSIZE | PARENB);
75 tty.c_cflag |= CS8;
76
77 //tty.c_cc[VMIN] = 1;
78 //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
79
80 ret = tcsetattr(fd, TCSANOW, &tty);
81 if (ret != 0) {
82 perror("tcsetattr");
83 return ret;
84 }
85
86 return 0;
87}
88
89static int write_serial(int fd, const void *data, size_t size)
90{
91 int ret;
92
93 ret = write(fd, data, size);
94 if (ret != size) {
95 fprintf(stderr, "write %d/%zd: ", ret, size);
96 perror("");
97 exit(1);
98 }
99
100 return 0;
101}
102
103static int read_serial(int fd, void *data, size_t size)
104{
105 size_t got = 0;
106 int ret;
107
108 while (got < size) {
109 ret = read(fd, (char *)data + got, size - got);
110 if (ret <= 0) {
111 fprintf(stderr, "read %d %zd/%zd: ",
112 ret, got, size);
113 perror("");
114 exit(1);
115 }
116 got += ret;
117 }
118
119 return 0;
120}
121
c097fb89 122/* addr arg is always byte address */
123static void set_addr8(int fd, uint32_t addr)
d9502b8d 124{
125 uint8_t cmd[6] = {
c097fb89 126 CMD_ADDR, addr >> 16,
127 CMD_ADDR, addr >> 8,
128 CMD_ADDR, addr >> 0
129 };
130 write_serial(fd, cmd, sizeof(cmd));
131}
132
133static void set_addr16(int fd, uint32_t addr)
134{
135 set_addr8(fd, addr >> 1);
136}
137
138static uint16_t read_bus8(int fd, uint32_t addr)
139{
140 uint8_t cmd[7] = {
141 CMD_ADDR, addr >> 16,
142 CMD_ADDR, addr >> 8,
143 CMD_ADDR, addr >> 0,
144 CMD_RD | PAR_SINGE | PAR_MODE8
d9502b8d 145 };
c097fb89 146 uint8_t r;
147
d9502b8d 148 write_serial(fd, cmd, sizeof(cmd));
c097fb89 149 read_serial(fd, &r, sizeof(r));
150 return r;
d9502b8d 151}
152
c097fb89 153static uint16_t read_bus16(int fd, uint32_t addr)
d9502b8d 154{
155 uint8_t cmd[7] = {
156 CMD_ADDR, addr >> 17,
157 CMD_ADDR, addr >> 9,
158 CMD_ADDR, addr >> 1,
159 CMD_RD | PAR_SINGE
160 };
161 uint16_t r;
162
163 write_serial(fd, cmd, sizeof(cmd));
164 read_serial(fd, &r, sizeof(r));
165 return ntohs(r);
166}
167
c097fb89 168static void write_bus8(int fd, uint32_t addr, uint16_t d)
169{
170 uint8_t cmd[8] = {
171 CMD_ADDR, addr >> 16,
172 CMD_ADDR, addr >> 8,
173 CMD_ADDR, addr >> 0,
174 CMD_WR | PAR_SINGE | PAR_MODE8,
175 d
176 };
177
178 write_serial(fd, cmd, sizeof(cmd));
179}
180
181static void write_bus16(int fd, uint32_t addr, uint16_t d)
d9502b8d 182{
183 uint8_t cmd[9] = {
184 CMD_ADDR, addr >> 17,
185 CMD_ADDR, addr >> 9,
186 CMD_ADDR, addr >> 1,
187 CMD_WR | PAR_SINGE,
188 d >> 8, d
189 };
190
191 write_serial(fd, cmd, sizeof(cmd));
192}
193
c097fb89 194static void read_block8(int fd, void *dst, uint32_t size)
d9502b8d 195{
c097fb89 196 // PAR_MODE8 does not work, so read as 16bit and throw away MSB
197 uint8_t tmp[0x10000], *d8 = dst;
d9502b8d 198 uint8_t cmd[5] = {
c097fb89 199 CMD_LEN, size >> 8,
200 CMD_LEN, size >> 0,
d9502b8d 201 CMD_RD | PAR_INC
202 };
c097fb89 203 int i;
d9502b8d 204
205 assert(size <= 0x10000);
206 write_serial(fd, cmd, sizeof(cmd));
207 read_serial(fd, dst, size);
c097fb89 208 read_serial(fd, tmp, size);
209
210 for (i = 0; i < size / 2; i++)
211 d8[i] = d8[i * 2 + 1];
212 d8 += size / 2;
213 for (i = 0; i < size / 2; i++)
214 d8[i] = tmp[i * 2 + 1];
d9502b8d 215}
216
c097fb89 217static void read_block16(int fd, void *dst, uint32_t size)
d9502b8d 218{
c097fb89 219 uint8_t cmd[5] = {
220 CMD_LEN, size >> 9,
221 CMD_LEN, size >> 1,
222 CMD_RD | PAR_INC
223 };
d9502b8d 224
c097fb89 225 assert(size <= 0x10000);
226 write_serial(fd, cmd, sizeof(cmd));
227 read_serial(fd, dst, size);
d9502b8d 228}
229
c097fb89 230static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
d9502b8d 231{
c097fb89 232 uint8_t cmd[] = {
233 // unlock
234 CMD_ADDR, 0,
235 CMD_ADDR, 0x0a,
236 CMD_ADDR, 0xaa,
237 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
238 CMD_ADDR, 0,
239 CMD_ADDR, 0x05,
240 CMD_ADDR, 0x55,
241 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
242 // program setup
243 CMD_ADDR, 0,
244 CMD_ADDR, 0x0a,
245 CMD_ADDR, 0xaa,
246 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
247 // program data
248 CMD_ADDR, addr >> 16,
249 CMD_ADDR, addr >> 8,
250 CMD_ADDR, addr >> 0,
251 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
252 CMD_RY
253 };
d9502b8d 254
c097fb89 255 write_serial(fd, cmd, sizeof(cmd));
d9502b8d 256}
257
c097fb89 258static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
d9502b8d 259{
260 uint8_t cmd[] = {
261 // unlock
262 CMD_ADDR, 0,
263 CMD_ADDR, 0x05,
264 CMD_ADDR, 0x55,
265 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
266 CMD_ADDR, 0,
267 CMD_ADDR, 0x02,
268 CMD_ADDR, 0xaa,
269 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
270 // program setup
271 CMD_ADDR, 0,
272 CMD_ADDR, 0x05,
273 CMD_ADDR, 0x55,
274 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
275 // program data
276 CMD_ADDR, addr >> 17,
277 CMD_ADDR, addr >> 9,
278 CMD_ADDR, addr >> 1,
279 CMD_WR | PAR_SINGE, d[0], d[1],
280 CMD_RY
281 };
282
283 write_serial(fd, cmd, sizeof(cmd));
284}
285
563a41af 286// -- 8bit+LoROM --
287
288static uint32_t lorom_rom_addr(uint32_t a)
c5e4a2c2 289{
563a41af 290 return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff);
291}
292
293static void set_addr8l(int fd, uint32_t a)
294{
295 set_addr8(fd, lorom_rom_addr(a));
296}
297
298static uint16_t read_bus8l(int fd, uint32_t a)
299{
300 return read_bus8(fd, lorom_rom_addr(a));
301}
302
303static void write_bus8l(int fd, uint32_t a, uint16_t d)
304{
305 write_bus8(fd, lorom_rom_addr(a), d);
306}
307
308static void flash_seq_write8l(int fd, uint32_t a, const uint8_t *d)
309{
310 a = lorom_rom_addr(a);
c5e4a2c2 311 uint8_t cmd[] = {
312 // unlock
313 CMD_ADDR, 0,
314 CMD_ADDR, 0x8a,
315 CMD_ADDR, 0xaa,
316 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
317 CMD_ADDR, 0,
318 CMD_ADDR, 0x85,
319 CMD_ADDR, 0x55,
320 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
321 // program setup
322 CMD_ADDR, 0,
323 CMD_ADDR, 0x8a,
324 CMD_ADDR, 0xaa,
325 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
326 // program data
563a41af 327 CMD_ADDR, a >> 16,
328 CMD_ADDR, a >> 8,
329 CMD_ADDR, a >> 0,
c5e4a2c2 330 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
331 CMD_RY
332 };
333
334 write_serial(fd, cmd, sizeof(cmd));
335}
336
563a41af 337// -- 8bit+LoROM+adapter --
338
339static uint32_t do_flipflops(int fd, uint32_t a)
340{
341 static uint32_t abits_now = ~0u; // A23, A22, A21
342 uint32_t abits = (a >> 21) & 7;
343
344 if (abits != abits_now) {
345 // printf("flipflops: %x->%x\n", abits_now, abits);
346 write_bus16(fd, 0xa13000, abits);
347 abits_now = abits;
348 }
349 return a & 0x1fffff;
350}
351
352static void set_addr8la(int fd, uint32_t a)
353{
354 set_addr8(fd, do_flipflops(fd, lorom_rom_addr(a)));
355}
356
357static uint16_t read_bus8la(int fd, uint32_t a)
358{
359 return read_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)));
360}
361
362static void write_bus8la(int fd, uint32_t a, uint16_t d)
363{
364 write_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)), d);
365}
366
367static void flash_seq_write8la(int fd, uint32_t a, const uint8_t *d)
368{
369 // we should clear flipflops for the flash commands, but this
370 // doesn't seem to be necessary as the flash chip seems to
371 // ignore the upper bits when looking for commands, and this
372 // extra clearing would slow things down
373 a = do_flipflops(fd, lorom_rom_addr(a));
374 uint8_t cmd[] = {
375 // unlock
376 CMD_ADDR, 0,
377 CMD_ADDR, 0x8a,
378 CMD_ADDR, 0xaa,
379 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
380 CMD_ADDR, 0,
381 CMD_ADDR, 0x85,
382 CMD_ADDR, 0x55,
383 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
384 // program setup
385 CMD_ADDR, 0,
386 CMD_ADDR, 0x8a,
387 CMD_ADDR, 0xaa,
388 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
389 // program data
390 CMD_ADDR, a >> 16,
391 CMD_ADDR, a >> 8,
392 CMD_ADDR, a >> 0,
393 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
394 CMD_RY
395 };
396
397 write_serial(fd, cmd, sizeof(cmd));
398}
399
400// -- 8bit+LoROM+adapter+sram --
401
402static uint32_t lorom_sram_addr(uint32_t a)
403{
404 return a | 0x600000;
405}
406
407static void set_addr8las(int fd, uint32_t a)
408{
409 set_addr8(fd, do_flipflops(fd, lorom_sram_addr(a)));
410}
411
412static uint16_t read_bus8las(int fd, uint32_t a)
413{
414 return read_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)));
415}
416
417static void write_bus8las(int fd, uint32_t a, uint16_t d)
418{
419 write_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)), d);
420}
421
422static void flash_seq_write8las(int fd, uint32_t a, const uint8_t *d)
423{
424}
425
c5e4a2c2 426#define N0 ""
427#define N1 "8bit"
428#define N2 "8bit+LoROM"
429#define N3 "8bit+LoROM+adapter"
563a41af 430#define N4 "8bit+LoROM+adapter+sram"
c097fb89 431static const struct iof
432{
c5e4a2c2 433 const char *name;
434 int addrs_remapped;
c097fb89 435 void (*set_addr)(int fd, uint32_t addr);
436 uint16_t (*read_bus)(int fd, uint32_t addr);
437 void (*write_bus)(int fd, uint32_t addr, uint16_t d);
438 void (*read_block)(int fd, void *dst, uint32_t size);
439 void (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
440}
441io_ops[] =
442{
563a41af 443 { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
444 { N1, 0, set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 },
445 { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l },
446 { N3, 1, set_addr8la, read_bus8la, write_bus8la, read_block8, flash_seq_write8la },
447 { N4, 0, set_addr8las, read_bus8las, write_bus8las, read_block8, flash_seq_write8las },
c097fb89 448};
449
c5e4a2c2 450static const struct iof *io = &io_ops[0];
c097fb89 451
452static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
453{
454 // unlock
455 io->write_bus(fd, 0xaaa, 0xaa);
456 io->write_bus(fd, 0x555, 0x55);
457
458 io->write_bus(fd, 0xaaa, cmd);
459 return io->read_bus(fd, addr);
460}
461
c5e4a2c2 462static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d)
c097fb89 463{
464 // printf("erase %06x\n", addr);
465 io->write_bus(fd, 0xaaa, 0xaa);
466 io->write_bus(fd, 0x555, 0x55);
467 io->write_bus(fd, 0xaaa, 0x80);
468
469 io->write_bus(fd, 0xaaa, 0xaa);
470 io->write_bus(fd, 0x555, 0x55);
c5e4a2c2 471 io->write_bus(fd, addr, d);
472}
473
474static void flash_seq_erase(int fd, uint32_t addr)
475{
476 flash_seq_erase_d(fd, addr, 0x30);
477}
478
479static void flash_seq_erase_full(int fd)
480{
481 flash_seq_erase_d(fd, 0xaaa, 0x10);
c097fb89 482}
483
d9502b8d 484// status wait + dummy read to cause a wait?
485static uint16_t ry_read(int fd)
486{
487 uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
488 uint16_t rv = 0;
489
490 write_serial(fd, cmd, sizeof(cmd));
491 read_serial(fd, &rv, sizeof(rv));
492 return ntohs(rv);
493}
494
495static void set_delay(int fd, uint8_t delay)
496{
497 uint8_t cmd[2] = { CMD_DELAY, delay };
498
499 write_serial(fd, cmd, sizeof(cmd));
500}
501
502static struct flash_info {
503 uint16_t mid;
504 uint16_t did;
505 uint32_t size;
506 uint16_t region_cnt;
507 struct {
508 uint32_t block_size;
509 uint32_t block_count;
510 uint32_t start;
511 uint32_t size;
512 } region[4];
513} info;
514
515static void read_info(int fd)
516{
517 static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
518 uint32_t total = 0;
519 uint16_t resp[3];
520 uint32_t i, a;
521
522 info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
c097fb89 523 info.did = io->read_bus(fd, 2);
d9502b8d 524
525 // could enter CFI directly, but there seems to be a "stack"
526 // of modes, so 2 exits would be needed
c097fb89 527 io->write_bus(fd, 0, 0xf0);
d9502b8d 528
c097fb89 529 io->write_bus(fd, 0xaa, 0x98); // CFI Query
530 resp[0] = io->read_bus(fd, 0x20);
531 resp[1] = io->read_bus(fd, 0x22);
532 resp[2] = io->read_bus(fd, 0x24);
d9502b8d 533 if (memcmp(resp, qry, sizeof(resp))) {
534 fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n",
535 resp[0], resp[1], resp[2]);
536 exit(1);
537 }
c097fb89 538 info.size = 1u << io->read_bus(fd, 0x4e);
539 info.region_cnt = io->read_bus(fd, 0x58);
d9502b8d 540 assert(0 < info.region_cnt && info.region_cnt <= 4);
541 for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
c097fb89 542 info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
543 info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
544 info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
545 info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
d9502b8d 546 info.region[i].start = total;
547 info.region[i].size =
548 info.region[i].block_size * info.region[i].block_count;
549 assert(info.region[i].size);
550 total += info.region[i].size;
551 }
552
c097fb89 553 io->write_bus(fd, 0, 0xf0); // flash reset
d9502b8d 554
555 printf("Flash info:\n");
556 printf("Manufacturer ID: %04x\n", info.mid);
557 printf("Device ID: %04x\n", info.did);
558 printf("size: %u\n", info.size);
559 printf("Erase Block Regions: %u\n", info.region_cnt);
560 for (i = 0; i < info.region_cnt; i++)
561 printf(" %5u x %u\n", info.region[i].block_size,
562 info.region[i].block_count);
563 if (info.size != total)
564 fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
565}
566
567static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
568{
f34ac796 569 uint32_t i, base, faddr;
d9502b8d 570
571 assert(info.region_cnt);
f34ac796 572 assert(info.size);
573
574 // get a flash address to allow mapper hardware
575 faddr = addr & (info.size - 1);
576 base = addr & ~(info.size - 1);
577
d9502b8d 578 for (i = 0; i < info.region_cnt; i++) {
f34ac796 579 if (info.region[i].start <= faddr
580 && faddr < info.region[i].start + info.region[i].size)
d9502b8d 581 {
f34ac796 582 uint32_t blk = (faddr - info.region[i].start)
d9502b8d 583 / info.region[i].block_size
584 + blk_offset;
f34ac796 585 return base + info.region[i].start
d9502b8d 586 + blk * info.region[i].block_size;
587 }
588 }
589
590 fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
591 exit(1);
592}
593
594static void print_progress(uint32_t done, uint32_t total)
595{
596 int i, step;
597
c097fb89 598 printf("\r%06x/%06x |", done, total);
d9502b8d 599
600 step = (total + 19) / 20;
601 for (i = step; i <= total; i += step)
602 fputc(done >= i ? '=' : '-', stdout);
603 printf("| %3d%%", done * 100 / total);
604 fflush(stdout);
605 if (done >= total)
606 fputc('\n', stdout);
607}
608
78ad8139 609static FILE *open_prep_read(const char *fname, long *size)
610{
611 FILE *f = fopen(fname, "rb");
612 if (!f) {
613 fprintf(stderr, "fopen %s: ", fname);
614 perror("");
615 exit(1);
616 }
617 if (*size <= 0) {
618 fseek(f, 0, SEEK_END);
619 *size = ftell(f);
620 fseek(f, 0, SEEK_SET);
621 }
622 if (*size <= 0) {
623 fprintf(stderr, "size of %s is %ld\n", fname, *size);
624 exit(1);
625 }
626 return f;
627}
628
11d3b79e 629static const char *portname =
630#ifdef __APPLE__
631 "/dev/cu.usbserial-AL0254JM";
632#else
633 "/dev/ttyUSB0";
634#endif
af236294 635
d9502b8d 636static void usage(const char *argv0)
637{
c5e4a2c2 638 size_t i;
639
d9502b8d 640 printf("usage:\n"
641 "%s [options]\n"
af236294 642 " -d <ttydevice> (default %s)\n"
d9502b8d 643 " -r <file> [size] dump the cart (default 4MB)\n"
78ad8139 644 " -w <file> [size] program the flash (def. file size)\n"
645 " -s <file> [size] simple write (SRAM, etc, def. file size)\n"
c5e4a2c2 646 " -e <size> erase (rounds to block size); can specify 'full'\n"
78ad8139 647 " -a <start_address> read/write start address (default 0)\n"
c5e4a2c2 648 " -m <n> use an address mapper n, one of:\n"
af236294 649 , argv0, portname);
c5e4a2c2 650 for (i = 1; i < sizeof(io_ops) / sizeof(io_ops[0]); i++)
651 printf(
652 " %zd: %s\n", i, io_ops[i].name);
653 printf( " -v verify written data\n"
654 " -i get info about the flash chip\n");
d9502b8d 655 exit(1);
656}
657
658static void invarg(int argc, char *argv[], int arg)
659{
660 if (arg < argc)
661 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
662 else
663 fprintf(stderr, "missing required argument %d\n", arg);
664 exit(1);
665}
666
667static void *getarg(int argc, char *argv[], int arg)
668{
669 if (arg >= argc)
670 invarg(argc, argv, arg);
671 return argv[arg];
672}
673
c5e4a2c2 674static long getarg_l(int argc, char *argv[], int arg)
675{
676 char *endp = NULL;
677 long r;
678
679 if (arg >= argc)
680 invarg(argc, argv, arg);
681 r = strtol(argv[arg], &endp, 0);
682 if (endp == NULL || *endp != 0)
683 invarg(argc, argv, arg);
684 return r;
685}
686
687// 32K to easily handle SNES LoROM
688static uint8_t g_block[0x8000];
689static uint8_t g_block2[sizeof(g_block)];
d9502b8d 690
691int main(int argc, char *argv[])
692{
d9502b8d 693 const char *fname_w = NULL;
694 const char *fname_r = NULL;
78ad8139 695 const char *fname_ws = NULL;
d9502b8d 696 long size_w = 0;
697 long size_r = 0;
78ad8139 698 long size_ws = 0;
d9502b8d 699 long size_e = 0;
700 long size_v = 0;
701 long len, address_in = 0;
702 long a, a_blk, end;
703 int do_info = 0;
704 int do_verify = 0;
c097fb89 705 int write_step = 2;
d9502b8d 706 FILE *f_w = NULL;
707 FILE *f_r = NULL;
78ad8139 708 FILE *f_ws = NULL;
d9502b8d 709 uint8_t id[2] = { 0, 0 };
710 uint8_t cmd;
711 uint16_t rv;
712 int arg = 1;
713 int fd;
714
715 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
716 usage(argv[0]);
717
718 for (arg = 1; arg < argc; arg++) {
719 if (!strcmp(argv[arg], "-d")) {
720 portname = getarg(argc, argv, ++arg);
721 continue;
722 }
723 if (!strcmp(argv[arg], "-r")) {
724 fname_r = getarg(argc, argv, ++arg);
725 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 726 size_r = getarg_l(argc, argv, ++arg);
d9502b8d 727 if (size_r <= 0)
728 invarg(argc, argv, arg);
729 }
730 continue;
731 }
732 if (!strcmp(argv[arg], "-w")) {
733 fname_w = getarg(argc, argv, ++arg);
734 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 735 size_w = getarg_l(argc, argv, ++arg);
d9502b8d 736 if (size_w <= 0)
737 invarg(argc, argv, arg);
738 }
739 continue;
740 }
78ad8139 741 if (!strcmp(argv[arg], "-s")) {
742 fname_ws = getarg(argc, argv, ++arg);
743 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 744 size_ws = getarg_l(argc, argv, ++arg);
78ad8139 745 if (size_ws <= 0)
746 invarg(argc, argv, arg);
747 }
748 continue;
749 }
d9502b8d 750 if (!strcmp(argv[arg], "-a")) {
c5e4a2c2 751 address_in = getarg_l(argc, argv, ++arg);
752 if (address_in < 0)
d9502b8d 753 invarg(argc, argv, arg);
754 continue;
755 }
756 if (!strcmp(argv[arg], "-e")) {
c5e4a2c2 757 arg++;
758 if (!strcmp(getarg(argc, argv, arg), "full"))
759 size_e = -1;
760 else {
761 size_e = getarg_l(argc, argv, arg);
762 if (size_e <= 0)
763 invarg(argc, argv, arg);
764 }
d9502b8d 765 continue;
766 }
c5e4a2c2 767 if (!strcmp(argv[arg], "-m")) {
768 long v = getarg_l(argc, argv, ++arg);
769 if ((size_t)v >= sizeof(io_ops) / sizeof(io_ops[0]))
770 invarg(argc, argv, arg);
771 io = &io_ops[v];
772 if (v != 0)
773 write_step = 1;
c097fb89 774 continue;
775 }
d9502b8d 776 if (!strcmp(argv[arg], "-v")) {
777 do_verify = 1;
778 continue;
779 }
780 if (!strcmp(argv[arg], "-i")) {
781 do_info = 1;
782 continue;
783 }
784 invarg(argc, argv, arg);
785 }
786
787 if (fname_r && size_r == 0)
788 size_r = 0x400000;
789
790 if (fname_w) {
78ad8139 791 f_w = open_prep_read(fname_w, &size_w);
c5e4a2c2 792 if (size_e == 0 && io->addrs_remapped)
793 size_e = -1;
794 if (size_e != -1 && size_e < size_w)
d9502b8d 795 size_e = size_w;
796 if (do_verify)
797 size_v = size_w;
798 }
799
800 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
801 if (fd < 0) {
802 fprintf(stderr, "open %s: ", portname);
803 perror("");
804 return 1;
805 }
806
78ad8139 807 if (fname_ws)
808 f_ws = open_prep_read(fname_ws, &size_ws);
809
d9502b8d 810 setup(fd);
811
812 cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
813 write_serial(fd, &cmd, sizeof(cmd));
814 read_serial(fd, id, sizeof(id));
815 if (id[0] != id[1] || id[0] == 0) {
816 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
817 return 1;
818 }
819 printf("flashkit id: %02x\n", id[0]);
820
821 set_delay(fd, 1);
5a2c48a5 822
823 if (do_info || size_e || f_w)
824 io->write_bus(fd, 0, 0xf0); // flash reset
d9502b8d 825
826 if (do_info || size_e)
827 read_info(fd);
828
c5e4a2c2 829 if (size_e == -1) {
830 printf("performing full erase..."); fflush(stdout);
831 flash_seq_erase_full(fd);
832 rv = ry_read(fd);
833 if (rv != 0xffff) {
834 fprintf(stderr, "\nerase error: %04x\n", rv);
835 return 1;
836 }
837 printf(" done.\n");
838 }
839 else if (size_e) {
d9502b8d 840 // set_delay(fd, 0); // ?
841 a_blk = get_block_addr(address_in, 0);
842 end = address_in + size_e;
843
844 printf("erasing %ld bytes:\n", size_e);
845 print_progress(0, size_e);
846 for (a = address_in; a < end; ) {
847 flash_seq_erase(fd, a_blk);
848 rv = ry_read(fd);
849 if (rv != 0xffff) {
850 fprintf(stderr, "\nerase error: %lx %04x\n",
851 a_blk, rv);
c5e4a2c2 852 return 1;
d9502b8d 853 }
854
855 a_blk = get_block_addr(a_blk, 1);
856 a += a_blk - a;
857 print_progress(a - address_in, size_e);
858 }
859 }
860 if (f_w != NULL) {
861 uint8_t b[2];
862 set_delay(fd, 0);
78ad8139 863 printf("flashing %ld bytes:\n", size_w);
c097fb89 864 for (a = 0; a < size_w; a += write_step) {
d9502b8d 865 ssize_t r;
866
867 b[1] = 0xff;
c097fb89 868 len = min(size_w - a, write_step);
d9502b8d 869 r = fread(b, 1, len, f_w);
870 if (r != len) {
871 perror("\nfread");
872 return 1;
873 }
c097fb89 874 io->flash_seq_write(fd, address_in + a, b);
d9502b8d 875
c097fb89 876 if (!(a & 0x3ff))
d9502b8d 877 print_progress(a, size_w);
878 }
879 print_progress(a, size_w);
880 rv = ry_read(fd);
c097fb89 881 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
d9502b8d 882 fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
883 rv, b[0], b[1]);
884 rewind(f_w);
885 set_delay(fd, 1);
886 }
78ad8139 887 if (f_ws != NULL) {
888 printf("writing %ld bytes:\n", size_ws);
889 for (a = 0; a < size_ws; a += write_step) {
890 uint16_t b = 0xffff;
891 ssize_t r;
892
893 len = min(size_ws - a, write_step);
894 r = fread(&b, 1, len, f_ws);
895 if (r != len) {
896 perror("\nfread");
897 return 1;
898 }
899 if (write_step == 2)
900 b = htons(b);
901 io->write_bus(fd, address_in + a, b);
902
903 if (!(a & 0x3ff))
904 print_progress(a, size_ws);
905 }
906 print_progress(a, size_ws);
907 }
d9502b8d 908
909 if (fname_r || size_v) {
910 long blks, blks_v, done, verify_diff = 0;
911
912 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
913 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
914 blks = max(blks, blks_v);
915 if (fname_r) {
916 f_r = fopen(fname_r, "wb");
917 if (!f_r) {
918 fprintf(stderr, "fopen %s: ", fname_r);
919 perror("");
920 return 1;
921 }
922 }
923
924 printf("reading %ld bytes:\n", max(size_r, size_v));
925 print_progress(0, blks * sizeof(g_block));
c097fb89 926 io->set_addr(fd, address_in);
d9502b8d 927 for (done = 0; done < size_r || done < size_v; ) {
c5e4a2c2 928 if (io->addrs_remapped)
929 io->set_addr(fd, address_in + done);
c097fb89 930 io->read_block(fd, g_block, sizeof(g_block));
d9502b8d 931 if (f_r && done < size_r) {
932 len = min(size_r - done, sizeof(g_block));
933 if (fwrite(g_block, 1, len, f_r) != len) {
934 perror("fwrite");
935 return 1;
936 }
937 }
938 if (done < size_v) {
939 len = min(size_v - done, sizeof(g_block));
940 if (fread(g_block2, 1, len, f_w) != len) {
941 perror("fread");
942 return 1;
943 }
944 verify_diff |= memcmp(g_block, g_block2, len);
945 }
946 done += sizeof(g_block);
947 print_progress(done, blks * sizeof(g_block));
948 }
949 if (verify_diff) {
950 fprintf(stderr, "verify FAILED\n");
951 return 1;
952 }
953 }
954 if (f_r)
955 fclose(f_r);
956 if (f_w)
957 fclose(f_w);
958
959 return 0;
960}