// 6502 emulator - gui functions
// n. landsteiner, electronic tradion 2005; e-tradion.net

// conf & globals

var runThrou=false;
var runStep, totalSteps;
var maxRun=255;
var simCycleDelay=40;
var loaded=false;

// lookup tables

var hextab= ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
var opctab= [
	['BRK','imp'], ['ORA','inx'], ['???','imp'], ['???','imp'],
	['???','imp'], ['ORA','zpg'], ['ASL','zpg'], ['???','imp'],
	['PHP','imp'], ['ORA','imm'], ['ASL','acc'], ['???','imp'],
	['???','imp'], ['ORA','abs'], ['ASL','abs'], ['???','imp'],
	['BPL','rel'], ['ORA','iny'], ['???','imp'], ['???','imp'],
	['???','imp'], ['ORA','zpx'], ['ASL','zpx'], ['???','imp'],
	['CLC','imp'], ['ORA','aby'], ['???','imp'], ['???','imp'],
	['???','imp'], ['ORA','abx'], ['ASL','abx'], ['???','imp'],
	['JSR','abs'], ['AND','inx'], ['???','imp'], ['???','imp'],
	['BIT','zpg'], ['AND','zpg'], ['ROL','zpg'], ['???','imp'],
	['PLP','imp'], ['AND','imm'], ['ROL','acc'], ['???','imp'],
	['BIT','abs'], ['AND','abs'], ['ROL','abs'], ['???','imp'],
	['BMI','rel'], ['AND','iny'], ['???','imp'], ['???','imp'],
	['???','imp'], ['AND','zpx'], ['ROL','zpx'], ['???','imp'],
	['SEC','imp'], ['AND','aby'], ['???','imp'], ['???','imp'],
	['???','imp'], ['AND','abx'], ['ROL','abx'], ['???','imp'],
	['RTI','imp'], ['EOR','inx'], ['???','imp'], ['???','imp'],
	['???','imp'], ['EOR','zpg'], ['LSR','zpg'], ['???','imp'],
	['PHA','imp'], ['EOR','imm'], ['LSR','acc'], ['???','imp'],
	['JMP','abs'], ['EOR','abs'], ['LSR','abs'], ['???','imp'],
	['BVC','rel'], ['EOR','iny'], ['???','imp'], ['???','imp'],
	['???','imp'], ['EOR','zpx'], ['LSR','zpx'], ['???','imp'],
	['CLI','imp'], ['EOR','aby'], ['???','imp'], ['???','imp'],
	['???','imp'], ['EOR','abx'], ['LSR','abx'], ['???','imp'],
	['RTS','imp'], ['ADC','inx'], ['???','imp'], ['???','imp'],
	['???','imp'], ['ADC','zpg'], ['ROR','zpg'], ['???','imp'],
	['PLA','imp'], ['ADC','imm'], ['ROR','acc'], ['???','imp'],
	['JMP','ind'], ['ADC','abs'], ['ROR','abs'], ['???','imp'],
	['BVS','rel'], ['ADC','iny'], ['???','imp'], ['???','imp'],
	['???','imp'], ['ADC','zpx'], ['ROR','zpx'], ['???','imp'],
	['SEI','imp'], ['ADC','aby'], ['???','imp'], ['???','imp'],
	['???','imp'], ['ADC','abx'], ['ROR','abx'], ['???','imp'],
	['???','imp'], ['STA','inx'], ['???','imp'], ['???','imp'],
	['STY','zpg'], ['STA','zpg'], ['STX','zpg'], ['???','imp'],
	['DEY','imp'], ['???','imp'], ['TXA','imp'], ['???','imp'],
	['STY','abs'], ['STA','abs'], ['STX','abs'], ['???','imp'],
	['BCC','rel'], ['STA','iny'], ['???','imp'], ['???','imp'],
	['STY','zpx'], ['STA','zpx'], ['STX','zpy'], ['???','imp'],
	['TYA','imp'], ['STA','aby'], ['TXS','imp'], ['???','imp'],
	['???','imp'], ['STA','abx'], ['???','imp'], ['???','imp'],
	['LDY','imm'], ['LDA','inx'], ['LDX','imm'], ['???','imp'],
	['LDY','zpg'], ['LDA','zpg'], ['LDX','zpg'], ['???','imp'],
	['TAY','imp'], ['LDA','imm'], ['TAX','imp'], ['???','imp'],
	['LDY','abs'], ['LDA','abs'], ['LDX','abs'], ['???','imp'],
	['BCS','rel'], ['LDA','iny'], ['???','imp'], ['???','imp'],
	['LDY','zpx'], ['LDA','zpx'], ['LDX','zpy'], ['???','imp'],
	['CLV','imp'], ['LDA','aby'], ['TSX','imp'], ['???','imp'],
	['LDY','abx'], ['LDA','abx'], ['LDX','aby'], ['???','imp'],
	['CPY','imm'], ['CMP','inx'], ['???','imp'], ['???','imp'],
	['CPY','zpg'], ['CMP','zpg'], ['DEC','zpg'], ['???','imp'],
	['INY','imp'], ['CMP','imm'], ['DEX','imp'], ['???','imp'],
	['CPY','abs'], ['CMP','abs'], ['DEC','abs'], ['???','imp'],
	['BNE','rel'], ['CMP','iny'], ['???','imp'], ['???','imp'],
	['???','imp'], ['CMP','zpx'], ['DEC','zpx'], ['???','imp'],
	['CLD','imp'], ['CMP','aby'], ['???','imp'], ['???','imp'],
	['???','imp'], ['CMP','abx'], ['DEC','abx'], ['???','imp'],
	['CPX','imm'], ['SBC','inx'], ['???','imp'], ['???','imp'],
	['CPX','zpg'], ['SBC','zpg'], ['INC','zpg'], ['???','imp'],
	['INX','imp'], ['SBC','imm'], ['NOP','imp'], ['???','imp'],
	['CPX','abs'], ['SBC','abs'], ['INC','abs'], ['???','imp'],
	['BEQ','rel'], ['SBC','iny'], ['???','imp'], ['???','imp'],
	['???','imp'], ['SBC','zpx'], ['INC','zpx'], ['???','imp'],
	['SED','imp'], ['SBC','aby'], ['???','imp'], ['???','imp'],
	['???','imp'], ['SBC','abx'], ['INC','abx'], ['???','imp']
];

