/**
 * Copyright 2009 matsondawson@gmail.com
 * 
 * No reproducible in any way without permission. 
 *    
 */

// Debug counters
var compiledSectionsCount = 0;
var compiledInstructionsCount = 0;
var loopsOptimizedCount = 0;
var lsrsOptimizedCount = 0;
var aslsOptimizedCount = 0;

/**
 * Returns maximum value out of a and b.
 */ 
function max(a,b) { return (a>b)?a:b; }

/**
 * 6502 Vectors.
 */ 
var NMI_VECTOR = 0xFFFA;
var RESET_VECTOR = 0xFFFC;
var IRQ_VECTOR = 0xFFFE;

/**
 * 6502 Flags
 */ 
var N = 1 << 7, V = 1 << 6, R = 1 << 5, B = 1 << 4,
        D = 1 << 3, I = 1 << 2, Z = 1 << 1, C = 1;

/**
 * i is the cycle counter within a frame
 */ 
var i=0;

/**
 * Read value from offset in memory, or peripheral.
 */ 
function read(offset) {
    return (offset>0x912F || offset<0x9110)
      ?m[offset]
      :Via1Read(offset);
}

/**
 * Write value to offset in memory, or peripheral.
 */ 
var ramMax = 0x2000;

/**
 * 1 if has 3k expansion memory at 0x400.
 */ 
var has3k = 0;

/**
 * Returns 1 if code execution is to continue, else 0.
 */ 
function write(offset, value) {
    // No memory above 0x2000, and cannot write to ROM
		if ((!has3k && offset>=0x400 && offset<0x1000) || offset==ramMax) return;// || (offset>=0x6000 && offset<0x9000)) return;
		
		if (m[offset]==value) return;
    m[offset]=value;

    // Writing to ram
    if (offset<0x9110 || offset>0x912F) {
      // May be dynamic code so have to clear out any precompiled code within range of this memory location.
			switch(coderange[offset]) {
        case -3: // -3 = non-compiled/data
					modCount[offset>>3]++;
        case -1: // -1 = dynamic value
					return;
    		case -2:
    		  // Needs recompile to dynamic immediates
          coderange[offset] = -1; // -1 means immediate is now dynamic
          
          // Find instruction this byte belongs to
          while(coderange[offset]<0) offset--;
  
          // Compile from offset to change to dynamic, possibly breaking up an existing code block
          uncompiledCodeHandler(offset,0x10000);

          // Compile any other code blocks that previously contained the dynamic ones
          var minAffectedCodeBlock = offset-coderange[offset];
          for(var loop=offset-1; loop>=minAffectedCodeBlock; loop--) {
            if (compiled[loop]!=uncompiledCodeHandler) {
              uncompiledCodeHandler(loop,offset);
            }
    			}
      		// If this recompiled the code we are executing, we need to stop execution.
      		return !(pc<minAffectedCodeBlock || pc>offset);
      		
    		default: // >=0 = instruction
    		  var minAffectedCodeBlock = offset-coderange[offset];

          // when an inst is modified convert all related code blocks to data
          // and exit write if within same block.
          // This allows for a return to static code blocks and merging of split dynamic blocks
          for(var loop=minAffectedCodeBlock; loop<=offset; loop++) {
            coderange[loop]=-3;
            compiled[loop]=uncompiledCodeHandler;
    			}
    			// also erase instruction at offsets immediates
    			loop++;
          while(coderange[loop]==-2 || coderange[loop]==-1) {
            coderange[loop++]=-3;
          }
    			
      		// If this the code changed is the code being executed, we need to stop execution.
      		return !(pc<minAffectedCodeBlock || pc>=offset);
      }
      
		}
		// Via
    else Via1Write(offset,value);
}

/**
 * Push v onto the stack and decrement the stack pointer.
 */ 
function push(v) { m[0x100 + sp]=v,sp=(sp-1)&255; }

/**
 * Inline version of push.
 */ 
function rp_push(v) {
	return "m[0x100+sp]=_v_,sp=(sp-1)&255;".replace(/_v_/,v);
}

/**
 * Increment the stack pointer and pull a value off the stack.
 */ 
function pop() {
	return m[0x100 + (sp=(sp+1)&0xFF)];
}

/**
 * Inline version of pop.
 */ 
var st_pop = "m[0x100 + (sp=(sp+1)&255)]";

/**
 * Add a value to a.
 */ 
function adc(b) {
  if (p & 8) { // D=8
      var sum = (a & 0xF) + (b & 0xF) + (p & 1); // C=1
      if (sum > 0x09) sum += 0x06;
      if (sum > 0x1F) sum -= 0x10;

      sum += (a & 0xF0) + (b & 0xF0);
      p = (p&60) | (((~(a ^ b) & (a ^ sum)) & 128) >> 1); // N = 128
      a = sum & 0xFF;
      p |= (a & 128) | (a?0:2);
      if (sum >= 0xA0) {
          a = (a+0x60)&0xFF;
          p |= 1; // C=1
      }
  } else {
      var sum2 = a + b + (p&1);//(p & C);
      p = (p&60) | (((~(a ^ b) & (a ^ sum2)) & 128) >> 1) | ((sum2 >> 8) & 1); // N = 128, C=1
      a = sum2 & 0xFF;
      p |= (a?0:2)|(a&128)
  }
}

/**
 * Subtract a value from a.
 */ 
function sbc(b) {
  if (p & 8) { // D=8
      var sumadd = 0, sum = (a & 0xF) - (b & 0xF) - (~p & 1);//C=1
      if (sum < -10) sumadd = 10;
      else if (sum < 0) sumadd = -6;

      sum += (a & 0xF0) - (b & 0xF0);
      p = (p&60) | ((sum&0xFF)?0:2) | ((sum & 128) | (((a ^ b) & (a ^ sum)) & 128) >> 1); // N=128
      sum += sumadd;
      if (sum < 0) {
          sum += 0xA0;
      } else {
          if (sum >= 0xA0) {
              sum -= 0x60;
          }
          p |= 1; //C=1
      }

      a = sum & 0xFF;
  } else {
      var sum = a - b - (~p & 1); // C=1
      p = (p&60) | ((((a ^ b) & (a ^ sum)) & 128) >> 1) | (((~sum) >> 8) & 1) | (sum & 128); // N=128, V=1
      a = sum & 0xFF;
      if (a == 0) {
          p |= 2;  //Z=2
      }
  }
}

/**
 * These are place holders for setting nz flags.
 * They are used for optimisation and string replaced at the end of 
 * uncompiledCodeHandler. 
 */ 
var nz_a = "NZA", nz_y = "NZY", nz_x = "NZX", nz_b = "NZB";

