// virtual univac
// n.landsteiner 2001
// www.masswerk.at


// definition & setup

var parityRef=new Array(
	64, 1, 2, 67, 4, 69, 70, 7, 8, 73, 74, 11, 76, 13, 14, 79,
	16, 81, 82, 19, 84, 21, 22, 87, 88, 25, 26, 91, 28, 93, 94, 31,
	32, 97, 98, 35, 100, 37, 38, 103, 104, 41, 42, 107, 44, 109, 110, 47,
	112, 49, 50, 115, 52, 117, 118, 55, 56, 121, 122, 59, 124, 61, 62, 127
	);
var parityCheck=new Array();
for (var i=0; i<parityRef.length; i++) {
	parityCheck[parityRef[i]]=true;
};
var isDigit=new Array();
var digitVal=new Array();
for (var i=3; i<13; i++) {
	isDigit[i]=1; digitVal[i]=i-3;
}

var btnRef= [
	'CTB', ['all', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
	'ITS', ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'minus'],
	'OS', ['m', 'a', 'x', 'l', 'f', 'c', 'cr', 'syi', 'empty']
	];
var btn=new Array();
for (var i=0; i<btnRef.length; i+=2) {
	btn[btnRef[i]]=new Array();
	for (var n in btnRef[i+1]) btn[btnRef[i]][n]=false;
};

// memory
var mem=new Array();
for (var i=0; i<1000; i++) {
	mem[i]=new Array();
}

// servos

function ServoUnit() {
	this.tape='';
	this.pos=0;
	this.b=false;
	this.f=false;
	this.r=false;
	this.w=false;
	this.lock=false;
	this.fir=true;
	this.bir=true
}
var servo=new Array();
for (var i=0; i<10; i++) servo[i]=new ServoUnit();
var tapeBlockHead=false;
var currServo=0;
var currServoDir=0;

var s_cd, s_t, s_ds, s_lim, s_bh, s_fd, s_ld;

// registers
var SR=new Array();
for (var i=1; i<7; i++) {
	SR[i]=new Array();
};
var regRef=new Array('A','X','L','F','CC','CR','V','Y','I','O', 'SYI');
var regLen=new Array( 1,  1,  1,  1,  1,   1,   2,  10, 60, 60,  1);
var r=new Array();
for (var i=0; i<regRef.length; i++) r[regRef[i]]=new Array();

var ffRef=new Array(
	'IER', 0, 'OR', 0, 'IEROR', 0, 'GE3', 0, 'REP', 0, 'STL', 0,
	'STP', 1, 'TO', 1, 'TS', 0, 'S1CP', 0, 'S1X', 0, 'OF', 0,
	'IRT', 0, 'DR', 0, 'RV', 0, 'FB', 0, 'RI', 0,
	'WI', 0, 'ROL', 0, 'IPR', 0, 'D12', 0, 'IPE', 0,
	'SYI1', 1, 'SYI2', 0, 'OPR', 0
	);
var FF=new Array();

var egRef=new Array(
	'HSBOE',34, 'AdderAlph',38, 'FTOUT',54, 'FTINT',55, 'TANK',56,
	'SERVO',57, 'IS720',58, 'ISOE',59, 'OSOE',60, 'IOINT',61, 'TAPE',62
	);
var EG=new Array();
var egNeon=new Array();
for (var i=0; i<egRef.length; i+=2) egNeon[egRef[i]]=egRef[i+1];

var counterRef=new Array('CY',2,0, 'PC',4,0, 'MQC',4,3, 'SYIC',4,3, 'SYOC',4,3, 'ITC',3,1, 'OTC',3,1);
var cntL=new Array();
var cntOS=new Array();
for (var i=0; i<counterRef.length; i+=3) {
	cntL[counterRef[i]]=counterRef[i+1];
	cntOS[counterRef[i]]=counterRef[i+2]
};

var swiRef=new Array(58, 70, 75, 76, 79);
var SWI=new Array();

var siRef=new Array('SR', 'IOS', 'EG', 'BP');
var siColorRef=new Array('B','Y','G','');
var SI=new Array();
var siColor=new Array();
for (var i=0; i<siRef.length; i++) {
	siColor[siRef[i]]=siColorRef[i];
	SI[siRef[i]]=0
}

//counters
var CY,PC,MQC,ISC,OSC,ITC,OTC;
var ftM,ftC, CP,adder_of;

// const
var d0=67;
var roundOff=new Array(3,3,3,3,3,3,3,3,3,3,3,8);
var roundOffMult=new Array(3,8,3,3,3,3,3,3,3,3,3,3);
var w0=new Array(3,3,3,3,3,3,3,3,3,3,3,3);
var wb0=new Array(0,0,0,0,0,0,0,0,0,0,0,0);

var nextStep='';
var stallTimer=setTimeout('//',100); clearTimeout(stallTimer);
var stall="setFF('STL',1)";


// resets

function masterReset() {
	resetSwitches();
	resetErrors();
	resetFFs();
	resetCounters();
	resetRegs();
	resetMem();
	setSrZero(0,0);
	copyWord(w0, r.CC);
	resetServos();
	nextStep='cycle()';
	stallTimer=setTimeout(stall,3000)
}

function resetMem() {
	for (var i=0; i<1000; i++) {
		for (var d=0; d<12; d++) mem[i][d]=3;
	}
}
function resetCounters() {
	for (var i=0; i<counterRef.length; i+=3) setCounter(counterRef[i],0);
}
function resetRegs() {
	for (var i=0; i<regRef.length; i++) {
		if (regRef[i]=='A') copyWord(roundOff, r.A)
		else copyWord(wb0, r[regRef[i]]);
	}
}
function resetFFs() {
	for (var i=0; i<ffRef.length; i+=2) setFF(ffRef[i],ffRef[i+1]);
}
function resetSwitches() {
	for (var i=0; i<swiRef.length; i++) {
		document.images['sw'+swiRef[i]].src=imgRes.switch_md.src;
		SWI[swiRef[i]]=0
	};
	switchIOS('md');
	switchButton('ITS','1',2);
	switchButton('OS','m',2);
	switchButton('CTB','rls');
	for (var i=0; i<siRef.length; i++) setSI(siRef[i],0);
	switchFlipDn(62,1)
}
function resetErrors() {
	for (var i=0; i<egRef.length; i+=2) setError(egRef[i],0);
}

