amiga: CDTV CD-ROM support

- CDTV can boot from CD and play audio discs
- Alternatively use the A570 expansion for the A500
This commit is contained in:
Dirk Best 2025-03-05 17:39:29 +01:00
parent 32a2633ba7
commit 48b7920613
7 changed files with 970 additions and 163 deletions

View File

@ -12,14 +12,17 @@
- ROM label for the A690: "391298-01 V1.0 Copyright ©1991 CBM C480"
- The ROM P/N 391298-01 seems to have been used for multiple versions
- There are expansion slots for a 2 MB RAM expansion and a SCSI module
- Uses the CR-512-B drive from MKE (Matsushita Kotobuki Electronics)
- An FPGA is used in place of many discrete logic chips of the CDTV
TODO:
- DMAC/CD-ROM drive hookup (needs DMAC rev 2)
- Volume control (LC7883M)
***************************************************************************/
#include "emu.h"
#include "a570.h"
#include "speaker.h"
#define VERBOSE (LOG_GENERAL)
@ -37,7 +40,10 @@ namespace bus::amiga::cpuslot {
a570_device::a570_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, AMIGA_CPUSLOT_A570, tag, owner, clock),
device_amiga_cpuslot_interface(mconfig, *this),
m_irq(*this, "irq"),
m_dmac(*this, "dmac"),
m_tpi(*this, "tpi"),
m_drive(*this, "drive"),
m_config(*this, "config")
{
}
@ -94,12 +100,39 @@ const tiny_rom_entry *a570_device::device_rom_region() const
void a570_device::device_add_mconfig(machine_config &config)
{
INPUT_MERGER_ANY_HIGH(config, m_irq);
m_irq->output_handler().set([this] (int state) { m_host->int2_w(state); });
AMIGA_DMAC_REV2(config, m_dmac, 28.37516_MHz_XTAL / 4); // 7M
m_dmac->cfgout_cb().set([this] (int state) { m_host->cfgout_w(state); });
m_dmac->int_cb().set([this] (int state) { m_host->int2_w(state); });
m_dmac->int_cb().set(m_irq, FUNC(input_merger_any_high_device::in_w<0>));
m_dmac->csx0_read_cb().set(m_drive, FUNC(cr511b_device::read));
m_dmac->csx0_write_cb().set(m_drive, FUNC(cr511b_device::write));
m_dmac->csx0_a4_read_cb().set(m_tpi, FUNC(tpi6525_device::read));
m_dmac->csx0_a4_write_cb().set(m_tpi, FUNC(tpi6525_device::write));
m_dmac->xdack_read_cb().set(m_drive, FUNC(cr511b_device::read));
AT28C16(config, "nvram0", 0);
AT28C16(config, "nvram1", 0);
TPI6525(config, m_tpi, 0);
m_tpi->out_irq_cb().set(m_irq, FUNC(input_merger_any_high_device::in_w<1>));
m_tpi->out_pb_cb().set(FUNC(a570_device::tpi_portb_w));
CR511B(config, m_drive, 0);
m_drive->add_route(0, "lspeaker", 1.0);
m_drive->add_route(1, "rspeaker", 1.0);
m_drive->scor_cb().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert();
m_drive->stch_cb().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert();
m_drive->sten_cb().set(m_tpi, FUNC(tpi6525_device::i3_w));
m_drive->sten_cb().append(FUNC(a570_device::sten_w));
m_drive->drq_cb().set(m_tpi, FUNC(tpi6525_device::i4_w));
m_drive->drq_cb().append(FUNC(a570_device::drq_w));
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();
// TODO: Add stereo input for Amiga sound
}
@ -118,6 +151,33 @@ void a570_device::device_start()
// register for save states
save_pointer(NAME(m_ram), 0x200000/2);
save_item(NAME(m_sten));
}
void a570_device::sten_w(int state)
{
m_sten = bool(state);
}
void a570_device::drq_w(int state)
{
if (m_sten)
m_dmac->xdreq_w(state);
}
void a570_device::tpi_portb_w(uint8_t data)
{
// 7------- daclch (lc7883m)
// -6------ dacst (lc7883m)
// --5----- dacatt (lc7883m)
// ---4---- weprom
// ----3--- dten (drive)
// -----2-- xaen (drive)
// ------1- enable (drive)
// -------0 cmd (drive)
m_drive->enable_w(BIT(data, 1));
m_drive->cmd_w(BIT(data, 0));
}
// the dmac handles this

View File

@ -14,8 +14,12 @@
#pragma once
#include "cpuslot.h"
#include "machine/6525tpi.h"
#include "machine/at28c16.h"
#include "machine/cr511b.h"
#include "machine/dmac.h"
#include "machine/input_merger.h"
namespace bus::amiga::cpuslot {
@ -38,9 +42,19 @@ protected:
private:
void map(address_map &map) ATTR_COLD;
void sten_w(int state);
void drq_w(int state);
void tpi_portb_w(uint8_t data);
required_device<input_merger_any_high_device> m_irq;
required_device<amiga_dmac_rev2_device> m_dmac;
required_device<tpi6525_device> m_tpi;
required_device<cr511b_device> m_drive;
required_ioport m_config;
bool m_sten;
std::unique_ptr<uint16_t[]> m_ram;
};

View File

