summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Miodrag Milanović <mmicko@gmail.com>2016-10-22 12:12:13 +0200
committer GitHub <noreply@github.com>2016-10-22 12:12:13 +0200
commit333bff8de64dfaeb98134000412796b4fdef970b (patch)
tree848a1e72569523d898fecbe04e432962b7060c42
parent2bfd3a6ab2cdcc72d6918624ceaba6ed4d263771 (diff)
parenta060de50628ad5a3f2ec590bc3c315c7c4e33223 (diff)
Merge pull request #1540 from mamehaze/mgs
new WORKING Magnet System (prototype) [David Haywood, Ricky2001, ArcadeHacker, IFW]
-rw-r--r--scripts/target/mame/arcade.lua6
-rw-r--r--src/mame/drivers/cedar_magnet.cpp796
-rw-r--r--src/mame/drivers/cedar_magnet.h131
-rw-r--r--src/mame/drivers/cedar_magnet_board.cpp90
-rw-r--r--src/mame/drivers/cedar_magnet_board.h50
-rw-r--r--src/mame/drivers/cedar_magnet_flop.cpp232
-rw-r--r--src/mame/drivers/cedar_magnet_flop.h49
-rw-r--r--src/mame/drivers/cedar_magnet_plane.cpp162
-rw-r--r--src/mame/drivers/cedar_magnet_plane.h59
-rw-r--r--src/mame/drivers/cedar_magnet_sound.cpp168
-rw-r--r--src/mame/drivers/cedar_magnet_sound.h53
-rw-r--r--src/mame/drivers/cedar_magnet_sprite.cpp336
-rw-r--r--src/mame/drivers/cedar_magnet_sprite.h83
-rw-r--r--src/mame/mame.lst6
14 files changed, 2221 insertions, 0 deletions
diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua
index 56b990a7665..0116a073a0e 100644
--- a/scripts/target/mame/arcade.lua
+++ b/scripts/target/mame/arcade.lua
@@ -4333,6 +4333,12 @@ files {
MAME_DIR .. "src/mame/machine/cdislave.h",
MAME_DIR .. "src/mame/machine/cdicdic.cpp",
MAME_DIR .. "src/mame/machine/cdicdic.h",
+ MAME_DIR .. "src/mame/drivers/cedar_magnet.cpp",
+ MAME_DIR .. "src/mame/drivers/cedar_magnet_sound.cpp",
+ MAME_DIR .. "src/mame/drivers/cedar_magnet_plane.cpp",
+ MAME_DIR .. "src/mame/drivers/cedar_magnet_sprite.cpp",
+ MAME_DIR .. "src/mame/drivers/cedar_magnet_board.cpp",
+ MAME_DIR .. "src/mame/drivers/cedar_magnet_flop.cpp",
MAME_DIR .. "src/mame/drivers/cesclass.cpp",
MAME_DIR .. "src/mame/drivers/chance32.cpp",
MAME_DIR .. "src/mame/drivers/chexx.cpp",
diff --git a/src/mame/drivers/cedar_magnet.cpp b/src/mame/drivers/cedar_magnet.cpp
new file mode 100644
index 00000000000..fe4617278ab
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet.cpp
@@ -0,0 +1,796 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+// thanks to: Ricky2001, ArcadeHacker, IFW
+
+/*
+ todo:
+ - fix sound emulation
+ - fix sprite communication / banking
+ * bit "output bit 0x02 %d (IC21)" at 0x42 might be important
+ * mag_exzi currently requires a gross hack to stop the sprite CPU crashing on startup
+ * mag_xain sometimes leaves old sprites on the screen, probably due to a lost clear
+ command
+ - fix flipscreen
+ - verify behavior of unknown / unused ports / interrupt sources etc.
+ - verify the disk images, convert to a better format that can natively store protection
+ * RAW data also available if required
+ * as mentioned, the disks are copy protected, see notes below
+ * are the bad tiles shortly into the first level of mag_exzi caused by a bad dump or
+ bad comms?
+ - Use proper floppy drive emulation code that originally came from MESS (tied with above)
+ - verify all clocks and screen params (50hz seems to match original videos)
+ - work out why we need a protection hack and replace it with proper emulation
+ * there are no per-game protection devices, so it's something to do with the base hardware
+ * there seem to be 2 checks, one based on a weird sector on the discs, the other based on
+ a port read
+ - add additional hardware notes from ArcadeHacker
+
+*/
+
+
+/*
+
+ Magnet System by
+
+ EFO SA (Electrónica Funcional Operativa SA).
+ based on Cedar hardware
+
+
+ http://retrolaser.es/cedar-computer-el-ordenador-profesional-de-efo-sa/
+ http://www.recreativas.org/magnet-system-2427-efosa
+
+ A number of original games as well as conversions were advertised for this system, it is however
+ believed that EFO went bankrupt before anything hit the market. The only 3 dumped games are
+ conversions and appear to be in incomplete states (it is rather easy to break Time Scanner for
+ example, the ball simply gets stuck in some places) These are not simply bootlegs, they're
+ completely original pieces of code more akin to home computer ports.
+
+ The following were advertised
+ Original Games
+ - A Day in Space **
+ - Crazy Driver
+ - Jungle Trophy
+ - Quadrum
+ - War Mission **
+ - The Burning Cave
+ - Scorpio
+ - Paris Dakar **
+ - Sailing Race
+ - Formula
+
+ Ports / Conversions
+ - Exzisus *
+ - Double Dragon
+ - Flying Shark
+ - Time Scanner *
+ - Xain d'Sleena *
+ - Boody Kids (Booby Kids?)
+
+ ** screenshots present on flyer
+ * dumps exist
+
+
+
+Disk Protection
+
+Sectors are 1024 (0x400) bytes long but marked on the disc as 512 bytes as a copy protection
+Sector numbering for each track starts at 200 (0xC8) again, presumably as a protection.
+Each track has 6 sectors (200 - 205)
+The drive runs at 240 RPM allowing for ~25% extra data. (attempting to dump at other speeds won't work)
+
+ data order / sector marking
+ track 0, side 0, sector 200...205 (instead of sector 0...5)
+ track 0, side 1, sector 200...205
+ track 1, side 0, sector 200...205
+ track 1, side 1, sector 200...205
+
+Note, the games store settings / scores to the disk and don't provide any kind of 'factory reset'
+option, so if used the data will not be pristine.
+
+PCB Setup
+
+The hardware consists of 5 main PCBs in a cage.
+1x Audio PCB (on top)
+1x Master PCB
+2x Plane PCBs (both identical aside from jumper settings)
+1x Sprite PCB
+
+There are small memory sub-boards on the Master PCB and Sprite PCB; due to the awkwardness of
+the banking at times (and the fact that even with 4 banks of 256 colours, only one can be active)
+I suspect the additional memory was an afterthought.
+
+(put more details hardware notes here)
+
+
+*/
+
+#include "cedar_magnet.h"
+
+/***********************
+
+ Memory maps
+
+***********************/
+
+static ADDRESS_MAP_START( cedar_magnet_mainboard_sub_pal_map, AS_PROGRAM, 8, cedar_magnet_state )
+// these are 3x MOTOROLA MM2114N SRAM 4096 bit RAM (twice the size because we map bytes, but only 4 bits are used)
+// these are on the master board memory sub-board
+ AM_RANGE(0x2400, 0x27ff) AM_RAM_WRITE(palette_r_w) AM_SHARE("pal_r")
+ AM_RANGE(0x2800, 0x2bff) AM_RAM_WRITE(palette_g_w) AM_SHARE("pal_g")
+ AM_RANGE(0x3000, 0x33ff) AM_RAM_WRITE(palette_b_w) AM_SHARE("pal_b")
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_magnet_mainboard_sub_ram_map, AS_PROGRAM, 8, cedar_magnet_state )
+// these are 8x SIEMENS HYB 41256-15 AA - 262,144 bit DRAM (32kbytes)
+// these are on the master board memory sub-board
+ AM_RANGE(0x00000, 0x3ffff) AM_RAM AM_SHARE("ram0")
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_magnet_map, AS_PROGRAM, 8, cedar_magnet_state )
+ AM_RANGE(0x0000, 0xffff) AM_DEVICE("bank0", address_map_bank_device, amap8)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_magnet_io, AS_IO, 8, cedar_magnet_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+
+ AM_RANGE(0x18, 0x18) AM_READWRITE(port18_r, port18_w)
+ AM_RANGE(0x19, 0x19) AM_READWRITE(port19_r, port19_w)
+ AM_RANGE(0x1a, 0x1a) AM_READ(port1a_r)
+ AM_RANGE(0x1b, 0x1b) AM_WRITE(port1b_w)
+
+ AM_RANGE(0x20, 0x23) AM_DEVREADWRITE("z80pio_ic48", z80pio_device, read_alt, write_alt)
+ AM_RANGE(0x40, 0x43) AM_DEVREADWRITE("z80pio_ic49", z80pio_device, read_alt, write_alt)
+
+ AM_RANGE(0x60, 0x63) AM_DEVREADWRITE("flop", cedar_magnet_flop_device, read, write)
+
+ AM_RANGE(0x64, 0x64) AM_READ_PORT("P1_IN")
+ AM_RANGE(0x68, 0x68) AM_READ_PORT("P2_IN")
+ AM_RANGE(0x6c, 0x6c) AM_READ_PORT("TEST")
+
+ // banking / access controls to the sub-board memory
+ AM_RANGE(0x70, 0x70) AM_WRITE(rambank_palbank_w)
+ AM_RANGE(0x74, 0x74) AM_WRITE(palupload_w)
+ AM_RANGE(0x78, 0x78) AM_READWRITE(watchdog_r, paladdr_w)
+ AM_RANGE(0x7c, 0x7c) AM_READ(port7c_r) // protection??
+
+ AM_RANGE(0xff, 0xff) AM_WRITE(soundlatch_w)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_bank0, AS_PROGRAM, 8, cedar_magnet_state )
+ /* memory configuration 0 */
+ AM_RANGE(0x00000, 0x0ffff) AM_DEVICE("mb_sub_ram", address_map_bank_device, amap8)
+
+ /* memory configuration 1 */
+ AM_RANGE(0x10000, 0x1dfff) AM_DEVICE("mb_sub_ram", address_map_bank_device, amap8)
+ AM_RANGE(0x1e000, 0x1ffff) AM_ROM AM_REGION("maincpu", 0x0000)
+
+ /* memory configuration 2*/
+ AM_RANGE(0x20000, 0x2bfff) AM_DEVICE("mb_sub_ram", address_map_bank_device, amap8)
+ AM_RANGE(0x2c000, 0x2ffff) AM_READWRITE(other_cpu_r, other_cpu_w)
+
+ /* memory configuration 3*/
+ AM_RANGE(0x30000, 0x31fff) AM_ROM AM_REGION("maincpu", 0x0000) AM_MIRROR(0x0e000)
+ADDRESS_MAP_END
+
+
+/***********************
+
+ 7x - ports
+ Main board RAM sub-board
+
+***********************/
+
+WRITE8_MEMBER(cedar_magnet_state::rambank_palbank_w)
+{
+ // ---- --xx
+ // xx = program bank
+ m_sub_ram_bankdev->set_bank(data & 0x03);
+
+ // yyy? yy-- palette bank
+ m_palbank = data;
+ int palbank = ((data & 0xc0) >> 6) | (data & 0x3c);
+ m_sub_pal_bankdev->set_bank(palbank);
+}
+
+WRITE8_MEMBER(cedar_magnet_state::palupload_w)
+{
+ m_sub_pal_bankdev->write8(space, m_paladdr, data);
+}
+
+WRITE8_MEMBER(cedar_magnet_state::paladdr_w)
+{
+ m_paladdr = data;
+}
+
+READ8_MEMBER(cedar_magnet_state::watchdog_r)
+{
+ // watchdog
+ return 0x00;
+}
+
+
+/***********************
+
+ 7c - protection??
+
+***********************/
+
+READ8_MEMBER(cedar_magnet_state::port7c_r)
+{
+ //printf("%s: port7c_r\n", device().machine().describe_context());
+ return 0x01;
+}
+
+
+/***********************
+
+ 1x ports
+ Unknown, debug? protection?
+
+***********************/
+
+READ8_MEMBER(cedar_magnet_state::port18_r)
+{
+// printf("%s: port18_r\n", device().machine().describe_context());
+ return 0x00;
+}
+
+WRITE8_MEMBER(cedar_magnet_state::port18_w)
+{
+// printf("%s: port18_w %02x\n", device().machine().describe_context(), data);
+}
+
+READ8_MEMBER(cedar_magnet_state::port19_r)
+{
+ UINT8 ret = 0x00;
+// printf("%s: port19_r\n", device().machine().describe_context());
+
+// 9496 in a,($19)
+// 9498 bit 2,a
+
+ ret |= 0x04;
+
+ return ret;
+}
+
+READ8_MEMBER(cedar_magnet_state::port1a_r)
+{
+// printf("%s: port1a_r\n", device().machine().describe_context());
+ return 0x00;
+}
+
+
+WRITE8_MEMBER(cedar_magnet_state::port19_w)
+{
+// printf("%s: port19_w %02x\n", device().machine().describe_context(), data);
+}
+
+WRITE8_MEMBER(cedar_magnet_state::port1b_w)
+{
+// printf("%s: port1b_w %02x\n", device().machine().describe_context(), data);
+}
+
+/***********************
+
+ Palette / Video
+
+***********************/
+
+void cedar_magnet_state::set_palette(int offset)
+{
+ m_palette->set_pen_color(offset^0xff, pal4bit(m_pal_r[offset]), pal4bit(m_pal_g[offset]), pal4bit(m_pal_b[offset]));
+}
+
+WRITE8_MEMBER(cedar_magnet_state::palette_r_w)
+{
+ m_pal_r[offset] = data;
+ set_palette(offset);
+}
+
+WRITE8_MEMBER(cedar_magnet_state::palette_g_w)
+{
+ m_pal_g[offset] = data;
+ set_palette(offset);
+}
+
+WRITE8_MEMBER(cedar_magnet_state::palette_b_w)
+{
+ m_pal_b[offset] = data;
+ set_palette(offset);
+}
+
+UINT32 cedar_magnet_state::screen_update_cedar_magnet(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+
+ bitmap.fill(m_palette->black_pen(), cliprect);
+
+ int pal = (m_palbank >> 6);
+
+ m_cedplane1->draw(screen, bitmap, cliprect,pal);
+ m_cedplane0->draw(screen, bitmap, cliprect,pal);
+ m_cedsprite->draw(screen, bitmap, cliprect,pal);
+
+ return 0;
+}
+
+void cedar_magnet_state::video_start()
+{
+}
+
+/***********************
+
+ Access to other CPUs
+
+***********************/
+
+WRITE8_MEMBER(cedar_magnet_state::soundlatch_w)
+{
+// printf("%s: writing soundlatch_w! %02x\n", device().machine().describe_context(), data);
+ portff_data = data;
+ m_cedsound->write_command(data);
+}
+
+READ8_MEMBER(cedar_magnet_state::other_cpu_r)
+{
+ int bankbit0 = (m_ic48_pio_pa_val & 0x60) >> 5;
+ int plane0select = (m_ic48_pio_pa_val & 0x07) >> 0;
+ int plane1select = (m_ic48_pio_pb_val & 0x07) >> 0;
+ int spriteselect = (m_ic48_pio_pb_val & 0x70) >> 4;
+ int soundselect = (m_ic49_pio_pb_val & 0x70) >> 4;
+ int windowbank = (m_ic49_pio_pb_val & 0x0c) >> 2;
+ int unk2 = (m_ic49_pio_pb_val & 0x03) >> 0;
+
+ int cpus_accessed = 0;
+ UINT8 ret = 0x00;
+
+ int offset2 = offset + windowbank * 0x4000;
+
+ if (spriteselect == 0x1)
+ {
+ cpus_accessed++;
+ ret |= m_cedsprite->read_cpu_bus(offset2);
+ }
+
+ if (plane0select == 0x1)
+ {
+ cpus_accessed++;
+ ret |= m_cedplane0->read_cpu_bus(offset2);
+ }
+
+ if (plane1select == 0x1)
+ {
+ cpus_accessed++;
+ ret |= m_cedplane1->read_cpu_bus(offset2);
+ }
+
+ if (soundselect == 0x1)
+ {
+ cpus_accessed++;
+ ret |= m_cedsound->read_cpu_bus(offset2);
+ logerror("%s: reading soundselect! %04x - bank bits %d %d %d %d %d %d %d\n", device().machine().describe_context(), offset,bankbit0, plane0select, plane1select, spriteselect, soundselect, windowbank, unk2);
+ }
+
+ if (cpus_accessed != 1)
+ logerror("%s: reading multiple CPUS!!! %04x - bank bits %d %d %d %d %d %d %d\n", device().machine().describe_context(), offset,bankbit0, plane0select, plane1select, spriteselect, soundselect, windowbank, unk2);
+
+// if ((offset==0) || (offset2 == 0xe) || (offset2 == 0xf) || (offset2 == 0x68))
+// logerror("%s: reading banked bus area %04x - bank bits %d %d %d %d %d %d %d\n", device().machine().describe_context(), offset,bankbit0, plane0select, plane1select, spriteselect, soundselect, windowbank, unk2);
+
+ return ret;
+}
+
+WRITE8_MEMBER(cedar_magnet_state::other_cpu_w)
+{
+ int bankbit0 = (m_ic48_pio_pa_val & 0x60) >> 5;
+ int plane0select = (m_ic48_pio_pa_val & 0x07) >> 0;
+ int plane1select = (m_ic48_pio_pb_val & 0x07) >> 0;
+ int spriteselect = (m_ic48_pio_pb_val & 0x70) >> 4;
+ int soundselect = (m_ic49_pio_pb_val & 0x70) >> 4;
+ int windowbank = (m_ic49_pio_pb_val & 0x0c) >> 2;
+ int unk2 = (m_ic49_pio_pb_val & 0x03) >> 0;
+
+ int cpus_accessed = 0;
+
+ int offset2 = offset + windowbank * 0x4000;
+
+ if (spriteselect == 0x1)
+ {
+ cpus_accessed++;
+ m_cedsprite->write_cpu_bus(offset2, data);
+ }
+
+ if (plane0select == 0x1)
+ {
+ cpus_accessed++;
+ m_cedplane0->write_cpu_bus(offset2, data);
+ }
+
+ if (plane1select == 0x1)
+ {
+ cpus_accessed++;
+ m_cedplane1->write_cpu_bus(offset2, data);
+ }
+
+ if (soundselect == 0x1)
+ {
+ cpus_accessed++;
+ m_cedsound->write_cpu_bus(offset2, data);
+ // printf("%s: sound cpu write %04x %02x - bank bits %d %d %d %d %d %d %d\n", device().machine().describe_context(), offset,data, bankbit0, plane0select, plane1select, spriteselect, soundselect, windowbank, unk2);
+ }
+
+ if (cpus_accessed != 1)
+ logerror("%s: writing multiple CPUS!!! %04x %02x - bank bits %d %d %d %d %d %d %d\n", device().machine().describe_context(), offset,data, bankbit0, plane0select, plane1select, spriteselect, soundselect, windowbank, unk2);
+
+// if ((offset==0) || (offset2 == 0xe) || (offset2 == 0xf) || (offset2 == 0x68))
+// printf("%s: other cpu write %04x %02x - bank bits %d %d %d %d %d %d %d\n", device().machine().describe_context(), offset,data, bankbit0, plane0select, plane1select, spriteselect, soundselect, windowbank, unk2);
+}
+
+
+void cedar_magnet_state::handle_sub_board_cpu_lines(cedar_magnet_board_device* dev, int old_data, int data)
+{
+ if (old_data != data)
+ {
+ if (data & 0x04)
+ dev->reset_assert();
+ else
+ dev->reset_clear();
+
+ if (data & 0x02)
+ dev->halt_clear();
+ else
+ dev->halt_assert();
+ }
+}
+
+/***********************
+
+ IC 48 PIO handlers
+ (mapped at 0x20 / 0x22)
+
+***********************/
+
+READ8_MEMBER( cedar_magnet_state::ic48_pio_pa_r ) // 0x20
+{
+ UINT8 ret = m_ic48_pio_pa_val & ~0x08;
+
+ ret |= ioport("COIN1")->read()<<3;
+ if (!m_cedplane0->is_running()) ret &= ~0x01;
+
+ // interrupt source stuff??
+ ret &= ~0x10;
+
+ if (LOG_IC48_PIO_PA) printf("%s: ic48_pio_pa_r (returning %02x)\n", device().machine().describe_context(), ret);
+ return ret;
+}
+
+WRITE8_MEMBER( cedar_magnet_state::ic48_pio_pa_w ) // 0x20
+{
+ int oldplane0select = (m_ic48_pio_pa_val & 0x07) >> 0;
+
+ // bits 19 are set to input?
+ m_ic48_pio_pa_val = data;
+
+ // address 0x20 - pio ic48 port a
+ if (LOG_IC48_PIO_PA) printf("%s: ic48_pio_pa_w %02x (memory banking etc.)\n", device().machine().describe_context(), data);
+
+ if (LOG_IC48_PIO_PA) printf("output bit 0x80 %d (unused)\n", (data >> 7)&1); // A7 -> 12 J4 unpopulated
+ if (LOG_IC48_PIO_PA) printf("output bit 0x40 %d (bank)\n", (data >> 6)&1); // A6 -> 2 74HC10 3NAND IC19
+ if (LOG_IC48_PIO_PA) printf("output bit 0x20 %d (bank)\n", (data >> 5)&1); // A5 -> 4 74HC10 3NAND IC19
+ if (LOG_IC48_PIO_PA) printf("input bit 0x10 %d (interrupt source related?)\n", (data >> 4)&1); // 10 in // A4 <- 9 74HC74 IC20 <- input from 18 74LS244 IC61
+ if (LOG_IC48_PIO_PA) printf("input bit 0x08 %d (COIN1)\n", (data >> 3)&1); // 08 in // A3 <- 4 74HC14P (inverter) IC4 <- EDGE 21 COIN1
+ if (LOG_IC48_PIO_PA) printf("output bit 0x04 %d (plane0 CPU/bus related?)\n", (data >> 2)&1); // A2 -> 45 J6
+ if (LOG_IC48_PIO_PA) printf("output bit 0x02 %d (plane0 CPU/bus related?)\n", (data >> 1)&1); // A1 -> 47 J6
+ if (LOG_IC48_PIO_PA) printf("input bit 0x01 %d (plane0 CPU/bus related?)\n", (data >> 0)&1); // A0 -> 49 J6
+
+ int bankbit0 = (m_ic48_pio_pa_val & 0x60) >> 5;
+ m_bank0->set_bank(bankbit0);
+
+ int plane0select = (m_ic48_pio_pa_val & 0x07) >> 0;
+
+ handle_sub_board_cpu_lines(m_cedplane0, oldplane0select, plane0select);
+}
+
+
+READ8_MEMBER( cedar_magnet_state::ic48_pio_pb_r ) // 0x22
+{
+ UINT8 ret = m_ic48_pio_pb_val & ~0x80;
+
+ ret |= ioport("COIN2")->read()<<7;
+
+ if (!m_cedsprite->is_running()) ret &= ~0x10;
+ if (!m_cedplane1->is_running()) ret &= ~0x01;
+
+ if (LOG_IC48_PIO_PB) printf("%s: ic48_pio_pb_r (returning %02x)\n", device().machine().describe_context(), ret);
+ return ret;
+}
+
+WRITE8_MEMBER(cedar_magnet_state::ic48_pio_pb_w) // 0x22
+{
+ int oldplane1select = (m_ic48_pio_pb_val & 0x07) >> 0;
+ int oldspriteselect = (m_ic48_pio_pb_val & 0x70) >> 4;
+
+ m_ic48_pio_pb_val = data;
+
+ if (LOG_IC48_PIO_PB) printf("%s: ic48_pio_pb_w %02x\n", device().machine().describe_context(), data);
+
+ // address 0x22 - pio ic48 port b
+ if (LOG_IC48_PIO_PB) printf("input bit 0x80 %d (COIN2)\n", (data >> 7)&1); // B7 <- 2 74HC14P (inverter) IC4 <- EDGE 22 COIN2
+ if (LOG_IC48_PIO_PB) printf("output bit 0x40 (J6) (sprite CPU/bus related?) %d\n", (data >> 6)&1); // B6 -> 41 J6
+ if (LOG_IC48_PIO_PB) printf("output bit 0x20 (J6) (sprite CPU/bus related?) %d\n", (data >> 5)&1); // B5 -> 43 J6
+ if (LOG_IC48_PIO_PB) printf("input bit 0x10 (J6) (sprite CPU/bus related?) %d\n", (data >> 4)&1); // B4 -> 44 J6
+ if (LOG_IC48_PIO_PB) printf("output bit 0x08 (Q8) %d\n", (data >> 3)&1); // B3 -> Q8 transistor
+ if (LOG_IC48_PIO_PB) printf("output bit 0x04 (J6) (plane1 CPU/bus related?) %d\n", (data >> 2)&1); // B2 -> 46 J6
+ if (LOG_IC48_PIO_PB) printf("output bit 0x02 (J6) (plane1 CPU/bus related?) %d\n", (data >> 1)&1); // B1 -> 48 J6
+ if (LOG_IC48_PIO_PB) printf("input bit 0x01 (J6) (plane1 CPU/bus related?) %d\n", (data >> 0)&1); // B0 -> 50 J6
+
+ int plane1select = (m_ic48_pio_pb_val & 0x07) >> 0;
+ int spriteselect = (m_ic48_pio_pb_val & 0x70) >> 4;
+
+ handle_sub_board_cpu_lines(m_cedplane1, oldplane1select, plane1select);
+ handle_sub_board_cpu_lines(m_cedsprite, oldspriteselect, spriteselect);
+}
+
+/***********************
+
+ IC 49 PIO handlers
+ (mapped at 0x42)
+
+***********************/
+
+READ8_MEMBER( cedar_magnet_state::ic49_pio_pb_r ) // 0x42
+{
+ UINT8 ret = m_ic49_pio_pb_val;
+
+ if (!m_cedsound->is_running()) ret &= ~0x10;
+
+ if (LOG_IC49_PIO_PB) printf("%s: ic49_pio_pb_r (returning %02x)\n", device().machine().describe_context(), ret);
+ return ret;
+}
+
+WRITE8_MEMBER( cedar_magnet_state::ic49_pio_pb_w ) // 0x42
+{
+ int oldsoundselect = (m_ic49_pio_pb_val & 0x70) >> 4;
+
+ m_ic49_pio_pb_val = data;
+
+ //printf("%s: ic49_pio_pb_w %02x\n", device().machine().describe_context(), data);
+
+ // address 0x42 - pio ic49 port b
+ if (LOG_IC49_PIO_PB) printf("output bit 0x80 %d (Q9)\n", (data >> 7)&1); // B7 -> Q9 transistor
+ if (LOG_IC49_PIO_PB) printf("output bit 0x40 %d (sound CPU bus related) (J3)\n", (data >> 6)&1); // B6 -> 9 J3
+ if (LOG_IC49_PIO_PB) printf("output bit 0x20 %d (sound CPU bus related) (J3)\n", (data >> 5)&1); // B5 -> 8 J3
+ if (LOG_IC49_PIO_PB) printf("input bit 0x10 %d (sound CPU bus related) (J3)\n", (data >> 4)&1); // B4 -> 7 J3 // input?
+ if (LOG_IC49_PIO_PB) printf("output bit 0x08 %d (J7)\n", (data >> 3)&1); // B3 -> 35 J7 bank bits
+ if (LOG_IC49_PIO_PB) printf("output bit 0x04 %d (J7)\n", (data >> 2)&1); // B2 -> 36 J7 bank bits
+ // there is code to mask out both bottom bits here before load operations?
+ if (LOG_IC49_PIO_PB) printf("output bit 0x02 %d (IC21)\n", (data >> 1)&1); // B1 -> 3 74HC04 IC21 (set before some SPRITE cpu operations, possibly halts the blitter?)
+ if (LOG_IC49_PIO_PB) printf("output bit 0x01 (LED) %d\n", (data >> 0)&1); // B0 -> LED LD1
+
+
+
+ int soundselect = (m_ic49_pio_pb_val & 0x70) >> 4;
+
+ handle_sub_board_cpu_lines(m_cedsound, oldsoundselect, soundselect);
+}
+
+/***********************
+
+ Init / Inputs / Machine
+
+***********************/
+
+void cedar_magnet_state::machine_start()
+{
+ save_item(NAME(m_paladdr));
+}
+
+void cedar_magnet_state::machine_reset()
+{
+ m_ic48_pio_pa_val = 0xff;
+ portff_data = 0x00;
+
+ int bankbit0 = (m_ic48_pio_pa_val & 0x60) >> 5;
+ m_bank0->set_bank(bankbit0);
+ m_sub_ram_bankdev->set_bank(3);
+ m_sub_pal_bankdev->set_bank(0);
+}
+
+
+static INPUT_PORTS_START( cedar_magnet )
+ PORT_START("COIN1")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN1 )
+
+ PORT_START("COIN2")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN2 )
+
+ PORT_START("P1_IN")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START1 )
+
+ PORT_START("P2_IN")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START2 )
+
+ PORT_START("TEST")
+ PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_SERVICE_NO_TOGGLE( 0x80, IP_ACTIVE_LOW )
+INPUT_PORTS_END
+
+INTERRUPT_GEN_MEMBER(cedar_magnet_state::irq)
+{
+ if (m_prothack)
+ m_prothack(this);
+
+ m_maincpu->set_input_line(0, HOLD_LINE);
+ // maybe generate the irqs for the other PCBs here?
+}
+
+static MACHINE_CONFIG_START( cedar_magnet, cedar_magnet_state )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu", Z80,4000000) /* ? MHz */
+ MCFG_CPU_PROGRAM_MAP(cedar_magnet_map)
+ MCFG_CPU_IO_MAP(cedar_magnet_io)
+ MCFG_CPU_VBLANK_INT_DRIVER("screen", cedar_magnet_state, irq)
+
+ MCFG_DEVICE_ADD("bank0", ADDRESS_MAP_BANK, 0)
+ MCFG_DEVICE_PROGRAM_MAP(cedar_bank0)
+ MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
+ MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8)
+ MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(18)
+ MCFG_ADDRESS_MAP_BANK_STRIDE(0x10000)
+
+ MCFG_DEVICE_ADD("mb_sub_ram", ADDRESS_MAP_BANK, 0)
+ MCFG_DEVICE_PROGRAM_MAP(cedar_magnet_mainboard_sub_ram_map)
+ MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
+ MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8)
+ MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(18)
+ MCFG_ADDRESS_MAP_BANK_STRIDE(0x10000)
+
+ MCFG_DEVICE_ADD("mb_sub_pal", ADDRESS_MAP_BANK, 0)
+ MCFG_DEVICE_PROGRAM_MAP(cedar_magnet_mainboard_sub_pal_map)
+ MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
+ MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8)
+ MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(8+6)
+ MCFG_ADDRESS_MAP_BANK_STRIDE(0x100)
+
+ MCFG_DEVICE_ADD("z80pio_ic48", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+ MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_state, ic48_pio_pa_r))
+ MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_state, ic48_pio_pa_w))
+ MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_state, ic48_pio_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_state, ic48_pio_pb_w))
+
+ MCFG_DEVICE_ADD("z80pio_ic49", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+// MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_state, ic49_pio_pa_r)) // NOT USED
+// MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_state, ic49_pio_pa_w)) // NOT USED
+ MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_state, ic49_pio_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_state, ic49_pio_pb_w))
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_REFRESH_RATE(50)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0))
+ MCFG_SCREEN_SIZE(256, 256)
+ MCFG_SCREEN_VISIBLE_AREA(0, 256-8-1, 0, 192-1)
+ MCFG_SCREEN_UPDATE_DRIVER(cedar_magnet_state, screen_update_cedar_magnet)
+ MCFG_SCREEN_PALETTE("palette")
+
+ MCFG_PALETTE_ADD("palette", 0x400)
+
+ MCFG_CEDAR_MAGNET_SOUND_ADD("cedtop")
+ MCFG_CEDAR_MAGNET_PLANE_ADD("cedplane0")
+ MCFG_CEDAR_MAGNET_PLANE_ADD("cedplane1")
+ MCFG_CEDAR_MAGNET_SPRITE_ADD("cedsprite")
+
+ MCFG_CEDAR_MAGNET_FLOP_ADD("flop")
+
+ MCFG_QUANTUM_PERFECT_CPU("maincpu")
+MACHINE_CONFIG_END
+
+
+#define BIOS_ROM \
+ ROM_REGION( 0x10000, "maincpu", 0 ) \
+ ROM_LOAD( "Magnet-Master-VID-E03.BIN", 0x00000, 0x02000, CRC(86c4a4f0) SHA1(6db1a006b2e0b2a7cc9748ade881debb098b6757) )
+
+
+
+ROM_START( cedmag )
+ BIOS_ROM
+
+ ROM_REGION( 0x100000, "flop:disk", ROMREGION_ERASE00 )
+ // no disk inserted
+ROM_END
+
+ROM_START( mag_time )
+ BIOS_ROM
+
+ ROM_REGION( 0x100000, "flop:disk", 0 )
+ ROM_LOAD( "timescanner.img", 0x00000, 0xf0000, CRC(214c558c) SHA1(9c71fce35acaf17ac685f77aebb1b0a930060f0b) )
+ROM_END
+
+ROM_START( mag_exzi )
+ BIOS_ROM
+
+ ROM_REGION( 0x100000, "flop:disk", ROMREGION_ERASE00 ) // I don't 100% trust this one
+ ROM_LOAD( "exzisus.img", 0x00000, 0xf0000, BAD_DUMP CRC(1ac7409e) SHA1(b894bd65b0b9699e18a1ab49f309c460488f0ef8) )
+ROM_END
+
+ROM_START( mag_xain )
+ BIOS_ROM
+
+ ROM_REGION( 0x100000, "flop:disk", ROMREGION_ERASE00 )
+ ROM_LOAD( "xain.img", 0x00000, 0xf0000, CRC(5647849f) SHA1(edd2f3f6359424583bf526bf4601476dc849e617) )
+ROM_END
+
+/*
+ protection? (Time Scanner note)
+
+ one part of the code is a weird loop checking values from port 0x7c while doing other nonsensical stuff, a flag gets set to 0xff if it fails
+
+ the other part is after reading the weird extra block on the disk (score / protection data at 0xea400 in the disk image*) and again a flag
+ gets set to 0xff in certain conditions there's then a check after inserting a coin, these values can't be 0xff at that point, and there
+ doesn't appear to be any code to reset them.
+
+ *0xea400 is/was track 4e, side 00, sector 01 for future reference if the floppy format changes
+
+ all games have the same code in them but at different addresses
+*/
+
+
+void protection_hack(UINT8* ram, int address1, int address2)
+{
+ if ((ram[address1] == 0x3e) && (ram[address1+1] == 0xff)) ram[address1] = 0xc9;
+ if ((ram[address2] == 0x3e) && (ram[address2+1] == 0xff)) ram[address2] = 0xc9;
+}
+
+void mag_time_protection_hack(cedar_magnet_state* state)
+{
+ protection_hack(state->m_ram0, 0x8bc, 0x905);
+}
+
+void mag_xain_protection_hack(cedar_magnet_state* state)
+{
+ protection_hack(state->m_ram0, 0x796, 0x7df);
+}
+
+void mag_exzi_protection_hack(cedar_magnet_state* state)
+{
+ protection_hack(state->m_ram0, 0x8b6, 0x8ff);
+}
+
+
+
+DRIVER_INIT_MEMBER(cedar_magnet_state, mag_time)
+{
+ m_prothack = mag_time_protection_hack;
+}
+
+DRIVER_INIT_MEMBER(cedar_magnet_state, mag_xain)
+{
+ m_prothack = mag_xain_protection_hack;
+}
+
+DRIVER_INIT_MEMBER(cedar_magnet_state, mag_exzi)
+{
+ m_prothack = mag_exzi_protection_hack;
+}
+
+GAME( 1987, cedmag, 0, cedar_magnet, cedar_magnet, driver_device, 0, ROT0, "EFO SA / Cedar", "Magnet System (prototype)", MACHINE_IS_BIOS_ROOT )
+
+GAME( 1987, mag_time, cedmag, cedar_magnet, cedar_magnet, cedar_magnet_state, mag_time, ROT90, "EFO SA / Cedar", "Time Scanner (TS 2.0, Magnet System, prototype)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND ) // original game was by Sega
+GAME( 1987, mag_exzi, cedmag, cedar_magnet, cedar_magnet, cedar_magnet_state, mag_exzi, ROT0, "EFO SA / Cedar", "Exzisus (EX 1.0, Magnet System, prototype)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND ) // original game was by Taito
+GAME( 1987, mag_xain, cedmag, cedar_magnet, cedar_magnet, cedar_magnet_state, mag_xain, ROT0, "EFO SA / Cedar", "Xain'd Sleena (SC 3.0, Magnet System, prototype)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND ) // original game was by Technos
diff --git a/src/mame/drivers/cedar_magnet.h b/src/mame/drivers/cedar_magnet.h
new file mode 100644
index 00000000000..feaa610889c
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet.h
@@ -0,0 +1,131 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+/*
+
+
+*/
+
+#include "emu.h"
+#include "cpu/z80/z80.h"
+#include "cpu/z80/z80daisy.h"
+#include "machine/z80pio.h"
+#include "machine/bankdev.h"
+#include "machine/z80ctc.h"
+#include "sound/ay8910.h"
+
+#include "cedar_magnet_sound.h"
+#include "cedar_magnet_plane.h"
+#include "cedar_magnet_sprite.h"
+#include "cedar_magnet_flop.h"
+
+#define LOG_IC49_PIO_PB 0
+#define LOG_IC48_PIO_PB 0
+#define LOG_IC48_PIO_PA 0
+
+class cedar_magnet_state : public driver_device
+{
+public:
+ cedar_magnet_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag),
+ m_bank0(*this, "bank0"),
+ m_sub_ram_bankdev(*this, "mb_sub_ram"),
+ m_sub_pal_bankdev(*this, "mb_sub_pal"),
+ m_ram0(*this, "ram0"),
+ m_pal_r(*this, "pal_r"),
+ m_pal_g(*this, "pal_g"),
+ m_pal_b(*this, "pal_b"),
+
+ m_ic48_pio(*this, "z80pio_ic48"),
+ m_ic49_pio(*this, "z80pio_ic49"),
+ m_palette(*this, "palette"),
+ m_maincpu(*this, "maincpu"),
+ m_cedsound(*this, "cedtop"),
+ m_cedplane0(*this, "cedplane0"),
+ m_cedplane1(*this, "cedplane1"),
+ m_cedsprite(*this, "cedsprite")
+ {
+ m_ic48_pio_pa_val = 0xff;
+ m_ic48_pio_pb_val = 0xff;
+ m_ic49_pio_pb_val = 0xff;
+ m_prothack = nullptr;
+ }
+
+ required_device<address_map_bank_device> m_bank0;
+ required_device<address_map_bank_device> m_sub_ram_bankdev;
+ required_device<address_map_bank_device> m_sub_pal_bankdev;
+
+ required_shared_ptr<UINT8> m_ram0;
+ required_shared_ptr<UINT8> m_pal_r;
+ required_shared_ptr<UINT8> m_pal_g;
+ required_shared_ptr<UINT8> m_pal_b;
+
+ required_device<z80pio_device> m_ic48_pio;
+ required_device<z80pio_device> m_ic49_pio;
+
+ DECLARE_READ8_MEMBER(ic48_pio_pa_r);
+ DECLARE_WRITE8_MEMBER(ic48_pio_pa_w);
+
+ DECLARE_READ8_MEMBER(ic48_pio_pb_r);
+ DECLARE_WRITE8_MEMBER(ic48_pio_pb_w);
+
+ DECLARE_READ8_MEMBER(ic49_pio_pb_r);
+ DECLARE_WRITE8_MEMBER(ic49_pio_pb_w);
+
+ // 1x range ports
+ DECLARE_WRITE8_MEMBER(port18_w);
+ DECLARE_WRITE8_MEMBER(port19_w);
+ DECLARE_WRITE8_MEMBER(port1b_w);
+
+ DECLARE_READ8_MEMBER(port18_r);
+ DECLARE_READ8_MEMBER(port19_r);
+ DECLARE_READ8_MEMBER(port1a_r);
+
+ // 7x range ports
+ DECLARE_WRITE8_MEMBER(rambank_palbank_w);
+ DECLARE_WRITE8_MEMBER(palupload_w);
+ DECLARE_WRITE8_MEMBER(paladdr_w);
+ DECLARE_READ8_MEMBER(watchdog_r);
+ DECLARE_READ8_MEMBER(port7c_r);
+
+ // other ports
+ DECLARE_WRITE8_MEMBER(soundlatch_w);
+ UINT8 portff_data;
+
+ DECLARE_READ8_MEMBER(other_cpu_r);
+ DECLARE_WRITE8_MEMBER(other_cpu_w);
+
+ UINT8 m_paladdr;
+ int m_palbank;
+
+ UINT8 m_ic48_pio_pa_val;
+ UINT8 m_ic48_pio_pb_val;
+ UINT8 m_ic49_pio_pb_val;
+
+ void set_palette(int offset);
+ DECLARE_WRITE8_MEMBER(palette_r_w);
+ DECLARE_WRITE8_MEMBER(palette_g_w);
+ DECLARE_WRITE8_MEMBER(palette_b_w);
+
+ void handle_sub_board_cpu_lines(cedar_magnet_board_device* dev, int old_data, int data);
+ INTERRUPT_GEN_MEMBER(irq);
+ void (*m_prothack)(cedar_magnet_state*);
+
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+ virtual void video_start() override;
+ UINT32 screen_update_cedar_magnet(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ required_device<palette_device> m_palette;
+ required_device<cpu_device> m_maincpu;
+
+ required_device<cedar_magnet_sound_device> m_cedsound;
+ required_device<cedar_magnet_plane_device> m_cedplane0;
+ required_device<cedar_magnet_plane_device> m_cedplane1;
+ required_device<cedar_magnet_sprite_device> m_cedsprite;
+
+ DECLARE_DRIVER_INIT(mag_time);
+ DECLARE_DRIVER_INIT(mag_xain);
+ DECLARE_DRIVER_INIT(mag_exzi);
+
+};
+
+
diff --git a/src/mame/drivers/cedar_magnet_board.cpp b/src/mame/drivers/cedar_magnet_board.cpp
new file mode 100644
index 00000000000..3b95fcc384c
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_board.cpp
@@ -0,0 +1,90 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+
+#include "emu.h"
+#include "cedar_magnet_plane.h"
+
+
+//const device_type CEDAR_MAGNET_BASE = &device_creator<cedar_magnet_board_device>;
+
+cedar_magnet_board_device::cedar_magnet_board_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)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source)
+// m_ram(*this, "ram")
+{
+}
+
+INTERRUPT_GEN_MEMBER(cedar_magnet_board_device::irq)
+{
+ m_cpu->set_input_line(0, HOLD_LINE);
+}
+
+void cedar_magnet_board_device::device_start()
+{
+}
+
+void cedar_magnet_board_device::write_cpu_bus(int offset, UINT8 data)
+{
+ device_t* cpu = m_cpu;
+ address_space& ap = cpu->memory().space(AS_PROGRAM);
+ ap.write_byte(offset, data);
+}
+
+UINT8 cedar_magnet_board_device::read_cpu_bus(int offset)
+{
+ device_t* cpu = m_cpu;
+ address_space& ap = cpu->memory().space(AS_PROGRAM);
+ return ap.read_byte(offset);
+}
+
+bool cedar_magnet_board_device::is_running(void)
+{
+ return m_is_running;
+}
+
+TIMER_CALLBACK_MEMBER(cedar_magnet_board_device::reset_assert_callback)
+{
+ m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
+}
+
+TIMER_CALLBACK_MEMBER(cedar_magnet_board_device::reset_clear_callback)
+{
+ m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
+}
+
+TIMER_CALLBACK_MEMBER(cedar_magnet_board_device::halt_assert_callback)
+{
+ m_cpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
+ m_is_running = false;
+}
+
+TIMER_CALLBACK_MEMBER(cedar_magnet_board_device::halt_clear_callback)
+{
+ m_cpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
+ m_is_running = true;
+}
+
+void cedar_magnet_board_device::halt_assert(void)
+{
+ machine().scheduler().timer_set(attotime::from_usec(2), timer_expired_delegate(FUNC(cedar_magnet_board_device::halt_assert_callback),this));
+}
+
+void cedar_magnet_board_device::halt_clear(void)
+{
+ machine().scheduler().timer_set(attotime::from_usec(2), timer_expired_delegate(FUNC(cedar_magnet_board_device::halt_clear_callback),this));
+}
+
+void cedar_magnet_board_device::reset_assert(void)
+{
+ machine().scheduler().timer_set(attotime::from_usec(1), timer_expired_delegate(FUNC(cedar_magnet_board_device::reset_assert_callback),this));
+}
+
+void cedar_magnet_board_device::reset_clear(void)
+{
+ machine().scheduler().timer_set(attotime::from_usec(1), timer_expired_delegate(FUNC(cedar_magnet_board_device::reset_clear_callback),this));
+}
+
+void cedar_magnet_board_device::device_reset()
+{
+ halt_assert();
+}
diff --git a/src/mame/drivers/cedar_magnet_board.h b/src/mame/drivers/cedar_magnet_board.h
new file mode 100644
index 00000000000..2d04f8a156c
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_board.h
@@ -0,0 +1,50 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+// just a base device to hold some common functions of the EFO / Cedar Magnet System PCBs
+
+#pragma once
+
+#ifndef CEDAR_MAGNET_BOARD_DEF
+#define CEDAR_MAGNET_BOARD_DEF
+
+#include "cpu/z80/z80.h"
+#include "cpu/z80/z80daisy.h"
+#include "machine/z80pio.h"
+
+extern const device_type CEDAR_MAGNET_PLANE;
+
+class cedar_magnet_board_device : public device_t
+{
+public:
+ // construction/destruction
+ cedar_magnet_board_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);
+
+ UINT8* m_ram;
+ z80_device* m_cpu;
+
+ virtual UINT8 read_cpu_bus(int offset);
+ virtual void write_cpu_bus(int offset, UINT8 data);
+
+ TIMER_CALLBACK_MEMBER(halt_assert_callback);
+ TIMER_CALLBACK_MEMBER(halt_clear_callback);
+ TIMER_CALLBACK_MEMBER(reset_assert_callback);
+ TIMER_CALLBACK_MEMBER(reset_clear_callback);
+
+ void halt_assert(void);
+ void halt_clear(void);
+ void reset_assert(void);
+ void reset_clear(void);
+ bool is_running(void);
+ bool m_is_running;
+
+ INTERRUPT_GEN_MEMBER(irq);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+};
+
+#endif
diff --git a/src/mame/drivers/cedar_magnet_flop.cpp b/src/mame/drivers/cedar_magnet_flop.cpp
new file mode 100644
index 00000000000..7d4bdd8d42c
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_flop.cpp
@@ -0,0 +1,232 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+// todo, scrap this and use the core SAB 2797B emulation
+// (FM - MFM type) @ 1.25mhz (oscillates 1.250~1.251)
+
+
+#include "emu.h"
+#include "cedar_magnet_flop.h"
+
+extern const device_type CEDAR_MAGNET_FLOP = &device_creator<cedar_magnet_flop_device>;
+
+cedar_magnet_flop_device::cedar_magnet_flop_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CEDAR_MAGNET_FLOP, "Cedar Floppy Simulation", tag, owner, clock, "cedmag_flop", __FILE__)
+{
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( cedar_magnet_flop )
+ MCFG_NVRAM_ADD_NO_FILL("floppy_nvram")
+MACHINE_CONFIG_END
+
+machine_config_constructor cedar_magnet_flop_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( cedar_magnet_flop );
+}
+
+void cedar_magnet_flop_device::device_start()
+{
+ subdevice<nvram_device>("floppy_nvram")->set_base(memregion("disk")->base(), 0xf0000);
+}
+
+void cedar_magnet_flop_device::device_reset()
+{
+ m_flopdat = 0;
+ m_flopcmd = 0;
+ m_flopsec = 0;
+ m_flopstat = 0;
+ m_floptrk = 0;
+}
+
+READ8_MEMBER(cedar_magnet_flop_device::port60_r)
+{
+ UINT8 ret = m_flopstat;
+ return ret;
+}
+
+READ8_MEMBER(cedar_magnet_flop_device::port61_r)
+{
+ UINT8 ret = m_curtrack;
+ return ret;
+}
+
+READ8_MEMBER(cedar_magnet_flop_device::port63_r)
+{
+ UINT8 ret = rand();
+
+ // printf("%s: port63_r (DATA) (%02x)\n", device().machine().describe_context(), ret);
+
+ if ((m_flopcmd&0xf0) == 0x90) // reading data
+ {
+ UINT8 *flop = memregion("disk")->base();
+ int side = (m_flopcmd & 0x02)>>1;
+ int read_offset_base = (m_flopsec * 0x400) + (m_curtrack * 0x3000) + (side * 0x1800);
+
+ int sector_size = 1024;
+
+ if (m_secoffs < sector_size)
+ {
+ m_flopstat |= 0x05;
+
+ int read_offset = read_offset_base + m_secoffs;
+ ret = flop[read_offset];
+
+ if (m_secoffs == 0)
+ {
+ // this is weird data, protection??
+ if(read_offset_base==0xea400)
+ printf("reading sector %d offset %d (from disk image at %04x) (cur track %02x cur side %02x cur sector %02x)\n", m_flopsec, m_secoffs, read_offset, m_curtrack, side, m_flopsec);
+ }
+
+ m_secoffs++;
+
+ if (m_secoffs == sector_size)
+ {
+ //printf("finished sector read\n");
+ m_flopstat &= ~0x05;
+
+ m_secoffs = 0;
+ m_flopsec++;
+ }
+ }
+ else
+ {
+ printf("read past sector!! %d\n", m_secoffs);
+ m_secoffs++;
+ }
+
+ }
+ else
+ {
+ fatalerror("read data in non-read mode?\n");
+ }
+//
+ return ret;
+}
+
+WRITE8_MEMBER(cedar_magnet_flop_device::port60_w)
+{
+ //printf("%s: port60_w (COMMAND) %02x\n", device().machine().describe_context(), data);
+ m_flopcmd = data;
+
+
+ switch (m_flopcmd & 0xf0)
+ {
+ case 0x00:
+ //printf("restore\n");
+ m_flopstat = 0x06;
+ m_curtrack = 0x00;
+ break;
+
+ case 0x10:
+ //printf("seek track\n");
+ m_curtrack = m_flopdat;
+ break;
+
+
+ case 0x90:
+ //printf("read sector\n");
+ m_flopstat |= 0x07;
+ m_secoffs = 0;
+ break;
+
+ case 0xb0:
+ //printf("write sector\n");
+ m_flopstat |= 0x07;
+ m_secoffs = 0;
+ break;
+
+ case 0xd0:
+ //printf("force interrupt?\n");
+ // m_flopstat = 0x06;
+ // m_flopstat &= ~0x07;
+ // m_maincpu->set_input_line(0, HOLD_LINE);
+
+ break;
+
+
+ default:
+ printf("unhandled disk command %02x\n", m_flopcmd);
+ }
+
+}
+
+WRITE8_MEMBER(cedar_magnet_flop_device::port62_w)
+{
+ //printf("%s: port62_w (SECTOR) %02x\n", device().machine().describe_context(), data);
+ m_flopsec = data;
+
+ if (m_flopsec < 200)
+ {
+ printf("sector specified <200!\n");
+ }
+
+ m_flopsec -= 200;
+}
+
+WRITE8_MEMBER(cedar_magnet_flop_device::port63_w)
+{
+ //printf("%s: port63_w (DATA) %02x\n", device().machine().describe_context(), data);
+ m_flopdat = data;
+
+ if ((m_flopcmd & 0xf0) == 0xb0) // writing data
+ {
+ UINT8 *flop = memregion("disk")->base();
+ int side = (m_flopcmd & 0x02)>>1;
+ int read_offset_base = (m_flopsec * 0x400) + (m_curtrack * 0x3000) + (side * 0x1800);
+
+ int sector_size = 1024;
+
+ if (m_secoffs < sector_size)
+ {
+ m_flopstat |= 0x05;
+
+ int read_offset = read_offset_base + m_secoffs;
+ flop[read_offset] = data;
+
+ if (m_secoffs == 0)
+ {
+ printf("writing sector %d offset %d (from disk image at %04x) (cur track %02x cur side %02x cur sector %02x)\n", m_flopsec, m_secoffs, read_offset, m_curtrack, side, m_flopsec);
+ }
+
+ m_secoffs++;
+
+ if (m_secoffs == sector_size)
+ {
+ //printf("finished sector read\n");
+ m_flopstat &= ~0x05;
+
+ m_secoffs = 0;
+ m_flopsec++;
+ }
+ }
+ }
+
+}
+
+WRITE8_MEMBER(cedar_magnet_flop_device::write)
+{
+ switch (offset & 3)
+ {
+ case 0x00:port60_w(space, offset, data);break;
+ // case 0x01:port61_w(space, offset, data);break;
+ case 0x02:port62_w(space, offset, data);break;
+ case 0x03:port63_w(space, offset, data);break;
+ default:break;
+ }
+}
+
+READ8_MEMBER(cedar_magnet_flop_device::read)
+{
+ switch (offset & 3)
+ {
+ case 0x00: return port60_r(space, offset);
+ case 0x01: return port61_r(space, offset);
+ //case 0x02: return port62_r(space, offset);
+ case 0x03: return port63_r(space, offset);
+ default: return 0x00;
+ }
+
+ return 0x00;
+} \ No newline at end of file
diff --git a/src/mame/drivers/cedar_magnet_flop.h b/src/mame/drivers/cedar_magnet_flop.h
new file mode 100644
index 00000000000..7ff44b40947
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_flop.h
@@ -0,0 +1,49 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#pragma once
+
+#ifndef CEDAR_MAGNET_FLOP_DEF
+#define CEDAR_MAGNET_FLOP_DEF
+
+extern const device_type CEDAR_MAGNET_FLOP;
+
+#define MCFG_CEDAR_MAGNET_FLOP_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, CEDAR_MAGNET_FLOP, 0)
+
+#include "machine/nvram.h"
+
+class cedar_magnet_flop_device : public device_t
+{
+public:
+ // construction/destruction
+ cedar_magnet_flop_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ UINT8 m_flopdat;
+ UINT8 m_flopcmd;
+ UINT8 m_flopsec;
+ UINT8 m_flopstat;
+ UINT8 m_floptrk;
+
+ UINT8 m_curtrack;
+ int m_secoffs;
+
+ DECLARE_READ8_MEMBER(port60_r);
+ DECLARE_READ8_MEMBER(port61_r);
+ DECLARE_READ8_MEMBER(port63_r);
+
+ DECLARE_WRITE8_MEMBER(port60_w);
+ DECLARE_WRITE8_MEMBER(port62_w);
+ DECLARE_WRITE8_MEMBER(port63_w);
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+protected:
+ virtual machine_config_constructor device_mconfig_additions() const override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+private:
+};
+
+#endif
diff --git a/src/mame/drivers/cedar_magnet_plane.cpp b/src/mame/drivers/cedar_magnet_plane.cpp
new file mode 100644
index 00000000000..ffa92d03643
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_plane.cpp
@@ -0,0 +1,162 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+
+#include "emu.h"
+#include "cedar_magnet_plane.h"
+
+
+extern const device_type CEDAR_MAGNET_PLANE = &device_creator<cedar_magnet_plane_device>;
+
+
+cedar_magnet_plane_device::cedar_magnet_plane_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : cedar_magnet_board_device(mconfig, CEDAR_MAGNET_PLANE, "Cedar Plane", tag, owner, clock, "cedmag_plane", __FILE__)
+{
+}
+
+
+
+static ADDRESS_MAP_START( cedar_magnet_plane_map, AS_PROGRAM, 8, cedar_magnet_plane_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM AM_SHARE("ram")
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_magnet_plane_io, AS_IO, 8, cedar_magnet_plane_device )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+
+ AM_RANGE(0xc0, 0xc3) AM_DEVREADWRITE("z80pio0", z80pio_device, read_alt, write_alt)
+ AM_RANGE(0xc4, 0xc7) AM_DEVREADWRITE("z80pio1", z80pio_device, read_alt, write_alt)
+
+ AM_RANGE(0xcc, 0xcc) AM_WRITE(plane_portcc_w)
+ AM_RANGE(0xcd, 0xcd) AM_WRITE(plane_portcd_w)
+ AM_RANGE(0xce, 0xce) AM_WRITE(plane_portce_w)
+ AM_RANGE(0xcf, 0xcf) AM_WRITE(plane_portcf_w)
+
+ADDRESS_MAP_END
+
+
+
+
+WRITE8_MEMBER(cedar_magnet_plane_device::plane_portcc_w)
+{
+ m_framebuffer[((m_curline&0xff)*0x100)+(m_lineoffset&0xff)] = data;
+
+ // counters simply wrap when they reach the maximum, don't move onto next row/colummn (confirmed by xain)
+ if (m_pio0_pa_data&0x01)
+ {
+ m_lineoffset++;
+ }
+ else
+ {
+ m_curline++;
+ }
+}
+
+WRITE8_MEMBER(cedar_magnet_plane_device::plane_portcd_w)
+{
+ m_lineoffset = data;
+}
+
+WRITE8_MEMBER(cedar_magnet_plane_device::plane_portce_w)
+{
+ m_curline = data;
+
+}
+
+WRITE8_MEMBER(cedar_magnet_plane_device::plane_portcf_w)
+{
+ // does it have a meaning or is it just some kind of watchdog?
+ m_cf_data = data;
+}
+
+static MACHINE_CONFIG_FRAGMENT( cedar_magnet_plane )
+ MCFG_CPU_ADD("planecpu", Z80,4000000)
+ MCFG_CPU_PROGRAM_MAP(cedar_magnet_plane_map)
+ MCFG_CPU_IO_MAP(cedar_magnet_plane_io)
+ MCFG_CPU_VBLANK_INT_DRIVER(":screen", cedar_magnet_board_device, irq)
+
+ MCFG_DEVICE_ADD("z80pio0", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+ MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_plane_device, pio0_pa_r))
+ MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_plane_device, pio0_pa_w))
+// MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_plane_device, pio0_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_plane_device, pio0_pb_w))
+
+ MCFG_DEVICE_ADD("z80pio1", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+// MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_plane_device, pio1_pa_r))
+ MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_plane_device, pio1_pa_w))
+// MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_plane_device, pio1_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_plane_device, pio1_pb_w))
+MACHINE_CONFIG_END
+
+
+READ8_MEMBER(cedar_magnet_plane_device::pio0_pa_r)
+{
+// this is read
+// logerror("%s: pio0_pa_r\n", machine().describe_context());
+ return 0x00;
+}
+
+
+WRITE8_MEMBER(cedar_magnet_plane_device::pio0_pa_w)
+{
+ m_pio0_pa_data = data;
+
+ // 7ex- 321d
+ //
+ // e = video enable
+ // d = draw direction
+ // x = done? gets set at end of each frame at least, but unlike video enable, also when video shouldn't be enabled
+ // 7 = always set?
+ // 321 = always set after startup?
+}
+
+WRITE8_MEMBER(cedar_magnet_plane_device::pio0_pb_w)
+{
+ m_pio0_pb_data = data;
+}
+
+WRITE8_MEMBER(cedar_magnet_plane_device::pio1_pa_w)
+{
+ m_scrollx = data;
+}
+
+WRITE8_MEMBER(cedar_magnet_plane_device::pio1_pb_w)
+{
+ m_scrolly = data;
+}
+
+machine_config_constructor cedar_magnet_plane_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( cedar_magnet_plane );
+}
+
+void cedar_magnet_plane_device::device_start()
+{
+ m_cpu = subdevice<z80_device>("planecpu");
+ m_ram = (UINT8*)memshare("ram")->ptr();
+ save_item(NAME(m_framebuffer));
+}
+
+UINT32 cedar_magnet_plane_device::draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int palbase)
+{
+ int count = 0;
+
+ if (!(m_pio0_pa_data & 0x40))
+ return 0;
+
+ for (int y = 0;y < 256;y++)
+ {
+ UINT16 *dst = &bitmap.pix16((y-m_scrolly)&0xff);
+
+ for (int x = 0; x < 256;x++)
+ {
+ UINT8 pix = m_framebuffer[count];
+ count++;
+
+ if (pix) dst[(x-m_scrollx)&0xff] = pix + palbase*0x100;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/mame/drivers/cedar_magnet_plane.h b/src/mame/drivers/cedar_magnet_plane.h
new file mode 100644
index 00000000000..fe5c481aa7d
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_plane.h
@@ -0,0 +1,59 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#pragma once
+
+#ifndef CEDAR_MAGNET_PLANE_DEF
+#define CEDAR_MAGNET_PLANE_DEF
+
+#include "cedar_magnet_board.h"
+
+extern const device_type CEDAR_MAGNET_PLANE;
+
+#define MCFG_CEDAR_MAGNET_PLANE_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, CEDAR_MAGNET_PLANE, 0)
+
+
+class cedar_magnet_plane_device : public cedar_magnet_board_device
+{
+public:
+ // construction/destruction
+ cedar_magnet_plane_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ8_MEMBER(pio0_pa_r);
+ DECLARE_WRITE8_MEMBER(pio0_pa_w);
+// DECLARE_READ8_MEMBER(pio0_pb_r);
+ DECLARE_WRITE8_MEMBER(pio0_pb_w);
+
+// DECLARE_READ8_MEMBER(pio1_pa_r);
+ DECLARE_WRITE8_MEMBER(pio1_pa_w);
+// DECLARE_READ8_MEMBER(pio1_pb_r);
+ DECLARE_WRITE8_MEMBER(pio1_pb_w);
+
+ DECLARE_WRITE8_MEMBER(plane_portcc_w);
+ DECLARE_WRITE8_MEMBER(plane_portcd_w);
+ DECLARE_WRITE8_MEMBER(plane_portce_w);
+ DECLARE_WRITE8_MEMBER(plane_portcf_w);
+
+ UINT8 m_framebuffer[0x10000];
+ int m_curline;
+ int m_lineoffset;
+
+ UINT8 m_pio0_pa_data;
+ UINT8 m_pio0_pb_data;
+ UINT8 m_scrollx;
+ UINT8 m_scrolly;
+ int m_direction;
+
+ UINT8 m_cd_data;
+ UINT8 m_cf_data;
+ UINT32 draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int palbase);
+protected:
+ virtual machine_config_constructor device_mconfig_additions() const override;
+ virtual void device_start() override;
+// virtual void device_reset() override;
+
+private:
+};
+
+#endif
diff --git a/src/mame/drivers/cedar_magnet_sound.cpp b/src/mame/drivers/cedar_magnet_sound.cpp
new file mode 100644
index 00000000000..a155762f13c
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_sound.cpp
@@ -0,0 +1,168 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+/*
+ THIS IMPLEMENTATION IS ENTIRELY INCORRECT AND SHOULD NOT BE TRUSTED!
+*/
+
+#include "emu.h"
+#include "cedar_magnet_sound.h"
+
+
+extern const device_type CEDAR_MAGNET_SOUND = &device_creator<cedar_magnet_sound_device>;
+
+
+cedar_magnet_sound_device::cedar_magnet_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : cedar_magnet_board_device(mconfig, CEDAR_MAGNET_SOUND, "Cedar Sound", tag, owner, clock, "cedmag_sound", __FILE__),
+ m_ctc0(*this, "ctc0"),
+ m_ctc1(*this, "ctc1")
+{
+}
+
+
+READ8_MEMBER(cedar_magnet_sound_device::top_port14_r)
+{
+// UINT8 ret = m_command;
+// m_command = 0;
+ return rand();
+}
+
+void cedar_magnet_sound_device::write_command(UINT8 data)
+{
+ m_command = data;
+ m_cpu->set_input_line(0, HOLD_LINE);
+}
+
+
+static ADDRESS_MAP_START( cedar_magnet_sound_map, AS_PROGRAM, 8, cedar_magnet_sound_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM AM_SHARE("ram")
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_magnet_sound_io, AS_IO, 8, cedar_magnet_sound_device )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+
+ AM_RANGE(0x00, 0x03) AM_DEVREADWRITE("ctc0", z80ctc_device, read, write)
+ AM_RANGE(0x04, 0x07) AM_DEVREADWRITE("ctc1", z80ctc_device, read, write)
+
+ AM_RANGE(0x0c, 0x0c) AM_DEVWRITE("aysnd0", ay8910_device, address_w)
+ AM_RANGE(0x0d, 0x0d) AM_DEVWRITE("aysnd0", ay8910_device, data_w)
+
+ AM_RANGE(0x10, 0x10) AM_DEVWRITE("aysnd1", ay8910_device, address_w)
+ AM_RANGE(0x11, 0x11) AM_DEVWRITE("aysnd1", ay8910_device, data_w)
+
+ AM_RANGE(0x14, 0x14) AM_READ(top_port14_r)
+
+ADDRESS_MAP_END
+
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc0_z0_w)
+{
+// printf("USED ctc0_z0_w %d\n", state);
+}
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc0_z1_w)
+{
+// printf("USED ctc0_z1_w %d\n", state);
+}
+
+
+// I don't think any of the below are used
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc1_z0_w)
+{
+ printf("ctc1_z0_w %d\n", state);
+}
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc1_z1_w)
+{
+ printf("ctc1_z1_w %d\n", state);
+}
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc1_z2_w)
+{
+ printf("ctc1_z2_w %d\n", state);
+}
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc0_z2_w)
+{
+ printf("ctc0_z2_w %d\n", state);
+}
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc0_int_w)
+{
+ //printf("ctc0_int_w %d\n", state);
+}
+
+WRITE_LINE_MEMBER(cedar_magnet_sound_device::ctc1_int_w)
+{
+/*
+ switch (rand()&0x1)
+ {
+ case 0x00:
+ m_ctc0->trg0(rand()&1);
+ break;
+ case 0x01:
+ m_ctc0->trg1(rand()&1);
+ break;
+ }
+*/
+}
+
+#if 0
+static const z80_daisy_config daisy_chain[] =
+{
+ { "ctc1" },
+ { "ctc0" },
+ { nullptr }
+};
+#endif
+
+static MACHINE_CONFIG_FRAGMENT( cedar_magnet_sound )
+ MCFG_CPU_ADD("topcpu", Z80,4000000)
+ MCFG_CPU_PROGRAM_MAP(cedar_magnet_sound_map)
+ MCFG_CPU_IO_MAP(cedar_magnet_sound_io)
+// MCFG_Z80_DAISY_CHAIN(daisy_chain)
+
+ MCFG_DEVICE_ADD("ctc0", Z80CTC, 4000000/8 )
+ MCFG_Z80CTC_INTR_CB(WRITELINE(cedar_magnet_sound_device, ctc0_int_w))
+ MCFG_Z80CTC_ZC0_CB(WRITELINE(cedar_magnet_sound_device, ctc0_z0_w))
+ MCFG_Z80CTC_ZC1_CB(WRITELINE(cedar_magnet_sound_device, ctc0_z1_w))
+ MCFG_Z80CTC_ZC2_CB(WRITELINE(cedar_magnet_sound_device, ctc0_z2_w))
+
+ MCFG_DEVICE_ADD("ctc1", Z80CTC, 4000000/8 )
+ MCFG_Z80CTC_INTR_CB(INPUTLINE("topcpu", INPUT_LINE_IRQ0))
+// MCFG_Z80CTC_INTR_CB(DEVWRITELINE("ctc0", z80ctc_device, trg0))
+ MCFG_Z80CTC_INTR_CB(WRITELINE(cedar_magnet_sound_device, ctc1_int_w))
+ MCFG_Z80CTC_ZC0_CB(WRITELINE(cedar_magnet_sound_device, ctc1_z0_w))
+ MCFG_Z80CTC_ZC1_CB(WRITELINE(cedar_magnet_sound_device, ctc1_z1_w))
+ MCFG_Z80CTC_ZC2_CB(WRITELINE(cedar_magnet_sound_device, ctc1_z2_w))
+
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ MCFG_SOUND_ADD("aysnd0", AY8910, 4000000/2)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.5)
+
+ MCFG_SOUND_ADD("aysnd1", AY8910, 4000000/2)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.5)
+
+ MCFG_SOUND_ADD("adpcm", MSM5205, 4000000/16)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
+
+MACHINE_CONFIG_END
+
+machine_config_constructor cedar_magnet_sound_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( cedar_magnet_sound );
+}
+
+void cedar_magnet_sound_device::device_start()
+{
+ m_cpu = subdevice<z80_device>("topcpu");
+ m_ram = (UINT8*)memshare("ram")->ptr();
+}
+
+void cedar_magnet_sound_device::device_reset()
+{
+ m_command = 0;
+ cedar_magnet_board_device::device_reset();
+}
diff --git a/src/mame/drivers/cedar_magnet_sound.h b/src/mame/drivers/cedar_magnet_sound.h
new file mode 100644
index 00000000000..909482ab059
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_sound.h
@@ -0,0 +1,53 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#pragma once
+
+#ifndef CEDAR_MAGNET_SOUND_DEF
+#define CEDAR_MAGNET_SOUND_DEF
+
+#include "cpu/z80/z80.h"
+#include "cpu/z80/z80daisy.h"
+#include "machine/z80ctc.h"
+#include "sound/ay8910.h"
+#include "sound/msm5205.h"
+#include "cedar_magnet_board.h"
+
+extern const device_type CEDAR_MAGNET_SOUND;
+
+#define MCFG_CEDAR_MAGNET_SOUND_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, CEDAR_MAGNET_SOUND, 0)
+
+
+class cedar_magnet_sound_device : public cedar_magnet_board_device
+{
+public:
+ // construction/destruction
+ cedar_magnet_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ required_device<z80ctc_device> m_ctc0;
+ required_device<z80ctc_device> m_ctc1;
+
+ DECLARE_READ8_MEMBER(top_port14_r);
+
+ void write_command(UINT8 data);
+ UINT8 m_command;
+
+ DECLARE_WRITE_LINE_MEMBER(ctc1_z0_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc1_z1_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc1_z2_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc0_z0_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc0_z1_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc0_z2_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc0_int_w);
+ DECLARE_WRITE_LINE_MEMBER(ctc1_int_w);
+
+protected:
+ virtual machine_config_constructor device_mconfig_additions() const override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+};
+
+#endif
diff --git a/src/mame/drivers/cedar_magnet_sprite.cpp b/src/mame/drivers/cedar_magnet_sprite.cpp
new file mode 100644
index 00000000000..e79b83faf84
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_sprite.cpp
@@ -0,0 +1,336 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+/*
+
+ todo: sometimes sprites get left onscreen (xain)
+
+*/
+#include "emu.h"
+#include "cedar_magnet_sprite.h"
+
+
+extern const device_type CEDAR_MAGNET_SPRITE = &device_creator<cedar_magnet_sprite_device>;
+
+
+cedar_magnet_sprite_device::cedar_magnet_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : cedar_magnet_board_device(mconfig, CEDAR_MAGNET_SPRITE, "Cedar Sprite", tag, owner, clock, "cedmag_sprite", __FILE__),
+ m_sprite_ram_bankdev(*this, "sp_sub_ram"),
+ m_pio0(*this, "z80pio0"),
+ m_pio1(*this, "z80pio1"),
+ m_pio2(*this, "z80pio2")
+{
+}
+
+static ADDRESS_MAP_START( cedar_magnet_sprite_sub_ram_map, AS_PROGRAM, 8, cedar_magnet_sprite_device )
+// these are 8x SIEMENS HYB 41256-15 AA - 262,144 bit DRAM (32kbytes)
+// these are on the sprite board memory sub-board
+ AM_RANGE(0x00000, 0x3ffff) AM_RAM AM_SHARE("ram")
+ADDRESS_MAP_END
+
+READ8_MEMBER(cedar_magnet_sprite_device::exzisus_hack_r)
+{
+ //printf("exzisus_hack_r\n");
+ int pc = m_cpu->pc();
+
+ // exzisus has startup code outside of the first 0x400 bytes
+ // but the main cpu only transfers 0x400 bytes of the code to the other banks?!
+ if ((pc >= 0x3e0) && (pc <= 0x800))
+ {
+ return m_ram[0x400 + offset];
+ }
+ else
+ {
+ return m_ram[0x400 + offset + (pio2_pb_data & 0x3)*0x10000];
+ }
+
+}
+
+
+static ADDRESS_MAP_START( cedar_magnet_sprite_map, AS_PROGRAM, 8, cedar_magnet_sprite_device )
+ AM_RANGE(0x00400, 0x007ff) AM_READ(exzisus_hack_r)
+
+ AM_RANGE(0x00000, 0x0ffff) AM_DEVICE("sp_sub_ram", address_map_bank_device, amap8)
+
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( cedar_magnet_sprite_io, AS_IO, 8, cedar_magnet_sprite_device )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+
+ AM_RANGE(0xc0, 0xc3) AM_DEVREADWRITE("z80pio0", z80pio_device, read_alt, write_alt)
+ AM_RANGE(0xc4, 0xc7) AM_DEVREADWRITE("z80pio1", z80pio_device, read_alt, write_alt)
+ AM_RANGE(0xc8, 0xcb) AM_DEVREADWRITE("z80pio2", z80pio_device, read_alt, write_alt)
+
+ AM_RANGE(0x80, 0x80) AM_WRITE(sprite_port80_w)
+ AM_RANGE(0x84, 0x84) AM_WRITE(sprite_port84_w)
+
+ AM_RANGE(0x88, 0x88) AM_WRITE(sprite_port88_w) // increasing values // upper address?
+
+ AM_RANGE(0x8c, 0x8c) AM_WRITE(sprite_port8c_w) // written after 88 (possible data upload?)
+
+ AM_RANGE(0x9c, 0x9c) AM_WRITE(sprite_port9c_w) // ?
+
+ADDRESS_MAP_END
+
+void cedar_magnet_sprite_device::do_blit()
+{
+// printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+// printf("~~~~~~~~~~~~~~~~~ drawing sprite with x:%02x y:%02x code:%04x size:%02x unk:%02x\n", m_loweraddr, m_upperaddr, (m_spritecodehigh << 8) | m_spritecodelow, m_spritesize, pio0_pb_data);
+// printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+
+ int ysize;
+ int xsize;
+ int erase = 0;
+
+ // bit 0x80 is always set
+ if ((m_spritesize & 0x7f) == 0x00)
+ ysize = xsize = 8;
+
+ if ((m_spritesize & 0x7f) == 0x01)
+ ysize = xsize = 16;
+
+ if ((m_spritesize & 0x7f) == 0x02)
+ ysize = xsize = 32;
+
+ if ((m_spritesize & 0x7f) == 0x03)
+ ysize = xsize = 64;
+
+ // m_spritesize
+ // pio0_pb_data
+
+
+ int source = (m_spritecodehigh << 8) | m_spritecodelow;
+
+ if (source == 0)
+ erase = 1;
+
+ source &= ~0x3f;
+
+ for (int y = 0;y < ysize;y++)
+ {
+ for (int x = 0;x < xsize;x++)
+ {
+ int xpos = (m_loweraddr + x);
+ int ypos = (m_upperaddr + y);
+
+ UINT8 data = m_ram[source + ((m_uppersprite & 0x3) * 0x10000)];
+
+ if (!(pio0_pb_data & 0x02))
+ data = rand();
+
+ source++;
+
+ xpos &= 0xff;
+
+ // without this some sprites incorrectly wraparound on the volcano table.
+ if (!erase)
+ {
+ if (!(pio0_pb_data & 0x40))
+ {
+ if (xpos >= 0xff-64)
+ continue;
+ }
+ else
+ {
+ if (xpos < 64)
+ continue;
+ }
+ }
+
+ //if ((ypos >= 0) && (ypos < 0x100))
+ ypos &= 0xff;
+
+ {
+ int offset = (ypos * 256) + xpos;
+
+ if (erase == 1)
+ {
+ m_framebuffer[offset] = 0;
+ }
+ else
+ {
+ if (data) m_framebuffer[offset] = data;
+ }
+ }
+ }
+ }
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::sprite_port80_w)
+{
+ m_spritecodelow = data;
+// printf("%s:sprite numlow / trigger %02x\n", machine().describe_context(), data);
+
+ do_blit();
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::sprite_port84_w)
+{
+ m_spritecodehigh = data;
+ m_high_write = 1;
+// printf("%s:sprite numhigh %02x\n", machine().describe_context(), data);
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::sprite_port88_w)
+{
+// frequent
+// printf("%s:sprite_y_coordinate %02x\n", machine().describe_context(), data);
+ m_upperaddr = data;
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::pio2_pa_w)
+{
+// frequent
+// printf("%s:sprite_x_coordinate %02x\n", machine().describe_context(), data);
+ m_loweraddr = data;
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::sprite_port8c_w)
+{
+ int address = (m_upperaddr << 8) | m_loweraddr;
+ m_framebuffer[address] = data;
+ if (data!=0x00) printf("sprite_port8c_w write %04x %02x\n", address, data);
+}
+
+// possible watchdog?
+WRITE8_MEMBER(cedar_magnet_sprite_device::sprite_port9c_w)
+{
+// printf("%s:sprite_port9c_w %02x\n", machine().describe_context(), data);
+}
+
+static MACHINE_CONFIG_FRAGMENT( cedar_magnet_sprite )
+ MCFG_CPU_ADD("spritecpu", Z80,4000000)
+ MCFG_CPU_PROGRAM_MAP(cedar_magnet_sprite_map)
+ MCFG_CPU_IO_MAP(cedar_magnet_sprite_io)
+ MCFG_CPU_VBLANK_INT_DRIVER(":screen", cedar_magnet_board_device, irq)
+
+ MCFG_DEVICE_ADD("z80pio0", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+ MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_sprite_device, pio0_pa_r))
+ MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_sprite_device, pio0_pa_w))
+// MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_sprite_device, pio0_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_sprite_device, pio0_pb_w))
+
+ MCFG_DEVICE_ADD("z80pio1", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+// MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_sprite_device, pio1_pa_r))
+ MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_sprite_device, pio1_pa_w))
+// MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_sprite_device, pio1_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_sprite_device, pio1_pb_w))
+
+ MCFG_DEVICE_ADD("z80pio2", Z80PIO, 4000000/2)
+// MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0))
+// MCFG_Z80PIO_IN_PA_CB(READ8(cedar_magnet_sprite_device, pio2_pa_r))
+ MCFG_Z80PIO_OUT_PA_CB(WRITE8(cedar_magnet_sprite_device, pio2_pa_w))
+// MCFG_Z80PIO_IN_PB_CB(READ8(cedar_magnet_sprite_device, pio2_pb_r))
+ MCFG_Z80PIO_OUT_PB_CB(WRITE8(cedar_magnet_sprite_device, pio2_pb_w))
+
+ MCFG_DEVICE_ADD("sp_sub_ram", ADDRESS_MAP_BANK, 0)
+ MCFG_DEVICE_PROGRAM_MAP(cedar_magnet_sprite_sub_ram_map)
+ MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
+ MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8)
+ MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(18)
+ MCFG_ADDRESS_MAP_BANK_STRIDE(0x10000)
+MACHINE_CONFIG_END
+
+
+READ8_MEMBER(cedar_magnet_sprite_device::pio0_pa_r)
+{
+// actually read
+// printf("%s: pio0_pa_r\n", machine().describe_context());
+ return 0x00;
+}
+
+
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::pio0_pa_w)
+{
+ m_spritesize = data;
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::pio0_pb_w)
+{
+ pio0_pb_data = data;
+ //printf("%s: pio0_pb_w %02x\n", machine().describe_context(), data);
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::pio1_pa_w)
+{
+ //printf("%s: pio1_pa_w %02x\n", machine().describe_context(), data);
+}
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::pio1_pb_w)
+{
+ //printf("%s: pio1_pb_w %02x\n", machine().describe_context(), data);
+}
+
+
+WRITE8_MEMBER(cedar_magnet_sprite_device::pio2_pb_w)
+{
+ // this feels like a hack
+ // the game writes here when creating the sprite list so that it can access the correct gfd data
+ // however the actual LIST data is always in bank 0 (it can't be in any other bank, those areas are occupied by actual gfx)
+ if (m_high_write)
+ {
+ m_uppersprite = data & 0x03;
+ m_high_write = 0;
+ return;
+
+ }
+
+ pio2_pb_data = data;
+ //printf("%s: ******************************************* BANK? **** pio2_pb_w %02x\n", machine().describe_context(), data);
+ // yes, it ends up banking the ram right out from under itself during startup execution...
+ // during this time the main cpu is waiting in a loop, after which it copies the startup code again, and reboots it.
+ m_sprite_ram_bankdev->set_bank(data & 0x03);
+}
+
+
+machine_config_constructor cedar_magnet_sprite_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( cedar_magnet_sprite );
+}
+
+void cedar_magnet_sprite_device::device_start()
+{
+ m_cpu = subdevice<z80_device>("spritecpu");
+ m_ram = (UINT8*)memshare("ram")->ptr();
+}
+
+
+
+void cedar_magnet_sprite_device::device_reset()
+{
+ halt_assert();
+ m_sprite_ram_bankdev->set_bank(0);
+ pio2_pb_data = 0x00;
+ m_spritesize = 0xff;
+}
+
+UINT32 cedar_magnet_sprite_device::draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int palbase)
+{
+// printf("-----------------------------------------------------------------------------------------------------------\n");
+// printf("--------------------------------------------- FRAME -------------------------------------------------------\n");
+// printf("-----------------------------------------------------------------------------------------------------------\n");
+
+ UINT8* mem = m_framebuffer;
+ int count = 0;
+
+// if (!(m_m_spritesize & 0x40))
+// return 0;
+
+ for (int y = 0;y < 256;y++)
+ {
+ UINT16 *dst = &bitmap.pix16((y)&0xff);
+
+ for (int x = 0; x < 256;x++)
+ {
+ UINT8 pix = mem[count];
+ count++;
+
+ if (pix) dst[(x)&0xff] = pix + palbase*0x100;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/mame/drivers/cedar_magnet_sprite.h b/src/mame/drivers/cedar_magnet_sprite.h
new file mode 100644
index 00000000000..52eeb88c042
--- /dev/null
+++ b/src/mame/drivers/cedar_magnet_sprite.h
@@ -0,0 +1,83 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#pragma once
+
+#ifndef CEDAR_MAGNET_SPRITE_DEF
+#define CEDAR_MAGNET_SPRITE_DEF
+
+#include "cpu/z80/z80.h"
+#include "cpu/z80/z80daisy.h"
+#include "machine/z80pio.h"
+#include "machine/bankdev.h"
+#include "cedar_magnet_board.h"
+
+extern const device_type CEDAR_MAGNET_SPRITE;
+
+#define MCFG_CEDAR_MAGNET_SPRITE_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, CEDAR_MAGNET_SPRITE, 0)
+
+
+class cedar_magnet_sprite_device : public cedar_magnet_board_device
+{
+public:
+ // construction/destruction
+ cedar_magnet_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ UINT8 m_framebuffer[0x10000];
+ UINT8 pio2_pb_data;
+
+ required_device<address_map_bank_device> m_sprite_ram_bankdev;
+
+ DECLARE_READ8_MEMBER(pio0_pa_r);
+ DECLARE_WRITE8_MEMBER(pio0_pa_w);
+// DECLARE_READ8_MEMBER(pio0_pb_r);
+ DECLARE_WRITE8_MEMBER(pio0_pb_w);
+
+// DECLARE_READ8_MEMBER(pio1_pa_r);
+ DECLARE_WRITE8_MEMBER(pio1_pa_w);
+// DECLARE_READ8_MEMBER(pio1_pb_r);
+ DECLARE_WRITE8_MEMBER(pio1_pb_w);
+
+// DECLARE_READ8_MEMBER(pio2_pa_r);
+ DECLARE_WRITE8_MEMBER(pio2_pa_w);
+// DECLARE_READ8_MEMBER(pio2_pb_r);
+ DECLARE_WRITE8_MEMBER(pio2_pb_w);
+
+ DECLARE_WRITE8_MEMBER(sprite_port80_w);
+ DECLARE_WRITE8_MEMBER(sprite_port84_w);
+ DECLARE_WRITE8_MEMBER(sprite_port88_w);
+ DECLARE_WRITE8_MEMBER(sprite_port8c_w);
+ DECLARE_WRITE8_MEMBER(sprite_port9c_w);
+
+ DECLARE_READ8_MEMBER(exzisus_hack_r);
+
+ UINT8 m_upperaddr;
+ UINT8 m_loweraddr;
+
+ void do_blit();
+
+ UINT8 m_spritesize;
+ UINT8 pio0_pb_data;
+ UINT8 m_spritecodelow;
+ UINT8 m_spritecodehigh;
+
+ int m_high_write;
+ UINT8 m_uppersprite;
+
+ INTERRUPT_GEN_MEMBER(irq);
+
+ required_device<z80pio_device> m_pio0;
+ required_device<z80pio_device> m_pio1;
+ required_device<z80pio_device> m_pio2;
+
+ UINT32 draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int palbase);
+protected:
+ virtual machine_config_constructor device_mconfig_additions() const override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+};
+
+#endif
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 242d9e28796..4c92e70c3f6 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -9278,6 +9278,12 @@ quizard4 // (c) TAB Austria 1998
quizard4_40 // (c) TAB Austria 1997
quizard4_41 // (c) TAB Austria 1998
+@source:cedar_magnet.cpp
+cedmag
+mag_time
+mag_exzi
+mag_xain
+
@source:centiped.cpp
bullsdrt // (c) 1985 Shinkai
caterplr // (bootleg)