function clearC() {
	for (var i=0; i<12; i++) r.CC[i]=3;
	setSrZero(0,d0);
	setSI('SR',0);
	setCounter('PC',0);
	setCounter('CY',0);
	setFF('STP',1)
}
function initialClear() {
	for (var i=0; i<regRef.length; i++) {
		if ((regRef[i]=='CC') ||  (regRef[i]=='CR') || (regRef[i]=='I') ||  (regRef[i]=='O')) continue;
		if (regRef[i]=='A') copyWord(roundOff, r.A)
		else copyWord(wb0, r[regRef[i]]);
	}	
}
function generalClear() {
	resetCounters();
	resetFFs();
	resetErrors();
	setFF('STL',1);
}
function clearFFTS() {
	setFF('TS',0);
	setFF('TO',1);
	setFF('REP',0)
}

function resetServos() {
	for (var i=0; i<10; i++) resetServo(i);
	tapeBlockHead=false;
	currServo=0;
	currServoDir=0
}

function resetServo(i) {
	with (servo[i]) {
		pos=0;
		b=false;
		f=false;
		r=false;
		w=false;
		lk=false;
		rw=false
		fir=true;
		bir=true
	};
	document.images['neS'+i+'ok'].src=imgRes.neonG_hi.src;
	document.images['neS'+i+'r'].src=imgRes.neon_lo.src;
	document.images['neS'+i+'f'].src=imgRes.neon_lo.src;
	document.images['neS'+i+'b'].src=imgRes.neon_lo.src;
	document.images['neS'+i+'w'].src=imgRes.neon_lo.src;
	document.images['neS'+i+'rw'].src=imgRes.neon_lo.src;
	document.images['neS'+i+'lk'].src=imgRes.neon_lo.src
}


// interface

function switchFlipUp(sw,lk) {
	document.images['sw'+sw].src=imgRes.switch_hi.src;
	sw=parseInt(sw,10);
	if ((sw>0) && (sw<28)) setSrSwitch(sw,1)
	else if ((sw>27) && (sw<31)) {
		setSrZero(sw-27,d0);
		setSI('SR',0)
	}
	else if (sw==69) setFF('CT',1)
	else if (sw==77) clearFFTS()
	else if (sw==78) initialClear()
	else if (SWI[sw]!=2) SWI[sw]=2;
	if (!lk) setTimeout("switchNeutral('"+sw+"')",250);
}

function switchFlipDn(sw,lk) {
	document.images['sw'+sw].src=imgRes.switch_lo.src;
	sw=parseInt(sw,10);
	if ((sw>0) && (sw<28)) setSrSwitch(sw,0)
	else if ((sw>27) && (sw<31)) {
		setSrZero(sw-27,0);
		if (!SWI[34]) setSI('SR',1);
	}
	else if (sw==66) stop()
	else if (sw==67) setCounter('CY',0)
	else if (sw==69) setFF('CT',0)
	else if (sw==73) clearC()
	else if (sw==77) generalClear()
	else if (sw==78) resetMem()
	else if (SWI[sw]!=1) {
		SWI[sw]=1;
		if (sw<64) {
			setSI('EG',SI.EG+1);
			if (sw==34) setError('HSBOE',0)
			else if (sw==58) setError('IS720',0)
			else if (sw==62) setError('TAPE',0);
		}
		else if (sw==70) setSI('BP',SI.BP+1);
	};
	if (!lk) setTimeout("switchNeutral('"+sw+"')",250);
}

function switchFlipMd(sw) {
	document.images['sw'+sw].src=imgRes.switch_md.src;
	sw=parseInt(sw,10);
	if ((sw>30) && (SWI[sw]!=0)) {
		SWI[sw]=0;
		if (sw<64) setSI('EG',SI.EG-1)
		else if (sw==70) setSI('BP',SI.BP-1);
	}
}

function switchNeutral(sw) {
	document.images['sw'+sw].src=imgRes.switch_md.src;
}

function setNeon(n,x) {
	if (!document.images['ne'+n]) alert(n);
	document.images['ne'+n].src=(x)?imgRes.neon_hi.src:imgRes.neon_lo.src;
}

function switchButton(gr, bt, m) {
	if (m==1) {
		if (btn[gr][bt]) {
			btn[gr][bt]=false;
			setButton(gr, bt, 0)
		}
		else {
			btn[gr][bt]=true;
			setButton(gr, bt, 1)
		}
	}
	else if ((m==2) || (bt=='rls')) {
		if (bt!='rls') {
			if (btn[gr][bt]) return;
			btn[gr][bt]=true;
			setButton(gr, bt, 1)
		}
		else {
			setTimeout('setButton("'+gr+'","rls",0)',250);
		};
		for (var b in btn[gr]) {
			if ((btn[gr][b]) && (b!=bt)) {
				btn[gr][b]=false;
				setButton(gr, b, 0);
				if (document.images['ne'+gr+'_'+b]) setNeon(gr+'_'+b,0);
			}
		}
	}
	else {
		btn[gr][bt]=(btn[gr][bt])? false:true;
		setButton(gr, bt, 1);
		setTimeout('setButton("'+gr+'","'+bt+'",0)',250)
	};
	if (gr=='CTB') {
		var si=(SWI[70])? 1:0;
		for (var b in btn.CTB) {
			if (b=='rls') continue;
			if (btn.CTB[b]) si++;
		};
		setSI('BP',si)
	};
	if (document.images['ne'+gr+'_'+bt]) setNeon(gr+'_'+bt,btn[gr][bt]);
}

function setButton(gr, bt, x) {
	document.images['bt_'+gr+'_'+bt].src=(x)? imgRes['bt_'+bt+'_hi'].src:imgRes['bt_'+bt+'_lo'].src;
}

function switchIOS(m) {
	document.images.swios.src=imgRes['ios_'+m].src;
	if (m=='up') setSI('IOS',3)
	else if (m=='dn') setSI('IOS',1)
	else if (m=='le') setSI('IOS',2)
	else if (m=='re') setSI('IOS',4)
	else setSI('IOS',0);
}


// inner functionality

function copyWord(a,b) {
	for (var i=0; i<12; i++) b[i]=a[i];
}

function getParity(d) {
	var p=1;
	var m=1;
	while (m<64) {
		if (d&m) p=!(p);
		m<<=1
	};
	return (p)? 1:0;
}

function normalize(x) {
	if (x<10) return '00'+x
	else if (x<100) return '0'+x
	else return x
}

function setFF(f,x) {
	FF[f]=x;
	setNeon(f,x)
}