@ -1,97 +1,764 @@
// license:BSD-3-Clause
// copyright-holders:Dirk Best
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************
CR-511-B CD-ROM drive
CD-ROM drive with a custom MKE/Panasonic interface as used in the
Commodore CDTV and early SoundBlaster cards.
Commodore CDTV. Similar to the interface on early SoundBlaster cards.
Hardware:
- MN188161REB1
- M50423FP
- 4464S-08LL
- LC8951
TODO:
- Subcode P-W data
- Timing for status data or status change
***************************************************************************/
#include "emu.h"
#include "cr511b.h"
#define LOG_CMD (1 << 1)
#define LOG_PARAM (1 << 2)
#define LOG_DATA (1 << 3)
#define LOG_SUBQ (1 << 4)
#define LOG_SUBQ2 (1 << 5) // log subq data to popmessage
#define VERBOSE (LOG_GENERAL | LOG_CMD | LOG_PARAM)
#define LOGPARAM LOGMASKED(LOG_PARAM, "-> Param: %02x %02x %02x %02x %02x %02x\n", \
m_input_fifo[1], m_input_fifo[2], m_input_fifo[3], \
m_input_fifo[4], m_input_fifo[5], m_input_fifo[6])
#include "logmacro.h"
//**************************************************************************
// DEVICE DEFINITIONS
// TYPE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(CR511B, cr511b_device, "cr511b", "CR-511-B CD-ROM drive")
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
cr511b_device::cr511b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
cdrom_image_device(mconfig, CR511B, tag, owner, clock),
device_mixer_interface(mconfig, *this, 2),
m_cdda(*this, "cdda"),
m_stch_cb(*this),
m_sten_cb(*this),
m_drq_cb(*this),
m_dten_cb(*this),
m_scor_cb(*this),
m_input_fifo_pos(0),
m_output_fifo_pos(0),
m_output_fifo_length(0),
m_status(0),
m_enabled(false),
m_cmd(false),
m_status_ready(false),
m_data_ready(false)
{
set_interface("cdrom");
}
//**************************************************************************
// MACHINE DEFINITIONS
//**************************************************************************
void cr511b_device::device_add_mconfig(machine_config &config)
{
CDROM(config, m_cdrom).set_interface("cdrom");
CDDA(config, m_cdda);
m_cdda->add_route(0, ":lspeaker", 1.0);
m_cdda->add_route(1, ":rspeaker", 1.0);
m_cdda->set_cdrom_tag("cdrom");
m_cdda->add_route(0, DEVICE_SELF, 1.0, AUTO_ALLOC_INPUT, 0);
m_cdda->add_route(1, DEVICE_SELF, 1.0, AUTO_ALLOC_INPUT, 1);
m_cdda->set_cdrom_tag(*this);
m_cdda->audio_end_cb().set(FUNC(cr511b_device::audio_end_cb));
}
//**************************************************************************
// LIVE DEVICE
// MACHINE EMULATION
//**************************************************************************
//-------------------------------------------------
// cr511b_device - constructor
//-------------------------------------------------
cr511b_device::cr511b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, CR511B, tag, owner, clock),
m_cdrom(*this, "cdrom"),
m_cdda(*this, "cdda"),
m_stch_handler(*this),
m_sten_handler(*this),
m_drq_handler(*this),
m_dten_handler(*this),
m_scor_handler(*this),
m_xaen_handler(*this),
//m_motor(false),
m_enabled(-1),
m_cmd(-1)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cr511b_device::device_start()
{
}
cdrom_image_device::device_start();
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
m_frame_timer = timer_alloc(FUNC(cr511b_device::frame_cb), this);
m_stch_timer = timer_alloc(FUNC(cr511b_device::stch), this);
m_sten_timer = timer_alloc(FUNC(cr511b_device::sten), this);
std::fill(std::begin(m_input_fifo), std::end(m_input_fifo), 0x00);
std::fill(std::begin(m_output_fifo), std::end(m_output_fifo), 0x00);
// register for save states
save_item(NAME(m_input_fifo));
save_item(NAME(m_input_fifo_pos));
save_item(NAME(m_output_fifo));
save_item(NAME(m_output_fifo_pos));
save_item(NAME(m_output_fifo_length));
save_item(NAME(m_status));
save_item(NAME(m_sector_size));
save_item(NAME(m_transfer_lba));
save_item(NAME(m_transfer_sectors));
save_item(NAME(m_transfer_length));
save_item(NAME(m_transfer_buffer));
save_item(NAME(m_transfer_buffer_pos));
save_item(NAME(m_enabled));
save_item(NAME(m_cmd));
save_item(NAME(m_status_ready));
save_item(NAME(m_data_ready));
}
void cr511b_device::device_reset()
{
cdrom_image_device::device_reset();
m_input_fifo_pos = 0;
m_output_fifo_pos = 0;
m_output_fifo_length = 0;
m_status_ready = false;
m_data_ready = false;
m_status = STATUS_READY;
if (exists())
m_status |= STATUS_MEDIA;
m_sten_cb(1);
m_stch_cb(0);
}
//**************************************************************************
// IMPLEMENTATION
//**************************************************************************
std::pair<std::error_condition, std::string> cr511b_device::call_load()
{
auto ret = cdrom_image_device::call_load();
if (!ret.first)
status_change(m_status | STATUS_MEDIA);
return ret;
}
void cr511b_device::call_unload()
{
status_change(STATUS_READY);
cdrom_image_device::call_unload();
}
uint32_t cr511b_device::lba_to_msf(int32_t lba)
{
uint32_t msf = 0;
lba += 2 * 75; // lba 0 is equivalent to msf 00:02:00
msf |= ((lba / (60 * 75)) & 0xff) << 16;
msf |= (((lba / 75) % 60) & 0xff) << 8;
msf |= ((lba % 75) & 0xff) << 0;
return msf;
}
int32_t cr511b_device::msf_to_lba(uint32_t msf)
{
uint32_t lba = 0;
lba += ((msf >> 16) & 0xff) * 60 * 75;
lba += ((msf >> 8) & 0xff) * 75;
lba += ((msf >> 0) & 0xff);
lba -= 2 * 75; // msf 00:02:00 is equivalent to lba 0
return lba;
}
int cr511b_device::size_to_track_type()
{
switch (m_sector_size)
{
case 2048: return cdrom_file::CD_TRACK_MODE1;
}
// have only seen 2048 so far
fatalerror("Unknown sector mode: %d\n", m_sector_size);
}
TIMER_CALLBACK_MEMBER(cr511b_device::frame_cb)
{
if (m_transfer_sectors > 0)
{
// old data hasn't been read completely yet
if (m_data_ready)
return;
LOGMASKED(LOG_DATA, "Reading sector: %d\n", m_transfer_lba);
read_data(m_transfer_lba, m_transfer_buffer, size_to_track_type());
// prepare for next sector
m_transfer_lba++;
m_transfer_sectors--;
m_transfer_buffer_pos = 0;
// signal that we have data
m_data_ready = true;
m_drq_cb(1);
}
else if (m_status & STATUS_PLAYING)
{
// TODO: subcode handling
m_scor_cb(0);
m_scor_cb(1);
}
}
TIMER_CALLBACK_MEMBER(cr511b_device::stch)
{
m_stch_cb(1);
m_stch_cb(0);
}
void cr511b_device::status_change(uint8_t status)
{
if (m_status != status)
{
m_status = status;
if (m_status & STATUS_MOTOR)
m_frame_timer->adjust(attotime::from_hz(75), 0, attotime::from_hz(75));
else
m_frame_timer->adjust(attotime::never);
m_stch_timer->adjust(attotime::from_usec(64 * 3)); // TODO
}
}
TIMER_CALLBACK_MEMBER(cr511b_device::sten)
{
m_status_ready = true;
m_sten_cb(0);
m_sten_cb(1);
}
void cr511b_device::status_enable(uint8_t output_length)
{
m_input_fifo_pos = 0;
m_output_fifo_pos = 0;
m_output_fifo_length = output_length;
// do we have status data to send?
if (m_output_fifo_length > 0)
{
if (m_input_fifo[0] != 0x87 || (VERBOSE & LOG_SUBQ))
LOGMASKED(LOG_CMD, "-> Output: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", m_output_fifo[0], m_output_fifo[1], m_output_fifo[2], m_output_fifo[3], m_output_fifo[4], m_output_fifo[5], m_output_fifo[6], m_output_fifo[7], m_output_fifo[8], m_output_fifo[9], m_output_fifo[10], m_output_fifo[11]);
m_sten_timer->adjust(attotime::from_usec(64 * 4)); // TODO
}
}
void cr511b_device::audio_end_cb(int state)
{
if (!state)
return;
LOGMASKED(LOG_CMD, "Playing audio finished\n", state);
status_change(m_status & ~STATUS_PLAYING);
}
uint8_t cr511b_device::read()
{
return 0xff;
uint8_t data = 0xff;
if (!m_enabled)
{
LOG("Read while not enabled!\n");
return data;
}
if (m_cmd)
{
// command mode
if (m_status_ready)
{
data = m_output_fifo[m_output_fifo_pos];
// clear old data once read
m_output_fifo[m_output_fifo_pos++] = 0x00;
LOGMASKED(LOG_DATA, "Data from drive: %02x (%d of %d)\n", data, m_output_fifo_pos, m_output_fifo_length);
// more data?
if (m_output_fifo_pos < m_output_fifo_length)
{
m_sten_cb(0);
m_sten_cb(1);
}
else
{
m_status_ready = false;
}
}
}
else
{
// data mode
if (m_data_ready)
{
data = m_transfer_buffer[m_transfer_buffer_pos];
m_transfer_length--;
LOGMASKED(LOG_DATA, "Data = %02x, pos %d, length %d\n", data, m_transfer_buffer_pos, m_transfer_length);
// finished transferring this sector?
if (++m_transfer_buffer_pos == m_sector_size)
{
m_data_ready = false;
m_drq_cb(0);
if (m_transfer_sectors == 0)
{
LOGMASKED(LOG_DATA, "Read done\n");
status_change(m_status | STATUS_SUCCESS);
}
}
}
}
return data;
}
void cr511b_device::write(uint8_t data)
{
}
// verify that we're enabled and are in command mode
if (!m_enabled || !m_cmd)
{
LOG("Invalid write: %02x (enabled: %d, cmd: %d)\n", data, m_enabled, m_cmd);
return;
}
void cr511b_device::enable_w(int state)
{
m_enabled = state;
m_input_fifo[m_input_fifo_pos++] = data;
switch (m_input_fifo[0])
{
case 0x01: if (m_input_fifo_pos == 7) cmd_seek(); break;
case 0x02: if (m_input_fifo_pos == 7) cmd_read(); break;
case 0x04: if (m_input_fifo_pos == 7) cmd_motor_on(); break;
case 0x05: if (m_input_fifo_pos == 7) cmd_motor_off(); break;
case 0x09: if (m_input_fifo_pos == 7) cmd_play_lba(); break;
case 0x0a: if (m_input_fifo_pos == 7) cmd_play_msf(); break;
case 0x0b: if (m_input_fifo_pos == 7) cmd_play_track(); break;
case 0x81: if (m_input_fifo_pos == 1) cmd_read_status(); break;
case 0x82: if (m_input_fifo_pos == 7) cmd_read_error(); break;
case 0x84: if (m_input_fifo_pos == 7) cmd_set_mode(); break;
case 0x87: if (m_input_fifo_pos == 7) cmd_read_subq(); break;
case 0x89: if (m_input_fifo_pos == 7) cmd_read_disc_info(); break;
case 0x8a: if (m_input_fifo_pos == 7) cmd_read_toc(); break;
case 0x8b: if (m_input_fifo_pos == 7) cmd_pause(); break;
case 0xa3: if (m_input_fifo_pos == 7) cmd_front_panel(); break;
default:
LOG("Unknown command: %02x\n", m_input_fifo[0]);
status_enable(0);
break;
}
}
void cr511b_device::cmd_w(int state)
{
m_cmd = state;
m_cmd = !bool(state); // active low
}
void cr511b_device::enable_w(int state)
{
m_enabled = !bool(state); // active low
}
void cr511b_device::cmd_seek()
{
LOGMASKED(LOG_CMD, "Command: Seek\n");
LOGPARAM;
// TODO: Does this enable STATUS_SUCCESS?
status_change(m_status | STATUS_MOTOR);
status_enable(0);
}
void cr511b_device::cmd_read()
{
LOGMASKED(LOG_CMD, "Command: Read\n");
LOGPARAM;
m_transfer_lba = (m_input_fifo[1] << 16) | (m_input_fifo[2] << 8) | (m_input_fifo[3] << 0);
m_transfer_sectors = (m_input_fifo[4] << 8) | (m_input_fifo[5] << 0);
m_transfer_length = m_transfer_sectors * m_sector_size;
LOGMASKED(LOG_CMD, "-> LBA %d, sectors %d\n", m_transfer_lba, m_transfer_sectors);
m_cdda->stop_audio();
uint8_t status = m_status;
status &= ~STATUS_PLAYING;
status |= STATUS_MOTOR;
status_change(status);
status_enable(0);
}
void cr511b_device::cmd_motor_on()
{
LOGMASKED(LOG_CMD, "Command: Motor On\n");
LOGPARAM;
// TODO: Does this enable STATUS_SUCCESS?
status_change(m_status | STATUS_MOTOR);
status_enable(0);
}
void cr511b_device::cmd_motor_off()
{
LOGMASKED(LOG_CMD, "Command: Motor Off\n");
LOGPARAM;
// TODO: Does this enable STATUS_SUCCESS?
status_change(m_status & ~STATUS_MOTOR);
status_enable(0);
}
void cr511b_device::cmd_play_lba()
{
LOGMASKED(LOG_CMD, "Command: Play LBA\n");
LOGPARAM;
// haven't found anything that uses it yet
fatalerror("Play LBA: Not implemented\n");
}
void cr511b_device::cmd_play_msf()
{
LOGMASKED(LOG_CMD, "Command: Play MSF\n");
LOGPARAM;
uint32_t start = (m_input_fifo[1] << 16) | (m_input_fifo[2] << 8) | (m_input_fifo[3] << 0);
uint32_t end = (m_input_fifo[4] << 16) | (m_input_fifo[5] << 8) | (m_input_fifo[6] << 0);
int32_t start_lba = msf_to_lba(start);
int32_t end_lba = msf_to_lba(end);
// play to the end of the disc?
if (end == 0xffffff)
end_lba = get_track_start(0xaa) - 1;
if (start == 0 && end == 0)
{
LOGMASKED(LOG_CMD, "Stop audio\n");
uint8_t status = m_status;
if (m_cdda->audio_active())
status |= STATUS_SUCCESS;
m_cdda->stop_audio();
status &= ~STATUS_PLAYING;
status &= ~STATUS_MOTOR;
status_change(status);
}
else if (start_lba < end_lba)
{
LOGMASKED(LOG_CMD, "Playing audio %02d:%02d.%02d to %02d:%02d.%02d (LBA %d to %d)\n",
m_input_fifo[1], m_input_fifo[2], m_input_fifo[3],
m_input_fifo[4], m_input_fifo[5], m_input_fifo[6], start_lba, end_lba);
m_cdda->start_audio(start_lba, end_lba - start_lba);
uint8_t status = m_status;
status |= STATUS_PLAYING;
status |= STATUS_MOTOR;
status_change(status);
}
else
{
LOGMASKED(LOG_CMD, "Invalid range %d to %d!\n", start_lba, end_lba);
status_change(m_status | STATUS_ERROR);
}
status_enable(0);
}
void cr511b_device::cmd_play_track()
{
LOGMASKED(LOG_CMD, "Command: Play Track\n");
LOGPARAM;
uint8_t start_track = m_input_fifo[1];
uint8_t start_index = m_input_fifo[2]; // TODO
uint8_t end_track = m_input_fifo[3];
uint8_t end_index = m_input_fifo[4]; // TODO
uint32_t start_lba = get_track_start(start_track - 1);
uint32_t end_lba = get_track_start(end_track - 1) - 1;
LOGMASKED(LOG_CMD, "Playing audio track %d-%d to %d-%d (LBA %d to %d)\n", start_track, start_index, end_track, end_index, start_lba, end_lba);
m_cdda->start_audio(start_lba, end_lba - start_lba);
status_change(m_status | STATUS_PLAYING | STATUS_MOTOR);
status_enable(0);
}
void cr511b_device::cmd_read_status()
{
LOGMASKED(LOG_CMD, "Command: Read Status\n");
m_status &= ~STATUS_SUCCESS;
m_output_fifo[0] = m_status;
status_enable(1);
}
void cr511b_device::cmd_read_error()
{
LOGMASKED(LOG_CMD, "Command: Read Error\n");
LOGPARAM;
m_status &= ~STATUS_ERROR;
m_status |= STATUS_READY;
m_status |= STATUS_SUCCESS;
m_output_fifo[2] |= (m_status & 0x10);
status_enable(6);
}
void cr511b_device::cmd_version()
{
LOGMASKED(LOG_CMD, "Command: Version\n");
LOGPARAM;
// haven't found anything that uses it yet
fatalerror("Version: Not implemented\n");
}
void cr511b_device::cmd_set_mode()
{
LOGMASKED(LOG_CMD, "Command: Set Mode\n");
LOGPARAM;
// 01: unknown (value seen: 02)
// 02: sector size hi
// 03: sector size lo
// 04: unknown (value seen 00)
// 05: unknown (value seen 0f)
// 06: unknown (value seen 00)
m_sector_size = (m_input_fifo[2] << 8) | m_input_fifo[3];
LOGMASKED(LOG_CMD, "Sector size = %d\n", m_sector_size);
m_status |= STATUS_SUCCESS; // ?
status_enable(0);
}
void cr511b_device::cmd_read_subq()
{
LOGMASKED(LOG_SUBQ, "Command: Read SubQ\n");
if (VERBOSE & LOG_SUBQ)
LOGPARAM;
// 01: bit 1 - msf or lba
// 02: unknown
// 03: unknown
// 04: unknown
// 05: unknown
// 06: unknown
bool msf = bool(BIT(m_input_fifo[1], 1));
if (m_cdda->audio_active())
{
uint32_t lba = m_cdda->get_audio_lba();
uint8_t track = get_track(lba);
uint32_t disc_pos = lba;
uint32_t track_pos = lba - get_track_start(track);
if (msf)
{
disc_pos = lba_to_msf(disc_pos);
track_pos = lba_to_msf(track_pos - 150);
if (VERBOSE & LOG_SUBQ2)
popmessage("Playing track %d at %02d:%02d.%02d (disc %02d:%02d.%02d)", track + 1,
track_pos >> 16 & 0xff, track_pos >> 8 & 0xff, track_pos & 0xff,
disc_pos >> 16 & 0xff, disc_pos >> 8 & 0xff, disc_pos & 0xff);
}
else
{
if (VERBOSE & LOG_SUBQ2)
popmessage("Playing track %d at %d (disc %d)", track, track_pos, disc_pos);
}
m_output_fifo[1] = get_adr_control(track);
m_output_fifo[2] = track + 1;
m_output_fifo[3] = 0x01; // TODO: index
m_output_fifo[4] = disc_pos >> 24;
m_output_fifo[5] = disc_pos >> 16;
m_output_fifo[6] = disc_pos >> 8;
m_output_fifo[7] = disc_pos >> 0;
m_output_fifo[8] = track_pos >> 24;
m_output_fifo[9] = track_pos >> 16;
m_output_fifo[10] = track_pos >> 8;
m_output_fifo[11] = track_pos >> 0;
m_output_fifo[12] = 0; // TODO: upc flag
if (m_cdda->audio_paused())
m_output_fifo[0] = AUDIO_STATUS_PAUSED;
else
m_output_fifo[0] = AUDIO_STATUS_PLAY;
}
else if (m_cdda->audio_ended())
m_output_fifo[0] = AUDIO_STATUS_COMPLETED;
else
m_output_fifo[0] = AUDIO_STATUS_NO_STATUS;
LOGMASKED(LOG_SUBQ, "-> Audio status = %02x\n", m_output_fifo[0]);
status_enable(13);
}
void cr511b_device::cmd_read_disc_info()
{
LOGMASKED(LOG_CMD, "Command: Read Disc Info\n");
LOGPARAM;
uint8_t last_track = get_last_track();
uint32_t last_lba = get_track_start(0xaa);
uint32_t last_msf = lba_to_msf(last_lba);
m_output_fifo[0] = 1; // first track
m_output_fifo[1] = last_track;
m_output_fifo[2] = last_msf >> 16;
m_output_fifo[3] = last_msf >> 8;
m_output_fifo[4] = last_msf >> 0;
status_change(m_status | STATUS_MOTOR | STATUS_SUCCESS);
status_enable(5);
}
void cr511b_device::cmd_read_toc()
{
LOGMASKED(LOG_CMD, "Command: Read TOC\n");
LOGPARAM;
// 01: bit 1 - msf or lba
// 02: track
// 03: unused?
// 04: unused?
// 05: unused?
// 06: unused?
bool msf = bool(BIT(m_input_fifo[1], 1));
uint8_t track = m_input_fifo[2];
uint8_t status = m_status;
status |= STATUS_MOTOR;
if (track > get_last_track())
{
LOGMASKED(LOG_CMD, "Invalid track requested: %d\n", track);
status |= STATUS_ERROR;
status_enable(0);
}
else if (track == 0)
{
uint32_t track_start = get_track_start(0xaa);
LOGMASKED(LOG_CMD, "Track 0 requested, lead out start %d\n", track_start);
if (msf)
track_start = lba_to_msf(track_start);
m_output_fifo[1] = get_adr_control(0xaa);
m_output_fifo[2] = 1; // first track
m_output_fifo[3] = get_last_track();
m_output_fifo[4] = track_start >> 24;
m_output_fifo[5] = track_start >> 16;
m_output_fifo[6] = track_start >> 8;
m_output_fifo[7] = track_start >> 0;
status_enable(8);
}
else
{
uint32_t track_start = get_track_start(track - 1);
LOGMASKED(LOG_CMD, "Track %d requested, start %d\n", track, track_start);
if (msf)
track_start = lba_to_msf(track_start);
m_output_fifo[1] = get_adr_control(track - 1);
m_output_fifo[2] = track;
m_output_fifo[3] = 0;
m_output_fifo[4] = track_start >> 24;
m_output_fifo[5] = track_start >> 16;
m_output_fifo[6] = track_start >> 8;
m_output_fifo[7] = track_start >> 0;
status_enable(8);
}
status_change(status);
}
void cr511b_device::cmd_pause()
{
LOGMASKED(LOG_CMD, "Command: Pause\n");
LOGPARAM;
// 01: 00 = pause audio, other values?
// 02: unused?
// 03: unused?
// 04: unused?
// 05: unused?
// 06: unused?
m_cdda->pause_audio(m_input_fifo[1] == 0x00);
status_enable(0);
}
void cr511b_device::cmd_front_panel()
{
LOGMASKED(LOG_CMD, "Command: Front Panel\n");
LOGPARAM;
// enables direct control of the drive by the front panel buttons: stop, play/pause, ff, rew
// 01: enable/disable
// 02: unused?
// 03: unused?
// 04: unused?
// 05: unused?
// 06: unused?
status_enable(0);
}

