1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 #include "emu.h"
4 #include "k053250.h"
5 #include "screen.h"
6 
7 
8 DEFINE_DEVICE_TYPE(K053250, k053250_device, "k053250", "K053250 LVC")
9 
k053250_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)10 k053250_device::k053250_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
11 	: device_t(mconfig, K053250, tag, owner, clock),
12 		device_gfx_interface(mconfig, *this),
13 		device_video_interface(mconfig, *this),
14 		m_rom(*this, DEVICE_SELF)
15 {
16 }
17 
unpack_nibbles()18 void k053250_device::unpack_nibbles()
19 {
20 	m_unpacked_rom.resize(m_rom.length()*2);
21 
22 	for (int i = 0; i < m_rom.length(); i++)
23 	{
24 		m_unpacked_rom[2*i] = m_rom[i] >> 4;
25 		m_unpacked_rom[2*i+1] = m_rom[i] & 0xf;
26 	}
27 }
28 
device_start()29 void k053250_device::device_start()
30 {
31 	m_ram.resize(0x6000/2);
32 	m_buffer[0] = &m_ram[0x2000];
33 	m_buffer[1] = &m_ram[0x2800];
34 
35 	unpack_nibbles();
36 
37 	save_item(NAME(m_ram));
38 	save_item(NAME(m_regs));
39 	save_item(NAME(m_page));
40 	save_item(NAME(m_frame));
41 }
42 
device_reset()43 void k053250_device::device_reset()
44 {
45 	m_page = 0;
46 	m_frame = -1;
47 	memset(m_regs, 0, sizeof(m_regs));
48 }
49 
50 // 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)51 inline void k053250_device::pdraw_scanline32(bitmap_rgb32 &bitmap, const pen_t *pal_base, uint8_t *source,
52 										const rectangle &cliprect, int linepos, int scroll, int zoom,
53 										uint32_t clipmask, uint32_t wrapmask, uint32_t orientation, bitmap_ind8 &priority, uint8_t pri)
54 {
55 // a sixteen-bit fixed point resolution should be adequate to our application
56 #define FIXPOINT_PRECISION      16
57 #define FIXPOINT_PRECISION_HALF (1<<(FIXPOINT_PRECISION-1))
58 
59 	int end_pixel, flip, dst_min, dst_max, dst_start, dst_length;
60 
61 	uint32_t src_wrapmask;
62 	uint8_t  *src_base;
63 	int src_fx, src_fdx;
64 	int pix_data, dst_offset;
65 	uint8_t  *pri_base;
66 	uint32_t *dst_base;
67 	int dst_adv;
68 
69 	// flip X and flip Y also switch role when the X Y coordinates are swapped
70 	if (!(orientation & ORIENTATION_SWAP_XY))
71 	{
72 		flip = orientation & ORIENTATION_FLIP_X;
73 		dst_min = cliprect.min_x;
74 		dst_max = cliprect.max_x;
75 	}
76 	else
77 	{
78 		flip = orientation & ORIENTATION_FLIP_Y;
79 		dst_min = cliprect.min_y;
80 		dst_max = cliprect.max_y;
81 	}
82 
83 	if (clipmask)
84 	{
85 		// reject scanlines that are outside of the target bitmap's right(bottom) clip boundary
86 		dst_start = -scroll;
87 		if (dst_start > dst_max) return;
88 
89 		// calculate target length
90 		dst_length = clipmask + 1;
91 		if (zoom) dst_length = (dst_length << 6) / zoom;
92 
93 		// reject scanlines that are outside of the target bitmap's left(top) clip boundary
94 		end_pixel = dst_start + dst_length - 1;
95 		if (end_pixel < dst_min) return;
96 
97 		// clip scanline tail
98 		if ((end_pixel -= dst_max) > 0) dst_length -= end_pixel;
99 
100 		// reject 0-length scanlines
101 		if (dst_length <= 0) return;
102 
103 		// calculate zoom factor
104 		src_fdx = zoom << (FIXPOINT_PRECISION-6);
105 
106 		// clip scanline head
107 		end_pixel = dst_min;
108 		if ((end_pixel -= dst_start) > 0)
109 		{
110 			// chop scanline to the correct length and move target start location to the left(top) clip boundary
111 			dst_length -= end_pixel;
112 			dst_start = dst_min;
113 
114 			// and skip the source for the left(top) clip region
115 			src_fx = end_pixel * src_fdx + FIXPOINT_PRECISION_HALF;
116 		}
117 		else
118 			// the point five bias is to ensure even distribution of stretched or shrinked pixels
119 			src_fx = FIXPOINT_PRECISION_HALF;
120 
121 		// adjust flipped source
122 		if (flip)
123 		{
124 			// start from the target's clipped end if the scanline is flipped
125 			dst_start = dst_max + dst_min - dst_start - (dst_length-1);
126 
127 			// and move source start location to the opposite end
128 			src_fx += (dst_length-1) * src_fdx - 1;
129 			src_fdx = -src_fdx;
130 		}
131 	}
132 	else
133 	{
134 		// draw wrapped scanline at virtual bitmap boundary when source clipping is off
135 		dst_start = dst_min;
136 		dst_length = dst_max - dst_min + 1; // target scanline spans the entire visible area
137 		src_fdx = zoom << (FIXPOINT_PRECISION-6);
138 
139 		// pre-advance source for the clipped region
140 		if (!flip)
141 			src_fx = (scroll + dst_min) * src_fdx + FIXPOINT_PRECISION_HALF;
142 		else
143 		{
144 			src_fx = (scroll + dst_max) * src_fdx + FIXPOINT_PRECISION_HALF-1;
145 			src_fdx = -src_fdx;
146 		}
147 	}
148 
149 	if (!(orientation & ORIENTATION_SWAP_XY))
150 	{
151 		// calculate target increment for horizontal scanlines which is exactly one
152 		dst_adv = 1;
153 		dst_offset = dst_length;
154 		pri_base = &priority.pix(linepos, dst_start + dst_offset);
155 		dst_base = &bitmap.pix(linepos, dst_start + dst_length);
156 	}
157 	else
158 	{
159 		// calculate target increment for vertical scanlines which is the bitmap's pitch value
160 		dst_adv = bitmap.rowpixels();
161 		dst_offset= dst_length * dst_adv;
162 		pri_base = &priority.pix(dst_start, linepos + dst_offset);
163 		dst_base = &bitmap.pix(dst_start, linepos + dst_offset);
164 	}
165 
166 	// generalized
167 	src_base = source;
168 
169 	// there is no need to wrap source offsets along with source clipping
170 	// so we set all bits of the wrapmask to one
171 	src_wrapmask = (clipmask) ? ~0 : wrapmask;
172 
173 	dst_offset = -dst_offset; // negate target offset in order to terminated draw loop at 0 condition
174 
175 	if (pri)
176 	{
177 		// draw scanline and update priority bitmap
178 		do
179 		{
180 			pix_data = src_base[(src_fx>>FIXPOINT_PRECISION) & src_wrapmask];
181 			src_fx += src_fdx;
182 
183 			if (pix_data)
184 			{
185 				pix_data = pal_base[pix_data];
186 				pri_base[dst_offset] = pri;
187 				dst_base[dst_offset] = pix_data;
188 			}
189 		}
190 		while (dst_offset += dst_adv);
191 	}
192 	else
193 	{
194 		// draw scanline but do not update priority bitmap
195 		do
196 		{
197 			pix_data = src_base[(src_fx>>FIXPOINT_PRECISION) & src_wrapmask];
198 			src_fx += src_fdx;
199 
200 			if (pix_data)
201 			{
202 				dst_base[dst_offset] = pal_base[pix_data];
203 			}
204 		}
205 		while (dst_offset += dst_adv);
206 	}
207 
208 #undef FIXPOINT_PRECISION
209 #undef FIXPOINT_PRECISION_HALF
210 }
211 
draw(bitmap_rgb32 & bitmap,const rectangle & cliprect,int colorbase,int flags,bitmap_ind8 & priority_bitmap,int priority)212 void k053250_device::draw( bitmap_rgb32 &bitmap, const rectangle &cliprect, int colorbase, int flags, bitmap_ind8 &priority_bitmap, int priority )
213 {
214 	uint8_t *pix_ptr;
215 	const pen_t *pal_base, *pal_ptr;
216 	uint32_t src_clipmask, src_wrapmask, dst_wrapmask;
217 	int linedata_offs, line_pos, line_start, line_end, scroll_corr;
218 	int color, offset, zoom, scroll, passes, i;
219 	bool wrap500 = false;
220 
221 	uint16_t *line_ram = m_buffer[m_page];                // pointer to physical line RAM
222 	int map_scrollx = short(m_regs[0] << 8 | m_regs[1]) - m_offx; // signed horizontal scroll value
223 	int map_scrolly = short(m_regs[2] << 8 | m_regs[3]) - m_offy; // signed vertical scroll value
224 	uint8_t ctrl = m_regs[4];                                   // register four is the main control register
225 
226 	// copy visible boundary values to more accessible locations
227 	int dst_minx  = cliprect.min_x;
228 	int dst_maxx  = cliprect.max_x;
229 	int dst_miny  = cliprect.min_y;
230 	int dst_maxy  = cliprect.max_y;
231 
232 	int orientation  = 0;   // orientation defaults to no swapping and no flipping
233 	int dst_height   = 512; // virtual bitmap height defaults to 512 pixels
234 	int linedata_adv = 4;   // line info packets are four words(eight bytes) apart
235 
236 	// switch X and Y parameters when the first bit of the control register is cleared
237 	if (!(ctrl & 0x01)) orientation |= ORIENTATION_SWAP_XY;
238 
239 	// invert X parameters when the forth bit of the control register is set
240 	if   (ctrl & 0x08)  orientation |= ORIENTATION_FLIP_X;
241 
242 	// invert Y parameters when the fifth bit of the control register is set
243 	if   (ctrl & 0x10)  orientation |= ORIENTATION_FLIP_Y;
244 
245 	switch (ctrl >> 5) // the upper four bits of the control register select source and target dimensions
246 	{
247 		case 0 :
248 			// Xexex: L6 galaxies
249 			// Metam: L4 forest, L5 arena, L6 tower interior, final boss
250 
251 			// crop source offset between 0 and 255 inclusive,
252 			// and set virtual bitmap height to 256 pixels
253 			src_wrapmask = src_clipmask = 0xff;
254 			dst_height = 0x100;
255 		break;
256 		case 1 :
257 			// Xexex: prologue, L7 nebulae
258 
259 			// the source offset is cropped to 0 and 511 inclusive
260 			src_wrapmask = src_clipmask = 0x1ff;
261 		break;
262 		case 4 :
263 			// Xexex: L1 sky and boss, L3 planet, L5 poly-face, L7 battle ship patches
264 			// Metam: L1 summoning circle, L3 caves, L6 gargoyle towers
265 
266 			// crop source offset between 0 and 255 inclusive,
267 			// and allow source offset to wrap back at 0x500 to -0x300
268 			src_wrapmask = src_clipmask = 0xff;
269 			wrap500 = true;
270 		break;
271 //      case 2 : // Xexex: title
272 //      case 7 : // Xexex: L4 organic stage
273 		default:
274 			// crop source offset between 0 and 1023 inclusive,
275 			// keep other dimensions to their defaults
276 			src_wrapmask = src_clipmask = 0x3ff;
277 		break;
278 	}
279 
280 	// disable source clipping when the third bit of the control register is set
281 	if (ctrl & 0x04) src_clipmask = 0;
282 
283 	if (!(orientation & ORIENTATION_SWAP_XY))   // normal orientaion with no X Y switching
284 	{
285 		line_start = dst_miny;          // the first scanline starts at the minimum Y clip location
286 		line_end   = dst_maxy;          // the last scanline ends at the maximum Y clip location
287 		scroll_corr = map_scrollx;      // concentrate global X scroll
288 		linedata_offs = map_scrolly;    // determine where to get info for the first line
289 
290 		if (orientation & ORIENTATION_FLIP_X)
291 		{
292 			scroll_corr = -scroll_corr; // X scroll adjustment should be negated in X flipped scenarioes
293 		}
294 
295 		if (orientation & ORIENTATION_FLIP_Y)
296 		{
297 			linedata_adv = -linedata_adv;           // traverse line RAM backward in Y flipped scenarioes
298 			linedata_offs += bitmap.height() - 1;   // and get info for the first line from the bottom
299 		}
300 
301 		dst_wrapmask = ~0;  // scanlines don't seem to wrap horizontally in normal orientation
302 		passes = 1;         // draw scanline in a single pass
303 	}
304 	else  // orientaion with X and Y parameters switched
305 	{
306 		line_start = dst_minx;          // the first scanline starts at the minimum X clip location
307 		line_end   = dst_maxx;          // the last scanline ends at the maximum X clip location
308 		scroll_corr = map_scrolly;      // concentrate global Y scroll
309 		linedata_offs = map_scrollx;    // determine where to get info for the first line
310 
311 		if (orientation & ORIENTATION_FLIP_Y)
312 		{
313 			scroll_corr = 0x100 - scroll_corr;  // apply common vertical correction
314 
315 			// Y correction (ref: 1st and 5th boss)
316 			scroll_corr -= 2;   // apply unique vertical correction
317 
318 			// X correction (ref: 1st boss, seems to undo non-rotated global X offset)
319 			linedata_offs -= 5; // apply unique horizontal correction
320 		}
321 
322 		if (orientation & ORIENTATION_FLIP_X)
323 		{
324 			linedata_adv = -linedata_adv;       // traverse line RAM backward in X flipped scenarioes
325 			linedata_offs += bitmap.width() - 1;    // and get info for the first line from the bottom
326 		}
327 
328 		if (src_clipmask)
329 		{
330 			// determine target wrap boundary and draw scanline in two passes if the source is clipped
331 			dst_wrapmask = dst_height - 1;
332 			passes = 2;
333 		}
334 		else
335 		{
336 			// otherwise disable target wraparound and draw scanline in a single pass
337 			dst_wrapmask = ~0;
338 			passes = 1;
339 		}
340 	}
341 
342 	linedata_offs *= 4;                             // each line info packet has four words(eight bytes)
343 	linedata_offs &= 0x7ff;                         // and it should wrap at the four-kilobyte boundary
344 	linedata_offs += line_start * linedata_adv;     // pre-advance line info offset for the clipped region
345 
346 	// load physical palette base
347 	pal_base = palette().pens() + (colorbase << 4) % palette().entries();
348 
349 	// walk the target bitmap within the visible area vertically or horizontally, one line at a time
350 	for (line_pos=line_start; line_pos <= line_end; linedata_offs += linedata_adv, line_pos++)
351 	{
352 		linedata_offs &= 0x7ff;                     // line info data wraps at the four-kilobyte boundary
353 
354 		color  = line_ram[linedata_offs];           // get scanline color code
355 		if (color == 0xffff) continue;              // reject scanline if color code equals minus one
356 
357 		offset   = line_ram[linedata_offs + 1];     // get first pixel offset in ROM
358 		if (!(color & 0xff) && !offset) continue;   // reject scanline if both color and pixel offset are 0
359 
360 		// calculate physical palette location
361 		// there can be thirty-two color codes and each code represents sixteen pens
362 		pal_ptr = pal_base + ((color & 0x1f) << 4);
363 
364 		// calculate physical pixel location
365 		// each offset unit represents 256 pixels and should wrap at ROM boundary for safety
366 		pix_ptr = &m_unpacked_rom[((offset << 8) % m_unpacked_rom.size())];
367 
368 		// get scanline zoom factor
369 		// For example, 0x20 doubles the length, 0x40 maintains a one-to-one length,
370 		// and 0x80 halves the length. The zoom center is at the beginning of the
371 		// scanline therefore it is not necessary to adjust render start position
372 		zoom    = line_ram[linedata_offs + 2];
373 
374 		scroll  = (short)line_ram[linedata_offs + 3];   // get signed local scroll value for the current scanline
375 
376 		// scavenged from old code; improves Xexex' first level sky
377 		if (wrap500 && scroll >= 0x500) scroll -= 0x800;
378 
379 		scroll += scroll_corr;  // apply final scroll correction
380 		scroll &= dst_wrapmask; // wraparound scroll value if necessary
381 
382 		// draw scanlines wrapped at virtual bitmap boundary in two passes
383 		// this should not impose too much overhead due to clipping performed by the render code
384 		i = passes;
385 		do
386 		{
387 			/*
388 			    Parameter descriptions:
389 
390 			    bitmap       : pointer to a MAME bitmap as the render target
391 			    pal_ptr      : pointer to the palette's physical location relative to the scanline
392 			    pix_ptr      : pointer to the physical start location of source pixels in ROM
393 			    cliprect     : pointer to a rectangle structue which describes the visible area of the target bitmap
394 			    line_pos     : scanline render position relative to the target bitmap
395 			                   should be a Y offset to the target bitmap in normal orientaion,
396 			                   or an X offset to the target bitmap if X,Y are swapped
397 			    scroll       : source scroll value of the scanline
398 			    zoom         : source zoom factor of the scanline
399 			    src_clipmask : source offset clip mask; source pixels with offsets beyond the scope of this mask will not be drawn
400 			    src_wrapmask : source offset wrap mask; wraps source offset around, no effect when src_clipmask is set
401 			    orientation  : flags indicating whether scanlines should be drawn horizontally, vertically, forward or backward
402 			    priority     : value to be written to the priority bitmap, no effect when equals 0
403 			*/
404 			pdraw_scanline32(bitmap, pal_ptr, pix_ptr, cliprect,
405 				line_pos, scroll, zoom, src_clipmask, src_wrapmask, orientation, priority_bitmap, (uint8_t)priority);
406 
407 			// shift scanline position one virtual screen upward to render the wrapped end if necessary
408 			scroll -= dst_height;
409 		}
410 		while (--i);
411 	}
412 }
413 
dma(int limiter)414 void k053250_device::dma(int limiter)
415 {
416 	int current_frame = screen().frame_number();
417 
418 	if (limiter && current_frame == m_frame)
419 		return; // make sure we only do DMA transfer once per frame
420 
421 	m_frame = current_frame;
422 	memcpy(m_buffer[m_page], &m_ram[0], 0x1000);
423 	m_page ^= 1;
424 }
425 
reg_r(offs_t offset)426 uint16_t k053250_device::reg_r(offs_t offset)
427 {
428 	return m_regs[offset];
429 }
430 
reg_w(offs_t offset,uint16_t data,uint16_t mem_mask)431 void k053250_device::reg_w(offs_t offset, uint16_t data, uint16_t mem_mask)
432 {
433 	if (ACCESSING_BITS_0_7)
434 	{
435 		// start LVC DMA transfer at the falling edge of control register's bit1
436 		if (offset == 4 && !(data & 2) && (m_regs[4] & 2))
437 			dma(1);
438 
439 		m_regs[offset] = data;
440 	}
441 }
442 
ram_r(offs_t offset)443 uint16_t k053250_device::ram_r(offs_t offset)
444 {
445 	return m_ram[offset];
446 }
447 
ram_w(offs_t offset,uint16_t data,uint16_t mem_mask)448 void k053250_device::ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
449 {
450 	COMBINE_DATA(&m_ram[offset]);
451 }
452 
rom_r(offs_t offset)453 uint16_t k053250_device::rom_r(offs_t offset)
454 {
455 	return m_rom[0x80000 * m_regs[6] + 0x800 * m_regs[7] + offset/2];
456 }
457