summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/rad_eu3a05.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/rad_eu3a05.cpp')
-rw-r--r--src/mame/drivers/rad_eu3a05.cpp1203
1 files changed, 1203 insertions, 0 deletions
diff --git a/src/mame/drivers/rad_eu3a05.cpp b/src/mame/drivers/rad_eu3a05.cpp
new file mode 100644
index 00000000000..6c2f11ad3af
--- /dev/null
+++ b/src/mame/drivers/rad_eu3a05.cpp
@@ -0,0 +1,1203 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood, R.Belmont
+
+/*
+ Radica Games 6502 based 'TV Game' hardware
+
+ These use a 6502 derived CPU under a glob
+ The CPU die is marked 'ELAN EU3A05'
+
+ There is a second glob surrounded by TSOP48 pads
+ this contains the ROM
+
+ Space Invaders uses a 3rd glob marked
+ AMIC (C) (M) 1998-1 AM3122A
+ this is presumably for the bitmap layer on Qix
+
+ --
+ Known games on this hardare
+
+ Tetris
+ Space Invaders
+
+ ---
+ Other games that might be on this hardware
+
+ Skateboarding
+ + some of the earlier PlayTV games (not Soccer, that's XaviX, see xavix.cpp)
+
+ ---
+ The XaviX ones seem to have a XaviX logo on the external packaging while the
+ ones for this driver don't seem to have any specific marking.
+
+
+ Notes:
+
+ To access internal test on Tetris hold P1 Down + P1 Anticlockwise (Button 2) on boot
+ There appears to be a similar mode for Invaders but I don't know if it's accessible
+
+
+ RAM 0xa0 and 0xa1 contain the ACD0 and AD1 values and player 2 controls if between
+ certain values? probably read via serial??
+
+ Custom Interrupt purposes
+
+ TETRIS
+
+ ffb0
+ nothing of note?
+
+ ffb4
+ stuff with 500e, 500c and 500d
+
+ ffb8
+ stuff with 50a4 / 50a5 / 50a6 and memory address e2
+
+ ffbc
+ stuff with 50a4 / 50a5 / 50a6 and memory address e2 (similar to above, different bits)
+
+ ffc0 - doesn't exist
+ ffc4 - doesn't exist
+ ffc8 - doesn't exist
+ ffd0 - doesn't exist
+
+ ffd4
+ main irq?
+
+ ffd8
+ jumps straight to an rti
+
+ ffdc
+ accesses 501d / 501b
+
+ SPACE INVADERS
+
+ ffb0
+ rti
+
+ ffb4
+ rti
+
+ ffb8
+ rti
+
+ ffbc
+ decreases 301 bit 02
+ stuff wit 50a5
+
+ ffc0
+ decreases 302
+ stuff with 50a5 bit 04
+
+ ffc4
+ decreases 303
+ stuff with 50a5 bit 08
+
+ ffc8
+ decreases 304
+ stuff with 50a5 bit 10
+
+ ffcc
+ uses 307
+ stuff with 50a5 bit 20
+
+ ffd0
+ dead loop
+
+ ffd4
+ main interrupt
+
+ ffd8
+ dead loop
+
+ ffdc
+ dead loop
+
+ ffe0
+ dead loop
+
+ ffe4
+ rti
+
+ ffe8
+ dead loop
+
+ ffec
+ dead loop
+
+
+ Flaws (NOT emulation bugs, happen on hardware):
+ --
+
+ In QIX the sprites lag behind the line drawing, so you see the line infront of your player until you stop moving
+
+ In Space Invaders the UFO can sometimes glitch for a frame when appearing, and wraps around at the edges
+ (even if the hardware supports having higher priority tiles to prevent this, as used by Lunar Rescue, it isn't
+ used here)
+
+ Colony 7 has a typo in the instructions
+
+ The fake 'colour band' effect does not apply to the thruster (and several other elements) in Lunar Rescue
+
+ Enemies in Phoenix are rendered above the score panel
+
+ The 200pt right facing bird on the Phoenix score table is corrupt
+
+ Uncertain (to check)
+
+ Space Invaders seems to be using a darker than expected palette, there are lighter colours in the palette but
+ they don't seem to be used. It's difficult to judge from hardware videos, although it definitely isn't as
+ white as the menu, so this might also be a non-bug.
+
+
+*/
+
+#include "emu.h"
+#include "cpu/m6502/m6502.h"
+//#include "cpu/m6502/m65c02.h"
+#include "screen.h"
+#include "speaker.h"
+#include "machine/bankdev.h"
+#include "audio/rad_eu3a05.h"
+#include "machine/rad_eu3a05gpio.h"
+
+class radica_eu3a05_state : public driver_device
+{
+public:
+ radica_eu3a05_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_ram(*this, "ram"),
+ m_vram(*this, "vram"),
+ m_spriteram(*this, "spriteram"),
+ m_palram(*this, "palram"),
+ m_pixram(*this, "pixram"),
+ m_bank(*this, "bank"),
+ m_gfxdecode(*this, "gfxdecode"),
+ m_palette(*this, "palette")
+ { }
+
+ // screen updates
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ // system
+ DECLARE_READ8_MEMBER(radicasi_5003_r);
+ DECLARE_READ8_MEMBER(radicasi_pal_ntsc_r);
+ DECLARE_READ8_MEMBER(radicasi_rombank_lo_r);
+ DECLARE_WRITE8_MEMBER(radicasi_rombank_lo_w);
+ DECLARE_WRITE8_MEMBER(radicasi_rombank_hi_w);
+
+
+ // DMA
+ DECLARE_WRITE8_MEMBER(radicasi_dmasrc_lo_w);
+ DECLARE_WRITE8_MEMBER(radicasi_dmasrc_md_w);
+ DECLARE_WRITE8_MEMBER(radicasi_dmasrc_hi_w);
+ DECLARE_READ8_MEMBER(radicasi_dmasrc_lo_r);
+ DECLARE_READ8_MEMBER(radicasi_dmasrc_md_r);
+ DECLARE_READ8_MEMBER(radicasi_dmasrc_hi_r);
+ DECLARE_WRITE8_MEMBER(radicasi_dmadst_lo_w);
+ DECLARE_WRITE8_MEMBER(radicasi_dmadst_hi_w);
+ DECLARE_READ8_MEMBER(radicasi_dmadst_lo_r);
+ DECLARE_READ8_MEMBER(radicasi_dmadst_hi_r);
+ DECLARE_WRITE8_MEMBER(radicasi_dmasize_lo_w);
+ DECLARE_WRITE8_MEMBER(radicasi_dmasize_hi_w);
+ DECLARE_READ8_MEMBER(radicasi_dmasize_lo_r);
+ DECLARE_READ8_MEMBER(radicasi_dmasize_hi_r);
+ DECLARE_READ8_MEMBER(radicasi_dmatrg_r);
+ DECLARE_WRITE8_MEMBER(radicasi_dmatrg_w);
+
+ // VIDEO
+ // tile bases
+ DECLARE_WRITE8_MEMBER(radicasi_tile_gfxbase_lo_w);
+ DECLARE_WRITE8_MEMBER(radicasi_tile_gfxbase_hi_w);
+ DECLARE_READ8_MEMBER(radicasi_tile_gfxbase_lo_r);
+ DECLARE_READ8_MEMBER(radicasi_tile_gfxbase_hi_r);
+ // sprite tile bases
+ DECLARE_WRITE8_MEMBER(radicasi_sprite_gfxbase_lo_w);
+ DECLARE_WRITE8_MEMBER(radicasi_sprite_gfxbase_hi_w);
+ DECLARE_READ8_MEMBER(radicasi_sprite_gfxbase_lo_r);
+ DECLARE_READ8_MEMBER(radicasi_sprite_gfxbase_hi_r);
+
+ DECLARE_WRITE8_MEMBER(radicasi_vidctrl_w);
+
+ DECLARE_READ8_MEMBER(radicasi_sprite_bg_scroll_r);
+ DECLARE_WRITE8_MEMBER(radicasi_sprite_bg_scroll_w);
+
+ // more sound regs?
+ DECLARE_READ8_MEMBER(radicasi_50a9_r);
+ DECLARE_WRITE8_MEMBER(radicasi_50a9_w);
+
+ INTERRUPT_GEN_MEMBER(interrupt);
+
+ DECLARE_READ8_MEMBER(radicasi_nmi_vector_r);
+ DECLARE_READ8_MEMBER(radicasi_irq_vector_r);
+
+ // for callback
+ DECLARE_READ8_MEMBER(read_full_space);
+
+ void radicasi(machine_config &config);
+
+protected:
+ // driver_device overrides
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ virtual void video_start() override;
+
+private:
+ required_device<cpu_device> m_maincpu;
+ required_shared_ptr<uint8_t> m_ram;
+ required_shared_ptr<uint8_t> m_vram;
+ required_shared_ptr<uint8_t> m_spriteram;
+ required_shared_ptr<uint8_t> m_palram;
+ required_shared_ptr<uint8_t> m_pixram;
+ required_device<address_map_bank_device> m_bank;
+ required_device<gfxdecode_device> m_gfxdecode;
+ required_device<palette_device> m_palette;
+
+ uint8_t m_rombank_hi;
+ uint8_t m_rombank_lo;
+ uint8_t m_vidctrl;
+ uint8_t m_50a9_data;
+
+ uint8_t m_dmasrc_lo_data;
+ uint8_t m_dmasrc_md_data;
+ uint8_t m_dmasrc_hi_data;
+ uint8_t m_dmadst_lo_data;
+ uint8_t m_dmadst_hi_data;
+ uint8_t m_dmasize_lo_data;
+ uint8_t m_dmasize_hi_data;
+
+ uint8_t m_tile_gfxbase_lo_data;
+ uint8_t m_tile_gfxbase_hi_data;
+
+ uint8_t m_sprite_gfxbase_lo_data;
+ uint8_t m_sprite_gfxbase_hi_data;
+
+ uint8_t m_bg_scroll[2];
+
+ int m_custom_irq;
+ int m_custom_nmi;
+ uint16_t m_custom_irq_vector;
+ uint16_t m_custom_nmi_vector;
+
+ bool get_tile_data(int base, int drawpri, int& tile, int &attr, int &unk2);
+ void draw_tilemaps(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int drawpri);
+ void draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+};
+
+void radica_eu3a05_state::video_start()
+{
+}
+
+/* (m_tile_gfxbase_lo_data | (m_tile_gfxbase_hi_data << 8)) * 0x100
+ gives you the actual rom address, everything references the 3MByte - 4MByte region, like the banking so
+ the system can probably have up to a 4MByte rom, all games we have so far just use the upper 1MByte of
+ that space (Tetris seems to rely on mirroring? as it sets all addresses up for the lower 1MB instead)
+*/
+
+void radica_eu3a05_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ address_space& fullbankspace = m_bank->space(AS_PROGRAM);
+
+ /*
+ Sprites
+ AA yy xx cc XX YY aa bb
+
+ yy = y position
+ xx = x position
+ XX = texture x start
+ YY = texture y start
+ aa = same as unk2 on tiles
+ bb = sometimes set in invaders
+ cc = same as attr on tiles (colour / priority?)
+
+ AA = attributes
+ e--- fFsS
+ e = enable
+ S = SizeX
+ s = SizeY
+ F = FlipX
+ f = FlipY (assumed, not seen)
+
+ */
+
+ for (int i = 0; i < 512; i += 8)
+ {
+ uint8_t x = m_spriteram[i + 2];
+ uint8_t y = m_spriteram[i + 1];
+
+ /*
+ Space Invaders draws the player base with this specific y value before the start of each life
+ and expects it to NOT wrap around. There are no high priority tiles or anything else to hide
+ and it doesn't appear on real hardware.
+
+ it's possible sprites don't wrap around at all (but then you couldn't have smooth entry at the
+ top of the screen - there are no extra y co-ordinate bits. However there would have been easier
+ ways to hide this tho as there are a bunch of unseen lines at the bottom of the screen anyway!
+
+ needs further investigation.
+ */
+ if (y==255)
+ continue;
+
+ uint8_t tex_x = m_spriteram[i + 4];
+ uint8_t tex_y = m_spriteram[i + 5];
+
+ uint8_t flags = m_spriteram[i + 0];
+ uint8_t attr = m_spriteram[i + 3];
+ uint8_t unk2 = m_spriteram[i + 6];
+
+
+ //int priority = attr & 0x0f;
+ int colour = attr & 0xf0;
+
+ // ? game select has this set to 0xff, but clearly doesn't want the palette to change!
+ // while in Space Invaders this has to be palette for the UFO to be red.
+ if (colour & 0x80)
+ colour = 0;
+
+ int transpen = 0;
+
+ /* HACK - how is this calculated
+ phoenix and the game select need it like this
+ it isn't a simple case of unk2 being transpen either because Qix has some elements set to 0x07
+ where the transpen needs to be 0x00 and Space Invaders has it set to 0x04
+ it could be a global register rather than something in the spritelist?
+ */
+ if ((attr == 0xff) && (unk2 == 0xff))
+ transpen = 0xff;
+
+
+ if (!(flags & 0x80))
+ continue;
+
+ int sizex = 8;
+ int sizey = 8;
+
+ if (flags & 0x01)
+ {
+ sizex = 16;
+ }
+
+ if (flags & 0x02)
+ {
+ sizey = 16;
+ }
+
+ int base = (m_sprite_gfxbase_lo_data | (m_sprite_gfxbase_hi_data << 8)) * 0x100;
+
+ for (int yy = 0; yy < sizey; yy++)
+ {
+ uint16_t* row;
+
+ if (flags & 0x08) // guess flipy
+ {
+ row = &bitmap.pix16((y + (sizey - 1 - yy)) & 0xff);
+ }
+ else
+ {
+ row = &bitmap.pix16((y + yy) & 0xff);
+ }
+
+ for (int xx = 0; xx < sizex; xx++)
+ {
+ int realaddr = base + ((tex_x + xx) & 0xff);
+ realaddr += ((tex_y + yy) & 0xff) * 256;
+
+ uint8_t pix = fullbankspace.read_byte(realaddr);
+
+ if (pix != transpen)
+ {
+ if (flags & 0x04) // flipx
+ {
+ row[(x + (sizex - 1 - xx)) & 0xff] = (pix + ((colour & 0x70) << 1)) & 0xff;
+ }
+ else
+ {
+ row[(x + xx) & 0xff] = (pix + ((colour & 0x70) << 1)) & 0xff;
+ }
+ }
+ }
+ }
+ }
+}
+
+double hue2rgb(double p, double q, double t)
+{
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6.0f) return p + (q - p) * 6 * t;
+ if (t < 1 / 2.0f) return q;
+ if (t < 2 / 3.0f) return p + (q - p) * (2 / 3.0f - t) * 6;
+ return p;
+}
+
+// a hacky mess for now
+bool radica_eu3a05_state::get_tile_data(int base, int drawpri, int& tile, int &attr, int &unk2)
+{
+ tile = m_vram[base * 4] + (m_vram[(base * 4) + 1] << 8);
+
+ // these seem to be the basically the same as attr/unk2 in the sprites, which also make
+ // very little sense.
+ attr = m_vram[(base * 4) + 2];
+ unk2 = m_vram[(base * 4) + 3];
+
+ /* hack for phoenix title screens.. the have attr set to 0x3f which change the colour bank in ways we don't want
+ and also by our interpretation of 0x0f bits sets the tiles to priority over sprites (although in this case
+ that might tbe ok, because it looks like the sprites also have that set */
+ if (unk2 == 0x07)
+ attr = 0;
+
+ int priority = attr & 0x0f;
+
+ // likely wrong
+ if ((drawpri == 0 && priority == 0x0f) || (drawpri == 1 && priority != 0x0f))
+ return false;
+
+ return true;
+}
+void radica_eu3a05_state::draw_tilemaps(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int drawpri)
+{
+ /* this doesn't handle 8x8 4bpp, haven't seen it used
+ nor is there support for horizontal scrolling etc.
+ for the same reasons
+ */
+
+ int scroll = (m_bg_scroll[1] << 8) | m_bg_scroll[0];
+ address_space& fullbankspace = m_bank->space(AS_PROGRAM);
+
+ if (m_vidctrl & 0x40) // 16x16 tiles
+ {
+ int startrow = (scroll >> 4) & 0x1f;
+
+ for (int y = 0; y < 16; y++)
+ {
+ for (int x = 0; x < 16; x++)
+ {
+ int realstartrow = (startrow + y);
+
+ if (realstartrow >= 28)
+ realstartrow -= 28;
+
+ int base = (((realstartrow + y) & 0x1f) * 8) + x;
+
+ int tile,attr,unk2;
+
+ if (!get_tile_data(base, drawpri, tile,attr,unk2))
+ continue;
+
+ int colour = attr & 0xf0;
+
+ if (m_vidctrl & 0x20) // 4bpp mode
+ {
+ tile = (tile & 0xf) + ((tile & ~0xf) * 16);
+ tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5);
+ }
+ else
+ {
+ tile = (tile & 0xf) + ((tile & ~0xf) * 16);
+ tile <<= 1;
+
+ tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ int drawline = (y * 16) + i;
+ drawline -= scroll & 0xf;
+
+ if ((drawline >= 0) && (drawline < 256))
+ {
+ uint16_t* row = &bitmap.pix16(drawline);
+
+ if (m_vidctrl & 0x20) // 4bpp
+ {
+ for (int xx = 0; xx < 16; xx += 2)
+ {
+ int realaddr = ((tile + i * 16) << 3) + (xx >> 1);
+ uint8_t pix = fullbankspace.read_byte(realaddr);
+ row[x * 16 + xx + 0] = ((pix & 0xf0) >> 4) + colour;
+ row[x * 16 + xx + 1] = ((pix & 0x0f) >> 0) + colour;
+ }
+ }
+ else // 8bpp
+ {
+ for (int xx = 0; xx < 16; xx++)
+ {
+ int realaddr = ((tile + i * 32) << 3) + xx;
+ uint8_t pix = fullbankspace.read_byte(realaddr);
+ row[x * 16 + xx] = (pix + ((colour & 0x70) << 1)) & 0xff;
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ else // 8x8 tiles
+ {
+ // Phoenix scrolling actually skips a pixel, jumping from 0x001 to 0x1bf, scroll 0x000 isn't used, maybe it has other meanings?
+
+ int startrow = (scroll >> 3) & 0x3f;
+
+ for (int y = 0; y < 32; y++)
+ {
+ for (int x = 0; x < 32; x++)
+ {
+ int realstartrow = (startrow + y);
+
+ if (realstartrow >= 56)
+ realstartrow -= 56;
+
+ int base = (((realstartrow) & 0x3f) * 32) + x;
+
+ int tile,attr,unk2;
+
+ if (!get_tile_data(base, drawpri, tile,attr,unk2))
+ continue;
+
+ int colour = attr & 0xf0;
+
+ tile = (tile & 0x1f) + ((tile & ~0x1f) * 8);
+ tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5);
+
+ for (int i = 0; i < 8; i++)
+ {
+ int drawline = (y * 8) + i;
+ drawline -= scroll & 0x7;
+
+ if ((drawline >= 0) && (drawline < 256))
+ {
+ uint16_t* row = &bitmap.pix16(drawline);
+
+ for (int xx = 0; xx < 8; xx++)
+ {
+ int realaddr = ((tile + i * 32) << 3) + xx;
+ uint8_t pix = fullbankspace.read_byte(realaddr);
+ row[x * 8 + xx] = (pix + ((colour & 0x70) << 1)) & 0xff;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+uint32_t radica_eu3a05_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ bitmap.fill(0, cliprect);
+
+ // Palette
+ int offs = 0;
+ for (int index = 0;index < 256; index++)
+ {
+ uint16_t dat = m_palram[offs++] << 8;
+ dat |= m_palram[offs++];
+
+ // llll lsss ---h hhhh
+ int l_raw = (dat & 0xf800) >> 11;
+ int sl_raw = (dat & 0x0700) >> 8;
+ int h_raw = (dat & 0x001f) >> 0;
+
+ double l = (double)l_raw / 31.0f;
+ double s = (double)sl_raw / 7.0f;
+ double h = (double)h_raw / 24.0f;
+
+ double r, g, b;
+
+ if (s == 0) {
+ r = g = b = l; // greyscale
+ } else {
+ double q = l < 0.5f ? l * (1 + s) : l + s - l * s;
+ double p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3.0f);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1/3.0f);
+ }
+
+ int r_real = r * 255.0f;
+ int g_real = g * 255.0f;
+ int b_real = b * 255.0f;
+
+ m_palette->set_pen_color(index, r_real, g_real, b_real);
+ }
+
+
+ draw_tilemaps(screen,bitmap,cliprect,0);
+ draw_sprites(screen,bitmap,cliprect);
+ draw_tilemaps(screen,bitmap,cliprect,1);
+
+ return 0;
+}
+
+// System control
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_rombank_hi_w)
+{
+ // written with the banking?
+ //logerror("%s: radicasi_rombank_hi_w (set ROM bank) %02x\n", machine().describe_context(), data);
+ m_rombank_hi = data;
+
+ m_bank->set_bank(m_rombank_lo | (m_rombank_hi << 8));
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_rombank_lo_w)
+{
+ //logerror("%s: radicasi_rombank_lo_w (select ROM bank) %02x\n", machine().describe_context(), data);
+ m_rombank_lo = data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_rombank_lo_r)
+{
+ return m_rombank_lo;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_pal_ntsc_r)
+{
+ // how best to handle this, we probably need to run the PAL machine at 50hz
+ // the text under the radica logo differs between regions
+ logerror("%s: radicasi_pal_ntsc_r (region + more?)\n", machine().describe_context());
+ return 0xff; // NTSC
+ //return 0x00; // PAL
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_5003_r)
+{
+ /* masked with 0x0f, 0x01 or 0x03 depending on situation..
+
+ I think it might just be an RNG because if you return 0x00
+ your shots can never hit the stage 3 enemies in Phoenix and
+ if you return 0xff they always hit. On the real hardware it
+ seems to be random. Could also just be a crude frame counter
+ used for the same purpose.
+
+ */
+
+ logerror("%s: radicasi_5003_r (RNG?)\n", machine().describe_context());
+
+ return machine().rand();
+}
+
+
+// Video device
+
+// Tile bases
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_tile_gfxbase_lo_w)
+{
+ //logerror("%s: radicasi_tile_gfxbase_lo_w (select GFX base lower) %02x\n", machine().describe_context(), data);
+ m_tile_gfxbase_lo_data = data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_tile_gfxbase_hi_w)
+{
+ //logerror("%s: radicasi_tile_gfxbase_hi_w (select GFX base upper) %02x\n", machine().describe_context(), data);
+ m_tile_gfxbase_hi_data = data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_tile_gfxbase_lo_r)
+{
+ //logerror("%s: radicasi_tile_gfxbase_lo_r (GFX base lower)\n", machine().describe_context());
+ return m_tile_gfxbase_lo_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_tile_gfxbase_hi_r)
+{
+ //logerror("%s: radicasi_tile_gfxbase_hi_r (GFX base upper)\n", machine().describe_context());
+ return m_tile_gfxbase_hi_data;
+}
+
+// Sprite Tile bases
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_sprite_gfxbase_lo_w)
+{
+ //logerror("%s: radicasi_sprite_gfxbase_lo_w (select Sprite GFX base lower) %02x\n", machine().describe_context(), data);
+ m_sprite_gfxbase_lo_data = data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_sprite_gfxbase_hi_w)
+{
+ //logerror("%s: radicasi_sprite_gfxbase_hi_w (select Sprite GFX base upper) %02x\n", machine().describe_context(), data);
+ m_sprite_gfxbase_hi_data = data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_sprite_gfxbase_lo_r)
+{
+ //logerror("%s: radicasi_sprite_gfxbase_lo_r (Sprite GFX base lower)\n", machine().describe_context());
+ return m_sprite_gfxbase_lo_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_sprite_gfxbase_hi_r)
+{
+ //logerror("%s: radicasi_sprite_gfxbase_hi_r (Sprite GFX base upper)\n", machine().describe_context());
+ return m_sprite_gfxbase_hi_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_sprite_bg_scroll_r)
+{
+ return m_bg_scroll[offset];
+
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_sprite_bg_scroll_w)
+{
+ m_bg_scroll[offset] = data;
+}
+
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_vidctrl_w)
+{
+ logerror("%s: radicasi_vidctrl_w %02x (video control?)\n", machine().describe_context(), data);
+ /*
+ c3 8bpp 16x16 1100 0011
+ e3 4bpp 16x16 1110 0011
+ 83 8bpp 8x8 1000 0011
+ 02 8bpp 8x8 (phoenix) 0000 0010
+ */
+ m_vidctrl = data;
+}
+
+// DMA device
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmasrc_lo_w)
+{
+ logerror("%s: radicasi_dmasrc_lo_w (select DMA source low) %02x\n", machine().describe_context(), data);
+ m_dmasrc_lo_data = data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmasrc_md_w)
+{
+ logerror("%s: radicasi_dmasrc_md_w (select DMA source middle) %02x\n", machine().describe_context(), data);
+ m_dmasrc_md_data = data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmasrc_hi_w)
+{
+ logerror("%s: radicasi_dmasrc_hi_w (select DMA source upper) %02x\n", machine().describe_context(), data);
+ m_dmasrc_hi_data = data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmasrc_lo_r)
+{
+ logerror("%s: radicasi_dmasrc_lo_r (DMA source low)\n", machine().describe_context());
+ return m_dmasrc_lo_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmasrc_md_r)
+{
+ logerror("%s: radicasi_dmasrc_md_r (DMA source middle)\n", machine().describe_context());
+ return m_dmasrc_md_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmasrc_hi_r)
+{
+ logerror("%s: radicasi_dmasrc_hi_r (DMA source upper)\n", machine().describe_context());
+ return m_dmasrc_hi_data;
+}
+
+
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmadst_lo_w)
+{
+ logerror("%s: radicasi_dmadst_lo_w (select DMA Dest lower) %02x\n", machine().describe_context(), data);
+ m_dmadst_lo_data = data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmadst_hi_w)
+{
+ logerror("%s: radicasi_dmadst_hi_w (select DMA Dest upper) %02x\n", machine().describe_context(), data);
+ m_dmadst_hi_data = data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmadst_lo_r)
+{
+ logerror("%s: radicasi_dmadst_lo_r (DMA Dest lower)\n", machine().describe_context());
+ return m_dmadst_lo_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmadst_hi_r)
+{
+ logerror("%s: radicasi_dmadst_hi_r (DMA Dest upper)\n", machine().describe_context());
+ return m_dmadst_hi_data;
+}
+
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmasize_lo_w)
+{
+ logerror("%s: radicasi_dmasize_lo_w (select DMA Size lower) %02x\n", machine().describe_context(), data);
+ m_dmasize_lo_data = data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmasize_hi_w)
+{
+ logerror("%s: radicasi_dmasize_hi_w (select DMA Size upper) %02x\n", machine().describe_context(), data);
+ m_dmasize_hi_data = data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmasize_lo_r)
+{
+ logerror("%s: radicasi_dmasize_lo_r (DMA Size lower)\n", machine().describe_context());
+ return m_dmasize_lo_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmasize_hi_r)
+{
+ logerror("%s: radicasi_dmasize_hi_r (DMA Size upper)\n", machine().describe_context());
+ return m_dmasize_hi_data;
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_dmatrg_r)
+{
+ logerror("%s: radicasi_dmatrg_r (DMA operation state?)\n", machine().describe_context());
+ return 0x00;//m_dmatrg_data;
+}
+
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_dmatrg_w)
+{
+ logerror("%s: radicasi_dmatrg_w (trigger DMA operation) %02x\n", machine().describe_context(), data);
+ //m_dmatrg_data = data;
+
+ address_space& fullbankspace = m_bank->space(AS_PROGRAM);
+ address_space& destspace = m_maincpu->space(AS_PROGRAM);
+
+ if (data)
+ {
+ int src = (m_dmasrc_lo_data << 0) | (m_dmasrc_md_data << 8) | (m_dmasrc_hi_data << 16);
+ uint16_t dest = (m_dmadst_lo_data | (m_dmadst_hi_data << 8));
+ uint16_t size = (m_dmasize_lo_data | (m_dmasize_hi_data << 8));
+
+ logerror(" Doing DMA %06x to %04x size %04x\n", src, dest, size);
+
+ for (int i = 0; i < size; i++)
+ {
+ uint8_t dat = fullbankspace.read_byte(src + i);
+ destspace.write_byte(dest + i, dat);
+ }
+ }
+}
+
+
+
+
+// probably also sound device, maybe for forcing channels to stop?
+READ8_MEMBER(radica_eu3a05_state::radicasi_50a9_r)
+{
+ logerror("%s: radicasi_50a9_r\n", machine().describe_context());
+ return m_50a9_data;
+}
+
+WRITE8_MEMBER(radica_eu3a05_state::radicasi_50a9_w)
+{
+ logerror("%s: radicasi_50a9_w %02x\n", machine().describe_context(), data);
+ m_50a9_data = data;
+}
+
+// sound callback
+READ8_MEMBER(radica_eu3a05_state::read_full_space)
+{
+ address_space& fullbankspace = m_bank->space(AS_PROGRAM);
+ return fullbankspace.read_byte(offset);
+}
+
+static ADDRESS_MAP_START( radicasi_map, AS_PROGRAM, 8, radica_eu3a05_state )
+ // can the addresses move around?
+ AM_RANGE(0x0000, 0x05ff) AM_RAM AM_SHARE("ram")
+ AM_RANGE(0x0600, 0x3dff) AM_RAM AM_SHARE("vram")
+ AM_RANGE(0x3e00, 0x3fff) AM_RAM AM_SHARE("spriteram")
+ AM_RANGE(0x4800, 0x49ff) AM_RAM AM_SHARE("palram")
+
+ // 500x system regs?
+ AM_RANGE(0x5003, 0x5003) AM_READ(radicasi_5003_r)
+ AM_RANGE(0x500b, 0x500b) AM_READ(radicasi_pal_ntsc_r) // PAL / NTSC flag at least
+ AM_RANGE(0x500c, 0x500c) AM_WRITE(radicasi_rombank_hi_w)
+ AM_RANGE(0x500d, 0x500d) AM_READWRITE(radicasi_rombank_lo_r, radicasi_rombank_lo_w)
+
+ // 501x DMA controller
+ AM_RANGE(0x500F, 0x500F) AM_READWRITE(radicasi_dmasrc_lo_r, radicasi_dmasrc_lo_w)
+ AM_RANGE(0x5010, 0x5010) AM_READWRITE(radicasi_dmasrc_md_r, radicasi_dmasrc_md_w)
+ AM_RANGE(0x5011, 0x5011) AM_READWRITE(radicasi_dmasrc_hi_r, radicasi_dmasrc_hi_w)
+
+ AM_RANGE(0x5012, 0x5012) AM_READWRITE(radicasi_dmadst_lo_r, radicasi_dmadst_lo_w)
+ AM_RANGE(0x5013, 0x5013) AM_READWRITE(radicasi_dmadst_hi_r, radicasi_dmadst_hi_w)
+
+ AM_RANGE(0x5014, 0x5014) AM_READWRITE(radicasi_dmasize_lo_r, radicasi_dmasize_lo_w)
+ AM_RANGE(0x5015, 0x5015) AM_READWRITE(radicasi_dmasize_hi_r, radicasi_dmasize_hi_w)
+
+ AM_RANGE(0x5016, 0x5016) AM_READWRITE(radicasi_dmatrg_r, radicasi_dmatrg_w)
+
+ // 502x - 503x video regs area?
+ AM_RANGE(0x5020, 0x5026) AM_RAM // unknown, space invaders sets these to fixed values, tetris has them as 00
+ AM_RANGE(0x5027, 0x5027) AM_WRITE(radicasi_vidctrl_w)
+
+ AM_RANGE(0x5029, 0x5029) AM_READWRITE(radicasi_tile_gfxbase_lo_r, radicasi_tile_gfxbase_lo_w) // tilebase
+ AM_RANGE(0x502a, 0x502a) AM_READWRITE(radicasi_tile_gfxbase_hi_r, radicasi_tile_gfxbase_hi_w) // tilebase
+
+ AM_RANGE(0x502b, 0x502b) AM_READWRITE(radicasi_sprite_gfxbase_lo_r, radicasi_sprite_gfxbase_lo_w) // tilebase (spr?)
+ AM_RANGE(0x502c, 0x502c) AM_READWRITE(radicasi_sprite_gfxbase_hi_r, radicasi_sprite_gfxbase_hi_w) // tilebase (spr?)
+
+ AM_RANGE(0x5031, 0x5032) AM_READWRITE(radicasi_sprite_bg_scroll_r, radicasi_sprite_bg_scroll_w)
+
+
+ // 504x GPIO area?
+ AM_RANGE(0x5040, 0x5046) AM_DEVREADWRITE("gpio", radica6502_gpio_device, gpio_r, gpio_w)
+ AM_RANGE(0x5048, 0x504a) AM_DEVWRITE("gpio", radica6502_gpio_device, gpio_unk_w)
+
+ // 506x unknown
+ AM_RANGE(0x5060, 0x506d) AM_RAM // read/written by tetris
+
+ // 508x sound
+ AM_RANGE(0x5080, 0x5091) AM_DEVREADWRITE("6ch_sound", radica6502_sound_device, radicasi_sound_addr_r, radicasi_sound_addr_w)
+ AM_RANGE(0x5092, 0x50a3) AM_DEVREADWRITE("6ch_sound", radica6502_sound_device, radicasi_sound_size_r, radicasi_sound_size_w)
+ AM_RANGE(0x50a4, 0x50a4) AM_DEVREADWRITE("6ch_sound", radica6502_sound_device, radicasi_sound_unk_r, radicasi_sound_unk_w)
+ AM_RANGE(0x50a5, 0x50a5) AM_DEVREADWRITE("6ch_sound", radica6502_sound_device, radicasi_sound_trigger_r, radicasi_sound_trigger_w)
+
+ AM_RANGE(0x50a8, 0x50a8) AM_DEVREAD("6ch_sound", radica6502_sound_device, radicasi_50a8_r) // possible 'stopped' status of above channels, waits for it to be 0x3f in places
+
+ AM_RANGE(0x50a9, 0x50a9) AM_READWRITE(radicasi_50a9_r, radicasi_50a9_w)
+
+ //AM_RANGE(0x5000, 0x50ff) AM_RAM
+
+ AM_RANGE(0x6000, 0xdfff) AM_DEVICE("bank", address_map_bank_device, amap8)
+
+ // not sure how these work,, might be a modified 6502 core instead.
+ AM_RANGE(0xfffa, 0xfffb) AM_READ(radicasi_nmi_vector_r)
+ AM_RANGE(0xfffe, 0xffff) AM_READ(radicasi_irq_vector_r)
+
+ AM_RANGE(0xe000, 0xffff) AM_ROM AM_REGION("maincpu", 0x3f8000)
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( radicasi_bank_map, AS_PROGRAM, 8, radica_eu3a05_state )
+ AM_RANGE(0x000000, 0x3fffff) AM_ROM AM_REGION("maincpu", 0)
+ AM_RANGE(0x400000, 0x40ffff) AM_RAM // ?? only ever cleared maybe a mirror of below?
+ AM_RANGE(0x800000, 0x80ffff) AM_RAM AM_SHARE("pixram") // Qix writes here and sets the tile base here instead of ROM so it can have a pixel layer
+
+ AM_RANGE(0x000000, 0xffffff) AM_NOP // shut up any logging when video params are invalid
+ADDRESS_MAP_END
+
+static INPUT_PORTS_START( rad_sinv )
+ PORT_START("IN0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) // MENU
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( rad_tetr )
+ PORT_START("IN0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) // Anticlockwise
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) // Clockwise
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) // and Select
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ /* Player 2 inputs must be read via serial or similar
+ the game doesn't read them directly, or even let
+ you select player 2 mode by default
+ */
+INPUT_PORTS_END
+
+/* both NMI and IRQ vectors just point to RTI
+ there is a table of jumps just before that, those appear to be the real interrupt functions?
+
+ patch the main IRQ to be the one that decreases an address the code is waiting for
+ the others look like they might be timer service routines
+*/
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_nmi_vector_r)
+{
+ if (m_custom_nmi)
+ {
+ return m_custom_nmi_vector >> (offset*8);
+ }
+ else
+ {
+ uint8_t *rom = memregion("maincpu")->base();
+ return rom[0x3f9ffa + offset];
+ }
+}
+
+READ8_MEMBER(radica_eu3a05_state::radicasi_irq_vector_r)
+{
+ if (m_custom_irq)
+ {
+ return m_custom_irq_vector >> (offset*8);
+ }
+ else
+ {
+ uint8_t *rom = memregion("maincpu")->base();
+ return rom[0x3f9ffe + offset];
+ }
+}
+
+void radica_eu3a05_state::machine_start()
+{
+ m_custom_irq = 0;
+ m_custom_irq_vector = 0x0000;
+
+ m_custom_nmi = 0;
+ m_custom_nmi_vector = 0x0000;
+
+ m_bank->set_bank(0x7f);
+}
+
+void radica_eu3a05_state::machine_reset()
+{
+ /* the 6502 core sets the default stack value to 0x01bd
+ and Tetris does not initialize it to anything else
+
+ Tetris stores the playfield data at 0x100 - 0x1c7 and
+ has a clear routine that will erase that range and
+ trash the stack
+
+ It seems likely this 6502 sets it to 0x1ff by default
+ at least.
+
+ According to
+ http://mametesters.org/view.php?id=6486
+ this isn't right for known 6502 types either
+ */
+ m_maincpu->set_state_int(M6502_S, 0x1ff);
+}
+
+static const gfx_layout helper_4bpp_8_layout =
+{
+ 8,1,
+ RGN_FRAC(1,1),
+ 4,
+ { 0,1,2,3 },
+ { STEP8(0,4) },
+ { 0 },
+ 8 * 4
+};
+
+static const gfx_layout helper_8bpp_8_layout =
+{
+ 8,1,
+ RGN_FRAC(1,1),
+ 8,
+ { 0,1,2,3,4,5,6,7 },
+ { STEP8(0,8) },
+ { 0 },
+ 8 * 8
+};
+
+
+// these are fake just to make looking at the texture pages easier
+static const uint32_t texlayout_xoffset_8bpp[256] = { STEP256(0,8) };
+static const uint32_t texlayout_yoffset_8bpp[256] = { STEP256(0,256*8) };
+static const gfx_layout texture_helper_8bpp_layout =
+{
+ 256, 256,
+ RGN_FRAC(1,1),
+ 8,
+ { 0,1,2,3,4,5,6,7 },
+ EXTENDED_XOFFS,
+ EXTENDED_YOFFS,
+ 256*256*8,
+ texlayout_xoffset_8bpp,
+ texlayout_yoffset_8bpp
+};
+
+static const uint32_t texlayout_xoffset_4bpp[256] = { STEP256(0,4) };
+static const uint32_t texlayout_yoffset_4bpp[256] = { STEP256(0,256*4) };
+static const gfx_layout texture_helper_4bpp_layout =
+{
+ 256, 256,
+ RGN_FRAC(1,1),
+ 4,
+ { 0,1,2,3 },
+ EXTENDED_XOFFS,
+ EXTENDED_YOFFS,
+ 256*256*4,
+ texlayout_xoffset_4bpp,
+ texlayout_yoffset_4bpp
+};
+
+static GFXDECODE_START( radicasi_fake )
+ GFXDECODE_ENTRY( "maincpu", 0, helper_4bpp_8_layout, 0x0, 1 )
+ GFXDECODE_ENTRY( "maincpu", 0, texture_helper_4bpp_layout, 0x0, 1 )
+ GFXDECODE_ENTRY( "maincpu", 0, helper_8bpp_8_layout, 0x0, 1 )
+ GFXDECODE_ENTRY( "maincpu", 0, texture_helper_8bpp_layout, 0x0, 1 )
+GFXDECODE_END
+
+INTERRUPT_GEN_MEMBER(radica_eu3a05_state::interrupt)
+{
+ m_custom_irq = 1;
+ m_custom_irq_vector = 0xffd4;
+
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0,HOLD_LINE);
+ /*
+ m_custom_nmi = 1;
+ m_custom_nmi_vector = 0xffd4;
+
+ m_maincpu->set_input_line(INPUT_LINE_NMI,PULSE_LINE);
+ */
+}
+
+MACHINE_CONFIG_START(radica_eu3a05_state::radicasi)
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu",M6502,XTAL(21'281'370)/2) // Tetris has a XTAL(21'281'370), not confirmed on Space Invaders, actual CPU clock unknown.
+ MCFG_CPU_PROGRAM_MAP(radicasi_map)
+ MCFG_CPU_VBLANK_INT_DRIVER("screen", radica_eu3a05_state, interrupt)
+
+ MCFG_DEVICE_ADD("bank", ADDRESS_MAP_BANK, 0)
+ MCFG_DEVICE_PROGRAM_MAP(radicasi_bank_map)
+ MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
+ MCFG_ADDRESS_MAP_BANK_DATA_WIDTH(8)
+ MCFG_ADDRESS_MAP_BANK_ADDR_WIDTH(24)
+ MCFG_ADDRESS_MAP_BANK_STRIDE(0x8000)
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_REFRESH_RATE(60)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500))
+ MCFG_SCREEN_UPDATE_DRIVER(radica_eu3a05_state, screen_update)
+ MCFG_SCREEN_SIZE(32*8, 32*8)
+ MCFG_SCREEN_VISIBLE_AREA(0*8, 32*8-1, 0*8, 28*8-1)
+ MCFG_SCREEN_PALETTE("palette")
+
+ MCFG_PALETTE_ADD("palette", 256)
+
+ MCFG_GFXDECODE_ADD("gfxdecode", "palette", radicasi_fake)
+
+ MCFG_DEVICE_ADD("gpio", RADICA6502_GPIO, 0)
+ MCFG_RADICA6502_GPIO_READ_PORT0_CB(IOPORT("IN0"))
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_DEVICE_ADD("6ch_sound", RADICA6502_SOUND, 8000)
+ MCFG_RADICA6502_SOUND_SPACE_READ_CB(READ8(radica_eu3a05_state, read_full_space))
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
+MACHINE_CONFIG_END
+
+
+ROM_START( rad_tetr )
+ ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
+ ROM_LOAD( "tetrisrom.bin", 0x000000, 0x100000, CRC(40538e08) SHA1(1aef9a2c678e39243eab8d910bb7f9f47bae0aee) )
+ ROM_RELOAD(0x100000, 0x100000)
+ ROM_RELOAD(0x200000, 0x100000)
+ ROM_RELOAD(0x300000, 0x100000)
+ROM_END
+
+ROM_START( rad_sinv )
+ ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
+ ROM_LOAD( "spaceinvadersrom.bin", 0x000000, 0x100000, CRC(5ffb2c8f) SHA1(9bde42ec5c65d9584a802de7d7c8b842ebf8cbd8) )
+ ROM_RELOAD(0x100000, 0x100000)
+ ROM_RELOAD(0x200000, 0x100000)
+ ROM_RELOAD(0x300000, 0x100000)
+ROM_END
+
+CONS( 2004, rad_sinv, 0, 0, radicasi, rad_sinv, radica_eu3a05_state, 0, "Radica (licensed from Taito)", "Space Invaders [Lunar Rescue, Colony 7, Qix, Phoenix] (Radica, Arcade Legends TV Game)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // "5 Taito games in 1"
+CONS( 2004, rad_tetr, 0, 0, radicasi, rad_tetr, radica_eu3a05_state, 0, "Radica (licensed from Elorg / The Tetris Company)", "Tetris (Radica, Arcade Legends TV Game)", MACHINE_NOT_WORKING ) // "5 Tetris games in 1"