summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/spg2xx_zone.cpp
blob: ca003a00c30a2f970acb87c362601538a050f104 (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
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

/* 'Zone' systems */

#include "emu.h"
#include "includes/spg2xx.h"


class wireless60_state : public spg2xx_game_state
{
public:
	wireless60_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_bankmask(0x7)
	{ }

	void wireless60(machine_config& config);

	void init_lx_jg7415();
	void init_zone100();

protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;

	uint8_t m_w60_controller_input;
	uint16_t m_w60_porta_data;
	uint16_t m_w60_p1_ctrl_mask;
	uint16_t m_w60_p2_ctrl_mask;
	uint8_t m_bankmask;

	void wireless60_porta_w(uint16_t data);
	void wireless60_portb_w(uint16_t data);
	uint16_t wireless60_porta_r();

private:
};

class zone40_state : public wireless60_state
{
public:
	zone40_state(const machine_config& mconfig, device_type type, const char* tag) :
		wireless60_state(mconfig, type, tag),
		m_romregion(*this, "maincpu")
	{ }

	void zone40(machine_config &config);
	void zone40p(machine_config &config);

	void init_zone40();
	void init_reactmd();


protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;

private:
	virtual void mem_map_z40(address_map &map);
	uint16_t z40_rom_r(offs_t offset);
	uint16_t zone40_porta_r();
	void zone40_porta_w(uint16_t data);
	required_region_ptr<uint16_t> m_romregion;
	uint16_t m_z40_rombase;
	uint16_t m_porta_dat;
	int m_romsize;
};


void wireless60_state::wireless60_porta_w(uint16_t data)
{
	//logerror("%s: wireless60_porta_w %04x\n", machine().describe_context(), data);

	m_w60_porta_data = (data & 0x300) | m_w60_p1_ctrl_mask | m_w60_p2_ctrl_mask;
	switch (m_w60_porta_data & 0x300)
	{
	case 0x300:
		m_w60_controller_input = -1;
		break;

	case 0x200:
		m_w60_controller_input++;
		break;

	default:
		uint16_t temp1 = m_io_p1->read();
		uint16_t temp2 = m_io_p2->read();
		uint16_t temp3 = 1 << m_w60_controller_input;
		if (temp1 & temp3) m_w60_porta_data ^= m_w60_p1_ctrl_mask;
		if (temp2 & temp3) m_w60_porta_data ^= m_w60_p2_ctrl_mask;
		break;
	}
}

uint16_t wireless60_state::wireless60_porta_r()
{
	//logerror("%s: wireless60_porta_r\n", machine().describe_context());
	return m_w60_porta_data;
}

void wireless60_state::wireless60_portb_w(uint16_t data)
{
	logerror("%s: wireless60_portb_w (bankswitch) %04x\n", machine().describe_context(), data);
	switch_bank(data & m_bankmask);
}

void zone40_state::zone40_porta_w(uint16_t data)
{
	wireless60_porta_w(data);

	m_z40_rombase = (m_z40_rombase & 0xff00) | (data & 0x0ff);

	// toggled twice before games in the same 64MB bank, toggled once before games in a different 64MB bank
	if ((data & 0x2000) && (!(m_porta_dat & 0x2000)))
		m_z40_rombase ^= 0x0100;

	m_porta_dat = data;

	//logerror("%s: zone40_porta_w %04x z80 bank is now %04x \n", machine().describe_context(), data, m_z40_rombase);

	m_maincpu->invalidate_cache();

}

uint16_t zone40_state::zone40_porta_r()
{
	uint16_t ret = wireless60_porta_r() & (0x0300 | m_w60_p1_ctrl_mask | m_w60_p2_ctrl_mask);
	ret = (ret & 0xdf00) | (m_porta_dat & 0x20ff);
	return ret;
}


uint16_t zone40_state::z40_rom_r(offs_t offset)
{
	// due to granularity of rom bank this manual method is safer
	return m_romregion[(offset + (m_z40_rombase * 0x20000)) & (m_romsize-1)];
}

void zone40_state::mem_map_z40(address_map &map)
{
	map(0x000000, 0x3fffff).r(FUNC(zone40_state::z40_rom_r));
}

void wireless60_state::machine_start()
{
	spg2xx_game_state::machine_start();

	save_item(NAME(m_w60_controller_input));
	save_item(NAME(m_w60_porta_data));

	m_w60_p1_ctrl_mask = 0x0400;
	m_w60_p2_ctrl_mask = 0x0800;
}

void zone40_state::machine_start()
{
	wireless60_state::machine_start();

	save_item(NAME(m_z40_rombase));
	save_item(NAME(m_porta_dat));

	m_z40_rombase = 0xe0;
	m_porta_dat = 0x20e0;
	m_w60_p1_ctrl_mask = 0x0400;
	m_w60_p2_ctrl_mask = 0x1000;

	m_romsize = (memregion("maincpu")->bytes()/2);
}

void wireless60_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
	m_w60_controller_input = -1;
	m_w60_porta_data = 0;
}

