trunk/src/emu/bus/a2bus/a2mcms.c
| r0 | r31131 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:R. Belmont |
| 3 | /********************************************************************* |
| 4 | |
| 5 | a2mcms.c |
| 6 | |
| 7 | Implementation of the Mountain Computer Music System. |
| 8 | This was sold standalone and also used as part of the alphaSyntauri |
| 9 | and SoundChaser systems. |
| 10 | |
| 11 | *********************************************************************/ |
| 12 | |
| 13 | #include "a2mcms.h" |
| 14 | #include "includes/apple2.h" |
| 15 | |
| 16 | // the actual sound device (a slot device can't currently also be a sound device so we keep this private here) |
| 17 | enum |
| 18 | { |
| 19 | CTRL_IRQS = 0, |
| 20 | CTRL_DMA, |
| 21 | CTRL_MASTERVOL |
| 22 | }; |
| 23 | |
| 24 | |
| 25 | class mcms_device : public device_t, public device_sound_interface |
| 26 | { |
| 27 | public: |
| 28 | // construction/destruction |
| 29 | mcms_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 30 | |
| 31 | DECLARE_WRITE8_MEMBER(voiceregs_w); |
| 32 | DECLARE_WRITE8_MEMBER(control_w); |
| 33 | UINT8 get_pen_rand(void) { m_stream->update(); return m_rand; } |
| 34 | |
| 35 | template<class _Object> static devcb_base &set_irq_cb(device_t &device, _Object wr) { return downcast<mcms_device &>(device).m_write_irq.set_callback(wr); } |
| 36 | devcb_write_line m_write_irq; |
| 37 | |
| 38 | protected: |
| 39 | // device-level overrides |
| 40 | virtual void device_start(); |
| 41 | virtual void device_reset(); |
| 42 | virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); |
| 43 | |
| 44 | virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); |
| 45 | |
| 46 | private: |
| 47 | sound_stream *m_stream; |
| 48 | address_space *m_6502space; |
| 49 | emu_timer *m_timer, *m_clrtimer; |
| 50 | bool m_enabled; |
| 51 | UINT8 m_vols[16]; |
| 52 | UINT8 m_table[16]; |
| 53 | UINT16 m_freq[16]; |
| 54 | UINT16 m_acc[16]; |
| 55 | UINT8 m_mastervol; |
| 56 | UINT8 m_rand; |
| 57 | }; |
| 58 | |
| 59 | const device_type MCMS = &device_creator<mcms_device>; |
| 60 | |
| 61 | /*************************************************************************** |
| 62 | PARAMETERS |
| 63 | ***************************************************************************/ |
| 64 | |
| 65 | //************************************************************************** |
| 66 | // GLOBAL VARIABLES |
| 67 | //************************************************************************** |
| 68 | |
| 69 | const device_type A2BUS_MCMS1 = &device_creator<a2bus_mcms1_device>; |
| 70 | const device_type A2BUS_MCMS2 = &device_creator<a2bus_mcms2_device>; |
| 71 | |
| 72 | #define ENGINE_TAG "engine" |
| 73 | |
| 74 | #define MCFG_MCMS_IRQ_CALLBACK(_cb) \ |
| 75 | devcb = &mcms_device::set_irq_cb(*device, DEVCB_##_cb); |
| 76 | |
| 77 | MACHINE_CONFIG_FRAGMENT( a2mcms ) |
| 78 | MCFG_SPEAKER_STANDARD_STEREO("mcms_l", "mcms_r") |
| 79 | |
| 80 | MCFG_DEVICE_ADD(ENGINE_TAG, MCMS, 1000000) |
| 81 | MCFG_MCMS_IRQ_CALLBACK(WRITELINE(a2bus_mcms1_device, irq_w)) |
| 82 | |
| 83 | MCFG_SOUND_ROUTE(0, "mcms_l", 1.0) |
| 84 | MCFG_SOUND_ROUTE(1, "mcms_r", 1.0) |
| 85 | MACHINE_CONFIG_END |
| 86 | |
| 87 | /*************************************************************************** |
| 88 | FUNCTION PROTOTYPES |
| 89 | ***************************************************************************/ |
| 90 | |
| 91 | //------------------------------------------------- |
| 92 | // machine_config_additions - device-specific |
| 93 | // machine configurations |
| 94 | //------------------------------------------------- |
| 95 | |
| 96 | machine_config_constructor a2bus_mcms1_device::device_mconfig_additions() const |
| 97 | { |
| 98 | return MACHINE_CONFIG_NAME( a2mcms ); |
| 99 | } |
| 100 | |
| 101 | //************************************************************************** |
| 102 | // LIVE DEVICE - Card 1 |
| 103 | //************************************************************************** |
| 104 | |
| 105 | a2bus_mcms1_device::a2bus_mcms1_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : |
| 106 | device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 107 | device_a2bus_card_interface(mconfig, *this), |
| 108 | m_mcms(*this, ENGINE_TAG) |
| 109 | { |
| 110 | } |
| 111 | |
| 112 | a2bus_mcms1_device::a2bus_mcms1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : |
| 113 | device_t(mconfig, A2BUS_MCMS1, "Mountain Computer Music System (card 1)", tag, owner, clock, "a2mcms1", __FILE__), |
| 114 | device_a2bus_card_interface(mconfig, *this), |
| 115 | m_mcms(*this, ENGINE_TAG) |
| 116 | { |
| 117 | } |
| 118 | |
| 119 | //------------------------------------------------- |
| 120 | // device_start - device-specific startup |
| 121 | //------------------------------------------------- |
| 122 | |
| 123 | void a2bus_mcms1_device::device_start() |
| 124 | { |
| 125 | // set_a2bus_device makes m_slot valid |
| 126 | set_a2bus_device(); |
| 127 | } |
| 128 | |
| 129 | void a2bus_mcms1_device::device_reset() |
| 130 | { |
| 131 | } |
| 132 | |
| 133 | // read once at c0n0 to disable 125 Hz IRQs |
| 134 | // read once at c0n1 to enable 125 Hz IRQs |
| 135 | UINT8 a2bus_mcms1_device::read_c0nx(address_space &space, UINT8 offset) |
| 136 | { |
| 137 | if (offset == 0) |
| 138 | { |
| 139 | m_mcms->control_w(space, CTRL_IRQS, 0); |
| 140 | } |
| 141 | else if (offset == 1) |
| 142 | { |
| 143 | m_mcms->control_w(space, CTRL_IRQS, 1); |
| 144 | } |
| 145 | |
| 146 | return 0xff; |
| 147 | } |
| 148 | |
| 149 | // read at Cn00: light gun in bit 7, bits 0-5 = 'random' number |
| 150 | UINT8 a2bus_mcms1_device::read_cnxx(address_space &space, UINT8 offset) |
| 151 | { |
| 152 | return m_mcms->get_pen_rand(); |
| 153 | } |
| 154 | |
| 155 | // write 0-255 to Cn00 to set the master volume |
| 156 | void a2bus_mcms1_device::write_cnxx(address_space &space, UINT8 offset, UINT8 data) |
| 157 | { |
| 158 | if (offset == 0) |
| 159 | { |
| 160 | m_mcms->control_w(space, CTRL_MASTERVOL, data); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | mcms_device *a2bus_mcms1_device::get_engine(void) |
| 165 | { |
| 166 | return m_mcms; |
| 167 | } |
| 168 | |
| 169 | WRITE_LINE_MEMBER(a2bus_mcms1_device::irq_w) |
| 170 | { |
| 171 | if (state == ASSERT_LINE) |
| 172 | { |
| 173 | raise_slot_irq(); |
| 174 | } |
| 175 | else |
| 176 | { |
| 177 | lower_slot_irq(); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | //************************************************************************** |
| 182 | // LIVE DEVICE - Card 2 |
| 183 | //************************************************************************** |
| 184 | |
| 185 | a2bus_mcms2_device::a2bus_mcms2_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : |
| 186 | device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 187 | device_a2bus_card_interface(mconfig, *this) |
| 188 | { |
| 189 | } |
| 190 | |
| 191 | a2bus_mcms2_device::a2bus_mcms2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : |
| 192 | device_t(mconfig, A2BUS_MCMS2, "Mountain Computer Music System (card 2)", tag, owner, clock, "a2mcms2", __FILE__), |
| 193 | device_a2bus_card_interface(mconfig, *this) |
| 194 | { |
| 195 | } |
| 196 | |
| 197 | //------------------------------------------------- |
| 198 | // device_start - device-specific startup |
| 199 | //------------------------------------------------- |
| 200 | |
| 201 | void a2bus_mcms2_device::device_start() |
| 202 | { |
| 203 | // set_a2bus_device makes m_slot valid |
| 204 | set_a2bus_device(); |
| 205 | |
| 206 | if (m_slot < 2) |
| 207 | { |
| 208 | fatalerror("MCMS: Card 2 must be in slot 2 or greater\n"); |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | void a2bus_mcms2_device::device_reset() |
| 213 | { |
| 214 | m_card1 = static_cast<a2bus_mcms1_device *>(m_a2bus->m_device_list[m_slot-1]); |
| 215 | m_engine = m_card1->get_engine(); |
| 216 | } |
| 217 | |
| 218 | // here to soak up false reads from indexed accesses |
| 219 | UINT8 a2bus_mcms2_device::read_c0nx(address_space &space, UINT8 offset) |
| 220 | { |
| 221 | return 0xff; |
| 222 | } |
| 223 | |
| 224 | // write once to c0n0 to disable the card (reset also disables) |
| 225 | // write twice to c0n1 to enable the card (value doesn't matter) |
| 226 | void a2bus_mcms2_device::write_c0nx(address_space &space, UINT8 offset, UINT8 data) |
| 227 | { |
| 228 | if (offset == 0) |
| 229 | { |
| 230 | m_engine->control_w(space, CTRL_DMA, 0); |
| 231 | } |
| 232 | else if (offset == 1) |
| 233 | { |
| 234 | m_engine->control_w(space, CTRL_DMA, 1); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | void a2bus_mcms2_device::write_cnxx(address_space &space, UINT8 offset, UINT8 data) |
| 239 | { |
| 240 | m_engine->voiceregs_w(space, offset, data); |
| 241 | } |
| 242 | |
| 243 | |
| 244 | /* |
| 245 | Sound device implementation |
| 246 | */ |
| 247 | |
| 248 | mcms_device::mcms_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 249 | : device_t(mconfig, MCMS, "Mountain Computer Music System engine", tag, owner, clock, "msmseng", __FILE__), |
| 250 | device_sound_interface(mconfig, *this), |
| 251 | m_write_irq(*this) |
| 252 | { |
| 253 | } |
| 254 | |
| 255 | void mcms_device::device_start() |
| 256 | { |
| 257 | m_write_irq.resolve(); |
| 258 | m_stream = machine().sound().stream_alloc(*this, 0, 2, 31250); |
| 259 | m_timer = timer_alloc(0, NULL); |
| 260 | m_clrtimer = timer_alloc(1, NULL); |
| 261 | m_enabled = false; |
| 262 | memset(m_vols, 0, sizeof(m_vols)); |
| 263 | memset(m_table, 0, sizeof(m_table)); |
| 264 | memset(m_freq, 0, sizeof(m_freq)); |
| 265 | memset(m_acc, 0, sizeof(m_acc)); |
| 266 | |
| 267 | // the card detect programs volumes and wavetable page but not freq and expects the accumulator to increment |
| 268 | for (int i = 0; i < 16; i++) |
| 269 | { |
| 270 | m_freq[i] = 0x0040; |
| 271 | } |
| 272 | |
| 273 | save_item(NAME(m_enabled)); |
| 274 | save_item(NAME(m_vols)); |
| 275 | save_item(NAME(m_table)); |
| 276 | save_item(NAME(m_freq)); |
| 277 | save_item(NAME(m_acc)); |
| 278 | save_item(NAME(m_mastervol)); |
| 279 | save_item(NAME(m_rand)); |
| 280 | } |
| 281 | |
| 282 | void mcms_device::device_reset() |
| 283 | { |
| 284 | m_write_irq(CLEAR_LINE); |
| 285 | m_timer->adjust(attotime::never); |
| 286 | m_clrtimer->adjust(attotime::never); |
| 287 | m_enabled = false; |
| 288 | } |
| 289 | |
| 290 | void mcms_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) |
| 291 | { |
| 292 | if (tid == 0) |
| 293 | { |
| 294 | m_write_irq(ASSERT_LINE); |
| 295 | // clear this IRQ in 10 cycles (?) |
| 296 | m_clrtimer->adjust(attotime::from_usec(10), 0); |
| 297 | } |
| 298 | else if (tid == 1) |
| 299 | { |
| 300 | m_write_irq(CLEAR_LINE); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | void mcms_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 305 | { |
| 306 | stream_sample_t *outL, *outR; |
| 307 | int i, v; |
| 308 | UINT16 wptr; |
| 309 | INT8 sample; |
| 310 | INT32 mixL, mixR; |
| 311 | |
| 312 | outL = outputs[1]; |
| 313 | outR = outputs[0]; |
| 314 | |
| 315 | if (m_enabled) |
| 316 | { |
| 317 | for (i = 0; i < samples; i++) |
| 318 | { |
| 319 | mixL = mixR = 0; |
| 320 | |
| 321 | for (v = 0; v < 16; v++) |
| 322 | { |
| 323 | m_acc[v] += m_freq[v]; |
| 324 | wptr = (m_table[v]<<8) | (m_acc[v]>>8); |
| 325 | m_rand = (m_acc[v]>>8) & 0x1f; |
| 326 | |
| 327 | sample = (m_6502space->read_byte(wptr) ^ 0x80); |
| 328 | if (v & 1) |
| 329 | { |
| 330 | mixL += sample * m_vols[v]; |
| 331 | } |
| 332 | else |
| 333 | { |
| 334 | mixR += sample * m_vols[v]; |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | outL[i] = (mixL * m_mastervol)>>9; |
| 339 | outR[i] = (mixR * m_mastervol)>>9; |
| 340 | } |
| 341 | } |
| 342 | else |
| 343 | { |
| 344 | for (i = 0; i < samples; i++) |
| 345 | { |
| 346 | outL[i] = outR[i] = 0; |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | WRITE8_MEMBER(mcms_device::voiceregs_w) |
| 352 | { |
| 353 | m_stream->update(); |
| 354 | if (offset >= 0x20) |
| 355 | { |
| 356 | if (offset & 1) // amp |
| 357 | { |
| 358 | m_vols[(offset-0x21)/2] = data; |
| 359 | } |
| 360 | else // wavetable page |
| 361 | { |
| 362 | m_table[(offset-0x20)/2] = data; |
| 363 | } |
| 364 | } |
| 365 | else |
| 366 | { |
| 367 | if (offset & 1) // freq L |
| 368 | { |
| 369 | if (offset == 0x1f) |
| 370 | { |
| 371 | m_freq[0] &= 0xff00; |
| 372 | m_freq[0] |= data; |
| 373 | } |
| 374 | else |
| 375 | { |
| 376 | int reg = (offset/2)+1; |
| 377 | m_freq[reg] &= 0xff00; |
| 378 | m_freq[reg] |= data; |
| 379 | } |
| 380 | } |
| 381 | else // freq H |
| 382 | { |
| 383 | int reg = (offset/2); |
| 384 | m_freq[reg] &= 0x00ff; |
| 385 | m_freq[reg] |= (data<<8); |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | WRITE8_MEMBER(mcms_device::control_w) |
| 391 | { |
| 392 | // keep the space (TODO: we need to define a formal DMA mechanism from machine/apple2 out to the slots) |
| 393 | m_6502space = &space; |
| 394 | |
| 395 | m_stream->update(); |
| 396 | |
| 397 | switch (offset) |
| 398 | { |
| 399 | case CTRL_IRQS: |
| 400 | if (data == 0) |
| 401 | { |
| 402 | m_timer->adjust(attotime::never); |
| 403 | } |
| 404 | else |
| 405 | { |
| 406 | m_timer->adjust(attotime::zero, 0, attotime::from_hz(125)); |
| 407 | } |
| 408 | break; |
| 409 | |
| 410 | case CTRL_DMA: |
| 411 | m_enabled = (data == 0) ? false : true; |
| 412 | break; |
| 413 | |
| 414 | case CTRL_MASTERVOL: |
| 415 | m_mastervol = data; |
| 416 | break; |
| 417 | } |
| 418 | } |
| 419 | |
trunk/src/emu/bus/a2bus/a2mcms.h
| r0 | r31131 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:R. Belmont |
| 3 | /********************************************************************* |
| 4 | |
| 5 | a2mcms.h |
| 6 | |
| 7 | Implementation of the Mountain Computer Music System. |
| 8 | This was sold standalone and also used as part of the alphaSyntauri |
| 9 | and SoundChaser systems. |
| 10 | |
| 11 | *********************************************************************/ |
| 12 | |
| 13 | #ifndef __A2BUS_MCMS__ |
| 14 | #define __A2BUS_MCMS__ |
| 15 | |
| 16 | #include "emu.h" |
| 17 | #include "a2bus.h" |
| 18 | |
| 19 | //************************************************************************** |
| 20 | // TYPE DEFINITIONS |
| 21 | //************************************************************************** |
| 22 | |
| 23 | // forward declaration |
| 24 | class mcms_device; |
| 25 | |
| 26 | // card 1 |
| 27 | class a2bus_mcms1_device: |
| 28 | public device_t, |
| 29 | public device_a2bus_card_interface |
| 30 | { |
| 31 | public: |
| 32 | // construction/destruction |
| 33 | a2bus_mcms1_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); |
| 34 | a2bus_mcms1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 35 | |
| 36 | // optional information overrides |
| 37 | virtual machine_config_constructor device_mconfig_additions() const; |
| 38 | |
| 39 | // comms from card 2 (oscillator parameter writes) |
| 40 | mcms_device *get_engine(void); |
| 41 | |
| 42 | DECLARE_WRITE_LINE_MEMBER(irq_w); |
| 43 | |
| 44 | required_device<mcms_device> m_mcms; |
| 45 | |
| 46 | protected: |
| 47 | virtual void device_start(); |
| 48 | virtual void device_reset(); |
| 49 | |
| 50 | // overrides of standard a2bus slot functions |
| 51 | virtual UINT8 read_c0nx(address_space &space, UINT8 offset); |
| 52 | virtual UINT8 read_cnxx(address_space &space, UINT8 offset); |
| 53 | virtual void write_cnxx(address_space &space, UINT8 offset, UINT8 data); |
| 54 | virtual bool take_c800() { return false; } |
| 55 | }; |
| 56 | |
| 57 | // card 2 |
| 58 | class a2bus_mcms2_device: |
| 59 | public device_t, |
| 60 | public device_a2bus_card_interface |
| 61 | { |
| 62 | public: |
| 63 | // construction/destruction |
| 64 | a2bus_mcms2_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); |
| 65 | a2bus_mcms2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 66 | |
| 67 | protected: |
| 68 | virtual void device_start(); |
| 69 | virtual void device_reset(); |
| 70 | |
| 71 | // overrides of standard a2bus slot functions |
| 72 | virtual UINT8 read_c0nx(address_space &space, UINT8 offset); |
| 73 | virtual void write_c0nx(address_space &space, UINT8 offset, UINT8 data); |
| 74 | virtual void write_cnxx(address_space &space, UINT8 offset, UINT8 data); |
| 75 | virtual bool take_c800() { return false; } |
| 76 | |
| 77 | private: |
| 78 | a2bus_mcms1_device *m_card1; // card 1 for passthrough |
| 79 | class mcms_device *m_engine; |
| 80 | }; |
| 81 | |
| 82 | // device type definition |
| 83 | extern const device_type A2BUS_MCMS1; |
| 84 | extern const device_type A2BUS_MCMS2; |
| 85 | |
| 86 | #endif /* __A2BUS_MCMS__ */ |