BizHawk/BizHawk.Emulation/Computers/Commodore64/Sid.cs

503 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
namespace BizHawk.Emulation.Computers.Commodore64
{
public enum SidEnvelopeState
{
Disabled,
Attack,
Decay,
Release
}
public enum SidMode
{
Sid6581,
Sid8580
}
public class SidRegs
{
public int[] ATK = new int[3];
public bool BP;
public bool D3;
public int[] DCY = new int[3];
public int[] ENV = new int[3];
public int[] F = new int[3];
public int FC;
public bool[] FILT = new bool[3];
public bool FILTEX;
public bool[] GATE = new bool[3];
public bool HP;
public bool LP;
public bool[] NOISE = new bool[3];
public int[] OSC = new int[3];
public int POTX;
public int POTY;
public int[] PW = new int[3];
public int RES;
public int[] RLS = new int[3];
public bool[] RMOD = new bool[3];
public bool[] SAW = new bool[3];
public int[] SR = new int[3];
public bool[] SQU = new bool[3];
public int[] STN = new int[3];
public bool[] SYNC = new bool[3];
public bool[] TEST = new bool[3];
public bool[] TRI = new bool[3];
public int VOL;
public SidRegs()
{
// power on state
SR[0] = 0x7FFFFF;
SR[1] = 0x7FFFFF;
SR[2] = 0x7FFFFF;
}
public byte this[int addr]
{
get
{
int result;
int index;
addr &= 0x1F;
switch (addr)
{
case 0x00:
case 0x07:
case 0x0E:
result = F[addr / 7] & 0xFF;
break;
case 0x01:
case 0x08:
case 0x0F:
result = (F[addr / 7] & 0xFF00) >> 8;
break;
case 0x02:
case 0x09:
case 0x10:
result = PW[addr / 7] & 0xFF;
break;
case 0x03:
case 0x0A:
case 0x11:
result = (PW[addr / 7] & 0x0F00) >> 8;
break;
case 0x04:
case 0x0B:
case 0x12:
index = addr / 7;
result = GATE[index] ? 0x01 : 0x00;
result |= SYNC[index] ? 0x02 : 0x00;
result |= RMOD[index] ? 0x04 : 0x00;
result |= TEST[index] ? 0x08 : 0x00;
result |= TRI[index] ? 0x10 : 0x00;
result |= SAW[index] ? 0x20 : 0x00;
result |= SQU[index] ? 0x40 : 0x00;
result |= NOISE[index] ? 0x80 : 0x00;
break;
case 0x05:
case 0x0C:
case 0x13:
index = addr / 7;
result = (ATK[index] & 0xF) << 4;
result |= DCY[index] & 0xF;
break;
case 0x06:
case 0x0D:
case 0x14:
index = addr / 7;
result = (STN[index] & 0xF) << 4;
result |= RLS[index] & 0xF;
break;
case 0x15:
result = FC & 0x7;
break;
case 0x16:
result = (FC & 0x7F8) >> 3;
break;
case 0x17:
result = FILT[0] ? 0x01 : 0x00;
result |= FILT[1] ? 0x02 : 0x00;
result |= FILT[2] ? 0x04 : 0x00;
result |= FILTEX ? 0x08 : 0x00;
result |= (RES & 0xF) << 4;
break;
case 0x18:
result = (VOL & 0xF);
result |= LP ? 0x10 : 0x00;
result |= BP ? 0x20 : 0x00;
result |= HP ? 0x40 : 0x00;
result |= D3 ? 0x80 : 0x00;
break;
case 0x19:
result = POTX ;
break;
case 0x1A:
result = POTY;
break;
case 0x1B:
result = OSC[2] >> 4;
break;
case 0x1C:
result = ENV[2];
break;
default:
result = 0;
break;
}
return (byte)(result & 0xFF);
}
set
{
int val = value;
int index;
addr &= 0x1F;
switch (addr)
{
case 0x00:
case 0x07:
case 0x0E:
index = addr / 7;
F[index] &= 0xFF00;
F[index] |= val;
break;
case 0x01:
case 0x08:
case 0x0F:
index = addr / 7;
F[index] &= 0xFF;
F[index] |= val << 8;
break;
case 0x02:
case 0x09:
case 0x10:
index = addr / 7;
PW[index] &= 0x0700;
PW[index] |= val;
break;
case 0x03:
case 0x0A:
case 0x11:
index = addr / 7;
F[index] &= 0xFF;
F[index] |= (val & 0x07) << 8;
break;
case 0x04:
case 0x0B:
case 0x12:
index = addr / 7;
GATE[index] = ((val & 0x01) != 0x00);
SYNC[index] = ((val & 0x02) != 0x00);
RMOD[index] = ((val & 0x04) != 0x00);
TEST[index] = ((val & 0x08) != 0x00);
TRI[index] = ((val & 0x10) != 0x00);
SAW[index] = ((val & 0x20) != 0x00);
SQU[index] = ((val & 0x40) != 0x00);
NOISE[index] = ((val & 0x80) != 0x00);
break;
case 0x05:
case 0x0C:
case 0x13:
index = addr / 7;
ATK[index] = (val >> 4) & 0xF;
DCY[index] = val & 0xF;
break;
case 0x06:
case 0x0D:
case 0x14:
index = addr / 7;
STN[index] = (val >> 4) & 0xF;
RLS[index] = val & 0xF;
break;
case 0x15:
FC &= 0x7F8;
FC |= val & 0x7;
break;
case 0x16:
FC &= 0x7;
FC |= val << 3;
break;
case 0x17:
FILT[0] = ((val & 0x01) != 0x00);
FILT[1] = ((val & 0x02) != 0x00);
FILT[2] = ((val & 0x04) != 0x00);
FILTEX = ((val & 0x08) != 0x00);
RES = (val >> 4);
break;
case 0x18:
VOL = (val & 0xF);
LP = ((val & 0x10) != 0x00);
BP = ((val & 0x20) != 0x00);
HP = ((val & 0x40) != 0x00);
D3 = ((val & 0x80) != 0x00);
break;
case 0x19:
POTX = val;
break;
case 0x1A:
POTY = val;
break;
case 0x1B:
OSC[2] = val << 4;
break;
case 0x1C:
ENV[2] = val;
break;
}
}
}
}
public partial class Sid : ISoundProvider
{
private int[] envRateIndex = {
9, 32, 63, 95,
149, 220, 267, 313,
392, 977, 1954, 3126,
3907, 11720, 19532, 31251
};
private int[] syncIndex = { 2, 0, 1 };
public Func<int> ReadPotX;
public Func<int> ReadPotY;
public int clock;
public bool[] envEnable = new bool[3];
public int[] envExpCounter = new int[3];
public int[] envRate = new int[3];
public int[] envRateCounter = new int[3];
public SidEnvelopeState[] envState = new SidEnvelopeState[3];
public bool[] gateLastCycle = new bool[3];
public int output;
public SidRegs regs;
public int[] waveClock = new int[3];
public Sid()
{
ReadPotX = DummyReadPot;
ReadPotY = DummyReadPot;
HardReset();
}
private int DummyReadPot()
{
return 0;
}
public void HardReset()
{
regs = new SidRegs();
}
public byte Peek(int addr)
{
return regs[addr & 0x1F];
}
public void PerformCycle()
{
// accumulator is 24 bits
clock = (clock + 1) & 0xFFFFFF;
ProcessVoice(0);
ProcessVoice(1);
ProcessVoice(2);
// query pots every 512 cycles
if ((clock & 0x1FF) == 0x000)
{
regs.POTX = ReadPotX() & 0xFF;
regs.POTY = ReadPotY() & 0xFF;
}
}
public void Poke(int addr, byte val)
{
regs[addr & 0x1F] = val;
}
private void ProcessEnvelope(int index)
{
// envelope counter is 15 bits
envRateCounter[index] &= 0x7FFF;
if (!gateLastCycle[index] && regs.GATE[index])
{
envState[index] = SidEnvelopeState.Attack;
envEnable[index] = true;
}
else if (gateLastCycle[index] && !regs.GATE[index])
{
envState[index] = SidEnvelopeState.Release;
}
if (envRateCounter[index] == envRate[index])
{
envExpCounter[index] = 0;
if (envEnable[index])
{
}
}
gateLastCycle[index] = regs.GATE[index];
}
private void ProcessShiftRegister(int index)
{
int newBit = ((regs.SR[index] >> 22) ^ (regs.SR[index] >> 17)) & 0x1;
regs.SR[index] = ((regs.SR[index] << 1) | newBit) & 0x7FFFFF;
}
private void ProcessVoice(int index)
{
int triOutput;
int sawOutput;
int squOutput;
int noiseOutput;
int finalOutput = 0xFFFFFF;
bool outputEnabled = false;
// triangle waveform
if (regs.TRI[index])
{
triOutput = waveClock[index] >> 12;
if (regs.SYNC[index])
triOutput ^= regs.OSC[syncIndex[index]] & 0x800;
if ((triOutput & 0x800) != 0x000)
triOutput &= 0xFFF;
triOutput <<= 1;
finalOutput &= triOutput;
outputEnabled = true;
}
// saw waveform
if (regs.SAW[index])
{
sawOutput = waveClock[index] >> 12;
finalOutput &= sawOutput;
outputEnabled = true;
}
// square waveform
if (regs.SQU[index])
{
if (regs.TEST[index])
{
squOutput = 0xFFF;
}
else
{
if (regs.PW[index] >= (waveClock[index] >> 12))
squOutput = 0xFFF;
else
squOutput = 0x000;
}
finalOutput &= squOutput;
outputEnabled = true;
}
// noise waveform
if (regs.NOISE[index])
{
int sr = regs.SR[index];
noiseOutput = sr & 0x100000 >> 9;
noiseOutput |= sr & 0x040000 >> 8;
noiseOutput |= sr & 0x004000 >> 5;
noiseOutput |= sr & 0x000800 >> 3;
noiseOutput |= sr & 0x000200 >> 2;
noiseOutput |= sr & 0x000020 << 1;
noiseOutput |= sr & 0x000004 << 3;
noiseOutput |= sr & 0x000001 << 4;
finalOutput &= noiseOutput;
outputEnabled = true;
}
// test bit resets the oscillator and silences output
if (regs.TEST[index])
{
waveClock[index] = 0x000000;
outputEnabled = false;
}
else
{
// shift register for generating noise
if ((waveClock[index] & 0x7FFFF) == 0x00000)
ProcessShiftRegister(index);
// increment wave clock
waveClock[index] = (waveClock[index] + regs.F[index]) & 0xFFFFFF;
}
// process the envelope generator
//ProcessEnvelope(index);
// write to internal reg
if (outputEnabled)
regs.OSC[index] = finalOutput;
else
regs.OSC[index] = 0x000000;
}
public byte Read(ushort addr)
{
addr &= 0x1F;
switch (addr)
{
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
// can only read these regs
return regs[addr];
default:
return 0;
}
}
private void UpdateEnvelopeRateCounter(int index)
{
switch (envState[index])
{
case SidEnvelopeState.Attack:
envRate[index] = envRateIndex[regs.ATK[index]];
break;
case SidEnvelopeState.Decay:
envRate[index] = envRateIndex[regs.DCY[index]];
break;
case SidEnvelopeState.Release:
envRate[index] = envRateIndex[regs.RLS[index]];
break;
}
}
public void Write(ushort addr, byte val)
{
addr &= 0x1F;
switch (addr)
{
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
// can't write these regs
break;
default:
regs[addr] = val;
break;
}
}
}
}