// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************
Commodore 1551 Single Disk Drive emulation
**********************************************************************/
#include "emu.h"
#include "c1551.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
#define M6510T_TAG "u2"
#define M6523_0_TAG "u3"
#define M6523_1_TAG "ci_u2"
#define C64H156_TAG "u6"
#define PLA_TAG "u1"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(C1551, c1551_device, "c1551", "Commodore 1551")
//-------------------------------------------------
// ROM( c1551 )
//-------------------------------------------------
ROM_START( c1551 ) // schematic 251860
ROM_REGION( 0x4000, M6510T_TAG, 0 )
ROM_LOAD( "318001-01.u4", 0x0000, 0x4000, CRC(6d16d024) SHA1(fae3c788ad9a6cc2dbdfbcf6c0264b2ca921d55e) )
ROM_REGION( 0xf5, PLA_TAG, 0 ) // schematic 251925
ROM_LOAD( "251641-03.u1", 0x00, 0xf5, NO_DUMP )
ROM_END
//-------------------------------------------------
// rom_region - device-specific ROM region
//-------------------------------------------------
const tiny_rom_entry *c1551_device::device_rom_region() const
{
return ROM_NAME( c1551 );
}
//-------------------------------------------------
// M6510_INTERFACE( cpu_intf )
//-------------------------------------------------
READ8_MEMBER( c1551_device::port_r )
{
/*
bit description
P0
P1
P2
P3
P4 WPS
P5
P6
P7 BYTE LTCHED
*/
uint8_t data = 0;
// write protect sense
data |= !m_floppy->wpt_r() << 4;
// byte latched
data |= m_ga->atn_r() << 7;
return data;
}
WRITE8_MEMBER( c1551_device::port_w )
{
/*
bit description
P0 STP0A
P1 STP0B
P2 MTR0
P3 ACT0
P4
P5 DS0
P6 DS1
P7
*/
// stepper motor
m_ga->stp_w(data & 0x03);
// spindle motor
m_ga->mtr_w(BIT(data, 2));
// activity LED
m_led[LED_ACT] = BIT(data, 3);
// density select
m_ga->ds_w((data >> 5) & 0x03);
}
//-------------------------------------------------
// tpi6525_interface tpi0_intf
//-------------------------------------------------
READ8_MEMBER( c1551_device::tcbm_data_r )
{
/*
bit description
PA0 TCBM PA0
PA1 TCBM PA1
PA2 TCBM PA2
PA3 TCBM PA3
PA4 TCBM PA4
PA5 TCBM PA5
PA6 TCBM PA6
PA7 TCBM PA7
*/
return m_tcbm_data;
}
WRITE8_MEMBER( c1551_device::tcbm_data_w )
{
/*
bit description
PA0 TCBM PA0
PA1 TCBM PA1
PA2 TCBM PA2
PA3 TCBM PA3
PA4 TCBM PA4
PA5 TCBM PA5
PA6 TCBM PA6
PA7 TCBM PA7
*/
m_tcbm_data = data;
}
READ8_MEMBER( c1551_device::tpi0_r )
{
uint8_t data = m_tpi0->read(space, offset);
m_ga->ted_w(0);
m_ga->ted_w(1);
return data;
}
WRITE8_MEMBER( c1551_device::tpi0_w )
{
m_tpi0->write(space, offset, data);
m_ga->ted_w(0);
m_ga->ted_w(1);
}
READ8_MEMBER( c1551_device::tpi0_pc_r )
{
/*
bit description
PC0
PC1
PC2
PC3
PC4
PC5 JP1
PC6 _SYNC
PC7 TCBM DAV
*/
uint8_t data = 0;
// JP1
data |= m_jp1->read() << 5;
// SYNC detect line
data |= m_ga->sync_r() << 6;
// TCBM data valid
data |= m_dav << 7;
return data;
}
WRITE8_MEMBER( c1551_device::tpi0_pc_w )
{
/*
bit description
PC0 TCBM STATUS0
PC1 TCBM STATUS1
PC2 TCBM DEV
PC3 TCBM ACK
PC4 MODE
PC5
PC6
PC7
*/
// TCBM status
m_status = data & 0x03;
// TCBM device number
m_dev = BIT(data, 2);
// TCBM acknowledge
m_ack = BIT(data, 3);
// read/write mode
m_ga->oe_w(BIT(data, 4));
}
//-------------------------------------------------
// tpi6525_interface tpi1_intf
//-------------------------------------------------
READ8_MEMBER( c1551_device::tpi1_pb_r )
{
/*
bit description
PB0 STATUS0
PB1 STATUS1
PB2
PB3
PB4
PB5
PB6
PB7
*/
return m_status & 0x03;
}
READ8_MEMBER( c1551_device::tpi1_pc_r )
{
/*
bit description
PC0
PC1
PC2
PC3
PC4
PC5
PC6
PC7 TCBM ACK
*/
uint8_t data = 0;
// TCBM acknowledge
data |= m_ack << 7;
return data;
}
WRITE8_MEMBER( c1551_device::tpi1_pc_w )
{
/*
bit description
PC0
PC1
PC2
PC3
PC4
PC5
PC6 TCBM DAV
PC7
*/
// TCBM data valid
m_dav = BIT(data, 6);
}
//-------------------------------------------------
// ADDRESS_MAP( c1551_mem )
//-------------------------------------------------
void c1551_device::c1551_mem(address_map &map)
{
map(0x0000, 0x07ff).mirror(0x0800).ram();
map(0x4000, 0x4007).mirror(0x3ff8).rw(this, FUNC(c1551_device::tpi0_r), FUNC(c1551_device::tpi0_w));
map(0xc000, 0xffff).rom().region(M6510T_TAG, 0);
}
//-------------------------------------------------
// SLOT_INTERFACE( c1551_floppies )
//-------------------------------------------------
static void c1551_floppies(device_slot_interface &device)
{
device.option_add("525ssqd", FLOPPY_525_SSQD);
}
//-------------------------------------------------
// FLOPPY_FORMATS( floppy_formats )
//-------------------------------------------------
FLOPPY_FORMATS_MEMBER( c1551_device::floppy_formats )
FLOPPY_D64_FORMAT,
FLOPPY_G64_FORMAT
FLOPPY_FORMATS_END
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
MACHINE_CONFIG_START(c1551_device::device_add_mconfig)
MCFG_DEVICE_ADD(M6510T_TAG, M6510T, XTAL(16'000'000)/8)
MCFG_DEVICE_PROGRAM_MAP(c1551_mem)
MCFG_M6510T_PORT_CALLBACKS(READ8(*this, c1551_device, port_r), WRITE8(*this, c1551_device, port_w))
MCFG_QUANTUM_PERFECT_CPU(M6510T_TAG)
MCFG_PLS100_ADD(PLA_TAG)
MCFG_DEVICE_ADD(M6523_0_TAG, TPI6525, 0)
MCFG_TPI6525_IN_PA_CB(READ8(*this, c1551_device, tcbm_data_r))
MCFG_TPI6525_OUT_PA_CB(WRITE8(*this, c1551_device, tcbm_data_w))
MCFG_TPI6525_IN_PB_CB(READ8(C64H156_TAG, c64h156_device, yb_r))
MCFG_TPI6525_OUT_PB_CB(WRITE8(C64H156_TAG, c64h156_device, yb_w))
MCFG_TPI6525_IN_PC_CB(READ8(*this, c1551_device, tpi0_pc_r))
MCFG_TPI6525_OUT_PC_CB(WRITE8(*this, c1551_device, tpi0_pc_w))
MCFG_DEVICE_ADD(M6523_1_TAG, TPI6525, 0)
MCFG_TPI6525_IN_PA_CB(READ8(*this, c1551_device, tcbm_data_r))
MCFG_TPI6525_OUT_PA_CB(WRITE8(*this, c1551_device, tcbm_data_w))
MCFG_TPI6525_IN_PB_CB(READ8(*this, c1551_device, tpi1_pb_r))
MCFG_TPI6525_IN_PC_CB(READ8(*this, c1551_device, tpi1_pc_r))
MCFG_TPI6525_OUT_PC_CB(WRITE8(*this, c1551_device, tpi1_pc_w))
MCFG_DEVICE_ADD(C64H156_TAG, C64H156, XTAL(16'000'000))
MCFG_64H156_BYTE_CALLBACK(WRITELINE(C64H156_TAG, c64h156_device, atni_w))
MCFG_FLOPPY_DRIVE_ADD_FIXED(C64H156_TAG":0", c1551_floppies, "525ssqd", c1551_device::floppy_formats)
MCFG_PLUS4_PASSTHRU_EXPANSION_SLOT_ADD()
MACHINE_CONFIG_END
//-------------------------------------------------
// INPUT_PORTS( c1551 )
//-------------------------------------------------
static INPUT_PORTS_START( c1551 )
PORT_START("JP1")
PORT_DIPNAME( 0x01, 0x00, "Device Number" )
PORT_DIPSETTING( 0x00, "8" )
PORT_DIPSETTING( 0x01, "9" )
INPUT_PORTS_END
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
ioport_constructor c1551_device::device_input_ports() const
{
return INPUT_PORTS_NAME( c1551 );
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// c1551_device - constructor
//-------------------------------------------------
c1551_device::c1551_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, C1551, tag, owner, clock)
, device_plus4_expansion_card_interface(mconfig, *this)
, m_maincpu(*this, M6510T_TAG)
, m_tpi0(*this, M6523_0_TAG)
, m_tpi1(*this, M6523_1_TAG)
, m_ga(*this, C64H156_TAG)
, m_pla(*this, PLA_TAG)
, m_floppy(*this, C64H156_TAG":0:525ssqd")
, m_exp(*this, PLUS4_EXPANSION_SLOT_TAG)
, m_jp1(*this, "JP1")
, m_led(*this, "led%u", 0U)
, m_tcbm_data(0xff)
, m_status(1)
, m_dav(1)
, m_ack(1)
, m_dev(0)
, m_irq_timer(nullptr)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void c1551_device::device_start()
{
m_led.resolve();
// allocate timers
m_irq_timer = timer_alloc();
m_irq_timer->adjust(attotime::zero, CLEAR_LINE);
// install image callbacks
m_ga->set_floppy(m_floppy);
// register for state saving
save_item(NAME(m_tcbm_data));
save_item(NAME(m_status));
save_item(NAME(m_dav));
save_item(NAME(m_ack));
save_item(NAME(m_dev));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void c1551_device::device_reset()
{
m_maincpu->reset();
m_tpi0->reset();
m_exp->reset();
// initialize gate array
m_ga->test_w(1);
m_ga->soe_w(1);
m_ga->accl_w(1);
m_ga->atna_w(1);
}
//-------------------------------------------------
// device_timer - handler timer events
//-------------------------------------------------
void c1551_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
m_maincpu->set_input_line(M6502_IRQ_LINE, param);
if (param == ASSERT_LINE)
{
// Ts = 0.7*R2*C1 = 0.7*100R*0.1uF = 7us
m_irq_timer->adjust(attotime::from_usec(7), CLEAR_LINE);
}
else
{
// Tm = 0.7*(R1+R2)*C1 = 0.7*(120K+100R)*0.1uF = 0.008407s
m_irq_timer->adjust(attotime::from_usec(8407), ASSERT_LINE);
}
}
//-------------------------------------------------
// tpi1_selected -
//-------------------------------------------------
bool c1551_device::tpi1_selected(offs_t offset)
{
#ifdef PLA_DUMPED
int mux = 0, ras = 0, phi0 = 0, f7 = 0;
uint16_t input = A5 << 15 | A6 << 14 | A7 << 13 | A8 << 12 | A9 << 11 | mux << 10 | A10 << 9 | m_dev << 8 | ras << 7 | phi0 << 6 | A15 << 5 | A14 << 4 | A13 << 3 | A12 << 2 | A11 << 1 | f7;
uint8_t data = m_pla->read(input);
return BIT(data, 0) ? true : false;
#endif
offs_t start_address = m_dev ? 0xfee0 : 0xfec0;
if (offset >= start_address && offset < (start_address + 0x20))
{
return true;
}
return false;
}
//-------------------------------------------------
// plus4_cd_r - cartridge data read
//-------------------------------------------------
uint8_t c1551_device::plus4_cd_r(address_space &space, offs_t offset, uint8_t data, int ba, int cs0, int c1l, int c2l, int cs1, int c1h, int c2h)
{
data = m_exp->cd_r(space, offset, data, ba, cs0, c1l, c2l, cs1, c1h, c2h);
if (tpi1_selected(offset))
{
data = m_tpi1->read(space, offset & 0x07);
}
return data;
}
//-------------------------------------------------
// plus4_cd_w - cartridge data write
//-------------------------------------------------
void c1551_device::plus4_cd_w(address_space &space, offs_t offset, uint8_t data, int ba, int cs0, int c1l, int c2l, int cs1, int c1h, int c2h)
{
if (tpi1_selected(offset))
{
m_tpi1->write(space, offset & 0x07, data);
}
m_exp->cd_w(space, offset, data, ba, cs0, c1l, c2l, cs1, c1h, c2h);
}