|
|
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.11 root 12:
13: /* 2007/03/xx [NP] Support for cycle precise border removal / hardware scrolling by using */
14: /* Cycles_GetCounterOnWriteAccess (support left/right border and lines with*/
15: /* length of +26, +2, -2, +44, -106 bytes). */
16: /* Add support for 'Enchanted Lands' second removal of right border. */
17: /* More precise support for reading video counter $ff8205/07/09. */
18: /* 2007/04/14 [NP] Precise reloading of $ff8201/03 into $ff8205/07/09 at line 310 on cycle */
19: /* RESTART_VIDEO_COUNTER_CYCLE (ULM DSOTS Demo). */
20: /* 2007/04/16 [NP] Better Video_CalculateAddress. We must substract a "magic" 12 cycles to */
21: /* Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) to get a correct */
22: /* value (No Cooper's video synchro protection is finaly OK :) ). */
23: /* 2007/04/17 [NP] - Switch to 60 Hz to remove top border on line 33 should occur before */
24: /* LINE_REMOVE_TOP_CYCLE (a few cycles before the HBL) */
25: /* 2007/04/23 [NP] - Slight change in Video_StoreResolution to ignore hi res if the line */
26: /* has left/right border removed -> assume of lo res line. */
27: /* - Handle simultaneous removal of right border and bottom border with */
28: /* the same long switch to 60 Hz (Sync Screen in SNY II). */
29: /* 2007/05/06 [NP] More precise tests for top border's removal. */
30: /* 2007/05/11 [NP] Add support for mid res overscan (No Cooper Greetings). */
31: /* 2007/05/12 [NP] - LastCycleSync50 and LastCycleSync60 for better top border's removal */
32: /* in Video_EndHBL. */
33: /* - Use VideoOffset in Video_CopyScreenLineColor to handle missing planes */
34: /* depending on line (mid/lo and borders). */
35: /* 2007/09/25 [NP] Replace printf by calls to HATARI_TRACE. */
36: /* 2007/10/02 [NP] Use the new int.c to add interrupts with INT_CPU_CYCLE / INT_MFP_CYCLE. */
37: /* 2007/10/23 [NP] Add support for 0 byte line (60/50 switch at cycle 56). Allow 5 lines */
38: /* hardscroll (e.g. SHFORSTV.EXE by Paulo Simmoes). */
39: /* 2007/10/31 [NP] Use BORDERMASK_LEFT_OFF_MID when left border is removed with hi/med */
40: /* switch (ST CNX in PYM). */
41: /* 2007/11/02 [NP] Add support for 4 pixel hardware scrolling ("Let's Do The Twist" by */
42: /* ST CNX in Punish Your Machine). */
43: /* 2007/11/05 [NP] Depending on the position of the mid res switch, the planes will be */
44: /* shifted when doing midres overscan (Best Part Of the Creation in PYM */
45: /* or No Cooper Greetings). */
46: /* 2007/11/30 [NP] A hi/mid switch to remove the left border can be either used to initiate*/
47: /* a right hardware scrolling in low res (St Cnx) or a complete mid res */
48: /* overscan line (Dragonnels Reset Part). */
49: /* Use bit 0-15, 16-19 and 20-23 in ScreenBorderMask[] to track border */
50: /* trick, STF hardware scrolling and plane shifting. */
51: /* 2007/12/22 [NP] Very precise values for VBL_VIDEO_CYCLE_OFFSET, HBL_VIDEO_CYCLE_OFFSET */
52: /* TIMERB_VIDEO_CYCLE_OFFSET and RESTART_VIDEO_COUNTER_CYCLE. These values */
53: /* were calculated using sttiming.s on a real STF and should give some very*/
54: /* accurate results (also uses 56 cycles instead of 44 to process an */
55: /* exception). */
56: /* 2007/12/29 [NP] Better support for starting line 2 bytes earlier (if the line starts in */
57: /* 60 Hz and goes back to 50 Hz later), when combined with top border */
58: /* removal (Mindbomb Demo - D.I. No Shit). */
59: /* 2007/12/30 [NP] Slight improvement of VideoAdress in Video_CalculateAddress when reading*/
60: /* during the top border. */
61: /* Correct the case where removing top border on line 33 could also be */
62: /* interpreted as a right border removal (which is not possible since the */
63: /* display is still off at that point). */
64: /* 2008/01/03 [NP] Better handling of nStartHBL and nEndHBL when switching freq from */
65: /* 50 to 60 Hz. Allows emulation of a "short" 50 Hz screen of 171 lines */
66: /* and a more precise removal of bottom border in 50 and 60 Hz. */
67: /* 2008/01/04 [NP] More generic detection for removing 2 bytes to the right of the line */
68: /* when switching from 60 to 50 Hz (works even with a big number of cycles */
69: /* between the freq changes) (Phaleon's Menus). */
70: /* 2008/01/06 [NP] More generic detection for stopping the display in the middle of a line */
71: /* with a hi / lo res switch (-106 bytes per line). Although switch to */
72: /* hi res should occur at cycle 160, some demos use 164 (Phaleon's Menus). */
73: /* 2008/01/06 [NP] Better bottom border's removal in 50 Hz : switch to 60 Hz must occur */
74: /* before cycle LINE_REMOVE_BOTTOM_CYCLE on line 263 and switch back to 50 */
75: /* Hz must occur after LINE_REMOVE_BOTTOM_CYCLE on line 263 (this means */
76: /* we can already be in 50 Hz when Video_EndHBL is called and still remove */
77: /* the bottom border). This is similar to the tests used to remove the */
78: /* top border. */
79: /* 2008/01/12 [NP] In Video_SetHBLPaletteMaskPointers, consider that if a color's change */
80: /* occurs after cycle LINE_END_CYCLE_NO_RIGHT, then it's related to the */
81: /* next line. */
82: /* FIXME : it would be better to handle all color changes through spec512.c*/
83: /* and drop the 16 colors palette per line. */
84: /* FIXME : we should use Cycles_GetCounterOnWriteAccess, but it doesn't */
85: /* support multiple accesses like move.l or movem. */
86: /* 2008/01/12 [NP] Handle 60 Hz switch during the active display of the last line to remove*/
87: /* the bottom border : this should also shorten line by 2 bytes (F.N.I.L. */
88: /* Demo by TNT). */
89: /* 2008/01/15 [NP] Don't do 'left+2' if switch back to 50 Hz occurs when line is not active*/
90: /* (after cycle LINE_END_CYCLE_60) (XXX International Demos). */
91: /* 2008/01/31 [NP] Improve left border detection : allow switch to low res on cycle <= 28 */
92: /* instead of <= 20 (Vodka Demo Main Menu). */
93: /* 2008/02/02 [NP] Added 0 byte line detection when switching hi/lo res at position 28 */
94: /* (Lemmings screen in Nostalgic-o-demo). */
95: /* 2008/02/03 [NP] On STE, write to video counter $ff8205/07/09 should only be applied */
96: /* immediatly if display has not started for the line (before cycle */
97: /* LINE_END_CYCLE_50). If write occurs after, the change to pVideoRaster */
98: /* should be delayed to the end of the line, after processing the current */
99: /* line with Video_CopyScreenLineColor (Stardust Tunnel Demo). */
100: /* 2008/02/04 [NP] The problem is similar when writing to hwscroll $ff8264, we must delay */
101: /* the change until the end of the line if display was already started */
102: /* (Mindrewind by Reservoir Gods). */
103: /* 2008/02/06 [NP] On STE, when left/right borders are off and hwscroll > 0, we must read */
104: /* 6 bytes less than the expected value (E605 by Light). */
105: /* 2008/02/17 [NP] In Video_CopyScreenLine, LineWidth*2 bytes should be added after */
106: /* pNewVideoRaster is copied to pVideoRaster (Braindamage Demo). */
107: /* When reading a byte at ff8205/07/09, all video address bytes should be */
108: /* updated in Video_ScreenCounter_ReadByte, not just the byte that was */
109: /* read. Fix programs that just modify one byte in the video address */
110: /* counter (e.g. sub #1,$ff8207 in Braindamage Demo). */
111: /* 2008/02/19 [NP] In Video_CalculateAddress, use pVideoRaster instead of VideoBase to */
112: /* determine the video address when display is off in the upper part of */
113: /* the screen (in case ff8205/07/09 were modified on STE). */
114: /* 2008/02/20 [NP] Better handling in Video_ScreenCounter_WriteByte by changing only one */
115: /* byte and keeping the other (Braindamage End Part). */
116: /* 2008/03/08 [NP] Use M68000_INT_VIDEO when calling M68000_Exception(). */
117: /* 2008/03/13 [NP] On STE, LineWidth value in $ff820f is added to the shifter counter just */
118: /* when display is turned off on a line (when right border is started, */
119: /* which is usually on cycle 376). */
120: /* This means a write to $ff820f should be applied immediatly only if it */
121: /* occurs before cycle LineEndCycle. Else, it is stored in NewLineWidth */
122: /* and used after Video_CopyScreenLine has processed the current line */
123: /* (improve the bump mapping part in Pacemaker by Paradox). */
124: /* LineWidth should be added to pVideoRaster before checking the possible */
125: /* modification of $ff8205/07/09 in Video_CopyScreenLine. */
126: /* 2008/03/14 [NP] Rename ScanLineSkip to LineWidth (more consistent with STE docs). */
127: /* On STE, better support for writing to video counter, line width and */
128: /* hw scroll. If write to register occurs just at the start of a new line */
129: /* but before Video_EndHBL (because the move started just before cycle 512)*/
130: /* then the new value should not be set immediatly but stored and set */
131: /* during Video_EndHBL (fix the bump mapping part in Pacemaker by Paradox).*/
1.1.1.12! root 132: /* 2008/03/25 [NP] On STE, when bSteBorderFlag is true, we should add 16 pixels to the left*/
! 133: /* border, not to the right one (Just Musix 2 Menu by DHS). */
! 134: /* 2008/03/26 [NP] Clear the rest of the border when using border tricks left+2, left+8 */
! 135: /* or right-106 (remove garbage pixels when hatari resolution changes). */
! 136: /* 2008/03/29 [NP] Function Video_SetSystemTimings to use different values depending on */
! 137: /* the machine type. On STE, top/bottom border removal can occur at cycle */
! 138: /* 500 instead of 504 on STF. */
1.1.1.11 root 139:
140:
1.1.1.12! root 141:
! 142: const char Video_rcsid[] = "Hatari $Id: video.c,v 1.99 2008/03/29 11:05:48 npomarede Exp $";
1.1 root 143:
1.1.1.7 root 144: #include <SDL_endian.h>
1.1.1.4 root 145:
1.1 root 146: #include "main.h"
1.1.1.6 root 147: #include "configuration.h"
1.1.1.10 root 148: #include "cycles.h"
1.1 root 149: #include "fdc.h"
150: #include "int.h"
151: #include "ikbd.h"
1.1.1.8 root 152: #include "ioMem.h"
1.1.1.4 root 153: #include "keymap.h"
1.1 root 154: #include "m68000.h"
155: #include "memorySnapShot.h"
156: #include "mfp.h"
1.1.1.11 root 157: #include "printer.h"
1.1 root 158: #include "screen.h"
159: #include "shortcut.h"
160: #include "sound.h"
161: #include "spec512.h"
162: #include "stMemory.h"
163: #include "vdi.h"
164: #include "video.h"
165: #include "ymFormat.h"
1.1.1.11 root 166: #include "falcon/videl.h"
167: #include "falcon/hostscreen.h"
168: #include "trace.h"
1.1.1.4 root 169:
170:
1.1.1.9 root 171: #define BORDERMASK_NONE 0x00 /* Borders masks */
172: #define BORDERMASK_LEFT 0x01
173: #define BORDERMASK_RIGHT 0x02
1.1.1.10 root 174: #define BORDERMASK_MIDDLE 0x04
1.1.1.9 root 175:
1.1.1.11 root 176: /* The border's mask allows to keep track of all the border tricks */
177: /* applied to one video line. The masks for all lines are stored in the array */
178: /* ScreenBorderMask[]. */
179: /* - bits 0-15 are used to describe the border tricks. */
180: /* - bits 16-19 are used to store the pixels count in case of right hardware */
181: /* scrolling on STF. */
182: /* - bits 20-23 are used to store the bytes offset to apply for some particular */
183: /* tricks (for example mid res overscan can shift display by 0 or 2 bytes */
184: /* depending on when the switch to mid res is done after removing the left */
185: /* border). */
186:
187: #define BORDERMASK_LEFT_OFF 0x01 /* removal of left border with hi/lo res switch -> +26 bytes */
188: #define BORDERMASK_LEFT_PLUS_2 0x02 /* line starts earlier in 60 Hz -> +2 bytes */
189: #define BORDERMASK_STOP_MIDDLE 0x04 /* line ends in hires at cycle 160 -> -106 bytes */
190: #define BORDERMASK_RIGHT_MINUS_2 0x08 /* line ends earlier in 60 Hz -> -2 bytes */
191: #define BORDERMASK_RIGHT_OFF 0x10 /* removal of right border -> +44 bytes */
192: #define BORDERMASK_RIGHT_OFF_FULL 0x20 /* full removal of right border and next left border -> +22 bytes */
193: #define BORDERMASK_OVERSCAN_MID_RES 0x40 /* some borders were removed and the line is in mid res instead of low res */
194: #define BORDERMASK_EMPTY_LINE 0x80 /* 60/50 Hz switch prevents the line to start, video counter is not incremented */
195: #define BORDERMASK_LEFT_OFF_MID 0x100 /* removal of left border with hi/mid res switch -> +26 bytes (for 4 pixels hardware scrolling) */
196:
197:
198:
199: int STRes = ST_LOW_RES; /* current ST resolution */
200: int TTRes; /* TT shifter resolution mode */
1.1.1.9 root 201:
1.1.1.11 root 202: BOOL bUseSTShifter; /* Falcon: whether to use ST palette */
203: BOOL bUseHighRes; /* Use hi-res (ie Mono monitor) */
1.1 root 204: int OverscanMode; /* OVERSCANMODE_xxxx for current display frame */
1.1.1.8 root 205: Uint16 HBLPalettes[(NUM_VISIBLE_LINES+1)*16]; /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
206: Uint16 *pHBLPalettes; /* Pointer to current palette lists, one per HBL */
1.1.1.9 root 207: Uint32 HBLPaletteMasks[NUM_VISIBLE_LINES+1]; /* Bit mask of palette colours changes, top bit set is resolution change */
208: Uint32 *pHBLPaletteMasks;
1.1.1.6 root 209: int nScreenRefreshRate = 50; /* 50 or 60 Hz in color, 70 Hz in mono */
1.1.1.8 root 210: Uint32 VideoBase; /* Base address in ST Ram for screen (read on each VBL) */
1.1.1.9 root 211:
1.1.1.11 root 212: int nVBLs; /* VBL Counter */
213: int nHBL; /* HBL line */
214: int nStartHBL; /* Start HBL for visible screen */
215: int nEndHBL; /* End HBL for visible screen */
1.1.1.10 root 216: int nScanlinesPerFrame = 313; /* Number of scan lines per frame */
217: int nCyclesPerLine = 512; /* Cycles per horizontal line scan */
218: static int nFirstVisibleHbl = 34; /* The first line of the ST screen that is copied to the PC screen buffer */
219:
1.1.1.9 root 220: static Uint8 HWScrollCount; /* HW scroll pixel offset, STe only (0...15) */
1.1.1.11 root 221: int NewHWScrollCount = -1; /* Used in STE mode when writing to the scrolling register $ff8265 */
222: static Uint8 LineWidth; /* Scan line width add, STe only (words, minus 1) */
223: int NewLineWidth = -1; /* Used in STE mode when writing to the line width register $ff820f */
1.1.1.8 root 224: static Uint8 *pVideoRaster; /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
225: static Uint8 VideoShifterByte; /* VideoShifter (0xff8260) value store in video chip */
226: static int LeftRightBorder; /* BORDERMASK_xxxx used to simulate left/right border removal */
1.1.1.11 root 227: static int LineStartCycle; /* Cycle where display starts for the current line */
228: static int LineEndCycle; /* Cycle where display ends for the current line */
1.1.1.10 root 229: static BOOL bSteBorderFlag; /* TRUE when screen width has been switched to 336 (e.g. in Obsession) */
1.1.1.11 root 230: static BOOL bTTColorsSync, bTTColorsSTSync; /* whether TT colors need convertion to SDL */
1.1 root 231:
1.1.1.11 root 232: int ScreenBorderMask[ MAX_SCANLINES_PER_FRAME ];
233: int LastCycleSync50; /* value of Cycles_GetCounterOnWriteAccess last time ff820a was set to 0x02 for the current VBL */
234: int LastCycleSync60; /* value of Cycles_GetCounterOnWriteAccess last time ff820a was set to 0x00 for the current VBL */
235: int NewVideoHi = -1; /* new value for $ff8205 on STE */
236: int NewVideoMed = -1; /* new value for $ff8207 on STE */
237: int NewVideoLo = -1; /* new value for $ff8209 on STE */
1.1 root 238:
1.1.1.12! root 239: int LineRemoveTopCycle = LINE_REMOVE_TOP_CYCLE_STF;
! 240: int LineRemoveBottomCycle = LINE_REMOVE_BOTTOM_CYCLE_STF;
! 241:
! 242:
1.1 root 243: /*-----------------------------------------------------------------------*/
1.1.1.11 root 244: /**
245: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
246: */
1.1 root 247: void Video_MemorySnapShot_Capture(BOOL bSave)
248: {
1.1.1.11 root 249: /* Save/Restore details */
250: MemorySnapShot_Store(&VideoShifterByte, sizeof(VideoShifterByte));
251: MemorySnapShot_Store(&TTRes, sizeof(TTRes));
252: MemorySnapShot_Store(&bUseSTShifter, sizeof(bUseSTShifter));
253: MemorySnapShot_Store(&bUseHighRes, sizeof(bUseHighRes));
254: MemorySnapShot_Store(&nVBLs, sizeof(nVBLs));
255: MemorySnapShot_Store(&nHBL, sizeof(nHBL));
256: MemorySnapShot_Store(&nStartHBL, sizeof(nStartHBL));
257: MemorySnapShot_Store(&nEndHBL, sizeof(nEndHBL));
258: MemorySnapShot_Store(&OverscanMode, sizeof(OverscanMode));
259: MemorySnapShot_Store(HBLPalettes, sizeof(HBLPalettes));
260: MemorySnapShot_Store(HBLPaletteMasks, sizeof(HBLPaletteMasks));
261: MemorySnapShot_Store(&VideoBase, sizeof(VideoBase));
262: MemorySnapShot_Store(&LineWidth, sizeof(LineWidth));
263: MemorySnapShot_Store(&HWScrollCount, sizeof(HWScrollCount));
264: MemorySnapShot_Store(&pVideoRaster, sizeof(pVideoRaster));
265: MemorySnapShot_Store(&nScanlinesPerFrame, sizeof(nScanlinesPerFrame));
266: MemorySnapShot_Store(&nCyclesPerLine, sizeof(nCyclesPerLine));
267: MemorySnapShot_Store(&nFirstVisibleHbl, sizeof(nFirstVisibleHbl));
268: MemorySnapShot_Store(&bSteBorderFlag, sizeof(bSteBorderFlag));
1.1 root 269: }
270:
1.1.1.8 root 271:
1.1 root 272: /*-----------------------------------------------------------------------*/
1.1.1.12! root 273: /*
! 274: * Set specific video timings, depending on the system being emulated.
! 275: */
! 276: void Video_SetSystemTimings(void)
! 277: {
! 278: if ( ConfigureParams.System.nMachineType == MACHINE_ST )
! 279: {
! 280: LineRemoveTopCycle = LINE_REMOVE_TOP_CYCLE_STF;
! 281: LineRemoveBottomCycle = LINE_REMOVE_BOTTOM_CYCLE_STF;
! 282: }
! 283:
! 284: else /* STE, Falcon, TT */
! 285: {
! 286: LineRemoveTopCycle = LINE_REMOVE_TOP_CYCLE_STE;
! 287: LineRemoveBottomCycle = LINE_REMOVE_BOTTOM_CYCLE_STE;
! 288: }
! 289: }
! 290:
! 291:
! 292: /*-----------------------------------------------------------------------*/
1.1.1.11 root 293: /**
294: * Calculate and return video address pointer.
295: */
1.1.1.8 root 296: static Uint32 Video_CalculateAddress(void)
1.1 root 297: {
1.1.1.11 root 298: int X, nFrameCycles, NbBytes;
299: int HblCounterVideo;
300: Uint32 VideoAddress; /* Address of video display in ST screen space */
301: int nSyncByte;
302: int LineBorderMask;
303: int PrevSize;
304: int CurSize;
305:
306:
307: /* Find number of cycles passed during frame */
308: /* We need to substract '12' for correct video address calculation */
309: nFrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12;
310:
311: /* Now find which pixel we are on (ignore left/right borders) */
312: X = nFrameCycles % nCyclesPerLine;
313:
314: /* Get real video line count (can be different from nHBL) */
315: HblCounterVideo = nFrameCycles / nCyclesPerLine;
316:
317:
318: nSyncByte = IoMem_ReadByte(0xff820a) & 2; /* only keep bit 1 */
319: if (nSyncByte) /* 50 Hz */
320: {
321: LineStartCycle = LINE_START_CYCLE_50;
322: LineEndCycle = LINE_END_CYCLE_50;
323: }
324: else /* 60 Hz */
325: {
326: LineStartCycle = LINE_START_CYCLE_60;
327: LineEndCycle = LINE_END_CYCLE_60;
328: }
329:
330:
331: /* Top of screen is usually 63 lines from VBL in 50 Hz */
332: if (nFrameCycles < nStartHBL*nCyclesPerLine)
333: {
334: /* pVideoRaster was set during Video_ClearOnVBL using VideoBase */
335: /* and it could also have been modified on STE by writing to ff8205/07/09 */
336: /* We should not use ff8201/ff8203 which are reloaded in ff8205/ff8207 only once per VBL */
337: VideoAddress = pVideoRaster - STRam;
338: }
339:
340: else if (nFrameCycles > RESTART_VIDEO_COUNTER_CYCLE)
341: {
342: /* This is where ff8205/ff8207 are reloaded with the content of ff8201/ff8203 on a real ST */
343: /* (used in ULM DSOTS demos). VideoBase is also reloaded in Video_ClearOnVBL to be sure */
344: VideoBase = (Uint32)IoMem_ReadByte(0xff8201)<<16 | (Uint32)IoMem_ReadByte(0xff8203)<<8;
345: if (ConfigureParams.System.nMachineType != MACHINE_ST)
346: {
347: /* on STe 2 aligned, on Falcon 4 aligned, on TT 8 aligned. We do STe. */
348: VideoBase |= IoMem_ReadByte(0xff820d) & ~1;
349: }
350:
351: VideoAddress = VideoBase;
352: }
353:
354: else
355: {
356: VideoAddress = pVideoRaster - STRam; /* pVideoRaster is updated by Video_CopyScreenLineColor */
357:
358: /* Now find which pixel we are on (ignore left/right borders) */
359: X = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) % nCyclesPerLine;
360:
361: /* Get real video line count (can be different from nHBL) */
362: HblCounterVideo = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) / nCyclesPerLine;
363:
364: /* Correct the case when read overlaps end of line / start of next line */
365: /* Video_CopyScreenLineColor was not called yet to update VideoAddress */
366: /* so we need to determine the size of the previous line to get the */
367: /* correct value of VideoAddress. */
368: PrevSize = 0;
369: if ( HblCounterVideo < nHBL )
370: X = 0;
371: else if ( ( HblCounterVideo > nHBL ) /* HblCounterVideo = nHBL+1 */
372: && ( nHBL >= nStartHBL ) ) /* if nHBL was not visible, PrevSize = 0 */
373: {
374: LineBorderMask = ScreenBorderMask[ HblCounterVideo-1 ]; /* get border mask for nHBL */
375: PrevSize = BORDERBYTES_NORMAL; /* normal line */
376:
377: if (LineBorderMask & BORDERMASK_LEFT_OFF)
378: PrevSize += BORDERBYTES_LEFT;
379: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
380: PrevSize += 2;
381:
382: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
383: PrevSize -= 106;
384: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
385: PrevSize -= 2;
386: else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
387: PrevSize += BORDERBYTES_RIGHT;
388:
389: if (LineBorderMask & BORDERMASK_EMPTY_LINE)
390: PrevSize = 0;
391: }
392:
393:
394: LineBorderMask = ScreenBorderMask[ HblCounterVideo ];
395:
396: CurSize = BORDERBYTES_NORMAL; /* normal line */
397:
398: if (LineBorderMask & BORDERMASK_LEFT_OFF)
399: CurSize += BORDERBYTES_LEFT;
400: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
401: CurSize += 2;
402:
403: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
404: CurSize -= 106;
405: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
406: CurSize -= 2;
407: else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
408: CurSize += BORDERBYTES_RIGHT;
409: if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
410: CurSize += BORDERBYTES_RIGHT_FULL;
411:
412: if ( LineBorderMask & BORDERMASK_LEFT_PLUS_2)
413: LineStartCycle = LINE_START_CYCLE_60;
414: else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
415: LineStartCycle = LINE_START_CYCLE_70;
416:
417: LineEndCycle = LineStartCycle + CurSize*2;
418:
419:
420: if ( X < LineStartCycle )
421: X = LineStartCycle; /* display is disabled in the left border */
422: else if ( X > LineEndCycle )
423: X = LineEndCycle; /* display is disabled in the right border */
424:
425: NbBytes = ( (X-LineStartCycle)>>1 ) & (~1); /* 2 cycles per byte */
426:
427:
428: /* when left border is open, we have 2 bytes less than theorical value */
429: /* (26 bytes in left border, which is not a multiple of 4 cycles) */
430: if ( LineBorderMask & BORDERMASK_LEFT_OFF )
431: NbBytes -= 2;
432:
433: if ( LineBorderMask & BORDERMASK_EMPTY_LINE )
434: NbBytes = 0;
435:
436: /* Add line cycles if we have not reached end of screen yet: */
437: if (nFrameCycles < nEndHBL*nCyclesPerLine)
438: VideoAddress += PrevSize + NbBytes;
439: }
440:
441: HATARI_TRACE ( HATARI_TRACE_VIDEO_ADDR , "video base=%x raster=%x addr=%x video_cyc=%d line_cyc=%d/X=%d @ nHBL=%d/video_hbl=%d %d<->%d pc=%x instr_cyc=%d\n",
442: VideoBase, pVideoRaster - STRam, VideoAddress, Cycles_GetCounter(CYCLES_COUNTER_VIDEO),
443: Cycles_GetCounter(CYCLES_COUNTER_VIDEO) % nCyclesPerLine, X,
444: nHBL, HblCounterVideo, LineStartCycle, LineEndCycle, M68000_GetPC(), CurrentInstrCycles );
445:
446: return VideoAddress;
447: }
448:
449:
450: /*-----------------------------------------------------------------------*/
451: /**
452: * Write to VideoShifter (0xff8260), resolution bits
453: */
1.1.1.9 root 454: static void Video_WriteToShifter(Uint8 Byte)
1.1 root 455: {
1.1.1.11 root 456: static int nLastHBL = -1, nLastVBL = -1, nLastByte, nLastCycles, nLastFrameCycles;
457: int nFrameCycles, nLineCycles;
458: int HblCounterVideo;
459:
460: nFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
461:
462: /* We only care for cycle position in the actual screen line */
463: nLineCycles = nFrameCycles % nCyclesPerLine;
464:
465: HblCounterVideo = nFrameCycles / nCyclesPerLine;
466:
467: HATARI_TRACE ( HATARI_TRACE_VIDEO_RES ,"shifter=0x%2.2X video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n",
468: Byte, nFrameCycles, nLineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
469:
470: /* Remove left border : +26 bytes */
471: /* this can be done with a hi/lo res switch or a hi/med res switch */
472: if (nLastByte == 0x02 && Byte == 0x00
473: && nLineCycles <= (LINE_START_CYCLE_70+28)
474: && nFrameCycles-nLastFrameCycles <= 30)
475: {
476: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect remove left\n" );
477: LeftRightBorder |= BORDERMASK_LEFT;
478: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_LEFT_OFF;
479: LineStartCycle = LINE_START_CYCLE_70;
480: }
481:
482: if (nLastByte == 0x02 && Byte == 0x01
483: && nLineCycles <= (LINE_START_CYCLE_70+20)
484: && nFrameCycles-nLastFrameCycles <= 30)
485: {
486: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect remove left mid\n" );
487: LeftRightBorder |= BORDERMASK_LEFT;
488: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_LEFT_OFF_MID; /* a later switch to low res might gives right scrolling */
489: /* By default, this line will be in mid res, except if we detect hardware scrolling later */
490: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_OVERSCAN_MID_RES | ( 2 << 20 );
491: LineStartCycle = LINE_START_CYCLE_70;
492: }
493:
494: /* Empty line switching res */
495: else if ( ( nFrameCycles-nLastFrameCycles <= 16 )
496: && ( nLastCycles == LINE_EMPTY_CYCLE_70 ) )
497: {
498: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect empty line res\n" );
499: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_EMPTY_LINE;
500: LineStartCycle = LINE_START_CYCLE_60;
501: }
502:
503: /* Start right border near middle of the line : -106 bytes */
504: // if (nLastByte == 0x02 && Byte == 0x00
505: // && nFrameCycles-nLastFrameCycles <= 20
506: // && nLineCycles >= LINE_END_CYCLE_70 && nLineCycles <= (LINE_END_CYCLE_70+20) )
507: if ( ( nLastByte == 0x02 && Byte == 0x00 )
508: && ( nLastHBL == HblCounterVideo ) /* switch during the same line */
509: && ( nLastCycles <= LINE_END_CYCLE_70+4 ) /* switch to hi res before cycle 164 */
510: && ( nLineCycles >= LINE_END_CYCLE_70+4 ) ) /* switch to lo res after cycle 164 */
511: {
512: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect stop middle\n" );
513: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_STOP_MIDDLE;
514: LineEndCycle = LINE_END_CYCLE_70;
515: }
516:
517: /* Remove right border a second time after removing it a first time : */
518: /* this removes left border on next line too (used in 'Enchanted Lands')*/
519: if ( ScreenBorderMask[ HblCounterVideo ] & BORDERMASK_RIGHT_OFF
520: && nLastByte == 0x02 && Byte == 0x00
521: && nFrameCycles-nLastFrameCycles <= 20
522: && nLastCycles == LINE_END_CYCLE_50_2 )
523: {
524: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect remove right full\n" );
525: LeftRightBorder |= BORDERMASK_RIGHT; /* Program tries to open right border */
526: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_RIGHT_OFF_FULL;
527: ScreenBorderMask[ HblCounterVideo+1 ] |= BORDERMASK_LEFT_OFF; /* no left border on next line */
528: LineEndCycle = LINE_END_CYCLE_FULL;
529: }
530:
531: /* If left border is opened and we switch to medium resolution */
532: /* during the next cycles, then we assume a mid res overscan line */
533: /* instead of a low res overscan line */
534: /* Used in 'No Cooper' greetings by 1984 and 'Punish Your Machine' by Delta Force */
535: if ( ScreenBorderMask[ HblCounterVideo ] & BORDERMASK_LEFT_OFF
536: && Byte == 0x01 )
537: {
538: if ( nLineCycles == LINE_LEFT_MID_CYCLE_1 ) /* 'No Cooper' timing */
539: {
540: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect midres overscan offset 0 byte\n" );
541: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_OVERSCAN_MID_RES | ( 0 << 20 );
542: }
543: else if ( nLineCycles == LINE_LEFT_MID_CYCLE_2 ) /* 'Best Part Of The Creation / PYM' timing */
544: {
545: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect midres overscan offset 2 bytes\n" );
546: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_OVERSCAN_MID_RES | ( 2 << 20 );
547: }
548: }
549:
550: /* If left border was opened with a hi/mid res switch */
551: /* we need to check if the switch to low res can trigger */
552: /* a right hardware scrolling. */
553: /* We store the pixels count in the upper 16 bits */
554: if ( ScreenBorderMask[ HblCounterVideo ] & BORDERMASK_LEFT_OFF_MID
555: && Byte == 0x00 && nLineCycles <= LINE_SCROLL_1_CYCLE_50 )
556: {
557: /* The hi/mid switch was a switch to do low res hardware scrolling, */
558: /* so we must cancel the mid res overscan bit. */
559: ScreenBorderMask[ HblCounterVideo ] &= (~BORDERMASK_OVERSCAN_MID_RES);
560:
561: if ( nLineCycles == LINE_SCROLL_13_CYCLE_50 ) /* cycle 20 */
562: {
563: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll\n" );
564: ScreenBorderMask[ HblCounterVideo ] |= ( 13 << 16 );
565: }
566: else if ( nLineCycles == LINE_SCROLL_9_CYCLE_50 ) /* cycle 24 */
567: {
568: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll\n" );
569: ScreenBorderMask[ HblCounterVideo ] |= ( 9 << 16 );
570: }
571: else if ( nLineCycles == LINE_SCROLL_5_CYCLE_50 ) /* cycle 28 */
572: {
573: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll\n" );
574: ScreenBorderMask[ HblCounterVideo ] |= ( 5 << 16 );
575: }
576: else if ( nLineCycles == LINE_SCROLL_1_CYCLE_50 ) /* cycle 32 */
577: {
578: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll\n" );
579: ScreenBorderMask[ HblCounterVideo ] |= ( 1 << 16 );
580: }
581: }
582:
583: nLastVBL = nVBLs;
584: nLastHBL = HblCounterVideo;
585: nLastByte = Byte;
586: nLastCycles = nLineCycles;
587: nLastFrameCycles = nFrameCycles;
588: }
589:
590:
591: /*-----------------------------------------------------------------------*/
592: /**
593: * Write to VideoSync (0xff820a), Hz setting
594: */
1.1.1.8 root 595: void Video_Sync_WriteByte(void)
1.1 root 596: {
1.1.1.11 root 597: static int nLastHBL = -1, nLastVBL = -1, nLastByte, nLastCycles, nLastFrameCycles;
598: int nFrameCycles, nLineCycles;
599: int HblCounterVideo;
600: Uint8 Byte;
601:
602: /* We're only interested in lower 2 bits (50/60Hz) */
603: Byte = IoMem[0xff820a] & 2; /* only keep bit 1 (50/60 Hz) */
604:
605: nFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
606:
607: /* We only care for cycle position in the actual screen line */
608: nLineCycles = nFrameCycles % nCyclesPerLine;
609:
610: HblCounterVideo = nFrameCycles / nCyclesPerLine;
611:
612: HATARI_TRACE ( HATARI_TRACE_VIDEO_SYNC ,"sync=0x%2.2X video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n",
613: Byte, nFrameCycles, nLineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
614:
615:
616: if ( ( nLastByte == 0x00 ) && ( Byte == 0x02 )/* switched from 60 Hz to 50 Hz? */
617: && ( nLastVBL == nVBLs ) ) /* switched during the same VBL */
618: {
619: /* Add 2 bytes to left border */
620: // if ( nFrameCycles-nLastFrameCycles <= 24
621: // && nLastCycles <= LINE_START_CYCLE_60 && nLineCycles >= LINE_START_CYCLE_50 )
622: if ( ( LastCycleSync60 <= HblCounterVideo * nCyclesPerLine + LINE_START_CYCLE_60 )
623: && ( nLineCycles >= LINE_START_CYCLE_50 ) /* The line started in 60 Hz and continues in 50 Hz */
624: && ( nLineCycles <= LINE_END_CYCLE_60 ) /* change when line is active */
625: && ( ( ScreenBorderMask[ HblCounterVideo ] & ( BORDERMASK_LEFT_OFF | BORDERMASK_LEFT_OFF_MID ) ) == 0 ) )
626: {
627: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect left+2\n" );
628: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_LEFT_PLUS_2;
629: LineStartCycle = LINE_START_CYCLE_60;
630: }
631:
632: /* Empty line switching freq */
633: else if ( ( nFrameCycles-nLastFrameCycles <= 24 )
634: && ( nLastCycles == LINE_START_CYCLE_50 ) && ( nLineCycles > LINE_START_CYCLE_50 ) )
635: {
636: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect empty line freq\n" );
637: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_EMPTY_LINE;
638: LineStartCycle = LINE_START_CYCLE_60;
639: }
640:
641: /* Remove 2 bytes to the right */
642: // else if ( nFrameCycles-nLastFrameCycles <= 128
643: // && nLastCycles <= LINE_END_CYCLE_60 && nLineCycles > LINE_END_CYCLE_60
644: if ( ( nLineCycles > LINE_END_CYCLE_60 )
645: && ( ( nLastCycles > LINE_START_CYCLE_60 ) && ( nLastCycles <= LINE_END_CYCLE_60 ) )
646: && ( nLastHBL == HblCounterVideo )
647: && ( ( ScreenBorderMask[ HblCounterVideo ] & BORDERMASK_STOP_MIDDLE ) == 0 ) )
648: {
649: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect right-2\n" );
650: LeftRightBorder |= BORDERMASK_MIDDLE; /* Program tries to shorten line by 2 bytes */
651: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_RIGHT_MINUS_2;
652: LineEndCycle = LINE_END_CYCLE_60;
653: }
654: }
655:
656: /* special case for right border : some programs don't switch back to */
657: /* 50 Hz immediatly (sync screen in SNY II), so we just check if */
658: /* freq changes to 60 Hz at the position where line should end in 50 Hz */
659: if ( ( nLastByte == 0x02 && Byte == 0x00 ) /* switched from 50 Hz to 60 Hz? */
660: && ( HblCounterVideo >= nStartHBL ) ) /* only if display is on */
661: {
662: if ( ( nLineCycles == LINE_END_CYCLE_50 )
663: && ( ( ScreenBorderMask[ HblCounterVideo ] & BORDERMASK_STOP_MIDDLE ) == 0 ) )
664: {
665: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect remove right\n" );
666: LeftRightBorder |= BORDERMASK_RIGHT; /* Program tries to open right border */
667: ScreenBorderMask[ HblCounterVideo ] |= BORDERMASK_RIGHT_OFF;
668: LineEndCycle = LINE_END_CYCLE_NO_RIGHT;
669: }
670: }
671:
672:
673: /* Store cycle position of freq 50/60 to check for top/bottom border removal in Video_EndHBL. */
674: /* Also update start/end line depending on the current value of nHBL */
675: if ( Byte == 0x02 ) /* switch to 50 Hz */
676: {
677: LastCycleSync50 = nFrameCycles;
678:
679: if ( ( nHBL < SCREEN_START_HBL_50HZ ) /* nStartHBL can change only if display is not ON yet */
680: && ( OverscanMode & OVERSCANMODE_TOP ) == 0 ) /* update only if top was not removed */
681: nStartHBL = SCREEN_START_HBL_50HZ;
682:
683: if ( ( nHBL < SCREEN_END_HBL_50HZ ) /* nEndHBL can change only if display is not OFF yet */
684: && ( OverscanMode & OVERSCANMODE_BOTTOM ) == 0 ) /* update only if bottom was not removed */
685: nEndHBL = SCREEN_END_HBL_50HZ; /* 263 */
686: }
687: else if ( Byte == 0x00 ) /* switch to 60 Hz */
688: {
689: LastCycleSync60 = nFrameCycles;
690:
691: if ( nHBL < SCREEN_START_HBL_60HZ ) /* nStartHBL can change only if display is not ON yet */
692: nStartHBL = SCREEN_START_HBL_60HZ;
693:
694: if ( ( nHBL < SCREEN_END_HBL_60HZ ) /* nEndHBL can change only if display is not OFF yet */
695: && ( OverscanMode & OVERSCANMODE_BOTTOM ) == 0 ) /* update only if bottom was not removed */
696: nEndHBL = SCREEN_END_HBL_60HZ; /* 234 */
697: }
698:
699: nLastVBL = nVBLs;
700: nLastHBL = HblCounterVideo;
701: nLastByte = Byte;
702: nLastCycles = nLineCycles;
703: nLastFrameCycles = nFrameCycles;
704: }
705:
706:
707: /*-----------------------------------------------------------------------*/
708: /**
709: * Reset Sync/Shifter table at start of each HBL
710: */
1.1.1.9 root 711: static void Video_StartHBL(void)
1.1 root 712: {
1.1.1.11 root 713: int nSyncByte;
714:
715: LeftRightBorder = BORDERMASK_NONE;
716:
717: nSyncByte = IoMem_ReadByte(0xff820a);
718: if (nSyncByte & 2) /* 50 Hz */
719: {
720: LineStartCycle = LINE_START_CYCLE_50;
721: LineEndCycle = LINE_END_CYCLE_50;
722: }
723: else /* 60 Hz */
724: {
725: LineStartCycle = LINE_START_CYCLE_60;
726: LineEndCycle = LINE_END_CYCLE_60;
727: }
1.1 root 728: }
729:
1.1.1.2 root 730:
731: /*-----------------------------------------------------------------------*/
1.1.1.11 root 732: /**
733: * Store whole palette on first line so have reference to work from
734: */
1.1.1.7 root 735: static void Video_StoreFirstLinePalette(void)
1.1 root 736: {
1.1.1.11 root 737: Uint16 *pp2;
738: int i;
739:
740: pp2 = (Uint16 *)&IoMem[0xff8240];
741: for (i = 0; i < 16; i++)
742: HBLPalettes[i] = SDL_SwapBE16(*pp2++);
1.1 root 743:
1.1.1.11 root 744: /* And set mask flag with palette and resolution */
745: // FIXME ; enlever PALETTEMASK_RESOLUTION
746:
747: // if ( ScreenBorderMask[ nFirstVisibleHbl ] == BORDERMASK_NONE ) // no border trick, store the current res
748: HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((Uint32)IoMem_ReadByte(0xff8260)&0x3)<<16);
749: // else // border removal, assume low res for the whole line
750: // HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (0<<16);
1.1 root 751: }
752:
1.1.1.2 root 753:
754: /*-----------------------------------------------------------------------*/
1.1.1.11 root 755: /**
756: * Store resolution on each line (used to test if mixed low/medium resolutions)
757: */
1.1.1.7 root 758: static void Video_StoreResolution(int y)
1.1 root 759: {
1.1.1.11 root 760: Uint8 res;
761: int Mask;
1.1 root 762:
1.1.1.11 root 763: /* Clear resolution, and set with current value */
764: if (!(bUseHighRes || bUseVDIRes))
765: {
766: HBLPaletteMasks[y] &= ~(0x3<<16);
767: res = IoMem_ReadByte(0xff8260)&0x3;
768:
769: Mask = ScreenBorderMask[ y+nFirstVisibleHbl ];
770:
771: if ( Mask & BORDERMASK_OVERSCAN_MID_RES ) /* special case for mid res to render the overscan line */
772: res = 1; /* mid res instead of low res */
773: else if ( Mask != BORDERMASK_NONE ) /* border removal : assume low res for the whole line */
774: res = 0;
1.1.1.2 root 775:
1.1.1.11 root 776: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)res)<<16;
777:
778: #if 0
779: if ( ( Mask == BORDERMASK_NONE ) /* no border trick, store the current res */
780: || ( res == 0 ) || ( res == 1 ) ) /* if border trick, ignore passage to hi res */
781: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)res)<<16;
782: else /* border removal or hi res : assume low res for the whole line */
783: HBLPaletteMasks[y] |= (0)<<16;
784:
785: /* special case for mid res to render the overscan line */
786: if ( Mask & BORDERMASK_OVERSCAN_MID_RES )
787: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)1)<<16; /* mid res instead of low res */
788: #endif
1.1 root 789:
1.1.1.11 root 790: // fprintf ( stderr , "store res %d line %d %x %x\n" , res , y , Mask , HBLPaletteMasks[y] );
791: }
1.1.1.9 root 792: }
793:
794:
795: /*-----------------------------------------------------------------------*/
1.1.1.11 root 796: /**
797: * Copy one line of monochrome screen into buffer for conversion later.
798: */
799: static void Video_CopyScreenLineMono(void)
800: {
801: Uint32 addr;
802:
803: /* Copy one line - 80 bytes in ST high resolution */
804: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_MONOLINE);
805: pVideoRaster += SCREENBYTES_MONOLINE;
806:
807: /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
808: if (HWScrollCount)
809: {
810: Uint16 *pScrollAdj;
811: int nNegScrollCnt;
812:
813: pScrollAdj = (Uint16 *)pSTScreen;
814: nNegScrollCnt = 16 - HWScrollCount;
815:
816: /* Shift the whole line by the given scroll count */
817: while ((Uint8*)pScrollAdj < pSTScreen + SCREENBYTES_MONOLINE-2)
818: {
819: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
820: | (do_get_mem_word(pScrollAdj+1) >> nNegScrollCnt));
821: ++pScrollAdj;
822: }
823:
824: /* Handle the last 16 pixels of the line */
825: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
826: | (do_get_mem_word(pVideoRaster) >> nNegScrollCnt));
827:
828: /* HW scrolling advances Shifter video counter by one */
829: pVideoRaster += 1 * 2;
830: }
831:
832: /* LineWidth is zero on ST. */
833: /* On STE, the Shifter skips the given amount of words. */
834: pVideoRaster += LineWidth*2;
835:
836: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
837: /* that occurred while the display was already ON */
838: if ( NewVideoHi >= 0 )
839: {
840: addr = ( ( pVideoRaster - STRam ) & 0x00ffff ) | ( NewVideoHi << 16 );
841: pVideoRaster = &STRam[addr & ~1];
842: NewVideoHi = -1;
843: }
844: if ( NewVideoMed >= 0 )
845: {
846: addr = ( ( pVideoRaster - STRam ) & 0xff00ff ) | ( NewVideoMed << 8 );
847: pVideoRaster = &STRam[addr & ~1];
848: NewVideoMed = -1;
849: }
850: if ( NewVideoLo >= 0 )
851: {
852: addr = ( ( pVideoRaster - STRam ) & 0xffff00 ) | ( NewVideoLo );
853: pVideoRaster = &STRam[addr & ~1];
854: NewVideoLo = -1;
855: }
856:
857: /* On STE, if we wrote to the hwscroll register, we set the */
858: /* new value here, once the current line was processed */
859: if ( NewHWScrollCount >= 0 )
860: {
861: HWScrollCount = NewHWScrollCount;
862: NewHWScrollCount = -1;
863: }
864:
865: /* On STE, if we wrote to the linewidth register, we set the */
866: /* new value here, once the current line was processed */
867: if ( NewLineWidth >= 0 )
868: {
869: LineWidth = NewLineWidth;
870: NewLineWidth = -1;
871: }
872:
873: /* Each screen line copied to buffer is always same length */
874: pSTScreen += SCREENBYTES_MONOLINE;
875: }
876:
877:
878: /*-----------------------------------------------------------------------*/
879: /**
880: * Copy one line of color screen into buffer for conversion later.
881: * Possible lines may be top/bottom border, and/or left/right borders.
882: */
1.1.1.9 root 883: static void Video_CopyScreenLineColor(void)
884: {
1.1.1.11 root 885: int LineBorderMask = ScreenBorderMask[ nHBL ];
886: int VideoOffset = 0;
887: int STF_PixelScroll = 0;
888: Uint32 addr;
889:
890: //fprintf(stderr , "copy line %d start %d end %d %d %x\n" , nHBL, nStartHBL, nEndHBL, LineBorderMask, pVideoRaster - STRam);
891:
892: /* If left border is opened, we need to compensate one missing word in low res (1 plan) */
893: /* If overscan is in mid res, the offset is variable */
894: if ( LineBorderMask & BORDERMASK_OVERSCAN_MID_RES )
895: VideoOffset = - ( ( LineBorderMask >> 20 ) & 0x0f ); /* No Cooper=0 PYM=-2 in mid res overscan */
896:
897: else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
898: VideoOffset = -2; /* always 2 bytes in low res overscan */
899:
900: /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
901: /* Depending on the number of pixels, we need to compensate for some skipped words */
902: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_MID )
903: {
904: STF_PixelScroll = ( LineBorderMask >> 16 ) & 0x0f;
905:
906: if ( STF_PixelScroll == 13 ) VideoOffset = 2+8;
907: else if ( STF_PixelScroll == 9 ) VideoOffset = 0+8;
908: else if ( STF_PixelScroll == 5 ) VideoOffset = -2+8;
909: else if ( STF_PixelScroll == 1 ) VideoOffset = -4+8;
910: else VideoOffset = 0; /* never used ? */
911:
912: // fprintf(stderr , "scr off %d %d\n" , STF_PixelScroll , VideoOffset);
913: }
914:
915: /* Is total blank line? I.e. top/bottom border? */
916: if ((nHBL < nStartHBL) || (nHBL >= nEndHBL)
917: || (LineBorderMask & BORDERMASK_EMPTY_LINE)) /* 60/50 Hz trick to obtain an empty line */
918: {
919: /* Clear line to color '0' */
920: memset(pSTScreen, 0, SCREENBYTES_LINE);
921: }
922: else
923: {
924: /* Does have left border? If not, clear to color '0' */
925: if ((LineBorderMask & BORDERMASK_LEFT_OFF)
926: || (LineBorderMask & BORDERMASK_LEFT_OFF_MID))
927: {
928: /* The "-2" in the following line is needed so that the offset is a multiple of 8 */
929: pVideoRaster += BORDERBYTES_LEFT-SCREENBYTES_LEFT+VideoOffset;
930: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_LEFT);
931: pVideoRaster += SCREENBYTES_LEFT;
932: }
933: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
934: {
1.1.1.12! root 935: /* bigger line by 2 bytes on the left */
! 936: memset(pSTScreen,0,SCREENBYTES_LEFT-2); /* clear unused pixels */
1.1.1.11 root 937: memcpy(pSTScreen+SCREENBYTES_LEFT-2, pVideoRaster, 2);
938: pVideoRaster += 2;
939: }
1.1.1.12! root 940: else if (bSteBorderFlag) /* STE specific */
! 941: {
! 942: /* bigger line by 8 bytes on the left */
! 943: memset(pSTScreen,0,SCREENBYTES_LEFT-4*2); /* clear unused pixels */
! 944: memcpy(pSTScreen+SCREENBYTES_LEFT-4*2, pVideoRaster, 4*2);
! 945: pVideoRaster += 4*2;
! 946: }
1.1.1.11 root 947: else
948: memset(pSTScreen,0,SCREENBYTES_LEFT);
949:
950: /* Short line due to hires in the middle ? */
951: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
952: {
953: /* 106 bytes less in the line */
954: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE-106);
1.1.1.12! root 955: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-106, 0, 106); /* clear unused pixels */
1.1.1.11 root 956: pVideoRaster += (SCREENBYTES_MIDDLE-106);
957: }
958: else
959: {
960: /* normal middle part (160 bytes) */
961: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE);
962: pVideoRaster += SCREENBYTES_MIDDLE;
963: }
964:
965: /* Does have right border ? */
966: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
967: {
968: memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE, pVideoRaster, SCREENBYTES_RIGHT);
969: pVideoRaster += BORDERBYTES_RIGHT-SCREENBYTES_RIGHT;
970: pVideoRaster += SCREENBYTES_RIGHT;
971: }
972: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
973: {
974: /* Shortened line by 2 bytes */
975: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-2, 0, SCREENBYTES_RIGHT+2);
976: pVideoRaster -= 2;
977: }
978: else
979: {
980: /* Simply clear right border to '0' */
981: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
982: }
983:
984: /* Full right border removal up to the end of the line (cycle 512) */
985: if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
986: pVideoRaster += BORDERBYTES_RIGHT_FULL;
987:
988: /* Correct the offset for pVideoRaster from BORDERMASK_LEFT_OFF above if needed */
989: pVideoRaster -= VideoOffset; /* VideoOffset is 0 or -2 */
990:
991:
992: /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
993: /* Shift the line by STF_PixelScroll pixels to the right (we don't need to scroll */
994: /* the first 16 pixels / 8 bytes). */
995: if (STF_PixelScroll != 0)
996: {
997: Uint16 *pScreenLineEnd;
998: int count;
999:
1000: pScreenLineEnd = (Uint16 *) ( pSTScreen + SCREENBYTES_LINE - 2 );
1001: for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineEnd-- )
1002: do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 4 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
1003: }
1004:
1005:
1006: /* STE specific */
1.1.1.12! root 1007: if ( !bSteBorderFlag && HWScrollCount) /* Handle STE fine scrolling (HWScrollCount is zero on ST) */
1.1.1.11 root 1008: {
1009: Uint16 *pScrollAdj; /* Pointer to actual position in line */
1010: int nNegScrollCnt;
1011: Uint16 *pScrollEndAddr; /* Pointer to end of the line */
1012:
1013: nNegScrollCnt = 16 - HWScrollCount;
1014: if (LineBorderMask & BORDERMASK_LEFT_OFF)
1015: pScrollAdj = (Uint16 *)pSTScreen;
1016: else
1017: pScrollAdj = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT);
1018: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
1019: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LINE - 8);
1020: else
1021: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8);
1022:
1023: if (STRes == ST_MEDIUM_RES)
1024: {
1025: /* TODO: Implement fine scrolling for medium resolution, too */
1026: /* HW scrolling advances Shifter video counter by one */
1027: pVideoRaster += 2 * 2;
1028: }
1029: else
1030: {
1031: /* Shift the whole line to the left by the given scroll count */
1032: while (pScrollAdj < pScrollEndAddr)
1033: {
1034: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
1035: | (do_get_mem_word(pScrollAdj+4) >> nNegScrollCnt));
1036: ++pScrollAdj;
1037: }
1038: /* Handle the last 16 pixels of the line */
1039: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
1040: {
1041: /* When right border is open, we have to deal with this ugly offset
1042: * of 46-SCREENBYTES_RIGHT - The demo "Mind rewind" is a good example */
1043: Uint16 *pVideoLineEnd = (Uint16 *)(pVideoRaster - (46 - SCREENBYTES_RIGHT));
1044: do_put_mem_word(pScrollAdj+0, (do_get_mem_word(pScrollAdj+0) << HWScrollCount)
1045: | (do_get_mem_word(pVideoLineEnd++) >> nNegScrollCnt));
1046: do_put_mem_word(pScrollAdj+1, (do_get_mem_word(pScrollAdj+1) << HWScrollCount)
1047: | (do_get_mem_word(pVideoLineEnd++) >> nNegScrollCnt));
1048: do_put_mem_word(pScrollAdj+2, (do_get_mem_word(pScrollAdj+2) << HWScrollCount)
1049: | (do_get_mem_word(pVideoLineEnd++) >> nNegScrollCnt));
1050: do_put_mem_word(pScrollAdj+3, (do_get_mem_word(pScrollAdj+3) << HWScrollCount)
1051: | (do_get_mem_word(pVideoLineEnd++) >> nNegScrollCnt));
1052: }
1053: else
1054: {
1055: do_put_mem_word(pScrollAdj+0, (do_get_mem_word(pScrollAdj+0) << HWScrollCount)
1056: | (do_get_mem_word(pVideoRaster+0) >> nNegScrollCnt));
1057: do_put_mem_word(pScrollAdj+1, (do_get_mem_word(pScrollAdj+1) << HWScrollCount)
1058: | (do_get_mem_word(pVideoRaster+2) >> nNegScrollCnt));
1059: do_put_mem_word(pScrollAdj+2, (do_get_mem_word(pScrollAdj+2) << HWScrollCount)
1060: | (do_get_mem_word(pVideoRaster+4) >> nNegScrollCnt));
1061: do_put_mem_word(pScrollAdj+3, (do_get_mem_word(pScrollAdj+3) << HWScrollCount)
1062: | (do_get_mem_word(pVideoRaster+6) >> nNegScrollCnt));
1063: }
1064: /* HW scrolling advances Shifter video counter by one */
1065: pVideoRaster += 4 * 2;
1066:
1067: /* On STE, when we have a 230 bytes overscan line and HWScrollCount > 0 */
1068: /* we must read 6 bytes less than expected */
1069: if ( (LineBorderMask & BORDERMASK_LEFT_OFF) && (LineBorderMask & BORDERMASK_RIGHT_OFF) )
1070: pVideoRaster -= 6; /* we don't add 8 bytes, but 2 */
1071:
1072: }
1073: }
1074:
1075: /* LineWidth is zero on ST. */
1076: /* On STE, the Shifter skips the given amount of words. */
1077: pVideoRaster += LineWidth*2;
1078:
1079: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
1080: /* that occurred while the display was already ON */
1081: if ( NewVideoHi >= 0 )
1082: {
1083: addr = ( ( pVideoRaster - STRam ) & 0x00ffff ) | ( NewVideoHi << 16 );
1084: pVideoRaster = &STRam[addr & ~1];
1085: NewVideoHi = -1;
1086: }
1087: if ( NewVideoMed >= 0 )
1088: {
1089: addr = ( ( pVideoRaster - STRam ) & 0xff00ff ) | ( NewVideoMed << 8 );
1090: pVideoRaster = &STRam[addr & ~1];
1091: NewVideoMed = -1;
1092: }
1093: if ( NewVideoLo >= 0 )
1094: {
1095: addr = ( ( pVideoRaster - STRam ) & 0xffff00 ) | ( NewVideoLo );
1096: pVideoRaster = &STRam[addr & ~1];
1097: NewVideoLo = -1;
1098: }
1099:
1100: /* On STE, if we wrote to the hwscroll register, we set the */
1101: /* new value here, once the current line was processed */
1102: if ( NewHWScrollCount >= 0 )
1103: {
1104: HWScrollCount = NewHWScrollCount;
1105: NewHWScrollCount = -1;
1106: }
1107:
1108: /* On STE, if we wrote to the linewidth register, we set the */
1109: /* new value here, once the current line was processed */
1110: if ( NewLineWidth >= 0 )
1111: {
1112: LineWidth = NewLineWidth;
1113: NewLineWidth = -1;
1114: }
1115: }
1116:
1117: /* Each screen line copied to buffer is always same length */
1118: pSTScreen += SCREENBYTES_LINE;
1119: }
1120:
1121:
1122: /*-----------------------------------------------------------------------*/
1123: /**
1124: * Copy extended GEM resolution screen
1125: */
1.1.1.9 root 1126: static void Video_CopyVDIScreen(void)
1.1 root 1127: {
1.1.1.11 root 1128: /* Copy whole screen, don't care about being exact as for GEM only */
1129: memcpy(pSTScreen, pVideoRaster, ((VDIWidth*VDIPlanes)/8)*VDIHeight);
1.1 root 1130: }
1131:
1.1.1.2 root 1132:
1133: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1134: /**
1135: * Check at end of each HBL to see if any Sync/Shifter hardware tricks have been attempted
1136: * This is the place to check if top/bottom border were removed.
1137: * NOTE : the tests must be made with nHBL in ascending order.
1138: */
1.1.1.9 root 1139: static void Video_EndHBL(void)
1.1 root 1140: {
1.1.1.11 root 1141: Uint8 SyncByte = IoMem_ReadByte(0xff820a) & 2; /* only keep bit 1 (50/60 Hz) */
1142:
1143: // fprintf(stderr,"video_endhbl %d last60 %d last 50 %d\n", nHBL, LastCycleSync60, LastCycleSync50);
1144:
1145: /* Remove top border if the switch to 60 Hz was made during this vbl before cycle */
1.1.1.12! root 1146: /* 33*512+LineRemoveTopCycle and if the switch to 50 Hz has not yet occured or */
! 1147: /* occured before the 60 Hz or occured after cycle 33*512+LineRemoveTopCycle. */
1.1.1.11 root 1148: if (( nHBL == SCREEN_START_HBL_60HZ-1)
1.1.1.12! root 1149: && ((LastCycleSync60 >= 0) && (LastCycleSync60 <= (SCREEN_START_HBL_60HZ-1) * nCyclesPerLine + LineRemoveTopCycle))
! 1150: && ((LastCycleSync50 < LastCycleSync60) || (LastCycleSync50 > (SCREEN_START_HBL_60HZ-1) * nCyclesPerLine + LineRemoveTopCycle)))
1.1.1.11 root 1151: {
1152: /* Top border */
1153: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_V , "detect remove top\n" );
1154: OverscanMode |= OVERSCANMODE_TOP; /* Set overscan bit */
1155: nStartHBL = SCREEN_START_HBL_60HZ; /* New start screen line */
1156: pHBLPaletteMasks -= OVERSCAN_TOP; // FIXME useless ?
1157: pHBLPalettes -= OVERSCAN_TOP; // FIXME useless ?
1158: }
1159:
1160: /* Remove bottom border for a 60 Hz screen */
1161: else if ((nHBL == SCREEN_END_HBL_60HZ-1) /* last displayed line in 60 Hz */
1162: && (SyncByte == 0x02) /* current freq is 50 Hz */
1163: && (LastCycleSync50 >= 0) /* change occurred during this VBL */
1164: && (nStartHBL == SCREEN_START_HBL_60HZ) /* screen starts in 60 Hz */
1165: && ((OverscanMode & OVERSCANMODE_TOP) == 0)) /* and top border was not removed : this screen is only 60 Hz */
1166: {
1167: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_V , "detect remove bottom 60Hz\n" );
1168: OverscanMode |= OVERSCANMODE_BOTTOM;
1169: nEndHBL = SCANLINES_PER_FRAME_60HZ; /* new end for a 60 Hz screen */
1170: }
1171:
1172: /* Remove bottom border for a 50 Hz screen (similar method to the one for top border) */
1173: else if ((nHBL == SCREEN_END_HBL_50HZ-1) /* last displayed line in 50 Hz */
1.1.1.12! root 1174: && ((LastCycleSync60 >= 0) && (LastCycleSync60 <= (SCREEN_END_HBL_50HZ-1) * nCyclesPerLine + LineRemoveBottomCycle))
! 1175: && ((LastCycleSync50 < LastCycleSync60) || (LastCycleSync50 > (SCREEN_END_HBL_50HZ-1) * nCyclesPerLine + LineRemoveBottomCycle))
1.1.1.11 root 1176: && ((OverscanMode & OVERSCANMODE_BOTTOM) == 0)) /* border was not already removed at line SCREEN_END_HBL_60HZ */
1177: {
1178: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_V , "detect remove bottom\n" );
1179: OverscanMode |= OVERSCANMODE_BOTTOM;
1180: nEndHBL = SCREEN_END_HBL_50HZ+OVERSCAN_BOTTOM; /* new end for a 50 Hz screen */
1181:
1182: /* Some programs turn to 60 Hz during the active display of the last line to */
1183: /* remove the bottom border (FNIL by TNT), in that case, we should also remove */
1184: /* 2 bytes to this line (this is wrong practice as it can distort the display on a real ST) */
1185: if (LastCycleSync60 <= (SCREEN_END_HBL_50HZ-1) * nCyclesPerLine + LINE_END_CYCLE_60)
1186: {
1187: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect right-2\n" );
1188: LeftRightBorder |= BORDERMASK_MIDDLE; /* Program tries to shorten line by 2 bytes */
1189: ScreenBorderMask[ nHBL ] |= BORDERMASK_RIGHT_MINUS_2;
1190: LineEndCycle = LINE_END_CYCLE_60;
1191: }
1192: }
1193:
1194: /* Store palette for very first line on screen - HBLPalettes[0] */
1195: if (nHBL == nFirstVisibleHbl)
1196: {
1197: /* Store ALL palette for this line into raster table for datum */
1198: Video_StoreFirstLinePalette();
1199: }
1200:
1201: if (!bUseVDIRes)
1202: {
1203: if (bUseHighRes)
1204: {
1205: /* Copy for hi-res (no overscan) */
1206: if (nHBL >= nFirstVisibleHbl && nHBL < nFirstVisibleHbl+SCREEN_HEIGHT_HBL_MONO)
1207: Video_CopyScreenLineMono();
1208: }
1209: /* Are we in possible visible color display (including borders)? */
1210: else if (nHBL >= nFirstVisibleHbl && nHBL < nFirstVisibleHbl+NUM_VISIBLE_LINES)
1211: {
1212: /* Copy line of screen to buffer to simulate TV raster trace
1213: * - required for mouse cursor display/game updates
1214: * Eg, Lemmings and The Killing Game Show are good examples */
1215: Video_CopyScreenLineColor();
1216:
1217: /* Store resolution for every line so can check for mix low/med screens */
1218: Video_StoreResolution(nHBL-nFirstVisibleHbl);
1219: }
1220: }
1221:
1222: /* Finally increase HBL count */
1223: nHBL++;
1224:
1225: Video_StartHBL(); /* Setup next one */
1226: }
1227:
1228:
1229: /*-----------------------------------------------------------------------*/
1230: /**
1231: * HBL interrupt : this occurs at the end of every line, on cycle 512 (in 50 Hz)
1232: * It takes 56 cycles to handle the 68000's exception.
1233: */
1234: void Video_InterruptHandler_HBL(void)
1235: {
1236: HATARI_TRACE ( HATARI_TRACE_VIDEO_HBL , "HBL %d video_cyc=%d pending_int_cnt=%d\n" ,
1237: nHBL , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE ) );
1.1.1.10 root 1238:
1.1.1.11 root 1239: /* Remove this interrupt from list and re-order */
1240: Int_AcknowledgeInterrupt();
1241:
1242: /* Generate new HBL, if need to - there are 313 HBLs per frame in 50 Hz */
1243: if (nHBL < nScanlinesPerFrame-1)
1244: Int_AddAbsoluteInterrupt(nCyclesPerLine, INT_CPU_CYCLE, INTERRUPT_VIDEO_HBL);
1245:
1246: M68000_Exception ( EXCEPTION_HBLANK , M68000_INT_VIDEO ); /* Horizontal blank interrupt, level 2! */
1247:
1248: Video_EndHBL(); /* Increase HBL count, copy line to display buffer and do any video trickery */
1.1 root 1249: }
1250:
1.1.1.2 root 1251:
1.1.1.9 root 1252: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1253: /**
1254: * End Of Line interrupt
1255: * As this occurs at the end of a line we cannot get timing for START of first
1256: * line (as in Spectrum 512)
1257: */
1.1.1.9 root 1258: void Video_InterruptHandler_EndLine(void)
1259: {
1.1.1.11 root 1260: /* Remove this interrupt from list and re-order */
1261: Int_AcknowledgeInterrupt();
1262: /* Generate new Endline, if need to - there are 313 HBLs per frame */
1263: if (nHBL < nScanlinesPerFrame-1)
1264: Int_AddAbsoluteInterrupt(nCyclesPerLine, INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE);
1265:
1266: /* Is this a good place to send the keyboard packets? Done once per frame */
1267: if (nHBL == nStartHBL)
1268: {
1269: /* On each VBL send automatic keyboard packets for mouse, joysticks etc... */
1270: IKBD_SendAutoKeyboardCommands();
1271: }
1272:
1273: /* Timer B occurs at END of first visible screen line in Event Count mode */
1274: if (nHBL >= nStartHBL && nHBL < nEndHBL)
1275: {
1276: /* Handle Timer B when using Event Count mode */
1277: if (MFP_TBCR == 0x08) /* Is timer in Event Count mode? */
1278: MFP_TimerB_EventCount_Interrupt();
1279: }
1280:
1281: //Video_EndHBL(); /* now done in Video_InterruptHandler_HBL */
1282:
1283: /* If we don't often pump data into the event queue, the SDL misses events... grr... */
1284: if (!(nHBL & 63))
1285: {
1286: Main_EventHandler();
1287: }
1.1.1.9 root 1288: }
1289:
1290:
1291: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1292: /**
1293: * Clear raster line table to store changes in palette/resolution on a line
1294: * basic. Called once on VBL interrupt.
1295: */
1.1 root 1296: void Video_SetScreenRasters(void)
1297: {
1.1.1.11 root 1298: pHBLPaletteMasks = HBLPaletteMasks;
1299: pHBLPalettes = HBLPalettes;
1300: memset(pHBLPaletteMasks, 0, sizeof(Uint32)*NUM_VISIBLE_LINES); /* Clear array */
1.1 root 1301: }
1302:
1.1.1.2 root 1303:
1304: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1305: /**
1306: * Set pointers to HBLPalette tables to store correct colours/resolutions
1307: */
1.1.1.9 root 1308: static void Video_SetHBLPaletteMaskPointers(void)
1.1 root 1309: {
1.1.1.11 root 1310: int FrameCycles;
1311: int Line;
1312: int Cycle;
1313:
1314: /* FIXME [NP] We should use Cycles_GetCounterOnWriteAccess, but it wouldn't */
1315: /* work when using multiple accesses instructions like move.l or movem */
1316: /* To correct this, assume a delay of 8 cycles (should give a good approximation */
1317: /* of a move.w or movem.l for example) */
1318: // FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
1319: FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
1320:
1321: /* Find 'line' into palette - screen starts 63 lines down, less 29 for top overscan */
1322: Line = (FrameCycles-(nFirstVisibleHbl*nCyclesPerLine)+SCREEN_START_CYCLE)/nCyclesPerLine;
1323: // Line = ( FrameCycles / nCyclesPerLine ) - nFirstVisibleHbl;
1324: Cycle = FrameCycles % nCyclesPerLine;
1325:
1326: /* FIXME [NP] if the color change occurs after the last visible pixel of a line */
1327: /* we consider the palette should be modified on the next line. This is quite */
1328: /* a hack, we should handle all color changes through spec512.c to have cycle */
1329: /* accuracy all the time. */
1330: // if ( Cycle >= LINE_END_CYCLE_NO_RIGHT-8 );
1331: // Line++;
1332:
1333: if (Line < 0) /* Limit to top/bottom of possible visible screen */
1334: Line = 0;
1335: if (Line >= NUM_VISIBLE_LINES)
1336: Line = NUM_VISIBLE_LINES-1;
1337:
1338: /* Store pointers */
1339: pHBLPaletteMasks = &HBLPaletteMasks[Line]; /* Next mask entry */
1340: pHBLPalettes = &HBLPalettes[16*Line]; /* Next colour raster list x16 colours */
1.1 root 1341: }
1.1.1.8 root 1342:
1343:
1.1.1.9 root 1344: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1345: /**
1346: * Set video shifter timing variables according to screen refresh rate.
1347: * Note: The following equation must be satisfied for correct timings:
1348: *
1349: * nCyclesPerLine * nScanlinesPerFrame * nScreenRefreshRate = 8 MHz
1350: */
1.1.1.10 root 1351: static void Video_ResetShifterTimings(void)
1352: {
1.1.1.11 root 1353: Uint8 nSyncByte;
1.1.1.10 root 1354:
1.1.1.11 root 1355: nSyncByte = IoMem_ReadByte(0xff820a);
1.1.1.10 root 1356:
1.1.1.11 root 1357: if (bUseHighRes)
1358: {
1359: /* 71 Hz, monochrome */
1360: nScreenRefreshRate = 71;
1361: nScanlinesPerFrame = SCANLINES_PER_FRAME_71HZ;
1362: nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1363: nStartHBL = SCREEN_START_HBL_71HZ;
1364: nFirstVisibleHbl = FIRST_VISIBLE_HBL_71HZ;
1365: }
1366: else if (nSyncByte & 2) /* Check if running in 50 Hz or in 60 Hz */
1367: {
1368: /* 50 Hz */
1369: nScreenRefreshRate = 50;
1370: nScanlinesPerFrame = SCANLINES_PER_FRAME_50HZ;
1371: nCyclesPerLine = CYCLES_PER_LINE_50HZ;
1372: nStartHBL = SCREEN_START_HBL_50HZ;
1373: nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ;
1374: }
1375: else
1376: {
1377: /* 60 Hz */
1378: nScreenRefreshRate = 60;
1379: nScanlinesPerFrame = SCANLINES_PER_FRAME_60HZ;
1380: nCyclesPerLine = CYCLES_PER_LINE_60HZ;
1381: nStartHBL = SCREEN_START_HBL_60HZ;
1382: nFirstVisibleHbl = FIRST_VISIBLE_HBL_60HZ;
1383: }
1384:
1385: if (bUseHighRes)
1386: {
1387: nEndHBL = nStartHBL + SCREEN_HEIGHT_HBL_MONO;
1388: }
1389: else
1390: {
1391: nEndHBL = nStartHBL + SCREEN_HEIGHT_HBL_COLOR;
1392: }
1393:
1394: /* Reset freq changes position for the next VBL to come */
1395: LastCycleSync50 = -1;
1396: LastCycleSync60 = -1;
1397: }
1.1.1.10 root 1398:
1399:
1.1.1.11 root 1400: /*-----------------------------------------------------------------------*/
1401: /**
1402: * Clear the array indicating the border state of each line
1403: */
1404: static void Video_ClearScreenBorder(void)
1405: {
1406: memset(ScreenBorderMask, 0, sizeof(int)*MAX_SCANLINES_PER_FRAME);
1.1.1.10 root 1407: }
1408:
1409:
1410: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1411: /**
1412: * Called on VBL, set registers ready for frame
1413: */
1.1.1.9 root 1414: static void Video_ClearOnVBL(void)
1415: {
1.1.1.11 root 1416: /* New screen, so first HBL */
1417: nHBL = 0;
1418: OverscanMode = OVERSCANMODE_NONE;
1419:
1420: Video_ResetShifterTimings();
1421:
1422: /* Get screen address pointer, aligned to 256 bytes on ST (ie ignore lowest byte) */
1423: VideoBase = (Uint32)IoMem_ReadByte(0xff8201)<<16 | (Uint32)IoMem_ReadByte(0xff8203)<<8;
1424: if (ConfigureParams.System.nMachineType != MACHINE_ST)
1425: {
1426: /* on STe 2 aligned, on Falcon 4 aligned, on TT 8 aligned. We do STe. */
1427: VideoBase |= IoMem_ReadByte(0xff820d) & ~1;
1428: }
1429: pVideoRaster = &STRam[VideoBase];
1430: pSTScreen = pFrameBuffer->pSTScreen;
1431:
1432: Video_StartHBL();
1433: Video_SetScreenRasters();
1434: Video_ClearScreenBorder();
1435: Spec512_StartVBL();
1436: }
1437:
1438:
1439: /*-----------------------------------------------------------------------*/
1440: /**
1441: * Get width, height and bpp according to TT-Resolution
1442: */
1443: void Video_GetTTRes(int *width, int *height, int *bpp)
1444: {
1445: switch (TTRes)
1446: {
1447: case ST_LOW_RES: *width = 320; *height = 200; *bpp = 4; break;
1448: case ST_MEDIUM_RES:*width = 640; *height = 200; *bpp = 2; break;
1449: case ST_HIGH_RES: *width = 640; *height = 400; *bpp = 1; break;
1450: case TT_LOW_RES: *width = 320; *height = 480; *bpp = 8; break;
1451: case TT_MEDIUM_RES:*width = 640; *height = 480; *bpp = 4; break;
1452: case TT_HIGH_RES: *width = 1280; *height = 960; *bpp = 1; break;
1453: default:
1454: fprintf(stderr, "TT res error!\n");
1455: *width = 320; *height = 200; *bpp = 4;
1456: break;
1457: }
1458: }
1459:
1460:
1461: /*-----------------------------------------------------------------------*/
1462: /**
1463: * Convert TT palette to SDL palette
1464: */
1465: static void Video_UpdateTTPalette(int bpp)
1466: {
1467: Uint32 ttpalette, src, dst;
1468: Uint8 r,g,b, lowbyte, highbyte;
1469: Uint16 stcolor, ttcolor;
1470: int i, offset, colors;
1471:
1472: ttpalette = 0xff8400;
1473:
1474: if (!bTTColorsSTSync)
1475: {
1476: /* sync TT ST-palette to TT-palette */
1477: src = 0xff8240; /* ST-palette */
1478: offset = (IoMem_ReadWord(0xff8262) & 0x0f);
1479: /*fprintf(stdout, "offset: %d\n", offset);*/
1480: dst = ttpalette + offset * 16*SIZE_WORD;
1481:
1482: for (i = 0; i < 16; i++)
1483: {
1484: stcolor = IoMem_ReadWord(src);
1485: ttcolor = ((stcolor&0x700) << 1) | ((stcolor&0x70) << 1) | ((stcolor&0x7) << 1);
1486: IoMem_WriteWord(dst, ttcolor);
1487: src += SIZE_WORD;
1488: dst += SIZE_WORD;
1489: }
1490: bTTColorsSTSync = TRUE;
1491: }
1492:
1493: colors = 1 << bpp;
1494: if (bpp == 1)
1495: {
1496: /* Monochrome mode... palette is hardwired (?) */
1497: HostScreen_setPaletteColor(0, 255, 255, 255);
1498: HostScreen_setPaletteColor(1, 0, 0, 0);
1499: }
1500: else
1501: {
1502: for (i = 0; i < colors; i++)
1503: {
1504: lowbyte = IoMem_ReadByte(ttpalette++);
1505: highbyte = IoMem_ReadByte(ttpalette++);
1506: r = (lowbyte & 0x0f) << 4;
1507: g = (highbyte & 0xf0);
1508: b = (highbyte & 0x0f) << 4;
1509: //printf("%d: (%d,%d,%d)\n", i,r,g,b);
1510: HostScreen_setPaletteColor(i, r,g,b);
1511: }
1512: }
1513:
1514: HostScreen_updatePalette(colors);
1515: bTTColorsSync = TRUE;
1516: }
1517:
1518:
1519: /*-----------------------------------------------------------------------*/
1520: /**
1521: * Update TT palette and blit TT screen using VIDEL code
1522: */
1523: static void Video_RenderTTScreen(void)
1524: {
1525: static int nPrevTTRes = -1;
1526: int width, height, bpp;
1527:
1528: Video_GetTTRes(&width, &height, &bpp);
1529: if (TTRes != nPrevTTRes)
1530: {
1531: HostScreen_setWindowSize(width, height, 8);
1532: nPrevTTRes = TTRes;
1533: if (bpp == 1) /* Assert that mono palette will be used in mono mode */
1534: bTTColorsSync = FALSE;
1535: }
1536:
1537: /* colors need synching? */
1538: if (!(bTTColorsSync && bTTColorsSTSync))
1539: {
1540: Video_UpdateTTPalette(bpp);
1541: }
1542:
1543: /* Yes, we are abusing the Videl routines for rendering the TT modes! */
1544: if (!HostScreen_renderBegin())
1545: return;
1546: if (ConfigureParams.Screen.bZoomLowRes)
1547: VIDEL_ConvertScreenZoom(width, height, bpp, width * bpp / 16);
1548: else
1549: VIDEL_ConvertScreenNoZoom(width, height, bpp, width * bpp / 16);
1550: HostScreen_renderEnd();
1551: HostScreen_update1(FALSE);
1552: }
1553:
1554:
1555: /*-----------------------------------------------------------------------*/
1556: /**
1557: * Draw screen (either with ST/STE shifter drawing functions or with
1558: * Videl drawing functions)
1559: */
1560: static void Video_DrawScreen(void)
1561: {
1562: /* Skip frame if need to */
1563: if (nVBLs % (ConfigureParams.Screen.FrameSkips+1))
1564: return;
1565:
1566: /* Use extended VDI resolution?
1567: * If so, just copy whole screen on VBL rather than per HBL */
1568: if (bUseVDIRes)
1569: Video_CopyVDIScreen();
1570:
1571: /* Now draw the screen! */
1572: if (ConfigureParams.System.nMachineType == MACHINE_FALCON && !bUseVDIRes)
1573: {
1574: VIDEL_renderScreen();
1575: }
1576: else if (ConfigureParams.System.nMachineType == MACHINE_TT && !bUseVDIRes)
1577: {
1578: Video_RenderTTScreen();
1579: }
1580: else
1581: Screen_Draw();
1582: }
1583:
1584:
1585: /*-----------------------------------------------------------------------*/
1586: /**
1587: * Start HBL and VBL interrupts.
1588: */
1589: void Video_StartInterrupts(void)
1590: {
1591: Int_AddAbsoluteInterrupt(nCyclesPerLine - TIMERB_VIDEO_CYCLE_OFFSET - VBL_VIDEO_CYCLE_OFFSET,
1592: INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE);
1593: Int_AddAbsoluteInterrupt(nCyclesPerLine + HBL_VIDEO_CYCLE_OFFSET - VBL_VIDEO_CYCLE_OFFSET,
1594: INT_CPU_CYCLE, INTERRUPT_VIDEO_HBL);
1595: Int_AddAbsoluteInterrupt(CYCLES_PER_FRAME, INT_CPU_CYCLE, INTERRUPT_VIDEO_VBL);
1596: }
1597:
1598:
1599: /*-----------------------------------------------------------------------*/
1600: /**
1601: * VBL interrupt, draw screen and reset counters
1602: */
1603: void Video_InterruptHandler_VBL(void)
1604: {
1605: int PendingCyclesOver;
1.1.1.9 root 1606:
1.1.1.11 root 1607: /* Store cycles we went over for this frame(this is our inital count) */
1608: PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE ); /* +ve */
1.1.1.10 root 1609:
1.1.1.11 root 1610: /* Remove this interrupt from list and re-order */
1611: Int_AcknowledgeInterrupt();
1.1.1.9 root 1612:
1.1.1.11 root 1613: /* Start VBL & HBL interrupts */
1614: Video_StartInterrupts();
1.1.1.9 root 1615:
1.1.1.11 root 1616: /* Set frame cycles, used for Video Address */
1617: Cycles_SetCounter(CYCLES_COUNTER_VIDEO, PendingCyclesOver + VBL_VIDEO_CYCLE_OFFSET);
1.1.1.9 root 1618:
1.1.1.11 root 1619: /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
1620: Keymap_DebounceAllKeys();
1621: /* Act on shortcut keys */
1622: ShortCut_ActKey();
1.1.1.9 root 1623:
1.1.1.11 root 1624: Video_DrawScreen();
1.1.1.9 root 1625:
1.1.1.11 root 1626: /* Check printer status */
1627: Printer_CheckIdleStatus();
1628:
1629: /* Update counter for number of screen refreshes per second */
1630: nVBLs++;
1631: /* Set video registers for frame */
1632: Video_ClearOnVBL();
1633: /* Store off PSG registers for YM file, is enabled */
1634: YMFormat_UpdateRecording();
1635: /* Generate 1/50th second of sound sample data, to be played by sound thread */
1636: Sound_Update_VBL();
1637:
1638: HATARI_TRACE ( HATARI_TRACE_VIDEO_VBL , "VBL %d video_cyc=%d pending_cyc=%d\n" ,
1639: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver );
1640:
1641: M68000_Exception ( EXCEPTION_VBLANK , M68000_INT_VIDEO ); /* Vertical blank interrupt, level 4! */
1642:
1643: /* And handle any messages, check for quit message */
1644: Main_EventHandler(); /* Process messages, set 'bQuitProgram' if user tries to quit */
1645: if (bQuitProgram)
1646: {
1647: /* Pass NULL interrupt function to quit cleanly */
1648: Int_AddAbsoluteInterrupt(4, INT_CPU_CYCLE, INTERRUPT_NULL);
1649: M68000_SetSpecial(SPCFLAG_BRK); /* Assure that CPU core shuts down */
1650: }
1651:
1652: Main_WaitOnVbl();
1.1.1.9 root 1653: }
1654:
1655:
1656: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1657: /**
1658: * Reset video chip
1659: */
1.1.1.9 root 1660: void Video_Reset(void)
1661: {
1.1.1.11 root 1662: /* NOTE! Must reset all of these register type things here!!!! */
1.1.1.9 root 1663:
1.1.1.11 root 1664: /* Are we in high-res? */
1665: if (bUseHighRes)
1666: VideoShifterByte = ST_HIGH_RES; /* Boot up for mono monitor */
1667: else
1668: VideoShifterByte = ST_LOW_RES;
1669: if (bUseVDIRes)
1670: VideoShifterByte = VDIRes;
1671:
1.1.1.12! root 1672: /* Set system specific timings */
! 1673: Video_SetSystemTimings();
! 1674:
1.1.1.11 root 1675: /* Reset VBL counter */
1676: nVBLs = 0;
1677: /* Reset addresses */
1678: VideoBase = 0L;
1679: /* Reset STe screen variables */
1680: LineWidth = 0;
1681: HWScrollCount = 0;
1682: bSteBorderFlag = FALSE;
1683:
1684: NewLineWidth = -1; /* cancel pending modifications set before the reset */
1685: NewHWScrollCount = -1;
1686:
1687: /* Clear ready for new VBL */
1688: Video_ClearOnVBL();
1.1.1.9 root 1689: }
1690:
1691:
1692: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1693: /**
1694: * Write to video address base high and med register (0xff8201 and 0xff8203).
1695: * When a program writes to these registers, some other video registers
1696: * are reset to zero.
1697: */
1.1.1.9 root 1698: void Video_ScreenBaseSTE_WriteByte(void)
1699: {
1.1.1.11 root 1700: IoMem[0xff820d] = 0; /* Reset screen base low register */
1.1.1.8 root 1701:
1.1.1.11 root 1702: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_VIDEO_STE ) )
1703: {
1704: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
1705: int nLineCycles = nFrameCycles % nCyclesPerLine;
1706: HATARI_TRACE_PRINT ( "write ste video base=%x video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" , (IoMem[0xff8201]<<16)+(IoMem[0xff8203]<<8) ,
1707: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
1708: }
1.1.1.8 root 1709: }
1710:
1711: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1712: /**
1713: * Read video address counter and update ff8205/07/09
1714: */
1715: void Video_ScreenCounter_ReadByte(void)
1.1.1.8 root 1716: {
1.1.1.11 root 1717: Uint32 addr;
1.1.1.8 root 1718:
1.1.1.11 root 1719: addr = Video_CalculateAddress(); /* get current video address */
1720: IoMem[0xff8205] = ( addr >> 16 ) & 0xff;
1721: IoMem[0xff8207] = ( addr >> 8 ) & 0xff;
1722: IoMem[0xff8209] = addr & 0xff;
1.1.1.8 root 1723: }
1724:
1.1.1.9 root 1725: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1726: /**
1727: * Write to video address counter (0xff8205, 0xff8207 and 0xff8209).
1728: * Called on STE only and like with base address, you cannot set lowest bit.
1729: * If display has not started yet for this line, we can change pVideoRaster now.
1730: * Else we store the new value of the Hi/Med/Lo address to change it at the end
1731: * of the current line when Video_CopyScreenLineColor is called.
1732: * We must change only the byte that was modified and keep the two others ones.
1733: */
1.1.1.9 root 1734: void Video_ScreenCounter_WriteByte(void)
1735: {
1.1.1.11 root 1736: Uint8 AddrByte;
1737: Uint32 addr;
1738: int nFrameCycles;
1739: int nLineCycles;
1740: int HblCounterVideo;
1741:
1742: nFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);;
1743: nLineCycles = nFrameCycles % nCyclesPerLine;
1744:
1745: /* Get real video line count (can be different from nHBL) */
1746: HblCounterVideo = nFrameCycles / nCyclesPerLine;
1747:
1748: AddrByte = IoMem[ IoAccessCurrentAddress ];
1749:
1750: /* If display has not started, we can still modify pVideoRaster */
1751: /* We must also check the write does not overlap the end of the line */
1752: if ( ( ( nLineCycles <= LINE_START_CYCLE_50 ) && ( nHBL == HblCounterVideo ) )
1753: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL ) )
1754: {
1755: addr = Video_CalculateAddress(); /* get current video address */
1756: if ( IoAccessCurrentAddress == 0xff8205 )
1757: addr = ( addr & 0x00ffff ) | ( AddrByte << 16 );
1758: else if ( IoAccessCurrentAddress == 0xff8207 )
1759: addr = ( addr & 0xff00ff ) | ( AddrByte << 8 );
1760: else if ( IoAccessCurrentAddress == 0xff8209 )
1761: addr = ( addr & 0xffff00 ) | ( AddrByte );
1762:
1763: pVideoRaster = &STRam[addr & ~1]; /* set new video address */
1764: }
1765:
1766: /* Can't change pVideoRaster now, store the modified byte for Video_CopyScreenLineColor */
1767: else
1768: {
1769: if ( IoAccessCurrentAddress == 0xff8205 )
1770: NewVideoHi = AddrByte;
1771: else if ( IoAccessCurrentAddress == 0xff8207 )
1772: NewVideoMed = AddrByte;
1773: else if ( IoAccessCurrentAddress == 0xff8209 )
1774: NewVideoLo = AddrByte;
1775: }
1776:
1777: HATARI_TRACE ( HATARI_TRACE_VIDEO_STE , "write ste video %x val=0x%x video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
1778: IoAccessCurrentAddress, AddrByte,
1779: nFrameCycles, nLineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.9 root 1780: }
1.1.1.8 root 1781:
1782: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1783: /**
1784: * Read video sync register (0xff820a)
1785: */
1.1.1.8 root 1786: void Video_Sync_ReadByte(void)
1787: {
1.1.1.11 root 1788: /* Nothing... */
1.1.1.8 root 1789: }
1790:
1791: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1792: /**
1793: * Read video base address low byte (0xff820d). A plain ST can only store
1794: * screen addresses rounded to 256 bytes (i.e. no lower byte).
1795: */
1.1.1.8 root 1796: void Video_BaseLow_ReadByte(void)
1797: {
1.1.1.11 root 1798: if (ConfigureParams.System.nMachineType == MACHINE_ST)
1799: IoMem[0xff820d] = 0; /* On ST this is always 0 */
1.1.1.9 root 1800:
1.1.1.11 root 1801: /* Note that you should not do anything here for STe because
1802: * VideoBase address is set in an interrupt and would be wrong
1803: * here. It's fine like this.
1804: */
1.1.1.8 root 1805: }
1806:
1807: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1808: /**
1809: * Read video line width register (0xff820f)
1810: */
1.1.1.8 root 1811: void Video_LineWidth_ReadByte(void)
1812: {
1.1.1.11 root 1813: if (ConfigureParams.System.nMachineType == MACHINE_ST)
1814: IoMem[0xff820f] = 0; /* On ST this is always 0 */
1815: else
1816: IoMem[0xff820f] = LineWidth;
1.1.1.8 root 1817: }
1818:
1819: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1820: /**
1821: * Read video shifter mode register (0xff8260)
1822: */
1.1.1.8 root 1823: void Video_ShifterMode_ReadByte(void)
1824: {
1.1.1.11 root 1825: if (bUseHighRes)
1826: IoMem[0xff8260] = 2; /* If mono monitor, force to high resolution */
1827: else
1828: IoMem[0xff8260] = VideoShifterByte; /* Read shifter register */
1.1.1.8 root 1829: }
1830:
1.1.1.10 root 1831: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1832: /**
1833: * Read horizontal scroll register (0xff8265)
1834: */
1.1.1.10 root 1835: void Video_HorScroll_Read(void)
1836: {
1.1.1.11 root 1837: IoMem[0xff8265] = HWScrollCount;
1.1.1.10 root 1838: }
1.1.1.8 root 1839:
1840:
1841: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1842: /**
1843: * Write video line width register (0xff820f) - STE only.
1844: * Content of LineWidth is added to the shifter counter when display is
1845: * turned off (start of the right border, usually at cycle 376)
1846: */
1.1.1.10 root 1847: void Video_LineWidth_WriteByte(void)
1848: {
1.1.1.11 root 1849: Uint8 NewWidth;
1850: int nFrameCycles;
1851: int nLineCycles;
1852: int HblCounterVideo;
1853:
1854: nFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);;
1855: nLineCycles = nFrameCycles % nCyclesPerLine;
1856:
1857: /* Get real video line count (can be different from nHBL) */
1858: HblCounterVideo = nFrameCycles / nCyclesPerLine;
1859:
1860: NewWidth = IoMem_ReadByte(0xff820f);
1861:
1862: /* We must also check the write does not overlap the end of the line */
1863: if ( ( ( nLineCycles <= LineEndCycle ) && ( nHBL == HblCounterVideo ) )
1864: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL ) )
1865: LineWidth = NewWidth; /* display is on, we can still change */
1866: else
1867: NewLineWidth = NewWidth; /* display is off, can't change LineWidth once in right border */
1868:
1869: HATARI_TRACE ( HATARI_TRACE_VIDEO_STE , "write ste linewidth=0x%x video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" , NewWidth,
1870: nFrameCycles, nLineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.10 root 1871: }
1872:
1873: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1874: /**
1875: * Write to video shifter palette registers (0xff8240-0xff825e)
1876: */
1.1.1.8 root 1877: static void Video_ColorReg_WriteWord(Uint32 addr)
1878: {
1.1.1.11 root 1879: if (!bUseHighRes) /* Don't store if hi-res */
1880: {
1881: int idx;
1882: Uint16 col;
1883: Video_SetHBLPaletteMaskPointers(); /* Set 'pHBLPalettes' etc.. according cycles into frame */
1884: col = IoMem_ReadWord(addr);
1885: if (ConfigureParams.System.nMachineType == MACHINE_ST)
1886: col &= 0x777; /* Mask off to ST 512 palette */
1887: else
1888: col &= 0xfff; /* Mask off to STe 4096 palette */
1889: IoMem_WriteWord(addr, col); /* (some games write 0xFFFF and read back to see if STe) */
1890: Spec512_StoreCyclePalette(col, addr); /* Store colour into CyclePalettes[] */
1891: idx = (addr-0xff8240)/2; /* words */
1892: pHBLPalettes[idx] = col; /* Set colour x */
1893: *pHBLPaletteMasks |= 1 << idx; /* And mask */
1894:
1895: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_VIDEO_COLOR ) )
1896: {
1897: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
1898: int nLineCycles = nFrameCycles % nCyclesPerLine;
1899: HATARI_TRACE_PRINT ( "write col addr=%x col=%x video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" , addr, col,
1900: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
1901: }
1902:
1903: }
1.1.1.8 root 1904: }
1905:
1906: void Video_Color0_WriteWord(void)
1907: {
1.1.1.11 root 1908: Video_ColorReg_WriteWord(0xff8240);
1.1.1.8 root 1909: }
1910:
1911: void Video_Color1_WriteWord(void)
1912: {
1.1.1.11 root 1913: Video_ColorReg_WriteWord(0xff8242);
1.1.1.8 root 1914: }
1915:
1916: void Video_Color2_WriteWord(void)
1917: {
1.1.1.11 root 1918: Video_ColorReg_WriteWord(0xff8244);
1.1.1.8 root 1919: }
1920:
1921: void Video_Color3_WriteWord(void)
1922: {
1.1.1.11 root 1923: Video_ColorReg_WriteWord(0xff8246);
1.1.1.8 root 1924: }
1925:
1926: void Video_Color4_WriteWord(void)
1927: {
1.1.1.11 root 1928: Video_ColorReg_WriteWord(0xff8248);
1.1.1.8 root 1929: }
1930:
1931: void Video_Color5_WriteWord(void)
1932: {
1.1.1.11 root 1933: Video_ColorReg_WriteWord(0xff824a);
1.1.1.8 root 1934: }
1935:
1936: void Video_Color6_WriteWord(void)
1937: {
1.1.1.11 root 1938: Video_ColorReg_WriteWord(0xff824c);
1.1.1.8 root 1939: }
1940:
1941: void Video_Color7_WriteWord(void)
1942: {
1.1.1.11 root 1943: Video_ColorReg_WriteWord(0xff824e);
1.1.1.8 root 1944: }
1945:
1946: void Video_Color8_WriteWord(void)
1947: {
1.1.1.11 root 1948: Video_ColorReg_WriteWord(0xff8250);
1.1.1.8 root 1949: }
1950:
1951: void Video_Color9_WriteWord(void)
1952: {
1.1.1.11 root 1953: Video_ColorReg_WriteWord(0xff8252);
1.1.1.8 root 1954: }
1955:
1956: void Video_Color10_WriteWord(void)
1957: {
1.1.1.11 root 1958: Video_ColorReg_WriteWord(0xff8254);
1.1.1.8 root 1959: }
1960:
1961: void Video_Color11_WriteWord(void)
1962: {
1.1.1.11 root 1963: Video_ColorReg_WriteWord(0xff8256);
1.1.1.8 root 1964: }
1965:
1966: void Video_Color12_WriteWord(void)
1967: {
1.1.1.11 root 1968: Video_ColorReg_WriteWord(0xff8258);
1.1.1.8 root 1969: }
1970:
1971: void Video_Color13_WriteWord(void)
1972: {
1.1.1.11 root 1973: Video_ColorReg_WriteWord(0xff825a);
1.1.1.8 root 1974: }
1975:
1976: void Video_Color14_WriteWord(void)
1977: {
1.1.1.11 root 1978: Video_ColorReg_WriteWord(0xff825c);
1.1.1.8 root 1979: }
1980:
1981: void Video_Color15_WriteWord(void)
1982: {
1.1.1.11 root 1983: Video_ColorReg_WriteWord(0xff825e);
1.1.1.8 root 1984: }
1985:
1986:
1987: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1988: /**
1989: * Write video shifter mode register (0xff8260)
1990: */
1.1.1.8 root 1991: void Video_ShifterMode_WriteByte(void)
1992: {
1.1.1.11 root 1993: if (ConfigureParams.System.nMachineType == MACHINE_TT)
1994: {
1995: TTRes = IoMem_ReadByte(0xff8260) & 7;
1996: IoMem_WriteByte(0xff8262, TTRes); /* Copy to TT shifter mode register */
1997: }
1998: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
1999: {
2000: /* - activate STE palette
2001: * - TODO: set line width ($8210)
2002: * - TODO: sets paramaters in $82c2 (double lines/interlace & cycles/pixel)
2003: */
2004: bUseSTShifter = TRUE;
2005: }
2006: if (!bUseHighRes && !bUseVDIRes) /* Don't store if hi-res and don't store if VDI resolution */
2007: {
2008: VideoShifterByte = IoMem[0xff8260] & 3; /* We only care for lower 2-bits */
2009: Video_WriteToShifter(VideoShifterByte);
2010: Video_SetHBLPaletteMaskPointers();
2011: *pHBLPaletteMasks &= 0xff00ffff;
2012: /* Store resolution after palette mask and set resolution write bit: */
2013: *pHBLPaletteMasks |= (((Uint32)VideoShifterByte|0x04)<<16);
2014: }
1.1.1.10 root 2015: }
2016:
2017: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2018: /**
2019: * Write to horizontal scroll register (0xff8265).
2020: * Note: The STE shifter has a funny "bug" that allows to increase the
2021: * resolution to 336x200 instead of 320x200. It occurs when a program writes
2022: * certain values to 0xff8264:
2023: * move.w #1,$ffff8264 ; Word access!
2024: * clr.b $ffff8264 ; Byte access!
2025: * Some games (Obsession, Skulls) and demos (Pacemaker by Paradox) use this
2026: * feature to increase the resolution, so we have to emulate this bug, too!
2027: */
1.1.1.10 root 2028: void Video_HorScroll_Write(void)
2029: {
1.1.1.11 root 2030: static BOOL bFirstSteAccess = FALSE;
2031: Uint8 ScrollCount;
2032: int nFrameCycles;
2033: int nLineCycles;
2034: int HblCounterVideo;
2035:
2036: nFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);;
2037: nLineCycles = nFrameCycles % nCyclesPerLine;
1.1.1.10 root 2038:
1.1.1.11 root 2039: /* Get real video line count (can be different from nHBL) */
2040: HblCounterVideo = nFrameCycles / nCyclesPerLine;
1.1.1.10 root 2041:
1.1.1.11 root 2042: ScrollCount = IoMem[0xff8265];
2043: ScrollCount &= 0x0f;
1.1.1.10 root 2044:
1.1.1.11 root 2045: /* We must also check the write does not overlap the end of the line */
2046: if ( ( ( nLineCycles <= LINE_START_CYCLE_50 ) && ( nHBL == HblCounterVideo ) )
2047: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL ) )
2048: HWScrollCount = ScrollCount; /* display has not started, we can still change */
2049: else
2050: NewHWScrollCount = ScrollCount; /* display has started, can't change HWScrollCount now */
1.1.1.10 root 2051:
1.1.1.11 root 2052: HATARI_TRACE ( HATARI_TRACE_VIDEO_STE , "write ste hwscroll=%x video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" , ScrollCount,
2053: nFrameCycles, nLineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
2054:
2055: /*fprintf(stderr, "Write to 0x%x (0x%x, 0x%x, %i)\n", IoAccessBaseAddress,
2056: IoMem[0xff8264], ScrollCount, nIoMemAccessSize);*/
2057:
2058: if (IoAccessBaseAddress == 0xff8264 && nIoMemAccessSize == SIZE_WORD
2059: && ScrollCount == 1)
2060: {
2061: /*fprintf(stderr, "STE border removal - access 1\n");*/
2062: bFirstSteAccess = TRUE;
2063: }
2064: else if (bFirstSteAccess && ScrollCount == 1 &&
2065: IoAccessBaseAddress == 0xff8264 && nIoMemAccessSize == SIZE_BYTE)
2066: {
2067: /*fprintf(stderr, "STE border removal - access 2\n");*/
2068: bSteBorderFlag = TRUE;
1.1.1.12! root 2069: HATARI_TRACE ( HATARI_TRACE_VIDEO_BORDER_H , "detect STE left+8\n" );
1.1.1.11 root 2070: }
2071: else
2072: {
2073: bFirstSteAccess = bSteBorderFlag = FALSE;
2074: }
2075: }
2076:
2077:
2078: /*-----------------------------------------------------------------------*/
2079: /**
2080: * Write to TT shifter mode register (0xff8262)
2081: */
2082: void Video_TTShiftMode_WriteWord(void)
2083: {
2084: TTRes = IoMem_ReadByte(0xff8262) & 7;
2085:
2086: /*fprintf(stderr, "Write to FF8262: %x, res=%i\n", IoMem_ReadWord(0xff8262), TTRes);*/
2087:
2088: /* Is it an ST compatible resolution? */
2089: if (TTRes <= 2)
2090: {
2091: IoMem_WriteByte(0xff8260, TTRes);
2092: Video_ShifterMode_WriteByte();
2093: }
2094: }
2095:
2096: /*-----------------------------------------------------------------------*/
2097: /**
2098: * Write to TT color register (0xff8400)
2099: */
2100: void Video_TTColorRegs_WriteWord(void)
2101: {
2102: bTTColorsSync = FALSE;
2103: }
2104:
2105: /*-----------------------------------------------------------------------*/
2106: /**
2107: * Write to ST color register on TT (0xff8400)
2108: */
2109: void Video_TTColorSTRegs_WriteWord(void)
2110: {
2111: bTTColorsSTSync = FALSE;
1.1.1.8 root 2112: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.