Extended support for RGB cards (#819) (PR #837)

Video mode: "Color (RGB Card/Monitor)"
. RGB HGR rendering (no half-dot-shift, no vertical blend)
. RGB DHGR rendering
  - Color 140-mode is a real 140x192 mode with no color fringe (Video7 patent, shows that way on real hardware)
  - Mixed mode: correct transition for Color 140-mode <-> B&W (validated on a real IIc adapter.)
. Complete Féline support (eg. 2 distinct greys)
. Fixed Video7 SL7 inverse text
. Fixed DHIRES mixed mode detection
. NB. Prince of Persia will switch to B&W (560-mode) when going from HGR back to DHGR (same on real hardware).

Video mode: "Color (Composite Idealized)"
. Previously this was "Color (RGB Monitor)" (and before that, the AppleWin 1.25 "Color (Standard)")
. Now this mode does not support the extra RGB cards' video modes
This commit is contained in:
Cyril Lambin 2020-09-27 15:09:02 +02:00 committed by GitHub
parent b9d80a0317
commit 08458a9d29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 537 additions and 82 deletions

View file

@ -1751,6 +1751,10 @@ static bool ProcessCmdLine(LPSTR lpCmdLine)
{
g_cmdLine.newVideoType = VT_COLOR_MONITOR_RGB;
}
else if (strcmp(lpCmdLine, "-video-mode=rgb-videocard") == 0)
{
g_cmdLine.newVideoType = VT_COLOR_VIDEOCARD_RGB;
}
else if (strcmp(lpCmdLine, "-video-mode=composite-monitor") == 0) // GH#763
{
g_cmdLine.newVideoType = VT_COLOR_MONITOR_NTSC;
@ -2041,6 +2045,9 @@ static void RepeatInitialization(void)
FrameCreateWindow(); // g_hFrameWindow is now valid
LogFileOutput("Main: FrameCreateWindow() - post\n");
// Init palette color
VideoSwitchVideocardPalette(RGB_GetVideocard(), GetVideoType());
// Allow the 4 hardcoded slots to be configurated as empty
// NB. this state is not persisted to the Registry/conf.ini (just as '-s7 empty' isn't)
// TODO: support bSlotEmpty[] for slots: 0,4,5

View file

@ -443,6 +443,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
static void updateScreenText80 ( long cycles6502 );
static void updateScreenText40RGB ( long cycles6502 );
static void updateScreenText80RGB ( long cycles6502 );
static void updateScreenDoubleHires80Simplified(long cycles6502);
static void updateScreenDoubleHires80RGB(long cycles6502);
//===========================================================================
static void set_csbits()
@ -830,7 +832,7 @@ inline void updateVideoScannerAddress()
(g_pFuncUpdateGraphicsScreen == updateScreenText80) ||
(g_pFuncUpdateGraphicsScreen == updateScreenText80RGB) ||
(g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED && (g_pFuncUpdateTextScreen == updateScreenText80 || g_pFuncUpdateGraphicsScreen == updateScreenText80RGB)))
&& (g_eVideoType != VT_COLOR_MONITOR_RGB)) // Fix for "Ansi Story" (Turn the disk over) - Top row of TEXT80 is shifted by 1 pixel
&& (g_eVideoType != VT_COLOR_MONITOR_RGB) && (g_eVideoType != VT_COLOR_VIDEOCARD_RGB)) // Fix for "Ansi Story" (Turn the disk over) - Top row of TEXT80 is shifted by 1 pixel
{
g_pVideoAddress -= 1;
}
@ -1235,7 +1237,41 @@ void updateScreenDoubleHires40 (long cycles6502) // wsUpdateVideoHires0
//===========================================================================
void updateScreenDoubleHires80Simplified (long cycles6502 ) // wsUpdateVideoDblHires
void updateScreenDoubleHires80Simplified(long cycles6502) // wsUpdateVideoDblHires
{
if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED)
{
g_pFuncUpdateTextScreen(cycles6502);
return;
}
for (; cycles6502 > 0; --cycles6502)
{
uint16_t addr = getVideoScannerAddressHGR();
if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if ((g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END) && (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_COLORBURST_BEG))
{
g_nColorBurstPixels = 1024;
}
else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint16_t addr = getVideoScannerAddressHGR();
uint8_t a = *MemGetAuxPtr(addr);
uint8_t m = *MemGetMainPtr(addr);
UpdateDHiResCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, true, true);
g_pVideoAddress += 14;
}
}
updateVideoScannerHorzEOLSimple();
}
}
//===========================================================================
void updateScreenDoubleHires80RGB (long cycles6502 ) // wsUpdateVideoDblHires
{
if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED)
{
@ -1270,31 +1306,16 @@ void updateScreenDoubleHires80Simplified (long cycles6502 ) // wsUpdateVideoDblH
int width = UpdateDHiRes160Cell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress);
g_pVideoAddress += width;
}
else if (RGB_Is560Mode() || (RGB_IsMixMode() && !((a | m) & 0x80)))
else if (RGB_Is560Mode())// || (RGB_IsMixMode() && !((a | m) & 0x80)))
{
update7MonoPixels(a);
update7MonoPixels(m);
}
else if (!RGB_IsMixMode() || (RGB_IsMixMode() && (a & m & 0x80)))
else
{
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, true, true);
UpdateDHiResCellRGB(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, RGB_IsMixMode(), RGB_IsMixModeInvertBit7());
g_pVideoAddress += 14;
}
else // RGB_IsMixMode() && ((a ^ m) & 0x80)
{
if (a & 0x80) // RGB color, then monochrome
{
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, true ,false);
g_pVideoAddress += 7;
update7MonoPixels(m);
}
else // monochrome, then RGB color
{
update7MonoPixels(a);
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, false, true);
g_pVideoAddress += 7;
}
}
}
}
updateVideoScannerHorzEOLSimple();
@ -1514,6 +1535,36 @@ static void updateScreenSingleHires40Duochrome(long cycles6502)
}
}
//===========================================================================
static void updateScreenSingleHires40RGB(long cycles6502)
{
if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED)
{
g_pFuncUpdateTextScreen(cycles6502);
return;
}
for (; cycles6502 > 0; --cycles6502)
{
if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if ((g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END) && (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_COLORBURST_BEG))
{
g_nColorBurstPixels = 1024;
}
else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint16_t addr = getVideoScannerAddressHGR();
UpdateHiResRGBCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress);
g_pVideoAddress += 14;
}
}
updateVideoScannerHorzEOLSimple();
}
}
//===========================================================================
void updateScreenSingleHires40 (long cycles6502)
{
if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED)
@ -1670,7 +1721,7 @@ void updateScreenText40RGB(long cycles6502)
if (0 == g_nVideoCharSet && 0x40 == (m & 0xC0)) // Flash only if mousetext not active
c ^= g_nTextFlashMask;
UpdateText40ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, c);
UpdateText40ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, c, m);
g_pVideoAddress += 14;
}
@ -1712,7 +1763,8 @@ void updateScreenText80 (long cycles6502)
aux ^= g_nTextFlashMask;
uint16_t bits = (main << 7) | (aux & 0x7f);
if (g_eVideoType != VT_COLOR_MONITOR_RGB) // No extra 14M bit needed for VT_COLOR_MONITOR_RGB
if ((g_eVideoType != VT_COLOR_MONITOR_RGB) // No extra 14M bit needed for VT_COLOR_MONITOR_RGB
&& (g_eVideoType != VT_COLOR_VIDEOCARD_RGB))
bits = (bits << 1) | g_nLastColumnPixelNTSC; // GH#555: Align TEXT80 chars with DHGR
updatePixels( bits );
@ -1754,9 +1806,9 @@ void updateScreenText80RGB(long cycles6502)
if ((0 == g_nVideoCharSet) && 0x40 == (a & 0xC0)) // Flash only if mousetext not active
aux ^= g_nTextFlashMask;
UpdateText80ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, (uint8_t)aux);
UpdateText80ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, (uint8_t)aux, a);
g_pVideoAddress += 7;
UpdateText80ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, (uint8_t)main);
UpdateText80ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, (uint8_t)main, m);
g_pVideoAddress += 7;
uint16_t bits = (main << 7) | (aux & 0x7f);
@ -1846,7 +1898,7 @@ uint16_t NTSC_VideoGetScannerAddressForDebugger(void)
//===========================================================================
void NTSC_SetVideoTextMode( int cols )
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
if (g_eVideoType == VT_COLOR_VIDEOCARD_RGB)
{
if (cols == 40)
g_pFuncUpdateTextScreen = updateScreenText40RGB;
@ -1921,7 +1973,7 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
}
// Video7_SL7 extra RGB modes handling
if (g_eVideoType == VT_COLOR_MONITOR_RGB
if (g_eVideoType == VT_COLOR_VIDEOCARD_RGB
&& RGB_GetVideocard() == RGB_Videocard_e::Video7_SL7
// Exclude following modes (fallback through regular NTSC rendering with RGB text)
// VF_DHIRES = 1 -> regular Apple IIe modes
@ -1976,12 +2028,12 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
{
if (uVideoModeFlags & VF_80COL)
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
if (g_eVideoType == VT_COLOR_VIDEOCARD_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenText80RGB;
else
g_pFuncUpdateGraphicsScreen = updateScreenText80;
}
else if (g_eVideoType == VT_COLOR_MONITOR_RGB)
else if (g_eVideoType == VT_COLOR_VIDEOCARD_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenText40RGB;
else
g_pFuncUpdateGraphicsScreen = updateScreenText40;
@ -1994,6 +2046,8 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires80Simplified;
else if (g_eVideoType == VT_COLOR_VIDEOCARD_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires80RGB;
else
g_pFuncUpdateGraphicsScreen = updateScreenDoubleHires80;
}
@ -2006,6 +2060,8 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40Simplified;
else if (g_eVideoType == VT_COLOR_VIDEOCARD_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40RGB;
else
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40;
}
@ -2016,7 +2072,7 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
{
if (uVideoModeFlags & VF_80COL)
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
if ((g_eVideoType == VT_COLOR_MONITOR_RGB) || (g_eVideoType == VT_COLOR_VIDEOCARD_RGB))
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores80Simplified;
else
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores80;
@ -2028,7 +2084,7 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
}
else
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
if ((g_eVideoType == VT_COLOR_MONITOR_RGB) || (g_eVideoType == VT_COLOR_VIDEOCARD_RGB))
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40Simplified;
else
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40;
@ -2105,6 +2161,7 @@ void NTSC_SetVideoStyle(void)
goto _mono;
case VT_COLOR_MONITOR_RGB:
case VT_COLOR_VIDEOCARD_RGB:
case VT_MONO_WHITE:
r = 0xFF;
g = 0xFF;

View file

@ -117,7 +117,9 @@ const BYTE DoubleHiresPalIndex[16] = {
#define SETRGBCOLOR(r,g,b) {b,g,r,0}
static RGBQUAD PalIndex2RGB[] =
static RGBQUAD* g_pPaletteRGB;
static RGBQUAD PaletteRGB_NTSC[] =
{
// hires
#if DO_OPT_PALETTE
@ -128,6 +130,7 @@ static RGBQUAD PalIndex2RGB[] =
SETRGBCOLOR(/*HGR_BLACK, */ 0x00,0x00,0x00), // For TV emulation HGR Video Mode
SETRGBCOLOR(/*HGR_WHITE, */ 0xFF,0xFF,0xFF),
#else
// Note: this is a placeholder. This palette is overwritten by VideoInitializeOriginal()
SETRGBCOLOR(/*HGR_BLACK, */ 0x00,0x00,0x00), // For TV emulation HGR Video Mode
SETRGBCOLOR(/*HGR_WHITE, */ 0xFF,0xFF,0xFF),
SETRGBCOLOR(/*BLUE, */ 0x0D,0xA1,0xFF), // FC Linards Tweaked 0x00,0x00,0xFF -> 0x0D,0xA1,0xFF
@ -145,6 +148,7 @@ static RGBQUAD PalIndex2RGB[] =
SETRGBCOLOR(/*HGR_PINK, */ 0xFF,0x32,0xB5), // 0xD0,0x40,0xA0 -> 0xFF,0x32,0xB5
// lores & dhires
// Note: this is a placeholder. This palette is overwritten by VideoInitializeOriginal()
SETRGBCOLOR(/*BLACK,*/ 0x00,0x00,0x00), // 0
SETRGBCOLOR(/*DEEP_RED,*/ 0x9D,0x09,0x66), // 0xD0,0x00,0x30 -> Linards Tweaked 0x9D,0x09,0x66
SETRGBCOLOR(/*DARK_BLUE,*/ 0x2A,0x2A,0xE5), // 4 // Linards Tweaked 0x00,0x00,0x80 -> 0x2A,0x2A,0xE5
@ -163,6 +167,44 @@ static RGBQUAD PalIndex2RGB[] =
SETRGBCOLOR(/*WHITE,*/ 0xFF,0xFF,0xFF),
};
// Le Chat Mauve Feline's palette
// extracted from a white-balanced RGB video capture
static RGBQUAD PaletteRGB_Feline[] =
{
SETRGBCOLOR(/*HGR_BLACK, */ 0x00,0x00,0x00),
SETRGBCOLOR(/*HGR_WHITE, */ 0xFF,0xFF,0xFF),
SETRGBCOLOR(/*BLUE, */ 0x00,0x8A,0xB5),
SETRGBCOLOR(/*ORANGE, */ 0xFF,0x72,0x47),
SETRGBCOLOR(/*GREEN, */ 0x6F,0xE6,0x2C),
SETRGBCOLOR(/*MAGENTA, */ 0xAA,0x1A,0xD1),
// TV emu
SETRGBCOLOR(/*HGR_GREY1, */ 0x80,0x80,0x80),
SETRGBCOLOR(/*HGR_GREY2, */ 0x80,0x80,0x80),
SETRGBCOLOR(/*HGR_YELLOW,*/ 0x9E,0x9E,0x00),
SETRGBCOLOR(/*HGR_AQUA, */ 0x00,0xCD,0x4A),
SETRGBCOLOR(/*HGR_PURPLE,*/ 0x61,0x61,0xFF),
SETRGBCOLOR(/*HGR_PINK, */ 0xFF,0x32,0xB5),
// Feline
SETRGBCOLOR(/*BLACK,*/ 0x00,0x00,0x00),
SETRGBCOLOR(/*DEEP_RED,*/ 0xAC,0x12,0x4C),
SETRGBCOLOR(/*DARK_BLUE,*/ 0x00,0x07,0x83),
SETRGBCOLOR(/*MAGENTA,*/ 0xAA,0x1A,0xD1),
SETRGBCOLOR(/*DARK_GREEN,*/ 0x00,0x83,0x2F),
SETRGBCOLOR(/*DARK_GRAY,*/ 0x9F,0x97,0x7E),
SETRGBCOLOR(/*BLUE,*/ 0x00,0x8A,0xB5),
SETRGBCOLOR(/*LIGHT_BLUE,*/ 0x9F,0x9E,0xFF),
SETRGBCOLOR(/*BROWN,*/ 0x7A,0x5F,0x00),
SETRGBCOLOR(/*ORANGE,*/ 0xFF,0x72,0x47),
SETRGBCOLOR(/*LIGHT_GRAY,*/ 0x78,0x68,0x7F),
SETRGBCOLOR(/*PINK,*/ 0xFF,0x7A,0xCF),
SETRGBCOLOR(/*GREEN,*/ 0x6F,0xE6,0x2C),
SETRGBCOLOR(/*YELLOW,*/ 0xFF,0xF6,0x7B),
SETRGBCOLOR(/*AQUA,*/ 0x6C,0xEE,0xB2),
SETRGBCOLOR(/*WHITE,*/ 0xFF,0xFF,0xFF),
};
//===========================================================================
static void V_CreateLookup_DoubleHires ()
@ -501,8 +543,8 @@ static void CopyMixedSource(int x, int y, int sx, int sy, bgra_t *pVideoAddress)
}
else
{
_ASSERT( colormixbuffer[h] < (sizeof(PalIndex2RGB)/sizeof(PalIndex2RGB[0])) );
const RGBQUAD& rRGB = PalIndex2RGB[ colormixbuffer[h] ];
_ASSERT( colormixbuffer[h] < (sizeof(PaletteRGB_NTSC)/sizeof(PaletteRGB_NTSC[0])) );
const RGBQUAD& rRGB = g_pPaletteRGB[ colormixbuffer[h] ];
*(pDst+nBytes) = *reinterpret_cast<const UINT32 *>(&rRGB);
}
@ -533,8 +575,8 @@ static void CopySource(int w, int h, int sx, int sy, bgra_t *pVideoAddress, cons
{
for (int nBytes=0; nBytes<w; ++nBytes)
{
_ASSERT( *(pSrc+nBytes+nSrcAdjustment) < (sizeof(PalIndex2RGB)/sizeof(PalIndex2RGB[0])) );
const RGBQUAD& rRGB = PalIndex2RGB[ *(pSrc+nBytes+nSrcAdjustment) ];
_ASSERT( *(pSrc+nBytes+nSrcAdjustment) < (sizeof(PaletteRGB_NTSC)/sizeof(PaletteRGB_NTSC[0])) );
const RGBQUAD& rRGB = g_pPaletteRGB[ *(pSrc+nBytes+nSrcAdjustment) ];
*(pDst+nBytes) = *reinterpret_cast<const UINT32 *>(&rRGB);
}
}
@ -569,25 +611,25 @@ void UpdateHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)
#define COLOR ((xpixel + PIXEL) & 3)
#define VALUE (dwordval >> (4 + PIXEL - COLOR))
void UpdateDHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress, bool updateAux, bool updateMain)
void UpdateDHiResCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, bool updateAux, bool updateMain)
{
const int xpixel = x*14;
const int xpixel = x * 14;
uint8_t *pAux = MemGetAuxPtr(addr);
uint8_t *pMain = MemGetMainPtr(addr);
uint8_t* pAux = MemGetAuxPtr(addr);
uint8_t* pMain = MemGetMainPtr(addr);
BYTE byteval1 = (x > 0) ? *(pMain-1) : 0;
BYTE byteval1 = (x > 0) ? *(pMain - 1) : 0;
BYTE byteval2 = *pAux;
BYTE byteval3 = *pMain;
BYTE byteval4 = (x < 39) ? *(pAux+1) : 0;
BYTE byteval4 = (x < 39) ? *(pAux + 1) : 0;
DWORD dwordval = (byteval1 & 0x70) | ((byteval2 & 0x7F) << 7) |
((byteval3 & 0x7F) << 14) | ((byteval4 & 0x07) << 21);
DWORD dwordval = (byteval1 & 0x70) | ((byteval2 & 0x7F) << 7) |
((byteval3 & 0x7F) << 14) | ((byteval4 & 0x07) << 21);
#define PIXEL 0
if (updateAux)
{
CopySource(7,2, SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR, LOBYTE(VALUE), pVideoAddress);
CopySource(7, 2, SRCOFFS_DHIRES + 10 * HIBYTE(VALUE) + COLOR, LOBYTE(VALUE), pVideoAddress);
pVideoAddress += 7;
}
#undef PIXEL
@ -595,11 +637,323 @@ void UpdateDHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress, bool
#define PIXEL 7
if (updateMain)
{
CopySource(7,2, SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR, LOBYTE(VALUE), pVideoAddress);
CopySource(7, 2, SRCOFFS_DHIRES + 10 * HIBYTE(VALUE) + COLOR, LOBYTE(VALUE), pVideoAddress);
}
#undef PIXEL
}
//===========================================================================
// RGB videocards HGR
void UpdateHiResRGBCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress)
{
const int xpixel = x * 14;
int xoffset = x & 1; // offset to start of the 2 bytes
addr -= xoffset;
uint8_t* pMain = MemGetMainPtr(addr);
// We need all 28 bits because each pixel needs a three bit evaluation
uint8_t byteval1 = (x < 2 ? 0 : *(pMain - 1));
uint8_t byteval2 = *pMain;
uint8_t byteval3 = *(pMain + 1);
uint8_t byteval4 = (x >= 38 ? 0 : *(pMain + 2));
// all 28 bits chained
DWORD dwordval = (byteval1 & 0x7F) | ((byteval2 & 0x7F) << 7) | ((byteval3 & 0x7F) << 14) | ((byteval4 & 0x7F) << 21);
// Extraction of 14 color pixels
UINT32 colors[14];
int color = 0;
DWORD dwordval_tmp = dwordval;
dwordval_tmp = dwordval_tmp >> 7;
bool offset = (byteval2 & 0x80);
for (int i = 0; i < 14; i++)
{
if (i == 7) offset = (byteval3 & 0x80);
color = dwordval_tmp & 0x3;
// Two cases because AppleWin's palette is in a strange order
if (offset)
colors[i] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[1 + color]);
else
colors[i] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[6 - color]);
if (i%2) dwordval_tmp >>= 2;
}
// Black and White
UINT32 bw[2];
bw[0] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[0]);
bw[1] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[1]);
DWORD mask = 0x01C0; // 00|000001 1|1000000
DWORD chck1 = 0x0140; // 00|000001 0|1000000
DWORD chck2 = 0x0080; // 00|000000 1|0000000
// HIRES render in RGB works on a pixel-basis (1-bit data in framebuffer)
// The pixel can be 'color', if it makes a 101 or 010 pattern with the two neighbour bits
// In all other cases, it's black if 0 and white if 1
// The value of 'color' is defined on a 2-bits basis
UINT32* pDst = (UINT32*)pVideoAddress;
if (xoffset)
{
// Second byte of the 14 pixels block
dwordval = dwordval >> 7;
xoffset = 7;
}
for (int i = xoffset; i < xoffset+7; i++)
{
if (((dwordval & mask) == chck1) || ((dwordval & mask) == chck2))
{
// Color pixel
*(pDst) = colors[i];
*(pDst + 1) = *(pDst);
pDst += 2;
}
else
{
// B&W pixel
*(pDst) = bw[(dwordval & chck2 ? 1 : 0)];
*(pDst + 1) = *(pDst);
pDst += 2;
}
// Next pixel
dwordval = dwordval >> 1;
}
const bool bIsHalfScanLines = IsVideoStyle(VS_HALF_SCANLINES);
// Second line
UINT32* pSrc = (UINT32*)pVideoAddress;
pDst = pSrc - GetFrameBufferWidth();
if (bIsHalfScanLines)
{
// Scanlines
std::fill(pDst, pDst + 14, 0);
}
else
{
for (int i = 0; i < 14; i++)
*(pDst + i) = *(pSrc + i);
}
}
bool dhgr_lastcell_iscolor = true;
int dhgr_lastbit = 0;
void UpdateDHiResCellRGB(int x, int y, uint16_t addr, bgra_t* pVideoAddress, bool isMixMode, bool isBit7Inversed)
{
const int xpixel = x * 14;
int xoffset = x & 1; // offset to start of the 2 bytes
addr -= xoffset;
uint8_t* pAux = MemGetAuxPtr(addr);
uint8_t* pMain = MemGetMainPtr(addr);
// We need all 28 bits because one 4-bits pixel overlaps two 14-bits cells
uint8_t byteval1 = *pAux;
uint8_t byteval2 = *pMain;
uint8_t byteval3 = *(pAux + 1);
uint8_t byteval4 = *(pMain + 1);
// all 28 bits chained
DWORD dwordval = (byteval1 & 0x7F) | ((byteval2 & 0x7F) << 7) | ((byteval3 & 0x7F) << 14) | ((byteval4 & 0x7F) << 21);
// Extraction of 7 color pixels and 7x4 bits
int bits[7];
UINT32 colors[7];
int color = 0;
DWORD dwordval_tmp = dwordval;
for (int i = 0; i < 7; i++)
{
bits[i] = dwordval_tmp & 0xF;
color = ((bits[i] & 7) << 1) | ((bits[i] & 8) >> 3); // DHGR colors are rotated 1 bit to the right
colors[i] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[12 + color]);
dwordval_tmp >>= 4;
}
UINT32 bw[2];
bw[0] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[12 + 0]);
bw[1] = *reinterpret_cast<const UINT32*>(&g_pPaletteRGB[12 + 15]);
if (isBit7Inversed)
{
// Invert mixed mode detection
byteval1 = ~byteval1;
byteval2 = ~byteval2;
byteval3 = ~byteval3;
byteval4 = ~byteval4;
}
// RGB DHGR is quite a mess:
// Color mode is a real 140x192 RGB mode with no color fringe (ref. patent US4631692, "THE 140x192 VIDEO MODE")
// BW mode is a real 560x192 monochrome mode
// Mixed mode seems easy but has a few traps since it's based on 4-bits cells coded into 7-bits bytes:
// - Bit 7 of each byte defines the mode of the following 7 bits (BW or Color);
// - BW pixels are 1 bit wide, color pixels are usually 4 bits wide;
// - A color pixel can be less than 4 bits wide if it crosses a byte boundary and falls into a BW byte;
// - If a 4-bit cell of BW bits crosses a byte boundary and falls into a Color byte, then the last BW bit is repeated until the next color pixel starts.
//
// (Tested on Le Chat Mauve IIc adapter, which was made under patent of Video-7)
UINT32* pDst = (UINT32*)pVideoAddress;
if (xoffset == 0) // First cell
{
if ((byteval1 & 0x80) || !isMixMode)
{
// Color
// Color cell 0
*(pDst++) = colors[0];
*(pDst++) = colors[0];
*(pDst++) = colors[0];
*(pDst++) = colors[0];
// Color cell 1
*(pDst++) = colors[1];
*(pDst++) = colors[1];
*(pDst++) = colors[1];
dwordval >>= 7;
dhgr_lastcell_iscolor = true;
}
else
{
// BW
for (int i = 0; i < 7; i++)
{
dhgr_lastbit = dwordval & 1;
*(pDst++) = bw[dhgr_lastbit];
dwordval >>= 1;
}
dhgr_lastcell_iscolor = false;
}
if ((byteval2 & 0x80) || !isMixMode)
{
// Remaining of color cell 1
if (dhgr_lastcell_iscolor)
{
*(pDst++) = colors[1];
}
else
{
// Repeat last BW bit once
*(pDst++) = bw[dhgr_lastbit];
}
// Color cell 2
*(pDst++) = colors[2];
*(pDst++) = colors[2];
*(pDst++) = colors[2];
*(pDst++) = colors[2];
// Color cell 3
*(pDst++) = colors[3];
*(pDst++) = colors[3];
dhgr_lastcell_iscolor = true;
}
else
{
for (int i = 0; i < 7; i++)
{
dhgr_lastbit = dwordval & 1;
*(pDst++) = bw[dhgr_lastbit];
dwordval >>= 1;
}
dhgr_lastcell_iscolor = false;
}
}
else // Second cell
{
dwordval >>= 14;
if ((byteval3 & 0x80) || !isMixMode)
{
// Remaining of color cell 3
if (dhgr_lastcell_iscolor)
{
*(pDst++) = colors[3];
*(pDst++) = colors[3];
}
else
{
// Repeat last BW bit twice
*(pDst++) = bw[dhgr_lastbit];
*(pDst++) = bw[dhgr_lastbit];
}
// Color cell 4
*(pDst++) = colors[4];
*(pDst++) = colors[4];
*(pDst++) = colors[4];
*(pDst++) = colors[4];
// Color cell 5
*(pDst++) = colors[5];
dwordval >>= 7;
dhgr_lastcell_iscolor = true;
}
else
{
for (int i = 0; i < 7; i++)
{
dhgr_lastbit = dwordval & 1;
*(pDst++) = bw[dhgr_lastbit];
dwordval >>= 1;
}
dhgr_lastcell_iscolor = false;
}
if ((byteval4 & 0x80) || !isMixMode)
{
// Remaining of color cell 5
if (dhgr_lastcell_iscolor)
{
*(pDst++) = colors[5];
*(pDst++) = colors[5];
*(pDst++) = colors[5];
}
else
{
// Repeat last BW bit three times
*(pDst++) = bw[dhgr_lastbit];
*(pDst++) = bw[dhgr_lastbit];
*(pDst++) = bw[dhgr_lastbit];
}
// Color cell 6
*(pDst++) = colors[6];
*(pDst++) = colors[6];
*(pDst++) = colors[6];
*(pDst++) = colors[6];
dhgr_lastcell_iscolor = true;
}
else
{
for (int i = 0; i < 7; i++)
{
dhgr_lastbit = dwordval & 1;
*(pDst++) = bw[dhgr_lastbit];
dwordval >>= 1;
}
dhgr_lastcell_iscolor = false;
}
}
const bool bIsHalfScanLines = IsVideoStyle(VS_HALF_SCANLINES);
// Second line
UINT32* pSrc = (UINT32*)pVideoAddress ;
pDst = pSrc - GetFrameBufferWidth();
if (bIsHalfScanLines)
{
// Scanlines
std::fill(pDst, pDst + 14, 0);
}
else
{
for (int i = 0; i < 14; i++)
*(pDst + i) = *(pSrc + i);
}
}
#if 1
// Squash the 640 pixel image into 560 pixels
int UpdateDHiRes160Cell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)
@ -708,7 +1062,7 @@ void UpdateDLoResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)
//===========================================================================
// Color TEXT (some RGB cards only)
// Default BG and FG are usually defined by hardware switches, defaults to black/white
void UpdateText40ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits)
void UpdateText40ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits, uint8_t character)
{
uint8_t foreground = g_nRegularTextFG;
uint8_t background = g_nRegularTextBG;
@ -718,13 +1072,25 @@ void UpdateText40ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, u
foreground = val >> 4;
background = val & 0x0F;
}
else if (g_RGBVideocard == RGB_Videocard_e::Video7_SL7 && character < 0x80)
{
// in regular 40COL mode, the SL7 videocard renders inverse characters as B&W
foreground = 15;
background = 0;
}
UpdateDuochromeCell(2, 14, pVideoAddress, bits, foreground, background);
}
void UpdateText80ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits)
void UpdateText80ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits, uint8_t character)
{
UpdateDuochromeCell(2, 7, pVideoAddress, bits, g_nRegularTextFG, g_nRegularTextBG);
if (g_RGBVideocard == RGB_Videocard_e::Video7_SL7 && character < 0x80)
{
// in all 80COL modes, the SL7 videocard renders inverse characters as B&W
UpdateDuochromeCell(2, 7, pVideoAddress, bits, 15, 0);
}
else
UpdateDuochromeCell(2, 7, pVideoAddress, bits, g_nRegularTextFG, g_nRegularTextBG);
}
//===========================================================================
@ -755,8 +1121,8 @@ void UpdateDuochromeCell(int h, int w, bgra_t* pVideoAddress, uint8_t bits, uint
background += 12;
foreground += 12;
// get bg/fg colors
colors[0] = PalIndex2RGB[background];
colors[1] = PalIndex2RGB[foreground];
colors[0] = g_pPaletteRGB[background];
colors[1] = g_pPaletteRGB[foreground];
int nbits = bits;
int doublepixels = (w == 14); // Double pixel (HiRes or Text40)
@ -815,11 +1181,24 @@ void VideoInitializeOriginal(baseColors_t pBaseNtscColors)
// CREATE THE SOURCE IMAGE AND DRAW INTO THE SOURCE BIT BUFFER
V_CreateDIBSections();
memcpy(&PalIndex2RGB[BLACK], *pBaseNtscColors, sizeof(RGBQUAD)*kNumBaseColors);
PalIndex2RGB[HGR_BLUE] = PalIndex2RGB[BLUE];
PalIndex2RGB[HGR_ORANGE] = PalIndex2RGB[ORANGE];
PalIndex2RGB[HGR_GREEN] = PalIndex2RGB[GREEN];
PalIndex2RGB[HGR_VIOLET] = PalIndex2RGB[MAGENTA];
// Replace the default palette with true NTSC-generated colors
memcpy(&PaletteRGB_NTSC[BLACK], *pBaseNtscColors, sizeof(RGBQUAD) * kNumBaseColors);
PaletteRGB_NTSC[HGR_BLUE] = PaletteRGB_NTSC[BLUE];
PaletteRGB_NTSC[HGR_ORANGE] = PaletteRGB_NTSC[ORANGE];
PaletteRGB_NTSC[HGR_GREEN] = PaletteRGB_NTSC[GREEN];
PaletteRGB_NTSC[HGR_VIOLET] = PaletteRGB_NTSC[MAGENTA];
}
//===========================================================================
// RGB videocards may use a different palette thant the NTSC-generated one
void VideoSwitchVideocardPalette(RGB_Videocard_e videocard, VideoType_e type)
{
g_pPaletteRGB = PaletteRGB_NTSC;
if (type==VideoType_e::VT_COLOR_VIDEOCARD_RGB && videocard == RGB_Videocard_e::LeChatMauve_Feline)
{
g_pPaletteRGB = PaletteRGB_Feline;
}
}
//===========================================================================
@ -837,13 +1216,14 @@ static bool g_rgbInvertBit7 = false;
// . NB. There's a final 5th AN3 transition to set DHGR mode
void RGB_SetVideoMode(WORD address)
{
if ((address&~1) == 0x0C) // 0x0C or 0x0D? (80COL)
if ((address & ~1) == 0x0C) // 0x0C or 0x0D? (80COL)
{
g_rgbSet80COL = true;
return;
}
if ((address&~1) != 0x5E) // 0x5E or 0x5F? (DHIRES)
if ((address & ~1) != 0x5E) // 0x5E or 0x5F? (DHIRES)
return;
// Precondition before toggling AN3:
@ -853,33 +1233,37 @@ void RGB_SetVideoMode(WORD address)
// . Apple II desktop sets DHGR B&W mode with HIRES off! (GH#631)
// Maybe there is no video-mode precondition?
// . After setting 80COL on/off then need a 0x5E->0x5F toggle. So if we see a 0x5F then reset (GH#633)
if ((g_uVideoMode & VF_MIXED) || (g_rgbSet80COL && address == 0x5F))
{
g_rgbMode = 0;
g_rgbPrevAN3Addr = 0;
g_rgbSet80COL = false;
return;
}
if (address == 0x5F && g_rgbPrevAN3Addr == 0x5E) // Check for AN3 clock transition
// From Video7 patent and Le Chat Mauve manuals (under patent of Video7), no precondition is needed.
// In Prince of Persia, in the game demo, the RGB card switches to BW DHIRES after the HGR animation with Jaffar.
// It's actually the same on real hardware (tested on IIc RGB adapter).
if (address == 0x5F)
{
g_rgbFlags = (g_rgbFlags<<1) & 3;
g_rgbFlags |= ((g_uVideoMode & VF_80COL) ? 0 : 1); // clock in !80COL
g_rgbMode = g_rgbFlags; // latch F2,F1
if ((g_rgbPrevAN3Addr == 0x5E) && g_rgbSet80COL)
{
g_rgbFlags = (g_rgbFlags << 1) & 3;
g_rgbFlags |= ((g_uVideoMode & VF_80COL) ? 0 : 1); // clock in !80COL
g_rgbMode = g_rgbFlags; // latch F2,F1
}
g_rgbSet80COL = false;
}
g_rgbPrevAN3Addr = address;
g_rgbSet80COL = false;
}
bool RGB_Is140Mode(void) // Extended 80-Column Text/AppleColor Card's Mode 2
{
return g_rgbMode == 0;
// Feline falls back to this mode instead of 160
return g_rgbMode == 0 || (g_RGBVideocard == RGB_Videocard_e::LeChatMauve_Feline && g_rgbMode == 1);
}
bool RGB_Is160Mode(void) // Extended 80-Column Text/AppleColor Card: N/A
{
return g_rgbMode == 1;
// Unsupported by Feline
return g_rgbMode == 1 && (g_RGBVideocard != RGB_Videocard_e::LeChatMauve_Feline);
}
bool RGB_IsMixMode(void) // Extended 80-Column Text/AppleColor Card's Mode 3

