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

Nichibutsu 1414M4 device emulation

Written by Angelo Salese, based on researches by Tomasz Slanina with Legion

This is some fancy blitter DMA or MCU or even a discrete structure that copies text strings to a 8-bit text layer in
various Nihon Bussan games;

TODO:
- Identify what exactly this "device" is;
- The overlying text layer should actually be a base device for this (and used where not applicable like galivan,
  armedf and bigfightr);
- Command triggering condition not understood (and diverges between galivan.cpp and armedf.cpp);
- where is the condition that makes "insert coin" text to properly blink?
- first byte meaning is completely unknown;
- Ninja Emaki triggers unknown commands 0x8000 & 0xff20;

Notes:
- Just before any string in the "MCU" rom, there's a control byte, this meaning is as follows:
  0?-- ---- ---- ---- interpret as data?
  10-- ---- ---- ---- single string transfer
  11-- ---- ---- ---- src -> dst copy, if destination != 0 fixed src, otherwise do a src -> dst
  --xx xxxx xxxx xxxx destination offset in the VRAM tilemap

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

#include "emu.h"
#include "machine/nb1414m4.h"
#include "screen.h"

DEFINE_DEVICE_TYPE(NB1414M4, nb1414m4_device, "nb1414m4", "NB1414M4 Mahjong Custom")

nb1414m4_device::nb1414m4_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, NB1414M4, tag, owner, clock)
	, device_video_interface(mconfig, *this)
	, m_data(*this, DEVICE_SELF)
{
}


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

void nb1414m4_device::device_start()
{
	m_previous_0200_command = 0xffff;
	m_previous_0200_command_frame = 0;
	m_in_game = false;
	save_item(NAME(m_previous_0200_command));
	save_item(NAME(m_previous_0200_command_frame));
	save_item(NAME(m_in_game));
}

//-------------------------------------------------
//  device_reset - device-specific startup
//-------------------------------------------------

void nb1414m4_device::device_reset()
{
	m_previous_0200_command = 0xffff;
	m_previous_0200_command_frame = 0;
	m_in_game = false;
}

/*****************************************************************************
    DEVICE HANDLERS
*****************************************************************************/

void nb1414m4_device::dma(uint16_t src, uint16_t dst, uint16_t size, uint8_t condition, uint8_t *vram)
{
	int i;

	for(i=0;i<size;i++)
	{
		if(i+dst+0x000 < 18) //avoid param overwrite
			continue;

		vram[i+dst+0x000] = (condition) ? (m_data[i+(0)+src] & 0xff) : 0x20;

		vram[i+dst+0x400] = (condition) ? (m_data[i+(size)+src] & 0xff) : m_data[0x13];
	}
}

void nb1414m4_device::fill(uint16_t dst, uint8_t tile, uint8_t pal, uint8_t *vram)
{
	int i;

	for(i=0;i<0x400;i++)
	{
		if(i+dst+0x000 < 18) //avoid param overwrite
			continue;

		vram[i+dst+0x000] = tile;
		vram[i+dst+0x400] = pal;
	}
}

void nb1414m4_device::insert_coin_msg(uint8_t *vram)
{
	if (m_in_game)
		return;

	int credit_count = (vram[0xf] & 0xff);
	uint8_t fl_cond = screen().frame_number() & 0x10; /* for insert coin "flickering" */
	uint16_t dst;

	if(credit_count == 0)
	{
		dst = (m_data[0x01]<<8|m_data[0x02]) & 0x3fff;

		dma(0x0003,dst,0x10,fl_cond,vram);
	}
	else
	{
		dst = (m_data[0x49]<<8|m_data[0x4a]) & 0x3fff;

		dma(0x004b,dst,0x18,1,vram);
	}
}

