#include "6581_.h"
#include "mytypes.h"



#define SID_NUM_MAX			3
#define SID_REGS			25
#define SID_CHAN_REGS			7

#define SID_REG_FC_LO			0x15
#define SID_REG_MODE_VOL		0x18



static uword sidPorts[SID_NUM_MAX];
static ubyte sidValues[SID_NUM_MAX][SID_REGS];
static uword sidDisChan;



static void outp(uword addr, ubyte value)
{
	__asm {
		mov dx, addr
		mov al, value
		out dx, al
	}
}



void sidConfigure(uword *_sidPorts, int _clockSpeed)
{
	for (int i = 0; i < 3; i++) {
		sidPorts[i] = _sidPorts[i];
    }
}


void sidReset(int sidChipNum)
{
	if (sidPorts[sidChipNum] == 0) return;

	for (int reg = 0; reg < SID_REGS; reg++)
    {
		uword portAddr = sidPorts[sidChipNum] + reg;
        outp(portAddr, 0);
   }
}


void sidResume(int sidChipNum)
{
	if (sidPorts[sidChipNum] == 0) return;

	for (int reg = 0; reg < SID_REGS; reg++)
	{
		uword portAddr = sidPorts[sidChipNum] + reg;
		ubyte data = sidValues[sidChipNum][reg];
		outp(portAddr, data);
	}
}


static void sidResetChan(int sidChipNum, int chanNum)
{
	if (sidPorts[sidChipNum] == 0) return;

	for (int reg = chanNum * SID_CHAN_REGS;
    		reg < (chanNum * SID_CHAN_REGS) + SID_CHAN_REGS; reg++)
	{
		uword portAddr = sidPorts[sidChipNum] + reg;
		outp(portAddr, 0);
	}
}


static void sidResumeChan(int sidChipNum, int chanNum)
{
	if (sidPorts[sidChipNum] == 0) return;

	for (int reg = chanNum * SID_CHAN_REGS;
    		reg < (chanNum * SID_CHAN_REGS) + SID_CHAN_REGS; reg++)
	{
		uword portAddr = sidPorts[sidChipNum] + reg;
		ubyte data = sidValues[sidChipNum][reg];
		outp(portAddr, data);
	}
}


static void sidResetFilter(int sidChipNum)
{
	if (sidPorts[sidChipNum] == 0) return;

	for (int reg = SID_REG_FC_LO; reg < SID_REG_MODE_VOL; reg++)
	{
		uword portAddr = sidPorts[sidChipNum] + reg;
		ubyte data = (reg == SID_REG_MODE_VOL) ?
        	sidValues[sidChipNum][reg] & 0x8f : 0;
		outp(portAddr, data);
	}
}


static void sidResumeFilter(int sidChipNum)
{
	if (sidPorts[sidChipNum] == 0) return;

	for (int reg = SID_REG_FC_LO; reg <= SID_REG_MODE_VOL; reg++)
	{
		uword portAddr = sidPorts[sidChipNum] + reg;
		ubyte data = sidValues[sidChipNum][reg];
		outp(portAddr, data);
	}
}


void sidSetDisChan(uword disChan)
{
    for (int sidChipNum = 0; sidChipNum < SID_NUM_MAX; sidChipNum++)
    {
    	for (int sidChanNum = 0; sidChanNum < 3; sidChanNum++)
        {
        	int chan = sidChipNum * 3 + sidChanNum;
            if (CHAN_DISABLED(disChan, chan) &&
            		!CHAN_DISABLED(sidDisChan, chan))
            {
				sidResetChan(sidChipNum, sidChanNum);
            }
            else if (!CHAN_DISABLED(disChan, chan) &&
            		CHAN_DISABLED(sidDisChan, chan))
            {
				sidResumeChan(sidChipNum, sidChanNum);
            }
        }

        if (FILTER_DISABLED(disChan, sidChipNum) &&
        		!FILTER_DISABLED(sidDisChan, sidChipNum))
        {
        	sidResetFilter(sidChipNum);
        }
        else if (!FILTER_DISABLED(sidDisChan, sidChipNum) &&
        		FILTER_DISABLED(disChan, sidChipNum))
        {
        	sidResumeFilter(sidChipNum);
        }
    }

	sidDisChan = disChan;
}


void sidWriteData(int sidChipNum, uword reg, ubyte data)
{
	if (sidPorts[sidChipNum] == 0) return;

    sidValues[sidChipNum][reg] = data;

    if (reg < SID_REG_FC_LO)
    {
	    uint chanNum = (sidChipNum * 3) + (reg / SID_CHAN_REGS);
    	if (CHAN_DISABLED(sidDisChan, chanNum))
        {
        	return;
	    }
  	}
    else if (reg < SID_REG_MODE_VOL)
    {
    	if (FILTER_DISABLED(sidDisChan, sidChipNum))
        {
        	return;

        }
    }
    else if (reg == SID_REG_MODE_VOL)
    {
    	if (FILTER_DISABLED(sidDisChan, sidChipNum))
        {
        	data &= 0x8f;
        }
    }

	uword portAddr = sidPorts[sidChipNum] + reg;
	outp(portAddr, data);
}
