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

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

unix.superglobalmegacorp.com

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