summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/nb1412m2.cpp
blob: ee9e49bc2c0cef1d4303aa07b2d0172bf20fa43a (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
// license:LGPL-2.1+
// copyright-holders:Angelo Salese
/***************************************************************************

Nichibutsu 1412M2 device emulation

Written by Angelo Salese

Fancy data decrypter + timer + ?
Most creative usage is hooked up in Mighty Guy sound CPU,
other games just uses it as a much simpler protection chip providing code
snippets.
It is unknown at current stage if inside the chip there's a MCU
(with internal ROM).

TODO:
- main sound timer is "jumpy" (like BGM tempo gets screwy then fixes itself somehow),
  fiddling with port 0x90 seems to improve things, why?
- DAC timer is guessworked;

Legacy notes from drivers:
- m_mAmazonProtReg[4] bit 0 used on hiscore data (clear on code),
  0x29f vs 0x29e (not an offset?)
- static const uint16_t mAmazonProtData[] =
{
    0x0000,0x5000,0x5341,0x4b45,0x5349,0x4755,0x5245, <- default high scores (0x40db4) - wrong data ?
    0x0000,0x4000,0x0e4b,0x4154,0x5544,0x4f4e,0x0e0e,
    0x0000,0x3000,0x414e,0x4b41,0x4b45,0x5544,0x4f4e,
    0x0000,0x2000,0x0e0e,0x4b49,0x5455,0x4e45,0x0e0e,
    0x0000,0x1000,0x0e4b,0x414b,0x4553,0x4f42,0x410e,

    0x4ef9,0x0000,0x62fa,0x0000,0x4ef9,0x0000,0x805E,0x0000,    <- code (0x40d92)
    0xc800 <- checksum
};
- static const uint16_t mAmatelasProtData[] =
{
    0x0000,0x5000,0x5341,0x4b45,0x5349,0x4755,0x5245, <- default high scores (0x40db4)
    0x0000,0x4000,0x0e4b,0x4154,0x5544,0x4f4e,0x0e0e,
    0x0000,0x3000,0x414e,0x4b41,0x4b45,0x5544,0x4f4e,
    0x0000,0x2000,0x0e0e,0x4b49,0x5455,0x4e45,0x0e0e,
    0x0000,0x1000,0x0e4b,0x414b,0x4553,0x4f42,0x410e,
    0x4ef9,0x0000,0x632e,0x0000,0x4ef9,0x0000,0x80C2,0x0000, <- code (0x40d92)
    0x6100 <- checksum
};
- static const uint16_t mHoreKidProtData[] =
{
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, <- N/A
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x4e75,0x4e75,0x4e75,0x4e75,0x4e75,0x4e75,0x4e75,0x4e75, <- code (0x40dba) It actually never jumps there?
    0x1800 <- checksum
};

- mightguy:
 * sound "protection" uses address/data to ports 2/3 (R/W)
 * Register map:
 * 0x32: always 5, rom read trigger?
 * 0x33: - address 1 (source?)
 * 0x34: /
 * 0x35: - address 2 (adjust value in rom?)
 * 0x36: /
 * 0x37: R reused for ym3526 register set, read protection rom (same as amatelas)
 *
 * 0x40: counter set, always 1?
 * 0x41: R bit 0 pulse timer? W oneshot timer?
 * 0x42: counter set, always 3?
 * 0x43: counter set, always 3?
 *
 * 0x92: data in/out (for dac?)
 * 0x94: test register? (W 0xaa, checks if R 0xaa)

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

#include "emu.h"
#include "machine/nb1412m2.h"



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

// device type definition
DEFINE_DEVICE_TYPE(NB1412M2, nb1412m2_device, "nb1412m2", "NB1412M2 Mahjong Custom")


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

void nb1412m2_device::nb1412m2_map(address_map &map)
{
	// data decrypter
	map(0x32, 0x32).w(FUNC(nb1412m2_device::rom_op_w));
	map(0x33, 0x34).w(FUNC(nb1412m2_device::rom_address_w));
	map(0x35, 0x36).w(FUNC(nb1412m2_device::rom_adjust_w));
	map(0x37, 0x37).r(FUNC(nb1412m2_device::rom_decrypt_r));

	// timer
	map(0x40, 0x40).w(FUNC(nb1412m2_device::timer_w));
	map(0x41, 0x41).rw(FUNC(nb1412m2_device::timer_r), FUNC(nb1412m2_device::timer_ack_w));
	map(0x42, 0x43).nopw(); // always 0x03

	// DAC control
	map(0x11, 0x11).nopw(); // - unknown (volume/channel control?)
	map(0x18, 0x18).w(FUNC(nb1412m2_device::dac_timer_w)); // timer frequency
	map(0x19, 0x19).nopw(); // 2 written at POST
	map(0x51, 0x52).w(FUNC(nb1412m2_device::dac_address_w)); //  start address

	// latches?
	map(0x90, 0x90).rw(FUNC(nb1412m2_device::const90_r),FUNC(nb1412m2_device::const90_w)); //ram();
	map(0x92, 0x92).ram();
	map(0x94, 0x94).ram(); //rw(this,FUNC(nb1412m2_device::xor_r),FUNC(nb1412m2_device::xor_w));

	// 16-bit registers (1=upper address), more latches?
	map(0xa0, 0xa1).ram();
	map(0xa2, 0xa3).ram();
}

//-------------------------------------------------
//  nb1412m2_device - constructor
//-------------------------------------------------

nb1412m2_device::nb1412m2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, NB1412M2, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, m_space_config("regs", ENDIANNESS_LITTLE, 8, 8, 0, address_map_constructor(FUNC(nb1412m2_device::nb1412m2_map), this))
	, m_data(*this, DEVICE_SELF)
	, m_dac_cb(*this)
{
}

device_memory_interface::space_config_vector nb1412m2_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(0, &m_space_config)
	};
}

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

void nb1412m2_device::device_start()
{
	save_item(NAME(m_command));
	save_item(NAME(m_rom_address));
	save_item(NAME(m_dac_start_address));
	save_item(NAME(m_dac_current_address));
	save_item(NAME(m_adj_address));
	save_item(NAME(m_rom_op));
	save_item(NAME(m_timer_reg));
	save_item(NAME(m_dac_playback));
	save_item(NAME(m_dac_frequency));
	save_item(NAME(m_const90));

	m_timer = timer_alloc(TIMER_MAIN);
	m_timer->adjust(attotime::never);
	m_dac_timer = timer_alloc(TIMER_DAC);

	m_dac_cb.resolve_safe();
}


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

void nb1412m2_device::device_reset()
{
	m_command = 0xff;
	m_rom_address = 0;
	m_adj_address = 0;
	m_rom_op = 0;
	m_const90 = 0x18; // fixes coin sample if inserted at first title screen
	m_dac_current_address = m_dac_start_address = 0;
	m_dac_frequency = 4000;
	m_timer_reg = false;
	m_dac_playback = false;
	m_dac_timer->adjust(attotime::never);
}

void nb1412m2_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_MAIN:
			m_timer_reg = true;
			m_timer->adjust(attotime::never);
			break;
		case TIMER_DAC:
			if(m_dac_playback == true)
			{
				uint8_t dac_value;
				dac_value = m_data[m_dac_current_address];
				m_dac_cb(dac_value);
				m_dac_current_address++;
				m_dac_current_address&= m_data.mask();
				if(dac_value == 0x80)
					m_dac_playback = false;
			}

			break;
		default:
			throw emu_fatalerror("Unknown id in nb1412m2_device::device_timer");
	}
}

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

WRITE8_MEMBER( nb1412m2_device::rom_op_w )
{
//  data == 2 starts DAC playback
	if(data == 2)
	{
		m_dac_current_address = m_dac_start_address;
		m_dac_timer->adjust(attotime::never);
		m_dac_timer->adjust(attotime::from_hz(m_dac_frequency), 0, attotime::from_hz(m_dac_frequency));
		m_dac_playback = true;
	}
//  TODO: data == 5 probably loads the result into 0x37
//  if(data != 5)
//      printf("%02x rom_op\n",data);

	m_rom_op = data;
}

WRITE8_MEMBER( nb1412m2_device::rom_address_w )
{
	if(offset == 0) // rom hi address
	{
		m_rom_address &= 0x00ff;
		m_rom_address |= data << 8;
	}
	else // rom lo address
	{
		m_rom_address &= 0xff00;
		m_rom_address |= data;
	}
}

WRITE8_MEMBER( nb1412m2_device::rom_adjust_w )
{
	if(offset == 0) // rom hi address
	{
		m_adj_address &= 0x00ff;
		m_adj_address |= data << 8;
	}
	else // rom lo address
	{
		m_adj_address &= 0xff00;
		m_adj_address |= data;
	}
}

// readback from ROM data
READ8_MEMBER( nb1412m2_device::rom_decrypt_r )
{
	uint8_t prot_adj;

	// all games but Mighty Guy uses this form as protection:
	// 0xff: -> 0x44
	// Mighty Guy variants
	// 0x86: SFXs -> 0xbd
	// 0x94: BGM  -> 0xaf
	// 0x00: DAC  -> 0x43
	// summing adjust entry and actual value needed always make 0x143 therefore:
	prot_adj = (0x43 - m_data[m_adj_address]) & 0xff;

//  printf("%02x %04x %04x %02x\n",m_data[m_adj_address],m_rom_address,m_adj_address,m_rom_op);

	return m_data[m_rom_address & 0x1fff] - prot_adj;
}

// Mighty Guy specifics
READ8_MEMBER( nb1412m2_device::timer_r )
{
	return m_timer_reg == true;
}

WRITE8_MEMBER( nb1412m2_device::timer_w )
{
	if(data != 1)
		logerror("nb1412m2: timer_w with data == %02x\n",data);

	// TODO: timing of this, related to m_const90?
	m_timer->adjust(attotime::from_hz(960));
}

WRITE8_MEMBER( nb1412m2_device::timer_ack_w )
{
	m_timer_reg = false;
	m_timer->adjust(attotime::never);
}

WRITE8_MEMBER( nb1412m2_device::dac_address_w )
{
	if(offset == 0) // dac hi address
	{
		m_dac_start_address &= 0x00ff;
		m_dac_start_address |= data << 8;
	}
	else // dac lo address
	{
		m_dac_start_address &= 0xff00;
		m_dac_start_address |= data;
	}
}

// seems directly correlated to the DAC timer frequency
// 0xd0 - 0xe0 - 0xf0 are the settings used
WRITE8_MEMBER( nb1412m2_device::dac_timer_w )
{
//  popmessage("%02x",data);
	// TODO: unknown algo, 0xe0*18 gives 4032 Hz which seems close enough for sample 36
	m_dac_frequency = data*18;
}

// controls music tempo
READ8_MEMBER( nb1412m2_device::const90_r )
{
	// TODO: likely wrong
	return m_const90;
}

WRITE8_MEMBER( nb1412m2_device::const90_w )
{
	m_const90 = data;
}

// public accessors
READ8_MEMBER( nb1412m2_device::data_r )
{
	return this->space().read_byte(m_command);
}

WRITE8_MEMBER( nb1412m2_device::data_w )
{
	this->space().write_byte(m_command, data);
}

WRITE8_MEMBER( nb1412m2_device::command_w )
{
	m_command = data;
}