summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/jaleco_ms32_sysctrl.cpp
blob: c9007080a663a83a9257b3c1e2ea1c7b9b9fce60 (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
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Alex Marshall
/******************************************************************************

    Jaleco MS32 System Control Unit

    A simple system controller for late 90s Jaleco HWs

    Known features:
    - CRTC & screen(s?) control;
    - dot clock control;
    - irq/reset controller;
    - programmable timer;
    - watchdog;

    First use in MS32, then their later (?) 68k revision.

    TODO:
    - pinpoint exact timing generation for programmable irq
      (free counter or based on host screen beams)
    - interface with multiple screens is a mystery,
      cfr. dual screen bnstars, stepping stage HW.
      Most likely former effectively controls both screens in demux while
      latter has no way to set the other screen(s)?
    - watchdog timing;
    - upper address line seems unconnected by 68k,
      and is it a mystery how watchdog is supposed to route here and assuming
      it is and not actually disabled by pin.
    - network/COPROs irq connections, specifically for f1superb;
    - actual chip name;

    BTANBs:
    - in p47aces v1.0 (p47acesa) code messes up the prg irq timer setup,
      causing SFX overloads by using Spitfire ship with 30 Hz autofire
      and shooting at point blank range over walls/enemies.
      This has been fixed in v1.1

*******************************************************************************/

#include "emu.h"
#include "jaleco_ms32_sysctrl.h"

//*****************************************************************************
//  GLOBAL VARIABLES
//*****************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(JALECO_MS32_SYSCTRL, jaleco_ms32_sysctrl_device, "jaleco_ms32_sysctrl", "Jaleco MS32 System Control Unit")


//*****************************************************************************
//  LIVE DEVICE
//*****************************************************************************

//-------------------------------------------------
//  jaleco_ms32_sysctrl_device - constructor
//-------------------------------------------------

jaleco_ms32_sysctrl_device::jaleco_ms32_sysctrl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, JALECO_MS32_SYSCTRL, tag, owner, clock)
	, m_screen(*this, finder_base::DUMMY_TAG)
	, m_flip_screen_cb(*this)
	, m_vblank_cb(*this)
	, m_field_cb(*this)
	, m_prg_timer_cb(*this)
	, m_sound_ack_cb(*this)
	, m_sound_reset_cb(*this)
	, m_invert_vblank_lines(false)
{
}

void jaleco_ms32_sysctrl_device::amap(address_map& map)
{
//  0xba0000 in 68k, 0xfce00000 in MS32 mapped at lower 16-bits mask
	map(0x00, 0x01).w(FUNC(jaleco_ms32_sysctrl_device::control_w));
	map(0x02, 0x03).w(FUNC(jaleco_ms32_sysctrl_device::hblank_w));
	map(0x04, 0x05).w(FUNC(jaleco_ms32_sysctrl_device::hdisplay_w));
	map(0x06, 0x07).w(FUNC(jaleco_ms32_sysctrl_device::hbp_w));
	map(0x08, 0x09).w(FUNC(jaleco_ms32_sysctrl_device::hfp_w));
	map(0x0a, 0x0b).w(FUNC(jaleco_ms32_sysctrl_device::vblank_w));
	map(0x0c, 0x0d).w(FUNC(jaleco_ms32_sysctrl_device::vdisplay_w));
	map(0x0e, 0x0f).w(FUNC(jaleco_ms32_sysctrl_device::vbp_w));
	map(0x10, 0x11).w(FUNC(jaleco_ms32_sysctrl_device::vfp_w));
	map(0x18, 0x19).w(FUNC(jaleco_ms32_sysctrl_device::timer_interval_w));
	map(0x1a, 0x1b).w(FUNC(jaleco_ms32_sysctrl_device::timer_ack_w));
	map(0x1c, 0x1d).w(FUNC(jaleco_ms32_sysctrl_device::sound_reset_w));
	map(0x1e, 0x1f).w(FUNC(jaleco_ms32_sysctrl_device::irq_ack_w));
//  map(0x24, 0x27).w // sound comms bidirectional acks?
	map(0x26, 0x27).w(FUNC(jaleco_ms32_sysctrl_device::sound_ack_w));
	map(0x28, 0x29).nopw(); // watchdog on MS32
	map(0x2c, 0x2d).w(FUNC(jaleco_ms32_sysctrl_device::field_ack_w));
	map(0x2e, 0x2f).w(FUNC(jaleco_ms32_sysctrl_device::vblank_ack_w));
}

//-------------------------------------------------
//  device_add_mconfig - device-specific machine
//  configuration additions
//-------------------------------------------------