/**
 * Inline code for rotate left.
 */ 
function rp_rol(v) {
	return "(nb=(((b="+v+")<<1)&255)|(p&"+C+")),p=(nb?0:"+Z+")|(nb&"+N+")|(b>>7)|(p&"+(~(N|Z|C)&255)+")/*NZA*/";
}

/**
 * Inline code for rotate right.
 */ 
function rp_ror(v) {
	return "(nb=((b="+v+")>>1)|((p&"+C+")<<7)),p=(nb?0:"+Z+")|(nb&"+N+")|(b&"+C+")|(p&"+(~(N|Z|C)&255)+")/*NZA*/";
}

/**
 * Inline code for bit.
 */ 
function rp_bit(v) {
	return "p=((a&(b="+v+"))?0:"+Z+")|(b&"+(N|V)+")|(p&"+(~(N|V|Z)&255)+")/*NZA*/";
}

/**
 * Inline code for logic shift right.
 */ 
function rp_lsr(v) {
  return ("(b=_v_)>>1,p=((b&254)?0:"+Z+")|(b&"+C+")|(p&"+(~(N|Z|C)&255)+")/*NZA*/").replace(/_v_/g,v);
}
function rp_lsr_multiple(v,howmany) {
  return ("(b=_v_>>"+(howmany-1)+")>>1,p=((b&254)?0:"+Z+")|(b&"+C+")|(p&"+(~(N|Z|C)&255)+")/*NZA*/").replace(/_v_/g,v);
}

/**
 * Inline code for arithmetic shift left.
 */ 
function rp_asl(v) {
	return ("(b=_v_<<1)&255,p=((b&255)?0:"+Z+")|(b>>8)|(b&"+N+")|(p&"+(~(N|Z|C)&255)+")/*NZA*/").replace(/_v_/g,v);
}
function rp_asl_multiple(v,howmany) {
  return ("(b=_v_<<"+howmany+")&255,p=((b&255)?0:"+Z+")|((b>>8)&1)|(b&"+N+")|(p&"+(~(N|Z|C)&255)+")/*NZA*/").replace(/_v_/g,v);
}

function isInteger(s) {
  return (s.toString().search(/^-?[0-9]+$/) == 0);
}

/**
 * Inline code for setting nzc flags with non constant.
 */ 
function rp_nzc(r,v) {
	if (isInteger(v) || v=="a") {
		return ("p=((_r_>=_v_)?((_r_==_v_?"+(Z|C)+":"+C+")):"+N+")|(p&"+(~(Z|C|N)&255)+")/*NZA*/").replace(/_r_/g,r).replace(/_v_/g,v);
	}
  else {
		return ("p=((_r_>=(v=_v_))?((_r_==v?"+(Z|C)+":"+C+")):"+N+")|(p&"+(~(Z|C|N)&255)+")/*NZA*/").replace(/_r_/g,r).replace(/_v_/g,v);
	}
}


/**
 * Replace occurrences of a _value_ in a string with passed v.
 */ 
function rp(st,v) {
	return st.replace(/_value_/g,v);
}

function rp_bra(cond,rel,offset) {

  var imoff = (isInteger(rel))
    ?((rel>127?(rel-256):rel)+2+offset)
    :"(((b="+rel+")>127?(b-256):b)+"+(offset+2)+")";
  var isNeg = (cond!=1 && cond!=2 && cond!=4 && cond!=8 && cond!=16 && cond!=32 && cond!=64 && cond!=128);
  if (isNeg) cond=~cond;
  
  return "pc=("+(isNeg?"~p":"p")+"&"+cond+")?"+imoff+":"+(offset+2)+";";
}

/**
 * Uh-oh, somethings not right.  Warn the user.
 */ 
function halt(v) {
	alert("Unknown/Unimplemented instruction at "+v.toString(16)+": "+(m[v]?m[v].toString(16):"undefined"));
	exit(); // Yeh, I know this ins't a method, but it has the same effect.
}

/**
 * Compile the instruction at offset and return the equivalent source code. 
 */ 
