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