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