vic.cpp
1 /* 2 Copyright Frank Bösing, 2017 3 4 This file is part of Teensy64. 5 6 Teensy64 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 3 of the License, or 9 (at your option) any later version. 10 11 Teensy64 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 Teensy64. If not, see <http://www.gnu.org/licenses/>. 18 19 Diese Datei ist Teil von Teensy64. 20 21 Teensy64 ist Freie Software: Sie können es unter den Bedingungen 22 der GNU General Public License, wie von der Free Software Foundation, 23 Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren 24 veröffentlichten Version, weiterverbreiten und/oder modifizieren. 25 26 Teensy64 wird in der Hoffnung, dass es nützlich sein wird, aber 27 OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite 28 Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. 29 Siehe die GNU General Public License für weitere Details. 30 31 Sie sollten eine Kopie der GNU General Public License zusammen mit diesem 32 Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>. 33 34 */ 35 36 /* 37 TODOs: 38 - Fix Bugs.. 39 - FLD - (OK 08/17) test this more.. 40 - Sprite Stretching (requires "MOBcounter") 41 - BA Signal -> CPU 42 - xFLI 43 - ... 44 - DMA Delay (?) - needs partial rewrite (idle - > badline in middle of line. Is the 3.6 fast enough??) 45 - optimize more 46 */ 47 48 49 #include "Teensy64.h" 50 #include "vic.h" 51 #include <string.h> 52 #include <math.h> 53 #include <stdlib.h> 54 55 #define min(a, b) (((a) < (b)) ? (a) : (b)) 56 #define max(a, b) (((a) > (b)) ? (a) : (b)) 57 58 #define PALETTE(r,g,b) (RGBVAL16(r,g,b)) //(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) 59 #include "vic_palette.h" 60 61 62 63 #define BORDER (240-200)/2 64 #define SCREEN_HEIGHT (200+2*BORDER) 65 #define SCREEN_WIDTH 320 66 //#define LINE_MEM_WIDTH 320 67 #define FIRSTDISPLAYLINE ( 51 - BORDER ) 68 #define LASTDISPLAYLINE ( 250 + BORDER ) 69 #define BORDER_LEFT 0 70 #define BORDER_RIGHT 0 71 72 typedef uint16_t tpixel; 73 74 #define MAXCYCLESSPRITES0_2 3 75 #define MAXCYCLESSPRITES3_7 5 76 #define MAXCYCLESSPRITES (MAXCYCLESSPRITES0_2 + MAXCYCLESSPRITES3_7) 77 78 79 /*****************************************************************************************************/ 80 /*****************************************************************************************************/ 81 /*****************************************************************************************************/ 82 83 inline __attribute__((always_inline)) 84 void fastFillLine(tpixel * p, const tpixel * pe, const uint16_t col, uint16_t * spl); 85 inline __attribute__((always_inline)) 86 void fastFillLineNoSprites(tpixel * p, const tpixel * pe, const uint16_t col); 87 88 89 /*****************************************************************************************************/ 90 /*****************************************************************************************************/ 91 /*****************************************************************************************************/ 92 93 #define SPRITENUM(data) (1 << ((data >> 8) & 0x07)) 94 #define CHARSETPTR() cpu.vic.charsetPtr = cpu.vic.charsetPtrBase + cpu.vic.rc; 95 #define CYCLES(x) {if (cpu.vic.badline) {cia_clockt(x);} else {cpu_clock(x);} } 96 97 #define BADLINE(x) {if (cpu.vic.badline) { \ 98 cpu.vic.lineMemChr[x] = cpu.RAM[cpu.vic.videomatrix + vc + x]; \ 99 cpu.vic.lineMemCol[x] = cpu.vic.COLORRAM[vc + x]; \ 100 cia1_clock(1); \ 101 cia2_clock(1); \ 102 } else { \ 103 cpu_clock(1); \ 104 } \ 105 }; 106 107 #define SPRITEORFIXEDCOLOR() \ 108 sprite = *spl++; \ 109 if (sprite) { \ 110 *p++ = cpu.vic.palette[sprite & 0x0f]; \ 111 } else { \ 112 *p++ = col; \ 113 } 114 115 116 #if 0 117 #define PRINTOVERFLOW \ 118 if (p>pe) { \ 119 Serial.print("VIC overflow Mode "); \ 120 Serial.println(mode); \ 121 } 122 123 #define PRINTOVERFLOWS \ 124 if (p>pe) { \ 125 Serial.print("VIC overflow (Sprite) Mode "); \ 126 Serial.println(mode); \ 127 } 128 #else 129 #define PRINTOVERFLOW 130 #define PRINTOVERFLOWS 131 #endif 132 133 /*****************************************************************************************************/ 134 void mode0 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 135 // Standard-Textmodus(ECM/BMM/MCM=0/0/0) 136 /* 137 Standard-Textmodus (ECM / BMM / MCM = 0/0/0) 138 In diesem Modus (wie in allen Textmodi) liest der VIC aus der videomatrix 8-Bit-Zeichenzeiger, 139 die die Adresse der Punktmatrix des Zeichens im Zeichengenerator angibt. Damit ist ein Zeichensatz 140 von 256 Zeichen verfügbar, die jeweils aus 8×8 Pixeln bestehen, die in 8 aufeinanderfolgenden Bytes 141 im Zeichengenerator abgelegt sind. Mit den Bits VM10-13 und CB11-13 aus Register $d018 lassen sich 142 videomatrix und Zeichengenerator im Speicher verschieben. Im Standard-Textmodus entspricht jedes Bit 143 im Zeichengenerator direkt einem Pixel auf dem Bildschirm. Die Vordergrundfarbe ist für jedes Zeichen 144 im Farbnibble aus der videomatrix angegeben, die Hintergrundfarbe wird global durch Register $d021 festgelegt. 145 146 +----+----+----+----+----+----+----+----+ 147 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 148 +----+----+----+----+----+----+----+----+ 149 | 8 Pixel (1 Bit/Pixel) | 150 | | 151 | "0": Hintergrundfarbe 0 ($d021) | 152 | "1": Farbe aus Bits 8-11 der c-Daten | 153 +---------------------------------------+ 154 155 */ 156 157 uint8_t chr, pixel; 158 uint16_t fgcol; 159 uint16_t bgcol; 160 uint16_t x = 0; 161 162 CHARSETPTR(); 163 164 if (cpu.vic.lineHasSprites) { 165 166 do { 167 168 BADLINE(x); 169 170 chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; 171 fgcol = cpu.vic.lineMemCol[x]; 172 x++; 173 unsigned m = min(8, pe - p); 174 for (unsigned i = 0; i < m; i++) { 175 int sprite = *spl++; 176 177 if (sprite) { // Sprite: Ja 178 int spritenum = SPRITENUM(sprite); 179 int spritepixel = sprite & 0x0f; 180 181 if (sprite & 0x4000) { // Sprite: Hinter Text MDP = 1 182 if (chr & 0x80) { 183 cpu.vic.fgcollision |= spritenum; 184 pixel = fgcol; 185 } else { 186 pixel = spritepixel; 187 } 188 } else { // Sprite: Vor Text //MDP = 0 189 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; 190 pixel = spritepixel; 191 } 192 } else { // Kein Sprite 193 pixel = (chr & 0x80) ? fgcol : cpu.vic.B0C; 194 } 195 196 *p++ = cpu.vic.palette[pixel]; 197 chr = chr << 1; 198 199 } 200 } while (p < pe); 201 PRINTOVERFLOWS 202 } else { //Keine Sprites 203 204 while (p < pe - 8) { 205 206 BADLINE(x) 207 208 chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; 209 fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; 210 bgcol = cpu.vic.colors[1]; 211 x++; 212 213 *p++ = (chr & 0x80) ? fgcol : bgcol; 214 *p++ = (chr & 0x40) ? fgcol : bgcol; 215 *p++ = (chr & 0x20) ? fgcol : bgcol; 216 *p++ = (chr & 0x10) ? fgcol : bgcol; 217 *p++ = (chr & 0x08) ? fgcol : bgcol; 218 *p++ = (chr & 0x04) ? fgcol : bgcol; 219 *p++ = (chr & 0x02) ? fgcol : bgcol; 220 *p++ = (chr & 0x01) ? fgcol : bgcol; 221 222 }; 223 224 while (p < pe) { 225 226 BADLINE(x) 227 228 chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; 229 fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; 230 bgcol = cpu.vic.colors[1]; 231 x++; 232 233 *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; 234 *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; 235 *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; 236 *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; 237 *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; 238 *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; 239 *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; 240 *p++ = (chr & 0x01) ? fgcol : bgcol; 241 }; 242 PRINTOVERFLOW 243 } 244 while (x<40) {BADLINE(x); x++;} 245 }; 246 247 /*****************************************************************************************************/ 248 void mode1 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 249 /* 250 Multicolor-Textmodus (ECM/BMM/MCM=0/0/1) 251 Dieser Modus ermöglicht es, auf Kosten der horizontalen Auflösung vierfarbige Zeichen darzustellen. 252 Ist Bit 11 der c-Daten Null, wird das Zeichen wie im Standard-Textmodus dargestellt, wobei aber nur die 253 Farben 0-7 für den Vordergrund zur Verfügung stehen. Ist Bit 11 gesetzt, bilden jeweils zwei horizontal 254 benachbarte Bits der Punktmatrix ein Pixel. Dadurch ist die Auflösung des Zeichens auf 4×8 reduziert 255 (die Pixel sind doppelt so breit, die Gesamtbreite der Zeichen ändert sich also nicht). 256 Interessant ist, daß nicht nur die Bitkombination „00”, sondern auch „01” für die Spritepriorität 257 und -kollisionserkennung zum "Hintergrund" gezählt wird. 258 259 +----+----+----+----+----+----+----+----+ 260 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 261 +----+----+----+----+----+----+----+----+ 262 | 8 Pixel (1 Bit/Pixel) | 263 | | MC-Flag = 0 264 | "0": Hintergrundfarbe 0 ($d021) | 265 | "1": Farbe aus Bits 8-10 der c-Daten | 266 +---------------------------------------+ 267 | 4 Pixel (2 Bit/Pixel) | 268 | | 269 | "00": Hintergrundfarbe 0 ($d021) | MC-Flag = 1 270 | "01": Hintergrundfarbe 1 ($d022) | 271 | "10": Hintergrundfarbe 2 ($d023) | 272 | "11": Farbe aus Bits 8-10 der c-Daten | 273 +---------------------------------------+ 274 275 */ 276 277 // POKE 53270,PEEK(53270) OR 16 278 // poke 53270,peek(53270) or 16 279 280 uint16_t bgcol, fgcol, pixel; 281 uint16_t colors[4]; 282 uint8_t chr; 283 uint8_t x = 0; 284 285 CHARSETPTR(); 286 287 if (cpu.vic.lineHasSprites) { 288 289 colors[0] = cpu.vic.B0C; 290 291 do { 292 293 if (cpu.vic.idle) { 294 cpu_clock(1); 295 fgcol = colors[1] = colors[2] = colors[3] = 0; 296 chr = cpu.RAM[cpu.vic.bank + 0x3fff]; 297 } else { 298 BADLINE(x); 299 fgcol = cpu.vic.lineMemCol[x]; 300 colors[1] = cpu.vic.R[0x22]; 301 colors[2] = cpu.vic.R[0x23]; 302 colors[3] = fgcol & 0x07; 303 chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; 304 } 305 306 x++; 307 308 if ((fgcol & 0x08) == 0) { //Zeichen ist HIRES 309 310 unsigned m = min(8, pe - p); 311 for (unsigned i = 0; i < m; i++) { 312 313 int sprite = *spl++; 314 315 if (sprite) { // Sprite: Ja 316 317 /* 318 Sprite-Prioritäten (Anzeige) 319 MDP = 1: Grafikhintergrund, Sprite, Vordergrund 320 MDP = 0: Grafikhintergrund, Vordergrund, Sprite 321 322 Kollision: 323 Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, 324 sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. 325 326 */ 327 int spritenum = SPRITENUM(sprite); 328 pixel = sprite & 0x0f; //Hintergrundgrafik 329 if (sprite & 0x4000) { // MDP = 1 330 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 331 cpu.vic.fgcollision |= spritenum; 332 pixel = colors[3]; 333 } 334 } else { // MDP = 0 335 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 336 } 337 338 } else { // Kein Sprite 339 pixel = (chr >> 7) ? colors[3] : colors[0]; 340 } 341 342 *p++ = cpu.vic.palette[pixel]; 343 344 chr = chr << 1; 345 } 346 347 } else {//Zeichen ist MULTICOLOR 348 349 for (unsigned i = 0; i < 4; i++) { 350 if (p >= pe) break; 351 int c = (chr >> 6) & 0x03; 352 chr = chr << 2; 353 354 int sprite = *spl++; 355 356 if (sprite) { // Sprite: Ja 357 int spritenum = SPRITENUM(sprite); 358 pixel = sprite & 0x0f; //Hintergrundgrafik 359 if (sprite & 0x4000) { // MDP = 1 360 361 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 362 cpu.vic.fgcollision |= spritenum; 363 pixel = colors[c]; 364 } 365 366 } else { // MDP = 0 367 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 368 } 369 370 } else { // Kein Sprite 371 pixel = colors[c]; 372 373 } 374 *p++ = cpu.vic.palette[pixel]; 375 if (p >= pe) break; 376 377 sprite = *spl++; 378 379 //Das gleiche nochmal für das nächste Pixel 380 if (sprite) { // Sprite: Ja 381 int spritenum = SPRITENUM(sprite); 382 pixel = sprite & 0x0f; //Hintergrundgrafik 383 if (sprite & 0x4000) { // MDP = 1 384 385 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 386 cpu.vic.fgcollision |= spritenum; 387 pixel = colors[c]; 388 } 389 } else { // MDP = 0 390 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 391 } 392 } else { // Kein Sprite 393 pixel = colors[c]; 394 } 395 *p++ = cpu.vic.palette[pixel]; 396 397 } 398 399 } 400 401 } while (p < pe); 402 PRINTOVERFLOWS 403 } else { //Keine Sprites 404 405 while (p < pe - 8) { 406 407 int c; 408 409 bgcol = cpu.vic.colors[1]; 410 colors[0] = bgcol; 411 412 if (cpu.vic.idle) { 413 cpu_clock(1); 414 c = colors[1] = colors[2] = colors[3] = 0; 415 chr = cpu.RAM[cpu.vic.bank + 0x3fff]; 416 } else { 417 BADLINE(x); 418 419 colors[1] = cpu.vic.colors[2]; 420 colors[2] = cpu.vic.colors[3]; 421 422 chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; 423 c = cpu.vic.lineMemCol[x]; 424 } 425 426 x++; 427 428 if ((c & 0x08) == 0) { //Zeichen ist HIRES 429 fgcol = cpu.vic.palette[c & 0x07]; 430 *p++ = (chr & 0x80) ? fgcol : bgcol; 431 *p++ = (chr & 0x40) ? fgcol : bgcol; 432 *p++ = (chr & 0x20) ? fgcol : bgcol; 433 *p++ = (chr & 0x10) ? fgcol : bgcol; 434 *p++ = (chr & 0x08) ? fgcol : bgcol; 435 *p++ = (chr & 0x04) ? fgcol : bgcol; 436 *p++ = (chr & 0x02) ? fgcol : bgcol; 437 *p++ = (chr & 0x01) ? fgcol : bgcol; 438 } else {//Zeichen ist MULTICOLOR 439 440 colors[3] = cpu.vic.palette[c & 0x07]; 441 pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; *p++ = pixel; 442 pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; *p++ = pixel; 443 pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; *p++ = pixel; 444 pixel = colors[(chr ) & 0x03]; *p++ = pixel; *p++ = pixel; 445 } 446 447 }; 448 449 while (p < pe) { 450 451 int c; 452 453 bgcol = cpu.vic.colors[1]; 454 colors[0] = bgcol; 455 456 if (cpu.vic.idle) { 457 cpu_clock(1); 458 c = colors[1] = colors[2] = colors[3] = 0; 459 chr = cpu.RAM[cpu.vic.bank + 0x3fff]; 460 } else { 461 BADLINE(x); 462 463 colors[1] = cpu.vic.colors[2]; 464 colors[2] = cpu.vic.colors[3]; 465 466 chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; 467 c = cpu.vic.lineMemCol[x]; 468 } 469 470 x++; 471 472 if ((c & 0x08) == 0) { //Zeichen ist HIRES 473 fgcol = cpu.vic.palette[c & 0x07]; 474 *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; 475 *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; 476 *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; 477 *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; 478 *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; 479 *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; 480 *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; 481 *p++ = (chr & 0x01) ? fgcol : bgcol; 482 } else {//Zeichen ist MULTICOLOR 483 484 colors[3] = cpu.vic.palette[c & 0x07]; 485 pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; 486 pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; 487 pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; 488 pixel = colors[(chr ) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; 489 } 490 491 }; 492 PRINTOVERFLOW 493 } 494 while (x<40) {BADLINE(x); x++;} 495 } 496 497 /*****************************************************************************************************/ 498 void mode2 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 499 /* 500 Standard-Bitmap-Modus (ECM / BMM / MCM = 0/1/0) ("HIRES") 501 In diesem Modus (wie in allen Bitmap-Modi) liest der VIC die Grafikdaten aus einer 320×200-Bitmap, 502 in der jedes Bit direkt einem Punkt auf dem Bildschirm entspricht. Die Daten aus der videomatrix 503 werden für die Farbinformation benutzt. Da die videomatrix weiterhin nur eine 40×25-Matrix ist, 504 können die Farben nur für Blöcke von 8×8 Pixeln individuell bestimmt werden (also eine Art YC-8:1-Format). 505 Da die Entwickler des VIC-II den Bitmap-Modus mit sowenig zusätzlichem Schaltungsaufwand wie möglich realisieren wollten 506 (der VIC-I hatte noch keinen Bitmap-Modus), ist die Bitmap etwas ungewöhnlich im Speicher abgelegt: 507 Im Gegensatz zu modernen Videochips, die die Bitmap linear aus dem Speicher lesen, bilden beim VIC jeweils 8 aufeinanderfolgende Bytes einen 8×8-Pixelblock 508 auf dem Bildschirm. Mit den Bits VM10-13 und CB13 aus Register $d018 lassen sich videomatrix und Bitmap im Speicher verschieben. 509 Im Standard-Bitmap-Modus entspricht jedes Bit in der Bitmap direkt einem Pixel auf dem Bildschirm. 510 Für jeden 8×8-Block können Vorder- und Hintergrundfarbe beliebig eingestellt werden. 511 512 +----+----+----+----+----+----+----+----+ 513 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 514 +----+----+----+----+----+----+----+----+ 515 | 8 Pixel (1 Bit/Pixel) | 516 | | 517 | "0": Farbe aus Bits 0-3 der c-Daten | 518 | "1": Farbe aus Bits 4-7 der c-Daten | 519 +---------------------------------------+ 520 521 522 http://www.devili.iki.fi/Computers/Commodore/C64/Programmers_Reference/Chapter_3/page_127.html 523 */ 524 525 uint8_t chr; 526 uint16_t fgcol, pixel; 527 uint16_t bgcol; 528 uint8_t x = 0; 529 uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; 530 531 if (cpu.vic.lineHasSprites) { 532 do { 533 534 BADLINE(x); 535 536 uint8_t t = cpu.vic.lineMemChr[x]; 537 fgcol = t >> 4; 538 bgcol = t & 0x0f; 539 chr = bP[x * 8]; 540 541 x++; 542 543 unsigned m = min(8, pe - p); 544 for (unsigned i = 0; i < m; i++) { 545 546 int sprite = *spl++; 547 548 chr = chr << 1; 549 if (sprite) { // Sprite: Ja 550 /* 551 Sprite-Prioritäten (Anzeige) 552 MDP = 1: Grafikhintergrund, Sprite, Vordergrund 553 MDP = 0: Grafikhintergung, Vordergrund, Sprite 554 555 Kollision: 556 Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, 557 sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. 558 559 */ 560 int spritenum = SPRITENUM(sprite); 561 pixel = sprite & 0x0f; //Hintergrundgrafik 562 if (sprite & 0x4000) { // MDP = 1 563 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 564 cpu.vic.fgcollision |= spritenum; 565 pixel = fgcol; 566 } 567 } else { // MDP = 0 568 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 569 } 570 571 } else { // Kein Sprite 572 pixel = (chr & 0x80) ? fgcol :cpu.vic.B0C; 573 } 574 575 *p++ = cpu.vic.palette[pixel]; 576 577 } 578 } while (p < pe); 579 PRINTOVERFLOWS 580 } else { //Keine Sprites 581 582 while (p < pe - 8) { 583 //color-ram not used! 584 BADLINE(x); 585 586 uint8_t t = cpu.vic.lineMemChr[x]; 587 fgcol = cpu.vic.palette[t >> 4]; 588 bgcol = cpu.vic.palette[t & 0x0f]; 589 chr = bP[x * 8]; 590 x++; 591 592 *p++ = (chr & 0x80) ? fgcol : bgcol; 593 *p++ = (chr & 0x40) ? fgcol : bgcol; 594 *p++ = (chr & 0x20) ? fgcol : bgcol; 595 *p++ = (chr & 0x10) ? fgcol : bgcol; 596 *p++ = (chr & 0x08) ? fgcol : bgcol; 597 *p++ = (chr & 0x04) ? fgcol : bgcol; 598 *p++ = (chr & 0x02) ? fgcol : bgcol; 599 *p++ = (chr & 0x01) ? fgcol : bgcol; 600 }; 601 while (p < pe) { 602 //color-ram not used! 603 BADLINE(x); 604 605 uint8_t t = cpu.vic.lineMemChr[x]; 606 fgcol = cpu.vic.palette[t >> 4]; 607 bgcol = cpu.vic.palette[t & 0x0f]; 608 chr = bP[x * 8]; 609 610 x++; 611 612 *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; 613 *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; 614 *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; 615 *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; 616 *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; 617 *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; 618 *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; 619 *p++ = (chr & 0x01) ? fgcol : bgcol; 620 621 }; 622 PRINTOVERFLOW 623 } 624 while (x<40) {BADLINE(x); x++;} 625 } 626 /*****************************************************************************************************/ 627 void mode3 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 628 /* 629 Multicolor-Bitmap-Modus (ECM/BMM/MCM=0/1/1) 630 631 Ähnlich wie beim Multicolor-Textmodus bilden auch in diesem Modus jeweils 632 zwei benachbarte Bits ein (doppelt so breites) Pixel. Die Auflösung 633 reduziert sich damit auf 160×200 Pixel. 634 635 Genau wie beim Multicolor-Textmodus wird die Bitkombination "01" für die 636 Spritepriorität und -kollisionserkennung zum "Hintergrund" gezählt. 637 638 +----+----+----+----+----+----+----+----+ 639 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 640 +----+----+----+----+----+----+----+----+ 641 | 4 Pixel (2 Bit/Pixel) | 642 | | 643 | "00": Hintergrundfarbe 0 ($d021) | 644 | "01": Farbe aus Bits 4-7 der c-Daten | 645 | "10": Farbe aus Bits 0-3 der c-Daten | 646 | "11": Farbe aus Bits 8-11 der c-Daten | 647 +---------------------------------------+ 648 649 POKE 53265,PEEK(53625)OR 32: POKE 53270,PEEK(53270)OR 16 650 */ 651 uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; 652 uint16_t colors[4]; 653 uint16_t pixel; 654 uint8_t chr, x; 655 656 x = 0; 657 658 if (cpu.vic.lineHasSprites) { 659 colors[0] = cpu.vic.B0C; 660 do { 661 662 if (cpu.vic.idle) { 663 cpu_clock(1); 664 colors[1] = colors[2] = colors[3] = 0; 665 chr = cpu.RAM[cpu.vic.bank + 0x3fff]; 666 } else { 667 BADLINE(x); 668 uint8_t t = cpu.vic.lineMemChr[x]; 669 colors[1] = t >> 4;//10 670 colors[2] = t & 0x0f; //01 671 colors[3] = cpu.vic.lineMemCol[x]; 672 chr = bP[x * 8]; 673 }; 674 675 x++; 676 677 for (unsigned i = 0; i < 4; i++) { 678 if (p >= pe) break; 679 uint32_t c = (chr >> 6) & 0x03; 680 chr = chr << 2; 681 682 int sprite = *spl++; 683 684 if (sprite) { // Sprite: Ja 685 int spritenum = SPRITENUM(sprite); 686 pixel = sprite & 0x0f; //Hintergrundgrafik 687 if (sprite & 0x4000) { // MDP = 1 688 if (c & 0x02) { //Vordergrundpixel ist gesetzt 689 cpu.vic.fgcollision |= spritenum; 690 pixel = colors[c]; 691 } 692 } else { // MDP = 0 693 if (c & 0x02) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt 694 } 695 } else { // Kein Sprite 696 pixel = colors[c]; 697 } 698 699 *p++ = cpu.vic.palette[pixel]; 700 if (p >= pe) break; 701 702 sprite = *spl++; 703 704 if (sprite) { // Sprite: Ja 705 int spritenum = SPRITENUM(sprite); 706 pixel = sprite & 0x0f; //Hintergrundgrafik 707 if (sprite & 0x4000) { // MDP = 1 708 709 if (c & 0x02) { //Vordergrundpixel ist gesetzt 710 cpu.vic.fgcollision |= spritenum; 711 pixel = colors[c]; 712 } 713 } else { // MDP = 0 714 if (c & 0x02) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt 715 } 716 } else { // Kein Sprite 717 pixel = colors[c]; 718 } 719 720 *p++ = cpu.vic.palette[pixel]; 721 722 } 723 724 } while (p < pe); 725 PRINTOVERFLOWS 726 727 } else { //Keine Sprites 728 729 while (p < pe - 8) { 730 731 colors[0] = cpu.vic.colors[1]; 732 733 if (cpu.vic.idle) { 734 cpu_clock(1); 735 colors[1] = colors[2] = colors[3] = 0; 736 chr = cpu.RAM[cpu.vic.bank + 0x3fff]; 737 } else { 738 BADLINE(x); 739 740 uint8_t t = cpu.vic.lineMemChr[x]; 741 colors[1] = cpu.vic.palette[t >> 4];//10 742 colors[2] = cpu.vic.palette[t & 0x0f]; //01 743 colors[3] = cpu.vic.palette[cpu.vic.lineMemCol[x]]; 744 chr = bP[x * 8]; 745 } 746 747 x++; 748 pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; *p++ = pixel; 749 pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; *p++ = pixel; 750 pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; *p++ = pixel; 751 pixel = colors[chr & 0x03]; *p++ = pixel; *p++ = pixel; 752 753 }; 754 while (p < pe) { 755 756 colors[0] = cpu.vic.colors[1]; 757 758 if (cpu.vic.idle) { 759 cpu_clock(1); 760 colors[1] = colors[2] = colors[3] = 0; 761 chr = cpu.RAM[cpu.vic.bank + 0x3fff]; 762 } else { 763 BADLINE(x); 764 765 uint8_t t = cpu.vic.lineMemChr[x]; 766 colors[1] = cpu.vic.palette[t >> 4];//10 767 colors[2] = cpu.vic.palette[t & 0x0f]; //01 768 colors[3] = cpu.vic.palette[cpu.vic.lineMemCol[x]]; 769 chr = bP[x * 8]; 770 } 771 772 x++; 773 pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; 774 pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; 775 pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; 776 pixel = colors[chr & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; 777 778 }; 779 PRINTOVERFLOW 780 } 781 while (x<40) {BADLINE(x); x++;} 782 } 783 /*****************************************************************************************************/ 784 void mode4 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 785 //ECM-Textmodus (ECM/BMM/MCM=1/0/0) 786 /* 787 Dieser Textmodus entspricht dem Standard-Textmodus, erlaubt es aber, für 788 jedes einzelne Zeichen eine von vier Hintergrundfarben auszuwählen. Die 789 Auswahl geschieht über die oberen beiden Bits des Zeichenzeigers. Dadurch 790 reduziert sich der Zeichenvorrat allerdings von 256 auf 64 Zeichen. 791 792 +----+----+----+----+----+----+----+----+ 793 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 794 +----+----+----+----+----+----+----+----+ 795 | 8 Pixel (1 Bit/Pixel) | 796 | | 797 | "0": Je nach Bits 6/7 der c-Daten | 798 | 00: Hintergrundfarbe 0 ($d021) | 799 | 01: Hintergrundfarbe 1 ($d022) | 800 | 10: Hintergrundfarbe 2 ($d023) | 801 | 11: Hintergrundfarbe 3 ($d024) | 802 | "1": Farbe aus Bits 8-11 der c-Daten | 803 +---------------------------------------+ 804 */ 805 // https://www.c64-wiki.de/wiki/Hintergrundfarbe 806 // POKE 53265, PEEK(53265) OR 64:REM CURSOR BLINKT ROT abc 807 808 uint8_t chr, pixel; 809 uint16_t fgcol; 810 uint16_t bgcol; 811 uint8_t x = 0; 812 813 CHARSETPTR(); 814 if (cpu.vic.lineHasSprites) { 815 do { 816 817 BADLINE(x); 818 819 uint32_t td = cpu.vic.lineMemChr[x]; 820 bgcol = cpu.vic.R[0x21 + ((td >> 6) & 0x03)]; 821 chr = cpu.vic.charsetPtr[(td & 0x3f) * 8]; 822 fgcol = cpu.vic.lineMemCol[x]; 823 824 x++; 825 826 unsigned m = min(8, pe - p); 827 for (unsigned i = 0; i < m; i++) { 828 829 int sprite = *spl++; 830 831 if (sprite) { // Sprite: Ja 832 int spritenum = SPRITENUM(sprite); 833 if (sprite & 0x4000) { // Sprite: Hinter Text 834 if (chr & 0x80) { 835 cpu.vic.fgcollision |= spritenum; 836 pixel = fgcol; 837 } else pixel = bgcol; 838 } else { // Sprite: Vor Text 839 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; 840 pixel = sprite & 0x0f; 841 } 842 } else { // Kein Sprite 843 pixel = (chr & 0x80) ? fgcol : bgcol; 844 } 845 846 chr = chr << 1; 847 *p++ = cpu.vic.palette[pixel]; 848 } 849 } while (p < pe); 850 PRINTOVERFLOWS 851 } 852 else //Keine Sprites 853 while (p < pe - 8) { 854 855 BADLINE(x); 856 857 uint32_t td = cpu.vic.lineMemChr[x]; 858 bgcol = cpu.vic.palette[cpu.vic.R[0x21 + ((td >> 6) & 0x03)]]; 859 chr = cpu.vic.charsetPtr[(td & 0x3f) * 8]; 860 fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; 861 x++; 862 863 *p++ = (chr & 0x80) ? fgcol : bgcol; 864 *p++ = (chr & 0x40) ? fgcol : bgcol; 865 *p++ = (chr & 0x20) ? fgcol : bgcol; 866 *p++ = (chr & 0x10) ? fgcol : bgcol; 867 *p++ = (chr & 0x08) ? fgcol : bgcol; 868 *p++ = (chr & 0x04) ? fgcol : bgcol; 869 *p++ = (chr & 0x02) ? fgcol : bgcol; 870 *p++ = (chr & 0x01) ? fgcol : bgcol; 871 872 }; 873 while (p < pe) { 874 875 BADLINE(x); 876 877 uint32_t td = cpu.vic.lineMemChr[x]; 878 bgcol = cpu.vic.palette[cpu.vic.R[0x21 + ((td >> 6) & 0x03)]]; 879 chr = cpu.vic.charsetPtr[(td & 0x3f) * 8]; 880 fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; 881 882 x++; 883 884 *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; 885 *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; 886 *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; 887 *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; 888 *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; 889 *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; 890 *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; 891 *p++ = (chr & 0x01) ? fgcol : bgcol; 892 893 }; 894 PRINTOVERFLOW 895 while (x<40) {BADLINE(x); x++;} 896 } 897 898 /*****************************************************************************************************/ 899 /* Ungültige Modi ************************************************************************************/ 900 /*****************************************************************************************************/ 901 902 void mode5 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 903 /* 904 Ungültiger Textmodus (ECM/BMM/MCM=1/0/1) 905 906 Das gleichzeitige Setzen der ECM- und MCM-Bits wählt keinen der 907 "offiziellen" Grafikmodi des VIC, sondern erzeugt nur schwarze Pixel. 908 Nichtsdestotrotz erzeugt der Grafikdatensequenzer auch in diesem Modus 909 intern gültige Grafikdaten, die die Spritekollisionserkennung triggern 910 können. Über den Umweg der Spritekollisionen kann man die erzeugten Daten 911 auch auslesen (sehen kann man nichts, das Bild ist schwarz). Man kann so 912 allerdings nur Vordergrund- und Hintergrundpixel unterscheiden, die 913 Farbinformation läßt sich aus den Spritekollisionen nicht gewinnen. 914 915 Die erzeugte Grafik entspricht der des Multicolor-Textmodus, allerdings ist 916 der Zeichenvorrat genau wie im ECM-Modus auf 64 Zeichen eingeschränkt. 917 */ 918 CHARSETPTR(); 919 920 uint8_t chr, pixel; 921 uint16_t fgcol; 922 uint8_t x = 0; 923 924 if (cpu.vic.lineHasSprites) { 925 926 do { 927 928 BADLINE(x); 929 930 chr = cpu.vic.charsetPtr[(cpu.vic.lineMemChr[x] & 0x3F) * 8]; 931 fgcol = cpu.vic.lineMemCol[x]; 932 933 x++; 934 935 if ((fgcol & 0x08) == 0) { //Zeichen ist HIRES 936 937 unsigned m = min(8, pe - p); 938 for (unsigned i = 0; i < m; i++) { 939 940 int sprite = *spl; 941 *spl++ = 0; 942 943 if (sprite) { // Sprite: Ja 944 945 /* 946 Sprite-Prioritäten (Anzeige) 947 MDP = 1: Grafikhintergrund, Sprite, Vordergrund 948 MDP = 0: Grafikhintergrund, Vordergrund, Sprite 949 950 Kollision: 951 Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, 952 sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. 953 954 */ 955 int spritenum = SPRITENUM(sprite); 956 pixel = sprite & 0x0f; //Hintergrundgrafik 957 958 if (sprite & 0x4000) { // MDP = 1 959 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 960 cpu.vic.fgcollision |= spritenum; 961 pixel = 0; 962 } 963 } else { // MDP = 0 964 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 965 } 966 967 } else { // Kein Sprite 968 pixel = 0; 969 } 970 971 *p++ = cpu.vic.palette[pixel]; 972 973 chr = chr << 1; 974 } 975 976 } else {//Zeichen ist MULTICOLOR 977 978 for (unsigned i = 0; i < 4; i++) { 979 if (p >= pe) break; 980 981 chr = chr << 2; 982 983 int sprite = *spl; 984 *spl++ = 0; 985 986 if (sprite) { // Sprite: Ja 987 int spritenum = SPRITENUM(sprite); 988 pixel = sprite & 0x0f; //Hintergrundgrafik 989 if (sprite & 0x4000) { // MDP = 1 990 991 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 992 cpu.vic.fgcollision |= spritenum; 993 pixel = 0; 994 } 995 } else { // MDP = 0 996 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 997 } 998 999 } else { // Kein Sprite 1000 pixel = 0; 1001 1002 } 1003 *p++ = cpu.vic.palette[pixel]; 1004 if (p >= pe) break; 1005 1006 sprite = *spl; 1007 *spl++ = 0; 1008 //Das gleiche nochmal für das nächste Pixel 1009 if (sprite) { // Sprite: Ja 1010 int spritenum = SPRITENUM(sprite); 1011 pixel = sprite & 0x0f; //Hintergrundgrafik 1012 if (sprite & 0x4000) { // MDP = 1 1013 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 1014 cpu.vic.fgcollision |= spritenum; 1015 pixel = 0; 1016 } 1017 } else { // MDP = 0 1018 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 1019 } 1020 } else { // Kein Sprite 1021 pixel = 0; 1022 } 1023 *p++ = cpu.vic.palette[pixel]; 1024 1025 } 1026 1027 } 1028 1029 } while (p < pe); 1030 PRINTOVERFLOWS 1031 1032 } else { //Keine Sprites 1033 //Farbe immer schwarz 1034 const uint16_t bgcol = palette[0]; 1035 while (p < pe - 8) { 1036 1037 BADLINE(x); 1038 x++; 1039 *p++ = bgcol; *p++ = bgcol; 1040 *p++ = bgcol; *p++ = bgcol; 1041 *p++ = bgcol; *p++ = bgcol; 1042 *p++ = bgcol; *p++ = bgcol; 1043 1044 }; 1045 while (p < pe) { 1046 1047 BADLINE(x); 1048 x++; 1049 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1050 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1051 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1052 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; 1053 1054 }; 1055 PRINTOVERFLOW 1056 } 1057 while (x<40) {BADLINE(x); x++;} 1058 } 1059 /*****************************************************************************************************/ 1060 void mode6 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 1061 /* 1062 Ungültiger Bitmap-Modus 1 (ECM/BMM/MCM=1/1/0) 1063 1064 Dieser Modus erzeugt nur ebenfalls nur ein schwarzes Bild, die Pixel lassen 1065 sich allerdings auch hier mit dem Spritekollisionstrick auslesen. 1066 1067 Der Aufbau der Grafik ist im Prinzip wie im Standard-Bitmap-Modus, aber die 1068 Bits 9 und 10 der g-Adressen sind wegen dem gesetzten ECM-Bit immer Null, 1069 entsprechend besteht auch die Grafik - grob gesagt - aus vier 1070 "Abschnitten", die jeweils viermal wiederholt dargestellt werden. 1071 1072 */ 1073 1074 uint8_t chr, pixel; 1075 uint8_t x = 0; 1076 uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; 1077 1078 if (cpu.vic.lineHasSprites) { 1079 1080 do { 1081 1082 BADLINE(x); 1083 1084 chr = bP[x * 8]; 1085 1086 x++; 1087 1088 unsigned m = min(8, pe - p); 1089 for (unsigned i = 0; i < m; i++) { 1090 1091 int sprite = *spl; 1092 *spl++ = 0; 1093 1094 chr = chr << 1; 1095 if (sprite) { // Sprite: Ja 1096 /* 1097 Sprite-Prioritäten (Anzeige) 1098 MDP = 1: Grafikhintergrund, Sprite, Vordergrund 1099 MDP = 0: Grafikhintergung, Vordergrund, Sprite 1100 1101 Kollision: 1102 Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, 1103 sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. 1104 1105 */ 1106 int spritenum = SPRITENUM(sprite); 1107 pixel = sprite & 0x0f; //Hintergrundgrafik 1108 if (sprite & 0x4000) { // MDP = 1 1109 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 1110 cpu.vic.fgcollision |= spritenum; 1111 pixel = 0; 1112 } 1113 } else { // MDP = 0 1114 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt 1115 } 1116 1117 } else { // Kein Sprite 1118 pixel = 0; 1119 } 1120 1121 *p++ = cpu.vic.palette[pixel]; 1122 1123 } 1124 1125 } while (p < pe); 1126 PRINTOVERFLOWS 1127 1128 } else { //Keine Sprites 1129 //Farbe immer schwarz 1130 const uint16_t bgcol = palette[0]; 1131 while (p < pe - 8) { 1132 1133 BADLINE(x); 1134 x++; 1135 *p++ = bgcol; *p++ = bgcol; 1136 *p++ = bgcol; *p++ = bgcol; 1137 *p++ = bgcol; *p++ = bgcol; 1138 *p++ = bgcol; *p++ = bgcol; 1139 1140 }; 1141 while (p < pe) { 1142 1143 BADLINE(x); 1144 x++; 1145 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1146 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1147 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1148 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; 1149 1150 }; 1151 PRINTOVERFLOW 1152 } 1153 while (x<40) {BADLINE(x); x++;} 1154 } 1155 /*****************************************************************************************************/ 1156 void mode7 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { 1157 /* 1158 Ungültiger Bitmap-Modus 2 (ECM/BMM/MCM=1/1/1) 1159 1160 Der letzte ungültige Modus liefert auch ein schwarzes Bild, das sich jedoch 1161 genauso mit Hilfe der Sprite-Grafik-Kollisionen "abtasten" läßt. 1162 1163 Der Aufbau der Grafik ist im Prinzip wie im Multicolor-Bitmap-Modus, aber 1164 die Bits 9 und 10 der g-Adressen sind wegen dem gesetzten ECM-Bit immer 1165 Null, was sich in der Darstellung genauso wie beim ersten ungültigen 1166 Bitmap-Modus wiederspiegelt. Die Bitkombination "01" wird wie gewohnt zum 1167 Hintergrund gezählt. 1168 1169 */ 1170 1171 uint8_t chr; 1172 uint8_t x = 0; 1173 uint16_t pixel; 1174 uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; 1175 1176 if (cpu.vic.lineHasSprites) { 1177 1178 do { 1179 1180 BADLINE(x); 1181 1182 chr = bP[x * 8]; 1183 x++; 1184 1185 for (unsigned i = 0; i < 4; i++) { 1186 if (p >= pe) break; 1187 1188 chr = chr << 2; 1189 1190 int sprite = *spl; 1191 *spl++ = 0; 1192 1193 if (sprite) { // Sprite: Ja 1194 int spritenum = SPRITENUM(sprite); 1195 pixel = sprite & 0x0f;//Hintergrundgrafik 1196 if (sprite & 0x4000) { // MDP = 1 1197 1198 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 1199 cpu.vic.fgcollision |= spritenum; 1200 pixel = 0; 1201 } 1202 } else { // MDP = 0 1203 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt 1204 } 1205 } else { // Kein Sprite 1206 pixel = 0; 1207 } 1208 1209 *p++ = cpu.vic.palette[pixel]; 1210 if (p >= pe) break; 1211 1212 sprite = *spl; 1213 *spl++ = 0; 1214 1215 if (sprite) { // Sprite: Ja 1216 int spritenum = SPRITENUM(sprite); 1217 pixel = sprite & 0x0f;//Hintergrundgrafik 1218 if (sprite & 0x4000) { // MDP = 1 1219 1220 if (chr & 0x80) { //Vordergrundpixel ist gesetzt 1221 cpu.vic.fgcollision |= spritenum; 1222 pixel = 0; 1223 } 1224 } else { // MDP = 0 1225 if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt 1226 } 1227 } else { // Kein Sprite 1228 pixel = 0; 1229 } 1230 1231 *p++ = cpu.vic.palette[pixel]; 1232 1233 } 1234 1235 } while (p < pe); 1236 PRINTOVERFLOWS 1237 1238 } else { //Keine Sprites 1239 1240 const uint16_t bgcol = palette[0]; 1241 while (p < pe - 8) { 1242 1243 BADLINE(x); 1244 x++; 1245 *p++ = bgcol; *p++ = bgcol; 1246 *p++ = bgcol; *p++ = bgcol; 1247 *p++ = bgcol; *p++ = bgcol; 1248 *p++ = bgcol; *p++ = bgcol; 1249 1250 }; 1251 while (p < pe) { 1252 1253 BADLINE(x); 1254 x++; 1255 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1256 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1257 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; 1258 *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; 1259 1260 }; 1261 PRINTOVERFLOW 1262 1263 } 1264 while (x<40) {BADLINE(x); x++;} 1265 } 1266 /*****************************************************************************************************/ 1267 /*****************************************************************************************************/ 1268 /*****************************************************************************************************/ 1269 1270 typedef void (*modes_t)( tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc ); //Funktionspointer 1271 const modes_t modes[8] = {mode0, mode1, mode2, mode3, mode4, mode5, mode6, mode7}; 1272 1273 1274 static tpixel linebuffer[SCREEN_WIDTH]; 1275 1276 void vic_do(void) { 1277 1278 uint16_t vc; 1279 uint16_t xscroll; 1280 tpixel *pe; 1281 tpixel *p; 1282 uint16_t *spl; 1283 uint8_t mode; 1284 1285 /*****************************************************************************************************/ 1286 /* Linecounter ***************************************************************************************/ 1287 /*****************************************************************************************************/ 1288 /* 1289 ?PEEK(678) NTSC =0 1290 ?PEEK(678) PAL = 1 1291 */ 1292 1293 if ( cpu.vic.rasterLine >= LINECNT ) { 1294 1295 //reSID sound needs much time - too much to keep everything in sync and with stable refreshrate 1296 //but it is not called very often, so most of the time, we have more time than needed. 1297 //We can measure the time needed for a frame and calc a correction factor to speed things up. 1298 unsigned long m = fbmicros(); 1299 cpu.vic.neededTime = (m - cpu.vic.timeStart); 1300 cpu.vic.timeStart = m; 1301 cpu.vic.lineClock.setIntervalFast(LINETIMER_DEFAULT_FREQ - ((float)cpu.vic.neededTime / (float)LINECNT - LINETIMER_DEFAULT_FREQ )); 1302 1303 cpu.vic.rasterLine = 0; 1304 cpu.vic.vcbase = 0; 1305 cpu.vic.denLatch = 0; 1306 //if (cpu.vic.rasterLine == LINECNT) 1307 //delay(50); 1308 emu_DrawVsync(); 1309 1310 } else cpu.vic.rasterLine++; 1311 1312 int r = cpu.vic.rasterLine; 1313 1314 if (r == cpu.vic.intRasterLine )//Set Rasterline-Interrupt 1315 cpu.vic.R[0x19] |= 1 | ((cpu.vic.R[0x1a] & 1) << 7); 1316 1317 /*****************************************************************************************************/ 1318 /* Badlines ******************************************************************************************/ 1319 /*****************************************************************************************************/ 1320 /* 1321 Ein Bad-Line-Zustand liegt in einem beliebigen Taktzyklus vor, wenn an der 1322 negativen Flanke von ø0 zu Beginn des Zyklus RASTER >= $30 und RASTER <= 1323 $f7 und die unteren drei Bits von RASTER mit YSCROLL übereinstimmen und in 1324 einem beliebigen Zyklus von Rasterzeile $30 das DEN-Bit gesetzt war. 1325 1326 (default 3) 1327 yscroll : POKE 53265, PEEK(53265) AND 248 OR 1:POKE 1024,0 1328 yscroll : poke 53265, peek(53265) and 248 or 1 1329 1330 DEN : POKE 53265, PEEK(53265) AND 224 Bildschirm aus 1331 1332 Die einzige Verwendung von YSCROLL ist der Vergleich mit r in der Badline 1333 1334 */ 1335 1336 if (r == 0x30 ) cpu.vic.denLatch |= cpu.vic.DEN; 1337 1338 /* 3.7.2 1339 2. In der ersten Phase von Zyklus 14 jeder Zeile wird VC mit VCBASE geladen 1340 (VCBASE->VC) und VMLI gelöscht. Wenn zu diesem Zeitpunkt ein 1341 Bad-Line-Zustand vorliegt, wird zusätzlich RC auf Null gesetzt. 1342 */ 1343 1344 vc = cpu.vic.vcbase; 1345 1346 cpu.vic.badline = (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL)); 1347 1348 if (cpu.vic.badline) { 1349 cpu.vic.idle = 0; 1350 cpu.vic.rc = 0; 1351 } 1352 1353 /*****************************************************************************************************/ 1354 /*****************************************************************************************************/ 1355 #if 1 1356 { 1357 int t = MAXCYCLESSPRITES3_7 - cpu.vic.spriteCycles3_7; 1358 if (t > 0) cpu_clock(t); 1359 if (cpu.vic.spriteCycles3_7 > 0) cia_clockt(cpu.vic.spriteCycles3_7); 1360 } 1361 #endif 1362 1363 //HBlank: 1364 cpu_clock(10); 1365 1366 #ifdef ADDITIONALCYCLES 1367 cpu_clock(ADDITIONALCYCLES); 1368 #endif 1369 1370 //cpu.vic.videomatrix = cpu.vic.bank + (unsigned)(cpu.vic.R[0x18] & 0xf0) * 64; 1371 1372 /* Rand oben /unten **********************************************************************************/ 1373 /* 1374 RSEL Höhe des Anzeigefensters Erste Zeile Letzte Zeile 1375 0 24 Textzeilen/192 Pixel 55 ($37) 246 ($f6) = 192 sichtbare Zeilen, der Rest ist Rand oder unsichtbar 1376 1 25 Textzeilen/200 Pixel 51 ($33) 250 ($fa) = 200 sichtbare Zeilen, der Rest ist Rand oder unsichtbar 1377 */ 1378 1379 if (cpu.vic.borderFlag) { 1380 int firstLine = (cpu.vic.RSEL) ? 0x33 : 0x37; 1381 if ((cpu.vic.DEN) && (r == firstLine)) cpu.vic.borderFlag = false; 1382 } else { 1383 int lastLine = (cpu.vic.RSEL) ? 0xfb : 0xf7; 1384 if (r == lastLine) cpu.vic.borderFlag = true; 1385 } 1386 1387 if (r < FIRSTDISPLAYLINE || r > LASTDISPLAYLINE ) { 1388 if (r == 0) 1389 cpu_clock(CYCLESPERRASTERLINE - 10 - 2 - MAXCYCLESSPRITES - 1); // (minus hblank l + r) 1390 else 1391 cpu_clock(CYCLESPERRASTERLINE - 10 - 2 - MAXCYCLESSPRITES ); 1392 goto noDisplayIncRC; 1393 } 1394 1395 //max_x = (!cpu.vic.CSEL) ? 40:38; 1396 //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH; 1397 p = &linebuffer[0]; //tft.getLineBuffer((r - FIRSTDISPLAYLINE)); 1398 pe = p + SCREEN_WIDTH; 1399 //Left Screenborder: Cycle 10 1400 spl = &cpu.vic.spriteLine[24]; 1401 cpu_clock(6); 1402 1403 1404 if (cpu.vic.borderFlag) { 1405 cpu_clock(5); 1406 fastFillLineNoSprites(p, pe + BORDER_RIGHT, cpu.vic.colors[0]); 1407 goto noDisplayIncRC ; 1408 } 1409 1410 1411 /*****************************************************************************************************/ 1412 /* DISPLAY *******************************************************************************************/ 1413 /*****************************************************************************************************/ 1414 1415 1416 //max_x = (!cpu.vic.CSEL) ? 40:38; 1417 //X-Scrolling: 1418 1419 xscroll = cpu.vic.XSCROLL; 1420 1421 if (xscroll > 0) { 1422 uint16_t col = cpu.vic.colors[0]; 1423 1424 if (!cpu.vic.CSEL) { 1425 cpu_clock(1); 1426 uint16_t sprite; 1427 for (int i = 0; i < xscroll; i++) { 1428 SPRITEORFIXEDCOLOR(); 1429 } 1430 } else { 1431 spl += xscroll; 1432 for (unsigned i = 0; i < xscroll; i++) { 1433 *p++ = col; 1434 } 1435 1436 } 1437 } 1438 1439 /*****************************************************************************************************/ 1440 /*****************************************************************************************************/ 1441 /*****************************************************************************************************/ 1442 1443 1444 cpu.vic.fgcollision = 0; 1445 mode = (cpu.vic.ECM << 2) | (cpu.vic.BMM << 1) | cpu.vic.MCM; 1446 1447 if ( !cpu.vic.idle) { 1448 1449 #if 0 1450 static uint8_t omode = 99; 1451 if (mode != omode) { 1452 Serial.print("Graphicsmode:"); 1453 Serial.println(mode); 1454 omode = mode; 1455 } 1456 #endif 1457 1458 modes[mode](p, pe, spl, vc); 1459 vc = (vc + 40) & 0x3ff; 1460 1461 } else { 1462 /* 1463 3.7.3.9. Idle-Zustand 1464 --------------------- 1465 1466 Im Idle-Zustand liest der VIC die Grafikdaten von Adresse $3fff (bzw. $39ff 1467 bei gesetztem ECM-Bit) und stellt sie im ausgewählten Grafikmodus dar, 1468 wobei aber die Videomatrix-Daten (normalerweise in den c-Zugriffen gelesen) 1469 nur aus "0"-Bits bestehen. Es wird also immer wiederholt das Byte an 1470 Adresse $3fff/$39ff ausgegeben. 1471 1472 c-Zugriff 1473 1474 Es werden keine c-Zugriffe ausgeführt. 1475 1476 Daten 1477 1478 +----+----+----+----+----+----+----+----+----+----+----+----+ 1479 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 1480 +----+----+----+----+----+----+----+----+----+----+----+----+ 1481 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1482 +----+----+----+----+----+----+----+----+----+----+----+----+ 1483 1484 g-Zugriff 1485 1486 Adressen (ECM=0) 1487 1488 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 1489 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 1490 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 1491 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1492 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 1493 1494 Adressen (ECM=1) 1495 1496 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 1497 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 1498 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 1499 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1500 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 1501 1502 Daten 1503 1504 +----+----+----+----+----+----+----+----+ 1505 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 1506 +----+----+----+----+----+----+----+----+ 1507 | 8 Pixel (1 Bit/Pixel) | Standard-Textmodus/ 1508 | | Multicolor-Textmodus/ 1509 | "0": Hintergrundfarbe 0 ($d021) | ECM-Textmodus 1510 | "1": Schwarz | 1511 +---------------------------------------+ 1512 | 8 Pixel (1 Bit/Pixel) | Standard-Bitmap-Modus/ 1513 | | Ungültiger Textmodus/ 1514 | "0": Schwarz (Hintergrund) | Ungültiger Bitmap-Modus 1 1515 | "1": Schwarz (Vordergrund) | 1516 +---------------------------------------+ 1517 | 4 Pixel (2 Bit/Pixel) | Multicolor-Bitmap-Modus 1518 | | 1519 | "00": Hintergrundfarbe 0 ($d021) | 1520 | "01": Schwarz (Hintergrund) | 1521 | "10": Schwarz (Vordergrund) | 1522 | "11": Schwarz (Vordergrund) | 1523 +---------------------------------------+ 1524 | 4 Pixel (2 Bit/Pixel) | Ungültiger Bitmap-Modus 2 1525 | | 1526 | "00": Schwarz (Hintergrund) | 1527 | "01": Schwarz (Hintergrund) | 1528 | "10": Schwarz (Vordergrund) | 1529 | "11": Schwarz (Vordergrund) | 1530 +---------------------------------------+ 1531 */ 1532 //Modes 1 & 3 1533 if (mode == 1 || mode == 3) { 1534 modes[mode](p, pe, spl, vc); 1535 } else {//TODO: all other modes 1536 fastFillLine(p, pe, cpu.vic.palette[0], spl); 1537 } 1538 } 1539 1540 /* 1541 Bei den MBC- und MMC-Interrupts löst jeweils nur die erste Kollision einen 1542 Interrupt aus (d.h. wenn die Kollisionsregister $d01e bzw. $d01f vor der 1543 Kollision den Inhalt Null hatten). Um nach einer Kollision weitere 1544 Interrupts auszulösen, muß das betreffende Register erst durch Auslesen 1545 gelöscht werden. 1546 */ 1547 1548 if (cpu.vic.fgcollision) { 1549 if (cpu.vic.MD == 0) { 1550 cpu.vic.R[0x19] |= 2 | ( (cpu.vic.R[0x1a] & 2) << 6); 1551 } 1552 cpu.vic.MD |= cpu.vic.fgcollision; 1553 } 1554 1555 /*****************************************************************************************************/ 1556 1557 if (!cpu.vic.CSEL) { 1558 cpu_clock(1); 1559 uint16_t col = cpu.vic.colors[0]; 1560 //p = &screen[r - FIRSTDISPLAYLINE][0]; 1561 //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + BORDER_LEFT; 1562 p = &linebuffer[0]; // tft.getLineBuffer((r - FIRSTDISPLAYLINE)); 1563 #if 0 1564 // Sprites im Rand 1565 uint16_t sprite; 1566 uint16_t * spl; 1567 spl = &cpu.vic.spriteLine[24 + xscroll]; 1568 1569 SPRITEORFIXEDCOLOR() 1570 SPRITEORFIXEDCOLOR() 1571 SPRITEORFIXEDCOLOR() 1572 SPRITEORFIXEDCOLOR() 1573 SPRITEORFIXEDCOLOR() 1574 SPRITEORFIXEDCOLOR() 1575 SPRITEORFIXEDCOLOR() //7 1576 #else 1577 //keine Sprites im Rand 1578 *p++ = col; *p++ = col; *p++ = col; *p++ = col; 1579 *p++ = col; *p++ = col; *p = col; 1580 1581 #endif 1582 1583 //Rand rechts: 1584 //p = &screen[r - FIRSTDISPLAYLINE][SCREEN_WIDTH - 9]; 1585 //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + SCREEN_WIDTH - 9 + BORDER_LEFT; 1586 p = &linebuffer[SCREEN_WIDTH - 9 + BORDER_LEFT]; //tft.getLineBuffer((r - FIRSTDISPLAYLINE)) + SCREEN_WIDTH - 9 + BORDER_LEFT; 1587 pe = p + 9; 1588 1589 #if 0 1590 // Sprites im Rand 1591 spl = &cpu.vic.spriteLine[24 + SCREEN_WIDTH - 9 + xscroll]; 1592 while (p < pe) { 1593 SPRITEORFIXEDCOLOR(); 1594 } 1595 #else 1596 //keine Sprites im Rand 1597 //while (p < pe) { 1598 // *p++ = col; 1599 //} 1600 #endif 1601 1602 1603 1604 } 1605 emu_DrawLine16(&linebuffer[0], SCREEN_WIDTH, SCREEN_HEIGHT, (r - FIRSTDISPLAYLINE)); 1606 1607 1608 1609 //Rechter Rand nach CSEL, im Textbereich 1610 cpu_clock(5); 1611 1612 1613 noDisplayIncRC: 1614 /* 3.7.2 1615 5. In der ersten Phase von Zyklus 58 wird geprüft, ob RC=7 ist. Wenn ja, 1616 geht die Videologik in den Idle-Zustand und VCBASE wird mit VC geladen 1617 (VC->VCBASE). Ist die Videologik danach im Display-Zustand (liegt ein 1618 Bad-Line-Zustand vor, ist dies immer der Fall), wird RC erhöht. 1619 */ 1620 1621 if (cpu.vic.rc == 7) { 1622 cpu.vic.idle = 1; 1623 cpu.vic.vcbase = vc; 1624 } 1625 //Ist dies richtig ?? 1626 if ((!cpu.vic.idle) || (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL))) { 1627 cpu.vic.rc = (cpu.vic.rc + 1) & 0x07; 1628 } 1629 1630 1631 /*****************************************************************************************************/ 1632 /* Sprites *******************************************************************************************/ 1633 /*****************************************************************************************************/ 1634 1635 cpu.vic.spriteCycles0_2 = 0; 1636 cpu.vic.spriteCycles3_7 = 0; 1637 1638 if (cpu.vic.lineHasSprites) { 1639 cpu.vic.lineHasSprites = 0; 1640 memset(cpu.vic.spriteLine, 0, sizeof(cpu.vic.spriteLine) ); 1641 } 1642 1643 uint32_t spriteYCheck = cpu.vic.R[0x15]; //Sprite enabled Register 1644 1645 if (spriteYCheck) { 1646 1647 unsigned short R17 = cpu.vic.R[0x17]; //Sprite-y-expansion 1648 unsigned char collision = 0; 1649 short lastSpriteNum = 0; 1650 1651 for (unsigned short i = 0; i < 8; i++) { 1652 if (!spriteYCheck) break; 1653 1654 unsigned b = 1 << i; 1655 1656 if (spriteYCheck & b ) { 1657 spriteYCheck &= ~b; 1658 short y = cpu.vic.R[i * 2 + 1]; 1659 1660 if ( (r >= y ) && //y-Position > Sprite-y ? 1661 (((r < y + 21) && (~R17 & b )) || // ohne y-expansion 1662 ((r < y + 2 * 21 ) && (R17 & b ))) ) //mit y-expansion 1663 { 1664 1665 //Sprite Cycles 1666 if (i < 3) { 1667 if (!lastSpriteNum) cpu.vic.spriteCycles0_2 += 1; 1668 cpu.vic.spriteCycles0_2 += 2; 1669 } else { 1670 if (!lastSpriteNum) cpu.vic.spriteCycles3_7 += 1; 1671 cpu.vic.spriteCycles3_7 += 2; 1672 } 1673 lastSpriteNum = i; 1674 //Sprite Cycles END 1675 1676 1677 if (r < FIRSTDISPLAYLINE || r > LASTDISPLAYLINE ) continue; 1678 1679 uint16_t x = (((cpu.vic.R[0x10] >> i) & 1) << 8) | cpu.vic.R[i * 2]; 1680 if (x >= SPRITE_MAX_X) continue; 1681 1682 unsigned short lineOfSprite = r - y; 1683 if (R17 & b) lineOfSprite = lineOfSprite / 2; // Y-Expansion 1684 unsigned short spriteadr = cpu.vic.bank | cpu.RAM[cpu.vic.videomatrix + (1024 - 8) + i] << 6 | (lineOfSprite * 3); 1685 unsigned spriteData = ((unsigned)cpu.RAM[ spriteadr ] << 16) | ((unsigned)cpu.RAM[ spriteadr + 1 ] << 8) | ((unsigned)cpu.RAM[ spriteadr + 2 ]); 1686 1687 if (!spriteData) continue; 1688 cpu.vic.lineHasSprites = 1; 1689 1690 uint16_t * slp = &cpu.vic.spriteLine[x]; //Sprite-Line-Pointer 1691 unsigned short upperByte = ( 0x80 | ( (cpu.vic.MDP & b) ? 0x40 : 0 ) | i ) << 8; //Bit7 = Sprite "da", Bit 6 = Sprite-Priorität vor Grafik/Text, Bits 3..0 = Spritenummer 1692 1693 //Sprite in Spritezeile schreiben: 1694 if ((cpu.vic.MMC & b) == 0) { // NO MULTICOLOR 1695 1696 uint16_t color = upperByte | cpu.vic.R[0x27 + i]; 1697 1698 if ((cpu.vic.MXE & b) == 0) { // NO MULTICOLOR, NO SPRITE-EXPANSION 1699 1700 for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { 1701 int c = (spriteData >> 23) & 0x01; 1702 spriteData = (spriteData << 1); 1703 1704 if (c) { 1705 if (*slp == 0) *slp = color; 1706 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1707 } 1708 slp++; 1709 1710 } 1711 1712 } else { // NO MULTICOLOR, SPRITE-EXPANSION 1713 1714 for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { 1715 int c = (spriteData >> 23) & 0x01; 1716 spriteData = (spriteData << 1); 1717 //So wie oben, aber zwei gleiche Pixel 1718 1719 if (c) { 1720 if (*slp == 0) *slp = color; 1721 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1722 slp++; 1723 if (*slp == 0) *slp = color; 1724 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1725 slp++; 1726 } else { 1727 slp += 2; 1728 } 1729 1730 } 1731 } 1732 1733 1734 1735 } else { // MULTICOLOR 1736 /* Im Mehrfarbenmodus (Multicolor-Modus) bekommen alle Sprites zwei zusätzliche gemeinsame Farben. 1737 Die horizontale Auflösung wird von 24 auf 12 halbiert, da bei der Sprite-Definition jeweils zwei Bits zusammengefasst werden. 1738 */ 1739 uint16_t colors[4]; 1740 //colors[0] = 1; //dummy, color 0 is transparent 1741 colors[1] = upperByte | cpu.vic.R[0x25]; 1742 colors[2] = upperByte | cpu.vic.R[0x27 + i]; 1743 colors[3] = upperByte | cpu.vic.R[0x26]; 1744 1745 if ((cpu.vic.MXE & b) == 0) { // MULTICOLOR, NO SPRITE-EXPANSION 1746 for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { 1747 int c = (spriteData >> 22) & 0x03; 1748 spriteData = (spriteData << 2); 1749 1750 if (c) { 1751 if (*slp == 0) *slp = colors[c]; 1752 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1753 slp++; 1754 if (*slp == 0) *slp = colors[c]; 1755 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1756 slp++; 1757 } else { 1758 slp += 2; 1759 } 1760 1761 } 1762 1763 } else { // MULTICOLOR, SPRITE-EXPANSION 1764 for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { 1765 int c = (spriteData >> 22) & 0x03; 1766 spriteData = (spriteData << 2); 1767 1768 //So wie oben, aber vier gleiche Pixel 1769 if (c) { 1770 if (*slp == 0) *slp = colors[c]; 1771 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1772 slp++; 1773 if (*slp == 0) *slp = colors[c]; 1774 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1775 slp++; 1776 if (*slp == 0) *slp = colors[c]; 1777 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1778 slp++; 1779 if (*slp == 0) *slp = colors[c]; 1780 else collision |= b | (1 << ((*slp >> 8) & 0x07)); 1781 slp++; 1782 } else { 1783 slp += 4; 1784 } 1785 1786 } 1787 1788 } 1789 } 1790 1791 } 1792 else lastSpriteNum = 0; 1793 } 1794 1795 } 1796 1797 if (collision) { 1798 if (cpu.vic.MM == 0) { 1799 cpu.vic.R[0x19] |= 4 | ((cpu.vic.R[0x1a] & 4) << 5 ); 1800 } 1801 cpu.vic.MM |= collision; 1802 } 1803 1804 } 1805 /*****************************************************************************************************/ 1806 #if 0 1807 { 1808 int t = MAXCYCLESSPRITES0_2 - cpu.vic.spriteCycles0_2; 1809 if (t > 0) cpu_clock(t); 1810 if (cpu.vic.spriteCycles0_2 > 0) cia_clockt(cpu.vic.spriteCycles0_2); 1811 } 1812 #endif 1813 1814 //HBlank: 1815 #if PAL 1816 cpu_clock(2); 1817 #else 1818 cpu_clock(3); 1819 #endif 1820 1821 1822 #if 0 1823 if (cpu.vic.idle) { 1824 Serial.print("Cycles line "); 1825 Serial.print(r); 1826 Serial.print(": "); 1827 Serial.println(cpu.lineCyclesAbs); 1828 } 1829 #endif 1830 1831 1832 return; 1833 } 1834 1835 /*****************************************************************************************************/ 1836 /*****************************************************************************************************/ 1837 /*****************************************************************************************************/ 1838 void fastFillLineNoSprites(tpixel * p, const tpixel * pe, const uint16_t col) { 1839 int i = 0; 1840 1841 while (p < pe) { 1842 *p++ = col; 1843 i = (i + 1) & 0x07; 1844 if (!i) CYCLES(1); 1845 } 1846 1847 1848 } 1849 1850 void fastFillLine(tpixel * p, const tpixel * pe, const uint16_t col, uint16_t * spl) { 1851 if (spl != NULL && cpu.vic.lineHasSprites) { 1852 int i = 0; 1853 uint16_t sprite; 1854 while ( p < pe ) { 1855 SPRITEORFIXEDCOLOR(); 1856 i = (i + 1) & 0x07; 1857 if (!i) CYCLES(1); 1858 }; 1859 1860 } else { 1861 1862 fastFillLineNoSprites(p, pe, col); 1863 1864 } 1865 } 1866 1867 /*****************************************************************************************************/ 1868 /*****************************************************************************************************/ 1869 /*****************************************************************************************************/ 1870 1871 void vic_displaySimpleModeScreen(void) { 1872 } 1873 1874 1875 void vic_do_simple(void) { 1876 uint16_t vc; 1877 int cycles = 0; 1878 1879 if ( cpu.vic.rasterLine >= LINECNT ) { 1880 1881 //reSID sound needs much time - too much to keep everything in sync and with stable refreshrate 1882 //but it is not called very often, so most of the time, we have more time than needed. 1883 //We can measure the time needed for a frame and calc a correction factor to speed things up. 1884 unsigned long m = fbmicros(); 1885 cpu.vic.neededTime = (m - cpu.vic.timeStart); 1886 cpu.vic.timeStart = m; 1887 cpu.vic.lineClock.setIntervalFast(LINETIMER_DEFAULT_FREQ - ((float)cpu.vic.neededTime / (float)LINECNT - LINETIMER_DEFAULT_FREQ )); 1888 1889 cpu.vic.rasterLine = 0; 1890 cpu.vic.vcbase = 0; 1891 cpu.vic.denLatch = 0; 1892 1893 } else { 1894 cpu.vic.rasterLine++; 1895 cpu_clock(1); 1896 cycles += 1; 1897 } 1898 1899 int r = cpu.vic.rasterLine; 1900 1901 if (r == cpu.vic.intRasterLine )//Set Rasterline-Interrupt 1902 cpu.vic.R[0x19] |= 1 | ((cpu.vic.R[0x1a] & 1) << 7); 1903 1904 cpu_clock(9); 1905 cycles += 9; 1906 1907 if (r == 0x30 ) cpu.vic.denLatch |= cpu.vic.DEN; 1908 1909 vc = cpu.vic.vcbase; 1910 1911 cpu.vic.badline = (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL)); 1912 1913 if (cpu.vic.badline) { 1914 cpu.vic.idle = 0; 1915 cpu.vic.rc = 0; 1916 } 1917 1918 1919 /* Rand oben /unten **********************************************************************************/ 1920 /* 1921 RSEL Höhe des Anzeigefensters Erste Zeile Letzte Zeile 1922 0 24 Textzeilen/192 Pixel 55 ($37) 246 ($f6) = 192 sichtbare Zeilen, der Rest ist Rand oder unsichtbar 1923 1 25 Textzeilen/200 Pixel 51 ($33) 250 ($fa) = 200 sichtbare Zeilen, der Rest ist Rand oder unsichtbar 1924 */ 1925 1926 if (cpu.vic.borderFlag) { 1927 int firstLine = (cpu.vic.RSEL) ? 0x33 : 0x37; 1928 if ((cpu.vic.DEN) && (r == firstLine)) cpu.vic.borderFlag = false; 1929 } else { 1930 int lastLine = (cpu.vic.RSEL) ? 0xfb : 0xf7; 1931 if (r == lastLine) cpu.vic.borderFlag = true; 1932 } 1933 1934 1935 //left screenborder 1936 cpu_clock(6); 1937 cycles += 6; 1938 1939 CYCLES(40); 1940 cycles += 40; 1941 vc += 40; 1942 1943 //right screenborder 1944 cpu_clock(4); //1 1945 cycles += 4; 1946 1947 1948 if (cpu.vic.rc == 7) { 1949 cpu.vic.idle = 1; 1950 cpu.vic.vcbase = vc; 1951 } 1952 //Ist dies richtig ?? 1953 if ((!cpu.vic.idle) || (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL))) { 1954 cpu.vic.rc = (cpu.vic.rc + 1) & 0x07; 1955 } 1956 1957 cpu_clock(3); //1 1958 cycles += 3; 1959 1960 int cyclesleft = CYCLESPERRASTERLINE - cycles; 1961 if (cyclesleft) cpu_clock(cyclesleft); 1962 1963 } 1964 1965 1966 /*****************************************************************************************************/ 1967 /*****************************************************************************************************/ 1968 /*****************************************************************************************************/ 1969 1970 void installPalette(void) { 1971 memcpy(cpu.vic.palette, (void*)palette, sizeof(cpu.vic.palette)); 1972 } 1973 1974 1975 /*****************************************************************************************************/ 1976 /*****************************************************************************************************/ 1977 /*****************************************************************************************************/ 1978 1979 void vic_adrchange(void) { 1980 uint8_t r18 = cpu.vic.R[0x18]; 1981 cpu.vic.videomatrix = cpu.vic.bank + (unsigned)(r18 & 0xf0) * 64; 1982 1983 unsigned charsetAddr = r18 & 0x0e; 1984 if ((cpu.vic.bank & 0x4000) == 0) { 1985 if (charsetAddr == 0x04) cpu.vic.charsetPtrBase = ((uint8_t *)&rom_characters); 1986 else if (charsetAddr == 0x06) cpu.vic.charsetPtrBase = ((uint8_t *)&rom_characters) + 0x800; 1987 else 1988 cpu.vic.charsetPtrBase = &cpu.RAM[charsetAddr * 0x400 + cpu.vic.bank] ; 1989 } else 1990 cpu.vic.charsetPtrBase = &cpu.RAM[charsetAddr * 0x400 + cpu.vic.bank]; 1991 1992 cpu.vic.bitmapPtr = (uint8_t*) &cpu.RAM[cpu.vic.bank | ((r18 & 0x08) * 0x400)]; 1993 if ((cpu.vic.R[0x11] & 0x60) == 0x60) cpu.vic.bitmapPtr = (uint8_t*)((uintptr_t)cpu.vic.bitmapPtr & 0xf9ff); 1994 1995 } 1996 /*****************************************************************************************************/ 1997 void vic_write(uint32_t address, uint8_t value) { 1998 1999 address &= 0x3F; 2000 2001 switch (address) { 2002 case 0x11 : 2003 cpu.vic.R[address] = value; 2004 cpu.vic.intRasterLine = (cpu.vic.intRasterLine & 0xff) | ((((uint16_t) value) << 1) & 0x100); 2005 if (cpu.vic.rasterLine == 0x30 ) cpu.vic.denLatch |= value & 0x10; 2006 2007 cpu.vic.badline = (cpu.vic.denLatch && (cpu.vic.rasterLine >= 0x30) && (cpu.vic.rasterLine <= 0xf7) && ( (cpu.vic.rasterLine & 0x07) == (value & 0x07))); 2008 2009 if (cpu.vic.badline) { 2010 cpu.vic.idle = 0; 2011 } 2012 2013 vic_adrchange(); 2014 2015 break; 2016 case 0x12 : 2017 cpu.vic.intRasterLine = (cpu.vic.intRasterLine & 0x100) | value; 2018 cpu.vic.R[address] = value; 2019 break; 2020 case 0x18 : 2021 cpu.vic.R[address] = value; 2022 vic_adrchange(); 2023 break; 2024 case 0x19 : //IRQs 2025 cpu.vic.R[0x19] &= (~value & 0x0f); 2026 break; 2027 case 0x1A : //IRQ Mask 2028 cpu.vic.R[address] = value & 0x0f; 2029 break; 2030 case 0x1e: 2031 case 0x1f: 2032 cpu.vic.R[address] = 0; 2033 break; 2034 case 0x20 ... 0x2E: 2035 cpu.vic.R[address] = value & 0x0f; 2036 cpu.vic.colors[address - 0x20] = cpu.vic.palette[value & 0x0f]; 2037 break; 2038 case 0x2F ... 0x3F: 2039 break; 2040 default : 2041 cpu.vic.R[address] = value; 2042 break; 2043 } 2044 2045 //#if DEBUGVIC 2046 #if 0 2047 Serial.print("VIC "); 2048 Serial.print(address, HEX); 2049 Serial.print("="); 2050 Serial.println(value, HEX); 2051 //logAddr(address, value, 1); 2052 #endif 2053 } 2054 2055 /*****************************************************************************************************/ 2056 /*****************************************************************************************************/ 2057 /*****************************************************************************************************/ 2058 2059 uint8_t vic_read(uint32_t address) { 2060 uint8_t ret; 2061 2062 address &= 0x3F; 2063 switch (address) { 2064 2065 case 0x11: 2066 ret = (cpu.vic.R[address] & 0x7F) | ((cpu.vic.rasterLine & 0x100) >> 1); 2067 break; 2068 case 0x12: 2069 ret = cpu.vic.rasterLine; 2070 break; 2071 case 0x16: 2072 ret = cpu.vic.R[address] | 0xC0; 2073 break; 2074 case 0x18: 2075 ret = cpu.vic.R[address] | 0x01; 2076 break; 2077 case 0x19: 2078 ret = cpu.vic.R[address] | 0x70; 2079 break; 2080 case 0x1a: 2081 ret = cpu.vic.R[address] | 0xF0; 2082 break; 2083 case 0x1e: 2084 case 0x1f: 2085 ret = cpu.vic.R[address]; 2086 cpu.vic.R[address] = 0; 2087 break; 2088 case 0x20 ... 0x2E: 2089 ret = cpu.vic.R[address] | 0xF0; 2090 break; 2091 case 0x2F ... 0x3F: 2092 ret = 0xFF; 2093 break; 2094 default: 2095 ret = cpu.vic.R[address]; 2096 break; 2097 } 2098 2099 #if DEBUGVIC 2100 Serial.print("VIC "); 2101 logAddr(address, ret, 0); 2102 #endif 2103 return ret; 2104 } 2105 2106 /*****************************************************************************************************/ 2107 /*****************************************************************************************************/ 2108 /*****************************************************************************************************/ 2109 2110 void resetVic(void) { 2111 enableCycleCounter(); 2112 2113 cpu.vic.intRasterLine = 0; 2114 cpu.vic.rasterLine = 0; 2115 cpu.vic.lineHasSprites = 0; 2116 memset(&cpu.RAM[0x400], 0, 1000); 2117 memset(&cpu.vic, 0, sizeof(cpu.vic)); 2118 2119 2120 2121 installPalette(); 2122 2123 //http://dustlayer.com/vic-ii/2013/4/22/when-visibility-matters 2124 cpu.vic.R[0x11] = 0x9B; 2125 cpu.vic.R[0x16] = 0x08; 2126 cpu.vic.R[0x18] = 0x14; 2127 cpu.vic.R[0x19] = 0x0f; 2128 2129 for (unsigned i = 0; i < sizeof(cpu.vic.COLORRAM); i++) 2130 cpu.vic.COLORRAM[i] = (rand() & 0x0F); 2131 2132 cpu.RAM[0x39FF] = 0x0; 2133 cpu.RAM[0x3FFF] = 0x0; 2134 cpu.RAM[0x39FF + 16384] = 0x0; 2135 cpu.RAM[0x3FFF + 16384] = 0x0; 2136 cpu.RAM[0x39FF + 32768] = 0x0; 2137 cpu.RAM[0x3FFF + 32768] = 0x0; 2138 cpu.RAM[0x39FF + 49152] = 0x0; 2139 cpu.RAM[0x3FFF + 49152] = 0x0; 2140 2141 vic_adrchange(); 2142 } 2143 2144 2145 /* 2146 ?PEEK(678) NTSC =0 2147 ?PEEK(678) PAL = 1 2148 PRINT TIME$ 2149 */ 2150 /* 2151 Raster- Takt- sichtb. sichtbare 2152 VIC-II System zeilen zyklen Zeilen Pixel/Zeile 2153 ------------------------------------------------------- 2154 6569 PAL 312 63 284 403 2155 6567R8 NTSC 263 65 235 418 2156 6567R56A NTSC 262 64 234 411 2157 */