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