// Here I realized I could simplify the math, leveraging the 9-bits you get after an addition due to carry/ror

#include <peekpoke.h>

static const unsigned char charset[8*64*4] = { // 64 ECM characters - a dither pattern
 0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,2,0,32,0,0,0,0,4,0,32,0,8,0,0,0,4,16,0,64,4,0,0,0,0,18,0,64,0,10,0,64,8,0,2,16,0,66,0,128,4,32,2,0,8,65,0,
 0,32,2,136,0,34,136,0,0,18,64,8,18,0,66,8,32,1,168,0,2,16,66,8,9,32,4,160,1,8,160,2,16,130,20,0,74,4,80,2,1,72,2,36,144,66,8,34,40,129,40,10,160,8,66,8,8,33,44,64,42,64,21,128,
 65,42,0,41,2,88,34,9,9,69,32,138,16,82,8,162,146,0,90,66,144,9,68,146,148,66,53,130,52,64,137,32,168,68,145,66,24,130,105,18,65,42,72,150,33,204,1,50,171,8,169,2,88,34,169,36,16,85,170,85,82,132,73,34,
 181,74,162,169,20,72,81,136,149,82,102,88,13,162,4,85,138,41,230,26,160,170,84,145,202,119,1,117,130,84,155,66,165,86,241,78,40,141,80,50,90,155,213,43,241,6,84,18,84,187,208,71,42,153,101,170,74,185,85,250,171,84,133,82,
 54,91,165,186,21,106,85,154,213,109,134,185,12,247,137,86,170,117,212,45,154,226,93,182,37,170,213,62,233,181,86,173,109,178,173,107,214,42,222,181,106,183,218,117,148,91,237,181,78,233,190,213,121,207,114,173,218,167,89,175,90,247,173,118,
 53,238,181,235,86,237,159,117,85,238,181,87,253,86,191,109,174,187,110,173,119,222,183,234,118,219,237,151,253,106,191,218,109,247,157,246,191,85,253,214,249,166,253,175,217,191,230,189,106,223,117,251,223,181,127,213,118,247,93,235,189,247,125,215,
 215,125,174,251,93,255,183,222,247,189,239,254,171,222,251,175,189,247,191,109,251,223,182,251,191,237,183,125,239,253,191,238,223,251,190,247,239,125,247,222,246,223,251,126,223,255,251,111,223,251,191,245,127,237,255,239,247,191,253,239,254,239,187,255,
 251,255,127,247,238,255,191,247,223,251,255,191,247,253,191,255,255,239,255,175,255,254,223,255,255,191,247,255,255,253,191,255,255,239,255,255,255,251,127,255,255,255,223,255,255,253,255,255,255,255,255,247,255,255,255,255,255,255,255,255,255,255,255,255,
 0,0,0,255,255,0,0,0,8,28,62,127,127,28,62,0,24,24,24,24,24,24,24,24,0,0,0,255,255,0,0,0,0,0,255,255,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,255,255,0,0,48,48,48,48,48,48,48,48,
 12,12,12,12,12,12,12,12,0,0,0,224,240,56,24,24,24,24,28,15,7,0,0,0,24,24,56,240,224,0,0,0,192,192,192,192,192,192,255,255,192,224,112,56,28,14,7,3,3,7,14,28,56,112,224,192,255,255,192,192,192,192,192,192,
 255,255,3,3,3,3,3,3,0,60,126,126,126,126,60,0,0,0,0,0,0,255,255,0,54,127,127,127,62,28,8,0,96,96,96,96,96,96,96,96,0,0,0,7,15,28,24,24,195,231,126,60,60,126,231,195,0,60,126,102,102,126,60,0,
 24,24,102,102,24,24,60,0,6,6,6,6,6,6,6,6,8,28,62,127,62,28,8,0,24,24,24,255,255,24,24,24,192,192,48,48,192,192,48,48,24,24,24,24,24,24,24,24,0,0,3,62,118,54,54,0,255,127,63,31,15,7,3,1,
 0,0,0,0,0,0,0,0,240,240,240,240,240,240,240,240,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,192,192,192,192,192,192,192,192,204,204,51,51,204,204,51,51,3,3,3,3,3,3,3,3,
 0,0,0,0,204,204,51,51,255,254,252,248,240,224,192,128,3,3,3,3,3,3,3,3,24,24,24,31,31,24,24,24,0,0,0,0,15,15,15,15,24,24,24,31,31,0,0,0,0,0,0,248,248,24,24,24,0,0,0,0,0,0,255,255,
 0,0,0,31,31,24,24,24,24,24,24,255,255,0,0,0,0,0,0,255,255,24,24,24,24,24,24,248,248,24,24,24,192,192,192,192,192,192,192,192,224,224,224,224,224,224,224,224,7,7,7,7,7,7,7,7,255,255,0,0,0,0,0,0,
 255,255,255,0,0,0,0,0,0,0,0,0,0,255,255,255,3,3,3,3,3,3,255,255,0,0,0,0,240,240,240,240,15,15,15,15,0,0,0,0,24,24,24,248,248,0,0,0,240,240,240,240,0,0,0,0,240,240,240,240,15,15,15,15,
 195,153,145,145,159,153,195,255,231,195,153,129,153,153,153,255,131,153,153,131,153,153,131,255,195,153,159,159,159,153,195,255,135,147,153,153,153,147,135,255,129,159,159,135,159,159,129,255,129,159,159,135,159,159,159,255,195,153,159,145,153,153,195,255,
 153,153,153,129,153,153,153,255,195,231,231,231,231,231,195,255,225,243,243,243,243,147,199,255,153,147,135,143,135,147,153,255,159,159,159,159,159,159,129,255,156,136,128,148,156,156,156,255,153,137,129,129,145,153,153,255,195,153,153,153,153,153,195,255,
 131,153,153,131,159,159,159,255,195,153,153,153,153,195,241,255,131,153,153,131,135,147,153,255,195,153,159,195,249,153,195,255,129,231,231,231,231,231,231,255,153,153,153,153,153,153,195,255,153,153,153,153,153,195,231,255,156,156,156,148,128,136,156,255,
 153,153,195,231,195,153,153,255,153,153,153,195,231,231,231,255,129,249,243,231,207,159,129,255,195,207,207,207,207,207,195,255,243,237,207,131,207,157,3,255,195,243,243,243,243,243,195,255,255,231,195,129,231,231,231,231,255,239,207,128,128,207,239,255,
 255,255,255,255,255,255,255,255,231,231,231,231,255,255,231,255,153,153,153,255,255,255,255,255,153,153,0,153,0,153,153,255,231,193,159,195,249,131,231,255,157,153,243,231,207,153,185,255,195,153,195,199,152,153,192,255,249,243,231,255,255,255,255,255,
 243,231,207,207,207,231,243,255,207,231,243,243,243,231,207,255,255,153,195,0,195,153,255,255,255,231,231,129,231,231,255,255,255,255,255,255,255,231,231,207,255,255,255,129,255,255,255,255,255,255,255,255,255,231,231,255,255,252,249,243,231,207,159,255,
 195,153,145,137,153,153,195,255,231,231,199,231,231,231,129,255,195,153,249,243,207,159,129,255,195,153,249,227,249,153,195,255,249,241,225,153,128,249,249,255,129,159,131,249,249,153,195,255,195,153,159,131,153,153,195,255,129,153,243,231,231,231,231,255,
 195,153,153,195,153,153,195,255,195,153,153,193,249,153,195,255,255,255,231,255,255,231,255,255,255,255,231,255,255,231,231,207,241,231,207,159,207,231,241,255,255,255,129,255,129,255,255,255,143,231,243,249,243,231,143,255,195,153,249,243,231,255,231,255,
 255,255,255,0,0,255,255,255,247,227,193,128,128,227,193,255,231,231,231,231,231,231,231,231,255,255,255,0,0,255,255,255,255,255,0,0,255,255,255,255,255,0,0,255,255,255,255,255,255,255,255,255,0,0,255,255,207,207,207,207,207,207,207,207,
 243,243,243,243,243,243,243,243,255,255,255,31,15,199,231,231,231,231,227,240,248,255,255,255,231,231,199,15,31,255,255,255,63,63,63,63,63,63,0,0,63,31,143,199,227,241,248,252,252,248,241,227,199,143,31,63,0,0,63,63,63,63,63,63,
 0,0,252,252,252,252,252,252,255,195,129,129,129,129,195,255,255,255,255,255,255,0,0,255,201,128,128,128,193,227,247,255,159,159,159,159,159,159,159,159,255,255,255,248,240,227,231,231,60,24,129,195,195,129,24,60,255,195,129,153,153,129,195,255,
 231,231,153,153,231,231,195,255,249,249,249,249,249,249,249,249,247,227,193,128,193,227,247,255,231,231,231,0,0,231,231,231,63,63,207,207,63,63,207,207,231,231,231,231,231,231,231,231,255,255,252,193,137,201,201,255,0,128,192,224,240,248,252,254,
 255,255,255,255,255,255,255,255,15,15,15,15,15,15,15,15,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,63,63,63,63,63,63,63,63,51,51,204,204,51,51,204,204,252,252,252,252,252,252,252,252,
 255,255,255,255,51,51,204,204,0,1,3,7,15,31,63,127,252,252,252,252,252,252,252,252,231,231,231,224,224,231,231,231,255,255,255,255,240,240,240,240,231,231,231,224,224,255,255,255,255,255,255,7,7,231,231,231,255,255,255,255,255,255,0,0,
 255,255,255,224,224,231,231,231,231,231,231,0,0,255,255,255,255,255,255,0,0,231,231,231,231,231,231,7,7,231,231,231,63,63,63,63,63,63,63,63,31,31,31,31,31,31,31,31,248,248,248,248,248,248,248,248,0,0,255,255,255,255,255,255,
 0,0,0,255,255,255,255,255,255,255,255,255,255,0,0,0,252,252,252,252,252,252,0,0,255,255,255,255,15,15,15,15,240,240,240,240,255,255,255,255,231,231,231,7,7,255,255,255,15,15,15,15,255,255,255,255,15,15,15,15,240,240,240,240,
};

