Match NUSIZ during player decode or draw to real hardware.

This commit is contained in:
Christian Speckner 2017-02-06 00:30:39 +01:00
parent 8b4096e1ef
commit 1a7ac60232
2 changed files with 90 additions and 140 deletions

View File

@ -20,7 +20,6 @@
enum Count: Int8 { enum Count: Int8 {
renderCounterOffset = -5, renderCounterOffset = -5,
renderCounterOffsetWide = -6
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -39,28 +38,25 @@ void Player::reset()
myHmmClocks = 0; myHmmClocks = 0;
myCounter = 0; myCounter = 0;
myIsMoving = false; myIsMoving = false;
myWidth = 8;
myEffectiveWidth = 8;
myIsRendering = false; myIsRendering = false;
myRenderCounter = 0; myRenderCounter = 0;
myPatternOld = 0; myPatternOld = 0;
myPatternNew = 0; myPatternNew = 0;
myPattern = 0;
myIsReflected = 0; myIsReflected = 0;
myIsDelaying = false; myIsDelaying = false;
myColor = myObjectColor = myDebugColor = 0; myColor = myObjectColor = myDebugColor = 0;
myDebugEnabled = false; myDebugEnabled = false;
collision = myCollisionMaskDisabled; collision = myCollisionMaskDisabled;
myDivider = 1;
updatePattern(); mySampleCounter = 0;
myDividerPending = 0;
myDividerChangeCounter = -1;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::grp(uInt8 pattern) void Player::grp(uInt8 pattern)
{ {
myPatternNew = pattern; myPatternNew = pattern;
if (!myIsDelaying) updatePattern();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -73,37 +69,58 @@ void Player::hmp(uInt8 value)
void Player::nusiz(uInt8 value) void Player::nusiz(uInt8 value)
{ {
const uInt8 masked = value & 0x07; const uInt8 masked = value & 0x07;
const uInt8 oldWidth = myWidth;
if (masked == 5) switch (masked) {
myWidth = 16; case 5:
else if (masked == 7) myDividerPending = 2;
myWidth = 32; break;
else
myWidth = 8;
myDecodes = DrawCounterDecodes::get().playerDecodes()[masked]; case 7:
myDividerPending = 4;
break;
// This is an incomplete description of the effects seen in issues #87 and #82. More investigation default:
// is required for a complete description (#63) myDividerPending = 1;
if (myIsRendering && myRenderCounter >= (8 - myWidth / 4 - 2) && oldWidth == 8 && myWidth != oldWidth) break;
myEffectiveWidth = oldWidth;
else
myEffectiveWidth = myWidth;
if (myRenderCounter >= myEffectiveWidth)
myIsRendering = false;
// NUSIZ during decode seems to affect the decoding logic. The rods in Meltdown
// are highly sensitive to this effect, and this seems to model it adequately.
if (myIsRendering && myRenderCounter < 0) {
if (myWidth > 8 && oldWidth == 8)
myRenderCounter += (myRenderCounter < -2 ? -1 : 1);
else if (myWidth == 8 && oldWidth > 8 && myRenderCounter < -3)
myRenderCounter++;
} }
if (oldWidth != myWidth) updatePattern(); if (myIsRendering) {
switch ((myDivider << 4) | myDividerPending) {
case 0x12:
case 0x14:
if ((myRenderCounter - Count::renderCounterOffset) < 3)
myDivider = myDividerPending;
else
myDividerChangeCounter = 1;
break;
case 0x21:
case 0x41:
if ((myRenderCounter - Count::renderCounterOffset) < 3) {
myDivider = myDividerPending;
} else if ((myRenderCounter - Count::renderCounterOffset) < 5) {
myDivider = myDividerPending;
myRenderCounter--;
} else {
myDividerChangeCounter = 1;
}
break;
default:
if (myRenderCounter < 1)
myDivider = myDividerPending;
else
myDividerChangeCounter = (myDivider - (myRenderCounter - 1) % myDivider);
break;
}
} else {
myDivider = myDividerPending;
}
myDecodes = DrawCounterDecodes::get().playerDecodes()[masked];
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -111,8 +128,6 @@ void Player::resp(uInt8 counter)
{ {
myCounter = counter; myCounter = counter;
const Int8 renderCounterOffset = myWidth > 8 ? Count::renderCounterOffsetWide : Count::renderCounterOffset;
// This tries to account for the effects of RESP during draw counter decode as // This tries to account for the effects of RESP during draw counter decode as
// described in Andrew Towers' notes. Still room for tuning.' // described in Andrew Towers' notes. Still room for tuning.'
if (myIsRendering && (myRenderCounter - renderCounterOffset) < 4) if (myIsRendering && (myRenderCounter - renderCounterOffset) < 4)
@ -122,11 +137,7 @@ void Player::resp(uInt8 counter)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::refp(uInt8 value) void Player::refp(uInt8 value)
{ {
const bool oldIsReflected = myIsReflected;
myIsReflected = (value & 0x08) > 0; myIsReflected = (value & 0x08) > 0;
if (myIsReflected != oldIsReflected) updatePattern();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -135,15 +146,12 @@ void Player::vdelp(uInt8 value)
const bool oldIsDelaying = myIsDelaying; const bool oldIsDelaying = myIsDelaying;
myIsDelaying = (value & 0x01) > 0; myIsDelaying = (value & 0x01) > 0;
if (myIsDelaying != oldIsDelaying) updatePattern();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::toggleEnabled(bool enabled) void Player::toggleEnabled(bool enabled)
{ {
myIsSuppressed = !enabled; myIsSuppressed = !enabled;
updatePattern();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -197,11 +205,16 @@ bool Player::movementTick(uInt32 clock, bool apply)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::render() void Player::render()
{ {
collision = ( if (!myIsRendering || myRenderCounter < (myDivider > 1 ? 1 : 0)) {
myIsRendering && collision = myCollisionMaskDisabled;
myRenderCounter >= 0 && return;
(myPattern & (1 << (myWidth - myRenderCounter - 1))) }
) ? myCollisionMaskEnabled : myCollisionMaskDisabled;
uInt8 pixel =
(myIsDelaying ? myPatternOld : myPatternNew) &
(1 << (!myIsReflected ? (7 - mySampleCounter) : mySampleCounter));
collision = (pixel > 0) ? myCollisionMaskEnabled : myCollisionMaskDisabled;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -209,10 +222,27 @@ void Player::tick()
{ {
if (myDecodes[myCounter]) { if (myDecodes[myCounter]) {
myIsRendering = true; myIsRendering = true;
myEffectiveWidth = myWidth; mySampleCounter = 0;
myRenderCounter = myWidth > 8 ? Count::renderCounterOffsetWide : Count::renderCounterOffset; myRenderCounter = Count::renderCounterOffset;
} else if (myIsRendering && ++myRenderCounter >= myEffectiveWidth) { } else if (myIsRendering) {
myIsRendering = false; myRenderCounter++;
if (myDivider == 1) {
if (myRenderCounter > 0) {
mySampleCounter++;
}
if (myRenderCounter >= 0 && myDividerChangeCounter >= 0 && myDividerChangeCounter-- == 0)
myDivider = myDividerPending;
} else {
if (myRenderCounter > 1 && (((myRenderCounter - 1) % myDivider) == 0))
mySampleCounter++;
if (myRenderCounter > 0 && myDividerChangeCounter >= 0 && myDividerChangeCounter-- == 0)
myDivider = myDividerPending;
}
if (mySampleCounter > 7) myIsRendering = false;
} }
if (++myCounter >= 160) myCounter = 0; if (++myCounter >= 160) myCounter = 0;
@ -224,22 +254,20 @@ void Player::shufflePatterns()
const uInt8 oldPatternOld = myPatternOld; const uInt8 oldPatternOld = myPatternOld;
myPatternOld = myPatternNew; myPatternOld = myPatternNew;
if (myIsDelaying && oldPatternOld != myPatternOld) updatePattern();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 Player::getRespClock() const uInt8 Player::getRespClock() const
{ {
switch (myWidth) switch (myDivider)
{ {
case 8: case 1:
return (myCounter + 160 - 5) % 160; return (myCounter + 160 - 5) % 160;
case 16: case 2:
return (myCounter + 160 - 9) % 160; return (myCounter + 160 - 9) % 160;
case 32: case 4:
return (myCounter + 160 - 12) % 160; return (myCounter + 160 - 12) % 160;
default: default:
@ -247,84 +275,6 @@ uInt8 Player::getRespClock() const
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::updatePattern()
{
if (myIsSuppressed) {
myPattern = 0;
return;
}
const uInt32 pattern = myIsDelaying ? myPatternOld : myPatternNew;
switch (myWidth)
{
case 8:
if (myIsReflected) {
myPattern =
((pattern & 0x01) << 7) |
((pattern & 0x02) << 5) |
((pattern & 0x04) << 3) |
((pattern & 0x08) << 1) |
((pattern & 0x10) >> 1) |
((pattern & 0x20) >> 3) |
((pattern & 0x40) >> 5) |
((pattern & 0x80) >> 7);
} else {
myPattern = pattern;
}
break;
case 16:
if (myIsReflected) {
myPattern =
((3 * (pattern & 0x01)) << 14) |
((3 * (pattern & 0x02)) << 11) |
((3 * (pattern & 0x04)) << 8) |
((3 * (pattern & 0x08)) << 5) |
((3 * (pattern & 0x10)) << 2) |
((3 * (pattern & 0x20)) >> 1) |
((3 * (pattern & 0x40)) >> 4) |
((3 * (pattern & 0x80)) >> 7);
} else {
myPattern =
((3 * (pattern & 0x01))) |
((3 * (pattern & 0x02)) << 1) |
((3 * (pattern & 0x04)) << 2) |
((3 * (pattern & 0x08)) << 3) |
((3 * (pattern & 0x10)) << 4) |
((3 * (pattern & 0x20)) << 5) |
((3 * (pattern & 0x40)) << 6) |
((3 * (pattern & 0x80)) << 7);
}
break;
case 32:
if (myIsReflected) {
myPattern =
((0xF * (pattern & 0x01)) << 28) |
((0xF * (pattern & 0x02)) << 23) |
((0xF * (pattern & 0x04)) << 18) |
((0xF * (pattern & 0x08)) << 13) |
((0xF * (pattern & 0x10)) << 8) |
((0xF * (pattern & 0x20)) << 3) |
((0xF * (pattern & 0x40)) >> 2) |
((0xF * (pattern & 0x80)) >> 7);
} else {
myPattern =
((0xF * (pattern & 0x01))) |
((0xF * (pattern & 0x02)) << 3) |
((0xF * (pattern & 0x04)) << 6) |
((0xF * (pattern & 0x08)) << 9) |
((0xF * (pattern & 0x10)) << 12) |
((0xF * (pattern & 0x20)) << 15) |
((0xF * (pattern & 0x40)) << 18) |
((0xF * (pattern & 0x80)) << 21);
}
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::applyColors() void Player::applyColors()
{ {

View File

@ -86,7 +86,6 @@ class Player : public Serializable
private: private:
void updatePattern();
void applyColors(); void applyColors();
private: private:
@ -102,17 +101,18 @@ class Player : public Serializable
uInt8 myHmmClocks; uInt8 myHmmClocks;
uInt8 myCounter; uInt8 myCounter;
bool myIsMoving; bool myIsMoving;
uInt8 myWidth;
uInt8 myEffectiveWidth;
bool myIsRendering; bool myIsRendering;
Int8 myRenderCounter; Int8 myRenderCounter;
uInt8 myDivider;
uInt8 myDividerPending;
uInt8 mySampleCounter;
Int8 myDividerChangeCounter;
const uInt8* myDecodes; const uInt8* myDecodes;
uInt8 myPatternOld; uInt8 myPatternOld;
uInt8 myPatternNew; uInt8 myPatternNew;
uInt32 myPattern;
bool myIsReflected; bool myIsReflected;
bool myIsDelaying; bool myIsDelaying;