void nb1414m4_device::credit_msg(uint8_t *vram)
{
	int credit_count = (vram[0xf] & 0xff);
	uint8_t fl_cond = screen().frame_number() & 0x10; /* for insert coin "flickering" */
	uint16_t dst;

	dst = ((m_data[0x023]<<8)|(m_data[0x024]&0xff)) & 0x3fff;
	dma(0x0025,dst,0x10,1,vram); /* credit */

	/* credit num */
	dst = ((m_data[0x045]<<8)|(m_data[0x046]&0xff)) & 0x3fff;
	vram[dst+0x000] = (credit_count & 0xf0) ? (((credit_count & 0xf0)>>4) + 0x30) : 0x20;
	vram[dst+0x400] = (m_data[0x47]);
	vram[dst+0x001] = ((credit_count & 0x0f) + 0x30);
	vram[dst+0x401] = (m_data[0x48]);

	if (m_in_game)
		return;

	if(credit_count == 1) /* ONE PLAYER ONLY */
	{
		dst = ((m_data[0x07b]<<8)|(m_data[0x07c]&0xff)) & 0x3fff;
		dma(0x007d,dst,0x18,fl_cond,vram);
	}
	else if(credit_count > 1) /* ONE OR TWO PLAYERS */
	{
		dst = ((m_data[0x0ad]<<8)|(m_data[0x0ae]&0xff)) & 0x3fff;
		dma(0x00af,dst,0x18,fl_cond,vram);
	}
}

void nb1414m4_device::kozure_score_msg(uint16_t dst, uint8_t src_base, uint8_t *vram)
{
	int i;
	uint8_t first_digit;
	uint8_t res;

	first_digit = 0;

	for(i=0;i<6;i++)
	{
		res = ((vram[(i/2)+5+src_base*3] >> (!(i & 1) * 4)) & 0xf);

		if(first_digit || res)
		{
			vram[i+dst+0x0000] = res + 0x30;
			first_digit = 1;
		}
		else
			vram[i+dst+0x0000] = 0x20;

		vram[i+dst+0x0400] = m_data[0x10f+(src_base*0x1c)+i];
	}

	vram[6+dst+0x0000] = vram[5+dst+0x0000] == 0x20 ? 0x20 : 0x30;
	vram[6+dst+0x0400] = m_data[0x10f+(src_base*0x1c)+6];
	vram[7+dst+0x0000] = 0x30;
	vram[7+dst+0x0400] = m_data[0x10f+(src_base*0x1c)+7];

}

void nb1414m4_device::_0200(uint16_t mcu_cmd, uint8_t *vram)
{
	uint16_t dst;

	// In any game, this bit is set when now playing.
	// If it is set, "INSERT COIN" etc. are not displayed.
	m_in_game = (mcu_cmd & 0x80) != 0;

	// If the same command in an extremely short cycle (1 frame or less), ignored it.
	// This is required to displaying continue screen of ninjaemak.
	// ninjaemak calls this command to clear the screen, draw the continuation screen, and then immediately call the same command.
	// This second command must be ignored in order not to corrupt the continue screen.
	if (m_previous_0200_command == mcu_cmd && (screen().frame_number() - m_previous_0200_command_frame) <= 1)
		return;

	dst = (m_data[0x330+((mcu_cmd & 0xf)*2)]<<8)|(m_data[0x331+((mcu_cmd & 0xf)*2)]&0xff);

	dst &= 0x3fff;

	if(dst & 0x7ff) // fill
		fill(0x0000,m_data[dst],m_data[dst+1],vram);
	else // src -> dst
		dma(dst,0x0000,0x400,1,vram);

	m_previous_0200_command = mcu_cmd;
	m_previous_0200_command_frame = screen().frame_number();
}

