summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/rulechan.cpp
blob: c421cf105089da95a52b4a5e5d843cab8a7b7cb4 (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
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
// license:BSD-3-Clause
// copyright-holders: Grull Osgo.
/********************************************************************

  Super Ball

  6-player Electromechanical Roulette.

  Copyright 199x by Electro Chance.
  Industria Argentina.

  Driver by Grull Osgo.

  2 sets dumped:

  - Super Ball (Version EC-3.50 N322),     1991.
  - Super Ball (Version EC-3.50 N165),     1991.


*********************************************************************

  PCB specs: Hardware not available


*********************************************************************

  Edge Connector:


       Solder side |  | Components
  -----------------|--|-----------------
             +5Vcc |01| GND
             +5Vcc |02| -5Vcc
          Player 2 |03| Player 1
          Player 4 |04| Player 3
          Player 6 |06| Player 5
                   |07|
                   |08| Left
             Right |09| Down
        Credits IN |10| Credits OUT
               Bet |11| Up
                   |12|
                   |13|
                   |14|
                   |15|
                   |16|
                   |17|
                   |18| +5VCC
        Counter IN |19|
                   |20| Counter OUT
      Operator Key |21|
                   |22| Programming
                   |23|
             Green |24| Sync
              Blue |25| Red
            +12Vcc |26| Speaker
             +5Vcc |27| GND
             +5Vcc |28| GND


  DIP Switches:

  .---------------------------------------+-----+-----+-----+-----+-----+-----+-----+-----.
  | DIP Switches                          |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  +---------------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
  +---------------------------------------+-----+-----------------------------------------+
  | Reset total                  No       | OFF |                                         |
  | (Full Reset)                 Si       | ON  |                                         |
  '---------------------------------------+-----+-----+-----------------------------------+
  | Repite                       No       |     | OFF |                                   |
  | (repeat)                     Si       |     | ON  |                                   |
  +---------------------------------------+-----+-----+-----+-----------------------------+
  | Demo                         No       |     |     | OFF |                             |
  |                              Si       |     |     | ON  |                             |
  +---------------------------------------+-----+-----+-----+-----+-----------------------+
  | Acceso a Pagina              No       |                 | OFF |                       |
  | (settings access)            Si       |                 | ON  |                       |
  +---------------------------------------+-----------------+-----+-----+-----------------+
  | Test plato                   No       |                       | OFF |                 |
  | (wheel test)                 Si       |                       | ON  |                 |
  +---------------------------------------+-----------------------+-----+-----+-----------+
  | Inmovilizar                  No       |                             | OFF |           |
  | (freeze)                     Si       |                             | ON  |           |
  +---------------------------------------+-----------------------------+-----+-----+-----+
  | Doble cero (double zero)     No       |                                   | OFF |     |
  | (Jackpot program)            Si       |                                   | ON  |     |
  +---------------------------------------+-----------------------------------+-----+-----+
  | Castellano                   No       |                                         | OFF |
  | (spanish)                    Si       |                                         | ON  |
  +---------------------------------------+-----------------------------------------+-----+


*********************************************************************

  Game Initialization
  -------------------

  (You must enable "LAMPS" layout for this procedure)

  When the game runs by the very first time (no nvram yet), it will
  complain about "DATA ERROR" in a blue message window. After a few
  seconds, it will bring up another red window with the "HARD ERROR 02"
  message.

  So, we must initialize the game following the next instructions:

  1.- Toggle ON "Operator Key" (turn to green).

  2.- Press once "Page Key". It will show a green window titled
      "CONTROL ADMINISTRATIVO" and a message with a security token.

  3.- Press again "Page Key". It will bring up a new window where we
      must type a password. At this time, in the layout, under the
      roulette leds will appear the password we need (six numeric digits).

  4.- Enter the required paswword using the credits in (IN1....IN6)
      and credits out (OUT1...OUT6) buttons following the key assignment
      indications located under the password field.
      Use the "E" button to finish once all numbers were typed.
      Use the "B" button clear last digit typed, in case of mistake.

  5.- Once finished that, the game will reboot and will be ready
      to play. Also, password showed on layout will dissapear.

      In case that (by unknown reason) the game asks for
      "CONTROL ADMINISTRATIVO" again, follow the instructions starting
      from step "1". After that, we will can play again.


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


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

  Dev notes:

  Again, this driver was written based on a couple of ROM dumps and a
  lot of reverse engineering, with no harware available, but guessing
  that this firmware and hardware are similar to others well known
  roulettes like Lucky Ball, Lucky Roulette, Corona, Re900, etc...

  The most exciting part of this work was discover that this game runs
  with an electromechanical roulette, not a tipical LED roulette. It
  added an extra challenge to the work, wich implies a full develop
  of an electromechanical part simulation, objective that finally
  could be reached.

  Surprisingly, this game firmware includes a full communications
  module, accesible via RS232 serial interface, that let the users
  some useful things like reconfigure hardware and game options or
  get different kinds of reports, like accounting, statistics and
  many other technical items. All this tasks are performed from a
  PC running a D.O.S. program provided by the maker and fortunately
  still available. Even more, there is another standalone D.O.S.
  software provided by the manufacturer (also still available) to
  get the passwords needed when hardware fails or administrative
  tasks are required.

  Another interesting thing found on this game is that it can be
  configured for a single or a double zero roulette, depending on
  what kind of roulette has attached.


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


#define CPU_CLOCK       XTAL(8'000'000)     // guess
#define VID_CLOCK       XTAL(21'477'272)    // guess
#define TMS_CLOCK       (CPU_CLOCK / 4)      // guess
#define VDP_MEM         0x20000  // 4x 4464 (64K x 4 DRAM)

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/eepromser.h"
#include "machine/timer.h"
#include "video/v9938.h"
#include "sound/ay8910.h"
#include "screen.h"
#include "speaker.h"
#include "rulechan.lh"

#define BIT2    BIT(m_p30,2)
#define BIT3    BIT(m_p30,3)
#define BIT4    BIT(m_p30,4)
#define BIT5    BIT(m_p30,5)

#define SND_FLG 0xf009
#define RAM_PSW 0xf078

#define BALLIN  (m_ballin >= 2)
#define MOTORON (m_p32 != 0xf0)
#define LEDNOTNUM (m_led != m_num)


class rulechan_state : public driver_device
{
public:
	rulechan_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_v9938(*this, "v9938")
		, m_maincpu(*this, "maincpu")
		, m_eeprom(*this, "eeprom")
		, m_aux(*this, "AUX")
		, m_dsw(*this, "DSW")
		, m_keymx(*this, "IN%u", 0)
		, m_lamps(*this, "lamp%u", 0U)
		, m_digits(*this, "digit%u", 0U)
	{ }

	void rulechan(machine_config &config);
	void rulechan_init();

private:

	DECLARE_READ8_MEMBER(port0_r);
	DECLARE_WRITE8_MEMBER(port0_w);
	DECLARE_READ8_MEMBER(port2_r);
	DECLARE_READ8_MEMBER(port3_r);
	DECLARE_READ8_MEMBER(port30_r);
	DECLARE_READ8_MEMBER(port31_r);
	DECLARE_WRITE8_MEMBER(port31_w);
	DECLARE_WRITE8_MEMBER(port32_w);
	DECLARE_READ8_MEMBER(psg_portA_r);
	DECLARE_READ8_MEMBER(psg_portB_r);

	TIMER_DEVICE_CALLBACK_MEMBER( ball_speed );
	TIMER_DEVICE_CALLBACK_MEMBER( wheel_speed );

	uint8_t m_sline;
	uint8_t m_p30;
	uint8_t m_p31;
	uint8_t m_p32;
	uint8_t m_step;
	uint8_t m_updn2;
	uint8_t m_updn3;
	uint8_t m_updn4;
	uint8_t m_num;
	uint8_t m_spin;
	uint8_t m_tspin;
	uint8_t d_spin;
	uint8_t m_ballin;
	uint8_t m_led;
	uint8_t m_pass[6];
	uint8_t m_sndsrt[10]= { 0x0a, 0x0e, 0x06, 0x0a, 0x0b, 0x48, 0x0c, 0x00, 0x0d, 0x01 };

	required_device<v9938_device> m_v9938;
	required_device<cpu_device> m_maincpu;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_ioport m_aux;
	required_ioport m_dsw;
	required_ioport_array<6> m_keymx;

	void main_io(address_map &map);
	void main_map(address_map &map);
	void sound_sort();
	void sound_off();

	virtual void machine_start() override { m_lamps.resolve(); m_digits.resolve(); }
	output_finder<63> m_lamps;
	output_finder<6> m_digits;
};


/* BCD to Seven Segment Decoder */

static uint8_t dec_7seg(int data)
{
	uint8_t segment;
	switch (data)
	{
		case 0: segment = 0x3f; break;
		case 1: segment = 0x06; break;
		case 2: segment = 0x5b; break;
		case 3: segment = 0x4f; break;
		case 4: segment = 0x66; break;
		case 5: segment = 0x6d; break;
		case 6: segment = 0x7d; break;
		case 7: segment = 0x07; break;
		case 8: segment = 0x7f; break;
		case 9: segment = 0x6f; break;
		default: segment = 0x00;
	}

	return segment;
}


/**************************************
*             Memory Map              *
**************************************/

void rulechan_state::main_map(address_map &map)
{
	map(0x0000, 0xdfff).rom();
	map(0xe000, 0xffff).ram().share("nvram");
}

void rulechan_state::main_io(address_map &map)
{
	map.global_mask(0xff);

	map(0x00, 0x00).rw(FUNC(rulechan_state::port0_r),FUNC(rulechan_state::port0_w));   // Matrix scan line selector - Must be 0xf0 at power-up.
	map(0x01, 0x01).portw("EEPROMOUT");                                                // EEPROM ok.
	map(0x02, 0x02).r(FUNC(rulechan_state::port2_r));                                  // Matrix button read.
	map(0x03, 0x03).r(FUNC(rulechan_state::port3_r));
	map(0x10, 0x11).w("ay8910", FUNC(ay8910_device::address_data_w));                  // sound ok.
	map(0x12, 0x12).r("ay8910", FUNC(ay8910_device::data_r));                          // ports ok.
	map(0x20, 0x23).rw(m_v9938, FUNC(v9938_device::read), FUNC(v9938_device::write));  // video ok.
	map(0x30, 0x30).r(FUNC(rulechan_state::port30_r));                                 // wheel control.
	map(0x31, 0x31).rw(FUNC(rulechan_state::port31_r),FUNC(rulechan_state::port31_w)); // wheel control - read: Must be 0x00 at power-up.
	map(0x32, 0x32).w(FUNC(rulechan_state::port32_w));                                 // wheel control.
	map(0x40, 0x43).nopr().nopw();
	map(0x60, 0x60).nopw();                                                            // Watchdog.
}


/**************************
*      Read Handlers      *
**************************/

READ8_MEMBER(rulechan_state::port0_r)
{
	return 0xf0;
}

READ8_MEMBER(rulechan_state::port2_r)
{
	return m_keymx[m_sline]->read();
}

READ8_MEMBER(rulechan_state::port3_r)
{
	return 0xff;
}

/******************************
   Port 30 - Wheel control    *
*  bit 2 - ball detector      *
*  bit 3 - step detector      *
*  bit 4 - Zero detector      *
*  bit 5 - Ball in shotter    *
******************************/

READ8_MEMBER(rulechan_state::port30_r)
{
	return m_p30;
}

READ8_MEMBER(rulechan_state::port31_r)
{
	return 0x00;
}

READ8_MEMBER(rulechan_state::psg_portA_r)
{
	m_lamps[60] = (BIT(m_aux->read(), 3)) ? 0 : 1;    // Show Operator Key via layout lamp.
	m_lamps[61] = (BIT(m_aux->read(), 7)) ? 0 : 1;    // Show Page Key via layout lamp.
	return m_aux->read();                             // Operator Key read.
}

READ8_MEMBER(rulechan_state::psg_portB_r)
{
	return m_dsw->read();                   // DIP Switch read.
}


/***********************
*    Write Handlers    *
***********************/

WRITE8_MEMBER(rulechan_state::port0_w)
{
	m_sline = data & 0x07;                 // Matrix scan line selector.

	if (m_sline > 5)
	{
		m_sline = 0;
	}
}

/****************************************
   Port 31 - Wheel driver               *
*  bit 1 - Port 32 Data Latch impulse   *
*  bit 4 - ball drop control            *
*  bit 7 - ball shooter                 *
****************************************/

WRITE8_MEMBER(rulechan_state::port31_w)
{
	m_p31 = data;

	if(BIT(m_p31, 4))
	{
		m_p30 |= 0x20;
		m_ballin = 0;      // Drop ball....ball in shooter.
	}

	if(BIT(m_p31, 7))                      // Shoot ball.
	{
		m_p30 &= 0xdf;                     // ball out....
		m_num = machine().rand() % 37;     // sort winning number.

		//logerror("shooting ball 2d\n", m_num);
	}
}

/****************************************
   Port 32h - Motor Control             *
*                                       *
*  data latch for speed control ...     *
*                                       *
****************************************/

WRITE8_MEMBER(rulechan_state::port32_w)
{
	m_p32 = data;
}


/************************************************
*    Extra Sound - Roulette spinning ball       *
************************************************/

void rulechan_state::sound_off()
{
		m_maincpu->space(AS_IO).write_byte(0x10, 0x07);
		m_maincpu->space(AS_IO).write_byte(0x11, m_maincpu->space(AS_PROGRAM).read_byte(SND_FLG) | 0x20);
		m_maincpu->space(AS_IO).write_byte(0x10, 0x0e);
}

void rulechan_state::sound_sort()
{
	for (int i = 0; i < 5; i++)
	{
		m_maincpu->space(AS_IO).write_byte(0x10, m_sndsrt[(2 * i)]);
		m_maincpu->space(AS_IO).write_byte(0x11, m_sndsrt[(2 * i) + 1]);
	}
	m_maincpu->space(AS_IO).write_byte(0x10, 0x07);
	m_maincpu->space(AS_IO).write_byte(0x11, m_maincpu->space(AS_PROGRAM).read_byte(SND_FLG) & 0xdf);
	m_maincpu->space(AS_IO).write_byte(0x10, 0x0e);
}


/***********************
*    Wheel Simulation  *
***********************/

TIMER_DEVICE_CALLBACK_MEMBER(rulechan_state::wheel_speed)
{
	if(m_step == 0)
	{
		if((BIT4) & (m_updn4 == 0))
		{
			m_p30 &= 0xef;
			m_updn4 = 1;

			//logerror("1:port_p30:- Reset bit 4 pulse start -%2x cont_pasos:%2d\n",m_p30, m_step);
			return;
		}

		if((!BIT4) & (m_updn4 == 1))
		{
			m_p30 |= 0x10;

			//logerror("2:port_p30:- Set bit 4 -%2x cont_pasos:%2d\n",m_p30, m_step);
			return;
		}

		if((BIT4) & (m_updn4 == 1))
		{
			m_updn4 = 0;
			m_step++;

			//logerror("3:port_p30:-end mark for reset bit 4 -%2x cont_pasos:%2d\n",m_p30, m_step);
			return;
		}
	}
	else
	{
		if(BIT3 & (m_updn3 == 0))
		{
			m_p30 &= 0xf7;

			//logerror("4:port_p30:-reset bit 3 -%2x cont_pasos:%2d\n",m_p30, m_step);
			return;
		}

		if(!BIT3 & (m_updn3 == 0))
		{
			if(!BIT2)
			{
				m_p30 |= 0x04;

				//logerror("5:port_p30:-set bit 2 -%2x cont_pasos:%2d\n",m_p30, m_step);
				return;
			}
			else
			{
				if((m_step - 1 == m_num) & (m_updn2 == 0))
				{
					if(!BIT5)   // ball in pocket?...
					{
						m_p30 &= 0xfb;
						m_updn2 = 1;
						m_ballin++;
						logerror("Ball In Pocket m_num:%2x\n", m_num);
						return;
					}
				}
			}

			m_updn2 = 0;
			m_updn3 = 1;
		}

		if(!BIT3 & (m_updn3 == 1))
		{
			m_p30 |= 0x08;
			m_updn3 = 0;
			m_step++;

			if (m_step == 39)
			{
				m_step = 0;
				m_p30 |= 0x1c;
			}

			//logerror("6:port_p30:-set bit 3 -%2x cont_pasos:%2d \n",m_p30, m_step);
			return;
		}
	}
}


/***********************
*    Ball Simulation   *
***********************/

TIMER_DEVICE_CALLBACK_MEMBER(rulechan_state::ball_speed)
{
	if(MOTORON)
	{
		if(d_spin == 0)
		{
			m_tspin++;
			d_spin = m_spin;

			if(BALLIN)
			{
				m_tspin = 37;   // breaking ball once per number step.
			}

			if(m_tspin == 37)
			{
				m_tspin = 0;
				m_spin++;
				d_spin = m_spin;   // breaking ball once per round.
			}

			if((!BALLIN) | (LEDNOTNUM & BALLIN ))
			{
				m_led++;

				if(m_led == 37)
				{
					m_led = 0;
				}

				for (int i = 0; i < 37; i++)
				{
					m_lamps[i + 20] = (m_led == i) ? 1 : 0;   // update roulette led lamps.
				}

				sound_sort();
			}
		}
		else
		{
			d_spin--;     // burn cycles...
			sound_off();
		}
	}
	else
	{
		for (int i = 0; i < 37; i++)
		{
			m_lamps[i + 20] = (m_num == i) ? 1 : 0;
		}

		m_spin=d_spin=m_tspin=m_ballin=0;
	}

	/* END of Ball simulation */

/* if needed, get pass and shows it on layout*/
	m_pass[0] = m_maincpu->space(AS_PROGRAM).read_byte(RAM_PSW);

	if((m_pass[0] <= 0x39) & (m_pass[0] >= 0x30))
	{
		for(int i = 0; i < 6; i++)
		{
			m_pass[i]= m_maincpu->space(AS_PROGRAM).read_byte(RAM_PSW + i);
			m_lamps[10 + i] = dec_7seg(m_pass[i] - 0x30);
		}
	}
	else
	{
		for(int i = 0; i < 6; i++)
		{
			m_lamps[10 + i] = dec_7seg(0xff);
		}
	}
}


/**************************************
*            Input Ports              *
**************************************/

static INPUT_PORTS_START( rulechan )

	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P1 Key-In")  PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P1 Key-Out") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON6 )

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P2 Key-In")  PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P2 Key-Out") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P3 Key-In")  PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P3 Key-Out") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P4 Key-In")  PORT_CODE(KEYCODE_4)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P4 Key-Out") PORT_CODE(KEYCODE_V)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P5 Key-In")  PORT_CODE(KEYCODE_5)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P5 Key-Out") PORT_CODE(KEYCODE_B)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 )  PORT_NAME("Bet")        PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 )  PORT_NAME("Down")       PORT_CODE(KEYCODE_DOWN)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 )  PORT_NAME("Up")         PORT_CODE(KEYCODE_UP)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON4 )  PORT_NAME("Left")       PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON5 )  PORT_NAME("Right")      PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED  )

	PORT_START("IN5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P6 Key-In")  PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("P6 Key-Out") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("AUX")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE )  PORT_NAME("Tilt 1")    PORT_CODE(KEYCODE_7)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SERVICE )  PORT_NAME("Tilt 2")    PORT_CODE(KEYCODE_8)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_SERVICE )  PORT_TOGGLE            PORT_CODE(KEYCODE_0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_READ_LINE_DEVICE_MEMBER("eeprom", eeprom_serial_93cxx_device, do_read)   // bit 6 is EEPROM data.
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_CODE(KEYCODE_9)

	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x01, "Borrado Total" )  PORT_DIPLOCATION("DSW:1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, "Repite" )         PORT_DIPLOCATION("DSW:2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "Demo" )           PORT_DIPLOCATION("DSW:3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, "Acceso" )         PORT_DIPLOCATION("DSW:4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, "Test de Plato" )  PORT_DIPLOCATION("DSW:5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Inmoviliza" )     PORT_DIPLOCATION("DSW:6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, "Doble Cero" )     PORT_DIPLOCATION("DSW:7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, "Castellano" )     PORT_DIPLOCATION("DSW:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", eeprom_serial_93cxx_device, di_write)    // bit 3 is data (active high).
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", eeprom_serial_93cxx_device, clk_write)   // bit 4 is clock (active high).
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", eeprom_serial_93cxx_device, cs_write)    // bit 5 is cs.

