void UpdateLineCounterAndIRQ(int lineNum) { _lineCounter = lineNum; if ((_ram.ReadIO(0xd01a, false) & 0x1) > 0) { int targetLineNum = (_ram.ReadIO(0xd011, false) & 0x80) * 2 + _ram.ReadIO(0xd012, false); if (_lineCounter == targetLineNum) { _processor.SetIRQ(); } } if (_timer > 0 && (_ram.ReadIO(0xdc0e, false) & 0x1) > 0) { _timer -= VIC2.CYCLES_PER_LINE; if (_timer <= 0) { _timer = 0; if (_timerIRQEnable) { _timerIRQFlag = true; _processor.SetIRQ(); } } } }
public void BufferSamples(int cpuCycles) { if (cpuCycles == 0) { return; } // Adjust amount of cycles to render based on buffer fill float multiplier = 1f + (1764 - samples.Count) / 8192f; // Let multiplier remain at 1 when we're executing the playroutine, to make sure ADSR behavior is accurate if (cpuCycles <= VIC2.CYCLES_PER_LINE * 2) { multiplier = 1f; } cpuCycles = (int)(multiplier * cpuCycles); for (int i = 0; i < 3; ++i) { ushort ioBase = (ushort)(0xd400 + i * 7); _channels[i].frequency = (ushort)(_ram.ReadIO(ioBase) | (_ram.ReadIO((ushort)(ioBase + 1)) << 8)); _channels[i].pulse = (ushort)(_ram.ReadIO((ushort)(ioBase + 2)) | (_ram.ReadIO((ushort)(ioBase + 3)) << 8)); _channels[i].waveform = _ram.ReadIO((ushort)(ioBase + 4)); _channels[i].ad = _ram.ReadIO((ushort)(ioBase + 5)); _channels[i].sr = _ram.ReadIO((ushort)(ioBase + 6)); } float masterVol = (_ram.ReadIO(0xd418) & 0xf) / 22.5f; byte filterSelect = (byte)(_ram.ReadIO(0xd418) & 0x70); byte filterCtrl = _ram.ReadIO(0xd417); // Filter cutoff & resonance // Adjusted to be slightly darker than jsSID float cutoff = _ram.ReadIO(0xd416) + 0.2f; cutoff = 1f - 1.463f * Mathf.Exp(cutoff * _cutoffRatio); if (cutoff < 0.035f) { cutoff = 0.035f; } float resonance = (filterCtrl > 0x5f) ? 8f / (filterCtrl >> 4) : 1.41f; for (int i = 0; i < cpuCycles; ++i) { for (int j = 0; j < 3; ++j) { _channels[j].Clock(); } for (int j = 0; j < 3; ++j) { if (_channels[j].doSync && (_channels[j].syncTarget.waveform & 0x2) != 0) { _channels[j].syncTarget.ResetAccumulator(); } } ++_cycleAccumulator; if (_cycleAccumulator >= _cyclesPerSample) { _cycleAccumulator -= _cyclesPerSample; float output = 0f; float filterInput = 0f; if ((filterCtrl & 1) == 0) { output += _channels[0].GetOutput(); } else { filterInput += _channels[0].GetOutput(); } if ((filterCtrl & 2) == 0) { output += _channels[1].GetOutput(); } else { filterInput += _channels[1].GetOutput(); } if ((filterCtrl & 4) == 0) { output += _channels[2].GetOutput(); } else { filterInput += _channels[2].GetOutput(); } // Highpass float temp = filterInput + _prevBandPass * resonance + _prevLowPass; if ((filterSelect & 0x40) != 0) { output -= temp; } // Bandpass temp = _prevBandPass - temp * cutoff; _prevBandPass = temp; if ((filterSelect & 0x20) != 0) { output -= temp; } // Lowpass temp = _prevLowPass + temp * cutoff; _prevLowPass = temp; if ((filterSelect & 0x10) != 0) { output += temp; } output *= masterVol; newSamples.Add(output); } } lock (samplesLock) { samples.AddRange(newSamples); newSamples.Clear(); } }
public void RenderNextLine() { if (_lineNum >= 200) { return; } int pixelStart = _lineNum * 320; Color black = _palette[0]; Color bgColor = _palette[_ram.ReadIO(0xd021) & 0xf]; Color borderColor = _palette[_ram.ReadIO(0xd020) & 0xf]; byte control = _ram.ReadIO(0xd011); int yScroll = control & 0x7; int xScroll = _ram.ReadIO(0xd016) & 0x7; bool hBorders = (_ram.ReadIO(0xd016) & 0x8) == 0; bool vBorders = (control & 0x8) == 0; bool displayEnable = (control & 0x10) != 0; bool bitmapMode = (control & 0x20) != 0; bool multiColor = (_ram.ReadIO(0xd016) & 0x10) != 0; bool ebcMode = (control & 0x40) != 0; ushort videoBank = (ushort)(0xc000 - (_ram.ReadIO(0xdd00) & 0x3) * 0x4000); ushort charData = (ushort)(videoBank + (_ram.ReadIO(0xd018) & 0xe) * 0x400); ushort bitmapData = (ushort)(videoBank + (_ram.ReadIO(0xd018) & 0x8) * 0x400); ushort screenAddress = (ushort)(videoBank + (_ram.ReadIO(0xd018) & 0xf0) * 0x40); Color mc1 = _palette[_ram.ReadIO(0xd022) & 0xf]; Color mc2 = _palette[_ram.ReadIO(0xd023) & 0xf]; Color mc3 = _palette[_ram.ReadIO(0xd024) & 0xf]; if ((_lineNum == 0 && ((_lineNum + 3) & 0x7) >= yScroll) || (((_lineNum + 3) & 0x7) == yScroll && _lineNum >= _nextBadlineLineNum)) { DoBadLine(yScroll); } // HACK for Hessian scrolling: actually get chars & colors every line if (!_idleState) { for (int i = 0; i < 40; ++i) { _lineChars[i] = _ram.ReadRAM((ushort)(screenAddress + _currentCharRow * 40 + i)); _lineColors[i] = (byte)(_ram.ReadIO((ushort)(0xd800 + _currentCharRow * 40 + i)) & 0xf); } } int charIndex = 0; int charRow = (_lineNum + 3 - yScroll) & 0x7; int bit = 0x80 << xScroll; bool renderSprites = true; // V-border or display off if (!displayEnable || (vBorders && (_lineNum < 4 || _lineNum >= 196))) { for (int i = 0; i < 320; ++i) { _pixels[pixelStart + i] = borderColor; } renderSprites = false; } else // Idle state or illegal mode (render just black) if (_idleState || (ebcMode && multiColor)) { for (int i = 0; i < 320; ++i) { _pixels[pixelStart + i] = black; } } // Charmode else if (!bitmapMode) { byte charByte = ebcMode ? _ram.ReadRAM((ushort)(charData + (_lineChars[charIndex] & 0x3f) * 8 + charRow)) : _ram.ReadRAM((ushort)(charData + _lineChars[charIndex] * 8 + charRow)); // Singlecolor if (!multiColor) { for (int i = 0; i < 320; ++i) { if (hBorders && (i < 7 || i >= 311)) { _pixels[pixelStart + i] = borderColor; } else { if (bit > 0x80 || charByte == 0 || (charByte & bit) == 0) { if (!ebcMode) { _pixels[pixelStart + i] = bgColor; } else { switch (_lineChars[charIndex] >> 6) { case 0: _pixels[pixelStart + i] = bgColor; break; case 1: _pixels[pixelStart + i] = mc1; break; case 2: _pixels[pixelStart + i] = mc2; break; case 3: _pixels[pixelStart + i] = mc3; break; } } } else { _pixels[pixelStart + i] = _palette[_lineColors[charIndex]]; } } bit >>= 1; if (bit == 0) { bit = 0x80; ++charIndex; if (charIndex < 40) { charByte = ebcMode ? _ram.ReadRAM((ushort)(charData + (_lineChars[charIndex] & 0x3f) * 8 + charRow)) : _ram.ReadRAM((ushort)(charData + _lineChars[charIndex] * 8 + charRow)); } } } } // Multicolor else { int bitPairShift = 0x7 + xScroll; for (int i = 0; i < 320; ++i) { if (hBorders && (i < 7 || i >= 311)) { _pixels[pixelStart + i] = borderColor; } else { if (bit > 0x80 || charByte == 0) { _pixels[pixelStart + i] = bgColor; } else { if (_lineColors[charIndex] < 0x8) { if ((charByte & bit) != 0) { _pixels[pixelStart + i] = _palette[_lineColors[charIndex]]; } else { _pixels[pixelStart + i] = bgColor; } } else { byte bitPair = (byte)((charByte >> (bitPairShift & 0x6)) & 0x3); switch (bitPair) { case 0: _pixels[pixelStart + i] = bgColor; break; case 1: _pixels[pixelStart + i] = mc1; break; case 2: _pixels[pixelStart + i] = mc2; break; case 3: _pixels[pixelStart + i] = _palette[_lineColors[charIndex] & 0x7]; break; } } } } bit >>= 1; --bitPairShift; if (bit == 0) { bit = 0x80; bitPairShift = 0x7; ++charIndex; if (charIndex < 40) { charByte = _ram.ReadRAM((ushort)(charData + _lineChars[charIndex] * 8 + charRow)); } } } } } else if (bitmapMode) { byte charByte = _ram.ReadRAM((ushort)(bitmapData + _bitmapRow * 320 + charIndex * 8 + charRow)); // Singlecolor if (!multiColor) { for (int i = 0; i < 320; ++i) { if (hBorders && (i < 7 || i >= 311)) { _pixels[pixelStart + i] = borderColor; } else { if (bit > 0x80 || charByte == 0) { _pixels[pixelStart + i] = bgColor; } else { if ((charByte & bit) != 0) { _pixels[pixelStart + i] = _palette[_lineChars[charIndex] >> 4]; } else { _pixels[pixelStart + i] = _palette[_lineChars[charIndex] & 0xf]; } } } bit >>= 1; if (bit == 0) { bit = 0x80; ++charIndex; if (charIndex < 40) { charByte = _ram.ReadRAM((ushort)(bitmapData + _bitmapRow * 320 + charIndex * 8 + charRow)); } } } } // Multicolor else { int bitPairShift = 0x7 + xScroll; for (int i = 0; i < 320; ++i) { if (hBorders && (i < 7 || i >= 311)) { _pixels[pixelStart + i] = borderColor; } else { if (bit > 0x80 || charByte == 0) { _pixels[pixelStart + i] = bgColor; } else { byte bitPair = (byte)((charByte >> (bitPairShift & 0x6)) & 0x3); switch (bitPair) { case 0: _pixels[pixelStart + i] = bgColor; break; case 1: _pixels[pixelStart + i] = _palette[_lineChars[charIndex] >> 4]; break; case 2: _pixels[pixelStart + i] = _palette[_lineChars[charIndex] & 0xf]; break; case 3: _pixels[pixelStart + i] = _palette[_lineColors[charIndex] & 0xf]; break; } } } bit >>= 1; --bitPairShift; if (bit == 0) { bit = 0x80; bitPairShift = 0x7; ++charIndex; if (charIndex < 40) { charByte = _ram.ReadRAM((ushort)(bitmapData + _bitmapRow * 320 + charIndex * 8 + charRow)); } } } } } byte spriteFlags = _ram.ReadIO(0xd015); byte spriteMCFlags = _ram.ReadIO(0xd01c); byte spriteXMSBFlags = _ram.ReadIO(0xd010); byte spriteXExpandFlags = _ram.ReadIO(0xd01d); Color sprMc1 = _palette[_ram.ReadIO(0xd025) & 0xf]; Color sprMc2 = _palette[_ram.ReadIO(0xd026) & 0xf]; for (int i = 7; i >= 0; --i) { byte spriteY = _ram.ReadIO((ushort)(0xd001 + i * 2)); if (!_spriteActive[i] && (spriteFlags & bitValues[i]) != 0) { if (_lineNum == spriteY - 50 || (_lineNum == 0 && spriteY >= 30 && spriteY < 50)) { _spriteActive[i] = true; _spriteRow[i] = (byte)(_lineNum + 50 - spriteY); } } if (_spriteActive[i]) { // TODO: Y expansion, background priority if (renderSprites) { int startX = _ram.ReadIO((ushort)(0xd000 + i * 2)); if ((spriteXMSBFlags & bitValues[i]) != 0) { startX += 256; } bool xExpand = (spriteXExpandFlags & bitValues[i]) != 0; if (xExpand && startX >= 480 && startX < 504) { startX -= 504; } ushort spriteData = (ushort)(videoBank + _ram.ReadRAM((ushort)(screenAddress + 0x3f8 + i)) * 0x40 + _spriteRow[i] * 3); Color spriteColor = _palette[_ram.ReadIO((ushort)(0xd027 + i)) & 0xf]; for (int j = 0; j < 24; ++j) { int k = xExpand ? (startX + j * 2 - 24) : (startX + j - 24); byte spriteByte = _ram.ReadRAM((ushort)(spriteData + (j >> 3))); for (int l = 0; l < (xExpand ? 2 : 1); ++l) { if (k >= 0 && k <= 320 && (!hBorders || (k >= 7 && k < 311)) && spriteByte != 0) { if ((spriteMCFlags & bitValues[i]) != 0) { byte bitPair = (byte)((spriteByte >> (6 - (j & 0x6))) & 0x3); switch (bitPair) { case 1: _pixels[pixelStart + k] = sprMc1; break; case 2: _pixels[pixelStart + k] = spriteColor; break; case 3: _pixels[pixelStart + k] = sprMc2; break; } } else { if ((spriteByte & bitValues[7 - (j & 0x7)]) != 0) { _pixels[pixelStart + k] = spriteColor; } } } ++k; } } } ++_spriteRow[i]; if (_spriteRow[i] >= 21) { _spriteActive[i] = false; } } } // Done, increment linecount ++_lineNum; }