/**
 * Copyright 2009 matsondawson@gmail.com
 * 
 * No reproducible in any way without permission. 
 *    
 */

// This is a complete hack of the Via on a vic20. 

// Via memory locations
var Via1ORB = 0x9120;
var Via1IRB = 0x9120;
var Via1ORA = 0x9121;
var Via1IRA = 0x9121;
var Via1DDRB = 0x9122;
var Via1DDRA = 0x9123;
var Via1T1C_L = 0x9124;
var Via1T1C_H = 0x9125;
var Via1T1L_L = 0x9126;
var Via1T1L_H = 0x9127;
var Via1T2C_L = 0x9128;
var Via1T2C_H = 0x9129;
var Via1SR = 0x912A;
var Via1ACR = 0x912B;
var Via1PCR = 0x912C;
var Via1IFR = 0x912D;
var Via1IER = 0x912E;
var Via1ORA_nohs = 0x912F;
var Via1IRA_nohs = 0x912F;

var Via2ORB = 0x9110;
var Via2IRB = 0x9110;
var Via2ORA = 0x9111;
var Via2IRA = 0x9111;
var Via2DDRB = 0x9112;
var Via2DDRA = 0x9113;
var Via2T1C_L = 0x9114;
var Via2T1C_H = 0x9115;
var Via2T1L_L = 0x9116;
var Via2T1L_H = 0x9117;
var Via2T2C_L = 0x9118;
var Via2T2C_H = 0x9119;
var Via2SR = 0x911A;
var Via2ACR = 0x911B;
var Via2PCR = 0x911C;
var Via2IFR = 0x911D;
var Via2IER = 0x911E;
var Via2ORA_nohs = 0x911F;
var Via2IRA_nohs = 0x911F;

var keysdown = [0,0,0,0,0,0,0,0];

var writeReg1, writeValue1;
var pins_ira1 = 0, pins_irb1 = 0, t2l_l1 = 0;
var inhibitT1Interrupt1=0, inhibitT2Interrupt1=0;
var isvia1interrupt = 0;

var pins_ira2 = 0, pins_irb2 = 0, t2l_l2 = 0;
var inhibitT1Interrupt2=0, inhibitT2Interrupt2=0;
var isvia2Nmi = 0;

/**
 * Initialise VIA.
 */ 
function Via1() {
	pins_ira1 = pins_irb1 = t2l_l1 = writeReg1 = writeValue1 = 0;
	inhibitT1Interrupt1 = inhibitT2Interrupt1 = 1;

	pins_ira2 = pins_irb2 = t2l_l2 = 0;
	inhibitT1Interrupt2 = inhibitT2Interrupt2 = 1;
}

