|
|
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'*/
335: /* 2014/03/21 [NP] For STE in med res overscan at 60 Hz, add a 3 pixels shift to have */
336: /* bitmaps and color changes synchronised (fix 'HighResMode' by Paradox). */
337: /* protection code running at address $ff8240). */
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'). */
! 350: /* 2015/0818 [NP] In Video_CalculateAddress, handle the case when reading overlaps end */
! 351: /* of line / start of next line and STE's linewidth at $FF820F != 0. */
1.1.1.11 root 352:
1.1.1.12 root 353:
1.1.1.15 root 354: const char Video_fileid[] = "Hatari video.c : " __DATE__ " " __TIME__;
1.1 root 355:
1.1.1.7 root 356: #include <SDL_endian.h>
1.1.1.4 root 357:
1.1 root 358: #include "main.h"
1.1.1.6 root 359: #include "configuration.h"
1.1.1.10 root 360: #include "cycles.h"
1.1 root 361: #include "fdc.h"
1.1.1.16 root 362: #include "cycInt.h"
1.1.1.8 root 363: #include "ioMem.h"
1.1.1.4 root 364: #include "keymap.h"
1.1 root 365: #include "m68000.h"
1.1.1.21 root 366: #include "hatari-glue.h"
1.1 root 367: #include "memorySnapShot.h"
368: #include "mfp.h"
1.1.1.11 root 369: #include "printer.h"
1.1 root 370: #include "screen.h"
1.1.1.13 root 371: #include "screenSnapShot.h"
1.1 root 372: #include "shortcut.h"
373: #include "sound.h"
1.1.1.17 root 374: #include "dmaSnd.h"
1.1 root 375: #include "spec512.h"
376: #include "stMemory.h"
377: #include "vdi.h"
378: #include "video.h"
379: #include "ymFormat.h"
1.1.1.11 root 380: #include "falcon/videl.h"
381: #include "falcon/hostscreen.h"
1.1.1.16 root 382: #include "avi_record.h"
1.1.1.21 root 383: #include "ikbd.h"
1.1.1.22 root 384: #include "floppy_ipf.h"
1.1.1.4 root 385:
386:
1.1.1.11 root 387: /* The border's mask allows to keep track of all the border tricks */
388: /* applied to one video line. The masks for all lines are stored in the array */
389: /* ScreenBorderMask[]. */
390: /* - bits 0-15 are used to describe the border tricks. */
391: /* - bits 20-23 are used to store the bytes offset to apply for some particular */
1.1.1.15 root 392: /* tricks (for example med res overscan can shift display by 0 or 2 bytes */
393: /* depending on when the switch to med res is done after removing the left */
1.1.1.11 root 394: /* border). */
395:
1.1.1.14 root 396: #define BORDERMASK_NONE 0x00 /* no effect on this line */
1.1.1.11 root 397: #define BORDERMASK_LEFT_OFF 0x01 /* removal of left border with hi/lo res switch -> +26 bytes */
398: #define BORDERMASK_LEFT_PLUS_2 0x02 /* line starts earlier in 60 Hz -> +2 bytes */
399: #define BORDERMASK_STOP_MIDDLE 0x04 /* line ends in hires at cycle 160 -> -106 bytes */
400: #define BORDERMASK_RIGHT_MINUS_2 0x08 /* line ends earlier in 60 Hz -> -2 bytes */
401: #define BORDERMASK_RIGHT_OFF 0x10 /* removal of right border -> +44 bytes */
402: #define BORDERMASK_RIGHT_OFF_FULL 0x20 /* full removal of right border and next left border -> +22 bytes */
1.1.1.15 root 403: #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 404: #define BORDERMASK_EMPTY_LINE 0x80 /* 60/50 Hz switch prevents the line to start, video counter is not incremented */
1.1.1.15 root 405: #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 406: #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 407: #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 408:
409:
410: int STRes = ST_LOW_RES; /* current ST resolution */
411: int TTRes; /* TT shifter resolution mode */
1.1.1.13 root 412: int nFrameSkips; /* speed up by skipping video frames */
1.1.1.9 root 413:
1.1.1.13 root 414: bool bUseHighRes; /* Use hi-res (ie Mono monitor) */
1.1 root 415: int OverscanMode; /* OVERSCANMODE_xxxx for current display frame */
1.1.1.17 root 416: Uint16 HBLPalettes[HBL_PALETTE_LINES]; /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
1.1.1.8 root 417: Uint16 *pHBLPalettes; /* Pointer to current palette lists, one per HBL */
1.1.1.17 root 418: Uint32 HBLPaletteMasks[HBL_PALETTE_MASKS]; /* Bit mask of palette colours changes, top bit set is resolution change */
1.1.1.9 root 419: Uint32 *pHBLPaletteMasks;
1.1.1.15 root 420: int nScreenRefreshRate = 50; /* 50 or 60 Hz in color, 71 Hz in mono */
1.1.1.8 root 421: Uint32 VideoBase; /* Base address in ST Ram for screen (read on each VBL) */
1.1.1.9 root 422:
1.1.1.11 root 423: int nVBLs; /* VBL Counter */
424: int nHBL; /* HBL line */
425: int nStartHBL; /* Start HBL for visible screen */
426: int nEndHBL; /* End HBL for visible screen */
1.1.1.10 root 427: int nScanlinesPerFrame = 313; /* Number of scan lines per frame */
428: int nCyclesPerLine = 512; /* Cycles per horizontal line scan */
1.1.1.13 root 429: static int nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ; /* The first line of the ST screen that is copied to the PC screen buffer */
430: 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 431: static int CyclesPerVBL = 313*512; /* Number of cycles per VBL */
1.1.1.10 root 432:
1.1.1.13 root 433: static Uint8 HWScrollCount; /* HW scroll pixel offset, STE only (0...15) */
434: static int NewHWScrollCount = -1; /* Used in STE mode when writing to the scrolling registers $ff8264/65 */
435: static Uint8 HWScrollPrefetch; /* 0 when scrolling with $ff8264, 1 when scrolling with $ff8265 */
436: static int NewHWScrollPrefetch = -1; /* Used in STE mode when writing to the scrolling registers $ff8264/65 */
437: static Uint8 LineWidth; /* Scan line width add, STe only (words, minus 1) */
438: static int NewLineWidth = -1; /* Used in STE mode when writing to the line width register $ff820f */
1.1.1.16 root 439: static int VideoCounterDelayedOffset = 0; /* Used in STE mode when changing video counter while display is on */
440: 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 441: static Uint8 *pVideoRaster; /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
1.1.1.15 root 442: static bool bSteBorderFlag; /* true when screen width has been switched to 336 (e.g. in Obsession) */
1.1.1.13 root 443: static int NewSteBorderFlag = -1; /* New value for next line */
1.1.1.21 root 444: static bool bTTColorsSync, bTTColorsSTSync; /* whether TT colors need conversion to SDL */
445:
446: bool bTTSampleHold = false; /* TT special video mode */
447: static bool bTTHypermono = false; /* TT special video mode */
448:
449: static int TTSpecialVideoMode = 0; /* TT special video mode */
450: static int nPrevTTSpecialVideoMode = 0; /* TT special video mode */
1.1 root 451:
1.1.1.17 root 452: static int LastCycleScroll8264; /* value of Cycles_GetCounterOnWriteAccess last time ff8264 was set for the current VBL */
453: static int LastCycleScroll8265; /* value of Cycles_GetCounterOnWriteAccess last time ff8265 was set for the current VBL */
454:
455: static int LineRemoveTopCycle = LINE_REMOVE_TOP_CYCLE_STF;
456: static int LineRemoveBottomCycle = LINE_REMOVE_BOTTOM_CYCLE_STF;
457: static int RestartVideoCounterCycle = RESTART_VIDEO_COUNTER_CYCLE_STF;
458: static int VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STF;
1.1.1.13 root 459:
460: int LineTimerBCycle = LINE_END_CYCLE_50 + TIMERB_VIDEO_CYCLE_OFFSET; /* position of the Timer B interrupt on active lines */
1.1.1.15 root 461: int TimerBEventCountCycleStart = -1; /* value of Cycles_GetCounterOnWriteAccess last time timer B was started for the current VBL */
1.1 root 462:
1.1.1.20 root 463: int HblJitterIndex = 0;
464: const int HblJitterArray[] = {
465: 8,4,4,0,0 /* measured on STF */
466: };
467: const int HblJitterArrayPending[] = {
468: 4,4,4,4,4 // { 8,8,12,8,12 }; /* measured on STF, not always accurate */
469: };
470: int VblJitterIndex = 0;
471: const int VblJitterArray[] = {
472: 8,0,4,0,4 /* measured on STF */
473: };
474: const int VblJitterArrayPending[] = {
475: 8,8,12,8,12 /* not verified on STF, use the same as HBL */
476: };
1.1.1.14 root 477:
1.1.1.23! root 478: static int BlankLines = 0; /* Number of empty line with no signal (by switching hi/lo near cycles 500) */
1.1.1.12 root 479:
1.1.1.15 root 480:
481: typedef struct
482: {
483: int VBL; /* VBL for this Pos (or -1 if Pos not defined for now) */
484: int FrameCycles; /* Number of cycles since this VBL */
485: int HBL; /* HBL in the VBL */
486: int LineCycles; /* cycles in the HBL */
487: } SHIFTER_POS;
488:
489:
490: typedef struct
491: {
492: int StartCycle; /* first cycle of this line, as returned by Cycles_GetCounter */
493:
494: Uint32 BorderMask; /* borders' states for this line */
495: int DisplayPixelShift; /* number of pixels to shift the whole line (<0 shift to the left, >0 shift to the right) */
496: /* On STF, this is obtained when switching hi/med for a variable number of cycles, */
497: /* but just removing left border will shift the line too. */
498:
499: int DisplayStartCycle; /* cycle where display starts for this line (0-512) : 0, 52 or 56 */
500: int DisplayEndCycle; /* cycle where display ends for this line (0-512) : 0, 160, 372, 376, 460 or 512 */
501: int DisplayBytes; /* how many bytes to display for this line */
502:
503: } SHIFTER_LINE;
504:
505:
506: typedef struct
507: {
508: int HBL_CyclePos; /* cycle position for the HBL int (depends on freq/res) */
509: int TimerB_CyclePos; /* cycle position for the Timer B int (depends on freq/res) */
510:
511: int Freq; /* value of ff820a & 2, or -1 if not set */
512: int Res; /* value of ff8260 & 3, or -1 if not set */
513: SHIFTER_POS FreqPos50; /* position of latest freq change to 50 Hz*/
514: SHIFTER_POS FreqPos60; /* position of latest freq change to 60 Hz*/
515: SHIFTER_POS ResPosLo; /* position of latest change to low res */
516: SHIFTER_POS ResPosMed; /* position of latest change to med res */
517: SHIFTER_POS ResPosHi; /* position of latest change to high res */
518:
519: SHIFTER_POS Scroll8264Pos; /* position of latest write to $ff8264 */
520: SHIFTER_POS Scroll8265Pos; /* position of latest write to $ff8265 */
521:
522: SHIFTER_LINE ShifterLines[ MAX_SCANLINES_PER_FRAME ];
523: } SHIFTER_FRAME;
524:
525:
1.1.1.23! root 526: static SHIFTER_FRAME ShifterFrame;
1.1.1.15 root 527:
528:
529:
530: /*--------------------------------------------------------------*/
531: /* Local functions prototypes */
532: /*--------------------------------------------------------------*/
533:
534: static void Video_SetSystemTimings ( void );
535:
536: static Uint32 Video_CalculateAddress ( void );
1.1.1.16 root 537: static int Video_GetMMUStartCycle ( int DisplayStartCycle );
1.1.1.15 root 538: static void Video_WriteToShifter ( Uint8 Res );
539: static void Video_Sync_SetDefaultStartEnd ( Uint8 Freq , int HblCounterVideo , int LineCycles );
540:
541: static int Video_HBL_GetPos ( void );
1.1.1.18 root 542: static int Video_TimerB_GetDefaultPos ( void );
1.1.1.15 root 543: static void Video_EndHBL ( void );
544: static void Video_StartHBL ( void );
545:
546: static void Video_StoreFirstLinePalette(void);
547: static void Video_StoreResolution(int y);
548: static void Video_CopyScreenLineMono(void);
549: static void Video_CopyScreenLineColor(void);
550: static void Video_CopyVDIScreen(void);
551: static void Video_SetHBLPaletteMaskPointers(void);
552:
553: static void Video_UpdateTTPalette(int bpp);
554: static void Video_DrawScreen(void);
555:
556: static void Video_ResetShifterTimings(void);
557: static void Video_InitShifterLines(void);
558: static void Video_ClearOnVBL(void);
559:
560: static void Video_AddInterrupt ( int Pos , interrupt_id Handler );
561: static void Video_AddInterruptHBL ( int Pos );
562:
1.1.1.22 root 563: static void Video_ColorReg_WriteWord(void);
564: static void Video_ColorReg_ReadWord(void);
1.1.1.15 root 565:
566:
1.1 root 567: /*-----------------------------------------------------------------------*/
1.1.1.11 root 568: /**
569: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
570: */
1.1.1.13 root 571: void Video_MemorySnapShot_Capture(bool bSave)
1.1 root 572: {
1.1.1.23! root 573: Uint32 addr;
! 574:
1.1.1.11 root 575: /* Save/Restore details */
576: MemorySnapShot_Store(&TTRes, sizeof(TTRes));
577: MemorySnapShot_Store(&bUseHighRes, sizeof(bUseHighRes));
578: MemorySnapShot_Store(&nVBLs, sizeof(nVBLs));
579: MemorySnapShot_Store(&nHBL, sizeof(nHBL));
580: MemorySnapShot_Store(&nStartHBL, sizeof(nStartHBL));
581: MemorySnapShot_Store(&nEndHBL, sizeof(nEndHBL));
582: MemorySnapShot_Store(&OverscanMode, sizeof(OverscanMode));
583: MemorySnapShot_Store(HBLPalettes, sizeof(HBLPalettes));
584: MemorySnapShot_Store(HBLPaletteMasks, sizeof(HBLPaletteMasks));
585: MemorySnapShot_Store(&VideoBase, sizeof(VideoBase));
1.1.1.23! root 586: if ( bSave )
! 587: {
! 588: addr = pVideoRaster - STRam;
! 589: MemorySnapShot_Store(&addr, sizeof(addr));
! 590: }
! 591: else
! 592: {
! 593: MemorySnapShot_Store(&addr, sizeof(addr));
! 594: pVideoRaster = &STRam[VideoBase];
! 595: }
1.1.1.11 root 596: MemorySnapShot_Store(&LineWidth, sizeof(LineWidth));
597: MemorySnapShot_Store(&HWScrollCount, sizeof(HWScrollCount));
598: MemorySnapShot_Store(&nScanlinesPerFrame, sizeof(nScanlinesPerFrame));
599: MemorySnapShot_Store(&nCyclesPerLine, sizeof(nCyclesPerLine));
600: MemorySnapShot_Store(&nFirstVisibleHbl, sizeof(nFirstVisibleHbl));
601: MemorySnapShot_Store(&bSteBorderFlag, sizeof(bSteBorderFlag));
1.1.1.14 root 602: MemorySnapShot_Store(&HblJitterIndex, sizeof(HblJitterIndex));
603: MemorySnapShot_Store(&VblJitterIndex, sizeof(VblJitterIndex));
1.1.1.15 root 604: MemorySnapShot_Store(&ShifterFrame, sizeof(ShifterFrame));
1.1.1.21 root 605: MemorySnapShot_Store(&bTTSampleHold, sizeof(bTTSampleHold));
606: MemorySnapShot_Store(&bTTHypermono, sizeof(bTTHypermono));
607: MemorySnapShot_Store(&TTSpecialVideoMode, sizeof(TTSpecialVideoMode));
1.1.1.15 root 608: }
609:
610:
611: /*-----------------------------------------------------------------------*/
612: /**
613: * Reset video chip
614: */
615: void Video_Reset(void)
616: {
617: /* NOTE! Must reset all of these register type things here!!!! */
1.1.1.19 root 618: Video_Reset_Glue();
1.1.1.15 root 619:
620: /* Set system specific timings */
621: Video_SetSystemTimings();
622:
623: /* Reset VBL counter */
624: nVBLs = 0;
625: /* Reset addresses */
626: VideoBase = 0L;
627:
628: /* Reset shifter's state variables */
629: ShifterFrame.Freq = -1;
630: ShifterFrame.Res = -1;
631: ShifterFrame.FreqPos50.VBL = -1;
632: ShifterFrame.FreqPos60.VBL = -1;
633: ShifterFrame.ResPosLo.VBL = -1;
634: ShifterFrame.ResPosMed.VBL = -1;
635: ShifterFrame.ResPosHi.VBL = -1;
636: ShifterFrame.Scroll8264Pos.VBL = -1;
637: ShifterFrame.Scroll8265Pos.VBL = -1;
638:
639: Video_InitShifterLines ();
640:
641: /* Reset STE screen variables */
642: LineWidth = 0;
643: HWScrollCount = 0;
644: bSteBorderFlag = false;
645:
646: NewLineWidth = -1; /* cancel pending modifications set before the reset */
647: NewHWScrollCount = -1;
648:
1.1.1.16 root 649: VideoCounterDelayedOffset = 0;
650: pVideoRasterDelayed = NULL;
651:
1.1.1.15 root 652: /* Reset jitter indexes */
653: HblJitterIndex = 0;
654: VblJitterIndex = 0;
655:
656: /* Clear framecycles counter */
657: Cycles_SetCounter(CYCLES_COUNTER_VIDEO, 0);
658:
659: /* Clear ready for new VBL */
660: Video_ClearOnVBL();
661: }
662:
663:
664: /*-----------------------------------------------------------------------*/
665: /**
666: * Reset the GLUE chip responsible for generating the H/V sync signals.
667: * When the 68000 RESET instruction is called, frequency and resolution
668: * should be reset to 0.
669: */
670: void Video_Reset_Glue(void)
671: {
1.1.1.20 root 672: Uint8 VideoShifterByte;
673:
1.1.1.19 root 674: IoMem_WriteByte(0xff820a,0); /* Video frequency */
675:
676: /* Are we in high-res? */
677: if (bUseHighRes)
678: VideoShifterByte = ST_HIGH_RES; /* Mono monitor */
679: else
680: VideoShifterByte = ST_LOW_RES;
681: if (bUseVDIRes)
682: VideoShifterByte = VDIRes;
683:
684: IoMem_WriteByte(0xff8260, VideoShifterByte);
1.1 root 685: }
686:
1.1.1.8 root 687:
1.1 root 688: /*-----------------------------------------------------------------------*/
1.1.1.12 root 689: /*
690: * Set specific video timings, depending on the system being emulated.
691: */
1.1.1.15 root 692: static void Video_SetSystemTimings(void)
1.1.1.12 root 693: {
1.1.1.19 root 694: if ( ConfigureParams.System.nMachineType == MACHINE_ST )
695: {
696: LineRemoveTopCycle = LINE_REMOVE_TOP_CYCLE_STF;
697: LineRemoveBottomCycle = LINE_REMOVE_BOTTOM_CYCLE_STF;
698: RestartVideoCounterCycle = RESTART_VIDEO_COUNTER_CYCLE_STF;
699: VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STF;
700: }
1.1.1.20 root 701: else /* STE, TT */
1.1.1.19 root 702: {
703: LineRemoveTopCycle = LINE_REMOVE_TOP_CYCLE_STE;
704: LineRemoveBottomCycle = LINE_REMOVE_BOTTOM_CYCLE_STE;
705: RestartVideoCounterCycle = RESTART_VIDEO_COUNTER_CYCLE_STE;
706: VblVideoCycleOffset = VBL_VIDEO_CYCLE_OFFSET_STE;
707: }
1.1.1.12 root 708: }
709:
710:
711: /*-----------------------------------------------------------------------*/
1.1.1.11 root 712: /**
1.1.1.15 root 713: * Convert the elapsed number of cycles since the start of the VBL
714: * into the corresponding HBL number and the cycle position in the current
715: * HBL. We use the starting cycle position of the closest HBL to compute
716: * the cycle position on the line (this allows to mix lines with different
717: * values for nCyclesPerLine).
718: * We can have 2 cases on the limit where the real video line count can be
719: * different from nHBL :
720: * - when reading video address between cycle 0 and 12, LineCycle will be <0,
721: * so we need to use the data from line nHBL-1
722: * - if LineCycle >= nCyclesPerLine, this means the HBL int was not processed
723: * yet, so the video line number is in fact nHBL+1
724: */
725:
726: void Video_ConvertPosition ( int FrameCycles , int *pHBL , int *pLineCycles )
727: {
1.1.1.16 root 728: if ( 0 && FrameCycles >= CyclesPerVBL ) /* rare case between end of last hbl and start of next VBL (during 64 cycles) */
1.1.1.15 root 729: {
1.1.1.16 root 730: *pHBL = ( FrameCycles - CyclesPerVBL ) / nCyclesPerLine;
731: *pLineCycles = ( FrameCycles - CyclesPerVBL ) % nCyclesPerLine;
732: //fprintf ( stderr , "out of vbl FrameCycles %d CyclesPerVBL %d nHBL=%d %d %d\n" , FrameCycles , CyclesPerVBL, nHBL , *pHBL , *pLineCycles );
1.1.1.15 root 733: }
1.1.1.16 root 734:
735: else /* most common case */
1.1.1.15 root 736: {
1.1.1.16 root 737: *pHBL = nHBL;
738: *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL ].StartCycle;
1.1.1.15 root 739:
1.1.1.16 root 740: if ( *pLineCycles < 0 ) /* reading from the previous video line */
741: {
742: *pHBL = nHBL-1;
743: *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle;
744: }
745:
746: else if ( *pLineCycles >= nCyclesPerLine ) /* reading on the next line, but HBL int was delayed */
747: {
748: *pHBL = nHBL+1;
749: *pLineCycles -= nCyclesPerLine;
750: }
751: }
1.1.1.15 root 752:
753: if ( *pLineCycles < 0 )
754: fprintf ( stderr , "bug nHBL=%d %d %d\n" , nHBL , *pHBL , *pLineCycles );
755:
756: //if ( ( *pHBL != FrameCycles / nCyclesPerLine ) || ( *pLineCycles != FrameCycles % nCyclesPerLine ) )
757: // LOG_TRACE ( TRACE_VIDEO_ADDR , "conv pos %d %d - %d %d\n" , *pHBL , FrameCycles / nCyclesPerLine , *pLineCycles , FrameCycles % nCyclesPerLine );
758: // LOG_TRACE ( TRACE_VIDEO_ADDR , "conv pos %d %d %d\n" , FrameCycles , *pHBL , *pLineCycles );
759: }
760:
761:
762: void Video_GetPosition ( int *pFrameCycles , int *pHBL , int *pLineCycles )
763: {
764: *pFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
765: Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
766: }
767:
768:
769: void Video_GetPosition_OnWriteAccess ( int *pFrameCycles , int *pHBL , int *pLineCycles )
770: {
771: *pFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
772: Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
773: }
774:
775:
776: void Video_GetPosition_OnReadAccess ( int *pFrameCycles , int *pHBL , int *pLineCycles )
777: {
778: *pFrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO);
779: Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
780: }
781:
782:
783: /*-----------------------------------------------------------------------*/
784: /**
1.1.1.11 root 785: * Calculate and return video address pointer.
786: */
1.1.1.15 root 787: static Uint32 Video_CalculateAddress ( void )
1.1 root 788: {
1.1.1.15 root 789: int FrameCycles, HblCounterVideo, LineCycles;
790: int X, NbBytes;
1.1.1.11 root 791: Uint32 VideoAddress; /* Address of video display in ST screen space */
792: int nSyncByte;
1.1.1.23! root 793: int Res;
1.1.1.11 root 794: int LineBorderMask;
795: int PrevSize;
796: int CurSize;
1.1.1.15 root 797: int LineStartCycle , LineEndCycle;
1.1.1.11 root 798:
799: /* Find number of cycles passed during frame */
1.1.1.21 root 800: /* We need to subtract '12' for correct video address calculation */
1.1.1.15 root 801: FrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12;
1.1.1.11 root 802:
803: /* Now find which pixel we are on (ignore left/right borders) */
1.1.1.15 root 804: Video_ConvertPosition ( FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.11 root 805:
1.1.1.23! root 806: Res = IoMem_ReadByte ( 0xff8260 ) & 3;
! 807: if ( Res & 2 ) /* hi res */
1.1.1.11 root 808: {
1.1.1.23! root 809: LineStartCycle = LINE_START_CYCLE_71;
! 810: LineEndCycle = LINE_END_CYCLE_71;
! 811: HblCounterVideo = FrameCycles / nCyclesPerLine;
! 812: LineCycles = FrameCycles % nCyclesPerLine;
1.1.1.11 root 813: }
1.1.1.23! root 814: else
1.1.1.11 root 815: {
1.1.1.23! root 816: nSyncByte = IoMem_ReadByte(0xff820a) & 2; /* only keep bit 1 */
! 817: if (nSyncByte) /* 50 Hz */
! 818: {
! 819: LineStartCycle = LINE_START_CYCLE_50;
! 820: LineEndCycle = LINE_END_CYCLE_50;
! 821: }
! 822: else /* 60 Hz */
! 823: {
! 824: LineStartCycle = LINE_START_CYCLE_60;
! 825: LineEndCycle = LINE_END_CYCLE_60;
! 826: }
1.1.1.11 root 827: }
828:
1.1.1.23! root 829: X = LineCycles;
1.1.1.11 root 830:
831: /* Top of screen is usually 63 lines from VBL in 50 Hz */
1.1.1.15 root 832: if ( HblCounterVideo < nStartHBL )
1.1.1.11 root 833: {
834: /* pVideoRaster was set during Video_ClearOnVBL using VideoBase */
835: /* and it could also have been modified on STE by writing to ff8205/07/09 */
1.1.1.15 root 836: /* So, we should not use ff8201/ff8203 which are reloaded in ff8205/ff8207 only once per VBL */
837: /* but use pVideoRaster - STRam instead to get current shifter video address */
1.1.1.11 root 838: VideoAddress = pVideoRaster - STRam;
839: }
840:
1.1.1.23! root 841: /* Special case when reading video counter in hi-res (used in the demo 'My Socks Are Weapons' by Legacy) */
! 842: /* This assumes a standard 640x400 resolution with no border removed, so code is simpler */
! 843: /* [NP] TODO : this should be handled in a more generic way with low/med cases */
! 844: /* even when Hatari is not started in monochrome mode */
! 845: else if ( Res & 2 ) /* Hi res */
! 846: {
! 847: if ( X < LineStartCycle )
! 848: X = LineStartCycle; /* display is disabled in the left border */
! 849: else if ( X > LineEndCycle )
! 850: X = LineEndCycle; /* display is disabled in the right border */
! 851:
! 852: NbBytes = ( (X-LineStartCycle)>>1 ) & (~1); /* 2 cycles per byte */
! 853:
! 854: /* One line uses 80 bytes instead of the standard 160 bytes in low/med res */
! 855: if ( HblCounterVideo < nStartHBL + VIDEO_HEIGHT_HBL_MONO )
! 856: VideoAddress = VideoBase + ( HblCounterVideo - nStartHBL ) * ( BORDERBYTES_NORMAL / 2 ) + NbBytes;
! 857: else
! 858: VideoAddress = VideoBase + VIDEO_HEIGHT_HBL_MONO * ( BORDERBYTES_NORMAL / 2 );
! 859: }
! 860:
1.1.1.15 root 861: else if (FrameCycles > RestartVideoCounterCycle)
1.1.1.11 root 862: {
863: /* This is where ff8205/ff8207 are reloaded with the content of ff8201/ff8203 on a real ST */
864: /* (used in ULM DSOTS demos). VideoBase is also reloaded in Video_ClearOnVBL to be sure */
865: VideoBase = (Uint32)IoMem_ReadByte(0xff8201)<<16 | (Uint32)IoMem_ReadByte(0xff8203)<<8;
866: if (ConfigureParams.System.nMachineType != MACHINE_ST)
867: {
1.1.1.20 root 868: /* on STe 2 aligned, on TT 8 aligned. We do STe. */
1.1.1.11 root 869: VideoBase |= IoMem_ReadByte(0xff820d) & ~1;
870: }
871:
872: VideoAddress = VideoBase;
873: }
874:
875: else
876: {
877: VideoAddress = pVideoRaster - STRam; /* pVideoRaster is updated by Video_CopyScreenLineColor */
878:
879: /* Now find which pixel we are on (ignore left/right borders) */
1.1.1.15 root 880: // X = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) % nCyclesPerLine;
1.1.1.11 root 881:
882: /* Get real video line count (can be different from nHBL) */
1.1.1.15 root 883: // HblCounterVideo = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) / nCyclesPerLine;
1.1.1.11 root 884:
885: /* Correct the case when read overlaps end of line / start of next line */
886: /* Video_CopyScreenLineColor was not called yet to update VideoAddress */
887: /* so we need to determine the size of the previous line to get the */
888: /* correct value of VideoAddress. */
889: PrevSize = 0;
890: if ( HblCounterVideo < nHBL )
891: X = 0;
892: else if ( ( HblCounterVideo > nHBL ) /* HblCounterVideo = nHBL+1 */
893: && ( nHBL >= nStartHBL ) ) /* if nHBL was not visible, PrevSize = 0 */
894: {
1.1.1.15 root 895: LineBorderMask = ShifterFrame.ShifterLines[ HblCounterVideo-1 ].BorderMask; /* get border mask for nHBL */
1.1.1.11 root 896: PrevSize = BORDERBYTES_NORMAL; /* normal line */
897:
898: if (LineBorderMask & BORDERMASK_LEFT_OFF)
899: PrevSize += BORDERBYTES_LEFT;
900: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
901: PrevSize += 2;
902:
903: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
904: PrevSize -= 106;
905: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
906: PrevSize -= 2;
907: else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
908: PrevSize += BORDERBYTES_RIGHT;
909:
910: if (LineBorderMask & BORDERMASK_EMPTY_LINE)
911: PrevSize = 0;
1.1.1.23! root 912:
! 913: /* On STE, the Shifter skips the given amount of words as soon as display is disabled */
! 914: /* which is the case here when reading overlaps end/start of line (LineWidth is 0 on STF) */
! 915: PrevSize += LineWidth*2;
1.1.1.11 root 916: }
917:
918:
1.1.1.15 root 919: LineBorderMask = ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask;
1.1.1.11 root 920:
921: CurSize = BORDERBYTES_NORMAL; /* normal line */
922:
923: if (LineBorderMask & BORDERMASK_LEFT_OFF)
924: CurSize += BORDERBYTES_LEFT;
925: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
926: CurSize += 2;
1.1.1.16 root 927: else if (bSteBorderFlag) /* bigger line by 8 bytes on the left (STE specific) */
928: CurSize += 8;
929: else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
930: CurSize += 8; /* 8 more bytes are loaded when scrolling with prefetching */
1.1.1.11 root 931:
932: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
933: CurSize -= 106;
934: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
935: CurSize -= 2;
936: else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
937: CurSize += BORDERBYTES_RIGHT;
938: if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
939: CurSize += BORDERBYTES_RIGHT_FULL;
940:
941: if ( LineBorderMask & BORDERMASK_LEFT_PLUS_2)
942: LineStartCycle = LINE_START_CYCLE_60;
943: else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1.1.1.15 root 944: LineStartCycle = LINE_START_CYCLE_71;
1.1.1.16 root 945: else if ( bSteBorderFlag )
946: LineStartCycle -= 16; /* display starts 16 pixels earlier */
947: else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
948: LineStartCycle -= 16; /* shifter starts reading 16 pixels earlier when scrolling with prefetching */
1.1.1.11 root 949:
950: LineEndCycle = LineStartCycle + CurSize*2;
951:
952:
953: if ( X < LineStartCycle )
1.1.1.16 root 954: X = LineStartCycle; /* display is disabled in the left border */
1.1.1.11 root 955: else if ( X > LineEndCycle )
1.1.1.16 root 956: {
957: X = LineEndCycle; /* display is disabled in the right border */
1.1.1.23! root 958: /* On STE, the Shifter skips the given amount of words as soon as display is disabled */
! 959: /* (LineWidth is 0 on STF) */
1.1.1.16 root 960: VideoAddress += LineWidth*2;
961: }
1.1.1.11 root 962:
963: NbBytes = ( (X-LineStartCycle)>>1 ) & (~1); /* 2 cycles per byte */
964:
965:
966: /* when left border is open, we have 2 bytes less than theorical value */
967: /* (26 bytes in left border, which is not a multiple of 4 cycles) */
968: if ( LineBorderMask & BORDERMASK_LEFT_OFF )
969: NbBytes -= 2;
970:
971: if ( LineBorderMask & BORDERMASK_EMPTY_LINE )
972: NbBytes = 0;
973:
1.1.1.16 root 974: /* Add line cycles if we have not reached end of screen yet */
1.1.1.18 root 975: if ( HblCounterVideo < nEndHBL + BlankLines )
1.1.1.11 root 976: VideoAddress += PrevSize + NbBytes;
977: }
978:
1.1.1.16 root 979: LOG_TRACE(TRACE_VIDEO_ADDR , "video base=%x raster=%x addr=%x video_cyc=%d "
980: "line_cyc=%d/X=%d @ nHBL=%d/video_hbl=%d %d<->%d pc=%x instr_cyc=%d\n",
981: VideoBase, (int)(pVideoRaster - STRam), VideoAddress,
982: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), LineCycles, X, nHBL,
983: HblCounterVideo, LineStartCycle, LineEndCycle, M68000_GetPC(), CurrentInstrCycles);
1.1.1.11 root 984:
985: return VideoAddress;
986: }
987:
988:
989: /*-----------------------------------------------------------------------*/
990: /**
1.1.1.16 root 991: * Calculate the cycle where the STF/STE's MMU starts reading
992: * data to send them to the shifter.
993: * On STE, if hscroll is used, prefetch will cause this position to
994: * happen 16 cycles earlier.
995: * This function should use the same logic as in Video_CalculateAddress.
996: * NOTE : this function is not completly accurate, as even when there's
997: * no hscroll (on STF) the mmu starts reading 16 cycles before display starts.
998: * But it's good enough to emulate writing to ff8205/07/09 on STE.
999: */
1000: static int Video_GetMMUStartCycle ( int DisplayStartCycle )
1001: {
1002: if ( bSteBorderFlag )
1003: DisplayStartCycle -= 16; /* display starts 16 pixels earlier */
1004: else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
1005: DisplayStartCycle -= 16; /* shifter starts reading 16 pixels earlier when scrolling with prefetching */
1006:
1007: return DisplayStartCycle;
1008: }
1009:
1010:
1011: /*-----------------------------------------------------------------------*/
1012: /**
1.1.1.11 root 1013: * Write to VideoShifter (0xff8260), resolution bits
1014: */
1.1.1.15 root 1015: static void Video_WriteToShifter ( Uint8 Res )
1.1 root 1016: {
1.1.1.15 root 1017: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.11 root 1018:
1.1.1.15 root 1019: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.11 root 1020:
1.1.1.15 root 1021: 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",
1022: Res, FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1023:
1024:
1.1.1.13 root 1025: /* Ignore consecutive writes of the same value */
1.1.1.15 root 1026: if ( Res == ShifterFrame.Res )
1.1.1.13 root 1027: return; /* do nothing */
1028:
1.1.1.15 root 1029:
1030: if ( Res == 0x02 ) /* switch to high res */
1031: {
1032: if ( LineCycles < ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle ) /* start could be 0,52,56 */
1033: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_71;
1034:
1035: if ( ( LineCycles < ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) /* end could be 160,372,376,460 */
1036: && ( LineCycles < LINE_END_CYCLE_71 ) )
1037: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_71;
1038: }
1039: else /* switch to lo/med res */
1040: {
1041: /* In lo/med res, display start/end depends on the freq register in $ff820a */
1042: Video_Sync_SetDefaultStartEnd ( IoMem[0xff820a] & 2 , HblCounterVideo , LineCycles );
1043: }
1044:
1045:
1.1.1.11 root 1046: /* Remove left border : +26 bytes */
1.1.1.15 root 1047: /* This can be done with a hi/lo res switch or a hi/med res switch */
1048: if ( ( ShifterFrame.Res == 0x02 ) && ( Res == 0x00 ) /* switched from hi res to lo res */
1.1.1.16 root 1049: // && ( LineCycles >= 12 ) /* switch back to low res should be after cycle 8 */
1.1.1.19 root 1050: && ( ( ShifterFrame.ResPosHi.LineCycles < 12 ) || ( ShifterFrame.ResPosHi.LineCycles >= 504 ) ) /* switch to hi between 504 and 8 */
1.1.1.15 root 1051: && ( LineCycles <= (LINE_START_CYCLE_71+28) )
1.1.1.17 root 1052: && ( FrameCycles - ShifterFrame.ResPosHi.FrameCycles <= 32 ) )
1.1.1.15 root 1053: {
1.1.1.20 root 1054: if ( ( ( ConfigureParams.System.nMachineType == MACHINE_STE ) /* special case for 504/4 and 508/4 on STE -> add 20 bytes to left border */
1.1.1.17 root 1055: || ( ConfigureParams.System.nMachineType == MACHINE_MEGA_STE ) )
1.1.1.20 root 1056: && ( ( ( ShifterFrame.ResPosHi.LineCycles == 504 ) && ( LineCycles == 4 ) )
1057: || ( ( ShifterFrame.ResPosHi.LineCycles == 508 ) && ( LineCycles == 4 ) ) ) )
1.1.1.16 root 1058: {
1059: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_LEFT_OFF_2_STE;
1060: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_71+16; /* starts 16 pixels later */
1.1.1.17 root 1061: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -8; /* screen is shifted 8 pixels to the left */
1.1.1.16 root 1062: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left 2 ste %d<->%d\n" ,
1063: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1064: }
1065: else /* other case for STF/STE -> add 26 bytes */
1066: {
1067: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_LEFT_OFF;
1068: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_71;
1069: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -4; /* screen is shifted 4 pixels to the left */
1070: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" ,
1071: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1072: }
1.1.1.15 root 1073: }
1074:
1075: if ( ( ShifterFrame.Res == 0x02 ) && ( Res == 0x01 ) /* switched from hi res to med res */
1076: && ( LineCycles <= (LINE_START_CYCLE_71+20) )
1077: && ( FrameCycles - ShifterFrame.ResPosHi.FrameCycles <= 30 ) )
1078: {
1079: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_LEFT_OFF_MED; /* a later switch to low res might gives right scrolling */
1080: /* By default, this line will be in med res, except if we detect hardware scrolling later */
1081: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 2 << 20 );
1082: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_71;
1083: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left med %d<->%d\n" ,
1084: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1085: }
1086:
1087: /* Empty line switching res on STF : switch to hi res on cycle 28, then go back to med/lo res */
1088: /* This creates a 0 byte line, the video counter won't change for this line */
1089: else if ( ( ShifterFrame.Res == 0x02 ) /* switched from hi res */
1090: && ( FrameCycles - ShifterFrame.ResPosHi.FrameCycles <= 16 )
1091: && ( ShifterFrame.ResPosHi.LineCycles == LINE_EMPTY_CYCLE_71_STF )
1092: && ( ConfigureParams.System.nMachineType == MACHINE_ST ) )
1093: {
1094: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_EMPTY_LINE;
1095: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = 0;
1096: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = 0;
1097: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res %d<->%d\n" ,
1098: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1099: }
1100:
1101: /* Empty line switching res on STE (switch is 4 cycles later than on STF) */
1102: else if ( ( ShifterFrame.Res == 0x02 ) /* switched from hi res */
1103: && ( FrameCycles - ShifterFrame.ResPosHi.FrameCycles <= 16 )
1104: && ( ShifterFrame.ResPosHi.LineCycles == LINE_EMPTY_CYCLE_71_STE )
1.1.1.17 root 1105: && ( ( ConfigureParams.System.nMachineType == MACHINE_STE )
1106: || ( ConfigureParams.System.nMachineType == MACHINE_MEGA_STE ) ) )
1.1.1.15 root 1107: {
1108: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_EMPTY_LINE;
1109: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = 0;
1110: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = 0;
1111: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res %d<->%d\n" ,
1112: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1113: }
1114:
1115: /* Empty line switching res on STF : switch to hi res just before the HBL then go back to lo/med res */
1.1.1.19 root 1116: /* Next HBL will be an empty line (used in 'No Buddies Land' and 'Delirious Demo IV / NGC') */
1.1.1.15 root 1117: else if ( ( ShifterFrame.Res == 0x02 ) /* switched from hi res */
1.1.1.18 root 1118: && ( ( ShifterFrame.ResPosHi.LineCycles == 500-4 ) || ( ShifterFrame.ResPosHi.LineCycles == 500 ) )
1.1.1.15 root 1119: && ( LineCycles == 508 ) )
1120: {
1.1.1.16 root 1121: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= BORDERMASK_EMPTY_LINE;
1122: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = 0;
1123: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = 0;
1.1.1.18 root 1124: BlankLines++; /* no video signal at all for this line */
1.1.1.16 root 1125: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res 2 %d<->%d for nHBL=%d\n" ,
1126: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle , nHBL+1 );
1.1.1.15 root 1127: }
1128:
1129: /* Start right border near middle of the line : -106 bytes */
1130: /* Switch to hi res just before the start of the right border in hi res, then go back to lo/mid res */
1131: if ( ( ShifterFrame.Res == 0x02 ) /* switched from hi res */
1132: && ( ShifterFrame.ResPosHi.HBL == HblCounterVideo ) /* switch during the same line */
1133: && ( ShifterFrame.ResPosHi.LineCycles <= LINE_END_CYCLE_71+4 ) /* switched to hi res before cycle 164 */
1134: && ( LineCycles >= LINE_END_CYCLE_71+4 ) ) /* switch to lo res after cycle 164 */
1135: {
1136: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_STOP_MIDDLE;
1137: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_71;
1138: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect stop middle %d<->%d\n" ,
1139: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1140: }
1141:
1142: /* Remove right border a second time after removing it a first time. Display will */
1143: /* stop at cycle 512 instead of 460. */
1144: /* This removes left border on next line too (used in 'Enchanted Lands') */
1.1.1.16 root 1145: /* If right border was not removed, then we will get an empty line for the next HBL (used in Beyond by Kruz) */
1146: if ( ( ShifterFrame.Res == 0x02 ) /* switched from hi res */
1147: && ( LineCycles > LINE_END_CYCLE_50_2 ) /* switch to low just after end of right border */
1148: && ( ShifterFrame.ResPosHi.LineCycles <= LINE_END_CYCLE_50_2 ) /* switch to hi just before end of right border */
1149: && ( FrameCycles - ShifterFrame.ResPosHi.FrameCycles <= 20 ) )
1150: {
1151: if ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_RIGHT_OFF ) /* Enchanted Lands */
1152: {
1153: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_RIGHT_OFF_FULL;
1154: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_FULL;
1155: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= BORDERMASK_LEFT_OFF; /* no left border on next line */
1156: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = LINE_START_CYCLE_71;
1157: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove right full %d<->%d\n" ,
1158: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1159: }
1160: else /* Pax Plax Parralax in Beyond by Kruz */
1161: {
1162: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask = BORDERMASK_EMPTY_LINE;
1163: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = 0;
1164: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = 0;
1165: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res 3 %d<->%d for nHBL=%d\n" ,
1166: ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle , nHBL+1 );
1167: }
1.1.1.15 root 1168: }
1169:
1170: /* If left border is opened and we switch to medium resolution during the next cycles, */
1171: /* then we assume a med res overscan line instead of a low res overscan line. */
1172: /* Note that in that case, the switch to med res can shift the display by 0-3 words */
1.1.1.11 root 1173: /* Used in 'No Cooper' greetings by 1984 and 'Punish Your Machine' by Delta Force */
1.1.1.15 root 1174: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
1175: && ( Res == 0x01 ) )
1.1.1.11 root 1176: {
1.1.1.18 root 1177: if ( ( LineCycles == LINE_LEFT_MED_CYCLE_1 ) /* 'No Cooper' timing */
1178: || ( LineCycles == LINE_LEFT_MED_CYCLE_1+16 ) ) /* 'No Cooper' timing while removing bottom border */
1.1.1.11 root 1179: {
1.1.1.15 root 1180: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect med res overscan offset 0 byte\n" );
1181: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 0 << 20 );
1.1.1.11 root 1182: }
1.1.1.15 root 1183: else if ( LineCycles == LINE_LEFT_MED_CYCLE_2 ) /* 'Best Part Of The Creation / PYM' timing */
1.1.1.11 root 1184: {
1.1.1.15 root 1185: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect med res overscan offset 2 bytes\n" );
1186: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 2 << 20 );
1.1.1.11 root 1187: }
1188: }
1189:
1.1.1.15 root 1190: /* If left border was opened with a hi/med res switch we need to check */
1191: /* if the switch to low res can trigger a right hardware scrolling. */
1192: /* We store the pixels count in DisplayPixelShift */
1193: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF_MED )
1194: && ( Res == 0x00 ) && ( LineCycles <= LINE_SCROLL_1_CYCLE_50 ) )
1.1.1.11 root 1195: {
1.1.1.15 root 1196: /* The hi/med switch was a switch to do low res hardware scrolling, */
1197: /* so we must cancel the med res overscan bit. */
1198: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= (~BORDERMASK_OVERSCAN_MED_RES);
1.1.1.11 root 1199:
1.1.1.15 root 1200: if ( LineCycles == LINE_SCROLL_13_CYCLE_50 ) /* cycle 20 */
1.1.1.11 root 1201: {
1.1.1.15 root 1202: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll\n" );
1203: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
1.1.1.11 root 1204: }
1.1.1.15 root 1205: else if ( LineCycles == LINE_SCROLL_9_CYCLE_50 ) /* cycle 24 */
1.1.1.11 root 1206: {
1.1.1.15 root 1207: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll\n" );
1208: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
1.1.1.11 root 1209: }
1.1.1.15 root 1210: else if ( LineCycles == LINE_SCROLL_5_CYCLE_50 ) /* cycle 28 */
1.1.1.11 root 1211: {
1.1.1.15 root 1212: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll\n" );
1213: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
1.1.1.11 root 1214: }
1.1.1.15 root 1215: else if ( LineCycles == LINE_SCROLL_1_CYCLE_50 ) /* cycle 32 */
1.1.1.11 root 1216: {
1.1.1.15 root 1217: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll\n" );
1218: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
1.1.1.11 root 1219: }
1220: }
1221:
1.1.1.18 root 1222: #define SCROLL2_4PX
1223: #ifdef SCROLL2_4PX
1224: /* Left border was removed with a hi/lo switch, then a med res switch was made */
1225: /* Depending on the low res switch, the screen will be shifted as a low res overscan line */
1226: /* This is a different method than the one used by ST Connexion with only 3 res switches */
1227: /* (so we must cancel the med res overscan bit) */
1228: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_OVERSCAN_MED_RES )
1229: && ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & ( 0xf << 20 ) ) == 0 )
1230: && ( Res == 0x00 ) && ( LineCycles <= 40 ) )
1231: {
1232: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= (~BORDERMASK_OVERSCAN_MED_RES); /* cancel mid res */
1233:
1234: if ( LineCycles == 28 )
1235: {
1236: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll 2\n" );
1237: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
1238: }
1239: else if ( LineCycles == 32 )
1240: {
1241: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll 2\n" );
1242: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
1243: }
1244: else if ( LineCycles == 36 )
1245: {
1246: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll 2\n" );
1247: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
1248: }
1249: else if ( LineCycles == 40 )
1250: {
1251: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll 2\n" );
1252: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
1253: }
1254: }
1255: #endif
1256:
1.1.1.15 root 1257:
1258: /* Update HBL's position only if display has not reached pos LINE_START_CYCLE_50 */
1259: /* and HBL interrupt was already handled at the beginning of this line. */
1260: /* This also changes the number of cycles per line. */
1261: if ( ( LineCycles <= LINE_START_CYCLE_50 ) && ( HblCounterVideo == nHBL ) )
1262: {
1263: nCyclesPerLine = Video_HBL_GetPos();
1264: Video_AddInterruptHBL ( nCyclesPerLine );
1265: }
1266:
1267:
1268: /* Update Timer B's position */
1269: LineTimerBCycle = Video_TimerB_GetPos ( HblCounterVideo );
1270: Video_AddInterruptTimerB ( LineTimerBCycle );
1271:
1272:
1273: ShifterFrame.Res = Res;
1274: if ( Res == 0x02 ) /* high res */
1275: {
1276: ShifterFrame.ResPosHi.VBL = nVBLs;
1277: ShifterFrame.ResPosHi.FrameCycles = FrameCycles;
1278: ShifterFrame.ResPosHi.HBL = HblCounterVideo;
1279: ShifterFrame.ResPosHi.LineCycles = LineCycles;
1280: }
1281: else if ( Res == 0x01 ) /* med res */
1282: {
1283: ShifterFrame.ResPosMed.VBL = nVBLs;
1284: ShifterFrame.ResPosMed.FrameCycles = FrameCycles;
1285: ShifterFrame.ResPosMed.HBL = HblCounterVideo;
1286: ShifterFrame.ResPosMed.LineCycles = LineCycles;
1287: }
1288: else /* low res */
1289: {
1290: ShifterFrame.ResPosLo.VBL = nVBLs;
1291: ShifterFrame.ResPosLo.FrameCycles = FrameCycles;
1292: ShifterFrame.ResPosLo.HBL = HblCounterVideo;
1293: ShifterFrame.ResPosLo.LineCycles = LineCycles;
1294: }
1295: }
1296:
1297:
1298:
1299: /*-----------------------------------------------------------------------*/
1300: /**
1301: * Set some default values for DisplayStartCycle/DisplayEndCycle
1302: * when changing frequency in lo/med res (testing orders are important
1303: * because the line can already have some borders changed).
1304: * This is necessary as some freq changes can modify start/end
1305: * even if they're not made at the exact borders' positions.
1306: * These values will be modified later if some borders are changed.
1307: */
1308: static void Video_Sync_SetDefaultStartEnd ( Uint8 Freq , int HblCounterVideo , int LineCycles )
1309: {
1310: if ( Freq == 0x02 ) /* switch to 50 Hz */
1311: {
1312: if ( ( LineCycles <= ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle ) /* start could be 0,52,56 */
1313: && ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle == LINE_START_CYCLE_60 ) )
1314: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_50;
1315:
1316: if ( ( LineCycles <= ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) /* end could be 160,372,376,460 */
1317: && ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle < LINE_END_CYCLE_50 ) )
1318: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_50;
1319: }
1320:
1321: else /* switch to 60 Hz */
1322: {
1323: if ( LineCycles < ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle ) /* start could be 0,52,56 */
1324: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_60;
1325:
1326: if ( ( LineCycles < ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) /* end could be 160,372,376,460 */
1327: && ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle <= LINE_END_CYCLE_50 ) )
1328: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_60;
1329: }
1330:
1.1.1.21 root 1331: //fprintf ( stderr , "sync default pos %d %d %d\n", HblCounterVideo , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1.1.1.11 root 1332: }
1333:
1334:
1335: /*-----------------------------------------------------------------------*/
1336: /**
1337: * Write to VideoSync (0xff820a), Hz setting
1338: */
1.1.1.15 root 1339: void Video_Sync_WriteByte ( void )
1.1 root 1340: {
1.1.1.15 root 1341: int FrameCycles, HblCounterVideo, LineCycles;
1342: Uint8 Freq;
1.1.1.11 root 1343:
1344:
1.1.1.15 root 1345: if ( bUseVDIRes )
1346: return; /* no 50/60 Hz freq in VDI mode */
1.1.1.11 root 1347:
1348:
1.1.1.15 root 1349: /* We're only interested in bit 1 (50/60Hz) */
1350: Freq = IoMem[0xff820a] & 2;
1.1.1.11 root 1351:
1.1.1.15 root 1352: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1353:
1354: 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",
1355: Freq, FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1356:
1.1.1.13 root 1357: /* Ignore consecutive writes of the same value */
1.1.1.15 root 1358: if ( Freq == ShifterFrame.Freq )
1.1.1.13 root 1359: return; /* do nothing */
1360:
1.1.1.15 root 1361: /* Ignore freq changes if we are in high res */
1362: /* 2009/04/26 : don't ignore for now (see ST Cnx in Punish Your Machine) */
1363: // if ( ShifterFrame.Res == 0x02 )
1364: // return; /* do nothing */
1365:
1366: /* Set some default values for DisplayStartCycle/DisplayEndCycle before checking for border removal */
1367: Video_Sync_SetDefaultStartEnd ( Freq , HblCounterVideo , LineCycles );
1368:
1369:
1370: if ( ( ShifterFrame.Freq == 0x00 ) && ( Freq == 0x02 ) /* switched from 60 Hz to 50 Hz ? */
1371: // && ( ShifterFrame.FreqPos60.VBL == nVBLs ) /* switched during the same VBL */
1372: && ( HblCounterVideo >= nStartHBL ) /* only if display is on */
1.1.1.18 root 1373: && ( HblCounterVideo < nEndHBL + BlankLines ) ) /* only if display is on */
1.1.1.15 root 1374: {
1.1.1.18 root 1375: /* Blank line switching freq on STF : switch to 60 Hz on cycle 28, then go back to 50 Hz on cycle 36 */
1376: /* This creates a blank line where no signal is displayed, but the video counter will still change for this line */
1377: /* This blank line can be combined with left/right border changes */
1378: if ( ( FrameCycles - ShifterFrame.FreqPos60.FrameCycles <= 16 )
1379: && ( ShifterFrame.FreqPos60.LineCycles == LINE_EMPTY_CYCLE_71_STF )
1380: && ( ConfigureParams.System.nMachineType == MACHINE_ST ) )
1381: {
1382: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_BLANK_LINE;
1.1.1.22 root 1383: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line freq stf\n" );
1.1.1.18 root 1384: }
1385:
1.1.1.15 root 1386: /* Add 2 bytes to left border : switch to 60 Hz before LINE_START_CYCLE_60 to force an early start */
1387: /* of the DE signal, then go back to 50 Hz. Note that depending on where the 50 Hz switch is made */
1388: /* the HBL signal will be at position 508 (60 Hz line) or 512 (50 Hz line) */
1.1.1.22 root 1389: /* Obtaining a +2 line with 512 cycles requires a 2 cycles precision and is "wake up" state dependent : */
1390: /* - On STF, switch must be on cycles 36/56 or 36/54 (depending on wake up state) */
1391: /* - On STE, switch can be on cycles 36/56 or 36/54 (no wake up state in STE) */
1392: /* TODO : we should change HBL signal to be on cycles 508 or 512 (it will always be 512 for now) */
1.1.1.15 root 1393: if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle == LINE_START_CYCLE_60 )
1394: && ( LineCycles >= LINE_START_CYCLE_50 ) /* The line started in 60 Hz and continues in 50 Hz */
1395: && ( LineCycles <= ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) ) /* change when line is active */
1.1.1.11 root 1396: {
1.1.1.22 root 1397: /* [FIXME] 'Panic' by Paulo Simoes, dont' trigger left+2 (need 2 cycles precision) */
1398: /* The switch to 50 Hz on line 34 cycle 56 should just start a normal 50 Hz line, not a left+2 */
1399: /* For now, we detect that we're running 'Panic' and if so we don't do left+2 (ugly hack...) */
1400: if ( ( STMemory_ReadLong ( M68000_GetPC() ) == 0x4e7352b8 )
1401: && ( STMemory_ReadLong ( M68000_GetPC()+4 ) == 0x04664e73 )
1402: && ( HblCounterVideo == 34 ) && ( LineCycles == 56 ) )
1403: {
1404: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_50;
1405: }
1.1.1.23! root 1406: /* Same for WinUAE's cpu core : GetPC() points to the current instr, not to the next one */
! 1407: if ( ( STMemory_ReadLong ( M68000_GetPC()+2 ) == 0x4e7352b8 )
! 1408: && ( STMemory_ReadLong ( M68000_GetPC()+4+2 ) == 0x04664e73 )
! 1409: && ( HblCounterVideo == 34 ) && ( LineCycles == 56 ) )
! 1410: {
! 1411: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_50;
! 1412: }
! 1413:
! 1414: /* [FIXME] 'Gen 4 Demo' by Ziggy Stardust / OVR. Same problem as 'Panic' above */
! 1415: else if ( ( STMemory_ReadLong ( M68000_GetPC()-4 ) == 0x0002820a )
! 1416: && ( STMemory_ReadLong ( M68000_GetPC()+6 ) == 0x10388209 )
! 1417: && ( HblCounterVideo == 34 ) && ( LineCycles == 56 ) )
! 1418: {
! 1419: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = LINE_START_CYCLE_50;
! 1420: }
1.1.1.22 root 1421:
1422: /* Normal case where left+2 should be made */
1423: else
1424: {
1425: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_LEFT_PLUS_2;
1426: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_50;
1427: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 %d<->%d\n" ,
1.1.1.15 root 1428: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1.1.1.22 root 1429: }
1.1.1.11 root 1430: }
1431:
1.1.1.22 root 1432: /* Empty line switching freq on STF : start the line in 50 Hz, change to 60 Hz at the exact place */
1.1.1.15 root 1433: /* where display is enabled in 50 Hz, then go back to 50 Hz. */
1434: /* Due to 4 cycles precision instead of 2, we must accept a 60 Hz switch at pos 56 or 56+4 */
1435: else if ( ( FrameCycles - ShifterFrame.FreqPos60.FrameCycles <= 24 )
1436: && ( ( ShifterFrame.FreqPos60.LineCycles == LINE_START_CYCLE_50 ) || ( ShifterFrame.FreqPos60.LineCycles == LINE_START_CYCLE_50+4 ) )
1.1.1.22 root 1437: && ( LineCycles > LINE_START_CYCLE_50 )
1438: && ( ConfigureParams.System.nMachineType == MACHINE_ST ) )
1439: {
1440: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_EMPTY_LINE;
1441: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = 0;
1442: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = 0;
1443: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line freq stf %d<->%d\n" ,
1444: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1445: }
1446:
1447: /* Empty line switching freq on STE : similar to STF above, but doesn't require a 2 cycles precision */
1448: /* The switches are made at cycles 40/52 */
1449: else if ( ( FrameCycles - ShifterFrame.FreqPos60.FrameCycles <= 24 )
1450: && ( ShifterFrame.FreqPos60.LineCycles == 40 )
1451: && ( LineCycles == LINE_START_CYCLE_60 )
1452: && ( ConfigureParams.System.nMachineType == MACHINE_STE ) )
1.1.1.15 root 1453: {
1454: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_EMPTY_LINE;
1455: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = 0;
1456: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = 0;
1.1.1.22 root 1457: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line freq ste %d<->%d\n" ,
1.1.1.15 root 1458: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1.1.1.11 root 1459: }
1.1.1.15 root 1460:
1.1.1.22 root 1461:
1.1.1.15 root 1462: /* Remove 2 bytes to the right : start the line in 50 Hz (pos 0 or 56), change to 60 Hz before the position */
1463: /* where display is disabled in 60 Hz, then go back to 50 Hz */
1464: if ( ( LineCycles > LINE_END_CYCLE_60 ) /* back to 50 Hz after end of 60 Hz line */
1465: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle != LINE_START_CYCLE_60 ) /* start could be 0 or 56 */
1466: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle == LINE_END_CYCLE_60 ) )
1467: {
1468: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_RIGHT_MINUS_2;
1469: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" ,
1470: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1471: }
1472:
1.1.1.11 root 1473: }
1474:
1.1.1.15 root 1475:
1476: if ( ( ShifterFrame.Freq == 0x02 && Freq == 0x00 ) /* switched from 50 Hz to 60 Hz ? */
1477: && ( HblCounterVideo >= nStartHBL ) /* only if display is on */
1.1.1.18 root 1478: && ( HblCounterVideo < nEndHBL + BlankLines ) ) /* only if display is on */
1.1.1.15 root 1479: {
1480: /* remove right border, display 44 bytes more : switch to 60 Hz at the position where */
1.1.1.21 root 1481: /* the line ends in 50 Hz. Some programs don't switch back to 50 Hz immediately */
1.1.1.15 root 1482: /* (sync screen in SNY II), so we just check if freq changes to 60 Hz at the position where line should end in 50 Hz */
1483: if ( ( LineCycles == LINE_END_CYCLE_50 )
1484: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle == LINE_END_CYCLE_50 ) )
1485: {
1486: ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_RIGHT_OFF;
1.1.1.16 root 1487: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = LINE_END_CYCLE_NO_RIGHT;
1.1.1.15 root 1488: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove right %d<->%d\n" ,
1489: ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
1.1.1.11 root 1490: }
1491: }
1492:
1493:
1494: /* Store cycle position of freq 50/60 to check for top/bottom border removal in Video_EndHBL. */
1.1.1.15 root 1495: if ( Freq == 0x02 ) /* switch to 50 Hz */
1.1.1.11 root 1496: {
1.1.1.13 root 1497: if ( ( HblCounterVideo < VIDEO_START_HBL_50HZ ) /* nStartHBL can change only if display is not ON yet */
1498: && ( OverscanMode & OVERSCANMODE_TOP ) == 0 ) /* update only if top was not removed */
1499: nStartHBL = VIDEO_START_HBL_50HZ;
1.1.1.11 root 1500:
1.1.1.13 root 1501: if ( ( HblCounterVideo < VIDEO_END_HBL_50HZ ) /* nEndHBL can change only if display is not OFF yet */
1.1.1.11 root 1502: && ( OverscanMode & OVERSCANMODE_BOTTOM ) == 0 ) /* update only if bottom was not removed */
1.1.1.13 root 1503: nEndHBL = VIDEO_END_HBL_50HZ; /* 263 */
1.1.1.11 root 1504: }
1.1.1.15 root 1505: else if ( Freq == 0x00 ) /* switch to 60 Hz */
1.1.1.11 root 1506: {
1.1.1.13 root 1507: if ( ( HblCounterVideo < VIDEO_START_HBL_60HZ-1 ) /* nStartHBL can change only if display is not ON yet */
1.1.1.15 root 1508: || ( ( HblCounterVideo == VIDEO_START_HBL_60HZ-1 ) && ( LineCycles <= LineRemoveTopCycle ) ) )
1.1.1.13 root 1509: nStartHBL = VIDEO_START_HBL_60HZ;
1.1.1.11 root 1510:
1.1.1.13 root 1511: if ( ( HblCounterVideo < VIDEO_END_HBL_60HZ ) /* nEndHBL can change only if display is not OFF yet */
1.1.1.11 root 1512: && ( OverscanMode & OVERSCANMODE_BOTTOM ) == 0 ) /* update only if bottom was not removed */
1.1.1.13 root 1513: nEndHBL = VIDEO_END_HBL_60HZ; /* 234 */
1514: }
1515:
1.1.1.15 root 1516:
1.1.1.13 root 1517: /* If the frequence changed, we need to update the EndLine interrupt */
1.1.1.21 root 1518: /* so that it happens TIMERB_VIDEO_CYCLE_OFFSET cycles after the current DisplayEndCycle.*/
1.1.1.13 root 1519: /* We check if the change affects the current line or the next one. */
1.1.1.15 root 1520: /* We also need to check if the HBL interrupt and nCyclesPerLine need */
1521: /* to be updated first. */
1522: if ( Freq != ShifterFrame.Freq )
1523: {
1524: /* Update HBL's position only if display has not reached pos LINE_START_CYCLE_50 */
1525: /* and HBL interrupt was already handled at the beginning of this line. */
1526: /* This also changes the number of cycles per line. */
1527: if ( ( LineCycles <= LINE_START_CYCLE_50 ) && ( HblCounterVideo == nHBL ) )
1528: {
1.1.1.22 root 1529: int CyclesPerLine_old = nCyclesPerLine;
1530:
1.1.1.15 root 1531: nCyclesPerLine = Video_HBL_GetPos();
1532: Video_AddInterruptHBL ( nCyclesPerLine );
1.1.1.22 root 1533:
1534: /* In case we're mixing 50 Hz (512 cycles) and 60 Hz (508 cycles) lines on the same screen, */
1535: /* we must update the position where the next VBL will happen (instead of the initial value in CyclesPerVBL) */
1536: /* We check if number of cycles per line changes, and if so, we update the VBL's position */
1537: if ( CyclesPerLine_old != nCyclesPerLine )
1538: {
1539: CyclesPerVBL += ( nCyclesPerLine - CyclesPerLine_old ); /* +4 or -4 */
1540: CycInt_ModifyInterrupt ( nCyclesPerLine - CyclesPerLine_old , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL );
1541: }
1.1.1.15 root 1542: }
1543:
1544: /* Update Timer B's position */
1545: LineTimerBCycle = Video_TimerB_GetPos ( HblCounterVideo );
1546: Video_AddInterruptTimerB ( LineTimerBCycle );
1547: }
1548:
1549:
1550: ShifterFrame.Freq = Freq;
1551: if ( Freq == 0x02 ) /* 50 Hz */
1552: {
1553: ShifterFrame.FreqPos50.VBL = nVBLs;
1554: ShifterFrame.FreqPos50.FrameCycles = FrameCycles;
1555: ShifterFrame.FreqPos50.HBL = HblCounterVideo;
1556: ShifterFrame.FreqPos50.LineCycles = LineCycles;
1557: }
1558: else
1559: {
1560: ShifterFrame.FreqPos60.VBL = nVBLs;
1561: ShifterFrame.FreqPos60.FrameCycles = FrameCycles;
1562: ShifterFrame.FreqPos60.HBL = HblCounterVideo;
1563: ShifterFrame.FreqPos60.LineCycles = LineCycles;
1564: }
1565: }
1566:
1567:
1568: /*-----------------------------------------------------------------------*/
1569: /**
1570: * Compute the cycle position where the HBL should happen on each line.
1571: * In low/med res, the position depends on the video frequency (50/60 Hz)
1572: * In high res, the position is always the same.
1573: * This position also gives the number of CPU cycles per video line.
1574: */
1575: static int Video_HBL_GetPos ( void )
1576: {
1577: int Pos;
1578:
1579: if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 ) /* hi res */
1580: Pos = CYCLES_PER_LINE_71HZ;
1581:
1582: else /* low res or med res */
1583: {
1584: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz, pos 512 */
1585: Pos = CYCLES_PER_LINE_50HZ;
1586: else /* 60 Hz, pos 508 */
1587: Pos = CYCLES_PER_LINE_60HZ;
1588: }
1589:
1590: return Pos;
1591: }
1592:
1593:
1594: /*-----------------------------------------------------------------------*/
1595: /**
1596: * Compute the cycle position where the timer B should happen on each
1597: * visible line.
1598: * We compute Timer B position for the given LineNumber, using start/end
1599: * display cycles from ShifterLines[ LineNumber ].
1600: * The position depends on the start of line / end of line positions
1601: * (which depend on the current frequency / border tricks) and
1602: * on the value of the bit 3 in the MFP's AER.
1603: * If bit is 0, timer B will count end of line events (usual case),
1604: * but if bit is 1, timer B will count start of line events (eg Seven Gates Of Jambala)
1605: */
1606: int Video_TimerB_GetPos ( int LineNumber )
1607: {
1608: int Pos;
1609:
1610: if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) /* we're counting end of line events */
1611: {
1612: Pos = ShifterFrame.ShifterLines[ LineNumber ].DisplayEndCycle + TIMERB_VIDEO_CYCLE_OFFSET;
1613: }
1614: else /* we're counting start of line events */
1615: {
1616: Pos = ShifterFrame.ShifterLines[ LineNumber ].DisplayStartCycle + TIMERB_VIDEO_CYCLE_OFFSET;
1617: }
1618:
1.1.1.18 root 1619: //fprintf ( stderr , "timerb pos=%d\n" , Pos );
1620: return Pos;
1621: }
1622:
1623:
1624: /*-----------------------------------------------------------------------*/
1625: /**
1626: * Compute the default cycle position where the timer B should happen
1627: * on the next line, when restarting the INTERRUPT_VIDEO_ENDLINE handler.
1628: * In low/med res, the position depends on the video frequency (50/60 Hz)
1629: * In high res, the position is always the same.
1630: */
1.1.1.23! root 1631: static int Video_TimerB_GetDefaultPos ( void )
1.1.1.18 root 1632: {
1633: int Pos;
1634:
1635: if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) /* we're counting end of line events */
1636: {
1637: if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 ) /* hi res */
1638: Pos = LINE_END_CYCLE_71;
1639:
1640: else /* low res or med res */
1641: {
1642: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz, pos 376 */
1643: Pos = LINE_END_CYCLE_50;
1644: else /* 60 Hz, pos 372 */
1645: Pos = LINE_END_CYCLE_60;
1646: }
1647: }
1648: else /* we're counting start of line events */
1649: {
1650: if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 ) /* hi res */
1651: Pos = LINE_START_CYCLE_71;
1652:
1653: else /* low res or med res */
1654: {
1655: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz, pos 56 */
1656: Pos = LINE_START_CYCLE_50;
1657: else /* 60 Hz, pos 52 */
1658: Pos = LINE_START_CYCLE_60;
1659: }
1660: }
1661:
1662: Pos += TIMERB_VIDEO_CYCLE_OFFSET;
1663:
1664: //fprintf ( stderr , "timerb default pos=%d\n" , Pos );
1.1.1.15 root 1665: return Pos;
1666: }
1667:
1668:
1669: /*-----------------------------------------------------------------------*/
1670: /**
1671: * HBL interrupt : this occurs at the end of every line, on cycle 512 (in 50 Hz)
1672: * It takes 56 cycles to handle the 68000's exception.
1673: */
1674: void Video_InterruptHandler_HBL ( void )
1675: {
1676: int FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
1677: int PendingCyclesOver;
1678: int NewHBLPos;
1679:
1680: /* How many cycle was this HBL delayed (>= 0) */
1681: PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
1682:
1683: /* Remove this interrupt from list and re-order */
1.1.1.16 root 1684: CycInt_AcknowledgeInterrupt();
1.1.1.15 root 1685:
1.1.1.17 root 1686: /* Videl Vertical counter increment (To be removed when Videl emulation is finished) */
1687: /* VFC is incremented every half line, here, we increment it every line (should be completed) */
1688: if (ConfigureParams.System.nMachineType == MACHINE_FALCON) {
1689: vfc_counter += 1;
1690: }
1691:
1692:
1.1.1.15 root 1693: /* Increment the hbl jitter index */
1694: HblJitterIndex++;
1.1.1.20 root 1695: HblJitterIndex %= HBL_JITTER_ARRAY_SIZE;
1.1.1.15 root 1696:
1697: LOG_TRACE ( TRACE_VIDEO_HBL , "HBL %d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
1698: nHBL , FrameCycles , PendingCyclesOver , HblJitterArray[ HblJitterIndex ] );
1699:
1700: /* Default cycle position for next HBL */
1701: NewHBLPos = Video_HBL_GetPos();
1702:
1703: /* Generate new HBL, if need to - there are 313 HBLs per frame in 50 Hz */
1704: if (nHBL < nScanlinesPerFrame-1)
1705: Video_AddInterruptHBL ( NewHBLPos );
1706:
1.1.1.22 root 1707:
1708: /* In case we're mixing 50 Hz (512 cycles) and 60 Hz (508 cycles) lines on the same screen, */
1709: /* we must update the position where the next VBL will happen (instead of the initial value in CyclesPerVBL) */
1710: /* During a 50 Hz screen, each 60 Hz line will make the VBL happen 4 cycles earlier */
1711: if ( ( nScanlinesPerFrame == SCANLINES_PER_FRAME_50HZ )
1712: && ( NewHBLPos == CYCLES_PER_LINE_60HZ ) )
1713: {
1714: CyclesPerVBL -= 4;
1715: CycInt_ModifyInterrupt ( -4 , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL );
1716: }
1717: /* During a 60 Hz screen, each 50 Hz line will make the VBL happen 4 cycles later */
1718: else if ( ( nScanlinesPerFrame == SCANLINES_PER_FRAME_60HZ )
1719: && ( NewHBLPos == CYCLES_PER_LINE_50HZ ) )
1720: {
1721: CyclesPerVBL += 4;
1722: CycInt_ModifyInterrupt ( 4 , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL );
1723: }
1724:
1725:
1.1.1.21 root 1726: /* Print traces if pending HBL bit changed just before IACK when HBL interrupt is allowed */
1727: if ( ( CPU_IACK == true ) && ( regs.intmask < 2 ) )
1.1.1.15 root 1728: {
1.1.1.21 root 1729: if ( pendingInterrupts & ( 1 << 2 ) )
1730: {
1731: 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" ,
1732: nHBL , nVBLs , FrameCycles , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
1733: }
1734: else
1735: {
1736: 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" ,
1737: nHBL , nVBLs , FrameCycles , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
1738: }
1.1.1.15 root 1739: }
1740:
1.1.1.21 root 1741: /* Set pending bit for HBL interrupt in the CPU IPL */
1.1.1.23! root 1742: M68000_Exception(EXCEPTION_NR_HBLANK , M68000_EXC_SRC_AUTOVEC); /* Horizontal blank interrupt, level 2 */
1.1.1.21 root 1743:
1.1.1.15 root 1744:
1745: Video_EndHBL(); /* Check some borders removal and copy line to display buffer */
1746:
1.1.1.17 root 1747: DmaSnd_STE_HBL_Update(); /* Update STE DMA sound if needed */
1748:
1.1.1.22 root 1749: /* TEMP IPF */
1750: IPF_Emulate();
1751: /* TEMP IPF */
1752:
1.1.1.15 root 1753: nHBL++; /* Increase HBL count */
1754:
1755: if (nHBL < nScanlinesPerFrame)
1756: {
1757: /* Update start cycle for next HBL */
1758: ShifterFrame.ShifterLines[ nHBL ].StartCycle = FrameCycles - PendingCyclesOver;
1759: LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d start=%d %x\n", nHBL,
1760: ShifterFrame.ShifterLines[nHBL].StartCycle, ShifterFrame.ShifterLines[nHBL].StartCycle);
1761:
1762: /* Setup next HBL */
1763: Video_StartHBL();
1764: }
1765: }
1766:
1767:
1768: /*-----------------------------------------------------------------------*/
1769: /**
1770: * Check at end of each HBL to see if any Shifter hardware tricks have been attempted
1771: * and copy the line to the screen buffer.
1772: * This is the place to check if top/bottom border were removed, as well as if some
1773: * left/right border changes were not validated before.
1774: * NOTE : the tests must be made with nHBL in ascending order.
1775: */
1776: static void Video_EndHBL(void)
1777: {
1778: //
1779: // Handle top/bottom borders removal when switching freq
1780: //
1781:
1782: /* Remove top border if the switch to 60 Hz was made during this vbl before cycle */
1.1.1.21 root 1783: /* LineRemoveTopCycle on line 33 and if the switch to 50 Hz has not yet occurred or */
1784: /* occurred before the 60 Hz or occurred after cycle LineRemoveTopCycle on line 33. */
1.1.1.15 root 1785: if ( ( nHBL == VIDEO_START_HBL_60HZ-1 ) /* last HBL before first line of a 60 Hz screen */
1786: && ( ShifterFrame.FreqPos60.VBL == nVBLs ) /* switch to 60 Hz during this VBL */
1.1.1.20 root 1787: && ( ( ShifterFrame.FreqPos60.HBL < nHBL )
1788: || ( ( ShifterFrame.FreqPos60.HBL == nHBL ) && ( ShifterFrame.FreqPos60.LineCycles <= LineRemoveTopCycle ) ) )
1.1.1.15 root 1789: && ( ( ShifterFrame.FreqPos50.VBL < nVBLs )
1790: || ( ShifterFrame.FreqPos50.FrameCycles < ShifterFrame.FreqPos60.FrameCycles )
1791: || ( ShifterFrame.FreqPos50.HBL > nHBL )
1.1.1.21 root 1792: || ( ( ShifterFrame.FreqPos50.HBL == nHBL ) && ( ShifterFrame.FreqPos50.LineCycles > LineRemoveTopCycle ) ) ) )
1.1.1.15 root 1793: {
1794: /* Top border */
1795: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove top\n" );
1796: OverscanMode |= OVERSCANMODE_TOP; /* Set overscan bit */
1797: nStartHBL = VIDEO_START_HBL_60HZ; /* New start screen line */
1798: pHBLPaletteMasks -= OVERSCAN_TOP; // FIXME useless ?
1799: pHBLPalettes -= OVERSCAN_TOP; // FIXME useless ?
1800: }
1801:
1802: /* Remove bottom border for a 60 Hz screen (tests are similar to the ones for top border) */
1.1.1.18 root 1803: else if ( ( nHBL == VIDEO_END_HBL_60HZ + BlankLines - 1 ) /* last displayed line in 60 Hz */
1.1.1.15 root 1804: && ( nStartHBL == VIDEO_START_HBL_60HZ ) /* screen started in 60 Hz */
1805: && ( ( OverscanMode & OVERSCANMODE_TOP ) == 0 ) /* and top border was not removed : this screen is only 60 Hz */
1806: && ( ShifterFrame.FreqPos50.VBL == nVBLs ) /* switch to 50 Hz during this VBL */
1.1.1.20 root 1807: && ( ( ShifterFrame.FreqPos50.HBL < nHBL )
1.1.1.22 root 1808: || ( ( ShifterFrame.FreqPos50.HBL == nHBL ) && ( ShifterFrame.FreqPos50.LineCycles <= LineRemoveBottomCycle-4 ) ) )
1.1.1.15 root 1809: && ( ( ShifterFrame.FreqPos60.VBL < nVBLs )
1810: || ( ShifterFrame.FreqPos60.FrameCycles < ShifterFrame.FreqPos50.FrameCycles )
1811: || ( ShifterFrame.FreqPos60.HBL > nHBL )
1.1.1.22 root 1812: || ( ( ShifterFrame.FreqPos60.HBL == nHBL ) && ( ShifterFrame.FreqPos60.LineCycles > LineRemoveBottomCycle-4 ) ) ) )
1.1.1.15 root 1813: {
1814: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove bottom 60Hz\n" );
1815: OverscanMode |= OVERSCANMODE_BOTTOM;
1816: nEndHBL = SCANLINES_PER_FRAME_60HZ; /* new end for a 60 Hz screen */
1817: }
1818:
1819: /* Remove bottom border for a 50 Hz screen (tests are similar to the ones for top border) */
1.1.1.18 root 1820: else if ( ( nHBL == VIDEO_END_HBL_50HZ + BlankLines - 1 ) /* last displayed line in 50 Hz */
1.1.1.15 root 1821: && ( ( OverscanMode & OVERSCANMODE_BOTTOM ) == 0 ) /* border was not already removed at line VIDEO_END_HBL_60HZ-1 */
1822: && ( ShifterFrame.FreqPos60.VBL == nVBLs ) /* switch to 60 Hz during this VBL */
1.1.1.20 root 1823: && ( ( ShifterFrame.FreqPos60.HBL < nHBL )
1824: || ( ( ShifterFrame.FreqPos60.HBL == nHBL ) && ( ShifterFrame.FreqPos60.LineCycles <= LineRemoveBottomCycle ) ) )
1.1.1.15 root 1825: && ( ( ShifterFrame.FreqPos50.VBL < nVBLs )
1826: || ( ShifterFrame.FreqPos50.FrameCycles < ShifterFrame.FreqPos60.FrameCycles )
1827: || ( ShifterFrame.FreqPos50.HBL > nHBL )
1.1.1.21 root 1828: || ( ( ShifterFrame.FreqPos50.HBL == nHBL ) && ( ShifterFrame.FreqPos50.LineCycles > LineRemoveBottomCycle ) ) ) )
1.1.1.13 root 1829: {
1.1.1.15 root 1830: LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove bottom\n" );
1831: OverscanMode |= OVERSCANMODE_BOTTOM;
1832: nEndHBL = VIDEO_END_HBL_50HZ+VIDEO_HEIGHT_BOTTOM_50HZ; /* new end for a 50 Hz screen */
1833: }
1834:
1835:
1836: //
1837: // Check some left/right borders effects that were not detected earlier
1838: // (this is usually due to staying in 60 Hz for too long, which is often a bad
1839: // coding practice as it can distort the display on a real ST)
1840: //
1841:
1842: /* Special case when the line was not started in 60 Hz, then switched to 60 Hz */
1843: /* and was not restored to 50 Hz before the end of the line. In that case, the */
1844: /* line ends 2 bytes earlier on the right (line can start at LINE_START_CYCLE_71/50) */
1845: /* Some programs also turn to 60 Hz too early during the active display of the last */
1846: /* line to remove the bottom border (FNIL by TNT), in that case, we should also remove */
1847: /* 2 bytes to this line */
1848: if ( ( ( ShifterFrame.ShifterLines[ nHBL ].BorderMask & BORDERMASK_RIGHT_MINUS_2 ) == 0 )
1849: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle != LINE_START_CYCLE_60 ) /* start could be 0 or 56 */
1850: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle == LINE_END_CYCLE_60 ) )
1851: {
1852: ShifterFrame.ShifterLines[ nHBL ].BorderMask |= BORDERMASK_RIGHT_MINUS_2;
1853: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect late right-2 %d<->%d\n" ,
1854: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
1855: }
1856:
1857:
1858: /* Similar case when line started in 60 Hz but did not end at the usual LINE_END_CYCLE_60 position */
1859: /* (line can end at LINE_END_CYCLE_71/50 or have right border removed) */
1860: /* This means left border had 2 bytes more to display */
1861: if ( ( ( ShifterFrame.ShifterLines[ nHBL ].BorderMask & BORDERMASK_LEFT_PLUS_2 ) == 0 )
1862: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == LINE_START_CYCLE_60 )
1863: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle != LINE_END_CYCLE_60 ) ) /* end could be 160, 372 or 460 */
1864: {
1865: ShifterFrame.ShifterLines[ nHBL ].BorderMask |= BORDERMASK_LEFT_PLUS_2;
1866: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect late left+2 %d<->%d\n" ,
1867: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
1868: }
1869:
1870:
1871: /* Although a 'left+2' was detected earlier, the freq was switched back to 60 Hz during DE, so the line is just */
1872: /* a normal 60 Hz line ; we must cancel the 'left+2' flag */
1873: else if ( ( ShifterFrame.ShifterLines[ nHBL ].BorderMask & BORDERMASK_LEFT_PLUS_2 )
1874: && ( ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle == LINE_END_CYCLE_60 ) )
1875: {
1876: ShifterFrame.ShifterLines[ nHBL ].BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
1877: LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel late left+2 %d<->%d\n" ,
1878: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
1879: }
1880:
1881:
1.1.1.13 root 1882:
1.1.1.15 root 1883: /* Store palette for very first line on screen - HBLPalettes[0] */
1884: if (nHBL == nFirstVisibleHbl-1)
1885: {
1886: /* Store ALL palette for this line into raster table for datum */
1887: Video_StoreFirstLinePalette();
1888: }
1889:
1890: if (bUseHighRes)
1891: {
1892: /* Copy for hi-res (no overscan) */
1893: if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
1894: Video_CopyScreenLineMono();
1895: }
1896: /* Are we in possible visible color display (including borders)? */
1897: else if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
1898: {
1899: /* Store resolution for every line so can check for mix low/med screens */
1900: Video_StoreResolution(nHBL-nFirstVisibleHbl);
1901:
1902: /* Copy line of screen to buffer to simulate TV raster trace
1903: * - required for mouse cursor display/game updates
1904: * Eg, Lemmings and The Killing Game Show are good examples */
1905: Video_CopyScreenLineColor();
1906: }
1907: }
1908:
1909:
1910: /*-----------------------------------------------------------------------*/
1911: /**
1912: * Set default values for the next HBL, depending on the current res/freq.
1913: * We set the number of cycles per line, as well as some default values
1914: * for display start/end cycle.
1915: */
1916: static void Video_StartHBL(void)
1917: {
1.1.1.19 root 1918: if ((IoMem_ReadByte(0xff8260) & 3) == 2) /* hi res */
1.1.1.15 root 1919: {
1920: nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1921: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = LINE_START_CYCLE_71;
1922: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = LINE_END_CYCLE_71;
1923: }
1924: else
1925: {
1926: if ( IoMem_ReadByte ( 0xff820a ) & 2 ) /* 50 Hz */
1927: {
1928: nCyclesPerLine = CYCLES_PER_LINE_50HZ;
1929: if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 ) /* start not set yet */
1930: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = LINE_START_CYCLE_50;
1931: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = LINE_END_CYCLE_50;
1932: }
1933: else /* 60 Hz */
1934: {
1935: nCyclesPerLine = CYCLES_PER_LINE_60HZ;
1936: if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 ) /* start not set yet */
1937: ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = LINE_START_CYCLE_60;
1938: ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = LINE_END_CYCLE_60;
1939: }
1940: }
1.1.1.21 root 1941: //fprintf (stderr , "Video_StartHBL %d %d %d\n", nHBL , ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
1.1.1.15 root 1942: }
1943:
1944:
1945: /*-----------------------------------------------------------------------*/
1946: /**
1947: * End Of Line interrupt
1948: * This interrupt is started on cycle position 404 in 50 Hz and on cycle
1949: * position 400 in 60 Hz. 50 Hz display ends at cycle 376 and 60 Hz displays
1.1.1.21 root 1950: * ends at cycle 372. This means the EndLine interrupt happens 24 cycles
1.1.1.15 root 1951: * after DisplayEndCycle.
1952: * Note that if bit 3 of MFP AER is 1, then timer B will count start of line
1.1.1.21 root 1953: * instead of end of line (at cycle 52+24 or 56+24)
1.1.1.15 root 1954: */
1955: void Video_InterruptHandler_EndLine(void)
1956: {
1957: int FrameCycles, HblCounterVideo, LineCycles;
1958: int PendingCycles = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
1959:
1960: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1961:
1962: LOG_TRACE ( TRACE_VIDEO_HBL , "EndLine TB %d video_cyc=%d line_cyc=%d pending_int_cnt=%d\n" ,
1963: nHBL , FrameCycles , LineCycles , PendingCycles );
1964:
1965: /* Remove this interrupt from list and re-order */
1.1.1.16 root 1966: CycInt_AcknowledgeInterrupt();
1.1.1.15 root 1967:
1968: /* Ignore HBLs in VDI mode */
1969: if (bUseVDIRes)
1970: return;
1971:
1972: /* Generate new Endline, if need to - there are 313 HBLs per frame */
1973: if (nHBL < nScanlinesPerFrame-1)
1974: {
1.1.1.21 root 1975: /* By default, next EndLine's int will be on line nHBL+1 at pos 376+24 or 372+24 */
1.1.1.15 root 1976: if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) /* count end of line */
1.1.1.13 root 1977: {
1.1.1.15 root 1978: /* If EndLine int is delayed too much (more than 100 cycles), nLineCycles will */
1979: /* be in the range 0..xxx instead of 400..512. In that case, we need to add */
1980: /* nCyclesPerLine to be in the range 512..x+512 */
1981: /* Maximum possible delay should be around 160 cycles on STF (DIVS) */
1982: /* In that case, HBL int will be delayed too, so we will have HblCounterVideo == nHBL+1 */
1983: if ( HblCounterVideo == nHBL+1 ) /* int happened in fact on the next line nHBL+1 */
1984: LineCycles += nCyclesPerLine;
1.1.1.13 root 1985:
1.1.1.18 root 1986: LineTimerBCycle = Video_TimerB_GetDefaultPos ();
1.1.1.13 root 1987: }
1988:
1.1.1.15 root 1989: else /* count start of line, no possible delay to handle */
1.1.1.13 root 1990: {
1.1.1.18 root 1991: LineTimerBCycle = Video_TimerB_GetDefaultPos ();
1.1.1.13 root 1992: }
1993:
1.1.1.15 root 1994: //fprintf ( stderr , "new tb %d %d %d\n" , LineTimerBCycle , nCyclesPerLine , LineTimerBCycle - LineCycles + nCyclesPerLine );
1.1.1.16 root 1995: CycInt_AddRelativeInterrupt ( LineTimerBCycle - LineCycles + nCyclesPerLine,
1.1.1.15 root 1996: INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
1.1.1.11 root 1997: }
1998:
1.1.1.15 root 1999: /* Timer B occurs at END of first visible screen line in Event Count mode */
1.1.1.18 root 2000: if ( ( nHBL >= nStartHBL ) && ( nHBL < nEndHBL + BlankLines ) )
1.1.1.11 root 2001: {
1.1.1.15 root 2002: /* Handle Timer B when using Event Count mode */
2003: /* We must ensure that the write to fffa1b to activate timer B was */
2004: /* completed before the point where the end of line signal was generated */
2005: /* (in the case of a move.b #8,$fffa1b that would happen 4 cycles */
2006: /* before end of line, the interrupt should not be generated) */
2007: if ( (MFP_TBCR == 0x08) /* Is timer in Event Count mode ? */
2008: && ( ( TimerBEventCountCycleStart == -1 ) /* timer B was started during a previous VBL */
2009: || ( TimerBEventCountCycleStart < FrameCycles-PendingCycles ) ) ) /* timer B was started before this possible interrupt */
1.1.1.21 root 2010: MFP_TimerB_EventCount_Interrupt ( PendingCycles ); /* we have a valid timer B interrupt */
1.1.1.11 root 2011: }
1.1 root 2012: }
2013:
1.1.1.2 root 2014:
1.1.1.15 root 2015:
2016:
1.1.1.2 root 2017: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2018: /**
2019: * Store whole palette on first line so have reference to work from
2020: */
1.1.1.7 root 2021: static void Video_StoreFirstLinePalette(void)
1.1 root 2022: {
1.1.1.11 root 2023: Uint16 *pp2;
2024: int i;
2025:
2026: pp2 = (Uint16 *)&IoMem[0xff8240];
2027: for (i = 0; i < 16; i++)
1.1.1.22 root 2028: {
1.1.1.11 root 2029: HBLPalettes[i] = SDL_SwapBE16(*pp2++);
1.1.1.22 root 2030: if ( ConfigureParams.System.nMachineType == MACHINE_ST)
2031: HBLPalettes[i] &= 0x777; /* Force unused "random" bits to 0 */
2032: }
1.1 root 2033:
1.1.1.11 root 2034: /* And set mask flag with palette and resolution */
2035: // FIXME ; enlever PALETTEMASK_RESOLUTION
2036:
1.1.1.15 root 2037: // if ( ShifterFrame.ShifterLines[ nFirstVisibleHbl ].BorderMask == BORDERMASK_NONE ) // no border trick, store the current res
1.1.1.11 root 2038: HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((Uint32)IoMem_ReadByte(0xff8260)&0x3)<<16);
2039: // else // border removal, assume low res for the whole line
2040: // HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (0<<16);
1.1 root 2041: }
2042:
1.1.1.2 root 2043:
2044: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2045: /**
2046: * Store resolution on each line (used to test if mixed low/medium resolutions)
2047: */
1.1.1.7 root 2048: static void Video_StoreResolution(int y)
1.1 root 2049: {
1.1.1.11 root 2050: Uint8 res;
2051: int Mask;
1.1 root 2052:
1.1.1.11 root 2053: /* Clear resolution, and set with current value */
2054: if (!(bUseHighRes || bUseVDIRes))
2055: {
1.1.1.20 root 2056: if ( y >= HBL_PALETTE_MASKS ) /* we're above the limit (res was switched to mono for more than 1 VBL in color mode ?) */
2057: {
2058: // fprintf ( stderr , "store res %d line %d hbl %d %x %x %d\n" , res , y , nHBL, Mask , HBLPaletteMasks[y] , sizeof(HBLPalettes) );
2059: y = HBL_PALETTE_MASKS - 1; /* store in the last palette line */
2060: }
2061:
1.1.1.11 root 2062: HBLPaletteMasks[y] &= ~(0x3<<16);
2063: res = IoMem_ReadByte(0xff8260)&0x3;
2064:
1.1.1.15 root 2065: Mask = ShifterFrame.ShifterLines[ y+nFirstVisibleHbl ].BorderMask;
1.1.1.11 root 2066:
1.1.1.15 root 2067: if ( Mask & BORDERMASK_OVERSCAN_MED_RES ) /* special case for med res to render the overscan line */
2068: res = 1; /* med res instead of low res */
1.1.1.11 root 2069: else if ( Mask != BORDERMASK_NONE ) /* border removal : assume low res for the whole line */
2070: res = 0;
1.1.1.2 root 2071:
1.1.1.11 root 2072: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)res)<<16;
2073:
2074: #if 0
2075: if ( ( Mask == BORDERMASK_NONE ) /* no border trick, store the current res */
2076: || ( res == 0 ) || ( res == 1 ) ) /* if border trick, ignore passage to hi res */
2077: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)res)<<16;
2078: else /* border removal or hi res : assume low res for the whole line */
2079: HBLPaletteMasks[y] |= (0)<<16;
2080:
1.1.1.15 root 2081: /* special case for med res to render the overscan line */
2082: if ( Mask & BORDERMASK_OVERSCAN_MED_RES )
2083: HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)1)<<16; /* med res instead of low res */
1.1.1.11 root 2084: #endif
1.1 root 2085:
1.1.1.11 root 2086: // fprintf ( stderr , "store res %d line %d %x %x\n" , res , y , Mask , HBLPaletteMasks[y] );
2087: }
1.1.1.9 root 2088: }
2089:
2090:
2091: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2092: /**
2093: * Copy one line of monochrome screen into buffer for conversion later.
2094: */
2095: static void Video_CopyScreenLineMono(void)
2096: {
2097: /* Copy one line - 80 bytes in ST high resolution */
2098: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_MONOLINE);
2099: pVideoRaster += SCREENBYTES_MONOLINE;
2100:
2101: /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
2102: if (HWScrollCount)
2103: {
2104: Uint16 *pScrollAdj;
2105: int nNegScrollCnt;
2106:
2107: pScrollAdj = (Uint16 *)pSTScreen;
2108: nNegScrollCnt = 16 - HWScrollCount;
2109:
2110: /* Shift the whole line by the given scroll count */
2111: while ((Uint8*)pScrollAdj < pSTScreen + SCREENBYTES_MONOLINE-2)
2112: {
2113: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
2114: | (do_get_mem_word(pScrollAdj+1) >> nNegScrollCnt));
2115: ++pScrollAdj;
2116: }
2117:
2118: /* Handle the last 16 pixels of the line */
2119: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
2120: | (do_get_mem_word(pVideoRaster) >> nNegScrollCnt));
2121:
2122: /* HW scrolling advances Shifter video counter by one */
2123: pVideoRaster += 1 * 2;
2124: }
2125:
2126: /* LineWidth is zero on ST. */
2127: /* On STE, the Shifter skips the given amount of words. */
2128: pVideoRaster += LineWidth*2;
2129:
2130: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
2131: /* that occurred while the display was already ON */
1.1.1.16 root 2132: if ( VideoCounterDelayedOffset != 0 )
1.1.1.11 root 2133: {
1.1.1.16 root 2134: pVideoRaster += ( VideoCounterDelayedOffset & ~1 );
2135: VideoCounterDelayedOffset = 0;
1.1.1.11 root 2136: }
1.1.1.16 root 2137:
2138: if ( pVideoRasterDelayed != NULL )
1.1.1.11 root 2139: {
1.1.1.16 root 2140: pVideoRaster = pVideoRasterDelayed;
2141: pVideoRasterDelayed = NULL;
1.1.1.11 root 2142: }
2143:
2144: /* On STE, if we wrote to the hwscroll register, we set the */
2145: /* new value here, once the current line was processed */
2146: if ( NewHWScrollCount >= 0 )
2147: {
2148: HWScrollCount = NewHWScrollCount;
2149: NewHWScrollCount = -1;
2150: }
2151:
2152: /* On STE, if we wrote to the linewidth register, we set the */
2153: /* new value here, once the current line was processed */
2154: if ( NewLineWidth >= 0 )
2155: {
2156: LineWidth = NewLineWidth;
2157: NewLineWidth = -1;
2158: }
2159:
2160: /* Each screen line copied to buffer is always same length */
2161: pSTScreen += SCREENBYTES_MONOLINE;
1.1.1.22 root 2162:
2163: /* We must keep the new video address in a 24 bit space */
2164: /* (in case it pointed to IO space and is now >= 0x1000000) */
2165: pVideoRaster = ( ( pVideoRaster - STRam ) & 0xffffff ) + STRam;
1.1.1.11 root 2166: }
2167:
2168:
2169: /*-----------------------------------------------------------------------*/
2170: /**
2171: * Copy one line of color screen into buffer for conversion later.
2172: * Possible lines may be top/bottom border, and/or left/right borders.
2173: */
1.1.1.9 root 2174: static void Video_CopyScreenLineColor(void)
2175: {
1.1.1.15 root 2176: int LineBorderMask;
1.1.1.11 root 2177: int VideoOffset = 0;
2178: int STF_PixelScroll = 0;
1.1.1.14 root 2179: int LineRes;
1.1.1.17 root 2180: Uint8 *pVideoRasterEndLine; /* addr of the last byte copied from pVideoRaster to pSTScreen (for HWScrollCount) */
2181: int i;
1.1.1.14 root 2182:
1.1.1.15 root 2183: LineBorderMask = ShifterFrame.ShifterLines[ nHBL ].BorderMask;
1.1.1.16 root 2184: STF_PixelScroll = ShifterFrame.ShifterLines[ nHBL ].DisplayPixelShift;
1.1.1.15 root 2185:
2186: /* Get resolution for this line (in case of mixed low/med screen) */
1.1.1.20 root 2187: i = nHBL-nFirstVisibleHbl;
2188: if ( i >= HBL_PALETTE_MASKS )
2189: i = HBL_PALETTE_MASKS - 1;
2190: LineRes = ( HBLPaletteMasks[i] >> 16 ) & 1; /* 0=low res 1=med res */
1.1.1.11 root 2191:
1.1.1.16 root 2192: //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 2193:
1.1.1.22 root 2194: /* FIXME [NP] : when removing left border and displaying med res at 60 Hz on STE, we have a 3 pixel shift */
2195: /* to correct to have bitmaps and color changes in sync. */
2196: /* For now we only shift for med @ 60 Hz, but this should be measured for all */
2197: /* freq and low / med res combinations on a real STE (fix "HighResMode" demo by Paradox). */
2198: if ( ( ConfigureParams.System.nMachineType == MACHINE_STE )
2199: && ( LineBorderMask & BORDERMASK_LEFT_OFF_MED )
2200: && ( nCyclesPerLine == 508 )
2201: )
2202: {
2203: STF_PixelScroll = 3;
2204: }
2205:
1.1.1.11 root 2206: /* If left border is opened, we need to compensate one missing word in low res (1 plan) */
1.1.1.15 root 2207: /* If overscan is in med res, the offset is variable */
2208: if ( LineBorderMask & BORDERMASK_OVERSCAN_MED_RES )
2209: VideoOffset = - ( ( LineBorderMask >> 20 ) & 0x0f ); /* No Cooper=0 PYM=-2 in med res overscan */
1.1.1.11 root 2210:
2211: else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1.1.1.18 root 2212: {
2213: #ifdef SCROLL2_4PX
2214: int ShiftPixels = 0;
2215:
2216: if ( STF_PixelScroll == 13 ) { VideoOffset = 2; ShiftPixels = 8; }
2217: else if ( STF_PixelScroll == 9 ) { VideoOffset = 0; ShiftPixels = 8; }
2218: else if ( STF_PixelScroll == 5 ) { VideoOffset = -2; ShiftPixels = 8; }
2219: else if ( STF_PixelScroll == 1 ) { VideoOffset = -4; ShiftPixels = 8; }
2220:
2221: else VideoOffset = -2; /* Normal low res left border removal without 4 pixels scrolling */
2222:
2223: STF_PixelScroll -= ShiftPixels;
2224: #else
1.1.1.14 root 2225: VideoOffset = -2; /* always 2 bytes in low res overscan */
1.1.1.18 root 2226: #endif
2227: }
1.1.1.11 root 2228:
1.1.1.16 root 2229: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_2_STE )
2230: VideoOffset = -4; /* 4 first bytes of the line are not shown */
2231:
1.1.1.11 root 2232: /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
2233: /* Depending on the number of pixels, we need to compensate for some skipped words */
1.1.1.15 root 2234: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_MED )
1.1.1.11 root 2235: {
1.1.1.16 root 2236: if ( STF_PixelScroll == 13 ) VideoOffset = 2;
2237: else if ( STF_PixelScroll == 9 ) VideoOffset = 0;
2238: else if ( STF_PixelScroll == 5 ) VideoOffset = -2;
2239: else if ( STF_PixelScroll == 1 ) VideoOffset = -4;
1.1.1.11 root 2240: else VideoOffset = 0; /* never used ? */
2241:
1.1.1.16 root 2242: STF_PixelScroll -= 8; /* removing left border in mid res also shifts display to the left */
1.1.1.11 root 2243: // fprintf(stderr , "scr off %d %d\n" , STF_PixelScroll , VideoOffset);
2244: }
2245:
1.1.1.13 root 2246:
1.1.1.11 root 2247: /* Is total blank line? I.e. top/bottom border? */
1.1.1.18 root 2248: if ((nHBL < nStartHBL) || (nHBL >= nEndHBL + BlankLines)
1.1.1.15 root 2249: || (LineBorderMask & BORDERMASK_EMPTY_LINE))
1.1.1.11 root 2250: {
2251: /* Clear line to color '0' */
2252: memset(pSTScreen, 0, SCREENBYTES_LINE);
2253: }
2254: else
2255: {
1.1.1.18 root 2256: /* Does have left border ? */
1.1.1.16 root 2257: if ( LineBorderMask & ( BORDERMASK_LEFT_OFF | BORDERMASK_LEFT_OFF_MED ) ) /* bigger line by 26 bytes on the left */
1.1.1.11 root 2258: {
2259: pVideoRaster += BORDERBYTES_LEFT-SCREENBYTES_LEFT+VideoOffset;
2260: memcpy(pSTScreen, pVideoRaster, SCREENBYTES_LEFT);
2261: pVideoRaster += SCREENBYTES_LEFT;
2262: }
1.1.1.16 root 2263: else if ( LineBorderMask & BORDERMASK_LEFT_OFF_2_STE ) /* bigger line by 20 bytes on the left (STE specific) */
2264: { /* bytes 0-3 are not shown, only next 16 bytes (32 pixels, 4 bitplanes) */
2265: if ( SCREENBYTES_LEFT > BORDERBYTES_LEFT_2_STE )
2266: {
2267: memset ( pSTScreen, 0, SCREENBYTES_LEFT-BORDERBYTES_LEFT_2_STE+4 ); /* clear unused pixels + bytes 0-3 */
2268: memcpy ( pSTScreen+SCREENBYTES_LEFT-BORDERBYTES_LEFT_2_STE+4, pVideoRaster+VideoOffset+4, BORDERBYTES_LEFT_2_STE-4 );
2269: }
2270: else
2271: memcpy ( pSTScreen, pVideoRaster+BORDERBYTES_LEFT_2_STE-SCREENBYTES_LEFT+VideoOffset, SCREENBYTES_LEFT );
2272:
2273: pVideoRaster += BORDERBYTES_LEFT_2_STE+VideoOffset;
2274: }
2275: else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2) /* bigger line by 2 bytes on the left */
1.1.1.11 root 2276: {
1.1.1.16 root 2277: if ( SCREENBYTES_LEFT > 2 )
2278: {
2279: memset(pSTScreen,0,SCREENBYTES_LEFT-2); /* clear unused pixels */
2280: memcpy(pSTScreen+SCREENBYTES_LEFT-2, pVideoRaster, 2);
2281: }
2282: else
2283: { /* nothing to copy, left border is not large enough */
2284: }
2285:
1.1.1.11 root 2286: pVideoRaster += 2;
2287: }
1.1.1.16 root 2288: else if (bSteBorderFlag) /* bigger line by 8 bytes on the left (STE specific) */
1.1.1.12 root 2289: {
1.1.1.16 root 2290: if ( SCREENBYTES_LEFT > 4*2 )
2291: {
2292: memset(pSTScreen,0,SCREENBYTES_LEFT-4*2); /* clear unused pixels */
2293: memcpy(pSTScreen+SCREENBYTES_LEFT-4*2, pVideoRaster, 4*2);
2294: }
2295: else
2296: { /* nothing to copy, left border is not large enough */
2297: }
2298:
1.1.1.12 root 2299: pVideoRaster += 4*2;
2300: }
1.1.1.11 root 2301: else
1.1.1.16 root 2302: memset(pSTScreen,0,SCREENBYTES_LEFT); /* left border not removed, clear to color '0' */
1.1.1.11 root 2303:
2304: /* Short line due to hires in the middle ? */
2305: if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
2306: {
2307: /* 106 bytes less in the line */
2308: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE-106);
1.1.1.12 root 2309: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-106, 0, 106); /* clear unused pixels */
1.1.1.11 root 2310: pVideoRaster += (SCREENBYTES_MIDDLE-106);
2311: }
2312: else
2313: {
2314: /* normal middle part (160 bytes) */
2315: memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE);
2316: pVideoRaster += SCREENBYTES_MIDDLE;
2317: }
2318:
2319: /* Does have right border ? */
2320: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
2321: {
2322: memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE, pVideoRaster, SCREENBYTES_RIGHT);
1.1.1.17 root 2323: pVideoRasterEndLine = pVideoRaster + SCREENBYTES_RIGHT;
1.1.1.16 root 2324: pVideoRaster += BORDERBYTES_RIGHT;
1.1.1.11 root 2325: }
2326: else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
2327: {
2328: /* Shortened line by 2 bytes */
2329: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-2, 0, SCREENBYTES_RIGHT+2);
2330: pVideoRaster -= 2;
1.1.1.17 root 2331: pVideoRasterEndLine = pVideoRaster;
1.1.1.11 root 2332: }
2333: else
2334: {
2335: /* Simply clear right border to '0' */
2336: memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
1.1.1.17 root 2337: pVideoRasterEndLine = pVideoRaster;
1.1.1.11 root 2338: }
2339:
1.1.1.18 root 2340: /* Shifter read bytes and borders can change, but display is blank, so finally clear the line with color 0 */
2341: if (LineBorderMask & BORDERMASK_BLANK_LINE)
2342: memset(pSTScreen, 0, SCREENBYTES_LINE);
2343:
1.1.1.11 root 2344: /* Full right border removal up to the end of the line (cycle 512) */
2345: if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
2346: pVideoRaster += BORDERBYTES_RIGHT_FULL;
2347:
2348: /* Correct the offset for pVideoRaster from BORDERMASK_LEFT_OFF above if needed */
2349: pVideoRaster -= VideoOffset; /* VideoOffset is 0 or -2 */
2350:
2351:
2352: /* STE specific */
1.1.1.13 root 2353: if (!bSteBorderFlag && HWScrollCount) /* Handle STE fine scrolling (HWScrollCount is zero on ST) */
1.1.1.11 root 2354: {
1.1.1.13 root 2355: Uint16 *pScrollAdj; /* Pointer to actual position in line */
1.1.1.11 root 2356: int nNegScrollCnt;
1.1.1.13 root 2357: Uint16 *pScrollEndAddr; /* Pointer to end of the line */
1.1.1.11 root 2358:
2359: nNegScrollCnt = 16 - HWScrollCount;
2360: if (LineBorderMask & BORDERMASK_LEFT_OFF)
2361: pScrollAdj = (Uint16 *)pSTScreen;
1.1.1.16 root 2362: else if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
2363: {
2364: if ( SCREENBYTES_LEFT > BORDERBYTES_LEFT_2_STE )
1.1.1.17 root 2365: pScrollAdj = (Uint16 *)(pSTScreen+8); /* don't scroll the 8 first bytes (keep color 0)*/
1.1.1.16 root 2366: else
2367: pScrollAdj = (Uint16 *)pSTScreen; /* we render less bytes on screen than a real ST, scroll the whole line */
2368: }
1.1.1.11 root 2369: else
2370: pScrollAdj = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT);
1.1.1.16 root 2371:
1.1.1.17 root 2372: /* When shifting the line to the left, we will have 'HWScrollCount' missing pixels at */
2373: /* the end of the line. We must complete these last 16 pixels with pixels from the */
2374: /* video counter last accessed value in pVideoRasterEndLine. */
2375: /* There're 2 passes : */
2376: /* - shift whole line except the last 16 pixels */
2377: /* - shift/complete the last 16 pixels */
2378:
2379: /* Addr of the last byte to shift in the 1st pass (excluding the last 16 pixels of the line) */
1.1.1.11 root 2380: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
2381: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LINE - 8);
2382: else
2383: pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8);
2384:
1.1.1.17 root 2385:
2386: if ( LineRes == 1 ) /* med res */
1.1.1.11 root 2387: {
1.1.1.15 root 2388: /* 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 2389: pScrollEndAddr += 2; /* 2 Uint16 = 4 bytes = 16 pixels */
1.1.1.14 root 2390:
1.1.1.17 root 2391: /* Shift the whole line to the left by the given scroll count (except the last 16 pixels) */
1.1.1.14 root 2392: while (pScrollAdj < pScrollEndAddr)
2393: {
2394: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
2395: | (do_get_mem_word(pScrollAdj+2) >> nNegScrollCnt));
2396: ++pScrollAdj;
2397: }
1.1.1.17 root 2398: /* Handle the last 16 pixels of the line (complete the line with pixels from pVideoRasterEndLine) */
2399: for ( i=0 ; i<2 ; i++ )
2400: do_put_mem_word(pScrollAdj+i, (do_get_mem_word(pScrollAdj+i) << HWScrollCount)
2401: | (do_get_mem_word(pVideoRasterEndLine+i*2) >> nNegScrollCnt));
1.1.1.14 root 2402:
2403: /* Depending on whether $ff8264 or $ff8265 was used to scroll, */
2404: /* we prefetched 16 pixel (4 bytes) */
1.1.1.13 root 2405: if ( HWScrollPrefetch == 1 ) /* $ff8265 prefetches 16 pixels */
2406: pVideoRaster += 2 * 2; /* 2 bitplans */
1.1.1.14 root 2407:
2408: /* If scrolling with $ff8264, there's no prefetch, which means display starts */
2409: /* 16 pixels later but still stops at the normal point (eg we display */
2410: /* (320-16) pixels in low res). We shift the whole line 4 bytes to the right to */
2411: /* get the correct result (using memmove, as src/dest are overlapping). */
2412: else
2413: {
2414: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
2415: memmove ( pSTScreen+4 , pSTScreen , SCREENBYTES_LINE - 4 );
2416: else
2417: memmove ( pSTScreen+4 , pSTScreen , SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 4 );
2418:
2419: memset ( pSTScreen , 0 , 4 ); /* first 16 pixels are color '0' */
2420: }
1.1.1.11 root 2421: }
1.1.1.14 root 2422:
1.1.1.17 root 2423: else /* low res */
1.1.1.11 root 2424: {
1.1.1.17 root 2425: /* Shift the whole line to the left by the given scroll count (except the last 16 pixels) */
1.1.1.11 root 2426: while (pScrollAdj < pScrollEndAddr)
2427: {
2428: do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
2429: | (do_get_mem_word(pScrollAdj+4) >> nNegScrollCnt));
2430: ++pScrollAdj;
2431: }
1.1.1.17 root 2432: /* Handle the last 16 pixels of the line (complete the line with pixels from pVideoRasterEndLine) */
2433: for ( i=0 ; i<4 ; i++ )
2434: do_put_mem_word(pScrollAdj+i, (do_get_mem_word(pScrollAdj+i) << HWScrollCount)
2435: | (do_get_mem_word(pVideoRasterEndLine+i*2) >> nNegScrollCnt));
1.1.1.13 root 2436:
2437: /* Depending on whether $ff8264 or $ff8265 was used to scroll, */
2438: /* we prefetched 16 pixel (8 bytes) */
2439: if ( HWScrollPrefetch == 1 ) /* $ff8265 prefetches 16 pixels */
2440: pVideoRaster += 4 * 2; /* 4 bitplans */
2441:
2442: /* If scrolling with $ff8264, there's no prefetch, which means display starts */
2443: /* 16 pixels later but still stops at the normal point (eg we display */
2444: /* (320-16) pixels in low res). We shift the whole line 8 bytes to the right to */
2445: /* get the correct result (using memmove, as src/dest are overlapping). */
2446: else
2447: {
2448: if (LineBorderMask & BORDERMASK_RIGHT_OFF)
2449: memmove ( pSTScreen+8 , pSTScreen , SCREENBYTES_LINE - 8 );
2450: else
2451: memmove ( pSTScreen+8 , pSTScreen , SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8 );
2452:
2453: memset ( pSTScreen , 0 , 8 ); /* first 16 pixels are color '0' */
2454: }
1.1.1.11 root 2455:
2456: /* On STE, when we have a 230 bytes overscan line and HWScrollCount > 0 */
1.1.1.17 root 2457: /* we must read 6 bytes less than expected if scrolling is using prefetching ($ff8265) */
2458: /* (this is not the case for the 224 bytes overscan which is a multiple of 8) */
1.1.1.11 root 2459: if ( (LineBorderMask & BORDERMASK_LEFT_OFF) && (LineBorderMask & BORDERMASK_RIGHT_OFF) )
1.1.1.17 root 2460: {
2461: if ( HWScrollPrefetch == 1 )
2462: pVideoRaster -= 6; /* we don't add 8 bytes (see above), but 2 */
2463: else
2464: pVideoRaster -= 0;
2465: }
1.1.1.11 root 2466:
2467: }
2468: }
2469:
2470: /* LineWidth is zero on ST. */
2471: /* On STE, the Shifter skips the given amount of words. */
2472: pVideoRaster += LineWidth*2;
2473:
2474: /* On STE, handle modifications of the video counter address $ff8205/07/09 */
2475: /* that occurred while the display was already ON */
1.1.1.16 root 2476: if ( VideoCounterDelayedOffset != 0 )
1.1.1.11 root 2477: {
1.1.1.16 root 2478: pVideoRaster += ( VideoCounterDelayedOffset & ~1 );
2479: // fprintf ( stderr , "adjust video counter offset=%d new video=%x\n" , VideoCounterDelayedOffset , pVideoRaster-STRam );
2480: VideoCounterDelayedOffset = 0;
1.1.1.11 root 2481: }
1.1.1.16 root 2482:
2483: if ( pVideoRasterDelayed != NULL )
1.1.1.11 root 2484: {
1.1.1.16 root 2485: pVideoRaster = pVideoRasterDelayed;
2486: // fprintf ( stderr , "adjust video counter const new video=%x\n" , pVideoRaster-STRam );
2487: pVideoRasterDelayed = NULL;
1.1.1.11 root 2488: }
2489:
2490: /* On STE, if we wrote to the hwscroll register, we set the */
2491: /* new value here, once the current line was processed */
2492: if ( NewHWScrollCount >= 0 )
2493: {
2494: HWScrollCount = NewHWScrollCount;
1.1.1.13 root 2495: HWScrollPrefetch = NewHWScrollPrefetch;
1.1.1.11 root 2496: NewHWScrollCount = -1;
1.1.1.13 root 2497: NewHWScrollPrefetch = -1;
2498: }
2499:
2500: /* On STE, if we trigger the left border + 16 pixels trick, we set the */
2501: /* new value here, once the current line was processed */
2502: if ( NewSteBorderFlag >= 0 )
2503: {
2504: if ( NewSteBorderFlag == 0 )
1.1.1.15 root 2505: bSteBorderFlag = false;
1.1.1.13 root 2506: else
1.1.1.15 root 2507: bSteBorderFlag = true;
1.1.1.13 root 2508: NewSteBorderFlag = -1;
1.1.1.11 root 2509: }
2510:
2511: /* On STE, if we wrote to the linewidth register, we set the */
2512: /* new value here, once the current line was processed */
2513: if ( NewLineWidth >= 0 )
2514: {
2515: LineWidth = NewLineWidth;
2516: NewLineWidth = -1;
2517: }
1.1.1.16 root 2518:
2519:
2520: /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
1.1.1.21 root 2521: /* as well as scrolling occurring when removing the left border. */
1.1.1.16 root 2522: /* If >0, shift the line by STF_PixelScroll pixels to the right */
2523: /* If <0, shift the line by -STF_PixelScroll pixels to the left */
2524: /* This should be handled after the STE's hardware scrolling as it will scroll */
2525: /* the whole displayed area (while the STE scrolls pixels inside the displayed area) */
2526: if ( STF_PixelScroll > 0 )
2527: {
2528: Uint16 *pScreenLineEnd;
2529: int count;
2530:
2531: pScreenLineEnd = (Uint16 *) ( pSTScreen + SCREENBYTES_LINE - 2 );
1.1.1.17 root 2532: if ( LineRes == 0 ) /* low res */
2533: {
2534: for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineEnd-- )
2535: do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 4 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
2536: /* Handle the first 16 pixels of the line (add color 0 pixels to the extreme left) */
2537: do_put_mem_word ( pScreenLineEnd-0 , ( do_get_mem_word ( pScreenLineEnd-0 ) >> STF_PixelScroll ) );
2538: do_put_mem_word ( pScreenLineEnd-1 , ( do_get_mem_word ( pScreenLineEnd-1 ) >> STF_PixelScroll ) );
2539: do_put_mem_word ( pScreenLineEnd-2 , ( do_get_mem_word ( pScreenLineEnd-2 ) >> STF_PixelScroll ) );
2540: do_put_mem_word ( pScreenLineEnd-3 , ( do_get_mem_word ( pScreenLineEnd-3 ) >> STF_PixelScroll ) );
2541: }
2542: else /* med res */
2543: {
2544: for ( count = 0 ; count < ( SCREENBYTES_LINE - 4 ) / 2 ; count++ , pScreenLineEnd-- )
2545: do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 2 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
2546: /* Handle the first 16 pixels of the line (add color 0 pixels to the extreme left) */
2547: do_put_mem_word ( pScreenLineEnd-0 , ( do_get_mem_word ( pScreenLineEnd-0 ) >> STF_PixelScroll ) );
2548: do_put_mem_word ( pScreenLineEnd-1 , ( do_get_mem_word ( pScreenLineEnd-1 ) >> STF_PixelScroll ) );
2549: }
1.1.1.16 root 2550: }
2551: else if ( STF_PixelScroll < 0 )
2552: {
2553: Uint16 *pScreenLineStart;
2554: int count;
2555:
2556: STF_PixelScroll = -STF_PixelScroll;
2557: pScreenLineStart = (Uint16 *)pSTScreen;
1.1.1.17 root 2558: if ( LineRes == 0 ) /* low res */
2559: {
2560: for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineStart++ )
2561: do_put_mem_word ( pScreenLineStart , ( ( do_get_mem_word ( pScreenLineStart ) << STF_PixelScroll ) | ( do_get_mem_word ( pScreenLineStart + 4 ) >> (16-STF_PixelScroll) ) ) );
2562: /* Handle the last 16 pixels of the line (add color 0 pixels to the extreme right) */
2563: do_put_mem_word ( pScreenLineStart+0 , ( do_get_mem_word ( pScreenLineStart+0 ) << STF_PixelScroll ) );
2564: do_put_mem_word ( pScreenLineStart+1 , ( do_get_mem_word ( pScreenLineStart+1 ) << STF_PixelScroll ) );
2565: do_put_mem_word ( pScreenLineStart+2 , ( do_get_mem_word ( pScreenLineStart+2 ) << STF_PixelScroll ) );
2566: do_put_mem_word ( pScreenLineStart+3 , ( do_get_mem_word ( pScreenLineStart+3 ) << STF_PixelScroll ) );
2567: }
2568: else /* med res */
2569: {
2570: for ( count = 0 ; count < ( SCREENBYTES_LINE - 4 ) / 2 ; count++ , pScreenLineStart++ )
2571: do_put_mem_word ( pScreenLineStart , ( ( do_get_mem_word ( pScreenLineStart ) << STF_PixelScroll ) | ( do_get_mem_word ( pScreenLineStart + 2 ) >> (16-STF_PixelScroll) ) ) );
2572: /* Handle the last 16 pixels of the line (add color 0 pixels to the extreme right) */
2573: do_put_mem_word ( pScreenLineStart+0 , ( do_get_mem_word ( pScreenLineStart+0 ) << STF_PixelScroll ) );
2574: do_put_mem_word ( pScreenLineStart+1 , ( do_get_mem_word ( pScreenLineStart+1 ) << STF_PixelScroll ) );
2575: }
1.1.1.16 root 2576: }
1.1.1.11 root 2577: }
2578:
2579: /* Each screen line copied to buffer is always same length */
2580: pSTScreen += SCREENBYTES_LINE;
1.1.1.22 root 2581:
2582: /* We must keep the new video address in a 24 bit space */
2583: /* (in case it pointed to IO space and is now >= 0x1000000) */
2584: pVideoRaster = ( ( pVideoRaster - STRam ) & 0xffffff ) + STRam;
1.1.1.11 root 2585: }
2586:
2587:
2588: /*-----------------------------------------------------------------------*/
2589: /**
2590: * Copy extended GEM resolution screen
2591: */
1.1.1.9 root 2592: static void Video_CopyVDIScreen(void)
1.1 root 2593: {
1.1.1.11 root 2594: /* Copy whole screen, don't care about being exact as for GEM only */
2595: memcpy(pSTScreen, pVideoRaster, ((VDIWidth*VDIPlanes)/8)*VDIHeight);
1.1 root 2596: }
2597:
1.1.1.2 root 2598:
2599: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2600: /**
2601: * Clear raster line table to store changes in palette/resolution on a line
2602: * basic. Called once on VBL interrupt.
2603: */
1.1 root 2604: void Video_SetScreenRasters(void)
2605: {
1.1.1.11 root 2606: pHBLPaletteMasks = HBLPaletteMasks;
2607: pHBLPalettes = HBLPalettes;
2608: memset(pHBLPaletteMasks, 0, sizeof(Uint32)*NUM_VISIBLE_LINES); /* Clear array */
1.1 root 2609: }
2610:
1.1.1.2 root 2611:
2612: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2613: /**
2614: * Set pointers to HBLPalette tables to store correct colours/resolutions
2615: */
1.1.1.9 root 2616: static void Video_SetHBLPaletteMaskPointers(void)
1.1 root 2617: {
1.1.1.15 root 2618: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.11 root 2619: int Line;
2620:
2621: /* FIXME [NP] We should use Cycles_GetCounterOnWriteAccess, but it wouldn't */
2622: /* work when using multiple accesses instructions like move.l or movem */
1.1.1.15 root 2623: /* To correct this, we assume a delay of 8 cycles (should give a good approximation */
1.1.1.11 root 2624: /* of a move.w or movem.l for example) */
2625: // FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
2626: FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
2627:
2628: /* Find 'line' into palette - screen starts 63 lines down, less 29 for top overscan */
1.1.1.15 root 2629: Video_ConvertPosition ( FrameCycles , &HblCounterVideo , &LineCycles );
2630: Line = HblCounterVideo - nFirstVisibleHbl;
1.1.1.11 root 2631:
2632: /* FIXME [NP] if the color change occurs after the last visible pixel of a line */
2633: /* we consider the palette should be modified on the next line. This is quite */
2634: /* a hack, we should handle all color changes through spec512.c to have cycle */
2635: /* accuracy all the time. */
1.1.1.15 root 2636: if ( LineCycles >= LINE_END_CYCLE_NO_RIGHT )
2637: Line++;
1.1.1.11 root 2638:
2639: if (Line < 0) /* Limit to top/bottom of possible visible screen */
2640: Line = 0;
2641: if (Line >= NUM_VISIBLE_LINES)
2642: Line = NUM_VISIBLE_LINES-1;
2643:
2644: /* Store pointers */
2645: pHBLPaletteMasks = &HBLPaletteMasks[Line]; /* Next mask entry */
2646: pHBLPalettes = &HBLPalettes[16*Line]; /* Next colour raster list x16 colours */
1.1 root 2647: }
1.1.1.8 root 2648:
2649:
1.1.1.9 root 2650: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2651: /**
2652: * Set video shifter timing variables according to screen refresh rate.
2653: * Note: The following equation must be satisfied for correct timings:
2654: *
2655: * nCyclesPerLine * nScanlinesPerFrame * nScreenRefreshRate = 8 MHz
2656: */
1.1.1.10 root 2657: static void Video_ResetShifterTimings(void)
2658: {
1.1.1.11 root 2659: Uint8 nSyncByte;
1.1.1.10 root 2660:
1.1.1.11 root 2661: nSyncByte = IoMem_ReadByte(0xff820a);
1.1.1.10 root 2662:
1.1.1.19 root 2663: if ((IoMem_ReadByte(0xff8260) & 3) == 2)
1.1.1.11 root 2664: {
2665: /* 71 Hz, monochrome */
2666: nScreenRefreshRate = 71;
2667: nScanlinesPerFrame = SCANLINES_PER_FRAME_71HZ;
2668: nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1.1.1.13 root 2669: nStartHBL = VIDEO_START_HBL_71HZ;
1.1.1.11 root 2670: nFirstVisibleHbl = FIRST_VISIBLE_HBL_71HZ;
1.1.1.13 root 2671: nLastVisibleHbl = FIRST_VISIBLE_HBL_71HZ + VIDEO_HEIGHT_HBL_MONO;
1.1.1.11 root 2672: }
2673: else if (nSyncByte & 2) /* Check if running in 50 Hz or in 60 Hz */
2674: {
2675: /* 50 Hz */
2676: nScreenRefreshRate = 50;
2677: nScanlinesPerFrame = SCANLINES_PER_FRAME_50HZ;
2678: nCyclesPerLine = CYCLES_PER_LINE_50HZ;
1.1.1.13 root 2679: nStartHBL = VIDEO_START_HBL_50HZ;
1.1.1.11 root 2680: nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ;
1.1.1.13 root 2681: nLastVisibleHbl = FIRST_VISIBLE_HBL_50HZ + NUM_VISIBLE_LINES;
1.1.1.11 root 2682: }
2683: else
2684: {
2685: /* 60 Hz */
2686: nScreenRefreshRate = 60;
2687: nScanlinesPerFrame = SCANLINES_PER_FRAME_60HZ;
2688: nCyclesPerLine = CYCLES_PER_LINE_60HZ;
1.1.1.13 root 2689: nStartHBL = VIDEO_START_HBL_60HZ;
1.1.1.11 root 2690: nFirstVisibleHbl = FIRST_VISIBLE_HBL_60HZ;
1.1.1.13 root 2691: nLastVisibleHbl = FIRST_VISIBLE_HBL_60HZ + NUM_VISIBLE_LINES;
1.1.1.11 root 2692: }
2693:
2694: if (bUseHighRes)
2695: {
1.1.1.13 root 2696: nEndHBL = nStartHBL + VIDEO_HEIGHT_HBL_MONO;
1.1.1.11 root 2697: }
2698: else
2699: {
1.1.1.13 root 2700: nEndHBL = nStartHBL + VIDEO_HEIGHT_HBL_COLOR;
1.1.1.11 root 2701: }
2702:
2703: /* Reset freq changes position for the next VBL to come */
1.1.1.13 root 2704: LastCycleScroll8264 = -1;
2705: LastCycleScroll8265 = -1;
1.1.1.15 root 2706:
2707: TimerBEventCountCycleStart = -1; /* reset timer B activation cycle for this VBL */
2708:
1.1.1.18 root 2709: BlankLines = 0;
1.1.1.11 root 2710: }
1.1.1.10 root 2711:
2712:
1.1.1.11 root 2713: /*-----------------------------------------------------------------------*/
2714: /**
1.1.1.15 root 2715: * Clear the array indicating the state of each video line.
1.1.1.11 root 2716: */
1.1.1.15 root 2717: static void Video_InitShifterLines ( void )
1.1.1.11 root 2718: {
1.1.1.15 root 2719: int i;
2720:
2721: for ( i=0 ; i<MAX_SCANLINES_PER_FRAME ; i++ )
2722: {
2723: ShifterFrame.ShifterLines[i].BorderMask = 0;
2724: ShifterFrame.ShifterLines[i].DisplayPixelShift = 0;
2725: ShifterFrame.ShifterLines[i].DisplayStartCycle = -1;
2726: }
2727:
2728: ShifterFrame.ShifterLines[0].StartCycle = 0; /* 1st HBL starts at cycle 0 */
1.1.1.10 root 2729: }
2730:
2731:
2732: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2733: /**
2734: * Called on VBL, set registers ready for frame
2735: */
1.1.1.9 root 2736: static void Video_ClearOnVBL(void)
2737: {
1.1.1.11 root 2738: /* New screen, so first HBL */
2739: nHBL = 0;
2740: OverscanMode = OVERSCANMODE_NONE;
2741:
2742: Video_ResetShifterTimings();
2743:
2744: /* Get screen address pointer, aligned to 256 bytes on ST (ie ignore lowest byte) */
2745: VideoBase = (Uint32)IoMem_ReadByte(0xff8201)<<16 | (Uint32)IoMem_ReadByte(0xff8203)<<8;
2746: if (ConfigureParams.System.nMachineType != MACHINE_ST)
2747: {
1.1.1.20 root 2748: /* on STe 2 aligned, on TT 8 aligned. We do STe. */
1.1.1.11 root 2749: VideoBase |= IoMem_ReadByte(0xff820d) & ~1;
2750: }
2751: pVideoRaster = &STRam[VideoBase];
2752: pSTScreen = pFrameBuffer->pSTScreen;
2753:
2754: Video_SetScreenRasters();
1.1.1.15 root 2755: Video_InitShifterLines();
1.1.1.11 root 2756: Spec512_StartVBL();
1.1.1.15 root 2757: Video_StartHBL(); /* Init ShifterFrame.ShifterLines[0] */
1.1.1.11 root 2758: }
2759:
2760:
2761: /*-----------------------------------------------------------------------*/
2762: /**
2763: * Get width, height and bpp according to TT-Resolution
2764: */
2765: void Video_GetTTRes(int *width, int *height, int *bpp)
2766: {
2767: switch (TTRes)
2768: {
2769: case ST_LOW_RES: *width = 320; *height = 200; *bpp = 4; break;
2770: case ST_MEDIUM_RES:*width = 640; *height = 200; *bpp = 2; break;
2771: case ST_HIGH_RES: *width = 640; *height = 400; *bpp = 1; break;
2772: case TT_LOW_RES: *width = 320; *height = 480; *bpp = 8; break;
2773: case TT_MEDIUM_RES:*width = 640; *height = 480; *bpp = 4; break;
2774: case TT_HIGH_RES: *width = 1280; *height = 960; *bpp = 1; break;
2775: default:
2776: fprintf(stderr, "TT res error!\n");
2777: *width = 320; *height = 200; *bpp = 4;
2778: break;
2779: }
2780: }
2781:
2782:
2783: /*-----------------------------------------------------------------------*/
2784: /**
2785: * Convert TT palette to SDL palette
2786: */
2787: static void Video_UpdateTTPalette(int bpp)
2788: {
2789: Uint32 ttpalette, src, dst;
2790: Uint8 r,g,b, lowbyte, highbyte;
2791: Uint16 stcolor, ttcolor;
2792: int i, offset, colors;
2793:
2794: ttpalette = 0xff8400;
2795:
2796: if (!bTTColorsSTSync)
2797: {
2798: /* sync TT ST-palette to TT-palette */
2799: src = 0xff8240; /* ST-palette */
2800: offset = (IoMem_ReadWord(0xff8262) & 0x0f);
1.1.1.13 root 2801: /*fprintf(stderr, "offset: %d\n", offset);*/
1.1.1.11 root 2802: dst = ttpalette + offset * 16*SIZE_WORD;
2803:
2804: for (i = 0; i < 16; i++)
2805: {
2806: stcolor = IoMem_ReadWord(src);
1.1.1.21 root 2807: ttcolor = ((stcolor&0x777) << 1) | ((stcolor&0x888) >> 3);
1.1.1.11 root 2808: IoMem_WriteWord(dst, ttcolor);
2809: src += SIZE_WORD;
2810: dst += SIZE_WORD;
2811: }
1.1.1.15 root 2812: bTTColorsSTSync = true;
1.1.1.11 root 2813: }
2814:
2815: colors = 1 << bpp;
1.1.1.21 root 2816: if ((bpp == 1) && (TTRes == TT_HIGH_RES))
1.1.1.11 root 2817: {
2818: /* Monochrome mode... palette is hardwired (?) */
2819: HostScreen_setPaletteColor(0, 255, 255, 255);
2820: HostScreen_setPaletteColor(1, 0, 0, 0);
2821: }
1.1.1.21 root 2822: else if (bpp == 1)
2823: {
2824: /* Monochrome mode... palette is taken from first and last TT color */
2825: ttpalette = 0xff8400;
2826: lowbyte = IoMem_ReadByte(ttpalette++);
2827: highbyte = IoMem_ReadByte(ttpalette++);
2828: r = (lowbyte & 0x0f) << 4;
2829: g = (highbyte & 0xf0);
2830: b = (highbyte & 0x0f) << 4;
2831: //printf("%d: (%d,%d,%d)\n", 0,r,g,b);
2832: if(bTTHypermono)
2833: {
2834: r = g = b = highbyte;
2835: }
2836: HostScreen_setPaletteColor(0, r,g,b);
2837:
2838: ttpalette = 0xff85fe;
2839: lowbyte = IoMem_ReadByte(ttpalette++);
2840: highbyte = IoMem_ReadByte(ttpalette++);
2841: r = (lowbyte & 0x0f) << 4;
2842: g = (highbyte & 0xf0);
2843: b = (highbyte & 0x0f) << 4;
2844: if(bTTHypermono)
2845: {
2846: r = g = b = highbyte;
2847: }
2848: //printf("%d: (%d,%d,%d)\n", 1,r,g,b);
2849: HostScreen_setPaletteColor(1, r,g,b);
2850:
2851: }
1.1.1.11 root 2852: else
2853: {
2854: for (i = 0; i < colors; i++)
2855: {
2856: lowbyte = IoMem_ReadByte(ttpalette++);
2857: highbyte = IoMem_ReadByte(ttpalette++);
2858: r = (lowbyte & 0x0f) << 4;
2859: g = (highbyte & 0xf0);
2860: b = (highbyte & 0x0f) << 4;
2861: //printf("%d: (%d,%d,%d)\n", i,r,g,b);
1.1.1.21 root 2862: if(bTTHypermono)
2863: {
2864: r = g = b = highbyte;
2865: }
1.1.1.11 root 2866: HostScreen_setPaletteColor(i, r,g,b);
2867: }
2868: }
2869:
2870: HostScreen_updatePalette(colors);
1.1.1.15 root 2871: bTTColorsSync = true;
1.1.1.11 root 2872: }
2873:
2874:
2875: /*-----------------------------------------------------------------------*/
2876: /**
1.1.1.13 root 2877: * Update TT palette and blit TT screen using VIDEL code.
2878: * @return true if the screen contents changed
1.1.1.11 root 2879: */
1.1.1.16 root 2880: bool Video_RenderTTScreen(void)
1.1.1.11 root 2881: {
2882: static int nPrevTTRes = -1;
2883: int width, height, bpp;
2884:
2885: Video_GetTTRes(&width, &height, &bpp);
2886: if (TTRes != nPrevTTRes)
2887: {
1.1.1.23! root 2888: HostScreen_setWindowSize(width, height, 8, false);
1.1.1.11 root 2889: nPrevTTRes = TTRes;
2890: if (bpp == 1) /* Assert that mono palette will be used in mono mode */
1.1.1.15 root 2891: bTTColorsSync = false;
1.1.1.11 root 2892: }
2893:
2894: /* colors need synching? */
2895: if (!(bTTColorsSync && bTTColorsSTSync))
2896: {
2897: Video_UpdateTTPalette(bpp);
2898: }
1.1.1.21 root 2899: else if (TTSpecialVideoMode != nPrevTTSpecialVideoMode)
2900: {
2901: Video_UpdateTTPalette(bpp);
2902: nPrevTTSpecialVideoMode = TTSpecialVideoMode;
2903: }
1.1.1.11 root 2904:
2905: /* Yes, we are abusing the Videl routines for rendering the TT modes! */
2906: if (!HostScreen_renderBegin())
1.1.1.13 root 2907: return false;
1.1.1.16 root 2908: if (nScreenZoomX * nScreenZoomY != 1)
1.1.1.11 root 2909: VIDEL_ConvertScreenZoom(width, height, bpp, width * bpp / 16);
2910: else
1.1.1.23! root 2911: VIDEL_ConvertScreenNoZoom(width, height, bpp, width * bpp / 16);
1.1.1.13 root 2912:
1.1.1.22 root 2913: HostScreen_update1(HostScreen_renderEnd(), false);
1.1.1.13 root 2914: return true;
1.1.1.11 root 2915: }
2916:
2917:
2918: /*-----------------------------------------------------------------------*/
2919: /**
2920: * Draw screen (either with ST/STE shifter drawing functions or with
2921: * Videl drawing functions)
2922: */
2923: static void Video_DrawScreen(void)
2924: {
2925: /* Skip frame if need to */
1.1.1.13 root 2926: if (nVBLs % (nFrameSkips+1))
1.1.1.11 root 2927: return;
2928:
2929: /* Use extended VDI resolution?
2930: * If so, just copy whole screen on VBL rather than per HBL */
2931: if (bUseVDIRes)
2932: Video_CopyVDIScreen();
2933:
2934: /* Now draw the screen! */
2935: if (ConfigureParams.System.nMachineType == MACHINE_FALCON && !bUseVDIRes)
2936: {
1.1.1.17 root 2937: VIDEL_renderScreen();
1.1.1.11 root 2938: }
2939: else if (ConfigureParams.System.nMachineType == MACHINE_TT && !bUseVDIRes)
2940: {
1.1.1.17 root 2941: Video_RenderTTScreen();
1.1.1.11 root 2942: }
2943: else
1.1.1.13 root 2944: {
2945: /* Before drawing the screen, ensure all unused lines are cleared to color 0 */
2946: /* (this can happen in 60 Hz when hatari is displaying the screen's border) */
2947: /* pSTScreen was set during Video_CopyScreenLineColor */
1.1.1.14 root 2948: if (!bUseVDIRes && nHBL < nLastVisibleHbl)
1.1.1.13 root 2949: memset(pSTScreen, 0, SCREENBYTES_LINE * ( nLastVisibleHbl - nHBL ) );
2950:
1.1.1.17 root 2951: Screen_Draw();
1.1.1.13 root 2952: }
1.1.1.11 root 2953: }
2954:
2955:
2956: /*-----------------------------------------------------------------------*/
2957: /**
1.1.1.15 root 2958: * Start HBL, Timer B and VBL interrupts.
2959: */
2960:
2961:
2962: /**
2963: * Start HBL or Timer B interrupt at position Pos. If position Pos was
2964: * already reached, then the interrupt is set on the next line.
2965: */
2966:
2967: static void Video_AddInterrupt ( int Pos , interrupt_id Handler )
2968: {
2969: int FrameCycles , HblCounterVideo , LineCycles;
2970:
2971: if ( nHBL >= nScanlinesPerFrame )
2972: return; /* don't set a new hbl/timer B if we're on the last line, as the vbl will happen first */
2973:
2974: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.16 root 2975: //fprintf ( stderr , "add int pos=%d handler=%d LineCycles=%d nCyclesPerLine=%d \n" , Pos , Handler , LineCycles , nCyclesPerLine );
1.1.1.15 root 2976:
2977: if ( LineCycles < Pos ) /* changed before reaching the new Pos on the current line */
1.1.1.16 root 2978: CycInt_AddRelativeInterrupt ( Pos - LineCycles , INT_CPU_CYCLE, Handler );
1.1.1.15 root 2979: else /* Pos will be applied on next line */
1.1.1.16 root 2980: CycInt_AddRelativeInterrupt ( Pos - LineCycles + nCyclesPerLine , INT_CPU_CYCLE, Handler );
1.1.1.15 root 2981: }
2982:
2983:
2984: static void Video_AddInterruptHBL ( int Pos )
2985: {
1.1.1.16 root 2986: //fprintf ( stderr , "add hbl pos=%d\n" , Pos );
1.1.1.15 root 2987: if ( !bUseVDIRes )
2988: Video_AddInterrupt ( Pos , INTERRUPT_VIDEO_HBL );
2989: }
2990:
2991:
2992: void Video_AddInterruptTimerB ( int Pos )
2993: {
1.1.1.16 root 2994: //fprintf ( stderr , "add timerb pos=%d\n" , Pos );
1.1.1.15 root 2995: if ( !bUseVDIRes )
2996: Video_AddInterrupt ( Pos , INTERRUPT_VIDEO_ENDLINE );
2997: }
2998:
2999:
3000: /**
3001: * Add some video interrupts to handle the first HBL and the first Timer B
3002: * in a new VBL. Also add an interrupt to trigger the next VBL.
3003: * This function is called from the VBL, so we use PendingCycleOver to take into account
3004: * the possible delay occurring when the VBL was executed.
1.1.1.16 root 3005: * In monochrome mode (71 Hz) a line is 224 cycles, which means if VBL is delayed
3006: * by a DIVS, FrameCycles can already be > 224 and we need to add an immediate
3007: * interrupt for hbl/timer in the next 4/8 cycles (else crash might happen as
3008: * line 0 processing would be skipped).
1.1.1.11 root 3009: */
1.1.1.15 root 3010: void Video_StartInterrupts ( int PendingCyclesOver )
1.1.1.11 root 3011: {
1.1.1.16 root 3012: int FrameCycles , HblCounterVideo , LineCycles;
3013: int Pos;
3014:
1.1.1.15 root 3015: /* HBL/Timer B are not emulated in VDI mode */
1.1.1.14 root 3016: if (!bUseVDIRes)
3017: {
1.1.1.16 root 3018: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 3019:
1.1.1.16 root 3020: /* Set Timer B interrupt for line 0 */
3021: Pos = Video_TimerB_GetPos ( 0 );
3022: if ( Pos > FrameCycles ) /* check Pos for line 0 was not already reached */
3023: Video_AddInterruptTimerB ( Pos );
3024: else /* the VBL was delayed by more than 1 HBL, add an immediate timer B */
3025: {
3026: LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d delayed too much video_cyc=%d >= pos=%d for first timer B, add immediate timer B\n" ,
3027: nVBLs , FrameCycles , Pos );
3028: CycInt_AddRelativeInterrupt ( 4 , INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
3029: }
3030:
3031: /* Set HBL interrupt for line 0 */
3032: Pos = Video_HBL_GetPos();
3033: if ( Pos > FrameCycles ) /* check Pos for line 0 was not already reached */
3034: Video_AddInterruptHBL ( Pos );
3035: else /* the VBL was delayed by more than 1 HBL, add an immediate HBL */
3036: {
3037: LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d delayed too much video_cyc=%d >= pos=%d for first HBL, add immediate HBL\n" ,
3038: nVBLs , FrameCycles , Pos );
3039: CycInt_AddRelativeInterrupt ( 8 , INT_CPU_CYCLE, INTERRUPT_VIDEO_HBL ); /* use 8 instead of 4 to happen after immediate timer b */
3040: }
1.1.1.14 root 3041: }
1.1.1.15 root 3042:
3043: /* TODO replace CYCLES_PER_FRAME */
1.1.1.16 root 3044: CyclesPerVBL = CYCLES_PER_FRAME;
1.1.1.18 root 3045: /* Note: Refresh rate less than 50 Hz does not make sense! */
3046: assert(CyclesPerVBL <= CPU_FREQ/49);
3047: /* Add new VBL interrupt: */
1.1.1.16 root 3048: CycInt_AddRelativeInterrupt(CyclesPerVBL - PendingCyclesOver, INT_CPU_CYCLE, INTERRUPT_VIDEO_VBL);
1.1.1.11 root 3049: }
3050:
3051:
3052: /*-----------------------------------------------------------------------*/
3053: /**
1.1.1.15 root 3054: * VBL interrupt : set new interrupts, draw screen, generate sound,
3055: * reset counters, ...
1.1.1.11 root 3056: */
1.1.1.15 root 3057: void Video_InterruptHandler_VBL ( void )
1.1.1.11 root 3058: {
3059: int PendingCyclesOver;
1.1.1.9 root 3060:
1.1.1.21 root 3061: /* Store cycles we went over for this frame(this is our initial count) */
1.1.1.11 root 3062: PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE ); /* +ve */
1.1.1.10 root 3063:
1.1.1.11 root 3064: /* Remove this interrupt from list and re-order */
1.1.1.16 root 3065: CycInt_AcknowledgeInterrupt();
1.1.1.9 root 3066:
1.1.1.14 root 3067: /* Increment the vbl jitter index */
3068: VblJitterIndex++;
1.1.1.20 root 3069: VblJitterIndex %= VBL_JITTER_ARRAY_SIZE;
1.1.1.14 root 3070:
1.1.1.11 root 3071: /* Set frame cycles, used for Video Address */
1.1.1.13 root 3072: Cycles_SetCounter(CYCLES_COUNTER_VIDEO, PendingCyclesOver + VblVideoCycleOffset);
1.1.1.9 root 3073:
1.1.1.11 root 3074: /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
3075: Keymap_DebounceAllKeys();
1.1.1.9 root 3076:
1.1.1.11 root 3077: Video_DrawScreen();
1.1.1.9 root 3078:
1.1.1.11 root 3079: /* Check printer status */
3080: Printer_CheckIdleStatus();
3081:
3082: /* Update counter for number of screen refreshes per second */
3083: nVBLs++;
3084: /* Set video registers for frame */
3085: Video_ClearOnVBL();
1.1.1.15 root 3086:
1.1.1.17 root 3087: /* Videl Vertical counter reset (To be removed when Videl emulation is finished) */
3088: if (ConfigureParams.System.nMachineType == MACHINE_FALCON) {
3089: vfc_counter = 0;
3090: }
3091:
1.1.1.15 root 3092: /* Since we don't execute HBL functions in VDI mode, we've got to
3093: * initialize the first HBL palette here when VDI mode is enabled. */
3094: if (bUseVDIRes)
3095: Video_StoreFirstLinePalette();
3096:
3097: /* Start VBL, HBL and Timer B interrupts (this must be done after resetting
3098: * video cycle counter setting default freq values in Video_ClearOnVBL) */
3099: Video_StartInterrupts(PendingCyclesOver);
3100:
3101: /* Act on shortcut keys */
3102: ShortCut_ActKey();
3103:
1.1.1.21 root 3104: /* Update the IKBD's internal clock */
3105: IKBD_UpdateClockOnVBL ();
3106:
1.1.1.16 root 3107: /* Record video frame is necessary */
3108: if ( bRecordingAvi )
3109: Avi_RecordVideoStream ();
3110:
1.1.1.11 root 3111: /* Store off PSG registers for YM file, is enabled */
3112: YMFormat_UpdateRecording();
3113: /* Generate 1/50th second of sound sample data, to be played by sound thread */
3114: Sound_Update_VBL();
3115:
1.1.1.15 root 3116: LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
1.1.1.14 root 3117: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
1.1.1.11 root 3118:
1.1.1.21 root 3119: /* Print traces if pending VBL bit changed just before IACK when VBL interrupt is allowed */
3120: if ( ( CPU_IACK == true ) && ( regs.intmask < 4 ) )
3121: {
3122: if ( pendingInterrupts & ( 1 << 4 ) )
3123: {
3124: 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" ,
3125: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
3126: }
3127: else
3128: {
3129: LOG_TRACE ( TRACE_VIDEO_VBL , "VBL %d, new pending VBL set just before iack video_cyc=%d pending_cyc=%d jitter=%d\n" ,
3130: nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
3131: }
3132: }
3133:
3134: /* Set pending bit for VBL interrupt in the CPU IPL */
1.1.1.23! root 3135: M68000_Exception(EXCEPTION_NR_VBLANK, M68000_EXC_SRC_AUTOVEC); /* Vertical blank interrupt, level 4 */
1.1.1.11 root 3136:
3137: Main_WaitOnVbl();
1.1.1.9 root 3138: }
3139:
3140:
3141: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3142: /**
1.1.1.13 root 3143: * Write to video address base high, med and low register (0xff8201/03/0d).
3144: * On STE, when a program writes to high or med registers, base low register
3145: * is reset to zero.
1.1.1.11 root 3146: */
1.1.1.9 root 3147: void Video_ScreenBaseSTE_WriteByte(void)
3148: {
1.1.1.13 root 3149: if ( ( IoAccessCurrentAddress == 0xff8201 ) || ( IoAccessCurrentAddress == 0xff8203 ) )
3150: IoMem[0xff820d] = 0; /* Reset screen base low register */
1.1.1.8 root 3151:
1.1.1.15 root 3152: if (LOG_TRACE_LEVEL(TRACE_VIDEO_STE))
1.1.1.11 root 3153: {
1.1.1.15 root 3154: int FrameCycles, HblCounterVideo, LineCycles;
3155:
3156: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
3157:
3158: 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 3159: (IoMem[0xff8201]<<16)+(IoMem[0xff8203]<<8)+IoMem[0xff820d] ,
1.1.1.15 root 3160: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 3161: }
1.1.1.8 root 3162: }
3163:
3164: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3165: /**
3166: * Read video address counter and update ff8205/07/09
3167: */
3168: void Video_ScreenCounter_ReadByte(void)
1.1.1.8 root 3169: {
1.1.1.11 root 3170: Uint32 addr;
1.1.1.8 root 3171:
1.1.1.11 root 3172: addr = Video_CalculateAddress(); /* get current video address */
3173: IoMem[0xff8205] = ( addr >> 16 ) & 0xff;
3174: IoMem[0xff8207] = ( addr >> 8 ) & 0xff;
3175: IoMem[0xff8209] = addr & 0xff;
1.1.1.8 root 3176: }
3177:
1.1.1.9 root 3178: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3179: /**
3180: * Write to video address counter (0xff8205, 0xff8207 and 0xff8209).
3181: * Called on STE only and like with base address, you cannot set lowest bit.
1.1.1.16 root 3182: *
3183: * As Hatari processes/converts one complete video line at a time, we have 3 cases :
3184: * - If display has not started yet for this line (left border), we can change pVideoRaster now.
3185: * We must take into account that the MMU starts 16 cycles earlier when hscroll is used.
3186: * - If display has stopped for this line (right border), we will change pVideoRaster
3187: * in Video_CopyScreenLineColor using pVideoRasterDelayed once the line has been processed.
3188: * - If the write is made while display is on, then we must compute an offset of what
3189: * the new address should have been, to correctly emulate the video address at the
3190: * end of the line while taking into account the fact that the video pointer is incrementing
3191: * during the active part of the line (this is the most "tricky" case)
3192: *
3193: * 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 3194: */
1.1.1.9 root 3195: void Video_ScreenCounter_WriteByte(void)
3196: {
1.1.1.11 root 3197: Uint8 AddrByte;
1.1.1.16 root 3198: Uint32 addr_cur;
3199: Uint32 addr_new = 0;
1.1.1.15 root 3200: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.13 root 3201: int Delayed;
1.1.1.16 root 3202: int MMUStartCycle;
1.1.1.11 root 3203:
1.1.1.15 root 3204: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.11 root 3205:
3206: AddrByte = IoMem[ IoAccessCurrentAddress ];
3207:
1.1.1.16 root 3208: /* Get current video address from the shifter */
3209: addr_cur = Video_CalculateAddress();
3210: /* Correct the address in case a modification of ff8205/07/09 was already delayed */
3211: addr_new = addr_cur + VideoCounterDelayedOffset;
3212: /* Correct the address in case video counter was already modified in the right border */
3213: if ( pVideoRasterDelayed != NULL )
3214: addr_new = pVideoRasterDelayed - STRam;
3215:
3216: /* addr_new should now be the same as on a real STE */
3217: /* Compute the new video address with one modified byte */
3218: if ( IoAccessCurrentAddress == 0xff8205 )
1.1.1.20 root 3219: addr_new = ( addr_new & 0x00ffff ) | ( ( AddrByte & 0x3f ) << 16 );
1.1.1.16 root 3220: else if ( IoAccessCurrentAddress == 0xff8207 )
3221: addr_new = ( addr_new & 0xff00ff ) | ( AddrByte << 8 );
3222: else if ( IoAccessCurrentAddress == 0xff8209 )
3223: addr_new = ( addr_new & 0xffff00 ) | ( AddrByte );
3224:
1.1.1.23! root 3225: addr_new &= ~1; /* clear bit 0 */
! 3226:
1.1.1.16 root 3227: MMUStartCycle = Video_GetMMUStartCycle ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle );
3228:
1.1.1.11 root 3229: /* If display has not started, we can still modify pVideoRaster */
1.1.1.16 root 3230: /* We must also check the write does not overlap the end of the line (to be sure Video_EndHBL is called first) */
3231: if ( ( ( LineCycles <= MMUStartCycle ) && ( nHBL == HblCounterVideo ) )
1.1.1.18 root 3232: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.11 root 3233: {
1.1.1.23! root 3234: pVideoRaster = &STRam[addr_new]; /* set new video address */
1.1.1.16 root 3235: VideoCounterDelayedOffset = 0;
3236: pVideoRasterDelayed = NULL;
1.1.1.15 root 3237: Delayed = false;
1.1.1.11 root 3238: }
3239:
1.1.1.16 root 3240: /* Display is OFF (right border) but we can't change pVideoRaster now, we must process Video_CopyScreenLineColor first */
1.1.1.18 root 3241: else if ( ( nHBL >= nStartHBL ) && ( nHBL < nEndHBL + BlankLines ) /* line should be active */
1.1.1.16 root 3242: && ( ( LineCycles > ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle ) /* we're in the right border */
3243: || ( HblCounterVideo == nHBL+1 ) ) ) /* or the write overlaps the next line and Video_EndHBL was not called yet */
3244: {
3245: VideoCounterDelayedOffset = 0;
1.1.1.23! root 3246: pVideoRasterDelayed = &STRam[addr_new]; /* new value for pVideoRaster at the end of Video_CopyScreenLineColor */
1.1.1.16 root 3247: Delayed = true;
3248: }
3249:
3250: /* Counter is modified while display is ON, store the bytes offset for Video_CopyScreenLineColor */
3251: /* Even on a real STE, modifying video address in this case will cause artefacts */
1.1.1.11 root 3252: else
3253: {
1.1.1.16 root 3254: VideoCounterDelayedOffset = addr_new - addr_cur;
3255: pVideoRasterDelayed = NULL;
1.1.1.15 root 3256: Delayed = true;
1.1.1.23! root 3257:
! 3258: /* [FIXME] 'RGBeast' by Aggression : write to FF8209 on STE while display is on, */
! 3259: /* in that case video counter is not correct */
! 3260: if ( STMemory_ReadLong ( M68000_InstrPC ) == 0x03cafffb ) /* movep.l d1,$fffb(a2) */
! 3261: VideoCounterDelayedOffset += 2;
1.1.1.11 root 3262: }
3263:
1.1.1.16 root 3264: LOG_TRACE(TRACE_VIDEO_STE , "write ste video %x val=0x%x video_old=%x video_new=%x offset=%x delayed=%s"
3265: " video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
3266: IoAccessCurrentAddress, AddrByte, addr_cur , addr_new , VideoCounterDelayedOffset , Delayed ? "yes" : "no" ,
1.1.1.15 root 3267: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.9 root 3268: }
1.1.1.8 root 3269:
3270: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3271: /**
3272: * Read video sync register (0xff820a)
3273: */
1.1.1.8 root 3274: void Video_Sync_ReadByte(void)
3275: {
1.1.1.17 root 3276: if ( (ConfigureParams.System.nMachineType == MACHINE_ST)
3277: || (ConfigureParams.System.nMachineType == MACHINE_STE)
3278: || (ConfigureParams.System.nMachineType == MACHINE_MEGA_STE) )
1.1.1.14 root 3279: IoMem[0xff820a] |= 0xfc; /* set unused bits 2-7 to 1 */
1.1.1.8 root 3280: }
3281:
3282: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3283: /**
3284: * Read video base address low byte (0xff820d). A plain ST can only store
3285: * screen addresses rounded to 256 bytes (i.e. no lower byte).
3286: */
1.1.1.8 root 3287: void Video_BaseLow_ReadByte(void)
3288: {
1.1.1.11 root 3289: if (ConfigureParams.System.nMachineType == MACHINE_ST)
3290: IoMem[0xff820d] = 0; /* On ST this is always 0 */
1.1.1.9 root 3291:
1.1.1.11 root 3292: /* Note that you should not do anything here for STe because
3293: * VideoBase address is set in an interrupt and would be wrong
3294: * here. It's fine like this.
3295: */
1.1.1.8 root 3296: }
3297:
3298: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3299: /**
3300: * Read video line width register (0xff820f)
3301: */
1.1.1.8 root 3302: void Video_LineWidth_ReadByte(void)
3303: {
1.1.1.11 root 3304: if (ConfigureParams.System.nMachineType == MACHINE_ST)
3305: IoMem[0xff820f] = 0; /* On ST this is always 0 */
1.1.1.18 root 3306:
3307: /* If we're not in STF mode, we use the value already stored in $ff820f */
1.1.1.8 root 3308: }
3309:
3310: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3311: /**
3312: * Read video shifter mode register (0xff8260)
3313: */
1.1.1.8 root 3314: void Video_ShifterMode_ReadByte(void)
3315: {
1.1.1.11 root 3316: if (bUseHighRes)
1.1.1.14 root 3317: IoMem[0xff8260] = 2; /* If mono monitor, force to high resolution */
3318:
1.1.1.17 root 3319: if (ConfigureParams.System.nMachineType == MACHINE_ST)
3320: IoMem[0xff8260] |= 0xfc; /* On STF, set unused bits 2-7 to 1 */
1.1.1.20 root 3321: else
3322: IoMem[0xff8260] &= 0x03; /* Only use bits 0 and 1, unused bits 2-7 are set to 0 */
1.1.1.8 root 3323: }
3324:
1.1.1.10 root 3325: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3326: /**
3327: * Read horizontal scroll register (0xff8265)
3328: */
1.1.1.10 root 3329: void Video_HorScroll_Read(void)
3330: {
1.1.1.11 root 3331: IoMem[0xff8265] = HWScrollCount;
1.1.1.10 root 3332: }
1.1.1.8 root 3333:
3334: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3335: /**
3336: * Write video line width register (0xff820f) - STE only.
3337: * Content of LineWidth is added to the shifter counter when display is
3338: * turned off (start of the right border, usually at cycle 376)
3339: */
1.1.1.10 root 3340: void Video_LineWidth_WriteByte(void)
3341: {
1.1.1.11 root 3342: Uint8 NewWidth;
1.1.1.15 root 3343: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.13 root 3344: int Delayed;
1.1.1.11 root 3345:
1.1.1.15 root 3346: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.11 root 3347:
3348: NewWidth = IoMem_ReadByte(0xff820f);
3349:
3350: /* We must also check the write does not overlap the end of the line */
1.1.1.15 root 3351: if ( ( ( nHBL == HblCounterVideo ) && ( LineCycles <= ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) )
1.1.1.18 root 3352: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.13 root 3353: {
1.1.1.11 root 3354: LineWidth = NewWidth; /* display is on, we can still change */
1.1.1.13 root 3355: NewLineWidth = -1; /* cancel 'pending' change */
1.1.1.15 root 3356: Delayed = false;
1.1.1.13 root 3357: }
1.1.1.11 root 3358: else
1.1.1.13 root 3359: {
1.1.1.11 root 3360: NewLineWidth = NewWidth; /* display is off, can't change LineWidth once in right border */
1.1.1.15 root 3361: Delayed = true;
1.1.1.13 root 3362: }
1.1.1.11 root 3363:
1.1.1.15 root 3364: 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 3365: NewWidth, Delayed ? "yes" : "no" ,
1.1.1.15 root 3366: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.10 root 3367: }
3368:
3369: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3370: /**
3371: * Write to video shifter palette registers (0xff8240-0xff825e)
1.1.1.14 root 3372: *
3373: * Note that there's a special "strange" case when writing only to the upper byte
3374: * of the color reg (instead of writing 16 bits at once with .W/.L).
3375: * In that case, the byte written to address x is automatically written
3376: * 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 3377: * Similarly, when writing a byte to address x+1, it's also written to address x
1.1.1.14 root 3378: * So : move.w #0,$ff8240 -> color 0 is now $000
3379: * move.b #7,$ff8240 -> color 0 is now $707 !
1.1.1.22 root 3380: * move.b #$55,$ff8241 -> color 0 is now $555 !
1.1.1.14 root 3381: * move.b #$71,$ff8240 -> color 0 is now $171 (bytes are first copied, then masked)
1.1.1.11 root 3382: */
1.1.1.23! root 3383: static void Video_ColorReg_WriteWord(void)
1.1.1.8 root 3384: {
1.1.1.15 root 3385: if (!bUseHighRes && !bUseVDIRes) /* Don't store if hi-res or VDI resolution */
1.1.1.11 root 3386: {
3387: int idx;
3388: Uint16 col;
1.1.1.22 root 3389: Uint32 addr;
3390: addr = IoAccessCurrentAddress;
3391:
1.1.1.11 root 3392: Video_SetHBLPaletteMaskPointers(); /* Set 'pHBLPalettes' etc.. according cycles into frame */
1.1.1.14 root 3393:
3394: /* Handle special case when writing only to the upper byte of the color reg */
3395: if ( ( nIoMemAccessSize == SIZE_BYTE ) && ( ( IoAccessCurrentAddress & 1 ) == 0 ) )
3396: col = ( IoMem_ReadByte(addr) << 8 ) + IoMem_ReadByte(addr); /* copy upper byte into lower byte */
1.1.1.22 root 3397: /* Same when writing only to the lower byte of the color reg */
3398: else if ( ( nIoMemAccessSize == SIZE_BYTE ) && ( ( IoAccessCurrentAddress & 1 ) == 1 ) )
3399: col = ( IoMem_ReadByte(addr) << 8 ) + IoMem_ReadByte(addr); /* copy lower byte into upper byte */
3400: /* Usual case, writing a word or a long (2 words) */
3401: else
3402: col = IoMem_ReadWord(addr);
1.1.1.14 root 3403:
1.1.1.11 root 3404: if (ConfigureParams.System.nMachineType == MACHINE_ST)
1.1.1.22 root 3405: col &= 0x777; /* Mask off to ST 512 palette */
1.1.1.11 root 3406: else
1.1.1.22 root 3407: col &= 0xfff; /* Mask off to STe 4096 palette */
3408:
3409: addr &= 0xfffffffe; /* Ensure addr is even to store the 16 bit color */
3410:
1.1.1.11 root 3411: IoMem_WriteWord(addr, col); /* (some games write 0xFFFF and read back to see if STe) */
3412: Spec512_StoreCyclePalette(col, addr); /* Store colour into CyclePalettes[] */
3413: idx = (addr-0xff8240)/2; /* words */
3414: pHBLPalettes[idx] = col; /* Set colour x */
3415: *pHBLPaletteMasks |= 1 << idx; /* And mask */
3416:
1.1.1.15 root 3417: if (LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
1.1.1.11 root 3418: {
1.1.1.15 root 3419: int FrameCycles, HblCounterVideo, LineCycles;
3420:
3421: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
3422:
3423: 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 3424: IoAccessCurrentAddress, col,
1.1.1.15 root 3425: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 3426: }
3427:
3428: }
1.1.1.8 root 3429: }
3430:
1.1.1.22 root 3431: /*
3432: * Read from video shifter palette registers (0xff8240-0xff825e)
3433: *
3434: * NOTE [NP] : On STF, only 3 bits are used for RGB (instead of 4 on STE) ;
3435: * the content of bits 3, 7 and 11 is not defined and will be 0 or 1
3436: * depending on the latest activity on the BUS (last word access by the CPU or
3437: * the shifter). As precisely emulating these bits is quite complicated,
3438: * we use random values for now.
3439: * NOTE [NP] : When executing code from the IO addresses between 0xff8240-0xff825e
3440: * the unused bits on STF are set to '0' (used in "The Union Demo" protection).
3441: * So we use rand() only if PC is located in RAM.
3442: */
1.1.1.23! root 3443: static void Video_ColorReg_ReadWord(void)
1.1.1.22 root 3444: {
3445: Uint16 col;
3446: Uint32 addr;
3447: addr = IoAccessCurrentAddress;
3448:
3449: col = IoMem_ReadWord(addr);
3450:
3451: if ( (ConfigureParams.System.nMachineType == MACHINE_ST)
3452: && ( M68000_GetPC() < 0x400000 ) ) /* PC in RAM < 4MB */
3453: {
3454: col = ( col & 0x777 ) | ( rand() & 0x888 );
3455: IoMem_WriteWord ( addr , col );
3456: }
3457:
3458: if (LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
3459: {
3460: int FrameCycles, HblCounterVideo, LineCycles;
3461:
3462: Video_GetPosition_OnReadAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
3463:
3464: 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" ,
3465: IoAccessCurrentAddress, col,
3466: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
3467: }
3468: }
3469:
3470: /*
3471: * [NP] TODO : due to how .L accesses are handled in ioMem.c, we can't call directly
3472: * Video_ColorReg_WriteWord from ioMemTabST.c / ioMemTabSTE.c, we must use an intermediate
3473: * function, else .L accesses will not change 2 .W color regs, but only one.
3474: * This should be changed in ioMem.c to do 2 separate .W accesses, as would do a real 68000
3475: */
3476:
1.1.1.8 root 3477: void Video_Color0_WriteWord(void)
3478: {
1.1.1.22 root 3479: Video_ColorReg_WriteWord();
1.1.1.8 root 3480: }
3481:
3482: void Video_Color1_WriteWord(void)
3483: {
1.1.1.22 root 3484: Video_ColorReg_WriteWord();
1.1.1.8 root 3485: }
3486:
3487: void Video_Color2_WriteWord(void)
3488: {
1.1.1.22 root 3489: Video_ColorReg_WriteWord();
1.1.1.8 root 3490: }
3491:
3492: void Video_Color3_WriteWord(void)
3493: {
1.1.1.22 root 3494: Video_ColorReg_WriteWord();
1.1.1.8 root 3495: }
3496:
3497: void Video_Color4_WriteWord(void)
3498: {
1.1.1.22 root 3499: Video_ColorReg_WriteWord();
1.1.1.8 root 3500: }
3501:
3502: void Video_Color5_WriteWord(void)
3503: {
1.1.1.22 root 3504: Video_ColorReg_WriteWord();
1.1.1.8 root 3505: }
3506:
3507: void Video_Color6_WriteWord(void)
3508: {
1.1.1.22 root 3509: Video_ColorReg_WriteWord();
1.1.1.8 root 3510: }
3511:
3512: void Video_Color7_WriteWord(void)
3513: {
1.1.1.22 root 3514: Video_ColorReg_WriteWord();
1.1.1.8 root 3515: }
3516:
3517: void Video_Color8_WriteWord(void)
3518: {
1.1.1.22 root 3519: Video_ColorReg_WriteWord();
1.1.1.8 root 3520: }
3521:
3522: void Video_Color9_WriteWord(void)
3523: {
1.1.1.22 root 3524: Video_ColorReg_WriteWord();
1.1.1.8 root 3525: }
3526:
3527: void Video_Color10_WriteWord(void)
3528: {
1.1.1.22 root 3529: Video_ColorReg_WriteWord();
1.1.1.8 root 3530: }
3531:
3532: void Video_Color11_WriteWord(void)
3533: {
1.1.1.22 root 3534: Video_ColorReg_WriteWord();
1.1.1.8 root 3535: }
3536:
3537: void Video_Color12_WriteWord(void)
3538: {
1.1.1.22 root 3539: Video_ColorReg_WriteWord();
1.1.1.8 root 3540: }
3541:
3542: void Video_Color13_WriteWord(void)
3543: {
1.1.1.22 root 3544: Video_ColorReg_WriteWord();
1.1.1.8 root 3545: }
3546:
3547: void Video_Color14_WriteWord(void)
3548: {
1.1.1.22 root 3549: Video_ColorReg_WriteWord();
1.1.1.8 root 3550: }
3551:
3552: void Video_Color15_WriteWord(void)
3553: {
1.1.1.22 root 3554: Video_ColorReg_WriteWord();
3555: }
3556:
3557:
3558: void Video_Color0_ReadWord(void)
3559: {
3560: Video_ColorReg_ReadWord();
3561: }
3562:
3563: void Video_Color1_ReadWord(void)
3564: {
3565: Video_ColorReg_ReadWord();
3566: }
3567:
3568: void Video_Color2_ReadWord(void)
3569: {
3570: Video_ColorReg_ReadWord();
3571: }
3572:
3573: void Video_Color3_ReadWord(void)
3574: {
3575: Video_ColorReg_ReadWord();
3576: }
3577:
3578: void Video_Color4_ReadWord(void)
3579: {
3580: Video_ColorReg_ReadWord();
3581: }
3582:
3583: void Video_Color5_ReadWord(void)
3584: {
3585: Video_ColorReg_ReadWord();
3586: }
3587:
3588: void Video_Color6_ReadWord(void)
3589: {
3590: Video_ColorReg_ReadWord();
3591: }
3592:
3593: void Video_Color7_ReadWord(void)
3594: {
3595: Video_ColorReg_ReadWord();
3596: }
3597:
3598: void Video_Color8_ReadWord(void)
3599: {
3600: Video_ColorReg_ReadWord();
3601: }
3602:
3603: void Video_Color9_ReadWord(void)
3604: {
3605: Video_ColorReg_ReadWord();
3606: }
3607:
3608: void Video_Color10_ReadWord(void)
3609: {
3610: Video_ColorReg_ReadWord();
3611: }
3612:
3613: void Video_Color11_ReadWord(void)
3614: {
3615: Video_ColorReg_ReadWord();
3616: }
3617:
3618: void Video_Color12_ReadWord(void)
3619: {
3620: Video_ColorReg_ReadWord();
3621: }
3622:
3623: void Video_Color13_ReadWord(void)
3624: {
3625: Video_ColorReg_ReadWord();
3626: }
3627:
3628: void Video_Color14_ReadWord(void)
3629: {
3630: Video_ColorReg_ReadWord();
3631: }
3632:
3633: void Video_Color15_ReadWord(void)
3634: {
3635: Video_ColorReg_ReadWord();
1.1.1.8 root 3636: }
3637:
3638:
3639: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3640: /**
3641: * Write video shifter mode register (0xff8260)
3642: */
1.1.1.8 root 3643: void Video_ShifterMode_WriteByte(void)
3644: {
1.1.1.20 root 3645: Uint8 VideoShifterByte;
3646:
3647: if (ConfigureParams.System.nMachineType == MACHINE_TT)
3648: {
3649: TTRes = IoMem_ReadByte(0xff8260) & 7;
3650: /* Copy to TT shifter mode register: */
3651: IoMem_WriteByte(0xff8262, TTRes);
1.1.1.21 root 3652:
3653: bTTSampleHold = false;
3654: bTTHypermono = false;
1.1.1.20 root 3655: }
3656: else if (!bUseVDIRes) /* ST and STE mode */
1.1.1.11 root 3657: {
1.1.1.18 root 3658: /* We only care for lower 2-bits */
3659: VideoShifterByte = IoMem[0xff8260] & 3;
1.1.1.22 root 3660: /* 3 is not a valid resolution, use high res instead */
1.1.1.18 root 3661: if ( VideoShifterByte == 3 )
1.1.1.13 root 3662: {
1.1.1.22 root 3663: VideoShifterByte = 2;
3664: IoMem_WriteByte(0xff8260,2);
1.1.1.13 root 3665: }
1.1.1.18 root 3666:
1.1.1.11 root 3667: Video_WriteToShifter(VideoShifterByte);
3668: Video_SetHBLPaletteMaskPointers();
3669: *pHBLPaletteMasks &= 0xff00ffff;
3670: /* Store resolution after palette mask and set resolution write bit: */
3671: *pHBLPaletteMasks |= (((Uint32)VideoShifterByte|0x04)<<16);
3672: }
1.1.1.10 root 3673: }
3674:
3675: /*-----------------------------------------------------------------------*/
1.1.1.11 root 3676: /**
1.1.1.13 root 3677: * Handle horizontal scrolling to the left.
3678: * On STE, there're 2 registers that can scroll the line :
3679: * - $ff8264 : scroll without prefetch
1.1.1.14 root 3680: * - $ff8265 : scroll with prefetch
1.1.1.13 root 3681: * Both registers will scroll the line to the left by skipping the amount
3682: * of pixels in $ff8264 or $ff8265 (from 0 to 15).
3683: * As some pixels will be skipped, this means the shifter needs to read
3684: * 16 other pixels in advance in some internal registers to have an uninterrupted flow of pixels.
3685: *
1.1.1.14 root 3686: * These 16 pixels can be prefetched before the display starts (on cycle 56 for example) when using
1.1.1.13 root 3687: * $ff8265 to scroll the line. In that case 8 more bytes per line (low res) will be read. Most programs
3688: * are using $ff8265 to scroll the line.
3689: *
3690: * When using $ff8264, the next 16 pixels will not be prefetched before the display
3691: * starts, they will be read when the display normally starts (cycle 56). While
3692: * reading these 16 pixels, the shifter won't be able to display anything, which will
3693: * result in 16 pixels having the color 0. So, reading the 16 pixels will in fact delay
3694: * the real start of the line, which will look as if it started 16 pixels later. As the
3695: * shifter will stop the display at cycle 56+320 anyway, this means the last 16 pixels
3696: * of each line won't be displayed and you get the equivalent of a shorter 304 pixels line.
3697: * As a consequence, this register is rarely used to scroll the line.
3698: *
1.1.1.21 root 3699: * By writing a value > 0 in $ff8265 (to start prefetching) and immediately after a value of 0
1.1.1.13 root 3700: * in $ff8264 (no scroll and no prefetch), it's possible to fill the internal registers used
3701: * for the scrolling even if scrolling is set to 0. In that case, the shifter will start displaying
3702: * each line 16 pixels earlier (as the data are already available in the internal registers).
3703: * This allows to have 336 pixels per line (instead of 320) for all the remaining lines on the screen.
3704: *
3705: * Although some programs are using this sequence :
3706: * move.w #1,$ffff8264 ; Word access!
3707: * clr.b $ffff8264 ; Byte access!
3708: * It is also possible to add 16 pixels by doing :
3709: * move.b #X,$ff8265 ; with X > 0
3710: * move.b #0,$ff8264
1.1.1.11 root 3711: * Some games (Obsession, Skulls) and demos (Pacemaker by Paradox) use this
3712: * feature to increase the resolution, so we have to emulate this bug, too!
1.1.1.13 root 3713: *
3714: * So considering a low res line of 320 pixels (160 bytes) :
3715: * - if both $ff8264/65 are 0, no scrolling happens, the shifter reads 160 bytes and displays 320 pixels (same as STF)
3716: * - if $ff8265 > 0, line is scrolled, the shifter reads 168 bytes and displays 320 pixels.
3717: * - if $ff8264 > 0, line is scrolled, the shifter reads 160 bytes and displays 304 pixels,
3718: * the display starts 16 pixels later.
3719: * - if $ff8265 > 0 and then $ff8264 = 0, there's no scrolling, the shifter reads 168 bytes and displays 336 pixels,
3720: * the display starts 16 pixels earlier.
1.1.1.11 root 3721: */
1.1.1.13 root 3722:
3723: void Video_HorScroll_Write_8264(void)
3724: {
3725: Video_HorScroll_Write();
3726: }
3727:
3728: void Video_HorScroll_Write_8265(void)
3729: {
3730: Video_HorScroll_Write();
3731: }
3732:
1.1.1.10 root 3733: void Video_HorScroll_Write(void)
3734: {
1.1.1.13 root 3735: Uint32 RegAddr;
1.1.1.11 root 3736: Uint8 ScrollCount;
1.1.1.13 root 3737: Uint8 Prefetch;
1.1.1.15 root 3738: int FrameCycles, HblCounterVideo, LineCycles;
3739: bool Add16px = false;
1.1.1.13 root 3740: static Uint8 LastVal8265 = 0;
3741: int Delayed;
3742:
1.1.1.15 root 3743: Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.10 root 3744:
1.1.1.13 root 3745: RegAddr = IoAccessCurrentAddress; /* 0xff8264 or 0xff8265 */
3746: ScrollCount = IoMem[ RegAddr ];
1.1.1.11 root 3747: ScrollCount &= 0x0f;
1.1.1.10 root 3748:
1.1.1.13 root 3749: if ( RegAddr == 0xff8264 )
3750: {
3751: Prefetch = 0; /* scroll without prefetch */
1.1.1.15 root 3752: LastCycleScroll8264 = FrameCycles;
1.1.1.11 root 3753:
1.1.1.15 root 3754: ShifterFrame.Scroll8264Pos.VBL = nVBLs;
3755: ShifterFrame.Scroll8264Pos.FrameCycles = FrameCycles;
3756: ShifterFrame.Scroll8264Pos.HBL = HblCounterVideo;
3757: ShifterFrame.Scroll8264Pos.LineCycles = LineCycles;
3758:
3759: if ( ( ScrollCount == 0 ) && ( LastVal8265 > 0 )
3760: && ( ShifterFrame.Scroll8265Pos.VBL > 0 ) /* a write to ff8265 has been made */
3761: && ( ShifterFrame.Scroll8265Pos.VBL == ShifterFrame.Scroll8264Pos.VBL ) /* during the same VBL */
3762: && ( ShifterFrame.Scroll8264Pos.FrameCycles - ShifterFrame.Scroll8265Pos.FrameCycles <= 40 ) )
1.1.1.13 root 3763: {
1.1.1.15 root 3764: LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect ste left+16 pixels\n" );
3765: Add16px = true;
1.1.1.13 root 3766: }
3767: }
3768: else
1.1.1.11 root 3769: {
1.1.1.13 root 3770: Prefetch = 1; /* scroll with prefetch */
1.1.1.15 root 3771: LastCycleScroll8265 = FrameCycles;
3772:
3773: ShifterFrame.Scroll8265Pos.VBL = nVBLs;
3774: ShifterFrame.Scroll8265Pos.FrameCycles = FrameCycles;
3775: ShifterFrame.Scroll8265Pos.HBL = HblCounterVideo;
3776: ShifterFrame.Scroll8265Pos.LineCycles = LineCycles;
3777:
1.1.1.13 root 3778: LastVal8265 = ScrollCount;
1.1.1.15 root 3779: Add16px = false;
1.1.1.11 root 3780: }
1.1.1.13 root 3781:
3782:
3783: /* If the write was made before display starts on the current line, then */
3784: /* we can still change the value now. Else, the new values will be used */
3785: /* for line n+1. */
3786: /* We must also check the write does not overlap the end of the line */
1.1.1.15 root 3787: if ( ( ( LineCycles <= LINE_START_CYCLE_50 ) && ( nHBL == HblCounterVideo ) )
1.1.1.18 root 3788: || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.11 root 3789: {
1.1.1.13 root 3790: HWScrollCount = ScrollCount; /* display has not started, we can still change */
3791: HWScrollPrefetch = Prefetch;
3792: bSteBorderFlag = Add16px;
3793: NewHWScrollCount = -1; /* cancel 'pending' change */
1.1.1.15 root 3794: Delayed = false;
1.1.1.11 root 3795: }
3796: else
3797: {
1.1.1.13 root 3798: NewHWScrollCount = ScrollCount; /* display has started, can't change HWScrollCount now */
3799: NewHWScrollPrefetch = Prefetch;
3800: if ( Add16px )
3801: NewSteBorderFlag = 1;
3802: else
3803: NewSteBorderFlag = 0;
1.1.1.15 root 3804: Delayed = true;
1.1.1.11 root 3805: }
3806:
1.1.1.15 root 3807: 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 3808: RegAddr , ScrollCount, Delayed ? "yes" : "no" ,
1.1.1.15 root 3809: FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.13 root 3810: }
1.1.1.11 root 3811:
3812: /*-----------------------------------------------------------------------*/
3813: /**
3814: * Write to TT shifter mode register (0xff8262)
3815: */
3816: void Video_TTShiftMode_WriteWord(void)
3817: {
3818: TTRes = IoMem_ReadByte(0xff8262) & 7;
1.1.1.21 root 3819: TTSpecialVideoMode = IoMem_ReadByte(0xff8262) & 0x90;
1.1.1.11 root 3820:
3821: /*fprintf(stderr, "Write to FF8262: %x, res=%i\n", IoMem_ReadWord(0xff8262), TTRes);*/
3822:
3823: /* Is it an ST compatible resolution? */
3824: if (TTRes <= 2)
3825: {
3826: IoMem_WriteByte(0xff8260, TTRes);
3827: Video_ShifterMode_WriteByte();
1.1.1.21 root 3828: IoMem_WriteByte(0xff8262, TTRes | TTSpecialVideoMode);
3829: }
3830:
3831: if(TTSpecialVideoMode & 0x80)
3832: {
3833: bTTSampleHold = true;
3834: }
3835: else
3836: {
3837: bTTSampleHold = false;
3838: }
3839:
3840: if(TTSpecialVideoMode & 0x10)
3841: {
3842: bTTHypermono = true;
3843: }
3844: else
3845: {
3846: bTTHypermono = false;
1.1.1.11 root 3847: }
3848: }
3849:
3850: /*-----------------------------------------------------------------------*/
3851: /**
3852: * Write to TT color register (0xff8400)
3853: */
3854: void Video_TTColorRegs_WriteWord(void)
3855: {
1.1.1.15 root 3856: bTTColorsSync = false;
1.1.1.11 root 3857: }
3858:
3859: /*-----------------------------------------------------------------------*/
3860: /**
1.1.1.15 root 3861: * Write to ST color register on TT (0xff8240)
1.1.1.11 root 3862: */
3863: void Video_TTColorSTRegs_WriteWord(void)
3864: {
1.1.1.15 root 3865: bTTColorsSTSync = false;
1.1.1.8 root 3866: }
1.1.1.22 root 3867:
3868:
3869: /*-----------------------------------------------------------------------*/
3870: /**
3871: * display video related information (for debugger info command)
3872: */
1.1.1.23! root 3873: void Video_Info(FILE *fp, Uint32 dummy)
1.1.1.22 root 3874: {
3875: const char *mode;
3876: switch (OverscanMode) {
3877: case OVERSCANMODE_NONE:
3878: mode = "none";
3879: break;
3880: case OVERSCANMODE_TOP:
3881: mode = "top";
3882: break;
3883: case OVERSCANMODE_BOTTOM:
3884: mode = "bottom";
3885: break;
3886: case OVERSCANMODE_TOP|OVERSCANMODE_BOTTOM:
3887: mode = "top+bottom";
3888: break;
3889: default:
3890: mode = "unknown";
3891: }
1.1.1.23! root 3892: fprintf(fp, "Video base : 0x%x\n", VideoBase);
! 3893: fprintf(fp, "VBL counter : %d\n", nVBLs);
! 3894: fprintf(fp, "HBL line : %d\n", nHBL);
! 3895: fprintf(fp, "V-overscan : %s\n", mode);
! 3896: fprintf(fp, "Refresh rate : %d Hz\n", nScreenRefreshRate);
! 3897: fprintf(fp, "Frame skips : %d\n", nFrameSkips);
1.1.1.22 root 3898:
3899: /* TODO: any other information that would be useful to show? */
3900: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.