2006-02-25 20:50:29 +00:00
/*
AppleWin : An Apple //e emulator for Windows
Copyright ( C ) 1994 - 1996 , Michael O ' Brien
Copyright ( C ) 1999 - 2001 , Oliver Schmidt
Copyright ( C ) 2002 - 2005 , Tom Charlesworth
2011-01-06 17:47:17 +00:00
Copyright ( C ) 2006 - 2010 , Tom Charlesworth , Michael Pohoreski , Nick Westgate
2006-02-25 20:50:29 +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
*/
/* Description: Emulation of video modes
*
* Author : Various
*/
# include "StdAfx.h"
# include "..\resource\resource.h"
2012-03-27 21:20:36 +00:00
# include "Configuration\PropertySheet.h"
2006-02-25 20:50:29 +00:00
2011-02-17 17:29:30 +00:00
# define HALF_PIXEL_SOLID 1
2011-02-19 23:04:44 +00:00
# define HALF_PIXEL_BLEED 0
2011-02-17 17:29:30 +00:00
2011-02-19 22:14:08 +00:00
# define COLORS_TWEAKED 1
2006-02-25 20:50:29 +00:00
/* reference: technote tn-iigs-063 "Master Color Values"
Color Color Register LR HR DHR Master Color R , G , B
Name Value # # # Value
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Black 0 0 0 , 4 0 $ 0000 ( 0 , 0 , 0 ) - > ( 00 , 00 , 00 ) Windows
( Magenta ) Deep Red 1 1 1 $ 0 D03 ( D , 0 , 3 ) - > ( D0 , 00 , 30 ) Custom
Dark Blue 2 2 8 $ 000 9 ( 0 , 0 , 9 ) - > ( 00 , 00 , 80 ) Windows
( Violet ) Purple 3 3 2 9 $ 0 D2D ( D , 2 , D ) - > ( FF , 00 , FF ) Windows
Dark Green 4 4 4 $ 0072 ( 0 , 7 , 2 ) - > ( 00 , 80 , 00 ) Windows
( Gray 1 ) Dark Gray 5 5 5 $ 0555 ( 5 , 5 , 5 ) - > ( 80 , 80 , 80 ) Windows
( Blue ) Medium Blue 6 6 6 C $ 022F ( 2 , 2 , F ) - > ( 00 , 00 , FF ) Windows
( Cyan ) Light Blue 7 7 D $ 06 AF ( 6 , A , F ) - > ( 60 , A0 , FF ) Custom
Brown 8 8 2 $ 0850 ( 8 , 5 , 0 ) - > ( 80 , 50 , 00 ) Custom
Orange 9 9 5 3 $ 0F 60 ( F , 6 , 0 ) - > ( FF , 80 , 00 ) Custom ( modified to match better with the other Hi - Res Colors )
( Gray 2 ) Light Gray A A A $ 0 AAA ( A , A , A ) - > ( C0 , C0 , C0 ) Windows
Pink B B B $ 0F 98 ( F , 9 , 8 ) - > ( FF , 90 , 80 ) Custom
( Green ) Light Green C C 1 6 $ 01 D0 ( 1 , D , 0 ) - > ( 00 , FF , 00 ) Windows
Yellow D D 7 $ 0FF 0 ( F , F , 0 ) - > ( FF , FF , 00 ) Windows
( Aqua ) Aquamarine E E E $ 04F 9 ( 4 , F , 9 ) - > ( 40 , FF , 90 ) Custom
White F F 3 , 7 F $ 0FF F ( F , F , F ) - > ( FF , FF , FF ) Windows
LR : Lo - Res HR : Hi - Res DHR : Double Hi - Res */
2012-09-16 21:53:07 +00:00
# define FLASH_80_COL 1 // Bug #7238
2006-03-01 03:49:14 +00:00
# define HALF_SHIFT_DITHER 0
2006-03-01 00:23:42 +00:00
enum Color_Palette_Index_e
{
2011-02-19 22:14:08 +00:00
// The first 10 are the DEFAULT Windows colors (as it reserves 20 colors)
BLACK = 0x00 // 0x00,0x00,0x00
, DARK_RED = 0x01 // 0x80,0x00,0x00
, DARK_GREEN = 0x02 // 0x00,0x80,0x00 (Half Green)
, DARK_YELLOW = 0x03 // 0x80,0x80,0x00
, DARK_BLUE = 0x04 // 0x00,0x00,0x80 (Half Blue)
, DARK_MAGENTA = 0x05 // 0x80,0x00,0x80
, DARK_CYAN = 0x06 // 0x00,0x80,0x80
, LIGHT_GRAY = 0x07 // 0xC0,0xC0,0xC0
, MONEY_GREEN = 0x08 // 0xC0,0xDC,0xC0 // not used
, SKY_BLUE = 0x09 // 0xA6,0xCA,0xF0 // not used
// Really need to have Quarter Green and Quarter Blue for Hi-Res
2011-02-18 16:30:12 +00:00
// OUR CUSTOM COLORS -- the extra colors HGR mode can display
2011-02-19 22:14:08 +00:00
// , DEEP_BLUE // Breaks TV Emulation Reference Test !?!? // Breaks the dam palette -- black monochrome TEXT output bug *sigh*
2006-03-01 00:23:42 +00:00
, DEEP_RED
, LIGHT_BLUE
, BROWN
, ORANGE
, PINK
, AQUA
2011-02-19 22:14:08 +00:00
// CUSTOM HGR COLORS (don't change order) - For tv emulation HGR Video Mode
2006-03-01 00:23:42 +00:00
, HGR_BLACK
, HGR_WHITE
2011-02-19 22:14:08 +00:00
, HGR_BLUE // HCOLOR=6 BLUE , $81
, HGR_RED // HCOLOR=5 ORANGE , $82
, HGR_GREEN // HCOLOR=1 GREEN , $01
, HGR_MAGENTA // HCOLOR=2 MAGENTA, $02
2006-03-01 00:23:42 +00:00
, HGR_GREY1
, HGR_GREY2
, HGR_YELLOW
, HGR_AQUA
, HGR_PURPLE
, HGR_PINK
2011-01-07 09:23:16 +00:00
// MONOCHROME
2011-02-19 22:14:08 +00:00
// NOTE: 50% is assumed to come after 100% luminance !!! See: V_CreateLookup_MonoHiRes()
2011-01-07 09:23:16 +00:00
// User customizable
, MONOCHROME_CUSTOM // 100% luminance
, MONOCHROME_CUSTOM_50 // 50% luminance
// Pre-set "Monochromes"
2006-03-01 00:23:42 +00:00
, MONOCHROME_AMBER
2011-01-08 04:17:27 +00:00
// , MONOCHROME_AMBER_50 // BUG - something trashing our palette entry !!!
2006-03-01 00:23:42 +00:00
, MONOCHROME_GREEN
2011-01-08 04:17:27 +00:00
// , MONOCHROME_GREEN_50 // BUG - something trashing our palette entry !!!
2006-03-01 00:23:42 +00:00
2009-02-16 20:30:38 +00:00
, DEBUG_COLORS_START
, DEBUG_COLORS_END = DEBUG_COLORS_START + NUM_DEBUG_COLORS
// DD Full Screen Palette ?!?!
// , LOGO_COLORS_START
// , LOGO_COLORS_END = LOGO_COLORS_START + 128
2006-03-01 00:23:42 +00:00
, NUM_COLOR_PALETTE
2011-02-19 22:14:08 +00:00
// The last 10 are the DEFAULT Windows colors (as it reserves 20 colors)
, CREAM = 0xF6
, MEDIUM_GRAY = 0xF7
, DARK_GRAY = 0xF8
, RED = 0xF9
, GREEN = 0xFA
, YELLOW = 0xFB
, BLUE = 0xFC
, MAGENTA = 0xFD
, CYAN = 0xFE
, WHITE = 0xFF
2006-03-01 00:23:42 +00:00
} ;
2011-02-19 22:14:08 +00:00
// __ Map HGR color index to Palette index
enum ColorMapping
{
CM_Magenta
, CM_Blue
, CM_Green
, CM_Orange
, CM_Black
, CM_White
, NUM_COLOR_MAPPING
} ;
const BYTE HiresToPalIndex [ NUM_COLOR_MAPPING ] =
{
HGR_MAGENTA
, HGR_BLUE
, HGR_GREEN
, HGR_RED
, HGR_BLACK
, HGR_WHITE
} ;
const BYTE LoresResColors [ 16 ] = {
// BLACK, DEEP_RED, DARK_BLUE, MAGENTA,
// DARK_GREEN,DARK_GRAY,BLUE, LIGHT_BLUE,
// BROWN, ORANGE, LIGHT_GRAY,PINK,
// GREEN, YELLOW, AQUA, WHITE
BLACK , DEEP_RED , DARK_BLUE , MAGENTA ,
DARK_GREEN , DARK_GRAY , BLUE , LIGHT_BLUE ,
BROWN , ORANGE , LIGHT_GRAY , PINK ,
GREEN , YELLOW , AQUA , HGR_WHITE
} ;
const BYTE DoubleHiresPalIndex [ 16 ] = {
// BLACK, DARK_BLUE, DARK_GREEN,BLUE,
// BROWN, LIGHT_GRAY,GREEN, AQUA,
// DEEP_RED,MAGENTA, DARK_GRAY, LIGHT_BLUE,
// ORANGE, PINK, YELLOW, WHITE
BLACK , DARK_BLUE , DARK_GREEN , BLUE ,
BROWN , LIGHT_GRAY , GREEN , AQUA ,
DEEP_RED , MAGENTA , DARK_GRAY , LIGHT_BLUE ,
ORANGE , PINK , YELLOW , HGR_WHITE
} ;
2011-01-08 02:58:20 +00:00
const int SRCOFFS_40COL = 0 ; // 0
const int SRCOFFS_IIPLUS = ( SRCOFFS_40COL + 256 ) ; // 256
const int SRCOFFS_80COL = ( SRCOFFS_IIPLUS + 256 ) ; // 512
const int SRCOFFS_LORES = ( SRCOFFS_80COL + 128 ) ; // 640
const int SRCOFFS_HIRES = ( SRCOFFS_LORES + 16 ) ; // 656
const int SRCOFFS_DHIRES = ( SRCOFFS_HIRES + 512 ) ; // 1168
const int SRCOFFS_TOTAL = ( SRCOFFS_DHIRES + 2560 ) ; // 3278
# define SW_80COL (g_bVideoMode & VF_80COL)
# define SW_DHIRES (g_bVideoMode & VF_DHIRES)
# define SW_HIRES (g_bVideoMode & VF_HIRES)
# define SW_MASK2 (g_bVideoMode & VF_MASK2)
# define SW_MIXED (g_bVideoMode & VF_MIXED)
# define SW_PAGE2 (g_bVideoMode & VF_PAGE2)
# define SW_TEXT (g_bVideoMode & VF_TEXT)
2006-02-25 20:50:29 +00:00
2006-06-29 03:28:25 +00:00
# define SETSOURCEPIXEL(x,y,c) g_aSourceStartofLine[(y)][(x)] = (c)
2006-02-25 20:50:29 +00:00
2011-01-09 04:42:46 +00:00
# define SETFRAMECOLOR(i,r,g,b) g_pFramebufferinfo->bmiColors[i].rgbRed = r; \
g_pFramebufferinfo - > bmiColors [ i ] . rgbGreen = g ; \
g_pFramebufferinfo - > bmiColors [ i ] . rgbBlue = b ; \
g_pFramebufferinfo - > bmiColors [ i ] . rgbReserved = PC_NOCOLLAPSE ;
2006-02-25 20:50:29 +00:00
2011-02-19 22:14:08 +00:00
# define HGR_MATRIX_YOFFSET 2 // For tv emulation HGR Video Mode
2006-02-25 20:50:29 +00:00
2006-03-12 09:05:39 +00:00
// video scanner constants
int const kHBurstClock = 53 ; // clock when Color Burst starts
int const kHBurstClocks = 4 ; // clocks per Color Burst duration
int const kHClock0State = 0x18 ; // H[543210] = 011000
int const kHClocks = 65 ; // clocks per horizontal scan (including HBL)
int const kHPEClock = 40 ; // clock when HPE (horizontal preset enable) goes low
int const kHPresetClock = 41 ; // clock when H state presets
int const kHSyncClock = 49 ; // clock when HSync starts
int const kHSyncClocks = 4 ; // clocks per HSync duration
int const kNTSCScanLines = 262 ; // total scan lines including VBL (NTSC)
int const kNTSCVSyncLine = 224 ; // line when VSync starts (NTSC)
int const kPALScanLines = 312 ; // total scan lines including VBL (PAL)
int const kPALVSyncLine = 264 ; // line when VSync starts (PAL)
int const kVLine0State = 0x100 ; // V[543210CBA] = 100000000
int const kVPresetLine = 256 ; // line when V state presets
int const kVSyncLines = 4 ; // lines per VSync duration
2014-06-27 22:43:25 +01:00
static BYTE celldirty [ 40 ] [ 32 ] ; // [TC: 27/06/2014] NB. No longer used!
2011-01-09 04:42:46 +00:00
// NUM_COLOR_PALETTE
static COLORREF customcolors [ 256 ] ; // MONOCHROME is last custom color
2006-06-29 03:28:25 +00:00
static HBITMAP g_hDeviceBitmap ;
static HDC g_hDeviceDC ;
2009-02-16 20:30:38 +00:00
LPBYTE g_pFramebufferbits = NULL ; // last drawn frame
static LPBITMAPINFO g_pFramebufferinfo = NULL ;
2006-06-29 03:28:25 +00:00
2011-01-06 17:10:23 +00:00
static LPBYTE g_aFrameBufferOffset [ FRAMEBUFFER_H ] ; // array of pointers to start of each scanline
2006-06-29 03:28:25 +00:00
static LPBYTE g_pHiresBank1 ;
static LPBYTE g_pHiresBank0 ;
2006-07-02 22:58:12 +00:00
HBITMAP g_hLogoBitmap ;
2006-06-29 03:28:25 +00:00
static HPALETTE g_hPalette ;
static HBITMAP g_hSourceBitmap ;
static LPBYTE g_pSourcePixels ;
static LPBITMAPINFO g_pSourceHeader ;
const int MAX_SOURCE_Y = 512 ;
static LPBYTE g_aSourceStartofLine [ MAX_SOURCE_Y ] ;
static LPBYTE g_pTextBank1 ; // Aux
static LPBYTE g_pTextBank0 ; // Main
2006-02-25 20:50:29 +00:00
2011-02-19 22:14:08 +00:00
// For tv emulation HGR Video Mode
2006-06-29 03:28:25 +00:00
// 2 extra scan lines on bottom?
2008-08-31 04:31:35 +00:00
static BYTE hgrpixelmatrix [ FRAMEBUFFER_W / 2 ] [ FRAMEBUFFER_H / 2 + 2 * HGR_MATRIX_YOFFSET ] ;
2006-02-25 20:50:29 +00:00
static BYTE colormixbuffer [ 6 ] ;
static WORD colormixmap [ 6 ] [ 6 ] [ 6 ] ;
//
2011-01-04 17:08:01 +00:00
int g_nAltCharSetOffset = 0 ; // alternate character set
2008-08-25 05:25:27 +00:00
2011-01-04 17:08:01 +00:00
bool g_bVideoDisplayPage2 = 0 ;
2014-06-26 22:44:02 +01:00
/*bool*/ UINT g_VideoForceFullRedraw = 1 ;
2008-08-25 05:25:27 +00:00
2006-02-25 20:50:29 +00:00
static LPBYTE framebufferaddr = ( LPBYTE ) 0 ;
2011-01-06 17:10:23 +00:00
static LONG g_nFrameBufferPitch = 0 ;
2011-01-08 21:29:27 +00:00
BOOL g_bGraphicsMode = 0 ;
2006-02-25 20:50:29 +00:00
static BOOL hasrefreshed = 0 ;
static DWORD lastpageflip = 0 ;
2011-01-08 21:29:27 +00:00
COLORREF monochrome = RGB ( 0xC0 , 0xC0 , 0xC0 ) ;
2006-02-25 20:50:29 +00:00
static BOOL rebuiltsource = 0 ;
static LPBYTE vidlastmem = NULL ;
2011-01-04 17:08:01 +00:00
2011-01-08 21:29:27 +00:00
int g_bVideoMode = VF_TEXT ;
2009-02-14 03:53:28 +00:00
2011-02-19 22:14:08 +00:00
DWORD g_eVideoType = VT_COLOR_TVEMU ;
2011-02-19 22:39:48 +00:00
DWORD g_uHalfScanLines = true ; // drop 50% scan lines for a more authentic look
2009-02-14 03:53:28 +00:00
2006-02-25 20:50:29 +00:00
static bool g_bTextFlashState = false ;
static bool g_bTextFlashFlag = false ;
2006-03-12 09:05:39 +00:00
static bool bVideoScannerNTSC = true ; // NTSC video scanning (or PAL)
2006-02-25 20:50:29 +00:00
//-------------------------------------
// Video consts:
2011-01-08 02:58:20 +00:00
const UINT nVBlStop_NTSC = 21 ;
const UINT nVBlStop_PAL = 29 ;
// NOTE: KEEP IN SYNC: VideoType_e g_aVideoChoices g_apVideoModeDesc
TCHAR g_aVideoChoices [ ] =
2011-02-19 22:14:08 +00:00
TEXT ( " Monochrome (Custom Luminance) \0 " )
2011-02-18 15:29:24 +00:00
TEXT ( " Color (Standard) \0 " )
2011-02-19 22:14:08 +00:00
TEXT ( " Color (Text Optimized) \0 " )
2011-01-08 02:58:20 +00:00
TEXT ( " Color (TV emulation) \0 " )
2011-02-15 17:16:36 +00:00
TEXT ( " Monochrome (Amber) \0 " )
TEXT ( " Monochrome (Green) \0 " )
TEXT ( " Monochrome (White) \0 " )
2011-01-08 02:58:20 +00:00
;
2011-02-19 22:14:08 +00:00
// AppleWin 1.19.4 VT_COLOR_AUTHENTIC -> VT_COLOR_HALFPIXEL -> VT_COLOR_STANDARD "Color Half-Pixel Authentic
2011-01-08 02:58:20 +00:00
// NOTE: KEEP IN SYNC: VideoType_e g_aVideoChoices g_apVideoModeDesc
// The window title will be set to this.
char * g_apVideoModeDesc [ NUM_VIDEO_MODES ] =
{
2011-02-19 22:14:08 +00:00
" Monochrome (Custom) "
, " Standard "
, " Text Optimized "
, " TV "
, " Amber "
, " Green "
, " White "
2011-01-08 02:58:20 +00:00
} ;
2006-02-25 20:50:29 +00:00
2008-07-14 16:02:44 +00:00
// Prototypes (Private) _____________________________________________
2006-02-25 20:50:29 +00:00
2011-02-19 22:14:08 +00:00
void V_CreateLookup_DoubleHires ( ) ;
void V_CreateLookup_Hires ( ) ; // Old "Full-Pixel" support only: STANDARD, TEXT_OPTIMIZED, TVEMU
void V_CreateLookup_HiResHalfPixel_Authentic ( ) ; // New "Half_Pixel" support: STANDARD, TEXT_OPTIMIZED
void V_CreateLookup_HiresHalfShiftFull ( ) ;
void V_CreateLookup_Lores ( ) ;
void V_CreateLookup_Text ( HDC dc ) ;
// Monochrome Full-Pixel Support
void V_CreateLookup_MonoDoubleHiRes ( ) ;
void V_CreateLookup_MonoHiRes ( ) ;
void V_CreateLookup_MonoLoRes ( ) ;
void V_CreateLookup_MonoText ( HDC dc ) ;
2011-01-08 02:58:20 +00:00
// Monochrome Half-Pixel Support
2011-02-19 22:14:08 +00:00
void V_CreateLookup_MonoHiResHalfPixel_Real ( ) ;
2011-01-08 02:58:20 +00:00
bool g_bDisplayPrintScreenFileName = false ;
2013-12-23 03:20:54 +00:00
bool g_bShowPrintScreenWarningDialog = true ;
2011-01-08 02:58:20 +00:00
void Util_MakeScreenShotFileName ( char * pFinalFileName_ ) ;
bool Util_TestScreenShotFileName ( const char * pFileName ) ;
// true = 280x192
// false = 560x384
void Video_SaveScreenShot ( const char * pScreenShotFileName ) ;
void Video_MakeScreenShot ( FILE * pFile ) ;
int GetMonochromeIndex ( ) ;
2011-01-09 04:42:46 +00:00
void V_CreateIdentityPalette ( ) ;
void V_CreateDIBSections ( ) ;
HBRUSH V_CreateCustomBrush ( COLORREF nColor ) ;
2011-01-08 02:58:20 +00:00
/** Our BitBlit() / VRAM_Copy()
2011-01-06 17:10:23 +00:00
@ param dx Dst X
@ param dy Dst Y
@ param w Width ( same for src & dst )
@ param h Height ( same for src & dst )
@ param sx Src X
@ param sy Src Y
// =========================================================================== */
2014-06-26 22:44:02 +01:00
static inline void CopySource8 ( int dx , int dy , int w , int h , int sx , int sy )
2006-02-25 20:50:29 +00:00
{
2011-01-06 17:10:23 +00:00
LPBYTE pDst = g_aFrameBufferOffset [ dy ] + dx ;
LPBYTE pSrc = g_aSourceStartofLine [ sy ] + sx ;
int nBytes ;
while ( h - - )
2006-02-25 20:50:29 +00:00
{
2011-01-06 17:10:23 +00:00
nBytes = w ;
// If not multiple of 3 bytes, copy first 3 bytes, so the next copy is 4-byte aligned.
while ( nBytes & 3 )
{
2014-06-26 22:44:02 +01:00
- - nBytes ;
* ( pDst + nBytes ) = * ( pSrc + nBytes ) ;
2011-01-06 17:10:23 +00:00
}
// Copy 4 bytes at a time
while ( nBytes )
{
2014-06-26 22:44:02 +01:00
nBytes - = 4 ;
* ( LPDWORD ) ( pDst + nBytes ) = * ( LPDWORD ) ( pSrc + nBytes ) ;
2011-01-06 17:10:23 +00:00
}
pDst - = g_nFrameBufferPitch ;
pSrc - = SRCOFFS_TOTAL ;
}
2006-02-25 20:50:29 +00:00
}
2014-06-26 22:44:02 +01:00
static void CopySource ( int dx , int dy , int w , int h , int sx , int sy )
{
if ( ! g_bIsFullScreen | | ! GetFullScreen32Bit ( ) )
{
CopySource8 ( dx , dy , w , h , sx , sy ) ;
return ;
}
UINT32 * pDst = ( UINT32 * ) ( g_aFrameBufferOffset [ dy ] + dx * sizeof ( UINT32 ) ) ;
LPBYTE pSrc = g_aSourceStartofLine [ sy ] + sx ;
int nBytes ;
while ( h - - )
{
nBytes = w ;
while ( nBytes )
{
- - nBytes ;
const RGBQUAD & rRGB = g_pFramebufferinfo - > bmiColors [ * ( pSrc + nBytes ) ] ;
const UINT32 rgb = ( ( ( UINT32 ) rRGB . rgbRed ) < < 16 ) | ( ( ( UINT32 ) rRGB . rgbGreen ) < < 8 ) | ( ( UINT32 ) rRGB . rgbBlue ) ;
* ( pDst + nBytes ) = rgb ;
}
pDst - = g_nFrameBufferPitch / sizeof ( UINT32 ) ;
pSrc - = SRCOFFS_TOTAL ;
}
}
2011-01-06 17:47:17 +00:00
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-01-08 02:58:20 +00:00
void CreateFrameOffsetTable ( LPBYTE addr , LONG pitch )
{
if ( ( framebufferaddr = = addr ) & & ( g_nFrameBufferPitch = = pitch ) )
return ;
framebufferaddr = addr ;
g_nFrameBufferPitch = pitch ;
// CREATE THE OFFSET TABLE FOR EACH SCAN LINE IN THE FRAME BUFFER
for ( int y = 0 ; y < FRAMEBUFFER_H ; y + + )
g_aFrameBufferOffset [ y ] = framebufferaddr + g_nFrameBufferPitch * ( ( FRAMEBUFFER_H - 1 ) - y ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2011-01-09 04:42:46 +00:00
void VideoInitialize ( )
{
// CREATE A BUFFER FOR AN IMAGE OF THE LAST DRAWN MEMORY
vidlastmem = ( LPBYTE ) VirtualAlloc ( NULL , 0x10000 , MEM_COMMIT , PAGE_READWRITE ) ;
ZeroMemory ( vidlastmem , 0x10000 ) ;
// LOAD THE LOGO
g_hLogoBitmap = ( HBITMAP ) LoadImage ( g_hInstance , MAKEINTRESOURCE ( IDB_APPLEWIN ) , IMAGE_BITMAP , 0 , 0 , LR_CREATEDIBSECTION ) ;
// CREATE A BITMAPINFO STRUCTURE FOR THE FRAME BUFFER
g_pFramebufferinfo = ( LPBITMAPINFO ) VirtualAlloc (
NULL ,
sizeof ( BITMAPINFOHEADER ) + 256 * sizeof ( RGBQUAD ) ,
MEM_COMMIT ,
PAGE_READWRITE ) ;
ZeroMemory ( g_pFramebufferinfo , sizeof ( BITMAPINFOHEADER ) + 256 * sizeof ( RGBQUAD ) ) ;
g_pFramebufferinfo - > bmiHeader . biSize = sizeof ( BITMAPINFOHEADER ) ;
g_pFramebufferinfo - > bmiHeader . biWidth = FRAMEBUFFER_W ;
g_pFramebufferinfo - > bmiHeader . biHeight = FRAMEBUFFER_H ;
g_pFramebufferinfo - > bmiHeader . biPlanes = 1 ;
g_pFramebufferinfo - > bmiHeader . biBitCount = 8 ;
g_pFramebufferinfo - > bmiHeader . biClrUsed = 256 ;
// CREATE A BITMAPINFO STRUCTURE FOR THE SOURCE IMAGE
g_pSourceHeader = ( LPBITMAPINFO ) VirtualAlloc (
NULL ,
sizeof ( BITMAPINFOHEADER ) + 256 * sizeof ( RGBQUAD ) ,
MEM_COMMIT ,
PAGE_READWRITE ) ;
ZeroMemory ( g_pSourceHeader , sizeof ( BITMAPINFOHEADER ) ) ;
g_pSourceHeader - > bmiHeader . biSize = sizeof ( BITMAPINFOHEADER ) ;
g_pSourceHeader - > bmiHeader . biWidth = SRCOFFS_TOTAL ;
g_pSourceHeader - > bmiHeader . biHeight = 512 ;
g_pSourceHeader - > bmiHeader . biPlanes = 1 ;
g_pSourceHeader - > bmiHeader . biBitCount = 8 ;
g_pSourceHeader - > bmiHeader . biClrUsed = 256 ;
// VideoReinitialize() ... except we set the frame buffer palette....
V_CreateIdentityPalette ( ) ;
//RGB() -> none
//PALETTERGB() -> PC_EXPLICIT
//??? RGB() -> PC_NOCOLLAPSE
for ( int iColor = 0 ; iColor < NUM_COLOR_PALETTE ; iColor + + )
customcolors [ iColor ] = ( ( DWORD ) PC_EXPLICIT < < 24 ) | RGB (
g_pFramebufferinfo - > bmiColors [ iColor ] . rgbRed ,
g_pFramebufferinfo - > bmiColors [ iColor ] . rgbGreen ,
g_pFramebufferinfo - > bmiColors [ iColor ] . rgbBlue
) ;
// CREATE THE FRAME BUFFER DIB SECTION AND DEVICE CONTEXT,
// CREATE THE SOURCE IMAGE DIB SECTION AND DRAW INTO THE SOURCE BIT BUFFER
V_CreateDIBSections ( ) ;
// RESET THE VIDEO MODE SWITCHES AND THE CHARACTER SET OFFSET
VideoResetState ( ) ;
}
//===========================================================================
int GetMonochromeIndex ( )
{
int iMonochrome ;
switch ( g_eVideoType )
{
case VT_MONO_AMBER : iMonochrome = MONOCHROME_AMBER ; break ;
case VT_MONO_GREEN : iMonochrome = MONOCHROME_GREEN ; break ;
2011-02-19 22:14:08 +00:00
case VT_MONO_WHITE : iMonochrome = HGR_WHITE ; break ;
2011-01-09 04:42:46 +00:00
default : iMonochrome = MONOCHROME_CUSTOM ; break ; // caller will use MONOCHROME_CUSTOM MONOCHROME_CUSTOM_50 !
}
return iMonochrome ;
}
//===========================================================================
void V_CreateIdentityPalette ( )
2009-02-16 20:30:38 +00:00
{
if ( g_hPalette )
{
DeleteObject ( g_hPalette ) ;
}
2011-01-09 04:42:46 +00:00
g_hPalette = ( HPALETTE ) 0 ;
2011-02-19 22:14:08 +00:00
SETFRAMECOLOR ( BLACK , 0x00 , 0x00 , 0x00 ) ; // 0
SETFRAMECOLOR ( DARK_RED , 0x80 , 0x00 , 0x00 ) ; // 1 // used by TV
SETFRAMECOLOR ( DARK_GREEN , 0x00 , 0x80 , 0x00 ) ; // 2
SETFRAMECOLOR ( DARK_YELLOW , 0x80 , 0x80 , 0x00 ) ; // 3
2014-07-10 22:52:26 +01:00
SETFRAMECOLOR ( DARK_BLUE , 0x00 , 0x00 , 0x80 ) ; // 4
2011-02-19 22:14:08 +00:00
SETFRAMECOLOR ( DARK_MAGENTA , 0x80 , 0x00 , 0x80 ) ; // 5
SETFRAMECOLOR ( DARK_CYAN , 0x00 , 0x80 , 0x80 ) ; // 6
2014-07-10 22:52:26 +01:00
SETFRAMECOLOR ( LIGHT_GRAY , 0xC0 , 0xC0 , 0xC0 ) ; // 7 // GR: COLOR=10
2011-02-19 22:14:08 +00:00
SETFRAMECOLOR ( MONEY_GREEN , 0xC0 , 0xDC , 0xC0 ) ; // 8 // not used
SETFRAMECOLOR ( SKY_BLUE , 0xA6 , 0xCA , 0xF0 ) ; // 9 // not used
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// SET FRAME BUFFER TABLE ENTRIES TO CUSTOM COLORS
2011-02-19 22:14:08 +00:00
# if COLORS_TWEAKED
SETFRAMECOLOR ( DARK_RED , 0x9D , 0x09 , 0x66 ) ; // 1 // Linards Tweaked
SETFRAMECOLOR ( DARK_GREEN , 0x00 , 0x76 , 0x1A ) ; // 2 // Linards Tweaked
2014-07-10 22:52:26 +01:00
SETFRAMECOLOR ( DARK_BLUE , 0x2A , 0x2A , 0xE5 ) ; // 4 // Linards Tweaked
2011-02-19 22:14:08 +00:00
SETFRAMECOLOR ( DEEP_RED , 0x9D , 0x09 , 0x66 ) ; // 0xD0,0x00,0x30 -> Linards Tweaked 0x9D,0x09,0x66
SETFRAMECOLOR ( LIGHT_BLUE , 0xAA , 0xAA , 0xFF ) ; // 0x60,0xA0,0xFF -> Linards Tweaked 0xAA,0xAA,0xFF
SETFRAMECOLOR ( BROWN , 0x55 , 0x55 , 0x00 ) ; // 0x80,0x50,0x00 -> Linards Tweaked 0x55,0x55,0x00
SETFRAMECOLOR ( ORANGE , 0xF2 , 0x5E , 0x00 ) ; // 0xFF,0x80,0x00 -> Linards Tweaked 0xF2,0x5E,0x00
SETFRAMECOLOR ( PINK , 0xFF , 0x89 , 0xE5 ) ; // 0xFF,0x90,0x80 -> Linards Tweaked 0xFF,0x89,0xE5
SETFRAMECOLOR ( AQUA , 0x62 , 0xF6 , 0x99 ) ; // 0x40,0xFF,0x90 -> Linards Tweaked 0x62,0xF6,0x99
SETFRAMECOLOR ( HGR_BLACK , 0x00 , 0x00 , 0x00 ) ; // For TV emulation HGR Video Mode
SETFRAMECOLOR ( HGR_WHITE , 0xFF , 0xFF , 0xFE ) ; // BUG: PALETTE COLLAPSE! NOT white!? Win32 collapses the palette if you have duplicate colors!
SETFRAMECOLOR ( HGR_BLUE , 0x0D , 0xA1 , 0xFF ) ; // 0x00,0x80,0xFF -> Linards Tweaked 0x0D,0xA1,0xFF
SETFRAMECOLOR ( HGR_RED , 0xF2 , 0x5E , 0x00 ) ; // 0xF0,0x50,0x00 -> Linards Tweaked 0xF2,0x5E,0x00
SETFRAMECOLOR ( HGR_GREEN , 0x38 , 0xCB , 0x00 ) ; // 0x20,0xC0,0x00 -> Linards Tweaked 0x38,0xCB,0x00
SETFRAMECOLOR ( HGR_MAGENTA , 0xC7 , 0x34 , 0xFF ) ; // 0xA0,0x00,0xFF -> Linards Tweaked 0xC7,0x34,0xFF
2006-03-01 00:23:42 +00:00
SETFRAMECOLOR ( HGR_GREY1 , 0x80 , 0x80 , 0x80 ) ;
SETFRAMECOLOR ( HGR_GREY2 , 0x80 , 0x80 , 0x80 ) ;
2011-02-19 22:14:08 +00:00
SETFRAMECOLOR ( HGR_YELLOW , 0x9E , 0x9E , 0x00 ) ; // 0xD0,0xB0,0x10 -> 0x9E,0x9E,0x00
SETFRAMECOLOR ( HGR_AQUA , 0x00 , 0xCD , 0x4A ) ; // 0x20,0xB0,0xB0 -> 0x00,0xCD,0x4A
SETFRAMECOLOR ( HGR_PURPLE , 0x61 , 0x61 , 0xFF ) ; // 0x60,0x50,0xE0 -> 0x61,0x61,0xFF
SETFRAMECOLOR ( HGR_PINK , 0xFF , 0x32 , 0xB5 ) ; // 0xD0,0x40,0xA0 -> 0xFF,0x32,0xB5
# else
SETFRAMECOLOR ( DEEP_RED , 0xD0 , 0x00 , 0x30 ) ; // 0xD0,0x00,0x30
SETFRAMECOLOR ( LIGHT_BLUE , 0x60 , 0xA0 , 0xFF ) ; // 0x60,0xA0,0xFF
SETFRAMECOLOR ( BROWN , 0x80 , 0x50 , 0x00 ) ; // 0x80,0x50,0x00
SETFRAMECOLOR ( ORANGE , 0xFF , 0x80 , 0x00 ) ; // 0xFF,0x80,0x00
SETFRAMECOLOR ( PINK , 0xFF , 0x90 , 0x80 ) ; // 0xFF,0x90,0x80
SETFRAMECOLOR ( AQUA , 0x40 , 0xFF , 0x90 ) ; // 0x40,0xFF,0x90
SETFRAMECOLOR ( HGR_BLACK , 0x00 , 0x00 , 0x00 ) ; // For TV emulation HGR Video Mode
SETFRAMECOLOR ( HGR_WHITE , 0xFF , 0xFF , 0xFE ) ; // BUG: PALETTE COLLAPSE! NOT white!? Win32 collapses the palette if you have duplicate colors!
SETFRAMECOLOR ( HGR_BLUE , 0x00 , 0x80 , 0xFF ) ; // 0x00,0x80,0xFF
SETFRAMECOLOR ( HGR_RED , 0xF0 , 0x50 , 0x00 ) ; // 0xF0,0x50,0x00
SETFRAMECOLOR ( HGR_GREEN , 0x20 , 0xC0 , 0x00 ) ; // 0x20,0xC0,0x00
SETFRAMECOLOR ( HGR_MAGENTA , 0xA0 , 0x00 , 0xFF ) ; // 0xA0,0x00,0xFF
SETFRAMECOLOR ( HGR_GREY1 , 0x80 , 0x80 , 0x80 ) ;
SETFRAMECOLOR ( HGR_GREY2 , 0x80 , 0x80 , 0x80 ) ;
SETFRAMECOLOR ( HGR_YELLOW , 0xD0 , 0xB0 , 0x10 ) ; // 0xD0,0xB0,0x10
SETFRAMECOLOR ( HGR_AQUA , 0x20 , 0xB0 , 0xB0 ) ; // 0x20,0xB0,0xB0
SETFRAMECOLOR ( HGR_PURPLE , 0x60 , 0x50 , 0xE0 ) ; // 0x60,0x50,0xE0
SETFRAMECOLOR ( HGR_PINK , 0xD0 , 0x40 , 0xA0 ) ; // 0xD0,0x40,0xA0
# endif
2006-03-01 00:23:42 +00:00
SETFRAMECOLOR ( MONOCHROME_CUSTOM
, GetRValue ( monochrome )
, GetGValue ( monochrome )
2011-01-09 04:42:46 +00:00
, GetBValue ( monochrome )
) ;
2006-03-01 00:23:42 +00:00
2011-01-07 09:23:16 +00:00
SETFRAMECOLOR ( MONOCHROME_CUSTOM_50
2011-01-09 04:42:46 +00:00
, ( ( GetRValue ( monochrome ) / 2 ) & 0xFF )
, ( ( GetGValue ( monochrome ) / 2 ) & 0xFF )
, ( ( GetBValue ( monochrome ) / 2 ) & 0xFF )
) ;
2011-01-07 09:23:16 +00:00
2011-02-19 22:14:08 +00:00
// SEE: V_CreateLookup_MonoText
2011-02-15 17:16:36 +00:00
SETFRAMECOLOR ( MONOCHROME_AMBER , 0xFF , 0x80 , 0x01 ) ; // Used for Monochrome Hi-Res graphics not text!
SETFRAMECOLOR ( MONOCHROME_GREEN , 0x00 , 0xC0 , 0x01 ) ; // Used for Monochrome Hi-Res graphics not text!
2011-01-09 04:42:46 +00:00
// BUG PALETTE COLLAPSE: WTF?? Soon as we set 0xFF,0xFF,0xFF we lose text colors?!?!
// Windows is collapsing the palette!!!
2011-02-19 22:14:08 +00:00
//SETFRAMECOLOR( MONOCHROME_WHITE , 0xFE,0xFE,0xFE); // Used for Monochrome Hi-Res graphics not text!
2011-01-09 04:42:46 +00:00
2011-02-19 22:14:08 +00:00
# if COLORS_TWEAKED
SETFRAMECOLOR ( CREAM , 0xFF , 0xFB , 0xF0 ) ; // F6
SETFRAMECOLOR ( MEDIUM_GRAY , 0xA0 , 0xA0 , 0xA4 ) ; // F7
SETFRAMECOLOR ( DARK_GRAY , 0x80 , 0x80 , 0x80 ) ; // F8
SETFRAMECOLOR ( RED , 0xFF , 0x00 , 0x00 ) ; // F9
SETFRAMECOLOR ( GREEN , 0x38 , 0xCB , 0x00 ) ; // FA Linards Tweaked
SETFRAMECOLOR ( YELLOW , 0xD5 , 0xD5 , 0x1A ) ; // FB Linards Tweaked
SETFRAMECOLOR ( BLUE , 0x0D , 0xA1 , 0xFF ) ; // FC Linards Tweaked
SETFRAMECOLOR ( MAGENTA , 0xC7 , 0x34 , 0xFF ) ; // FD Linards Tweaked
SETFRAMECOLOR ( CYAN , 0x00 , 0xFF , 0xFF ) ; // FE
SETFRAMECOLOR ( WHITE , 0xFF , 0xFF , 0xFF ) ; // FF
# else
2011-01-09 04:42:46 +00:00
SETFRAMECOLOR ( CREAM , 0xFF , 0xFB , 0xF0 ) ; // F6
SETFRAMECOLOR ( MEDIUM_GRAY , 0xA0 , 0xA0 , 0xA4 ) ; // F7
SETFRAMECOLOR ( DARK_GRAY , 0x80 , 0x80 , 0x80 ) ; // F8
SETFRAMECOLOR ( RED , 0xFF , 0x00 , 0x00 ) ; // F9
SETFRAMECOLOR ( GREEN , 0x00 , 0xFF , 0x00 ) ; // FA
SETFRAMECOLOR ( YELLOW , 0xFF , 0xFF , 0x00 ) ; // FB
SETFRAMECOLOR ( BLUE , 0x00 , 0x00 , 0xFF ) ; // FC
SETFRAMECOLOR ( MAGENTA , 0xFF , 0x00 , 0xFF ) ; // FD
SETFRAMECOLOR ( CYAN , 0x00 , 0xFF , 0xFF ) ; // FE
SETFRAMECOLOR ( WHITE , 0xFF , 0xFF , 0xFF ) ; // FF
2011-02-19 22:14:08 +00:00
# endif
2006-03-01 00:23:42 +00:00
// IF WE ARE IN A PALETTIZED VIDEO MODE, CREATE AN IDENTITY PALETTE
HWND window = GetDesktopWindow ( ) ;
HDC dc = GetDC ( window ) ;
2011-01-30 21:59:17 +00:00
// int GetDeviceCaps( HDC, nIndex );
int colors = GetDeviceCaps ( dc , SIZEPALETTE ) ; // 16/24/32bpp = 0
int system = GetDeviceCaps ( dc , NUMCOLORS ) ; // 16/24/32bpp = -1
2006-03-01 00:23:42 +00:00
2009-02-16 20:30:38 +00:00
#if 0
// DD Full Screen Palette
// Full Screen Debug Colors
BYTE * pTmp ;
pTmp = ( ( BYTE * ) g_pFramebufferinfo ) + sizeof ( BITMAPINFOHEADER ) ;
pTmp + = ( DEBUG_COLORS_START * 4 ) ;
Debug_UpdatePalette ( pTmp ) ;
// GET THE PALETTE ENTRIES OF THE LOGO
RGBQUAD aLogoPalette [ 256 ] ;
ZeroMemory ( aLogoPalette , sizeof ( aLogoPalette ) ) ;
if ( g_hLogoBitmap )
2006-03-01 00:23:42 +00:00
{
2009-02-16 20:30:38 +00:00
BYTE * pSrc = NULL ;
BITMAP bmp ;
PBITMAPINFO pbmi ;
// WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if ( GetObject ( g_hLogoBitmap , sizeof ( BITMAP ) , ( LPSTR ) & bmp ) )
{
pSrc = ( BYTE * ) pbmi - > bmiColors ;
// Logo uses 128 colors
pTmp = ( ( BYTE * ) g_pFramebufferinfo ) + sizeof ( BITMAPINFOHEADER ) ;
pTmp + = ( LOGO_COLORS_START * 4 ) ;
int iPal = 0 ;
for ( iPal = 0 ; iPal < 128 ; iPal + + )
{
* pTmp + + = * pSrc + + ;
* pTmp + + = * pSrc + + ;
* pTmp + + = * pSrc + + ;
* pTmp + + = * pSrc + + ;
}
}
}
# endif
2006-02-25 20:50:29 +00:00
2011-01-09 04:42:46 +00:00
int bRasterPalette = ( GetDeviceCaps ( dc , RASTERCAPS ) & RC_PALETTE ) ;
if ( bRasterPalette & & ( colors < = 256 ) )
2009-02-16 20:30:38 +00:00
{
2006-03-01 00:23:42 +00:00
// GET THE PALETTE ENTRIES OF THE LOGO
2009-02-16 20:30:38 +00:00
RGBQUAD aLogoPalette [ 256 ] ;
ZeroMemory ( aLogoPalette , sizeof ( aLogoPalette ) ) ;
2006-06-29 03:28:25 +00:00
if ( g_hLogoBitmap ) {
2006-03-01 00:23:42 +00:00
HDC memdc = CreateCompatibleDC ( dc ) ;
2006-06-29 03:28:25 +00:00
SelectObject ( memdc , g_hLogoBitmap ) ;
2009-02-16 20:30:38 +00:00
GetDIBColorTable ( memdc , 0 , colors , aLogoPalette ) ;
2006-03-01 00:23:42 +00:00
DeleteDC ( memdc ) ;
}
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// CREATE A PALETTE ENTRY ARRAY
2011-01-09 04:42:46 +00:00
LOGPALETTE * paldata = ( LOGPALETTE * ) VirtualAlloc (
NULL ,
sizeof ( LOGPALETTE ) + 256 * sizeof ( PALETTEENTRY ) ,
MEM_COMMIT ,
PAGE_READWRITE
) ;
2006-03-01 00:23:42 +00:00
paldata - > palVersion = 0x300 ;
paldata - > palNumEntries = colors ;
GetSystemPaletteEntries ( dc , 0 , colors , paldata - > palPalEntry ) ;
// FILL IN THE PALETTE ENTRIES
int paletteindex = 0 ;
int logoindex = 0 ;
int halftoneindex = 0 ;
// COPY THE SYSTEM PALETTE ENTRIES AT THE BEGINNING OF THE PALETTE
for ( ; paletteindex < system / 2 ; paletteindex + + )
paldata - > palPalEntry [ paletteindex ] . peFlags = 0 ;
// FILL IN THE MIDDLE PORTION OF THE PALETTE WITH OUR OWN COLORS
for ( int ourindex = DEEP_RED ; ourindex < = NUM_COLOR_PALETTE ; ourindex + + ) {
2008-08-25 00:36:48 +00:00
paldata - > palPalEntry [ paletteindex ] . peRed = g_pFramebufferinfo - > bmiColors [ ourindex ] . rgbRed ;
paldata - > palPalEntry [ paletteindex ] . peGreen = g_pFramebufferinfo - > bmiColors [ ourindex ] . rgbGreen ;
paldata - > palPalEntry [ paletteindex ] . peBlue = g_pFramebufferinfo - > bmiColors [ ourindex ] . rgbBlue ;
2006-03-01 00:23:42 +00:00
paldata - > palPalEntry [ paletteindex ] . peFlags = PC_NOCOLLAPSE ;
paletteindex + + ;
}
2009-02-16 20:30:38 +00:00
2006-03-01 00:23:42 +00:00
for ( ; paletteindex < colors - system / 2 ; paletteindex + + ) {
// IF THIS PALETTE ENTRY IS NEEDED FOR THE LOGO, COPY IN THE LOGO COLOR
2009-02-16 20:30:38 +00:00
if ( aLogoPalette [ logoindex ] . rgbRed & &
aLogoPalette [ logoindex ] . rgbGreen & &
aLogoPalette [ logoindex ] . rgbBlue )
{
paldata - > palPalEntry [ paletteindex ] . peRed = aLogoPalette [ logoindex ] . rgbRed ;
paldata - > palPalEntry [ paletteindex ] . peGreen = aLogoPalette [ logoindex ] . rgbGreen ;
paldata - > palPalEntry [ paletteindex ] . peBlue = aLogoPalette [ logoindex ] . rgbBlue ;
2006-03-01 00:23:42 +00:00
}
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// OTHERWISE, ADD A HALFTONING COLOR, SO THAT OTHER APPLICATIONS
// RUNNING IN THE BACKGROUND WILL HAVE SOME REASONABLE COLORS TO USE
2009-02-16 20:30:38 +00:00
else
{
static BYTE halftonetable [ 6 ] = { 32 , 64 , 96 , 160 , 192 , 224 } ;
paldata - > palPalEntry [ paletteindex ] . peRed = halftonetable [ halftoneindex % 6 ] ;
paldata - > palPalEntry [ paletteindex ] . peGreen = halftonetable [ halftoneindex / 6 % 6 ] ;
paldata - > palPalEntry [ paletteindex ] . peBlue = halftonetable [ halftoneindex / 36 % 6 ] ;
+ + halftoneindex ;
2006-03-01 00:23:42 +00:00
}
2009-02-16 20:30:38 +00:00
2006-03-01 00:23:42 +00:00
+ + logoindex ;
paldata - > palPalEntry [ paletteindex ] . peFlags = PC_NOCOLLAPSE ;
}
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// COPY THE SYSTEM PALETTE ENTRIES AT THE END OF THE PALETTE
for ( ; paletteindex < colors ; paletteindex + + )
paldata - > palPalEntry [ paletteindex ] . peFlags = 0 ;
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// FILL THE FRAME BUFFER TABLE WITH COLORS FROM OUR PALETTE
2009-02-16 20:30:38 +00:00
for ( int iPal = 0 ; iPal < colors ; iPal + + ) {
g_pFramebufferinfo - > bmiColors [ iPal ] . rgbRed = paldata - > palPalEntry [ iPal ] . peRed ;
g_pFramebufferinfo - > bmiColors [ iPal ] . rgbGreen = paldata - > palPalEntry [ iPal ] . peGreen ;
g_pFramebufferinfo - > bmiColors [ iPal ] . rgbBlue = paldata - > palPalEntry [ iPal ] . peBlue ;
2006-03-01 00:23:42 +00:00
}
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// CREATE THE PALETTE
2006-06-29 03:28:25 +00:00
g_hPalette = CreatePalette ( paldata ) ;
2006-03-01 00:23:42 +00:00
VirtualFree ( paldata , 0 , MEM_RELEASE ) ;
}
ReleaseDC ( window , dc ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_MonoText ( HDC hDstDC )
2009-02-14 03:53:28 +00:00
{
2011-01-09 04:42:46 +00:00
static HBITMAP hCharBitmap [ 4 ] ;
HDC hSrcDC = CreateCompatibleDC ( hDstDC ) ;
2006-02-25 20:50:29 +00:00
2011-01-09 04:42:46 +00:00
hCharBitmap [ 0 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET40 " ) ) ;
hCharBitmap [ 1 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET82 " ) ) ;
hCharBitmap [ 2 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET8C " ) ) ; // FIXME: Pravets 8M probably has the same charset as Pravets 8C
hCharBitmap [ 3 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET8C " ) ) ;
HBRUSH hBrush ;
switch ( g_eVideoType )
{
case VT_MONO_AMBER : hBrush = CreateSolidBrush ( RGB ( 0xFF , 0x80 , 0x00 ) ) ; break ;
case VT_MONO_GREEN : hBrush = CreateSolidBrush ( RGB ( 0x00 , 0xC0 , 0x00 ) ) ; break ;
case VT_MONO_WHITE : hBrush = CreateSolidBrush ( RGB ( 0xFF , 0xFF , 0xFF ) ) ; break ;
default : hBrush = CreateSolidBrush ( monochrome ) ; break ;
}
SelectObject ( hSrcDC , hCharBitmap [ g_nCharsetType ] ) ;
SelectObject ( hDstDC , hBrush ) ;
// TODO: Update with APPLE_FONT_Y_ values
BitBlt ( hDstDC , SRCOFFS_40COL , 0 , 256 , 512 , hSrcDC , 0 , 0 , MERGECOPY ) ;
BitBlt ( hDstDC , SRCOFFS_IIPLUS , 0 , 256 , 256 , hSrcDC , 0 , 512 , MERGECOPY ) ;
StretchBlt ( hDstDC , SRCOFFS_80COL , 0 , 128 , 512 , hSrcDC , 0 , 0 , 256 , 512 , MERGECOPY ) ;
SelectObject ( hDstDC , GetStockObject ( NULL_BRUSH ) ) ;
DeleteObject ( hBrush ) ;
DeleteDC ( hSrcDC ) ;
DeleteObject ( hCharBitmap ) ;
}
//===========================================================================
void V_CreateDIBSections ( )
{
2009-02-14 03:53:28 +00:00
CopyMemory ( g_pSourceHeader - > bmiColors , g_pFramebufferinfo - > bmiColors , 256 * sizeof ( RGBQUAD ) ) ;
2006-02-25 20:50:29 +00:00
2009-02-14 03:53:28 +00:00
// CREATE THE DEVICE CONTEXT
HWND window = GetDesktopWindow ( ) ;
HDC dc = GetDC ( window ) ;
if ( g_hDeviceDC )
{
DeleteDC ( g_hDeviceDC ) ;
}
g_hDeviceDC = CreateCompatibleDC ( dc ) ;
2006-02-25 20:50:29 +00:00
2011-01-08 04:21:36 +00:00
// CREATE THE FRAME BUFFER DIB SECTION
if ( g_hDeviceBitmap )
DeleteObject ( g_hDeviceBitmap ) ;
g_hDeviceBitmap = CreateDIBSection (
2011-01-09 04:42:46 +00:00
dc ,
g_pFramebufferinfo ,
DIB_RGB_COLORS ,
2011-01-08 04:21:36 +00:00
( LPVOID * ) & g_pFramebufferbits , 0 , 0
) ;
SelectObject ( g_hDeviceDC , g_hDeviceBitmap ) ;
// CREATE THE SOURCE IMAGE DIB SECTION
HDC sourcedc = CreateCompatibleDC ( dc ) ;
ReleaseDC ( window , dc ) ;
if ( g_hSourceBitmap )
DeleteObject ( g_hSourceBitmap ) ;
2011-01-09 04:42:46 +00:00
2011-01-08 04:21:36 +00:00
g_hSourceBitmap = CreateDIBSection (
2011-01-09 04:42:46 +00:00
sourcedc ,
g_pSourceHeader ,
DIB_RGB_COLORS ,
2011-01-08 04:21:36 +00:00
( LPVOID * ) & g_pSourcePixels , 0 , 0
) ;
SelectObject ( sourcedc , g_hSourceBitmap ) ;
2006-02-25 20:50:29 +00:00
2006-06-29 03:28:25 +00:00
// CREATE THE OFFSET TABLE FOR EACH SCAN LINE IN THE SOURCE IMAGE
for ( int y = 0 ; y < MAX_SOURCE_Y ; y + + )
g_aSourceStartofLine [ y ] = g_pSourcePixels + SRCOFFS_TOTAL * ( ( MAX_SOURCE_Y - 1 ) - y ) ;
2006-02-25 20:50:29 +00:00
2006-03-01 00:23:42 +00:00
// DRAW THE SOURCE IMAGE INTO THE SOURCE BIT BUFFER
2011-02-19 22:14:08 +00:00
ZeroMemory ( g_pSourcePixels , SRCOFFS_TOTAL * 512 ) ; // 32 bytes/pixel * 16 colors = 512 bytes/row
2006-03-01 00:23:42 +00:00
2011-01-09 04:42:46 +00:00
// First monochrome mode is seperate from others
2014-07-10 22:52:26 +01:00
if ( ( g_eVideoType > = VT_COLOR_STANDARD ) & & ( g_eVideoType < VT_MONO_AMBER ) )
2006-03-01 00:23:42 +00:00
{
2011-02-19 22:14:08 +00:00
V_CreateLookup_Text ( sourcedc ) ;
V_CreateLookup_Lores ( ) ;
2011-01-08 02:58:20 +00:00
2011-02-19 22:14:08 +00:00
if ( g_eVideoType = = VT_COLOR_TVEMU )
V_CreateLookup_Hires ( ) ;
else
V_CreateLookup_HiResHalfPixel_Authentic ( ) ;
V_CreateLookup_DoubleHires ( ) ;
2006-03-01 00:23:42 +00:00
}
else
{
2011-02-19 22:14:08 +00:00
V_CreateLookup_MonoText ( sourcedc ) ;
V_CreateLookup_MonoLoRes ( ) ;
2011-01-08 02:58:20 +00:00
2011-01-09 04:42:46 +00:00
switch ( g_eVideoType )
2011-01-08 02:58:20 +00:00
{
2011-02-15 17:16:36 +00:00
case VT_MONO_AMBER : /* intentional fall-thru */
case VT_MONO_GREEN : /* intentional fall-thru */
case VT_MONO_WHITE : /* intentional fall-thru */
2011-02-19 22:14:08 +00:00
case VT_MONO_HALFPIXEL_REAL : V_CreateLookup_MonoHiResHalfPixel_Real ( ) ; break ;
default : V_CreateLookup_MonoHiRes ( ) ; break ;
2011-01-08 02:58:20 +00:00
}
2011-02-19 22:14:08 +00:00
V_CreateLookup_MonoDoubleHiRes ( ) ;
2006-03-01 00:23:42 +00:00
}
DeleteDC ( sourcedc ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_DoubleHires ( )
{
2006-02-25 20:50:29 +00:00
# define OFFSET 3
# define SIZE 10
2011-02-19 22:14:08 +00:00
2006-02-25 20:50:29 +00:00
for ( int column = 0 ; column < 256 ; column + + ) {
int coloffs = SIZE * column ;
for ( unsigned byteval = 0 ; byteval < 256 ; byteval + + ) {
int color [ SIZE ] ;
ZeroMemory ( color , sizeof ( color ) ) ;
unsigned pattern = MAKEWORD ( byteval , column ) ;
2006-02-28 16:39:34 +00:00
int pixel ;
for ( pixel = 1 ; pixel < 15 ; pixel + + ) {
2006-02-25 20:50:29 +00:00
if ( pattern & ( 1 < < pixel ) ) {
int pixelcolor = 1 < < ( ( pixel - OFFSET ) & 3 ) ;
if ( ( pixel > = OFFSET + 2 ) & & ( pixel < SIZE + OFFSET + 2 ) & & ( pattern & ( 0x7 < < ( pixel - 4 ) ) ) )
color [ pixel - ( OFFSET + 2 ) ] | = pixelcolor ;
if ( ( pixel > = OFFSET + 1 ) & & ( pixel < SIZE + OFFSET + 1 ) & & ( pattern & ( 0xF < < ( pixel - 4 ) ) ) )
color [ pixel - ( OFFSET + 1 ) ] | = pixelcolor ;
if ( ( pixel > = OFFSET + 0 ) & & ( pixel < SIZE + OFFSET + 0 ) )
color [ pixel - ( OFFSET + 0 ) ] | = pixelcolor ;
if ( ( pixel > = OFFSET - 1 ) & & ( pixel < SIZE + OFFSET - 1 ) & & ( pattern & ( 0xF < < ( pixel + 1 ) ) ) )
color [ pixel - ( OFFSET - 1 ) ] | = pixelcolor ;
if ( ( pixel > = OFFSET - 2 ) & & ( pixel < SIZE + OFFSET - 2 ) & & ( pattern & ( 0x7 < < ( pixel + 2 ) ) ) )
color [ pixel - ( OFFSET - 2 ) ] | = pixelcolor ;
}
}
2009-02-16 20:30:38 +00:00
if ( g_eVideoType = = VT_COLOR_TEXT_OPTIMIZED )
2006-02-25 20:50:29 +00:00
{
2011-02-19 22:14:08 +00:00
// Activate for fringe reduction on white HGR text - drawback: loss of color mix patterns in HGR Video Mode.
2006-02-25 20:50:29 +00:00
for ( pixel = 0 ; pixel < 13 ; pixel + + )
{
if ( ( pattern & ( 0xF < < pixel ) ) = = ( unsigned ) ( 0xF < < pixel ) )
for ( int pos = pixel ; pos < pixel + 4 ; pos + + )
if ( pos > = OFFSET & & pos < SIZE + OFFSET )
color [ pos - OFFSET ] = 15 ;
}
}
int y = byteval < < 1 ;
for ( int x = 0 ; x < SIZE ; x + + ) {
2011-02-19 22:14:08 +00:00
SETSOURCEPIXEL ( SRCOFFS_DHIRES + coloffs + x , y , DoubleHiresPalIndex [ color [ x ] ] ) ;
SETSOURCEPIXEL ( SRCOFFS_DHIRES + coloffs + x , y + 1 , DoubleHiresPalIndex [ color [ x ] ] ) ;
2006-02-25 20:50:29 +00:00
}
}
}
# undef SIZE
# undef OFFSET
}
2006-03-01 00:23:42 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_Hires ( )
2006-03-01 00:23:42 +00:00
{
2008-08-27 08:47:52 +00:00
int iMonochrome = GetMonochromeIndex ( ) ;
2011-02-19 22:14:08 +00:00
// BYTE colorval[6] = {MAGENTA,BLUE,GREEN,ORANGE,BLACK,WHITE};
2006-03-01 00:23:42 +00:00
// BYTE colorval[6] = {HGR_MAGENTA,HGR_BLUE,HGR_GREEN,HGR_RED,HGR_BLACK,HGR_WHITE};
for ( int iColumn = 0 ; iColumn < 16 ; iColumn + + )
{
int coloffs = iColumn < < 5 ;
for ( unsigned iByte = 0 ; iByte < 256 ; iByte + + )
{
int aPixels [ 11 ] ;
aPixels [ 0 ] = iColumn & 4 ;
aPixels [ 1 ] = iColumn & 8 ;
aPixels [ 9 ] = iColumn & 1 ;
aPixels [ 10 ] = iColumn & 2 ;
int nBitMask = 1 ;
int iPixel ;
for ( iPixel = 2 ; iPixel < 9 ; iPixel + + ) {
aPixels [ iPixel ] = ( ( iByte & nBitMask ) ! = 0 ) ;
nBitMask < < = 1 ;
}
int hibit = ( ( iByte & 0x80 ) ! = 0 ) ;
int x = 0 ;
int y = iByte < < 1 ;
while ( x < 28 )
{
int adj = ( x > = 14 ) < < 1 ;
int odd = ( x > = 14 ) ;
for ( iPixel = 2 ; iPixel < 9 ; iPixel + + )
{
int color = CM_Black ;
if ( aPixels [ iPixel ] )
{
if ( aPixels [ iPixel - 1 ] | | aPixels [ iPixel + 1 ] )
color = CM_White ;
else
color = ( ( odd ^ ( iPixel & 1 ) ) < < 1 ) | hibit ;
}
else if ( aPixels [ iPixel - 1 ] & & aPixels [ iPixel + 1 ] )
{
2011-02-19 22:14:08 +00:00
// Activate fringe reduction on white HGR text - drawback: loss of color mix patterns in HGR video mode.
// VT_COLOR_STANDARD = Fill in colors in between white pixels
// VT_COLOR_TVEMU = Fill in colors in between white pixels (Post Processing will mix/merge colors)
// VT_COLOR_TEXT_OPTIMIZED --> !(aPixels[iPixel-2] && aPixels[iPixel+2]) = Don't fill in colors in between white
if ( ( g_eVideoType = = VT_COLOR_TVEMU ) | | ! ( aPixels [ iPixel - 2 ] & & aPixels [ iPixel + 2 ] ) )
color = ( ( odd ^ ! ( iPixel & 1 ) ) < < 1 ) | hibit ; // No white HGR text optimization
2006-03-01 00:23:42 +00:00
}
2011-02-19 22:14:08 +00:00
//if (g_eVideoType == VT_MONO_AUTHENTIC) {
2008-08-27 08:47:52 +00:00
// int nMonoColor = (color != CM_Black) ? iMonochrome : BLACK;
// SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj ,y , nMonoColor); // buggy
// SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj+1,y , nMonoColor); // buggy
// SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj ,y+1,BLACK); // BL
// SETSOURCEPIXEL(SRCOFFS_HIRES+coloffs+x+adj+1,y+1,BLACK); // BR
2011-02-19 22:14:08 +00:00
//} else
2008-08-27 08:47:52 +00:00
{
// Colors - Top/Bottom Left/Right
// cTL cTR
// cBL cBR
2011-02-19 22:14:08 +00:00
SETSOURCEPIXEL ( SRCOFFS_HIRES + coloffs + x + adj , y , HiresToPalIndex [ color ] ) ; // cTL
SETSOURCEPIXEL ( SRCOFFS_HIRES + coloffs + x + adj + 1 , y , HiresToPalIndex [ color ] ) ; // cTR
SETSOURCEPIXEL ( SRCOFFS_HIRES + coloffs + x + adj , y + 1 , HiresToPalIndex [ color ] ) ; // cBL
SETSOURCEPIXEL ( SRCOFFS_HIRES + coloffs + x + adj + 1 , y + 1 , HiresToPalIndex [ color ] ) ; // cBR
2008-08-27 08:47:52 +00:00
}
2006-03-01 00:23:42 +00:00
x + = 2 ;
}
}
}
}
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_Lores ( )
{
for ( int color = 0 ; color < 16 ; color + + )
for ( int x = 0 ; x < 16 ; x + + )
for ( int y = 0 ; y < 16 ; y + + )
SETSOURCEPIXEL ( SRCOFFS_LORES + x , ( color < < 4 ) + y , LoresResColors [ color ] ) ;
2006-02-25 20:50:29 +00:00
}
2006-03-01 00:23:42 +00:00
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_MonoDoubleHiRes ( )
2006-03-01 00:23:42 +00:00
{
int iMonochrome = GetMonochromeIndex ( ) ;
for ( int column = 0 ; column < 256 ; column + + )
{
int coloffs = 10 * column ;
for ( unsigned byteval = 0 ; byteval < 256 ; byteval + + )
{
unsigned pattern = MAKEWORD ( byteval , column ) ;
int y = byteval < < 1 ;
for ( int x = 0 ; x < 10 ; x + + )
{
BYTE colorval = pattern & ( 1 < < ( x + 3 ) ) ? iMonochrome : BLACK ;
2009-02-14 03:53:28 +00:00
#if 0
2009-02-16 20:30:38 +00:00
if ( g_eVideoType = = VT_MONO_AUTHENTIC )
2008-08-27 08:47:52 +00:00
{
SETSOURCEPIXEL ( SRCOFFS_DHIRES + coloffs + x , y , colorval ) ;
SETSOURCEPIXEL ( SRCOFFS_DHIRES + coloffs + x , y + 1 , BLACK ) ;
}
else
2009-02-14 03:53:28 +00:00
# endif
2008-08-27 08:47:52 +00:00
{
SETSOURCEPIXEL ( SRCOFFS_DHIRES + coloffs + x , y , colorval ) ;
SETSOURCEPIXEL ( SRCOFFS_DHIRES + coloffs + x , y + 1 , colorval ) ;
}
2006-03-01 00:23:42 +00:00
}
}
}
}
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_MonoHiRes ( )
2006-03-01 00:23:42 +00:00
{
2014-07-10 22:52:26 +01:00
const int iMonochrome = GetMonochromeIndex ( ) ;
2006-03-01 00:23:42 +00:00
for ( int column = 0 ; column < 512 ; column + = 16 )
{
2011-01-09 04:42:46 +00:00
for ( int y = 0 ; y < 512 ; y + = 2 ) // optimization: Byte=0..FF, Row=Byte*2
2006-03-01 00:23:42 +00:00
{
2011-01-09 04:42:46 +00:00
unsigned val = ( y > > 1 ) ; // iByte = (y / 2)
for ( int x = 0 ; x < 16 ; x + = 2 ) // 8 pixels
2006-03-01 00:23:42 +00:00
{
BYTE colorval = ( val & 1 ) ? iMonochrome : BLACK ;
val > > = 1 ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + column + x , y , colorval ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + column + x + 1 , y , colorval ) ;
2008-08-27 08:47:52 +00:00
{
SETSOURCEPIXEL ( SRCOFFS_HIRES + column + x , y + 1 , colorval ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + column + x + 1 , y + 1 , colorval ) ;
}
2006-03-01 00:23:42 +00:00
}
}
}
2014-07-10 22:52:26 +01:00
}
//===========================================================================
void V_CreateLookup_HiResHalfPixel_Authentic ( ) // Colors are solid (100% coverage)
{
// 2-bits from previous byte, 2-bits from next byte = 2^4 = 16 total permutations
2011-01-07 09:23:16 +00:00
for ( int iColumn = 0 ; iColumn < 16 ; iColumn + + )
{
2014-07-10 22:52:26 +01:00
int offsetx = iColumn < < 5 ; // every column is 32 bytes wide -- 7 apple pixels = 14 pixels + 2 pad + 14 pixels + 2 pad
2011-01-07 09:23:16 +00:00
for ( unsigned iByte = 0 ; iByte < 256 ; iByte + + )
{
2014-07-10 22:52:26 +01:00
int aPixels [ 11 ] ; // c2 c1 b7 b6 b5 b4 b3 b2 b1 b0 c8 c4
2011-01-07 09:23:16 +00:00
2014-07-10 22:52:26 +01:00
/*
aPixel [ i ]
A 9 | 8 7 6 5 4 3 2 | 1 0
Z W | b b b b b b b | X Y
- - - - + - - - - - - - - - - - - - + - - - -
prev | existing | next
bits | hi - res byte | bits
Legend :
XYZW = iColumn in binary
b = Bytes in binary
*/
// aPixel[] = 48bbbbbbbb12, where b = iByte in binary, # is bit-n of column
aPixels [ 0 ] = iColumn & 4 ; // previous byte, 2nd last pixel
aPixels [ 1 ] = iColumn & 8 ; // previous byte, last pixel
aPixels [ 9 ] = iColumn & 1 ; // next byte, first pixel
aPixels [ 10 ] = iColumn & 2 ; // next byte, second pixel
2011-01-07 09:23:16 +00:00
2014-07-10 22:52:26 +01:00
// Convert raw pixel Byte value to binary and stuff into bit array of pixels on off
2011-01-07 09:23:16 +00:00
int nBitMask = 1 ;
int iPixel ;
for ( iPixel = 2 ; iPixel < 9 ; iPixel + + )
{
aPixels [ iPixel ] = ( ( iByte & nBitMask ) ! = 0 ) ;
nBitMask < < = 1 ;
}
2014-07-10 22:52:26 +01:00
int hibit = ( iByte > > 7 ) & 1 ; // ((iByte & 0x80) != 0);
2011-01-07 09:23:16 +00:00
int x = 0 ;
int y = iByte < < 1 ;
2014-07-10 22:52:26 +01:00
/* Test cases
81 blue
2000 : D5 AA D5 AA
82 orange
2800 : AA D5 AA D5
FF white bleed " thru "
3000 : 7F 80 7F 80
3800 : FF 80 FF 80
2028 : 80 7F 80 7F
2828 : 80 FF 80 FF
Edge Case for Half Luminance !
2000 : C4 00 // Green HalfLumBlue
2400 : C4 80 // Green Green
Edge Case for Color Bleed !
2000 : 40 00
2400 : 40 80
*/
// Fixup missing pixels that normally have been scan-line shifted -- Apple "half-pixel" -- but cross 14-pixel boundaries.
if ( hibit )
{
if ( aPixels [ 1 ] ) // preceeding pixel on?
#if 0 // Optimization: Doesn't seem to matter if we ignore the 2 pixels of the next byte
for ( iPixel = 0 ; iPixel < 9 ; iPixel + + ) // NOTE: You MUST start with the preceding 2 pixels !!!
if ( aPixels [ iPixel ] ) // pixel on
# endif
{
if ( aPixels [ 2 ] | | aPixels [ 0 ] ) // White if pixel from previous byte and first pixel of this byte is on
{
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y , HGR_WHITE ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y + 1 , HGR_WHITE ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y , HGR_WHITE ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y + 1 , HGR_WHITE ) ;
} else { // Optimization: odd = (iPixel & 1); if (!odd) case is same as if(odd) !!! // Reference: Gumball - Gumball Machine
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y , HGR_RED ) ; // left half of orange pixels
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y + 1 , HGR_RED ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y , HGR_BLUE ) ; // right half of blue pixels 4, 11, 18, ...
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y + 1 , HGR_BLUE ) ;
}
}
# if HALF_PIXEL_SOLID
// Test Patterns
// 81 blue
// 2000:D5 AA D5 AA -> 2001:AA D5 should not have black gap, should be blue
// 82 orange
// 2800:AA D5 AA D5
// Game: Elite -- Loading Logo
// 2444:BB F7 -> 2000:BB F7 // Should not have orange in-between gap -- Elite "Firebird" Logo
// -> 2400:00 BB F7 // Should not have blue in-between gap )
// 21D0:C0 00 -> HalfLumBlue
// 25D0:C0 D0 88 -> Blue black orange black orange
// 29D0:C0 90 88 -> Blue black orange
// Game: Ultima 4 -- Ultima 4 Logo - bottom half of screen has a "mini-game" / demo -- far right has tree and blue border
// 2176:2A AB green black_gap white blue_border // Should have black gap between green and white
else if ( aPixels [ 0 ] ) // prev prev pixel on
{
// Game: Gumball
// 218E:AA 97 => 2000: A9 87 orange_white // Should have no gap between orange and white
// 229A:AB A9 87 -> 2000: 00 A9 87 white orange black blue_white // Should have no gap between blue and white
// 2001:BB F7 white blue white (Gumball Intermission)
// Torture Half-Pixel HGR Tests: This is a real bitch to solve -- we really need to check:
// if (hibit_prev_byte && !aPixels[iPixel-3] && aPixels[iPixel-2] && !aPixels[iPixel] && hibit_this_byte) then set first half-pixel of this byte to either blue or orange
// 2000:A9 87 halfblack blue black black orange black orange black
// 2400:BB F7 halfblack white white black white white white halfblack
// or
// 2000:A0 83 orange should "bleed" thru
// 2400:B0 83 should have black gap
if ( aPixels [ 2 ] )
# if HALF_PIXEL_BLEED // No Half-Pixel Bleed
if ( aPixels [ 3 ] ) {
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y , DARK_BLUE ) ; // Gumball: 229A: AB A9 87
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y + 1 , DARK_BLUE ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y , BROWN ) ; // half luminance red Elite: 2444: BB F7
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y + 1 , BROWN ) ; // half luminance red Gumball: 218E: AA 97
} else {
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y , HGR_BLUE ) ; // 2000:D5 AA D5
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y + 1 , HGR_BLUE ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y , HGR_RED ) ; // 2000: AA D5
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y + 1 , HGR_RED ) ;
}
# else
if ( ( g_eVideoType = = VT_COLOR_STANDARD ) | | ( ! aPixels [ 3 ] ) )
{ // "Text optimized" IF this pixel on, and adjacent right pixel off, then colorize first half-pixel of this byte
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y , HGR_BLUE ) ; // 2000:D5 AA D5
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 0 , y + 1 , HGR_BLUE ) ;
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y , HGR_RED ) ; // 2000: AA D5
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + 16 , y + 1 , HGR_RED ) ;
}
# endif // HALF_PIXEL_BLEED
}
# endif // HALF_PIXEL_SOLID
}
x + = hibit ;
2011-01-07 09:23:16 +00:00
while ( x < 28 )
{
2014-07-10 22:52:26 +01:00
int adj = ( x > = 14 ) < < 1 ; // Adjust start of 7 last pixels to be 16-byte aligned!
2011-01-07 09:23:16 +00:00
int odd = ( x > = 14 ) ;
for ( iPixel = 2 ; iPixel < 9 ; iPixel + + )
{
2014-07-10 22:52:26 +01:00
int color = CM_Black ;
if ( aPixels [ iPixel ] ) // pixel on
2011-01-07 09:23:16 +00:00
{
2014-07-10 22:52:26 +01:00
color = CM_White ;
if ( aPixels [ iPixel - 1 ] | | aPixels [ iPixel + 1 ] ) // adjacent pixels are always white
color = CM_White ;
2011-01-07 09:23:16 +00:00
else
2014-07-10 22:52:26 +01:00
color = ( ( odd ^ ( iPixel & 1 ) ) < < 1 ) | hibit ; // map raw color to our hi-res colors
2011-01-07 11:43:36 +00:00
}
2014-07-10 22:52:26 +01:00
# if HALF_PIXEL_SOLID
else if ( aPixels [ iPixel - 1 ] & & aPixels [ iPixel + 1 ] ) // IF prev_pixel && next_pixel THEN
2011-01-07 11:43:36 +00:00
{
2014-07-10 22:52:26 +01:00
// Activate fringe reduction on white HGR text - drawback: loss of color mix patterns in HGR video mode.
if (
( g_eVideoType = = VT_COLOR_STANDARD ) // Fill in colors in between white pixels
| | ( g_eVideoType = = VT_COLOR_TVEMU ) // Fill in colors in between white pixels (Post Processing will mix/merge colors)
| | ! ( aPixels [ iPixel - 2 ] & & aPixels [ iPixel + 2 ] ) ) // VT_COLOR_TEXT_OPTIMIZED -> Don't fill in colors in between white
2011-01-07 11:43:36 +00:00
{
2014-07-10 22:52:26 +01:00
// Test Pattern: Ultima 4 Logo - Castle
// 3AC8: 36 5B 6D 36
color = ( ( odd ^ ! ( iPixel & 1 ) ) < < 1 ) | hibit ; // No white HGR text optimization
2011-02-19 22:14:08 +00:00
}
2011-02-17 17:29:30 +00:00
}
# endif
2011-02-19 22:14:08 +00:00
// Colors - Top/Bottom Left/Right
// cTL cTR
// cBL cBR
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + adj , y , HiresToPalIndex [ color ] ) ; // cTL
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + adj + 1 , y , HiresToPalIndex [ color ] ) ; // cTR
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + adj , y + 1 , HiresToPalIndex [ color ] ) ; // cBL
SETSOURCEPIXEL ( SRCOFFS_HIRES + offsetx + x + adj + 1 , y + 1 , HiresToPalIndex [ color ] ) ; // cBR
2011-02-17 17:29:30 +00:00
x + = 2 ;
}
}
}
}
}
2011-01-08 21:29:27 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_MonoHiResHalfPixel_Real ( )
2011-01-08 21:29:27 +00:00
{
int iMono = GetMonochromeIndex ( ) ;
for ( int iColumn = 0 ; iColumn < 16 ; iColumn + + )
{
int offset = iColumn < < 5 ; // every column is 32 bytes wide
for ( unsigned iByte = 0 ; iByte < 256 ; iByte + + )
{
int aPixels [ 11 ] ; // c2 c1 b7 b6 b5 b4 b3 b2 b1 b0 c8 c4
aPixels [ 0 ] = iColumn & 4 ;
aPixels [ 1 ] = iColumn & 8 ;
aPixels [ 9 ] = iColumn & 1 ;
aPixels [ 10 ] = iColumn & 2 ;
int nBitMask = 1 ;
int iPixel ;
for ( iPixel = 2 ; iPixel < 9 ; iPixel + + )
{
aPixels [ iPixel ] = ( ( iByte & nBitMask ) ! = 0 ) ;
nBitMask < < = 1 ;
}
int hibit = ( iByte > > 7 ) & 1 ; // ((iByte & 0x80) != 0);
int x = 0 ;
int y = iByte < < 1 ;
2011-01-09 00:45:12 +00:00
// Fixup missing pixels that normally have been scan-line shifted -- Apple "half-pixel" -- but cross 14-pixel boundaries.
2011-01-08 21:29:27 +00:00
if ( hibit )
{
2011-01-09 00:40:21 +00:00
/* Test Cases
// Games
Archon Logo
Gumball ( at Machine )
// Applesoft
HGR : HCOLOR = 5 : HPLOT 0 , 0 TO 279 , 0
2011-01-09 00:45:12 +00:00
2011-01-09 00:40:21 +00:00
// Blue
// Orange
2011-01-09 00:45:12 +00:00
CALL - 151
C050 C052 C057
2000 : D0 80 00
2011-01-09 00:40:21 +00:00
2800 : 80 D0 00
*/
if ( aPixels [ 1 ] ) // preceeding pixel on?
2011-01-08 21:29:27 +00:00
{
2011-01-09 00:40:21 +00:00
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x , y , iMono ) ; // first 7 HGR_BLUE
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x , y + 1 , iMono ) ; // first 7
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x + 16 , y , iMono ) ; // second 7 HGR_RED
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x + 16 , y + 1 , iMono ) ; // second 7
2011-01-08 21:29:27 +00:00
}
}
while ( x < 28 )
{
int adj = ( x > = 14 ) < < 1 ; // Adjust start of 7 last pixels to be 16-byte aligned!
int odd = ( x > = 14 ) ;
for ( iPixel = 2 ; iPixel < 9 ; iPixel + + )
{
int color = aPixels [ iPixel ] ? iMono : HGR_BLACK ;
// Colors - Top/Bottom Left/Right
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x + adj + hibit , y , color ) ; // TL
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x + adj + 1 + hibit , y , color ) ; // BL
2011-01-09 00:45:12 +00:00
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x + adj + hibit , y + 1 , color ) ; // BL
2011-01-08 21:29:27 +00:00
SETSOURCEPIXEL ( SRCOFFS_HIRES + offset + x + adj + 1 + hibit , y + 1 , color ) ; // BR
x + = 2 ;
}
}
}
}
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_MonoLoRes ( ) {
2006-03-01 00:23:42 +00:00
int iMonochrome = GetMonochromeIndex ( ) ;
2008-08-27 08:47:52 +00:00
for ( int color = 0 ; color < 16 ; color + + )
{
for ( int x = 0 ; x < 16 ; x + + )
{
for ( int y = 0 ; y < 16 ; y + + )
{
BYTE colorval = ( color > > ( x & 3 ) & 1 ) ? iMonochrome : BLACK ;
2009-02-14 03:53:28 +00:00
#if 0
2009-02-16 20:30:38 +00:00
if ( g_eVideoType = = VT_MONO_AUTHENTIC )
2008-08-27 08:47:52 +00:00
{
if ( y & 1 )
SETSOURCEPIXEL ( SRCOFFS_LORES + x , ( color < < 4 ) + y , BLACK ) ;
else
SETSOURCEPIXEL ( SRCOFFS_LORES + x , ( color < < 4 ) + y , colorval ) ;
}
else
2009-02-14 03:53:28 +00:00
# endif
2008-08-27 08:47:52 +00:00
{
SETSOURCEPIXEL ( SRCOFFS_LORES + x , ( color < < 4 ) + y , colorval ) ;
}
}
}
}
2006-02-25 20:50:29 +00:00
}
2008-08-27 08:47:52 +00:00
// google: CreateDIBPatternBrushPt
// http://209.85.141.104/search?q=cache:mB3htrQGW8kJ:bookfire.net/wince/wince-programming-ms-press2/source/prowice/ch02e.htm
struct BRUSHBMP
{
BITMAPINFOHEADER bmi ;
COLORREF dwPal [ 2 ] ;
BYTE bBits [ 64 ] ;
} ;
2011-01-09 04:42:46 +00:00
HBRUSH V_CreateCustomBrush ( COLORREF nColor )
2008-08-27 08:47:52 +00:00
{
BRUSHBMP brbmp ;
BYTE * pBytes ;
int i ;
//DWORD dwBits[6][2] =
//{
// {0x000000ff,0x00000000}, // HS_HORIZONTAL 0 /* ----- */
// {0x10101010,0x10101010}, // HS_VERTICAL 1 /* ||||| */
// {0x01020408,0x10204080}, // HS_FDIAGONAL 2 /* \\\\\ */
// {0x80402010,0x08040201}, // HS_BDIAGONAL 3 /* ///// */
// {0x101010ff,0x10101010}, // HS_CROSS 4 /* +++++ */
// {0x81422418,0x18244281}, // HS_DIAGCROSS 5 /* xxxxx */
//};
// if ((HatchStyle < 0) || (HatchStyle > 6))
// return 0;
int HatchStyle = 0 ;
DWORD dwBits [ 1 ] [ 2 ] =
{
2011-01-09 04:42:46 +00:00
// {0xff00ff00,0xff00ff00} // every other scan line
{ 0xFFFFFFFF , 0xFFFFFFFF }
2008-08-27 08:47:52 +00:00
} ;
memset ( & brbmp , 0 , sizeof ( brbmp ) ) ;
brbmp . bmi . biSize = sizeof ( BITMAPINFOHEADER ) ;
brbmp . bmi . biWidth = 8 ;
brbmp . bmi . biHeight = 8 ;
brbmp . bmi . biPlanes = 1 ;
brbmp . bmi . biBitCount = 1 ;
brbmp . bmi . biClrUsed = 2 ;
brbmp . bmi . biClrImportant = 2 ;
// Initialize the palette of the bitmap.
brbmp . dwPal [ 0 ] = PALETTERGB ( 0x00 , 0x00 , 0x00 ) ;
brbmp . dwPal [ 1 ] = PALETTERGB (
( BYTE ) ( ( nColor > > 16 ) & 0xff ) ,
( BYTE ) ( ( nColor > > 8 ) & 0xff ) ,
( BYTE ) ( ( nColor > > 0 ) & 0xff ) ) ;
// Write the hatch data to the bitmap.
pBytes = ( BYTE * ) & dwBits [ HatchStyle ] ;
for ( i = 0 ; i < 8 ; i + + )
brbmp . bBits [ i * 4 ] = * pBytes + + ;
// Return the handle of the brush created.
return CreateDIBPatternBrushPt ( & brbmp , DIB_RGB_COLORS ) ;
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
void V_CreateLookup_Text ( HDC dc )
2006-06-29 03:28:25 +00:00
{
HDC memdc = CreateCompatibleDC ( dc ) ;
2009-01-09 21:59:22 +00:00
static HBITMAP hCharBitmap [ 4 ] ;
2008-06-20 23:47:25 +00:00
//The charset is set below
hCharBitmap [ 0 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET40 " ) ) ;
hCharBitmap [ 1 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET82 " ) ) ;
2009-02-14 03:53:28 +00:00
hCharBitmap [ 2 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET8C " ) ) ; // FIXME: Pravets 8M probably has the same charset as Pravets 8C
2009-01-09 21:59:22 +00:00
hCharBitmap [ 3 ] = LoadBitmap ( g_hInstance , TEXT ( " CHARSET8C " ) ) ;
2008-06-20 23:47:25 +00:00
SelectObject ( memdc , hCharBitmap [ g_nCharsetType ] ) ;
2006-06-29 03:28:25 +00:00
BitBlt (
dc // hdcDest
, SRCOFFS_40COL , 0 // nXDest, nYDest
, 256 , 512 // nWidth, nHeight
, memdc // hdcSrc
, 0 , 0 // nXSrc, nYSrc
, SRCCOPY ) ; // dwRop
// Chars for Apple ][
BitBlt ( dc , SRCOFFS_IIPLUS , 0 , 256 , 256 , memdc , 0 , 512 , SRCCOPY ) ;
// Chars for 80 col mode
StretchBlt ( dc , SRCOFFS_80COL , 0 , 128 , 512 , memdc , 0 , 0 , 256 , 512 , SRCCOPY ) ;
DeleteDC ( memdc ) ;
2008-06-20 23:47:25 +00:00
DeleteObject ( hCharBitmap ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2009-02-18 22:24:50 +00:00
void SetLastDrawnImage ( )
{
memcpy ( vidlastmem + 0x400 , g_pTextBank0 , 0x400 ) ;
if ( SW_HIRES )
memcpy ( vidlastmem + 0x2000 , g_pHiresBank0 , 0x2000 ) ;
if ( SW_DHIRES & & SW_HIRES )
memcpy ( vidlastmem , g_pHiresBank1 , 0x2000 ) ;
else if ( SW_80COL ) // Don't test for !SW_HIRES, as some 80-col text routines have SW_HIRES set (Bug #8300)
memcpy ( vidlastmem , g_pTextBank1 , 0x400 ) ;
int loop ;
for ( loop = 0 ; loop < 256 ; loop + + )
{
* ( memdirty + loop ) & = ~ 2 ;
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2012-09-18 19:55:29 +00:00
static inline int GetOriginal2EOffset ( BYTE ch )
{
// No mousetext for original IIe
return ! IsOriginal2E ( ) | | ! g_nAltCharSetOffset | | ( ch < 0x40 ) | | ( ch > 0x5F ) ? 0 : - g_nAltCharSetOffset ;
}
2006-06-29 03:28:25 +00:00
bool Update40ColCell ( int x , int y , int xpixel , int ypixel , int offset )
2006-02-25 20:50:29 +00:00
{
2006-06-29 03:28:25 +00:00
BYTE ch = * ( g_pTextBank0 + offset ) ;
2008-08-25 05:25:27 +00:00
bool bCharChanged = ( ch ! = * ( vidlastmem + offset + 0x400 ) | | g_VideoForceFullRedraw ) ;
2006-02-25 20:50:29 +00:00
// FLASHing chars:
// - FLASHing if:Alt Char Set is OFF && 0x40<=char<=0x7F
// - The inverse of this char is located at: char+0x40
2006-06-29 03:28:25 +00:00
bool bCharFlashing = ( g_nAltCharSetOffset = = 0 ) & & ( ch > = 0x40 ) & & ( ch < = 0x7F ) ;
2006-02-25 20:50:29 +00:00
if ( bCharChanged | | ( bCharFlashing & & g_bTextFlashFlag ) )
{
bool bInvert = bCharFlashing ? g_bTextFlashState : false ;
2006-03-09 21:42:17 +00:00
2006-02-25 20:50:29 +00:00
CopySource ( xpixel , ypixel ,
2006-06-29 03:28:25 +00:00
APPLE_FONT_WIDTH , APPLE_FONT_HEIGHT ,
2007-05-28 11:16:42 +00:00
( IS_APPLE2 ? SRCOFFS_IIPLUS : SRCOFFS_40COL ) + ( ( ch & 0x0F ) < < 4 ) ,
2012-09-18 19:55:29 +00:00
( ch & 0xF0 ) + g_nAltCharSetOffset + GetOriginal2EOffset ( ch ) + ( bInvert ? 0x40 : 0x00 ) ) ;
2006-02-25 20:50:29 +00:00
2006-06-29 03:28:25 +00:00
return true ;
2006-02-25 20:50:29 +00:00
}
2006-06-29 03:28:25 +00:00
return false ;
}
inline bool _Update80ColumnCell ( BYTE c , const int xPixel , const int yPixel , bool bCharFlashing )
{
bool bInvert = bCharFlashing ? g_bTextFlashState : false ;
CopySource (
xPixel , yPixel ,
( APPLE_FONT_WIDTH / 2 ) , APPLE_FONT_HEIGHT ,
2012-09-18 19:55:29 +00:00
SRCOFFS_80COL + ( ( c & 0x0F ) < < 3 ) ,
( c & 0xF0 ) + g_nAltCharSetOffset + GetOriginal2EOffset ( c ) + ( bInvert ? 0x40 : 0x00 ) ) ;
2006-06-29 03:28:25 +00:00
return true ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2006-06-29 03:28:25 +00:00
bool Update80ColCell ( int x , int y , int xpixel , int ypixel , int offset )
{
bool bDirty = false ;
# if FLASH_80_COL
BYTE c1 = * ( g_pTextBank1 + offset ) ; // aux
BYTE c0 = * ( g_pTextBank0 + offset ) ; // main
2008-08-25 05:25:27 +00:00
bool bC1Changed = ( c1 ! = * ( vidlastmem + offset + 0 ) | | g_VideoForceFullRedraw ) ;
bool bC0Changed = ( c0 ! = * ( vidlastmem + offset + 0x400 ) | | g_VideoForceFullRedraw ) ;
2006-06-29 03:28:25 +00:00
bool bC1Flashing = ( g_nAltCharSetOffset = = 0 ) & & ( c1 > = 0x40 ) & & ( c1 < = 0x7F ) ;
bool bC0Flashing = ( g_nAltCharSetOffset = = 0 ) & & ( c0 > = 0x40 ) & & ( c0 < = 0x7F ) ;
if ( bC1Changed | | ( bC1Flashing & & g_bTextFlashFlag ) )
bDirty = _Update80ColumnCell ( c1 , xpixel , ypixel , bC1Flashing ) ;
if ( bC0Changed | | ( bC0Flashing & & g_bTextFlashFlag ) )
bDirty | = _Update80ColumnCell ( c0 , xpixel + 7 , ypixel , bC0Flashing ) ;
# else
BYTE auxval = * ( g_pTextBank1 + offset ) ; // aux
BYTE mainval = * ( g_pTextBank0 + offset ) ; // main
if ( ( auxval ! = * ( vidlastmem + offset ) ) | |
( mainval ! = * ( vidlastmem + offset + 0x400 ) ) | |
2008-08-25 05:25:27 +00:00
g_VideoForceFullRedraw )
2006-06-29 03:28:25 +00:00
{
CopySource ( xpixel , ypixel ,
( APPLE_FONT_WIDTH / 2 ) , APPLE_FONT_HEIGHT ,
SRCOFFS_80COL + ( ( auxval & 15 ) < < 3 ) ,
( ( auxval > > 4 ) < < 4 ) + g_nAltCharSetOffset ) ;
CopySource ( xpixel + 7 , ypixel ,
( APPLE_FONT_WIDTH / 2 ) , APPLE_FONT_HEIGHT ,
SRCOFFS_80COL + ( ( mainval & 15 ) < < 3 ) ,
( ( mainval > > 4 ) < < 4 ) + g_nAltCharSetOffset ) ;
bDirty = true ;
}
# endif
return bDirty ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2006-06-29 03:28:25 +00:00
bool UpdateDHiResCell ( int x , int y , int xpixel , int ypixel , int offset )
{
bool bDirty = false ;
int yoffset = 0 ;
2006-02-25 20:50:29 +00:00
while ( yoffset < 0x2000 ) {
2006-06-29 03:28:25 +00:00
BYTE byteval1 = ( x > 0 ) ? * ( g_pHiresBank0 + offset + yoffset - 1 ) : 0 ;
BYTE byteval2 = * ( g_pHiresBank1 + offset + yoffset ) ;
BYTE byteval3 = * ( g_pHiresBank0 + offset + yoffset ) ;
BYTE byteval4 = ( x < 39 ) ? * ( g_pHiresBank1 + offset + yoffset + 1 ) : 0 ;
2006-02-25 20:50:29 +00:00
if ( ( byteval2 ! = * ( vidlastmem + offset + yoffset ) ) | |
( byteval3 ! = * ( vidlastmem + offset + yoffset + 0x2000 ) ) | |
( ( x > 0 ) & & ( ( byteval1 & 0x70 ) ! = ( * ( vidlastmem + offset + yoffset + 0x1FFF ) & 0x70 ) ) ) | |
( ( x < 39 ) & & ( ( byteval4 & 0x07 ) ! = ( * ( vidlastmem + offset + yoffset + 1 ) & 0x07 ) ) ) | |
2008-08-25 05:25:27 +00:00
g_VideoForceFullRedraw ) {
2006-02-25 20:50:29 +00:00
DWORD dwordval = ( byteval1 & 0x70 ) | ( ( byteval2 & 0x7F ) < < 7 ) |
( ( byteval3 & 0x7F ) < < 14 ) | ( ( byteval4 & 0x07 ) < < 21 ) ;
# define PIXEL 0
# define COLOR ((xpixel + PIXEL) & 3)
# define VALUE (dwordval >> (4 + PIXEL - COLOR))
CopySource ( xpixel + PIXEL , ypixel + ( yoffset > > 9 ) , 7 , 2 ,
SRCOFFS_DHIRES + 10 * HIBYTE ( VALUE ) + COLOR , LOBYTE ( VALUE ) < < 1 ) ;
# undef PIXEL
# define PIXEL 7
CopySource ( xpixel + PIXEL , ypixel + ( yoffset > > 9 ) , 7 , 2 ,
SRCOFFS_DHIRES + 10 * HIBYTE ( VALUE ) + COLOR , LOBYTE ( VALUE ) < < 1 ) ;
# undef PIXEL
# undef COLOR
# undef VALUE
2006-06-29 03:28:25 +00:00
bDirty = true ;
2006-02-25 20:50:29 +00:00
}
yoffset + = 0x400 ;
}
2006-06-29 03:28:25 +00:00
return bDirty ;
2006-02-25 20:50:29 +00:00
}
2011-02-19 22:14:08 +00:00
/*
Color Reference Tests :
2000 : D5 AA D5 AA D5 AA // blue blue blue
2400 : AA D5 2 A 55 55 2 A //+ red green magenta
// //= grey aqua purple
2 C00 : AA D5 AA D5 2 A 55 // red red green
3000 : 2 A 55 55 2 A 55 2 A //+ green magenta magenta
// //= yellow pink grey
*/
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-02-19 22:14:08 +00:00
BYTE MixColors ( BYTE c1 , BYTE c2 )
{
// For tv emulation HGR Video Mode
# define COMBINATION(c1,c2,ref1,ref2) (((c1)==(ref1)&&(c2)==(ref2)) || ((c1)==(ref2)&&(c2)==(ref1)))
if ( c1 = = c2 )
return c1 ;
if ( COMBINATION ( c1 , c2 , HGR_BLUE , HGR_RED ) )
return HGR_GREY1 ;
else if ( COMBINATION ( c1 , c2 , HGR_GREEN , HGR_MAGENTA ) )
return HGR_GREY2 ;
else if ( COMBINATION ( c1 , c2 , HGR_RED , HGR_GREEN ) )
return HGR_YELLOW ;
else if ( COMBINATION ( c1 , c2 , HGR_BLUE , HGR_GREEN ) )
return HGR_AQUA ;
else if ( COMBINATION ( c1 , c2 , HGR_BLUE , HGR_MAGENTA ) )
return HGR_PURPLE ;
else if ( COMBINATION ( c1 , c2 , HGR_RED , HGR_MAGENTA ) )
return HGR_PINK ;
else
return MONOCHROME_CUSTOM ; // visible failure indicator
2006-02-25 20:50:29 +00:00
# undef COMBINATION
}
//===========================================================================
2011-02-19 22:14:08 +00:00
void CreateColorMixMap ( )
{
// For tv emulation HGR Video Mode
# define FROM_NEIGHBOUR 0x00
2006-02-25 20:50:29 +00:00
int t , m , b ;
BYTE cTop , cMid , cBot ;
WORD mixTop , mixBot ;
2011-02-19 22:14:08 +00:00
# define MIX_THRESHOLD 0x12 // bottom 2 HGR colors
2006-02-25 20:50:29 +00:00
for ( t = 0 ; t < 6 ; t + + )
for ( m = 0 ; m < 6 ; m + + )
for ( b = 0 ; b < 6 ; b + + ) {
cTop = t | 0x10 ;
cMid = m | 0x10 ;
cBot = b | 0x10 ;
2011-02-19 22:14:08 +00:00
if ( cMid < MIX_THRESHOLD ) {
2006-02-25 20:50:29 +00:00
mixTop = mixBot = cMid ;
} else {
2011-02-19 22:14:08 +00:00
if ( cTop < MIX_THRESHOLD ) {
2006-02-25 20:50:29 +00:00
mixTop = FROM_NEIGHBOUR ;
} else {
mixTop = MixColors ( cMid , cTop ) ;
}
2011-02-19 22:14:08 +00:00
if ( cBot < MIX_THRESHOLD ) {
2006-02-25 20:50:29 +00:00
mixBot = FROM_NEIGHBOUR ;
} else {
mixBot = MixColors ( cMid , cBot ) ;
}
if ( mixTop = = FROM_NEIGHBOUR & & mixBot ! = FROM_NEIGHBOUR ) {
mixTop = mixBot ;
} else if ( mixBot = = FROM_NEIGHBOUR & & mixTop ! = FROM_NEIGHBOUR ) {
mixBot = mixTop ;
} else if ( mixBot = = FROM_NEIGHBOUR & & mixTop = = FROM_NEIGHBOUR ) {
mixBot = mixTop = cMid ;
}
}
colormixmap [ t ] [ m ] [ b ] = ( mixTop < < 8 ) | mixBot ;
}
# undef FROM_NEIGHBOUR
}
//===========================================================================
2011-02-19 22:14:08 +00:00
void __stdcall MixColorsVertical ( int matx , int maty )
{
// For tv emulation HGR Video Mode
2006-02-25 20:50:29 +00:00
WORD twoHalfPixel ;
int bot1idx , bot2idx ;
if ( SW_MIXED & & maty > 159 ) {
if ( maty < 161 ) {
bot1idx = hgrpixelmatrix [ matx ] [ maty + 1 ] & 0x0F ;
bot2idx = 0 ;
} else {
bot1idx = bot2idx = 0 ;
}
} else {
bot1idx = hgrpixelmatrix [ matx ] [ maty + 1 ] & 0x0F ;
bot2idx = hgrpixelmatrix [ matx ] [ maty + 2 ] & 0x0F ;
}
twoHalfPixel = colormixmap [ hgrpixelmatrix [ matx ] [ maty - 2 ] & 0x0F ]
[ hgrpixelmatrix [ matx ] [ maty - 1 ] & 0x0F ]
[ hgrpixelmatrix [ matx ] [ maty ] & 0x0F ] ;
colormixbuffer [ 0 ] = ( twoHalfPixel & 0xFF00 ) > > 8 ;
colormixbuffer [ 1 ] = twoHalfPixel & 0x00FF ;
twoHalfPixel = colormixmap [ hgrpixelmatrix [ matx ] [ maty - 1 ] & 0x0F ]
[ hgrpixelmatrix [ matx ] [ maty ] & 0x0F ]
[ bot1idx ] ;
colormixbuffer [ 2 ] = ( twoHalfPixel & 0xFF00 ) > > 8 ;
colormixbuffer [ 3 ] = twoHalfPixel & 0x00FF ;
twoHalfPixel = colormixmap [ hgrpixelmatrix [ matx ] [ maty ] & 0x0F ]
[ bot1idx ]
[ bot2idx ] ;
colormixbuffer [ 4 ] = ( twoHalfPixel & 0xFF00 ) > > 8 ;
colormixbuffer [ 5 ] = twoHalfPixel & 0x00FF ;
}
//===========================================================================
2014-06-26 22:44:02 +01:00
static inline void __stdcall CopyMixedSource8 ( int x , int y , int sourcex , int sourcey )
2011-02-19 22:14:08 +00:00
{
// For tv emulation HGR Video Mode
2006-02-25 20:50:29 +00:00
2014-06-26 22:44:02 +01:00
const BYTE * const currsourceptr = g_aSourceStartofLine [ sourcey ] + sourcex ;
BYTE * const currdestptr = g_aFrameBufferOffset [ y * 2 ] + ( x * 2 ) ;
const int matx = x ;
const int maty = HGR_MATRIX_YOFFSET + y ;
const int hgrlinesabove = ( y > 0 ) ? 1 : 0 ;
const int hgrlinesbelow = SW_MIXED ? ( ( y < 159 ) ? 1 : 0 ) : ( ( y < 191 ) ? 1 : 0 ) ;
const int istart = 2 - ( hgrlinesabove * 2 ) ;
const int iend = 3 + ( hgrlinesbelow * 2 ) ;
// transfer 7 pixels (i.e. the visible part of an apple hgr-byte) from row to pixelmatrix
for ( int count = 0 , bufxoffset = 0 ; count < 7 ; count + + , bufxoffset + = 2 )
{
hgrpixelmatrix [ matx + count ] [ maty ] = * ( currsourceptr + bufxoffset ) ;
// color mixing between adjacent scanlines at current x position
MixColorsVertical ( matx + count , maty ) ;
// transfer up to 6 mixed (half-)pixels of current column to framebuffer
BYTE * currptr = currdestptr + bufxoffset ;
if ( hgrlinesabove )
currptr + = g_nFrameBufferPitch * 2 ;
for ( int i = istart ; i < = iend ; currptr - = g_nFrameBufferPitch , i + + )
{
* currptr = * ( currptr + 1 ) = colormixbuffer [ i ] ;
}
2006-02-25 20:50:29 +00:00
}
}
2014-06-26 22:44:02 +01:00
// For tv emulation HGR Video Mode
static void __stdcall CopyMixedSource ( int x , int y , int sourcex , int sourcey )
{
if ( ! g_bIsFullScreen | | ! GetFullScreen32Bit ( ) )
{
CopyMixedSource8 ( x , y , sourcex , sourcey ) ;
return ;
}
const BYTE * const currsourceptr = g_aSourceStartofLine [ sourcey ] + sourcex ;
UINT32 * const currdestptr = ( UINT32 * ) ( g_aFrameBufferOffset [ y * 2 ] + ( x * 2 ) * sizeof ( UINT32 ) ) ;
const int matx = x ;
const int maty = HGR_MATRIX_YOFFSET + y ;
const int hgrlinesabove = ( y > 0 ) ? 1 : 0 ;
const int hgrlinesbelow = SW_MIXED ? ( ( y < 159 ) ? 1 : 0 ) : ( ( y < 191 ) ? 1 : 0 ) ;
const int istart = 2 - ( hgrlinesabove * 2 ) ;
const int iend = 3 + ( hgrlinesbelow * 2 ) ;
// transfer 7 pixels (i.e. the visible part of an apple hgr-byte) from row to pixelmatrix
for ( int count = 0 , bufxoffset = 0 ; count < 7 ; count + + , bufxoffset + = 2 )
{
hgrpixelmatrix [ matx + count ] [ maty ] = * ( currsourceptr + bufxoffset ) ;
// color mixing between adjacent scanlines at current x position
MixColorsVertical ( matx + count , maty ) ;
// transfer up to 6 mixed (half-)pixels of current column to framebuffer
UINT32 * currptr = currdestptr + bufxoffset ;
if ( hgrlinesabove )
currptr + = ( g_nFrameBufferPitch / sizeof ( UINT32 ) ) * 2 ;
for ( int i = istart ; i < = iend ; currptr - = g_nFrameBufferPitch / sizeof ( UINT32 ) , i + + )
{
RGBQUAD & rRGB = g_pFramebufferinfo - > bmiColors [ colormixbuffer [ i ] ] ;
const UINT32 rgb = ( ( ( UINT32 ) rRGB . rgbRed ) < < 16 ) | ( ( ( UINT32 ) rRGB . rgbGreen ) < < 8 ) | ( ( UINT32 ) rRGB . rgbBlue ) ;
* currptr = * ( currptr + 1 ) = rgb ;
}
}
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2006-06-29 03:28:25 +00:00
bool UpdateHiResCell ( int x , int y , int xpixel , int ypixel , int offset )
{
bool bDirty = false ;
int yoffset = 0 ;
2011-01-06 17:10:23 +00:00
while ( yoffset < 0x2000 )
{
#if 0 // TRACE_VIDEO
static char sText [ 256 ] ;
sprintf ( sText , " x: %3d y: %3d xpix: %3d ypix: %3d offset: %04X \n "
, x , y , xpixel , ypixel , offset , yoffset
) ;
OutputDebugString ( " sText " ) ;
# endif
2011-01-06 17:47:17 +00:00
2011-01-06 17:10:23 +00:00
BYTE byteval1 = ( x > 0 ) ? * ( g_pHiresBank0 + offset + yoffset - 1 ) : 0 ;
2011-01-06 17:47:17 +00:00
BYTE byteval2 = * ( g_pHiresBank0 + offset + yoffset ) ;
2011-01-06 17:10:23 +00:00
BYTE byteval3 = ( x < 39 ) ? * ( g_pHiresBank0 + offset + yoffset + 1 ) : 0 ;
if ( ( byteval2 ! = * ( vidlastmem + offset + yoffset + 0x2000 ) ) | |
( ( x > 0 ) & & ( ( byteval1 & 0x60 ) ! = ( * ( vidlastmem + offset + yoffset + 0x1FFF ) & 0x60 ) ) ) | |
( ( x < 39 ) & & ( ( byteval3 & 0x03 ) ! = ( * ( vidlastmem + offset + yoffset + 0x2001 ) & 0x03 ) ) ) | |
g_VideoForceFullRedraw )
{
2006-02-25 20:50:29 +00:00
# define COLOFFS (((byteval1 & 0x60) << 2) | \
2011-01-06 17:47:17 +00:00
( ( byteval3 & 0x03 ) < < 5 ) )
2011-01-06 17:10:23 +00:00
if ( g_eVideoType = = VT_COLOR_TVEMU )
{
CopyMixedSource (
xpixel > > 1 , ( ypixel + ( yoffset > > 9 ) ) > > 1 ,
SRCOFFS_HIRES + COLOFFS + ( ( x & 1 ) < < 4 ) , ( ( ( int ) byteval2 ) < < 1 )
) ;
}
else
{
CopySource (
2011-01-06 17:47:17 +00:00
xpixel , ypixel + ( yoffset > > 9 ) ,
14 , 2 , // 2x upscale: 280x192 -> 560x384
2011-01-06 17:10:23 +00:00
SRCOFFS_HIRES + COLOFFS + ( ( x & 1 ) < < 4 ) , ( ( ( int ) byteval2 ) < < 1 )
) ;
}
2006-02-25 20:50:29 +00:00
# undef COLOFFS
2011-01-06 17:10:23 +00:00
bDirty = true ;
}
yoffset + = 0x400 ;
}
2006-06-29 03:28:25 +00:00
return bDirty ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2006-06-29 03:28:25 +00:00
bool UpdateLoResCell ( int x , int y , int xpixel , int ypixel , int offset )
{
BYTE val = * ( g_pTextBank0 + offset ) ;
2008-08-25 05:25:27 +00:00
if ( ( val ! = * ( vidlastmem + offset + 0x400 ) ) | | g_VideoForceFullRedraw )
2006-06-29 03:28:25 +00:00
{
CopySource ( xpixel , ypixel ,
14 , 8 ,
SRCOFFS_LORES + ( ( x & 1 ) < < 1 ) , ( ( val & 0xF ) < < 4 ) ) ;
CopySource ( xpixel , ypixel + 8 ,
14 , 8 ,
SRCOFFS_LORES + ( ( x & 1 ) < < 1 ) , ( val & 0xF0 ) ) ;
return true ;
}
return false ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2009-07-23 21:43:08 +00:00
# define ROL_NIB(x) ( (((x)<<1)&0xF) | (((x)>>3)&1) )
2006-06-29 03:28:25 +00:00
bool UpdateDLoResCell ( int x , int y , int xpixel , int ypixel , int offset )
2006-02-25 20:50:29 +00:00
{
2009-07-23 21:43:08 +00:00
BYTE auxval = * ( g_pTextBank1 + offset ) ;
BYTE mainval = * ( g_pTextBank0 + offset ) ;
2006-02-25 20:50:29 +00:00
if ( ( auxval ! = * ( vidlastmem + offset ) ) | |
( mainval ! = * ( vidlastmem + offset + 0x400 ) ) | |
2008-08-25 05:25:27 +00:00
g_VideoForceFullRedraw
2006-02-25 20:50:29 +00:00
)
{
2009-07-23 21:43:08 +00:00
const BYTE auxval_h = auxval > > 4 ;
const BYTE auxval_l = auxval & 0xF ;
auxval = ( ROL_NIB ( auxval_h ) < < 4 ) | ROL_NIB ( auxval_l ) ; // Fix Bug #14879
2008-08-27 08:47:52 +00:00
CopySource ( xpixel , ypixel , 7 , 8 , SRCOFFS_LORES + ( ( x & 1 ) < < 1 ) , ( ( auxval & 0xF ) < < 4 ) ) ;
CopySource ( xpixel , ypixel + 8 , 7 , 8 , SRCOFFS_LORES + ( ( x & 1 ) < < 1 ) , ( auxval & 0xF0 ) ) ;
2006-02-25 20:50:29 +00:00
//
2008-08-27 08:47:52 +00:00
CopySource ( xpixel + 7 , ypixel , 7 , 8 , SRCOFFS_LORES + ( ( x & 1 ) < < 1 ) , ( ( mainval & 0xF ) < < 4 ) ) ;
CopySource ( xpixel + 7 , ypixel + 8 , 7 , 8 , SRCOFFS_LORES + ( ( x & 1 ) < < 1 ) , ( mainval & 0xF0 ) ) ;
2006-06-29 03:28:25 +00:00
return true ;
2006-02-25 20:50:29 +00:00
}
2006-06-29 03:28:25 +00:00
return false ;
2006-02-25 20:50:29 +00:00
}
//
// ----- ALL GLOBALLY ACCESSIBLE FUNCTIONS ARE BELOW THIS LINE -----
//
//===========================================================================
BOOL VideoApparentlyDirty ( )
{
2008-08-25 05:25:27 +00:00
if ( SW_MIXED | | g_VideoForceFullRedraw )
2006-02-25 20:50:29 +00:00
return 1 ;
2008-08-31 04:31:35 +00:00
DWORD address = ( SW_HIRES & & ! SW_TEXT )
? ( 0x20 < < ( int ) g_bVideoDisplayPage2 )
: ( 0x04 < < ( int ) g_bVideoDisplayPage2 ) ;
2006-02-25 20:50:29 +00:00
DWORD length = ( SW_HIRES & & ! SW_TEXT ) ? 0x20 : 0x4 ;
while ( length - - )
if ( * ( memdirty + ( address + + ) ) & 2 )
return 1 ;
//
bool bCharFlashing = false ;
// Scan visible text page for any flashing chars
2006-06-29 03:28:25 +00:00
if ( ( SW_TEXT | | SW_MIXED ) & & ( g_nAltCharSetOffset = = 0 ) )
2006-02-25 20:50:29 +00:00
{
2008-08-31 04:31:35 +00:00
BYTE * pnMemText = MemGetMainPtr ( 0x400 < < ( int ) g_bVideoDisplayPage2 ) ;
2006-02-25 20:50:29 +00:00
// Scan 8 long-lines of 120 chars (at 128 char offsets):
// . Skip 8-char holes in TEXT
for ( UINT y = 0 ; y < 8 ; y + + )
{
for ( UINT x = 0 ; x < 40 * 3 ; x + + )
{
BYTE ch = pnMemText [ y * 128 + x ] ;
if ( ( ch > = 0x40 ) & & ( ch < = 0x7F ) )
{
bCharFlashing = true ;
break ;
}
}
}
}
if ( bCharFlashing )
return 1 ;
return 0 ;
}
//===========================================================================
void VideoBenchmark ( ) {
Sleep ( 500 ) ;
// PREPARE TWO DIFFERENT FRAME BUFFERS, EACH OF WHICH HAVE HALF OF THE
// BYTES SET TO 0x14 AND THE OTHER HALF SET TO 0xAA
int loop ;
LPDWORD mem32 = ( LPDWORD ) mem ;
for ( loop = 4096 ; loop < 6144 ; loop + + )
* ( mem32 + loop ) = ( ( loop & 1 ) ^ ( ( loop & 0x40 ) > > 6 ) ) ? 0x14141414
: 0xAAAAAAAA ;
for ( loop = 6144 ; loop < 8192 ; loop + + )
* ( mem32 + loop ) = ( ( loop & 1 ) ^ ( ( loop & 0x40 ) > > 6 ) ) ? 0xAAAAAAAA
: 0x14141414 ;
// SEE HOW MANY TEXT FRAMES PER SECOND WE CAN PRODUCE WITH NOTHING ELSE
// GOING ON, CHANGING HALF OF THE BYTES IN THE VIDEO BUFFER EACH FRAME TO
// SIMULATE THE ACTIVITY OF AN AVERAGE GAME
DWORD totaltextfps = 0 ;
2011-01-04 17:08:01 +00:00
g_bVideoMode = VF_TEXT ;
2006-02-25 20:50:29 +00:00
FillMemory ( mem + 0x400 , 0x400 , 0x14 ) ;
VideoRedrawScreen ( ) ;
DWORD milliseconds = GetTickCount ( ) ;
while ( GetTickCount ( ) = = milliseconds ) ;
milliseconds = GetTickCount ( ) ;
DWORD cycle = 0 ;
do {
if ( cycle & 1 )
FillMemory ( mem + 0x400 , 0x400 , 0x14 ) ;
else
CopyMemory ( mem + 0x400 , mem + ( ( cycle & 2 ) ? 0x4000 : 0x6000 ) , 0x400 ) ;
VideoRefreshScreen ( ) ;
if ( cycle + + > = 3 )
cycle = 0 ;
totaltextfps + + ;
} while ( GetTickCount ( ) - milliseconds < 1000 ) ;
// SEE HOW MANY HIRES FRAMES PER SECOND WE CAN PRODUCE WITH NOTHING ELSE
// GOING ON, CHANGING HALF OF THE BYTES IN THE VIDEO BUFFER EACH FRAME TO
// SIMULATE THE ACTIVITY OF AN AVERAGE GAME
DWORD totalhiresfps = 0 ;
2011-01-04 17:08:01 +00:00
g_bVideoMode = VF_HIRES ;
2006-02-25 20:50:29 +00:00
FillMemory ( mem + 0x2000 , 0x2000 , 0x14 ) ;
VideoRedrawScreen ( ) ;
milliseconds = GetTickCount ( ) ;
while ( GetTickCount ( ) = = milliseconds ) ;
milliseconds = GetTickCount ( ) ;
cycle = 0 ;
do {
if ( cycle & 1 )
FillMemory ( mem + 0x2000 , 0x2000 , 0x14 ) ;
else
CopyMemory ( mem + 0x2000 , mem + ( ( cycle & 2 ) ? 0x4000 : 0x6000 ) , 0x2000 ) ;
VideoRefreshScreen ( ) ;
if ( cycle + + > = 3 )
cycle = 0 ;
totalhiresfps + + ;
} while ( GetTickCount ( ) - milliseconds < 1000 ) ;
// DETERMINE HOW MANY 65C02 CLOCK CYCLES WE CAN EMULATE PER SECOND WITH
// NOTHING ELSE GOING ON
CpuSetupBenchmark ( ) ;
DWORD totalmhz10 = 0 ;
milliseconds = GetTickCount ( ) ;
while ( GetTickCount ( ) = = milliseconds ) ;
milliseconds = GetTickCount ( ) ;
cycle = 0 ;
do {
CpuExecute ( 100000 ) ;
totalmhz10 + + ;
} while ( GetTickCount ( ) - milliseconds < 1000 ) ;
// IF THE PROGRAM COUNTER IS NOT IN THE EXPECTED RANGE AT THE END OF THE
// CPU BENCHMARK, REPORT AN ERROR AND OPTIONALLY TRACK IT DOWN
if ( ( regs . pc < 0x300 ) | | ( regs . pc > 0x400 ) )
2006-05-14 00:44:38 +00:00
if ( MessageBox ( g_hFrameWindow ,
2006-02-25 20:50:29 +00:00
TEXT ( " The emulator has detected a problem while running " )
TEXT ( " the CPU benchmark. Would you like to gather more " )
TEXT ( " information? " ) ,
TEXT ( " Benchmarks " ) ,
MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND ) = = IDYES ) {
BOOL error = 0 ;
WORD lastpc = 0x300 ;
int loop = 0 ;
while ( ( loop < 10000 ) & & ! error ) {
CpuSetupBenchmark ( ) ;
CpuExecute ( loop ) ;
if ( ( regs . pc < 0x300 ) | | ( regs . pc > 0x400 ) )
error = 1 ;
else {
lastpc = regs . pc ;
+ + loop ;
}
}
if ( error ) {
TCHAR outstr [ 256 ] ;
wsprintf ( outstr ,
TEXT ( " The emulator experienced an error %u clock cycles " )
TEXT ( " into the CPU benchmark. Prior to the error, the " )
TEXT ( " program counter was at $%04X. After the error, it " )
TEXT ( " had jumped to $%04X. " ) ,
( unsigned ) loop ,
( unsigned ) lastpc ,
( unsigned ) regs . pc ) ;
2006-05-14 00:44:38 +00:00
MessageBox ( g_hFrameWindow ,
2006-02-25 20:50:29 +00:00
outstr ,
TEXT ( " Benchmarks " ) ,
MB_ICONINFORMATION | MB_SETFOREGROUND ) ;
}
else
2006-05-14 00:44:38 +00:00
MessageBox ( g_hFrameWindow ,
2006-02-25 20:50:29 +00:00
TEXT ( " The emulator was unable to locate the exact " )
TEXT ( " point of the error. This probably means that " )
TEXT ( " the problem is external to the emulator, " )
TEXT ( " happening asynchronously, such as a problem in " )
TEXT ( " a timer interrupt handler. " ) ,
TEXT ( " Benchmarks " ) ,
MB_ICONINFORMATION | MB_SETFOREGROUND ) ;
}
// DO A REALISTIC TEST OF HOW MANY FRAMES PER SECOND WE CAN PRODUCE
// WITH FULL EMULATION OF THE CPU, JOYSTICK, AND DISK HAPPENING AT
// THE SAME TIME
DWORD realisticfps = 0 ;
FillMemory ( mem + 0x2000 , 0x2000 , 0xAA ) ;
VideoRedrawScreen ( ) ;
milliseconds = GetTickCount ( ) ;
while ( GetTickCount ( ) = = milliseconds ) ;
milliseconds = GetTickCount ( ) ;
cycle = 0 ;
do {
if ( realisticfps < 10 ) {
int cycles = 100000 ;
while ( cycles > 0 ) {
DWORD executedcycles = CpuExecute ( 103 ) ;
cycles - = executedcycles ;
DiskUpdatePosition ( executedcycles ) ;
JoyUpdatePosition ( ) ;
2006-03-12 09:05:39 +00:00
}
2006-02-25 20:50:29 +00:00
}
if ( cycle & 1 )
FillMemory ( mem + 0x2000 , 0x2000 , 0xAA ) ;
else
CopyMemory ( mem + 0x2000 , mem + ( ( cycle & 2 ) ? 0x4000 : 0x6000 ) , 0x2000 ) ;
VideoRefreshScreen ( ) ;
if ( cycle + + > = 3 )
cycle = 0 ;
realisticfps + + ;
} while ( GetTickCount ( ) - milliseconds < 1000 ) ;
// DISPLAY THE RESULTS
VideoDisplayLogo ( ) ;
TCHAR outstr [ 256 ] ;
wsprintf ( outstr ,
TEXT ( " Pure Video FPS: \t %u hires, %u text \n " )
TEXT ( " Pure CPU MHz: \t %u.%u%s \n \n " )
TEXT ( " EXPECTED AVERAGE VIDEO GAME \n " )
TEXT ( " PERFORMANCE: %u FPS " ) ,
( unsigned ) totalhiresfps ,
( unsigned ) totaltextfps ,
( unsigned ) ( totalmhz10 / 10 ) ,
( unsigned ) ( totalmhz10 % 10 ) ,
2007-05-28 11:16:42 +00:00
( LPCTSTR ) ( IS_APPLE2 ? TEXT ( " (6502) " ) : TEXT ( " " ) ) ,
2006-02-25 20:50:29 +00:00
( unsigned ) realisticfps ) ;
2006-05-14 00:44:38 +00:00
MessageBox ( g_hFrameWindow ,
2006-02-25 20:50:29 +00:00
outstr ,
TEXT ( " Benchmarks " ) ,
MB_ICONINFORMATION | MB_SETFOREGROUND ) ;
}
//===========================================================================
2011-01-09 04:42:46 +00:00
BYTE VideoCheckMode ( WORD , WORD address , BYTE , BYTE , ULONG uExecutedCycles )
2007-05-28 11:16:42 +00:00
{
address & = 0xFF ;
2006-02-25 20:50:29 +00:00
if ( address = = 0x7F )
2010-08-17 07:52:23 +00:00
return MemReadFloatingBus ( SW_DHIRES ! = 0 , uExecutedCycles ) ;
2006-02-25 20:50:29 +00:00
else {
BOOL result = 0 ;
switch ( address ) {
case 0x1A : result = SW_TEXT ; break ;
case 0x1B : result = SW_MIXED ; break ;
case 0x1D : result = SW_HIRES ; break ;
2006-06-29 03:28:25 +00:00
case 0x1E : result = g_nAltCharSetOffset ; break ;
2006-02-25 20:50:29 +00:00
case 0x1F : result = SW_80COL ; break ;
case 0x7F : result = SW_DHIRES ; break ;
}
return KeybGetKeycode ( ) | ( result ? 0x80 : 0 ) ;
}
}
//===========================================================================
2014-07-08 20:58:48 +01:00
// Check if we should call VideoRefreshScreen() based on unexpected page
2014-07-10 22:52:26 +01:00
// - Only called from 2 places in main ContinueExecution() loop
2014-07-08 20:58:48 +01:00
void VideoCheckPage ( BOOL force )
{
const bool bUnexpectedPage = ( g_bVideoDisplayPage2 ! = ( SW_PAGE2 ! = 0 ) ) ;
//_ASSERT(!bUnexpectedPage); // [TC] Q: When does this happen? A: EG. When page-flipping && Scroll-Lock is pressed
if ( bUnexpectedPage & & // Unexpected page &&
( force | | ( emulmsec - lastpageflip > 500 ) ) ) // force || >500ms since last flip
{
g_bVideoDisplayPage2 = ( SW_PAGE2 ! = 0 ) ;
VideoRefreshScreen ( ) ;
hasrefreshed = 1 ;
lastpageflip = emulmsec ;
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-08-17 07:52:23 +00:00
/*
// Drol expects = 80
68 DE A5 02 LDX # 02
68E0 AD 50 C0 LDA TXTCLR
68E3 C9 80 CMP # 80
68E5 D0 F7 BNE $ 68 DE
6957 A5 02 LDX # 02
6959 AD 50 C0 LDA TXTCLR
695 C C9 80 CMP # 80
695 E D0 F7 BNE $ 68 DE
69 D3 A5 02 LDX # 02
69 D5 AD 50 C0 LDA TXTCLR
69 D8 C9 80 CMP # 80
69 DA D0 F7 BNE $ 68 DE
// Karateka expects < 80
07 DE AD 19 C0 LDA RDVBLBAR
07E1 30 FB BMI $ 7 DE
77 A1 AD 19 C0 LDA RDVBLBAR
77 A4 30 FB BMI $ 7 DE
// Gumball expects non-zero low-nibble on VBL
BBB5 A5 60 LDA $ 60
BBB7 4 D 50 C0 EOR TXTCLR
BBBA 85 60 STA $ 60
BBBC 29 0F AND # $ 0F
BBBE F0 F5 BEQ $ BBB5
BBC0 C9 0F CMP # $ 0F
BBC2 F0 F1 BEQ $ BBB5
// Diversi-Dial (DD4.DSK or DIAL.DSK)
F822 LDA RDVBLBAR
F825 EOR # $ 3 C
BMI $ F82A
RTS
F82A LDA $ F825 + 1
EOR # $ 80
STA $ F825 + 1
BMI $ F86A
. . .
F86A RTS
*/
2011-01-09 04:42:46 +00:00
BYTE VideoCheckVbl ( WORD , WORD , BYTE , BYTE , ULONG uExecutedCycles )
2006-02-25 20:50:29 +00:00
{
2010-08-17 07:52:23 +00:00
bool bVblBar = false ;
VideoGetScannerAddress ( & bVblBar , uExecutedCycles ) ;
2006-03-12 09:05:39 +00:00
2010-08-17 07:52:23 +00:00
BYTE r = KeybGetKeycode ( ) ;
return ( r & ~ 0x80 ) | ( ( bVblBar ) ? 0x80 : 0 ) ;
2006-03-01 00:23:42 +00:00
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-01-09 04:42:46 +00:00
void VideoChooseColor ( )
{
CHOOSECOLOR cc ;
ZeroMemory ( & cc , sizeof ( CHOOSECOLOR ) ) ;
cc . lStructSize = sizeof ( CHOOSECOLOR ) ;
cc . hwndOwner = g_hFrameWindow ;
cc . rgbResult = monochrome ;
cc . lpCustColors = customcolors + 1 ;
cc . Flags = CC_RGBINIT | CC_SOLIDCOLOR ;
if ( ChooseColor ( & cc ) )
{
monochrome = cc . rgbResult ;
VideoReinitialize ( ) ;
if ( ( g_nAppMode ! = MODE_LOGO ) & & ( g_nAppMode ! = MODE_DEBUG ) )
{
VideoRedrawScreen ( ) ;
}
Config_Save_Video ( ) ;
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
void VideoDestroy ( ) {
// DESTROY BUFFERS
2008-08-25 00:36:48 +00:00
VirtualFree ( g_pFramebufferinfo , 0 , MEM_RELEASE ) ;
2006-06-29 03:28:25 +00:00
VirtualFree ( g_pSourceHeader , 0 , MEM_RELEASE ) ;
2006-02-25 20:50:29 +00:00
VirtualFree ( vidlastmem , 0 , MEM_RELEASE ) ;
2008-08-25 00:36:48 +00:00
g_pFramebufferinfo = NULL ;
2006-06-29 03:28:25 +00:00
g_pSourceHeader = NULL ;
2006-02-25 20:50:29 +00:00
vidlastmem = NULL ;
// DESTROY FRAME BUFFER
2006-06-29 03:28:25 +00:00
DeleteDC ( g_hDeviceDC ) ;
DeleteObject ( g_hDeviceBitmap ) ;
g_hDeviceDC = ( HDC ) 0 ;
g_hDeviceBitmap = ( HBITMAP ) 0 ;
2006-02-25 20:50:29 +00:00
// DESTROY SOURCE IMAGE
2006-06-29 03:28:25 +00:00
DeleteObject ( g_hSourceBitmap ) ;
g_hSourceBitmap = ( HBITMAP ) 0 ;
2006-02-25 20:50:29 +00:00
// DESTROY LOGO
2006-06-29 03:28:25 +00:00
if ( g_hLogoBitmap ) {
DeleteObject ( g_hLogoBitmap ) ;
g_hLogoBitmap = ( HBITMAP ) 0 ;
2006-02-25 20:50:29 +00:00
}
// DESTROY PALETTE
2006-06-29 03:28:25 +00:00
if ( g_hPalette ) {
DeleteObject ( g_hPalette ) ;
g_hPalette = ( HPALETTE ) 0 ;
2006-02-25 20:50:29 +00:00
}
}
2006-07-02 22:58:12 +00:00
//===========================================================================
2012-12-29 14:53:52 +00:00
void VideoDrawLogoBitmap ( HDC hDstDC , int xoff , int yoff , int srcw , int srch , int scale )
2006-07-02 22:58:12 +00:00
{
HDC hSrcDC = CreateCompatibleDC ( hDstDC ) ;
SelectObject ( hSrcDC , g_hLogoBitmap ) ;
2012-12-29 14:53:52 +00:00
StretchBlt (
2006-07-02 22:58:12 +00:00
hDstDC , // hdcDest
2012-12-29 14:53:52 +00:00
xoff , yoff , // nXDest, nYDest
scale * srcw , scale * srch , // nWidth, nHeight
2006-07-02 22:58:12 +00:00
hSrcDC , // hdcSrc
0 , 0 , // nXSrc, nYSrc
2012-12-29 14:53:52 +00:00
srcw , srch ,
2006-07-02 22:58:12 +00:00
SRCCOPY // dwRop
) ;
DeleteObject ( hSrcDC ) ;
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2009-02-18 07:54:24 +00:00
void VideoDisplayLogo ( )
{
2012-12-29 14:53:52 +00:00
int xoff = 0 , yoff = 0 , scale = 0 ;
2006-07-02 22:58:12 +00:00
HDC hFrameDC = FrameGetDC ( ) ;
2006-02-25 20:50:29 +00:00
2006-07-02 22:58:12 +00:00
// DRAW THE LOGO
HBRUSH brush = CreateSolidBrush ( PALETTERGB ( 0x70 , 0x30 , 0xE0 ) ) ;
2012-12-29 14:53:52 +00:00
SelectObject ( hFrameDC , brush ) ;
SelectObject ( hFrameDC , GetStockObject ( NULL_PEN ) ) ;
int nViewportCX , nViewportCY ;
GetViewportCXCY ( nViewportCX , nViewportCY ) ;
Rectangle ( hFrameDC , 0 , 0 , nViewportCX + 1 , nViewportCY + 1 ) ;
2006-07-02 22:58:12 +00:00
if ( g_hLogoBitmap )
{
2012-12-29 14:53:52 +00:00
BITMAP bm ;
if ( GetObject ( g_hLogoBitmap , sizeof ( bm ) , & bm ) )
{
scale = nViewportCX / bm . bmWidth ;
if ( nViewportCY / bm . bmHeight < scale )
scale = nViewportCY / bm . bmHeight ;
if ( scale > 0 )
{
if ( nViewportCX > bm . bmWidth )
xoff = ( nViewportCX - ( scale * bm . bmWidth ) ) / 2 ;
if ( nViewportCY > bm . bmHeight )
yoff = ( nViewportCY - ( scale * bm . bmHeight ) ) / 2 ;
VideoDrawLogoBitmap ( hFrameDC , xoff , yoff , bm . bmWidth , bm . bmHeight , scale ) ;
}
}
2006-07-02 22:58:12 +00:00
}
2006-02-25 20:50:29 +00:00
2006-07-02 22:58:12 +00:00
// DRAW THE VERSION NUMBER
HFONT font = CreateFont ( - 20 , 0 , 0 , 0 , FW_NORMAL , 0 , 0 , 0 , ANSI_CHARSET ,
OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , DEFAULT_QUALITY ,
VARIABLE_PITCH | 4 | FF_SWISS ,
TEXT ( " Arial " ) ) ;
SelectObject ( hFrameDC , font ) ;
SetTextAlign ( hFrameDC , TA_RIGHT | TA_TOP ) ;
SetBkMode ( hFrameDC , TRANSPARENT ) ;
2006-02-25 20:50:29 +00:00
2011-02-20 07:32:09 +00:00
char szVersion [ 64 ] = " " ;
2009-02-18 07:54:24 +00:00
sprintf ( szVersion , " Version %s " , VERSIONSTRING ) ;
2012-12-29 14:53:52 +00:00
# define DRAWVERSION(x,y,c) \
SetTextColor ( hFrameDC , c ) ; \
TextOut ( hFrameDC , \
scale * 540 + x + xoff , scale * 358 + y + yoff , \
szVersion , \
2009-02-18 07:54:24 +00:00
strlen ( szVersion ) ) ;
2006-02-25 20:50:29 +00:00
2006-07-02 22:58:12 +00:00
if ( GetDeviceCaps ( hFrameDC , PLANES ) * GetDeviceCaps ( hFrameDC , BITSPIXEL ) < = 4 ) {
2011-02-20 07:32:09 +00:00
DRAWVERSION ( 2 , 2 , RGB ( 0x00 , 0x00 , 0x00 ) ) ;
DRAWVERSION ( 1 , 1 , RGB ( 0x00 , 0x00 , 0x00 ) ) ;
DRAWVERSION ( 0 , 0 , RGB ( 0xFF , 0x00 , 0xFF ) ) ;
} else {
DRAWVERSION ( 1 , 1 , PALETTERGB ( 0x30 , 0x30 , 0x70 ) ) ;
DRAWVERSION ( - 1 , - 1 , PALETTERGB ( 0xC0 , 0x70 , 0xE0 ) ) ;
DRAWVERSION ( 0 , 0 , PALETTERGB ( 0x70 , 0x30 , 0xE0 ) ) ;
2006-07-02 22:58:12 +00:00
}
2006-02-25 20:50:29 +00:00
2011-02-20 07:32:09 +00:00
# if _DEBUG
sprintf ( szVersion , " DEBUG " ) ;
2012-12-29 14:53:52 +00:00
DRAWVERSION ( 2 , - 358 * scale , RGB ( 0x00 , 0x00 , 0x00 ) ) ;
DRAWVERSION ( 1 , - 357 * scale , RGB ( 0x00 , 0x00 , 0x00 ) ) ;
DRAWVERSION ( 0 , - 356 * scale , RGB ( 0xFF , 0x00 , 0xFF ) ) ;
2011-02-20 07:32:09 +00:00
# endif
2006-02-25 20:50:29 +00:00
# undef DRAWVERSION
2006-07-02 22:58:12 +00:00
FrameReleaseDC ( ) ;
DeleteObject ( brush ) ;
DeleteObject ( font ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
BOOL VideoHasRefreshed ( ) {
BOOL result = hasrefreshed ;
hasrefreshed = 0 ;
return result ;
}
//===========================================================================
2009-02-16 20:30:38 +00:00
void VideoRealizePalette ( HDC dc )
{
#if 0
if ( g_bIsFullScreen )
{
if ( ! g_pDDPal )
{
PALETTEENTRY aPal [ 256 ] ;
BYTE * pSrc = ( ( BYTE * ) g_pFramebufferinfo ) + sizeof ( BITMAPINFOHEADER ) ;
BYTE * pDst = ( ( BYTE * ) aPal ) ;
int iPal ;
for ( iPal = 0 ; iPal < 256 ; iPal + + )
{
* ( pDst + 0 ) = * ( pSrc + 2 ) ; // BGR -> RGB
* ( pDst + 1 ) = * ( pSrc + 1 ) ;
* ( pDst + 2 ) = * ( pSrc + 0 ) ;
* ( pDst + 3 ) = 0 ;
pDst + = 4 ;
pSrc + = 4 ;
}
if ( g_pDD - > CreatePalette ( DDPCAPS_8BIT , aPal , & g_pDDPal , NULL ) ! = DD_OK )
{
g_pDDPal = NULL ;
}
}
if ( g_pDDPal )
{
g_pDDPrimarySurface - > SetPalette ( g_pDDPal ) ; // this sets the palette for the primary surface
}
}
else
{
if ( g_hPalette )
{
SelectPalette ( dc , g_hPalette , 0 ) ;
RealizePalette ( dc ) ;
}
}
# endif
if ( g_hPalette )
{
SelectPalette ( dc , g_hPalette , 0 ) ;
RealizePalette ( dc ) ;
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2014-06-27 22:43:25 +01:00
// Called by DrawFrameWindow() when in fullscreen mode (eg. after WM_PAINT msg)
2014-06-26 22:44:02 +01:00
VideoUpdateFuncPtr_t VideoRedrawScreen ( UINT n )
{
g_VideoForceFullRedraw = n ;
return VideoRefreshScreen ( ) ;
}
2011-01-07 09:23:16 +00:00
VideoUpdateFuncPtr_t VideoRedrawScreen ( )
{
g_VideoForceFullRedraw = 1 ;
return VideoRefreshScreen ( ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2008-08-25 05:25:27 +00:00
void _Video_Dirty ( )
{
ZeroMemory ( celldirty , 40 * 32 ) ;
}
//===========================================================================
void _Video_SetupBanks ( bool bBank2 )
{
2008-08-31 04:31:35 +00:00
g_pHiresBank1 = MemGetAuxPtr ( 0x2000 < < ( int ) bBank2 ) ;
g_pHiresBank0 = MemGetMainPtr ( 0x2000 < < ( int ) bBank2 ) ;
g_pTextBank1 = MemGetAuxPtr ( 0x400 < < ( int ) bBank2 ) ;
g_pTextBank0 = MemGetMainPtr ( 0x400 < < ( int ) bBank2 ) ;
2008-08-25 05:25:27 +00:00
}
2006-02-25 20:50:29 +00:00
2008-08-25 05:25:27 +00:00
//===========================================================================
2014-07-20 10:23:23 +01:00
// NB. Can get "big" 1000+ms times: these occur during disk loading when the emulator is at full-speed.
//#define DEBUG_REFRESH_TIMINGS
# if defined(_DEBUG) && defined(DEBUG_REFRESH_TIMINGS)
static void DebugRefresh ( char uDebugFlag )
{
static DWORD uLastRefreshTime = 0 ;
const DWORD uTimeBetweenRefreshes = uLastRefreshTime ? emulmsec - uLastRefreshTime : 0 ;
uLastRefreshTime = emulmsec ;
if ( ! uTimeBetweenRefreshes )
return ; // 1st time in func
char szStr [ 100 ] ;
sprintf ( szStr , " Time between refreshes = %d ms %c \n " , uTimeBetweenRefreshes , ( uDebugFlag = = 0 ) ? ' ' : uDebugFlag ) ;
OutputDebugString ( szStr ) ;
}
# endif
2011-01-07 09:23:16 +00:00
VideoUpdateFuncPtr_t VideoRefreshScreen ( )
{
2014-07-20 10:23:23 +01:00
# if defined(_DEBUG) && defined(DEBUG_REFRESH_TIMINGS)
DebugRefresh ( 0 ) ;
# endif
2011-01-07 09:23:16 +00:00
// CHECK EACH CELL FOR CHANGED BYTES. REDRAW PIXELS FOR THE CHANGED BYTES
// IN THE FRAME BUFFER. MARK CELLS IN WHICH REDRAWING HAS TAKEN PLACE AS
// DIRTY.
2008-08-25 05:25:27 +00:00
_Video_Dirty ( ) ;
_Video_SetupBanks ( g_bVideoDisplayPage2 ) ;
VideoUpdateFuncPtr_t pfUpdate = SW_TEXT
? SW_80COL
? Update80ColCell
: Update40ColCell
: SW_HIRES
? ( SW_DHIRES & & SW_80COL )
? UpdateDHiResCell
: UpdateHiResCell
: ( SW_DHIRES & & SW_80COL )
? UpdateDLoResCell
: UpdateLoResCell ;
bool bMixed = ( SW_MIXED ) ? true : false ;
_Video_RedrawScreen ( pfUpdate , bMixed ) ;
2014-06-26 22:44:02 +01:00
//g_VideoForceFullRedraw = 0;
if ( g_VideoForceFullRedraw ) - - g_VideoForceFullRedraw ;
2011-01-07 09:23:16 +00:00
return pfUpdate ;
2008-08-25 05:25:27 +00:00
}
//===========================================================================
void _Video_RedrawScreen ( VideoUpdateFuncPtr_t pfUpdate , bool bMixed )
{
2009-02-16 20:30:38 +00:00
LPBYTE pDstFrameBufferBits = 0 ;
LONG pitch = 0 ;
HDC hFrameDC = FrameGetVideoDC ( & pDstFrameBufferBits , & pitch ) ;
CreateFrameOffsetTable ( pDstFrameBufferBits , pitch ) ; // ptr to start of each scanline
2006-06-29 03:28:25 +00:00
2006-02-25 20:50:29 +00:00
BOOL anydirty = 0 ;
int y = 0 ;
int ypixel = 0 ;
2009-02-14 03:53:28 +00:00
2006-02-25 20:50:29 +00:00
while ( y < 20 ) {
int offset = ( ( y & 7 ) < < 7 ) + ( ( y > > 3 ) * 40 ) ;
int x = 0 ;
int xpixel = 0 ;
while ( x < 40 ) {
2008-08-25 05:25:27 +00:00
anydirty | = celldirty [ x ] [ y ] = pfUpdate ( x , y , xpixel , ypixel , offset + x ) ;
2006-02-25 20:50:29 +00:00
+ + x ;
xpixel + = 14 ;
}
+ + y ;
ypixel + = 16 ;
}
2008-08-25 05:25:27 +00:00
if ( bMixed ) {
pfUpdate = SW_80COL
? Update80ColCell
: Update40ColCell ;
}
2006-02-25 20:50:29 +00:00
while ( y < 24 ) {
int offset = ( ( y & 7 ) < < 7 ) + ( ( y > > 3 ) * 40 ) ;
int x = 0 ;
int xpixel = 0 ;
while ( x < 40 ) {
2008-08-25 05:25:27 +00:00
anydirty | = celldirty [ x ] [ y ] = pfUpdate ( x , y , xpixel , ypixel , offset + x ) ;
2006-02-25 20:50:29 +00:00
+ + x ;
xpixel + = 14 ;
}
+ + y ;
ypixel + = 16 ;
}
// Clear this flag after TEXT screen has been updated
g_bTextFlashFlag = false ;
2009-02-14 03:53:28 +00:00
// 50% Half Scan Line
if ( g_uHalfScanLines )
{
2009-02-18 22:24:50 +00:00
// 50% Half Scan Line clears every odd scanline.
// Shift-Print Screen saves only the even rows.
// NOTE: Keep in sync with _Video_RedrawScreen() & Video_MakeScreenShot()
2014-06-26 22:44:02 +01:00
// [TC-10/06-2014] In full-screen mode, there's noticable flicker when blanking out these alt lines.
// - Consider doing this 50% operation in CopySource() instead
2009-02-18 07:54:24 +00:00
for ( int y = 1 ; y < FRAMEBUFFER_H ; y + = 2 )
2014-06-26 22:44:02 +01:00
{
if ( ! g_bIsFullScreen | | ! GetFullScreen32Bit ( ) )
{
unsigned char * pSrc = g_aFrameBufferOffset [ y ] ; // 8-bit
for ( int x = 0 ; x < FRAMEBUFFER_W ; x + + )
* pSrc + + = 0 ;
}
else
2009-02-14 03:53:28 +00:00
{
2014-06-26 22:44:02 +01:00
unsigned int * pSrc = ( unsigned int * ) g_aFrameBufferOffset [ y ] ; // 32-bit
for ( int x = 0 ; x < FRAMEBUFFER_W ; x + + )
* pSrc + + = 0 ;
2009-02-14 03:53:28 +00:00
}
}
}
2006-02-25 20:50:29 +00:00
2011-01-09 04:42:46 +00:00
# if 1
2009-02-16 20:30:38 +00:00
// New simpified code:
// . Oliver Schmidt gets a flickering mouse cursor with this code
if ( hFrameDC & & anydirty )
{
2012-12-29 14:53:52 +00:00
int nViewportCX , nViewportCY ;
GetViewportCXCY ( nViewportCX , nViewportCY ) ;
StretchBlt ( hFrameDC , 0 , 0 , nViewportCX , nViewportCY , g_hDeviceDC , 0 , 0 , FRAMEBUFFER_W , FRAMEBUFFER_H , SRCCOPY ) ;
2009-02-16 20:30:38 +00:00
GdiFlush ( ) ;
}
# else
// Original code:
if ( ! hFrameDC | | ! anydirty )
{
FrameReleaseVideoDC ( ) ;
SetLastDrawnImage ( ) ;
g_VideoForceFullRedraw = 0 ;
return ;
}
2006-02-25 20:50:29 +00:00
// COPY DIRTY CELLS FROM THE DEVICE DEPENDENT BITMAP ONTO THE SCREEN
// IN LONG HORIZONTAL RECTANGLES
BOOL remainingdirty = 0 ;
y = 0 ;
ypixel = 0 ;
while ( y < 24 ) {
int start = - 1 ;
int startx = 0 ;
int x = 0 ;
int xpixel = 0 ;
while ( x < 40 ) {
if ( ( x = = 39 ) & & celldirty [ x ] [ y ] )
if ( start > = 0 ) {
xpixel + = 14 ;
celldirty [ x ] [ y ] = 0 ;
}
else
remainingdirty = 1 ;
if ( ( start > = 0 ) & & ! celldirty [ x ] [ y ] ) {
2008-08-31 04:31:35 +00:00
if ( ( x - startx > 1 ) | | ( ( x = = 39 ) & & ( xpixel = = FRAMEBUFFER_W ) ) ) {
2006-02-25 20:50:29 +00:00
int height = 1 ;
while ( ( y + height < 24 )
& & celldirty [ startx ] [ y + height ]
& & celldirty [ x - 1 ] [ y + height ]
& & celldirty [ ( startx + x - 1 ) > > 1 ] [ y + height ] )
height + + ;
2009-02-16 20:30:38 +00:00
BitBlt ( hFrameDC , start , ypixel , xpixel - start , height < < 4 ,
2006-06-29 03:28:25 +00:00
g_hDeviceDC , start , ypixel , SRCCOPY ) ;
2006-02-25 20:50:29 +00:00
while ( height - - ) {
int loop = startx ;
2008-08-31 04:31:35 +00:00
while ( loop < x + ( xpixel = = FRAMEBUFFER_W ) )
2006-02-25 20:50:29 +00:00
celldirty [ loop + + ] [ y + height ] = 0 ;
}
start = - 1 ;
}
else
remainingdirty = 1 ;
start = - 1 ;
}
else if ( ( start = = - 1 ) & & celldirty [ x ] [ y ] & & ( x < 39 ) ) {
start = xpixel ;
startx = x ;
}
x + + ;
xpixel + = 14 ;
}
y + + ;
ypixel + = 16 ;
}
// COPY ANY REMAINING DIRTY CELLS FROM THE DEVICE DEPENDENT BITMAP
// ONTO THE SCREEN IN VERTICAL RECTANGLES
if ( remainingdirty ) {
int x = 0 ;
int xpixel = 0 ;
while ( x < 40 ) {
int start = - 1 ;
int y = 0 ;
int ypixel = 0 ;
while ( y < 24 ) {
if ( ( y = = 23 ) & & celldirty [ x ] [ y ] ) {
if ( start = = - 1 )
start = ypixel ;
ypixel + = 16 ;
celldirty [ x ] [ y ] = 0 ;
}
if ( ( start > = 0 ) & & ! celldirty [ x ] [ y ] ) {
2009-02-16 20:30:38 +00:00
BitBlt ( hFrameDC , xpixel , start , 14 , ypixel - start ,
2006-06-29 03:28:25 +00:00
g_hDeviceDC , xpixel , start , SRCCOPY ) ;
2006-02-25 20:50:29 +00:00
start = - 1 ;
}
else if ( ( start = = - 1 ) & & celldirty [ x ] [ y ] )
start = ypixel ;
y + + ;
ypixel + = 16 ;
}
x + + ;
xpixel + = 14 ;
}
}
GdiFlush ( ) ;
# endif
2011-01-08 21:29:27 +00:00
FrameReleaseVideoDC ( ) ;
SetLastDrawnImage ( ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2011-01-08 21:29:27 +00:00
void VideoReinitialize ( )
{
2011-01-09 04:42:46 +00:00
V_CreateIdentityPalette ( ) ;
V_CreateDIBSections ( ) ;
2006-02-25 20:50:29 +00:00
}
2011-01-08 21:29:27 +00:00
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-01-08 21:29:27 +00:00
void VideoResetState ( )
{
g_nAltCharSetOffset = 0 ;
g_bVideoDisplayPage2 = 0 ;
g_bVideoMode = VF_TEXT ;
g_VideoForceFullRedraw = 1 ;
#if 0 // Debug HGR2 without having to exec 6502 code
g_bVideoDisplayPage2 = 1 ;
g_bVideoMode = VF_TEXT | VF_HIRES ;
# endif
2006-02-25 20:50:29 +00:00
}
2011-01-08 21:29:27 +00:00
2006-02-25 20:50:29 +00:00
//===========================================================================
2011-01-09 04:42:46 +00:00
BYTE VideoSetMode ( WORD , WORD address , BYTE write , BYTE , ULONG uExecutedCycles )
2007-05-28 11:16:42 +00:00
{
2011-01-08 21:29:27 +00:00
address & = 0xFF ;
DWORD oldpage2 = SW_PAGE2 ;
int oldvalue = g_nAltCharSetOffset + ( int ) ( g_bVideoMode & ~ ( VF_MASK2 | VF_PAGE2 ) ) ;
switch ( address ) {
case 0x00 : g_bVideoMode & = ~ VF_MASK2 ; break ;
case 0x01 : g_bVideoMode | = VF_MASK2 ; break ;
case 0x0C : if ( ! IS_APPLE2 ) g_bVideoMode & = ~ VF_80COL ; break ;
case 0x0D : if ( ! IS_APPLE2 ) g_bVideoMode | = VF_80COL ; break ;
case 0x0E : if ( ! IS_APPLE2 ) g_nAltCharSetOffset = 0 ; break ; // Alternate char set off
case 0x0F : if ( ! IS_APPLE2 ) g_nAltCharSetOffset = 256 ; break ; // Alternate char set on
case 0x50 : g_bVideoMode & = ~ VF_TEXT ; break ;
case 0x51 : g_bVideoMode | = VF_TEXT ; break ;
case 0x52 : g_bVideoMode & = ~ VF_MIXED ; break ;
case 0x53 : g_bVideoMode | = VF_MIXED ; break ;
case 0x54 : g_bVideoMode & = ~ VF_PAGE2 ; break ;
case 0x55 : g_bVideoMode | = VF_PAGE2 ; break ;
case 0x56 : g_bVideoMode & = ~ VF_HIRES ; break ;
case 0x57 : g_bVideoMode | = VF_HIRES ; break ;
case 0x5E : if ( ! IS_APPLE2 ) g_bVideoMode | = VF_DHIRES ; break ;
case 0x5F : if ( ! IS_APPLE2 ) g_bVideoMode & = ~ VF_DHIRES ; break ;
}
if ( SW_MASK2 )
g_bVideoMode & = ~ VF_PAGE2 ;
if ( oldvalue ! = g_nAltCharSetOffset + ( int ) ( g_bVideoMode & ~ ( VF_MASK2 | VF_PAGE2 ) ) ) {
g_bGraphicsMode = ! SW_TEXT ;
g_VideoForceFullRedraw = 1 ;
}
if ( g_bFullSpeed & & oldpage2 & & ! SW_PAGE2 ) {
static DWORD lasttime = 0 ;
DWORD currtime = GetTickCount ( ) ;
if ( currtime - lasttime > = 20 )
lasttime = currtime ;
else
oldpage2 = SW_PAGE2 ;
}
2014-07-08 20:58:48 +01:00
if ( oldpage2 ! = SW_PAGE2 )
{
g_bVideoDisplayPage2 = ( SW_PAGE2 ! = 0 ) ;
if ( ! g_VideoForceFullRedraw )
{
# if 1
VideoRefreshScreen ( ) ;
hasrefreshed = 1 ;
# else
g_VideoForceFullRedraw = 1 ; // GH#129,GH204: Defer the redraw until the main ContinueExecution() loop (TODO: What effect does this have on other games?)
# endif
2011-01-08 21:29:27 +00:00
}
lastpageflip = emulmsec ;
}
2014-07-08 20:58:48 +01:00
2011-01-08 21:29:27 +00:00
return MemReadFloatingBus ( uExecutedCycles ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
// Called at 60Hz (every 16.666ms)
void VideoUpdateFlash ( )
{
static UINT nTextFlashCnt = 0 ;
2009-02-22 14:39:50 +00:00
// Flash rate:
// . NTSC : 60/16 ~= 4Hz
// . PAL : 50/16 ~= 3Hz
nTextFlashCnt = ( nTextFlashCnt + 1 ) & 0xf ;
2006-02-25 20:50:29 +00:00
2008-11-25 22:38:54 +00:00
// BUG: In unthrottled CPU mode, flash rate should not be affected
2009-02-22 14:39:50 +00:00
if ( nTextFlashCnt = = 0 )
2006-02-25 20:50:29 +00:00
{
g_bTextFlashState = ! g_bTextFlashState ;
2006-06-12 22:06:50 +00:00
// Redraw any FLASHing chars if any text showing. NB. No FLASH g_nAppMode for 80 cols
2006-06-29 05:37:44 +00:00
if ( ( SW_TEXT | | SW_MIXED ) ) // && !SW_80COL) // FIX: FLASH 80-Column
2006-02-25 20:50:29 +00:00
g_bTextFlashFlag = true ;
}
}
//===========================================================================
bool VideoGetSW80COL ( )
{
return SW_80COL ? true : false ;
}
//===========================================================================
DWORD VideoGetSnapshot ( SS_IO_Video * pSS )
{
2006-06-29 03:28:25 +00:00
pSS - > bAltCharSet = ! ( g_nAltCharSetOffset = = 0 ) ;
2011-01-04 17:08:01 +00:00
pSS - > dwVidMode = g_bVideoMode ;
2006-02-25 20:50:29 +00:00
return 0 ;
}
2006-03-12 09:05:39 +00:00
//===========================================================================
2006-02-25 20:50:29 +00:00
DWORD VideoSetSnapshot ( SS_IO_Video * pSS )
{
2006-06-29 03:28:25 +00:00
g_nAltCharSetOffset = ! pSS - > bAltCharSet ? 0 : 256 ;
2011-01-04 17:08:01 +00:00
g_bVideoMode = pSS - > dwVidMode ;
2006-02-25 20:50:29 +00:00
//
2011-01-08 21:29:27 +00:00
g_bGraphicsMode = ! SW_TEXT ;
2008-08-25 05:25:27 +00:00
g_bVideoDisplayPage2 = ( SW_PAGE2 ! = 0 ) ;
2006-02-25 20:50:29 +00:00
return 0 ;
}
2006-03-12 09:05:39 +00:00
//===========================================================================
2008-12-30 15:45:48 +00:00
//
// References to Jim Sather's books are given as eg:
// UTAIIe:5-7,P3 (Understanding the Apple IIe, chapter 5, page 7, Paragraph 3)
//
2007-08-06 21:38:35 +00:00
WORD VideoGetScannerAddress ( bool * pbVblBar_OUT , const DWORD uExecutedCycles )
2006-03-12 09:05:39 +00:00
{
// get video scanner position
//
2010-08-17 07:52:23 +00:00
int nCycles = CpuGetCyclesThisVideoFrame ( uExecutedCycles ) ;
2006-03-12 09:05:39 +00:00
// machine state switches
//
2008-12-30 15:45:48 +00:00
int nHires = ( SW_HIRES & & ! SW_TEXT ) ? 1 : 0 ;
int nPage2 = ( SW_PAGE2 ) ? 1 : 0 ;
2006-03-12 09:05:39 +00:00
int n80Store = ( MemGet80Store ( ) ) ? 1 : 0 ;
// calculate video parameters according to display standard
//
int nScanLines = bVideoScannerNTSC ? kNTSCScanLines : kPALScanLines ;
int nVSyncLine = bVideoScannerNTSC ? kNTSCVSyncLine : kPALVSyncLine ;
int nScanCycles = nScanLines * kHClocks ;
// calculate horizontal scanning state
//
int nHClock = ( nCycles + kHPEClock ) % kHClocks ; // which horizontal scanning clock
int nHState = kHClock0State + nHClock ; // H state bits
if ( nHClock > = kHPresetClock ) // check for horizontal preset
{
nHState - = 1 ; // correct for state preset (two 0 states)
}
int h_0 = ( nHState > > 0 ) & 1 ; // get horizontal state bits
int h_1 = ( nHState > > 1 ) & 1 ;
int h_2 = ( nHState > > 2 ) & 1 ;
int h_3 = ( nHState > > 3 ) & 1 ;
int h_4 = ( nHState > > 4 ) & 1 ;
int h_5 = ( nHState > > 5 ) & 1 ;
// calculate vertical scanning state
//
int nVLine = nCycles / kHClocks ; // which vertical scanning line
int nVState = kVLine0State + nVLine ; // V state bits
if ( ( nVLine > = kVPresetLine ) ) // check for previous vertical state preset
{
nVState - = nScanLines ; // compensate for preset
}
int v_A = ( nVState > > 0 ) & 1 ; // get vertical state bits
int v_B = ( nVState > > 1 ) & 1 ;
int v_C = ( nVState > > 2 ) & 1 ;
int v_0 = ( nVState > > 3 ) & 1 ;
int v_1 = ( nVState > > 4 ) & 1 ;
int v_2 = ( nVState > > 5 ) & 1 ;
int v_3 = ( nVState > > 6 ) & 1 ;
int v_4 = ( nVState > > 7 ) & 1 ;
int v_5 = ( nVState > > 8 ) & 1 ;
// calculate scanning memory address
//
2008-12-30 15:45:48 +00:00
if ( nHires & & SW_MIXED & & v_4 & & v_2 ) // HIRES TIME signal (UTAIIe:5-7,P3)
2006-03-12 09:05:39 +00:00
{
2008-12-30 15:45:48 +00:00
nHires = 0 ; // address is in text memory for mixed hires
2006-03-12 09:05:39 +00:00
}
2008-12-30 15:45:48 +00:00
int nAddend0 = 0x0D ; // 1 1 0 1
int nAddend1 = ( h_5 < < 2 ) | ( h_4 < < 1 ) | ( h_3 < < 0 ) ;
int nAddend2 = ( v_4 < < 3 ) | ( v_3 < < 2 ) | ( v_4 < < 1 ) | ( v_3 < < 0 ) ;
int nSum = ( nAddend0 + nAddend1 + nAddend2 ) & 0x0F ; // SUM (UTAIIe:5-9)
int nAddress = 0 ; // build address from video scanner equations (UTAIIe:5-8,T5.1)
nAddress | = h_0 < < 0 ; // a0
nAddress | = h_1 < < 1 ; // a1
nAddress | = h_2 < < 2 ; // a2
nAddress | = nSum < < 3 ; // a3 - a6
nAddress | = v_0 < < 7 ; // a7
nAddress | = v_1 < < 8 ; // a8
nAddress | = v_2 < < 9 ; // a9
2008-12-30 16:40:05 +00:00
int p2a = ! ( nPage2 & & ! n80Store ) ;
int p2b = nPage2 & & ! n80Store ;
2008-12-30 15:45:48 +00:00
2006-03-12 09:05:39 +00:00
if ( nHires ) // hires?
{
2008-12-30 15:45:48 +00:00
// Y: insert hires-only address bits
2006-03-12 09:05:39 +00:00
//
2008-12-30 15:45:48 +00:00
nAddress | = v_A < < 10 ; // a10
nAddress | = v_B < < 11 ; // a11
2006-03-12 09:05:39 +00:00
nAddress | = v_C < < 12 ; // a12
2008-12-30 15:45:48 +00:00
nAddress | = p2a < < 13 ; // a13
nAddress | = p2b < < 14 ; // a14
2006-03-12 09:05:39 +00:00
}
else
{
2008-12-30 15:45:48 +00:00
// N: insert text-only address bits
2006-03-12 09:05:39 +00:00
//
2008-12-30 15:45:48 +00:00
nAddress | = p2a < < 10 ; // a10
nAddress | = p2b < < 11 ; // a11
// Apple ][ (not //e) and HBL?
//
if ( IS_APPLE2 & & // Apple II only (UTAIIe:I-4,#5)
2008-12-30 16:40:05 +00:00
! h_5 & & ( ! h_4 | | ! h_3 ) ) // HBL (UTAIIe:8-10,F8.5)
2006-03-12 09:05:39 +00:00
{
2008-12-30 16:40:05 +00:00
nAddress | = 1 < < 12 ; // Y: a12 (add $1000 to address!)
2006-03-12 09:05:39 +00:00
}
}
// update VBL' state
//
if ( pbVblBar_OUT ! = NULL )
{
2008-12-30 16:40:05 +00:00
* pbVblBar_OUT = ! v_4 | | ! v_3 ; // VBL' = (v_4 & v_3)' (UTAIIe:5-10,#3)
2006-03-12 09:05:39 +00:00
}
return static_cast < WORD > ( nAddress ) ;
}
//===========================================================================
2007-08-06 21:38:35 +00:00
// Derived from VideoGetScannerAddress()
bool VideoGetVbl ( const DWORD uExecutedCycles )
{
// get video scanner position
//
2010-08-17 07:52:23 +00:00
int nCycles = CpuGetCyclesThisVideoFrame ( uExecutedCycles ) ;
2007-08-06 21:38:35 +00:00
// calculate video parameters according to display standard
//
int nScanLines = bVideoScannerNTSC ? kNTSCScanLines : kPALScanLines ;
// calculate vertical scanning state
//
int nVLine = nCycles / kHClocks ; // which vertical scanning line
int nVState = kVLine0State + nVLine ; // V state bits
if ( ( nVLine > = kVPresetLine ) ) // check for previous vertical state preset
{
nVState - = nScanLines ; // compensate for preset
}
int v_3 = ( nVState > > 6 ) & 1 ;
int v_4 = ( nVState > > 7 ) & 1 ;
// update VBL' state
//
if ( v_4 & v_3 ) // VBL?
{
return false ; // Y: VBL' is false
}
else
{
return true ; // N: VBL' is true
}
}
2010-08-17 07:52:23 +00:00
//===========================================================================
2008-08-25 00:36:48 +00:00
# define SCREENSHOT_BMP 1
# define SCREENSHOT_TGA 0
2008-07-14 16:02:44 +00:00
// alias for nSuffixScreenShotFileName
2008-08-31 04:31:35 +00:00
static int g_nLastScreenShot = 0 ;
2008-07-14 16:02:44 +00:00
const int nMaxScreenShot = 999999999 ;
2008-08-31 04:31:35 +00:00
2011-02-15 17:16:36 +00:00
static int g_iScreenshotType ;
2008-08-31 04:31:35 +00:00
static char * g_pLastDiskImageName = NULL ;
2008-07-14 16:02:44 +00:00
//const int nMaxScreenShot = 2;
2008-08-31 04:31:35 +00:00
//===========================================================================
void Video_ResetScreenshotCounter ( char * pImageName )
{
g_nLastScreenShot = 0 ;
g_pLastDiskImageName = pImageName ;
}
2008-07-14 16:02:44 +00:00
//===========================================================================
void Util_MakeScreenShotFileName ( char * pFinalFileName_ )
{
2008-08-31 04:31:35 +00:00
char sPrefixScreenShotFileName [ 256 ] = " AppleWin_ScreenShot " ;
2011-02-15 17:16:36 +00:00
// TODO: g_sScreenshotDir
2008-08-31 04:31:35 +00:00
char * pPrefixFileName = g_pLastDiskImageName ? g_pLastDiskImageName : sPrefixScreenShotFileName ;
2008-08-25 00:36:48 +00:00
# if SCREENSHOT_BMP
2008-08-31 04:31:35 +00:00
sprintf ( pFinalFileName_ , " %s_%09d.bmp " , pPrefixFileName , g_nLastScreenShot ) ;
2008-08-25 00:36:48 +00:00
# endif
# if SCREENSHOT_TGA
2008-08-31 04:31:35 +00:00
sprintf ( pFinalFileName_ , " %s%09d.tga " , pPrefixFileName , g_nLastScreenShot ) ;
2008-08-25 00:36:48 +00:00
# endif
2008-07-14 16:02:44 +00:00
}
2009-02-16 20:30:38 +00:00
// Returns TRUE if file exists, else FALSE
2007-08-06 21:38:35 +00:00
//===========================================================================
2008-07-14 16:02:44 +00:00
bool Util_TestScreenShotFileName ( const char * pFileName )
{
bool bFileExists = false ;
FILE * pFile = fopen ( pFileName , " rt " ) ;
if ( pFile )
{
fclose ( pFile ) ;
bFileExists = true ;
}
return bFileExists ;
}
//===========================================================================
2008-08-31 04:31:35 +00:00
void Video_TakeScreenShot ( int iScreenShotType )
2008-07-14 16:02:44 +00:00
{
char sScreenShotFileName [ MAX_PATH ] ;
2008-08-31 04:31:35 +00:00
g_iScreenshotType = iScreenShotType ;
2008-07-14 16:02:44 +00:00
// find last screenshot filename so we don't overwrite the existing user ones
bool bExists = true ;
while ( bExists )
{
2008-08-31 04:31:35 +00:00
if ( g_nLastScreenShot > nMaxScreenShot ) // Holy Crap! User has maxed the number of screenshots!?
2008-07-14 16:02:44 +00:00
{
sprintf ( sScreenShotFileName , " You have more then %d screenshot filenames! They will no longer be saved. \n \n Either move some of your screenshots or increase the maximum in video.cpp \n " , nMaxScreenShot ) ;
2013-12-06 21:10:41 +00:00
MessageBox ( g_hFrameWindow , sScreenShotFileName , " Warning " , MB_OK ) ;
2008-08-31 04:31:35 +00:00
g_nLastScreenShot = 0 ;
2008-07-14 16:02:44 +00:00
return ;
}
Util_MakeScreenShotFileName ( sScreenShotFileName ) ;
bExists = Util_TestScreenShotFileName ( sScreenShotFileName ) ;
if ( ! bExists )
{
break ;
}
2008-08-31 04:31:35 +00:00
g_nLastScreenShot + + ;
2008-07-14 16:02:44 +00:00
}
Video_SaveScreenShot ( sScreenShotFileName ) ;
2008-08-31 04:31:35 +00:00
g_nLastScreenShot + + ;
2008-07-14 16:02:44 +00:00
}
2008-08-25 00:36:48 +00:00
typedef char int8 ;
2008-07-14 16:02:44 +00:00
typedef short int16 ;
typedef int int32 ;
2008-08-25 00:36:48 +00:00
typedef unsigned char u8 ;
typedef signed short s16 ;
/// turn of MSVC struct member padding
# pragma pack(push,1)
struct bgra_t
{
u8 b ;
u8 g ;
u8 r ;
u8 a ; // reserved on Win32
} ;
2008-07-14 16:02:44 +00:00
struct WinBmpHeader_t
{
2008-08-25 00:36:48 +00:00
// BITMAPFILEHEADER // Addr Size
char nCookie [ 2 ] ; // 0x00 0x02 BM
int32 nSizeFile ; // 0x02 0x04 0 = ignore
int16 nReserved1 ; // 0x06 0x02
int16 nReserved2 ; // 0x08 0x02
int32 nOffsetData ; // 0x0A 0x04
// == 0x0D (14)
// BITMAPINFOHEADER
int32 nStructSize ; // 0x0E 0x04 biSize
int32 nWidthPixels ; // 0x12 0x04 biWidth
int32 nHeightPixels ; // 0x16 0x04 biHeight
int16 nPlanes ; // 0x1A 0x02 biPlanes
int16 nBitsPerPixel ; // 0x1C 0x02 biBitCount
int32 nCompression ; // 0x1E 0x04 biCompression 0 = BI_RGB
int32 nSizeImage ; // 0x22 0x04 0 = ignore
int32 nXPelsPerMeter ; // 0x26 0x04
int32 nYPelsPerMeter ; // 0x2A 0x04
int32 nPaletteColors ; // 0x2E 0x04
int32 nImportantColors ; // 0x32 0x04
// == 0x28 (40)
// RGBQUAD
// pixelmap
2008-07-14 16:02:44 +00:00
} ;
2008-08-25 00:36:48 +00:00
# pragma pack(pop)
2008-07-14 16:02:44 +00:00
WinBmpHeader_t g_tBmpHeader ;
2008-08-25 00:36:48 +00:00
# if SCREENSHOT_TGA
enum TargaImageType_e
{
TARGA_RGB = 2
} ;
struct TargaHeader_t
{ // Addr Bytes
u8 nIdBytes ; // 00 01 size of ID field that follows 18 byte header (0 usually)
u8 bHasPalette ; // 01 01
u8 iImageType ; // 02 01 type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed
s16 iPaletteFirstColor ; // 03 02
s16 nPaletteColors ; // 05 02
u8 nPaletteBitsPerEntry ; // 07 01 number of bits per palette entry 15,16,24,32
s16 nOriginX ; // 08 02 image x origin
s16 nOriginY ; // 0A 02 image y origin
s16 nWidthPixels ; // 0C 02
s16 nHeightPixels ; // 0E 02
u8 nBitsPerPixel ; // 10 01 image bits per pixel 8,16,24,32
u8 iDescriptor ; // 11 01 image descriptor bits (vh flip bits)
// pixel data...
u8 aPixelData [ 1 ] ; // rgb
} ;
TargaHeader_t g_tTargaHeader ;
# endif // SCREENSHOT_TGA
2008-07-14 16:02:44 +00:00
//===========================================================================
2008-08-25 00:36:48 +00:00
void Video_MakeScreenShot ( FILE * pFile )
2008-07-14 16:02:44 +00:00
{
2008-08-25 00:36:48 +00:00
# if SCREENSHOT_BMP
g_tBmpHeader . nCookie [ 0 ] = ' B ' ; // 0x42
g_tBmpHeader . nCookie [ 1 ] = ' M ' ; // 0x4d
g_tBmpHeader . nSizeFile = 0 ;
2008-07-14 16:02:44 +00:00
g_tBmpHeader . nReserved1 = 0 ;
g_tBmpHeader . nReserved2 = 0 ;
2008-08-25 00:36:48 +00:00
g_tBmpHeader . nOffsetData = sizeof ( WinBmpHeader_t ) + ( 256 * sizeof ( bgra_t ) ) ;
g_tBmpHeader . nStructSize = 0x28 ; // sizeof( WinBmpHeader_t );
2008-08-31 04:31:35 +00:00
g_tBmpHeader . nWidthPixels = g_iScreenshotType ? FRAMEBUFFER_W / 2 : FRAMEBUFFER_W ;
g_tBmpHeader . nHeightPixels = g_iScreenshotType ? FRAMEBUFFER_H / 2 : FRAMEBUFFER_H ;
2008-07-14 16:02:44 +00:00
g_tBmpHeader . nPlanes = 1 ;
2008-08-25 00:36:48 +00:00
g_tBmpHeader . nBitsPerPixel = 8 ;
2008-07-14 16:02:44 +00:00
g_tBmpHeader . nCompression = BI_RGB ;
g_tBmpHeader . nSizeImage = 0 ;
g_tBmpHeader . nXPelsPerMeter = 0 ;
g_tBmpHeader . nYPelsPerMeter = 0 ;
2008-08-25 00:36:48 +00:00
g_tBmpHeader . nPaletteColors = 256 ;
2008-07-14 16:02:44 +00:00
g_tBmpHeader . nImportantColors = 0 ;
2008-08-25 00:36:48 +00:00
// char sText[256];
// sprintf( sText, "sizeof: BITMAPFILEHEADER = %d\n", sizeof(BITMAPFILEHEADER) ); // = 14
2013-12-06 21:10:41 +00:00
// MessageBox( g_hFrameWindow, sText, "Info 1", MB_OK );
2008-08-25 00:36:48 +00:00
// sprintf( sText, "sizeof: BITMAPINFOHEADER = %d\n", sizeof(BITMAPINFOHEADER) ); // = 40
2013-12-06 21:10:41 +00:00
// MessageBox( g_hFrameWindow, sText, "Info 2", MB_OK );
2008-08-25 00:36:48 +00:00
char sIfSizeZeroOrUnknown_BadWinBmpHeaderPackingSize [ sizeof ( WinBmpHeader_t ) = = ( 14 + 40 ) ] ;
sIfSizeZeroOrUnknown_BadWinBmpHeaderPackingSize ;
// Write Header
int nLen ;
fwrite ( & g_tBmpHeader , sizeof ( g_tBmpHeader ) , 1 , pFile ) ;
2009-02-16 20:30:38 +00:00
2008-08-25 00:36:48 +00:00
// Write Palette Data
u8 * pSrc = ( ( u8 * ) g_pFramebufferinfo ) + sizeof ( BITMAPINFOHEADER ) ;
nLen = g_tBmpHeader . nPaletteColors * sizeof ( bgra_t ) ; // RGBQUAD
fwrite ( pSrc , nLen , 1 , pFile ) ;
pSrc + = nLen ;
// Write Pixel Data
// No need to use GetDibBits() since we already have http://msdn.microsoft.com/en-us/library/ms532334.aspx
// @reference: "Storing an Image" http://msdn.microsoft.com/en-us/library/ms532340(VS.85).aspx
pSrc = ( ( u8 * ) g_pFramebufferbits ) ;
nLen = g_tBmpHeader . nWidthPixels * g_tBmpHeader . nHeightPixels * g_tBmpHeader . nBitsPerPixel / 8 ;
2008-08-31 04:31:35 +00:00
if ( g_iScreenshotType = = SCREENSHOT_280x192 )
{
2013-03-28 22:28:42 +00:00
pSrc + = FRAMEBUFFER_W ; // Start on odd scanline (otherwise for 50% scanline mode get an all black image!)
2008-08-31 04:31:35 +00:00
u8 aScanLine [ 280 ] ;
u8 * pDst ;
2009-02-18 22:24:50 +00:00
// 50% Half Scan Line clears every odd scanline.
// Shift-Print Screen saves only the even rows.
// NOTE: Keep in sync with _Video_RedrawScreen() & Video_MakeScreenShot()
2008-08-31 04:31:35 +00:00
for ( int y = 0 ; y < FRAMEBUFFER_H / 2 ; y + + )
{
pDst = aScanLine ;
for ( int x = 0 ; x < FRAMEBUFFER_W / 2 ; x + + )
{
2013-03-28 22:28:42 +00:00
* pDst + + = pSrc [ 1 ] ; // correction for left edge loss of scaled scanline [Bill Buckel, B#18928]
2008-08-31 04:31:35 +00:00
pSrc + = 2 ; // skip odd pixels
}
fwrite ( aScanLine , FRAMEBUFFER_W / 2 , 1 , pFile ) ;
pSrc + = FRAMEBUFFER_W ; // scan lines doubled - skip odd ones
}
}
else
{
fwrite ( pSrc , nLen , 1 , pFile ) ;
}
2008-08-25 00:36:48 +00:00
# endif // SCREENSHOT_BMP
# if SCREENSHOT_TGA
TargaHeader_t * pHeader = & g_tTargaHeader ;
memset ( ( void * ) pHeader , 0 , sizeof ( TargaHeader_t ) ) ;
pHeader - > iImageType = TARGA_RGB ;
2008-08-31 04:31:35 +00:00
pHeader - > nWidthPixels = FRAMEBUFFER_W ;
pHeader - > nHeightPixels = FRAMEBUFFER_H ;
2008-08-25 00:36:48 +00:00
pHeader - > nBitsPerPixel = 24 ;
# endif // SCREENSHOT_TGA
2008-07-14 16:02:44 +00:00
}
//===========================================================================
void Video_SaveScreenShot ( const char * pScreenShotFileName )
{
FILE * pFile = fopen ( pScreenShotFileName , " wb " ) ;
if ( pFile )
{
2008-08-25 00:36:48 +00:00
Video_MakeScreenShot ( pFile ) ;
2008-07-14 16:02:44 +00:00
fclose ( pFile ) ;
}
2008-08-25 01:10:37 +00:00
if ( g_bDisplayPrintScreenFileName )
{
2013-12-06 21:10:41 +00:00
MessageBox ( g_hFrameWindow , pScreenShotFileName , " Screen Captured " , MB_OK ) ;
2008-08-25 01:10:37 +00:00
}
2008-07-14 16:02:44 +00:00
}
2012-03-27 21:20:36 +00:00
//===========================================================================
void Config_Load_Video ( )
{
REGLOAD ( TEXT ( REGVALUE_VIDEO_MODE ) , & g_eVideoType ) ;
REGLOAD ( TEXT ( REGVALUE_VIDEO_HALF_SCAN_LINES ) , & g_uHalfScanLines ) ;
REGLOAD ( TEXT ( REGVALUE_VIDEO_MONO_COLOR ) , & monochrome ) ;
if ( g_eVideoType > = NUM_VIDEO_MODES )
g_eVideoType = VT_COLOR_STANDARD ; // Old default: VT_COLOR_TVEMU
}
void Config_Save_Video ( )
{
REGSAVE ( TEXT ( REGVALUE_VIDEO_MODE ) , g_eVideoType ) ;
REGSAVE ( TEXT ( REGVALUE_VIDEO_HALF_SCAN_LINES ) , g_uHalfScanLines ) ;
REGSAVE ( TEXT ( REGVALUE_VIDEO_MONO_COLOR ) , monochrome ) ;
}