/********************************************************************* coco_fdc.c CoCo/Dragon FDC The CoCo and Dragon both use the Western Digital floppy disk controllers. The CoCo uses either the WD1793 or the WD1773, the Dragon uses the WD2797, which mostly uses the same command set with some subtle differences, most notably the 2797 handles disk side select internally. The Dragon Alpha also uses the WD2797, however as this is a built in interface and not an external cartrige, it is dealt with in the main coco.c file. The wd's variables are mapped to $FF48-$FF4B on the CoCo and on $FF40-$FF43 on the Dragon. In addition, there is another register called DSKREG that controls the interface with the wd1793. DSKREG is detailed below: But they appear to be References: CoCo: Disk Basic Unravelled Dragon: Inferences from the PC-Dragon source code DragonDos Controller, Disk and File Formats by Graham E Kinns --------------------------------------------------------------------------- DSKREG - the control register CoCo ($FF40) Dragon ($FF48) Bit Bit 7 halt enable flag 7 not used 6 drive select #3 6 not used 5 density (0=single, 1=double) 5 NMI enable flag and NMI enable flag 4 write precompensation 4 write precompensation 3 drive motor activation 3 single density enable 2 drive select #2 2 drive motor activation 1 drive select #1 1 drive select high bit 0 drive select #0 0 drive select low bit Reading from $FF48-$FF4F clears bit 7 of DSKREG ($FF40) --------------------------------------------------------------------------- 2007-02-22, P.Harvey-Smith Began implementing the Dragon Delta Dos controler, this was actually the first Dragon disk controler to market, beating Dragon Data's by a couple of months, it is based around the WD2791 FDC, which is compatible with the WD1793/WD2797 used by the standard CoCo and Dragon disk controlers except that it used an inverted data bus, which is the reason the read/write handlers invert the data. This controler like, the DragonDos WD2797 is mapped at $FF40-$FF43, in the normal register order. The Delta cart also has a register (74LS174 hex flipflop) at $FF44 encoded as follows :- Bit 7 not used 6 not used 5 not used 4 Single (0) / Double (1) density select 3 5.25"(0) / 8"(1) Clock select 2 Side select 1 Drive select ms bit 0 Drive select ls bit *********************************************************************/ #include "driver.h" #include "inptport.h" #include "cococart.h" #include "machine/wd17xx.h" #include "machine/ds1315.h" #include "machine/m6242b.h" #include "devices/coco_vhd.h" #define LOG_FDC 0 typedef struct _fdc_info fdc_info; struct _fdc_info { UINT8 dskreg; unsigned drq : 1; unsigned intrq : 1; void (*update_lines)(coco_cartridge *cartridge); }; /*************************************************************************** GENERAL FDC CODE ***************************************************************************/ /*------------------------------------------------- fdc_get_info -------------------------------------------------*/ static fdc_info *fdc_get_info(coco_cartridge *cartridge) { return cococart_get_extra_data(cartridge); } /*------------------------------------------------- fdc_callback - callback from the FDC -------------------------------------------------*/ static void fdc_callback(wd17xx_state_t state, void *param) { coco_cartridge *cartridge = (coco_cartridge *) param; fdc_info *info = fdc_get_info(cartridge); switch(state) { case WD17XX_IRQ_CLR: info->intrq = 0; break; case WD17XX_IRQ_SET: info->intrq = 1; break; case WD17XX_DRQ_CLR: info->drq = 0; break; case WD17XX_DRQ_SET: info->drq = 1; break; } (*info->update_lines)(cartridge); } /*------------------------------------------------- fdc_init - general function to initialize FDC -------------------------------------------------*/ static void fdc_init(coco_cartridge *cartridge, wd17xx_type_t type, void (*update_lines)(coco_cartridge *cartridge), int initial_drq) { fdc_info *info = fdc_get_info(cartridge); /* initialize variables */ memset(info, 0, sizeof(*info)); info->update_lines = update_lines; info->drq = initial_drq ? 1 : 0; /* initialize FDC */ wd17xx_init(type, fdc_callback, (void *) cartridge); } /*------------------------------------------------- cococart_fdc - general FDC function -------------------------------------------------*/ static void cococart_fdc(UINT32 state, cococartinfo *info) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case COCOCARTINFO_INT_DATASIZE: info->i = sizeof(fdc_info); break; } } /*************************************************************************** COCO-SPECIFIC FDC CODE ***************************************************************************/ /*------------------------------------------------- fdc_coco_update_lines - CoCo specific disk controller lines -------------------------------------------------*/ static void fdc_coco_update_lines(coco_cartridge *cartridge) { fdc_info *info = fdc_get_info(cartridge); /* clear HALT enable under certain circumstances */ if ((info->intrq != 0) && (info->dskreg & 0x20)) info->dskreg &= ~0x80; /* clear halt enable */ /* set the NMI line */ cococart_set_line( cartridge, COCOCART_LINE_NMI, ((info->intrq != 0) && (info->dskreg & 0x20)) ? COCOCART_LINE_VALUE_ASSERT : COCOCART_LINE_VALUE_CLEAR); /* set the HALT line */ cococart_set_line( cartridge, COCOCART_LINE_HALT, ((info->drq == 0) && (info->dskreg & 0x80)) ? COCOCART_LINE_VALUE_ASSERT : COCOCART_LINE_VALUE_CLEAR); } /*------------------------------------------------- fdc_coco_init - function to initialize CoCo FDC -------------------------------------------------*/ static void fdc_coco_init(coco_cartridge *cartridge) { fdc_init(cartridge, WD_TYPE_1773, fdc_coco_update_lines, 1); } /*------------------------------------------------- fdc_coco_dskreg_w - function to write to CoCo dskreg -------------------------------------------------*/ static void fdc_coco_dskreg_w(coco_cartridge *cartridge, UINT8 data) { fdc_info *info = fdc_get_info(cartridge); UINT8 drive = 0; UINT8 head = 0; int motor_mask = 0; if (LOG_FDC) { logerror("fdc_coco_dskreg_w(): %c%c%c%c%c%c%c%c ($%02x)\n", data & 0x80 ? 'H' : 'h', data & 0x40 ? '3' : '.', data & 0x20 ? 'D' : 'S', data & 0x10 ? 'P' : 'p', data & 0x08 ? 'M' : 'm', data & 0x04 ? '2' : '.', data & 0x02 ? '1' : '.', data & 0x01 ? '0' : '.', data); } /* An email from John Kowalski informed me that if the DS3 is * high, and one of the other drive bits is selected (DS0-DS2), then the * second side of DS0, DS1, or DS2 is selected. If multiple bits are * selected in other situations, then both drives are selected, and any * read signals get yucky. */ motor_mask = 0x08; if (data & 0x04) drive = 2; else if (data & 0x02) drive = 1; else if (data & 0x01) drive = 0; else if (data & 0x40) drive = 3; else motor_mask = 0; head = ((data & 0x40) && (drive != 3)) ? 1 : 0; info->dskreg = data; (*info->update_lines)(cartridge); wd17xx_set_drive(drive); wd17xx_set_side(head); wd17xx_set_density((info->dskreg & 0x20) ? DEN_MFM_LO : DEN_FM_LO); } /*------------------------------------------------- fdc_coco_r - function to read from CoCo FDC -------------------------------------------------*/ static UINT8 fdc_coco_r(coco_cartridge *cartridge, UINT16 addr) { UINT8 result = 0; switch(addr & 0xEF) { case 8: result = wd17xx_status_r(0); break; case 9: result = wd17xx_track_r(0); break; case 10: result = wd17xx_sector_r(0); break; case 11: result = wd17xx_data_r(0); break; } return result; } /*------------------------------------------------- fdc_coco_w - function to write to CoCo FDC -------------------------------------------------*/ static void fdc_coco_w(coco_cartridge *cartridge, UINT16 addr, UINT8 data) { switch(addr & 0xEF) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: fdc_coco_dskreg_w(cartridge, data); break; case 8: wd17xx_command_w(0, data); break; case 9: wd17xx_track_w(0, data); break; case 10: wd17xx_sector_w(0, data); break; case 11: wd17xx_data_w(0, data); break; }; } /*------------------------------------------------- cococart_fdc_coco - get info function for the CoCo FDC -------------------------------------------------*/ void cococart_fdc_coco(UINT32 state, cococartinfo *info) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case COCOCARTINFO_INT_DATASIZE: info->i = sizeof(fdc_info); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case COCOCARTINFO_PTR_INIT: info->init = fdc_coco_init; break; case COCOCARTINFO_PTR_FF40_R: info->rh = fdc_coco_r; break; case COCOCARTINFO_PTR_FF40_W: info->wh = fdc_coco_w; break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case COCOCARTINFO_STR_NAME: strcpy(info->s, "coco_fdc"); break; default: cococart_fdc(state, info); break; } } /*************************************************************************** COCO3 EXTRA FEATURES/HACKS ***************************************************************************/ typedef enum { RTC_DISTO = 0x00, RTC_CLOUD9 = 0x01 } rtc_type_t; /*------------------------------------------------- real_time_clock -------------------------------------------------*/ static rtc_type_t real_time_clock(void) { return (rtc_type_t) (int) readinputportbytag_safe("real_time_clock", 0x00); } /*------------------------------------------------- fdc_coco3plus_r -------------------------------------------------*/ static UINT8 fdc_coco3plus_r(coco_cartridge *cartridge, UINT16 addr) { UINT8 result = fdc_coco_r(cartridge, addr); switch(addr) { case 0x10: /* FF50 */ if (real_time_clock() == RTC_DISTO) result = m6242_data_r(0); break; case 0x38: /* FF78 */ if (real_time_clock() == RTC_CLOUD9) ds1315_r_0(addr); break; case 0x39: /* FF79 */ if (real_time_clock() == RTC_CLOUD9) ds1315_r_1(addr); break; case 0x3C: /* FF7C */ if (real_time_clock() == RTC_CLOUD9) result = ds1315_r_data(addr); break; case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: if (device_count(IO_VHD) > 0) result = coco_vhd_io_r(addr); break; } return result; } /*------------------------------------------------- fdc_coco3plus_w -------------------------------------------------*/ static void fdc_coco3plus_w(coco_cartridge *cartridge, UINT16 addr, UINT8 data) { fdc_coco_w(cartridge, addr, data); switch(addr) { case 0x10: /* FF50 */ if (real_time_clock() == RTC_DISTO) m6242_data_w(0, data); break; case 0x11: /* FF51 */ if (real_time_clock() == RTC_DISTO) m6242_address_w(0, data); break; case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: if (device_count(IO_VHD) > 0) coco_vhd_io_w(addr, data); break; } } /*------------------------------------------------- cococart_fdc_coco3_plus - get info function for CoCo FDC, with extra CoCo 3 features -------------------------------------------------*/ void cococart_fdc_coco3_plus(UINT32 state, cococartinfo *info) { switch (state) { /* --- the following bits of info are returned as pointers to data or functions --- */ case COCOCARTINFO_PTR_FF40_R: info->rh = fdc_coco3plus_r; break; case COCOCARTINFO_PTR_FF40_W: info->wh = fdc_coco3plus_w; break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case COCOCARTINFO_STR_NAME: strcpy(info->s, "coco3_plus_fdc"); break; default: cococart_fdc_coco(state, info); break; } } /*************************************************************************** DRAGON-SPECIFIC FDC CODE ***************************************************************************/ /*------------------------------------------------- fdc_dragon_update_lines - Dragon specific disk controller lines -------------------------------------------------*/ static void fdc_dragon_update_lines(coco_cartridge *cartridge) { fdc_info *info = fdc_get_info(cartridge); /* set the NMI line */ cococart_set_line( cartridge, COCOCART_LINE_NMI, ((info->intrq != 0) && (info->dskreg & 0x20)) ? COCOCART_LINE_VALUE_ASSERT : COCOCART_LINE_VALUE_CLEAR); /* set the CART line */ cococart_set_line( cartridge, COCOCART_LINE_CART, (info->drq != 0) ? COCOCART_LINE_VALUE_ASSERT : COCOCART_LINE_VALUE_CLEAR); } /*------------------------------------------------- fdc_dragon_init - function to initialize Dragon FDC -------------------------------------------------*/ static void fdc_dragon_init(coco_cartridge *cartridge) { fdc_init(cartridge, WD_TYPE_179X, fdc_dragon_update_lines, 0); } /*------------------------------------------------- fdc_dragon_dskreg_w - function to write to Dragon dskreg -------------------------------------------------*/ static void fdc_dragon_dskreg_w(coco_cartridge *cartridge, UINT8 data) { fdc_info *info = fdc_get_info(cartridge); if (LOG_FDC) { logerror("fdc_dragon_dskreg_w(): %c%c%c%c%c%c%c%c ($%02x)\n", data & 0x80 ? 'X' : 'x', data & 0x40 ? 'X' : 'x', data & 0x20 ? 'N' : 'n', data & 0x10 ? 'P' : 'p', data & 0x08 ? 'S' : 'D', data & 0x04 ? 'M' : 'm', data & 0x02 ? '1' : '0', data & 0x01 ? '1' : '0', data); } if (data & 0x04) wd17xx_set_drive(data & 0x03); wd17xx_set_density((data & 0x08) ? DEN_FM_LO: DEN_MFM_LO); info->dskreg = data; } /*------------------------------------------------- fdc_dragon_r - function to read from Dragon FDC -------------------------------------------------*/ static UINT8 fdc_dragon_r(coco_cartridge *cartridge, UINT16 addr) { UINT8 result = 0; switch(addr & 0xEF) { case 0: result = wd17xx_status_r(0); break; case 1: result = wd17xx_track_r(0); break; case 2: result = wd17xx_sector_r(0); break; case 3: result = wd17xx_data_r(0); break; } return result; } /*------------------------------------------------- fdc_dragon_w - function to write to Dragon FDC -------------------------------------------------*/ static void fdc_dragon_w(coco_cartridge *cartridge, UINT16 addr, UINT8 data) { switch(addr & 0xEF) { case 0: wd17xx_command_w(0, data); /* disk head is encoded in the command byte */ /* Only for type 3 & 4 commands */ if (data & 0x80) wd17xx_set_side((data & 0x02) ? 1 : 0); break; case 1: wd17xx_track_w(0, data); break; case 2: wd17xx_sector_w(0, data); break; case 3: wd17xx_data_w(0, data); break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: fdc_dragon_dskreg_w(cartridge, data); break; }; } /*------------------------------------------------- cococart_fdc_dragon - get info function for the Dragon FDC -------------------------------------------------*/ void cococart_fdc_dragon(UINT32 state, cococartinfo *info) { switch(state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case COCOCARTINFO_INT_DATASIZE: info->i = sizeof(fdc_info); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case COCOCARTINFO_PTR_INIT: info->init = fdc_dragon_init; break; case COCOCARTINFO_PTR_FF40_R: info->rh = fdc_dragon_r; break; case COCOCARTINFO_PTR_FF40_W: info->wh = fdc_dragon_w; break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case COCOCARTINFO_STR_NAME: strcpy(info->s, "dragon_fdc"); break; default: cococart_fdc(state, info); break; } }