function Via1clock(ccc) {
	// end via1 specific

  	if (writeReg1) {
  		switch (writeReg1) {
  		case Via1ORA:
  		case Via1ORA_nohs:
  			m[Via1ORA] = m[Via1ORA_nohs] = writeValue1;
				updateJoystickAndKeyboard();
  			break;
  		case Via1T1C_L:
  			// write to Via1T1C_L, is actually a write to Via1T1L_L
  			m[Via1T1L_L] = writeValue1;
  			break;
  		case Via1T1C_H:
  			m[Via1T1L_H] = m[Via1T1C_H] = writeValue1;
  			m[Via1T1C_L] = m[Via1T1L_L];
  			// reset interrupt flag
  			m[Via1IFR] &= ~0x40;
  			isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
  			inhibitT1Interrupt1 = 0;
  			break;
  		case Via1T2C_L:
  			t2l_l1 = writeValue1;
  			break;
  		case Via1T2C_H:
  			m[Via1T2C_H] = writeValue1;
  			m[Via1T2C_L] = t2l_l1;
  			// reset interrupt flag
  			m[Via1IFR] &= ~0x20;
  			isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
  			inhibitT2Interrupt1 = 0;
  			break;
  		case Via1IFR:
  			m[Via1IFR] &= ~writeValue1;
        isvia1interrupt = m[Via1IER]&m[Via1IFR]&127;
  			break;
  		case Via1IER:
  			if (writeValue1>127) m[Via1IER] |= writeValue1; else m[Via1IER] &= ~writeValue1;
  			isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
  			break;

			case Via2ORA:
  		case Via2ORA_nohs:
  			m[Via2ORA] = m[Via2ORA_nohs] = writeValue1;
  			break;
  		case Via2T1C_L:
  			// write to Via2T1C_L, is actually a write to Via2T1L_L
  			m[Via2T1L_L] = writeValue1;
  			break;
  		case Via2T1C_H:
  			m[Via2T1L_H] = m[Via2T1C_H] = writeValue1;
  			m[Via2T1C_L] = m[Via2T1L_L];
  			// reset interrupt flag
  			m[Via2IFR] &= ~0x40;
  			isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
  			inhibitT1Interrupt2 = 0;
  			break;
  		case Via2T2C_L:
  			t2l_l1 = writeValue1;
  			break;
  		case Via2T2C_H:
  			m[Via2T2C_H] = writeValue1;
  			m[Via2T2C_L] = t2l_l1;
  			// reset interrupt flag
  			m[Via2IFR] &= ~0x20;
  			isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
  			inhibitT2Interrupt1 = 0;
  			break;
  		case Via2IFR:
  			m[Via2IFR] &= ~writeValue1;/*Via2IsInterrupt();*/isvia2Nmi = m[Via2IER]&m[Via2IFR]&127;
  			break;
  		case Via2IER:
  			if (writeValue1>127) m[Via2IER] |= writeValue1; else m[Via2IER] &= ~writeValue1;
  			isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
  			break;
			
			case Via1DDRA:
			case Via1DDRB:
			case Via1ORB:
				m[writeReg1] = writeValue1;
				updateJoystickAndKeyboard();
				break;

  		default:
  			m[writeReg1] = writeValue1;
  		}
  	}

    /*
      var Via1T1C_L = 0x9124;
      var Via1T1C_H = 0x9125;
      var Via1T1L_L = 0x9126;
      var Via1T1L_H = 0x9127;
      var Via1ACR = 0x912B;
    */
        
  	var acr = m[0x912B]; // Via1ACR
    if ((m[0x9124] -= ccc)<0) { // Via1T1C_L
		  m[0x9124]&=255; // Via1T1C_L
		  m[0x9125]--; // Via1T1C_H
		  if (m[Via1T1C_H]==-1) {
        m[Via1T1C_H]=255;
        
        if (acr & 0x40) {// continuous interrupt - reload counter
  				  m[Via1T1C_L] = m[Via1T1L_L];
	          m[Via1T1C_H] = m[Via1T1L_H];
          m[Via1IFR] |= 0x40;
          isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
  			
  			} else if (!inhibitT1Interrupt1) {
  				m[Via1IFR] |= 0x40; // Set interrupt flag Via1IFR
  				inhibitT1Interrupt1 = 1;
          isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
  			}
      }
		}

    /*
    var Via1T2C_L = 0x9128;
    var Via1T2C_H = 0x9129;
    */

		// &0x20 - timed countdown
		if ((acr & 0x20) == 0) {
		  m[0x9128] -= ccc; // Via1T2C_L
      if (m[0x9128]<0) { // Via1T2C_L
        m[0x9128]&=255; // Via1T2C_L
        m[0x9129]--; // Via1T2C_H
        if (m[0x9129]==-1) { // Via1T2C_H
          m[0x9129]=255; // Via1T2C_H
          if (!inhibitT2Interrupt1) {
  					m[0x912D] |= 0x20; // Set interrupt flag 
  					inhibitT2Interrupt1 = 1;
            isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
  				}
        }
      }
		}

		
		/*
      var Via2T1C_L = 0x9114;
      var Via2T1C_H = 0x9115;
      var Via2T1L_L = 0x9116;
      var Via2T1L_H = 0x9117;
      var Via2ACR = 0x911B;
    */
        
  	var acr = m[0x911B]; // Via2ACR
    if ((m[0x9114] -= ccc)<0) { // Via2T1C_L
		  m[0x9114]&=255; // Via2T1C_L
		  m[0x9115]--; // Via2T1C_H
		  if (m[Via2T1C_H]==-1) {
        m[Via2T1C_H]=255;
        
        if (acr & 0x40) {// continuous interrupt - reload counter
  				  m[Via2T1C_L] = m[Via2T1L_L];
	          m[Via2T1C_H] = m[Via2T1L_H];
          m[Via2IFR] |= 0x40;
          isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
  			
  			} else if (!inhibitT1Interrupt2) {
  				m[Via2IFR] |= 0x40; // Set interrupt flag Via2IFR
  				inhibitT1Interrupt2 = 1;
          isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
  			}
      }
		}

    /*
    var Via2T2C_L = 0x9118;
    var Via2T2C_H = 0x9119;
    */

		// &0x20 - timed countdown
		if ((acr & 0x20) == 0) {
		  m[0x9118] -= ccc; // Via2T2C_L
      if (m[0x9118]<0) { // Via2T2C_L
        m[0x9118]&=255; // Via2T2C_L
        m[0x9119]--; // Via2T2C_H
        if (m[0x9119]==-1) { // Via2T2C_H
          m[0x9119]=255; // Via2T2C_H
          if (!inhibitT2Interrupt2) {
  					m[0x911D] |= 0x20; // Set interrupt flag 
  					inhibitT2Interrupt2 = 1;
            isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
  				}
        }
      }
		}

		writeReg1=0;
}

