]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/z180-serv.c
Adaptions for fatfs R0.15
[z180-stamp.git] / avr / z180-serv.c
1 /*
2 * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7 #include "z180-serv.h"
8 #include "common.h"
9 #include <util/atomic.h>
10
11 #include "config.h"
12 #include "background.h"
13 #include "env.h"
14 #include "ff.h"
15 #include "serial.h"
16 #include "z80-if.h"
17 #include "debug.h"
18 #include "print-utils.h"
19 #include "timer.h"
20 #include "time.h"
21 #include "bcd.h"
22 #include "rtc.h"
23
24 #define DEBUG_CPM_SDIO 0 /* set to 1 to debug */
25
26 #define debug_cpmsd(fmt, args...) \
27 debug_cond(DEBUG_CPM_SDIO, fmt, ##args)
28
29
30 /*--------------------------------------------------------------------------*/
31
32 struct msg_item {
33 uint8_t fct;
34 uint8_t sub_min, sub_max;
35 void (*func)(uint8_t, int, uint8_t *);
36 };
37
38 uint32_t msg_to_addr(uint8_t *msg)
39 {
40 union {
41 uint32_t as32;
42 uint8_t as8[4];
43 } addr;
44
45 addr.as8[0] = msg[0];
46 addr.as8[1] = msg[1];
47 addr.as8[2] = msg[2];
48 addr.as8[3] = 0;
49
50 return addr.as32;
51 }
52
53
54 static int msg_xmit_header(uint8_t func, uint8_t subf, int len)
55 {
56 z80_memfifo_putc(fifo_msgout, 0xAE);
57 z80_memfifo_putc(fifo_msgout, len+2);
58 z80_memfifo_putc(fifo_msgout, func);
59 z80_memfifo_putc(fifo_msgout, subf);
60
61 return 0;
62 }
63
64 int msg_xmit(uint8_t func, uint8_t subf, int len, uint8_t *msg)
65 {
66 msg_xmit_header(func, subf, len);
67 while (len--)
68 z80_memfifo_putc(fifo_msgout, *msg++);
69
70 return 0;
71 }
72
73 void do_msg_ini_memfifo(uint8_t subf, int len, uint8_t * msg)
74 {
75 (void)len;
76
77 z80_memfifo_init(subf, msg_to_addr(msg));
78 }
79
80
81 void do_msg_char_out(uint8_t subf, int len, uint8_t * msg)
82 {
83 (void)subf;
84
85 while (len--)
86 putchar(*msg++);
87 }
88
89 /* echo message */
90 void do_msg_echo(uint8_t subf, int len, uint8_t * msg)
91 {
92 (void)subf;
93
94 /* send re-echo */
95 msg_xmit(1, 3, len, msg);
96 }
97
98 /* get timer */
99 void do_msg_get_timer(uint8_t subf, int len, uint8_t * msg)
100 {
101 uint32_t time_ms = (len >= 4) ? *(uint32_t *) msg : 0;
102
103 time_ms = get_timer(time_ms);
104 msg_xmit(3, subf, sizeof(time_ms), (uint8_t *) &time_ms);
105 }
106
107 /* ---------------------------------------------------------------------------*/
108
109 #define CPM_DAY_OFFSET ((1978-1900) * 365 + 19) /* 19 leap years */
110
111 /*
112 * Convert CP/M time stamp to a broken-down time structure
113 *
114 */
115 int mk_date_time (int len, uint8_t *msg, struct tm *tmp)
116 {
117 time_t stamp;
118
119 if (len != 5)
120 return -1;
121
122 /* days since 2000-01-01 */
123 long days = msg[3] + (msg[4] << 8) - 8036;
124
125 if (days < 0)
126 return -1;
127
128 stamp = days * ONE_DAY;
129 stamp += bcd2bin(msg[0]);
130 stamp += bcd2bin(msg[1]) * 60 ;
131 stamp += bcd2bin(msg[2]) * 3600L;
132 gmtime_r(&stamp, tmp);
133 return 0;
134 }
135
136 void mk_cpm_time(struct tm *tmp, uint8_t cpm_time[5])
137 {
138 uint16_t days = 1;
139 uint_fast8_t leap=2;
140
141 for (int year=78; year < tmp->tm_year; year++) {
142 days = days + 365 + (leap == 0);
143 leap = (leap+1)%4;
144 }
145 days += tmp->tm_yday;
146
147 cpm_time[0] = bin2bcd(tmp->tm_sec);
148 cpm_time[1] = bin2bcd(tmp->tm_min);
149 cpm_time[2] = bin2bcd(tmp->tm_hour);
150 cpm_time[3] = days;
151 cpm_time[4] = days >> 8;
152 }
153
154 /* get/set cp/m time */
155 void do_msg_get_set_time(uint8_t subf, int len, uint8_t * msg)
156 {
157 struct tm t;
158 uint8_t cpm_time[5];
159 int rc;
160
161 memset(cpm_time, 0, ARRAY_SIZE(cpm_time));
162
163 switch (subf) {
164 case 3: /* set date & time */
165 /* initialize t with current time */
166 rc = rtc_get (&t);
167
168 if (rc >= 0) {
169 /* insert new date & time */
170 if (mk_date_time (len, msg, &t) != 0) {
171 my_puts_P(PSTR("## set_time: Bad date format\n"));
172 break;
173 }
174
175 time_t time;
176 time = mk_gmtime(&t);
177 gmtime_r(&time, &t);
178
179 /* and write to RTC */
180 rc = rtc_set (&t);
181 if(rc)
182 my_puts_P(PSTR("## set_time: Set date failed\n"));
183 } else {
184 my_puts_P(PSTR("## set_time: Get date failed\n"));
185 }
186 /* FALL TROUGH */
187 case 2: /* get date & time */
188 rc = rtc_get (&t);
189 if (rc >= 0) {
190 time_t time;
191 time = mk_gmtime(&t);
192 //mktime(&t);
193 gmtime_r(&time, &t);
194
195 mk_cpm_time(&t, cpm_time);
196 } else {
197 my_puts_P(PSTR("## get_time: Get date failed\n"));
198 }
199 break;
200 }
201
202 msg_xmit(3, subf, sizeof(cpm_time), cpm_time);
203 }
204
205 /* ---------------------------------------------------------------------------*/
206
207 static uint8_t drv;
208 static uint8_t disk_buffer[CONFIG_CPM_BLOCK_SIZE];
209 static struct cpm_drive_s drv_table[CONFIG_CPM_MAX_DRIVE];
210 static int handle_cpm_drv_to;
211
212 typedef enum {SINGLE, START, MIDDLE, END} dbgmsg_t;
213
214 void drv_debug(dbgmsg_t phase, const FLASH char *const fmt, ...) \
215 {
216 struct cpm_drive_s *dp = &drv_table[drv];
217
218 if (dp->opt & DRV_OPT_DEBUG) {
219
220 va_list ap;
221 va_start (ap, fmt);
222
223 if (phase == SINGLE || phase == START)
224 printf_P(PSTR("# %7lu dsk%d: "), get_timer(0), drv);
225
226 vfprintf_P (stdout, fmt, ap);
227
228 if (phase == SINGLE || phase == END)
229 putc('\n', stdout);
230
231 va_end (ap);
232 }
233 }
234
235 int drv_list(void)
236 {
237 for (uint8_t i = 0; i < CONFIG_CPM_MAX_DRIVE; i++) {
238 struct cpm_drive_s * p = &drv_table[i];
239 if (p->img_name) {
240 printf_P(PSTR(" dsk%d: %2S %3S attached to %s\n"), i,
241 p->opt&DRV_OPT_RO ? PSTR("RO") : PSTR("RW"),
242 p->opt&DRV_OPT_DEBUG ? PSTR("DBG") : PSTR(""),
243 p->img_name);
244 }
245 }
246 return 0;
247 }
248
249 int drv_detach(uint8_t unit)
250 {
251 drv = unit;
252 if (drv < CONFIG_CPM_MAX_DRIVE) {
253 struct cpm_drive_s *p = &drv_table[drv];
254
255 drv_debug(SINGLE, PSTR("detach from '%s'"), p->img_name ? p->img_name : "-");
256
257 if (p->img_name) {
258 f_close(&p->fd);
259 free(p->img_name);
260 p->opt = 0;
261 p->flags &= ~DRV_FLG_DIRTY;
262 p->img_name = NULL;
263
264 uint32_t scb = getenv_ulong(PSTR(ENV_CPM3_SCB), 16, 0);
265 if (scb && (z80_bus_cmd(Request) & ZST_ACQUIRED)) {
266 z80_write(scb + 0xf0, 0xff);
267 z80_write(p->dph + 11, 0xff);
268 z80_bus_cmd(Release);
269 }
270 }
271 }
272 return 0;
273 }
274
275 static int drv_find_file_attached(const char *fn)
276 {
277 for (uint8_t i = 0; i < CONFIG_CPM_MAX_DRIVE; i++) {
278 struct cpm_drive_s *p = &drv_table[i];
279 if (p->img_name && !strcmp(fn, p->img_name)) {
280 return i;
281 }
282 }
283 return -1;
284 }
285
286 int drv_attach(uint8_t unit, const char *filename, drv_opt_t options)
287 {
288 int res;
289
290 drv = unit;
291 if (drv >= CONFIG_CPM_MAX_DRIVE)
292 return EATRANGE;
293
294 struct cpm_drive_s *p = &drv_table[drv];
295
296 if (options & DRV_OPT_REATTATCH) {
297 if (filename) {
298 return EUNEXPARG;
299 }
300
301 if (!p->img_name) {
302 return EATNOT;
303 }
304
305 /* change options */
306 if ((p->opt ^ options) & DRV_OPT_RO) {
307 f_close(&p->fd);
308 res = f_open(&p->fd, p->img_name,
309 FA_READ | (options&DRV_OPT_RO ? 0 : FA_WRITE));
310 }
311
312 p->opt = options & ~DRV_OPT_REATTATCH;
313
314 } else {
315
316 if (p->img_name)
317 return EATALRDY;
318 if (drv_find_file_attached(filename) >= 0)
319 return EATOTHER;
320
321 p->opt = options;
322
323 /* new attachment */
324
325 if ((p->img_name = strdup(filename)) == NULL)
326 return ENOMEM;
327
328 res = f_open(&p->fd, p->img_name,
329 FA_READ | (options&DRV_OPT_RO ? 0 : FA_WRITE));
330
331 if (!res && f_size(&p->fd) < CONFIG_CPM_DISKSIZE) {
332 #if 0
333 unsigned int bw;
334 debug_cpmsd(" expanding image file from %ld to %ld\n",
335 f_size(&p->fd), CONFIG_CPM_DISKSIZE);
336
337 res = f_lseek(&p->fd, CONFIG_CPM_DISKSIZE-CONFIG_CPM_BLOCK_SIZE);
338 if (!res) {
339 memset(disk_buffer, 0xe5, CONFIG_CPM_BLOCK_SIZE);
340 res = f_write(&p->fd, disk_buffer, CONFIG_CPM_BLOCK_SIZE, &bw);
341 if (res || bw < CONFIG_CPM_BLOCK_SIZE) {
342 debug_cpmsd(" failed! res: %d, bytes written: %u\n", res, bw);
343 }
344 p->flags |= DRV_FLG_DIRTY;
345 bg_setstat(handle_cpm_drv_to, 1);
346 }
347 #else
348 drv_debug(SINGLE, PSTR("wrong image file size: %ld, should be %ld"),
349 f_size(&p->fd), CONFIG_CPM_DISKSIZE);
350 res = 64;
351 #endif
352 }
353 if (res) {
354 drv_detach(drv);
355 return EATOPEN;
356 }
357 }
358
359 return ESUCCESS;
360 }
361
362
363 int cpm_drv_to(int state)
364 {
365 static uint32_t ts;
366
367 switch(state) {
368 case 0:
369 break;
370
371 case 1:
372 ts = get_timer(0);
373 state = 2;
374 break;
375
376 case 2:
377 if (get_timer(ts) > 1000) {
378 for (uint_fast8_t i=0; i < CONFIG_CPM_MAX_DRIVE; i++) {
379 if (drv_table[i].flags & DRV_FLG_DIRTY) {
380 drv_table[i].flags &= ~DRV_FLG_DIRTY;
381 f_sync(&drv_table[i].fd);
382 drv = i;
383 drv_debug(SINGLE, PSTR("f_sync"));
384 }
385 }
386 state = 0;
387 }
388 }
389 return state;
390 }
391
392 static const FLASH char * const FLASH rc_messages[] = {
393 FSTR("OK"),
394 FSTR("Internal error: wrong message len"), /* 01 */
395 FSTR("Invalid relative drive #"), /* 02 */
396 FSTR("Bus timeout"), /* 03 */
397 FSTR("Access byond disk size"), /* 04 */
398 FSTR("Write protect"), /* 05 */
399 FSTR("No media"), /* 06 */
400 FSTR("R/W address == 0 !!!!"), /* 07 */
401 };
402
403 void msg_cpm_result(uint8_t subf, uint8_t rc, int res)
404 {
405 uint8_t result_msg[3];
406
407 if (res)
408 rc |= 0x80;
409
410 result_msg[0] = rc;
411 result_msg[1] = res;
412 result_msg[2] = res >> 8;
413
414 msg_xmit(2, subf, sizeof(result_msg), result_msg);
415
416 if (rc) {
417 #if defined(GCC_BUG_61443)
418 char msg[40];
419 strncpy_P(msg, rc_messages[rc & 0x7f], sizeof msg -1);
420 drv_debug(END, PSTR(" rc: %.02x/%d, '%s'"),
421 rc, res, msg);
422 #else
423 drv_debug(END, PSTR(" rc: %.02x/%d, '%S'"),
424 rc, res, rc_messages[rc & 0x7f]);
425 #endif
426 } else
427 drv_debug(END, PSTR(""));
428
429 }
430
431 /*
432 db 2 ; disk command
433 ds 1 ; subcommand (login/read/write)
434 ds 1 ; @adrv (8 bits) +0
435 ds 1 ; @rdrv (8 bits) +1
436 ds 3 ; @xdph (24 bits) +2
437 */
438
439 void do_msg_cpm_login(uint8_t subf, int len, uint8_t * msg)
440 {
441 struct cpm_drive_s *dp;
442 FRESULT res = 0;
443
444 (void)subf;
445
446 /* Get relative drive number */
447 drv = msg[1];
448 drv_debug(START, PSTR("login"));
449
450 if (len != 5) {
451 return msg_cpm_result(subf, 0x01, res);
452 }
453
454 if ( drv >= CONFIG_CPM_MAX_DRIVE) {
455 /* invalid relative drive number */
456 return msg_cpm_result(subf, 0x02, res);
457 }
458
459 dp = &drv_table[drv];
460 dp->flags &= ~DRV_FLG_OPEN;
461 dp->dph = ((uint32_t)msg[4] << 16) + ((uint16_t)msg[3] << 8) + msg[2];
462
463 if (dp->img_name == NULL) {
464 /* no file attached */
465 return msg_cpm_result(subf, 0x06, res);
466 }
467
468 f_close(&dp->fd);
469 res = f_open(&dp->fd, dp->img_name,
470 FA_READ | (dp->opt&DRV_OPT_RO ? 0 : FA_WRITE));
471
472 dp->flags |= DRV_FLG_OPEN;
473
474 /* send result*/
475 msg_cpm_result(subf, 0x00, res);
476 }
477
478
479 /*
480 db 2 ; disk command
481 ds 1 ; subcommand (login/read/write)
482 ds 1 ; @adrv (8 bits) +0
483 ds 1 ; @rdrv (8 bits) +1
484 ds 2 ; @trk (16 bits) +2
485 ds 2 ; @sect(16 bits) +4
486 ds 1 ; @cnt (8 bits) +6
487 ds 3 ; phys. transfer addr +7
488 */
489
490 #define ADRV 0
491 #define RDRV 1
492 #define TRK 2
493 #define SEC 4
494 #define CNT 6
495 #define ADDR 7
496
497 void do_msg_cpm_rw(uint8_t subf, int len, uint8_t * msg)
498 {
499 struct cpm_drive_s *dp;
500 uint32_t addr;
501 uint32_t pos;
502 uint16_t track;
503 uint16_t sec;
504 uint8_t secs;
505 bool dowrite;
506 FRESULT res = 0;
507 uint8_t rc = 0;
508 bool buserr = 0;
509
510 drv = msg[RDRV];
511 dowrite = (subf == 2);
512
513 drv_debug(START, PSTR("%2S"), dowrite ? PSTR("W ") : PSTR(" R"));
514
515 if (len != 10) {
516 return msg_cpm_result(subf, 0x01, res);
517 }
518 if ( drv>= CONFIG_CPM_MAX_DRIVE) {
519 return msg_cpm_result(subf, 0x02, res);
520 }
521
522 dp = &drv_table[drv];
523 track = (uint16_t)(msg[TRK+1] << 8) + msg[TRK];
524 sec = (uint16_t)(msg[SEC+1] << 8) + msg[SEC];
525 secs = msg[CNT];
526 addr = ((uint32_t)msg[ADDR+2] << 16) + ((uint16_t)msg[ADDR+1] << 8) + msg[ADDR];
527
528 if (dp->img_name == NULL) {
529 /* no media */
530 return msg_cpm_result(subf, 0x06, res);
531 }
532
533 /* TODO: tracks per sector from dpb */
534 pos = (track * 8UL + sec) * CONFIG_CPM_BLOCK_SIZE;
535
536 drv_debug(MIDDLE, PSTR(" T:%4d, S:%2d, cnt:%2d, lba: %.8lx, addr: %.5lx"),
537 track, sec, secs, pos, addr);
538
539 if (addr == 0) {
540 return msg_cpm_result(subf, 0x07, res);
541 }
542
543 if (dowrite && dp->opt & DRV_OPT_RO) {
544 return msg_cpm_result(subf, 0x05, res);
545 }
546
547
548 if (pos + secs * CONFIG_CPM_BLOCK_SIZE > CONFIG_CPM_DISKSIZE) {
549 drv_debug(MIDDLE, PSTR(" access > DISKSIZE:%.8lx!"),
550 CONFIG_CPM_DISKSIZE);
551 return msg_cpm_result(subf, 0x04, res);
552 }
553
554 res = f_lseek(&dp->fd, pos);
555
556 while (!res && secs--) {
557 unsigned int brw;
558 if (dowrite) {
559 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
560 buserr = 1;
561 break;
562 } else {
563 z80_read_block(disk_buffer, addr, CONFIG_CPM_BLOCK_SIZE);
564 z80_bus_cmd(Release);
565 }
566 res = f_write(&dp->fd, disk_buffer, CONFIG_CPM_BLOCK_SIZE, &brw);
567 } else {
568 res = f_read(&dp->fd, disk_buffer, CONFIG_CPM_BLOCK_SIZE, &brw);
569 if (res == FR_OK) {
570 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
571 buserr = 1;
572 break;
573 } else {
574 z80_write_block(disk_buffer, addr, CONFIG_CPM_BLOCK_SIZE);
575 z80_bus_cmd(Release);
576 }
577 }
578 }
579 if (brw != CONFIG_CPM_BLOCK_SIZE) {
580 drv_debug(MIDDLE, PSTR(" short rd/wr: res: %d, brw: %u"),
581 res, brw);
582 res = 64;
583 }
584 addr += CONFIG_CPM_BLOCK_SIZE;
585 }
586
587 if (dowrite && !res) {
588 dp->flags |= DRV_FLG_DIRTY;
589 bg_setstat(handle_cpm_drv_to, 1);
590 }
591
592 if (buserr) {
593 /* Bus timeout. how can this happen? */
594 rc = 0x03;
595 }
596
597 /* send result*/
598 msg_cpm_result(subf, rc, res);
599 }
600
601
602 const FLASH struct msg_item z80_messages[] =
603 {
604 { 0, /* fct nr. */
605 1, 3, /* sub fct nr. from, to */
606 do_msg_ini_memfifo},
607 { 1,
608 1, 1,
609 do_msg_char_out},
610 { 1,
611 2, 2,
612 do_msg_echo},
613 { 2,
614 0, 0,
615 do_msg_cpm_login},
616 { 2,
617 1, 2,
618 do_msg_cpm_rw},
619 { 3,
620 1, 1,
621 do_msg_get_timer},
622 { 3,
623 2, 3, /* 2: get, 3: set time and date */
624 do_msg_get_set_time},
625 { 0xff, /* end mark */
626 0, 0,
627 0},
628
629 };
630
631
632
633
634 void do_message(int len, uint8_t *msg)
635 {
636 uint8_t fct, sub_fct;
637 int_fast8_t i = 0;
638
639 if (len >= 2) {
640 fct = *msg++;
641 sub_fct = *msg++;
642 len -= 2;
643
644 while (fct != z80_messages[i].fct) {
645 if (z80_messages[i].fct == 0xff) {
646 DBG_P(1, "do_message: Unknown function: %i, %i\n",
647 fct, sub_fct);
648 return; /* TODO: unknown message # */
649 }
650
651 ++i;
652 }
653
654 while (fct == z80_messages[i].fct) {
655 if (sub_fct >= z80_messages[i].sub_min &&
656 sub_fct <= z80_messages[i].sub_max )
657 break;
658 ++i;
659 }
660
661 if (z80_messages[i].fct != fct) {
662 DBG_P(1, "do_message: Unknown sub function: %i, %i\n",
663 fct, sub_fct);
664 return; /* TODO: unknown message sub# */
665 }
666
667 (z80_messages[i].func)(sub_fct, len, msg);
668
669
670 } else {
671 /* TODO: error */
672 DBG_P(1, "do_message: to few arguments (%i); this shouldn't happen!\n", len);
673 }
674 }
675
676
677
678 #define CTRBUF_LEN 256
679
680 void check_msg_fifo(void)
681 {
682 int ch;
683 static int_fast8_t state;
684 static int msglen,idx;
685 static uint8_t buffer[CTRBUF_LEN];
686
687 while ((ch = z80_memfifo_getc(fifo_msgin)) >= 0) {
688 switch (state) {
689 case 0: /* wait for start of message */
690 if (ch == 0xAE) { /* TODO: magic number */
691 msglen = 0;
692 idx = 0;
693 state = 1;
694 }
695 break;
696 case 1: /* get msg len */
697 if (ch > 0 && ch <= CTRBUF_LEN) {
698 msglen = ch;
699 state = 2;
700 } else
701 state = 0;
702 break;
703 case 2: /* get message */
704 buffer[idx++] = ch;
705 if (idx == msglen) {
706 do_message(msglen, buffer);
707 state = 0;
708 }
709 break;
710 }
711 }
712 }
713
714
715 int msg_handling(int state)
716 {
717 bool pending;
718
719 ATOMIC_BLOCK(ATOMIC_FORCEON) {
720 pending = (Stat & S_MSG_PENDING) != 0;
721 Stat &= ~S_MSG_PENDING;
722 }
723
724 if (pending) {
725 uint8_t init_request;
726 z80_bus_cmd(Request);
727 init_request = z80_read(0x43);
728 z80_bus_cmd(Release);
729 if ( init_request != 0) {
730 /* Get address of fifo 0 */
731 z80_bus_cmd(Request);
732 uint32_t fifo_addr = z80_read(0x40) +
733 ((uint16_t) z80_read(0x40+1) << 8) +
734 ((uint32_t) z80_read(0x40+2) << 16);
735 z80_write(0x43, 0);
736 z80_bus_cmd(Release);
737
738 if (fifo_addr != 0) {
739 z80_memfifo_init(fifo_msgin, fifo_addr);
740 state = 1;
741 } else
742 state = 0;
743
744 } else {
745 check_msg_fifo();
746 }
747 }
748
749 return state;
750 }
751
752
753 static int handle_msg_handling;
754
755 void setup_z180_serv(void)
756 {
757
758 handle_msg_handling = bg_register(msg_handling, 0);
759 handle_cpm_drv_to = bg_register(cpm_drv_to, 0);
760 }
761
762 void restart_z180_serv(void)
763 {
764 z80_bus_cmd(Request);
765 z80_memset(0x40, 0, 4);
766 z80_bus_cmd(Release);
767
768 for (int i = 0; i < NUM_FIFOS; i++)
769 z80_memfifo_init(i, 0);
770 bg_setstat(handle_msg_handling, 0);
771
772 }
773
774 #if 0
775 /*--------------------------------------------------------------------------*/
776
777 const FLASH uint8_t iniprog[] = {
778 0xAF, // xor a
779 0xED, 0x39, 0x36, // out0 (rcr),a ;disable DRAM refresh
780 0x3E, 0x30, // ld a,030h
781 0xED, 0x39, 0x32 //out0 (dcntl),a ;0 mem, max i/0 wait states
782 };
783
784 const FLASH uint8_t sertest[] = {
785 0xAF, // xor a
786 0xED, 0x39, 0x36, // out0 (rcr),a ;disable DRAM refresh
787 0x3E, 0x30, // ld a,030h
788 0xED, 0x39, 0x32, // out0 (dcntl),a ;0 mem, max i/0 wait states
789 0x3E, 0x80, // ld a,M_MPBT ;no MP, PS=10, DR=16, SS=0
790 0xED, 0x39, 0x03, // out0 (cntlb1),a
791 0x3E, 0x64, // ld a,M_RE + M_TE + M_MOD2 ;
792 0xED, 0x39, 0x01, // out0 (cntla1),a
793 0x3E, 0x00, // ld a,0
794 0xED, 0x39, 0x05, // out0 (stat1),a ;Enable rx interrupts
795 0xED, 0x38, 0x05, //l0:in0 a,(stat1)
796 0xE6, 0x80, // and 80h
797 0x28, 0xF9, // jr z,l0
798 0xED, 0x00, 0x09, // in0 b,(rdr1)
799 0xED, 0x38, 0x05, //l1:in0 a,(stat1)
800 0xE6, 0x02, // and 02h
801 0x28, 0xF9, // jr z,l1
802 0xED, 0x01, 0x07, // out0 (tdr1),b
803 0x18, 0xEA, // jr l0
804 };
805
806 const FLASH uint8_t test1[] = {
807 0xAF, // xor a
808 0xED, 0x39, 0x36, // out0 (rcr),a ;disable DRAM refresh
809 0x3E, 0x30, // ld a,030h
810 0xED, 0x39, 0x32, // out0 (dcntl),a ;0 mem, max i/0 wait states
811 0x21, 0x1E, 0x00, // ld hl,dmclrt ;load DMA registers
812 0x06, 0x08, // ld b,dmct_e-dmclrt
813 0x0E, 0x20, // ld c,sar0l
814 0xED, 0x93, // otimr
815 0x3E, 0xC3, // ld a,0c3h ;dst +1, src +1, burst
816 0xED, 0x39, 0x31, // out0 (dmode),a ;
817 0x3E, 0x62, // ld a,062h ;enable dma0,
818 0xED, 0x39, 0x30, //cl_1: out0 (dstat),a ;copy 64k
819 0x18, 0xFB, // jr cl_1 ;
820 0x00, 0x00, //dmclrt: dw 0 ;src (inc)
821 0x00, // db 0 ;src
822 0x00, 0x00, // dw 0 ;dst (inc),
823 0x00, // db 0 ;dst
824 0x00, 0x00, // dw 0 ;count (64k)
825 };
826 #endif