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