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

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

unix.superglobalmegacorp.com

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