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