OSDN Git Service

[VM][FMTOWNS][WIP] Adding CD-ROM/CD controller.This still be not completed.
authorK.Ohta <whatisthis.sowhat@gmail.com>
Thu, 31 Jan 2019 10:26:50 +0000 (19:26 +0900)
committerK.Ohta <whatisthis.sowhat@gmail.com>
Thu, 31 Jan 2019 10:26:50 +0000 (19:26 +0900)
[VM][UPD71071] Add 16bits transfer mode.

source/src/vm/fmtowns/cdc.cpp [new file with mode: 0644]
source/src/vm/fmtowns/towns_cdrom.cpp [new file with mode: 0644]
source/src/vm/upd71071.cpp
source/src/vm/upd71071.h

diff --git a/source/src/vm/fmtowns/cdc.cpp b/source/src/vm/fmtowns/cdc.cpp
new file mode 100644 (file)
index 0000000..b0720ea
--- /dev/null
@@ -0,0 +1,229 @@
+
+#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;
+       }
+}
diff --git a/source/src/vm/fmtowns/towns_cdrom.cpp b/source/src/vm/fmtowns/towns_cdrom.cpp
new file mode 100644 (file)
index 0000000..b666fee
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+       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;
+}
index 0206332..1bc1f10 100644 (file)
@@ -182,6 +182,20 @@ void UPD71071::write_signal(int id, uint32_t data, uint32_t mask)
 
 // 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
@@ -195,10 +209,9 @@ void UPD71071::do_dma()
                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);
@@ -207,18 +220,14 @@ void UPD71071::do_dma()
                                        } 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;
                                        }
@@ -227,9 +236,27 @@ void UPD71071::do_dma()
                                        } 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
index b3f308d..825d2b1 100644 (file)
@@ -18,7 +18,8 @@
 #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:
@@ -69,6 +70,7 @@ public:
        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