function setCounter(c,x) {
	self[c]=x;
	if (x<0) x+=10;
	x+=cntOS[c];
	for (var i=0; i<cntL[c]; i++) setNeon(c+i,x&(1<<i));
}

function setError(e,x) {
	EG[e]=x;
	setNeon(egNeon[e],x);
}

function setSrZero(reg,m) {
	if (reg!=2) {
		setSR(1,m);
		setSR(2,m)
	};
	if (reg!=1) {
		setSR(4,m);
		setSR(5,m);
		setSR(6,m)
	}
}

function setSI(n,x) {
	SI[n]=x;
	document.images['si'+n].src=(x)? imgRes['neon'+siColor[n]+'_hi'].src:imgRes.neon_lo.src;
}

function setSrSwitch(b,x) {
	setSI('SR',1);
	if (b==1) setSR(1,(SR[1]&63)|(64*x))
	else if (b<8) {
		var p=1<<(7-b);
		if (x) setSR(1,SR[1]|p)
		else setSR(1,SR[1]&(127-p))
	}
	else {
		if (b>12) b+=3
		else b-=2;
		var d=Math.ceil(b/5);
		if (b%5==1) {
			setSR(d,(SR[d]&15)|(64*x))
		}
		else {
			var p=1<<(5-(b-(d-1)*5));
			if (x) setSR(d,SR[d]|p)
			else  setSR(d,SR[d]&(79-p))
		}
	}
}

function setSR(n,d, pm) {
	if (n==3) return;
	var l, os;
	if ((pm) && (SI.SR)) setSI('SR',0);
	if (n==1) {
		os=1;
		l=6
	}
	else {
		os=(n==2)? n*5-2: n*5-7;
		l=4;
		if (!pm) d&=79;
	};
	if (pm) d=parityRef[d&63]  // get parity
	else d&=127;         // set as is
	SR[n]=d;
	setNeon(os, d&64);
	for (var i=0; i<l; i++) {
		setNeon(os+l-i, (d&1<<i));
	};
	if (n<3) analyzeSR(n);
}

var srNos= new Array(7, 12, 0, 17, 22, 27);
var srOS;

function swapSR() {
	var ps=new Array( parityRef[r.CR[0+srOS]], parityRef[r.CR[1+srOS]], 0, parityRef[r.CR[3+srOS]], parityRef[r.CR[4+srOS]], parityRef[r.CR[5+srOS]]);
	for (var i=0; i<6; i++) {
		if (i==2) continue;
		SR[i+1]=ps[i];
		setNeon(srNos[i], ps[i]&1);
		setNeon(srNos[i]-1, ps[i]&2);
		setNeon(srNos[i]-2, ps[i]&4);
		setNeon(srNos[i]-3, ps[i]&8);
		if (i==0) {
			setNeon(3, ps[0]&16);
			setNeon(2, ps[0]&32);
			setNeon(1, ps[0]&64)
		}
		else {
			setNeon(srNos[i]-4, ps[i]&64)
		}
	};
	analyzeSR(1);
	analyzeSR(2);
	if (SI.SR) setSI('SR',0);
}

function analyzeSR(n) {
	if (n>2) return;
	var s='SR'+n+'c';
	var c=SR[n]&15;
	for (var i=n; i<=12; i++) {
		setNeon(s+i, (i==c));
	};
	if (n==1) {
		if ((c<1) || (c>12)) {
			setNeon('SR1r0', 0);
			setNeon('SR1r1', 0);
			setNeon('SR1r2', 0);
			setNeon('SR1r3', 0);
		}
		else {
			var z1=SR[n]&32;
			var z2=SR[n]&16;
			setNeon('SR1r0', ((!z1) && (!z2)));
			setNeon('SR1r1', ((!z1) && (z2)));
			setNeon('SR1r2', ((z1) && (!z2)));
			setNeon('SR1r3', ((z1) && (z2)));
		}
	}
}


// cycling

function doTO(toLvl) {
	clearTimeout(stallTimer);
	stallTimer=setTimeout(stall,3000);
	setTO();
	if (!toLvl) {
		setCounter('PC',0);
		setCounter('MQC',0);
		nextStep='cycle()';
		for (var n in EG) {
			if (EG[n]) {
				nextStep='cycle()';
				return
			}
		}
	};
	if (FF.STP) {
		nextStep='cycle()';
		return
	};
	if (SI.IOS>toLvl) {}
	else setTimeout(nextStep,1)
}

function setTS() {
	setFF('TO',0); setFF('TS',1)
}
function setTO() {
	setFF('TS',0); setFF('TO',1)
}

function stepFromError() {
	setError('FTINT',0);
	setErro('HSBOE',0);
	setError('AdderAlph',0);
	setFF('OF',0);
	setFF('IER',0);
	setFF('OR',0);
	setFF('IEROR',0);
	setFF('GE3',0);
	setFF('REP',0);
	setFF('S1CP',0);
	setFF('S1X',0);
	setCounter('PC',0);
	setCounter('MQC',0);
	nextStep='cycle()';
	doTO(10);
}

function cycle() {
	if (CY==0) {
		// alpha
		setTS();
		if (FF.OF) copyWord(w0, r.CR)
		else copyWord(r.CC, r.CR);
		setSR(6,r.CR[11],1);
		setSR(5,r.CR[10],1);
		setSR(4,r.CR[9],1);
		setSR(2,r.CR[7],1);
		setSR(1,r.CR[6],1);
		incCY();
	}
	else if (CY==1) {
		// beta
		var m=getSRM();
		if (m<0) return;
		copyWord(mem[m], r.CR);
		setTO();
		if (FF.OF) setFF('OF',0)
		else incCC();
		setTS();
		//setSR(6,r.CR[5],1);
		//setSR(5,r.CR[4],1);
		//setSR(4,r.CR[3],1);
		//setSR(2,r.CR[1],1);
		//setSR(1,r.CR[0],1);
		srOS=0;
		swapSR();
		incCY();
	}
	else {
		// gamma, delta
		var m=getSRM();
		if (m<0) return;
		ftM=m;
		var inst=getSRI1();
		if (inst<0) return;
		doFT(inst);
	}
}

function prepareDelta() {
	//setSR(6,r.CR[11],1);
	//setSR(5,r.CR[10],1);
	//setSR(4,r.CR[9],1);
	//setSR(2,r.CR[7],1);
	//setSR(1,r.CR[6],1)
	srOS=6;
	swapSR();
}

