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

1.1       root        1: /*
1.1.1.7   root        2:   Hatari - spec512.c
1.1       root        3: 
1.1.1.17  root        4:   This file is distributed under the GNU General Public License, version 2
                      5:   or at 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).            */
1.1.1.13  root       47: /* 2008/12/14  [NP]    In Spec512_StoreCyclePalette, instead of approximating write position   */
                     48: /*                     by Cycles_GetCounter+8, we use different cases for movem, .l acces and  */
                     49: /*                     .w acces (similar to cycles.c). This gives correct results when using   */
                     50: /*                     "move.w d0,(a0) or move.w (a0)+,(a1)+" for example, which were shifted  */
                     51: /*                     8 or 4 pixels too late. Calibration was made using a custom program to  */
                     52: /*                     compare the results with a real STF in different cases (fix Froggies    */
                     53: /*                     Over The Fence Main Menu).                                              */
1.1.1.14  root       54: /* 2008/12/21  [NP]    Use BusMode to adjust Cycles_GetCounterOnReadAccess and                 */
                     55: /*                     Cycles_GetCounterOnWriteAccess depending on who is owning the           */
                     56: /*                     bus (cpu, blitter).                                                     */
                     57: /* 2009/07/28  [NP]    Use different timings for movem.l and movem.w                           */
1.1.1.18! root       58: /* 2014/01/02  [NP]    In Spec512_StoreCyclePalette, write occurs during the last cycles for   */
        !            59: /*                     i_ADD and i_SUB (fix 'add d1,(a0)' in '4-pixel plasma' by TOS Crew).    */
1.1.1.10  root       60: 
                     61: 
1.1.1.14  root       62: const char Spec512_fileid[] = "Hatari spec512.c : " __DATE__ " " __TIME__;
1.1       root       63: 
1.1.1.18! root       64: #include <SDL_endian.h>
1.1.1.4   root       65: 
1.1       root       66: #include "main.h"
1.1.1.9   root       67: #include "cycles.h"
1.1.1.15  root       68: #include "cycInt.h"
1.1.1.10  root       69: #include "m68000.h"
1.1.1.13  root       70: #include "ioMem.h"
1.1       root       71: #include "screen.h"
                     72: #include "spec512.h"
                     73: #include "video.h"
1.1.1.10  root       74: #include "configuration.h"
1.1.1.12  root       75: 
1.1       root       76: 
1.1.1.9   root       77: /* 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       78: #define MAX_CYCLEPALETTES_PERLINE  ((512/4) + 1)               /* +1 for the '-1' added as a line's terminator */
1.1.1.9   root       79: 
                     80: /* Store writes to palette by cycles per scan line, colour and index in ST */
                     81: typedef struct
                     82: {
1.1.1.10  root       83:        int LineCycles;       /* Number of cycles into line (MUST be div by 4) */
                     84:        Uint16 Colour;        /* ST Colour value */
                     85:        Uint16 Index;         /* Index into ST palette (0...15) */
                     86: }
                     87: CYCLEPALETTE;
1.1.1.2   root       88: 
1.1.1.8   root       89: /* 314k; 1024-bytes per line */
1.1.1.9   root       90: static CYCLEPALETTE CyclePalettes[(MAX_SCANLINES_PER_FRAME+1)*MAX_CYCLEPALETTES_PERLINE];
                     91: static CYCLEPALETTE *pCyclePalette;
                     92: static int nCyclePalettes[(MAX_SCANLINES_PER_FRAME+1)];  /* Number of entries in above table for each scanline */
1.1.1.10  root       93: static int nPalettesAccesses;   /* Number of times accessed palette registers */
1.1.1.8   root       94: static Uint16 CycleColour;
                     95: static int CycleColourIndex;
                     96: static int nScanLine, ScanLineCycleCount;
1.1.1.12  root       97: static bool bIsSpec512Display;
1.1       root       98: 
1.1.1.4   root       99: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.10  root      100: static const int STRGBPalEndianTable[16] =
                    101: {
                    102:        0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15
                    103: };
1.1.1.4   root      104: #endif
                    105: 
1.1.1.2   root      106: 
                    107: /*-----------------------------------------------------------------------*/
1.1.1.10  root      108: /**
1.1.1.16  root      109:  * Return true if this frame is a Spectrum 512 style image (can be low/med
                    110:  * res mix).
1.1.1.10  root      111:  */
1.1.1.12  root      112: bool Spec512_IsImage(void)
1.1       root      113: {
1.1.1.16  root      114:        /* Spec512 mode was triggered in low or med res ? */
                    115:        if (bIsSpec512Display)
1.1.1.14  root      116:                return true;
1.1       root      117: 
1.1.1.14  root      118:        return false;
1.1       root      119: }
                    120: 