void zone40_state::machine_reset()
{
	wireless60_state::machine_reset();
	m_z40_rombase = 0x1e0;
	m_maincpu->invalidate_cache();
	m_maincpu->reset();
}

static INPUT_PORTS_START( wirels60 )
	PORT_START("P1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("Menu")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("Start")

	PORT_START("P2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(2) PORT_NAME("Joypad Up")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(2) PORT_NAME("Joypad Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(2) PORT_NAME("Joypad Left")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("Joypad Right")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(2) PORT_NAME("A Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(2) PORT_NAME("B Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(2) PORT_NAME("Menu")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(2) PORT_NAME("Start")
INPUT_PORTS_END


void wireless60_state::wireless60(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &wireless60_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_out().set(FUNC(wireless60_state::wireless60_porta_w));
	m_maincpu->portb_out().set(FUNC(wireless60_state::wireless60_portb_w));
	m_maincpu->porta_in().set(FUNC(wireless60_state::wireless60_porta_r));
}

void zone40_state::zone40(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &zone40_state::mem_map_z40);

	spg2xx_base(config);

	m_maincpu->porta_out().set(FUNC(zone40_state::zone40_porta_w));
	m_maincpu->porta_in().set(FUNC(zone40_state::zone40_porta_r));
}

void zone40_state::zone40p(machine_config &config)
{
	zone40(config);
	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}


void wireless60_state::init_lx_jg7415()
{
	uint8_t blocks[32] = {
		// these parts of the ROM contain the code that gets selected
		0x00, 0x01, 0x06, 0x07, 0x08, 0x09, 0x0e, 0x0f,   0x10, 0x11, 0x16, 0x17, 0x18, 0x19, 0x1e, 0x1f,
		// these parts of the ROM contain code / data but go unused, this seems intentional, some of these areas don't read consistently so likely this double size ROM was used knowing that some areas were bad and could be avoided
		0x02, 0x03, 0x04, 0x05, 0x0a, 0x0b, 0x0c, 0x0d,   0x12, 0x13, 0x14, 0x15, 0x1a, 0x1b, 0x1c, 0x1d
	};

	uint8_t *src = memregion("maincpu")->base();
	std::vector<u8> buffer(0x10000000);

	for (int addr = 0; addr < 0x10000000; addr++)
	{
		int bank = (addr & 0xf800000) >> 23;
		int newbank = blocks[bank];
		int newaddr = (addr & 0x07fffff) | (newbank << 23);
		buffer[addr] = src[newaddr];
	}

	std::copy(buffer.begin(), buffer.end(), &src[0]);

	m_bankmask = 0xf;
}

void wireless60_state::init_zone100()
{
	m_bankmask = 0xf;
}

void zone40_state::init_zone40()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 11, 10, 3,  2,  4,  12, 5,  13,
									 9,  1,  8,  0,  6,  7,  14, 15);

		ROM[i] = ROM[i] ^ 0xa5a5;
	}
}


void zone40_state::init_reactmd()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 15, 13, 14, 12,  7,  6,  5,  4,
									 11, 10, 9,  8,   3,  1,  2,  0);

		ROM[i] = ROM[i] ^ 0xa5a5;
	}
}



ROM_START( zone40 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zone40.bin", 0x0000, 0x4000000, CRC(4ba1444f) SHA1(de83046ab93421486668a247972ad6d3cda19440) )
ROM_END

ROM_START( reactmd )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 ) // this contains the SunPlus games
	ROM_LOAD16_WORD_SWAP( "reactor_md_sunplus-full.bin", 0x0000, 0x4000000, CRC(843aa58c) SHA1(07cdc6d4aa0057939c145ece01a9aca73c7f1f2b) )
	ROM_IGNORE(0x4000000) // the 2nd half of the ROM can't be accessed by the PCB (address line tied low) (contains garbage? data)

	ROM_REGION( 0x2000000, "mdrom", ROMREGION_ERASE00 ) // this contains the MD games and main boot menu
	ROM_LOAD16_WORD_SWAP( "reactormd.bin", 0x0000, 0x2000000, CRC(fe9664a4) SHA1(d475b524f576c9d1d90aed20c7467cc652396baf) )