function getSRM() {
	var m1=SR[4];
	var m2=SR[5];
	var m3=SR[6];
	if (SI.SR) {
		if ((!SWI[34]) && (!(parityCheck[m1]+parityCheck[m2]+parityCheck[m3]))) {
			setError('HSBOE',1);
			return -1
		};
		setSI('SR',0)
	};
	m1&=63; m2&=63; m3&=63;
	if ((!isDigit[m1]) || (!isDigit[m2])) {
		setError('TANK',1);
		alert ('Static register coding error:\n++ tank selection error on SR4 ('+m1+') or SR5 ('+m2+') ++');
		return -1
	};
	setTS();
	if (!isDigit[m3]) {
		if (!((m3&48) && (isDigit[m3&15]))) {
			setFF('TS',0);
			alert('Static register coding error:\n++ SR6 ('+m3+') outside numeric range ++')
		}
		else {
			alert('Static register coding error:\n++ time selection error on SR6 ('+m3+') ++');
		}
		return -1
	};
	return parseInt(digitRef[m1]+digitRef[m2]+digitRef[m3],10);
}

function getSRI1() {
	var m1=SR[1];
	var m2=SR[2];
	if (SI.SR) {
		if ((!SWI[34]) && (!(parityCheck[m1]+parityCheck[m2]))) {
			setError('HSBOE',1);
			return -1
		};
		setSI('SR',0)
	};
	return m1&=63;
}

function getSRI2() {
	return SR[2]&=63;
}

function incCY() {
	//if (SWI[70]!=1) {
		if (CY==2) prepareDelta();
		CY++;
		if (CY>3) CY=0;
		setCounter('CY',CY);
	//};
	nextStep='cycle()';
	doTO(0)
}

function incCC() {
	var c=0;
	for (var i=11; i>8; i--) {
		r.CC[i]+=c+1;
		if (r.CC[i]>12) {
			c=1;
			r.CC[i]=3
		}
		else return
	}
}

function doFT(d) {
	var v=digitRef[d];
	//alert('instruction: '+v+'\nCY: '+CY+'\nCC: '+translate(r.CC));
	if (d==21) ft_m2X('A')       //B
	else if (d==38) ft_m2X('L')  //L
	else if (d==25) ft_m2F()     //F
	else if (d==56) ft_mwT('V',1,1) //V
	else if (d==59) ft_mwT('Y',9,1) //Y
	else if (d==22) ft_R2m('A',1)   //C
	else if (d==27) ft_R2m('A')     //H
	else if (d==26) ft_R2m('F')     //G
	else if (d==36) ft_R2m('X')     //J
	else if (d==57) ft_mwT('V',1,0) //W
	else if (d==60) ft_mwT('Y',1,0) //Z
	else if (d==37) ft_A2L()       //K
	else if (d==44) ft_CC2m()      //R
	else if (d==24) ft_extract()   //E
	else if (d==18) ft_shift(1,0)  //.
	else if (d==19) ft_shift(0,0)  //;
	else if (d== 2) ft_shift(1,1)  //-
	else if (d== 3) ft_shift(0,1)  //0
	else if (d==20) ft_add(0)   //A
	else if (d==53) ft_add(1)   //S
	else if (d==58) ft_addX()   //X
	else if (d==39) ft_mult(0)  //M
	else if (d==40) ft_mult(1)  //N
	else if (d==42) ft_mult(2)  //P
	else if (d==23) ft_div()    //D
	else if (d==55) ft_utc()    //U
	else if (d==43) ft_cmp()    //Q
	else if (d==54) ft_cmp(1)   //T
	else if (d==4) ft_read(1,0) //1
	else if (d==5) ft_read(0,0) //2
	else if (d==6) ft_read(1,1) //3
	else if (d==7) ft_read(0,1) //4
	else if ((d==8) || (d==10)) ft_write() //5,7
	
	else if (d==17) {  //.
		if (SWI[70]) setFF('STP',1);
		endFT()
	}
	else if (d==12) {  //9
		setFF('STP',1);
		endFT()
	}
	else {
		// FT-error in TS
		nextStep='cycle()'
	}
}

function endFT() {
	if (FF.OF) {
		setFF('STP',(getSRI2()==2))
		nextStep='cycle()'
		return
	};
	incCY();
}

// transfer instructions 

function ft_m2X(reg) {
	for (var i=0; i<12; i++) r[reg][i]=r.X[i]=mem[ftM][i];
	endFT()
}
function ft_m2F() {
	copyWord(mem[ftM], r.F);
	endFT()
}
function ft_mwT(reg,l,r2m) {
	var os;
	if (PC==0) {
		ftC=ftM%10;
		ftM-=ftC;
		os=0
	}
	else {
		setTS();
		os=PC*12;
	};
	if (r2m) {
		for (var i=0; i<12; i++) r[reg][os+i]=mem[ftM+ftC][i];
	}
	else {
		for (var i=0; i<12; i++) mem[ftM+ftC][i]=r[reg][os+i];
	}
	ftC=(ftC+1)%10;
	if (PC<l) {
		setCounter('PC',PC+1);
		nextStep='ft_rmT("'+reg+'",'+l+','+r2m+')';
		doTO(3)
	}
	else endFT()
}
function ft_R2m(reg,m) {
	copyWord(r[reg], mem[ftM]);
	if (m) copyWord(w0, r.A);
	endFT()
}
function ft_A2L() {
	copyWord(r.A, r.L);
	copyWord(w0, r.A);
	endFT()
}
function ft_CC2m() {
	copyWord(m[ftM], r.CC[i]);
	m[ftM][5]=55; //u
	endFT()
}
function ft_extract() {
	endFT();
}
function ft_shift(rt,s) {
	if (PC==0) {
		var l=getSRI2();
		if (!isDigit[l]) ftC=-14
		else ftC=parseInt(l,10)-4;
		if (ftC==-1) {
			endFT();
			return
		}
	}
	else {
		if (PC==13) {
			setError('FTINT',1)
			nextStep='cycle()'
			doTO();
			return
		};
		setTS();
	}
	if (rt) {
		for (var i=11; i>s; i--) r.A[i]=r.A[i-1];
		r.A[s]=3
	}
	else {
		for (var i=s; i<10; i++) r.A[i]=r.A[i+1];
		r.A[11]=3
	};
	if (PC<ftC) {
		setCounter('PC',PC+1);
		nextStep='ft_shift('+rt+','+s+')';
		doTO(3)
	}
	else endFT()
}

// choice instructions

function ft_utc() {
	for (var i=9; i<12; i++) r.CC[i]=r.CR[i];
	endFT();
}

