Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
Vdp |
|
| 3.9642857142857144;3.964 |
1 | /* |
|
2 | * Vdp.java |
|
3 | * |
|
4 | * This file is part of JavaGear. |
|
5 | * |
|
6 | * JavaGear is free software; you can redistribute it and/or modify |
|
7 | * it under the terms of the GNU General Public License as published by |
|
8 | * the Free Software Foundation; either version 2 of the License, or |
|
9 | * (at your option) any later version. |
|
10 | * |
|
11 | * JavaGear is distributed in the hope that it will be useful, |
|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 | * GNU General Public License for more details. |
|
15 | * |
|
16 | * You should have received a copy of the GNU General Public License |
|
17 | * along with JavaGear; if not, write to the Free Software |
|
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
19 | */ |
|
20 | ||
21 | package uk.co.javagear; |
|
22 | ||
23 | import java.util.Arrays; |
|
24 | import org.apache.log4j.Logger; |
|
25 | ||
26 | /** |
|
27 | * SMS and GG VDP Emulation. |
|
28 | * |
|
29 | * @author Copyright (C) 2002-2003 Chris White |
|
30 | * @version 18th January 2003 |
|
31 | * @see "JavaGear Final Project Report" |
|
32 | */ |
|
33 | public final class Vdp { |
|
34 | ||
35 | /** |
|
36 | * Connection to Z80 interrupt line. |
|
37 | */ |
|
38 | private InterruptLine irq; |
|
39 | ||
40 | /** |
|
41 | * Pointer to general parameters. |
|
42 | */ |
|
43 | private Setup setup; |
|
44 | ||
45 | /** |
|
46 | * Video RAM. |
|
47 | */ |
|
48 | private byte[] vRam; |
|
49 | ||
50 | /** |
|
51 | * Colour RAM. |
|
52 | */ |
|
53 | private int[] cRam; |
|
54 | ||
55 | /** |
|
56 | * VDP Registers. |
|
57 | */ |
|
58 | private int[] vdpreg; |
|
59 | ||
60 | /** |
|
61 | * Status Register. |
|
62 | */ |
|
63 | private int status; |
|
64 | ||
65 | /** |
|
66 | * First or second byte of command word. |
|
67 | */ |
|
68 | private boolean firstByte; |
|
69 | ||
70 | /** |
|
71 | * Command word first byte latch. |
|
72 | */ |
|
73 | private int commandByte; |
|
74 | ||
75 | /** |
|
76 | * Location in VRAM. |
|
77 | */ |
|
78 | private int location; |
|
79 | ||
80 | /** |
|
81 | * Store type of operation taking place. |
|
82 | */ |
|
83 | private int operation; |
|
84 | ||
85 | /** |
|
86 | * Buffer VRAM reads. |
|
87 | */ |
|
88 | private int readBuffer; |
|
89 | ||
90 | /** |
|
91 | * Current line number. |
|
92 | */ |
|
93 | private int line; |
|
94 | ||
95 | /** |
|
96 | * Vertical line interrupt counter. |
|
97 | */ |
|
98 | private int counter; |
|
99 | ||
100 | /** |
|
101 | * Line interrupt pending. |
|
102 | */ |
|
103 | private boolean lineint; |
|
104 | ||
105 | /** |
|
106 | * Frame interrupt pending. |
|
107 | */ |
|
108 | private boolean frameint; |
|
109 | ||
110 | /** |
|
111 | * Background priorites. |
|
112 | */ |
|
113 | private boolean[] bgPriority; |
|
114 | ||
115 | /** |
|
116 | * Sprite collisions. |
|
117 | */ |
|
118 | private boolean[] spritecol; |
|
119 | ||
120 | /** |
|
121 | * Pointer to current display. |
|
122 | */ |
|
123 | private int[] display; |
|
124 | ||
125 | /** |
|
126 | * Pointer to previous frame. |
|
127 | */ |
|
128 | private int[] oldDisplay; |
|
129 | ||
130 | /** |
|
131 | * Frame 1. |
|
132 | */ |
|
133 | private int[] display1; |
|
134 | ||
135 | /** |
|
136 | * Frame 2. |
|
137 | */ |
|
138 | private int[] display2; |
|
139 | ||
140 | /** |
|
141 | * Generate background layer. |
|
142 | */ |
|
143 | 0 | private boolean displayBackgroundLayer = true; |
144 | ||
145 | /** |
|
146 | * Generate sprite layer. |
|
147 | */ |
|
148 | 0 | private boolean displaySpriteLayer = true; |
149 | ||
150 | /** |
|
151 | * NTSC/PAL. |
|
152 | */ |
|
153 | private boolean ntsc; |
|
154 | ||
155 | ||
156 | /** |
|
157 | * Vdp Constructor. |
|
158 | * |
|
159 | * @param set pointer to general parameters |
|
160 | * @param i pointer to Z80 interrupt line |
|
161 | */ |
|
162 | 0 | public Vdp(Setup set, InterruptLine i) { |
163 | 0 | this.irq = i; |
164 | 0 | this.setup = set; |
165 | ||
166 | 0 | display1 = new int[setup.SMS_WIDTH * (setup.SMS_HEIGHT + 1)]; |
167 | 0 | display2 = new int[setup.SMS_WIDTH * (setup.SMS_HEIGHT + 1)]; |
168 | ||
169 | // 16K of Video RAM |
|
170 | 0 | vRam = new byte[0x4000]; |
171 | ||
172 | // 64 Bytes of Colour RAM (32 on SMS) |
|
173 | 0 | cRam = new int[0x40]; |
174 | ||
175 | // 15 Registers, (0-10) used by SMS, but some programs write > 10 |
|
176 | 0 | vdpreg = new int[16]; |
177 | ||
178 | 0 | bgPriority = new boolean[setup.SMS_WIDTH + 7]; |
179 | 0 | spritecol = new boolean[setup.SMS_WIDTH]; |
180 | ||
181 | 0 | reset(); |
182 | 0 | } |
183 | ||
184 | /** |
|
185 | * Reset VDP. |
|
186 | */ |
|
187 | public void reset() { |
|
188 | 0 | display = display1; |
189 | 0 | oldDisplay = display2; |
190 | 0 | firstByte = true; |
191 | 0 | lineint = false; |
192 | 0 | frameint = false; |
193 | 0 | location = 0; |
194 | 0 | counter = 0; |
195 | 0 | status = 0; |
196 | 0 | operation = 0; |
197 | 0 | vdpreg[0] = 0; |
198 | 0 | vdpreg[1] = 0; |
199 | 0 | vdpreg[2] = 0x0E; // B1-B3 high on startup |
200 | 0 | vdpreg[3] = 0; |
201 | 0 | vdpreg[4] = 0; |
202 | 0 | vdpreg[5] = 0x7E; // B1-B6 high on startup |
203 | 0 | vdpreg[6] = 0; |
204 | 0 | vdpreg[7] = 0; |
205 | 0 | vdpreg[8] = 0; |
206 | 0 | vdpreg[9] = 0; |
207 | 0 | vdpreg[10] = 0; |
208 | ||
209 | // Clear Memory |
|
210 | 0 | Arrays.fill(display1, 0); |
211 | 0 | Arrays.fill(display2, 0); |
212 | 0 | Arrays.fill(vRam, (byte) 0); |
213 | 0 | Arrays.fill(cRam, 0); |
214 | 0 | Arrays.fill(bgPriority, false); |
215 | 0 | Arrays.fill(spritecol, false); |
216 | ||
217 | 0 | irq.setLine(false); |
218 | 0 | } |
219 | ||
220 | ||
221 | /** |
|
222 | * Set NTSC / PAL VDP Mode. |
|
223 | * |
|
224 | * @param b <code>true</code> if NTSC, <code>false</code> if PAL. |
|
225 | */ |
|
226 | public void setNTSC(boolean b) { |
|
227 | 0 | ntsc = b; |
228 | 0 | } |
229 | ||
230 | /** |
|
231 | * Returns the value of property <code>display</code>. The pointer to the current display. |
|
232 | * |
|
233 | * @return the value of property <code>display</code>. |
|
234 | */ |
|
235 | public int[] getDisplay() { |
|
236 | 0 | return display; |
237 | } |
|
238 | ||
239 | /** |
|
240 | * Sets the value of property <code>display</code>. The pointer to the current display. |
|
241 | * |
|
242 | * @param display the new value of property <code>display</code>. |
|
243 | */ |
|
244 | public void setDisplay(int[] display) { |
|
245 | 0 | this.display = display; |
246 | 0 | } |
247 | ||
248 | /** |
|
249 | * Toggle background layer generation on/off. |
|
250 | */ |
|
251 | public void setBackgroundLayer() { |
|
252 | 0 | displayBackgroundLayer = !displayBackgroundLayer; |
253 | 0 | } |
254 | ||
255 | /** |
|
256 | * Toggle Sprite Layer Generation On/Off. |
|
257 | */ |
|
258 | public void setSpriteLayer() { |
|
259 | 0 | displaySpriteLayer = !displaySpriteLayer; |
260 | 0 | } |
261 | ||
262 | ||
263 | /** |
|
264 | * Get a VDP register. |
|
265 | * |
|
266 | * @param index index of register. |
|
267 | * |
|
268 | * @return register data. |
|
269 | */ |
|
270 | public int getReg(int index) { |
|
271 | 0 | return vdpreg[index]; |
272 | } |
|
273 | ||
274 | ||
275 | /** |
|
276 | * Get line number under generation. |
|
277 | * |
|
278 | * @return line number. |
|
279 | */ |
|
280 | ||
281 | public int getLineNo() { |
|
282 | 0 | return line; |
283 | } |
|
284 | ||
285 | ||
286 | /** |
|
287 | * Get line interrupt counter. |
|
288 | * |
|
289 | * @return line interrupt counter. |
|
290 | */ |
|
291 | ||
292 | public int getCounter() { |
|
293 | 0 | return counter; |
294 | } |
|
295 | ||
296 | ||
297 | /** |
|
298 | * Get line interrupt pending flag. |
|
299 | * |
|
300 | * @return <code>true</code> if pending. |
|
301 | */ |
|
302 | public boolean getLineIntPending() { |
|
303 | 0 | return lineint; |
304 | } |
|
305 | ||
306 | ||
307 | /** |
|
308 | * Get frame interrupt pending flag. |
|
309 | * |
|
310 | * @return <code>true</code> if pending. |
|
311 | */ |
|
312 | public boolean getFrameIntPending() { |
|
313 | 0 | return frameint; |
314 | } |
|
315 | ||
316 | ||
317 | /** |
|
318 | * Get interruptLine status. |
|
319 | * |
|
320 | * @return <code>true</code> if pending. |
|
321 | */ |
|
322 | public boolean getIrqStatus() { |
|
323 | 0 | return irq.getLine(); |
324 | } |
|
325 | ||
326 | ||
327 | /** |
|
328 | * Get sprite overflow status. |
|
329 | * |
|
330 | * @return <code>true</code> if overflow has occurred. |
|
331 | */ |
|
332 | public boolean getSpriteOverflow() { |
|
333 | 0 | return (status & 0x40) == 0x40; |
334 | } |
|
335 | ||
336 | /** |
|
337 | * Read vertical port. |
|
338 | * |
|
339 | * @return VCounter value |
|
340 | */ |
|
341 | public int getVCount() { |
|
342 | 0 | if (ntsc) { |
343 | 0 | if (line > 0xDA) { // Values from 00 to DA, then jump to D5-FF |
344 | 0 | return line - 6; |
345 | } |
|
346 | 0 | } else if (!ntsc) { |
347 | 0 | if (line > 0xF2) { |
348 | 0 | return line - 0x39; |
349 | } |
|
350 | } |
|
351 | 0 | return line; |
352 | } |
|
353 | ||
354 | ||
355 | /** |
|
356 | * Write to VDP control port (<code>0xBF</code>). |
|
357 | * |
|
358 | * @param value value to write. |
|
359 | */ |
|
360 | ||
361 | public void controlWrite(int value) { |
|
362 | // Store First Byte of Command Word |
|
363 | 0 | if (firstByte) { |
364 | 0 | firstByte = false; |
365 | 0 | commandByte = value; |
366 | } else { |
|
367 | 0 | firstByte = true; |
368 | ||
369 | // Set VDP Register |
|
370 | 0 | if ((value >> 4) == 0x08) { |
371 | 0 | vdpreg[(value & 0x0F)] = commandByte; // Set reg to previous byte |
372 | ||
373 | // Interrupt Control IE1/IE0, |
|
374 | // Verified using test program by Charles MacDonald |
|
375 | 0 | if (lineint) { |
376 | 0 | if (((value & 0x0F) == 0) && ((commandByte & 0x10) == 0x0)) { |
377 | 0 | irq.setLine(false); |
378 | 0 | } else if (((value & 0x0F) == 0) && ((commandByte & 0x10) == 0x10)) { |
379 | 0 | irq.setLine(true); |
380 | } |
|
381 | } |
|
382 | ||
383 | 0 | if (frameint) { |
384 | 0 | if (((value & 0x0F) == 1) && ((commandByte & 0x20) == 0x0)) { |
385 | 0 | irq.setLine(false); |
386 | 0 | } else if (((value & 0x0F) == 1) && ((commandByte & 0x20) == 0x20)) { |
387 | 0 | irq.setLine(true); |
388 | } |
|
389 | } |
|
390 | } else { |
|
391 | // Operation from B6 + B7 |
|
392 | 0 | operation = (value >> 6); |
393 | // Set location in VRAM |
|
394 | 0 | location = commandByte + ((value & 0x3F) << 8); |
395 | ||
396 | 0 | if (operation == 0) { |
397 | 0 | readBuffer = unsigned(vRam[location]); |
398 | 0 | if (++location > 0x3FFF) { |
399 | 0 | location = 0; |
400 | } |
|
401 | } |
|
402 | } |
|
403 | } |
|
404 | 0 | } |
405 | ||
406 | ||
407 | /** |
|
408 | * Read VDP control port (<code>0xBF</code>). |
|
409 | * |
|
410 | * @return copy of status register. |
|
411 | */ |
|
412 | public int controlRead() { |
|
413 | // Reset flag |
|
414 | 0 | firstByte = true; |
415 | ||
416 | // Create copy, as we'll need to clear bits of status reg |
|
417 | 0 | int statuscopy = status; |
418 | ||
419 | // Clear b7, b6, b5 when status register read |
|
420 | 0 | status &= ~0x80 & ~0x40 & ~0x20; |
421 | ||
422 | // Clear pending interrupts |
|
423 | 0 | lineint = false; |
424 | 0 | frameint = false; |
425 | ||
426 | // Clear IRQ Line |
|
427 | 0 | irq.setLine(false); |
428 | ||
429 | 0 | return statuscopy; |
430 | } |
|
431 | ||
432 | ||
433 | /** |
|
434 | * Write to VDP data port (<code>0xBE</code>). |
|
435 | * |
|
436 | * @param value value to write |
|
437 | */ |
|
438 | public void dataWrite(int value) { |
|
439 | // Reset flag |
|
440 | 0 | firstByte = true; |
441 | ||
442 | 0 | switch(operation) { |
443 | // VRAM Write |
|
444 | case 0x00: |
|
445 | case 0x01: |
|
446 | case 0x02: |
|
447 | 0 | vRam[location] = (byte) value; |
448 | 0 | break; |
449 | /* CRAM Write |
|
450 | * Instead of writing real colour to CRAM, write converted Java palette colours for |
|
451 | * speed. Slightly inaccurate, as CRAM doesn't contain real values, but it is never |
|
452 | * read by software. */ |
|
453 | case 0x03: |
|
454 | 0 | switch (setup.getSystem()) { |
455 | case SMS: |
|
456 | 0 | cRam[location & 0x1F] = SMS_JAVA[value & 0x3F]; // SMS |
457 | 0 | break; |
458 | case GG: |
|
459 | 0 | if ((location & 1) == 0) { // first byte |
460 | 0 | cRam[(location & 0x3F) >> 1] = GG_JAVA1[value]; // GG |
461 | } else { |
|
462 | 0 | cRam[((location & 0x3F) - 1) >> 1] |= GG_JAVA2[value & 0x0F]; |
463 | } |
|
464 | 0 | break; |
465 | default: |
|
466 | 0 | Logger.getLogger(this.getClass()).fatal("Invalid system: " |
467 | + setup.getSystem()); |
|
468 | } |
|
469 | 0 | break; |
470 | default: |
|
471 | break; |
|
472 | } |
|
473 | ||
474 | 0 | if (++location > 0x3FFF) { |
475 | 0 | location = 0; |
476 | } |
|
477 | 0 | } |
478 | ||
479 | ||
480 | /** |
|
481 | * Read VDP data port (<code>0xBE</code>). |
|
482 | * |
|
483 | * @return buffered read from VRAM |
|
484 | */ |
|
485 | public int dataRead() { |
|
486 | 0 | int value = 0; // Stores value to be returned |
487 | 0 | firstByte = true; // Reset flag |
488 | ||
489 | 0 | switch(operation) { |
490 | // VRAM Read |
|
491 | case 0x00: |
|
492 | case 0x01: |
|
493 | case 0x02: |
|
494 | 0 | value = readBuffer; |
495 | 0 | readBuffer = unsigned(vRam[location]); |
496 | 0 | break; |
497 | // CRAM Read |
|
498 | // Shouldn't occur but GameGear 'NBA Action' tries this. |
|
499 | case 0x03: |
|
500 | 0 | break; |
501 | default: |
|
502 | break; |
|
503 | } |
|
504 | ||
505 | 0 | if (++location > 0x3FFF) { |
506 | 0 | location = 0; |
507 | } |
|
508 | ||
509 | 0 | return value; |
510 | } |
|
511 | ||
512 | ||
513 | /** |
|
514 | * Generate VDP interrupts. |
|
515 | * Assert the IRQ line as necessary for a particular scanline. |
|
516 | * |
|
517 | * @param lineno line to check for interrupts. |
|
518 | */ |
|
519 | public void interrupts(int lineno) { |
|
520 | 0 | line = lineno; |
521 | ||
522 | 0 | if (lineno <= 192) { |
523 | // Frame Interrupt Pending |
|
524 | 0 | if (lineno == 192) { |
525 | 0 | status |= 0x80; |
526 | 0 | frameint = true; |
527 | } |
|
528 | ||
529 | // Counter Expired = Line Interrupt Pending |
|
530 | 0 | if (counter == 0) { |
531 | // Reload Counter |
|
532 | 0 | counter = vdpreg[10]; |
533 | 0 | lineint = true; |
534 | } else { // Otherwise Decrement Counter |
|
535 | 0 | counter--; |
536 | } |
|
537 | ||
538 | // Line Interrupts Enabled and Pending. Assert IRQ Line. |
|
539 | 0 | if (lineint && ((vdpreg[0] & 0x10) == 0x10)) { |
540 | 0 | irq.setLine(true); |
541 | } |
|
542 | } else { // lineno >= 193 |
|
543 | // Reload counter on every line outside active display + 1 |
|
544 | 0 | counter = vdpreg[10]; |
545 | ||
546 | // Frame Interrupts Enabled and Pending. Assert IRQ Line. |
|
547 | 0 | if (frameint && ((vdpreg[1] & 0x20) == 0x20) && (lineno < 224)) { |
548 | 0 | irq.setLine(true); |
549 | } |
|
550 | } |
|
551 | 0 | } |
552 | ||
553 | ||
554 | /** |
|
555 | * Render line of SMS/GG display. |
|
556 | * |
|
557 | * @param lineno line number to render. |
|
558 | */ |
|
559 | public void drawLine(int lineno) { |
|
560 | 0 | if (lineno > 191) { |
561 | 0 | return; |
562 | } |
|
563 | ||
564 | // Clear Collision Array at start of line |
|
565 | 0 | for (int i = spritecol.length; i-- != 0;) { |
566 | 0 | spritecol[i] = false; |
567 | } |
|
568 | ||
569 | 0 | if (lineno == 0) { |
570 | // Swap buffers |
|
571 | 0 | if (display == display1) { |
572 | 0 | display = display2; |
573 | 0 | oldDisplay = display1; |
574 | } else { |
|
575 | 0 | display = display1; |
576 | 0 | oldDisplay = display2; |
577 | } |
|
578 | } |
|
579 | ||
580 | // Blank Display |
|
581 | 0 | if ((vdpreg[1] & 0x40) != 0) { |
582 | // Draw Background Layer |
|
583 | 0 | if (displayBackgroundLayer) { |
584 | 0 | drawBg(lineno); |
585 | } else { |
|
586 | 0 | drawBGColour(lineno); |
587 | } |
|
588 | ||
589 | // Draw Sprite Layer |
|
590 | 0 | if (displaySpriteLayer) { |
591 | 0 | drawSprite(lineno); |
592 | } |
|
593 | } else { // Blank Display |
|
594 | 0 | drawBGColour(lineno); |
595 | } |
|
596 | ||
597 | // Blank Leftmost Column (SMS Only) |
|
598 | 0 | if (Setup.System.SMS == setup.getSystem() |
599 | && (vdpreg[0] & 0x20) == 0x20) { |
|
600 | 0 | blankColumn(lineno); |
601 | } |
|
602 | 0 | } |
603 | ||
604 | ||
605 | /** |
|
606 | * Render line of sprite layer. |
|
607 | * |
|
608 | * @param lineno line number to render. |
|
609 | */ |
|
610 | private void drawSprite(int lineno) { |
|
611 | // Sprite Attribute Table Address |
|
612 | 0 | int sat = (vdpreg[5] & ~0x01 & ~0x80) << 7; |
613 | ||
614 | // Number of Sprites drawn on this line (max 8) |
|
615 | 0 | int count = 0; |
616 | ||
617 | // Height of Sprites |
|
618 | 0 | int height = 8; |
619 | ||
620 | // Zoom Sprites |
|
621 | 0 | boolean zoomed = false; |
622 | ||
623 | // Enable 8x16 Sprites |
|
624 | 0 | if ((vdpreg[1] & 0x02) == 0x02) { |
625 | 0 | height = 16; |
626 | } |
|
627 | ||
628 | // Enable Zoomed Sprites |
|
629 | 0 | if ((vdpreg[1] & 0x01) == 0x01) { |
630 | 0 | height <<= 1; |
631 | 0 | zoomed = true; |
632 | } |
|
633 | ||
634 | // Search Sprite Attribute Table (64 Bytes) |
|
635 | 0 | for (int spriteno = 0; spriteno < 0x40; spriteno++) { |
636 | // Sprite Overflow (more than 8 sprites on line) |
|
637 | 0 | if (count >= 8) { |
638 | 0 | status |= 0x40; |
639 | 0 | return; |
640 | } |
|
641 | // Sprite Y Position |
|
642 | 0 | int y = vRam[sat + spriteno] & 0xFF; |
643 | // Address of Sprite in Sprite Attribute Table |
|
644 | 0 | int address = sat + (spriteno << 1); |
645 | // Sprite X Position |
|
646 | 0 | int x = vRam[address + 0x80] & 0xFF; |
647 | // Sprite Pattern Index |
|
648 | 0 | int i = vRam[address + 0x81] & 0xFF; |
649 | ||
650 | // VDP stops drawing if y == 208 |
|
651 | 0 | if (y == 208) { |
652 | 0 | return; |
653 | } |
|
654 | ||
655 | // y is actually at +1 of value |
|
656 | 0 | y++; |
657 | ||
658 | // Use Pattern Index from 100 - 1FFh |
|
659 | 0 | if ((vdpreg[6] & 0x04) == 0x04) { |
660 | 0 | i |= 0x100; |
661 | } |
|
662 | ||
663 | // When using 8x16 sprites LSB has no effect |
|
664 | 0 | if ((vdpreg[1] & 0x02) == 0x02) { |
665 | 0 | i &= ~0x01; |
666 | } |
|
667 | ||
668 | // Shift pixels left by 8 |
|
669 | 0 | if ((vdpreg[0] & 0x08) == 0x08) { |
670 | 0 | x -= 8; |
671 | } |
|
672 | ||
673 | // If off screen, draw from negative 16 onwards |
|
674 | 0 | if (y > 240) { |
675 | 0 | y -= 256; |
676 | } |
|
677 | ||
678 | // If Sprite Falls on this line |
|
679 | 0 | if ((lineno >= y) && ((lineno - y) < height)) { |
680 | 0 | int rowPrecal = lineno << 8; |
681 | ||
682 | // Plot Normal Sprites (Width = 8) |
|
683 | 0 | if (!zoomed) { |
684 | // Address of 8 Pixels in VRAM |
|
685 | 0 | int adr = (i << 5) + ((lineno - y) << 2); |
686 | ||
687 | // Cycle through 8 Pixels |
|
688 | 0 | for (int bit = 0x80; bit != 0; bit >>= 1) { |
689 | // Make sure we're in the valid viewing area |
|
690 | 0 | if ((x >= 0) && (y >= 0) && (x <= 255)) { |
691 | // Plot Single Pixel |
|
692 | 0 | plotSpritePixel(x, lineno, x + rowPrecal, adr, bit); |
693 | } |
|
694 | // Increement X Position |
|
695 | 0 | x++; |
696 | } |
|
697 | 0 | } else { // Plot Zoomed Sprites (Width = 16) |
698 | 0 | int adr = (i << 5) + (((lineno - y) >> 1) << 2); |
699 | ||
700 | 0 | int pixel = 0; |
701 | ||
702 | 0 | for (int bit = 0x80; bit != 0; bit >>= 1) { |
703 | 0 | if ((x + pixel + 1 >= 0) && (y >= 0) && (x <= 255)) { |
704 | // Plot Two Pixels |
|
705 | 0 | plotSpritePixel(x + pixel, lineno, x + pixel + rowPrecal, adr, bit); |
706 | 0 | plotSpritePixel(x + pixel + 1, lineno, x + pixel + 1 + rowPrecal |
707 | , adr, bit); |
|
708 | } |
|
709 | 0 | x++; |
710 | 0 | pixel++; |
711 | } |
|
712 | } |
|
713 | // Increment number of sprites drawn |
|
714 | 0 | count++; |
715 | } |
|
716 | } // end for loop |
|
717 | 0 | } |
718 | ||
719 | ||
720 | /** |
|
721 | * Plot single sprite pixel. |
|
722 | * |
|
723 | * @param x <i>x</i> coordinate of pixel (for location array). |
|
724 | * @param y <i>y</i> coordinate of pixel. |
|
725 | * @param location coordinates to plot pixel at <code>x+(y*256)</code>. |
|
726 | * @param address VRAM address of pixel plane. |
|
727 | * @param bit bit of plane to plot. |
|
728 | */ |
|
729 | private void plotSpritePixel(int x, int y, int location, int address, int bit) { |
|
730 | // Colour of pixel |
|
731 | 0 | int colour = 0; |
732 | ||
733 | // Set Colour of Pixel (0-15) |
|
734 | 0 | if ((vRam[address + 0] & bit) != 0) { |
735 | 0 | colour |= 0x01; |
736 | } |
|
737 | 0 | if ((vRam[address + 1] & bit) != 0) { |
738 | 0 | colour |= 0x02; |
739 | } |
|
740 | 0 | if ((vRam[address + 2] & bit) != 0) { |
741 | 0 | colour |= 0x04; |
742 | } |
|
743 | 0 | if ((vRam[address + 3] & bit) != 0) { |
744 | 0 | colour |= 0x08; |
745 | } |
|
746 | ||
747 | // Plot Pixel, check background priority for location |
|
748 | 0 | if (!(bgPriority[x]) && (colour != 0)) { |
749 | // Check for existing sprite collision |
|
750 | 0 | if (!spritecol[x]) { |
751 | 0 | spritecol[x] = true; |
752 | 0 | display[location] = cRam[colour + 16]; |
753 | } else { |
|
754 | 0 | status |= 0x20; // set bit 5 of status flag |
755 | } |
|
756 | } |
|
757 | 0 | } |
758 | ||
759 | ||
760 | /** |
|
761 | * Render line of background layer. |
|
762 | * |
|
763 | * @param lineno line number to render. |
|
764 | */ |
|
765 | private void drawBg(int lineno) { |
|
766 | // Backup the _real_ line number to be plotted |
|
767 | 0 | int y = lineno; |
768 | ||
769 | // Vertical Scroll Fine Tune |
|
770 | 0 | if (vdpreg[9] != 0) { |
771 | 0 | lineno += vdpreg[9] & 0x07; |
772 | } |
|
773 | ||
774 | // Address of Background Table in VRAM |
|
775 | 0 | int bgt = (vdpreg[2] & 0x0f & ~0x01) << 10; |
776 | ||
777 | // Tile Pattern Number |
|
778 | 0 | int pattern = 0; |
779 | ||
780 | // Palette To Use |
|
781 | 0 | int pal = 0; |
782 | ||
783 | // Horizontal Scroll Value |
|
784 | 0 | int hscroll = 0; |
785 | ||
786 | // Start Column (for Horizontal Scroll) |
|
787 | 0 | int startColumn = 0; |
788 | ||
789 | // Start Row (For Vertical Scroll) |
|
790 | 0 | int startRow = lineno; |
791 | ||
792 | // Tile Row Count |
|
793 | 0 | int tilex = 0; |
794 | ||
795 | // Row of Tile to Plot |
|
796 | 0 | int tiley = (lineno & 7); |
797 | ||
798 | // Adjust Horizontal Scroll |
|
799 | ||
800 | // Top Two Rows Not Affected by Horizontal Scrolling (SMS Only) |
|
801 | 0 | if (((vdpreg[0] & 0x40) == 0x40) && (y < 16) |
802 | && Setup.System.SMS == setup.getSystem()) { |
|
803 | 0 | hscroll = 0; |
804 | 0 | } else if (vdpreg[8] != 0) { |
805 | // Horizontal Scroll Fine Tune Value |
|
806 | 0 | hscroll = vdpreg[8] & 0x07; |
807 | // Horizontal Scroll Starting Column |
|
808 | 0 | startColumn = 32 - (vdpreg[8] >> 3); |
809 | } |
|
810 | ||
811 | // Omit First 5 Columns on Game Gear |
|
812 | 0 | if (setup.getSystem() == Setup.System.GG) { |
813 | 0 | startColumn = (startColumn + 5) & 0x1F; |
814 | } |
|
815 | ||
816 | // Adjust Vertical Scroll |
|
817 | 0 | if (vdpreg[9] != 0) { |
818 | // Vertical Scroll Starting Row |
|
819 | 0 | startRow += vdpreg[9] & 0xF8; |
820 | 0 | if (startRow > 223) { |
821 | 0 | startRow -= 224; |
822 | } |
|
823 | } |
|
824 | ||
825 | // Cycle through background table |
|
826 | 0 | for (int x = setup.getHStart(); x < setup.getHEnd(); x += 8) { |
827 | // wraps at 32 |
|
828 | 0 | if (startColumn == 32) { |
829 | 0 | startColumn = 0; |
830 | } |
|
831 | ||
832 | // Rightmost 8 columns Not Affected by Vertical Scrolling |
|
833 | 0 | if (((vdpreg[0] & 0x80) == 0x80) && (x > 184)) { |
834 | 0 | startRow = y; |
835 | 0 | tiley = (y & 7); |
836 | } |
|
837 | ||
838 | // Get the two bytes from VRAM containing the tile's properties |
|
839 | 0 | int tileProps = bgt + (startColumn << 1) + ((startRow - tiley) << 3); |
840 | 0 | int firstbyte = vRam[tileProps] & 0xFF; |
841 | 0 | int secondbyte = vRam[tileProps + 1] & 0xFF; |
842 | ||
843 | // Priority of tile |
|
844 | 0 | int priority = secondbyte & 0x10; |
845 | ||
846 | // Select Palette |
|
847 | 0 | pal = ((secondbyte & 0x08) == 0x08) ? 16 : 0; |
848 | ||
849 | // Pattern Number |
|
850 | 0 | pattern = (firstbyte + ((secondbyte & 0x01) << 8)) << 5; |
851 | ||
852 | // Address in VRAM of tile |
|
853 | int address; |
|
854 | ||
855 | 0 | address = ((secondbyte & 0x04) == 0) ? (tiley << 2) + pattern |
856 | : ((7 - tiley) << 2) + pattern; |
|
857 | ||
858 | // Rowcount |
|
859 | 0 | tilex = 0; |
860 | ||
861 | // X Position on Screen |
|
862 | int xpos; |
|
863 | ||
864 | // Precalculate Y Position |
|
865 | 0 | int rowPrecal = y << 8; |
866 | ||
867 | 0 | int address0 = vRam[address + 0]; |
868 | 0 | int address1 = vRam[address + 1]; |
869 | 0 | int address2 = vRam[address + 2]; |
870 | 0 | int address3 = vRam[address + 3]; |
871 | ||
872 | // Plots row of 8 pixels |
|
873 | 0 | for (int bit = 0x80; bit != 0; bit >>= 1) { |
874 | 0 | xpos = ((secondbyte & 0x02) == 0) ? (tilex + hscroll + x) |
875 | // Horizontal Tile Flip |
|
876 | : (7 - tilex + hscroll + x); |
|
877 | ||
878 | 0 | int colour = 0; |
879 | ||
880 | // Set Colour of Pixel (0-15) |
|
881 | 0 | if ((address0 & bit) != 0) { |
882 | 0 | colour |= 0x01; |
883 | } |
|
884 | 0 | if ((address1 & bit) != 0) { |
885 | 0 | colour |= 0x02; |
886 | } |
|
887 | 0 | if ((address2 & bit) != 0) { |
888 | 0 | colour |= 0x04; |
889 | } |
|
890 | 0 | if ((address3 & bit) != 0) { |
891 | 0 | colour |= 0x08; |
892 | } |
|
893 | ||
894 | // Set Priority Array (Sprites over background tile) |
|
895 | 0 | bgPriority[xpos] = (priority == 0x10) && (colour != 0); |
896 | ||
897 | 0 | display[xpos + rowPrecal] = cRam[colour + pal]; |
898 | ||
899 | 0 | tilex++; |
900 | } |
|
901 | 0 | startColumn++; |
902 | } // end loop |
|
903 | 0 | } |
904 | ||
905 | ||
906 | /** |
|
907 | * Draw a line of the current background colour. |
|
908 | * |
|
909 | * @param lineno line number to render. |
|
910 | */ |
|
911 | private void drawBGColour(int lineno) { |
|
912 | 0 | int colour = cRam[16 + (vdpreg[7] & 0x0F)]; |
913 | 0 | int rowPrecal = lineno << 8; |
914 | 0 | Arrays.fill(display, rowPrecal, rowPrecal + setup.SMS_WIDTH, colour); |
915 | 0 | } |
916 | ||
917 | ||
918 | /** |
|
919 | * Blank leftmost column of a particular line. |
|
920 | * |
|
921 | * @param lineno line number to render. |
|
922 | * @deprecated now, faster <code>fillRect</code> method used. |
|
923 | */ |
|
924 | private void blankColumn(int lineno) { |
|
925 | 0 | int colour = cRam[16 + (vdpreg[7] & 0x0F)]; |
926 | 0 | int rowPrecal = lineno << 8; |
927 | 0 | Arrays.fill(display, rowPrecal, rowPrecal + 8, colour); |
928 | 0 | } |
929 | ||
930 | /** |
|
931 | * Unsign an integer. |
|
932 | * |
|
933 | * @param b value to unsign. |
|
934 | * @return unsigned value. |
|
935 | */ |
|
936 | private int unsigned(byte b) { |
|
937 | 0 | return b & 0xFF; |
938 | } |
|
939 | ||
940 | /** |
|
941 | * Compare this frame with the previous frame, and flag what lines have been changed. |
|
942 | * |
|
943 | * @param pixelUpdate array to flag changes in. |
|
944 | */ |
|
945 | public void flagChanges(boolean[] pixelUpdate) { |
|
946 | 0 | for (int y = setup.getVEnd(); y-- != 0; ) { |
947 | 0 | int rowPrecal = y << 8; |
948 | 0 | boolean updateRow = false; |
949 | 0 | for (int x = setup.getHEnd(); x-- != 0; ) { |
950 | 0 | if (display[x + rowPrecal] != oldDisplay[x + rowPrecal]) { |
951 | 0 | updateRow = true; |
952 | 0 | break; |
953 | } |
|
954 | } |
|
955 | 0 | pixelUpdate[y] = updateRow; |
956 | 0 | } |
957 | 0 | } |
958 | ||
959 | /** |
|
960 | * SMS Colours converted into Java RGB for speed purposes. |
|
961 | */ |
|
962 | 0 | private static final int[] SMS_JAVA = { |
963 | -16777216, -11206656, -5636096, -65536, -16755456, -11184896, -5614336, -43776, |
|
964 | -16733696, -11163136, -5592576, -22016, -16711936, -11141376, -5570816, -256, |
|
965 | -16777131, -11206571, -5636011, -65451, -16755371, -11184811, -5614251, -43691, |
|
966 | -16733611, -11163051, -5592491, -21931, -16711851, -11141291, -5570731, -171, |
|
967 | -16777046, -11206486, -5635926, -65366, -16755286, -11184726, -5614166, -43606, |
|
968 | -16733526, -11162966, -5592406, -21846, -16711766, -11141206, -5570646, -86, |
|
969 | -16776961, -11206401, -5635841, -65281, -16755201, -11184641, -5614081, -43521, |
|
970 | -16733441, -11162881, -5592321, -21761, -16711681, -11141121, -5570561, -1}; |
|
971 | ||
972 | /** |
|
973 | * GG Colours. |
|
974 | */ |
|
975 | 0 | private static final int[] GG_JAVA1 = { |
976 | -16777216, -15728640, -14680064, -13631488, -12582912, -11534336, -10485760, |
|
977 | -9437184, -8388608, -7340032, -6291456, -5242880, -4194304, -3145728, |
|
978 | -2097152, -1048576, -16773120, -15724544, -14675968, -13627392, -12578816, |
|
979 | -11530240, -10481664, -9433088, -8384512, -7335936, -6287360, -5238784, |
|
980 | -4190208, -3141632, -2093056, -1044480, -16769024, -15720448, -14671872, |
|
981 | -13623296, -12574720, -11526144, -10477568, -9428992, -8380416, -7331840, |
|
982 | -6283264, -5234688, -4186112, -3137536, -2088960, -1040384, -16764928, |
|
983 | -15716352, -14667776, -13619200, -12570624, -11522048, -10473472, -9424896, |
|
984 | -8376320, -7327744, -6279168, -5230592, -4182016, -3133440, -2084864, |
|
985 | -1036288, -16760832, -15712256, -14663680, -13615104, -12566528, -11517952, |
|
986 | -10469376, -9420800, -8372224, -7323648, -6275072, -5226496, -4177920, |
|
987 | -3129344, -2080768, -1032192, -16756736, -15708160, -14659584, -13611008, |
|
988 | -12562432, -11513856, -10465280, -9416704, -8368128, -7319552, -6270976, |
|
989 | -5222400, -4173824, -3125248, -2076672, -1028096, -16752640, -15704064, |
|
990 | -14655488, -13606912, -12558336, -11509760, -10461184, -9412608, -8364032, |
|
991 | -7315456, -6266880, -5218304, -4169728, -3121152, -2072576, -1024000, |
|
992 | -16748544, -15699968, -14651392, -13602816, -12554240, -11505664, -10457088, |
|
993 | -9408512, -8359936, -7311360, -6262784, -5214208, -4165632, -3117056, |
|
994 | -2068480, -1019904, -16744448, -15695872, -14647296, -13598720, -12550144, |
|
995 | -11501568, -10452992, -9404416, -8355840, -7307264, -6258688, -5210112, |
|
996 | -4161536, -3112960, -2064384, -1015808, -16740352, -15691776, -14643200, |
|
997 | -13594624, -12546048, -11497472, -10448896, -9400320, -8351744, -7303168, |
|
998 | -6254592, -5206016, -4157440, -3108864, -2060288, -1011712, -16736256, |
|
999 | -15687680, -14639104, -13590528, -12541952, -11493376, -10444800, -9396224, |
|
1000 | -8347648, -7299072, -6250496, -5201920, -4153344, -3104768, -2056192, |
|
1001 | -1007616, -16732160, -15683584, -14635008, -13586432, -12537856, -11489280, |
|
1002 | -10440704, -9392128, -8343552, -7294976, -6246400, -5197824, -4149248, |
|
1003 | -3100672, -2052096, -1003520, -16728064, -15679488, -14630912, -13582336, |
|
1004 | -12533760, -11485184, -10436608, -9388032, -8339456, -7290880, -6242304, |
|
1005 | -5193728, -4145152, -3096576, -2048000, -999424, -16723968, -15675392, |
|
1006 | -14626816, -13578240, -12529664, -11481088, -10432512, -9383936, -8335360, |
|
1007 | -7286784, -6238208, -5189632, -4141056, -3092480, -2043904, -995328, |
|
1008 | -16719872, -15671296, -14622720, -13574144, -12525568, -11476992, -10428416, |
|
1009 | -9379840, -8331264, -7282688, -6234112, -5185536, -4136960, -3088384, |
|
1010 | -2039808, -991232, -16715776, -15667200, -14618624, -13570048, -12521472, |
|
1011 | -11472896, -10424320, -9375744, -8327168, -7278592, -6230016, -5181440, |
|
1012 | -4132864, -3084288, -2035712, -987136}; |
|
1013 | ||
1014 | 0 | private static final int[] GG_JAVA2 = { |
1015 | 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240}; |
|
1016 | ||
1017 | } |