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