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

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'*/
1.1.1.24! root      335: /*                     protection code running at address $ff8240).                            */
1.1.1.22  root      336: /* 2014/03/21  [NP]    For STE in med res overscan at 60 Hz, add a 3 pixels shift to have      */
                    337: /*                     bitmaps and color changes synchronised (fix 'HighResMode' by Paradox).  */
                    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').                                                           */
1.1.1.24! root      350: /* 2015/08/18  [NP]    In Video_CalculateAddress, handle the case when reading overlaps end    */
1.1.1.23  root      351: /*                     of line / start of next line and STE's linewidth at $FF820F != 0.       */
1.1.1.24! root      352: /* 2015/09/28  [NP]    In Video_ScreenCounter_ReadByte, take VideoCounterDelayedOffset into    */
        !           353: /*                     account to handle the case where ff8205/07/09 are modified when display */
        !           354: /*                     is ON and read just after (this is sometimes used to detect if the      */
        !           355: /*                     machine is an STF or an STE) (fix STE detection in the Menu screen of   */
        !           356: /*                     the 'Place To Be Again' demo).                                          */
        !           357: /* 2015/09/29  [NP]    Add different values for RestartVideoCounterCycle when using 60 Hz      */
        !           358: /*                     (fix 60 Hz spectrum 512 double buffer image in the intro of the         */
        !           359: /*                     'Place To Be Again' demo)                                               */
        !           360: /* 2015/10/30  [NP]    In Video_CopyScreenLineColor, correctly show the last 8 pixels on       */
        !           361: /*                     the right when displaying an STE 224 byte overscan line containing      */
        !           362: /*                     416 usable pixels (eg 'Drone' by DHS, 'PhotoChrome Viewer' by DML)      */
        !           363: /* 2015/11/15  [NP]    Call ShortCut_ActKey() earlier in Video_InterruptHandler_VBL, before    */
        !           364: /*                     acknowledging it to get more consistent state in memory snapshots       */
        !           365: /*                     created using shortcut keys.                                            */
        !           366: /* 2015/12/31  [NP]    More accurate reloading of $ff8201/03 into $ff8205/07/09 at line 310 on */
        !           367: /*                     cycle 48 (STF 50 Hz) and line 260 cycle 48 (STF 60 Hz) (used in Intro   */
        !           368: /*                     and Menu of 'Dark Side Of The Spoon' by ULM).                           */
        !           369: /* 2016/01/15  [NP]    Don't call Video_AddInterruptHBL if we're handling the special HBL to   */
        !           370: /*                     restart video counter (in case freq/res are modified between            */
        !           371: /*                     cycles 0 and 48)                                                        */
        !           372: /* 2016/01/31  [NP]    Video registers can be accessed at cycles 4n or 4n+2, except colors and */
        !           373: /*                     resolution which must use M68000_SyncCpuBus_OnRead/WriteAccess() to be  */
        !           374: /*                     at 4n. This requires to use CPU in cycle exact mode.                    */
        !           375: /* 2016/02/04  [NP]    On STF, the switch back to low res to remove left border should be made */
        !           376: /*                     at pos > 4. If made before, left border is not removed.                 */
        !           377: /* 2016/02/04  [NP]    Add support for left+2 at 50 Hz if switch back to 50 Hz is made at      */
        !           378: /*                     pos 54 (requires 2 cycle precision in CE mode) (wsdetect by Troed/Sync, */
        !           379: /*                     report STF as WS1)                                                      */
        !           380: /* 2016/02/19  [NP]    Improve blank line detection when switching to 60 Hz at cycle 28 and    */
        !           381: /*                     switching back to 50 Hz before cycle 56 (allow combining blank line     */
        !           382: /*                     and left+2 in 'Closure' by Sync)                                        */
        !           383: /* 2016/02/22  [NP]    Better accuracy for the number of cycles per VBL when mixing 50 Hz and  */
        !           384: /*                     60 Hz line. Instead of starting next VBL from the current one and       */
        !           385: /*                     updating its delay on each freq change, we start it from the last HBL   */
        !           386: /*                     of the screen (which should be more similar to how real HW works)       */
        !           387: /*                     (eg 160240 cycles per VBL in "keyboard no jitter" test program by NyH). */
        !           388: /* 2016/02/26  [NP]    Add support for 'remove left' including a med res stabiliser, by doing  */
        !           389: /*                     hi/med/low switches at cycles 0/8/16 ('Closure' by Sync).               */
        !           390: /* 2016/03/15  [NP]    Allow to have different video timings for all machines/wakeup states    */
        !           391: /* 2016/05/10  [NP]    Rewrite freq/res registers handling into Video_Update_Glue_State(),     */
        !           392: /*                     taking all STF wakeup states into account ('Closure' by Sync and        */
        !           393: /*                     'shforstv' by Paulo Simoes work in all 4 wakeup states, TCB screen in   */
        !           394: /*                     Swedish New Year has a centered logo in WS2, Nostalgic demo main menu   */
        !           395: /*                     by Oxygene is correctly broken in WS2/4).                               */
        !           396: /* 2016/05/25  [NP]    Rewrite top/bottom border handling into Video_Update_Glue_State()       */
        !           397: /* 2016/06/06  [NP]    Add preliminary support for STE timings in Video_Update_Glue_State().   */
        !           398: /*                     Similar to STF timings, with support for STE specific "left+20".        */
        !           399: /*                     (fix "We Were @" by Oxygene, "More or less 0", "Sea of colors" and more */
        !           400: /*                     demos by DHS)                                                           */
        !           401: /* 2016/06/07  [NP]    In Video_Update_Glue_State(), add STE timings for "0 byte" and "left+2" */
        !           402: /*                     lines (fix "LoSTE" and "Closure" by Sync in STE mode)                   */
1.1.1.11  root      403: 
1.1.1.12  root      404: 
1.1.1.15  root      405: const char Video_fileid[] = "Hatari video.c : " __DATE__ " " __TIME__;
1.1       root      406: 
1.1.1.7   root      407: #include <SDL_endian.h>
1.1.1.4   root      408: 
1.1       root      409: #include "main.h"
1.1.1.6   root      410: #include "configuration.h"
1.1.1.10  root      411: #include "cycles.h"
1.1       root      412: #include "fdc.h"
1.1.1.16  root      413: #include "cycInt.h"
1.1.1.8   root      414: #include "ioMem.h"
1.1.1.4   root      415: #include "keymap.h"
1.1       root      416: #include "m68000.h"
1.1.1.21  root      417: #include "hatari-glue.h"
1.1       root      418: #include "memorySnapShot.h"
                    419: #include "mfp.h"
1.1.1.11  root      420: #include "printer.h"
1.1       root      421: #include "screen.h"
1.1.1.24! root      422: #include "screenConvert.h"
1.1.1.13  root      423: #include "screenSnapShot.h"
1.1       root      424: #include "shortcut.h"
                    425: #include "sound.h"
1.1.1.17  root      426: #include "dmaSnd.h"
1.1       root      427: #include "spec512.h"
                    428: #include "stMemory.h"
                    429: #include "vdi.h"
                    430: #include "video.h"
                    431: #include "ymFormat.h"
1.1.1.11  root      432: #include "falcon/videl.h"
1.1.1.16  root      433: #include "avi_record.h"
1.1.1.21  root      434: #include "ikbd.h"
1.1.1.22  root      435: #include "floppy_ipf.h"
1.1.1.4   root      436: 
                    437: 
1.1.1.11  root      438: /* The border's mask allows to keep track of all the border tricks             */
                    439: /* applied to one video line. The masks for all lines are stored in the array  */
                    440: /* ScreenBorderMask[].                                                         */
                    441: /* - bits 0-15 are used to describe the border tricks.                         */
                    442: /* - bits 20-23 are used to store the bytes offset to apply for some particular        */
1.1.1.15  root      443: /*   tricks (for example med res overscan can shift display by 0 or 2 bytes    */
                    444: /*   depending on when the switch to med res is done after removing the left   */
1.1.1.11  root      445: /*   border).                                                                  */
                    446: 
1.1.1.14  root      447: #define BORDERMASK_NONE                        0x00    /* no effect on this line */
1.1.1.11  root      448: #define BORDERMASK_LEFT_OFF            0x01    /* removal of left border with hi/lo res switch -> +26 bytes */
                    449: #define BORDERMASK_LEFT_PLUS_2         0x02    /* line starts earlier in 60 Hz -> +2 bytes */
                    450: #define BORDERMASK_STOP_MIDDLE         0x04    /* line ends in hires at cycle 160 -> -106 bytes */
                    451: #define BORDERMASK_RIGHT_MINUS_2       0x08    /* line ends earlier in 60 Hz -> -2 bytes */
                    452: #define BORDERMASK_RIGHT_OFF           0x10    /* removal of right border -> +44 bytes */
                    453: #define BORDERMASK_RIGHT_OFF_FULL      0x20    /* full removal of right border and next left border -> +22 bytes */
1.1.1.15  root      454: #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      455: #define BORDERMASK_EMPTY_LINE          0x80    /* 60/50 Hz switch prevents the line to start, video counter is not incremented */
1.1.1.15  root      456: #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      457: #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      458: #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      459: 
1.1.1.24! root      460: #define BORDERMASK_NO_DE               0x800
        !           461: #define BORDERMASK_BLANK               0x1000
        !           462: #define BORDERMASK_NO_COUNT            0x2000
        !           463: #define BORDERMASK_NO_SYNC             0x4000
        !           464: #define BORDERMASK_SYNC_HIGH           0x8000
        !           465: 
        !           466: //#define STF_SHORT_TOP
1.1.1.11  root      467: 
                    468: int STRes = ST_LOW_RES;                         /* current ST resolution */
                    469: int TTRes;                                      /* TT shifter resolution mode */
1.1.1.13  root      470: int nFrameSkips;                                /* speed up by skipping video frames */
1.1.1.9   root      471: 
1.1.1.13  root      472: bool bUseHighRes;                               /* Use hi-res (ie Mono monitor) */
1.1.1.24! root      473: int VerticalOverscan;                          /* V_OVERSCAN_xxxx for current display frame */
1.1.1.17  root      474: Uint16 HBLPalettes[HBL_PALETTE_LINES];          /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
1.1.1.8   root      475: Uint16 *pHBLPalettes;                           /* Pointer to current palette lists, one per HBL */
1.1.1.17  root      476: Uint32 HBLPaletteMasks[HBL_PALETTE_MASKS];      /* Bit mask of palette colours changes, top bit set is resolution change */
1.1.1.9   root      477: Uint32 *pHBLPaletteMasks;
1.1.1.24! root      478: int nScreenRefreshRate = VIDEO_50HZ;           /* 50 or 60 Hz in color, 71 Hz in mono */
1.1.1.8   root      479: Uint32 VideoBase;                               /* Base address in ST Ram for screen (read on each VBL) */
1.1.1.9   root      480: 
1.1.1.11  root      481: int nVBLs;                                      /* VBL Counter */
                    482: int nHBL;                                       /* HBL line */
                    483: int nStartHBL;                                  /* Start HBL for visible screen */
                    484: int nEndHBL;                                    /* End HBL for visible screen */
1.1.1.10  root      485: int nScanlinesPerFrame = 313;                   /* Number of scan lines per frame */
                    486: int nCyclesPerLine = 512;                       /* Cycles per horizontal line scan */
1.1.1.13  root      487: static int nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ;                  /* The first line of the ST screen that is copied to the PC screen buffer */
                    488: 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      489: static int CyclesPerVBL = 313*512;             /* Number of cycles per VBL */
1.1.1.10  root      490: 
1.1.1.13  root      491: static Uint8 HWScrollCount;                    /* HW scroll pixel offset, STE only (0...15) */
                    492: static int NewHWScrollCount = -1;              /* Used in STE mode when writing to the scrolling registers $ff8264/65 */
                    493: static Uint8 HWScrollPrefetch;                 /* 0 when scrolling with $ff8264, 1 when scrolling with $ff8265 */
                    494: static int NewHWScrollPrefetch = -1;           /* Used in STE mode when writing to the scrolling registers $ff8264/65 */
                    495: static Uint8 LineWidth;                                /* Scan line width add, STe only (words, minus 1) */
                    496: static int NewLineWidth = -1;                  /* Used in STE mode when writing to the line width register $ff820f */
1.1.1.16  root      497: static int VideoCounterDelayedOffset = 0;      /* Used in STE mode when changing video counter while display is on */
                    498: 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      499: static Uint8 *pVideoRaster;                    /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
1.1.1.15  root      500: static bool bSteBorderFlag;                    /* true when screen width has been switched to 336 (e.g. in Obsession) */
1.1.1.13  root      501: static int NewSteBorderFlag = -1;              /* New value for next line */
1.1.1.24! root      502: static bool bTTColorsSync;                     /* whether TT colors need conversion to SDL */
1.1.1.21  root      503: 
1.1.1.24! root      504: int TTSpecialVideoMode;                                /* TT special video mode */
        !           505: static int nPrevTTSpecialVideoMode;            /* TT special video mode */
1.1       root      506: 
1.1.1.17  root      507: static int LastCycleScroll8264;                        /* value of Cycles_GetCounterOnWriteAccess last time ff8264 was set for the current VBL */
                    508: static int LastCycleScroll8265;                        /* value of Cycles_GetCounterOnWriteAccess last time ff8265 was set for the current VBL */
                    509: 
1.1.1.24! root      510: static bool RestartVideoCounter = false;       /* true when reaching the HBL to restart video counter */
1.1.1.13  root      511: 
1.1.1.24! root      512: int    LineTimerBPos = LINE_END_CYCLE_50 + TIMERB_VIDEO_CYCLE_OFFSET;  /* position of the Timer B interrupt on active lines */
1.1.1.15  root      513: int    TimerBEventCountCycleStart = -1;        /* value of Cycles_GetCounterOnWriteAccess last time timer B was started for the current VBL */
1.1       root      514: 
1.1.1.20  root      515: int HblJitterIndex = 0;
                    516: const int HblJitterArray[] = {
                    517:        8,4,4,0,0 /* measured on STF */
                    518: };
                    519: const int HblJitterArrayPending[] = {
                    520:        4,4,4,4,4 // { 8,8,12,8,12 }; /* measured on STF, not always accurate */
                    521: };
                    522: int VblJitterIndex = 0;
                    523: const int VblJitterArray[] = {
                    524:        8,0,4,0,4 /* measured on STF */
                    525: };
                    526: const int VblJitterArrayPending[] = {
                    527:        8,8,12,8,12 /* not verified on STF, use the same as HBL */
                    528: };
1.1.1.14  root      529: 
1.1.1.23  root      530: static int     BlankLines = 0;                 /* Number of empty line with no signal (by switching hi/lo near cycles 500) */
1.1.1.12  root      531: 
1.1.1.15  root      532: 
                    533: typedef struct
                    534: {
                    535:        int     VBL;                            /* VBL for this Pos (or -1 if Pos not defined for now) */
                    536:        int     FrameCycles;                    /* Number of cycles since this VBL */
                    537:        int     HBL;                            /* HBL in the VBL */
                    538:        int     LineCycles;                     /* cycles in the HBL */
                    539: } SHIFTER_POS;
                    540: 
                    541: 
                    542: typedef struct 
                    543: {
                    544:        int     StartCycle;                     /* first cycle of this line, as returned by Cycles_GetCounter */
                    545: 
                    546:        Uint32  BorderMask;                     /* borders' states for this line */
                    547:        int     DisplayPixelShift;              /* number of pixels to shift the whole line (<0 shift to the left, >0 shift to the right) */
                    548:                                                /* On STF, this is obtained when switching hi/med for a variable number of cycles, */
                    549:                                                /* but just removing left border will shift the line too. */
                    550: 
                    551:        int     DisplayStartCycle;              /* cycle where display starts for this line (0-512) : 0, 52 or 56 */
                    552:        int     DisplayEndCycle;                /* cycle where display ends for this line (0-512) : 0, 160, 372, 376, 460 or 512 */
                    553:        int     DisplayBytes;                   /* how many bytes to display for this line */
                    554: 
                    555: } SHIFTER_LINE;
                    556: 
                    557: 
                    558: typedef struct
                    559: {
                    560:        int     HBL_CyclePos;                   /* cycle position for the HBL int (depends on freq/res) */
                    561:        int     TimerB_CyclePos;                /* cycle position for the Timer B int (depends on freq/res) */
                    562: 
                    563:        int     Freq;                           /* value of ff820a & 2, or -1 if not set */
                    564:        int     Res;                            /* value of ff8260 & 3, or -1 if not set */
                    565:        SHIFTER_POS     FreqPos50;              /* position of latest freq change to 50 Hz*/
                    566:        SHIFTER_POS     FreqPos60;              /* position of latest freq change to 60 Hz*/
                    567:        SHIFTER_POS     ResPosLo;               /* position of latest change to low res */
                    568:        SHIFTER_POS     ResPosMed;              /* position of latest change to med res */
                    569:        SHIFTER_POS     ResPosHi;               /* position of latest change to high res */
                    570: 
                    571:        SHIFTER_POS     Scroll8264Pos;          /* position of latest write to $ff8264 */
                    572:        SHIFTER_POS     Scroll8265Pos;          /* position of latest write to $ff8265 */
                    573: 
                    574:        SHIFTER_LINE    ShifterLines[ MAX_SCANLINES_PER_FRAME ];
                    575: } SHIFTER_FRAME;
                    576: 
                    577: 
1.1.1.23  root      578: static SHIFTER_FRAME   ShifterFrame;
1.1.1.15  root      579: 
                    580: 
1.1.1.24! root      581: #define        VIDEO_TIMING_STF_WS1            0
        !           582: #define        VIDEO_TIMING_STF_WS2            1
        !           583: #define        VIDEO_TIMING_STF_WS3            2
        !           584: #define        VIDEO_TIMING_STF_WS4            3
        !           585: #define        VIDEO_TIMING_STE                4               
        !           586: #define        VIDEO_TIMING_TT                 5               
        !           587: 
        !           588: #define VIDEO_TIMING_MAX_NB            6       /* Number of different timings structs we need to store */
        !           589: 
        !           590: #define VIDEO_TIMING_DEFAULT           VIDEO_TIMING_STF_WS3
        !           591: 
        !           592: typedef struct
        !           593: {
        !           594:        const char *VideoTimingName;
        !           595: 
        !           596:        int     Preload_Start_Hi;               /*   0 (STE) */
        !           597:        int     H_Start_Hi;                     /*   4 */
        !           598:        int     Blank_Stop_Low_60;              /*  24 */
        !           599:        int     Blank_Stop_Low_50;              /*  28 */
        !           600:        int     Preload_Start_Low_60;           /*  36 (STE) */
        !           601:        int     H_Start_Low_60;                 /*  52 */
        !           602:        int     Line_Set_Pal;                   /*  54 */
        !           603:        int     Preload_Start_Low_50;           /*  40 (STE) */
        !           604:        int     H_Start_Low_50;                 /*  56 */
        !           605:        int     H_Stop_Hi;                      /* 164 */
        !           606:        int     Blank_Start_Hi;                 /* 184 */
        !           607:        int     H_Stop_Low_60;                  /* 372 */
        !           608:        int     H_Stop_Low_50;                  /* 376 */
        !           609:        int     Blank_Start_Low;                /* 450 */
        !           610:        int     HSync_Start_Offset_Low;         /* 462 or 458 */
        !           611:        int     HSync_Stop_Offset_Low;          /* 502 or 498 */
        !           612: 
        !           613:        int     RemoveTopBorder_Pos;            /* 502 */
        !           614:        int     RemoveBottomBorder_Pos;         /* 502 */
        !           615: 
        !           616:        int     V_Start_Line_50;                /*  63 or 63-16 */
        !           617:        int     V_Start_Line_60;                /*  34 */
        !           618:        int     V_Start_Line_Hi;                /*  34 */
        !           619:        int     V_End_Line_50;                  /* 263 or 263-16 */
        !           620:        int     V_End_Line_60;                  /* 234 */
        !           621:        int     V_End_Line_Hi;                  /* 434 */
        !           622:        int     V_End_Line_NoBottom_50;         /* 310 */
        !           623:        int     V_End_Line_NoBottom_60;         /* 263 */
        !           624: 
        !           625:        int     RestartVideoCounter_Line_60;    /* 260 */
        !           626:        int     RestartVideoCounter_Line_50;    /* 310 */
        !           627:        int     RestartVideoCounter_Pos;        /*  48 */
        !           628: 
        !           629:        int     VblVideoCycleOffset;
        !           630:        int     Hbl_Int_Pos_Low_60;             /* 508 */
        !           631:        int     Hbl_Int_Pos_Low_50;             /* 512 */
        !           632:        int     Hbl_Int_Pos_Hi;                 /* 224 */
        !           633: 
        !           634: } VIDEO_TIMING;
        !           635: 
        !           636: 
        !           637: static VIDEO_TIMING    VideoTimings[ VIDEO_TIMING_MAX_NB ];
        !           638: static VIDEO_TIMING    *pVideoTiming;
        !           639: static int             VideoTiming;
        !           640: 
        !           641: 
        !           642: /* Convert a horizontal video position measured at 8 MHz on STF/STE */
        !           643: /* to the equivalent number of cycles when CPU runs at 8/16/32 MHz */
        !           644: #define VIDEO_HPOS_TO_CYCLE( pos )     ( pos << nCpuFreqShift )
        !           645: #define VIDEO_CYCLE_TO_HPOS( cyc )     ( cyc >> nCpuFreqShift )
        !           646: 
1.1.1.15  root      647: 
                    648: /*--------------------------------------------------------------*/
                    649: /* Local functions prototypes                                   */
                    650: /*--------------------------------------------------------------*/
                    651: 
1.1.1.24! root      652: static void    Video_InitTimings_Copy ( VIDEO_TIMING *pSrc , VIDEO_TIMING *pDest , int inc );
1.1.1.15  root      653: 
                    654: static Uint32  Video_CalculateAddress ( void );
1.1.1.16  root      655: static int     Video_GetMMUStartCycle ( int DisplayStartCycle );
1.1.1.24! root      656: static void    Video_WriteToGlueShifterRes ( Uint8 Res );
        !           657: static void    Video_Update_Glue_State ( int FrameCycles , int HblCounterVideo , int LineCycles , bool WriteToRes );
1.1.1.15  root      658: 
1.1.1.24! root      659: static int     Video_HBL_GetDefaultPos ( void );
        !           660: static int     Video_HBL_GetCurrentPos ( void );
        !           661: static int     Video_TimerB_GetPosFromDE ( int DE_start , int DE_end );
1.1.1.18  root      662: static int     Video_TimerB_GetDefaultPos ( void );
1.1.1.15  root      663: static void    Video_EndHBL ( void );
                    664: static void    Video_StartHBL ( void );
                    665: 
                    666: static void    Video_StoreFirstLinePalette(void);
1.1.1.24! root      667: static void    Video_StoreResolution(int y , bool start);
1.1.1.15  root      668: static void    Video_CopyScreenLineMono(void);
                    669: static void    Video_CopyScreenLineColor(void);
                    670: static void    Video_SetHBLPaletteMaskPointers(void);
                    671: 
                    672: static void    Video_DrawScreen(void);
                    673: 
                    674: static void    Video_ResetShifterTimings(void);
                    675: static void    Video_InitShifterLines(void);
1.1.1.24! root      676: static void    Video_RestartVideoCounter(void);
1.1.1.15  root      677: static void    Video_ClearOnVBL(void);
                    678: 
1.1.1.24! root      679: static void    Video_AddInterrupt ( int Line , int PosCycles , interrupt_id Handler );
        !           680: static void    Video_AddInterruptHBL ( int Line , int Pos );
1.1.1.15  root      681: 
1.1.1.22  root      682: static void    Video_ColorReg_WriteWord(void);
                    683: static void    Video_ColorReg_ReadWord(void);
1.1.1.15  root      684: 
                    685: 
1.1       root      686: /*-----------------------------------------------------------------------*/
1.1.1.11  root      687: /**
                    688:  * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
                    689:  */
1.1.1.13  root      690: void Video_MemorySnapShot_Capture(bool bSave)
1.1       root      691: {
1.1.1.23  root      692:        Uint32  addr;
                    693: 
1.1.1.11  root      694:        /* Save/Restore details */
                    695:        MemorySnapShot_Store(&TTRes, sizeof(TTRes));
                    696:        MemorySnapShot_Store(&bUseHighRes, sizeof(bUseHighRes));
                    697:        MemorySnapShot_Store(&nVBLs, sizeof(nVBLs));
                    698:        MemorySnapShot_Store(&nHBL, sizeof(nHBL));
                    699:        MemorySnapShot_Store(&nStartHBL, sizeof(nStartHBL));
                    700:        MemorySnapShot_Store(&nEndHBL, sizeof(nEndHBL));
1.1.1.24! root      701:        MemorySnapShot_Store(&VerticalOverscan, sizeof(VerticalOverscan));
1.1.1.11  root      702:        MemorySnapShot_Store(HBLPalettes, sizeof(HBLPalettes));
                    703:        MemorySnapShot_Store(HBLPaletteMasks, sizeof(HBLPaletteMasks));
                    704:        MemorySnapShot_Store(&VideoBase, sizeof(VideoBase));
1.1.1.23  root      705:        if ( bSave )
                    706:        {
                    707:                addr = pVideoRaster - STRam;
                    708:                MemorySnapShot_Store(&addr, sizeof(addr));
                    709:        }
                    710:        else
                    711:        {
                    712:                MemorySnapShot_Store(&addr, sizeof(addr));
                    713:                pVideoRaster = &STRam[VideoBase];
                    714:        }
1.1.1.11  root      715:        MemorySnapShot_Store(&LineWidth, sizeof(LineWidth));
                    716:        MemorySnapShot_Store(&HWScrollCount, sizeof(HWScrollCount));
                    717:        MemorySnapShot_Store(&nScanlinesPerFrame, sizeof(nScanlinesPerFrame));
                    718:        MemorySnapShot_Store(&nCyclesPerLine, sizeof(nCyclesPerLine));
                    719:        MemorySnapShot_Store(&nFirstVisibleHbl, sizeof(nFirstVisibleHbl));
                    720:        MemorySnapShot_Store(&bSteBorderFlag, sizeof(bSteBorderFlag));
1.1.1.14  root      721:        MemorySnapShot_Store(&HblJitterIndex, sizeof(HblJitterIndex));
                    722:        MemorySnapShot_Store(&VblJitterIndex, sizeof(VblJitterIndex));
1.1.1.15  root      723:        MemorySnapShot_Store(&ShifterFrame, sizeof(ShifterFrame));
1.1.1.21  root      724:        MemorySnapShot_Store(&TTSpecialVideoMode, sizeof(TTSpecialVideoMode));
1.1.1.15  root      725: }
                    726: 
                    727: 
                    728: /*-----------------------------------------------------------------------*/
                    729: /**
                    730:  * Reset video chip
                    731:  */
                    732: void Video_Reset(void)
                    733: {
                    734:        /* NOTE! Must reset all of these register type things here!!!! */
1.1.1.19  root      735:        Video_Reset_Glue();
1.1.1.15  root      736: 
                    737:        /* Set system specific timings */
1.1.1.24! root      738:        Video_SetTimings ( ConfigureParams.System.nMachineType , ConfigureParams.System.VideoTimingMode );
1.1.1.15  root      739: 
                    740:        /* Reset VBL counter */
                    741:        nVBLs = 0;
                    742:        /* Reset addresses */
                    743:        VideoBase = 0L;
                    744: 
                    745:        /* Reset shifter's state variables */
                    746:        ShifterFrame.Freq = -1;
                    747:        ShifterFrame.Res = -1;
                    748:        ShifterFrame.FreqPos50.VBL = -1;
                    749:        ShifterFrame.FreqPos60.VBL = -1;
                    750:        ShifterFrame.ResPosLo.VBL = -1;
                    751:        ShifterFrame.ResPosMed.VBL = -1;
                    752:        ShifterFrame.ResPosHi.VBL = -1;
                    753:        ShifterFrame.Scroll8264Pos.VBL = -1;
                    754:        ShifterFrame.Scroll8265Pos.VBL = -1;
                    755: 
                    756:        Video_InitShifterLines ();
                    757: 
                    758:        /* Reset STE screen variables */
                    759:        LineWidth = 0;
                    760:        HWScrollCount = 0;
                    761:        bSteBorderFlag = false;
                    762: 
                    763:        NewLineWidth = -1;                      /* cancel pending modifications set before the reset */
                    764:        NewHWScrollCount = -1;
                    765: 
1.1.1.16  root      766:        VideoCounterDelayedOffset = 0;
                    767:        pVideoRasterDelayed = NULL;
                    768: 
1.1.1.15  root      769:        /* Reset jitter indexes */
                    770:        HblJitterIndex = 0;
                    771:        VblJitterIndex = 0;
                    772: 
1.1.1.24! root      773:        TTSpecialVideoMode = nPrevTTSpecialVideoMode = 0;
        !           774: 
1.1.1.15  root      775:        /* Clear framecycles counter */
                    776:        Cycles_SetCounter(CYCLES_COUNTER_VIDEO, 0);
                    777: 
                    778:        /* Clear ready for new VBL */
                    779:        Video_ClearOnVBL();
                    780: }
                    781: 
                    782: 
                    783: /*-----------------------------------------------------------------------*/
                    784: /**
                    785:  * Reset the GLUE chip responsible for generating the H/V sync signals.
                    786:  * When the 68000 RESET instruction is called, frequency and resolution
                    787:  * should be reset to 0.
                    788:  */
                    789: void Video_Reset_Glue(void)
                    790: {
1.1.1.20  root      791:        Uint8 VideoShifterByte;
                    792: 
1.1.1.19  root      793:        IoMem_WriteByte(0xff820a,0);                    /* Video frequency */
                    794: 
                    795:        /* Are we in high-res? */
                    796:        if (bUseHighRes)
                    797:                VideoShifterByte = ST_HIGH_RES;         /* Mono monitor */
                    798:        else
                    799:                VideoShifterByte = ST_LOW_RES;
                    800:        if (bUseVDIRes)
                    801:                VideoShifterByte = VDIRes;
                    802: 
                    803:        IoMem_WriteByte(0xff8260, VideoShifterByte);
1.1       root      804: }
                    805: 
