NTSC: Per-line rendering (WIP) - TEXT40 and speed test

This commit is contained in:
tomcw
2017-04-14 20:33:42 +01:00
parent 0a29adacc2
commit dd36db0f7b
3 changed files with 304 additions and 6 deletions

View File

@@ -257,7 +257,10 @@ static void ContinueExecution(void)
if (g_bFullSpeed) if (g_bFullSpeed)
{ {
if (!bWasFullSpeed) if (!bWasFullSpeed)
{
VideoRedrawScreenDuringFullSpeed(0, true); // Init for full-speed mode VideoRedrawScreenDuringFullSpeed(0, true); // Init for full-speed mode
NTSC_SetFullSpeedEvent(true);
}
// Don't call Spkr_Mute() - will get speaker clicks // Don't call Spkr_Mute() - will get speaker clicks
MB_Mute(); MB_Mute();
@@ -275,7 +278,10 @@ static void ContinueExecution(void)
else else
{ {
if (bWasFullSpeed) if (bWasFullSpeed)
{
VideoRedrawScreenAfterFullSpeed(g_dwCyclesThisFrame); VideoRedrawScreenAfterFullSpeed(g_dwCyclesThisFrame);
NTSC_SetFullSpeedEvent(false);
}
// Don't call Spkr_Demute() // Don't call Spkr_Demute()
MB_Demute(); MB_Demute();

View File

@@ -137,10 +137,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
static unsigned (*g_pHorzClockOffset)[VIDEO_SCANNER_MAX_HORZ] = 0; static unsigned (*g_pHorzClockOffset)[VIDEO_SCANNER_MAX_HORZ] = 0;
typedef void (*UpdateScreenFunc_t)(long); typedef void (*UpdateScreenFunc_t)(long);
static UpdateScreenFunc_t g_apFuncVideoUpdateScanline[VIDEO_SCANNER_Y_DISPLAY];
static UpdateScreenFunc_t g_pFuncUpdateTextScreen = 0; // updateScreenText40; static UpdateScreenFunc_t g_pFuncUpdateTextScreen = 0; // updateScreenText40;
static UpdateScreenFunc_t g_pFuncUpdateGraphicsScreen = 0; // updateScreenText40; static UpdateScreenFunc_t g_pFuncUpdateGraphicsScreen = 0; // updateScreenText40;
static UpdateScreenFunc_t g_pFuncModeSwitchDelayed = 0; typedef void (*UpdateScreenFunc2_t)(UINT, BYTE*);
static UpdateScreenFunc2_t g_pFuncUpdateTextScreen2 = 0; // updateScreenText40;
static UpdateScreenFunc2_t g_pFuncUpdateGraphicsScreen2 = 0; // updateScreenText40;
typedef void (*UpdatePixelFunc_t)(uint16_t); typedef void (*UpdatePixelFunc_t)(uint16_t);
static UpdatePixelFunc_t g_pFuncUpdateBnWPixel = 0; //updatePixelBnWMonitorSingleScanline; static UpdatePixelFunc_t g_pFuncUpdateBnWPixel = 0; //updatePixelBnWMonitorSingleScanline;
@@ -408,6 +409,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
INLINE void updateFramebufferMonitorDoubleScanline( uint16_t signal, bgra_t *pTable ); INLINE void updateFramebufferMonitorDoubleScanline( uint16_t signal, bgra_t *pTable );
INLINE void updatePixels( uint16_t bits ); INLINE void updatePixels( uint16_t bits );
INLINE void updateVideoScannerHorzEOL(); INLINE void updateVideoScannerHorzEOL();
INLINE void updateVideoScannerHorzEOL2();
INLINE void updateVideoScannerAddress(); INLINE void updateVideoScannerAddress();
INLINE uint16_t updateVideoScannerAddressTXT(); INLINE uint16_t updateVideoScannerAddressTXT();
INLINE uint16_t updateVideoScannerAddressHGR(); INLINE uint16_t updateVideoScannerAddressHGR();
@@ -438,6 +440,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
static void updateScreenText40 ( long cycles6502 ); static void updateScreenText40 ( long cycles6502 );
static void updateScreenText80 ( long cycles6502 ); static void updateScreenText80 ( long cycles6502 );
static void InitVideoLineItem(void);
//=========================================================================== //===========================================================================
static void set_csbits() static void set_csbits()
{ {
@@ -753,6 +757,43 @@ inline void updateVideoScannerHorzEOL()
} }
} }
inline void updateVideoScannerHorzEOL2()
{
if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if (g_nColorBurstPixels < 2)
{
// NOTE: This writes out-of-bounds for a 560x384 framebuffer
g_pFuncUpdateBnWPixel(0);
g_pFuncUpdateBnWPixel(0);
g_pFuncUpdateBnWPixel(0);
g_pFuncUpdateBnWPixel(0);
}
else
{
// NOTE: This writes out-of-bounds for a 560x384 framebuffer
g_pFuncUpdateHuePixel(0);
g_pFuncUpdateHuePixel(0);
g_pFuncUpdateHuePixel(0);
g_pFuncUpdateHuePixel(g_nLastColumnPixelNTSC); // BUGFIX: ARCHON: green fringe on end of line
}
}
g_nVideoClockHorz = 0;
if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
{
g_nVideoClockVert = 0;
updateFlashRate();
}
if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
updateVideoScannerAddress();
}
}
//=========================================================================== //===========================================================================
inline void updateVideoScannerAddress() inline void updateVideoScannerAddress()
{ {
@@ -1361,6 +1402,22 @@ void updateScreenText40 (long cycles6502)
} }
} }
void updateScreenText40_2(UINT cycles6502, BYTE* pVideoData)
{
for (; cycles6502 > 0; --cycles6502)
{
uint8_t m = *pVideoData;
pVideoData += 2;
uint8_t c = getCharSetBits(m);
uint16_t bits = g_aPixelDoubleMaskHGR[c & 0x7F]; // Optimization: hgrbits second 128 entries are mirror of first 128
if (0 == g_nVideoCharSet && 0x40 == (m & 0xC0)) // Flash only if mousetext not active
bits ^= g_nTextFlashMask;
updatePixels( bits );
}
}
//=========================================================================== //===========================================================================
void updateScreenText80 (long cycles6502) void updateScreenText80 (long cycles6502)
{ {
@@ -1470,8 +1527,6 @@ void NTSC_SetVideoTextMode( int cols )
//=========================================================================== //===========================================================================
void NTSC_SetVideoMode( uint32_t uVideoModeFlags ) void NTSC_SetVideoMode( uint32_t uVideoModeFlags )
{ {
int h = g_nVideoClockHorz;
g_nVideoMixed = uVideoModeFlags & VF_MIXED; g_nVideoMixed = uVideoModeFlags & VF_MIXED;
g_nVideoCharSet = VideoGetSWAltCharSet() ? 1 : 0; g_nVideoCharSet = VideoGetSWAltCharSet() ? 1 : 0;
@@ -1510,6 +1565,8 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags )
else else
g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40; g_pFuncUpdateGraphicsScreen = updateScreenSingleLores40;
} }
g_pFuncUpdateGraphicsScreen2 = updateScreenText40_2;
} }
//=========================================================================== //===========================================================================
@@ -1610,6 +1667,9 @@ _mono:
} }
//=========================================================================== //===========================================================================
void NTSC_SpeedTest(void);
void NTSC_VideoInit( uint8_t* pFramebuffer ) // wsVideoInit void NTSC_VideoInit( uint8_t* pFramebuffer ) // wsVideoInit
{ {
make_csbits(); make_csbits();
@@ -1627,6 +1687,8 @@ void NTSC_VideoInit( uint8_t* pFramebuffer ) // wsVideoInit
VideoReinitialize(); // Setup g_pFunc_ntsc*Pixel() VideoReinitialize(); // Setup g_pFunc_ntsc*Pixel()
NTSC_SpeedTest();
#if HGR_TEST_PATTERN #if HGR_TEST_PATTERN
// Init HGR to almost all-possible-combinations // Init HGR to almost all-possible-combinations
// CALL-151 // CALL-151
@@ -1692,6 +1754,8 @@ void NTSC_VideoReinitialize( DWORD cyclesThisFrame )
g_nVideoClockHorz = cyclesThisFrame % VIDEO_SCANNER_MAX_HORZ; g_nVideoClockHorz = cyclesThisFrame % VIDEO_SCANNER_MAX_HORZ;
updateVideoScannerAddress(); // Pre-condition: g_nVideoClockVert updateVideoScannerAddress(); // Pre-condition: g_nVideoClockVert
InitVideoLineItem();
} }
//=========================================================================== //===========================================================================
@@ -1765,12 +1829,175 @@ static void VideoUpdateCycles( int cyclesLeftToUpdate )
g_pFuncUpdateGraphicsScreen(cyclesLeftToUpdate); g_pFuncUpdateGraphicsScreen(cyclesLeftToUpdate);
} }
//===========================================================================
//const UINT kVideoMode_Invalid = (UINT)-1;
struct VideoLineData
{
BYTE dataMain;
BYTE dataAux;
};
struct VideoLineItem
{
UINT videoMode;
int videoCharSet;
UINT numCycles;
UpdateScreenFunc2_t func;
VideoLineData data[1]; // variable length
};
static BYTE g_videoLine[sizeof(VideoLineItem)*40] = {0xff}; // 40 display bytes (NB. Init to 0xff so that videoMode is invalid)
static VideoLineItem* g_pVideoLineItem = NULL;
static VideoLineData* g_pVideoLineNextData = NULL;
static bool g_bDontRenderVideoLine = false;
// FullSpeed start/end causes problems. Two cases:
// 1) FullSpeed starts after VIDEO_SCANNER_HORZ_START, so we have a partial g_videoLine, eg. cycles [0..20], then FS starts at cycle-21
// -> When FS starts, then just reset g_videoLine
// 2) FullSpeed ends after VIDEO_SCANNER_HORZ_START, so InitVideoLineItem() won't have been called, eg. FS ends at cycle-14, then cycles [15..39]
// -> When FS ends , then set a flag not VideoRenderLine() for this partial scanline
void NTSC_SetFullSpeedEvent(bool bStartFullSpeed)
{
if (bStartFullSpeed) // Just started full-speed mode
InitVideoLineItem(); // - so ditch any partial items for this scanline
else // Just ended full-speed mode
g_bDontRenderVideoLine = true; // - so signal not to VideoRenderLine() at end of this current scanline
}
static void SetVideoLineItem(void)
{
g_pVideoLineItem->videoMode = g_uVideoMode;
g_pVideoLineItem->videoCharSet = g_nVideoCharSet;
g_pVideoLineItem->numCycles = 0;
g_pVideoLineItem->func = g_pFuncUpdateGraphicsScreen2;
}
static void InitVideoLineItem(void)
{
g_pVideoLineItem = (VideoLineItem*) g_videoLine;
g_pVideoLineNextData = &g_pVideoLineItem->data[0];
SetVideoLineItem();
g_bDontRenderVideoLine = false;
}
static void NextVideoLineItem(void)
{
g_pVideoLineItem = (VideoLineItem*) g_pVideoLineNextData;
g_pVideoLineNextData = &g_pVideoLineItem->data[0];
SetVideoLineItem();
}
static void VideoRenderLine( void )
{
int currCharSet = g_nVideoCharSet;
VideoLineItem* pItem = (VideoLineItem*) &g_videoLine;
UINT cycles = 0;
int v190 = 0;
while (cycles < 40)
{
// if (pItem->videoMode == kVideoMode_Invalid) // Eg. FullSpeed off
// break;
if (g_nVideoClockVert == 190)
v190++;
g_nVideoCharSet = pItem->videoCharSet;
pItem->func( pItem->numCycles, (BYTE*)&pItem->data[0] );
cycles += pItem->numCycles;
pItem = (VideoLineItem*) ((BYTE*)&pItem->data[0] + sizeof(VideoLineData) * pItem->numCycles);
}
g_nVideoCharSet = currCharSet;
}
static void VideoBuildLine( int cycles )
{
bool bHires = (g_uVideoMode & VF_HIRES) && !(g_uVideoMode & VF_TEXT); // SW_HIRES && !SW_TEXT
if (g_nVideoClockVert > VIDEO_SCANNER_Y_MIXED && g_uVideoMode & VF_MIXED)
bHires = false;
if (g_nVideoClockHorz < VIDEO_SCANNER_HORZ_COLORBURST_BEG && (g_nVideoClockHorz + cycles) > VIDEO_SCANNER_HORZ_COLORBURST_BEG)
{
// TODO: Need better logic for cases when cycles =~1000 (multi-line) and cycles = 17030 (whole screen)
if (bHires)
{
g_nColorBurstPixels = 1024;
}
else
{
const UINT attentuateMaxCycles = VIDEO_SCANNER_HORZ_COLORBURST_END-VIDEO_SCANNER_HORZ_COLORBURST_BEG;
UINT attentuateCycles = (g_nVideoClockHorz + cycles) - VIDEO_SCANNER_HORZ_COLORBURST_BEG;
if (attentuateCycles > attentuateMaxCycles)
attentuateCycles = attentuateMaxCycles;
g_nColorBurstPixels -= attentuateCycles;
if (g_nColorBurstPixels < 0)
g_nColorBurstPixels = 0;
}
}
//
if (g_nVideoClockHorz > VIDEO_SCANNER_HORZ_START)
{
if (g_pVideoLineItem->videoMode != g_uVideoMode || g_pVideoLineItem->videoCharSet != g_nVideoCharSet)
NextVideoLineItem();
}
BYTE *pMain = NULL, *pAux = NULL;
while (cycles--)
{
if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START && g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
if (g_nVideoClockHorz == VIDEO_SCANNER_HORZ_START || pMain == NULL)
{
if (g_nVideoClockHorz == VIDEO_SCANNER_HORZ_START)
InitVideoLineItem();
uint16_t addr = bHires ? updateVideoScannerAddressHGR() : updateVideoScannerAddressTXT();
pMain = MemGetMainPtr(addr);
pAux = MemGetAuxPtr(addr); // TODO: Support APPLE2 types with no aux mem
}
g_pVideoLineItem->numCycles++;
g_pVideoLineNextData->dataMain = *pMain++;
g_pVideoLineNextData->dataAux = *pAux++;
g_pVideoLineNextData++;
}
g_nVideoClockHorz++;
if (g_nVideoClockHorz == VIDEO_SCANNER_MAX_HORZ)
{
if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY)
{
// *(UINT*)g_pVideoLineNextData = kVideoMode_Invalid; // End of data
if (!g_bDontRenderVideoLine)
VideoRenderLine();
}
updateVideoScannerHorzEOL2();
if (g_nVideoClockVert > VIDEO_SCANNER_Y_MIXED && g_uVideoMode & VF_MIXED)
bHires = false;
}
}
}
//=========================================================================== //===========================================================================
void NTSC_VideoUpdateCycles( long cycles6502 ) void NTSC_VideoUpdateCycles( long cycles6502 )
{ {
_ASSERT(cycles6502 < VIDEO_SCANNER_6502_CYCLES); // Use NTSC_VideoRedrawWholeScreen() instead _ASSERT(cycles6502 < VIDEO_SCANNER_6502_CYCLES); // Use NTSC_VideoRedrawWholeScreen() instead
VideoUpdateCycles(cycles6502); VideoBuildLine(cycles6502);
// VideoUpdateCycles(cycles6502);
} }
//=========================================================================== //===========================================================================
@@ -1781,7 +2008,7 @@ void NTSC_VideoRedrawWholeScreen( void )
const uint16_t currVideoClockHorz = g_nVideoClockHorz; const uint16_t currVideoClockHorz = g_nVideoClockHorz;
#endif #endif
VideoUpdateCycles(VIDEO_SCANNER_6502_CYCLES); // VideoUpdateCycles(VIDEO_SCANNER_6502_CYCLES);
#ifdef _DEBUG #ifdef _DEBUG
_ASSERT(currVideoClockVert == g_nVideoClockVert); _ASSERT(currVideoClockVert == g_nVideoClockVert);
@@ -1794,3 +2021,67 @@ bool NTSC_GetColorBurst( void )
{ {
return (g_nColorBurstPixels < 2) ? false : true; return (g_nColorBurstPixels < 2) ? false : true;
} }
//===========================================================================
void SpeedTest_Reset(void)
{
g_nVideoClockHorz = 0;
g_nVideoClockVert = 0;
NTSC_SetVideoMode( VF_TEXT );
LPBYTE pMain = MemGetMainPtr(0x400);
for (UINT i=0; i<0x3ff; i++)
pMain[i] = i&0xff;
}
void NTSC_SpeedTest(void)
{
const UINT kNumRepeats = 1000;
char szDbg[100];
// Test1
SpeedTest_Reset();
DWORD tStart1 = GetTickCount();
for (UINT n=0; n<kNumRepeats; n++)
{
int cyclesLeft = VIDEO_SCANNER_6502_CYCLES;
UINT cycles = 2;
while (cyclesLeft > 0)
{
VideoBuildLine(cycles);
cyclesLeft -= cycles;
cycles++;
if (cycles == 8)
cycles = 2;
}
}
DWORD tDuration1 = GetTickCount() - tStart1;
sprintf(szDbg, "Test1 time = %d ms\n", tDuration1);
OutputDebugString(szDbg);
//
// Test2
SpeedTest_Reset();
DWORD tStart2 = GetTickCount();
for (UINT n=0; n<kNumRepeats; n++)
{
int cyclesLeft = VIDEO_SCANNER_6502_CYCLES;
UINT cycles = 2;
while (cyclesLeft > 0)
{
VideoUpdateCycles(cycles);
cyclesLeft -= cycles;
cycles++;
if (cycles == 8)
cycles = 2;
}
}
DWORD tDuration2 = GetTickCount() - tStart2;
sprintf(szDbg, "Test2 time = %d ms\n", tDuration2);
OutputDebugString(szDbg);
}

View File

@@ -16,6 +16,7 @@
extern void NTSC_VideoReinitialize( DWORD cyclesThisFrame ); extern void NTSC_VideoReinitialize( DWORD cyclesThisFrame );
extern void NTSC_VideoInitAppleType(); extern void NTSC_VideoInitAppleType();
extern void NTSC_VideoInitChroma(); extern void NTSC_VideoInitChroma();
extern void NTSC_SetFullSpeedEvent( bool bStartFullSpeed );
extern void NTSC_VideoUpdateCycles( long cycles6502 ); extern void NTSC_VideoUpdateCycles( long cycles6502 );
extern void NTSC_VideoRedrawWholeScreen( void ); extern void NTSC_VideoRedrawWholeScreen( void );
extern bool NTSC_GetColorBurst( void ); extern bool NTSC_GetColorBurst( void );