var prims=new Object;
// forth prims
// pkey ( -- k )
// can't call this directly from interpreter
prims["(key)"]=function() {
    if (Kq.length > 0) {
        // if a key is available, push it immediately
        S.push(Kq.shift());
        return 0;
    } else {
        // otherwise, arrange for execution to restart at the current point 
        // when a key does arrive
        ip--;
        Kq.waiting=1;
        return 1; // abort next loop
    }
}

// pop the stack and emit the character
// ( k -- )
prims.emit=function() {
    chOut(S.pop());
}
// emit a string on the stack
prims['(type)']=function() {
    var sa=M[S.pop()];
    stOut(sa);
}
// fetch char from string
// ( c-addr -- char )
prims["c@"]=function() {
    var ca=toCaddr(S.pop());
    if (ca.ofs > M[ca.addr].length) {
        S.push(0);
    } else {
        S.push(M[ca.addr].charCodeAt(ca.ofs));
    }
}
// store char to string
// ( char c-addr -- )
prims["c!"]=function() {
    var ca=S.pop();
    var ch=S.pop();
    if (ca.ofs != undefined) {
        var s=M[ca.addr];
        if (s.length == ca.ofs) {
            M[ca.addr]=s.concat(toChar(ch));
        } else {
            M[ca.addr]=s.substr(0,ca.ofs-1).concat(toChar(ch),s.substr(ca.ofs+1));
        }
    } else {
        M[ca]=toChar(ch);
    }
}
// (c-addr c -- c-addr)
prims["chars+"]=function() {
    var c=S.pop();
    var ca=S.pop();
    if (ca.ofs != undefined) {
        ca.ofs+=c;
        S.push(ca);
    } else {
        S.push(new Caddr(ca,c));
    }
}
// (c-addr -- c-addr)
prims["char+"]=function() {
    var ca=S.pop();
    if (ca.ofs != undefined) {
        ca.ofs++;
        S.push(ca);
    } else {
        S.push(new Caddr(ca,1));
    }
}
// append character to specified string
// for normal c, pass in 'here'
// ( c-addr c -- )
prims['(c,)']=function() {
    var c=S.pop();
    var sa=S.pop();
    M[sa]=M[sa].concat(toChar(c));
}
// remove last char from string
// ( c-addr -- c )
prims['(c-)']=function() {
    var sa=S.pop();
    S.push(M[sa].charCodeAt(M[sa].length-1));
    M[sa]=M[sa].substr(0,M[sa].length-1);
}
// append cell to data area, extending memory
prims[',']=function() {
    var c=S.pop();
    M.push(c);
}
prims['(count)']=function() {
    S.push(M[S.tos()].length);
}

// here - data pointer
// points at next available cell OR current string
prims.here=function() {
	if (M[M.length-1].ofs != undefined) {
		S.push(M.length-1);
	} else {
		S.push(M.length);
	}
}
// extend data - one cell at a time
prims.allot=function() {
	var n=S.pop();
	if (n>0) {
		while(n--) {
			M.push(0);
		}
	} else {
		while(n++) {
			M.pop();
		}
	}
}
// align,aligned - no-ops (?)
prims.align=function() {
}
prims.aligned=function() {
}

prims.literal=function() {
    S.push(w);
}
// comparisons
prims['=']=function() {
    var t=S.pop();
    var u=S.pop();
    S.push(t==u);
}
prims['0<']=function() {
    var t=S.pop();
    S.push((t<0)?1:0);
}
prims['<']=function() {
    var u2=S.pop();
    var u1=S.pop();
    S.push((u1<u2)?1:0);
}
prims['>']=function() {
    var u2=S.pop();
    var u1=S.pop();
    S.push((u1>u2)?1:0);
}
prims.invert=function() {
	var x=S.pop();
	S.push(~x);
}

// stack ops
prims.dup=function() {
    S.push(S.tos());
}
prims['?dup']=function() {
	var tos=S.tos();
    if (tos != 0) {
		S.push(tos);
	}
}
prims.over=function() {
	S.push(S.s[S.s.length-2]);
}
prims.swap=function() {
    var t=S.pop();
    var u=S.pop();
    S.push(t);
    S.push(u);
}
prims.drop=function() {
    S.pop();
}


var stats=new Object;
// flow control prims
prims.docol=function(i) {
    if(stats[M[w-3]] == undefined) stats[M[w-3]]=0;
    stats[M[w-3]]++;
    RS.push(ip);
    ip=w;
}
prims['repstat']=function() {
    for (var z in stats) {
        trace2(z+": "+stats[z]);
    }
}
prims['clrstat']=function() {
    stats=new Object;
}

