2014-12-31 22:53:55 +00:00
/*
AppleWin : An Apple //e emulator for Windows
Copyright ( C ) 2010 - 2011 , William S Simms
2016-07-26 18:14:06 +00:00
Copyright ( C ) 2014 - 2016 , Michael Pohoreski , Tom Charlesworth
2014-12-31 22:53:55 +00:00
AppleWin is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
AppleWin is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with AppleWin ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
// Includes
# include "StdAfx.h"
2020-11-11 21:15:27 +00:00
# include "NTSC.h"
2020-11-26 21:50:06 +00:00
# include "Core.h"
2018-08-05 21:19:51 +00:00
# include "CPU.h" // CpuGetCyclesThisVideoFrame()
2020-04-02 19:17:32 +00:00
# include "Memory.h" // MemGetMainPtr(), MemGetAuxPtr(), MemGetAnnunciator()
2020-12-28 16:25:29 +00:00
# include "Interface.h" // GetFrameBuffer()
2019-01-19 16:51:45 +00:00
# include "RGBMonitor.h"
2021-11-30 21:41:02 +00:00
# include "VidHD.h"
2014-12-31 22:53:55 +00:00
2016-07-26 18:14:06 +00:00
# include "NTSC_CharSet.h"
2014-12-31 22:53:55 +00:00
2020-12-12 19:17:59 +00:00
// Some reference material here from 2000:
// http://www.kreativekorp.com/miscpages/a2info/munafo.shtml
//
2018-09-09 12:56:55 +00:00
2015-01-13 19:47:04 +00:00
# define NTSC_REMOVE_WHITE_RINGING 1 // 0 = theoritical dimmed white has chroma, 1 = pure white without chroma tinting
# define NTSC_REMOVE_BLACK_GHOSTING 1 // 1 = remove black smear/smudges carrying over
2016-10-15 05:28:39 +00:00
# define NTSC_REMOVE_GRAY_CHROMA 1 // 1 = remove all chroma in gray1 and gray2
2015-01-13 19:02:42 +00:00
2015-01-11 06:47:07 +00:00
# define DEBUG_PHASE_ZERO 0
2015-01-10 19:43:51 +00:00
2015-01-10 19:24:27 +00:00
# define ALT_TABLE 0
# if ALT_TABLE
# include "ntsc_rgb.h"
# endif
2014-12-31 22:53:55 +00:00
// Defines
2015-01-03 03:14:18 +00:00
# define HGR_TEST_PATTERN 0
2015-01-03 03:46:40 +00:00
2015-01-03 20:47:51 +00:00
# ifdef _MSC_VER
# define INLINE __forceinline
# else
# define INLINE inline
# endif
2014-12-31 22:53:55 +00:00
# define PI 3.1415926535898f
2015-01-03 03:46:40 +00:00
# define DEG_TO_RAD(x) (PI*(x) / 180.f) // 2PI=360, PI=180,PI/2=90,PI/4=45
2014-12-31 22:53:55 +00:00
# define RAD_45 PI*0.25f
# define RAD_90 PI*0.5f
# define RAD_360 PI*2.f
2015-01-03 04:58:27 +00:00
// sadly float64 precision is needed
# define real double
2014-12-31 22:53:55 +00:00
2015-01-13 19:02:42 +00:00
//#define CYCLESTART (PI/4.f) // PI/4 = 45 degrees
# define CYCLESTART (DEG_TO_RAD(45))
2014-12-31 22:53:55 +00:00
2015-01-02 17:40:36 +00:00
2014-12-31 22:53:55 +00:00
// Globals (Public) ___________________________________________________
2023-01-14 14:08:42 +00:00
static uint16_t g_nVideoClockVert = 0 ; // 9-bit: VC VB VA V5 V4 V3 V2 V1 V0 = 0 .. 262
static uint16_t g_nVideoClockHorz = 0 ; // 6-bit: H5 H4 H3 H2 H1 H0 = 0 .. 64, 25 >= visible (NB. final hpos is 2 cycles long, so a line is 65 cycles)
2014-12-31 22:53:55 +00:00
// Globals (Private) __________________________________________________
static int g_nVideoCharSet = 0 ;
static int g_nVideoMixed = 0 ;
static int g_nHiresPage = 1 ;
static int g_nTextPage = 1 ;
2019-11-18 15:08:59 +00:00
static bool g_bDelayVideoMode = false ; // NB. No need to save to save-state, as it will be done immediately after opcode completes in NTSC_VideoUpdateCycles()
2019-06-15 17:15:00 +00:00
static uint32_t g_uNewVideoModeFlags = 0 ;
2014-12-31 22:53:55 +00:00
// Understanding the Apple II, Timing Generation and the Video Scanner, Pg 3-11
// Vertical Scanning
// Horizontal Scanning
// "There are exactly 17030 (65 x 262) 6502 cycles in every television scan of an American Apple."
# define VIDEO_SCANNER_MAX_HORZ 65 // TODO: use Video.cpp: kHClocks
# define VIDEO_SCANNER_MAX_VERT 262 // TODO: use Video.cpp: kNTSCScanLines
2019-06-28 20:34:34 +00:00
static const UINT VIDEO_SCANNER_6502_CYCLES = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_MAX_VERT ;
# define VIDEO_SCANNER_MAX_VERT_PAL 312
static const UINT VIDEO_SCANNER_6502_CYCLES_PAL = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_MAX_VERT_PAL ;
static UINT g_videoScannerMaxVert = VIDEO_SCANNER_MAX_VERT ; // default to NTSC
static UINT g_videoScanner6502Cycles = VIDEO_SCANNER_6502_CYCLES ; // default to NTSC
2014-12-31 22:53:55 +00:00
2015-01-02 07:50:04 +00:00
# define VIDEO_SCANNER_HORZ_COLORBURST_BEG 12
# define VIDEO_SCANNER_HORZ_COLORBURST_END 16
2014-12-31 22:53:55 +00:00
# define VIDEO_SCANNER_HORZ_START 25 // first displayable horz scanner index
# define VIDEO_SCANNER_Y_MIXED 160 // num scanlins for mixed graphics + text
# define VIDEO_SCANNER_Y_DISPLAY 192 // max displayable scanlines
2021-11-30 21:41:02 +00:00
# define VIDEO_SCANNER_Y_DISPLAY_IIGS 200
2014-12-31 22:53:55 +00:00
2021-11-30 21:41:02 +00:00
// These 3 vars are initialized in NTSC_VideoInit()
2021-01-03 16:21:24 +00:00
static bgra_t * g_pVideoAddress = 0 ;
2021-11-30 21:41:02 +00:00
// To maintain the 280x192 aspect ratio for 560px width, we double every scan line -> 560x384
// NB. For IIgs SHR, the 320x200 is again doubled (to 640x400), but this gives a ~16:9 ratio, when 4:3 is probably required (ie. stretch height from 200 to 240)
static bgra_t * g_pScanLines [ VIDEO_SCANNER_Y_DISPLAY_IIGS * 2 ] ;
static UINT g_kFrameBufferWidth = 0 ;
2017-10-11 16:38:36 +00:00
2018-08-02 21:37:20 +00:00
static unsigned short ( * g_pHorzClockOffset ) [ VIDEO_SCANNER_MAX_HORZ ] = 0 ;
2015-01-03 01:56:16 +00:00
2015-01-03 03:46:40 +00:00
typedef void ( * UpdateScreenFunc_t ) ( long ) ;
2015-01-03 04:00:24 +00:00
static UpdateScreenFunc_t g_pFuncUpdateTextScreen = 0 ; // updateScreenText40;
static UpdateScreenFunc_t g_pFuncUpdateGraphicsScreen = 0 ; // updateScreenText40;
2015-01-03 11:31:19 +00:00
static UpdateScreenFunc_t g_pFuncModeSwitchDelayed = 0 ;
2015-01-03 03:46:40 +00:00
typedef void ( * UpdatePixelFunc_t ) ( uint16_t ) ;
2015-01-03 04:20:30 +00:00
static UpdatePixelFunc_t g_pFuncUpdateBnWPixel = 0 ; //updatePixelBnWMonitorSingleScanline;
static UpdatePixelFunc_t g_pFuncUpdateHuePixel = 0 ; //updatePixelHueMonitorSingleScanline;
2014-12-31 22:53:55 +00:00
2015-01-03 02:00:31 +00:00
static uint8_t g_nTextFlashCounter = 0 ;
2015-01-01 19:59:54 +00:00
static uint16_t g_nTextFlashMask = 0 ;
2014-12-31 22:53:55 +00:00
2015-01-02 19:46:57 +00:00
static unsigned g_aPixelMaskGR [ 16 ] ;
2014-12-31 22:53:55 +00:00
static uint16_t g_aPixelDoubleMaskHGR [ 128 ] ; // hgrbits -> g_aPixelDoubleMaskHGR: 7-bit mono 280 pixels to 560 pixel doubling
static int g_nLastColumnPixelNTSC ;
static int g_nColorBurstPixels ;
# define INITIAL_COLOR_PHASE 0
static int g_nColorPhaseNTSC = INITIAL_COLOR_PHASE ;
static int g_nSignalBitsNTSC = 0 ;
# define NTSC_NUM_PHASES 4
# define NTSC_NUM_SEQUENCES 4096
2015-01-08 20:05:23 +00:00
/*extern*/ uint32_t g_nChromaSize = 0 ; // for NTSC_VideoGetChromaTable()
static bgra_t g_aBnWMonitor [ NTSC_NUM_SEQUENCES ] ;
static bgra_t g_aHueMonitor [ NTSC_NUM_PHASES ] [ NTSC_NUM_SEQUENCES ] ;
static bgra_t g_aBnwColorTV [ NTSC_NUM_SEQUENCES ] ;
static bgra_t g_aHueColorTV [ NTSC_NUM_PHASES ] [ NTSC_NUM_SEQUENCES ] ;
2014-12-31 22:53:55 +00:00
2015-01-02 20:12:22 +00:00
// g_aBnWMonitor * g_nMonochromeRGB -> g_aBnWMonitorCustom
// g_aBnwColorTV * g_nMonochromeRGB -> g_aBnWColorTVCustom
static bgra_t g_aBnWMonitorCustom [ NTSC_NUM_SEQUENCES ] ;
static bgra_t g_aBnWColorTVCustom [ NTSC_NUM_SEQUENCES ] ;
2015-01-01 17:59:58 +00:00
2015-01-04 09:10:30 +00:00
# define CHROMA_ZEROS 2
# define CHROMA_POLES 2
# define CHROMA_GAIN 7.438011255f // Should this be 7.15909 MHz ?
# define CHROMA_0 -0.7318893645f
# define CHROMA_1 1.2336442711f
2014-12-31 22:53:55 +00:00
//#define LUMGAIN 1.062635655e+01
//#define LUMCOEF1 -0.3412038399
//#define LUMCOEF2 0.9647813115
2015-01-04 09:10:30 +00:00
# define LUMA_ZEROS 2
# define LUMA_POLES 2
# define LUMA_GAIN 13.71331570f // Should this be 14.318180 MHz ?
# define LUMA_0 -0.3961075449f
# define LUMA_1 1.1044202472f
# define SIGNAL_ZEROS 2
# define SIGNAL_POLES 2
# define SIGNAL_GAIN 7.614490548f // Should this be 7.15909 MHz ?
# define SIGNAL_0 -0.2718798058f
# define SIGNAL_1 0.7465656072f
2014-12-31 22:53:55 +00:00
// Tables
2018-08-05 21:19:51 +00:00
// Video scanner tables are now runtime-generated using UTAIIe logic
2019-06-28 20:34:34 +00:00
static unsigned short g_aClockVertOffsetsHGR [ VIDEO_SCANNER_MAX_VERT_PAL ] ;
static unsigned short g_aClockVertOffsetsTXT [ VIDEO_SCANNER_MAX_VERT_PAL / 8 ] ;
static unsigned short APPLE_IIP_HORZ_CLOCK_OFFSET [ 5 ] [ VIDEO_SCANNER_MAX_HORZ ] ; // 5 = CEILING(312/64) = CEILING(262/64)
2018-08-05 21:19:51 +00:00
static unsigned short APPLE_IIE_HORZ_CLOCK_OFFSET [ 5 ] [ VIDEO_SCANNER_MAX_HORZ ] ;
2018-08-02 21:37:20 +00:00
2018-08-05 21:19:51 +00:00
# ifdef _DEBUG
2018-08-02 21:37:20 +00:00
static unsigned short g_kClockVertOffsetsHGR [ VIDEO_SCANNER_MAX_VERT ] =
2014-12-31 22:53:55 +00:00
{
0x0000 , 0x0400 , 0x0800 , 0x0C00 , 0x1000 , 0x1400 , 0x1800 , 0x1C00 , 0x0080 , 0x0480 , 0x0880 , 0x0C80 , 0x1080 , 0x1480 , 0x1880 , 0x1C80 ,
0x0100 , 0x0500 , 0x0900 , 0x0D00 , 0x1100 , 0x1500 , 0x1900 , 0x1D00 , 0x0180 , 0x0580 , 0x0980 , 0x0D80 , 0x1180 , 0x1580 , 0x1980 , 0x1D80 ,
0x0200 , 0x0600 , 0x0A00 , 0x0E00 , 0x1200 , 0x1600 , 0x1A00 , 0x1E00 , 0x0280 , 0x0680 , 0x0A80 , 0x0E80 , 0x1280 , 0x1680 , 0x1A80 , 0x1E80 ,
0x0300 , 0x0700 , 0x0B00 , 0x0F00 , 0x1300 , 0x1700 , 0x1B00 , 0x1F00 , 0x0380 , 0x0780 , 0x0B80 , 0x0F80 , 0x1380 , 0x1780 , 0x1B80 , 0x1F80 ,
0x0000 , 0x0400 , 0x0800 , 0x0C00 , 0x1000 , 0x1400 , 0x1800 , 0x1C00 , 0x0080 , 0x0480 , 0x0880 , 0x0C80 , 0x1080 , 0x1480 , 0x1880 , 0x1C80 ,
0x0100 , 0x0500 , 0x0900 , 0x0D00 , 0x1100 , 0x1500 , 0x1900 , 0x1D00 , 0x0180 , 0x0580 , 0x0980 , 0x0D80 , 0x1180 , 0x1580 , 0x1980 , 0x1D80 ,
0x0200 , 0x0600 , 0x0A00 , 0x0E00 , 0x1200 , 0x1600 , 0x1A00 , 0x1E00 , 0x0280 , 0x0680 , 0x0A80 , 0x0E80 , 0x1280 , 0x1680 , 0x1A80 , 0x1E80 ,
0x0300 , 0x0700 , 0x0B00 , 0x0F00 , 0x1300 , 0x1700 , 0x1B00 , 0x1F00 , 0x0380 , 0x0780 , 0x0B80 , 0x0F80 , 0x1380 , 0x1780 , 0x1B80 , 0x1F80 ,
0x0000 , 0x0400 , 0x0800 , 0x0C00 , 0x1000 , 0x1400 , 0x1800 , 0x1C00 , 0x0080 , 0x0480 , 0x0880 , 0x0C80 , 0x1080 , 0x1480 , 0x1880 , 0x1C80 ,
0x0100 , 0x0500 , 0x0900 , 0x0D00 , 0x1100 , 0x1500 , 0x1900 , 0x1D00 , 0x0180 , 0x0580 , 0x0980 , 0x0D80 , 0x1180 , 0x1580 , 0x1980 , 0x1D80 ,
0x0200 , 0x0600 , 0x0A00 , 0x0E00 , 0x1200 , 0x1600 , 0x1A00 , 0x1E00 , 0x0280 , 0x0680 , 0x0A80 , 0x0E80 , 0x1280 , 0x1680 , 0x1A80 , 0x1E80 ,
0x0300 , 0x0700 , 0x0B00 , 0x0F00 , 0x1300 , 0x1700 , 0x1B00 , 0x1F00 , 0x0380 , 0x0780 , 0x0B80 , 0x0F80 , 0x1380 , 0x1780 , 0x1B80 , 0x1F80 ,
0x0000 , 0x0400 , 0x0800 , 0x0C00 , 0x1000 , 0x1400 , 0x1800 , 0x1C00 , 0x0080 , 0x0480 , 0x0880 , 0x0C80 , 0x1080 , 0x1480 , 0x1880 , 0x1C80 ,
0x0100 , 0x0500 , 0x0900 , 0x0D00 , 0x1100 , 0x1500 , 0x1900 , 0x1D00 , 0x0180 , 0x0580 , 0x0980 , 0x0D80 , 0x1180 , 0x1580 , 0x1980 , 0x1D80 ,
0x0200 , 0x0600 , 0x0A00 , 0x0E00 , 0x1200 , 0x1600 , 0x1A00 , 0x1E00 , 0x0280 , 0x0680 , 0x0A80 , 0x0E80 , 0x1280 , 0x1680 , 0x1A80 , 0x1E80 ,
0x0300 , 0x0700 , 0x0B00 , 0x0F00 , 0x1300 , 0x1700 , 0x1B00 , 0x1F00 , 0x0380 , 0x0780 , 0x0B80 , 0x0F80 , 0x1380 , 0x1780 , 0x1B80 , 0x1F80 ,
0x0B80 , 0x0F80 , 0x1380 , 0x1780 , 0x1B80 , 0x1F80
} ;
2019-06-28 20:34:34 +00:00
static unsigned short g_kClockVertOffsetsTXT [ 33 ] = // 33 = CEILING(262/8)
2014-12-31 22:53:55 +00:00
{
0x0000 , 0x0080 , 0x0100 , 0x0180 , 0x0200 , 0x0280 , 0x0300 , 0x0380 ,
0x0000 , 0x0080 , 0x0100 , 0x0180 , 0x0200 , 0x0280 , 0x0300 , 0x0380 ,
0x0000 , 0x0080 , 0x0100 , 0x0180 , 0x0200 , 0x0280 , 0x0300 , 0x0380 ,
2015-01-03 02:38:45 +00:00
0x0000 , 0x0080 , 0x0100 , 0x0180 , 0x0200 , 0x0280 , 0x0300 , 0x0380 ,
0x380
2014-12-31 22:53:55 +00:00
} ;
2018-08-02 21:37:20 +00:00
static unsigned short kAPPLE_IIP_HORZ_CLOCK_OFFSET [ 5 ] [ VIDEO_SCANNER_MAX_HORZ ] =
2014-12-31 22:53:55 +00:00
{
{ 0x1068 , 0x1068 , 0x1069 , 0x106A , 0x106B , 0x106C , 0x106D , 0x106E , 0x106F ,
0x1070 , 0x1071 , 0x1072 , 0x1073 , 0x1074 , 0x1075 , 0x1076 , 0x1077 ,
0x1078 , 0x1079 , 0x107A , 0x107B , 0x107C , 0x107D , 0x107E , 0x107F ,
0x0000 , 0x0001 , 0x0002 , 0x0003 , 0x0004 , 0x0005 , 0x0006 , 0x0007 ,
0x0008 , 0x0009 , 0x000A , 0x000B , 0x000C , 0x000D , 0x000E , 0x000F ,
0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F ,
0x0020 , 0x0021 , 0x0022 , 0x0023 , 0x0024 , 0x0025 , 0x0026 , 0x0027 } ,
{ 0x1010 , 0x1010 , 0x1011 , 0x1012 , 0x1013 , 0x1014 , 0x1015 , 0x1016 , 0x1017 ,
0x1018 , 0x1019 , 0x101A , 0x101B , 0x101C , 0x101D , 0x101E , 0x101F ,
0x1020 , 0x1021 , 0x1022 , 0x1023 , 0x1024 , 0x1025 , 0x1026 , 0x1027 ,
0x0028 , 0x0029 , 0x002A , 0x002B , 0x002C , 0x002D , 0x002E , 0x002F ,
0x0030 , 0x0031 , 0x0032 , 0x0033 , 0x0034 , 0x0035 , 0x0036 , 0x0037 ,
0x0038 , 0x0039 , 0x003A , 0x003B , 0x003C , 0x003D , 0x003E , 0x003F ,
0x0040 , 0x0041 , 0x0042 , 0x0043 , 0x0044 , 0x0045 , 0x0046 , 0x0047 ,
0x0048 , 0x0049 , 0x004A , 0x004B , 0x004C , 0x004D , 0x004E , 0x004F } ,
{ 0x1038 , 0x1038 , 0x1039 , 0x103A , 0x103B , 0x103C , 0x103D , 0x103E , 0x103F ,
0x1040 , 0x1041 , 0x1042 , 0x1043 , 0x1044 , 0x1045 , 0x1046 , 0x1047 ,
0x1048 , 0x1049 , 0x104A , 0x104B , 0x104C , 0x104D , 0x104E , 0x104F ,
0x0050 , 0x0051 , 0x0052 , 0x0053 , 0x0054 , 0x0055 , 0x0056 , 0x0057 ,
0x0058 , 0x0059 , 0x005A , 0x005B , 0x005C , 0x005D , 0x005E , 0x005F ,
0x0060 , 0x0061 , 0x0062 , 0x0063 , 0x0064 , 0x0065 , 0x0066 , 0x0067 ,
0x0068 , 0x0069 , 0x006A , 0x006B , 0x006C , 0x006D , 0x006E , 0x006F ,
0x0070 , 0x0071 , 0x0072 , 0x0073 , 0x0074 , 0x0075 , 0x0076 , 0x0077 } ,
{ 0x1060 , 0x1060 , 0x1061 , 0x1062 , 0x1063 , 0x1064 , 0x1065 , 0x1066 , 0x1067 ,
0x1068 , 0x1069 , 0x106A , 0x106B , 0x106C , 0x106D , 0x106E , 0x106F ,
0x1070 , 0x1071 , 0x1072 , 0x1073 , 0x1074 , 0x1075 , 0x1076 , 0x1077 ,
0x0078 , 0x0079 , 0x007A , 0x007B , 0x007C , 0x007D , 0x007E , 0x007F ,
0x0000 , 0x0001 , 0x0002 , 0x0003 , 0x0004 , 0x0005 , 0x0006 , 0x0007 ,
0x0008 , 0x0009 , 0x000A , 0x000B , 0x000C , 0x000D , 0x000E , 0x000F ,
0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F } ,
{ 0x1060 , 0x1060 , 0x1061 , 0x1062 , 0x1063 , 0x1064 , 0x1065 , 0x1066 , 0x1067 ,
0x1068 , 0x1069 , 0x106A , 0x106B , 0x106C , 0x106D , 0x106E , 0x106F ,
0x1070 , 0x1071 , 0x1072 , 0x1073 , 0x1074 , 0x1075 , 0x1076 , 0x1077 ,
0x0078 , 0x0079 , 0x007A , 0x007B , 0x007C , 0x007D , 0x007E , 0x007F ,
0x0000 , 0x0001 , 0x0002 , 0x0003 , 0x0004 , 0x0005 , 0x0006 , 0x0007 ,
0x0008 , 0x0009 , 0x000A , 0x000B , 0x000C , 0x000D , 0x000E , 0x000F ,
0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F }
} ;
2018-08-02 21:37:20 +00:00
static unsigned short kAPPLE_IIE_HORZ_CLOCK_OFFSET [ 5 ] [ VIDEO_SCANNER_MAX_HORZ ] =
2014-12-31 22:53:55 +00:00
{
2016-12-11 15:02:12 +00:00
{ 0x0068 , 0x0068 , 0x0069 , 0x006A , 0x006B , 0x006C , 0x006D , 0x006E , 0x006F ,
2014-12-31 22:53:55 +00:00
0x0070 , 0x0071 , 0x0072 , 0x0073 , 0x0074 , 0x0075 , 0x0076 , 0x0077 ,
0x0078 , 0x0079 , 0x007A , 0x007B , 0x007C , 0x007D , 0x007E , 0x007F ,
0x0000 , 0x0001 , 0x0002 , 0x0003 , 0x0004 , 0x0005 , 0x0006 , 0x0007 ,
0x0008 , 0x0009 , 0x000A , 0x000B , 0x000C , 0x000D , 0x000E , 0x000F ,
0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F ,
0x0020 , 0x0021 , 0x0022 , 0x0023 , 0x0024 , 0x0025 , 0x0026 , 0x0027 } ,
{ 0x0010 , 0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F ,
0x0020 , 0x0021 , 0x0022 , 0x0023 , 0x0024 , 0x0025 , 0x0026 , 0x0027 ,
0x0028 , 0x0029 , 0x002A , 0x002B , 0x002C , 0x002D , 0x002E , 0x002F ,
0x0030 , 0x0031 , 0x0032 , 0x0033 , 0x0034 , 0x0035 , 0x0036 , 0x0037 ,
0x0038 , 0x0039 , 0x003A , 0x003B , 0x003C , 0x003D , 0x003E , 0x003F ,
0x0040 , 0x0041 , 0x0042 , 0x0043 , 0x0044 , 0x0045 , 0x0046 , 0x0047 ,
0x0048 , 0x0049 , 0x004A , 0x004B , 0x004C , 0x004D , 0x004E , 0x004F } ,
{ 0x0038 , 0x0038 , 0x0039 , 0x003A , 0x003B , 0x003C , 0x003D , 0x003E , 0x003F ,
0x0040 , 0x0041 , 0x0042 , 0x0043 , 0x0044 , 0x0045 , 0x0046 , 0x0047 ,
0x0048 , 0x0049 , 0x004A , 0x004B , 0x004C , 0x004D , 0x004E , 0x004F ,
0x0050 , 0x0051 , 0x0052 , 0x0053 , 0x0054 , 0x0055 , 0x0056 , 0x0057 ,
0x0058 , 0x0059 , 0x005A , 0x005B , 0x005C , 0x005D , 0x005E , 0x005F ,
0x0060 , 0x0061 , 0x0062 , 0x0063 , 0x0064 , 0x0065 , 0x0066 , 0x0067 ,
0x0068 , 0x0069 , 0x006A , 0x006B , 0x006C , 0x006D , 0x006E , 0x006F ,
0x0070 , 0x0071 , 0x0072 , 0x0073 , 0x0074 , 0x0075 , 0x0076 , 0x0077 } ,
{ 0x0060 , 0x0060 , 0x0061 , 0x0062 , 0x0063 , 0x0064 , 0x0065 , 0x0066 , 0x0067 ,
0x0068 , 0x0069 , 0x006A , 0x006B , 0x006C , 0x006D , 0x006E , 0x006F ,
0x0070 , 0x0071 , 0x0072 , 0x0073 , 0x0074 , 0x0075 , 0x0076 , 0x0077 ,
0x0078 , 0x0079 , 0x007A , 0x007B , 0x007C , 0x007D , 0x007E , 0x007F ,
0x0000 , 0x0001 , 0x0002 , 0x0003 , 0x0004 , 0x0005 , 0x0006 , 0x0007 ,
0x0008 , 0x0009 , 0x000A , 0x000B , 0x000C , 0x000D , 0x000E , 0x000F ,
0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F } ,
{ 0x0060 , 0x0060 , 0x0061 , 0x0062 , 0x0063 , 0x0064 , 0x0065 , 0x0066 , 0x0067 ,
0x0068 , 0x0069 , 0x006A , 0x006B , 0x006C , 0x006D , 0x006E , 0x006F ,
0x0070 , 0x0071 , 0x0072 , 0x0073 , 0x0074 , 0x0075 , 0x0076 , 0x0077 ,
0x0078 , 0x0079 , 0x007A , 0x007B , 0x007C , 0x007D , 0x007E , 0x007F ,
0x0000 , 0x0001 , 0x0002 , 0x0003 , 0x0004 , 0x0005 , 0x0006 , 0x0007 ,
0x0008 , 0x0009 , 0x000A , 0x000B , 0x000C , 0x000D , 0x000E , 0x000F ,
0x0010 , 0x0011 , 0x0012 , 0x0013 , 0x0014 , 0x0015 , 0x0016 , 0x0017 ,
0x0018 , 0x0019 , 0x001A , 0x001B , 0x001C , 0x001D , 0x001E , 0x001F }
} ;
2018-08-05 21:19:51 +00:00
# endif
2014-12-31 22:53:55 +00:00
2016-04-12 22:21:05 +00:00
static csbits_t csbits ; // charset, optionally followed by alt charset
2015-01-03 01:40:30 +00:00
2014-12-31 22:53:55 +00:00
// Prototypes
2019-09-01 15:47:48 +00:00
INLINE void updateFramebufferTVSingleScanline ( uint16_t signal , bgra_t * pTable ) ;
INLINE void updateFramebufferTVDoubleScanline ( uint16_t signal , bgra_t * pTable ) ;
2015-01-03 20:47:51 +00:00
INLINE void updateFramebufferMonitorSingleScanline ( uint16_t signal , bgra_t * pTable ) ;
INLINE void updateFramebufferMonitorDoubleScanline ( uint16_t signal , bgra_t * pTable ) ;
INLINE void updatePixels ( uint16_t bits ) ;
INLINE void updateVideoScannerHorzEOL ( ) ;
INLINE void updateVideoScannerAddress ( ) ;
2015-01-03 04:00:24 +00:00
2015-01-03 04:58:27 +00:00
static void initChromaPhaseTables ( ) ;
static real initFilterChroma ( real z ) ;
static real initFilterLuma0 ( real z ) ;
static real initFilterLuma1 ( real z ) ;
static real initFilterSignal ( real z ) ;
static void initPixelDoubleMasks ( void ) ;
2015-01-03 04:20:30 +00:00
static void updateMonochromeTables ( uint16_t r , uint16_t g , uint16_t b ) ;
2015-01-03 04:00:24 +00:00
2015-01-03 04:20:30 +00:00
static void updatePixelBnWColorTVSingleScanline ( uint16_t compositeSignal ) ;
static void updatePixelBnWColorTVDoubleScanline ( uint16_t compositeSignal ) ;
static void updatePixelBnWMonitorSingleScanline ( uint16_t compositeSignal ) ;
static void updatePixelBnWMonitorDoubleScanline ( uint16_t compositeSignal ) ;
static void updatePixelHueColorTVSingleScanline ( uint16_t compositeSignal ) ;
static void updatePixelHueColorTVDoubleScanline ( uint16_t compositeSignal ) ;
static void updatePixelHueMonitorSingleScanline ( uint16_t compositeSignal ) ;
static void updatePixelHueMonitorDoubleScanline ( uint16_t compositeSignal ) ;
static void updateScreenDoubleHires40 ( long cycles6502 ) ;
static void updateScreenDoubleHires80 ( long cycles6502 ) ;
static void updateScreenDoubleLores40 ( long cycles6502 ) ;
static void updateScreenDoubleLores80 ( long cycles6502 ) ;
static void updateScreenSingleHires40 ( long cycles6502 ) ;
static void updateScreenSingleLores40 ( long cycles6502 ) ;
static void updateScreenText40 ( long cycles6502 ) ;
static void updateScreenText80 ( long cycles6502 ) ;
2020-09-01 20:32:44 +00:00
static void updateScreenText40RGB ( long cycles6502 ) ;
static void updateScreenText80RGB ( long cycles6502 ) ;
2020-09-27 13:09:02 +00:00
static void updateScreenDoubleHires80Simplified ( long cycles6502 ) ;
static void updateScreenDoubleHires80RGB ( long cycles6502 ) ;
2021-11-30 21:41:02 +00:00
static void updateScreenSHR ( long cycles6502 ) ;
2015-01-03 04:00:24 +00:00
2016-04-12 22:21:05 +00:00
//===========================================================================
static void set_csbits ( )
{
// NB. For models that don't have an alt charset then set /g_nVideoCharSet/ to zero
switch ( GetApple2Type ( ) )
{
case A2TYPE_APPLE2 : csbits = & csbits_a2 [ 0 ] ; g_nVideoCharSet = 0 ; break ;
case A2TYPE_APPLE2PLUS : csbits = & csbits_a2 [ 0 ] ; g_nVideoCharSet = 0 ; break ;
2020-04-02 19:17:32 +00:00
case A2TYPE_APPLE2JPLUS : csbits = & csbits_a2j [ MemGetAnnunciator ( 2 ) ? 1 : 0 ] ; g_nVideoCharSet = 0 ; break ;
2019-01-10 21:38:21 +00:00
case A2TYPE_APPLE2E : csbits = Get2e_csbits ( ) ; break ;
case A2TYPE_APPLE2EENHANCED : csbits = Get2e_csbits ( ) ; break ;
2016-04-12 22:21:05 +00:00
case A2TYPE_PRAVETS82 : csbits = & csbits_pravets82 [ 0 ] ; g_nVideoCharSet = 0 ; break ; // Apple ][ clone
case A2TYPE_PRAVETS8M : csbits = & csbits_pravets8M [ 0 ] ; g_nVideoCharSet = 0 ; break ; // Apple ][ clone
case A2TYPE_PRAVETS8A : csbits = & csbits_pravets8C [ 0 ] ; break ; // Apple //e clone
2016-10-31 21:05:10 +00:00
case A2TYPE_TK30002E : csbits = & csbits_enhanced2e [ 0 ] ; break ; // Enhanced Apple //e clone
2020-12-28 16:25:29 +00:00
case A2TYPE_BASE64A : csbits = & csbits_base64a [ GetVideo ( ) . GetVideoRomRockerSwitch ( ) ? 0 : 1 ] ; g_nVideoCharSet = 0 ; break ; // Apple ][ clone
2020-04-02 19:17:32 +00:00
default : _ASSERT ( 0 ) ; csbits = & csbits_enhanced2e [ 0 ] ; break ;
2016-04-12 22:21:05 +00:00
}
}
2014-12-31 22:53:55 +00:00
//===========================================================================
inline float clampZeroOne ( const float & x )
{
if ( x < 0.f ) return 0.f ;
if ( x > 1.f ) return 1.f ;
/* ...... */ return x ;
}
2015-01-02 06:04:38 +00:00
//===========================================================================
inline uint8_t getCharSetBits ( int iChar )
{
2015-01-02 07:50:04 +00:00
return csbits [ g_nVideoCharSet ] [ iChar ] [ g_nVideoClockVert & 7 ] ;
2015-01-02 06:04:38 +00:00
}
//===========================================================================
inline uint16_t getLoResBits ( uint8_t iByte )
{
return g_aPixelMaskGR [ ( iByte > > ( g_nVideoClockVert & 4 ) ) & 0xF ] ;
}
2014-12-31 22:53:55 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
inline uint32_t getScanlineColor ( const uint16_t signal , const bgra_t * pTable )
2014-12-31 22:53:55 +00:00
{
2018-05-13 16:33:13 +00:00
g_nSignalBitsNTSC = ( ( g_nSignalBitsNTSC < < 1 ) | signal ) & 0xFFF ; // 12-bit
2015-01-03 04:58:27 +00:00
return * ( uint32_t * ) & pTable [ g_nSignalBitsNTSC ] ;
2014-12-31 22:53:55 +00:00
}
//===========================================================================
2019-08-30 15:43:28 +00:00
inline uint32_t * getScanlineNextInbetween ( )
2014-12-31 22:53:55 +00:00
{
2017-10-11 16:38:36 +00:00
return ( uint32_t * ) ( g_pVideoAddress - 1 * g_kFrameBufferWidth ) ;
2015-01-03 04:58:27 +00:00
}
2014-12-31 22:53:55 +00:00
2019-09-01 15:38:27 +00:00
#if 0 // don't use this pixel, as it's from the previous video-frame!
2019-08-30 20:58:03 +00:00
inline uint32_t * getScanlineNext ( )
{
return ( uint32_t * ) ( g_pVideoAddress - 2 * g_kFrameBufferWidth ) ;
}
2019-09-01 15:38:27 +00:00
# endif
2015-01-03 04:58:27 +00:00
//===========================================================================
2019-08-30 15:43:28 +00:00
inline uint32_t * getScanlinePreviousInbetween ( )
2015-01-03 04:58:27 +00:00
{
2017-10-11 16:38:36 +00:00
return ( uint32_t * ) ( g_pVideoAddress + 1 * g_kFrameBufferWidth ) ;
2015-01-03 04:58:27 +00:00
}
2015-01-03 03:08:30 +00:00
2019-08-30 15:43:28 +00:00
inline uint32_t * getScanlinePrevious ( )
2015-01-03 04:58:27 +00:00
{
2017-10-11 16:38:36 +00:00
return ( uint32_t * ) ( g_pVideoAddress + 2 * g_kFrameBufferWidth ) ;
2015-01-03 04:58:27 +00:00
}
//===========================================================================
2019-08-30 15:43:28 +00:00
inline uint32_t * getScanlineCurrent ( )
2015-01-03 04:58:27 +00:00
{
return ( uint32_t * ) g_pVideoAddress ;
}
//===========================================================================
inline void updateColorPhase ( )
{
g_nColorPhaseNTSC + + ;
g_nColorPhaseNTSC & = 3 ;
2015-01-02 06:04:38 +00:00
}
2015-01-03 19:45:44 +00:00
//===========================================================================
inline void updateFlashRate ( ) // TODO: Flash rate should be constant (regardless of CPU speed)
{
// BUG: In unthrottled CPU mode, flash rate should not be affected
// Flash rate:
// . NTSC : 60/16 ~= 4Hz
// . PAL : 50/16 ~= 3Hz
if ( ( + + g_nTextFlashCounter & 0xF ) = = 0 )
g_nTextFlashMask ^ = - 1 ; // 16-bits
// The old way to handle flashing was
// if ((SW_TEXT || SW_MIXED) ) // && !SW_80COL) // FIX: FLASH 80-Column
// g_nTextFlashMask = true;
// The new way is to check the active char set, inlined:
// if (0 == g_nVideoCharSet && 0x40 == (m & 0xC0)) // Flash only if mousetext not active
}
2015-01-02 19:46:57 +00:00
#if 0
# define updateFramebufferMonitorSingleScanline(signal,table) \
2015-01-02 06:04:38 +00:00
do { \
2015-01-02 19:46:57 +00:00
uint32_t * cp , * mp ; \
2015-01-02 06:04:38 +00:00
g_nSignalBitsNTSC = ( ( g_nSignalBitsNTSC < < 1 ) | signal ) & 0xFFF ; \
2015-01-02 19:46:57 +00:00
cp = ( uint32_t * ) & table [ g_nSignalBitsNTSC ] ; \
* ( uint32_t * ) g_pVideoAddress = * cp ; \
mp = ( uint32_t * ) ( g_pVideoAddress - FRAMEBUFFER_W ) ; \
* mp = ( ( * cp & 0x00fcfcfc ) > > 2 ) | ALPHA32_MASK ; \
g_pVideoAddress + + ; \
2015-01-02 06:04:38 +00:00
} while ( 0 )
2015-01-04 09:10:30 +00:00
// prevp is never used nor blended with!
2019-09-01 15:47:48 +00:00
# define updateFramebufferTVSingleScanline(signal,table) \
2015-01-02 06:04:38 +00:00
do { \
2019-08-30 20:58:03 +00:00
uint32_t ntscp , /*prevp,*/ betwp ; \
2015-01-02 19:46:57 +00:00
uint32_t * prevlin , * between ; \
2015-01-02 06:04:38 +00:00
g_nSignalBitsNTSC = ( ( g_nSignalBitsNTSC < < 1 ) | signal ) & 0xFFF ; \
2019-08-30 20:58:03 +00:00
/*prevlin = (uint32_t*)(g_pVideoAddress + 2*FRAMEBUFFER_W);*/ \
2015-01-02 19:46:57 +00:00
between = ( uint32_t * ) ( g_pVideoAddress + 1 * FRAMEBUFFER_W ) ; \
ntscp = * ( uint32_t * ) & table [ g_nSignalBitsNTSC ] ; /* raw current NTSC color */ \
2019-08-30 20:58:03 +00:00
/*prevp = *prevlin;*/ \
2015-01-02 06:04:38 +00:00
betwp = ntscp - ( ( ntscp & 0x00fcfcfc ) > > 2 ) ; \
2015-01-02 19:46:57 +00:00
* between = betwp | ALPHA32_MASK ; \
* ( uint32_t * ) g_pVideoAddress = ntscp ; \
g_pVideoAddress + + ; \
2015-01-02 06:04:38 +00:00
} while ( 0 )
2015-01-02 19:46:57 +00:00
# define updateFramebufferMonitorDoubleScanline(signal,table) \
2015-01-02 06:04:38 +00:00
do { \
2015-01-02 19:46:57 +00:00
uint32_t * cp , * mp ; \
2015-01-02 06:04:38 +00:00
g_nSignalBitsNTSC = ( ( g_nSignalBitsNTSC < < 1 ) | signal ) & 0xFFF ; \
2015-01-02 19:46:57 +00:00
cp = ( uint32_t * ) & table [ g_nSignalBitsNTSC ] ; \
mp = ( uint32_t * ) ( g_pVideoAddress - FRAMEBUFFER_W ) ; \
* ( uint32_t * ) g_pVideoAddress = * mp = * cp ; \
g_pVideoAddress + + ; \
2015-01-02 06:04:38 +00:00
} while ( 0 )
2019-09-01 15:47:48 +00:00
# define updateFramebufferTVDoubleScanline(signal,table) \
2015-01-02 06:04:38 +00:00
do { \
2015-01-02 19:46:57 +00:00
uint32_t ntscp , prevp , betwp ; \
uint32_t * prevlin , * between ; \
2015-01-02 06:04:38 +00:00
g_nSignalBitsNTSC = ( ( g_nSignalBitsNTSC < < 1 ) | signal ) & 0xFFF ; \
2015-01-02 19:46:57 +00:00
prevlin = ( uint32_t * ) ( g_pVideoAddress + 2 * FRAMEBUFFER_W ) ; \
between = ( uint32_t * ) ( g_pVideoAddress + 1 * FRAMEBUFFER_W ) ; \
ntscp = * ( uint32_t * ) & table [ g_nSignalBitsNTSC ] ; /* raw current NTSC color */ \
2015-01-02 06:04:38 +00:00
prevp = * prevlin ; \
betwp = ( ( ntscp & 0x00fefefe ) > > 1 ) + ( ( prevp & 0x00fefefe ) > > 1 ) ; \
2015-01-02 19:46:57 +00:00
* between = betwp | ALPHA32_MASK ; \
* ( uint32_t * ) g_pVideoAddress = ntscp ; \
g_pVideoAddress + + ; \
2015-01-02 06:04:38 +00:00
} while ( 0 )
2015-01-02 19:46:57 +00:00
# else
2015-01-03 04:58:27 +00:00
//===========================================================================
2019-08-31 14:02:32 +00:00
// Original: Prev1(inbetween) = current - 25% of previous AppleII scanline
2019-09-01 15:38:27 +00:00
// GH#650: Prev1(inbetween) = 50% of (50% current + 50% of previous AppleII scanline)
2019-09-01 15:47:48 +00:00
inline void updateFramebufferTVSingleScanline ( uint16_t signal , bgra_t * pTable )
2015-01-02 19:46:57 +00:00
{
2019-08-30 15:43:28 +00:00
uint32_t * pLine0Curr = getScanlineCurrent ( ) ;
2019-09-01 15:38:27 +00:00
uint32_t * pLine1Prev = getScanlinePreviousInbetween ( ) ;
uint32_t * pLine2Prev = getScanlinePrevious ( ) ;
2019-08-30 20:58:03 +00:00
const uint32_t color0 = getScanlineColor ( signal , pTable ) ;
2019-09-01 15:38:27 +00:00
const uint32_t color2 = * pLine2Prev ;
2019-08-30 20:58:03 +00:00
uint32_t color1 = ( ( color0 & 0x00fefefe ) > > 1 ) + ( ( color2 & 0x00fefefe ) > > 1 ) ; // 50% Blend
color1 = ( color1 & 0x00fefefe ) > > 1 ; // ... then 50% brightness for inbetween line
2019-09-01 15:38:27 +00:00
* pLine1Prev = color1 | ALPHA32_MASK ;
2019-08-30 20:58:03 +00:00
* pLine0Curr = color0 ;
2019-09-01 15:38:27 +00:00
// GH#650: Draw to final inbetween scanline to avoid residue from other video modes (eg. Amber->TV B&W)
if ( g_nVideoClockVert = = ( VIDEO_SCANNER_Y_DISPLAY - 1 ) )
* getScanlineNextInbetween ( ) = ( ( color0 & 0x00fcfcfc ) > > 2 ) | ALPHA32_MASK ; // 50% of (50% current + black)) = 25% of current
2019-08-30 15:43:28 +00:00
g_pVideoAddress + + ;
2015-01-02 19:46:57 +00:00
}
2015-01-03 04:58:27 +00:00
//===========================================================================
2019-08-31 14:02:32 +00:00
2019-09-01 16:35:15 +00:00
// Original: Prev1(inbetween) = 50% current + 50% of previous AppleII scanline
2019-09-01 15:47:48 +00:00
inline void updateFramebufferTVDoubleScanline ( uint16_t signal , bgra_t * pTable )
2015-01-02 19:46:57 +00:00
{
2019-08-30 15:43:28 +00:00
uint32_t * pLine0Curr = getScanlineCurrent ( ) ;
2019-09-01 15:38:27 +00:00
uint32_t * pLine1Prev = getScanlinePreviousInbetween ( ) ;
uint32_t * pLine2Prev = getScanlinePrevious ( ) ;
2019-08-30 20:58:03 +00:00
const uint32_t color0 = getScanlineColor ( signal , pTable ) ;
2019-09-01 15:38:27 +00:00
const uint32_t color2 = * pLine2Prev ;
2019-08-30 20:58:03 +00:00
const uint32_t color1 = ( ( color0 & 0x00fefefe ) > > 1 ) + ( ( color2 & 0x00fefefe ) > > 1 ) ; // 50% Blend
2019-09-01 15:38:27 +00:00
* pLine1Prev = color1 | ALPHA32_MASK ;
2019-08-30 20:58:03 +00:00
* pLine0Curr = color0 ;
2019-09-01 15:38:27 +00:00
// GH#650: Draw to final inbetween scanline to avoid residue from other video modes (eg. Amber->TV B&W)
if ( g_nVideoClockVert = = ( VIDEO_SCANNER_Y_DISPLAY - 1 ) )
* getScanlineNextInbetween ( ) = ( ( color0 & 0x00fefefe ) > > 1 ) | ALPHA32_MASK ; // (50% current + black)) = 50% of current
2019-08-30 15:43:28 +00:00
g_pVideoAddress + + ;
2015-01-02 19:46:57 +00:00
}
2015-01-03 04:58:27 +00:00
//===========================================================================
inline void updateFramebufferMonitorSingleScanline ( uint16_t signal , bgra_t * pTable )
2015-01-02 19:46:57 +00:00
{
2019-08-30 15:43:28 +00:00
uint32_t * pLine0Curr = getScanlineCurrent ( ) ;
2019-08-31 14:02:32 +00:00
uint32_t * pLine1Next = getScanlineNextInbetween ( ) ;
2015-01-02 19:46:57 +00:00
const uint32_t color0 = getScanlineColor ( signal , pTable ) ;
2019-04-06 12:44:52 +00:00
const uint32_t color1 = 0 ; // Remove blending for consistent DHGR MIX mode (GH#631)
// const uint32_t color1 = ((color0 & 0x00fcfcfc) >> 2); // 25% Blend (original)
2015-01-02 19:46:57 +00:00
2019-08-30 15:43:28 +00:00
* pLine1Next = color1 | ALPHA32_MASK ;
* pLine0Curr = color0 ;
g_pVideoAddress + + ;
2015-01-02 19:46:57 +00:00
}
2015-01-03 04:58:27 +00:00
//===========================================================================
inline void updateFramebufferMonitorDoubleScanline ( uint16_t signal , bgra_t * pTable )
2015-01-02 19:46:57 +00:00
{
2019-08-30 15:43:28 +00:00
uint32_t * pLine0Curr = getScanlineCurrent ( ) ;
2019-08-31 14:02:32 +00:00
uint32_t * pLine1Next = getScanlineNextInbetween ( ) ;
2015-01-02 19:46:57 +00:00
const uint32_t color0 = getScanlineColor ( signal , pTable ) ;
2019-08-30 15:43:28 +00:00
* pLine1Next = color0 ;
* pLine0Curr = color0 ;
g_pVideoAddress + + ;
2015-01-02 19:46:57 +00:00
}
# endif
2015-01-02 06:04:38 +00:00
2018-09-09 12:56:55 +00:00
//===========================================================================
inline bool GetColorBurst ( void )
{
return g_nColorBurstPixels > = 2 ;
}
2015-01-02 06:04:38 +00:00
//===========================================================================
2018-05-24 20:34:26 +00:00
2019-02-02 15:51:27 +00:00
void update7MonoPixels ( uint16_t bits )
{
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ;
}
//===========================================================================
2018-05-24 20:34:26 +00:00
// NB. g_nLastColumnPixelNTSC = bits.b13 will be superseded by these parent funcs which use bits.b14:
// . updateScreenDoubleHires80(), updateScreenDoubleLores80(), updateScreenText80()
2020-08-17 17:11:31 +00:00
inline void updatePixels ( uint16_t bits )
2015-01-02 06:04:38 +00:00
{
2018-09-09 12:56:55 +00:00
if ( ! GetColorBurst ( ) )
2015-01-02 06:04:38 +00:00
{
/* #1 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
2015-01-02 06:04:38 +00:00
/* #2 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
2015-01-02 06:04:38 +00:00
/* #3 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
2015-01-02 06:04:38 +00:00
/* #4 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
2015-01-02 06:04:38 +00:00
/* #5 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
2015-01-02 06:04:38 +00:00
/* #6 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
2015-01-02 06:04:38 +00:00
/* #7 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateBnWPixel ( bits & 1 ) ; bits > > = 1 ;
g_pFuncUpdateBnWPixel ( bits & 1 ) ;
2018-05-24 20:34:26 +00:00
g_nLastColumnPixelNTSC = bits & 1 ;
2015-01-02 06:04:38 +00:00
}
2014-12-31 22:53:55 +00:00
else
2015-01-02 06:04:38 +00:00
{
2015-01-03 04:05:49 +00:00
/* #1 of 7 */ // abcd efgh ijkl mnop
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0abc defg hijk lmno
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 00ab cdef ghi jklmn
2015-01-02 06:04:38 +00:00
/* #2 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 000a bcde fghi jklm
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 abcd efgh ijkl
2015-01-02 06:04:38 +00:00
/* #3 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0abc defg hijk
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 00ab cdef ghij
2015-01-02 06:04:38 +00:00
/* #4 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 000a bcde fghi
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0000 abcd efgh
2015-01-02 06:04:38 +00:00
/* #5 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0000 0abc defg
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0000 00ab cdef
2015-01-02 06:04:38 +00:00
/* #6 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0000 000a bcde
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0000 0000 abcd
2015-01-02 06:04:38 +00:00
/* #7 of 7 */
2015-01-03 04:05:49 +00:00
g_pFuncUpdateHuePixel ( bits & 1 ) ; bits > > = 1 ; // 0000 0000 0000 0abc
g_pFuncUpdateHuePixel ( bits & 1 ) ;
2018-05-24 20:34:26 +00:00
g_nLastColumnPixelNTSC = bits & 1 ;
2015-01-02 06:04:38 +00:00
}
2014-12-31 22:53:55 +00:00
}
2015-01-02 06:04:38 +00:00
//===========================================================================
2018-09-09 12:56:55 +00:00
2019-01-09 21:29:36 +00:00
inline void updateVideoScannerHorzEOLSimple ( )
{
if ( VIDEO_SCANNER_MAX_HORZ = = + + g_nVideoClockHorz )
{
2022-11-20 20:54:15 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY ) // Only write to video memory when in visible part of display (GH#1143)
{
* ( uint32_t * ) g_pVideoAddress = 0 | ALPHA32_MASK ; // VT_COLOR_IDEALIZED: TEXT -> HGR can leave junk on RHS (GH#1106)
* ( getScanlineNextInbetween ( ) ) = 0 | ALPHA32_MASK ; // ...and clear junk on RHS for non-'50% Scan lines'
}
2022-06-02 19:47:13 +00:00
2019-01-09 21:29:36 +00:00
g_nVideoClockHorz = 0 ;
2019-06-28 20:34:34 +00:00
if ( + + g_nVideoClockVert = = g_videoScannerMaxVert )
2019-01-09 21:29:36 +00:00
{
g_nVideoClockVert = 0 ;
updateFlashRate ( ) ;
}
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
updateVideoScannerAddress ( ) ;
}
}
}
2018-09-09 12:56:55 +00:00
// NOTE: This writes out-of-bounds for a 560x384 framebuffer
2015-01-03 04:58:27 +00:00
inline void updateVideoScannerHorzEOL ( )
2014-12-31 22:53:55 +00:00
{
2015-01-03 04:58:27 +00:00
if ( VIDEO_SCANNER_MAX_HORZ = = + + g_nVideoClockHorz )
{
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
2018-09-09 12:56:55 +00:00
if ( ! GetColorBurst ( ) )
2015-01-03 04:58:27 +00:00
{
2018-09-09 12:56:55 +00:00
// Only for: VF_TEXT && !VF_MIXED (ie. full 24-row TEXT40 or TEXT80)
2022-05-29 21:00:20 +00:00
g_pFuncUpdateBnWPixel ( g_nLastColumnPixelNTSC ) ; // last pixel in 14M video modes
g_pFuncUpdateBnWPixel ( 0 ) ; // 14M ringing pixel! (better definition for 80COL char's right-hand edge)
// Direct write instead of g_pFuncUpdateBnWPixel(0) to avoid random pixels on RHS in VT_COLOR_MONITOR_NTSC
* ( uint32_t * ) g_pVideoAddress + + = 0 | ALPHA32_MASK ;
* ( uint32_t * ) g_pVideoAddress + + = 0 | ALPHA32_MASK ;
2015-01-03 04:58:27 +00:00
}
else
{
2022-05-29 21:00:20 +00:00
g_pFuncUpdateHuePixel ( g_nLastColumnPixelNTSC ) ; // last pixel in 14M video modes
g_pFuncUpdateHuePixel ( 0 ) ; // 14M ringing pixel! (better definition for 80COL char's right-hand edge)
// Direct write instead of g_pFuncUpdateHuePixel(0) to avoid random pixels on RHS in VT_COLOR_MONITOR_NTSC
2023-01-02 11:15:40 +00:00
* ( uint32_t * ) g_pVideoAddress = 0 | ALPHA32_MASK ;
* ( getScanlineNextInbetween ( ) ) = 0 | ALPHA32_MASK ; g_pVideoAddress + + ; // Clear junk on RHS for TV (Color/B&W) & Monitor (NTSC/PAL). (GH#1157)
* ( uint32_t * ) g_pVideoAddress = 0 | ALPHA32_MASK ;
* ( getScanlineNextInbetween ( ) ) = 0 | ALPHA32_MASK ; g_pVideoAddress + + ; // Clear junk on RHS for TV (Color/B&W) & Monitor (NTSC/PAL). (GH#1157)
2018-09-09 12:56:55 +00:00
}
}
g_nVideoClockHorz = 0 ;
2019-06-28 20:34:34 +00:00
if ( + + g_nVideoClockVert = = g_videoScannerMaxVert )
2018-09-09 12:56:55 +00:00
{
g_nVideoClockVert = 0 ;
updateFlashRate ( ) ;
}
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
updateVideoScannerAddress ( ) ;
}
}
}
2021-11-30 21:41:02 +00:00
inline void updateVideoScannerHorzEOL_SHR ( )
{
if ( VIDEO_SCANNER_MAX_HORZ = = + + g_nVideoClockHorz )
{
g_nVideoClockHorz = 0 ;
if ( + + g_nVideoClockVert = = g_videoScannerMaxVert )
{
g_nVideoClockVert = 0 ;
}
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY_IIGS )
{
updateVideoScannerAddress ( ) ;
}
}
}
2015-01-02 06:04:38 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
inline void updateVideoScannerAddress ( )
2014-12-31 22:53:55 +00:00
{
2022-06-26 15:09:41 +00:00
if ( g_nVideoMixed & & GetVideo ( ) . GetVideoRefreshRate ( ) = = VR_50HZ ) // GH#763
{
if ( g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
g_nColorBurstPixels = 0 ; // instantaneously kill color-burst!
else if ( g_nVideoClockVert = = 0 & & ( GetVideo ( ) . GetVideoMode ( ) & VF_TEXT ) = = 0 )
g_nColorBurstPixels = 1024 ; // setup for line-0 (when TEXT is off), ie. so GetColorBurst() returns true below (GH#1119)
}
2020-08-17 17:11:31 +00:00
2021-11-30 21:41:02 +00:00
if ( g_pFuncUpdateGraphicsScreen = = updateScreenSHR )
{
g_pVideoAddress = g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY_IIGS ? g_pScanLines [ 2 * g_nVideoClockVert ] : g_pScanLines [ 0 ] ;
return ;
}
g_pVideoAddress = g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY ? g_pScanLines [ 2 * g_nVideoClockVert ] : g_pScanLines [ 0 ] ;
2018-09-09 12:56:55 +00:00
// Adjust, as these video styles have 2x 14M pixels of pre-render
2019-01-09 21:29:36 +00:00
// NB. For VT_COLOR_MONITOR_NTSC, also check color-burst so that TEXT and MIXED(HGR+TEXT) render the TEXT at the same offset (GH#341)
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_MONO_TV | | GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_TV | | ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_MONITOR_NTSC & & GetColorBurst ( ) ) )
2018-09-09 14:41:04 +00:00
g_pVideoAddress - = 2 ;
// GH#555: For the 14M video modes (DHGR,DGR,80COL), start rendering 1x 14M pixel early to account for these video modes being shifted right by 1 pixel
// NB. This 1 pixel shift right is a workaround for the 14M video modes that actually start 7x 14M pixels to the left on *real h/w*.
// . 7x 14M pixels early + 1x 14M pixel shifted right = 2 complete color phase rotations.
// . ie. the 14M colors are correct, but being 1 pixel out is the closest we can get the 7M and 14M video modes to overlap.
// . The alternative is to render the 14M correctly 7 pixels early, but have 7-pixel borders left (for 7M modes) or right (for 14M modes).
2019-01-09 21:29:36 +00:00
if ( ( ( g_pFuncUpdateGraphicsScreen = = updateScreenDoubleHires80 ) | |
2018-09-09 14:41:04 +00:00
( g_pFuncUpdateGraphicsScreen = = updateScreenDoubleLores80 ) | |
( g_pFuncUpdateGraphicsScreen = = updateScreenText80 ) | |
2020-09-01 20:32:44 +00:00
( g_pFuncUpdateGraphicsScreen = = updateScreenText80RGB ) | |
( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED & & ( g_pFuncUpdateTextScreen = = updateScreenText80 | | g_pFuncUpdateGraphicsScreen = = updateScreenText80RGB ) ) )
2020-12-28 16:25:29 +00:00
& & ( GetVideo ( ) . GetVideoType ( ) ! = VT_COLOR_IDEALIZED ) & & ( GetVideo ( ) . GetVideoType ( ) ! = VT_COLOR_VIDEOCARD_RGB ) ) // Fix for "Ansi Story" (Turn the disk over) - Top row of TEXT80 is shifted by 1 pixel
2018-09-09 14:41:04 +00:00
{
g_pVideoAddress - = 1 ;
}
2018-09-09 12:56:55 +00:00
2021-11-30 21:41:02 +00:00
// Centre the older //e video modes when running with a VidHD
g_pVideoAddress + = GetVideo ( ) . GetFrameBufferCentringValue ( ) ;
2022-05-29 21:00:20 +00:00
if ( GetVideo ( ) . HasVidHD ( ) )
{
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_MONITOR_NTSC )
{
// EG. Switching between TEXT (full 24 lines) and MIXED (HGR with purple vertical line-0)
// - AppleWin-Test repo, Tests-Various.dsk, option-C
g_pVideoAddress - = 2 ;
* ( uint32_t * ) g_pVideoAddress + + = 0 | ALPHA32_MASK ;
* ( uint32_t * ) g_pVideoAddress + + = 0 | ALPHA32_MASK ;
}
}
2015-01-03 04:58:27 +00:00
g_nColorPhaseNTSC = INITIAL_COLOR_PHASE ;
g_nLastColumnPixelNTSC = 0 ;
g_nSignalBitsNTSC = 0 ;
2014-12-31 22:53:55 +00:00
}
2022-05-29 21:00:20 +00:00
//===========================================================================
# if 1
# define CLEAR_COLOUR_TOP 0
# define CLEAR_COLOUR_SIDE 0
# else // debug
# define CLEAR_COLOUR_TOP 0x0000FF00 // green
# define CLEAR_COLOUR_SIDE 0x00FF0000 // red
# endif
static void ClearOverscanVideoArea ( void )
{
if ( g_pFuncUpdateGraphicsScreen = = updateScreenSHR )
return ;
bgra_t * pSaveVideoAddress = g_pVideoAddress ; // save g_pVideoAddress
g_pVideoAddress = g_pScanLines [ 0 ] ;
uint32_t * pLine1Prev = getScanlinePreviousInbetween ( ) ;
g_pVideoAddress = pSaveVideoAddress ; // restore g_pVideoAddress
2022-06-07 20:00:33 +00:00
const int kOverscanOffsetL = 3 ; // In updateVideoScannerAddress(), g_pVideoAddress could be adjusted by: -2 + -1 = -3
const int kOverscanSpanL = 3 ;
const int kOverscanOverlapL = kOverscanSpanL - kOverscanOffsetL ;
2022-05-29 21:00:20 +00:00
2022-06-07 20:00:33 +00:00
const int kOverscanOffsetR = 2 ;
const int kOverscanSpanR = 4 ; // In updateVideoScannerHorzEOL() it writes 4 extra pixels
const int kOverscanOverlapR = kOverscanSpanR - kOverscanOffsetR ;
2022-05-29 21:00:20 +00:00
2022-06-07 20:00:33 +00:00
const int kHorzPixels = ( VIDEO_SCANNER_MAX_HORZ - VIDEO_SCANNER_HORZ_START ) * 14 ;
2022-05-29 21:00:20 +00:00
pLine1Prev + = GetVideo ( ) . GetFrameBufferCentringValue ( ) - kOverscanOffsetL ; // Centre the older //e video modes when running with a VidHD
// Clear this line at Y=-1
for ( uint32_t i = 0 ; i < ( kHorzPixels + ( kOverscanSpanL - kOverscanOverlapL ) + ( kOverscanSpanR - kOverscanOverlapR ) ) ; i + + )
* pLine1Prev + + = CLEAR_COLOUR_TOP | ALPHA32_MASK ;
// Clear overscan before & after display area
for ( uint32_t i = 0 ; i < VIDEO_SCANNER_Y_DISPLAY * 2 ; i + + )
{
uint32_t * pScanLine = ( ( uint32_t * ) g_pScanLines [ i ] ) ;
pScanLine + = GetVideo ( ) . GetFrameBufferCentringValue ( ) - kOverscanOffsetL ; // Centre the older //e video modes when running with a VidHD
for ( uint32_t j = 0 ; j < kOverscanSpanL + 1 ; j + + )
pScanLine [ j ] = CLEAR_COLOUR_SIDE | ALPHA32_MASK ;
pScanLine + = kOverscanOffsetL + kHorzPixels - kOverscanOffsetR ;
for ( uint32_t j = 0 ; j < kOverscanSpanR ; j + + )
pScanLine [ j ] = CLEAR_COLOUR_SIDE | ALPHA32_MASK ;
}
}
2015-01-04 02:32:34 +00:00
//===========================================================================
2018-08-05 21:19:51 +00:00
INLINE uint16_t getVideoScannerAddressTXT ( )
2015-01-04 02:32:34 +00:00
{
2022-01-05 07:37:38 +00:00
uint16_t nAddress = ( g_aClockVertOffsetsTXT [ g_nVideoClockVert / 8 ]
+ g_pHorzClockOffset [ g_nVideoClockVert / 64 ] [ g_nVideoClockHorz ]
+ ( g_nTextPage * 0x400 ) ) ;
return nAddress ;
2015-01-04 02:32:34 +00:00
}
//===========================================================================
2018-08-05 21:19:51 +00:00
INLINE uint16_t getVideoScannerAddressHGR ( )
2015-01-04 02:32:34 +00:00
{
2018-08-05 21:19:51 +00:00
// NB. For both A2 and //e use APPLE_IIE_HORZ_CLOCK_OFFSET - see VideoGetScannerAddress() where only TEXT mode adds $1000
2022-01-05 07:37:38 +00:00
uint16_t nAddress = ( g_aClockVertOffsetsHGR [ g_nVideoClockVert ]
+ APPLE_IIE_HORZ_CLOCK_OFFSET [ g_nVideoClockVert / 64 ] [ g_nVideoClockHorz ]
+ ( g_nHiresPage * 0x2000 ) ) ;
return nAddress ;
2015-01-04 02:32:34 +00:00
}
2022-10-02 19:23:54 +00:00
//===========================================================================
INLINE uint16_t getVideoScannerAddressTXTorHGR ( )
{
const bool isTextAddr = ( ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED ) | |
( g_uNewVideoModeFlags & VF_TEXT ) | |
! ( g_uNewVideoModeFlags & VF_HIRES ) ) ;
if ( isTextAddr )
return getVideoScannerAddressTXT ( ) ;
else
return getVideoScannerAddressHGR ( ) ;
}
2022-10-02 15:28:03 +00:00
//===========================================================================
INLINE uint16_t getVideoScannerAddressSHR ( )
{
// 2 pixels per byte in 320-pixel mode = 160 bytes/scanline
// 4 pixels per byte in 640-pixel mode = 160 bytes/scanline
const UINT kBytesPerScanline = 160 ;
const UINT kBytesPerCycle = 4 ;
return 0x2000 + kBytesPerScanline * g_nVideoClockVert + kBytesPerCycle * ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START ) ;
}
2015-01-03 04:58:27 +00:00
// Non-Inline _________________________________________________________
2014-12-31 22:53:55 +00:00
// Build the 4 phase chroma lookup table
// The YI'Q' colors are hard-coded
//===========================================================================
2015-01-03 04:58:27 +00:00
static void initChromaPhaseTables ( void )
2014-12-31 22:53:55 +00:00
{
int phase , s , t , n ;
real z , y0 , y1 , c , i , q ;
real phi , zz ;
float brightness ;
double r64 , g64 , b64 ;
float r32 , g32 , b32 ;
for ( phase = 0 ; phase < 4 ; + + phase )
{
phi = ( phase * RAD_90 ) + CYCLESTART ;
for ( s = 0 ; s < NTSC_NUM_SEQUENCES ; + + s )
{
t = s ;
y0 = y1 = c = i = q = 0.0 ;
for ( n = 0 ; n < 12 ; + + n )
{
z = ( real ) ( 0 ! = ( t & 0x800 ) ) ;
t = t < < 1 ;
for ( int k = 0 ; k < 2 ; k + + )
{
//z = z * 1.25;
2015-01-03 04:58:27 +00:00
zz = initFilterSignal ( z ) ;
2015-01-04 09:10:30 +00:00
c = initFilterChroma ( zz ) ; // "Mostly" correct _if_ CYCLESTART = PI/4 = 45 degrees
y0 = initFilterLuma0 ( zz ) ;
y1 = initFilterLuma1 ( zz - c ) ;
2015-01-13 19:02:42 +00:00
2014-12-31 22:53:55 +00:00
c = c * 2.f ;
i = i + ( c * cos ( phi ) - i ) / 8.f ;
q = q + ( c * sin ( phi ) - q ) / 8.f ;
2015-01-13 19:02:42 +00:00
phi + = RAD_45 ;
2014-12-31 22:53:55 +00:00
} // k
} // samples
brightness = clampZeroOne ( ( float ) z ) ;
2015-01-02 20:12:22 +00:00
g_aBnWMonitor [ s ] . b = ( uint8_t ) ( brightness * 255 ) ;
g_aBnWMonitor [ s ] . g = ( uint8_t ) ( brightness * 255 ) ;
g_aBnWMonitor [ s ] . r = ( uint8_t ) ( brightness * 255 ) ;
g_aBnWMonitor [ s ] . a = 255 ;
2014-12-31 22:53:55 +00:00
brightness = clampZeroOne ( ( float ) y1 ) ;
2015-01-02 20:12:22 +00:00
g_aBnwColorTV [ s ] . b = ( uint8_t ) ( brightness * 255 ) ;
g_aBnwColorTV [ s ] . g = ( uint8_t ) ( brightness * 255 ) ;
g_aBnwColorTV [ s ] . r = ( uint8_t ) ( brightness * 255 ) ;
g_aBnwColorTV [ s ] . a = 255 ;
2014-12-31 22:53:55 +00:00
/*
YI ' V ' to RGB
[ r g b ] = [ y i v ] [ 1 1 1 ]
[ 0.956 - 0.272 - 1.105 ]
[ 0.621 - 0.647 1.702 ]
[ r ] [ 1 0.956 0.621 ] [ y ]
[ g ] = [ 1 - 0.272 - 0.647 ] [ i ]
[ b ] [ 1 - 1.105 1.702 ] [ v ]
*/
# define I_TO_R 0.956f
# define I_TO_G -0.272f
# define I_TO_B -1.105f
# define Q_TO_R 0.621f
# define Q_TO_G -0.647f
# define Q_TO_B 1.702f
r64 = y0 + ( I_TO_R * i ) + ( Q_TO_R * q ) ;
g64 = y0 + ( I_TO_G * i ) + ( Q_TO_G * q ) ;
b64 = y0 + ( I_TO_B * i ) + ( Q_TO_B * q ) ;
2015-01-13 19:02:42 +00:00
2014-12-31 22:53:55 +00:00
b32 = clampZeroOne ( ( float ) b64 ) ;
g32 = clampZeroOne ( ( float ) g64 ) ;
r32 = clampZeroOne ( ( float ) r64 ) ;
2016-10-15 05:28:39 +00:00
int color = s & 15 ;
2015-01-13 19:02:42 +00:00
# if NTSC_REMOVE_WHITE_RINGING
2016-10-15 05:28:39 +00:00
if ( color = = 15 ) // white
2015-01-13 19:02:42 +00:00
{
r32 = 1 ;
g32 = 1 ;
b32 = 1 ;
}
# endif
# if NTSC_REMOVE_BLACK_GHOSTING
2016-10-15 05:28:39 +00:00
if ( color = = 0 ) // Black
2015-01-13 19:02:42 +00:00
{
r32 = 0 ;
g32 = 0 ;
b32 = 0 ;
}
# endif
2016-10-15 05:28:39 +00:00
# if NTSC_REMOVE_GRAY_CHROMA
if ( color = = 5 ) // Gray1 & Gray2
{
const float g = ( float ) 0x83 / ( float ) 0xFF ;
r32 = g ;
g32 = g ;
b32 = g ;
}
if ( color = = 10 ) // Gray2 & Gray1
{
const float g = ( float ) 0x78 / ( float ) 0xFF ;
r32 = g ;
g32 = g ;
b32 = g ;
}
# endif
2015-01-02 20:12:22 +00:00
g_aHueMonitor [ phase ] [ s ] . b = ( uint8_t ) ( b32 * 255 ) ;
g_aHueMonitor [ phase ] [ s ] . g = ( uint8_t ) ( g32 * 255 ) ;
g_aHueMonitor [ phase ] [ s ] . r = ( uint8_t ) ( r32 * 255 ) ;
g_aHueMonitor [ phase ] [ s ] . a = 255 ;
2016-10-15 05:28:39 +00:00
2014-12-31 22:53:55 +00:00
r64 = y1 + ( I_TO_R * i ) + ( Q_TO_R * q ) ;
g64 = y1 + ( I_TO_G * i ) + ( Q_TO_G * q ) ;
b64 = y1 + ( I_TO_B * i ) + ( Q_TO_B * q ) ;
2015-01-02 07:50:04 +00:00
2014-12-31 22:53:55 +00:00
b32 = clampZeroOne ( ( float ) b64 ) ;
g32 = clampZeroOne ( ( float ) g64 ) ;
r32 = clampZeroOne ( ( float ) r64 ) ;
2015-01-13 19:02:42 +00:00
# if NTSC_REMOVE_WHITE_RINGING
2016-10-15 05:28:39 +00:00
if ( color = = 15 ) // white
2015-01-13 19:02:42 +00:00
{
r32 = 1 ;
g32 = 1 ;
b32 = 1 ;
}
# endif
# if NTSC_REMOVE_BLACK_GHOSTING
2016-10-15 05:28:39 +00:00
if ( color = = 0 ) // Black
2015-01-13 19:02:42 +00:00
{
r32 = 0 ;
g32 = 0 ;
b32 = 0 ;
}
# endif
2015-01-02 20:12:22 +00:00
g_aHueColorTV [ phase ] [ s ] . b = ( uint8_t ) ( b32 * 255 ) ;
g_aHueColorTV [ phase ] [ s ] . g = ( uint8_t ) ( g32 * 255 ) ;
g_aHueColorTV [ phase ] [ s ] . r = ( uint8_t ) ( r32 * 255 ) ;
g_aHueColorTV [ phase ] [ s ] . a = 255 ;
2014-12-31 22:53:55 +00:00
}
}
2015-01-11 06:47:07 +00:00
# if DEBUG_PHASE_ZERO
uint8_t * p = ( uint8_t * ) g_aHueMonitor ;
* p + + = 0xFF ;
* p + + = 0x00 ;
* p + + = 0x00 ;
* p + + = 0xFF ;
# endif
2014-12-31 22:53:55 +00:00
}
2015-01-04 09:10:30 +00:00
/*
http : //www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
Sample Rate : ? ? ?
Corner Freq 1 : ?
Corner Freq 2 : ?
double ButterworthLowPass2 ( double a , double b , double g , double z )
{
const int POLES = 2 ;
static double x [ POLES + 1 ] ;
static double y [ POLES + 1 ] ;
for ( int iPole = 0 ; iPole < POLES ; iPole + + )
{
x [ iPole ] = x [ iPole + 1 ] ;
y [ iPole ] = y [ iPole + 1 ] ;
}
x [ POLES ] = z / g ;
y [ POLES ] = x [ 0 ] + x [ 2 ] + ( 2.f * x [ 1 ] ) + ( a * y [ 0 ] ) + ( b * y [ 1 ] ) ;
return y [ 2 ] ;
}
*/
// What filter is this ??
// Filter Order: 2 -> poles for low pass
2014-12-31 22:53:55 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
static real initFilterChroma ( real z )
2014-12-31 22:53:55 +00:00
{
2015-01-04 09:10:30 +00:00
static real x [ CHROMA_ZEROS + 1 ] = { 0 , 0 , 0 } ;
static real y [ CHROMA_POLES + 1 ] = { 0 , 0 , 0 } ;
2014-12-31 22:53:55 +00:00
2015-01-04 09:10:30 +00:00
x [ 0 ] = x [ 1 ] ; x [ 1 ] = x [ 2 ] ; x [ 2 ] = z / CHROMA_GAIN ;
2015-01-04 10:03:54 +00:00
y [ 0 ] = y [ 1 ] ; y [ 1 ] = y [ 2 ] ; y [ 2 ] = - x [ 0 ] + x [ 2 ] + ( CHROMA_0 * y [ 0 ] ) + ( CHROMA_1 * y [ 1 ] ) ; // inverted x[0]
2014-12-31 22:53:55 +00:00
2015-01-04 09:10:30 +00:00
return y [ 2 ] ;
2014-12-31 22:53:55 +00:00
}
2015-01-04 09:10:30 +00:00
// Butterworth Lowpass digital filter
// Filter Order: 2 -> poles for low pass
2015-01-02 06:04:38 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
static real initFilterLuma0 ( real z )
2014-12-31 22:53:55 +00:00
{
2015-01-04 09:10:30 +00:00
static real x [ LUMA_ZEROS + 1 ] = { 0 , 0 , 0 } ;
static real y [ LUMA_POLES + 1 ] = { 0 , 0 , 0 } ;
2014-12-31 22:53:55 +00:00
2015-01-04 09:10:30 +00:00
x [ 0 ] = x [ 1 ] ; x [ 1 ] = x [ 2 ] ; x [ 2 ] = z / LUMA_GAIN ;
y [ 0 ] = y [ 1 ] ; y [ 1 ] = y [ 2 ] ; y [ 2 ] = x [ 0 ] + x [ 2 ] + ( 2.f * x [ 1 ] ) + ( LUMA_0 * y [ 0 ] ) + ( LUMA_1 * y [ 1 ] ) ;
2015-01-03 04:58:27 +00:00
2015-01-04 09:10:30 +00:00
return y [ 2 ] ;
2014-12-31 22:53:55 +00:00
}
2015-01-04 09:10:30 +00:00
// Butterworth Lowpass digital filter
// Filter Order: 2 -> poles for low pass
2015-01-02 06:04:38 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
static real initFilterLuma1 ( real z )
2014-12-31 22:53:55 +00:00
{
2015-01-04 09:10:30 +00:00
static real x [ LUMA_ZEROS + 1 ] = { 0 , 0 , 0 } ;
static real y [ LUMA_POLES + 1 ] = { 0 , 0 , 0 } ;
2015-01-03 04:58:27 +00:00
2015-01-04 09:10:30 +00:00
x [ 0 ] = x [ 1 ] ; x [ 1 ] = x [ 2 ] ; x [ 2 ] = z / LUMA_GAIN ;
y [ 0 ] = y [ 1 ] ; y [ 1 ] = y [ 2 ] ; y [ 2 ] = x [ 0 ] + x [ 2 ] + ( 2.f * x [ 1 ] ) + ( LUMA_0 * y [ 0 ] ) + ( LUMA_1 * y [ 1 ] ) ;
2015-01-03 04:58:27 +00:00
2015-01-04 09:10:30 +00:00
return y [ 2 ] ;
2014-12-31 22:53:55 +00:00
}
2015-01-04 09:10:30 +00:00
// Butterworth Lowpass digital filter
// Filter Order: 2 -> poles for low pass
2015-01-02 06:04:38 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
static real initFilterSignal ( real z )
2014-12-31 22:53:55 +00:00
{
2015-01-04 09:10:30 +00:00
static real x [ SIGNAL_ZEROS + 1 ] = { 0 , 0 , 0 } ;
static real y [ SIGNAL_POLES + 1 ] = { 0 , 0 , 0 } ;
2015-01-03 04:58:27 +00:00
2015-01-04 09:10:30 +00:00
x [ 0 ] = x [ 1 ] ; x [ 1 ] = x [ 2 ] ; x [ 2 ] = z / SIGNAL_GAIN ;
y [ 0 ] = y [ 1 ] ; y [ 1 ] = y [ 2 ] ; y [ 2 ] = x [ 0 ] + x [ 2 ] + ( 2.f * x [ 1 ] ) + ( SIGNAL_0 * y [ 0 ] ) + ( SIGNAL_1 * y [ 1 ] ) ;
2015-01-03 04:58:27 +00:00
2015-01-04 09:10:30 +00:00
return y [ 2 ] ;
2014-12-31 22:53:55 +00:00
}
2015-01-02 06:04:38 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
static void initPixelDoubleMasks ( void )
2014-12-31 22:53:55 +00:00
{
2015-01-03 04:58:27 +00:00
/*
Convert 7 - bit monochrome luminance to 14 - bit double pixel luminance
Chroma will be applied later based on the color phase in updatePixelHueMonitorDoubleScanline ( luminanceBit )
0x001 - > 0x0003
0x002 - > 0x000C
0x004 - > 0x0030
0x008 - > 0x00C0
: - > :
0x100 - > 0x4000
*/
for ( uint8_t byte = 0 ; byte < 0x80 ; byte + + ) // Optimization: hgrbits second 128 entries are mirror of first 128
for ( uint8_t bits = 0 ; bits < 7 ; bits + + ) // high bit = half pixel shift; pre-optimization: bits < 8
if ( byte & ( 1 < < bits ) ) // pow2 mask
g_aPixelDoubleMaskHGR [ byte ] | = 3 < < ( bits * 2 ) ;
for ( uint16_t color = 0 ; color < 16 ; color + + )
g_aPixelMaskGR [ color ] = ( color < < 12 ) | ( color < < 8 ) | ( color < < 4 ) | ( color < < 0 ) ;
2014-12-31 22:53:55 +00:00
}
2015-01-01 17:59:58 +00:00
//===========================================================================
2015-01-03 04:00:24 +00:00
void updateMonochromeTables ( uint16_t r , uint16_t g , uint16_t b )
2015-01-01 17:59:58 +00:00
{
for ( int iSample = 0 ; iSample < NTSC_NUM_SEQUENCES ; iSample + + )
{
2015-01-02 20:12:22 +00:00
g_aBnWMonitorCustom [ iSample ] . b = ( g_aBnWMonitor [ iSample ] . b * b ) > > 8 ;
g_aBnWMonitorCustom [ iSample ] . g = ( g_aBnWMonitor [ iSample ] . g * g ) > > 8 ;
g_aBnWMonitorCustom [ iSample ] . r = ( g_aBnWMonitor [ iSample ] . r * r ) > > 8 ;
g_aBnWMonitorCustom [ iSample ] . a = 0xFF ;
g_aBnWColorTVCustom [ iSample ] . b = ( g_aBnwColorTV [ iSample ] . b * b ) > > 8 ;
g_aBnWColorTVCustom [ iSample ] . g = ( g_aBnwColorTV [ iSample ] . g * g ) > > 8 ;
g_aBnWColorTVCustom [ iSample ] . r = ( g_aBnwColorTV [ iSample ] . r * r ) > > 8 ;
g_aBnWColorTVCustom [ iSample ] . a = 0xFF ;
2015-01-01 17:59:58 +00:00
}
}
2014-12-31 22:53:55 +00:00
//===========================================================================
2015-01-03 04:58:27 +00:00
static void updatePixelBnWMonitorSingleScanline ( uint16_t compositeSignal )
2014-12-31 22:53:55 +00:00
{
2015-01-03 04:58:27 +00:00
updateFramebufferMonitorSingleScanline ( compositeSignal , g_aBnWMonitorCustom ) ;
2020-08-17 17:11:31 +00:00
updateColorPhase ( ) ; // Maintain color-phase, as could be switching graphics/text video modes mid-scanline
2014-12-31 22:53:55 +00:00
}
//===========================================================================
2015-01-03 04:58:27 +00:00
static void updatePixelBnWMonitorDoubleScanline ( uint16_t compositeSignal )
2014-12-31 22:53:55 +00:00
{
2015-01-03 04:58:27 +00:00
updateFramebufferMonitorDoubleScanline ( compositeSignal , g_aBnWMonitorCustom ) ;
2020-08-17 17:11:31 +00:00
updateColorPhase ( ) ; // Maintain color-phase, as could be switching graphics/text video modes mid-scanline
2015-01-03 04:58:27 +00:00
}
2014-12-31 22:53:55 +00:00
2015-01-03 04:58:27 +00:00
//===========================================================================
static void updatePixelBnWColorTVSingleScanline ( uint16_t compositeSignal )
{
2019-09-01 15:47:48 +00:00
updateFramebufferTVSingleScanline ( compositeSignal , g_aBnWColorTVCustom ) ;
2020-08-17 17:11:31 +00:00
updateColorPhase ( ) ; // Maintain color-phase, as could be switching graphics/text video modes mid-scanline
2015-01-03 04:58:27 +00:00
}
2014-12-31 22:53:55 +00:00
2015-01-03 04:58:27 +00:00
//===========================================================================
static void updatePixelBnWColorTVDoubleScanline ( uint16_t compositeSignal )
{
2019-09-01 15:47:48 +00:00
updateFramebufferTVDoubleScanline ( compositeSignal , g_aBnWColorTVCustom ) ;
2020-08-17 17:11:31 +00:00
updateColorPhase ( ) ; // Maintain color-phase, as could be switching graphics/text video modes mid-scanline
2015-01-03 04:58:27 +00:00
}
2014-12-31 22:53:55 +00:00
2015-01-03 04:58:27 +00:00
//===========================================================================
static void updatePixelHueColorTVSingleScanline ( uint16_t compositeSignal )
{
2019-09-01 15:47:48 +00:00
updateFramebufferTVSingleScanline ( compositeSignal , g_aHueColorTV [ g_nColorPhaseNTSC ] ) ;
2015-01-03 04:58:27 +00:00
updateColorPhase ( ) ;
}
2015-01-02 06:04:38 +00:00
2015-01-03 04:58:27 +00:00
//===========================================================================
static void updatePixelHueColorTVDoubleScanline ( uint16_t compositeSignal )
{
2019-09-01 15:47:48 +00:00
updateFramebufferTVDoubleScanline ( compositeSignal , g_aHueColorTV [ g_nColorPhaseNTSC ] ) ;
2015-01-03 04:58:27 +00:00
updateColorPhase ( ) ;
}
2015-01-02 06:04:38 +00:00
2015-01-03 04:58:27 +00:00
//===========================================================================
static void updatePixelHueMonitorSingleScanline ( uint16_t compositeSignal )
{
updateFramebufferMonitorSingleScanline ( compositeSignal , g_aHueMonitor [ g_nColorPhaseNTSC ] ) ;
updateColorPhase ( ) ;
}
2015-01-02 06:04:38 +00:00
2015-01-03 04:58:27 +00:00
//===========================================================================
static void updatePixelHueMonitorDoubleScanline ( uint16_t compositeSignal )
{
updateFramebufferMonitorDoubleScanline ( compositeSignal , g_aHueMonitor [ g_nColorPhaseNTSC ] ) ;
updateColorPhase ( ) ;
2015-01-01 19:59:54 +00:00
}
2014-12-31 22:53:55 +00:00
//===========================================================================
2015-01-03 04:00:24 +00:00
void updateScreenDoubleHires40 ( long cycles6502 ) // wsUpdateVideoHires0
2014-12-31 22:53:55 +00:00
{
2015-01-02 07:50:04 +00:00
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
2014-12-31 22:53:55 +00:00
{
2015-01-03 03:46:40 +00:00
g_pFuncUpdateTextScreen ( cycles6502 ) ;
2015-01-02 07:50:04 +00:00
return ;
2014-12-31 22:53:55 +00:00
}
2015-01-02 07:50:04 +00:00
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressHGR ( ) ;
2014-12-31 22:53:55 +00:00
2015-01-02 07:50:04 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
2014-12-31 22:53:55 +00:00
{
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
2014-12-31 22:53:55 +00:00
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
2015-01-02 07:50:04 +00:00
uint8_t m = pMain [ 0 ] ;
uint16_t bits = g_aPixelDoubleMaskHGR [ m & 0x7F ] ; // Optimization: hgrbits second 128 entries are mirror of first 128
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2018-09-09 12:56:55 +00:00
// NB. No zeroPixel0_14M(), since no color phase shift (or use of g_nLastColumnPixelNTSC)
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
//===========================================================================
2019-01-09 21:29:36 +00:00
2020-09-27 13:09:02 +00:00
void updateScreenDoubleHires80Simplified ( long cycles6502 ) // wsUpdateVideoDblHires
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
uint8_t a = * MemGetAuxPtr ( addr ) ;
uint8_t m = * MemGetMainPtr ( addr ) ;
UpdateDHiResCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress , true , true ) ;
g_pVideoAddress + = 14 ;
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
//===========================================================================
void updateScreenDoubleHires80RGB ( long cycles6502 ) // wsUpdateVideoDblHires
2019-01-09 21:29:36 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
2019-02-02 15:51:27 +00:00
uint8_t a = * MemGetAuxPtr ( addr ) ;
uint8_t m = * MemGetMainPtr ( addr ) ;
2019-04-06 16:31:26 +00:00
if ( RGB_IsMixModeInvertBit7 ( ) ) // Invert high bit? (GH#633)
2019-04-06 14:17:18 +00:00
{
a ^ = 0x80 ;
m ^ = 0x80 ;
}
2019-02-03 16:39:27 +00:00
if ( RGB_Is160Mode ( ) )
{
int width = UpdateDHiRes160Cell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress ) ;
g_pVideoAddress + = width ;
}
2020-10-17 11:31:44 +00:00
else if ( RGB_Is560Mode ( ) )
2019-02-02 15:51:27 +00:00
{
update7MonoPixels ( a ) ;
update7MonoPixels ( m ) ;
}
2020-09-27 13:09:02 +00:00
else
2019-02-02 15:51:27 +00:00
{
2020-09-27 13:09:02 +00:00
UpdateDHiResCellRGB ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress , RGB_IsMixMode ( ) , RGB_IsMixModeInvertBit7 ( ) ) ;
2019-02-02 15:51:27 +00:00
g_pVideoAddress + = 14 ;
}
2019-01-09 21:29:36 +00:00
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
2015-01-03 04:00:24 +00:00
void updateScreenDoubleHires80 ( long cycles6502 ) // wsUpdateVideoDblHires
2014-12-31 22:53:55 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
2015-01-03 03:46:40 +00:00
g_pFuncUpdateTextScreen ( cycles6502 ) ;
2014-12-31 22:53:55 +00:00
return ;
}
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressHGR ( ) ;
2014-12-31 22:53:55 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
2014-12-31 22:53:55 +00:00
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
uint8_t * pAux = MemGetAuxPtr ( addr ) ;
2015-01-02 07:50:04 +00:00
uint8_t m = pMain [ 0 ] ;
uint8_t a = pAux [ 0 ] ;
2018-05-24 20:34:26 +00:00
uint16_t bits = ( ( m & 0x7f ) < < 7 ) | ( a & 0x7f ) ;
bits = ( bits < < 1 ) | g_nLastColumnPixelNTSC ;
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2018-05-24 20:34:26 +00:00
g_nLastColumnPixelNTSC = ( bits > > 14 ) & 1 ;
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
//===========================================================================
2015-01-03 04:00:24 +00:00
void updateScreenDoubleLores40 ( long cycles6502 ) // wsUpdateVideo7MLores
2014-12-31 22:53:55 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
2015-01-03 03:46:40 +00:00
g_pFuncUpdateTextScreen ( cycles6502 ) ;
2014-12-31 22:53:55 +00:00
return ;
}
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressTXT ( ) ;
2014-12-31 22:53:55 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
2014-12-31 22:53:55 +00:00
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
2015-01-01 19:59:54 +00:00
uint8_t m = pMain [ 0 ] ;
2015-01-02 01:20:36 +00:00
uint16_t lo = getLoResBits ( m ) ;
2015-01-02 07:50:04 +00:00
uint16_t bits = g_aPixelDoubleMaskHGR [ ( 0xFF & lo > > ( ( 1 - ( g_nVideoClockHorz & 1 ) ) * 2 ) ) & 0x7F ] ; // Optimization: hgrbits
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2018-09-09 12:56:55 +00:00
// NB. No zeroPixel0_14M(), since no color phase shift (or use of g_nLastColumnPixelNTSC)
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
//===========================================================================
2019-01-09 21:29:36 +00:00
static void updateScreenDoubleLores80Simplified ( long cycles6502 ) // wsUpdateVideoDblLores
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
uint16_t addr = getVideoScannerAddressTXT ( ) ;
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressTXT ( ) ;
UpdateDLoResCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress ) ;
g_pVideoAddress + = 14 ;
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
2015-01-03 04:00:24 +00:00
void updateScreenDoubleLores80 ( long cycles6502 ) // wsUpdateVideoDblLores
2014-12-31 22:53:55 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
2015-01-03 03:46:40 +00:00
g_pFuncUpdateTextScreen ( cycles6502 ) ;
2014-12-31 22:53:55 +00:00
return ;
}
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressTXT ( ) ;
2014-12-31 22:53:55 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
2014-12-31 22:53:55 +00:00
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
uint8_t * pAux = MemGetAuxPtr ( addr ) ;
2014-12-31 22:53:55 +00:00
2015-01-01 19:59:54 +00:00
uint8_t m = pMain [ 0 ] ;
uint8_t a = pAux [ 0 ] ;
2015-01-02 01:20:36 +00:00
uint16_t lo = getLoResBits ( m ) ;
uint16_t hi = getLoResBits ( a ) ;
2015-01-01 19:59:54 +00:00
uint16_t main = lo > > ( ( ( 1 - ( g_nVideoClockHorz & 1 ) ) * 2 ) + 3 ) ;
uint16_t aux = hi > > ( ( ( 1 - ( g_nVideoClockHorz & 1 ) ) * 2 ) + 3 ) ;
uint16_t bits = ( main < < 7 ) | ( aux & 0x7f ) ;
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2018-05-24 20:34:26 +00:00
g_nLastColumnPixelNTSC = ( bits > > 14 ) & 1 ;
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
//===========================================================================
2020-10-17 11:31:44 +00:00
// Handles both the "SingleHires40" & "DoubleHires40" cases, via UpdateHiResCell()
static void updateScreenHires40Simplified ( long cycles6502 )
2019-01-09 21:29:36 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
2020-10-17 11:31:44 +00:00
UpdateHiResCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress ) ;
g_pVideoAddress + = 14 ;
2019-01-09 21:29:36 +00:00
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
2020-09-01 20:32:44 +00:00
//===========================================================================
static void updateScreenSingleHires40Duochrome ( long cycles6502 )
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
UpdateHiResDuochromeCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress ) ;
g_pVideoAddress + = 14 ;
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
2020-09-27 13:09:02 +00:00
//===========================================================================
static void updateScreenSingleHires40RGB ( long cycles6502 )
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressHGR ( ) ;
UpdateHiResRGBCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress ) ;
g_pVideoAddress + = 14 ;
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
//===========================================================================
2015-01-03 04:00:24 +00:00
void updateScreenSingleHires40 ( long cycles6502 )
2014-12-31 22:53:55 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
2015-01-03 03:46:40 +00:00
g_pFuncUpdateTextScreen ( cycles6502 ) ;
2014-12-31 22:53:55 +00:00
return ;
}
2018-05-24 20:34:26 +00:00
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressHGR ( ) ;
2014-12-31 22:53:55 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
2014-12-31 22:53:55 +00:00
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
2015-01-02 07:50:04 +00:00
uint8_t m = pMain [ 0 ] ;
uint16_t bits = g_aPixelDoubleMaskHGR [ m & 0x7F ] ; // Optimization: hgrbits second 128 entries are mirror of first 128
if ( m & 0x80 )
bits = ( bits < < 1 ) | g_nLastColumnPixelNTSC ;
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2018-06-23 16:50:23 +00:00
// For last hpos && bit6=1: (GH#555)
// * if bit7=0 (no shift) then clear g_nLastColumnPixelNTSC to prevent a 3rd 14M (aka DHGR) pixel being drawn
// . even though this is off-screen, it still has an on-screen affect (making the green dot more white on the screen edge).
// * if bit7=1 (half-dot shift) then also clear g_nLastColumnPixelNTSC
// . not sure if this is correct though
if ( g_nVideoClockHorz = = ( VIDEO_SCANNER_MAX_HORZ - 1 ) )
g_nLastColumnPixelNTSC = 0 ;
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
//===========================================================================
2019-01-09 21:29:36 +00:00
static void updateScreenSingleLores40Simplified ( long cycles6502 )
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
g_pFuncUpdateTextScreen ( cycles6502 ) ;
return ;
}
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
uint16_t addr = getVideoScannerAddressTXT ( ) ;
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint16_t addr = getVideoScannerAddressTXT ( ) ;
UpdateLoResCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress ) ;
g_pVideoAddress + = 14 ;
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
2015-01-03 04:00:24 +00:00
void updateScreenSingleLores40 ( long cycles6502 )
2014-12-31 22:53:55 +00:00
{
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED )
{
2015-01-03 03:46:40 +00:00
g_pFuncUpdateTextScreen ( cycles6502 ) ;
2014-12-31 22:53:55 +00:00
return ;
}
2015-01-02 07:50:04 +00:00
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressTXT ( ) ;
2014-12-31 22:53:55 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
2014-12-31 22:53:55 +00:00
{
g_nColorBurstPixels = 1024 ;
}
else if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
2015-01-01 19:59:54 +00:00
uint8_t m = pMain [ 0 ] ;
2015-01-02 07:50:04 +00:00
uint16_t lo = getLoResBits ( m ) ;
uint16_t bits = lo > > ( ( 1 - ( g_nVideoClockHorz & 1 ) ) * 2 ) ;
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
//===========================================================================
2015-01-03 04:00:24 +00:00
void updateScreenText40 ( long cycles6502 )
2014-12-31 22:53:55 +00:00
{
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressTXT ( ) ;
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
if ( g_nColorBurstPixels > 0 )
g_nColorBurstPixels - = 1 ;
}
else if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
2015-01-02 07:50:04 +00:00
uint8_t m = pMain [ 0 ] ;
uint8_t c = getCharSetBits ( m ) ;
uint16_t bits = g_aPixelDoubleMaskHGR [ c & 0x7F ] ; // Optimization: hgrbits second 128 entries are mirror of first 128
2015-01-03 19:45:44 +00:00
if ( 0 = = g_nVideoCharSet & & 0x40 = = ( m & 0xC0 ) ) // Flash only if mousetext not active
2015-01-02 07:50:04 +00:00
bits ^ = g_nTextFlashMask ;
2015-01-03 19:45:44 +00:00
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2015-01-02 07:50:04 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
2015-01-02 07:50:04 +00:00
}
2020-09-01 20:32:44 +00:00
//===========================================================================
void updateScreenText40RGB ( long cycles6502 )
{
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
uint16_t addr = getVideoScannerAddressTXT ( ) ;
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
if ( g_nColorBurstPixels > 0 )
g_nColorBurstPixels - = 1 ;
}
else if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint8_t * pMain = MemGetMainPtr ( addr ) ;
uint8_t m = pMain [ 0 ] ;
uint8_t c = getCharSetBits ( m ) ;
if ( 0 = = g_nVideoCharSet & & 0x40 = = ( m & 0xC0 ) ) // Flash only if mousetext not active
c ^ = g_nTextFlashMask ;
2020-09-27 13:09:02 +00:00
UpdateText40ColorCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress , c , m ) ;
2020-09-01 20:32:44 +00:00
g_pVideoAddress + = 14 ;
}
}
updateVideoScannerHorzEOLSimple ( ) ;
}
}
2015-01-02 07:50:04 +00:00
//===========================================================================
2015-01-03 04:00:24 +00:00
void updateScreenText80 ( long cycles6502 )
2015-01-02 07:50:04 +00:00
{
2015-01-03 11:31:19 +00:00
for ( ; cycles6502 > 0 ; - - cycles6502 )
2014-12-31 22:53:55 +00:00
{
2018-08-05 21:19:51 +00:00
uint16_t addr = getVideoScannerAddressTXT ( ) ;
2014-12-31 22:53:55 +00:00
2015-01-02 07:50:04 +00:00
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
2014-12-31 22:53:55 +00:00
{
2015-01-02 07:50:04 +00:00
if ( g_nColorBurstPixels > 0 )
g_nColorBurstPixels - = 1 ;
}
else if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
2014-12-31 22:53:55 +00:00
{
2015-01-04 02:32:34 +00:00
uint8_t * pMain = MemGetMainPtr ( addr ) ;
uint8_t * pAux = MemGetAuxPtr ( addr ) ;
2015-01-02 07:50:04 +00:00
uint8_t m = pMain [ 0 ] ;
uint8_t a = pAux [ 0 ] ;
uint16_t main = getCharSetBits ( m ) ;
uint16_t aux = getCharSetBits ( a ) ;
2015-01-03 19:45:44 +00:00
if ( ( 0 = = g_nVideoCharSet ) & & 0x40 = = ( m & 0xC0 ) ) // Flash only if mousetext not active
2015-01-02 07:50:04 +00:00
main ^ = g_nTextFlashMask ;
2015-01-03 19:45:44 +00:00
if ( ( 0 = = g_nVideoCharSet ) & & 0x40 = = ( a & 0xC0 ) ) // Flash only if mousetext not active
2015-01-02 07:50:04 +00:00
aux ^ = g_nTextFlashMask ;
2018-05-24 20:34:26 +00:00
uint16_t bits = ( main < < 7 ) | ( aux & 0x7f ) ;
2020-12-28 16:25:29 +00:00
if ( ( GetVideo ( ) . GetVideoType ( ) ! = VT_COLOR_IDEALIZED ) // No extra 14M bit needed for VT_COLOR_IDEALIZED
& & ( GetVideo ( ) . GetVideoType ( ) ! = VT_COLOR_VIDEOCARD_RGB ) )
2019-01-09 21:29:36 +00:00
bits = ( bits < < 1 ) | g_nLastColumnPixelNTSC ; // GH#555: Align TEXT80 chars with DHGR
2020-08-17 17:11:31 +00:00
2015-01-03 04:03:57 +00:00
updatePixels ( bits ) ;
2018-05-24 20:34:26 +00:00
g_nLastColumnPixelNTSC = ( bits > > 14 ) & 1 ;
2014-12-31 22:53:55 +00:00
}
}
2015-01-03 04:00:24 +00:00
updateVideoScannerHorzEOL ( ) ;
2014-12-31 22:53:55 +00:00
}
}
2020-09-01 20:32:44 +00:00
//===========================================================================
void updateScreenText80RGB ( long cycles6502 )
{
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
uint16_t addr = getVideoScannerAddressTXT ( ) ;
if ( ( g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_COLORBURST_BEG ) )
{
if ( g_nColorBurstPixels > 0 )
g_nColorBurstPixels - = 1 ;
}
else if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY )
{
if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint8_t * pMain = MemGetMainPtr ( addr ) ;
uint8_t * pAux = MemGetAuxPtr ( addr ) ;
uint8_t m = pMain [ 0 ] ;
uint8_t a = pAux [ 0 ] ;
uint16_t main = getCharSetBits ( m ) ;
uint16_t aux = getCharSetBits ( a ) ;
if ( ( 0 = = g_nVideoCharSet ) & & 0x40 = = ( m & 0xC0 ) ) // Flash only if mousetext not active
main ^ = g_nTextFlashMask ;
if ( ( 0 = = g_nVideoCharSet ) & & 0x40 = = ( a & 0xC0 ) ) // Flash only if mousetext not active
aux ^ = g_nTextFlashMask ;
2020-09-27 13:09:02 +00:00
UpdateText80ColorCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress , ( uint8_t ) aux , a ) ;
2020-09-01 20:32:44 +00:00
g_pVideoAddress + = 7 ;
2020-09-27 13:09:02 +00:00
UpdateText80ColorCell ( g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START , g_nVideoClockVert , addr , g_pVideoAddress , ( uint8_t ) main , m ) ;
2020-09-01 20:32:44 +00:00
g_pVideoAddress + = 7 ;
uint16_t bits = ( main < < 7 ) | ( aux & 0x7f ) ;
g_nLastColumnPixelNTSC = ( bits > > 14 ) & 1 ;
}
}
updateVideoScannerHorzEOL ( ) ;
}
}
2021-11-30 21:41:02 +00:00
//===========================================================================
void updateScreenSHR ( long cycles6502 )
{
for ( ; cycles6502 > 0 ; - - cycles6502 )
{
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY_IIGS )
{
2022-10-02 15:28:03 +00:00
uint16_t addr = getVideoScannerAddressSHR ( ) ;
2021-11-30 21:41:02 +00:00
if ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START )
{
uint32_t * pAux = ( uint32_t * ) MemGetAuxPtr ( addr ) ; // 8 pixels (320 mode) / 16 pixels (640 mode)
uint32_t a = pAux [ 0 ] ;
uint8_t * pControl = MemGetAuxPtr ( 0x9D00 + g_nVideoClockVert ) ; // scan-line control byte
uint8_t c = pControl [ 0 ] ;
2022-02-05 22:02:26 +00:00
bool is640Mode = ! ! ( c & 0x80 ) ;
bool isColorFillMode = ! ! ( c & 0x20 ) ;
2021-11-30 21:41:02 +00:00
UINT paletteSelectCode = c & 0xf ;
const UINT kColorsPerPalette = 16 ;
const UINT kColorSize = 2 ;
uint16_t addrPalette = 0x9E00 + paletteSelectCode * kColorsPerPalette * kColorSize ;
VidHDCard : : UpdateSHRCell ( is640Mode , isColorFillMode , addrPalette , g_pVideoAddress , a ) ;
g_pVideoAddress + = 16 ;
}
}
updateVideoScannerHorzEOL_SHR ( ) ;
}
}
2015-01-05 16:39:29 +00:00
// Functions (Public) _____________________________________________________________________________
2015-01-03 04:58:27 +00:00
2015-01-08 20:05:23 +00:00
//===========================================================================
uint32_t * NTSC_VideoGetChromaTable ( bool bHueTypeMonochrome , bool bMonitorTypeColorTV )
{
if ( bHueTypeMonochrome )
{
g_nChromaSize = sizeof ( g_aBnwColorTV ) ;
if ( bMonitorTypeColorTV )
return ( uint32_t * ) g_aBnwColorTV ;
else
return ( uint32_t * ) g_aBnWMonitor ;
} else {
g_nChromaSize = sizeof ( g_aHueColorTV ) ;
if ( bMonitorTypeColorTV )
return ( uint32_t * ) g_aHueColorTV ;
else
2015-01-10 19:24:27 +00:00
# if ALT_TABLE
g_nChromaSize = sizeof ( T_NTSC ) ;
return ( uint32_t * ) T_NTSC ;
# endif
2015-01-08 20:05:23 +00:00
return ( uint32_t * ) g_aHueMonitor ;
}
}
//===========================================================================
2018-02-02 20:19:48 +00:00
void NTSC_VideoClockResync ( const DWORD dwCyclesThisFrame )
{
2019-06-28 20:34:34 +00:00
g_nVideoClockVert = ( uint16_t ) ( dwCyclesThisFrame / VIDEO_SCANNER_MAX_HORZ ) % g_videoScannerMaxVert ;
g_nVideoClockHorz = ( uint16_t ) ( dwCyclesThisFrame % VIDEO_SCANNER_MAX_HORZ ) ;
2018-02-02 20:19:48 +00:00
}
2015-01-08 20:05:23 +00:00
2018-02-02 20:19:48 +00:00
//===========================================================================
uint16_t NTSC_VideoGetScannerAddress ( const ULONG uExecutedCycles )
2016-11-17 19:52:49 +00:00
{
2018-02-02 20:19:48 +00:00
if ( g_bFullSpeed )
{
// Ensure that NTSC video-scanner gets updated during full-speed, so video-dependent Apple II code doesn't hang
NTSC_VideoClockResync ( CpuGetCyclesThisVideoFrame ( uExecutedCycles ) ) ;
}
2016-12-11 15:27:07 +00:00
const uint16_t currVideoClockVert = g_nVideoClockVert ;
const uint16_t currVideoClockHorz = g_nVideoClockHorz ;
// Required for ANSI STORY (end credits) vert scrolling mid-scanline mixed mode: DGR80, TEXT80, DGR80
2019-06-15 17:15:00 +00:00
g_nVideoClockHorz - = 1 ;
2016-12-11 15:27:07 +00:00
if ( ( SHORT ) g_nVideoClockHorz < 0 )
{
g_nVideoClockHorz + = VIDEO_SCANNER_MAX_HORZ ;
g_nVideoClockVert - = 1 ;
if ( ( SHORT ) g_nVideoClockVert < 0 )
2019-06-28 20:34:34 +00:00
g_nVideoClockVert = g_videoScannerMaxVert - 1 ;
2016-12-11 15:27:07 +00:00
}
2022-10-02 19:23:54 +00:00
uint16_t addr = getVideoScannerAddressTXTorHGR ( ) ;
2016-12-11 15:27:07 +00:00
g_nVideoClockVert = currVideoClockVert ;
g_nVideoClockHorz = currVideoClockHorz ;
2015-01-08 20:05:23 +00:00
2016-12-11 15:27:07 +00:00
return addr ;
2015-01-08 20:05:23 +00:00
}
2023-01-14 14:08:42 +00:00
void NTSC_GetVideoVertHorzForDebugger ( uint16_t & vert , uint16_t & horz )
2019-09-21 15:37:45 +00:00
{
ResetCyclesExecutedForDebugger ( ) ; // if in full-speed, then reset cycles so that CpuCalcCycles() doesn't ASSERT
2022-10-02 19:23:54 +00:00
NTSC_VideoGetScannerAddress ( 0 ) ;
2023-01-12 21:51:08 +00:00
vert = g_nVideoClockVert ;
horz = g_nVideoClockHorz ;
2019-09-21 15:37:45 +00:00
}
2023-01-14 14:08:42 +00:00
uint16_t NTSC_GetVideoVertForDebugger ( void )
2023-01-13 21:19:10 +00:00
{
uint16_t vert , horz ;
2023-01-14 14:08:42 +00:00
NTSC_GetVideoVertHorzForDebugger ( vert , horz ) ;
2023-01-13 21:19:10 +00:00
return vert ;
}
2015-01-03 04:58:27 +00:00
//===========================================================================
void NTSC_SetVideoTextMode ( int cols )
{
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB )
2020-09-01 20:32:44 +00:00
{
if ( cols = = 40 )
g_pFuncUpdateTextScreen = updateScreenText40RGB ;
else
g_pFuncUpdateTextScreen = updateScreenText80RGB ;
}
else if ( cols = = 40 )
2015-01-03 04:58:27 +00:00
g_pFuncUpdateTextScreen = updateScreenText40 ;
else
g_pFuncUpdateTextScreen = updateScreenText80 ;
}
//===========================================================================
2019-06-15 17:15:00 +00:00
void NTSC_SetVideoMode ( uint32_t uVideoModeFlags , bool bDelay /*=false*/ )
2015-01-03 04:58:27 +00:00
{
2022-10-02 15:28:03 +00:00
g_uNewVideoModeFlags = uVideoModeFlags ;
2021-11-30 21:41:02 +00:00
if ( uVideoModeFlags & VF_SHR )
{
g_pFuncUpdateGraphicsScreen = updateScreenSHR ;
g_pFuncUpdateTextScreen = updateScreenSHR ;
return ;
}
if ( g_pFuncUpdateGraphicsScreen = = updateScreenSHR & & ! ( uVideoModeFlags & VF_SHR ) )
{
// Was SHR mode, so clear the framebuffer to remove any SHR residue in the borders
GetVideo ( ) . ClearFrameBuffer ( ) ;
}
2019-07-27 18:59:45 +00:00
if ( bDelay & & ! g_bFullSpeed )
2019-06-15 17:15:00 +00:00
{
2019-07-27 18:59:45 +00:00
// (GH#670) NB. if g_bFullSpeed then NTSC_VideoUpdateCycles() won't be called on the next 6502 opcode.
// - Instead it's called when !g_bFullSpeed (eg. drive motor off), then the stale g_uNewVideoModeFlags will get used for NTSC_SetVideoMode()!
2019-06-15 17:15:00 +00:00
g_bDelayVideoMode = true ;
return ;
}
2016-11-06 21:59:45 +00:00
g_nVideoMixed = uVideoModeFlags & VF_MIXED ;
2020-12-28 16:25:29 +00:00
g_nVideoCharSet = GetVideo ( ) . VideoGetSWAltCharSet ( ) ? 1 : 0 ;
2015-01-03 04:58:27 +00:00
2020-09-01 20:32:44 +00:00
RGB_DisableTextFB ( ) ;
2015-01-03 04:58:27 +00:00
g_nTextPage = 1 ;
g_nHiresPage = 1 ;
2019-01-09 21:29:36 +00:00
if ( uVideoModeFlags & VF_PAGE2 )
{
2016-09-16 22:05:39 +00:00
// Apple IIe, Technical Notes, #3: Double High-Resolution Graphics
2015-01-05 16:39:29 +00:00
// 80STORE must be OFF to display page 2
2019-01-09 21:29:36 +00:00
if ( 0 = = ( uVideoModeFlags & VF_80STORE ) )
{
2015-01-03 04:58:27 +00:00
g_nTextPage = 2 ;
g_nHiresPage = 2 ;
}
}
2022-01-05 05:40:23 +00:00
if ( uVideoModeFlags & VF_PAGE0 ) // Pseudo page ($0000)
{
g_nHiresPage = 0 ;
}
if ( uVideoModeFlags & VF_PAGE3 ) // Pseudo page ($6000)
{
g_nHiresPage = 3 ;
}
if ( uVideoModeFlags & VF_PAGE4 ) // Pseudo page ($8000)
{
g_nHiresPage = 4 ;
}
if ( uVideoModeFlags & VF_PAGE5 ) // Pseudo page ($A000)
{
g_nHiresPage = 5 ;
}
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoRefreshRate ( ) = = VR_50HZ & & g_pVideoAddress ) // GH#763 / NB. g_pVideoAddress==NULL when called via VideoResetState()
2020-08-17 17:11:31 +00:00
{
if ( uVideoModeFlags & VF_TEXT )
{
g_nColorBurstPixels = 0 ; // (For mid-line video mode change) Instantaneously kill color-burst! (not correct as TV's can take many lines)
// Switching mid-line from graphics to TEXT
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_MONITOR_NTSC & &
2020-09-01 20:32:44 +00:00
g_pFuncUpdateGraphicsScreen ! = updateScreenText40 & & g_pFuncUpdateGraphicsScreen ! = updateScreenText40RGB
& & g_pFuncUpdateGraphicsScreen ! = updateScreenText80 & & g_pFuncUpdateGraphicsScreen ! = updateScreenText80RGB )
2020-08-17 17:11:31 +00:00
{
2020-08-18 16:40:56 +00:00
* ( uint32_t * ) & g_pVideoAddress [ 0 ] = 0 ; // blank out any stale pixel data, eg. ANSI STORY (at end credits)
* ( uint32_t * ) & g_pVideoAddress [ 1 ] = 0 ;
g_pVideoAddress + = 2 ; // eg. FT's TRIBU demo & ANSI STORY (at "turn the disk over!")
2020-08-17 17:11:31 +00:00
}
}
else
{
2022-10-02 20:52:13 +00:00
if ( ! g_nVideoMixed | | g_nVideoClockVert < VIDEO_SCANNER_Y_MIXED ) // 50HZ(PAL) will kill color-burst if 'mixed and >=160' - so don't re-enable color-burst! (GH#1131)
g_nColorBurstPixels = 1024 ; // (For mid-line video mode change)
2020-08-17 17:11:31 +00:00
// Switching mid-line from TEXT to graphics
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_MONITOR_NTSC & &
2020-09-01 20:32:44 +00:00
( g_pFuncUpdateGraphicsScreen = = updateScreenText40 | | g_pFuncUpdateGraphicsScreen = = updateScreenText40RGB
| | g_pFuncUpdateGraphicsScreen = = updateScreenText80 | | g_pFuncUpdateGraphicsScreen = = updateScreenText80RGB ) )
2020-08-17 17:11:31 +00:00
{
2020-08-18 16:40:56 +00:00
g_pVideoAddress - = 2 ; // eg. FT's TRIBU demo & ANSI STORY (at "turn the disk over!")
2020-08-17 17:11:31 +00:00
}
}
}
2020-09-01 20:32:44 +00:00
// Video7_SL7 extra RGB modes handling
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB
2020-09-01 20:32:44 +00:00
& & RGB_GetVideocard ( ) = = RGB_Videocard_e : : Video7_SL7
// Exclude following modes (fallback through regular NTSC rendering with RGB text)
// VF_DHIRES = 1 -> regular Apple IIe modes
// VF_DHIRES = 0 and VF_TEXT=0, VF_DHIRES=1, VF_80COL=1 -> DHIRES modes, setup by F1/F2
& & ! ( ! ( uVideoModeFlags & VF_DHIRES ) | |
( ( uVideoModeFlags & VF_DHIRES ) & & ! ( uVideoModeFlags & VF_TEXT ) & & ( uVideoModeFlags & VF_DHIRES ) & & ( uVideoModeFlags & VF_80COL ) )
)
)
{
RGB_EnableTextFB ( ) ; // F/B text only shows in 40col mode anyway
// ----- Video-7 SL7 extra modes ----- (from the videocard manual)
// AN3 TEXT HIRES 80COL
// 0 1 ? 0 F/B Text
// 0 1 ? 1 80 col Text
// 0 0 0 0 LoRes (mixed with F/B Text)
// 0 0 0 1 DLoRes (mixed with 80 col. Text)
// 0 0 1 0 F/B HiRes (mixed with F/B Text)
if ( uVideoModeFlags & VF_TEXT )
{
if ( uVideoModeFlags & VF_80COL )
{
// 80 col text
g_pFuncUpdateGraphicsScreen = updateScreenText80RGB ;
}
else
{
g_pFuncUpdateGraphicsScreen = updateScreenText40RGB ;
}
}
else if ( uVideoModeFlags & VF_HIRES )
{
// F/B HiRes
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40Duochrome ;
g_pFuncUpdateTextScreen = updateScreenText40RGB ;
}
else if ( uVideoModeFlags & VF_80COL )
{
// DLoRes
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores80Simplified ;
g_pFuncUpdateTextScreen = updateScreenText80RGB ;
}
else
{
// LoRes + F/B Text
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40Simplified ;
g_pFuncUpdateTextScreen = updateScreenText40RGB ;
}
}
// Regular NTSC modes
else if ( uVideoModeFlags & VF_TEXT )
2019-01-09 21:29:36 +00:00
{
2016-11-06 21:59:45 +00:00
if ( uVideoModeFlags & VF_80COL )
2020-09-01 20:32:44 +00:00
{
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB )
2020-09-01 20:32:44 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenText80RGB ;
else
g_pFuncUpdateGraphicsScreen = updateScreenText80 ;
}
2020-12-28 16:25:29 +00:00
else if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB )
2020-09-01 20:32:44 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenText40RGB ;
2015-01-03 04:58:27 +00:00
else
g_pFuncUpdateGraphicsScreen = updateScreenText40 ;
}
2019-01-09 21:29:36 +00:00
else if ( uVideoModeFlags & VF_HIRES )
{
2016-11-06 21:59:45 +00:00
if ( uVideoModeFlags & VF_DHIRES )
2019-01-09 21:29:36 +00:00
{
2016-11-06 21:59:45 +00:00
if ( uVideoModeFlags & VF_80COL )
2019-01-09 21:29:36 +00:00
{
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_IDEALIZED )
2019-01-09 21:29:36 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires80Simplified ;
2020-12-28 16:25:29 +00:00
else if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB )
2020-09-27 13:09:02 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires80RGB ;
2019-01-09 21:29:36 +00:00
else
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires80 ;
}
2015-01-03 04:58:27 +00:00
else
2019-01-09 21:29:36 +00:00
{
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_IDEALIZED )
2020-10-17 11:31:44 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenHires40Simplified ; // handles both Single/Double Hires40 (EG. FT's DIGIDREAM demo)
2020-12-28 16:25:29 +00:00
// else if (GetVideo().GetVideoType() == VT_COLOR_VIDEOCARD_RGB)
2020-10-17 11:31:44 +00:00
// // TODO
else
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires40 ;
2019-01-09 21:29:36 +00:00
}
}
2015-01-03 04:58:27 +00:00
else
2019-01-09 21:29:36 +00:00
{
2020-12-28 16:25:29 +00:00
if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_IDEALIZED )
2020-10-17 11:31:44 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenHires40Simplified ;
2020-12-28 16:25:29 +00:00
else if ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB )
2020-09-27 13:09:02 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40RGB ;
2019-01-09 21:29:36 +00:00
else
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40 ;
}
2015-01-03 04:58:27 +00:00
}
2019-01-09 21:29:36 +00:00
else
{
2016-11-06 21:59:45 +00:00
if ( uVideoModeFlags & VF_DHIRES )
2019-01-09 21:29:36 +00:00
{
2016-11-06 21:59:45 +00:00
if ( uVideoModeFlags & VF_80COL )
2019-01-09 21:29:36 +00:00
{
2020-12-28 16:25:29 +00:00
if ( ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_IDEALIZED ) | | ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB ) )
2019-01-09 21:29:36 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores80Simplified ;
else
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores80 ;
}
2015-01-03 04:58:27 +00:00
else
2019-01-09 21:29:36 +00:00
{
2015-01-03 04:58:27 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores40 ;
2019-01-09 21:29:36 +00:00
}
}
2015-01-03 04:58:27 +00:00
else
2019-01-09 21:29:36 +00:00
{
2020-12-28 16:25:29 +00:00
if ( ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_IDEALIZED ) | | ( GetVideo ( ) . GetVideoType ( ) = = VT_COLOR_VIDEOCARD_RGB ) )
2019-01-09 21:29:36 +00:00
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40Simplified ;
else
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40 ;
}
2015-01-03 04:58:27 +00:00
}
}
//===========================================================================
2019-08-09 21:58:12 +00:00
2020-08-17 17:11:31 +00:00
void NTSC_SetVideoStyle ( void )
2015-01-03 04:58:27 +00:00
{
2020-12-28 16:25:29 +00:00
const bool half = GetVideo ( ) . IsVideoStyle ( VS_HALF_SCANLINES ) ;
const VideoRefreshRate_e refresh = GetVideo ( ) . GetVideoRefreshRate ( ) ;
2015-01-03 04:58:27 +00:00
uint8_t r , g , b ;
2020-12-28 16:25:29 +00:00
switch ( GetVideo ( ) . GetVideoType ( ) )
2015-01-03 04:58:27 +00:00
{
2016-05-17 21:03:45 +00:00
case VT_COLOR_TV :
2016-04-04 21:15:47 +00:00
r = 0xFF ;
g = 0xFF ;
b = 0xFF ;
updateMonochromeTables ( r , g , b ) ;
2015-01-03 04:58:27 +00:00
if ( half )
{
g_pFuncUpdateBnWPixel = updatePixelBnWColorTVSingleScanline ;
g_pFuncUpdateHuePixel = updatePixelHueColorTVSingleScanline ;
}
2020-08-17 17:11:31 +00:00
else
{
2015-01-03 04:58:27 +00:00
g_pFuncUpdateBnWPixel = updatePixelBnWColorTVDoubleScanline ;
g_pFuncUpdateHuePixel = updatePixelHueColorTVDoubleScanline ;
}
break ;
2019-01-09 21:29:36 +00:00
case VT_COLOR_MONITOR_NTSC :
2015-01-03 04:58:27 +00:00
default :
2016-04-04 21:15:47 +00:00
r = 0xFF ;
g = 0xFF ;
b = 0xFF ;
updateMonochromeTables ( r , g , b ) ;
2015-01-03 04:58:27 +00:00
if ( half )
{
g_pFuncUpdateBnWPixel = updatePixelBnWMonitorSingleScanline ;
g_pFuncUpdateHuePixel = updatePixelHueMonitorSingleScanline ;
}
2020-08-17 17:11:31 +00:00
else
{
2015-01-03 04:58:27 +00:00
g_pFuncUpdateBnWPixel = updatePixelBnWMonitorDoubleScanline ;
g_pFuncUpdateHuePixel = updatePixelHueMonitorDoubleScanline ;
}
break ;
2016-05-17 21:03:45 +00:00
case VT_MONO_TV :
2015-01-03 04:58:27 +00:00
r = 0xFF ;
g = 0xFF ;
b = 0xFF ;
updateMonochromeTables ( r , g , b ) ; // Custom Monochrome color
if ( half )
g_pFuncUpdateBnWPixel = g_pFuncUpdateHuePixel = updatePixelBnWColorTVSingleScanline ;
2020-08-17 17:11:31 +00:00
else
2015-01-03 04:58:27 +00:00
g_pFuncUpdateBnWPixel = g_pFuncUpdateHuePixel = updatePixelBnWColorTVDoubleScanline ;
break ;
case VT_MONO_AMBER :
r = 0xFF ;
g = 0x80 ;
b = 0x00 ;
goto _mono ;
case VT_MONO_GREEN :
r = 0x00 ;
g = 0xC0 ;
b = 0x00 ;
goto _mono ;
2020-10-17 13:26:33 +00:00
case VT_COLOR_IDEALIZED :
2020-09-27 13:09:02 +00:00
case VT_COLOR_VIDEOCARD_RGB :
2015-01-03 04:58:27 +00:00
case VT_MONO_WHITE :
r = 0xFF ;
g = 0xFF ;
b = 0xFF ;
goto _mono ;
2016-05-17 21:03:45 +00:00
case VT_MONO_CUSTOM :
2015-01-03 04:58:27 +00:00
// From WinGDI.h
// #define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
//#define GetRValue(rgb) (LOBYTE(rgb))
//#define GetGValue(rgb) (LOBYTE(((WORD)(rgb)) >> 8))
//#define GetBValue(rgb) (LOBYTE((rgb)>>16))
2020-12-28 16:25:29 +00:00
r = ( GetVideo ( ) . GetMonochromeRGB ( ) > > 0 ) & 0xFF ;
g = ( GetVideo ( ) . GetMonochromeRGB ( ) > > 8 ) & 0xFF ;
b = ( GetVideo ( ) . GetMonochromeRGB ( ) > > 16 ) & 0xFF ;
2015-01-03 04:58:27 +00:00
_mono :
updateMonochromeTables ( r , g , b ) ; // Custom Monochrome color
if ( half )
g_pFuncUpdateBnWPixel = g_pFuncUpdateHuePixel = updatePixelBnWMonitorSingleScanline ;
else
g_pFuncUpdateBnWPixel = g_pFuncUpdateHuePixel = updatePixelBnWMonitorDoubleScanline ;
break ;
2022-05-29 21:00:20 +00:00
}
ClearOverscanVideoArea ( ) ;
2015-01-03 04:58:27 +00:00
}
2014-12-31 22:53:55 +00:00
//===========================================================================
2020-11-10 20:33:55 +00:00
static void GenerateVideoTables ( void ) ;
static void GenerateBaseColors ( baseColors_t pBaseNtscColors ) ;
2018-08-02 21:37:20 +00:00
2020-10-11 20:29:26 +00:00
void NTSC_Destroy ( void )
{
2020-12-28 16:25:29 +00:00
// After a VM restart, this will point to an old FrameBuffer
2020-10-11 20:29:26 +00:00
// - if it's now unmapped then this can cause a crash in NTSC_SetVideoMode()!
g_pVideoAddress = 0 ;
2021-01-03 16:21:24 +00:00
g_kFrameBufferWidth = 0 ;
memset ( g_pScanLines , 0 , sizeof ( g_pScanLines ) ) ;
2020-10-11 20:29:26 +00:00
}
2015-01-02 06:04:38 +00:00
void NTSC_VideoInit ( uint8_t * pFramebuffer ) // wsVideoInit
2014-12-31 22:53:55 +00:00
{
2015-01-02 06:04:38 +00:00
make_csbits ( ) ;
2018-08-02 21:37:20 +00:00
GenerateVideoTables ( ) ;
2015-01-03 04:58:27 +00:00
initPixelDoubleMasks ( ) ;
initChromaPhaseTables ( ) ;
2015-01-03 04:00:24 +00:00
updateMonochromeTables ( 0xFF , 0xFF , 0xFF ) ;
2014-12-31 22:53:55 +00:00
2021-01-03 16:21:24 +00:00
g_kFrameBufferWidth = GetVideo ( ) . GetFrameBufferWidth ( ) ;
2021-11-30 21:41:02 +00:00
for ( int y = 0 ; y < ( VIDEO_SCANNER_Y_DISPLAY_IIGS * 2 ) ; y + + )
2018-05-24 20:34:26 +00:00
{
2020-12-28 16:25:29 +00:00
uint32_t offset = sizeof ( bgra_t ) * GetVideo ( ) . GetFrameBufferWidth ( )
* ( ( GetVideo ( ) . GetFrameBufferHeight ( ) - 1 ) - y - GetVideo ( ) . GetFrameBufferBorderHeight ( ) )
+ ( sizeof ( bgra_t ) * GetVideo ( ) . GetFrameBufferBorderWidth ( ) ) ;
g_pScanLines [ y ] = ( bgra_t * ) ( GetVideo ( ) . GetFrameBuffer ( ) + offset ) ;
2018-05-24 20:34:26 +00:00
}
2015-01-02 06:04:38 +00:00
2015-01-03 03:14:18 +00:00
g_pVideoAddress = g_pScanLines [ 0 ] ;
2015-01-02 06:04:38 +00:00
2015-01-03 04:00:24 +00:00
g_pFuncUpdateTextScreen = updateScreenText40 ;
g_pFuncUpdateGraphicsScreen = updateScreenText40 ;
2015-01-02 07:50:04 +00:00
2021-01-13 22:02:48 +00:00
GetVideo ( ) . VideoReinitialize ( true ) ; // Setup g_pFunc_ntsc*Pixel()
2015-01-02 10:45:47 +00:00
2019-01-09 21:29:36 +00:00
bgra_t baseColors [ kNumBaseColors ] ;
GenerateBaseColors ( & baseColors ) ;
VideoInitializeOriginal ( & baseColors ) ;
2015-01-02 06:04:38 +00:00
# if HGR_TEST_PATTERN
2015-01-06 06:03:49 +00:00
// Init HGR to almost all-possible-combinations
2015-01-02 06:04:38 +00:00
// CALL-151
// C050 C053 C057
unsigned char b = 0 ;
unsigned char * main , * aux ;
2015-01-06 06:03:49 +00:00
uint16_t ad ;
2015-01-02 06:04:38 +00:00
for ( unsigned page = 0 ; page < 2 ; page + + )
{
2015-01-06 06:03:49 +00:00
// for( unsigned w = 0; w < 2; w++ ) // 16 cols
2015-01-02 06:04:38 +00:00
{
for ( unsigned z = 0 ; z < 2 ; z + + ) // 8 cols
{
b = 0 ; // 4 columns * 64 rows
for ( unsigned x = 0 ; x < 4 ; x + + ) // 4 cols
{
for ( unsigned y = 0 ; y < 64 ; y + + ) // 1 col
{
unsigned y2 = y * 2 ;
2015-01-06 06:03:49 +00:00
ad = 0x2000 + ( y2 & 7 ) * 0x400 + ( ( y2 / 8 ) & 7 ) * 0x80 + ( y2 / 64 ) * 0x28 + 2 * x + 10 * z ; // + 20*w;
2015-01-02 06:04:38 +00:00
ad + = 0x2000 * page ;
main = MemGetMainPtr ( ad ) ;
aux = MemGetAuxPtr ( ad ) ;
2015-01-06 06:03:49 +00:00
main [ 0 ] = b ; main [ 1 ] = z + page * 0x80 ;
2015-01-02 06:04:38 +00:00
aux [ 0 ] = z ; aux [ 1 ] = 0 ;
2015-01-06 06:03:49 +00:00
if ( page = = 1 )
{
// Columns = # of consecutive pixels
// x = 0, 1, 2, 3
// # = 3, 5, 7, 9
// b = 3, 7, 15, 31
// = (4 << x) - 1
main [ 0 + z ] = ( 0x80 * ( y / 32 ) + ( ( ( 4 < < x ) - 1 ) < < ( y / 8 ) ) ) ; // (3 | 3+x*2)
main [ 1 + z ] = ( 0x80 * ( y / 32 ) + ( ( ( 4 < < x ) - 1 ) < < ( y / 8 ) ) ) > > 8 ;
}
2015-01-02 06:04:38 +00:00
y2 = y * 2 + 1 ;
2015-01-06 06:03:49 +00:00
ad = 0x2000 + ( y2 & 7 ) * 0x400 + ( ( y2 / 8 ) & 7 ) * 0x80 + ( y2 / 64 ) * 0x28 + 2 * x + 10 * z ; // + 20*w;
2015-01-02 06:04:38 +00:00
ad + = 0x2000 * page ;
main = MemGetMainPtr ( ad ) ;
aux = MemGetAuxPtr ( ad ) ;
2015-01-06 06:03:49 +00:00
main [ 0 ] = 0 ; main [ 1 ] = z + page * 0x80 ;
2015-01-02 06:04:38 +00:00
aux [ 0 ] = b ; aux [ 1 ] = 0 ;
b + + ;
}
}
}
2014-12-31 22:53:55 +00:00
}
}
2015-01-02 06:04:38 +00:00
# endif
2014-12-31 22:53:55 +00:00
2015-01-02 06:04:38 +00:00
}
//===========================================================================
2019-03-16 14:27:40 +00:00
void NTSC_VideoReinitialize ( DWORD cyclesThisFrame , bool bInitVideoScannerAddress )
2016-04-05 21:17:29 +00:00
{
2019-11-03 15:05:28 +00:00
if ( cyclesThisFrame > = g_videoScanner6502Cycles )
{
// Possible, since ContinueExecution() loop waits until: cycles > g_videoScanner6502Cycles && VBL
cyclesThisFrame % = g_videoScanner6502Cycles ;
}
2016-04-05 21:17:29 +00:00
g_nVideoClockVert = ( uint16_t ) ( cyclesThisFrame / VIDEO_SCANNER_MAX_HORZ ) ;
g_nVideoClockHorz = cyclesThisFrame % VIDEO_SCANNER_MAX_HORZ ;
2019-03-16 14:27:40 +00:00
if ( bInitVideoScannerAddress ) // GH#611
updateVideoScannerAddress ( ) ; // Pre-condition: g_nVideoClockVert
2016-04-05 21:17:29 +00:00
}
//===========================================================================
void NTSC_VideoInitAppleType ( )
2015-01-02 06:04:38 +00:00
{
2016-04-12 22:21:05 +00:00
int model = GetApple2Type ( ) ;
2015-01-02 06:04:38 +00:00
2016-03-21 23:48:02 +00:00
// anything other than low bit set means not II/II+ (TC: include Pravets machines too?)
2015-01-02 06:04:38 +00:00
if ( model & 0xFFFE )
g_pHorzClockOffset = APPLE_IIE_HORZ_CLOCK_OFFSET ;
else
g_pHorzClockOffset = APPLE_IIP_HORZ_CLOCK_OFFSET ;
2016-04-12 22:21:05 +00:00
set_csbits ( ) ;
2015-01-02 06:04:38 +00:00
}
2015-01-08 20:05:23 +00:00
//===========================================================================
void NTSC_VideoInitChroma ( )
{
initChromaPhaseTables ( ) ;
}
2015-01-02 06:04:38 +00:00
//===========================================================================
2016-11-16 11:18:41 +00:00
2016-11-17 19:52:49 +00:00
// NB. NTSC video-scanner doesn't get updated during full-speed, so video-dependent Apple II code can hang
2016-11-16 11:18:41 +00:00
//bool NTSC_VideoIsVbl ()
//{
// return (g_nVideoClockVert >= VIDEO_SCANNER_Y_DISPLAY) && (g_nVideoClockVert < VIDEO_SCANNER_MAX_VERT);
//}
2015-01-02 06:04:38 +00:00
//===========================================================================
2016-09-25 09:42:14 +00:00
2019-06-28 20:34:34 +00:00
// Pre: cyclesLeftToUpdate = [0...g_videoScanner6502Cycles]
2016-10-30 12:31:29 +00:00
// . 2-14: After one emulated 6502/65C02 opcode (optionally with IRQ)
// . ~1000: After 1ms of Z80 emulation
// . 17030: From NTSC_VideoRedrawWholeScreen()
static void VideoUpdateCycles ( int cyclesLeftToUpdate )
2016-09-25 09:42:14 +00:00
{
2016-09-25 20:50:07 +00:00
const int cyclesToEndOfLine = VIDEO_SCANNER_MAX_HORZ - g_nVideoClockHorz ;
2016-09-25 09:42:14 +00:00
2016-09-25 20:50:07 +00:00
if ( g_nVideoClockVert < VIDEO_SCANNER_Y_MIXED )
2016-09-25 09:42:14 +00:00
{
2016-09-25 20:50:07 +00:00
const int cyclesToLine160 = VIDEO_SCANNER_MAX_HORZ * ( VIDEO_SCANNER_Y_MIXED - g_nVideoClockVert - 1 ) + cyclesToEndOfLine ;
2016-10-30 12:31:29 +00:00
int cycles = cyclesLeftToUpdate < cyclesToLine160 ? cyclesLeftToUpdate : cyclesToLine160 ;
g_pFuncUpdateGraphicsScreen ( cycles ) ; // lines [currV...159]
cyclesLeftToUpdate - = cycles ;
2016-09-25 20:50:07 +00:00
2019-06-28 20:34:34 +00:00
const int cyclesFromLine160ToLine261 = g_videoScanner6502Cycles - ( VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_Y_MIXED ) ;
2016-10-30 12:31:29 +00:00
cycles = cyclesLeftToUpdate < cyclesFromLine160ToLine261 ? cyclesLeftToUpdate : cyclesFromLine160ToLine261 ;
g_pFuncUpdateGraphicsScreen ( cycles ) ; // lines [160..191..261]
cyclesLeftToUpdate - = cycles ;
2016-09-25 20:50:07 +00:00
// Any remaining cyclesLeftToUpdate: lines [0...currV)
2016-09-25 09:42:14 +00:00
}
else
{
2019-06-28 20:34:34 +00:00
const int cyclesToLine262 = VIDEO_SCANNER_MAX_HORZ * ( g_videoScannerMaxVert - g_nVideoClockVert - 1 ) + cyclesToEndOfLine ;
2016-10-30 12:31:29 +00:00
int cycles = cyclesLeftToUpdate < cyclesToLine262 ? cyclesLeftToUpdate : cyclesToLine262 ;
g_pFuncUpdateGraphicsScreen ( cycles ) ; // lines [currV...261]
cyclesLeftToUpdate - = cycles ;
2016-09-25 20:50:07 +00:00
const int cyclesFromLine0ToLine159 = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_Y_MIXED ;
2016-10-30 12:31:29 +00:00
cycles = cyclesLeftToUpdate < cyclesFromLine0ToLine159 ? cyclesLeftToUpdate : cyclesFromLine0ToLine159 ;
g_pFuncUpdateGraphicsScreen ( cycles ) ; // lines [0..159]
cyclesLeftToUpdate - = cycles ;
2016-09-25 20:50:07 +00:00
// Any remaining cyclesLeftToUpdate: lines [160...currV)
2016-09-25 09:42:14 +00:00
}
2016-09-25 20:50:07 +00:00
if ( cyclesLeftToUpdate )
g_pFuncUpdateGraphicsScreen ( cyclesLeftToUpdate ) ;
2016-10-30 12:31:29 +00:00
}
//===========================================================================
2019-06-28 20:34:34 +00:00
void NTSC_VideoUpdateCycles ( UINT cycles6502 )
2016-10-30 12:31:29 +00:00
{
2020-05-23 15:23:06 +00:00
# ifdef LOG_PERF_TIMINGS
extern UINT64 g_timeVideo ;
PerfMarker perfMarker ( g_timeVideo ) ;
# endif
2019-06-28 20:34:34 +00:00
_ASSERT ( cycles6502 & & cycles6502 < g_videoScanner6502Cycles ) ; // Use NTSC_VideoRedrawWholeScreen() instead
2019-06-15 17:15:00 +00:00
if ( g_bDelayVideoMode )
{
VideoUpdateCycles ( 1 ) ; // Video mode change is delayed by 1 cycle
g_bDelayVideoMode = false ;
NTSC_SetVideoMode ( g_uNewVideoModeFlags ) ;
cycles6502 - - ;
if ( ! cycles6502 )
return ;
}
2016-10-30 12:31:29 +00:00
VideoUpdateCycles ( cycles6502 ) ;
}
//===========================================================================
void NTSC_VideoRedrawWholeScreen ( void )
{
# ifdef _DEBUG
const uint16_t currVideoClockVert = g_nVideoClockVert ;
const uint16_t currVideoClockHorz = g_nVideoClockHorz ;
# endif
2017-04-23 20:37:01 +00:00
// (GH#405) For full-speed: whole screen updates will occur periodically
// . The V/H pos will have been recalc'ed, so won't be continuous from previous (whole screen) update
// . So the redraw must start at H-pos=0 & with the usual reinit for the start of a new line
const uint16_t horz = g_nVideoClockHorz ;
g_nVideoClockHorz = 0 ;
updateVideoScannerAddress ( ) ;
2019-06-28 20:34:34 +00:00
VideoUpdateCycles ( g_videoScanner6502Cycles ) ;
2016-09-25 20:50:07 +00:00
2017-04-23 20:37:01 +00:00
VideoUpdateCycles ( horz ) ; // Finally update to get to correct H-pos
2016-09-25 20:50:07 +00:00
# ifdef _DEBUG
_ASSERT ( currVideoClockVert = = g_nVideoClockVert ) ;
_ASSERT ( currVideoClockHorz = = g_nVideoClockHorz ) ;
# endif
2016-09-25 09:42:14 +00:00
}
2016-10-02 21:22:10 +00:00
2018-08-02 21:37:20 +00:00
//===========================================================================
2018-08-05 21:19:51 +00:00
static bool CheckVideoTables2 ( eApple2Type type , uint32_t mode )
2018-08-02 21:37:20 +00:00
{
2018-08-05 21:19:51 +00:00
SetApple2Type ( type ) ;
NTSC_VideoInitAppleType ( ) ;
2018-08-02 21:37:20 +00:00
2020-12-28 16:25:29 +00:00
GetVideo ( ) . SetVideoMode ( mode ) ;
2018-08-05 21:19:51 +00:00
g_nVideoClockHorz = g_nVideoClockVert = 0 ;
for ( DWORD cycles = 0 ; cycles < VIDEO_SCANNER_MAX_VERT * VIDEO_SCANNER_MAX_HORZ ; cycles + + )
2018-08-02 21:37:20 +00:00
{
2020-12-28 16:25:29 +00:00
WORD addr1 = GetVideo ( ) . VideoGetScannerAddress ( cycles ) ;
WORD addr2 = GetVideo ( ) . GetVideoMode ( ) & VF_TEXT ? getVideoScannerAddressTXT ( )
: getVideoScannerAddressHGR ( ) ;
2018-08-05 21:19:51 +00:00
_ASSERT ( addr1 = = addr2 ) ;
if ( addr1 ! = addr2 )
{
2022-02-13 21:37:05 +00:00
LogOutput ( " vpos=%04X, hpos=%02X, Video_adr=$%04X, NTSC_adr=$%04X \n " , g_nVideoClockVert , g_nVideoClockHorz , addr1 , addr2 ) ;
2018-08-05 21:19:51 +00:00
return false ;
}
g_nVideoClockHorz + + ;
if ( g_nVideoClockHorz = = VIDEO_SCANNER_MAX_HORZ )
{
g_nVideoClockHorz = 0 ;
g_nVideoClockVert + + ;
}
2018-08-02 21:37:20 +00:00
}
2018-08-05 21:19:51 +00:00
return true ;
}
static void CheckVideoTables ( void )
{
CheckVideoTables2 ( A2TYPE_APPLE2PLUS , VF_HIRES ) ;
CheckVideoTables2 ( A2TYPE_APPLE2PLUS , VF_TEXT ) ;
CheckVideoTables2 ( A2TYPE_APPLE2E , VF_HIRES ) ;
CheckVideoTables2 ( A2TYPE_APPLE2E , VF_TEXT ) ;
}
2019-06-28 20:34:34 +00:00
static bool IsNTSC ( void )
{
return g_videoScannerMaxVert = = VIDEO_SCANNER_MAX_VERT ;
}
2018-08-05 21:19:51 +00:00
static void GenerateVideoTables ( void )
{
eApple2Type currentApple2Type = GetApple2Type ( ) ;
2020-12-28 16:25:29 +00:00
uint32_t currentVideoMode = GetVideo ( ) . GetVideoMode ( ) ;
2019-06-28 20:34:34 +00:00
int currentHiresPage = g_nHiresPage ;
int currentTextPage = g_nTextPage ;
g_nHiresPage = g_nTextPage = 1 ;
2018-08-05 21:19:51 +00:00
//
// g_aClockVertOffsetsHGR[]
//
2020-12-28 16:25:29 +00:00
GetVideo ( ) . SetVideoMode ( VF_HIRES ) ;
2018-08-02 21:37:20 +00:00
{
2019-06-28 20:34:34 +00:00
UINT i = 0 , cycle = VIDEO_SCANNER_HORZ_START ;
for ( ; i < VIDEO_SCANNER_MAX_VERT ; i + + , cycle + = VIDEO_SCANNER_MAX_HORZ )
{
2020-12-28 16:25:29 +00:00
g_aClockVertOffsetsHGR [ i ] = GetVideo ( ) . VideoGetScannerAddress ( cycle , Video : : VS_PartialAddrV ) ;
2019-06-28 20:34:34 +00:00
if ( IsNTSC ( ) ) _ASSERT ( g_aClockVertOffsetsHGR [ i ] = = g_kClockVertOffsetsHGR [ i ] ) ;
}
if ( ! IsNTSC ( ) )
{
for ( ; i < VIDEO_SCANNER_MAX_VERT_PAL ; i + + , cycle + = VIDEO_SCANNER_MAX_HORZ )
2020-12-28 16:25:29 +00:00
g_aClockVertOffsetsHGR [ i ] = GetVideo ( ) . VideoGetScannerAddress ( cycle , Video : : VS_PartialAddrV ) ;
2019-06-28 20:34:34 +00:00
}
2018-08-02 21:37:20 +00:00
}
2018-08-05 21:19:51 +00:00
//
// g_aClockVertOffsetsTXT[]
2018-08-02 21:37:20 +00:00
//
2020-12-28 16:25:29 +00:00
GetVideo ( ) . SetVideoMode ( VF_TEXT ) ;
2018-08-02 21:37:20 +00:00
{
2019-06-28 20:34:34 +00:00
UINT i = 0 , cycle = VIDEO_SCANNER_HORZ_START ;
for ( ; i < ( 256 + 8 ) / 8 ; i + + , cycle + = VIDEO_SCANNER_MAX_HORZ * 8 )
{
2020-12-28 16:25:29 +00:00
g_aClockVertOffsetsTXT [ i ] = GetVideo ( ) . VideoGetScannerAddress ( cycle , Video : : VS_PartialAddrV ) ;
2019-06-28 20:34:34 +00:00
if ( IsNTSC ( ) ) _ASSERT ( g_aClockVertOffsetsTXT [ i ] = = g_kClockVertOffsetsTXT [ i ] ) ;
}
if ( ! IsNTSC ( ) )
{
for ( ; i < VIDEO_SCANNER_MAX_VERT_PAL / 8 ; i + + , cycle + = VIDEO_SCANNER_MAX_HORZ * 8 )
2020-12-28 16:25:29 +00:00
g_aClockVertOffsetsTXT [ i ] = GetVideo ( ) . VideoGetScannerAddress ( cycle , Video : : VS_PartialAddrV ) ;
2019-06-28 20:34:34 +00:00
}
2018-08-02 21:37:20 +00:00
}
2018-08-05 21:19:51 +00:00
//
// APPLE_IIP_HORZ_CLOCK_OFFSET[]
2018-08-02 21:37:20 +00:00
//
2020-12-28 16:25:29 +00:00
GetVideo ( ) . SetVideoMode ( VF_TEXT ) ;
2018-08-05 21:19:51 +00:00
SetApple2Type ( A2TYPE_APPLE2PLUS ) ;
2018-08-02 21:37:20 +00:00
for ( UINT j = 0 ; j < 5 ; j + + )
{
for ( UINT i = 0 , cycle = j * 64 * VIDEO_SCANNER_MAX_HORZ ; i < VIDEO_SCANNER_MAX_HORZ ; i + + , cycle + + )
{
2020-12-28 16:25:29 +00:00
APPLE_IIP_HORZ_CLOCK_OFFSET [ j ] [ i ] = GetVideo ( ) . VideoGetScannerAddress ( cycle , Video : : VS_PartialAddrH ) ;
2019-06-28 20:34:34 +00:00
if ( IsNTSC ( ) ) _ASSERT ( APPLE_IIP_HORZ_CLOCK_OFFSET [ j ] [ i ] = = kAPPLE_IIP_HORZ_CLOCK_OFFSET [ j ] [ i ] ) ;
2018-08-02 21:37:20 +00:00
}
}
2018-08-05 21:19:51 +00:00
//
// APPLE_IIE_HORZ_CLOCK_OFFSET[]
2018-08-02 21:37:20 +00:00
//
2020-12-28 16:25:29 +00:00
GetVideo ( ) . SetVideoMode ( VF_TEXT ) ;
2018-08-02 21:37:20 +00:00
SetApple2Type ( A2TYPE_APPLE2E ) ;
for ( UINT j = 0 ; j < 5 ; j + + )
{
for ( UINT i = 0 , cycle = j * 64 * VIDEO_SCANNER_MAX_HORZ ; i < VIDEO_SCANNER_MAX_HORZ ; i + + , cycle + + )
{
2020-12-28 16:25:29 +00:00
APPLE_IIE_HORZ_CLOCK_OFFSET [ j ] [ i ] = GetVideo ( ) . VideoGetScannerAddress ( cycle , Video : : VS_PartialAddrH ) ;
2019-06-28 20:34:34 +00:00
if ( IsNTSC ( ) ) _ASSERT ( APPLE_IIE_HORZ_CLOCK_OFFSET [ j ] [ i ] = = kAPPLE_IIE_HORZ_CLOCK_OFFSET [ j ] [ i ] ) ;
2018-08-02 21:37:20 +00:00
}
}
2018-08-05 21:19:51 +00:00
//
CheckVideoTables ( ) ;
2018-08-02 21:37:20 +00:00
SetApple2Type ( currentApple2Type ) ;
2020-12-28 16:25:29 +00:00
GetVideo ( ) . SetVideoMode ( currentVideoMode ) ;
2019-06-28 20:34:34 +00:00
g_nHiresPage = currentHiresPage ;
g_nTextPage = currentTextPage ;
2018-08-02 21:37:20 +00:00
}
2019-01-09 21:29:36 +00:00
2019-06-28 20:34:34 +00:00
static void GenerateBaseColors ( baseColors_t pBaseNtscColors )
2019-01-09 21:29:36 +00:00
{
for ( UINT i = 0 ; i < 16 ; i + + )
{
g_nColorPhaseNTSC = INITIAL_COLOR_PHASE ;
g_nSignalBitsNTSC = 0 ;
// 12 iterations for colour to "stabilise", then 4 iterations to calc the average
// - after colour "stabilises" then it repeats through 4 phases (with different RGB values for each phase)
uint32_t bits = ( i < < 12 ) | ( i < < 8 ) | ( i < < 4 ) | i ; // 16 bits
uint32_t colors [ 4 ] ;
for ( UINT j = 0 ; j < 16 ; j + + )
{
colors [ j & 3 ] = getScanlineColor ( bits & 1 , g_aHueColorTV [ g_nColorPhaseNTSC ] ) ;
bits > > = 1 ;
updateColorPhase ( ) ;
}
int r = ( ( ( colors [ 0 ] > > 16 ) & 0xff ) + ( ( colors [ 1 ] > > 16 ) & 0xff ) + ( ( colors [ 2 ] > > 16 ) & 0xff ) + ( ( colors [ 3 ] > > 16 ) & 0xff ) ) / 4 ;
int g = ( ( ( colors [ 0 ] > > 8 ) & 0xff ) + ( ( colors [ 1 ] > > 8 ) & 0xff ) + ( ( colors [ 2 ] > > 8 ) & 0xff ) + ( ( colors [ 3 ] > > 8 ) & 0xff ) ) / 4 ;
int b = ( ( ( colors [ 0 ] ) & 0xff ) + ( ( colors [ 1 ] ) & 0xff ) + ( ( colors [ 2 ] ) & 0xff ) + ( ( colors [ 3 ] ) & 0xff ) ) / 4 ;
uint32_t color = ( ( r < < 16 ) | ( g < < 8 ) | b ) | ALPHA32_MASK ;
( * pBaseNtscColors ) [ i ] = * ( bgra_t * ) & color ;
}
}
2019-06-28 20:34:34 +00:00
//===========================================================================
void NTSC_SetRefreshRate ( VideoRefreshRate_e rate )
{
if ( rate = = VR_50HZ )
{
g_videoScannerMaxVert = VIDEO_SCANNER_MAX_VERT_PAL ;
g_videoScanner6502Cycles = VIDEO_SCANNER_6502_CYCLES_PAL ;
}
else
{
g_videoScannerMaxVert = VIDEO_SCANNER_MAX_VERT ;
g_videoScanner6502Cycles = VIDEO_SCANNER_6502_CYCLES ;
}
GenerateVideoTables ( ) ;
}
UINT NTSC_GetCyclesPerFrame ( void )
{
return g_videoScanner6502Cycles ;
}
2019-09-21 15:37:45 +00:00
2019-09-22 15:53:38 +00:00
UINT NTSC_GetCyclesPerLine ( void )
{
return VIDEO_SCANNER_MAX_HORZ ;
}
2019-09-21 15:37:45 +00:00
UINT NTSC_GetVideoLines ( void )
{
2020-12-28 16:25:29 +00:00
return ( GetVideo ( ) . GetVideoRefreshRate ( ) = = VR_50HZ ) ? VIDEO_SCANNER_MAX_VERT_PAL : VIDEO_SCANNER_MAX_VERT ;
2019-09-21 15:37:45 +00:00
}
2020-10-11 15:08:05 +00:00
// Get # cycles until rising Vbl edge: !VBl -> VBl at (0,192)
// . NB. Called from CMouseInterface::SyncEventCallback(), which occurs *before* NTSC_VideoUpdateCycles()
// therefore g_nVideoClockVert/Horz will be behind, so correct 'cycleCurrentPos' by adding 'cycles'.
UINT NTSC_GetCyclesUntilVBlank ( int cycles )
{
const UINT cyclesPerFrames = NTSC_GetCyclesPerFrame ( ) ;
if ( g_bFullSpeed )
return cyclesPerFrames ; // g_nVideoClockVert/Horz not correct & accuracy isn't important: so just wait a frame's worth of cycles
const UINT cycleVBl = VIDEO_SCANNER_Y_DISPLAY * VIDEO_SCANNER_MAX_HORZ ;
const UINT cycleCurrentPos = ( g_nVideoClockVert * VIDEO_SCANNER_MAX_HORZ + g_nVideoClockHorz + cycles ) % cyclesPerFrames ;
return ( cycleCurrentPos < cycleVBl ) ?
( cycleVBl - cycleCurrentPos ) :
( cyclesPerFrames - cycleCurrentPos + cycleVBl ) ;
}
2023-01-14 14:30:12 +00:00
bool NTSC_GetVblBar ( void )
{
const UINT visibleScanLines = ( ( g_uNewVideoModeFlags & VF_SHR ) = = 0 ) ? VIDEO_SCANNER_Y_DISPLAY : VIDEO_SCANNER_Y_DISPLAY_IIGS ;
return g_nVideoClockVert < visibleScanLines ;
}
2019-09-21 15:37:45 +00:00
bool NTSC_IsVisible ( void )
{
2023-01-14 14:30:12 +00:00
return NTSC_GetVblBar ( ) & & ( g_nVideoClockHorz > = VIDEO_SCANNER_HORZ_START ) ;
2019-09-21 15:37:45 +00:00
}
2022-10-02 15:28:03 +00:00
// For debugger
uint16_t NTSC_GetScannerAddressAndData ( uint32_t & data , int & dataSize )
{
if ( g_uNewVideoModeFlags & VF_SHR )
{
2022-10-02 19:23:54 +00:00
uint16_t addr = getVideoScannerAddressSHR ( ) ;
2022-10-02 15:28:03 +00:00
uint32_t * pAux = ( uint32_t * ) MemGetAuxPtr ( addr ) ; // 8 pixels (320 mode) / 16 pixels (640 mode)
data = pAux [ 0 ] ;
dataSize = 4 ;
return addr ;
}
//
// Copy logic from NTSC_SetVideoMode()
if ( g_uNewVideoModeFlags & VF_TEXT )
{
if ( g_uNewVideoModeFlags & VF_80COL )
dataSize = 2 ;
else
dataSize = 1 ;
}
else if ( g_uNewVideoModeFlags & VF_HIRES )
{
if ( g_uNewVideoModeFlags & VF_DHIRES )
{
if ( g_uNewVideoModeFlags & VF_80COL )
dataSize = 2 ;
else
dataSize = 1 ;
}
else
{
dataSize = 1 ;
}
}
else
{
if ( g_uNewVideoModeFlags & VF_DHIRES )
{
if ( g_uNewVideoModeFlags & VF_80COL )
dataSize = 2 ;
else
dataSize = 1 ;
}
else
{
dataSize = 1 ;
}
}
// Extra logic for MIXED mode
if ( g_nVideoMixed & & g_nVideoClockVert > = VIDEO_SCANNER_Y_MIXED & & ( g_uNewVideoModeFlags & VF_80COL ) )
dataSize = 2 ;
2022-10-02 19:23:54 +00:00
uint16_t addr = getVideoScannerAddressTXTorHGR ( ) ;
2022-10-02 15:28:03 +00:00
data = 0 ;
2022-10-02 19:23:54 +00:00
2022-10-02 15:28:03 +00:00
if ( dataSize = = 2 )
{
uint8_t * pAux = MemGetAuxPtr ( addr ) ;
data = pAux [ 0 ] < < 8 ;
}
uint8_t * pMain = MemGetMainPtr ( addr ) ;
data | = pMain [ 0 ] ;
return addr ;
}