View File

@ -1,11 +1,11 @@
// license:BSD-3-Clause
// copyright-holders:Dirk Best
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************
CR-511-B CD-ROM drive
CD-ROM drive with a custom MKE/Panasonic interface as used in the
Commodore CDTV and early SoundBlaster cards.
Commodore CDTV. Similar to the interface on early SoundBlaster cards.
1 _RESET 2 GND
3 EFFK 4 SCCK
@ -38,65 +38,122 @@
#include "imagedev/cdromimg.h"
#include "sound/cdda.h"
class cr511b_device : public device_t
class cr511b_device : public cdrom_image_device, public device_mixer_interface
{
public:
// construction/destruction
cr511b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// callbacks
auto stch_handler() { return m_stch_handler.bind(); }
auto sten_handler() { return m_sten_handler.bind(); }
auto drq_handler() { return m_drq_handler.bind(); }
auto dten_handler() { return m_dten_handler.bind(); }
auto scor_handler() { return m_scor_handler.bind(); }
auto xaen_handler() { return m_xaen_handler.bind(); }
auto stch_cb() { return m_stch_cb.bind(); }
auto sten_cb() { return m_sten_cb.bind(); }
auto drq_cb() { return m_drq_cb.bind(); }
auto dten_cb() { return m_dten_cb.bind(); }
auto scor_cb() { return m_scor_cb.bind(); }
uint8_t read();
void write(uint8_t data);
void enable_w(int state);
void cmd_w(int state);
void enable_w(int state);
protected:
// device-level overrides
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
// device_image_interface implementation
virtual std::pair<std::error_condition, std::string> call_load() override;
virtual void call_unload() override;
private:
enum
{
STATUS_DOOR_CLOSED = 0x80,
STATUS_MEDIA = 0x40,
STATUS_MOTOR = 0x20,
STATUS_ERROR = 0x10,
STATUS_SUCCESS = 0x08,
STATUS_PLAYING = 0x04,
STATUS_DOOR_LOCKED = 0x02,
STATUS_READY = 0x01
};
uint32_t lba_to_msf(int32_t lba);
int32_t msf_to_lba(uint32_t msf);
int size_to_track_type();
TIMER_CALLBACK_MEMBER(frame_cb);
TIMER_CALLBACK_MEMBER(stch);
void status_change(uint8_t status);
TIMER_CALLBACK_MEMBER(sten);
void status_enable(uint8_t output_length);
void audio_end_cb(int state);
// commands
void cmd_seek();
void cmd_read();
void cmd_motor_on();
void cmd_motor_off();
void cmd_play_lba();
void cmd_play_msf();
void cmd_play_track();
void cmd_read_status();
void cmd_read_error();
void cmd_version();
void cmd_set_mode();
void cmd_read_subq();
void cmd_read_disc_info();
void cmd_read_toc();
void cmd_pause();
void cmd_front_panel();
// drive status
static constexpr uint8_t STATUS_DOOR_CLOSED = 0x80; // unverified, not used
static constexpr uint8_t STATUS_MEDIA = 0x40;
static constexpr uint8_t STATUS_MOTOR = 0x20;
static constexpr uint8_t STATUS_ERROR = 0x10;
static constexpr uint8_t STATUS_SUCCESS = 0x08; // last command has successfully executed
static constexpr uint8_t STATUS_PLAYING = 0x04;
static constexpr uint8_t STATUS_DOOR_LOCKED = 0x02; // unverified, not used
static constexpr uint8_t STATUS_READY = 0x01;
// audio status
static constexpr uint8_t AUDIO_STATUS_INVALID = 0x00;
static constexpr uint8_t AUDIO_STATUS_PLAY = 0x11;
static constexpr uint8_t AUDIO_STATUS_PAUSED = 0x12;
static constexpr uint8_t AUDIO_STATUS_COMPLETED = 0x13;
static constexpr uint8_t AUDIO_STATUS_ERROR = 0x14;
static constexpr uint8_t AUDIO_STATUS_NO_STATUS = 0x15;
required_device<cdrom_image_device> m_cdrom;
required_device<cdda_device> m_cdda;
devcb_write_line m_stch_handler;
devcb_write_line m_sten_handler;
devcb_write_line m_drq_handler;
devcb_write_line m_dten_handler;
devcb_write_line m_scor_handler;
devcb_write_line m_xaen_handler;
devcb_write_line m_stch_cb;
devcb_write_line m_sten_cb;
devcb_write_line m_drq_cb;
devcb_write_line m_dten_cb;
devcb_write_line m_scor_cb;
//bool m_motor;
emu_timer *m_frame_timer;
emu_timer *m_stch_timer;
emu_timer *m_sten_timer;
// state of lines
int m_enabled;
int m_cmd;
uint8_t m_input_fifo[16];
uint8_t m_input_fifo_pos;
// data transfer
//uint8_t m_sector_buffer[CD_MAX_SECTOR_DATA];
uint8_t m_output_fifo[16];
uint8_t m_output_fifo_pos;
uint8_t m_output_fifo_length;
uint8_t m_status;
uint16_t m_sector_size;
uint32_t m_transfer_lba;
uint16_t m_transfer_sectors;
uint32_t m_transfer_length;
uint8_t m_transfer_buffer[2352];
uint16_t m_transfer_buffer_pos;
// external lines
bool m_enabled;
bool m_cmd;
bool m_status_ready;
bool m_data_ready;
};
// device type declaration
DECLARE_DEVICE_TYPE(CR511B, cr511b_device)
#endif // MAME_MACHINE_CR511B_H