prims[";s"]=function() {
    ip=RS.pop();
}
prims.branch=function() {
    // trace("branch to "+i.dat);
    ip=M[ip];
}
prims.zbranch=function() {
    var t=S.pop();
    if (!t) {
        ip=M[ip];
    } else {
        ip++;
    }
}

// rstack ops
prims[">r"]=function() {
	RS.push(S.pop());
}
prims["r>"]=function() {
	S.push(RS.pop());
}
prims["r@"]=function() {
	S.push(RS.tos());
}
prims['-']=function() {
	var u2=S.pop();
	var u1=S.pop();
	S.push(u1-u2);
}
prims["+"]=function() {
	var u2=S.pop();
	var u1=S.pop();
	S.push(u1.plus(u2));
}
prims['0=']=function() {
	var t=S.pop();
	S.push((t==0)?1:0);
}
prims['1+']=function() {
	var u=S.pop();
	S.push(u+1);
}
prims['1-']=function() {
	var u=S.pop();
	S.push(u-1);
}
prims.and=function() {
	var t=S.pop();
	var u=S.pop();
	S.push((t && u)?1:0);
}
prims.or=function() {
	var t=S.pop();
	var u=S.pop();
	S.push((t || u)?1:0);
}
prims["@"]=function() {
	S.push(M[S.pop()]);
}
prims['!']=function() {
	var a=S.pop();
	var v=S.pop();
	M[a]=v;
}
prims['+!']=function() {
	var a=S.pop();
	var n=S.pop();
	M[a]+=n;
}


// ( n*i xt -- j*i )
// execute word specified by xt
// an xt is either an object: {prim: primitive} 
// or the cfa of a non-primitive (colon def, var, ...)
// if a prim, just evecute it directly
// if a cfa, grab that prim and the pfa and somehow merge them together...
prims.execute=function() {
    var xt=S.pop();
    if (xt != undefined && xt.prim != undefined) {
        xt.prim();
    } else {
        w=(xt+1);
        M[xt].prim();
    }
}

// ( string -- xt 1 | string 0 ) 
// does the string (or xt) represent a primitive or a non-prim?
prims["?prim"]=function() {
    var s=S.pop();
    if (s && s.prim != undefined) {
        S.push(s);
	S.push(1);
    } else if (prims[s]) {
        S.push(new Prim(s));
	S.push(1);
    } else {
        S.push(s);
        S.push(0);
    }
}

// ( xt -- -1 | 1 )
// give 1 for immediate prims, -1 for non-imm
// note that prims are never immediate
prims["imm?"]=function() {
    var xt=S.pop();
    if (M[xt] && M[xt].imm) {
        S.push(1);
    } else {
        S.push(-1);
    }
}
// ( xt f -- ) 
// set immediate state
prims["imm!"]=function() {
    var f=S.pop();
    var xt=S.pop();
	M[xt].imm=f;
}

prims['>s']=function() {
    var n=S.pop();
    S.push(String(n));
}
prims['>n']=function() {
    var s=S.pop();
    S.push(Number(s));
}

// : (load) ( c-addr "name" -- )
prims['(load)']=function() {
	var fn=S.pop();
	var ca=S.pop();
    var xr=new XMLRemoteRequest();
    var doc=xr.getRemoteDocumentString(fn);
	M[ca]=doc;
}

// : (e-load) ( c-addr "name" -- )
prims['(e-load)']=function() {
	var fn=S.pop();
	var ca=S.pop();
	var doc=document.getElementById(fn).value;
	M[ca]=doc;
}

// facility primitives
// : at-xy ( u1 u2 -- )
// move cursor
prims['at-xy']=function() {
	var u2=S.pop();
	var u1=S.pop();
	IOmoveTo(u1,u2);
}
prims['key?']=function() {
	S.push(Kq.length>0?1:0);
}
prims['page']=function() {
	clrscr();
	IOmoveTo(0,0);
}
// : ms ( u -- )  
// delay execution at least u milliseconds
prims.ms=function() {
	var d=S.pop();
	window.setTimeout(doNext,d);
	return 1;
}
// ( -- )
// refresh screen - mostly unneeded
prims.update=function() {
    updated();
    // break out of doNext loop to allow UI to run
	window.setTimeout(doNext,0);
	return 1;
}

// misc - debugging prims
prims.S=function() {
	trace2(S.s);
}
    

// end prims
