|
|
1.1 root 1: /*
1.1.1.5 root 2: Hatari - video.c
3:
1.1.1.21 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.7 root 7: 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). */
1.1.1.21 root 20: /* 2007/04/16 [NP] Better Video_CalculateAddress. We must subtract a "magic" 12 cycles to */
1.1.1.11 root 21: /* Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) to get a correct */
1.1.1.21 root 22: /* value (No Cooper's video synchro protection is finally OK :) ). */
1.1.1.11 root 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. */
1.1.1.15 root 30: /* 2007/05/11 [NP] Add support for med res overscan (No Cooper Greetings). */
1.1.1.11 root 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 */
1.1.1.15 root 34: /* depending on line (med/lo and borders). */
1.1.1.11 root 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). */
1.1.1.15 root 39: /* 2007/10/31 [NP] Use BORDERMASK_LEFT_OFF_MED when left border is removed with hi/med */
1.1.1.11 root 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). */
1.1.1.15 root 43: /* 2007/11/05 [NP] Depending on the position of the med res switch, the planes will be */
44: /* shifted when doing med res overscan (Best Part Of the Creation in PYM */
1.1.1.11 root 45: /* or No Cooper Greetings). */
1.1.1.15 root 46: /* 2007/11/30 [NP] A hi/med 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 med res */
1.1.1.11 root 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 */
1.1.1.13 root 55: /* HBL/VBL/MFP exception). */
1.1.1.11 root 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 */
1.1.1.21 root 96: /* immediately if display has not started for the line (before cycle */
1.1.1.11 root 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). */
1.1.1.21 root 120: /* This means a write to $ff820f should be applied immediately only if it */
1.1.1.11 root 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)*/
1.1.1.21 root 130: /* then the new value should not be set immediately but stored and set */
1.1.1.11 root 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.13 root 139: /* 2008/04/02 [NP] Correct a rare case in Video_Sync_WriteByte at the end of line 33 : */
140: /* nStartHBL was set to 33 instead of 64, which gave a wrong address in */
141: /* Video_CalculateAddress. */
142: /* 2008/04/04 [NP] The value of RestartVideoCounterCycle is slightly different between */
143: /* an STF and an STE. */
144: /* 2008/04/05 [NP] The value of VblVideoCycleOffset is different of 4 cycles between */
145: /* STF and STE (fix end part in Pacemaker by Paradox). */
146: /* 2008/04/09 [NP] Preliminary support for lines using different frequencies in the same */
147: /* screen. In Video_InterruptHandler_EndLine, if the current freq is 50 Hz,*/
148: /* then next int should be scheduled in 512 cycles ; if freq is 60 Hz, */
149: /* next int should be in 508 cycles (used by timer B event count mode). */
150: /* 2008/04/10 [NP] Update LineEndCycle after changing freq to 50 or 60 Hz. */
151: /* Set EndLine interrupt to happen 28 cycles after LineEndCycle. This way */
152: /* Timer B occurs at cycle 404 in 50 Hz, or cycle 400 in 60 Hz (improve */
153: /* flickering bottom border in B.I.G. Demo screen 1). */
154: /* 2008/04/12 [NP] In the case of a 'right-2' line, we should not change the EndLine's int */
155: /* position when switching back to 50 Hz ; the int should happen at */
156: /* position LINE_END_CYCLE_60 + 28 (Anomaly Demo main menu). */
157: /* 2008/05/31 [NP] Ignore consecutives writes of the same value in the freq/res register. */
158: /* Only the 1st write matters, else this could confuse the code to remove */
159: /* top/bottom border (fix OSZI.PRG demo by ULM). */
160: /* 2008/06/07 [NP] In Video_SetHBLPaletteMaskPointers, use LineStartCycle instead of the */
161: /* 50 Hz constant SCREEN_START_CYCLE. */
162: /* Rename SCREEN_START_HBL_xxx to VIDEO_START_HBL_xxx. */
163: /* Rename SCREEN_END_HBL_xxx to VIDEO_END_HBL_xxx. */
164: /* Rename SCREEN_HEIGHT_HBL_xxx to VIDEO_HEIGHT_HBL_xxx. */
165: /* Use VIDEO_HEIGHT_BOTTOM_50HZ instead of OVERSCAN_BOTTOM. */
166: /* 2008/06/16 [NP] When Hatari is configured to display the screen's borders, 274 lines */
167: /* will be rendered on screen, but if the shifter is in 60 Hz, the last */
168: /* 16 lines will never be used, which can leave some bad pixels on */
169: /* screen. We clear the remaining lines before calling 'Screen_Draw'. */
170: /* (in FNIL by Delta Force, fix flickering gfx in the bottom border of the */
171: /* F2 screen : last 16 lines were the ones from the menu where bottom */
172: /* border was removed ). */
173: /* 2008/06/26 [NP] Improve STE scrolling : handle $ff8264 (no prefetch) and $ff8265 */
174: /* (prefetch). See Video_HorScroll_Write for details on both registers. */
175: /* More generic support for starting display 16 pixels earlier on STE */
176: /* by writing to $ff8265 and settting $ff8264=0 just after. */
177: /* (fix Digiworld 2 by ICE, which uses $ff8264 for horizontal scroll). */
178: /* 2008/07/07 [NP] Ignore other 50/60 Hz switches once the right border was removed, keep */
179: /* the timer B to occur at pos 460+28 (fix Oxygene screen in Transbeauce 2)*/
180: /* 2008/07/14 [NP] When removing only left border in 60Hz, line size is 26+158 bytes */
181: /* instead of 26+160 bytes in 50 Hz (HigResMode demo by Paradox). */
182: /* 2008/07/19 [NP] If $ff8260==3 (which is not a valid resolution mode), we use 0 instead */
183: /* (low res) (fix Omegakul screen in old Omega Demo from 1988). */
184: /* 2008/09/05 [NP] No need to test 60/50 switch if HblCounterVideo < nStartHBL (display */
185: /* has not started yet). */
186: /* 2008/09/25 [NP] Use nLastVisibleHbl to store the number of the last hbl line that should*/
187: /* be copied to the emulator's screen buffer. */
1.1.1.21 root 188: /* On STE, allow to change immediately video address, hw scroll and */
1.1.1.13 root 189: /* linewidth when nHBL>=nLastVisibleHbl instead of nHBL>=nEndHBL */
190: /* (fix Power Rise / Xtrem D demo). */
191: /* 2008/11/15 [NP] For STE registers, add in the TRACE call if the write is delayed or */
192: /* not (linewidth, hwscroll, video address). */
193: /* On STE, allow to change linewdith, hwscroll and video address with no */
194: /* delay as soon as nHBL >= nEndHBL (revert previous changes). Power Rise */
195: /* is still working due to NewHWScrollCount=-1 when setting immediate */
196: /* hwscroll. Fix regression in Braindamage. */
1.1.1.14 root 197: /* 2008/11/29 [NP] Increment jitter's index for HBL and VBL each time a possible interrupt */
198: /* occurs. Each interrupt can have a jitter between 0, 4 and 8 cycles ; the*/
199: /* jitter follows a predefined pattern of 5 values. The HBL and the VBL */
200: /* have their own pattern. See InterruptAddJitter() in uae-cpu/newcpu.c */
201: /* (fix Fullscreen tunnel in Suretrip 49% by Checkpoint and digi sound in */
202: /* Swedish New Year's TCB screen). */
203: /* 2008/12/10 [NP] Enhance support for 0 byte line. The 60/50 Hz switch can happen at */
204: /* cycles 56/64, but also at 58/66 (because access to $ff820a doesn't */
205: /* require to be on a 4 cycles boundary). As hatari doesn't handle */
206: /* multiple of 2 cycles, we allow cycles 56/64 and 60/68 (fix nosync.tos */
207: /* that uses the STOP instruction to produce a 0 byte line on the first */
208: /* displayed line (found on atari-forum.com)). */
1.1.1.17 root 209: /* 2008/12/26 [NP] When reading $ff8260 on STF, set unused bits to 1 instead of 0 */
1.1.1.14 root 210: /* (fix wrong TOS resolution in Awesome Menu Disk 16). */
211: /* Set unused bit to 1 when reading $ff820a too. */
212: /* 2009/01/16 [NP] Handle special case when writing only in upper byte of a color reg. */
213: /* 2009/01/21 [NP] Implement STE horizontal scroll for medium res (fixes cool_ste.prg). */
214: /* Take the current res into account in Video_CopyScreenLineColor to */
1.1.1.15 root 215: /* allow mixing low/med res with horizontal scroll on STE. */
216: /* 2009/01/24 [NP] Better detection of 'right-2' when freq is changed to 60 Hz and */
217: /* restored to 50 after the end of the current line (fixes games menu on */
218: /* BBC compil 10). */
219: /* 2009/01/31 [NP] Handle a rare case where 'move.b #8,$fffa1f' to start the timer B is */
220: /* done just a few cycles before the actual signal for end of line. In that*/
221: /* case we must ensure that the write was really effective before the end */
222: /* of line (else no interrupt should be made) (fix Pompey Pirate Menu #57).*/
223: /* 2009/02/08 [NP] Handle special case for simultaneous HBL exceptions (fixes flickering in*/
224: /* Monster Business and Super Monaco GP). */
225: /* 2009/02/25 [NP] Ignore other 50/60 Hz switches after display was stopped in the middle */
226: /* of the line with a hi/lo switch. Correct missing end of line timer B */
227: /* interrupt in that case (fix flickering Dragon Ball part in Blood disk 2 */
228: /* by Holocaust). */
229: /* 2008/02/02 [NP] Added 0 byte line detection in STE mode when switching hi/lo res */
230: /* at position 32 (Lemmings screen in Nostalgic-o-demo). */
231: /* 2009/03/28 [NP] Depending on bit 3 of MFP's AER, timer B will count end of line events */
232: /* (bit=0) or start of line events (bit=1) (fix Seven Gates Of Jambala). */
233: /* 2009/04/02 [NP] Add another method to obtain a 0 byte line, by switching to hi/lo res */
234: /* at position 500/508 (fix the game No Buddies Land). */
235: /* 2009/04/xx [NP] Rewrite of many parts : add SHIFTER_FRAME structure, better accuracy */
236: /* when mixing 50/60 Hz lines and reading $ff8209, better emulation of */
237: /* HBL and Timer B position when changing freq/res, better emulation of */
238: /* freq changes for top/bottom/right borders. */
239: /* 2009/07/16 [NP] In Video_SetHBLPaletteMaskPointers, if LineCycle>460 we consider the */
240: /* color's change should be applied to next line (used when spec512 mode */
241: /* if off). */
1.1.1.16 root 242: /* 2009/10/31 [NP] Depending on the overscan mode, the displayed lines must be shifted */
243: /* left or right (fix Spec 512 images in the Overscan Demos, fix pixels */
244: /* alignment in screens mixing normal lines and overscan lines). */
245: /* 2009/12/02 [NP] If we switch hi/lo around position 464 (as in Enchanted Lands) and */
246: /* right border was not removed, then we get an empty line on the next */
247: /* HBL (fix Pax Plax Parralax in Beyond by Kruz). */
248: /* 2009/12/06 [NP] Add support for STE 224 bytes overscan without stabiliser by switching */
249: /* hi/lo at cycle 504/4 to remove left border (fix More Or Less Zero and */
250: /* Cernit Trandafir by DHS, as well as Save The Earth by Defence Force). */
251: /* 2009/12/13 [NP] Improve STE 224 bytes lines : correctly set leftmost 16 pixels to color */
252: /* 0 and correct small glitches when combined with hscroll ($ff8264). */
253: /* 2009/12/13 [NP] Line scrolling caused by hi/lo switch (STF_PixelScroll) should be */
254: /* applied after STE's hardware scrolling, else in overscan 4 color 0 */
255: /* pixels will appear in the right border (because overscan shift the */
256: /* whole displayed area 4 pixels to the left) (fix possible regression on */
257: /* STE introduced on 2009/10/31). */
258: /* 2010/01/10 [NP] In Video_CalculateAddress, take bSteBorderFlag into account (+16 pixels */
259: /* in left border on STE). */
260: /* 2010/01/10 [NP] In Video_CalculateAddress, take HWScrollPrefetch into account (shifter */
261: /* starts 16 pixels earlier) (fix EPSS demo by Unit 17). */
262: /* 2010/02/05 [NP] In Video_CalculateAddress, take STE's LineWidth into account when */
263: /* display is disabled in the right border (fix flickering in Utopos). */
264: /* 2010/02/07 [NP] Better support for modifying $ff8205/07/09 while display is on */
265: /* (fix EPSS demo by Unit 17). */
266: /* 2010/04/12 [NP] Improve timings when writing to $ff8205/07/09 when hscroll is used, */
267: /* using Video_GetMMUStartCycle (fix Pacemaker's Bump Part by Paradox). */
268: /* 2010/05/02 [NP] In Video_ConvertPosition, handle the case where we read the position */
269: /* between the last HBL and the start of the next VBL. During 64 cycles */
270: /* FrameCycles can be >= CYCLES_PER_FRAME (harmless fix, only useful when */
271: /* using --trace to get correct positions in the logs). */
272: /* 2010/05/04 [NP] Improve Video_ConvertPosition, use CyclesPerVBL instead of evaluating */
273: /* CYCLES_PER_FRAME (whose value could have changed this the start of the */
274: /* VBL). */
275: /* 2010/05/15 [NP] In Video_StartInterrupts() when running in monochrome (224 cycles per */
276: /* line), the VBL could sometimes be delayed by 160 cycles (divs) and */
277: /* hbl/timer B interrupts for line 0 were not called, which could cause an */
278: /* assert/crash in Hatari when setting timer B on line 2. */
279: /* If we detect VBL was delayed too much, we add hbl/timer b in the next */
280: /* 4 cycles. */
1.1.1.17 root 281: /* 2010/07/05 [NP] When removing left border, allow up to 32 cycles between hi and low */
282: /* res switching (fix Megabeer by Invizibles). */
283: /* 2010/11/01 [NP] On STE, the 224 bytes overscan will shift the screen 8 pixels to the */
284: /* left. */
285: /* For 230 bytes overscan, handle scrolling prefetching when computing */
286: /* pVideoRaster for the next line. */
287: /* 2010/12/12 [NP] In Video_CopyScreenLineColor, use pVideoRasterEndLine to improve */
288: /* STE's horizontal scrolling for any line's length (160, 224, 230, ...). */
289: /* Fix the last 16 pixels for 224 bytes overscan (More Or Less Zero and */
290: /* Cernit Trandafir by DHS, Save The Earth by Defence Force). */
291: /* 2011/04/03 [NP] Call DmaSnd_HBL_Update() on each HBL to handle programs that modify */
292: /* the samples data while those data are played by the DMA sound. */
293: /* (fixes the game Power Up Plus and the demo Mental Hangover). */
1.1.1.18 root 294: /* 2011/07/30 [NP] Add blank line detection in STF mode when switching 60/50 Hz at cycle */
295: /* 28. The shifter will still read bytes and border removal is possible, */
296: /* but the line will be blank (we use color 0 for now, but the line should */
297: /* be black). */
298: /* (fix spectrum 512 part in Overscan Demo and shforstv by Paulo Simoes */
299: /* by removing "parasite" pixels on the 1st line). */
300: /* 2011/11/17 [NP] Improve timings used for the 0 byte line when switching hi/lo at the */
301: /* end of the line. The hi/lo switch can be at 496/508 or 500/508 */
302: /* (fix NGC screen in Delirious Demo IV). */
303: /* 2011/11/18 [NP] Add support for another method to do 4 pixel hardware scrolling by doing*/
304: /* a med/lo switch after the hi/lo switch to remove left border */
305: /* (fix NGC screen in Delirious Demo IV). */
306: /* 2011/11/19 [NP] The 0 byte line obtained by switching hi/lo at the end of the line has */
307: /* no video signal at all (blank). In that case, the screen is shifted one */
308: /* line down, and bottom border removal will happen one line later too */
309: /* (fix NGC screen in Delirious Demo IV). */
1.1.1.19 root 310: /* 2012/01/11 [NP] Don't remove left border when the hi/lo switch is made at cycle >= 12 */
311: /* (fix 'Kill The Beast 2' in the Vodka Demo) */
1.1.1.20 root 312: /* 2012/05/19 [NP] Allow bottom border to be removed when switch back to 50 Hz is made at */
313: /* cycle 504 and more (instead of 508 and more). Same for top border */
314: /* (fix 'Musical Wonders 1990' by Offbeat). */
1.1.1.21 root 315: /* 2013/03/05 [NP] An extra 4 cycle delay is added by the MFP to set IRQ when the timer B */
316: /* expires in event count mode. Update TIMERB_VIDEO_CYCLE_OFFSET to 24 */
317: /* cycles instead of 28 to compensate for this and keep the same position. */
318: /* 2013/04/26 [NP] Cancel changes from 2012/05/19, 'Musical Wonders 1990' is really broken */
319: /* on a real STF and bottom border is not removed. */
320: /* 2013/05/03 [NP] Add support for IACK sequence when handling HBL/VBL exceptions. Allow */
321: /* to handle the case where interrupt pending bit is set twice (correct */
322: /* fix for Super Monaco GP, Super Hang On, Monster Business, European */
323: /* Demo's Intro, BBC Menu 52). */
1.1.1.22 root 324: /* 2013/07/17 [NP] Handle a special case when writing only in lower byte of a color reg. */
325: /* 2013/12/02 [NP] If $ff8260==3 (which is not a valid resolution mode), we use 2 instead */
326: /* (high res) (cancel wrong change from 2008/07/19 and fix 'The World Is */
327: /* My Oyster - Convention Report Part' by Aura). */
328: /* 2013/12/24 [NP] In Video_ColorReg_ReadWord, randomly return 0 or 1 for unused bits */
329: /* in STF's color registers (fix 'UMD 8730' by PHF in STF mode) */
330: /* 2013/12/28 [NP] For bottom border removal on a 60 Hz screen, max position to go back */
331: /* to 60 Hz should be 4 cycles earlier, as a 60 Hz line starts 4 cycles */
332: /* earlier (fix STE demo "It's a girl 2" by Paradox). */
333: /* 2014/02/22 [NP] In Video_ColorReg_ReadWord(), don't set unused STF bits to rand() if */
334: /* the PC is not executing from the RAM between 0 and 4MB (fix 'Union Demo'*/
1.1.1.24 root 335: /* protection code running at address $ff8240). */
1.1.1.22 root 336: /* 2014/03/21 [NP] For STE in med res overscan at 60 Hz, add a 3 pixels shift to have */
337: /* bitmaps and color changes synchronised (fix 'HighResMode' by Paradox). */
338: /* 2014/05/08 [NP] In case we're mixing 50 Hz and 60 Hz lines (512 or 508 cycles), we must */
339: /* update the position where the VBL interrupt will happen (fix "keyboard */
340: /* no jitter" test program by Nyh, with 4 lines at 60 Hz and 160240 cycles */
341: /* per VBL). */
342: /* 2014/05/31 [NP] Ensure pVideoRaster always points into a 24 bit space region. In case */
343: /* video address at $ff8201/03 is set into IO space $ffxxxx, the new value */
344: /* for video pointer should not be >= $1000000 (fix "Leavin' Teramis" */
345: /* which sets video address to $ffe100 to display "loading please wait". */
346: /* In that case, we must display $ffe100-$ffffff then $0-$5e00) */
1.1.1.23 root 347: /* 2015/06/19 [NP] In Video_CalculateAddress, handle a special/simplified case when reading*/
348: /* video pointer in hi res (fix protection in 'My Socks Are Weapons' demo */
349: /* by 'Legacy'). */
1.1.1.24 root 350: /* 2015/08/18 [NP] In Video_CalculateAddress, handle the case when reading overlaps end */
1.1.1.23 root 351: /* of line / start of next line and STE's linewidth at $FF820F != 0. */
1.1.1.24 root 352: /* 2015/09/28 [NP] In Video_ScreenCounter_ReadByte, take VideoCounterDelayedOffset into */
353: /* account to handle the case where ff8205/07/09 are modified when display */
354: /* is ON and read just after (this is sometimes used to detect if the */
355: /* machine is an STF or an STE) (fix STE detection in the Menu screen of */
356: /* the 'Place To Be Again' demo). */
357: /* 2015/09/29 [NP] Add different values for RestartVideoCounterCycle when using 60 Hz */
358: /* (fix 60 Hz spectrum 512 double buffer image in the intro of the */
359: /* 'Place To Be Again' demo) */
360: /* 2015/10/30 [NP] In Video_CopyScreenLineColor, correctly show the last 8 pixels on */
361: /* the right when displaying an STE 224 byte overscan line containing */
362: /* 416 usable pixels (eg 'Drone' by DHS, 'PhotoChrome Viewer' by DML) */
363: /* 2015/11/15 [NP] Call ShortCut_ActKey() earlier in Video_InterruptHandler_VBL, before */
364: /* acknowledging it to get more consistent state in memory snapshots */
365: /* created using shortcut keys. */
366: /* 2015/12/31 [NP] More accurate reloading of $ff8201/03 into $ff8205/07/09 at line 310 on */
367: /* cycle 48 (STF 50 Hz) and line 260 cycle 48 (STF 60 Hz) (used in Intro */
368: /* and Menu of 'Dark Side Of The Spoon' by ULM). */
369: /* 2016/01/15 [NP] Don't call Video_AddInterruptHBL if we're handling the special HBL to */
370: /* restart video counter (in case freq/res are modified between */
371: /* cycles 0 and 48) */
372: /* 2016/01/31 [NP] Video registers can be accessed at cycles 4n or 4n+2, except colors and */
373: /* resolution which must use M68000_SyncCpuBus_OnRead/WriteAccess() to be */
374: /* at 4n. This requires to use CPU in cycle exact mode. */
375: /* 2016/02/04 [NP] On STF, the switch back to low res to remove left border should be made */
376: /* at pos > 4. If made before, left border is not removed. */
377: /* 2016/02/04 [NP] Add support for left+2 at 50 Hz if switch back to 50 Hz is made at */
378: /* pos 54 (requires 2 cycle precision in CE mode) (wsdetect by Troed/Sync, */
379: /* report STF as WS1) */
380: /* 2016/02/19 [NP] Improve blank line detection when switching to 60 Hz at cycle 28 and */
381: /* switching back to 50 Hz before cycle 56 (allow combining blank line */
382: /* and left+2 in 'Closure' by Sync) */
383: /* 2016/02/22 [NP] Better accuracy for the number of cycles per VBL when mixing 50 Hz and */
384: /* 60 Hz line. Instead of starting next VBL from the current one and */
385: /* updating its delay on each freq change, we start it from the last HBL */
386: /* of the screen (which should be more similar to how real HW works) */
387: /* (eg 160240 cycles per VBL in "keyboard no jitter" test program by NyH). */
388: /* 2016/02/26 [NP] Add support for 'remove left' including a med res stabiliser, by doing */
389: /* hi/med/low switches at cycles 0/8/16 ('Closure' by Sync). */
390: /* 2016/03/15 [NP] Allow to have different video timings for all machines/wakeup states */
391: /* 2016/05/10 [NP] Rewrite freq/res registers handling into Video_Update_Glue_State(), */
392: /* taking all STF wakeup states into account ('Closure' by Sync and */
393: /* 'shforstv' by Paulo Simoes work in all 4 wakeup states, TCB screen in */
394: /* Swedish New Year has a centered logo in WS2, Nostalgic demo main menu */
395: /* by Oxygene is correctly broken in WS2/4). */
396: /* 2016/05/25 [NP] Rewrite top/bottom border handling into Video_Update_Glue_State() */
397: /* 2016/06/06 [NP] Add preliminary support for STE timings in Video_Update_Glue_State(). */
398: /* Similar to STF timings, with support for STE specific "left+20". */
399: /* (fix "We Were @" by Oxygene, "More or less 0", "Sea of colors" and more */
400: /* demos by DHS) */
401: /* 2016/06/07 [NP] In Video_Update_Glue_State(), add STE timings for "0 byte" and "left+2" */
402: /* lines (fix "LoSTE" and "Closure" by Sync in STE mode) */
1.1.1.25! root 403: /* 2017/02/24 [NP] If a line has right-2/left+2 on a 60 Hz screen, then it should be */
! 404: /* considered as a normal 60 Hz line (fix 60 Hz bottom border removal in */
! 405: /* 'Protracker 2.1' and 'Neochrome master' when running at 60 Hz) */
! 406: /* 2017/02/24 [NP] Don't change DE_end / right-2 unless DE_start has been set, else it */
! 407: /* could mean the freq change overlaps with the next HBL handler on line */
! 408: /* n+1 (eg: move at cycle 508 line n that changes freq to 60Hz at cycle 4 */
! 409: /* line n+1, before handler for new HBL n+1 was called) (fix wrong right-2 */
! 410: /* during 60 Hz bottom border removal in 'Protracker 2.1' and 'Neochrome */
! 411: /* master' when running at 60 Hz) */
! 412: /* 2017/05/09 [NP] Add support for 4 pixel hardscroll propagated on every line (fix */
! 413: /* beescrn4.prg by Paulo Simoes) */
! 414: /* 2017/10/30 [NP] In Video_ResetShifterTimings, use VIDEO_HEIGHT_HBL_MONO only if screen */
! 415: /* is in Mono mode and video resolution=high (fix 'Audio Sculpture' when */
! 416: /* running in Mono mode) */
1.1.1.11 root 417:
1.1.1.12 root 418:
1.1.1.15 root 419: const char Video_fileid[] = "Hatari video.c : " __DATE__ " " __TIME__;
1.1 root 420:
1.1.1.7 root 421: #include <SDL_endian.h>
1.1.1.4 root 422:
1.1 root 423: #include "main.h"
1.1.1.6 root 424: #include "configuration.h"
1.1.1.10 root 425: #include "cycles.h"
1.1 root 426: #include "fdc.h"
1.1.1.16 root 427: #include "cycInt.h"
1.1.1.8 root 428: #include "ioMem.h"
1.1.1.4 root 429: #include "keymap.h"
1.1 root 430: #include "m68000.h"
1.1.1.21 root 431: #include "hatari-glue.h"
1.1 root 432: #include "memorySnapShot.h"
433: #include "mfp.h"
1.1.1.11 root 434: #include "printer.h"
1.1 root 435: #include "screen.h"
1.1.1.24 root 436: #include "screenConvert.h"
1.1.1.13 root 437: #include "screenSnapShot.h"
1.1 root 438: #include "shortcut.h"
439: #include "sound.h"
1.1.1.17 root 440: #include "dmaSnd.h"
1.1 root 441: #include "spec512.h"
442: #include "stMemory.h"
443: #include "vdi.h"
444: #include "video.h"
445: #include "ymFormat.h"
1.1.1.11 root 446: #include "falcon/videl.h"
1.1.1.25! root 447: #include "blitter.h"
1.1.1.16 root 448: #include "avi_record.h"
1.1.1.21 root 449: #include "ikbd.h"
1.1.1.22 root 450: #include "floppy_ipf.h"
1.1.1.25! root 451: #include "statusbar.h"
1.1.1.4 root 452:
453:
1.1.1.11 root 454: /* The border's mask allows to keep track of all the border tricks */
455: /* applied to one video line. The masks for all lines are stored in the array */
456: /* ScreenBorderMask[]. */
457: /* - bits 0-15 are used to describe the border tricks. */
458: /* - bits 20-23 are used to store the bytes offset to apply for some particular */
1.1.1.15 root 459: /* tricks (for example med res overscan can shift display by 0 or 2 bytes */
460: /* depending on when the switch to med res is done after removing the left */
1.1.1.11 root 461: /* border). */
462:
1.1.1.14 root 463: #define BORDERMASK_NONE 0x00 /* no effect on this line */
1.1.1.11 root 464: #define BORDERMASK_LEFT_OFF 0x01 /* removal of left border with hi/lo res switch -> +26 bytes */
465: #define BORDERMASK_LEFT_PLUS_2 0x02 /* line starts earlier in 60 Hz -> +2 bytes */
466: #define BORDERMASK_STOP_MIDDLE 0x04 /* line ends in hires at cycle 160 -> -106 bytes */
467: #define BORDERMASK_RIGHT_MINUS_2 0x08 /* line ends earlier in 60 Hz -> -2 bytes */
468: #define BORDERMASK_RIGHT_OFF 0x10 /* removal of right border -> +44 bytes */
469: #define BORDERMASK_RIGHT_OFF_FULL 0x20 /* full removal of right border and next left border -> +22 bytes */
1.1.1.15 root 470: #define BORDERMASK_OVERSCAN_MED_RES 0x40 /* some borders were removed and the line is in med res instead of low res */
1.1.1.11 root 471: #define BORDERMASK_EMPTY_LINE 0x80 /* 60/50 Hz switch prevents the line to start, video counter is not incremented */
1.1.1.15 root 472: #define BORDERMASK_LEFT_OFF_MED 0x100 /* removal of left border with hi/med res switch -> +26 bytes (for 4 pixels hardware scrolling) */
1.1.1.16 root 473: #define BORDERMASK_LEFT_OFF_2_STE 0x200 /* shorter removal of left border with hi/lo res switch -> +20 bytes (STE only)*/
1.1.1.18 root 474: #define BORDERMASK_BLANK_LINE 0x400 /* 60/50 Hz switch blanks the rest of the line, but video counter is still incremented */
1.1.1.11 root 475:
1.1.1.24 root 476: #define BORDERMASK_NO_DE 0x800
477: #define BORDERMASK_BLANK 0x1000
478: #define BORDERMASK_NO_COUNT 0x2000
479: #define BORDERMASK_NO_SYNC 0x4000
480: #define BORDERMASK_SYNC_HIGH 0x8000
481:
482: //#define STF_SHORT_TOP
1.1.1.11 root 483:
484: int STRes = ST_LOW_RES; /* current ST resolution */
485: int TTRes; /* TT shifter resolution mode */
1.1.1.13 root 486: int nFrameSkips; /* speed up by skipping video frames */
1.1.1.9 root 487:
1.1.1.13 root 488: bool bUseHighRes; /* Use hi-res (ie Mono monitor) */
1.1.1.24 root 489: int VerticalOverscan; /* V_OVERSCAN_xxxx for current display frame */
1.1.1.17 root 490: Uint16 HBLPalettes[HBL_PALETTE_LINES]; /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
1.1.1.8 root 491: Uint16 *pHBLPalettes; /* Pointer to current palette lists, one per HBL */
1.1.1.17 root 492: Uint32 HBLPaletteMasks[HBL_PALETTE_MASKS]; /* Bit mask of palette colours changes, top bit set is resolution change */
1.1.1.9 root 493: Uint32 *pHBLPaletteMasks;
1.1.1.24 root 494: int nScreenRefreshRate = VIDEO_50HZ; /* 50 or 60 Hz in color, 71 Hz in mono */
1.1.1.8 root 495: Uint32 VideoBase; /* Base address in ST Ram for screen (read on each VBL) */
1.1.1.9 root 496:
1.1.1.11 root 497: int nVBLs; /* VBL Counter */
498: int nHBL; /* HBL line */
499: int nStartHBL; /* Start HBL for visible screen */
500: int nEndHBL; /* End HBL for visible screen */
1.1.1.10 root 501: int nScanlinesPerFrame = 313; /* Number of scan lines per frame */
502: int nCyclesPerLine = 512; /* Cycles per horizontal line scan */
1.1.1.13 root 503: static int nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ; /* The first line of the ST screen that is copied to the PC screen buffer */
504: static int nLastVisibleHbl = FIRST_VISIBLE_HBL_50HZ+NUM_VISIBLE_LINES; /* The last line of the ST screen that is copied to the PC screen buffer */
1.1.1.16 root 505: static int CyclesPerVBL = 313*512; /* Number of cycles per VBL */
1.1.1.10 root 506:
1.1.1.13 root 507: static Uint8 HWScrollCount; /* HW scroll pixel offset, STE only (0...15) */
508: static int NewHWScrollCount = -1; /* Used in STE mode when writing to the scrolling registers $ff8264/65 */
509: static Uint8 HWScrollPrefetch; /* 0 when scrolling with $ff8264, 1 when scrolling with $ff8265 */
510: static int NewHWScrollPrefetch = -1; /* Used in STE mode when writing to the scrolling registers $ff8264/65 */
511: static Uint8 LineWidth; /* Scan line width add, STe only (words, minus 1) */
512: static int NewLineWidth = -1; /* Used in STE mode when writing to the line width register $ff820f */
1.1.1.16 root 513: static int VideoCounterDelayedOffset = 0; /* Used in STE mode when changing video counter while display is on */
514: static Uint8 *pVideoRasterDelayed = NULL; /* Used in STE mode when changing video counter while display is off in the right border */
1.1.1.13 root 515: static Uint8 *pVideoRaster; /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
1.1.1.15 root 516: static bool bSteBorderFlag; /* true when screen width has been switched to 336 (e.g. in Obsession) */
1.1.1.13 root 517: static int NewSteBorderFlag = -1; /* New value for next line */
1.1.1.24 root 518: static bool bTTColorsSync; /* whether TT colors need conversion to SDL */
1.1.1.21 root 519:
1.1.1.24 root 520: int TTSpecialVideoMode; /* TT special video mode */
521: static int nPrevTTSpecialVideoMode; /* TT special video mode */
1.1 root 522:
1.1.1.17 root 523: static int LastCycleScroll8264; /* value of Cycles_GetCounterOnWriteAccess last time ff8264 was set for the current VBL */
524: static int LastCycleScroll8265; /* value of Cycles_GetCounterOnWriteAccess last time ff8265 was set for the current VBL */
525:
1.1.1.24 root 526: static bool RestartVideoCounter = false; /* true when reaching the HBL to restart video counter */
1.1.1.13 root 527:
1.1.1.24 root 528: int LineTimerBPos = LINE_END_CYCLE_50 + TIMERB_VIDEO_CYCLE_OFFSET; /* position of the Timer B interrupt on active lines */
1.1.1.15 root 529: int TimerBEventCountCycleStart = -1; /* value of Cycles_GetCounterOnWriteAccess last time timer B was started for the current VBL */
1.1 root 530:
1.1.1.20 root 531: int HblJitterIndex = 0;
532: const int HblJitterArray[] = {
533: 8,4,4,0,0 /* measured on STF */
534: };
535: const int HblJitterArrayPending[] = {
536: 4,4,4,4,4 // { 8,8,12,8,12 }; /* measured on STF, not always accurate */
537: };
538: int VblJitterIndex = 0;
539: const int VblJitterArray[] = {
540: 8,0,4,0,4 /* measured on STF */
541: };
542: const int VblJitterArrayPending[] = {
543: 8,8,12,8,12 /* not verified on STF, use the same as HBL */
544: };
1.1.1.14 root 545:
1.1.1.23 root 546: static int BlankLines = 0; /* Number of empty line with no signal (by switching hi/lo near cycles 500) */
1.1.1.12 root 547:
1.1.1.15 root 548:
549: typedef struct
550: {
551: int VBL; /* VBL for this Pos (or -1 if Pos not defined for now) */
552: int FrameCycles; /* Number of cycles since this VBL */
553: int HBL; /* HBL in the VBL */
554: int LineCycles; /* cycles in the HBL */
555: } SHIFTER_POS;
556:
557:
558: typedef struct
559: {
560: int StartCycle; /* first cycle of this line, as returned by Cycles_GetCounter */
561:
562: Uint32 BorderMask; /* borders' states for this line */
563: int DisplayPixelShift; /* number of pixels to shift the whole line (<0 shift to the left, >0 shift to the right) */
564: /* On STF, this is obtained when switching hi/med for a variable number of cycles, */
565: /* but just removing left border will shift the line too. */
566:
567: int DisplayStartCycle; /* cycle where display starts for this line (0-512) : 0, 52 or 56 */
568: int DisplayEndCycle; /* cycle where display ends for this line (0-512) : 0, 160, 372, 376, 460 or 512 */
569: int DisplayBytes; /* how many bytes to display for this line */
570:
571: } SHIFTER_LINE;
572:
573:
574: typedef struct
575: {
576: int HBL_CyclePos; /* cycle position for the HBL int (depends on freq/res) */
577: int TimerB_CyclePos; /* cycle position for the Timer B int (depends on freq/res) */
578:
579: int Freq; /* value of ff820a & 2, or -1 if not set */
580: int Res; /* value of ff8260 & 3, or -1 if not set */
581: SHIFTER_POS FreqPos50; /* position of latest freq change to 50 Hz*/
582: SHIFTER_POS FreqPos60; /* position of latest freq change to 60 Hz*/
583: SHIFTER_POS ResPosLo; /* position of latest change to low res */
584: SHIFTER_POS ResPosMed; /* position of latest change to med res */
585: SHIFTER_POS ResPosHi; /* position of latest change to high res */
586:
587: SHIFTER_POS Scroll8264Pos; /* position of latest write to $ff8264 */
588: SHIFTER_POS Scroll8265Pos; /* position of latest write to $ff8265 */
589:
590: SHIFTER_LINE ShifterLines[ MAX_SCANLINES_PER_FRAME ];
591: } SHIFTER_FRAME;
592:
593:
1.1.1.23 root 594: static SHIFTER_FRAME ShifterFrame;
1.1.1.15 root 595:
596:
1.1.1.24 root 597: #define VIDEO_TIMING_STF_WS1 0
598: #define VIDEO_TIMING_STF_WS2 1
599: #define VIDEO_TIMING_STF_WS3 2
600: #define VIDEO_TIMING_STF_WS4 3
601: #define VIDEO_TIMING_STE 4
602: #define VIDEO_TIMING_TT 5
603:
604: #define VIDEO_TIMING_MAX_NB 6 /* Number of different timings structs we need to store */
605:
606: #define VIDEO_TIMING_DEFAULT VIDEO_TIMING_STF_WS3
607:
608: typedef struct
609: {
610: const char *VideoTimingName;
611:
612: int Preload_Start_Hi; /* 0 (STE) */
613: int H_Start_Hi; /* 4 */
614: int Blank_Stop_Low_60; /* 24 */
615: int Blank_Stop_Low_50; /* 28 */
616: int Preload_Start_Low_60; /* 36 (STE) */
617: int H_Start_Low_60; /* 52 */
618: int Line_Set_Pal; /* 54 */
619: int Preload_Start_Low_50; /* 40 (STE) */
620: int H_Start_Low_50; /* 56 */
621: int H_Stop_Hi; /* 164 */
622: int Blank_Start_Hi; /* 184 */
623: int H_Stop_Low_60; /* 372 */
624: int H_Stop_Low_50; /* 376 */
625: int Blank_Start_Low; /* 450 */
626: int HSync_Start_Offset_Low; /* 462 or 458 */
627: int HSync_Stop_Offset_Low; /* 502 or 498 */
628:
629: int RemoveTopBorder_Pos; /* 502 */
630: int RemoveBottomBorder_Pos; /* 502 */
631:
632: int V_Start_Line_50; /* 63 or 63-16 */
633: int V_Start_Line_60; /* 34 */
634: int V_Start_Line_Hi; /* 34 */
635: int V_End_Line_50; /* 263 or 263-16 */
636: int V_End_Line_60; /* 234 */
637: int V_End_Line_Hi; /* 434 */
638: int V_End_Line_NoBottom_50; /* 310 */
639: int V_End_Line_NoBottom_60; /* 263 */
640:
641: int RestartVideoCounter_Line_60; /* 260 */
642: int RestartVideoCounter_Line_50; /* 310 */
1.1.1.25! root 643: int RestartVideoCounter_Pos; /* 56 */
1.1.1.24 root 644:
645: int VblVideoCycleOffset;
646: int Hbl_Int_Pos_Low_60; /* 508 */
647: int Hbl_Int_Pos_Low_50; /* 512 */
648: int Hbl_Int_Pos_Hi; /* 224 */
649:
650: } VIDEO_TIMING;
651:
652:
653: static VIDEO_TIMING VideoTimings[ VIDEO_TIMING_MAX_NB ];
654: static VIDEO_TIMING *pVideoTiming;
655: static int VideoTiming;
656:
657:
658: /* Convert a horizontal video position measured at 8 MHz on STF/STE */
659: /* to the equivalent number of cycles when CPU runs at 8/16/32 MHz */
660: #define VIDEO_HPOS_TO_CYCLE( pos ) ( pos << nCpuFreqShift )
661: #define VIDEO_CYCLE_TO_HPOS( cyc ) ( cyc >> nCpuFreqShift )
662:
1.1.1.15 root 663:
664: /*--------------------------------------------------------------*/
665: /* Local functions prototypes */
666: /*--------------------------------------------------------------*/
667:
1.1.1.24 root 668: static void Video_InitTimings_Copy ( VIDEO_TIMING *pSrc , VIDEO_TIMING *pDest , int inc );
1.1.1.15 root 669:
670: static Uint32 Video_CalculateAddress ( void );
1.1.1.16 root 671: static int Video_GetMMUStartCycle ( int DisplayStartCycle );
1.1.1.24 root 672: static void Video_WriteToGlueShifterRes ( Uint8 Res );
673: static void Video_Update_Glue_State ( int FrameCycles , int HblCounterVideo , int LineCycles , bool WriteToRes );
1.1.1.15 root 674:
1.1.1.24 root 675: static int Video_HBL_GetDefaultPos ( void );
676: static int Video_HBL_GetCurrentPos ( void );
677: static int Video_TimerB_GetPosFromDE ( int DE_start , int DE_end );
1.1.1.18 root 678: static int Video_TimerB_GetDefaultPos ( void );
1.1.1.15 root 679: static void Video_EndHBL ( void );
680: static void Video_StartHBL ( void );
681:
682: static void Video_StoreFirstLinePalette(void);
1.1.1.24 root 683: static void Video_StoreResolution(int y , bool start);
1.1.1.15 root 684: static void Video_CopyScreenLineMono(void);
685: static void Video_CopyScreenLineColor(void);
686: static void Video_SetHBLPaletteMaskPointers(void);
687:
688: static void Video_DrawScreen(void);
689:
690: static void Video_ResetShifterTimings(void);
691: static void Video_InitShifterLines(void);
1.1.1.24 root 692: static void Video_RestartVideoCounter(void);
1.1.1.15 root 693: static void Video_ClearOnVBL(void);
694:
1.1.1.24 root 695: static void Video_AddInterrupt ( int Line , int PosCycles , interrupt_id Handler );
696: static void Video_AddInterruptHBL ( int Line , int Pos );
1.1.1.15 root 697:
1.1.1.22 root 698: static void Video_ColorReg_WriteWord(void);
699: static void Video_ColorReg_ReadWord(void);
1.1.1.15 root 700:
701:
1.1 root 702: /*-----------------------------------------------------------------------*/
1.1.1.11 root 703: /**
704: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
705: */
1.1.1.13 root 706: void Video_MemorySnapShot_Capture(bool bSave)
1.1 root 707: {
1.1.1.23 root 708: Uint32 addr;
709:
1.1.1.11 root 710: /* Save/Restore details */
711: MemorySnapShot_Store(&TTRes, sizeof(TTRes));
712: MemorySnapShot_Store(&bUseHighRes, sizeof(bUseHighRes));
713: MemorySnapShot_Store(&nVBLs, sizeof(nVBLs));
714: MemorySnapShot_Store(&nHBL, sizeof(nHBL));
715: MemorySnapShot_Store(&nStartHBL, sizeof(nStartHBL));
716: MemorySnapShot_Store(&nEndHBL, sizeof(nEndHBL));
1.1.1.24 root 717: MemorySnapShot_Store(&VerticalOverscan, sizeof(VerticalOverscan));
1.1.1.11 root 718: MemorySnapShot_Store(HBLPalettes, sizeof(HBLPalettes));
719: MemorySnapShot_Store(HBLPaletteMasks, sizeof(HBLPaletteMasks));
720: MemorySnapShot_Store(&VideoBase, sizeof(VideoBase));
1.1.1.23 root 721: if ( bSave )
722: {
723: addr = pVideoRaster - STRam;
724: MemorySnapShot_Store(&addr, sizeof(addr));
725: }
726: else
727: {
728: MemorySnapShot_Store(&addr, sizeof(addr));
729: pVideoRaster = &STRam[VideoBase];
730: }
1.1.1.11 root 731: MemorySnapShot_Store(&LineWidth, sizeof(LineWidth));
732: MemorySnapShot_Store(&HWScrollCount, sizeof(HWScrollCount));
733: MemorySnapShot_Store(&nScanlinesPerFrame, sizeof(nScanlinesPerFrame));
734: MemorySnapShot_Store(&nCyclesPerLine, sizeof(nCyclesPerLine));
735: MemorySnapShot_Store(&nFirstVisibleHbl, sizeof(nFirstVisibleHbl));
736: MemorySnapShot_Store(&bSteBorderFlag, sizeof(bSteBorderFlag));
1.1.1.14 root 737: MemorySnapShot_Store(&HblJitterIndex, sizeof(HblJitterIndex));
738: MemorySnapShot_Store(&VblJitterIndex, sizeof(VblJitterIndex));
1.1.1.15 root 739: MemorySnapShot_Store(&ShifterFrame, sizeof(ShifterFrame));
1.1.1.21 root 740: MemorySnapShot_Store(&TTSpecialVideoMode, sizeof(TTSpecialVideoMode));
1.1.1.15 root 741: }
742:
743:
744: /*-----------------------------------------------------------------------*/
745: /**
746: * Reset video chip
747: */
748: void Video_Reset(void)
749: {
750: /* NOTE! Must reset all of these register type things here!!!! */
1.1.1.19 root 751: Video_Reset_Glue();
1.1.1.15 root 752:
753: /* Set system specific timings */
1.1.1.24 root 754: Video_SetTimings ( ConfigureParams.System.nMachineType , ConfigureParams.System.VideoTimingMode );
1.1.1.15 root 755:
756: /* Reset VBL counter */
757: nVBLs = 0;
758: /* Reset addresses */
759: VideoBase = 0L;
760:
761: /* Reset shifter's state variables */
762: ShifterFrame.Freq = -1;
763: ShifterFrame.Res = -1;
764: ShifterFrame.FreqPos50.VBL = -1;
765: ShifterFrame.FreqPos60.VBL = -1;
766: ShifterFrame.ResPosLo.VBL = -1;
767: ShifterFrame.ResPosMed.VBL = -1;
768: ShifterFrame.ResPosHi.VBL = -1;
769: ShifterFrame.Scroll8264Pos.VBL = -1;
770: ShifterFrame.Scroll8265Pos.VBL = -1;
771:
772: Video_InitShifterLines ();
773:
774: /* Reset STE screen variables */
775: LineWidth = 0;
776: HWScrollCount = 0;
777: bSteBorderFlag = false;
778:
779: NewLineWidth = -1; /* cancel pending modifications set before the reset */
780: NewHWScrollCount = -1;
781:
1.1.1.16 root 782: VideoCounterDelayedOffset = 0;
783: pVideoRasterDelayed = NULL;
784:
1.1.1.15 root 785: /* Reset jitter indexes */
786: HblJitterIndex = 0;
787: VblJitterIndex = 0;
788:
1.1.1.24 root 789: TTSpecialVideoMode = nPrevTTSpecialVideoMode = 0;
790:
1.1.1.15 root 791: /* Clear framecycles counter */
792: Cycles_SetCounter(CYCLES_COUNTER_VIDEO, 0);
793:
794: /* Clear ready for new VBL */
795: Video_ClearOnVBL();
796: }
797:
798:
799: /*-----------------------------------------------------------------------*/
800: /**
801: * Reset the GLUE chip responsible for generating the H/V sync signals.
802: * When the 68000 RESET instruction is called, frequency and resolution
803: * should be reset to 0.
804: */
805: void Video_Reset_Glue(void)
806: {
1.1.1.20 root 807: Uint8 VideoShifterByte;
808:
1.1.1.19 root 809: IoMem_WriteByte(0xff820a,0); /* Video frequency */
810:
811: /* Are we in high-res? */
812: if (bUseHighRes)
813: VideoShifterByte = ST_HIGH_RES; /* Mono monitor */
814: else
815: VideoShifterByte = ST_LOW_RES;
816: if (bUseVDIRes)
817: VideoShifterByte = VDIRes;
818:
819: IoMem_WriteByte(0xff8260, VideoShifterByte);
1.1 root 820: }
821:
1.1.1.8 root 822:
1.1 root 823: /*-----------------------------------------------------------------------*/
1.1.1.12 root 824: /*
1.1.1.24 root 825: * Init video timings values for all emulated machines.
826: * This should be called only once, when emulator starts
827: */
828: void Video_InitTimings(void)
829: {
830: VIDEO_TIMING *pVideoTiming1;
831: VIDEO_TIMING *pVideoTiming2;
832:
833: /* Init all timings for STF machine */
834: pVideoTiming1 = &VideoTimings[ VIDEO_TIMING_STF_WS1 ];
835:
836: /* Init all timings for WS1 mode */
837: pVideoTiming1->VideoTimingName = "WS1";
838: pVideoTiming1->H_Start_Hi = 4;
839: pVideoTiming1->Blank_Stop_Low_60 = 24;
840: pVideoTiming1->Blank_Stop_Low_50 = 28;
841: pVideoTiming1->H_Start_Low_60 = 52;
842: pVideoTiming1->Line_Set_Pal = 54;
843: pVideoTiming1->H_Start_Low_50 = 56;
844: pVideoTiming1->H_Stop_Hi = 164;
845: pVideoTiming1->Blank_Start_Hi = 184;
846: pVideoTiming1->H_Stop_Low_60 = 372;
847: pVideoTiming1->H_Stop_Low_50 = 376;
848: pVideoTiming1->Blank_Start_Low = 450;
849: pVideoTiming1->HSync_Start_Offset_Low = -50; /* 458/462 (line cycles-50) */
850: pVideoTiming1->HSync_Stop_Offset_Low = -10; /* 498/502 (line cycles-10) */
851: pVideoTiming1->RemoveTopBorder_Pos = 502;
852: pVideoTiming1->RemoveBottomBorder_Pos = 502;
853: pVideoTiming1->V_Start_Line_50 = VIDEO_START_HBL_50HZ; /* 63 or 63-16 */
854: pVideoTiming1->V_Start_Line_60 = VIDEO_START_HBL_60HZ; /* 34 */
855: pVideoTiming1->V_Start_Line_Hi = VIDEO_START_HBL_71HZ; /* 34 */
856: pVideoTiming1->V_End_Line_50 = VIDEO_END_HBL_50HZ; /* 263 or 263-16 */
857: pVideoTiming1->V_End_Line_60 = VIDEO_END_HBL_60HZ; /* 234 */
858: pVideoTiming1->V_End_Line_Hi = VIDEO_END_HBL_71HZ; /* 434 */
859: pVideoTiming1->V_End_Line_NoBottom_50 = pVideoTiming1->V_End_Line_50 + VIDEO_HEIGHT_BOTTOM_50HZ; /* 310 (TODO:check on real HW) */
860: pVideoTiming1->V_End_Line_NoBottom_60 = pVideoTiming1->V_End_Line_60 + VIDEO_HEIGHT_BOTTOM_60HZ; /* 263 (TODO:check on real HW) */
861: pVideoTiming1->RestartVideoCounter_Line_60 = RESTART_VIDEO_COUNTER_LINE_60HZ; /* 260 */
862: pVideoTiming1->RestartVideoCounter_Line_50 = RESTART_VIDEO_COUNTER_LINE_50HZ; /* 310 */
1.1.1.25! root 863: pVideoTiming1->RestartVideoCounter_Pos = RESTART_VIDEO_COUNTER_CYCLE_STF; /* 56 */
1.1.1.24 root 864: pVideoTiming1->VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STF - 4;
865: pVideoTiming1->Hbl_Int_Pos_Low_60 = CYCLES_PER_LINE_60HZ - 4;
866: pVideoTiming1->Hbl_Int_Pos_Low_50 = CYCLES_PER_LINE_50HZ - 4;
867: pVideoTiming1->Hbl_Int_Pos_Hi = CYCLES_PER_LINE_71HZ - 4;
868: #ifdef STF_SHORT_TOP
869: pVideoTiming1->V_Start_Line_50 -= 16;
870: pVideoTiming1->V_End_Line_50 -= 16;
871: #endif
872: /* WS2/WS3/WS4 timings are derived from WS1 with an increment */
873: pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_STF_WS2 ];
874: pVideoTiming2->VideoTimingName = "WS2";
875: Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 3 );
876: pVideoTiming2->VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STF;
877: pVideoTiming2->Hbl_Int_Pos_Low_60 = CYCLES_PER_LINE_60HZ;
878: pVideoTiming2->Hbl_Int_Pos_Low_50 = CYCLES_PER_LINE_50HZ;
879: pVideoTiming2->Hbl_Int_Pos_Hi = CYCLES_PER_LINE_71HZ;
880:
881: pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_STF_WS3 ];
882: pVideoTiming2->VideoTimingName = "WS3";
883: Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 1 );
884: pVideoTiming2->VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STF;
885: pVideoTiming2->Hbl_Int_Pos_Low_60 = CYCLES_PER_LINE_60HZ;
886: pVideoTiming2->Hbl_Int_Pos_Low_50 = CYCLES_PER_LINE_50HZ;
887: pVideoTiming2->Hbl_Int_Pos_Hi = CYCLES_PER_LINE_71HZ;
888:
889: pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_STF_WS4 ];
890: pVideoTiming2->VideoTimingName = "WS4";
891: Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 2 );
892: pVideoTiming2->VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STF;
893: pVideoTiming2->Hbl_Int_Pos_Low_60 = CYCLES_PER_LINE_60HZ;
894: pVideoTiming2->Hbl_Int_Pos_Low_50 = CYCLES_PER_LINE_50HZ;
895: pVideoTiming2->Hbl_Int_Pos_Hi = CYCLES_PER_LINE_71HZ;
896:
897:
898: /* Init all timings for STE machine */
899: pVideoTiming1 = &VideoTimings[ VIDEO_TIMING_STE ];
900: pVideoTiming1->VideoTimingName = "STE";
901: pVideoTiming1->Preload_Start_Hi = 0;
902: pVideoTiming1->H_Start_Hi = 4;
903: pVideoTiming1->Blank_Stop_Low_60 = 24;
904: pVideoTiming1->Blank_Stop_Low_50 = 28;
905: pVideoTiming1->Preload_Start_Low_60 = 36;
906: pVideoTiming1->H_Start_Low_60 = 52;
907: pVideoTiming1->Line_Set_Pal = 56;
908: pVideoTiming1->Preload_Start_Low_50 = 40;
909: pVideoTiming1->H_Start_Low_50 = 56;
910: pVideoTiming1->H_Stop_Hi = 164;
911: pVideoTiming1->Blank_Start_Hi = 184;
912: pVideoTiming1->H_Stop_Low_60 = 372;
913: pVideoTiming1->H_Stop_Low_50 = 376;
914: pVideoTiming1->Blank_Start_Low = 448;
915: pVideoTiming1->HSync_Start_Offset_Low = -52; /* 456/460 (line cycles-52) */
916: pVideoTiming1->HSync_Stop_Offset_Low = -12; /* 496/500 (line cycles-12) */
917: pVideoTiming1->RemoveTopBorder_Pos = 500;
918: pVideoTiming1->RemoveBottomBorder_Pos = 500;
919: pVideoTiming1->V_Start_Line_50 = VIDEO_START_HBL_50HZ; /* 63 */
920: pVideoTiming1->V_Start_Line_60 = VIDEO_START_HBL_60HZ; /* 34 */
921: pVideoTiming1->V_Start_Line_Hi = VIDEO_START_HBL_71HZ; /* 34 */
922: pVideoTiming1->V_End_Line_50 = VIDEO_END_HBL_50HZ; /* 263 */
923: pVideoTiming1->V_End_Line_60 = VIDEO_END_HBL_60HZ; /* 234 */
924: pVideoTiming1->V_End_Line_Hi = VIDEO_END_HBL_71HZ; /* 434 */
925: pVideoTiming1->V_End_Line_NoBottom_50 = pVideoTiming1->V_End_Line_50 + VIDEO_HEIGHT_BOTTOM_50HZ; /* 310 */
926: pVideoTiming1->V_End_Line_NoBottom_60 = pVideoTiming1->V_End_Line_60 + VIDEO_HEIGHT_BOTTOM_60HZ; /* 263 */
927: pVideoTiming1->RestartVideoCounter_Line_60 = RESTART_VIDEO_COUNTER_LINE_60HZ; /* 260 */
928: pVideoTiming1->RestartVideoCounter_Line_50 = RESTART_VIDEO_COUNTER_LINE_50HZ; /* 310 */
1.1.1.25! root 929: pVideoTiming1->RestartVideoCounter_Pos = RESTART_VIDEO_COUNTER_CYCLE_STE; /* 60 */
! 930: pVideoTiming1->VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STE;
1.1.1.24 root 931: pVideoTiming1->Hbl_Int_Pos_Low_60 = CYCLES_PER_LINE_60HZ;
932: pVideoTiming1->Hbl_Int_Pos_Low_50 = CYCLES_PER_LINE_50HZ;
933: pVideoTiming1->Hbl_Int_Pos_Hi = CYCLES_PER_LINE_71HZ;
934:
935: /* Use the STE timings for TT */
936: pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_TT ];
937: pVideoTiming2->VideoTimingName = "TT";
938: Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 0 );
939:
940: /* Set timings to a default mode (this will be overriden later when choosing the emulated machine) */
941: pVideoTiming = &VideoTimings[ VIDEO_TIMING_DEFAULT ];
942: }
943:
944:
945:
946: /*-----------------------------------------------------------------------*/
947: /*
948: * Copy some video timings, adding 'inc' to the values that depend on
949: * wakeup state.
950: */
951: static void Video_InitTimings_Copy ( VIDEO_TIMING *pSrc , VIDEO_TIMING *pDest , int inc )
952: {
953: pDest->Preload_Start_Hi = pSrc->Preload_Start_Hi + inc;
954: pDest->H_Start_Hi = pSrc->H_Start_Hi + inc;
955: pDest->Blank_Stop_Low_60 = pSrc->Blank_Stop_Low_60 + inc;
956: pDest->Blank_Stop_Low_50 = pSrc->Blank_Stop_Low_50 + inc;
957: pDest->Preload_Start_Low_60 = pSrc->Preload_Start_Low_60 + inc;
958: pDest->H_Start_Low_60 = pSrc->H_Start_Low_60 + inc;
959: pDest->Line_Set_Pal = pSrc->Line_Set_Pal + inc;
960: pDest->Preload_Start_Low_50 = pSrc->Preload_Start_Low_50 + inc;
961: pDest->H_Start_Low_50 = pSrc->H_Start_Low_50 + inc;
962: pDest->H_Stop_Hi = pSrc->H_Stop_Hi + inc;
963: pDest->Blank_Start_Hi = pSrc->Blank_Start_Hi + inc;
964: pDest->H_Stop_Low_60 = pSrc->H_Stop_Low_60 + inc;
965: pDest->H_Stop_Low_50 = pSrc->H_Stop_Low_50 + inc;
966: pDest->Blank_Start_Low = pSrc->Blank_Start_Low + inc;
967: pDest->HSync_Start_Offset_Low = pSrc->HSync_Start_Offset_Low + inc;
968: pDest->HSync_Stop_Offset_Low = pSrc->HSync_Stop_Offset_Low + inc;
969: pDest->RemoveTopBorder_Pos = pSrc->RemoveTopBorder_Pos + inc;
970: pDest->RemoveBottomBorder_Pos = pSrc->RemoveBottomBorder_Pos + inc;
971:
972: pDest->V_Start_Line_50 = pSrc->V_Start_Line_50;
973: pDest->V_Start_Line_60 = pSrc->V_Start_Line_60;
974: pDest->V_Start_Line_Hi = pSrc->V_Start_Line_Hi;
975: pDest->V_End_Line_50 = pSrc->V_End_Line_50;
976: pDest->V_End_Line_60 = pSrc->V_End_Line_60;
977: pDest->V_End_Line_Hi = pSrc->V_End_Line_Hi;
978: pDest->V_End_Line_NoBottom_50 = pSrc->V_End_Line_NoBottom_50;
979: pDest->V_End_Line_NoBottom_60 = pSrc->V_End_Line_NoBottom_60;
980:
981: pDest->RestartVideoCounter_Line_60 = pSrc->RestartVideoCounter_Line_60;
982: pDest->RestartVideoCounter_Line_50 = pSrc->RestartVideoCounter_Line_50;
983: pDest->RestartVideoCounter_Pos = pSrc->RestartVideoCounter_Pos;
984:
985: pDest->VblVideoCycleOffset = pSrc->VblVideoCycleOffset;
986: pDest->Hbl_Int_Pos_Low_60 = pSrc->Hbl_Int_Pos_Low_60;
987: pDest->Hbl_Int_Pos_Low_50 = pSrc->Hbl_Int_Pos_Low_50;
988: pDest->Hbl_Int_Pos_Hi = pSrc->Hbl_Int_Pos_Hi;
989: }
990:
991:
992:
993:
994: /*-----------------------------------------------------------------------*/
995: /*
1.1.1.12 root 996: * Set specific video timings, depending on the system being emulated.
1.1.1.24 root 997: * - STF is known to have 4 possible states depending on the MMU/GLUE synchronisation
998: * - STE has MMU and GLUE merged in the same chip, so there's only 1 state
999: * (other video differences are sometimes visible on STF/STE, so there might be
1000: * more states, affecting the shifter for example)
1.1.1.12 root 1001: */
1.1.1.24 root 1002: void Video_SetTimings( MACHINETYPE MachineType , VIDEOTIMINGMODE Mode )
1.1.1.12 root 1003: {
1.1.1.24 root 1004: /* Default timing for TT/Falcon (not really important */
1005: /* as TT/Falcon don't use cycle precise video effects) */
1006: VideoTiming = VIDEO_TIMING_DEFAULT;
1007:
1008: if ( ( MachineType == MACHINE_STE ) || ( MachineType == MACHINE_MEGA_STE ) )
1009: VideoTiming = VIDEO_TIMING_STE; /* Only one choice for STE */
1010:
1011: else if ( MachineType == MACHINE_TT )
1012: VideoTiming = VIDEO_TIMING_TT; /* Only one choice for TT (same values as STE) */
1013:
1014: else if ( ( MachineType == MACHINE_ST ) || ( MachineType == MACHINE_MEGA_ST ) ) /* 4 wakeup states are possible for STF */
1.1.1.19 root 1015: {
1.1.1.24 root 1016: if ( Mode == VIDEO_TIMING_MODE_RANDOM )
1017: Mode = VIDEO_TIMING_MODE_WS1 + rand() % 4; /* random between the 4 modes WS1, WS2, WS3, WS4 */
1018:
1019: if ( Mode == VIDEO_TIMING_MODE_WS1 ) VideoTiming = VIDEO_TIMING_STF_WS1;
1020: else if ( Mode == VIDEO_TIMING_MODE_WS2 ) VideoTiming = VIDEO_TIMING_STF_WS2;
1021: else if ( Mode == VIDEO_TIMING_MODE_WS3 ) VideoTiming = VIDEO_TIMING_STF_WS3;
1022: else VideoTiming = VIDEO_TIMING_STF_WS4;
1.1.1.19 root 1023: }
1.1.1.24 root 1024:
1025: pVideoTiming = &VideoTimings[ VideoTiming ];
1.1.1.25! root 1026:
! 1027: Log_Printf(LOG_DEBUG, "Video_SetSystemTimings %d %d -> %d (%s) %d %d %d\n", MachineType, Mode,
! 1028: VideoTiming, pVideoTiming->VideoTimingName, pVideoTiming->RemoveTopBorder_Pos,
! 1029: pVideoTiming->RemoveBottomBorder_Pos, pVideoTiming->VblVideoCycleOffset);
1.1.1.24 root 1030: }
1031:
1032:
1033: const char *Video_GetTimings_Name ( void )
1034: {
1035: return pVideoTiming->VideoTimingName;
1.1.1.12 root 1036: }
1037:
1038:
1039: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1040: /**
1.1.1.15 root 1041: * Convert the elapsed number of cycles since the start of the VBL
1042: * into the corresponding HBL number and the cycle position in the current
1043: * HBL. We use the starting cycle position of the closest HBL to compute
1044: * the cycle position on the line (this allows to mix lines with different
1045: * values for nCyclesPerLine).
1046: * We can have 2 cases on the limit where the real video line count can be
1047: * different from nHBL :
1048: * - when reading video address between cycle 0 and 12, LineCycle will be <0,
1049: * so we need to use the data from line nHBL-1
1050: * - if LineCycle >= nCyclesPerLine, this means the HBL int was not processed
1051: * yet, so the video line number is in fact nHBL+1
1052: */
1053:
1054: void Video_ConvertPosition ( int FrameCycles , int *pHBL , int *pLineCycles )
1055: {
1.1.1.24 root 1056: if ( ( nHBL == nScanlinesPerFrame ) /* rare case between end of last hbl and start of next VBL (during 64 cycles) */
1057: && ( Config_IsMachineST() || Config_IsMachineSTE() ) ) /* for example, cycle 160336 will give HBL=0 and LineCycles=80 */
1.1.1.15 root 1058: {
1.1.1.24 root 1059: *pHBL = 0;
1060: *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle - nCyclesPerLine;
1061: if ( *pLineCycles < 0 ) /* reading before end of last hbl (possible in WS1) */
1062: {
1063: *pHBL = nHBL-1;
1064: *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle;
1065: }
1.1.1.16 root 1066: //fprintf ( stderr , "out of vbl FrameCycles %d CyclesPerVBL %d nHBL=%d %d %d\n" , FrameCycles , CyclesPerVBL, nHBL , *pHBL , *pLineCycles );
1.1.1.15 root 1067: }
1.1.1.16 root 1068:
1069: else /* most common case */
1.1.1.15 root 1070: {
1.1.1.16 root 1071: *pHBL = nHBL;
1072: *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL ].StartCycle;
1.1.1.24 root 1073: // fprintf ( stderr , "conv pos nHBL=%d %d %d %d\n" , nHBL , FrameCycles , *pLineCycles , ShifterFrame.ShifterLines[ nHBL ].StartCycle );
1.1.1.15 root 1074:
1.1.1.16 root 1075: if ( *pLineCycles < 0 ) /* reading from the previous video line */
1076: {
1077: *pHBL = nHBL-1;
1078: *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle;
1079: }
1080:
1081: else if ( *pLineCycles >= nCyclesPerLine ) /* reading on the next line, but HBL int was delayed */
1082: {
1083: *pHBL = nHBL+1;
1084: *pLineCycles -= nCyclesPerLine;
1085: }
1086: }
1.1.1.15 root 1087:
1088: if ( *pLineCycles < 0 )
1.1.1.24 root 1089: fprintf ( stderr , "bug nHBL=%d %d %d %d\n" , nHBL , FrameCycles , *pHBL , *pLineCycles );
1.1.1.15 root 1090:
1091: //if ( ( *pHBL != FrameCycles / nCyclesPerLine ) || ( *pLineCycles != FrameCycles % nCyclesPerLine ) )
1092: // LOG_TRACE ( TRACE_VIDEO_ADDR , "conv pos %d %d - %d %d\n" , *pHBL , FrameCycles / nCyclesPerLine , *pLineCycles , FrameCycles % nCyclesPerLine );
1093: // LOG_TRACE ( TRACE_VIDEO_ADDR , "conv pos %d %d %d\n" , FrameCycles , *pHBL , *pLineCycles );
1094: }
1095:
1096:
1097: void Video_GetPosition ( int *pFrameCycles , int *pHBL , int *pLineCycles )
1098: {
1099: *pFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
1100: Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
1101: }
1102:
1103:
1104: void Video_GetPosition_OnWriteAccess ( int *pFrameCycles , int *pHBL , int *pLineCycles )
1105: {
1106: *pFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
1107: Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
1108: }
1109:
1110:
1111: void Video_GetPosition_OnReadAccess ( int *pFrameCycles , int *pHBL , int *pLineCycles )
1112: {
1113: *pFrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO);
1114: Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
1115: }
1116:
1117:
1118: /*-----------------------------------------------------------------------*/
1119: /**
1.1.1.11 root 1120: * Calculate and return video address pointer.
1121: */
1.1.1.15 root 1122: static Uint32 Video_CalculateAddress ( void )
1.1 root 1123: {
1.1.1.15 root 1124: int FrameCycles, HblCounterVideo, LineCycles;
1125: int X, NbBytes;
1.1.1.11 root 1126: Uint32 VideoAddress; /* Address of video display in ST screen space */
1127: int nSyncByte;
1.1.1.23 root 1128: int Res;
1.1.1.11 root 1129: int LineBorderMask;
1130: int PrevSize;
1131: int CurSize;
1.1.1.15 root 1132: int LineStartCycle , LineEndCycle;
1.1.1.11 root 1133:
1134: /* Find number of cycles passed during frame */
1.1.1.24 root 1135: /* We need to subtract '8' for correct video address calculation */
1136: FrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 8;
1.1.1.11 root 1137:
1138: /* Now find which pixel we are on (ignore left/right borders) */
1.1.1.15 root 1139: Video_ConvertPosition ( FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.11 root 1140:
1.1.1.23 root 1141: Res = IoMem_ReadByte ( 0xff8260 ) & 3;
1.1.1.24 root 1142:
1143: /* [FIXME] 'Delirious Demo IV' protection : reads FF8209 between a high/low switch */
1144: /* on a low res screen. So far, Hatari doesn't handle mixed resolutions */
1145: /* on the same line, so we ignore the hi switch in that case */
1146: if ( ( M68000_InstrPC == 0x2110 ) && ( STMemory_ReadLong ( M68000_InstrPC ) == 0x14101280 ) ) /* move.b (a0),d2 + move.b d0,(a1) */
1147: Res = 0; /* force to low res to pass the protection */
1148:
1.1.1.23 root 1149: if ( Res & 2 ) /* hi res */
1.1.1.11 root 1150: {
1.1.1.23 root 1151: LineStartCycle = LINE_START_CYCLE_71;
1152: LineEndCycle = LINE_END_CYCLE_71;
1153: HblCounterVideo = FrameCycles / nCyclesPerLine;
1154: LineCycles = FrameCycles % nCyclesPerLine;
1.1.1.11 root 1155: }
1.1.1.23 root 1156: else
1.1.1.11 root 1157: {
1.1.1.23 root 1158: nSyncByte = IoMem_ReadByte(0xff820a) & 2; /* only keep bit 1 */
1159: if (nSyncByte) /* 50 Hz */
1160: {
1161: LineStartCycle = LINE_START_CYCLE_50;
1162: LineEndCycle = LINE_END_CYCLE_50;
1163: }
1164: else /* 60 Hz */
1165: {
1166: LineStartCycle = LINE_START_CYCLE_60;
1167: LineEndCycle = LINE_END_CYCLE_60;
1168: }
1.1.1.11 root 1169: }
1170:
1.1.1.23 root 1171: X = LineCycles;
1.1.1.11 root 1172:
1173: /* Top of screen is usually 63 lines from VBL in 50 Hz */
1.1.1.15 root 1174: if ( HblCounterVideo < nStartHBL )
1.1.1.11 root 1175: {
1176: /* pVideoRaster was set during Video_ClearOnVBL using VideoBase */
1177: /* and it could also have been modified on STE by writing to ff8205/07/09 */
1.1.1.15 root 1178: /* So, we should not use ff8201/ff8203 which are reloaded in ff8205/ff8207 only once per VBL */
1179: /* but use pVideoRaster - STRam instead to get current shifter video address */
1.1.1.11 root 1180: VideoAddress = pVideoRaster - STRam;
1181: }
1182:
1.1.1.23 root 1183: /* Special case when reading video counter in hi-res (used in the demo 'My Socks Are Weapons' by Legacy) */
1184: /* This assumes a standard 640x400 resolution with no border removed, so code is simpler */
1185: /* [NP] TODO : this should be handled in a more generic way with low/med cases */
1186: /* even when Hatari is not started in monochrome mode */
1187: else if ( Res & 2 ) /* Hi res */
1188: {
1189: if ( X < LineStartCycle )
1190: X = LineStartCycle; /* display is disabled in the left border */
1191: else if ( X > LineEndCycle )
1192: X = LineEndCycle; /* display is disabled in the right border */
1193:
1194: NbBytes = ( (X-LineStartCycle)>>1 ) & (~1); /* 2 cycles per byte */
1195:
1196: /* One line uses 80 bytes instead of the standard 160 bytes in low/med res */
1197: if ( HblCounterVideo < nStartHBL + VIDEO_HEIGHT_HBL_MONO )
1198: VideoAddress = VideoBase + ( HblCounterVideo - nStartHBL ) * ( BORDERBYTES_NORMAL / 2 ) + NbBytes;
1199: else
1200: VideoAddress = VideoBase + VIDEO_HEIGHT_HBL_MONO * ( BORDERBYTES_NORMAL / 2 );
1201: }
1202:
1.1.1.11 root 1203: else
1204: {
1205: VideoAddress = pVideoRaster - STRam; /* pVideoRaster is updated by Video_CopyScreenLineColor */
1206:
1207: /* Now find which pixel we are on (ignore left/right borders) */
1.1.1.15 root 1208: // X = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) % nCyclesPerLine;
1.1.1.11 root 1209:
1210: /* Get real video line count (can be different from nHBL) */
1.1.1.15 root 1211: // HblCounterVideo = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) / nCyclesPerLine;
1.1.1.11 root 1212:
1213: /* Correct the case when read overlaps end of line / start of next line */
1214: /* Video_CopyScreenLineColor was not called yet to update VideoAddress */
1215: /* so we need to determine the size of the previous line to get the */
1216: /* correct value of VideoAddress. */
1217: PrevSize = 0;
1218: if ( HblCounterVideo < nHBL )
1219: X = 0;
1220: else if ( ( HblCounterVideo > nHBL ) /* HblCounterVideo = nHBL+1 */
1221: && ( nHBL >= nStartHBL ) ) /* if nHBL was not visible, PrevSize = 0 */
1222: {
1.1.1.15 root 1223: LineBorderMask = ShifterFrame.ShifterLines[ HblCounterVideo-1 ].BorderMask; /* get border mask for nHBL */
1.1.1.11 root 1224: PrevSize = BORDERBYTES_NORMAL; /* normal line */
1225:
1226: if (LineBorderMask & BORDERMASK_LEFT_OFF)
1227: PrevSize += BORDERBYTES_LEFT;
1228: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
1229: PrevSize += 2;
1230:
1231: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
1232: PrevSize -= 106;
1233: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
1234: PrevSize -= 2;
1235: else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
1236: PrevSize += BORDERBYTES_RIGHT;
1237:
1.1.1.24 root 1238: if (LineBorderMask & ( BORDERMASK_EMPTY_LINE | BORDERMASK_NO_DE ) )
1.1.1.11 root 1239: PrevSize = 0;
1.1.1.23 root 1240:
1241: /* On STE, the Shifter skips the given amount of words as soon as display is disabled */
1242: /* which is the case here when reading overlaps end/start of line (LineWidth is 0 on STF) */
1243: PrevSize += LineWidth*2;
1.1.1.11 root 1244: }
1245:
1246:
1.1.1.15 root 1247: LineBorderMask = ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask;
1.1.1.11 root 1248:
1249: CurSize = BORDERBYTES_NORMAL; /* normal line */
1250:
1251: if (LineBorderMask & BORDERMASK_LEFT_OFF)
1252: CurSize += BORDERBYTES_LEFT;
1.1.1.24 root 1253: else if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
1254: CurSize += BORDERBYTES_LEFT_2_STE;
1.1.1.11 root 1255: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
1256: CurSize += 2;
1.1.1.16 root 1257: else if (bSteBorderFlag) /* bigger line by 8 bytes on the left (STE specific) */
1258: CurSize += 8;
1259: else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
1260: CurSize += 8; /* 8 more bytes are loaded when scrolling with prefetching */
1.1.1.11 root 1261:
1262: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
1263: CurSize -= 106;
1264: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
1265: CurSize -= 2;
1266: else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
1267: CurSize += BORDERBYTES_RIGHT;
1268: if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
1269: CurSize += BORDERBYTES_RIGHT_FULL;
1270:
1271: if ( LineBorderMask & BORDERMASK_LEFT_PLUS_2)
1272: LineStartCycle = LINE_START_CYCLE_60;
1273: else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1.1.1.15 root 1274: LineStartCycle = LINE_START_CYCLE_71;
1.1.1.16 root 1275: else if ( bSteBorderFlag )
1276: LineStartCycle -= 16; /* display starts 16 pixels earlier */
1277: else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
1278: LineStartCycle -= 16; /* shifter starts reading 16 pixels earlier when scrolling with prefetching */
1.1.1.11 root 1279:
1280: LineEndCycle = LineStartCycle + CurSize*2;
1281:
1282:
1283: if ( X < LineStartCycle )
1.1.1.16 root 1284: X = LineStartCycle; /* display is disabled in the left border */
1.1.1.11 root 1285: else if ( X > LineEndCycle )
1.1.1.16 root 1286: {
1287: X = LineEndCycle; /* display is disabled in the right border */
1.1.1.23 root 1288: /* On STE, the Shifter skips the given amount of words as soon as display is disabled */
1289: /* (LineWidth is 0 on STF) */
1.1.1.16 root 1290: VideoAddress += LineWidth*2;
1291: }
1.1.1.11 root 1292:
1293: NbBytes = ( (X-LineStartCycle)>>1 ) & (~1); /* 2 cycles per byte */
1294:
1295:
1296: /* when left border is open, we have 2 bytes less than theorical value */
1297: /* (26 bytes in left border, which is not a multiple of 4 cycles) */
1298: if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1299: NbBytes -= 2;
1300:
1.1.1.24 root 1301: if ( LineBorderMask & ( BORDERMASK_EMPTY_LINE | BORDERMASK_NO_DE ) )
1.1.1.11 root 1302: NbBytes = 0;
1303:
1.1.1.16 root 1304: /* Add line cycles if we have not reached end of screen yet */
1.1.1.18 root 1305: if ( HblCounterVideo < nEndHBL + BlankLines )
1.1.1.11 root 1306: VideoAddress += PrevSize + NbBytes;
1307: }
1308:
1.1.1.16 root 1309: LOG_TRACE(TRACE_VIDEO_ADDR , "video base=%x raster=%x addr=%x video_cyc=%d "
1310: "line_cyc=%d/X=%d @ nHBL=%d/video_hbl=%d %d<->%d pc=%x instr_cyc=%d\n",
1311: VideoBase, (int)(pVideoRaster - STRam), VideoAddress,
1312: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), LineCycles, X, nHBL,
1313: HblCounterVideo, LineStartCycle, LineEndCycle, M68000_GetPC(), CurrentInstrCycles);
1.1.1.11 root 1314:
1315: return VideoAddress;
1316: }
1317:
1318:
1319: /*-----------------------------------------------------------------------*/
1320: /**
1.1.1.16 root 1321: * Calculate the cycle where the STF/STE's MMU starts reading
1322: * data to send them to the shifter.
1323: * On STE, if hscroll is used, prefetch will cause this position to
1324: * happen 16 cycles earlier.
1325: * This function should use the same logic as in Video_CalculateAddress.
1326: * NOTE : this function is not completly accurate, as even when there's
1327: * no hscroll (on STF) the mmu starts reading 16 cycles before display starts.
1328: * But it's good enough to emulate writing to ff8205/07/09 on STE.
1329: */
1330: static int Video_GetMMUStartCycle ( int DisplayStartCycle )
1331: {
1332: if ( bSteBorderFlag )
1333: DisplayStartCycle -= 16; /* display starts 16 pixels earlier */
1334: else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
1335: DisplayStartCycle -= 16; /* shifter starts reading 16 pixels earlier when scrolling with prefetching */
1336:
1337: return DisplayStartCycle;
1338: }
1339:
1340:
1341: /*-----------------------------------------------------------------------*/
1342: /**
1.1.1.11 root 1343: * Write to VideoShifter (0xff8260), resolution bits
1344: */
1.1.1.24 root 1345: static void Video_WriteToGlueShifterRes ( Uint8 Res )
1.1 root 1346: {
1.1.1.15 root 1347: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.11 root 1348:
1.1.1.15 root 1349: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 1350: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.11 root 1351:
1.1.1.15 root 1352: LOG_TRACE(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",
1353: Res, FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1354:
1355:
1.1.1.13 root 1356: /* Ignore consecutive writes of the same value */
1.1.1.15 root 1357: if ( Res == ShifterFrame.Res )
1.1.1.13 root 1358: return; /* do nothing */
1359:
1.1.1.15 root 1360:
1.1.1.24 root 1361: Video_Update_Glue_State ( FrameCycles , HblCounterVideo , LineCycles , true );
1.1.1.15 root 1362:
1363:
1364: if ( ( ShifterFrame.Res == 0x02 ) && ( Res == 0x01 ) /* switched from hi res to med res */
1365: && ( LineCycles <= (LINE_START_CYCLE_71+20) )
1.1.1.24 root 1366: && ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF ) )
1.1.1.15 root 1367: {
1.1.1.24 root 1368: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= ~BORDERMASK_LEFT_OFF; /* cancel left off low res */
1.1.1.15 root 1369: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_LEFT_OFF_MED; /* a later switch to low res might gives right scrolling */
1370: /* By default, this line will be in med res, except if we detect hardware scrolling later */
1371: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 2 << 20 );
1.1.1.24 root 1372: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
1.1.1.15 root 1373: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left med %d<->%d\n" ,
1374: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1375: }
1376:
1377:
1.1.1.24 root 1378: /* If left border is opened with hi/lo and we switch to medium resolution during the next cycles, */
1.1.1.15 root 1379: /* then we assume a med res overscan line instead of a low res overscan line. */
1380: /* Note that in that case, the switch to med res can shift the display by 0-3 words */
1.1.1.11 root 1381: /* Used in 'No Cooper' greetings by 1984 and 'Punish Your Machine' by Delta Force */
1.1.1.15 root 1382: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
1383: && ( Res == 0x01 ) )
1.1.1.11 root 1384: {
1.1.1.18 root 1385: if ( ( LineCycles == LINE_LEFT_MED_CYCLE_1 ) /* 'No Cooper' timing */
1386: || ( LineCycles == LINE_LEFT_MED_CYCLE_1+16 ) ) /* 'No Cooper' timing while removing bottom border */
1.1.1.11 root 1387: {
1.1.1.15 root 1388: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect med res overscan offset 0 byte\n" );
1389: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 0 << 20 );
1.1.1.11 root 1390: }
1.1.1.15 root 1391: else if ( LineCycles == LINE_LEFT_MED_CYCLE_2 ) /* 'Best Part Of The Creation / PYM' timing */
1.1.1.11 root 1392: {
1.1.1.15 root 1393: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect med res overscan offset 2 bytes\n" );
1394: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 2 << 20 );
1.1.1.11 root 1395: }
1396: }
1397:
1.1.1.15 root 1398: /* If left border was opened with a hi/med res switch we need to check */
1399: /* if the switch to low res can trigger a right hardware scrolling. */
1400: /* We store the pixels count in DisplayPixelShift */
1401: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF_MED )
1402: && ( Res == 0x00 ) && ( LineCycles <= LINE_SCROLL_1_CYCLE_50 ) )
1.1.1.11 root 1403: {
1.1.1.24 root 1404: /* The hi/med switch was a switch to do low res hardware scrolling */
1405: /* or to do a 'remove left' that includes a med res stabiliser, */
1.1.1.15 root 1406: /* so we must cancel the med res overscan bit. */
1407: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= (~BORDERMASK_OVERSCAN_MED_RES);
1.1.1.11 root 1408:
1.1.1.24 root 1409: if ( LineCycles == LINE_LEFT_STAB_LOW ) /* cycle 16 */
1410: {
1411: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with med stab\n" );
1412: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;
1413: }
1414:
1415: else if ( LineCycles == LINE_SCROLL_13_CYCLE_50 ) /* cycle 20 */
1.1.1.11 root 1416: {
1.1.1.15 root 1417: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll\n" );
1418: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
1.1.1.11 root 1419: }
1.1.1.15 root 1420: else if ( LineCycles == LINE_SCROLL_9_CYCLE_50 ) /* cycle 24 */
1.1.1.11 root 1421: {
1.1.1.15 root 1422: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll\n" );
1423: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
1.1.1.11 root 1424: }
1.1.1.15 root 1425: else if ( LineCycles == LINE_SCROLL_5_CYCLE_50 ) /* cycle 28 */
1.1.1.11 root 1426: {
1.1.1.15 root 1427: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll\n" );
1428: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
1.1.1.11 root 1429: }
1.1.1.15 root 1430: else if ( LineCycles == LINE_SCROLL_1_CYCLE_50 ) /* cycle 32 */
1.1.1.11 root 1431: {
1.1.1.15 root 1432: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll\n" );
1433: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
1.1.1.11 root 1434: }
1435: }
1436:
1.1.1.18 root 1437: /* Left border was removed with a hi/lo switch, then a med res switch was made */
1438: /* Depending on the low res switch, the screen will be shifted as a low res overscan line */
1439: /* This is a different method than the one used by ST Connexion with only 3 res switches */
1440: /* (so we must cancel the med res overscan bit) */
1441: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_OVERSCAN_MED_RES )
1442: && ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & ( 0xf << 20 ) ) == 0 )
1443: && ( Res == 0x00 ) && ( LineCycles <= 40 ) )
1444: {
1.1.1.24 root 1445: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= (~BORDERMASK_OVERSCAN_MED_RES); /* cancel med res */
1.1.1.18 root 1446:
1447: if ( LineCycles == 28 )
1448: {
1449: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll 2\n" );
1450: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
1451: }
1452: else if ( LineCycles == 32 )
1453: {
1454: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll 2\n" );
1455: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
1456: }
1457: else if ( LineCycles == 36 )
1458: {
1459: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll 2\n" );
1460: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
1461: }
1462: else if ( LineCycles == 40 )
1463: {
1464: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll 2\n" );
1465: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
1466: }
1467: }
1468:
1.1.1.25! root 1469: /* Paulo Simoes' 4 pixel hardscroll on the whole screen, without removing left border */
! 1470: /* All following lines will be shifted too, not just the one where the med/low switch happens */
! 1471: if ( ( ShifterFrame.Res == 0x01 ) && ( Res == 0x00 ) /* switched from med res to low res */
! 1472: && ( ShifterFrame.ResPosMed.LineCycles == 84 ) ) /* med res at cycle 84 */
! 1473: {
! 1474: /* The med/low switch was a switch to do low res hardware scrolling */
! 1475: if ( LineCycles == 100 )
! 1476: {
! 1477: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll 3\n" );
! 1478: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
! 1479: }
! 1480: else if ( LineCycles == 104 )
! 1481: {
! 1482: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll 3\n" );
! 1483: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
! 1484: pVideoRaster -= 2;
! 1485: }
! 1486: else if ( LineCycles == 92 )
! 1487: {
! 1488: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll 3\n" );
! 1489: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
! 1490: pVideoRaster -= 4;
! 1491: }
! 1492: else if ( LineCycles == 96 )
! 1493: {
! 1494: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll 3\n" );
! 1495: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
! 1496: pVideoRaster -= 6;
! 1497: }
! 1498:
! 1499: /* Mark all the following lines as shifted too */
! 1500: int i;
! 1501: for ( i=HblCounterVideo+1 ; i<MAX_SCANLINES_PER_FRAME ; i++ )
! 1502: ShifterFrame.ShifterLines[ i ].DisplayPixelShift = ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift;
! 1503: }
! 1504:
1.1.1.24 root 1505: /* TEMP for 'closure' in WS2 */
1506: /* -> stay in hi res for 16 cycles to do the stab (hi/50/lo at 4/12/20) */
1507: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
1508: && ( Res == 0x00 ) && ( LineCycles == 20 )
1509: && ( ShifterFrame.ResPosHi.LineCycles == 4 )
1510: && ( STMemory_ReadLong ( M68000_GetPC()-4 ) == 0x32883088 ) /* move.w a0,(a1) + move.w a0,(a0) */
1511: )
1512: {
1513: /* for now we simulate the same border as in ws1/3/4, but there's no med res in fact */
1514: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with no med stab closure ws2\n" );
1515: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask = BORDERMASK_LEFT_OFF_MED;
1516: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0; /* see special case in Video_CopyScreenLineColor() */
1.1.1.15 root 1517: }
1.1.1.24 root 1518: /* TEMP for 'closure' in WS2 */
1.1.1.15 root 1519:
1.1.1.24 root 1520: /* TEMP for 'closure' in STE mode */
1521: /* -> stay in hi res for 16 cycles to do the stab (hi/50/lo at 0/8/16) */
1522: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
1523: && ( Res == 0x00 ) && ( LineCycles == 16 )
1524: && ( ShifterFrame.ResPosHi.LineCycles == 0 )
1525: && ( STMemory_ReadLong ( M68000_GetPC()-4 ) == 0x32883088 ) /* move.w a0,(a1) + move.w a0,(a0) */
1526: )
1527: {
1528: /* for now we simulate the same border as in ws1/3/4, but there's no med res in fact */
1529: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with no med stab closure ste\n" );
1530: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask = BORDERMASK_LEFT_OFF_MED;
1531: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0; /* see special case in Video_CopyScreenLineColor() */
1532: }
1533: /* TEMP for 'closure' in STE mode */
1.1.1.15 root 1534:
1.1.1.24 root 1535: /* TEMP for 'death of the left border' by TNT */
1536: /* -> stay in hi res for 16 cycles (hi/lo at 0/16) without stabiliser */
1537: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
1538: && ( Res == 0x00 ) && ( LineCycles == 16 )
1539: && ( ShifterFrame.ResPosHi.LineCycles == 0 )
1540: && ( STMemory_ReadLong ( M68000_GetPC()-0x0c ) == 0x51c9fffc ) /* dbf d1,$fffc */
1541: && ( M68000_GetPC() == 0x72c )
1542: )
1543: {
1544: /* we simulate a 13 px scroll, which compensates for 2 bytes in the video planes */
1545: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with no stab dolb\n" );
1546: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13; /* see special case in Video_CopyScreenLineColor() */
1547: }
1548: /* TEMP for 'death of the left border' by TNT */
1.1.1.15 root 1549:
1550:
1.1.1.24 root 1551: /* Store cycle position of this change of resolution */
1.1.1.15 root 1552: ShifterFrame.Res = Res;
1553: if ( Res == 0x02 ) /* high res */
1554: {
1555: ShifterFrame.ResPosHi.VBL = nVBLs;
1556: ShifterFrame.ResPosHi.FrameCycles = FrameCycles;
1557: ShifterFrame.ResPosHi.HBL = HblCounterVideo;
1558: ShifterFrame.ResPosHi.LineCycles = LineCycles;
1559: }
1560: else if ( Res == 0x01 ) /* med res */
1561: {
1562: ShifterFrame.ResPosMed.VBL = nVBLs;
1563: ShifterFrame.ResPosMed.FrameCycles = FrameCycles;
1564: ShifterFrame.ResPosMed.HBL = HblCounterVideo;
1565: ShifterFrame.ResPosMed.LineCycles = LineCycles;
1566: }
1567: else /* low res */
1568: {
1569: ShifterFrame.ResPosLo.VBL = nVBLs;
1570: ShifterFrame.ResPosLo.FrameCycles = FrameCycles;
1571: ShifterFrame.ResPosLo.HBL = HblCounterVideo;
1572: ShifterFrame.ResPosLo.LineCycles = LineCycles;
1573: }
1574: }
1575:
1576:
1577:
1.1.1.24 root 1578:
1.1.1.15 root 1579: /*-----------------------------------------------------------------------*/
1580: /**
1.1.1.24 root 1581: * Update the state of the GLUE (border start/end, hbl position, timer b position, ...)
1582: * depending on the actual video frequency. This is where border removal and similar
1583: * tricks are detected.
1584: *
1585: * This function should be called after a write to the freq register
1586: * at $FF820A or to the resolution register at $FF8260.
1587: * We have 2 different parts for STF and STE, but the timing structure
1588: * is rather similar (on STE, GLUE and MMU chips were merged into the GST MCU chip
1589: * and we need to handle preload due to hscroll)
1590: *
1591: * As seen on a real chip, freq and res registers are merged into a single
1592: * state that can represent 3 values (50, 60 or 71 Hz).
1593: * The same method is used here, which means all comparisons are made relative to
1594: * the current video frequency (allowing for example to remove bottom border using
1595: * a switch to high resolution)
1.1.1.15 root 1596: */
1.1.1.24 root 1597:
1598: /*
1599: * Examples of some common switches altering horizontal display start/end :
1600: * - Remove left border : +26 bytes
1601: * This can be done with a hi/lo or a hi/med res switch between 504 and 8
1602: * - Remove left border STE : +20 bytes
1603: * Switch back to lo/med should be at cycle 4
1604: * - Start right border near middle of the line : -106 bytes
1605: * Switch to hi res just before the start of the right border in hi res, then go back to lo/med res
1606: * - Empty line switching res on STF/STE : switch to hi res on cycle 28, then go back to med/lo res
1607: * This creates a 0 byte/blank line, the video counter won't change for this line
1608: * (used in Lemmings demo (part 2) in 'Nostalgic Demo' by Oxygene)
1609: * - Empty line switching res on STF : switch to hi res just before the HBL (at ~502, hsync stop) then go back to lo/med res
1610: * Next HBL will be a 0 byte/blank line (used in 'No Buddies Land' and 'Delirious Demo IV / NGC')
1611: * - Remove right border a second time after removing it a first time. Display will stop at cycle 512 instead of 460.
1612: * Switch to hi res at ~462 (hsync start); This removes left border on next line too (used in 'Enchanted Lands')
1613: * If right border was not removed, then we will get an empty line for the next HBL (used in 'Pax Plax Parralax' in 'Beyond' by Kruz)
1614: * - Blank line switching freq on STF : switch to 60 Hz on cycle 28, then go back to 50 Hz before pal start (cycle 56)
1615: * This creates a blank line where no signal is displayed, but the video counter will still change for this line.
1616: * This blank line can be combined with left/right border changes (used in 'Overscan Demos - part 6' by Paulo Simoes and 'Closure' by Sync)
1617: * - Add 2 bytes to left border : switch to 60 Hz before pos 52 to force an early start of the DE signal, then go back to 50 Hz.
1618: * Note that depending on where the 50 Hz switch is made the HBL signal will be at position 508 (60 Hz line) or 512 (50 Hz line)
1619: * Obtaining a +2 line with 512 cycles requires a 2 cycles precision and is "wakeup state" dependent
1620: * - If switch to 50 Hz is made at cycle 54 (requires 2 cycle precision), then the line will start 2 bytes earlier and will be 50 Hz (HBL at cycle 512)
1621: * - If switch to 50 Hz is made at cycle >= 56, then the line will start 2 bytes earlier and will be 60 Hz (HBL at cycle 508)
1622: * - Empty line switching freq on STF : start the line in 50 Hz, change to 60 Hz at the exact place
1623: * where display is enabled in 50 Hz, then go back to 50 Hz.
1624: * This creates a 0 byte line (used in 'shforstv' by Paulo Simoes and 'Closure' by Sync)
1625: * - Remove 2 bytes to the right : start the line in 50 Hz (pos 0 or 56) and change to 60 Hz before the position
1626: * where display is disabled in 60 Hz (optionally go back to 50 Hz after this position, but not necessary)
1627: * - Remove right border : +44 bytes ; switch to 60 Hz at the position where the line ends in 50 Hz
1628: * Some programs don't switch back to 50 Hz immediately (Sync screen in 'SNY II', 'Closure' by Sync)
1629: */
1630:
1631: /*
1632: In Hatari, the state machine is updated only on every write to freq/res registers
1633: (instead of doing it on every cycle as on real chip).
1634: The following pseudo code should be equivalent to a state machine updated on
1635: every cycle (but some rare cases/combinations could be missing)
1636:
1637: if ( freq == 71 & pos <= start_hi )
1638: match_found;
1639: hbl = 224:
1640: if ( !no_de )
1641: start = 4;
1642: end = 164;
1643: remove left;
1644: if ( left+2 )
1645: cancel left+2;
1646:
1647: else if ( freq == 71 & pos <= blank_stop_50 )
1648: match_found;
1649: hbl = 224:
1650: if ( !no_de )
1651: end = 164;
1652: blank line no DE;
1653: if ( left+2 )
1654: cancel left+2;
1655:
1656: else if ( freq == 71 & pos <= start_50 )
1657: match_found;
1658: hbl = 224:
1659: if ( !no_de )
1660: end = 164;
1661: line no DE;
1662: if ( left+2 )
1663: cancel left+2;
1664:
1665: else if ( freq != 71 )
1666: if ( pos <= start_hi & remove left )
1667: cancel remove left;
1668: if ( pos <= blank_stop_50 & blank line no DE & !ignore line )
1669: cancel blank line no DE;
1670: if ( pos <= start_50 & line no DE & !blank line & !ignore line )
1671: cancel line no DE;
1672:
1673: if ( freq == 60 & pos < start_pal )
1674: match_found;
1675: hbl = 508;
1676: end = 372;
1677: right-2;
1678: if ( !no_de )
1679: if ( pos <= blank_stop_50 )
1680: blank line with DE;
1681: if ( start == 56 )
1682: start=52;
1683: left+2;
1684:
1685: else if ( freq == 50 & pos <= start_60 )
1686: match_found;
1687: hbl = 512;
1688: if ( !no_de )
1689: end = 376;
1690: if (right-2)
1691: cancel right-2
1692: if ( start == 52 )
1693: start=56;
1694: if ( left+2 )
1695: cancel left+2;
1696:
1697: else if ( freq == 50 & pos <= start_pal )
1698: match_found;
1699: hbl = 512;
1700: if ( !no_de )
1701: end = 376
1702: if (right-2)
1703: cancel right-2
1704: left+2 50 Hz
1705:
1706: if ( STF & freq == 60 & pos > start_60 & pos <= start_50 & !no_de )
1707: match_found;
1708: if ( start == 56 )
1709: start=0; end=0;
1710: empty line no DE;
1711:
1712: else if ( STE & freq == 60 & pos > preload_start_60 & pos <= preload_start_50 )
1713: match_found;
1714: if ( start == 56 )
1715: start=0; end=0;
1716: empty line no DE;
1717:
1718: freq_test_next
1719: if ( match_found )
1720: goto freq_test_done
1721:
1722:
1723: if ( freq == 71 & pos <= end & pos <= end_hi & !no_de )
1724: end=164;
1725: stop middle;
1726: if (right-2)
1727: cancel right-2
1728:
1729: else if ( freq == 71 & pos <= end & !no_de )
1730: end=512;
1731: remove right / right full;
1732:
1733: else if ( freq == 71 & pos <= hsync_start_50/60 )
1734: empty line res 3 no sync;
1735:
1736: else if ( freq == 71 & pos <= hsync_stop_50/60 )
1737: empty line res 2 sync high;
1738:
1739:
1740: else if ( freq == 60 & pos <= end & pos <= end_60 & !no_de )
1741: if ( end == 376 )
1742: right-2;
1743: end=372;
1744: if ( stop middle )
1745: cancel stop middle;
1746: if ( remove right | right full )
1747: cancel remove right | right full;
1748:
1749:
1750: else if ( freq == 50 & pos <= end & pos <= end_50 & !no_de )
1751: end=376;
1752: if ( right-2 )
1753: cancel right-2;
1754: if ( stop middle )
1755: cancel stop middle;
1756: if ( remove right | right full )
1757: cancel remove right | right full;
1758:
1759: else if ( freq == 60 & pos <= end & pos > end_60 & pos <= end_50 & !no_de )
1760: if ( end == 376 )
1761: end=hsync_start_50/60;
1762: remove right;
1763: if ( right-2 )
1764: cancel right-2;
1765:
1766: else if ( freq != 71 & pos <= hsync_start_50/60 )
1767: if ( pos <= end )
1768: end = hsync_start_50/60;
1769: if ( remove right full )
1770: cancel remove right full;
1771: else if ( empty line res 3 no sync )
1772: cancel empty line res 3 no sync;
1773:
1774: else if ( freq != 71 & pos <= hsync_stop_50/60 )
1775: if ( empty line res 2 sync high )
1776: cancel empty line res 2 sync high;
1777:
1778: freq_test_done
1779:
1780: */
1781:
1782:
1783: static void Video_Update_Glue_State ( int FrameCycles , int HblCounterVideo , int LineCycles , bool WriteToRes )
1784: {
1785: int FreqHz;
1786: int DE_start;
1787: int DE_end;
1788: int HBL_Pos;
1789: int nCyclesPerLine_new;
1790: int Freq_match_found;
1791: Uint32 BorderMask;
1792: int Top_Pos;
1793: int Bottom_Pos;
1794:
1795:
1796: /* FreqHz will be 50, 60 or 71 */
1797: if ( IoMem[0xff8260] & 2 )
1798: FreqHz = VIDEO_71HZ;
1799: else
1800: {
1801: /* We're only interested in bit 1 (50/60Hz) */
1802: FreqHz = ( IoMem[0xff820a] & 2 ) ? VIDEO_50HZ : VIDEO_60HZ;
1803: }
1804:
1805:
1806: /* GLUE will latch freq register 1 cycle later than res register */
1807: /* To take this into account in our case, we subtract 1 cycle to the res write position */
1808: /* that will be used for all the comparison in the state machine */
1809: /* (this is not the case for the STE GST MCU) */
1810: if ( Config_IsMachineST() && WriteToRes )
1811: LineCycles--;
1812:
1813:
1814: DE_start = ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle;
1815: DE_end = ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle;
1816: BorderMask = ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask;
1817: HBL_Pos = -1;
1818: nCyclesPerLine_new = -1;
1819: Freq_match_found = 0;
1820:
1821: //fprintf ( stderr , "pom %d %d-%d %d-%d\n" , nHBL , nStartHBL , nEndHBL , DE_start , DE_end );
1822:
1823:
1824: /*
1825: * Check Freq's value before DE_start
1826: * We need 2 different cases for ST and STE
1827: */
1828:
1829: /*************** STF ***************/
1830: if ( Config_IsMachineST() )
1831: {
1832: if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->H_Start_Hi ) )
1833: {
1834: Freq_match_found = 1;
1835: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi; /* 220/224 */
1836: nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
1837: if ( !( BorderMask & BORDERMASK_NO_DE ) )
1838: {
1839: DE_start = pVideoTiming->H_Start_Hi; /* 4 */
1840: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
1841:
1842: BorderMask |= BORDERMASK_LEFT_OFF;
1843: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -4; /* screen is shifted 4 pixels to the left */
1844: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" , DE_start , DE_end );
1845:
1846: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
1847: {
1848: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
1849: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
1850: }
1851: }
1852: }
1853:
1854: else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
1855: {
1856: Freq_match_found = 1;
1857: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi; /* 220/224 */
1858: nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
1859: if ( !( BorderMask & BORDERMASK_NO_DE ) )
1860: {
1861: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
1862:
1863: BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE );
1864: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line no DE res stf %d<->%d\n" , DE_start , DE_end );
1865: }
1866:
1867: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
1868: {
1869: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
1870: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
1871: }
1872: }
1873:
1874: else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->H_Start_Low_50 ) )
1875: {
1876: Freq_match_found = 1;
1877: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi; /* 220/224 */
1878: nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
1879: if ( !( BorderMask & BORDERMASK_NO_DE ) )
1880: {
1881: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
1882:
1883: BorderMask |= BORDERMASK_NO_DE;
1884: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE res stf %d<->%d\n" , DE_start , DE_end );
1885: }
1886:
1887: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
1888: {
1889: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
1890: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
1891: }
1892: }
1893:
1894: else if ( FreqHz != VIDEO_71HZ )
1895: {
1896: if ( ( LineCycles <= pVideoTiming->H_Start_Hi ) && ( BorderMask & BORDERMASK_LEFT_OFF ) )
1897: {
1898: if ( FreqHz == VIDEO_50HZ )
1899: DE_start = pVideoTiming->H_Start_Low_50; /* 56 */
1900: else
1901: {
1902: DE_start = pVideoTiming->H_Start_Low_60; /* 52 */
1903: BorderMask |= BORDERMASK_LEFT_PLUS_2;
1904: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz %d<->%d\n" , DE_start , DE_end );
1905: }
1906:
1907: BorderMask &= ~BORDERMASK_LEFT_OFF;
1908: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;
1909: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove left %d<->%d\n" , DE_start , DE_end );
1910: }
1911:
1912: if ( ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) && ( BorderMask & ( BORDERMASK_BLANK | BORDERMASK_NO_DE ) )
1913: && !( BorderMask & BORDERMASK_NO_COUNT ) )
1914: {
1915: BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE );
1916: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel blank line no DE %d<->%d\n" , DE_start , DE_end );
1917: }
1918: else if ( ( LineCycles <= pVideoTiming->H_Start_Low_50 ) && ( BorderMask & BORDERMASK_NO_DE ) && !( BorderMask & BORDERMASK_BLANK )
1919: && !( BorderMask & BORDERMASK_NO_COUNT ) )
1920: {
1921: /* we can cancel a "line no de", but not a "blank line no de" */
1922: BorderMask &= ~BORDERMASK_NO_DE;
1923: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel line no DE %d<->%d\n" , DE_start , DE_end );
1924: }
1925: }
1926:
1927: /* The line was in 50 Hz and continues in 60 Hz */
1928: if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles < pVideoTiming->Line_Set_Pal ) )
1929: {
1930: //fprintf ( stderr , "pom0a\n" );
1931: Freq_match_found = 1;
1932: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_60; /* 504/508 */
1933: nCyclesPerLine_new = CYCLES_PER_LINE_60HZ;
1934: if ( !( BorderMask & BORDERMASK_NO_DE ) )
1935: {
1.1.1.25! root 1936: if ( DE_start > 0 ) /* ie only if StartHBL was called */
! 1937: {
! 1938: DE_end = pVideoTiming->H_Stop_Low_60; /* 372 */
! 1939:
! 1940: BorderMask |= BORDERMASK_RIGHT_MINUS_2;
! 1941: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" , DE_start , DE_end );
! 1942: }
1.1.1.24 root 1943:
1944: if ( ( LineCycles > pVideoTiming->Blank_Stop_Low_60 ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
1945: {
1946: BorderMask |= BORDERMASK_BLANK;
1947: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line freq stf %d<->%d\n" , DE_start , DE_end );
1948: }
1949:
1950: if ( DE_start == pVideoTiming->H_Start_Low_50 ) /* 56 */
1951: {
1952: DE_start = pVideoTiming->H_Start_Low_60; /* 52 */
1953:
1954: BorderMask |= BORDERMASK_LEFT_PLUS_2;
1955: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz %d<->%d\n" , DE_start , DE_end );
1956: }
1957: }
1958: }
1959:
1960: else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->H_Start_Low_60 ) )
1961: {
1962: //fprintf ( stderr , "pom1a\n" );
1963: Freq_match_found = 1;
1964: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50; /* 508/512 */
1965: nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
1966: if ( !( BorderMask & BORDERMASK_NO_DE ) )
1967: {
1968: DE_end = pVideoTiming->H_Stop_Low_50; /* 376 */
1969:
1970: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
1971: {
1972: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
1973: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
1974: }
1975:
1976: if ( DE_start == pVideoTiming->H_Start_Low_60 ) /* 52 */
1977: {
1978: DE_start = pVideoTiming->H_Start_Low_50; /* 56 */
1979:
1980: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
1981: {
1982: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
1983: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
1984: }
1985: }
1986: }
1987: }
1988:
1989: else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->Line_Set_Pal ) )
1990: {
1991: //fprintf ( stderr , "pom2a\n" );
1992: Freq_match_found = 1;
1993: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50; /* 508/512 */
1994: nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
1995: if ( !( BorderMask & BORDERMASK_NO_DE ) )
1996: {
1997: DE_end = pVideoTiming->H_Stop_Low_50; /* 376 */
1998:
1999: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
2000: {
2001: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
2002: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
2003: }
2004:
2005: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 50Hz %d<->%d\n" , DE_start , DE_end );
2006: }
2007: }
2008:
2009: if ( ( FreqHz == VIDEO_60HZ )
2010: && ( LineCycles > pVideoTiming->H_Start_Low_60 ) && ( LineCycles <= pVideoTiming->H_Start_Low_50 )
2011: && !( BorderMask & BORDERMASK_NO_DE ) )
2012: {
2013: Freq_match_found = 1;
2014: //fprintf ( stderr , "pom3a\n" );
2015:
2016: if ( DE_start == pVideoTiming->H_Start_Low_50 ) /* 56 */
2017: {
2018: DE_start = 0 ;
2019: DE_end = 0;
2020:
2021: BorderMask |= BORDERMASK_NO_DE;
2022: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE freq stf %d<->%d\n" , DE_start , DE_end );
2023: }
2024: }
2025: }
2026:
2027: /*************** STE ***************/
2028: else if ( Config_IsMachineSTE() )
2029: {
2030: if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->H_Start_Hi ) )
2031: {
2032: Freq_match_found = 1;
2033: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi; /* 220/224 */
2034: nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
2035: if ( !( BorderMask & BORDERMASK_NO_DE ) )
2036: {
2037: DE_start = pVideoTiming->H_Start_Hi; /* 4 */
2038: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
2039:
2040: BorderMask |= BORDERMASK_LEFT_OFF;
2041: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -4; /* screen is shifted 4 pixels to the left */
2042: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" , DE_start , DE_end );
2043:
2044: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
2045: {
2046: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
2047: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
2048: }
2049: }
2050: }
2051:
2052: else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
2053: {
2054: Freq_match_found = 1;
2055: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi; /* 220/224 */
2056: nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
2057: if ( !( BorderMask & BORDERMASK_NO_DE ) )
2058: {
2059: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
2060:
2061: BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE );
2062: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line no DE res ste %d<->%d\n" , DE_start , DE_end );
2063: }
2064:
2065: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
2066: {
2067: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
2068: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
2069: }
2070: }
2071:
2072: else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->Preload_Start_Low_50 ) )
2073: {
2074: Freq_match_found = 1;
2075: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi; /* 220/224 */
2076: nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
2077: if ( !( BorderMask & BORDERMASK_NO_DE ) )
2078: {
2079: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
2080:
2081: BorderMask |= BORDERMASK_NO_DE;
2082: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE res ste %d<->%d\n" , DE_start , DE_end );
2083: }
2084:
2085: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
2086: {
2087: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
2088: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
2089: }
2090: }
2091:
2092: else if ( FreqHz != VIDEO_71HZ )
2093: {
2094: if ( ( LineCycles < pVideoTiming->H_Start_Hi ) && ( BorderMask & BORDERMASK_LEFT_OFF ) )
2095: {
2096: if ( FreqHz == VIDEO_50HZ )
2097: DE_start = pVideoTiming->H_Start_Low_50; /* 56 */
2098: else
2099: {
2100: DE_start = pVideoTiming->H_Start_Low_60; /* 52 */
2101: BorderMask |= BORDERMASK_LEFT_PLUS_2;
2102: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz %d<->%d\n" , DE_start , DE_end );
2103: }
2104:
2105: BorderMask &= ~BORDERMASK_LEFT_OFF;
2106: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;
2107: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove left %d<->%d\n" , DE_start , DE_end );
2108: }
2109: else if ( ( LineCycles == pVideoTiming->H_Start_Hi ) && ( BorderMask & BORDERMASK_LEFT_OFF ) )
2110: {
2111: DE_start = pVideoTiming->Preload_Start_Hi + 16;
2112: BorderMask &= ~BORDERMASK_LEFT_OFF;
2113: BorderMask |= BORDERMASK_LEFT_OFF_2_STE;
2114: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -8; /* screen is shifted 8 pixels to the left */
2115: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left 2 ste %d<->%d\n" , DE_start , DE_end );
2116: }
2117:
2118: if ( ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) && ( BorderMask & ( BORDERMASK_BLANK | BORDERMASK_NO_DE ) )
2119: && !( BorderMask & BORDERMASK_NO_COUNT ) )
2120: {
2121: BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE );
2122: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel blank line no DE %d<->%d\n" , DE_start , DE_end );
2123: }
2124: else if ( ( LineCycles <= pVideoTiming->Preload_Start_Low_50 ) && ( BorderMask & BORDERMASK_NO_DE ) && !( BorderMask & BORDERMASK_BLANK )
2125: && !( BorderMask & BORDERMASK_NO_COUNT ) )
2126: {
2127: /* we can cancel a "line no de", but not a "blank line no de" */
2128: BorderMask &= ~BORDERMASK_NO_DE;
2129: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel line no DE %d<->%d\n" , DE_start , DE_end );
2130: }
2131: }
2132:
2133: /* The line was in 50 Hz and continues in 60 Hz */
2134: if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles < pVideoTiming->Line_Set_Pal ) )
2135: {
2136: //fprintf ( stderr , "pom0a\n" );
2137: Freq_match_found = 1;
2138: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_60; /* 504/508 */
2139: nCyclesPerLine_new = CYCLES_PER_LINE_60HZ;
2140: if ( !( BorderMask & BORDERMASK_NO_DE ) )
2141: {
1.1.1.25! root 2142: if ( DE_start > 0 ) /* ie only if StartHBL was called */
! 2143: {
! 2144: DE_end = pVideoTiming->H_Stop_Low_60; /* 372 */
! 2145:
! 2146: BorderMask |= BORDERMASK_RIGHT_MINUS_2;
! 2147: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" , DE_start , DE_end );
! 2148: }
1.1.1.24 root 2149:
2150: if ( ( LineCycles > pVideoTiming->Blank_Stop_Low_60 ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
2151: {
2152: BorderMask |= BORDERMASK_BLANK;
2153: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line freq ste %d<->%d\n" , DE_start , DE_end );
2154: }
2155:
2156: if ( LineCycles <= pVideoTiming->Preload_Start_Low_60 )
2157: {
2158: if ( DE_start == pVideoTiming->H_Start_Low_50 ) /* 56 */
2159: {
2160: DE_start = pVideoTiming->H_Start_Low_60; /* 52 */
2161:
2162: BorderMask |= BORDERMASK_LEFT_PLUS_2;
2163: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz ste %d<->%d\n" , DE_start , DE_end );
2164: }
2165: }
2166: else
2167: {
2168: /* normal line starting at 56 but running at 60 Hz ; we don't do anything special */
2169: }
2170: }
2171: }
2172:
2173: else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->Preload_Start_Low_60 ) )
2174: {
2175: //fprintf ( stderr , "pom1a\n" );
2176: Freq_match_found = 1;
2177: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50; /* 508/512 */
2178: nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
2179: if ( !( BorderMask & BORDERMASK_NO_DE ) )
2180: {
2181: DE_end = pVideoTiming->H_Stop_Low_50; /* 376 */
2182:
2183: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
2184: {
2185: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
2186: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
2187: }
2188:
2189: if ( DE_start == pVideoTiming->H_Start_Low_60 ) /* 52 */
2190: {
2191: DE_start = pVideoTiming->H_Start_Low_50; /* 56 */
2192:
2193: if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
2194: {
2195: BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
2196: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 ste %d<->%d\n" , DE_start , DE_end );
2197: }
2198: }
2199: }
2200: }
2201:
2202: else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->Line_Set_Pal ) )
2203: {
2204: //fprintf ( stderr , "pom2a\n" );
2205: Freq_match_found = 1;
2206: HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50; /* 508/512 */
2207: nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
2208: if ( !( BorderMask & BORDERMASK_NO_DE ) )
2209: {
2210: DE_end = pVideoTiming->H_Stop_Low_50; /* 376 */
2211:
2212: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
2213: {
2214: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
2215: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
2216: }
2217:
2218: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 50Hz ste %d<->%d\n" , DE_start , DE_end );
2219: }
2220: }
2221:
2222: if ( ( FreqHz == VIDEO_60HZ )
2223: && ( LineCycles > pVideoTiming->Preload_Start_Low_60 ) && ( LineCycles <= pVideoTiming->Preload_Start_Low_50 )
2224: && !( BorderMask & BORDERMASK_NO_DE ) )
2225: {
2226: Freq_match_found = 1;
2227: //fprintf ( stderr , "pom3a\n" );
2228:
2229: if ( DE_start == pVideoTiming->H_Start_Low_50 ) /* 56 */
2230: {
2231: DE_start = 0 ;
2232: DE_end = 0;
2233:
2234: BorderMask |= BORDERMASK_NO_DE;
2235: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE freq ste %d<->%d\n" , DE_start , DE_end );
2236: }
2237: }
2238: }
2239:
2240:
2241: //fprintf ( stderr , "pom0 %d-%d\n" , DE_start , DE_end );
2242: /* If we found a match before DE_start, then there's no need */
2243: /* to check between DE_start and DE_end */
2244: if ( Freq_match_found )
2245: goto Freq_Test_Done;
2246:
2247: //fprintf ( stderr , "pom1\n" );
2248:
2249: /*
2250: * Check Freq's value between DE_start and DE_end
2251: */
2252:
2253: if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= DE_end ) && ( LineCycles <= pVideoTiming->H_Stop_Hi ) /* 160 */
2254: && !( BorderMask & BORDERMASK_NO_DE ) )
1.1.1.15 root 2255: {
1.1.1.24 root 2256: //fprintf ( stderr , "pom3\n" );
2257: DE_end = pVideoTiming->H_Stop_Hi; /* 164 */
2258:
2259: BorderMask |= BORDERMASK_STOP_MIDDLE;
2260: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect stop middle %d<->%d\n" , DE_start , DE_end );
1.1.1.15 root 2261:
1.1.1.24 root 2262: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
2263: {
2264: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
2265: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
2266: }
1.1.1.15 root 2267: }
2268:
1.1.1.24 root 2269: else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= DE_end ) && !( BorderMask & BORDERMASK_NO_DE ) )
1.1.1.15 root 2270: {
1.1.1.24 root 2271: //fprintf ( stderr , "pom3\n" );
2272: DE_end = LINE_END_CYCLE_FULL; /* 512 */
2273:
2274: BorderMask |= ( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL );
2275: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= BORDERMASK_LEFT_OFF; /* no left border on next line */
2276: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
2277:
2278: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove right/right full %d<->%d\n" , DE_start , DE_end );
1.1.1.15 root 2279:
2280: }
2281:
1.1.1.24 root 2282: else if ( ( FreqHz == VIDEO_71HZ )
2283: && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low ) ) /* 458/462 depends on 50/60 freq (line cycles-50) */
2284: {
2285: BorderMask |= BORDERMASK_NO_SYNC;
2286: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
2287: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = 0;
2288: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = 0;
2289: BlankLines++; /* glue's line counter not incremented, display ends 1 line later */
1.1.1.11 root 2290:
1.1.1.24 root 2291: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res 3 no sync %d<->%d\n" , DE_start , DE_end );
2292: }
1.1.1.11 root 2293:
1.1.1.24 root 2294: else if ( ( FreqHz == VIDEO_71HZ )
2295: && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Stop_Offset_Low ) ) /* 498/502 depends on 50/60 freq (line cycles-10) */
2296: {
2297: BorderMask |= BORDERMASK_SYNC_HIGH;
2298: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
2299: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = 0;
2300: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = 0;
2301: BlankLines++; /* glue's line counter not incremented, display ends 1 line later */
1.1.1.11 root 2302:
1.1.1.24 root 2303: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res 2 sync high %d<->%d\n" , DE_start , DE_end );
2304: }
1.1.1.11 root 2305:
1.1.1.24 root 2306: else if ( FreqHz == VIDEO_71HZ ) /* rest of the line after HSync_Stop_Offset_Low */
2307: {
2308: /* Next line will start as "remove left" by default (eg E605 by Light or DHS demos on STE) */
2309: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= BORDERMASK_LEFT_OFF;
2310: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
2311: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = pVideoTiming->H_Stop_Hi;
2312: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayPixelShift = -4; /* screen is shifted 4 pixels to the left */
1.1.1.11 root 2313:
1.1.1.24 root 2314: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left on next hbl %d<->%d\n" , DE_start , DE_end );
2315: }
1.1.1.11 root 2316:
1.1.1.24 root 2317: if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles <= DE_end ) && ( LineCycles <= pVideoTiming->H_Stop_Low_60 ) /* 372 */
2318: && !( BorderMask & BORDERMASK_NO_DE ) )
2319: {
2320: //fprintf ( stderr , "pom2\n" );
2321: if ( DE_end == pVideoTiming->H_Stop_Low_50 ) /* 376 */
2322: {
2323: BorderMask |= BORDERMASK_RIGHT_MINUS_2;
2324: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" , DE_start , DE_end );
2325: }
1.1.1.11 root 2326:
1.1.1.24 root 2327: DE_end = pVideoTiming->H_Stop_Low_60; /* 372 */
1.1.1.15 root 2328:
1.1.1.24 root 2329: if ( BorderMask & BORDERMASK_STOP_MIDDLE )
2330: {
2331: BorderMask &= ~BORDERMASK_STOP_MIDDLE;
2332: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel stop middle %d<->%d\n" , DE_start , DE_end );
2333: }
2334: else if ( BorderMask & ( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL ) )
2335: {
2336: BorderMask &= ~( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL );
2337: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~BORDERMASK_LEFT_OFF;
2338: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
2339: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove right/right full %d<->%d\n" , DE_start , DE_end );
2340: }
2341: }
1.1.1.11 root 2342:
1.1.1.24 root 2343: else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= DE_end ) && ( LineCycles <= pVideoTiming->H_Stop_Low_50 ) /* 376 */
2344: && !( BorderMask & BORDERMASK_NO_DE ) )
2345: {
2346: //fprintf ( stderr , "pom3\n" );
2347: DE_end = pVideoTiming->H_Stop_Low_50; /* 376 */
1.1.1.13 root 2348:
1.1.1.24 root 2349: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
2350: {
2351: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
2352: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
2353: }
2354: else if ( BorderMask & BORDERMASK_STOP_MIDDLE )
2355: {
2356: BorderMask &= ~BORDERMASK_STOP_MIDDLE;
2357: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel stop middle %d<->%d\n" , DE_start , DE_end );
2358: }
2359: else if ( BorderMask & ( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL ) )
2360: {
2361: BorderMask &= ~( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL );
2362: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~BORDERMASK_LEFT_OFF;
2363: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
2364: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove right/right full %d<->%d\n" , DE_start , DE_end );
2365: }
2366: }
2367:
2368: else if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles <= DE_end )
2369: && ( LineCycles > pVideoTiming->H_Stop_Low_60 ) && ( LineCycles <= pVideoTiming->H_Stop_Low_50 )
2370: && !( BorderMask & BORDERMASK_NO_DE ) )
2371: {
2372: //fprintf ( stderr , "pom4\n" );
2373: if ( DE_end == pVideoTiming->H_Stop_Low_50 ) /* 376 */
2374: {
2375: DE_end = nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low; /* 458/462 depends on 50/60 freq (line cycles-50) */
2376:
2377: BorderMask |= BORDERMASK_RIGHT_OFF;
2378: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove right %d<->%d\n" , DE_start , DE_end );
1.1.1.15 root 2379:
1.1.1.24 root 2380: if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
2381: {
2382: BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
2383: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
2384: }
2385: }
2386: }
2387:
2388: else if ( ( FreqHz != VIDEO_71HZ )
2389: && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low ) ) /* 458/462 depends on 50/60 freq (line cycles-50) */
2390: {
2391: //fprintf ( stderr , "pom5\n" );
2392: if ( LineCycles <= DE_end )
2393: {
2394: DE_end = nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low; /* 458/462 depends on 50/60 freq (line cycles-50) */
1.1.1.15 root 2395:
1.1.1.24 root 2396: if ( BorderMask & BORDERMASK_RIGHT_OFF_FULL )
2397: {
2398: BorderMask &= ~BORDERMASK_RIGHT_OFF_FULL;
2399: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~BORDERMASK_LEFT_OFF;
2400: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
2401: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove right full %d<->%d\n" , DE_start , DE_end );
2402: }
2403: }
2404:
2405: else if ( BorderMask & BORDERMASK_NO_SYNC )
2406: {
2407: BorderMask &= ~BORDERMASK_NO_SYNC;
2408: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
2409: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
2410: BlankLines--;
2411: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel empty line res 3 no sync %d<->%d\n" , DE_start , DE_end );
2412: }
2413: }
1.1.1.15 root 2414:
1.1.1.24 root 2415: else if ( ( FreqHz != VIDEO_71HZ )
2416: && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Stop_Offset_Low ) ) /* 498/502 depends on 50/60 freq (line cycles-10) */
2417: {
2418: //fprintf ( stderr , "pom6\n" );
2419: if ( BorderMask & BORDERMASK_SYNC_HIGH )
2420: {
2421: BorderMask &= ~BORDERMASK_SYNC_HIGH;
2422: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
2423: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
2424: BlankLines--;
2425: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel empty line res 2 sync high %d<->%d\n" , DE_start , DE_end );
2426: }
2427: }
2428:
2429:
2430: /* Go here directly if there's no more Freq/Res changes to test */
2431: Freq_Test_Done:
2432:
2433: /* Update HBL's position only if display has not reached pos pVideoTiming->Line_Set_Pal */
2434: /* and HBL interrupt was already handled at the beginning of this line. */
2435: if ( ( HBL_Pos > 0 ) && ( HblCounterVideo == nHBL ) )
2436: {
2437: /* Don't modify HBL's position now if we're handling the special HBL for video counter restart */
2438: if ( RestartVideoCounter == false )
2439: Video_AddInterruptHBL ( HblCounterVideo , HBL_Pos );
2440:
2441: ShifterFrame.HBL_CyclePos = HBL_Pos;
2442:
2443: if ( nCyclesPerLine_new > 0 )
2444: nCyclesPerLine_new <<= nCpuFreqShift;
1.1.1.25! root 2445:
1.1.1.24 root 2446: // TODO group the 3 if's into 1
2447:
2448: /* In case we're mixing 50 Hz (512 cycles) and 60 Hz (508 cycles) lines on the same screen, */
2449: /* we must update the position where the next VBL will happen (instead of the initial value in CyclesPerVBL) */
2450: /* We check if number of cycles per line changes only on the last line, and if so, we update the VBL's position */
2451: /* Since we setup VBL at the start of the last HBL, any change of number of cycles per line will */
2452: /* in fact change the position of the next VBL */
2453: if ( ( nCyclesPerLine_new > 0 )
2454: && ( nHBL == nScanlinesPerFrame-1 ) && ( nCyclesPerLine != nCyclesPerLine_new ) )
2455: {
2456: CyclesPerVBL += ( nCyclesPerLine_new - nCyclesPerLine ); /* +4 or -4 */
2457: CycInt_ModifyInterrupt ( nCyclesPerLine_new - nCyclesPerLine , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL );
2458: }
2459:
2460: /* If HBL_Pos changed, the cycles per line might change too */
2461: if ( nCyclesPerLine_new > 0 )
2462: nCyclesPerLine = nCyclesPerLine_new;
2463: }
2464:
2465:
2466: /* Update Timer B's position if DE start/end changed (and this is not an empty 0 byte line) */
2467: /* In most cases Timer B will count end of line events, but it can also count start of line events */
2468: /* (eg 'Seven Gates Of Jambala') so we need to check both start/end values */
2469: if ( ( DE_end > 0 )
2470: && ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle != DE_start )
2471: || ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle != DE_end ) ) )
2472: {
2473: LineTimerBPos = Video_TimerB_GetPosFromDE ( DE_start , DE_end );
2474: Video_AddInterruptTimerB ( HblCounterVideo , LineCycles , LineTimerBPos );
2475: }
2476:
2477:
2478: /*
2479: * Check if top/bottom borders' position should change
2480: */
2481:
2482: /* If first displayed line is not reached yet, we can still change top border */
2483: if ( ( HblCounterVideo < nStartHBL-1 )
2484: || ( ( HblCounterVideo == nStartHBL-1 ) && ( LineCycles <= pVideoTiming->RemoveTopBorder_Pos ) ) )
2485: {
2486: if ( FreqHz == VIDEO_71HZ ) Top_Pos = pVideoTiming->V_Start_Line_Hi;
2487: else if ( FreqHz == VIDEO_60HZ ) Top_Pos = pVideoTiming->V_Start_Line_60;
2488: else Top_Pos = pVideoTiming->V_Start_Line_50;
2489:
2490: /* Change position if new position is not reached yet */
2491: if ( ( Top_Pos != nStartHBL )
2492: && ( ( HblCounterVideo < Top_Pos-1 )
2493: || ( ( HblCounterVideo == Top_Pos-1 ) && ( LineCycles <= pVideoTiming->RemoveTopBorder_Pos ) ) ) )
2494: {
2495: nStartHBL = Top_Pos;
2496: /* If 50 Hz screen and first line is before 50 Hz position -> top border removed */
2497: if ( ( nScreenRefreshRate == VIDEO_50HZ ) && ( nStartHBL < pVideoTiming->V_Start_Line_50 ) )
2498: VerticalOverscan |= V_OVERSCAN_NO_TOP;
2499: else
2500: VerticalOverscan &= ~V_OVERSCAN_NO_TOP;
2501:
2502: VerticalOverscan &= ~V_OVERSCAN_NO_DE;
2503: }
2504: else
2505: {
2506: // TODO CHECK ON STF [NP] : if freq is changed after top_pos and before nStartHBL, then we get a screen
2507: // where vertical DE is never activated ? (eg 60/50 Hz switch at end of line 62)
2508: VerticalOverscan |= V_OVERSCAN_NO_DE;
2509: }
2510: }
2511:
2512: /* If last displayed line not reached yet, we can still change bottom border */
2513: if ( ( HblCounterVideo < nEndHBL-1 )
2514: || ( ( HblCounterVideo == nEndHBL-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
2515: {
2516: if ( FreqHz == VIDEO_71HZ ) Bottom_Pos = pVideoTiming->V_End_Line_Hi;
2517: else if ( FreqHz == VIDEO_60HZ ) Bottom_Pos = pVideoTiming->V_End_Line_60;
2518: else Bottom_Pos = pVideoTiming->V_End_Line_50;
2519:
2520: if ( ( HblCounterVideo < pVideoTiming->V_End_Line_60-1 ) /* 234 */
2521: || ( ( HblCounterVideo == pVideoTiming->V_End_Line_60-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
2522: {
2523: if ( ( nScreenRefreshRate == VIDEO_60HZ ) && ( FreqHz != VIDEO_60HZ ) )
1.1.1.22 root 2524: {
1.1.1.24 root 2525: /* 60 Hz screen where freq != 60 Hz on last line -> bottom border removed */
2526: nEndHBL = pVideoTiming->V_End_Line_NoBottom_60;
2527: VerticalOverscan |= V_OVERSCAN_NO_BOTTOM_60;
1.1.1.22 root 2528: }
1.1.1.24 root 2529: else if ( ( nScreenRefreshRate == VIDEO_50HZ ) && ( FreqHz == VIDEO_60HZ ) )
1.1.1.23 root 2530: {
1.1.1.24 root 2531: /* 50 Hz screen ending at 60 Hz screen's position -> short bottom border (-29 lines) */
2532: nEndHBL = pVideoTiming->V_End_Line_60;
2533: VerticalOverscan |= V_OVERSCAN_BOTTOM_SHORT_50;
1.1.1.23 root 2534: }
1.1.1.24 root 2535: else
1.1.1.23 root 2536: {
1.1.1.24 root 2537: nEndHBL = Bottom_Pos;
2538: VerticalOverscan &= ~( V_OVERSCAN_NO_BOTTOM_60 | V_OVERSCAN_BOTTOM_SHORT_50 );
1.1.1.23 root 2539: }
1.1.1.24 root 2540: }
1.1.1.22 root 2541:
1.1.1.24 root 2542: else if ( ( HblCounterVideo < pVideoTiming->V_End_Line_50-1 ) /* 263 */
2543: || ( ( HblCounterVideo == pVideoTiming->V_End_Line_50-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
2544: {
2545: if ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_60 )
2546: {
2547: /* Bottom border already removed above, can't be changed now */
2548: }
2549: else if ( ( nScreenRefreshRate == VIDEO_50HZ ) && ( FreqHz != VIDEO_50HZ ) )
2550: {
2551: /* 50 Hz screen where freq != 50 Hz on last line -> bottom border removed */
2552: nEndHBL = pVideoTiming->V_End_Line_NoBottom_50;
2553: VerticalOverscan |= V_OVERSCAN_NO_BOTTOM_50;
2554: }
1.1.1.22 root 2555: else
2556: {
1.1.1.24 root 2557: nEndHBL = Bottom_Pos;
2558: VerticalOverscan &= ~V_OVERSCAN_NO_BOTTOM_50;
1.1.1.22 root 2559: }
1.1.1.11 root 2560: }
2561:
1.1.1.24 root 2562: else if ( ( HblCounterVideo < pVideoTiming->V_End_Line_Hi-1 ) /* 434 */
2563: || ( ( HblCounterVideo == pVideoTiming->V_End_Line_Hi-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
1.1.1.22 root 2564: {
1.1.1.24 root 2565: if ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_50 )
2566: {
2567: /* Bottom border already removed above, can't be changed now */
2568: }
2569: else
2570: {
2571: nEndHBL = Bottom_Pos;
2572: }
1.1.1.22 root 2573: }
1.1.1.24 root 2574: }
1.1.1.22 root 2575:
1.1.1.15 root 2576:
1.1.1.24 root 2577: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "video new V_DE %d<->%d H_DE %d<->%d shift=%d border=%x hbl_pos=%d cycles_line=%d video_hbl_w=%d\n" ,
2578: nStartHBL , nEndHBL , DE_start , DE_end ,
2579: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift , BorderMask , HBL_Pos , nCyclesPerLine_new , HblCounterVideo );
2580:
2581: /* Save new values */
2582: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = DE_start;
2583: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = DE_end;
2584: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask = BorderMask;
2585: }
1.1.1.22 root 2586:
1.1.1.15 root 2587:
1.1.1.11 root 2588:
1.1.1.15 root 2589:
1.1.1.24 root 2590: /*-----------------------------------------------------------------------*/
2591: /**
2592: * Write to VideoSync (0xff820a), Hz setting
2593: */
2594: void Video_Sync_WriteByte ( void )
2595: {
2596: int FrameCycles, HblCounterVideo, LineCycles;
2597: Uint8 Freq;
1.1.1.11 root 2598:
2599:
1.1.1.24 root 2600: if ( bUseVDIRes )
2601: return; /* no 50/60 Hz freq in VDI mode */
1.1.1.11 root 2602:
1.1.1.24 root 2603: /* We're only interested in bit 1 (50/60Hz) */
2604: Freq = IoMem[0xff820a] & 2;
1.1.1.11 root 2605:
1.1.1.24 root 2606: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
2607: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.13 root 2608:
1.1.1.24 root 2609: LOG_TRACE(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",
2610: Freq, FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.15 root 2611:
1.1.1.24 root 2612: /* Ignore consecutive writes of the same value */
2613: if ( Freq == ShifterFrame.Freq )
2614: return; /* do nothing */
1.1.1.22 root 2615:
1.1.1.24 root 2616: /* Ignore freq changes if we are in high res */
2617: /* 2009/04/26 : don't ignore for now (see ST Cnx in Punish Your Machine) */
2618: // TODO in res part, update freq when swith to !high
2619: // if ( ShifterFrame.Res == 0x02 )
2620: // return; /* do nothing */
1.1.1.22 root 2621:
1.1.1.15 root 2622:
1.1.1.24 root 2623: Video_Update_Glue_State ( FrameCycles , HblCounterVideo , LineCycles , false );
2624:
2625:
2626: /* TEMP for 'Gen4 Demo' by Ziggy / OVR in WS2,WS3,WS4 : */
2627: /* top border is removed 4 cycles too late (due to double STOP instruction ?) and trigger a wrong "left+2" */
2628: if ( ( STMemory_ReadLong ( 0xc000 ) == 0x69676779 ) /* "iggy" */
2629: && ( M68000_GetPC() == 0x635e )
2630: && ( STMemory_ReadLong ( M68000_GetPC() ) == 0x11fc0002 ) /* move.b #2 */
2631: && ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_PLUS_2 )
2632: )
2633: {
2634: /* cancel a wrong left+2 */
2635: LOG_TRACE(TRACE_VIDEO_BORDER_H , "cancel wrong left+2 gen4/ziggy\n" );
2636: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
2637: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Low_50;
2638: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Low_50;
2639: nCyclesPerLine = 512;
1.1.1.15 root 2640: }
1.1.1.24 root 2641: /* TEMP for 'Gen4 Demo' by Ziggy / OVR in WS2,WS3,WS4 : */
1.1.1.15 root 2642:
2643:
1.1.1.24 root 2644: /* Store cycle position of freq 50/60 to check for top/bottom border removal in Video_EndHBL. */
1.1.1.15 root 2645: ShifterFrame.Freq = Freq;
1.1.1.24 root 2646: if ( Freq == 0x02 ) /* 50 Hz */
1.1.1.15 root 2647: {
2648: ShifterFrame.FreqPos50.VBL = nVBLs;
2649: ShifterFrame.FreqPos50.FrameCycles = FrameCycles;
2650: ShifterFrame.FreqPos50.HBL = HblCounterVideo;
2651: ShifterFrame.FreqPos50.LineCycles = LineCycles;
2652: }
1.1.1.24 root 2653: else /* 60 Hz */
1.1.1.15 root 2654: {
2655: ShifterFrame.FreqPos60.VBL = nVBLs;
2656: ShifterFrame.FreqPos60.FrameCycles = FrameCycles;
2657: ShifterFrame.FreqPos60.HBL = HblCounterVideo;
2658: ShifterFrame.FreqPos60.LineCycles = LineCycles;
2659: }
2660: }
2661:
2662:
2663: /*-----------------------------------------------------------------------*/
2664: /**
1.1.1.24 root 2665: * Compute the default cycle position where the HBL should happen on each line.
1.1.1.15 root 2666: * In low/med res, the position depends on the video frequency (50/60 Hz)
2667: * In high res, the position is always the same.
1.1.1.24 root 2668: * This position can change later when switching between hi res / 50 Hz / 60 Hz
2669: *
2670: * The position is measured for a CPU running at 8 MHz, it should be converted
2671: * to the correct number of cycles when using higher CPU speed (16 or 32 MHz)
1.1.1.15 root 2672: */
1.1.1.24 root 2673: static int Video_HBL_GetDefaultPos ( void )
1.1.1.15 root 2674: {
2675: int Pos;
2676:
2677: if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 ) /* hi res */
1.1.1.24 root 2678: Pos = pVideoTiming->Hbl_Int_Pos_Hi;
1.1.1.15 root 2679:
2680: else /* low res or med res */
2681: {
2682: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz, pos 512 */
1.1.1.24 root 2683: Pos = pVideoTiming->Hbl_Int_Pos_Low_50;
1.1.1.15 root 2684: else /* 60 Hz, pos 508 */
1.1.1.24 root 2685: Pos = pVideoTiming->Hbl_Int_Pos_Low_60;
1.1.1.15 root 2686: }
2687:
2688: return Pos;
2689: }
2690:
2691:
1.1.1.24 root 2692: /**
2693: * Return the current HBL position (depending on the latest switch
2694: * to hi res / 50 Hz / 60 Hz)
2695: */
2696: static int Video_HBL_GetCurrentPos ( void )
2697: {
2698: return ShifterFrame.HBL_CyclePos;
2699: }
2700:
2701:
1.1.1.15 root 2702: /*-----------------------------------------------------------------------*/
2703: /**
2704: * Compute the cycle position where the timer B should happen on each
2705: * visible line.
2706: * We compute Timer B position for the given LineNumber, using start/end
2707: * display cycles from ShifterLines[ LineNumber ].
2708: * The position depends on the start of line / end of line positions
2709: * (which depend on the current frequency / border tricks) and
2710: * on the value of the bit 3 in the MFP's AER.
2711: * If bit is 0, timer B will count end of line events (usual case),
2712: * but if bit is 1, timer B will count start of line events (eg Seven Gates Of Jambala)
1.1.1.24 root 2713: *
2714: * The position is measured for a CPU running at 8 MHz, it should be converted
2715: * to the correct number of cycles when using higher CPU speed (16 or 32 MHz)
1.1.1.15 root 2716: */
1.1.1.24 root 2717:
2718: static int Video_TimerB_GetPosFromDE ( int DE_start , int DE_end )
1.1.1.15 root 2719: {
2720: int Pos;
2721:
2722: if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) /* we're counting end of line events */
1.1.1.24 root 2723: Pos = DE_end + TIMERB_VIDEO_CYCLE_OFFSET;
1.1.1.15 root 2724: else /* we're counting start of line events */
1.1.1.24 root 2725: Pos = DE_start + TIMERB_VIDEO_CYCLE_OFFSET;
2726:
2727: return Pos;
2728: }
2729:
2730:
2731: int Video_TimerB_GetPos ( int LineNumber )
2732: {
2733: int Pos;
2734:
2735: Pos = Video_TimerB_GetPosFromDE ( ShifterFrame.ShifterLines[ LineNumber ].DisplayStartCycle ,
2736: ShifterFrame.ShifterLines[ LineNumber ].DisplayEndCycle );
1.1.1.15 root 2737:
1.1.1.18 root 2738: //fprintf ( stderr , "timerb pos=%d\n" , Pos );
2739: return Pos;
2740: }
2741:
2742:
2743: /*-----------------------------------------------------------------------*/
2744: /**
2745: * Compute the default cycle position where the timer B should happen
2746: * on the next line, when restarting the INTERRUPT_VIDEO_ENDLINE handler.
2747: * In low/med res, the position depends on the video frequency (50/60 Hz)
2748: * In high res, the position is always the same.
2749: */
1.1.1.24 root 2750:
2751: // TODO : use Video_TimerB_GetPosFromDE and Video_AddInterruptTimerB
1.1.1.23 root 2752: static int Video_TimerB_GetDefaultPos ( void )
1.1.1.18 root 2753: {
2754: int Pos;
2755:
2756: if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) /* we're counting end of line events */
2757: {
2758: if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 ) /* hi res */
2759: Pos = LINE_END_CYCLE_71;
2760:
2761: else /* low res or med res */
2762: {
2763: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz, pos 376 */
2764: Pos = LINE_END_CYCLE_50;
2765: else /* 60 Hz, pos 372 */
2766: Pos = LINE_END_CYCLE_60;
2767: }
2768: }
2769: else /* we're counting start of line events */
2770: {
2771: if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 ) /* hi res */
2772: Pos = LINE_START_CYCLE_71;
2773:
2774: else /* low res or med res */
2775: {
2776: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz, pos 56 */
2777: Pos = LINE_START_CYCLE_50;
2778: else /* 60 Hz, pos 52 */
2779: Pos = LINE_START_CYCLE_60;
2780: }
2781: }
2782:
2783: Pos += TIMERB_VIDEO_CYCLE_OFFSET;
2784:
2785: //fprintf ( stderr , "timerb default pos=%d\n" , Pos );
1.1.1.15 root 2786: return Pos;
2787: }
2788:
2789:
2790: /*-----------------------------------------------------------------------*/
2791: /**
2792: * HBL interrupt : this occurs at the end of every line, on cycle 512 (in 50 Hz)
2793: * It takes 56 cycles to handle the 68000's exception.
2794: */
2795: void Video_InterruptHandler_HBL ( void )
2796: {
1.1.1.24 root 2797: int FrameCycles , HblCounterVideo , LineCycles;
1.1.1.15 root 2798: int PendingCyclesOver;
2799: int NewHBLPos;
2800:
1.1.1.24 root 2801: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2802:
1.1.1.15 root 2803: /* How many cycle was this HBL delayed (>= 0) */
2804: PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
1.1.1.24 root 2805: PendingCyclesOver <<= nCpuFreqShift;
1.1.1.15 root 2806:
2807: /* Remove this interrupt from list and re-order */
1.1.1.16 root 2808: CycInt_AcknowledgeInterrupt();
1.1.1.15 root 2809:
1.1.1.24 root 2810:
2811: /* Handle the intermediate HBL interrupt used to restart video counter on HBL 310 or 260 */
2812: if ( RestartVideoCounter )
2813: {
2814: // fprintf ( stderr , "restart video counter check nhbl=%d hbl=%d cyc=%d\n" , nHBL , HblCounterVideo , LineCycles);
2815: if ( ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 2 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_50 ) )
2816: || ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 0 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_60 )
2817: && ( nScanlinesPerFrame == SCANLINES_PER_FRAME_60HZ ) ) )
2818: {
2819: Video_RestartVideoCounter();
2820: LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d cyc=%d restart video counter 0x%x\n", nHBL, LineCycles, VideoBase );
2821: }
2822:
2823: /* Restore the normal HBL interrupt on the same line */
2824: Video_AddInterruptHBL ( nHBL , Video_HBL_GetCurrentPos() );
2825:
2826: RestartVideoCounter = false;
2827: return;
2828: }
2829:
2830:
2831: if (Config_IsMachineFalcon())
2832: {
1.1.1.25! root 2833: VIDEL_VideoRasterHBL();
1.1.1.17 root 2834: }
2835:
2836:
1.1.1.15 root 2837: /* Increment the hbl jitter index */
2838: HblJitterIndex++;
1.1.1.20 root 2839: HblJitterIndex %= HBL_JITTER_ARRAY_SIZE;
1.1.1.15 root 2840:
2841: LOG_TRACE ( TRACE_VIDEO_HBL , "HBL %d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
2842: nHBL , FrameCycles , PendingCyclesOver , HblJitterArray[ HblJitterIndex ] );
2843:
1.1.1.21 root 2844: /* Print traces if pending HBL bit changed just before IACK when HBL interrupt is allowed */
2845: if ( ( CPU_IACK == true ) && ( regs.intmask < 2 ) )
1.1.1.15 root 2846: {
1.1.1.21 root 2847: if ( pendingInterrupts & ( 1 << 2 ) )
2848: {
2849: LOG_TRACE ( TRACE_VIDEO_HBL , "HBL %d, pending set again just before iack, skip one HBL interrupt VBL=%d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
2850: nHBL , nVBLs , FrameCycles , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
2851: }
2852: else
2853: {
2854: LOG_TRACE ( TRACE_VIDEO_HBL , "HBL %d, new pending HBL set just before iack VBL=%d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
2855: nHBL , nVBLs , FrameCycles , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
2856: }
1.1.1.15 root 2857: }
2858:
1.1.1.21 root 2859: /* Set pending bit for HBL interrupt in the CPU IPL */
1.1.1.23 root 2860: M68000_Exception(EXCEPTION_NR_HBLANK , M68000_EXC_SRC_AUTOVEC); /* Horizontal blank interrupt, level 2 */
1.1.1.21 root 2861:
1.1.1.15 root 2862:
1.1.1.25! root 2863: if (!Config_IsMachineFalcon())
! 2864: {
! 2865: Video_EndHBL(); /* Check some borders removal and copy line to display buffer */
! 2866: }
1.1.1.15 root 2867:
1.1.1.17 root 2868: DmaSnd_STE_HBL_Update(); /* Update STE DMA sound if needed */
2869:
1.1.1.22 root 2870: /* TEMP IPF */
2871: IPF_Emulate();
2872: /* TEMP IPF */
2873:
1.1.1.15 root 2874: nHBL++; /* Increase HBL count */
2875:
2876: if (nHBL < nScanlinesPerFrame)
2877: {
1.1.1.24 root 2878: /* Update start cycle for next HBL : start for previous HBL + number of cycles for previous line */
2879: //fprintf ( stderr , "hbl %llu\n" , CyclesGlobalClockCounter );
2880: //if ( FrameCycles - PendingCyclesOver != ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle + nCyclesPerLine )
2881: // fprintf ( stderr , "mismatch StartCycle hbl %d : %d != %d\n" , nHBL , FrameCycles - PendingCyclesOver , ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle + nCyclesPerLine );
2882: ShifterFrame.ShifterLines[ nHBL ].StartCycle = ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle + nCyclesPerLine;
1.1.1.15 root 2883: LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d start=%d %x\n", nHBL,
2884: ShifterFrame.ShifterLines[nHBL].StartCycle, ShifterFrame.ShifterLines[nHBL].StartCycle);
2885:
2886: /* Setup next HBL */
2887: Video_StartHBL();
1.1.1.24 root 2888:
2889: /* Default cycle position for next HBL */
2890: NewHBLPos = Video_HBL_GetDefaultPos();
2891: ShifterFrame.HBL_CyclePos = NewHBLPos;
2892: //fprintf ( stderr , "NewHBLPos %d\n", NewHBLPos );
2893: Video_AddInterruptHBL ( nHBL , NewHBLPos );
2894:
2895: /* Add new VBL interrupt just after the last HBL (for example : VblVideoCycleOffset cycles after end of HBL 312 at 50 Hz) */
2896: /* We setup VBL one HBL earlier (eg at nHBL=312 instead of 313) to be sure we don't miss it */
2897: /* in case last HBL would be delayed too much (by more than VblVideoCycleOffset cycles) */
2898: if (nHBL == nScanlinesPerFrame-1)
2899: {
2900: int CyclesToVbl = ShifterFrame.ShifterLines[nHBL].StartCycle + nCyclesPerLine +
2901: ( pVideoTiming->VblVideoCycleOffset << nCpuFreqShift ) - FrameCycles;
2902: CycInt_AddRelativeInterrupt( CyclesToVbl, INT_CPU_CYCLE, INTERRUPT_VIDEO_VBL);
2903: }
2904: }
2905:
2906: /* Check if video counter should be restarted on this HBL */
2907: if ( RestartVideoCounter )
2908: {
2909: //fprintf ( stderr , "restart video counter nhbl=%d hbl=%d cyc=%d restart_cyc=%d\n" , nHBL , HblCounterVideo , LineCycles, pVideoTiming->RestartVideoCounter_Pos);
2910: /* If HBL was delayed after RestartVideoCounter_Pos, we can restart immediately if we have */
2911: /* the correct freq/hbl combination (we need to check nHBL == HblCounterVideo for the WS1 case where LineCycles can be 508) */
2912: if ( ( nHBL == HblCounterVideo ) && ( LineCycles >= pVideoTiming->RestartVideoCounter_Pos ) )
2913: {
2914: if ( ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 2 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_50 ) )
2915: || ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 0 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_60 ) ) )
2916: {
2917: Video_RestartVideoCounter();
2918: LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d cyc=%d restart video counter 0x%x (immediate)\n", nHBL, LineCycles, VideoBase );
2919: }
2920: RestartVideoCounter = false;
2921: }
2922:
2923: /* HBL was not delayed after RestartVideoCounter_Pos, so we set an intermediate HBL interrupt */
2924: /* This intermediate HBL interrupt will then set the real HBL interrupt at the end of the line */
2925: else
2926: {
2927: Video_AddInterruptHBL ( nHBL , pVideoTiming->RestartVideoCounter_Pos );
2928: }
1.1.1.15 root 2929: }
2930: }
2931:
2932:
2933: /*-----------------------------------------------------------------------*/
2934: /**
2935: * Check at end of each HBL to see if any Shifter hardware tricks have been attempted
2936: * and copy the line to the screen buffer.
2937: * This is the place to check if top/bottom border were removed, as well as if some
2938: * left/right border changes were not validated before.
2939: * NOTE : the tests must be made with nHBL in ascending order.
2940: */
2941: static void Video_EndHBL(void)
2942: {
2943: //
2944: // Handle top/bottom borders removal when switching freq
1.1.1.24 root 2945: // Detection is made in Video_Update_Glue_State() so we just print some logs
1.1.1.15 root 2946: //
2947:
1.1.1.24 root 2948: if ( ( nHBL == nStartHBL + BlankLines - 1 )
2949: && ( VerticalOverscan & V_OVERSCAN_NO_TOP ) )
1.1.1.15 root 2950: {
1.1.1.24 root 2951: /* 50 Hz screen where first line is before 50 Hz position -> top border removed */
1.1.1.15 root 2952: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove top\n" );
2953: pHBLPaletteMasks -= OVERSCAN_TOP; // FIXME useless ?
1.1.1.24 root 2954: pHBLPalettes -= OVERSCAN_TOP; // FIXME useless ?
1.1.1.15 root 2955: }
2956:
1.1.1.24 root 2957: else if ( ( nHBL == pVideoTiming->V_End_Line_50 + BlankLines - 1 )
2958: && ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_50 ) )
1.1.1.13 root 2959: {
1.1.1.24 root 2960: /* 50 Hz screen where freq != 50 Hz on last line -> bottom border removed */
1.1.1.15 root 2961: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove bottom\n" );
2962: }
2963:
1.1.1.24 root 2964: else if ( ( nHBL == pVideoTiming->V_End_Line_60 + BlankLines - 1 )
2965: && ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_60 ) )
2966: {
2967: /* 60 Hz screen where freq != 60 Hz on last line -> bottom border removed */
2968: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove bottom 60Hz\n" );
1.1.1.15 root 2969: }
2970:
1.1.1.24 root 2971: else if ( ( nHBL == pVideoTiming->V_End_Line_60 + BlankLines - 1 )
2972: && ( VerticalOverscan & V_OVERSCAN_BOTTOM_SHORT_50 ) )
2973: {
2974: /* 50 Hz screen ending at 60 Hz screen's position -> short bottom border (-29 lines) */
2975: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect short bottom border\n" );
1.1.1.15 root 2976: }
2977:
2978:
2979: /* Store palette for very first line on screen - HBLPalettes[0] */
2980: if (nHBL == nFirstVisibleHbl-1)
2981: {
2982: /* Store ALL palette for this line into raster table for datum */
2983: Video_StoreFirstLinePalette();
2984: }
2985:
2986: if (bUseHighRes)
2987: {
2988: /* Copy for hi-res (no overscan) */
2989: if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
2990: Video_CopyScreenLineMono();
2991: }
2992: /* Are we in possible visible color display (including borders)? */
2993: else if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
2994: {
1.1.1.24 root 2995: /* Update resolution at the end of the line to check for mix low/med screens */
2996: Video_StoreResolution(nHBL-nFirstVisibleHbl , false);
1.1.1.15 root 2997:
2998: /* Copy line of screen to buffer to simulate TV raster trace
2999: * - required for mouse cursor display/game updates
3000: * Eg, Lemmings and The Killing Game Show are good examples */
3001: Video_CopyScreenLineColor();
3002: }
3003: }
3004:
3005:
3006: /*-----------------------------------------------------------------------*/
3007: /**
3008: * Set default values for the next HBL, depending on the current res/freq.
3009: * We set the number of cycles per line, as well as some default values
3010: * for display start/end cycle.
3011: */
3012: static void Video_StartHBL(void)
3013: {
1.1.1.24 root 3014: RestartVideoCounter = false;
3015:
1.1.1.19 root 3016: if ((IoMem_ReadByte(0xff8260) & 3) == 2) /* hi res */
1.1.1.15 root 3017: {
3018: nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1.1.1.24 root 3019: if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 ) /* start not set yet */
3020: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
3021: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Hi;
3022:
3023: /* If the whole screen is not in 71 Hz, then this HBL will default to "left off" */
3024: if ( nScreenRefreshRate != VIDEO_71HZ )
3025: {
3026: ShifterFrame.ShifterLines[ nHBL ].BorderMask |= BORDERMASK_LEFT_OFF;
3027: ShifterFrame.ShifterLines[ nHBL ].DisplayPixelShift = -4;
3028: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" ,
3029: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
3030: }
1.1.1.15 root 3031: }
3032: else
3033: {
3034: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz */
3035: {
3036: nCyclesPerLine = CYCLES_PER_LINE_50HZ;
3037: if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 ) /* start not set yet */
1.1.1.24 root 3038: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Low_50;
3039: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Low_50;
1.1.1.15 root 3040: }
3041: else /* 60 Hz */
3042: {
3043: nCyclesPerLine = CYCLES_PER_LINE_60HZ;
3044: if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 ) /* start not set yet */
1.1.1.24 root 3045: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Low_60;
3046: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Low_60;
3047:
3048: /* If the whole screen is in 50 Hz, then this HBL will default to "left+2" + "right-2" (ie 60 Hz line) */
3049: if ( nScreenRefreshRate == VIDEO_50HZ )
3050: {
3051: ShifterFrame.ShifterLines[ nHBL ].BorderMask |= ( BORDERMASK_LEFT_PLUS_2 | BORDERMASK_RIGHT_MINUS_2 ) ;
3052: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 / right-2 60Hz %d<->%d\n" ,
3053: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
3054: }
1.1.1.15 root 3055: }
1.1.1.24 root 3056:
3057: /* Handle accurate restart of video counter only in low/med res */
3058: if ( ( nHBL == pVideoTiming->RestartVideoCounter_Line_50 ) || ( nHBL == pVideoTiming->RestartVideoCounter_Line_60 ) )
3059: RestartVideoCounter = true;
1.1.1.15 root 3060: }
1.1.1.24 root 3061:
3062: nCyclesPerLine <<= nCpuFreqShift;
1.1.1.25! root 3063:
1.1.1.21 root 3064: //fprintf (stderr , "Video_StartHBL %d %d %d\n", nHBL , ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
1.1.1.24 root 3065:
3066: if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
3067: {
3068: /* Store resolution at the beginning of the line */
3069: Video_StoreResolution(nHBL-nFirstVisibleHbl , true);
3070: }
1.1.1.15 root 3071: }
3072:
3073:
3074: /*-----------------------------------------------------------------------*/
3075: /**
3076: * End Of Line interrupt
3077: * This interrupt is started on cycle position 404 in 50 Hz and on cycle
3078: * position 400 in 60 Hz. 50 Hz display ends at cycle 376 and 60 Hz displays
1.1.1.21 root 3079: * ends at cycle 372. This means the EndLine interrupt happens 24 cycles
1.1.1.15 root 3080: * after DisplayEndCycle.
3081: * Note that if bit 3 of MFP AER is 1, then timer B will count start of line
1.1.1.21 root 3082: * instead of end of line (at cycle 52+24 or 56+24)
1.1.1.15 root 3083: */
3084: void Video_InterruptHandler_EndLine(void)
3085: {
3086: int FrameCycles, HblCounterVideo, LineCycles;
3087: int PendingCycles = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
3088:
3089: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3090:
3091: LOG_TRACE ( TRACE_VIDEO_HBL , "EndLine TB %d video_cyc=%d line_cyc=%d pending_int_cnt=%d\n" ,
3092: nHBL , FrameCycles , LineCycles , PendingCycles );
3093:
3094: /* Remove this interrupt from list and re-order */
1.1.1.16 root 3095: CycInt_AcknowledgeInterrupt();
1.1.1.15 root 3096:
3097: /* Ignore HBLs in VDI mode */
3098: if (bUseVDIRes)
3099: return;
3100:
3101: /* Generate new Endline, if need to - there are 313 HBLs per frame */
3102: if (nHBL < nScanlinesPerFrame-1)
3103: {
1.1.1.21 root 3104: /* By default, next EndLine's int will be on line nHBL+1 at pos 376+24 or 372+24 */
1.1.1.15 root 3105: if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) /* count end of line */
1.1.1.13 root 3106: {
1.1.1.15 root 3107: /* If EndLine int is delayed too much (more than 100 cycles), nLineCycles will */
3108: /* be in the range 0..xxx instead of 400..512. In that case, we need to add */
3109: /* nCyclesPerLine to be in the range 512..x+512 */
3110: /* Maximum possible delay should be around 160 cycles on STF (DIVS) */
3111: /* In that case, HBL int will be delayed too, so we will have HblCounterVideo == nHBL+1 */
3112: if ( HblCounterVideo == nHBL+1 ) /* int happened in fact on the next line nHBL+1 */
3113: LineCycles += nCyclesPerLine;
1.1.1.13 root 3114:
1.1.1.24 root 3115: LineTimerBPos = Video_TimerB_GetDefaultPos ();
1.1.1.13 root 3116: }
3117:
1.1.1.15 root 3118: else /* count start of line, no possible delay to handle */
1.1.1.13 root 3119: {
1.1.1.24 root 3120: LineTimerBPos = Video_TimerB_GetDefaultPos ();
1.1.1.13 root 3121: }
3122:
1.1.1.24 root 3123: // TODO : use Video_AddInterruptTimerB
3124: //fprintf ( stderr , "new tb %d %d %d\n" , LineTimerBPos , nCyclesPerLine , (LineTimerBPos<<nCpuFreqShift) - LineCycles + nCyclesPerLine );
3125: LineTimerBPos <<= nCpuFreqShift; /* convert Pos at 8 MHz into number of cycles at 8/16/32 MHz */
3126: CycInt_AddRelativeInterrupt ( LineTimerBPos - LineCycles + nCyclesPerLine,
1.1.1.15 root 3127: INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
1.1.1.11 root 3128: }
3129:
1.1.1.15 root 3130: /* Timer B occurs at END of first visible screen line in Event Count mode */
1.1.1.18 root 3131: if ( ( nHBL >= nStartHBL ) && ( nHBL < nEndHBL + BlankLines ) )
1.1.1.11 root 3132: {
1.1.1.15 root 3133: /* Handle Timer B when using Event Count mode */
3134: /* We must ensure that the write to fffa1b to activate timer B was */
3135: /* completed before the point where the end of line signal was generated */
3136: /* (in the case of a move.b #8,$fffa1b that would happen 4 cycles */
3137: /* before end of line, the interrupt should not be generated) */
3138: if ( (MFP_TBCR == 0x08) /* Is timer in Event Count mode ? */
3139: && ( ( TimerBEventCountCycleStart == -1 ) /* timer B was started during a previous VBL */
3140: || ( TimerBEventCountCycleStart < FrameCycles-PendingCycles ) ) ) /* timer B was started before this possible interrupt */
1.1.1.21 root 3141: MFP_TimerB_EventCount_Interrupt ( PendingCycles ); /* we have a valid timer B interrupt */
1.1.1.11 root 3142: }
1.1 root 3143: }
3144:
1.1.1.2 root 3145:
1.1.1.15 root 3146:
3147:
1.1.1.2 root 3148: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3149: /**
3150: * Store whole palette on first line so have reference to work from
3151: */
1.1.1.7 root 3152: static void Video_StoreFirstLinePalette(void)
1.1 root 3153: {
1.1.1.11 root 3154: Uint16 *pp2;
3155: int i;
3156:
3157: pp2 = (Uint16 *)&IoMem[0xff8240];
3158: for (i = 0; i < 16; i++)
1.1.1.22 root 3159: {
1.1.1.11 root 3160: HBLPalettes[i] = SDL_SwapBE16(*pp2++);
1.1.1.24 root 3161: if (Config_IsMachineST())
1.1.1.22 root 3162: HBLPalettes[i] &= 0x777; /* Force unused "random" bits to 0 */
3163: }
1.1 root 3164:
1.1.1.11 root 3165: /* And set mask flag with palette and resolution */
3166: // FIXME ; enlever PALETTEMASK_RESOLUTION
3167:
1.1.1.15 root 3168: // if ( ShifterFrame.ShifterLines[ nFirstVisibleHbl ].BorderMask == BORDERMASK_NONE ) // no border trick, store the current res
1.1.1.11 root 3169: HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((Uint32)IoMem_ReadByte(0xff8260)&0x3)<<16);
3170: // else // border removal, assume low res for the whole line
3171: // HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (0<<16);
1.1 root 3172: }
3173:
1.1.1.2 root 3174:
3175: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3176: /**
3177: * Store resolution on each line (used to test if mixed low/medium resolutions)
1.1.1.24 root 3178: * This functions is called twice per line :
3179: * - during Video_StartHBL (start=true) to set the default resolution when
3180: * starting a new line
3181: * - during Video_EndHBL (start=false) to update the resolution before rendering
3182: * the line on the screen (in case some border removals were used during this line)
1.1.1.11 root 3183: */
1.1.1.24 root 3184: static void Video_StoreResolution(int y , bool start)
1.1 root 3185: {
1.1.1.11 root 3186: Uint8 res;
3187: int Mask;
1.1 root 3188:
1.1.1.11 root 3189: /* Clear resolution, and set with current value */
3190: if (!(bUseHighRes || bUseVDIRes))
3191: {
1.1.1.20 root 3192: if ( y >= HBL_PALETTE_MASKS ) /* we're above the limit (res was switched to mono for more than 1 VBL in color mode ?) */
3193: {
3194: // fprintf ( stderr , "store res %d line %d hbl %d %x %x %d\n" , res , y , nHBL, Mask , HBLPaletteMasks[y] , sizeof(HBLPalettes) );
3195: y = HBL_PALETTE_MASKS - 1; /* store in the last palette line */
3196: }
3197:
1.1.1.24 root 3198: if ( start ) /* Just store current resolution */
3199: res = IoMem_ReadByte(0xff8260)&0x3;
3200: else /* Check if resolution needs to be forced to low/med */
3201: {
3202: res = ( HBLPaletteMasks[y] >> 16 ) & 0x3;
3203: Mask = ShifterFrame.ShifterLines[ y+nFirstVisibleHbl ].BorderMask;
3204:
3205: if ( Mask & BORDERMASK_OVERSCAN_MED_RES ) /* special case for med res to render the overscan line */
3206: res = 1; /* med res instead of low res */
3207: else if ( Mask != BORDERMASK_NONE ) /* border removal : assume low res for the whole line */
3208: res = 0;
3209: }
1.1.1.2 root 3210:
1.1.1.24 root 3211: HBLPaletteMasks[y] &= ~(0x3<<16);
1.1.1.11 root 3212: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)res)<<16;
3213:
3214: // fprintf ( stderr , "store res %d line %d %x %x\n" , res , y , Mask , HBLPaletteMasks[y] );
3215: }
1.1.1.9 root 3216: }
3217:
3218:
3219: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3220: /**
3221: * Copy one line of monochrome screen into buffer for conversion later.
3222: */
3223: static void Video_CopyScreenLineMono(void)
3224: {
3225: /* Copy one line - 80 bytes in ST high resolution */
3226: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_MONOLINE);
3227: pVideoRaster += SCREENBYTES_MONOLINE;
3228:
3229: /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
3230: if (HWScrollCount)
3231: {
3232: Uint16 *pScrollAdj;
3233: int nNegScrollCnt;
3234:
3235: pScrollAdj = (Uint16 *)pSTScreen;
3236: nNegScrollCnt = 16 - HWScrollCount;
3237:
3238: /* Shift the whole line by the given scroll count */
3239: while ((Uint8*)pScrollAdj < pSTScreen + SCREENBYTES_MONOLINE-2)
3240: {
3241: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
3242: | (do_get_mem_word(pScrollAdj+1) >> nNegScrollCnt));
3243: ++pScrollAdj;
3244: }
3245:
3246: /* Handle the last 16 pixels of the line */
3247: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
3248: | (do_get_mem_word(pVideoRaster) >> nNegScrollCnt));
3249:
3250: /* HW scrolling advances Shifter video counter by one */
3251: pVideoRaster += 1 * 2;
3252: }
3253:
3254: /* LineWidth is zero on ST. */
3255: /* On STE, the Shifter skips the given amount of words. */
3256: pVideoRaster += LineWidth*2;
3257:
3258: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
3259: /* that occurred while the display was already ON */
1.1.1.16 root 3260: if ( VideoCounterDelayedOffset != 0 )
1.1.1.11 root 3261: {
1.1.1.16 root 3262: pVideoRaster += ( VideoCounterDelayedOffset & ~1 );
3263: VideoCounterDelayedOffset = 0;
1.1.1.11 root 3264: }
1.1.1.16 root 3265:
3266: if ( pVideoRasterDelayed != NULL )
1.1.1.11 root 3267: {
1.1.1.16 root 3268: pVideoRaster = pVideoRasterDelayed;
3269: pVideoRasterDelayed = NULL;
1.1.1.11 root 3270: }
3271:
3272: /* On STE, if we wrote to the hwscroll register, we set the */
3273: /* new value here, once the current line was processed */
3274: if ( NewHWScrollCount >= 0 )
3275: {
3276: HWScrollCount = NewHWScrollCount;
3277: NewHWScrollCount = -1;
3278: }
3279:
3280: /* On STE, if we wrote to the linewidth register, we set the */
3281: /* new value here, once the current line was processed */
3282: if ( NewLineWidth >= 0 )
3283: {
3284: LineWidth = NewLineWidth;
3285: NewLineWidth = -1;
3286: }
3287:
3288: /* Each screen line copied to buffer is always same length */
3289: pSTScreen += SCREENBYTES_MONOLINE;
1.1.1.22 root 3290:
3291: /* We must keep the new video address in a 24 bit space */
3292: /* (in case it pointed to IO space and is now >= 0x1000000) */
3293: pVideoRaster = ( ( pVideoRaster - STRam ) & 0xffffff ) + STRam;
1.1.1.11 root 3294: }
3295:
3296:
3297: /*-----------------------------------------------------------------------*/
3298: /**
3299: * Copy one line of color screen into buffer for conversion later.
3300: * Possible lines may be top/bottom border, and/or left/right borders.
3301: */
1.1.1.9 root 3302: static void Video_CopyScreenLineColor(void)
3303: {
1.1.1.15 root 3304: int LineBorderMask;
1.1.1.11 root 3305: int VideoOffset = 0;
3306: int STF_PixelScroll = 0;
1.1.1.14 root 3307: int LineRes;
1.1.1.17 root 3308: Uint8 *pVideoRasterEndLine; /* addr of the last byte copied from pVideoRaster to pSTScreen (for HWScrollCount) */
3309: int i;
1.1.1.24 root 3310: Uint32 VideoMask;
1.1.1.14 root 3311:
1.1.1.15 root 3312: LineBorderMask = ShifterFrame.ShifterLines[ nHBL ].BorderMask;
1.1.1.16 root 3313: STF_PixelScroll = ShifterFrame.ShifterLines[ nHBL ].DisplayPixelShift;
1.1.1.15 root 3314:
1.1.1.25! root 3315: /* We must keep the new video address in a 22 or 24 bit space depending on the machine type */
! 3316: /* (for example in case it pointed to IO space and is now >= 0x1000000) */
! 3317: VideoMask = ( DMA_MaskAddressHigh() << 16 ) | 0xffff; /* 0x3fffff or 0xffffff */
! 3318:
1.1.1.15 root 3319: /* Get resolution for this line (in case of mixed low/med screen) */
1.1.1.20 root 3320: i = nHBL-nFirstVisibleHbl;
3321: if ( i >= HBL_PALETTE_MASKS )
3322: i = HBL_PALETTE_MASKS - 1;
3323: LineRes = ( HBLPaletteMasks[i] >> 16 ) & 1; /* 0=low res 1=med res */
1.1.1.11 root 3324:
1.1.1.16 root 3325: //fprintf(stderr , "copy line %d start %d end %d 0x%x 0x%x\n" , nHBL, nStartHBL, nEndHBL, LineBorderMask, pVideoRaster - STRam);
1.1.1.11 root 3326:
1.1.1.25! root 3327: /* If a line is left+2 / right-2 but the whole screen is in 60 Hz, then it's a normal 60 Hz line, */
! 3328: /* not a 50 Hz line with different borders */
! 3329: if ( ( nScreenRefreshRate == VIDEO_60HZ )
! 3330: && ( ( LineBorderMask & ( BORDERMASK_LEFT_PLUS_2 | BORDERMASK_RIGHT_MINUS_2 ) ) == ( BORDERMASK_LEFT_PLUS_2 | BORDERMASK_RIGHT_MINUS_2 ) ) )
! 3331: {
! 3332: LineBorderMask &= ~( BORDERMASK_LEFT_PLUS_2 | BORDERMASK_RIGHT_MINUS_2 );
! 3333: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 / right-2, normal 60Hz line on 60 Hz screen %d<->%d\n" ,
! 3334: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
! 3335: }
! 3336:
! 3337:
1.1.1.22 root 3338: /* FIXME [NP] : when removing left border and displaying med res at 60 Hz on STE, we have a 3 pixel shift */
3339: /* to correct to have bitmaps and color changes in sync. */
3340: /* For now we only shift for med @ 60 Hz, but this should be measured for all */
3341: /* freq and low / med res combinations on a real STE (fix "HighResMode" demo by Paradox). */
1.1.1.24 root 3342: if ( Config_IsMachineSTE()
1.1.1.22 root 3343: && ( LineBorderMask & BORDERMASK_LEFT_OFF_MED )
3344: && ( nCyclesPerLine == 508 )
3345: )
3346: {
3347: STF_PixelScroll = 3;
3348: }
3349:
1.1.1.11 root 3350: /* If left border is opened, we need to compensate one missing word in low res (1 plan) */
1.1.1.15 root 3351: /* If overscan is in med res, the offset is variable */
3352: if ( LineBorderMask & BORDERMASK_OVERSCAN_MED_RES )
3353: VideoOffset = - ( ( LineBorderMask >> 20 ) & 0x0f ); /* No Cooper=0 PYM=-2 in med res overscan */
1.1.1.11 root 3354:
3355: else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1.1.1.18 root 3356: {
3357: int ShiftPixels = 0;
3358:
3359: if ( STF_PixelScroll == 13 ) { VideoOffset = 2; ShiftPixels = 8; }
3360: else if ( STF_PixelScroll == 9 ) { VideoOffset = 0; ShiftPixels = 8; }
3361: else if ( STF_PixelScroll == 5 ) { VideoOffset = -2; ShiftPixels = 8; }
3362: else if ( STF_PixelScroll == 1 ) { VideoOffset = -4; ShiftPixels = 8; }
3363:
3364: else VideoOffset = -2; /* Normal low res left border removal without 4 pixels scrolling */
3365:
3366: STF_PixelScroll -= ShiftPixels;
3367: }
1.1.1.11 root 3368:
1.1.1.16 root 3369: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_2_STE )
3370: VideoOffset = -4; /* 4 first bytes of the line are not shown */
3371:
1.1.1.11 root 3372: /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
1.1.1.24 root 3373: /* as well as 'remove left + med stab' ('Closure' demo Troed/Sync) */
1.1.1.11 root 3374: /* Depending on the number of pixels, we need to compensate for some skipped words */
1.1.1.15 root 3375: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_MED )
1.1.1.11 root 3376: {
1.1.1.24 root 3377: /* TEMP for 'Closure' in STE : planes are shifted and pixels are not aligned */
3378: if ( Config_IsMachineSTE() && ( STF_PixelScroll == 0 ) )
3379: {
3380: VideoOffset = -6;
3381: STF_PixelScroll -= 10; /* FIXME : should be measured on real STE */
3382: }
3383: /* TEMP for 'Closure' in STE : planes are shifted and pixels are not aligned */
3384:
3385: else
3386: {
1.1.1.16 root 3387: if ( STF_PixelScroll == 13 ) VideoOffset = 2;
3388: else if ( STF_PixelScroll == 9 ) VideoOffset = 0;
3389: else if ( STF_PixelScroll == 5 ) VideoOffset = -2;
3390: else if ( STF_PixelScroll == 1 ) VideoOffset = -4;
1.1.1.24 root 3391: else if ( STF_PixelScroll == 0 ) VideoOffset = -4; /* 'Closure' in STF : no 4 pixels scroll, but planes are shifted */
1.1.1.11 root 3392: else VideoOffset = 0; /* never used ? */
3393:
1.1.1.24 root 3394: STF_PixelScroll -= 8; /* removing left border in med res also shifts display to the left */
3395: }
3396:
1.1.1.11 root 3397: // fprintf(stderr , "scr off %d %d\n" , STF_PixelScroll , VideoOffset);
3398: }
3399:
1.1.1.13 root 3400:
1.1.1.11 root 3401: /* Is total blank line? I.e. top/bottom border? */
1.1.1.18 root 3402: if ((nHBL < nStartHBL) || (nHBL >= nEndHBL + BlankLines)
1.1.1.24 root 3403: || (LineBorderMask & ( BORDERMASK_EMPTY_LINE|BORDERMASK_NO_DE ) ))
1.1.1.11 root 3404: {
3405: /* Clear line to color '0' */
3406: memset(pSTScreen, 0, SCREENBYTES_LINE);
3407: }
3408: else
3409: {
1.1.1.18 root 3410: /* Does have left border ? */
1.1.1.16 root 3411: if ( LineBorderMask & ( BORDERMASK_LEFT_OFF | BORDERMASK_LEFT_OFF_MED ) ) /* bigger line by 26 bytes on the left */
1.1.1.11 root 3412: {
3413: pVideoRaster += BORDERBYTES_LEFT-SCREENBYTES_LEFT+VideoOffset;
3414: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_LEFT);
3415: pVideoRaster += SCREENBYTES_LEFT;
3416: }
1.1.1.16 root 3417: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_2_STE ) /* bigger line by 20 bytes on the left (STE specific) */
3418: { /* bytes 0-3 are not shown, only next 16 bytes (32 pixels, 4 bitplanes) */
3419: if ( SCREENBYTES_LEFT > BORDERBYTES_LEFT_2_STE )
3420: {
3421: memset ( pSTScreen, 0, SCREENBYTES_LEFT-BORDERBYTES_LEFT_2_STE+4 ); /* clear unused pixels + bytes 0-3 */
3422: memcpy ( pSTScreen+SCREENBYTES_LEFT-BORDERBYTES_LEFT_2_STE+4, pVideoRaster+VideoOffset+4, BORDERBYTES_LEFT_2_STE-4 );
3423: }
3424: else
3425: memcpy ( pSTScreen, pVideoRaster+BORDERBYTES_LEFT_2_STE-SCREENBYTES_LEFT+VideoOffset, SCREENBYTES_LEFT );
3426:
3427: pVideoRaster += BORDERBYTES_LEFT_2_STE+VideoOffset;
3428: }
3429: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2) /* bigger line by 2 bytes on the left */
1.1.1.11 root 3430: {
1.1.1.16 root 3431: if ( SCREENBYTES_LEFT > 2 )
3432: {
3433: memset(pSTScreen,0,SCREENBYTES_LEFT-2); /* clear unused pixels */
3434: memcpy(pSTScreen+SCREENBYTES_LEFT-2, pVideoRaster, 2);
3435: }
3436: else
3437: { /* nothing to copy, left border is not large enough */
3438: }
3439:
1.1.1.11 root 3440: pVideoRaster += 2;
3441: }
1.1.1.16 root 3442: else if (bSteBorderFlag) /* bigger line by 8 bytes on the left (STE specific) */
1.1.1.12 root 3443: {
1.1.1.16 root 3444: if ( SCREENBYTES_LEFT > 4*2 )
3445: {
3446: memset(pSTScreen,0,SCREENBYTES_LEFT-4*2); /* clear unused pixels */
3447: memcpy(pSTScreen+SCREENBYTES_LEFT-4*2, pVideoRaster, 4*2);
3448: }
3449: else
3450: { /* nothing to copy, left border is not large enough */
3451: }
3452:
1.1.1.12 root 3453: pVideoRaster += 4*2;
3454: }
1.1.1.11 root 3455: else
1.1.1.16 root 3456: memset(pSTScreen,0,SCREENBYTES_LEFT); /* left border not removed, clear to color '0' */
1.1.1.11 root 3457:
3458: /* Short line due to hires in the middle ? */
3459: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
3460: {
3461: /* 106 bytes less in the line */
3462: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE-106);
1.1.1.12 root 3463: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-106, 0, 106); /* clear unused pixels */
1.1.1.11 root 3464: pVideoRaster += (SCREENBYTES_MIDDLE-106);
3465: }
3466: else
3467: {
3468: /* normal middle part (160 bytes) */
1.1.1.25! root 3469: //#define MMU_TEST
! 3470: #ifndef MMU_TEST
1.1.1.11 root 3471: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE);
1.1.1.25! root 3472: #else
! 3473: /* Use a slower/more accurate rendering where each word is dynamically read from memory */
! 3474: /* (this is used to test MMU translation and when video address points after end of RAM) */
! 3475: for ( i=0 ; i<80 ; i++ )
! 3476: do_put_mem_word ( pSTScreen+SCREENBYTES_LEFT+i*2 , (Uint16)get_word ( ( pVideoRaster-STRam+i*2 ) & VideoMask ) );
! 3477: #endif
1.1.1.11 root 3478: pVideoRaster += SCREENBYTES_MIDDLE;
3479: }
3480:
3481: /* Does have right border ? */
3482: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
3483: {
3484: memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE, pVideoRaster, SCREENBYTES_RIGHT);
1.1.1.17 root 3485: pVideoRasterEndLine = pVideoRaster + SCREENBYTES_RIGHT;
1.1.1.16 root 3486: pVideoRaster += BORDERBYTES_RIGHT;
1.1.1.11 root 3487: }
3488: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
3489: {
3490: /* Shortened line by 2 bytes */
3491: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-2, 0, SCREENBYTES_RIGHT+2);
3492: pVideoRaster -= 2;
1.1.1.17 root 3493: pVideoRasterEndLine = pVideoRaster;
1.1.1.11 root 3494: }
3495: else
3496: {
3497: /* Simply clear right border to '0' */
3498: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
1.1.1.17 root 3499: pVideoRasterEndLine = pVideoRaster;
1.1.1.11 root 3500: }
3501:
1.1.1.18 root 3502: /* Shifter read bytes and borders can change, but display is blank, so finally clear the line with color 0 */
1.1.1.24 root 3503: if (LineBorderMask & ( BORDERMASK_BLANK_LINE | BORDERMASK_BLANK ) )
1.1.1.18 root 3504: memset(pSTScreen, 0, SCREENBYTES_LINE);
3505:
1.1.1.11 root 3506: /* Full right border removal up to the end of the line (cycle 512) */
3507: if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
3508: pVideoRaster += BORDERBYTES_RIGHT_FULL;
3509:
3510: /* Correct the offset for pVideoRaster from BORDERMASK_LEFT_OFF above if needed */
3511: pVideoRaster -= VideoOffset; /* VideoOffset is 0 or -2 */
3512:
3513:
3514: /* STE specific */
1.1.1.13 root 3515: if (!bSteBorderFlag && HWScrollCount) /* Handle STE fine scrolling (HWScrollCount is zero on ST) */
1.1.1.11 root 3516: {
1.1.1.13 root 3517: Uint16 *pScrollAdj; /* Pointer to actual position in line */
1.1.1.11 root 3518: int nNegScrollCnt;
1.1.1.13 root 3519: Uint16 *pScrollEndAddr; /* Pointer to end of the line */
1.1.1.11 root 3520:
3521: nNegScrollCnt = 16 - HWScrollCount;
3522: if (LineBorderMask & BORDERMASK_LEFT_OFF)
3523: pScrollAdj = (Uint16 *)pSTScreen;
1.1.1.16 root 3524: else if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
3525: {
3526: if ( SCREENBYTES_LEFT > BORDERBYTES_LEFT_2_STE )
1.1.1.17 root 3527: pScrollAdj = (Uint16 *)(pSTScreen+8); /* don't scroll the 8 first bytes (keep color 0)*/
1.1.1.16 root 3528: else
3529: pScrollAdj = (Uint16 *)pSTScreen; /* we render less bytes on screen than a real ST, scroll the whole line */
3530: }
1.1.1.11 root 3531: else
3532: pScrollAdj = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT);
1.1.1.16 root 3533:
1.1.1.17 root 3534: /* When shifting the line to the left, we will have 'HWScrollCount' missing pixels at */
3535: /* the end of the line. We must complete these last 16 pixels with pixels from the */
3536: /* video counter last accessed value in pVideoRasterEndLine. */
3537: /* There're 2 passes : */
3538: /* - shift whole line except the last 16 pixels */
3539: /* - shift/complete the last 16 pixels */
3540:
3541: /* Addr of the last byte to shift in the 1st pass (excluding the last 16 pixels of the line) */
1.1.1.11 root 3542: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
3543: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LINE - 8);
3544: else
3545: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8);
3546:
1.1.1.17 root 3547:
3548: if ( LineRes == 1 ) /* med res */
1.1.1.11 root 3549: {
1.1.1.15 root 3550: /* in med res, 16 pixels are 4 bytes, not 8 as in low res, so only the last 4 bytes need a special case */
1.1.1.17 root 3551: pScrollEndAddr += 2; /* 2 Uint16 = 4 bytes = 16 pixels */
1.1.1.14 root 3552:
1.1.1.17 root 3553: /* Shift the whole line to the left by the given scroll count (except the last 16 pixels) */
1.1.1.14 root 3554: while (pScrollAdj < pScrollEndAddr)
3555: {
3556: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
3557: | (do_get_mem_word(pScrollAdj+2) >> nNegScrollCnt));
3558: ++pScrollAdj;
3559: }
1.1.1.17 root 3560: /* Handle the last 16 pixels of the line (complete the line with pixels from pVideoRasterEndLine) */
3561: for ( i=0 ; i<2 ; i++ )
3562: do_put_mem_word(pScrollAdj+i, (do_get_mem_word(pScrollAdj+i) << HWScrollCount)
3563: | (do_get_mem_word(pVideoRasterEndLine+i*2) >> nNegScrollCnt));
1.1.1.14 root 3564:
3565: /* Depending on whether $ff8264 or $ff8265 was used to scroll, */
3566: /* we prefetched 16 pixel (4 bytes) */
1.1.1.13 root 3567: if ( HWScrollPrefetch == 1 ) /* $ff8265 prefetches 16 pixels */
3568: pVideoRaster += 2 * 2; /* 2 bitplans */
1.1.1.14 root 3569:
3570: /* If scrolling with $ff8264, there's no prefetch, which means display starts */
3571: /* 16 pixels later but still stops at the normal point (eg we display */
3572: /* (320-16) pixels in low res). We shift the whole line 4 bytes to the right to */
3573: /* get the correct result (using memmove, as src/dest are overlapping). */
3574: else
3575: {
3576: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
3577: memmove ( pSTScreen+4 , pSTScreen , SCREENBYTES_LINE - 4 );
3578: else
3579: memmove ( pSTScreen+4 , pSTScreen , SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 4 );
3580:
3581: memset ( pSTScreen , 0 , 4 ); /* first 16 pixels are color '0' */
3582: }
1.1.1.11 root 3583: }
1.1.1.14 root 3584:
1.1.1.17 root 3585: else /* low res */
1.1.1.11 root 3586: {
1.1.1.17 root 3587: /* Shift the whole line to the left by the given scroll count (except the last 16 pixels) */
1.1.1.11 root 3588: while (pScrollAdj < pScrollEndAddr)
3589: {
3590: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
3591: | (do_get_mem_word(pScrollAdj+4) >> nNegScrollCnt));
3592: ++pScrollAdj;
3593: }
1.1.1.17 root 3594: /* Handle the last 16 pixels of the line (complete the line with pixels from pVideoRasterEndLine) */
3595: for ( i=0 ; i<4 ; i++ )
3596: do_put_mem_word(pScrollAdj+i, (do_get_mem_word(pScrollAdj+i) << HWScrollCount)
3597: | (do_get_mem_word(pVideoRasterEndLine+i*2) >> nNegScrollCnt));
1.1.1.13 root 3598:
3599: /* Depending on whether $ff8264 or $ff8265 was used to scroll, */
3600: /* we prefetched 16 pixel (8 bytes) */
3601: if ( HWScrollPrefetch == 1 ) /* $ff8265 prefetches 16 pixels */
3602: pVideoRaster += 4 * 2; /* 4 bitplans */
3603:
3604: /* If scrolling with $ff8264, there's no prefetch, which means display starts */
3605: /* 16 pixels later but still stops at the normal point (eg we display */
3606: /* (320-16) pixels in low res). We shift the whole line 8 bytes to the right to */
3607: /* get the correct result (using memmove, as src/dest are overlapping). */
3608: else
3609: {
3610: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
3611: memmove ( pSTScreen+8 , pSTScreen , SCREENBYTES_LINE - 8 );
3612: else
3613: memmove ( pSTScreen+8 , pSTScreen , SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8 );
3614:
3615: memset ( pSTScreen , 0 , 8 ); /* first 16 pixels are color '0' */
3616: }
1.1.1.11 root 3617:
3618: /* On STE, when we have a 230 bytes overscan line and HWScrollCount > 0 */
1.1.1.17 root 3619: /* we must read 6 bytes less than expected if scrolling is using prefetching ($ff8265) */
3620: /* (this is not the case for the 224 bytes overscan which is a multiple of 8) */
1.1.1.11 root 3621: if ( (LineBorderMask & BORDERMASK_LEFT_OFF) && (LineBorderMask & BORDERMASK_RIGHT_OFF) )
1.1.1.17 root 3622: {
3623: if ( HWScrollPrefetch == 1 )
3624: pVideoRaster -= 6; /* we don't add 8 bytes (see above), but 2 */
3625: else
3626: pVideoRaster -= 0;
3627: }
1.1.1.11 root 3628:
3629: }
3630: }
3631:
3632: /* LineWidth is zero on ST. */
3633: /* On STE, the Shifter skips the given amount of words. */
3634: pVideoRaster += LineWidth*2;
3635:
3636: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
3637: /* that occurred while the display was already ON */
1.1.1.16 root 3638: if ( VideoCounterDelayedOffset != 0 )
1.1.1.11 root 3639: {
1.1.1.24 root 3640: // fprintf ( stderr , "adjust video counter offset=%d old video=%x\n" , VideoCounterDelayedOffset , pVideoRaster-STRam );
1.1.1.16 root 3641: pVideoRaster += ( VideoCounterDelayedOffset & ~1 );
3642: // fprintf ( stderr , "adjust video counter offset=%d new video=%x\n" , VideoCounterDelayedOffset , pVideoRaster-STRam );
3643: VideoCounterDelayedOffset = 0;
1.1.1.11 root 3644: }
1.1.1.16 root 3645:
3646: if ( pVideoRasterDelayed != NULL )
1.1.1.11 root 3647: {
1.1.1.16 root 3648: pVideoRaster = pVideoRasterDelayed;
3649: // fprintf ( stderr , "adjust video counter const new video=%x\n" , pVideoRaster-STRam );
3650: pVideoRasterDelayed = NULL;
1.1.1.11 root 3651: }
3652:
3653: /* On STE, if we wrote to the hwscroll register, we set the */
3654: /* new value here, once the current line was processed */
3655: if ( NewHWScrollCount >= 0 )
3656: {
3657: HWScrollCount = NewHWScrollCount;
1.1.1.13 root 3658: HWScrollPrefetch = NewHWScrollPrefetch;
1.1.1.11 root 3659: NewHWScrollCount = -1;
1.1.1.13 root 3660: NewHWScrollPrefetch = -1;
3661: }
3662:
3663: /* On STE, if we trigger the left border + 16 pixels trick, we set the */
3664: /* new value here, once the current line was processed */
3665: if ( NewSteBorderFlag >= 0 )
3666: {
3667: if ( NewSteBorderFlag == 0 )
1.1.1.15 root 3668: bSteBorderFlag = false;
1.1.1.13 root 3669: else
1.1.1.15 root 3670: bSteBorderFlag = true;
1.1.1.13 root 3671: NewSteBorderFlag = -1;
1.1.1.11 root 3672: }
3673:
3674: /* On STE, if we wrote to the linewidth register, we set the */
3675: /* new value here, once the current line was processed */
3676: if ( NewLineWidth >= 0 )
3677: {
3678: LineWidth = NewLineWidth;
3679: NewLineWidth = -1;
3680: }
1.1.1.16 root 3681:
3682:
3683: /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
1.1.1.21 root 3684: /* as well as scrolling occurring when removing the left border. */
1.1.1.16 root 3685: /* If >0, shift the line by STF_PixelScroll pixels to the right */
3686: /* If <0, shift the line by -STF_PixelScroll pixels to the left */
3687: /* This should be handled after the STE's hardware scrolling as it will scroll */
3688: /* the whole displayed area (while the STE scrolls pixels inside the displayed area) */
3689: if ( STF_PixelScroll > 0 )
3690: {
3691: Uint16 *pScreenLineEnd;
3692: int count;
3693:
3694: pScreenLineEnd = (Uint16 *) ( pSTScreen + SCREENBYTES_LINE - 2 );
1.1.1.17 root 3695: if ( LineRes == 0 ) /* low res */
3696: {
3697: for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineEnd-- )
3698: do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 4 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
3699: /* Handle the first 16 pixels of the line (add color 0 pixels to the extreme left) */
3700: do_put_mem_word ( pScreenLineEnd-0 , ( do_get_mem_word ( pScreenLineEnd-0 ) >> STF_PixelScroll ) );
3701: do_put_mem_word ( pScreenLineEnd-1 , ( do_get_mem_word ( pScreenLineEnd-1 ) >> STF_PixelScroll ) );
3702: do_put_mem_word ( pScreenLineEnd-2 , ( do_get_mem_word ( pScreenLineEnd-2 ) >> STF_PixelScroll ) );
3703: do_put_mem_word ( pScreenLineEnd-3 , ( do_get_mem_word ( pScreenLineEnd-3 ) >> STF_PixelScroll ) );
3704: }
3705: else /* med res */
3706: {
3707: for ( count = 0 ; count < ( SCREENBYTES_LINE - 4 ) / 2 ; count++ , pScreenLineEnd-- )
3708: do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 2 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
3709: /* Handle the first 16 pixels of the line (add color 0 pixels to the extreme left) */
3710: do_put_mem_word ( pScreenLineEnd-0 , ( do_get_mem_word ( pScreenLineEnd-0 ) >> STF_PixelScroll ) );
3711: do_put_mem_word ( pScreenLineEnd-1 , ( do_get_mem_word ( pScreenLineEnd-1 ) >> STF_PixelScroll ) );
3712: }
1.1.1.16 root 3713: }
3714: else if ( STF_PixelScroll < 0 )
3715: {
3716: Uint16 *pScreenLineStart;
3717: int count;
1.1.1.24 root 3718: int STE_HWScrollLeft;
3719: Uint16 extra_word;
1.1.1.16 root 3720:
3721: STF_PixelScroll = -STF_PixelScroll;
3722: pScreenLineStart = (Uint16 *)pSTScreen;
1.1.1.24 root 3723:
3724: STE_HWScrollLeft = 0;
3725: if ( !bSteBorderFlag && HWScrollCount )
3726: STE_HWScrollLeft = HWScrollCount;
3727:
1.1.1.17 root 3728: if ( LineRes == 0 ) /* low res */
3729: {
3730: for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineStart++ )
1.1.1.24 root 3731: do_put_mem_word ( pScreenLineStart , ( ( do_get_mem_word ( pScreenLineStart ) << STF_PixelScroll )
3732: | ( do_get_mem_word ( pScreenLineStart + 4 ) >> (16-STF_PixelScroll) ) ) );
3733:
3734: /*
3735: * Handle the last 16 pixels of the line after the shift to the left :
3736: * - if this is a 224 byte STE overscan line, then the last 8 pixels to the extreme right should be displayed
3737: * - for other cases (230 byte overscan), "entering" pixels to the extreme right should be set to color 0
3738: */
3739: if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
3740: {
3741: /* This is one can be complicated, because we can have STE scroll to the left + the global */
3742: /* 8 pixel left scroll addded when using a 224 bytes overscan line. We use extra_word to fetch */
3743: /* those missing pixels */
3744: for ( i=0 ; i<4 ; i++ )
3745: {
3746: if ( STE_HWScrollLeft == 0 )
3747: extra_word = do_get_mem_word ( pVideoRasterEndLine + i*2 );
3748: else
3749: extra_word = ( do_get_mem_word ( pVideoRasterEndLine + i*2 ) << STE_HWScrollLeft )
3750: | ( do_get_mem_word ( pVideoRasterEndLine + 8 + i*2 ) >> (16-STE_HWScrollLeft) );
3751:
3752: do_put_mem_word ( pScreenLineStart+i , ( ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll )
3753: | ( extra_word >> (16-STF_PixelScroll) ) ) );
3754: }
3755: }
3756: else
3757: {
3758: for ( i=0 ; i<4 ; i++ )
3759: do_put_mem_word ( pScreenLineStart+i , ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll ) );
3760:
3761: }
1.1.1.17 root 3762: }
3763: else /* med res */
3764: {
3765: for ( count = 0 ; count < ( SCREENBYTES_LINE - 4 ) / 2 ; count++ , pScreenLineStart++ )
1.1.1.24 root 3766: do_put_mem_word ( pScreenLineStart , ( ( do_get_mem_word ( pScreenLineStart ) << STF_PixelScroll )
3767: | ( do_get_mem_word ( pScreenLineStart + 2 ) >> (16-STF_PixelScroll) ) ) );
3768:
3769: /* Handle the last 16 pixels of the line */
3770: if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
3771: {
3772: for ( i=0 ; i<2 ; i++ )
3773: {
3774: if ( STE_HWScrollLeft == 0 )
3775: extra_word = do_get_mem_word ( pVideoRasterEndLine + i*2 );
3776: else
3777: extra_word = ( do_get_mem_word ( pVideoRasterEndLine + i*2 ) << STE_HWScrollLeft )
3778: | ( do_get_mem_word ( pVideoRasterEndLine + 8 + i*2 ) >> (16-STE_HWScrollLeft) );
3779:
3780: do_put_mem_word ( pScreenLineStart+i , ( ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll )
3781: | ( extra_word >> (16-STF_PixelScroll) ) ) );
3782: }
3783: }
3784: else
3785: {
3786: for ( i=0 ; i<2 ; i++ )
3787: do_put_mem_word ( pScreenLineStart+i , ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll ) );
3788: }
3789:
1.1.1.17 root 3790: }
1.1.1.16 root 3791: }
1.1.1.11 root 3792: }
3793:
3794: /* Each screen line copied to buffer is always same length */
3795: pSTScreen += SCREENBYTES_LINE;
1.1.1.22 root 3796:
1.1.1.24 root 3797: /* We must keep the new video address in a 22 or 24 bit space depending on the machine type */
3798: /* (for example in case it pointed to IO space and is now >= 0x1000000) */
3799: pVideoRaster = ( ( pVideoRaster - STRam ) & VideoMask ) + STRam;
3800: //fprintf ( stderr , "video counter new=%x\n" , pVideoRaster-STRam );
1.1 root 3801: }
3802:
1.1.1.2 root 3803:
3804: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3805: /**
3806: * Clear raster line table to store changes in palette/resolution on a line
3807: * basic. Called once on VBL interrupt.
3808: */
1.1 root 3809: void Video_SetScreenRasters(void)
3810: {
1.1.1.11 root 3811: pHBLPaletteMasks = HBLPaletteMasks;
3812: pHBLPalettes = HBLPalettes;
3813: memset(pHBLPaletteMasks, 0, sizeof(Uint32)*NUM_VISIBLE_LINES); /* Clear array */
1.1 root 3814: }
3815:
1.1.1.2 root 3816:
3817: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3818: /**
3819: * Set pointers to HBLPalette tables to store correct colours/resolutions
3820: */
1.1.1.9 root 3821: static void Video_SetHBLPaletteMaskPointers(void)
1.1 root 3822: {
1.1.1.15 root 3823: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.11 root 3824: int Line;
3825:
3826: /* FIXME [NP] We should use Cycles_GetCounterOnWriteAccess, but it wouldn't */
3827: /* work when using multiple accesses instructions like move.l or movem */
1.1.1.15 root 3828: /* To correct this, we assume a delay of 8 cycles (should give a good approximation */
1.1.1.11 root 3829: /* of a move.w or movem.l for example) */
3830: // FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
3831: FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
3832:
3833: /* Find 'line' into palette - screen starts 63 lines down, less 29 for top overscan */
1.1.1.15 root 3834: Video_ConvertPosition ( FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 3835: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
3836:
1.1.1.15 root 3837: Line = HblCounterVideo - nFirstVisibleHbl;
1.1.1.11 root 3838:
3839: /* FIXME [NP] if the color change occurs after the last visible pixel of a line */
3840: /* we consider the palette should be modified on the next line. This is quite */
3841: /* a hack, we should handle all color changes through spec512.c to have cycle */
3842: /* accuracy all the time. */
1.1.1.15 root 3843: if ( LineCycles >= LINE_END_CYCLE_NO_RIGHT )
3844: Line++;
1.1.1.11 root 3845:
3846: if (Line < 0) /* Limit to top/bottom of possible visible screen */
3847: Line = 0;
3848: if (Line >= NUM_VISIBLE_LINES)
3849: Line = NUM_VISIBLE_LINES-1;
3850:
3851: /* Store pointers */
3852: pHBLPaletteMasks = &HBLPaletteMasks[Line]; /* Next mask entry */
3853: pHBLPalettes = &HBLPalettes[16*Line]; /* Next colour raster list x16 colours */
1.1 root 3854: }
1.1.1.8 root 3855:
3856:
1.1.1.9 root 3857: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3858: /**
3859: * Set video shifter timing variables according to screen refresh rate.
3860: * Note: The following equation must be satisfied for correct timings:
3861: *
3862: * nCyclesPerLine * nScanlinesPerFrame * nScreenRefreshRate = 8 MHz
3863: */
1.1.1.10 root 3864: static void Video_ResetShifterTimings(void)
3865: {
1.1.1.11 root 3866: Uint8 nSyncByte;
1.1.1.25! root 3867: int RefreshRate_prev;
1.1.1.10 root 3868:
1.1.1.11 root 3869: nSyncByte = IoMem_ReadByte(0xff820a);
1.1.1.25! root 3870: RefreshRate_prev = nScreenRefreshRate;
1.1.1.10 root 3871:
1.1.1.19 root 3872: if ((IoMem_ReadByte(0xff8260) & 3) == 2)
1.1.1.11 root 3873: {
3874: /* 71 Hz, monochrome */
1.1.1.24 root 3875: nScreenRefreshRate = VIDEO_71HZ;
1.1.1.11 root 3876: nScanlinesPerFrame = SCANLINES_PER_FRAME_71HZ;
3877: nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1.1.1.13 root 3878: nStartHBL = VIDEO_START_HBL_71HZ;
1.1.1.11 root 3879: nFirstVisibleHbl = FIRST_VISIBLE_HBL_71HZ;
1.1.1.13 root 3880: nLastVisibleHbl = FIRST_VISIBLE_HBL_71HZ + VIDEO_HEIGHT_HBL_MONO;
1.1.1.11 root 3881: }
3882: else if (nSyncByte & 2) /* Check if running in 50 Hz or in 60 Hz */
3883: {
3884: /* 50 Hz */
1.1.1.24 root 3885: nScreenRefreshRate = VIDEO_50HZ;
1.1.1.11 root 3886: nScanlinesPerFrame = SCANLINES_PER_FRAME_50HZ;
3887: nCyclesPerLine = CYCLES_PER_LINE_50HZ;
1.1.1.13 root 3888: nStartHBL = VIDEO_START_HBL_50HZ;
1.1.1.11 root 3889: nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ;
1.1.1.13 root 3890: nLastVisibleHbl = FIRST_VISIBLE_HBL_50HZ + NUM_VISIBLE_LINES;
1.1.1.11 root 3891: }
3892: else
3893: {
3894: /* 60 Hz */
1.1.1.24 root 3895: nScreenRefreshRate = VIDEO_60HZ;
1.1.1.11 root 3896: nScanlinesPerFrame = SCANLINES_PER_FRAME_60HZ;
3897: nCyclesPerLine = CYCLES_PER_LINE_60HZ;
1.1.1.13 root 3898: nStartHBL = VIDEO_START_HBL_60HZ;
1.1.1.11 root 3899: nFirstVisibleHbl = FIRST_VISIBLE_HBL_60HZ;
1.1.1.13 root 3900: nLastVisibleHbl = FIRST_VISIBLE_HBL_60HZ + NUM_VISIBLE_LINES;
1.1.1.11 root 3901: }
3902:
1.1.1.24 root 3903: nCyclesPerLine <<= nCpuFreqShift;
3904:
1.1.1.25! root 3905: /* Use VIDEO_HEIGHT_HBL_MONO only when using Mono mode and video resolution = high */
! 3906: /* For other cases (low/med res) in color or Mono mode, we use VIDEO_HEIGHT_HBL_COLOR */
! 3907: /* (fix 'Audio Sculpture' which temporarily switches to low res even when started in Mono mode) */
! 3908: if ( bUseHighRes && ( nScreenRefreshRate == VIDEO_71HZ ) )
1.1.1.11 root 3909: {
1.1.1.13 root 3910: nEndHBL = nStartHBL + VIDEO_HEIGHT_HBL_MONO;
1.1.1.11 root 3911: }
3912: else
3913: {
1.1.1.13 root 3914: nEndHBL = nStartHBL + VIDEO_HEIGHT_HBL_COLOR;
1.1.1.11 root 3915: }
3916:
3917: /* Reset freq changes position for the next VBL to come */
1.1.1.13 root 3918: LastCycleScroll8264 = -1;
3919: LastCycleScroll8265 = -1;
1.1.1.15 root 3920:
3921: TimerBEventCountCycleStart = -1; /* reset timer B activation cycle for this VBL */
3922:
1.1.1.18 root 3923: BlankLines = 0;
1.1.1.25! root 3924:
! 3925: /* Update refresh rate in status bar if necessary */
! 3926: if ( RefreshRate_prev != nScreenRefreshRate )
! 3927: Statusbar_UpdateInfo ();
1.1.1.11 root 3928: }
1.1.1.10 root 3929:
3930:
1.1.1.11 root 3931: /*-----------------------------------------------------------------------*/
3932: /**
1.1.1.15 root 3933: * Clear the array indicating the state of each video line.
1.1.1.11 root 3934: */
1.1.1.15 root 3935: static void Video_InitShifterLines ( void )
1.1.1.11 root 3936: {
1.1.1.15 root 3937: int i;
3938:
3939: for ( i=0 ; i<MAX_SCANLINES_PER_FRAME ; i++ )
3940: {
3941: ShifterFrame.ShifterLines[i].BorderMask = 0;
3942: ShifterFrame.ShifterLines[i].DisplayPixelShift = 0;
3943: ShifterFrame.ShifterLines[i].DisplayStartCycle = -1;
3944: }
3945:
3946: ShifterFrame.ShifterLines[0].StartCycle = 0; /* 1st HBL starts at cycle 0 */
1.1.1.10 root 3947: }
3948:
3949:
3950: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3951: /**
1.1.1.24 root 3952: * Reload VideoBase with the content of ff8201/03.
3953: * On a real ST, this is done 3 HBL before starting a new VBL (HBL 310 at 50 Hz
3954: * and HBL 260 at 60 Hz) and on cycle 48 of this HBL.
3955: * This is where ff8205/ff8207 are reloaded with the content of ff8201/ff8203 on a real ST
3956: * (used in ULM DSOTS demos). VideoBase is also reloaded in Video_ClearOnVBL to be sure
3957: * (when video mode is not low/med res)
1.1.1.11 root 3958: */
1.1.1.24 root 3959: static void Video_RestartVideoCounter(void)
1.1.1.9 root 3960: {
1.1.1.11 root 3961: /* Get screen address pointer, aligned to 256 bytes on ST (ie ignore lowest byte) */
3962: VideoBase = (Uint32)IoMem_ReadByte(0xff8201)<<16 | (Uint32)IoMem_ReadByte(0xff8203)<<8;
1.1.1.24 root 3963: if (!Config_IsMachineST())
1.1.1.11 root 3964: {
1.1.1.20 root 3965: /* on STe 2 aligned, on TT 8 aligned. We do STe. */
1.1.1.11 root 3966: VideoBase |= IoMem_ReadByte(0xff820d) & ~1;
3967: }
3968: pVideoRaster = &STRam[VideoBase];
1.1.1.24 root 3969: }
3970:
3971:
3972: /*-----------------------------------------------------------------------*/
3973: /**
3974: * Called on VBL, set registers ready for frame
3975: */
3976: static void Video_ClearOnVBL(void)
3977: {
3978: /* New screen, so first HBL */
3979: nHBL = 0;
3980: VerticalOverscan = V_OVERSCAN_NONE;
3981:
3982: Video_ResetShifterTimings();
3983:
1.1.1.25! root 3984: if (Config_IsMachineFalcon() && !bUseVDIRes)
! 3985: VIDEL_RestartVideoCounter();
! 3986: else
! 3987: Video_RestartVideoCounter();
1.1.1.24 root 3988:
1.1.1.11 root 3989: pSTScreen = pFrameBuffer->pSTScreen;
3990:
3991: Video_SetScreenRasters();
1.1.1.15 root 3992: Video_InitShifterLines();
1.1.1.11 root 3993: Spec512_StartVBL();
1.1.1.15 root 3994: Video_StartHBL(); /* Init ShifterFrame.ShifterLines[0] */
1.1.1.11 root 3995: }
3996:
3997:
3998: /*-----------------------------------------------------------------------*/
3999: /**
4000: * Get width, height and bpp according to TT-Resolution
4001: */
4002: void Video_GetTTRes(int *width, int *height, int *bpp)
4003: {
4004: switch (TTRes)
4005: {
4006: case ST_LOW_RES: *width = 320; *height = 200; *bpp = 4; break;
4007: case ST_MEDIUM_RES:*width = 640; *height = 200; *bpp = 2; break;
4008: case ST_HIGH_RES: *width = 640; *height = 400; *bpp = 1; break;
4009: case TT_LOW_RES: *width = 320; *height = 480; *bpp = 8; break;
4010: case TT_MEDIUM_RES:*width = 640; *height = 480; *bpp = 4; break;
4011: case TT_HIGH_RES: *width = 1280; *height = 960; *bpp = 1; break;
4012: default:
4013: fprintf(stderr, "TT res error!\n");
4014: *width = 320; *height = 200; *bpp = 4;
4015: break;
4016: }
4017: }
4018:
4019:
4020: /**
1.1.1.24 root 4021: * Set a TT palette color
1.1.1.11 root 4022: */
1.1.1.24 root 4023: static void Video_SetTTPaletteColor(int idx, Uint32 addr)
1.1.1.11 root 4024: {
4025: Uint8 r,g,b, lowbyte, highbyte;
4026:
1.1.1.24 root 4027: lowbyte = IoMem_ReadByte(addr + 1);
1.1.1.11 root 4028:
1.1.1.24 root 4029: if (TTSpecialVideoMode & 0x10) /* TT Hyper-mono mode? */
1.1.1.11 root 4030: {
1.1.1.24 root 4031: r = g = b = lowbyte;
4032: }
4033: else
4034: {
4035: highbyte = IoMem_ReadByte(addr);
4036: r = (highbyte << 4) | (highbyte & 0x0f);
4037: g = (lowbyte & 0xf0) | (lowbyte >> 4);
4038: b = (lowbyte << 4) | (lowbyte & 0x0f);
1.1.1.11 root 4039: }
4040:
1.1.1.24 root 4041: //printf("%d (%x): (%d,%d,%d)\n", idx, addr, r,g,b);
4042: Screen_SetPaletteColor(idx, r,g,b);
4043: }
4044:
4045: /**
4046: * Which 256-color TT palette 16-color "bank" is mapped to ST(e) palette
4047: */
4048: static int TTPaletteSTBank(void)
4049: {
4050: return IoMem_ReadByte(0xff8263) & 0x0f;
4051: }
4052:
4053: /**
4054: * Convert TT palette to SDL palette
4055: */
4056: static void Video_UpdateTTPalette(int bpp)
4057: {
4058: if (TTRes == TT_HIGH_RES || (bUseVDIRes && bpp == 1))
1.1.1.11 root 4059: {
4060: /* Monochrome mode... palette is hardwired (?) */
1.1.1.24 root 4061: Screen_SetPaletteColor(0, 255, 255, 255);
4062: Screen_SetPaletteColor(1, 0, 0, 0);
1.1.1.11 root 4063: }
1.1.1.21 root 4064: else if (bpp == 1)
4065: {
1.1.1.24 root 4066: /* Duochrome mode... palette is taken last two TT colors */
4067: int base = (IoMem_ReadWord(0xff8400) & 0x2) >> 1;
4068: Video_SetTTPaletteColor(base, 0xff85fc);
4069: Video_SetTTPaletteColor(base ^ 1, 0xff85fe);
1.1.1.21 root 4070: }
1.1.1.11 root 4071: else
4072: {
1.1.1.24 root 4073: Uint32 ttpalette = 0xff8400;
4074: int i, colors = 1 << bpp;
4075:
4076: if (colors <= 16)
4077: {
4078: /* use correct ST palette bank */
4079: ttpalette += TTPaletteSTBank() * 16*SIZE_WORD;
4080: }
4081:
1.1.1.11 root 4082: for (i = 0; i < colors; i++)
4083: {
1.1.1.24 root 4084: Video_SetTTPaletteColor(i, ttpalette);
4085: ttpalette += SIZE_WORD;
1.1.1.11 root 4086: }
4087: }
4088:
1.1.1.15 root 4089: bTTColorsSync = true;
1.1.1.11 root 4090: }
4091:
4092:
4093: /*-----------------------------------------------------------------------*/
4094: /**
1.1.1.13 root 4095: * Update TT palette and blit TT screen using VIDEL code.
4096: * @return true if the screen contents changed
1.1.1.11 root 4097: */
1.1.1.16 root 4098: bool Video_RenderTTScreen(void)
1.1.1.11 root 4099: {
4100: static int nPrevTTRes = -1;
4101: int width, height, bpp;
4102:
4103: Video_GetTTRes(&width, &height, &bpp);
4104: if (TTRes != nPrevTTRes)
4105: {
1.1.1.24 root 4106: Screen_SetGenConvSize(width, height, ConfigureParams.Screen.nForceBpp, false);
1.1.1.11 root 4107: nPrevTTRes = TTRes;
4108: if (bpp == 1) /* Assert that mono palette will be used in mono mode */
1.1.1.15 root 4109: bTTColorsSync = false;
1.1.1.11 root 4110: }
4111:
4112: /* colors need synching? */
1.1.1.24 root 4113: if (!bTTColorsSync || TTSpecialVideoMode != nPrevTTSpecialVideoMode)
1.1.1.21 root 4114: {
4115: Video_UpdateTTPalette(bpp);
4116: nPrevTTSpecialVideoMode = TTSpecialVideoMode;
4117: }
1.1.1.11 root 4118:
1.1.1.24 root 4119: return Screen_GenDraw(VideoBase, width, height, bpp, width * bpp / 16,
4120: 0, 0, 0, 0);
1.1.1.11 root 4121: }
4122:
4123:
4124: /*-----------------------------------------------------------------------*/
4125: /**
4126: * Draw screen (either with ST/STE shifter drawing functions or with
4127: * Videl drawing functions)
4128: */
4129: static void Video_DrawScreen(void)
4130: {
4131: /* Skip frame if need to */
1.1.1.13 root 4132: if (nVBLs % (nFrameSkips+1))
1.1.1.11 root 4133: return;
4134:
4135: /* Now draw the screen! */
1.1.1.24 root 4136: if (bUseVDIRes)
4137: {
4138: if (Config_IsMachineTT() && !bTTColorsSync)
4139: {
4140: Video_UpdateTTPalette(VDIPlanes);
4141: }
4142: else if (Config_IsMachineFalcon())
4143: {
4144: VIDEL_UpdateColors();
4145: }
4146: Screen_GenDraw(VideoBase, VDIWidth, VDIHeight, VDIPlanes,
4147: VDIWidth * VDIPlanes / 16, 0, 0, 0, 0);
4148: }
4149: else if (Config_IsMachineFalcon())
1.1.1.11 root 4150: {
1.1.1.17 root 4151: VIDEL_renderScreen();
1.1.1.11 root 4152: }
1.1.1.24 root 4153: else if (Config_IsMachineTT())
1.1.1.11 root 4154: {
1.1.1.17 root 4155: Video_RenderTTScreen();
1.1.1.11 root 4156: }
4157: else
1.1.1.13 root 4158: {
4159: /* Before drawing the screen, ensure all unused lines are cleared to color 0 */
4160: /* (this can happen in 60 Hz when hatari is displaying the screen's border) */
4161: /* pSTScreen was set during Video_CopyScreenLineColor */
1.1.1.24 root 4162: if (nHBL < nLastVisibleHbl)
1.1.1.13 root 4163: memset(pSTScreen, 0, SCREENBYTES_LINE * ( nLastVisibleHbl - nHBL ) );
4164:
1.1.1.17 root 4165: Screen_Draw();
1.1.1.13 root 4166: }
1.1.1.11 root 4167: }
4168:
4169:
4170: /*-----------------------------------------------------------------------*/
4171: /**
1.1.1.15 root 4172: * Start HBL, Timer B and VBL interrupts.
4173: */
4174:
4175:
4176: /**
4177: * Start HBL or Timer B interrupt at position Pos. If position Pos was
4178: * already reached, then the interrupt is set on the next line.
4179: */
4180:
1.1.1.24 root 4181: static void Video_AddInterrupt ( int Line , int Pos , interrupt_id Handler )
1.1.1.15 root 4182: {
4183: int FrameCycles , HblCounterVideo , LineCycles;
1.1.1.24 root 4184: int CyclesToPos;
1.1.1.15 root 4185:
4186: if ( nHBL >= nScanlinesPerFrame )
4187: return; /* don't set a new hbl/timer B if we're on the last line, as the vbl will happen first */
4188:
4189: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 4190: //fprintf ( stderr , "add int pos=%d handler=%d LineCycles=%d nCyclesPerLine=%d\n" , Pos , Handler , LineCycles , nCyclesPerLine );
4191:
4192: Pos <<= nCpuFreqShift; /* convert Pos at 8 MHz into number of cycles at 8/16/32 MHz */
4193:
4194: if ( Line <= nHBL )
4195: CyclesToPos = Pos + ShifterFrame.ShifterLines[Line].StartCycle - FrameCycles;
4196: else /* Pos is on the next line (after the current hbl) */
4197: CyclesToPos = Pos + ShifterFrame.ShifterLines[Line-1].StartCycle - FrameCycles + nCyclesPerLine;
1.1.1.15 root 4198:
1.1.1.24 root 4199: CycInt_AddRelativeInterrupt ( CyclesToPos , INT_CPU_CYCLE, Handler );
4200: //fprintf ( stderr , "add int pos=%d handler=%d LineCycles=%d nCyclesPerLine=%d -> %d cycles\n" , Pos , Handler , LineCycles , nCyclesPerLine , CycleToPos );
1.1.1.15 root 4201: }
4202:
4203:
1.1.1.24 root 4204: static void Video_AddInterruptHBL ( int Line , int Pos )
1.1.1.15 root 4205: {
1.1.1.24 root 4206: //fprintf ( stderr , "add hbl line=%d pos=%d\n" , Line , Pos );
1.1.1.15 root 4207: if ( !bUseVDIRes )
1.1.1.24 root 4208: {
4209: Video_AddInterrupt ( Line , Pos , INTERRUPT_VIDEO_HBL );
4210: }
1.1.1.15 root 4211: }
4212:
4213:
1.1.1.24 root 4214: void Video_AddInterruptTimerB ( int LineVideo , int CycleVideo , int Pos )
1.1.1.15 root 4215: {
1.1.1.24 root 4216: //fprintf ( stderr , "add timerb line=%d cycle=%d pos=%d\n" , LineVideo , CycleVideo , Pos );
1.1.1.15 root 4217: if ( !bUseVDIRes )
1.1.1.24 root 4218: {
4219: /* If new position is not reached yet, next interrupt should be on current line */
4220: /* else it should be on next line */
4221: if ( ( Pos << nCpuFreqShift ) > CycleVideo )
4222: Video_AddInterrupt ( LineVideo , Pos , INTERRUPT_VIDEO_ENDLINE );
4223: else
4224: Video_AddInterrupt ( LineVideo+1 , Pos , INTERRUPT_VIDEO_ENDLINE );
4225: }
1.1.1.15 root 4226: }
4227:
4228:
4229: /**
4230: * Add some video interrupts to handle the first HBL and the first Timer B
4231: * in a new VBL. Also add an interrupt to trigger the next VBL.
4232: * This function is called from the VBL, so we use PendingCycleOver to take into account
4233: * the possible delay occurring when the VBL was executed.
1.1.1.16 root 4234: * In monochrome mode (71 Hz) a line is 224 cycles, which means if VBL is delayed
4235: * by a DIVS, FrameCycles can already be > 224 and we need to add an immediate
4236: * interrupt for hbl/timer in the next 4/8 cycles (else crash might happen as
4237: * line 0 processing would be skipped).
1.1.1.11 root 4238: */
1.1.1.15 root 4239: void Video_StartInterrupts ( int PendingCyclesOver )
1.1.1.11 root 4240: {
1.1.1.16 root 4241: int FrameCycles , HblCounterVideo , LineCycles;
4242: int Pos;
4243:
1.1.1.15 root 4244: /* HBL/Timer B are not emulated in VDI mode */
1.1.1.14 root 4245: if (!bUseVDIRes)
4246: {
1.1.1.24 root 4247: ShifterFrame.ShifterLines[0].StartCycle = 0; /* 1st HBL always starts at cycle 0 */
4248:
1.1.1.16 root 4249: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 4250:
1.1.1.16 root 4251: /* Set Timer B interrupt for line 0 */
4252: Pos = Video_TimerB_GetPos ( 0 );
1.1.1.24 root 4253: if ( ( Pos << nCpuFreqShift ) > FrameCycles ) /* check Pos for line 0 was not already reached */
4254: Video_AddInterruptTimerB ( HblCounterVideo , LineCycles , Pos );
1.1.1.16 root 4255: else /* the VBL was delayed by more than 1 HBL, add an immediate timer B */
4256: {
4257: LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d delayed too much video_cyc=%d >= pos=%d for first timer B, add immediate timer B\n" ,
4258: nVBLs , FrameCycles , Pos );
4259: CycInt_AddRelativeInterrupt ( 4 , INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
4260: }
4261:
4262: /* Set HBL interrupt for line 0 */
1.1.1.24 root 4263: Pos = Video_HBL_GetDefaultPos();
4264: ShifterFrame.HBL_CyclePos = Pos;
4265: if ( ( Pos << nCpuFreqShift ) > FrameCycles ) /* check Pos for line 0 was not already reached */
4266: Video_AddInterruptHBL ( HblCounterVideo , Pos );
1.1.1.16 root 4267: else /* the VBL was delayed by more than 1 HBL, add an immediate HBL */
4268: {
4269: LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d delayed too much video_cyc=%d >= pos=%d for first HBL, add immediate HBL\n" ,
4270: nVBLs , FrameCycles , Pos );
4271: CycInt_AddRelativeInterrupt ( 8 , INT_CPU_CYCLE, INTERRUPT_VIDEO_HBL ); /* use 8 instead of 4 to happen after immediate timer b */
4272: }
1.1.1.14 root 4273: }
1.1.1.15 root 4274:
1.1.1.24 root 4275: /* When using VDI, we setup the next VBL here ; else it will be setup at the start of the last HBL */
4276: else
4277: {
4278: /* Add new VBL interrupt */
4279: CyclesPerVBL = nScanlinesPerFrame * nCyclesPerLine; /* TODO [NP] use cpufreq / 50 instead ? */
4280: CycInt_AddRelativeInterrupt( CyclesPerVBL - PendingCyclesOver , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL);
4281: }
1.1.1.11 root 4282: }
4283:
4284:
4285: /*-----------------------------------------------------------------------*/
4286: /**
1.1.1.15 root 4287: * VBL interrupt : set new interrupts, draw screen, generate sound,
4288: * reset counters, ...
1.1.1.11 root 4289: */
1.1.1.15 root 4290: void Video_InterruptHandler_VBL ( void )
1.1.1.11 root 4291: {
4292: int PendingCyclesOver;
1.1.1.24 root 4293: int PendingInterruptCount_save;
4294: static Uint64 VBL_ClockCounter;
4295:
4296: PendingInterruptCount_save = PendingInterruptCount;
4297:
4298: /* Act on shortcut keys */
4299: /* NOTE [NP] : ShortCut_ActKey should be called as soon as possible in the VBL handler, */
4300: /* before acknowledging it. This way, we will get a call of the VBL handler when restoring */
4301: /* a memory snapshot that was created using shortcut keys. Else, a VBL will be lost when restoring */
4302: /* the snapshot if we use '--memstate' option, which can break some programs during several VBL */
4303: /* after the restore. */
4304: /* TODO : a better way would be to handle shortcut keys inside the main CPU loop in newcpu.c */
4305: /* to always get a consistent state (doing it from an interrupt handler is not correct). */
4306: /* (eg, fix restoring screen 1 in the B.I.G. Demo, where the raster bar can be wrongly displayed) */
4307: ShortCut_ActKey();
4308:
4309: /* In case we press a shortcut for reset, PendingInterruptCount will be changed to >0 and we will get */
4310: /* some warnings "bug nHBL=...". To avoid this we restore the value (<= 0) saved at the start of the VBL */
4311: if ( PendingInterruptCount > 0 )
4312: PendingInterruptCount = PendingInterruptCount_save;
1.1.1.9 root 4313:
1.1.1.21 root 4314: /* Store cycles we went over for this frame(this is our initial count) */
1.1.1.11 root 4315: PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE ); /* +ve */
1.1.1.10 root 4316:
1.1.1.11 root 4317: /* Remove this interrupt from list and re-order */
1.1.1.24 root 4318: /* NOTE [NP] : in case ShortCut_ActKey above was used to change some parameters that require */
4319: /* a reset, then the current int won't be the VBL handler anymore. In that case we should not */
4320: /* acknowledge else this will ack another int and this will break Falcon DMA sound for example */
4321: /* TODO : see above to handle shortcut keys in the main CPU loop */
4322: if ( CycInt_GetActiveInt() == INTERRUPT_VIDEO_VBL )
4323: CycInt_AcknowledgeInterrupt();
1.1.1.9 root 4324:
1.1.1.14 root 4325: /* Increment the vbl jitter index */
4326: VblJitterIndex++;
1.1.1.20 root 4327: VblJitterIndex %= VBL_JITTER_ARRAY_SIZE;
1.1.1.14 root 4328:
1.1.1.11 root 4329: /* Set frame cycles, used for Video Address */
1.1.1.24 root 4330: Cycles_SetCounter(CYCLES_COUNTER_VIDEO, PendingCyclesOver + ( pVideoTiming->VblVideoCycleOffset << nCpuFreqShift ) );
1.1.1.9 root 4331:
1.1.1.11 root 4332: /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
4333: Keymap_DebounceAllKeys();
1.1.1.9 root 4334:
1.1.1.11 root 4335: Video_DrawScreen();
1.1.1.9 root 4336:
1.1.1.11 root 4337: /* Check printer status */
4338: Printer_CheckIdleStatus();
4339:
4340: /* Update counter for number of screen refreshes per second */
4341: nVBLs++;
4342: /* Set video registers for frame */
4343: Video_ClearOnVBL();
1.1.1.17 root 4344:
1.1.1.15 root 4345: /* Since we don't execute HBL functions in VDI mode, we've got to
4346: * initialize the first HBL palette here when VDI mode is enabled. */
4347: if (bUseVDIRes)
4348: Video_StoreFirstLinePalette();
4349:
4350: /* Start VBL, HBL and Timer B interrupts (this must be done after resetting
4351: * video cycle counter setting default freq values in Video_ClearOnVBL) */
4352: Video_StartInterrupts(PendingCyclesOver);
4353:
1.1.1.21 root 4354: /* Update the IKBD's internal clock */
4355: IKBD_UpdateClockOnVBL ();
4356:
1.1.1.16 root 4357: /* Record video frame is necessary */
4358: if ( bRecordingAvi )
4359: Avi_RecordVideoStream ();
4360:
1.1.1.11 root 4361: /* Store off PSG registers for YM file, is enabled */
4362: YMFormat_UpdateRecording();
4363: /* Generate 1/50th second of sound sample data, to be played by sound thread */
4364: Sound_Update_VBL();
4365:
1.1.1.25! root 4366: /* Update the blitter's stats for the previous VBL */
! 4367: Blitter_StatsUpdateRate ( (int)( CyclesGlobalClockCounter - PendingCyclesOver - VBL_ClockCounter ) );
! 4368:
1.1.1.24 root 4369: LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d video_cyc=%d pending_cyc=%d jitter=%d vbl_cycles=%d\n" ,
4370: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] ,
4371: (int)( CyclesGlobalClockCounter - PendingCyclesOver - VBL_ClockCounter ) );
4372:
4373: VBL_ClockCounter = CyclesGlobalClockCounter - PendingCyclesOver;
1.1.1.11 root 4374:
1.1.1.21 root 4375: /* Print traces if pending VBL bit changed just before IACK when VBL interrupt is allowed */
4376: if ( ( CPU_IACK == true ) && ( regs.intmask < 4 ) )
4377: {
4378: if ( pendingInterrupts & ( 1 << 4 ) )
4379: {
4380: LOG_TRACE ( TRACE_VIDEO_VBL , "VBL %d, pending set again just before iack, skip one VBL interrupt video_cyc=%d pending_cyc=%d jitter=%d\n" ,
4381: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
4382: }
4383: else
4384: {
4385: LOG_TRACE ( TRACE_VIDEO_VBL , "VBL %d, new pending VBL set just before iack video_cyc=%d pending_cyc=%d jitter=%d\n" ,
4386: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
4387: }
4388: }
4389:
4390: /* Set pending bit for VBL interrupt in the CPU IPL */
1.1.1.23 root 4391: M68000_Exception(EXCEPTION_NR_VBLANK, M68000_EXC_SRC_AUTOVEC); /* Vertical blank interrupt, level 4 */
1.1.1.11 root 4392:
4393: Main_WaitOnVbl();
1.1.1.9 root 4394: }
4395:
4396:
4397: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4398: /**
1.1.1.13 root 4399: * Write to video address base high, med and low register (0xff8201/03/0d).
1.1.1.24 root 4400: * On STE/TT, when a program writes to high or med registers, base low register
1.1.1.13 root 4401: * is reset to zero.
1.1.1.11 root 4402: */
1.1.1.24 root 4403: void Video_ScreenBase_WriteByte(void)
1.1.1.9 root 4404: {
1.1.1.24 root 4405: /* On STF/STE machines with <= 4MB of RAM, video addresses are limited to $3fffff */
4406: if ( IoAccessCurrentAddress == 0xff8201 )
4407: IoMem[ 0xff8201 ] &= DMA_MaskAddressHigh();
4408:
4409: /* On STE/TT, reset screen base low register */
4410: if (!Config_IsMachineST() &&
4411: (IoAccessCurrentAddress == 0xff8201 || IoAccessCurrentAddress == 0xff8203))
4412: IoMem[0xff820d] = 0;
1.1.1.8 root 4413:
1.1.1.15 root 4414: if (LOG_TRACE_LEVEL(TRACE_VIDEO_STE))
1.1.1.11 root 4415: {
1.1.1.15 root 4416: int FrameCycles, HblCounterVideo, LineCycles;
4417:
4418: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 4419: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.15 root 4420:
4421: LOG_TRACE_PRINT ( "write ste video base=0x%x video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
1.1.1.13 root 4422: (IoMem[0xff8201]<<16)+(IoMem[0xff8203]<<8)+IoMem[0xff820d] ,
1.1.1.15 root 4423: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 4424: }
1.1.1.8 root 4425: }
4426:
4427: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4428: /**
4429: * Read video address counter and update ff8205/07/09
4430: */
4431: void Video_ScreenCounter_ReadByte(void)
1.1.1.8 root 4432: {
1.1.1.11 root 4433: Uint32 addr;
1.1.1.8 root 4434:
1.1.1.11 root 4435: addr = Video_CalculateAddress(); /* get current video address */
1.1.1.24 root 4436:
4437: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
4438: /* that occurred while the display was already ON */
4439: if ( VideoCounterDelayedOffset != 0 )
4440: {
4441: addr += ( VideoCounterDelayedOffset & ~1 );
4442: // fprintf ( stderr , "adjust video counter offset=%d new video=%x\n" , VideoCounterDelayedOffset , addr );
4443: }
4444:
1.1.1.11 root 4445: IoMem[0xff8205] = ( addr >> 16 ) & 0xff;
4446: IoMem[0xff8207] = ( addr >> 8 ) & 0xff;
4447: IoMem[0xff8209] = addr & 0xff;
1.1.1.8 root 4448: }
4449:
1.1.1.9 root 4450: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4451: /**
4452: * Write to video address counter (0xff8205, 0xff8207 and 0xff8209).
1.1.1.24 root 4453: * Called on STE/TT only and like with base address, you cannot set lowest bit.
1.1.1.16 root 4454: *
4455: * As Hatari processes/converts one complete video line at a time, we have 3 cases :
4456: * - If display has not started yet for this line (left border), we can change pVideoRaster now.
4457: * We must take into account that the MMU starts 16 cycles earlier when hscroll is used.
4458: * - If display has stopped for this line (right border), we will change pVideoRaster
4459: * in Video_CopyScreenLineColor using pVideoRasterDelayed once the line has been processed.
4460: * - If the write is made while display is on, then we must compute an offset of what
4461: * the new address should have been, to correctly emulate the video address at the
4462: * end of the line while taking into account the fact that the video pointer is incrementing
4463: * during the active part of the line (this is the most "tricky" case)
4464: *
4465: * To compute the new address, we must change only the byte that was modified and keep the two others ones.
1.1.1.11 root 4466: */
1.1.1.9 root 4467: void Video_ScreenCounter_WriteByte(void)
4468: {
1.1.1.11 root 4469: Uint8 AddrByte;
1.1.1.16 root 4470: Uint32 addr_cur;
4471: Uint32 addr_new = 0;
1.1.1.15 root 4472: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.13 root 4473: int Delayed;
1.1.1.16 root 4474: int MMUStartCycle;
1.1.1.11 root 4475:
1.1.1.15 root 4476: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 4477: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
4478:
4479: /* On STF/STE machines with <= 4MB of RAM, video addresses are limited to $3fffff */
4480: if ( IoAccessCurrentAddress == 0xff8205 )
4481: IoMem[ 0xff8205 ] &= DMA_MaskAddressHigh();
1.1.1.11 root 4482:
4483: AddrByte = IoMem[ IoAccessCurrentAddress ];
4484:
1.1.1.16 root 4485: /* Get current video address from the shifter */
4486: addr_cur = Video_CalculateAddress();
4487: /* Correct the address in case a modification of ff8205/07/09 was already delayed */
4488: addr_new = addr_cur + VideoCounterDelayedOffset;
4489: /* Correct the address in case video counter was already modified in the right border */
4490: if ( pVideoRasterDelayed != NULL )
4491: addr_new = pVideoRasterDelayed - STRam;
4492:
4493: /* addr_new should now be the same as on a real STE */
4494: /* Compute the new video address with one modified byte */
4495: if ( IoAccessCurrentAddress == 0xff8205 )
1.1.1.24 root 4496: addr_new = ( addr_new & 0x00ffff ) | ( AddrByte << 16 );
1.1.1.16 root 4497: else if ( IoAccessCurrentAddress == 0xff8207 )
4498: addr_new = ( addr_new & 0xff00ff ) | ( AddrByte << 8 );
4499: else if ( IoAccessCurrentAddress == 0xff8209 )
4500: addr_new = ( addr_new & 0xffff00 ) | ( AddrByte );
4501:
1.1.1.23 root 4502: addr_new &= ~1; /* clear bit 0 */
4503:
1.1.1.16 root 4504: MMUStartCycle = Video_GetMMUStartCycle ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle );
4505:
1.1.1.11 root 4506: /* If display has not started, we can still modify pVideoRaster */
1.1.1.16 root 4507: /* We must also check the write does not overlap the end of the line (to be sure Video_EndHBL is called first) */
4508: if ( ( ( LineCycles <= MMUStartCycle ) && ( nHBL == HblCounterVideo ) )
1.1.1.18 root 4509: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.11 root 4510: {
1.1.1.23 root 4511: pVideoRaster = &STRam[addr_new]; /* set new video address */
1.1.1.16 root 4512: VideoCounterDelayedOffset = 0;
4513: pVideoRasterDelayed = NULL;
1.1.1.15 root 4514: Delayed = false;
1.1.1.11 root 4515: }
4516:
1.1.1.16 root 4517: /* Display is OFF (right border) but we can't change pVideoRaster now, we must process Video_CopyScreenLineColor first */
1.1.1.18 root 4518: else if ( ( nHBL >= nStartHBL ) && ( nHBL < nEndHBL + BlankLines ) /* line should be active */
1.1.1.16 root 4519: && ( ( LineCycles > ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle ) /* we're in the right border */
4520: || ( HblCounterVideo == nHBL+1 ) ) ) /* or the write overlaps the next line and Video_EndHBL was not called yet */
4521: {
4522: VideoCounterDelayedOffset = 0;
1.1.1.23 root 4523: pVideoRasterDelayed = &STRam[addr_new]; /* new value for pVideoRaster at the end of Video_CopyScreenLineColor */
1.1.1.16 root 4524: Delayed = true;
4525: }
4526:
4527: /* Counter is modified while display is ON, store the bytes offset for Video_CopyScreenLineColor */
4528: /* Even on a real STE, modifying video address in this case will cause artefacts */
1.1.1.11 root 4529: else
4530: {
1.1.1.16 root 4531: VideoCounterDelayedOffset = addr_new - addr_cur;
4532: pVideoRasterDelayed = NULL;
1.1.1.15 root 4533: Delayed = true;
1.1.1.23 root 4534:
1.1.1.24 root 4535: /* [FIXME] 'E605' Earth part by Light : write to FF8209 on STE while display is on, */
4536: /* in that case video counter is not correct */
4537: if ( STMemory_ReadLong ( M68000_InstrPC ) == 0x01c9ffc3 ) /* movep.l d0,-$3d(a1) */
4538: VideoCounterDelayedOffset += 6; /* or -2 ? */
4539:
4540: /* [FIXME] 'Tekila' part in Delirious Demo IV : write to FF8209 on STE while display is on, */
4541: /* in that case video counter is not correct */
4542: else if ( ( STMemory_ReadLong ( M68000_InstrPC ) == 0x11c48209 ) /* move.b d4,$ff8209.w */
4543: && ( STMemory_ReadLong ( M68000_InstrPC-4 ) == 0x11c28207 ) /* move.b d2,$ff8207.w */
4544: && ( STMemory_ReadLong ( M68000_InstrPC-8 ) == 0x82054842 ) )
4545: {
4546: VideoCounterDelayedOffset += 2;
4547: if ( VideoCounterDelayedOffset == 256 ) /* write sometimes happens at the same time */
4548: VideoCounterDelayedOffset = 0; /* ff8207 increases */
4549: /* partial fix, some errors remain for other cases where write happens at the same time ff8207 increases ... */
4550: }
4551:
1.1.1.11 root 4552: }
4553:
1.1.1.16 root 4554: LOG_TRACE(TRACE_VIDEO_STE , "write ste video %x val=0x%x video_old=%x video_new=%x offset=%x delayed=%s"
4555: " video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
4556: IoAccessCurrentAddress, AddrByte, addr_cur , addr_new , VideoCounterDelayedOffset , Delayed ? "yes" : "no" ,
1.1.1.15 root 4557: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.9 root 4558: }
1.1.1.8 root 4559:
4560: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4561: /**
4562: * Read video sync register (0xff820a)
4563: */
1.1.1.8 root 4564: void Video_Sync_ReadByte(void)
4565: {
1.1.1.24 root 4566: if (Config_IsMachineST() || Config_IsMachineSTE())
1.1.1.14 root 4567: IoMem[0xff820a] |= 0xfc; /* set unused bits 2-7 to 1 */
1.1.1.8 root 4568: }
4569:
4570: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4571: /**
4572: * Read video base address low byte (0xff820d). A plain ST can only store
4573: * screen addresses rounded to 256 bytes (i.e. no lower byte).
4574: */
1.1.1.8 root 4575: void Video_BaseLow_ReadByte(void)
4576: {
1.1.1.24 root 4577: if (Config_IsMachineST())
1.1.1.11 root 4578: IoMem[0xff820d] = 0; /* On ST this is always 0 */
1.1.1.9 root 4579:
1.1.1.11 root 4580: /* Note that you should not do anything here for STe because
4581: * VideoBase address is set in an interrupt and would be wrong
4582: * here. It's fine like this.
4583: */
1.1.1.8 root 4584: }
4585:
4586: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4587: /**
4588: * Read video line width register (0xff820f)
4589: */
1.1.1.8 root 4590: void Video_LineWidth_ReadByte(void)
4591: {
1.1.1.24 root 4592: if (Config_IsMachineST())
1.1.1.11 root 4593: IoMem[0xff820f] = 0; /* On ST this is always 0 */
1.1.1.18 root 4594:
4595: /* If we're not in STF mode, we use the value already stored in $ff820f */
1.1.1.8 root 4596: }
4597:
4598: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4599: /**
1.1.1.24 root 4600: * Read video resolution register (0xff8260)
4601: * NOTE : resolution register is stored in both the GLUE and the SHIFTER
4602: * As the value is read from the SHIFTER, we need to round memory access to 4 cycle
1.1.1.11 root 4603: */
1.1.1.24 root 4604: void Video_Res_ReadByte(void)
1.1.1.8 root 4605: {
1.1.1.24 root 4606: /* Access to shifter regs are on a 4 cycle boundary */
4607: M68000_SyncCpuBus_OnReadAccess();
4608:
1.1.1.11 root 4609: if (bUseHighRes)
1.1.1.14 root 4610: IoMem[0xff8260] = 2; /* If mono monitor, force to high resolution */
4611:
1.1.1.24 root 4612: if (Config_IsMachineST())
1.1.1.17 root 4613: IoMem[0xff8260] |= 0xfc; /* On STF, set unused bits 2-7 to 1 */
1.1.1.24 root 4614: else if (Config_IsMachineTT())
4615: IoMem[0xff8260] &= 0x07; /* Only use bits 0, 1 and 2 */
1.1.1.20 root 4616: else
4617: IoMem[0xff8260] &= 0x03; /* Only use bits 0 and 1, unused bits 2-7 are set to 0 */
1.1.1.8 root 4618: }
4619:
1.1.1.10 root 4620: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4621: /**
4622: * Read horizontal scroll register (0xff8265)
4623: */
1.1.1.10 root 4624: void Video_HorScroll_Read(void)
4625: {
1.1.1.11 root 4626: IoMem[0xff8265] = HWScrollCount;
1.1.1.10 root 4627: }
1.1.1.8 root 4628:
4629: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4630: /**
4631: * Write video line width register (0xff820f) - STE only.
4632: * Content of LineWidth is added to the shifter counter when display is
4633: * turned off (start of the right border, usually at cycle 376)
4634: */
1.1.1.10 root 4635: void Video_LineWidth_WriteByte(void)
4636: {
1.1.1.11 root 4637: Uint8 NewWidth;
1.1.1.15 root 4638: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.13 root 4639: int Delayed;
1.1.1.11 root 4640:
1.1.1.15 root 4641: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 4642: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.11 root 4643:
4644: NewWidth = IoMem_ReadByte(0xff820f);
4645:
4646: /* We must also check the write does not overlap the end of the line */
1.1.1.15 root 4647: if ( ( ( nHBL == HblCounterVideo ) && ( LineCycles <= ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) )
1.1.1.18 root 4648: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.13 root 4649: {
1.1.1.11 root 4650: LineWidth = NewWidth; /* display is on, we can still change */
1.1.1.13 root 4651: NewLineWidth = -1; /* cancel 'pending' change */
1.1.1.15 root 4652: Delayed = false;
1.1.1.13 root 4653: }
1.1.1.11 root 4654: else
1.1.1.13 root 4655: {
1.1.1.11 root 4656: NewLineWidth = NewWidth; /* display is off, can't change LineWidth once in right border */
1.1.1.15 root 4657: Delayed = true;
1.1.1.13 root 4658: }
1.1.1.11 root 4659:
1.1.1.15 root 4660: LOG_TRACE(TRACE_VIDEO_STE , "write ste linewidth=0x%x delayed=%s video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n",
1.1.1.13 root 4661: NewWidth, Delayed ? "yes" : "no" ,
1.1.1.15 root 4662: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.10 root 4663: }
4664:
4665: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4666: /**
4667: * Write to video shifter palette registers (0xff8240-0xff825e)
1.1.1.14 root 4668: *
4669: * Note that there's a special "strange" case when writing only to the upper byte
4670: * of the color reg (instead of writing 16 bits at once with .W/.L).
4671: * In that case, the byte written to address x is automatically written
4672: * to address x+1 too (but we shouldn't copy x in x+1 after masking x ; we apply the mask at the end)
1.1.1.22 root 4673: * Similarly, when writing a byte to address x+1, it's also written to address x
1.1.1.14 root 4674: * So : move.w #0,$ff8240 -> color 0 is now $000
4675: * move.b #7,$ff8240 -> color 0 is now $707 !
1.1.1.22 root 4676: * move.b #$55,$ff8241 -> color 0 is now $555 !
1.1.1.14 root 4677: * move.b #$71,$ff8240 -> color 0 is now $171 (bytes are first copied, then masked)
1.1.1.11 root 4678: */
1.1.1.23 root 4679: static void Video_ColorReg_WriteWord(void)
1.1.1.8 root 4680: {
1.1.1.24 root 4681: Uint32 addr;
4682: Uint16 col;
4683: int idx;
1.1.1.22 root 4684:
1.1.1.24 root 4685: addr = IoAccessCurrentAddress;
1.1.1.14 root 4686:
1.1.1.24 root 4687: /* Access to shifter regs are on a 4 cycle boundary */
4688: M68000_SyncCpuBus_OnWriteAccess();
1.1.1.14 root 4689:
1.1.1.24 root 4690: /* Handle special case when writing only to the upper byte of the color reg */
4691: if (nIoMemAccessSize == SIZE_BYTE && (IoAccessCurrentAddress & 1) == 0)
4692: col = (IoMem_ReadByte(addr) << 8) + IoMem_ReadByte(addr); /* copy upper byte into lower byte */
4693: /* Same when writing only to the lower byte of the color reg */
4694: else if (nIoMemAccessSize == SIZE_BYTE && (IoAccessCurrentAddress & 1) == 1)
4695: col = (IoMem_ReadByte(addr) << 8) + IoMem_ReadByte(addr); /* copy lower byte into upper byte */
4696: /* Usual case, writing a word or a long (2 words) */
4697: else
4698: col = IoMem_ReadWord(addr);
4699:
4700: if (Config_IsMachineST())
4701: col &= 0x777; /* Mask off to ST 512 palette */
4702: else
4703: col &= 0xfff; /* Mask off to STe 4096 palette */
4704:
4705: addr &= 0xfffffffe; /* Ensure addr is even to store the 16 bit color */
4706: IoMem_WriteWord(addr, col); /* (some games write 0xFFFF and read back to see if STe) */
4707:
4708: idx = (addr - 0xff8240) / 2; /* words */
4709:
4710: if (bUseHighRes || (bUseVDIRes && VDIPlanes == 1))
4711: {
4712: if (idx == 0)
4713: {
4714: Screen_SetPaletteColor(col & 1, 0, 0, 0);
4715: Screen_SetPaletteColor(!(col & 1), 255, 255, 255);
4716: }
4717: }
4718: else if (bUseVDIRes)
4719: {
4720: int r, g, b;
4721: r = (col >> 8) & 0x0f;
4722: r = ((r & 7) << 1) | (r >> 3);
4723: r |= r << 4;
4724: g = (col >> 4) & 0x0f;
4725: g = ((g & 7) << 1) | (g >> 3);
4726: g |= g << 4;
4727: b = col & 0x0f;
4728: b = ((b & 7) << 1) | (b >> 3);
4729: b |= b << 4;
4730: Screen_SetPaletteColor(idx, r, g, b);
4731: }
4732: else /* Don't store if hi-res or VDI resolution */
4733: {
4734: Video_SetHBLPaletteMaskPointers(); /* Set 'pHBLPalettes' etc.. according cycles into frame */
1.1.1.22 root 4735:
1.1.1.11 root 4736: Spec512_StoreCyclePalette(col, addr); /* Store colour into CyclePalettes[] */
4737: pHBLPalettes[idx] = col; /* Set colour x */
4738: *pHBLPaletteMasks |= 1 << idx; /* And mask */
4739:
1.1.1.15 root 4740: if (LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
1.1.1.11 root 4741: {
1.1.1.15 root 4742: int FrameCycles, HblCounterVideo, LineCycles;
4743:
4744: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 4745: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.15 root 4746:
4747: LOG_TRACE_PRINT ( "write col addr=%x col=%x video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
1.1.1.22 root 4748: IoAccessCurrentAddress, col,
1.1.1.15 root 4749: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 4750: }
4751:
4752: }
1.1.1.8 root 4753: }
4754:
1.1.1.22 root 4755: /*
4756: * Read from video shifter palette registers (0xff8240-0xff825e)
4757: *
4758: * NOTE [NP] : On STF, only 3 bits are used for RGB (instead of 4 on STE) ;
4759: * the content of bits 3, 7 and 11 is not defined and will be 0 or 1
4760: * depending on the latest activity on the BUS (last word access by the CPU or
4761: * the shifter). As precisely emulating these bits is quite complicated,
4762: * we use random values for now.
4763: * NOTE [NP] : When executing code from the IO addresses between 0xff8240-0xff825e
4764: * the unused bits on STF are set to '0' (used in "The Union Demo" protection).
4765: * So we use rand() only if PC is located in RAM.
4766: */
1.1.1.23 root 4767: static void Video_ColorReg_ReadWord(void)
1.1.1.22 root 4768: {
4769: Uint16 col;
4770: Uint32 addr;
4771: addr = IoAccessCurrentAddress;
4772:
1.1.1.24 root 4773: /* Access to shifter regs are on a 4 cycle boundary */
4774: M68000_SyncCpuBus_OnReadAccess();
4775:
1.1.1.22 root 4776: col = IoMem_ReadWord(addr);
4777:
1.1.1.24 root 4778: if (Config_IsMachineST() && M68000_GetPC() < 0x400000) /* PC in RAM < 4MB */
1.1.1.22 root 4779: {
4780: col = ( col & 0x777 ) | ( rand() & 0x888 );
4781: IoMem_WriteWord ( addr , col );
4782: }
4783:
4784: if (LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
4785: {
4786: int FrameCycles, HblCounterVideo, LineCycles;
4787:
4788: Video_GetPosition_OnReadAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 4789: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.22 root 4790:
4791: LOG_TRACE_PRINT ( "read col addr=%x col=%x video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
4792: IoAccessCurrentAddress, col,
4793: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
4794: }
4795: }
4796:
4797: /*
4798: * [NP] TODO : due to how .L accesses are handled in ioMem.c, we can't call directly
4799: * Video_ColorReg_WriteWord from ioMemTabST.c / ioMemTabSTE.c, we must use an intermediate
4800: * function, else .L accesses will not change 2 .W color regs, but only one.
4801: * This should be changed in ioMem.c to do 2 separate .W accesses, as would do a real 68000
4802: */
4803:
1.1.1.8 root 4804: void Video_Color0_WriteWord(void)
4805: {
1.1.1.22 root 4806: Video_ColorReg_WriteWord();
1.1.1.8 root 4807: }
4808:
4809: void Video_Color1_WriteWord(void)
4810: {
1.1.1.22 root 4811: Video_ColorReg_WriteWord();
1.1.1.8 root 4812: }
4813:
4814: void Video_Color2_WriteWord(void)
4815: {
1.1.1.22 root 4816: Video_ColorReg_WriteWord();
1.1.1.8 root 4817: }
4818:
4819: void Video_Color3_WriteWord(void)
4820: {
1.1.1.22 root 4821: Video_ColorReg_WriteWord();
1.1.1.8 root 4822: }
4823:
4824: void Video_Color4_WriteWord(void)
4825: {
1.1.1.22 root 4826: Video_ColorReg_WriteWord();
1.1.1.8 root 4827: }
4828:
4829: void Video_Color5_WriteWord(void)
4830: {
1.1.1.22 root 4831: Video_ColorReg_WriteWord();
1.1.1.8 root 4832: }
4833:
4834: void Video_Color6_WriteWord(void)
4835: {
1.1.1.22 root 4836: Video_ColorReg_WriteWord();
1.1.1.8 root 4837: }
4838:
4839: void Video_Color7_WriteWord(void)
4840: {
1.1.1.22 root 4841: Video_ColorReg_WriteWord();
1.1.1.8 root 4842: }
4843:
4844: void Video_Color8_WriteWord(void)
4845: {
1.1.1.22 root 4846: Video_ColorReg_WriteWord();
1.1.1.8 root 4847: }
4848:
4849: void Video_Color9_WriteWord(void)
4850: {
1.1.1.22 root 4851: Video_ColorReg_WriteWord();
1.1.1.8 root 4852: }
4853:
4854: void Video_Color10_WriteWord(void)
4855: {
1.1.1.22 root 4856: Video_ColorReg_WriteWord();
1.1.1.8 root 4857: }
4858:
4859: void Video_Color11_WriteWord(void)
4860: {
1.1.1.22 root 4861: Video_ColorReg_WriteWord();
1.1.1.8 root 4862: }
4863:
4864: void Video_Color12_WriteWord(void)
4865: {
1.1.1.22 root 4866: Video_ColorReg_WriteWord();
1.1.1.8 root 4867: }
4868:
4869: void Video_Color13_WriteWord(void)
4870: {
1.1.1.22 root 4871: Video_ColorReg_WriteWord();
1.1.1.8 root 4872: }
4873:
4874: void Video_Color14_WriteWord(void)
4875: {
1.1.1.22 root 4876: Video_ColorReg_WriteWord();
1.1.1.8 root 4877: }
4878:
4879: void Video_Color15_WriteWord(void)
4880: {
1.1.1.22 root 4881: Video_ColorReg_WriteWord();
4882: }
4883:
4884:
4885: void Video_Color0_ReadWord(void)
4886: {
4887: Video_ColorReg_ReadWord();
4888: }
4889:
4890: void Video_Color1_ReadWord(void)
4891: {
4892: Video_ColorReg_ReadWord();
4893: }
4894:
4895: void Video_Color2_ReadWord(void)
4896: {
4897: Video_ColorReg_ReadWord();
4898: }
4899:
4900: void Video_Color3_ReadWord(void)
4901: {
4902: Video_ColorReg_ReadWord();
4903: }
4904:
4905: void Video_Color4_ReadWord(void)
4906: {
4907: Video_ColorReg_ReadWord();
4908: }
4909:
4910: void Video_Color5_ReadWord(void)
4911: {
4912: Video_ColorReg_ReadWord();
4913: }
4914:
4915: void Video_Color6_ReadWord(void)
4916: {
4917: Video_ColorReg_ReadWord();
4918: }
4919:
4920: void Video_Color7_ReadWord(void)
4921: {
4922: Video_ColorReg_ReadWord();
4923: }
4924:
4925: void Video_Color8_ReadWord(void)
4926: {
4927: Video_ColorReg_ReadWord();
4928: }
4929:
4930: void Video_Color9_ReadWord(void)
4931: {
4932: Video_ColorReg_ReadWord();
4933: }
4934:
4935: void Video_Color10_ReadWord(void)
4936: {
4937: Video_ColorReg_ReadWord();
4938: }
4939:
4940: void Video_Color11_ReadWord(void)
4941: {
4942: Video_ColorReg_ReadWord();
4943: }
4944:
4945: void Video_Color12_ReadWord(void)
4946: {
4947: Video_ColorReg_ReadWord();
4948: }
4949:
4950: void Video_Color13_ReadWord(void)
4951: {
4952: Video_ColorReg_ReadWord();
4953: }
4954:
4955: void Video_Color14_ReadWord(void)
4956: {
4957: Video_ColorReg_ReadWord();
4958: }
4959:
4960: void Video_Color15_ReadWord(void)
4961: {
4962: Video_ColorReg_ReadWord();
1.1.1.8 root 4963: }
4964:
4965:
4966: /*-----------------------------------------------------------------------*/
1.1.1.11 root 4967: /**
1.1.1.24 root 4968: * Write to video resolution register (0xff8260)
4969: * NOTE : resolution register is stored in both the GLUE and the SHIFTER
4970: * When writing to ff8260, the GLUE gets the new value immediately, before
4971: * rounding to 4 cycles. The rounding happens later, when the SHIFTER reads
4972: * the data from the bus, as explained by Ijor on atari-forum.com :
4973: * - CPU starts a bus cycle addressing shifter RES
4974: * - CPU drives the data bus with the new value
4975: * - GLUE reads the value from the CPU data bus
4976: * - Possible wait states inserted here by MMU
4977: * - MMU connects the CPU data bus to the RAM data bus
4978: * - SHIFTER reads the value from the RAM data bus
4979: * - CPU finishes the bus cycle
1.1.1.11 root 4980: */
1.1.1.24 root 4981: void Video_Res_WriteByte(void)
1.1.1.8 root 4982: {
1.1.1.20 root 4983: Uint8 VideoShifterByte;
4984:
1.1.1.24 root 4985: if (Config_IsMachineTT())
1.1.1.20 root 4986: {
4987: TTRes = IoMem_ReadByte(0xff8260) & 7;
4988: /* Copy to TT shifter mode register: */
4989: IoMem_WriteByte(0xff8262, TTRes);
4990: }
4991: else if (!bUseVDIRes) /* ST and STE mode */
1.1.1.11 root 4992: {
1.1.1.18 root 4993: /* We only care for lower 2-bits */
4994: VideoShifterByte = IoMem[0xff8260] & 3;
1.1.1.22 root 4995: /* 3 is not a valid resolution, use high res instead */
1.1.1.18 root 4996: if ( VideoShifterByte == 3 )
1.1.1.13 root 4997: {
1.1.1.22 root 4998: VideoShifterByte = 2;
4999: IoMem_WriteByte(0xff8260,2);
1.1.1.13 root 5000: }
1.1.1.18 root 5001:
1.1.1.24 root 5002: Video_WriteToGlueShifterRes(VideoShifterByte);
1.1.1.11 root 5003: Video_SetHBLPaletteMaskPointers();
5004: *pHBLPaletteMasks &= 0xff00ffff;
5005: /* Store resolution after palette mask and set resolution write bit: */
5006: *pHBLPaletteMasks |= (((Uint32)VideoShifterByte|0x04)<<16);
5007: }
1.1.1.24 root 5008:
5009: /* Access to shifter regs are on a 4 cycle boundary */
5010: /* Rounding is added here, after the value was processed by Video_Update_Glue_State() */
5011: M68000_SyncCpuBus_OnWriteAccess();
1.1.1.10 root 5012: }
5013:
5014: /*-----------------------------------------------------------------------*/
1.1.1.11 root 5015: /**
1.1.1.13 root 5016: * Handle horizontal scrolling to the left.
5017: * On STE, there're 2 registers that can scroll the line :
5018: * - $ff8264 : scroll without prefetch
1.1.1.14 root 5019: * - $ff8265 : scroll with prefetch
1.1.1.13 root 5020: * Both registers will scroll the line to the left by skipping the amount
5021: * of pixels in $ff8264 or $ff8265 (from 0 to 15).
5022: * As some pixels will be skipped, this means the shifter needs to read
5023: * 16 other pixels in advance in some internal registers to have an uninterrupted flow of pixels.
5024: *
1.1.1.14 root 5025: * These 16 pixels can be prefetched before the display starts (on cycle 56 for example) when using
1.1.1.13 root 5026: * $ff8265 to scroll the line. In that case 8 more bytes per line (low res) will be read. Most programs
5027: * are using $ff8265 to scroll the line.
5028: *
5029: * When using $ff8264, the next 16 pixels will not be prefetched before the display
5030: * starts, they will be read when the display normally starts (cycle 56). While
5031: * reading these 16 pixels, the shifter won't be able to display anything, which will
5032: * result in 16 pixels having the color 0. So, reading the 16 pixels will in fact delay
5033: * the real start of the line, which will look as if it started 16 pixels later. As the
5034: * shifter will stop the display at cycle 56+320 anyway, this means the last 16 pixels
5035: * of each line won't be displayed and you get the equivalent of a shorter 304 pixels line.
5036: * As a consequence, this register is rarely used to scroll the line.
5037: *
1.1.1.21 root 5038: * By writing a value > 0 in $ff8265 (to start prefetching) and immediately after a value of 0
1.1.1.13 root 5039: * in $ff8264 (no scroll and no prefetch), it's possible to fill the internal registers used
5040: * for the scrolling even if scrolling is set to 0. In that case, the shifter will start displaying
5041: * each line 16 pixels earlier (as the data are already available in the internal registers).
5042: * This allows to have 336 pixels per line (instead of 320) for all the remaining lines on the screen.
5043: *
5044: * Although some programs are using this sequence :
5045: * move.w #1,$ffff8264 ; Word access!
5046: * clr.b $ffff8264 ; Byte access!
5047: * It is also possible to add 16 pixels by doing :
5048: * move.b #X,$ff8265 ; with X > 0
5049: * move.b #0,$ff8264
1.1.1.11 root 5050: * Some games (Obsession, Skulls) and demos (Pacemaker by Paradox) use this
5051: * feature to increase the resolution, so we have to emulate this bug, too!
1.1.1.13 root 5052: *
5053: * So considering a low res line of 320 pixels (160 bytes) :
5054: * - if both $ff8264/65 are 0, no scrolling happens, the shifter reads 160 bytes and displays 320 pixels (same as STF)
5055: * - if $ff8265 > 0, line is scrolled, the shifter reads 168 bytes and displays 320 pixels.
5056: * - if $ff8264 > 0, line is scrolled, the shifter reads 160 bytes and displays 304 pixels,
5057: * the display starts 16 pixels later.
5058: * - if $ff8265 > 0 and then $ff8264 = 0, there's no scrolling, the shifter reads 168 bytes and displays 336 pixels,
5059: * the display starts 16 pixels earlier.
1.1.1.11 root 5060: */
1.1.1.13 root 5061:
1.1.1.24 root 5062: void Video_HorScroll_Read_8264(void)
5063: {
5064: /* Access to shifter regs are on a 4 cycle boundary */
5065: M68000_SyncCpuBus_OnReadAccess();
5066: }
5067:
5068: void Video_HorScroll_Read_8265(void)
5069: {
5070: /* Access to shifter regs are on a 4 cycle boundary */
5071: M68000_SyncCpuBus_OnReadAccess();
5072: /* [NP] TODO : it seems ff8265 has some additional wait states */
5073: }
5074:
1.1.1.13 root 5075: void Video_HorScroll_Write_8264(void)
5076: {
1.1.1.24 root 5077: /* Access to shifter regs are on a 4 cycle boundary */
5078: M68000_SyncCpuBus_OnWriteAccess();
5079:
1.1.1.13 root 5080: Video_HorScroll_Write();
5081: }
5082:
5083: void Video_HorScroll_Write_8265(void)
5084: {
1.1.1.24 root 5085: /* Access to shifter regs are on a 4 cycle boundary */
5086: M68000_SyncCpuBus_OnWriteAccess();
5087: /* [NP] TODO : it seems ff8265 has some additional wait states */
5088:
1.1.1.13 root 5089: Video_HorScroll_Write();
5090: }
5091:
1.1.1.10 root 5092: void Video_HorScroll_Write(void)
5093: {
1.1.1.13 root 5094: Uint32 RegAddr;
1.1.1.11 root 5095: Uint8 ScrollCount;
1.1.1.13 root 5096: Uint8 Prefetch;
1.1.1.15 root 5097: int FrameCycles, HblCounterVideo, LineCycles;
5098: bool Add16px = false;
1.1.1.13 root 5099: static Uint8 LastVal8265 = 0;
5100: int Delayed;
5101:
1.1.1.15 root 5102: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24 root 5103: LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.10 root 5104:
1.1.1.13 root 5105: RegAddr = IoAccessCurrentAddress; /* 0xff8264 or 0xff8265 */
5106: ScrollCount = IoMem[ RegAddr ];
1.1.1.11 root 5107: ScrollCount &= 0x0f;
1.1.1.10 root 5108:
1.1.1.13 root 5109: if ( RegAddr == 0xff8264 )
5110: {
5111: Prefetch = 0; /* scroll without prefetch */
1.1.1.15 root 5112: LastCycleScroll8264 = FrameCycles;
1.1.1.11 root 5113:
1.1.1.15 root 5114: ShifterFrame.Scroll8264Pos.VBL = nVBLs;
5115: ShifterFrame.Scroll8264Pos.FrameCycles = FrameCycles;
5116: ShifterFrame.Scroll8264Pos.HBL = HblCounterVideo;
5117: ShifterFrame.Scroll8264Pos.LineCycles = LineCycles;
5118:
5119: if ( ( ScrollCount == 0 ) && ( LastVal8265 > 0 )
5120: && ( ShifterFrame.Scroll8265Pos.VBL > 0 ) /* a write to ff8265 has been made */
5121: && ( ShifterFrame.Scroll8265Pos.VBL == ShifterFrame.Scroll8264Pos.VBL ) /* during the same VBL */
5122: && ( ShifterFrame.Scroll8264Pos.FrameCycles - ShifterFrame.Scroll8265Pos.FrameCycles <= 40 ) )
1.1.1.13 root 5123: {
1.1.1.15 root 5124: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect ste left+16 pixels\n" );
5125: Add16px = true;
1.1.1.13 root 5126: }
5127: }
5128: else
1.1.1.11 root 5129: {
1.1.1.13 root 5130: Prefetch = 1; /* scroll with prefetch */
1.1.1.15 root 5131: LastCycleScroll8265 = FrameCycles;
5132:
5133: ShifterFrame.Scroll8265Pos.VBL = nVBLs;
5134: ShifterFrame.Scroll8265Pos.FrameCycles = FrameCycles;
5135: ShifterFrame.Scroll8265Pos.HBL = HblCounterVideo;
5136: ShifterFrame.Scroll8265Pos.LineCycles = LineCycles;
5137:
1.1.1.13 root 5138: LastVal8265 = ScrollCount;
1.1.1.15 root 5139: Add16px = false;
1.1.1.11 root 5140: }
1.1.1.13 root 5141:
5142:
5143: /* If the write was made before display starts on the current line, then */
5144: /* we can still change the value now. Else, the new values will be used */
5145: /* for line n+1. */
5146: /* We must also check the write does not overlap the end of the line */
1.1.1.24 root 5147: if ( ( ( LineCycles <= pVideoTiming->H_Start_Low_50 ) && ( nHBL == HblCounterVideo ) )
1.1.1.18 root 5148: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.11 root 5149: {
1.1.1.13 root 5150: HWScrollCount = ScrollCount; /* display has not started, we can still change */
5151: HWScrollPrefetch = Prefetch;
5152: bSteBorderFlag = Add16px;
5153: NewHWScrollCount = -1; /* cancel 'pending' change */
1.1.1.15 root 5154: Delayed = false;
1.1.1.11 root 5155: }
5156: else
5157: {
1.1.1.13 root 5158: NewHWScrollCount = ScrollCount; /* display has started, can't change HWScrollCount now */
5159: NewHWScrollPrefetch = Prefetch;
5160: if ( Add16px )
5161: NewSteBorderFlag = 1;
5162: else
5163: NewSteBorderFlag = 0;
1.1.1.15 root 5164: Delayed = true;
1.1.1.11 root 5165: }
5166:
1.1.1.15 root 5167: LOG_TRACE(TRACE_VIDEO_STE , "write ste %x hwscroll=%x delayed=%s video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
1.1.1.13 root 5168: RegAddr , ScrollCount, Delayed ? "yes" : "no" ,
1.1.1.15 root 5169: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.13 root 5170: }
1.1.1.11 root 5171:
1.1.1.24 root 5172:
5173: /*-----------------------------------------------------------------------*/
5174: /**
5175: * Helper for TT->ST color reg copies
5176: */
5177: static void TT2STColor(Uint32 ttaddr, Uint32 staddr)
5178: {
5179: Uint16 stcolor, ttcolor;
5180:
5181: ttcolor = IoMem_ReadWord(ttaddr);
5182: stcolor = ((ttcolor & 0xeee) >> 1) | ((ttcolor&0x111) << 3);
5183: IoMem_WriteWord(staddr, stcolor);
5184: #if 0
5185: fprintf(stderr, "0x%x: 0x%03x (TT) -> 0x%x: 0x%03x (ST)\n",
5186: ttaddr, ttcolor, staddr, stcolor);
5187: #endif
5188: }
5189:
1.1.1.11 root 5190: /*-----------------------------------------------------------------------*/
5191: /**
5192: * Write to TT shifter mode register (0xff8262)
5193: */
5194: void Video_TTShiftMode_WriteWord(void)
5195: {
1.1.1.24 root 5196: Uint32 stpalette = 0xff8240;
5197: Uint32 ttpalette = 0xff8400;
5198: int i;
5199:
1.1.1.11 root 5200: TTRes = IoMem_ReadByte(0xff8262) & 7;
1.1.1.21 root 5201: TTSpecialVideoMode = IoMem_ReadByte(0xff8262) & 0x90;
1.1.1.11 root 5202:
5203: /*fprintf(stderr, "Write to FF8262: %x, res=%i\n", IoMem_ReadWord(0xff8262), TTRes);*/
5204:
5205: /* Is it an ST compatible resolution? */
5206: if (TTRes <= 2)
5207: {
5208: IoMem_WriteByte(0xff8260, TTRes);
1.1.1.24 root 5209: Video_Res_WriteByte();
1.1.1.21 root 5210: IoMem_WriteByte(0xff8262, TTRes | TTSpecialVideoMode);
5211: }
5212:
1.1.1.24 root 5213: /* ST palette needs to be updated in case there was a bank switch */
5214: ttpalette += TTPaletteSTBank() * 16*SIZE_WORD;
5215: #if 0
5216: fprintf(stderr, "TT ST Palette bank: %d\n", TTPaletteSTBank());
5217: #endif
5218: for (i = 0; i < 16*SIZE_WORD; i += SIZE_WORD)
1.1.1.21 root 5219: {
1.1.1.24 root 5220: TT2STColor(ttpalette, stpalette);
5221: ttpalette += SIZE_WORD;
5222: stpalette += SIZE_WORD;
1.1.1.11 root 5223: }
1.1.1.24 root 5224: /* in case bank was switched and there are <= 16 colors */
5225: bTTColorsSync = false;
1.1.1.11 root 5226: }
5227:
1.1.1.24 root 5228:
1.1.1.11 root 5229: /*-----------------------------------------------------------------------*/
5230: /**
1.1.1.24 root 5231: * Write to TT color register area (at 0xff8400)
5232: *
5233: * Sync TT to ST color register
5234: *
5235: * Although registers themselves are word sized, writes to this area
5236: * can be of any size. Hatari IO-area handling doesn't feed them here
5237: * word sized, that would require separate handler for each palette
5238: * entry, and there are 256 of them.
5239: */
5240: void Video_TTColorRegs_Write(void)
5241: {
5242: const Uint32 stpalette = 0xff8240;
5243: const Uint32 ttpalette = 0xff8400;
5244: int offset, i;
5245: Uint32 addr;
5246:
5247: /* ensure even address for byte accesses */
5248: addr = IoAccessCurrentAddress & 0xfffffffe;
5249:
5250: offset = addr - (ttpalette + TTPaletteSTBank() * 16*SIZE_WORD);
5251:
5252: /* in case it was long access */
5253: for (i = 0; i < nIoMemAccessSize; i += 2)
5254: {
5255: /* outside ST->TT color reg mapping bank? */
5256: if (offset < 0 || offset >= 16*SIZE_WORD)
5257: continue;
5258: TT2STColor(addr, stpalette + offset);
5259: offset += 2;
5260: addr += 2;
5261: }
1.1.1.15 root 5262: bTTColorsSync = false;
1.1.1.11 root 5263: }
5264:
5265: /*-----------------------------------------------------------------------*/
5266: /**
1.1.1.24 root 5267: * Write to ST color register area on TT (starting at 0xff8240)
5268: *
5269: * ST color register write on TT -> sync to TT color register
5270: *
5271: * Although registers themselves are word sized, writes to this area
5272: * can be of any size. Hatari IO-area handling doesn't feed them here
5273: * word sized, that would require separate handler for each palette
5274: * entry.
5275: */
5276: void Video_TTColorRegs_STRegWrite(void)
5277: {
5278: const Uint32 stpalette = 0xff8240;
5279: const Uint32 ttpalette = 0xff8400;
5280: Uint16 ttcolor;
5281: Uint16 stcolor;
5282: int offset, i;
5283: Uint32 addr;
5284:
5285: /* byte writes don't have effect on TT */
5286: if (nIoMemAccessSize < 2)
5287: return;
5288:
5289: addr = IoAccessCurrentAddress;
5290:
5291: offset = addr - stpalette;
5292: assert(offset >= 0 && offset < 16*SIZE_WORD);
5293: offset += TTPaletteSTBank() * 16*SIZE_WORD;
5294:
5295: /* in case it was long access */
5296: for (i = 0; i < nIoMemAccessSize; i += 2)
5297: {
5298: /* program may write 0xFFFF and read it back
5299: * to check for STe palette so need to be masked
5300: */
5301: stcolor = IoMem_ReadWord(addr) & 0xfff;
5302: IoMem_WriteWord(addr, stcolor);
5303: /* Sync ST(e) color to TT register */
5304: ttcolor = ((stcolor & 0x777) << 1) | ((stcolor & 0x888) >> 3);
5305: IoMem_WriteWord(ttpalette + offset, ttcolor);
5306: #if 0
5307: fprintf(stderr, "0x%x: 0x%03x (ST) -> 0x%x: 0x%03x (TT)\n",
5308: addr, stcolor, ttpalette + offset, ttcolor);
5309: #endif
5310: offset += 2;
5311: addr += 2;
5312: }
5313: bTTColorsSync = false;
1.1.1.8 root 5314: }
1.1.1.22 root 5315:
5316:
5317: /*-----------------------------------------------------------------------*/
5318: /**
5319: * display video related information (for debugger info command)
5320: */
1.1.1.23 root 5321: void Video_Info(FILE *fp, Uint32 dummy)
1.1.1.22 root 5322: {
5323: const char *mode;
1.1.1.24 root 5324: switch (VerticalOverscan) {
5325: case V_OVERSCAN_NONE:
1.1.1.22 root 5326: mode = "none";
5327: break;
1.1.1.24 root 5328: case V_OVERSCAN_NO_TOP:
1.1.1.22 root 5329: mode = "top";
5330: break;
1.1.1.24 root 5331: case V_OVERSCAN_NO_BOTTOM_50:
5332: case V_OVERSCAN_NO_BOTTOM_60:
1.1.1.22 root 5333: mode = "bottom";
5334: break;
1.1.1.24 root 5335: case V_OVERSCAN_NO_TOP|V_OVERSCAN_NO_BOTTOM_50:
1.1.1.22 root 5336: mode = "top+bottom";
5337: break;
5338: default:
5339: mode = "unknown";
5340: }
1.1.1.23 root 5341: fprintf(fp, "Video base : 0x%x\n", VideoBase);
5342: fprintf(fp, "VBL counter : %d\n", nVBLs);
5343: fprintf(fp, "HBL line : %d\n", nHBL);
5344: fprintf(fp, "V-overscan : %s\n", mode);
5345: fprintf(fp, "Refresh rate : %d Hz\n", nScreenRefreshRate);
5346: fprintf(fp, "Frame skips : %d\n", nFrameSkips);
1.1.1.22 root 5347:
5348: /* TODO: any other information that would be useful to show? */
5349: }
1.1.1.24 root 5350:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.