static const char palette[5] = {0,9,2,7,1};
#define SCREEN (0xC800)
#define CHARSET (0xC000)
#define COLORRAM (0xD800)
#define PRECALC (0x2000)

void init(void) { 
  register unsigned short i16;
  register unsigned char i8;
  
  // Initialize random number gen (SID noise waveform)
  POKE(54286,255); // max frequency
  POKE(54287,255); // max frequency
  POKE(54290,128); // voice 3 to noise
  
  POKE(53280, 0); // black border color 
  
  // Initialize the ECM mode and character set  
  POKE(53265, PEEK(53265)|64); // Sets bit 6 in $D011, turns off BMM
  POKE(53281, palette[0]); // color1 - $D021
  POKE(53282, palette[1]);
  POKE(53283, palette[2]);
  POKE(53284, palette[3]);
  
  for(i16=0;i16<2048;i16++){ // copy the charset to 0x2000
    POKE(CHARSET+i16, charset[i16]);
  }
 
  // vic2 - bank 3 - important, default bank 0 has ROM shadows we want to avoid
  POKE(0xdd00, PEEK(0xdd00) & 0xfc);
  POKE(53272, 32 ); // point vic2 to the new SCREEN and CHARSET

  // Precompute palette[1+(x>>6)]
  for(i8=255;;i8--){
    ((unsigned char*)PRECALC)[i8] = palette[1+(i8>>6)]; 
    if(i8==0) break;
  }
}

