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