1.1.1.2   root      121: 
                    122: /*-----------------------------------------------------------------------*/
1.1.1.10  root      123: /**
1.1.1.14  root      124:  * We store every palette access in a table to perform Spectrum 512 color
                    125:  * effects. This is cleared on each VBL.
1.1.1.10  root      126:  */
1.1       root      127: void Spec512_StartVBL(void)
                    128: {
1.1.1.10  root      129:        /* Clear number of cycle palettes on each frame */
                    130:        memset(nCyclePalettes, 0x0, sizeof(nCyclePalettes));
                    131: 
1.1.1.14  root      132:        /* Clear number of times accessed on entry in palette (used to check if
                    133:         * it is true Spectrum 512 image) */
1.1.1.10  root      134:        nPalettesAccesses = 0;
                    135: 
                    136:        /* Set as not Spectrum 512 displayed image */
1.1.1.14  root      137:        bIsSpec512Display = false;
1.1       root      138: }
                    139: 
1.1.1.2   root      140: 
                    141: /*-----------------------------------------------------------------------*/
1.1.1.10  root      142: /**
                    143:  * Store color into table 'CyclePalettes[]' for screen conversion according
                    144:  * to cycles into frame.
                    145:  */
1.1.1.8   root      146: void Spec512_StoreCyclePalette(Uint16 col, Uint32 addr)
1.1       root      147: {
1.1.1.10  root      148:        CYCLEPALETTE *pTmpCyclePalette;
                    149:        int FrameCycles, ScanLine, nHorPos;
1.1       root      150: 
1.1.1.10  root      151:        if (!ConfigureParams.Screen.nSpec512Threshold)
                    152:                return;
1.1.1.4   root      153: 
1.1.1.10  root      154:        CycleColour = col;
                    155:        CycleColourIndex = (addr-0xff8240)>>1;
1.1       root      156: 
1.1.1.10  root      157:        /* Find number of cycles into frame */
                    158:        /* FIXME [NP] We should use Cycles_GetCounterOnWriteAccess, but it wouldn't     */
                    159:        /* work when using multiple accesses instructions like move.l or movem  */
                    160:        /* To correct this, assume a delay of 8 cycles (should give a good approximation */
                    161:        /* of a move.w or movem.l for example) */
                    162:        //  FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
1.1.1.13  root      163:        if ( BusMode == BUS_MODE_BLITTER )
                    164:        {
                    165:                FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
                    166:        }
                    167:        else                                                    /* BUS_MODE_CPU */
                    168:        {
                    169: #ifdef OLD_CYC_PAL
                    170:                FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
                    171: #else
                    172:                if ( OpcodeFamily == i_MVMLE )
                    173:                {
1.1.1.14  root      174: //                     FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
                    175:                        FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO)
                    176:                                      + (CurrentInstrCycles & ~3);
                    177:                        if (nIoMemAccessSize == SIZE_LONG)      /* long access */
                    178:                                FrameCycles -= 0;
                    179:                        else                                    /* word access */
                    180:                                FrameCycles -= 4;
                    181:                }
1.1.1.18! root      182:                else if ( ( OpcodeFamily == i_ADD ) || ( OpcodeFamily == i_SUB ) )
        !           183:                {
        !           184:                        FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO)
        !           185:                                      + (CurrentInstrCycles & ~3);
        !           186:                        FrameCycles -= 0;                       /* write is made at the end, after prefetch */
        !           187:                }
        !           188:                else                                            /* default case write, then prefetch (mostly for 'move') */
1.1.1.14  root      189:                {
                    190:                        FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO)
                    191:                                      + (CurrentInstrCycles & ~3);
                    192:                        if (nIoMemAccessSize == SIZE_LONG)      /* long access */
                    193:                                FrameCycles -= 8;
                    194:                        else                                    /* word/byte access */
                    195:                                FrameCycles -= 4;
1.1.1.13  root      196:                }
                    197: #endif
                    198:        }
1.1.1.10  root      199: 
                    200: 
                    201:        /* Find scan line we are currently on and get index into cycle-palette table */
1.1.1.14  root      202:        Video_ConvertPosition ( FrameCycles , &ScanLine , &nHorPos );   
1.1.1.11  root      203: 
                    204:        if (ScanLine > MAX_SCANLINES_PER_FRAME)
                    205:                return;
                    206: 