View File

@ -14,7 +14,7 @@
- SCSI
- Support newer variant
- DAWR
- Data corruption when installing WB31
- Rev 1: Data corruption when installing WB31
- FIFO?
***************************************************************************/
@ -47,6 +47,8 @@ amiga_dmac_device::amiga_dmac_device(const machine_config &mconfig, device_type
m_css_write_cb(*this),
m_csx0_read_cb(*this, 0),
m_csx0_write_cb(*this),
m_csx0_a4_read_cb(*this, 0),
m_csx0_a4_write_cb(*this),
m_csx1_read_cb(*this, 0),
m_csx1_write_cb(*this),
m_sdack_read_cb(*this, 0),
@ -99,6 +101,7 @@ void amiga_dmac_device::map(address_map &map)
map(0x008e, 0x008f).w(FUNC(amiga_dmac_device::dawr_w));
map(0x0090, 0x0093).rw(FUNC(amiga_dmac_device::css_r), FUNC(amiga_dmac_device::css_w)).umask16(0x00ff);
map(0x00a0, 0x00a7).rw(FUNC(amiga_dmac_device::csx0_r), FUNC(amiga_dmac_device::csx0_w)).umask16(0x00ff);
map(0x00b0, 0x00bf).rw(FUNC(amiga_dmac_device::csx0_a4_r), FUNC(amiga_dmac_device::csx0_a4_w)).umask16(0x00ff); // a4 + csx0
map(0x00c0, 0x00c7).rw(FUNC(amiga_dmac_device::csx1_r), FUNC(amiga_dmac_device::csx1_w)).umask16(0x00ff);
map(0x00e0, 0x00e1).rw(FUNC(amiga_dmac_device::st_dma_r), FUNC(amiga_dmac_device::st_dma_w));
map(0x00e2, 0x00e3).rw(FUNC(amiga_dmac_device::sp_dma_r), FUNC(amiga_dmac_device::sp_dma_w));
@ -192,7 +195,7 @@ TIMER_CALLBACK_MEMBER(amiga_dmac_device::update_dma)
m_acr++;
if (m_rev1 && (m_cntr & CNTR_TCEN))
if (m_cntr & CNTR_TCEN)
{
// we count words
if ((m_acr & 1) == 0)
@ -201,6 +204,8 @@ TIMER_CALLBACK_MEMBER(amiga_dmac_device::update_dma)
{
LOGMASKED(LOG_DMA, "Terminal count\n");
stop_dma();
m_istr |= ISTR_E_INT;
update_interrupts();
}
@ -254,22 +259,16 @@ void amiga_dmac_device::wtc_hi_w(offs_t offset, uint16_t data, uint16_t mem_mask
{
LOGMASKED(LOG_REGS, "wtc_hi_w: %04x & %04x\n", data, mem_mask);
if (m_rev1)
{
m_wtc &= (~(uint32_t) mem_mask) << 16 | 0x0000ffff;
m_wtc |= ((uint32_t) data & mem_mask) << 16;
}
m_wtc &= (~(uint32_t) mem_mask) << 16 | 0x0000ffff;
m_wtc |= ((uint32_t) data & mem_mask) << 16;
}
void amiga_dmac_device::wtc_lo_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
LOGMASKED(LOG_REGS, "wtc_lo_w: %04x & %04x\n", data, mem_mask);
if (m_rev1)
{
m_wtc &= 0xffff0000 & (~mem_mask);
m_wtc |= data & mem_mask;
}
m_wtc &= 0xffff0000 & (~mem_mask);
m_wtc |= data & mem_mask;
}
void amiga_dmac_device::acr_hi_w(offs_t offset, uint16_t data, uint16_t mem_mask)
@ -298,6 +297,8 @@ uint8_t amiga_dmac_device::css_r(offs_t offset) { return m_css_read_cb(offset);
void amiga_dmac_device::css_w(offs_t offset, uint8_t data) { m_css_write_cb(offset, data); }
uint8_t amiga_dmac_device::csx0_r(offs_t offset) { return m_csx0_read_cb(offset); }
void amiga_dmac_device::csx0_w(offs_t offset, uint8_t data) { m_csx0_write_cb(offset, data); }
uint8_t amiga_dmac_device::csx0_a4_r(offs_t offset) { return m_csx0_a4_read_cb(offset); }
void amiga_dmac_device::csx0_a4_w(offs_t offset, uint8_t data) { m_csx0_a4_write_cb(offset, data); }
uint8_t amiga_dmac_device::csx1_r(offs_t offset) { return m_csx1_read_cb(offset); }
void amiga_dmac_device::csx1_w(offs_t offset, uint8_t data) { m_csx1_write_cb(offset, data); }

View File

@ -26,6 +26,8 @@ public:
auto css_write_cb() { return m_css_write_cb.bind(); }
auto csx0_read_cb() { return m_csx0_read_cb.bind(); }
auto csx0_write_cb() { return m_csx0_write_cb.bind(); }
auto csx0_a4_read_cb() { return m_csx0_a4_read_cb.bind(); }
auto csx0_a4_write_cb() { return m_csx0_a4_write_cb.bind(); }
auto csx1_read_cb() { return m_csx1_read_cb.bind(); }
auto csx1_write_cb() { return m_csx1_write_cb.bind(); }
auto sdack_read_cb() { return m_sdack_read_cb.bind(); }
@ -77,6 +79,8 @@ private:
void css_w(offs_t offset, uint8_t data);
uint8_t csx0_r(offs_t offset);
void csx0_w(offs_t offset, uint8_t data);
uint8_t csx0_a4_r(offs_t offset);
void csx0_a4_w(offs_t offset, uint8_t data);
uint8_t csx1_r(offs_t offset);
void csx1_w(offs_t offset, uint8_t data);
@ -120,6 +124,8 @@ private:
devcb_write8 m_css_write_cb;
devcb_read8 m_csx0_read_cb;
devcb_write8 m_csx0_write_cb;
devcb_read8 m_csx0_a4_read_cb;
devcb_write8 m_csx0_a4_write_cb;
devcb_read8 m_csx1_read_cb;
devcb_write8 m_csx1_write_cb;
devcb_read8 m_sdack_read_cb;
@ -165,7 +171,7 @@ public:
amiga_dmac_rev2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// device type definition
// device type declaration
DECLARE_DEVICE_TYPE(AMIGA_DMAC_REV1, amiga_dmac_rev1_device)
DECLARE_DEVICE_TYPE(AMIGA_DMAC_REV2, amiga_dmac_rev2_device)

View File

@ -354,13 +354,6 @@ public:
u16 clock_r(offs_t offset);
void clock_w(offs_t offset, u16 data);
uint8_t dmac_scsi_data_read(offs_t offset);
void dmac_scsi_data_write(offs_t offset, uint8_t data);
void dmac_int_w(int state);
void tpi_port_b_write(uint8_t data);
void tpi_int_w(int state);
void cdtv(machine_config &config);
void cdtvn(machine_config &config);
void cdtv_mem(address_map &map) ATTR_COLD;
@ -369,21 +362,30 @@ public:
protected:
// driver_device overrides
virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
// amiga_state overrides
virtual bool int2_pending() override;
virtual bool int6_pending() override;
private:
// devices
void dmac_int_w(int state);
void tpi_portb_w(uint8_t data);
void tpi_int_w(int state);
void sten_w(int state);
void drq_w(int state);
required_device<msm6242_device> m_rtc;
required_device<amiga_dmac_rev1_device> m_dmac;
required_device<amiga_dmac_rev2_device> m_dmac;
required_device<tpi6525_device> m_tpi;
required_device<cr511b_device> m_cdrom;
// internal state
int m_dmac_irq;
int m_tpi_irq;
bool m_sten;
};
class a3000_state : public amiga_state
@ -643,43 +645,6 @@ void a500p_state::clock_w(offs_t offset, u16 data)
}
//**************************************************************************
// CD-ROM CONTROLLER
//**************************************************************************
uint8_t cdtv_state::dmac_scsi_data_read(offs_t offset)
{
if (offset >= 0xb0 && offset <= 0xbf)
return m_tpi->read(offset);
return 0xff;
}
void cdtv_state::dmac_scsi_data_write(offs_t offset, uint8_t data)
{
if (offset >= 0xb0 && offset <= 0xbf)
m_tpi->write(offset, data);
}
void cdtv_state::dmac_int_w(int state)
{
m_dmac_irq = state;
update_int2();
}
void cdtv_state::tpi_port_b_write(uint8_t data)
{
m_cdrom->cmd_w(BIT(data, 0));
m_cdrom->enable_w(BIT(data, 1));
}
void cdtv_state::tpi_int_w(int state)
{
m_tpi_irq = state;
update_int2();
}
//**************************************************************************
// DRIVER INIT
//**************************************************************************
@ -949,6 +914,15 @@ void cdtv_state::machine_start()
m_dmac->ramsz_w(0);
}
void cdtv_state::machine_reset()
{
amiga_state::machine_reset();
// start autoconfig
m_dmac->configin_w(0);
m_dmac->configin_w(1);
}
bool cdtv_state::int2_pending()
{
return m_cia_0_irq || m_dmac_irq || m_tpi_irq;
@ -959,6 +933,35 @@ bool cdtv_state::int6_pending()
return m_cia_1_irq;
}
void cdtv_state::dmac_int_w(int state)
{
m_dmac_irq = state;
update_int2();
}
void cdtv_state::tpi_portb_w(uint8_t data)
{
m_cdrom->enable_w(BIT(data, 1));
m_cdrom->cmd_w(BIT(data, 0));
}
void cdtv_state::tpi_int_w(int state)
{
m_tpi_irq = state;
update_int2();
}
void cdtv_state::sten_w(int state)
{
m_sten = bool(state);
}
void cdtv_state::drq_w(int state)
{
if (m_sten)
m_dmac->xdreq_w(state);
}
u32 a3000_state::scsi_r(offs_t offset, u32 mem_mask)
{
u32 data = 0xffffffff;
@ -1961,31 +1964,30 @@ void cdtv_state::cdtv(machine_config &config)
// 256kb memory card
NVRAM(config, "memcard", nvram_device::DEFAULT_ALL_0);
// real-time clock
MSM6242(config, m_rtc, XTAL(32'768));
// cd-rom controller
AMIGA_DMAC_REV1(config, m_dmac, amiga_state::CLK_7M_PAL);
m_dmac->css_read_cb().set(FUNC(cdtv_state::dmac_scsi_data_read));
m_dmac->css_write_cb().set(FUNC(cdtv_state::dmac_scsi_data_write));
AMIGA_DMAC_REV2(config, m_dmac, amiga_state::CLK_7M_PAL);
m_dmac->int_cb().set(FUNC(cdtv_state::dmac_int_w));
m_dmac->csx0_read_cb().set(m_cdrom, FUNC(cr511b_device::read));
m_dmac->csx0_write_cb().set(m_cdrom, FUNC(cr511b_device::write));
m_dmac->int_cb().set(FUNC(cdtv_state::dmac_int_w));
m_dmac->csx0_a4_read_cb().set(m_tpi, FUNC(tpi6525_device::read));
m_dmac->csx0_a4_write_cb().set(m_tpi, FUNC(tpi6525_device::write));
m_dmac->xdack_read_cb().set(m_cdrom, FUNC(cr511b_device::read));
TPI6525(config, m_tpi, 0);
m_tpi->out_irq_cb().set(FUNC(cdtv_state::tpi_int_w));
m_tpi->out_pb_cb().set(FUNC(cdtv_state::tpi_port_b_write));
m_tpi->out_pb_cb().set(FUNC(cdtv_state::tpi_portb_w));
// cd-rom
CR511B(config, m_cdrom, 0);
m_cdrom->scor_handler().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert();
m_cdrom->stch_handler().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert();
m_cdrom->sten_handler().set(m_tpi, FUNC(tpi6525_device::i3_w));
m_cdrom->xaen_handler().set(m_tpi, FUNC(tpi6525_device::pb2_w));
m_cdrom->drq_handler().set(m_dmac, FUNC(amiga_dmac_device::xdreq_w));
m_cdrom->dten_handler().set(m_dmac, FUNC(amiga_dmac_device::xdreq_w));
m_cdrom->add_route(0, "lspeaker", 1.0);
m_cdrom->add_route(1, "rspeaker", 1.0);
m_cdrom->scor_cb().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert();
m_cdrom->stch_cb().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert();
m_cdrom->sten_cb().set(m_tpi, FUNC(tpi6525_device::i3_w));
m_cdrom->sten_cb().append(FUNC(cdtv_state::sten_w));
m_cdrom->drq_cb().set(m_tpi, FUNC(tpi6525_device::i4_w));
m_cdrom->drq_cb().append(FUNC(cdtv_state::drq_w));
// software
SOFTWARE_LIST(config, "cd_list").set_original("cdtv");
}