1 // license:BSD-3-Clause
2 // copyright-holders:David Haywood
3 /* Xyonix *********************************************************************
4 
5 driver by David Haywood and Stephh
6 
7 Notes about the board:
8 
9 Ram is 2x 6264 (near Z80) and 1x 6264 near UM6845. Xtal is verified 16.000MHz,
10 I can also see another special chip . PHILKO PK8801. chip looks about the same as a
11 TMS3615 (though i have no idea what the chip actually is). its located next to the
12 prom, the 2x 256k roms, and the 1x 6264 ram.
13 Dip SW is 1 x 8-position
14 
15 on the PCB is an empty socket. written next to the socket is 68705P3. "oh no" you
16 say..... well, its unpopulated, so maybe it was never used? (another PCB was
17 found with the 68705 populated)
18 
19 
20 TODO:
21 - there are some more unknown commands for the I/O chip
22 
23 ******************************************************************************/
24 
25 #include "emu.h"
26 
27 #include "cpu/z80/z80.h"
28 #include "video/mc6845.h"
29 #include "sound/sn76496.h"
30 #include "screen.h"
31 #include "speaker.h"
32 #include "emupal.h"
33 #include "tilemap.h"
34 
35 
36 class xyonix_state : public driver_device
37 {
38 public:
xyonix_state(const machine_config & mconfig,device_type type,const char * tag)39 	xyonix_state(const machine_config &mconfig, device_type type, const char *tag) :
40 		driver_device(mconfig, type, tag),
41 		m_maincpu(*this, "maincpu"),
42 		m_crtc(*this, "crtc"),
43 		m_palette(*this, "palette"),
44 		m_gfxdecode(*this, "gfxdecode"),
45 		m_gfx(*this, "gfx1"),
46 		m_vidram(*this, "vidram")
47 	{ }
48 
49 	void xyonix(machine_config &config);
50 
51 protected:
52 	virtual void machine_start() override;
53 	virtual void machine_reset() override;
54 	virtual void video_start() override;
55 
56 private:
57 	required_device<cpu_device> m_maincpu;
58 	required_device<mc6845_device> m_crtc;
59 	required_device<palette_device> m_palette;
60 	required_device<gfxdecode_device> m_gfxdecode;
61 	required_memory_region m_gfx;
62 
63 	required_shared_ptr<uint8_t> m_vidram;
64 
65 	tilemap_t *m_tilemap;
66 
67 	int m_e0_data;
68 	int m_credits;
69 	int m_coins;
70 	int m_prev_coin;
71 	bool m_nmi_mask;
72 
73 	DECLARE_WRITE_LINE_MEMBER(nmiclk_w);
74 	void irqack_w(uint8_t data);
75 	void nmiack_w(uint8_t data);
76 	uint8_t io_r();
77 	void io_w(uint8_t data);
78 	void vidram_w(offs_t offset, uint8_t data);
79 
80 	TILE_GET_INFO_MEMBER(get_tile_info);
81 	void xyonix_palette(palette_device &palette) const;
82 	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
83 
84 	void handle_coins(int coin);
85 	void main_map(address_map &map);
86 	void port_map(address_map &map);
87 
88 //  MC6845_UPDATE_ROW(crtc_update_row);
89 };
90 
machine_start()91 void xyonix_state::machine_start()
92 {
93 	save_item(NAME(m_e0_data));
94 	save_item(NAME(m_credits));
95 	save_item(NAME(m_coins));
96 	save_item(NAME(m_prev_coin));
97 	save_item(NAME(m_nmi_mask));
98 }
99 
machine_reset()100 void xyonix_state::machine_reset()
101 {
102 	m_nmi_mask = false;
103 }
104 
irqack_w(uint8_t data)105 void xyonix_state::irqack_w(uint8_t data)
106 {
107 	m_maincpu->set_input_line(0, CLEAR_LINE);
108 }
109 
WRITE_LINE_MEMBER(xyonix_state::nmiclk_w)110 WRITE_LINE_MEMBER(xyonix_state::nmiclk_w)
111 {
112 	if (state && m_nmi_mask)
113 		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
114 }
115 
nmiack_w(uint8_t data)116 void xyonix_state::nmiack_w(uint8_t data)
117 {
118 	m_nmi_mask = BIT(data, 0);
119 	if (!m_nmi_mask)
120 		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
121 }
122 
xyonix_palette(palette_device & palette) const123 void xyonix_state::xyonix_palette(palette_device &palette) const
124 {
125 	const uint8_t *color_prom = memregion("proms")->base();
126 
127 	for (int i = 0; i < palette.entries(); i++)
128 	{
129 		int bit0, bit1, bit2;
130 
131 		// red component
132 		bit0 = BIT(color_prom[i], 0);
133 		bit1 = BIT(color_prom[i], 1);
134 		bit2 = BIT(color_prom[i], 2);
135 		int const r = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
136 
137 		// green component
138 		bit0 = BIT(color_prom[i], 5);
139 		bit1 = BIT(color_prom[i], 6);
140 		bit2 = BIT(color_prom[i], 7);
141 		int const g = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
142 
143 		// blue component
144 		bit0 = BIT(color_prom[i], 3);
145 		bit1 = BIT(color_prom[i], 4);
146 		int const b = 0x4f * bit0 + 0xa8 * bit1;
147 
148 		palette.set_pen_color(i,rgb_t(r,g,b));
149 	}
150 }
151 
152 
TILE_GET_INFO_MEMBER(xyonix_state::get_tile_info)153 TILE_GET_INFO_MEMBER(xyonix_state::get_tile_info)
154 {
155 	int tileno;
156 	int attr = m_vidram[tile_index+0x1000+1];
157 
158 	tileno = (m_vidram[tile_index+1] << 0) | ((attr & 0x0f) << 8);
159 
160 	tileinfo.set(0,tileno,attr >> 4,0);
161 }
162 
vidram_w(offs_t offset,uint8_t data)163 void xyonix_state::vidram_w(offs_t offset, uint8_t data)
164 {
165 	m_vidram[offset] = data;
166 	m_tilemap->mark_tile_dirty((offset-1)&0x0fff);
167 }
168 
video_start()169 void xyonix_state::video_start()
170 {
171 	m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(xyonix_state::get_tile_info)), TILEMAP_SCAN_ROWS, 4, 8, 80, 32);
172 }
173 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)174 uint32_t xyonix_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
175 {
176 	m_tilemap->draw(screen, bitmap, cliprect, 0, 0);
177 	return 0;
178 }
179 
180 #if 0
181 
182 // commented out because the tilemap renderer is much simpler
183 
184 MC6845_UPDATE_ROW( xyonix_state::crtc_update_row )
185 {
186 	const pen_t *pen = m_palette->pens();
187 
188 	for (int i = 0; i < x_count; i++)
189 	{
190 		uint8_t code = m_vidram[(0x0000 | (ma + i)) + 1];
191 		uint8_t attr = m_vidram[(0x1000 | (ma + i)) + 1];
192 
193 		// tile offset into the gfx rom
194 		uint16_t tile = ((((attr & 0x0f) << 8) | code) << 3) | ra;
195 
196 		// tile data (4 pixels with 4 bit color code)
197 		uint16_t data = m_gfx->base()[0x8000 | tile] << 8 | m_gfx->base()[tile];
198 
199 		// draw 4 pixels
200 		bitmap.pix(y, i * 4 + 0) = pen[(attr & 0xf0) | bitswap<4>(data, 4, 0, 12, 8)];
201 		bitmap.pix(y, i * 4 + 1) = pen[(attr & 0xf0) | bitswap<4>(data, 5, 1, 13, 9)];
202 		bitmap.pix(y, i * 4 + 2) = pen[(attr & 0xf0) | bitswap<4>(data, 6, 2, 14, 10)];
203 		bitmap.pix(y, i * 4 + 3) = pen[(attr & 0xf0) | bitswap<4>(data, 7, 3, 15, 11)];
204 	}
205 }
206 #endif
207 
208 
209 /* Inputs ********************************************************************/
210 
handle_coins(int coin)211 void xyonix_state::handle_coins(int coin)
212 {
213 	static const int coinage_table[4][2] = {{2,3},{2,1},{1,2},{1,1}};
214 	int tmp = 0;
215 
216 	//popmessage("Coin %d", m_coin);
217 
218 	if (coin & 1)   // Coin 2 !
219 	{
220 		tmp = (ioport("DSW")->read() & 0xc0) >> 6;
221 		m_coins++;
222 		if (m_coins >= coinage_table[tmp][0])
223 		{
224 			m_credits += coinage_table[tmp][1];
225 			m_coins -= coinage_table[tmp][0];
226 		}
227 		machine().bookkeeping().coin_lockout_global_w(0); /* Unlock all coin slots */
228 		machine().bookkeeping().coin_counter_w(1,1); machine().bookkeeping().coin_counter_w(1,0); /* Count slot B */
229 	}
230 
231 	if (coin & 2)   // Coin 1 !
232 	{
233 		tmp = (ioport("DSW")->read() & 0x30) >> 4;
234 		m_coins++;
235 		if (m_coins >= coinage_table[tmp][0])
236 		{
237 			m_credits += coinage_table[tmp][1];
238 			m_coins -= coinage_table[tmp][0];
239 		}
240 		machine().bookkeeping().coin_lockout_global_w(0); /* Unlock all coin slots */
241 		machine().bookkeeping().coin_counter_w(0,1); machine().bookkeeping().coin_counter_w(0,0); /* Count slot A */
242 	}
243 
244 	if (m_credits >= 9)
245 		m_credits = 9;
246 }
247 
248 
io_r()249 uint8_t xyonix_state::io_r()
250 {
251 	int regPC = m_maincpu->pc();
252 
253 	if (regPC == 0x27ba)
254 		return 0x88;
255 
256 	if (regPC == 0x27c2)
257 		return m_e0_data;
258 
259 	if (regPC == 0x27c7)
260 	{
261 		int coin;
262 
263 		switch (m_e0_data)
264 		{
265 			case 0x81 :
266 				return ioport("P1")->read() & 0x7f;
267 			case 0x82 :
268 				return ioport("P2")->read() & 0x7f;
269 			case 0x91:
270 				/* check coin inputs */
271 				coin = ((ioport("P1")->read() & 0x80) >> 7) | ((ioport("P2")->read() & 0x80) >> 6);
272 				if (coin ^ m_prev_coin && coin != 3)
273 				{
274 					if (m_credits < 9) handle_coins(coin);
275 				}
276 				m_prev_coin = coin;
277 				return m_credits;
278 			case 0x92:
279 				return ((ioport("P1")->read() & 0x80) >> 7) | ((ioport("P2")->read() & 0x80) >> 6);
280 			case 0xe0:  /* reset? */
281 				m_coins = 0;
282 				m_credits = 0;
283 				return 0xff;
284 			case 0xe1:
285 				m_credits--;
286 				return 0xff;
287 			case 0xfe:  /* Dip Switches 1 to 4 */
288 				return ioport("DSW")->read() & 0x0f;
289 			case 0xff:  /* Dip Switches 5 to 8 */
290 				return ioport("DSW")->read() >> 4;
291 		}
292 	}
293 
294 	//logerror ("xyonix_port_e0_r - PC = %04x - port = %02x\n", regPC, m_e0_data);
295 	//popmessage("%02x",m_e0_data);
296 
297 	return 0xff;
298 }
299 
io_w(uint8_t data)300 void xyonix_state::io_w(uint8_t data)
301 {
302 	//logerror ("xyonix_port_e0_w %02x - PC = %04x\n", data, m_maincpu->pc());
303 	m_e0_data = data;
304 }
305 
306 /* Mem / Port Maps ***********************************************************/
307 
main_map(address_map & map)308 void xyonix_state::main_map(address_map &map)
309 {
310 	map(0x0000, 0xbfff).rom();
311 	map(0xc000, 0xdfff).ram();
312 	map(0xe000, 0xffff).ram().w(FUNC(xyonix_state::vidram_w)).share("vidram");
313 }
314 
port_map(address_map & map)315 void xyonix_state::port_map(address_map &map)
316 {
317 	map.global_mask(0xff);
318 	map(0x20, 0x20).nopr().w("sn1", FUNC(sn76496_device::write));   /* SN76496 ready signal */
319 	map(0x21, 0x21).nopr().w("sn2", FUNC(sn76496_device::write));
320 	map(0x40, 0x40).w(FUNC(xyonix_state::nmiack_w));
321 	map(0x50, 0x50).w(FUNC(xyonix_state::irqack_w));
322 	map(0x60, 0x60).w(m_crtc, FUNC(mc6845_device::address_w));
323 	map(0x61, 0x61).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
324 	map(0xe0, 0xe0).rw(FUNC(xyonix_state::io_r), FUNC(xyonix_state::io_w));
325 }
326 
327 /* Inputs Ports **************************************************************/
328 
329 static INPUT_PORTS_START( xyonix )
330 	PORT_START("P1")
331 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
332 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
333 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
334 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
335 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
336 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
337 	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START1 )
338 	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN1 )      /* handled by xyonix_io_r() */
339 
340 	PORT_START("P2")
341 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
342 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
343 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
344 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
345 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
346 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
347 	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START2 )
348 	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN2 )      /* handled by xyonix_io_r() */
349 
350 	PORT_START("DSW")
351 	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Difficulty ) )
352 	PORT_DIPSETTING(    0x03, DEF_STR( Easy ) )
353 	PORT_DIPSETTING(    0x02, DEF_STR( Normal ) )
354 	PORT_DIPSETTING(    0x01, DEF_STR( Hard ) )
355 	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )          // DEF_STR( Very_Hard )
356 	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Allow_Continue ) )
357 	PORT_DIPSETTING(    0x04, DEF_STR( No ) )
358 	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
359 	PORT_SERVICE( 0x08, IP_ACTIVE_LOW )
360 	PORT_DIPNAME( 0x30, 0x30, DEF_STR( Coin_A ) )
361 	PORT_DIPSETTING(    0x10, DEF_STR( 2C_1C ) )
362 	PORT_DIPSETTING(    0x30, DEF_STR( 1C_1C ) )
363 	PORT_DIPSETTING(    0x00, DEF_STR( 2C_3C ) )
364 	PORT_DIPSETTING(    0x20, DEF_STR( 1C_2C ) )
365 	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Coin_B ) )
366 	PORT_DIPSETTING(    0x40, DEF_STR( 2C_1C ) )
367 	PORT_DIPSETTING(    0xc0, DEF_STR( 1C_1C ) )
368 	PORT_DIPSETTING(    0x00, DEF_STR( 2C_3C ) )
369 	PORT_DIPSETTING(    0x80, DEF_STR( 1C_2C ) )
370 INPUT_PORTS_END
371 
372 /* GFX Decode ****************************************************************/
373 
374 static const gfx_layout charlayout =
375 {
376 	4,8,
377 	RGN_FRAC(1,2),
378 	4,
379 	{ 0, 4, RGN_FRAC(1,2)+0, RGN_FRAC(1,2)+4 },
380 	{ 3, 2, 1, 0 },
381 	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
382 	4*16
383 };
384 
385 static GFXDECODE_START( gfx_xyonix )
386 	GFXDECODE_ENTRY( "gfx1", 0, charlayout, 0, 16 )
387 GFXDECODE_END
388 
389 
390 /* MACHINE driver *************************************************************/
391 
xyonix(machine_config & config)392 void xyonix_state::xyonix(machine_config &config)
393 {
394 	/* basic machine hardware */
395 	Z80(config, m_maincpu, 16000000 / 4);        /* 4 MHz ? */
396 	m_maincpu->set_addrmap(AS_PROGRAM, &xyonix_state::main_map);
397 	m_maincpu->set_addrmap(AS_IO, &xyonix_state::port_map);
398 	m_maincpu->set_periodic_int(FUNC(xyonix_state::irq0_line_assert), attotime::from_hz(4*60));  /* ?? controls music tempo */
399 
400 	/* video hardware */
401 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
402 	screen.set_raw(16_MHz_XTAL / 2, 508, 0, 320, 256, 0, 224); // 8 MHz?
403 	screen.set_screen_update(FUNC(xyonix_state::screen_update));
404 //  screen.set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
405 	screen.set_palette("palette");
406 
407 	GFXDECODE(config, m_gfxdecode, "palette", gfx_xyonix);
408 
409 	PALETTE(config, "palette", FUNC(xyonix_state::xyonix_palette), 256);
410 
411 	MC6845(config, m_crtc, 16_MHz_XTAL / 8); // 2 MHz?
412 	m_crtc->set_screen("screen");
413 	m_crtc->set_show_border_area(false);
414 	m_crtc->set_char_width(4);
415 //  m_crtc->set_update_row_callback(FUNC(xyonix_state::crtc_update_row));
416 	m_crtc->out_vsync_callback().set(FUNC(xyonix_state::nmiclk_w));
417 
418 	/* sound hardware */
419 	SPEAKER(config, "mono").front_center();
420 
421 	SN76496(config, "sn1", 16000000/4).add_route(ALL_OUTPUTS, "mono", 1.0);
422 	SN76496(config, "sn2", 16000000/4).add_route(ALL_OUTPUTS, "mono", 1.0);
423 }
424 
425 /* ROM Loading ***************************************************************/
426 
427 ROM_START( xyonix )
428 	ROM_REGION( 0x10000, "maincpu", 0 )
429 	ROM_LOAD( "xyonix3.bin", 0x00000, 0x10000, CRC(1960a74e) SHA1(5fd7bc31ca2f5f1e114d3d0ccf6554ebd712cbd3) )
430 
431 	ROM_REGION( 0x10000, "mcu", 0 )
432 	ROM_LOAD( "mc68705p3s.e7", 0x00000, 0x780, BAD_DUMP CRC(f60cdd86) SHA1(e18cc598153b3e108942328ee9c5b9f83b034c41) ) // FIXED BITS (xxxxxx0x)
433 
434 	ROM_REGION( 0x10000, "gfx1", 0 )
435 	ROM_LOAD( "xyonix1.bin", 0x00000, 0x08000, CRC(3dfa9596) SHA1(52cdbbe18f83cea7248c29588ea3a18c4bb7984f) )
436 	ROM_LOAD( "xyonix2.bin", 0x08000, 0x08000, CRC(db87343e) SHA1(62bc30cd65b2f8976cd73a0b349a9ccdb3faaad2) )
437 
438 	ROM_REGION( 0x0100, "proms", 0 )
439 	ROM_LOAD( "xyonix.pr",   0x0000, 0x0100, CRC(0012cfc9) SHA1(c7454107a1a8083a370b662c617117b769c0dc1c) )
440 ROM_END
441 
442 /* GAME drivers **************************************************************/
443 
444 GAME( 1989, xyonix, 0, xyonix, xyonix, xyonix_state, empty_init, ROT0, "Philko", "Xyonix", MACHINE_SUPPORTS_SAVE )
445