function compileInstruction(offset) {
  var dyn1 = coderange[offset+1]==-1;
  var dyn2 = dyn1 || (coderange[offset+2]==-1);
  
	var inst = m[offset];
	var immi = m[offset+1];
  var imm = dyn1?"m["+(offset+1)+"]":immi; 
	var imm_plus_1 = dyn1?(imm+"+1"):(imm+1);
	var zp_plus_1 = dyn1?("((m["+(offset+1)+"]+1)&255)"):(m[offset+1]+1)&255; 
	var addy =  dyn2?("("+imm+"|(m["+(offset+2)+"]<<8))"):(m[offset+1] | (m[offset+2]<<8)); 
	var addyi = m[offset+1] | (m[offset+2]<<8);
	var addyPlus1Overflow = dyn2?"(("+addy+">>8)<<8)|(("+addy+"+1)&0xFF)"
                                    :((addyi>>8)<<8)|((addyi+1)&0xFF); // Note JSR (ind) page boundry bug
  var isVolatileMem = dyn2 || (addyi>=0x9110 && addyi<0x9130);
	var isVolatileMemPossible = dyn2 || ((addyi+255)>=0x9110 && addyi<0x9130);
  

  var isImm0 = isInteger(imm) && imm==0;
  
	// ORA $zp
	var mem_zp_r = "m["+imm+"]";
	// ROR $zp
	var mem_zp_w = "if (write("+imm+",_value_)) { pc="+(offset+2)+"; return; }";

	// ORA $zp,x
	var mem_zpx_r = isImm0?"m[x]":"m[("+imm+"+x)&255]";
	// ROR $zp,x
	var mem_zpx_w = isImm0?"if (write(x,_value_)) { pc="+(offset+2)+"; return; }"
                        :"if (write(("+imm+"+x)&255,_value_)) { pc="+(offset+2)+"; return; }";
  
	// ORA $zp,y
	var mem_zpy_r = isImm0?"m[y]":"m[("+imm+"+y)&255]";
	// ROR $zp,y
	var mem_zpy_w = isImm0?"if (write(y,_value_)) { pc="+(offset+1)+"; return; }"
                        :"if (write(("+imm+"+y)&255,_value_)) { pc="+(offset+2)+"; return; }";

	// ORA $abs
	var mem_abs_r = isVolatileMem?"read("+addy+")":"m["+addy+"]";
	
	var mem_abs_r_plus_1 = isVolatileMem?"read("+addyPlus1Overflow+")":"m["+addyPlus1Overflow+"]";
	// ROR $abs
	var mem_abs_w = "if (write("+addy+",_value_)) { pc="+(offset+3)+"; return; }";
	var memi_abs_r = "("+mem_abs_r+"|("+mem_abs_r_plus_1+"<<8))";

	// ORA $abs,x
	var mem_absx_r = isVolatileMemPossible?"read("+addy+"+x)":"m["+addy+"+x]";
	// ROR $abs,x
	var mem_absx_w = "if (write("+addy+"+x,_value_)) { pc="+(offset+3)+"; return; }";

	// ORA $abs,y
	var mem_absy_r = isVolatileMemPossible?"read("+addy+"+y)":"m["+addy+"+y]";
	// ROR $abs,y
	var mem_absy_w = "if (write("+addy+"+y,_value_)) { pc="+(offset+3)+"; return; }";

	// ($zp)
	var mem_os_zp = isImm0?"(m[0]|(m[1]<<8))":"(m["+imm+"]|(m["+zp_plus_1+"]<<8))";
	
	var os_zp_y = mem_os_zp+"+y";
	// ORA ($zp),y
	var mem_os_zp_y_r = isVolatileMemPossible?"read("+os_zp_y+")":"m["+os_zp_y+"]";
	// ROR ($zp),y
	var mem_os_zp_y_w = "if (write("+os_zp_y+",_value_)) { pc="+(offset+2)+"; return; }";
	
	var os_zpx = isImm0?"(m[x]|(m[(x+1)&255]<<8))":"(m[("+imm+"+x)&255]|(m[("+imm_plus_1+"+x)&255]<<8))";
	// ORA ($zp,x)
	var mem_os_zpx_r = isVolatileMemPossible?"read("+os_zpx+")":"m["+os_zpx+"]";
	// ROR ($zp,x)
	var mem_os_zpx_w = "if (write("+os_zpx+",_value_)) { pc="+(offset+2)+"; return; }"; 
	
	var result = "halt("+offset+");";
	pcp = 1, cyc=1;

	switch(inst) {
		// BRK
		case 0x00: result="/*BRK*/"+rp_push((offset+1)>>8)+rp_push((offset+1)&255)+rp_push("p|"+B)
        +"p|="+I+";pc=m["+IRQ_VECTOR+"]|(m["+(IRQ_VECTOR+1)+"]<<8);"; cyc=7; pcp=2; break;
		// JSR abs
		case 0x20: result="/*JSR abs*/"+rp_push((offset+2)>>8)+rp_push((offset+2)&255)+"pc="+addy+";";
          cyc=6; pcp=3;
          break;
		// RTI
		case 0x40: result="/*RTI*/"+"p="+st_pop+"|"+R+";pc="+st_pop+"|("+st_pop+"<<8);"; cyc=6; pcp=1; break;
		// RTS
		case 0x60: result="/*RTS*/"+"pc=("+st_pop+"|("+st_pop+"<<8))+1;"; cyc=6; pcp=1; break;
		// LDY #
		case 0xA0: {
			if (isInteger(imm)) {
        if (imm==0) {
  				result="y=0;p=p&"+(~N&255)+"|"+Z+";";
  			}
  			else if(imm>127) {
  				result="y="+imm+";p=p&"+(~Z&255)+"|"+N+";";
  			}
  			else {
  				result="y="+imm+";p&="+(~(Z|N)&255)+";";
  			}
  			result = "/*LDY #imm*/"+result+"/*NZY*/";
  		}
  		else {
        result="/*LDY #imm*/y="+imm+";NZY";
			}
			cyc=2; pcp=2;
		}
		break;
		// CPY #
		case 0xC0: result="/*CPY #*/"+rp_nzc("y",imm)+";"; cyc=2; pcp=2; break;
		// CPX #
		case 0xE0: result="/*CPX #*/"+rp_nzc("x",imm)+";"; cyc=2; pcp=2; break;

		// LDX #
		case 0xA2:  {
		  if (isInteger(imm)) {
      	if (imm==0) {
  				result="x=0;p=p&"+(~N&255)+"|"+Z+";";
  			}
  			else if(imm>127) {
  				result="x="+imm+";p=p&"+(~Z&255)+"|"+N+";";
  			}
  			else {
  				result="x="+imm+";p&="+(~(Z|N)&255)+";";
  			}
  			result = "/*LDX #imm*/"+result+"/*NZX*/";
  		}
  		else {
  		  result="/*LDX #imm*/x="+imm+";NZX";
      }
			cyc=2; pcp=2;
		}
		break;
		// BIT zpg 
		case 0x24: result="/*BIT zpg*/"+rp_bit(mem_zp_r)+";"; cyc=3; pcp=2; break;
		// STY zpg
		case 0x84: result="/*STY zpg*/"+rp(mem_zp_w,"y"); cyc=3; pcp=2; break;
		// STY zpg,x
		case 0x94: result="/*STY zpg,x*/"+rp(mem_zpx_w,"y"); cyc=4; pcp=2; break;
		// LDY zpg
		case 0xA4: result="/*LDY zpg*/"+"y="+mem_zp_r+";"+nz_y; cyc=3; pcp=2; break;
		// LDY zp,x
		case 0xB4: result="/*LDY zp,x*/"+"y="+mem_zpx_r+";"+nz_y; cyc=4; pcp=2; break;
		// CPY zp
		case 0xC4: result="/*CPY zp*/"+rp_nzc("y",mem_zp_r)+";"; cyc=3; pcp=2; break;
		// CPX zp
		case 0xE4: result="/*CPX zp*/"+rp_nzc("x",mem_zp_r)+";"; cyc=3; pcp=2; break;

		// ORA (zp,x)
		case 0x01: result="/*ORA (zp,x)*/"+"a|="+mem_os_zpx_r+";"+nz_a; cyc=6; pcp=2; break;
		// AND (zp,x)
		case 0x21: result="/*AND (zp,x)*/"+"a&="+mem_os_zpx_r+";"+nz_a; cyc=6; pcp=2; break;
		// EOR (zp,x)
		case 0x41: result="/*EOR (zp,x)*/"+"a^="+mem_os_zpx_r+";"+nz_a; cyc=6; pcp=2; break;
		// ADC (zp,x)
		case 0x61: result="/*ADC (zp,x)*/"+"adc("+mem_os_zpx_r+");/*NZA*/"; cyc=6; pcp=2; break;
		// STA (zp,x)
		case 0x81: result="/*STA (zp,x)*/"+rp(mem_os_zpx_w,"a"); cyc=6; pcp=2; break;
		// LDA (zp,x)
		case 0xA1: result="/*LDA (zp,x)*/"+"a="+mem_os_zpx_r+";"+nz_a; cyc=6; pcp=2; break;
		// CMP (zp,x)
		case 0xC1: result="/*CMP (zp,x)*/"+rp_nzc("a",mem_os_zpx_r)+";"; cyc=6; pcp=2; break;
		// SBC (zp,x)
		case 0xE1: result="/*SBC (zp,x)*/"+"sbc("+mem_os_zpx_r+");/*NZA*/"; cyc=6; pcp=2; break;
		
		// ORA zp
		case 0x05: result="/*ORA zp*/"+"a|="+mem_zp_r+";"+nz_a; cyc=3; pcp=2; break;
		// AND zp
		case 0x25: result="/*AND zp*/"+"a&="+mem_zp_r+";"+nz_a; cyc=3; pcp=2; break;
		// EOR zp
		case 0x45: result="/*EOR zp*/"+"a^="+mem_zp_r+";"+nz_a; cyc=3; pcp=2; break;
		// ADC zp
		case 0x65: result="/*ADC zp*/"+"adc("+mem_zp_r+");/*NZA*/"; cyc=3; pcp=2; break;
		// STA zp
		case 0x85: result="/*STA zp*/"+rp(mem_zp_w,"a"); cyc=3; pcp=2; break;
		// LDA zp
		case 0xA5: result="/*LDA zp*/"+"a="+mem_zp_r+";"+nz_a; cyc=3; pcp=2; break;
		// CMP zp
		case 0xC5: result="/*CMP zp*/"+rp_nzc("a",mem_zp_r)+";"; cyc=3; pcp=2; break;
		// SBC zp
		case 0xE5: result="/*SBC zp*/"+"sbc("+mem_zp_r+");/*NZA*/"; cyc=3; pcp=2; break;

		// ASL zp 
		case 0x06: result="/*ASL zp*/"+rp(mem_zp_w,rp_asl(mem_zp_r)); cyc=5; pcp=2; break;
		// ASL zp,x 
		case 0x16: result="/*ASL zp,x*/"+rp(mem_zpx_w,rp_asl(mem_zpx_r)); cyc=5; pcp=2; break;
		// ROL zp 
		case 0x26: result="/*ROL zp*/"+rp(mem_zp_w,rp_rol(mem_zp_r)); cyc=5; pcp=2; break;
		// ROL zp,x 
		case 0x36: result="/*ROL zp,x*/"+rp(mem_zpx_w,rp_rol(mem_zpx_r)); cyc=5; pcp=2; break;
		// LSR zp 
		case 0x46: result="/*LSR zp*/"+rp(mem_zp_w,rp_lsr(mem_zp_r)); cyc=5; pcp=2; break;
		// LSR zp,x 
		case 0x56: result="/*LSR zp,x*/"+rp(mem_zpx_w,rp_lsr(mem_zpx_r)); cyc=5; pcp=2; break;
		// ROR zp
		case 0x66: result="/*ROR zp*/"+rp(mem_zp_w,rp_ror(mem_zp_r)); cyc=5; pcp=2; break;
		// ROR zp,x 
		case 0x76: result="/*ROR zp,x*/"+rp(mem_zpx_w,rp_ror(mem_zpx_r)); cyc=5; pcp=2; break;
		// STX zpg
		case 0x86: result="/*STX zp*/"+rp(mem_zp_w,"x"); cyc=3; pcp=2; break;
		// STX zpg,y
		case 0x96: result="/*STX zp,x*/"+rp(mem_zpy_w,"x"); cyc=4; pcp=2; break;
		// LDX zpg
		case 0xA6: result="/*LDX zp*/"+"x="+mem_zp_r+";"+nz_x; cyc=3; pcp=2; break;		
		// LDX zpg,y
		case 0xB6: result="/*LDX zp,x*/"+"x="+mem_zpy_r+";"+nz_x; cyc=4; pcp=2; break;
		// DEC zpg
		case 0xC6: result="/*DEC zp*/"+rp(mem_zp_w,"b=("+mem_zp_r+"-1)&255")+nz_b; cyc=5; pcp=2; break;
		// DEC zpg,x
		case 0xD6: result="/*DEC zp,x*/"+rp(mem_zpx_w,"b=("+mem_zpx_r+"-1)&255")+nz_b; cyc=5; pcp=2; break;
		// INC zpg
		case 0xE6: result="/*INC zp*/"+rp(mem_zp_w,"b=("+mem_zp_r+"+1)&255")+nz_b; cyc=5; pcp=2; break;
		// INC zpg,x
		case 0xF6: result="/*INC zp,x*/"+rp(mem_zpx_w,"b=("+mem_zpx_r+"+1)&255")+nz_b; cyc=5; pcp=2; break;
		
		// PHP
		case 0x08: result="/*PHP*/"+rp_push("p"); cyc=3; pcp=1; break;
		// CLC
		case 0x18: result="/*CLC*/"+"p&="+(~C&255)+";"; cyc=2; pcp=1;  break;
		// PLP
		case 0x28: result="/*PLP*/"+"p="+st_pop+"|"+R+";/*NZA*/"; cyc=4; pcp=1;  break;
		// SEC
		case 0x38: result="/*SEC*/"+"p|="+C+";"; cyc=2; pcp=1; break;
		// PHA
		case 0x48: result="/*PHA*/"+rp_push("a"); cyc=3; pcp=1; break;
		// CLI
		case 0x58: result="/*CLI*/"+"p&="+(~I&255)+";"; cyc=2; pcp=1;  break;
		// PLA
		case 0x68: result="/*PLA*/"+"a="+st_pop+";"+nz_a; cyc=4; pcp=1; break;
		// SEI
		case 0x78: result="/*SEI*/"+"p|="+I+";"; cyc=2; pcp=1;  break;
		// DEY
		case 0x88: result="/*DEY*/"+"y=(y-1)&255;"+nz_y; cyc=2; pcp=1; break;
		// TYA
		case 0x98: result="/*TYA*/"+"a=y;"+nz_a; cyc=2; pcp=1; break;
		// TAY
		case 0xA8: result="/*TAY*/"+"y=a;"+nz_y; cyc=2; pcp=1; break;
		// CLV
		case 0xB8: result="/*CLV*/"+"p&="+(~V&255)+";"; cyc=2; pcp=1;  break;
		// INY
		case 0xC8: result="/*INY*/"+"y=(y+1)&255;"+nz_y; cyc=2; pcp=1; break;
		// CLD
		case 0xD8: result="/*CLD*/"+"p&="+(~D&255)+";"; cyc=2; pcp=1;  break;
		// INX
		case 0xE8: result="/*INX*/"+"x=(x+1)&255;"+nz_x; cyc=2; pcp=1; break;
		// SED
		case 0xF8: result="/*SED*/"+"p|="+D+";"; cyc=2; pcp=1;  break;

		// ORA #imm
		case 0x09: 
      if (isInteger(imm)) {
        if (imm==0) {
          result=nz_a;
        }
        else if(imm<128) {
          result="a|="+imm+";"+nz_a;
        }
        else {
          result="a|="+imm+";p=p&"+(~Z&255)+"|"+N+";"+"/*NZA*/";
        }
        result = "/*ORA #imm*/"+result;
      }
      else {
			 result="/*ORA #imm*/a|="+imm+";NZA";
			}
			cyc=2; pcp=2;
      break;
		// AND #imm
		case 0x29:
		  if (isInteger(imm)) {
		    if (imm==0) {
          result="a=0;p=p&"+(~N&255)+"|Z;"+"/*NZA*/";
        }
        else if(imm<128) {
        result="a&="+imm+";p=p&"+(~(N|Z)&255)+"|(a?0:"+Z+");"+"/*NZA*/";
        }
        else {
          result="a&="+imm+";"+nz_a;
        }
        result = "/*AND #imm*/"+result;
      }
      else {
        result="/*AND #imm*/a&="+imm+";NZA";
      }
			cyc=2; pcp=2;
      break;
		// EOR #imm
		case 0x49: result="/*EOR #imm*/"+(isImm0?"":"a^="+imm+";")+nz_a; cyc=2; pcp=2; break;
		// ADC #imm
		case 0x69: result="/*ADC #imm*/"+"adc("+imm+");/*NZA*/"; cyc=2; pcp=2; break;
		// NOP #imm
		case 0x89: result="/*NOP #imm*/"; cyc=2; pcp=2; break;
		// LDA #imm
		case 0xA9: {
		  if (isInteger(imm)) {
        if (imm==0) {
          result="a=0;p=p&"+(~N&255)+"|"+Z+";";
        }
        else if(imm>127) {
          result="a="+imm+";p=p&"+(~Z&255)+"|"+N+";";
        }
        else {
          result="a="+imm+";p&="+(~(Z|N)&255)+";";
        }
		  	result = "/*LDA #imm*/"+result+"/*NZA*/";
		  }
		  else {
        result="/*LDA #imm*/a="+imm+";"+nz_a;
      }
			cyc=2; pcp=2;
		}
		break;
		// CMP #imm
		case 0xC9: result="/*CMP #imm*/"+rp_nzc("a",imm)+";"; cyc=2; pcp=2; break;
		
		// SBC #imm
		case 0xEB:
		case 0xE9: result="/*SBC #imm*/"+"sbc("+imm+");/*NZA*/"; cyc=2; pcp=2; break;

		// ASL A         
		case 0x0A: result="/*ASL A*/"+"a="+rp_asl("a")+";"; cyc=2; pcp=1;  break;
		// NOP
		case 0x1A:
		case 0x3A:
		case 0x5A:
		case 0x7A:
		case 0xDA:
		case 0xEA:
		case 0xFA: result="/*NOP*/"; cyc=2; pcp=1; break;
		// ROL A
		case 0x2A: result="/*ROL A*/"+"a="+rp_rol("a")+";"; cyc=2; pcp=1;  break;
		// LSR A
		case 0x4A: result="/*LSR A*/"+"a="+rp_lsr("a")+";"; cyc=2; pcp=1;  break;
		// ROR A
		case 0x6A: result="/*ROR A*/"+"a="+rp_ror("a")+";"; cyc=2; pcp=1;  break;
		// TXA
		case 0x8A: result="/*TXA*/"+"a=x;"+nz_a; cyc=2; pcp=1; break;
		// TXS
		case 0x9A: result="/*TXS*/"+"sp=x;"; cyc=2; pcp=1; break;
		// TAX
		case 0xAA: result="/*TAX*/"+"x=a;"+nz_x; cyc=2; pcp=1; break;
		// TSX
		case 0xBA: result="/*TSX*/"+"x=sp;"+nz_x; cyc=2; pcp=1; break;
		// DEX
		case 0xCA: result="/*DEX*/"+"x=(x-1)&255;"+nz_x; cyc=2; pcp=1; break;

		// BIT abs
		case 0x2C: result="/*BIT abs*/"+rp_bit(mem_abs_r)+";"; cyc=4; pcp=3; break;
		// JMP abs
		case 0x4C: result="/*JMP abs*/"+"pc="+addy+";"; cyc=3; pcp=3; break;
		// JMP (abs)
		case 0x6C: result="/*JMP (abs)*/"+"pc="+memi_abs_r+";"; cyc=5; pcp=3; break;
		// STY abs
		case 0x8C: result="/*STY abs*/"+rp(mem_abs_w,"y"); cyc=4; pcp=3; break;
		// LDY abs
		case 0xAC: result="/*LDY abs*/"+"y="+mem_abs_r+";"+nz_y; cyc=4; pcp=3; break;
		// LDY abs,x
		case 0xBC: result="/*LDY abs,x*/"+"y="+mem_absx_r+";"+nz_y; cyc=4; pcp=3; break;
		// CPY abs
		case 0xCC: result="/*CPY abs*/"+rp_nzc("y",mem_abs_r)+";"; cyc=4; pcp=3; break;
		// CPX abs
		case 0xEC: result="/*CPX abs*/"+rp_nzc("x",mem_abs_r)+";"; cyc=4; pcp=3; break;

		// ORA $abs
		case 0x0D: result="/*ORA abs*/"+"a|="+mem_abs_r+";"+nz_a; cyc=4; pcp=3; break;
		// AND $abs
		case 0x2D: result="/*AND abs*/"+"a&="+mem_abs_r+";"+nz_a; cyc=4; pcp=3; break;
		// EOR $abs
		case 0x4D: result="/*EOR abs*/"+"a^="+mem_abs_r+";"+nz_a; cyc=4; pcp=3; break;
		// ADC $abs
		case 0x6D: result="/*ADC abs*/"+"adc("+mem_abs_r+");/*NZA*/"; cyc=4; pcp=3; break;
		// STA $abs
		case 0x8D: result="/*STA abs*/"+rp(mem_abs_w,"a")+";"; cyc=4; pcp=3; break;
		// LDA $abs
		case 0xAD: result="/*LDA abs*/"+"a="+mem_abs_r+";"+nz_a; cyc=4; pcp=3; break;
		// CMP $abs
		case 0xCD: result="/*CMP abs*/"+rp_nzc("a",mem_abs_r)+";"; cyc=4; pcp=3; break;
		// SBC $abs
		case 0xED: result="/*SBC abs*/"+"sbc("+mem_abs_r+");/*NZA*/"; cyc=4; pcp=3; break;

		// ASL abs
		case 0x0E: result="/*ASL abs*/"+rp(mem_abs_w,rp_asl(mem_abs_r))+nz_b; cyc=6; pcp=3; break;
		// ASL a,x
		case 0x1E: result="/*ASL abs,x*/"+rp(mem_absx_w,rp_asl(mem_absx_r))+nz_b; cyc=7; pcp=3; break;
		// ROL a
		case 0x2E: result="/*ROL abs*/"+rp(mem_abs_w,rp_rol(mem_abs_r))+nz_b; cyc=6; pcp=3; break;
		// ROL a,x
		case 0x3E: result="/*ROL abs,x*/"+rp(mem_absx_w,rp_rol(mem_absx_r))+nz_b; cyc=7; pcp=3; break;
		// LSR a
		case 0x4E: result="/*LSR abs*/"+rp(mem_abs_w,rp_lsr(mem_abs_r))+nz_b; cyc=6; pcp=3; break;
		// LSR a,x
		case 0x5E: result="/*LSR abs,x*/"+rp(mem_absx_w,rp_lsr(mem_absx_r))+nz_b; cyc=7; pcp=3; break;
		// ROR a
		case 0x6E: result="/*ROR abs*/"+rp(mem_abs_w,rp_ror(mem_abs_r))+nz_b; cyc=6; pcp=3; break;
		// ROR a,x
		case 0x7E: result="/*ROR abs,x*/"+rp(mem_absx_w,rp_ror(mem_absx_r))+nz_b; cyc=7; pcp=3; break;
		// STX $abs
		case 0x8E: result="/*STX abs*/"+rp(mem_abs_w,"x"); cyc=4; pcp=3; break;
		// 9E = SHX
		// LDX $abs
		case 0xAE: result="/*LDX abs*/"+"x="+mem_abs_r+";"+nz_x; cyc=4; pcp=3; break;
		// LDX $abs,y
		case 0xBE: result="/*LDX abs,y*/"+"x="+mem_absy_r+";"+nz_x; cyc=4; pcp=3; break;
		// DEC $abs
		case 0xCE: result="/*DEC abs*/"+"b=("+mem_abs_r+"-1)&255;"+rp(mem_abs_w,"b")+nz_b; cyc=4; pcp=3; break;
		// DEC $abs,x
		case 0xDE: result="/*DEC abs,x*/"+"b=("+mem_absx_r+"-1)&255;"+rp(mem_absx_w,"b")+nz_b; cyc=7; pcp=3; break;
		// INC $abs
		case 0xEE: result="/*INC abs*/"+"b=("+mem_abs_r+"+1)&255;"+rp(mem_abs_w,"b")+nz_b; cyc=4; pcp=3; break;
		// INC $abs,x
		case 0xFE: result="/*INC abs,x*/"+"b=("+mem_absx_r+"+1)&255;"+rp(mem_absx_w,"b")+nz_b; cyc=7; pcp=3; break;

		// BPL rel 
		case 0x10: result="/*BPL rel*/"+rp_bra(~N,imm,offset); cyc=3; pcp=2; break;
		// BMI rel
		case 0x30: result="/*BMI rel*/"+rp_bra(N,imm,offset); cyc=3; pcp=2; break;
		// BVC rel
		case 0x50: result="/*BVC rel*/"+rp_bra(~V,imm,offset); cyc=3; pcp=2; break;
		// BVS rel
		case 0x70: result="/*BVS rel*/"+rp_bra(V,imm,offset); cyc=3; pcp=2; break;
		// BCC rel
		case 0x90: result="/*BCC rel*/"+rp_bra(~C,imm,offset); cyc=3; pcp=2; break;
		// BCS rel
		case 0xB0: result="/*BCS rel*/"+rp_bra(C,imm,offset); cyc=3; pcp=2; break;
		// BNE rel
		case 0xD0: result="/*BNE rel*/"+rp_bra(~Z,imm,offset); cyc=3; pcp=2; break;
		// BEQ rel
		case 0xF0: result="/*BEQ rel*/"+rp_bra(Z,imm,offset); cyc=3; pcp=2; break;

		// ORA (zp),y
		case 0x11: result="/*ORA (zp),y*/"+"a|="+mem_os_zp_y_r+";"+nz_a; cyc=3; pcp=2; break;
		// AND (zp),y
		case 0x31: result="/*AND (zp),y*/"+"a&="+mem_os_zp_y_r+";"+nz_a; cyc=3; pcp=2; break;
		// EOR (zp),y
		case 0x51: result="/*EOR (zp),y*/"+"a^="+mem_os_zp_y_r+";"+nz_a; cyc=3; pcp=2; break;
		// ADC (zp),y
		case 0x71: result="/*ADC (zp),y*/"+"adc("+mem_os_zp_y_r+");/*NZA*/"; cyc=3; pcp=2; break;
		// STA (zp),y
		case 0x91: result="/*STA (zp),y*/"+rp(mem_os_zp_y_w,"a"); cyc=3; pcp=2; break;
		// LDA (zp),y
		case 0xB1: result="/*LDA (zp),y*/"+"a="+mem_os_zp_y_r+";"+nz_a; cyc=3; pcp=2; break;
		// CMP (zp),y
		case 0xD1: result="/*CMP (zp),y*/"+rp_nzc("a",mem_os_zp_y_r)+";"; cyc=3; pcp=2; break;
		// SBC (zp),y
		case 0xF1: result="/*SBC (zp),y*/"+"sbc("+mem_os_zp_y_r+");/*NZA*/"; cyc=3; pcp=2; break;

		// ORA zp,x
		case 0x15: result="/*ORA zp,x*/"+"a|="+mem_zpx_r+";"+nz_a; cyc=4; pcp=2; break;
		// AND zp,x
		case 0x35: result="/*AND zp,x*/"+"a&="+mem_zpx_r+";"+nz_a; cyc=4; pcp=2; break;
		// EOR zp,x
		case 0x55: result="/*EOR zp,x*/"+"a^="+mem_zpx_r+";"+nz_a; cyc=4; pcp=2; break;
		// ADC zp,x
		case 0x75: result="/*ADC zp,x*/"+"adc("+mem_zpx_r+");/*NZA*/"; cyc=4; pcp=2; break;
		// STA zp,x
		case 0x95: result="/*STA zp,x*/"+rp(mem_zpx_w,"a"); cyc=4; pcp=2; break;
		// LDA zp,x
		case 0xB5: result="/*LDA zp,x*/"+"a="+mem_zpx_r+";"+nz_a; cyc=4; pcp=2; break;
		// CMP zp,x
		case 0xD5: result="/*CMP zp,x*/"+rp_nzc("a",mem_zpx_r)+";"; cyc=4; pcp=2; break;
		// SBC zp,x
		case 0xF5: result="/*ADC zp,x*/"+"sbc("+mem_zpx_r+");/*NZA*/"; cyc=4; pcp=2; break;

		// ORA abs,y
		case 0x19: result="/*ORA abs,y*/"+"a|="+mem_absy_r+";"+nz_a; cyc=4; pcp=3; break;
		// AND abs,y
		case 0x39: result="/*AND abs,y*/"+"a&="+mem_absy_r+";"+nz_a; cyc=4; pcp=3; break;
		// EOR abs,y
		case 0x59: result="/*EOR abs,y*/"+"a^="+mem_absy_r+";"+nz_a; cyc=4; pcp=3; break;
		// ADC abs,y
		case 0x79: result="/*ADC abs,y*/"+"adc("+mem_absy_r+");/*NZA*/"; cyc=4; pcp=3; break;
		// STA abs,y
		case 0x99: result="/*STA abs,y*/"+rp(mem_absy_w,"a"); cyc=4; pcp=3;  break;
		// LDA abs,y
		case 0xB9: result="/*LDA abs,y*/"+"a="+mem_absy_r+";"+nz_a; cyc=4; pcp=3; break;
		// CMP abs,y
		case 0xD9: result="/*CMP abs,y*/"+rp_nzc("a",mem_absy_r)+";"; cyc=4; pcp=3; break;
		// SBC abs,y
		case 0xF9: result="/*SBC abs,y*/"+"sbc("+mem_absy_r+");/*NZA*/"; cyc=4; pcp=3;  break;

		// ORA abs,x
		case 0x1D: result="/*ORA abs,x*/"+"a|="+mem_absx_r+";"+nz_a; cyc=4; pcp=3; break;
		// AND abs,x
		case 0x3D: result="/*AND abs,x*/"+"a&="+mem_absx_r+";"+nz_a; cyc=4; pcp=3; break;
		// EOR abs,x
		case 0x5D: result="/*EOR abs,x*/"+"a^="+mem_absx_r+";"+nz_a; cyc=4; pcp=3; break;
		// ADC abs,x
		case 0x7D: result="/*ADC abs,x*/"+"adc("+mem_absx_r+");/*NZA*/"; cyc=4; pcp=3; break;
		// STA abs,x
		case 0x9D: result="/*STA abs,x*/"+rp(mem_absx_w,"a"); cyc=4; pcp=3; break;
		// LDA abs,x
		case 0xBD: result="/*LDA abs,x*/"+"a="+mem_absx_r+";"+nz_a; cyc=4; pcp=3; break;
		// CMP abs,x
		case 0xDD: result="/*CMP abs,x*/"+rp_nzc("a",mem_absx_r)+";"; cyc=4; pcp=3; break;
		// SBC abs,x
		case 0xFD: result="/*SBC abs,x*/"+"sbc("+mem_absx_r+");/*NZA*/"; cyc=4; pcp=3; break;
	};

	return result;
}

