diff --git a/src/hdd.c b/src/hdd.c index 8128456..5d8a74d 100644 --- a/src/hdd.c +++ b/src/hdd.c @@ -20,11 +20,12 @@ static struct int is_mfm; } hdd_controllers[] = { - {"None", "none", &null_hdd_device, 0}, - {"AT Fixed Disk Adapter", "mfm_at", &mfm_at_device, 1}, - {"Fixed Disk Adapter (Xebec)", "mfm_xebec", &mfm_xebec_device, 1}, - {"XTIDE", "xtide", &xtide_device, 0}, - {"XTIDE (AT)", "xtide_at", &xtide_at_device, 0}, + {"None", "none", &null_hdd_device, 0}, + {"AT Fixed Disk Adapter", "mfm_at", &mfm_at_device, 1}, + {"DTC 5150X", "dtc5150x", &dtc_5150x_device, 1}, + {"Fixed Disk Adapter (Xebec)", "mfm_xebec", &mfm_xebec_device, 1}, + {"XTIDE", "xtide", &xtide_device, 0}, + {"XTIDE (AT)", "xtide_at", &xtide_at_device, 0}, {"", "", NULL, 0} }; diff --git a/src/mfm_xebec.c b/src/mfm_xebec.c index 65750be..5f5a788 100644 --- a/src/mfm_xebec.c +++ b/src/mfm_xebec.c @@ -70,12 +70,8 @@ typedef struct xebec_t uint8_t completion_byte; uint8_t error; -// int cylinder[2]; - int drive_sel; -// int max_cylinders[2], max_heads[2]; - mfm_drive_t drives[2]; int sector, head, cylinder; @@ -98,14 +94,22 @@ typedef struct xebec_t #define CMD_RECALIBRATE 0x01 #define CMD_READ_STATUS 0x03 #define CMD_VERIFY_SECTORS 0x05 +#define CMD_FORMAT_TRACK 0x06 #define CMD_READ_SECTORS 0x08 #define CMD_WRITE_SECTORS 0x0a +#define CMD_SEEK 0x0b #define CMD_INIT_DRIVE_PARAMS 0x0c #define CMD_WRITE_SECTOR_BUFFER 0x0f #define CMD_BUFFER_DIAGNOSTIC 0xe0 #define CMD_CONTROLLER_DIAGNOSTIC 0xe4 +#define CMD_DTC_GET_DRIVE_PARAMS 0xfb +#define CMD_DTC_SET_STEP_RATE 0xfc +#define CMD_DTC_SET_GEOMETRY 0xfe +#define CMD_DTC_GET_GEOMETRY 0xff -#define ERR_NOT_READY 0x04 +#define ERR_NOT_READY 0x04 +#define ERR_SEEK_ERROR 0x15 +#define ERR_ILLEGAL_SECTOR_ADDRESS 0x21 static uint8_t xebec_read(uint16_t port, void *p) { @@ -257,21 +261,25 @@ static int xebec_get_sector(xebec_t *xebec, off64_t *addr) if (drive->current_cylinder != xebec->cylinder) { pclog("mfm_get_sector: wrong cylinder\n"); + xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS; return 1; } if (xebec->head > heads) { pclog("mfm_get_sector: past end of configured heads\n"); + xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS; return 1; } if (xebec->head > drive->hpc) { pclog("mfm_get_sector: past end of heads\n"); + xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS; return 1; } if (xebec->sector >= 17) { pclog("mfm_get_sector: past end of sectors\n"); + xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS; return 1; } @@ -368,7 +376,12 @@ static void xebec_callback(void *p) // pclog("xebec Verify %i,%i,%i %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->sector_count); if (xebec_get_sector(xebec, &addr)) - fatal("xebec_get_sector failed\n"); + { + pclog("xebec_get_sector failed\n"); + xebec_error(xebec, xebec->error); + xebec_complete(xebec); + return; + } xebec_next_sector(xebec); @@ -385,11 +398,37 @@ static void xebec_callback(void *p) } break; + case CMD_FORMAT_TRACK: + { + off64_t addr; + int c; + + xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2); + drive->current_cylinder = (xebec->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : xebec->cylinder; + xebec->head = xebec->command[1] & 0x1f; +// pclog("Format track %i,%i\n", xebec->cylinder, xebec->head); + + if (xebec_get_sector(xebec, &addr)) + { + pclog("xebec_get_sector failed\n"); + xebec_error(xebec, xebec->error); + xebec_complete(xebec); + return; + } + + fseeko64(drive->hdfile, addr * 512, SEEK_SET); + for (c = 0; c < 17; c++) + fwrite(xebec->sector_buf, 512, 1, drive->hdfile); + + xebec_complete(xebec); + } + break; + case CMD_READ_SECTORS: switch (xebec->state) { case STATE_START_COMMAND: - xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xe0) << 3); + xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2); drive->current_cylinder = (xebec->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : xebec->cylinder; xebec->head = xebec->command[1] & 0x1f; xebec->sector = xebec->command[2] & 0x1f; @@ -397,54 +436,91 @@ static void xebec_callback(void *p) xebec->state = STATE_SEND_DATA; xebec->data_pos = 0; xebec->data_len = 512; + { + off64_t addr; + + if (xebec_get_sector(xebec, &addr)) + { + xebec_error(xebec, xebec->error); + xebec_complete(xebec); + return; + } +// pclog("xebec Read %i,%i,%i\n", xebec->cylinder, xebec->head, xebec->sector); + + fseeko64(drive->hdfile, addr * 512, SEEK_SET); + fread(xebec->sector_buf, 512, 1, drive->hdfile); + readflash = 1; + } if (xebec->irq_dma_mask & DMA_ENA) xebec->callback = XEBEC_TIME; else - fatal("READ_SECTORS no DMA\n"); + { + xebec->status = STAT_BSY | STAT_IO | STAT_REQ; + memcpy(xebec->data, xebec->sector_buf, 512); + } break; case STATE_SEND_DATA: + xebec->status = STAT_BSY; if (xebec->irq_dma_mask & DMA_ENA) { - do + for (; xebec->data_pos < 512; xebec->data_pos++) { - if (!xebec->data_pos) + int val = dma_channel_write(3, xebec->sector_buf[xebec->data_pos]); + + if (val == DMA_NODATA) { - off64_t addr; - - if (xebec_get_sector(xebec, &addr)) - fatal("xebec_get_sector failed\n"); - pclog("xebec Read %i,%i,%i %i %08llx %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->sector_count, addr, xebec->drive_sel); - fseeko64(drive->hdfile, addr * 512, SEEK_SET); - fread(xebec->sector_buf, 512, 1, drive->hdfile); + pclog("CMD_READ_SECTORS out of data!\n"); + xebec->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ; + xebec->callback = XEBEC_TIME; + return; } - - for (; xebec->data_pos < 512; xebec->data_pos++) - { - int val = dma_channel_write(3, xebec->sector_buf[xebec->data_pos]); - - if (val == DMA_NODATA) - { - pclog("CMD_READ_SECTORS out of data!\n"); - xebec->callback = XEBEC_TIME; - return; - } - } - - xebec_next_sector(xebec); - - xebec->data_pos = 0; - - xebec->sector_count = (xebec->sector_count-1) & 0xff; - - readflash = 1; - } while (xebec->sector_count); - - xebec_complete(xebec); + } + xebec->state = STATE_SENT_DATA; + xebec->callback = XEBEC_TIME; } else - fatal("Read sectors no DMA!\n"); + fatal("Read sectors no DMA! - shouldn't get here\n"); break; + + case STATE_SENT_DATA: + xebec_next_sector(xebec); + + xebec->data_pos = 0; + + xebec->sector_count = (xebec->sector_count-1) & 0xff; + + if (xebec->sector_count) + { + off64_t addr; + + if (xebec_get_sector(xebec, &addr)) + { + xebec_error(xebec, xebec->error); + xebec_complete(xebec); + return; + } + +// pclog("xebec Read %i,%i,%i\n", xebec->cylinder, xebec->head, xebec->sector); + + fseeko64(drive->hdfile, addr * 512, SEEK_SET); + fread(xebec->sector_buf, 512, 1, drive->hdfile); + readflash = 1; + + xebec->state = STATE_SEND_DATA; + + if (xebec->irq_dma_mask & DMA_ENA) + xebec->callback = XEBEC_TIME; + else + { + xebec->status = STAT_BSY | STAT_IO | STAT_REQ; + memcpy(xebec->data, xebec->sector_buf, 512); + } + } + else + xebec_complete(xebec); + break; + default: fatal("CMD_READ_SECTORS: bad state %i\n", xebec->state); @@ -455,7 +531,7 @@ static void xebec_callback(void *p) switch (xebec->state) { case STATE_START_COMMAND: - xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xe0) << 3); + xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2); drive->current_cylinder = (xebec->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : xebec->cylinder; xebec->head = xebec->command[1] & 0x1f; xebec->sector = xebec->command[2] & 0x1f; @@ -466,61 +542,93 @@ static void xebec_callback(void *p) if (xebec->irq_dma_mask & DMA_ENA) xebec->callback = XEBEC_TIME; else - fatal("READ_SECTORS no DMA\n"); + xebec->status = STAT_BSY | STAT_REQ; break; case STATE_RECEIVE_DATA: + xebec->status = STAT_BSY; if (xebec->irq_dma_mask & DMA_ENA) { - do - { - if (!xebec->data_pos) + for (; xebec->data_pos < 512; xebec->data_pos++) + { + int val = dma_channel_read(3); + + if (val == DMA_NODATA) { - off64_t addr; - -// pclog("xebec Write %i,%i,%i %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->sector_count); - if (xebec_get_sector(xebec, &addr)) - fatal("xebec_get_sector failed\n"); - - fseeko64(drive->hdfile, addr * 512, SEEK_SET); + pclog("CMD_WRITE_SECTORS out of data!\n"); + xebec->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ; + xebec->callback = XEBEC_TIME; + return; } - - for (; xebec->data_pos < 512; xebec->data_pos++) - { - int val = dma_channel_read(3); - - if (val == DMA_NODATA) - { - pclog("CMD_WRITE_SECTORS out of data!\n"); - xebec->callback = XEBEC_TIME; - return; - } - xebec->sector_buf[xebec->data_pos] = val & 0xff; - } - - fwrite(xebec->sector_buf, 512, 1, drive->hdfile); - - xebec_next_sector(xebec); + xebec->sector_buf[xebec->data_pos] = val & 0xff; + } - xebec->data_pos = 0; - - xebec->sector_count = (xebec->sector_count-1) & 0xff; - - readflash = 1; - } while (xebec->sector_count); - - xebec_complete(xebec); + xebec->state = STATE_RECEIVED_DATA; + xebec->callback = XEBEC_TIME; } else - fatal("Write sectors no DMA!\n"); + fatal("Write sectors no DMA! - should never get here\n"); + break; + + case STATE_RECEIVED_DATA: + if (!(xebec->irq_dma_mask & DMA_ENA)) + memcpy(xebec->sector_buf, xebec->data, 512); + + { + off64_t addr; + + if (xebec_get_sector(xebec, &addr)) + { + xebec_error(xebec, xebec->error); + xebec_complete(xebec); + return; + } + +// pclog("xebec Write %i,%i,%i %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->drive_sel); + + fseeko64(drive->hdfile, addr * 512, SEEK_SET); + fwrite(xebec->sector_buf, 512, 1, drive->hdfile); + } + + readflash = 1; + + xebec_next_sector(xebec); + xebec->data_pos = 0; + xebec->sector_count = (xebec->sector_count-1) & 0xff; + + if (xebec->sector_count) + { + xebec->state = STATE_RECEIVE_DATA; + if (xebec->irq_dma_mask & DMA_ENA) + xebec->callback = XEBEC_TIME; + else + xebec->status = STAT_BSY | STAT_REQ; + } + else + xebec_complete(xebec); break; default: fatal("CMD_WRITE_SECTORS: bad state %i\n", xebec->state); } break; - + + case CMD_SEEK: + if (!drive->hdfile) + xebec_error(xebec, ERR_NOT_READY); + else + { + int cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2); + + drive->current_cylinder = (cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : cylinder; + + if (cylinder != drive->current_cylinder) + xebec_error(xebec, ERR_SEEK_ERROR); + } + xebec_complete(xebec); + break; + case CMD_INIT_DRIVE_PARAMS: switch (xebec->state) { @@ -532,7 +640,6 @@ static void xebec_callback(void *p) break; case STATE_RECEIVED_DATA: -// pclog("State STATE_RECEIVED_DATA\n"); drive->cfg_cyl = xebec->data[1] | (xebec->data[0] << 8); drive->cfg_hpc = xebec->data[2]; pclog("Drive %i: cylinders=%i, heads=%i\n", xebec->drive_sel, drive->cfg_cyl, drive->cfg_hpc); @@ -548,24 +655,44 @@ static void xebec_callback(void *p) switch (xebec->state) { case STATE_START_COMMAND: + xebec->state = STATE_RECEIVE_DATA; + xebec->data_pos = 0; + xebec->data_len = 512; + if (xebec->irq_dma_mask & DMA_ENA) + xebec->callback = XEBEC_TIME; + else + xebec->status = STAT_BSY | STAT_REQ; + break; + + case STATE_RECEIVE_DATA: if (xebec->irq_dma_mask & DMA_ENA) { - int c; - - for (c = 0; c < 512; c++) + xebec->status = STAT_BSY; + + for (; xebec->data_pos < 512; xebec->data_pos++) { int val = dma_channel_read(3); if (val == DMA_NODATA) - fatal("CMD_WRITE_SECTOR_DATA out of data!\n"); + { + pclog("CMD_WRITE_SECTOR_BUFFER out of data!\n"); + xebec->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ; + xebec->callback = XEBEC_TIME; + return; + } - xebec->sector_buf[c] = val & 0xff; + xebec->data[xebec->data_pos] = val & 0xff; } - xebec_complete(xebec); + xebec->state = STATE_RECEIVED_DATA; + xebec->callback = XEBEC_TIME; } else - fatal("CMD_WRITE_SECTOR_BUFFER - no DMA\n"); + fatal("CMD_WRITE_SECTOR_BUFFER - should never get here!\n"); + break; + case STATE_RECEIVED_DATA: + memcpy(xebec->sector_buf, xebec->data, 512); + xebec_complete(xebec); break; default: @@ -578,6 +705,75 @@ static void xebec_callback(void *p) xebec_complete(xebec); break; + case 0xfa: + xebec_complete(xebec); + break; + + case CMD_DTC_SET_STEP_RATE: + xebec_complete(xebec); + break; + + case CMD_DTC_GET_DRIVE_PARAMS: + switch (xebec->state) + { + case STATE_START_COMMAND: + xebec->state = STATE_SEND_DATA; + xebec->data_pos = 0; + xebec->data_len = 4; + xebec->status = STAT_BSY | STAT_IO | STAT_REQ; + memset(xebec->data, 0, 4); + xebec->data[0] = drive->tracks & 0xff; + xebec->data[1] = 17 | ((drive->tracks >> 2) & 0xc0); + xebec->data[2] = drive->hpc-1; + pclog("Get drive params %02x %02x %02x %i\n", xebec->data[0], xebec->data[1], xebec->data[2], drive->tracks); + break; + + case STATE_SENT_DATA: + xebec_complete(xebec); + break; + + default: + fatal("CMD_INIT_DRIVE_PARAMS bad state %i\n", xebec->state); + } + break; + + case CMD_DTC_GET_GEOMETRY: + switch (xebec->state) + { + case STATE_START_COMMAND: + xebec->state = STATE_SEND_DATA; + xebec->data_pos = 0; + xebec->data_len = 16; + xebec->status = STAT_BSY | STAT_IO | STAT_REQ; + memset(xebec->data, 0, 16); + xebec->data[0x4] = drive->tracks & 0xff; + xebec->data[0x5] = (drive->tracks >> 8) & 0xff; + xebec->data[0xa] = drive->hpc; + break; + + case STATE_SENT_DATA: + xebec_complete(xebec); + break; + } + break; + + case CMD_DTC_SET_GEOMETRY: + switch (xebec->state) + { + case STATE_START_COMMAND: + xebec->state = STATE_RECEIVE_DATA; + xebec->data_pos = 0; + xebec->data_len = 16; + xebec->status = STAT_BSY | STAT_REQ; + break; + + case STATE_RECEIVED_DATA: + /*Bit of a cheat here - we always report the actual geometry of the drive in use*/ + xebec_complete(xebec); + break; + } + break; + default: fatal("Unknown Xebec command - %02x %02x %02x %02x %02x %02x\n", xebec->command[0], xebec->command[1], @@ -715,3 +911,43 @@ device_t mfm_xebec_device = NULL, NULL }; + +static void *dtc_5150x_init() +{ + xebec_t *xebec = malloc(sizeof(xebec_t)); + memset(xebec, 0, sizeof(xebec_t)); + + loadhd(xebec, 0, ide_fn[0]); + loadhd(xebec, 1, ide_fn[1]); + xebec->switches = 0xff; + + xebec->drives[0].cfg_cyl = xebec->drives[0].tracks; + xebec->drives[0].cfg_hpc = xebec->drives[0].hpc; + xebec->drives[1].cfg_cyl = xebec->drives[1].tracks; + xebec->drives[1].cfg_hpc = xebec->drives[1].hpc; + + rom_init(&xebec->bios_rom, "roms/dtc_cxd21a.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + + io_sethandler(0x0320, 0x0004, xebec_read, NULL, NULL, xebec_write, NULL, NULL, xebec); + + timer_add(xebec_callback, &xebec->callback, &xebec->callback, xebec); + + return xebec; +} +static int dtc_5150x_available() +{ + return rom_present("roms/dtc_cxd21a.bin"); +} + +device_t dtc_5150x_device = +{ + "DTC 5150X", + 0, + dtc_5150x_init, + xebec_close, + dtc_5150x_available, + NULL, + NULL, + NULL, + NULL +}; diff --git a/src/mfm_xebec.h b/src/mfm_xebec.h index d7991e7..c11101c 100644 --- a/src/mfm_xebec.h +++ b/src/mfm_xebec.h @@ -1 +1,2 @@ extern device_t mfm_xebec_device; +extern device_t dtc_5150x_device;