function ft_cmp(m) {
	if (PC==0) {
		var v;
		if (m) {
			v=0;
			for (var i=11; i>=0; i--)  v=(r.A[i]>r.L[i]);
		}
		else {
			v=1;
			for (var i=11; i>=0; i--) {
				if (r.A[i]!=r.L[i]) {
					v=0;
					break
				}
			}
		};
		setFF('CT',v);
		var n=getSRI2();
		if (n<0) {
			setEG('FTINT',1);
			return
		}
		else if ((btn.CTB.all) || (btn.CTB[n])) setFF('STP',1);
		nextStep='ft_cmp()';
		setCounter('PC',1);
		doTO(1);
	}
	else {
		setTS();
		if (FF.STP) ft_utc()
		else endFT()
	}
}


// arithmetic instructions

function ft_add(sub) {
	if (PC==0) {
		copyWord(mem[ftM], r.X);
		nextStep='ft_add('+sub+')';
		setCounter('PC',1);
		doTO(1)
	}
	else {
		setTS();
		adder(r.X,sub,0);
		setFF('OF',adder_of);
		endFT()
	}
}
function ft_addX() {
	adder(r.X,0,0);
	setFF('OF',adder_of);
	endFT()
}

function ft_mult(m) {
	nextStep='ft_mult('+m+')';
	if (PC==0) {
		copyWord(mem[ftM], r.X);
		if (m==1) {
			r.X[0]=(mem[ftM][0]==2)? 3:2;
		};
		copyWord(r.L, r.A);
		CP=comparator(r.A,r.X,1);
		r.A[0]=3;
		r.F[0]=3;
		r.L[0]=3;
		setCounter('PC',1);
		doTO(1)
	}
	else if (PC<3) {
		setTS();
		adder(r.L,0,1);
		setCounter('PC',PC+1);
		doTO(1)
	}
	else if (PC==3) {
		setTS();
		copyWord(r.A, r.F);
		if (m==2) copyWord(w0, r.A)
		else copyWord(roundOffMult, r.A);
		setCounter('MQC',0);
		setCounter('PC',PC+1);
		doTO(1)
	}
	else if (PC<15) {
		setTS();
		setFF('IER',0);
		setCounter('MQC',digitVal[r.X[11]]*-1);
		setFF('REP',1);
		ft_innerMult()
	}
	else {
		setTS();
		setFF('IER',0);
		for (var i=11; i>=0; i--) r.X[i]=r.X[i-1];
		r.X[0]=CP;
		r.A[0]=CP;
		endFT()
	}
}

function ft_innerMult() {
	if (!FF.TS) setTS();
	if (MQC<0) {
		setFF('GE3',(MQC<=-3));
		if (FF.GE3) {
			adder(r.F,0,1);
			setCounter('MQC',MQC+3);
		}
		else {
			adder(r.L,0,1);
			setCounter('MQC',MQC+1);
		};
		if (SI.IOS==4) {
			nextStep='ft_innerMult()';
			doTO(3)
		}
		else {
			setTimeout('ft_innerMult()',1)
		}
	}
	else {
		setFF('REP',0);
		setFF('IER',1);
		for (var i=11; i>=0; i--) r.X[i]=r.X[i-1];
		r.X[0]=r.A[11];
		for (var i=11; i>=0; i--) r.A[i]=r.A[i-1];
		r.A[0]=3;
		setCounter('PC',PC+1);
		nextStep='ft_mult()';
		if (PC<15) doTO(2)
		else doTO(1);
	}
}

function ft_div() {
	nextStep='ft_div()';
	if (PC==0) {
		copyWord(mem[ftM], r.A);
		CP=comparator(r.A,r.L,1);
		setCounter('MQC',0);
		setCounter('PC',1);
		doTO(1)
	}
	else if (PC==1) {
		setTS();
		for (var i=0; i<11; i++) r.A[i]=r.A[i+1];
		r.A[11]=3;
		r.L[0]=3;
		setCounter('PC',2);
		setFF('IEROR',1);
		doTO(1)
	}
	else if (PC<14) {
		setTS();
		setFF('OR',0);
		adder_of=false;
		setFF('REP',1);
		ft_innerDiv()
	}
	else if (PC==14) {
		setTS();
		setFF('IEROR',0);
		setFF('OR',0);
		copyWord(r.X, r.A);
		setCounter('PC',PC+1);
		doTO(1)
	}
	else {
		setTS();
		adder(roundOff,0,1);
		for (var i=11; i>0; i--) {
			r.A[i]=r.A[i-1];
			r.X[i]=r.X[i-1]
		};
		r.A[0]=CP;
		r.X[0]=CP;
		endFT()
	}
}

function ft_innerDiv() {
	if (!FF.TS) setTS();
	if (FF.IEROR) {
		adder(r.L,1,1);
		if (!adder_of) {
			setCounter('MQC',MQC+1);
			if (MQC>10) {
				setFF('OF',1);
				nextStep='cycle()';
				endFT();
				return
			}
			else {
				if (SI.IOS==4) {
					nextStep='ft_innerDiv()';
					doTO(3);
				}
				else setTimeout('ft_innerDiv()',1);
				return
			}
		};
		setFF('REP',0);
		setFF('OR',1);
		for (var i=0; i<11; i++) {
			r.X[i]=r.X[i+1];
			r.A[i]=r.A[i+1]
		}
		r.X[11]=MQC+3;
		r.A[11]=3;
		setCounter('MQC',-10);
		setFF('IEROR',0)
	}
	else {
		adder(r.L,0,1);
		if (!adder_of) {
			setCounter('MQC',MQC+1);
			if (SI.IOS==4) {
				nextStep='ft_innerDiv()';
				doTO(3);
			}
			else setTimeout('ft_innerDiv()',1);
			return
		};
		setFF('REP',0);
		setFF('OR',1);
		for (var i=0; i<11; i++) {
			r.X[i]=r.X[i+1];
			r.A[i]=r.A[i+1]
		}
		r.X[11]=(MQC*-1)+2;
		r.A[11]=3;
		setCounter('MQC',0);
		setFF('IEROR',1)
	};
	setCounter('PC',PC+1);
	nextStep='ft_div()';
	doTO(2)
}


// adder

function comparator(a,b,mult) {
	var cp=0;
	if (mult) {
		cp= (((a[0]==2) || (b[0]==2)) && (!((a[0]==2) && (b[0]==2))))? 2:3
	}
	else {
		for (var i=11; i>0; i--) {
			if (a[i]==b[i]) continue;
			cp= (a[i]<b[i])? 1:0;
		}
	};
	return cp
}

