summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/elan_eu3a05commonsys.cpp
blob: b4695f5e5b8ed56f867eb53e56951f6e95839699 (plain) (tree)
1
2
3
4
5
6
7
8
9






                                  
                                                                                              
 

                                                                                                     
 
                                                                                   




  
                           
                             
                           




























                                                                                           
                           


























                              
 





                              
 




















                                


                           
 
                                                                                              
 















                                                                                           
 
            
 


                                            

 
                           
 
                   
 



             
 



             
 



                   
 

                                                                                            
 




                                                                                      









                                                                                                                                                             
                                              

                               







                                                                                                                                           
























                                                                                                                                      
























                                                                                                                                                                             



                                                                                                                                  





                              


                                                





                                             


                                             

















                                                

                                                                                                                           















                                                                                 



                                                                 






























                                                                                                                                            


                                                     






                                                         


                                             





                                                         


                                                     
































                                                                                                                        

































                                                                                                
// 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),
	m_is_pal(false),
	m_allow_timer_irq(true)
{
}

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)
{
}

/*

rad_bb3 plays with address 0x5009 in this way, but never sets it, something else must set it (and 1->0 is 'activate' or 'acknowledge')

lda $5009
and #$ef
sta $5009

lda $5009
and #$df
sta $5009

0x5006 looks interesting too, again just seems to be masking out bits

(as one block of code)
lda $5006
and #$f0
sta $5006
lda $5006
and $#8f
sta $5006

todo: investigate rad_hnt3 which polls this address

*/

void elan_eu3a05commonsys_device::map(address_map &map)
{
	map(0x00, 0x00).ram();
	map(0x01, 0x01).ram();
	// 5002
	map(0x03, 0x03).r(FUNC(elan_eu3a05commonsys_device::elan_eu3a05_5003_r));
	map(0x04, 0x04).ram();
	// 5005
	map(0x06, 0x06).ram();
	map(0x07, 0x08).rw(FUNC(elan_eu3a05commonsys_device::intmask_r), FUNC(elan_eu3a05commonsys_device::intmask_w));
	map(0x09, 0x09).r(FUNC(elan_eu3a05commonsys_device::radica_5009_unk_r)); // rad_hnt3 polls this on startup
	map(0x0a, 0x0a).ram();
	map(0x0b, 0x0b).rw(FUNC(elan_eu3a05commonsys_device::elan_eu3a05_pal_ntsc_r), FUNC(elan_eu3a05commonsys_device::elan_eu3a05_500b_unk_w)); // PAL / NTSC flag at least
	map(0x0c, 0x0d).rw(FUNC(elan_eu3a05commonsys_device::elan_eu3a05_rombank_r), FUNC(elan_eu3a05commonsys_device::elan_eu3a05_rombank_w));
	// 0e
}


void elan_eu3a05commonsys_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_UNK:
		{
			// rad_bb3 unmasks the interrupt, but the jumps use pointers in RAM, which haven't been set up at the time
			// of unmasking, so we need to find some kind of global enable / disable, or timer enable.
			if (m_allow_timer_irq)
				generate_custom_interrupt(0);
			break;
		}
	}
}


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));

	m_unk_timer = timer_alloc(TIMER_UNK);
	m_unk_timer->adjust(attotime::never);
}

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);

	// generate at a fixed frequency for now, but can probably be configured.  drives 3D stages in Air Blaster Joystick
	m_unk_timer->adjust(attotime::from_hz(4096), 0, attotime::from_hz(2048));
}

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
	{
		if(machine().side_effects_disabled())
			return 0x00;

		fatalerror("NMI without custom vector!");
	}
}

// not currently used
READ8_MEMBER(elan_eu3a05commonsys_device::irq_vector_r)
{
	if(machine().side_effects_disabled())
		return 0x00;

	if (m_custom_irq)
	{
		return m_custom_irq_vector >> (offset*8);
	}
	else
	{
		if(machine().side_effects_disabled())
			return 0x00;

		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;
	}
}


READ8_MEMBER(elan_eu3a05commonsys_device::elan_eu3a05_pal_ntsc_r)
{
	// the text under the radica logo differs between regions, sometimes the titles too
	logerror("%s: elan_eu3a05_pal_ntsc_r (region + more?)\n", machine().describe_context());
	if (!m_is_pal) return 0xff; // NTSC
	else return 0x00; // PAL
}

READ8_MEMBER(elan_eu3a05commonsys_device::elan_eu3a05_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: elan_eu3a05_5003_r (RNG?)\n", machine().describe_context());

	return machine().rand();
}



WRITE8_MEMBER(elan_eu3a05commonsys_device::elan_eu3a05_500b_unk_w)
{
	// this is the PAL / NTSC flag when read, so what are writes?
	logerror("%s: elan_eu3a05_500b_unk_w %02x\n", machine().describe_context(), data);
}