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

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.10  root       21: 
                     22: 
                     23: /* 2008/01/12  [NP]    In Spec512_StoreCyclePalette, don't use Cycles_GetCounterOnWriteAccess  */
                     24: /*                     as it doesn't support movem for example. Use Cycles_GetCounter with     */
                     25: /*                     an average  correction of 8 cycles (this should be fixed).              */
                     26: /*                     In Spec512_StartScanLine, better handling of SCREENBYTES_LEFT when      */
                     27: /*                     border are present. Better alignement of pixel and color when left      */
                     28: /*                     border is removed (Rotofull in Nostalgia Demo, When Colors Are Going    */
                     29: /*                     Bang Bang by DF in Punish Your Machine).                                */
                     30: /* 2008/01/13  [NP]    In Spec512_StoreCyclePalette, if a movem was used to access several     */
                     31: /*                     color regs just before the end of a line, the value for nHorPos was not */
                     32: /*                     checked and could take values >= 512, which means some colors in the    */
                     33: /*                     palette were overwritten next time colors were stored on the next line  */
                     34: /*                     and the palette was not set correctly, especially in the bottom of the  */
                     35: /*                     screen (Decade Demo Main Menu, Punish Your Machine Main Menu, ULM       */
                     36: /*                     Hidden Screen in Oh Crickey...).                                        */
                     37: /* 2008/01/13  [NP]    One line should contain 512/4 + 1 colors slots, not 512/4. Else, if     */
                     38: /*                     a program manages to change colors every 4 cycles, the '-1' terminator  */
                     39: /*                     added by Spec512_StartFrame will in fact be written on cycle 0 in the   */
                     40: /*                     next line (this is theorical, practically no program changes the color  */
                     41: /*                     128 times per line).                                                    */
                     42: /*                     Use nCyclesPerLine instead of 512 to check if nHorPos is too big and if */
                     43: /*                     the colors must be stored on the next line when freq is 50 or 60Hz      */
                     44: /*                     (readme.prg by TEX (in 1987))                                           */
                     45: /* 2008/01/24  [NP]    In Spec512_StartScanLine, use different values for LineStartCycle when  */
                     46: /*                     running in 50 Hz or 60 Hz (TEX Spectrum Slideshow in 60 Hz).            */
                     47: 
                     48: 
1.1.1.12! root       49: const char Spec512_rcsid[] = "Hatari $Id: spec512.c,v 1.30 2008/11/03 22:29:56 npomarede Exp $";
1.1       root       50: 
1.1.1.4   root       51: #include <SDL_byteorder.h>
                     52: 
1.1       root       53: #include "main.h"
1.1.1.9   root       54: #include "cycles.h"
1.1       root       55: #include "int.h"
1.1.1.10  root       56: #include "m68000.h"
1.1       root       57: #include "screen.h"
                     58: #include "spec512.h"
                     59: #include "video.h"
1.1.1.10  root       60: #include "configuration.h"
1.1.1.12! root       61: 
1.1       root       62: 
1.1.1.9   root       63: /* As 68000 clock multiple of 4 this mean we can only write to the palette this many time per scanline */
1.1.1.10  root       64: #define MAX_CYCLEPALETTES_PERLINE  ((512/4) + 1)               /* +1 for the '-1' added as a line's terminator */
1.1.1.9   root       65: 
                     66: /* Store writes to palette by cycles per scan line, colour and index in ST */
                     67: typedef struct
                     68: {
1.1.1.10  root       69:        int LineCycles;       /* Number of cycles into line (MUST be div by 4) */
                     70:        Uint16 Colour;        /* ST Colour value */
                     71:        Uint16 Index;         /* Index into ST palette (0...15) */
                     72: }
                     73: CYCLEPALETTE;
1.1.1.2   root       74: 
1.1.1.8   root       75: /* 314k; 1024-bytes per line */
1.1.1.9   root       76: static CYCLEPALETTE CyclePalettes[(MAX_SCANLINES_PER_FRAME+1)*MAX_CYCLEPALETTES_PERLINE];
                     77: static CYCLEPALETTE *pCyclePalette;
                     78: static int nCyclePalettes[(MAX_SCANLINES_PER_FRAME+1)];  /* Number of entries in above table for each scanline */
