summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/elan_eu3a05commonsys.cpp
blob: 38713b8789c196c9297bddbd4881e4b4a15545c6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
// 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),
	m_whichtimer(0)
{
}

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(m_whichtimer);
			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));
}

uint8_t elan_eu3a05commonsys_device::intmask_r(offs_t offset)
{
	return m_intmask[offset];
}

void elan_eu3a05commonsys_device::intmask_w(offs_t offset, uint8_t data)
{
	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);
	}
}

uint8_t elan_eu3a05commonsys_device::nmi_vector_r(offs_t offset)
{
	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
uint8_t elan_eu3a05commonsys_device::irq_vector_r(offs_t offset)
{
	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!");
	}
}


void elan_eu3a05commonsys_device::elan_eu3a05_rombank_w(offs_t offset, uint8_t data)
{
	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;
	}
}

uint8_t elan_eu3a05commonsys_device::elan_eu3a05_rombank_r(offs_t offset)
{
	if (offset == 0x00)
	{
		return m_rombank_hi;
	}
	else
	{
		return m_rombank_lo;
	}
}


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

uint8_t 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();
}



void elan_eu3a05commonsys_device::elan_eu3a05_500b_unk_w(uint8_t data)
{
	// 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);
}