Annotation of hatari/src/spec512.c, revision 1.1.1.9

1.1       root        1: /*
1.1.1.7   root        2:   Hatari - spec512.c
1.1       root        3: 
1.1.1.7   root        4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
1.1       root        6: 
1.1.1.7   root        7:   Handle storing of writes to ST palette using clock-cycle counts. We can use
                      8:   this to accurately any form of Spectrum512 style images - even down to the
                      9:   way the screen colours change on decompression routines in menus!
                     10: 
                     11:   As the 68000 has a 4-clock cycle increment we can only change palette every
                     12:   4 cycles. This means that on one scanline (512 cycles in 50Hz) we have just
                     13:   512/4=128 places where palette writes can take place. We keep track of this
                     14:   in a table (storing on each scanline and color writes and the cycles on the
                     15:   scanline where they happen). When we draw the screen we simply keep a
                     16:   cycle-count on the line and check this with our table and update the 16-color
                     17:   palette with each change. As the table is already ordered this makes things
                     18:   very simple. Speed is a problem, though, as the palette can change once every
                     19:   4 pixels - that's a lot of processing.
1.1       root       20: */
1.1.1.9 ! root       21: const char Spec512_rcsid[] = "Hatari $Id: spec512.c,v 1.15 2006/02/23 21:00:43 thothy Exp $";
1.1       root       22: 
1.1.1.4   root       23: #include <SDL_byteorder.h>
                     24: 
1.1       root       25: #include "main.h"
1.1.1.9 ! root       26: #include "cycles.h"
1.1       root       27: #include "int.h"
                     28: #include "screen.h"
                     29: #include "spec512.h"
                     30: #include "video.h"
                     31: 
1.1.1.9 ! root       32: /* As 68000 clock multiple of 4 this mean we can only write to the palette this many time per scanline */
        !            33: #define MAX_CYCLEPALETTES_PERLINE  (512/4)
        !            34: 
        !            35: /* Store writes to palette by cycles per scan line, colour and index in ST */
        !            36: typedef struct
        !            37: {
        !            38:   int LineCycles;       /* Number of cycles into line (MUST be div by 4) */
        !            39:   Uint16 Colour;        /* ST Colour value */
        !            40:   Uint16 Index;         /* Index into ST palette (0...15) */
        !            41: } CYCLEPALETTE;
1.1.1.2   root       42: 
1.1.1.8   root       43: /* 314k; 1024-bytes per line */
1.1.1.9 ! root       44: static CYCLEPALETTE CyclePalettes[(MAX_SCANLINES_PER_FRAME+1)*MAX_CYCLEPALETTES_PERLINE];
        !            45: static CYCLEPALETTE *pCyclePalette;
        !            46: static int nCyclePalettes[(MAX_SCANLINES_PER_FRAME+1)];  /* Number of entries in above table for each scanline */
        !            47: static int nPalettesAccess[(MAX_SCANLINES_PER_FRAME+1)]; /* Number of times accessed palette register 'x' in this scan line */
1.1.1.8   root       48: static Uint16 CycleColour;
                     49: static int CycleColourIndex;
                     50: static int nScanLine, ScanLineCycleCount;
                     51: static BOOL bIsSpec512Display;
1.1       root       52: 
1.1.1.4   root       53: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
                     54: static const int STRGBPalEndianTable[16] = {0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15};
                     55: #endif
                     56: 
1.1.1.2   root       57: 
                     58: /*-----------------------------------------------------------------------*/
1.1       root       59: /*
                     60:   Return TRUE if this frame is a Spectrum 512 style image(MUST be low res/non-mix)
                     61: */
                     62: BOOL Spec512_IsImage(void)
                     63: {
1.1.1.2   root       64:   /* Normal Low res screen? */
1.1       root       65:   if ( (STRes==ST_LOW_RES) && (bIsSpec512Display) )
                     66:     return(TRUE);
                     67: 
                     68:   return(FALSE);
                     69: }
                     70: 
1.1.1.2   root       71: 
                     72: /*-----------------------------------------------------------------------*/
1.1       root       73: /*
                     74:   We store every palette access in a table to perform Spectrum 512 colour effects
                     75:   This is cleared on each VBL
                     76: */
                     77: void Spec512_StartVBL(void)
                     78: {
1.1.1.2   root       79:   /* Clear number of cycle palettes on each frame */
1.1.1.9 ! root       80:   memset(nCyclePalettes, 0x0, (nScanlinesPerFrame+1)*sizeof(int));
1.1.1.2   root       81:   /* Clear number of times accessed on entry in palette (used to check if is true Spectrum 512 image) */
1.1.1.9 ! root       82:   memset(nPalettesAccess, 0x0, (nScanlinesPerFrame+1)*sizeof(int));
1.1.1.2   root       83:   /* Set as not Spectrum 512 displayed image */
1.1       root       84:   bIsSpec512Display = FALSE;
                     85: }
                     86: 
