1 // license:BSD-3-Clause
2 // copyright-holders:MetalliC
3 /*************************************************************************
4 
5     Istrebiteli driver by MetalliC
6 
7     TODO:
8       hardware-like noice sound generation
9       accurate sprite collision
10 
11     how to play:
12       insert one or more coins, each coin gives 2 minutes of play time, then press 1 or 2 player game start
13       hit enemy 15 or more times to get bonus game
14 
15     test mode:
16       insert 12 or more coins then press 2 player start
17 
18     notes:
19       dumped PCB is early game version, have several bugs, possible test/prototype.
20       later version was seen in St.Petersburg arcade museum, CPU board have single 8Kx8 ROM.
21 
22 **************************************************************************/
23 
24 #include "emu.h"
25 #include "cpu/i8085/i8085.h"
26 #include "machine/i8255.h"
27 #include "emupal.h"
28 #include "screen.h"
29 #include "speaker.h"
30 #include "tilemap.h"
31 
32 #define I8080_TAG   "maincpu"
33 
34 /////////////////////////////////////////////////////////////
35 
36 class istrebiteli_sound_device : public device_t, public device_sound_interface
37 {
38 public:
39 	istrebiteli_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
40 
41 	void sound_w(uint8_t data);
42 
43 protected:
44 	// device_t overrides
45 	virtual void device_start() override;
46 
47 	// device_sound_interface overrides
48 	virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
49 
50 private:
51 	// internal state
52 	sound_stream *m_channel;
53 	uint8_t *m_rom;
54 	int m_rom_cnt;
55 	int m_rom_incr;
56 	int m_sample_num;
57 	bool m_cnt_reset;
58 	bool m_rom_out_en;
59 	uint8_t m_prev_data;
60 };
61 
DECLARE_DEVICE_TYPE(ISTREBITELI_SOUND,istrebiteli_sound_device)62 DECLARE_DEVICE_TYPE(ISTREBITELI_SOUND, istrebiteli_sound_device)
63 
64 //////////////////////////////////////////////////////////////
65 
66 DEFINE_DEVICE_TYPE(ISTREBITELI_SOUND, istrebiteli_sound_device, "istrebiteli_sound", "Istrebiteli Sound")
67 
68 istrebiteli_sound_device::istrebiteli_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
69 	: device_t(mconfig, ISTREBITELI_SOUND, tag, owner, clock),
70 		device_sound_interface(mconfig, *this),
71 		m_channel(nullptr),
72 		m_rom(nullptr),
73 		m_rom_cnt(0),
74 		m_rom_incr(0),
75 		m_sample_num(0),
76 		m_cnt_reset(true),
77 		m_rom_out_en(false),
78 		m_prev_data(0)
79 {
80 }
81 
device_start()82 void istrebiteli_sound_device::device_start()
83 {
84 	m_channel = stream_alloc(0, 1, clock() / 2);
85 	m_rom = machine().root_device().memregion("soundrom")->base();
86 }
87 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)88 void istrebiteli_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
89 {
90 	auto &buffer = outputs[0];
91 
92 	for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
93 	{
94 		int smpl = 0;
95 		if (m_rom_out_en)
96 			smpl = (m_rom[m_rom_cnt] >> m_sample_num) & 1;
97 
98 		// below is huge guess
99 		if ((m_prev_data & 0x40) == 0)              // b6 noice enable ?
100 			smpl &= machine().rand() & 1;
101 		smpl *= (m_prev_data & 0x80) ? 1000 : 4000; // b7 volume ?
102 
103 		buffer.put_int(sampindex, smpl, 32768);
104 		m_rom_cnt = (m_rom_cnt + m_rom_incr) & 0x1ff;
105 	}
106 }
107 
sound_w(uint8_t data)108 void istrebiteli_sound_device::sound_w(uint8_t data)
109 {
110 	m_cnt_reset = (data & 2) ? true : false;
111 	m_sample_num = (data >> 2) & 7;
112 	m_rom_out_en = (data & 0x20) ? false : true;
113 
114 	if (m_cnt_reset)
115 	{
116 		m_rom_cnt = 0;
117 		m_rom_incr = 0;
118 	}
119 	else
120 		m_rom_incr = 1;
121 //  if (m_prev_data != data)
122 //      printf("sound %02X rescnt %d sample %d outen %d b6 %d b7 %d\n", data, (data >> 1) & 1, (data >> 2) & 7, (data >> 5) & 1, (data >> 6) & 1, (data >> 7) & 1);
123 	m_prev_data = data;
124 }
125 
126 //////////////////////////////////////////////////////////////
127 
128 class istrebiteli_state : public driver_device
129 {
130 public:
istrebiteli_state(const machine_config & mconfig,device_type type,const char * tag)131 	istrebiteli_state(const machine_config &mconfig, device_type type, const char *tag)
132 		: driver_device(mconfig, type, tag)
133 		, m_maincpu(*this, I8080_TAG)
134 		, m_ppi0(*this, "ppi0")
135 		, m_ppi1(*this, "ppi1")
136 		, m_gfxdecode(*this, "gfxdecode")
137 		, m_sound_dev(*this, "custom")
138 	{
139 	}
140 
141 	void init_istreb();
142 	void init_moto();
143 
144 	void istreb(machine_config &config);
145 	void motogonki(machine_config &config);
146 
147 	template <int ID> DECLARE_READ_LINE_MEMBER(collision_r);
148 	DECLARE_CUSTOM_INPUT_MEMBER(coin_r);
149 
150 	DECLARE_INPUT_CHANGED_MEMBER(coin_inc);
151 
152 protected:
153 	virtual void machine_start() override;
154 	virtual void machine_reset() override;
155 	virtual void video_start() override;
156 
157 private:
158 	void istrebiteli_palette(palette_device &palette) const;
159 	void motogonki_palette(palette_device &palette) const;
160 	uint8_t ppi0_r(offs_t offset);
161 	void ppi0_w(offs_t offset, uint8_t data);
162 	uint8_t ppi1_r(offs_t offset);
163 	void ppi1_w(offs_t offset, uint8_t data);
164 	void sound_w(uint8_t data);
165 	void spr0_ctrl_w(uint8_t data);
166 	void spr1_ctrl_w(uint8_t data);
167 	void spr_xy_w(offs_t offset, uint8_t data);
168 	void moto_spr_xy_w(offs_t offset, uint8_t data);
169 	void tileram_w(offs_t offset, uint8_t data);
170 	void moto_tileram_w(offs_t offset, uint8_t data);
171 	void road_ctrl_w(uint8_t data);
172 	DECLARE_VIDEO_START(moto);
173 
174 	required_device<cpu_device> m_maincpu;
175 	required_device<i8255_device> m_ppi0;
176 	required_device<i8255_device> m_ppi1;
177 	required_device<gfxdecode_device> m_gfxdecode;
178 	required_device<istrebiteli_sound_device> m_sound_dev;
179 
180 	TILE_GET_INFO_MEMBER(get_tile_info);
181 	tilemap_t *m_tilemap;
182 	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
183 	uint32_t moto_screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
184 
185 	uint8_t coin_count;
186 	uint8_t m_spr_ctrl[2];
187 	uint8_t m_spr_collision[2];
188 	uint8_t m_spr_xy[8];
189 	uint8_t m_tileram[16];
190 	uint8_t m_road_scroll;
191 
192 	void io_map(address_map &map);
193 	void mem_map(address_map &map);
194 	void moto_io_map(address_map &map);
195 	void moto_mem_map(address_map &map);
196 };
197 
machine_start()198 void istrebiteli_state::machine_start()
199 {
200 	save_item(NAME(coin_count));
201 	save_item(NAME(m_spr_ctrl));
202 	save_item(NAME(m_spr_collision));
203 	save_item(NAME(m_spr_xy));
204 	save_item(NAME(m_tileram));
205 }
206 
machine_reset()207 void istrebiteli_state::machine_reset()
208 {
209 	coin_count = 0;
210 	memset(m_spr_ctrl, 0, sizeof(m_spr_ctrl));
211 	memset(m_spr_collision, 0, sizeof(m_spr_collision));
212 	memset(m_spr_xy, 0, sizeof(m_spr_xy));
213 	memset(m_tileram, 0, sizeof(m_tileram));
214 }
215 
216 static const rgb_t istreb_palette[4] = {
217 		rgb_t(0x00, 0x00, 0x00),
218 		rgb_t(0x00, 0x00, 0xff),
219 		rgb_t(0xff, 0xff, 0xff),
220 		rgb_t(0x00, 0x00, 0xff) };
221 
istrebiteli_palette(palette_device & palette) const222 void istrebiteli_state::istrebiteli_palette(palette_device &palette) const
223 {
224 	palette.set_pen_colors(0, istreb_palette);
225 }
226 
227 static const rgb_t moto_palette[4] = {
228 		rgb_t(0x00, 0x00, 0x00),
229 		rgb_t(0x00, 0x00, 0xff),
230 		rgb_t(0xff, 0xff, 0xff),
231 		rgb_t(0x00, 0x00, 0x00) };
232 
motogonki_palette(palette_device & palette) const233 void istrebiteli_state::motogonki_palette(palette_device &palette) const
234 {
235 	palette.set_pen_colors(0, moto_palette);
236 }
237 
TILE_GET_INFO_MEMBER(istrebiteli_state::get_tile_info)238 TILE_GET_INFO_MEMBER(istrebiteli_state::get_tile_info)
239 {
240 	tileinfo.set(0, m_tileram[tile_index] & 0x1f, 0, 0);
241 }
242 
init_istreb()243 void istrebiteli_state::init_istreb()
244 {
245 	uint8_t *gfx = memregion("sprite")->base();
246 	uint8_t temp[64];
247 
248 	for (int offs = 0; offs < 0x200; offs += 0x40)
249 	{
250 		memset(&temp[0], 0, sizeof(temp));
251 		for (int spnum = 0; spnum < 8; spnum++)
252 			for (int dot = 0; dot < 64; dot++)
253 				temp[(dot >> 3) + spnum * 8] |= ((gfx[offs + dot] >> spnum) & 1) << (dot & 7);
254 		memcpy(&gfx[offs], &temp[0], sizeof(temp));
255 	}
256 }
257 
init_moto()258 void istrebiteli_state::init_moto()
259 {
260 	uint8_t *gfx = memregion("sprite")->base();
261 	uint8_t temp[256];
262 
263 	for (int offs = 0; offs < 0x600; offs += 0x100)
264 	{
265 		memset(&temp[0], 0, sizeof(temp));
266 		for (int spnum = 0; spnum < 8; spnum++)
267 			for (int dot = 0; dot < 256; dot++)
268 				temp[(dot >> 3) + spnum * 32] |= ((gfx[offs + dot] >> spnum) & 1) << (dot & 7);
269 		memcpy(&gfx[offs], &temp[0], sizeof(temp));
270 	}
271 }
272 
video_start()273 void istrebiteli_state::video_start()
274 {
275 	m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(istrebiteli_state::get_tile_info)), TILEMAP_SCAN_ROWS,
276 		8, 16, 16, 1);
277 	m_tilemap->set_scrolldx(96, 96);
278 }
279 
VIDEO_START_MEMBER(istrebiteli_state,moto)280 VIDEO_START_MEMBER(istrebiteli_state, moto)
281 {
282 	m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(istrebiteli_state::get_tile_info)), TILEMAP_SCAN_ROWS,
283 		8, 16, 16, 1);
284 	m_tilemap->set_scrolldx(96, 96);
285 	m_tilemap->set_scrolldy(8, 8);
286 }
287 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)288 uint32_t istrebiteli_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
289 {
290 	bitmap.fill(1);
291 
292 	rectangle rect = cliprect;
293 	rect.offset(32, 64);
294 	rect.set_size(16*8, 16);
295 	m_tilemap->draw(screen, bitmap, rect, 0, 0);
296 
297 	m_gfxdecode->gfx(3)->transpen(bitmap, cliprect, (m_spr_ctrl[0] & 0x40) ? 5 : 7, 0, 0, 0, m_spr_xy[4], m_spr_xy[5], 1);
298 	m_gfxdecode->gfx(3)->transpen(bitmap, cliprect, (m_spr_ctrl[1] & 0x40) ? 4 : 6, 0, 0, 0, m_spr_xy[6], m_spr_xy[7], 1);
299 
300 	int spritecode;
301 
302 	spritecode = (m_spr_ctrl[0] & 0x1f) + ((m_spr_ctrl[0] & 0x80) >> 2);
303 	m_gfxdecode->gfx(1)->transpen(bitmap, cliprect, spritecode, 0, 0, 0, m_spr_xy[0], m_spr_xy[1], 1);
304 	spritecode = (m_spr_ctrl[1] & 0x1f) + ((m_spr_ctrl[1] & 0x80) >> 2);
305 	m_gfxdecode->gfx(2)->transpen(bitmap, cliprect, spritecode, 0, 0, 0, m_spr_xy[2], m_spr_xy[3], 1);
306 
307 	return 0;
308 }
309 
moto_screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)310 uint32_t istrebiteli_state::moto_screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
311 {
312 	bitmap.fill(0);
313 
314 	// port 38 m_spr_ctrl[0] bits
315 	//   0123 - sprite 1 (player) idx
316 	//   4    - road row scroll enable
317 	//   5    - ? 1 during game mode, 0 in atract |
318 	//   6    - ? 1 during atract mode, 0 in game | one of these is coin lock, other unknown
319 	//   7    - sprites/road enable
320 	// port 39 m_spr_ctrl[1] bits
321 	//   0123 - sprite 2 idx
322 	//   4567 - sprite 3 idx
323 	// port 3d road row scroll decrement, 1 or 2 (starts at m_spr_xy[5] line, during 16 lines which makes road curve)
324 
325 	// temp / debug code
326 	// out of road space should be filled with 1pix grey 1pix black pattern, road area is grey, mid road lines is white, all sprites is black.
327 	rectangle rect = cliprect;
328 	rect.offsetx(64 - (s8)m_spr_xy[4]);
329 	rect.set_width(64);
330 	bitmap.fill(1, rect);
331 
332 	int spritecode0 = ((m_spr_ctrl[0] >> 1) & 7) + ((m_spr_ctrl[0] << 3) & 8);
333 	m_gfxdecode->gfx(1)->transpen(bitmap, cliprect, spritecode0, 0, 0, 0, m_spr_xy[0], m_spr_xy[1], 1);
334 	int spritecode1 = ((m_spr_ctrl[1] >> 1) & 7) + ((m_spr_ctrl[1] << 3) & 8);
335 	m_gfxdecode->gfx(1)->transpen(bitmap, cliprect, spritecode1 + 16, 0, 0, 0, m_spr_xy[2], m_spr_xy[3], 1);
336 	int spritecode2 = ((m_spr_ctrl[1] >> 5) & 7) + ((m_spr_ctrl[1] >> 1) & 8);
337 	m_gfxdecode->gfx(1)->transpen(bitmap, cliprect, spritecode2 + 32, 0, 0, 0, m_spr_xy[6], m_spr_xy[7], 1);
338 
339 	rect = cliprect;
340 	rect.set_height(45);
341 	bitmap.fill(0, rect);
342 
343 	rect = cliprect;
344 	rect.offset(32, 8);
345 	rect.set_size(16 * 8, 16);
346 	m_tilemap->draw(screen, bitmap, rect, 0, 0);
347 
348 	//printf("PL %03d:%03d %X SP1 %03d:%03d %X SP2 %03d:%03d %X Road %03d:%03d %01X %d\n", m_spr_xy[0], m_spr_xy[1], spritecode0, m_spr_xy[2], m_spr_xy[3], spritecode1, m_spr_xy[6], m_spr_xy[7], spritecode2, m_spr_xy[4], m_spr_xy[5], (m_spr_ctrl[0] >> 4) & 7, m_road_scroll);
349 	return 0;
350 }
351 
tileram_w(offs_t offset,uint8_t data)352 void istrebiteli_state::tileram_w(offs_t offset, uint8_t data)
353 {
354 	offset ^= 15;
355 	m_tileram[offset] = data;
356 	m_tilemap->mark_tile_dirty(offset);
357 }
358 
moto_tileram_w(offs_t offset,uint8_t data)359 void istrebiteli_state::moto_tileram_w(offs_t offset, uint8_t data)
360 {
361 	m_tileram[offset] = data ^ 0xff;
362 	m_tilemap->mark_tile_dirty(offset);
363 }
364 
road_ctrl_w(uint8_t data)365 void istrebiteli_state::road_ctrl_w(uint8_t data)
366 {
367 	m_road_scroll = data;
368 }
369 
ppi0_r(offs_t offset)370 uint8_t istrebiteli_state::ppi0_r(offs_t offset)
371 {
372 	return m_ppi0->read(offset ^ 3) ^ 0xff;
373 }
ppi0_w(offs_t offset,uint8_t data)374 void istrebiteli_state::ppi0_w(offs_t offset, uint8_t data)
375 {
376 	m_ppi0->write(offset ^ 3, data ^ 0xff);
377 }
ppi1_r(offs_t offset)378 uint8_t istrebiteli_state::ppi1_r(offs_t offset)
379 {
380 	return m_ppi1->read(offset ^ 3) ^ 0xff;
381 }
ppi1_w(offs_t offset,uint8_t data)382 void istrebiteli_state::ppi1_w(offs_t offset, uint8_t data)
383 {
384 	m_ppi1->write(offset ^ 3, data ^ 0xff);
385 }
386 
sound_w(uint8_t data)387 void istrebiteli_state::sound_w(uint8_t data)
388 {
389 	machine().bookkeeping().coin_lockout_w(0, data & 1);
390 	if (data & 1)
391 		coin_count = 0;
392 	m_sound_dev->sound_w(data);
393 }
394 
spr0_ctrl_w(uint8_t data)395 void istrebiteli_state::spr0_ctrl_w(uint8_t data)
396 {
397 	m_spr_ctrl[0] = data;
398 	if (data & 0x80)
399 		m_spr_collision[0] = 0;
400 }
401 
spr1_ctrl_w(uint8_t data)402 void istrebiteli_state::spr1_ctrl_w(uint8_t data)
403 {
404 	m_spr_ctrl[1] = data;
405 	if (data & 0x80)
406 		m_spr_collision[1] = 0;
407 }
408 
spr_xy_w(offs_t offset,uint8_t data)409 void istrebiteli_state::spr_xy_w(offs_t offset, uint8_t data)
410 {
411 	m_spr_xy[offset ^ 7] = data;
412 }
413 
moto_spr_xy_w(offs_t offset,uint8_t data)414 void istrebiteli_state::moto_spr_xy_w(offs_t offset, uint8_t data)
415 {
416 	m_spr_xy[offset] = data;
417 }
418 
mem_map(address_map & map)419 void istrebiteli_state::mem_map(address_map &map)
420 {
421 	map(0x0000, 0x0fff).rom();
422 	map(0x1000, 0x13ff).ram();
423 }
424 
moto_mem_map(address_map & map)425 void istrebiteli_state::moto_mem_map(address_map &map)
426 {
427 	map(0x0000, 0x1fff).rom();
428 	map(0x2000, 0x23ff).ram(); // KR537RU8 16Kbit SRAM, only half used ?
429 }
430 
io_map(address_map & map)431 void istrebiteli_state::io_map(address_map &map)
432 {
433 	map.global_mask(0xff);
434 	map.unmap_value_high();
435 	map(0xb0, 0xbf).w(FUNC(istrebiteli_state::tileram_w));
436 	map(0xc0, 0xc3).rw(FUNC(istrebiteli_state::ppi0_r), FUNC(istrebiteli_state::ppi0_w));
437 	map(0xc4, 0xc7).rw(FUNC(istrebiteli_state::ppi1_r), FUNC(istrebiteli_state::ppi1_w));
438 	map(0xc8, 0xcf).w(FUNC(istrebiteli_state::spr_xy_w));
439 }
440 
moto_io_map(address_map & map)441 void istrebiteli_state::moto_io_map(address_map &map)
442 {
443 	map.global_mask(0xff);
444 	map.unmap_value_high();
445 	map(0x30, 0x37).w(FUNC(istrebiteli_state::moto_spr_xy_w));
446 	map(0x38, 0x3b).rw(m_ppi0, FUNC(i8255_device::read), FUNC(i8255_device::write));
447 	map(0x3a, 0x3a).w(FUNC(istrebiteli_state::sound_w));
448 	map(0x3c, 0x3f).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write));
449 	map(0x40, 0x4f).w(FUNC(istrebiteli_state::moto_tileram_w));
450 }
451 
452 template <int ID>
READ_LINE_MEMBER(istrebiteli_state::collision_r)453 READ_LINE_MEMBER(istrebiteli_state::collision_r)
454 {
455 	// piece of HACK
456 	// real hardware does per-pixel sprite collision detection
457 
458 	if ((m_spr_ctrl[ID] & 0x80) == 0)
459 	{
460 		int sx = m_spr_xy[0 + ID * 2];
461 		int sy = m_spr_xy[1 + ID * 2];
462 		int px = m_spr_xy[6 - ID * 2] + 3;
463 		int py = m_spr_xy[7 - ID * 2] + 3;
464 
465 		if (sx > 56 && px >= sx && px < (sx + 8) && py >= sy && py < (sy + 8))
466 			m_spr_collision[ID] |= 1;
467 	}
468 	return m_spr_collision[ID];
469 }
470 
CUSTOM_INPUT_MEMBER(istrebiteli_state::coin_r)471 CUSTOM_INPUT_MEMBER(istrebiteli_state::coin_r)
472 {
473 	return coin_count;
474 }
475 
INPUT_CHANGED_MEMBER(istrebiteli_state::coin_inc)476 INPUT_CHANGED_MEMBER(istrebiteli_state::coin_inc)
477 {
478 	if (oldval == 0 && newval == 1)
479 		++coin_count;
480 }
481 
482 static INPUT_PORTS_START( istreb )
483 	PORT_START("IN0")
484 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
485 	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_PLAYER(1)
486 	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_PLAYER(1)
487 	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_PLAYER(1)
488 	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(1)
489 	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_MEMBER(istrebiteli_state, collision_r<1>)
490 	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
491 	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
492 
493 	PORT_START("IN1")
494 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
495 	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_PLAYER(2)
496 	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_PLAYER(2)
497 	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_PLAYER(2)
498 	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(2)
499 	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_MEMBER(istrebiteli_state, collision_r<0>)
500 	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
501 	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
502 
503 	PORT_START("IN2")
504 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_START1)
505 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_START2)
506 	PORT_BIT(0x3c, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(istrebiteli_state, coin_r)
507 	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_HBLANK("screen")
508 	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_VBLANK("screen")
509 
510 	PORT_START("COIN")
511 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN1 ) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, istrebiteli_state,coin_inc, 0)
512 INPUT_PORTS_END
513 
514 static INPUT_PORTS_START( moto )
515 	PORT_START("IN0")
516 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) // handle left
517 	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  // handle right
518 	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    // speed 30
519 	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  // speed 80
520 	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        // speed 120, 3 above bits encode 0 30 50 80 100 120 speeds
521 	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2)       // skip RAM test
522 	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3)        // handle full left/right
523 	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON4)        // brake
524 
525 	PORT_START("IN1")
526 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_START1)  // coin, TODO check why it is locked
527 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON5) // collision ?
528 	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_HBLANK("screen") // guess, seems unused
529 	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_VBLANK("screen")
530 INPUT_PORTS_END
531 
532 static const gfx_layout char_layout =
533 {
534 	8,16,
535 	32,
536 	1,
537 	{ 0 },
538 	{ 7,6,5,4,3,2,1,0 },
539 	{ 0*8,1*8,2*8,3*8,4*8,5*8,6*8,7*8,8*8,9*8,10*8,11*8,12*8,13*8,14*8,15*8 },
540 	8*16
541 };
542 
543 static const gfx_layout sprite_layout =
544 {
545 	8,8,
546 	64,
547 	1,
548 	{ 0 },
549 	{ 0,1,2,3,4,5,6,7 },
550 	{ 7*8,6*8,5*8,4*8,3*8,2*8,1*8,0*8 },
551 	8*8
552 };
553 
554 static const gfx_layout moto_sprite_layout =
555 {
556 	16,16,
557 	16*3,
558 	1,
559 	{ 0 },
560 	{ 8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7 },
561 	{ 15*16,14*16,13*16,12*16,11*16,10*16,9*16,8*16,7*16,6*16,5*16,4*16,3*16,2*16,1*16,0*16 },
562 	16*16
563 };
564 
565 static const gfx_layout projectile_layout =
566 {
567 	16,16,
568 	8,
569 	1,
570 	{ 0 },
571 	{ 15*8,14*8,13*8,12*8,11*8,10*8,9*8,8*8,7*8,6*8,5*8,4*8,3*8,2*8,1*8,0*8 },
572 	{ 15*128,14*128,13*128,12*128,11*128,10*128,9*128,8*128,7*128,6*128,5*128,4*128,3*128,2*128,1*128,0*128 },
573 	1
574 };
575 
576 static GFXDECODE_START( gfx_istrebiteli )
577 	GFXDECODE_ENTRY( "chars", 0x0000, char_layout, 0, 2 )
578 	GFXDECODE_ENTRY( "sprite", 0x0000, sprite_layout, 2, 2 )
579 	GFXDECODE_ENTRY( "sprite", 0x0000, sprite_layout, 0, 2 )
580 	GFXDECODE_ENTRY( "sprite", 0x0200, projectile_layout, 0, 2 )
581 GFXDECODE_END
582 
GFXDECODE_START(gfx_motogonki)583 static GFXDECODE_START( gfx_motogonki )
584 	GFXDECODE_ENTRY( "chars", 0x0000, char_layout, 2, 2 )
585 	GFXDECODE_ENTRY( "sprite", 0x0000, moto_sprite_layout, 2, 2 )
586 GFXDECODE_END
587 
588 void istrebiteli_state::istreb(machine_config &config)
589 {
590 	/* basic machine hardware */
591 	I8080(config, m_maincpu, XTAL(8'000'000) / 4);       // KR580VM80A
592 	m_maincpu->set_addrmap(AS_PROGRAM, &istrebiteli_state::mem_map);
593 	m_maincpu->set_addrmap(AS_IO, &istrebiteli_state::io_map);
594 
595 	i8255_device &ppi0(I8255A(config, "ppi0"));
596 	ppi0.in_pa_callback().set_ioport("IN1");
597 	ppi0.in_pb_callback().set_ioport("IN0");
598 	ppi0.out_pc_callback().set(FUNC(istrebiteli_state::sound_w));
599 
600 	i8255_device &ppi1(I8255A(config, "ppi1"));
601 	ppi1.out_pa_callback().set(FUNC(istrebiteli_state::spr0_ctrl_w));
602 	ppi1.out_pb_callback().set(FUNC(istrebiteli_state::spr1_ctrl_w));
603 	ppi1.in_pc_callback().set_ioport("IN2");
604 
605 	/* video hardware */
606 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
607 	screen.set_raw(XTAL(8'000'000) / 2, 256, 64, 256, 312, 0, 256);
608 	screen.set_screen_update(FUNC(istrebiteli_state::screen_update));
609 	screen.set_palette("palette");
610 
611 	GFXDECODE(config, m_gfxdecode, "palette", gfx_istrebiteli);
612 	PALETTE(config, "palette", FUNC(istrebiteli_state::istrebiteli_palette), 4);
613 
614 	/* sound hardware */
615 	SPEAKER(config, "mono").front_center();
616 	ISTREBITELI_SOUND(config, m_sound_dev, XTAL(8'000'000) / 2 / 256).add_route(ALL_OUTPUTS, "mono", 1.00);
617 }
618 
motogonki(machine_config & config)619 void istrebiteli_state::motogonki(machine_config &config)
620 {
621 	/* basic machine hardware */
622 	I8080(config, m_maincpu, XTAL(15'700'000) / 9);       // KR580VM80A
623 	m_maincpu->set_addrmap(AS_PROGRAM, &istrebiteli_state::moto_mem_map);
624 	m_maincpu->set_addrmap(AS_IO, &istrebiteli_state::moto_io_map);
625 
626 	i8255_device &ppi0(I8255A(config, "ppi0"));
627 	ppi0.out_pa_callback().set(FUNC(istrebiteli_state::spr0_ctrl_w));
628 	ppi0.out_pb_callback().set(FUNC(istrebiteli_state::spr1_ctrl_w));
629 
630 	i8255_device &ppi1(I8255A(config, "ppi1"));
631 	ppi1.in_pa_callback().set_ioport("IN0");
632 	ppi1.out_pb_callback().set(FUNC(istrebiteli_state::road_ctrl_w));
633 	ppi1.in_pc_callback().set_ioport("IN1");
634 
635 	/* video hardware */
636 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
637 	screen.set_raw(XTAL(8'000'000) / 2, 256, 64, 256, 312, 0, 256);
638 	screen.set_screen_update(FUNC(istrebiteli_state::moto_screen_update));
639 	screen.set_palette("palette");
640 
641 	GFXDECODE(config, m_gfxdecode, "palette", gfx_motogonki);
642 	PALETTE(config, "palette", FUNC(istrebiteli_state::motogonki_palette), 4);
643 	MCFG_VIDEO_START_OVERRIDE(istrebiteli_state, moto)
644 
645 	/* sound hardware */
646 	SPEAKER(config, "mono").front_center();
647 	ISTREBITELI_SOUND(config, m_sound_dev, XTAL(8'000'000) / 2 / 256).add_route(ALL_OUTPUTS, "mono", 1.00);
648 }
649 
650 ROM_START( istreb )
651 	ROM_REGION( 0x1000, I8080_TAG, ROMREGION_ERASEFF )
652 	ROM_LOAD( "002-ia12.bin",   0x000, 0x200, CRC(de0bce75) SHA1(ca284e8220d0d55c1a4dd3e951b53404f40fc873) )
653 	ROM_LOAD( "002-ia9.bin",    0x200, 0x200, CRC(e9a93ee7) SHA1(63c2001140d2b30657fceca97a639b1acbf614c2) )
654 	ROM_LOAD( "002-ib11-2.bin", 0x400, 0x200, CRC(4bb8b875) SHA1(230193e08586f4585fe98b2b31c4c8aa57a83e70) )
655 	ROM_LOAD( "002-ib9.bin",    0x600, 0x200, CRC(4eb948b5) SHA1(e9926591d1b0c528630b54956993c01139c58913) )
656 	ROM_LOAD( "002-ib13.bin",   0x800, 0x200, CRC(4fec5b14) SHA1(72b01c28882d567cad6924e05849438e5fe7a133) )
657 
658 	ROM_REGION( 0x200, "chars", 0 )
659 	ROM_LOAD( "003-g8.bin", 0x000, 0x200, CRC(5cd7ad47) SHA1(2142711c8a3640b7aa258a2059cfb0f14297a5ac) )
660 
661 	ROM_REGION( 0x1000, "sprite", 0 )
662 	ROM_LOAD( "001-g4.bin",  0x000, 0x200, CRC(ca3c531b) SHA1(8295167895d51e626b6d5946b565d5e8b8466ac0) )
663 	ROM_LOAD( "001-g9.bin",  0x000, 0x200, CRC(ca3c531b) SHA1(8295167895d51e626b6d5946b565d5e8b8466ac0) )
664 	ROM_LOAD( "001-a11.bin", 0x200, 0x100, CRC(4e05b7dd) SHA1(335e975ae9e8f775c1ac07f60420680ad878c3ae) )
665 	ROM_LOAD( "001-b11.bin", 0x200, 0x100, CRC(4e05b7dd) SHA1(335e975ae9e8f775c1ac07f60420680ad878c3ae) )
666 
667 	ROM_REGION(0x200, "soundrom", 0)
668 	ROM_LOAD( "003-w3.bin", 0x000, 0x200, CRC(54eb4893) SHA1(c7a4724045c645ab728074ed7fef1882d9776005) )
669 ROM_END
670 
671 // hardware is similar to Istrebiteli, but bigger ROM, RAM location moved, CPU A/D buses is not inverted unlike Istrebiteli PCB
672 // test mode: PPI1 port A bits 1,6,7 must be active low (currently left+btn3+btn4), then insert coin (press start)
673 ROM_START( motogonki )
674 	ROM_REGION( 0x2000, I8080_TAG, ROMREGION_ERASEFF )
675 	ROM_LOAD( "005_mb3.b2",   0x000, 0x2000, CRC(4dd35ed6) SHA1(6a0ee9e370634e501b6ee15a9747a491b745a205) )
676 
677 	ROM_REGION( 0x200, "chars", 0 )
678 	ROM_LOAD( "003_ig8.g8", 0x000, 0x200, CRC(9af1e9de) SHA1(4bc89bc0c1f229ca3ebee983ae2fb3910d8ca599) )
679 
680 	ROM_REGION( 0x1000, "sprite", 0 )
681 	ROM_LOAD( "006_b1.b1",  0x000, 0x200, CRC(ae9820fb) SHA1(7727d20e314aee670ba36ca6ea7ca5a4da0fc1cd) ) // player bike sprites, 16 16x16
682 	ROM_LOAD( "006_02.b5",  0x200, 0x200, CRC(e5c17daf) SHA1(1b6ffeba7dd98da11e5eb953280dd53f0f77fa7f) ) // opponents, road signs, etc sprites, 16 16x16
683 	ROM_LOAD( "006_03.b7",  0x400, 0x200, CRC(e1731d8d) SHA1(744fd768754a65a66bfcdb1959b4d6796bff4fcb) ) // more opponents, road signs, etc sprites, 16 16x16
684 
685 	ROM_REGION(0x200, "soundrom", 0)
686 	ROM_LOAD( "003_iw3.w3", 0x000, 0x200, CRC(814854ba) SHA1(2cbfd60df01f00d7659393efa58547de660bf201) )
687 
688 	ROM_REGION( 0x300, "proms", 0 ) // KR556RT4 256x4 ROMs
689 	ROM_LOAD( "006_05.b3",  0x000, 0x100, CRC(7dc4f9c9) SHA1(8a40f9f021b1662b1c638c7fdcefead1687ca4f1) ) // road tilemap ?
690 	ROM_LOAD( "006_01.d3",  0x100, 0x100, CRC(b53b83c9) SHA1(8f9733c827cc9aacc7c182585dcbc5da01357468) ) // sprite generators outputs combine prom
691 	ROM_LOAD( "006_04.w13", 0x200, 0x100, CRC(e43a500c) SHA1(c9a90b54587d0dc9d7d66c419790627088f2546e) ) // ports 30-37 address decoder prom
692 ROM_END
693 
694 GAME( 198?, istreb,    0, istreb,    istreb, istrebiteli_state, init_istreb, ROT0, "Terminal", "Istrebiteli", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
695 GAME( 198?, motogonki, 0, motogonki, moto,   istrebiteli_state, init_moto,   ROT0, "Terminal", "Motogonki", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
696