1.1.1.8   root      806: 
1.1       root      807: /*-----------------------------------------------------------------------*/
1.1.1.12  root      808: /*
1.1.1.24! root      809:  * Init video timings values for all emulated machines.
        !           810:  * This should be called only once, when emulator starts
        !           811:  */
        !           812: void   Video_InitTimings(void)
        !           813: {
        !           814:        VIDEO_TIMING    *pVideoTiming1;
        !           815:        VIDEO_TIMING    *pVideoTiming2;
        !           816: 
        !           817:        /* Init all timings for STF machine */
        !           818:        pVideoTiming1 = &VideoTimings[ VIDEO_TIMING_STF_WS1 ];
        !           819: 
        !           820:        /* Init all timings for WS1 mode */
        !           821:        pVideoTiming1->VideoTimingName          = "WS1";
        !           822:        pVideoTiming1->H_Start_Hi               =   4;
        !           823:        pVideoTiming1->Blank_Stop_Low_60        =  24;
        !           824:        pVideoTiming1->Blank_Stop_Low_50        =  28;
        !           825:        pVideoTiming1->H_Start_Low_60           =  52;
        !           826:        pVideoTiming1->Line_Set_Pal             =  54;
        !           827:        pVideoTiming1->H_Start_Low_50           =  56;
        !           828:        pVideoTiming1->H_Stop_Hi                = 164;
        !           829:        pVideoTiming1->Blank_Start_Hi           = 184;
        !           830:        pVideoTiming1->H_Stop_Low_60            = 372;
        !           831:        pVideoTiming1->H_Stop_Low_50            = 376;
        !           832:        pVideoTiming1->Blank_Start_Low          = 450;
        !           833:        pVideoTiming1->HSync_Start_Offset_Low   = -50;                                          /* 458/462 (line cycles-50) */
        !           834:        pVideoTiming1->HSync_Stop_Offset_Low    = -10;                                          /* 498/502 (line cycles-10) */
        !           835:        pVideoTiming1->RemoveTopBorder_Pos      = 502;
        !           836:        pVideoTiming1->RemoveBottomBorder_Pos   = 502;
        !           837:        pVideoTiming1->V_Start_Line_50          = VIDEO_START_HBL_50HZ;                         /*  63 or 63-16 */
        !           838:        pVideoTiming1->V_Start_Line_60          = VIDEO_START_HBL_60HZ;                         /*  34 */
        !           839:        pVideoTiming1->V_Start_Line_Hi          = VIDEO_START_HBL_71HZ;                         /*  34 */
        !           840:        pVideoTiming1->V_End_Line_50            = VIDEO_END_HBL_50HZ;                           /* 263 or 263-16 */
        !           841:        pVideoTiming1->V_End_Line_60            = VIDEO_END_HBL_60HZ;                           /* 234 */
        !           842:        pVideoTiming1->V_End_Line_Hi            = VIDEO_END_HBL_71HZ;                           /* 434 */
        !           843:        pVideoTiming1->V_End_Line_NoBottom_50   = pVideoTiming1->V_End_Line_50 + VIDEO_HEIGHT_BOTTOM_50HZ;      /* 310 (TODO:check on real HW) */
        !           844:        pVideoTiming1->V_End_Line_NoBottom_60   = pVideoTiming1->V_End_Line_60 + VIDEO_HEIGHT_BOTTOM_60HZ;      /* 263 (TODO:check on real HW) */
        !           845:        pVideoTiming1->RestartVideoCounter_Line_60      = RESTART_VIDEO_COUNTER_LINE_60HZ;      /* 260 */
        !           846:        pVideoTiming1->RestartVideoCounter_Line_50      = RESTART_VIDEO_COUNTER_LINE_50HZ;      /* 310 */
        !           847:        pVideoTiming1->RestartVideoCounter_Pos  = RESTART_VIDEO_COUNTER_CYCLE_STF;              /*  48 */
        !           848:        pVideoTiming1->VblVideoCycleOffset      = VBL_VIDEO_CYCLE_OFFSET_STF - 4;
        !           849:        pVideoTiming1->Hbl_Int_Pos_Low_60       = CYCLES_PER_LINE_60HZ - 4;
        !           850:        pVideoTiming1->Hbl_Int_Pos_Low_50       = CYCLES_PER_LINE_50HZ - 4;
        !           851:        pVideoTiming1->Hbl_Int_Pos_Hi           = CYCLES_PER_LINE_71HZ - 4;
        !           852: #ifdef STF_SHORT_TOP
        !           853:        pVideoTiming1->V_Start_Line_50 -= 16;
        !           854:        pVideoTiming1->V_End_Line_50 -= 16;
        !           855: #endif
        !           856:        /* WS2/WS3/WS4 timings are derived from WS1 with an increment */        
        !           857:        pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_STF_WS2 ];
        !           858:        pVideoTiming2->VideoTimingName          = "WS2";
        !           859:        Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 3 );
        !           860:        pVideoTiming2->VblVideoCycleOffset      = VBL_VIDEO_CYCLE_OFFSET_STF;
        !           861:        pVideoTiming2->Hbl_Int_Pos_Low_60       = CYCLES_PER_LINE_60HZ;
        !           862:        pVideoTiming2->Hbl_Int_Pos_Low_50       = CYCLES_PER_LINE_50HZ;
        !           863:        pVideoTiming2->Hbl_Int_Pos_Hi           = CYCLES_PER_LINE_71HZ;
        !           864: 
        !           865:        pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_STF_WS3 ];
        !           866:        pVideoTiming2->VideoTimingName          = "WS3";
        !           867:        Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 1 );
        !           868:        pVideoTiming2->VblVideoCycleOffset      = VBL_VIDEO_CYCLE_OFFSET_STF;
        !           869:        pVideoTiming2->Hbl_Int_Pos_Low_60       = CYCLES_PER_LINE_60HZ;
        !           870:        pVideoTiming2->Hbl_Int_Pos_Low_50       = CYCLES_PER_LINE_50HZ;
        !           871:        pVideoTiming2->Hbl_Int_Pos_Hi           = CYCLES_PER_LINE_71HZ;
        !           872: 
        !           873:        pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_STF_WS4 ];
        !           874:        pVideoTiming2->VideoTimingName          = "WS4";
        !           875:        Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 2 );
        !           876:        pVideoTiming2->VblVideoCycleOffset      = VBL_VIDEO_CYCLE_OFFSET_STF;
        !           877:        pVideoTiming2->Hbl_Int_Pos_Low_60       = CYCLES_PER_LINE_60HZ;
        !           878:        pVideoTiming2->Hbl_Int_Pos_Low_50       = CYCLES_PER_LINE_50HZ;
        !           879:        pVideoTiming2->Hbl_Int_Pos_Hi           = CYCLES_PER_LINE_71HZ;
        !           880: 
        !           881: 
        !           882:        /* Init all timings for STE machine */
        !           883:        pVideoTiming1 = &VideoTimings[ VIDEO_TIMING_STE ];
        !           884:        pVideoTiming1->VideoTimingName          = "STE";
        !           885:        pVideoTiming1->Preload_Start_Hi         =   0;
        !           886:        pVideoTiming1->H_Start_Hi               =   4;
        !           887:        pVideoTiming1->Blank_Stop_Low_60        =  24;
        !           888:        pVideoTiming1->Blank_Stop_Low_50        =  28;
        !           889:        pVideoTiming1->Preload_Start_Low_60     =  36;
        !           890:        pVideoTiming1->H_Start_Low_60           =  52;
        !           891:        pVideoTiming1->Line_Set_Pal             =  56;
        !           892:        pVideoTiming1->Preload_Start_Low_50     =  40;
        !           893:        pVideoTiming1->H_Start_Low_50           =  56;
        !           894:        pVideoTiming1->H_Stop_Hi                = 164;
        !           895:        pVideoTiming1->Blank_Start_Hi           = 184;
        !           896:        pVideoTiming1->H_Stop_Low_60            = 372;
        !           897:        pVideoTiming1->H_Stop_Low_50            = 376;
        !           898:        pVideoTiming1->Blank_Start_Low          = 448;
        !           899:        pVideoTiming1->HSync_Start_Offset_Low   = -52;                                          /* 456/460 (line cycles-52) */
        !           900:        pVideoTiming1->HSync_Stop_Offset_Low    = -12;                                          /* 496/500 (line cycles-12) */
        !           901:        pVideoTiming1->RemoveTopBorder_Pos      = 500;
        !           902:        pVideoTiming1->RemoveBottomBorder_Pos   = 500;
        !           903:        pVideoTiming1->V_Start_Line_50          = VIDEO_START_HBL_50HZ;                         /*  63 */
        !           904:        pVideoTiming1->V_Start_Line_60          = VIDEO_START_HBL_60HZ;                         /*  34 */
        !           905:        pVideoTiming1->V_Start_Line_Hi          = VIDEO_START_HBL_71HZ;                         /*  34 */
        !           906:        pVideoTiming1->V_End_Line_50            = VIDEO_END_HBL_50HZ;                           /* 263 */
        !           907:        pVideoTiming1->V_End_Line_60            = VIDEO_END_HBL_60HZ;                           /* 234 */
        !           908:        pVideoTiming1->V_End_Line_Hi            = VIDEO_END_HBL_71HZ;                           /* 434 */
        !           909:        pVideoTiming1->V_End_Line_NoBottom_50   = pVideoTiming1->V_End_Line_50 + VIDEO_HEIGHT_BOTTOM_50HZ;      /* 310 */
        !           910:        pVideoTiming1->V_End_Line_NoBottom_60   = pVideoTiming1->V_End_Line_60 + VIDEO_HEIGHT_BOTTOM_60HZ;      /* 263 */
        !           911:        pVideoTiming1->RestartVideoCounter_Line_60      = RESTART_VIDEO_COUNTER_LINE_60HZ;      /* 260 */
        !           912:        pVideoTiming1->RestartVideoCounter_Line_50      = RESTART_VIDEO_COUNTER_LINE_50HZ;      /* 310 */
        !           913:        pVideoTiming1->RestartVideoCounter_Pos  = RESTART_VIDEO_COUNTER_CYCLE_STE;              /*  52 */
        !           914:        pVideoTiming1->VblVideoCycleOffset      = VBL_VIDEO_CYCLE_OFFSET_STF;
        !           915:        pVideoTiming1->Hbl_Int_Pos_Low_60       = CYCLES_PER_LINE_60HZ;
        !           916:        pVideoTiming1->Hbl_Int_Pos_Low_50       = CYCLES_PER_LINE_50HZ;
        !           917:        pVideoTiming1->Hbl_Int_Pos_Hi           = CYCLES_PER_LINE_71HZ;
        !           918: 
        !           919:        /* Use the STE timings for TT */
        !           920:        pVideoTiming2 = &VideoTimings[ VIDEO_TIMING_TT ];
        !           921:        pVideoTiming2->VideoTimingName = "TT";
        !           922:        Video_InitTimings_Copy ( pVideoTiming1 , pVideoTiming2 , 0 );
        !           923: 
        !           924:        /* Set timings to a default mode (this will be overriden later when choosing the emulated machine) */
        !           925:        pVideoTiming = &VideoTimings[ VIDEO_TIMING_DEFAULT ];
        !           926: }
        !           927: 
        !           928: 
        !           929: 
        !           930: /*-----------------------------------------------------------------------*/
        !           931: /*
        !           932:  * Copy some video timings, adding 'inc' to the values that depend on
        !           933:  * wakeup state.
        !           934:  */
        !           935: static void    Video_InitTimings_Copy ( VIDEO_TIMING *pSrc , VIDEO_TIMING *pDest , int inc )
        !           936: {
        !           937:        pDest->Preload_Start_Hi         = pSrc->Preload_Start_Hi + inc;
        !           938:        pDest->H_Start_Hi               = pSrc->H_Start_Hi + inc;
        !           939:        pDest->Blank_Stop_Low_60        = pSrc->Blank_Stop_Low_60 + inc;
        !           940:        pDest->Blank_Stop_Low_50        = pSrc->Blank_Stop_Low_50 + inc;
        !           941:        pDest->Preload_Start_Low_60     = pSrc->Preload_Start_Low_60 + inc;
        !           942:        pDest->H_Start_Low_60           = pSrc->H_Start_Low_60 + inc;
        !           943:        pDest->Line_Set_Pal             = pSrc->Line_Set_Pal + inc;
        !           944:        pDest->Preload_Start_Low_50     = pSrc->Preload_Start_Low_50 + inc;
        !           945:        pDest->H_Start_Low_50           = pSrc->H_Start_Low_50 + inc;
        !           946:        pDest->H_Stop_Hi                = pSrc->H_Stop_Hi + inc;
        !           947:        pDest->Blank_Start_Hi           = pSrc->Blank_Start_Hi + inc;
        !           948:        pDest->H_Stop_Low_60            = pSrc->H_Stop_Low_60 + inc;
        !           949:        pDest->H_Stop_Low_50            = pSrc->H_Stop_Low_50 + inc;
        !           950:        pDest->Blank_Start_Low          = pSrc->Blank_Start_Low + inc;
        !           951:        pDest->HSync_Start_Offset_Low   = pSrc->HSync_Start_Offset_Low + inc;
        !           952:        pDest->HSync_Stop_Offset_Low    = pSrc->HSync_Stop_Offset_Low + inc;
        !           953:        pDest->RemoveTopBorder_Pos      = pSrc->RemoveTopBorder_Pos + inc;
        !           954:        pDest->RemoveBottomBorder_Pos   = pSrc->RemoveBottomBorder_Pos + inc;
        !           955: 
        !           956:        pDest->V_Start_Line_50          = pSrc->V_Start_Line_50;
        !           957:        pDest->V_Start_Line_60          = pSrc->V_Start_Line_60;
        !           958:        pDest->V_Start_Line_Hi          = pSrc->V_Start_Line_Hi;
        !           959:        pDest->V_End_Line_50            = pSrc->V_End_Line_50;
        !           960:        pDest->V_End_Line_60            = pSrc->V_End_Line_60;
        !           961:        pDest->V_End_Line_Hi            = pSrc->V_End_Line_Hi;
        !           962:        pDest->V_End_Line_NoBottom_50   = pSrc->V_End_Line_NoBottom_50;
        !           963:        pDest->V_End_Line_NoBottom_60   = pSrc->V_End_Line_NoBottom_60;
        !           964: 
        !           965:        pDest->RestartVideoCounter_Line_60      = pSrc->RestartVideoCounter_Line_60;
        !           966:        pDest->RestartVideoCounter_Line_50      = pSrc->RestartVideoCounter_Line_50;
        !           967:        pDest->RestartVideoCounter_Pos  = pSrc->RestartVideoCounter_Pos;
        !           968: 
        !           969:        pDest->VblVideoCycleOffset      = pSrc->VblVideoCycleOffset;
        !           970:        pDest->Hbl_Int_Pos_Low_60       = pSrc->Hbl_Int_Pos_Low_60;
        !           971:        pDest->Hbl_Int_Pos_Low_50       = pSrc->Hbl_Int_Pos_Low_50;
        !           972:        pDest->Hbl_Int_Pos_Hi           = pSrc->Hbl_Int_Pos_Hi;
        !           973: }
        !           974: 
        !           975: 
        !           976: 
        !           977: 
        !           978: /*-----------------------------------------------------------------------*/
        !           979: /*
1.1.1.12  root      980:  * Set specific video timings, depending on the system being emulated.
1.1.1.24! root      981:  * - STF is known to have 4 possible states depending on the MMU/GLUE synchronisation
        !           982:  * - STE has MMU and GLUE merged in the same chip, so there's only 1 state
        !           983:  * (other video differences are sometimes visible on STF/STE, so there might be
        !           984:  * more states, affecting the shifter for example)
1.1.1.12  root      985:  */
1.1.1.24! root      986: void   Video_SetTimings( MACHINETYPE MachineType , VIDEOTIMINGMODE Mode )
1.1.1.12  root      987: {
1.1.1.24! root      988: fprintf ( stderr , "Video_SetSystemTimings1 %d %d\n" , MachineType , Mode );
        !           989: 
        !           990: 
        !           991:        /* Default timing for TT/Falcon (not really important */
        !           992:        /* as TT/Falcon don't use cycle precise video effects) */
        !           993:        VideoTiming = VIDEO_TIMING_DEFAULT;
        !           994: 
        !           995:        if ( ( MachineType == MACHINE_STE ) || ( MachineType == MACHINE_MEGA_STE ) )
        !           996:                VideoTiming = VIDEO_TIMING_STE;                 /* Only one choice for STE */
        !           997: 
        !           998:        else if ( MachineType == MACHINE_TT )
        !           999:                VideoTiming = VIDEO_TIMING_TT;                  /* Only one choice for TT (same values as STE) */
        !          1000: 
        !          1001:        else if ( ( MachineType == MACHINE_ST ) || ( MachineType == MACHINE_MEGA_ST ) ) /* 4 wakeup states are possible for STF */
1.1.1.19  root     1002:        {
1.1.1.24! root     1003:                if ( Mode == VIDEO_TIMING_MODE_RANDOM )
        !          1004:                        Mode = VIDEO_TIMING_MODE_WS1 + rand() % 4;      /* random between the 4 modes WS1, WS2, WS3, WS4 */
        !          1005: 
        !          1006:                if ( Mode == VIDEO_TIMING_MODE_WS1 )            VideoTiming = VIDEO_TIMING_STF_WS1;
        !          1007:                else if ( Mode == VIDEO_TIMING_MODE_WS2 )       VideoTiming = VIDEO_TIMING_STF_WS2;
        !          1008:                else if ( Mode == VIDEO_TIMING_MODE_WS3 )       VideoTiming = VIDEO_TIMING_STF_WS3;
        !          1009:                else                                            VideoTiming = VIDEO_TIMING_STF_WS4;
1.1.1.19  root     1010:        }
1.1.1.24! root     1011: 
        !          1012:        pVideoTiming = &VideoTimings[ VideoTiming ];
        !          1013: fprintf ( stderr , "Video_SetSystemTimings2 %d %d -> %d (%s) %d %d %d\n" , MachineType , Mode , VideoTiming , pVideoTiming->VideoTimingName , pVideoTiming->RemoveTopBorder_Pos , pVideoTiming->RemoveBottomBorder_Pos , pVideoTiming->VblVideoCycleOffset );
        !          1014: }
        !          1015: 
        !          1016: 
        !          1017: const char     *Video_GetTimings_Name ( void )
        !          1018: {
        !          1019:        return pVideoTiming->VideoTimingName;
1.1.1.12  root     1020: }
                   1021: 
                   1022: 
                   1023: /*-----------------------------------------------------------------------*/
1.1.1.11  root     1024: /**
1.1.1.15  root     1025:  * Convert the elapsed number of cycles since the start of the VBL
                   1026:  * into the corresponding HBL number and the cycle position in the current
                   1027:  * HBL. We use the starting cycle position of the closest HBL to compute
                   1028:  * the cycle position on the line (this allows to mix lines with different
                   1029:  * values for nCyclesPerLine).
                   1030:  * We can have 2 cases on the limit where the real video line count can be
                   1031:  * different from nHBL :
                   1032:  * - when reading video address between cycle 0 and 12, LineCycle will be <0,
                   1033:  *   so we need to use the data from line nHBL-1
                   1034:  * - if LineCycle >= nCyclesPerLine, this means the HBL int was not processed
                   1035:  *   yet, so the video line number is in fact nHBL+1
                   1036:  */
                   1037: 
                   1038: void   Video_ConvertPosition ( int FrameCycles , int *pHBL , int *pLineCycles )
                   1039: {
1.1.1.24! root     1040:        if ( ( nHBL == nScanlinesPerFrame )                             /* rare case between end of last hbl and start of next VBL (during 64 cycles) */
        !          1041:          && ( Config_IsMachineST() || Config_IsMachineSTE() ) )        /* for example, cycle 160336 will give HBL=0 and LineCycles=80 */
1.1.1.15  root     1042:        {
1.1.1.24! root     1043:                *pHBL = 0;
        !          1044:                *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle - nCyclesPerLine;
        !          1045:                if ( *pLineCycles < 0 )                                 /* reading before end of last hbl (possible in WS1) */
        !          1046:                {
        !          1047:                        *pHBL = nHBL-1;
        !          1048:                        *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle;
        !          1049:                }
1.1.1.16  root     1050:        //fprintf ( stderr , "out of vbl FrameCycles %d CyclesPerVBL %d nHBL=%d %d %d\n" , FrameCycles , CyclesPerVBL, nHBL , *pHBL , *pLineCycles );
1.1.1.15  root     1051:        }
1.1.1.16  root     1052: 
                   1053:        else                                                            /* most common case */
1.1.1.15  root     1054:        {
1.1.1.16  root     1055:                *pHBL = nHBL;
                   1056:                *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL ].StartCycle;
1.1.1.24! root     1057: //     fprintf ( stderr , "conv pos nHBL=%d %d %d %d\n" , nHBL , FrameCycles , *pLineCycles , ShifterFrame.ShifterLines[ nHBL ].StartCycle );
1.1.1.15  root     1058: 
1.1.1.16  root     1059:                if ( *pLineCycles < 0 )                                 /* reading from the previous video line */
                   1060:                {
                   1061:                        *pHBL = nHBL-1;
                   1062:                        *pLineCycles = FrameCycles - ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle;
                   1063:                }
                   1064:        
                   1065:                else if ( *pLineCycles >= nCyclesPerLine )              /* reading on the next line, but HBL int was delayed */
                   1066:                {
                   1067:                        *pHBL = nHBL+1;
                   1068:                        *pLineCycles -= nCyclesPerLine;
                   1069:                }
                   1070:        }
1.1.1.15  root     1071: 
                   1072: if ( *pLineCycles < 0 )
1.1.1.24! root     1073:        fprintf ( stderr , "bug nHBL=%d %d %d %d\n" , nHBL , FrameCycles , *pHBL , *pLineCycles );
1.1.1.15  root     1074: 
                   1075: //if ( ( *pHBL != FrameCycles / nCyclesPerLine ) || ( *pLineCycles != FrameCycles % nCyclesPerLine ) )
                   1076: //  LOG_TRACE ( TRACE_VIDEO_ADDR , "conv pos %d %d - %d %d\n" , *pHBL , FrameCycles / nCyclesPerLine , *pLineCycles , FrameCycles % nCyclesPerLine );
                   1077: //  LOG_TRACE ( TRACE_VIDEO_ADDR , "conv pos %d %d %d\n" , FrameCycles , *pHBL , *pLineCycles );
                   1078: }
                   1079: 
                   1080: 
                   1081: void   Video_GetPosition ( int *pFrameCycles , int *pHBL , int *pLineCycles )
                   1082: {
                   1083:        *pFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
                   1084:        Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
                   1085: }
                   1086: 
                   1087: 
                   1088: void   Video_GetPosition_OnWriteAccess ( int *pFrameCycles , int *pHBL , int *pLineCycles )
                   1089: {
                   1090:        *pFrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
                   1091:        Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
                   1092: }
                   1093: 
                   1094: 
                   1095: void   Video_GetPosition_OnReadAccess ( int *pFrameCycles , int *pHBL , int *pLineCycles )
                   1096: {
                   1097:        *pFrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO);
                   1098:        Video_ConvertPosition ( *pFrameCycles , pHBL , pLineCycles );
                   1099: }
                   1100: 
                   1101: 
                   1102: /*-----------------------------------------------------------------------*/
                   1103: /**
1.1.1.11  root     1104:  * Calculate and return video address pointer.
                   1105:  */
1.1.1.15  root     1106: static Uint32 Video_CalculateAddress ( void )
1.1       root     1107: {
1.1.1.15  root     1108:        int FrameCycles, HblCounterVideo, LineCycles;
                   1109:        int X, NbBytes;
1.1.1.11  root     1110:        Uint32 VideoAddress;      /* Address of video display in ST screen space */
                   1111:        int nSyncByte;
1.1.1.23  root     1112:        int Res;
1.1.1.11  root     1113:        int LineBorderMask;
                   1114:        int PrevSize;
                   1115:        int CurSize;
1.1.1.15  root     1116:        int LineStartCycle , LineEndCycle;
1.1.1.11  root     1117: 
                   1118:        /* Find number of cycles passed during frame */
1.1.1.24! root     1119:        /* We need to subtract '8' for correct video address calculation */
        !          1120:        FrameCycles = Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 8;
1.1.1.11  root     1121: 
                   1122:        /* Now find which pixel we are on (ignore left/right borders) */
1.1.1.15  root     1123:        Video_ConvertPosition ( FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.11  root     1124: 
1.1.1.23  root     1125:        Res = IoMem_ReadByte ( 0xff8260 ) & 3;
1.1.1.24! root     1126: 
        !          1127:        /* [FIXME] 'Delirious Demo IV' protection : reads FF8209 between a high/low switch */
        !          1128:        /* on a low res screen. So far, Hatari doesn't handle mixed resolutions */
        !          1129:        /* on the same line, so we ignore the hi switch in that case */
        !          1130:        if ( ( M68000_InstrPC == 0x2110 ) && ( STMemory_ReadLong ( M68000_InstrPC ) == 0x14101280 ) )   /* move.b (a0),d2 + move.b d0,(a1) */
        !          1131:                Res = 0;                                        /* force to low res to pass the protection */
        !          1132:        
1.1.1.23  root     1133:        if ( Res & 2 )                                          /* hi res */
1.1.1.11  root     1134:        {
1.1.1.23  root     1135:                LineStartCycle = LINE_START_CYCLE_71;
                   1136:                LineEndCycle = LINE_END_CYCLE_71;
                   1137:                HblCounterVideo = FrameCycles / nCyclesPerLine;
                   1138:                LineCycles = FrameCycles % nCyclesPerLine;
1.1.1.11  root     1139:        }
1.1.1.23  root     1140:        else
1.1.1.11  root     1141:        {
1.1.1.23  root     1142:                nSyncByte = IoMem_ReadByte(0xff820a) & 2;       /* only keep bit 1 */
                   1143:                if (nSyncByte)                                  /* 50 Hz */
                   1144:                {
                   1145:                        LineStartCycle = LINE_START_CYCLE_50;
                   1146:                        LineEndCycle = LINE_END_CYCLE_50;
                   1147:                }
                   1148:                else                                            /* 60 Hz */
                   1149:                {
                   1150:                        LineStartCycle = LINE_START_CYCLE_60;
                   1151:                        LineEndCycle = LINE_END_CYCLE_60;
                   1152:                }
1.1.1.11  root     1153:        }
                   1154: 
1.1.1.23  root     1155:        X = LineCycles;
1.1.1.11  root     1156: 
                   1157:        /* Top of screen is usually 63 lines from VBL in 50 Hz */
1.1.1.15  root     1158:        if ( HblCounterVideo < nStartHBL )
1.1.1.11  root     1159:        {
                   1160:                /* pVideoRaster was set during Video_ClearOnVBL using VideoBase */
                   1161:                /* and it could also have been modified on STE by writing to ff8205/07/09 */
1.1.1.15  root     1162:                /* So, we should not use ff8201/ff8203 which are reloaded in ff8205/ff8207 only once per VBL */
                   1163:                /* but use pVideoRaster - STRam instead to get current shifter video address */
1.1.1.11  root     1164:                VideoAddress = pVideoRaster - STRam;
                   1165:        }
                   1166: 
1.1.1.23  root     1167:        /* Special case when reading video counter in hi-res (used in the demo 'My Socks Are Weapons' by Legacy) */
                   1168:        /* This assumes a standard 640x400 resolution with no border removed, so code is simpler */
                   1169:        /* [NP] TODO : this should be handled in a more generic way with low/med cases */
                   1170:        /* even when Hatari is not started in monochrome mode */
                   1171:        else if ( Res & 2 )                                     /* Hi res */
                   1172:        {
                   1173:                if ( X < LineStartCycle )
                   1174:                        X = LineStartCycle;                     /* display is disabled in the left border */
                   1175:                else if ( X > LineEndCycle )
                   1176:                        X = LineEndCycle;                       /* display is disabled in the right border */
                   1177: 
                   1178:                NbBytes = ( (X-LineStartCycle)>>1 ) & (~1);     /* 2 cycles per byte */
                   1179: 
                   1180:                /* One line uses 80 bytes instead of the standard 160 bytes in low/med res */
                   1181:                if ( HblCounterVideo < nStartHBL + VIDEO_HEIGHT_HBL_MONO )
                   1182:                        VideoAddress = VideoBase + ( HblCounterVideo - nStartHBL ) * ( BORDERBYTES_NORMAL / 2 ) + NbBytes;
                   1183:                else
                   1184:                        VideoAddress = VideoBase + VIDEO_HEIGHT_HBL_MONO * ( BORDERBYTES_NORMAL / 2 );
                   1185:        }
                   1186: 
1.1.1.11  root     1187:        else
                   1188:        {
                   1189:                VideoAddress = pVideoRaster - STRam;            /* pVideoRaster is updated by Video_CopyScreenLineColor */
                   1190: 
                   1191:                /* Now find which pixel we are on (ignore left/right borders) */
1.1.1.15  root     1192: //             X = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) % nCyclesPerLine;
1.1.1.11  root     1193: 
                   1194:                /* Get real video line count (can be different from nHBL) */
1.1.1.15  root     1195: //             HblCounterVideo = ( Cycles_GetCounterOnReadAccess(CYCLES_COUNTER_VIDEO) - 12 ) / nCyclesPerLine;
1.1.1.11  root     1196: 
                   1197:                /* Correct the case when read overlaps end of line / start of next line */
                   1198:                /* Video_CopyScreenLineColor was not called yet to update VideoAddress */
                   1199:                /* so we need to determine the size of the previous line to get the */
                   1200:                /* correct value of VideoAddress. */
                   1201:                PrevSize = 0;
                   1202:                if ( HblCounterVideo < nHBL )
                   1203:                        X = 0;
                   1204:                else if ( ( HblCounterVideo > nHBL )            /* HblCounterVideo = nHBL+1 */
                   1205:                          &&  ( nHBL >= nStartHBL ) )           /* if nHBL was not visible, PrevSize = 0 */
                   1206:                {
1.1.1.15  root     1207:                        LineBorderMask = ShifterFrame.ShifterLines[ HblCounterVideo-1 ].BorderMask; /* get border mask for nHBL */
1.1.1.11  root     1208:                        PrevSize = BORDERBYTES_NORMAL;          /* normal line */
                   1209: 
                   1210:                        if (LineBorderMask & BORDERMASK_LEFT_OFF)
                   1211:                                PrevSize += BORDERBYTES_LEFT;
                   1212:                        else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
                   1213:                                PrevSize += 2;
                   1214: 
                   1215:                        if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
                   1216:                                PrevSize -= 106;
                   1217:                        else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
                   1218:                                PrevSize -= 2;
                   1219:                        else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
                   1220:                                PrevSize += BORDERBYTES_RIGHT;
                   1221: 
1.1.1.24! root     1222:                        if (LineBorderMask & ( BORDERMASK_EMPTY_LINE | BORDERMASK_NO_DE ) )
1.1.1.11  root     1223:                                PrevSize = 0;
1.1.1.23  root     1224: 
                   1225:                        /* On STE, the Shifter skips the given amount of words as soon as display is disabled */
                   1226:                        /* which is the case here when reading overlaps end/start of line (LineWidth is 0 on STF) */
                   1227:                        PrevSize += LineWidth*2;
1.1.1.11  root     1228:                }
                   1229: 
                   1230: 
1.1.1.15  root     1231:                LineBorderMask = ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask;
1.1.1.11  root     1232: 
                   1233:                CurSize = BORDERBYTES_NORMAL;                   /* normal line */
                   1234: 
                   1235:                if (LineBorderMask & BORDERMASK_LEFT_OFF)
                   1236:                        CurSize += BORDERBYTES_LEFT;
1.1.1.24! root     1237:                else if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
        !          1238:                        CurSize += BORDERBYTES_LEFT_2_STE;
1.1.1.11  root     1239:                else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)
                   1240:                        CurSize += 2;
1.1.1.16  root     1241:                else if (bSteBorderFlag)                        /* bigger line by 8 bytes on the left (STE specific) */
                   1242:                        CurSize += 8;
                   1243:                else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
                   1244:                        CurSize += 8;                           /* 8 more bytes are loaded when scrolling with prefetching */
1.1.1.11  root     1245: 
                   1246:                if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
                   1247:                        CurSize -= 106;
                   1248:                else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
                   1249:                        CurSize -= 2;
                   1250:                else if (LineBorderMask & BORDERMASK_RIGHT_OFF)
                   1251:                        CurSize += BORDERBYTES_RIGHT;
                   1252:                if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
                   1253:                        CurSize += BORDERBYTES_RIGHT_FULL;
                   1254: 
                   1255:                if ( LineBorderMask & BORDERMASK_LEFT_PLUS_2)
                   1256:                        LineStartCycle = LINE_START_CYCLE_60;
                   1257:                else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1.1.1.15  root     1258:                        LineStartCycle = LINE_START_CYCLE_71;
1.1.1.16  root     1259:                else if ( bSteBorderFlag )
                   1260:                        LineStartCycle -= 16;                   /* display starts 16 pixels earlier */
                   1261:                else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
                   1262:                        LineStartCycle -= 16;                   /* shifter starts reading 16 pixels earlier when scrolling with prefetching */
1.1.1.11  root     1263: 
                   1264:                LineEndCycle = LineStartCycle + CurSize*2;
                   1265: 
                   1266: 
                   1267:                if ( X < LineStartCycle )
1.1.1.16  root     1268:                        X = LineStartCycle;                     /* display is disabled in the left border */
1.1.1.11  root     1269:                else if ( X > LineEndCycle )
1.1.1.16  root     1270:                {
                   1271:                        X = LineEndCycle;                       /* display is disabled in the right border */
1.1.1.23  root     1272:                        /* On STE, the Shifter skips the given amount of words as soon as display is disabled */
                   1273:                        /* (LineWidth is 0 on STF) */
1.1.1.16  root     1274:                        VideoAddress += LineWidth*2;
                   1275:                }
1.1.1.11  root     1276: 
                   1277:                NbBytes = ( (X-LineStartCycle)>>1 ) & (~1);     /* 2 cycles per byte */
                   1278: 
                   1279: 
                   1280:                /* when left border is open, we have 2 bytes less than theorical value */
                   1281:                /* (26 bytes in left border, which is not a multiple of 4 cycles) */
                   1282:                if ( LineBorderMask & BORDERMASK_LEFT_OFF )
                   1283:                        NbBytes -= 2;
                   1284: 
1.1.1.24! root     1285:                if ( LineBorderMask & ( BORDERMASK_EMPTY_LINE | BORDERMASK_NO_DE ) )
1.1.1.11  root     1286:                        NbBytes = 0;
                   1287: 
1.1.1.16  root     1288:                /* Add line cycles if we have not reached end of screen yet */
1.1.1.18  root     1289:                if ( HblCounterVideo < nEndHBL + BlankLines )
1.1.1.11  root     1290:                        VideoAddress += PrevSize + NbBytes;
                   1291:        }
                   1292: 
1.1.1.16  root     1293:        LOG_TRACE(TRACE_VIDEO_ADDR , "video base=%x raster=%x addr=%x video_cyc=%d "
                   1294:                  "line_cyc=%d/X=%d @ nHBL=%d/video_hbl=%d %d<->%d pc=%x instr_cyc=%d\n",
                   1295:                  VideoBase, (int)(pVideoRaster - STRam), VideoAddress,
                   1296:                  Cycles_GetCounter(CYCLES_COUNTER_VIDEO), LineCycles, X, nHBL,
                   1297:                  HblCounterVideo, LineStartCycle, LineEndCycle, M68000_GetPC(), CurrentInstrCycles);
1.1.1.11  root     1298: 
                   1299:        return VideoAddress;
                   1300: }
                   1301: 
                   1302: 
                   1303: /*-----------------------------------------------------------------------*/
                   1304: /**
1.1.1.16  root     1305:  * Calculate the cycle where the STF/STE's MMU starts reading
                   1306:  * data to send them to the shifter.
                   1307:  * On STE, if hscroll is used, prefetch will cause this position to
                   1308:  * happen 16 cycles earlier.
                   1309:  * This function should use the same logic as in Video_CalculateAddress.
                   1310:  * NOTE : this function is not completly accurate, as even when there's
                   1311:  * no hscroll (on STF) the mmu starts reading 16 cycles before display starts.
                   1312:  * But it's good enough to emulate writing to ff8205/07/09 on STE.
                   1313:  */
                   1314: static int Video_GetMMUStartCycle ( int DisplayStartCycle )
                   1315: {
                   1316:        if ( bSteBorderFlag )
                   1317:                DisplayStartCycle -= 16;                        /* display starts 16 pixels earlier */
                   1318:        else if ( ( HWScrollCount > 0 ) && ( HWScrollPrefetch == 1 ) )
                   1319:                DisplayStartCycle -= 16;                        /* shifter starts reading 16 pixels earlier when scrolling with prefetching */
                   1320: 
                   1321:        return DisplayStartCycle;
                   1322: }
                   1323: 
                   1324: 
                   1325: /*-----------------------------------------------------------------------*/
                   1326: /**
1.1.1.11  root     1327:  * Write to VideoShifter (0xff8260), resolution bits
                   1328:  */
1.1.1.24! root     1329: static void Video_WriteToGlueShifterRes ( Uint8 Res )
1.1       root     1330: {
1.1.1.15  root     1331:        int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.11  root     1332: 
1.1.1.15  root     1333:        Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     1334:        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.11  root     1335: 
1.1.1.15  root     1336:        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",
                   1337:                       Res, FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11  root     1338: 
                   1339: 
1.1.1.13  root     1340:        /* Ignore consecutive writes of the same value */
1.1.1.15  root     1341:        if ( Res == ShifterFrame.Res )
1.1.1.13  root     1342:                return;                                         /* do nothing */
                   1343: 
1.1.1.15  root     1344: 
1.1.1.24! root     1345:        Video_Update_Glue_State ( FrameCycles , HblCounterVideo , LineCycles , true );
1.1.1.15  root     1346: 
                   1347: 
                   1348:        if ( ( ShifterFrame.Res == 0x02 ) && ( Res == 0x01 )    /* switched from hi res to med res */
                   1349:                && ( LineCycles <= (LINE_START_CYCLE_71+20) )
1.1.1.24! root     1350:                && ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF ) )
1.1.1.15  root     1351:        {
1.1.1.24! root     1352:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= ~BORDERMASK_LEFT_OFF;        /* cancel left off low res */
1.1.1.15  root     1353:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_LEFT_OFF_MED;     /* a later switch to low res might gives right scrolling */
                   1354:                /* By default, this line will be in med res, except if we detect hardware scrolling later */
                   1355:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 2 << 20 );
1.1.1.24! root     1356:                ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
1.1.1.15  root     1357:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left med %d<->%d\n" ,
                   1358:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle , ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle );
                   1359:        }
                   1360: 
                   1361: 
1.1.1.24! root     1362:        /* If left border is opened with hi/lo and we switch to medium resolution during the next cycles, */
1.1.1.15  root     1363:        /* then we assume a med res overscan line instead of a low res overscan line. */
                   1364:        /* Note that in that case, the switch to med res can shift the display by 0-3 words */
1.1.1.11  root     1365:        /* Used in 'No Cooper' greetings by 1984 and 'Punish Your Machine' by Delta Force */
1.1.1.15  root     1366:        if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
                   1367:                && ( Res == 0x01 ) )
1.1.1.11  root     1368:        {
1.1.1.18  root     1369:                if ( ( LineCycles == LINE_LEFT_MED_CYCLE_1 )            /* 'No Cooper' timing */
                   1370:                  || ( LineCycles == LINE_LEFT_MED_CYCLE_1+16 ) )       /* 'No Cooper' timing while removing bottom border */
1.1.1.11  root     1371:                {
1.1.1.15  root     1372:                        LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect med res overscan offset 0 byte\n" );
                   1373:                        ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 0 << 20 );
1.1.1.11  root     1374:                }
1.1.1.15  root     1375:                else if ( LineCycles == LINE_LEFT_MED_CYCLE_2 )         /* 'Best Part Of The Creation / PYM' timing */
1.1.1.11  root     1376:                {
1.1.1.15  root     1377:                        LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect med res overscan offset 2 bytes\n" );
                   1378:                        ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask |= BORDERMASK_OVERSCAN_MED_RES | ( 2 << 20 );
1.1.1.11  root     1379:                }
                   1380:        }
                   1381: 
1.1.1.15  root     1382:        /* If left border was opened with a hi/med res switch we need to check */
                   1383:        /* if the switch to low res can trigger a right hardware scrolling. */
                   1384:        /* We store the pixels count in DisplayPixelShift */
                   1385:        if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF_MED )
                   1386:                && ( Res == 0x00 ) && ( LineCycles <= LINE_SCROLL_1_CYCLE_50 ) )
1.1.1.11  root     1387:        {
1.1.1.24! root     1388:                /* The hi/med switch was a switch to do low res hardware scrolling */
        !          1389:                /* or to do a 'remove left' that includes a med res stabiliser, */
1.1.1.15  root     1390:                /* so we must cancel the med res overscan bit. */
                   1391:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= (~BORDERMASK_OVERSCAN_MED_RES);
1.1.1.11  root     1392: 
1.1.1.24! root     1393:                if ( LineCycles == LINE_LEFT_STAB_LOW )                 /* cycle 16 */
        !          1394:                {
        !          1395:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with med stab\n" );
        !          1396:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;
        !          1397:                }
        !          1398: 
        !          1399:                else if ( LineCycles == LINE_SCROLL_13_CYCLE_50 )       /* cycle 20 */
1.1.1.11  root     1400:                {
1.1.1.15  root     1401:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll\n" );
                   1402:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
1.1.1.11  root     1403:                }
1.1.1.15  root     1404:                else if ( LineCycles == LINE_SCROLL_9_CYCLE_50 )        /* cycle 24 */
1.1.1.11  root     1405:                {
1.1.1.15  root     1406:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll\n" );
                   1407:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
1.1.1.11  root     1408:                }
1.1.1.15  root     1409:                else if ( LineCycles == LINE_SCROLL_5_CYCLE_50 )        /* cycle 28 */
1.1.1.11  root     1410:                {
1.1.1.15  root     1411:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll\n" );
                   1412:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
1.1.1.11  root     1413:                }
1.1.1.15  root     1414:                else if ( LineCycles == LINE_SCROLL_1_CYCLE_50 )        /* cycle 32 */
1.1.1.11  root     1415:                {
1.1.1.15  root     1416:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll\n" );
                   1417:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
1.1.1.11  root     1418:                }
                   1419:        }
                   1420: 
