Plug & Play / Elan EU3A05 / EU3A14 splitting up, identifying of common features etc. (#5849)

* elan eu3a05 / eu3a14 - identify some common bits (nw)

* some elan refactoring baed on current knowledge / standards (nw)

* elan refactoring (nw)

* elan splitting / refactoring (nw)

* validate (nw)

* note used areas (nw)

* register observations (nw)

* another observation (nw)

* add the sprite double feature (nw)

* rendering tweaks (nw)

* rendering improvements for air blaster 3d stages (nw)

* refactoring (nw)
This commit is contained in:
David Haywood 2019-11-04 03:50:48 +00:00 committed by ajrhacker
parent 36bfba32f7
commit d24fc57a95
17 changed files with 1657 additions and 1236 deletions

View File

@ -3695,6 +3695,16 @@ files {
MAME_DIR .. "src/mame/audio/elan_eu3a05.h",
MAME_DIR .. "src/mame/machine/elan_eu3a05gpio.cpp",
MAME_DIR .. "src/mame/machine/elan_eu3a05gpio.h",
MAME_DIR .. "src/mame/machine/elan_eu3a05commonsys.cpp",
MAME_DIR .. "src/mame/machine/elan_eu3a05commonsys.h",
MAME_DIR .. "src/mame/machine/elan_eu3a05sys.cpp",
MAME_DIR .. "src/mame/machine/elan_eu3a05sys.h",
MAME_DIR .. "src/mame/machine/elan_eu3a14sys.cpp",
MAME_DIR .. "src/mame/machine/elan_eu3a14sys.h",
MAME_DIR .. "src/mame/video/elan_eu3a05commonvid.cpp",
MAME_DIR .. "src/mame/video/elan_eu3a05commonvid.h",
MAME_DIR .. "src/mame/video/elan_eu3a05vid.cpp",
MAME_DIR .. "src/mame/video/elan_eu3a05vid.h",
MAME_DIR .. "src/mame/drivers/trkfldch.cpp",
MAME_DIR .. "src/mame/drivers/tvgame.cpp",
MAME_DIR .. "src/mame/drivers/spg110.cpp",

View File

@ -6,7 +6,7 @@
#include "emu.h"
#include "elan_eu3a05.h"
DEFINE_DEVICE_TYPE(RADICA6502_SOUND, radica6502_sound_device, "radica6502sound", "Elan EU3A05 / EU3A14 Sound")
DEFINE_DEVICE_TYPE(ELAN_EU3A05_SOUND, elan_eu3a05_sound_device, "elan_eu3a05sound", "Elan EU3A05 / EU3A14 Sound")
#define LOG_AUDIO (1U << 0)
@ -16,21 +16,21 @@ DEFINE_DEVICE_TYPE(RADICA6502_SOUND, radica6502_sound_device, "radica6502sound",
#include "logmacro.h"
radica6502_sound_device::radica6502_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, RADICA6502_SOUND, tag, owner, clock)
elan_eu3a05_sound_device::elan_eu3a05_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, ELAN_EU3A05_SOUND, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_stream(nullptr)
, m_space_read_cb(*this)
{
}
void radica6502_sound_device::device_start()
void elan_eu3a05_sound_device::device_start()
{
m_space_read_cb.resolve_safe(0);
m_stream = stream_alloc(0, 1, 8000);
}
void radica6502_sound_device::device_reset()
void elan_eu3a05_sound_device::device_reset()
{
for (int i = 0; i < 6; i++)
{
@ -46,7 +46,7 @@ void radica6502_sound_device::device_reset()
// sound_stream_update - handle a stream update
//-------------------------------------------------
void radica6502_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
void elan_eu3a05_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
// reset the output stream
memset(outputs[0], 0, samples * sizeof(*outputs[0]));
@ -95,7 +95,7 @@ void radica6502_sound_device::sound_stream_update(sound_stream &stream, stream_s
}
void radica6502_sound_device::handle_sound_addr_w(int which, int offset, uint8_t data)
void elan_eu3a05_sound_device::handle_sound_addr_w(int which, int offset, uint8_t data)
{
switch (offset)
{
@ -116,7 +116,7 @@ void radica6502_sound_device::handle_sound_addr_w(int which, int offset, uint8_t
}
}
uint8_t radica6502_sound_device::handle_sound_addr_r(int which, int offset)
uint8_t elan_eu3a05_sound_device::handle_sound_addr_r(int which, int offset)
{
switch (offset)
{
@ -136,19 +136,19 @@ uint8_t radica6502_sound_device::handle_sound_addr_r(int which, int offset)
return 0x00;
}
WRITE8_MEMBER(radica6502_sound_device::radicasi_sound_addr_w)
WRITE8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_addr_w)
{
m_stream->update();
handle_sound_addr_w(offset / 3, offset % 3, data);
}
READ8_MEMBER(radica6502_sound_device::radicasi_sound_addr_r)
READ8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_addr_r)
{
m_stream->update();
return handle_sound_addr_r(offset / 3, offset % 3);
}
void radica6502_sound_device::handle_sound_size_w(int which, int offset, uint8_t data)
void elan_eu3a05_sound_device::handle_sound_size_w(int which, int offset, uint8_t data)
{
switch (offset)
{
@ -169,7 +169,7 @@ void radica6502_sound_device::handle_sound_size_w(int which, int offset, uint8_t
}
}
uint8_t radica6502_sound_device::handle_sound_size_r(int which, int offset)
uint8_t elan_eu3a05_sound_device::handle_sound_size_r(int which, int offset)
{
switch (offset)
{
@ -189,19 +189,19 @@ uint8_t radica6502_sound_device::handle_sound_size_r(int which, int offset)
return 0x00;
}
WRITE8_MEMBER(radica6502_sound_device::radicasi_sound_size_w)
WRITE8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_size_w)
{
m_stream->update();
handle_sound_size_w(offset / 3, offset % 3, data);
}
READ8_MEMBER(radica6502_sound_device::radicasi_sound_size_r)
READ8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_size_r)
{
m_stream->update();
return handle_sound_size_r(offset / 3, offset % 3);
}
READ8_MEMBER(radica6502_sound_device::radicasi_sound_trigger_r)
READ8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_r)
{
m_stream->update();
@ -210,7 +210,7 @@ READ8_MEMBER(radica6502_sound_device::radicasi_sound_trigger_r)
}
WRITE8_MEMBER(radica6502_sound_device::radicasi_sound_trigger_w)
WRITE8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_w)
{
m_stream->update();
@ -231,16 +231,16 @@ WRITE8_MEMBER(radica6502_sound_device::radicasi_sound_trigger_w)
/* this is read/written with the same individual bits for each channel as the trigger
maybe related to interrupts? */
READ8_MEMBER(radica6502_sound_device::radicasi_sound_unk_r)
READ8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_unk_r)
{
LOGMASKED( LOG_AUDIO, "%s: radicasi_sound_unk_r\n", machine().describe_context());
LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_sound_unk_r\n", machine().describe_context());
// don't think this reads back what was written probably a status of something instead?
return 0x00; //m_sound_unk;
}
WRITE8_MEMBER(radica6502_sound_device::radicasi_sound_unk_w)
WRITE8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_sound_unk_w)
{
LOGMASKED( LOG_AUDIO, "%s: radicasi_sound_unk_w %02x\n", machine().describe_context(), data);
LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_sound_unk_w %02x\n", machine().describe_context(), data);
for (int i = 0; i < 6; i++)
{
@ -256,7 +256,7 @@ WRITE8_MEMBER(radica6502_sound_device::radicasi_sound_unk_w)
LOGMASKED( LOG_AUDIO, " UNEXPECTED BITS SET");
}
void radica6502_sound_device::handle_sound_trigger(int which)
void elan_eu3a05_sound_device::handle_sound_trigger(int which)
{
LOGMASKED( LOG_AUDIO, "Triggering operation on channel (%d) with params %08x %08x\n", which, m_sound_byte_address[which], m_sound_byte_len[which]);
@ -265,10 +265,10 @@ void radica6502_sound_device::handle_sound_trigger(int which)
}
READ8_MEMBER(radica6502_sound_device::radicasi_50a8_r)
READ8_MEMBER(elan_eu3a05_sound_device::elan_eu3a05_50a8_r)
{
m_stream->update();
LOGMASKED( LOG_AUDIO, "%s: radicasi_50a8_r\n", machine().describe_context());
LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_50a8_r\n", machine().describe_context());
return m_isstopped;
}

View File

@ -1,33 +1,33 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_AUDIO_RAD_EU3A05_H
#define MAME_AUDIO_RAD_EU3A05_H
#ifndef MAME_AUDIO_ELAN_EU3A05_H
#define MAME_AUDIO_ELAN_EU3A05_H
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> radica6502_sound_device
// ======================> elan_eu3a05_sound_device
class radica6502_sound_device : public device_t, public device_sound_interface
class elan_eu3a05_sound_device : public device_t, public device_sound_interface
{
public:
radica6502_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
elan_eu3a05_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
auto space_read_callback() { return m_space_read_cb.bind(); }
DECLARE_WRITE8_MEMBER(radicasi_sound_addr_w);
DECLARE_READ8_MEMBER(radicasi_sound_addr_r);
DECLARE_WRITE8_MEMBER(radicasi_sound_size_w);
DECLARE_READ8_MEMBER(radicasi_sound_size_r);
DECLARE_READ8_MEMBER(radicasi_sound_trigger_r);
DECLARE_WRITE8_MEMBER(radicasi_sound_trigger_w);
DECLARE_READ8_MEMBER(radicasi_sound_unk_r);
DECLARE_WRITE8_MEMBER(radicasi_sound_unk_w);
DECLARE_WRITE8_MEMBER(elan_eu3a05_sound_addr_w);
DECLARE_READ8_MEMBER(elan_eu3a05_sound_addr_r);
DECLARE_WRITE8_MEMBER(elan_eu3a05_sound_size_w);
DECLARE_READ8_MEMBER(elan_eu3a05_sound_size_r);
DECLARE_READ8_MEMBER(elan_eu3a05_sound_trigger_r);
DECLARE_WRITE8_MEMBER(elan_eu3a05_sound_trigger_w);
DECLARE_READ8_MEMBER(elan_eu3a05_sound_unk_r);
DECLARE_WRITE8_MEMBER(elan_eu3a05_sound_unk_w);
DECLARE_READ8_MEMBER(radicasi_50a8_r);
DECLARE_READ8_MEMBER(elan_eu3a05_50a8_r);
protected:
// device-level overrides
virtual void device_start() override;
@ -57,6 +57,6 @@ private:
uint8_t handle_sound_size_r(int which, int offset);
};
DECLARE_DEVICE_TYPE(RADICA6502_SOUND, radica6502_sound_device)
DECLARE_DEVICE_TYPE(ELAN_EU3A05_SOUND, elan_eu3a05_sound_device)
#endif // MAME_AUDIO_RAD_EU3A05_H

File diff suppressed because it is too large Load Diff

View File

@ -68,46 +68,21 @@
#include "machine/bankdev.h"
#include "audio/elan_eu3a05.h"
#include "machine/timer.h"
/*
TODO: fill these in for other games, this is for Golden Tee Home
ffb0 rti
ffb4 rti
ffb8 rti
ffbc rti
ffc0 rti
ffc4 rti
ffc8 rti
ffcc rti
ffd0 rti
ffd4 main irq?
ffd8 rti
ffdc rti
ffe0 something with 5045 bit 0x08 and 9d in ram (increase or decrease) (ADC interrupt)
ffe4 something with 5045 bit 0x20 and 9c in ram (increase of decrease) (ADC interrupt)
ffe8 rti
ffec rti
regular NMI (e3f0 - jump to ($19e2) which seems to point to rti, but could move..)
regular IRQ (e3f3 - points to rti)
*/
#include "machine/elan_eu3a14sys.h"
#include "video/elan_eu3a05commonvid.h"
class radica_eu3a14_state : public driver_device
class elan_eu3a14_state : public driver_device
{
public:
radica_eu3a14_state(const machine_config &mconfig, device_type type, const char *tag)
elan_eu3a14_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_sys(*this, "sys"),
m_sound(*this, "eu3a05sound"),
m_commonvid(*this, "commonvid"),
m_mainregion(*this, "maincpu"),
m_palram(*this, "palram"),
m_scrollregs(*this, "scrollregs"),
m_tilecfg(*this, "tilecfg"),
m_rowscrollregs(*this, "rowscrollregs"),
@ -117,7 +92,6 @@ public:
m_spriteaddr(*this, "spriteaddr"),
m_spritebase(*this, "spritebase"),
m_mainram(*this, "mainram"),
m_dmaparams(*this, "dmaparams"),
m_bank(*this, "bank"),
m_palette(*this, "palette"),
m_gfxdecode(*this, "gfxdecode"),
@ -135,8 +109,6 @@ public:
void init_rad_hnt3();
private:
READ8_MEMBER(irq_vector_r);
// screen updates
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
@ -145,14 +117,8 @@ private:
INTERRUPT_GEN_MEMBER(interrupt);
DECLARE_READ8_MEMBER(radicasi_pal_ntsc_r);
DECLARE_READ8_MEMBER(elan_eu3a05_pal_ntsc_r);
DECLARE_READ8_MEMBER(dma_trigger_r);
DECLARE_WRITE8_MEMBER(dma_trigger_w);
DECLARE_READ8_MEMBER(radicasi_rombank_lo_r);
DECLARE_WRITE8_MEMBER(radicasi_rombank_lo_w);
DECLARE_WRITE8_MEMBER(radicasi_rombank_hi_w);
DECLARE_WRITE8_MEMBER(porta_dir_w);
DECLARE_WRITE8_MEMBER(portb_dir_w);
@ -181,11 +147,11 @@ private:
virtual void video_start() override;
double hue2rgb(double p, double q, double t);
required_device<cpu_device> m_maincpu;
required_device<elan_eu3a14sys_device> m_sys;
required_device<elan_eu3a05_sound_device> m_sound;
required_device<elan_eu3a05commonvid_device> m_commonvid;
required_region_ptr<uint8_t> m_mainregion;
required_shared_ptr<uint8_t> m_palram;
required_shared_ptr<uint8_t> m_scrollregs;
required_shared_ptr<uint8_t> m_tilecfg;
required_shared_ptr<uint8_t> m_rowscrollregs;
@ -195,15 +161,12 @@ private:
required_shared_ptr<uint8_t> m_spriteaddr;
required_shared_ptr<uint8_t> m_spritebase;
required_shared_ptr<uint8_t> m_mainram;
required_shared_ptr<uint8_t> m_dmaparams;
required_device<address_map_bank_device> m_bank;
required_device<palette_device> m_palette;
required_device<gfxdecode_device> m_gfxdecode;
required_device<screen_device> m_screen;
required_ioport m_tvtype;
uint8_t m_rombank_hi;
uint8_t m_rombank_lo;
int m_tilerambase;
int m_spriterambase;
@ -211,8 +174,6 @@ private:
uint8_t m_portdir[3];
void handle_palette(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void draw_background_ramlayer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect);
int get_xscroll_for_screenypos(int line);
void draw_background_tile(bitmap_ind16 &bitmap, const rectangle &cliprect, int bpp, int tileno, int palette, int priority, int flipx, int flipy, int xpos, int ypos, int transpen, int size, int base, int drawfromram);
@ -224,61 +185,12 @@ private:
};
void radica_eu3a14_state::video_start()
void elan_eu3a14_state::video_start()
{
m_screen->register_screen_bitmap(m_prioritybitmap);
}
double radica_eu3a14_state::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;
}
void radica_eu3a14_state::handle_palette(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
// Palette
int offs = 0;
for (int index = 0; index < 512; 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);
}
}
void radica_eu3a14_state::draw_background_tile(bitmap_ind16& bitmap, const rectangle& cliprect, int bpp, int tileno, int palette, int priority, int flipx, int flipy, int xpos, int ypos, int transpen, int size, int base, int drawfromram)
void elan_eu3a14_state::draw_background_tile(bitmap_ind16& bitmap, const rectangle& cliprect, int bpp, int tileno, int palette, int priority, int flipx, int flipy, int xpos, int ypos, int transpen, int size, int base, int drawfromram)
{
int baseaddr = base * 256;
@ -485,7 +397,7 @@ void radica_eu3a14_state::draw_background_tile(bitmap_ind16& bitmap, const recta
}
}
int radica_eu3a14_state::get_xscroll_for_screenypos(int ydraw)
int elan_eu3a14_state::get_xscroll_for_screenypos(int ydraw)
{
if ((ydraw < 0) || (ydraw >= 224))
return 0;
@ -531,7 +443,7 @@ int radica_eu3a14_state::get_xscroll_for_screenypos(int ydraw)
}
void radica_eu3a14_state::draw_background_page(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int ramstart, int ramend, int xbase, int ybase, int size, int bpp, int base, int pagewidth, int pageheight, int bytespertile, int palettepri, int drawfromram)
void elan_eu3a14_state::draw_background_page(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int ramstart, int ramend, int xbase, int ybase, int size, int bpp, int base, int pagewidth, int pageheight, int bytespertile, int palettepri, int drawfromram)
{
int palette = ((palettepri & 0xf0) >> 4) | ((palettepri & 0x08) << 1);
@ -584,7 +496,7 @@ void radica_eu3a14_state::draw_background_page(screen_device& screen, bitmap_ind
}
}
void radica_eu3a14_state::draw_background_ramlayer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect)
void elan_eu3a14_state::draw_background_ramlayer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect)
{
// this register use is questionable
if (m_ramtilecfg[0] & 0x80)
@ -643,7 +555,7 @@ void radica_eu3a14_state::draw_background_ramlayer(screen_device& screen, bitmap
}
void radica_eu3a14_state::draw_background(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
void elan_eu3a14_state::draw_background(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
int yscroll = m_scrollregs[2] | (m_scrollregs[3] << 8);
@ -761,7 +673,7 @@ void radica_eu3a14_state::draw_background(screen_device &screen, bitmap_ind16 &b
}
void radica_eu3a14_state::draw_sprite_pix(const rectangle& cliprect, uint16_t* dst, uint8_t* pridst, int realx, int priority, uint8_t pix, uint8_t mask, uint8_t shift, int palette)
void elan_eu3a14_state::draw_sprite_pix(const rectangle& cliprect, uint16_t* dst, uint8_t* pridst, int realx, int priority, uint8_t pix, uint8_t mask, uint8_t shift, int palette)
{
if (realx >= cliprect.min_x && realx <= cliprect.max_x)
{
@ -776,7 +688,7 @@ void radica_eu3a14_state::draw_sprite_pix(const rectangle& cliprect, uint16_t* d
}
}
void radica_eu3a14_state::draw_sprite_line(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int offset, int line, int palette, int flipx, int priority, int xpos, int ypos, int bpp)
void elan_eu3a14_state::draw_sprite_line(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int offset, int line, int palette, int flipx, int priority, int xpos, int ypos, int bpp)
{
offset = offset * 2;
@ -889,7 +801,7 @@ void radica_eu3a14_state::draw_sprite_line(screen_device &screen, bitmap_ind16 &
}
void radica_eu3a14_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
void elan_eu3a14_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
// first 4 sprite entries seem to be garbage sprites, so we start at 0x20
// likely we're just interpreting them wrong and they're used for blanking things or clipping?
@ -983,14 +895,13 @@ void radica_eu3a14_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitm
uint32_t radica_eu3a14_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
uint32_t elan_eu3a14_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
m_spriterambase = (m_spriteaddr[0] * 0x200) - 0x200;
bitmap.fill(0, cliprect);
m_prioritybitmap.fill(0, cliprect);
handle_palette(screen, bitmap, cliprect);
draw_background(screen, bitmap, cliprect);
draw_sprites(screen, bitmap, cliprect);
@ -999,143 +910,90 @@ uint32_t radica_eu3a14_state::screen_update(screen_device &screen, bitmap_ind16
}
// sound callback
READ8_MEMBER(radica_eu3a14_state::read_full_space)
READ8_MEMBER(elan_eu3a14_state::read_full_space)
{
address_space& fullbankspace = m_bank->space(AS_PROGRAM);
return fullbankspace.read_byte(offset);
}
// irq controller seems to be like the Radica Space Invaders
READ8_MEMBER(radica_eu3a14_state::irq_vector_r)
{
if (m_custom_irq)
{
return m_custom_irq_vector >> (offset*8);
}
else
{
uint8_t *rom = memregion("maincpu")->base();
return rom[0x001ffe + offset];
}
}
/*
code at 0000 maps to e000
code at 1000 maps to f000
data at 2000
data at 3000
data at 4000
blank 5000
blank 6000
code at 7000 maps to 3000
code at 8000 maps to 6000
9000 maps to 7000
a000 maps to 8000
b000 maps to 9000
c000 maps to a000
d000 maps to b000
e000 maps to c000
*/
WRITE8_MEMBER(radica_eu3a14_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_eu3a14_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_eu3a14_state::radicasi_rombank_lo_r)
{
return m_rombank_lo;
}
READ8_MEMBER(radica_eu3a14_state::radicasi_pal_ntsc_r)
READ8_MEMBER(elan_eu3a14_state::elan_eu3a05_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());
logerror("%s: elan_eu3a05_pal_ntsc_r (region + more?)\n", machine().describe_context());
return m_tvtype->read();
}
WRITE8_MEMBER(radica_eu3a14_state::porta_dir_w)
WRITE8_MEMBER(elan_eu3a14_state::porta_dir_w)
{
m_portdir[0] = data;
// update state
}
WRITE8_MEMBER(radica_eu3a14_state::portb_dir_w)
WRITE8_MEMBER(elan_eu3a14_state::portb_dir_w)
{
m_portdir[1] = data;
// update state
}
WRITE8_MEMBER(radica_eu3a14_state::portc_dir_w)
WRITE8_MEMBER(elan_eu3a14_state::portc_dir_w)
{
m_portdir[2] = data;
// update state
}
WRITE8_MEMBER(radica_eu3a14_state::porta_dat_w)
WRITE8_MEMBER(elan_eu3a14_state::porta_dat_w)
{
}
WRITE8_MEMBER(radica_eu3a14_state::portb_dat_w)
WRITE8_MEMBER(elan_eu3a14_state::portb_dat_w)
{
}
WRITE8_MEMBER(radica_eu3a14_state::portc_dat_w)
WRITE8_MEMBER(elan_eu3a14_state::portc_dat_w)
{
}
void radica_eu3a14_state::bank_map(address_map &map)
void elan_eu3a14_state::bank_map(address_map &map)
{
map(0x000000, 0x3fffff).rom().region("maincpu", 0);
}
void radica_eu3a14_state::radica_eu3a14_map(address_map &map)
void elan_eu3a14_state::radica_eu3a14_map(address_map &map)
{
map(0x0000, 0x01ff).ram();
map(0x0200, 0x3fff).ram().share("mainram"); // 200-9ff is sprites? a00 - ??? is tilemap?
map(0x4800, 0x4bff).ram().share("palram");
map(0x4800, 0x4bff).rw(m_commonvid, FUNC(elan_eu3a05commonvid_device::palette_r), FUNC(elan_eu3a05commonvid_device::palette_w));
// 5000 - 501f = SYSTEM AREA
// similar to eu3a05, at least for pal flags and rom banking
// 5001 write
// 5004 write
// 5006 write
map(0x5007, 0x5007).noprw();
map(0x5008, 0x5008).nopw(); // startup (read too)
map(0x5009, 0x5009).r(FUNC(radica_eu3a14_state::radica_5009_unk_r)); // rad_hnt3 polls this on startup
map(0x5007, 0x5008).rw(m_sys, FUNC(elan_eu3a05commonsys_device::intmask_r), FUNC(elan_eu3a05commonsys_device::intmask_w));
map(0x5009, 0x5009).r(FUNC(elan_eu3a14_state::radica_5009_unk_r)); // rad_hnt3 polls this on startup
map(0x500a, 0x500a).nopw(); // startup
map(0x500b, 0x500b).r(FUNC(radica_eu3a14_state::radicasi_pal_ntsc_r)).nopw(); // PAL / NTSC flag at least
map(0x500c, 0x500c).w(FUNC(radica_eu3a14_state::radicasi_rombank_hi_w));
map(0x500d, 0x500d).rw(FUNC(radica_eu3a14_state::radicasi_rombank_lo_r), FUNC(radica_eu3a14_state::radicasi_rombank_lo_w));
map(0x500b, 0x500b).r(FUNC(elan_eu3a14_state::elan_eu3a05_pal_ntsc_r)).nopw(); // PAL / NTSC flag at least
map(0x500c, 0x500d).rw(m_sys, FUNC(elan_eu3a05commonsys_device::elan_eu3a05_rombank_r), FUNC(elan_eu3a05commonsys_device::elan_eu3a05_rombank_w));
// DMA is similar to, but not the same as eu3a05
map(0x500f, 0x5017).ram().share("dmaparams");
map(0x5018, 0x5018).rw(FUNC(radica_eu3a14_state::dma_trigger_r), FUNC(radica_eu3a14_state::dma_trigger_w));
map(0x500f, 0x5017).rw(m_sys, FUNC(elan_eu3a14sys_device::dma_param_r), FUNC(elan_eu3a14sys_device::dma_param_w));
map(0x5018, 0x5018).rw(m_sys, FUNC(elan_eu3a14sys_device::dma_trigger_r), FUNC(elan_eu3a14sys_device::dma_trigger_w));
// 5019 - 46 on startup (hnt3) 22 (bb3, foot) na (gtg) 09 (rsg)
// 501a - 01 on startup (hnt3) 03 (bb3, foot) na (gtg) 02,01 (rsg)
// probably GPIO like eu3a05, although it access 47/48 as unknown instead of 48/49/4a
map(0x5040, 0x5040).w(FUNC(radica_eu3a14_state::porta_dir_w));
map(0x5041, 0x5041).portr("IN0").w(FUNC(radica_eu3a14_state::porta_dat_w));
map(0x5042, 0x5042).w(FUNC(radica_eu3a14_state::portb_dir_w));
map(0x5043, 0x5043).portr("IN1").w(FUNC(radica_eu3a14_state::portb_dat_w));
map(0x5044, 0x5044).w(FUNC(radica_eu3a14_state::portc_dir_w));
map(0x5045, 0x5045).portr("IN2").w(FUNC(radica_eu3a14_state::portc_dat_w));
map(0x5040, 0x5040).w(FUNC(elan_eu3a14_state::porta_dir_w));
map(0x5041, 0x5041).portr("IN0").w(FUNC(elan_eu3a14_state::porta_dat_w));
map(0x5042, 0x5042).w(FUNC(elan_eu3a14_state::portb_dir_w));
map(0x5043, 0x5043).portr("IN1").w(FUNC(elan_eu3a14_state::portb_dat_w));
map(0x5044, 0x5044).w(FUNC(elan_eu3a14_state::portc_dir_w));
map(0x5045, 0x5045).portr("IN2").w(FUNC(elan_eu3a14_state::portc_dat_w));
map(0x5046, 0x5046).nopw();
map(0x5047, 0x5047).nopw();
@ -1144,13 +1002,13 @@ void radica_eu3a14_state::radica_eu3a14_map(address_map &map)
// 5060 - 506e r/w during startup on foot
// sound appears to be the same as eu3a05
map(0x5080, 0x5091).rw("6ch_sound", FUNC(radica6502_sound_device::radicasi_sound_addr_r), FUNC(radica6502_sound_device::radicasi_sound_addr_w));
map(0x5092, 0x50a3).rw("6ch_sound", FUNC(radica6502_sound_device::radicasi_sound_size_r), FUNC(radica6502_sound_device::radicasi_sound_size_w));
map(0x50a4, 0x50a4).rw("6ch_sound", FUNC(radica6502_sound_device::radicasi_sound_unk_r), FUNC(radica6502_sound_device::radicasi_sound_unk_w)); // read frequently on this
map(0x50a5, 0x50a5).rw("6ch_sound", FUNC(radica6502_sound_device::radicasi_sound_trigger_r), FUNC(radica6502_sound_device::radicasi_sound_trigger_w));
map(0x5080, 0x5091).rw(m_sound, FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_addr_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_addr_w));
map(0x5092, 0x50a3).rw(m_sound, FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_size_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_size_w));
map(0x50a4, 0x50a4).rw(m_sound, FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_unk_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_unk_w)); // read frequently on this
map(0x50a5, 0x50a5).rw(m_sound, FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_w));
map(0x50a6, 0x50a6).nopw(); // startup
map(0x50a7, 0x50a7).nopw(); // startup
map(0x50a8, 0x50a8).r("6ch_sound", FUNC(radica6502_sound_device::radicasi_50a8_r));
map(0x50a8, 0x50a8).r(m_sound, FUNC(elan_eu3a05_sound_device::elan_eu3a05_50a8_r));
map(0x50a9, 0x50a9).nopw(); // startup, read foot
// video regs are in the 51xx range
@ -1204,38 +1062,8 @@ void radica_eu3a14_state::radica_eu3a14_map(address_map &map)
map(0xe000, 0xffff).rom().region("maincpu", 0x0000);
map(0xfffe, 0xffff).r(FUNC(radica_eu3a14_state::irq_vector_r));
}
READ8_MEMBER(radica_eu3a14_state::dma_trigger_r)
{
logerror("%s: dma_trigger_r\n", machine().describe_context());
return 0;
}
WRITE8_MEMBER(radica_eu3a14_state::dma_trigger_w)
{
uint32_t dmasrc = (m_dmaparams[2] << 16) | (m_dmaparams[1] << 8) | (m_dmaparams[0] << 0);
uint32_t dmadst = (m_dmaparams[5] << 16) | (m_dmaparams[4] << 8) | (m_dmaparams[3] << 0);
uint32_t dmalen = (m_dmaparams[8] << 16) | (m_dmaparams[7] << 8) | (m_dmaparams[6] << 0);
//logerror("%s: dma_trigger_w %02x (src %08x dst %08x size %08x)\n", machine().describe_context(), data, dmasrc, dmadst, dmalen);
address_space& fullbankspace = m_bank->space(AS_PROGRAM);
address_space& destspace = m_maincpu->space(AS_PROGRAM);
if (data == 0x08)
{
for (int i = 0; i < dmalen; i++)
{
uint8_t dat = fullbankspace.read_byte(dmasrc + i);
destspace.write_byte(dmadst + i, dat);
}
}
else
{
logerror("UNKNOWN DMA TRIGGER VALUE\n");
}
map(0xfffa, 0xfffb).r(m_sys, FUNC(elan_eu3a05commonsys_device::nmi_vector_r)); // custom vectors handled with NMI for now
//map(0xfffe, 0xffff).r(m_sys, FUNC(elan_eu3a05commonsys_device::irq_vector_r)); // allow normal IRQ for brk
}
@ -1687,11 +1515,11 @@ static INPUT_PORTS_START( radica_bb3p )
PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END
void radica_eu3a14_state::machine_start()
void elan_eu3a14_state::machine_start()
{
}
void radica_eu3a14_state::machine_reset()
void elan_eu3a14_state::machine_reset()
{
// rather be safe
m_maincpu->set_state_int(M6502_S, 0x1ff);
@ -1706,33 +1534,29 @@ void radica_eu3a14_state::machine_reset()
}
TIMER_DEVICE_CALLBACK_MEMBER(radica_eu3a14_state::scanline_cb)
TIMER_DEVICE_CALLBACK_MEMBER(elan_eu3a14_state::scanline_cb)
{
// these interrupts need to occur based on how fast the trackball is
// being moved, the direction is read in a port.
int scanline = param;
if (scanline == 20)
{
// vertical trackball
m_custom_irq_vector = 0xffe0;
m_maincpu->set_input_line(INPUT_LINE_IRQ0,HOLD_LINE);
m_sys->generate_custom_interrupt(12);
}
if (scanline == 40)
{
// horizontal trackball
m_custom_irq_vector = 0xffe4;
m_maincpu->set_input_line(INPUT_LINE_IRQ0,HOLD_LINE);
m_sys->generate_custom_interrupt(13);
}
}
INTERRUPT_GEN_MEMBER(radica_eu3a14_state::interrupt)
INTERRUPT_GEN_MEMBER(elan_eu3a14_state::interrupt)
{
m_custom_irq = 1;
m_custom_irq_vector = 0xffd4;
m_maincpu->set_input_line(INPUT_LINE_IRQ0,HOLD_LINE);
m_sys->generate_custom_interrupt(9);
}
@ -1792,14 +1616,18 @@ GFXDECODE_END
void radica_eu3a14_state::radica_eu3a14(machine_config &config)
void elan_eu3a14_state::radica_eu3a14(machine_config &config)
{
/* basic machine hardware */
M6502(config, m_maincpu, XTAL(21'477'272)/2); // marked as 21'477'270
m_maincpu->set_addrmap(AS_PROGRAM, &radica_eu3a14_state::radica_eu3a14_map);
m_maincpu->set_vblank_int("screen", FUNC(radica_eu3a14_state::interrupt));
m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a14_state::radica_eu3a14_map);
m_maincpu->set_vblank_int("screen", FUNC(elan_eu3a14_state::interrupt));
ADDRESS_MAP_BANK(config, "bank").set_map(&radica_eu3a14_state::bank_map).set_options(ENDIANNESS_LITTLE, 8, 24, 0x8000);
ADDRESS_MAP_BANK(config, "bank").set_map(&elan_eu3a14_state::bank_map).set_options(ENDIANNESS_LITTLE, 8, 24, 0x8000);
ELAN_EU3A14_SYS(config, m_sys, 0);
m_sys->set_cpu("maincpu");
m_sys->set_addrbank("bank");
GFXDECODE(config, m_gfxdecode, m_palette, gfx_helper);
@ -1807,29 +1635,34 @@ void radica_eu3a14_state::radica_eu3a14(machine_config &config)
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
screen.set_refresh_hz(60);
screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
screen.set_screen_update(FUNC(radica_eu3a14_state::screen_update));
screen.set_screen_update(FUNC(elan_eu3a14_state::screen_update));
screen.set_size(32*8, 32*8);
screen.set_visarea(0*8, 32*8-1, 0*8, 28*8-1);
screen.set_palette(m_palette);
PALETTE(config, m_palette).set_entries(512);
ELAN_EU3A05_COMMONVID(config, m_commonvid, 0);
m_commonvid->set_palette("palette");
m_commonvid->set_entries(512);
/* sound hardware */
SPEAKER(config, "mono").front_center();
radica6502_sound_device &sound(RADICA6502_SOUND(config, "6ch_sound", 8000));
sound.space_read_callback().set(FUNC(radica_eu3a14_state::read_full_space));
sound.add_route(ALL_OUTPUTS, "mono", 1.0);
ELAN_EU3A05_SOUND(config, m_sound, 8000);
m_sound->space_read_callback().set(FUNC(elan_eu3a14_state::read_full_space));
m_sound->add_route(ALL_OUTPUTS, "mono", 1.0);
}
void radica_eu3a14_state::radica_eu3a14_adc(machine_config &config)
void elan_eu3a14_state::radica_eu3a14_adc(machine_config &config)
{
radica_eu3a14(config);
TIMER(config, "scantimer").configure_scanline(FUNC(radica_eu3a14_state::scanline_cb), "screen", 0, 1);
TIMER(config, "scantimer").configure_scanline(FUNC(elan_eu3a14_state::scanline_cb), "screen", 0, 1);
}
void radica_eu3a14_state::radica_eu3a14p(machine_config &config) // TODO, clocks differ too, what are they on PAL?
void elan_eu3a14_state::radica_eu3a14p(machine_config &config) // TODO, clocks differ too, what are they on PAL?
{
radica_eu3a14(config);
@ -1837,19 +1670,19 @@ void radica_eu3a14_state::radica_eu3a14p(machine_config &config) // TODO, clocks
}
void radica_eu3a14_state::init_rad_gtg()
void elan_eu3a14_state::init_rad_gtg()
{
// must be registers to control this
m_tilerambase = 0x0a00 - 0x200;
}
void radica_eu3a14_state::init_rad_foot()
void elan_eu3a14_state::init_rad_foot()
{
// must be registers to control this
m_tilerambase = 0x0200 - 0x200;
}
void radica_eu3a14_state::init_rad_hnt3()
void elan_eu3a14_state::init_rad_hnt3()
{
// must be registers to control this
m_tilerambase = 0x0200 - 0x200;
@ -1907,20 +1740,20 @@ ROM_START( rad_baskp )
ROM_LOAD( "basketball.bin", 0x000000, 0x400000, CRC(7d6ff53c) SHA1(1c75261d55e0107a3b8e8d4c1eb2854750f2d0e8) )
ROM_END
CONS( 2006, rad_gtg, 0, 0, radica_eu3a14_adc, rad_gtg, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios (licensed from Incredible Technologies)", "Golden Tee Golf: Home Edition", MACHINE_NOT_WORKING )
CONS( 2006, rad_gtg, 0, 0, radica_eu3a14_adc, rad_gtg, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios (licensed from Incredible Technologies)", "Golden Tee Golf: Home Edition", MACHINE_NOT_WORKING )
CONS( 2005, rad_rsg, 0, 0, radica_eu3a14, rad_rsg, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Play TV Real Swing Golf", MACHINE_NOT_WORKING )
// some Connectv branded Real Swing Golf units have a language selection, so there are likely other PAL revisions of this
CONS( 2005, rad_rsgp, rad_rsg, 0, radica_eu3a14p, rad_rsgp, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Connectv Real Swing Golf", MACHINE_NOT_WORKING )
CONS( 2005, rad_rsg, 0, 0, radica_eu3a14, rad_rsg, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Play TV Real Swing Golf", MACHINE_NOT_WORKING )
// some Connectv branded Real Swing Golf units have a language selection (checksum in test mode confirmed as different on said units)
CONS( 2005, rad_rsgp, rad_rsg, 0, radica_eu3a14p, rad_rsgp, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Connectv Real Swing Golf", MACHINE_NOT_WORKING )
// also has a Connectv Real Soccer logo in the roms, apparently unused, maybe that was to be the US title (without the logo being changed to Play TV) but Play TV Soccer ended up being a different game licensed from Epoch instead.
CONS( 2006, rad_foot, 0, 0, radica_eu3a14p, radica_foot, radica_eu3a14_state, init_rad_foot, "Radica / Medialink", "Connectv Football", MACHINE_NOT_WORKING )
CONS( 2006, rad_foot, 0, 0, radica_eu3a14p, radica_foot, elan_eu3a14_state, init_rad_foot, "Radica / Medialink", "Connectv Football", MACHINE_NOT_WORKING )
CONS( 2005, rad_bb3, 0, 0, radica_eu3a14, radica_bb3, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Play TV Baseball 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_bb3p, rad_bb3, 0, radica_eu3a14p, radica_bb3p, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Connectv Baseball 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_bb3, 0, 0, radica_eu3a14, radica_bb3, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Play TV Baseball 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_bb3p, rad_bb3, 0, radica_eu3a14p, radica_bb3p, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Connectv Baseball 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_hnt3, 0, 0, radica_eu3a14, radica_hnt3, radica_eu3a14_state, init_rad_hnt3, "Radica / V-Tac Technology Co Ltd.", "Play TV Huntin' 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_hnt3p,rad_hnt3, 0, radica_eu3a14p, radica_hnt3p, radica_eu3a14_state, init_rad_hnt3, "Radica / V-Tac Technology Co Ltd.", "Connectv Huntin' 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_hnt3, 0, 0, radica_eu3a14, radica_hnt3, elan_eu3a14_state, init_rad_hnt3, "Radica / V-Tac Technology Co Ltd.", "Play TV Huntin' 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_hnt3p,rad_hnt3, 0, radica_eu3a14p, radica_hnt3p, elan_eu3a14_state, init_rad_hnt3, "Radica / V-Tac Technology Co Ltd.", "Connectv Huntin' 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_bask, 0, 0, radica_eu3a14, radica_bask, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Play TV Basketball", MACHINE_NOT_WORKING )
CONS( 2005, rad_baskp,rad_bask, 0, radica_eu3a14, radica_baskp, radica_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Connectv Basketball", MACHINE_NOT_WORKING )
CONS( 2005, rad_bask, 0, 0, radica_eu3a14, radica_bask, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Play TV Basketball", MACHINE_NOT_WORKING )
CONS( 2005, rad_baskp,rad_bask, 0, radica_eu3a14, radica_baskp, elan_eu3a14_state, init_rad_gtg, "Radica / FarSight Studios", "Connectv Basketball", MACHINE_NOT_WORKING )

View File

@ -0,0 +1,307 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#include "emu.h"
#include "elan_eu3a05commonsys.h"
/*
Both the Elan EU3A05 and EU3A14 CPU types implement some kind of custom interrupt handling
It isn't clear if this is a completely new addition to the CPU, or just an interface / controller
sitting on top of the existing NMI or IRQ support in the core providing custom vectors.
The interrupt handlers are 16 4-byte entries starting at 0xffb0 in memory space
*/
/*
-----------------------
Custom Interrupt purposes
-----------------------
TETRIS (enables 5007 : 0a, 5008: 0f)
ffb0 (enabled)
nothing of note?
ffb4 (enabled)
stuff with 500e, 500c and 500d
ffb8 (enabled)
stuff with 50a4 / 50a5 / 50a6 and memory address e2
ffbc (enabled)
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 (enabled)
main irq?
ffd8
jumps straight to an rti
ffdc (enabled) - probably P2 input related? ADC interrupt?
accesses 501d / 501b
-----------------------
SPACE INVADERS
ffb0
rti
ffb4
rti
ffb8 (enabled by phoenix)
rti
ffbc (enabled by phoenix)
decreases 301 bit 02
stuff with 50a5
ffc0 (enabled by phoenix)
decreases 302
stuff with 50a5 bit 04
ffc4 (enabled by phoenix)
decreases 303
stuff with 50a5 bit 08
ffc8 (enabled by phoenix)
decreases 304
stuff with 50a5 bit 10
ffcc (enabled by phoenix)
uses 307
stuff with 50a5 bit 20
ffd0
dead loop
ffd4 (enabled by all games)
main interrupt
ffd8
dead loop
ffdc
dead loop
ffe0
dead loop
ffe4
rti
ffe8
dead loop
ffec
dead loop
-----------------------
AIR BLASTER JOYSTICK
all these 60xx jumps expect bank 00 or 0e or 3a or 7d to be active, so IRQs must be masked
ffb0: jmp to 6000 (ends up jumping to pointer from RAM)
ffb4: jmp to e08e (stuff with 500c/500d/506e etc.)
ffb8: jmp to 601c (stub handler) (has function in bank 0e - writes 00 then 01 to 50a5)
ffbc: jmp to 602a (stub handler)
ffc0: jmp to 6038 (stub handler)
ffc4: jmp to 6046 (stub handler)
ffc8: jmp to 6054 (stub handler)
ffcc: jmp to 6062 (stub handler)
ffd0: jmp to 6070 (stub handler)
ffd4: jmp to 607e (valid code - main IRQ?)
ffd8: jmp to 608c (stub handler)
ffdc: jmp to 609a (stub handler)
ffe0: jmp to 60a8 (stub handler)
ffe4: jmp to 60b6 (stub handler)
ffe8: jmp to 60c4 (stub handler)
ffec: jmp to 60d2 (stub handler)
fff0: 7d
fffa: e0 60 (60e0 vector) (stub handler)
fffc: 88 e1 (e188 startup vector)
fffe: 02 e0 (e002 vector)
-----------------------
GOLDEN TEE HOME
ffb0 rti
ffb4 rti
ffb8 rti
ffbc rti
ffc0 rti
ffc4 rti
ffc8 rti
ffcc rti
ffd0 rti
ffd4 main irq?
ffd8 rti
ffdc rti
ffe0 something with 5045 bit 0x08 and 9d in ram (increase or decrease) (ADC interrupt)
ffe4 something with 5045 bit 0x20 and 9c in ram (increase of decrease) (ADC interrupt)
ffe8 rti
ffec rti
regular NMI (e3f0 - jump to ($19e2) which seems to point to rti, but could move..)
regular IRQ (e3f3 - points to rti)
*/
DEFINE_DEVICE_TYPE(ELAN_EU3A05_COMMONSYS, elan_eu3a05commonsys_device, "elan_eu3a05commonsys", "Elan EU3A05/EU3A14 Common System")
elan_eu3a05commonsys_device::elan_eu3a05commonsys_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
m_cpu(*this, finder_base::DUMMY_TAG),
m_bank(*this, finder_base::DUMMY_TAG)
{
}
elan_eu3a05commonsys_device::elan_eu3a05commonsys_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
elan_eu3a05commonsys_device(mconfig, ELAN_EU3A05_COMMONSYS, tag, owner, clock)
{
}
void elan_eu3a05commonsys_device::device_start()
{
save_item(NAME(m_rombank_lo));
save_item(NAME(m_rombank_hi));
save_item(NAME(m_intmask));
save_item(NAME(m_custom_irq));
save_item(NAME(m_custom_nmi));
save_item(NAME(m_custom_irq_vector));
save_item(NAME(m_custom_nmi_vector));
}
void elan_eu3a05commonsys_device::device_reset()
{
// all interrupts disabled?
m_intmask[0] = 0x00;
m_intmask[1] = 0x00;
m_custom_irq = 0;
m_custom_irq_vector = 0x0000;
m_custom_nmi = 0;
m_custom_nmi_vector = 0x0000;
m_rombank_lo = 0x7f;
m_rombank_hi = 0x00;
m_bank->set_bank(0x7f);
}
READ8_MEMBER(elan_eu3a05commonsys_device::intmask_r)
{
return m_intmask[offset];
}
WRITE8_MEMBER(elan_eu3a05commonsys_device::intmask_w)
{
m_intmask[offset] = data;
}
void elan_eu3a05commonsys_device::generate_custom_interrupt(int level)
{
// Air Blaster uses brk in the code, which is problematic for custom IRQs
// m_custom_irq = 1;
// m_custom_irq_vector = 0xffd4;
// m_maincpu->set_input_line(INPUT_LINE_IRQ0,HOLD_LINE);
// 5007 5008
// --ee --v- ssss ss-t
// 10 5432 10
// vector = 0xffb0 + 4 * bit position from right
// each bit seems to relate to an IRQ level
// v = vbl interrupt bit (vector 0xffd4)
// t = used for object movement (enemies / bullets) on air blaster chase levels (vector 0xffb0) needs to be frequent, timer? or hbl?
// s = sound channel related? (air blaster enables s1)
// e = 'event' interrupts (rad_gtg)
// vbl irq masking is important for air blaster or it will suffer from random crashes
uint16_t intmask = (m_intmask[0] << 8) | m_intmask[1];
if (intmask & (1 << level))
{
m_custom_nmi = 1;
m_custom_nmi_vector = 0xffb0 + level * 4;
m_cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}
}
READ8_MEMBER(elan_eu3a05commonsys_device::nmi_vector_r)
{
if (m_custom_nmi)
{
return m_custom_nmi_vector >> (offset*8);
}
else
{
fatalerror("NMI without custom vector!");
}
}
// not currently used
READ8_MEMBER(elan_eu3a05commonsys_device::irq_vector_r)
{
if (m_custom_irq)
{
return m_custom_irq_vector >> (offset*8);
}
else
{
fatalerror("IRQ without custom vector!");
}
}
WRITE8_MEMBER(elan_eu3a05commonsys_device::elan_eu3a05_rombank_w)
{
if (offset == 0x00)
{
// written with the banking?
//logerror("%s: elan_eu3a05_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));
}
else
{
//logerror("%s: elan_eu3a05_rombank_lo_w (select ROM bank) %02x\n", machine().describe_context(), data);
m_rombank_lo = data;
}
}
READ8_MEMBER(elan_eu3a05commonsys_device::elan_eu3a05_rombank_r)
{
if (offset == 0x00)
{
return m_rombank_hi;
}
else
{
return m_rombank_lo;
}
}

View File

@ -0,0 +1,50 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_MACHINE_ELAN_EU3A05COMMONSYS_H
#define MAME_MACHINE_ELAN_EU3A05COMMONSYS_H
#include "cpu/m6502/m6502.h"
#include "machine/bankdev.h"
class elan_eu3a05commonsys_device : public device_t
{
public:
elan_eu3a05commonsys_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
elan_eu3a05commonsys_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
template <typename T> void set_cpu(T &&tag) { m_cpu.set_tag(std::forward<T>(tag)); }
template <typename T> void set_addrbank(T &&tag) { m_bank.set_tag(std::forward<T>(tag)); }
void generate_custom_interrupt(int level);
DECLARE_READ8_MEMBER(intmask_r);
DECLARE_WRITE8_MEMBER(intmask_w);
DECLARE_READ8_MEMBER(nmi_vector_r);
DECLARE_READ8_MEMBER(irq_vector_r);
DECLARE_WRITE8_MEMBER(elan_eu3a05_rombank_w);
DECLARE_READ8_MEMBER(elan_eu3a05_rombank_r);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
required_device<m6502_device> m_cpu;
required_device<address_map_bank_device> m_bank;
uint8_t m_intmask[2];
int m_custom_irq;
int m_custom_nmi;
uint16_t m_custom_irq_vector;
uint16_t m_custom_nmi_vector;
uint8_t m_rombank_hi;
uint8_t m_rombank_lo;
};
DECLARE_DEVICE_TYPE(ELAN_EU3A05_COMMONSYS, elan_eu3a05commonsys_device)
#endif // MAME_MACHINE_ELAN_EU3A05COMMONSYS_H

View File

@ -4,24 +4,24 @@
#include "emu.h"
#include "elan_eu3a05gpio.h"
DEFINE_DEVICE_TYPE(RADICA6502_GPIO, radica6502_gpio_device, "radica6502gpio", "Elan EU3A05 GPIO")
DEFINE_DEVICE_TYPE(ELAN_EU3A05_GPIO, elan_eu3a05gpio_device, "elan_eu3a05gpio", "Elan EU3A05 GPIO")
radica6502_gpio_device::radica6502_gpio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, RADICA6502_GPIO, tag, owner, clock)
elan_eu3a05gpio_device::elan_eu3a05gpio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, ELAN_EU3A05_GPIO, tag, owner, clock)
, m_space_read0_cb(*this)
, m_space_read1_cb(*this)
, m_space_read2_cb(*this)
{
}
void radica6502_gpio_device::device_start()
void elan_eu3a05gpio_device::device_start()
{
m_space_read0_cb.resolve_safe(0xff);
m_space_read1_cb.resolve_safe(0xff);
m_space_read2_cb.resolve_safe(0xff);
}
void radica6502_gpio_device::device_reset()
void elan_eu3a05gpio_device::device_reset()
{
for (int i = 0; i < 3; i++)
{
@ -30,7 +30,7 @@ void radica6502_gpio_device::device_reset()
}
}
uint8_t radica6502_gpio_device::read_port_data(int which)
uint8_t elan_eu3a05gpio_device::read_port_data(int which)
{
//todo, actually use the direction registers
switch (which)
@ -43,12 +43,12 @@ uint8_t radica6502_gpio_device::read_port_data(int which)
return 0xff;
}
uint8_t radica6502_gpio_device::read_direction(int which)
uint8_t elan_eu3a05gpio_device::read_direction(int which)
{
return m_ddr[which];
}
READ8_MEMBER(radica6502_gpio_device::gpio_r)
READ8_MEMBER(elan_eu3a05gpio_device::gpio_r)
{
int port = offset/2;
@ -56,19 +56,19 @@ READ8_MEMBER(radica6502_gpio_device::gpio_r)
else return read_port_data(port);
}
void radica6502_gpio_device::write_port_data(int which, uint8_t data)
void elan_eu3a05gpio_device::write_port_data(int which, uint8_t data)
{
//todo, actually use the direction registers
logerror("%s: write_port_data (port %d) %02x (direction register %02x)\n", machine().describe_context(), which, data, m_ddr[which]);
}
void radica6502_gpio_device::write_direction(int which, uint8_t data)
void elan_eu3a05gpio_device::write_direction(int which, uint8_t data)
{
logerror("%s: write_direction (port %d) %02x\n", machine().describe_context(), which, data);
m_ddr[which] = data;
}
WRITE8_MEMBER(radica6502_gpio_device::gpio_w)
WRITE8_MEMBER(elan_eu3a05gpio_device::gpio_w)
{
int port = offset/2;
@ -76,7 +76,7 @@ WRITE8_MEMBER(radica6502_gpio_device::gpio_w)
else return write_port_data(port, data);
}
WRITE8_MEMBER(radica6502_gpio_device::gpio_unk_w)
WRITE8_MEMBER(elan_eu3a05gpio_device::gpio_unk_w)
{
logerror("%s: gpio_unk_w (port %d) %02x (direction register %02x)\n", machine().describe_context(), offset, data, m_ddr[offset]);
}

View File

@ -1,14 +1,14 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_MACHINE_RAD_EU3A05GPIO_H
#define MAME_MACHINE_RAD_EU3A05GPIO_H
#ifndef MAME_MACHINE_ELAN_EU3A05GPIO_H
#define MAME_MACHINE_ELAN_EU3A05GPIO_H
class radica6502_gpio_device : public device_t
class elan_eu3a05gpio_device : public device_t
{
public:
radica6502_gpio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
elan_eu3a05gpio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
auto read_0_callback() { return m_space_read0_cb.bind(); }
auto read_1_callback() { return m_space_read1_cb.bind(); }
@ -38,6 +38,6 @@ private:
uint8_t m_unk[3];
};
DECLARE_DEVICE_TYPE(RADICA6502_GPIO, radica6502_gpio_device)
DECLARE_DEVICE_TYPE(ELAN_EU3A05_GPIO, elan_eu3a05gpio_device)
#endif // MAME_MACHINE_RAD_EU3A05GPIO_H

View File

@ -0,0 +1,74 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#include "emu.h"
#include "elan_eu3a05sys.h"
// DMA size and destination are 16-bit here, they're 24-bit on EU3A14
DEFINE_DEVICE_TYPE(ELAN_EU3A05_SYS, elan_eu3a05sys_device, "elan_eu3a05sys", "Elan EU3A05 System")
elan_eu3a05sys_device::elan_eu3a05sys_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
elan_eu3a05commonsys_device(mconfig, ELAN_EU3A05_SYS, tag, owner, clock)
{
}
void elan_eu3a05sys_device::device_start()
{
elan_eu3a05commonsys_device::device_start();
save_item(NAME(m_dmaparams));
}
void elan_eu3a05sys_device::device_reset()
{
elan_eu3a05commonsys_device::device_reset();
for (int i = 0; i < 7; i++)
m_dmaparams[i] = 0x00;
}
READ8_MEMBER(elan_eu3a05sys_device::dma_param_r)
{
return m_dmaparams[offset];
}
WRITE8_MEMBER(elan_eu3a05sys_device::dma_param_w)
{
m_dmaparams[offset] = data;
}
READ8_MEMBER(elan_eu3a05sys_device::elan_eu3a05_dmatrg_r)
{
logerror("%s: elan_eu3a05_dmatrg_r (DMA operation state?)\n", machine().describe_context());
return 0x00;//m_dmatrg_data;
}
WRITE8_MEMBER(elan_eu3a05sys_device::elan_eu3a05_dmatrg_w)
{
logerror("%s: elan_eu3a05_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_cpu->space(AS_PROGRAM);
if (data)
{
int src = (m_dmaparams[0]) | (m_dmaparams[1] << 8) | (m_dmaparams[2] << 16);
uint16_t dest = m_dmaparams[3] | (m_dmaparams[4] << 8);
uint16_t size = m_dmaparams[5] | (m_dmaparams[6] << 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);
}
}
}

View File

@ -0,0 +1,31 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_MACHINE_ELAN_EU3A05SYS_H
#define MAME_MACHINE_ELAN_EU3A05SYS_H
#include "elan_eu3a05commonsys.h"
class elan_eu3a05sys_device : public elan_eu3a05commonsys_device
{
public:
elan_eu3a05sys_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8_MEMBER(elan_eu3a05_dmatrg_r);
DECLARE_WRITE8_MEMBER(elan_eu3a05_dmatrg_w);
DECLARE_READ8_MEMBER(dma_param_r);
DECLARE_WRITE8_MEMBER(dma_param_w);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
private:
uint8_t m_dmaparams[7];
};
DECLARE_DEVICE_TYPE(ELAN_EU3A05_SYS, elan_eu3a05sys_device)
#endif // MAME_MACHINE_ELAN_EU3A05SYS_H

View File

@ -0,0 +1,71 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#include "emu.h"
#include "elan_eu3a14sys.h"
// DMA size and destination are 24-bit here, they're 16-bit on EU3A05
DEFINE_DEVICE_TYPE(ELAN_EU3A14_SYS, elan_eu3a14sys_device, "elan_eu3a14sys", "Elan EU3A14 System")
elan_eu3a14sys_device::elan_eu3a14sys_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
elan_eu3a05commonsys_device(mconfig, ELAN_EU3A14_SYS, tag, owner, clock)
{
}
void elan_eu3a14sys_device::device_start()
{
elan_eu3a05commonsys_device::device_start();
save_item(NAME(m_dmaparams));
}
void elan_eu3a14sys_device::device_reset()
{
elan_eu3a05commonsys_device::device_reset();
for (int i = 0; i < 9; i++)
m_dmaparams[i] = 0x00;
}
READ8_MEMBER(elan_eu3a14sys_device::dma_param_r)
{
return m_dmaparams[offset];
}
WRITE8_MEMBER(elan_eu3a14sys_device::dma_param_w)
{
m_dmaparams[offset] = data;
}
READ8_MEMBER(elan_eu3a14sys_device::dma_trigger_r)
{
logerror("%s: dma_trigger_r\n", machine().describe_context());
return 0;
}
WRITE8_MEMBER(elan_eu3a14sys_device::dma_trigger_w)
{
uint32_t dmasrc = (m_dmaparams[2] << 16) | (m_dmaparams[1] << 8) | (m_dmaparams[0] << 0);
uint32_t dmadst = (m_dmaparams[5] << 16) | (m_dmaparams[4] << 8) | (m_dmaparams[3] << 0);
uint32_t dmalen = (m_dmaparams[8] << 16) | (m_dmaparams[7] << 8) | (m_dmaparams[6] << 0);
//logerror("%s: dma_trigger_w %02x (src %08x dst %08x size %08x)\n", machine().describe_context(), data, dmasrc, dmadst, dmalen);
address_space& fullbankspace = m_bank->space(AS_PROGRAM);
address_space& destspace = m_cpu->space(AS_PROGRAM);
if (data == 0x08)
{
for (int i = 0; i < dmalen; i++)
{
uint8_t dat = fullbankspace.read_byte(dmasrc + i);
destspace.write_byte(dmadst + i, dat);
}
}
else
{
logerror("UNKNOWN DMA TRIGGER VALUE\n");
}
}

View File

@ -0,0 +1,33 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_MACHINE_ELAN_EU3A14SYS_H
#define MAME_MACHINE_ELAN_EU3A14SYS_H
#include "elan_eu3a05commonsys.h"
class elan_eu3a14sys_device : public elan_eu3a05commonsys_device
{
public:
elan_eu3a14sys_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8_MEMBER(dma_trigger_r);
DECLARE_WRITE8_MEMBER(dma_trigger_w);
DECLARE_READ8_MEMBER(dma_param_r);
DECLARE_WRITE8_MEMBER(dma_param_w);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
private:
uint8_t m_dmaparams[9];
};
DECLARE_DEVICE_TYPE(ELAN_EU3A14_SYS, elan_eu3a14sys_device)
#endif // MAME_MACHINE_ELAN_EU3A14SYS_H

View File

@ -0,0 +1,87 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#include "emu.h"
#include "elan_eu3a05commonvid.h"
/*
Common video functions shared by Elan EU3A05 and EU3A14 CPU types
*/
DEFINE_DEVICE_TYPE(ELAN_EU3A05_COMMONVID, elan_eu3a05commonvid_device, "elan_eu3a05commonvid", "Elan EU3A05/EU3A14 Common Video")
elan_eu3a05commonvid_device::elan_eu3a05commonvid_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: elan_eu3a05commonvid_device(mconfig, ELAN_EU3A05_COMMONVID, tag, owner, clock)
{
}
elan_eu3a05commonvid_device::elan_eu3a05commonvid_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, m_palette(*this, finder_base::DUMMY_TAG)
{
}
void elan_eu3a05commonvid_device::device_start()
{
}
void elan_eu3a05commonvid_device::device_reset()
{
}
double elan_eu3a05commonvid_device::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;
}
void elan_eu3a05commonvid_device::update_pen(int pen)
{
uint16_t dat = m_palram[(pen*2)] << 8;
dat |= m_palram[(pen*2)+1];
// 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(pen, r_real, g_real, b_real);
}
READ8_MEMBER(elan_eu3a05commonvid_device::palette_r)
{
return m_palram[offset];
}
WRITE8_MEMBER(elan_eu3a05commonvid_device::palette_w)
{
m_palram[offset] = data;
update_pen(offset/2);
}

View File

@ -0,0 +1,35 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_VIDEO_ELAN_EU3A05COMMONVID_H
#define MAME_VIDEO_ELAN_EU3A05COMMONVID_H
#include "emupal.h"
class elan_eu3a05commonvid_device : public device_t
{
public:
elan_eu3a05commonvid_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
elan_eu3a05commonvid_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
template <typename T> void set_palette(T &&tag) { m_palette.set_tag(std::forward<T>(tag)); }
void set_entries(int entries) { m_palram.resize(entries*2); }
DECLARE_READ8_MEMBER(palette_r);
DECLARE_WRITE8_MEMBER(palette_w);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
private:
required_device<palette_device> m_palette;
std::vector<uint8_t> m_palram;
void update_pen(int pen);
double hue2rgb(double p, double q, double t);
};
DECLARE_DEVICE_TYPE(ELAN_EU3A05_COMMONVID, elan_eu3a05commonvid_device)
#endif // MAME_VIDEO_ELAN_EU3A05COMMONVID_H

View File

@ -0,0 +1,596 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#include "emu.h"
#include "elan_eu3a05vid.h"
DEFINE_DEVICE_TYPE(ELAN_EU3A05_VID, elan_eu3a05vid_device, "elan_eu3a05vid", "Elan EU3A05 Video")
// map(0x0600, 0x3dff).ram().share("vram");
// map(0x3e00, 0x3fff).ram().share("spriteram");
elan_eu3a05vid_device::elan_eu3a05vid_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: elan_eu3a05commonvid_device(mconfig, ELAN_EU3A05_VID, tag, owner, clock),
m_cpu(*this, finder_base::DUMMY_TAG),
m_bank(*this, finder_base::DUMMY_TAG)
{
}
void elan_eu3a05vid_device::device_start()
{
elan_eu3a05commonvid_device::device_start();
save_item(NAME(m_vidctrl));
save_item(NAME(m_tile_gfxbase_lo_data));
save_item(NAME(m_tile_gfxbase_hi_data));
save_item(NAME(m_sprite_gfxbase_lo_data));
save_item(NAME(m_sprite_gfxbase_hi_data));
save_item(NAME(m_tile_scroll));
save_item(NAME(m_splitpos));
}
void elan_eu3a05vid_device::device_reset()
{
elan_eu3a05commonvid_device::device_reset();
m_vidctrl = 0x00; // need to default to an 8x8 mode for Space Invaders test mode at least
for (int i=0;i<2;i++)
m_tile_scroll[i] = 0x00;
for (int i=0;i<2;i++)
m_tile_xscroll[i] = 0x00;
m_tile_gfxbase_lo_data = 0x00;
m_tile_gfxbase_hi_data = 0x00;
m_sprite_gfxbase_lo_data = 0x00;
m_sprite_gfxbase_hi_data = 0x00;
m_splitpos = 0x00;
}
uint8_t elan_eu3a05vid_device::read_spriteram(int offset)
{
address_space& cpuspace = m_cpu->space(AS_PROGRAM);
int realoffset = offset+0x3e00;
if (realoffset < 0x4000)
{
return cpuspace.read_byte(realoffset);
}
else
return 0x00;
}
uint8_t elan_eu3a05vid_device::read_vram(int offset)
{
address_space& cpuspace = m_cpu->space(AS_PROGRAM);
int realoffset = offset+0x0600;
if (realoffset < 0x4000)
{
return cpuspace.read_byte(realoffset);
}
else
return 0x00;
}
/* (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 elan_eu3a05vid_device::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
address_space& fullbankspace = m_bank->space(AS_PROGRAM);
/*
Sprites
FF yy xx AA 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
AA = same as attr on tiles (colour / priority?)
FF = flags ( e-dD fFsS )
e = enable
D = ZoomX to double size (boss explosions on Air Blaster)
d = ZoomY to double size (boss explosions on Air Blaster)
S = SizeX
s = SizeY
F = FlipX
f = FlipY (assumed, not seen)
*/
for (int i = 0; i < 512; i += 8)
{
uint8_t x = read_spriteram(i + 2);
uint8_t y = read_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!
Air Blaster Joystick seems to indicate there is no sprite wrapping - sprites abruptly enter
the screen in pieces on real hardware.
needs further investigation.
*/
if (y==255)
continue;
uint8_t tex_x = read_spriteram(i + 4);
uint8_t tex_y = read_spriteram(i + 5);
uint8_t flags = read_spriteram(i + 0);
uint8_t attr = read_spriteram(i + 3);
uint8_t unk2 = read_spriteram(i + 6);
const int doubleX = (flags & 0x10)>>4;
const int doubleY = (flags & 0x20)>>5;
//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;
if (doubleX)
sizex = sizex * 2;
if (doubleY)
sizey = sizey * 2;
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;
if (!doubleX)
realaddr = base + ((tex_x + xx) & 0xff);
else
realaddr = base + ((tex_x + (xx>>1)) & 0xff);
if (!doubleY)
realaddr += ((tex_y + yy) & 0xff) * 256;
else
realaddr += ((tex_y + (yy>>1)) & 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;
}
}
}
}
}
}
// a hacky mess for now
bool elan_eu3a05vid_device::get_tile_data(int base, int drawpri, int& tile, int &attr, int &unk2)
{
tile = read_vram(base * 4) + (read_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 = read_vram((base * 4) + 2);
unk2 = read_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 elan_eu3a05vid_device::draw_tilemaps(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int drawpri)
{
/*
this doesn't handle 8x8 4bpp (not used by anything yet)
*/
int scroll = (m_tile_scroll[1] << 8) | m_tile_scroll[0];
address_space& fullbankspace = m_bank->space(AS_PROGRAM);
// Phoenix scrolling actually skips a pixel, jumping from 0x001 to 0x1bf, scroll 0x000 isn't used, maybe it has other meanings?
int totalyrow;
int totalxcol;
int mapyrowsbase;
int tileysize;
int tilexsize;
int startrow;
if (m_vidctrl & 0x40) // 16x16 tiles
{
totalyrow = 16;
totalxcol = 16;
mapyrowsbase = 14;
tileysize = 16;
tilexsize = 16;
startrow = (scroll >> 4) & 0x1f;
}
else
{
totalyrow = 32;
totalxcol = 32;
mapyrowsbase = 28;
tileysize = 8;
tilexsize = 8;
startrow = (scroll >> 3) & 0x3f;
}
for (int y = 0; y < totalyrow; y++)
{
for (int x = 0; x < totalxcol * 2; x++)
{
int realstartrow = (startrow + y);
int yrows;
if (m_vidctrl & 0x01)
yrows = mapyrowsbase;
else
yrows = mapyrowsbase * 2;
if (realstartrow >= yrows)
realstartrow -= yrows;
// in double width & double height mode the page addressing needs adjusting
if (!(m_vidctrl & 0x02))
{
if (!(m_vidctrl & 0x01))
{
if (realstartrow >= (yrows / 2))
{
realstartrow += yrows / 2;
}
}
}
for (int i = 0; i < tileysize; i++)
{
int drawline = (y * tileysize) + i;
drawline -= scroll & (tileysize - 1);
if ((drawline >= 0) && (drawline < 256))
{
int scrollx;
// split can be probably configured in more ways than this
if (drawline > m_splitpos)
scrollx = get_xscroll(1);
else
scrollx = get_xscroll(0);
int base;
if (m_vidctrl & 0x40) // 16x16 tiles
{
base = (((realstartrow + y) & 0x3f) * 8) + x;
}
else
{
base = (((realstartrow) & 0x7f) * totalxcol) + (x & (totalxcol - 1));
}
if (!(m_vidctrl & 0x02))
{
if (x & totalxcol)
{
base += totalxcol * mapyrowsbase;
}
}
int tile, attr, unk2;
if (!get_tile_data(base, drawpri, tile, attr, unk2))
continue;
int colour = attr & 0xf0;
if (m_vidctrl & 0x40) // 16x16 tiles
{
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);
}
}
else
{
if (m_vidctrl & 0x20) // 4bpp
{
// TODO
tile = 0x0000;//machine().rand() & 0x1ff;
}
else
{
tile = (tile & 0x1f) + ((tile & ~0x1f) * 8);
tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5);
}
}
uint16_t* row = &bitmap.pix16(drawline);
if (m_vidctrl & 0x40) // 16x16 tiles
{
if (m_vidctrl & 0x20) // 4bpp
{
for (int xx = 0; xx < tilexsize; xx += 2)
{
int realaddr = ((tile + i * 16) << 3) + (xx >> 1);
uint8_t pix = fullbankspace.read_byte(realaddr);
int drawxpos;
drawxpos = x * 16 + xx + 0;
drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = ((pix & 0xf0) >> 4) + colour;
drawxpos = x * 16 + xx + 1;
drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = ((pix & 0x0f) >> 0) + colour;
}
}
else // 8bpp
{
for (int xx = 0; xx < tilexsize; xx++)
{
int realaddr = ((tile + i * 32) << 3) + xx;
uint8_t pix = fullbankspace.read_byte(realaddr);
int drawxpos = x * 16 + xx;
drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = (pix + ((colour & 0x70) << 1)) & 0xff;
}
}
}
else // 8x8 tiles
{
if (m_vidctrl & 0x20) // 4bpp
{
// TODO
}
else
{
for (int xx = 0; xx < tilexsize; xx++)
{
const int realaddr = ((tile + i * 32) << 3) + xx;
const uint8_t pix = fullbankspace.read_byte(realaddr);
int drawxpos = x * tilexsize + xx - scrollx;
drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = (pix + ((colour & 0x70) << 1)) & 0xff;
}
}
}
}
}
}
}
}
uint32_t elan_eu3a05vid_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
bitmap.fill(0, cliprect);
draw_tilemaps(screen,bitmap,cliprect,0);
draw_sprites(screen,bitmap,cliprect);
draw_tilemaps(screen,bitmap,cliprect,1);
return 0;
}
// Tile bases
WRITE8_MEMBER(elan_eu3a05vid_device::tile_gfxbase_lo_w)
{
//logerror("%s: tile_gfxbase_lo_w (select GFX base lower) %02x\n", machine().describe_context(), data);
m_tile_gfxbase_lo_data = data;
}
WRITE8_MEMBER(elan_eu3a05vid_device::tile_gfxbase_hi_w)
{
//logerror("%s: tile_gfxbase_hi_w (select GFX base upper) %02x\n", machine().describe_context(), data);
m_tile_gfxbase_hi_data = data;
}
READ8_MEMBER(elan_eu3a05vid_device::tile_gfxbase_lo_r)
{
//logerror("%s: tile_gfxbase_lo_r (GFX base lower)\n", machine().describe_context());
return m_tile_gfxbase_lo_data;
}
READ8_MEMBER(elan_eu3a05vid_device::tile_gfxbase_hi_r)
{
//logerror("%s: tile_gfxbase_hi_r (GFX base upper)\n", machine().describe_context());
return m_tile_gfxbase_hi_data;
}
// Sprite Tile bases
WRITE8_MEMBER(elan_eu3a05vid_device::sprite_gfxbase_lo_w)
{
//logerror("%s: sprite_gfxbase_lo_w (select Sprite GFX base lower) %02x\n", machine().describe_context(), data);
m_sprite_gfxbase_lo_data = data;
}
WRITE8_MEMBER(elan_eu3a05vid_device::sprite_gfxbase_hi_w)
{
//logerror("%s: sprite_gfxbase_hi_w (select Sprite GFX base upper) %02x\n", machine().describe_context(), data);
m_sprite_gfxbase_hi_data = data;
}
READ8_MEMBER(elan_eu3a05vid_device::sprite_gfxbase_lo_r)
{
//logerror("%s: sprite_gfxbase_lo_r (Sprite GFX base lower)\n", machine().describe_context());
return m_sprite_gfxbase_lo_data;
}
READ8_MEMBER(elan_eu3a05vid_device::sprite_gfxbase_hi_r)
{
//logerror("%s: sprite_gfxbase_hi_r (Sprite GFX base upper)\n", machine().describe_context());
return m_sprite_gfxbase_hi_data;
}
READ8_MEMBER(elan_eu3a05vid_device::tile_scroll_r)
{
return m_tile_scroll[offset];
}
WRITE8_MEMBER(elan_eu3a05vid_device::tile_scroll_w)
{
m_tile_scroll[offset] = data;
}
READ8_MEMBER(elan_eu3a05vid_device::tile_xscroll_r)
{
return m_tile_xscroll[offset];
}
WRITE8_MEMBER(elan_eu3a05vid_device::tile_xscroll_w)
{
m_tile_xscroll[offset] = data;
}
READ8_MEMBER(elan_eu3a05vid_device::splitpos_r)
{
return m_splitpos;
}
WRITE8_MEMBER(elan_eu3a05vid_device::splitpos_w)
{
m_splitpos = data;
}
uint16_t elan_eu3a05vid_device::get_xscroll(int which)
{
switch (which)
{
case 0x0: return (m_tile_xscroll[1] << 8) | (m_tile_xscroll[0]);
case 0x1: return (m_tile_xscroll[3] << 8) | (m_tile_xscroll[2]);
}
return 0x0000;
}
READ8_MEMBER(elan_eu3a05vid_device::elan_eu3a05_vidctrl_r)
{
return m_vidctrl;
}
WRITE8_MEMBER(elan_eu3a05vid_device::elan_eu3a05_vidctrl_w)
{
logerror("%s: elan_eu3a05_vidctrl_w %02x (video control?)\n", machine().describe_context(), data);
/*
c3 8bpp 16x16 1100 0011 abl logo
e3 4bpp 16x16 1110 0011
83 8bpp 8x8 1000 0011 air blaster logo
02 8bpp 8x8 (phoenix) 0000 0010 air blaster 2d normal
03 8bpp 8x8 0000 0011 air blaster 2d bosses
00 0000 0000 air blaster 3d stages
?tb- --wh
? = unknown
t = tile size (1 = 16x16, 0 = 8x8)
b = bpp (0 = 8bpp, 1 = 4bpp)
- = haven't seen used
h = tilemap height? (0 = double height)
w = tilemap width? (0 = double width)
space invaders test mode doesn't initialize this
*/
m_vidctrl = data;
}

