-
Notifications
You must be signed in to change notification settings - Fork 1
/
flash4.c
745 lines (640 loc) · 26 KB
/
flash4.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "libcpm.h"
#include "z180dma.h"
#include "bankswitch.h"
#include "detectcpu.h"
#include "buffers.h"
#include "calling.h"
typedef enum {
ACTION_UNKNOWN,
ACTION_READ,
ACTION_WRITE,
ACTION_VERIFY
} action_t;
static action_t action = ACTION_UNKNOWN;
typedef enum {
ACCESS_NONE,
ACCESS_AUTO,
// through BIOS interfaces:
ACCESS_ROMWBW_OLD, // prior to v2.6
ACCESS_ROMWBW_26, // v2.6 and later
ACCESS_UNABIOS,
// direct hardware poking:
ACCESS_Z180DMA,
ACCESS_P112,
ACCESS_N8VEM_SBC,
} access_t;
static access_t access = ACCESS_AUTO;
/* the strategy flags describe quirks for programming particular chips */
#define ST_NORMAL (0x00) /* default: no special strategy required */
#define ST_PROGRAM_SECTORS (0x01) /* bit 0: program sector (not byte) at a time (Atmel AT29C style) */
#define ST_ERASE_CHIP (0x02) /* bit 1: erase whole chip (sector_count must be exactly 1) instead of individual sectors */
typedef struct {
unsigned int chip_id;
char *chip_name;
unsigned int sector_size; /* in multiples of 128 bytes */
unsigned int sector_count;
unsigned char strategy;
} flashrom_chip_t;
static flashrom_chip_t flashrom_chips[] = {
{ 0x0120, "29F010", 128, 8, ST_NORMAL },
{ 0x01A4, "29F040", 512, 8, ST_NORMAL },
{ 0x1F04, "AT49F001NT", 1024, 1, ST_ERASE_CHIP }, /* multiple but unequal sized sectors */
{ 0x1F05, "AT49F001N", 1024, 1, ST_ERASE_CHIP }, /* multiple but unequal sized sectors */
{ 0x1F07, "AT49F002N", 2048, 1, ST_ERASE_CHIP }, /* multiple but unequal sized sectors */
{ 0x1F08, "AT49F002NT", 2048, 1, ST_ERASE_CHIP }, /* multiple but unequal sized sectors */
{ 0x1F13, "AT49F040", 4096, 1, ST_ERASE_CHIP }, /* single sector device */
{ 0x1F5D, "AT29C512", 1, 512, ST_PROGRAM_SECTORS },
{ 0x1FA4, "AT29C040", 2, 2048, ST_PROGRAM_SECTORS },
{ 0x1FD5, "AT29C010", 1, 1024, ST_PROGRAM_SECTORS },
{ 0x1FDA, "AT29C020", 2, 1024, ST_PROGRAM_SECTORS },
{ 0x2020, "M29F010", 128, 8, ST_NORMAL },
{ 0x20E2, "M29F040", 512, 8, ST_NORMAL },
{ 0x37A4, "A29010B", 256, 4, ST_NORMAL },
{ 0x3786, "A29040B", 512, 8, ST_NORMAL },
{ 0xBFD5, "39VF010", 32, 32, ST_NORMAL },
{ 0xBFD6, "39VF020", 32, 64, ST_NORMAL },
{ 0xBFD7, "39VF040", 32, 128, ST_NORMAL },
{ 0xBFB5, "39SF010", 32, 32, ST_NORMAL },
{ 0xBFB6, "39SF020", 32, 64, ST_NORMAL },
{ 0xBFB7, "39SF040", 32, 128, ST_NORMAL },
{ 0xC2A4, "MX29F040", 512, 8, ST_NORMAL },
/* terminate the list */
{ 0x0000, NULL, 0, 0, 0 }
};
/* special ROM entry for ROM/EPROM/EEPROM with /ROM switch */
static flashrom_chip_t rom_chip = { 0x0000, "rom", 8, 512, 0 }; /* 512 x 1KB "sectors" */
static flashrom_chip_t *flashrom_type = NULL;
static bool verbose = false;
static bool chip_count_forced = false;
static unsigned int chip_count = 1; /* number of chips */
static unsigned long flashrom_chip_size; /* individual chip size, in bytes */
static unsigned long flashrom_size; /* total size of all chips, in bytes; always equal to chip_count * flashrom_chip_size */
static unsigned long flashrom_sector_size; /* chip sector size, in bytes */
/* function pointers set at runtime to switch between bank switching and Z180 DMA engine */
void (*flashrom_chip_write)(unsigned long address, unsigned char value) CALLING = NULL;
unsigned char (*flashrom_chip_read)(unsigned long address) CALLING = NULL;
void (*flashrom_block_read)(unsigned long address, unsigned char *buffer, unsigned int length) CALLING = NULL;
bool (*flashrom_block_verify)(unsigned long address, unsigned char *buffer, unsigned int length) CALLING = NULL;
void (*flashrom_block_write)(unsigned long address, unsigned char *buffer, unsigned int length) CALLING = NULL;
/* useful to provide some feedback that something is actually happening with large-sector devices */
#define SPINNER_LENGTH 4
static char spinner_char[SPINNER_LENGTH] = {'|', '/', '-', '\\'};
static unsigned char spinner_pos=0;
char spinner(void)
{
spinner_pos = (spinner_pos + 1) % SPINNER_LENGTH;
return spinner_char[spinner_pos];
}
void help(void)
{
puts("\nSyntax:\n\tFLASH4 READ filename [options]\n" \
"\tFLASH4 VERIFY filename [options]\n" \
"\tFLASH4 WRITE filename [options]\n\n" \
"Options (access method is auto-detected by default)\n" \
"\t/V\t\tVerbose details about verify/program process\n" \
"\t/PARTIAL\tAllow flashing a large ROM from a smaller image file\n" \
"\t/ROM\t\tAllow read-only use of unknown chip types\n" \
"\t/Z180DMA\tForce Z180 DMA engine\n" \
"\t/UNABIOS\tForce UNA BIOS bank switching\n" \
"\t/ROMWBW\t\tForce RomWBW (v2.6+) bank switching\n" \
"\t/ROMWBWOLD\tForce RomWBW (v2.5 and earlier) bank switching\n" \
"\t/P112\t\tForce P112 bank switching\n" \
"\t/2 ... /9\tForce programming multiple devices");
cpm_abort();
}
void abort_and_solicit_report(void)
{
puts("Please email will@sowerbutts.com if you would like support for your\nsystem added to this program.");
cpm_abort();
}
unsigned long flashrom_sector_address(unsigned int sector)
{
return flashrom_sector_size * ((unsigned long)sector);
}
void flashrom_wait_toggle_bit(unsigned long address)
{
unsigned char a, b, matches=0;
/* wait for toggle bit to indicate completion */
/* data sheet says two additional reads are required to match
* after the first match */
do{
a = flashrom_chip_read(address);
b = flashrom_chip_read(address);
if(a==b)
matches++;
else
matches=0;
}while(matches < 2);
}
unsigned long chip_base_address(unsigned long address)
{
return address & (~0x7FFFUL);
}
void flashrom_chip_erase(unsigned long base_address)
{
base_address = chip_base_address(base_address);
flashrom_chip_write(base_address | 0x5555, 0xAA);
flashrom_chip_write(base_address | 0x2AAA, 0x55);
flashrom_chip_write(base_address | 0x5555, 0x80);
flashrom_chip_write(base_address | 0x5555, 0xAA);
flashrom_chip_write(base_address | 0x2AAA, 0x55);
flashrom_chip_write(base_address | 0x5555, 0x10);
flashrom_wait_toggle_bit(base_address);
}
void flashrom_sector_erase(unsigned long address)
{
unsigned long base_address;
base_address = chip_base_address(address);
flashrom_chip_write(base_address | 0x5555, 0xAA);
flashrom_chip_write(base_address | 0x2AAA, 0x55);
flashrom_chip_write(base_address | 0x5555, 0x80);
flashrom_chip_write(base_address | 0x5555, 0xAA);
flashrom_chip_write(base_address | 0x2AAA, 0x55);
flashrom_chip_write(address, 0x30);
flashrom_wait_toggle_bit(address);
}
/* this is used only for programming atmel 29C parts which have a combined erase/program cycle */
void flashrom_sector_program(unsigned long address, unsigned char *buffer, unsigned int count)
{
unsigned long prog_address;
prog_address = chip_base_address(address);
flashrom_chip_write(prog_address | 0x5555, 0xAA);
flashrom_chip_write(prog_address | 0x2AAA, 0x55);
flashrom_chip_write(prog_address | 0x5555, 0xA0); /* software data protection activated */
prog_address = address;
while(count--){
flashrom_chip_write(prog_address++, *(buffer++));
}
flashrom_wait_toggle_bit(address);
}
void delay10ms(void)
{
unsigned int a, b=0;
/* delay for around 10msec (calibrated for ~40 MHz Z180, delay will be longer on slower CPUs) */
for(a=0; a<18000; a++)
b++;
}
unsigned int flashrom_read_id_word(unsigned long base_address)
{
return ((unsigned int)flashrom_chip_read(base_address) << 8) | flashrom_chip_read(base_address | 0x0001);
}
unsigned int flashrom_identify_device(unsigned long base_address)
{
unsigned int flashrom_device_id;
/* put the flash memory into identify mode */
flashrom_chip_write(base_address | 0x5555, 0xAA);
flashrom_chip_write(base_address | 0x2AAA, 0x55);
flashrom_chip_write(base_address | 0x5555, 0x90);
/* atmel 29C parts require a pause for 10msec at this point */
delay10ms();
/* load manufacturer and device IDs */
flashrom_device_id = flashrom_read_id_word(base_address);
/* put the flash memory back into read mode */
flashrom_chip_write(base_address | 0x5555, 0xF0);
/* atmel 29C parts require a pause for 10msec at this point */
delay10ms();
return flashrom_device_id;
}
void flashrom_setup(void)
{
if(flashrom_type){
flashrom_sector_size = (unsigned long)flashrom_type->sector_size * 128L;
flashrom_chip_size = flashrom_sector_size * (unsigned long)flashrom_type->sector_count;
}else{
/* reset parameters */
flashrom_chip_size = 0;
flashrom_sector_size = 0;
}
flashrom_size = flashrom_chip_size * (unsigned long)chip_count;
}
bool flashrom_identify(void)
{
unsigned int flashrom_device_id;
unsigned int flashrom_mem_contents;
unsigned long address = 0;
int chip;
flashrom_mem_contents = flashrom_read_id_word(address);
flashrom_device_id = flashrom_identify_device(address);
printf("Flash memory chip ID is 0x%04X: ", flashrom_device_id);
for(flashrom_type = flashrom_chips; flashrom_type->chip_id; flashrom_type++)
if(flashrom_type->chip_id == flashrom_device_id)
break;
if(!flashrom_type->chip_id){
/* we scanned the whole table without finding our chip */
flashrom_type = NULL;
puts("Unknown flash chip.");
/* some boards have jumpers to switch /WE between being tied high or connected to CPU /WR */
if(flashrom_device_id == flashrom_mem_contents)
puts("Chip ID matches memory contents: Check ROM's Write Enable pin is connected.");
return false;
}
flashrom_setup();
printf("%s (%dKB)\n", flashrom_type->chip_name, flashrom_type->sector_size * flashrom_type->sector_count / 8);
/* RomWBW reports the number of 32KB ROM banks, allowing us to auto-detect
when multiple chips are installed. Do this only if the user has not
manually specified multiple chips. rom_bank_count is zero if the BIOS
cannot report the number of ROM banks. */
if(rom_bank_count && !chip_count_forced){
chip = rom_bank_count / (int)(flashrom_chip_size >> 15);
if(chip > 1){
printf("BIOS reports %d x 32KB ROM banks: %d chips\n", rom_bank_count, chip);
chip_count = chip;
flashrom_setup();
}
}
/* check any additional chips are of the same type */
for(chip=1; chip < chip_count; chip++){
address += flashrom_chip_size;
flashrom_device_id = flashrom_identify_device(address);
if(verbose)
printf("Chip at 0x%06lX has ID %04X\n", address, flashrom_device_id);
if(flashrom_device_id != flashrom_type->chip_id){
printf("Mismatched chip types: Flash chip %d has ID 0x%04X\n" \
"This program requires all flash chips to be of the same type.\n",
1+chip, flashrom_device_id);
cpm_abort();
}
}
return true;
}
void flashrom_read(cpm_fcb *outfile)
{
unsigned long offset;
unsigned int block;
unsigned char r;
offset = 0;
block = 0;
while(offset < flashrom_size){
if(!(offset & 0x3FF))
printf("\rRead %d/%dKB ", (int)(offset >> 10), (int)(flashrom_size >> 10));
flashrom_block_read(offset, rombuffer, CPM_BLOCK_SIZE);
r = cpm_f_write_random(outfile, block++, rombuffer);
if(r){
printf("cpm_f_write()=%d\n", r);
cpm_abort();
}
offset += CPM_BLOCK_SIZE;
}
puts("\rRead complete.");
}
bool read_data_from_file(cpm_fcb *infile, unsigned int block, unsigned int count)
{
unsigned char *ptr, r;
ptr = filebuffer;
/* give the user something pretty to watch */
if(!verbose){
putchar('\x08');
putchar(spinner());
}
while(count--){
r = cpm_f_read_random(infile, block++, ptr);
switch(r){
case 1:
case 4:
return true;
case 0:
break;
default:
printf("cpm_f_read()=%d\n", r);
cpm_abort();
}
ptr += CPM_BLOCK_SIZE;
}
return false; /* not EOF */
}
unsigned int flashrom_verify_and_write(cpm_fcb *infile, bool perform_write)
{
unsigned int sector_count, sector=0, block=0, subsector=0, mismatch=0;
unsigned int subsectors_per_sector, blocks_per_subsector, bytes_per_subsector;
unsigned long flash_address;
bool verify_okay;
bool eof = false;
/* We verify or program at most one sector at once. If a sector is larger
than our memory buffer for data read from disk, we divide it up into
multiple "subsectors". If a sector already contains the desired data we
avoid reprogramming it (thanks to John Coffman for this super idea). */
subsectors_per_sector = flashrom_type->sector_size / FILEBUFFER_BLOCKS;
if(subsectors_per_sector == 0){
subsectors_per_sector = 1;
blocks_per_subsector = flashrom_type->sector_size;
}else{
blocks_per_subsector = FILEBUFFER_BLOCKS;
/* sanity check */
if(flashrom_type->sector_size % blocks_per_subsector){
printf("Unexpected sector size %d\n", flashrom_type->sector_size);
abort_and_solicit_report();
}
}
bytes_per_subsector = blocks_per_subsector * CPM_BLOCK_SIZE;
if( ((flashrom_type->strategy & ST_ERASE_CHIP) && flashrom_type->sector_count != 1) ||
((flashrom_type->strategy & ST_PROGRAM_SECTORS) && subsectors_per_sector != 1)){
puts("FAILED SANITY CHECKS :(");
abort_and_solicit_report();
}
sector_count = chip_count * flashrom_type->sector_count;
for(sector=0; (sector < sector_count) && !eof; sector++){
printf("%s%s: sector %3d/%d %s",
verbose ? "" : "\r",
perform_write ? "Write" : "Verify",
sector, sector_count,
verbose ? "" : " ");
/* verify sector */
flash_address = flashrom_sector_address(sector);
block = sector * flashrom_type->sector_size;
verify_okay = true;
for(subsector=0; subsector < subsectors_per_sector; subsector++){
if(read_data_from_file(infile, block, blocks_per_subsector)){
eof = true;
break;
}else if(!flashrom_block_verify(flash_address, filebuffer, bytes_per_subsector)){
verify_okay = false;
break;
}
block += blocks_per_subsector;
flash_address += bytes_per_subsector;
}
if(verbose)
printf(verify_okay ? "verified\n" : (perform_write ? "mismatch, " : "FAILED\n"));
if(!verify_okay){
mismatch++;
if(perform_write){
if(subsector){
/* we need to rewind to the first subsector */
flash_address = flashrom_sector_address(sector);
block = sector * flashrom_type->sector_size;
eof = read_data_from_file(infile, block, blocks_per_subsector);
subsector = 0;
}
/* erase and program sector */
if(flashrom_type->strategy & ST_PROGRAM_SECTORS){
/* This type of chip has a combined erase/program cycle that programs a whole
sector at once. The sectors are quite small (128 or 256 bytes) so there is
exactly 1 subsector (and we employ a sanity check to ensure this is true).
Additionally we can be sure that we are not at EOF yet. */
flashrom_sector_program(flash_address, filebuffer, bytes_per_subsector);
}else{
if(flashrom_type->strategy & ST_ERASE_CHIP){
if(verbose)
printf("chip erase, ");
flashrom_chip_erase(flash_address);
}else{
if(verbose)
printf("sector erase, ");
flashrom_sector_erase(flash_address);
}
while(true){
flashrom_block_write(flash_address, filebuffer, bytes_per_subsector);
subsector++;
if(subsector >= subsectors_per_sector)
break;
block += blocks_per_subsector;
flash_address += bytes_per_subsector;
if(read_data_from_file(infile, block, blocks_per_subsector)){
eof = true;
break;
}
}
if(verbose)
puts("programmed");
}
}
}
/* P112 can address only first 32KB of any device */
if(access == ACCESS_P112){
if(sector >= ((32768 / 128) / flashrom_type->sector_size))
eof = true; /* force EOF at end of addressable region */
}
}
/* report outcome */
if(perform_write){
printf("\rWrite complete: Reprogrammed %d/%d sectors.\n", mismatch, sector_count);
}else{
if(sector != sector_count)
printf("\rPartial verify (%d/%d sectors)", sector-1, sector_count);
else
printf("\rVerify (%d sectors)", sector_count);
if(mismatch){
printf(" complete: %d sectors contain errors.\n" \
"\n*** VERIFY FAILED ***\n\n",
mismatch);
}else
puts(" complete: OK!");
}
return mismatch;
}
bool check_file_size(cpm_fcb *imagefile, bool allow_partial)
{
unsigned int file_size;
unsigned int rom_size;
file_size = cpm_f_getsize(imagefile);
rom_size = flashrom_type->sector_count * flashrom_type->sector_size * chip_count;
if(file_size == rom_size)
return true;
if(allow_partial &&
(rom_size > file_size) &&
(file_size != 0) &&
(file_size & 0xff) == 0) /* file is exact multiple of 32KB long */
return true;
return false;
}
bool una_bios_present(void)
{
unsigned int **bios_signature = (unsigned int **)BIOS_SIGNATURE_ADDR;
return (**bios_signature == BIOS_SIGNATURE_UNA);
}
bool romwbw_bios_present(void)
{
unsigned int **bios_signature = (unsigned int **)BIOS_SIGNATURE_ADDR;
return (**bios_signature == BIOS_SIGNATURE_ROMWBW_26);
}
bool old_romwbw_bios_present(void)
{
return (*((unsigned int*)CPM_SIGNATURE_ADDR) == CPM_SIGNATURE_ROMWBW);
}
static const char *bpbios_p112_signature = "B/P-DX";
bool bpbios_p112_present(void)
{
return (memcmp((const char*)(*((unsigned int*)BIOS_ENTRY_ADDR) + 0x75), bpbios_p112_signature, 6) == 0);
}
access_t access_auto_select(void)
{
// Note that versions of RomWBW before approx 2014-08 place a
// signature at CPM_SIGNATURE_ADDR but not BIOS_SIGNATURE_ADDR.
// Therefore we cannot rely on the latter to confirm if RomWBW
// HBIOS is present or not.
if(una_bios_present())
return ACCESS_UNABIOS;
if(bpbios_p112_present())
return ACCESS_P112;
if(romwbw_bios_present()) // important to check this before old_romwbw_bios_present()
return ACCESS_ROMWBW_26;
if(old_romwbw_bios_present())
return ACCESS_ROMWBW_OLD;
if(detect_z180_cpu())
return ACCESS_Z180DMA;
return ACCESS_NONE;
}
void main(int argc, const char *argv[]) CALLING
{
int i;
unsigned int mismatch;
cpm_fcb imagefile;
const char *filename = NULL;
bool allow_partial=false;
bool rom_mode=false;
puts("FLASH4 by Will Sowerbutts <will@sowerbutts.com> version 1.3.9\n");
/* determine access mode */
for(i=1; i<argc; i++){ /* check for manual mode override */
if(strcmp(argv[i], "/Z180DMA") == 0)
access = ACCESS_Z180DMA;
else if(strcmp(argv[i], "/ROMWBWOLD") == 0)
access = ACCESS_ROMWBW_OLD;
else if(strcmp(argv[i], "/ROMWBW") == 0)
access = ACCESS_ROMWBW_26;
else if(strcmp(argv[i], "/UNABIOS") == 0)
access = ACCESS_UNABIOS;
else if(strcmp(argv[i], "/P112") == 0)
access = ACCESS_P112;
else if(strcmp(argv[i], "/N8VEMSBC") == 0)
access = ACCESS_N8VEM_SBC;
else if(strcmp(argv[i], "/ROM") == 0)
rom_mode = true;
else if(strcmp(argv[i], "/V") == 0)
verbose = true;
else if(strcmp(argv[i], "/P") == 0 || strcmp(argv[i], "/PARTIAL") == 0)
allow_partial = true;
else if(argv[i][0] == '/' && argv[i][1] >= '1' && argv[i][1] <= '9'){
chip_count = argv[i][1] - '0';
chip_count_forced = true;
}else if(argv[i][0] == '/'){
printf("Unrecognised option \"%s\"\n", argv[i]);
help();
}else{
/* non-option command line parameters */
if(action == ACTION_UNKNOWN){ /* action comes first */
if(strcmp(argv[i], "READ") == 0)
action = ACTION_READ;
else if(strcmp(argv[i], "VERIFY") == 0)
action = ACTION_VERIFY;
else if(strcmp(argv[i], "WRITE") == 0)
action = ACTION_WRITE;
else{
printf("Unrecognised command \"%s\"\n", argv[i]);
help();
}
}else if(filename == NULL){
filename = argv[i];
}else{
printf("Unexpected command line argument \"%s\"\n", argv[i]);
help();
}
}
}
if(access == ACCESS_AUTO)
access = access_auto_select();
// assume bank switching
flashrom_chip_read = flashrom_chip_read_bankswitch;
flashrom_chip_write = flashrom_chip_write_bankswitch;
flashrom_block_read = flashrom_block_read_bankswitch;
flashrom_block_write = flashrom_block_write_bankswitch;
flashrom_block_verify = flashrom_block_verify_bankswitch;
switch(access){
case ACCESS_Z180DMA:
puts("Using Z180 DMA engine.");
if(chip_count != 1){
puts("Z180 DMA engine supports programming a single device only.");
return;
}
init_z180dma();
flashrom_chip_read = flashrom_chip_read_z180dma;
flashrom_chip_write = flashrom_chip_write_z180dma;
flashrom_block_read = flashrom_block_read_z180dma;
flashrom_block_write = flashrom_block_write_z180dma;
flashrom_block_verify = flashrom_block_verify_z180dma;
break;
case ACCESS_UNABIOS:
puts("Using UNA BIOS bank switching.");
init_bankswitch(BANKSWITCH_UNABIOS);
break;
case ACCESS_ROMWBW_OLD:
puts("Using RomWBW (old) bank switching.");
init_bankswitch(BANKSWITCH_ROMWBW_OLD);
break;
case ACCESS_ROMWBW_26:
puts("Using RomWBW (v2.6+) bank switching.");
init_bankswitch(BANKSWITCH_ROMWBW_26);
break;
case ACCESS_P112:
puts("Using P112 bank switching.");
init_bankswitch(BANKSWITCH_P112);
break;
case ACCESS_N8VEM_SBC:
puts("Using N8VEM SBC bank switching.");
init_bankswitch(BANKSWITCH_N8VEM_SBC);
break;
case ACCESS_NONE:
case ACCESS_AUTO:
puts("Cannot determine how to access your flash ROM chip.");
abort_and_solicit_report();
}
/* identify flash ROM chip */
if(!flashrom_identify()){
puts("Your flash memory chip is not recognised.");
if(rom_mode && (action == ACTION_VERIFY || action == ACTION_READ)){
puts("Assuming 512KB ROM");
flashrom_type = &rom_chip;
flashrom_setup();
}else{
abort_and_solicit_report();
}
}
printf("Flash memory has %d chip%s %d sectors of %ld bytes, total %dKB\n",
chip_count, chip_count == 1 ? ",":"s, each",
flashrom_type->sector_count, flashrom_sector_size,
(int)(flashrom_size >> 10));
/* P112 with ROMs larger than 32KB are limited */
if(access == ACCESS_P112 && flashrom_size > 32768){
puts("P112 can address only first 32KB: Partial mode enabled.");
allow_partial = true;
chip_count = 1;
flashrom_chip_size = 32768;
flashrom_size = 32768;
}
if(action == ACTION_UNKNOWN || !filename)
help();
cpm_f_prepare(&imagefile, filename);
/* execute action */
switch(action){
case ACTION_READ:
cpm_f_delete(&imagefile); /* remove existing file first */
if(cpm_f_create(&imagefile)){
printf("Cannot create file \"%s\".\n", filename);
return;
}
flashrom_read(&imagefile);
break;
case ACTION_VERIFY:
case ACTION_WRITE:
if(cpm_f_open(&imagefile)){
printf("Cannot open file \"%s\".\n", filename);
return;
}
if(!check_file_size(&imagefile, allow_partial)){
puts("Image file size does not match ROM size: Aborting\n" \
"You may use /PARTIAL to program only the start of the ROM, however for\n" \
"safety reasons the image file must be a multiple of exactly 32KB long.");
return;
}
if(action == ACTION_WRITE)
mismatch = flashrom_verify_and_write(&imagefile, true); /* we avoid verifying if nothing changed */
else
mismatch = 1; /* force a verify if we're not writing */
if(mismatch)
flashrom_verify_and_write(&imagefile, false);
break;
}
cpm_f_close(&imagefile);
}