1.1.1.18  root     1421:        /* Left border was removed with a hi/lo switch, then a med res switch was made */
                   1422:        /* Depending on the low res switch, the screen will be shifted as a low res overscan line */
                   1423:        /* This is a different method than the one used by ST Connexion with only 3 res switches */
                   1424:        /* (so we must cancel the med res overscan bit) */
                   1425:        if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_OVERSCAN_MED_RES )
                   1426:                && ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & ( 0xf << 20 ) ) == 0 )
                   1427:                && ( Res == 0x00 ) && ( LineCycles <= 40 )  )
                   1428:        {
1.1.1.24! root     1429:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= (~BORDERMASK_OVERSCAN_MED_RES);      /* cancel med res */
1.1.1.18  root     1430: 
                   1431:                if ( LineCycles == 28 )
                   1432:                {
                   1433:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 13 pixels right scroll 2\n" );
                   1434:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;
                   1435:                }
                   1436:                else if ( LineCycles == 32 )
                   1437:                {
                   1438:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 9 pixels right scroll 2\n" );
                   1439:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 9;
                   1440:                }
                   1441:                else if ( LineCycles == 36 )
                   1442:                {
                   1443:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 5 pixels right scroll 2\n" );
                   1444:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 5;
                   1445:                }
                   1446:                else if ( LineCycles == 40 )
                   1447:                {
                   1448:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect 1 pixel right scroll 2\n" );
                   1449:                        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 1;
                   1450:                }
                   1451:        }
                   1452: 
1.1.1.24! root     1453:        /* TEMP for 'closure' in WS2 */
        !          1454:        /* -> stay in hi res for 16 cycles to do the stab (hi/50/lo at 4/12/20) */
        !          1455:        if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
        !          1456:                && ( Res == 0x00 ) && ( LineCycles == 20 )
        !          1457:                && ( ShifterFrame.ResPosHi.LineCycles == 4 )
        !          1458:                && ( STMemory_ReadLong ( M68000_GetPC()-4 ) == 0x32883088 )     /* move.w a0,(a1) + move.w a0,(a0) */
        !          1459:           )
        !          1460:        {
        !          1461:                /* for now we simulate the same border as in ws1/3/4, but there's no med res in fact */
        !          1462:                LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with no med stab closure ws2\n" );
        !          1463:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask = BORDERMASK_LEFT_OFF_MED;
        !          1464:                ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;     /* see special case in Video_CopyScreenLineColor() */
1.1.1.15  root     1465:        }
1.1.1.24! root     1466:        /* TEMP for 'closure' in WS2 */
1.1.1.15  root     1467: 
1.1.1.24! root     1468:        /* TEMP for 'closure' in STE mode */
        !          1469:        /* -> stay in hi res for 16 cycles to do the stab (hi/50/lo at 0/8/16) */
        !          1470:        if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
        !          1471:                && ( Res == 0x00 ) && ( LineCycles == 16 )
        !          1472:                && ( ShifterFrame.ResPosHi.LineCycles == 0 )
        !          1473:                && ( STMemory_ReadLong ( M68000_GetPC()-4 ) == 0x32883088 )     /* move.w a0,(a1) + move.w a0,(a0) */
        !          1474:           )
        !          1475:        {
        !          1476:                /* for now we simulate the same border as in ws1/3/4, but there's no med res in fact */
        !          1477:                LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with no med stab closure ste\n" );
        !          1478:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask = BORDERMASK_LEFT_OFF_MED;
        !          1479:                ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;     /* see special case in Video_CopyScreenLineColor() */
        !          1480:        }
        !          1481:        /* TEMP for 'closure' in STE mode */
1.1.1.15  root     1482: 
1.1.1.24! root     1483:        /* TEMP for 'death of the left border' by TNT */
        !          1484:        /* -> stay in hi res for 16 cycles (hi/lo at 0/16) without stabiliser */
        !          1485:        if ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_OFF )
        !          1486:                && ( Res == 0x00 ) && ( LineCycles == 16 )
        !          1487:                && ( ShifterFrame.ResPosHi.LineCycles == 0 )
        !          1488:                && ( STMemory_ReadLong ( M68000_GetPC()-0x0c ) == 0x51c9fffc )  /* dbf d1,$fffc */
        !          1489:                && ( M68000_GetPC() == 0x72c )
        !          1490:           )
        !          1491:        {
        !          1492:                /* we simulate a 13 px scroll, which compensates for 2 bytes in the video planes */
        !          1493:                LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect remove left with no stab dolb\n" );
        !          1494:                ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 13;    /* see special case in Video_CopyScreenLineColor() */
        !          1495:        }
        !          1496:        /* TEMP for 'death of the left border' by TNT */
1.1.1.15  root     1497: 
                   1498: 
1.1.1.24! root     1499:        /* Store cycle position of this change of resolution */
1.1.1.15  root     1500:        ShifterFrame.Res = Res;
                   1501:        if ( Res == 0x02 )                                              /* high res */
                   1502:        {
                   1503:                ShifterFrame.ResPosHi.VBL = nVBLs;
                   1504:                ShifterFrame.ResPosHi.FrameCycles = FrameCycles;
                   1505:                ShifterFrame.ResPosHi.HBL = HblCounterVideo;
                   1506:                ShifterFrame.ResPosHi.LineCycles = LineCycles;
                   1507:        }
                   1508:        else if ( Res == 0x01 )                                         /* med res */
                   1509:        {
                   1510:                ShifterFrame.ResPosMed.VBL = nVBLs;
                   1511:                ShifterFrame.ResPosMed.FrameCycles = FrameCycles;
                   1512:                ShifterFrame.ResPosMed.HBL = HblCounterVideo;
                   1513:                ShifterFrame.ResPosMed.LineCycles = LineCycles;
                   1514:        }
                   1515:        else                                                            /* low res */
                   1516:        {
                   1517:                ShifterFrame.ResPosLo.VBL = nVBLs;
                   1518:                ShifterFrame.ResPosLo.FrameCycles = FrameCycles;
                   1519:                ShifterFrame.ResPosLo.HBL = HblCounterVideo;
                   1520:                ShifterFrame.ResPosLo.LineCycles = LineCycles;
                   1521:        }
                   1522: }
                   1523: 
                   1524: 
                   1525: 
1.1.1.24! root     1526: 
1.1.1.15  root     1527: /*-----------------------------------------------------------------------*/
                   1528: /**
1.1.1.24! root     1529:  * Update the state of the GLUE (border start/end, hbl position, timer b position, ...)
        !          1530:  * depending on the actual video frequency. This is where border removal and similar
        !          1531:  * tricks are detected.
        !          1532:  *
        !          1533:  * This function should be called after a write to the freq register
        !          1534:  * at $FF820A or to the resolution register at $FF8260.
        !          1535:  * We have 2 different parts for STF and STE, but the timing structure
        !          1536:  * is rather similar (on STE, GLUE and MMU chips were merged into the GST MCU chip
        !          1537:  * and we need to handle preload due to hscroll)
        !          1538:  *
        !          1539:  * As seen on a real chip, freq and res registers are merged into a single
        !          1540:  * state that can represent 3 values (50, 60 or 71 Hz).
        !          1541:  * The same method is used here, which means all comparisons are made relative to
        !          1542:  * the current video frequency (allowing for example to remove bottom border using
        !          1543:  * a switch to high resolution)
1.1.1.15  root     1544:  */
1.1.1.24! root     1545: 
        !          1546: /*
        !          1547:  * Examples of some common switches altering horizontal display start/end :
        !          1548:  * - Remove left border : +26 bytes
        !          1549:  *   This can be done with a hi/lo or a hi/med res switch between 504 and 8
        !          1550:  * - Remove left border STE : +20 bytes
        !          1551:  *   Switch back to lo/med should be at cycle 4
        !          1552:  * - Start right border near middle of the line : -106 bytes
        !          1553:  *   Switch to hi res just before the start of the right border in hi res, then go back to lo/med res
        !          1554:  * - Empty line switching res on STF/STE : switch to hi res on cycle 28, then go back to med/lo res
        !          1555:  *   This creates a 0 byte/blank line, the video counter won't change for this line
        !          1556:  *   (used in Lemmings demo (part 2) in 'Nostalgic Demo' by Oxygene)
        !          1557:  * - Empty line switching res on STF : switch to hi res just before the HBL (at ~502, hsync stop) then go back to lo/med res
        !          1558:  *   Next HBL will be a 0 byte/blank line (used in 'No Buddies Land' and 'Delirious Demo IV / NGC')
        !          1559:  * - Remove right border a second time after removing it a first time. Display will stop at cycle 512 instead of 460.
        !          1560:  *   Switch to hi res at ~462 (hsync start); This removes left border on next line too (used in 'Enchanted Lands')
        !          1561:  *   If right border was not removed, then we will get an empty line for the next HBL (used in 'Pax Plax Parralax' in 'Beyond' by Kruz)
        !          1562:  * - Blank line switching freq on STF : switch to 60 Hz on cycle 28, then go back to 50 Hz before pal start (cycle 56)
        !          1563:  *   This creates a blank line where no signal is displayed, but the video counter will still change for this line.
        !          1564:  *   This blank line can be combined with left/right border changes (used in 'Overscan Demos - part 6' by Paulo Simoes and 'Closure' by Sync)
        !          1565:  * - Add 2 bytes to left border : switch to 60 Hz before pos 52 to force an early start of the DE signal, then go back to 50 Hz.
        !          1566:  *   Note that depending on where the 50 Hz switch is made the HBL signal will be at position 508 (60 Hz line) or 512 (50 Hz line)
        !          1567:  *   Obtaining a +2 line with 512 cycles requires a 2 cycles precision and is "wakeup state" dependent
        !          1568:  *    - If switch to 50 Hz is made at cycle 54 (requires 2 cycle precision), then the line will start 2 bytes earlier and will be 50 Hz (HBL at cycle 512)
        !          1569:  *    - If switch to 50 Hz is made at cycle >= 56, then the line will start 2 bytes earlier and will be 60 Hz (HBL at cycle 508)
        !          1570:  * - Empty line switching freq on STF : start the line in 50 Hz, change to 60 Hz at the exact place
        !          1571:  *   where display is enabled in 50 Hz, then go back to 50 Hz.
        !          1572:  *   This creates a 0 byte line (used in 'shforstv' by Paulo Simoes and 'Closure' by Sync)
        !          1573:  * - Remove 2 bytes to the right : start the line in 50 Hz (pos 0 or 56) and change to 60 Hz before the position
        !          1574:  *   where display is disabled in 60 Hz (optionally go back to 50 Hz after this position, but not necessary)
        !          1575:  * - Remove right border : +44 bytes ; switch to 60 Hz at the position where the line ends in 50 Hz
        !          1576:  *   Some programs don't switch back to 50 Hz immediately (Sync screen in 'SNY II', 'Closure' by Sync)
        !          1577:  */
        !          1578: 
        !          1579: /*
        !          1580:   In Hatari, the state machine is updated only on every write to freq/res registers
        !          1581:   (instead of doing it on every cycle as on real chip).
        !          1582:   The following pseudo code should be equivalent to a state machine updated on
        !          1583:   every cycle (but some rare cases/combinations could be missing)
        !          1584: 
        !          1585:   if ( freq == 71 & pos <= start_hi )
        !          1586:     match_found;
        !          1587:     hbl = 224:
        !          1588:     if ( !no_de )
        !          1589:       start = 4;
        !          1590:       end = 164;
        !          1591:       remove left;
        !          1592:       if ( left+2 )
        !          1593:         cancel left+2;
        !          1594: 
        !          1595:   else if ( freq == 71 & pos <= blank_stop_50 )
        !          1596:     match_found;
        !          1597:     hbl = 224:
        !          1598:     if ( !no_de )
        !          1599:       end = 164;
        !          1600:       blank line no DE;
        !          1601:       if ( left+2 )
        !          1602:         cancel left+2;
        !          1603: 
        !          1604:   else if ( freq == 71 & pos <= start_50 )
        !          1605:     match_found;
        !          1606:     hbl = 224:
        !          1607:     if ( !no_de )
        !          1608:       end = 164;
        !          1609:       line no DE;
        !          1610:       if ( left+2 )
        !          1611:         cancel left+2;
        !          1612: 
        !          1613:   else if ( freq != 71 )
        !          1614:     if ( pos <= start_hi & remove left )
        !          1615:       cancel remove left;
        !          1616:     if ( pos <= blank_stop_50 & blank line no DE & !ignore line )
        !          1617:       cancel blank line no DE;
        !          1618:     if ( pos <= start_50 & line no DE & !blank line & !ignore line )
        !          1619:       cancel line no DE;
        !          1620: 
        !          1621:   if ( freq == 60  & pos < start_pal )
        !          1622:     match_found;
        !          1623:     hbl = 508;
        !          1624:     end = 372;
        !          1625:     right-2;
        !          1626:     if ( !no_de )
        !          1627:       if ( pos <= blank_stop_50 )
        !          1628:         blank line with DE;
        !          1629:       if ( start == 56 )
        !          1630:         start=52;
        !          1631:         left+2;
        !          1632: 
        !          1633:   else if ( freq == 50  & pos <= start_60 )
        !          1634:     match_found;
        !          1635:     hbl = 512;
        !          1636:     if ( !no_de )
        !          1637:       end = 376;
        !          1638:       if (right-2)
        !          1639:         cancel right-2
        !          1640:       if ( start == 52 )
        !          1641:         start=56;
        !          1642:         if ( left+2 )
        !          1643:           cancel left+2;
        !          1644: 
        !          1645:   else if ( freq == 50  & pos <= start_pal )
        !          1646:     match_found;
        !          1647:     hbl = 512;
        !          1648:     if ( !no_de )
        !          1649:       end = 376
        !          1650:       if (right-2)
        !          1651:         cancel right-2
        !          1652:       left+2 50 Hz
        !          1653: 
        !          1654:   if ( STF & freq == 60 & pos > start_60 & pos <= start_50 & !no_de )
        !          1655:     match_found;
        !          1656:     if ( start == 56 )
        !          1657:       start=0; end=0;
        !          1658:       empty line no DE;
        !          1659: 
        !          1660:   else if ( STE & freq == 60 & pos > preload_start_60 & pos <= preload_start_50 )
        !          1661:     match_found;
        !          1662:     if ( start == 56 )
        !          1663:       start=0; end=0;
        !          1664:       empty line no DE;
        !          1665: 
        !          1666: freq_test_next
        !          1667:   if ( match_found )
        !          1668:     goto freq_test_done
        !          1669: 
        !          1670: 
        !          1671:   if ( freq == 71 & pos <= end & pos <= end_hi & !no_de )
        !          1672:     end=164;
        !          1673:     stop middle;
        !          1674:     if (right-2)
        !          1675:       cancel right-2
        !          1676: 
        !          1677:   else if ( freq == 71 & pos <= end & !no_de )
        !          1678:     end=512;
        !          1679:     remove right / right full;
        !          1680: 
        !          1681:   else if ( freq == 71 & pos <= hsync_start_50/60 )
        !          1682:     empty line res 3 no sync;
        !          1683: 
        !          1684:   else if ( freq == 71 & pos <= hsync_stop_50/60 )
        !          1685:     empty line res 2 sync high;
        !          1686: 
        !          1687: 
        !          1688:   else if ( freq == 60 & pos <= end & pos <= end_60 & !no_de )
        !          1689:     if ( end == 376 )
        !          1690:       right-2;
        !          1691:     end=372;
        !          1692:     if ( stop middle )
        !          1693:       cancel stop middle;
        !          1694:     if ( remove right | right full )
        !          1695:       cancel remove right | right full;
        !          1696: 
        !          1697: 
        !          1698:   else if ( freq == 50 & pos <= end & pos <= end_50 & !no_de )
        !          1699:     end=376;
        !          1700:     if ( right-2 )
        !          1701:       cancel right-2;
        !          1702:     if ( stop middle )
        !          1703:       cancel stop middle;
        !          1704:     if ( remove right | right full )
        !          1705:       cancel remove right | right full;
        !          1706: 
        !          1707:   else if ( freq == 60 & pos <= end & pos > end_60 & pos <= end_50 & !no_de )
        !          1708:     if ( end == 376 )
        !          1709:       end=hsync_start_50/60;
        !          1710:       remove right;
        !          1711:       if ( right-2 )
        !          1712:         cancel right-2;
        !          1713: 
        !          1714:   else if ( freq != 71 & pos <= hsync_start_50/60 )
        !          1715:     if ( pos <= end )
        !          1716:       end = hsync_start_50/60;
        !          1717:       if ( remove right full )
        !          1718:         cancel remove right full;
        !          1719:     else if ( empty line res 3 no sync )
        !          1720:       cancel empty line res 3 no sync;
        !          1721: 
        !          1722:   else if ( freq != 71 & pos <= hsync_stop_50/60 )
        !          1723:     if ( empty line res 2 sync high )
        !          1724:       cancel empty line res 2 sync high;
        !          1725: 
        !          1726: freq_test_done
        !          1727: 
        !          1728: */
        !          1729: 
        !          1730: 
        !          1731: static void Video_Update_Glue_State ( int FrameCycles , int HblCounterVideo , int LineCycles , bool WriteToRes )
        !          1732: {
        !          1733:        int FreqHz;
        !          1734:        int DE_start;
        !          1735:        int DE_end;
        !          1736:        int HBL_Pos;
        !          1737:        int nCyclesPerLine_new;
        !          1738:        int Freq_match_found;
        !          1739:        Uint32  BorderMask;
        !          1740:        int Top_Pos;
        !          1741:        int Bottom_Pos;
        !          1742: 
        !          1743: 
        !          1744:        /* FreqHz will be 50, 60 or 71 */
        !          1745:        if ( IoMem[0xff8260] & 2 )
        !          1746:                FreqHz = VIDEO_71HZ;
        !          1747:        else
        !          1748:        {
        !          1749:                /* We're only interested in bit 1 (50/60Hz) */
        !          1750:                FreqHz = ( IoMem[0xff820a] & 2 ) ? VIDEO_50HZ : VIDEO_60HZ;
        !          1751:        }
        !          1752: 
        !          1753: 
        !          1754:        /* GLUE will latch freq register 1 cycle later than res register */
        !          1755:        /* To take this into account in our case, we subtract 1 cycle to the res write position */
        !          1756:        /* that will be used for all the comparison in the state machine */
        !          1757:        /* (this is not the case for the STE GST MCU) */
        !          1758:        if ( Config_IsMachineST() && WriteToRes )
        !          1759:                LineCycles--;
        !          1760: 
        !          1761: 
        !          1762:        DE_start = ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle;
        !          1763:        DE_end = ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle;
        !          1764:        BorderMask = ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask;
        !          1765:        HBL_Pos = -1;
        !          1766:        nCyclesPerLine_new = -1;
        !          1767:        Freq_match_found = 0;
        !          1768: 
        !          1769: //fprintf ( stderr , "pom %d %d-%d %d-%d\n" , nHBL , nStartHBL , nEndHBL , DE_start , DE_end );
        !          1770: 
        !          1771: 
        !          1772:        /*
        !          1773:         * Check Freq's value before DE_start
        !          1774:         * We need 2 different cases for ST and STE
        !          1775:         */
        !          1776: 
        !          1777:        /*************** STF ***************/
        !          1778:        if ( Config_IsMachineST() )
        !          1779:        {
        !          1780:          if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->H_Start_Hi ) )
        !          1781:          {
        !          1782:            Freq_match_found = 1;
        !          1783:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi;             /* 220/224 */
        !          1784:            nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
        !          1785:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1786:            {
        !          1787:              DE_start = pVideoTiming->H_Start_Hi;              /* 4 */
        !          1788:              DE_end = pVideoTiming->H_Stop_Hi;                 /* 164 */
        !          1789: 
        !          1790:              BorderMask |= BORDERMASK_LEFT_OFF;
        !          1791:              ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -4;              /* screen is shifted 4 pixels to the left */
        !          1792:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" , DE_start , DE_end );
        !          1793: 
        !          1794:              if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          1795:              {
        !          1796:                BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          1797:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          1798:              }
        !          1799:            }
        !          1800:          }
        !          1801: 
        !          1802:          else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
        !          1803:          {
        !          1804:            Freq_match_found = 1;
        !          1805:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi;             /* 220/224 */
        !          1806:            nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
        !          1807:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1808:            {
        !          1809:              DE_end = pVideoTiming->H_Stop_Hi;                 /* 164 */
        !          1810: 
        !          1811:              BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE );
        !          1812:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line no DE res stf %d<->%d\n" , DE_start , DE_end );
        !          1813:            }
        !          1814: 
        !          1815:            if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          1816:            {
        !          1817:              BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          1818:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          1819:            }
        !          1820:          }
        !          1821: 
        !          1822:          else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->H_Start_Low_50 ) )
        !          1823:          {
        !          1824:            Freq_match_found = 1;
        !          1825:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi;             /* 220/224 */
        !          1826:            nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
        !          1827:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1828:            {
        !          1829:              DE_end = pVideoTiming->H_Stop_Hi;                 /* 164 */
        !          1830: 
        !          1831:              BorderMask |= BORDERMASK_NO_DE;
        !          1832:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE res stf %d<->%d\n" , DE_start , DE_end );
        !          1833:            }
        !          1834: 
        !          1835:            if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          1836:            {
        !          1837:              BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          1838:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          1839:            }
        !          1840:          }
        !          1841: 
        !          1842:          else if ( FreqHz != VIDEO_71HZ )
        !          1843:          {
        !          1844:            if ( ( LineCycles <= pVideoTiming->H_Start_Hi ) && ( BorderMask & BORDERMASK_LEFT_OFF ) )
        !          1845:            {
        !          1846:              if ( FreqHz == VIDEO_50HZ )
        !          1847:                DE_start = pVideoTiming->H_Start_Low_50;        /* 56 */
        !          1848:              else
        !          1849:              {
        !          1850:                DE_start = pVideoTiming->H_Start_Low_60;        /* 52 */
        !          1851:                BorderMask |= BORDERMASK_LEFT_PLUS_2;
        !          1852:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz %d<->%d\n" , DE_start , DE_end );
        !          1853:              }
        !          1854: 
        !          1855:              BorderMask &= ~BORDERMASK_LEFT_OFF;
        !          1856:              ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;
        !          1857:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove left %d<->%d\n" , DE_start , DE_end );
        !          1858:            }
        !          1859: 
        !          1860:            if ( ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) && ( BorderMask & ( BORDERMASK_BLANK | BORDERMASK_NO_DE ) )
        !          1861:                && !( BorderMask & BORDERMASK_NO_COUNT ) )
        !          1862:            {
        !          1863:              BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE );
        !          1864:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel blank line no DE %d<->%d\n" , DE_start , DE_end );
        !          1865:            }
        !          1866:            else if ( ( LineCycles <= pVideoTiming->H_Start_Low_50 ) && ( BorderMask & BORDERMASK_NO_DE ) && !( BorderMask & BORDERMASK_BLANK )
        !          1867:                && !( BorderMask & BORDERMASK_NO_COUNT ) )
        !          1868:            {
        !          1869:              /* we can cancel a "line no de", but not a "blank line no de" */
        !          1870:              BorderMask &= ~BORDERMASK_NO_DE;
        !          1871:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel line no DE %d<->%d\n" , DE_start , DE_end );
        !          1872:            }
        !          1873:          }
        !          1874: 
        !          1875:          /* The line was in 50 Hz and continues in 60 Hz */
        !          1876:          if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles < pVideoTiming->Line_Set_Pal ) )
        !          1877:          {
        !          1878: //fprintf ( stderr , "pom0a\n" );
        !          1879:            Freq_match_found = 1;
        !          1880:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_60;         /* 504/508 */
        !          1881:            nCyclesPerLine_new = CYCLES_PER_LINE_60HZ;
        !          1882:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1883:            {
        !          1884:              DE_end = pVideoTiming->H_Stop_Low_60;             /* 372 */
        !          1885:        
        !          1886:              BorderMask |= BORDERMASK_RIGHT_MINUS_2;
        !          1887:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" , DE_start , DE_end );
        !          1888: 
        !          1889:              if ( ( LineCycles > pVideoTiming->Blank_Stop_Low_60 ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
        !          1890:              {
        !          1891:                BorderMask |= BORDERMASK_BLANK;
        !          1892:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line freq stf %d<->%d\n" , DE_start , DE_end );
        !          1893:              }
        !          1894:        
        !          1895:              if ( DE_start == pVideoTiming->H_Start_Low_50 )   /*  56 */
        !          1896:              {
        !          1897:                DE_start = pVideoTiming->H_Start_Low_60;        /*  52 */
        !          1898:        
        !          1899:                BorderMask |= BORDERMASK_LEFT_PLUS_2;
        !          1900:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz %d<->%d\n" , DE_start , DE_end );
        !          1901:              }
        !          1902:            }
        !          1903:          }
        !          1904: 
        !          1905:          else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->H_Start_Low_60 ) )
        !          1906:          {
        !          1907: //fprintf ( stderr , "pom1a\n" );
        !          1908:            Freq_match_found = 1;
        !          1909:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50;         /* 508/512 */
        !          1910:            nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
        !          1911:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1912:            {
        !          1913:              DE_end = pVideoTiming->H_Stop_Low_50;             /* 376 */
        !          1914: 
        !          1915:              if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          1916:              {
        !          1917:                BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          1918:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          1919:              }
        !          1920: 
        !          1921:              if ( DE_start == pVideoTiming->H_Start_Low_60 )   /*  52 */
        !          1922:              {
        !          1923:                DE_start = pVideoTiming->H_Start_Low_50;        /*  56 */
        !          1924: 
        !          1925:                if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          1926:                {
        !          1927:                  BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          1928:                  LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          1929:                }
        !          1930:              }
        !          1931:            }
        !          1932:          }
        !          1933: 
        !          1934:          else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->Line_Set_Pal ) )
        !          1935:          {
        !          1936: //fprintf ( stderr , "pom2a\n" );
        !          1937:            Freq_match_found = 1;
        !          1938:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50;         /* 508/512 */
        !          1939:            nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
        !          1940:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1941:            {
        !          1942:              DE_end = pVideoTiming->H_Stop_Low_50;             /* 376 */
        !          1943: 
        !          1944:              if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          1945:              {
        !          1946:                BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          1947:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          1948:              }
        !          1949: 
        !          1950:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 50Hz %d<->%d\n" , DE_start , DE_end );
        !          1951:            }
        !          1952:          }
        !          1953: 
        !          1954:          if ( ( FreqHz == VIDEO_60HZ )
        !          1955:            && ( LineCycles > pVideoTiming->H_Start_Low_60 ) && ( LineCycles <= pVideoTiming->H_Start_Low_50 )
        !          1956:            && !( BorderMask & BORDERMASK_NO_DE ) )
        !          1957:          {
        !          1958:            Freq_match_found = 1;
        !          1959: //fprintf ( stderr , "pom3a\n" );
        !          1960: 
        !          1961:            if ( DE_start == pVideoTiming->H_Start_Low_50 )     /*  56 */
        !          1962:            {
        !          1963:              DE_start = 0 ;
        !          1964:              DE_end = 0;
        !          1965: 
        !          1966:              BorderMask |= BORDERMASK_NO_DE;
        !          1967:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE freq stf %d<->%d\n" , DE_start , DE_end );
        !          1968:            }
        !          1969:          }
        !          1970:        }
        !          1971: 
        !          1972:        /*************** STE ***************/
        !          1973:        else if ( Config_IsMachineSTE() )
        !          1974:        {
        !          1975:          if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->H_Start_Hi ) )
        !          1976:          {
        !          1977:            Freq_match_found = 1;
        !          1978:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi;             /* 220/224 */
        !          1979:            nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
        !          1980:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          1981:            {
        !          1982:              DE_start = pVideoTiming->H_Start_Hi;              /* 4 */
        !          1983:              DE_end = pVideoTiming->H_Stop_Hi;                 /* 164 */
        !          1984: 
        !          1985:              BorderMask |= BORDERMASK_LEFT_OFF;
        !          1986:              ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -4;              /* screen is shifted 4 pixels to the left */
        !          1987:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" , DE_start , DE_end );
        !          1988: 
        !          1989:              if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          1990:              {
        !          1991:                BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          1992:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          1993:              }
        !          1994:            }
        !          1995:          }
        !          1996: 
        !          1997:          else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
        !          1998:          {
        !          1999:            Freq_match_found = 1;
        !          2000:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi;             /* 220/224 */
        !          2001:            nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
        !          2002:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          2003:            {
        !          2004:              DE_end = pVideoTiming->H_Stop_Hi;                 /* 164 */
        !          2005: 
        !          2006:              BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE );
        !          2007:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line no DE res ste %d<->%d\n" , DE_start , DE_end );
        !          2008:            }
        !          2009: 
        !          2010:            if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          2011:            {
        !          2012:              BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          2013:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          2014:            }
        !          2015:          }
        !          2016: 
        !          2017:          else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= pVideoTiming->Preload_Start_Low_50 ) )
        !          2018:          {
        !          2019:            Freq_match_found = 1;
        !          2020:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Hi;             /* 220/224 */
        !          2021:            nCyclesPerLine_new = CYCLES_PER_LINE_71HZ;
        !          2022:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          2023:            {
        !          2024:              DE_end = pVideoTiming->H_Stop_Hi;                 /* 164 */
        !          2025: 
        !          2026:              BorderMask |= BORDERMASK_NO_DE;
        !          2027:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE res ste %d<->%d\n" , DE_start , DE_end );
        !          2028:            }
        !          2029: 
        !          2030:            if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          2031:            {
        !          2032:              BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          2033:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 %d<->%d\n" , DE_start , DE_end );
        !          2034:            }
        !          2035:          }
        !          2036: 
        !          2037:          else if ( FreqHz != VIDEO_71HZ )
        !          2038:          {
        !          2039:            if ( ( LineCycles < pVideoTiming->H_Start_Hi ) && ( BorderMask & BORDERMASK_LEFT_OFF ) )
        !          2040:            {
        !          2041:              if ( FreqHz == VIDEO_50HZ )
        !          2042:                DE_start = pVideoTiming->H_Start_Low_50;        /* 56 */
        !          2043:              else
        !          2044:              {
        !          2045:                DE_start = pVideoTiming->H_Start_Low_60;        /* 52 */
        !          2046:                BorderMask |= BORDERMASK_LEFT_PLUS_2;
        !          2047:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz %d<->%d\n" , DE_start , DE_end );
        !          2048:              }
        !          2049: 
        !          2050:              BorderMask &= ~BORDERMASK_LEFT_OFF;
        !          2051:              ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = 0;
        !          2052:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove left %d<->%d\n" , DE_start , DE_end );
        !          2053:            }
        !          2054:            else if ( ( LineCycles == pVideoTiming->H_Start_Hi ) && ( BorderMask & BORDERMASK_LEFT_OFF ) )
        !          2055:            {
        !          2056:              DE_start = pVideoTiming->Preload_Start_Hi + 16;
        !          2057:              BorderMask &= ~BORDERMASK_LEFT_OFF;
        !          2058:              BorderMask |= BORDERMASK_LEFT_OFF_2_STE;
        !          2059:              ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift = -8;              /* screen is shifted 8 pixels to the left */
        !          2060:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left 2 ste %d<->%d\n" , DE_start , DE_end );
        !          2061:            }
        !          2062: 
        !          2063:            if ( ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) && ( BorderMask & ( BORDERMASK_BLANK | BORDERMASK_NO_DE ) )
        !          2064:                && !( BorderMask & BORDERMASK_NO_COUNT ) )
        !          2065:            {
        !          2066:              BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE );
        !          2067:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel blank line no DE %d<->%d\n" , DE_start , DE_end );
        !          2068:            }
        !          2069:            else if ( ( LineCycles <= pVideoTiming->Preload_Start_Low_50 ) && ( BorderMask & BORDERMASK_NO_DE ) && !( BorderMask & BORDERMASK_BLANK )
        !          2070:                && !( BorderMask & BORDERMASK_NO_COUNT ) )
        !          2071:            {
        !          2072:              /* we can cancel a "line no de", but not a "blank line no de" */
        !          2073:              BorderMask &= ~BORDERMASK_NO_DE;
        !          2074:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel line no DE %d<->%d\n" , DE_start , DE_end );
        !          2075:            }
        !          2076:          }
        !          2077: 
        !          2078:          /* The line was in 50 Hz and continues in 60 Hz */
        !          2079:          if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles < pVideoTiming->Line_Set_Pal ) )
        !          2080:          {
        !          2081: //fprintf ( stderr , "pom0a\n" );
        !          2082:            Freq_match_found = 1;
        !          2083:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_60;         /* 504/508 */
        !          2084:            nCyclesPerLine_new = CYCLES_PER_LINE_60HZ;
        !          2085:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          2086:            {
        !          2087:              DE_end = pVideoTiming->H_Stop_Low_60;             /* 372 */
        !          2088:        
        !          2089:              BorderMask |= BORDERMASK_RIGHT_MINUS_2;
        !          2090:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" , DE_start , DE_end );
        !          2091: 
        !          2092:              if ( ( LineCycles > pVideoTiming->Blank_Stop_Low_60 ) && ( LineCycles <= pVideoTiming->Blank_Stop_Low_50 ) )
        !          2093:              {
        !          2094:                BorderMask |= BORDERMASK_BLANK;
        !          2095:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect blank line freq ste %d<->%d\n" , DE_start , DE_end );
        !          2096:              }
        !          2097:        
        !          2098:              if ( LineCycles <= pVideoTiming->Preload_Start_Low_60 )
        !          2099:              {
        !          2100:                if ( DE_start == pVideoTiming->H_Start_Low_50 ) /*  56 */
        !          2101:                {
        !          2102:                  DE_start = pVideoTiming->H_Start_Low_60;      /*  52 */
        !          2103:        
        !          2104:                  BorderMask |= BORDERMASK_LEFT_PLUS_2;
        !          2105:                  LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 60Hz ste %d<->%d\n" , DE_start , DE_end );
        !          2106:                }
        !          2107:              }
        !          2108:              else
        !          2109:              {
        !          2110:                /* normal line starting at 56 but running at 60 Hz ; we don't do anything special */
        !          2111:              }
        !          2112:            }
        !          2113:          }
        !          2114: 
        !          2115:          else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->Preload_Start_Low_60 ) )
        !          2116:          {
        !          2117: //fprintf ( stderr , "pom1a\n" );
        !          2118:            Freq_match_found = 1;
        !          2119:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50;         /* 508/512 */
        !          2120:            nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
        !          2121:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          2122:            {
        !          2123:              DE_end = pVideoTiming->H_Stop_Low_50;             /* 376 */
        !          2124: 
        !          2125:              if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          2126:              {
        !          2127:                BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          2128:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          2129:              }
        !          2130: 
        !          2131:              if ( DE_start == pVideoTiming->H_Start_Low_60 )   /*  52 */
        !          2132:              {
        !          2133:                DE_start = pVideoTiming->H_Start_Low_50;        /*  56 */
        !          2134: 
        !          2135:                if ( BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          2136:                {
        !          2137:                  BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          2138:                  LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel left+2 ste %d<->%d\n" , DE_start , DE_end );
        !          2139:                }
        !          2140:              }
        !          2141:            }
        !          2142:          }
        !          2143: 
        !          2144:          else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= pVideoTiming->Line_Set_Pal ) )
        !          2145:          {
        !          2146: //fprintf ( stderr , "pom2a\n" );
        !          2147:            Freq_match_found = 1;
        !          2148:            HBL_Pos = pVideoTiming->Hbl_Int_Pos_Low_50;         /* 508/512 */
        !          2149:            nCyclesPerLine_new = CYCLES_PER_LINE_50HZ;
        !          2150:            if ( !( BorderMask & BORDERMASK_NO_DE ) )
        !          2151:            {
        !          2152:              DE_end = pVideoTiming->H_Stop_Low_50;             /* 376 */
        !          2153: 
        !          2154:              if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          2155:              {
        !          2156:                BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          2157:                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          2158:              }
        !          2159: 
        !          2160:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 50Hz ste %d<->%d\n" , DE_start , DE_end );
        !          2161:            }
        !          2162:          }
        !          2163: 
        !          2164:          if ( ( FreqHz == VIDEO_60HZ )
        !          2165:            && ( LineCycles > pVideoTiming->Preload_Start_Low_60 ) && ( LineCycles <= pVideoTiming->Preload_Start_Low_50 )
        !          2166:            && !( BorderMask & BORDERMASK_NO_DE ) )
        !          2167:          {
        !          2168:            Freq_match_found = 1;
        !          2169: //fprintf ( stderr , "pom3a\n" );
        !          2170: 
        !          2171:            if ( DE_start == pVideoTiming->H_Start_Low_50 )     /*  56 */
        !          2172:            {
        !          2173:              DE_start = 0 ;
        !          2174:              DE_end = 0;
        !          2175: 
        !          2176:              BorderMask |= BORDERMASK_NO_DE;
        !          2177:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect line no DE freq ste %d<->%d\n" , DE_start , DE_end );
        !          2178:            }
        !          2179:          }
        !          2180:        }
        !          2181: 
        !          2182: 
        !          2183: //fprintf ( stderr , "pom0 %d-%d\n" , DE_start , DE_end );
        !          2184:        /* If we found a match before DE_start, then there's no need */
        !          2185:        /* to check between DE_start and DE_end */
        !          2186:        if ( Freq_match_found )
        !          2187:          goto Freq_Test_Done;
        !          2188: 
        !          2189: //fprintf ( stderr , "pom1\n" );
        !          2190: 
        !          2191:        /*
        !          2192:         * Check Freq's value between DE_start and DE_end
        !          2193:         */
        !          2194: 
        !          2195:        if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= DE_end ) && ( LineCycles <= pVideoTiming->H_Stop_Hi )  /* 160 */
        !          2196:          && !( BorderMask & BORDERMASK_NO_DE ) )
