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

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

unix.superglobalmegacorp.com

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