function adder(x,sub,mult) {
	var c=0;
	var v=new Array();
	var s,l,m;
	var a=r.A;
	var b=x;
	if (mult) {
		m=(sub)? 0:1;
		l=0;
	}
	else {
		var sa=(a[0]==2);
		var sb=(sub)? !(b[0]==2):(b[0]==2);
		m=(sa==sb);
		l=1;
		if (comparator(a,b)) {
			a=x; b=r.A;
			s=(sb)? 2:3;
		}
		else {
			s=(sa)? 2:3;
		}
	};
	if (m==1) {
		for (var i=11; i>=l; i--) {
			var alpha=(isDigit[a[i]])? 0:1;
			alpha|=(isDigit[a[i]])? 0:2;
			if (alpha) {
				if (alpha==3) { setError('AdderAlph',1); nextstep='cycle()'; return };
				v[i]=(alpha==1)? a[i]:b[i];
				c=0
			}
			else {
				v[i]=a[i]+b[i]+c-3;
				if (v[i]>12) {
					v[i]-=10;
					c=1
				}
				else { c=0 }
			};
		}
	}
	else {
		for (var i=11; i>=l; i--) {
			var alpha=(isDigit[a[i]])? 0:1;
			alpha|=(isDigit[a[i]])? 0:2;
			if (alpha) {
				if (alpha==3) { setError('AdderAlph',1); return };
				v[i]=(alpha==1)? a[i]:b[i];
				c=0
			}
			else {
				v[i]=a[i]-b[i]+c+3;
				if (v[i]<3) {
					v[i]+=10;
					c=-1
				}
				else { c=0 }
			}
		}
	};
	if (!mult) {
		v[0]=s;
		if (s=='-') {
			var z=1;
			for (var i=11; i>0; i--) {
				if (r.A[i]!=3) {
					z=0;
					break
				}
			}
			if (z) r.A[0]=3;
		}
	};
	adder_of=(c!=0);
	r.A=v
}

// I/O

function ft_read(fwd,i2m) {
	currServo=getSRI2();
	currServoDir=fwd;
	if (i2m) {
		ft_i2m();
	}
	else {
		ft_servoRead();
	}
}

function ft_write() {
	currServo=getSRI2();
	if (currServo==3) {
		if (bt.OS.m) sc_write(mem[ftM])
		else if (bt.OS.a) sc_write(r.A)
		else if (bt.OS.x) sc_write(r.X)
		else if (bt.OS.l) sc_write(r.L)
		else if (bt.OS.f) sc_write(r.F)
		else if (bt.OS.c) sc_write(r.CC)
		else if (bt.OS.cr) sc_write(r.CR)
		else if (bt.OS.syi) sc_write(r.SYI)
		else if (bt.OS.empty) {
		}
	}
	else ft_m2o();
}

function sc_write(a) {
	var alph=(document.forms.console.printermode.selectedIndex==0)? 'LC': 'CD';
	var os=0;
	while (1) {
		for (var i=os; i<os+12; i++) {
			if (alph!='DC') {
				if (a[i]==45) alph='UC'
				else if (a[i]==47) alph='LC'
				else if (a[i]==61) {
					alph='UC'; sc_shift=2;
				}
			};
			document.forms.console.scp.value+=printerCode[alph][a[i]];
			if (sc_shift>0) {
				sc_shift--;
				if (sc_shift==0) alph='LC';
			}
		};
		document.forms.console.scp.value+=printerCode[alph][0]
		if (a.length<=os+12) break
		else os+=12;
	}
	endFT();
}


function ft_i2m() {
	nextStep='ft_i2m()';
	if (PC==0) {
		setCounter('SYIC',0);
		setCounter('ITC',0);
		setCounter('PC',PC+1);
		doTO(1);
	}
	else {
		if (SYIC==0) {
			var m2=getSRM();
			if (m2<0) return;
			ftM=m2;
		}
		else {
			setTS();
		};
		var m=ftM+SYIC;
		var os=(ITC*10+SYIC)*12
		for (var i=0; i<12; i++) mem[m][i]=r.I[os+i];
		if (SYIC==9) {
			setCounter('SYIC',0);
			setCounter('ITC', ITC+1);
			setCounter('PC',PC+1);
			if (PC<7) {
				incCRby10();
				doTO(2);
			}
			else {
				if (currServo==3) endFT()
				else {
					nextStep='ft_servoRead()';
					doTO(1);
				}
			}
		}
		else {
			setCounter('SYIC',SYIC+1);
			if (SI.IOS==4) doTO(3)
			else setTimeout(nextStep,1);
		}
	}
}

function ft_m2o() {
	nextStep='ft_m2o()';
	if (PC==0) {
		setCounter('SYOC',0);
		setCounter('OTC',0);
		setCounter('PC',PC+1);
		doTO(1);
	}
	else {
		if (SY=C==0) {
			var m2=getSRM();
			if (m2<0) return;
			ftM=m2;
		}
		else {
			setTS();
		};
		var m=ftM+SYOC;
		var os=(OTC*10+SYOC)*12
		for (var i=0; i<12; i++) r.O[os+i]=mem[m][i];
		if (SYOC==9) {
			setCounter('SYOC',0);
			setCounter('OTC', OTC+1);
			setCounter('PC',PC+1);
			if (PC<7) {
				incCRby10();
				doTO(2);
			}
			else {
				nextStep='ft_servoWrite()';
				doTO(1)
			}
		}
		else {
			setCounter('SYOC',SYOC+1);
			if (SI.IOS==4) doTO(3)
			else setTimeout(nextStep,1);
		}
	}
}

function incCRby10() {
	var n=(CY==2)? 4:10;
	r.CR[n]+=1;
	if (r.CR[n]==13) {
		r.CR[n]=3;
		r.CR[n-1]+=1;
		if (r.CR[n-1]==13) r.CR[n-1]=3;
	};
	swapSR()
}

