]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/z80-if.c
new debug command: xx test. get freq command from single step branch.
[z180-stamp.git] / avr / z80-if.c
1 /*
2 * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7 /**
8 *
9 * Pin assignments
10 *
11 * | Z180-Sig | AVR-Port | Dir |
12 * +------------+---------------+-------+
13 * | A0 | PA 0 | O |
14 * | A1 | PA 1 | O |
15 * | A2 | PA 2 | O |
16 * | A3 | PA 3 | O |
17 * | A4 | PA 4 | O |
18 * | A5 | PA 5 | O |
19 * | A6 | PA 6 | O |
20 * | A7 | PA 7 | O |
21 * | A8 | PC 0 | O |
22 * | A9 | PC 1 | O |
23 * | A10 | PC 2 | O |
24 * | A11 | PC 3 | O |
25 * | A12 | PC 4 | O |
26 * | A13 | PC 5 | O |
27 * | A14 | PC 6 | O |
28 * | A15 | PC 7 | O |
29 * | A16 | PE 2 | O |
30 * | A17 | PE 3 | O |
31 * | A18 | PE 4 | O |
32 * | D0 | PF 0 | I/O |
33 * | D1 | PF 1 | I/O |
34 * | D2 | PF 2 | I/O |
35 * | D3 | PF 3 | I/O |
36 * | D4 | PF 4 | I/O |
37 * | D5 | PF 5 | I/O |
38 * | D6 | PF 6 | I/O |
39 * | D7 | PF 7 | I/O |
40 * | RD | PD 3 | O |
41 * | WR | PD 2 | O |
42 * | MREQ | PD 4 | O |
43 * | RST | PD 5 | O |
44 * | BUSREQ | PD 7 | O |
45 * | BUSACK | PD 6 | I |
46 * |
47 * | Optional
48 * +------------------------------------+
49 * | STEP | PG 0 | O |
50 * | RUN | PG 1 | O |
51 * | WAIT | PG 2 | I |
52
53 */
54
55
56 #include "z80-if.h"
57 #include <util/atomic.h>
58 #include "debug.h"
59 #include "config.h"
60 #include "env.h"
61
62
63 //#define P_ZCLK PORTB
64 //#define ZCLK 5
65 //#define DDR_ZCLK DDRB
66 #define P_MREQ PORTD
67 #define MREQ 4
68 #define DDR_MREQ DDRD
69 #define P_RD PORTD
70 #define RD 3
71 #define P_WR PORTD
72 #define WR 2
73 #define P_BUSREQ PORTD
74 #define BUSREQ 7
75 #define DDR_BUSREQ DDRD
76 #define P_BUSACK PORTD
77 #define PIN_BUSACK PIND
78 #define BUSACK 6
79 #define DDR_BUSACK DDRD
80 #define P_RST PORTD
81 #define PIN_RST PIND
82 #define DDR_RST DDRD
83 #define RST 5
84
85
86 #define P_DB PORTF
87 #define PIN_DB PINF
88 #define DDR_DB DDRF
89
90 #define P_ADL PORTA
91 #define P_ADH PORTC
92 #define P_ADB PORTE
93 #define PIN_ADB PINE
94 #define DDR_ADL DDRA
95 #define DDR_ADH DDRC
96 #define DDR_ADB DDRE
97
98 #define ADB_WIDTH 3
99 #define ADB_SHIFT 2
100 //#define ADB_PORT PORTE
101
102
103 //#define Z80_O_ZCLK SBIT(P_ZCLK, 5)
104 #define Z80_O_MREQ SBIT(P_MREQ, 4)
105 #define Z80_O_RD SBIT(P_RD, 3)
106 #define Z80_O_WR SBIT(P_WR, 2)
107 #define Z80_O_BUSREQ SBIT(P_BUSREQ, 7)
108 //#define Z80_O_NMI SBIT(P_NMI, )
109 #define Z80_O_RST SBIT(P_RST, 5)
110 #define Z80_I_RST SBIT(PIN_RST, 5)
111 #define Z80_I_BUSACK SBIT(PIN_BUSACK, 6)
112 //#define Z80_I_HALT SBIT(P_HALT, )
113
114 /* Optional */
115 #define P_RUN PORTG
116 #define RUN 1
117 #define DDR_RUN DDRG
118 #define P_STEP PORTG
119 #define STEP 0
120 #define DDR_STEP DDRG
121 #define P_WAIT PORTG
122 #define WAIT 2
123 #define DDR_WAIT DDRG
124 /* All three signals are on the same Port (PortG) */
125 #define PORT_SS PORTG
126 #define DDR_SS DDRG
127 #define PIN_SS PING
128 #define Z80_O_RUN SBIT(PORT_SS, RUN)
129 #define Z80_O_STEP SBIT(PORT_SS, STEP)
130 #define Z80_I_WAIT SBIT(PORT_SS, WAIT)
131
132
133 #define BUS_TO 20
134
135
136 #define MASK(n) ((1<<(n))-1)
137 #define SMASK(w,s) (MASK(w) << (s))
138
139 void z80_bus_request_or_exit(void)
140 {
141 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED))
142 cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
143 }
144
145 static zstate_t zstate;
146 static volatile uint8_t timer; /* used for bus timeout */
147
148 #if 0
149 static volatile uint16_t req_cycles_ovl;
150
151 ISR(TIMER4_COMPB_vect)
152 {
153 req_cycles_ovl++;
154 }
155 #endif
156
157 /*---------------------------------------------------------*/
158 /* 10Hz timer interrupt generated by OC5A */
159 /*---------------------------------------------------------*/
160
161 ISR(TIMER5_COMPA_vect)
162 {
163
164 uint8_t i = timer;
165
166 if (i)
167 timer = i - 1;
168 }
169
170 /*--------------------------------------------------------------------------*/
171
172
173 static void z80_addrbus_set_in(void)
174 {
175 /* /MREQ, /RD, /WR: Input, no pullup */
176 DDR_MREQ &= ~(_BV(MREQ) | _BV(RD) | _BV(WR));
177 Z80_O_MREQ = 0;
178 Z80_O_RD = 0;
179 Z80_O_WR = 0;
180
181 P_ADL = 0;
182 DDR_ADL = 0;
183 P_ADH = 0;
184 DDR_ADH = 0;
185 PIN_ADB = P_ADB & (MASK(ADB_WIDTH) << ADB_SHIFT);
186 DDR_ADB = DDR_ADB & ~(MASK(ADB_WIDTH) << ADB_SHIFT);
187 }
188
189
190 static void z80_addrbus_set_out(void)
191 {
192 /* /MREQ, /RD, /WR: Output and high */
193 Z80_O_MREQ = 1;
194 Z80_O_RD = 1;
195 Z80_O_WR = 1;
196 DDR_MREQ |= _BV(MREQ) | _BV(RD) | _BV(WR);
197
198 DDR_ADL = 0xff;
199 DDR_ADH = 0xff;
200 DDR_ADB = DDR_ADB | (MASK(ADB_WIDTH) << ADB_SHIFT);
201 }
202
203
204 static void z80_dbus_set_in(void)
205 {
206 DDR_DB = 0;
207 P_DB = 0;
208 }
209
210
211 static void z80_dbus_set_out(void)
212 {
213 DDR_DB = 0xff;
214 }
215
216 static void z80_reset_active(void)
217 {
218 if (Stat & S_RESET_POLARITY)
219 Z80_O_RST = 1;
220 else
221 Z80_O_RST = 0;
222 }
223
224 static void z80_reset_inactive(void)
225 {
226 if (Stat & S_RESET_POLARITY)
227 Z80_O_RST = 0;
228 else
229 Z80_O_RST = 1;
230 }
231
232 static void z80_reset_pulse(void)
233 {
234 z80_reset_active();
235 _delay_us(10);
236 z80_reset_inactive();
237 }
238
239
240 void z80_setup_bus(void)
241 {
242 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
243
244 /* /ZRESET: Input, no pullup */
245 DDR_RST &= ~_BV(RST);
246 Z80_O_RST = 0;
247
248 /* /BUSREQ: Output and high */
249 Z80_O_BUSREQ = 1;
250 DDR_BUSREQ |= _BV(BUSREQ);
251
252 /* /BUSACK: Input, no pullup */
253 DDR_BUSACK &= ~_BV(BUSACK);
254 P_BUSACK &= ~_BV(BUSACK);
255
256 z80_addrbus_set_in();
257 z80_dbus_set_in();
258
259 if (getenv_yesno(PSTR(ENV_SINGLESTEP))) {
260 /* /RUN & /STEP: output, /WAIT: input */
261
262 PORT_SS = (PORT_SS & ~_BV(RUN)) | _BV(STEP);
263 DDR_SS = (DDR_SS & ~_BV(WAIT)) | _BV(RUN) | _BV(STEP);
264 }
265
266 if (Z80_I_RST)
267 Stat |= S_RESET_POLARITY;
268 else
269 Stat &= ~S_RESET_POLARITY;
270 z80_reset_active();
271 DDR_RST |= _BV(RST);
272
273 zstate = RESET;
274 }
275
276 /* Timer 5 */
277 PRR1 &= ~_BV(PRTIM5);
278 OCR5A = F_CPU / 1024 / 10 - 1; /* Timer: 10Hz interval (OC4A) */
279 TCCR5B = (0b01<<WGM52)|(0b101<<CS40); /* CTC Mode, Prescaler 1024 */
280 TIMSK5 = _BV(OCIE5A); /* Enable oca interrupt */
281
282 }
283
284
285 zstate_t z80_bus_state(void)
286 {
287 return zstate;
288 }
289
290
291 static void z80_busreq_hpulse(void)
292 {
293 z80_dbus_set_in();
294 z80_addrbus_set_in();
295
296 #if 0
297 ATOMIC_BLOCK(ATOMIC_FORCEON) {
298 Z80_O_BUSREQ = 1;
299 Z80_O_BUSREQ = 1; /* 2 AVR clock cycles */
300 Z80_O_BUSREQ = 0; /* 2 AVR clock cycles */
301 }
302 #endif
303
304 #if 1
305 ATOMIC_BLOCK(ATOMIC_FORCEON) {
306 Z80_O_BUSREQ = 1;
307
308 do {
309 if (Z80_I_BUSACK == 1) {
310 Z80_O_BUSREQ = 0;
311 break;
312 }
313 } while (1);
314 }
315 #endif
316
317 if (zstate & ZST_ACQUIRED) {
318 timer = BUS_TO;
319 while (Z80_I_BUSACK == 1 && timer)
320 ;
321 if (Z80_I_BUSACK == 0)
322 z80_addrbus_set_out();
323 }
324 }
325
326
327 /*
328
329 + | | | | |
330 + State | RESET | RESET_AQRD | RUNNING | RUNNING_AQRD |
331 + | | | | |
332 + | 0 | 1 | 2 | 3 |
333 Event + | | | | |
334 ----------------+---------------+---------------+---------------+---------------+
335 | | | | |
336 Reset | 0 | 0 | 0 | 0 |
337 | | | | |
338 | | | | |
339 Request | 1 | | 3 | |
340 | | | | |
341 | | | | |
342 Release | | 0 | | 2 |
343 | | | | |
344 | | | | |
345 Run | 2 | 3 | | |
346 | | | | |
347 | | | | |
348 Restart | | | 2 | 3 |
349 | | | | |
350 | | | | |
351 M_Cycle | | | | 3 |
352 | | | | |
353 | | | | |
354 */
355
356 zstate_t z80_bus_cmd(bus_cmd_t cmd)
357 {
358 switch (cmd) {
359
360 case Reset:
361 z80_dbus_set_in();
362 z80_addrbus_set_in();
363 z80_reset_active();
364 _delay_us(10);
365 Z80_O_BUSREQ = 1;
366 timer = BUS_TO;
367 while (Z80_I_BUSACK == 0 && timer)
368 ;
369 zstate = RESET;
370 break;
371
372 case Request:
373 switch (zstate) {
374 case RESET:
375 Z80_O_BUSREQ = 0;
376 z80_reset_inactive();
377 timer = BUS_TO;
378 while (Z80_I_BUSACK == 1 && timer)
379 ;
380 if (Z80_I_BUSACK == 0) {
381 z80_addrbus_set_out();
382 zstate = RESET_AQRD;
383 } else {
384 z80_reset_active();
385 Z80_O_BUSREQ = 1;
386 }
387 break;
388
389 case RUNNING:
390 Z80_O_BUSREQ = 0;
391 timer = BUS_TO;
392 while (Z80_I_BUSACK == 1 && timer)
393 ;
394 if (Z80_I_BUSACK == 0) {
395 z80_addrbus_set_out();
396 zstate = RUNNING_AQRD;
397 } else {
398 Z80_O_BUSREQ = 1;
399 }
400 break;
401
402 default:
403 break;
404 }
405 break;
406
407 case Release:
408 switch (zstate) {
409 case RESET_AQRD:
410 z80_dbus_set_in();
411 z80_addrbus_set_in();
412 z80_reset_active();
413 _delay_us(10);
414 Z80_O_BUSREQ = 1;
415 timer = BUS_TO;
416 while (Z80_I_BUSACK == 0 && timer)
417 ;
418 zstate = RESET;
419 break;
420 case RUNNING_AQRD:
421 z80_dbus_set_in();
422 z80_addrbus_set_in();
423 Z80_O_BUSREQ = 1;
424 timer = BUS_TO;
425 while (Z80_I_BUSACK == 0 && timer)
426 ;
427 zstate = RUNNING;
428 break;
429 default:
430 break;
431 }
432 break;
433
434 case Run:
435 switch (zstate) {
436 case RESET:
437 _delay_ms(20); /* TODO: */
438 z80_reset_inactive();
439 zstate = RUNNING;
440 break;
441
442 case RESET_AQRD:
443 z80_dbus_set_in();
444 z80_addrbus_set_in();
445 z80_reset_pulse();
446 z80_addrbus_set_out();
447 zstate = RUNNING_AQRD;
448 break;
449 default:
450 break;
451 }
452 break;
453
454 case Restart:
455 switch (zstate) {
456 case RUNNING:
457 case RUNNING_AQRD:
458 z80_reset_pulse();
459 break;
460 default:
461 break;
462 }
463 break;
464
465 case M_Cycle:
466 switch (zstate) {
467 case RUNNING_AQRD:
468 z80_busreq_hpulse(); /* TODO: */
469 break;
470 default:
471 break;
472 }
473 }
474 return zstate;
475 }
476
477
478 /*--------------------------------------------------------------------------*/
479
480 static
481 //inline __attribute__ ((always_inline))
482 void z80_setaddress(uint32_t addr)
483 {
484 P_ADL = addr;
485 P_ADH = (addr & 0xff00) >> 8;
486 PIN_ADB = (((addr >> 16) << ADB_SHIFT) ^ P_ADB) & MASK(ADB_WIDTH) << ADB_SHIFT;
487 }
488
489 int32_t z80_memsize_detect(void)
490 {
491 const uint8_t PATTERN_1 = 0x55;
492 const uint8_t PATTERN_2 = ~PATTERN_1;
493 uint32_t addr;
494
495 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED))
496 return -EBUSTO;
497
498 uint8_t ram_0 = z80_read(0);
499 uint8_t ram_1 = z80_read(1);
500
501 z80_write(0, ram_0 ^ 0xff);
502 z80_write(1, ram_1);
503 if ((z80_read(0) ^ ram_0) != 0xff) {
504 addr = 0;
505 } else {
506 z80_write(0, PATTERN_1);
507 for (addr=1; addr < CONFIG_SYS_RAMSIZE_MAX; addr <<= 1) {
508 uint8_t ram_i = z80_read(addr);
509 z80_write(addr, PATTERN_2);
510 if (z80_read(0) != PATTERN_1 || z80_read(addr) != PATTERN_2)
511 break;
512 z80_write(addr, ram_i);
513 }
514 }
515
516 z80_write(0, ram_0);
517 z80_bus_cmd(Release);
518
519 return addr;
520 }
521
522 /*--------------------------------------------------------------------------*/
523
524 void z80_write(uint32_t addr, uint8_t data)
525 {
526 z80_setaddress(addr);
527 Z80_O_MREQ = 0;
528 z80_dbus_set_out();
529 P_DB = data;
530 P_DB = data;
531 Z80_O_WR = 0;
532 Z80_O_WR = 0;
533 Z80_O_WR = 1;
534 Z80_O_MREQ = 1;
535 }
536
537 uint8_t z80_read(uint32_t addr)
538 {
539 uint8_t data;
540
541 z80_setaddress(addr);
542 Z80_O_MREQ = 0;
543 z80_dbus_set_in();
544 Z80_O_RD = 0;
545 Z80_O_RD = 0;
546 Z80_O_RD = 0;
547 data = PIN_DB;
548 Z80_O_RD = 1;
549 Z80_O_MREQ = 1;
550
551 return data;
552 }
553
554
555 void z80_memset(uint32_t addr, uint8_t data, uint32_t length)
556 {
557 z80_dbus_set_out();
558 Z80_O_MREQ = 0;
559 P_DB = data;
560 while(length--) {
561 z80_setaddress(addr++);
562 Z80_O_WR = 0;
563 Z80_O_WR = 0;
564 Z80_O_WR = 1;
565 }
566 Z80_O_MREQ = 1;
567 }
568
569 void z80_write_block_P(const FLASH uint8_t *src, uint32_t dest, uint32_t length)
570 {
571 uint8_t data;
572
573 z80_dbus_set_out();
574 Z80_O_MREQ = 0;
575 while(length--) {
576 z80_setaddress(dest++);
577 data = *src++;
578 P_DB = data;
579 P_DB = data;
580 Z80_O_WR = 0;
581 Z80_O_WR = 0;
582 Z80_O_WR = 1;
583 }
584 Z80_O_MREQ = 1;
585 }
586
587 void z80_write_block(const uint8_t *src, uint32_t dest, uint32_t length)
588 {
589 uint8_t data;
590
591 z80_dbus_set_out();
592 Z80_O_MREQ = 0;
593 while(length--) {
594 z80_setaddress(dest++);
595 data = *src++;
596 P_DB = data;
597 P_DB = data;
598 Z80_O_WR = 0;
599 Z80_O_WR = 0;
600 Z80_O_WR = 1;
601 }
602 Z80_O_MREQ = 1;
603 }
604
605 void z80_read_block (uint8_t *dest, uint32_t src, size_t length)
606 {
607 uint8_t data;
608
609 Z80_O_MREQ = 0;
610 z80_dbus_set_in();
611 while(length--) {
612 z80_setaddress(src++);
613 Z80_O_RD = 0;
614 Z80_O_RD = 0;
615 Z80_O_RD = 0;
616 data = PIN_DB;
617 Z80_O_RD = 1;
618 *dest++ = data;
619 }
620 Z80_O_MREQ = 1;
621 }
622
623 /*--------------------------------------------------------------------------*/
624
625 /*
626 0179' rx.bs_mask: ds 1 ; (buf_len - 1)
627 017A' rx.in_idx: ds 1 ;
628 017B' rx.out_idx: ds 1 ;
629 017C' rx.buf: ds rx.buf_len ;
630 018B' rx.buf_end equ $-1 ; last byte (start+len-1)
631
632 018C' tx.bs_mask: ds 1 ; (buf_len - 1)
633 018D' tx.in_idx: ds 1 ;
634 018E' tx.out_idx: ds 1 ;
635 018F' tx.buf: ds tx.buf_len ;
636 019E' tx.buf_end equ $-1 ; last byte
637 */
638
639
640 typedef struct __attribute__((packed)) {
641 uint8_t mask;
642 uint8_t in_idx;
643 uint8_t out_idx;
644 uint8_t buf[];
645 } zfifo_t;
646
647
648
649 #define FIFO_BUFSIZE_MASK -3
650 #define FIFO_INDEX_IN -2
651 #define FIFO_INDEX_OUT -1
652
653
654 static struct {
655 uint32_t base;
656 uint8_t idx_out,
657 idx_in,
658 mask;
659 } fifo_dsc[NUM_FIFOS];
660
661
662 void z80_memfifo_init(const fifo_t f, uint32_t addr)
663 {
664 fifo_dsc[f].base = addr;
665
666
667 if (addr != 0) {
668 z80_bus_cmd(Request);
669 fifo_dsc[f].mask = z80_read(addr + FIFO_BUFSIZE_MASK);
670 fifo_dsc[f].idx_in = z80_read(addr + FIFO_INDEX_IN);
671 fifo_dsc[f].idx_out = z80_read(addr + FIFO_INDEX_OUT);
672 z80_bus_cmd(Release);
673
674 if (fifo_dsc[f].idx_in != 0 || fifo_dsc[f].idx_out != 0) {
675 DBG_P(1, "## z80_memfifo_init: %i, %lx, in: %.2x, out: %.2x, mask: %.2x\n",
676 f, addr, fifo_dsc[f].idx_in, fifo_dsc[f].idx_out, fifo_dsc[f].mask);
677 }
678 }
679 }
680
681
682 int z80_memfifo_is_empty(const fifo_t f)
683 {
684 int rc = 1;
685
686 if (fifo_dsc[f].base != 0) {
687
688 uint32_t adr = fifo_dsc[f].base + FIFO_INDEX_IN;
689 uint8_t idx;
690
691 z80_bus_cmd(Request);
692 idx = z80_read(adr);
693 z80_bus_cmd(Release);
694 rc = idx == fifo_dsc[f].idx_out;
695 }
696
697 return rc;
698 }
699
700 int z80_memfifo_is_full(const fifo_t f)
701 {
702 int rc = 0;
703
704 if (fifo_dsc[f].base != 0) {
705 z80_bus_cmd(Request);
706 rc = ((fifo_dsc[f].idx_in + 1) & fifo_dsc[f].mask)
707 == z80_read(fifo_dsc[f].base+FIFO_INDEX_OUT);
708 z80_bus_cmd(Release);
709 }
710 return rc;
711 }
712
713
714 uint8_t z80_memfifo_getc_wait(const fifo_t f)
715 {
716 uint8_t rc, idx;
717
718 while (z80_memfifo_is_empty(f))
719 ;
720
721 z80_bus_cmd(Request);
722 idx = fifo_dsc[f].idx_out;
723 rc = z80_read(fifo_dsc[f].base+idx);
724 fifo_dsc[f].idx_out = ++idx & fifo_dsc[f].mask;
725 z80_write(fifo_dsc[f].base+FIFO_INDEX_OUT, fifo_dsc[f].idx_out);
726 z80_bus_cmd(Release);
727
728 return rc;
729 }
730
731 int z80_memfifo_getc(const fifo_t f)
732 {
733 int rc = -1;
734
735 if (fifo_dsc[f].base != 0) {
736 uint8_t idx = fifo_dsc[f].idx_out;
737 z80_bus_cmd(Request);
738 if (idx != z80_read(fifo_dsc[f].base + FIFO_INDEX_IN)) {
739 rc = z80_read(fifo_dsc[f].base+idx);
740 fifo_dsc[f].idx_out = ++idx & fifo_dsc[f].mask;
741 z80_write(fifo_dsc[f].base+FIFO_INDEX_OUT, fifo_dsc[f].idx_out);
742 }
743 z80_bus_cmd(Release);
744 }
745
746 return rc;
747 }
748
749
750 void z80_memfifo_putc(fifo_t f, uint8_t val)
751 {
752 int idx;
753
754 while (z80_memfifo_is_full(f))
755 ;
756
757 z80_bus_cmd(Request);
758 idx = fifo_dsc[f].idx_in;
759 z80_write(fifo_dsc[f].base+idx, val);
760 fifo_dsc[f].idx_in = ++idx & fifo_dsc[f].mask;
761 z80_write(fifo_dsc[f].base+FIFO_INDEX_IN, fifo_dsc[f].idx_in);
762 z80_bus_cmd(Release);
763 }
764
765 /*--------------------------------------------------------------------------*/
766
767 void z80_load_mem(int_fast8_t verbosity,
768 const FLASH unsigned char data[],
769 const FLASH unsigned long *sections,
770 const FLASH unsigned long address[],
771 const FLASH unsigned long length_of_sections[])
772 {
773 uint32_t sec_base = 0;
774
775 if (verbosity > 1)
776 printf_P(PSTR("Loading Z180 memory... \n"));
777
778 for (unsigned sec = 0; sec < *sections; sec++) {
779 if (verbosity > 0) {
780 printf_P(PSTR(" From: 0x%.5lX to: 0x%.5lX (%5li bytes)\n"),
781 address[sec],
782 address[sec]+length_of_sections[sec] - 1,
783 length_of_sections[sec]);
784 }
785
786 z80_write_block_P((const FLASH unsigned char *) &data[sec_base], /* src */
787 address[sec], /* dest */
788 length_of_sections[sec]); /* len */
789 sec_base += length_of_sections[sec];
790 }
791 }