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