/**
 * Length of instructions.
 * Used by dynamic code detection routine.  
 */ 
var instsize = [
  2,2,1,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3,
  3,2,1,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3,
  1,2,1,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3,
  1,2,1,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3,
  2,2,2,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,3,2,2,2,2,1,3,1,3,3,3,3,3,
  2,2,2,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3,
  2,2,2,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3,
  2,2,2,2,2,2,2,2,1,2,1,2,3,3,3,3,
  2,2,1,2,2,2,2,2,1,3,1,3,3,3,3,3
];

/**
 * Main memory, peripheral memory (shared).
 */ 
var m = new Array(65536);

/**
 * Compiled code array.
 */ 
var compiled = new Array(65536);

/**
 * Array used to handle dynamic code.
 * The value inside the array represents how many previous
 * memory locations must be cleared if this memory location is cleared.
 */ 
var coderange = new Array(65536);

var modCount = new Array(8192);

/**
 * 1 if optimizations are to be applied such as:
 * - delay loop removal
 * - multiple logic shift grouping
 * - removing unused flag settings    
 */
var isOptimising = 1;

/**
 * This is the default handler for code locations not compiled yet.
 */ 
function uncompiledCodeHandler(offset,setMaxPc) {
  if (!offset) offset=pc;
  
  // pcNext will walk through code from pc.     
	var pcNext = offset;
	
	// Used to fill coderange table with correct range to code block start. 
	var range=0;
	
	// Count of cycles in this code block.
	var cyct=0;
	
	// True if the last instruction in the code block modifys the pc, else 0.
  var lastWasPcModifyingInstruction = 0;
	
	// Compiled code to return
	var compiledCode="";
	
	// If maxPc is about to be passed then a new code block is created.
  var maxPc = setMaxPc?setMaxPc:0x10000;

	
	// String to replace useless NZ flag setting tags with.
  var repstr = "";

	// Little optimisation hack for code that uses decrement branch loops for delays.
	if (isOptimising) {
		// This instruction
		var inst = m[pcNext];
		// The next instruction, if inst was 8 bits
		var nextInst = m[pcNext+1];
		// The relative offset if next isInst is a branch
		var rel = m[pcNext+2];

		if (nextInst==0xD0 && rel==(-3+256)) {
			if (inst==0x88) { // DEY
	      // // Reset y to minimum so only one loop is performed. 
				compiledCode += "/* Wait Loop using Y Optimized *"+"/oy=y;y=1;";
				cyct = "(oy + oy<<2)+"; // 5 is the number of cycles it takes to do a decrement, branch
				loopsOptimizedCount++;
			}
	    else if (inst==0xCA) { // DEX
	      // Reset x to minimum so only one loop is performed.
				compiledCode += "/* Wait Loop using X Optimized *"+"/ox=x;x=1;";
				cyct = "(ox + ox<<2)+"; // 5 is the number of cycles it takes to do a decrement, branch
				loopsOptimizedCount++;
			}
		}
		// SBC #1
		else if ((inst==0xEB || inst==0xE9) && nextInst==1 ) {
			nextInst = m[pcNext+2];
			// The relative offset if next isInst is a branch
			var rel = m[pcNext+3];
			
			if (nextInst==0xD0 && rel==(-4+256)) {
				// Reset a to minimum so only one loop is performed.
				compiledCode += "/* Wait Loop using A Optimized *"+"/oa=a;a=1;";
				cyct = "(oa + oa<<2)+"; // 5 is the number of cycles it takes to do a sbc #1, branch
				loopsOptimizedCount++;
			}
		}
	}

  // Convert multiple instructions to a javascript code block
	do {

    // This instruction
		var inst = m[pcNext];
		// It's immediate/zp/pc.l value
		var imm = m[pcNext+1];
		// It's full address if an absolute instruction
		var addy = imm | (m[pcNext+2]<<8);
		// True if you should use read() instead of m[] to read the value.
		var isVolatileMemPossible = (addy+255)>=0x9110 && addy<0x9130;
		
		// If this instruction is a load/store from a peripheral then update the Vias first.
		var ia15 = inst&15;
		// All absolute load/store operations
		var isLoadStoreOperation = (ia15==0x1||ia15==0x3||(ia15==0x9&&((inst>>4)&1)==1)||(ia15==0xB && ((inst>>4)&1)==1)||ia15>=0xC)
		// true if via must be called before instruction 
		var isAddVia = isLoadStoreOperation && isVolatileMemPossible;
	
    var nextInstCompiled = "";


		if (isOptimising) {
			// LSR A multiple test

      var pcTest = pcNext;
      var count=0;
      while(m[pcTest++]==0x4a) count++;
      if (isOptimising && count>1) {
        nextInstCompiled = "/* LSR"+count+" */ a="+rp_lsr_multiple("a",count)+";";
        cyc = count<<1;
        lsrsOptimizedCount += (pcp = count); 
      }

			// ASL A multiple test
			if (count==0) {			
				pcTest = pcNext;
	      while(m[pcTest++]==0x0a) count++;
	      if (isOptimising && count>1) {
	        nextInstCompiled = "/* ASL"+count+" *"+"/ a="+rp_asl_multiple("a",count)+";";
	        cyc = count<<1;
	        aslsOptimizedCount += (pcp = count); 
	      }
			}
    }
    
    // If instruction hasn't already been created by optimizer then do a normal compile
    if (nextInstCompiled=="") {
      nextInstCompiled = compileInstruction(pcNext);
    }

    // Update total cycle count.
    cyct += cyc;
    
    // Add the via call if need be
		if (isAddVia) {
      // Call via and update global cycle count
      compiledCode+="Via1clock("+cyct+");"+((cyct==0)?"":"tcyc+="+cyct+";");
      // Reset total cycle count.
      cyct=0;
    }

		// Generate the code
		compiledCode += nextInstCompiled;

		// Update the code range array from pCnext to pcNext + instruction size
		coderange[pcNext] = max(coderange[pcNext],range);
		range+=pcp;
    for(var jj=pcNext+1; jj<pcNext+pcp; jj++) {
			coderange[jj] = max(coderange[jj],-2); // -2 means this is an instruction immediate, -1 means this is a dynamic instruction immediate
		}

		// Update pcNext to point to next instruction
		pcNext += pcp;

    // Update debug value
    compiledInstructionsCount++;
    
    // True if last instruction modified the program counter.
		lastWasPcModifyingInstruction = (inst==0 || inst==0x20 || inst==0x40 || inst==0x4C || inst==0x6C || inst==0x60 || inst==0x10 || inst==0x30 || inst==0x50 || inst==0x70 || inst==0x90 || inst==0xB0 || inst==0xD0 || inst==0xF0);
	} while(pcNext<maxPc && !lastWasPcModifyingInstruction);//); // last inst is not a branch/jump/jsr/brk/rts

  // Optimise out unused flag setting code.
  // TODO handle flags optimization when there is a PHP
  if (isOptimising && compiledCode.indexOf("/*PHP*/")==-1) {
  	var idx = compiledCode.lastIndexOf("NZ");
  	if(idx!=-1) {
  		compiledCode = compiledCode.substring(0,idx).replace(/NZ./g,"")+compiledCode.substring(idx);
  	}
  	
    // Replace flag modification placeholders with real code.
  	if (compiledCode.indexOf("NZA")!=-1) compiledCode = compiledCode.replace(/NZA/g,"p=(a?(a&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");");
  	else if (compiledCode.indexOf("NZY")!=-1) compiledCode = compiledCode.replace(/NZY/g,"p=(y?(y&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");");
  	else if (compiledCode.indexOf("NZX")!=-1) compiledCode = compiledCode.replace(/NZX/g,"p=(x?(x&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");");
  	else if (compiledCode.indexOf("NZB")!=-1) compiledCode = compiledCode.replace(/NZB/g,"p=(b?(b&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");");
	}
	else {
    // Replace flag modification placeholders with real code.
  	compiledCode = compiledCode.replace(/NZA/g,"p=(a?(a&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");")
  	       .replace(/NZY/g,"p=(y?(y&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");")
  	       .replace(/NZX/g,"p=(x?(x&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");")
  	       .replace(/NZB/g,"p=(b?(b&"+N+"):"+Z+")|(p&"+(~(N|Z)&255)+");");
	}

  // Genereate javascript code to create handler function / if last instruction didn't modify pc, then pc needs to be set.
	var compiledFunction = "compiled["+offset+"] = function() { "
      +compiledCode
      +"cyc="+cyct+";"
      +((pcNext<maxPc || lastWasPcModifyingInstruction)?"":"pc="+pcNext)
      +"}";

  // Insert the compiled code block
	eval(compiledFunction);
	
  // Update debug values.
  if (isdebug && traceCount) {
    debug.value+=offset.toString(16)+" "+pcNext.toString(16)+" "+compiledFunction+"\n\n";
    if (--traceCount==0) {
      isdebug=0;
      alert("Trace Compilations Complete");
    }
  }
	// Excecute the code block, if setMaxPc is set then this is a recompile from a write, so don't execute
	if (!setMaxPc) compiled[offset]();

  compiledSectionsCount++;
}