/*
[0x02] & 0x01 p1 up
[0x02] & 0x02 p1 down
[0x02] & 0x04 p1 left
[0x02] & 0x08 p1 right
[0x02] & 0x10 p1 button 1
[0x02] & 0x20 p1 button 2
[0x02] & 0x40 p1 button 3
[0x03] & 0x01 p2 up
[0x03] & 0x02 p2 down
[0x03] & 0x04 p2 left
[0x03] & 0x08 p2 right
[0x03] & 0x10 p2 button 1
[0x03] & 0x20 p2 button 2
[0x03] & 0x40 p2 button 3
[0x04] & 0x10 service
[0x04] & 0x04 coin A
[0x04] & 0x08 coin B
[0x04] & 0x01 start 1
[0x04] & 0x02 start 2
[0x05] DSW1
[0x06] DSW2
[0x07] & 0x40 demo sounds ON / OFF
[0x07] & 0x7 lives setting
[0x07] & 0x80 cabinet (upright / table)
[0x07] & 0x30 difficulty (easy / normal / hard / hardest)
[0x0f] coinage A
[0x10] coinage B
[0x11] sound test num
*/
void nb1414m4_device::_0600(uint8_t is2p, uint8_t *vram)
{
	uint16_t dst;
	int i;

	dst = ((m_data[0x1f5]<<8)|(m_data[0x1f6]&0xff)) & 0x3fff;
	vram[dst] = (vram[7] & 0x7) + 0x30;//m_data[0x1f7];

	dst = ((m_data[0x1f8]<<8)|(m_data[0x1f9]&0xff)) & 0x3fff;
	dma(0x1fa + (((vram[7] & 0x30) >> 4) * 0x18),dst,12,1,vram);

	// 0x25a - 0x261 unknown meaning

	dst = ((m_data[0x262]<<8)|(m_data[0x263]&0xff)) & 0x3fff;
	dma(0x264 + (((vram[7] & 0x80) >> 7) * 0x18),dst,12,1,vram);

	dst = ((m_data[0x294]<<8)|(m_data[0x295]&0xff)) & 0x3fff;
	dma(0x296 + (((vram[7] & 0x40) >> 6) * 0x18),dst,12,1,vram);

	dst = ((m_data[0x2c6]<<8)|(m_data[0x2c7]&0xff)) & 0x3fff;
	vram[dst] = ((vram[0xf] & 0xf0) >> 4) + 0x30;//m_data[0x2c8];

	dst = ((m_data[0x2c9]<<8)|(m_data[0x2ca]&0xff)) & 0x3fff;
	vram[dst] = ((vram[0xf] & 0x0f) >> 0) + 0x30;//m_data[0x2cb];

	dst = ((m_data[0x2cc]<<8)|(m_data[0x2cd]&0xff)) & 0x3fff;
	vram[dst] = ((vram[0x10] & 0xf0) >> 4) + 0x30;//m_data[0x2ce];

	dst = ((m_data[0x2cf]<<8)|(m_data[0x2d0]&0xff)) & 0x3fff;
	vram[dst] = ((vram[0x10] & 0x0f) >> 0) + 0x30;//m_data[0x2d1];

	dst = ((m_data[0x2d2]<<8)|(m_data[0x2d3]&0xff)) & 0x3fff;
	vram[dst+0] = ((vram[0x11] & 0xf0) >> 4) + 0x30;//m_data[0x2d4];
	vram[dst+1] = (vram[0x11] & 0x0f) + 0x30;//m_data[0x2d5];

	dst = ((m_data[0x2d6]<<8)|(m_data[0x2d7]&0xff)) & 0x3fff;
	dma(0x2d8 + (is2p * 0x18),dst,12,1,vram); // 1p / 2p string

	dst = ((m_data[0x308]<<8)|(m_data[0x309]&0xff)) & 0x3fff;
	for(i=0;i<5;i++) /* system inputs */
		dma(0x310 + (((vram[0x04] >> (4-i)) & 1) * 6),dst + (i * 0x20),0x3,1,vram);

	dst = ((m_data[0x30a]<<8)|(m_data[0x30b]&0xff)) & 0x3fff;
	for(i=0;i<7;i++) /* 1p / 2p inputs */
		dma(0x310 + (((vram[0x02 + is2p] >> (6-i)) & 1) * 6),dst + (i * 0x20),0x3,1,vram);

	dst = ((m_data[0x30c]<<8)|(m_data[0x30d]&0xff)) & 0x3fff;
	for(i=0;i<8;i++) /* dips */
		dma(0x310 + (((vram[0x05] >> (7-i)) & 1) * 6),dst + (i * 0x20),0x3,1,vram);

	dst = ((m_data[0x30e]<<8)|(m_data[0x30f]&0xff)) & 0x3fff;
	for(i=0;i<8;i++) /* dips */
		dma(0x310 + (((vram[0x06] >> (7-i)) & 1) * 6),dst + (i * 0x20),0x3,1,vram);
}