1.1.1.10  root       79: static int nPalettesAccesses;   /* Number of times accessed palette registers */
1.1.1.8   root       80: static Uint16 CycleColour;
                     81: static int CycleColourIndex;
                     82: static int nScanLine, ScanLineCycleCount;
1.1.1.12! root       83: static bool bIsSpec512Display;
1.1       root       84: 
1.1.1.4   root       85: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.10  root       86: static const int STRGBPalEndianTable[16] =
                     87: {
                     88:        0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15
                     89: };
1.1.1.4   root       90: #endif
                     91: 
1.1.1.2   root       92: 
                     93: /*-----------------------------------------------------------------------*/
1.1.1.10  root       94: /**
                     95:  * Return TRUE if this frame is a Spectrum 512 style image(MUST be low res/non-mix)
                     96:  */
1.1.1.12! root       97: bool Spec512_IsImage(void)
1.1       root       98: {
1.1.1.10  root       99:        /* Normal Low res screen? */
                    100:        if (STRes == ST_LOW_RES && bIsSpec512Display)
                    101:                return TRUE;
1.1       root      102: 
1.1.1.10  root      103:        return FALSE;
1.1       root      104: }
                    105: 
1.1.1.2   root      106: 
                    107: /*-----------------------------------------------------------------------*/
1.1.1.10  root      108: /**
                    109:  * We store every palette access in a table to perform Spectrum 512 colour effects
                    110:  * This is cleared on each VBL
                    111:  */
1.1       root      112: void Spec512_StartVBL(void)
                    113: {
1.1.1.10  root      114:        /* Clear number of cycle palettes on each frame */
                    115:        memset(nCyclePalettes, 0x0, sizeof(nCyclePalettes));
                    116: 
                    117:        /* Clear number of times accessed on entry in palette (used to check if is true Spectrum 512 image) */
                    118:        nPalettesAccesses = 0;
                    119: 
                    120:        /* Set as not Spectrum 512 displayed image */
                    121:        bIsSpec512Display = FALSE;
1.1       root      122: }
                    123: 
1.1.1.2   root      124: 
                    125: /*-----------------------------------------------------------------------*/
1.1.1.10  root      126: /**
                    127:  * Store color into table 'CyclePalettes[]' for screen conversion according
                    128:  * to cycles into frame.
                    129:  */
