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