1.1.1.15  root     2197:        {
1.1.1.24! root     2198: //fprintf ( stderr , "pom3\n" );
        !          2199:          DE_end = pVideoTiming->H_Stop_Hi;                     /* 164 */
        !          2200: 
        !          2201:          BorderMask |= BORDERMASK_STOP_MIDDLE;
        !          2202:          LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect stop middle %d<->%d\n" , DE_start , DE_end );
1.1.1.15  root     2203: 
1.1.1.24! root     2204:          if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          2205:          {
        !          2206:            BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          2207:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          2208:          }
1.1.1.15  root     2209:        }
                   2210: 
1.1.1.24! root     2211:        else if ( ( FreqHz == VIDEO_71HZ ) && ( LineCycles <= DE_end ) && !( BorderMask & BORDERMASK_NO_DE ) )
1.1.1.15  root     2212:        {
1.1.1.24! root     2213: //fprintf ( stderr , "pom3\n" );
        !          2214:          DE_end = LINE_END_CYCLE_FULL;                         /* 512 */
        !          2215: 
        !          2216:          BorderMask |= ( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL );
        !          2217:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= BORDERMASK_LEFT_OFF;     /* no left border on next line */
        !          2218:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
        !          2219: 
        !          2220:          LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove right/right full %d<->%d\n" , DE_start , DE_end );
1.1.1.15  root     2221: 
                   2222:        }
                   2223: 
1.1.1.24! root     2224:        else if ( ( FreqHz == VIDEO_71HZ )
        !          2225:                && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low ) )    /* 458/462 depends on 50/60 freq (line cycles-50) */
        !          2226:        {
        !          2227:          BorderMask |= BORDERMASK_NO_SYNC;
        !          2228:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
        !          2229:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = 0;
        !          2230:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = 0;
        !          2231:          BlankLines++;                                         /* glue's line counter not incremented, display ends 1 line later */
1.1.1.11  root     2232: 
1.1.1.24! root     2233:          LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res 3 no sync %d<->%d\n" , DE_start , DE_end );
        !          2234:        }
1.1.1.11  root     2235: 
1.1.1.24! root     2236:        else if ( ( FreqHz == VIDEO_71HZ )
        !          2237:                && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Stop_Offset_Low ) )     /* 498/502 depends on 50/60 freq (line cycles-10) */
        !          2238:        {
        !          2239:          BorderMask |= BORDERMASK_SYNC_HIGH;
        !          2240:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= ( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
        !          2241:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = 0;
        !          2242:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = 0;
        !          2243:          BlankLines++;                                         /* glue's line counter not incremented, display ends 1 line later */
1.1.1.11  root     2244: 
1.1.1.24! root     2245:          LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect empty line res 2 sync high %d<->%d\n" , DE_start , DE_end );
        !          2246:        }
1.1.1.11  root     2247: 
1.1.1.24! root     2248:        else if ( FreqHz == VIDEO_71HZ )                        /* rest of the line after HSync_Stop_Offset_Low */
        !          2249:        {
        !          2250:          /* Next line will start as "remove left" by default (eg E605 by Light or DHS demos on STE) */
        !          2251:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask |= BORDERMASK_LEFT_OFF;
        !          2252:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
        !          2253:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayEndCycle = pVideoTiming->H_Stop_Hi;
        !          2254:          ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayPixelShift = -4;                /* screen is shifted 4 pixels to the left */
1.1.1.11  root     2255: 
1.1.1.24! root     2256:          LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left on next hbl %d<->%d\n" , DE_start , DE_end );
        !          2257:        }
1.1.1.11  root     2258: 
1.1.1.24! root     2259:        if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles <= DE_end ) && ( LineCycles <= pVideoTiming->H_Stop_Low_60 )      /* 372 */
        !          2260:          && !( BorderMask & BORDERMASK_NO_DE ) )
        !          2261:        {
        !          2262: //fprintf ( stderr , "pom2\n" );
        !          2263:          if ( DE_end == pVideoTiming->H_Stop_Low_50 )          /* 376 */
        !          2264:          {
        !          2265:            BorderMask |= BORDERMASK_RIGHT_MINUS_2;
        !          2266:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect right-2 %d<->%d\n" , DE_start , DE_end );
        !          2267:          }
1.1.1.11  root     2268: 
1.1.1.24! root     2269:          DE_end = pVideoTiming->H_Stop_Low_60;                 /* 372 */
1.1.1.15  root     2270: 
1.1.1.24! root     2271:          if ( BorderMask & BORDERMASK_STOP_MIDDLE )
        !          2272:          {
        !          2273:            BorderMask &= ~BORDERMASK_STOP_MIDDLE;
        !          2274:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel stop middle %d<->%d\n" , DE_start , DE_end );
        !          2275:          }
        !          2276:          else if ( BorderMask & ( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL ) )
        !          2277:          {
        !          2278:            BorderMask &= ~( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL );
        !          2279:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~BORDERMASK_LEFT_OFF;
        !          2280:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
        !          2281:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove right/right full %d<->%d\n" , DE_start , DE_end );
        !          2282:          }
        !          2283:        }
1.1.1.11  root     2284: 
1.1.1.24! root     2285:        else if ( ( FreqHz == VIDEO_50HZ ) && ( LineCycles <= DE_end ) && ( LineCycles <= pVideoTiming->H_Stop_Low_50 ) /* 376 */
        !          2286:          && !( BorderMask & BORDERMASK_NO_DE ) )
        !          2287:        {
        !          2288: //fprintf ( stderr , "pom3\n" );
        !          2289:          DE_end = pVideoTiming->H_Stop_Low_50;                 /* 376 */
1.1.1.13  root     2290: 
1.1.1.24! root     2291:          if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          2292:          {
        !          2293:            BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          2294:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          2295:          }
        !          2296:          else if ( BorderMask & BORDERMASK_STOP_MIDDLE )
        !          2297:          {
        !          2298:            BorderMask &= ~BORDERMASK_STOP_MIDDLE;
        !          2299:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel stop middle %d<->%d\n" , DE_start , DE_end );
        !          2300:          }
        !          2301:          else if ( BorderMask & ( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL ) )
        !          2302:          {
        !          2303:            BorderMask &= ~( BORDERMASK_RIGHT_OFF | BORDERMASK_RIGHT_OFF_FULL );
        !          2304:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~BORDERMASK_LEFT_OFF;
        !          2305:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
        !          2306:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove right/right full %d<->%d\n" , DE_start , DE_end );
        !          2307:          }
        !          2308:        }
        !          2309: 
        !          2310:        else if ( ( FreqHz == VIDEO_60HZ ) && ( LineCycles <= DE_end )
        !          2311:          && ( LineCycles > pVideoTiming->H_Stop_Low_60 ) && ( LineCycles <= pVideoTiming->H_Stop_Low_50 )
        !          2312:          && !( BorderMask & BORDERMASK_NO_DE ) )
        !          2313:        {
        !          2314: //fprintf ( stderr , "pom4\n" );
        !          2315:          if ( DE_end == pVideoTiming->H_Stop_Low_50 )          /* 376 */
        !          2316:          {
        !          2317:            DE_end = nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low;     /* 458/462 depends on 50/60 freq (line cycles-50) */
        !          2318: 
        !          2319:            BorderMask |= BORDERMASK_RIGHT_OFF;
        !          2320:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove right %d<->%d\n" , DE_start , DE_end );
1.1.1.15  root     2321: 
1.1.1.24! root     2322:            if ( BorderMask & BORDERMASK_RIGHT_MINUS_2 )
        !          2323:            {
        !          2324:              BorderMask &= ~BORDERMASK_RIGHT_MINUS_2;
        !          2325:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel right-2 %d<->%d\n" , DE_start , DE_end );
        !          2326:            }
        !          2327:          }
        !          2328:        }
        !          2329: 
        !          2330:        else if ( ( FreqHz != VIDEO_71HZ )
        !          2331:                && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low ) )    /* 458/462 depends on 50/60 freq (line cycles-50) */
        !          2332:        {
        !          2333: //fprintf ( stderr , "pom5\n" );
        !          2334:          if ( LineCycles <= DE_end )
        !          2335:          {
        !          2336:            DE_end = nCyclesPerLine + pVideoTiming->HSync_Start_Offset_Low;     /* 458/462 depends on 50/60 freq (line cycles-50) */
1.1.1.15  root     2337: 
1.1.1.24! root     2338:            if ( BorderMask & BORDERMASK_RIGHT_OFF_FULL )
        !          2339:            {
        !          2340:              BorderMask &= ~BORDERMASK_RIGHT_OFF_FULL;
        !          2341:              ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~BORDERMASK_LEFT_OFF;
        !          2342:              ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
        !          2343:              LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel remove right full %d<->%d\n" , DE_start , DE_end );
        !          2344:            }
        !          2345:          }
        !          2346: 
        !          2347:          else if ( BorderMask & BORDERMASK_NO_SYNC )
        !          2348:          {
        !          2349:            BorderMask &= ~BORDERMASK_NO_SYNC;
        !          2350:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
        !          2351:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
        !          2352:            BlankLines--;
        !          2353:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel empty line res 3 no sync %d<->%d\n" , DE_start , DE_end );
        !          2354:          }
        !          2355:        }
1.1.1.15  root     2356: 
1.1.1.24! root     2357:        else if ( ( FreqHz != VIDEO_71HZ )
        !          2358:                && ( LineCycles <= nCyclesPerLine + pVideoTiming->HSync_Stop_Offset_Low ) )     /* 498/502 depends on 50/60 freq (line cycles-10) */
        !          2359:        {
        !          2360: //fprintf ( stderr , "pom6\n" );
        !          2361:          if ( BorderMask & BORDERMASK_SYNC_HIGH )
        !          2362:          {
        !          2363:            BorderMask &= ~BORDERMASK_SYNC_HIGH;
        !          2364:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].BorderMask &= ~( BORDERMASK_BLANK | BORDERMASK_NO_DE | BORDERMASK_NO_COUNT );
        !          2365:            ShifterFrame.ShifterLines[ HblCounterVideo+1 ].DisplayStartCycle = -1;
        !          2366:            BlankLines--;
        !          2367:            LOG_TRACE ( TRACE_VIDEO_BORDER_H , "cancel empty line res 2 sync high %d<->%d\n" , DE_start , DE_end );
        !          2368:          }
        !          2369:        }
        !          2370: 
        !          2371: 
        !          2372:        /* Go here directly if there's no more Freq/Res changes to test */
        !          2373: Freq_Test_Done:
        !          2374: 
        !          2375:        /* Update HBL's position only if display has not reached pos pVideoTiming->Line_Set_Pal */
        !          2376:        /* and HBL interrupt was already handled at the beginning of this line. */
        !          2377:        if ( ( HBL_Pos > 0 ) && ( HblCounterVideo == nHBL ) )
        !          2378:        {
        !          2379:                /* Don't modify HBL's position now if we're handling the special HBL for video counter restart */
        !          2380:                if ( RestartVideoCounter == false )
        !          2381:                        Video_AddInterruptHBL ( HblCounterVideo , HBL_Pos );
        !          2382: 
        !          2383:                ShifterFrame.HBL_CyclePos = HBL_Pos;
        !          2384: 
        !          2385: #ifndef OLD_CPU_SHIFT
        !          2386:                if ( nCyclesPerLine_new > 0 )
        !          2387:                        nCyclesPerLine_new <<= nCpuFreqShift;
        !          2388: #endif
        !          2389:        // TODO group the 3 if's into 1
        !          2390: 
        !          2391:                /* In case we're mixing 50 Hz (512 cycles) and 60 Hz (508 cycles) lines on the same screen, */
        !          2392:                /* we must update the position where the next VBL will happen (instead of the initial value in CyclesPerVBL) */
        !          2393:                /* We check if number of cycles per line changes only on the last line, and if so, we update the VBL's position */
        !          2394:                /* Since we setup VBL at the start of the last HBL, any change of number of cycles per line will */
        !          2395:                /* in fact change the position of the next VBL */
        !          2396:                if ( ( nCyclesPerLine_new > 0 )
        !          2397:                  && ( nHBL == nScanlinesPerFrame-1 ) && ( nCyclesPerLine != nCyclesPerLine_new ) )
        !          2398:                {
        !          2399:                        CyclesPerVBL += ( nCyclesPerLine_new - nCyclesPerLine );                /* +4 or -4 */
        !          2400:                        CycInt_ModifyInterrupt ( nCyclesPerLine_new - nCyclesPerLine , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL );
        !          2401:                }
        !          2402: 
        !          2403:                /* If HBL_Pos changed, the cycles per line might change too */
        !          2404:                if ( nCyclesPerLine_new > 0 )
        !          2405:                        nCyclesPerLine = nCyclesPerLine_new;
        !          2406:        }
        !          2407: 
        !          2408: 
        !          2409:        /* Update Timer B's position if DE start/end changed (and this is not an empty 0 byte line) */
        !          2410:        /* In most cases Timer B will count end of line events, but it can also count start of line events */
        !          2411:        /* (eg 'Seven Gates Of Jambala') so we need to check both start/end values */
        !          2412:        if ( ( DE_end > 0 )
        !          2413:            && ( ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle != DE_start )
        !          2414:              || ( ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle != DE_end ) ) )
        !          2415:        {
        !          2416:                LineTimerBPos = Video_TimerB_GetPosFromDE ( DE_start , DE_end );
        !          2417:                Video_AddInterruptTimerB ( HblCounterVideo , LineCycles , LineTimerBPos );
        !          2418:        }
        !          2419: 
        !          2420: 
        !          2421:        /*
        !          2422:         * Check if top/bottom borders' position should change
        !          2423:         */
        !          2424: 
        !          2425:        /* If first displayed line is not reached yet, we can still change top border */
        !          2426:        if ( ( HblCounterVideo < nStartHBL-1 )
        !          2427:          || ( ( HblCounterVideo == nStartHBL-1 ) && ( LineCycles <= pVideoTiming->RemoveTopBorder_Pos ) ) )
        !          2428:        {
        !          2429:                if ( FreqHz == VIDEO_71HZ )             Top_Pos = pVideoTiming->V_Start_Line_Hi;
        !          2430:                else if ( FreqHz == VIDEO_60HZ )        Top_Pos = pVideoTiming->V_Start_Line_60;
        !          2431:                else                                    Top_Pos = pVideoTiming->V_Start_Line_50;
        !          2432: 
        !          2433:                /* Change position if new position is not reached yet */
        !          2434:                if ( ( Top_Pos != nStartHBL )
        !          2435:                  && ( ( HblCounterVideo < Top_Pos-1 )
        !          2436:                    || ( ( HblCounterVideo == Top_Pos-1 ) && ( LineCycles <= pVideoTiming->RemoveTopBorder_Pos ) ) ) )
        !          2437:                {
        !          2438:                        nStartHBL = Top_Pos;
        !          2439:                        /* If 50 Hz screen and first line is before 50 Hz position -> top border removed */
        !          2440:                        if ( ( nScreenRefreshRate == VIDEO_50HZ ) && ( nStartHBL < pVideoTiming->V_Start_Line_50 ) )
        !          2441:                                VerticalOverscan |= V_OVERSCAN_NO_TOP;
        !          2442:                        else
        !          2443:                                VerticalOverscan &= ~V_OVERSCAN_NO_TOP;
        !          2444: 
        !          2445:                        VerticalOverscan &= ~V_OVERSCAN_NO_DE;
        !          2446:                }
        !          2447:                else
        !          2448:                {
        !          2449:                        // TODO CHECK ON STF [NP] : if freq is changed after top_pos and before nStartHBL, then we get a screen
        !          2450:                        // where vertical DE is never activated ? (eg 60/50 Hz switch at end of line 62)
        !          2451:                        VerticalOverscan |= V_OVERSCAN_NO_DE;
        !          2452:                }
        !          2453:        }
        !          2454: 
        !          2455:        /* If last displayed line not reached yet, we can still change bottom border */
        !          2456:        if ( ( HblCounterVideo < nEndHBL-1 )
        !          2457:          || ( ( HblCounterVideo == nEndHBL-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
        !          2458:        {
        !          2459:                if ( FreqHz == VIDEO_71HZ )             Bottom_Pos = pVideoTiming->V_End_Line_Hi;
        !          2460:                else if ( FreqHz == VIDEO_60HZ )        Bottom_Pos = pVideoTiming->V_End_Line_60;
        !          2461:                else                                    Bottom_Pos = pVideoTiming->V_End_Line_50;
        !          2462: 
        !          2463:                if ( ( HblCounterVideo < pVideoTiming->V_End_Line_60-1 )                /* 234 */
        !          2464:                  || ( ( HblCounterVideo == pVideoTiming->V_End_Line_60-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
        !          2465:                {
        !          2466:                        if ( ( nScreenRefreshRate == VIDEO_60HZ ) && ( FreqHz != VIDEO_60HZ ) )
1.1.1.22  root     2467:                        {
1.1.1.24! root     2468:                                /* 60 Hz screen where freq != 60 Hz on last line -> bottom border removed */
        !          2469:                                nEndHBL = pVideoTiming->V_End_Line_NoBottom_60;
        !          2470:                                VerticalOverscan |= V_OVERSCAN_NO_BOTTOM_60;
1.1.1.22  root     2471:                        }
1.1.1.24! root     2472:                        else if ( ( nScreenRefreshRate == VIDEO_50HZ ) && ( FreqHz == VIDEO_60HZ ) )
1.1.1.23  root     2473:                        {
1.1.1.24! root     2474:                                /* 50 Hz screen ending at 60 Hz screen's position -> short bottom border (-29 lines) */
        !          2475:                                nEndHBL = pVideoTiming->V_End_Line_60;
        !          2476:                                VerticalOverscan |= V_OVERSCAN_BOTTOM_SHORT_50;
1.1.1.23  root     2477:                        }
1.1.1.24! root     2478:                        else
1.1.1.23  root     2479:                        {
1.1.1.24! root     2480:                                nEndHBL = Bottom_Pos;
        !          2481:                                VerticalOverscan &= ~( V_OVERSCAN_NO_BOTTOM_60 | V_OVERSCAN_BOTTOM_SHORT_50 );
1.1.1.23  root     2482:                        }
1.1.1.24! root     2483:                }
1.1.1.22  root     2484: 
1.1.1.24! root     2485:                else if ( ( HblCounterVideo < pVideoTiming->V_End_Line_50-1 )           /* 263 */
        !          2486:                  || ( ( HblCounterVideo == pVideoTiming->V_End_Line_50-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
        !          2487:                {
        !          2488:                        if ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_60 )
        !          2489:                        {
        !          2490:                                /* Bottom border already removed above, can't be changed now */
        !          2491:                        }
        !          2492:                        else if ( ( nScreenRefreshRate == VIDEO_50HZ ) && ( FreqHz != VIDEO_50HZ ) )
        !          2493:                        {
        !          2494:                                /* 50 Hz screen where freq != 50 Hz on last line -> bottom border removed */
        !          2495:                                nEndHBL = pVideoTiming->V_End_Line_NoBottom_50;
        !          2496:                                VerticalOverscan |= V_OVERSCAN_NO_BOTTOM_50;
        !          2497:                        }
1.1.1.22  root     2498:                        else
                   2499:                        {
1.1.1.24! root     2500:                                nEndHBL = Bottom_Pos;
        !          2501:                                VerticalOverscan &= ~V_OVERSCAN_NO_BOTTOM_50;
1.1.1.22  root     2502:                        }
1.1.1.11  root     2503:                }
                   2504: 
1.1.1.24! root     2505:                else if ( ( HblCounterVideo < pVideoTiming->V_End_Line_Hi-1 )           /* 434 */
        !          2506:                  || ( ( HblCounterVideo == pVideoTiming->V_End_Line_Hi-1 ) && ( LineCycles <= pVideoTiming->RemoveBottomBorder_Pos ) ) )
1.1.1.22  root     2507:                {
1.1.1.24! root     2508:                        if ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_50 )
        !          2509:                        {
        !          2510:                                /* Bottom border already removed above, can't be changed now */
        !          2511:                        }
        !          2512:                        else
        !          2513:                        {
        !          2514:                                nEndHBL = Bottom_Pos;
        !          2515:                        }
1.1.1.22  root     2516:                }
1.1.1.24! root     2517:        }
1.1.1.22  root     2518: 
1.1.1.15  root     2519: 
1.1.1.24! root     2520:        LOG_TRACE ( TRACE_VIDEO_BORDER_H , "video new V_DE %d<->%d H_DE %d<->%d shift=%d border=%x hbl_pos=%d cycles_line=%d video_hbl_w=%d\n" ,
        !          2521:                nStartHBL , nEndHBL , DE_start , DE_end ,
        !          2522:                ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayPixelShift , BorderMask , HBL_Pos , nCyclesPerLine_new , HblCounterVideo  );
        !          2523: 
        !          2524:        /* Save new values */
        !          2525:        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayStartCycle = DE_start;
        !          2526:        ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle = DE_end;
        !          2527:        ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask = BorderMask;
        !          2528: }
1.1.1.22  root     2529: 
1.1.1.15  root     2530: 
1.1.1.11  root     2531: 
1.1.1.15  root     2532: 
1.1.1.24! root     2533: /*-----------------------------------------------------------------------*/
        !          2534: /**
        !          2535:  * Write to VideoSync (0xff820a), Hz setting
        !          2536:  */
        !          2537: void Video_Sync_WriteByte ( void )
        !          2538: {
        !          2539:        int FrameCycles, HblCounterVideo, LineCycles;
        !          2540:        Uint8 Freq;
1.1.1.11  root     2541: 
                   2542: 
1.1.1.24! root     2543:        if ( bUseVDIRes )
        !          2544:                return;                                         /* no 50/60 Hz freq in VDI mode */
1.1.1.11  root     2545: 
1.1.1.24! root     2546:        /* We're only interested in bit 1 (50/60Hz) */
        !          2547:        Freq = IoMem[0xff820a] & 2;
1.1.1.11  root     2548: 
1.1.1.24! root     2549:        Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
        !          2550:        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.13  root     2551: 
1.1.1.24! root     2552:        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",
        !          2553:                       Freq, FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.15  root     2554: 
1.1.1.24! root     2555:        /* Ignore consecutive writes of the same value */
        !          2556:        if ( Freq == ShifterFrame.Freq )
        !          2557:                return;                                         /* do nothing */
1.1.1.22  root     2558: 
1.1.1.24! root     2559:        /* Ignore freq changes if we are in high res */
        !          2560:        /* 2009/04/26 : don't ignore for now (see ST Cnx in Punish Your Machine) */
        !          2561: // TODO in res part, update freq when swith to !high
        !          2562: //     if ( ShifterFrame.Res == 0x02 )
        !          2563: //             return;                                         /* do nothing */
1.1.1.22  root     2564: 
1.1.1.15  root     2565: 
1.1.1.24! root     2566:        Video_Update_Glue_State ( FrameCycles , HblCounterVideo , LineCycles , false );
        !          2567: 
        !          2568: 
        !          2569:        /* TEMP for 'Gen4 Demo' by Ziggy / OVR in WS2,WS3,WS4 : */
        !          2570:        /* top border is removed 4 cycles too late (due to double STOP instruction ?) and trigger a wrong "left+2" */
        !          2571:        if ( ( STMemory_ReadLong ( 0xc000 ) == 0x69676779 )                     /* "iggy" */
        !          2572:                && ( M68000_GetPC() == 0x635e )
        !          2573:                && ( STMemory_ReadLong ( M68000_GetPC() ) == 0x11fc0002 )       /* move.b #2 */
        !          2574:                && ( ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask & BORDERMASK_LEFT_PLUS_2 )
        !          2575:           )
        !          2576:        {
        !          2577:                /* cancel a wrong left+2 */
        !          2578:                LOG_TRACE(TRACE_VIDEO_BORDER_H , "cancel wrong left+2 gen4/ziggy\n" );
        !          2579:                ShifterFrame.ShifterLines[ HblCounterVideo ].BorderMask &= ~BORDERMASK_LEFT_PLUS_2;
        !          2580:                ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Low_50;
        !          2581:                ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Low_50;
        !          2582:                nCyclesPerLine = 512;
1.1.1.15  root     2583:        }
1.1.1.24! root     2584:        /* TEMP for 'Gen4 Demo' by Ziggy / OVR in WS2,WS3,WS4 : */
1.1.1.15  root     2585: 
                   2586: 
1.1.1.24! root     2587:        /* Store cycle position of freq 50/60 to check for top/bottom border removal in Video_EndHBL. */
1.1.1.15  root     2588:        ShifterFrame.Freq = Freq;
1.1.1.24! root     2589:        if ( Freq == 0x02 )                                                     /* 50 Hz */
1.1.1.15  root     2590:        {
                   2591:                ShifterFrame.FreqPos50.VBL = nVBLs;
                   2592:                ShifterFrame.FreqPos50.FrameCycles = FrameCycles;
                   2593:                ShifterFrame.FreqPos50.HBL = HblCounterVideo;
                   2594:                ShifterFrame.FreqPos50.LineCycles = LineCycles;
                   2595:        }
1.1.1.24! root     2596:        else                                                                    /* 60 Hz */
1.1.1.15  root     2597:        {
                   2598:                ShifterFrame.FreqPos60.VBL = nVBLs;
                   2599:                ShifterFrame.FreqPos60.FrameCycles = FrameCycles;
                   2600:                ShifterFrame.FreqPos60.HBL = HblCounterVideo;
                   2601:                ShifterFrame.FreqPos60.LineCycles = LineCycles;
                   2602:        }
                   2603: }
                   2604: 
                   2605: 
                   2606: /*-----------------------------------------------------------------------*/
                   2607: /**
1.1.1.24! root     2608:  * Compute the default cycle position where the HBL should happen on each line.
1.1.1.15  root     2609:  * In low/med res, the position depends on the video frequency (50/60 Hz)
                   2610:  * In high res, the position is always the same.
1.1.1.24! root     2611:  * This position can change later when switching between hi res / 50 Hz / 60 Hz
        !          2612:  *
        !          2613:  * The position is measured for a CPU running at 8 MHz, it should be converted
        !          2614:  * to the correct number of cycles when using higher CPU speed (16 or 32 MHz)
1.1.1.15  root     2615:  */
1.1.1.24! root     2616: static int Video_HBL_GetDefaultPos ( void )
1.1.1.15  root     2617: {
                   2618:        int Pos;
                   2619: 
                   2620:        if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 )         /* hi res */
1.1.1.24! root     2621:                Pos = pVideoTiming->Hbl_Int_Pos_Hi;
1.1.1.15  root     2622: 
                   2623:        else                                                    /* low res or med res */
                   2624:        {
                   2625:                if ( IoMem_ReadByte ( 0xff820a ) & 2 )          /* 50 Hz, pos 512 */
1.1.1.24! root     2626:                        Pos = pVideoTiming->Hbl_Int_Pos_Low_50;
1.1.1.15  root     2627:                else                                            /* 60 Hz, pos 508 */
1.1.1.24! root     2628:                        Pos = pVideoTiming->Hbl_Int_Pos_Low_60;
1.1.1.15  root     2629:        }
                   2630: 
                   2631:        return Pos;
                   2632: }
                   2633: 
                   2634: 
1.1.1.24! root     2635: /**
        !          2636:  * Return the current HBL position (depending on the latest switch
        !          2637:  * to hi res / 50 Hz / 60 Hz)
        !          2638:  */
        !          2639: static int Video_HBL_GetCurrentPos ( void )
        !          2640: {
        !          2641:        return ShifterFrame.HBL_CyclePos;
        !          2642: }
        !          2643: 
        !          2644: 
1.1.1.15  root     2645: /*-----------------------------------------------------------------------*/
                   2646: /**
                   2647:  * Compute the cycle position where the timer B should happen on each
                   2648:  * visible line.
                   2649:  * We compute Timer B position for the given LineNumber, using start/end
                   2650:  * display cycles from ShifterLines[ LineNumber ].
                   2651:  * The position depends on the start of line / end of line positions
                   2652:  * (which depend on the current frequency / border tricks) and
                   2653:  * on the value of the bit 3 in the MFP's AER.
                   2654:  * If bit is 0, timer B will count end of line events (usual case),
                   2655:  * but if bit is 1, timer B will count start of line events (eg Seven Gates Of Jambala)
1.1.1.24! root     2656:  *
        !          2657:  * The position is measured for a CPU running at 8 MHz, it should be converted
        !          2658:  * to the correct number of cycles when using higher CPU speed (16 or 32 MHz)
1.1.1.15  root     2659:  */
1.1.1.24! root     2660: 
        !          2661: static int Video_TimerB_GetPosFromDE ( int DE_start , int DE_end )
1.1.1.15  root     2662: {
                   2663:        int Pos;
                   2664: 
                   2665:        if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 )                    /* we're counting end of line events */
1.1.1.24! root     2666:                Pos = DE_end + TIMERB_VIDEO_CYCLE_OFFSET;
1.1.1.15  root     2667:        else                                                            /* we're counting start of line events */
1.1.1.24! root     2668:                Pos = DE_start + TIMERB_VIDEO_CYCLE_OFFSET;
        !          2669: 
        !          2670:        return Pos;
        !          2671: }
        !          2672: 
        !          2673: 
        !          2674: int Video_TimerB_GetPos ( int LineNumber )
        !          2675: {
        !          2676:        int Pos;
        !          2677: 
        !          2678:        Pos = Video_TimerB_GetPosFromDE ( ShifterFrame.ShifterLines[ LineNumber ].DisplayStartCycle ,
        !          2679:                                          ShifterFrame.ShifterLines[ LineNumber ].DisplayEndCycle );
1.1.1.15  root     2680: 
1.1.1.18  root     2681: //fprintf ( stderr , "timerb pos=%d\n" , Pos );
                   2682:        return Pos;
                   2683: }
                   2684: 
                   2685: 
                   2686: /*-----------------------------------------------------------------------*/
                   2687: /**
                   2688:  * Compute the default cycle position where the timer B should happen
                   2689:  * on the next line, when restarting the INTERRUPT_VIDEO_ENDLINE handler.
                   2690:  * In low/med res, the position depends on the video frequency (50/60 Hz)
                   2691:  * In high res, the position is always the same.
                   2692:  */
1.1.1.24! root     2693: 
        !          2694: // TODO : use Video_TimerB_GetPosFromDE and Video_AddInterruptTimerB
1.1.1.23  root     2695: static int Video_TimerB_GetDefaultPos ( void )
1.1.1.18  root     2696: {
                   2697:        int Pos;
                   2698: 
                   2699:        if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 )                    /* we're counting end of line events */
                   2700:        {
                   2701:                if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 )         /* hi res */
                   2702:                        Pos = LINE_END_CYCLE_71;
                   2703: 
                   2704:                else                                                    /* low res or med res */
                   2705:                {
                   2706:                        if ( IoMem_ReadByte ( 0xff820a ) & 2 )          /* 50 Hz, pos 376 */
                   2707:                                Pos = LINE_END_CYCLE_50;
                   2708:                        else                                            /* 60 Hz, pos 372 */
                   2709:                                Pos = LINE_END_CYCLE_60;
                   2710:                }
                   2711:        }
                   2712:        else                                                            /* we're counting start of line events */
                   2713:        {
                   2714:                if ( ( IoMem_ReadByte ( 0xff8260 ) & 3 ) == 2 )         /* hi res */
                   2715:                        Pos = LINE_START_CYCLE_71;
                   2716: 
                   2717:                else                                                    /* low res or med res */
                   2718:                {
                   2719:                        if ( IoMem_ReadByte ( 0xff820a ) & 2 )          /* 50 Hz, pos 56 */
                   2720:                                Pos = LINE_START_CYCLE_50;
                   2721:                        else                                            /* 60 Hz, pos 52 */
                   2722:                                Pos = LINE_START_CYCLE_60;
                   2723:                }
                   2724:        }
                   2725: 
                   2726:        Pos += TIMERB_VIDEO_CYCLE_OFFSET;
                   2727: 
                   2728: //fprintf ( stderr , "timerb default pos=%d\n" , Pos );
1.1.1.15  root     2729:        return Pos;
                   2730: }
                   2731: 
                   2732: 
                   2733: /*-----------------------------------------------------------------------*/
                   2734: /**
                   2735:  * HBL interrupt : this occurs at the end of every line, on cycle 512 (in 50 Hz)
                   2736:  * It takes 56 cycles to handle the 68000's exception.
                   2737:  */
                   2738: void Video_InterruptHandler_HBL ( void )
                   2739: {
1.1.1.24! root     2740:        int FrameCycles , HblCounterVideo , LineCycles;
1.1.1.15  root     2741:        int PendingCyclesOver;
                   2742:        int NewHBLPos;
                   2743: 
1.1.1.24! root     2744:        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
        !          2745: 
1.1.1.15  root     2746:        /* How many cycle was this HBL delayed (>= 0) */
                   2747:        PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
1.1.1.24! root     2748: #ifndef OLD_CPU_SHIFT
        !          2749:        PendingCyclesOver <<= nCpuFreqShift;
        !          2750: #endif
1.1.1.15  root     2751: 
                   2752:        /* Remove this interrupt from list and re-order */
1.1.1.16  root     2753:        CycInt_AcknowledgeInterrupt();
1.1.1.15  root     2754: 
1.1.1.24! root     2755: 
        !          2756:        /* Handle the intermediate HBL interrupt used to restart video counter on HBL 310 or 260 */
        !          2757:        if ( RestartVideoCounter )
        !          2758:        {
        !          2759: //             fprintf ( stderr , "restart video counter check nhbl=%d hbl=%d cyc=%d\n" , nHBL , HblCounterVideo , LineCycles);
        !          2760:                if ( ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 2 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_50 ) )
        !          2761:                  || ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 0 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_60 )
        !          2762:                        && ( nScanlinesPerFrame == SCANLINES_PER_FRAME_60HZ ) ) )
        !          2763:                {
        !          2764:                        Video_RestartVideoCounter();
        !          2765:                        LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d cyc=%d restart video counter 0x%x\n", nHBL, LineCycles, VideoBase );
        !          2766:                }
        !          2767: 
        !          2768:                /* Restore the normal HBL interrupt on the same line */
        !          2769:                Video_AddInterruptHBL ( nHBL , Video_HBL_GetCurrentPos() );
        !          2770: 
        !          2771:                RestartVideoCounter = false;
        !          2772:                return;
        !          2773:        }
        !          2774: 
        !          2775: 
1.1.1.17  root     2776:        /* Videl Vertical counter increment (To be removed when Videl emulation is finished) */
                   2777:        /* VFC is incremented every half line, here, we increment it every line (should be completed) */
1.1.1.24! root     2778:        if (Config_IsMachineFalcon())
        !          2779:        {
1.1.1.17  root     2780:                vfc_counter += 1;
                   2781:        }
                   2782: 
                   2783:        
1.1.1.15  root     2784:        /* Increment the hbl jitter index */
                   2785:        HblJitterIndex++;
1.1.1.20  root     2786:        HblJitterIndex %= HBL_JITTER_ARRAY_SIZE;
1.1.1.15  root     2787:        
                   2788:        LOG_TRACE ( TRACE_VIDEO_HBL , "HBL %d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
                   2789:                       nHBL , FrameCycles , PendingCyclesOver , HblJitterArray[ HblJitterIndex ] );
                   2790: 
1.1.1.21  root     2791:        /* Print traces if pending HBL bit changed just before IACK when HBL interrupt is allowed */
                   2792:        if ( ( CPU_IACK == true ) && ( regs.intmask < 2 ) )
1.1.1.15  root     2793:        {
1.1.1.21  root     2794:                if ( pendingInterrupts & ( 1 << 2 ) )
                   2795:                {
                   2796:                        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" ,
                   2797:                                nHBL , nVBLs , FrameCycles , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
                   2798:                }
                   2799:                else
                   2800:                {
                   2801:                        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" ,
                   2802:                                nHBL , nVBLs , FrameCycles , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
                   2803:                }
1.1.1.15  root     2804:        }
                   2805: 
1.1.1.21  root     2806:        /* Set pending bit for HBL interrupt in the CPU IPL */
1.1.1.23  root     2807:        M68000_Exception(EXCEPTION_NR_HBLANK , M68000_EXC_SRC_AUTOVEC); /* Horizontal blank interrupt, level 2 */
1.1.1.21  root     2808: 
1.1.1.15  root     2809: 
                   2810:        Video_EndHBL();                                 /* Check some borders removal and copy line to display buffer */
                   2811: 
