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

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

unix.superglobalmegacorp.com

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