addrtab= {
	acc:'A',
	abs:'abs',
	abx:'abs,X',
	aby:'abs,Y',
	imm:'#',
	imp:'impl',
	ind:'ind',
	inx:'X,ind',
	iny:'ind,Y',
	rel:'rel',
	zpg:'zpg',
	zpx:'zpg,X',
	zpy:'zpg,Y'
}

// constructor mods (ie4 fix)

var IE4_keyref;
var IE4_keycoderef;

function IE4_makeKeyref() {
	IE4_keyref= new Array();
	IE4_keycoderef= new Array();
	var hex= new Array('A','B','C','D','E','F');
	for (var i=0; i<=15; i++) {
		var high=(i<10)? i:hex[i-10];
		for (var k=0; k<=15; k++) {
			var low=(k<10)? k:hex[k-10];
			var cc=i*16+k;
			if (cc>=32) {
				var cs=unescape("%"+high+low);
				IE4_keyref[cc]=cs;
				IE4_keycoderef[cs]=cc;
			}
		}
	}
}

function _ie4_strfrchr(cc) {
	return (cc!=null)? IE4_keyref[cc] : '';
}

function _ie4_strchcdat(n) {
	cs=this.charAt(n);
	return (IE4_keycoderef[cs])? IE4_keycoderef[cs] : 0;
}

if (!String.fromCharCode) {
	IE4_makeKeyref();
	String.fromCharCode=_ie4_strfrchr;
};
if (!String.prototype.charCodeAt) {
	if (!IE4_keycoderef) IE4_makeKeyref();
	String.prototype.charCodeAt=_ie4_strchcdat;
};

// functions

function getReg(r) {
	switch (r) {
		case 'PC' : return pc;
		case 'AC' : return a;
		case 'XR' : return x;
		case 'YR' : return y;
		case 'SR' : return flags;
		case 'SP' : return sp;
		default : return '';
	}
}

function setReg(r,v) {
	switch (r) {
		case 'PC' : pc=v; break;
		case 'AC' : a=v; break;
		case 'XR' : x=v; break;
		case 'YR' : y=v; break;
		case 'SR' : flags=v; break;
		case 'SP' : sp=v; break;
		default : ;
	}
}

