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