INPUT_PORTS_END


/**************************************
*           Machine Driver            *
**************************************/

MACHINE_CONFIG_START(rulechan_state::rulechan)

	/* basic machine hardware */
	Z80(config, m_maincpu, CPU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &rulechan_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &rulechan_state::main_io);
	m_maincpu->set_periodic_int(FUNC(rulechan_state::irq0_line_hold), attotime::from_hz(120));

	/* nvram */
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* eeprom */
	EEPROM_93C46_8BIT(config, "eeprom");

	MCFG_TIMER_DRIVER_ADD_PERIODIC("ball_speed", rulechan_state, ball_speed, attotime::from_hz(60))
	MCFG_TIMER_DRIVER_ADD_PERIODIC("wheel_speed", rulechan_state, wheel_speed, attotime::from_hz(60))

	/* video hardware */
	v9938_device &v9938(V9938(config, "v9938", VID_CLOCK));
	v9938.set_screen_ntsc("screen");
	v9938.set_vram_size(VDP_MEM);
	//v9938.int_cb().set_inputline("maincpu", 0);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware   */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay_re900(AY8910(config, "ay8910", TMS_CLOCK));
	ay_re900.port_a_read_callback().set(FUNC(rulechan_state::psg_portA_r));
	ay_re900.port_b_read_callback().set(FUNC(rulechan_state::psg_portB_r));
	ay_re900.add_route(ALL_OUTPUTS, "mono", 0.5);

