Added support for RGB cards & Video7 SL7 RGB extra modes (#819) (PR #826)

Currently only Apple Color Card and Video7 SL7 are supported.

The RGB card can be defined through the command-line:
-rgb-card-type: apple (default), sl7, eve, feline

The default text color can be modified when SL7 is selected. On the actual card this was defined by dip switches.
-rgb-card-foreground <color>
can be 6 (blue), 9 (amber), 12 (green) and white (15)

Details:
. Added RGB FG/BG Text + mixed GR mode
. Added Color Text80
. Added Duochrome HGR
. RGB card defaults to Apple Color Card + B&W text
This commit is contained in:
Cyril Lambin 2020-09-01 22:32:44 +02:00 committed by GitHub
parent ef913fe827
commit 3e33d7f6d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 388 additions and 6 deletions

View File

@ -1333,6 +1333,9 @@ struct CmdLine
newVideoRefreshRate = VR_NONE;
clockMultiplier = 0.0; // 0 => not set from cmd-line
model = A2TYPE_MAX;
rgbCard = RGB_Videocard_e::Apple;
rgbCardForegroundColor = 15;
rgbCardBackgroundColor = 0;
for (UINT i = 0; i < NUM_SLOTS; i++)
{
@ -1367,6 +1370,9 @@ struct CmdLine
VideoRefreshRate_e newVideoRefreshRate;
double clockMultiplier;
eApple2Type model;
RGB_Videocard_e rgbCard;
int rgbCardForegroundColor;
int rgbCardBackgroundColor;
std::string strCurrentDir;
};
@ -1798,6 +1804,37 @@ static bool ProcessCmdLine(LPSTR lpCmdLine)
{
g_cmdLine.newVideoRefreshRate = VR_60HZ;
}
else if (strcmp(lpCmdLine, "-rgb-card-type") == 0)
{
// RGB video card valide types are: "apple", "sl7", "eve", "feline"
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "apple") == 0)
g_cmdLine.rgbCard = RGB_Videocard_e::Apple;
else if (strcmp(lpCmdLine, "sl7") == 0)
g_cmdLine.rgbCard = RGB_Videocard_e::Video7_SL7;
else if (strcmp(lpCmdLine, "eve") == 0)
g_cmdLine.rgbCard = RGB_Videocard_e::LeChatMauve_EVE;
else if (strcmp(lpCmdLine, "feline") == 0)
g_cmdLine.rgbCard = RGB_Videocard_e::LeChatMauve_Feline;
else
LogFileOutput("-rgb-card-type: unsupported type: %s\n", lpCmdLine);
}
else if (strcmp(lpCmdLine, "-rgb-card-foreground") == 0)
{
// Default hardware-defined Text foreground color, for some RGB cards only
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
g_cmdLine.rgbCardForegroundColor = atoi(lpCmdLine);
}
else if (strcmp(lpCmdLine, "-rgb-card-background") == 0)
{
// Default hardware-defined Text background color, for some RGB cards only
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
g_cmdLine.rgbCardBackgroundColor = atoi(lpCmdLine);
}
else if (strcmp(lpCmdLine, "-power-on") == 0)
{
g_cmdLine.bBoot = true;
@ -1944,6 +1981,8 @@ static void RepeatInitialization(void)
if (g_cmdLine.model != A2TYPE_MAX)
SetApple2Type(g_cmdLine.model);
RGB_SetVideocard(g_cmdLine.rgbCard, g_cmdLine.rgbCardForegroundColor, g_cmdLine.rgbCardBackgroundColor);
if (g_cmdLine.newVideoType >= 0)
{
SetVideoType( (VideoType_e)g_cmdLine.newVideoType );

View File

@ -441,6 +441,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
static void updateScreenSingleLores40( long cycles6502 );
static void updateScreenText40 ( long cycles6502 );
static void updateScreenText80 ( long cycles6502 );
static void updateScreenText40RGB ( long cycles6502 );
static void updateScreenText80RGB ( long cycles6502 );
//===========================================================================
static void set_csbits()
@ -826,7 +828,8 @@ inline void updateVideoScannerAddress()
if (((g_pFuncUpdateGraphicsScreen == updateScreenDoubleHires80) ||
(g_pFuncUpdateGraphicsScreen == updateScreenDoubleLores80) ||
(g_pFuncUpdateGraphicsScreen == updateScreenText80) ||
(g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED && g_pFuncUpdateTextScreen == 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_pVideoAddress -= 1;
@ -1482,6 +1485,35 @@ static void updateScreenSingleHires40Simplified (long cycles6502)
}
}
//===========================================================================
static void updateScreenSingleHires40Duochrome(long cycles6502)
{
if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED)
{
g_pFuncUpdateTextScreen(cycles6502);
return;
}
for (; cycles6502 > 0; --cycles6502)
{
if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if ((g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END) && (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_COLORBURST_BEG))
{
g_nColorBurstPixels = 1024;
}
else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint16_t addr = getVideoScannerAddressHGR();
UpdateHiResDuochromeCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress);
g_pVideoAddress += 14;
}
}
updateVideoScannerHorzEOLSimple();
}
}
void updateScreenSingleHires40 (long cycles6502)
{
if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED)
@ -1614,6 +1646,40 @@ void updateScreenText40 (long cycles6502)
}
}
//===========================================================================
void updateScreenText40RGB(long cycles6502)
{
for (; cycles6502 > 0; --cycles6502)
{
uint16_t addr = getVideoScannerAddressTXT();
if ((g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END) && (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_COLORBURST_BEG))
{
if (g_nColorBurstPixels > 0)
g_nColorBurstPixels -= 1;
}
else if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint8_t* pMain = MemGetMainPtr(addr);
uint8_t m = pMain[0];
uint8_t c = getCharSetBits(m);
if (0 == g_nVideoCharSet && 0x40 == (m & 0xC0)) // Flash only if mousetext not active
c ^= g_nTextFlashMask;
UpdateText40ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, c);
g_pVideoAddress += 14;
}
}
updateVideoScannerHorzEOLSimple();
}
}
//===========================================================================
void updateScreenText80 (long cycles6502)
{
@ -1657,6 +1723,51 @@ void updateScreenText80 (long cycles6502)
}
}
//===========================================================================
void updateScreenText80RGB(long cycles6502)
{
for (; cycles6502 > 0; --cycles6502)
{
uint16_t addr = getVideoScannerAddressTXT();
if ((g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_END) && (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_COLORBURST_BEG))
{
if (g_nColorBurstPixels > 0)
g_nColorBurstPixels -= 1;
}
else if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint8_t* pMain = MemGetMainPtr(addr);
uint8_t* pAux = MemGetAuxPtr(addr);
uint8_t m = pMain[0];
uint8_t a = pAux[0];
uint16_t main = getCharSetBits(m);
uint16_t aux = getCharSetBits(a);
if ((0 == g_nVideoCharSet) && 0x40 == (m & 0xC0)) // Flash only if mousetext not active
main ^= g_nTextFlashMask;
if ((0 == g_nVideoCharSet) && 0x40 == (a & 0xC0)) // Flash only if mousetext not active
aux ^= g_nTextFlashMask;
UpdateText80ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, (uint8_t)aux);
g_pVideoAddress += 7;
UpdateText80ColorCell(g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, (uint8_t)main);
g_pVideoAddress += 7;
uint16_t bits = (main << 7) | (aux & 0x7f);
g_nLastColumnPixelNTSC = (bits >> 14) & 1;
}
}
updateVideoScannerHorzEOL();
}
}
// Functions (Public) _____________________________________________________________________________
//===========================================================================
@ -1735,7 +1846,14 @@ uint16_t NTSC_VideoGetScannerAddressForDebugger(void)
//===========================================================================
void NTSC_SetVideoTextMode( int cols )
{
if( cols == 40 )
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
{
if (cols == 40)
g_pFuncUpdateTextScreen = updateScreenText40RGB;
else
g_pFuncUpdateTextScreen = updateScreenText80RGB;
}
else if( cols == 40 )
g_pFuncUpdateTextScreen = updateScreenText40;
else
g_pFuncUpdateTextScreen = updateScreenText80;
@ -1753,9 +1871,12 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
return;
}
g_nVideoMixed = uVideoModeFlags & VF_MIXED;
g_nVideoCharSet = VideoGetSWAltCharSet() ? 1 : 0;
RGB_DisableTextFB();
g_nTextPage = 1;
g_nHiresPage = 1;
if (uVideoModeFlags & VF_PAGE2)
@ -1777,7 +1898,8 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
// Switching mid-line from graphics to TEXT
if (g_eVideoType == VT_COLOR_MONITOR_NTSC &&
g_pFuncUpdateGraphicsScreen != updateScreenText40 && g_pFuncUpdateGraphicsScreen != updateScreenText80)
g_pFuncUpdateGraphicsScreen != updateScreenText40 && g_pFuncUpdateGraphicsScreen != updateScreenText40RGB
&& g_pFuncUpdateGraphicsScreen != updateScreenText80 && g_pFuncUpdateGraphicsScreen != updateScreenText80RGB)
{
*(uint32_t*)&g_pVideoAddress[0] = 0; // blank out any stale pixel data, eg. ANSI STORY (at end credits)
*(uint32_t*)&g_pVideoAddress[1] = 0;
@ -1790,17 +1912,77 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
// Switching mid-line from TEXT to graphics
if (g_eVideoType == VT_COLOR_MONITOR_NTSC &&
(g_pFuncUpdateGraphicsScreen == updateScreenText40 || g_pFuncUpdateGraphicsScreen == updateScreenText80))
(g_pFuncUpdateGraphicsScreen == updateScreenText40 || g_pFuncUpdateGraphicsScreen == updateScreenText40RGB
|| g_pFuncUpdateGraphicsScreen == updateScreenText80 || g_pFuncUpdateGraphicsScreen == updateScreenText80RGB))
{
g_pVideoAddress -= 2; // eg. FT's TRIBU demo & ANSI STORY (at "turn the disk over!")
}
}
}
if (uVideoModeFlags & VF_TEXT)
// Video7_SL7 extra RGB modes handling
if (g_eVideoType == VT_COLOR_MONITOR_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
// VF_DHIRES = 0 and VF_TEXT=0, VF_DHIRES=1, VF_80COL=1 -> DHIRES modes, setup by F1/F2
&& !(!(uVideoModeFlags & VF_DHIRES) ||
((uVideoModeFlags & VF_DHIRES) && !(uVideoModeFlags & VF_TEXT) && (uVideoModeFlags & VF_DHIRES) && (uVideoModeFlags & VF_80COL))
)
)
{
RGB_EnableTextFB(); // F/B text only shows in 40col mode anyway
// ----- Video-7 SL7 extra modes ----- (from the videocard manual)
// AN3 TEXT HIRES 80COL
// 0 1 ? 0 F/B Text
// 0 1 ? 1 80 col Text
// 0 0 0 0 LoRes (mixed with F/B Text)
// 0 0 0 1 DLoRes (mixed with 80 col. Text)
// 0 0 1 0 F/B HiRes (mixed with F/B Text)
if (uVideoModeFlags & VF_TEXT)
{
if (uVideoModeFlags & VF_80COL)
{
// 80 col text
g_pFuncUpdateGraphicsScreen = updateScreenText80RGB;
}
else
{
g_pFuncUpdateGraphicsScreen = updateScreenText40RGB;
}
}
else if (uVideoModeFlags & VF_HIRES)
{
// F/B HiRes
g_pFuncUpdateGraphicsScreen = updateScreenSingleHires40Duochrome;
g_pFuncUpdateTextScreen = updateScreenText40RGB;
}
else if (uVideoModeFlags & VF_80COL)
{
// DLoRes
g_pFuncUpdateGraphicsScreen = updateScreenDoubleLores80Simplified;
g_pFuncUpdateTextScreen = updateScreenText80RGB;
}
else
{
// LoRes + F/B Text
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40Simplified;
g_pFuncUpdateTextScreen = updateScreenText40RGB;
}
}
// Regular NTSC modes
else if (uVideoModeFlags & VF_TEXT)
{
if (uVideoModeFlags & VF_80COL)
g_pFuncUpdateGraphicsScreen = updateScreenText80;
{
if (g_eVideoType == VT_COLOR_MONITOR_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenText80RGB;
else
g_pFuncUpdateGraphicsScreen = updateScreenText80;
}
else if (g_eVideoType == VT_COLOR_MONITOR_RGB)
g_pFuncUpdateGraphicsScreen = updateScreenText40RGB;
else
g_pFuncUpdateGraphicsScreen = updateScreenText40;
}

View File

@ -8,6 +8,14 @@
#include "RGBMonitor.h"
#include "YamlHelper.h"
// RGB videocards types
static RGB_Videocard_e g_RGBVideocard = RGB_Videocard_e::Apple;
static int g_nTextFBMode = 0; // F/B Text
static int g_nRegularTextFG = 15; // Default TEXT color
static int g_nRegularTextBG = 0; // Default TEXT background color
const int HIRES_COLUMN_SUBUNIT_SIZE = 16;
const int HIRES_COLUMN_UNIT_SIZE = (HIRES_COLUMN_SUBUNIT_SIZE)*2;
const int HIRES_NUMBER_COLUMNS = (1<<5); // 5 bits
@ -697,6 +705,88 @@ 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)
{
uint8_t foreground = g_nRegularTextFG;
uint8_t background = g_nRegularTextBG;
if (g_nTextFBMode)
{
const BYTE val = *MemGetAuxPtr(addr); // RGB cards with F/B text use their own AUX memory!
foreground = val >> 4;
background = val & 0x0F;
}
UpdateDuochromeCell(2, 14, pVideoAddress, bits, foreground, background);
}
void UpdateText80ColorCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress, uint8_t bits)
{
UpdateDuochromeCell(2, 7, pVideoAddress, bits, g_nRegularTextFG, g_nRegularTextBG);
}
//===========================================================================
// Duochrome HGR (some RGB cards only)
void UpdateHiResDuochromeCell(int x, int y, uint16_t addr, bgra_t* pVideoAddress)
{
BYTE bits = *MemGetMainPtr(addr);
BYTE val = *MemGetAuxPtr(addr);
const uint8_t foreground = val >> 4;
const uint8_t background = val & 0x0F;
UpdateDuochromeCell(2, 14, pVideoAddress, bits, foreground, background);
}
//===========================================================================
// Writes a duochrome cell
// 7 bits define a foreground/background pattern
// Used on many RGB cards but activated differently, depending on the card.
// Can be used in TEXT or HGR mode. The foreground & background colors could be fixed by hardware switches or data lying in AUX.
void UpdateDuochromeCell(int h, int w, bgra_t* pVideoAddress, uint8_t bits, uint8_t foreground, uint8_t background)
{
UINT32* pDst = (UINT32*)pVideoAddress;
const bool bIsHalfScanLines = IsVideoStyle(VS_HALF_SCANLINES);
const UINT frameBufferWidth = GetFrameBufferWidth();
RGBQUAD colors[2];
// use LoRes palette
background += 12;
foreground += 12;
// get bg/fg colors
colors[0] = PalIndex2RGB[background];
colors[1] = PalIndex2RGB[foreground];
int nbits = bits;
int doublepixels = (w == 14); // Double pixel (HiRes or Text40)
while (h--)
{
bits = nbits;
if (bIsHalfScanLines && !(h & 1))
{
// 50% Half Scan Line clears every odd scanline (and SHIFT+PrintScreen saves only the even rows)
std::fill(pDst, pDst + w, 0);
}
else
{
for (int nBytes = 0; nBytes < w; nBytes += (doublepixels?2:1))
{
int bit = (bits & 1);
bits >>= 1;
const RGBQUAD& rRGB = colors[bit];
*(pDst + nBytes) = *reinterpret_cast<const UINT32*>(&rRGB);
if (doublepixels)
{
*(pDst + nBytes + 1) = *reinterpret_cast<const UINT32*>(&rRGB);
}
}
}
pDst -= frameBufferWidth;
}
}
//===========================================================================
static LPBYTE g_pSourcePixels = NULL;
@ -734,6 +824,7 @@ void VideoInitializeOriginal(baseColors_t pBaseNtscColors)
//===========================================================================
static UINT g_rgbFlags = 0;
static UINT g_rgbMode = 0;
static WORD g_rgbPrevAN3Addr = 0;
@ -857,3 +948,50 @@ void RGB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT cardVersion)
yamlLoadHelper.PopMap();
}
RGB_Videocard_e RGB_GetVideocard(void)
{
return g_RGBVideocard;
}
void RGB_SetVideocard(RGB_Videocard_e videocard, int text_foreground, int text_background)
{
g_RGBVideocard = videocard;
// black & white text
RGB_SetRegularTextFG(15);
RGB_SetRegularTextBG(0);
if (videocard == RGB_Videocard_e::Video7_SL7 &&
(text_foreground == 6 || text_foreground == 9 || text_foreground == 12 || text_foreground == 15))
{
// SL7: Only Blue, Amber (Orange), Green, White are supported by hardware switches
RGB_SetRegularTextFG(text_foreground);
RGB_SetRegularTextBG(0);
}
}
void RGB_SetRegularTextFG(int color)
{
g_nRegularTextFG = color;
}
void RGB_SetRegularTextBG(int color)
{
g_nRegularTextBG = color;
}
void RGB_EnableTextFB()
{
g_nTextFBMode = 1;
}
void RGB_DisableTextFB()
{
g_nTextFBMode = 0;
}
int RGB_IsTextFB()
{
return g_nTextFBMode;
}

View File

@ -1,8 +1,23 @@
// Handling of RGB videocards
enum RGB_Videocard_e
{
Apple,
Video7_SL7,
LeChatMauve_EVE,
LeChatMauve_Feline
};
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);
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 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);
const UINT kNumBaseColors = 16;
typedef bgra_t (*baseColors_t)[kNumBaseColors];
@ -19,3 +34,11 @@ void RGB_SetInvertBit7(bool state);
void RGB_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void RGB_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT cardVersion);
RGB_Videocard_e RGB_GetVideocard(void);
void RGB_SetVideocard(RGB_Videocard_e videocard, int text_foreground = -1, int text_background = -1);
void RGB_SetRegularTextFG(int color);
void RGB_SetRegularTextBG(int color);
void RGB_EnableTextFB();
void RGB_DisableTextFB();
int RGB_IsTextFB();