function Multiplier()
{
  this.reset();
}

Multiplier.prototype.reset = function()
{
  this.op1=0;
  this.op2=0;
  this.multype=0;
  this.res=0;
  this.sumextend=0;
}

Multiplier.umul = 0;
Multiplier.smul = 1;
Multiplier.umac = 2;
Multiplier.smac = 3;

Multiplier.prototype.load = function(address)
{
  switch (address)
    {
      case 0x130: // MPY
      case 0x132: // MPYS
      case 0x134: // MAC
      case 0x136: // MACS
        return this.op1;
      case 0x138: // OP2
        return this.op2;
      case 0x13a: // ResLo
        return this.res & 0xffff;
      case 0x13c: // ResHi
        return (this.res >> 16) & 0xffff;
      case 0x13e: // SumExtend
        return this.sumextend;
    }
}

Multiplier.prototype.store = function(address, value)
{
  switch (address)
    {
      case 0x130: // MPY
        this.op1 = value;
        this.multype = Multiplier.umul;
        break;
      case 0x132: // MPYS
        this.op1 = value;
        this.multype = Multiplier.smul;
        break;
      case 0x134: // MAC
        this.op1 = value;
        this.multype = Multiplier.umac;
        break;
      case 0x136: // MACS
        this.op1 = value;
        this.multype = Multiplier.smac;
        break;
      case 0x138: // OP2
        this.op2 = value;
        this.doit();
        break;
      case 0x13a: // ResLo
        this.res = (this.res & 0xffff0000) | (value & 0xffff);
        break;
      case 0x13c: // ResHi
        this.res = (this.res & 0xffff) | ((value & 0xffff) << 16);
        break;
      case 0x13e: // SumExtend
        this.sumextend = value;
        break;
    }
}

function toShort(x)
{
  if (x & 0x8000)
    return x-65536;
  else
    return x;
}

function toUInt32(x)
{
  x &= 0xffffffff;
  if (x<0)
    x+=0x100000000;
  return x;
}

Multiplier.prototype.doit = function()
{
  switch (this.multype)
    { 
      case Multiplier.umul:
        this.res = toUInt32(this.op1 * this.op2);
        this.sumextend = 0;
        break;
      case Multiplier.smul:        
        this.res = toShort(this.op1) * toShort(this.op2);
        if (this.res & 0x80000000)
          this.sumextend = -1;
        else 
          this.sumextend = 0;
        this.res = toUInt32(this.res);
        break;
      case Multiplier.umac:
        this.res += this.op1 * this.op2;
        if (this.res > 0xffffffff)                
          this.sumextend = 1;        
        else 
          this.sumextend = 0;          
        this.res = toUInt32(this.res);
        break;
      case Multiplier.smac:                 
        this.res += toShort(this.op1) * toShort(this.op2);
        if (this.res & 0x80000000)
          this.sumextend = -1;
        else 
          this.sumextend = 0;
        this.res = toUInt32(this.res);        
        break;
    }
}

function TimerA()
{
  this.reset();
}

TimerA.prototype.reset = function()
{
  this.TACTL = 0;
  this.CCTL0 = 0;
  this.CCTL1 = 0;
  this.CCTL2 = 0;
  this.TAR = 0;
  this.CCR0 = 0;
  this.CCR1 = 0;
  this.CCR2 = 0;
}

TimerA.prototype.load = function(address)
{
  switch (address)
    {
      case 0x160: // TACTL
        return this.TACTL;
      case 0x162: // CCTL0
        return this.CCTL0;
      case 0x164: // CCTL1
        return this.CCTL1;
      case 0x166: // CCTL2
        return this.CCTL2;
      case 0x170: // TAR
        return this.TAR;
      case 0x172: // CCR0
        return this.CCR0;
      case 0x174: // CCR1
        return this.CCR1;
      case 0x176: // CCR2
        return this.CCR2;
    }
}

TimerA.prototype.store = function (address, value)
{
  switch (address)
    {
      case 0x160: // TACTL
        this.TACTL = value;
        if (value & 0x4)
          this.TAR = 0;
        break;
      case 0x162: // CCTL0
        this.CCTL0 = value;
        break;
      case 0x164: // CCTL1
        this.CCTL1 = value;
        break;
      case 0x166: // CCTL2
        this.CCTL2 = value;
        break;
      case 0x170: // TAR
        this.TAR = value;
        break;
      case 0x172: // CCR0
        this.CCR0 = value;
        break;
      case 0x174: // CCR1
        this.CCR1 = value;
        break;
      case 0x176: // CCR2
        this.CCR2 = value;
        break;
    }
}

TimerA.prototype.increment = function(cycles)
{
  this.TAR += cycles;
  switch (this.TACTL & 0x30)
    {
      case 0x00: // stop
        break;
      case 0x10: // up to ccr0
        if (this.TAR >= this.CCR0)
          {
            this.TAR = 0;
            if (this.CCTL0 & 0x10)
              return 6;
          }
        break;
      case 0x20: // continuous up
        if (this.TAR >= 0xffff)
          {
            this.TAR = 0;
            if (this.CCTL0 & 0x10)
              return 6;
          }
        break;
      case 0x30: // up/down
        break;
    }
  this.TAR = this.TAR & 0xffff;
  return -1;
}

multiplier = new Multiplier;
timerA = new TimerA;
bytePeripherals = new Array(0x100);
wordPeripherals = new Array(0x80);
for (i=0; i<bytePeripherals.length;i++)
  bytePeripherals[i] = 0xcd;
for (i=0;i<wordPeripherals.length;i++)
  wordPeripherals[i] = 0xcdcd;

function reset()
{
  multiplier.reset();
  timerA.reset();
  for (i=0; i<bytePeripherals.length;i++)
    bytePeripherals[i] = 0xcd;
  for (i=0;i<wordPeripherals.length;i++)
    wordPeripherals[i] = 0xcdcd;
}

function pollForInterrupts(elapsedCycleCount)
{
  return timerA.increment(elapsedCycleCount);
}

function loadPeripheral(address)
{
  if (address < 0x6) // IE1, IE2, IFG1, IFG2, ME1, ME2
    return 0;
  else if (address >= 0x130 && address < 0x140) // MPY
    return multiplier.load(address);
  else if (address >= 0x160 && address < 0x180) // TIMERA
    return timerA.load(address);
  else if (address < 0x100)
    return bytePeripherals[address];
  else if (address < 0x200)
    return wordPeripherals[(address-0x100)/2];
  else
    return 0xcd;
}

function storePeripheral(address, value)
{
  if (address == 0x77) // uart output
    WScript.Echo(String.fromCharCode(value)); // output will appear in the JavaScript console window
  else if (address >= 0x130 && address < 0x140) // MPY
    multiplier.store(address, value);
  else if (address >= 0x160 && address < 0x180) // TIMERA
    timerA.store(address, value);
  else if (address < 0x100)
    bytePeripherals[address] = value;
  else if (address < 0x200)
    wordPeripherals[(address-0x100)/2] = value;
}