void nb1414m4_device::_0e00(uint16_t mcu_cmd, uint8_t *vram)
{
	uint16_t dst;

	dst = ((m_data[0xdf]<<8)|(m_data[0xe0]&0xff)) & 0x3fff;
	dma(0x00e1,dst,8,1,vram); /* hi-score */

	dst = ((m_data[0xfb]<<8)|(m_data[0xfc]&0xff)) & 0x3fff;
	dma(0x00fd,dst,8,!(mcu_cmd & 1),vram); /* 1p-msg */
	dst = ((m_data[0x10d]<<8)|(m_data[0x10e]&0xff)) & 0x3fff;
	kozure_score_msg(dst,0,vram); /* 1p score */

	if(mcu_cmd & 0x80)
	{
		dst = ((m_data[0x117]<<8)|(m_data[0x118]&0xff)) & 0x3fff;
		dma(0x0119,dst,8,!(mcu_cmd & 2),vram); /* 2p-msg */
		dst = ((m_data[0x129]<<8)|(m_data[0x12a]&0xff)) & 0x3fff;
		kozure_score_msg(dst,1,vram); /* 2p score */
	}

	if((mcu_cmd & 0x04) == 0)
	{
		dst = ((m_data[0x133]<<8)|(m_data[0x134]&0xff)) & 0x3fff;
		dma(0x0135,dst,0x10,1,vram); /* game over */
		insert_coin_msg(vram);
		if((mcu_cmd & 0x18) == 0) // TODO: either one of these two disables credit display
			credit_msg(vram);
	}
}

/*****************************************************************************
    DEVICE SETTERS
*****************************************************************************/

void nb1414m4_device::vblank_trigger()
{
	// TODO: use this for frame number synchronization instead of screen().frame_number()
	//  real HW references definitely syncs insert coin blinking after POST so whatever is the host actually
	//  have an interest over vblank signal.
}

void nb1414m4_device::exec(uint16_t mcu_cmd, uint8_t *vram, uint16_t &scrollx, uint16_t &scrolly, tilemap_t *tilemap)
{
	/* latch fg scroll values */
	scrollx = (vram[0x0d] & 0xff) | ((vram[0x0e] & 0xff) << 8);
	scrolly = (vram[0x0b] & 0xff) | ((vram[0x0c] & 0xff) << 8);

	/* process the command */
	switch(mcu_cmd & 0xff00)
	{
		/* title screen / continue screens */
		case 0x0000: insert_coin_msg(vram); credit_msg(vram); break;

		/* direct DMA'ing / fill */
		case 0x0200: _0200(mcu_cmd & 0x87,vram); break;

		/* service mode */
		case 0x0600: _0600(mcu_cmd & 1,vram); break;

		/* gameplay */
		case 0x0e00: _0e00(mcu_cmd & 0xff,vram); break;

		case 0x8000: break; //Ninja Emaki, attract mode
		case 0xff00: break; //Ninja Emaki POST, presumably invalid
		default:
			popmessage("NB 1414M4 executes %04x command, contact MAMEdev",mcu_cmd);
			break;
	}

	/* mark tiles dirty */
	tilemap->mark_all_dirty();
}