function updateJoystickAndKeyboard() {
	if (fire) pins_ira2|=32; else pins_ira2&=~32;
	if (left) pins_ira2|=16; else pins_ira2&=~16;
	if (up) pins_ira2|=4; else pins_ira2&=~4;
	if (down) pins_ira2|=8; else pins_ira2&=~8;

  // Output on pins of ORB depends on data direction register.
	var column = ~(m[Via1ORB]&m[Via1DDRB]), row = ~(m[Via1ORA]&m[Via1DDRA]);
	pins_ira1=pins_irb1=0;

	for(var i=0; i<8; i++) {
		if ((column&(1<<i))!=0) pins_ira1 |= keysdown[i];
		pins_irb1 |= ((keysdown[i]&row)!=0)?(1<<i):0;
  }
  
  if ((m[Via1DDRB]&0x80)==0) {
    if (right) pins_irb1|=0x80; else pins_irb1&=~0x80;
  }
}

function Via1Read(regnum, type) {

	switch(regnum) {
		// Read of input register A, depends wholey on whats going on on the pins
		case Via1IRA:
		case Via1IRA_nohs:
			return (~pins_ira1 & ~m[Via1DDRA])&255;
		// Via1IRB works differently, it will read what's on the pins for inputs, but whats in the register for outputs
		case Via1IRB: {
			var ddrb = m[Via1DDRB]; // 0 in DDRB is input
			var pins_in = ~pins_irb1 & ~ddrb;
			var reg_in = m[Via1ORB] & ddrb;
			return (pins_in | reg_in)&255;
		}
		case Via1T1C_L: {
			// reset interrupt flag
			m[Via1IFR]&=~0x40;
			isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
			inhibitT1Interrupt1=0;
			return m[regnum];
		}	
		case Via1T2C_L: {
			// reset interrupt flag
			m[Via1IFR]&=~0x20;
			isvia1interrupt = m[0x912E]&m[0x912D]&127; // Via1IER, Via1IFR
			inhibitT2Interrupt1=0;
			return m[regnum];
		}
		// Interrupt flag register
		case Via1IFR: {
			var result = m[Via1IFR]&0x7F;
			// If any flag set top bit must be set
			if (result!=0) result |= 0x80;
			return result;
		}
		case Via1IER: return m[Via1IER]|0x80; // interrupt enable register
		
		// Read of input register A, depends wholey on whats going on on the pins
		case Via2IRA:
		case Via2IRA_nohs:
			return (~pins_ira2 & ~m[Via2DDRA])&255;
		// Via2IRB works differently, it will read what's on the pins for inputs, but whats in the register for outputs
		case Via2IRB: {
			var ddrb = m[Via2DDRB]; // 0 in DDRB is input
			var pins_in = ~pins_irb2 & ~ddrb;
			var reg_in = m[Via2ORB] & ddrb;
			return (pins_in | reg_in)&255;
		}
		case Via2T1C_L: {
			// reset interrupt flag
			m[Via2IFR]&=~0x40;
			isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
			inhibitT1Interrupt1=0;
			return m[regnum];
		}	
		case Via2T2C_L: {
			// reset interrupt flag
			m[Via2IFR]&=~0x20;
			isvia2Nmi = m[0x911E]&m[0x911D]&127; // Via2IER, Via2IFR
			inhibitT2Interrupt1=0;
			return m[regnum];
		}
		// Interrupt flag register
		case Via2IFR: {
			var result = m[Via2IFR]&0x7F;
			// If any flag set top bit must be set
			if (result!=0) result |= 0x80;
			return result;
		}
		case Via2IER: return m[Via2IER]|0x80; // interrupt enable register
		
		default: return m[regnum];
	}
}

function Via1Write(regnum, value) {
	writeReg1 = regnum;
	writeValue1 = value;
}