|
|
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.9 ! root 21: const char Spec512_rcsid[] = "Hatari $Id: spec512.c,v 1.15 2006/02/23 21:00:43 thothy Exp $";
1.1 root 22:
1.1.1.4 root 23: #include <SDL_byteorder.h>
24:
1.1 root 25: #include "main.h"
1.1.1.9 ! root 26: #include "cycles.h"
1.1 root 27: #include "int.h"
28: #include "screen.h"
29: #include "spec512.h"
30: #include "video.h"
31:
1.1.1.9 ! root 32: /* As 68000 clock multiple of 4 this mean we can only write to the palette this many time per scanline */
! 33: #define MAX_CYCLEPALETTES_PERLINE (512/4)
! 34:
! 35: /* Store writes to palette by cycles per scan line, colour and index in ST */
! 36: typedef struct
! 37: {
! 38: int LineCycles; /* Number of cycles into line (MUST be div by 4) */
! 39: Uint16 Colour; /* ST Colour value */
! 40: Uint16 Index; /* Index into ST palette (0...15) */
! 41: } CYCLEPALETTE;
1.1.1.2 root 42:
1.1.1.8 root 43: /* 314k; 1024-bytes per line */
1.1.1.9 ! root 44: static CYCLEPALETTE CyclePalettes[(MAX_SCANLINES_PER_FRAME+1)*MAX_CYCLEPALETTES_PERLINE];
! 45: static CYCLEPALETTE *pCyclePalette;
! 46: static int nCyclePalettes[(MAX_SCANLINES_PER_FRAME+1)]; /* Number of entries in above table for each scanline */
! 47: static int nPalettesAccess[(MAX_SCANLINES_PER_FRAME+1)]; /* Number of times accessed palette register 'x' in this scan line */
1.1.1.8 root 48: static Uint16 CycleColour;
49: static int CycleColourIndex;
50: static int nScanLine, ScanLineCycleCount;
51: static BOOL bIsSpec512Display;
1.1 root 52:
1.1.1.4 root 53: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
54: static const int STRGBPalEndianTable[16] = {0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15};
55: #endif
56:
1.1.1.2 root 57:
58: /*-----------------------------------------------------------------------*/
1.1 root 59: /*
60: Return TRUE if this frame is a Spectrum 512 style image(MUST be low res/non-mix)
61: */
62: BOOL Spec512_IsImage(void)
63: {
1.1.1.2 root 64: /* Normal Low res screen? */
1.1 root 65: if ( (STRes==ST_LOW_RES) && (bIsSpec512Display) )
66: return(TRUE);
67:
68: return(FALSE);
69: }
70:
1.1.1.2 root 71:
72: /*-----------------------------------------------------------------------*/
1.1 root 73: /*
74: We store every palette access in a table to perform Spectrum 512 colour effects
75: This is cleared on each VBL
76: */
77: void Spec512_StartVBL(void)
78: {
1.1.1.2 root 79: /* Clear number of cycle palettes on each frame */
1.1.1.9 ! root 80: memset(nCyclePalettes, 0x0, (nScanlinesPerFrame+1)*sizeof(int));
1.1.1.2 root 81: /* Clear number of times accessed on entry in palette (used to check if is true Spectrum 512 image) */
1.1.1.9 ! root 82: memset(nPalettesAccess, 0x0, (nScanlinesPerFrame+1)*sizeof(int));
1.1.1.2 root 83: /* Set as not Spectrum 512 displayed image */
1.1 root 84: bIsSpec512Display = FALSE;
85: }
86:
1.1.1.2 root 87:
88: /*-----------------------------------------------------------------------*/
1.1 root 89: /*
1.1.1.4 root 90: Store color into table 'CyclePalettes[]' for screen conversion according
91: to cycles into frame.
1.1 root 92: */
1.1.1.8 root 93: void Spec512_StoreCyclePalette(Uint16 col, Uint32 addr)
1.1 root 94: {
1.1.1.7 root 95: CYCLEPALETTE *pTmpCyclePalette;
1.1.1.9 ! root 96: int FrameCycles, ScanLine, nHorPos;
1.1 root 97:
1.1.1.4 root 98: CycleColour = col;
99: CycleColourIndex = (addr-0xff8240)>>1;
100:
1.1.1.2 root 101: /* Find number of cycles into frame */
1.1.1.9 ! root 102: FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
1.1 root 103:
1.1.1.2 root 104: /* Find scan line we are currently on and get index into cycle-palette table */
1.1.1.9 ! root 105: ScanLine = FrameCycles / nCyclesPerLine;
1.1.1.7 root 106: pTmpCyclePalette = &CyclePalettes[ (ScanLine*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[ScanLine] ];
1.1.1.9 ! root 107:
! 108: nHorPos = FrameCycles % nCyclesPerLine;
! 109:
! 110: /* Temporary hack until we've got better CPU cycles emulation:
! 111: * The position needs to be corrected for a few positions.
! 112: * I think it might be related to the shifter which handles 16 pixels
! 113: * at a time / plane, since here we arrive every 20 cycles there are
! 114: * synchronizations problems. Now the very good question is : why does
! 115: * it happen only for the left part of the screen ???!!!!!
! 116: * If we add the same correction for the right part, then bad black pixels
! 117: * appear. This definetely requires some investigation... */
! 118: #if 0
! 119: if (nHorPos == 104 || nHorPos == 124 || nHorPos == 144)
! 120: nHorPos += 4;
! 121: #endif
! 122:
1.1.1.2 root 123: /* Do we have a previous entry at the same cycles? If so, 68000 have used a 'move.l' instruction so stagger writes */
1.1.1.7 root 124: if (nCyclePalettes[ScanLine]>0)
125: {
1.1.1.9 ! root 126: /* In case the ST uses a move.l or a movem.l to update colors, we need
! 127: * to add at least 4 cycles between each color: */
! 128: if ((pTmpCyclePalette-1)->LineCycles >= nHorPos)
! 129: nHorPos = (pTmpCyclePalette-1)->LineCycles + 4;
1.1 root 130: }
131:
1.1.1.2 root 132: /* Store palette access */
1.1.1.9 ! root 133: pTmpCyclePalette->LineCycles = nHorPos; /* Cycles into scanline */
! 134: pTmpCyclePalette->Colour = CycleColour; /* Store ST/STe color RGB */
! 135: pTmpCyclePalette->Index = CycleColourIndex; /* And index (0...15) */
! 136:
! 137: /* Increment count (this can never overflow as you cannot write to the palette more than 'MAX_CYCLEPALETTES_PERLINE' times per scanline) */
1.1 root 138: nCyclePalettes[ScanLine]++;
139:
1.1.1.2 root 140: /* Check if program wrote to certain palette entry multiple times on a single scan-line */
141: /* If we did then we must be using a Spectrum512 image or some kind of colour cycling... */
1.1 root 142: nPalettesAccess[ScanLine]++;
1.1.1.9 ! root 143: if (nPalettesAccess[ScanLine] >= 32)
1.1.1.7 root 144: {
1.1.1.9 ! root 145: bIsSpec512Display = TRUE;
1.1.1.5 root 146: }
1.1 root 147: }
148:
1.1.1.2 root 149:
150: /*-----------------------------------------------------------------------*/
1.1 root 151: /*
1.1.1.5 root 152: Begin palette calculation for Spectrum 512 style images,
1.1 root 153: */
154: void Spec512_StartFrame(void)
155: {
156: int i;
157:
1.1.1.2 root 158: /* Set so screen gets full-update when returns from Spectrum 512 display */
1.1 root 159: Screen_SetFullUpdate();
160:
1.1.1.2 root 161: /* Set terminators on each line, so when scan during conversion we know when to stop */
1.1.1.9 ! root 162: for (i = 0; i < (nScanlinesPerFrame+1); i++)
1.1.1.4 root 163: {
1.1 root 164: pCyclePalette = &CyclePalettes[ (i*MAX_CYCLEPALETTES_PERLINE) + nCyclePalettes[i] ];
1.1.1.2 root 165: pCyclePalette->LineCycles = -1; /* Term */
1.1 root 166: }
167:
1.1.1.2 root 168: /* Copy first line palette, kept in 'HBLPalettes' and store to 'STRGBPalette' */
1.1 root 169: for(i=0; i<16; i++)
1.1.1.4 root 170: {
171: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.8 root 172: STRGBPalette[STRGBPalEndianTable[i]] = ST2RGB[pHBLPalettes[i]];
1.1.1.4 root 173: #else
1.1.1.8 root 174: STRGBPalette[i] = ST2RGB[pHBLPalettes[i]];
1.1.1.4 root 175: #endif
176: }
1.1 root 177:
1.1.1.2 root 178: /* Ready for first call to 'Spec512_ScanLine' */
1.1 root 179: nScanLine = 0;
1.1.1.5 root 180: if (OverscanMode & OVERSCANMODE_TOP)
181: nScanLine += OVERSCAN_TOP;
1.1 root 182:
1.1.1.2 root 183: /* Skip to first line(where start to draw screen from) */
1.1 root 184: for (i=0; i<(STScreenStartHorizLine+(nStartHBL-OVERSCAN_TOP)); i++)
185: Spec512_ScanWholeLine();
186: }
187:
1.1.1.2 root 188:
189: /*-----------------------------------------------------------------------*/
1.1 root 190: /*
191: Scan whole line and build up palette - need to do this so when get to screen line we have
192: the correct 16 colours set
193: */
194: void Spec512_ScanWholeLine(void)
195: {
1.1.1.2 root 196: /* Store pointer to line of palette cycle writes */
1.1 root 197: pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
1.1.1.2 root 198: /* Ready for next scan line */
1.1 root 199: nScanLine++;
200:
1.1.1.2 root 201: /* Update palette entries until we reach start of displayed screen */
1.1 root 202: ScanLineCycleCount = 0;
1.1.1.2 root 203: Spec512_EndScanLine(); /* Read whole line of palettes and update 'STRGBPalette' */
1.1 root 204: }
205:
1.1.1.2 root 206:
207: /*-----------------------------------------------------------------------*/
1.1 root 208: /*
209: Build up palette for this scan line and store in 'ScanLinePalettes'
210: */
211: void Spec512_StartScanLine(void)
212: {
213: int i;
214:
1.1.1.2 root 215: /* Store pointer to line of palette cycle writes */
1.1 root 216: pCyclePalette = &CyclePalettes[nScanLine*MAX_CYCLEPALETTES_PERLINE];
1.1.1.2 root 217: /* Ready for next scan line */
1.1 root 218: nScanLine++;
219:
1.1.1.2 root 220: /* Update palette entries until we reach start of displayed screen */
1.1 root 221: ScanLineCycleCount = 0;
1.1.1.9 ! root 222: 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.2 root 223: Spec512_UpdatePaletteSpan(); /* Update palette for this 4-cycle period */
224: /* And skip for left border is not using overscan display to user */
225: for (i=0; i<(STScreenLeftSkipBytes/2); i++) /* Eg, 16 bytes = 32 pixels or 8 palette periods */
1.1 root 226: Spec512_UpdatePaletteSpan();
227: }
228:
1.1.1.2 root 229:
230: /*-----------------------------------------------------------------------*/
1.1 root 231: /*
232: Run to end of scan line looking up palettes so 'STRGBPalette' is up-to-date
233: */
234: void Spec512_EndScanLine(void)
235: {
1.1.1.2 root 236: /* Continue to reads palette until complete so have correct version for next line */
1.1.1.9 ! root 237: while (ScanLineCycleCount < nCyclesPerLine)
1.1 root 238: Spec512_UpdatePaletteSpan();
239: }
240:
1.1.1.2 root 241:
242: /*-----------------------------------------------------------------------*/
1.1 root 243: /*
244: Update palette for 4-pixels span, storing to 'STRGBPalette'
245: */
246: void Spec512_UpdatePaletteSpan(void)
247: {
1.1.1.2 root 248: if( pCyclePalette->LineCycles == ScanLineCycleCount )
249: {
250: /* Need to update palette with new entry */
1.1.1.4 root 251: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.8 root 252: STRGBPalette[STRGBPalEndianTable[pCyclePalette->Index]] = ST2RGB[pCyclePalette->Colour];
1.1.1.4 root 253: #else
1.1.1.8 root 254: STRGBPalette[pCyclePalette->Index] = ST2RGB[pCyclePalette->Colour];
1.1.1.4 root 255: #endif
1.1.1.2 root 256: pCyclePalette += 1;
257: }
258: ScanLineCycleCount += 4; /* Next 4 cycles */
1.1 root 259: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.