1.1.1.17  root     2812:        DmaSnd_STE_HBL_Update();                        /* Update STE DMA sound if needed */
                   2813: 
1.1.1.22  root     2814:        /* TEMP IPF */
                   2815:        IPF_Emulate();
                   2816:        /* TEMP IPF */
                   2817: 
1.1.1.15  root     2818:        nHBL++;                                         /* Increase HBL count */
                   2819: 
                   2820:        if (nHBL < nScanlinesPerFrame)
                   2821:        {
1.1.1.24! root     2822:                /* Update start cycle for next HBL : start for previous HBL + number of cycles for previous line */
        !          2823: //fprintf ( stderr , "hbl %llu\n" , CyclesGlobalClockCounter  );
        !          2824: //if ( FrameCycles - PendingCyclesOver != ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle + nCyclesPerLine )
        !          2825: //  fprintf ( stderr , "mismatch StartCycle hbl %d : %d != %d\n" , nHBL , FrameCycles - PendingCyclesOver , ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle + nCyclesPerLine );
        !          2826:                ShifterFrame.ShifterLines[ nHBL ].StartCycle = ShifterFrame.ShifterLines[ nHBL-1 ].StartCycle + nCyclesPerLine;
1.1.1.15  root     2827:                LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d start=%d %x\n", nHBL,
                   2828:                          ShifterFrame.ShifterLines[nHBL].StartCycle, ShifterFrame.ShifterLines[nHBL].StartCycle);
                   2829: 
                   2830:                /* Setup next HBL */
                   2831:                Video_StartHBL();
1.1.1.24! root     2832: 
        !          2833:                /* Default cycle position for next HBL */
        !          2834:                NewHBLPos = Video_HBL_GetDefaultPos();
        !          2835:                ShifterFrame.HBL_CyclePos = NewHBLPos;
        !          2836: //fprintf ( stderr , "NewHBLPos %d\n", NewHBLPos );
        !          2837:                Video_AddInterruptHBL ( nHBL , NewHBLPos );
        !          2838: 
        !          2839:                /* Add new VBL interrupt just after the last HBL (for example : VblVideoCycleOffset cycles after end of HBL 312 at 50 Hz) */
        !          2840:                /* We setup VBL one HBL earlier (eg at nHBL=312 instead of 313) to be sure we don't miss it */
        !          2841:                /* in case last HBL would be delayed too much (by more than VblVideoCycleOffset cycles) */
        !          2842:                if (nHBL == nScanlinesPerFrame-1)
        !          2843:                {
        !          2844: #ifdef OLD_CPU_SHIFT
        !          2845:                        int CyclesToVbl = ShifterFrame.ShifterLines[nHBL].StartCycle + nCyclesPerLine +
        !          2846:                                pVideoTiming->VblVideoCycleOffset - FrameCycles;
        !          2847: #else
        !          2848:                        int CyclesToVbl = ShifterFrame.ShifterLines[nHBL].StartCycle + nCyclesPerLine +
        !          2849:                               ( pVideoTiming->VblVideoCycleOffset << nCpuFreqShift ) - FrameCycles;
        !          2850: #endif
        !          2851:                        CycInt_AddRelativeInterrupt( CyclesToVbl, INT_CPU_CYCLE, INTERRUPT_VIDEO_VBL);
        !          2852:                }
        !          2853:        }
        !          2854: 
        !          2855:        /* Check if video counter should be restarted on this HBL */
        !          2856:        if ( RestartVideoCounter )
        !          2857:        {
        !          2858:                //fprintf ( stderr , "restart video counter nhbl=%d hbl=%d cyc=%d restart_cyc=%d\n" , nHBL , HblCounterVideo , LineCycles, pVideoTiming->RestartVideoCounter_Pos);
        !          2859:                /* If HBL was delayed after RestartVideoCounter_Pos, we can restart immediately if we have */
        !          2860:                /* the correct freq/hbl combination (we need to check nHBL == HblCounterVideo for the WS1 case where LineCycles can be 508) */
        !          2861:                if ( ( nHBL == HblCounterVideo ) && ( LineCycles >= pVideoTiming->RestartVideoCounter_Pos ) )
        !          2862:                {
        !          2863:                        if ( ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 2 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_50 ) )
        !          2864:                          || ( ( ( IoMem_ReadByte ( 0xff820a ) & 2 ) == 0 ) && ( nHBL == pVideoTiming->RestartVideoCounter_Line_60 ) ) )
        !          2865:                        {
        !          2866:                                Video_RestartVideoCounter();
        !          2867:                                LOG_TRACE(TRACE_VIDEO_HBL, "HBL %d cyc=%d restart video counter 0x%x (immediate)\n", nHBL, LineCycles, VideoBase );
        !          2868:                        }
        !          2869:                        RestartVideoCounter = false;
        !          2870:                }
        !          2871:                
        !          2872:                /* HBL was not delayed after RestartVideoCounter_Pos, so we set an intermediate HBL interrupt */
        !          2873:                /* This intermediate HBL interrupt will then set the real HBL interrupt at the end of the line */
        !          2874:                else
        !          2875:                {
        !          2876:                        Video_AddInterruptHBL ( nHBL , pVideoTiming->RestartVideoCounter_Pos );
        !          2877:                }
1.1.1.15  root     2878:        }
                   2879: }
                   2880: 
                   2881: 
                   2882: /*-----------------------------------------------------------------------*/
                   2883: /**
                   2884:  * Check at end of each HBL to see if any Shifter hardware tricks have been attempted
                   2885:  * and copy the line to the screen buffer.
                   2886:  * This is the place to check if top/bottom border were removed, as well as if some
                   2887:  * left/right border changes were not validated before.
                   2888:  * NOTE : the tests must be made with nHBL in ascending order.
                   2889:  */
                   2890: static void Video_EndHBL(void)
                   2891: {
                   2892:        //
                   2893:        // Handle top/bottom borders removal when switching freq
1.1.1.24! root     2894:        // Detection is made in Video_Update_Glue_State() so we just print some logs
1.1.1.15  root     2895:        //
                   2896: 
1.1.1.24! root     2897:        if ( ( nHBL == nStartHBL + BlankLines - 1 )
        !          2898:          && ( VerticalOverscan & V_OVERSCAN_NO_TOP ) )
1.1.1.15  root     2899:        {
1.1.1.24! root     2900:                /* 50 Hz screen where first line is before 50 Hz position -> top border removed */
1.1.1.15  root     2901:                LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove top\n" );
                   2902:                pHBLPaletteMasks -= OVERSCAN_TOP;       // FIXME useless ?
1.1.1.24! root     2903:                pHBLPalettes -= OVERSCAN_TOP;           // FIXME useless ?
1.1.1.15  root     2904:        }
                   2905: 
1.1.1.24! root     2906:        else if ( ( nHBL == pVideoTiming->V_End_Line_50 + BlankLines - 1 )
        !          2907:          && ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_50 ) )
1.1.1.13  root     2908:        {
1.1.1.24! root     2909:                /* 50 Hz screen where freq != 50 Hz on last line -> bottom border removed */
1.1.1.15  root     2910:                LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove bottom\n" );
                   2911:        }
                   2912: 
1.1.1.24! root     2913:        else if ( ( nHBL == pVideoTiming->V_End_Line_60 + BlankLines - 1 )
        !          2914:          && ( VerticalOverscan & V_OVERSCAN_NO_BOTTOM_60 ) )
        !          2915:        {
        !          2916:                /* 60 Hz screen where freq != 60 Hz on last line -> bottom border removed */
        !          2917:                LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect remove bottom 60Hz\n" );
1.1.1.15  root     2918:        }
                   2919: 
1.1.1.24! root     2920:        else if ( ( nHBL == pVideoTiming->V_End_Line_60 + BlankLines - 1 )
        !          2921:          && ( VerticalOverscan & V_OVERSCAN_BOTTOM_SHORT_50 ) )
        !          2922:        {
        !          2923:                /* 50 Hz screen ending at 60 Hz screen's position -> short bottom border (-29 lines) */
        !          2924:                LOG_TRACE ( TRACE_VIDEO_BORDER_V , "detect short bottom border\n" );
1.1.1.15  root     2925:        }
                   2926: 
                   2927: 
                   2928:        /* Store palette for very first line on screen - HBLPalettes[0] */
                   2929:        if (nHBL == nFirstVisibleHbl-1)
                   2930:        {
                   2931:                /* Store ALL palette for this line into raster table for datum */
                   2932:                Video_StoreFirstLinePalette();
                   2933:        }
                   2934: 
                   2935:        if (bUseHighRes)
                   2936:        {
                   2937:                /* Copy for hi-res (no overscan) */
                   2938:                if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
                   2939:                        Video_CopyScreenLineMono();
                   2940:        }
                   2941:        /* Are we in possible visible color display (including borders)? */
                   2942:        else if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
                   2943:        {
1.1.1.24! root     2944:                /* Update resolution at the end of the line to check for mix low/med screens */
        !          2945:                Video_StoreResolution(nHBL-nFirstVisibleHbl , false);
1.1.1.15  root     2946: 
                   2947:                /* Copy line of screen to buffer to simulate TV raster trace
                   2948:                 * - required for mouse cursor display/game updates
                   2949:                 * Eg, Lemmings and The Killing Game Show are good examples */
                   2950:                Video_CopyScreenLineColor();
                   2951:        }
                   2952: }
                   2953: 
                   2954: 
                   2955: /*-----------------------------------------------------------------------*/
                   2956: /**
                   2957:  * Set default values for the next HBL, depending on the current res/freq.
                   2958:  * We set the number of cycles per line, as well as some default values
                   2959:  * for display start/end cycle.
                   2960:  */
                   2961: static void Video_StartHBL(void)
                   2962: {
1.1.1.24! root     2963:        RestartVideoCounter = false;
        !          2964: 
1.1.1.19  root     2965:        if ((IoMem_ReadByte(0xff8260) & 3) == 2)  /* hi res */
1.1.1.15  root     2966:        {
                   2967:                nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1.1.1.24! root     2968:                if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 )                /* start not set yet */
        !          2969:                        ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Hi;
        !          2970:                ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Hi;
        !          2971: 
        !          2972:                /* If the whole screen is not in 71 Hz, then this HBL will default to "left off" */
        !          2973:                if ( nScreenRefreshRate != VIDEO_71HZ )
        !          2974:                {
        !          2975:                        ShifterFrame.ShifterLines[ nHBL ].BorderMask |= BORDERMASK_LEFT_OFF;
        !          2976:                        ShifterFrame.ShifterLines[ nHBL ].DisplayPixelShift = -4;
        !          2977:                        LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect remove left %d<->%d\n" ,
        !          2978:                                ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle  , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
        !          2979:                }
1.1.1.15  root     2980:        }
                   2981:        else
                   2982:        {
                   2983:                if ( IoMem_ReadByte ( 0xff820a ) & 2 )          /* 50 Hz */
                   2984:                {
                   2985:                        nCyclesPerLine = CYCLES_PER_LINE_50HZ;
                   2986:                        if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 )        /* start not set yet */
1.1.1.24! root     2987:                                ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Low_50;
        !          2988:                        ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Low_50;
1.1.1.15  root     2989:                }
                   2990:                else                                            /* 60 Hz */
                   2991:                {
                   2992:                        nCyclesPerLine = CYCLES_PER_LINE_60HZ;
                   2993:                        if ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle == -1 )        /* start not set yet */
1.1.1.24! root     2994:                                ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle = pVideoTiming->H_Start_Low_60;
        !          2995:                        ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle = pVideoTiming->H_Stop_Low_60;
        !          2996: 
        !          2997:                        /* If the whole screen is in 50 Hz, then this HBL will default to "left+2" + "right-2" (ie 60 Hz line) */
        !          2998:                        if ( nScreenRefreshRate == VIDEO_50HZ )
        !          2999:                        {
        !          3000:                                ShifterFrame.ShifterLines[ nHBL ].BorderMask |= ( BORDERMASK_LEFT_PLUS_2 | BORDERMASK_RIGHT_MINUS_2 ) ;
        !          3001:                                LOG_TRACE ( TRACE_VIDEO_BORDER_H , "detect left+2 / right-2 60Hz %d<->%d\n" ,
        !          3002:                                        ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle  , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
        !          3003:                        }
1.1.1.15  root     3004:                }
1.1.1.24! root     3005: 
        !          3006:                /* Handle accurate restart of video counter only in low/med res */
        !          3007:                if ( ( nHBL == pVideoTiming->RestartVideoCounter_Line_50 ) || ( nHBL == pVideoTiming->RestartVideoCounter_Line_60 ) )
        !          3008:                        RestartVideoCounter = true;
1.1.1.15  root     3009:        }
1.1.1.24! root     3010: 
        !          3011: #ifndef OLD_CPU_SHIFT
        !          3012:        nCyclesPerLine <<= nCpuFreqShift;
        !          3013: #endif
1.1.1.21  root     3014: //fprintf (stderr , "Video_StartHBL %d %d %d\n", nHBL , ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle , ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle );
1.1.1.24! root     3015: 
        !          3016:        if (nHBL >= nFirstVisibleHbl && nHBL < nLastVisibleHbl)
        !          3017:        {
        !          3018:                /* Store resolution at the beginning of the line */
        !          3019:                Video_StoreResolution(nHBL-nFirstVisibleHbl , true);
        !          3020:        }
1.1.1.15  root     3021: }
                   3022: 
                   3023: 
                   3024: /*-----------------------------------------------------------------------*/
                   3025: /**
                   3026:  * End Of Line interrupt
                   3027:  * This interrupt is started on cycle position 404 in 50 Hz and on cycle
                   3028:  * position 400 in 60 Hz. 50 Hz display ends at cycle 376 and 60 Hz displays
1.1.1.21  root     3029:  * ends at cycle 372. This means the EndLine interrupt happens 24 cycles
1.1.1.15  root     3030:  * after DisplayEndCycle.
                   3031:  * Note that if bit 3 of MFP AER is 1, then timer B will count start of line
1.1.1.21  root     3032:  * instead of end of line (at cycle 52+24 or 56+24)
1.1.1.15  root     3033:  */
                   3034: void Video_InterruptHandler_EndLine(void)
                   3035: {
                   3036:        int FrameCycles, HblCounterVideo, LineCycles;
                   3037:        int PendingCycles = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
                   3038: 
                   3039:        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                   3040: 
                   3041:        LOG_TRACE ( TRACE_VIDEO_HBL , "EndLine TB %d video_cyc=%d line_cyc=%d pending_int_cnt=%d\n" ,
                   3042:                       nHBL , FrameCycles , LineCycles , PendingCycles );
                   3043: 
                   3044:        /* Remove this interrupt from list and re-order */
1.1.1.16  root     3045:        CycInt_AcknowledgeInterrupt();
1.1.1.15  root     3046: 
                   3047:        /* Ignore HBLs in VDI mode */
                   3048:        if (bUseVDIRes)
                   3049:                return;
                   3050: 
                   3051:        /* Generate new Endline, if need to - there are 313 HBLs per frame */
                   3052:        if (nHBL < nScanlinesPerFrame-1)
                   3053:        {
1.1.1.21  root     3054:                /* By default, next EndLine's int will be on line nHBL+1 at pos 376+24 or 372+24 */
1.1.1.15  root     3055:                if ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 )            /* count end of line */
1.1.1.13  root     3056:                {
1.1.1.15  root     3057:                        /* If EndLine int is delayed too much (more than 100 cycles), nLineCycles will */
                   3058:                        /* be in the range 0..xxx instead of 400..512. In that case, we need to add */
                   3059:                        /* nCyclesPerLine to be in the range 512..x+512 */
                   3060:                        /* Maximum possible delay should be around 160 cycles on STF (DIVS) */
                   3061:                        /* In that case, HBL int will be delayed too, so we will have HblCounterVideo == nHBL+1 */
                   3062:                        if ( HblCounterVideo == nHBL+1 )                /* int happened in fact on the next line nHBL+1 */
                   3063:                                LineCycles += nCyclesPerLine;
1.1.1.13  root     3064: 
1.1.1.24! root     3065:                        LineTimerBPos = Video_TimerB_GetDefaultPos ();
1.1.1.13  root     3066:                }
                   3067: 
1.1.1.15  root     3068:                else                                                    /* count start of line, no possible delay to handle */
1.1.1.13  root     3069:                {
1.1.1.24! root     3070:                        LineTimerBPos = Video_TimerB_GetDefaultPos ();
1.1.1.13  root     3071:                }
                   3072: 
1.1.1.24! root     3073:                // TODO : use Video_AddInterruptTimerB
        !          3074: #ifdef OLD_CPU_SHIFT
        !          3075: //fprintf ( stderr , "new tb %d %d %d\n" , LineTimerBPos , nCyclesPerLine , LineTimerBPos - LineCycles + nCyclesPerLine );
        !          3076:                CycInt_AddRelativeInterrupt ( LineTimerBPos - LineCycles + nCyclesPerLine,
        !          3077:                                         INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
        !          3078: #else
        !          3079: //fprintf ( stderr , "new tb %d %d %d\n" , LineTimerBPos , nCyclesPerLine , (LineTimerBPos<<nCpuFreqShift) - LineCycles + nCyclesPerLine );
        !          3080:                LineTimerBPos <<= nCpuFreqShift;                        /* convert Pos at 8 MHz into number of cycles at 8/16/32 MHz */
        !          3081:                CycInt_AddRelativeInterrupt ( LineTimerBPos - LineCycles + nCyclesPerLine,
1.1.1.15  root     3082:                                         INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
1.1.1.24! root     3083: #endif
1.1.1.11  root     3084:        }
                   3085: 
1.1.1.15  root     3086:        /* Timer B occurs at END of first visible screen line in Event Count mode */
1.1.1.18  root     3087:        if ( ( nHBL >= nStartHBL ) && ( nHBL < nEndHBL + BlankLines ) )
1.1.1.11  root     3088:        {
1.1.1.15  root     3089:                /* Handle Timer B when using Event Count mode */
                   3090:                /* We must ensure that the write to fffa1b to activate timer B was */
                   3091:                /* completed before the point where the end of line signal was generated */
                   3092:                /* (in the case of a move.b #8,$fffa1b that would happen 4 cycles */
                   3093:                /* before end of line, the interrupt should not be generated) */
                   3094:                if ( (MFP_TBCR == 0x08)                                         /* Is timer in Event Count mode ? */
                   3095:                        && ( ( TimerBEventCountCycleStart == -1 )               /* timer B was started during a previous VBL */
                   3096:                          || ( TimerBEventCountCycleStart < FrameCycles-PendingCycles ) ) )     /* timer B was started before this possible interrupt */
1.1.1.21  root     3097:                        MFP_TimerB_EventCount_Interrupt ( PendingCycles );      /* we have a valid timer B interrupt */
1.1.1.11  root     3098:        }
1.1       root     3099: }
                   3100: 
1.1.1.2   root     3101: 
1.1.1.15  root     3102: 
                   3103: 
1.1.1.2   root     3104: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3105: /**
                   3106:  * Store whole palette on first line so have reference to work from
                   3107:  */
1.1.1.7   root     3108: static void Video_StoreFirstLinePalette(void)
1.1       root     3109: {
1.1.1.11  root     3110:        Uint16 *pp2;
                   3111:        int i;
                   3112: 
                   3113:        pp2 = (Uint16 *)&IoMem[0xff8240];
                   3114:        for (i = 0; i < 16; i++)
1.1.1.22  root     3115:        {
1.1.1.11  root     3116:                HBLPalettes[i] = SDL_SwapBE16(*pp2++);
1.1.1.24! root     3117:                if (Config_IsMachineST())
1.1.1.22  root     3118:                        HBLPalettes[i] &= 0x777;                        /* Force unused "random" bits to 0 */
                   3119:        }
1.1       root     3120: 
1.1.1.11  root     3121:        /* And set mask flag with palette and resolution */
                   3122: //     FIXME ; enlever PALETTEMASK_RESOLUTION
                   3123: 
1.1.1.15  root     3124: //     if ( ShifterFrame.ShifterLines[ nFirstVisibleHbl ].BorderMask == BORDERMASK_NONE )      // no border trick, store the current res
1.1.1.11  root     3125:        HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((Uint32)IoMem_ReadByte(0xff8260)&0x3)<<16);
                   3126: //     else                                            // border removal, assume low res for the whole line
                   3127: //             HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (0<<16);
1.1       root     3128: }
                   3129: 
1.1.1.2   root     3130: 
                   3131: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3132: /**
                   3133:  * Store resolution on each line (used to test if mixed low/medium resolutions)
1.1.1.24! root     3134:  * This functions is called twice per line :
        !          3135:  *  - during Video_StartHBL (start=true) to set the default resolution when
        !          3136:  *    starting a new line
        !          3137:  *  - during Video_EndHBL (start=false) to update the resolution before rendering
        !          3138:  *    the line on the screen (in case some border removals were used during this line)
1.1.1.11  root     3139:  */
1.1.1.24! root     3140: static void Video_StoreResolution(int y , bool start)
1.1       root     3141: {
1.1.1.11  root     3142:        Uint8 res;
                   3143:        int Mask;
1.1       root     3144: 
1.1.1.11  root     3145:        /* Clear resolution, and set with current value */
                   3146:        if (!(bUseHighRes || bUseVDIRes))
                   3147:        {
1.1.1.20  root     3148:                if ( y >= HBL_PALETTE_MASKS )                           /* we're above the limit (res was switched to mono for more than 1 VBL in color mode ?) */
                   3149:                {
                   3150: //                     fprintf ( stderr , "store res %d line %d hbl %d %x %x %d\n" , res , y , nHBL, Mask , HBLPaletteMasks[y] , sizeof(HBLPalettes) );
                   3151:                        y = HBL_PALETTE_MASKS - 1;                      /* store in the last palette line */
                   3152:                }
                   3153: 
1.1.1.24! root     3154:                if ( start )                                            /* Just store current resolution */
        !          3155:                        res = IoMem_ReadByte(0xff8260)&0x3;
        !          3156:                else                                                    /* Check if resolution needs to be forced to low/med */
        !          3157:                {
        !          3158:                        res = ( HBLPaletteMasks[y] >> 16 ) & 0x3;
        !          3159:                        Mask = ShifterFrame.ShifterLines[ y+nFirstVisibleHbl ].BorderMask;
        !          3160: 
        !          3161:                        if ( Mask & BORDERMASK_OVERSCAN_MED_RES )       /* special case for med res to render the overscan line */
        !          3162:                                res = 1;                                /* med res instead of low res */
        !          3163:                        else if ( Mask != BORDERMASK_NONE )             /* border removal : assume low res for the whole line */
        !          3164:                                res = 0;
        !          3165:                }
1.1.1.2   root     3166: 
1.1.1.24! root     3167:                HBLPaletteMasks[y] &= ~(0x3<<16);
1.1.1.11  root     3168:                HBLPaletteMasks[y] |= PALETTEMASK_RESOLUTION|((Uint32)res)<<16;
                   3169: 
                   3170: //   fprintf ( stderr , "store res %d line %d %x %x\n" , res , y , Mask , HBLPaletteMasks[y] );
                   3171:        }
1.1.1.9   root     3172: }
                   3173: 
                   3174: 
                   3175: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3176: /**
                   3177:  * Copy one line of monochrome screen into buffer for conversion later.
                   3178:  */
                   3179: static void Video_CopyScreenLineMono(void)
                   3180: {
                   3181:        /* Copy one line - 80 bytes in ST high resolution */
                   3182:        memcpy(pSTScreen, pVideoRaster, SCREENBYTES_MONOLINE);
                   3183:        pVideoRaster += SCREENBYTES_MONOLINE;
                   3184: 
                   3185:        /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
                   3186:        if (HWScrollCount)
                   3187:        {
                   3188:                Uint16 *pScrollAdj;
                   3189:                int nNegScrollCnt;
                   3190: 
                   3191:                pScrollAdj = (Uint16 *)pSTScreen;
                   3192:                nNegScrollCnt = 16 - HWScrollCount;
                   3193: 
                   3194:                /* Shift the whole line by the given scroll count */
                   3195:                while ((Uint8*)pScrollAdj < pSTScreen + SCREENBYTES_MONOLINE-2)
                   3196:                {
                   3197:                        do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
                   3198:                                        | (do_get_mem_word(pScrollAdj+1) >> nNegScrollCnt));
                   3199:                        ++pScrollAdj;
                   3200:                }
                   3201: 
                   3202:                /* Handle the last 16 pixels of the line */
                   3203:                do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
                   3204:                                | (do_get_mem_word(pVideoRaster) >> nNegScrollCnt));
                   3205: 
                   3206:                /* HW scrolling advances Shifter video counter by one */
                   3207:                pVideoRaster += 1 * 2;
                   3208:        }
                   3209: 
                   3210:        /* LineWidth is zero on ST. */
                   3211:        /* On STE, the Shifter skips the given amount of words. */
                   3212:        pVideoRaster += LineWidth*2;
                   3213: 
                   3214:        /* On STE, handle modifications of the video counter address $ff8205/07/09 */
                   3215:        /* that occurred while the display was already ON */
1.1.1.16  root     3216:        if ( VideoCounterDelayedOffset != 0 )
1.1.1.11  root     3217:        {
1.1.1.16  root     3218:                pVideoRaster += ( VideoCounterDelayedOffset & ~1 );
                   3219:                VideoCounterDelayedOffset = 0;
1.1.1.11  root     3220:        }
1.1.1.16  root     3221: 
                   3222:        if ( pVideoRasterDelayed != NULL )
1.1.1.11  root     3223:        {
1.1.1.16  root     3224:                pVideoRaster = pVideoRasterDelayed;
                   3225:                pVideoRasterDelayed = NULL;
1.1.1.11  root     3226:        }
                   3227: 
                   3228:        /* On STE, if we wrote to the hwscroll register, we set the */
                   3229:        /* new value here, once the current line was processed */
                   3230:        if ( NewHWScrollCount >= 0 )
                   3231:        {
                   3232:                HWScrollCount = NewHWScrollCount;
                   3233:                NewHWScrollCount = -1;
                   3234:        }
                   3235: 
                   3236:        /* On STE, if we wrote to the linewidth register, we set the */
                   3237:        /* new value here, once the current line was processed */
                   3238:        if ( NewLineWidth >= 0 )
                   3239:        {
                   3240:                LineWidth = NewLineWidth;
                   3241:                NewLineWidth = -1;
                   3242:        }
                   3243: 
                   3244:        /* Each screen line copied to buffer is always same length */
                   3245:        pSTScreen += SCREENBYTES_MONOLINE;
1.1.1.22  root     3246: 
                   3247:        /* We must keep the new video address in a 24 bit space */
                   3248:        /* (in case it pointed to IO space and is now >= 0x1000000) */
                   3249:        pVideoRaster = ( ( pVideoRaster - STRam ) & 0xffffff ) + STRam;
1.1.1.11  root     3250: }
                   3251: 
                   3252: 
                   3253: /*-----------------------------------------------------------------------*/
                   3254: /**
                   3255:  * Copy one line of color screen into buffer for conversion later.
                   3256:  * Possible lines may be top/bottom border, and/or left/right borders.
                   3257:  */
1.1.1.9   root     3258: static void Video_CopyScreenLineColor(void)
                   3259: {
1.1.1.15  root     3260:        int LineBorderMask;
1.1.1.11  root     3261:        int VideoOffset = 0;
                   3262:        int STF_PixelScroll = 0;
1.1.1.14  root     3263:        int LineRes;
1.1.1.17  root     3264:        Uint8 *pVideoRasterEndLine;                     /* addr of the last byte copied from pVideoRaster to pSTScreen (for HWScrollCount) */
                   3265:        int i;
1.1.1.24! root     3266:        Uint32 VideoMask;
1.1.1.14  root     3267: 
1.1.1.15  root     3268:        LineBorderMask = ShifterFrame.ShifterLines[ nHBL ].BorderMask;
1.1.1.16  root     3269:        STF_PixelScroll = ShifterFrame.ShifterLines[ nHBL ].DisplayPixelShift;
1.1.1.15  root     3270: 
                   3271:        /* Get resolution for this line (in case of mixed low/med screen) */
1.1.1.20  root     3272:        i = nHBL-nFirstVisibleHbl;
                   3273:        if ( i >= HBL_PALETTE_MASKS )
                   3274:                i = HBL_PALETTE_MASKS - 1;
                   3275:        LineRes = ( HBLPaletteMasks[i] >> 16 ) & 1;             /* 0=low res  1=med res */
1.1.1.11  root     3276: 
1.1.1.16  root     3277:        //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     3278: 
1.1.1.22  root     3279:        /* FIXME [NP] : when removing left border and displaying med res at 60 Hz on STE, we have a 3 pixel shift */
                   3280:        /* to correct to have bitmaps and color changes in sync. */
                   3281:        /* For now we only shift for med @ 60 Hz, but this should be measured for all */
                   3282:        /* freq and low / med res combinations on a real STE (fix "HighResMode" demo by Paradox). */
1.1.1.24! root     3283:        if ( Config_IsMachineSTE()
1.1.1.22  root     3284:          && ( LineBorderMask & BORDERMASK_LEFT_OFF_MED )
                   3285:          && ( nCyclesPerLine == 508 )
                   3286:          )
                   3287:        {
                   3288:                STF_PixelScroll = 3;                    
                   3289:        }
                   3290: 
1.1.1.11  root     3291:        /* If left border is opened, we need to compensate one missing word in low res (1 plan) */
1.1.1.15  root     3292:        /* If overscan is in med res, the offset is variable */
                   3293:        if ( LineBorderMask & BORDERMASK_OVERSCAN_MED_RES )
                   3294:                VideoOffset = - ( ( LineBorderMask >> 20 ) & 0x0f );            /* No Cooper=0  PYM=-2 in med res overscan */
1.1.1.11  root     3295: 
                   3296:        else if ( LineBorderMask & BORDERMASK_LEFT_OFF )
1.1.1.18  root     3297:        {
                   3298:                int     ShiftPixels = 0;
                   3299: 
                   3300:                if      ( STF_PixelScroll == 13 )       { VideoOffset = 2;      ShiftPixels = 8; }
                   3301:                else if ( STF_PixelScroll == 9 )        { VideoOffset = 0;      ShiftPixels = 8; }
                   3302:                else if ( STF_PixelScroll == 5 )        { VideoOffset = -2;     ShiftPixels = 8; }
                   3303:                else if ( STF_PixelScroll == 1 )        { VideoOffset = -4;     ShiftPixels = 8; }
                   3304: 
                   3305:                else                                    VideoOffset = -2;       /* Normal low res left border removal without 4 pixels scrolling */
                   3306: 
                   3307:                STF_PixelScroll -= ShiftPixels;
                   3308:        }
1.1.1.11  root     3309: 
1.1.1.16  root     3310:        else if ( LineBorderMask & BORDERMASK_LEFT_OFF_2_STE )
                   3311:                VideoOffset = -4;                                               /* 4 first bytes of the line are not shown */
                   3312: 
1.1.1.11  root     3313:        /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
1.1.1.24! root     3314:        /* as well as 'remove left + med stab' ('Closure' demo Troed/Sync) */
1.1.1.11  root     3315:        /* Depending on the number of pixels, we need to compensate for some skipped words */
1.1.1.15  root     3316:        else if ( LineBorderMask & BORDERMASK_LEFT_OFF_MED )
1.1.1.11  root     3317:        {
1.1.1.24! root     3318:                /* TEMP for 'Closure' in STE : planes are shifted and pixels are not aligned */
        !          3319:                if ( Config_IsMachineSTE() && ( STF_PixelScroll == 0 ) )
        !          3320:                {
        !          3321:                        VideoOffset = -6;
        !          3322:                        STF_PixelScroll -= 10;                                  /* FIXME : should be measured on real STE */
        !          3323:                }
        !          3324:                /* TEMP for 'Closure' in STE : planes are shifted and pixels are not aligned */
        !          3325: 
        !          3326:                else
        !          3327:                {
1.1.1.16  root     3328:                if      ( STF_PixelScroll == 13 )       VideoOffset = 2;
                   3329:                else if ( STF_PixelScroll == 9 )        VideoOffset = 0;
                   3330:                else if ( STF_PixelScroll == 5 )        VideoOffset = -2;
                   3331:                else if ( STF_PixelScroll == 1 )        VideoOffset = -4;
1.1.1.24! root     3332:                else if ( STF_PixelScroll == 0 )        VideoOffset = -4;       /* 'Closure' in STF : no 4 pixels scroll, but planes are shifted */
1.1.1.11  root     3333:                else                                    VideoOffset = 0;        /* never used ? */
                   3334: 
1.1.1.24! root     3335:                STF_PixelScroll -= 8;                                   /* removing left border in med res also shifts display to the left */
        !          3336:                }
        !          3337: 
1.1.1.11  root     3338:                // fprintf(stderr , "scr off %d %d\n" , STF_PixelScroll , VideoOffset);
                   3339:        }
                   3340: 
1.1.1.13  root     3341: 
1.1.1.11  root     3342:        /* Is total blank line? I.e. top/bottom border? */
1.1.1.18  root     3343:        if ((nHBL < nStartHBL) || (nHBL >= nEndHBL + BlankLines)
1.1.1.24! root     3344:            || (LineBorderMask & ( BORDERMASK_EMPTY_LINE|BORDERMASK_NO_DE ) ))
1.1.1.11  root     3345:        {
                   3346:                /* Clear line to color '0' */
                   3347:                memset(pSTScreen, 0, SCREENBYTES_LINE);
                   3348:        }
                   3349:        else
                   3350:        {
1.1.1.18  root     3351:                /* Does have left border ? */
1.1.1.16  root     3352:                if ( LineBorderMask & ( BORDERMASK_LEFT_OFF | BORDERMASK_LEFT_OFF_MED ) )       /* bigger line by 26 bytes on the left */
1.1.1.11  root     3353:                {
                   3354:                        pVideoRaster += BORDERBYTES_LEFT-SCREENBYTES_LEFT+VideoOffset;
                   3355:                        memcpy(pSTScreen, pVideoRaster, SCREENBYTES_LEFT);
                   3356:                        pVideoRaster += SCREENBYTES_LEFT;
                   3357:                }
1.1.1.16  root     3358:                else if ( LineBorderMask & BORDERMASK_LEFT_OFF_2_STE )  /* bigger line by 20 bytes on the left (STE specific) */
                   3359:                {                                                       /* bytes 0-3 are not shown, only next 16 bytes (32 pixels, 4 bitplanes) */
                   3360:                        if ( SCREENBYTES_LEFT > BORDERBYTES_LEFT_2_STE )
                   3361:                        {
                   3362:                                memset ( pSTScreen, 0, SCREENBYTES_LEFT-BORDERBYTES_LEFT_2_STE+4 );     /* clear unused pixels + bytes 0-3 */
                   3363:                                memcpy ( pSTScreen+SCREENBYTES_LEFT-BORDERBYTES_LEFT_2_STE+4, pVideoRaster+VideoOffset+4, BORDERBYTES_LEFT_2_STE-4 );
                   3364:                        }
                   3365:                        else
                   3366:                                memcpy ( pSTScreen, pVideoRaster+BORDERBYTES_LEFT_2_STE-SCREENBYTES_LEFT+VideoOffset, SCREENBYTES_LEFT );
                   3367: 
                   3368:                        pVideoRaster += BORDERBYTES_LEFT_2_STE+VideoOffset;
                   3369:                }
                   3370:                else if (LineBorderMask & BORDERMASK_LEFT_PLUS_2)       /* bigger line by 2 bytes on the left */
1.1.1.11  root     3371:                {
1.1.1.16  root     3372:                        if ( SCREENBYTES_LEFT > 2 )
                   3373:                        {
                   3374:                                memset(pSTScreen,0,SCREENBYTES_LEFT-2);         /* clear unused pixels */
                   3375:                                memcpy(pSTScreen+SCREENBYTES_LEFT-2, pVideoRaster, 2);
                   3376:                        }
                   3377:                        else
                   3378:                        {                                               /* nothing to copy, left border is not large enough */
                   3379:                        }
                   3380: 
1.1.1.11  root     3381:                        pVideoRaster += 2;
                   3382:                }
1.1.1.16  root     3383:                else if (bSteBorderFlag)                                /* bigger line by 8 bytes on the left (STE specific) */
1.1.1.12  root     3384:                {
1.1.1.16  root     3385:                        if ( SCREENBYTES_LEFT > 4*2 )
                   3386:                        {
                   3387:                                memset(pSTScreen,0,SCREENBYTES_LEFT-4*2);       /* clear unused pixels */
                   3388:                                memcpy(pSTScreen+SCREENBYTES_LEFT-4*2, pVideoRaster, 4*2);
                   3389:                        }
                   3390:                        else
                   3391:                        {                                               /* nothing to copy, left border is not large enough */
                   3392:                        }
                   3393: 
1.1.1.12  root     3394:                        pVideoRaster += 4*2;
                   3395:                }
1.1.1.11  root     3396:                else
1.1.1.16  root     3397:                        memset(pSTScreen,0,SCREENBYTES_LEFT);           /* left border not removed, clear to color '0' */
1.1.1.11  root     3398: 
                   3399:                /* Short line due to hires in the middle ? */
                   3400:                if (LineBorderMask & BORDERMASK_STOP_MIDDLE)
                   3401:                {
                   3402:                        /* 106 bytes less in the line */
                   3403:                        memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE-106);
1.1.1.12  root     3404:                        memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-106, 0, 106);      /* clear unused pixels */
1.1.1.11  root     3405:                        pVideoRaster += (SCREENBYTES_MIDDLE-106);
                   3406:                }
                   3407:                else
                   3408:                {
                   3409:                        /* normal middle part (160 bytes) */
                   3410:                        memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE);
                   3411:                        pVideoRaster += SCREENBYTES_MIDDLE;
                   3412:                }
                   3413: 
                   3414:                /* Does have right border ? */
                   3415:                if (LineBorderMask & BORDERMASK_RIGHT_OFF)
                   3416:                {
                   3417:                        memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE, pVideoRaster, SCREENBYTES_RIGHT);