MACHINE_CONFIG_END


/**************************************
*              ROM Load               *
**************************************/

ROM_START( rulechan )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "eprom322", 0x00000, 0x10000, CRC(a78e106f) SHA1(23e25ca60296e8002df9b9cf16ad8fe8a1e6c93b) )

	ROM_REGION(0x80, "eeprom", 0)
	ROM_LOAD( "eeprom.322", 0x0000, 0x0080, CRC(ded905d2) SHA1(999e8e54a31a232092c4345434b93358226d1144) )
ROM_END

ROM_START( rulechab )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "eprom165", 0x00000, 0x10000, CRC(1669f266) SHA1(14e70b91e260e3bc338870936954e09c182fe591) )

	ROM_REGION(0x80, "eeprom", 0)
	ROM_LOAD( "eeprom.165", 0x0000, 0x0080, CRC(042ac516) SHA1(a32d82acb438cceda0f03b58be6977eeb589836c) )
ROM_END


/************************
*      Driver Init      *
************************/

void rulechan_state::rulechan_init()
{
	m_p30 = 0x3c;
	m_p32 = 0xf0;  // Motor off at startup
	m_step = 0;
	m_updn2 = 0;
	m_updn3 = 0;
	m_updn4 = 0;
}


/**************************************
*           Game Driver(s)            *
**************************************/

//    YEAR  NAME      PARENT    MACHINE   INPUT     STATE           INIT           ROT    COMPANY          FULLNAME                            FLAGS                                                                   LAYOUT
GAMEL(1991, rulechan, 0,        rulechan, rulechan, rulechan_state, rulechan_init, ROT0, "ElectroChance", "Super Ball (Version EC-3.50 N322)", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL | MACHINE_CLICKABLE_ARTWORK, layout_rulechan)
GAMEL(1991, rulechab, 0,        rulechan, rulechan, rulechan_state, rulechan_init, ROT0, "ElectroChance", "Super Ball (Version EC-3.50 N165)", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL | MACHINE_CLICKABLE_ARTWORK, layout_rulechan)