function setRegister(r) {
	var prstr= (r=='PC')? getHexWord(pc) : getHexByte(getReg(r));
	var v=prompt('Please enter a hex value for '+r+':', prstr);
	v=parseInt(v,16);
	if (isNaN(v)) return;
	v=Math.abs(Math.floor(v));
	if (r=='SR') v|=32;
	if (r=='PC') v&=0xffff
	else v&=255;
	setReg(r,v);
	updateReg(r);
	if (r=='PC') disassemble();
}

function flipSRBit(b) {
	if (b==5) return;
	flags^=(1<<b);
	updateReg('SR')
}

function resetProcessor() {
	resetCPU();
	updateReg();
	disassemble()
}

function inialize() {
	window.status='initializing ...';
	resetProcessor();
	loaded=true;
	window.status='ready.';
}

function updateReg(r) {
	if (r==null) {
		updateReg('PC');
		updateReg('AC');
		updateReg('XR');
		updateReg('YR');
		updateReg('SR');
		updateReg('SP');
		writeDisplay('dispCycles', 'total cycles: '+processorCycles);
		return;
	};
	var obj;
	if (r=='PC') writeDisplay('dispPC',getHexWord(pc))
	else writeDisplay('disp'+r,''+getHexByte(getReg(r)));
	if (r=='SR') {
		for (var i=0; i<8; i++) {
			var b=((flags&(1<<i))>0)? 1:0;
			writeDisplay('dispSR'+i,b);
		}
	}
}

function writeDisplay(n,v) {
	var obj;
	if (document.getElementById) obj=document.getElementById(n)
	else if (document.all) obj=document.all[n];
	if (obj) obj.innerHTML=v;
}

function getHexByte(v) {
	return ''+hextab[Math.floor(v/16)]+hextab[v&0x0f]
}

function getHexWord(v) {
	return ''+hextab[Math.floor(v/0x1000)]+hextab[Math.floor((v&0x0f00)/256)]+hextab[Math.floor((v&0xf0)/16)]+hextab[v&0x000f]
}

function disassemble() {
	var instr=ImmediateByte();
	var op1=getHexByte(ByteAt(pc+1));
	var op2=getHexByte(ByteAt(pc+2));
	var adr=getHexWord(pc);
	var ops=getHexByte(instr);
	var disas=opctab[instr][0];
	var adm=opctab[instr][1];
	if (op1==null) op1=0;
	if (op2==null) op2=0;
	switch (adm) {
		case 'imm' :
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;#$'+op1;
			break;
		case 'zpg' :
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;$'+op1;
			break;
		case 'acc' :
			ops+='&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;A';
			break;
		case 'abs' :
			ops+='&nbsp;'+op1+'&nbsp;'+op2;
			disas+='&nbsp;$'+op2+op1;
			break;
		case 'zpx' :
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;$'+op1+',X';
			break;
		case 'zpy' :
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;$'+op1+',Y';
			break;
		case 'abx' :
			ops+='&nbsp;'+op1+'&nbsp;'+op2;
			disas+='&nbsp;$'+op2+op1+',X';
			break;
		case 'aby' :
			ops+='&nbsp;'+op1+'&nbsp;'+op2;
			disas+='&nbsp;$'+op2+op1+',Y';
			break;
		case 'iny' :
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;($'+op1+'),Y';
			break;
		case 'inx' :
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;($'+op1+',X)';
			break;
		case 'rel' :
			var opv=ByteAt(pc+1);
			var targ=pc+2;
			if (opv&128) targ-=(opv^255)+1
			else targ +=opv;
			targ&=0xffff;
			ops+='&nbsp;'+op1+'&nbsp;&nbsp;&nbsp;';
			disas+='&nbsp;$'+getHexWord(targ);
			break;
		case 'ind' :
			ops+='&nbsp;'+op1+'&nbsp;'+op2;
			disas+='&nbsp;($'+op2+op1+')';
			break;
		default :
			ops+='&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
	};
	writeDisplay('dispStep',adr+'&nbsp;'+ops+'&nbsp;&nbsp;'+disas)
}

function memLookup() {
	var v=prompt('Please enter an address (0000-FFFF):');
	if (v==null) return;
	if (v=='') v=0;
	var m=parseInt(v,16);
	if (isNaN(m)) {
		alert('Sorry:\n"'+v+'" is not a valid hex number!')
	}
	else {
		m=Math.abs(Math.floor(m))&0xfff0;
		var s='';
		var b1=getHexWord(m);
		var b2=getHexWord(m+15);
		s+=b1+': ';
		for (var i=0; i<16; i++) {
			s+=getHexByte(ByteAt(m+i));
			if (i==7) s+='\n'+getHexWord(m+8)+': '
			else if (i<15) s+=' ';
		};
		alert('Memory at '+b1+'-'+b2+':\n \n'+s)
	}
}

