add adapter handling for HiROM
[flashkit-mdc.git] / flashkit.c
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
43 enum 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 };
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)
55
56 static struct flash_info {
57         uint32_t prog_addr;
58         uint16_t mid;
59         uint16_t did;
60         uint32_t size;
61         uint16_t region_cnt;
62         struct {
63                 uint32_t block_size;
64                 uint32_t block_count;
65                 uint32_t start;
66                 uint32_t size;
67         } region[4];
68 } info;
69
70 static int setup(int fd)
71 {
72         struct termios tty;
73         int ret;
74
75         memset(&tty, 0, sizeof(tty));
76
77         ret = tcgetattr(fd, &tty);
78         if (ret != 0)
79         {
80                 perror("tcgetattr");
81                 return 1;
82         }
83
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);
89         tty.c_cflag |= CS8;
90
91         //tty.c_cc[VMIN] = 1;
92         //tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
93
94         ret = tcsetattr(fd, TCSANOW, &tty);
95         if (ret != 0) {
96                 perror("tcsetattr");
97                 return ret;
98         }
99
100         return 0;
101 }
102
103 static int write_serial(int fd, const void *data, size_t size)
104 {
105         int ret;
106
107         ret = write(fd, data, size);
108         if (ret != size) {
109                 fprintf(stderr, "write %d/%zd: ", ret, size);
110                 perror("");
111                 exit(1);
112         }
113
114         return 0;
115 }
116
117 static int read_serial(int fd, void *data, size_t size)
118 {
119         size_t got = 0;
120         int ret;
121
122         while (got < size) {
123                 ret = read(fd, (char *)data + got, size - got);
124                 if (ret <= 0) {
125                         fprintf(stderr, "read %d %zd/%zd: ",
126                                 ret, got, size);
127                         perror("");
128                         exit(1);
129                 }
130                 got += ret;
131         }
132
133         return 0;
134 }
135
136 /* addr arg is always byte address */
137 static void set_addr8(int fd, uint32_t addr)
138 {
139         uint8_t cmd[6] = {
140                 CMD_ADDR, addr >> 16,
141                 CMD_ADDR, addr >> 8,
142                 CMD_ADDR, addr >> 0
143         };
144         write_serial(fd, cmd, sizeof(cmd));
145 }
146
147 static void set_addr16(int fd, uint32_t addr)
148 {
149         set_addr8(fd, addr >> 1);
150 }
151
152 static uint16_t read_bus8(int fd, uint32_t addr)
153 {
154         uint8_t cmd[7] = {
155                 CMD_ADDR, addr >> 16,
156                 CMD_ADDR, addr >> 8,
157                 CMD_ADDR, addr >> 0,
158                 CMD_RD | PAR_SINGE | PAR_MODE8
159         };
160         uint8_t r;
161
162         write_serial(fd, cmd, sizeof(cmd));
163         read_serial(fd, &r, sizeof(r));
164         return r;
165 }
166
167 static uint16_t read_bus16(int fd, uint32_t addr)
168 {
169         uint8_t cmd[7] = {
170                 CMD_ADDR, addr >> 17,
171                 CMD_ADDR, addr >> 9,
172                 CMD_ADDR, addr >> 1,
173                 CMD_RD | PAR_SINGE
174         };
175         uint16_t r;
176
177         write_serial(fd, cmd, sizeof(cmd));
178         read_serial(fd, &r, sizeof(r));
179         return ntohs(r);
180 }
181
182 static void write_bus8(int fd, uint32_t addr, uint16_t d)
183 {
184         uint8_t cmd[8] = {
185                 CMD_ADDR, addr >> 16,
186                 CMD_ADDR, addr >> 8,
187                 CMD_ADDR, addr >> 0,
188                 CMD_WR | PAR_SINGE | PAR_MODE8,
189                 d
190         };
191
192         write_serial(fd, cmd, sizeof(cmd));
193 }
194
195 static void write_bus16(int fd, uint32_t addr, uint16_t d)
196 {
197         uint8_t cmd[9] = {
198                 CMD_ADDR, addr >> 17,
199                 CMD_ADDR, addr >> 9,
200                 CMD_ADDR, addr >> 1,
201                 CMD_WR | PAR_SINGE,
202                 d >> 8, d
203         };
204
205         write_serial(fd, cmd, sizeof(cmd));
206 }
207
208 static void read_block8(int fd, void *dst, uint32_t size)
209 {
210         // PAR_MODE8 does not work, so read as 16bit and throw away MSB
211         uint8_t tmp[0x10000], *d8 = dst;
212         uint8_t cmd[5] = {
213                 CMD_LEN, size >> 8,
214                 CMD_LEN, size >> 0,
215                 CMD_RD | PAR_INC
216         };
217         int i;
218
219         assert(size <= 0x10000);
220         write_serial(fd, cmd, sizeof(cmd));
221         read_serial(fd, dst, size);
222         read_serial(fd, tmp, size);
223
224         for (i = 0; i < size / 2; i++)
225                 d8[i] = d8[i * 2 + 1];
226         d8 += size / 2;
227         for (i = 0; i < size / 2; i++)
228                 d8[i] = tmp[i * 2 + 1];
229 }
230
231 static void read_block16(int fd, void *dst, uint32_t size)
232 {
233         uint8_t cmd[5] = {
234                 CMD_LEN, size >> 9,
235                 CMD_LEN, size >> 1,
236                 CMD_RD | PAR_INC
237         };
238
239         assert(size <= 0x10000);
240         write_serial(fd, cmd, sizeof(cmd));
241         read_serial(fd, dst, size);
242 }
243
244 static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
245 {
246         uint8_t cmd[] = {
247                 // unlock
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,
256                 // program setup
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,
261                 // program data
262                 CMD_ADDR, addr >> 16,
263                 CMD_ADDR, addr >> 8,
264                 CMD_ADDR, addr >> 0,
265                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
266                 CMD_RY
267         };
268
269         assert(info.prog_addr);
270         write_serial(fd, cmd, sizeof(cmd));
271 }
272
273 static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
274 {
275         uint8_t cmd[] = {
276                 // unlock
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,
285                 // program setup
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,
290                 // program data
291                 CMD_ADDR, addr >> 17,
292                 CMD_ADDR, addr >> 9,
293                 CMD_ADDR, addr >> 1,
294                 CMD_WR | PAR_SINGE, d[0], d[1],
295                 CMD_RY
296         };
297
298         assert(info.prog_addr);
299         write_serial(fd, cmd, sizeof(cmd));
300 }
301
302 // -- 8bit+LoROM --
303
304 static uint32_t lorom_rom_addr(uint32_t a)
305 {
306         return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff);
307 }
308
309 static void set_addr8l(int fd, uint32_t a)
310 {
311         set_addr8(fd, lorom_rom_addr(a));
312 }
313
314 static uint16_t read_bus8l(int fd, uint32_t a)
315 {
316         return read_bus8(fd, lorom_rom_addr(a));
317 }
318
319 static void write_bus8l(int fd, uint32_t a, uint16_t d)
320 {
321         write_bus8(fd, lorom_rom_addr(a), d);
322 }
323
324 static void flash_seq_write8l(int fd, uint32_t a, const uint8_t *d)
325 {
326         a = lorom_rom_addr(a);
327         uint8_t cmd[] = {
328                 // unlock
329                 CMD_ADDR, 0,
330                 CMD_ADDR, 0x8a,
331                 CMD_ADDR, 0xaa,
332                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
333                 CMD_ADDR, 0,
334                 CMD_ADDR, 0x85,
335                 CMD_ADDR, 0x55,
336                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
337                 // program setup
338                 CMD_ADDR, 0,
339                 CMD_ADDR, 0x8a,
340                 CMD_ADDR, 0xaa,
341                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
342                 // program data
343                 CMD_ADDR, a >> 16,
344                 CMD_ADDR, a >> 8,
345                 CMD_ADDR, a >> 0,
346                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
347                 CMD_RY
348         };
349
350         write_serial(fd, cmd, sizeof(cmd));
351 }
352
353 // -- 8bit+LoROM+adapter --
354
355 static uint32_t do_flipflops(int fd, uint32_t a)
356 {
357         static uint32_t abits_now = ~0u; // A23, A22, A21
358         uint32_t abits = (a >> 21) & 7;
359
360         if (abits != abits_now) {
361                 // printf("flipflops: %x->%x\n", abits_now, abits);
362                 write_bus16(fd, 0xa13000, abits);
363                 abits_now = abits;
364         }
365         return a & 0x1fffff;
366 }
367
368 static void set_addr8la(int fd, uint32_t a)
369 {
370         set_addr8(fd, do_flipflops(fd, lorom_rom_addr(a)));
371 }
372
373 static uint16_t read_bus8la(int fd, uint32_t a)
374 {
375         return read_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)));
376 }
377
378 static void write_bus8la(int fd, uint32_t a, uint16_t d)
379 {
380         write_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)), d);
381 }
382
383 static void flash_seq_write8la(int fd, uint32_t a, const uint8_t *d)
384 {
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));
390         uint8_t cmd[] = {
391                 // unlock
392                 CMD_ADDR, 0,
393                 CMD_ADDR, 0x8a,
394                 CMD_ADDR, 0xaa,
395                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
396                 CMD_ADDR, 0,
397                 CMD_ADDR, 0x85,
398                 CMD_ADDR, 0x55,
399                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
400                 // program setup
401                 CMD_ADDR, 0,
402                 CMD_ADDR, 0x8a,
403                 CMD_ADDR, 0xaa,
404                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
405                 // program data
406                 CMD_ADDR, a >> 16,
407                 CMD_ADDR, a >> 8,
408                 CMD_ADDR, a >> 0,
409                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
410                 CMD_RY
411         };
412
413         write_serial(fd, cmd, sizeof(cmd));
414 }
415
416 // -- 8bit+LoROM+adapter+sram --
417
418 static uint32_t lorom_sram_addr(uint32_t a)
419 {
420         return a | 0x600000;
421 }
422
423 static void set_addr8las(int fd, uint32_t a)
424 {
425         set_addr8(fd, do_flipflops(fd, lorom_sram_addr(a)));
426 }
427
428 static uint16_t read_bus8las(int fd, uint32_t a)
429 {
430         return read_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)));
431 }
432
433 static void write_bus8las(int fd, uint32_t a, uint16_t d)
434 {
435         write_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)), d);
436 }
437
438 static void flash_seq_write8las(int fd, uint32_t a, const uint8_t *d)
439 {
440 }
441
442 // -- 8bit+adapter --
443
444 static void set_addr8a(int fd, uint32_t a)
445 {
446         set_addr8(fd, do_flipflops(fd, a));
447 }
448
449 static uint16_t read_bus8a(int fd, uint32_t a)
450 {
451         return read_bus8(fd, do_flipflops(fd, a));
452 }
453
454 static void write_bus8a(int fd, uint32_t a, uint16_t d)
455 {
456         write_bus8(fd, do_flipflops(fd, a), d);
457 }
458
459 static void flash_seq_write8a(int fd, uint32_t a, const uint8_t *d)
460 {
461         // no flipflop clearing, see flash_seq_write8la
462         a = do_flipflops(fd, a);
463         uint8_t cmd[] = {
464                 // unlock
465                 CMD_ADDR, 0,
466                 CMD_ADDR, 0x8a,
467                 CMD_ADDR, 0xaa,
468                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
469                 CMD_ADDR, 0,
470                 CMD_ADDR, 0x85,
471                 CMD_ADDR, 0x55,
472                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
473                 // program setup
474                 CMD_ADDR, 0,
475                 CMD_ADDR, 0x8a,
476                 CMD_ADDR, 0xaa,
477                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
478                 // program data
479                 CMD_ADDR, a >> 16,
480                 CMD_ADDR, a >> 8,
481                 CMD_ADDR, a >> 0,
482                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
483                 CMD_RY
484         };
485
486         write_serial(fd, cmd, sizeof(cmd));
487 }
488
489 // -- 8bit+adapter+nocart --
490
491 static void set_addr8an(int fd, uint32_t a)
492 {
493         set_addr8(fd, do_flipflops(fd, a) | 0x200000);
494 }
495
496 static uint16_t read_bus8an(int fd, uint32_t a)
497 {
498         return read_bus8(fd, do_flipflops(fd, a) | 0x200000);
499 }
500
501 static void write_bus8an(int fd, uint32_t a, uint16_t d)
502 {
503         write_bus8(fd, do_flipflops(fd, a) | 0x200000, d);
504 }
505
506 static void flash_seq_write8an(int fd, uint32_t a, const uint8_t *d)
507 {
508 }
509
510 #define N0 ""
511 #define N1 "8bit"
512 #define N2 "8bit+LoROM"
513 #define N3 "8bit+LoROM+adapter"
514 #define N4 "8bit+LoROM+adapter+sram"
515 #define N5 "8bit+adapter (use '-a 0x400000' for HiROM)"
516 #define N6 "8bit+adapter+nocart"
517 static const struct iof
518 {
519         const char *name;
520         int      addrs_remapped;
521         void     (*set_addr)(int fd, uint32_t addr);
522         uint16_t (*read_bus)(int fd, uint32_t addr);
523         void     (*write_bus)(int fd, uint32_t addr, uint16_t d);
524         void     (*read_block)(int fd, void *dst, uint32_t size);
525         void     (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
526 }
527 io_ops[] =
528 {
529         { N0, 0, set_addr16,   read_bus16,   write_bus16,   read_block16, flash_seq_write16   },
530         { N1, 0, set_addr8,    read_bus8,    write_bus8,    read_block8,  flash_seq_write8    },
531         { N2, 1, set_addr8l,   read_bus8l,   write_bus8l,   read_block8,  flash_seq_write8l   },
532         { N3, 1, set_addr8la,  read_bus8la,  write_bus8la,  read_block8,  flash_seq_write8la  },
533         { N4, 0, set_addr8las, read_bus8las, write_bus8las, read_block8,  flash_seq_write8las },
534         { N5, 0, set_addr8a,   read_bus8a,   write_bus8a,   read_block8,  flash_seq_write8a   },
535         { N6, 0, set_addr8an,  read_bus8an,  write_bus8an,  read_block8,  flash_seq_write8an  },
536 };
537
538 static const struct iof *io = &io_ops[0];
539
540 static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
541 {
542         // unlock
543         assert(info.prog_addr);
544         io->write_bus(fd, info.prog_addr >> 0, 0xaa);
545         io->write_bus(fd, info.prog_addr >> 1, 0x55);
546
547         io->write_bus(fd, info.prog_addr >> 0, cmd);
548         return io->read_bus(fd, addr);
549 }
550
551 static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d)
552 {
553         // printf("erase %06x\n", addr);
554         assert(info.prog_addr);
555         io->write_bus(fd, info.prog_addr >> 0, 0xaa);
556         io->write_bus(fd, info.prog_addr >> 1, 0x55);
557         io->write_bus(fd, info.prog_addr >> 0, 0x80);
558
559         io->write_bus(fd, info.prog_addr >> 0, 0xaa);
560         io->write_bus(fd, info.prog_addr >> 1, 0x55);
561         io->write_bus(fd, addr, d);
562 }
563
564 static void flash_seq_erase(int fd, uint32_t addr)
565 {
566         flash_seq_erase_d(fd, addr, 0x30);
567 }
568
569 static void flash_seq_erase_full(int fd)
570 {
571         flash_seq_erase_d(fd, info.prog_addr, 0x10);
572 }
573
574 // status wait + dummy read to cause a wait?
575 static uint16_t ry_read(int fd)
576 {
577         uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
578         uint16_t rv = 0;
579
580         write_serial(fd, cmd, sizeof(cmd));
581         read_serial(fd, &rv, sizeof(rv));
582         return ntohs(rv);
583 }
584
585 static void set_delay(int fd, uint8_t delay)
586 {
587         uint8_t cmd[2] = { CMD_DELAY, delay };
588
589         write_serial(fd, cmd, sizeof(cmd));
590 }
591
592 static void read_info(int fd)
593 {
594         static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
595         uint16_t resp_cfi[3], sst_mid = ~0;
596         uint32_t total = 0;
597         uint32_t i, a;
598
599
600         // see if this chip understands CFI (common flash interface)
601         io->write_bus(fd, 0xaa, 0x98);
602         resp_cfi[0] = io->read_bus(fd, 0x20);
603         resp_cfi[1] = io->read_bus(fd, 0x22);
604         resp_cfi[2] = io->read_bus(fd, 0x24);
605         if (memcmp(resp_cfi, qry, sizeof(resp_cfi)) == 0)
606         {
607                 info.size = 1u << io->read_bus(fd, 0x4e);
608                 info.region_cnt = io->read_bus(fd, 0x58);
609                 assert(0 < info.region_cnt && info.region_cnt <= 4);
610                 for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
611                         info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
612                         info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
613                         info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
614                         info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
615                         info.region[i].start = total;
616                         info.region[i].size =
617                                 info.region[i].block_size * info.region[i].block_count;
618                         assert(info.region[i].size);
619                         total += info.region[i].size;
620                 }
621                 if (info.size != total)
622                         fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
623
624                 info.prog_addr = 0xaaa;
625                 io->write_bus(fd, 0, 0xf0);
626                 info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
627                 info.did = io->read_bus(fd, 2);
628         }
629         else
630         {
631                 // try the SST protocol
632                 info.prog_addr = 0x5555;
633                 sst_mid = flash_seq_r(fd, 0x90, 0);
634                 if (sst_mid == 0xbf)
635                 {
636                         info.mid = sst_mid;
637                         info.did = io->read_bus(fd, 0x01);
638                         switch (info.did) {
639                         case 0xb5:
640                         case 0xb6:
641                         case 0xb7:
642                                 info.size = 128 * 1024 << (info.did - 0xb5);
643                                 break;
644                         default:
645                                 fprintf(stderr, "unrecognized SST device %02x\n", info.did);
646                                 exit(1);
647                         }
648                         info.region_cnt = 1;
649                         info.region[0].block_count = info.size / 4096;
650                         info.region[0].block_size = 4096;
651                         info.region[0].start = 0;
652                         info.region[0].size = info.size;
653                 }
654                 else
655                         info.prog_addr = 0;
656         }
657
658         io->write_bus(fd, 0, 0xf0); // flash reset
659
660         if (info.prog_addr == 0) {
661                 fprintf(stderr, "unable to identify the flash chip :(\n");
662                 fprintf(stderr, "CFI response: %02x %02x %02x\n",
663                         resp_cfi[0], resp_cfi[1], resp_cfi[2]);
664                 fprintf(stderr, "SST MID: %02x\n", sst_mid);
665                 exit(1);
666         }
667
668         printf("Flash info:\n");
669         printf("Manufacturer ID: %04x\n", info.mid);
670         printf("Device ID: %04x\n", info.did);
671         printf("size: %u\n", info.size);
672         printf("Erase Block Regions: %u\n", info.region_cnt);
673         for (i = 0; i < info.region_cnt; i++)
674                 printf("  %5u x %u\n", info.region[i].block_size,
675                         info.region[i].block_count);
676 }
677
678 static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
679 {
680         uint32_t i, base, faddr;
681
682         assert(info.region_cnt);
683         assert(info.size);
684
685         // get a flash address to allow mapper hardware
686         faddr = addr & (info.size - 1);
687         base = addr & ~(info.size - 1);
688
689         for (i = 0; i < info.region_cnt; i++) {
690                 if (info.region[i].start <= faddr
691                     && faddr < info.region[i].start + info.region[i].size)
692                 {
693                         uint32_t blk = (faddr - info.region[i].start)
694                                         / info.region[i].block_size
695                                         + blk_offset;
696                         return base + info.region[i].start
697                                 + blk * info.region[i].block_size;
698                 }
699         }
700
701         fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
702         exit(1);
703 }
704
705 static void print_progress(uint32_t done, uint32_t total)
706 {
707         int i, step;
708
709         printf("\r%06x/%06x |", done, total);
710
711         step = (total + 19) / 20;
712         for (i = step; i <= total; i += step)
713                 fputc(done >= i ? '=' : '-', stdout);
714         printf("| %3d%%", done * 100 / total);
715         fflush(stdout);
716         if (done >= total)
717                 fputc('\n', stdout);
718 }
719
720 static FILE *open_prep_read(const char *fname, long *size)
721 {
722         FILE *f = fopen(fname, "rb");
723         if (!f) {
724                 fprintf(stderr, "fopen %s: ", fname);
725                 perror("");
726                 exit(1);
727         }
728         if (*size <= 0) {
729                 fseek(f, 0, SEEK_END);
730                 *size = ftell(f);
731                 fseek(f, 0, SEEK_SET);
732         }
733         if (*size <= 0) {
734                 fprintf(stderr, "size of %s is %ld\n", fname, *size);
735                 exit(1);
736         }
737         return f;
738 }
739
740 static const char *portname =
741 #ifdef __APPLE__
742         "/dev/cu.usbserial-AL0254JM";
743 #else
744         "/dev/ttyUSB0";
745 #endif
746
747 static void usage(const char *argv0)
748 {
749         size_t i;
750
751         printf("usage:\n"
752                 "%s [options]\n"
753                 "  -d <ttydevice>      (default %s)\n"
754                 "  -r <file> [size]    dump the cart (default 4MB)\n"
755                 "  -w <file> [size]    program the flash (def. file size)\n"
756                 "  -s <file> [size]    simple write (SRAM, etc, def. file size)\n"
757                 "  -e <size>           erase (rounds to block size); can specify 'full'\n"
758                 "  -a <start_address>  read/write start address (default 0)\n"
759                 "  -m <n>              use an address mapper n, one of:\n"
760                 , argv0, portname);
761         for (i = 1; i < sizeof(io_ops) / sizeof(io_ops[0]); i++)
762                 printf(
763                 "                       %zd: %s\n", i, io_ops[i].name);
764         printf( "  -v                  verify written data\n"
765                 "  -i                  get info about the flash chip\n");
766         exit(1);
767 }
768
769 static void invarg(int argc, char *argv[], int arg)
770 {
771         if (arg < argc)
772                 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
773         else
774                 fprintf(stderr, "missing required argument %d\n", arg);
775         exit(1);
776 }
777
778 static void *getarg(int argc, char *argv[], int arg)
779 {
780         if (arg >= argc)
781                 invarg(argc, argv, arg);
782         return argv[arg];
783 }
784
785 static long getarg_l(int argc, char *argv[], int arg)
786 {
787         char *endp = NULL;
788         long r;
789
790         if (arg >= argc)
791                 invarg(argc, argv, arg);
792         r = strtol(argv[arg], &endp, 0);
793         if (endp == NULL || *endp != 0)
794                 invarg(argc, argv, arg);
795         return r;
796 }
797
798 // 32K to easily handle SNES LoROM
799 static uint8_t g_block[0x8000];
800 static uint8_t g_block2[sizeof(g_block)];
801
802 int main(int argc, char *argv[])
803 {
804         const char *fname_w = NULL;
805         const char *fname_r = NULL;
806         const char *fname_ws = NULL;
807         long size_w = 0;
808         long size_r = 0;
809         long size_ws = 0;
810         long size_e = 0;
811         long size_v = 0;
812         long len, address_in = 0;
813         long a, a_blk, end;
814         int do_info = 0;
815         int do_verify = 0;
816         int write_step = 2;
817         FILE *f_w = NULL;
818         FILE *f_r = NULL;
819         FILE *f_ws = NULL;
820         uint8_t id[2] = { 0, 0 };
821         uint8_t cmd;
822         uint16_t rv;
823         int arg = 1;
824         int fd;
825
826         if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
827                 usage(argv[0]);
828
829         for (arg = 1; arg < argc; arg++) {
830                 if (!strcmp(argv[arg], "-d")) {
831                         portname = getarg(argc, argv, ++arg);
832                         continue;
833                 }
834                 if (!strcmp(argv[arg], "-r")) {
835                         fname_r = getarg(argc, argv, ++arg);
836                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
837                                 size_r = getarg_l(argc, argv, ++arg);
838                                 if (size_r <= 0)
839                                         invarg(argc, argv, arg);
840                         }
841                         continue;
842                 }
843                 if (!strcmp(argv[arg], "-w")) {
844                         fname_w = getarg(argc, argv, ++arg);
845                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
846                                 size_w = getarg_l(argc, argv, ++arg);
847                                 if (size_w <= 0)
848                                         invarg(argc, argv, arg);
849                         }
850                         continue;
851                 }
852                 if (!strcmp(argv[arg], "-s")) {
853                         fname_ws = getarg(argc, argv, ++arg);
854                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
855                                 size_ws = getarg_l(argc, argv, ++arg);
856                                 if (size_ws <= 0)
857                                         invarg(argc, argv, arg);
858                         }
859                         continue;
860                 }
861                 if (!strcmp(argv[arg], "-a")) {
862                         address_in = getarg_l(argc, argv, ++arg);
863                         if (address_in < 0)
864                                 invarg(argc, argv, arg);
865                         continue;
866                 }
867                 if (!strcmp(argv[arg], "-e")) {
868                         arg++;
869                         if (!strcmp(getarg(argc, argv, arg), "full"))
870                                 size_e = -1;
871                         else {
872                                 size_e = getarg_l(argc, argv, arg);
873                                 if (size_e <= 0)
874                                         invarg(argc, argv, arg);
875                         }
876                         continue;
877                 }
878                 if (!strcmp(argv[arg], "-m")) {
879                         long v = getarg_l(argc, argv, ++arg);
880                         if ((size_t)v >= sizeof(io_ops) / sizeof(io_ops[0]))
881                                 invarg(argc, argv, arg);
882                         io = &io_ops[v];
883                         if (v != 0)
884                                 write_step = 1;
885                         continue;
886                 }
887                 if (!strcmp(argv[arg], "-v")) {
888                         do_verify = 1;
889                         continue;
890                 }
891                 if (!strcmp(argv[arg], "-i")) {
892                         do_info = 1;
893                         continue;
894                 }
895                 invarg(argc, argv, arg);
896         }
897
898         if (fname_r && size_r == 0)
899                 size_r = 0x400000;
900
901         if (fname_w) {
902                 f_w = open_prep_read(fname_w, &size_w);
903                 if (size_e == 0 && io->addrs_remapped)
904                         size_e = -1;
905                 if (size_e != -1 && size_e < size_w)
906                         size_e = size_w;
907                 if (do_verify)
908                         size_v = size_w;
909         }
910
911         fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
912         if (fd < 0) {
913                 fprintf(stderr, "open %s: ", portname);
914                 perror("");
915                 return 1;
916         }
917
918         if (fname_ws)
919                 f_ws = open_prep_read(fname_ws, &size_ws);
920
921         setup(fd);
922
923         cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
924         write_serial(fd, &cmd, sizeof(cmd));
925         read_serial(fd, id, sizeof(id));
926         if (id[0] != id[1] || id[0] == 0) {
927                 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
928                 return 1;
929         }
930         printf("flashkit id: %02x\n", id[0]);
931
932         set_delay(fd, 1);
933
934         if (do_info || size_e || f_w)
935                 io->write_bus(fd, 0, 0xf0); // flash reset
936
937         if (do_info || size_e)
938                 read_info(fd);
939
940         if (size_e == -1) {
941                 printf("performing full erase..."); fflush(stdout);
942                 flash_seq_erase_full(fd);
943                 rv = ry_read(fd);
944                 if (rv != 0xffff) {
945                         fprintf(stderr, "\nerase error: %04x\n", rv);
946                         return 1;
947                 }
948                 printf(" done.\n");
949         }
950         else if (size_e) {
951                 // set_delay(fd, 0); // ?
952                 a_blk = get_block_addr(address_in, 0);
953                 end = address_in + size_e;
954
955                 printf("erasing %ld bytes:\n", size_e);
956                 print_progress(0, size_e);
957                 for (a = address_in; a < end; ) {
958                         flash_seq_erase(fd, a_blk);
959                         rv = ry_read(fd);
960                         if (rv != 0xffff) {
961                                 fprintf(stderr, "\nerase error: %lx %04x\n",
962                                         a_blk, rv);
963                                 return 1;
964                         }
965
966                         a_blk = get_block_addr(a_blk, 1);
967                         a += a_blk - a;
968                         print_progress(a - address_in, size_e);
969                 }
970         }
971         if (f_w != NULL) {
972                 uint8_t b[2];
973                 set_delay(fd, 0);
974                 printf("flashing %ld bytes:\n", size_w);
975                 for (a = 0; a < size_w; a += write_step) {
976                         ssize_t r;
977
978                         b[1] = 0xff;
979                         len = min(size_w - a, write_step);
980                         r = fread(b, 1, len, f_w);
981                         if (r != len) {
982                                 perror("\nfread");
983                                 return 1;
984                         }
985                         io->flash_seq_write(fd, address_in + a, b);
986
987                         if (!(a & 0x3ff))
988                                 print_progress(a, size_w);
989                 }
990                 print_progress(a, size_w);
991                 rv = ry_read(fd);
992                 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
993                         fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
994                                 rv, b[0], b[1]);
995                 rewind(f_w);
996                 set_delay(fd, 1);
997         }
998         if (f_ws != NULL) {
999                 printf("writing %ld bytes:\n", size_ws);
1000                 for (a = 0; a < size_ws; a += write_step) {
1001                         uint16_t b = 0xffff;
1002                         ssize_t r;
1003
1004                         len = min(size_ws - a, write_step);
1005                         r = fread(&b, 1, len, f_ws);
1006                         if (r != len) {
1007                                 perror("\nfread");
1008                                 return 1;
1009                         }
1010                         if (write_step == 2)
1011                                 b = htons(b);
1012                         io->write_bus(fd, address_in + a, b);
1013
1014                         if (!(a & 0x3ff))
1015                                 print_progress(a, size_ws);
1016                 }
1017                 print_progress(a, size_ws);
1018         }
1019
1020         if (fname_r || size_v) {
1021                 long blks, blks_v, done, verify_diff = 0;
1022
1023                 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
1024                 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
1025                 blks = max(blks, blks_v);
1026                 if (fname_r) {
1027                         f_r = fopen(fname_r, "wb");
1028                         if (!f_r) {
1029                                 fprintf(stderr, "fopen %s: ", fname_r);
1030                                 perror("");
1031                                 return 1;
1032                         }
1033                 }
1034
1035                 printf("reading %ld bytes:\n", max(size_r, size_v));
1036                 print_progress(0, blks * sizeof(g_block));
1037                 a_blk = -1;
1038                 for (done = 0; done < size_r || done < size_v; ) {
1039                         a = address_in + done;
1040                         if (io->addrs_remapped || (a >> 21) != a_blk) {
1041                                 a_blk = a >> 21;
1042                                 io->set_addr(fd, a);
1043                         }
1044                         io->read_block(fd, g_block, sizeof(g_block));
1045                         if (f_r && done < size_r) {
1046                                 len = min(size_r - done, sizeof(g_block));
1047                                 if (fwrite(g_block, 1, len, f_r) != len) {
1048                                         perror("fwrite");
1049                                         return 1;
1050                                 }
1051                         }
1052                         if (done < size_v) {
1053                                 len = min(size_v - done, sizeof(g_block));
1054                                 if (fread(g_block2, 1, len, f_w) != len) {
1055                                         perror("fread");
1056                                         return 1;
1057                                 }
1058                                 verify_diff |= memcmp(g_block, g_block2, len);
1059                         }
1060                         done += sizeof(g_block);
1061                         print_progress(done, blks * sizeof(g_block));
1062                 }
1063                 if (verify_diff) {
1064                         fprintf(stderr, "verify FAILED\n");
1065                         return 1;
1066                 }
1067         }
1068         if (f_r)
1069                 fclose(f_r);
1070         if (f_w)
1071                 fclose(f_w);
1072
1073         return 0;
1074 }