+ if (len != 5) { /* TODO: check adrv, rdrv */
+ return msg_cpm_result(subf, 0x01, res);
+ }
+
+ debug_cpmsd("\n## %7lu login: %c:\n", get_timer(0), msg[0]+'A');
+
+
+ drv = msg[0];
+ if ( drv>= MAX_DRIVE) {
+ return msg_cpm_result(subf, 0x02, res);
+ }
+
+/*
+ uint32_t dph = ((uint32_t)msg[4] << 16) + ((uint16_t)msg[3] << 8) + msg[2];
+*/
+
+ if (drv_table[drv].img_name != NULL) {
+ debug_cpmsd("## %7lu close: '%s'\n", get_timer(0), drv_table[drv].img_name);
+ f_close(&drv_table[drv].fd);
+ drv_table[drv].dirty = false;
+ free(drv_table[drv].img_name);
+ drv_table[drv].img_name = NULL;
+ }
+
+ strcpy_P((char *)disk_buffer, PSTR("dsk0"));
+ disk_buffer[3] = msg[0] + '0';
+ if (((np = getenv_char((char*)disk_buffer)) == NULL) ||
+ ((drv_table[drv].img_name = strdup(np)) == NULL)) {
+ return msg_cpm_result(subf, 0x03, res);
+ }
+
+
+ res = f_open(&drv_table[drv].fd, drv_table[drv].img_name,
+ FA_WRITE | FA_READ);
+
+ debug_cpmsd("## %7lu open: '%s', (env: '%s'), res: %d\n", get_timer(0),
+ drv_table[drv].img_name, disk_buffer, res);
+
+ /* send result*/
+ msg_cpm_result(subf, 0x00, res);
+}
+
+
+/*
+ db 2 ; disk command
+ ds 1 ; subcommand (login/read/write)
+ ds 1 ; @adrv (8 bits) +0
+ ds 1 ; @rdrv (8 bits) +1
+ ds 2 ; @trk (16 bits) +2
+ ds 2 ; @sect(16 bits) +4
+ ds 1 ; @cnt (8 bits) +6
+ ds 3 ; phys. transfer addr +7
+*/
+
+#define ADRV 0
+#define RDRV 1
+#define TRK 2
+#define SEC 4
+#define CNT 6
+#define ADDR 7
+
+void do_msg_cpm_rw(uint8_t subf, int len, uint8_t * msg)
+{
+ uint8_t drv;
+ uint32_t addr;
+ uint32_t pos;
+ uint8_t secs;
+ bool dowrite = (subf == 2);
+ FRESULT res = 0;
+ uint8_t rc = 0;
+ bool buserr = 0;
+
+ if (len != 10) { /* TODO: check adrv, rdrv */
+ return msg_cpm_result(subf, 0x01, res);
+ }
+
+ drv = msg[ADRV];
+ if ( drv>= MAX_DRIVE) {
+ return msg_cpm_result(subf, 0x02, res);
+ }
+
+ secs = msg[CNT];
+ addr = ((uint32_t)msg[ADDR+2] << 16) + ((uint16_t)msg[ADDR+1] << 8) + msg[ADDR];
+
+
+ /* TODO: tracks per sector from dpb */
+ pos = (((uint16_t)(msg[TRK+1] << 8) + msg[TRK]) * 8
+ + ((uint32_t)(msg[SEC+1] << 8) + msg[SEC])) * BLOCK_SIZE;
+
+ debug_cpmsd("## %7lu cpm_rw: %s %c: trk:%4d, sec: %d, pos: %.8lx, secs: %2d, "
+ "addr: %.5lx\n", get_timer(0), dowrite ? "write" : " read",
+ msg[ADRV]+'A', ((uint16_t)(msg[TRK+1] << 8) + msg[TRK]), msg[SEC],
+ pos, msg[CNT], addr);
+
+ res = f_lseek(&drv_table[drv].fd, pos);
+ while (!res && secs--) {
+ unsigned int brw;
+ if (dowrite) {
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ buserr = 1;
+ break;
+ } else {
+ z80_read_block(disk_buffer, addr, BLOCK_SIZE);
+ z80_bus_cmd(Release);
+ }
+ res = f_write(&drv_table[drv].fd, disk_buffer, BLOCK_SIZE, &brw);
+ } else {
+ res = f_read(&drv_table[drv].fd, disk_buffer, BLOCK_SIZE, &brw);
+ if (res == FR_OK && brw == BLOCK_SIZE) {
+ if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
+ buserr = 1;
+ break;
+ } else {
+ z80_write_block(disk_buffer, addr, BLOCK_SIZE);
+ z80_bus_cmd(Release);
+ }
+ }
+ }
+
+ if (brw != BLOCK_SIZE) {
+ debug_cpmsd("## %7lu f_read res: %d, bytes rd/wr: %u\n", get_timer(0), res, brw);
+ dump_ram(disk_buffer, 0, 64, "Read Data");
+ res = -1;
+ }
+
+ addr += BLOCK_SIZE;
+ }
+
+ if (dowrite && !res) {
+// res = f_sync(&drv_table[drv].fd);
+ drv_table[drv].dirty = true;
+ bg_setstat(handle_cpm_drv_to, 1);
+ }
+
+
+ if (buserr) {
+ debug_cpmsd("Bus timeout\n");
+ rc = 0x03;
+ }
+
+ /* send result*/
+ msg_cpm_result(subf, rc, res);