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