diff options
Diffstat (limited to 'src/devices/bus/a2bus/agat_fdc.cpp')
-rw-r--r-- | src/devices/bus/a2bus/agat_fdc.cpp | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/src/devices/bus/a2bus/agat_fdc.cpp b/src/devices/bus/a2bus/agat_fdc.cpp new file mode 100644 index 00000000000..cb79d0cd699 --- /dev/null +++ b/src/devices/bus/a2bus/agat_fdc.cpp @@ -0,0 +1,488 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev +/********************************************************************* + + agat_fdc.c + + Implementation of the Agat 840K floppy controller card + + Technical manual: + http://agatcomp.ru/Reading/serkov/hainfo/023-01to.shtml + http://agatcomp.ru/Reading/serkov/hainfo/023-01to1.shtml + + Schematic: + http://agatcomp.ru/Reading/fl800k/FD840/TEAC_023-adj-HI.jpg + + On-disk format: + https://github.com/sintech/AGAT/blob/master/docs/agat-840k-format.txt + http://www.torlus.com/floppy/forum/viewtopic.php?f=19&t=1385 + +*********************************************************************/ + +#include "emu.h" +#include "agat_fdc.h" + +#include "formats/aim_dsk.h" +#include "formats/ds9_dsk.h" + + +#define LOG_LSS (1U << 1) // Show warnings +#define LOG_SHIFT (1U << 2) // Shows shift register contents + +//#define VERBOSE (LOG_GENERAL | LOG_SHIFT | LOG_LSS) +//#define VERBOSE (LOG_GENERAL) +#include "logmacro.h" + +#define LOGLSS(...) LOGMASKED(LOG_LSS, __VA_ARGS__) +#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__) + + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +DEFINE_DEVICE_TYPE(A2BUS_AGAT_FDC, a2bus_agat_fdc_device, "agat_fdc", "Agat 840K floppy card") + +#define AGAT_FDC_ROM_REGION "agat_fdc_rom" +#define AGAT_FDC_ROM_D6_REGION "agat_fdc_d6_rom" + +ROM_START( agat9 ) + ROM_REGION(0x100, AGAT_FDC_ROM_REGION, 0) + // zagorsk + ROM_LOAD( "teac.rom", 0x0000, 0x0100, CRC(94266928) SHA1(5d369bad6cdd6a70b0bb16480eba69640de87a2e) ) + + ROM_REGION(0x200, AGAT_FDC_ROM_D6_REGION, 0) + ROM_LOAD( "d6encdec.bin", 0x0000, 0x0200, CRC(66e7e896) SHA1(b5305e82c81240a6fdc932a559b5493c59f302c6) ) +ROM_END + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +FLOPPY_FORMATS_MEMBER( a2bus_agat_fdc_device::floppy_formats ) + FLOPPY_DS9_FORMAT, + FLOPPY_AIM_FORMAT +FLOPPY_FORMATS_END + +static void agat_floppies(device_slot_interface &device) +{ + device.option_add("525dsqd", FLOPPY_525_QD); +} + +void a2bus_agat_fdc_device::device_add_mconfig (machine_config &config) +{ + FLOPPY_CONNECTOR(config, floppy0, agat_floppies, "525dsqd", a2bus_agat_fdc_device::floppy_formats); + FLOPPY_CONNECTOR(config, floppy1, agat_floppies, "525dsqd", a2bus_agat_fdc_device::floppy_formats); + + I8255(config, m_d14); + // PA not connected + m_d14->in_pb_callback().set(FUNC(a2bus_agat_fdc_device::d14_i_b)); // status signals from drive + m_d14->out_pc_callback().set(FUNC(a2bus_agat_fdc_device::d14_o_c)); // control + + I8255(config, m_d15); + m_d15->in_pa_callback().set(FUNC(a2bus_agat_fdc_device::d15_i_a)); // read data +// m_d15->out_pb_callback().set(FUNC(a2bus_agat_fdc_device::d15_o_b)); // write data + m_d15->in_pc_callback().set(FUNC(a2bus_agat_fdc_device::d15_i_c)); + m_d15->out_pc_callback().set(FUNC(a2bus_agat_fdc_device::d15_o_c)); +} + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const tiny_rom_entry *a2bus_agat_fdc_device::device_rom_region() const +{ + return ROM_NAME( agat9 ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +a2bus_agat_fdc_device::a2bus_agat_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , device_a2bus_card_interface(mconfig, *this) + , m_d14(*this, "d14") + , m_d15(*this, "d15") + , floppy0(*this, "0") + , floppy1(*this, "1") + , m_rom(nullptr) + , m_rom_d6(nullptr) +{ +} + +a2bus_agat_fdc_device::a2bus_agat_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + a2bus_agat_fdc_device(mconfig, A2BUS_AGAT_FDC, tag, owner, clock) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void a2bus_agat_fdc_device::device_start() +{ + set_unscaled_clock((XTAL(14'300'000) / 14.0) * 4.0); + + m_rom = device().machine().root_device().memregion(this->subtag(AGAT_FDC_ROM_REGION).c_str())->base(); + m_rom_d6 = device().machine().root_device().memregion(this->subtag(AGAT_FDC_ROM_D6_REGION).c_str())->base(); + + floppy = nullptr; + if (floppy0) + { + floppy = floppy0->get_device(); + } + + m_mxcs = MXCSR_SYNC; + + m_timer_lss = timer_alloc(TIMER_ID_LSS); + m_timer_motor = timer_alloc(TIMER_ID_MOTOR); + + m_seektime = 6; // ms, per es5323.txt + m_waittime = 32; // us - 16 bits x 2 us +} + +void a2bus_agat_fdc_device::device_reset() +{ + active = 0; + cycles = time_to_cycles(machine().time()); + data_reg = 0x00; + address = 0xff; + + m_mxcs |= MXCSR_SYNC; + m_mxcs &= ~MXCSR_TR; + + // Just a timer to be sure that the lss is updated from time to + // time, so that there's no hiccup when it's talked to again. + m_timer_lss->adjust(attotime::from_msec(10), 0, attotime::from_msec(10)); +} + +void a2bus_agat_fdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_ID_LSS: + lss_sync(); + break; + + case TIMER_ID_MOTOR: + active = 0; + floppy->mon_w(1); + break; + } +} + +uint64_t a2bus_agat_fdc_device::time_to_cycles(const attotime &tm) +{ + // Clock is falling edges of the ~4Mhz clock + + uint64_t cycles = tm.as_ticks(clock()*2); + cycles = (cycles+1) >> 1; + return cycles; +} + +attotime a2bus_agat_fdc_device::cycles_to_time(uint64_t cycles) +{ + return attotime::from_ticks(cycles*2+1, clock()*2); +} + +void a2bus_agat_fdc_device::lss_start() +{ + cycles = time_to_cycles(machine().time()) + 1; + data_reg = 0x00; + address = 0xff; + bits = 8; +} + +void a2bus_agat_fdc_device::lss_sync() +{ + if(!active) + return; + + attotime next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles-1)) : attotime::never; + uint64_t cycles_limit = time_to_cycles(machine().time()); + uint64_t cycles_next_flux = next_flux != attotime::never ? time_to_cycles(next_flux) : uint64_t(-1); + uint64_t cycles_next_flux_down = cycles_next_flux != uint64_t(-1) ? cycles_next_flux+1 : uint64_t(-1); + + LOGLSS("LSS at %11.6f: %d (limit %d next %d) in %02x\n", + machine().time().as_double(), cycles, cycles_limit, cycles_next_flux, address); + + if(cycles >= cycles_next_flux && cycles < cycles_next_flux_down) + address &= ~0x40; + else + address |= 0x40; + + while (cycles < cycles_limit) { + uint64_t cycles_next_trans = cycles_limit; + if(cycles_next_trans > cycles_next_flux && cycles < cycles_next_flux) + { + cycles_next_trans = cycles_next_flux; + LOGLSS("lss: next_trans up (%d < %d; %d)\n", cycles, cycles_next_flux, cycles_limit); + } + if(cycles_next_trans > cycles_next_flux_down && cycles < cycles_next_flux_down) + { + cycles_next_trans = cycles_next_flux_down; + LOGLSS("lss: next_trans down (%d < %d; %d)\n", cycles, cycles_next_flux_down, cycles_limit); + } + + while (cycles < cycles_next_trans) { + uint8_t opcode = m_rom_d6[address]; + + if (cycles_next_flux != uint64_t(-1)) + { + LOGLSS("lss: %d (limit %d next %d) in %03x out %02x (addr %02x end %d bit %d sync %d)\n", + cycles, cycles_limit, cycles_next_flux, address, opcode, + opcode & 0x3f, BIT(opcode, 6), BIT(opcode, 5), BIT(opcode, 7)); + } + if (!BIT(opcode, 6)) // end bit + { + data_reg <<= 1; + data_reg |= !BIT(opcode, 5); + LOGSHIFT("lss shift: %d (to %02x, %2d bits) at %d%s\n", BIT(opcode, 5), data_reg, bits, cycles, + BIT(opcode, 7) ? " (sync)":""); + + if (BIT(opcode, 7) == BIT(opcode, 5)) // hack + { + bits++; + } + else + { + m_mxcs &= ~MXCSR_SYNC; + bits = 0; + } + + if (bits == 16) + { + address |= 0x80; + m_d15->pc4_w(0); + m_d15->pc4_w(1); + bits = 8; + LOGSHIFT("lss data: %02x\n", data_reg); + } + else + { + address &= ~0x80; + } + } + + address &= ~0x3f; + address |= (opcode & 0x3f); + + cycles++; + } + + if(cycles == cycles_next_flux) + address &= ~0x40; + else if(cycles == cycles_next_flux_down) { + address |= 0x40; + next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles)) : attotime::never; + if (next_flux != attotime::never) { + cycles_next_flux = time_to_cycles(next_flux); + LOGLSS("lss next: %d cycles\n", cycles_next_flux+1-cycles_next_flux_down); + cycles_next_flux_down = cycles_next_flux+1; + } else { + cycles_next_flux = uint64_t(-1); + cycles_next_flux_down = uint64_t(-1); + } + } + } +} + +/*------------------------------------------------- + read_c0nx - called for reads from this card's c0nx space +-------------------------------------------------*/ + +uint8_t a2bus_agat_fdc_device::read_c0nx(uint8_t offset) +{ + u8 data; + + lss_sync(); + + switch (offset) + { + case 0: case 1: case 2: case 3: + data = m_d14->read(offset); + break; + + case 4: case 5: case 6: case 7: + data = m_d15->read(offset - 4); + break; + + default: + data = 0xff; + break; + } + + return data; +} + + +/*------------------------------------------------- + write_c0nx - called for writes to this card's c0nx space +-------------------------------------------------*/ + +void a2bus_agat_fdc_device::write_c0nx(uint8_t offset, uint8_t data) +{ + lss_sync(); + + switch (offset) + { + case 0: case 1: case 2: case 3: + m_d14->write(offset, data); + break; + + case 4: case 5: case 6: case 7: + m_d15->write(offset - 4, data); + break; + + case 8: // D9.15 - write desync + break; + + case 9: // D9.14 - step + LOG("step at %11.6f\n", machine().time().as_double()); + if (floppy && active) + { + floppy->stp_w(1); + floppy->stp_w(0); + } + break; + + case 10: // D9.13 - reset desync flipflop + m_mxcs |= MXCSR_SYNC; + break; + + default: + break; + } +} + +/*------------------------------------------------- + read_cnxx - called for reads from this card's c0nx space +-------------------------------------------------*/ + +uint8_t a2bus_agat_fdc_device::read_cnxx(uint8_t offset) +{ + return m_rom[offset]; +} + + +/* + * all signals active low. write support not implemented; WPT is always active. + * + * b0-b1 type of drive 2: 00 - ES 5323.01 "1000 KB", 01 - "500 KB", 10 - "250 KB", 11 - not present + * b2-b3 type of drive 1: -""- + * b4 INDEX/SECTOR + * b5 WRITE PROTECT + * b6 TRACK 0 + * b7 READY + * + * C0x1 + */ +READ8_MEMBER(a2bus_agat_fdc_device::d14_i_b) +{ + u8 data = 0x3; + + // all signals active low + if (floppy) + { + data |= (floppy->idx_r() << 4) ^ 0x10; +// data |= floppy->wpt_r() << 5; + data |= floppy->trk00_r() << 6; + data |= floppy->ready_r() << 7; + } + else + { + data |= 0xf0; + } + + LOG("status A: %s %s (t %d) %s %s\n", BIT(data, 7) ? "ready" : "READY", BIT(data, 6) ? "tk00" : "TK00", + floppy ? floppy->get_cyl() : -1, BIT(data, 5) ? "wpt" : "WPT", BIT(data, 4) ? "index" : "INDEX"); + + return data; +} + +/* + * b0 AH strong write precomp + * b1 -- NC + * b2 -- step direction (1 - inward, 0 - outward) + * b3 -- drive select (0 - drive 1, 1 - drive 2) + * b4 -- head select (0 - bottom, 1 - top) + * b5 AH write precomp off + * b6 AH write enable + * b7 AH motor on + * + * C0x2 + */ +WRITE8_MEMBER(a2bus_agat_fdc_device::d14_o_c) +{ + m_unit = BIT(data, 3); + + if (floppy) + { + floppy->dir_w(!BIT(data, 2)); + floppy->ss_w(BIT(data, 4)); +// floppy->wtg_w(!BIT(data, 6)); +// floppy->mon_w(!BIT(data, 7)); // tied to 'drive select', 'motor on' and 'head load' + } + if (BIT(data, 7)) + { + m_d15->pc4_w(0); + m_d15->pc4_w(1); + floppy->mon_w(0); + if (!active) + { + active = 1; + lss_start(); + } + } +#if 0 + else + { + m_timer_motor->adjust(attotime::from_msec(1000)); + } +#endif + + LOG("D14 C <- %02X (unit %d side %d drtn %d wtg %d mon %d)\n", + data, m_unit, BIT(data, 4), !BIT(data, 2), !BIT(data, 6), !BIT(data, 7)); +} + +// data are latched in by write to PC4 +READ8_MEMBER(a2bus_agat_fdc_device::d15_i_a) +{ + return data_reg; +} + +// C0x6 +// +// b6 AL desync detected +// b7 AH read or write data ready +READ8_MEMBER(a2bus_agat_fdc_device::d15_i_c) +{ + LOG("status B: @ %4d %s %s\n", 0, + BIT(m_mxcs, 7) ? "ready" : "READY", BIT(m_mxcs, 6) ? "SYNC" : "sync"); + + return m_mxcs; +} + +// C0x7 +// +// b0 -- connected to b7, set if m_intr[PORT_B] +// b2 AH b7 = ready for write data +// b3 -- connected to b7, set if m_intr[PORT_A] +// b4 AH b7 = read data ready +WRITE8_MEMBER(a2bus_agat_fdc_device::d15_o_c) +{ + if (BIT(data, 0) || BIT(data, 3)) + { + m_mxcs |= MXCSR_TR; + } + else + { + m_mxcs &= ~MXCSR_TR; + } +} + |