1.1.1.10  root      207:        pTmpCyclePalette = &CyclePalettes[ (ScanLine*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[ScanLine] ];
                    208: 
                    209: 
                    210:        /* Do we have a previous entry at the same cycles? If so, 68000 have used a 'move.l' instruction so stagger writes */
                    211:        if (nCyclePalettes[ScanLine] > 0)
                    212:        {
                    213:                /* In case the ST uses a move.l or a movem.l to update colors, we need
                    214:                 * to add at least 4 cycles between each color: */
                    215:                if ((pTmpCyclePalette-1)->LineCycles >= nHorPos)
                    216:                        nHorPos = (pTmpCyclePalette-1)->LineCycles + 4;
                    217: 
                    218:                if ( nHorPos >= nCyclesPerLine )        /* end of line reached, continue on the next line */
                    219:                {
                    220:                        ScanLine++;
                    221:                        pTmpCyclePalette = &CyclePalettes[ (ScanLine*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[ScanLine] ];
                    222:                        nHorPos = nCyclePalettes[ScanLine] * 4; /* 4 cycles per access */
                    223:                }
                    224:        }
                    225: 
                    226:        /* Store palette access */
                    227:        pTmpCyclePalette->LineCycles = nHorPos;           /* Cycles into scanline */
                    228:        pTmpCyclePalette->Colour = CycleColour;           /* Store ST/STe color RGB */
                    229:        pTmpCyclePalette->Index = CycleColourIndex;       /* And index (0...15) */
                    230: 
1.1.1.14  root      231:        if ( 0 && LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
1.1.1.10  root      232:        {
1.1.1.14  root      233:                int FrameCycles, HblCounterVideo, LineCycles;
                    234: 
                    235:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    236:                LOG_TRACE_PRINT("spec store col line %d cyc=%d col=%x idx=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
                    237:                                ScanLine, nHorPos, CycleColour, CycleColourIndex, FrameCycles,
                    238:                                LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
1.1.1.10  root      239:        }
                    240: 
                    241:        /* Increment count (this can never overflow as you cannot write to the palette more than 'MAX_CYCLEPALETTES_PERLINE-1' times per scanline) */
                    242:        nCyclePalettes[ScanLine]++;
                    243: 
                    244:        /* Check if program wrote to palette registers multiple times on a frame. */
                    245:        /* If so it must be using a spec512 image or some kind of color cycling. */
                    246:        nPalettesAccesses++;
                    247:        if (nPalettesAccesses >= ConfigureParams.Screen.nSpec512Threshold)
                    248:        {
1.1.1.14  root      249:                bIsSpec512Display = true;
1.1.1.10  root      250:        }
1.1       root      251: }
                    252: 
1.1.1.2   root      253: 
                    254: /*-----------------------------------------------------------------------*/
1.1.1.10  root      255: /**
                    256:  * Begin palette calculation for Spectrum 512 style images,
                    257:  */
1.1       root      258: void Spec512_StartFrame(void)
                    259: {
1.1.1.10  root      260:        int i;
1.1       root      261: 
1.1.1.10  root      262:        /* Set so screen gets full-update when returns from Spectrum 512 display */
                    263:        Screen_SetFullUpdate();
1.1       root      264: 
1.1.1.10  root      265:        /* Set terminators on each line, so when scan during conversion we know when to stop */
                    266:        for (i = 0; i < (nScanlinesPerFrame+1); i++)
                    267:        {
                    268:                pCyclePalette = &CyclePalettes[ (i*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[i] ];
                    269:                pCyclePalette->LineCycles = -1;          /* Term */
                    270:        }
                    271: 
1.1.1.12  root      272:        /* Copy first line palette, kept in 'HBLPalettes' and store to 'STRGBPalette' */
                    273:        for (i = 0; i < 16; i++)
                    274:        {
1.1.1.4   root      275: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.12  root      276:                STRGBPalette[STRGBPalEndianTable[i]] = ST2RGB[pHBLPalettes[i]];
1.1.1.4   root      277: #else
1.1.1.12  root      278:                STRGBPalette[i] = ST2RGB[pHBLPalettes[i]];
1.1.1.4   root      279: #endif
1.1.1.12  root      280:        }
1.1       root      281: 
1.1.1.10  root      282:        /* Ready for first call to 'Spec512_ScanLine' */
                    283:        nScanLine = 0;
                    284:        if (OverscanMode & OVERSCANMODE_TOP)
                    285:                nScanLine += OVERSCAN_TOP;
                    286: 
                    287:        /* Skip to first line(where start to draw screen from) */
                    288:        for (i = 0; i < (STScreenStartHorizLine+(nStartHBL-OVERSCAN_TOP)); i++)
                    289:                Spec512_ScanWholeLine();
1.1       root      290: }
                    291: 
1.1.1.2   root      292: 
                    293: /*-----------------------------------------------------------------------*/
1.1.1.10  root      294: /**
                    295:  * Scan whole line and build up palette - need to do this so when get to screen line we have
                    296:  * the correct 16 colours set
                    297:  */
1.1       root      298: void Spec512_ScanWholeLine(void)
                    299: {
1.1.1.10  root      300:        /* Store pointer to line of palette cycle writes */
                    301:        pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
                    302:        /* Ready for next scan line */
                    303:        nScanLine++;
                    304: 
                    305:        /* Update palette entries until we reach start of displayed screen */
                    306:        ScanLineCycleCount = 0;
                    307:        Spec512_EndScanLine();        /* Read whole line of palettes and update 'STRGBPalette' */
1.1       root      308: }
                    309: 
1.1.1.2   root      310: 
                    311: /*-----------------------------------------------------------------------*/
1.1.1.10  root      312: /**
                    313:  * Build up palette for this scan line and store in 'ScanLinePalettes'
                    314:  */
1.1       root      315: void Spec512_StartScanLine(void)
                    316: {
1.1.1.10  root      317:        int i;
                    318:        int LineStartCycle;
1.1       root      319: 
1.1.1.10  root      320:        /* Store pointer to line of palette cycle writes */
                    321:        pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
                    322:        /* Ready for next scan line */
                    323:        nScanLine++;
                    324: 
                    325:        if ( nScanlinesPerFrame == SCANLINES_PER_FRAME_50HZ )
                    326:                LineStartCycle = LINE_START_CYCLE_50;                   /* The screen was 50 Hz */
                    327:        else
                    328:                LineStartCycle = LINE_START_CYCLE_60;                   /* The screen was 60 Hz */
                    329: 
                    330:        /* Update palette entries until we reach start of displayed screen */
                    331:        ScanLineCycleCount = 0;
                    332: //     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.13  root      333: #ifdef OLD_CYC_PAL
1.1.1.10  root      334:        for (i=0; i<((LineStartCycle-SCREENBYTES_LEFT*2)/4 + 6); i++)   /* [NP] '6' is required to align pixels and colors */
1.1.1.13  root      335: #else
                    336:        for (i=0; i<((LineStartCycle-SCREENBYTES_LEFT*2)/4 + 7); i++)   /* [NP] '7' is required to align pixels and colors */
                    337: #endif
1.1.1.10  root      338:                Spec512_UpdatePaletteSpan();              /* Update palette for this 4-cycle period */
                    339: 
                    340:        /* And skip for left border is not using overscan display to user */
                    341:        for (i=0; i<(STScreenLeftSkipBytes/2); i++)   /* Eg, 16 bytes = 32 pixels or 8 palette periods */
                    342:                Spec512_UpdatePaletteSpan();
1.1       root      343: }
                    344: 
1.1.1.2   root      345: 
                    346: /*-----------------------------------------------------------------------*/
1.1.1.10  root      347: /**
                    348:  * Run to end of scan line looking up palettes so 'STRGBPalette' is up-to-date
                    349:  */
1.1       root      350: void Spec512_EndScanLine(void)
                    351: {
1.1.1.10  root      352:        /* Continue to reads palette until complete so have correct version for next line */
                    353:        while (ScanLineCycleCount < nCyclesPerLine)
                    354:                Spec512_UpdatePaletteSpan();
1.1       root      355: }
                    356: 
1.1.1.2   root      357: 
                    358: /*-----------------------------------------------------------------------*/
1.1.1.10  root      359: /**
                    360:  * Update palette for 4-pixels span, storing to 'STRGBPalette'
                    361:  */
1.1       root      362: void Spec512_UpdatePaletteSpan(void)
                    363: {
1.1.1.10  root      364:        if (pCyclePalette->LineCycles == ScanLineCycleCount)
                    365:        {
                    366:                /* Need to update palette with new entry */
1.1.1.4   root      367: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.10  root      368:                STRGBPalette[STRGBPalEndianTable[pCyclePalette->Index]] = ST2RGB[pCyclePalette->Colour];
1.1.1.4   root      369: #else
1.1.1.10  root      370:                STRGBPalette[pCyclePalette->Index] = ST2RGB[pCyclePalette->Colour];
1.1.1.16  root      371: //fprintf ( stderr , "upd spec cyc %d %x %x\n" , ScanLineCycleCount , pCyclePalette->Index , pCyclePalette->Colour );
1.1.1.4   root      372: #endif
1.1.1.10  root      373:                pCyclePalette += 1;
                    374:        }
                    375:        ScanLineCycleCount += 4;      /* Next 4 cycles */
1.1       root      376: }

unix.superglobalmegacorp.com

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