1.1.1.17  root     3418:                        pVideoRasterEndLine = pVideoRaster + SCREENBYTES_RIGHT;
1.1.1.16  root     3419:                        pVideoRaster += BORDERBYTES_RIGHT;
1.1.1.11  root     3420:                }
                   3421:                else if (LineBorderMask & BORDERMASK_RIGHT_MINUS_2)
                   3422:                {
                   3423:                        /* Shortened line by 2 bytes */
                   3424:                        memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE-2, 0, SCREENBYTES_RIGHT+2);
                   3425:                        pVideoRaster -= 2;
1.1.1.17  root     3426:                        pVideoRasterEndLine = pVideoRaster;
1.1.1.11  root     3427:                }
                   3428:                else
                   3429:                {
                   3430:                        /* Simply clear right border to '0' */
                   3431:                        memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
1.1.1.17  root     3432:                        pVideoRasterEndLine = pVideoRaster;
1.1.1.11  root     3433:                }
                   3434: 
1.1.1.18  root     3435:                /* Shifter read bytes and borders can change, but display is blank, so finally clear the line with color 0 */
1.1.1.24! root     3436:                if (LineBorderMask & ( BORDERMASK_BLANK_LINE | BORDERMASK_BLANK ) )
1.1.1.18  root     3437:                        memset(pSTScreen, 0, SCREENBYTES_LINE);
                   3438: 
1.1.1.11  root     3439:                /* Full right border removal up to the end of the line (cycle 512) */
                   3440:                if (LineBorderMask & BORDERMASK_RIGHT_OFF_FULL)
                   3441:                        pVideoRaster += BORDERBYTES_RIGHT_FULL;
                   3442: 
                   3443:                /* Correct the offset for pVideoRaster from BORDERMASK_LEFT_OFF above if needed */
                   3444:                pVideoRaster -= VideoOffset;            /* VideoOffset is 0 or -2 */
                   3445: 
                   3446: 
                   3447:                /* STE specific */
1.1.1.13  root     3448:                if (!bSteBorderFlag && HWScrollCount)           /* Handle STE fine scrolling (HWScrollCount is zero on ST) */
1.1.1.11  root     3449:                {
1.1.1.13  root     3450:                        Uint16 *pScrollAdj;     /* Pointer to actual position in line */
1.1.1.11  root     3451:                        int nNegScrollCnt;
1.1.1.13  root     3452:                        Uint16 *pScrollEndAddr; /* Pointer to end of the line */
1.1.1.11  root     3453: 
                   3454:                        nNegScrollCnt = 16 - HWScrollCount;
                   3455:                        if (LineBorderMask & BORDERMASK_LEFT_OFF)
                   3456:                                pScrollAdj = (Uint16 *)pSTScreen;
1.1.1.16  root     3457:                        else if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
                   3458:                        {
                   3459:                                if ( SCREENBYTES_LEFT > BORDERBYTES_LEFT_2_STE )
1.1.1.17  root     3460:                                        pScrollAdj = (Uint16 *)(pSTScreen+8);   /* don't scroll the 8 first bytes (keep color 0)*/
1.1.1.16  root     3461:                                else
                   3462:                                        pScrollAdj = (Uint16 *)pSTScreen;       /* we render less bytes on screen than a real ST, scroll the whole line */
                   3463:                        }
1.1.1.11  root     3464:                        else
                   3465:                                pScrollAdj = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT);
1.1.1.16  root     3466: 
1.1.1.17  root     3467:                        /* When shifting the line to the left, we will have 'HWScrollCount' missing pixels at   */
                   3468:                        /* the end of the line. We must complete these last 16 pixels with pixels from the      */
                   3469:                        /* video counter last accessed value in pVideoRasterEndLine.                            */
                   3470:                        /* There're 2 passes :                                                                  */
                   3471:                        /*  - shift whole line except the last 16 pixels                                        */
                   3472:                        /*  - shift/complete the last 16 pixels                                                 */
                   3473: 
                   3474:                        /* Addr of the last byte to shift in the 1st pass (excluding the last 16 pixels of the line) */
1.1.1.11  root     3475:                        if (LineBorderMask & BORDERMASK_RIGHT_OFF)
                   3476:                                pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LINE - 8);
                   3477:                        else
                   3478:                                pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8);
                   3479: 
1.1.1.17  root     3480: 
                   3481:                        if ( LineRes == 1 )                             /* med res */
1.1.1.11  root     3482:                        {
1.1.1.15  root     3483:                                /* 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     3484:                                pScrollEndAddr += 2;                    /* 2 Uint16 = 4 bytes = 16 pixels */
1.1.1.14  root     3485: 
1.1.1.17  root     3486:                                /* Shift the whole line to the left by the given scroll count (except the last 16 pixels) */
1.1.1.14  root     3487:                                while (pScrollAdj < pScrollEndAddr)
                   3488:                                {
                   3489:                                        do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
                   3490:                                                        | (do_get_mem_word(pScrollAdj+2) >> nNegScrollCnt));
                   3491:                                        ++pScrollAdj;
                   3492:                                }
1.1.1.17  root     3493:                                /* Handle the last 16 pixels of the line (complete the line with pixels from pVideoRasterEndLine) */
                   3494:                                for ( i=0 ; i<2 ; i++ )
                   3495:                                        do_put_mem_word(pScrollAdj+i, (do_get_mem_word(pScrollAdj+i) << HWScrollCount)
                   3496:                                                | (do_get_mem_word(pVideoRasterEndLine+i*2) >> nNegScrollCnt));
1.1.1.14  root     3497: 
                   3498:                                /* Depending on whether $ff8264 or $ff8265 was used to scroll, */
                   3499:                                /* we prefetched 16 pixel (4 bytes) */
1.1.1.13  root     3500:                                if ( HWScrollPrefetch == 1 )            /* $ff8265 prefetches 16 pixels */
                   3501:                                        pVideoRaster += 2 * 2;          /* 2 bitplans */
1.1.1.14  root     3502: 
                   3503:                                /* If scrolling with $ff8264, there's no prefetch, which means display starts */
                   3504:                                /* 16 pixels later but still stops at the normal point (eg we display */
                   3505:                                /* (320-16) pixels in low res). We shift the whole line 4 bytes to the right to */
                   3506:                                /* get the correct result (using memmove, as src/dest are overlapping). */
                   3507:                                else
                   3508:                                {
                   3509:                                        if (LineBorderMask & BORDERMASK_RIGHT_OFF)
                   3510:                                                memmove ( pSTScreen+4 , pSTScreen , SCREENBYTES_LINE - 4 );
                   3511:                                        else
                   3512:                                                memmove ( pSTScreen+4 , pSTScreen , SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 4 );
                   3513: 
                   3514:                                        memset ( pSTScreen , 0 , 4 );   /* first 16 pixels are color '0' */
                   3515:                                }
1.1.1.11  root     3516:                        }
1.1.1.14  root     3517: 
1.1.1.17  root     3518:                        else                                            /* low res */
1.1.1.11  root     3519:                        {
1.1.1.17  root     3520:                                /* Shift the whole line to the left by the given scroll count (except the last 16 pixels) */
1.1.1.11  root     3521:                                while (pScrollAdj < pScrollEndAddr)
                   3522:                                {
                   3523:                                        do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
                   3524:                                                        | (do_get_mem_word(pScrollAdj+4) >> nNegScrollCnt));
                   3525:                                        ++pScrollAdj;
                   3526:                                }
1.1.1.17  root     3527:                                /* Handle the last 16 pixels of the line (complete the line with pixels from pVideoRasterEndLine) */
                   3528:                                for ( i=0 ; i<4 ; i++ )
                   3529:                                        do_put_mem_word(pScrollAdj+i, (do_get_mem_word(pScrollAdj+i) << HWScrollCount)
                   3530:                                                | (do_get_mem_word(pVideoRasterEndLine+i*2) >> nNegScrollCnt));
1.1.1.13  root     3531: 
                   3532:                                /* Depending on whether $ff8264 or $ff8265 was used to scroll, */
                   3533:                                /* we prefetched 16 pixel (8 bytes) */
                   3534:                                if ( HWScrollPrefetch == 1 )            /* $ff8265 prefetches 16 pixels */
                   3535:                                        pVideoRaster += 4 * 2;          /* 4 bitplans */
                   3536: 
                   3537:                                /* If scrolling with $ff8264, there's no prefetch, which means display starts */
                   3538:                                /* 16 pixels later but still stops at the normal point (eg we display */
                   3539:                                /* (320-16) pixels in low res). We shift the whole line 8 bytes to the right to */
                   3540:                                /* get the correct result (using memmove, as src/dest are overlapping). */
                   3541:                                else
                   3542:                                {
                   3543:                                        if (LineBorderMask & BORDERMASK_RIGHT_OFF)
                   3544:                                                memmove ( pSTScreen+8 , pSTScreen , SCREENBYTES_LINE - 8 );
                   3545:                                        else
                   3546:                                                memmove ( pSTScreen+8 , pSTScreen , SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8 );
                   3547: 
                   3548:                                        memset ( pSTScreen , 0 , 8 );   /* first 16 pixels are color '0' */
                   3549:                                }
1.1.1.11  root     3550: 
                   3551:                                /* On STE, when we have a 230 bytes overscan line and HWScrollCount > 0 */
1.1.1.17  root     3552:                                /* we must read 6 bytes less than expected if scrolling is using prefetching ($ff8265) */
                   3553:                                /* (this is not the case for the 224 bytes overscan which is a multiple of 8) */
1.1.1.11  root     3554:                                if ( (LineBorderMask & BORDERMASK_LEFT_OFF) && (LineBorderMask & BORDERMASK_RIGHT_OFF) )
1.1.1.17  root     3555:                                  {
                   3556:                                    if ( HWScrollPrefetch == 1 )
                   3557:                                        pVideoRaster -= 6;              /* we don't add 8 bytes (see above), but 2 */
                   3558:                                    else
                   3559:                                        pVideoRaster -= 0;
                   3560:                                  }
1.1.1.11  root     3561: 
                   3562:                        }
                   3563:                }
                   3564: 
                   3565:                /* LineWidth is zero on ST. */
                   3566:                /* On STE, the Shifter skips the given amount of words. */
                   3567:                pVideoRaster += LineWidth*2;
                   3568: 
                   3569:                /* On STE, handle modifications of the video counter address $ff8205/07/09 */
                   3570:                /* that occurred while the display was already ON */
1.1.1.16  root     3571:                if ( VideoCounterDelayedOffset != 0 )
1.1.1.11  root     3572:                {
1.1.1.24! root     3573: //               fprintf ( stderr , "adjust video counter offset=%d old video=%x\n" , VideoCounterDelayedOffset , pVideoRaster-STRam );
1.1.1.16  root     3574:                        pVideoRaster += ( VideoCounterDelayedOffset & ~1 );
                   3575: //               fprintf ( stderr , "adjust video counter offset=%d new video=%x\n" , VideoCounterDelayedOffset , pVideoRaster-STRam );
                   3576:                        VideoCounterDelayedOffset = 0;
1.1.1.11  root     3577:                }
1.1.1.16  root     3578: 
                   3579:                if ( pVideoRasterDelayed != NULL )
1.1.1.11  root     3580:                {
1.1.1.16  root     3581:                        pVideoRaster = pVideoRasterDelayed;
                   3582: //               fprintf ( stderr , "adjust video counter const new video=%x\n" , pVideoRaster-STRam );
                   3583:                        pVideoRasterDelayed = NULL;
1.1.1.11  root     3584:                }
                   3585: 
                   3586:                /* On STE, if we wrote to the hwscroll register, we set the */
                   3587:                /* new value here, once the current line was processed */
                   3588:                if ( NewHWScrollCount >= 0 )
                   3589:                {
                   3590:                        HWScrollCount = NewHWScrollCount;
1.1.1.13  root     3591:                        HWScrollPrefetch = NewHWScrollPrefetch;
1.1.1.11  root     3592:                        NewHWScrollCount = -1;
1.1.1.13  root     3593:                        NewHWScrollPrefetch = -1;
                   3594:                }
                   3595: 
                   3596:                /* On STE, if we trigger the left border + 16 pixels trick, we set the */
                   3597:                /* new value here, once the current line was processed */
                   3598:                if ( NewSteBorderFlag >= 0 )
                   3599:                {
                   3600:                        if ( NewSteBorderFlag == 0 )
1.1.1.15  root     3601:                                bSteBorderFlag = false;
1.1.1.13  root     3602:                        else
1.1.1.15  root     3603:                                bSteBorderFlag = true;
1.1.1.13  root     3604:                        NewSteBorderFlag = -1;
1.1.1.11  root     3605:                }
                   3606: 
                   3607:                /* On STE, if we wrote to the linewidth register, we set the */
                   3608:                /* new value here, once the current line was processed */
                   3609:                if ( NewLineWidth >= 0 )
                   3610:                {
                   3611:                        LineWidth = NewLineWidth;
                   3612:                        NewLineWidth = -1;
                   3613:                }
1.1.1.16  root     3614: 
                   3615: 
                   3616:                /* Handle 4 pixels hardware scrolling ('ST Cnx' demo in 'Punish Your Machine') */
1.1.1.21  root     3617:                /* as well as scrolling occurring when removing the left border. */
1.1.1.16  root     3618:                /* If >0, shift the line by STF_PixelScroll pixels to the right */
                   3619:                /* If <0, shift the line by -STF_PixelScroll pixels to the left */
                   3620:                /* This should be handled after the STE's hardware scrolling as it will scroll */
                   3621:                /* the whole displayed area (while the STE scrolls pixels inside the displayed area) */
                   3622:                if ( STF_PixelScroll > 0 )
                   3623:                {
                   3624:                        Uint16 *pScreenLineEnd;
                   3625:                        int count;
                   3626: 
                   3627:                        pScreenLineEnd = (Uint16 *) ( pSTScreen + SCREENBYTES_LINE - 2 );
1.1.1.17  root     3628:                        if ( LineRes == 0 )                     /* low res */
                   3629:                        {
                   3630:                                for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineEnd-- )
                   3631:                                        do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 4 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
                   3632:                                /* Handle the first 16 pixels of the line (add color 0 pixels to the extreme left) */
                   3633:                                do_put_mem_word ( pScreenLineEnd-0 , ( do_get_mem_word ( pScreenLineEnd-0 ) >> STF_PixelScroll ) );
                   3634:                                do_put_mem_word ( pScreenLineEnd-1 , ( do_get_mem_word ( pScreenLineEnd-1 ) >> STF_PixelScroll ) );
                   3635:                                do_put_mem_word ( pScreenLineEnd-2 , ( do_get_mem_word ( pScreenLineEnd-2 ) >> STF_PixelScroll ) );
                   3636:                                do_put_mem_word ( pScreenLineEnd-3 , ( do_get_mem_word ( pScreenLineEnd-3 ) >> STF_PixelScroll ) );
                   3637:                        }
                   3638:                        else                                    /* med res */
                   3639:                        {
                   3640:                                for ( count = 0 ; count < ( SCREENBYTES_LINE - 4 ) / 2 ; count++ , pScreenLineEnd-- )
                   3641:                                        do_put_mem_word ( pScreenLineEnd , ( ( do_get_mem_word ( pScreenLineEnd - 2 ) << 16 ) | ( do_get_mem_word ( pScreenLineEnd ) ) ) >> STF_PixelScroll );
                   3642:                                /* Handle the first 16 pixels of the line (add color 0 pixels to the extreme left) */
                   3643:                                do_put_mem_word ( pScreenLineEnd-0 , ( do_get_mem_word ( pScreenLineEnd-0 ) >> STF_PixelScroll ) );
                   3644:                                do_put_mem_word ( pScreenLineEnd-1 , ( do_get_mem_word ( pScreenLineEnd-1 ) >> STF_PixelScroll ) );
                   3645:                        }
1.1.1.16  root     3646:                }
                   3647:                else if ( STF_PixelScroll < 0 )
                   3648:                {
                   3649:                        Uint16 *pScreenLineStart;
                   3650:                        int count;
1.1.1.24! root     3651:                        int STE_HWScrollLeft;
        !          3652:                        Uint16 extra_word;
1.1.1.16  root     3653: 
                   3654:                        STF_PixelScroll = -STF_PixelScroll;
                   3655:                        pScreenLineStart = (Uint16 *)pSTScreen;
1.1.1.24! root     3656: 
        !          3657:                        STE_HWScrollLeft = 0;
        !          3658:                        if ( !bSteBorderFlag && HWScrollCount )
        !          3659:                                STE_HWScrollLeft = HWScrollCount;
        !          3660: 
1.1.1.17  root     3661:                        if ( LineRes == 0 )                     /* low res */
                   3662:                        {
                   3663:                                for ( count = 0 ; count < ( SCREENBYTES_LINE - 8 ) / 2 ; count++ , pScreenLineStart++ )
1.1.1.24! root     3664:                                        do_put_mem_word ( pScreenLineStart , ( ( do_get_mem_word ( pScreenLineStart ) << STF_PixelScroll )
        !          3665:                                                | ( do_get_mem_word ( pScreenLineStart + 4 ) >> (16-STF_PixelScroll) ) ) );
        !          3666: 
        !          3667:                                /*
        !          3668:                                 * Handle the last 16 pixels of the line after the shift to the left :
        !          3669:                                 * - if this is a 224 byte STE overscan line, then the last 8 pixels to the extreme right should be displayed
        !          3670:                                 * - for other cases (230 byte overscan), "entering" pixels to the extreme right should be set to color 0
        !          3671:                                 */
        !          3672:                                if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
        !          3673:                                {
        !          3674:                                        /* This is one can be complicated, because we can have STE scroll to the left + the global */
        !          3675:                                        /* 8 pixel left scroll addded when using a 224 bytes overscan line. We use extra_word to fetch */
        !          3676:                                        /* those missing pixels */
        !          3677:                                        for ( i=0 ; i<4 ; i++ )
        !          3678:                                        {
        !          3679:                                                if ( STE_HWScrollLeft == 0 )
        !          3680:                                                        extra_word = do_get_mem_word ( pVideoRasterEndLine + i*2 );
        !          3681:                                                else
        !          3682:                                                        extra_word = ( do_get_mem_word ( pVideoRasterEndLine + i*2 ) << STE_HWScrollLeft )
        !          3683:                                                                | ( do_get_mem_word ( pVideoRasterEndLine + 8 + i*2 ) >> (16-STE_HWScrollLeft) );
        !          3684:                                                                
        !          3685:                                                do_put_mem_word ( pScreenLineStart+i , ( ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll )
        !          3686:                                                        | ( extra_word >> (16-STF_PixelScroll) ) ) );
        !          3687:                                        }
        !          3688:                                }
        !          3689:                                else
        !          3690:                                {
        !          3691:                                        for ( i=0 ; i<4 ; i++ )
        !          3692:                                                do_put_mem_word ( pScreenLineStart+i , ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll ) );
        !          3693: 
        !          3694:                                }
1.1.1.17  root     3695:                        }
                   3696:                        else                                    /* med res */
                   3697:                        {
                   3698:                                for ( count = 0 ; count < ( SCREENBYTES_LINE - 4 ) / 2 ; count++ , pScreenLineStart++ )
1.1.1.24! root     3699:                                        do_put_mem_word ( pScreenLineStart , ( ( do_get_mem_word ( pScreenLineStart ) << STF_PixelScroll )
        !          3700:                                                | ( do_get_mem_word ( pScreenLineStart + 2 ) >> (16-STF_PixelScroll) ) ) );
        !          3701: 
        !          3702:                                /* Handle the last 16 pixels of the line */
        !          3703:                                if (LineBorderMask & BORDERMASK_LEFT_OFF_2_STE)
        !          3704:                                {
        !          3705:                                        for ( i=0 ; i<2 ; i++ )
        !          3706:                                        {
        !          3707:                                                if ( STE_HWScrollLeft == 0 )
        !          3708:                                                        extra_word = do_get_mem_word ( pVideoRasterEndLine + i*2 );
        !          3709:                                                else
        !          3710:                                                        extra_word = ( do_get_mem_word ( pVideoRasterEndLine + i*2 ) << STE_HWScrollLeft )
        !          3711:                                                                | ( do_get_mem_word ( pVideoRasterEndLine + 8 + i*2 ) >> (16-STE_HWScrollLeft) );
        !          3712:                                                                
        !          3713:                                                do_put_mem_word ( pScreenLineStart+i , ( ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll )
        !          3714:                                                        | ( extra_word >> (16-STF_PixelScroll) ) ) );
        !          3715:                                        }
        !          3716:                                }
        !          3717:                                else
        !          3718:                                {
        !          3719:                                        for ( i=0 ; i<2 ; i++ )
        !          3720:                                                do_put_mem_word ( pScreenLineStart+i , ( do_get_mem_word ( pScreenLineStart+i ) << STF_PixelScroll ) );
        !          3721:                                }
        !          3722: 
1.1.1.17  root     3723:                        }
1.1.1.16  root     3724:                }
1.1.1.11  root     3725:        }
                   3726: 
                   3727:        /* Each screen line copied to buffer is always same length */
                   3728:        pSTScreen += SCREENBYTES_LINE;
1.1.1.22  root     3729: 
1.1.1.24! root     3730:        /* We must keep the new video address in a 22 or 24 bit space depending on the machine type */
        !          3731:        /* (for example in case it pointed to IO space and is now >= 0x1000000) */
        !          3732:        VideoMask = ( DMA_MaskAddressHigh() << 16 ) | 0xffff;           /* 0x3fffff or 0xffffff */
        !          3733:        pVideoRaster = ( ( pVideoRaster - STRam ) & VideoMask ) + STRam;
        !          3734: //fprintf ( stderr , "video counter new=%x\n" , pVideoRaster-STRam );
1.1       root     3735: }
                   3736: 
1.1.1.2   root     3737: 
                   3738: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3739: /**
                   3740:  * Clear raster line table to store changes in palette/resolution on a line
                   3741:  * basic. Called once on VBL interrupt.
                   3742:  */
1.1       root     3743: void Video_SetScreenRasters(void)
                   3744: {
1.1.1.11  root     3745:        pHBLPaletteMasks = HBLPaletteMasks;
                   3746:        pHBLPalettes = HBLPalettes;
                   3747:        memset(pHBLPaletteMasks, 0, sizeof(Uint32)*NUM_VISIBLE_LINES);  /* Clear array */
1.1       root     3748: }
                   3749: 
1.1.1.2   root     3750: 
                   3751: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3752: /**
                   3753:  * Set pointers to HBLPalette tables to store correct colours/resolutions
                   3754:  */
1.1.1.9   root     3755: static void Video_SetHBLPaletteMaskPointers(void)
1.1       root     3756: {
1.1.1.15  root     3757:        int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.11  root     3758:        int Line;
                   3759: 
                   3760:        /* FIXME [NP] We should use Cycles_GetCounterOnWriteAccess, but it wouldn't     */
                   3761:        /* work when using multiple accesses instructions like move.l or movem  */
1.1.1.15  root     3762:        /* To correct this, we assume a delay of 8 cycles (should give a good approximation */
1.1.1.11  root     3763:        /* of a move.w or movem.l for example) */
                   3764:        //  FrameCycles = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
                   3765:        FrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO) + 8;
                   3766: 
                   3767:        /* Find 'line' into palette - screen starts 63 lines down, less 29 for top overscan */
1.1.1.15  root     3768:        Video_ConvertPosition ( FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     3769:        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
        !          3770: 
1.1.1.15  root     3771:        Line = HblCounterVideo - nFirstVisibleHbl;
1.1.1.11  root     3772: 
                   3773:        /* FIXME [NP] if the color change occurs after the last visible pixel of a line */
                   3774:        /* we consider the palette should be modified on the next line. This is quite */
                   3775:        /* a hack, we should handle all color changes through spec512.c to have cycle */
                   3776:        /* accuracy all the time. */
1.1.1.15  root     3777:        if ( LineCycles >= LINE_END_CYCLE_NO_RIGHT )
                   3778:                Line++;
1.1.1.11  root     3779: 
                   3780:        if (Line < 0)        /* Limit to top/bottom of possible visible screen */
                   3781:                Line = 0;
                   3782:        if (Line >= NUM_VISIBLE_LINES)
                   3783:                Line = NUM_VISIBLE_LINES-1;
                   3784: 
                   3785:        /* Store pointers */
                   3786:        pHBLPaletteMasks = &HBLPaletteMasks[Line];  /* Next mask entry */
                   3787:        pHBLPalettes = &HBLPalettes[16*Line];       /* Next colour raster list x16 colours */
1.1       root     3788: }
1.1.1.8   root     3789: 
                   3790: 
1.1.1.9   root     3791: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3792: /**
                   3793:  * Set video shifter timing variables according to screen refresh rate.
                   3794:  * Note: The following equation must be satisfied for correct timings:
                   3795:  *
                   3796:  *   nCyclesPerLine * nScanlinesPerFrame * nScreenRefreshRate = 8 MHz
                   3797:  */
1.1.1.10  root     3798: static void Video_ResetShifterTimings(void)
                   3799: {
1.1.1.11  root     3800:        Uint8 nSyncByte;
1.1.1.10  root     3801: 
1.1.1.11  root     3802:        nSyncByte = IoMem_ReadByte(0xff820a);
1.1.1.10  root     3803: 
1.1.1.19  root     3804:        if ((IoMem_ReadByte(0xff8260) & 3) == 2)
1.1.1.11  root     3805:        {
                   3806:                /* 71 Hz, monochrome */
1.1.1.24! root     3807:                nScreenRefreshRate = VIDEO_71HZ;
1.1.1.11  root     3808:                nScanlinesPerFrame = SCANLINES_PER_FRAME_71HZ;
                   3809:                nCyclesPerLine = CYCLES_PER_LINE_71HZ;
1.1.1.13  root     3810:                nStartHBL = VIDEO_START_HBL_71HZ;
1.1.1.11  root     3811:                nFirstVisibleHbl = FIRST_VISIBLE_HBL_71HZ;
1.1.1.13  root     3812:                nLastVisibleHbl = FIRST_VISIBLE_HBL_71HZ + VIDEO_HEIGHT_HBL_MONO;
1.1.1.11  root     3813:        }
                   3814:        else if (nSyncByte & 2)  /* Check if running in 50 Hz or in 60 Hz */
                   3815:        {
                   3816:                /* 50 Hz */
1.1.1.24! root     3817:                nScreenRefreshRate = VIDEO_50HZ;
1.1.1.11  root     3818:                nScanlinesPerFrame = SCANLINES_PER_FRAME_50HZ;
                   3819:                nCyclesPerLine = CYCLES_PER_LINE_50HZ;
1.1.1.13  root     3820:                nStartHBL = VIDEO_START_HBL_50HZ;
1.1.1.11  root     3821:                nFirstVisibleHbl = FIRST_VISIBLE_HBL_50HZ;
1.1.1.13  root     3822:                nLastVisibleHbl = FIRST_VISIBLE_HBL_50HZ + NUM_VISIBLE_LINES;
1.1.1.11  root     3823:        }
                   3824:        else
                   3825:        {
                   3826:                /* 60 Hz */
1.1.1.24! root     3827:                nScreenRefreshRate = VIDEO_60HZ;
1.1.1.11  root     3828:                nScanlinesPerFrame = SCANLINES_PER_FRAME_60HZ;
                   3829:                nCyclesPerLine = CYCLES_PER_LINE_60HZ;
1.1.1.13  root     3830:                nStartHBL = VIDEO_START_HBL_60HZ;
1.1.1.11  root     3831:                nFirstVisibleHbl = FIRST_VISIBLE_HBL_60HZ;
1.1.1.13  root     3832:                nLastVisibleHbl = FIRST_VISIBLE_HBL_60HZ + NUM_VISIBLE_LINES;
1.1.1.11  root     3833:        }
                   3834: 
1.1.1.24! root     3835: #ifndef OLD_CPU_SHIFT
        !          3836:        nCyclesPerLine <<= nCpuFreqShift;
        !          3837: #endif
        !          3838: 
1.1.1.11  root     3839:        if (bUseHighRes)
                   3840:        {
1.1.1.13  root     3841:                nEndHBL = nStartHBL + VIDEO_HEIGHT_HBL_MONO;
1.1.1.11  root     3842:        }
                   3843:        else
                   3844:        {
1.1.1.13  root     3845:                nEndHBL = nStartHBL + VIDEO_HEIGHT_HBL_COLOR;
1.1.1.11  root     3846:        }
                   3847: 
                   3848:        /* Reset freq changes position for the next VBL to come */
1.1.1.13  root     3849:        LastCycleScroll8264 = -1;
                   3850:        LastCycleScroll8265 = -1;
1.1.1.15  root     3851: 
                   3852:        TimerBEventCountCycleStart = -1;                /* reset timer B activation cycle for this VBL */
                   3853: 
1.1.1.18  root     3854:        BlankLines = 0;
1.1.1.11  root     3855: }
1.1.1.10  root     3856: 
                   3857: 
1.1.1.11  root     3858: /*-----------------------------------------------------------------------*/
                   3859: /**
1.1.1.15  root     3860:  * Clear the array indicating the state of each video line.
1.1.1.11  root     3861:  */
1.1.1.15  root     3862: static void Video_InitShifterLines ( void )
1.1.1.11  root     3863: {
1.1.1.15  root     3864:        int     i;
                   3865: 
                   3866:        for ( i=0 ; i<MAX_SCANLINES_PER_FRAME ; i++ )
                   3867:        {
                   3868:                ShifterFrame.ShifterLines[i].BorderMask = 0;
                   3869:                ShifterFrame.ShifterLines[i].DisplayPixelShift = 0;
                   3870:                ShifterFrame.ShifterLines[i].DisplayStartCycle = -1;
                   3871:        }
                   3872: 
                   3873:        ShifterFrame.ShifterLines[0].StartCycle = 0;                    /* 1st HBL starts at cycle 0 */
1.1.1.10  root     3874: }
                   3875: 
                   3876: 
                   3877: /*-----------------------------------------------------------------------*/
