// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************
EACA Colour Genie Floppy Disc Controller
TODO:
- How does it turn off the motor?
- What's the source of the timer and the exact timings?
***************************************************************************/
#include "emu.h"
#include "floppy.h"
#include "formats/cgenie_dsk.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"
//**************************************************************************
// CONSTANTS/MACROS
//**************************************************************************
#define VERBOSE 0
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(CGENIE_FDC, cgenie_fdc_device, "cgenie_fdc", "Colour Genie FDC")
void cgenie_fdc_device::mmio(address_map &map)
{
map(0xe0, 0xe3).mirror(0x10).rw(FUNC(cgenie_fdc_device::irq_r), FUNC(cgenie_fdc_device::select_w));
map(0xec, 0xec).mirror(0x10).r("wd2793", FUNC(wd2793_device::status_r)).w(FUNC(cgenie_fdc_device::command_w));
map(0xed, 0xed).mirror(0x10).rw("wd2793", FUNC(wd2793_device::track_r), FUNC(wd2793_device::track_w));
map(0xee, 0xee).mirror(0x10).rw("wd2793", FUNC(wd2793_device::sector_r), FUNC(wd2793_device::sector_w));
map(0xef, 0xef).mirror(0x10).rw("wd2793", FUNC(wd2793_device::data_r), FUNC(wd2793_device::data_w));
}
void cgenie_fdc_device::floppy_formats(format_registration &fr)
{
fr.add_mfm_containers();
fr.add(FLOPPY_CGENIE_FORMAT);
}
static void cgenie_floppies(device_slot_interface &device)
{
device.option_add("sssd", FLOPPY_525_SSSD);
device.option_add("sd", FLOPPY_525_SD);
device.option_add("ssdd", FLOPPY_525_SSDD);
device.option_add("dd", FLOPPY_525_DD);
device.option_add("ssqd", FLOPPY_525_SSQD);
device.option_add("qd", FLOPPY_525_QD);
}
//-------------------------------------------------
// rom_region - device-specific ROM region
//-------------------------------------------------
ROM_START( cgenie_fdc )
ROM_REGION(0x2000, "software", 0)
ROM_DEFAULT_BIOS("unk1")
// from cgemu
ROM_SYSTEM_BIOS(0, "unk1", "Unknown 1")
ROMX_LOAD("cgdos.rom", 0x0000, 0x2000, CRC(2a96cf74) SHA1(6dcac110f87897e1ee7521aefbb3d77a14815893), ROM_BIOS(0))
// can't read label
ROM_SYSTEM_BIOS(1, "unk2", "Unknown 2")
ROMX_LOAD("cgdos_a.c", 0x0000, 0x1000, CRC(6164e9d1) SHA1(c42ab4e73173893918abc871d01b63a3030cf6cc), ROM_BIOS(1))
ROMX_LOAD("cgdos_a.d", 0x1000, 0x1000, CRC(b09eb5d1) SHA1(80db78f665a488ad8cbffea696274b53fb90e492), ROM_BIOS(1))
// typed in from source code
ROM_SYSTEM_BIOS(2, "v2", "Version 2")
ROMX_LOAD("cgdos-v2.rom", 0x0000, 0x2000, BAD_DUMP CRC(9dace9c1) SHA1(513fef9fea81bee8f5edcf831095e90941e7cd69), ROM_BIOS(2))
ROM_END
const tiny_rom_entry *cgenie_fdc_device::device_rom_region() const
{
return ROM_NAME( cgenie_fdc );
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void cgenie_fdc_device::device_add_mconfig(machine_config &config)
{
TIMER(config, "timer").configure_periodic(FUNC(cgenie_fdc_device::timer_callback), attotime::from_msec(25));
WD2793(config, m_fdc, 4_MHz_XTAL / 4);
m_fdc->intrq_wr_callback().set(FUNC(cgenie_fdc_device::intrq_w));
FLOPPY_CONNECTOR(config, "wd2793:0", cgenie_floppies, "ssdd", cgenie_fdc_device::floppy_formats);
FLOPPY_CONNECTOR(config, "wd2793:1", cgenie_floppies, "ssdd", cgenie_fdc_device::floppy_formats);
FLOPPY_CONNECTOR(config, "wd2793:2", cgenie_floppies, nullptr, cgenie_fdc_device::floppy_formats);
FLOPPY_CONNECTOR(config, "wd2793:3", cgenie_floppies, nullptr, cgenie_fdc_device::floppy_formats);
// SOFTWARE_LIST(config, "floppy_list").set_original("cgenie_flop");
GENERIC_SOCKET(config, "socket", generic_plain_slot, "cgenie_flop_rom", "bin,rom").set_device_load(FUNC(cgenie_fdc_device::socket_load));
SOFTWARE_LIST(config, "rom_list").set_original("cgenie_flop_rom");
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// cgenie_fdc_device - constructor
//-------------------------------------------------
cgenie_fdc_device::cgenie_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, CGENIE_FDC, tag, owner, clock),
device_cg_exp_interface(mconfig, *this),
m_fdc(*this, "wd2793"),
m_floppy0(*this, "wd2793:0"),
m_floppy1(*this, "wd2793:1"),
m_floppy2(*this, "wd2793:2"),
m_floppy3(*this, "wd2793:3"),
m_socket(*this, "socket"),
m_floppy(nullptr),
m_irq_status(0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cgenie_fdc_device::device_start()
{
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void cgenie_fdc_device::device_reset()
{
// dos rom
m_slot->m_program->install_rom(0xc000, 0xdfff, memregion("software")->base());
// memory mapped i/o
m_slot->m_program->install_device(0xff00, 0xffff, *this, &cgenie_fdc_device::mmio);
// map extra socket
if (m_socket->exists())
m_slot->m_program->install_read_handler(0xe000, 0xefff, read8sm_delegate(*m_socket, FUNC(generic_slot_device::read_rom)));
}
//**************************************************************************
// IMPLEMENTATION
//**************************************************************************
uint8_t cgenie_fdc_device::irq_r()
{
uint8_t data = m_irq_status;
if (!machine().side_effects_disabled())
{
m_irq_status &= ~IRQ_TIMER;
m_slot->int_w(m_irq_status ? ASSERT_LINE : CLEAR_LINE);
}
return data;
}
TIMER_DEVICE_CALLBACK_MEMBER( cgenie_fdc_device::timer_callback )
{
m_irq_status |= IRQ_TIMER;
m_slot->int_w(ASSERT_LINE);
}
DEVICE_IMAGE_LOAD_MEMBER( cgenie_fdc_device::socket_load )
{
uint32_t const size = m_socket->common_get_size("rom");
if (size > 0x1000)
return std::make_pair(image_error::INVALIDLENGTH, "Unsupported ROM size (must be no more than 4K)");
m_socket->rom_alloc(0x1000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
m_socket->common_load_rom(m_socket->get_rom_base(), size, "rom");
return std::make_pair(std::error_condition(), std::string());
}
void cgenie_fdc_device::intrq_w(int state)
{
if (VERBOSE)
logerror("cgenie_fdc_device::intrq_w: %d\n", state);
if (state)
m_irq_status |= IRQ_WDC;
else
m_irq_status &= ~IRQ_WDC;
m_slot->int_w(m_irq_status ? ASSERT_LINE : CLEAR_LINE);
}
void cgenie_fdc_device::select_w(uint8_t data)
{
if (VERBOSE)
logerror("cgenie_fdc_device::motor_w: 0x%02x\n", data);
m_floppy = nullptr;
if (BIT(data, 0)) m_floppy = m_floppy0->get_device();
if (BIT(data, 1)) m_floppy = m_floppy1->get_device();
if (BIT(data, 2)) m_floppy = m_floppy2->get_device();
if (BIT(data, 3)) m_floppy = m_floppy3->get_device();
m_fdc->set_floppy(m_floppy);
if (m_floppy)
{
m_floppy->ss_w(BIT(data, 4));
m_floppy->mon_w(0);
}
}
void cgenie_fdc_device::command_w(uint8_t data)
{
// density select is encoded into this pseudo-command
if ((data & 0xfe) == 0xfe)
m_fdc->dden_w(!BIT(data, 0));
// forward to the controller
m_fdc->cmd_w(data);
}