ROM_END

ROM_START( itvg49 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "49in1sports.bin", 0x0000, 0x8000000, CRC(bb8a1c4e) SHA1(a493177de7365037b67ead0155a902313722a61c) )
ROM_END


ROM_START( zone60 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zone60.bin", 0x0000, 0x4000000, CRC(4cb637d1) SHA1(1f97cbdb4299ac0fbafc2a3aa592066cb0727066))
ROM_END

ROM_START( wirels60 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "wirels60.bin", 0x0000, 0x4000000, CRC(b4df8b28) SHA1(00e3da542e4bc14baf4724ad436f66d4c0f65c84))
ROM_END

ROM_START( zone100 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zone100.bin", 0x0000, 0x8000000, CRC(b966a54e) SHA1(e38156ebc4e2f2935b1acbeca33d1866d45c4f65) )
ROM_END

// PCB marked 'Zone 100 110728 V2.1'
ROM_START( lx_jg7415 )
	ROM_REGION( 0x10000000, "maincpu", ROMREGION_ERASE00 )
	// reads of some unused areas were not 100% consistent, but this seems intentional, the game has a ROM twice the size it needs and is wired up in such a way that those bad areas are unused by the game
	// if adding a clone make sure to check if there are actual differences in the used areas
	ROM_LOAD16_WORD_SWAP( "rom.bin", 0x0000, 0x10000000, CRC(59442e00) SHA1(7e91cf6b19c37f9b4fa4dc21e241c6634d6a6f95) )
ROM_END

ROM_START( zonemini )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zonemini.bin", 0x0000, 0x4000000, CRC(ba8c367c) SHA1(92ce2e895145ad76ea68ab7575d2c52aa0c0c5a9) )
ROM_END

ROM_START( react )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "reactor.bin", 0x0000, 0x0800000, CRC(0378c594) SHA1(b2214e3e235f26fb501df6c66a9b2c0da87b1c73))
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)
ROM_END

// These have games from Waixing and were likely manufactured  by Subor and sold by Ultimate Products Ltd.
// Many of these games are rewrites of VT1682 based titles, which in turn were based on older NES/VT ones
// Badminton hangs in units where it is present (cause not yet investigated), otherwise everything runs

// Waixing = "Fuzhou Waixing Computer Science & Technology Co.,LTD"

CONS( 2009, zone40,   0, 0, zone40,     wirels60, zone40_state,      init_zone40,     "Ultimate Products Ltd. / Waixing",                      "Zone 40",                             MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2009, itvg49,   0, 0, zone40p,    wirels60, zone40_state,      init_reactmd,    "TaiKee / Waixing",                                      "Interactive TV Games 49-in-1 (PAL)",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, zonemini, 0, 0, zone40,     wirels60, zone40_state,      init_reactmd,    "Ultimate Products Ltd. / Waixing",                      "Zone Mini",                           MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2009, react,    0, 0, zone40,     wirels60, zone40_state,      init_reactmd,    "Ultimate Products Ltd. / Waixing",                      "Reactor 32-in-1 (NTSC)",              MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// Two systems in one unit - Genesis on a Chip and SunPlus, only the SunPlus part is currently emulated.  Genesis on a chip is a very poor implementation with many issues on real hardware.
// This should actually boot to a menu on the MD size, with the SunPlus only being enabled if selected from that menu.  MD side menu runs in some enhanced / custom MD mode tho.
// Badminton hangs, as it does in the 49-in-1 above
CONS( 2009, reactmd,  0, 0, zone40p,    wirels60, zone40_state,      init_reactmd,    "AtGames / Sega / Waixing",                              "Reactor MD (PAL)",                    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// These have a newer selection of games by JungleTac instead of the Waixing ones

CONS( 2010, zone60,   0, 0, wireless60, wirels60, wireless60_state,  empty_init,      "Ultimate Products (HK) Ltd / Jungle's Soft",  "Zone 60",                             MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, zone100,  0, 0, wireless60, wirels60, wireless60_state,  init_zone100,    "Ultimate Products (HK) Ltd / Jungle's Soft",  "Zone 100",                            MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // unit was black, menus still show white controllers, unlike wireless 60
CONS( 2010, wirels60, 0, 0, wireless60, wirels60, wireless60_state,  empty_init,      "Kids Station Toys Inc / Jungle Soft",         "Wireless 60",                         MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2011, lx_jg7415,0, 0, wireless60, wirels60, wireless60_state,  init_lx_jg7415,  "Lexibook / JungleTac",                        "Lexibook JG7415 120-in-1",            MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )