|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.