/** fMSX: portable MSX emulator ******************************/
/**                                                         **/
/**                         Common.h                        **/
/**                                                         **/
/** This file contains screen refresh drivers which are     **/
/** common for both X11 and VGA implementations. It is      **/
/** included either from Unix.c or MSDOS.c.                 **/
/**                                                         **/
/** Copyright (C) Marat Fayzullin 1994,1995                 **/
/**     You are not allowed to distribute this software     **/
/**     commercially. Please, notify me, if you make any    **/
/**     changes to this file.                               **/
/*************************************************************/

void Sound(void)
{
  byte J;
  if(Verbose>1)
    for(J=0;J<3;J++)
      if(Volume[J])
        printf("CHANNEL %c: F=%d V=%d\n",J+'A',Freq[J],Volume[J]);
}


void Sprites(void)
{
  register byte N,M,I,J,X,Y,C,H;
  register byte *P,*T,*S;
  register int L;

  for(N=0,S=SprTab;(N<32)&&(S[0]!=208);N++,S+=4);
  H=ScanLines212? 212:192;
  M=SolidColor0;
  
  if(Sprites16x16)
    for(S-=4;N;N--,S-=4)
    {
      C=S[3]&0x0F;L=S[3]&0x80? S[1]-32:S[1];
      if((L<=256-16)&&(L>=0)&&(C||M))
      {
        P=XBuf+WIDTH*(HEIGHT-192)/2+(WIDTH-256)/2+L;
        T=SprGen+((long)(S[2]&0xFC)<<3);
        C=XPal[C];Y=S[0]+1;

        if(Y<H) { P+=WIDTH*Y;Y=(Y>H-16)? H-Y:16; } 
        else    { T+=256-Y;Y-=(Y>240)? 240:Y; }

        for(;Y;Y--,T++,P+=WIDTH-8)
          for(X=0,J=*T,I=*(T+16);X<8;X++,J<<=1,I<<=1,P++)
          {
            if(J&0x80) *P=C;
            if(I&0x80) *(P+8)=C;
          }          
      }
    }
  else
    for(S-=4;N;N--,S-=4)
    {
      C=S[3]&0x0F;L=S[3]&0x80? S[1]-32:S[1];
      if((L<=256-8)&&(L>=0)&&(C||M))
      {
        P=XBuf+WIDTH*(HEIGHT-192)/2+(WIDTH-256)/2+L;
        T=SprGen+((long)S[2]<<3);C=XPal[C];Y=S[0]+1;
     
        if(Y<H) { P+=WIDTH*Y;Y=(Y>H-8)? H-Y:8; }
        else    { T+=256-Y;Y-=(Y>248)? 248:Y; }

        for(;Y;Y--,T++,P+=WIDTH-8)
          for(X=0,J=*T;X<8;X++,J<<=1,P++)
            if(J&0x80) *P=C;
      }
    }
}


void ColorSprites(void)
{
  register byte N,M,I,J,X,Y,C,H;
  register byte *P,*T,*AT,*PT,*CT;
  register int L;

  H=ScanLines212? 212:192;
  memset(SBuf,0,(word)H<<8);
  if(SpritesOFF) return;

  for(N=0,AT=SprTab;(N<32)&&(AT[0]!=216);N++,AT+=4);
  CT=SprTab-0x0200+((long)N<<4)-16;AT-=4;
  M=SolidColor0;

  if(Sprites16x16)
    for(;N;N--,AT-=4,CT-=16)
    {
      P=SBuf;PT=SprGen+((long)(AT[2]&0xFC)<<3);T=CT;

      Y=AT[0]+1-VDP[23];      
      if(Y<H) { P+=(word)Y<<8;Y=(Y>H-16)? H-Y:16; }
      else    { Y=256-Y;PT+=Y;T+=Y;Y=(Y<16)? 16-Y:0; }      

      for(;Y;Y--,T++,PT++,P+=256)
      {
        L=*T&0x80? AT[1]-32:AT[1];C=*T&0x0F;
        if((L<=256-16)&&(L>=0)&&(C||M))
        {
          P+=L;J=*PT;I=*(PT+16);
          if(*T&0x40)
            for(X=8;X;X--,J<<=1,I<<=1,P++)
            {
              if(J&0x80) *P|=C;
              if(I&0x80) *(P+8)|=C;
            }
          else
            for(C<<=4,X=8;X;X--,J<<=1,I<<=1,P++)
            {
              if(J&0x80) *P=(*P&0x0F)|C;
              if(I&0x80) *(P+8)=(*(P+8)&0x0F)|C;
            }
          P-=8+L;
        }
      }
    }
  else
    for(;N;N--,AT-=4,CT-=16)
    {
      P=SBuf;PT=SprGen+((int)AT[2]<<3);T=CT;

      Y=AT[0]+1-VDP[23];
      if(Y<H) { P+=(word)Y<<8;Y=(Y>H-8)? H-Y:8; }
      else    { Y=256-Y;PT+=Y;T+=Y;Y=(Y<8)? 8-Y:0; }

      for(;Y;Y--,T++,PT++,P+=256)
      {
        L=*T&0x80? AT[1]-32:AT[1];C=*T&0x0F;
        if((L<=256-8)&&(L>=0)&&(C||M))
        {
          P+=L;J=*PT;
          if(*T&0x40)
            for(X=8;X;X--,J<<=1,P++)
            { if(J&0x80) *P|=C; }
          else
            for(C<<=4,X=8;X;X--,J<<=1,P++)
            { if(J&0x80) *P=(*P&0x0F)|C; }
          P-=8+L;
        }
      }
    }
}


