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