|
|
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:
7: Video hardware handling. This code handling all to do with the video chip. So, we handle
8: VBLs, HBLs, copying the ST screen to a buffer to simulate the TV raster trace, border
9: removal, palette changes per HBL, the 'video address pointer' etc...
10: */
1.1.1.6 ! root 11: static char rcsid[] = "Hatari $Id: video.c,v 1.16 2003/06/28 14:32:36 thothy Exp $";
1.1 root 12:
1.1.1.4 root 13: #include <SDL.h>
14:
1.1 root 15: #include "main.h"
16: #include "debug.h"
17: #include "decode.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.4 root 22: #include "keymap.h"
1.1 root 23: #include "m68000.h"
24: #include "memAlloc.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:
1.1 root 36: #include "syncTables.h" /* Clock-cycle tables */
37:
1.1.1.4 root 38:
1.1 root 39: long VideoAddress; /* Address of video display in ST screen space */
40: unsigned char VideoSyncByte,VideoShifterByte; /* VideoSync (0xff820a) and VideoShifter(0xff8260) values store in video chip */
1.1.1.2 root 41: BOOL bUseHighRes = FALSE; /* Use hi-res (ie Mono monitor) */
1.1 root 42: int nVBLs, nHBL; /* VBL Counter, HBL line */
43: int nStartHBL,nEndHBL; /* Start/End HBL for visible screen(64 lines in Top border) */
44: int OverscanMode; /* OVERSCANMODE_xxxx for current display frame */
45: unsigned short int HBLPalettes[(NUM_VISIBLE_LINES+1)*16]; /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
46: unsigned long HBLPaletteMasks[NUM_VISIBLE_LINES+1]; /* Bit mask of palette colours changes, top bit set is resolution change */
47: unsigned short int *pHBLPalettes; /* Pointer to current palette lists, one per HBL */
48: unsigned long *pHBLPaletteMasks;
49: unsigned long VideoBase; /* Base address in ST Ram for screen(read on each VBL) */
50: unsigned long VideoRaster; /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
51: int SyncHandler_Value; /* Value to pass to 'Video_SyncHandler_xxxx' functions */
52: int LeftRightBorder; /* BORDERMASK_xxxx used to simulate left/right border removal */
1.1.1.6 ! root 53: int VBLCounter; /* VBL counter */
! 54: int nScreenRefreshRate = 50; /* 50 or 60 Hz in color, 70 Hz in mono */
1.1 root 55:
56:
57: /*-----------------------------------------------------------------------*/
58: /*
59: Reset video chip
60: */
61: void Video_Reset(void)
62: {
63: /* NOTE! Must reset all of these register type things here!!!! */
64: VideoSyncByte = 0;
65: /* Are we in high-res? */
66: if (bUseHighRes)
67: VideoShifterByte = ST_HIGH_RES; /* Boot up for mono monitor */
68: else
69: VideoShifterByte = ST_LOW_RES;
1.1.1.4 root 70: if(bUseVDIRes)
71: VideoShifterByte = VDIRes;
1.1 root 72:
73: /* Reset VBL counter */
74: nVBLs = 0;
75: /* Reset addresses */
76: VideoBase = 0L;
77: /* Clear ready for new VBL */
78: Video_ClearOnVBL();
79: }
80:
81: /*-----------------------------------------------------------------------*/
82: /*
83: Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
84: */
85: void Video_MemorySnapShot_Capture(BOOL bSave)
86: {
87: /* Save/Restore details */
88: MemorySnapShot_Store(&VideoAddress,sizeof(VideoAddress));
89: MemorySnapShot_Store(&VideoSyncByte,sizeof(VideoSyncByte));
90: MemorySnapShot_Store(&VideoShifterByte,sizeof(VideoShifterByte));
91: MemorySnapShot_Store(&bUseHighRes,sizeof(bUseHighRes));
92: MemorySnapShot_Store(&nVBLs,sizeof(nVBLs));
93: MemorySnapShot_Store(&nHBL,sizeof(nHBL));
94: MemorySnapShot_Store(&nStartHBL,sizeof(nStartHBL));
95: MemorySnapShot_Store(&nEndHBL,sizeof(nEndHBL));
96: MemorySnapShot_Store(&OverscanMode,sizeof(OverscanMode));
97: MemorySnapShot_Store(HBLPalettes,sizeof(HBLPalettes));
98: MemorySnapShot_Store(HBLPaletteMasks,sizeof(HBLPaletteMasks));
99: MemorySnapShot_Store(&VideoBase,sizeof(VideoBase));
100: MemorySnapShot_Store(&VideoRaster,sizeof(VideoRaster));
101: }
102:
103: /*-----------------------------------------------------------------------*/
104: /*
105: Called on VBL, set registers ready for frame
106: */
107: void Video_ClearOnVBL(void)
108: {
109: /* New screen, so first HBL */
110: nHBL = 0;
111: nStartHBL = SCREEN_START_HBL;
112: nEndHBL = SCREEN_START_HBL+SCREEN_HEIGHT_HBL;
113: OverscanMode = OVERSCANMODE_NONE;
114:
115: /* Get screen address pointer, align to 256 bytes(ie ignore lowest byte) */
116: VideoBase = (unsigned long)STMemory_ReadByte(0xff8201)<<16 | (unsigned long)STMemory_ReadByte(0xff8203)<<8;
117: VideoRaster = (unsigned long)STRam+VideoBase;
118: pSTScreen = pFrameBuffer->pSTScreen;
119:
120: Video_StartHBL();
121: Video_SetScreenRasters();
122: Spec512_StartVBL();
123: }
124:
125: /*-----------------------------------------------------------------------*/
126: /*
127: Calculate Video address pointer and store in 'VideoAddress'
128: */
129: void Video_CalculateAddress(void)
130: {
131: int X,Y,FrameCycles,nPixelsIn;
132:
133: /* Find number of cycles passed during frame */
134: FrameCycles = Int_FindFrameCycles();
135:
136: /* Top of screen is usually 64 lines from VBL(64x512=32768 cycles) */
137: if (FrameCycles<(nStartHBL*CYCLES_PER_LINE))
138: VideoAddress = 0;
139: else {
140: /* Now find which pixel we are on(ignore left/right borders) */
141: /* 96 + 320 + 96 = 512 pixels per scan line(each pixel is one cycle) */
142: nPixelsIn = FrameCycles-(nStartHBL*CYCLES_PER_LINE);
143: /* Convert this to an X,Y pixel on screen */
144: Y = nPixelsIn/512;
145: X = nPixelsIn - (Y*512);
146: if (X<SCREEN_START_CYCLE) /* Limit if in NULL area outside screen */
147: X = SCREEN_START_CYCLE;
148: if (X>(512-SCREEN_START_CYCLE))
149: X = (512-SCREEN_START_CYCLE);
150: /* X is now from 96 to 416(320 pixels), subtract 96 to give X pixel across screen! */
151: X = ((X-SCREEN_START_CYCLE)>>1)&(~1);
1.1.1.6 ! root 152:
1.1 root 153: if (Y<(nEndHBL-nStartHBL)) /* Limit to end of screen */
154: VideoAddress = (Y*160) + X;
155: else
156: VideoAddress = ((nEndHBL-nStartHBL)*160);
157: }
158:
159: /* Offset from start of screen(MUST use address loading into video display) */
160: VideoAddress += VideoBase;
161: }
162:
163: /*-----------------------------------------------------------------------*/
164: /*
165: Read Video address pointer(from current cycle count), and return 24-bit address in 'ebx'
166: */
167: unsigned long Video_ReadAddress(void)
168: {
169: Video_CalculateAddress(); /* Find address from current cycle count into display frame */
170: return( VideoAddress );
171: }
172:
173: /*-----------------------------------------------------------------------*/
174: /*
175: VBL interrupt, draw screen and reset counters
176: */
177: void Video_InterruptHandler_VBL(void)
178: {
179: int PendingCyclesOver;
1.1.1.6 ! root 180: int nNewMilliTicks;
! 181: static int nOldMilliTicks = 0;
! 182: signed int nDelay;
1.1 root 183:
184: /* Store cycles we went over for this frame(this is our inital count) */
185: PendingCyclesOver = -PendingInterruptCount; /* +ve */
186:
187: /* Remove this interrupt from list and re-order */
188: Int_AcknowledgeInterrupt();
1.1.1.6 ! root 189: /* Start HBL interrupts - MUST do before add in cycles */
1.1 root 190: Int_AddAbsoluteInterrupt(CYCLES_ENDLINE,INTERRUPT_VIDEO_ENDLINE);
191: Int_AddAbsoluteInterrupt(CYCLES_HBL,INTERRUPT_VIDEO_HBL);
192: Int_AddAbsoluteInterrupt(CYCLES_PER_FRAME,INTERRUPT_VIDEO_VBL);
193:
194: /* Set frame cycles, used for Video Address */
195: nFrameCyclesOver = PendingCyclesOver; /* Number of cycles into frame */
196:
1.1.1.6 ! root 197: /* Set the screen refresh rate */
! 198: #if 0
! 199: if(bUseHighRes)
! 200: nScreenRefreshRate = 70;
! 201: else if(STRam[0xff820a] & 2) /* Is it 50Hz or is it 60Hz? */
! 202: nScreenRefreshRate = 50;
! 203: else
! 204: nScreenRefreshRate = 60;
! 205: #endif
! 206:
! 207: VBLCounter += 1;
1.1.1.2 root 208:
1.1.1.3 root 209: /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
210: Keymap_DebounceAllKeys();
1.1 root 211: /* Check 'Function' keys, so if press F12 we update screen correctly to Window! */
212: ShortCut_CheckKeys();
213: /* See if need to save/restore emulation state during this 'safe-zone' */
214: MemorySnapShot_CheckSaveRestore();
215: /* Use extended VDI resolution? If so, just copy whole screen on VBL rather than per HBL */
216: if (bUseVDIRes)
217: Video_CopyVDIScreen();
1.1.1.5 root 218:
1.1 root 219: /* Draw screen, skip frame if need to */
1.1.1.6 ! root 220: if (ConfigureParams.Screen.bFrameSkip)
1.1.1.5 root 221: {
1.1 root 222: if (nVBLs&1)
223: Screen_Draw();
224: }
225: else
226: Screen_Draw();
1.1.1.5 root 227:
1.1 root 228: /* Update counter for number of screen refreshes per second(for debugging) */
229: nVBLs++;
230: /* Set video registers for frame */
231: Video_ClearOnVBL();
232: /* Store off PSG registers for YM file, is enabled */
233: YMFormat_UpdateRecording();
1.1.1.4 root 234: /* Generate 1/50th second of sound sample data, to be played by sound thread */
1.1 root 235: Sound_Update_VBL();
236:
1.1.1.5 root 237: MakeSR();
238: if (4>FIND_IPL) /* Vertical blank, level 4! */
239: {
1.1 root 240: ExceptionVector = EXCEPTION_VBLANK;
241: M68000_Exception(); /* VBL interrupt */
242: }
1.1.1.5 root 243: else
244: {
1.1 root 245: /* Higher priority interrupt is currently being executed(eg MFP). Set VBL to occur later */
246: if (!Int_InterruptActive(INTERRUPT_VIDEO_VBL_PENDING))
247: Int_AddAbsoluteInterrupt(100,INTERRUPT_VIDEO_VBL_PENDING);
248: }
249:
1.1.1.5 root 250: /* And handle any messages, check for quit message */
1.1 root 251: Main_EventHandler(); /* Process messages, set 'bQuitProgram' if user tries to quit */
252: if(bQuitProgram)
253: Int_AddAbsoluteInterrupt(4, 0L); /* Pass NULL interrupt function to quit cleanly */
1.1.1.6 ! root 254:
! 255: /* Wait, so we stay in sync with the sound */
! 256: do
! 257: {
! 258: nNewMilliTicks = SDL_GetTicks();
! 259: nDelay = 1000/nScreenRefreshRate - (nNewMilliTicks-nOldMilliTicks);
! 260: if(nDelay > 2)
! 261: {
! 262: /* SDL_Delay seems to be quite inaccurate, so we don't wait the whole time */
! 263: SDL_Delay(nDelay - 1);
! 264: }
! 265: }
! 266: while(nDelay > 0);
! 267: nOldMilliTicks = nNewMilliTicks;
1.1 root 268: }
269:
270:
271: /*-----------------------------------------------------------------------*/
272: /*
273: This doesn't make sense... I always thought if a 68000 interrupt, eg the VBL, occurs while a
274: higher priority interrupt was in service the interrupt was ignored. This does not seem to be
275: the case. This really needs checking on a real ST, but I have noticed some menu discs run
276: sound routines from the MFP timers which overlap the VBL, yet the VBL still occurs...
277: */
278: void Video_InterruptHandler_VBL_Pending(void)
279: {
280: /* Remove this interrupt from list and re-order */
281: Int_AcknowledgeInterrupt();
282:
1.1.1.5 root 283: MakeSR();
1.1 root 284: /* Check if can execute VBL */
1.1.1.5 root 285: if (4>FIND_IPL) /* Vertical blank, level 4! */
286: {
1.1 root 287: ExceptionVector = EXCEPTION_VBLANK;
288: M68000_Exception(); /* VBL interrupt */
289: }
290: else
291: Int_AddAbsoluteInterrupt(100,INTERRUPT_VIDEO_VBL_PENDING);
292: }
293:
294: /*-----------------------------------------------------------------------*/
295: /*
296: End Of Line interrupt
1.1.1.5 root 297: As this occurs at the end of a line we cannot get timing for START of first
298: line (as in Spectrum 512)
1.1 root 299: */
300: void Video_InterruptHandler_EndLine(void)
301: {
302: /* Remove this interrupt from list and re-order */
303: Int_AcknowledgeInterrupt();
304: /* Generate new HBL, if need to - there are 313 HBLs per frame */
305: if (nHBL<(SCANLINES_PER_FRAME-1))
306: Int_AddAbsoluteInterrupt(CYCLES_PER_LINE,INTERRUPT_VIDEO_ENDLINE);
307:
1.1.1.5 root 308: /* Is this a good place to send the keyboard packets? Done once per frame */
309: if(nHBL == nStartHBL)
310: {
311: /* On each VBL send automatic keyboard packets for mouse, joysticks etc... */
312: IKBD_SendAutoKeyboardCommands();
313: }
314:
1.1 root 315: /* Timer A/B occur at END of first visible screen line in Event Count mode */
1.1.1.6 ! root 316: // if ( (nHBL>=FIRST_VISIBLE_HBL) && (nHBL<FIRST_VISIBLE_HBL+NUM_VISIBLE_LINES) )
1.1 root 317: if ( (nHBL>=nStartHBL) && (nHBL<nEndHBL) )
318: {
319: /* Handle Timers A and B when using Event Count mode(timer taken from HBL) */
1.1.1.2 root 320: if (MFP_TACR==0x08) /* Is timer in Event Count mode? */
1.1 root 321: MFP_TimerA_EventCount_Interrupt();
1.1.1.2 root 322: if (MFP_TBCR==0x08) /* Is timer in Event Count mode? */
1.1 root 323: MFP_TimerB_EventCount_Interrupt();
324: }
325:
1.1.1.2 root 326: FDC_UpdateHBL(); /* Update FDC motion */
327: Video_EndHBL(); /* Increase HBL count, copy line to display buffer and do any video trickery */
1.1 root 328:
329: /* If we don't often pump data into the event queue, the SDL misses events... grr... */
330: if( !(nHBL&63) )
1.1.1.5 root 331: {
1.1 root 332: Main_EventHandler();
1.1.1.5 root 333: }
1.1 root 334: }
335:
336:
1.1.1.2 root 337: /*-----------------------------------------------------------------------*/
1.1 root 338: /*
339: HBL interrupt - this is very inaccurate on ST and appears to occur around 1/3rd way into
340: the display!
341: */
342: void Video_InterruptHandler_HBL(void)
343: {
1.1.1.2 root 344: /* Remove this interrupt from list and re-order */
1.1 root 345: Int_AcknowledgeInterrupt();
1.1.1.2 root 346: /* Generate new Timer AB, if need to - there are 313 HBLs per frame */
1.1.1.6 ! root 347: if (nHBL<(SCANLINES_PER_FRAME-1)) {
1.1 root 348: Int_AddAbsoluteInterrupt(CYCLES_PER_LINE,INTERRUPT_VIDEO_HBL);
1.1.1.6 ! root 349: }
1.1 root 350:
1.1.1.5 root 351: MakeSR();
352: if (2>FIND_IPL) /* Horizontal blank, level 2! */
353: {
1.1 root 354: ExceptionVector = EXCEPTION_HBLANK;
1.1.1.2 root 355: M68000_Exception(); /* HBL interrupt */
1.1 root 356: }
357: }
358:
359:
1.1.1.2 root 360: /*-----------------------------------------------------------------------*/
1.1 root 361: /*
362: Sync Table handlers
363: */
364: void Video_SyncHandler_SetLeftRightBorder(void)
365: {
1.1.1.2 root 366: LeftRightBorder |= SyncHandler_Value; /* Turn on left/right borders */
1.1 root 367: }
368:
369: void Video_SyncHandler_SetSyncScrollOffset(void)
370: {
1.1.1.2 root 371: VideoRaster += SyncHandler_Value; /* Add offset to video address(sync-scroll) */
1.1 root 372: }
373:
374: void Video_SyncHandler_SetTopBorder(void)
375: {
1.1.1.2 root 376: OverscanMode |= OVERSCANMODE_TOP; /* Set overscan bit */
377: nStartHBL = FIRST_VISIBLE_HBL; /* New start screen line */
1.1 root 378: }
379:
380: void Video_SyncHandler_SetBottomBorder(void)
381: {
1.1.1.2 root 382: OverscanMode |= OVERSCANMODE_BOTTOM; /* Set overscan bit */
383: nEndHBL = SCREEN_START_HBL+SCREEN_HEIGHT_HBL+OVERSCAN_BOTTOM; /* New end screen line */
1.1 root 384: }
385:
1.1.1.2 root 386:
387: /*-----------------------------------------------------------------------*/
1.1 root 388: /*
389: Write to VideoShifter(0xff8260), resolution bits
390: */
391: void Video_WriteToShifter(void)
392: {
1.1.1.2 root 393: /* Store into table so can test at end of HBL */
1.1 root 394: Video_StoreSyncShifterAccess(0xff8260,VideoShifterByte);
395: }
396:
1.1.1.2 root 397:
398: /*-----------------------------------------------------------------------*/
1.1 root 399: /*
400: Write to VideoSync(0xff820a), Hz setting
401: */
402: void Video_WriteToSync(void)
403: {
1.1.1.2 root 404: /* Store into table so can test at end of HBL */
1.1 root 405: Video_StoreSyncShifterAccess(0xff820a,VideoSyncByte);
406: }
407:
1.1.1.2 root 408:
409: /*-----------------------------------------------------------------------*/
1.1 root 410: /*
411: Check access Video(0xff8260 or 0xff820a) to see if opened top/bottom/left/right borders or
412: even tried for a SyncScroll
413: */
414: BOOL Video_CheckSyncShifterTable(unsigned int Address, unsigned char Byte, int FrameCycles, SYNCSHIFTER_ACCESS_TABLE *pAccessTable)
415: {
416: SYNCSHIFTER_ACCESS *pSyncShifter;
417: BOOL bFoundMatch=FALSE;
418: int i=0;
419:
1.1.1.2 root 420: /* Scan each shifter entry to see if this new values matches - if not rest */
1.1 root 421: while(pAccessTable[i].nChecks) {
1.1.1.2 root 422: /* Get table of entries, index at 'counter' */
1.1 root 423: pSyncShifter = &pAccessTable[i].pSyncShifterAccess[pAccessTable[i].nCount];
1.1.1.2 root 424: /* Does next value match table? */
1.1 root 425: if ( (pSyncShifter->Address==Address) && (pSyncShifter->Byte==Byte) && (pSyncShifter->FrameCycles==FrameCycles) ) {
1.1.1.2 root 426: /* Set flag (used for debugging) */
1.1 root 427: bFoundMatch = TRUE;
428:
1.1.1.2 root 429: /* Got match increase count */
1.1 root 430: pAccessTable[i].nCount++;
1.1.1.2 root 431: /* Is table complete? */
1.1 root 432: if (pAccessTable[i].nCount==pAccessTable[i].nChecks) {
1.1.1.2 root 433: /* Reset count, ready for next time */
1.1 root 434: pAccessTable[i].nCount = 0;
1.1.1.2 root 435: /* Yes, call handler function with passed variable */
1.1 root 436: SyncHandler_Value = pAccessTable[i].Value;
437: CALL_VAR(pAccessTable[i].pFunc);
438: }
439: }
440: else {
1.1.1.2 root 441: /* No match, reset table count */
1.1 root 442: pAccessTable[i].nCount = 0;
443: }
444:
445: i++;
446: }
447:
448: return(bFoundMatch);
449: }
450:
1.1.1.2 root 451:
452: /*-----------------------------------------------------------------------*/
1.1 root 453: /*
454: Store Sync/Shifter write into table so can check at end of HBL for border/sync-scroll hardware tricks
455: */
456: void Video_StoreSyncShifterAccess(unsigned int Address,unsigned char Byte)
457: {
458: int FrameCycles = Int_FindFrameCycles();
459:
1.1.1.2 root 460: /* Get back to where instruction started for timing */
461: //FrameCycles -= lastInstructionCycles;
1.1 root 462:
463: // char szString[256];
464: // sprintf(szString,"0x%X=0x%2.2X %d(%d) @ %d",Address,Byte,FrameCycles,FrameCycles&511,nHBL);
465: // debug << szString << endl;
466:
467: Video_CheckSyncShifterTable(Address,Byte,FrameCycles&511, pLeftRightBorderAccessTable);
468: // Video_CheckSyncShifterTable(Address,Byte,FrameCycles&511, pSyncScrollerAccessTable);
469: }
470:
1.1.1.2 root 471:
472: /*-----------------------------------------------------------------------*/
1.1 root 473: /*
474: Reset Sync/Shifter table at start of each HBL
475: */
476: void Video_StartHBL(void)
477: {
478: LeftRightBorder = 0;
479: }
480:
1.1.1.2 root 481:
482: /*-----------------------------------------------------------------------*/
1.1 root 483: /*
484: Store whole palette on first line so have reference to work from
485: */
486: void Video_StoreFirstLinePalette(void)
487: {
488: unsigned short int *pp2;
489: int i;
490:
491: pp2 = (unsigned short int *)((unsigned long)STRam+0xff8240);
492: for(i=0; i<16; i++)
493: HBLPalettes[i] = STMemory_Swap68000Int(*pp2++);
1.1.1.2 root 494: /* And set mask flag with palette and resolution */
1.1 root 495: HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((unsigned long)STMemory_ReadByte(0xff8260)&0x3)<<16);
496: }
497:
1.1.1.2 root 498:
499: /*-----------------------------------------------------------------------*/
1.1 root 500: /*
501: Store resolution on each line(used to test if mixed low/medium resolutions)
502: */
503: void Video_StoreResolution(int y)
504: {
1.1.1.2 root 505: /* Clear resolution, and set with current value */
1.1 root 506: if (!(bUseHighRes || bUseVDIRes) ) {
507: HBLPaletteMasks[y] &= ~(0x3<<16);
508: HBLPaletteMasks[y] |= ((unsigned long)STMemory_ReadByte(0xff8260)&0x3)<<16;
509: }
510: }
511:
1.1.1.2 root 512:
513: /*-----------------------------------------------------------------------*/
1.1 root 514: /*
515: Copy line of screen into buffer for conversion later.
516: Possible lines may be top/bottom border, and/or left/right borders
517: */
518: void Video_CopyScreenLine(int BorderMask)
519: {
1.1.1.2 root 520: /* Only copy screen line if not doing high VDI resolution */
1.1 root 521: if (!bUseVDIRes) {
522: BorderMask |= LeftRightBorder;
523:
524: if (bUseHighRes) {
1.1.1.2 root 525: /* Copy for hi-res (no overscan) */
1.1 root 526: memcpy(pSTScreen,(char *)VideoRaster,SCREENBYTES_MIDDLE);
527: VideoRaster += SCREENBYTES_MIDDLE;
1.1.1.2 root 528: /* Each screen line copied to buffer is always same length */
1.1 root 529: pSTScreen += SCREENBYTES_MIDDLE;
530: }
531: else {
1.1.1.2 root 532: /* Is total blank line? Ie top/bottom border? */
1.1 root 533: if (BorderMask&(BORDERMASK_TOP|BORDERMASK_BOTTOM)) {
1.1.1.2 root 534: /* Clear line to colour '0' */
1.1 root 535: memset(pSTScreen,0,SCREENBYTES_LINE);
536: }
537: else {
1.1.1.2 root 538: /* Does have left border? If not, clear to colour '0' */
1.1 root 539: if (BorderMask&BORDERMASK_LEFT) {
540: VideoRaster += 24-SCREENBYTES_LEFT;
541: memcpy(pSTScreen,(char *)VideoRaster,SCREENBYTES_LEFT);
542: VideoRaster += SCREENBYTES_LEFT;
543: }
544: else
545: memset(pSTScreen,0,SCREENBYTES_LEFT);
1.1.1.2 root 546: /* Copy middle - always present */
1.1 root 547: memcpy(pSTScreen+SCREENBYTES_LEFT,(char *)VideoRaster,SCREENBYTES_MIDDLE);
548: VideoRaster += SCREENBYTES_MIDDLE;
1.1.1.2 root 549: /* Does have right border? If not, clear to colour '0' */
1.1 root 550: if (BorderMask&BORDERMASK_RIGHT) {
551: memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,(char *)VideoRaster,SCREENBYTES_RIGHT);
552: VideoRaster += 46-SCREENBYTES_RIGHT;
553: VideoRaster += SCREENBYTES_RIGHT;
554: }
555: else
556: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
557: }
1.1.1.6 ! root 558:
1.1.1.2 root 559: /* Each screen line copied to buffer is always same length */
1.1 root 560: pSTScreen += SCREENBYTES_LINE;
561: }
562: }
563: }
564:
1.1.1.2 root 565:
566: /*-----------------------------------------------------------------------*/
1.1 root 567: /*
568: Copy extended GEM resolution screen
569: */
570: void Video_CopyVDIScreen(void)
571: {
1.1.1.2 root 572: /* Copy whole screen, don't care about being exact as for GEM only */
573: memcpy(pSTScreen,(char *)VideoRaster,((VDIWidth*VDIPlanes)/8)*VDIHeight); /* 640x400x16colour */
1.1 root 574: }
575:
1.1.1.2 root 576:
577: /*-----------------------------------------------------------------------*/
1.1 root 578: /*
579: Check at end of each HBL to see if any Sync/Shifter hardware tricks have been attempted
580: */
581: void Video_EndHBL(void)
582: {
1.1.1.2 root 583: /* Are we in possible visible display (including borders)? */
1.1 root 584: if ( (nHBL>=FIRST_VISIBLE_HBL) && (nHBL<(FIRST_VISIBLE_HBL+NUM_VISIBLE_LINES)) ) {
1.1.1.2 root 585: /* Copy line of screen to buffer to simulate TV raster trace - required for mouse cursor display/game updates */
586: /* Eg, Lemmings and The Killing Game Show are good examples */
1.1 root 587:
1.1.1.2 root 588: if (nHBL<nStartHBL) /* Are we in top border blank (ie no top overscan enabled) */
1.1 root 589: Video_CopyScreenLine(BORDERMASK_TOP);
1.1.1.2 root 590: else if (nHBL>=nEndHBL) /* Are we in bottom border blank */
1.1 root 591: Video_CopyScreenLine(BORDERMASK_BOTTOM);
1.1.1.2 root 592: else /* Must be in visible screen(including overscan), ignore left/right borders for now */
1.1 root 593: Video_CopyScreenLine(BORDERMASK_NONE);
594:
1.1.1.2 root 595: if (nHBL==FIRST_VISIBLE_HBL) { /* Very first line on screen - HBLPalettes[0] */
596: /* Store ALL palette for this line into raster table for datum */
1.1 root 597: Video_StoreFirstLinePalette();
598: }
1.1.1.2 root 599: /* Store resolution for every line so can check for mix low/medium screens */
1.1 root 600: Video_StoreResolution(nHBL-FIRST_VISIBLE_HBL);
601: }
602:
1.1.1.2 root 603: /* Finally increase HBL count */
1.1 root 604: nHBL++;
605:
1.1.1.2 root 606: Video_StartHBL(); /* Setup next one */
1.1 root 607: }
608:
1.1.1.2 root 609:
610: /* Clear raster line table to store changes in palette/resolution on a line basic */
611: /* Call once on VBL interrupt */
1.1 root 612: void Video_SetScreenRasters(void)
613: {
614: pHBLPaletteMasks = HBLPaletteMasks;
615: pHBLPalettes = HBLPalettes;
616: Memory_Clear(pHBLPaletteMasks,sizeof(unsigned long)*NUM_VISIBLE_LINES);
617: }
618:
1.1.1.2 root 619:
620: /*-----------------------------------------------------------------------*/
1.1 root 621: /*
622: Set pointers to HBLPalette tables to store correct colours/resolutions
623: */
624: void Video_SetHBLPaletteMaskPointers(void)
625: {
626: int FrameCycles;
627: int Line;
628:
629: /* Top of standard screen is 64 lines from VBL(64x512=32768 cycles) */
630: /* Each line is 64+320+64+64(Blank) = 512 pixels per scan line */
631: /* Timer occurs at end of 64+320+64; Display Enable(DE)=Low */
632: /* HBL is incorrect on machine and occurs around 96+ cycles in */
633:
634: /* Top of standard screen is 64 lines from VBL(64x512=32768 cycles) */
635: /* Each line is 96 + 320 + 96 = 512 pixels per scan line(each pixel is one cycle) */
636: FrameCycles = Int_FindFrameCycles();
637:
638: /* Find 'line' into palette - screen starts 64 lines down, less 28 for top overscan */
639: /* And if write to last 96 cycle of line it will count as the NEXT line(needed else games may flicker) */
640: Line = (FrameCycles-(FIRST_VISIBLE_HBL*CYCLES_PER_LINE)+SCREEN_START_CYCLE)/CYCLES_PER_LINE;
641: if (Line<0) /* Limit to top/bottom of possible visible screen */
642: Line = 0;
643: if (Line>=NUM_VISIBLE_LINES)
644: Line = NUM_VISIBLE_LINES-1;
645:
1.1.1.2 root 646: /* Store pointers */
1.1 root 647: pHBLPaletteMasks = &HBLPaletteMasks[Line]; /* Next mask entry */
648: pHBLPalettes = &HBLPalettes[16*Line]; /* Next colour raster list x16 colours */
649: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.