// I realized that I was being dumb, and can code a fast 4-tap non-overflowing fire using RORs...
// so now it's much simpler! I should do some cycle counting, I had the idea of doing 2 taps but
// changing the pattern per "pixel" and frame... but I think now it's not needed...

// Cycle count: 2 4 4 2 3 4 4 2 3 2 2 5 2 4 5 2 2 = 52
// so even with only 2 samples it would be 2 4 4 2 x x x x x x 2 5 2 4 5 2 2 = 34...
// I think to run at full speed I would need to bring this down to (63*7+20)/40 = about 11 !

// NOTE: because of preprocessor to string, L has to be an integer, can't be an expression, even if constant
// uses zeropage ff
#define ASM_LINE_S(L) \
__asm__("ldx #40");\
__asm__("@L2_" #L ":");\
__asm__("lda %w,x", SCREEN+L+39);\
__asm__("adc %w,x", SCREEN+L+41);\
__asm__("ror");\
__asm__("sta $ff");\
__asm__("lda %w,x", SCREEN+L+40);\
__asm__("adc %w,x", SCREEN+L+80);\
__asm__("ror");\
__asm__("adc $ff");\
__asm__("ror");\
__asm__("sbc #5");\
__asm__("sta %w,x", SCREEN+L);\
__asm__("tay");\
__asm__("lda %w,y", PRECALC);\
__asm__("sta %w,x", COLORRAM+L);\
__asm__("dex");\
__asm__("bpl @L2_" #L );

// this version checks for underflow - also subtracing a bit more because why not
#define ASM_LINE_C(L) \
__asm__("ldx #40");\
__asm__("@L2_" #L ":");\
__asm__("lda %w,x", SCREEN+L+39);\
__asm__("adc %w,x", SCREEN+L+41);\
__asm__("ror");\
__asm__("sta $ff");\
__asm__("lda %w,x", SCREEN+L+40);\
__asm__("adc %w,x", SCREEN+L+80);\
__asm__("ror");\
__asm__("adc $ff");\
__asm__("ror");\
__asm__("sbc #7");\
__asm__("bpl @L_" #L );\
__asm__("lda #0");\
__asm__("@L_" #L ":");\
__asm__("sta %w,x", SCREEN+L);\
__asm__("tay");\
__asm__("lda %w,y", PRECALC);\
__asm__("sta %w,x", COLORRAM+L);\
__asm__("dex");\
__asm__("bpl @L2_" #L );

void main(void) {
  // "register" forces variables to the zero-page
  // ZP range is defined in c64.cfg for cc65: start = $0002, size = $001A;
  register unsigned char i8, ii8, iii8;
  register unsigned short i16, ii16;
  
  init();
  __asm__("sei"); // disable interrupts

  // Move the palette for the lines to zeropage f0-f3
  //static const char palette[5] = {0,9,2,7,1};
  __asm__("lda #9");
  __asm__("sta $f0");
  __asm__("lda #2");
  __asm__("sta $f1");
  __asm__("lda #7");
  __asm__("sta $f2");
  __asm__("lda #1");
  __asm__("sta $f3");
  
  while(1){      
    // the random line is now actually offscreen
    // this is why I needed to do the trickery with using bank3
    // and moving the screen away... in the default 0x400 screen location
    // there is no extra space to put a line outside the screen buffer
    __asm__("ldx #40");
    __asm__("@L2_:");
    __asm__("lda %w", (unsigned short)54299);
    // to see the "sparkles" only -> __asm__("lda #0");
    __asm__("sta %w,x", SCREEN+(40*25));
    __asm__("sta %w,x", SCREEN+(40*26)); // the effect reads up to two lines down
    __asm__("tay");\
    __asm__("lda %w,y", PRECALC);
    __asm__("sta %w,x", COLORRAM+(40*25));
    __asm__("dex");
    __asm__("bpl @L2_");   

    // add a random sparkle (not bothering to set the color, will be blurred immediately & recomputed)
    __asm__("ldx %w", (unsigned short)54299);
    __asm__("lda #255");
    __asm__("sta %w,x", SCREEN+(40*25)-255); // somewhere down the screen
    __asm__("lda #0");
    __asm__("sta %w,x", SCREEN+(40*10)); // add a "dark" sparkle near the top
    
    ASM_LINE_C(0);
    ASM_LINE_C(40);
    ASM_LINE_C(80);
    ASM_LINE_C(120);
    ASM_LINE_C(160);
    ASM_LINE_S(200);
    ASM_LINE_S(240);
    ASM_LINE_S(280);
    ASM_LINE_S(320);
    ASM_LINE_S(360);
    ASM_LINE_S(400);
    ASM_LINE_S(440);
    ASM_LINE_S(480);    
    ASM_LINE_S(520);
    ASM_LINE_S(560);    
    ASM_LINE_S(600);    
    ASM_LINE_S(640);    
    ASM_LINE_S(680);    
    ASM_LINE_S(720);      
    ASM_LINE_S(760);    
    ASM_LINE_S(800);    
    ASM_LINE_S(840);    
    ASM_LINE_S(880); 
    ASM_LINE_S(920);
    ASM_LINE_S(960);
  }
  
  return;
}