1.1.1.11  root     3878: /**
1.1.1.24! root     3879:  * Reload VideoBase with the content of ff8201/03.
        !          3880:  * On a real ST, this is done 3 HBL before starting a new VBL (HBL 310 at 50 Hz
        !          3881:  * and HBL 260 at 60 Hz) and on cycle 48 of this HBL.
        !          3882:  * This is where ff8205/ff8207 are reloaded with the content of ff8201/ff8203 on a real ST
        !          3883:  * (used in ULM DSOTS demos). VideoBase is also reloaded in Video_ClearOnVBL to be sure
        !          3884:  * (when video mode is not low/med res)
1.1.1.11  root     3885:  */
1.1.1.24! root     3886: static void Video_RestartVideoCounter(void)
1.1.1.9   root     3887: {
1.1.1.11  root     3888:        /* Get screen address pointer, aligned to 256 bytes on ST (ie ignore lowest byte) */
                   3889:        VideoBase = (Uint32)IoMem_ReadByte(0xff8201)<<16 | (Uint32)IoMem_ReadByte(0xff8203)<<8;
1.1.1.24! root     3890:        if (!Config_IsMachineST())
1.1.1.11  root     3891:        {
1.1.1.20  root     3892:                /* on STe 2 aligned, on TT 8 aligned. We do STe. */
1.1.1.11  root     3893:                VideoBase |= IoMem_ReadByte(0xff820d) & ~1;
                   3894:        }
                   3895:        pVideoRaster = &STRam[VideoBase];
1.1.1.24! root     3896: }
        !          3897: 
        !          3898: 
        !          3899: /*-----------------------------------------------------------------------*/
        !          3900: /**
        !          3901:  * Called on VBL, set registers ready for frame
        !          3902:  */
        !          3903: static void Video_ClearOnVBL(void)
        !          3904: {
        !          3905:        /* New screen, so first HBL */
        !          3906:        nHBL = 0;
        !          3907:        VerticalOverscan = V_OVERSCAN_NONE;
        !          3908: 
        !          3909:        Video_ResetShifterTimings();
        !          3910: 
        !          3911:        Video_RestartVideoCounter();
        !          3912: 
1.1.1.11  root     3913:        pSTScreen = pFrameBuffer->pSTScreen;
                   3914: 
                   3915:        Video_SetScreenRasters();
1.1.1.15  root     3916:        Video_InitShifterLines();
1.1.1.11  root     3917:        Spec512_StartVBL();
1.1.1.15  root     3918:        Video_StartHBL();                                       /* Init ShifterFrame.ShifterLines[0] */
1.1.1.11  root     3919: }
                   3920: 
                   3921: 
                   3922: /*-----------------------------------------------------------------------*/
                   3923: /**
                   3924:  * Get width, height and bpp according to TT-Resolution
                   3925:  */
                   3926: void Video_GetTTRes(int *width, int *height, int *bpp)
                   3927: {
                   3928:        switch (TTRes)
                   3929:        {
                   3930:         case ST_LOW_RES:   *width = 320;  *height = 200; *bpp = 4; break;
                   3931:         case ST_MEDIUM_RES:*width = 640;  *height = 200; *bpp = 2; break;
                   3932:         case ST_HIGH_RES:  *width = 640;  *height = 400; *bpp = 1; break;
                   3933:         case TT_LOW_RES:   *width = 320;  *height = 480; *bpp = 8; break;
                   3934:         case TT_MEDIUM_RES:*width = 640;  *height = 480; *bpp = 4; break;
                   3935:         case TT_HIGH_RES:  *width = 1280; *height = 960; *bpp = 1; break;
                   3936:         default:
                   3937:                fprintf(stderr, "TT res error!\n");
                   3938:                *width = 320; *height = 200; *bpp = 4;
                   3939:                break;
                   3940:        }
                   3941: }
                   3942: 
                   3943: 
                   3944: /**
1.1.1.24! root     3945:  * Set a TT palette color
1.1.1.11  root     3946:  */
1.1.1.24! root     3947: static void Video_SetTTPaletteColor(int idx, Uint32 addr)
1.1.1.11  root     3948: {
                   3949:        Uint8 r,g,b, lowbyte, highbyte;
                   3950: 
1.1.1.24! root     3951:        lowbyte = IoMem_ReadByte(addr + 1);
1.1.1.11  root     3952: 
1.1.1.24! root     3953:        if (TTSpecialVideoMode & 0x10)          /* TT Hyper-mono mode? */
1.1.1.11  root     3954:        {
1.1.1.24! root     3955:                r = g = b = lowbyte;
        !          3956:        }
        !          3957:        else
        !          3958:        {
        !          3959:                highbyte = IoMem_ReadByte(addr);
        !          3960:                r = (highbyte << 4) | (highbyte  & 0x0f);
        !          3961:                g = (lowbyte & 0xf0) | (lowbyte >> 4);
        !          3962:                b = (lowbyte << 4) | (lowbyte & 0x0f);
1.1.1.11  root     3963:        }
                   3964: 
1.1.1.24! root     3965:        //printf("%d (%x): (%d,%d,%d)\n", idx, addr, r,g,b);
        !          3966:        Screen_SetPaletteColor(idx, r,g,b);
        !          3967: }
        !          3968: 
        !          3969: /**
        !          3970:  * Which 256-color TT palette 16-color "bank" is mapped to ST(e) palette
        !          3971:  */
        !          3972: static int TTPaletteSTBank(void)
        !          3973: {
        !          3974:        return IoMem_ReadByte(0xff8263) & 0x0f;
        !          3975: }
        !          3976: 
        !          3977: /**
        !          3978:  * Convert TT palette to SDL palette
        !          3979:  */
        !          3980: static void Video_UpdateTTPalette(int bpp)
        !          3981: {
        !          3982:        if (TTRes == TT_HIGH_RES || (bUseVDIRes && bpp == 1))
1.1.1.11  root     3983:        {
                   3984:                /* Monochrome mode... palette is hardwired (?) */
1.1.1.24! root     3985:                Screen_SetPaletteColor(0, 255, 255, 255);
        !          3986:                Screen_SetPaletteColor(1, 0, 0, 0);
1.1.1.11  root     3987:        }
1.1.1.21  root     3988:        else if (bpp == 1)
                   3989:        {
1.1.1.24! root     3990:                /* Duochrome mode... palette is taken last two TT colors */
        !          3991:                int base = (IoMem_ReadWord(0xff8400) & 0x2) >> 1;
        !          3992:                Video_SetTTPaletteColor(base, 0xff85fc);
        !          3993:                Video_SetTTPaletteColor(base ^ 1, 0xff85fe);
1.1.1.21  root     3994:        }
1.1.1.11  root     3995:        else
                   3996:        {
1.1.1.24! root     3997:                Uint32 ttpalette = 0xff8400;
        !          3998:                int i, colors = 1 << bpp;
        !          3999: 
        !          4000:                if (colors <= 16)
        !          4001:                {
        !          4002:                        /* use correct ST palette bank */
        !          4003:                        ttpalette += TTPaletteSTBank() * 16*SIZE_WORD;
        !          4004:                }
        !          4005: 
1.1.1.11  root     4006:                for (i = 0; i < colors; i++)
                   4007:                {
1.1.1.24! root     4008:                        Video_SetTTPaletteColor(i, ttpalette);
        !          4009:                        ttpalette += SIZE_WORD;
1.1.1.11  root     4010:                }
                   4011:        }
                   4012: 
1.1.1.15  root     4013:        bTTColorsSync = true;
1.1.1.11  root     4014: }
                   4015: 
                   4016: 
                   4017: /*-----------------------------------------------------------------------*/
                   4018: /**
1.1.1.13  root     4019:  * Update TT palette and blit TT screen using VIDEL code.
                   4020:  * @return  true if the screen contents changed
1.1.1.11  root     4021:  */
1.1.1.16  root     4022: bool Video_RenderTTScreen(void)
1.1.1.11  root     4023: {
                   4024:        static int nPrevTTRes = -1;
                   4025:        int width, height, bpp;
                   4026: 
                   4027:        Video_GetTTRes(&width, &height, &bpp);
                   4028:        if (TTRes != nPrevTTRes)
                   4029:        {
1.1.1.24! root     4030:                Screen_SetGenConvSize(width, height, ConfigureParams.Screen.nForceBpp, false);
1.1.1.11  root     4031:                nPrevTTRes = TTRes;
                   4032:                if (bpp == 1)   /* Assert that mono palette will be used in mono mode */
1.1.1.15  root     4033:                        bTTColorsSync = false;
1.1.1.11  root     4034:        }
                   4035: 
                   4036:        /* colors need synching? */
1.1.1.24! root     4037:        if (!bTTColorsSync || TTSpecialVideoMode != nPrevTTSpecialVideoMode)
1.1.1.21  root     4038:        {
                   4039:                Video_UpdateTTPalette(bpp);
                   4040:                nPrevTTSpecialVideoMode = TTSpecialVideoMode;
                   4041:        }
1.1.1.11  root     4042: 
1.1.1.24! root     4043:        return Screen_GenDraw(VideoBase, width, height, bpp, width * bpp / 16,
        !          4044:                              0, 0, 0, 0);
1.1.1.11  root     4045: }
                   4046: 
                   4047: 
                   4048: /*-----------------------------------------------------------------------*/
                   4049: /**
                   4050:  * Draw screen (either with ST/STE shifter drawing functions or with
                   4051:  * Videl drawing functions)
                   4052:  */
                   4053: static void Video_DrawScreen(void)
                   4054: {
                   4055:        /* Skip frame if need to */
1.1.1.13  root     4056:        if (nVBLs % (nFrameSkips+1))
1.1.1.11  root     4057:                return;
                   4058: 
                   4059:        /* Now draw the screen! */
1.1.1.24! root     4060:        if (bUseVDIRes)
        !          4061:        {
        !          4062:                if (Config_IsMachineTT() && !bTTColorsSync)
        !          4063:                {
        !          4064:                        Video_UpdateTTPalette(VDIPlanes);
        !          4065:                }
        !          4066:                else if (Config_IsMachineFalcon())
        !          4067:                {
        !          4068:                        VIDEL_UpdateColors();
        !          4069:                }
        !          4070:                Screen_GenDraw(VideoBase, VDIWidth, VDIHeight, VDIPlanes,
        !          4071:                               VDIWidth * VDIPlanes / 16, 0, 0, 0, 0);
        !          4072:        }
        !          4073:        else if (Config_IsMachineFalcon())
1.1.1.11  root     4074:        {
1.1.1.17  root     4075:                VIDEL_renderScreen();
1.1.1.11  root     4076:        }
1.1.1.24! root     4077:        else if (Config_IsMachineTT())
1.1.1.11  root     4078:        {
1.1.1.17  root     4079:                Video_RenderTTScreen();
1.1.1.11  root     4080:        }
                   4081:        else
1.1.1.13  root     4082:        {
                   4083:                /* Before drawing the screen, ensure all unused lines are cleared to color 0 */
                   4084:                /* (this can happen in 60 Hz when hatari is displaying the screen's border) */
                   4085:                /* pSTScreen was set during Video_CopyScreenLineColor */
1.1.1.24! root     4086:                if (nHBL < nLastVisibleHbl)
1.1.1.13  root     4087:                        memset(pSTScreen, 0, SCREENBYTES_LINE * ( nLastVisibleHbl - nHBL ) );
                   4088: 
1.1.1.17  root     4089:                Screen_Draw();
1.1.1.13  root     4090:        }
1.1.1.11  root     4091: }
                   4092: 
                   4093: 
                   4094: /*-----------------------------------------------------------------------*/
                   4095: /**
1.1.1.15  root     4096:  * Start HBL, Timer B and VBL interrupts.
                   4097:  */
                   4098: 
                   4099: 
                   4100: /**
                   4101:  * Start HBL or Timer B interrupt at position Pos. If position Pos was
                   4102:  * already reached, then the interrupt is set on the next line.
                   4103:  */
                   4104: 
1.1.1.24! root     4105: static void Video_AddInterrupt ( int Line , int Pos , interrupt_id Handler )
1.1.1.15  root     4106: {
                   4107:        int FrameCycles , HblCounterVideo , LineCycles;
1.1.1.24! root     4108:        int CyclesToPos;
1.1.1.15  root     4109: 
                   4110:        if ( nHBL >= nScanlinesPerFrame )
                   4111:          return;                               /* don't set a new hbl/timer B if we're on the last line, as the vbl will happen first */
                   4112:        
                   4113:        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     4114: //fprintf ( stderr , "add int pos=%d handler=%d LineCycles=%d nCyclesPerLine=%d\n" , Pos , Handler , LineCycles , nCyclesPerLine );
        !          4115: 
        !          4116: #ifndef OLD_CPU_SHIFT
        !          4117:        Pos <<= nCpuFreqShift;                  /* convert Pos at 8 MHz into number of cycles at 8/16/32 MHz */
        !          4118: #endif
        !          4119: 
        !          4120:        if ( Line <= nHBL )
        !          4121:                CyclesToPos = Pos + ShifterFrame.ShifterLines[Line].StartCycle - FrameCycles;
        !          4122:        else                                    /* Pos is on the next line (after the current hbl) */
        !          4123:                CyclesToPos = Pos + ShifterFrame.ShifterLines[Line-1].StartCycle - FrameCycles + nCyclesPerLine;
1.1.1.15  root     4124: 
1.1.1.24! root     4125:        CycInt_AddRelativeInterrupt ( CyclesToPos , INT_CPU_CYCLE, Handler );
        !          4126: //fprintf ( stderr , "add int pos=%d handler=%d LineCycles=%d nCyclesPerLine=%d -> %d cycles\n" , Pos , Handler , LineCycles , nCyclesPerLine , CycleToPos );
1.1.1.15  root     4127: }
                   4128: 
                   4129: 
1.1.1.24! root     4130: static void Video_AddInterruptHBL ( int Line , int Pos )
1.1.1.15  root     4131: {
1.1.1.24! root     4132: //fprintf ( stderr , "add hbl line=%d pos=%d\n" , Line , Pos );
1.1.1.15  root     4133:        if ( !bUseVDIRes )
1.1.1.24! root     4134:        {
        !          4135:                Video_AddInterrupt ( Line , Pos , INTERRUPT_VIDEO_HBL );
        !          4136:        }
1.1.1.15  root     4137: }
                   4138: 
                   4139: 
1.1.1.24! root     4140: void Video_AddInterruptTimerB ( int LineVideo , int CycleVideo , int Pos )
1.1.1.15  root     4141: {
1.1.1.24! root     4142: //fprintf ( stderr , "add timerb line=%d cycle=%d pos=%d\n" , LineVideo , CycleVideo , Pos );
1.1.1.15  root     4143:        if ( !bUseVDIRes )
1.1.1.24! root     4144:        {
        !          4145:                /* If new position is not reached yet, next interrupt should be on current line */
        !          4146:                /* else it should be on next line */
        !          4147: #ifdef OLD_CPU_SHIFT
        !          4148:                if ( Pos > CycleVideo )
        !          4149: #else
        !          4150:                if ( ( Pos << nCpuFreqShift ) > CycleVideo )
        !          4151: #endif
        !          4152:                        Video_AddInterrupt ( LineVideo , Pos , INTERRUPT_VIDEO_ENDLINE );
        !          4153:                else
        !          4154:                        Video_AddInterrupt ( LineVideo+1 , Pos , INTERRUPT_VIDEO_ENDLINE );
        !          4155:        }
1.1.1.15  root     4156: }
                   4157: 
                   4158: 
                   4159: /**
                   4160:  * Add some video interrupts to handle the first HBL and the first Timer B
                   4161:  * in a new VBL. Also add an interrupt to trigger the next VBL.
                   4162:  * This function is called from the VBL, so we use PendingCycleOver to take into account
                   4163:  * the possible delay occurring when the VBL was executed.
1.1.1.16  root     4164:  * In monochrome mode (71 Hz) a line is 224 cycles, which means if VBL is delayed
                   4165:  * by a DIVS, FrameCycles can already be > 224 and we need to add an immediate
                   4166:  * interrupt for hbl/timer in the next 4/8 cycles (else crash might happen as
                   4167:  * line 0 processing would be skipped).
1.1.1.11  root     4168:  */
1.1.1.15  root     4169: void Video_StartInterrupts ( int PendingCyclesOver )
1.1.1.11  root     4170: {
1.1.1.16  root     4171:        int FrameCycles , HblCounterVideo , LineCycles;
                   4172:        int Pos;
                   4173: 
1.1.1.15  root     4174:        /* HBL/Timer B are not emulated in VDI mode */
1.1.1.14  root     4175:        if (!bUseVDIRes)
                   4176:        {
1.1.1.24! root     4177:                ShifterFrame.ShifterLines[0].StartCycle = 0;            /* 1st HBL always starts at cycle 0 */
        !          4178: 
1.1.1.16  root     4179:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15  root     4180: 
1.1.1.16  root     4181:                /* Set Timer B interrupt for line 0 */
                   4182:                Pos = Video_TimerB_GetPos ( 0 );
1.1.1.24! root     4183: #ifdef OLD_CPU_SHIFT
1.1.1.16  root     4184:                if ( Pos > FrameCycles )                /* check Pos for line 0 was not already reached */
1.1.1.24! root     4185: #else
        !          4186:                if ( ( Pos << nCpuFreqShift ) > FrameCycles )   /* check Pos for line 0 was not already reached */
        !          4187: #endif
        !          4188:                        Video_AddInterruptTimerB ( HblCounterVideo , LineCycles , Pos );
1.1.1.16  root     4189:                else                                    /* the VBL was delayed by more than 1 HBL, add an immediate timer B */
                   4190:                {
                   4191:                        LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d delayed too much video_cyc=%d >= pos=%d for first timer B, add immediate timer B\n" ,
                   4192:                                nVBLs , FrameCycles , Pos );
                   4193:                        CycInt_AddRelativeInterrupt ( 4 , INT_CPU_CYCLE, INTERRUPT_VIDEO_ENDLINE );
                   4194:                }
                   4195: 
                   4196:                /* Set HBL interrupt for line 0 */
1.1.1.24! root     4197:                Pos = Video_HBL_GetDefaultPos();
        !          4198:                ShifterFrame.HBL_CyclePos = Pos;
        !          4199: #ifdef OLD_CPU_SHIFT
1.1.1.16  root     4200:                if ( Pos > FrameCycles )                /* check Pos for line 0 was not already reached */
1.1.1.24! root     4201: #else
        !          4202:                if ( ( Pos << nCpuFreqShift ) > FrameCycles )   /* check Pos for line 0 was not already reached */
        !          4203: #endif
        !          4204:                        Video_AddInterruptHBL ( HblCounterVideo , Pos );
1.1.1.16  root     4205:                else                                    /* the VBL was delayed by more than 1 HBL, add an immediate HBL */
                   4206:                {
                   4207:                        LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d delayed too much video_cyc=%d >= pos=%d for first HBL, add immediate HBL\n" ,
                   4208:                                nVBLs , FrameCycles , Pos );
                   4209:                        CycInt_AddRelativeInterrupt ( 8 , INT_CPU_CYCLE, INTERRUPT_VIDEO_HBL );         /* use 8 instead of 4 to happen after immediate timer b */
                   4210:                }
1.1.1.14  root     4211:        }
1.1.1.15  root     4212: 
1.1.1.24! root     4213:        /* When using VDI, we setup the next VBL here ; else it will be setup at the start of the last HBL */
        !          4214:        else
        !          4215:        {
        !          4216:                /* Add new VBL interrupt */
        !          4217:                CyclesPerVBL = nScanlinesPerFrame * nCyclesPerLine;             /* TODO [NP] use cpufreq / 50 instead ? */
        !          4218:                CycInt_AddRelativeInterrupt( CyclesPerVBL - PendingCyclesOver , INT_CPU_CYCLE , INTERRUPT_VIDEO_VBL);
        !          4219:        }
1.1.1.11  root     4220: }
                   4221: 
                   4222: 
                   4223: /*-----------------------------------------------------------------------*/
                   4224: /**
1.1.1.15  root     4225:  * VBL interrupt : set new interrupts, draw screen, generate sound,
                   4226:  * reset counters, ...
1.1.1.11  root     4227:  */
1.1.1.15  root     4228: void Video_InterruptHandler_VBL ( void )
1.1.1.11  root     4229: {
                   4230:        int PendingCyclesOver;
1.1.1.24! root     4231:        int PendingInterruptCount_save;
        !          4232:        static Uint64 VBL_ClockCounter;
        !          4233: 
        !          4234:        PendingInterruptCount_save = PendingInterruptCount;
        !          4235: 
        !          4236:        /* Act on shortcut keys */
        !          4237:        /* NOTE [NP] : ShortCut_ActKey should be called as soon as possible in the VBL handler, */
        !          4238:        /* before acknowledging it. This way, we will get a call of the VBL handler when restoring */
        !          4239:        /* a memory snapshot that was created using shortcut keys. Else, a VBL will be lost when restoring */
        !          4240:        /* the snapshot if we use '--memstate' option, which can break some programs during several VBL */
        !          4241:        /* after the restore. */
        !          4242:        /* TODO : a better way would be to handle shortcut keys inside the main CPU loop in newcpu.c */
        !          4243:        /* to always get a consistent state (doing it from an interrupt handler is not correct). */
        !          4244:        /* (eg, fix restoring screen 1 in the B.I.G. Demo, where the raster bar can be wrongly displayed) */
        !          4245:        ShortCut_ActKey();
        !          4246: 
        !          4247:        /* In case we press a shortcut for reset, PendingInterruptCount will be changed to >0 and we will get */
        !          4248:        /* some warnings "bug nHBL=...". To avoid this we restore the value (<= 0) saved at the start of the VBL */
        !          4249:        if ( PendingInterruptCount > 0 )
        !          4250:                PendingInterruptCount = PendingInterruptCount_save;
1.1.1.9   root     4251: 
1.1.1.21  root     4252:        /* Store cycles we went over for this frame(this is our initial count) */
1.1.1.11  root     4253:        PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );    /* +ve */
1.1.1.10  root     4254: 
1.1.1.11  root     4255:        /* Remove this interrupt from list and re-order */
1.1.1.24! root     4256:        /* NOTE [NP] : in case ShortCut_ActKey above was used to change some parameters that require */
        !          4257:        /* a reset, then the current int won't be the VBL handler anymore. In that case we should not */
        !          4258:        /* acknowledge else this will ack another int and this will break Falcon DMA sound for example */
        !          4259:        /* TODO : see above to handle shortcut keys in the main CPU loop */
        !          4260:        if ( CycInt_GetActiveInt() == INTERRUPT_VIDEO_VBL )
        !          4261:                CycInt_AcknowledgeInterrupt();
1.1.1.9   root     4262: 
1.1.1.14  root     4263:        /* Increment the vbl jitter index */
                   4264:        VblJitterIndex++;
1.1.1.20  root     4265:        VblJitterIndex %= VBL_JITTER_ARRAY_SIZE;
1.1.1.14  root     4266:        
1.1.1.11  root     4267:        /* Set frame cycles, used for Video Address */
1.1.1.24! root     4268: #ifdef OLD_CPU_SHIFT
        !          4269:        Cycles_SetCounter(CYCLES_COUNTER_VIDEO, PendingCyclesOver + pVideoTiming->VblVideoCycleOffset);
        !          4270: #else
        !          4271:        Cycles_SetCounter(CYCLES_COUNTER_VIDEO, PendingCyclesOver + ( pVideoTiming->VblVideoCycleOffset << nCpuFreqShift ) );
        !          4272: #endif
1.1.1.9   root     4273: 
1.1.1.11  root     4274:        /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
                   4275:        Keymap_DebounceAllKeys();
1.1.1.9   root     4276: 
1.1.1.11  root     4277:        Video_DrawScreen();
1.1.1.9   root     4278: 
1.1.1.11  root     4279:        /* Check printer status */
                   4280:        Printer_CheckIdleStatus();
                   4281: 
                   4282:        /* Update counter for number of screen refreshes per second */
                   4283:        nVBLs++;
                   4284:        /* Set video registers for frame */
                   4285:        Video_ClearOnVBL();
1.1.1.15  root     4286: 
1.1.1.17  root     4287:        /* Videl Vertical counter reset (To be removed when Videl emulation is finished) */
1.1.1.24! root     4288:        if (Config_IsMachineFalcon())
        !          4289:        {
1.1.1.17  root     4290:                vfc_counter = 0;
                   4291:        }
                   4292:        
1.1.1.15  root     4293:        /* Since we don't execute HBL functions in VDI mode, we've got to
                   4294:         * initialize the first HBL palette here when VDI mode is enabled. */
                   4295:        if (bUseVDIRes)
                   4296:                Video_StoreFirstLinePalette();
                   4297: 
                   4298:        /* Start VBL, HBL and Timer B interrupts (this must be done after resetting
                   4299:          * video cycle counter setting default freq values in Video_ClearOnVBL) */
                   4300:        Video_StartInterrupts(PendingCyclesOver);
                   4301: 
1.1.1.21  root     4302:        /* Update the IKBD's internal clock */
                   4303:        IKBD_UpdateClockOnVBL ();
                   4304: 
1.1.1.16  root     4305:        /* Record video frame is necessary */
                   4306:        if ( bRecordingAvi )
                   4307:                Avi_RecordVideoStream ();
                   4308: 
1.1.1.11  root     4309:        /* Store off PSG registers for YM file, is enabled */
                   4310:        YMFormat_UpdateRecording();
                   4311:        /* Generate 1/50th second of sound sample data, to be played by sound thread */
                   4312:        Sound_Update_VBL();
                   4313: 
1.1.1.24! root     4314:        LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d video_cyc=%d pending_cyc=%d jitter=%d vbl_cycles=%d\n" ,
        !          4315:                        nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] ,
        !          4316:                        (int)( CyclesGlobalClockCounter - PendingCyclesOver - VBL_ClockCounter ) );
        !          4317: 
        !          4318:        VBL_ClockCounter = CyclesGlobalClockCounter - PendingCyclesOver;
1.1.1.11  root     4319: 
1.1.1.21  root     4320:        /* Print traces if pending VBL bit changed just before IACK when VBL interrupt is allowed */
                   4321:        if ( ( CPU_IACK == true ) && ( regs.intmask < 4 ) )
                   4322:        {
                   4323:                if ( pendingInterrupts & ( 1 << 4 ) )
                   4324:                {
                   4325:                        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" ,
                   4326:                                nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
                   4327:                }
                   4328:                else
                   4329:                {
                   4330:                        LOG_TRACE ( TRACE_VIDEO_VBL , "VBL %d, new pending VBL set just before iack video_cyc=%d pending_cyc=%d jitter=%d\n" ,
                   4331:                                nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
                   4332:                }
                   4333:        }
                   4334: 
                   4335:        /* Set pending bit for VBL interrupt in the CPU IPL */
1.1.1.23  root     4336:        M68000_Exception(EXCEPTION_NR_VBLANK, M68000_EXC_SRC_AUTOVEC);  /* Vertical blank interrupt, level 4 */
1.1.1.11  root     4337: 
                   4338:        Main_WaitOnVbl();
1.1.1.9   root     4339: }
                   4340: 
                   4341: 
                   4342: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4343: /**
1.1.1.13  root     4344:  * Write to video address base high, med and low register (0xff8201/03/0d).
1.1.1.24! root     4345:  * On STE/TT, when a program writes to high or med registers, base low register
1.1.1.13  root     4346:  * is reset to zero.
1.1.1.11  root     4347:  */
1.1.1.24! root     4348: void Video_ScreenBase_WriteByte(void)
1.1.1.9   root     4349: {
1.1.1.24! root     4350:        /* On STF/STE machines with <= 4MB of RAM, video addresses are limited to $3fffff */
        !          4351:        if ( IoAccessCurrentAddress == 0xff8201 )
        !          4352:                IoMem[ 0xff8201 ] &= DMA_MaskAddressHigh();
        !          4353: 
        !          4354:        /* On STE/TT, reset screen base low register */
        !          4355:        if (!Config_IsMachineST() &&
        !          4356:            (IoAccessCurrentAddress == 0xff8201 || IoAccessCurrentAddress == 0xff8203))
        !          4357:                IoMem[0xff820d] = 0;
1.1.1.8   root     4358: 
1.1.1.15  root     4359:        if (LOG_TRACE_LEVEL(TRACE_VIDEO_STE))
1.1.1.11  root     4360:        {
1.1.1.15  root     4361:                int FrameCycles, HblCounterVideo, LineCycles;
                   4362: 
                   4363:                Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     4364:                LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.15  root     4365:                
                   4366:                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     4367:                        (IoMem[0xff8201]<<16)+(IoMem[0xff8203]<<8)+IoMem[0xff820d] ,
1.1.1.15  root     4368:                        FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11  root     4369:        }
1.1.1.8   root     4370: }
                   4371: 
                   4372: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4373: /**
                   4374:  * Read video address counter and update ff8205/07/09
                   4375:  */
                   4376: void Video_ScreenCounter_ReadByte(void)
1.1.1.8   root     4377: {
1.1.1.11  root     4378:        Uint32 addr;
1.1.1.8   root     4379: 
1.1.1.11  root     4380:        addr = Video_CalculateAddress();                /* get current video address */
1.1.1.24! root     4381: 
        !          4382:        /* On STE, handle modifications of the video counter address $ff8205/07/09 */
        !          4383:        /* that occurred while the display was already ON */
        !          4384:        if ( VideoCounterDelayedOffset != 0 )
        !          4385:        {
        !          4386:                addr += ( VideoCounterDelayedOffset & ~1 );
        !          4387: //             fprintf ( stderr , "adjust video counter offset=%d new video=%x\n" , VideoCounterDelayedOffset , addr );
        !          4388:        }
        !          4389: 
1.1.1.11  root     4390:        IoMem[0xff8205] = ( addr >> 16 ) & 0xff;
                   4391:        IoMem[0xff8207] = ( addr >> 8 ) & 0xff;
                   4392:        IoMem[0xff8209] = addr & 0xff;
1.1.1.8   root     4393: }
                   4394: 
1.1.1.9   root     4395: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4396: /**
                   4397:  * Write to video address counter (0xff8205, 0xff8207 and 0xff8209).
1.1.1.24! root     4398:  * Called on STE/TT only and like with base address, you cannot set lowest bit.
1.1.1.16  root     4399:  *
                   4400:  * As Hatari processes/converts one complete video line at a time, we have 3 cases :
                   4401:  * - If display has not started yet for this line (left border), we can change pVideoRaster now.
                   4402: *    We must take into account that the MMU starts 16 cycles earlier when hscroll is used.
                   4403:  * - If display has stopped for this line (right border), we will change pVideoRaster
                   4404:  *   in Video_CopyScreenLineColor using pVideoRasterDelayed once the line has been processed.
                   4405:  * - If the write is made while display is on, then we must compute an offset of what
                   4406:  *   the new address should have been, to correctly emulate the video address at the
                   4407:  *   end of the line while taking into account the fact that the video pointer is incrementing
                   4408:  *   during the active part of the line (this is the most "tricky" case)
                   4409:  *
                   4410:  * 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     4411:  */