void RefreshScrF(register byte Y1,register byte Y2)
{
  if(Verbose>1)
    printf
    (
      "ScrMODE %d: ChrTab=%X ChrGen=%X ColTab=%X SprTab=%X SprGen=%X\n",
      ScrMode,ChrTab-VRAM,ChrGen-VRAM,ColTab-VRAM,SprTab-VRAM,SprGen-VRAM
    );
  memset(XBuf+(HEIGHT-192)/2+WIDTH*Y1,XPal[BGColor],(Y2-Y1+1)*WIDTH);
}


void RefreshScr0(register byte Y1,register byte Y2)
{
  register byte X,Y,J,K,FC,BC;
  register byte *P,*S,*T;

  P=XBuf+WIDTH*(HEIGHT-192)/2;

  if(ScreenON)
  {
    BC=XPal[BGColor];FC=XPal[FGColor];
    P+=(WIDTH-240)/2;T=ChrTab;

    for(Y=ScanLines212? 26:24;Y;Y--,P+=WIDTH*8-6*40)
      for(X=0;X<40;X++,T++)
      {
        S=ChrGen+((long)*T<<3);
        for(J=0;J<8;J++)
        {
          K=*S++;
          *P++=K&0x80? FC:BC;*P++=K&0x40? FC:BC;
          *P++=K&0x20? FC:BC;*P++=K&0x10? FC:BC;
          *P++=K&0x08? FC:BC;*P  =K&0x04? FC:BC;
          P+=WIDTH-5;
        }
        P+=6-WIDTH*8;
      }
  }
  else memset(P+((word)Y1<<8),XPal[BGColor],(Y2-Y1+1)*WIDTH);
    
  if(EndOfFrame) PutImage;
}


void RefreshScr1(register byte Y1,register byte Y2)
{
  register byte X,Y,K,J,FC,BC;
  register byte *P,*S,*T;

  P=XBuf+WIDTH*(HEIGHT-192)/2;

  if(ScreenON)
  {
    P+=(WIDTH-256)/2;T=ChrTab;

    for(Y=ScanLines212? 26:24;Y;Y--,P+=WIDTH*8-8*32)
      for(X=0;X<32;X++,T++)
      {
        S=ChrGen+((long)*T<<3);
        BC=*(ColTab+(*T>>3));
        FC=XPal[BC>>4];BC=XPal[BC&0x0F];
        for(J=0;J<8;J++)
	{
          K=*S++;
          *P++=K&0x80? FC:BC;*P++=K&0x40? FC:BC;
          *P++=K&0x20? FC:BC;*P++=K&0x10? FC:BC;
          *P++=K&0x08? FC:BC;*P++=K&0x04? FC:BC;
          *P++=K&0x02? FC:BC;*P  =K&0x01? FC:BC;
          P+=WIDTH-7;  
        }
        P+=8-WIDTH*8;
      }
    if(!SpritesOFF) Sprites();
  }
  else memset(P+((word)Y1<<8),XPal[BGColor],(Y2-Y1+1)*WIDTH);
    
  if(EndOfFrame) PutImage;
}