1.1.1.2   root       87: 
                     88: /*-----------------------------------------------------------------------*/
1.1       root       89: /*
1.1.1.4   root       90:   Store color into table 'CyclePalettes[]' for screen conversion according
                     91:   to cycles into frame.
1.1       root       92: */
1.1.1.8   root       93: void Spec512_StoreCyclePalette(Uint16 col, Uint32 addr)
1.1       root       94: {
1.1.1.7   root       95:   CYCLEPALETTE *pTmpCyclePalette;
1.1.1.9 ! root       96:   int FrameCycles, ScanLine, nHorPos;
1.1       root       97: 
1.1.1.4   root       98:   CycleColour = col;
                     99:   CycleColourIndex = (addr-0xff8240)>>1;
                    100: 
1.1.1.2   root      101:   /* Find number of cycles into frame */
1.1.1.9 ! root      102:   FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
1.1       root      103: 
1.1.1.2   root      104:   /* Find scan line we are currently on and get index into cycle-palette table */
1.1.1.9 ! root      105:   ScanLine = FrameCycles / nCyclesPerLine;
1.1.1.7   root      106:   pTmpCyclePalette = &CyclePalettes[ (ScanLine*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[ScanLine] ];
1.1.1.9 ! root      107: 
        !           108:   nHorPos = FrameCycles % nCyclesPerLine;
        !           109: 
        !           110:   /* Temporary hack until we've got better CPU cycles emulation:
        !           111:    * The position needs to be corrected for a few positions.
        !           112:    * I think it might be related to the shifter which handles 16 pixels
        !           113:    * at a time / plane, since here we arrive every 20 cycles there are
        !           114:    * synchronizations problems. Now the very good question is : why does
        !           115:    * it happen only for the left part of the screen ???!!!!!
        !           116:    * If we add the same correction for the right part, then bad black pixels
        !           117:    * appear. This definetely requires some investigation... */
        !           118: #if 0
        !           119:   if (nHorPos == 104 || nHorPos == 124 || nHorPos == 144)
        !           120:     nHorPos += 4;
        !           121: #endif
        !           122: 
1.1.1.2   root      123:   /* Do we have a previous entry at the same cycles? If so, 68000 have used a 'move.l' instruction so stagger writes */
1.1.1.7   root      124:   if (nCyclePalettes[ScanLine]>0)
                    125:   {
1.1.1.9 ! root      126:     /* In case the ST uses a move.l or a movem.l to update colors, we need
        !           127:      * to add at least 4 cycles between each color: */
        !           128:     if ((pTmpCyclePalette-1)->LineCycles >= nHorPos)
        !           129:       nHorPos = (pTmpCyclePalette-1)->LineCycles + 4;
1.1       root      130:   }
                    131: 
1.1.1.2   root      132:   /* Store palette access */
1.1.1.9 ! root      133:   pTmpCyclePalette->LineCycles = nHorPos;           /* Cycles into scanline */
        !           134:   pTmpCyclePalette->Colour = CycleColour;           /* Store ST/STe color RGB */
        !           135:   pTmpCyclePalette->Index = CycleColourIndex;       /* And index (0...15) */
        !           136: 
        !           137:   /* Increment count (this can never overflow as you cannot write to the palette more than 'MAX_CYCLEPALETTES_PERLINE' times per scanline) */
1.1       root      138:   nCyclePalettes[ScanLine]++;
                    139: 
1.1.1.2   root      140:   /* Check if program wrote to certain palette entry multiple times on a single scan-line  */
                    141:   /* If we did then we must be using a Spectrum512 image or some kind of colour cycling... */
1.1       root      142:   nPalettesAccess[ScanLine]++;
1.1.1.9 ! root      143:   if (nPalettesAccess[ScanLine] >= 32)
1.1.1.7   root      144:   {
1.1.1.9 ! root      145:     bIsSpec512Display = TRUE;
1.1.1.5   root      146:   }
1.1       root      147: }
                    148: 
1.1.1.2   root      149: 
                    150: /*-----------------------------------------------------------------------*/
1.1       root      151: /*
1.1.1.5   root      152:   Begin palette calculation for Spectrum 512 style images,
1.1       root      153: */
                    154: void Spec512_StartFrame(void)
                    155: {
                    156:   int i;
                    157: 
1.1.1.2   root      158:   /* Set so screen gets full-update when returns from Spectrum 512 display */
1.1       root      159:   Screen_SetFullUpdate();
                    160: 
1.1.1.2   root      161:   /* Set terminators on each line, so when scan during conversion we know when to stop */
1.1.1.9 ! root      162:   for (i = 0; i < (nScanlinesPerFrame+1); i++)
1.1.1.4   root      163:   {
1.1       root      164:     pCyclePalette = &CyclePalettes[ (i*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[i] ];
1.1.1.2   root      165:     pCyclePalette->LineCycles = -1;          /* Term */
1.1       root      166:   }
                    167: 
1.1.1.2   root      168:   /* Copy first line palette, kept in 'HBLPalettes' and store to 'STRGBPalette' */
1.1       root      169:   for(i=0; i<16; i++)
1.1.1.4   root      170:   {
                    171: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.8   root      172:     STRGBPalette[STRGBPalEndianTable[i]] = ST2RGB[pHBLPalettes[i]];
1.1.1.4   root      173: #else
1.1.1.8   root      174:     STRGBPalette[i] = ST2RGB[pHBLPalettes[i]];
1.1.1.4   root      175: #endif
                    176:   }
1.1       root      177: 
1.1.1.2   root      178:   /* Ready for first call to 'Spec512_ScanLine' */
1.1       root      179:   nScanLine = 0;
1.1.1.5   root      180:   if (OverscanMode & OVERSCANMODE_TOP)
                    181:     nScanLine += OVERSCAN_TOP;
1.1       root      182: 
1.1.1.2   root      183:   /* Skip to first line(where start to draw screen from) */
1.1       root      184:   for (i=0; i<(STScreenStartHorizLine+(nStartHBL-OVERSCAN_TOP)); i++)
                    185:     Spec512_ScanWholeLine();
                    186: }
                    187: 
1.1.1.2   root      188: 
                    189: /*-----------------------------------------------------------------------*/
1.1       root      190: /*
                    191:   Scan whole line and build up palette - need to do this so when get to screen line we have
                    192:   the correct 16 colours set
                    193: */
                    194: void Spec512_ScanWholeLine(void)
                    195: {
1.1.1.2   root      196:   /* Store pointer to line of palette cycle writes */
1.1       root      197:   pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
1.1.1.2   root      198:   /* Ready for next scan line */
1.1       root      199:   nScanLine++;
                    200: 
1.1.1.2   root      201:   /* Update palette entries until we reach start of displayed screen */
1.1       root      202:   ScanLineCycleCount = 0;
1.1.1.2   root      203:   Spec512_EndScanLine();        /* Read whole line of palettes and update 'STRGBPalette' */
1.1       root      204: }
                    205: 
1.1.1.2   root      206: 
                    207: /*-----------------------------------------------------------------------*/
1.1       root      208: /*
                    209:   Build up palette for this scan line and store in 'ScanLinePalettes'
                    210: */
                    211: void Spec512_StartScanLine(void)
                    212: {
                    213:   int i;
                    214: 
1.1.1.2   root      215:   /* Store pointer to line of palette cycle writes */
1.1       root      216:   pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
1.1.1.2   root      217:   /* Ready for next scan line */
1.1       root      218:   nScanLine++;
                    219: 
1.1.1.2   root      220:   /* Update palette entries until we reach start of displayed screen */
1.1       root      221:   ScanLineCycleCount = 0;
1.1.1.9 ! root      222:   for(i=0; i<((SCREEN_START_CYCLE-16)/4); i++)  /* This '16' is as we've already added in the 'move' instruction timing */
1.1.1.2   root      223:     Spec512_UpdatePaletteSpan();                /* Update palette for this 4-cycle period */
                    224:   /* And skip for left border is not using overscan display to user */
                    225:   for (i=0; i<(STScreenLeftSkipBytes/2); i++)   /* Eg, 16 bytes = 32 pixels or 8 palette periods */
1.1       root      226:     Spec512_UpdatePaletteSpan();
                    227: }
                    228: 
1.1.1.2   root      229: 
                    230: /*-----------------------------------------------------------------------*/
1.1       root      231: /*
                    232:   Run to end of scan line looking up palettes so 'STRGBPalette' is up-to-date
                    233: */
                    234: void Spec512_EndScanLine(void)
                    235: {
1.1.1.2   root      236:   /* Continue to reads palette until complete so have correct version for next line */
1.1.1.9 ! root      237:   while (ScanLineCycleCount < nCyclesPerLine)
1.1       root      238:     Spec512_UpdatePaletteSpan();
                    239: }
                    240: 
1.1.1.2   root      241: 
                    242: /*-----------------------------------------------------------------------*/
1.1       root      243: /*
                    244:   Update palette for 4-pixels span, storing to 'STRGBPalette'
                    245: */
                    246: void Spec512_UpdatePaletteSpan(void)
                    247: {
1.1.1.2   root      248:   if( pCyclePalette->LineCycles == ScanLineCycleCount )
                    249:    {
                    250:     /* Need to update palette with new entry */
1.1.1.4   root      251: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.8   root      252:     STRGBPalette[STRGBPalEndianTable[pCyclePalette->Index]] = ST2RGB[pCyclePalette->Colour];
1.1.1.4   root      253: #else
1.1.1.8   root      254:     STRGBPalette[pCyclePalette->Index] = ST2RGB[pCyclePalette->Colour];
1.1.1.4   root      255: #endif
1.1.1.2   root      256:     pCyclePalette += 1;
                    257:    }
                    258:   ScanLineCycleCount += 4;      /* Next 4 cycles */
1.1       root      259: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.