1.1.1.8   root      130: void Spec512_StoreCyclePalette(Uint16 col, Uint32 addr)
1.1       root      131: {
1.1.1.10  root      132:        CYCLEPALETTE *pTmpCyclePalette;
                    133:        int FrameCycles, ScanLine, nHorPos;
1.1       root      134: 
1.1.1.10  root      135:        if (!ConfigureParams.Screen.nSpec512Threshold)
                    136:                return;
1.1.1.4   root      137: 
1.1.1.10  root      138:        CycleColour = col;
                    139:        CycleColourIndex = (addr-0xff8240)>>1;
1.1       root      140: 
1.1.1.10  root      141:        /* Find number of cycles into frame */
                    142:        /* FIXME [NP] We should use Cycles_GetCounterOnWriteAccess, but it wouldn't     */
                    143:        /* work when using multiple accesses instructions like move.l or movem  */
                    144:        /* To correct this, assume a delay of 8 cycles (should give a good approximation */
                    145:        /* of a move.w or movem.l for example) */
                    146:        //  FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
                    147:        FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
                    148: 
                    149: 
                    150:        /* Find scan line we are currently on and get index into cycle-palette table */
                    151:        ScanLine = FrameCycles / nCyclesPerLine;
1.1.1.11  root      152: 
                    153:        if (ScanLine > MAX_SCANLINES_PER_FRAME)
                    154:                return;
                    155: 
1.1.1.10  root      156:        pTmpCyclePalette = &CyclePalettes[ (ScanLine*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[ScanLine] ];
                    157: 
                    158:        nHorPos = FrameCycles % nCyclesPerLine;
                    159: 
                    160:        /* Do we have a previous entry at the same cycles? If so, 68000 have used a 'move.l' instruction so stagger writes */
                    161:        if (nCyclePalettes[ScanLine] > 0)
                    162:        {
                    163:                /* In case the ST uses a move.l or a movem.l to update colors, we need
                    164:                 * to add at least 4 cycles between each color: */
                    165:                if ((pTmpCyclePalette-1)->LineCycles >= nHorPos)
                    166:                        nHorPos = (pTmpCyclePalette-1)->LineCycles + 4;
                    167: 
                    168:                if ( nHorPos >= nCyclesPerLine )        /* end of line reached, continue on the next line */
                    169:                {
                    170:                        ScanLine++;
                    171:                        pTmpCyclePalette = &CyclePalettes[ (ScanLine*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[ScanLine] ];
                    172:                        nHorPos = nCyclePalettes[ScanLine] * 4; /* 4 cycles per access */
                    173:                }
                    174:        }
                    175: 
                    176:        /* Store palette access */
                    177:        pTmpCyclePalette->LineCycles = nHorPos;           /* Cycles into scanline */
                    178:        pTmpCyclePalette->Colour = CycleColour;           /* Store ST/STe color RGB */
                    179:        pTmpCyclePalette->Index = CycleColourIndex;       /* And index (0...15) */
                    180: 
                    181:        if ( 0 && HATARI_TRACE_LEVEL ( HATARI_TRACE_VIDEO_COLOR ) )
                    182:        {
                    183:                int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
                    184:                int nLineCycles = nFrameCycles % nCyclesPerLine;
                    185:                HATARI_TRACE_PRINT ( "spec store col line %d cyc=%d col=%x idx=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" , ScanLine , nHorPos , CycleColour , CycleColourIndex ,
                    186:                                     nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
                    187:        }
                    188: 
                    189:        /* Increment count (this can never overflow as you cannot write to the palette more than 'MAX_CYCLEPALETTES_PERLINE-1' times per scanline) */
                    190:        nCyclePalettes[ScanLine]++;
                    191: 
                    192:        /* Check if program wrote to palette registers multiple times on a frame. */
                    193:        /* If so it must be using a spec512 image or some kind of color cycling. */
                    194:        nPalettesAccesses++;
                    195:        if (nPalettesAccesses >= ConfigureParams.Screen.nSpec512Threshold)
                    196:        {
                    197:                bIsSpec512Display = TRUE;
                    198:        }
1.1       root      199: }
                    200: 
1.1.1.2   root      201: 
                    202: /*-----------------------------------------------------------------------*/
1.1.1.10  root      203: /**
                    204:  * Begin palette calculation for Spectrum 512 style images,
                    205:  */
1.1       root      206: void Spec512_StartFrame(void)
                    207: {
1.1.1.10  root      208:        int i;
1.1       root      209: 
1.1.1.10  root      210:        /* Set so screen gets full-update when returns from Spectrum 512 display */
                    211:        Screen_SetFullUpdate();
1.1       root      212: 
1.1.1.10  root      213:        /* Set terminators on each line, so when scan during conversion we know when to stop */
                    214:        for (i = 0; i < (nScanlinesPerFrame+1); i++)
                    215:        {
                    216:                pCyclePalette = &CyclePalettes[ (i*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[i] ];
                    217:                pCyclePalette->LineCycles = -1;          /* Term */
                    218:        }
                    219: 
1.1.1.12! root      220:        /* Copy first line palette, kept in 'HBLPalettes' and store to 'STRGBPalette' */
        !           221:        for (i = 0; i < 16; i++)
        !           222:        {
1.1.1.4   root      223: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.12! root      224:                STRGBPalette[STRGBPalEndianTable[i]] = ST2RGB[pHBLPalettes[i]];
1.1.1.4   root      225: #else
1.1.1.12! root      226:                STRGBPalette[i] = ST2RGB[pHBLPalettes[i]];
1.1.1.4   root      227: #endif
1.1.1.12! root      228:        }
1.1       root      229: 
1.1.1.10  root      230:        /* Ready for first call to 'Spec512_ScanLine' */
                    231:        nScanLine = 0;
                    232:        if (OverscanMode & OVERSCANMODE_TOP)
                    233:                nScanLine += OVERSCAN_TOP;
                    234: 
                    235:        /* Skip to first line(where start to draw screen from) */
                    236:        for (i = 0; i < (STScreenStartHorizLine+(nStartHBL-OVERSCAN_TOP)); i++)
                    237:                Spec512_ScanWholeLine();
1.1       root      238: }
                    239: 
1.1.1.2   root      240: 
                    241: /*-----------------------------------------------------------------------*/
1.1.1.10  root      242: /**
                    243:  * Scan whole line and build up palette - need to do this so when get to screen line we have
                    244:  * the correct 16 colours set
                    245:  */
1.1       root      246: void Spec512_ScanWholeLine(void)
                    247: {
1.1.1.10  root      248:        /* Store pointer to line of palette cycle writes */
                    249:        pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
                    250:        /* Ready for next scan line */
                    251:        nScanLine++;
                    252: 
                    253:        /* Update palette entries until we reach start of displayed screen */
                    254:        ScanLineCycleCount = 0;
                    255:        Spec512_EndScanLine();        /* Read whole line of palettes and update 'STRGBPalette' */
1.1       root      256: }
                    257: 
1.1.1.2   root      258: 
                    259: /*-----------------------------------------------------------------------*/
1.1.1.10  root      260: /**
                    261:  * Build up palette for this scan line and store in 'ScanLinePalettes'
                    262:  */
1.1       root      263: void Spec512_StartScanLine(void)
                    264: {
1.1.1.10  root      265:        int i;
                    266:        int LineStartCycle;
1.1       root      267: 
1.1.1.10  root      268:        /* Store pointer to line of palette cycle writes */
                    269:        pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
                    270:        /* Ready for next scan line */
                    271:        nScanLine++;
                    272: 
                    273:        if ( nScanlinesPerFrame == SCANLINES_PER_FRAME_50HZ )
                    274:                LineStartCycle = LINE_START_CYCLE_50;                   /* The screen was 50 Hz */
                    275:        else
                    276:                LineStartCycle = LINE_START_CYCLE_60;                   /* The screen was 60 Hz */
                    277: 
                    278:        /* Update palette entries until we reach start of displayed screen */
                    279:        ScanLineCycleCount = 0;
                    280: //     for(i=0; i<((SCREEN_START_CYCLE-16)/4); i++)  /* This '16' is as we've already added in the 'move' instruction timing */
                    281:        for (i=0; i<((LineStartCycle-SCREENBYTES_LEFT*2)/4 + 6); i++)   /* [NP] '6' is required to align pixels and colors */
                    282:                Spec512_UpdatePaletteSpan();              /* Update palette for this 4-cycle period */
                    283: 
                    284:        /* And skip for left border is not using overscan display to user */
                    285:        for (i=0; i<(STScreenLeftSkipBytes/2); i++)   /* Eg, 16 bytes = 32 pixels or 8 palette periods */
                    286:                Spec512_UpdatePaletteSpan();
1.1       root      287: }
                    288: 
1.1.1.2   root      289: 
                    290: /*-----------------------------------------------------------------------*/
1.1.1.10  root      291: /**
                    292:  * Run to end of scan line looking up palettes so 'STRGBPalette' is up-to-date
                    293:  */
1.1       root      294: void Spec512_EndScanLine(void)
                    295: {
1.1.1.10  root      296:        /* Continue to reads palette until complete so have correct version for next line */
                    297:        while (ScanLineCycleCount < nCyclesPerLine)
                    298:                Spec512_UpdatePaletteSpan();
1.1       root      299: }
                    300: 
1.1.1.2   root      301: 
                    302: /*-----------------------------------------------------------------------*/
1.1.1.10  root      303: /**
                    304:  * Update palette for 4-pixels span, storing to 'STRGBPalette'
                    305:  */
1.1       root      306: void Spec512_UpdatePaletteSpan(void)
                    307: {
1.1.1.10  root      308:        if (pCyclePalette->LineCycles == ScanLineCycleCount)
                    309:        {
                    310:                /* Need to update palette with new entry */
1.1.1.4   root      311: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.10  root      312:                STRGBPalette[STRGBPalEndianTable[pCyclePalette->Index]] = ST2RGB[pCyclePalette->Colour];
1.1.1.4   root      313: #else
1.1.1.10  root      314:                STRGBPalette[pCyclePalette->Index] = ST2RGB[pCyclePalette->Colour];
1.1.1.4   root      315: #endif
1.1.1.10  root      316:                pCyclePalette += 1;
                    317:        }
                    318:        ScanLineCycleCount += 4;      /* Next 4 cycles */
1.1       root      319: }

unix.superglobalmegacorp.com

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