[VM][UPD71071] Add 16bits transfer mode.
--- /dev/null
+
+#include "towns_cdrom.h"
+
+namespace FMTOWNS {
+
+void CDC::initialize()
+{
+}
+
+void CDC::write_io8(uint32_t address, uint32_t data)
+{
+ /*
+ * 04C0h : Master control register
+ * 04C2h : Command register
+ * 04C4h : Parameter register
+ * 04C6h : Transfer control register.
+ */
+
+ switch(address & 0x0f) {
+ case 0x00: // Master control register
+ {
+ if((data & 0x80) != 0) {
+ if(submpu_intr) output_signals(&output_submpu_intr, 0x00000000);
+ submpu_intr = false;
+ }
+ if((data & 0x40) != 0) {
+ if(dma_intr) output_signals(&output_dma_intr, 0x00000000);
+ dma_intr = false;
+ }
+ submpu_intr_mask = ((data & 0x02) != 0) ? true : false;
+ dma_intr_mask = ((data & 0x01) != 0) ? true : false;
+ if((data & 0x04) != 0) this->reset();
+ w_regs[address & 0x0f] = data;
+ }
+ break;
+ case 0x02: // Command register
+ {
+ command_type_play = ((data & 0x80) != 0) ? true : false; // false = status command
+ stat_reply_intr = ((data & 0x40) != 0) ? true : false;
+ req_status = ((data & 0x20) != 0) ? true : false;
+ if(command_type_play) {
+ enqueue_command_play(data & 0x1f);
+ } else {
+ enqueue_command_status(data & 0x1f);
+ }
+ w_regs[address & 0x0f] = data;
+ }
+ break;
+ case 0x04: // Parameter register
+ {
+ if(param_fifo->full()) {
+ param_fifo->read(); // Dummy read
+ }
+ param_fifo->write((int)(data & 0xff));
+ w_regs[address & 0x0f] = data;
+ }
+ break;
+ case 0x06:
+ {
+ dma_transfer = ((data & 0x10) != 0) ? true : false;
+ pio_transfer = ((data & 0x08) != 0) ? true : false;
+ w_regs[address & 0x0f] = data;
+ }
+ break;
+ default:
+ if((addr & 0x01) == 0) {
+ w_regs[address & 0x0f] = data;
+ }
+ break;
+ }
+}
+
+uint32_t CDC::read_io8(uint32_t address)
+{
+ /*
+ * 04C0h : Master status register
+ */
+ uint32_t val = 0xff;
+ switch(addr & 0x0f) {
+ case 0x0: //Master status
+ {
+ val = 0x00;
+ val = val | ((submpu_intr) ? 0x80 : 0x00);
+ val = val | ((dma_intr) ? 0x40 : 0x00);
+ val = val | ((software_transfer_phase) ? 0x20 : 0x00);
+ val = val | ((d_dmac->read_signal(SIG_UPD71071_IS_TRANSFERING + 3) !=0) ? 0x10 : 0x00); // USING DMAC ch.3
+ val = val | ((has_status) ? 0x02 : 0x00);
+ val = val | ((submpu_ready) ? 0x01 : 0x00);
+ }
+ break;
+ case 0x2: // Status register
+ val = (uint32_t)(stat_fifo->read() & 0xff);
+ break;
+ case 0x4: //
+ if(pio_transfer) {
+ val = data_reg;
+ }
+ break;
+ case 0xc: // Sub code status register
+ val = 0x00;
+ val = val | (subq_fifo->empty()) ? 0x00 : 0x01;
+ val = val | ((d_cdrom->read_signal(SIG_TOWNS_CDROM_SUBQ_OVERRUN) != 0) ? 0x02 : 0x00);
+ break;
+ case 0xd:
+ val = (uint32_t)(subq_fifo->read() & 0xff);
+ break;
+ }
+ return val;
+}
+
+void CDC::read_cdrom(bool req_reply)
+{
+ uint8_t* command = d_cdrom->command;
+ extra_status = 0;
+ if(!(d_cdrom->is_device_ready())) {
+ if(req_reply) write_status(0x10, 0x00, 0x00, 0x00);
+ return;
+ }
+ if(param_fifo->count() < 6) {
+ // Error
+ return;
+ }
+
+ uint8_t m1, s1, f1;
+ uint8_t m2, s2, f2;
+ f2 = (uint8_t)(param_fifo->read() & 0xff);
+ s2 = (uint8_t)(param_fifo->read() & 0xff);
+ m2 = (uint8_t)(param_fifo->read() & 0xff);
+
+ f1 = (uint8_t)(param_fifo->read() & 0xff);
+ s1 = (uint8_t)(param_fifo->read() & 0xff);
+ m1 = (uint8_t)(param_fifo->read() & 0xff);
+
+ uint32_t lba1 = ((uint32_t)m1 & 0x1f) * 0x10000 + ((uint32_t)s1) * 0x100 + (uint32_t)f1;
+ uint32_t lba2 = ((uint32_t)m2 & 0x1f) * 0x10000 + ((uint32_t)s2) * 0x100 + (uint32_t)f2;
+ uint32_t __remain;
+ int track = get_track(lba1);
+ if(track < 2) {
+ if(lba1 >= 150) {
+ lba1 = lba1 - 150;
+ } else {
+ lba1 = 0;
+ }
+ if(lba2 >= 150) {
+ lba2 = lba2 - 150;
+ } else {
+ lba2 = 0;
+ }
+ }
+ set_cdda_status(CDDA_OFF);
+ if(lba1 > lba2) { // NOOP?
+ extra_status = 0;
+ write_status(0x01, 0x00, 0x00, 0x00);
+ return;
+ }
+ __remain = lba2 - lba1;
+ seek_time = get_seek_time(lba1);
+
+ command[0] = SCSI_CMD_READ12;
+ command[1] = 0; // LUN = 0
+ command[2] = 0;
+ command[3] = m2 & 0x1f;
+ command[4] = s2;
+ command[5] = f2;
+
+ command[6] = 0;
+ command[7] = (uint8_t)((__remain / 0x10000) & 0xff);
+ command[8] = (uint8_t)((__remain / 0x100) & 0xff);
+ command[9] = (uint8_t) (__remain % 0x100);
+
+ if(req_reply) {
+ extra_status = 2;
+ write_status(0x00, 0x00, 0x00, 0x00);
+ } else {
+ extra_status = 0;
+ if(pio_transfer) {
+ write_status(0x21, 0x00, 0x00, 0x00);
+ } else {
+ write_status(0x22, 0x00, 0x00, 0x00);
+ }
+ }
+ d_cdrom->start_command();
+}
+
+void CDC::play_cdda(bool req_reply)
+{
+ uint8_t* command = d_cdrom->command;
+ if(!(d_cdrom->is_device_ready())) {
+ if(req_reply) write_status(0x10, 0x00, 0x00, 0x00);
+ return;
+ }
+ if(param_fifo->count() < 6) {
+ // Error
+ return;
+ }
+ command[0] = TOWNS_CDROM_CDDA_PLAY;
+ command[1] = 0;
+ command[2] = 0;
+ command[3] = (uint8_t)(command_queue->read() & 0xff);
+ command[4] = (uint8_t)(command_queue->read() & 0xff);
+ commadn[5] = (uint8_t)(command_queue->read() & 0xff);
+ command[6] = 0;
+ command[7] = (uint8_t)(command_queue->read() & 0xff);
+ command[8] = (uint8_t)(command_queue->read() & 0xff);
+ command[9] = (uint8_t)(command_queue->read() & 0xff);
+
+
+ if(req_reply) {
+ extra_status = 1;
+ write_status(0x00, 0x03, 0x00, 0x00);
+ }
+ d_cdrom->start_command();
+
+}
+
+void CDC::write_status(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
+{
+ status_fifo->clear();
+ status_fifo->write(a);
+ status_fifo->write(b);
+ status_fifo->write(c);
+ status_fifo->write(d);
+ if(stat_reply_intr) {
+ if(!(submpu_intr_mask)) {
+ output_signals(&output_submpu_intr, 0xffffffff);
+ }
+ submpu_intr = true;
+ }
+}
--- /dev/null
+/*
+ FUJITSU FM Towns Emulator 'eFMTowns'
+
+ Author : Kyuma.Ohta <whatisthis.sowhat _at_ gmail.com>
+ Date : 2019.01.31 -
+
+ [Towns CDROM]
+*/
+
+
+#include "../scsi_cdrom.h"
+#include "../fifo.h"
+
+#define CDDA_OFF 0
+#define CDDA_PLAYING 1
+#define CDDA_PAUSED 2
+
+#define _SCSI_DEBUG_LOG
+#define _CDROM_DEBUG_LOG
+
+// Event must be larger than 116.
+
+namespace FMTOWNS {
+void TOWNS_CDROM::initialize()
+{
+ SCSI_CDROM::initialize();
+}
+
+void TOWNS_CDROM::release()
+{
+ SCSI_CDROM::release();
+}
+
+void TOWNS_CDROM::reset()
+{
+ SCSI_CDROM::reset();
+}
+
+void TOWNS_CDROM::write_signal(int id, uint32_t data, uint32_t mask)
+{
+ SCSI_CDROM::write_signal(id, data, mask);
+}
+
+uint32_t TOWNS_CDROM::read_signal(int id)
+{
+ return SCSI_CDROM::read_signal(id);
+}
+
+void TOWNS_CDROM::event_callback(int event_id, int err)
+{
+ SCSI_CDROM::event_callback(event_id, err);
+}
+
+int TOWNS_CDROM::get_command_length(int value)
+{
+ switch(value) {
+ case TOWNS_CDROM_CDDA_PLAY:
+ return 10;
+ break;
+ case TOWNS_CDROM_CDDA_PAUSE:
+ return 4;
+ break;
+ case TOWNS_CDROM_CDDA_UNPAUSE:
+ return 4;
+ break;
+ case TOWNS_CDROM_CDDA_STOP:
+ return 4;
+ break;
+ }
+
+ return SCSI_CDROM::get_command_length(value);
+}
+
+void TOWNS_CDROM::start_command()
+{
+ touch_sound();
+ switch(command[0]) {
+ case TOWNS_CDROM_CDDA_PLAY:
+ play_cdda_from_cmd();
+ return;
+ break;
+ case TOWNS_CDROM_CDDA_PAUSE:
+ pause_cdda_from_cmd();
+ return;
+ break;
+ case TOWNS_CDROM_CDDA_UNPAUSE:
+ unpause_cdda_from_cmd();
+ return;
+ break;
+ case TOWNS_CDROM_CDDA_STOP:
+ stop_cdda_from_cmd();
+ return;
+ break;
+ case SCSI_CMD_TST_U_RDY:
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQ_SENSE:
+ case SCSI_CMD_RD_DEFECT:
+ case SCSI_CMD_RD_CAPAC:
+ case SCSI_CMD_MODE_SEL6: // OK?
+ case SCSI_CMD_READ6:
+ case SCSI_CMD_READ10:
+ case SCSI_CMD_READ12:
+ SCSI_CDROM::start_command();
+ return;
+ break;
+ case 0xff:
+ // End of List
+ set_dat(SCSI_STATUS_CHKCOND);
+ return;
+ break;
+ default:
+ #ifdef _SCSI_DEBUG_LOG
+ this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: Unknown %02X\n"), scsi_id, command[0]);
+ #endif
+ set_dat(SCSI_STATUS_GOOD);
+ set_phase_delay(SCSI_PHASE_STATUS, 10.0);
+ }
+}
+
+
+// From MAME 0203's fmtowns.cpp .
+void TOWNS_CDROM::play_cdda_from_cmd()
+{
+ uint8_t m_start = command[3];
+ uint8_t s_start = command[4];
+ uint8_t f_start = command[5];
+ uint8_t m_end = command[7];
+ uint8_t s_end = command[8];
+ uint8_t f_end = command[9];
+ if(is_device_ready()) {
+ cdda_start_frame = FROM_BCD(f_start) + (FROM_BCD(s_start) + FROM_BCD(m_start) * 60) * 75;
+ cdda_end_frame = FROM_BCD(f_end) + (FROM_BCD(s_end) + FROM_BCD(m_end) * 60) * 75;
+ int track = get_track(cdda_start_frame);
+ if(cdda_start_frame >= toc_table[track].pregap) {
+ cdda_start_frame -= toc_table[track].pregap;
+ }
+ if(cdda_start_frame < toc_table[track].index0) {
+ cdda_start_frame = toc_table[track].index0; // don't play pregap
+ } else if(cdda_start_frame > max_logical_block) {
+ cdda_start_frame = 0;
+ }
+ int track = current_track;
+ cdda_playing_frame = cdda_start_frame;
+ if(cdda_end_frame > toc_table[track + 1].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track + 1].index1) {
+ auto_increment_track = true;
+ }
+ if(event_cdda_delay_play >= 0) {
+ cancel_event(this, event_cdda_delay_play);
+ event_cdda_delay_play = -1;
+ }
+ register_event(this, EVENT_CDDA_DELAY_PLAY, 10.0, false, &event_cdda_delay_play);
+
+ }
+}
+
+void TOWNS_CDROM::set_subq(void)
+{
+ if(is_device_ready()) {
+ // create track info
+ uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
+ uint32_t msf_abs = lba_to_msf_alt(frame);
+ int track;
+ double delay_time = 10.0;
+ track = current_track;
+ if((cdda_status == CDDA_OFF) && (toc_table[track].is_audio)) { // OK? (or force ERROR) 20181120 K.O
+ //set_cdda_status(CDDA_PLAYING);
+ delay_time = get_seek_time(frame);
+ //delay_time = 10.0;
+ if(event_cdda_delay_play >= 0) {
+ cancel_event(this, event_cdda_delay_play);
+ event_cdda_delay_play = -1;
+ }
+ register_event(this, EVENT_CDDA_DELAY_PLAY, delay_time, false, &event_cdda_delay_play);
+ }
+ uint32_t msf_rel = lba_to_msf_alt(frame - toc_table[track].index0);
+
+ write_signals(&output_subq_overrun, (subq_buffer->empty()) ? 0x00000000 : 0xffffffff);
+ subq_buffer->clear();
+ // http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-130.pdf
+ //subq_buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
+
+ subq_buffer->write(TO_BCD(track + 1)); // TNO
+ subq_buffer->write(TO_BCD((cdda_status == CDDA_PLAYING) ? 0x00 : ((cdda_status == CDDA_PAUSED) ? 0x00 : 0x01))); // INDEX
+ subq_buffer->write(TO_BCD((msf_rel >> 16) & 0xff)); // M (relative)
+ subq_buffer->write(TO_BCD((msf_rel >> 8) & 0xff)); // S (relative)
+ subq_buffer->write(TO_BCD((msf_rel >> 0) & 0xff)); // F (relative)
+ subq_buffer->write(TO_BCD(0x00)); // Zero (relative)
+ subq_buffer->write(TO_BCD((msf_abs >> 16) & 0xff)); // M (absolute)
+ subq_buffer->write(TO_BCD((msf_abs >> 8) & 0xff)); // S (absolute)
+ subq_buffer->write(TO_BCD((msf_abs >> 0) & 0xff)); // F (absolute)
+ // transfer length
+ remain = subq_buffer->count();
+ // set first data
+ // change to data in phase
+ set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
+ } else {
+ //write_signals(&output_subq_overrun, (subq_buffer->empty()) ? 0x00000000 : 0xffffffff); // OK?
+ subq_buffer->clear();
+ // transfer length
+ remain = subq_buffer->count();
+ set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
+ set_phase_delay(SCSI_PHASE_STATUS, 10.0);
+ }
+ return;
+}
// note: if SINGLE_MODE_DMA is defined, do_dma() is called in every machine cycle
+int UPD71071::read_signal(int ch)
+{
+ if((ch >= (SIG_UPD71071_IS_TRANSFERING + 0)) && (ch < (SIG_UPD71071_IS_TRANSFERING + 4))) {
+ bool _nch = ch - SIG_UPD71071_IS_TRANSFERING;
+ if((cmd & 0x04) != 0) return 0x00; // Not transfering
+ if((dma[_nch].creg == 0)) return 0x00; //
+ return 0xffffffff;
+ } else if((ch >= (SIG_UPD71071_IS_16BITS_TRANSFER + 0)) && (ch < (SIG_UPD71071_IS_16BITS_TRANSFER + 4))) {
+ bool _nch = ch - SIG_UPD71071_IS_16BITS_TRANSFER;
+ return ((dma[_nch].mode & 1) != 0) ? 0xffffffff : 0;
+ }
+ return 0;
+}
+
void UPD71071::do_dma()
{
// check DDMA
if(((req | sreq) & bit) && !(mask & bit)) {
// execute dma
while((req | sreq) & bit) {
- // ToDo: Will check WORD transfer mode for FM-Towns.(mode.bit0 = '1).
-/*
+ // Will check WORD transfer mode for FM-Towns.(mode.bit0 = '1).
if((dma[c].mode & 0x01) == 1) {
- // 16bit transfer mode
+ // 8bit transfer mode
if((dma[c].mode & 0x0c) == 0x00) {
// verify
uint32_t val = dma[c].dev->read_dma_io16(0);
} else if((dma[c].mode & 0x0c) == 0x04) {
// io -> memory
uint32_t val;
- if(dma[c].dev != NULL) {
- val = dma[c].dev->read_dma_io16(0);
- } else {
- val = 0xffff;
- }
+ val = dma[c].dev->read_dma_io16(0);
d_mem->write_dma_data16(dma[c].areg, val);
// update temporary register
tmp = val;
} else if((dma[c].mode & 0x0c) == 0x08) {
// memory -> io
uint32_t val = d_mem->read_dma_data16(dma[c].areg);
- if(dma[c].dev != NULL) dma[c].dev->write_dma_io16(0, val);
+ dma[c].dev->write_dma_io16(0, val);
// update temporary register
tmp = val;
}
} else {
dma[c].areg = (dma[c].areg + 2) & 0xffffff;
}
- } else
-*/
- {
+ if(dma[c].creg-- == 0) { // OK?
+ // TC
+ if(dma[c].mode & 0x10) {
+ // auto initialize
+ dma[c].areg = dma[c].bareg;
+ dma[c].creg = dma[c].bcreg;
+ } else {
+ mask |= bit;
+ }
+ req &= ~bit;
+ sreq &= ~bit;
+ tc |= bit;
+
+ write_signals(&outputs_tc, 0xffffffff);
+ } else if(_SINGLE_MODE_DMA) {
+ if((dma[c].mode & 0xc0) == 0x40) {
+ // single mode
+ break;
+ }
+ }
+ } else {
// 8bit transfer mode
if((dma[c].mode & 0x0c) == 0x00) {
// verify
#define SIG_UPD71071_CH1 1
#define SIG_UPD71071_CH2 2
#define SIG_UPD71071_CH3 3
-
+#define SIG_UPD71071_IS_TRANSFERING 4 /* 4 - 7 */
+#define SIG_UPD71071_IS_16BITS_TRANSFER 8 /* 8 - 11 */
class UPD71071 : public DEVICE
{
private:
void write_io8(uint32_t addr, uint32_t data);
uint32_t read_io8(uint32_t addr);
void write_signal(int id, uint32_t data, uint32_t mask);
+ uint32_t read_signal(int id);
void do_dma();
bool process_state(FILEIO* state_fio, bool loading);
// unique functions