src/mess/audio/tvc.c
| r0 | r15117 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | Videotone TVC 32/64 sound emulation |
| 4 | |
| 5 | ***************************************************************************/ |
| 6 | |
| 7 | #include "emu.h" |
| 8 | #include "includes/tvc.h" |
| 9 | |
| 10 | // device type definition |
| 11 | const device_type TVC_SOUND = &device_creator<tvc_sound_device>; |
| 12 | |
| 13 | //------------------------------------------------- |
| 14 | // tvc_sound_device - constructor |
| 15 | //------------------------------------------------- |
| 16 | |
| 17 | tvc_sound_device::tvc_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 18 | : device_t(mconfig, TVC_SOUND, "TVC 64 Custom Sound", tag, owner, clock), |
| 19 | device_sound_interface(mconfig, *this) |
| 20 | { |
| 21 | } |
| 22 | |
| 23 | //------------------------------------------------- |
| 24 | // device_start - device-specific startup |
| 25 | //------------------------------------------------- |
| 26 | void tvc_sound_device::device_start() |
| 27 | { |
| 28 | m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate(), this ); |
| 29 | } |
| 30 | |
| 31 | //------------------------------------------------- |
| 32 | // device_reset - device-specific reset |
| 33 | //------------------------------------------------- |
| 34 | void tvc_sound_device::device_reset() |
| 35 | { |
| 36 | m_enabled = 0; |
| 37 | m_freq = 0; |
| 38 | m_incr = 0; |
| 39 | m_signal = 1; |
| 40 | |
| 41 | } |
| 42 | |
| 43 | //------------------------------------------------- |
| 44 | // sound_stream_update - handle update requests for |
| 45 | // our sound stream |
| 46 | //------------------------------------------------- |
| 47 | |
| 48 | void tvc_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 49 | { |
| 50 | int rate = machine().sample_rate() / 2; |
| 51 | |
| 52 | if (m_enabled && m_freq) |
| 53 | { |
| 54 | while( samples-- > 0 ) |
| 55 | { |
| 56 | *outputs[0]++ = m_signal * (m_volume * 0x0800); |
| 57 | m_incr -= m_freq; |
| 58 | while(m_incr < 0) |
| 59 | { |
| 60 | m_incr += rate; |
| 61 | m_signal = -m_signal; |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | else |
| 66 | { |
| 67 | // fill output with 0 if the sound is disabled |
| 68 | memset(outputs[0], 0, samples * sizeof(stream_sample_t)); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | |
| 73 | |
| 74 | //------------------------------------------------- |
| 75 | // ports write |
| 76 | //------------------------------------------------- |
| 77 | |
| 78 | WRITE8_MEMBER(tvc_sound_device::write) |
| 79 | { |
| 80 | m_stream->update(); |
| 81 | |
| 82 | m_ports[offset] = data; |
| 83 | |
| 84 | switch(offset) |
| 85 | { |
| 86 | case 1: |
| 87 | m_enabled = BIT(data, 4); |
| 88 | // fall through |
| 89 | |
| 90 | case 0: |
| 91 | { |
| 92 | UINT16 pitch = (m_ports[0] | (m_ports[1]<<8)) & 0x0fff; |
| 93 | m_freq = (pitch == 0x0fff) ? 0 : (int)(195312.5 / (4096 - pitch)); |
| 94 | break; |
| 95 | } |
| 96 | |
| 97 | case 2: |
| 98 | m_volume = (data>>2) & 0x0f; |
| 99 | break; |
| 100 | } |
| 101 | } |
src/mess/drivers/tvc.c
| r15116 | r15117 | |
| 5 | 5 | 12/05/2009 Skeleton driver. |
| 6 | 6 | |
| 7 | 7 | TODO: |
| 8 | | - add sound |
| 9 | 8 | - cassette load/save |
| 10 | 9 | - floppy interface |
| 11 | 10 | - expansion ports |
| r15116 | r15117 | |
| 13 | 12 | ****************************************************************************/ |
| 14 | 13 | |
| 15 | 14 | #include "emu.h" |
| 16 | | #include "cpu/z80/z80.h" |
| 17 | | #include "video/mc6845.h" |
| 18 | | #include "machine/ram.h" |
| 19 | | #include "imagedev/cartslot.h" |
| 15 | #include "includes/tvc.h" |
| 20 | 16 | |
| 21 | | |
| 22 | | class tvc_state : public driver_device |
| 17 | void tvc_state::tvc_set_mem_page(UINT8 data) |
| 23 | 18 | { |
| 24 | | public: |
| 25 | | tvc_state(const machine_config &mconfig, device_type type, const char *tag) |
| 26 | | : driver_device(mconfig, type, tag) { } |
| 27 | | |
| 28 | | UINT8 m_video_mode; |
| 29 | | UINT8 m_keyline; |
| 30 | | UINT8 m_flipflop; |
| 31 | | UINT8 m_col[4]; |
| 32 | | DECLARE_WRITE8_MEMBER(tvc_bank_w); |
| 33 | | DECLARE_WRITE8_MEMBER(tvc_video_mode_w); |
| 34 | | DECLARE_WRITE8_MEMBER(tvc_palette_w); |
| 35 | | DECLARE_WRITE8_MEMBER(tvc_keyboard_w); |
| 36 | | DECLARE_READ8_MEMBER(tvc_keyboard_r); |
| 37 | | DECLARE_READ8_MEMBER(tvc_flipflop_r); |
| 38 | | DECLARE_WRITE8_MEMBER(tvc_flipflop_w); |
| 39 | | DECLARE_READ8_MEMBER(tvc_port59_r); |
| 40 | | DECLARE_WRITE8_MEMBER(tvc_port0_w); |
| 41 | | }; |
| 42 | | |
| 43 | | |
| 44 | | |
| 45 | | static void tvc_set_mem_page(running_machine &machine, UINT8 data) |
| 46 | | { |
| 47 | | tvc_state *state = machine.driver_data<tvc_state>(); |
| 48 | | address_space *space = machine.device("maincpu")->memory().space(AS_PROGRAM); |
| 49 | | switch(data & 0x18) { |
| 19 | address_space *space = m_maincpu->memory().space(AS_PROGRAM); |
| 20 | switch(data & 0x18) |
| 21 | { |
| 50 | 22 | case 0x00 : // system ROM selected |
| 23 | if (m_bank_type[0] != TVC_ROM_BANK) |
| 24 | { |
| 51 | 25 | space->install_read_bank(0x0000, 0x3fff, "bank1"); |
| 52 | 26 | space->unmap_write(0x0000, 0x3fff); |
| 53 | | state->membank("bank1")->set_base(machine.root_device().memregion("sys")->base()); |
| 54 | | break; |
| 27 | m_bank_type[0] = TVC_ROM_BANK; |
| 28 | } |
| 29 | membank("bank1")->set_base(memregion("sys")->base()); |
| 30 | break; |
| 55 | 31 | case 0x08 : // Cart ROM selected |
| 32 | if (m_bank_type[0] != TVC_ROM_BANK) |
| 33 | { |
| 56 | 34 | space->install_read_bank(0x0000, 0x3fff, "bank1"); |
| 57 | 35 | space->unmap_write(0x0000, 0x3fff); |
| 58 | | state->membank("bank1")->set_base(machine.root_device().memregion("cart")->base()); |
| 59 | | break; |
| 36 | m_bank_type[0] = TVC_ROM_BANK; |
| 37 | } |
| 38 | membank("bank1")->set_base(memregion("cart")->base()); |
| 39 | break; |
| 60 | 40 | case 0x10 : // RAM selected |
| 41 | if (m_bank_type[0] != TVC_RAM_BANK) |
| 42 | { |
| 61 | 43 | space->install_readwrite_bank(0x0000, 0x3fff, "bank1"); |
| 62 | | state->membank("bank1")->set_base(machine.device<ram_device>(RAM_TAG)->pointer()); |
| 63 | | break; |
| 44 | m_bank_type[0] = TVC_RAM_BANK; |
| 45 | } |
| 46 | membank("bank1")->set_base(m_ram->pointer()); |
| 47 | break; |
| 64 | 48 | } |
| 65 | 49 | // Bank 2 is always RAM |
| 66 | | state->membank("bank2")->set_base(machine.device<ram_device>(RAM_TAG)->pointer() + 0x4000); |
| 50 | membank("bank2")->set_base(m_ram->pointer() + 0x4000); |
| 67 | 51 | if ((data & 0x20)==0) { |
| 68 | 52 | // Video RAM |
| 69 | | state->membank("bank3")->set_base(machine.device<ram_device>(RAM_TAG)->pointer() + 0x10000); |
| 53 | membank("bank3")->set_base(m_ram->pointer() + 0x10000); |
| 70 | 54 | } else { |
| 71 | 55 | // System RAM page 3 |
| 72 | | state->membank("bank3")->set_base(machine.device<ram_device>(RAM_TAG)->pointer() + 0x8000); |
| 56 | membank("bank3")->set_base(m_ram->pointer() + 0x8000); |
| 73 | 57 | } |
| 74 | | switch(data & 0xc0) { |
| 58 | switch(data & 0xc0) |
| 59 | { |
| 75 | 60 | case 0x00 : // Cart ROM selected |
| 61 | if (m_bank_type[1] != TVC_ROM_BANK) |
| 62 | { |
| 76 | 63 | space->install_read_bank(0xc000, 0xffff, "bank4"); |
| 77 | 64 | space->unmap_write(0xc000, 0xffff); |
| 78 | | state->membank("bank4")->set_base(machine.root_device().memregion("cart")->base()); |
| 79 | | break; |
| 65 | m_bank_type[1] = TVC_ROM_BANK; |
| 66 | } |
| 67 | membank("bank4")->set_base(memregion("cart")->base()); |
| 68 | break; |
| 80 | 69 | case 0x40 : // System ROM selected |
| 70 | if (m_bank_type[1] != TVC_ROM_BANK) |
| 71 | { |
| 81 | 72 | space->install_read_bank(0xc000, 0xffff, "bank4"); |
| 82 | 73 | space->unmap_write(0xc000, 0xffff); |
| 83 | | state->membank("bank4")->set_base(machine.root_device().memregion("sys")->base()); |
| 84 | | break; |
| 74 | m_bank_type[1] = TVC_ROM_BANK; |
| 75 | } |
| 76 | membank("bank4")->set_base(memregion("sys")->base()); |
| 77 | break; |
| 85 | 78 | case 0x80 : // RAM selected |
| 79 | if (m_bank_type[1] != TVC_RAM_BANK) |
| 80 | { |
| 86 | 81 | space->install_readwrite_bank(0xc000, 0xffff, "bank4"); |
| 87 | | state->membank("bank4")->set_base(machine.device<ram_device>(RAM_TAG)->pointer()+0xc000); |
| 88 | | break; |
| 82 | m_bank_type[1] = TVC_RAM_BANK; |
| 83 | } |
| 84 | membank("bank4")->set_base(m_ram->pointer()+0xc000); |
| 85 | break; |
| 89 | 86 | case 0xc0 : // External ROM selected |
| 87 | if (m_bank_type[1] != TVC_ROM_BANK) |
| 88 | { |
| 90 | 89 | space->install_read_bank(0xc000, 0xffff, "bank4"); |
| 91 | 90 | space->unmap_write(0xc000, 0xffff); |
| 92 | | state->membank("bank4")->set_base(machine.root_device().memregion("ext")->base()); |
| 93 | | break; |
| 94 | | |
| 91 | m_bank_type[1] = TVC_ROM_BANK; |
| 92 | } |
| 93 | membank("bank4")->set_base(memregion("ext")->base()); |
| 94 | break; |
| 95 | 95 | } |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | 98 | WRITE8_MEMBER(tvc_state::tvc_bank_w) |
| 99 | 99 | { |
| 100 | | tvc_set_mem_page(machine(), data); |
| 100 | tvc_set_mem_page(data); |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | | WRITE8_MEMBER(tvc_state::tvc_video_mode_w) |
| 104 | | { |
| 105 | | m_video_mode = data & 0x03; |
| 106 | | } |
| 107 | | |
| 108 | | |
| 109 | 103 | WRITE8_MEMBER(tvc_state::tvc_palette_w) |
| 110 | 104 | { |
| 111 | 105 | // 0 I 0 G | 0 R 0 B |
| r15116 | r15117 | |
| 129 | 123 | return ioport(keynames[m_keyline & 0x0f])->read(); |
| 130 | 124 | } |
| 131 | 125 | |
| 132 | | READ8_MEMBER(tvc_state::tvc_flipflop_r) |
| 126 | READ8_MEMBER(tvc_state::tvc_int_state_r) |
| 133 | 127 | { |
| 134 | | return m_flipflop; |
| 128 | /* |
| 129 | x--- ---- ACK from printer |
| 130 | -x-- ---- colour |
| 131 | --x- ---- cassette input |
| 132 | ---x ---- vblank or tone interrupt |
| 133 | ---- xxxx expansions interrupt |
| 134 | */ |
| 135 | |
| 136 | return 0x40 | (m_flipflop << 4); |
| 135 | 137 | } |
| 136 | 138 | |
| 137 | 139 | WRITE8_MEMBER(tvc_state::tvc_flipflop_w) |
| 138 | 140 | { |
| 139 | | m_flipflop |= 0x10; |
| 141 | // every write here clears the vblank flipflop |
| 142 | m_flipflop = 1; |
| 143 | device_set_input_line(m_maincpu, 0, CLEAR_LINE); |
| 140 | 144 | } |
| 141 | | READ8_MEMBER(tvc_state::tvc_port59_r) |
| 145 | |
| 146 | READ8_MEMBER(tvc_state::tvc_exp_id_r) |
| 142 | 147 | { |
| 148 | // TODO: read expansion slot ID |
| 143 | 149 | return 0xff; |
| 144 | 150 | } |
| 145 | 151 | |
| 146 | | WRITE8_MEMBER(tvc_state::tvc_port0_w) |
| 152 | WRITE8_MEMBER(tvc_state::tvc_border_color_w) |
| 147 | 153 | { |
| 154 | // x-x- x-x- border color (I G R B) |
| 148 | 155 | } |
| 156 | |
| 157 | |
| 158 | WRITE8_MEMBER(tvc_state::tvc_sound_w) |
| 159 | { |
| 160 | switch(offset) |
| 161 | { |
| 162 | case 1: |
| 163 | // bit 6-7 - cassette motors |
| 164 | break; |
| 165 | case 2: |
| 166 | // bit 0-1 - video mode |
| 167 | m_video_mode = data & 0x03; |
| 168 | break; |
| 169 | } |
| 170 | |
| 171 | // sound ports |
| 172 | m_sound->write(space, offset, data); |
| 173 | } |
| 174 | |
| 175 | |
| 149 | 176 | static ADDRESS_MAP_START(tvc_mem, AS_PROGRAM, 8, tvc_state ) |
| 150 | 177 | AM_RANGE(0x0000, 0x3fff) AM_RAMBANK("bank1") |
| 151 | 178 | AM_RANGE(0x4000, 0x7fff) AM_RAMBANK("bank2") |
| r15116 | r15117 | |
| 156 | 183 | static ADDRESS_MAP_START( tvc_io , AS_IO, 8, tvc_state ) |
| 157 | 184 | ADDRESS_MAP_UNMAP_HIGH |
| 158 | 185 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 159 | | AM_RANGE(0x00, 0x00) AM_WRITE(tvc_port0_w) |
| 186 | AM_RANGE(0x00, 0x00) AM_WRITE(tvc_border_color_w) |
| 160 | 187 | AM_RANGE(0x02, 0x02) AM_WRITE(tvc_bank_w) |
| 161 | 188 | AM_RANGE(0x03, 0x03) AM_WRITE(tvc_keyboard_w) |
| 162 | | AM_RANGE(0x06, 0x06) AM_WRITE(tvc_video_mode_w) |
| 189 | AM_RANGE(0x04, 0x06) AM_WRITE(tvc_sound_w) |
| 163 | 190 | AM_RANGE(0x07, 0x07) AM_WRITE(tvc_flipflop_w) |
| 164 | 191 | AM_RANGE(0x58, 0x58) AM_READ(tvc_keyboard_r) |
| 165 | | AM_RANGE(0x59, 0x59) AM_READ(tvc_flipflop_r) |
| 166 | | AM_RANGE(0x5a, 0x5a) AM_READ(tvc_port59_r) |
| 192 | AM_RANGE(0x59, 0x59) AM_READ(tvc_int_state_r) |
| 193 | AM_RANGE(0x5a, 0x5a) AM_READ(tvc_exp_id_r) |
| 167 | 194 | AM_RANGE(0x60, 0x63) AM_WRITE(tvc_palette_w) |
| 168 | 195 | AM_RANGE(0x70, 0x70) AM_DEVWRITE("crtc", mc6845_device, address_w) |
| 169 | 196 | AM_RANGE(0x71, 0x71) AM_DEVREADWRITE("crtc", mc6845_device, register_r, register_w) |
| r15116 | r15117 | |
| 269 | 296 | INPUT_PORTS_END |
| 270 | 297 | |
| 271 | 298 | |
| 272 | | static MACHINE_START(tvc) |
| 299 | void tvc_state::machine_start() |
| 273 | 300 | { |
| 274 | | tvc_state *state = machine.driver_data<tvc_state>(); |
| 275 | | int i; |
| 301 | for (int i=0; i<4; i++) |
| 302 | m_col[i] = i; |
| 276 | 303 | |
| 277 | | for (i=0; i<4; i++) |
| 278 | | state->m_col[i] = i; |
| 279 | | |
| 280 | | state->m_flipflop = 0xff; |
| 304 | m_flipflop = 0; |
| 281 | 305 | } |
| 282 | 306 | |
| 283 | | static MACHINE_RESET(tvc) |
| 307 | void tvc_state::machine_reset() |
| 284 | 308 | { |
| 285 | | tvc_state *state = machine.driver_data<tvc_state>(); |
| 286 | | memset(machine.device<ram_device>(RAM_TAG)->pointer(),0,(64+14)*1024); |
| 287 | | tvc_set_mem_page(machine, 0); |
| 288 | | state->m_video_mode = 0; |
| 309 | memset(m_ram->pointer(), 0, m_ram->size()); |
| 310 | tvc_set_mem_page(0); |
| 311 | m_video_mode = 0; |
| 289 | 312 | } |
| 290 | 313 | |
| 291 | | static VIDEO_START( tvc ) |
| 292 | | { |
| 293 | | } |
| 294 | | |
| 295 | 314 | static MC6845_UPDATE_ROW( tvc_update_row ) |
| 296 | 315 | { |
| 297 | 316 | tvc_state *state = device->machine().driver_data<tvc_state>(); |
| r15116 | r15117 | |
| 305 | 324 | for ( i = 0; i < x_count; i++ ) |
| 306 | 325 | { |
| 307 | 326 | UINT16 offset = i + (y * 64); |
| 308 | | UINT8 data = device->machine().device<ram_device>(RAM_TAG)->pointer()[ offset + 0x10000]; |
| 327 | UINT8 data = state->m_ram->pointer()[offset + 0x10000]; |
| 309 | 328 | *p++ = palette[state->m_col[BIT(data,7)]]; |
| 310 | 329 | *p++ = palette[state->m_col[BIT(data,6)]]; |
| 311 | 330 | *p++ = palette[state->m_col[BIT(data,5)]]; |
| r15116 | r15117 | |
| 322 | 341 | for ( i = 0; i < x_count; i++ ) |
| 323 | 342 | { |
| 324 | 343 | UINT16 offset = i + (y * 64); |
| 325 | | UINT8 data = device->machine().device<ram_device>(RAM_TAG)->pointer()[ offset + 0x10000]; |
| 344 | UINT8 data = state->m_ram->pointer()[offset + 0x10000]; |
| 326 | 345 | *p++ = palette[state->m_col[BIT(data,3)*2 + BIT(data,7)]]; |
| 327 | 346 | *p++ = palette[state->m_col[BIT(data,3)*2 + BIT(data,7)]]; |
| 328 | 347 | *p++ = palette[state->m_col[BIT(data,2)*2 + BIT(data,6)]]; |
| r15116 | r15117 | |
| 339 | 358 | for ( i = 0; i < x_count; i++ ) |
| 340 | 359 | { |
| 341 | 360 | UINT16 offset = i + (y * 64); |
| 342 | | UINT8 data = device->machine().device<ram_device>(RAM_TAG)->pointer()[ offset + 0x10000]; |
| 361 | UINT8 data = state->m_ram->pointer()[offset + 0x10000]; |
| 343 | 362 | UINT8 col0 = ((data & 0x80)>>4) | ((data & 0x20)>>4) | ((data & 0x08)>>1) | ((data & 0x02)>>1); |
| 344 | 363 | UINT8 col1 = ((data & 0x40)>>3) | ((data & 0x10)>>3) | (data & 0x04) | (data & 0x01); |
| 345 | 364 | *p++ = palette[col0]; |
| r15116 | r15117 | |
| 400 | 419 | static INTERRUPT_GEN( tvc_interrupt ) |
| 401 | 420 | { |
| 402 | 421 | tvc_state *state = device->machine().driver_data<tvc_state>(); |
| 403 | | state->m_flipflop &= ~0x10; |
| 404 | | device_set_input_line(device, 0, HOLD_LINE); |
| 422 | state->m_flipflop = 0; |
| 423 | device_set_input_line(device, 0, ASSERT_LINE); |
| 405 | 424 | } |
| 406 | 425 | |
| 407 | 426 | static MACHINE_CONFIG_START( tvc, tvc_state ) |
| r15116 | r15117 | |
| 410 | 429 | MCFG_CPU_PROGRAM_MAP(tvc_mem) |
| 411 | 430 | MCFG_CPU_IO_MAP(tvc_io) |
| 412 | 431 | |
| 413 | | MCFG_MACHINE_START(tvc) |
| 414 | | MCFG_MACHINE_RESET(tvc) |
| 415 | | |
| 416 | 432 | MCFG_CPU_VBLANK_INT("screen", tvc_interrupt) |
| 417 | 433 | |
| 418 | | /* video hardware */ |
| 434 | /* video hardware */ |
| 419 | 435 | MCFG_SCREEN_ADD("screen", RASTER) |
| 420 | 436 | MCFG_SCREEN_REFRESH_RATE(50) |
| 421 | 437 | MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */ |
| r15116 | r15117 | |
| 426 | 442 | MCFG_PALETTE_LENGTH( 16 ) |
| 427 | 443 | MCFG_PALETTE_INIT(tvc) |
| 428 | 444 | |
| 429 | | MCFG_MC6845_ADD("crtc", MC6845, 3125000, tvc_crtc6845_interface) // clk taken from schematics |
| 445 | MCFG_MC6845_ADD("crtc", MC6845, 3125000/2, tvc_crtc6845_interface) // clk taken from schematics |
| 430 | 446 | |
| 431 | | MCFG_VIDEO_START(tvc) |
| 432 | | |
| 433 | 447 | /* internal ram */ |
| 434 | 448 | MCFG_RAM_ADD(RAM_TAG) |
| 435 | 449 | MCFG_RAM_DEFAULT_SIZE("80K") |
| 436 | 450 | |
| 451 | /* sound hardware */ |
| 452 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 453 | MCFG_SOUND_ADD("custom", TVC_SOUND, 0) |
| 454 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.75) |
| 455 | |
| 456 | /* cartridge */ |
| 437 | 457 | MCFG_CARTSLOT_ADD("cart") |
| 438 | 458 | MCFG_CARTSLOT_EXTENSION_LIST("crt,rom,bin") |
| 439 | 459 | MCFG_CARTSLOT_NOT_MANDATORY |