1.1.1.9   root     4412: void Video_ScreenCounter_WriteByte(void)
                   4413: {
1.1.1.11  root     4414:        Uint8 AddrByte;
1.1.1.16  root     4415:        Uint32 addr_cur;
                   4416:        Uint32 addr_new = 0;
1.1.1.15  root     4417:        int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.13  root     4418:        int Delayed;
1.1.1.16  root     4419:        int MMUStartCycle;
1.1.1.11  root     4420: 
1.1.1.15  root     4421:        Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     4422:        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
        !          4423: 
        !          4424:        /* On STF/STE machines with <= 4MB of RAM, video addresses are limited to $3fffff */
        !          4425:        if ( IoAccessCurrentAddress == 0xff8205 )
        !          4426:                IoMem[ 0xff8205 ] &= DMA_MaskAddressHigh();
1.1.1.11  root     4427: 
                   4428:        AddrByte = IoMem[ IoAccessCurrentAddress ];
                   4429: 
1.1.1.16  root     4430:        /* Get current video address from the shifter */
                   4431:        addr_cur = Video_CalculateAddress();
                   4432:        /* Correct the address in case a modification of ff8205/07/09 was already delayed */
                   4433:        addr_new = addr_cur + VideoCounterDelayedOffset;
                   4434:        /* Correct the address in case video counter was already modified in the right border */
                   4435:        if ( pVideoRasterDelayed != NULL )
                   4436:                addr_new = pVideoRasterDelayed - STRam;
                   4437:        
                   4438:        /* addr_new should now be the same as on a real STE */
                   4439:        /* Compute the new video address with one modified byte */
                   4440:        if ( IoAccessCurrentAddress == 0xff8205 )
1.1.1.24! root     4441:                addr_new = ( addr_new & 0x00ffff ) | ( AddrByte << 16 );
1.1.1.16  root     4442:        else if ( IoAccessCurrentAddress == 0xff8207 )
                   4443:                addr_new = ( addr_new & 0xff00ff ) | ( AddrByte << 8 );
                   4444:        else if ( IoAccessCurrentAddress == 0xff8209 )
                   4445:                addr_new = ( addr_new & 0xffff00 ) | ( AddrByte );
                   4446: 
1.1.1.23  root     4447:        addr_new &= ~1;                                         /* clear bit 0 */
                   4448: 
1.1.1.16  root     4449:        MMUStartCycle = Video_GetMMUStartCycle ( ShifterFrame.ShifterLines[ nHBL ].DisplayStartCycle );
                   4450: 
1.1.1.11  root     4451:        /* If display has not started, we can still modify pVideoRaster */
1.1.1.16  root     4452:        /* We must also check the write does not overlap the end of the line (to be sure Video_EndHBL is called first) */
                   4453:        if ( ( ( LineCycles <= MMUStartCycle ) && ( nHBL == HblCounterVideo ) )
1.1.1.18  root     4454:                || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.11  root     4455:        {
1.1.1.23  root     4456:                pVideoRaster = &STRam[addr_new];                /* set new video address */
1.1.1.16  root     4457:                VideoCounterDelayedOffset = 0;
                   4458:                pVideoRasterDelayed = NULL;
1.1.1.15  root     4459:                Delayed = false;
1.1.1.11  root     4460:        }
                   4461: 
1.1.1.16  root     4462:        /* Display is OFF (right border) but we can't change pVideoRaster now, we must process Video_CopyScreenLineColor first */
1.1.1.18  root     4463:        else if ( ( nHBL >= nStartHBL ) && ( nHBL < nEndHBL + BlankLines )      /* line should be active */
1.1.1.16  root     4464:                && ( ( LineCycles > ShifterFrame.ShifterLines[ nHBL ].DisplayEndCycle )         /* we're in the right border */
                   4465:                  || ( HblCounterVideo == nHBL+1 ) ) )          /* or the write overlaps the next line and Video_EndHBL was not called yet */
                   4466:        {
                   4467:                VideoCounterDelayedOffset = 0;
1.1.1.23  root     4468:                pVideoRasterDelayed = &STRam[addr_new];         /* new value for pVideoRaster at the end of Video_CopyScreenLineColor */
1.1.1.16  root     4469:                Delayed = true;
                   4470:        }
                   4471: 
                   4472:        /* Counter is modified while display is ON, store the bytes offset for Video_CopyScreenLineColor */
                   4473:        /* Even on a real STE, modifying video address in this case will cause artefacts */
1.1.1.11  root     4474:        else
                   4475:        {
1.1.1.16  root     4476:                VideoCounterDelayedOffset = addr_new - addr_cur;
                   4477:                pVideoRasterDelayed = NULL;
1.1.1.15  root     4478:                Delayed = true;
1.1.1.23  root     4479: 
1.1.1.24! root     4480:                /* [FIXME] 'E605' Earth part by Light : write to FF8209 on STE while display is on, */
        !          4481:                 /* in that case video counter is not correct */
        !          4482:                if ( STMemory_ReadLong ( M68000_InstrPC ) == 0x01c9ffc3 )       /* movep.l d0,-$3d(a1) */
        !          4483:                        VideoCounterDelayedOffset += 6;                         /* or -2 ? */
        !          4484: 
        !          4485:                /* [FIXME] 'Tekila' part in Delirious Demo IV : write to FF8209 on STE while display is on, */
        !          4486:                 /* in that case video counter is not correct */
        !          4487:                else if ( ( STMemory_ReadLong ( M68000_InstrPC ) == 0x11c48209 )        /* move.b d4,$ff8209.w */
        !          4488:                        && ( STMemory_ReadLong ( M68000_InstrPC-4 ) == 0x11c28207 )     /* move.b d2,$ff8207.w */
        !          4489:                        && ( STMemory_ReadLong ( M68000_InstrPC-8 ) == 0x82054842 ) )
        !          4490:                {
        !          4491:                        VideoCounterDelayedOffset += 2; 
        !          4492:                        if ( VideoCounterDelayedOffset == 256 )                 /* write sometimes happens at the same time */
        !          4493:                                VideoCounterDelayedOffset = 0;                  /* ff8207 increases */
        !          4494:                        /* partial fix, some errors remain for other cases where write happens at the same time ff8207 increases ... */
        !          4495:                }
        !          4496: 
1.1.1.11  root     4497:        }
                   4498: 
1.1.1.16  root     4499:        LOG_TRACE(TRACE_VIDEO_STE , "write ste video %x val=0x%x video_old=%x video_new=%x offset=%x delayed=%s"
                   4500:                                " video_cyc_w=%d line_cyc_w=%d @ nHBL=%d/video_hbl_w=%d pc=%x instr_cyc=%d\n" ,
                   4501:                                IoAccessCurrentAddress, AddrByte, addr_cur , addr_new , VideoCounterDelayedOffset , Delayed ? "yes" : "no" ,
1.1.1.15  root     4502:                                FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.9   root     4503: }
1.1.1.8   root     4504: 
                   4505: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4506: /**
                   4507:  * Read video sync register (0xff820a)
                   4508:  */
1.1.1.8   root     4509: void Video_Sync_ReadByte(void)
                   4510: {
1.1.1.24! root     4511:        if (Config_IsMachineST() || Config_IsMachineSTE())
1.1.1.14  root     4512:                IoMem[0xff820a] |= 0xfc;                /* set unused bits 2-7 to 1 */
1.1.1.8   root     4513: }
                   4514: 
                   4515: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4516: /**
                   4517:  * Read video base address low byte (0xff820d). A plain ST can only store
                   4518:  * screen addresses rounded to 256 bytes (i.e. no lower byte).
                   4519:  */
1.1.1.8   root     4520: void Video_BaseLow_ReadByte(void)
                   4521: {
1.1.1.24! root     4522:        if (Config_IsMachineST())
1.1.1.11  root     4523:                IoMem[0xff820d] = 0;        /* On ST this is always 0 */
1.1.1.9   root     4524: 
1.1.1.11  root     4525:        /* Note that you should not do anything here for STe because
                   4526:         * VideoBase address is set in an interrupt and would be wrong
                   4527:         * here.   It's fine like this.
                   4528:         */
1.1.1.8   root     4529: }
                   4530: 
                   4531: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4532: /**
                   4533:  * Read video line width register (0xff820f)
                   4534:  */
1.1.1.8   root     4535: void Video_LineWidth_ReadByte(void)
                   4536: {
1.1.1.24! root     4537:        if (Config_IsMachineST())
1.1.1.11  root     4538:                IoMem[0xff820f] = 0;        /* On ST this is always 0 */
1.1.1.18  root     4539: 
                   4540:        /* If we're not in STF mode, we use the value already stored in $ff820f */
1.1.1.8   root     4541: }
                   4542: 
                   4543: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4544: /**
1.1.1.24! root     4545:  * Read video resolution register (0xff8260)
        !          4546:  * NOTE : resolution register is stored in both the GLUE and the SHIFTER
        !          4547:  * As the value is read from the SHIFTER, we need to round memory access to 4 cycle
1.1.1.11  root     4548:  */
1.1.1.24! root     4549: void Video_Res_ReadByte(void)
1.1.1.8   root     4550: {
1.1.1.24! root     4551:        /* Access to shifter regs are on a 4 cycle boundary */
        !          4552:        M68000_SyncCpuBus_OnReadAccess();
        !          4553: 
1.1.1.11  root     4554:        if (bUseHighRes)
1.1.1.14  root     4555:                IoMem[0xff8260] = 2;                    /* If mono monitor, force to high resolution */
                   4556: 
1.1.1.24! root     4557:        if (Config_IsMachineST())
1.1.1.17  root     4558:                IoMem[0xff8260] |= 0xfc;                /* On STF, set unused bits 2-7 to 1 */
1.1.1.24! root     4559:        else if (Config_IsMachineTT())
        !          4560:                IoMem[0xff8260] &= 0x07;                /* Only use bits 0, 1 and 2 */
1.1.1.20  root     4561:        else
                   4562:                IoMem[0xff8260] &= 0x03;                /* Only use bits 0 and 1, unused bits 2-7 are set to 0 */
1.1.1.8   root     4563: }
                   4564: 
1.1.1.10  root     4565: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4566: /**
                   4567:  * Read horizontal scroll register (0xff8265)
                   4568:  */
1.1.1.10  root     4569: void Video_HorScroll_Read(void)
                   4570: {
1.1.1.11  root     4571:        IoMem[0xff8265] = HWScrollCount;
1.1.1.10  root     4572: }
1.1.1.8   root     4573: 
                   4574: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4575: /**
                   4576:  * Write video line width register (0xff820f) - STE only.
                   4577:  * Content of LineWidth is added to the shifter counter when display is
                   4578:  * turned off (start of the right border, usually at cycle 376)
                   4579:  */
1.1.1.10  root     4580: void Video_LineWidth_WriteByte(void)
                   4581: {
1.1.1.11  root     4582:        Uint8 NewWidth;
1.1.1.15  root     4583:        int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.13  root     4584:        int Delayed;
1.1.1.11  root     4585: 
1.1.1.15  root     4586:        Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     4587:        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.11  root     4588: 
                   4589:        NewWidth = IoMem_ReadByte(0xff820f);
                   4590: 
                   4591:        /* We must also check the write does not overlap the end of the line */
1.1.1.15  root     4592:        if ( ( ( nHBL == HblCounterVideo ) && ( LineCycles <= ShifterFrame.ShifterLines[ HblCounterVideo ].DisplayEndCycle ) )
1.1.1.18  root     4593:                || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.13  root     4594:        {
1.1.1.11  root     4595:                LineWidth = NewWidth;           /* display is on, we can still change */
1.1.1.13  root     4596:                NewLineWidth = -1;              /* cancel 'pending' change */
1.1.1.15  root     4597:                Delayed = false;
1.1.1.13  root     4598:        }
1.1.1.11  root     4599:        else
1.1.1.13  root     4600:        {
1.1.1.11  root     4601:                NewLineWidth = NewWidth;        /* display is off, can't change LineWidth once in right border */
1.1.1.15  root     4602:                Delayed = true;
1.1.1.13  root     4603:        }
1.1.1.11  root     4604: 
1.1.1.15  root     4605:        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     4606:                                        NewWidth, Delayed ? "yes" : "no" ,
1.1.1.15  root     4607:                                        FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.10  root     4608: }
                   4609: 
                   4610: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4611: /**
                   4612:  * Write to video shifter palette registers (0xff8240-0xff825e)
1.1.1.14  root     4613:  *
                   4614:  * Note that there's a special "strange" case when writing only to the upper byte
                   4615:  * of the color reg (instead of writing 16 bits at once with .W/.L).
                   4616:  * In that case, the byte written to address x is automatically written
                   4617:  * 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     4618:  * Similarly, when writing a byte to address x+1, it's also written to address x
1.1.1.14  root     4619:  * So :        move.w #0,$ff8240       -> color 0 is now $000
                   4620:  *     move.b #7,$ff8240       -> color 0 is now $707 !
1.1.1.22  root     4621:  *     move.b #$55,$ff8241     -> color 0 is now $555 !
1.1.1.14  root     4622:  *     move.b #$71,$ff8240     -> color 0 is now $171 (bytes are first copied, then masked)
1.1.1.11  root     4623:  */
1.1.1.23  root     4624: static void Video_ColorReg_WriteWord(void)
1.1.1.8   root     4625: {
1.1.1.24! root     4626:        Uint32 addr;
        !          4627:        Uint16 col;
        !          4628:        int idx;
1.1.1.22  root     4629: 
1.1.1.24! root     4630:        addr = IoAccessCurrentAddress;
1.1.1.14  root     4631: 
1.1.1.24! root     4632:        /* Access to shifter regs are on a 4 cycle boundary */
        !          4633:        M68000_SyncCpuBus_OnWriteAccess();
1.1.1.14  root     4634: 
1.1.1.24! root     4635:        /* Handle special case when writing only to the upper byte of the color reg */
        !          4636:        if (nIoMemAccessSize == SIZE_BYTE && (IoAccessCurrentAddress & 1) == 0)
        !          4637:                col = (IoMem_ReadByte(addr) << 8) + IoMem_ReadByte(addr);       /* copy upper byte into lower byte */
        !          4638:        /* Same when writing only to the lower byte of the color reg */
        !          4639:        else if (nIoMemAccessSize == SIZE_BYTE && (IoAccessCurrentAddress & 1) == 1)
        !          4640:                col = (IoMem_ReadByte(addr) << 8) + IoMem_ReadByte(addr);       /* copy lower byte into upper byte */
        !          4641:        /* Usual case, writing a word or a long (2 words) */
        !          4642:        else
        !          4643:                col = IoMem_ReadWord(addr);
        !          4644: 
        !          4645:        if (Config_IsMachineST())
        !          4646:                col &= 0x777;                   /* Mask off to ST 512 palette */
        !          4647:        else
        !          4648:                col &= 0xfff;                   /* Mask off to STe 4096 palette */
        !          4649: 
        !          4650:        addr &= 0xfffffffe;                     /* Ensure addr is even to store the 16 bit color */
        !          4651:        IoMem_WriteWord(addr, col);             /* (some games write 0xFFFF and read back to see if STe) */
        !          4652: 
        !          4653:        idx = (addr - 0xff8240) / 2;            /* words */
        !          4654: 
        !          4655:        if (bUseHighRes || (bUseVDIRes && VDIPlanes == 1))
        !          4656:        {
        !          4657:                if (idx == 0)
        !          4658:                {
        !          4659:                        Screen_SetPaletteColor(col & 1, 0, 0, 0);
        !          4660:                        Screen_SetPaletteColor(!(col & 1), 255, 255, 255);
        !          4661:                }
        !          4662:        }
        !          4663:        else if (bUseVDIRes)
        !          4664:        {
        !          4665:                int r, g, b;
        !          4666:                r = (col >> 8) & 0x0f;
        !          4667:                r = ((r & 7) << 1) | (r >> 3);
        !          4668:                r |= r << 4;
        !          4669:                g = (col >> 4) & 0x0f;
        !          4670:                g = ((g & 7) << 1) | (g >> 3);
        !          4671:                g |= g << 4;
        !          4672:                b = col & 0x0f;
        !          4673:                b = ((b & 7) << 1) | (b >> 3);
        !          4674:                b |= b << 4;
        !          4675:                Screen_SetPaletteColor(idx, r, g, b);
        !          4676:        }
        !          4677:        else    /* Don't store if hi-res or VDI resolution */
        !          4678:        {
        !          4679:                Video_SetHBLPaletteMaskPointers();     /* Set 'pHBLPalettes' etc.. according cycles into frame */
1.1.1.22  root     4680: 
1.1.1.11  root     4681:                Spec512_StoreCyclePalette(col, addr);  /* Store colour into CyclePalettes[] */
                   4682:                pHBLPalettes[idx] = col;               /* Set colour x */
                   4683:                *pHBLPaletteMasks |= 1 << idx;         /* And mask */
                   4684: 
1.1.1.15  root     4685:                if (LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
1.1.1.11  root     4686:                {
1.1.1.15  root     4687:                        int FrameCycles, HblCounterVideo, LineCycles;
                   4688: 
                   4689:                        Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     4690:                        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.15  root     4691: 
                   4692:                        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     4693:                                IoAccessCurrentAddress, col,
1.1.1.15  root     4694:                                FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11  root     4695:                }
                   4696: 
                   4697:        }
1.1.1.8   root     4698: }
                   4699: 
1.1.1.22  root     4700: /*
                   4701:  * Read from video shifter palette registers (0xff8240-0xff825e)
                   4702:  *
                   4703:  * NOTE [NP] : On STF, only 3 bits are used for RGB (instead of 4 on STE) ;
                   4704:  * the content of bits 3, 7 and 11 is not defined and will be 0 or 1
                   4705:  * depending on the latest activity on the BUS (last word access by the CPU or
                   4706:  * the shifter). As precisely emulating these bits is quite complicated,
                   4707:  * we use random values for now.
                   4708:  * NOTE [NP] : When executing code from the IO addresses between 0xff8240-0xff825e
                   4709:  * the unused bits on STF are set to '0' (used in "The Union Demo" protection).
                   4710:  * So we use rand() only if PC is located in RAM.
                   4711:  */
1.1.1.23  root     4712: static void Video_ColorReg_ReadWord(void)
1.1.1.22  root     4713: {
                   4714:        Uint16 col;
                   4715:        Uint32 addr;
                   4716:        addr = IoAccessCurrentAddress;
                   4717: 
1.1.1.24! root     4718:        /* Access to shifter regs are on a 4 cycle boundary */
        !          4719:        M68000_SyncCpuBus_OnReadAccess();
        !          4720: 
1.1.1.22  root     4721:        col = IoMem_ReadWord(addr);
                   4722: 
1.1.1.24! root     4723:        if (Config_IsMachineST() && M68000_GetPC() < 0x400000)          /* PC in RAM < 4MB */
1.1.1.22  root     4724:        {
                   4725:                col = ( col & 0x777 ) | ( rand() & 0x888 );
                   4726:                IoMem_WriteWord ( addr , col );
                   4727:        }
                   4728: 
                   4729:        if (LOG_TRACE_LEVEL(TRACE_VIDEO_COLOR))
                   4730:        {
                   4731:                int FrameCycles, HblCounterVideo, LineCycles;
                   4732: 
                   4733:                Video_GetPosition_OnReadAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     4734:                LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.22  root     4735: 
                   4736:                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" ,
                   4737:                        IoAccessCurrentAddress, col,
                   4738:                        FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
                   4739:        }
                   4740: }
                   4741: 
                   4742: /*
                   4743:  * [NP] TODO : due to how .L accesses are handled in ioMem.c, we can't call directly
                   4744:  * Video_ColorReg_WriteWord from ioMemTabST.c / ioMemTabSTE.c, we must use an intermediate
                   4745:  * function, else .L accesses will not change 2 .W color regs, but only one.
                   4746:  * This should be changed in ioMem.c to do 2 separate .W accesses, as would do a real 68000
                   4747:  */
                   4748: 
1.1.1.8   root     4749: void Video_Color0_WriteWord(void)
                   4750: {
1.1.1.22  root     4751:        Video_ColorReg_WriteWord();
1.1.1.8   root     4752: }
                   4753: 
                   4754: void Video_Color1_WriteWord(void)
                   4755: {
1.1.1.22  root     4756:        Video_ColorReg_WriteWord();
1.1.1.8   root     4757: }
                   4758: 
                   4759: void Video_Color2_WriteWord(void)
                   4760: {
1.1.1.22  root     4761:        Video_ColorReg_WriteWord();
1.1.1.8   root     4762: }
                   4763: 
                   4764: void Video_Color3_WriteWord(void)
                   4765: {
1.1.1.22  root     4766:        Video_ColorReg_WriteWord();
1.1.1.8   root     4767: }
                   4768: 
                   4769: void Video_Color4_WriteWord(void)
                   4770: {
1.1.1.22  root     4771:        Video_ColorReg_WriteWord();
1.1.1.8   root     4772: }
                   4773: 
                   4774: void Video_Color5_WriteWord(void)
                   4775: {
1.1.1.22  root     4776:        Video_ColorReg_WriteWord();
1.1.1.8   root     4777: }
                   4778: 
                   4779: void Video_Color6_WriteWord(void)
                   4780: {
1.1.1.22  root     4781:        Video_ColorReg_WriteWord();
1.1.1.8   root     4782: }
                   4783: 
                   4784: void Video_Color7_WriteWord(void)
                   4785: {
1.1.1.22  root     4786:        Video_ColorReg_WriteWord();
1.1.1.8   root     4787: }
                   4788: 
                   4789: void Video_Color8_WriteWord(void)
                   4790: {
1.1.1.22  root     4791:        Video_ColorReg_WriteWord();
1.1.1.8   root     4792: }
                   4793: 
                   4794: void Video_Color9_WriteWord(void)
                   4795: {
1.1.1.22  root     4796:        Video_ColorReg_WriteWord();
1.1.1.8   root     4797: }
                   4798: 
                   4799: void Video_Color10_WriteWord(void)
                   4800: {
1.1.1.22  root     4801:        Video_ColorReg_WriteWord();
1.1.1.8   root     4802: }
                   4803: 
                   4804: void Video_Color11_WriteWord(void)
                   4805: {
1.1.1.22  root     4806:        Video_ColorReg_WriteWord();
1.1.1.8   root     4807: }
                   4808: 
                   4809: void Video_Color12_WriteWord(void)
                   4810: {
1.1.1.22  root     4811:        Video_ColorReg_WriteWord();
1.1.1.8   root     4812: }
                   4813: 
                   4814: void Video_Color13_WriteWord(void)
                   4815: {
1.1.1.22  root     4816:        Video_ColorReg_WriteWord();
1.1.1.8   root     4817: }
                   4818: 
                   4819: void Video_Color14_WriteWord(void)
                   4820: {
1.1.1.22  root     4821:        Video_ColorReg_WriteWord();
1.1.1.8   root     4822: }
                   4823: 
                   4824: void Video_Color15_WriteWord(void)
                   4825: {
1.1.1.22  root     4826:        Video_ColorReg_WriteWord();
                   4827: }
                   4828: 
                   4829: 
                   4830: void Video_Color0_ReadWord(void)
                   4831: {
                   4832:        Video_ColorReg_ReadWord();
                   4833: }
                   4834: 
                   4835: void Video_Color1_ReadWord(void)
                   4836: {
                   4837:        Video_ColorReg_ReadWord();
                   4838: }
                   4839: 
                   4840: void Video_Color2_ReadWord(void)
                   4841: {
                   4842:        Video_ColorReg_ReadWord();
                   4843: }
                   4844: 
                   4845: void Video_Color3_ReadWord(void)
                   4846: {
                   4847:        Video_ColorReg_ReadWord();
                   4848: }
                   4849: 
                   4850: void Video_Color4_ReadWord(void)
                   4851: {
                   4852:        Video_ColorReg_ReadWord();
                   4853: }
                   4854: 
                   4855: void Video_Color5_ReadWord(void)
                   4856: {
                   4857:        Video_ColorReg_ReadWord();
                   4858: }
                   4859: 
                   4860: void Video_Color6_ReadWord(void)
                   4861: {
                   4862:        Video_ColorReg_ReadWord();
                   4863: }
                   4864: 
                   4865: void Video_Color7_ReadWord(void)
                   4866: {
                   4867:        Video_ColorReg_ReadWord();
                   4868: }
                   4869: 
                   4870: void Video_Color8_ReadWord(void)
                   4871: {
                   4872:        Video_ColorReg_ReadWord();
                   4873: }
                   4874: 
                   4875: void Video_Color9_ReadWord(void)
                   4876: {
                   4877:        Video_ColorReg_ReadWord();
                   4878: }
                   4879: 
                   4880: void Video_Color10_ReadWord(void)
                   4881: {
                   4882:        Video_ColorReg_ReadWord();
                   4883: }
                   4884: 
                   4885: void Video_Color11_ReadWord(void)
                   4886: {
                   4887:        Video_ColorReg_ReadWord();
                   4888: }
                   4889: 
                   4890: void Video_Color12_ReadWord(void)
                   4891: {
                   4892:        Video_ColorReg_ReadWord();
                   4893: }
                   4894: 
                   4895: void Video_Color13_ReadWord(void)
                   4896: {
                   4897:        Video_ColorReg_ReadWord();
                   4898: }
                   4899: 
                   4900: void Video_Color14_ReadWord(void)
                   4901: {
                   4902:        Video_ColorReg_ReadWord();
                   4903: }
                   4904: 
                   4905: void Video_Color15_ReadWord(void)
                   4906: {
                   4907:        Video_ColorReg_ReadWord();
1.1.1.8   root     4908: }
                   4909: 
                   4910: 
                   4911: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4912: /**
1.1.1.24! root     4913:  * Write to video resolution register (0xff8260)
        !          4914:  * NOTE : resolution register is stored in both the GLUE and the SHIFTER
        !          4915:  * When writing to ff8260, the GLUE gets the new value immediately, before
        !          4916:  * rounding to 4 cycles. The rounding happens later, when the SHIFTER reads
        !          4917:  * the data from the bus, as explained by Ijor on atari-forum.com :
        !          4918:  * - CPU starts a bus cycle addressing shifter RES
        !          4919:  * - CPU drives the data bus with the new value
        !          4920:  * - GLUE reads the value from the CPU data bus
        !          4921:  * - Possible wait states inserted here by MMU
        !          4922:  * - MMU connects the CPU data bus to the RAM data bus
        !          4923:  * - SHIFTER reads the value from the RAM data bus
        !          4924:  * - CPU finishes the bus cycle
1.1.1.11  root     4925:  */
1.1.1.24! root     4926: void Video_Res_WriteByte(void)
1.1.1.8   root     4927: {
1.1.1.20  root     4928:        Uint8 VideoShifterByte;
                   4929: 
1.1.1.24! root     4930:        if (Config_IsMachineTT())
1.1.1.20  root     4931:        {
                   4932:                TTRes = IoMem_ReadByte(0xff8260) & 7;
                   4933:                /* Copy to TT shifter mode register: */
                   4934:                IoMem_WriteByte(0xff8262, TTRes);
                   4935:        }
                   4936:        else if (!bUseVDIRes)   /* ST and STE mode */
1.1.1.11  root     4937:        {
1.1.1.18  root     4938:                /* We only care for lower 2-bits */
                   4939:                VideoShifterByte = IoMem[0xff8260] & 3;
1.1.1.22  root     4940:                /* 3 is not a valid resolution, use high res instead */
1.1.1.18  root     4941:                if ( VideoShifterByte == 3 )
1.1.1.13  root     4942:                {
1.1.1.22  root     4943:                        VideoShifterByte = 2;
                   4944:                        IoMem_WriteByte(0xff8260,2);
1.1.1.13  root     4945:                }
1.1.1.18  root     4946: 
1.1.1.24! root     4947:                Video_WriteToGlueShifterRes(VideoShifterByte);
1.1.1.11  root     4948:                Video_SetHBLPaletteMaskPointers();
                   4949:                *pHBLPaletteMasks &= 0xff00ffff;
                   4950:                /* Store resolution after palette mask and set resolution write bit: */
                   4951:                *pHBLPaletteMasks |= (((Uint32)VideoShifterByte|0x04)<<16);
                   4952:        }
1.1.1.24! root     4953: 
        !          4954:        /* Access to shifter regs are on a 4 cycle boundary */
        !          4955:        /* Rounding is added here, after the value was processed by Video_Update_Glue_State() */
        !          4956:        M68000_SyncCpuBus_OnWriteAccess();
1.1.1.10  root     4957: }
                   4958: 
                   4959: /*-----------------------------------------------------------------------*/
1.1.1.11  root     4960: /**
1.1.1.13  root     4961:  * Handle horizontal scrolling to the left.
                   4962:  * On STE, there're 2 registers that can scroll the line :
                   4963:  *  - $ff8264 : scroll without prefetch
1.1.1.14  root     4964:  *  - $ff8265 : scroll with prefetch
1.1.1.13  root     4965:  * Both registers will scroll the line to the left by skipping the amount
                   4966:  * of pixels in $ff8264 or $ff8265 (from 0 to 15).
                   4967:  * As some pixels will be skipped, this means the shifter needs to read
                   4968:  * 16 other pixels in advance in some internal registers to have an uninterrupted flow of pixels.
                   4969:  *
1.1.1.14  root     4970:  * These 16 pixels can be prefetched before the display starts (on cycle 56 for example) when using
1.1.1.13  root     4971:  * $ff8265 to scroll the line. In that case 8 more bytes per line (low res) will be read. Most programs
                   4972:  * are using $ff8265 to scroll the line.
                   4973:  *
                   4974:  * When using $ff8264, the next 16 pixels will not be prefetched before the display
                   4975:  * starts, they will be read when the display normally starts (cycle 56). While
                   4976:  * reading these 16 pixels, the shifter won't be able to display anything, which will
                   4977:  * result in 16 pixels having the color 0. So, reading the 16 pixels will in fact delay
                   4978:  * the real start of the line, which will look as if it started 16 pixels later. As the
                   4979:  * shifter will stop the display at cycle 56+320 anyway, this means the last 16 pixels
                   4980:  * of each line won't be displayed and you get the equivalent of a shorter 304 pixels line.
                   4981:  * As a consequence, this register is rarely used to scroll the line.
                   4982:  *
1.1.1.21  root     4983:  * By writing a value > 0 in $ff8265 (to start prefetching) and immediately after a value of 0
1.1.1.13  root     4984:  * in $ff8264 (no scroll and no prefetch), it's possible to fill the internal registers used
                   4985:  * for the scrolling even if scrolling is set to 0. In that case, the shifter will start displaying
                   4986:  * each line 16 pixels earlier (as the data are already available in the internal registers).
                   4987:  * This allows to have 336 pixels per line (instead of 320) for all the remaining lines on the screen.
                   4988:  *
                   4989:  * Although some programs are using this sequence :
                   4990:  *     move.w  #1,$ffff8264            ; Word access!
                   4991:  *     clr.b   $ffff8264               ; Byte access!
                   4992:  * It is also possible to add 16 pixels by doing :
                   4993:  *     move.b  #X,$ff8265              ; with X > 0
                   4994:  *     move.b  #0,$ff8264
1.1.1.11  root     4995:  * Some games (Obsession, Skulls) and demos (Pacemaker by Paradox) use this
                   4996:  * feature to increase the resolution, so we have to emulate this bug, too!
1.1.1.13  root     4997:  *
                   4998:  * So considering a low res line of 320 pixels (160 bytes) :
                   4999:  *     - if both $ff8264/65 are 0, no scrolling happens, the shifter reads 160 bytes and displays 320 pixels (same as STF)
                   5000:  *     - if $ff8265 > 0, line is scrolled, the shifter reads 168 bytes and displays 320 pixels.
                   5001:  *     - if $ff8264 > 0, line is scrolled, the shifter reads 160 bytes and displays 304 pixels,
                   5002:  *             the display starts 16 pixels later.
                   5003:  *     - if $ff8265 > 0 and then $ff8264 = 0, there's no scrolling, the shifter reads 168 bytes and displays 336 pixels,
                   5004:  *             the display starts 16 pixels earlier.
1.1.1.11  root     5005:  */
1.1.1.13  root     5006: 
1.1.1.24! root     5007: void Video_HorScroll_Read_8264(void)
        !          5008: {
        !          5009:        /* Access to shifter regs are on a 4 cycle boundary */
        !          5010:        M68000_SyncCpuBus_OnReadAccess();
        !          5011: }
        !          5012: 
        !          5013: void Video_HorScroll_Read_8265(void)
        !          5014: {
        !          5015:        /* Access to shifter regs are on a 4 cycle boundary */
        !          5016:        M68000_SyncCpuBus_OnReadAccess();
        !          5017:        /* [NP] TODO : it seems ff8265 has some additional wait states */
        !          5018: }
        !          5019: 
1.1.1.13  root     5020: void Video_HorScroll_Write_8264(void)
                   5021: {
1.1.1.24! root     5022:        /* Access to shifter regs are on a 4 cycle boundary */
        !          5023:        M68000_SyncCpuBus_OnWriteAccess();
        !          5024: 
1.1.1.13  root     5025:        Video_HorScroll_Write();
                   5026: }
                   5027: 
                   5028: void Video_HorScroll_Write_8265(void)
                   5029: {
1.1.1.24! root     5030:        /* Access to shifter regs are on a 4 cycle boundary */
        !          5031:        M68000_SyncCpuBus_OnWriteAccess();
        !          5032:        /* [NP] TODO : it seems ff8265 has some additional wait states */
        !          5033: 
1.1.1.13  root     5034:        Video_HorScroll_Write();
                   5035: }
                   5036: 
1.1.1.10  root     5037: void Video_HorScroll_Write(void)
                   5038: {
1.1.1.13  root     5039:        Uint32 RegAddr;
1.1.1.11  root     5040:        Uint8 ScrollCount;
1.1.1.13  root     5041:        Uint8 Prefetch;
1.1.1.15  root     5042:        int FrameCycles, HblCounterVideo, LineCycles;
                   5043:        bool Add16px = false;
1.1.1.13  root     5044:        static Uint8 LastVal8265 = 0;
                   5045:        int Delayed;
                   5046: 
1.1.1.15  root     5047:        Video_GetPosition_OnWriteAccess ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.24! root     5048:        LineCycles = VIDEO_CYCLE_TO_HPOS ( LineCycles );
1.1.1.10  root     5049: 
1.1.1.13  root     5050:        RegAddr = IoAccessCurrentAddress;               /* 0xff8264 or 0xff8265 */
                   5051:        ScrollCount = IoMem[ RegAddr ];
1.1.1.11  root     5052:        ScrollCount &= 0x0f;
1.1.1.10  root     5053: 
1.1.1.13  root     5054:        if ( RegAddr == 0xff8264 )
                   5055:        {
                   5056:                Prefetch = 0;                           /* scroll without prefetch */
1.1.1.15  root     5057:                LastCycleScroll8264 = FrameCycles;
1.1.1.11  root     5058: 
1.1.1.15  root     5059:                ShifterFrame.Scroll8264Pos.VBL = nVBLs;
                   5060:                ShifterFrame.Scroll8264Pos.FrameCycles = FrameCycles;
                   5061:                ShifterFrame.Scroll8264Pos.HBL = HblCounterVideo;
                   5062:                ShifterFrame.Scroll8264Pos.LineCycles = LineCycles;
                   5063: 
                   5064:                if ( ( ScrollCount == 0 ) && ( LastVal8265 > 0 )
                   5065:                        && ( ShifterFrame.Scroll8265Pos.VBL > 0 )               /* a write to ff8265 has been made */
                   5066:                        && ( ShifterFrame.Scroll8265Pos.VBL == ShifterFrame.Scroll8264Pos.VBL )         /* during the same VBL */
                   5067:                        && ( ShifterFrame.Scroll8264Pos.FrameCycles - ShifterFrame.Scroll8265Pos.FrameCycles <= 40 ) )
1.1.1.13  root     5068:                {
1.1.1.15  root     5069:                        LOG_TRACE(TRACE_VIDEO_BORDER_H , "detect ste left+16 pixels\n" );
                   5070:                        Add16px = true;
1.1.1.13  root     5071:                }
                   5072:        }
                   5073:        else
1.1.1.11  root     5074:        {
1.1.1.13  root     5075:                Prefetch = 1;                           /* scroll with prefetch */
1.1.1.15  root     5076:                LastCycleScroll8265 = FrameCycles;
                   5077: 
                   5078:                ShifterFrame.Scroll8265Pos.VBL = nVBLs;
                   5079:                ShifterFrame.Scroll8265Pos.FrameCycles = FrameCycles;
                   5080:                ShifterFrame.Scroll8265Pos.HBL = HblCounterVideo;
                   5081:                ShifterFrame.Scroll8265Pos.LineCycles = LineCycles;
                   5082: 
1.1.1.13  root     5083:                LastVal8265 = ScrollCount;
1.1.1.15  root     5084:                Add16px = false;
1.1.1.11  root     5085:        }
1.1.1.13  root     5086: 
                   5087: 
                   5088:        /* If the write was made before display starts on the current line, then */
                   5089:        /* we can still change the value now. Else, the new values will be used */
                   5090:        /* for line n+1. */
                   5091:        /* We must also check the write does not overlap the end of the line */
1.1.1.24! root     5092:        if ( ( ( LineCycles <= pVideoTiming->H_Start_Low_50 ) && ( nHBL == HblCounterVideo ) )
1.1.1.18  root     5093:                || ( nHBL < nStartHBL ) || ( nHBL >= nEndHBL + BlankLines ) )
1.1.1.11  root     5094:        {
1.1.1.13  root     5095:                HWScrollCount = ScrollCount;            /* display has not started, we can still change */
                   5096:                HWScrollPrefetch = Prefetch;
                   5097:                bSteBorderFlag = Add16px;
                   5098:                NewHWScrollCount = -1;                  /* cancel 'pending' change */
1.1.1.15  root     5099:                Delayed = false;
1.1.1.11  root     5100:        }
                   5101:        else
                   5102:        {
1.1.1.13  root     5103:                NewHWScrollCount = ScrollCount;         /* display has started, can't change HWScrollCount now */
                   5104:                NewHWScrollPrefetch = Prefetch;
                   5105:                if ( Add16px )
                   5106:                        NewSteBorderFlag = 1;
                   5107:                else
                   5108:                        NewSteBorderFlag = 0;
1.1.1.15  root     5109:                Delayed = true;
1.1.1.11  root     5110:        }
                   5111: 
1.1.1.15  root     5112:        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     5113:                RegAddr , ScrollCount, Delayed ? "yes" : "no" ,
1.1.1.15  root     5114:                FrameCycles, LineCycles, nHBL, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.13  root     5115: }
1.1.1.11  root     5116: 
1.1.1.24! root     5117: 
        !          5118: /*-----------------------------------------------------------------------*/
        !          5119: /**
        !          5120:  * Helper for TT->ST color reg copies
        !          5121:  */
        !          5122: static void TT2STColor(Uint32 ttaddr, Uint32 staddr)
        !          5123: {
        !          5124:        Uint16 stcolor, ttcolor;
        !          5125: 
        !          5126:        ttcolor = IoMem_ReadWord(ttaddr);
        !          5127:        stcolor = ((ttcolor & 0xeee) >> 1) | ((ttcolor&0x111) << 3);
        !          5128:        IoMem_WriteWord(staddr, stcolor);
        !          5129: #if 0
        !          5130:        fprintf(stderr, "0x%x: 0x%03x (TT) -> 0x%x: 0x%03x (ST)\n",
        !          5131:                ttaddr, ttcolor, staddr, stcolor);
        !          5132: #endif
        !          5133: }
        !          5134: 
1.1.1.11  root     5135: /*-----------------------------------------------------------------------*/
                   5136: /**
                   5137:  * Write to TT shifter mode register (0xff8262)
                   5138:  */
                   5139: void Video_TTShiftMode_WriteWord(void)
                   5140: {
1.1.1.24! root     5141:        Uint32 stpalette = 0xff8240;
        !          5142:        Uint32 ttpalette = 0xff8400;
        !          5143:        int i;
        !          5144: 
1.1.1.11  root     5145:        TTRes = IoMem_ReadByte(0xff8262) & 7;
1.1.1.21  root     5146:        TTSpecialVideoMode = IoMem_ReadByte(0xff8262) & 0x90;
1.1.1.11  root     5147: 
                   5148:        /*fprintf(stderr, "Write to FF8262: %x, res=%i\n", IoMem_ReadWord(0xff8262), TTRes);*/
                   5149: 
                   5150:        /* Is it an ST compatible resolution? */
                   5151:        if (TTRes <= 2)
                   5152:        {
                   5153:                IoMem_WriteByte(0xff8260, TTRes);
1.1.1.24! root     5154:                Video_Res_WriteByte();
1.1.1.21  root     5155:                IoMem_WriteByte(0xff8262, TTRes | TTSpecialVideoMode);
                   5156:        }
                   5157: 
1.1.1.24! root     5158:        /* ST palette needs to be updated in case there was a bank switch */
        !          5159:        ttpalette += TTPaletteSTBank() * 16*SIZE_WORD;
        !          5160: #if 0
        !          5161:        fprintf(stderr, "TT ST Palette bank: %d\n", TTPaletteSTBank());
        !          5162: #endif
        !          5163:        for (i = 0; i < 16*SIZE_WORD; i += SIZE_WORD)
1.1.1.21  root     5164:        {
1.1.1.24! root     5165:                TT2STColor(ttpalette, stpalette);
        !          5166:                ttpalette += SIZE_WORD;
        !          5167:                stpalette += SIZE_WORD;
1.1.1.11  root     5168:        }
1.1.1.24! root     5169:        /* in case bank was switched and there are <= 16 colors */
        !          5170:        bTTColorsSync = false;
1.1.1.11  root     5171: }
                   5172: 
1.1.1.24! root     5173: 
1.1.1.11  root     5174: /*-----------------------------------------------------------------------*/
                   5175: /**
1.1.1.24! root     5176:  * Write to TT color register area (at 0xff8400)
        !          5177:  *
        !          5178:  * Sync TT to ST color register
        !          5179:  * 
        !          5180:  * Although registers themselves are word sized, writes to this area
        !          5181:  * can be of any size. Hatari IO-area handling doesn't feed them here
        !          5182:  * word sized, that would require separate handler for each palette
        !          5183:  * entry, and there are 256 of them.
        !          5184:  */
        !          5185: void Video_TTColorRegs_Write(void)
        !          5186: {
        !          5187:        const Uint32 stpalette = 0xff8240;
        !          5188:        const Uint32 ttpalette = 0xff8400;
        !          5189:        int offset, i;
        !          5190:        Uint32 addr;
        !          5191: 
        !          5192:        /* ensure even address for byte accesses */
        !          5193:        addr = IoAccessCurrentAddress & 0xfffffffe;
        !          5194: 
        !          5195:        offset = addr - (ttpalette + TTPaletteSTBank() * 16*SIZE_WORD);
        !          5196: 
        !          5197:        /* in case it was long access */
        !          5198:        for (i = 0; i < nIoMemAccessSize; i += 2)
        !          5199:        {
        !          5200:                /* outside ST->TT color reg mapping bank? */
        !          5201:                if (offset < 0 || offset >= 16*SIZE_WORD)
        !          5202:                        continue;
        !          5203:                TT2STColor(addr, stpalette + offset);
        !          5204:                offset += 2;
        !          5205:                addr += 2;
        !          5206:        }
1.1.1.15  root     5207:        bTTColorsSync = false;
1.1.1.11  root     5208: }
                   5209: 
                   5210: /*-----------------------------------------------------------------------*/
                   5211: /**
1.1.1.24! root     5212:  * Write to ST color register area on TT (starting at 0xff8240)
        !          5213:  *
        !          5214:  * ST color register write on TT -> sync to TT color register
        !          5215:  *
        !          5216:  * Although registers themselves are word sized, writes to this area
        !          5217:  * can be of any size. Hatari IO-area handling doesn't feed them here
        !          5218:  * word sized, that would require separate handler for each palette
        !          5219:  * entry.
        !          5220:  */
        !          5221: void Video_TTColorRegs_STRegWrite(void)
        !          5222: {
        !          5223:        const Uint32 stpalette = 0xff8240;
        !          5224:        const Uint32 ttpalette = 0xff8400;
        !          5225:        Uint16 ttcolor;
        !          5226:        Uint16 stcolor;
        !          5227:        int offset, i;
        !          5228:        Uint32 addr;
        !          5229: 
        !          5230:        /* byte writes don't have effect on TT */
        !          5231:        if (nIoMemAccessSize < 2)
        !          5232:                return;
        !          5233: 
        !          5234:        addr = IoAccessCurrentAddress;
        !          5235: 
        !          5236:        offset = addr - stpalette;
        !          5237:        assert(offset >= 0 && offset < 16*SIZE_WORD);
        !          5238:        offset += TTPaletteSTBank() * 16*SIZE_WORD;
        !          5239: 
        !          5240:        /* in case it was long access */
        !          5241:        for (i = 0; i < nIoMemAccessSize; i += 2)
        !          5242:        {
        !          5243:                /* program may write 0xFFFF and read it back
        !          5244:                 * to check for STe palette so need to be masked
        !          5245:                 */
        !          5246:                stcolor = IoMem_ReadWord(addr) & 0xfff;
        !          5247:                IoMem_WriteWord(addr, stcolor);
        !          5248:                /* Sync ST(e) color to TT register */
        !          5249:                ttcolor = ((stcolor & 0x777) << 1) | ((stcolor & 0x888) >> 3);
        !          5250:                IoMem_WriteWord(ttpalette + offset, ttcolor);
        !          5251: #if 0
        !          5252:                fprintf(stderr, "0x%x: 0x%03x (ST) -> 0x%x: 0x%03x (TT)\n",
        !          5253:                        addr, stcolor, ttpalette + offset, ttcolor);
        !          5254: #endif
        !          5255:                offset += 2;
        !          5256:                addr += 2;
        !          5257:        }
        !          5258:        bTTColorsSync = false;
1.1.1.8   root     5259: }
1.1.1.22  root     5260: 
                   5261: 
                   5262: /*-----------------------------------------------------------------------*/
                   5263: /**
                   5264:  * display video related information (for debugger info command)
                   5265:  */
1.1.1.23  root     5266: void Video_Info(FILE *fp, Uint32 dummy)
1.1.1.22  root     5267: {
                   5268:        const char *mode;
1.1.1.24! root     5269:        switch (VerticalOverscan) {
        !          5270:        case V_OVERSCAN_NONE:
1.1.1.22  root     5271:                mode = "none";
                   5272:                break;
1.1.1.24! root     5273:        case V_OVERSCAN_NO_TOP:
1.1.1.22  root     5274:                mode = "top";
                   5275:                break;
1.1.1.24! root     5276:        case V_OVERSCAN_NO_BOTTOM_50:
        !          5277:        case V_OVERSCAN_NO_BOTTOM_60:
1.1.1.22  root     5278:                mode = "bottom";
                   5279:                break;
1.1.1.24! root     5280:        case V_OVERSCAN_NO_TOP|V_OVERSCAN_NO_BOTTOM_50:
1.1.1.22  root     5281:                mode = "top+bottom";
                   5282:                break;
                   5283:        default:
                   5284:                mode = "unknown";
                   5285:        }
1.1.1.23  root     5286:        fprintf(fp, "Video base   : 0x%x\n", VideoBase);
                   5287:        fprintf(fp, "VBL counter  : %d\n", nVBLs);
                   5288:        fprintf(fp, "HBL line     : %d\n", nHBL);
                   5289:        fprintf(fp, "V-overscan   : %s\n", mode);
                   5290:        fprintf(fp, "Refresh rate : %d Hz\n", nScreenRefreshRate);
                   5291:        fprintf(fp, "Frame skips  : %d\n", nFrameSkips);
1.1.1.22  root     5292: 
                   5293:        /* TODO: any other information that would be useful to show? */
                   5294: }
1.1.1.24! root     5295: 

unix.superglobalmegacorp.com

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