1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 #include "emu.h"
4 #include "k053250_ps.h"
5 #include "screen.h"
6 
7 /*
8 
9   Registers
10 
11   0
12     xxxx xxxx  X-Scroll [7:0]
13 
14   1
15     xxxx xxxx  X-Scroll [15:8]
16 
17   2
18     xxxx xxxx  Y-Scroll [7:0]
19 
20   3
21     xxxx xxxx  Y-Scroll [15:8]
22 
23   4              Control
24     .... ...x    0:Swap XY 1:Normal
25     .... ..x.    Interrupt related?
26     .... .x..    0:Disable 1:Enable source clipping
27     .... x...    Flip X
28     ...x ....    Flip Y
29     xxx. ....    Wrap control
30 
31   5
32     .... ....    Unknown
33 
34   6
35     xxxx xxxx    ROM access address [7:0]
36 
37   7
38     xxxx xxxx    ROM access address [15:8]
39  */
40 
41 DEFINE_DEVICE_TYPE(K053250PS, k053250ps_device, "k053250ps", "K053250PS LVC")
42 
k053250ps_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)43 k053250ps_device::k053250ps_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
44 	: device_t(mconfig, K053250PS, tag, owner, clock),
45 		device_gfx_interface(mconfig, *this),
46 		device_video_interface(mconfig, *this),
47 		m_dmairq_cb(*this),
48 		m_rom(*this, DEVICE_SELF)
49 {
50 }
51 
unpack_nibbles()52 void k053250ps_device::unpack_nibbles()
53 {
54 	m_unpacked_rom.resize(m_rom.length()*2);
55 
56 	for (int i = 0; i < m_rom.length(); i++)
57 	{
58 		m_unpacked_rom[2*i] = m_rom[i] >> 4;
59 		m_unpacked_rom[2*i+1] = m_rom[i] & 0xf;
60 	}
61 }
62 
device_start()63 void k053250ps_device::device_start()
64 {
65 	m_ram.resize(0x6000/2);
66 	m_buffer[0] = &m_ram[0x0000];
67 	m_buffer[1] = &m_ram[0x0800];
68 
69 //  m_buffer[0] = &m_ram[0x2000];
70 //  m_buffer[1] = &m_ram[0x2800];
71 
72 	unpack_nibbles();
73 
74 	save_item(NAME(m_ram));
75 	save_item(NAME(m_regs));
76 	save_item(NAME(m_page));
77 	save_item(NAME(m_dmairq_on));
78 
79 	m_dmairq_cb.resolve_safe();
80 	m_timer_lvcdma = timer_alloc(0);
81 }
82 
device_reset()83 void k053250ps_device::device_reset()
84 {
85 	m_page = 0;
86 	memset(m_regs, 0, sizeof(m_regs));
87 
88 	m_timer_lvcdma_state = OD_IDLE;
89 	m_timer_lvcdma->adjust(attotime::never);
90 
91 	m_dmairq_on = false;
92 }
93 
94 // utility function to render a clipped scanline vertically or horizontally
pdraw_scanline32(bitmap_rgb32 & bitmap,const pen_t * pal_base,uint8_t * source,const rectangle & cliprect,int linepos,int scroll,int zoom,uint32_t clipmask,uint32_t wrapmask,uint32_t orientation,bitmap_ind8 & priority,uint8_t pri)95 inline void k053250ps_device::pdraw_scanline32(bitmap_rgb32 &bitmap, const pen_t *pal_base, uint8_t *source,
96 										const rectangle &cliprect, int linepos, int scroll, int zoom,
97 										uint32_t clipmask, uint32_t wrapmask, uint32_t orientation, bitmap_ind8 &priority, uint8_t pri)
98 {
99 // a sixteen-bit fixed point resolution should be adequate to our application
100 #define FIXPOINT_PRECISION      16
101 #define FIXPOINT_PRECISION_HALF (1<<(FIXPOINT_PRECISION-1))
102 
103 	int end_pixel, flip, dst_min, dst_max, dst_start, dst_length;
104 
105 	uint32_t src_wrapmask;
106 	uint8_t  *src_base;
107 	int src_fx, src_fdx;
108 	int pix_data, dst_offset;
109 	uint8_t  *pri_base;
110 	uint32_t *dst_base;
111 	int dst_adv;
112 
113 	// flip X and flip Y also switch role when the X Y coordinates are swapped
114 	if (!(orientation & ORIENTATION_SWAP_XY))
115 	{
116 		flip = orientation & ORIENTATION_FLIP_X;
117 		dst_min = cliprect.min_x;
118 		dst_max = cliprect.max_x;
119 	}
120 	else
121 	{
122 		flip = orientation & ORIENTATION_FLIP_Y;
123 		dst_min = cliprect.min_y;
124 		dst_max = cliprect.max_y;
125 	}
126 
127 	if (clipmask)
128 	{
129 		// reject scanlines that are outside of the target bitmap's right(bottom) clip boundary
130 		dst_start = -scroll;
131 		if (dst_start > dst_max) return;
132 
133 		// calculate target length
134 		dst_length = clipmask + 1;
135 		if (zoom) dst_length = (dst_length << 6) / zoom;
136 
137 		// reject scanlines that are outside of the target bitmap's left(top) clip boundary
138 		end_pixel = dst_start + dst_length - 1;
139 		if (end_pixel < dst_min) return;
140 
141 		// clip scanline tail
142 		if ((end_pixel -= dst_max) > 0) dst_length -= end_pixel;
143 
144 		// reject 0-length scanlines
145 		if (dst_length <= 0) return;
146 
147 		// calculate zoom factor
148 		src_fdx = zoom << (FIXPOINT_PRECISION-6);
149 
150 		// clip scanline head
151 		end_pixel = dst_min;
152 		if ((end_pixel -= dst_start) > 0)
153 		{
154 			// chop scanline to the correct length and move target start location to the left(top) clip boundary
155 			dst_length -= end_pixel;
156 			dst_start = dst_min;
157 
158 			// and skip the source for the left(top) clip region
159 			src_fx = end_pixel * src_fdx + FIXPOINT_PRECISION_HALF;
160 		}
161 		else
162 			// the point five bias is to ensure even distribution of stretched or shrinked pixels
163 			src_fx = FIXPOINT_PRECISION_HALF;
164 
165 		// adjust flipped source
166 		if (flip)
167 		{
168 			// start from the target's clipped end if the scanline is flipped
169 			dst_start = dst_max + dst_min - dst_start - (dst_length-1);
170 
171 			// and move source start location to the opposite end
172 			src_fx += (dst_length-1) * src_fdx - 1;
173 			src_fdx = -src_fdx;
174 		}
175 	}
176 	else
177 	{
178 		// draw wrapped scanline at virtual bitmap boundary when source clipping is off
179 		dst_start = dst_min;
180 		dst_length = dst_max - dst_min + 1; // target scanline spans the entire visible area
181 		src_fdx = zoom << (FIXPOINT_PRECISION-6);
182 
183 		// pre-advance source for the clipped region
184 		if (!flip)
185 			src_fx = (scroll + dst_min) * src_fdx + FIXPOINT_PRECISION_HALF;
186 		else
187 		{
188 			src_fx = (scroll + dst_max) * src_fdx + FIXPOINT_PRECISION_HALF-1;
189 			src_fdx = -src_fdx;
190 		}
191 	}
192 
193 	if (!(orientation & ORIENTATION_SWAP_XY))
194 	{
195 		// calculate target increment for horizontal scanlines which is exactly one
196 		dst_adv = 1;
197 		dst_offset = dst_length;
198 		pri_base = &priority.pix(linepos, dst_start + dst_offset);
199 		dst_base = &bitmap.pix(linepos, dst_start + dst_length);
200 	}
201 	else
202 	{
203 		// calculate target increment for vertical scanlines which is the bitmap's pitch value
204 		dst_adv = bitmap.rowpixels();
205 		dst_offset= dst_length * dst_adv;
206 		pri_base = &priority.pix(dst_start, linepos + dst_offset);
207 		dst_base = &bitmap.pix(dst_start, linepos + dst_offset);
208 	}
209 
210 	// generalized
211 	src_base = source;
212 
213 	// there is no need to wrap source offsets along with source clipping
214 	// so we set all bits of the wrapmask to one
215 	src_wrapmask = (clipmask) ? ~0 : wrapmask;
216 
217 	dst_offset = -dst_offset; // negate target offset in order to terminated draw loop at 0 condition
218 
219 	if (pri)
220 	{
221 		// draw scanline and update priority bitmap
222 		do
223 		{
224 			pix_data = src_base[(src_fx>>FIXPOINT_PRECISION) & src_wrapmask];
225 			src_fx += src_fdx;
226 
227 			if (pix_data)
228 			{
229 				pix_data = pal_base[pix_data];
230 				pri_base[dst_offset] = pri;
231 				dst_base[dst_offset] = pix_data;
232 			}
233 		}
234 		while (dst_offset += dst_adv);
235 	}
236 	else
237 	{
238 		// draw scanline but do not update priority bitmap
239 		do
240 		{
241 			pix_data = src_base[(src_fx>>FIXPOINT_PRECISION) & src_wrapmask];
242 			src_fx += src_fdx;
243 
244 			if (pix_data)
245 			{
246 				dst_base[dst_offset] = pal_base[pix_data];
247 			}
248 		}
249 		while (dst_offset += dst_adv);
250 	}
251 
252 #undef FIXPOINT_PRECISION
253 #undef FIXPOINT_PRECISION_HALF
254 }
255 
draw(bitmap_rgb32 & bitmap,const rectangle & cliprect,int colorbase,int flags,bitmap_ind8 & priority_bitmap,int priority)256 void k053250ps_device::draw( bitmap_rgb32 &bitmap, const rectangle &cliprect, int colorbase, int flags, bitmap_ind8 &priority_bitmap, int priority )
257 {
258 	static int16_t scroll_x;
259 
260 	if (machine().input().code_pressed(KEYCODE_A))
261 	{
262 		scroll_x--;
263 		popmessage("SCROLL: %d\n", scroll_x);
264 	}
265 
266 	else if (machine().input().code_pressed(KEYCODE_S))
267 	{
268 		scroll_x++;
269 		popmessage("SCROLL: %d\n", scroll_x);
270 	}
271 
272 	uint8_t *pix_ptr;
273 	const pen_t *pal_base, *pal_ptr;
274 	uint32_t src_clipmask, src_wrapmask; //, dst_wrapmask;
275 	int linedata_offs, line_pos, line_start, line_end, scroll_corr;
276 	int color, offset, zoom, scroll, passes, i;
277 	bool wrap500 = false;
278 
279 	uint16_t *line_ram = m_buffer[0];                // pointer to physical line RAM
280 	int map_scrollx = short(m_regs[0] << 8 | m_regs[1]) - m_offx; // signed horizontal scroll value
281 	int map_scrolly = short(m_regs[2] << 8 | m_regs[3]) - m_offy; // signed vertical scroll value
282 	uint8_t ctrl = m_regs[4];                                   // register four is the main control register
283 
284 	// copy visible boundary values to more accessible locations
285 	int dst_minx  = cliprect.min_x;
286 	int dst_maxx  = cliprect.max_x;
287 	int dst_miny  = cliprect.min_y;
288 	int dst_maxy  = cliprect.max_y;
289 
290 	int orientation  = 0;   // orientation defaults to no swapping and no flipping
291 	int dst_height   = 512; // virtual bitmap height defaults to 512 pixels
292 	int linedata_adv = 4;   // line info packets are four words(eight bytes) apart
293 
294 	// switch X and Y parameters when the first bit of the control register is cleared
295 	if (!(ctrl & 0x01)) orientation |= ORIENTATION_SWAP_XY;
296 
297 	// invert X parameters when the forth bit of the control register is set
298 	if   (ctrl & 0x08)  orientation |= ORIENTATION_FLIP_X;
299 
300 	// invert Y parameters when the fifth bit of the control register is set
301 	if   (ctrl & 0x10)  orientation |= ORIENTATION_FLIP_Y;
302 
303 	//printf("CTRL: %x\n", ctrl);
304 
305 	// 00
306 	// 0x12
307 	// 0x10
308 
309 	switch (ctrl >> 5) // the upper four bits of the control register select source and target dimensions
310 	{
311 		case 0 :
312 			// PirateSH
313 			// Xexex: L6 galaxies
314 			// Metam: L4 forest, L5 arena, L6 tower interior, final boss
315 
316 			// crop source offset between 0 and 255 inclusive,
317 			// and set virtual bitmap height to 256 pixels
318 			src_wrapmask = src_clipmask = 0xff;
319 			dst_height = 0x100;
320 		break;
321 		case 1 :
322 			// Xexex: prologue, L7 nebulae
323 
324 			// the source offset is cropped to 0 and 511 inclusive
325 			src_wrapmask = src_clipmask = 0x1ff;
326 		break;
327 		case 4 :
328 			// Xexex: L1 sky and boss, L3 planet, L5 poly-face, L7 battle ship patches
329 			// Metam: L1 summoning circle, L3 caves, L6 gargoyle towers
330 
331 			// crop source offset between 0 and 255 inclusive,
332 			// and allow source offset to wrap back at 0x500 to -0x300
333 			src_wrapmask = src_clipmask = 0xff;
334 			wrap500 = true;
335 		break;
336 //      case 2 : // Xexex: title
337 //      case 7 : // Xexex: L4 organic stage
338 		default:
339 			// crop source offset between 0 and 1023 inclusive,
340 			// keep other dimensions to their defaults
341 			src_wrapmask = src_clipmask = 0x3ff;
342 		break;
343 	}
344 
345 	// disable source clipping when the third bit of the control register is set
346 	if (ctrl & 0x04)
347 		src_clipmask = 0;
348 
349 	if (!(orientation & ORIENTATION_SWAP_XY))   // normal orientaion with no X Y switching
350 	{
351 		line_start = dst_miny;          // the first scanline starts at the minimum Y clip location
352 		line_end   = dst_maxy;          // the last scanline ends at the maximum Y clip location
353 		scroll_corr = map_scrollx;      // concentrate global X scroll
354 		linedata_offs = map_scrolly;    // determine where to get info for the first line
355 
356 		if (orientation & ORIENTATION_FLIP_X)
357 		{
358 			scroll_corr = -scroll_corr; // X scroll adjustment should be negated in X flipped scenarioes
359 		}
360 
361 		if (orientation & ORIENTATION_FLIP_Y)
362 		{
363 			linedata_adv = -linedata_adv;           // traverse line RAM backward in Y flipped scenarioes
364 			linedata_offs += bitmap.height() - 1;   // and get info for the first line from the bottom
365 		}
366 
367 //      dst_wrapmask = ~0;  // scanlines don't seem to wrap horizontally in normal orientation
368 		passes = 1;         // draw scanline in a single pass
369 	}
370 	else  // orientaion with X and Y parameters switched
371 	{
372 		line_start = dst_minx;          // the first scanline starts at the minimum X clip location
373 		line_end   = dst_maxx;          // the last scanline ends at the maximum X clip location
374 		scroll_corr = map_scrolly;      // concentrate global Y scroll
375 		linedata_offs = map_scrollx;    // determine where to get info for the first line
376 
377 		if (orientation & ORIENTATION_FLIP_Y)
378 		{
379 			scroll_corr = 0x100 - scroll_corr;  // apply common vertical correction
380 
381 			// Y correction (ref: 1st and 5th boss)
382 			scroll_corr -= 2;   // apply unique vertical correction
383 
384 			// X correction (ref: 1st boss, seems to undo non-rotated global X offset)
385 			linedata_offs -= 5; // apply unique horizontal correction
386 		}
387 
388 		if (orientation & ORIENTATION_FLIP_X)
389 		{
390 			linedata_adv = -linedata_adv;       // traverse line RAM backward in X flipped scenarioes
391 			linedata_offs += bitmap.width() - 1;    // and get info for the first line from the bottom
392 		}
393 
394 		if (src_clipmask)
395 		{
396 			// determine target wrap boundary and draw scanline in two passes if the source is clipped
397 //          dst_wrapmask = dst_height - 1;
398 			passes = 2;
399 		}
400 		else
401 		{
402 			// otherwise disable target wraparound and draw scanline in a single pass
403 //          dst_wrapmask = ~0;
404 			passes = 1;
405 		}
406 	}
407 
408 	// PJB - HOW IS THIS DETERMINED?
409 	src_clipmask = 0;
410 
411 	linedata_offs *= 4;                             // each line info packet has four words(eight bytes)
412 	linedata_offs &= 0x7ff;                         // and it should wrap at the four-kilobyte boundary
413 	linedata_offs += line_start * linedata_adv;     // pre-advance line info offset for the clipped region
414 
415 	// load physical palette base
416 	pal_base = palette().pens() + (colorbase << 4) % palette().entries();
417 
418 	//printf("Line Start: %u  Line End: %u  Advance: %u\n", line_start, line_end, linedata_adv);
419 
420 
421 	// walk the target bitmap within the visible area vertically or horizontally, one line at a time
422 	for (line_pos=line_start; line_pos <= line_end; linedata_offs += linedata_adv, line_pos++)
423 	{
424 		linedata_offs &= 0x7ff;                     // line info data wraps at the four-kilobyte boundary
425 
426 		color  = line_ram[linedata_offs];           // get scanline color code
427 		if (color == 0xffff) continue;              // reject scanline if color code equals minus one
428 
429 		offset   = line_ram[linedata_offs + 1];     // get first pixel offset in ROM
430 		if (!(color & 0xff) && !offset) continue;   // reject scanline if both color and pixel offset are 0
431 
432 		// calculate physical palette location
433 		// there can be thirty-two color codes and each code represents sixteen pens
434 		pal_ptr = pal_base + ((color & 0x1f) << 4);
435 
436 		// calculate physical pixel location
437 		// each offset unit represents 256 pixels and should wrap at ROM boundary for safety
438 		pix_ptr = &m_unpacked_rom[((offset << 8) % m_unpacked_rom.size())];
439 
440 		// get scanline zoom factor
441 		// For example, 0x20 doubles the length, 0x40 maintains a one-to-one length,
442 		// and 0x80 halves the length. The zoom center is at the beginning of the
443 		// scanline therefore it is not necessary to adjust render start position
444 		zoom    = line_ram[linedata_offs + 2];
445 
446 		scroll = ((short)line_ram[linedata_offs + 3]);   // get signed local scroll value for the current scanline
447 
448 		// scavenged from old code; improves Xexex' first level sky
449 		if (wrap500 && scroll >= 0x500) scroll -= 0x800;
450 
451 		if (1 && scroll >= 0x500) scroll -= 0x800;
452 
453 		scroll += scroll_corr;  // apply final scroll correction
454 		// PJB
455 		//scroll &= src_wrapmask; // wraparound scroll value if necessary
456 
457 		if (0)
458 		{
459 			printf("%u: [%x] COLR:%x OFFS:%x ZOOM:%x SCRL:%d (%.4x)\n", line_pos, linedata_offs, color, offset, zoom, scroll, line_ram[linedata_offs + 3]);
460 		}
461 
462 
463 		// draw scanlines wrapped at virtual bitmap boundary in two passes
464 		// this should not impose too much overhead due to clipping performed by the render code
465 		i = passes;
466 		do
467 		{
468 			/*
469 			    Parameter descriptions:
470 
471 			    bitmap       : pointer to a MAME bitmap as the render target
472 			    pal_ptr      : pointer to the palette's physical location relative to the scanline
473 			    pix_ptr      : pointer to the physical start location of source pixels in ROM
474 			    cliprect     : pointer to a rectangle structue which describes the visible area of the target bitmap
475 			    line_pos     : scanline render position relative to the target bitmap
476 			                   should be a Y offset to the target bitmap in normal orientaion,
477 			                   or an X offset to the target bitmap if X,Y are swapped
478 			    scroll       : source scroll value of the scanline
479 			    zoom         : source zoom factor of the scanline
480 			    src_clipmask : source offset clip mask; source pixels with offsets beyond the scope of this mask will not be drawn
481 			    src_wrapmask : source offset wrap mask; wraps source offset around, no effect when src_clipmask is set
482 			    orientation  : flags indicating whether scanlines should be drawn horizontally, vertically, forward or backward
483 			    priority     : value to be written to the priority bitmap, no effect when equals 0
484 			*/
485 			pdraw_scanline32(bitmap, pal_ptr, pix_ptr, cliprect,
486 				line_pos, scroll, zoom, src_clipmask, src_wrapmask, orientation, priority_bitmap, (uint8_t)priority);
487 
488 			// shift scanline position one virtual screen upward to render the wrapped end if necessary
489 			scroll -= dst_height;
490 		}
491 		while (--i);
492 	}
493 }
494 
reg_r(offs_t offset)495 uint16_t k053250ps_device::reg_r(offs_t offset)
496 {
497 	return m_regs[offset];
498 }
499 
500 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)501 void k053250ps_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
502 {
503 	switch(m_timer_lvcdma_state)
504 	{
505 		case OD_WAIT_START:
506 			m_timer_lvcdma_state = OD_WAIT_END;
507 			m_timer_lvcdma->adjust(attotime::from_ticks(4096, clock()));
508 			m_dmairq_cb(ASSERT_LINE);
509 
510 //          memcpy(m_buffer[m_page], &m_ram[0], 0x1000);
511 //          m_page ^= 1;
512 
513 			break;
514 
515 		case OD_WAIT_END:
516 			m_timer_lvcdma_state = OD_IDLE;
517 			m_timer_lvcdma->adjust(attotime::never);
518 			m_dmairq_cb(CLEAR_LINE);
519 
520 			if(/*(m_regs[4] & 0x02) &&*/ !m_dmairq_on)
521 			{
522 				m_dmairq_on = true;
523 				m_dmairq_cb(ASSERT_LINE);
524 			}
525 			break;
526 	}
527 }
528 
WRITE_LINE_MEMBER(k053250ps_device::vblank_w)529 WRITE_LINE_MEMBER(k053250ps_device::vblank_w)
530 {
531 	if (state == 1)
532 	{
533 		if (1)
534 		{
535 			if(m_dmairq_on)
536 			{
537 				m_dmairq_on = false;
538 				m_dmairq_cb(CLEAR_LINE);
539 			}
540 			m_timer_lvcdma_state = OD_WAIT_START;
541 			m_timer_lvcdma->adjust(attotime::from_ticks(256, clock()));
542 		}
543 		else
544 		{
545 			//memset(m_sram, 0, sizeof(m_sram));
546 		}
547 	}
548 }
549 
READ_LINE_MEMBER(k053250ps_device::dmairq_r)550 READ_LINE_MEMBER(k053250ps_device::dmairq_r)
551 {
552 	return !m_dmairq_on;
553 }
554 
555 
reg_w(offs_t offset,uint16_t data,uint16_t mem_mask)556 void k053250ps_device::reg_w(offs_t offset, uint16_t data, uint16_t mem_mask)
557 {
558 #if 0
559 	static char const *const regnames[] =
560 	{
561 		"SCROLL_X_L",
562 		"SCROLL_X_H",
563 		"SCROLL_Y_L",
564 		"SCROLL_X_H",
565 		"DMA Control",
566 		"UNKNOWN",
567 		"ROM_H",
568 		"ROM_L",
569 	};
570 
571 	printf("053250 REG_W[%x] (%s): %.2x\n", offset, regnames[offset], data & 0xff);
572 #endif
573 	if (ACCESSING_BITS_0_7)
574 		m_regs[offset] = data;
575 }
576 
ram_r(offs_t offset)577 uint16_t k053250ps_device::ram_r(offs_t offset)
578 {
579 	return m_ram[offset];
580 }
581 
ram_w(offs_t offset,uint16_t data,uint16_t mem_mask)582 void k053250ps_device::ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
583 {
584 	COMBINE_DATA(&m_ram[offset]);
585 }
586 
rom_r(offs_t offset)587 uint16_t k053250ps_device::rom_r(offs_t offset)
588 {
589 	return m_rom[0x80000 * m_regs[6] + 0x800 * m_regs[7] + offset/2];
590 }
591