diff options
Diffstat (limited to 'src/mame/casio/pv1000.cpp')
-rw-r--r-- | src/mame/casio/pv1000.cpp | 269 |
1 files changed, 174 insertions, 95 deletions
diff --git a/src/mame/casio/pv1000.cpp b/src/mame/casio/pv1000.cpp index 3912d7662d7..5f0f7dfebae 100644 --- a/src/mame/casio/pv1000.cpp +++ b/src/mame/casio/pv1000.cpp @@ -29,10 +29,10 @@ public: protected: // device-level overrides - virtual void device_start() override; + virtual void device_start() override ATTR_COLD; // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override; + virtual void sound_stream_update(sound_stream &stream) override; private: // internal state @@ -78,19 +78,20 @@ void pv1000_sound_device::voice_w(offs_t offset, uint8_t data) case 0x03: m_ctrl = data; break; - default: - { - const uint8_t per = ~data & 0x3f; - if ((per == 0) && (m_voice[offset].period != 0)) - { - // flip output once and stall there! - m_voice[offset].val = !m_voice[offset].val; - } + default: + { + const uint8_t per = ~data & 0x3f; - m_voice[offset].period = per; + if ((per == 0) && (m_voice[offset].period != 0)) + { + // flip output once and stall there! + m_voice[offset].val = !m_voice[offset].val; } - break; + + m_voice[offset].period = per; + } + break; } } @@ -116,14 +117,12 @@ void pv1000_sound_device::voice_w(offs_t offset, uint8_t data) square1 via i/o$F8 is -6dB, square2 via i/o$F9 is -3dB, defining square3 via i/o$FA as 0dB */ -void pv1000_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) +void pv1000_sound_device::sound_stream_update(sound_stream &stream) { - auto &buffer = outputs[0]; - // Each channel has a different volume via resistor mixing which correspond to -6dB, -3dB, 0dB drops - static const int volumes[3] = {0x1000, 0x1800, 0x2000}; + static const int volumes[3] = { 0x1000, 0x1800, 0x2000 }; - for (int index = 0; index < buffer.samples(); index++) + for (int index = 0; index < stream.samples(); index++) { s32 sum = 0; @@ -160,7 +159,7 @@ void pv1000_sound_device::sound_stream_update(sound_stream &stream, std::vector< sum += m_voice[2].val * volumes[2]; } - buffer.put_int(index, sum, 32768); + stream.put_int(0, index, sum, 32768); } } @@ -175,31 +174,35 @@ public: m_maincpu(*this, "maincpu"), m_sound(*this, "pv1000_sound"), m_cart(*this, "cartslot"), - m_p_videoram(*this, "videoram"), + m_videoram(*this, "videoram"), m_gfxdecode(*this, "gfxdecode"), m_screen(*this, "screen"), - m_palette(*this, "palette") + m_palette(*this, "palette"), + m_joysticks(*this, "IN%u", 0) { } void pv1000(machine_config &config); protected: - virtual void machine_start() override; - virtual void machine_reset() override; + virtual void machine_start() override ATTR_COLD; + virtual void machine_reset() override ATTR_COLD; private: void io_w(offs_t offset, uint8_t data); uint8_t io_r(offs_t offset); void gfxram_w(offs_t offset, uint8_t data); + uint8_t joystick_r(); uint8_t m_io_regs[8]{}; - uint8_t m_fd_data = 0; emu_timer *m_irq_on_timer = nullptr; - emu_timer *m_irq_off_timer = nullptr; + emu_timer *m_busrq_on_timer = nullptr; + emu_timer *m_busrq_off_timer = nullptr; + uint8_t m_irq_enabled = 0; + uint8_t m_irq_active = 0; uint8_t m_pcg_bank = 0; uint8_t m_force_pattern = 0; - uint8_t m_fd_buffer_flag = 0; uint8_t m_border_col = 0; + uint8_t m_render_disable = 0; uint8_t * m_gfxram = nullptr; void pv1000_postload(); @@ -207,23 +210,27 @@ private: required_device<cpu_device> m_maincpu; required_device<pv1000_sound_device> m_sound; required_device<generic_slot_device> m_cart; - required_shared_ptr<uint8_t> m_p_videoram; - uint32_t screen_update_pv1000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); - TIMER_CALLBACK_MEMBER(d65010_irq_on_cb); - TIMER_CALLBACK_MEMBER(d65010_irq_off_cb); - DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load); + required_shared_ptr<uint8_t> m_videoram; required_device<gfxdecode_device> m_gfxdecode; required_device<screen_device> m_screen; required_device<palette_device> m_palette; - void pv1000_mem(address_map &map); - void pv1000_io(address_map &map); + required_ioport_array<4> m_joysticks; + + uint32_t screen_update_pv1000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + TIMER_CALLBACK_MEMBER(d65010_irq_on_cb); + TIMER_CALLBACK_MEMBER(d65010_busrq_on_cb); + TIMER_CALLBACK_MEMBER(d65010_busrq_off_cb); + DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load); + + void pv1000_mem(address_map &map) ATTR_COLD; + void pv1000_io(address_map &map) ATTR_COLD; }; void pv1000_state::pv1000_mem(address_map &map) { - //map(0x0000, 0x7fff) // mapped by the cartslot - map(0xb800, 0xbbff).ram().share("videoram"); + //map(0x0000, 0x7fff) // mapped by the cartslot + map(0xb800, 0xbbff).ram().share(m_videoram); map(0xbc00, 0xbfff).ram().w(FUNC(pv1000_state::gfxram_w)).region("gfxram", 0); } @@ -237,10 +244,8 @@ void pv1000_state::pv1000_io(address_map &map) void pv1000_state::gfxram_w(offs_t offset, uint8_t data) { - uint8_t *gfxram = memregion("gfxram")->base(); - - gfxram[offset] = data; - m_gfxdecode->gfx(1)->mark_dirty(offset/32); + m_gfxram[offset] = data; + m_gfxdecode->gfx(1)->mark_dirty(offset / 32); } @@ -254,16 +259,46 @@ void pv1000_state::io_w(offs_t offset, uint8_t data) case 0x03: //logerror("io_w offset=%02x, data=%02x (%03d)\n", offset, data , data); m_sound->voice_w(offset, data); - break; + break; + + case 0x04: + /* Bit 1 = Matrix IRQ enabled * + * Bit 0 = Prerender IRQ enabled */ + m_irq_enabled = data & 3; + m_irq_active &= m_irq_enabled; + if (m_irq_active == 0) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); + + break; case 0x05: - m_fd_data = 0xf; + // Acknowledge Prerender IRQ + if (m_irq_active & 1) + { + m_irq_active &= ~1; + if (m_irq_active == 0) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); + } break; -// case 0x06 VRAM + PCG location, always fixed at 0xb8xx + + //case 0x06 VRAM + PCG location, always fixed at 0xb8xx + case 0x07: /* ---- -xxx unknown, border color? */ - m_pcg_bank = (data & 0x20) >> 5; + m_pcg_bank = (data & 0xe0) >> 5; m_force_pattern = ((data & 0x10) >> 4); /* Dig Dug relies on this */ + m_render_disable = ((data & 0x08) >> 3); + if (m_render_disable == 0) // If we're enabling rendering mid-scanline... + { + int hpos = m_screen->hpos(); // set_raw configured so that BUSREQ is asserted from 0 to 248 + int vpos = m_screen->vpos(); + if (hpos < 248 && vpos >= 26 && vpos < 192+26) + { + m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, ASSERT_LINE); + // The de-assertion is always automatically scheduled + } + } + m_border_col = data & 7; break; } @@ -274,39 +309,39 @@ void pv1000_state::io_w(offs_t offset, uint8_t data) uint8_t pv1000_state::io_r(offs_t offset) { - uint8_t data = m_io_regs[offset]; - -// logerror("io_r offset=%02x\n", offset); - + /* Real hardware always reads MSbit set from the two readable registers; + lidnariq suspects D7 is input-only and the 128s bit is high due to the + weak pull-ups on the data bus */ switch (offset) { - case 0x04: - /* Bit 1 = 1 => Data is available in port FD */ - /* Bit 0 = 1 => Buffer at port FD is empty */ - data = 0; - data = m_fd_buffer_flag & 1; - data |= m_fd_data ? 2 : 0; - m_fd_buffer_flag &= ~1; - break; - case 0x05: - static const char *const joynames[] = { "IN0", "IN1", "IN2", "IN3" }; + case 0x04: // port $FC returns player 2 joystick and interrupt status + return 0x80 | (joystick_r() & 0x0c) | (m_irq_active & 3); // Bit 1 = Matrix IRQ, Bit 0 = Prerender IRQ - data = 0; - - for (int i = 0; i < 4; i++) - { - if (m_io_regs[5] & 1 << i) + case 0x05: // port $FD returns both joysticks and acknowledges matrix scan IRQ + if (!machine().side_effects_disabled() && (m_irq_active & 2)) { - data |= ioport(joynames[i])->read(); - m_fd_data &= ~(1 << i); + m_irq_active &= ~2; + if (m_irq_active == 0) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); } - } + return 0x80 | joystick_r(); - //m_fd_data = 0; - break; + default: + /* Ports $F8-$FB, $FE, and $FF are undriven, and pulled high by the + resistors next to the Z80 */ + return 0xff; } +} + +uint8_t pv1000_state::joystick_r() +{ + uint8_t data = 0; + + for (int i = 0; i < 4; i++) + if (BIT(m_io_regs[5], i)) + data |= m_joysticks[i]->read(); - return data; + return data & 0x0f; } @@ -353,23 +388,27 @@ DEVICE_IMAGE_LOAD_MEMBER(pv1000_state::cart_load) uint32_t pv1000_state::screen_update_pv1000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { - bitmap.fill(m_border_col); // TODO: might be either black or colored by this register + bitmap.fill(m_border_col); // border is on top and bottom + + if (m_render_disable) + return 0; for (int y = 0; y < 24; y++) { - for (int x = 2; x < 30; x++) // left-right most columns are definitely masked by the border color + for (int x = 2; x < 30; x++) // left-right most columns never even drawn, black instead { - uint16_t tile = m_p_videoram[y * 32 + x]; + uint16_t tile = m_videoram[y * 32 + x]; if (tile < 0xe0 || m_force_pattern) { tile += (m_pcg_bank << 8); - m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8, y*8); + //When we adjusted timing in set_raw so that BUSREQ is asserted during a clean rectangle, we need to compensate for that here + m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8+16, y*8+26); } else { tile -= 0xe0; - m_gfxdecode->gfx(1)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8, y*8); + m_gfxdecode->gfx(1)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8+16, y*8+26); } } } @@ -380,33 +419,63 @@ uint32_t pv1000_state::screen_update_pv1000(screen_device &screen, bitmap_ind16 /* Interrupt is triggering 16 times during vblank. */ -/* we have chosen to trigger on scanlines 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255 */ +/* They are spaced every 4 scanlines, with equal padding before and after */ TIMER_CALLBACK_MEMBER(pv1000_state::d65010_irq_on_cb) { int vpos = m_screen->vpos(); int next_vpos = vpos + 4; - if(vpos == 195) - m_fd_buffer_flag |= 1; /* TODO: exact timing of this */ + m_irq_active |= 2 & m_irq_enabled; + if (vpos == 20) + { + m_irq_active |= 1 & m_irq_enabled; + } + + if (m_irq_active) + { + m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); + } - /* Set IRQ line and schedule release of IRQ line */ - m_maincpu->set_input_line(0, ASSERT_LINE); - m_irq_off_timer->adjust(m_screen->time_until_pos(vpos, 380/2)); + /* Schedule next IRQ trigger - /* Schedule next IRQ trigger */ - if (vpos >= 255) + These numbers are an artifact of defining Y=0 as the + top scanline of the non-blanking display. */ + if (vpos >= 258) { - next_vpos = 195; + next_vpos = 0; // 262=0, 4, 8, 12, 16, 20 } - m_irq_on_timer->adjust(m_screen->time_until_pos(next_vpos, 0)); + else if (vpos >= 20 && vpos < 222) + { + next_vpos = 222; // 226, 230, 234, 238, 242, 246, 250, 254, 258 + } + m_irq_on_timer->adjust(m_screen->time_until_pos(next_vpos, 224+32)); } - -TIMER_CALLBACK_MEMBER(pv1000_state::d65010_irq_off_cb) +/* Add BUSACK-triggered scanline renderer */ +TIMER_CALLBACK_MEMBER(pv1000_state::d65010_busrq_on_cb) { - m_maincpu->set_input_line(0, CLEAR_LINE); + int vpos = m_screen->vpos(); + int next_vpos = vpos + 1; + + if (m_render_disable == 0) + { + m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, ASSERT_LINE); + } + + // schedule the de-assertion of Busreq that corresponds to the current assertion + m_busrq_off_timer->adjust(m_screen->time_until_pos(vpos, 248)); + + if (vpos >= 192 + 26) + { + next_vpos = 26; + } + m_busrq_on_timer->adjust(m_screen->time_until_pos(next_vpos, 0)); } +TIMER_CALLBACK_MEMBER(pv1000_state::d65010_busrq_off_cb) +{ + m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, CLEAR_LINE); +} void pv1000_state::pv1000_postload() { @@ -418,7 +487,8 @@ void pv1000_state::pv1000_postload() void pv1000_state::machine_start() { m_irq_on_timer = timer_alloc(FUNC(pv1000_state::d65010_irq_on_cb), this); - m_irq_off_timer = timer_alloc(FUNC(pv1000_state::d65010_irq_off_cb), this); + m_busrq_on_timer = timer_alloc(FUNC(pv1000_state::d65010_busrq_on_cb), this); + m_busrq_off_timer = timer_alloc(FUNC(pv1000_state::d65010_busrq_off_cb), this); m_gfxram = memregion("gfxram")->base(); save_pointer(NAME(m_gfxram), 0x400); @@ -433,11 +503,12 @@ void pv1000_state::machine_start() } save_item(NAME(m_io_regs)); - save_item(NAME(m_fd_data)); + save_item(NAME(m_irq_enabled)); + save_item(NAME(m_irq_active)); save_item(NAME(m_pcg_bank)); save_item(NAME(m_force_pattern)); - save_item(NAME(m_fd_buffer_flag)); save_item(NAME(m_border_col)); + save_item(NAME(m_render_disable)); machine().save().register_postload(save_prepost_delegate(FUNC(pv1000_state::pv1000_postload), this)); } @@ -446,9 +517,9 @@ void pv1000_state::machine_start() void pv1000_state::machine_reset() { m_io_regs[5] = 0; - m_fd_data = 0; - m_irq_on_timer->adjust(m_screen->time_until_pos(195, 0)); - m_irq_off_timer->adjust(attotime::never); + m_irq_on_timer->adjust(m_screen->time_until_pos(222, 256)); + m_busrq_on_timer->adjust(m_screen->time_until_pos(26, 0)); + m_busrq_off_timer->adjust(m_screen->time_until_pos(26, 248)); } @@ -457,7 +528,7 @@ static const gfx_layout pv1000_3bpp_gfx = 8, 8, /* 8x8 characters */ RGN_FRAC(1,1), 3, - { 0, 8*8, 16*8 }, + { 16*8, 8*8, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, 8*8*4 @@ -478,11 +549,19 @@ void pv1000_state::pv1000(machine_config &config) /* D65010G031 - Video & sound chip */ SCREEN(config, m_screen, SCREEN_TYPE_RASTER); - m_screen->set_raw(17897725/3, 380, 0, 256, 262, 0, 192); + m_screen->set_raw(17897725/4, 288, 32, 32+224, 262, 0, 244); + // Pixel aspect is 48/35. + // Display aspect is MAME's 4:3 default. + + // Note that this value is overridden by the user's pv1000.cfg, if present. + // 206px x 48/35(PAR) / 4/3(DAR) = 212sl + m_screen->set_default_position( + 216/206.0, 0, //216 px in storage aspect; cropped to 206 px + 244/212.0, 0); //244 sl in storage aspect; cropped to 212 sl m_screen->set_screen_update(FUNC(pv1000_state::screen_update_pv1000)); m_screen->set_palette(m_palette); - PALETTE(config, m_palette, palette_device::BGR_3BIT); + PALETTE(config, m_palette, palette_device::RGB_3BIT); GFXDECODE(config, m_gfxdecode, m_palette, gfx_pv1000); @@ -500,7 +579,7 @@ void pv1000_state::pv1000(machine_config &config) ROM_START( pv1000 ) - ROM_REGION( 0x4000, "gfxrom", ROMREGION_ERASE00 ) + ROM_REGION( 0x8000, "gfxrom", ROMREGION_ERASE00 ) ROM_REGION( 0x400, "gfxram", ROMREGION_ERASE00 ) ROM_END |