void RefreshScr2(register byte Y1,register byte Y2)
{
  register byte X,Y,J,K,M,N,FC,BC;
  register byte *P,*S,*T,*PGT,*CLT,*C;

  P=XBuf+WIDTH*(HEIGHT-192)/2;

  if(ScreenON)
  {
    P+=(WIDTH-256)/2;
    PGT=ChrGen;CLT=ColTab;T=ChrTab;
    M=ScanLines212? 4:3;

    for(N=0;N<M;N++,PGT+=0x0800,CLT+=0x0800)
      for(Y=(N==3)? 2:8;Y;Y--,P+=WIDTH*8-8*32)
        for(X=0;X<32;X++,T++)
        {
          S=PGT+((long)*T<<3);C=CLT+((long)*T<<3);
          for(J=0;J<8;J++)
          {
            BC=*C++;K=*S++;
            FC=XPal[BC>>4];BC=XPal[BC&0x0F];
            *P++=K&0x80? FC:BC;*P++=K&0x40? FC:BC;
            *P++=K&0x20? FC:BC;*P++=K&0x10? FC:BC;
            *P++=K&0x08? FC:BC;*P++=K&0x04? FC:BC;
            *P++=K&0x02? FC:BC;*P  =K&0x01? FC:BC;
            P+=WIDTH-7;
          }
          P+=8-WIDTH*8;
        }
    if(!SpritesOFF) Sprites();
  }
  else memset(P+((word)Y1<<8),XPal[BGColor],(Y2-Y1+1)*WIDTH);
    
  if(EndOfFrame) PutImage;
}


void RefreshScr3(register byte Y1,register byte Y2)
{
  register byte X,Y,J,M,C1,C2;
  register byte *P,*T,*C;

  P=XBuf+WIDTH*(HEIGHT-192)/2;

  if(ScreenON)
  {
    P+=(WIDTH-256)/2;
    T=ChrTab;C=ColTab;
    M=ScanLines212? 26:24;

    for(Y=0;Y<M;Y++,P+=WIDTH*8-8*32)
      for(X=0;X<32;X++,T++)
      {
        C=ColTab+((long)*T<<3)+((Y&3)<<1);
        C1=XPal[*C>>4],C2=XPal[*C&15];
        for(J=0;J<4;J++,P+=WIDTH-7)
        { 
          *P++=C1;*P++=C1;*P++=C1;*P++=C1;
          *P++=C2;*P++=C2;*P++=C2;*P=C2;
        }
        C++;C1=XPal[*C>>4],C2=XPal[*C&15];
        for(J=0;J<4;J++,P+=WIDTH-7)
        {
          *P++=C1;*P++=C1;*P++=C1;*P++=C1;
          *P++=C2;*P++=C2;*P++=C2;*P=C2;
        }
        P+=8-8*WIDTH;  
      }
    if(!SpritesOFF) Sprites();
  }
  else memset(P+((word)Y1<<8),XPal[BGColor],(Y2-Y1+1)*WIDTH);
    
  if(EndOfFrame) PutImage;
}


void RefreshScr4(register byte Y1,register byte Y2)
{
  register word L;
  register byte X,I,J,K,N,CF,CB,C;
  byte *P,*R,*T;

  P=XBuf+WIDTH*(HEIGHT-192)/2+Y1*WIDTH;

  if(ScreenON)
  {
    ColorSprites();

    R=SBuf+((word)Y1<<8);
    P+=(WIDTH-256)/2;
    J=VDP[23]+Y1;
    T=ChrTab+((word)(J&0xF8)<<2);

    for(;Y1<=Y2;Y1++,J++,P+=WIDTH-256)
    {
      if(!J) T=ChrTab;
      N=J&0x07;
      for(X=0;X<32;X++,T++)
      {
        L=((word)(J&0xC0)<<5)+((word)*T<<3)+N;
        CB=*(ColTab+L);I=*(ChrGen+L);
        CF=CB>>4;CB&=0x0F;
        for(K=0;K<8;K++,I<<=1,R++)
        { 
          C=((*R>>4)|*R)&0x0F;
          *P++=XPal[C? C:(I&0x80)? CF:CB];
        }
      }
      if(N<7) T-=32;
    }
  }
  else memset(P,XPal[BGColor],(Y2-Y1+1)*WIDTH);
    
  if(EndOfFrame) PutImage;
}


void RefreshScr5(register byte Y1,register byte Y2)
{
  register byte X,J,C;
  register byte *P,*T,*R;

  P=XBuf+WIDTH*(HEIGHT-192)/2+Y1*WIDTH;

  if(ScreenON)
  {
    ColorSprites();
    R=SBuf+((word)Y1<<8);
    P+=(WIDTH-256)/2;
    J=VDP[23]+Y1;
    T=ChrTab+((word)J<<7);

    for(;Y1<=Y2;Y1++,J++,P+=WIDTH-256)
    {
      if(!J) T=ChrTab;
      for(X=0;X<128;X++,T++)
      {
        C=((*R>>4)|*R)&0x0F;R++;*P++=XPal[C? C:*T>>4];
        C=((*R>>4)|*R)&0x0F;R++;*P++=XPal[C? C:*T&0x0F];
      }
    }
  }
  else memset(P,XPal[BGColor],(Y2-Y1+1)*WIDTH);

  if(EndOfFrame) PutImage;
}
