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

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

unix.superglobalmegacorp.com

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