function ft_servoRead() {
	setTS();
	if ((currServo>=2) && (currServo<=12)) {
		if (currServo==2) currServo=0
		else currServo-=3;
		var s=servo[currServo];
		if (s.r) {
			setFF('RI',1);
			// should not occur
		}
		else {
			setFF('RI',0);
		};
		setFF('DR',currServoDir);
		setFF('RV',(!currServoDir));
		setFF('WI',0);
		setFF('ROL',0);
		s.r=true;
		s.w=false;
		setNeon('S'+currServo+'r',1);
		setNeon('S'+currServo+'f',FF.DR);
		setNeon('S'+currServo+'b',FF.RV);
		if (s.lk) {
			// interlock
			setError('IOINT',1);
			nextStep='cycle()';
			return
		};
		if ((s.fir) && (s.bir)) {
			setFF('FB',1);
			s.pos=0
		};
		s.fir=s.f=FF.DR;
		s.bir=s.b=FF.RV;
		setCounter('SYIC',0);
		setCounter('ITC',0);
		setCounter('PC',PC+1);
		nextStep='readFromTape()';
		doTO(1);
	}
	else {
		setError('SERVO',1);
		nextStep='cycle()';
		return
	}
}

function ft_servoWrite() {
	setTS();
	if ((currServo>=2) && (currServo<=12)) {
		if (currServo==2) currServo=0
		else currServo-=3;
		var s=servo[currServo];
		if (s.w) {
			setFF('WI',1);
			// should not occur
		}
		else {
			setFF('WI',0);
		};
		setFF('DR',1);
		setFF('RV',0);
		setFF('RI',0);
		setFF('ROL',0);
		s.r=false;
		s.r=true;
		setNeon('S'+currServo+'w',1);
		setNeon('S'+currServo+'f',1);
		if (s.lk) {
			// interlock
			setError('IOINT',1);
			nextStep='cycle()';
			return
		};
		if ((s.fir) && (s.bir)) {
			setFF('FB',1);
			s.pos=0
		};
		s.fir=s.f=1;
		s.bir=s.b=0;
		if (s.pos<=0) { s.tape=''; s.pos=0; }
		else if (s.pos<s.length) s.tape=s.tape.substring(0,s.pos)
		else s.pos=s.tape.length-1;
		setCounter('SYOC',0);
		setCounter('OTC',0);
		setCounter('PC',PC+1);
		nextStep='write2Tape()';
		doTO(1);
	}
	else {
		setError('SERVO',1);
		nextStep='cycle()';
		return
	}
}

function readFromTape() {
	// divided in 3 functions for MSIE sake
	var s=servo[currServo];
	var c, ds, lim, bh, fd, ld, cd;
	setTS();
	if (FF.DR) {
		s_ds=1;
		s_lim=s.tape.length;
		s_bh='[';
		s_fd=0; s_ld=11
	}
	else {
		s_ds=-1;
		s_lim=-1;
		s_bh=']';
		s_fd=11; s_ld=0
	};
	s_cd=s_fd;
	setTS();
	setNeon('S'+currServo+'rw',0);
	if ((FF.DR) && (!seekHeader(s))) {
		// no header and eof
		alert('++ tape error ++\nno block header found\nreached end of tape');
		setError('TAPE',1);
		nextStep='cycle()';
		return
	}
	else {
		if (s.tape.charAt(s.pos)=='[') s.pos--;
	};
	readFromTape2()
}
function readFromTape2() {
	var s=servo[currServo];
	var c=s.tape.charAt(s.pos);
	if ((c==s_bh) || (s.pos==s_lim)) {
		// next block or eof
		if ((!SWI[62]) || ((ITC==0) && (SYIC==0))) {
			setError('TAPE',1);
			nextStep='cycle()';
			return
		}
		else {
			if (s_cd!=s_fd) {
				for (var i=s_cd; i<=s_ld; i+=s_ds) r.SYI[i]=3;
				if (transferSYI()) {
					readFromTape3();
					return
				}
			};
			var os=(ITC*10+SYIC)*12;
			if (FF.RV) {
				var delta=720-os;
				for (var i=0; i<os; i++) r.I[i]=r.I[i+delta];
			};
			for (var i=os; i<720; i++) r.I[i]=3;
			readFromTape3();
			return
		}
	}
	else if ((c=='\r') || (c=='\n') || (c=='\t') || (c==' ')) {
		s.pos+=s_ds;
		setTimeout('readFromTape2()',0)
	}
	else if (digitLU[c]) {
		r.SYI[s_cd]=digitLU[c];
		s.pos+=s_ds
		if (s_cd!=s_ld) s_cd+=s_ds
		else {
			s_cd=s_fd;
			if (transferSYI()) {
				readFromTape3();
				return
			}
		};
	}
	else {
		setError('ISOE',1);
		nextStep='cycle()';
		return;
	};
	setTimeout('readFromTape2()',0)
}
function readFromTape3() {
	var s=servo[currServo];
	while (1) {
		// check for digit 721
		var c=s.tape.charAt(s.pos);
		if ((c==bh) || (s.pos==lim)) break;
		if ((c=='\r') || (c=='\n') || (c=='\t') || (c==' ')) {
			s.pos+=ds;
			continue
		}
		else if (SWI[58]) {
			s.pos+=s_ds;
			continue
		}
		else {
			setError('IS720',1);
			nextStep='cycle()';
			return
		}
	};
	if ((FF.RV) && (!seekHeader(s))) {
		// no header and eof
		alert('++ tape error ++\nno block header found\nreached front of tape');
		setError('TAPE',1);
		nextStep='cycle()';
		s.fir=true;
		setNeon('S'+currServo+'rw',1);
		return
	};
	s.r=false;
	setNeon('S'+currServo+'r',0);
	setFF('FB',0);
	if (s.f) {
		setFF('DR',0);
		s.f=false;
		setNeon('S'+currServo+'f',0)
		if (s.pos==s.tape.length) s.pos--;
	}
	else {
		setFF('RV',0);
		s.b=false;
		setNeon('S'+currServo+'b',0);
		if (s.pos<=0) {
			s.fir=true;
			setNeon('S'+currServo+'rw',1);
		}
	};
	endFT();
}


function write2Tape() {
	// divided in 3 functions for MSIE sake
	var s=servo[currServo];
	setTS();
	setNeon('S'+currServo+'rw',0);
	s_t='[]\n';
	s_cd=0;
	write2Tape2()
}
function write2Tape2() {
	var p=0;
	if (SYOC==9) {
		setCounter('SYOC',0);
		setCounter('OTC',OTC+1);
		if (OTC==6) {
			write2Tape3();
			return
		}
	};
	for (var i=s_cd; i<s_cd*+12; i++) {
		if (digitRef[r.O[i]]) {
			s_t+=digitRef[r.O[i]];
			if (p==2) {
				s_t+=' ';
				p=0;
			}
		}
		else {
			setError('OSOE',1);
			nextStep='cycle()';
			return
		}
	};
	s_t+='\n';
	s_cd+=12;
	setTimeout('write2Tape2()',0)
}
function write2Tape3() {
	var s=servo[currServo];
	s.tape+=s_t+'\n';
	s.pos=s.tape.length-1;
	setFF('FB',0);
	s.f=false;
	setNeon('S'+currServo+'f',0);
	s.w=false;
	setNeon('S'+currServo+'w',0);
	setFF('DR',0);
	endFT();
}

