Update VirtualC64 to 1.4.2 created autoload& run functionality first attempt at working save states
748 lines
23 KiB
C++
748 lines
23 KiB
C++
//
|
|
// PixelEngine.cpp
|
|
/*
|
|
* (C) 2015 Dirk W. Hoffmann. All rights reserved.
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "C64.h"
|
|
|
|
// DIRK DEBUG, REMOVE ASAP
|
|
extern unsigned dirktrace;
|
|
extern unsigned dirkcnt;
|
|
|
|
|
|
PixelEngine::PixelEngine() // C64 *c64)
|
|
{
|
|
setDescription("PixelEngine");
|
|
|
|
debug(3, " Creating PixelEngine at address %p...\n", this);
|
|
|
|
currentScreenBuffer = screenBuffer1[0];
|
|
pixelBuffer = currentScreenBuffer;
|
|
bufferoffset = 0;
|
|
|
|
// Register snapshot items
|
|
SnapshotItem items[] = {
|
|
|
|
// VIC state latching
|
|
{ &pipe.xCounter, sizeof(pipe.xCounter), CLEAR_ON_RESET },
|
|
{ pipe.spriteX, sizeof(pipe.spriteX), CLEAR_ON_RESET | WORD_FORMAT },
|
|
{ &pipe.spriteXexpand, sizeof(pipe.spriteXexpand), CLEAR_ON_RESET },
|
|
{ &pipe.registerCTRL1, sizeof(pipe.registerCTRL1), CLEAR_ON_RESET },
|
|
{ &pipe.registerCTRL2, sizeof(pipe.registerCTRL2), CLEAR_ON_RESET },
|
|
{ &pipe.g_data, sizeof(pipe.g_data), CLEAR_ON_RESET },
|
|
{ &pipe.g_character, sizeof(pipe.g_character), CLEAR_ON_RESET },
|
|
{ &pipe.g_color, sizeof(pipe.g_color), CLEAR_ON_RESET },
|
|
{ &pipe.mainFrameFF, sizeof(pipe.mainFrameFF), CLEAR_ON_RESET },
|
|
{ &pipe.verticalFrameFF, sizeof(pipe.verticalFrameFF), CLEAR_ON_RESET },
|
|
|
|
{ &bpipe.borderColor, sizeof(bpipe.borderColor), CLEAR_ON_RESET },
|
|
{ cpipe.backgroundColor, sizeof(cpipe.backgroundColor), CLEAR_ON_RESET | BYTE_FORMAT },
|
|
{ spipe.spriteColor, sizeof(spipe.spriteColor), CLEAR_ON_RESET | BYTE_FORMAT },
|
|
{ &spipe.spriteExtraColor1, sizeof(spipe.spriteExtraColor1), CLEAR_ON_RESET },
|
|
{ &spipe.spriteExtraColor2, sizeof(spipe.spriteExtraColor2), CLEAR_ON_RESET },
|
|
|
|
{ &displayMode, sizeof(displayMode), CLEAR_ON_RESET },
|
|
{ NULL, 0, 0 }};
|
|
|
|
registerSnapshotItems(items, sizeof(items));
|
|
}
|
|
|
|
PixelEngine::~PixelEngine()
|
|
{
|
|
debug(3, " Releasing PixelEngine...\n");
|
|
}
|
|
|
|
void
|
|
PixelEngine::reset()
|
|
{
|
|
VirtualComponent::reset();
|
|
|
|
// Establish bindings
|
|
vic = &c64->vic;
|
|
|
|
memset(&sr, 0, sizeof(sr));
|
|
memset(&sprite_sr, 0, sizeof(sprite_sr));
|
|
}
|
|
|
|
void
|
|
PixelEngine::resetScreenBuffers()
|
|
{
|
|
for (unsigned line = 0; line < PAL_RASTERLINES; line++) {
|
|
for (unsigned i = 0; i < NTSC_PIXELS; i++) {
|
|
screenBuffer1[line][i] = screenBuffer2[line][i] = (line % 2) ? colors[8] : colors[9];
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PixelEngine::beginFrame()
|
|
{
|
|
visibleColumn = false;
|
|
}
|
|
|
|
void
|
|
PixelEngine::beginRasterline()
|
|
{
|
|
// Clear z buffer. The buffer is initialized with the highest positive 8-bit value (meaning the pixel is far away)
|
|
// Not necessary, because canvas pixels are drawn first
|
|
// memset(zBuffer, SCHAR_MAX, sizeof(zBuffer));
|
|
|
|
// Clear pixel source
|
|
// Not necessary, because canvas pixels are drawn first
|
|
// memset(pixelSource, 0x00, sizeof(pixelSource));
|
|
|
|
// Adjust position of first pixel in buffer (make sure that screen is always centered)
|
|
if (c64->isPAL()) {
|
|
bufferoffset = PAL_LEFT_BORDER_WIDTH - 32;
|
|
} else {
|
|
bufferoffset = NTSC_LEFT_BORDER_WIDTH - 32;
|
|
}
|
|
|
|
// Prepare sprite pixel shift register
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
sprite_sr[i].remaining_bits = -1;
|
|
// sprite_sr[i].mcol_bits = sprite_sr[i].scol_bit = 0;
|
|
sprite_sr[i].col_bits = 0;
|
|
}
|
|
|
|
// Clear pixel buffer (has same size as pixelSource and zBuffer)
|
|
// FOR DEBUGGING ONLY, 0xBB is a randomly chose debug color
|
|
if (!vic->vblank)
|
|
memset(pixelBuffer, 0xBB, sizeof(pixelSource));
|
|
}
|
|
|
|
void
|
|
PixelEngine::endRasterline()
|
|
{
|
|
if (!vic->vblank) {
|
|
|
|
// Make the border look nice
|
|
expandBorders();
|
|
|
|
// Advance pixelBuffer
|
|
uint16_t nextline = c64->getRasterline() - PAL_UPPER_VBLANK + 1;
|
|
if (nextline < PAL_RASTERLINES) {
|
|
|
|
// Old code
|
|
// pixelBuffer += NTSC_PIXELS;
|
|
// pxbuf += NTSC_PIXELS;
|
|
|
|
// New code (slightly slower, but foolproof. Can't get outside the screen buffer)
|
|
pixelBuffer = currentScreenBuffer + (nextline * NTSC_PIXELS);
|
|
// pxbuf = pixelBuffer + bufshift;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
void
|
|
PixelEngine::endFrame()
|
|
{
|
|
// Switch active screen buffer
|
|
currentScreenBuffer = (currentScreenBuffer == screenBuffer1[0]) ? screenBuffer2[0] : screenBuffer1[0];
|
|
pixelBuffer = currentScreenBuffer;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// VIC state latching
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
inline void
|
|
PixelEngine::updateSpriteOnOff()
|
|
{
|
|
dc.spriteOnOff = dc.spriteOnOffPipe;
|
|
dc.spriteOnOffPipe = vic->spriteOnOff;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// High level drawing (canvas, sprites, border)
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
void
|
|
PixelEngine::draw()
|
|
{
|
|
if (vic->vblank)
|
|
return;
|
|
|
|
drawCanvas();
|
|
drawBorder();
|
|
drawSprites();
|
|
|
|
bufferoffset += 8;
|
|
}
|
|
|
|
void
|
|
PixelEngine::draw17()
|
|
{
|
|
if (vic->vblank)
|
|
return;
|
|
|
|
drawCanvas();
|
|
drawBorder17();
|
|
drawSprites();
|
|
|
|
bufferoffset += 8;
|
|
}
|
|
|
|
void
|
|
PixelEngine::draw55()
|
|
{
|
|
if (vic->vblank)
|
|
return;
|
|
|
|
drawCanvas();
|
|
drawBorder55();
|
|
drawSprites();
|
|
|
|
bufferoffset += 8;
|
|
}
|
|
|
|
void
|
|
PixelEngine::drawOutsideBorder()
|
|
{
|
|
if (vic->vblank)
|
|
return;
|
|
|
|
drawSprites();
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::drawBorder()
|
|
{
|
|
if (pipe.mainFrameFF) {
|
|
|
|
setFramePixel(0, colors[bpipe.borderColor]);
|
|
|
|
// After the first pixel has been drawn, color register changes show up
|
|
bpipe = vic->bp;
|
|
|
|
int rgba = colors[bpipe.borderColor];
|
|
setFramePixel(1, rgba);
|
|
setFramePixel(2, rgba);
|
|
setFramePixel(3, rgba);
|
|
setFramePixel(4, rgba);
|
|
setFramePixel(5, rgba);
|
|
setFramePixel(6, rgba);
|
|
setFramePixel(7, rgba);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::drawBorder17()
|
|
{
|
|
if (pipe.mainFrameFF && !vic->p.mainFrameFF) {
|
|
|
|
// 38 column mode
|
|
setFramePixel(0, colors[bpipe.borderColor]);
|
|
|
|
// After the first pixel has been drawn, color register changes show up
|
|
bpipe = vic->bp;
|
|
|
|
int rgba = colors[bpipe.borderColor];
|
|
setFramePixel(1, rgba);
|
|
setFramePixel(2, rgba);
|
|
setFramePixel(3, rgba);
|
|
setFramePixel(4, rgba);
|
|
setFramePixel(5, rgba);
|
|
setFramePixel(6, rgba);
|
|
// That's all, we only draw 7 pixels here
|
|
|
|
} else {
|
|
|
|
// 40 column mode
|
|
drawBorder();
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::drawBorder55()
|
|
{
|
|
if (!pipe.mainFrameFF && vic->p.mainFrameFF) {
|
|
|
|
// 38 column mode
|
|
setFramePixel(7, colors[bpipe.borderColor]);
|
|
|
|
} else {
|
|
|
|
// 40 column mode
|
|
drawBorder();
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::drawCanvas()
|
|
{
|
|
/* "Der Sequenzer gibt die Grafikdaten in jeder Rasterzeile im Bereich der
|
|
Anzeigespalte aus, sofern das vertikale Rahmenflipflop gelöscht ist (siehe
|
|
Abschnitt 3.9.). Außerhalb der Anzeigespalte und bei gesetztem Flipflop wird
|
|
die letzte aktuelle Hintergrundfarbe dargestellt (dieser Bereich ist
|
|
normalerweise vom Rahmen überdeckt)." [C.B.] */
|
|
|
|
if (!pipe.verticalFrameFF) {
|
|
|
|
uint8_t D011 = vic->p.registerCTRL1 & 0x60; // -xx- ----
|
|
uint8_t D016 = vic->p.registerCTRL2 & 0x10; // ---x ----
|
|
|
|
drawCanvasPixel(0);
|
|
|
|
// After the first pixel has been drawn, color register changes show up
|
|
cpipe = vic->cp;
|
|
|
|
drawCanvasPixel(1);
|
|
drawCanvasPixel(2);
|
|
drawCanvasPixel(3);
|
|
|
|
// After pixel 4, the one and zero bits in D016 and the one bits in D011 show up
|
|
// This corresponds to the behavior of the color latency chip model in VICE
|
|
displayMode |= D016; // latch 1s of D016
|
|
displayMode &= D016 | 0xEF; // latch 0s of D016
|
|
displayMode |= D011; // latch 1s of D011
|
|
|
|
drawCanvasPixel(4);
|
|
drawCanvasPixel(5);
|
|
|
|
// After pixel 6, the zero bits in D011 show up
|
|
// This corresponds to the behavior of the color latency chip model in VICE
|
|
displayMode &= D011 | 0x9F; // latch 0s of D011
|
|
|
|
drawCanvasPixel(6);
|
|
|
|
// TODO (seen in VICE)
|
|
// if D016 had a rising edge, clear the multicolor flipflop
|
|
/*
|
|
if (rising_edge_d016)
|
|
sr.mc_flop = false;
|
|
*/
|
|
drawCanvasPixel(7);
|
|
|
|
} else {
|
|
|
|
// "... bei gesetztem Flipflop wird die letzte aktuelle Hintergrundfarbe dargestellt."
|
|
int col = colors[vic->getBackgroundColor()];
|
|
// The following fix (which was done for border-bm-idle is wrong)
|
|
// int col = col_rgba[0];
|
|
setEightBackgroundPixels(col);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::drawCanvasPixel(uint8_t pixelnr)
|
|
{
|
|
assert(pixelnr < 8);
|
|
|
|
if (pixelnr == (pipe.registerCTRL2 & 0x07) /* horizontal raster scroll */ && sr.canLoad) {
|
|
|
|
// Load shift register
|
|
sr.data = pipe.g_data;
|
|
|
|
// Remember how to synthesize pixels
|
|
sr.latchedCharacter = pipe.g_character;
|
|
sr.latchedColor = pipe.g_color;
|
|
|
|
// Reset the multicolor synchronization flipflop
|
|
sr.mc_flop = true;
|
|
}
|
|
|
|
// Load colors
|
|
loadColors((DisplayMode)displayMode, sr.latchedCharacter, sr.latchedColor);
|
|
|
|
// Render pixel
|
|
if (multicol) {
|
|
if (sr.mc_flop)
|
|
sr.colorbits = (sr.data >> 6);
|
|
setMultiColorPixel(pixelnr, sr.colorbits);
|
|
} else {
|
|
setSingleColorPixel(pixelnr, sr.data >> 7);
|
|
}
|
|
|
|
// Shift register and toggle multicolor flipflop
|
|
sr.data <<= 1;
|
|
sr.mc_flop = !sr.mc_flop;
|
|
}
|
|
|
|
|
|
inline void
|
|
PixelEngine::drawSprites()
|
|
{
|
|
uint8_t firstDMA = vic->isFirstDMAcycle;
|
|
uint8_t secondDMA = vic->isSecondDMAcycle;
|
|
|
|
if (!dc.spriteOnOff && !dc.spriteOnOffPipe && !firstDMA && !secondDMA) // Quick exit
|
|
return;
|
|
|
|
// Update sprite color registers
|
|
spipe = vic->sp;
|
|
|
|
// Draw first four pixels for each sprite
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
if (GET_BIT(dc.spriteOnOff, i)) {
|
|
|
|
bool firstDMAi = GET_BIT(firstDMA, i);
|
|
bool secondDMAi = GET_BIT(secondDMA, i);
|
|
|
|
drawSpritePixel(i, 0, secondDMAi /* freeze */, 0 /* halt */, 0 /* load */);
|
|
drawSpritePixel(i, 1, secondDMAi /* freeze */, 0 /* halt */, 0 /* load */);
|
|
drawSpritePixel(i, 2, secondDMAi /* freeze */, secondDMAi /* halt */, 0 /* load */);
|
|
drawSpritePixel(i, 3, firstDMAi | secondDMAi /* freeze */, 0 /* halt */, 0 /* load */);
|
|
}
|
|
}
|
|
|
|
updateSpriteOnOff();
|
|
|
|
// Draw last four pixels for each sprite
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
if (GET_BIT(dc.spriteOnOff, i)) {
|
|
|
|
bool firstDMAi = GET_BIT(firstDMA, i);
|
|
bool secondDMAi = GET_BIT(secondDMA, i);
|
|
|
|
drawSpritePixel(i, 4, firstDMAi | secondDMAi /* freeze */, 0 /* halt */, secondDMAi /* load */);
|
|
drawSpritePixel(i, 5, firstDMAi | secondDMAi /* freeze */, 0 /* halt */, 0 /* load */);
|
|
drawSpritePixel(i, 6, firstDMAi | secondDMAi /* freeze */, 0 /* halt */, 0 /* load */);
|
|
drawSpritePixel(i, 7, firstDMAi /* freeze */, 0 /* halt */, 0 /* load */);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PixelEngine::drawSpritePixel(unsigned spritenr, unsigned pixelnr, bool freeze, bool halt, bool load)
|
|
{
|
|
assert(spritenr < 8);
|
|
assert(sprite_sr[spritenr].remaining_bits >= -1);
|
|
assert(sprite_sr[spritenr].remaining_bits <= 26);
|
|
|
|
bool multicol = vic->spriteIsMulticolor(spritenr);
|
|
|
|
// Load shift register if applicable
|
|
if (load) {
|
|
loadShiftRegister(spritenr);
|
|
}
|
|
|
|
// Stop shift register if applicable
|
|
if (halt) {
|
|
sprite_sr[spritenr].remaining_bits = -1;
|
|
sprite_sr[spritenr].col_bits = 0;
|
|
}
|
|
|
|
// Run shift register if applicable
|
|
if (!freeze) {
|
|
|
|
// Check for horizontal trigger condition
|
|
if (pipe.xCounter + pixelnr == pipe.spriteX[spritenr] && sprite_sr[spritenr].remaining_bits == -1) {
|
|
sprite_sr[spritenr].remaining_bits = 26; // 24 data bits + 2 clearing zeroes
|
|
sprite_sr[spritenr].exp_flop = true;
|
|
sprite_sr[spritenr].mc_flop = true;
|
|
}
|
|
|
|
// Run shift register if there are remaining pixels to draw
|
|
if (sprite_sr[spritenr].remaining_bits > 0) {
|
|
|
|
// Determine render mode (single color /multi color) and colors
|
|
// TODO: Latch multicolor value at proper cycles. Add dc. multicol
|
|
// sprite_sr[nr].mcol = vic->spriteIsMulticolor(nr);
|
|
sprite_sr[spritenr].col_bits = sprite_sr[spritenr].data >> (multicol && sprite_sr[spritenr].mc_flop ? 22 : 23);
|
|
|
|
// Toggle horizontal expansion flipflop for stretched sprites
|
|
if (GET_BIT(pipe.spriteXexpand, spritenr))
|
|
sprite_sr[spritenr].exp_flop = !sprite_sr[spritenr].exp_flop;
|
|
|
|
// Run shift register and toggle multicolor flipflop
|
|
if (sprite_sr[spritenr].exp_flop) {
|
|
sprite_sr[spritenr].data <<= 1;
|
|
sprite_sr[spritenr].mc_flop = !sprite_sr[spritenr].mc_flop;
|
|
sprite_sr[spritenr].remaining_bits--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw pixel
|
|
if (visibleColumn && vic->drawSprites) {
|
|
if (multicol)
|
|
setMultiColorSpritePixel(spritenr, pixelnr, sprite_sr[spritenr].col_bits & 0x03);
|
|
else
|
|
setSingleColorSpritePixel(spritenr, pixelnr, sprite_sr[spritenr].col_bits & 0x01);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Mid level drawing (semantic pixel rendering)
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
inline void
|
|
PixelEngine::loadColors(DisplayMode mode, uint8_t characterSpace, uint8_t colorSpace)
|
|
{
|
|
switch (mode) {
|
|
|
|
case STANDARD_TEXT:
|
|
|
|
col_rgba[0] = colors[cpipe.backgroundColor[0]];
|
|
col_rgba[1] = colors[colorSpace];
|
|
multicol = false;
|
|
break;
|
|
|
|
case MULTICOLOR_TEXT:
|
|
if (colorSpace & 0x8 /* MC flag */) {
|
|
col_rgba[0] = colors[cpipe.backgroundColor[0]];
|
|
col_rgba[1] = colors[cpipe.backgroundColor[1]];
|
|
col_rgba[2] = colors[cpipe.backgroundColor[2]];
|
|
col_rgba[3] = colors[colorSpace & 0x07];
|
|
multicol = true;
|
|
} else {
|
|
col_rgba[0] = colors[cpipe.backgroundColor[0]];
|
|
col_rgba[1] = colors[colorSpace];
|
|
multicol = false;
|
|
}
|
|
break;
|
|
|
|
case STANDARD_BITMAP:
|
|
col_rgba[0] = colors[characterSpace & 0x0F]; // color of '0' pixels
|
|
col_rgba[1] = colors[characterSpace >> 4]; // color of '1' pixels
|
|
multicol = false;
|
|
break;
|
|
|
|
case MULTICOLOR_BITMAP:
|
|
col_rgba[0] = colors[cpipe.backgroundColor[0]];
|
|
col_rgba[1] = colors[characterSpace >> 4];
|
|
col_rgba[2] = colors[characterSpace & 0x0F];
|
|
col_rgba[3] = colors[colorSpace];
|
|
multicol = true;
|
|
break;
|
|
|
|
case EXTENDED_BACKGROUND_COLOR:
|
|
col_rgba[0] = colors[cpipe.backgroundColor[characterSpace >> 6]];
|
|
col_rgba[1] = colors[colorSpace];
|
|
multicol = false;
|
|
break;
|
|
|
|
case INVALID_TEXT:
|
|
col_rgba[0] = colors[PixelEngine::BLACK];
|
|
col_rgba[1] = colors[PixelEngine::BLACK];
|
|
col_rgba[2] = colors[PixelEngine::BLACK];
|
|
col_rgba[3] = colors[PixelEngine::BLACK];
|
|
multicol = (colorSpace & 0x8 /* MC flag */);
|
|
break;
|
|
|
|
case INVALID_STANDARD_BITMAP:
|
|
col_rgba[0] = colors[PixelEngine::BLACK];
|
|
col_rgba[1] = colors[PixelEngine::BLACK];
|
|
multicol = false;
|
|
break;
|
|
|
|
case INVALID_MULTICOLOR_BITMAP:
|
|
col_rgba[0] = colors[PixelEngine::BLACK];
|
|
col_rgba[1] = colors[PixelEngine::BLACK];
|
|
col_rgba[2] = colors[PixelEngine::BLACK];
|
|
col_rgba[3] = colors[PixelEngine::BLACK];
|
|
multicol = true;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::setSingleColorPixel(unsigned pixelnr, uint8_t bit /* valid: 0, 1 */)
|
|
{
|
|
int rgba = col_rgba[bit];
|
|
|
|
if (bit)
|
|
setForegroundPixel(pixelnr, rgba);
|
|
else
|
|
setBackgroundPixel(pixelnr, rgba);
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::setMultiColorPixel(unsigned pixelnr, uint8_t two_bits /* valid: 00, 01, 10, 11 */)
|
|
{
|
|
int rgba = col_rgba[two_bits];
|
|
|
|
if (two_bits & 0x02)
|
|
setForegroundPixel(pixelnr, rgba);
|
|
else
|
|
setBackgroundPixel(pixelnr, rgba);
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::setSingleColorSpritePixel(unsigned spritenr, unsigned pixelnr, uint8_t bit)
|
|
{
|
|
if (bit) {
|
|
int rgba = colors[spipe.spriteColor[spritenr]];
|
|
setSpritePixel(pixelnr, rgba, spritenr);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::setMultiColorSpritePixel(unsigned spritenr, unsigned pixelnr, uint8_t two_bits)
|
|
{
|
|
int rgba;
|
|
|
|
switch (two_bits) {
|
|
case 0x01:
|
|
rgba = colors[spipe.spriteExtraColor1];
|
|
setSpritePixel(pixelnr, rgba, spritenr);
|
|
break;
|
|
|
|
case 0x02:
|
|
rgba = colors[spipe.spriteColor[spritenr]];
|
|
setSpritePixel(pixelnr, rgba, spritenr);
|
|
break;
|
|
|
|
case 0x03:
|
|
rgba = colors[spipe.spriteExtraColor2];
|
|
setSpritePixel(pixelnr, rgba, spritenr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
PixelEngine::setSpritePixel(unsigned pixelnr, int color, int nr)
|
|
{
|
|
uint8_t mask = (1 << nr);
|
|
|
|
// Check sprite/sprite collision
|
|
if (vic->spriteSpriteCollisionEnabled && (pixelSource[pixelnr] & 0x7F)) {
|
|
vic->iomem[0x1E] |= ((pixelSource[pixelnr] & 0x7F) | mask);
|
|
vic->triggerIRQ(4);
|
|
}
|
|
|
|
// Check sprite/background collision
|
|
if (vic->spriteBackgroundCollisionEnabled && (pixelSource[pixelnr] & 0x80)) {
|
|
vic->iomem[0x1F] |= mask;
|
|
vic->triggerIRQ(2);
|
|
}
|
|
|
|
if (nr == 7)
|
|
mask = 0;
|
|
|
|
setSpritePixel(pixelnr, color, vic->spriteDepth(nr), mask);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
// Low level drawing (pixel buffer access)
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
inline void
|
|
PixelEngine::setFramePixel(unsigned pixelnr, int rgba)
|
|
{
|
|
unsigned offset = bufferoffset + pixelnr;
|
|
assert(offset < NTSC_PIXELS);
|
|
|
|
pixelBuffer[offset] = rgba;
|
|
zBuffer[pixelnr] = BORDER_LAYER_DEPTH;
|
|
pixelSource[pixelnr] &= (~0x80); // disable sprite/foreground collision detection in border
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::setForegroundPixel(unsigned pixelnr, int rgba)
|
|
{
|
|
unsigned offset = bufferoffset + pixelnr;
|
|
assert(offset < NTSC_PIXELS);
|
|
|
|
// The zBuffer check is not necessary as the canvas pixels are the first to draw
|
|
// if (FOREGROUND_LAYER_DEPTH <= zBuffer[offset])
|
|
{
|
|
pixelBuffer[offset] = rgba;
|
|
zBuffer[pixelnr] = FOREGROUND_LAYER_DEPTH;
|
|
pixelSource[pixelnr] = 0x80;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PixelEngine::setBackgroundPixel(unsigned pixelnr, int rgba)
|
|
{
|
|
unsigned offset = bufferoffset + pixelnr;
|
|
assert(offset < NTSC_PIXELS);
|
|
|
|
// The zBuffer check is not necessary as the canvas pixels are the first to draw
|
|
// if (BACKGROUD_LAYER_DEPTH <= zBuffer[offset])
|
|
{
|
|
pixelBuffer[offset] = rgba;
|
|
zBuffer[pixelnr] = BACKGROUD_LAYER_DEPTH;
|
|
pixelSource[pixelnr] = 0x00;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
PixelEngine::setSpritePixel(unsigned pixelnr, int rgba, int depth, int source)
|
|
{
|
|
unsigned offset = bufferoffset + pixelnr;
|
|
assert(offset < NTSC_PIXELS);
|
|
|
|
if (depth <= zBuffer[pixelnr]) {
|
|
pixelBuffer[offset] = rgba;
|
|
zBuffer[pixelnr] = depth;
|
|
}
|
|
pixelSource[pixelnr] |= source;
|
|
|
|
}
|
|
|
|
void
|
|
PixelEngine::expandBorders()
|
|
{
|
|
int color, lastX;
|
|
unsigned leftPixelPos;
|
|
unsigned rightPixelPos;
|
|
|
|
if (c64->isPAL()) {
|
|
leftPixelPos = PAL_LEFT_BORDER_WIDTH - (4*8);
|
|
rightPixelPos = PAL_LEFT_BORDER_WIDTH + PAL_CANVAS_WIDTH + (4*8) - 1;
|
|
lastX = PAL_PIXELS;
|
|
} else {
|
|
leftPixelPos = NTSC_LEFT_BORDER_WIDTH - (4*8);
|
|
rightPixelPos = NTSC_LEFT_BORDER_WIDTH + NTSC_CANVAS_WIDTH + (4*8) - 1;
|
|
lastX = NTSC_PIXELS;
|
|
}
|
|
|
|
// Make picked pixels visible for debugging
|
|
// pixelBuffer[leftPixelPos + 1] = colors[5];
|
|
// pixelBuffer[rightPixelPos - 1] = colors[5];
|
|
|
|
color = pixelBuffer[leftPixelPos];
|
|
for (unsigned i = 0; i < leftPixelPos; i++) {
|
|
pixelBuffer[i] = color;
|
|
// pixelBuffer[i] = colors[5]; // for debugging
|
|
}
|
|
color = pixelBuffer[rightPixelPos];
|
|
for (unsigned i = rightPixelPos+1; i < lastX; i++) {
|
|
pixelBuffer[i] = color;
|
|
// pixelBuffer[i] = colors[5]; // for debugging
|
|
}
|
|
|
|
/*
|
|
// Draw grid lines
|
|
for (unsigned i = 0; i < NTSC_PIXELS; i += 10)
|
|
pixelBuffer[i] = 0xFFFFFFFF;
|
|
*/
|
|
}
|
|
|
|
void
|
|
PixelEngine::markLine(uint8_t color, unsigned start, unsigned end)
|
|
{
|
|
assert (end <= NTSC_PIXELS);
|
|
|
|
int rgba = colors[color];
|
|
for (unsigned i = start; i < end; i++) {
|
|
pixelBuffer[start + i] = rgba;
|
|
}
|
|
}
|