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