Commit 18180788 authored by Cayce's avatar Cayce
Browse files

apply missing parts of migration from fMSX 3.9 to 4.9, to complement 8d5f4a12

parent 01731fc6
......@@ -72,6 +72,9 @@ void Reset8910(AY8910 *D,int ClockHz,int First)
SetSound(4+First,SND_NOISE);
SetSound(5+First,SND_NOISE);
/* Configure noise generator */
SetNoise(0x10000,16,14);
/* Silence all channels */
for(J=0;J<AY8910_CHANNELS;J++)
{
......@@ -293,7 +296,7 @@ void Sync8910(AY8910 *D,byte Sync)
J = (D->Freq[3]? D->Volume[3]:0)
+ (D->Freq[4]? D->Volume[4]:0)
+ (D->Freq[5]? D->Volume[5]:0);
if(J) Drum(DRM_MIDI|28,(J+2)/3);
if(J) Drum(DRM_MIDI|28,J>255? 255:J);
}
if(Sync!=AY8910_FLUSH) D->Sync=Sync;
......
......@@ -37,7 +37,7 @@ typedef struct
byte R[16]; /* PSG registers contents */
int Freq[AY8910_CHANNELS]; /* Frequencies (0 for off) */
int Volume[AY8910_CHANNELS]; /* Volumes (0..255) */
int Clock; /* Base clock used by PSG */
int Clock; /* Base clock rate (Fin/16) */
int First; /* First used Sound() channel */
byte Changed; /* Bitmap of changed channels */
byte Sync; /* AY8910_SYNC/AY8910_ASYNC */
......
......@@ -112,9 +112,6 @@ void SetVideo(Image *Img,int X,int Y,int W,int H)
VideoY = Y<0? 0:Y>=Img->H? Img->H-1:Y;
VideoW = VideoX+W>Img->W? Img->W-VideoX:W;
VideoH = VideoY+H>Img->H? Img->H-VideoY:H;
#ifdef WINDOWS
FreeImage(&BigScreen);
#endif
}
/** WaitJoystick() *******************************************/
......
......@@ -35,11 +35,18 @@
#define EFF_SHOWFPS 0x0200 /* Show frames-per-sec count */
#define EFF_LCDLINES 0x0400 /* Apply LCD scanlines effect */
#define EFF_VKBD 0x0800 /* Draw virtual keyboard */
#define EFF_SOFTEN2 0x1000 /* Softening algorithm select */
#define EFF_FULLJOY 0x2000 /* Use full screen controls */
#define EFF_TILTJOY 0x4000 /* Use accelerometer controls */
#define EFF_PENJOY 0x8000 /* Use touchpad controls */
#define EFF_VSYNC 0x10000 /* Wait for VBlanks */
#define EFF_DIRECT 0x20000 /* Copy whole VideoImg */
#define EFF_CMYMASK 0x40000 /* Apply vertical CMY mask */
#define EFF_RGBMASK 0x80000 /* Apply vertical RGB mask */
#define EFF_SOFTEN3 0x1000000 /* Softening algorithm select */
#define EFF_MONO 0x2000000 /* Apply monochrome color */
#define EFF_VIGNETTE 0x4000000 /* Apply CRT-like vignetting */
#define EFF_4X3 0x8000000 /* Stretch video to 4x3 ratio */
#if defined(ANDROID)
#define EFF_FIXFFWD 0x100000 /* Persistent FFWD button */
#define EFF_GLES 0x200000 /* OpenGLES video rendering */
......@@ -59,6 +66,22 @@
#define EFF_VARBPP 0x200000 /* X11 determines Image depth */
#endif
#define EFF_SOFTEN_ALL (EFF_SOFTEN|EFF_SOFTEN2|EFF_SOFTEN3)
#define EFF_2XSAI (EFF_SOFTEN)
#define EFF_EPX (EFF_SOFTEN2)
#define EFF_EAGLE (EFF_SOFTEN|EFF_SOFTEN2)
#define EFF_SCALE2X (EFF_SOFTEN3)
#define EFF_HQ4X (EFF_SOFTEN|EFF_SOFTEN3)
#define EFF_NEAREST (EFF_SOFTEN2|EFF_SOFTEN3)
#define EFF_RASTER_ALL (EFF_TVLINES|EFF_LCDLINES)
#define EFF_RASTER (EFF_TVLINES|EFF_LCDLINES)
#define EFF_MASK_ALL (EFF_CMYMASK|EFF_RGBMASK|EFF_MONO)
#define EFF_GREEN (EFF_MONO|EFF_CMYMASK)
#define EFF_AMBER (EFF_MONO|EFF_RGBMASK)
#define EFF_SEPIA (EFF_MONO|EFF_CMYMASK|EFF_RGBMASK)
/** Button Bits **********************************************/
/** Bits returned by GetJoystick() and WaitJoystick(). **/
/*************************************************************/
......@@ -244,12 +267,60 @@ void LcdizeImage(Image *Img,int X,int Y,int W,int H);
/*************************************************************/
void RasterizeImage(Image *Img,int X,int Y,int W,int H);
/** CMYizeImage() ********************************************/
/** Apply vertical CMY stripes to the image. **/
/*************************************************************/
void CMYizeImage(Image *Img,int X,int Y,int W,int H);
/** RGBizeImage() ********************************************/
/** Apply vertical RGB stripes to the image. **/
/*************************************************************/
void RGBizeImage(Image *Img,int X,int Y,int W,int H);
/** MonoImage() **********************************************/
/** Turn image into monochrome. **/
/*************************************************************/
void MonoImage(Image *Img,int X,int Y,int W,int H);
/** SepiaImage() *********************************************/
/** Turn image into sepia tones. **/
/*************************************************************/
void SepiaImage(Image *Img,int X,int Y,int W,int H);
/** GreenImage() *********************************************/
/** Simulate green CRT phosphor. **/
/*************************************************************/
void GreenImage(Image *Img,int X,int Y,int W,int H);
/** AmberImage() *********************************************/
/** Simulate amber CRT phosphor. **/
/*************************************************************/
void AmberImage(Image *Img,int X,int Y,int W,int H);
/** SoftenImage() ********************************************/
/** Uses softening algorithm to interpolate image Src into **/
/** a bigger image Dst. **/
/*************************************************************/
void SoftenImage(Image *Dst,const Image *Src,int X,int Y,int W,int H);
/** SoftenSCALE2X() ******************************************/
/** Uses SCALE2X softening algorithm to interpolate image **/
/** Src into a bigger image Dst. **/
/*************************************************************/
void SoftenSCALE2X(Image *Dst,const Image *Src,int X,int Y,int W,int H);
/** SoftenEPX() **********************************************/
/** Uses EPX softening algorithm to interpolate image Src **/
/** into a bigger image Dst. **/
/*************************************************************/
void SoftenEPX(Image *Dst,const Image *Src,int X,int Y,int W,int H);
/** SoftenEAGLE() ********************************************/
/** Uses EAGLE softening algorithm to interpolate image Src **/
/** into a bigger image Dst. **/
/*************************************************************/
void SoftenEAGLE(Image *Dst,const Image *Src,int X,int Y,int W,int H);
/** ClearImage() *********************************************/
/** Clear image with a given color. **/
/*************************************************************/
......@@ -304,6 +375,11 @@ void SetPalette(pixel N,unsigned char R,unsigned char G,unsigned char B);
/*************************************************************/
unsigned int GetFreeAudio(void);
/** GetTotalAudio() ******************************************/
/** Get total amount of samples in the audio buffer. **/
/*************************************************************/
unsigned int GetTotalAudio(void);
/** WriteAudio() *********************************************/
/** Write up to a given number of samples to audio buffer. **/
/** Returns the number of samples written. **/
......@@ -391,6 +467,12 @@ int ProcessEvents(int Wait);
/*************************************************************/
void SetEffects(unsigned int NewEffects);
/** ParseEffects() *******************************************/
/** Parse command line visual effect options, removing them **/
/** from Args[] and applying to the initial Effects value. **/
/*************************************************************/
unsigned int ParseEffects(char *Args[],unsigned int Effects);
/** GetFilePath() ********************************************/
/** Extracts pathname from filename and returns a pointer **/
/** to the internal buffer containing just the path name **/
......
......@@ -185,13 +185,13 @@ int DSKFile(byte *Dsk,const char *FileName)
/*************************************************************/
const char *DSKFileName(const byte *Dsk,int ID)
{
const unsigned char *Name;
const char *Name;
/* Can't have ID that is out of bounds */
if((ID<1)||(ID>DSK_DIR_SIZE)) return(0);
/* Return file name */
Name=DIRENTRY(Dsk,ID-1);
return(!Name[0]||(Name[0]==0xE5)? 0:Name);
Name=(const char *)DIRENTRY(Dsk,ID-1);
return(!Name[0]||(Name[0]==(char)0xE5)? 0:Name);
}
/** DSKFileSize() ********************************************/
......@@ -371,7 +371,8 @@ int DSKDelete(byte *Dsk,int ID)
/*************************************************************/
byte *DSKLoad(const char *Name,byte *Dsk)
{
byte *Dsk1,*Buf,FN[32],*Path;
byte *Dsk1,*Buf;
char *Path,FN[32];
struct stat FS;
RFILE *F;
struct RDIR *D;
......@@ -457,7 +458,7 @@ byte *DSKLoad(const char *Name,byte *Dsk)
const byte *DSKSave(const char *Name,const byte *Dsk)
{
const char *T;
byte *Path,*P;
char *Path,*P;
struct stat FS;
RFILE *F;
int J,I,K;
......
......@@ -43,13 +43,13 @@ static const struct { byte Note;word Wheel; } Freqs[4096] =
#include "MIDIFreq.h"
};
static const int Programs[5] =
static const int Programs[] =
{
80, /* SND_MELODIC/SND_RECTANGLE */
80, /* SND_TRIANGLE */
122, /* SND_NOISE */
122, /* SND_PERIODIC */
80 /* SND_WAVE */
80, /* SND_WAVE */
};
static struct
......@@ -58,24 +58,25 @@ static struct
int Note;
int Pitch;
int Level;
int Power;
} MidiCH[MIDI_CHANNELS] =
{
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 },
{ -1,-1,-1,-1 }
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 },
{ -1,-1,-1,-1,256 }
};
static struct
......@@ -112,7 +113,9 @@ static struct
/** RenderAudio() Variables *******************************************/
static int SndRate = 0; /* Sound rate (0=Off) */
static int NoiseGen = 1; /* Noise generator seed */
static int NoiseGen = 0x10000; /* Noise generator seed */
static int NoiseOut = 16; /* NoiseGen bit used for output */
static int NoiseXor = 14; /* NoiseGen bit used for XORing */
int MasterSwitch = 0xFFFF; /* Switches to turn channels on/off */
int MasterVolume = 192; /* Master volume */
......@@ -153,13 +156,20 @@ void Sound(int Channel,int Freq,int Volume)
{
/* All parameters have to be valid */
if((Channel<0)||(Channel>=SND_CHANNELS)) return;
Freq = Freq<0? 0:Freq>20000? 0:Freq;
Freq = Freq<0? 0:Freq;
Volume = Volume<0? 0:Volume>255? 255:Volume;
/* Modify wave channel */
/* Modify channel parameters */
WaveCH[Channel].Volume = Volume;
WaveCH[Channel].Freq = Freq;
/* When disabling sound, reset waveform */
if(!Freq||!Volume)
{
WaveCH[Channel].Pos = 0;
WaveCH[Channel].Count = 0;
}
/* Log sound to MIDI file */
MIDISound(Channel,Freq,Volume);
}
......@@ -209,6 +219,18 @@ void SetChannels(int Volume,int Switch)
MasterSwitch = Switch&((1<<SND_CHANNELS)-1);
}
/** SetNoise() ***********************************************/
/** Initialize random noise generator to the given Seed and **/
/** then take random output from OUTBit and XOR it with **/
/** XORBit. **/
/*************************************************************/
void SetNoise(int Seed,int OUTBit,int XORBit)
{
NoiseGen = Seed;
NoiseOut = OUTBit;
NoiseXor = XORBit;
}
/** SetWave() ************************************************/
/** Set waveform for a given channel. The channel will be **/
/** marked with sound type SND_WAVE. Set Rate=0 if you want **/
......@@ -217,6 +239,8 @@ void SetChannels(int Volume,int Switch)
/*************************************************************/
void SetWave(int Channel,const signed char *Data,int Length,int Rate)
{
unsigned int I,J;
/* Channel and waveform length have to be valid */
if((Channel<0)||(Channel>=SND_CHANNELS)||(Length<=0)) return;
......@@ -229,7 +253,19 @@ void SetWave(int Channel,const signed char *Data,int Length,int Rate)
WaveCH[Channel].Data = Data;
/* Log instrument change to MIDI file */
MIDISetSound(Channel,Rate? -1:SND_MELODIC);
MIDISetSound(Channel,Rate? -1:SND_WAVE);
/* Compute overall waveform power for MIDI */
if(Rate) I=0;
else
{
for(J=I=0;J<Length;++J) I+=Data[J]>0? Data[J]:-Data[J];
I = (I<<1)/Length;
I = I>256? 256:I;
}
/* Will use power value when computing MIDI volume */
MidiCH[Channel].Power = I;
}
/** GetWave() ************************************************/
......@@ -414,10 +450,14 @@ void MIDISound(int Channel,int Freq,int Volume)
if(!Volume||!Freq) NoteOff(Channel);
else
{
/* SND_TRIANGLE is twice quieter than SND_MELODIC */
if(MidiCH[Channel].Type==SND_TRIANGLE) Volume=(Volume+1)/2;
/* SND_TRIANGLE has 1/2 volume of SND_MELODIC */
/* SND_WAVE may have different effective volume */
Volume = MidiCH[Channel].Type==SND_TRIANGLE? (Volume>>1)
: MidiCH[Channel].Type==SND_WAVE? ((Volume*MidiCH[Channel].Power)>>8)
: Volume;
/* Compute MIDI note parameters */
MIDIVolume = (127*Volume+128)/255;
MIDIVolume = Volume>>1;
MIDINote = Freqs[Freq/3].Note;
MIDIWheel = Freqs[Freq/3].Wheel;
......@@ -471,7 +511,7 @@ void MIDIDrum(int Type,int Force)
/* Release previous drum */
if(DrumOn) MIDIMessage(0x89,DrumOn,127);
/* Hit next drum */
if(Type) MIDIMessage(0x99,Type,(Force&0xFF)/2);
if(Type) MIDIMessage(0x99,Type,(Force&0xFF)>>1);
DrumOn=Type;
}
......@@ -577,7 +617,6 @@ unsigned int InitSound(unsigned int Rate,unsigned int Latency)
/* Initialize internal variables (keeping MasterVolume/MasterSwitch!) */
SndRate = 0;
NoiseGen = 1;
/* Reset sound parameters */
for(I=0;I<SND_CHANNELS;I++)
......@@ -618,7 +657,6 @@ void RenderAudio(int *Wave,unsigned int Samples)
/* Keep GCC happy about variable initialization */
N=L=A2=0;
/* Waveform generator */
for(J=0;J<SND_CHANNELS;J++)
if(WaveCH[J].Freq&&(V=WaveCH[J].Volume)&&(MasterSwitch&(1<<J)))
......@@ -693,18 +731,27 @@ void RenderAudio(int *Wave,unsigned int Samples)
case SND_NOISE: /* White Noise */
/* For high frequencies, recompute volume */
if(WaveCH[J].Freq<=SndRate) K=0x10000*WaveCH[J].Freq/SndRate;
else { V=V*SndRate/WaveCH[J].Freq;K=0x10000; }
if(WaveCH[J].Freq<SndRate)
K=((unsigned int)WaveCH[J].Freq<<16)/SndRate;
else
{
V = V*SndRate/WaveCH[J].Freq;
K = 0x10000;
}
L1=WaveCH[J].Count;
for(I=0;I<Samples;I++)
{
/* Use NoiseOut bit for output */
Wave[I]+=((NoiseGen>>NoiseOut)&1? 127:-128)*V;
L1+=K;
if(L1&0xFFFF0000)
{
if((NoiseGen<<=1)&0x80000000) NoiseGen^=0x08000001;
/* XOR NoiseOut and NoiseXOR bits and feed them back */
NoiseGen=
(((NoiseGen>>NoiseOut)^(NoiseGen>>NoiseXor))&1)
| ((NoiseGen<<1)&((2<<NoiseOut)-1));
L1&=0xFFFF;
}
Wave[I]+=(NoiseGen&1? 127:-128)*V;
}
WaveCH[J].Count=L1;
break;
......@@ -756,12 +803,12 @@ unsigned int PlayAudio(int *Wave,unsigned int Samples)
{
/* Compute number of samples to convert */
J = sizeof(Buf)/sizeof(sample);
J = Samples<=J? Samples:J;
J = Samples-K>J? J:Samples-K;
/* Convert samples */
for(I=0;I<J;++I)
{
D = ((*Wave++)*MasterVolume)/255;
D = ((*Wave++)*MasterVolume)>>8;
D = D>32767? 32767:D<-32768? -32768:D;
#if defined(BPU16)
Buf[I] = D+32768;
......
......@@ -100,6 +100,13 @@ void SetSound(int Channel,int NewType);
/*************************************************************/
void SetChannels(int Volume,int Switch);
/** SetNoise() ***********************************************/
/** Initialize random noise generator to the given Seed and **/
/** then take random output from OUTBit and XOR it with **/
/** XORBit. **/
/*************************************************************/
void SetNoise(int Seed,int OUTBit,int XORBit);
/** SetWave() ************************************************/
/** Set waveform for a given channel. The channel will be **/
/** marked with sound type SND_WAVE. Set Rate=0 if you want **/
......
......@@ -34,6 +34,7 @@ void Reset1793(WD1793 *D,FDIDisk *Disks,byte Eject)
D->WRLength = 0;
D->RDLength = 0;
D->Wait = 0;
D->Cmd = 0xD0;
/* For all drives... */
for(J=0;J<4;++J)
......@@ -60,8 +61,16 @@ byte Read1793(WD1793 *D,byte A)
A=D->R[0];
/* If no disk present, set F_NOTREADY */
if(!D->Disk[D->Drive]||!D->Disk[D->Drive]->Data) A|=F_NOTREADY;
if(D->Cmd<0x80)
{
/* Keep flipping F_INDEX bit as the disk rotates (Sam Coupe) */
D->R[0]=(D->R[0]^F_INDEX)&(F_INDEX|F_BUSY|F_NOTREADY|F_READONLY|F_TRACK0);
}
else
{
/* When reading status, clear all bits but F_BUSY and F_NOTREADY */
D->R[0]&=F_BUSY|F_NOTREADY;
D->R[0]&=F_BUSY|F_NOTREADY|F_READONLY|F_DRQ;
}
return(A);
case WD1793_TRACK:
case WD1793_SECTOR:
......@@ -125,6 +134,7 @@ byte Write1793(WD1793 *D,byte A,byte V)
{
/* Reset any executing command */
D->RDLength=D->WRLength=0;
D->Cmd=0xD0;
/* Either reset BUSY flag or reset all flags if BUSY=0 */
if(D->R[0]&F_BUSY) D->R[0]&=~F_BUSY;
else D->R[0]=D->Track[D->Drive]? 0:F_TRACK0;
......@@ -137,6 +147,7 @@ byte Write1793(WD1793 *D,byte A,byte V)
if(D->R[0]&F_BUSY) break;
/* Reset status register */
D->R[0]=0x00;
D->Cmd=V;
/* Depending on the command... */
switch(V&0xF0)
{
......
......@@ -90,6 +90,7 @@ typedef struct
byte LastS; /* Last STEP direction */
byte IRQ; /* 0x80: IRQ pending, 0x40: DRQ pending */
byte Wait; /* Expiration counter */
byte Cmd; /* Last command */
int WRLength; /* Data left to write */
int RDLength; /* Data left to read */
......
......@@ -103,8 +103,9 @@ pixel *RefreshBorder(byte Y,pixel C)
/*************************************************************/
void Sprites(byte Y,pixel *Line)
{
static const byte SprHeights[4] = { 8,16,16,32 };
pixel *P,C;
byte H,*PT,*AT;
byte OH,IH,*PT,*AT;
unsigned int M;
int L,K;
......@@ -112,10 +113,11 @@ void Sprites(byte Y,pixel *Line)
VDPStatus[0]&=~0x5F;
/* Assign initial values before counting */
H = Sprites16x16? 16:8;
OH = SprHeights[VDP[1]&0x03];
IH = SprHeights[VDP[1]&0x02];
AT = SprTab-4;
Y += VScroll;
C = 0;
C = MAXSPRITE1+1;
M = 0;
/* Count displayed sprites */
......@@ -124,17 +126,16 @@ void Sprites(byte Y,pixel *Line)
M<<=1;AT+=4; /* Iterating through SprTab */
K=AT[0]; /* K = sprite Y coordinate */
if(K==208) break; /* Iteration terminates if Y=208 */
if(K>256-H) K-=256; /* Y coordinate may be negative */
if(K>256-IH) K-=256; /* Y coordinate may be negative */
/* Mark all valid sprites with 1s, break at MAXSPRITE1 sprites */
if((Y>K)&&(Y<=K+H))
if((Y>K)&&(Y<=K+OH))
{
/* If we exceed the maximum number of sprites per line... */
if(++C==MAXSPRITE1+1)
if(!--C)
{
/* Record extra sprite number in the VDP status register */
VDPStatus[0]|=0x40|(L&0x1F);
/* Set 5thSprite flag in the VDP status register */
VDPStatus[0]|=0x40;
/* Stop drawing sprites, unless all-sprites option enabled */
if(!OPTION(MSX_ALLSPRITE)) break;
}
......@@ -144,6 +145,9 @@ void Sprites(byte Y,pixel *Line)
}
}
/* Mark last checked sprite (5th in line, Y=208, or sprite #31) */
VDPStatus[0]|=L<32? L:31;
/* Draw all marked sprites */
for(;M;M>>=1,AT-=4)
if(M&1)
......@@ -152,38 +156,82 @@ void Sprites(byte Y,pixel *Line)
L=C&0x80? AT[1]-32:AT[1]; /* Sprite may be shifted left by 32 */
C&=0x0F; /* C = sprite color */
if((L<256)&&(L>-H)&&C)
if((L<256)&&(L>-OH)&&C)
{
K=AT[0]; /* K = sprite Y coordinate */
if(K>256-H) K-=256; /* Y coordinate may be negative */
if(K>256-IH) K-=256; /* Y coordinate may be negative */
P=Line+L;
PT=SprGen+((int)(H>8? AT[2]&0xFC:AT[2])<<3)+Y-K-1;
K = Y-K-1;
PT = SprGen+((int)(IH>8? AT[2]&0xFC:AT[2])<<3)+(OH>IH? (K>>1):K);
C=XPal[C];
/* Mask 1: clip left sprite boundary */
K=L>=0? 0x0FFFF:(0x10000>>-L)-1;
K=L>=0? 0xFFFF:(0x10000>>(OH>IH? (-L>>1):-L))-1;
/* Mask 2: clip right sprite boundary */
if(L>256-H) K^=((0x00200>>(H-8))<<(L-257+H))-1;
L+=(int)OH-257;
if(L>=0)
{
L=(IH>8? 0x0002:0x0200)<<(OH>IH? (L>>1):L);
K&=~(L-1);
}
/* Get and clip the sprite data */
K&=((int)PT[0]<<8)|(H>8? PT[16]:0x00);
K&=((int)PT[0]<<8)|(IH>8? PT[16]:0x00);
/* Draw left 8 pixels of the sprite */
if(K&0xFF00)
/* If output size is bigger than the input size... */
if(OH>IH)
{
if(K&0x8000) P[0]=C;if(K&0x4000) P[1]=C;
if(K&0x2000) P[2]=C;if(K&0x1000) P[3]=C;
if(K&0x0800) P[4]=C;if(K&0x0400) P[5]=C;
if(K&0x0200) P[6]=C;if(K&0x0100) P[7]=C;
}
/* Big (zoomed) sprite */
/* Draw right 8 pixels of the sprite */
if(K&0x00FF)
/* Draw left 16 pixels of the sprite */
if(K&0xFF00)
{
if(K&0x8000) P[1]=P[0]=C;
if(K&0x4000) P[3]=P[2]=C;
if(K&0x2000) P[5]=P[4]=C;
if(K&0x1000) P[7]=P[6]=C;
if(K&0x0800) P[9]=P[8]=C;
if(K&0x0400) P[11]=P[10]=C;
if(K&0x0200) P[13]=P[12]=C;
if(K&0x0100) P[15]=P[14]=C;
}
/* Draw right 16 pixels of the sprite */
if(K&0x00FF)
{
if(K&0x0080) P[17]=P[16]=C;
if(K&0x0040) P[19]=P[18]=C;
if(K&0x0020) P[21]=P[20]=C;