void jaleco_ms32_sysctrl_device::device_add_mconfig(machine_config &config)
{
//  TIMER(config, "scantimer").configure_scanline(FUNC(jaleco_ms32_sysctrl_device::scanline_cb), m_screen, 0, 1);

	// TODO: watchdog
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void jaleco_ms32_sysctrl_device::device_resolve_objects()
{
	m_flip_screen_cb.resolve();
	m_vblank_cb.resolve();
	m_field_cb.resolve();
	m_prg_timer_cb.resolve();
	m_sound_reset_cb.resolve();
	m_sound_ack_cb.resolve();
}

void jaleco_ms32_sysctrl_device::device_start()
{
	save_item(NAME(m_dotclock_setting));
	save_item(NAME(m_crtc.horz_blank));
	save_item(NAME(m_crtc.horz_display));
	save_item(NAME(m_crtc.vert_blank));
	save_item(NAME(m_crtc.vert_display));
	save_item(NAME(m_flip_screen_state));
	save_item(NAME(m_timer.irq_enable));
	save_item(NAME(m_timer.interval));

	m_timer.prg_irq = timer_alloc(PRG_TIMER);
	m_timer_scanline = timer_alloc(SCANLINE_TIMER);
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void jaleco_ms32_sysctrl_device::device_reset()
{
	m_dotclock_setting = 0;
	m_crtc.horz_blank = 64;
	m_crtc.horz_display = 320;
	m_crtc.vert_blank = 39;
	m_crtc.vert_display = 224;
	m_timer.irq_enable = false;
	m_timer.interval = 1;
	flush_prg_timer();
	// bnstars1 never ever set up the CRTC at boot, causing no irq.
	// we currently compensate by basically giving one frame of time,
	// ofc on the real thing the first vblank is really when screen sync occurs.
	flush_scanline_timer(m_crtc.vert_display-1);
//  m_timer_scanline->adjust(attotime::never);
	// put flipping in a default state
	m_flip_screen_state = false;
	m_flip_screen_cb(0);
}

void jaleco_ms32_sysctrl_device::flush_prg_timer()
{
	attotime step;
	if (m_timer.irq_enable)
	{
		//const u16 htotal = m_crtc.horz_blank + m_crtc.horz_display;
		// TODO: unknown actual timings, with interval = 2 it should fire an irq every 16 scanlines in p47aces v1.1
		//       (excluding times where it is disabled) -> ~1000 Hz?
		step = attotime::from_nsec(500000) * m_timer.interval;
	}
	else
	{
		step = attotime::never;
		m_prg_timer_cb(0);
	}
	m_timer.prg_irq->adjust(step);
}

void jaleco_ms32_sysctrl_device::flush_scanline_timer(int current_scanline)
{
	// in typical Jaleco fashion (cfr. mega system 1), both irqs are somehow configurable (a pin?).
	// Examples are tp2ms32 and wpksocv2, wanting vblank as vector 9 and field as 10 otherwise they runs
	// at half speed, but then their config can't possibly work with p47aces (i.e. wants 10 and 9 respectively),
	// plus bnstars that locks up off the bat if the wrong irq runs at 60 Hz.
	// We currently hardwire via an init time setter here, making the irq acks to trigger properly as well.

	if (current_scanline == m_crtc.vert_display)
		m_invert_vblank_lines ? m_field_cb(1) : m_vblank_cb(1);

	// 30 Hz irq
	// TODO: unknown mechanics where this happens, is it even tied to scanline?
	if (current_scanline == 0 && m_screen->frame_number() & 1)
		m_invert_vblank_lines ? m_vblank_cb(1) : m_field_cb(1);

	uint32_t next_scanline = (current_scanline + 1) % crtc_vtotal();
	m_timer_scanline->adjust(m_screen->time_until_pos(next_scanline), next_scanline);
}

void jaleco_ms32_sysctrl_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case SCANLINE_TIMER:
			flush_scanline_timer(param);
			break;
		case PRG_TIMER:
			m_prg_timer_cb(1);
			flush_prg_timer();
			break;
	}
}

//*****************************************************************************
//  READ/WRITE HANDLERS
//*****************************************************************************

static constexpr u16 clamp_to_12bits_neg(u16 raw_data)
{
	// each write has a 12 bit resolution, for both CRTC and timer interval
	// TODO: nndmseal: on POST it sets up a 0x1000 for vblank, possibly for screen disable?
	// TODO: rockn2: on POST it sets up a vertical size of 487, is it trying to setup an interlace setting?
	return 0x1000 - (raw_data & 0xfff);
}

// ============================================================================
// CRTC
// ============================================================================

inline u32 jaleco_ms32_sysctrl_device::get_dotclock_frequency()
{
	const u8 dot_divider = m_dotclock_setting & 1 ? 6 : 8;
	return clock() / dot_divider;
}

inline u16 jaleco_ms32_sysctrl_device::crtc_vtotal()
{
	return m_crtc.vert_blank + m_crtc.vert_display;
}

