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