function transferSYI() {
	var os=(ITC*10+SYIC)*12;
	if (FF.RV) os=708-os;
	for (var i=0; i<12; i++) r.I[os+i]=r.SYI[i];
	if (FF.SYI1) {
		setFF('SYI1',0);
		setFF('SYI2',1)
	}
	else {
		setFF('SYI1',1);
		setFF('SYI2',0)
	};
	copyWord(wb0,r.SYI);
	if (SYIC==9) {
		setCounter('SYIC',0);
		setCounter('ITC',ITC+1)
		if (ITC==6) return true;
	}
	else setCounter('SYIC',SYIC+1);
	return false
}


function seekHeader(s) {
	var c,ds,lim,b1,b2;
	tapeBlockHead=false;
	if (s.f) {
		ds=1;
		lim=s.tape.length;
		b1='[';
		b2=']'
	}
	else {
		ds=-1;
		lim=-1;
		b1=']';
		b2='['
	};
	while (1) {
		if (s.pos==lim) {
			return false
		};
		c=s.tape.charAt(s.pos);
		if (c==b1) {
			tapeBlockHead=true;
			s.pos+=ds;
			continue
		};
		if (tapeBlockHead) {
			s.pos+=ds;
			if (c==b2) {
				tapeBlockHead=false;
				return true
			}
			else {
				continue
			}
		};
		s.pos+=ds
	}
}

// start

function start() {
	resetErrors();
	setFF('STP',0);
	setFF('STL',0);
	setTimeout(nextStep,10)
}
function stop() {
	setFF('STP',1)
}


// tape

function insertMem() {
	var t=document.forms.console.tape;
	var m=0;
	var d=0;
	var e=0;
	tapeBlockHead=false;
	for (var i=0; i<t.value.length; i++) {
		var c=t.value.charAt(i);
		if (c=='[') {
			tapeBlockHead=true;
			continue
		};
		if (tapeBlockHead) {
			if (c==']') tapeBlockHead=false;
			continue
		};
		if ((c=='\r') || (c=='\n') || (c=='\t') || (c==' ')) continue;
		if (digitLU[c]) {
			mem[m][d]=digitLU[c];
			d++;
			if (d>11) {
				d=0;
				m++;
				if (m>999) m=0;
			}
		}
		else {
			alert('++ illigal digit "'+c+'" at MEM '+normalize(m)+', digit '+(d+1)+' ++');
			e=1;
			break;
		}
	};
	if (d!=0) {
		if (!e) alert('++ incomplete data at MEM '+normalize(m)+' ('+d+' digits) ++');
		for (var i=d; i<12; i++) mem[m][i]=3;
	}
	else {
		alert('** loaded MEM 000 - '+normalize(m-1)+';  ready. **')
	};
	clearC();
	nextStep='cycle()'
}

function showTape() {
	var ts=document.forms.console.servoselect;
	var s=ts.options[ts.selectedIndex].value;
	document.forms.console.tape.value=servo[s].tape
}

function mountTape() {
	var ts=document.forms.console.servoselect;
	var s=ts.options[ts.selectedIndex].value;
	resetServo(s);
	servo[s].tape=document.forms.console.tape.value;
	document.forms.console.tape.value=' '
}


// test

function setTestMem() {
	var tm= new Array(
		'[univac-test]',
		' 000 001 l00 004',
		' m00 005 ,00 002',
		' c00 006 l00 004',
		' d00 006 900 004',
		' 011 111 111 111',
		' -12 345 678 901'
	);
	document.forms.console.tape.value=tm.join('\n');
}

function translate(a) {
	var v='';
	for (var i=0; i<12; i++) v+=digitRef[a[i]];
	return v
}
function show(a) {
	alert(translate(a))
}


// # digit tables (greek letter replacements: '{' = Sigma, '^' = delta)
// numeric: bit 0-3 (from LSB to HSB), zone: bit 4 & 5

// input / data
//   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   (numeric part)
var digitRef=new Array(
	'I', '^', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '~', '~', '~', // zone 00
	'R' ,',', '.', ';', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '~', '~', '~', // zone 01 (+16)
	'T' ,'~', '~', '/', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 'L', '~', 'U', // zone 10 (+32)
	'{', '§', '~', '~', '+', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'O', '~', '~'  // zone 11 (+48)
	);
	// specials: 0 (I): ignore,      1 (^): blank,    16 (R): cr,   32 (T): tab
	//          45 (L): shift-lock, 47 (U): unschift, 61 (O): single-shift ('only one')
	// '~': no code

var printerCode=new Array()
// normal printer code, lower case
printerCode.LC=new Array(
	'' , ' ', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '' , '' , '' ,
	'\n',',', '.', ';', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '' , '' , '' ,
	'\t','' , '' , '/', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', '' , '' , '' ,
	'^', '§', '' , '' , '+', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '' , '' , ''
	);
// printer code, upper case
printerCode.UC=new Array(
	'' , ' ', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '' , '' , '' ,
	'\n',',', '.', ';', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '' , '' , '' ,
	'\t','' , '' , '/', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '' , '' , '' ,
	'^', '§', '' , '' , '+', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '' , '' , ''
	);
// computer digit mode
printerCode.CD=new Array(
	'x', ' ', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '' , '' , '' ,
	'/', ',', '.', ';', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '' , '' , '' ,
	'*', '' , '' , '/', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 'z', '' , '8',
	'{', '§', '' , '' , '+', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '-', '' , ''
	);
	// 0: 'x', 16: '/', 32: '*' 45: 'z', 47: '8', 61: '-'
// unityper (new pulse combination)
printerCode.UT=new Array(
	'' , ' ', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', "'", '&', '(', // zone 00
	'\n',',', '.', ';', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '#', '¢', '@', // zone 01
	'\t','"', '|', ')', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '$', '*', '?', // zone 10
	'{', '§', ':', '+', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '%', '=', ''   // zone 11
	);

var digitLU=new Array();
for (var i=0; i<digitRef.length; i++) {
	if (digitRef[i]!='~') digitLU[digitRef[i]]=i;
}