function showMem() {
	var showASCII=document.MemLoader.showASCII.checked;
	var addr=parseInt(document.MemLoader.memAddr.value,16);
	if (isNaN(addr)) {
		alert('Sorry:\n"'+v+'" is not a valid hex number!')
	}
	else {
		addr=Math.abs(Math.floor(addr))&0xffff;
		var s='';
		var s2='';
		for (var k=0; k<8; k++) {
			for (var i=0; i<16; i++) {
				var da=addr+i;
				if (da>0xffff) {
					document.MemLoader.mem.value=s+'\n';
					return
				};
				if (i%8==0) s+=':'+getHexWord(da)+'  ';
				var b=ByteAt(da);
				s+=getHexByte(b);
				if (showASCII) {
					s2+= ((b>=32) && (b<=126))? String.fromCharCode(b) : '.';
				};
				if ((i==7) || (i==15)) {
					if (showASCII) {
						s+='  ; '+s2
						s2=''
					};
					if ((k<7) || (i<15)) s+='\n';
				}
				else s+=' ';
			};
			addr+=16
		};
		document.MemLoader.mem.value=s
	}
}

function loadMem() {
	var addr=parseInt(document.MemLoader.memAddr.value,16);
	if (isNaN(addr)) {
		alert('Sorry:\n"'+v+'" is not a valid hex number!')
	}
	else {
		addr=Math.abs(Math.floor(addr))&0xffff;
		loadData(addr,document.MemLoader.mem.value);
	}
}

function loadData(addr,data) {
	window.status='loading data to '+getHexWord(addr)+' ...';
	var lc='';
	var ofs=0;
	var mode=1;
	data=data.toUpperCase();
	for (var i=0; i<data.length; i++) {
		var c=data.charAt(i);
		if (mode==2) {
			if ((c=='\r') || (c=='\n')) mode=1;
		}
		else if (((c>='0') && (c<='9')) || ((c>='A') && (c<='F'))) {
			if (mode==1) {
				if (lc) {
					RAM[addr++]=parseInt(lc+c,16);
					if (addr>0xffff) break;
					lc=''
				}
				else {
					lc=c
				}
			}
		}
		else if (c==':') mode=0
		else if (c==';') mode=2
		else mode=1;
	};
	window.status='ready.';
}

function toggleASCII() {
	var showASCII=document.forms.MemLoader.showASCII;
	showASCII.checked=(!showASCII.checked)
}

function loadRom(addr,data) {
	window.status='loading data to '+getHexWord(addr)+' ...';
	var ofs=0;
	for (var i=0; i<data.length; i+=2) {
		RAM[addr++]=parseInt(data.charAt(i)+data.charAt(i+1),16);
	};
	window.status='ready.';
}

function loadC64Roms() {
	if (confirm('Sure to load C64 ROMs?\nMemory transfer could take some time ...')) {
		if ((self.c64loader) && (self.c64loader.location)) self.c64loader.location.href='c64ROMs/romloader.html'
		else alert('Sorry.\nIFRAME not found.');
	}
}

function masterReset() {
	if (confirm('Reset all registers?')) resetProcessor();
}

function run() {
	if (confirm('Sure to start a continous run?\n(Stops on "BRK". Lengthy code will be prompted.)')) {
		runThrou=true;
		runStep=0;
		totalSteps=0;
		simStep()
	}
}

function singleStep() {
	runThrou=false;
	simStep()
}

function simStep() {
	processorLoop()
	updateReg();
	disassemble();
	// continous?
	if (runThrou) {
		runStep++;
		if (breakFlag) {
			alert('Virtual 6502: Interrupt\nHalted on "BRK".\nSee stack (0100-01FF) for details.');
			return;
		}
		if (runStep>maxRun) {
			totalSteps+=runStep;
			runStep=0;
			if (confirm('Virtual 6502: Lengthy code ('+totalSteps+' steps).\nContinue continous run?')==false) return;
		};
		setTimeout('simStep()',40);
	}
}

onload=inialize;


// eof