Annotation of hatari/src/video.c, revision 1.1.1.23

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.