inline void jaleco_ms32_sysctrl_device::crtc_refresh_screen_params()
{
	rectangle visarea;
	const u16 htotal = m_crtc.horz_blank + m_crtc.horz_display;
	const u16 vtotal = crtc_vtotal();
	const attoseconds_t refresh = HZ_TO_ATTOSECONDS(get_dotclock_frequency()) * htotal * vtotal;
	visarea.set(0, m_crtc.horz_display - 1, 0, m_crtc.vert_display - 1);
	logerror("%s: CRTC setup total: %d x %d display: %d x %d\n", this->tag(), htotal, vtotal, m_crtc.horz_display, m_crtc.vert_display);
	m_screen->configure(htotal, vtotal, visarea, refresh);
	m_timer_scanline->adjust(m_screen->time_until_pos(vtotal), vtotal);
}

void jaleco_ms32_sysctrl_device::control_w(u16 data)
{
	/*
	 * ---- x--- programmable irq timer enable
	 * ---- -x-- used by f1superb, stepstag, bnstars
	 * ---- --x- flip screen
	 * ---- ---x dotclock select (1) 8 MHz (0) 6 MHz
	 */
	if (BIT(data, 0) != m_dotclock_setting)
	{
		m_dotclock_setting = BIT(data, 0);
		crtc_refresh_screen_params();
	}

	const bool current_flip = bool(BIT(data, 1));
	if (current_flip != m_flip_screen_state)
	{
		m_flip_screen_state = current_flip;
		m_flip_screen_cb(m_flip_screen_state ? ASSERT_LINE : CLEAR_LINE);
	}
	if (data & 0xf4)
		logerror("%s: enabled unknown bit in control_w %02x\n", this->tag(), data & 0xf4);

	m_timer.irq_enable = bool(BIT(data, 3));
	flush_prg_timer();
}

void jaleco_ms32_sysctrl_device::hblank_w(u16 data)
{
	m_crtc.horz_blank = clamp_to_12bits_neg(data);
	crtc_refresh_screen_params();
}

void jaleco_ms32_sysctrl_device::hdisplay_w(u16 data)
{
	m_crtc.horz_display = clamp_to_12bits_neg(data);
	crtc_refresh_screen_params();
}

void jaleco_ms32_sysctrl_device::hbp_w(u16 data)
{
	logerror("%s: HSYNC back porch %d\n", this->tag(), 0x1000 - data);
}

void jaleco_ms32_sysctrl_device::hfp_w(u16 data)
{
	logerror("%s: HSYNC front porch %d\n", this->tag(), 0x1000 - data);
}

void jaleco_ms32_sysctrl_device::vblank_w(u16 data)
{
	m_crtc.vert_blank = clamp_to_12bits_neg(data);
	crtc_refresh_screen_params();
}

void jaleco_ms32_sysctrl_device::vdisplay_w(u16 data)
{
	m_crtc.vert_display = clamp_to_12bits_neg(data);
	crtc_refresh_screen_params();
}

void jaleco_ms32_sysctrl_device::vbp_w(u16 data)
{
	logerror("%s: VSYNC back porch %d\n", this->tag(), clamp_to_12bits_neg(data));
}

void jaleco_ms32_sysctrl_device::vfp_w(u16 data)
{
	logerror("%s: VSYNC front porch %d\n", this->tag(), clamp_to_12bits_neg(data));
}

// ============================================================================
// Timer
// ============================================================================

void jaleco_ms32_sysctrl_device::timer_interval_w(u16 data)
{
	m_timer.interval = clamp_to_12bits_neg(data);
	flush_prg_timer();
}

void jaleco_ms32_sysctrl_device::timer_ack_w(u16 data)
{
	m_prg_timer_cb(0);
}

// ============================================================================
// ACK lines
// ============================================================================

void jaleco_ms32_sysctrl_device::sound_reset_w(u16 data)
{
	m_sound_reset_cb(data & 1);
	logerror("%s: sound_reset_w %02x\n", this->tag(), data);
}

void jaleco_ms32_sysctrl_device::vblank_ack_w(u16 data)
{
	m_vblank_cb(0);
}

void jaleco_ms32_sysctrl_device::field_ack_w(u16 data)
{
	m_field_cb(0);
}

void jaleco_ms32_sysctrl_device::sound_ack_w(u16 data)
{
	m_sound_ack_cb(1);
}

void jaleco_ms32_sysctrl_device::irq_ack_w(u16 data)
{
	// guess: 68k games calls this in vblank routine instead of
	// the designated line, maybe it's a 68k version difference
	// or maybe this is right
	m_vblank_cb(0);
	m_field_cb(0);
	// TODO: f1superb definitely clears comms irq with this
}