View file

@ -10,18 +10,21 @@ enum RGB_Videocard_e
void UpdateHiResCell(int x, int y, uint16_t addr, bgra_t *pVideoAddress);
void UpdateDHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress, bool updateAux, bool updateMain);
void UpdateDHiResCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, bool updateAux, bool updateMain);
void UpdateDHiResCellRGB(int x, int y, uint16_t addr, bgra_t* pVideoAddress, bool isMixMode, bool isBit7Inversed);
int UpdateDHiRes160Cell (int x, int y, uint16_t addr, bgra_t *pVideoAddress);
void UpdateLoResCell(int x, int y, uint16_t addr, bgra_t *pVideoAddress);
void UpdateDLoResCell(int x, int y, uint16_t addr, bgra_t *pVideoAddress);
void UpdateText40ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits);
void UpdateText80ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits);
void UpdateText40ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits, uint8_t character);
void UpdateText80ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits, uint8_t character);
void UpdateHiResDuochromeCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress);
void UpdateDuochromeCell(int h, int w, bgra_t* pVideoAddress, uint8_t bits, uint8_t foreground, uint8_t background);
void UpdateHiResRGBCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress);
const UINT kNumBaseColors = 16;
typedef bgra_t (*baseColors_t)[kNumBaseColors];
void VideoInitializeOriginal(baseColors_t pBaseNtscColors);
void VideoSwitchVideocardPalette(RGB_Videocard_e videocard, VideoType_e type);
void RGB_SetVideoMode(WORD address);
bool RGB_Is140Mode(void);

