|
|
1.1 root 1: /*
1.1.1.5 root 2: Hatari - video.c
3:
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: Video hardware handling. This code handling all to do with the video chip.
8: So, we handle VBLs, HBLs, copying the ST screen to a buffer to simulate the
9: TV raster trace, border removal, palette changes per HBL, the 'video address
10: pointer' etc...
1.1 root 11: */
1.1.1.9 ! root 12: char Video_rcsid[] = "Hatari $Id: video.c,v 1.41 2005/09/01 19:20:49 eerot Exp $";
1.1 root 13:
1.1.1.4 root 14: #include <SDL.h>
1.1.1.7 root 15: #include <SDL_endian.h>
1.1.1.4 root 16:
1.1 root 17: #include "main.h"
1.1.1.6 root 18: #include "configuration.h"
1.1 root 19: #include "fdc.h"
20: #include "int.h"
21: #include "ikbd.h"
1.1.1.8 root 22: #include "ioMem.h"
1.1.1.4 root 23: #include "keymap.h"
1.1 root 24: #include "m68000.h"
25: #include "memorySnapShot.h"
26: #include "mfp.h"
27: #include "screen.h"
28: #include "shortcut.h"
29: #include "sound.h"
30: #include "spec512.h"
31: #include "stMemory.h"
32: #include "vdi.h"
33: #include "video.h"
34: #include "ymFormat.h"
1.1.1.4 root 35:
36:
1.1.1.9 ! root 37: #define BORDERMASK_NONE 0x00 /* Borders masks */
! 38: #define BORDERMASK_LEFT 0x01
! 39: #define BORDERMASK_RIGHT 0x02
! 40:
! 41:
1.1.1.2 root 42: BOOL bUseHighRes = FALSE; /* Use hi-res (ie Mono monitor) */
1.1 root 43: int nVBLs, nHBL; /* VBL Counter, HBL line */
1.1.1.8 root 44: int nStartHBL, nEndHBL; /* Start/End HBL for visible screen(64 lines in Top border) */
1.1 root 45: int OverscanMode; /* OVERSCANMODE_xxxx for current display frame */
1.1.1.8 root 46: Uint16 HBLPalettes[(NUM_VISIBLE_LINES+1)*16]; /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
47: Uint16 *pHBLPalettes; /* Pointer to current palette lists, one per HBL */
1.1.1.9 ! root 48: Uint32 HBLPaletteMasks[NUM_VISIBLE_LINES+1]; /* Bit mask of palette colours changes, top bit set is resolution change */
! 49: Uint32 *pHBLPaletteMasks;
1.1.1.6 root 50: int nScreenRefreshRate = 50; /* 50 or 60 Hz in color, 70 Hz in mono */
1.1.1.8 root 51: Uint32 VideoBase; /* Base address in ST Ram for screen (read on each VBL) */
1.1.1.9 ! root 52:
! 53: static Uint8 HWScrollCount; /* HW scroll pixel offset, STe only (0...15) */
! 54: static Uint8 ScanLineSkip; /* Scan line width add, STe only (words, minus 1) */
1.1.1.8 root 55: static Uint8 *pVideoRaster; /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
56: static Uint8 VideoShifterByte; /* VideoShifter (0xff8260) value store in video chip */
57: static int LeftRightBorder; /* BORDERMASK_xxxx used to simulate left/right border removal */
1.1 root 58:
59:
60: /*-----------------------------------------------------------------------*/
61: /*
62: Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
63: */
64: void Video_MemorySnapShot_Capture(BOOL bSave)
65: {
66: /* Save/Restore details */
67: MemorySnapShot_Store(&VideoShifterByte,sizeof(VideoShifterByte));
68: MemorySnapShot_Store(&bUseHighRes,sizeof(bUseHighRes));
69: MemorySnapShot_Store(&nVBLs,sizeof(nVBLs));
70: MemorySnapShot_Store(&nHBL,sizeof(nHBL));
71: MemorySnapShot_Store(&nStartHBL,sizeof(nStartHBL));
72: MemorySnapShot_Store(&nEndHBL,sizeof(nEndHBL));
73: MemorySnapShot_Store(&OverscanMode,sizeof(OverscanMode));
74: MemorySnapShot_Store(HBLPalettes,sizeof(HBLPalettes));
75: MemorySnapShot_Store(HBLPaletteMasks,sizeof(HBLPaletteMasks));
76: MemorySnapShot_Store(&VideoBase,sizeof(VideoBase));
1.1.1.9 ! root 77: MemorySnapShot_Store(&ScanLineSkip,sizeof(ScanLineSkip));
! 78: MemorySnapShot_Store(&HWScrollCount,sizeof(HWScrollCount));
1.1.1.8 root 79: MemorySnapShot_Store(&pVideoRaster,sizeof(pVideoRaster));
1.1 root 80: }
81:
1.1.1.8 root 82:
1.1 root 83: /*-----------------------------------------------------------------------*/
84: /*
1.1.1.8 root 85: Calculate and return video address pointer.
1.1 root 86: */
1.1.1.8 root 87: static Uint32 Video_CalculateAddress(void)
1.1 root 88: {
1.1.1.9 ! root 89: int X, FrameCycles;
! 90: Uint32 VideoAddress; /* Address of video display in ST screen space */
1.1 root 91:
92: /* Find number of cycles passed during frame */
93: FrameCycles = Int_FindFrameCycles();
94:
1.1.1.9 ! root 95: /* Top of screen is usually 64 lines from VBL (64x512=32768 cycles) */
! 96: if (FrameCycles < nStartHBL*CYCLES_PER_LINE)
1.1.1.8 root 97: {
1.1.1.9 ! root 98: VideoAddress = VideoBase;
1.1.1.8 root 99: }
100: else
101: {
1.1.1.9 ! root 102: /* Now find which pixel we are on (ignore left/right borders) */
! 103: /* 96 + 320 + 96 = 512 pixels per scan line (each pixel is one cycle) */
! 104: X = FrameCycles % CYCLES_PER_LINE;
! 105: if (X < SCREEN_START_CYCLE) /* Limit if in NULL area outside screen */
1.1 root 106: X = SCREEN_START_CYCLE;
1.1.1.9 ! root 107: if (X > (CYCLES_PER_LINE - SCREEN_START_CYCLE))
! 108: X = (CYCLES_PER_LINE - SCREEN_START_CYCLE);
! 109: /* X is now from 96 to 416 (320 pixels), subtract 96 to give X pixel across screen! */
1.1 root 110: X = ((X-SCREEN_START_CYCLE)>>1)&(~1);
1.1.1.6 root 111:
1.1.1.9 ! root 112: VideoAddress = pVideoRaster - STRam;
! 113: /* Add line cycles if we have not reached end of screen yet: */
! 114: if (FrameCycles < nEndHBL*CYCLES_PER_LINE)
! 115: VideoAddress += X;
1.1 root 116: }
117:
1.1.1.8 root 118: return VideoAddress;
1.1 root 119: }
120:
1.1.1.7 root 121:
1.1 root 122: /*-----------------------------------------------------------------------*/
123: /*
124: HBL interrupt - this is very inaccurate on ST and appears to occur around 1/3rd way into
125: the display!
126: */
127: void Video_InterruptHandler_HBL(void)
128: {
1.1.1.2 root 129: /* Remove this interrupt from list and re-order */
1.1 root 130: Int_AcknowledgeInterrupt();
1.1.1.7 root 131:
1.1.1.2 root 132: /* Generate new Timer AB, if need to - there are 313 HBLs per frame */
1.1.1.7 root 133: if(nHBL < (SCANLINES_PER_FRAME-1))
1.1 root 134: Int_AddAbsoluteInterrupt(CYCLES_PER_LINE,INTERRUPT_VIDEO_HBL);
135:
1.1.1.7 root 136: M68000_Exception(EXCEPTION_HBLANK); /* Horizontal blank interrupt, level 2! */
1.1 root 137: }
138:
139:
1.1.1.2 root 140: /*-----------------------------------------------------------------------*/
1.1 root 141: /*
1.1.1.7 root 142: Write to VideoShifter (0xff8260), resolution bits
1.1 root 143: */
1.1.1.9 ! root 144: static void Video_WriteToShifter(Uint8 Byte)
1.1 root 145: {
1.1.1.7 root 146: static int nLastHBL = -1, LastByte, nLastCycles;
147: int nFrameCycles, nLineCycles;
148:
149: nFrameCycles = Int_FindFrameCycles();
1.1 root 150:
1.1.1.7 root 151: /* We only care for cycle position in the actual screen line */
152: nLineCycles = nFrameCycles % CYCLES_PER_LINE;
1.1 root 153:
1.1.1.7 root 154: /*fprintf(stderr,"Shifter=0x%2.2X %d (%d) @ %d\n",
155: Byte, nFrameCycles, nLineCycles, nHBL);*/
1.1 root 156:
1.1.1.7 root 157: /* Check if program tries to open left border.
158: * FIXME: This is a very inaccurate test that should be improved,
159: * but we probably need better CPU cycles emulation first. There's
160: * also no support for sync-scrolling yet :-( */
161: if (nHBL == nLastHBL && LastByte == 0x02 && Byte == 0x00
162: && nLineCycles <= 48 && nLineCycles-nLastCycles <= 16)
163: {
164: LeftRightBorder |= BORDERMASK_LEFT;
165: }
1.1.1.2 root 166:
1.1.1.7 root 167: nLastHBL = nHBL;
168: LastByte = Byte;
169: nLastCycles = nLineCycles;
1.1 root 170: }
171:
1.1.1.2 root 172:
173: /*-----------------------------------------------------------------------*/
1.1 root 174: /*
1.1.1.7 root 175: Write to VideoSync (0xff820a), Hz setting
1.1 root 176: */
1.1.1.8 root 177: void Video_Sync_WriteByte(void)
1.1 root 178: {
1.1.1.7 root 179: static int nLastHBL = -1, LastByte, nLastCycles;
180: int nFrameCycles, nLineCycles;
1.1.1.8 root 181: Uint8 Byte;
182:
1.1.1.9 ! root 183: /* Note: We're only interested in lower 2 bits (50/60Hz) */
! 184: Byte = IoMem[0xff820a] & 3;
1.1.1.8 root 185:
1.1.1.7 root 186: nFrameCycles = Int_FindFrameCycles();
1.1 root 187:
1.1.1.7 root 188: /* We only care for cycle position in the actual screen line */
189: nLineCycles = nFrameCycles % CYCLES_PER_LINE;
1.1.1.2 root 190:
1.1.1.7 root 191: /*fprintf(stderr,"Sync=0x%2.2X %d (%d) @ %d\n",
192: Byte, nFrameCycles, nLineCycles, nHBL);*/
1.1 root 193:
1.1.1.7 root 194: /* Check if program tries to open a border.
195: * FIXME: These are very inaccurate tests that should be improved,
196: * but we probably need better CPU cycles emulation first. There's
197: * also no support for sync-scrolling yet :-( */
198: if (LastByte == 0x00 && Byte == 0x02) /* switched from 50 Hz to 60 Hz and back to 50 Hz? */
199: {
200: if (nHBL >= OVERSCAN_TOP && nHBL <= 39 && nStartHBL > FIRST_VISIBLE_HBL)
201: {
202: /* Top border */
203: OverscanMode |= OVERSCANMODE_TOP; /* Set overscan bit */
204: nStartHBL = FIRST_VISIBLE_HBL; /* New start screen line */
205: pHBLPaletteMasks -= OVERSCAN_TOP;
206: pHBLPalettes -= OVERSCAN_TOP;
1.1 root 207: }
1.1.1.7 root 208: else if (nHBL == SCREEN_START_HBL+SCREEN_HEIGHT_HBL)
209: {
210: /* Bottom border */
211: OverscanMode |= OVERSCANMODE_BOTTOM; /* Set overscan bit */
212: nEndHBL = SCREEN_START_HBL+SCREEN_HEIGHT_HBL+OVERSCAN_BOTTOM; /* New end screen line */
1.1 root 213: }
214:
1.1.1.7 root 215: if (nHBL == nLastHBL && nLineCycles >= 400 && nLineCycles <= 480
216: && nLineCycles-nLastCycles <= 16)
217: {
218: /* Right border */
219: LeftRightBorder |= BORDERMASK_RIGHT;
220: }
1.1 root 221: }
222:
1.1.1.7 root 223: nLastHBL = nHBL;
224: LastByte = Byte;
225: nLastCycles = nLineCycles;
1.1 root 226: }
227:
1.1.1.2 root 228:
229: /*-----------------------------------------------------------------------*/
1.1 root 230: /*
231: Reset Sync/Shifter table at start of each HBL
232: */
1.1.1.9 ! root 233: static void Video_StartHBL(void)
1.1 root 234: {
1.1.1.9 ! root 235: LeftRightBorder = BORDERMASK_NONE;
1.1 root 236: }
237:
1.1.1.2 root 238:
239: /*-----------------------------------------------------------------------*/
1.1 root 240: /*
241: Store whole palette on first line so have reference to work from
242: */
1.1.1.7 root 243: static void Video_StoreFirstLinePalette(void)
1.1 root 244: {
1.1.1.8 root 245: Uint16 *pp2;
1.1 root 246: int i;
247:
1.1.1.8 root 248: pp2 = (Uint16 *)&IoMem[0xff8240];
1.1 root 249: for(i=0; i<16; i++)
1.1.1.7 root 250: HBLPalettes[i] = SDL_SwapBE16(*pp2++);
1.1.1.2 root 251: /* And set mask flag with palette and resolution */
1.1.1.8 root 252: HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((unsigned long)IoMem_ReadByte(0xff8260)&0x3)<<16);
1.1 root 253: }
254:
1.1.1.2 root 255:
256: /*-----------------------------------------------------------------------*/
1.1 root 257: /*
258: Store resolution on each line(used to test if mixed low/medium resolutions)
259: */
1.1.1.7 root 260: static void Video_StoreResolution(int y)
1.1 root 261: {
1.1.1.2 root 262: /* Clear resolution, and set with current value */
1.1.1.8 root 263: if (!(bUseHighRes || bUseVDIRes))
264: {
1.1 root 265: HBLPaletteMasks[y] &= ~(0x3<<16);
1.1.1.8 root 266: HBLPaletteMasks[y] |= ((unsigned long)IoMem_ReadByte(0xff8260)&0x3)<<16;
1.1 root 267: }
268: }
269:
1.1.1.2 root 270:
271: /*-----------------------------------------------------------------------*/
1.1 root 272: /*
1.1.1.9 ! root 273: Copy one line of monochrome screen into buffer for conversion later.
1.1 root 274: */
1.1.1.9 ! root 275: static void Video_CopyScreenLineMono(void)
1.1 root 276: {
1.1.1.9 ! root 277: int i;
1.1 root 278:
1.1.1.9 ! root 279: /* Since Hatari does not emulate monochrome HBLs correctly yet
! 280: * (only 200 are raised, just like in low resolution), we have to
! 281: * copy two lines each HBL to finally copy all 400 lines. */
! 282: for (i = 0; i < 2; i++)
! 283: {
! 284: /* Copy one line - 80 bytes in ST high resolution */
! 285: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_MONOLINE);
! 286: pVideoRaster += SCREENBYTES_MONOLINE;
! 287:
! 288: /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
! 289: if (HWScrollCount)
! 290: {
! 291: Uint16 *pScrollAdj;
! 292: int nNegScrollCnt;
! 293:
! 294: pScrollAdj = (Uint16 *)pSTScreen;
! 295: nNegScrollCnt = 16 - HWScrollCount;
! 296:
! 297: /* Shift the whole line by the given scroll count */
! 298: while ((Uint8*)pScrollAdj < pSTScreen + SCREENBYTES_MONOLINE-2)
! 299: {
! 300: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
! 301: | (do_get_mem_word(pScrollAdj+1) >> nNegScrollCnt));
! 302: ++pScrollAdj;
! 303: }
! 304:
! 305: /* Handle the last 16 pixels of the line */
! 306: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
! 307: | (do_get_mem_word(pVideoRaster) >> nNegScrollCnt));
! 308:
! 309: /* HW scrolling advances Shifter video counter by one */
! 310: pVideoRaster += 1 * 2;
! 311: }
! 312:
! 313: /* ScanLineSkip is zero on ST. */
! 314: /* On STE, the Shifter skips the given amount of words. */
! 315: pVideoRaster += ScanLineSkip*2;
! 316:
! 317: /* Each screen line copied to buffer is always same length */
! 318: pSTScreen += SCREENBYTES_MONOLINE;
! 319: }
! 320: }
! 321:
! 322:
! 323: /*-----------------------------------------------------------------------*/
! 324: /*
! 325: Copy one line of color screen into buffer for conversion later.
! 326: Possible lines may be top/bottom border, and/or left/right borders.
! 327: */
! 328: static void Video_CopyScreenLineColor(void)
! 329: {
! 330: /* Is total blank line? I.e. top/bottom border? */
! 331: if (nHBL < nStartHBL || nHBL >= nEndHBL)
! 332: {
! 333: /* Clear line to color '0' */
! 334: memset(pSTScreen, 0, SCREENBYTES_LINE);
! 335: }
! 336: else
! 337: {
! 338: /* Does have left border? If not, clear to color '0' */
! 339: if (LeftRightBorder & BORDERMASK_LEFT)
! 340: {
! 341: pVideoRaster += 24-SCREENBYTES_LEFT;
! 342: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_LEFT);
! 343: pVideoRaster += SCREENBYTES_LEFT;
! 344: }
! 345: else
! 346: memset(pSTScreen,0,SCREENBYTES_LEFT);
! 347:
! 348: /* Copy middle - always present */
! 349: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE);
! 350: pVideoRaster += SCREENBYTES_MIDDLE;
! 351:
! 352: /* Does have right border? If not, clear to color '0' */
! 353: if (LeftRightBorder & BORDERMASK_RIGHT)
! 354: {
! 355: memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE, pVideoRaster, SCREENBYTES_RIGHT);
! 356: pVideoRaster += 46-SCREENBYTES_RIGHT;
! 357: pVideoRaster += SCREENBYTES_RIGHT;
! 358: }
! 359: else
! 360: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
! 361:
! 362: /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
! 363: if (HWScrollCount)
! 364: {
! 365: Uint16 *pScrollAdj; /* Pointer to actual position in line */
! 366: int nNegScrollCnt;
! 367: Uint16 *pScrollEndAddr; /* Pointer to end of the line */
! 368:
! 369: nNegScrollCnt = 16 - HWScrollCount;
! 370: if (LeftRightBorder & BORDERMASK_LEFT)
! 371: pScrollAdj = (Uint16 *)pSTScreen;
! 372: else
! 373: pScrollAdj = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT);
! 374: if (LeftRightBorder & BORDERMASK_RIGHT)
! 375: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LINE - 8);
! 376: else
! 377: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8);
! 378:
! 379: if (STRes == ST_MEDIUM_RES)
! 380: {
! 381: /* TODO: Implement fine scrolling for medium resolution, too */
! 382: /* HW scrolling advances Shifter video counter by one */
! 383: pVideoRaster += 2 * 2;
1.1 root 384: }
1.1.1.9 ! root 385: else
! 386: {
! 387: /* Shift the whole line by the given scroll count */
! 388: while (pScrollAdj < pScrollEndAddr)
! 389: {
! 390: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
! 391: | (do_get_mem_word(pScrollAdj+4) >> nNegScrollCnt));
! 392: ++pScrollAdj;
1.1 root 393: }
1.1.1.9 ! root 394: /* Handle the last 16 pixels of the line */
! 395: if (LeftRightBorder & BORDERMASK_RIGHT)
! 396: {
! 397: /* When right border is open, we have to deal with this ugly offset
! 398: * of 46-SCREENBYTES_RIGHT=30 - The demo "Mind rewind" is a good example */
! 399: do_put_mem_word(pScrollAdj+0, (do_get_mem_word(pScrollAdj+0) << HWScrollCount)
! 400: | (do_get_mem_word(pVideoRaster-30) >> nNegScrollCnt));
! 401: do_put_mem_word(pScrollAdj+1, (do_get_mem_word(pScrollAdj+1) << HWScrollCount)
! 402: | (do_get_mem_word(pVideoRaster-28) >> nNegScrollCnt));
! 403: do_put_mem_word(pScrollAdj+2, (do_get_mem_word(pScrollAdj+2) << HWScrollCount)
! 404: | (do_get_mem_word(pVideoRaster-26) >> nNegScrollCnt));
! 405: do_put_mem_word(pScrollAdj+3, (do_get_mem_word(pScrollAdj+3) << HWScrollCount)
! 406: | (do_get_mem_word(pVideoRaster-24) >> nNegScrollCnt));
1.1 root 407: }
408: else
1.1.1.9 ! root 409: {
! 410: do_put_mem_word(pScrollAdj+0, (do_get_mem_word(pScrollAdj+0) << HWScrollCount)
! 411: | (do_get_mem_word(pVideoRaster+0) >> nNegScrollCnt));
! 412: do_put_mem_word(pScrollAdj+1, (do_get_mem_word(pScrollAdj+1) << HWScrollCount)
! 413: | (do_get_mem_word(pVideoRaster+2) >> nNegScrollCnt));
! 414: do_put_mem_word(pScrollAdj+2, (do_get_mem_word(pScrollAdj+2) << HWScrollCount)
! 415: | (do_get_mem_word(pVideoRaster+4) >> nNegScrollCnt));
! 416: do_put_mem_word(pScrollAdj+3, (do_get_mem_word(pScrollAdj+3) << HWScrollCount)
! 417: | (do_get_mem_word(pVideoRaster+6) >> nNegScrollCnt));
! 418: }
! 419: /* HW scrolling advances Shifter video counter by one */
! 420: pVideoRaster += 4 * 2;
1.1 root 421: }
1.1.1.9 ! root 422: }
! 423:
! 424: /* ScanLineSkip is zero on ST. */
! 425: /* On STE, the Shifter skips the given amount of words. */
! 426: pVideoRaster += ScanLineSkip*2;
! 427: }
! 428:
! 429: /* Each screen line copied to buffer is always same length */
! 430: pSTScreen += SCREENBYTES_LINE;
! 431: }
! 432:
1.1.1.6 root 433:
1.1.1.9 ! root 434: /*-----------------------------------------------------------------------*/
! 435: /*
! 436: Copy line of screen into buffer for conversion later.
! 437: */
! 438: static void Video_CopyScreenLine(void)
! 439: {
! 440: /* Read STE fine scrolling registers */
! 441: if (ConfigureParams.System.nMachineType != MACHINE_ST)
! 442: {
! 443: ScanLineSkip = STMemory_ReadByte(0xff820f);
! 444: HWScrollCount = STMemory_ReadByte(0xff8265) & 0x0f;
! 445: }
! 446:
! 447: /* Only copy screen line if not doing high VDI resolution */
! 448: if (!bUseVDIRes)
! 449: {
! 450: if (bUseHighRes)
! 451: {
! 452: /* Copy for hi-res (no overscan) */
! 453: Video_CopyScreenLineMono();
! 454: }
! 455: else
! 456: {
! 457: /* Copy for low and medium resolution */
! 458: Video_CopyScreenLineColor();
1.1 root 459: }
460: }
461: }
462:
1.1.1.2 root 463:
464: /*-----------------------------------------------------------------------*/
1.1 root 465: /*
466: Copy extended GEM resolution screen
467: */
1.1.1.9 ! root 468: static void Video_CopyVDIScreen(void)
1.1 root 469: {
1.1.1.2 root 470: /* Copy whole screen, don't care about being exact as for GEM only */
1.1.1.8 root 471: memcpy(pSTScreen, pVideoRaster, ((VDIWidth*VDIPlanes)/8)*VDIHeight); /* 640x400x16colour */
1.1 root 472: }
473:
1.1.1.2 root 474:
475: /*-----------------------------------------------------------------------*/
1.1 root 476: /*
477: Check at end of each HBL to see if any Sync/Shifter hardware tricks have been attempted
478: */
1.1.1.9 ! root 479: static void Video_EndHBL(void)
1.1 root 480: {
1.1.1.2 root 481: /* Are we in possible visible display (including borders)? */
1.1.1.9 ! root 482: if (nHBL >= FIRST_VISIBLE_HBL && nHBL < FIRST_VISIBLE_HBL+NUM_VISIBLE_LINES)
! 483: {
! 484: /* Copy line of screen to buffer to simulate TV raster trace
! 485: * - required for mouse cursor display/game updates
! 486: * Eg, Lemmings and The Killing Game Show are good examples */
! 487: Video_CopyScreenLine();
1.1 root 488:
1.1.1.9 ! root 489: if (nHBL == FIRST_VISIBLE_HBL) /* Very first line on screen - HBLPalettes[0] */
! 490: {
1.1.1.2 root 491: /* Store ALL palette for this line into raster table for datum */
1.1 root 492: Video_StoreFirstLinePalette();
493: }
1.1.1.2 root 494: /* Store resolution for every line so can check for mix low/medium screens */
1.1 root 495: Video_StoreResolution(nHBL-FIRST_VISIBLE_HBL);
496: }
497:
1.1.1.2 root 498: /* Finally increase HBL count */
1.1 root 499: nHBL++;
500:
1.1.1.2 root 501: Video_StartHBL(); /* Setup next one */
1.1 root 502: }
503:
1.1.1.2 root 504:
1.1.1.9 ! root 505: /*-----------------------------------------------------------------------*/
! 506: /*
! 507: End Of Line interrupt
! 508: As this occurs at the end of a line we cannot get timing for START of first
! 509: line (as in Spectrum 512)
! 510: */
! 511: void Video_InterruptHandler_EndLine(void)
! 512: {
! 513: /* Remove this interrupt from list and re-order */
! 514: Int_AcknowledgeInterrupt();
! 515: /* Generate new HBL, if need to - there are 313 HBLs per frame */
! 516: if (nHBL < SCANLINES_PER_FRAME-1)
! 517: Int_AddAbsoluteInterrupt(CYCLES_PER_LINE,INTERRUPT_VIDEO_ENDLINE);
! 518:
! 519: /* Is this a good place to send the keyboard packets? Done once per frame */
! 520: if (nHBL == nStartHBL)
! 521: {
! 522: /* On each VBL send automatic keyboard packets for mouse, joysticks etc... */
! 523: IKBD_SendAutoKeyboardCommands();
! 524: }
! 525:
! 526: /* Timer A/B occur at END of first visible screen line in Event Count mode */
! 527: if (nHBL >= nStartHBL && nHBL < nEndHBL)
! 528: {
! 529: /* Handle Timers A and B when using Event Count mode(timer taken from HBL) */
! 530: // FIXME: Really raise Timer A here?
! 531: // if (MFP_TACR==0x08) /* Is timer in Event Count mode? */
! 532: // MFP_TimerA_EventCount_Interrupt();
! 533: if (MFP_TBCR==0x08) /* Is timer in Event Count mode? */
! 534: MFP_TimerB_EventCount_Interrupt();
! 535: }
! 536:
! 537: FDC_UpdateHBL(); /* Update FDC motion */
! 538: Video_EndHBL(); /* Increase HBL count, copy line to display buffer and do any video trickery */
! 539:
! 540: /* If we don't often pump data into the event queue, the SDL misses events... grr... */
! 541: if (!(nHBL & 63))
! 542: {
! 543: Main_EventHandler();
! 544: }
! 545: }
! 546:
! 547:
! 548: /*-----------------------------------------------------------------------*/
! 549: /*
! 550: Clear raster line table to store changes in palette/resolution on a line
! 551: basic. Called once on VBL interrupt.
! 552: */
1.1 root 553: void Video_SetScreenRasters(void)
554: {
555: pHBLPaletteMasks = HBLPaletteMasks;
556: pHBLPalettes = HBLPalettes;
1.1.1.9 ! root 557: memset(pHBLPaletteMasks, 0, sizeof(Uint32)*NUM_VISIBLE_LINES); /* Clear array */
1.1 root 558: }
559:
1.1.1.2 root 560:
561: /*-----------------------------------------------------------------------*/
1.1 root 562: /*
563: Set pointers to HBLPalette tables to store correct colours/resolutions
564: */
1.1.1.9 ! root 565: static void Video_SetHBLPaletteMaskPointers(void)
1.1 root 566: {
567: int FrameCycles;
568: int Line;
569:
570: /* Top of standard screen is 64 lines from VBL(64x512=32768 cycles) */
571: /* Each line is 64+320+64+64(Blank) = 512 pixels per scan line */
572: /* Timer occurs at end of 64+320+64; Display Enable(DE)=Low */
573: /* HBL is incorrect on machine and occurs around 96+ cycles in */
574:
575: /* Top of standard screen is 64 lines from VBL(64x512=32768 cycles) */
576: /* Each line is 96 + 320 + 96 = 512 pixels per scan line(each pixel is one cycle) */
577: FrameCycles = Int_FindFrameCycles();
578:
579: /* Find 'line' into palette - screen starts 64 lines down, less 28 for top overscan */
580: /* And if write to last 96 cycle of line it will count as the NEXT line(needed else games may flicker) */
581: Line = (FrameCycles-(FIRST_VISIBLE_HBL*CYCLES_PER_LINE)+SCREEN_START_CYCLE)/CYCLES_PER_LINE;
582: if (Line<0) /* Limit to top/bottom of possible visible screen */
583: Line = 0;
584: if (Line>=NUM_VISIBLE_LINES)
585: Line = NUM_VISIBLE_LINES-1;
586:
1.1.1.2 root 587: /* Store pointers */
1.1 root 588: pHBLPaletteMasks = &HBLPaletteMasks[Line]; /* Next mask entry */
589: pHBLPalettes = &HBLPalettes[16*Line]; /* Next colour raster list x16 colours */
590: }
1.1.1.8 root 591:
592:
1.1.1.9 ! root 593: /*-----------------------------------------------------------------------*/
! 594: /*
! 595: Called on VBL, set registers ready for frame
! 596: */
! 597: static void Video_ClearOnVBL(void)
! 598: {
! 599: /* New screen, so first HBL */
! 600: nHBL = 0;
! 601: nStartHBL = SCREEN_START_HBL;
! 602: nEndHBL = SCREEN_START_HBL+SCREEN_HEIGHT_HBL;
! 603: OverscanMode = OVERSCANMODE_NONE;
! 604:
! 605: /* Get screen address pointer, aligned to 256 bytes on ST (ie ignore lowest byte) */
! 606: VideoBase = (Uint32)STMemory_ReadByte(0xff8201)<<16 | (Uint32)STMemory_ReadByte(0xff8203)<<8;
! 607: if (ConfigureParams.System.nMachineType != MACHINE_ST)
! 608: {
! 609: /* on STe 2 aligned, on Falcon 4 aligned, on TT 8 aligned. We do STe. */
! 610: VideoBase |= STMemory_ReadByte(0xff820d) & ~1;
! 611: }
! 612: pVideoRaster = &STRam[VideoBase];
! 613: pSTScreen = pFrameBuffer->pSTScreen;
! 614:
! 615: Video_StartHBL();
! 616: Video_SetScreenRasters();
! 617: Spec512_StartVBL();
! 618: }
! 619:
! 620:
! 621: /*-----------------------------------------------------------------------*/
! 622: /*
! 623: VBL interrupt, draw screen and reset counters
! 624: */
! 625: void Video_InterruptHandler_VBL(void)
! 626: {
! 627: int PendingCyclesOver;
! 628: int nNewMilliTicks;
! 629: static int nOldMilliTicks = 0;
! 630: signed int nDelay;
! 631:
! 632: /* Store cycles we went over for this frame(this is our inital count) */
! 633: PendingCyclesOver = -PendingInterruptCount; /* +ve */
! 634:
! 635: /* Remove this interrupt from list and re-order */
! 636: Int_AcknowledgeInterrupt();
! 637: /* Start HBL interrupts - MUST do before add in cycles */
! 638: Int_AddAbsoluteInterrupt(CYCLES_ENDLINE,INTERRUPT_VIDEO_ENDLINE);
! 639: Int_AddAbsoluteInterrupt(CYCLES_HBL,INTERRUPT_VIDEO_HBL);
! 640: Int_AddAbsoluteInterrupt(CYCLES_PER_FRAME,INTERRUPT_VIDEO_VBL);
! 641:
! 642: /* Set frame cycles, used for Video Address */
! 643: nFrameCyclesOver = PendingCyclesOver; /* Number of cycles into frame */
! 644:
! 645: /* Set the screen refresh rate */
! 646: #if 0
! 647: if(bUseHighRes)
! 648: nScreenRefreshRate = 70;
! 649: else if(IoMem[0xff820a] & 2) /* Is it 50Hz or is it 60Hz? */
! 650: nScreenRefreshRate = 50;
! 651: else
! 652: nScreenRefreshRate = 60;
! 653: #endif
! 654:
! 655: /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
! 656: Keymap_DebounceAllKeys();
! 657: /* Check shortcut keys */
! 658: ShortCut_CheckKeys();
! 659:
! 660: /* Draw screen, skip frame if need to */
! 661: if (!ConfigureParams.Screen.bFrameSkip || (nVBLs&1))
! 662: {
! 663: /* Use extended VDI resolution?
! 664: * If so, just copy whole screen on VBL rather than per HBL */
! 665: if (bUseVDIRes)
! 666: Video_CopyVDIScreen();
! 667:
! 668: /* Now draw the screen! */
! 669: Screen_Draw();
! 670: }
! 671:
! 672: /* Update counter for number of screen refreshes per second(for debugging) */
! 673: nVBLs++;
! 674: /* Set video registers for frame */
! 675: Video_ClearOnVBL();
! 676: /* Store off PSG registers for YM file, is enabled */
! 677: YMFormat_UpdateRecording();
! 678: /* Generate 1/50th second of sound sample data, to be played by sound thread */
! 679: Sound_Update_VBL();
! 680:
! 681: M68000_Exception(EXCEPTION_VBLANK); /* Vertical blank interrupt, level 4! */
! 682:
! 683: /* And handle any messages, check for quit message */
! 684: Main_EventHandler(); /* Process messages, set 'bQuitProgram' if user tries to quit */
! 685: if (bQuitProgram)
! 686: {
! 687: Int_AddAbsoluteInterrupt(4, 0L); /* Pass NULL interrupt function to quit cleanly */
! 688: set_special(SPCFLAG_BRK); /* Assure that CPU core shuts down */
! 689: }
! 690:
! 691: if (ConfigureParams.System.nMinMaxSpeed != MINMAXSPEED_MAX)
! 692: {
! 693: /* Wait, so we stay in sync with the sound */
! 694: do
! 695: {
! 696: nNewMilliTicks = SDL_GetTicks();
! 697: nDelay = 1000/nScreenRefreshRate - (nNewMilliTicks-nOldMilliTicks);
! 698: if(nDelay > 2)
! 699: {
! 700: /* SDL_Delay seems to be quite inaccurate, so we don't wait the whole time */
! 701: SDL_Delay(nDelay - 1);
! 702: }
! 703: }
! 704: while(nDelay > 0);
! 705: nOldMilliTicks = nNewMilliTicks;
! 706: }
! 707: }
! 708:
! 709:
! 710: /*-----------------------------------------------------------------------*/
! 711: /*
! 712: Reset video chip
! 713: */
! 714: void Video_Reset(void)
! 715: {
! 716: /* NOTE! Must reset all of these register type things here!!!! */
! 717:
! 718: /* Are we in high-res? */
! 719: if (bUseHighRes)
! 720: VideoShifterByte = ST_HIGH_RES; /* Boot up for mono monitor */
! 721: else
! 722: VideoShifterByte = ST_LOW_RES;
! 723: if(bUseVDIRes)
! 724: VideoShifterByte = VDIRes;
! 725:
! 726: /* Reset VBL counter */
! 727: nVBLs = 0;
! 728: /* Reset addresses */
! 729: VideoBase = 0L;
! 730: /* Reset STe screen variables */
! 731: ScanLineSkip = 0;
! 732: HWScrollCount = 0;
! 733: /* Clear ready for new VBL */
! 734: Video_ClearOnVBL();
! 735: }
! 736:
! 737:
! 738: /*-----------------------------------------------------------------------*/
! 739: /*
! 740: Write to video address base high and med register (0xff8201 and 0xff8203).
! 741: When a program writes to these registers, some other video registers
! 742: are reset to zero.
! 743: */
! 744: void Video_ScreenBaseSTE_WriteByte(void)
! 745: {
! 746: IoMem[0xff820d] = 0; /* Reset screen base low register */
! 747: }
1.1.1.8 root 748:
749: /*-----------------------------------------------------------------------*/
750: /*
751: Read video address counter high byte (0xff8205)
752: */
753: void Video_ScreenCounterHigh_ReadByte(void)
754: {
755: IoMem[0xff8205] = Video_CalculateAddress() >> 16; /* Get video address counter high byte */
756: }
757:
758: /*-----------------------------------------------------------------------*/
759: /*
760: Read video address counter med byte (0xff8207)
761: */
762: void Video_ScreenCounterMed_ReadByte(void)
763: {
764: IoMem[0xff8207] = Video_CalculateAddress() >> 8; /* Get video address counter med byte */
765: }
766:
767: /*-----------------------------------------------------------------------*/
768: /*
769: Read video address counter low byte (0xff8209)
770: */
771: void Video_ScreenCounterLow_ReadByte(void)
772: {
773: IoMem[0xff8209] = Video_CalculateAddress(); /* Get video address counter low byte */
774: }
775:
1.1.1.9 ! root 776: /*-----------------------------------------------------------------------*/
! 777: /*
! 778: Write to video address counter (0xff8205, 0xff8207 and 0xff8209).
! 779: Called on STE only and like with base address, you cannot set lowest bit.
! 780: */
! 781: void Video_ScreenCounter_WriteByte(void)
! 782: {
! 783: Uint32 addr;
! 784: addr = (IoMem[0xff8205] << 16) | (IoMem[0xff8207] << 8) | IoMem[0xff8209];
! 785: pVideoRaster = &STRam[addr & ~1];
! 786: }
1.1.1.8 root 787:
788: /*-----------------------------------------------------------------------*/
789: /*
790: Read video sync register (0xff820a)
791: */
792: void Video_Sync_ReadByte(void)
793: {
794: /* Nothing... */
795: }
796:
797: /*-----------------------------------------------------------------------*/
798: /*
799: Read video base address low byte (0xff820d). A plain ST can only store
800: screen addresses rounded to 256 bytes (i.e. no lower byte).
801: */
802: void Video_BaseLow_ReadByte(void)
803: {
804: if (ConfigureParams.System.nMachineType == MACHINE_ST)
1.1.1.9 ! root 805: IoMem[0xff820d] = 0; /* On ST this is always 0 */
! 806:
! 807: /* Note that you should not do anything here for STe because
! 808: * VideoBase address is set in an interrupt and would be wrong
! 809: * here. It's fine like this.
! 810: */
1.1.1.8 root 811: }
812:
813: /*-----------------------------------------------------------------------*/
814: /*
815: Read video line width register (0xff820f)
816: */
817: void Video_LineWidth_ReadByte(void)
818: {
1.1.1.9 ! root 819: if (ConfigureParams.System.nMachineType == MACHINE_ST)
! 820: IoMem[0xff820f] = 0; /* On ST this is always 0 */
1.1.1.8 root 821: }
822:
823: /*-----------------------------------------------------------------------*/
824: /*
825: Read video shifter mode register (0xff8260)
826: */
827: void Video_ShifterMode_ReadByte(void)
828: {
829: if (bUseHighRes)
830: IoMem[0xff8260] = 2; /* If mono monitor, force to high resolution */
831: else
832: IoMem[0xff8260] = VideoShifterByte; /* Read shifter register */
833: }
834:
835:
836:
837: /*-----------------------------------------------------------------------*/
838: /*
839: Write to video shifter palette registers (0xff8240-0xff825e)
840: */
841: static void Video_ColorReg_WriteWord(Uint32 addr)
842: {
1.1.1.9 ! root 843: if (!bUseHighRes) /* Don't store if hi-res */
1.1.1.8 root 844: {
1.1.1.9 ! root 845: int idx;
1.1.1.8 root 846: Uint16 col;
1.1.1.9 ! root 847: Video_SetHBLPaletteMaskPointers(); /* Set 'pHBLPalettes' etc.. according cycles into frame */
1.1.1.8 root 848: col = IoMem_ReadWord(addr);
1.1.1.9 ! root 849: if (ConfigureParams.System.nMachineType == MACHINE_ST)
! 850: col &= 0x777; /* Mask off to ST 512 palette */
! 851: else
! 852: col &= 0xfff; /* Mask off to STe 4096 palette */
! 853: IoMem_WriteWord(addr, col); /* (some games write 0xFFFF and read back to see if STe) */
! 854: Spec512_StoreCyclePalette(col, addr); /* Store colour into CyclePalettes[] */
! 855: idx = (addr-0xff8240)/2; /* words */
! 856: pHBLPalettes[idx] = col; /* Set colour x */
! 857: *pHBLPaletteMasks |= 1 << idx; /* And mask */
1.1.1.8 root 858: }
859: }
860:
861: void Video_Color0_WriteWord(void)
862: {
863: Video_ColorReg_WriteWord(0xff8240);
864: }
865:
866: void Video_Color1_WriteWord(void)
867: {
868: Video_ColorReg_WriteWord(0xff8242);
869: }
870:
871: void Video_Color2_WriteWord(void)
872: {
873: Video_ColorReg_WriteWord(0xff8244);
874: }
875:
876: void Video_Color3_WriteWord(void)
877: {
878: Video_ColorReg_WriteWord(0xff8246);
879: }
880:
881: void Video_Color4_WriteWord(void)
882: {
883: Video_ColorReg_WriteWord(0xff8248);
884: }
885:
886: void Video_Color5_WriteWord(void)
887: {
888: Video_ColorReg_WriteWord(0xff824a);
889: }
890:
891: void Video_Color6_WriteWord(void)
892: {
893: Video_ColorReg_WriteWord(0xff824c);
894: }
895:
896: void Video_Color7_WriteWord(void)
897: {
898: Video_ColorReg_WriteWord(0xff824e);
899: }
900:
901: void Video_Color8_WriteWord(void)
902: {
903: Video_ColorReg_WriteWord(0xff8250);
904: }
905:
906: void Video_Color9_WriteWord(void)
907: {
908: Video_ColorReg_WriteWord(0xff8252);
909: }
910:
911: void Video_Color10_WriteWord(void)
912: {
913: Video_ColorReg_WriteWord(0xff8254);
914: }
915:
916: void Video_Color11_WriteWord(void)
917: {
918: Video_ColorReg_WriteWord(0xff8256);
919: }
920:
921: void Video_Color12_WriteWord(void)
922: {
923: Video_ColorReg_WriteWord(0xff8258);
924: }
925:
926: void Video_Color13_WriteWord(void)
927: {
928: Video_ColorReg_WriteWord(0xff825a);
929: }
930:
931: void Video_Color14_WriteWord(void)
932: {
933: Video_ColorReg_WriteWord(0xff825c);
934: }
935:
936: void Video_Color15_WriteWord(void)
937: {
938: Video_ColorReg_WriteWord(0xff825e);
939: }
940:
941:
942: /*-----------------------------------------------------------------------*/
943: /*
944: Write video shifter mode register (0xff860)
945: */
946: void Video_ShifterMode_WriteByte(void)
947: {
948: if (!bUseHighRes && !bUseVDIRes) /* Don't store if hi-res and don't store if VDI resolution */
949: {
950: VideoShifterByte = IoMem[0xff8260] & 3; /* We only care for lower 2-bits */
951: Video_WriteToShifter(VideoShifterByte);
952: Video_SetHBLPaletteMaskPointers();
953: *pHBLPaletteMasks &= 0xff00ffff;
954: /* Store resolution after palette mask and set resolution write bit: */
955: *pHBLPaletteMasks |= (((unsigned long)VideoShifterByte|0x04)<<16);
956: }
957: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.