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

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

unix.superglobalmegacorp.com

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