static BYTE fdc_do_job_(unsigned int fnum, int buf, unsigned int drv, BYTE job, BYTE *header) { #endif unsigned int dnr; BYTE rc; int ret; int i; disk_addr_t dadr; BYTE *base; BYTE sector_data[256]; BYTE disk_id[2]; drive_t *drive; dadr.track = header[2]; dadr.sector = header[3]; /* determine drive/disk image to use */ if (drv < fdc[fnum].num_drives) { dnr = fnum + drv; } else { /* drive 1 on a single disk drive */ return FDC_ERR_SYNC; } rc = 0; base = &(fdc[fnum].buffer[(buf + 1) << 8]); #ifdef FDC_DEBUG log_message(fdc_log, "do job %02x, buffer %d ($%04x): d%d t%d s%d, " "image=%p, type=%04d", job, buf, (buf + 1) << 8, dnr, dadr.track, dadr.sector, fdc[dnr].image, fdc[dnr].image ? fdc[dnr].image->type : 0); #endif if (fdc[dnr].image == NULL && job != 0xd0) { #ifdef FDC_DEBUG log_message(fdc_log, "dnr=%d, image=NULL -> no disk!", dnr); #endif return FDC_ERR_SYNC; } file_system_bam_get_disk_id(dnr + 8, disk_id); switch (job) { case 0x80: /* read */ if (header[0] != disk_id[0] || header[1] != disk_id[1]) { rc = FDC_ERR_ID; break; } ret = disk_image_read_sector(fdc[dnr].image, sector_data, &dadr); if (ret < 0) { log_error(LOG_DEFAULT, "Cannot read T:%d S:%d from disk image.", dadr.track, dadr.sector); rc = FDC_ERR_DRIVE; } else { memcpy(base, sector_data, 256); rc = FDC_ERR_OK; } break; case 0x90: /* write */ if (header[0] != disk_id[0] || header[1] != disk_id[1]) { rc = FDC_ERR_ID; break; } if (fdc[dnr].image->read_only) { rc = FDC_ERR_WPROT; break; } memcpy(sector_data, base, 256); ret = disk_image_write_sector(fdc[dnr].image, sector_data, &dadr); if (ret < 0) { log_error(LOG_DEFAULT, "Could not update T:%d S:%d on disk image.", dadr.track, dadr.sector); rc = FDC_ERR_DRIVE; } else { rc = FDC_ERR_OK; } break; case 0xA0: /* verify */ if (header[0] != disk_id[0] || header[1] != disk_id[1]) { rc = FDC_ERR_ID; break; } ret = disk_image_read_sector(fdc[dnr].image, sector_data, &dadr); if (ret < 0) { log_error(LOG_DEFAULT, "Cannot read T:%d S:%d from disk image.", dadr.track, dadr.sector); rc = FDC_ERR_DRIVE; } else { rc = FDC_ERR_OK; for (i = 0; i < 256; i++) { if (fnum) { if (sector_data[i] != base[i]) { rc = FDC_ERR_VERIFY; } } else { if (sector_data[i] != base[i]) { rc = FDC_ERR_VERIFY; } } } } break; case 0xB0: /* seek - move to track and read ID(?) */ header[0] = disk_id[0]; header[1] = disk_id[1]; /* header[2] = fdc[dnr].last_track; */ dadr.track = header[2]; header[3] = 1; rc = FDC_ERR_OK; break; case 0xC0: /* bump (to track 0 and back to 18?) */ dadr.track = 1; if (DOS_IS_20(fdc[fnum].drive_type)) { header[2] = 18; } rc = FDC_ERR_OK; break; case 0xD0: /* jump to buffer - but we do not emulate FDC CPU */ #ifdef FDC_DEBUG log_message(fdc_log, "exec buffer %d ($%04x): %02x %02x %02x %02x %02x", buf, (buf + 1) << 8, base[0], base[1], base[2], base[3] ); #endif if (DOS_IS_40(fdc[fnum].drive_type) || DOS_IS_30(fdc[fnum].drive_type)) { if (!memcmp(fdc[fnum].iprom + 0x12f8, &fdc[fnum].buffer[0x100], 0x100)) { fdc[fnum].fdc_state = FDC_RESET2; return 0; } } if (DOS_IS_80(fdc[fnum].drive_type)) { static const BYTE jumpseq[] = { 0x78, 0x6c, 0xfc, 0xff }; if (!memcmp(jumpseq, &fdc[fnum].buffer[0x100], 4)) { fdc[fnum].fdc_state = FDC_RESET0; return 0; } } rc = FDC_ERR_DRIVE; break; case 0xE0: /* execute when drive/head ready. We do not emulate FDC CPU, but we handle the case when a disk is formatted */ /* we have to check for standard format code that is copied to buffers 0-3 */ if (DOS_IS_80(fdc[fnum].drive_type)) { rc = fdc_do_format_D80(fdc, fnum, dnr, dadr.track, dadr.sector, buf, header); } else if (DOS_IS_40(fdc[fnum].drive_type) || DOS_IS_30(fdc[fnum].drive_type)) { rc = fdc_do_format_D40(fdc, fnum, dnr, dadr.track, dadr.sector, buf, header); } else if (DOS_IS_20(fdc[fnum].drive_type)) { rc = fdc_do_format_D20(fdc, fnum, dnr, dadr.track, dadr.sector, buf, header); } else { rc = FDC_ERR_DRIVE; } break; case 0xF0: if (header[0] != disk_id[0] || header[1] != disk_id[1]) { rc = FDC_ERR_ID; break; } /* try to read block header from disk */ rc = FDC_ERR_OK; break; } drive = drive_context[dnr]->drive; drive->current_half_track = 2 * dadr.track; fdc[dnr].last_track = dadr.track; fdc[dnr].last_sector = dadr.sector; return rc; }
static void int_fdc(CLOCK offset, void *data) { CLOCK rclk; int i, j; drive_t *drive; unsigned int fnum; drive_context_t *drv = (drive_context_t *)data; fnum = drv->mynumber; rclk = drive_clk[fnum] - offset; #ifdef FDC_DEBUG if (fdc[fnum].fdc_state < FDC_RUN) { static int old_state[NUM_FDC] = { -1, -1 }; if (fdc[fnum].fdc_state != old_state[fnum]) log_message(fdc_log, "int_fdc%d %d: state=%d\n", fnum, rclk, fdc[fnum].fdc_state); old_state[fnum] = fdc[fnum].fdc_state; } #endif switch(fdc[fnum].fdc_state) { case FDC_RESET0: drive = drive_context[fnum]->drive; if (DOS_IS_80(fdc[fnum].drive_type)) { drive->current_half_track = 2 * 38; fdc[fnum].buffer[0] = 2; } else { drive->current_half_track = 2 * 18; fdc[fnum].buffer[0] = 0x3f; } if (DOS_IS_20(fdc[fnum].drive_type)) { fdc[fnum].fdc_state = FDC_RUN; } else { fdc[fnum].fdc_state++; } fdc[fnum].alarm_clk = rclk + 2000; alarm_set(fdc[fnum].fdc_alarm, fdc[fnum].alarm_clk); break; case FDC_RESET1: if (DOS_IS_80(fdc[fnum].drive_type)) { if (fdc[fnum].buffer[0] == 0) { fdc[fnum].buffer[0] = 1; fdc[fnum].fdc_state++; } } else { if (fdc[fnum].buffer[3] == 0xd0) { fdc[fnum].buffer[3] = 0; fdc[fnum].fdc_state++; } } fdc[fnum].alarm_clk = rclk + 2000; alarm_set(fdc[fnum].fdc_alarm, fdc[fnum].alarm_clk); break; case FDC_RESET2: if (DOS_IS_80(fdc[fnum].drive_type)) { if (fdc[fnum].buffer[0] == 0) { /* emulate routine written to buffer RAM */ fdc[fnum].buffer[1] = 0x0e; fdc[fnum].buffer[2] = 0x2d; /* number of sides on disk drive */ fdc[fnum].buffer[0xac] = (fdc[fnum].drive_type == DRIVE_TYPE_8050) ? 1 : 2; /* 0 = 4040 (2A), 1 = 8x80 (2C) drive type */ fdc[fnum].buffer[0xea] = 1; fdc[fnum].buffer[0xee] = 5; /* 3 for 4040, 5 for 8x50 */ fdc[fnum].buffer[0] = 3; /* 5 for 4040, 3 for 8x50 */ fdc[fnum].fdc_state = FDC_RUN; fdc[fnum].alarm_clk = rclk + 10000; } else { fdc[fnum].alarm_clk = rclk + 2000; } } else if (DOS_IS_40(fdc[fnum].drive_type) || DOS_IS_30(fdc[fnum].drive_type) ) { if (fdc[fnum].buffer[0] == 0) { fdc[fnum].buffer[0] = 0x0f; fdc[fnum].fdc_state = FDC_RUN; fdc[fnum].alarm_clk = rclk + 10000; } else { fdc[fnum].alarm_clk = rclk + 2000; } } alarm_set(fdc[fnum].fdc_alarm, fdc[fnum].alarm_clk); break; case FDC_RUN: /* check write protect switch */ if (fdc[fnum].wps_change) { fdc[fnum].buffer[0xA6] = 1; fdc[fnum].wps_change--; #ifdef FDC_DEBUG log_message(fdc_log, "Detect Unit %d Drive %d wps change", fnum + 8, fnum); #endif } if (fdc[fnum].num_drives == 2) { if (fdc[mk_drive1(fnum)].wps_change) { fdc[fnum].buffer[0xA6 + 1] = 1; fdc[mk_drive1(fnum)].wps_change--; #ifdef FDC_DEBUG log_message(fdc_log, "Detect Unit %d Drive 1 wps change", fnum + 8); #endif } } /* check buffers */ for (i=14; i >= 0; i--) { /* job there? */ if (fdc[fnum].buffer[i + 3] > 127) { /* pointer to buffer/block header: +0 = ID1 +1 = ID2 +2 = Track +3 = Sector */ j = 0x21 + (i << 3); #ifdef FDC_DEBUG log_message(fdc_log, "D/Buf %d/%x: Job code %02x t:%02d s:%02d", fnum, i, fdc[fnum].buffer[i+3], fdc[fnum].buffer[j+2],fdc[fnum].buffer[j+3]); #endif fdc[fnum].buffer[i + 3] = fdc_do_job(fnum, /* FDC# */ i, /* buffer# */ (unsigned int)fdc[fnum].buffer[i+3] & 1, /* drive */ (BYTE)(fdc[fnum].buffer[i+3] & 0xfe), /* job code */ &(fdc[fnum].buffer[j]) /* header */ ); } } /* check "move head", by half tracks I guess... */ for (i = 0; i < 2; i++) { if (fdc[fnum].buffer[i + 0xa1]) { #ifdef FDC_DEBUG log_message(fdc_log, "D %d: move head %d", fnum, fdc[fnum].buffer[i + 0xa1]); #endif fdc[fnum].buffer[i + 0xa1] = 0; } } fdc[fnum].alarm_clk = rclk + 30000; alarm_set(fdc[fnum].fdc_alarm, fdc[fnum].alarm_clk); /* job loop */ break; } }