View file

@ -101,8 +101,9 @@ static LPDIRECTDRAW g_lpDD = NULL;
// NOTE: KEEP IN SYNC: VideoType_e g_aVideoChoices g_apVideoModeDesc
TCHAR g_aVideoChoices[] =
TEXT("Monochrome (Custom)\0")
TEXT("Color (RGB Monitor)\0")
TEXT("Color (NTSC Monitor)\0")
TEXT("Color (Composite Idealized)\0") // newly added
TEXT("Color (RGB Card/Monitor)\0") // was "Color (RGB Monitor)"
TEXT("Color (Composite Monitor)\0") // was "Color (NTSC Monitor)"
TEXT("Color TV\0")
TEXT("B&W TV\0")
TEXT("Monochrome (Amber)\0")
@ -114,14 +115,15 @@ static LPDIRECTDRAW g_lpDD = NULL;
// The window title will be set to this.
const char *g_apVideoModeDesc[ NUM_VIDEO_MODES ] =
{
"Monochrome Monitor (Custom)"
, "Color (RGB Monitor)"
, "Color (NTSC/PAL Monitor)"
"Monochrome (Custom)"
, "Color (Composite Idealized)"
, "Color (RGB Card/Monitor)"
, "Color (Composite Monitor)"
, "Color TV"
, "B&W TV"
, "Amber Monitor"
, "Green Monitor"
, "White Monitor"
, "Monochrome (Amber)"
, "Monochrome (Green)"
, "Monochrome (White)"
};
// Prototypes (Private) _____________________________________________
@ -615,6 +617,7 @@ void VideoReinitialize (bool bInitVideoScannerAddress /*= true*/)
NTSC_SetVideoStyle();
NTSC_SetVideoTextMode( g_uVideoMode & VF_80COL ? 80 : 40 );
NTSC_SetVideoMode( g_uVideoMode ); // Pre-condition: g_nVideoClockHorz (derived from g_dwCyclesThisFrame)
VideoSwitchVideocardPalette(RGB_GetVideocard(), GetVideoType());
}
//===========================================================================

View file

@ -8,6 +8,7 @@
{
VT_MONO_CUSTOM
, VT_COLOR_MONITOR_RGB // Color rendering from AppleWin 1.25 (GH#357)
, VT_COLOR_VIDEOCARD_RGB // Real RGB card rendering
, VT_COLOR_MONITOR_NTSC // NTSC or PAL
, VT_COLOR_TV
, VT_MONO_TV