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