1 // license:BSD-3-Clause
2 // copyright-holders:MetalliC
3
4 /*
5 Gambling hardware based on "Specialist MX" computer
6 (c) 199? Dinaris
7
8 Main components:
9 Z80 CPU,
10 27256 ROM,
11 2x KR580VV55(i8255 PPI),
12 KR580VI53(i8253 PIT),
13 8x KR565RU5(4164) DRAM,
14 3x KR573RU10(HM6516) SRAM
15
16 How to enter statistics mode:
17 - press Test button, "CODE" text will appear
18 - repeat 4x times
19 - press Test button (code_digit - 1) times
20 - press and hold Test button until "INPUT" text appears
21 - release Test button
22 Currently we use 0000 hardware code, in such case game will accept any code sequence.
23 Now you may press "Clear stats" key few times to clear all the non-volatile play statistics.
24 Or press "Clear stats" once, then hold for a several seconds, and then press again - game will clear NVRAM and also raise few flags, effect is not known.
25
26 */
27
28 #include "emu.h"
29 #include "cpu/z80/z80.h"
30 #include "audio/special.h"
31 #include "machine/i8255.h"
32 #include "machine/pit8253.h"
33 #include "machine/nvram.h"
34 #include "machine/ticket.h"
35 #include "emupal.h"
36 #include "screen.h"
37 #include "speaker.h"
38
39 class dinaris_state : public driver_device
40 {
41 public:
dinaris_state(const machine_config & mconfig,device_type type,const char * tag)42 dinaris_state(const machine_config &mconfig, device_type type, const char *tag) :
43 driver_device(mconfig, type, tag)
44 , m_maincpu(*this, "maincpu")
45 , m_ppi(*this, "ppi8255")
46 , m_ppi2(*this, "ppi82552")
47 , m_pit(*this, "pit8253")
48 , m_palette(*this, "palette")
49 , m_nvram(*this, "nvram")
50 , m_vram(*this, "vram")
51 , m_hopper(*this, "hopper")
52 , m_lamps(*this, "lamp%u", 0U)
53 , m_sram_en(false)
54 , m_cold_boot(0)
55 { }
56
57 void dice(machine_config &config);
58
DECLARE_CUSTOM_INPUT_MEMBER(boot_r)59 DECLARE_CUSTOM_INPUT_MEMBER(boot_r) { return m_cold_boot; }
DECLARE_INPUT_CHANGED_MEMBER(ram_test)60 DECLARE_INPUT_CHANGED_MEMBER(ram_test) { m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE); }
61
62 protected:
63 enum
64 {
65 TIMER_COLD_BOOT
66 };
67
68 void machine_start() override;
69 virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
70
71 void dice_palette(palette_device &palette) const;
72 u32 screen_update_dice(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
73
74 void dice_mem(address_map &map);
75 void dice_io(address_map &map);
76
77 required_device<cpu_device> m_maincpu;
78 required_device<i8255_device> m_ppi;
79 required_device<i8255_device> m_ppi2;
80 required_device<pit8253_device> m_pit;
81 required_device<palette_device> m_palette;
82 required_device<nvram_device> m_nvram;
83 required_shared_ptr<u8> m_vram;
84 required_device<hopper_device> m_hopper;
85 output_finder<8> m_lamps;
86
87 void lamps_w(u8 data);
88 u8 ppi2a_r();
89 void ppi2c_w(u8 data);
90 u8 code_r(offs_t offset);
91
92 std::unique_ptr<u8[]> m_sram;
93 bool m_sram_en;
94 int m_cold_boot;
95 };
96
97
98 static INPUT_PORTS_START(dice)
99 PORT_START("P0")
100 PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_GAMBLE_LOW) // 6 or less / double less
101 PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_GAMBLE_D_UP) // 12/11
102 PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_GAMBLE_BET) // bet / field
103 PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_GAMBLE_HALF) // point/7
104 PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_GAMBLE_HIGH) // 8 or more / double more
105 PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_GAMBLE_DEAL) // start / double
106 PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_GAMBLE_PAYOUT) // payout / take
PORT_CUSTOM_MEMBER(dinaris_state,boot_r)107 PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(dinaris_state, boot_r)
108
109 PORT_START("P1")
110 PORT_DIPNAME(0x01, 0x00, DEF_STR(Language))
111 PORT_DIPSETTING(0x00, DEF_STR(English))
112 PORT_DIPSETTING(0x01, "Russian")
113 PORT_SERVICE_NO_TOGGLE(0x02, IP_ACTIVE_LOW)
114 PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_SERVICE1) PORT_NAME("Clear stats")
115 PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_COIN1)
116 PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("hopper", hopper_device, line_r)
117 PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_SERVICE2) PORT_NAME("Reset")
118 PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
119 PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
120
121 PORT_START("NMI")
122 PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_SERVICE3) PORT_NAME("RAM Test") PORT_CHANGED_MEMBER(DEVICE_SELF, dinaris_state, ram_test, 0)
123 INPUT_PORTS_END
124
125 void dinaris_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
126 {
127 switch (id)
128 {
129 case TIMER_COLD_BOOT:
130 m_cold_boot = 1;
131 break;
132 default:
133 throw emu_fatalerror("Unknown id in dinaris_state::device_timer");
134 }
135 }
136
machine_start()137 void dinaris_state::machine_start()
138 {
139 m_lamps.resolve();
140
141 constexpr int size = 0x800; // actually used only 256 bytes
142 m_sram = std::make_unique<u8[]>(size);
143 m_nvram->set_base(&m_sram[0], size);
144
145 save_pointer(NAME(m_sram), size);
146 save_item(NAME(m_sram_en));
147 save_item(NAME(m_cold_boot));
148
149 timer_set(attotime::from_msec(100), TIMER_COLD_BOOT);
150 }
151
dice_mem(address_map & map)152 void dinaris_state::dice_mem(address_map &map)
153 {
154 map(0x0000, 0x7fff).rom();
155 map(0x8000, 0x8fff).ram(); // SRAM
156 map(0x9000, 0xffff).ram().share("vram"); // DRAM
157 }
158
dice_io(address_map & map)159 void dinaris_state::dice_io(address_map &map)
160 {
161 map.global_mask(0x000f);
162 map(0x00, 0x03).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
163 map(0x04, 0x07).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write));
164 map(0x08, 0x0b).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
165 map(0x0c, 0x0d).r(FUNC(dinaris_state::code_r)).mirror(0x02);
166 }
167
168 // borrowed from Specialist MX, probably wrong
169 static constexpr rgb_t specimx_pens[16] = {
170 { 0x00, 0x00, 0x00 }, // 0
171 { 0x00, 0x00, 0xaa }, // 1
172 { 0x00, 0xaa, 0x00 }, // 2
173 { 0x00, 0xaa, 0xaa }, // 3
174 { 0xaa, 0x00, 0x00 }, // 4
175 { 0xaa, 0x00, 0xaa }, // 5
176 { 0xaa, 0xaa, 0x00 }, // 6
177 { 0xaa, 0xaa, 0xaa }, // 7
178 { 0x55, 0x55, 0x55 }, // 8
179 { 0x55, 0x55, 0xff }, // 9
180 { 0x55, 0xff, 0x55 }, // A
181 { 0x55, 0xff, 0xff }, // B
182 { 0xff, 0x55, 0x55 }, // C
183 { 0xff, 0x55, 0xff }, // D
184 { 0xff, 0xff, 0x55 }, // E
185 { 0xff, 0xff, 0xff } // F
186 };
187
dice_palette(palette_device & palette) const188 void dinaris_state::dice_palette(palette_device &palette) const
189 {
190 palette.set_pen_colors(0, specimx_pens);
191 }
192
screen_update_dice(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)193 u32 dinaris_state::screen_update_dice(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
194 {
195 // note: attribute area bytes set colors for 4x2 pix screen elements, not 8x1 like Specialist MX.
196 for (int x = 0; x < 48; x++)
197 {
198 for (int y = 0; y < 256; y++)
199 {
200 u8 const code = m_vram[0x0000 + y + x * 256];
201 u8 const color1 = m_vram[0x4000 + (y & 0xfe) + x * 256];
202 u8 const color2 = m_vram[0x4000 + (y | 0x01) + x * 256];
203 for (int b = 7; b >= 4; b--)
204 bitmap.pix(y, x * 8 + (7 - b)) = BIT(code, b) ? (color1 & 0xf) : (color1 >> 4);
205 for (int b = 3; b >= 0; b--)
206 bitmap.pix(y, x * 8 + (7 - b)) = BIT(code, b) ? (color2 & 0xf) : (color2 >> 4);
207 }
208 }
209 return 0;
210 }
211
lamps_w(u8 data)212 void dinaris_state::lamps_w(u8 data)
213 {
214 // TODO identify each lamp
215 for (int i = 0; i < 8; i++)
216 m_lamps[i] = BIT(data, i);
217 }
218
ppi2a_r()219 u8 dinaris_state::ppi2a_r()
220 {
221 return m_sram_en ? m_sram[m_ppi2->pb_r()] : 0xff;
222 }
223
ppi2c_w(u8 data)224 void dinaris_state::ppi2c_w(u8 data)
225 {
226 if (!BIT(data, 2))
227 m_sram_en = true;
228
229 if (!BIT(data, 1))
230 m_sram_en = false;
231
232 if (!BIT(data, 0) && m_sram_en)
233 m_sram[m_ppi2->pb_r()] = m_ppi2->pa_r();
234
235 m_hopper->motor_w(((data & 0x68) == 0x60) ? 1 : 0);
236 }
237
code_r(offs_t offset)238 u8 dinaris_state::code_r(offs_t offset)
239 {
240 // it is not clear how statistics mode code was set, related components was removed from PCB
241 // currently we return 0 so game will accept any code sequence
242 return 0x0000 >> (offset * 8);
243 }
244
dice(machine_config & config)245 void dinaris_state::dice(machine_config &config)
246 {
247 // Basic machine hardware
248 Z80(config, m_maincpu, XTAL(8'000'000) / 4); // not confirmed
249 m_maincpu->set_addrmap(AS_PROGRAM, &dinaris_state::dice_mem);
250 m_maincpu->set_addrmap(AS_IO, &dinaris_state::dice_io);
251 m_maincpu->set_vblank_int("screen", FUNC(dinaris_state::irq0_line_hold));
252
253 // Video
254 screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
255 screen.set_raw(XTAL(8'000'000), 512, 0, 384, 312, 0, 256);
256 screen.set_screen_update(FUNC(dinaris_state::screen_update_dice));
257 screen.set_palette(m_palette);
258
259 PALETTE(config, m_palette, FUNC(dinaris_state::dice_palette), 16);
260
261 // Sound
262 SPEAKER(config, "speaker").front_center();
263 SPECIMX_SND(config, "custom", 0).add_route(ALL_OUTPUTS, "speaker", 1.0);
264
265 PIT8253(config, m_pit, 0);
266 m_pit->set_clk<0>(XTAL(8'000'000) / 4);
267 m_pit->out_handler<0>().set("custom", FUNC(specimx_sound_device::set_input_ch0));
268 m_pit->set_clk<1>(XTAL(8'000'000) / 4);
269 m_pit->out_handler<1>().set("custom", FUNC(specimx_sound_device::set_input_ch1));
270 m_pit->set_clk<2>(XTAL(8'000'000) / 4);
271 m_pit->out_handler<2>().set("custom", FUNC(specimx_sound_device::set_input_ch2));
272
273 // Devices
274 I8255(config, m_ppi);
275 m_ppi->in_pa_callback().set_ioport("P0");
276 m_ppi->in_pb_callback().set_ioport("P1");
277 m_ppi->out_pc_callback().set(FUNC(dinaris_state::lamps_w));
278
279 I8255(config, m_ppi2);
280 m_ppi2->in_pa_callback().set(FUNC(dinaris_state::ppi2a_r));
281 m_ppi2->out_pc_callback().set(FUNC(dinaris_state::ppi2c_w));
282
283 NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_0);
284 HOPPER(config, m_hopper, attotime::from_msec(100), TICKET_MOTOR_ACTIVE_HIGH, TICKET_STATUS_ACTIVE_LOW);
285 }
286
287 ROM_START(dindice)
288 ROM_REGION( 0x8000, "maincpu", 0)
289 ROM_LOAD( "27256.bin", 0x0000, 0x8000, CRC(511f8ba8) SHA1(e75a2cab80ac6b08a19d1adb8ba9bb321aa5e7a8))
290 ROM_END
291
292 GAME( 199?, dindice, 0, dice, dice, dinaris_state, empty_init, ROT0, "Dinaris", "Dice game", MACHINE_SUPPORTS_SAVE|MACHINE_IMPERFECT_COLORS)
293