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

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

unix.superglobalmegacorp.com

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