1 /*
2 * vicii-cycle.c - Cycle based VIC-II emulation.
3 *
4 * Written by
5 * Hannu Nuotio <hannu.nuotio@tut.fi>
6 * Daniel Kahlin <daniel@kahlin.net>
7 *
8 * Based on code by
9 * Ettore Perazzoli <ettore@comm2000.it>
10 * Andreas Boose <viceteam@t-online.de>
11 *
12 * This file is part of VICE, the Versatile Commodore Emulator.
13 * See README for copyright notice.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 * 02111-1307 USA.
29 *
30 */
31
32 #include "vice.h"
33
34 #include "debug.h"
35 #include "lib.h"
36 #include "log.h"
37 #include "maincpu.h"
38 #include "types.h"
39 #include "vicii-chip-model.h"
40 #include "vicii-cycle.h"
41 #include "vicii-draw-cycle.h"
42 #include "vicii-fetch.h"
43 #include "vicii-irq.h"
44 #include "vicii-lightpen.h"
45 #include "vicii-resources.h"
46 #include "vicii.h"
47 #include "viciitypes.h"
48
check_badline(void)49 static inline void check_badline(void)
50 {
51 /* Check badline condition (line range and "allow bad lines" handled outside */
52 if ((vicii.raster_line & 7) == vicii.ysmooth) {
53 vicii.bad_line = 1;
54 vicii.idle_state = 0;
55 } else {
56 vicii.bad_line = 0;
57 }
58 }
59
check_sprite_display(void)60 static inline void check_sprite_display(void)
61 {
62 int i, b;
63 int enable = vicii.regs[0x15];
64
65 for (i = 0, b = 1; i < VICII_NUM_SPRITES; i++, b <<= 1) {
66 unsigned int y = vicii.regs[i * 2 + 1];
67 vicii.sprite[i].mc = vicii.sprite[i].mcbase;
68
69 if (vicii.sprite_dma & b) {
70 if ((enable & b) && (y == (vicii.raster_line & 0xff))) {
71 vicii.sprite_display_bits |= b;
72 }
73 } else {
74 vicii.sprite_display_bits &= ~b;
75 }
76 }
77 }
78
sprite_mcbase_update(void)79 static inline void sprite_mcbase_update(void)
80 {
81 int i;
82
83 for (i = 0; i < VICII_NUM_SPRITES; i++) {
84 if (vicii.sprite[i].exp_flop) {
85 vicii.sprite[i].mcbase = vicii.sprite[i].mc;
86 if (vicii.sprite[i].mcbase == 63) {
87 vicii.sprite_dma &= ~(1 << i);
88 }
89 }
90 }
91 }
92
check_exp(void)93 static inline void check_exp(void)
94 {
95 int i, b;
96 int y_exp = vicii.regs[0x17];
97
98 for (i = 0, b = 1; i < VICII_NUM_SPRITES; i++, b <<= 1) {
99 if ((vicii.sprite_dma & b) && (y_exp & b)) {
100 vicii.sprite[i].exp_flop ^= 1;
101 }
102 }
103 }
104
105 /* Enable DMA for sprite i. */
turn_sprite_dma_on(unsigned int i,int y_exp)106 static inline void turn_sprite_dma_on(unsigned int i, int y_exp)
107 {
108 vicii.sprite_dma |= 1 << i;
109 vicii.sprite[i].mcbase = 0;
110 vicii.sprite[i].exp_flop = 1;
111 }
112
check_sprite_dma(void)113 static inline void check_sprite_dma(void)
114 {
115 int i, b;
116 int enable = vicii.regs[0x15];
117 int y_exp = vicii.regs[0x17];
118
119 for (i = 0, b = 1; i < VICII_NUM_SPRITES; i++, b <<= 1) {
120 unsigned int y = vicii.regs[i * 2 + 1];
121
122 if ((enable & b) && (y == (vicii.raster_line & 0xff)) && !(vicii.sprite_dma & b)) {
123 turn_sprite_dma_on(i, y_exp & b);
124 }
125 }
126 }
127
cycle_phi1_fetch(unsigned int cycle_flags)128 static inline uint8_t cycle_phi1_fetch(unsigned int cycle_flags)
129 {
130 uint8_t data;
131 int s;
132
133 if (cycle_is_fetch_g(cycle_flags)) {
134 if (!vicii.idle_state) {
135 data = vicii_fetch_graphics();
136 } else {
137 data = vicii_fetch_idle_gfx();
138 }
139 return data;
140 }
141
142 if (cycle_is_sprite_ptr_dma0(cycle_flags)) {
143 s = cycle_get_sprite_num(cycle_flags);
144 data = vicii_fetch_sprite_pointer(s);
145 return data;
146 }
147 if (cycle_is_sprite_dma1_dma2(cycle_flags)) {
148 s = cycle_get_sprite_num(cycle_flags);
149 data = vicii_fetch_sprite_dma_1(s);
150 return data;
151 }
152
153 if (cycle_is_refresh(cycle_flags)) {
154 data = vicii_fetch_refresh();
155 return data;
156 }
157
158 data = vicii_fetch_idle();
159
160 return data;
161 }
162
check_vborder_top(int line)163 static inline void check_vborder_top(int line)
164 {
165 int rsel = vicii.regs[0x11] & 0x08;
166
167 if ((line == (rsel ? VICII_25ROW_START_LINE : VICII_24ROW_START_LINE)) && (vicii.regs[0x11] & 0x10)) {
168 vicii.vborder = 0;
169 vicii.set_vborder = 0;
170 }
171 }
172
check_vborder_bottom(int line)173 static inline void check_vborder_bottom(int line)
174 {
175 int rsel = vicii.regs[0x11] & 0x08;
176
177 if (line == (rsel ? VICII_25ROW_STOP_LINE : VICII_24ROW_STOP_LINE)) {
178 vicii.set_vborder = 1;
179 }
180 }
181
check_hborder(unsigned int cycle_flags)182 static inline void check_hborder(unsigned int cycle_flags)
183 {
184 int csel = vicii.regs[0x16] & 0x08;
185
186 /* Left border ends at cycles 17 (csel=1) or 18 (csel=0) on PAL. */
187 if (cycle_is_check_border_l(cycle_flags, csel)) {
188 check_vborder_bottom(vicii.raster_line);
189 vicii.vborder = vicii.set_vborder;
190 if (vicii.vborder == 0) {
191 vicii.main_border = 0;
192 }
193 }
194 /* Right border starts at cycles 56 (csel=0) or 57 (csel=1) on PAL. */
195 if (cycle_is_check_border_r(cycle_flags, csel)) {
196 vicii.main_border = 1;
197 }
198 }
199
vicii_cycle_start_of_frame(void)200 static inline void vicii_cycle_start_of_frame(void)
201 {
202 vicii.start_of_frame = 0;
203 vicii.raster_line = 0;
204 vicii.refresh_counter = 0xff;
205 vicii.allow_bad_lines = 0;
206 vicii.vcbase = 0;
207 vicii.vc = 0;
208 vicii.light_pen.triggered = 0;
209
210 /* Retrigger light pen if line is still held low */
211 if (vicii.light_pen.state) {
212 /* add offset depending on chip model (FIXME use proper variable) */
213 vicii.light_pen.x_extra_bits = (vicii.color_latency ? 2 : 1);
214 vicii_trigger_light_pen_internal(1);
215 }
216 }
217
vicii_cycle_end_of_line(void)218 static inline void vicii_cycle_end_of_line(void)
219 {
220 vicii_raster_draw_handler();
221 if (vicii.raster_line == vicii.screen_height - 1) {
222 vicii.start_of_frame = 1;
223 }
224 }
225
vicii_cycle_start_of_line(void)226 static inline void vicii_cycle_start_of_line(void)
227 {
228 /* Check DEN bit on first cycle of the line following the first DMA line */
229 if ((vicii.raster_line == VICII_FIRST_DMA_LINE) && !vicii.allow_bad_lines && (vicii.regs[0x11] & 0x10)) {
230 vicii.allow_bad_lines = 1;
231 }
232
233 /* Disallow bad lines after the last possible one has passed */
234 if (vicii.raster_line == VICII_LAST_DMA_LINE) {
235 vicii.allow_bad_lines = 0;
236 }
237
238 vicii.bad_line = 0;
239 }
240
241
next_vicii_cycle(void)242 static inline void next_vicii_cycle(void)
243 {
244 /* Next cycle */
245 vicii.raster_cycle++;
246
247 /* Handle wrapping */
248 if (vicii.raster_cycle == (unsigned int)vicii.cycles_per_line) {
249 vicii.raster_cycle = 0;
250 }
251 }
252
vicii_cycle(void)253 int vicii_cycle(void)
254 {
255 int ba_low = 0;
256 int can_sprite_sprite, can_sprite_background;
257 int may_crash;
258
259 /*VICII_DEBUG_CYCLE(("cycle: line %i, clk %i", vicii.raster_line, vicii.raster_cycle));*/
260
261 /* perform phi2 fetch after the cpu has executed */
262 vicii_fetch_sprites(vicii.cycle_flags);
263
264 /*
265 *
266 * End of Phi2
267 *
268 ******/
269
270 /* Next cycle */
271 next_vicii_cycle();
272 vicii.cycle_flags = vicii.cycle_table[vicii.raster_cycle];
273
274 /******
275 *
276 * Start of Phi1
277 *
278 */
279
280 /* Phi1 fetch */
281 vicii.last_read_phi1 = cycle_phi1_fetch(vicii.cycle_flags);
282
283 /* Check horizontal border flag */
284 check_hborder(vicii.cycle_flags);
285
286 can_sprite_sprite = (vicii.sprite_sprite_collisions == 0);
287 can_sprite_background = (vicii.sprite_background_collisions == 0);
288
289 /* Draw one cycle of pixels */
290 vicii_draw_cycle();
291
292 /* clear any collision registers as initiated by $d01e or $d01f reads */
293 switch (vicii.clear_collisions) {
294 case 0x1e:
295 vicii.sprite_sprite_collisions = 0;
296 vicii.clear_collisions = 0;
297 break;
298 case 0x1f:
299 vicii.sprite_background_collisions = 0;
300 vicii.clear_collisions = 0;
301 break;
302 default:
303 break;
304 }
305
306 /* Trigger collision IRQs */
307 if (can_sprite_sprite && vicii.sprite_sprite_collisions) {
308 vicii_irq_sscoll_set();
309 }
310 if (can_sprite_background && vicii.sprite_background_collisions) {
311 vicii_irq_sbcoll_set();
312 }
313
314 /*
315 *
316 * End of Phi1
317 *
318 ******/
319
320 /******
321 *
322 * Start of Phi2
323 *
324 */
325
326 /* Handle end of line/start of new line */
327 if (vicii.raster_cycle == VICII_PAL_CYCLE(1)) {
328 vicii_cycle_end_of_line();
329 vicii_cycle_start_of_line();
330 }
331
332 if (vicii.start_of_frame) {
333 if (vicii.raster_cycle == VICII_PAL_CYCLE(2)) {
334 vicii_cycle_start_of_frame();
335 }
336 } else {
337 if (vicii.raster_cycle == VICII_PAL_CYCLE(1)) {
338 vicii.raster_line++;
339 }
340 }
341
342 /*
343 * Trigger a raster IRQ if the raster comparison goes from
344 * non-match to match.
345 */
346 if (vicii.raster_line == vicii.raster_irq_line) {
347 if (!vicii.raster_irq_triggered) {
348 vicii_irq_raster_trigger();
349 vicii.raster_irq_triggered = 1;
350 }
351 } else {
352 vicii.raster_irq_triggered = 0;
353 }
354
355 /* Check vertical border flag */
356 check_vborder_top(vicii.raster_line);
357 /* Check vertical border flag */
358 check_vborder_bottom(vicii.raster_line);
359 if (vicii.raster_cycle == VICII_PAL_CYCLE(1)) {
360 vicii.vborder = vicii.set_vborder;
361 }
362
363 /******
364 *
365 * Sprite logic
366 *
367 */
368
369 /* Update sprite mcbase (Cycle 16 on PAL) */
370 /* if (vicii.raster_cycle == VICII_PAL_CYCLE(16)) { */
371 if (cycle_is_update_mcbase(vicii.cycle_flags)) {
372 sprite_mcbase_update();
373 }
374
375 /* Check sprite DMA (Cycles 55 & 56 on PAL) */
376 /* if (vicii.raster_cycle == VICII_PAL_CYCLE(55)
377 || vicii.raster_cycle == VICII_PAL_CYCLE(56) ) { */
378 if (cycle_is_check_spr_dma(vicii.cycle_flags)) {
379 check_sprite_dma();
380 }
381
382 /* Check sprite expansion flags (Cycle 56 on PAL) */
383 /* if (vicii.raster_cycle == VICII_PAL_CYCLE(56)) { */
384 if (cycle_is_check_spr_exp(vicii.cycle_flags)) {
385 check_exp();
386 }
387
388 /* Check sprite display (Cycle 58 on PAL) */
389 /* if (vicii.raster_cycle == VICII_PAL_CYCLE(58)) { */
390 if (cycle_is_check_spr_disp(vicii.cycle_flags)) {
391 check_sprite_display();
392 }
393
394 /******
395 *
396 * Graphics logic
397 *
398 */
399
400 may_crash = !vicii.bad_line && vicii.idle_state; /* flag for "VSP bug" simulation */
401
402 /* Check DEN bit on first DMA line */
403 if ((vicii.raster_line == VICII_FIRST_DMA_LINE) && !vicii.allow_bad_lines) {
404 vicii.allow_bad_lines = (vicii.regs[0x11] & 0x10) ? 1 : 0;
405 }
406
407 /* Check badline condition, trigger fetches */
408 if (vicii.allow_bad_lines) {
409 check_badline();
410 }
411
412 /* simulate the "VSP bug" problem */
413 if(vicii_resources.vsp_bug_enabled) {
414 /* FIXME: no support for "vsp channels", see VSP Lab (http://csdb.dk/release/?id=120810) */
415 if(vicii.bad_line && may_crash && (vicii.raster_cycle >= VICII_PAL_CYCLE(16)) &&
416 (vicii.raster_cycle < VICII_PAL_CYCLE(55))) {
417 int page, row;
418 for(page = 0; page < 256; page++) {
419 int seen0 = 0, seen1 = 0, fragile, result;
420 int firstrow = 7;
421 for(row = firstrow; row <= 0xff; row += 8) {
422 seen0 |= vicii.ram_base_phi1[(page << 8) | row] ^ 255;
423 seen1 |= vicii.ram_base_phi1[(page << 8) | row];
424 }
425 fragile = seen0 & seen1;
426 if(fragile && (lib_unsigned_rand(0, 0xff) < 10)) {
427 result = fragile & lib_unsigned_rand(0, 0xff);
428 for(row = firstrow; row <= 0xff; row += 8) {
429 log_message(vicii.log, "VSP Bug: Corrupting %04x, fragile %02x, new bits %02x", (page << 8) | row, fragile, result);
430 vicii.ram_base_phi1[(page << 8) | row] &= ~fragile;
431 vicii.ram_base_phi1[(page << 8) | row] |= result;
432 }
433 }
434 }
435 }
436 }
437
438 /* Update VC (Cycle 14 on PAL) */
439 /* if (vicii.raster_cycle == VICII_PAL_CYCLE(14)) { */
440 if (cycle_is_update_vc(vicii.cycle_flags)) {
441 vicii.vc = vicii.vcbase;
442 vicii.vmli = 0;
443 if (vicii.bad_line) {
444 vicii.rc = 0;
445 }
446 }
447
448 /* Update RC (Cycle 58 on PAL) */
449 /* if (vicii.raster_cycle == VICII_PAL_CYCLE(58)) { */
450 if (cycle_is_update_rc(vicii.cycle_flags)) {
451 /* `rc' makes the chip go to idle state when it reaches the
452 maximum value. */
453 if (vicii.rc == 7) {
454 vicii.idle_state = 1;
455 vicii.vcbase = vicii.vc;
456 }
457 if (!vicii.idle_state || vicii.bad_line) {
458 vicii.rc = (vicii.rc + 1) & 0x7;
459 vicii.idle_state = 0;
460 }
461 }
462
463 /******
464 *
465 * BA logic
466 *
467 */
468
469 /* Check BA for matrix fetch */
470 if (vicii.bad_line && cycle_is_fetch_ba(vicii.cycle_flags)) {
471 ba_low = 1;
472 }
473
474 /* Check BA for Sprite Phi2 fetch */
475 ba_low |= vicii_check_sprite_ba(vicii.cycle_flags);
476
477 /* if ba_low transitioning from non-active to active, always count
478 3 cycles before allowing any Phi2 accesses. */
479 if (ba_low) {
480 /* count down prefetch cycles */
481 if (vicii.prefetch_cycles) {
482 vicii.prefetch_cycles--;
483 }
484 } else {
485 /* this needs to be +1 because it gets decremented already in the
486 first ba cycle */
487 vicii.prefetch_cycles = 3 + 1;
488 }
489
490
491 /* Matrix fetch */
492 if (vicii.bad_line && cycle_may_fetch_c(vicii.cycle_flags)) {
493 #ifdef DEBUG
494 if (debug.maincpu_traceflg) {
495 log_debug("DMA at cycle %d %d", vicii.raster_cycle, maincpu_clk);
496 }
497 #endif
498 vicii_fetch_matrix();
499 }
500
501 /* clear internal bus (may get set by a VIC-II read or write) */
502 vicii.last_bus_phi2 = 0xff;
503
504 /* delay video mode for fetches by one cycle */
505 vicii.reg11_delay = vicii.regs[0x11];
506
507 /* trigger light pen if scheduled */
508 if (vicii.light_pen.trigger_cycle == maincpu_clk) {
509 vicii_trigger_light_pen_internal(0);
510 }
511
512 return ba_low;
513 }
514
515 /* The REU can use an additional cycle at the point where the dma of sprite 0 is turned on */
516 /* this is because of late setting of BA due to internal delays */
517 /* The CPU can't use this cycle as it checks the state later */
vicii_cycle_reu(void)518 int vicii_cycle_reu(void)
519 {
520 int check = vicii.raster_cycle == VICII_PAL_CYCLE(54) && (vicii.regs[0x15] & 1) && (vicii.regs[1] == (vicii.raster_line & 0xff)) && !(vicii.sprite_dma & 1);
521 return vicii_cycle() && !check;
522 }
523
524 /* Steal cycles from CPU */
vicii_steal_cycles(void)525 void vicii_steal_cycles(void)
526 {
527 int ba_low;
528
529 do {
530 maincpu_clk++;
531 ba_low = vicii_cycle();
532 } while (ba_low);
533 }
534