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

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

unix.superglobalmegacorp.com

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