trunk/src/emu/bus/msx_cart/yamaha.c
| r31025 | r31026 | |
| 4 | 4 | |
| 5 | 5 | TODO: |
| 6 | 6 | - Use a real YM2164 implementation for SFG05 |
| 7 | | - Implement midi functionality (YM2148) |
| 8 | 7 | |
| 9 | 8 | **************************************************************************/ |
| 10 | 9 | |
| 11 | 10 | #include "emu.h" |
| 12 | 11 | #include "yamaha.h" |
| 12 | #include "bus/midi/midi.h" |
| 13 | 13 | |
| 14 | 14 | |
| 15 | 15 | const device_type MSX_CART_SFG01 = &device_creator<msx_cart_sfg01>; |
| r31025 | r31026 | |
| 22 | 22 | , m_region_sfg(*this, "sfg") |
| 23 | 23 | , m_ym2151(*this, "ym2151") |
| 24 | 24 | , m_kbdc(*this, "kbdc") |
| 25 | , m_ym2148(*this, "ym2148") |
| 25 | 26 | , m_ym2151_irq_state(CLEAR_LINE) |
| 26 | 27 | , m_ym2148_irq_state(CLEAR_LINE) |
| 27 | 28 | , m_rom_mask(0) |
| 28 | | , m_ym2148_irq_vector(0xff) |
| 29 | | , m_ym2148_external_irq_vector(0xff) |
| 30 | 29 | { |
| 31 | 30 | } |
| 32 | 31 | |
| r31025 | r31026 | |
| 49 | 48 | // YM2148 (MKS) |
| 50 | 49 | |
| 51 | 50 | MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") |
| 52 | | MCFG_YM2151_ADD("ym2151", XTAL_4MHz) // The SFG01 uses a YM2151, the SFG05 uses a YM2164 |
| 51 | MCFG_YM2151_ADD("ym2151", XTAL_3_579545MHz) // The SFG01 uses a YM2151, the SFG05 uses a YM2164, input clock comes from the main cpu frequency |
| 53 | 52 | MCFG_YM2151_IRQ_HANDLER(WRITELINE(msx_cart_sfg, ym2151_irq_w)) |
| 54 | | // MCFG_YM2151_PORT_WRITE_HANDLER(WRITE8(msx_cart_sfg, port_w)) |
| 55 | 53 | MCFG_SOUND_ROUTE(0, "lspeaker", 0.80) |
| 56 | 54 | MCFG_SOUND_ROUTE(1, "rspeaker", 0.80) |
| 57 | 55 | |
| 56 | MCFG_DEVICE_ADD("ym2148", YM2148, XTAL_4MHz) |
| 57 | MCFG_YM2148_TXD_HANDLER(DEVWRITELINE("mdout", midi_port_device, write_txd)) |
| 58 | MCFG_YM2148_PORT_WRITE_HANDLER(DEVWRITE8("kbdc", msx_audio_kbdc_port_device, write)) |
| 59 | MCFG_YM2148_PORT_READ_HANDLER(DEVREAD8("kbdc", msx_audio_kbdc_port_device, read)) |
| 60 | MCFG_YM2148_IRQ_HANDLER(WRITELINE(msx_cart_sfg,ym2148_irq_w)) |
| 61 | |
| 58 | 62 | MCFG_MSX_AUDIO_KBDC_PORT_ADD("kbdc", msx_audio_keyboards, NULL) |
| 63 | |
| 64 | MCFG_MIDI_PORT_ADD("mdout", midiout_slot, "midiout") |
| 65 | |
| 66 | MCFG_MIDI_PORT_ADD("mdin", midiin_slot, "midiin") |
| 67 | MCFG_MIDI_RX_HANDLER(DEVWRITELINE("ym2148", ym2148_device, write_rxd)) |
| 59 | 68 | MACHINE_CONFIG_END |
| 60 | 69 | |
| 61 | 70 | |
| r31025 | r31026 | |
| 102 | 111 | |
| 103 | 112 | IRQ_CALLBACK_MEMBER(msx_cart_sfg::irq_callback) |
| 104 | 113 | { |
| 105 | | if (m_ym2148_irq_state == ASSERT_LINE) |
| 106 | | { |
| 107 | | return m_ym2148_irq_vector; |
| 108 | | } |
| 109 | | |
| 110 | | return m_ym2148_external_irq_vector; |
| 114 | return m_ym2148->get_irq_vector(); |
| 111 | 115 | } |
| 112 | 116 | |
| 113 | 117 | |
| r31025 | r31026 | |
| 147 | 151 | return m_ym2151->status_r(space, 0); |
| 148 | 152 | |
| 149 | 153 | case 0x3ff2: // YM-2148 keyboard column read |
| 150 | | return m_kbdc->read(space, 0); |
| 151 | | |
| 152 | 154 | case 0x3ff3: // YM-2148 -- |
| 153 | 155 | case 0x3ff4: // YM-2148 -- |
| 154 | 156 | case 0x3ff5: // YM-2148 MIDI UART data read register |
| 155 | 157 | case 0x3ff6: // YM-2148 MIDI UART status register |
| 156 | | break; |
| 158 | // ------x- - 1 = received a byte/receive buffer full? |
| 159 | // -------x - 1 = ready to send next byte/send buffer empty? |
| 160 | return m_ym2148->read(space, offset & 7); |
| 157 | 161 | } |
| 158 | 162 | |
| 159 | 163 | if (offset < 0x8000) |
| r31025 | r31026 | |
| 178 | 182 | break; |
| 179 | 183 | |
| 180 | 184 | case 0x3ff2: // YM-2148 write keyboard row |
| 181 | | m_kbdc->write(space, 0, data); |
| 182 | | break; |
| 183 | | |
| 184 | 185 | case 0x3ff3: // YM-2148 MIDI IRQ vector |
| 185 | | m_ym2148_irq_vector = data; |
| 186 | | break; |
| 187 | | |
| 188 | 186 | case 0x3ff4: // YM-2148 External IRQ vector |
| 189 | | m_ym2148_external_irq_vector = data; |
| 187 | case 0x3ff5: // YM-2148 MIDI UART data write register |
| 188 | case 0x3ff6: // YM-2148 MIDI UART command register |
| 189 | // On startup the sfg01 writes 0x80 |
| 190 | // followed by 0x05. |
| 191 | // Other write seen in the code: 0x15 |
| 192 | // |
| 193 | // x------- - 1 = reset |
| 194 | // -----x-- - 1 = enable receiving / sending midi data |
| 195 | // -------x - 1 = enable receiving / sending midi data |
| 196 | m_ym2148->write(space, offset & 7, data); |
| 190 | 197 | break; |
| 191 | 198 | |
| 192 | | case 0x3ff5: // YM-2148 MIDI UART data write register |
| 193 | | case 0x3ff6: // YM-2148 MIDI UART command register |
| 194 | 199 | default: |
| 195 | 200 | logerror("msx_cart_sfg::write_cart: write %02x to %04x\n", data, offset); |
| 196 | 201 | break; |
trunk/src/emu/machine/ym2148.c
| r0 | r31026 | |
| 1 | /************************************************************************** |
| 2 | |
| 3 | Yamaha YM-2148 emulation |
| 4 | |
| 5 | TODO: |
| 6 | - Finish implemention of midi out |
| 7 | |
| 8 | **************************************************************************/ |
| 9 | |
| 10 | #include "emu.h" |
| 11 | #include "ym2148.h" |
| 12 | |
| 13 | |
| 14 | const device_type YM2148 = &device_creator<ym2148_device>; |
| 15 | |
| 16 | |
| 17 | ym2148_device::ym2148_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 18 | : device_t(mconfig, YM2148, "YM2148", tag, owner, clock, "ym2148", __FILE__) |
| 19 | , device_serial_interface(mconfig, *this) |
| 20 | , m_txd_handler(*this) |
| 21 | , m_irq_handler(*this) |
| 22 | , m_port_write_handler(*this) |
| 23 | , m_port_read_handler(*this) |
| 24 | , m_irq_state(CLEAR_LINE) |
| 25 | , m_irq_vector(0xff) // guess |
| 26 | , m_external_irq_vector(0xff) // guess |
| 27 | , m_data_out(0) |
| 28 | , m_data_in(0) |
| 29 | , m_control(0) |
| 30 | , m_status(0) |
| 31 | , m_rxd(1) |
| 32 | , m_tx_busy(false) |
| 33 | { |
| 34 | } |
| 35 | |
| 36 | |
| 37 | void ym2148_device::device_start() |
| 38 | { |
| 39 | m_txd_handler.resolve_safe(); |
| 40 | m_irq_handler.resolve_safe(); |
| 41 | m_port_write_handler.resolve_safe(); |
| 42 | m_port_read_handler.resolve_safe(0xff); |
| 43 | |
| 44 | // Start a timer to trigger at clock / 8 / 16 |
| 45 | m_timer = timer_alloc(0); |
| 46 | m_timer->adjust(attotime::from_hz(m_clock / 8 / 16), 0, attotime::from_hz(m_clock / 8 / 16)); |
| 47 | } |
| 48 | |
| 49 | |
| 50 | void ym2148_device::device_reset() |
| 51 | { |
| 52 | set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); |
| 53 | transmit_register_reset(); |
| 54 | receive_register_reset(); |
| 55 | m_status |= STATUS_TRANSMIT_READY; |
| 56 | } |
| 57 | |
| 58 | |
| 59 | void ym2148_device::receive_clock() |
| 60 | { |
| 61 | if (m_control & CONTROL_RECEIVE_ENABLE) |
| 62 | { |
| 63 | /* get bit received from other side and update receive register */ |
| 64 | receive_register_update_bit(m_rxd); |
| 65 | |
| 66 | if (is_receive_register_full()) |
| 67 | { |
| 68 | receive_register_extract(); |
| 69 | |
| 70 | m_data_in = get_received_char(); |
| 71 | |
| 72 | if (m_status & STATUS_RECEIVE_BUFFER_FULL) |
| 73 | { |
| 74 | // Overrun error |
| 75 | m_status |= STATUS_OVERRUN_ERROR; |
| 76 | } |
| 77 | m_status |= STATUS_RECEIVE_BUFFER_FULL; |
| 78 | update_irq(); |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | |
| 84 | void ym2148_device::transmit_clock() |
| 85 | { |
| 86 | if (m_control & CONTROL_TRANSMIT_ENABLE) |
| 87 | { |
| 88 | if (!(m_status & STATUS_TRANSMIT_READY)) |
| 89 | { |
| 90 | /* is diserial ready for it? */ |
| 91 | if (is_transmit_register_empty()) |
| 92 | { |
| 93 | /* set it up */ |
| 94 | transmit_register_setup(m_data_out); |
| 95 | /* ready for next transmit */ |
| 96 | m_status |= STATUS_TRANSMIT_READY; |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /* if diserial has bits to send, make them so */ |
| 101 | if (!is_transmit_register_empty()) |
| 102 | { |
| 103 | UINT8 data = transmit_register_get_data_bit(); |
| 104 | m_tx_busy = true; |
| 105 | m_txd_handler(data); |
| 106 | } |
| 107 | |
| 108 | // is transmitter totally done? |
| 109 | if ((m_status & STATUS_TRANSMIT_READY) && is_transmit_register_empty()) |
| 110 | { |
| 111 | m_tx_busy = false; |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | |
| 117 | void ym2148_device::update_irq() |
| 118 | { |
| 119 | m_irq_state = CLEAR_LINE; |
| 120 | |
| 121 | if ((m_status & STATUS_RECEIVE_BUFFER_FULL) && (m_control & CONTROL_RECEIVE_IRQ_ENABLE)) |
| 122 | { |
| 123 | m_irq_state = ASSERT_LINE; |
| 124 | } |
| 125 | if ((m_status & STATUS_TRANSMIT_READY) && (m_control & CONTROL_TRANSMIT_IRQ_ENABLE)) |
| 126 | { |
| 127 | // m_irq_state = ASSERT_LINE; |
| 128 | } |
| 129 | |
| 130 | m_irq_handler(m_irq_state); |
| 131 | } |
| 132 | |
| 133 | |
| 134 | void ym2148_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) |
| 135 | { |
| 136 | receive_clock(); |
| 137 | transmit_clock(); |
| 138 | } |
| 139 | |
| 140 | |
| 141 | READ8_MEMBER(ym2148_device::read) |
| 142 | { |
| 143 | switch (offset & 7) |
| 144 | { |
| 145 | case 2: // External port read |
| 146 | return m_port_read_handler(); |
| 147 | |
| 148 | case 5: // Midi data read register |
| 149 | m_status &= ~STATUS_RECEIVE_BUFFER_FULL; |
| 150 | update_irq(); |
| 151 | return m_data_in; |
| 152 | |
| 153 | case 6: // Midi status register |
| 154 | return m_status; |
| 155 | } |
| 156 | return 0xff; |
| 157 | } |
| 158 | |
| 159 | |
| 160 | WRITE8_MEMBER(ym2148_device::write) |
| 161 | { |
| 162 | switch (offset & 7) |
| 163 | { |
| 164 | case 2: // External port write |
| 165 | m_port_write_handler(data); |
| 166 | break; |
| 167 | |
| 168 | case 3: // IRQ vector |
| 169 | m_irq_vector = data; |
| 170 | break; |
| 171 | |
| 172 | case 4: // External IRQ vector |
| 173 | m_external_irq_vector = data; |
| 174 | break; |
| 175 | |
| 176 | case 5: // Midi data write register |
| 177 | m_data_out = data; |
| 178 | m_status &= ~STATUS_TRANSMIT_READY; |
| 179 | break; |
| 180 | |
| 181 | case 6: // Midi control register |
| 182 | m_control = data; |
| 183 | |
| 184 | if (m_control & 0x80) |
| 185 | { |
| 186 | // Reset |
| 187 | receive_clock(); |
| 188 | transmit_clock(); |
| 189 | m_irq_state = CLEAR_LINE; |
| 190 | m_irq_handler(m_irq_state); |
| 191 | } |
| 192 | update_irq(); |
| 193 | break; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | |
| 198 | UINT8 ym2148_device::get_irq_vector() |
| 199 | { |
| 200 | return (m_irq_state == ASSERT_LINE) ? m_irq_vector : m_external_irq_vector; |
| 201 | } |
| 202 | |
| 203 | |
| 204 | WRITE_LINE_MEMBER(ym2148_device::write_rxd) |
| 205 | { |
| 206 | m_rxd = state; |
| 207 | } |
| 208 | |
trunk/src/emu/machine/ym2148.h
| r0 | r31026 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | ym2148.h |
| 4 | |
| 5 | Yamaha YM2148 Midi and keyboard interface |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #ifndef __YM2148_H__ |
| 10 | #define __YM2148_H__ |
| 11 | |
| 12 | |
| 13 | //************************************************************************** |
| 14 | // INTERFACE CONFIGURATION MACROS |
| 15 | //************************************************************************** |
| 16 | |
| 17 | #define MCFG_YM2148_TXD_HANDLER(_devcb) \ |
| 18 | devcb = &ym2148_device::set_txd_handler(*device, DEVCB_##_devcb); |
| 19 | |
| 20 | #define MCFG_YM2148_PORT_WRITE_HANDLER(_devcb) \ |
| 21 | devcb = &ym2148_device::set_port_write_handler(*device, DEVCB_##_devcb); |
| 22 | |
| 23 | #define MCFG_YM2148_PORT_READ_HANDLER(_devcb) \ |
| 24 | devcb = &ym2148_device::set_port_read_handler(*device, DEVCB_##_devcb); |
| 25 | |
| 26 | #define MCFG_YM2148_IRQ_HANDLER(_devcb) \ |
| 27 | devcb = &ym2148_device::set_irq_handler(*device, DEVCB_##_devcb); |
| 28 | |
| 29 | |
| 30 | class ym2148_device : public device_t, |
| 31 | public device_serial_interface |
| 32 | { |
| 33 | public: |
| 34 | // construction/destruction |
| 35 | ym2148_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 36 | |
| 37 | // static configuration helpers |
| 38 | template<class _Object> static devcb_base &set_txd_handler(device_t &device, _Object object) { return downcast<ym2148_device &>(device).m_txd_handler.set_callback(object); } |
| 39 | template<class _Object> static devcb_base &set_port_write_handler(device_t &device, _Object object) { return downcast<ym2148_device &>(device).m_port_write_handler.set_callback(object); } |
| 40 | template<class _Object> static devcb_base &set_port_read_handler(device_t &device, _Object object) { return downcast<ym2148_device &>(device).m_port_read_handler.set_callback(object); } |
| 41 | template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym2148_device &>(device).m_irq_handler.set_callback(object); } |
| 42 | |
| 43 | DECLARE_READ8_MEMBER(read); |
| 44 | DECLARE_WRITE8_MEMBER(write); |
| 45 | |
| 46 | DECLARE_WRITE_LINE_MEMBER(write_rxd); |
| 47 | UINT8 get_irq_vector(); |
| 48 | |
| 49 | protected: |
| 50 | virtual void device_start(); |
| 51 | virtual void device_reset(); |
| 52 | virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); |
| 53 | |
| 54 | private: |
| 55 | devcb_write_line m_txd_handler; |
| 56 | devcb_write_line m_irq_handler; |
| 57 | devcb_write8 m_port_write_handler; // write ST0-ST7 |
| 58 | devcb_read8 m_port_read_handler; // read SD0-SD7 |
| 59 | int m_irq_state; |
| 60 | UINT8 m_irq_vector; |
| 61 | UINT8 m_external_irq_vector; |
| 62 | // Does this chip have 1 or 2 data registers? |
| 63 | UINT8 m_data_out; |
| 64 | UINT8 m_data_in; |
| 65 | UINT8 m_control; |
| 66 | UINT8 m_status; |
| 67 | emu_timer *m_timer; |
| 68 | int m_rxd; |
| 69 | bool m_tx_busy; |
| 70 | |
| 71 | void receive_clock(); |
| 72 | void transmit_clock(); |
| 73 | void update_irq(); |
| 74 | |
| 75 | enum |
| 76 | { |
| 77 | STATUS_TRANSMIT_READY = 0x01, |
| 78 | STATUS_RECEIVE_BUFFER_FULL = 0x2, |
| 79 | STATUS_OVERRUN_ERROR = 0x20, |
| 80 | CONTROL_TRANSMIT_ENABLE = 0x01, |
| 81 | CONTROL_TRANSMIT_IRQ_ENABLE = 0x02, |
| 82 | CONTROL_RECEIVE_ENABLE = 0x04, |
| 83 | CONTROL_RECEIVE_IRQ_ENABLE = 0x08 |
| 84 | }; |
| 85 | }; |
| 86 | |
| 87 | |
| 88 | extern const device_type YM2148; |
| 89 | |
| 90 | |
| 91 | #endif |
| 92 | |