View File

@ -0,0 +1,80 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
#ifndef MAME_VIDEO_ELAN_EU3A05VID_H
#define MAME_VIDEO_ELAN_EU3A05VID_H
#include "elan_eu3a05commonvid.h"
#include "cpu/m6502/m6502.h"
#include "machine/bankdev.h"
class elan_eu3a05vid_device : public elan_eu3a05commonvid_device
{
public:
elan_eu3a05vid_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
template <typename T> void set_cpu(T &&tag) { m_cpu.set_tag(std::forward<T>(tag)); }
template <typename T> void set_addrbank(T &&tag) { m_bank.set_tag(std::forward<T>(tag)); }
// VIDEO
// tile bases
DECLARE_WRITE8_MEMBER(tile_gfxbase_lo_w);
DECLARE_WRITE8_MEMBER(tile_gfxbase_hi_w);
DECLARE_READ8_MEMBER(tile_gfxbase_lo_r);
DECLARE_READ8_MEMBER(tile_gfxbase_hi_r);
// sprite tile bases
DECLARE_WRITE8_MEMBER(sprite_gfxbase_lo_w);
DECLARE_WRITE8_MEMBER(sprite_gfxbase_hi_w);
DECLARE_READ8_MEMBER(sprite_gfxbase_lo_r);
DECLARE_READ8_MEMBER(sprite_gfxbase_hi_r);
DECLARE_READ8_MEMBER(elan_eu3a05_vidctrl_r);
DECLARE_WRITE8_MEMBER(elan_eu3a05_vidctrl_w);
DECLARE_READ8_MEMBER(tile_scroll_r);
DECLARE_WRITE8_MEMBER(tile_scroll_w);
DECLARE_READ8_MEMBER(tile_xscroll_r);
DECLARE_WRITE8_MEMBER(tile_xscroll_w);
DECLARE_READ8_MEMBER(splitpos_r);
DECLARE_WRITE8_MEMBER(splitpos_w);
uint16_t get_xscroll(int which);
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
private:
required_device<m6502_device> m_cpu;
required_device<address_map_bank_device> m_bank;
uint8_t m_vidctrl;
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_tile_scroll[2];
uint8_t m_tile_xscroll[4];
uint8_t m_splitpos;
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);
uint8_t read_spriteram(int offset);
uint8_t read_vram(int offset);
};
DECLARE_DEVICE_TYPE(ELAN_EU3A05_VID, elan_eu3a05vid_device)
#endif // MAME_VIDEO_ELAN_EU3A05VID_H