diff --git a/bin/History.txt b/bin/History.txt index 5ae99a94..7eaeeef0 100644 --- a/bin/History.txt +++ b/bin/History.txt @@ -8,6 +8,11 @@ https://github.com/AppleWin/AppleWin/issues/new Tom Charlesworth +1.27.8.0 - 9 Sep 2018 +--------------------- +. [Bug #555] Fix for showing 559th DHGR/DGR/TEXT80 vertical column (retaining 560-pixel display width). + + 1.27.7.0 - 6 Aug 2018 --------------------- . [Bug #564] Fixed 'Save State on Exit' not working correctly when there's a Configuration change to the hardware. diff --git a/resource/Applewin.rc b/resource/Applewin.rc index d93fa74e..88b11363 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -252,8 +252,8 @@ DISK_ICON ICON "DISK.ICO" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,27,7,0 - PRODUCTVERSION 1,27,7,0 + FILEVERSION 1,27,8,0 + PRODUCTVERSION 1,27,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -271,12 +271,12 @@ BEGIN VALUE "Comments", "https://github.com/AppleWin" VALUE "CompanyName", "AppleWin" VALUE "FileDescription", "Apple //e Emulator for Windows" - VALUE "FileVersion", "1, 27, 7, 0" + VALUE "FileVersion", "1, 27, 8, 0" VALUE "InternalName", "APPLEWIN" VALUE "LegalCopyright", " 1994-2018 Michael O'Brien, Oliver Schmidt, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis" VALUE "OriginalFilename", "APPLEWIN.EXE" VALUE "ProductName", "Apple //e Emulator" - VALUE "ProductVersion", "1, 27, 7, 0" + VALUE "ProductVersion", "1, 27, 8, 0" END END BLOCK "VarFileInfo" diff --git a/source/Frame.cpp b/source/Frame.cpp index b58379c0..eb4d5470 100644 --- a/source/Frame.cpp +++ b/source/Frame.cpp @@ -40,6 +40,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Memory.h" #include "Mockingboard.h" #include "MouseInterface.h" +#include "NTSC.h" #include "ParallelPrinter.h" #include "Pravets.h" #include "Registry.h" @@ -199,7 +200,7 @@ void SetAltEnterToggleFullScreen(bool mode) UINT GetFrameBufferBorderlessWidth(void) { - static const UINT uFrameBufferBorderlessW = 560; // 560 = Double Hi-Res + static const UINT uFrameBufferBorderlessW = NTSC_GetFrameBufferBorderlessWidth(); // 560 = Double Hi-Res, +1 for GH#555 return uFrameBufferBorderlessW; } diff --git a/source/Frame.h b/source/Frame.h index f5cace2d..c072520b 100644 --- a/source/Frame.h +++ b/source/Frame.h @@ -3,7 +3,6 @@ // 1.19.0.0 Hard Disk Status/Indicator Light #define HD_LED 1 - // Win32 extern HWND g_hFrameWindow; extern int g_nViewportCX; diff --git a/source/NTSC.cpp b/source/NTSC.cpp index 29dd332c..007ea275 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -23,13 +23,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "StdAfx.h" #include "Applewin.h" #include "CPU.h" // CpuGetCyclesThisVideoFrame() - #include "Frame.h" // FRAMEBUFFER_W FRAMEBUFFER_H + #include "Frame.h" #include "Memory.h" // MemGetMainPtr() MemGetBankPtr() #include "Video.h" // g_pFramebufferbits #include "NTSC.h" #include "NTSC_CharSet.h" + +// GH#555: Extend the 14M video modes by 1 pixel +// . 14M (DHGR,DGR,80COL) are shifted right by 1 pixel, so zero out the left-most visible pixel. +// . 7M (all other modes) are not shift right by 1 pixel, so zero out the right-most visible pixel. +// NB. This 1 pixel shift 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). +#define EXTEND_14M_VIDEO_BY_1_PIXEL 0 + #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 #define NTSC_REMOVE_GRAY_CHROMA 1 // 1 = remove all chroma in gray1 and gray2 @@ -120,6 +130,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // "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 + static const int VIDEO_SCANNER_6502_CYCLES = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_MAX_VERT; #define VIDEO_SCANNER_HORZ_COLORBURST_BEG 12 #define VIDEO_SCANNER_HORZ_COLORBURST_END 16 @@ -440,6 +451,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA static void updateScreenText40 ( long cycles6502 ); static void updateScreenText80 ( long cycles6502 ); +//=========================================================================== +// NB. This func only exists so that EXTEND_14M_VIDEO_BY_1_PIXEL only needs to exist in this cpp file! +UINT NTSC_GetFrameBufferBorderlessWidth(void) +{ +#if !EXTEND_14M_VIDEO_BY_1_PIXEL + return 560; // 560 = Double Hi-Res +#else + return 561; // 560 = Double Hi-Res, +1 for GH#555 +#endif +} + //=========================================================================== static void set_csbits() { @@ -657,13 +679,19 @@ inline void updateFramebufferMonitorDoubleScanline( uint16_t signal, bgra_t *pTa } #endif +//=========================================================================== +inline bool GetColorBurst( void ) +{ + return g_nColorBurstPixels >= 2; +} + //=========================================================================== // NB. g_nLastColumnPixelNTSC = bits.b13 will be superseded by these parent funcs which use bits.b14: // . updateScreenDoubleHires80(), updateScreenDoubleLores80(), updateScreenText80() inline void updatePixels( uint16_t bits ) { - if (g_nColorBurstPixels < 2) + if (!GetColorBurst()) { /* #1 of 7 */ g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1; @@ -716,27 +744,70 @@ inline void updatePixels( uint16_t bits ) } //=========================================================================== + +#if !EXTEND_14M_VIDEO_BY_1_PIXEL +// NOTE: This writes out-of-bounds for a 560x384 framebuffer inline void updateVideoScannerHorzEOL() { if (VIDEO_SCANNER_MAX_HORZ == ++g_nVideoClockHorz) { if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY) { - //VIDEO_DRAW_ENDLINE(); - if (g_nColorBurstPixels < 2) + if (!GetColorBurst()) { - // NOTE: This writes out-of-bounds for a 560x384 framebuffer + // Only for: VF_TEXT && !VF_MIXED (ie. full 24-row TEXT40 or TEXT80) g_pFuncUpdateBnWPixel(g_nLastColumnPixelNTSC); g_pFuncUpdateBnWPixel(0); g_pFuncUpdateBnWPixel(0); - g_pFuncUpdateBnWPixel(0); } else { - // NOTE: This writes out-of-bounds for a 560x384 framebuffer g_pFuncUpdateHuePixel(g_nLastColumnPixelNTSC); g_pFuncUpdateHuePixel(0); g_pFuncUpdateHuePixel(0); + } + } + + g_nVideoClockHorz = 0; + + if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT) + { + g_nVideoClockVert = 0; + + updateFlashRate(); + } + + if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY) + { + updateVideoScannerAddress(); + } + } +} +#endif + +//------------------------------------- + +#if EXTEND_14M_VIDEO_BY_1_PIXEL +// NB. Only needed for video modes that are 14M and shift the color phase, ie: +// . updateScreenDoubleHires80(), updateScreenDoubleLores80(), updateScreenText80() +// NOTE: This writes out-of-bounds for a 560x384 framebuffer +inline void updateVideoScannerHorzEOL_14M() +{ + if (VIDEO_SCANNER_MAX_HORZ == ++g_nVideoClockHorz) + { + if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY) + { + if (!GetColorBurst()) + { + // Only for: VF_TEXT && !VF_MIXED (ie. full 24-row TEXT40 or TEXT80) + g_pFuncUpdateBnWPixel(g_nLastColumnPixelNTSC); // 14M: Output a 561st dot + g_pFuncUpdateBnWPixel(0); + g_pFuncUpdateBnWPixel(0); + } + else + { + g_pFuncUpdateHuePixel(g_nLastColumnPixelNTSC); // 14M: Output a 561st dot + g_pFuncUpdateHuePixel(0); g_pFuncUpdateHuePixel(0); } } @@ -757,10 +828,76 @@ inline void updateVideoScannerHorzEOL() } } +//----------------- + +// NOTE: This writes out-of-bounds for a 560x384 framebuffer +inline void updateVideoScannerHorzEOL() +{ + if (VIDEO_SCANNER_MAX_HORZ == ++g_nVideoClockHorz) + { + if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY) + { + if ( !GetColorBurst() || // Only for: VF_TEXT && !VF_MIXED (ie. full 24-row TEXT40 or TEXT80) + (g_eVideoType == VT_MONO_CUSTOM) || (g_eVideoType == VT_MONO_AMBER) || (g_eVideoType == VT_MONO_GREEN) || (g_eVideoType == VT_MONO_WHITE) ) + { + g_pFuncUpdateBnWPixel(0); + g_pFuncUpdateBnWPixel(0); + + // 7M: Stop outputting video after 560 dots + *(UINT32*)&g_pVideoAddress[0] = 0; + *(UINT32*)&g_pVideoAddress[g_kFrameBufferWidth] = 0; + } + else + { + g_pFuncUpdateHuePixel(g_nLastColumnPixelNTSC); + g_pFuncUpdateHuePixel(0); + + // 7M: Stop outputting video after 560 dots + *(UINT32*)&g_pVideoAddress[0] = 0; + *(UINT32*)&g_pVideoAddress[g_kFrameBufferWidth] = 0; + } + } + + g_nVideoClockHorz = 0; + + if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT) + { + g_nVideoClockVert = 0; + + updateFlashRate(); + } + + if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY) + { + updateVideoScannerAddress(); + } + } +} +#endif + //=========================================================================== inline void updateVideoScannerAddress() { - g_pVideoAddress = g_nVideoClockVert= VIDEO_SCANNER_Y_MIXED && g_pFuncUpdateTextScreen == updateScreenText80)) + { + g_pVideoAddress -= 1; + } + g_nColorPhaseNTSC = INITIAL_COLOR_PHASE; g_nLastColumnPixelNTSC = 0; g_nSignalBitsNTSC = 0; @@ -1123,6 +1260,31 @@ static void updatePixelHueMonitorDoubleScanline (uint16_t compositeSignal) updateColorPhase(); } +//=========================================================================== + +#if EXTEND_14M_VIDEO_BY_1_PIXEL +// NB. Only needed for video modes that are 14M and shift the color phase, ie: +// . updateScreenDoubleHires80(), updateScreenDoubleLores80(), updateScreenText80() +inline void zeroPixel0_14M(void) // GH#555 +{ + if (g_nVideoClockHorz == VIDEO_SCANNER_HORZ_START) + { + UINT32* p = ((UINT32*)g_pVideoAddress) - 14; // Point back to pixel-0 + // NB. For VT_COLOR_MONITOR, also check color-burst so that TEXT and MIXED(HGR+TEXT) render the TEXT at the same offset (GH#341) + if (g_eVideoType == VT_MONO_TV || g_eVideoType == VT_COLOR_TV || (g_eVideoType == VT_COLOR_MONITOR && GetColorBurst())) + { + p[2] = 0; + p[g_kFrameBufferWidth+2] = 0; // Next line (there are 2 lines per Apple II scanline) + } + else + { + p[0] = 0; + p[g_kFrameBufferWidth+0] = 0; // Next line (there are 2 lines per Apple II scanline) + } + } +} +#endif + //=========================================================================== void updateScreenDoubleHires40 (long cycles6502) // wsUpdateVideoHires0 { @@ -1148,6 +1310,7 @@ void updateScreenDoubleHires40 (long cycles6502) // wsUpdateVideoHires0 uint8_t m = pMain[0]; uint16_t bits = g_aPixelDoubleMaskHGR[m & 0x7F]; // Optimization: hgrbits second 128 entries are mirror of first 128 updatePixels( bits ); + // NB. No zeroPixel0_14M(), since no color phase shift (or use of g_nLastColumnPixelNTSC) } } updateVideoScannerHorzEOL(); @@ -1185,9 +1348,17 @@ void updateScreenDoubleHires80 (long cycles6502 ) // wsUpdateVideoDblHires bits = (bits << 1) | g_nLastColumnPixelNTSC; updatePixels( bits ); g_nLastColumnPixelNTSC = (bits >> 14) & 1; + +#if EXTEND_14M_VIDEO_BY_1_PIXEL + zeroPixel0_14M(); +#endif } } +#if EXTEND_14M_VIDEO_BY_1_PIXEL + updateVideoScannerHorzEOL_14M(); +#else updateVideoScannerHorzEOL(); +#endif } } @@ -1217,6 +1388,7 @@ void updateScreenDoubleLores40 (long cycles6502) // wsUpdateVideo7MLores uint16_t lo = getLoResBits( m ); uint16_t bits = g_aPixelDoubleMaskHGR[(0xFF & lo >> ((1 - (g_nVideoClockHorz & 1)) * 2)) & 0x7F]; // Optimization: hgrbits updatePixels( bits ); + // NB. No zeroPixel0_14M(), since no color phase shift (or use of g_nLastColumnPixelNTSC) } } updateVideoScannerHorzEOL(); @@ -1258,9 +1430,18 @@ void updateScreenDoubleLores80 (long cycles6502) // wsUpdateVideoDblLores uint16_t bits = (main << 7) | (aux & 0x7f); updatePixels( bits ); g_nLastColumnPixelNTSC = (bits >> 14) & 1; + +#if EXTEND_14M_VIDEO_BY_1_PIXEL + zeroPixel0_14M(); +#endif } } +#if EXTEND_14M_VIDEO_BY_1_PIXEL + updateVideoScannerHorzEOL_14M(); +#else updateVideoScannerHorzEOL(); +#endif + } } @@ -1404,9 +1585,18 @@ void updateScreenText80 (long cycles6502) bits = (bits << 1) | g_nLastColumnPixelNTSC; // GH#555: Align TEXT80 chars with DHGR updatePixels( bits ); g_nLastColumnPixelNTSC = (bits >> 14) & 1; + +#if EXTEND_14M_VIDEO_BY_1_PIXEL + zeroPixel0_14M(); +#endif } } +#if EXTEND_14M_VIDEO_BY_1_PIXEL + updateVideoScannerHorzEOL_14M(); +#else updateVideoScannerHorzEOL(); +#endif + } } @@ -1642,7 +1832,6 @@ void NTSC_VideoInit( uint8_t* pFramebuffer ) // wsVideoInit for (int y = 0; y < (VIDEO_SCANNER_Y_DISPLAY*2); y++) { uint32_t offset = sizeof(bgra_t) * GetFrameBufferWidth() * ((GetFrameBufferHeight() - 1) - y - GetFrameBufferBorderHeight()) + (sizeof(bgra_t) * GetFrameBufferBorderWidth()); -// offset -= sizeof(bgra_t); // GH#555: Start 1 RGBA pixel before frame to account for g_nLastColumnPixelNTSC // TC: revert as lose half an HGR pixel on left-edge g_pScanLines[y] = (bgra_t*) (g_pFramebufferbits + offset); } @@ -1824,12 +2013,6 @@ void NTSC_VideoRedrawWholeScreen( void ) #endif } -//=========================================================================== -bool NTSC_GetColorBurst( void ) -{ - return (g_nColorBurstPixels < 2) ? false : true; -} - //=========================================================================== static bool CheckVideoTables2( eApple2Type type, uint32_t mode ) diff --git a/source/NTSC.h b/source/NTSC.h index 50ea29d1..d81f283b 100644 --- a/source/NTSC.h +++ b/source/NTSC.h @@ -1,6 +1,3 @@ -// Constants - const int VIDEO_SCANNER_6502_CYCLES = 17030; - // Globals (Public) extern uint16_t g_nVideoClockVert; extern uint16_t g_nVideoClockHorz; @@ -19,4 +16,4 @@ extern void NTSC_VideoInitChroma(); extern void NTSC_VideoUpdateCycles( long cycles6502 ); extern void NTSC_VideoRedrawWholeScreen( void ); - extern bool NTSC_GetColorBurst( void ); + extern UINT NTSC_GetFrameBufferBorderlessWidth( void ); diff --git a/source/Video.cpp b/source/Video.cpp index c5808efa..d5663f3b 100644 --- a/source/Video.cpp +++ b/source/Video.cpp @@ -567,6 +567,7 @@ void VideoRedrawScreen (void) //=========================================================================== // TC: Hacky-fix for GH#341 - better to draw to the correct position in the framebuffer to start with! (in NTSC.cpp) +// . NB. Now the dx is corrected in NTSC.cpp, updateVideoScannerAddress() static void VideoFrameBufferAdjust(int& xSrc, int& ySrc, bool bInvertY=false) { int dx=0, dy=0; @@ -574,15 +575,8 @@ static void VideoFrameBufferAdjust(int& xSrc, int& ySrc, bool bInvertY=false) if (g_eVideoType == VT_MONO_TV || g_eVideoType == VT_COLOR_TV) { // Adjust the src locations for the NTSC video modes - dx = 2; dy = -1; } - else if (g_eVideoType == VT_COLOR_MONITOR) - { - //if ((g_uVideoMode & VF_TEXT) == 0) // NB. Not sufficient, eg. ANSI STORY... - if ( NTSC_GetColorBurst() == true ) // ANSI STORY (end credits): split DGR/TEXT80/DGR on scanline - dx = 2; - } if (bInvertY) dy =- dy;