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