/**
 * Reset the 6502.
 */ 
function Cpu6502Reset() {
  Via1();
  
  // reset debug values.
  compiledSectionsCount = compiledInstructionsCount =
    loopsOptimizedCount = lsrsOptimizedCount = aslsOptimizedCount = 0;
  
  // Init registers
  pc = a = x = y = sp = 0;
  p = R;
  
  // Reset peripheral memory
  for(var i=0x9000; i<0x9130; i++) {
  	m[i]=0;
  }
  // Reset compiled code and code range arrays.
  for(var i=0; i<65536; i++) {
  	compiled[i]=uncompiledCodeHandler;
  	coderange[i]=-3;
  }
  
	for(var i=0; i<8192; i++) {
  	modCount[i]=i;
  }

  // Reload reset vector.
  pc = m[RESET_VECTOR] | (m[RESET_VECTOR+1]<<8);
}

/**
 * Init the 6502.
 */ 
function Cpu6502() {
  // Init ram
  for(var i=0; i<65536; i++) {
  	m[i]=0xCE;
  }
  
  Cpu6502Reset();
  
  // Load roms
  loadCart(chardata,0x8000,0x9000);
  loadCart(basicdata,0xc000,0xe000);
  loadCart(kerneldata,0xe000,0x10000);
  
  // Load reset address
  pc = m[RESET_VECTOR] | (m[RESET_VECTOR+1]<<8);
}


