summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author myrtle <gatecat@ds0.me>2026-05-08 13:27:01 +0200
committer GitHub <noreply@github.com>2026-05-08 07:27:01 -0400
commitc2ffdd5ec1b13c28c4e36049eee879d1b3fb7212 (patch)
treeb11ad51e9f1b420d85aea219528ccb4bf679a5d4
parentbcc561afdf812eff00e2d7c1a069aec2a2c66694 (diff)
pixtermu: Make the system boot and LCD/touchscreen work (#15220)
* pixtermu: Split up the APB peripherals * pixtermu: Implement the LH79524 timer * pixtermu: Implement dummy SSP * pixtermu: Make it boot and LCD/TS work
-rw-r--r--scripts/src/machine.lua12
-rw-r--r--src/devices/machine/lh79524_timer.cpp168
-rw-r--r--src/devices/machine/lh79524_timer.h49
-rw-r--r--src/mame/mattel/pixtermu.cpp353
4 files changed, 553 insertions, 29 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index d84f3d01041..9dcb61bb16d 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -2182,6 +2182,18 @@ end
---------------------------------------------------
--
+--@src/devices/machine/lh79524_timer.h,MACHINES["LH79524"] = true
+---------------------------------------------------
+
+if MACHINES["LH79524"] then
+ files {
+ MAME_DIR .. "src/devices/machine/lh79524_timer.cpp",
+ MAME_DIR .. "src/devices/machine/lh79524_timer.h",
+ }
+end
+
+---------------------------------------------------
+--
--@src/devices/machine/locomo.h,MACHINES["LOCOMO"] = true
---------------------------------------------------
diff --git a/src/devices/machine/lh79524_timer.cpp b/src/devices/machine/lh79524_timer.cpp
new file mode 100644
index 00000000000..d0656f010e5
--- /dev/null
+++ b/src/devices/machine/lh79524_timer.cpp
@@ -0,0 +1,168 @@
+// license:BSD-3-Clause
+// copyright-holders:Myrtle Shah
+/*****************************************************************************
+
+ LH79524 Timer
+
+*****************************************************************************/
+
+#include "emu.h"
+#include "lh79524_timer.h"
+
+
+DEFINE_DEVICE_TYPE(LH79524_TIMER, lh79524_timer_device, "lh79524_timer", "LH79524 Timer")
+
+
+lh79524_timer_device::lh79524_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, LH79524_TIMER, tag, owner, clock)
+ , m_irq_cb(*this) {
+}
+
+void lh79524_timer_device::set_timer_index(int index) {
+ timer_index = index;
+}
+
+
+void lh79524_timer_device::device_start() {
+ m_tick_timer = timer_alloc(FUNC(lh79524_timer_device::timer_update), this);
+
+ save_item(NAME(m_control));
+ save_item(NAME(m_cap_control));
+ save_item(NAME(m_inten));
+ save_item(NAME(m_status));
+ save_item(NAME(m_cnt));
+ save_item(NAME(m_cmp0));
+ save_item(NAME(m_cmp1));
+}
+
+void lh79524_timer_device::device_reset() {
+ m_control = 0;
+ m_cap_control = 0;
+ m_inten = 0;
+ m_status = 0;
+ m_cnt = 0;
+ m_cmp0 = 0;
+ m_cmp1 = 0;
+
+ m_tick_timer->adjust(attotime::never);
+
+ update_interrupt();
+}
+
+void lh79524_timer_device::update_interrupt() {
+ if ((m_status & 0x7) & (m_inten & 0x7)) {
+ m_irq_cb(ASSERT_LINE);
+ } else {
+ m_irq_cb(CLEAR_LINE);
+ }
+
+}
+
+void lh79524_timer_device::device_clock_changed() {
+ if (BIT(m_control, 1)) {
+ uint32_t hclk_div_value = (m_control >> 2) & 0x7;
+ m_tick_timer->adjust(attotime::from_hz(clock() / (2U << hclk_div_value)), 0, attotime::from_hz(clock() / (2U << hclk_div_value)));
+ } else {
+ m_tick_timer->adjust(attotime::never);
+ }
+}
+
+TIMER_CALLBACK_MEMBER(lh79524_timer_device::timer_update) {
+ if (!BIT(m_control, 1))
+ return;
+
+ bool tc = (timer_index == 0) ? BIT(m_cap_control, 14) : BIT(m_control, 13);
+
+ uint16_t limit = tc ? m_cmp1 : 0xffff;
+
+ if (m_cnt == m_cmp0) {
+ m_status |= 0x0002;
+ }
+
+ if (m_cnt == m_cmp1) {
+ m_status |= 0x0004;
+ }
+
+ if (m_cnt == limit) {
+ m_cnt = 0;
+ m_status |= 0x0001;
+ } else {
+ m_cnt++;
+ }
+
+ update_interrupt();
+}
+
+uint32_t lh79524_timer_device::read(offs_t offset, uint32_t mem_mask) {
+ offs_t addr = offset << 2;
+ if (timer_index >= 1 && addr >= 0x04) {
+ addr += 0x04;
+ }
+ switch (addr) {
+ case 0x00:
+ return m_control;
+ case 0x04:
+ return m_cap_control;
+ case 0x08:
+ return m_inten;
+ case 0x0c:
+ return m_status;
+ case 0x10:
+ return m_cnt;
+ case 0x14:
+ return m_cmp0;
+ case 0x18:
+ return m_cmp1;
+ default:
+ logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2);
+ }
+
+ return 0;
+}
+
+void lh79524_timer_device::write(offs_t offset, uint32_t data, uint32_t mem_mask) {
+ offs_t addr = offset << 2;
+ if (timer_index >= 1 && addr >= 0x04) {
+ addr += 0x04;
+ }
+ switch (addr) {
+ case 0x00:
+ if (data & 0x1) {
+ m_cnt = 0;
+ data &= ~0x1;
+ }
+ COMBINE_DATA(&m_control);
+ lh79524_timer_device::device_clock_changed();
+ break;
+ case 0x04:
+ COMBINE_DATA(&m_cap_control);
+ break;
+ case 0x08:
+ COMBINE_DATA(&m_inten);
+ update_interrupt();
+ break;
+ case 0x0c:
+ if (BIT(mem_mask, 0) && BIT(data, 0)) {
+ m_status &= ~0x1;
+ }
+ if (BIT(mem_mask, 1) && BIT(data, 1)) {
+ m_status &= ~0x2;
+ }
+ if (BIT(mem_mask, 2) && BIT(data, 2)) {
+ m_status &= ~0x4;
+ }
+ update_interrupt();
+ break;
+ case 0x10:
+ COMBINE_DATA(&m_cnt);
+ break;
+ case 0x14:
+ COMBINE_DATA(&m_cmp0);
+ break;
+ case 0x18:
+ COMBINE_DATA(&m_cmp1);
+ break;
+ default:
+ logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data);
+ }
+}
diff --git a/src/devices/machine/lh79524_timer.h b/src/devices/machine/lh79524_timer.h
new file mode 100644
index 00000000000..b6959a83bf6
--- /dev/null
+++ b/src/devices/machine/lh79524_timer.h
@@ -0,0 +1,49 @@
+// license:BSD-3-Clause
+// copyright-holders:Myrtle Shah
+
+
+#ifndef MAME_MACHINE_LH79524_TIMER_H
+#define MAME_MACHINE_LH79524_TIMER_H
+
+#pragma once
+
+class lh79524_timer_device : public device_t
+{
+public:
+ lh79524_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ void set_timer_index(int index);
+
+ void write(offs_t offset, uint32_t data, uint32_t mem_mask);
+ uint32_t read(offs_t offset, uint32_t mem_mask);
+ auto irq_cb() { return m_irq_cb.bind(); }
+
+protected:
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+ virtual void device_clock_changed() override;
+
+ TIMER_CALLBACK_MEMBER(timer_update);
+
+private:
+ void update_interrupt();
+
+ devcb_write_line m_irq_cb;
+
+ emu_timer *m_tick_timer;
+
+ int timer_index;
+
+ uint32_t m_control;
+ uint32_t m_cap_control;
+ uint32_t m_inten;
+ uint32_t m_status;
+ uint32_t m_cnt;
+ uint32_t m_cmp0;
+ uint32_t m_cmp1;
+
+ // TODO: compare
+};
+
+DECLARE_DEVICE_TYPE(LH79524_TIMER, lh79524_timer_device)
+
+#endif // MAME_MACHINE_LH79524_TIMER_H
diff --git a/src/mame/mattel/pixtermu.cpp b/src/mame/mattel/pixtermu.cpp
index 46e89ca28f6..f66559c727b 100644
--- a/src/mame/mattel/pixtermu.cpp
+++ b/src/mame/mattel/pixtermu.cpp
@@ -2,12 +2,12 @@
// copyright-holders:QUFB
/******************************************************************************
- Skeleton driver for Pixter Multi-Media.
+ WIP driver for Pixter Multi-Media.
- Currently hangs after reset:
-
- - If executing boot ROM, with boot configuration set to load NOR Flash or SRAM, it will reach an infinite loop at 0x1f0;
- - If executing nCS1 ROM, it will wait indefinitely due to unimplemented timer controls;
+ - The boot ROM is ignored, boot directly from NOR flash
+ - The buttons are not implemented, only the touchscreen input
+ - Audio and SSP DMA is not implemented, the SSP DMA interrupt is fired on Vblank to maintain progress
+ - The soft buttons below the touchscreen are just drawn as rectangles
References:
@@ -48,8 +48,14 @@
#include "bus/generic/slot.h"
#include "cpu/arm7/arm7.h"
+#include "machine/lh79524_timer.h"
+#include "machine/vic_pl192.h"
+
#include "softlist_dev.h"
+#include "emupal.h"
+#include "screen.h"
+
namespace {
class pixter_multimedia_state : public driver_device
@@ -57,39 +63,84 @@ class pixter_multimedia_state : public driver_device
public:
pixter_multimedia_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
+ , m_palette(*this, "palette")
+ , m_screen(*this, "screen")
+ , m_touch(*this, { "TOUCHX", "TOUCHY", "TOUCH" })
, m_cart(*this, "cartslot")
, m_maincpu(*this, "maincpu")
, m_ndcs0(*this, "ndcs0")
, m_internal_sram(*this, "internal_sram")
- , m_apb(*this, "apb", 0x27000, ENDIANNESS_LITTLE)
+ , m_timers(*this, "timer%u", 0U)
+ , m_vic(*this, "vic")
+ , m_clkrst(*this, "clkrst", 0x1000, ENDIANNESS_LITTLE)
+ , m_bootctl(*this, "bootctl", 0x1000, ENDIANNESS_LITTLE)
+ , m_lcdc(*this, "lcdc", 0x1000, ENDIANNESS_LITTLE)
+ , m_adc(*this, "adc", 0x100, ENDIANNESS_LITTLE)
+ , m_dma(*this, "dma", 0x100, ENDIANNESS_LITTLE)
, m_remap_view(*this, "remap")
{ }
void pixter_multimedia(machine_config &config);
private:
- // Remap Control, mapped at 0xfffe2008, offset 0x22008/4
- static inline constexpr uint32_t APB_REMAP = 34818;
- // Power-up Boot Configuration, mapped at 0xfffe6000, offset 0x26000/4
- static inline constexpr uint32_t APB_PBC = 38912;
- // nCS1 Override, mapped at 0xfffe6004, offset 0x26004/4
- static inline constexpr uint32_t APB_CS1OV = 38913;
- // External Peripheral Mapping, mapped at 0xfffe6008, offset 0x26008/4
- static inline constexpr uint32_t APB_EPM = 38914;
+ // Remap Control, mapped at 0xfffe2008, offset 0x0008/4
+ static inline constexpr uint32_t CLKRST_REMAP = 2;
+ // Power-up Boot Configuration, mapped at 0xfffe6000, offset 0x0000/4
+ static inline constexpr uint32_t BOOTCTL_PBC = 0;
+ // nCS1 Override, mapped at 0xfffe6004, offset 0x0004/4
+ static inline constexpr uint32_t BOOTCTL_CS1OV = 1;
+ // External Peripheral Mapping, mapped at 0xfffe6008, offset 0x0008/4
+ static inline constexpr uint32_t BOOTCTL_EPM = 2;
virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
DEVICE_IMAGE_LOAD_MEMBER(cart_load);
void arm7_map(address_map &map) ATTR_COLD;
- void apb_bridge_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+ void clkrst_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+ void bootctl_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+
+ void lcdc_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+ uint32_t lcdc_r(offs_t offset);
+
+ uint32_t adc_r(offs_t offset);
+ void adc_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+
+ void dma_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+
+ uint32_t ssp_r(offs_t offset);
+ void ssp_w(offs_t offset, uint32_t data, uint32_t mem_mask);
+
+ uint32_t gpioab_r(offs_t offset);
+ uint32_t gpiogh_r(offs_t offset);
+ uint32_t gpioij_r(offs_t offset);
+
+
+ int adc_count;
+
void apb_remap(uint32_t data);
+ uint32_t screen_update_pixtermu(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void screen_vblank(int state);
+
+ required_device<palette_device> m_palette;
+ required_device<screen_device> m_screen;
+
+ required_ioport_array<3> m_touch;
+
required_device<generic_slot_device> m_cart;
required_device<arm7_cpu_device> m_maincpu;
required_shared_ptr<uint32_t> m_ndcs0;
required_shared_ptr<uint32_t> m_internal_sram;
- memory_share_creator<uint32_t> m_apb;
+ required_device_array<lh79524_timer_device, 3> m_timers;
+ required_device<vic_pl190_device> m_vic;
+
+ memory_share_creator<uint32_t> m_clkrst;
+ memory_share_creator<uint32_t> m_bootctl;
+ memory_share_creator<uint32_t> m_lcdc;
+ memory_share_creator<uint32_t> m_adc;
+ memory_share_creator<uint32_t> m_dma;
+
memory_view m_remap_view;
};
@@ -97,7 +148,7 @@ void pixter_multimedia_state::apb_remap(uint32_t data)
{
// User's Guide - 1.6 Memory Interface Architecture
if (data == 0) {
- m_remap_view.select((m_apb[APB_PBC] & 0b0100) && (m_apb[APB_CS1OV] & 0b1) ? 0 : 3);
+ m_remap_view.select((m_bootctl[BOOTCTL_PBC] & 0b0100) && (m_bootctl[BOOTCTL_CS1OV] & 0b1) ? 0 : 3);
} else if (data < 3) {
m_remap_view.select(data);
} else {
@@ -122,12 +173,15 @@ void pixter_multimedia_state::machine_start()
void pixter_multimedia_state::machine_reset()
{
- m_apb[APB_PBC] = 0b0000; // Boot from NOR Flash or SRAM, 16-bit data bus width, nBLEx LOW for reads
- m_apb[APB_CS1OV] = 0b0; // nCS1 is routed for normal operation
- m_apb[APB_EPM] = 0b1111; // All external devices are accessible following reset
+ m_bootctl[BOOTCTL_PBC] = 0b0000; // Boot from NOR Flash or SRAM, 16-bit data bus width, nBLEx LOW for reads
+ m_bootctl[BOOTCTL_CS1OV] = 0b0; // nCS1 is routed for normal operation
+ m_bootctl[BOOTCTL_EPM] = 0b1111; // All external devices are accessible following reset
+
+ m_clkrst[CLKRST_REMAP] = 0b00; // Map nCS1
+
+ adc_count = 0;
- m_apb[APB_REMAP] = 0b00; // Map nCS1
- apb_remap(m_apb[APB_REMAP]);
+ apb_remap(m_clkrst[CLKRST_REMAP]);
}
DEVICE_IMAGE_LOAD_MEMBER(pixter_multimedia_state::cart_load)
@@ -197,28 +251,241 @@ void pixter_multimedia_state::arm7_map(address_map &map)
// Boot ROM
map(0x8000'0000, 0x8000'1fff).rom().region("bootrom", 0);
- // APB Bridge
- map(0xfffc'0000, 0xfffe'6fff).ram().share("apb").w(FUNC(pixter_multimedia_state::apb_bridge_w));
+ // APB Peripherals
+ // ADC
+ map(0xfffc'3000, 0xfffc'30ff).ram().share("adc").r(FUNC(pixter_multimedia_state::adc_r)).w(FUNC(pixter_multimedia_state::adc_w));
+ // Timers
+ map(0xfffc'4000, 0xfffc'402f).rw(m_timers[0], FUNC(lh79524_timer_device::read), FUNC(lh79524_timer_device::write));
+ map(0xfffc'4030, 0xfffc'404f).rw(m_timers[1], FUNC(lh79524_timer_device::read), FUNC(lh79524_timer_device::write));
+ map(0xfffc'4050, 0xfffc'406f).rw(m_timers[2], FUNC(lh79524_timer_device::read), FUNC(lh79524_timer_device::write));
+ // SSP
+ map(0xfffc'6000, 0xfffc'602f).r(FUNC(pixter_multimedia_state::ssp_r)).w(FUNC(pixter_multimedia_state::ssp_w));
+
+ // GPIO I/J
+ map(0xfffd'b000, 0xfffd'b00f).r(FUNC(pixter_multimedia_state::gpioij_r));
+ // GPIO G/H
+ map(0xfffd'c000, 0xfffd'c00f).r(FUNC(pixter_multimedia_state::gpiogh_r));
+ // GPIO A/B
+ map(0xfffd'f000, 0xfffd'f00f).r(FUNC(pixter_multimedia_state::gpioab_r));
+ // DMA
+ map(0xfffe'1000, 0xfffe'10ff).ram().share("dma").w(FUNC(pixter_multimedia_state::dma_w));
+
+ // Reset Clock and Power Controller
+ map(0xfffe'2000, 0xfffe'2fff).ram().share("clkrst").w(FUNC(pixter_multimedia_state::clkrst_w));
+ // Boot Controller
+ map(0xfffe'6000, 0xfffe'6fff).ram().share("bootctl").w(FUNC(pixter_multimedia_state::bootctl_w));
+
+
// External Memory Control
map(0xffff'1000, 0xffff'1fff).ram();
// Color LCD Control
- map(0xffff'4000, 0xffff'4fff).ram();
+ map(0xffff'4000, 0xffff'4fff).ram().share("lcdc").w(FUNC(pixter_multimedia_state::lcdc_w)).r(FUNC(pixter_multimedia_state::lcdc_r));
// USB Device
map(0xffff'5000, 0xffff'5fff).ram();
// Interrupt Vector Control
- map(0xffff'f000, 0xffff'ffff).ram();
+ map(0xffff'f000, 0xffff'ffff).m(m_vic, FUNC(vic_pl190_device::map)); // interrupt controller
}
-void pixter_multimedia_state::apb_bridge_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+void pixter_multimedia_state::clkrst_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
- if (offset == m_apb[APB_REMAP]) {
+ if (offset == CLKRST_REMAP) {
apb_remap(data);
}
- COMBINE_DATA(&m_apb[offset]);
+ COMBINE_DATA(&m_clkrst[offset]);
+}
+
+void pixter_multimedia_state::bootctl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ COMBINE_DATA(&m_bootctl[offset]);
+}
+
+
+void pixter_multimedia_state::lcdc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ offs_t addr = offset << 2;
+ if (addr >= 0x200 && addr <= 0x3fc) {
+ unsigned base = ((addr - 0x200) >> 2) * 2;
+ for (int j = 0; j < 2; j++) {
+ uint16_t ibgr1555 = data >> (16 * j);
+ uint16_t i = ((ibgr1555 >> 15) & 0x1) << 2;
+ uint16_t b = ((ibgr1555 >> 10) & 0x1F) << 3 | i;
+ uint16_t g = ((ibgr1555 >> 5) & 0x1F) << 3 | i;
+ uint16_t r = ((ibgr1555 >> 0) & 0x1F) << 3 | i;
+
+ m_palette->set_pen_color(base + j, r, g, b);
+ }
+ }
+ COMBINE_DATA(&m_lcdc[offset]);
+}
+
+uint32_t pixter_multimedia_state::lcdc_r(offs_t offset)
+{
+ return m_lcdc[offset];
+}
+
+uint32_t pixter_multimedia_state::ssp_r(offs_t offset)
+{
+ switch (offset << 2) {
+ case 0x0C: // status
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+void pixter_multimedia_state::ssp_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+}
+
+uint32_t pixter_multimedia_state::adc_r(offs_t offset)
+{
+ switch (offset << 2) {
+ case 0x08: { // result
+ if (adc_count > 0)
+ --adc_count;
+ unsigned index = ((m_adc[0x10 >> 2] & 0xF) - adc_count);
+ unsigned hc = m_adc[(0x24 >> 2) + index], lc = m_adc[(0x64 >> 2) + index];
+ unsigned result = 0x3ff;
+ if (hc == 0xFF80 && lc == 0x1080) { // touch or no touch
+ result = m_touch[2]->read() ? 0 : 0x3ff;
+ } else if (hc == 0xFFA0 && lc == 0x1080) {
+ result = 0;
+ } else if (hc == 0xFF91 && lc == 0x0015) { // Y position
+ result = (m_touch[1]->read() * 1023) / 176;
+ } else if (hc == 0xFF82 && lc == 0x00A2) { // X position
+ result = 1023 - ((8 + m_touch[0]->read()) * 1023) / 176;
+ }
+ // At least one extra channel is used for battery
+ return (result << 6) | (index & 0xF);
+ }
+ case 0x1C: // IRQ status
+ return (m_touch[2]->read() ? 8 : 0) | 4;
+ case 0x20: // FIFO status
+ if (adc_count == 16)
+ return 8;
+ else if (adc_count == 0)
+ return 4;
+ else
+ return 0;
+ default:
+ return m_adc[offset];
+ }
+}
+
+void pixter_multimedia_state::adc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ switch (offset << 2) {
+ case 0x14:
+ if(data & 0x4) { // start conversion
+ adc_count = (m_adc[0x10 >> 2] & 0xF) + 1;
+ };
+ return;
+ default:
+ break;
+ }
+ COMBINE_DATA(&m_adc[offset]);
+}
+
+uint32_t pixter_multimedia_state::gpioab_r(offs_t offset)
+{
+ switch (offset << 2) {
+ case 0x04: // port B data
+ return 0xFF;
+ default:
+ return 0;
+ }
+}
+
+uint32_t pixter_multimedia_state::gpiogh_r(offs_t offset)
+{
+ // Some of these GPIO are likely used for the buttons, which aren't implemented right now
+ switch (offset << 2) {
+ case 0x00: // port G data
+ return 0x00;
+ case 0x04: // port H data
+ return 0x00;
+ default:
+ return 0;
+ }
+}
+
+
+uint32_t pixter_multimedia_state::gpioij_r(offs_t offset)
+{
+ switch (offset << 2) {
+ case 0x00: // port I data
+ return 0xFF;
+ default:
+ return 0;
+ }
+}
+
+void pixter_multimedia_state::dma_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ switch (offset << 2) {
+ case 0xF4: // interrupt clear
+ if (data & 0x2) {
+ m_dma[0xF8 >> 2] &= ~0x2;
+ }
+ break;
+ default:
+ COMBINE_DATA(&m_dma[offset]);
+ break;
+ }
+ m_vic->irq_w<21>(((m_dma[0xF0 >> 2] & 0x2) & (m_dma[0xF8 >> 2] & 0x2)) ? ASSERT_LINE : CLEAR_LINE);
+}
+
+uint32_t pixter_multimedia_state::screen_update_pixtermu(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (!BIT(m_lcdc[0x01C>>2], 1))
+ return 0;
+ const uint32_t base = (m_lcdc[0x010>>2] >> 2) & 0xfffff;
+
+ for (int y = 0; y < 160; y++) {
+ for (int x = 0; x < 160; x++) {
+ int pixel = (y * 162 + x + 1);
+ uint8_t ind = m_ndcs0[base + pixel / 4] >> ((pixel % 4) * 8);
+ bitmap.pix(y, x) = ind;
+ }
+ }
+
+ // HACK: draw the soft buttons where the touchscreen extends below the LCD as white squares
+ // The label that is used in the physical hardware probably needs to be scanned
+ for (int i = 0; i < 9; i++) {
+ int x0 = (160 * i) / 9;
+ int y0 = 160;
+ for (int y = (y0 + 2); y <= (y0 + 14); y++) {
+ for (int x = (x0 + 2); x <= (x0 + 14); x++) {
+ bitmap.pix(y, x) = 0xFF;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void pixter_multimedia_state::screen_vblank(int state)
+{
+ // Triggering this on vblank is definitely wrong, but it's the best place to set this right now...
+ // The correct solution is to implement the SSP and DMA that drives the audio DAC. But without
+ // this interrupt the system won't run.
+ m_dma[0xF8 >> 2] |= 0x2;
+ m_vic->irq_w<21>(((m_dma[0xF0 >> 2] & 0x2) & (m_dma[0xF8 >> 2] & 0x2)) ? ASSERT_LINE : CLEAR_LINE);
}
static INPUT_PORTS_START( pixter_multimedia )
+
+ PORT_START("TOUCHX")
+ PORT_BIT(0x3ff, 80, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0,159) PORT_SENSITIVITY(45) PORT_KEYDELTA(13)
+
+ PORT_START("TOUCHY")
+ PORT_BIT(0x3ff, 80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0,171) PORT_SENSITIVITY(45) PORT_KEYDELTA(13)
+
+ PORT_START("TOUCH")
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Touch")
+ PORT_BIT(0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED)
+
INPUT_PORTS_END
void pixter_multimedia_state::pixter_multimedia(machine_config &config)
@@ -227,12 +494,40 @@ void pixter_multimedia_state::pixter_multimedia(machine_config &config)
ARM7(config, m_maincpu, 76'205'000);
m_maincpu->set_addrmap(AS_PROGRAM, &pixter_multimedia_state::arm7_map);
+ PL190_VIC(config, m_vic, 0);
+ m_vic->out_irq_cb().set_inputline(m_maincpu, arm7_cpu_device::ARM7_IRQ_LINE);
+ m_vic->out_fiq_cb().set_inputline(m_maincpu, arm7_cpu_device::ARM7_FIRQ_LINE);
+
+ for (int i=0; i<3; i++) {
+ LH79524_TIMER(config, m_timers[i], 76'205'000);
+ m_timers[i]->set_timer_index(i);
+ }
+
+ m_timers[0]->irq_cb().set(m_vic, FUNC(vic_pl190_device::irq_w<4>));
+ m_timers[1]->irq_cb().set(m_vic, FUNC(vic_pl190_device::irq_w<5>));
+ m_timers[2]->irq_cb().set(m_vic, FUNC(vic_pl190_device::irq_w<6>));
+
GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pixter_cart");
m_cart->set_endian(ENDIANNESS_LITTLE);
m_cart->set_width(GENERIC_ROM32_WIDTH);
m_cart->set_device_load(FUNC(pixter_multimedia_state::cart_load));
m_cart->set_must_be_loaded(false);
+ PALETTE(config, m_palette).set_format(palette_device::IRGB_1555, 256);
+
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_palette("palette");
+
+ m_screen->set_refresh_hz(60);
+ m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
+ // The LCD is 160x160 but this is a hack to draw the softbuttons below it
+ m_screen->set_size(160, 176);
+ m_screen->set_visarea(0, 160-1, 0, 176-1);
+ m_screen->set_screen_update(FUNC(pixter_multimedia_state::screen_update_pixtermu));
+
+ m_screen->screen_vblank().set(FUNC(pixter_multimedia_state::screen_vblank));
+
+
SOFTWARE_LIST(config, "cart_list").set_original("pixter_cart");
}