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  */