1 /*
2 * vicii-fetch.c - Phi2 data fetch for the MOS 6569 (VIC-II) emulation.
3 *
4 * Written by
5 * Andreas Boose <viceteam@t-online.de>
6 * Ettore Perazzoli <ettore@comm2000.it>
7 *
8 * DTV sections written by
9 * Hannu Nuotio <hannu.nuotio@tut.fi>
10 * Daniel Kahlin <daniel@kahlin.net>
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 <string.h>
35
36 #include "alarm.h"
37 #include "debug.h"
38 #include "c64cart.h"
39 #include "c64cartmem.h"
40 #include "dma.h"
41 #include "log.h"
42 #include "maincpu.h"
43 #include "mem.h"
44 #include "raster-changes.h"
45 #include "raster-sprite-status.h"
46 #include "raster-sprite.h"
47 #include "raster.h"
48 #include "types.h"
49 #include "vicii-fetch.h"
50 #include "vicii-irq.h"
51 #include "vicii-sprites.h"
52 #include "viciitypes.h"
53
54
55 /* Emulate a matrix line fetch, `num' bytes starting from `offs'. This takes
56 care of the 10-bit counter wraparound. */
vicii_fetch_matrix(int offs,int num,int num_0xff,int cycle)57 void vicii_fetch_matrix(int offs, int num, int num_0xff, int cycle)
58 {
59 int start_char;
60 int c;
61 uint8_t *colorram = NULL;
62
63 /*log_debug("OFF %02i NUM %02i NFF %02i",offs,num,num_0xff);*/
64
65 if (vicii.viciidtv) {
66 num_0xff = 0;
67 colorram = vicii.color_ram_ptr;
68 } else {
69 colorram = mem_color_ram_vicii;
70 }
71
72 if (num_0xff > 0) {
73 if (num <= num_0xff) {
74 memset(vicii.vbuf + offs, 0xff, num);
75 memset(vicii.cbuf + offs, vicii.ram_base_phi2[reg_pc] & 0xf, num);
76 /* FIXME: Crunch table in Multiplexer part of Krestage */
77 vicii.background_color_source = 0xff;
78 } else {
79 memset(vicii.vbuf + offs, 0xff, num_0xff);
80 memset(vicii.cbuf + offs, vicii.ram_base_phi2[reg_pc] & 0xf, num_0xff);
81 }
82 }
83
84 if (num > num_0xff) {
85 offs += num_0xff;
86 num -= num_0xff;
87
88 /* Matrix fetches are done during Phi2, the fabulous "bad lines" */
89 start_char = (vicii.mem_counter + offs) & 0x3ff;
90 c = 0x3ff - start_char + 1;
91
92 if (c >= num) {
93 memcpy(vicii.vbuf + offs, vicii.screen_base_phi2 + start_char, num);
94 if (!vicii.colorfetch_disable) {
95 memcpy(vicii.cbuf + offs, colorram + start_char, num);
96 }
97 } else {
98 memcpy(vicii.vbuf + offs, vicii.screen_base_phi2 + start_char, c);
99 memcpy(vicii.vbuf + offs + c, vicii.screen_base_phi2, num - c);
100
101 if (!vicii.colorfetch_disable) {
102 memcpy(vicii.cbuf + offs, colorram + start_char, c);
103 }
104
105 if (!vicii.colorfetch_disable) {
106 memcpy(vicii.cbuf + offs + c, colorram, num - c);
107 }
108 }
109 vicii.background_color_source = vicii.vbuf[VICII_SCREEN_TEXTCOLS - 1 /*- vicii.buf_offset*/];
110 }
111
112 /* Set correct background color in in the xsmooth area.
113 As this only affects the next line, the xsmooth color is immediately
114 set if the right border is opened. */
115 if (offs + num >= VICII_SCREEN_TEXTCOLS) {
116 switch (vicii.get_background_from_vbuf) {
117 case VICII_HIRES_BITMAP_MODE:
118 raster_changes_next_line_add_int(
119 &vicii.raster,
120 &vicii.raster.xsmooth_color,
121 vicii.background_color_source & 0x0f);
122 break;
123 case VICII_EXTENDED_TEXT_MODE:
124 raster_changes_next_line_add_int(
125 &vicii.raster,
126 &vicii.raster.xsmooth_color,
127 vicii.regs[0x21 + (vicii.background_color_source >> 6)]);
128 break;
129 }
130 }
131 }
132
133 /* If we are on a bad line, do the DMA. Return nonzero if cycles have been
134 stolen. */
do_matrix_fetch(CLOCK sub)135 inline static int do_matrix_fetch(CLOCK sub)
136 {
137 if (!vicii.memory_fetch_done) {
138 raster_t *raster;
139
140 raster = &vicii.raster;
141
142 vicii.memory_fetch_done = 1;
143 vicii.mem_counter = vicii.memptr;
144
145 if ((raster->current_line & 7) == (unsigned int)raster->ysmooth
146 && vicii.allow_bad_lines
147 && raster->current_line >= vicii.first_dma_line
148 && raster->current_line <= vicii.last_dma_line) {
149 vicii_fetch_matrix(0, VICII_SCREEN_TEXTCOLS, 0, VICII_FETCH_CYCLE);
150
151 raster->draw_idle_state = 0;
152 raster->ycounter = 0;
153
154 vicii.idle_state = 0;
155 vicii.idle_data_location = IDLE_NONE;
156 vicii.ycounter_reset_checked = 1;
157 vicii.memory_fetch_done = 2;
158
159 if ((vicii.fastmode == 0) && !vicii.badline_disable && !vicii.colorfetch_disable) {
160 dma_maincpu_steal_cycles(vicii.fetch_clk,
161 VICII_SCREEN_TEXTCOLS + 3 - sub, sub);
162 } else if (vicii.viciidtv && !vicii.colorfetch_disable) {
163 /* Steal cycles from DMA/Blitter */
164 dtvclockneg += VICII_SCREEN_TEXTCOLS + 3;
165 }
166 vicii.bad_line = 1;
167 return 1;
168 }
169 }
170
171 return 0;
172 }
173
handle_fetch_matrix(long offset,CLOCK sub,CLOCK * write_offset)174 inline static int handle_fetch_matrix(long offset, CLOCK sub,
175 CLOCK *write_offset)
176 {
177 raster_t *raster;
178 raster_sprite_status_t *sprite_status;
179
180 *write_offset = 0;
181
182 raster = &vicii.raster;
183 sprite_status = raster->sprite_status;
184
185 if (sprite_status->visible_msk == 0 && sprite_status->dma_msk == 0) {
186 do_matrix_fetch(sub);
187
188 /* As sprites are all turned off, there is no need for a sprite DMA
189 check; next time we will VICII_FETCH_MATRIX again. This works
190 because a VICII_CHECK_SPRITE_DMA is forced in `vic_store()'
191 whenever the mask becomes nonzero. */
192
193 /* This makes sure we only create VICII_FETCH_MATRIX events in the bad
194 line range. These checks are (a little) redundant for safety. */
195 if (raster->current_line < vicii.first_dma_line) {
196 vicii.fetch_clk += ((vicii.first_dma_line - raster->current_line) * vicii.cycles_per_line);
197 } else {
198 if (raster->current_line >= vicii.last_dma_line) {
199 vicii.fetch_clk += ((vicii.screen_height
200 - raster->current_line
201 + vicii.first_dma_line)
202 * vicii.cycles_per_line);
203 } else {
204 vicii.fetch_clk += vicii.cycles_per_line;
205 }
206 }
207
208 alarm_set(vicii.raster_fetch_alarm, vicii.fetch_clk);
209 return 1;
210 } else {
211 int fetch_done;
212
213 fetch_done = do_matrix_fetch(sub);
214
215 /* Sprites might be turned on, check for sprite DMA next
216 time. */
217 vicii.fetch_idx = VICII_CHECK_SPRITE_DMA;
218
219 /* Calculate time for next event. */
220 vicii.fetch_clk = VICII_LINE_START_CLK(maincpu_clk)
221 + vicii.sprite_fetch_cycle;
222
223 if (vicii.fetch_clk > maincpu_clk || offset == 0) {
224 /* Prepare the next fetch event. */
225 alarm_set(vicii.raster_fetch_alarm, vicii.fetch_clk);
226 return 1;
227 }
228
229 if (fetch_done && sub == 0) {
230 *write_offset = VICII_SCREEN_TEXTCOLS + 3;
231 }
232 }
233
234 return 0;
235 }
236
237 /*-----------------------------------------------------------------------*/
238
swap_sprite_data_buffers(void)239 inline static void swap_sprite_data_buffers(void)
240 {
241 uint32_t *tmp;
242 raster_sprite_status_t *sprite_status;
243
244 /* Swap sprite data buffers. */
245 sprite_status = vicii.raster.sprite_status;
246 tmp = sprite_status->sprite_data;
247 sprite_status->sprite_data = sprite_status->new_sprite_data;
248 sprite_status->new_sprite_data = tmp;
249 }
250
251 /* Enable DMA for sprite `num'. */
turn_sprite_dma_on(unsigned int num)252 inline static void turn_sprite_dma_on(unsigned int num)
253 {
254 raster_sprite_status_t *sprite_status;
255 raster_sprite_t *sprite;
256
257 sprite_status = vicii.raster.sprite_status;
258 sprite = sprite_status->sprites + num;
259
260 sprite_status->new_dma_msk |= 1 << num;
261 sprite->dma_flag = 1;
262 sprite->memptr = 0;
263 sprite->exp_flag = sprite->y_expanded ? 0 : 1;
264 sprite->memptr_inc = sprite->exp_flag ? 3 : 0;
265 }
266
check_sprite_dma(void)267 inline static void check_sprite_dma(void)
268 {
269 raster_sprite_status_t *sprite_status;
270 int i, b;
271
272 sprite_status = vicii.raster.sprite_status;
273
274 if (!sprite_status->visible_msk && !sprite_status->dma_msk) {
275 return;
276 }
277
278 sprite_status->new_dma_msk = sprite_status->dma_msk;
279
280 for (i = 0, b = 1; i < VICII_NUM_SPRITES; i++, b <<= 1) {
281 raster_sprite_t *sprite;
282
283 sprite = sprite_status->sprites + i;
284
285 if ((sprite_status->visible_msk & b)
286 && sprite->y == ((int)vicii.raster.current_line & 0xff)
287 && !sprite->dma_flag) {
288 turn_sprite_dma_on(i);
289 } else if (sprite->dma_flag) {
290 sprite->memptr = (sprite->memptr + sprite->memptr_inc) & 0x3f;
291
292 if (sprite->y_expanded) {
293 sprite->exp_flag = !sprite->exp_flag;
294 }
295
296 sprite->memptr_inc = sprite->exp_flag ? 3 : 0;
297
298 if (sprite->memptr == 63) {
299 sprite->dma_flag = 0;
300 sprite_status->new_dma_msk &= ~b;
301
302 if ((sprite_status->visible_msk & b)
303 && sprite->y == ((int)vicii.raster.current_line & 0xff)) {
304 turn_sprite_dma_on(i);
305 }
306 }
307 }
308 }
309 }
310
handle_check_sprite_dma(long offset,CLOCK sub)311 inline static int handle_check_sprite_dma(long offset, CLOCK sub)
312 {
313 swap_sprite_data_buffers();
314
315 check_sprite_dma();
316
317 if (vicii.raster.sprite_status->dma_msk || vicii.raster.sprite_status->new_dma_msk) {
318 vicii_sprites_reset_sprline();
319 }
320
321 /* FIXME? Slow! */
322 vicii.sprite_fetch_clk = (VICII_LINE_START_CLK(maincpu_clk) + vicii.sprite_fetch_cycle);
323 vicii.sprite_fetch_msk = vicii.raster.sprite_status->new_dma_msk;
324
325 if (vicii_sprites_fetch_table[vicii.sprite_fetch_msk][0].cycle == -1) {
326 if (vicii.raster.current_line >= vicii.first_dma_line - 1
327 && vicii.raster.current_line <= vicii.last_dma_line + 1) {
328 vicii.fetch_idx = VICII_FETCH_MATRIX;
329 vicii.fetch_clk = (vicii.sprite_fetch_clk
330 - vicii.sprite_fetch_cycle
331 + VICII_FETCH_CYCLE
332 + vicii.cycles_per_line);
333 } else {
334 vicii.fetch_idx = VICII_CHECK_SPRITE_DMA;
335 vicii.fetch_clk = (vicii.sprite_fetch_clk + vicii.cycles_per_line);
336 }
337 } else {
338 /* Next time, fetch sprite data. */
339 vicii.fetch_idx = VICII_FETCH_SPRITE;
340 vicii.sprite_fetch_idx = 0;
341 vicii.fetch_clk = (vicii.sprite_fetch_clk + vicii_sprites_fetch_table[vicii.sprite_fetch_msk][0].cycle);
342 }
343
344 /*log_debug("HCSD SCLK %i FCLK %i CLK %i OFFSET %li SUB %i",
345 vicii.store_clk, vicii.fetch_clk, clk, offset, sub);*/
346
347 if (vicii.store_clk != CLOCK_MAX) {
348 if (vicii.store_clk + offset - 3 < vicii.fetch_clk) {
349 vicii.ram_base_phi2[vicii.store_addr] = vicii.store_value;
350 }
351 vicii.store_clk = CLOCK_MAX;
352 }
353
354 vicii.num_idle_3fff_old = vicii.num_idle_3fff;
355 if (vicii.num_idle_3fff > 0) {
356 memcpy(vicii.idle_3fff_old, vicii.idle_3fff,
357 sizeof(idle_3fff_t) * vicii.num_idle_3fff);
358 }
359 vicii.num_idle_3fff = 0;
360
361 if (vicii.fetch_clk > maincpu_clk || offset == 0) {
362 alarm_set(vicii.raster_fetch_alarm, vicii.fetch_clk);
363 return 1;
364 }
365
366 return 0;
367 }
368
369 /*-----------------------------------------------------------------------*/
370
handle_fetch_sprite(long offset,CLOCK sub,CLOCK * write_offset)371 inline static int handle_fetch_sprite(long offset, CLOCK sub,
372 CLOCK *write_offset)
373 {
374 const vicii_sprites_fetch_t *sf;
375 unsigned int i;
376 int next_cycle, num_cycles;
377 raster_sprite_status_t *sprite_status;
378 uint8_t *bank_phi1, *bank_phi2, *spr_base;
379
380 sf = &vicii_sprites_fetch_table[vicii.sprite_fetch_msk][vicii.sprite_fetch_idx];
381
382 sprite_status = vicii.raster.sprite_status;
383 /* FIXME: the 3 byte sprite data is instead taken during a Ph1/Ph2/Ph1
384 sequence. This is of minor interest, though, only for CBM-II... */
385 bank_phi1 = vicii.ram_base_phi1 + vicii.vbank_phi1;
386 bank_phi2 = vicii.ram_base_phi2 + vicii.vbank_phi2;
387 spr_base = vicii.screen_base_phi1 + 0x3f8 + sf->first;
388 if (vicii.viciidtv) {
389 spr_base += (vicii.regs[0x4d] << 16);
390 }
391
392 /* Fetch sprite data. */
393 for (i = sf->first; i <= sf->last; i++, spr_base++) {
394 if (vicii.sprite_fetch_msk & (1 << i)) {
395 uint8_t *src_phi1, *src_phi2;
396 uint8_t *dest;
397 int my_memptr;
398
399 #ifdef DEBUG
400 if (debug.maincpu_traceflg) {
401 log_debug("SDMA %i", i);
402 }
403 #endif
404
405 src_phi1 = bank_phi1 + (*spr_base << 6);
406 src_phi2 = bank_phi2 + (*spr_base << 6);
407 my_memptr = sprite_status->sprites[i].memptr;
408 dest = (uint8_t *)(sprite_status->new_sprite_data + i);
409
410 if (export.ultimax_phi1) {
411 /* phi1 fetch from expansion port in ultimax mode */
412 #if 0
413 if (*spr_base >= 0xc0) {
414 /* src_phi1 = (romh_banks + 0x1000 + (romh_bank << 13)
415 + ((*spr_base - 0xc0) << 6)); */
416 src_phi1 = ultimax_romh_phi1_ptr((uint16_t)(0x1000 + ((*spr_base - 0xc0) << 6)));
417 }
418 #endif
419 uint8_t *ptr;
420 if ((ptr = ultimax_romh_phi1_ptr((uint16_t)(0x1000 + ((*spr_base - 0xc0) << 6))))) {
421 if (*spr_base >= 0xc0) {
422 src_phi1 = ptr;
423 }
424 } else {
425 goto phi1noultimax;
426 }
427 } else {
428 phi1noultimax:
429 if (((vicii.vbank_phi1 + (*spr_base << 6)) & vicii.vaddr_chargen_mask_phi1) == vicii.vaddr_chargen_value_phi1) {
430 src_phi1 = mem_chargen_rom_ptr + ((*spr_base & 0x3f) << 6);
431 }
432 }
433
434 if (export.ultimax_phi2) {
435 /* phi2 fetch from expansion port in ultimax mode */
436 #if 0
437 if (*spr_base >= 0xc0) {
438 /* src_phi2 = (romh_banks + 0x1000 + (romh_bank << 13)
439 + ((*spr_base - 0xc0) << 6)); */
440 src_phi2 = ultimax_romh_phi2_ptr((uint16_t)(0x1000 + ((*spr_base - 0xc0) << 6)));
441 }
442 #endif
443 uint8_t *ptr;
444 if ((ptr = ultimax_romh_phi2_ptr((uint16_t)(0x1000 + ((*spr_base - 0xc0) << 6))))) {
445 if (*spr_base >= 0xc0) {
446 src_phi2 = ptr;
447 }
448 } else {
449 goto phi2noultimax;
450 }
451 } else {
452 phi2noultimax:
453 if (((vicii.vbank_phi2 + (*spr_base << 6)) & vicii.vaddr_chargen_mask_phi2) == vicii.vaddr_chargen_value_phi2) {
454 src_phi2 = mem_chargen_rom_ptr + ((*spr_base & 0x3f) << 6);
455 }
456 }
457
458 if (vicii.viciidtv) {
459 src_phi1 += (vicii.regs[0x4d] << 16);
460 src_phi2 += (vicii.regs[0x4d] << 16);
461 }
462
463 dest[0] = src_phi2[my_memptr];
464 dest[1] = src_phi1[++my_memptr & 0x3f];
465 dest[2] = src_phi2[++my_memptr & 0x3f];
466 }
467 }
468
469 num_cycles = sf->num;
470
471 /*log_debug("SF %i VBL %i SUB %i",sf->num,vicii.bad_line,sub);*/
472
473 if ((vicii.fastmode == 0) && !vicii.badline_disable) {
474 dma_maincpu_steal_cycles(vicii.fetch_clk, num_cycles - sub, sub);
475 } else if (vicii.viciidtv) {
476 /* Steal cycles from DMA/Blitter */
477 dtvclockneg += num_cycles;
478 }
479
480 *write_offset = sub == 0 ? num_cycles : 0;
481
482 next_cycle = (sf + 1)->cycle;
483 vicii.sprite_fetch_idx++;
484
485 if (next_cycle == -1) {
486 /* Next time, handle bad lines. */
487 if (vicii.raster.current_line >= vicii.first_dma_line - 1
488 && vicii.raster.current_line <= vicii.last_dma_line + 1) {
489 vicii.fetch_idx = VICII_FETCH_MATRIX;
490 vicii.fetch_clk = (vicii.sprite_fetch_clk
491 - vicii.sprite_fetch_cycle
492 + VICII_FETCH_CYCLE
493 + vicii.cycles_per_line);
494 } else {
495 vicii.fetch_idx = VICII_CHECK_SPRITE_DMA;
496 vicii.fetch_clk = (vicii.sprite_fetch_clk + vicii.cycles_per_line);
497 }
498 } else {
499 vicii.fetch_clk = vicii.sprite_fetch_clk + next_cycle;
500 }
501
502 if (maincpu_clk >= vicii.draw_clk) {
503 vicii_raster_draw_alarm_handler(maincpu_clk - vicii.draw_clk, NULL);
504 }
505
506 if (vicii.fetch_clk > maincpu_clk || offset == 0) {
507 alarm_set(vicii.raster_fetch_alarm, vicii.fetch_clk);
508 return 1;
509 }
510
511 if (maincpu_clk >= vicii.raster_irq_clk) {
512 vicii_irq_alarm_handler(maincpu_clk - vicii.raster_irq_clk, NULL);
513 }
514
515 return 0;
516 }
517
518 /*-----------------------------------------------------------------------*/
519
520 /* Handle sprite/matrix fetch events. FIXME: could be made slightly
521 faster. */
vicii_fetch_alarm_handler(CLOCK offset,void * data)522 void vicii_fetch_alarm_handler(CLOCK offset, void *data)
523 {
524 CLOCK last_opcode_first_write_clk, last_opcode_last_write_clk;
525
526 /* This kludgy thing is used to emulate the behavior of the 6510 when BA
527 goes low. When BA goes low, every read access stops the processor
528 until BA is high again; write accesses happen as usual instead. */
529
530 if (offset > 0) {
531 switch (OPINFO_NUMBER(last_opcode_info)) {
532 case 0:
533 /* In BRK, IRQ and NMI the 3rd, 4th and 5th cycles are write
534 accesses, while the 1st, 2nd, 6th and 7th are read accesses. */
535 last_opcode_first_write_clk = maincpu_clk - 5;
536 last_opcode_last_write_clk = maincpu_clk - 3;
537 break;
538
539 case 0x20:
540 /* In JSR, the 4th and 5th cycles are write accesses, while the
541 1st, 2nd, 3rd and 6th are read accesses. */
542 last_opcode_first_write_clk = maincpu_clk - 3;
543 last_opcode_last_write_clk = maincpu_clk - 2;
544 break;
545
546 default:
547 /* In all the other opcodes, all the write accesses are the last
548 ones. */
549 if (maincpu_num_write_cycles() != 0) {
550 last_opcode_last_write_clk = maincpu_clk - 1;
551 last_opcode_first_write_clk = maincpu_clk
552 - maincpu_num_write_cycles();
553 } else {
554 last_opcode_first_write_clk = (CLOCK)0;
555 last_opcode_last_write_clk = last_opcode_first_write_clk;
556 }
557 break;
558 }
559 } else { /* offset <= 0, i.e. offset == 0 */
560 /* If we are called with no offset, we don't have to care about write
561 accesses. */
562 last_opcode_first_write_clk = last_opcode_last_write_clk = 0;
563 }
564
565 while (1) {
566 CLOCK sub;
567 CLOCK write_offset;
568 int leave;
569
570 if (vicii.fetch_clk < last_opcode_first_write_clk || vicii.fetch_clk > last_opcode_last_write_clk) {
571 sub = 0;
572 } else {
573 sub = last_opcode_last_write_clk - vicii.fetch_clk + 1;
574 }
575
576 switch (vicii.fetch_idx) {
577 case VICII_FETCH_MATRIX:
578 leave = handle_fetch_matrix(offset, sub, &write_offset);
579 last_opcode_first_write_clk += write_offset;
580 last_opcode_last_write_clk += write_offset;
581 break;
582
583 case VICII_CHECK_SPRITE_DMA:
584 leave = handle_check_sprite_dma(offset, sub);
585 break;
586
587 case VICII_FETCH_SPRITE:
588 default: /* Make compiler happy. */
589 leave = handle_fetch_sprite(offset, sub, &write_offset);
590 last_opcode_first_write_clk += write_offset;
591 last_opcode_last_write_clk += write_offset;
592 break;
593 }
594
595 if (leave) {
596 break;
597 }
598 }
599 }
600
vicii_fetch_init(void)601 void vicii_fetch_init(void)
602 {
603 vicii.raster_fetch_alarm = alarm_new(maincpu_alarm_context,
604 "VicIIRasterFetch",
605 vicii_fetch_alarm_handler, NULL);
606 }
607