/ MCUME_teensy / teensy64 / vic.cpp
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  */